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