On Tue, Sep 17, 2019 at 09:57:28AM +0200, Alexandre Ratchov wrote:
> 
> > Regarding the race I was talking about, doesn't dev_reopen() close the
> > current device before trying to open a new one, possibly finding an
> > incompatible device or failing to open it?  What happens in this case?
> 
> After a SIGHUP, if the new device is incompatible, the old one is
> reopened and sound continues. Now I understand your remark: oops!
> close() and open() are called in the wrong order, so another process
> could steal the device. I'll fix that.
> 

Here's a new diff with the fix. Now, the new device is opened before
closing the old one. I've fixed two other bugs in the pure audio
logic.

Index: dev.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/dev.c,v
retrieving revision 1.61
diff -u -p -u -p -r1.61 dev.c
--- dev.c       19 Sep 2019 05:10:19 -0000      1.61
+++ dev.c       20 Sep 2019 07:17:54 -0000
@@ -969,7 +969,8 @@ dev_new(char *path, struct aparams *par,
                return NULL;
        }
        d = xmalloc(sizeof(struct dev));
-       d->path = xstrdup(path);
+       d->path_list = NULL;
+       namelist_add(&d->path_list, path);
        d->num = dev_sndnum++;
        d->opt_list = NULL;
 
@@ -1174,6 +1175,74 @@ dev_close(struct dev *d)
        dev_freebufs(d);
 }
 
+/*
+ * Close the device, but attempt to migrate everything to a new sndio
+ * device.
+ */
+int
+dev_reopen(struct dev *d)
+{
+       struct slot *s;
+       long long pos;
+       unsigned int pstate;
+       int delta;
+
+       /* not opened */
+       if (d->pstate == DEV_CFG)
+               return 1;
+
+       /* save state */
+       delta = d->delta;
+       pstate = d->pstate;
+
+       if (!dev_sio_reopen(d))
+               return 0;
+
+       /* reopen returns a stopped device */
+       d->pstate = DEV_INIT;
+
+       /* reallocate new buffers, with new parameters */
+       dev_freebufs(d);
+       dev_allocbufs(d);
+
+       /*
+        * adjust time positions, make anything go back delta ticks, so
+        * that the new device can start at zero
+        */
+       for (s = d->slot_list; s != NULL; s = s->next) {
+               pos = (long long)s->delta * d->round + s->delta_rem;
+               pos -= (long long)delta * s->round;
+               s->delta_rem = pos % (int)d->round;
+               s->delta = pos / (int)d->round;
+               if (log_level >= 3) {
+                       slot_log(s);
+                       log_puts(": adjusted: delta -> ");
+                       log_puti(s->delta);
+                       log_puts(", delta_rem -> ");
+                       log_puti(s->delta_rem);
+                       log_puts("\n");
+               }
+
+               /* reinitilize the format conversion chain */
+               slot_initconv(s);
+       }
+       if (d->tstate == MMC_RUN) {
+               d->mtc.delta -= delta * MTC_SEC;
+               if (log_level >= 2) {
+                       dev_log(d);
+                       log_puts(": adjusted mtc: delta ->");
+                       log_puti(d->mtc.delta);
+                       log_puts("\n");
+               }
+       }
+
+       /* start the device if needed */
+       if (pstate == DEV_RUN)
+               dev_wakeup(d);
+
+       return 1;
+}
+
 int
 dev_ref(struct dev *d)
 {
@@ -1280,7 +1349,7 @@ dev_del(struct dev *d)
        }
        midi_del(d->midi);
        *p = d->next;
-       xfree(d->path);
+       namelist_clear(&d->path_list);
        xfree(d);
 }
 
