chiark / gitweb /
Internal review up to end of p11.
authorian <ian>
Tue, 2 Sep 1997 02:46:23 +0000 (02:46 +0000)
committerian <ian>
Tue, 2 Sep 1997 02:46:23 +0000 (02:46 +0000)
18 files changed:
INSTALL [new file with mode: 0644]
Makefile.in
client.c
common.h
configure.in
daemon.h
debug.c [moved from ddebug.c with 77% similarity]
language.i4
lexer.l.m4
lib.c
lib.h
overlord.c [new file with mode: 0644]
parser.c
process.c [moved from daemon.c with 55% similarity]
servexec.c [new file with mode: 0644]
spec.sgml
tokens.h.m4
version.h

diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..d7211b6
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,81 @@
+INSTALLATION INSTRUCTIONS:
+
+   $ ./configure
+   $ make
+   # make install
+
+This will not install the documentation, which is shipped as
+pre-prepared HTML and PostScript as well as debiandoc-sgml source.
+Put that (spec.html/ and spec.ps) where you will.
+
+SYSTEM REQUIREMENTS:
+
+Programs:
+
+* md5sum (Colin Plumb's or the GNU version)
+* GNU m4
+* GNU make
+* GNU flex
+* GCC is preferred but other compilers ought to work (though
+  no portability testing has yet been done).  ANSI C only.
+  ints must be at least 32 bits.
+* A sensible /bin/cat which notices write errors (ie not
+  SunOS4, BSD 4.3, or many others.)
+
+C Library:
+
+* [v]snprintf - a real version, not just [v]sprintf with a wrapper
+  that throws the argument away.
+* strsignal;
+* fnmatch;
+* BSD syslog(3);
+* strtoul;
+* memcpy, memset, memcpy;
+* realloc(0,size) must work and be equivalent to malloc(size).
+* <stdarg.h> (not varargs) and v[sf][n]printf.
+
+System interfaces:
+
+* setreuid(2), getreuid(2), getgroups(2), initgroups(3), with
+  the ability for root to (a) swap euid and ruid and
+  (b) give away all privilege by calling setreuid(ruid,ruid)
+  twice.
+* wait3 and waitpid, <wait.h> with WNOHANG, WIFSIGNALED,
+  WIFEXITEED, WTERMSIG, WEXITSTATUS and WCOREDUMP.
+* gid_t, uid_t, pid_t.
+* Unix-domain (AF_UNIX) stream sockets, for use with:
+  * BSD sockets - socket(), bind(), listen(), accept(), connect();
+  * socketpair(2);
+* lstat(2) (though stat(2) will be safe on systems without symlinks).
+* Pipes:
+  * creating using pipe(2) and mkfifo(2);
+  * proper interaction between open(O_RDWR), open(O_RDONLY),
+    open(O_WRONLY), close(), dup2, EPIPE, SIGPIPE, &c.;
+* POSIX signal handling - sigaction(2), sigprocmask(2), sigsuspend(2);
+
+To format the documentation:
+
+* debiandoc-sgml, and hence sp (aka nsgmls) and sgmlspm.
+* For PostScript output, Lout and possibly psutils.
+
+For debugging version (./configure --enable-debug):
+
+* initgroups(3) must use setgroups(2) and dynamic
+  linking must allow overriding setgroups(2) for initgroups(3);
+
+REENTRANCY IN THE LIBC:
+
+We assume, both in the client and server, that it is safe to use one
+stdio stream in a signal handler which interrupts use of a _different_
+stdio stream in another.  We make sure using setvbuf that we have
+pre-allocated buffers so that stdio doesn't need to use malloc() when
+we actually read or write.  stdio had better not do anything else
+weird.
+
+Furthermore, we assume that it is safe to use syslog in a signal
+handler which has interrupted a stdio operation (but we don't require
+that it be safe to invoke when the signal has interrupted a call to
+malloc, unless stdio makes gratuitous mallocs).  openlog will already
+have been called (but syslog will not necessarily have been called).
+
+We assume that strerror is completely reentrant.
index 2e7f5c5..0df4f95 100644 (file)
@@ -39,11 +39,12 @@ etcsubdir=$(etcdir)/userv
 
 SOURCES=       Makefile.in configure.in acconfig.h                     \
                client.c common.h version.h                             \
-               daemon.c daemon.h ddebug.c parser.c lib.c lib.h         \
+               overlord.c process.c servexec.c                         \
+               daemon.h debug.c parser.c lib.c lib.h                   \
                language.i4 lexer.l.m4 tokens.h.m4
 ALSOSHIP=      system.default system.override \
                spec.sgml overview.fig overview.ps \
-               COPYING buildship install-sh .cvsignore
+               COPYING INSTALL buildship install-sh .cvsignore
 GENSHIP=       lexer.l lexer.c tokens.h configure config.h.in \
                spec.html spec.ps overview.ps
 
@@ -60,17 +61,22 @@ install:    all
                if ! test -f $(etcsubdir)/system.override; then \
                        $(INSTALL_DATA) -o root -g root system.override $(etcsubdir); fi
 
-daemon:                daemon.o parserlexer.o ddebug.o lib.o
+daemon:                overlord.o process.o servexec.o parserlexer.o debug.o lib.o
+               $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
 
 lexer.l:       language.i4
 
 client.o:      config.h common.h pcsum.h version.h
 
-daemon.o:      config.h common.h pcsum.h daemon.h lib.h tokens.h
+process.o:     config.h common.h pcsum.h daemon.h lib.h tokens.h
+
+overlord.o:    config.h common.h pcsum.h daemon.h
+
+servexec.o:    config.h common.h pcsum.h daemon.h lib.h
 
 lib.o:         config.h lib.h
 
-ddebug.o:      config.h common.h pcsum.h daemon.h lib.h tokens.h
+debug.o:       config.h common.h pcsum.h daemon.h lib.h tokens.h
 
 parserlexer.o: lexer.c parser.c config.h common.h pcsum.h daemon.h lib.h tokens.h
 # lexer.c #include's parser.c at the end.  Blame flex.
index 9d12491..bee10a1 100644 (file)
--- a/client.c
+++ b/client.c
@@ -264,6 +264,7 @@ static void xfwrite(const void *p, size_t sz, FILE *file) {
 static void xfwritestring(const char *s, FILE *file) {
   int l;
   l= strlen(s);
+  assert(l<=MAX_GENERAL_STRING);
   xfwrite(&l,sizeof(l),file);
   xfwrite(s,sizeof(*s)*l,file);
 }
@@ -419,7 +420,12 @@ static void of_fdwait(const struct optioninfo *oip, const char *value, char *key
 static void of_defvar(const struct optioninfo *oip, const char *value, char *key) {
   int i;
 
+  if (strlen(key)>MAX_GENERAL_STRING)
+    usageerror("variable name `%s' is far too long",key);
+  if (strlen(value)>MAX_GENERAL_STRING)
+    usageerror("variable `%s' has value `%s' which is far too long",key,value);
   for (i=0; i<defvarsused && strcmp(defvarsarray[i][0],key); i++);
+  if (defvarsused >= MAX_ARGSDEFVARS) usageerror("far too many --defvar or -D options");
   if (i>=defvarsavail) {
     defvarsavail+=10; defvarsavail<<=1;
     defvarsarray= xrealloc(defvarsarray,sizeof(const char*)*2*defvarsavail);
@@ -761,7 +767,9 @@ int main(int argc, char *const *argv) {
 
   argc-= (argpp-argv);
   argv= argpp;
-
+  if (argc > MAX_ARGSDEFVARS) usageerror("far too many arguments");
+  if (ngids > MAX_GIDS) miscerror("caller is in far too many gids");
+  
   pw= getpwnam(serviceuser);
   if (!pw) miscerror("requested service user `%s' is not a user",serviceuser);
   serviceuid= pw->pw_uid;
@@ -972,6 +980,7 @@ int main(int argc, char *const *argv) {
     if (fdsetup[fd].catpid==-1) syscallerror("fork for cat for fd %d",fd);
     if (!fdsetup[fd].catpid) {
       snprintf(catnamebuf,sizeof(catnamebuf),"cat fd%d",fd);
+      catnamebuf[sizeof(catnamebuf)-1]= 0;
       sig.sa_handler= SIG_DFL;
       sigemptyset(&sig.sa_mask);
       sig.sa_flags= 0;
@@ -980,11 +989,10 @@ int main(int argc, char *const *argv) {
                catnamebuf,strerror(errno));
        exit(-1);
       }
-      catnamebuf[sizeof(catnamebuf)-1]= 0;
       reading= fdsetup[fd].mods & fdm_read;
       catdup(catnamebuf, fdsetup[fd].copyfd, reading ? 0 : 1);
       catdup(catnamebuf, fdsetup[fd].pipefd, reading ? 1 : 0);
-      execlp("cat",catnamebuf,(char*)0);
+      execl("/bin/cat",catnamebuf,(char*)0);
       fprintf(stderr,"userv: %s: cannot exec `cat': %s\n",catnamebuf,strerror(errno));
       exit(-1);
     }
index 539ecf0..5fdfd76 100644 (file)
--- a/common.h
+++ b/common.h
@@ -32,34 +32,32 @@ static const unsigned char protocolchecksumversion[PCSUMSIZE]= {
 # define VARDIR "/var/run/userv"
 #endif
 
-#define DIRSEP "/"
-
 #ifndef RENDEZVOUS
 # define RENDEZVOUS "socket"
 #endif
 
 #ifndef RENDEZVOUSPATH
-# define RENDEZVOUSPATH VARDIR DIRSEP RENDEZVOUS
+# define RENDEZVOUSPATH VARDIR "/" RENDEZVOUS
 #endif
 
 #ifndef PIPEFORMAT
-# ifdef AC_SYS_LONG_FILENAMES
-#  define PIPEFORMAT "pipe.%lu.%lu.%d"
-#  define PIPEFORMATEXTEND (sizeof(long)*3*2+sizeof(int)*3+1)
-# else
-#  define PIPEFORMAT "%lx.%lx.%x"
-#  define PIPEFORMATEXTEND (sizeof(long)*2*2+sizeof(int)*2+1)
-# endif
+# define PIPEFORMAT "%lx.%lx.%x"
+# define PIPEPATTERN "[0-9a-f]*.[0-9a-f]*.*[0-9a-f]"
+# define PIPEFORMATEXTEND (sizeof(long)*2*2+sizeof(int)*2+1)
+# define PIPEMAXLEN (sizeof(PIPEFORMAT)+PIPEFORMATEXTEND)
 #endif
 
 #ifndef PIPEPATHFORMAT
-# define PIPEPATHFORMAT VARDIR DIRSEP PIPEFORMAT
+# define PIPEPATHFORMAT VARDIR "/" PIPEFORMAT
 # define PIPEPATHMAXLEN (sizeof(PIPEPATHFORMAT)+PIPEFORMATEXTEND)
 #endif
 
-#define MAX_ALLOW_FD 255
+#define MAX_ALLOW_FD 1024
 #define MAX_INCLUDE_NEST 40
-#define MAX_OVERRIDE_LEN (1024*1024)
+#define MAX_GENERAL_STRING (1024*1024)
+#define MAX_OVERRIDE_LEN MAX_GENERAL_STRING
+#define MAX_ARGSDEFVARS 4096
+#define MAX_GIDS 1024
 
 #ifdef DEBUG
 # define BASE_MAGIC 0x5deb7567 /* "\x5d\xebug" */
index 4d8ab26..b42a728 100644 (file)
@@ -39,7 +39,6 @@ AC_ARG_ENABLE(debug,
 
 AC_PROG_CC
 AC_PROG_INSTALL
-AC_SYS_LONG_FILE_NAMES
 
 AC_SUBST(OPTIMISE)
 if test "${GCC-no}" = yes; then
index e37b862..25b6f71 100644 (file)
--- a/daemon.h
+++ b/daemon.h
 # endif
 #endif
 
+#ifndef DEFAULTPATH_USER
+# define DEFAULTPATH_USER "/usr/local/bin:/bin:/usr/bin"
+#endif
+
+#ifndef DEFAULTPATH_ROOT
+# define DEFAULTPATH_ROOT "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
+#endif
+  
+#ifndef SETENVIRONMENT
+# define SETENVIRONMENT "environment"
+#endif
+
 #define USERRCFILE                  "rc"
 #define SYSTEMUSERVCONFIGDIR        "userv"
 #define SHELLLIST                   "shells"
 #define DEFAULTINCLUDELOOKUP        ":default"
 #define EMPTYINCLUDELOOKUP          ":empty"
 
-#define USERDIRPREFIX               USERDIR DIRSEP
+#define USERDIRPREFIX               USERDIR "/"
 #define USERCONFIGDIRBASE           SYSTEMUSERVCONFIGDIR
 #define USERCONFIGDIR               HIDDENPREFIX USERCONFIGDIRBASE
-#define USERUSERVCONFIGPATH         USERDIR DIRSEP USERCONFIGDIR
-#define USERRCFILEPATH              USERUSERVCONFIGPATH DIRSEP USERRCFILE
-#define SYSTEMUSERVCONFIGPATH       SYSTEMCONFIGDIR DIRSEP SYSTEMUSERVCONFIGDIR
-#define SYSTEMRCFILEDEFAULTPATH     SYSTEMUSERVCONFIGPATH DIRSEP SYSTEMRCFILEDEFAULT
-#define SYSTEMRCFILEOVERRIDEPATH    SYSTEMUSERVCONFIGPATH DIRSEP SYSTEMRCFILEOVERRIDE
-#define SHELLLISTPATH               SYSTEMCONFIGDIR DIRSEP SHELLLIST
+#define USERUSERVCONFIGPATH         USERDIR "/" USERCONFIGDIR
+#define USERRCFILEPATH              USERUSERVCONFIGPATH "/" USERRCFILE
+#define SYSTEMUSERVCONFIGPATH       SYSTEMCONFIGDIR "/" SYSTEMUSERVCONFIGDIR
+#define SYSTEMRCFILEDEFAULTPATH     SYSTEMUSERVCONFIGPATH "/" SYSTEMRCFILEDEFAULT
+#define SYSTEMRCFILEOVERRIDEPATH    SYSTEMUSERVCONFIGPATH "/" SYSTEMRCFILEOVERRIDE
+#define SHELLLISTPATH               SYSTEMCONFIGDIR "/" SHELLLIST
+#define SETENVIRONMENTPATH          SYSTEMCONFIGDIR "/" SETENVIRONMENT
 
 #define USERDIR                     "~"
 #define HIDDENPREFIX                "."
 #define MAX_ERRMSG_LEN 2048
 #define ERRMSG_RESERVE_ERRNO 128
 
-int parse_string(const char *string, const char *descrip);
+int parse_string(const char *string, const char *descrip, int isinternal);
 void parseerrprint(const char *fmt, ...) PRINTFFORMAT(1,2);
 void ensurelogopen(int wantfacility);
 void ensurefdarray(int fd);
@@ -111,6 +124,10 @@ void debug_dumpparameter(const char *parm, char **values);
 pid_t nondebug_fork(void);
 const char *nondebug_serviceuserdir(const char *ifnondebug);
 
+void execservice(const int synchsocket[], int clientfd) NONRETURNING;
+void servicerequest(int sfd) NONRETURNING;
+int synchread(int fd, int ch);
+
 struct fdstate {
   int iswrite, realfd, holdfd;
   int wantstate;
@@ -120,7 +137,6 @@ struct fdstate {
   /* tokv_word_read, tokv_word_write */
 };
 
-extern gid_t *gidarray;
 extern char **argarray;
 extern char *((*defvararray)[2]);
 extern struct fdstate *fdarray; /* indexed by nominal fd */
@@ -130,14 +146,13 @@ extern struct request_msg request_mbuf;
 extern char *serviceuser, *service, *logname, *cwd;
 extern char *overridedata, *userrcfile;
 extern char *serviceuser_dir, *serviceuser_shell, *callinguser_shell;
-extern uid_t serviceuser_uid, callinguser_uid;
-extern gid_t serviceuser_gid;
+extern int service_ngids;
+extern gid_t *calling_gids, *service_gids;
+extern const char **calling_groups, **service_groups;
+extern uid_t serviceuser_uid;
 extern char *execpath, **execargs;
 extern int execute; /* One of the execution modes tokt_execmode */
 extern int setenvironment, suppressargs, disconnecthup;
-extern int ehandling; /* One of the error handling modes tokt_ehandlemode */
-extern int ehlogfacility, ehloglevel, syslogopenfacility, ehfilekeep;
-extern FILE *ehfile;
-extern char *ehfilename;
+extern int syslogopenfacility;
 
 #endif
similarity index 77%
rename from ddebug.c
rename to debug.c
index 1ea8a06..9174d83 100644 (file)
--- a/ddebug.c
+++ b/debug.c
@@ -96,6 +96,12 @@ static void truefalsedump(const char *whichstr, int val) {
   printf("%s: %s\n",whichstr,val?"yes":"no");
 }
 
+static void groupsdump(int ngids, const gid_t *gids, const char *const *groups) {
+  int i;
+  
+  for (i=0; i<ngids; i++) printf(" %ld(%s)",(long)gids[i],groups[i]);
+}
+
 void debug_dumprequest(pid_t mypid) {
   int i, fd;
   
@@ -103,15 +109,26 @@ void debug_dumprequest(pid_t mypid) {
          "client pid: %ld\n"
          "service: `%s'\n"
          "service user: `%s'\n"
-         "calling user: `%s'\n"
-         "calling cwd: `%s'\n"
-         "calling uid: %ld\n"
-         "calling gids:",
+        "service uid: %ld\n"
+         "service user shell: `%s'\n"
+         "service user dir: `%s'\n"
+        "service groups:",
          (long)mypid, (long)request_mbuf.clientpid,
-         service, serviceuser, logname, cwd,
-         (long)request_mbuf.callinguid);
-  for (i=0; i<request_mbuf.ngids; i++) printf(" %ld",(long)gidarray[i]);
-  printf("\n" "fds:");
+         service, serviceuser, (long)serviceuser_uid,
+        serviceuser_shell, serviceuser_dir);
+  groupsdump(service_ngids,service_gids,service_groups);
+  printf("\n"
+        "calling user: `%s'\n"
+        "calling uid: %ld\n"
+         "calling user shell: `%s'\n"
+        "calling groups:",
+        logname, (long)request_mbuf.callinguid,
+        callinguser_shell);
+  groupsdump(request_mbuf.ngids,calling_gids,calling_groups);
+  printf("\n"
+        "calling cwd: `%s'\n"
+        "fds:",
+         cwd);
   for (fd=0; fd<fdarrayused; fd++)
     if (fdarray[fd].iswrite != -1)
       printf(" %d%s",fd,fdarray[fd].iswrite ? "w" : "r");
@@ -146,14 +163,6 @@ void debug_dumpexecsettings(void) {
   truefalsedump("suppress-args",suppressargs);
   truefalsedump("disconnect-hup",disconnecthup);
   truefalsedump("set-environment",setenvironment);
-  printf("errors: ");
-  switch (ehandling) {
-  case tokv_word_errorstostderr: printf("stderr"); break;
-  case tokv_word_errorstofile: printf("file"); break;
-  case tokv_word_errorstosyslog: printf("syslog %d.%d",ehlogfacility,ehloglevel); break;
-  default: abort();
-  }
-  printf("\n");
 }
 
 void debug_dumpparameter(const char *parm, char **values) {
@@ -173,6 +182,22 @@ static int groupsallin(int na, const gid_t *lista,
 }
 
 int setgroups(size_t wantsize, const gid_t *wantlist) {
+  /* This is a bit of a hack really.  What we want when we're in debug mode is to
+   * have initgroups() be a no-op iff the groups are already set right (so that
+   * we notice if we're trying to change to the wrong user) but to fail if they're
+   * not.
+   *
+   * We can't just call initgroups() because it unconditionally calls
+   * setgroups, which always fails for non-root even if the two group
+   * lists are the same.  So here we have a faked-up setgroups which
+   * uses getgroups to see what the group list is and `succeeds' if
+   * the actual group list and the desired one have the same set of
+   * groups, and fails with EPERM if the real setgroups would have
+   * added group(s) or otherwise EINVAL if it would have removed some.
+   *
+   * The usual magic with dynamic linking makes the libc initgroups(3) call
+   * pick up our setgroups() rather than the real setgroups(2).
+   */
   int realsize, e;
   gid_t *reallist;
 
index 8d4088c..09b4a61 100644 (file)
@@ -32,19 +32,19 @@ divert(2)dnl
   format(``%-30s'',`tokv_$1=')`$3|toki_$1',
 divert(odiv)popdef(`odiv')')
 
-define(`cautotoki',`01')
-define(`cautotokt',eval(`010000'))
+define(`cautotoki',eval(`0x1'))
+define(`cautotokt',eval(`0x1000'))
 
-define(`autovalistype',`hasvalistype(`$1',format(``0%03o'',cautotoki),`$2')`'define(`cautotoki',incr(cautotoki))')
+define(`autovalistype',`hasvalistype(`$1',format(``0x%08x'',cautotoki),`$2')`'define(`cautotoki',incr(cautotoki))')
 
 define(`autovaldeftype',`pushdef(`odiv',divnum)divert(4)dnl
-  format(``%-25s'',`tokt_$1=')format(``0%011o'',cautotokt),
+  format(``%-25s'',`tokt_$1=')format(``0x%08x'',cautotokt),
 divert(odiv)popdef(`odiv')define(`cautotokt',eval(cautotokt`*2'))')
 
 define(`nametypelexpatexec',`
 autovalistype(`$1',`$2')
 pushdef(`odiv',divnum)divert(3)dnl
-`$3 { $4'`atnewline= 0; return tokv_$1; }'
+`$3 { $4'`return tokv_$1; }'
 divert(odiv)popdef(`odiv')')
 
 define(`wordtypelexexec',
@@ -67,6 +67,7 @@ autovaldeftype(`string')
 autovaldeftype(`execmode')
 autovaldeftype(`ehandlemode')
 autovaldeftype(`misc')
+autovaldeftype(`internal')
 
 dnl simple isdirectives
 define(`isdirectivefn',`dnl
@@ -74,7 +75,8 @@ wordtypelexexec(`$1',`tokt_directive$3',`lr_dir= $2; $4')dnl
 pushdef(`odiv',divnum)
 divert(odiv)popdef(`odiv')')
 define(`isdirective',`isdirectivefn(`$1',`df_'makename(`$1'),`$2')')
-define(`isdirectiveinternal',`isdirectivefn(`$1',`dfi_'makename(`$1'),`$2')')
+define(`isdirectiveinternal',`isdirectivefn(`$1',`dfi_'makename(`$1'),
+                                            `|tokt_internal$2')')
 define(`isexecmode',`isdirective(`$1',`|tokt_execmode')')
 define(`isehandlemode',`isdirective(`$1',`|tokt_ehandlemode')')
 define(`isfdwant',`isdirectivefn(`$1',`dfg_fdwant',`',
@@ -107,7 +109,7 @@ isdirective(`include-lookup')
 isdirectivefn(`include-lookup-all',`df_includelookup')
 isdirective(`include-directory')
 isdirective(`message')
-isdirectivefn(`_include-sysconfig',`df_include')
+isdirectivefn(`_include-sysconfig',`df_include',`|tokt_internal')
 isdirectiveinternal(`_include-user-rcfile')
 isdirectiveinternal(`_include-client-config')
 
@@ -165,7 +167,7 @@ isloglevellexpat(`warning',`warn(ing)?')
 isloglevel(`err')dnl also the word error, which has dual meaning (below)
 isloglevel(`crit')
 isloglevel(`alert')
-isloglevelexpat(`emerg',`emerg|panic')
+isloglevellexpat(`emerg',`emerg|panic')
 
 dnl syslog facilities
 define(`islogfacilitylexpat',
@@ -196,25 +198,10 @@ wordtypelexexec(`read',`tokt_readwrite',`')
 wordtypelexexec(`write',`tokt_readwrite',`')
 
 dnl small nonnegative integers and fd ranges
-dnl some of these have two tokt_ bits set, because they can be several
-dnl things.
-nametypelexpatexec(`ordinal',`tokt_number|tokt_fdrange|tokr_word',`[0-9]{1,8}',
-`{ char *ep;
-   lr_min=lr_max= (int)strtoul(yytext,&ep,10);
-   assert(!*ep); }; ')
-nametypelexpatexec(`fdrange',`tokt_fdrange|tokr_punct',`[0-9]{1,8}-[0-9]{1,8}',
-`{ char *ep;
-   lr_min=(int)strtoul(yytext,&ep,10);
-   assert(*ep == HYPHEN); assert(*++ep);
-   lr_max=(int)strtoul(ep,&ep,10);
-   if (lr_max < lr_min) {
-     atnewline= 0; parseerrprint("fd range has min > max"); return tokv_error;
-   }
-   assert(!*ep); }; ')
-nametypelexpatexec(`fdstoend',`tokt_fdrange|tokr_punct',`[0-9]{1,8}-',
-`{ char *ep;
-   lr_min= (int)strtoul(yytext,&ep,10); lr_max=-1;
-   assert(*ep == HYPHEN); assert(!*++ep); }; ')
+dnl some of these have two tokt_ bits set, because they can be several things.
+autovalistype(`ordinal',       `tokt_number|tokt_fdrange|tokr_word')
+autovalistype(`fdrange',       `tokt_fdrange|tokr_punct')
+autovalistype(`fdstoend',      `tokt_fdrange|tokr_punct')
 nametypelexpatexec(`dollar',`tokt_misc|tokr_punct',`\$',`')
 
 dnl non-word things
index 2f9dee6..0337686 100644 (file)
@@ -44,8 +44,75 @@ include(language.i4)
 
 #define HYPHEN '-'
 
-static int lineno, atnewline, notedreferer;
+typedef int directive_fnt(int dtoken);
+static directive_fnt df_reject, df_execute, df_executefrompath;
+static directive_fnt df_executefromdirectory;
+static directive_fnt df_errorstostderr, df_errorstosyslog, df_errorstofile;
+static directive_fnt dfg_fdwant, dfg_setflag;
+static directive_fnt df_reset, df_cd, df_userrcfile, df_include;
+static directive_fnt df_includelookup, df_includedirectory;
+static directive_fnt df_message, df_error, df_quit, df_eof;
+static directive_fnt df_if, df_catchquit, df_errorspush;
+static directive_fnt dfi_includeuserrcfile, dfi_includeclientconfig;
+/* directive functions return:
+ *  0 for success having scanned up to and including end of line but not beyond,
+ *  or tokv_error or tokv_quit.
+ */
+
+typedef int parmcondition_fnt(int ctoken, char **parmvalues, int *rtrue);
+static parmcondition_fnt pcf_glob, pcf_range, pcf_grep;
+/* all conditional functions return tokv_error for failure or 0 for success
+ *  at parsing and testing, in which case *rtrue is set to 0 or 1.
+ *  On success they have scanned up to and including the condition's
+ *  terminating newline.
+ * The parameter-based conditionals take a list of parameter values
+ * as obtained from the parameter functions and pa_parameter,
+ * and do _not_ free it.
+ */
+
+typedef int parameter_fnt(int ptoken, char ***rvalues);
+static parameter_fnt pf_service;
+static parameter_fnt pf_callinguser, pf_serviceuser;
+static parameter_fnt pf_callinggroup, pf_servicegroup;
+static parameter_fnt pf_callingusershell, pf_serviceusershell;
+/* Parameter functions return tokv_error or 0 for success at parsing
+ * and determining the value, in which case *rvalues is made to be
+ * a mallocd null-terminated array of pointers to mallocd strings.
+ * freeparm can be used to free such an array.
+ */
+
+static int yylex(void);
+/* Returns a token (which may be an eof or error exception) */
+
+static directive_fnt *lr_dir;
+static parmcondition_fnt *lr_parmcond;
+static parameter_fnt *lr_parameter;
+static int lr_loglevel, lr_logfacility, lr_min, lr_max, *lr_flag;
+static int lr_flagval, lr_controlend;
+static int lr_fdwant_readwrite; /* -1=never, 0=opt, 1=always */
+
+/* Forward declarations of things used in lexer and parser */
+
+struct parser_state {
+  int lineno, reportlineno, notedreferer, isinternal;
+  const char *filename;
+  YY_BUFFER_STATE ybuf;
+  struct parser_state *upstate;
+};
+
+static struct parser_state *cstate;
+
+struct error_handling {
+  int handling; /* One of the error handling modes tokt_ehandlemode */
+  int logfacility, loglevel, filekeep;
+  FILE *file;
+  char *filename;
+};
+
+static struct error_handling eh = { tokv_word_errorstostderr, 0,0,0,0,0 };
+
 static int dequote(char *inplace);
+
 %}
 %option noyywrap
 %%
@@ -54,14 +121,49 @@ dnl simple words
 undivert(3)
 changequote({*,*})
 {*
+[0-9]{1,8}             {
+                         char *ep;
+                         lr_min=lr_max= (int)strtoul(yytext,&ep,10);
+                         assert(!*ep);
+                         return tokv_ordinal;
+                       }
+[0-9]{1,8}-[0-9]{1,8}  {
+                         char *ep;
+                         lr_min=(int)strtoul(yytext,&ep,10);
+                         assert(*ep == HYPHEN);
+                         assert(*++ep);
+                         lr_max=(int)strtoul(ep,&ep,10);
+                         assert(!*ep);
+                         if (lr_max < lr_min) {
+                           parseerrprint("fd range has min > max");
+                           return tokv_error;
+                         }
+                         return tokv_fdrange;
+                       }
+[0-9]{1,8}-            {
+                         char *ep;
+                         lr_min= (int)strtoul(yytext,&ep,10);
+                         assert(*ep == HYPHEN);
+                         assert(!*++ep);
+                         lr_max=-1;
+                         return tokv_fdstoend;
+                       }
 [\ \t]+                        return tokv_lwsp;
-[\ \t]*\n              atnewline= 1; lineno++; return tokv_newline;
-[\ \t]*\#[^\n]*\n      atnewline= 1; lineno++; return tokv_newline;
-[\ \t]*\#[^\n]*                atnewline= 0; parseerrprint("missing newline at eof after comment"); return tokv_error;
-[^\ \t\n]+             atnewline= 0; return tokv_barestring;
+[\ \t]*\n              cstate->lineno++; return tokv_newline;
+[\ \t]*\#[^\n]*\n      cstate->lineno++; return tokv_newline;
+[\ \t]*\#[^\n]*                {
+                         parseerrprint("missing newline at eof after comment");
+                         return tokv_error;
+                       }
+[^\ \t\n]+             return tokv_barestring;
+\"([^\\\"\n]|\\[a-z]|\\[0-9]{3}|\\x[0-9A-Fa-f]{2}|\\[:punct:]|\\[ \t]*\n)*\" {
+                         return dequote(yytext);
+                       }
+\".*                   {
+                         parseerrprint("misquoted or unterminated string");
+                         return tokv_error;
+                       }
 <<EOF>>                        return tokv_eof;
-\"([^\\\"\n]|\\[a-z]|\\[0-9]{3}|\\x[0-9a-f]{2}|\\[:punct:]|\\[ \t]*\n)*\" return dequote(yytext);
-\".*                   atnewline= 0; parseerrprint("misquoted or unterminated string"); return tokv_error;
 *}
 changequote(`,')
 %%
diff --git a/lib.c b/lib.c
index 4af9b80..6c3cd0a 100644 (file)
--- a/lib.c
+++ b/lib.c
@@ -29,7 +29,7 @@
 #include "config.h"
 #include "lib.h"
 
-char *xmstrcat3save(const char *a, const char *b, const char *c) {
+char *xstrcat3save(const char *a, const char *b, const char *c) {
   char *r;
 
   r= xmalloc(strlen(a)+strlen(b)+strlen(c)+1);
@@ -39,7 +39,7 @@ char *xmstrcat3save(const char *a, const char *b, const char *c) {
   return r;
 }
 
-char *xmstrsave(const char *s) {
+char *xstrsave(const char *s) {
   char *r;
 
   r= xmalloc(strlen(s)+1);
@@ -47,7 +47,7 @@ char *xmstrsave(const char *s) {
   return r;
 }
 
-char *xmstrsubsave(const char *begin, int len) {
+char *xstrsubsave(const char *begin, int len) {
   char *r;
   
   r= xmalloc(len+1);
@@ -67,12 +67,6 @@ void *xrealloc(void *p, size_t s) {
   return p;
 }
 
-char *xstrdup(const char *str) {
-  char *r;
-  r= xmalloc(strlen(str)+1);
-  strcpy(r,str); return r;
-}
-
 void makeroom(char **buffer, int *size, int needed) {
   if (*size >= needed) return;
   *buffer= xrealloc(*buffer,needed);
diff --git a/lib.h b/lib.h
index 8a9c348..fc9d016 100644 (file)
--- a/lib.h
+++ b/lib.h
 #ifndef LIB_H
 #define LIB_H
 
-char *xmstrcat3save(const char *a, const char *b, const char *c);
-char *xmstrsave(const char *s);
-char *xmstrsubsave(const char *begin, int len);
+char *xstrcat3save(const char *a, const char *b, const char *c);
+char *xstrsave(const char *s);
+char *xstrsubsave(const char *begin, int len);
 
 void miscerror(const char *what) NONRETURNING;
 void syscallerror(const char *what) NONRETURNING;
 void *xmalloc(size_t s);
 void *xrealloc(void *p, size_t s);
-char *xstrdup(const char *str);
 void makeroom(char **buffer, int *size, int needed);
 
 /* It doesn't appear to be documented whether [v]snprintf put a null
diff --git a/overlord.c b/overlord.c
new file mode 100644 (file)
index 0000000..be2553a
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * userv - overlord.c
+ * daemon main program, collects request and forks handlers
+ *
+ * Copyright (C)1996-1997 Ian Jackson
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with userv; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <wait.h>
+#include <unistd.h>
+#include <signal.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <fnmatch.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <dirent.h>
+#include <sys/un.h>
+
+#include "config.h"
+#include "common.h"
+#include "daemon.h"
+
+static void checkstalepipes(void) {
+  /* There is an unimportant race here.  If there is a stale pipe but
+   * another pair of processes with the same pids is about to create a
+   * new one we can check that the pipe is stale before they recreate
+   * it but then only remove it afterwards; then we remove the pipe
+   * they're actually using causing that invocation to fail with
+   * ENOENT on the pipe.  However, this can only happen if things are
+   * already shafted, because we check for stale pipes at startup
+   * before any children have been started, and then only when a child
+   * dies unhelpfully - and we actually have to have some stale pipes
+   * for the race to exist.
+   */
+  DIR *dir;
+  struct dirent *de;
+  struct stat stab;
+  time_t now;
+  unsigned long timediff;
+  int r;
+  
+  if (time(&now) == -1) { syslog(LOG_ERR,"get current time: %m"); return; }
+  dir= opendir(".");
+  if (!dir) { syslog(LOG_ERR,"open directory " VARDIR ": %m"); return; }
+  while ((de= readdir(dir))) {
+    if (fnmatch(PIPEPATTERN,de->d_name,FNM_PATHNAME|FNM_PERIOD)) continue;
+    r= lstat(de->d_name,&stab); if (r && errno==ENOENT) continue;
+    if (r) { syslog(LOG_ERR,"could not stat `" VARDIR "/%s': %m",de->d_name); continue; }
+    timediff= (unsigned long)now - (unsigned long)stab.st_ctime;
+    if (timediff >= (~0UL>>1) || timediff < 3600) continue;
+    if (unlink(de->d_name) && errno!=ENOENT)
+      syslog(LOG_ERR,"could not remove stale pipe `%s': %m",de->d_name);
+  }
+  if (closedir(dir)) syslog(LOG_ERR,"close directory " VARDIR ": %m");
+}
+
+static void sighandler_chld(int x) {
+  pid_t r;
+  int status, es;
+
+  es= errno;
+  for (;;) {
+    r= waitpid((pid_t)-1,&status,WNOHANG);
+    if (!r || (r==-1 && errno==ECHILD)) break;
+    if (r==-1) { syslog(LOG_ERR,"wait in sigchild handler gave error: %m"); break; }
+    if (WIFSIGNALED(status)) {
+      if (WCOREDUMP(status))
+       syslog(LOG_ERR,"call pid %ld dumped core due to signal %s",(long)r,
+              strsignal(WTERMSIG(status)));
+      else
+       syslog(LOG_ERR,"call pid %ld died due to signal %s",
+              (long)r,strsignal(WTERMSIG(status)));
+    } else if (!WIFEXITED(status)) {
+      syslog(LOG_ERR,"call pid %ld died due to unknown reason, code %ld",
+            (long)r,status);
+    } else if (WEXITSTATUS(status)>12) {
+      if (WEXITSTATUS(status)>24)
+       syslog(LOG_ERR,"call pid %ld exited with status %ld >24",
+              (long)r,WEXITSTATUS(status));
+      checkstalepipes();
+    }
+  }
+  errno= es;
+  return;
+}
+
+static void blocksignals(int how) {
+  int r;
+  sigset_t set;
+
+  sigemptyset(&set);
+  sigaddset(&set,SIGCHLD);
+  r= sigprocmask(how,&set,0); assert(!r);
+}
+
+int main(int argc, char *const *argv) {
+  int mfd, sfd, csocklen, e;
+  struct sigaction childact;
+  struct sockaddr_un ssockname, csockname;
+  pid_t child;
+
+#ifdef NDEBUG
+  abort(); /* Do not disable assertions in this security-critical code ! */
+#endif
+
+  if (argc>1) { fputs("usage: uservd\n",stderr); exit(3); }
+
+  openlog(USERVD_LOGIDENT,LOG_NDELAY,USERVD_LOGFACILITY);
+
+  if (chdir(VARDIR)) { syslog(LOG_CRIT,"cannot change to " VARDIR ": %m"); exit(4); }
+  checkstalepipes();
+
+  mfd= socket(AF_UNIX,SOCK_STREAM,0);
+  if (!mfd) { syslog(LOG_CRIT,"cannot create master socket: %m"); exit(4); }
+
+  assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUS));
+  ssockname.sun_family= AF_UNIX;
+  strcpy(ssockname.sun_path,RENDEZVOUS);
+  unlink(RENDEZVOUS);
+  if (bind(mfd,(struct sockaddr*)&ssockname,sizeof(ssockname)))
+    { syslog(LOG_CRIT,"cannot bind master socket: %m"); exit(4); }
+  if (listen(mfd,5))
+    { syslog(LOG_CRIT,"cannot listen on master socket: %m"); exit(4); }
+
+  childact.sa_handler= sighandler_chld;
+  sigemptyset(&childact.sa_mask);
+  childact.sa_flags= SA_NOCLDSTOP;
+  if (sigaction(SIGCHLD,&childact,0))
+    { syslog(LOG_CRIT,"cannot setup sigchld handler: %m"); exit(4); }
+  syslog(LOG_NOTICE,"started");
+  for (;;) {
+    csocklen= sizeof(csockname);
+    blocksignals(SIG_UNBLOCK);
+    sfd= accept(mfd,(struct sockaddr*)&csockname,&csocklen);
+    e= errno;
+    blocksignals(SIG_BLOCK);
+    if (sfd<0) {
+      errno= e;
+      if (errno == EINTR) continue;
+      if (errno == ENOMEM) {
+        syslog(LOG_ERR,"unable to accept connection: %m");
+       continue;
+      } else {
+       syslog(LOG_CRIT,"unable to accept new connections: %m");
+       exit(5);
+      }
+    }
+    child= nondebug_fork();
+    if (child == (pid_t)-1) {
+      syslog(LOG_ERR,"unable to fork server: %m");
+      close(sfd);
+      continue;
+    }
+    if (!child) {
+      close(mfd);
+      closelog();
+      blocksignals(SIG_UNBLOCK);
+      servicerequest(sfd);
+    }
+    close(sfd);
+  }
+}
index 2395837..0f8e195 100644 (file)
--- a/parser.c
+++ b/parser.c
  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
-struct parser_state {
-  int lineno, atnewline, notedreferer;
-  const char *filename;
-  YY_BUFFER_STATE ybuf;
-  struct parser_state *upstate;
-};
-
-static const char *currentfile= 0;
-static struct parser_state *parser_topstate= 0;
-
 static directive_fnt *lr_dir=0;
 static parmcondition_fnt *lr_parmcond=0;
 static parameter_fnt *lr_parameter=0;
@@ -42,8 +32,16 @@ static int lr_fdwant_readwrite;
 static void useless(void) { (void)yyunput; (void)useless; /* to shut up GCC ! */ }
 
 static void closeerrorfile(void) {
-  if (ehfile && !ehfilekeep) { fclose(ehfile); free(ehfilename); }
-  ehfile= 0; ehfilename= 0; ehfilekeep= 0;
+  if (eh.file && !eh.filekeep) {
+    if (fclose(eh.file)) {
+      eh.handling= tokv_word_errorstostderr;
+      parseerrprint("error writing to error log file `%s': %s",
+                   eh.filename,strerror(errno));
+    }
+    free(eh.filename);
+  }
+  eh.handling= 0;
+  eh.file= 0; eh.filename= 0; eh.filekeep= 0;
 }
 
 static void senderrmsg(const char *errmsg, int useehandling) {
@@ -57,24 +55,24 @@ static void senderrmsg(const char *errmsg, int useehandling) {
     senderrmsgstderr(errmsg);
     break;
   case tokv_word_errorstosyslog:
-    ensurelogopen(ehlogfacility);
-    syslog(ehloglevel,"%s",errmsg);
+    ensurelogopen(eh.logfacility);
+    syslog(eh.loglevel,"%s",errmsg);
     break;
   case tokv_word_errorstofile:
     if (time(&now)==-1) syscallerror("get current time");
     lt= localtime(&now); if (!lt) syscallerror("convert current time");
-    if (fprintf(ehfile,"%d-%02d-%02d %02d:%02d:%02d: uservd: %s\n",
+    if (fprintf(eh.file,"%d-%02d-%02d %02d:%02d:%02d: uservd: %s\n",
                lt->tm_year+1900, lt->tm_mon+1, lt->tm_mday,
                lt->tm_hour, lt->tm_min, lt->tm_sec,
                errmsg) == EOF) {
       e= errno;
-      closeerrorfile(); ehandling= tokv_word_errorstofile;
+      closeerrorfile(); eh.handling= tokv_word_errorstofile;
       snyprintf(suberrmsg,sizeof(suberrmsg),
                "error writing to error log file `%.*s': %s;"
                " reverting to errors-to-stderr",
-               (int)(sizeof(suberrmsg)>>1),ehfilename,strerror(e));
-      senderrmsg(suberrmsg,ehandling);
-      senderrmsg(errmsg,ehandling);
+               (int)(sizeof(suberrmsg)>>1),eh.filename,strerror(e));
+      senderrmsg(suberrmsg,eh.handling);
+      senderrmsg(errmsg,eh.handling);
     }
     break;
   default:
@@ -82,22 +80,22 @@ static void senderrmsg(const char *errmsg, int useehandling) {
   }
 }
 
-static void errwhere(int iflineno, const char *filename, int *ifnotedreferer,
-                    struct parser_state *upstate, char *bufput, int bufputlen) {
+static void errwhere(struct parser_state *tstate, char *bufput, int bufputlen) {
   static const char suffix[]= "references ...";
   char errmsg[MAX_ERRMSG_LEN];
   
-  if (!upstate) {
-    filename= "<initialisation>";
-  } else if (!*ifnotedreferer && upstate->upstate && upstate->upstate->upstate) {
-    errwhere(upstate->lineno-upstate->atnewline,upstate->filename,
-            &upstate->notedreferer,upstate->upstate,
-            errmsg,sizeof(errmsg)-sizeof(suffix));
+  if (!tstate) {
+    strnycpy(bufput,"<initialisation>: ",bufputlen);
+    return;
+  }
+  if (!tstate->notedreferer && tstate->upstate && !tstate->upstate->isinternal) {
+    errwhere(tstate->upstate,errmsg,sizeof(errmsg)-sizeof(suffix));
     strcat(errmsg,suffix);
-    senderrmsg(errmsg,ehandling);
-    *ifnotedreferer= 1;
+    senderrmsg(errmsg,eh.handling);
+    tstate->notedreferer= 1;
   }
-  snyprintf(bufput,bufputlen,"%.*s:%d: ",bufputlen-10,filename,iflineno);
+  snyprintf(bufput,bufputlen,"%.*s:%d: ",bufputlen-10,
+           tstate->filename,tstate->reportlineno);
 }
 
 void parseerrprint(const char *fmt, ...) {
@@ -105,10 +103,9 @@ void parseerrprint(const char *fmt, ...) {
   char errmsg[MAX_ERRMSG_LEN];
 
   va_start(al,fmt);
-  errwhere(lineno-atnewline,currentfile,&notedreferer,parser_topstate,
-          errmsg,sizeof(errmsg)>>1);
+  errwhere(cstate,errmsg,sizeof(errmsg)>>1);
   vsnytprintfcat(errmsg,sizeof(errmsg),fmt,al);
-  senderrmsg(errmsg,ehandling);
+  senderrmsg(errmsg,eh.handling);
   va_end(al);
 }
 
@@ -209,44 +206,35 @@ static const char *string2path(const char *in) {
   static char *p=0;
   static int pl=0;
   
-  int l;
-  
-  if (strncmp(in,USERDIRPREFIX,sizeof(USERDIRPREFIX)-1)) return in;
-  l= strlen(serviceuser_dir)+strlen(in)+1-(sizeof(USERDIRPREFIX)-1)+
-     sizeof(DIRSEP)-1;
-  if (l>pl) { p= realloc(p,l); pl=l; }
-  strcpy(p,serviceuser_dir); strcat(p,DIRSEP);
-  strcat(p,in+(sizeof(USERDIRPREFIX)-1));
+  if (strncmp(in,"~/",2)) return in;
+  makeroom(&p,&pl,strlen(serviceuser_dir)+1+strlen(in+2)+1);
+  snyprintf(p,pl,"%s/%s",serviceuser_dir,in+2);
   return p;
 }
 
-static void parser_push(struct parser_state *saveinto,
-                       const char *newfile) {
-  saveinto->lineno= lineno;
-  saveinto->atnewline= atnewline;
-  saveinto->filename= currentfile;
-  saveinto->notedreferer= notedreferer;
-  saveinto->ybuf= YY_CURRENT_BUFFER;
-  saveinto->upstate= parser_topstate;
-
-  lineno= 1;
-  notedreferer= 0;
-  currentfile= newfile;
-  parser_topstate= saveinto;
+static void parser_push(struct parser_state *usestate,
+                       const char *newfile,
+                       YY_BUFFER_STATE ybuf,
+                       int isinternal) {
+  usestate->lineno= 1;
+  usestate->reportlineno= 1;
+  usestate->filename= newfile;
+  usestate->notedreferer= 0;
+  usestate->isinternal= isinternal;
+  usestate->ybuf= ybuf;
+  usestate->upstate= cstate;
+
+  cstate= usestate;
+  yy_switch_to_buffer(ybuf);
 }
 
 static void parser_pop(void) {
-  YY_BUFFER_STATE ybuf;
-  ybuf= YY_CURRENT_BUFFER;
-  
-  lineno= parser_topstate->lineno;
-  atnewline= parser_topstate->atnewline;
-  currentfile= parser_topstate->filename;
-  notedreferer= parser_topstate->notedreferer;
-  if (parser_topstate->ybuf) yy_switch_to_buffer(parser_topstate->ybuf);
-  parser_topstate= parser_topstate->upstate;
-  
-  yy_delete_buffer(ybuf);
+  struct parser_state *oldstate;
+
+  oldstate= cstate;
+  cstate= cstate->upstate;
+  if (cstate) yy_switch_to_buffer(cstate->ybuf);
+  yy_delete_buffer(oldstate->ybuf);
 }
 
 /* parser component functions pa_ return tokv_error or 0,
@@ -276,7 +264,7 @@ static int pa_mwsp(void) {
 static void parm_1string(char ***rvalues, const char *tocopy) {
   char **a;
   a= xmalloc(sizeof(char*)*2);
-  a[0]= xstrdup(tocopy);
+  a[0]= xstrsave(tocopy);
   a[1]= 0;
   *rvalues= a;
 }
@@ -361,6 +349,7 @@ static int pa_condition(int *rtrue) {
   } else if (token == tokv_openparen) {
     andor= 0; actrue= -1;
     for (;;) {
+      cstate->reportlineno= cstate->lineno;
       r= pa_condition(&ctrue); if (r) return r;
       switch (andor) {
       case 0:         assert(actrue==-1); actrue= ctrue;           break;
@@ -422,6 +411,7 @@ static int skip(int allowce) {
   int token, r;
 
   for (;;) { /* loop over lines */
+    cstate->reportlineno= cstate->lineno;
     do { token= yylex(); } while (token == tokv_lwsp);
     if (token & tokt_exception) {
       return token;
@@ -432,6 +422,7 @@ static int skip(int allowce) {
       r= token;
       while (r & tokt_controlstart) {
         r= skiptoeol(); if (r) return r;
+       cstate->reportlineno= cstate->lineno;
         r= skip(token); if (r & tokt_exception) return r;
       }
     } else if (!(token & tokt_directive) && !(token & tokt_condop)) {
@@ -454,6 +445,7 @@ static int parser(int allowce) {
   int token, r;
 
   for (;;) { /* loop over lines */
+    cstate->reportlineno= cstate->lineno;
     do { token= yylex(); } while (token == tokv_lwsp);
     if (token & tokt_exception) {
       return token;
@@ -470,17 +462,16 @@ static int parser(int allowce) {
   }
 }
 
-int parse_string(const char *string, const char *descrip) {
+int parse_string(const char *string, const char *descrip, int isinternal) {
   /* Returns the same things as parser, except that tokv_eof
    * is turned into 0. */
-  struct parser_state save;
+  struct parser_state usestate;
   YY_BUFFER_STATE ybuf;
   int r;
-  
-  parser_push(&save,descrip);
+
   ybuf= yy_scan_string(string);
   if (!ybuf) syscallerror("unable to create flex buffer for internal string");
-  yy_switch_to_buffer(ybuf);
+  parser_push(&usestate,descrip,ybuf,isinternal);
   
   r= parser(0);
 
@@ -494,7 +485,7 @@ static int parse_file(const char *string, int *didexist) {
    * is turned into 0. */
   static int fileparselevel= 0;
   
-  struct parser_state save;
+  struct parser_state usestate;
   YY_BUFFER_STATE ybuf;
   int r;
   FILE *file;
@@ -513,10 +504,9 @@ static int parse_file(const char *string, int *didexist) {
   }
   if (didexist) *didexist= 1;
 
-  parser_push(&save,string);
   ybuf= yy_create_buffer(file,YY_BUF_SIZE);
   if (!ybuf) syscallerror("unable to create flex buffer for file");
-  yy_switch_to_buffer(ybuf);
+  parser_push(&usestate,string,ybuf,0);
   fileparselevel++;
   
   r= parser(0);
@@ -635,7 +625,7 @@ static char *parm_ulong(unsigned long ul) {
 static int parm_usernameuid(char ***rvalues, const char *name, uid_t id) {
   char **a;
   a= xmalloc(sizeof(char*)*3);
-  a[0]= xstrdup(name);
+  a[0]= xstrsave(name);
   a[1]= parm_ulong(id);
   a[2]= 0;
   *rvalues= a; return 0;
@@ -659,7 +649,7 @@ static char *parm_gidname(gid_t id) {
     sprintf(ebuf,"look up group with id %lu",(unsigned long)id);
     syscallerror(ebuf);
   }
-  return xstrdup(ge->gr_name);
+  return xstrsave(ge->gr_name);
 }
 
 static int parm_gids(char ***rvalues, int size, gid_t *list) {
@@ -677,20 +667,11 @@ static int parm_gids(char ***rvalues, int size, gid_t *list) {
 }  
 
 int pf_callinggroup(int ptoken, char ***rvalues) {
-  return parm_gids(rvalues,request_mbuf.ngids,gidarray);
+  return parm_gids(rvalues,request_mbuf.ngids,calling_gids);
 }
 
 int pf_servicegroup(int ptoken, char ***rvalues) {
-  static int size=-1;
-  static gid_t *list;
-
-  if (size == -1) {
-    size= getgroups(0,0); if (size == -1) syscallerror("getgroups(0,0)");
-    list= xmalloc(sizeof(gid_t)*(size+1));
-    if (getgroups(size,list+1) != size) syscallerror("getgroups(size,list)");
-    list[0]= serviceuser_gid;
-  }
-  return parm_gids(rvalues,size,list);
+  return parm_gids(rvalues,service_ngids,service_gids);
 }
 
 int pf_callingusershell(int ptoken, char ***rvalues) {
@@ -749,7 +730,7 @@ static int paa_pathargs(const char **rv, char ***newargs_r) {
       size= (used+5)<<2;
       newargs= xrealloc(newargs,sizeof(char*)*(size+1));
     }
-    newargs[used++]= xmstrsave(yytext);
+    newargs[used++]= xstrsave(yytext);
   }
   newargs[used]= 0;
   *newargs_r= newargs;
@@ -769,7 +750,7 @@ int df_execute(int dtoken) {
   r= paa_pathargs(&rv,&newargs); if (r) return r;
   execute= tokv_word_execute;
   freecharparray(execargs); execargs= newargs;
-  free(execpath); execpath= xmstrsave(rv);
+  free(execpath); execpath= xstrsave(rv);
   return 0;
 }
 
@@ -797,8 +778,9 @@ int df_executefromdirectory(int dtoken) {
       return tokv_error;
     }
   }
-  l= strlen(rv)+sizeof(DIRSEP)+strlen(p);
-  fn= xmalloc(l); strcpy(fn,rv); strcat(fn,DIRSEP); strcat(fn,p);
+  l= strlen(rv)+1+strlen(p)+1;
+  fn= xmalloc(l);
+  snyprintf(fn,l,"%s/%s",rv,p);
   if (stat(fn,&stab)) {
     if (errno == ENOENT) { free(fn); freecharparray(newargs); return 0; }
     parseerrprint("failed to stat `%s' for execute-from-directory: %s",
@@ -820,7 +802,7 @@ int df_errorstostderr(int dtoken) {
   int r;
   
   r= pa_mnl(); if (r) return r;
-  closeerrorfile(); ehandling= dtoken; return 0;
+  closeerrorfile(); eh.handling= dtoken; return 0;
 }
 
 int df_errorstosyslog(int dtoken) {
@@ -845,8 +827,8 @@ int df_errorstosyslog(int dtoken) {
   }
   if (unexpected(token,tokv_newline,"end of line somewhere after errors-to-syslog"))
     return tokv_error;
-  closeerrorfile(); ehandling= tokv_word_errorstosyslog;
-  ehlogfacility= facility; ehloglevel= level; return 0;
+  closeerrorfile(); eh.handling= tokv_word_errorstosyslog;
+  eh.logfacility= facility; eh.loglevel= level; return 0;
 }
 
 int df_errorstofile(int dtoken) {
@@ -864,8 +846,8 @@ int df_errorstofile(int dtoken) {
     parseerrprint("unable to set line buffering on errors file: %s",strerror(errno));
     fclose(file); return tokv_error;
   }
-  closeerrorfile(); ehandling= tokv_word_errorstofile;
-  ehfile= file; ehfilename= xmstrsave(cp); ehfilekeep= 0; return 0;
+  closeerrorfile(); eh.handling= tokv_word_errorstofile;
+  eh.file= file; eh.filename= xstrsave(cp); return 0;
 }
 
 int dfg_setflag(int dtoken) {
@@ -879,7 +861,7 @@ int df_reset(int dtoken) {
   int r;
 
   r= pa_mnl(); if (r) return r;
-  r= parse_string(RESET_CONFIGURATION,"<builtin reset configuration>");
+  r= parse_string(RESET_CONFIGURATION,"<builtin reset configuration>",1);
   assert(!r); return 0;  
 }
 
@@ -898,7 +880,7 @@ int df_userrcfile(int dtoken) {
   int r;
 
   r= paa_1path(&cp); if (r) return r;
-  free(userrcfile); userrcfile= xstrdup(cp);
+  free(userrcfile); userrcfile= xstrsave(cp);
   return 0;
 }
 
@@ -920,7 +902,7 @@ int dfi_includeclientconfig(int dtoken) {
                  "found but configuration not overridden");
     return tokv_error;
   }
-  return parse_string(overridedata,"<configuration override data>");
+  return parse_string(overridedata,"<configuration override data>",0);
 }
 
 int df_include(int dtoken) {
@@ -1001,10 +983,8 @@ int df_includedirectory(int dtoken) {
     if (!isalnum(*p)) continue;
     while ((c= *++p)) if (!(isalnum(c) || c=='-')) break;
     if (c) continue;
-    makeroom(&buildbuf,&buildbuflen,cpl+tel+sizeof(DIRSEP));
-    strcpy(buildbuf,cp);
-    strcat(buildbuf,DIRSEP);
-    strcat(buildbuf,de->d_name);
+    makeroom(&buildbuf,&buildbuflen,cpl+1+tel+1);
+    snyprintf(buildbuf,buildbuflen,"%s/%s",cp,de->d_name);
     r= parse_file(buildbuf,&found); if (r) { closedir(d); return r; }
     if (!found) {
       parseerrprint("unable to open file `%s' in included directory `%s': %s",
@@ -1042,9 +1022,8 @@ int df_includelookup(int dtoken) {
   done= 0;
   cpl= strlen(cp);
   if (!parmvalues[0]) {
-    makeroom(&buildbuf,&buildbuflen,cpl+sizeof(DIRSEP NONEINCLUDELOOKUP));
-    strcpy(buildbuf,cp);
-    strcat(buildbuf,DIRSEP NONEINCLUDELOOKUP);
+    makeroom(&buildbuf,&buildbuflen,cpl+1+sizeof(NONEINCLUDELOOKUP));
+    snyprintf(buildbuf,buildbuflen,"%s/" NONEINCLUDELOOKUP,cp);
     r= parse_file(buildbuf,&thisdone);
     if (r) { freecharparray(parmvalues); return r; }
     if (thisdone) done= 1;
@@ -1053,10 +1032,10 @@ int df_includelookup(int dtoken) {
         *pp && (!done || dtoken == tokv_word_includelookupall);
         pp++) {
       makeroom(&buildbuf,&buildbuflen,
-              cpl+sizeof(DIRSEP)+strlen(*pp)*2+3+sizeof(EMPTYINCLUDELOOKUP));
+              cpl+1+strlen(*pp)*2+3+sizeof(EMPTYINCLUDELOOKUP)+1);
       strcpy(buildbuf,cp);
-      strcat(buildbuf,DIRSEP);
-      p= *pp; q= buildbuf+cpl+sizeof(DIRSEP)-1;
+      p= *pp; q= buildbuf+cpl;
+      *q++= '/';
       if (*p=='.') *q++= ':';
       while ((c= *p++)) {
        if (c=='/') { *q++= ':'; c='-'; }
@@ -1072,9 +1051,8 @@ int df_includelookup(int dtoken) {
   freecharparray(parmvalues);
   if (!done) {
     makeroom(&buildbuf,&buildbuflen,
-             cpl+sizeof(DIRSEP)+sizeof(DEFAULTINCLUDELOOKUP));
-    strcpy(buildbuf,cp);
-    strcat(buildbuf,DIRSEP DEFAULTINCLUDELOOKUP);
+             cpl+1+sizeof(DEFAULTINCLUDELOOKUP));
+    snyprintf(buildbuf,buildbuflen,"%s/" DEFAULTINCLUDELOOKUP,cp);
     r= parse_file(buildbuf,0); if (r) return r;
   }
   return 0;
@@ -1091,11 +1069,8 @@ int df_catchquit(int dtoken) {
   } else if (r == tokv_quit || r == tokv_error) {
     if (r == tokv_error) {
       r= parse_string(RESET_CONFIGURATION,
-                     "<builtin reset configuration (caught error)>");
+                     "<builtin reset configuration (caught error)>",1);
       assert(!r);
-      while (!atnewline) {
-       r= yylex(); if (r == tokv_error) return r;
-      }
     }
     r= skip(tokv_word_catchquit);
     if (r & tokt_controlend) {
@@ -1118,6 +1093,7 @@ int df_if(int dtoken) {
   } while (r == tokv_word_elif);
   if (r == tokv_word_else) {
     r= pa_mnl(); if (r) return r;
+    cstate->reportlineno= cstate->lineno;
     if (done) r= skip(tokv_word_if);
     else r= parser(tokv_word_if);
     if (!(r & tokv_word_if)) return r;
@@ -1182,29 +1158,18 @@ int df_eof(int dtoken) {
 }
 
 int df_errorspush(int dt) {
-  int saveehandling, saveehlogfacility, saveehloglevel, saveehfilekeep;
-  FILE *saveehfile;
-  char *saveehfilename;
+  struct error_handling save;
   int r;
 
   r= pa_mnl(); if (r) return r;
 
-  saveehandling= ehandling;
-  saveehlogfacility= ehlogfacility;
-  saveehloglevel= ehloglevel;
-  saveehfile= ehfile;
-  saveehfilekeep= ehfilekeep;
-  saveehfilename= ehfilename;
-  if (ehandling == tokv_word_errorstofile) ehfilekeep= 1;
+  save= eh;
+  eh.filekeep= 1;
 
   r= parser(tokv_word_errorspush);
-  
-  ehandling= saveehandling;
-  ehlogfacility= saveehlogfacility;
-  ehloglevel= saveehloglevel;
-  ehfile= saveehfile;
-  ehfilekeep= saveehfilekeep;
-  ehfilename= saveehfilename;
+
+  closeerrorfile();
+  eh= save;
 
   if (r & tokt_controlend) {
     assert(r == tokv_word_srorre);
similarity index 55%
rename from daemon.c
rename to process.c
index 97bffa5..8d20ce0 100644 (file)
--- a/daemon.c
+++ b/process.c
@@ -1,6 +1,6 @@
 /*
- * userv - daemon.c
- * daemon main program
+ * userv - process.c
+ * daemon code to process one request (is parent of service process)
  *
  * Copyright (C)1996-1997 Ian Jackson
  *
  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+/* We do some horrible asynchronous stuff with signals.
+ *
+ * The following objects &c. are used in signal handlers and so
+ * must be protected by calls to blocksignals if they are used in
+ * the main program:
+ *  the syslog() family of calls, and the associated
+ *    syslogopenfacility variable
+ *  swfile (stdio stream)
+ *
+ * The following objects are used in the main program unprotected
+ * and so must not be used in signal handlers:
+ *  srfile
+ *
+ * child and childtokill are used for communication between the
+ * main thread and the signal handlers; none of the signal handlers
+ * return so errno is OK too.
+ */
+
 #include <stdio.h>
 #include <stdarg.h>
 #include <unistd.h>
 #include <syslog.h>
 #include <pwd.h>
 #include <grp.h>
+#include <ctype.h>
+#include <limits.h>
 #include <sys/types.h>
 #include <sys/fcntl.h>
-#include <sys/socket.h>
 #include <sys/stat.h>
+#include <sys/socket.h>
 #include <sys/un.h>
-#include <limits.h>
-#include <ctype.h>
 
 #include "config.h"
 #include "common.h"
 
 /* NB: defaults for the execution state are not set here, but in
  * the RESET_CONFIGURATION #define in daemon.h. */
-gid_t *gidarray=0;
-char **argarray=0;
-char *((*defvararray)[2])=0;
-struct fdstate *fdarray=0;
-int fdarraysize=0, fdarrayused=0;
-int restfdwantstate= tokv_word_rejectfd, restfdwantrw= 0;
+char **argarray;
+char *((*defvararray)[2]);
+struct fdstate *fdarray;
+int fdarraysize, fdarrayused;
+int restfdwantstate= tokv_word_rejectfd, restfdwantrw;
 struct request_msg request_mbuf;
-char *serviceuser=0, *service=0, *logname=0, *cwd=0;
-char *overridedata=0, *userrcfile=0;
-char *serviceuser_dir=0, *serviceuser_shell=0, *callinguser_shell;
+char *serviceuser, *service, *logname, *cwd;
+char *overridedata, *userrcfile;
+char *serviceuser_dir, *serviceuser_shell, *callinguser_shell;
+int service_ngids;
+gid_t *calling_gids, *service_gids;
+const char **calling_groups, **service_groups;
 uid_t serviceuser_uid=-1;
-gid_t serviceuser_gid=-1;
-char *execpath=0, **execargs=0;
-int execute, setenvironment, suppressargs, disconnecthup, ehandling;
-int ehlogfacility=0, ehloglevel=0, ehfilekeep=0, syslogopenfacility=-1;
-FILE *ehfile=0;
-char *ehfilename=0;
-
-static FILE *swfile= 0, *srfile= 0;
-static pid_t child= -1, childtokill= -1;
-static const char **grouparray;
-
-static void sigchildhandler(int x) {
-  pid_t r;
-  int status, es;
-
-  es= errno;
+char *execpath, **execargs;
+int execute;
+int setenvironment, suppressargs, disconnecthup;
+int syslogopenfacility=-1;
+
+static FILE *swfile, *srfile;
+static pid_t child=-1, childtokill=-1;
+static pid_t mypid;
+
+/* Function shared with servexec.c: */
+
+int synchread(int fd, int ch) {
+  char synchmsg;
+  int r;
+  
   for (;;) {
-    r= waitpid((pid_t)-1,&status,WNOHANG);
-    if (!r || (r==-1 && errno==ECHILD)) break;
-    if (r==-1) { syslog(LOG_ERR,"wait in sigchild handler gave error: %m"); break; }
-    if (WIFSIGNALED(status))
-      if (WCOREDUMP(status))
-       syslog(LOG_ERR,"call pid %ld dumped core due to signal %s",(long)r,
-              strsignal(WTERMSIG(status)));
-      else
-       syslog(LOG_ERR,"call pid %ld died due to signal %s",
-              (long)r,strsignal(WTERMSIG(status)));
-    else if (!WIFEXITED(status))
-      syslog(LOG_ERR,"call pid %ld died due to unknown reason, code %ld",
-            (long)r,status);
-    else if (WEXITSTATUS(status)>24)
-      syslog(LOG_ERR,"call pid %ld exited with status %ld >24",
-            (long)r,WEXITSTATUS(status));
-  }
-  errno= es;
-  return;
+    r= read(fd,&synchmsg,1);
+    if (r==1) break;
+    if (r==0) { errno= ECONNRESET; return -1; }
+    assert(r<0);
+    if (errno!=EINTR) return -1;
+  };
+  assert(synchmsg==ch);
+  return 0;
+}
+
+/* General-purpose functions; these do nothing special about signals */
+
+static void blocksignals(void) {
+  int r;
+  sigset_t set;
+
+  sigemptyset(&set);
+  sigaddset(&set,SIGCHLD);
+  sigaddset(&set,SIGPIPE);
+  r= sigprocmask(SIG_BLOCK,&set,0); assert(!r);
 }
 
+static void xfwriteerror(void) {
+  if (errno != EPIPE) syscallerror("writing to client");
+  blocksignals();
+  ensurelogopen(USERVD_LOGFACILITY);
+  syslog(LOG_DEBUG,"client went away (broken pipe)");
+  disconnect(8);
+}
+
+static void xfwrite(const void *p, size_t sz, FILE *file) {
+  size_t nr;
+  nr= fwrite(p,1,sz,file);
+  if (nr != sz) xfwriteerror();
+}
+
+static void xfflush(FILE *file) {
+  if (fflush(file)) xfwriteerror();
+}
+
+/* Functions which may be called only from the main thread.  These may
+ * use main-thread objects and must block signals before using signal
+ * handler objects.
+ */
+
 static void xfread(void *p, size_t sz) {
   size_t nr;
   nr= fread(p,1,sz,srfile); if (nr == sz) return;
   if (ferror(srfile)) syscallerror("reading from client");
+  blocksignals();
   assert(feof(srfile));
   syslog(LOG_DEBUG,"client went away (unexpected EOF)");
   swfile= 0;
-  disconnect(12);
-}
-
-static void xfwrite(const void *p, size_t sz, FILE *file) {
-  size_t nr;
-  nr= fwrite(p,1,sz,file); if (nr == sz) return;
-  syscallerror("writing to client");
+  disconnect(8);
 }
 
 static char *xfreadsetstring(int l) {
   char *s;
+  assert(l<=MAX_GENERAL_STRING);
   s= xmalloc(l+1);
   xfread(s,sizeof(*s)*l);
   s[l]= 0;
@@ -126,50 +165,38 @@ static char *xfreadstring(void) {
   return xfreadsetstring(l);
 }
 
-static void xfflush(FILE *file) {
-  if (fflush(file)) syscallerror("flush client socket");
-}
-
-void ensurefdarray(int fd) {
-  if (fd < fdarrayused) return;
-  if (fd >= fdarraysize) {
-    fdarraysize= ((fd+2)<<1);
-    fdarray= xrealloc(fdarray,sizeof(struct fdstate)*fdarraysize);
-  }
-  while (fd >= fdarrayused) {
-    fdarray[fdarrayused].iswrite= -1;
-    fdarray[fdarrayused].realfd= -1;
-    fdarray[fdarrayused].wantstate= restfdwantstate;
-    fdarray[fdarrayused].wantrw= restfdwantrw;
-    fdarrayused++;
+static void getevent(struct event_msg *event_r) {
+  int fd;
+  
+  for (;;) {
+    xfread(event_r,sizeof(struct event_msg));
+    switch (event_r->type) {
+    case et_closereadfd:
+      fd= event_r->data.closereadfd.fd;
+      assert(fd<fdarrayused);
+      if (fdarray[fd].holdfd!=-1) {
+       if (close(fdarray[fd].holdfd)) syscallerror("cannot close holding fd");
+       fdarray[fd].holdfd= -1;
+      }
+      break;
+    case et_disconnect:
+      blocksignals();
+      syslog(LOG_DEBUG,"client disconnected");
+      disconnect(4);
+    default:
+      return;
+    }
   }
 }
 
-void ensurelogopen(int wantfacility) {
-  if (syslogopenfacility==wantfacility) return;
-  if (syslogopenfacility!=-1) closelog();
-  openlog(USERVD_LOGIDENT,LOG_NDELAY|LOG_PID,wantfacility);
-  syslogopenfacility= wantfacility;
-}
-
-void senderrmsgstderr(const char *errmsg) {
-  struct progress_msg progress_mbuf;
-  unsigned long ul;
-  int l;
-
-  l= strlen(errmsg);
-  memset(&progress_mbuf,0,sizeof(progress_mbuf));
-  progress_mbuf.magic= PROGRESS_MAGIC;
-  progress_mbuf.type= pt_errmsg;
-  progress_mbuf.data.errmsg.messagelen= l;
-  xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfile);
-  xfwrite(errmsg,l,swfile);
-  ul= PROGRESS_ERRMSG_END_MAGIC;
-  xfwrite(&ul,sizeof(ul),swfile);
-  xfflush(swfile);
-}
+/* Functions which may be called either from signal handlers or from
+ * the main thread.  They block signals in case they are on the main
+ * thread, and may only use signal handler objects.  None of them
+ * return.  If they did they'd have to restore the signal mask.
+ */
 
 void miscerror(const char *what) {
+  blocksignals();
   syslog(LOG_ERR,"failure: %s",what);
   disconnect(16);
 }
@@ -178,44 +205,22 @@ void syscallerror(const char *what) {
   int e;
 
   e= errno;
+  blocksignals();
   syslog(LOG_ERR,"system call failure: %s: %s",what,strerror(e));
   disconnect(18);
 }
 
-static void NONRETURNING generalfailure(const char *prefix, int reserveerrno,
-                                       int errnoval, const char *fmt, va_list al) {
-  char errmsg[MAX_ERRMSG_LEN];
-
-  if (prefix) {
-    strnycpy(errmsg,prefix,sizeof(errmsg));
-    strnytcat(errmsg,": ",sizeof(errmsg));
-  } else {
-    errmsg[0]= 0;
-  }
-  vsnytprintfcat(errmsg,sizeof(errmsg)-reserveerrno,fmt,al);
-  if (reserveerrno) {
-    strnytcat(errmsg,": ",sizeof(errmsg));
-    strnytcat(errmsg,strerror(errnoval),sizeof(errmsg));
-  }
-  senderrmsgstderr(errmsg);
-  syslog(LOG_DEBUG,"service failed (%s)",errmsg);
-  disconnect(12);
-}
-
-static void NONRETURNPRINTFFORMAT(1,2) failure(const char *fmt, ...) {
-  va_list al;
-
-  va_start(al,fmt);
-  generalfailure(0,0,0,fmt,al);
-}  
-
-static void NONRETURNPRINTFFORMAT(1,2) syscallfailure(const char *fmt, ...) {
-  va_list al;
-  int e;
+/* 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.
+ */
 
-  e= errno;
-  va_start(al,fmt);
-  generalfailure("system call failed",ERRMSG_RESERVE_ERRNO,e,fmt,al);
+void ensurelogopen(int wantfacility) {
+  if (syslogopenfacility==wantfacility) return;
+  if (syslogopenfacility!=-1) closelog();
+  openlog(USERVD_LOGIDENT,LOG_NDELAY|LOG_PID,wantfacility);
+  syslogopenfacility= wantfacility;
 }
 
 void NONRETURNING disconnect(int exitstatus) {
@@ -253,274 +258,158 @@ void NONRETURNING disconnect(int exitstatus) {
   _exit(exitstatus);
 }
 
-static void NONRETURNING syscallservfail(const char *msg) {
-  fputs("uservd(service): ",stderr);
-  perror(msg);
-  _exit(-1);
-}
-
-static void servresetsig(int signo) {
-  struct sigaction sig;
-  
-  sig.sa_handler= SIG_DFL;
-  sigemptyset(&sig.sa_mask);
-  sig.sa_flags= 0;
-  if (sigaction(signo,&sig,0)) syscallservfail("reset signal handler");
-}
-
-static int synchread(int fd, int ch) {
-  char synchmsg;
-  int r;
-  
-  for (;;) {
-    r= read(fd,&synchmsg,1);
-    if (r==1) break;
-    if (r==0) { errno= ECONNRESET; return -1; }
-    assert(r<0);
-    if (errno!=EINTR) return -1;
-  };
-  if (synchmsg != ch) { errno= EPROTO; return -1; }
-  return 0;
-}
-
-static const char *see_logname(void) { return serviceuser; }
-static const char *see_home(void) { return serviceuser_dir; }
-static const char *see_shell(void) { return serviceuser_shell; }
+static void NONRETURNING sighandler_chld(int ignored) {
+  struct progress_msg progress_mbuf;
+  int status;
+  pid_t returned;
 
-static const char *see_path(void) {
-  return serviceuser_uid ?
-    "/usr/local/bin:/bin:/usr/bin" :
-    "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin";
-}
+  returned= wait3(&status,WNOHANG,0);
+  if (returned==-1) syscallerror("wait for child failed");
+  if (!returned) syscallerror("spurious sigchld");
+  if (returned!=child) syscallerror("spurious child process");
+  child= childtokill= -1;
 
-static const char *see_service(void) { return service; }
-static const char *see_c_cwd(void) { return cwd; }
-static const char *see_c_logname(void) { return logname; }
-static const char *see_c_uid(void) {
-  static char buf[CHAR_BIT*sizeof(uid_t)/3+4];
-  snyprintf(buf,sizeof(buf),"%lu",(unsigned long)request_mbuf.callinguid);
-  return buf;
-}
+  memset(&progress_mbuf,0,sizeof(progress_mbuf));
+  progress_mbuf.magic= PROGRESS_MAGIC;
+  progress_mbuf.type= pt_terminated;
+  progress_mbuf.data.terminated.status= status;
+  xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfile);
+  xfflush(swfile);
 
-static const char *see_c_list(int n, const char *(*fn)(int i)) {
-  int l, i;
-  char *r;
-  
-  for (i=0, l=1; i<n; i++) l+= strlen(fn(i))+1;
-  r= xmalloc(l); r[l-1]= '*';
-  for (i=0, *r=0; i<n; i++) snytprintfcat(r,l,"%s ",fn(i));
-  assert(!r[l-1] && r[l-2]==' ');
-  r[l-2]= 0;
-  return r;
+  syslog(LOG_DEBUG,"service completed (status %d %d)",(status>>8)&0x0ff,status&0x0ff);
+  _exit(0);
 }
 
-static const char *seei_group(int i) {
-  return grouparray[i];
-}
-static const char *see_c_group(void) {
-  return see_c_list(request_mbuf.ngids,seei_group);
-}
+/* Functions which are called only during setup, before
+ * the signal asynchronicity starts.  They can do anything they like.
+ */
 
-static const char *seei_gid(int i) {
-  static char buf[CHAR_BIT*sizeof(gid_t)/3+4];
-  snyprintf(buf,sizeof(buf),"%d",gidarray[i]);
-  return buf;
-}
-static const char *see_c_gid(void) {
-  return see_c_list(request_mbuf.ngids,seei_gid);
+void ensurefdarray(int fd) {
+  if (fd < fdarrayused) return;
+  if (fd >= fdarraysize) {
+    fdarraysize= ((fd+2)<<1);
+    fdarray= xrealloc(fdarray,sizeof(struct fdstate)*fdarraysize);
+  }
+  while (fd >= fdarrayused) {
+    fdarray[fdarrayused].iswrite= -1;
+    fdarray[fdarrayused].realfd= -1;
+    fdarray[fdarrayused].holdfd= -1;
+    fdarray[fdarrayused].wantstate= restfdwantstate;
+    fdarray[fdarrayused].wantrw= restfdwantrw;
+    fdarrayused++;
+  }
 }
 
-static const struct servenvinfo {
-  const char *name;
-  const char *(*fn)(void);
-} servenvinfos[]= {
-  { "USER",           see_logname    },
-  { "LOGNAME",        see_logname    },
-  { "HOME",           see_home       },
-  { "SHELL",          see_shell      },
-  { "PATH",           see_path       },
-  { "USERV_SERVICE",  see_service    },
-  { "USERV_CWD",      see_c_cwd      },
-  { "USERV_USER",     see_c_logname  },
-  { "USERV_UID",      see_c_uid      },
-  { "USERV_GROUP",    see_c_group    },
-  { "USERV_GID",      see_c_gid      },
-  {  0                               }
-};
-
-static void NONRETURNING execservice(const int synchsocket[]) {
-  static const char *const setenvpfargs[]= {
-    "/bin/sh",
-    "-c",
-    ". " SYSTEMCONFIGDIR "/environment; exec \"$@\"",
-    "-",
-    0
-  };
-  int fd, realfd, holdfd, newfd, r, envvarbufsize=0, targ, nargs, i, l;
-  char *envvarbuf=0;
-  const char **args, *const *cpp;
-  char *const *pp;
-  char synchmsg;
-  const struct servenvinfo *sei;
+static void NONRETURNING generalfailure(const char *prefix, int reserveerrno,
+                                       int errnoval, const char *fmt, va_list al) {
+  char errmsg[MAX_ERRMSG_LEN];
 
-  if (dup2(fdarray[2].realfd,2)<0) {
-    static const char duperrmsg[]= "uservd(service): cannot dup2 for stderr\n";
-    write(fdarray[2].realfd,duperrmsg,sizeof(duperrmsg)-1);
-    _exit(-1);
+  if (prefix) {
+    strnycpy(errmsg,prefix,sizeof(errmsg));
+    strnytcat(errmsg,": ",sizeof(errmsg));
+  } else {
+    errmsg[0]= 0;
   }
-  if (close(synchsocket[0])) syscallservfail("close parent synch socket");
+  vsnytprintfcat(errmsg,sizeof(errmsg)-reserveerrno,fmt,al);
+  if (reserveerrno) {
+    strnytcat(errmsg,": ",sizeof(errmsg));
+    strnytcat(errmsg,strerror(errnoval),sizeof(errmsg));
+  }
+  senderrmsgstderr(errmsg);
+  syslog(LOG_DEBUG,"service failed (%s)",errmsg);
+  disconnect(12);
+}
 
-  if (setpgid(0,0)) syscallservfail("set process group");
-  synchmsg= 'y';
-  r= write(synchsocket[1],&synchmsg,1);
-  if (r!=1) syscallservfail("write synch byte to parent");
-  r= synchread(synchsocket[1],'g');
-  if (r) syscallservfail("reach synch byte from parent");
+static void NONRETURNPRINTFFORMAT(1,2) failure(const char *fmt, ...) {
+  va_list al;
 
-  if (close(fileno(swfile))) syscallservfail("close client socket fd");
-  for (fd=0; fd<fdarrayused; fd++) {
-    if (fdarray[fd].holdfd == -1) continue;
-    if (close(fdarray[fd].holdfd)) syscallservfail("close pipe hold fd");
-    fdarray[fd].holdfd= -1;
-  }
-  for (fd=0; fd<fdarrayused; fd++) {
-    if (fdarray[fd].realfd < fdarrayused) fdarray[fdarray[fd].realfd].holdfd= fd;
-  }
-  for (fd=0; fd<fdarrayused; fd++) {
-    realfd= fdarray[fd].realfd;
-    if (realfd == -1) continue;
-    holdfd= fdarray[fd].holdfd;
-    if (holdfd == fd) {
-      assert(realfd == fd);
-      fdarray[fd].holdfd= -1;
-      continue;
-    } else if (holdfd != -1) {
-      assert(fdarray[holdfd].realfd == fd);
-      newfd= dup(fd); if (newfd<0) syscallservfail("dup out of the way");
-      fdarray[holdfd].realfd= newfd;
-      if (newfd<fdarrayused) fdarray[newfd].holdfd= holdfd;
-      fdarray[fd].holdfd= -1;
-    }
-    if (dup2(fdarray[fd].realfd,fd)<0) syscallservfail("dup2 set up fd");
-    if (close(fdarray[fd].realfd)) syscallservfail("close old fd");
-    if (fcntl(fd,F_SETFD,0)<0) syscallservfail("set no-close-on-exec on fd");
-    fdarray[fd].realfd= fd;
-  }
-  servresetsig(SIGPIPE);
-  servresetsig(SIGCHLD);
-
-  for (sei= servenvinfos; sei->name; sei++)
-    if (setenv(sei->name,sei->fn(),1)) syscallservfail("setenv standard");
-  for (i=0; i<request_mbuf.nvars; i++) {
-    l= strlen(defvararray[i][0])+9;
-    if (l>envvarbufsize) { envvarbufsize= l; envvarbuf= xrealloc(envvarbuf,l); }
-    snyprintf(envvarbuf,l,"USERV_U_%s",defvararray[i][0]);
-    if (setenv(envvarbuf,defvararray[i][1],1)) syscallservfail("setenv defvar");
-  }
+  va_start(al,fmt);
+  generalfailure(0,0,0,fmt,al);
+}  
 
-  nargs= 0;
-  if (setenvironment) for (cpp= setenvpfargs; *cpp; cpp++) nargs++;
-  nargs++;
-  if (execargs) for (pp= execargs; *pp; pp++) nargs++;
-  if (!suppressargs) nargs+= request_mbuf.nargs;
-  args= xmalloc(sizeof(char*)*(nargs+1));
-  targ= 0;
-  if (setenvironment) for (cpp= setenvpfargs; *cpp; cpp++) args[targ++]= *cpp;
-  args[targ++]= execpath;
-  if (execargs) for (pp= execargs; *pp; pp++) args[targ++]= *pp;
-  if (!suppressargs) for (i=0; i<request_mbuf.nargs; i++) args[targ++]= argarray[i];
-  args[targ++]= 0;
-
-  execv(args[0],(char* const*)args);
-
-  syscallservfail("exec service program");
-  _exit(-1);
-}
+static void NONRETURNPRINTFFORMAT(1,2) syscallfailure(const char *fmt, ...) {
+  va_list al;
+  int e;
 
-static void NONRETURNING sighandler_pipe(int ignored) {
-  swfile= 0;
-  ensurelogopen(USERVD_LOGFACILITY);
-  syslog(LOG_DEBUG,"client went away (server got sigpipe)");
-  disconnect(8);
+  e= errno;
+  va_start(al,fmt);
+  generalfailure("system call failed",ERRMSG_RESERVE_ERRNO,e,fmt,al);
 }
 
-static void NONRETURNING sighandler_chld(int ignored) {
+void senderrmsgstderr(const char *errmsg) {
   struct progress_msg progress_mbuf;
-  int status;
-  pid_t returned;
-
-  returned= wait3(&status,WNOHANG,0);
-  if (returned==-1) syscallerror("wait for child failed");
-  if (!returned) syscallfailure("spurious sigchld");
-  if (returned!=child) syscallfailure("spurious child process (pid %ld)",(long)returned);
-  child= childtokill= -1;
+  unsigned long ul;
+  int l;
 
+  l= strlen(errmsg);
   memset(&progress_mbuf,0,sizeof(progress_mbuf));
   progress_mbuf.magic= PROGRESS_MAGIC;
-  progress_mbuf.type= pt_terminated;
-  progress_mbuf.data.terminated.status= status;
+  progress_mbuf.type= pt_errmsg;
+  progress_mbuf.data.errmsg.messagelen= l;
   xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfile);
+  xfwrite(errmsg,l,swfile);
+  ul= PROGRESS_ERRMSG_END_MAGIC;
+  xfwrite(&ul,sizeof(ul),swfile);
   xfflush(swfile);
-
-  syslog(LOG_DEBUG,"service completed (status %d %d)",(status>>8)&0x0ff,status&0x0ff);
-  _exit(0);
 }
 
-static void getevent(struct event_msg *event_r) {
-  int fd;
+static void getgroupnames(int ngids, gid_t *list, const char ***names_r) {
+  const char **names;
+  struct group *cgrp;
+  int i;
   
-  for (;;) {
-    xfread(event_r,sizeof(struct event_msg));
-    switch (event_r->type) {
-    case et_closereadfd:
-      fd= event_r->data.closereadfd.fd;
-      assert(fd<fdarrayused);
-      assert(fdarray[fd].holdfd!=-1);
-      if (close(fdarray[fd].holdfd)) syscallfailure("cannot close holding fd %d",fd);
-      break;
-    case et_disconnect:
-      syslog(LOG_DEBUG,"client disconnected");
-      disconnect(4);
-    default:
-      return;
-    }
+  names= xmalloc(sizeof(char*)*ngids);
+  for (i=0; i<ngids; i++) {
+    cgrp= getgrgid(list[i]);
+    if (!cgrp) miscerror("get group entry");
+    names[i]= xstrsave(cgrp->gr_name);
   }
+  *names_r= names;
 }
+  
+/* The per-request main program and its subfunctions. */
 
-static void NONRETURNING servicerequest(int sfd) {
-  struct opening_msg opening_mbuf;
-  struct progress_msg progress_mbuf;
-  struct event_msg event_mbuf;
-  pid_t mypid, newchild;
-  unsigned long ul;
-  int i,j, r, tempfd, fd, partsize, synchsocket[2];
-  char pipepathbuf[PIPEPATHMAXLEN];
-  const char *string, *delim, *nextstring;
-  char *part, *exectry;
-  char synchmsg;
-  struct stat stab;
-  struct sigaction sig;
-  struct group *cgrp;
-  struct passwd *pw;
+static void setup_comms(int sfd) {
+  static char swbuf[BUFSIZ];
+  static char srbuf[BUFSIZ];
 
+  struct sigaction sig;
+  
   ensurelogopen(USERVD_LOGFACILITY);
   syslog(LOG_DEBUG,"call connected");
 
   mypid= getpid(); if (mypid == -1) syscallerror("getpid");
 
+  sig.sa_handler= SIG_IGN;
+  sigemptyset(&sig.sa_mask);
+  sig.sa_flags= 0;
+  if (sigaction(SIGPIPE,&sig,0)) syscallerror("cannot ignore sigpipe");
+
   srfile= fdopen(sfd,"r");
   if (!srfile) syscallerror("turn socket fd into reading FILE*");
-  if (setvbuf(srfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket reads");
+  if (setvbuf(srfile,srbuf,_IOFBF,sizeof(srbuf)))
+    syscallerror("set buffering on socket reads");
 
   swfile= fdopen(sfd,"w");
   if (!swfile) syscallerror("turn socket fd into writing FILE*");
-  if (setvbuf(swfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket writes");
+  if (setvbuf(swfile,swbuf,_IOFBF,sizeof(swbuf)))
+    syscallerror("set buffering on socket writes");
+}
 
+static void send_opening(void) {
+  struct opening_msg opening_mbuf;
+
+  memset(&opening_mbuf,0,sizeof(opening_mbuf));
   opening_mbuf.magic= OPENING_MAGIC;
   memcpy(opening_mbuf.protocolchecksumversion,protocolchecksumversion,PCSUMSIZE);
   opening_mbuf.serverpid= mypid;
   xfwrite(&opening_mbuf,sizeof(opening_mbuf),swfile);
   xfflush(swfile);
+}
+
+static void receive_request(void) {
+  int i,j, fd;
+  unsigned long ul;
 
   xfread(&request_mbuf,sizeof(request_mbuf));
   serviceuser= xfreadsetstring(request_mbuf.serviceuserlen);
@@ -531,33 +420,44 @@ static void NONRETURNING servicerequest(int sfd) {
     assert(request_mbuf.overridelen <= MAX_OVERRIDE_LEN);
     overridedata= xfreadsetstring(request_mbuf.overridelen);
   } else {
+    assert(request_mbuf.overridelen == -1);
     overridedata= 0;
   }
-  gidarray= xmalloc(sizeof(gid_t)*request_mbuf.ngids);
-  xfread(gidarray,sizeof(gid_t)*request_mbuf.ngids);
+  assert(request_mbuf.ngids <= MAX_GIDS);
+  calling_gids= xmalloc(sizeof(gid_t)*request_mbuf.ngids);
+  xfread(calling_gids,sizeof(gid_t)*request_mbuf.ngids);
 
   fdarraysize= 4; fdarray= xmalloc(sizeof(struct fdstate)*fdarraysize);
   fdarrayused= 1; fdarray[0].iswrite= -1;
   fdarray[0].wantstate= tokv_word_rejectfd;
+  assert(request_mbuf.nreadfds+request_mbuf.nwritefds <= MAX_ALLOW_FD+1);
   for (i=0; i<request_mbuf.nreadfds+request_mbuf.nwritefds; i++) {
     xfread(&fd,sizeof(int));
+    assert(fd <= MAX_ALLOW_FD);
     ensurefdarray(fd);
     assert(fdarray[fd].iswrite == -1);
     fdarray[fd].iswrite= (i>=request_mbuf.nreadfds);
   }
 
+  assert(request_mbuf.nargs <= MAX_ARGSDEFVARS);
   argarray= xmalloc(sizeof(char*)*(request_mbuf.nargs));
   for (i=0; i<request_mbuf.nargs; i++) argarray[i]= xfreadstring();
+  assert(request_mbuf.nvars <= MAX_ARGSDEFVARS);
   defvararray= xmalloc(sizeof(char*)*request_mbuf.nvars*2);
   for (i=0; i<request_mbuf.nvars; i++)
     for (j=0; j<2; j++) defvararray[i][j]= xfreadstring();
   xfread(&ul,sizeof(ul));
   assert(ul == REQUEST_END_MAGIC);
+}
 
+static void establish_pipes(void) {
+  int fd, tempfd;
+  char pipepathbuf[PIPEMAXLEN];
+  
   for (fd=0; fd<fdarrayused; fd++) {
     if (fdarray[fd].iswrite == -1) continue;
-    sprintf(pipepathbuf, PIPEPATHFORMAT, (unsigned long)request_mbuf.clientpid,
-            (unsigned long)mypid, fd);
+    snyprintf(pipepathbuf,sizeof(pipepathbuf), PIPEFORMAT,
+             (unsigned long)request_mbuf.clientpid,(unsigned long)mypid,fd);
     tempfd= open(pipepathbuf,O_RDWR);
     if (tempfd == -1) syscallerror("prelim open pipe");
     if (!fdarray[fd].iswrite) {
@@ -572,14 +472,22 @@ static void NONRETURNING servicerequest(int sfd) {
     if (unlink(pipepathbuf)) syscallerror("unlink pipe");
     if (close(tempfd)) syscallerror("close prelim fd onto pipe");
   }
+}
+
+static void lookup_uidsgids(void) {
+  struct passwd *pw;
+
+  pw= getpwnam(logname);
+  if (!pw) miscerror("look up calling user");
+  assert(!strcmp(pw->pw_name,logname));
+  callinguser_shell= xstrsave(pw->pw_shell);
 
   pw= getpwnam(serviceuser);
   if (!pw) miscerror("look up service user");
   assert(!strcmp(pw->pw_name,serviceuser));
-  serviceuser_dir= xstrdup(nondebug_serviceuserdir(pw->pw_dir));
-  serviceuser_shell= xstrdup(pw->pw_shell);
+  serviceuser_dir= xstrsave(nondebug_serviceuserdir(pw->pw_dir));
+  serviceuser_shell= xstrsave(pw->pw_shell);
   serviceuser_uid= pw->pw_uid;
-  serviceuser_gid= pw->pw_gid;
   if (initgroups(pw->pw_name,pw->pw_gid)) syscallerror("initgroups");
   if (setreuid(pw->pw_uid,pw->pw_uid)) syscallerror("setreuid 1");
   if (setreuid(pw->pw_uid,pw->pw_uid)) syscallerror("setreuid 2");
@@ -587,30 +495,23 @@ static void NONRETURNING servicerequest(int sfd) {
     if (!setreuid(pw->pw_uid,0)) miscerror("setreuid 3 unexpectedly succeeded");
   if (errno != EPERM) syscallerror("setreuid 3 failed in unexpected way");
 
-  debug_dumprequest(mypid);
-
-  grouparray= xmalloc(sizeof(char*)*request_mbuf.ngids);
-  for (i=0; i<request_mbuf.ngids; i++) {
-    cgrp= getgrgid(gidarray[i]);
-    if (!cgrp) miscerror("get group entry for calling group");
-    grouparray[i]= xmstrsave(cgrp->gr_name);
-  }
+  service_ngids= getgroups(0,0); if (service_ngids == -1) syscallerror("getgroups(0,0)");
+  if (service_ngids > MAX_GIDS) miscerror("service user is in far too many groups");
+  service_gids= xmalloc(sizeof(gid_t)*(service_ngids+1));
+  service_gids[0]= pw->pw_gid;
+  if (getgroups(service_ngids,service_gids+1) != service_ngids)
+    syscallerror("getgroups(size,list)");
 
-  if (overridedata) {
-    r= parse_string(TOPLEVEL_OVERRIDDEN_CONFIGURATION,
-                   "<builtin toplevel override configuration>");
-  } else {
-    r= parse_string(TOPLEVEL_CONFIGURATION,
-                   "<builtin toplevel configuration>");
-  }
-
-  ensurelogopen(USERVD_LOGFACILITY);
+  getgroupnames(service_ngids,service_gids,&service_groups);
+  getgroupnames(request_mbuf.ngids,calling_gids,&calling_groups);
+}
 
-  if (r == tokv_error) failure("error encountered while parsing configuration files");
-  assert(r == tokv_quit);
+static void check_find_executable(void) {
+  int r, partsize;
+  char *part, *exectry;
+  const char *string, *delim, *nextstring;
+  struct stat stab;
 
-  debug_dumpexecsettings();
-  
   switch (execute) {
   case tokv_word_reject:
     failure("request rejected");
@@ -642,8 +543,8 @@ static void NONRETURNING servicerequest(int sfd) {
          partsize= strlen(string);
          nextstring= 0;
        }
-       part= xmstrsubsave(string,partsize);
-       exectry= part[0] ? xmstrcat3save(part,"/",service) : xmstrsave(service);
+       part= xstrsubsave(string,partsize);
+       exectry= part[0] ? xstrcat3save(part,"/",service) : xstrsave(service);
        free(part);
        r= stat(exectry,&stab);
        if (!r) { execpath= exectry; break; }
@@ -656,7 +557,11 @@ static void NONRETURNING servicerequest(int sfd) {
   default:
     abort();
   }
+}
 
+static void check_fds(void) {
+  int fd;
+  
   assert(fdarrayused>=2);
   if (!(fdarray[2].wantstate == tokv_word_requirefd ||
        fdarray[2].wantstate == tokv_word_allowfd) ||
@@ -705,38 +610,42 @@ static void NONRETURNING servicerequest(int sfd) {
       }
     }
   }
+}
+
+static void send_progress_ok(void) {
+  struct progress_msg progress_mbuf;
 
   memset(&progress_mbuf,0,sizeof(progress_mbuf));
   progress_mbuf.magic= PROGRESS_MAGIC;
   progress_mbuf.type= pt_ok;
   xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfile);
   xfflush(swfile);
+}
+
+static void fork_service_synch(void) {
+  pid_t newchild;
+  struct sigaction sig;
+  int r, synchsocket[2];
+  char synchmsg;
 
   r= socketpair(AF_UNIX,SOCK_STREAM,0,synchsocket);
-  if (r) syscallfailure("cannot create socket for synch");
+  if (r) syscallerror("cannot create socket for synch");
 
-  sig.sa_handler= sighandler_pipe;
+  sig.sa_handler= sighandler_chld;
   sigemptyset(&sig.sa_mask);
-  sigaddset(&sig.sa_mask,SIGPIPE);
   sigaddset(&sig.sa_mask,SIGCHLD);
   sig.sa_flags= 0;
-  if (sigaction(SIGPIPE,&sig,0)) syscallfailure("cannot set sigpipe handler");
-
-  sig.sa_handler= sighandler_chld;
-  if (sigaction(SIGCHLD,&sig,0)) syscallfailure("cannot set sigchld handler");
-
-  getevent(&event_mbuf);
-  assert(event_mbuf.type == et_confirm);
+  if (sigaction(SIGCHLD,&sig,0)) syscallerror("cannot set sigchld handler");
 
   newchild= fork();
-  if (newchild == -1) syscallfailure("cannot fork to invoke service");
-  if (!newchild) execservice(synchsocket);
+  if (newchild == -1) syscallerror("cannot fork to invoke service");
+  if (!newchild) execservice(synchsocket,fileno(swfile));
   childtokill= child= newchild;
 
-  if (close(synchsocket[1])) syscallfailure("cannot close other end of synch socket");
+  if (close(synchsocket[1])) syscallerror("cannot close other end of synch socket");
 
   r= synchread(synchsocket[0],'y');
-  if (r) syscallfailure("read synch byte from child");
+  if (r) syscallerror("read synch byte from child");
 
   childtokill= -child;
 
@@ -744,59 +653,42 @@ static void NONRETURNING servicerequest(int sfd) {
   r= write(synchsocket[0],&synchmsg,1);
   if (r!=1) syscallerror("write synch byte to child");
 
-  if (close(synchsocket[0])) syscallfailure("cannot close my end of synch socket");
+  if (close(synchsocket[0])) syscallerror("cannot close my end of synch socket");
+}
+
+void servicerequest(int sfd) {
+  struct event_msg event_mbuf;
+  int r;
+
+  setup_comms(sfd);
+  send_opening();
+  receive_request();
+  establish_pipes();
+  lookup_uidsgids();
+  debug_dumprequest(mypid);
+
+  if (overridedata)
+    r= parse_string(TOPLEVEL_OVERRIDDEN_CONFIGURATION,
+                   "<builtin toplevel override configuration>",1);
+  else
+    r= parse_string(TOPLEVEL_CONFIGURATION,
+                   "<builtin toplevel configuration>",1);
+  
+  ensurelogopen(USERVD_LOGFACILITY);
+  if (r == tokv_error) failure("error encountered while parsing configuration files");
+  assert(r == tokv_quit);
+
+  debug_dumpexecsettings();
+
+  check_find_executable();
+  check_fds();
+  send_progress_ok();
 
   getevent(&event_mbuf);
-  abort();
-}
+  assert(event_mbuf.type == et_confirm);
 
-int main(int argc, char *const *argv) {
-  int mfd, sfd, csocklen;
-  struct sigaction childact;
-  struct sockaddr_un ssockname, csockname;
-
-#ifdef NDEBUG
-  abort(); /* Do not disable assertions in this security-critical code ! */
-#endif
-
-  if (argc>1) { fputs("usage: uservd\n",stderr); exit(3); }
-
-  openlog(USERVD_LOGIDENT,LOG_NDELAY,USERVD_LOGFACILITY);
-  mfd= socket(AF_UNIX,SOCK_STREAM,0);
-  if (!mfd) { syslog(LOG_CRIT,"cannot create master socket: %m"); exit(4); }
-
-  assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUSPATH));
-  ssockname.sun_family= AF_UNIX;
-  strcpy(ssockname.sun_path,RENDEZVOUSPATH);
-  unlink(RENDEZVOUSPATH);
-  if (bind(mfd,(struct sockaddr*)&ssockname,sizeof(ssockname)))
-    { syslog(LOG_CRIT,"cannot bind master socket: %m"); exit(4); }
-  if (listen(mfd,5))
-    { syslog(LOG_CRIT,"cannot listen on master socket: %m"); exit(4); }
-
-  childact.sa_handler= sigchildhandler;
-  sigemptyset(&childact.sa_mask);
-  childact.sa_flags= SA_NOCLDSTOP;
-  if (sigaction(SIGCHLD,&childact,0))
-    { syslog(LOG_CRIT,"cannot setup sigchld handler: %m"); exit(4); }
-  syslog(LOG_NOTICE,"started");
-  for (;;) {
-    csocklen= sizeof(csockname);
-    sfd= accept(mfd,(struct sockaddr*)&csockname,&csocklen);
-    if (sfd == -1) {
-      if (errno == EINTR) continue;
-      if (errno == ENOMEM) {
-        syslog(LOG_ERR,"unable to accept connection: %m"); continue;
-      }
-      syslog(LOG_CRIT,"unable to accept new connections: %m"); exit(5);
-    }
-    child= nondebug_fork();
-    if (child == (pid_t)-1) {
-      syslog(LOG_ERR,"unable to fork server: %m"); close(sfd); continue;
-    }
-    if (!child) {
-      close(mfd); closelog(); servicerequest(sfd);
-    }
-    close(sfd);
-  }
+  fork_service_synch();
+  
+  getevent(&event_mbuf);
+  abort();
 }
diff --git a/servexec.c b/servexec.c
new file mode 100644 (file)
index 0000000..48a6e29
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * userv - execserv.c
+ * daemon code which executes actual service (ie child process)
+ *
+ * Copyright (C)1996-1997 Ian Jackson
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with userv; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include "config.h"
+#include "common.h"
+#include "daemon.h"
+#include "lib.h"
+
+static void NONRETURNING serv_syscallfail(const char *msg) {
+  fputs("uservd(service): ",stderr);
+  perror(msg);
+  _exit(-1);
+}
+
+static void serv_resetsignal(int signo) {
+  struct sigaction sig;
+  
+  sig.sa_handler= SIG_DFL;
+  sigemptyset(&sig.sa_mask);
+  sig.sa_flags= 0;
+  if (sigaction(signo,&sig,0)) serv_syscallfail("reset signal handler");
+}
+
+static const char *see_logname(void) { return serviceuser; }
+static const char *see_home(void) { return serviceuser_dir; }
+static const char *see_shell(void) { return serviceuser_shell; }
+
+static const char *see_path(void) {
+  return serviceuser_uid ? DEFAULTPATH_USER : DEFAULTPATH_ROOT;
+}
+
+static const char *see_service(void) { return service; }
+static const char *see_c_cwd(void) { return cwd; }
+static const char *see_c_logname(void) { return logname; }
+static const char *see_c_uid(void) {
+  static char buf[CHAR_BIT*sizeof(uid_t)/3+4];
+  snyprintf(buf,sizeof(buf),"%lu",(unsigned long)request_mbuf.callinguid);
+  return buf;
+}
+
+static const char *see_c_list(int n, const char *(*fn)(int i)) {
+  int l, i;
+  char *r;
+  
+  for (i=0, l=1; i<n; i++) l+= strlen(fn(i))+1;
+  r= xmalloc(l); r[l-1]= '*';
+  for (i=0, *r=0; i<n; i++) snytprintfcat(r,l,"%s ",fn(i));
+  assert(!r[l-1] && r[l-2]==' ');
+  r[l-2]= 0;
+  return r;
+}
+
+static const char *seei_group(int i) {
+  return calling_groups[i];
+}
+static const char *see_c_group(void) {
+  return see_c_list(request_mbuf.ngids,seei_group);
+}
+
+static const char *seei_gid(int i) {
+  static char buf[CHAR_BIT*sizeof(gid_t)/3+4];
+  
+  snyprintf(buf,sizeof(buf),"%d",calling_gids[i]);
+  return buf;
+}
+static const char *see_c_gid(void) {
+  return see_c_list(request_mbuf.ngids,seei_gid);
+}
+
+static const struct serv_envinfo {
+  const char *name;
+  const char *(*fn)(void);
+} serv_envinfos[]= {
+  { "USER",           see_logname    },
+  { "LOGNAME",        see_logname    },
+  { "HOME",           see_home       },
+  { "SHELL",          see_shell      },
+  { "PATH",           see_path       },
+  { "USERV_SERVICE",  see_service    },
+  { "USERV_CWD",      see_c_cwd      },
+  { "USERV_USER",     see_c_logname  },
+  { "USERV_UID",      see_c_uid      },
+  { "USERV_GROUP",    see_c_group    },
+  { "USERV_GID",      see_c_gid      },
+  {  0                               }
+};
+
+void execservice(const int synchsocket[], int clientfd) {
+  static const char *const setenvpfargs[]= {
+    "/bin/sh",
+    "-c",
+    ". " SETENVIRONMENTPATH "; exec \"$@\"",
+    "-",
+    0
+  };
+  int fd, realfd, holdfd, newfd, r, envvarbufsize=0, targ, nargs, i, l;
+  char *envvarbuf=0;
+  const char **args, *const *cpp;
+  char *const *pp;
+  char synchmsg;
+  const struct serv_envinfo *sei;
+
+  if (dup2(fdarray[2].realfd,2)<0) {
+    static const char duperrmsg[]= "uservd(service): cannot dup2 for stderr\n";
+    write(fdarray[2].realfd,duperrmsg,sizeof(duperrmsg)-1);
+    _exit(-1);
+  }
+  serv_resetsignal(SIGPIPE);
+  serv_resetsignal(SIGCHLD);
+
+  if (close(synchsocket[0])) serv_syscallfail("close parent synch socket");
+
+  if (setpgid(0,0)) serv_syscallfail("set process group");
+  synchmsg= 'y';
+  r= write(synchsocket[1],&synchmsg,1);
+  if (r!=1) serv_syscallfail("write synch byte to parent");
+  r= synchread(synchsocket[1],'g');
+  if (r) serv_syscallfail("reach synch byte from parent");
+
+  if (close(clientfd)) serv_syscallfail("close client socket fd");
+
+  /* Now we have to make all the fd's work.  It's rather a complicated
+   * algorithm, unfortunately.  We remember in holdfd[fd] whether fd
+   * is being used to hold a file descriptor we actually want for some
+   * other real fd in the service program; holdfd[fd] contains the fd
+   * we eventually want fd to be dup'd into, so that realfd[holdfd[fd]]==fd.
+   * After setting up the holdfds we go through the fds in order of
+   * eventual fd making sure that fd is the one we want it to be.  If the
+   * holdfd tells us we're currently storing some other fd in there we
+   * move it out of the way with dup and record its new location.
+   */
+  for (fd=0; fd<fdarrayused; fd++) {
+    if (fdarray[fd].holdfd == -1) continue;
+    if (close(fdarray[fd].holdfd)) serv_syscallfail("close pipe hold fd");
+    fdarray[fd].holdfd= -1;
+  }
+  for (fd=0; fd<fdarrayused; fd++) {
+    if (fdarray[fd].realfd < fdarrayused) fdarray[fdarray[fd].realfd].holdfd= fd;
+  }
+  for (fd=0; fd<fdarrayused; fd++) {
+    realfd= fdarray[fd].realfd;
+    if (realfd == -1) continue;
+    holdfd= fdarray[fd].holdfd;
+    if (holdfd == fd) {
+      assert(realfd == fd);
+      fdarray[fd].holdfd= -1;
+      continue;
+    } else if (holdfd != -1) {
+      assert(fdarray[holdfd].realfd == fd);
+      newfd= dup(fd); if (newfd<0) serv_syscallfail("dup out of the way");
+      fdarray[holdfd].realfd= newfd;
+      if (newfd<fdarrayused) fdarray[newfd].holdfd= holdfd;
+      fdarray[fd].holdfd= -1;
+    }
+    if (dup2(fdarray[fd].realfd,fd)<0) serv_syscallfail("dup2 set up fd");
+    if (close(fdarray[fd].realfd)) serv_syscallfail("close old fd");
+    if (fcntl(fd,F_SETFD,0)<0) serv_syscallfail("set no-close-on-exec on fd");
+    fdarray[fd].realfd= fd;
+  }
+
+  for (sei= serv_envinfos; sei->name; sei++)
+    if (setenv(sei->name,sei->fn(),1)) serv_syscallfail("setenv standard");
+  for (i=0; i<request_mbuf.nvars; i++) {
+    l= strlen(defvararray[i][0])+9;
+    if (l>envvarbufsize) { envvarbufsize= l; envvarbuf= xrealloc(envvarbuf,l); }
+    snyprintf(envvarbuf,l,"USERV_U_%s",defvararray[i][0]);
+    if (setenv(envvarbuf,defvararray[i][1],1)) serv_syscallfail("setenv defvar");
+  }
+
+  nargs= 0;
+  if (setenvironment) for (cpp= setenvpfargs; *cpp; cpp++) nargs++;
+  nargs++;
+  if (execargs) for (pp= execargs; *pp; pp++) nargs++;
+  if (!suppressargs) nargs+= request_mbuf.nargs;
+  args= xmalloc(sizeof(char*)*(nargs+1));
+  targ= 0;
+  if (setenvironment) for (cpp= setenvpfargs; *cpp; cpp++) args[targ++]= *cpp;
+  args[targ++]= execpath;
+  if (execargs) for (pp= execargs; *pp; pp++) args[targ++]= *pp;
+  if (!suppressargs) for (i=0; i<request_mbuf.nargs; i++) args[targ++]= argarray[i];
+  args[targ]= 0;
+
+  execv(args[0],(char* const*)args);
+
+  serv_syscallfail("exec service program");
+  _exit(-1);
+}
index 9c3f1e8..dba0322 100644 (file)
--- a/spec.sgml
+++ b/spec.sgml
@@ -811,9 +811,9 @@ be reset (as if by the <tt/reset/ directive) and parsing will likewise
 continue at <tt/hctac/.
 <p>
 
-If a serious lexical or syntax error is detected in the same
-configuration file as the <tt/catch-quit/, while looking for the
-<tt/hctac/, that error will not be caught.
+If a lexical or syntax error is detected in the same configuration
+file as the <tt/catch-quit/, while looking for the <tt/hctac/, that
+error will not be caught.
 
 </taglist>
 
index 21a3dbe..4755d9d 100644 (file)
@@ -24,65 +24,18 @@ include(language.i4)
 #define TOKENS_H
 
 enum tokens {
-  tokm_instance=           000000000777,
-  tokm_repres=             000000007000,
-  tokm_type=               017777770000,
-  tokr_nonstring=          000000001000,
-  tokr_word=               000000002000,
-  tokr_punct=              000000003000,
-  tokr_string=             000000004000,
+  tokm_instance=           0x000000ff,
+  tokm_repres=             0x00000f00,
+  tokm_type=               0xfffff000,
+  tokr_nonstring=          0x00000100,
+  tokr_word=               0x00000200,
+  tokr_punct=              0x00000300,
+  tokr_string=             0x00000400,
 undivert(4)
 undivert(1)
 undivert(2)
 };
 
-typedef int directive_fnt(int dtoken);
-directive_fnt df_reject, df_execute, df_executefrompath;
-directive_fnt df_executefromdirectory;
-directive_fnt df_errorstostderr, df_errorstosyslog, df_errorstofile;
-directive_fnt dfg_fdwant, dfg_setflag;
-directive_fnt df_reset, df_cd, df_userrcfile, df_include;
-directive_fnt df_includelookup, df_includedirectory;
-directive_fnt df_message, df_error, df_quit, df_eof;
-directive_fnt df_if, df_catchquit, df_errorspush;
-directive_fnt dfi_includeuserrcfile, dfi_includeclientconfig;
-/* directive functions return:
- *  0 for success
- *   having scanned up to and including end of line but not beyond
- *  an exception (eg tokv_error) for failure of some kind
- */
-
-typedef int parmcondition_fnt(int ctoken, char **parmvalues, int *rtrue);
-parmcondition_fnt pcf_glob, pcf_range, pcf_grep;
-/* all conditional functions return tokv_error or 0 for success at parsing
- *  and testing, in which case *rtrue is set to 0 or 1.  On success they
- *  have scanned up to and including the condition's terminating newline.
- * The parameter-based conditionals take a list of parameter values
- * as obtained from the parameter functions and pa_parameter,
- * and do _not_ free it.
- */
-
-typedef int parameter_fnt(int ptoken, char ***rvalues);
-parameter_fnt pf_service;
-parameter_fnt pf_callinguser, pf_serviceuser;
-parameter_fnt pf_callinggroup, pf_servicegroup;
-parameter_fnt pf_callingusershell, pf_serviceusershell;
-/* Parameter functions return tokv_error or 0 for success at parsing
- * and determining the value, in which case *rvalues is made to be
- * a mallocd null-terminated array of pointers to mallocd strings.
- * freeparm can be used to free such an array.
- */
-
-int yylex(void);
-/* Returns a token (which may be a eof or error exception) */
-
-extern directive_fnt *lr_dir;
-extern parmcondition_fnt *lr_parmcond;
-extern parameter_fnt *lr_parameter;
-extern int lr_loglevel, lr_logfacility, lr_min, lr_max, *lr_flag;
-extern int lr_flagval, lr_controlend;
-extern int lr_fdwant_readwrite; /* -1=never, 0=opt, 1=always */
-
 undivert(4)
 
 #endif
index 521c272..4894ee8 100644 (file)
--- a/version.h
+++ b/version.h
@@ -1 +1 @@
-#define VERSION "0.50"
+#define VERSION "0.55"