2 Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
17 * Netatalk utility functions
22 #endif /* HAVE_CONFIG_H */
29 #include <sys/types.h>
38 #include <atalk/adouble.h>
40 #include <atalk/afp.h>
41 #include <atalk/logger.h>
42 #include <atalk/vfs.h>
43 #include <atalk/util.h>
44 #include <atalk/unix.h>
45 #include <atalk/compat.h>
46 #include <atalk/errchk.h>
48 /* close all FDs >= a specified value */
49 static void closeall(int fd)
51 int fdlimit = sysconf(_SC_OPEN_MAX);
58 * Run command in a child and wait for it to finish
60 int run_cmd(const char *cmd, char **cmd_argv)
64 sigset_t sigs, oldsigs;
68 pthread_sigmask(SIG_SETMASK, &sigs, &oldsigs);
70 if ((pid = fork()) < 0) {
71 LOG(log_error, logtype_default, "run_cmd: fork: %s", strerror(errno));
78 execvp("mv", cmd_argv);
82 while ((wpid = waitpid(pid, &status, 0)) < 0) {
88 LOG(log_error, logtype_default, "waitpid(%d): %s", (int)pid, strerror(errno));
92 if (WIFEXITED(status))
93 status = WEXITSTATUS(status);
94 else if (WIFSIGNALED(status))
95 status = WTERMSIG(status);
97 LOG(log_note, logtype_default, "run_cmd(\"%s\"): status: %d", cmd, status);
102 pthread_sigmask(SIG_SETMASK, &oldsigs, NULL);
109 * Fork, exit parent, setsid(), optionally chdir("/"), optionally close all fds
111 * returns -1 on failure, but you can't do much except exit in that case
112 * since we may already have forked
114 int daemonize(int nochdir, int noclose)
142 open("/dev/null",O_RDWR);
150 static uid_t saved_uid = -1;
153 * seteuid(0) and back, if either fails and panic != 0 we PANIC
155 void become_root(void)
158 saved_uid = geteuid();
160 AFP_PANIC("Can't seteuid(0)");
164 void unbecome_root(void)
167 if (saved_uid == -1 || seteuid(saved_uid) < 0)
168 AFP_PANIC("Can't seteuid back");
174 * @brief get cwd in static buffer
176 * @returns pointer to path or pointer to error messages on error
178 const char *getcwdpath(void)
180 static char cwd[MAXPATHLEN + 1];
183 if ((p = getcwd(cwd, MAXPATHLEN)) != NULL)
186 return strerror(errno);
190 * @brief Request absolute path
192 * @returns Absolute filesystem path to object
194 const char *fullpathname(const char *name)
196 static char wd[MAXPATHLEN + 1];
201 if (getcwd(wd , MAXPATHLEN)) {
202 strlcat(wd, "/", MAXPATHLEN);
203 strlcat(wd, name, MAXPATHLEN);
205 strlcpy(wd, name, MAXPATHLEN);
212 * Takes a buffer with a path, strips slashs, returns basename
216 * "[/][dir/[...]]file"
218 * "[/][dir/[...]]dir/[/]"
219 * Result is "file" or "dir"
221 * @returns pointer to basename in path buffer, buffer is possibly modified
223 char *stripped_slashes_basename(char *p)
225 int i = strlen(p) - 1;
226 while (i > 0 && p[i] == '/')
228 return (strrchr(p, '/') ? strrchr(p, '/') + 1 : p);
232 * @brief symlink safe chdir replacement
234 * Only chdirs to dir if it doesn't contain symlinks.
236 * @returns 1 if a path element is a symlink, 0 otherwise, -1 on syserror
238 int lchdir(const char *dir)
240 char buf[MAXPATHLEN+1];
241 char cwd[MAXPATHLEN+1];
246 dir is a canonical path (without "../" "./" "//" )
251 if (getcwd(cwd, MAXPATHLEN) == NULL)
259 * chdir request | realpath result | ret
260 * (after getwcwd) | |
261 * =======================================
264 * /a/b/. | /c/d/e/f | 1
266 if (getcwd(buf, MAXPATHLEN) == NULL)
271 /* relative path requested,
274 for (; cwd[i]; i++) {
275 if (buf[i] != cwd[i])
286 for (i = 0; test[i]; i++) {
287 if (test[i] != dir[i]) {
306 * Store n random bytes an buf
308 void randombytes(void *buf, int n)
310 char *p = (char *)buf;
314 if ((fd = open("/dev/urandom", O_RDONLY)) != -1) {
315 /* generate from /dev/urandom */
316 if (read(fd, buf, n) != n) {
321 /* fd now != -1, so srandom wont be called below */
326 gettimeofday(&tv, NULL);
327 srandom((unsigned int)tv.tv_usec);
328 for (i=0 ; i < n ; i++)
329 p[i] = random() & 0xFF;
335 int gmem(gid_t gid, int ngroups, gid_t *groups)
339 for ( i = 0; i < ngroups; i++ ) {
340 if ( groups[ i ] == gid ) {
348 * realpath() replacement that always allocates storage for returned path
350 char *realpath_safe(const char *path)
354 #ifdef REALPATH_TAKES_NULL
355 if ((resolved_path = realpath(path, NULL)) == NULL) {
356 LOG(log_error, logtype_afpd, "realpath() cannot resolve path \"%s\"", path);
359 return resolved_path;
361 if ((resolved_path = malloc(MAXPATHLEN+1)) == NULL)
363 if (realpath(path, resolved_path) == NULL) {
365 LOG(log_error, logtype_afpd, "realpath() cannot resolve path \"%s\"", path);
368 /* Safe some memory */
370 if ((tmp = strdup(resolved_path)) == NULL) {
376 return resolved_path;
381 * Returns pointer to static buffer with basename of path
383 const char *basename_safe(const char *path)
385 static char buf[MAXPATHLEN+1];
386 strlcpy(buf, path, MAXPATHLEN);
387 return basename(buf);
391 * extended strtok allows the quoted strings
392 * modified strtok.c in glibc 2.0.6
394 char *strtok_quote(char *s, const char *delim)
396 static char *olds = NULL;
402 /* Scan leading delimiters. */
403 s += strspn (s, delim);
407 /* Find the end of the token. */
410 if (token[0] == '\"') {
412 s = strpbrk (token, "\"");
414 s = strpbrk (token, delim);
418 /* This token finishes the string. */
419 olds = strchr (token, '\0');
421 /* Terminate the token and make OLDS point past it. */