Index: dev.h
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/dev.h,v
retrieving revision 1.21
diff -u -p -u -p -r1.21 dev.h
--- dev.h       12 Jul 2019 06:30:55 -0000      1.21
+++ dev.h       20 Sep 2019 07:17:54 -0000
@@ -159,7 +159,7 @@ struct dev {
 #define DEV_INIT       1                       /* stopped */
 #define DEV_RUN                2                       /* playin & recording */
        unsigned int pstate;                    /* one of above */
-       char *path;                             /* sio path */
+       struct name *path_list;
 
        /*
         * actual parameters and runtime state (i.e. once opened)
@@ -201,6 +201,7 @@ extern struct dev *dev_list;
 
 void dev_log(struct dev *);
 void dev_close(struct dev *);
+int dev_reopen(struct dev *);
 struct dev *dev_new(char *, struct aparams *, unsigned int, unsigned int,
     unsigned int, unsigned int, unsigned int, unsigned int);
 struct dev *dev_bynum(int);
Index: fdpass.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/fdpass.c,v
retrieving revision 1.6
diff -u -p -u -p -r1.6 fdpass.c
--- fdpass.c    28 Jun 2019 13:35:03 -0000      1.6
+++ fdpass.c    20 Sep 2019 07:17:54 -0000
@@ -300,6 +300,7 @@ fdpass_in_helper(void *arg)
        struct fdpass *f = arg;
        struct dev *d;
        struct port *p;
+       struct name *path;
 
        if (!fdpass_recv(f, &cmd, &num, &mode, &fd))
                return;
@@ -314,7 +315,11 @@ fdpass_in_helper(void *arg)
                        fdpass_close(f);
                        return;
                }
-               fd = sio_sun_getfd(d->path, mode, 1);
+               for (path = d->path_list; path != NULL; path = path->next) {
+                       fd = sio_sun_getfd(path->str, mode, 1);
+                       if (fd != -1)
+                               break;
+               }
                break;
        case FDPASS_OPEN_MIDI:
                p = port_bynum(num);
@@ -326,7 +331,11 @@ fdpass_in_helper(void *arg)
                        fdpass_close(f);
                        return;
                }
-               fd = mio_rmidi_getfd(p->path, mode, 1);
+               for (path = p->path_list; path != NULL; path = path->next) {
+                       fd = mio_rmidi_getfd(path->str, mode, 1);
+                       if (fd != -1)
+                               break;
+               }
                break;
        default:
                fdpass_close(f);
Index: midi.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/midi.c,v
retrieving revision 1.21
diff -u -p -u -p -r1.21 midi.c
--- midi.c      29 Aug 2019 07:19:15 -0000      1.21
+++ midi.c      20 Sep 2019 07:17:54 -0000
@@ -438,7 +438,8 @@ port_new(char *path, unsigned int mode, 
        struct port *c;
 
        c = xmalloc(sizeof(struct port));
-       c->path = xstrdup(path);
+       c->path_list = NULL;
+       namelist_add(&c->path_list, path);
        c->state = PORT_CFG;
        c->hold = hold;
        c->midi = midi_new(&port_midiops, c, mode);
@@ -468,7 +469,7 @@ port_del(struct port *c)
 #endif
        }
        *p = c->next;
-       xfree(c->path);
+       namelist_clear(&c->path_list);
        xfree(c);
 }
 
@@ -592,4 +593,16 @@ port_done(struct port *c)
 {
        if (c->state == PORT_INIT)
                port_drain(c);
+}
+
+int
+port_reopen(struct port *p)
+{
+       if (p->state == PORT_CFG)
+               return 1;
+
+       if (!port_mio_reopen(p))
+               return 0;
+
+       return 1;
 }
Index: midi.h
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/midi.h,v
retrieving revision 1.9
diff -u -p -u -p -r1.9 midi.h
--- midi.h      29 Aug 2019 07:11:28 -0000      1.9
+++ midi.h      20 Sep 2019 07:17:54 -0000
@@ -88,7 +88,7 @@ struct port {
 #define PORT_DRAIN     2
        unsigned int state;
        unsigned int num;               /* port serial number */
-       char *path;
+       struct name *path_list;
        int hold;                       /* hold the port open ? */
        struct midi *midi;
 };
@@ -121,5 +121,6 @@ int  port_init(struct port *);
 void port_done(struct port *);
 void port_drain(struct port *);
 int  port_close(struct port *);
+int  port_reopen(struct port *);
 
 #endif /* !defined(MIDI_H) */
Index: miofile.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/miofile.c,v
retrieving revision 1.4
diff -u -p -u -p -r1.4 miofile.c
--- miofile.c   20 Dec 2015 11:38:33 -0000      1.4
+++ miofile.c   20 Sep 2019 07:17:54 -0000
@@ -51,7 +51,34 @@ port_mio_open(struct port *p)
        p->mio.hdl = fdpass_mio_open(p->num, p->midi->mode);
        if (p->mio.hdl == NULL)
                return 0;
