1 /* portability.c - code to workaround the deficiencies of various platforms.
2 *
3 * Copyright 2012 Rob Landley <rob@landley.net>
4 * Copyright 2012 Georgi Chorbadzhiyski <gf@unixsol.org>
5 */
6
7 #include "toys.h"
8
9 // We can't fork() on nommu systems, and vfork() requires an exec() or exit()
10 // before resuming the parent (because they share a heap until then). And no,
11 // we can't implement our own clone() call that does the equivalent of fork()
12 // because nommu heaps use physical addresses so if we copy the heap all our
13 // pointers are wrong. (You need an mmu in order to map two heaps to the same
14 // address range without interfering with each other.) In the absence of
15 // a portable way to tell malloc() to start a new heap without freeing the old
16 // one, you pretty much need the exec().)
17
18 // So we exec ourselves (via /proc/self/exe, if anybody knows a way to
19 // re-exec self without depending on the filesystem, I'm all ears),
20 // and use the arguments to signal reentry.
21
22 #if CFG_TOYBOX_FORK
xfork(void)23 pid_t xfork(void)
24 {
25 pid_t pid = fork();
26
27 if (pid < 0) perror_exit("fork");
28
29 return pid;
30 }
31 #endif
32
xgetrandom(void * buf,unsigned buflen,unsigned flags)33 int xgetrandom(void *buf, unsigned buflen, unsigned flags)
34 {
35 int fd;
36
37 #if CFG_TOYBOX_GETRANDOM
38 if (buflen == getrandom(buf, buflen, flags&~WARN_ONLY)) return 1;
39 if (errno!=ENOSYS && !(flags&WARN_ONLY)) perror_exit("getrandom");
40 #endif
41 fd = xopen(flags ? "/dev/random" : "/dev/urandom",O_RDONLY|(flags&WARN_ONLY));
42 if (fd == -1) return 0;
43 xreadall(fd, buf, buflen);
44 close(fd);
45
46 return 1;
47 }
48
49 // Get list of mounted filesystems, including stat and statvfs info.
50 // Returns a reversed list, which is good for finding overmounts and such.
51
52 #if defined(__APPLE__) || defined(__FreeBSD__)
53
54 #include <sys/mount.h>
55
xgetmountlist(char * path)56 struct mtab_list *xgetmountlist(char *path)
57 {
58 struct mtab_list *mtlist = 0, *mt;
59 struct statfs *entries;
60 int i, count;
61
62 if (path) error_exit("xgetmountlist");
63 if ((count = getmntinfo(&entries, 0)) == 0) perror_exit("getmntinfo");
64
65 // The "test" part of the loop is done before the first time through and
66 // again after each "increment", so putting the actual load there avoids
67 // duplicating it. If the load was NULL, the loop stops.
68
69 for (i = 0; i < count; ++i) {
70 struct statfs *me = &entries[i];
71
72 mt = xzalloc(sizeof(struct mtab_list) + strlen(me->f_fstypename) +
73 strlen(me->f_mntonname) + strlen(me->f_mntfromname) + strlen("") + 4);
74 dlist_add_nomalloc((void *)&mtlist, (void *)mt);
75
76 // Collect details about mounted filesystem.
77 // Don't report errors, just leave data zeroed.
78 stat(me->f_mntonname, &(mt->stat));
79 statvfs(me->f_mntonname, &(mt->statvfs));
80
81 // Remember information from struct statfs.
82 mt->dir = stpcpy(mt->type, me->f_fstypename)+1;
83 mt->device = stpcpy(mt->dir, me->f_mntonname)+1;
84 mt->opts = stpcpy(mt->device, me->f_mntfromname)+1;
85 strcpy(mt->opts, ""); /* TODO: reverse from f_flags? */
86 }
87
88 return mtlist;
89 }
90
91 #else
92
93 #include <mntent.h>
94
octal_deslash(char * s)95 static void octal_deslash(char *s)
96 {
97 char *o = s;
98
99 while (*s) {
100 if (*s == '\\') {
101 int i, oct = 0;
102
103 for (i = 1; i < 4; i++) {
104 if (!isdigit(s[i])) break;
105 oct = (oct<<3)+s[i]-'0';
106 }
107 if (i == 4) {
108 *o++ = oct;
109 s += i;
110 continue;
111 }
112 }
113 *o++ = *s++;
114 }
115
116 *o = 0;
117 }
118
119 // Check if this type matches list.
120 // Odd syntax: typelist all yes = if any, typelist all no = if none.
121
mountlist_istype(struct mtab_list * ml,char * typelist)122 int mountlist_istype(struct mtab_list *ml, char *typelist)
123 {
124 int len, skip;
125 char *t;
126
127 if (!typelist) return 1;
128
129 skip = strncmp(typelist, "no", 2);
130
131 for (;;) {
132 if (!(t = comma_iterate(&typelist, &len))) break;
133 if (!skip) {
134 // If one -t starts with "no", the rest must too
135 if (strncmp(t, "no", 2)) error_exit("bad typelist");
136 if (!strncmp(t+2, ml->type, len-2)) {
137 skip = 1;
138 break;
139 }
140 } else if (!strncmp(t, ml->type, len) && !ml->type[len]) {
141 skip = 0;
142 break;
143 }
144 }
145
146 return !skip;
147 }
148
xgetmountlist(char * path)149 struct mtab_list *xgetmountlist(char *path)
150 {
151 struct mtab_list *mtlist = 0, *mt;
152 struct mntent *me;
153 FILE *fp;
154 char *p = path ? path : "/proc/mounts";
155
156 if (!(fp = setmntent(p, "r"))) perror_exit("bad %s", p);
157
158 // The "test" part of the loop is done before the first time through and
159 // again after each "increment", so putting the actual load there avoids
160 // duplicating it. If the load was NULL, the loop stops.
161
162 while ((me = getmntent(fp))) {
163 mt = xzalloc(sizeof(struct mtab_list) + strlen(me->mnt_fsname) +
164 strlen(me->mnt_dir) + strlen(me->mnt_type) + strlen(me->mnt_opts) + 4);
165 dlist_add_nomalloc((void *)&mtlist, (void *)mt);
166
167 // Collect details about mounted filesystem
168 // Don't report errors, just leave data zeroed
169 if (!path) {
170 stat(me->mnt_dir, &(mt->stat));
171 statvfs(me->mnt_dir, &(mt->statvfs));
172 }
173
174 // Remember information from /proc/mounts
175 mt->dir = stpcpy(mt->type, me->mnt_type)+1;
176 mt->device = stpcpy(mt->dir, me->mnt_dir)+1;
177 mt->opts = stpcpy(mt->device, me->mnt_fsname)+1;
178 strcpy(mt->opts, me->mnt_opts);
179
180 octal_deslash(mt->dir);
181 octal_deslash(mt->device);
182 }
183 endmntent(fp);
184
185 return mtlist;
186 }
187
188 #endif
189
190 #ifdef __APPLE__
191
192 #include <sys/event.h>
193
xnotify_init(int max)194 struct xnotify *xnotify_init(int max)
195 {
196 struct xnotify *not = xzalloc(sizeof(struct xnotify));
197
198 not->max = max;
199 if ((not->kq = kqueue()) == -1) perror_exit("kqueue");
200 not->paths = xmalloc(max * sizeof(char *));
201 not->fds = xmalloc(max * sizeof(int));
202
203 return not;
204 }
205
xnotify_add(struct xnotify * not,int fd,char * path)206 int xnotify_add(struct xnotify *not, int fd, char *path)
207 {
208 struct kevent event;
209
210 if (not->count == not->max) error_exit("xnotify_add overflow");
211 EV_SET(&event, fd, EVFILT_VNODE, EV_ADD|EV_CLEAR, NOTE_WRITE, 0, NULL);
212 if (kevent(not->kq, &event, 1, NULL, 0, NULL) == -1 || event.flags & EV_ERROR)
213 return -1;
214 not->paths[not->count] = path;
215 not->fds[not->count++] = fd;
216
217 return 0;
218 }
219
xnotify_wait(struct xnotify * not,char ** path)220 int xnotify_wait(struct xnotify *not, char **path)
221 {
222 struct kevent event;
223 int i;
224
225 for (;;) {
226 if (kevent(not->kq, NULL, 0, &event, 1, NULL) != -1) {
227 // We get the fd for free, but still have to search for the path.
228 for (i = 0; i<not->count; i++) if (not->fds[i]==event.ident) {
229 *path = not->paths[i];
230
231 return event.ident;
232 }
233 }
234 }
235 }
236
237 #else
238
239 #include <sys/inotify.h>
240
xnotify_init(int max)241 struct xnotify *xnotify_init(int max)
242 {
243 struct xnotify *not = xzalloc(sizeof(struct xnotify));
244
245 not->max = max;
246 if ((not->kq = inotify_init()) < 0) perror_exit("inotify_init");
247 not->paths = xmalloc(max * sizeof(char *));
248 not->fds = xmalloc(max * 2 * sizeof(int));
249
250 return not;
251 }
252
xnotify_add(struct xnotify * not,int fd,char * path)253 int xnotify_add(struct xnotify *not, int fd, char *path)
254 {
255 int i = 2*not->count;
256
257 if (not->max == not->count) error_exit("xnotify_add overflow");
258 if ((not->fds[i] = inotify_add_watch(not->kq, path, IN_MODIFY))==-1)
259 return -1;
260 not->fds[i+1] = fd;
261 not->paths[not->count++] = path;
262
263 return 0;
264 }
265
xnotify_wait(struct xnotify * not,char ** path)266 int xnotify_wait(struct xnotify *not, char **path)
267 {
268 struct inotify_event ev;
269 int i;
270
271 for (;;) {
272 if (sizeof(ev)!=read(not->kq, &ev, sizeof(ev))) perror_exit("inotify");
273
274 for (i = 0; i<not->count; i++) if (ev.wd==not->fds[2*i]) {
275 *path = not->paths[i];
276
277 return not->fds[2*i+1];
278 }
279 }
280 }
281
282 #endif
283
284 #ifdef __APPLE__
285
xattr_get(const char * path,const char * name,void * value,size_t size)286 ssize_t xattr_get(const char *path, const char *name, void *value, size_t size)
287 {
288 return getxattr(path, name, value, size, 0, 0);
289 }
290
xattr_lget(const char * path,const char * name,void * value,size_t size)291 ssize_t xattr_lget(const char *path, const char *name, void *value, size_t size)
292 {
293 return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
294 }
295
xattr_fget(int fd,const char * name,void * value,size_t size)296 ssize_t xattr_fget(int fd, const char *name, void *value, size_t size)
297 {
298 return fgetxattr(fd, name, value, size, 0, 0);
299 }
300
xattr_list(const char * path,char * list,size_t size)301 ssize_t xattr_list(const char *path, char *list, size_t size)
302 {
303 return listxattr(path, list, size, 0);
304 }
305
xattr_llist(const char * path,char * list,size_t size)306 ssize_t xattr_llist(const char *path, char *list, size_t size)
307 {
308 return listxattr(path, list, size, XATTR_NOFOLLOW);
309 }
310
xattr_flist(int fd,char * list,size_t size)311 ssize_t xattr_flist(int fd, char *list, size_t size)
312 {
313 return flistxattr(fd, list, size, 0);
314 }
315
xattr_set(const char * path,const char * name,const void * value,size_t size,int flags)316 ssize_t xattr_set(const char* path, const char* name,
317 const void* value, size_t size, int flags)
318 {
319 return setxattr(path, name, value, size, 0, flags);
320 }
321
xattr_lset(const char * path,const char * name,const void * value,size_t size,int flags)322 ssize_t xattr_lset(const char* path, const char* name,
323 const void* value, size_t size, int flags)
324 {
325 return setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW);
326 }
327
xattr_fset(int fd,const char * name,const void * value,size_t size,int flags)328 ssize_t xattr_fset(int fd, const char* name,
329 const void* value, size_t size, int flags)
330 {
331 return fsetxattr(fd, name, value, size, 0, flags);
332 }
333
334 #else
335
xattr_get(const char * path,const char * name,void * value,size_t size)336 ssize_t xattr_get(const char *path, const char *name, void *value, size_t size)
337 {
338 return getxattr(path, name, value, size);
339 }
340
xattr_lget(const char * path,const char * name,void * value,size_t size)341 ssize_t xattr_lget(const char *path, const char *name, void *value, size_t size)
342 {
343 return lgetxattr(path, name, value, size);
344 }
345
xattr_fget(int fd,const char * name,void * value,size_t size)346 ssize_t xattr_fget(int fd, const char *name, void *value, size_t size)
347 {
348 return fgetxattr(fd, name, value, size);
349 }
350
xattr_list(const char * path,char * list,size_t size)351 ssize_t xattr_list(const char *path, char *list, size_t size)
352 {
353 return listxattr(path, list, size);
354 }
355
xattr_llist(const char * path,char * list,size_t size)356 ssize_t xattr_llist(const char *path, char *list, size_t size)
357 {
358 return llistxattr(path, list, size);
359 }
360
xattr_flist(int fd,char * list,size_t size)361 ssize_t xattr_flist(int fd, char *list, size_t size)
362 {
363 return flistxattr(fd, list, size);
364 }
365
xattr_set(const char * path,const char * name,const void * value,size_t size,int flags)366 ssize_t xattr_set(const char* path, const char* name,
367 const void* value, size_t size, int flags)
368 {
369 return setxattr(path, name, value, size, flags);
370 }
371
xattr_lset(const char * path,const char * name,const void * value,size_t size,int flags)372 ssize_t xattr_lset(const char* path, const char* name,
373 const void* value, size_t size, int flags)
374 {
375 return lsetxattr(path, name, value, size, flags);
376 }
377
xattr_fset(int fd,const char * name,const void * value,size_t size,int flags)378 ssize_t xattr_fset(int fd, const char* name,
379 const void* value, size_t size, int flags)
380 {
381 return fsetxattr(fd, name, value, size, flags);
382 }
383
384
385 #endif
386
387 #ifdef __APPLE__
388 // In the absence of a mknodat system call, fchdir to dirfd and back
389 // around a regular mknod call...
mknodat(int dirfd,const char * path,mode_t mode,dev_t dev)390 int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev)
391 {
392 int old_dirfd = open(".", O_RDONLY), result;
393
394 if (old_dirfd == -1 || fchdir(dirfd) == -1) return -1;
395 result = mknod(path, mode, dev);
396 if (fchdir(old_dirfd) == -1) perror_exit("mknodat couldn't return");
397 return result;
398 }
399 #endif
400
401 // Signals required by POSIX 2008:
402 // http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html
403
404 #define SIGNIFY(x) {SIG##x, #x}
405
406 static const struct signame signames[] = {
407 // POSIX
408 SIGNIFY(ABRT), SIGNIFY(ALRM), SIGNIFY(BUS),
409 SIGNIFY(FPE), SIGNIFY(HUP), SIGNIFY(ILL), SIGNIFY(INT), SIGNIFY(KILL),
410 SIGNIFY(PIPE), SIGNIFY(QUIT), SIGNIFY(SEGV), SIGNIFY(TERM),
411 SIGNIFY(USR1), SIGNIFY(USR2), SIGNIFY(SYS), SIGNIFY(TRAP),
412 SIGNIFY(VTALRM), SIGNIFY(XCPU), SIGNIFY(XFSZ),
413 // Non-POSIX signals that cause termination
414 SIGNIFY(PROF), SIGNIFY(IO),
415 #ifdef __linux__
416 SIGNIFY(STKFLT), SIGNIFY(POLL), SIGNIFY(PWR),
417 #elif defined(__APPLE__)
418 SIGNIFY(EMT), SIGNIFY(INFO),
419 #endif
420
421 // Note: sigatexit relies on all the signals with a default disposition that
422 // terminates the process coming *before* SIGCHLD.
423
424 // POSIX signals that don't cause termination
425 SIGNIFY(CHLD), SIGNIFY(CONT), SIGNIFY(STOP), SIGNIFY(TSTP),
426 SIGNIFY(TTIN), SIGNIFY(TTOU), SIGNIFY(URG),
427 // Non-POSIX signals that don't cause termination
428 SIGNIFY(WINCH),
429 };
430 int signames_len = ARRAY_LEN(signames);
431
432 #undef SIGNIFY
433
xsignal_all_killers(void * handler)434 void xsignal_all_killers(void *handler)
435 {
436 int i;
437
438 for (i=0; signames[i].num != SIGCHLD; i++)
439 if (signames[i].num != SIGKILL)
440 xsignal(signames[i].num, handler ? exit_signal : SIG_DFL);
441 }
442
443 // Convert a string like "9", "KILL", "SIGHUP", or "SIGRTMIN+2" to a number.
sig_to_num(char * sigstr)444 int sig_to_num(char *sigstr)
445 {
446 int i, offset;
447 char *s;
448
449 // Numeric?
450 i = estrtol(sigstr, &s, 10);
451 if (!errno && !*s) return i;
452
453 // Skip leading "SIG".
454 strcasestart(&sigstr, "sig");
455
456 // Named signal?
457 for (i=0; i<ARRAY_LEN(signames); i++)
458 if (!strcasecmp(sigstr, signames[i].name)) return signames[i].num;
459
460 // Real-time signal?
461 #ifdef SIGRTMIN
462 if (strcasestart(&sigstr, "rtmin")) i = SIGRTMIN;
463 else if (strcasestart(&sigstr, "rtmax")) i = SIGRTMAX;
464 else return -1;
465
466 // No offset?
467 if (!*sigstr) return i;
468
469 // We allow any offset that's still a real-time signal: SIGRTMIN+20 is fine.
470 // Others are more restrictive, only accepting what they show with -l.
471 offset = estrtol(sigstr, &s, 10);
472 if (errno || *s) return -1;
473 i += offset;
474 if (i >= SIGRTMIN && i <= SIGRTMAX) return i;
475 #endif
476
477 return -1;
478 }
479
num_to_sig(int sig)480 char *num_to_sig(int sig)
481 {
482 int i;
483
484 // A named signal?
485 for (i=0; i<signames_len; i++)
486 if (signames[i].num == sig) return signames[i].name;
487
488 // A real-time signal?
489 #ifdef SIGRTMIN
490 if (sig == SIGRTMIN) return "RTMIN";
491 if (sig == SIGRTMAX) return "RTMAX";
492 if (sig > SIGRTMIN && sig < SIGRTMAX) {
493 if (sig-SIGRTMIN <= SIGRTMAX-sig) sprintf(libbuf, "RTMIN+%d", sig-SIGRTMIN);
494 else sprintf(libbuf, "RTMAX-%d", SIGRTMAX-sig);
495 return libbuf;
496 }
497 #endif
498
499 return NULL;
500 }
501