--- /dev/null
+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.
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
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.
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);
}
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);
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;
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;
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);
}
# 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" */
AC_PROG_CC
AC_PROG_INSTALL
-AC_SYS_LONG_FILE_NAMES
AC_SUBST(OPTIMISE)
if test "${GCC-no}" = yes; then
# 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);
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;
/* 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 */
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
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;
"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");
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) {
}
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;
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',
autovaldeftype(`execmode')
autovaldeftype(`ehandlemode')
autovaldeftype(`misc')
+autovaldeftype(`internal')
dnl simple isdirectives
define(`isdirectivefn',`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',`',
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')
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',
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
#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
%%
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(`,')
%%
#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);
return r;
}
-char *xmstrsave(const char *s) {
+char *xstrsave(const char *s) {
char *r;
r= xmalloc(strlen(s)+1);
return r;
}
-char *xmstrsubsave(const char *begin, int len) {
+char *xstrsubsave(const char *begin, int len) {
char *r;
r= xmalloc(len+1);
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);
#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
--- /dev/null
+/*
+ * 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);
+ }
+}
* 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;
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) {
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:
}
}
-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, ...) {
char errmsg[MAX_ERRMSG_LEN];
va_start(al,fmt);
- errwhere(lineno-atnewline,currentfile,¬edreferer,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);
}
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,
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;
}
} 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;
int token, r;
for (;;) { /* loop over lines */
+ cstate->reportlineno= cstate->lineno;
do { token= yylex(); } while (token == tokv_lwsp);
if (token & tokt_exception) {
return token;
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)) {
int token, r;
for (;;) { /* loop over lines */
+ cstate->reportlineno= cstate->lineno;
do { token= yylex(); } while (token == tokv_lwsp);
if (token & tokt_exception) {
return token;
}
}
-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);
* is turned into 0. */
static int fileparselevel= 0;
- struct parser_state save;
+ struct parser_state usestate;
YY_BUFFER_STATE ybuf;
int r;
FILE *file;
}
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);
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;
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) {
}
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) {
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;
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;
}
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",
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) {
}
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) {
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) {
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;
}
int r;
r= paa_1path(&cp); if (r) return r;
- free(userrcfile); userrcfile= xstrdup(cp);
+ free(userrcfile); userrcfile= xstrsave(cp);
return 0;
}
"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) {
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",
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;
*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='-'; }
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;
} 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) {
} 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;
}
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);
/*
- * 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;
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);
}
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) {
_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);
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) {
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");
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");
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; }
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) ||
}
}
}
+}
+
+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;
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();
}
--- /dev/null
+/*
+ * 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);
+}
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>
#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
-#define VERSION "0.50"
+#define VERSION "0.55"