]> arthur.barton.de Git - netatalk.git/blob - libatalk/util/unix.c
37bfe140fe3d1d0101ab966a55ee90db3f67b9d8
[netatalk.git] / libatalk / util / unix.c
1 /*
2   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
3
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.
8
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.
13 */
14
15 /*!
16  * @file
17  * Netatalk utility functions
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif /* HAVE_CONFIG_H */
23
24 #include <unistd.h>
25 #include <stdint.h>
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <dirent.h>
33 #include <sys/time.h>
34 #include <time.h>
35 #include <sys/wait.h>
36 #include <libgen.h>
37
38 #include <atalk/adouble.h>
39 #include <atalk/ea.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>
47
48 /* close all FDs >= a specified value */
49 static void closeall(int fd)
50 {
51     int fdlimit = sysconf(_SC_OPEN_MAX);
52
53     while (fd < fdlimit)
54         close(fd++);
55 }
56
57 /*!
58  * Run command in a child and wait for it to finish
59  */
60 int run_cmd(const char *cmd, char **cmd_argv)
61 {
62     EC_INIT;
63     pid_t pid, wpid;
64     sigset_t sigs, oldsigs;
65         int status = 0;
66
67     sigfillset(&sigs);
68     pthread_sigmask(SIG_SETMASK, &sigs, &oldsigs);
69
70     if ((pid = fork()) < 0) {
71         LOG(log_error, logtype_default, "run_cmd: fork: %s", strerror(errno));
72         return -1;
73     }
74
75     if (pid == 0) {
76         /* child */
77         closeall(3);
78         execvp("mv", cmd_argv);
79     }
80
81     /* parent */
82         while ((wpid = waitpid(pid, &status, 0)) < 0) {
83             if (errno == EINTR)
84             continue;
85             break;
86         }
87         if (wpid != pid) {
88             LOG(log_error, logtype_default, "waitpid(%d): %s", (int)pid, strerror(errno));
89         EC_FAIL;
90         }
91
92     if (WIFEXITED(status))
93         status = WEXITSTATUS(status);
94     else if (WIFSIGNALED(status))
95         status = WTERMSIG(status);
96
97     LOG(log_note, logtype_default, "run_cmd(\"%s\"): status: %d", cmd, status);
98
99 EC_CLEANUP:
100     if (status != 0)
101         ret = status;
102     pthread_sigmask(SIG_SETMASK, &oldsigs, NULL);
103     EC_EXIT;
104 }
105
106 /*!
107  * Daemonize
108  *
109  * Fork, exit parent, setsid(), optionally chdir("/"), optionally close all fds
110  *
111  * returns -1 on failure, but you can't do much except exit in that case
112  * since we may already have forked
113  */
114 int daemonize(int nochdir, int noclose)
115 {
116     switch (fork()) {
117     case 0:
118         break;
119     case -1:
120         return -1;
121     default:
122         _exit(0);
123     }
124
125     if (setsid() < 0)
126         return -1;
127
128     switch (fork()) {
129     case 0: 
130         break;
131     case -1:
132         return -1;
133     default:
134         _exit(0);
135     }
136
137     if (!nochdir)
138         chdir("/");
139
140     if (!noclose) {
141         closeall(0);
142         open("/dev/null",O_RDWR);
143         dup(0);
144         dup(0);
145     }
146
147     return 0;
148 }
149
150 static uid_t saved_uid = -1;
151
152 /*
153  * seteuid(0) and back, if either fails and panic != 0 we PANIC
154  */
155 void become_root(void)
156 {
157     if (getuid() == 0) {
158         saved_uid = geteuid();
159         if (seteuid(0) != 0)
160             AFP_PANIC("Can't seteuid(0)");
161     }
162 }
163
164 void unbecome_root(void)
165 {
166     if (getuid() == 0) {
167         if (saved_uid == -1 || seteuid(saved_uid) < 0)
168             AFP_PANIC("Can't seteuid back");
169         saved_uid = -1;
170     }
171 }
172
173 /*!
174  * @brief get cwd in static buffer
175  *
176  * @returns pointer to path or pointer to error messages on error
177  */
178 const char *getcwdpath(void)
179 {
180     static char cwd[MAXPATHLEN + 1];
181     char *p;
182
183     if ((p = getcwd(cwd, MAXPATHLEN)) != NULL)
184         return p;
185     else
186         return strerror(errno);
187 }
188
189 /*!
190  * @brief Request absolute path
191  *
192  * @returns Absolute filesystem path to object
193  */
194 const char *fullpathname(const char *name)
195 {
196     static char wd[MAXPATHLEN + 1];
197
198     if (name[0] == '/')
199         return name;
200
201     if (getcwd(wd , MAXPATHLEN)) {
202         strlcat(wd, "/", MAXPATHLEN);
203         strlcat(wd, name, MAXPATHLEN);
204     } else {
205         strlcpy(wd, name, MAXPATHLEN);
206     }
207
208     return wd;
209 }
210
211 /*!
212  * Takes a buffer with a path, strips slashs, returns basename
213  *
214  * @param p (rw) path
215  *        path may be
216  *          "[/][dir/[...]]file"
217  *        or
218  *          "[/][dir/[...]]dir/[/]"
219  *        Result is "file" or "dir" 
220  *
221  * @returns pointer to basename in path buffer, buffer is possibly modified
222  */
223 char *stripped_slashes_basename(char *p)
224 {
225     int i = strlen(p) - 1;
226     while (i > 0 && p[i] == '/')
227         p[i--] = 0;
228     return (strrchr(p, '/') ? strrchr(p, '/') + 1 : p);
229 }
230
231 /*********************************************************************************
232  * chdir(), chmod(), chown(), stat() wrappers taking an additional option.
233  * Currently the only used options are O_NOFOLLOW, used to switch between symlink
234  * behaviour, and O_NETATALK_ACL for ochmod() indicating chmod_acl() shall be
235  * called which does special ACL handling depending on the filesytem
236  *********************************************************************************/
237
238 int ostat(const char *path, struct stat *buf, int options)
239 {
240     if (options & O_NOFOLLOW)
241         return lstat(path, buf);
242     else
243         return stat(path, buf);
244 }
245
246 int ochown(const char *path, uid_t owner, gid_t group, int options)
247 {
248     if (options & O_NOFOLLOW)
249         return lchown(path, owner, group);
250     else
251         return chown(path, owner, group);
252 }
253
254 /*!
255  * chmod() wrapper for symlink and ACL handling
256  *
257  * @param path       (r) path
258  * @param mode       (r) requested mode
259  * @param sb         (r) stat() of path or NULL
260  * @param option     (r) O_NOFOLLOW | O_NETATALK_ACL
261  *
262  * Options description:
263  * O_NOFOLLOW: don't chmod() symlinks, do nothing, return 0
264  * O_NETATALK_ACL: call chmod_acl() instead of chmod()
265  */
266 int ochmod(char *path, mode_t mode, const struct stat *st, int options)
267 {
268     struct stat sb;
269
270     if (!st) {
271         if (lstat(path, &sb) != 0)
272             return -1;
273         st = &sb;
274     }
275
276     if (options & O_NOFOLLOW)
277         if (S_ISLNK(st->st_mode))
278             return 0;
279
280     if (options & O_NETATALK_ACL) {
281         return chmod_acl(path, mode);
282     } else {
283         return chmod(path, mode);
284     }
285 }
286
287 /* 
288  * @brief ostat/fsstatat multiplexer
289  *
290  * ostatat mulitplexes ostat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored.
291  *
292  * @param dirfd   (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD
293  * @param path    (r) pathname
294  * @param st      (rw) pointer to struct stat
295  */
296 int ostatat(int dirfd, const char *path, struct stat *st, int options)
297 {
298 #ifdef HAVE_ATFUNCS
299     if (dirfd == -1)
300         dirfd = AT_FDCWD;
301     return fstatat(dirfd, path, st, (options & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0);
302 #else
303     return ostat(path, st, options);
304 #endif            
305
306     /* DEADC0DE */
307     return -1;
308 }
309
310 /*!
311  * @brief symlink safe chdir replacement
312  *
313  * Only chdirs to dir if it doesn't contain symlinks or if symlink checking
314  * is disabled
315  *
316  * @returns 1 if a path element is a symlink, 0 otherwise, -1 on syserror
317  */
318 int ochdir(const char *dir, int options)
319 {
320     char buf[MAXPATHLEN+1];
321     char cwd[MAXPATHLEN+1];
322     char *test;
323     int  i;
324
325     if (!(options & O_NOFOLLOW))
326         return chdir(dir);
327
328     /*
329      dir is a canonical path (without "../" "./" "//" )
330      but may end with a / 
331     */
332     *cwd = 0;
333     if (*dir != '/') {
334         if (getcwd(cwd, MAXPATHLEN) == NULL)
335             return -1;
336     }
337     if (chdir(dir) != 0)
338         return -1;
339
340     /* 
341      * Cases:
342      * chdir request   | realpath result | ret
343      * (after getwcwd) |                 |
344      * =======================================
345      * /a/b/.          | /a/b            | 0
346      * /a/b/.          | /c              | 1
347      * /a/b/.          | /c/d/e/f        | 1
348      */
349     if (getcwd(buf, MAXPATHLEN) == NULL)
350         return 1;
351
352     i = 0;
353     if (*cwd) {
354         /* relative path requested, 
355          * Same directory?
356         */
357         for (; cwd[i]; i++) {
358             if (buf[i] != cwd[i])
359                 return 1;
360         }
361         if (buf[i]) {
362             if (buf[i] != '/')
363                 return 1;
364             i++;
365         }                    
366     }
367
368     test = &buf[i];    
369     for (i = 0; test[i]; i++) {
370         if (test[i] != dir[i]) {
371             return 1;
372         }
373     }
374     /* trailing '/' ? */
375     if (!dir[i])
376         return 0;
377
378     if (dir[i] != '/')
379         return 1;
380
381     i++;
382     if (dir[i])
383         return 1;
384
385     return 0;
386 }
387
388 /*!
389  * Store n random bytes an buf
390  */
391 void randombytes(void *buf, int n)
392 {
393     char *p = (char *)buf;
394     int fd, i;
395     struct timeval tv;
396
397     if ((fd = open("/dev/urandom", O_RDONLY)) != -1) {
398         /* generate from /dev/urandom */
399         if (read(fd, buf, n) != n) {
400             close(fd);
401             fd = -1;
402         } else {
403             close(fd);
404             /* fd now != -1, so srandom wont be called below */
405         }
406     }
407
408     if (fd == -1) {
409         gettimeofday(&tv, NULL);
410         srandom((unsigned int)tv.tv_usec);
411         for (i=0 ; i < n ; i++)
412             p[i] = random() & 0xFF;
413     }
414
415     return;
416 }
417
418 int gmem(gid_t gid, int ngroups, gid_t *groups)
419 {
420     int         i;
421
422     for ( i = 0; i < ngroups; i++ ) {
423         if ( groups[ i ] == gid ) {
424             return( 1 );
425         }
426     }
427     return( 0 );
428 }
429
430 /*
431  * realpath() replacement that always allocates storage for returned path
432  */
433 char *realpath_safe(const char *path)
434 {
435     char *resolved_path;
436
437 #ifdef REALPATH_TAKES_NULL
438     if ((resolved_path = realpath(path, NULL)) == NULL) {
439         LOG(log_error, logtype_afpd, "realpath() cannot resolve path \"%s\"", path);
440         return NULL;
441     }
442     return resolved_path;
443 #else
444     if ((resolved_path = malloc(MAXPATHLEN+1)) == NULL)
445         return NULL;
446     if (realpath(path, resolved_path) == NULL) {
447         free(resolved_path);
448         LOG(log_error, logtype_afpd, "realpath() cannot resolve path \"%s\"", path);
449         return NULL;
450     }
451     /* Safe some memory */
452     char *tmp;
453     if ((tmp = strdup(resolved_path)) == NULL) {
454         free(resolved_path);
455         return NULL;
456     }
457     free(resolved_path);
458     resolved_path = tmp;
459     return resolved_path;
460 #endif
461 }
462
463 /**
464  * Returns pointer to static buffer with basename of path
465  **/
466 const char *basename_safe(const char *path)
467 {
468     static char buf[MAXPATHLEN+1];
469     strlcpy(buf, path, MAXPATHLEN);
470     return basename(buf);
471 }
472
473 /**
474  * extended strtok allows the quoted strings
475  * modified strtok.c in glibc 2.0.6
476  **/
477 char *strtok_quote(char *s, const char *delim)
478 {
479     static char *olds = NULL;
480     char *token;
481
482     if (s == NULL)
483         s = olds;
484
485     /* Scan leading delimiters.  */
486     s += strspn (s, delim);
487     if (*s == '\0')
488         return NULL;
489
490     /* Find the end of the token.  */
491     token = s;
492
493     if (token[0] == '\"') {
494         token++;
495         s = strpbrk (token, "\"");
496     } else {
497         s = strpbrk (token, delim);
498     }
499
500     if (s == NULL) {
501         /* This token finishes the string.  */
502         olds = strchr (token, '\0');
503     } else {
504         /* Terminate the token and make OLDS point past it.  */
505         *s = '\0';
506         olds = s + 1;
507     }
508     return token;
509 }
510
511 int set_groups(AFPObj *obj, struct passwd *pwd)
512 {
513     if (initgroups(pwd->pw_name, pwd->pw_gid) < 0)
514         LOG(log_error, logtype_afpd, "initgroups(%s, %d): %s", pwd->pw_name, pwd->pw_gid, strerror(errno));
515
516     if ((obj->ngroups = getgroups(0, NULL)) < 0) {
517         LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno));
518         return -1;
519     }
520
521     if (obj->groups)
522         free(obj->groups);
523     if (NULL == (obj->groups = calloc(obj->ngroups, sizeof(gid_t))) ) {
524         LOG(log_error, logtype_afpd, "login: %s calloc: %d", obj->ngroups);
525         return -1;
526     }
527
528     if ((obj->ngroups = getgroups(obj->ngroups, obj->groups)) < 0 ) {
529         LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno));
530         return -1;
531     }
532
533     return 0;
534 }
535
536 #define GROUPSTR_BUFSIZE 1024
537 const char *print_groups(int ngroups, gid_t *groups)
538 {
539     static char groupsstr[GROUPSTR_BUFSIZE];
540     int i;
541     char *s = groupsstr;
542
543     if (ngroups == 0)
544         return "-";
545
546     for (i = 0; (i < ngroups) && (s < &groupsstr[GROUPSTR_BUFSIZE]); i++) {
547         s += snprintf(s, &groupsstr[GROUPSTR_BUFSIZE] - s, " %u", groups[i]);
548     }
549
550     return groupsstr;
551 }