-static void usage(void) {
- if (fprintf(stderr,
- "usage: userv <options> [--] <service-user> <service-name> [<argument> ...]\n"
- "options: -f|--file <fd>[<fdmodifiers>]=<filename>\n"
- " -D|--defvar <name>=<value>\n"
- " -t|--timeout <seconds>\n"
- " -S|--signals <status>|number|number-nocore|highbit|stdout\n"
- " -w|--fdwait <fd>=wait|nowait|close\n"
- " -P|--sigpipe -H|--hidecwd -h|--help --copyright\n"
- " --override <configuration-data> } available only to\n"
- " --override-file <filename> } root or same user\n"
- "fdmodifiers: read write overwrite trunc[ate]\n"
- "(separate with commas) append sync excl[usive] creat[e] fd\n\n"
- "userv and uservd are copyright (C)1996-1997 Ian Jackson.\n"
- "They come with NO WARRANTY; type `userv --copyright' for details.\n")
- == EOF) syscallerror("write usage to stderr");
+/* 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(
+ "usage: userv <options> [--] <service-user> <service-name> [<argument> ...]\n"
+ "usage: userv <options> -B|--builtin [--] <builtin-service> [<info-argument> ...]\n"
+ "options: -f|--file <fd>[<fdmodifiers>]=<filename>\n"
+ " -D|--defvar <name>=<value>\n"
+ " -t|--timeout <seconds>\n"
+ " -S|--signals <status>|number|number-nocore|highbit|stdout\n"
+ " -w|--fdwait <fd>=wait|nowait|close\n"
+ " -P|--sigpipe -H|--hidecwd -h|--help|--version --copyright\n"
+ " --override <configuration-data> } available only\n"
+ " --override-file <filename> } to root\n"
+ " --spoof-user <username> } or same user\n"
+ "fdmodifiers: read write overwrite trunc[ate]\n"
+ "(separate with commas) append sync excl[usive] creat[e] fd\n"
+ "userv -B 'X' ... is same as userv --override 'execute-builtin X' - 'X' ...\n"
+ " for help, type `userv -B help'; remember to quote multi-word X\n"
+ "userv and uservd version " VERSION VEREXT ".\n"
+ COPYRIGHT("","\n"),
+ stream) < 0)
+ syscallerror("write usage message");