+/* Functions which may be called from signal handlers. These
+ * may use signal-handler objects. The main program may only
+ * call them with signals blocked, and they may not use any
+ * main-thread objects.
+ */
+
+static void disconnect(void) /* DOES return, unlike in daemon */ {
+ struct event_msg event_mbuf;
+ int r;
+
+ if (swfile) {
+ memset(&event_mbuf,0,sizeof(event_mbuf));
+ event_mbuf.magic= EVENT_MAGIC;
+ event_mbuf.type= et_disconnect;
+ r= fwrite(&event_mbuf,1,sizeof(event_mbuf),swfile);
+ if ((r != sizeof(event_mbuf) || fflush(swfile)) && errno != EPIPE)
+ syscallerror("write to server when disconnecting");
+ }
+ systemerror= 1;
+}
+
+static void sighandler_alrm(int ignored) /* DOES return, unlike in daemon */ {
+ int es;
+ es= errno;
+ fputs("userv: timeout\n",stderr);
+ disconnect();
+ errno= es;
+}
+
+static void sighandler_chld(int ignored) /* DOES return, unlike in daemon */ {
+ struct event_msg event_mbuf;
+ pid_t child;
+ int status, fd, r, es;
+
+ es= errno;
+ for (;;) {
+ child= wait3(&status,WNOHANG,0);
+ if (child == 0 || (child == -1 && errno == ECHILD)) break;
+ if (child == -1) syscallerror("wait for child process (in sigchld handler)");
+ for (fd=0; fd<fdsetupsize && fdsetup[fd].catpid != child; fd++);
+ if (fd>=fdsetupsize) continue; /* perhaps the caller gave us children */
+ if ((WIFEXITED(status) && WEXITSTATUS(status)==0) ||
+ (WIFSIGNALED(status) && WTERMSIG(status)==SIGPIPE) ||
+ (fdsetup[fd].killed && WIFSIGNALED(status) && WTERMSIG(status)==SIGKILL)) {
+ if (swfile && fdsetup[fd].mods & fdm_read) {
+ memset(&event_mbuf,0,sizeof(event_mbuf));
+ event_mbuf.magic= EVENT_MAGIC;
+ event_mbuf.type= et_closereadfd;
+ event_mbuf.data.closereadfd.fd= fd;
+ r= fwrite(&event_mbuf,1,sizeof(event_mbuf),swfile);
+ if (r != sizeof(event_mbuf) || fflush(swfile))
+ if (errno != EPIPE) syscallerror("inform service of closed read fd");
+ }
+ } else {
+ if (WIFEXITED(status))
+ fprintf(stderr,"userv: cat for fd %d exited with error exit status %d\n",
+ fd,WEXITSTATUS(status));
+ else if (WIFSIGNALED(status))
+ if (WCOREDUMP(status))
+ fprintf(stderr,"userv: cat for fd %d dumped core due to signal %s (%d)\n",
+ fd,strsignal(WTERMSIG(status)),WTERMSIG(status));
+ else
+ fprintf(stderr,"userv: cat for fd %d terminated by signal %s (%d)\n",
+ fd,strsignal(WTERMSIG(status)),WTERMSIG(status));
+ else
+ fprintf(stderr,"userv: cat for fd %d gave unknown wait status %d\n",
+ fd,status);
+ disconnect();
+ }
+ fdsetup[fd].catpid= -1;
+ }
+ errno= es;
+}
+
+/*
+ * Argument parsing. These functions which are called only during
+ * setup, before the signal asynchronicity starts.
+ */
+
+struct optioninfo;
+
+typedef void optionfunction(const struct optioninfo*, const char *value, char *key);
+
+struct optioninfo {
+ int abbrev;
+ const char *full;
+ int values; /* 0: no value; 1: single value; 2: key and value */
+ optionfunction *fn;
+};
+
+static void usage(FILE *stream) {
+ if (fputs(