-       p->mio.file = file_new(&port_mio_ops, p, p->path, mio_nfds(p->mio.hdl));
+       p->mio.file = file_new(&port_mio_ops, p, "port", mio_nfds(p->mio.hdl));
+       return 1;
+}
+
+/*
+ * Open an alternate port. Upon success, close the old port
+ * and continue using the new one.
+ */
+int
+port_mio_reopen(struct port *p)
+{
+       struct mio_hdl *hdl;
+
+       hdl = fdpass_mio_open(p->num, p->midi->mode);
+       if (hdl == NULL) {
+               if (log_level >= 1) {
+                       port_log(p);
+                       log_puts(": couldn't open an alternate port\n");
+               }
+               return 0;
+       }
+
+       /* close unused device */
+       file_del(p->mio.file);
+       mio_close(p->mio.hdl);
+
+       p->mio.hdl = hdl;
+       p->mio.file = file_new(&port_mio_ops, p, "port", mio_nfds(hdl));
        return 1;
 }
 
@@ -129,5 +156,6 @@ port_mio_hup(void *arg)
 {
        struct port *p = arg;
 
-       port_close(p);
+       if (!port_reopen(p))
+               port_close(p);
 }
Index: miofile.h
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/miofile.h,v
retrieving revision 1.1
diff -u -p -u -p -r1.1 miofile.h
--- miofile.h   23 Nov 2012 07:03:28 -0000      1.1
+++ miofile.h   20 Sep 2019 07:17:54 -0000
@@ -25,6 +25,7 @@ struct port_mio {
 };
 
 int  port_mio_open(struct port *);
+int  port_mio_reopen(struct port *);
 void port_mio_close(struct port *);
 
 #endif /* !defined(MIOFILE_H) */
Index: siofile.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/siofile.c,v
retrieving revision 1.15
diff -u -p -u -p -r1.15 siofile.c
--- siofile.c   29 Aug 2019 07:05:47 -0000      1.15
+++ siofile.c   20 Sep 2019 07:17:54 -0000
@@ -212,7 +212,7 @@ dev_sio_open(struct dev *d)
        if (!(mode & MODE_REC))
                d->mode &= ~MODE_REC;
        sio_onmove(d->sio.hdl, dev_sio_onmove, d);
-       d->sio.file = file_new(&dev_sio_ops, d, d->path, sio_nfds(d->sio.hdl));
+       d->sio.file = file_new(&dev_sio_ops, d, "dev", sio_nfds(d->sio.hdl));
        timo_set(&d->sio.watchdog, dev_sio_timeout, d);
        return 1;
  bad_close:
@@ -220,6 +220,79 @@ dev_sio_open(struct dev *d)
        return 0;
 }
 
+/*
+ * Open an alternate device. Upon success and if the new device is
+ * compatible with the old one, close the old device and continue
+ * using the new one. The new device is not started.
+ */
+int
+dev_sio_reopen(struct dev *d)
+{
+       struct sio_par par;
+       struct sio_hdl *hdl;
+
+       hdl = fdpass_sio_open(d->num, d->mode & (MODE_PLAY | MODE_REC));
+       if (hdl == NULL) {
+               if (log_level >= 1) {
+                       dev_log(d);
+                       log_puts(": couldn't open an alternate device\n");
+               }
+               return 0;
+       }
+
+       sio_initpar(&par);
+       par.bits = d->par.bits;
+       par.bps = d->par.bps;
+       par.sig = d->par.sig;
+       par.le = d->par.le;
+       par.msb = d->par.msb;
+       if (d->mode & SIO_PLAY)
+               par.pchan = d->pchan;
+       if (d->mode & SIO_REC)
+               par.rchan = d->rchan;
+       par.appbufsz = d->bufsz;
+       par.round = d->round;
+       par.rate = d->rate;
+       if (!sio_setpar(hdl, &par))
+               goto bad_close;
+       if (!sio_getpar(hdl, &par))
+               goto bad_close;
+
+       /* check if new parameters are compatible with old ones */
+       if (par.round != d->round || par.bufsz != d->bufsz ||
+           par.rate != d->rate) {
+               if (log_level >= 1) {
+                       dev_log(d);
+                       log_puts(": alternate device not compatible\n");
+               }
+               goto bad_close;
+       }
+
+       /* close unused device */
+       timo_del(&d->sio.watchdog);
+       file_del(d->sio.file);
+       sio_close(d->sio.hdl);
+
+       /* update parameters */
+       d->par.bits = par.bits;
+       d->par.bps = par.bps;
+       d->par.sig = par.sig;
+       d->par.le = par.le;
+       d->par.msb = par.msb;
+       if (d->mode & SIO_PLAY)
+               d->pchan = par.pchan;
+       if (d->mode & SIO_REC)
+               d->rchan = par.rchan;
+
+       d->sio.hdl = hdl;
+       d->sio.file = file_new(&dev_sio_ops, d, "dev", sio_nfds(hdl));
+       sio_onmove(hdl, dev_sio_onmove, d);
+       return 1;
+bad_close:
+       sio_close(hdl);
+       return 0;
+}
+
 void
 dev_sio_close(struct dev *d)
 {
@@ -494,5 +567,6 @@ dev_sio_hup(void *arg)
                log_puts(": disconnected\n");
        }
 #endif
