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)33 void xgetrandom(void *buf, unsigned buflen)
34 {
35 int fd;
36
37 // Linux keeps getrandom() in <sys/random.h> and getentropy() in <unistd.h>
38 // BSD/macOS only has getentropy(), but it's in <sys/random.h> (to be fair,
39 // they were there first). getrandom() and getentropy() both went into glibc
40 // in the same release (2.25 in 2017), so this test still works.
41 #if __has_include(<sys/random.h>)
42 while (buflen) {
43 if (getentropy(buf, fd = buflen>256 ? 256 : buflen)) break;
44 buflen -= fd;
45 buf += fd;
46 }
47 if (!buflen) return;
48 if (errno!=ENOSYS) perror_exit("getrandom");
49 #endif
50 xreadall(fd = xopen("/dev/urandom", O_RDONLY), buf, buflen);
51 close(fd);
52 }
53
54 // Get list of mounted filesystems, including stat and statvfs info.
55 // Returns a reversed list, which is good for finding overmounts and such.
56
57 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
58
59 #include <sys/mount.h>
60
xgetmountlist(char * path)61 struct mtab_list *xgetmountlist(char *path)
62 {
63 struct mtab_list *mtlist = 0, *mt;
64 struct statfs *entries;
65 int i, count;
66
67 if (path) error_exit("xgetmountlist");
68 if ((count = getmntinfo(&entries, 0)) == 0) perror_exit("getmntinfo");
69
70 // The "test" part of the loop is done before the first time through and
71 // again after each "increment", so putting the actual load there avoids
72 // duplicating it. If the load was NULL, the loop stops.
73
74 for (i = 0; i < count; ++i) {
75 struct statfs *me = &entries[i];
76
77 mt = xzalloc(sizeof(struct mtab_list) + strlen(me->f_fstypename) +
78 strlen(me->f_mntonname) + strlen(me->f_mntfromname) + strlen("") + 4);
79 dlist_add_nomalloc((void *)&mtlist, (void *)mt);
80
81 // Collect details about mounted filesystem.
82 // Don't report errors, just leave data zeroed.
83 stat(me->f_mntonname, &(mt->stat));
84 statvfs(me->f_mntonname, &(mt->statvfs));
85
86 // Remember information from struct statfs.
87 mt->dir = stpcpy(mt->type, me->f_fstypename)+1;
88 mt->device = stpcpy(mt->dir, me->f_mntonname)+1;
89 mt->opts = stpcpy(mt->device, me->f_mntfromname)+1;
90 strcpy(mt->opts, ""); /* TODO: reverse from f_flags? */
91 }
92
93 return mtlist;
94 }
95
96 #else
97
98 #include <mntent.h>
99
100 // Check if this type matches list.
101 // Odd syntax: typelist all yes = if any, typelist all no = if none.
102
mountlist_istype(struct mtab_list * ml,char * typelist)103 int mountlist_istype(struct mtab_list *ml, char *typelist)
104 {
105 int len, skip;
106 char *t;
107
108 if (!typelist) return 1;
109
110 // leading "no" indicates whether entire list is inverted
111 skip = strncmp(typelist, "no", 2);
112
113 for (;;) {
114 if (!(t = comma_iterate(&typelist, &len))) break;
115 if (!skip) {
116 // later "no" after first are ignored
117 strstart(&t, "no");
118 if (!strncmp(t, ml->type, len-2)) {
119 skip = 1;
120 break;
121 }
122 } else if (!strncmp(t, ml->type, len) && !ml->type[len]) {
123 skip = 0;
124 break;
125 }
126 }
127
128 return !skip;
129 }
130
xgetmountlist(char * path)131 struct mtab_list *xgetmountlist(char *path)
132 {
133 struct mtab_list *mtlist = 0, *mt;
134 struct mntent *me;
135 FILE *fp;
136 char *p = path ? path : "/proc/mounts";
137
138 if (!(fp = setmntent(p, "r"))) perror_exit("bad %s", p);
139
140 // The "test" part of the loop is done before the first time through and
141 // again after each "increment", so putting the actual load there avoids
142 // duplicating it. If the load was NULL, the loop stops.
143
144 while ((me = getmntent(fp))) {
145 mt = xzalloc(sizeof(struct mtab_list) + strlen(me->mnt_fsname) +
146 strlen(me->mnt_dir) + strlen(me->mnt_type) + strlen(me->mnt_opts) + 4);
147 dlist_add_nomalloc((void *)&mtlist, (void *)mt);
148
149 // Collect details about mounted filesystem
150 // Don't report errors, just leave data zeroed
151 if (!path) {
152 stat(me->mnt_dir, &(mt->stat));
153 statvfs(me->mnt_dir, &(mt->statvfs));
154 }
155
156 // Remember information from /proc/mounts
157 mt->dir = stpcpy(mt->type, me->mnt_type)+1;
158 mt->device = stpcpy(mt->dir, me->mnt_dir)+1;
159 mt->opts = stpcpy(mt->device, me->mnt_fsname)+1;
160 strcpy(mt->opts, me->mnt_opts);
161
162 octal_deslash(mt->dir);
163 octal_deslash(mt->device);
164 }
165 endmntent(fp);
166
167 return mtlist;
168 }
169
170 #endif
171
172 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
173
174 #include <sys/event.h>
175
xnotify_init(int max)176 struct xnotify *xnotify_init(int max)
177 {
178 struct xnotify *not = xzalloc(sizeof(struct xnotify));
179
180 not->max = max;
181 if ((not->kq = kqueue()) == -1) perror_exit("kqueue");
182 not->paths = xmalloc(max * sizeof(char *));
183 not->fds = xmalloc(max * sizeof(int));
184
185 return not;
186 }
187
xnotify_add(struct xnotify * not,int fd,char * path)188 int xnotify_add(struct xnotify *not, int fd, char *path)
189 {
190 struct kevent event;
191
192 if (not->count == not->max) error_exit("xnotify_add overflow");
193 EV_SET(&event, fd, EVFILT_VNODE, EV_ADD|EV_CLEAR, NOTE_WRITE, 0, NULL);
194 if (kevent(not->kq, &event, 1, NULL, 0, NULL) == -1 || event.flags & EV_ERROR)
195 error_exit("xnotify_add failed on %s", path);
196 not->paths[not->count] = path;
197 not->fds[not->count++] = fd;
198
199 return 0;
200 }
201
xnotify_wait(struct xnotify * not,char ** path)202 int xnotify_wait(struct xnotify *not, char **path)
203 {
204 struct kevent event;
205 int i;
206
207 for (;;) {
208 if (kevent(not->kq, NULL, 0, &event, 1, NULL) != -1) {
209 // We get the fd for free, but still have to search for the path.
210 for (i = 0; i<not->count; i++) if (not->fds[i]==event.ident) {
211 *path = not->paths[i];
212
213 return event.ident;
214 }
215 }
216 }
217 }
218
219 #else
220
221 #include <sys/inotify.h>
222
xnotify_init(int max)223 struct xnotify *xnotify_init(int max)
224 {
225 struct xnotify *not = xzalloc(sizeof(struct xnotify));
226
227 not->max = max;
228 if ((not->kq = inotify_init()) < 0) perror_exit("inotify_init");
229 not->paths = xmalloc(max * sizeof(char *));
230 not->fds = xmalloc(max * 2 * sizeof(int));
231
232 return not;
233 }
234
xnotify_add(struct xnotify * not,int fd,char * path)235 int xnotify_add(struct xnotify *not, int fd, char *path)
236 {
237 int i = 2*not->count;
238
239 if (not->max == not->count) error_exit("xnotify_add overflow");
240 if ((not->fds[i] = inotify_add_watch(not->kq, path, IN_MODIFY))==-1)
241 perror_exit("xnotify_add failed on %s", path);
242 not->fds[i+1] = fd;
243 not->paths[not->count++] = path;
244
245 return 0;
246 }
247
xnotify_wait(struct xnotify * not,char ** path)248 int xnotify_wait(struct xnotify *not, char **path)
249 {
250 struct inotify_event ev;
251 int i;
252
253 for (;;) {
254 if (sizeof(ev)!=read(not->kq, &ev, sizeof(ev))) perror_exit("inotify");
255
256 for (i = 0; i<not->count; i++) if (ev.wd==not->fds[2*i]) {
257 *path = not->paths[i];
258
259 return not->fds[2*i+1];
260 }
261 }
262 }
263
264 #endif
265
266 #ifdef __APPLE__
267
xattr_get(const char * path,const char * name,void * value,size_t size)268 ssize_t xattr_get(const char *path, const char *name, void *value, size_t size)
269 {
270 return getxattr(path, name, value, size, 0, 0);
271 }
272
xattr_lget(const char * path,const char * name,void * value,size_t size)273 ssize_t xattr_lget(const char *path, const char *name, void *value, size_t size)
274 {
275 return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
276 }
277
xattr_fget(int fd,const char * name,void * value,size_t size)278 ssize_t xattr_fget(int fd, const char *name, void *value, size_t size)
279 {
280 return fgetxattr(fd, name, value, size, 0, 0);
281 }
282
xattr_list(const char * path,char * list,size_t size)283 ssize_t xattr_list(const char *path, char *list, size_t size)
284 {
285 return listxattr(path, list, size, 0);
286 }
287
xattr_llist(const char * path,char * list,size_t size)288 ssize_t xattr_llist(const char *path, char *list, size_t size)
289 {
290 return listxattr(path, list, size, XATTR_NOFOLLOW);
291 }
292
xattr_flist(int fd,char * list,size_t size)293 ssize_t xattr_flist(int fd, char *list, size_t size)
294 {
295 return flistxattr(fd, list, size, 0);
296 }
297
xattr_set(const char * path,const char * name,const void * value,size_t size,int flags)298 ssize_t xattr_set(const char* path, const char* name,
299 const void* value, size_t size, int flags)
300 {
301 return setxattr(path, name, value, size, 0, flags);
302 }
303
xattr_lset(const char * path,const char * name,const void * value,size_t size,int flags)304 ssize_t xattr_lset(const char* path, const char* name,
305 const void* value, size_t size, int flags)
306 {
307 return setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW);
308 }
309
xattr_fset(int fd,const char * name,const void * value,size_t size,int flags)310 ssize_t xattr_fset(int fd, const char* name,
311 const void* value, size_t size, int flags)
312 {
313 return fsetxattr(fd, name, value, size, 0, flags);
314 }
315
316 #elif !defined(__FreeBSD__) && !defined(__OpenBSD__)
317
xattr_get(const char * path,const char * name,void * value,size_t size)318 ssize_t xattr_get(const char *path, const char *name, void *value, size_t size)
319 {
320 return getxattr(path, name, value, size);
321 }
322
xattr_lget(const char * path,const char * name,void * value,size_t size)323 ssize_t xattr_lget(const char *path, const char *name, void *value, size_t size)
324 {
325 return lgetxattr(path, name, value, size);
326 }
327
xattr_fget(int fd,const char * name,void * value,size_t size)328 ssize_t xattr_fget(int fd, const char *name, void *value, size_t size)
329 {
330 return fgetxattr(fd, name, value, size);
331 }
332
xattr_list(const char * path,char * list,size_t size)333 ssize_t xattr_list(const char *path, char *list, size_t size)
334 {
335 return listxattr(path, list, size);
336 }
337
xattr_llist(const char * path,char * list,size_t size)338 ssize_t xattr_llist(const char *path, char *list, size_t size)
339 {
340 return llistxattr(path, list, size);
341 }
342
xattr_flist(int fd,char * list,size_t size)343 ssize_t xattr_flist(int fd, char *list, size_t size)
344 {
345 return flistxattr(fd, list, size);
346 }
347
xattr_set(const char * path,const char * name,const void * value,size_t size,int flags)348 ssize_t xattr_set(const char* path, const char* name,
349 const void* value, size_t size, int flags)
350 {
351 return setxattr(path, name, value, size, flags);
352 }
353
xattr_lset(const char * path,const char * name,const void * value,size_t size,int flags)354 ssize_t xattr_lset(const char* path, const char* name,
355 const void* value, size_t size, int flags)
356 {
357 return lsetxattr(path, name, value, size, flags);
358 }
359
xattr_fset(int fd,const char * name,const void * value,size_t size,int flags)360 ssize_t xattr_fset(int fd, const char* name,
361 const void* value, size_t size, int flags)
362 {
363 return fsetxattr(fd, name, value, size, flags);
364 }
365
366
367 #endif
368
369 #ifdef __APPLE__
370 // In the absence of a mknodat system call, fchdir to dirfd and back
371 // around a regular mknod call...
mknodat(int dirfd,const char * path,mode_t mode,dev_t dev)372 int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev)
373 {
374 int old_dirfd = open(".", O_RDONLY), result;
375
376 if (old_dirfd == -1 || fchdir(dirfd) == -1) return -1;
377 result = mknod(path, mode, dev);
378 if (fchdir(old_dirfd) == -1) perror_exit("mknodat couldn't return");
379 return result;
380 }
381
382 // As of 10.15, macOS offers an fcntl F_PREALLOCATE rather than fallocate()
383 // or posix_fallocate() calls.
posix_fallocate(int fd,off_t offset,off_t length)384 int posix_fallocate(int fd, off_t offset, off_t length)
385 {
386 int e = errno, result;
387 fstore_t f;
388
389 f.fst_flags = F_ALLOCATEALL;
390 f.fst_posmode = F_PEOFPOSMODE;
391 f.fst_offset = offset;
392 f.fst_length = length;
393 if (fcntl(fd, F_PREALLOCATE, &f) == -1) result = errno;
394 else result = ftruncate(fd, length);
395 errno = e;
396 return result;
397 }
398 #endif
399
400 // Signals required by POSIX 2008:
401 // http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html
402
403 #define SIGNIFY(x) {SIG##x, #x}
404
405 static const struct signame signames[] = {
406 {0, "0"},
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 // signals only present/absent on some targets (mips and macos)
416 #ifdef SIGEMT
417 SIGNIFY(EMT),
418 #endif
419 #ifdef SIGINFO
420 SIGNIFY(INFO),
421 #endif
422 #ifdef SIGPOLL
423 SIGNIFY(POLL),
424 #endif
425 #ifdef SIGPWR
426 SIGNIFY(PWR),
427 #endif
428 #ifdef SIGSTKFLT
429 SIGNIFY(STKFLT),
430 #endif
431
432 // Note: sigatexit relies on all the signals with a default disposition that
433 // terminates the process coming *before* SIGCHLD.
434
435 // POSIX signals that don't cause termination
436 SIGNIFY(CHLD), SIGNIFY(CONT), SIGNIFY(STOP), SIGNIFY(TSTP),
437 SIGNIFY(TTIN), SIGNIFY(TTOU), SIGNIFY(URG),
438 // Non-POSIX signals that don't cause termination
439 SIGNIFY(WINCH),
440 };
441
442 #undef SIGNIFY
443
xsignal_all_killers(void * handler)444 void xsignal_all_killers(void *handler)
445 {
446 int i;
447
448 for (i = 1; signames[i].num != SIGCHLD; i++)
449 if (signames[i].num != SIGKILL) xsignal(signames[i].num, handler);
450 }
451
452 // Convert a string like "9", "KILL", "SIGHUP", or "SIGRTMIN+2" to a number.
sig_to_num(char * sigstr)453 int sig_to_num(char *sigstr)
454 {
455 int i, offset;
456 char *s;
457
458 // Numeric?
459 offset = estrtol(sigstr, &s, 10);
460 if (!errno && !*s) return offset;
461
462 // Skip leading "SIG".
463 strcasestart(&sigstr, "sig");
464
465 // Named signal?
466 for (i=0; i<ARRAY_LEN(signames); i++)
467 if (!strcasecmp(sigstr, signames[i].name)) return signames[i].num;
468
469 // Real-time signal?
470 #ifdef SIGRTMIN
471 if (strcasestart(&sigstr, "rtmin")) i = SIGRTMIN;
472 else if (strcasestart(&sigstr, "rtmax")) i = SIGRTMAX;
473 else return -1;
474
475 // No offset?
476 if (!*sigstr) return i;
477
478 // We allow any offset that's still a real-time signal: SIGRTMIN+20 is fine.
479 // Others are more restrictive, only accepting what they show with -l.
480 offset = estrtol(sigstr, &s, 10);
481 if (errno || *s) return -1;
482 i += offset;
483 if (i >= SIGRTMIN && i <= SIGRTMAX) return i;
484 #endif
485
486 return -1;
487 }
488
num_to_sig(int sig)489 char *num_to_sig(int sig)
490 {
491 int i;
492
493 // A named signal?
494 for (i=0; i<ARRAY_LEN(signames); i++)
495 if (signames[i].num == sig) return signames[i].name;
496
497 // A real-time signal?
498 #ifdef SIGRTMIN
499 if (sig == SIGRTMIN) return "RTMIN";
500 if (sig == SIGRTMAX) return "RTMAX";
501 if (sig > SIGRTMIN && sig < SIGRTMAX) {
502 if (sig-SIGRTMIN <= SIGRTMAX-sig) sprintf(libbuf, "RTMIN+%d", sig-SIGRTMIN);
503 else sprintf(libbuf, "RTMAX-%d", SIGRTMAX-sig);
504 return libbuf;
505 }
506 #endif
507
508 return NULL;
509 }
510
dev_minor(int dev)511 int dev_minor(int dev)
512 {
513 #if defined(__linux__)
514 return ((dev&0xfff00000)>>12)|(dev&0xff);
515 #elif defined(__APPLE__)
516 return dev&0xffffff;
517 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
518 return minor(dev);
519 #else
520 #error
521 #endif
522 }
523
dev_major(int dev)524 int dev_major(int dev)
525 {
526 #if defined(__linux__)
527 return (dev&0xfff00)>>8;
528 #elif defined(__APPLE__)
529 return (dev>>24)&0xff;
530 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
531 return major(dev);
532 #else
533 #error
534 #endif
535 }
536
dev_makedev(int major,int minor)537 int dev_makedev(int major, int minor)
538 {
539 #if defined(__linux__)
540 return (minor&0xff)|((major&0xfff)<<8)|((minor&0xfff00)<<12);
541 #elif defined(__APPLE__)
542 return (minor&0xffffff)|((major&0xff)<<24);
543 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
544 return makedev(major, minor);
545 #else
546 #error
547 #endif
548 }
549
fs_type_name(struct statfs * statfs)550 char *fs_type_name(struct statfs *statfs)
551 {
552 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
553 // macOS has an `f_type` field, but assigns values dynamically as filesystems
554 // are registered. They do give you the name directly though, so use that.
555 return statfs->f_fstypename;
556 #else
557 char *s = NULL;
558 struct {unsigned num; char *name;} nn[] = {
559 {0xADFF, "affs"}, {0x5346544e, "ntfs"}, {0x1Cd1, "devpts"},
560 {0x137D, "ext"}, {0xEF51, "ext2"}, {0xEF53, "ext3"},
561 {0x1BADFACE, "bfs"}, {0x9123683E, "btrfs"}, {0x28cd3d45, "cramfs"},
562 {0x3153464a, "jfs"}, {0x7275, "romfs"}, {0x01021994, "tmpfs"},
563 {0x3434, "nilfs"}, {0x6969, "nfs"}, {0x9fa0, "proc"},
564 {0x534F434B, "sockfs"}, {0x62656572, "sysfs"}, {0x517B, "smb"},
565 {0x4d44, "msdos"}, {0x4006, "fat"}, {0x43415d53, "smackfs"},
566 {0x73717368, "squashfs"}, {0xF2F52010, "f2fs"}, {0xE0F5E1E2, "erofs"},
567 {0x2011BAB0, "exfat"},
568 };
569 int i;
570
571 for (i=0; i<ARRAY_LEN(nn); i++)
572 if (nn[i].num == statfs->f_type) s = nn[i].name;
573 if (!s) sprintf(s = libbuf, "0x%x", (unsigned)statfs->f_type);
574 return s;
575 #endif
576 }
577
578 #if defined(__APPLE__)
579 #include <sys/disk.h>
get_block_device_size(int fd,unsigned long long * size)580 int get_block_device_size(int fd, unsigned long long* size)
581 {
582 unsigned long block_size, block_count;
583
584 if (!ioctl(fd, DKIOCGETBLOCKSIZE, &block_size) &&
585 !ioctl(fd, DKIOCGETBLOCKCOUNT, &block_count)) {
586 *size = block_count * block_size;
587 return 1;
588 }
589 return 0;
590 }
591 #elif defined(__linux__)
get_block_device_size(int fd,unsigned long long * size)592 int get_block_device_size(int fd, unsigned long long* size)
593 {
594 return (ioctl(fd, BLKGETSIZE64, size) >= 0);
595 }
596 #elif defined(__OpenBSD__)
597 #include <sys/dkio.h>
598 #include <sys/disklabel.h>
get_block_device_size(int fd,unsigned long long * size)599 int get_block_device_size(int fd, unsigned long long* size)
600 {
601 struct disklabel lab;
602 int status = (ioctl(fd, DIOCGDINFO, &lab) >= 0);
603 *size = lab.d_secsize * lab.d_nsectors;
604 return status;
605 }
606 #else
get_block_device_size(int fd,unsigned long long * size)607 int get_block_device_size(int fd, unsigned long long* size)
608 {
609 return 0;
610 }
611 #endif
612
613 // Return bytes copied from in to out. If bytes <0 copy all of in to out.
614 // If consumed isn't null, amount read saved there (return is written or error)
sendfile_len(int in,int out,long long bytes,long long * consumed)615 long long sendfile_len(int in, int out, long long bytes, long long *consumed)
616 {
617 long long total = 0, len, ww;
618 int try_cfr = 1;
619
620 if (consumed) *consumed = 0;
621 if (in>=0) while (bytes != total) {
622 ww = 0;
623 len = bytes-total;
624
625 errno = 0;
626 if (try_cfr) {
627 if (bytes<0 || bytes>(1<<30)) len = (1<<30);
628 // glibc added this constant in git at the end of 2017, shipped 2018-02.
629 // Android's had the constant for years, but you'll get SIGSYS if you use
630 // this system call before Android U (2023's release).
631 #if defined(__NR_copy_file_range) && !defined(__ANDROID__)
632 len = syscall(__NR_copy_file_range, in, 0, out, 0, len, 0);
633 #else
634 errno = EINVAL;
635 len = -1;
636 #endif
637 if (len < 0) {
638 try_cfr = 0;
639
640 continue;
641 }
642 } else {
643 if (bytes<0 || len>sizeof(libbuf)) len = sizeof(libbuf);
644 ww = len = read(in, libbuf, len);
645 }
646 if (len<1 && errno==EAGAIN) continue;
647 if (len<1) break;
648 if (consumed) *consumed += len;
649 if (ww && writeall(out, libbuf, len) != len) return -1;
650 total += len;
651 }
652
653 return total;
654 }
655
656 #ifdef __APPLE__
657 // The absolute minimum POSIX timer implementation to build timeout(1).
658 // Note that although timeout(1) uses POSIX timers to get the monotonic clock,
659 // that doesn't seem to be an option on macOS (without using other libraries),
660 // so we just mangle that back into a regular setitimer(ITIMER_REAL) call.
timer_create(clock_t c,struct sigevent * se,timer_t * t)661 int timer_create(clock_t c, struct sigevent *se, timer_t *t)
662 {
663 if (se->sigev_notify != SIGEV_SIGNAL || se->sigev_signo != SIGALRM)
664 error_exit("unimplemented");
665 *t = 1;
666 return 0;
667 }
668
timer_settime(timer_t t,int flags,struct itimerspec * new,void * old)669 int timer_settime(timer_t t, int flags, struct itimerspec *new, void *old)
670 {
671 struct itimerval mangled;
672
673 if (flags != 0 || old != 0) error_exit("unimplemented");
674 memset(&mangled, 0, sizeof(mangled));
675 mangled.it_value.tv_sec = new->it_value.tv_sec;
676 mangled.it_value.tv_usec = new->it_value.tv_nsec / 1000;
677 return setitimer(ITIMER_REAL, &mangled, NULL);
678 }
679 // glibc requires -lrt for linux syscalls, which pulls in libgcc_eh.a for
680 // static linking, and gcc 9.3 leaks pthread calls from that breaking the build
681 // These are both just linux syscalls: wrap them ourselves
682 #elif defined(__GLIBC__)
timer_create_wrap(clockid_t c,struct sigevent * se,timer_t * t)683 int timer_create_wrap(clockid_t c, struct sigevent *se, timer_t *t)
684 {
685 // convert overengineered structure to what kernel actually uses
686 struct ksigevent { void *sv; int signo, notify, tid; } kk = {
687 0, se->sigev_signo, se->sigev_notify, 0
688 };
689 int timer;
690
691 if (syscall(SYS_timer_create, c, &kk, &timer)<0) return -1;
692 *t = (timer_t)(long)timer;
693
694 return 0;
695 }
696
697 #if !defined(SYS_timer_settime) && defined(SYS_timer_settime64)
698 // glibc does not define defines SYS_timer_settime on 32-bit systems
699 // with 64-bit time_t defaults e.g. riscv32
700 #define SYS_timer_settime SYS_timer_settime64
701 #endif
702
timer_settime_wrap(timer_t t,int flags,struct itimerspec * val,struct itimerspec * old)703 int timer_settime_wrap(timer_t t, int flags, struct itimerspec *val,
704 struct itimerspec *old)
705 {
706 return syscall(SYS_timer_settime, t, flags, val, old);
707 }
708 #endif
709