X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=userv.git;a=blobdiff_plain;f=client.c;h=5f60d0e5cb0d2458b43c80871540812fe5f9dad5;hp=740aac54e44ecd357b2e21a61b58f82ab22b71b4;hb=59fb8163fff0941358976353d6f98a7156e593f2;hpb=a4833eb88857a3027cc2d9ff602f8f2e076b9d6d diff --git a/client.c b/client.c index 740aac5..5f60d0e 100644 --- a/client.c +++ b/client.c @@ -2,7 +2,7 @@ * userv - client.c * client code * - * Copyright (C)1996-1997,1999 Ian Jackson + * Copyright (C)1996-1997,1999-2001,2003 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 @@ -168,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; @@ -181,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; @@ -280,7 +284,7 @@ static void getprogress(struct progress_msg *progress_r, FILE *file) { for (i=0; idata.errmsg.messagelen; i++) { c= working_getc(file); if (c==EOF) protoreaderror(file,"in error message"); - if (isprint(c)) putc(c,stderr); + if (ISCHAR(isprint,c)) putc(c,stderr); else fprintf(stderr,"\\x%02x",(unsigned char)c); } putc('\n',stderr); @@ -300,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; @@ -348,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; } @@ -422,7 +415,7 @@ struct optioninfo { optionfunction *fn; }; -static void usage(void) { +static void usage(FILE *stream) { if (fputs( "usage: userv [--] [ ...]\n" "usage: userv -B|--builtin [--] [ ...]\n" @@ -431,7 +424,7 @@ static void usage(void) { " -t|--timeout \n" " -S|--signals |number|number-nocore|highbit|stdout\n" " -w|--fdwait =wait|nowait|close\n" - " -P|--sigpipe -H|--hidecwd -h|--help --copyright\n" + " -P|--sigpipe -H|--hidecwd -h|--help|--version --copyright\n" " --override } available only\n" " --override-file } to root\n" " --spoof-user } or same user\n" @@ -441,8 +434,8 @@ static void usage(void) { " 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, ...) { @@ -451,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); } @@ -504,43 +497,99 @@ static const struct fdmodifierinfo fdmodifierinfos[]= { { 0 } }; -static void addfdmodifier(int fd, const char *key) { +static void addfdmodifier(int fd, const char *key, size_t key_len) { const struct fdmodifierinfo *fdmip; if (!*key) return; - for (fdmip= fdmodifierinfos; fdmip->string && strcmp(fdmip->string,key); fdmip++); - if (!fdmip->string) usageerror("unknown fdmodifer `%s' for fd %d",key,fd); + for (fdmip= fdmodifierinfos; + fdmip->string && + !(strlen(fdmip->string) == key_len && + !memcmp(fdmip->string,key,key_len)); + fdmip++); + if (!fdmip->string) usageerror("unknown fdmodifer `%.*s' for fd %d", + (int)key_len,key,fd); if (fdmip->conflicts & fdsetup[fd].mods) - usageerror("fdmodifier `%s' conflicts with another for fd %d",key,fd); + usageerror("fdmodifier `%.*s' conflicts with another for fd %d", + (int)key_len,key,fd); fdsetup[fd].mods |= fdmip->implies; fdsetup[fd].oflags |= fdmip->oflags; } -static int fdstdnumber(const char *string) { - if (!strcmp(string,"stdin")) return 0; - else if (!strcmp(string,"stdout")) return 1; - else if (!strcmp(string,"stderr")) return 2; - else return -1; +static void addfdmodifier_fixed(int fd, const char *key) { + addfdmodifier(fd, key, strlen(key)); } +static int fdstdnumber(const char *string, const char **delim_r) { +#define FN(v,s) do{ \ + if (!memcmp(string,s,sizeof(s)-1)) { \ + *delim_r= string+sizeof(s)-1; \ + return v; \ + } \ + }while(0) + + FN(0,"stdin"); + FN(1,"stdout"); + FN(2,"stderr"); + + return -1; +} + +static int strtofd(const char *string, + const char **mods_r /* 0: no modifiers, must go to end */, + const char *what) { + int fd; + unsigned long ul; + char *delim_v; + const char *mods; + + fd= fdstdnumber(string,&mods); + if (fd>=0) { + if (*mods && *mods != ',') + usageerror("%s, when it is `stdin', `stdout' or `stderr'," + " must be delimited with a comma from any following" + " modifiers - `%s' is not permitted", + what, mods); + goto parsed; + } + + errno= 0; + ul= strtoul(string,&delim_v,10); + if (errno || delim_v == string || ul > INT_MAX) + usageerror("%s must be must be numeric file descriptor" + " or `stdin', `stdout' or `stderr'" + " - `%s' is not recognized", + what, string); + mods= delim_v; + fd= ul; + + parsed: + if (*mods==',') + mods++; + + if (mods_r) + *mods_r= mods; + else if (*mods) + usageerror("%s must be must be only file descriptor" + " - trailing portion or modifiers `%s' not permitted", + what, mods); + + if (fd > MAX_ALLOW_FD) + usageerror("%s file descriptor specified (%d)" + " is larger than maximum allowed (%d)", + what, fd, MAX_ALLOW_FD); + + return fd; +} + static void of_file(const struct optioninfo *oip, const char *value, char *key) { unsigned long fd, copyfd; struct stat stab; int oldarraysize, r; - char *delim; - - fd= strtoul(key,&delim,10); - if (delim == key) { - delim= strchr(key,','); - if (delim) *delim++= 0; - fd= fdstdnumber(key); - if (fd<0) usageerror("first part of argument to -f or --file must be numeric " - "file descriptor or `stdin', `stdout' or `stderr' - `%s' " - "is not recognized",key); - } - if (fd > MAX_ALLOW_FD) - usageerror("file descriptor specified (%lu) is larger than maximum allowed (%d)", - fd,MAX_ALLOW_FD); + size_t mod_len; + const char *mods, *delim; + + fd= strtofd(key,&mods,"first part of argument to -f or --file"); + if (fd >= fdsetupsize) { oldarraysize= fdsetupsize; fdsetupsize+=2; fdsetupsize<<=1; @@ -561,38 +610,29 @@ static void of_file(const struct optioninfo *oip, const char *value, char *key) fdsetup[fd].oflags= 0; fdsetup[fd].mods= 0; fdsetup[fd].copyfd= -1; - while (delim && *delim) { - key= delim; - delim= strchr(key,','); - if (delim) *delim++= 0; - addfdmodifier(fd,key); + while (mods && *mods) { + delim= strchr(mods,','); + mod_len= delim ? delim-mods : strlen(mods); + addfdmodifier(fd,mods,mod_len); + mods= delim ? delim+1 : 0; } if (!(fdsetup[fd].mods & (fdm_read|fdm_write))) { if (fd == 0) { - addfdmodifier(fd,"read"); + addfdmodifier_fixed(fd,"read"); } else if (fdsetup[fd].mods & fdm_fd) { - addfdmodifier(fd,"write"); + addfdmodifier_fixed(fd,"write"); } else { - addfdmodifier(fd,"overwrite"); + addfdmodifier_fixed(fd,"overwrite"); } } if (fdsetup[fd].mods & fdm_fd) { - copyfd= fdstdnumber(value); - if (copyfd<0) { - copyfd= strtoul(value,&delim,0); - if (*delim) - usageerror("value part of argument to --file with fd modifier must be " - "numeric or fd name - `%s' is not recognised",value); - else if (copyfd > MAX_ALLOW_FD) - usageerror("file descriptor %lu named as target of file descriptor redirection" - " (for file descriptor %lu) is larger than maximum allowed (%d)", - copyfd,fd,MAX_ALLOW_FD); - } + copyfd= strtofd(value,0, + "value part of argument to --file with fd modifier"); 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; } @@ -600,18 +640,9 @@ static void of_file(const struct optioninfo *oip, const char *value, char *key) static void of_fdwait(const struct optioninfo *oip, const char *value, char *key) { const struct fdmodifierinfo *fdmip; - unsigned long ul; int fd; - char *delim; - fd= fdstdnumber(key); - if (fd<0) { - ul= strtoul(key,&delim,0); - if (*delim) usageerror("first part of argument to --fdwait must be " - "numeric or fd name - `%s' is not recognised",key); - if (ul>MAX_ALLOW_FD) usageerror("first part of argument to --fdwait is too large"); - fd= ul; - } + fd= strtofd(key,0,"first part of argument to --fdwait"); if (fd >= fdsetupsize || !fdsetup[fd].filename) usageerror("file descriptor %d specified in --fdwait option is not open",fd); for (fdmip= fdmodifierinfos; fdmip->string && strcmp(fdmip->string,value); fdmip++); @@ -644,9 +675,11 @@ static void of_defvar(const struct optioninfo *oip, const char *value, char *key static void of_timeout(const struct optioninfo *oip, const char *value, char *key) { char *endp; unsigned long ul; - - ul= strtoul(value,&endp,0); - if (*endp) usageerror("timeout value `%s' must be a plain decimal string",value); + + errno= 0; + ul= strtoul(value,&endp,10); + if (errno || *endp) + usageerror("timeout value `%s' must be a plain decimal string",value); if (ul>INT_MAX) usageerror("timeout value %lu too large",ul); timeout= ul; } @@ -654,9 +687,10 @@ static void of_timeout(const struct optioninfo *oip, const char *value, char *ke static void of_signals(const struct optioninfo *oip, const char *value, char *key) { unsigned long numvalue; char *endp; - - numvalue= strtoul(value,&endp,0); - if (*endp) { + + errno= 0; + numvalue= strtoul(value,&endp,10); + if (errno || *endp) { if (!strcmp(value,"number")) signalsexit= se_number; else if (!strcmp(value,"number-nocore")) signalsexit= se_numbernocore; else if (!strcmp(value,"highbit")) signalsexit= se_highbit; @@ -678,7 +712,14 @@ 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); } @@ -694,7 +735,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 or\n" +" with userv; if not, write to Ian Jackson 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"); @@ -731,6 +772,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 }, @@ -865,7 +907,7 @@ static void determine_users(void) { } if (!loginname) { pw= getpwuid(myuid); if (!pw) miscerror("cannot determine your login name"); - loginname= pw->pw_name; + loginname= xstrsave(pw->pw_name); } if (!strcmp(serviceuser,"-")) serviceuser= loginname; @@ -946,7 +988,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); @@ -958,7 +1000,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; @@ -995,7 +1037,7 @@ static int server_connect(void) { if (errno == ECONNREFUSED || errno == ENOENT) syscallerror("uservd daemon is not running - service not available"); if (errno != EINTR) - syscallerror("unable to connect to uservd daemon: %m"); + fsyscallerror("unable to connect to uservd daemon: %m"); } return sfd; @@ -1028,16 +1070,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(); } } @@ -1121,6 +1163,21 @@ static void prepare_asynchsignals(void) { if (alarm(timeout)<0) syscallerror("set up timeout alarm"); } + +static void close_unwanted_pipes(void) { + int fd; + + for (fd=0; fd2) + if (close(fdsetup[fd].copyfd)) + if (errno != EBADF) + /* EBADF can be induced if cmd line specifies same fd twice */ + fsyscallerror("close real fd for %d",fd); + } +} + static void catdup(const char *which, int from, int to) { if (dup2(from,to)<0) { blocksignals(SIG_BLOCK); @@ -1142,13 +1199,13 @@ 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); } blocksignals(SIG_BLOCK); child= fork(); fdsetup[fd].catpid= child; blocksignals(SIG_UNBLOCK); - if (child==-1) syscallerror("fork for cat for fd %d",fd); + 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; @@ -1163,14 +1220,13 @@ static void connect_pipes(void) { reading= fdsetup[fd].mods & fdm_read; catdup(catnamebuf, fdsetup[fd].copyfd, reading ? 0 : 1); catdup(catnamebuf, fdsetup[fd].pipefd, reading ? 1 : 0); + close_unwanted_pipes(); execl("/bin/cat",catnamebuf,(char*)0); fprintf(stderr,"userv: %s: cannot exec `cat': %s\n",catnamebuf,strerror(errno)); 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); } + close_unwanted_pipes(); } static void server_sendconfirm(void) { @@ -1204,7 +1260,7 @@ static void dispose_remaining_pipes(void) { blocksignals(SIG_BLOCK); for (fd=0; fd