-       dev_close(d);
+       if (!dev_reopen(d))
+               dev_close(d);
 }
Index: siofile.h
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/siofile.h,v
retrieving revision 1.3
diff -u -p -u -p -r1.3 siofile.h
--- siofile.h   28 Sep 2013 18:49:32 -0000      1.3
+++ siofile.h   20 Sep 2019 07:17:54 -0000
@@ -38,6 +38,7 @@ struct dev_sio {
 };
 
 int dev_sio_open(struct dev *);
+int dev_sio_reopen(struct dev *);
 void dev_sio_close(struct dev *);
 void dev_sio_log(struct dev *);
 void dev_sio_start(struct dev *);
Index: sndiod.8
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/sndiod.8,v
retrieving revision 1.2
diff -u -p -u -p -r1.2 sndiod.8
--- sndiod.8    18 Jan 2016 11:38:07 -0000      1.2
+++ sndiod.8    20 Sep 2019 07:17:55 -0000
@@ -29,10 +29,12 @@
 .Op Fl C Ar min : Ns Ar max
 .Op Fl c Ar min : Ns Ar max
 .Op Fl e Ar enc
+.Op Fl F Ar device
 .Op Fl f Ar device
 .Op Fl j Ar flag
 .Op Fl L Ar addr
 .Op Fl m Ar mode
+.Op Fl Q Ar port
 .Op Fl q Ar port
 .Op Fl r Ar rate
 .Op Fl s Ar name
@@ -182,6 +184,18 @@ or
 Only the signedness and the precision are mandatory.
 Examples:
 .Va u8 , s16le , s24le3 , s24le4lsb .
+.It Fl F Ar device
+Specify an alternate device to use.
+If doesn't work, the one given with the last
+.Fl f
+or
+.Fl F
+options will be used.
+For instance, specifying a USB device following a
+PCI device allows
+.Nm
+to use the USB one preferably when it's connected
+and to fall back to the PCI one when it's disconnected.
 .It Fl f Ar device
 Add this
 .Xr sndio 7
@@ -245,6 +259,15 @@ but the same sub-device cannot be used f
 The default is
 .Ar play , Ns Ar rec
 (i.e. full-duplex).
+.It Fl Q Ar port
+Specify an alternate MIDI port to use.
+If doesn't work, the one given with the last
+.Fl Q
+or
+.Fl q
+options will be used.
+For instance, this allows to replace a USB MIDI controller without
+the need to restart programs using it.
 .It Fl q Ar port
 Expose the given MIDI port.
 This allows multiple programs to share the port.
@@ -376,11 +399,15 @@ is
 If
 .Nm
 is sent
-.Dv SIGHUP ,
 .Dv SIGINT
 or
 .Dv SIGTERM ,
 it terminates.
+If
+.Nm
+is sent
+.Dv SIGHUP ,
+it reopens all audio devices and MIDI ports.
 .Pp
 By default, when the program cannot accept
 recorded data fast enough or cannot provide data to play fast enough,
