summaryrefslogtreecommitdiff
path: root/runit/runit_lib.c
diff options
context:
space:
mode:
Diffstat (limited to 'runit/runit_lib.c')
-rw-r--r--runit/runit_lib.c999
1 files changed, 999 insertions, 0 deletions
diff --git a/runit/runit_lib.c b/runit/runit_lib.c
new file mode 100644
index 0000000..322bef8
--- /dev/null
+++ b/runit/runit_lib.c
@@ -0,0 +1,999 @@
+/*
+Copyright (c) 2001-2006, Gerrit Pape
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
+/* Collected into one file from runit's many tiny files */
+/* TODO: review, eliminate unneeded stuff, move good stuff to libbb */
+
+#include <sys/poll.h>
+#include <sys/file.h>
+#include "libbb.h"
+#include "runit_lib.h"
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK O_NDELAY
+#endif
+
+/*** buffer.c ***/
+
+void buffer_init(buffer *s,int (*op)(int fd,char *buf,unsigned len),int fd,char *buf,unsigned len)
+{
+ s->x = buf;
+ s->fd = fd;
+ s->op = op;
+ s->p = 0;
+ s->n = len;
+}
+
+
+/*** buffer_get.c ***/
+
+static int oneread(int (*op)(int fd,char *buf,unsigned len),int fd,char *buf,unsigned len)
+{
+ int r;
+
+ for (;;) {
+ r = op(fd,buf,len);
+ if (r == -1) if (errno == EINTR) continue;
+ return r;
+ }
+}
+
+static int getthis(buffer *s,char *buf,unsigned len)
+{
+ if (len > s->p) len = s->p;
+ s->p -= len;
+ memcpy(buf,s->x + s->n,len);
+ s->n += len;
+ return len;
+}
+
+int buffer_feed(buffer *s)
+{
+ int r;
+
+ if (s->p) return s->p;
+ r = oneread(s->op,s->fd,s->x,s->n);
+ if (r <= 0) return r;
+ s->p = r;
+ s->n -= r;
+ if (s->n > 0) memmove(s->x + s->n,s->x,r);
+ return r;
+}
+
+int buffer_bget(buffer *s,char *buf,unsigned len)
+{
+ int r;
+
+ if (s->p > 0) return getthis(s,buf,len);
+ if (s->n <= len) return oneread(s->op,s->fd,buf,s->n);
+ r = buffer_feed(s); if (r <= 0) return r;
+ return getthis(s,buf,len);
+}
+
+int buffer_get(buffer *s,char *buf,unsigned len)
+{
+ int r;
+
+ if (s->p > 0) return getthis(s,buf,len);
+ if (s->n <= len) return oneread(s->op,s->fd,buf,len);
+ r = buffer_feed(s); if (r <= 0) return r;
+ return getthis(s,buf,len);
+}
+
+char *buffer_peek(buffer *s)
+{
+ return s->x + s->n;
+}
+
+void buffer_seek(buffer *s,unsigned len)
+{
+ s->n += len;
+ s->p -= len;
+}
+
+
+/*** buffer_put.c ***/
+
+static int allwrite(int (*op)(int fd,char *buf,unsigned len),int fd,const char *buf,unsigned len)
+{
+ int w;
+
+ while (len) {
+ w = op(fd,(char*)buf,len);
+ if (w == -1) {
+ if (errno == EINTR) continue;
+ return -1; /* note that some data may have been written */
+ }
+ if (w == 0) ; /* luser's fault */
+ buf += w;
+ len -= w;
+ }
+ return 0;
+}
+
+int buffer_flush(buffer *s)
+{
+ int p;
+
+ p = s->p;
+ if (!p) return 0;
+ s->p = 0;
+ return allwrite(s->op,s->fd,s->x,p);
+}
+
+int buffer_putalign(buffer *s,const char *buf,unsigned len)
+{
+ unsigned n;
+
+ while (len > (n = s->n - s->p)) {
+ memcpy(s->x + s->p,buf,n);
+ s->p += n;
+ buf += n;
+ len -= n;
+ if (buffer_flush(s) == -1) return -1;
+ }
+ /* now len <= s->n - s->p */
+ memcpy(s->x + s->p,buf,len);
+ s->p += len;
+ return 0;
+}
+
+int buffer_put(buffer *s,const char *buf,unsigned len)
+{
+ unsigned n;
+
+ n = s->n;
+ if (len > n - s->p) {
+ if (buffer_flush(s) == -1) return -1;
+ /* now s->p == 0 */
+ if (n < BUFFER_OUTSIZE) n = BUFFER_OUTSIZE;
+ while (len > s->n) {
+ if (n > len) n = len;
+ if (allwrite(s->op,s->fd,buf,n) == -1) return -1;
+ buf += n;
+ len -= n;
+ }
+ }
+ /* now len <= s->n - s->p */
+ memcpy(s->x + s->p,buf,len);
+ s->p += len;
+ return 0;
+}
+
+int buffer_putflush(buffer *s,const char *buf,unsigned len)
+{
+ if (buffer_flush(s) == -1) return -1;
+ return allwrite(s->op,s->fd,buf,len);
+}
+
+int buffer_putsalign(buffer *s,const char *buf)
+{
+ return buffer_putalign(s,buf,strlen(buf));
+}
+
+int buffer_puts(buffer *s,const char *buf)
+{
+ return buffer_put(s,buf,strlen(buf));
+}
+
+int buffer_putsflush(buffer *s,const char *buf)
+{
+ return buffer_putflush(s,buf,strlen(buf));
+}
+
+
+/*** buffer_read.c ***/
+
+int buffer_unixread(int fd,char *buf,unsigned len)
+{
+ return read(fd,buf,len);
+}
+
+
+/*** buffer_write.c ***/
+
+int buffer_unixwrite(int fd,char *buf,unsigned len)
+{
+ return write(fd,buf,len);
+}
+
+
+/*** byte_chr.c ***/
+
+unsigned byte_chr(char *s,unsigned n,int c)
+{
+ char ch;
+ char *t;
+
+ ch = c;
+ t = s;
+ for (;;) {
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ }
+ return t - s;
+}
+
+
+/*** coe.c ***/
+
+int coe(int fd)
+{
+ return fcntl(fd,F_SETFD,FD_CLOEXEC);
+}
+
+
+/*** fd_copy.c ***/
+
+int fd_copy(int to,int from)
+{
+ if (to == from) return 0;
+ if (fcntl(from,F_GETFL,0) == -1) return -1;
+ close(to);
+ if (fcntl(from,F_DUPFD,to) == -1) return -1;
+ return 0;
+}
+
+
+/*** fd_move.c ***/
+
+int fd_move(int to,int from)
+{
+ if (to == from) return 0;
+ if (fd_copy(to,from) == -1) return -1;
+ close(from);
+ return 0;
+}
+
+
+/*** fifo.c ***/
+
+int fifo_make(const char *fn,int mode) { return mkfifo(fn,mode); }
+
+
+/*** fmt_ptime.c ***/
+
+unsigned fmt_ptime(char *s, struct taia *ta) {
+ struct tm *t;
+ unsigned long u;
+
+ if (ta->sec.x < 4611686018427387914ULL) return 0; /* impossible? */
+ u = ta->sec.x -4611686018427387914ULL;
+ if (!(t = gmtime((time_t*)&u))) return 0;
+ fmt_ulong(s, 1900 + t->tm_year);
+ s[4] = '-'; fmt_uint0(&s[5], t->tm_mon+1, 2);
+ s[7] = '-'; fmt_uint0(&s[8], t->tm_mday, 2);
+ s[10] = '_'; fmt_uint0(&s[11], t->tm_hour, 2);
+ s[13] = ':'; fmt_uint0(&s[14], t->tm_min, 2);
+ s[16] = ':'; fmt_uint0(&s[17], t->tm_sec, 2);
+ s[19] = '.'; fmt_uint0(&s[20], ta->nano, 9);
+ return 25;
+}
+
+unsigned fmt_taia(char *s, struct taia *t) {
+ static char hex[16] = "0123456789abcdef";
+ static char pack[TAIA_PACK];
+ int i;
+
+ taia_pack(pack, t);
+ s[0] = '@';
+ for (i = 0; i < 12; ++i) {
+ s[i*2+1] = hex[(pack[i] >> 4) &15];
+ s[i*2+2] = hex[pack[i] &15];
+ }
+ return 25;
+}
+
+
+/*** fmt_uint.c ***/
+
+unsigned fmt_uint(char *s,unsigned u)
+{
+ return fmt_ulong(s,u);
+}
+
+
+/*** fmt_uint0.c ***/
+
+unsigned fmt_uint0(char *s,unsigned u,unsigned n)
+{
+ unsigned len;
+ len = fmt_uint(FMT_LEN,u);
+ while (len < n) { if (s) *s++ = '0'; ++len; }
+ if (s) fmt_uint(s,u);
+ return len;
+}
+
+
+/*** fmt_ulong.c ***/
+
+unsigned fmt_ulong(char *s,unsigned long u)
+{
+ unsigned len; unsigned long q;
+ len = 1; q = u;
+ while (q > 9) { ++len; q /= 10; }
+ if (s) {
+ s += len;
+ do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */
+ }
+ return len;
+}
+
+
+/*** tai_now.c ***/
+
+void tai_now(struct tai *t)
+{
+ tai_unix(t,time((time_t *) 0));
+}
+
+
+/*** tai_pack.c ***/
+
+void tai_pack(char *s,const struct tai *t)
+{
+ uint64_t x;
+
+ x = t->x;
+ s[7] = x & 255; x >>= 8;
+ s[6] = x & 255; x >>= 8;
+ s[5] = x & 255; x >>= 8;
+ s[4] = x & 255; x >>= 8;
+ s[3] = x & 255; x >>= 8;
+ s[2] = x & 255; x >>= 8;
+ s[1] = x & 255; x >>= 8;
+ s[0] = x;
+}
+
+
+/*** tai_sub.c ***/
+
+void tai_sub(struct tai *t,const struct tai *u,const struct tai *v)
+{
+ t->x = u->x - v->x;
+}
+
+
+/*** tai_unpack.c ***/
+
+void tai_unpack(const char *s,struct tai *t)
+{
+ uint64_t x;
+
+ x = (unsigned char) s[0];
+ x <<= 8; x += (unsigned char) s[1];
+ x <<= 8; x += (unsigned char) s[2];
+ x <<= 8; x += (unsigned char) s[3];
+ x <<= 8; x += (unsigned char) s[4];
+ x <<= 8; x += (unsigned char) s[5];
+ x <<= 8; x += (unsigned char) s[6];
+ x <<= 8; x += (unsigned char) s[7];
+ t->x = x;
+}
+
+
+/*** taia_add.c ***/
+
+/* XXX: breaks tai encapsulation */
+
+void taia_add(struct taia *t,const struct taia *u,const struct taia *v)
+{
+ t->sec.x = u->sec.x + v->sec.x;
+ t->nano = u->nano + v->nano;
+ t->atto = u->atto + v->atto;
+ if (t->atto > 999999999UL) {
+ t->atto -= 1000000000UL;
+ ++t->nano;
+ }
+ if (t->nano > 999999999UL) {
+ t->nano -= 1000000000UL;
+ ++t->sec.x;
+ }
+}
+
+
+/*** taia_approx.c ***/
+
+double taia_approx(const struct taia *t)
+{
+ return tai_approx(&t->sec) + taia_frac(t);
+}
+
+
+/*** taia_frac.c ***/
+
+double taia_frac(const struct taia *t)
+{
+ return (t->atto * 0.000000001 + t->nano) * 0.000000001;
+}
+
+
+/*** taia_less.c ***/
+
+/* XXX: breaks tai encapsulation */
+
+int taia_less(const struct taia *t,const struct taia *u)
+{
+ if (t->sec.x < u->sec.x) return 1;
+ if (t->sec.x > u->sec.x) return 0;
+ if (t->nano < u->nano) return 1;
+ if (t->nano > u->nano) return 0;
+ return t->atto < u->atto;
+}
+
+
+/*** taia_now.c ***/
+
+void taia_now(struct taia *t)
+{
+ struct timeval now;
+ gettimeofday(&now,(struct timezone *) 0);
+ tai_unix(&t->sec,now.tv_sec);
+ t->nano = 1000 * now.tv_usec + 500;
+ t->atto = 0;
+}
+
+
+/*** taia_pack.c ***/
+
+void taia_pack(char *s,const struct taia *t)
+{
+ unsigned long x;
+
+ tai_pack(s,&t->sec);
+ s += 8;
+
+ x = t->atto;
+ s[7] = x & 255; x >>= 8;
+ s[6] = x & 255; x >>= 8;
+ s[5] = x & 255; x >>= 8;
+ s[4] = x;
+ x = t->nano;
+ s[3] = x & 255; x >>= 8;
+ s[2] = x & 255; x >>= 8;
+ s[1] = x & 255; x >>= 8;
+ s[0] = x;
+}
+
+
+/*** taia_sub.c ***/
+
+/* XXX: breaks tai encapsulation */
+
+void taia_sub(struct taia *t,const struct taia *u,const struct taia *v)
+{
+ unsigned long unano = u->nano;
+ unsigned long uatto = u->atto;
+
+ t->sec.x = u->sec.x - v->sec.x;
+ t->nano = unano - v->nano;
+ t->atto = uatto - v->atto;
+ if (t->atto > uatto) {
+ t->atto += 1000000000UL;
+ --t->nano;
+ }
+ if (t->nano > unano) {
+ t->nano += 1000000000UL;
+ --t->sec.x;
+ }
+}
+
+
+/*** taia_uint.c ***/
+
+/* XXX: breaks tai encapsulation */
+
+void taia_uint(struct taia *t,unsigned s)
+{
+ t->sec.x = s;
+ t->nano = 0;
+ t->atto = 0;
+}
+
+
+/*** stralloc_cat.c ***/
+
+int stralloc_cat(stralloc *sato,const stralloc *safrom)
+{
+ return stralloc_catb(sato,safrom->s,safrom->len);
+}
+
+
+/*** stralloc_catb.c ***/
+
+int stralloc_catb(stralloc *sa,const char *s,unsigned n)
+{
+ if (!sa->s) return stralloc_copyb(sa,s,n);
+ if (!stralloc_readyplus(sa,n + 1)) return 0;
+ memcpy(sa->s + sa->len,s,n);
+ sa->len += n;
+ sa->s[sa->len] = 'Z'; /* ``offensive programming'' */
+ return 1;
+}
+
+
+/*** stralloc_cats.c ***/
+
+int stralloc_cats(stralloc *sa,const char *s)
+{
+ return stralloc_catb(sa,s,strlen(s));
+}
+
+
+/*** stralloc_eady.c ***/
+
+GEN_ALLOC_ready(stralloc,char,s,len,a,i,n,x,30,stralloc_ready)
+GEN_ALLOC_readyplus(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus)
+
+
+/*** stralloc_opyb.c ***/
+
+int stralloc_copyb(stralloc *sa,const char *s,unsigned n)
+{
+ if (!stralloc_ready(sa,n + 1)) return 0;
+ memcpy(sa->s,s,n);
+ sa->len = n;
+ sa->s[n] = 'Z'; /* ``offensive programming'' */
+ return 1;
+}
+
+
+/*** stralloc_opys.c ***/
+
+int stralloc_copys(stralloc *sa,const char *s)
+{
+ return stralloc_copyb(sa,s,strlen(s));
+}
+
+
+/*** stralloc_pend.c ***/
+
+GEN_ALLOC_append(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus,stralloc_append)
+
+
+/*** iopause.c ***/
+
+void iopause(iopause_fd *x,unsigned len,struct taia *deadline,struct taia *stamp)
+{
+ struct taia t;
+ int millisecs;
+ double d;
+ int i;
+
+ if (taia_less(deadline,stamp))
+ millisecs = 0;
+ else {
+ t = *stamp;
+ taia_sub(&t,deadline,&t);
+ d = taia_approx(&t);
+ if (d > 1000.0) d = 1000.0;
+ millisecs = d * 1000.0 + 20.0;
+ }
+
+ for (i = 0;i < len;++i)
+ x[i].revents = 0;
+
+ poll(x,len,millisecs);
+ /* XXX: some kernels apparently need x[0] even if len is 0 */
+ /* XXX: how to handle EAGAIN? are kernels really this dumb? */
+ /* XXX: how to handle EINVAL? when exactly can this happen? */
+}
+
+
+/*** lock_ex.c ***/
+
+int lock_ex(int fd)
+{
+ return flock(fd,LOCK_EX);
+}
+
+
+/*** lock_exnb.c ***/
+
+int lock_exnb(int fd)
+{
+ return flock(fd,LOCK_EX | LOCK_NB);
+}
+
+
+/*** ndelay_off.c ***/
+
+int ndelay_off(int fd)
+{
+ return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK);
+}
+
+
+/*** ndelay_on.c ***/
+
+int ndelay_on(int fd)
+{
+ return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK);
+}
+
+
+/*** open_append.c ***/
+
+int open_append(const char *fn)
+{
+ return open(fn,O_WRONLY | O_NDELAY | O_APPEND | O_CREAT,0600);
+}
+
+
+/*** open_read.c ***/
+
+int open_read(const char *fn)
+{
+ return open(fn,O_RDONLY | O_NDELAY);
+}
+
+
+/*** open_trunc.c ***/
+
+int open_trunc(const char *fn)
+{
+ return open(fn,O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT,0644);
+}
+
+
+/*** open_write.c ***/
+
+int open_write(const char *fn)
+{
+ return open(fn,O_WRONLY | O_NDELAY);
+}
+
+
+/*** openreadclose.c ***/
+
+int openreadclose(const char *fn,stralloc *sa,unsigned bufsize)
+{
+ int fd;
+ fd = open_read(fn);
+ if (fd == -1) {
+ if (errno == ENOENT) return 0;
+ return -1;
+ }
+ if (readclose(fd,sa,bufsize) == -1) return -1;
+ return 1;
+}
+
+
+/*** pathexec_env.c ***/
+
+static stralloc plus;
+static stralloc tmp;
+
+int pathexec_env(const char *s,const char *t)
+{
+ if (!s) return 1;
+ if (!stralloc_copys(&tmp,s)) return 0;
+ if (t) {
+ if (!stralloc_cats(&tmp,"=")) return 0;
+ if (!stralloc_cats(&tmp,t)) return 0;
+ }
+ if (!stralloc_0(&tmp)) return 0;
+ return stralloc_cat(&plus,&tmp);
+}
+
+void pathexec(char **argv)
+{
+ char **e;
+ unsigned elen;
+ unsigned i;
+ unsigned j;
+ unsigned split;
+ unsigned t;
+
+ if (!stralloc_cats(&plus,"")) return;
+
+ elen = 0;
+ for (i = 0;environ[i];++i)
+ ++elen;
+ for (i = 0;i < plus.len;++i)
+ if (!plus.s[i])
+ ++elen;
+
+ e = malloc((elen + 1) * sizeof(char *));
+ if (!e) return;
+
+ elen = 0;
+ for (i = 0;environ[i];++i)
+ e[elen++] = environ[i];
+
+ j = 0;
+ for (i = 0;i < plus.len;++i)
+ if (!plus.s[i]) {
+ split = str_chr(plus.s + j,'=');
+ for (t = 0;t < elen;++t)
+ if (memcmp(plus.s + j,e[t],split) == 0)
+ if (e[t][split] == '=') {
+ --elen;
+ e[t] = e[elen];
+ break;
+ }
+ if (plus.s[j + split])
+ e[elen++] = plus.s + j;
+ j = i + 1;
+ }
+ e[elen] = 0;
+
+ pathexec_run(*argv,argv,e);
+ free(e);
+}
+
+
+/*** pathexec_run.c ***/
+
+static stralloc tmp;
+
+void pathexec_run(const char *file,char *const *argv,char *const *envp)
+{
+ const char *path;
+ unsigned split;
+ int savederrno;
+
+ if (file[str_chr(file,'/')]) {
+ execve(file,argv,envp);
+ return;
+ }
+
+ path = getenv("PATH");
+ if (!path) path = "/bin:/usr/bin";
+
+ savederrno = 0;
+ for (;;) {
+ split = str_chr(path,':');
+ if (!stralloc_copyb(&tmp,path,split)) return;
+ if (!split)
+ if (!stralloc_cats(&tmp,".")) return;
+ if (!stralloc_cats(&tmp,"/")) return;
+ if (!stralloc_cats(&tmp,file)) return;
+ if (!stralloc_0(&tmp)) return;
+
+ execve(tmp.s,argv,envp);
+ if (errno != ENOENT) {
+ savederrno = errno;
+ if ((errno != EACCES) && (errno != EPERM) && (errno != EISDIR)) return;
+ }
+
+ if (!path[split]) {
+ if (savederrno) errno = savederrno;
+ return;
+ }
+ path += split;
+ path += 1;
+ }
+}
+
+
+/*** pmatch.c ***/
+
+unsigned pmatch(const char *p, const char *s, unsigned len) {
+ for (;;) {
+ char c = *p++;
+ if (!c) return !len;
+ switch (c) {
+ case '*':
+ if (!(c = *p)) return 1;
+ for (;;) {
+ if (!len) return 0;
+ if (*s == c) break;
+ ++s; --len;
+ }
+ continue;
+ case '+':
+ if ((c = *p++) != *s) return 0;
+ for (;;) {
+ if (!len) return 1;
+ if (*s != c) break;
+ ++s; --len;
+ }
+ continue;
+ /*
+ case '?':
+ if (*p == '?') {
+ if (*s != '?') return 0;
+ ++p;
+ }
+ ++s; --len;
+ continue;
+ */
+ default:
+ if (!len) return 0;
+ if (*s != c) return 0;
+ ++s; --len;
+ continue;
+ }
+ }
+ return 0;
+}
+
+
+/*** prot.c ***/
+
+int prot_gid(int gid)
+{
+ gid_t x = gid;
+ if (setgroups(1,&x) == -1) return -1;
+ return setgid(gid); /* _should_ be redundant, but on some systems it isn't */
+}
+
+int prot_uid(int uid)
+{
+ return setuid(uid);
+}
+
+
+/*** readclose.c ***/
+
+int readclose_append(int fd,stralloc *sa,unsigned bufsize)
+{
+ int r;
+ for (;;) {
+ if (!stralloc_readyplus(sa,bufsize)) { close(fd); return -1; }
+ r = read(fd,sa->s + sa->len,bufsize);
+ if (r == -1) if (errno == EINTR) continue;
+ if (r <= 0) { close(fd); return r; }
+ sa->len += r;
+ }
+}
+
+int readclose(int fd,stralloc *sa,unsigned bufsize)
+{
+ if (!stralloc_copys(sa,"")) { close(fd); return -1; }
+ return readclose_append(fd,sa,bufsize);
+}
+
+
+/*** scan_ulong.c ***/
+
+unsigned scan_ulong(const char *s,unsigned long *u)
+{
+ unsigned pos = 0;
+ unsigned long result = 0;
+ unsigned long c;
+ while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 10) {
+ result = result * 10 + c;
+ ++pos;
+ }
+ *u = result;
+ return pos;
+}
+
+
+/*** seek_set.c ***/
+
+int seek_set(int fd,seek_pos pos)
+{
+ if (lseek(fd,(off_t) pos,SEEK_SET) == -1) return -1; return 0;
+}
+
+
+/*** sig.c ***/
+
+int sig_alarm = SIGALRM;
+int sig_child = SIGCHLD;
+int sig_cont = SIGCONT;
+int sig_hangup = SIGHUP;
+int sig_int = SIGINT;
+int sig_pipe = SIGPIPE;
+int sig_term = SIGTERM;
+
+void (*sig_defaulthandler)(int) = SIG_DFL;
+void (*sig_ignorehandler)(int) = SIG_IGN;
+
+
+/*** sig_block.c ***/
+
+void sig_block(int sig)
+{
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigaddset(&ss,sig);
+ sigprocmask(SIG_BLOCK,&ss,(sigset_t *) 0);
+}
+
+void sig_unblock(int sig)
+{
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigaddset(&ss,sig);
+ sigprocmask(SIG_UNBLOCK,&ss,(sigset_t *) 0);
+}
+
+void sig_blocknone(void)
+{
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0);
+}
+
+
+/*** sig_catch.c ***/
+
+void sig_catch(int sig,void (*f)(int))
+{
+ struct sigaction sa;
+ sa.sa_handler = f;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(sig,&sa,(struct sigaction *) 0);
+}
+
+
+/*** sig_pause.c ***/
+
+void sig_pause(void)
+{
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigsuspend(&ss);
+}
+
+
+/*** str_chr.c ***/
+
+unsigned str_chr(const char *s,int c)
+{
+ char ch;
+ const char *t;
+
+ ch = c;
+ t = s;
+ for (;;) {
+ if (!*t) break; if (*t == ch) break; ++t;
+ if (!*t) break; if (*t == ch) break; ++t;
+ if (!*t) break; if (*t == ch) break; ++t;
+ if (!*t) break; if (*t == ch) break; ++t;
+ }
+ return t - s;
+}
+
+
+/*** wait_nohang.c ***/
+
+int wait_nohang(int *wstat)
+{
+ return waitpid(-1,wstat,WNOHANG);
+}
+
+
+/*** wait_pid.c ***/
+
+int wait_pid(int *wstat, int pid)
+{
+ int r;
+
+ do
+ r = waitpid(pid,wstat,0);
+ while ((r == -1) && (errno == EINTR));
+ return r;
+}