chiark / gitweb /
regen
[userv.git] / client.c
index 3e9f08fa835da9dfa5fa921d5f345a8e87887a2b..c6b259bbd899c73046766ab8a1e33a572efe5ba5 100644 (file)
--- a/client.c
+++ b/client.c
@@ -2,7 +2,7 @@
  * userv - client.c
  * client code
  *
- * Copyright (C)1996-1997 Ian Jackson
+ * Copyright (C)1996-1997,1999 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
@@ -72,6 +72,7 @@
 
 #include "config.h"
 #include "common.h"
+#include "both.h"
 #include "version.h"
 
 enum fdmodifiervalues {
@@ -127,7 +128,7 @@ static uid_t myuid, spoofuid;
 static gid_t mygid, spoofgid, *gidarray;
 static int ngids;
 static struct opening_msg opening_mbuf;
-static const char *logname;
+static const char *loginname;
 static char *cwdbuf;
 static size_t cwdbufsize;
 static char *ovbuf;
@@ -167,7 +168,7 @@ static void NONRETURNPRINTFFORMAT(1,2) miscerror(const char *fmt, ...) {
   exit(-1);
 }
 
-static void NONRETURNPRINTFFORMAT(1,2) syscallerror(const char *fmt, ...) {
+static void NONRETURNPRINTFFORMAT(1,2) fsyscallerror(const char *fmt, ...) {
   va_list al;
   int e;
 
@@ -180,6 +181,10 @@ static void NONRETURNPRINTFFORMAT(1,2) syscallerror(const char *fmt, ...) {
   exit(-1);
 }
 
+void syscallerror(const char *what) {
+  fsyscallerror("%s",what);
+}
+
 static void NONRETURNING protoreaderror(FILE *file, const char *where) {
   int e;
 
@@ -213,7 +218,7 @@ static void NONRETURNPRINTFFORMAT(1,2) protoerror(const char *fmt, ...) {
 
 static void xfread(void *p, size_t sz, FILE *file) {
   size_t nr;
-  nr= fread(p,1,sz,file);
+  nr= working_fread(p,sz,file);
   if (nr != sz) protoreaderror(file,"in data");
 }
 
@@ -277,8 +282,9 @@ static void getprogress(struct progress_msg *progress_r, FILE *file) {
        protoerror("stderr message length %d is far too long",
                   progress_r->data.errmsg.messagelen);
       for (i=0; i<progress_r->data.errmsg.messagelen; i++) {
-       c= getc(file); if (c==EOF) protoreaderror(file,"in error message");
-       if (isprint(c)) putc(c,stderr);
+       c= working_getc(file);
+       if (c==EOF) protoreaderror(file,"in error message");
+       if (ISCHAR(isprint,c)) putc(c,stderr);
        else fprintf(stderr,"\\x%02x",(unsigned char)c);
       }
       putc('\n',stderr);
@@ -298,18 +304,7 @@ static void getprogress(struct progress_msg *progress_r, FILE *file) {
  * the signal asynchronicity starts.  They can do anything they like.
  */
 
-static void *xmalloc(size_t s) {
-  void *p;
-  p= malloc(s?s:1);
-  if (!p) syscallerror("malloc (%lu bytes)",(unsigned long)s);
-  return p;
-}
-
-static void *xrealloc(void *p, size_t s) {
-  p= realloc(p,s);
-  if (!p) syscallerror("realloc (%lu bytes)",(unsigned long)s);
-  return p;
-}
+/* This includes xmalloc and xrealloc from both.c */
 
 static void xfwritestring(const char *s, FILE *file) {
   int l;
@@ -346,7 +341,7 @@ static void disconnect(void) /* DOES return, unlike in daemon */ {
     event_mbuf.type= et_disconnect;
     r= fwrite(&event_mbuf,1,sizeof(event_mbuf),swfile);
     if ((r != sizeof(event_mbuf) || fflush(swfile)) && errno != EPIPE)
-      syscallerror("write to server when disconnecting\n");
+      syscallerror("write to server when disconnecting");
   }
   systemerror= 1;
 }
@@ -378,6 +373,7 @@ static void sighandler_chld(int ignored) /* DOES return, unlike in daemon */ {
        memset(&event_mbuf,0,sizeof(event_mbuf));
        event_mbuf.magic= EVENT_MAGIC;
        event_mbuf.type= et_closereadfd;
+        event_mbuf.data.closereadfd.fd= fd;
        r= fwrite(&event_mbuf,1,sizeof(event_mbuf),swfile);
        if (r != sizeof(event_mbuf) || fflush(swfile))
          if (errno != EPIPE) syscallerror("inform service of closed read fd");
@@ -419,7 +415,7 @@ struct optioninfo {
   optionfunction *fn;
 };
 
-static void usage(void) {
+static void usage(FILE *stream) {
   if (fputs(
     "usage: userv <options> [--] <service-user> <service-name> [<argument> ...]\n"
     "usage: userv <options> -B|--builtin [--] <builtin-service> [<info-argument> ...]\n"
@@ -428,16 +424,18 @@ static void usage(void) {
     "         -t|--timeout <seconds>\n"
     "         -S|--signals <status>|number|number-nocore|highbit|stdout\n"
     "         -w|--fdwait <fd>=wait|nowait|close\n"
-    "         -P|--sigpipe  -H|--hidecwd  -h|--help  --copyright\n"
+    "         -P|--sigpipe  -H|--hidecwd  -h|--help|--version  --copyright\n"
     "         --override <configuration-data> } available only\n"
     "         --override-file <filename>      }  to root\n"
     "         --spoof-user <username>         }  or same user\n"
     "fdmodifiers:            read    write  overwrite    trunc[ate]\n"
-    "(separate with commas)  append  sync   excl[usive]  creat[e]  fd\n\n"
-    "userv and uservd version " VERSION "; copyright (C)1996-1997 Ian Jackson.\n"
+    "(separate with commas)  append  sync   excl[usive]  creat[e]  fd\n"
+    "userv -B 'X' ... is same as userv --override 'execute-builtin X' - 'X' ...\n"
+    "         for help, type `userv -B help'; remember to quote multi-word X\n"
+    "userv and uservd version " VERSION VEREXT "; copyright (C)1996-1999 Ian Jackson.\n"
     "there is NO WARRANTY; type `userv --copyright' for details.\n",
-            stderr) < 0)
-    syscallerror("write usage to stderr");
+            stream) < 0)
+    syscallerror("write usage message");
 }
 
 static void NONRETURNPRINTFFORMAT(1,2) usageerror(const char *fmt, ...) {
@@ -446,7 +444,7 @@ static void NONRETURNPRINTFFORMAT(1,2) usageerror(const char *fmt, ...) {
   fputs("userv: ",stderr);
   vfprintf(stderr,fmt,al);
   fputs("\n\n",stderr);
-  usage();
+  usage(stderr);
   exit(-1);
 }
 
@@ -585,9 +583,9 @@ static void of_file(const struct optioninfo *oip, const char *value, char *key)
     }
     r= fstat(copyfd,&stab);
     if (r) {
-      if (oip) syscallerror("check filedescriptor %lu (named as target of file "
-                           "descriptor redirection for %lu)",copyfd,fd);
-      else syscallerror("check basic filedescriptor %lu at program start",copyfd);
+      if (oip) fsyscallerror("check filedescriptor %lu (named as target of file "
+                            "descriptor redirection for %lu)",copyfd,fd);
+      else fsyscallerror("check basic filedescriptor %lu at program start",copyfd);
     }
     fdsetup[fd].copyfd= copyfd;
   }
@@ -673,13 +671,20 @@ static void of_hidecwd(const struct optioninfo *oip, const char *value, char *ke
 }
 
 static void of_help(const struct optioninfo *oip, const char *value, char *key) {
-  usage();
+  usage(stdout);
+  if (fclose(stdout)) syscallerror("fclose stdout after writing usage message");
+  exit(0);
+}
+
+static void of_version(const struct optioninfo *oip, const char *value, char *key) {
+  if (puts(VERSION VEREXT) == EOF || fclose(stdout))
+    syscallerror("write version number");
   exit(0);
 }
 
 static void of_copyright(const struct optioninfo *oip, const char *value, char *key) {
   if (fputs(
-" userv - user service daemon and client; copyright (C)1996-1997 Ian Jackson\n\n"
+" userv - user service daemon and client; copyright (C)1996-1999 Ian Jackson\n\n"
 " This is free software; you can redistribute it and/or modify it under the\n"
 " terms of the GNU General Public License as published by the Free Software\n"
 " Foundation; either version 2 of the License, or (at your option) any\n"
@@ -689,7 +694,7 @@ static void of_copyright(const struct optioninfo *oip, const char *value, char *
 " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General\n"
 " Public License for more details.\n\n"
 " You should have received a copy of the GNU General Public License along\n"
-" with userv; if not, write to Ian Jackson <ian@chiark.greenend.org.uk> or\n"
+" with userv; if not, write to Ian Jackson <ian@davenant.greenend.org.uk> or\n"
 " to the Free Software Foundation, 59 Temple Place - Suite 330, Boston,\n"
 " MA 02111-1307, USA.\n",
            stdout) < 0) syscallerror("write usage to stderr");
@@ -726,6 +731,7 @@ const struct optioninfo optioninfos[]= {
   { 'H', "hidecwd",       0, of_hidecwd      },
   { 'B', "builtin",       0, of_builtin      },
   { 'h', "help",          0, of_help         },
+  {  0,  "version",       0, of_version      },
   {  0,  "copyright",     0, of_copyright    },
   {  0,  "override",      1, of_override     },
   {  0,  "override-file", 1, of_overridefile },
@@ -737,13 +743,14 @@ static void callvalueoption(const struct optioninfo *oip, char *arg) {
   char *equals;
   if (oip->values == 2) {
     equals= strchr(arg,'=');
-    if (!equals)
+    if (!equals) {
       if (oip->abbrev)
         usageerror("option --%s (-%c) passed argument `%s' with no `='",
                    oip->full,oip->abbrev,arg);
       else
         usageerror("option --%s passed argument `%s' with no `='",
                    oip->full,arg);
+    }
     *equals++= 0;
     (oip->fn)(oip,equals,arg);
   } else {
@@ -851,18 +858,18 @@ static void determine_users(void) {
   
   spoofuid= myuid;
   spoofgid= mygid;
-  logname= getenv("LOGNAME");
-  if (!logname) logname= getenv("USER");
-  if (logname) {
-    pw= getpwnam(logname);
-    if (!pw || pw->pw_uid != myuid) logname= 0;
+  loginname= getenv("LOGNAME");
+  if (!loginname) loginname= getenv("USER");
+  if (loginname) {
+    pw= getpwnam(loginname);
+    if (!pw || pw->pw_uid != myuid) loginname= 0;
   }
-  if (!logname) {
+  if (!loginname) {
     pw= getpwuid(myuid); if (!pw) miscerror("cannot determine your login name");
-    logname= pw->pw_name;
+    loginname= xstrsave(pw->pw_name);
   }
 
-  if (!strcmp(serviceuser,"-")) serviceuser= logname;
+  if (!strcmp(serviceuser,"-")) serviceuser= loginname;
   pw= getpwnam(serviceuser);
   if (!pw) miscerror("requested service user `%s' is not a user",serviceuser);
   serviceuid= pw->pw_uid;
@@ -872,9 +879,9 @@ static void determine_users(void) {
               " the user who will be providing the service");
 
   if (spoofuser) {
-    logname= spoofuser;
-    pw= getpwnam(logname);
-    if (!pw) miscerror("spoofed login name `%s' is not valid",logname);
+    loginname= spoofuser;
+    pw= getpwnam(loginname);
+    if (!pw) miscerror("spoofed login name `%s' is not valid",loginname);
     spoofuid= pw->pw_uid;
     spoofgid= pw->pw_gid;
     ngidssize= ngids; ngids= 0;
@@ -884,7 +891,7 @@ static void determine_users(void) {
     }
     gidarray[ngids++]= spoofgid;
     while ((gr= getgrent())) { /* ouch! getgrent has no error behaviour! */
-      for (mem= gr->gr_mem; *mem && strcmp(*mem,logname); mem++);
+      for (mem= gr->gr_mem; *mem && strcmp(*mem,loginname); mem++);
       if (!*mem) continue;
       if (ngids>=ngidssize) {
       if (ngids>=MAX_GIDS) miscerror("spoofed user is member of too many groups");
@@ -940,7 +947,7 @@ static void process_override(const char *servicename) {
     break;
   case ot_file:
     ovfile= fopen(overridevalue,"r");
-    if (!ovfile) syscallerror("open overriding configuration file `%s'",overridevalue);
+    if (!ovfile) fsyscallerror("open overriding configuration file `%s'",overridevalue);
     ovbuf= 0; ovavail= ovused= 0;
     while ((c= getc(ovfile)) != EOF) {
       if (!c) miscerror("overriding config file `%s' contains null(s)",overridevalue);
@@ -952,7 +959,7 @@ static void process_override(const char *servicename) {
       ovbuf[ovused++]= c;
     }
     if (ferror(ovfile) || fclose(ovfile))
-      syscallerror("read overriding configuration file `%s'",overridevalue);
+      fsyscallerror("read overriding configuration file `%s'",overridevalue);
     ovbuf= xrealloc(ovbuf,ovused+1);
     ovbuf[ovused]= 0;
     break;
@@ -988,7 +995,8 @@ static int server_connect(void) {
   while (connect(sfd,(struct sockaddr*)&ssockname,sizeof(ssockname))) {
     if (errno == ECONNREFUSED || errno == ENOENT)
       syscallerror("uservd daemon is not running - service not available");
-    syscallerror("unable to connect to uservd daemon");
+    if (errno != EINTR)
+      fsyscallerror("unable to connect to uservd daemon: %m");
   }
 
   return sfd;
@@ -1021,16 +1029,16 @@ static void server_preparepipes(void) {
     assert(!pipepathbuf[PIPEPATHMAXLEN]);
     priv_resume();
     if (unlink(pipepathbuf) && errno != ENOENT)
-      syscallerror("remove any old pipe `%s'",pipepathbuf);
+      fsyscallerror("remove any old pipe `%s'",pipepathbuf);
     if (mkfifo(pipepathbuf,0600)) /* permissions are irrelevant */
-      syscallerror("create pipe `%s'",pipepathbuf);
+      fsyscallerror("create pipe `%s'",pipepathbuf);
     tempfd= open(pipepathbuf,O_RDWR);
-    if (tempfd<0) syscallerror("prelim open pipe `%s' for read+write",pipepathbuf);
+    if (tempfd<0) fsyscallerror("prelim open pipe `%s' for read+write",pipepathbuf);
     assert(fdsetup[fd].mods & (fdm_read|fdm_write));
     fdsetup[fd].pipefd=
       open(pipepathbuf, (fdsetup[fd].mods & fdm_read) ? O_WRONLY : O_RDONLY);
-    if (fdsetup[fd].pipefd<0) syscallerror("real open pipe `%s'",pipepathbuf);
-    if (close(tempfd)) syscallerror("close prelim fd onto pipe `%s'",pipepathbuf);
+    if (fdsetup[fd].pipefd<0) fsyscallerror("real open pipe `%s'",pipepathbuf);
+    if (close(tempfd)) fsyscallerror("close prelim fd onto pipe `%s'",pipepathbuf);
     priv_suspend();
   }
 }
@@ -1045,7 +1053,8 @@ static void server_sendrequest(int argc, char *const *argv) {
   request_mbuf.clientpid= getpid();
   request_mbuf.serviceuserlen= strlen(serviceuser);
   request_mbuf.servicelen= strlen(argv[0]);
-  request_mbuf.lognamelen= strlen(logname);
+  request_mbuf.loginnamelen= strlen(loginname);
+  request_mbuf.spoofed= spoofuser ? 1 : 0;
   request_mbuf.cwdlen= cwdbufsize;
   request_mbuf.callinguid= spoofuid;
   request_mbuf.ngids= ngids+1;
@@ -1063,7 +1072,7 @@ static void server_sendrequest(int argc, char *const *argv) {
   xfwrite(&request_mbuf,sizeof(request_mbuf),swfile);
   xfwrite(serviceuser,sizeof(*serviceuser)*request_mbuf.serviceuserlen,swfile);
   xfwrite(argv[0],sizeof(*argv[0])*request_mbuf.servicelen,swfile);
-  xfwrite(logname,sizeof(*logname)*request_mbuf.lognamelen,swfile);
+  xfwrite(loginname,sizeof(*loginname)*request_mbuf.loginnamelen,swfile);
   xfwrite(cwdbuf,sizeof(*cwdbuf)*request_mbuf.cwdlen,swfile);
   if (ovused>=0) xfwrite(ovbuf,sizeof(*ovbuf)*ovused,swfile);
   xfwrite(&spoofgid,sizeof(gid_t),swfile);
@@ -1126,6 +1135,7 @@ static void connect_pipes(void) {
   struct sigaction sig;
   char catnamebuf[sizeof(int)*3+30];
   int fd, reading;
+  pid_t child;
   
   for (fd=0; fd<fdsetupsize; fd++) {
     if (!fdsetup[fd].filename) continue;
@@ -1133,11 +1143,14 @@ static void connect_pipes(void) {
       fdsetup[fd].copyfd=
        open(fdsetup[fd].filename,fdsetup[fd].oflags|O_NOCTTY,0777);
       if (fdsetup[fd].copyfd<0)
-       syscallerror("open file `%s' for fd %d",fdsetup[fd].filename,fd);
+       fsyscallerror("open file `%s' for fd %d",fdsetup[fd].filename,fd);
     }
-    fdsetup[fd].catpid= fork();
-    if (fdsetup[fd].catpid==-1) syscallerror("fork for cat for fd %d",fd);
-    if (!fdsetup[fd].catpid) {
+    blocksignals(SIG_BLOCK);
+    child= fork();
+    fdsetup[fd].catpid= child;
+    blocksignals(SIG_UNBLOCK);
+    if (child==-1) fsyscallerror("fork for cat for fd %d",fd);
+    if (!child) {
       snprintf(catnamebuf,sizeof(catnamebuf),"cat fd%d",fd);
       catnamebuf[sizeof(catnamebuf)-1]= 0;
       sig.sa_handler= SIG_DFL;
@@ -1156,8 +1169,8 @@ static void connect_pipes(void) {
       exit(-1);
     }
     if (fdsetup[fd].copyfd>2)
-      if (close(fdsetup[fd].copyfd)) syscallerror("close real fd for %d",fd);
-    if (close(fdsetup[fd].pipefd)) syscallerror("close pipe fd for %d",fd);
+      if (close(fdsetup[fd].copyfd)) fsyscallerror("close real fd for %d",fd);
+    if (close(fdsetup[fd].pipefd)) fsyscallerror("close pipe fd for %d",fd);
   }
 }
 
@@ -1192,7 +1205,7 @@ static void dispose_remaining_pipes(void) {
   blocksignals(SIG_BLOCK);
   for (fd=0; fd<fdsetupsize; fd++) {
     if (!(fdsetup[fd].catpid!=-1 && (fdsetup[fd].mods & fdm_close))) continue;
-    if (kill(fdsetup[fd].catpid,SIGKILL)) syscallerror("kill cat for %d",fd);
+    if (kill(fdsetup[fd].catpid,SIGKILL)) fsyscallerror("kill cat for %d",fd);
     fdsetup[fd].killed= 1;
   }
   blocksignals(SIG_UNBLOCK);