Index: sndiod.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/sndiod.c,v
retrieving revision 1.35
diff -u -p -u -p -r1.35 sndiod.c
--- sndiod.c    29 Jun 2019 21:23:18 -0000      1.35
+++ sndiod.c    20 Sep 2019 07:17:55 -0000
@@ -75,7 +75,7 @@
  * block size if neither ``-z'' nor ``-b'' is used
  */
 #ifndef DEFAULT_ROUND
-#define DEFAULT_ROUND  960
+#define DEFAULT_ROUND  480
 #endif
 
 /*
@@ -93,6 +93,7 @@
 #endif
 
 void sigint(int);
+void sighup(int);
 void opt_ch(int *, int *);
 void opt_enc(struct aparams *);
 int opt_mmc(void);
@@ -109,12 +110,13 @@ struct opt *mkopt(char *, struct dev *,
     int, int, int, int, int, int, int, int);
 
 unsigned int log_level = 0;
-volatile sig_atomic_t quit_flag = 0;
+volatile sig_atomic_t quit_flag = 0, reopen_flag = 0;
 
 char usagestr[] = "usage: sndiod [-d] [-a flag] [-b nframes] "
-    "[-C min:max] [-c min:max] [-e enc]\n\t"
-    "[-f device] [-j flag] [-L addr] [-m mode] [-q port] [-r rate]\n\t"
-    "[-s name] [-t mode] [-U unit] [-v volume] [-w flag] [-z nframes]\n";
+    "[-C min:max] [-c min:max]\n\t"
+    "[-e enc] [-F device] [-f device] [-j flag] [-L addr] [-m mode]\n\t"
+    "[-Q port] [-q port] [-r rate] [-s name] [-t mode] [-U unit]\n\t"
+    "[-v volume] [-w flag] [-z nframes]\n";
 
 /*
  * SIGINT handler, it raises the quit flag. If the flag is already set,
@@ -129,6 +131,16 @@ sigint(int s)
        quit_flag = 1;
 }
 
+/*
+ * SIGHUP handler, it raises the reopen flag, which requests devices
+ * to be reopened.
+ */
+void
+sighup(int s)
+{
+       reopen_flag = 1;
+}
+
 void
 opt_ch(int *rcmin, int *rcmax)
 {
@@ -231,6 +243,7 @@ setsig(void)
        struct sigaction sa;
 
        quit_flag = 0;
+       reopen_flag = 0;
        sigfillset(&sa.sa_mask);
        sa.sa_flags = SA_RESTART;
        sa.sa_handler = sigint;
@@ -238,6 +251,7 @@ setsig(void)
                err(1, "sigaction(int) failed");
        if (sigaction(SIGTERM, &sa, NULL) == -1)
                err(1, "sigaction(term) failed");
+       sa.sa_handler = sighup;
        if (sigaction(SIGHUP, &sa, NULL) == -1)
                err(1, "sigaction(hup) failed");
 }
