* 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
static char *cwdbuf;
static size_t cwdbufsize;
static char *ovbuf;
-static int ovused, systemerror;
+static int ovused, systemerror, socketfd;
static void blocksignals(int how) {
sigset_t set;
"(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"
+ "userv and uservd version " VERSION VEREXT ".\n"
+ "Copyright (C)1996-2003,2006 Ian Jackson; copyright (C)2000 Ben Harris.\n"
"there is NO WARRANTY; type `userv --copyright' for details.\n",
stream) < 0)
syscallerror("write usage message");
{ 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;
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) fsyscallerror("check filedescriptor %lu (named as target of file "
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++);
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;
}
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;
mypid= getpid(); if (mypid == (pid_t)-1) syscallerror("getpid");
myuid= getuid(); if (myuid == (uid_t)-1) syscallerror("getuid");
mygid= getgid(); if (mygid == (gid_t)-1) syscallerror("getgid");
- ngids= getgroups(0,0); if (ngids == (gid_t)-1) syscallerror("getgroups(0,0)");
+ ngids= getgroups(0,0); if (ngids == -1) syscallerror("getgroups(0,0)");
gidarray= xmalloc(sizeof(gid_t)*ngids);
if (getgroups(ngids,gidarray) != ngids) syscallerror("getgroups(ngids,)");
if (alarm(timeout)<0) syscallerror("set up timeout alarm");
}
+
+static void close_unwanted_pipes(void) {
+ int fd;
+
+ for (fd=0; fd<fdsetupsize; fd++) {
+ if (!fdsetup[fd].filename) continue;
+ if (close(fdsetup[fd].pipefd)) fsyscallerror("close pipe fd for %d",fd);
+ if (fdsetup[fd].copyfd>2)
+ 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);
reading= fdsetup[fd].mods & fdm_read;
catdup(catnamebuf, fdsetup[fd].copyfd, reading ? 0 : 1);
catdup(catnamebuf, fdsetup[fd].pipefd, reading ? 1 : 0);
+ if (close(socketfd))
+ fsyscallerror("%s: close client socket for for cat",catnamebuf);
+ 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)) fsyscallerror("close real fd for %d",fd);
- if (close(fdsetup[fd].pipefd)) fsyscallerror("close pipe fd for %d",fd);
}
+ close_unwanted_pipes();
}
static void server_sendconfirm(void) {
}
int main(int argc, char *const *argv) {
- int status, socketfd;
+ int status;
#ifdef NDEBUG
# error Do not disable assertions in this security-critical code !