]> arthur.barton.de Git - netatalk.git/blob - libatalk/util/unix.c
Merge remote branch 'sf/branch-allea' into branch-allea
[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
37 #include <atalk/adouble.h>
38 #include <atalk/ea.h>
39 #include <atalk/afp.h>
40 #include <atalk/logger.h>
41 #include <atalk/vfs.h>
42 #include <atalk/util.h>
43 #include <atalk/unix.h>
44 #include <atalk/compat.h>
45 #include <atalk/errchk.h>
46
47 /* close all FDs >= a specified value */
48 static void closeall(int fd)
49 {
50     int fdlimit = sysconf(_SC_OPEN_MAX);
51
52     while (fd < fdlimit)
53         close(fd++);
54 }
55
56 /*!
57  * Run command in a child and wait for it to finish
58  */
59 int run_cmd(const char *cmd, char **cmd_argv)
60 {
61     EC_INIT;
62     pid_t pid, wpid;
63     sigset_t sigs, oldsigs;
64         int status = 0;
65
66     sigfillset(&sigs);
67     pthread_sigmask(SIG_SETMASK, &sigs, &oldsigs);
68
69     if ((pid = fork()) < 0) {
70         LOG(log_error, logtype_default, "run_cmd: fork: %s", strerror(errno));
71         return -1;
72     }
73
74     if (pid == 0) {
75         /* child */
76         closeall(3);
77         execvp("mv", cmd_argv);
78     }
79
80     /* parent */
81         while ((wpid = waitpid(pid, &status, 0)) < 0) {
82             if (errno == EINTR)
83             continue;
84             break;
85         }
86         if (wpid != pid) {
87             LOG(log_error, logtype_default, "waitpid(%d): %s", (int)pid, strerror(errno));
88         EC_FAIL;
89         }
90
91     if (WIFEXITED(status))
92         status = WEXITSTATUS(status);
93     else if (WIFSIGNALED(status))
94         status = WTERMSIG(status);
95
96     LOG(log_note, logtype_default, "run_cmd(\"%s\"): status: %d", cmd, status);
97
98 EC_CLEANUP:
99     if (status != 0)
100         ret = status;
101     pthread_sigmask(SIG_SETMASK, &oldsigs, NULL);
102     EC_EXIT;
103 }
104
105 /*!
106  * Daemonize
107  *
108  * Fork, exit parent, setsid(), optionally chdir("/"), optionally close all fds
109  *
110  * returns -1 on failure, but you can't do much except exit in that case
111  * since we may already have forked
112  */
113 int daemonize(int nochdir, int noclose)
114 {
115     switch (fork()) {
116     case 0:
117         break;
118     case -1:
119         return -1;
120     default:
121         _exit(0);
122     }
123
124     if (setsid() < 0)
125         return -1;
126
127     switch (fork()) {
128     case 0: 
129         break;
130     case -1:
131         return -1;
132     default:
133         _exit(0);
134     }
135
136     if (!nochdir)
137         chdir("/");
138
139     if (!noclose) {
140         closeall(0);
141         open("/dev/null",O_RDWR);
142         dup(0);
143         dup(0);
144     }
145
146     return 0;
147 }
148
149 static uid_t saved_uid = -1;
150
151 /*
152  * seteuid(0) and back, if either fails and panic != 0 we PANIC
153  */
154 void become_root(void)
155 {
156     if (getuid() == 0) {
157         saved_uid = geteuid();
158         if (seteuid(0) != 0)
159             AFP_PANIC("Can't seteuid(0)");
160     }
161 }
162
163 void unbecome_root(void)
164 {
165     if (getuid() == 0) {
166         if (saved_uid == -1 || seteuid(saved_uid) < 0)
167             AFP_PANIC("Can't seteuid back");
168         saved_uid = -1;
169     }
170 }
171
172 /*!
173  * @brief get cwd in static buffer
174  *
175  * @returns pointer to path or pointer to error messages on error
176  */
177 const char *getcwdpath(void)
178 {
179     static char cwd[MAXPATHLEN + 1];
180     char *p;
181
182     if ((p = getcwd(cwd, MAXPATHLEN)) != NULL)
183         return p;
184     else
185         return strerror(errno);
186 }
187
188 /*!
189  * @brief Request absolute path
190  *
191  * @returns Absolute filesystem path to object
192  */
193 const char *fullpathname(const char *name)
194 {
195     static char wd[MAXPATHLEN + 1];
196
197     if (name[0] == '/')
198         return name;
199
200     if (getcwd(wd , MAXPATHLEN)) {
201         strlcat(wd, "/", MAXPATHLEN);
202         strlcat(wd, name, MAXPATHLEN);
203     } else {
204         strlcpy(wd, name, MAXPATHLEN);
205     }
206
207     return wd;
208 }
209
210 /*!
211  * Takes a buffer with a path, strips slashs, returns basename
212  *
213  * @param p (rw) path
214  *        path may be
215  *          "[/][dir/[...]]file"
216  *        or
217  *          "[/][dir/[...]]dir/[/]"
218  *        Result is "file" or "dir" 
219  *
220  * @returns pointer to basename in path buffer, buffer is possibly modified
221  */
222 char *stripped_slashes_basename(char *p)
223 {
224     int i = strlen(p) - 1;
225     while (i > 0 && p[i] == '/')
226         p[i--] = 0;
227     return (strrchr(p, '/') ? strrchr(p, '/') + 1 : p);
228 }
229
230 /*!
231  * @brief symlink safe chdir replacement
232  *
233  * Only chdirs to dir if it doesn't contain symlinks.
234  *
235  * @returns 1 if a path element is a symlink, 0 otherwise, -1 on syserror
236  */
237 int lchdir(const char *dir)
238 {
239     char buf[MAXPATHLEN+1];
240     char cwd[MAXPATHLEN+1];
241     char *test;
242     int  i;
243
244     /*
245      dir is a canonical path (without "../" "./" "//" )
246      but may end with a / 
247     */
248     *cwd = 0;
249     if (*dir != '/') {
250         if (getcwd(cwd, MAXPATHLEN) == NULL)
251             return -1;
252     }
253     if (chdir(dir) != 0)
254         return -1;
255
256     /* 
257      * Cases:
258      * chdir request   | realpath result | ret
259      * (after getwcwd) |                 |
260      * =======================================
261      * /a/b/.          | /a/b            | 0
262      * /a/b/.          | /c              | 1
263      * /a/b/.          | /c/d/e/f        | 1
264      */
265     if (getcwd(buf, MAXPATHLEN) == NULL)
266         return 1;
267
268     i = 0;
269     if (*cwd) {
270         /* relative path requested, 
271          * Same directory?
272         */
273         for (; cwd[i]; i++) {
274             if (buf[i] != cwd[i])
275                 return 1;
276         }
277         if (buf[i]) {
278             if (buf[i] != '/')
279                 return 1;
280             i++;
281         }                    
282     }
283
284     test = &buf[i];    
285     for (i = 0; test[i]; i++) {
286         if (test[i] != dir[i]) {
287             return 1;
288         }
289     }
290     /* trailing '/' ? */
291     if (!dir[i])
292         return 0;
293
294     if (dir[i] != '/')
295         return 1;
296
297     i++;
298     if (dir[i])
299         return 1;
300
301     return 0;
302 }
303
304 /*!
305  * Store n random bytes an buf
306  */
307 void randombytes(void *buf, int n)
308 {
309     char *p = (char *)buf;
310     int fd, i;
311     struct timeval tv;
312
313     if ((fd = open("/dev/urandom", O_RDONLY)) != -1) {
314         /* generate from /dev/urandom */
315         if (read(fd, buf, n) != n) {
316             close(fd);
317             fd = -1;
318         } else {
319             close(fd);
320             /* fd now != -1, so srandom wont be called below */
321         }
322     }
323
324     if (fd == -1) {
325         gettimeofday(&tv, NULL);
326         srandom((unsigned int)tv.tv_usec);
327         for (i=0 ; i < n ; i++)
328             p[i] = random() & 0xFF;
329     }
330
331     return;
332 }
333
334 int gmem(gid_t gid, int ngroups, gid_t *groups)
335 {
336     int         i;
337
338     for ( i = 0; i < ngroups; i++ ) {
339         if ( groups[ i ] == gid ) {
340             return( 1 );
341         }
342     }
343     return( 0 );
344 }