@@ -294,7 +308,8 @@ mkdev(char *path, struct aparams *par,
        struct dev *d;
 
        for (d = dev_list; d != NULL; d = d->next) {
-               if (strcmp(d->path, path) == 0)
+               if (d->path_list->next == NULL &&
+                   strcmp(d->path_list->str, path) == 0)
                        return d;
        }
        if (!bufsz && !round) {
@@ -316,7 +331,8 @@ mkport(char *path, int hold)
        struct port *c;
 
        for (c = port_list; c != NULL; c = c->next) {
-               if (strcmp(c->path, path) == 0)
+               if (c->path_list->next == NULL &&
+                   strcmp(c->path_list->str, path) == 0)
                        return c;
        }
        c = port_new(path, MODE_MIDIMASK, hold);
@@ -361,6 +377,7 @@ start_helper(int background)
        struct dev *d;
        struct port *p;
        struct passwd *pw;
+       struct name *n;
        int s[2];
        pid_t pid;
 
@@ -395,10 +412,14 @@ start_helper(int background)
                            setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
                                err(1, "cannot drop privileges");
                }
-               for (d = dev_list; d != NULL; d = d->next)
-                       dounveil(d->path, "rsnd/", "/dev/audio");
-               for (p = port_list; p != NULL; p = p->next)
-                       dounveil(p->path, "rmidi/", "/dev/rmidi");
+               for (d = dev_list; d != NULL; d = d->next) {
+                       for (n = d->path_list; n != NULL; n = n->next)
+                               dounveil(n->str, "rsnd/", "/dev/audio");
+               }
+               for (p = port_list; p != NULL; p = p->next) {
+                       for (n = p->path_list; n != NULL; n = n->next)
+                               dounveil(n->str, "rmidi/", "/dev/rmidi");
+               }
                if (pledge("stdio sendfd rpath wpath", NULL) == -1)
                        err(1, "pledge");
                while (file_poll())
@@ -461,7 +482,8 @@ main(int argc, char **argv)
        mode = MODE_PLAY | MODE_REC;
        tcpaddr_list = NULL;
 
-       while ((c = getopt(argc, argv, 
"a:b:c:C:de:f:j:L:m:q:r:s:t:U:v:w:x:z:")) != -1) {
+       while ((c = getopt(argc, argv,
+           "a:b:c:C:de:F:f:j:L:m:Q:q:r:s:t:U:v:w:x:z:")) != -1) {
                switch (c) {
                case 'd':
                        log_level++;
@@ -518,6 +540,11 @@ main(int argc, char **argv)
                case 'q':
                        mkport(optarg, hold);
                        break;
+               case 'Q':
+                       if (port_list == NULL)
+                               errx(1, "-Q %s: no ports defined", optarg);
+                       namelist_add(&port_list->path_list, optarg);
+                       break;
                case 'a':
                        hold = opt_onoff();
                        break;
@@ -538,6 +565,11 @@ main(int argc, char **argv)
                        mkdev(optarg, &par, 0, bufsz, round,
                            rate, hold, autovol);
                        break;
+               case 'F':
+                       if (dev_list == NULL)
+                               errx(1, "-F %s: no devices defined", optarg);
+                       namelist_add(&dev_list->path_list, optarg);
+                       break;
                default:
                        fputs(usagestr, stderr);
                        return 1;
@@ -617,6 +649,13 @@ main(int argc, char **argv)
        for (;;) {
                if (quit_flag)
                        break;
+               if (reopen_flag) {
+                       reopen_flag = 0;
+                       for (d = dev_list; d != NULL; d = d->next)
+                               dev_reopen(d);
+                       for (p = port_list; p != NULL; p = p->next)
+                               port_reopen(p);
+               }
                if (!fdpass_peer)
                        break;
                if (!file_poll())
Index: utils.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/utils.c,v
retrieving revision 1.5
diff -u -p -u -p -r1.5 utils.c
--- utils.c     5 Jul 2019 07:34:40 -0000       1.5
+++ utils.c     20 Sep 2019 07:17:55 -0000
@@ -188,3 +188,30 @@ xstrdup(char *s)
        memcpy(p, s, size);
        return p;
 }
+
+/*
+ * copy and append the given string to the name list
+ */
+void
+namelist_add(struct name **list, char *str)
+{
+       struct name *n;
+       size_t size;
+
+       size = strlen(str) + 1;
+       n = xmalloc(sizeof(struct name) + size);
+       memcpy(n->str, str, size);
+       n->next = *list;
+       *list = n;
+}
+
+void
+namelist_clear(struct name **list)
+{
+       struct name *n;
+
+       while ((n = *list) != NULL) {
+               *list = n->next;
+               xfree(n);
+       }
+}
Index: utils.h
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/utils.h,v
retrieving revision 1.3
diff -u -p -u -p -r1.3 utils.h
--- utils.h     12 May 2013 04:58:41 -0000      1.3
+++ utils.h     20 Sep 2019 07:17:55 -0000
@@ -20,6 +20,11 @@
 
 #include <stddef.h>
 
+struct name {
+       struct name *next;
+       char str[];
+};
+
 void log_puts(char *);
 void log_putx(unsigned long);
 void log_putu(unsigned long);
@@ -30,6 +35,9 @@ void log_flush(void);
 void *xmalloc(size_t);
 char *xstrdup(char *);
 void xfree(void *);
+
+void namelist_add(struct name **, char *);
+void namelist_clear(struct name **);
 
 /*
  * Log levels:

Reply via email to