chiark / gitweb /
Up to halfway down rhs p.12, inc builtin services, but not working yet.
authorian <ian>
Sat, 6 Sep 1997 18:27:14 +0000 (18:27 +0000)
committerian <ian>
Sat, 6 Sep 1997 18:27:14 +0000 (18:27 +0000)
12 files changed:
INSTALL
Makefile.in
client.c
common.h
daemon.h
debug.c
language.i4
lexer.l.m4
parser.c
process.c
servexec.c
spec.sgml

diff --git a/INSTALL b/INSTALL
index d7211b646a595be7b75e014974f624570715626f..8c5db28545b190e7ddf019230875dcf3fcccf802 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -32,6 +32,7 @@ C Library:
 * strtoul;
 * memcpy, memset, memcpy;
 * realloc(0,size) must work and be equivalent to malloc(size).
+* free(0) must work and do nothing
 * <stdarg.h> (not varargs) and v[sf][n]printf.
 
 System interfaces:
index 0df4f955ca62749e77559b35bfffa6a8d2f5cde8..4ef8e7dbbb08a81749ade47ed69b985693dd088f 100644 (file)
@@ -72,7 +72,7 @@ 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
+servexec.o:    config.h common.h pcsum.h daemon.h lib.h version.h
 
 lib.o:         config.h lib.h
 
index bee10a1a3a5857d95b5a2491b46bf1c31939fab5..7a810b534aa98d42da10dad7135d4b5810a825a2 100644 (file)
--- a/client.c
+++ b/client.c
@@ -135,15 +135,17 @@ struct fdsetupstate {
 enum signalsexitspecials { se_number=-100, se_numbernocore, se_highbit, se_stdout };
 enum overridetypes { ot_none, ot_string, ot_file };
 
-static const char *serviceuser=0;
+struct constkeyvaluepair { const char *key, *value; };
+
+static const char *serviceuser;
 static uid_t serviceuid, myuid;
-static struct fdsetupstate *fdsetup=0;
-static int fdsetupsize=0;
-static const char *(*defvarsarray)[2];
-static int defvarsavail=0, defvarsused=0;
-static unsigned long timeout=0;
+static struct fdsetupstate *fdsetup;
+static int fdsetupsize, builtin;
+static struct constkeyvaluepair *defvararray;
+static int defvaravail, defvarused;
+static unsigned long timeout;
 static int signalsexit=254;
-static int sigpipeok=0, hidecwd=0;
+static int sigpipeok, hidecwd;
 static int overridetype= ot_none;
 static const char *overridevalue, *spoofuser=0;
 
@@ -276,6 +278,7 @@ static void xfflush(FILE *file) {
 static void usage(void) {
   if (fprintf(stderr,
     "usage: userv <options> [--] <service-user> <service-name> [<argument> ...]\n"
+    "usage: userv <options> -B|--builtin [--] <builtin-service> [<info-argument> ...]\n"
     "options: -f|--file <fd>[<fdmodifiers>]=<filename>\n"
     "         -D|--defvar <name>=<value>\n"
     "         -t|--timeout <seconds>\n"
@@ -420,19 +423,21 @@ static void of_fdwait(const struct optioninfo *oip, const char *value, char *key
 static void of_defvar(const struct optioninfo *oip, const char *value, char *key) {
   int i;
 
+  if (!key[0])
+    usageerror("empty string not allowed as variable name");
   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);
+  for (i=0; i<defvarused && strcmp(defvararray[i].key,key); i++);
+  if (defvarused >= MAX_ARGSDEFVAR) usageerror("far too many --defvar or -D options");
+  if (i>=defvaravail) {
+    defvaravail+=10; defvaravail<<=1;
+    defvararray= xrealloc(defvararray,sizeof(struct constkeyvaluepair)*defvaravail);
   }
-  if (i==defvarsused) defvarsused++;
-  defvarsarray[i][0]= key;
-  defvarsarray[i][1]= value;
+  if (i==defvarused) defvarused++;
+  defvararray[i].key= key;
+  defvararray[i].value= value;
 }
 
 static void of_timeout(const struct optioninfo *oip, const char *value, char *key) {
@@ -467,6 +472,10 @@ static void of_hidecwd(const struct optioninfo *oip, const char *value, char *ke
   hidecwd=1;
 }
 
+static void of_builtin(const struct optioninfo *oip, const char *value, char *key) {
+  builtin=1;
+}
+
 static void of_help(const struct optioninfo *oip, const char *value, char *key) {
   usage();
   exit(0);
@@ -515,6 +524,7 @@ const struct optioninfo optioninfos[]= {
   { 'S', "signals",       1, of_signals      },
   { 'P', "sigpipe",       0, of_sigpipe      },
   { 'H', "hidecwd",       0, of_hidecwd      },
+  { 'B', "builtin",       0, of_builtin      },
   { 'h', "help",          0, of_help         },
   {  0,  "copyright",     0, of_copyright    },
   {  0,  "override",      1, of_override     },
@@ -676,7 +686,7 @@ int main(int argc, char *const *argv) {
   char *argp;
   const struct optioninfo *oip;
   struct sockaddr_un ssockname;
-  int sfd, ngids, i, j, tempfd, l, c, reading, fd, r, status, ngidssize;
+  int sfd, ngids, i, tempfd, l, c, reading, fd, r, status, ngidssize;
   sigset_t sset;
   unsigned long ul;
   size_t cwdbufsize;
@@ -715,7 +725,7 @@ int main(int argc, char *const *argv) {
   of_file(0,"stderr",fd2key);
 
   for (argpp= argv+1;
-       (argp= *argpp) && *argp == '-';
+       (argp= *argpp) && *argp == '-' && argp[1];
        argpp++) {
     if (!*++argp) usageerror("unknown option/argument `%s'",*argpp);
     if (*argp == '-') { /* Two hyphens */
@@ -749,9 +759,15 @@ int main(int argc, char *const *argv) {
       }
     }
   }
-  if (!*argpp) usageerror("no service user given after options");
-  serviceuser= *argpp++;
-  if (!*argpp) usageerror("no service name given after options and service user");
+  if (builtin) {
+    serviceuser= "-";
+  } else {
+    if (!*argpp) usageerror("no service user given after options");
+    serviceuser= *argpp++;
+  }
+  if (!*argpp) usageerror(builtin ?
+                         "no service name given after options and service user" :
+                         "no builtin service given after options");
   
   for (fd=0; fd<fdsetupsize; fd++) {
     if (!fdsetup[fd].filename) continue;
@@ -767,9 +783,23 @@ int main(int argc, char *const *argv) {
 
   argc-= (argpp-argv);
   argv= argpp;
-  if (argc > MAX_ARGSDEFVARS) usageerror("far too many arguments");
+  if (argc > MAX_ARGSDEFVAR) usageerror("far too many arguments");
   if (ngids > MAX_GIDS) miscerror("caller is in far too many gids");
   
+  spoofuid= myuid;
+  spoofgid= mygid;
+  logname= getenv("LOGNAME");
+  if (!logname) logname= getenv("USER");
+  if (logname) {
+    pw= getpwnam(logname);
+    if (!pw || pw->pw_uid != myuid) logname= 0;
+  }
+  if (!logname) {
+    pw= getpwuid(myuid); if (!pw) miscerror("cannot determine your login name");
+    logname= pw->pw_name;
+  }
+
+  if (!strcmp(serviceuser,"-")) serviceuser= logname;
   pw= getpwnam(serviceuser);
   if (!pw) miscerror("requested service user `%s' is not a user",serviceuser);
   serviceuid= pw->pw_uid;
@@ -799,19 +829,6 @@ int main(int argc, char *const *argv) {
       }
       gidarray[ngids++]= gr->gr_gid;
     }
-  } else {
-    spoofuid= myuid;
-    spoofgid= mygid;
-    logname= getenv("LOGNAME");
-    if (!logname) logname= getenv("USER");
-    if (logname) {
-      pw= getpwnam(logname);
-      if (!pw || pw->pw_uid != myuid) logname= 0;
-    }
-    if (!logname) {
-      pw= getpwuid(myuid); if (!pw) miscerror("cannot determine your login name");
-      logname= pw->pw_name;
-    }
   }
 
   cwdbufsize= 0; cwdbuf= 0;
@@ -931,7 +948,7 @@ int main(int argc, char *const *argv) {
     else request_mbuf.nreadfds++;
   }
   request_mbuf.nargs= argc-1;
-  request_mbuf.nvars= defvarsused;
+  request_mbuf.nvars= defvarused;
   request_mbuf.overridelen= ovused;
   xfwrite(&request_mbuf,sizeof(request_mbuf),swfile);
   xfwrite(serviceuser,sizeof(*serviceuser)*request_mbuf.serviceuserlen,swfile);
@@ -945,9 +962,10 @@ int main(int argc, char *const *argv) {
   xfwritefds(fdm_write,request_mbuf.nwritefds,swfile);
   for (i=1; i<argc; i++)
     xfwritestring(argv[i],swfile);
-  for (i=0; i<defvarsused; i++)
-    for (j=0; j<2; j++)
-      xfwritestring(defvarsarray[i][j],swfile);
+  for (i=0; i<defvarused; i++) {
+    xfwritestring(defvararray[i].key,swfile);
+    xfwritestring(defvararray[i].value,swfile);
+  }
   ul= REQUEST_END_MAGIC; xfwrite(&ul,sizeof(ul),swfile);
   xfflush(swfile);
 
index 5fdfd76670a356105e03710135dace489bb4c17a..73e015275594688e67fb039c489d63d9c77cb0b6 100644 (file)
--- a/common.h
+++ b/common.h
@@ -53,16 +53,15 @@ static const unsigned char protocolchecksumversion[PCSUMSIZE]= {
 #endif
 
 #define MAX_ALLOW_FD 1024
-#define MAX_INCLUDE_NEST 40
 #define MAX_GENERAL_STRING (1024*1024)
 #define MAX_OVERRIDE_LEN MAX_GENERAL_STRING
-#define MAX_ARGSDEFVARS 4096
+#define MAX_ARGSDEFVAR 4096
 #define MAX_GIDS 1024
 
 #ifdef DEBUG
-# define BASE_MAGIC 0x5deb7567 /* "\x5d\xebug" */
+# define BASE_MAGIC 0x5deb7567UL /* "\x5d\xebug" */
 #else
-# define BASE_MAGIC 0x755e7276 /* "u\x5erv" */
+# define BASE_MAGIC 0x755e7276UL /* "u\x5erv" */
 #endif
 
 enum {
index 25b6f71173357855a506f469aa8c8e16a03a131a..7b06bf7b77a6e6f3cc1c8a609059c7c433352623 100644 (file)
--- a/daemon.h
+++ b/daemon.h
@@ -25,7 +25,7 @@
 #include <sys/types.h>
 
 #define RESET_CONFIGURATION " \n\
-  cd " USERDIRPREFIX "        \n\
+  cd ~/                       \n\
   reject                      \n\
   no-set-environment          \n\
   suppress-args               \n\
@@ -64,7 +64,6 @@
 #define DEFAULTINCLUDELOOKUP        ":default"
 #define EMPTYINCLUDELOOKUP          ":empty"
 
-#define USERDIRPREFIX               USERDIR "/"
 #define USERCONFIGDIRBASE           SYSTEMUSERVCONFIGDIR
 #define USERCONFIGDIR               HIDDENPREFIX USERCONFIGDIRBASE
 #define USERUSERVCONFIGPATH         USERDIR "/" USERCONFIGDIR
@@ -118,15 +117,25 @@ const char *printtoken(int token);
 void senderrmsgstderr(const char *errmsg);
 void disconnect(int exitstatus) NONRETURNING;
 
+void always_dumpparameter(const char *parm, char **values);
+void always_dumpexecsettings(void);
+
 void debug_dumprequest(pid_t mypid);
 void debug_dumpexecsettings(void);
 void debug_dumpparameter(const char *parm, char **values);
 pid_t nondebug_fork(void);
 const char *nondebug_serviceuserdir(const char *ifnondebug);
 
+typedef void builtinserviceexec_fnt(const char *const *args);
+builtinserviceexec_fnt NONRETURNING bisexec_environment, bisexec_parameter;
+builtinserviceexec_fnt NONRETURNING bisexec_version;
+builtinserviceexec_fnt NONRETURNING bisexec_toplevel, bisexec_override, bisexec_reset;
+builtinserviceexec_fnt NONRETURNING bisexec_execute;
+
 void execservice(const int synchsocket[], int clientfd) NONRETURNING;
 void servicerequest(int sfd) NONRETURNING;
 int synchread(int fd, int ch);
+const char *defaultpath(void);
 
 struct fdstate {
   int iswrite, realfd, holdfd;
@@ -137,22 +146,25 @@ struct fdstate {
   /* tokv_word_read, tokv_word_write */
 };
 
-extern char **argarray;
-extern char *((*defvararray)[2]);
+struct keyvaluepair { char *key, *value; };
+
+extern struct request_msg request_mbuf;
+extern struct keyvaluepair *defvararray;
 extern struct fdstate *fdarray; /* indexed by nominal fd */
 extern int fdarraysize, fdarrayused;
 extern int restfdwantstate, restfdwantrw;
-extern struct request_msg request_mbuf;
+extern int service_ngids;
+extern char **argarray;
 extern char *serviceuser, *service, *logname, *cwd;
 extern char *overridedata, *userrcfile;
 extern char *serviceuser_dir, *serviceuser_shell, *callinguser_shell;
-extern int service_ngids;
 extern gid_t *calling_gids, *service_gids;
-extern const char **calling_groups, **service_groups;
 extern uid_t serviceuser_uid;
+extern const char **calling_groups, **service_groups;
 extern char *execpath, **execargs;
 extern int execute; /* One of the execution modes tokt_execmode */
 extern int setenvironment, suppressargs, disconnecthup;
+extern builtinserviceexec_fnt *execbuiltin;
 extern int syslogopenfacility;
 
 #endif
diff --git a/debug.c b/debug.c
index 9174d8337c61bf018048a815204a77a694190546..19713af4bcdf028b34ecfd488e63bfeb53de0fb2 100644 (file)
--- a/debug.c
+++ b/debug.c
 #include "lib.h"
 #include "tokens.h"
 
-#ifdef DEBUG
-
-static const char *sl_ident= "UNSET";
-static int sl_option=0, sl_facility=0;
-
-void openlog(const char *ident, int option, int facility) {
-  sl_ident= ident;
-  sl_option= option;
-  sl_facility= facility;
-}
-
-void syslog(int priority, const char *fmt, ...) {
-  va_list al;
-  fprintf(stderr,"syslog: %s<%d.%d>(%d): ",sl_ident,sl_facility,priority,sl_option);
-  va_start(al,fmt);
-  vfprintf(stderr,fmt,al);
-  va_end(al);
-  fputc('\n',stderr);
-}
-
-void closelog(void) {
-  sl_ident= "CLOSED";
-  sl_option= sl_facility= 0;
-}
-
 static void fdwantdumprwhead(int *donehead, const char *whichstr, const char *rwstr) {
   if (*donehead) return;
   printf("fds %s%s%s:",whichstr,rwstr?" ":"",rwstr?rwstr:"");
@@ -96,6 +71,69 @@ static void truefalsedump(const char *whichstr, int val) {
   printf("%s: %s\n",whichstr,val?"yes":"no");
 }
 
+void always_dumpparameter(const char *parm, char **values) {
+  printf("config parameter `%s':",parm);
+  while (*values) printf(" `%s'",*values++);
+  printf("\n");
+}
+
+void always_dumpexecsettings(void) {
+  char **cpp;
+  
+  if (userrcfile) printf("user-rcfile: `%s'\n",userrcfile);
+  else printf("user-rcfile: <none>\n");
+  fdwantdump("required",tokv_word_requirefd,"ERROR");
+  fdwantdump("allowed",tokv_word_allowfd,"either");
+  fdwantdump("ignored",tokv_word_ignorefd,0);
+  fdwantdump("null",tokv_word_nullfd,"both");
+  fdwantdump("rejected",tokv_word_rejectfd,0);
+  fputs("execute: ",stdout);
+  switch (execute) {
+  case tokv_word_reject: printf("reject"); break;
+  case tokv_word_execute: printf("`%s'",execpath); break;
+  case tokv_word_executefromdirectory: printf("from directory, `%s'",execpath); break;
+  case tokv_word_executefrompath: printf("from path"); break;
+  case tokv_word_executebuiltin: printf("builtin %s",execpath); break;
+  default: abort();
+  }
+  if (execargs) {
+    fputs("\n" "no exec arguments\n",stdout);
+  } else {
+    fputs("\n" "exec arguments:",stdout);
+    for (cpp= execargs; cpp; cpp++) printf(" `%s'",*cpp);
+    putchar('\n');
+  }
+  truefalsedump("set-environment",setenvironment);
+  truefalsedump("suppress-args",suppressargs);
+  truefalsedump("disconnect-hup",disconnecthup);
+  truefalsedump("set-environment",setenvironment);
+}
+
+#ifdef DEBUG
+
+static const char *sl_ident= "UNSET";
+static int sl_option=0, sl_facility=0;
+
+void openlog(const char *ident, int option, int facility) {
+  sl_ident= ident;
+  sl_option= option;
+  sl_facility= facility;
+}
+
+void syslog(int priority, const char *fmt, ...) {
+  va_list al;
+  fprintf(stderr,"syslog: %s<%d.%d>(%d): ",sl_ident,sl_facility,priority,sl_option);
+  va_start(al,fmt);
+  vfprintf(stderr,fmt,al);
+  va_end(al);
+  fputc('\n',stderr);
+}
+
+void closelog(void) {
+  sl_ident= "CLOSED";
+  sl_option= sl_facility= 0;
+}
+
 static void groupsdump(int ngids, const gid_t *gids, const char *const *groups) {
   int i;
   
@@ -136,39 +174,18 @@ void debug_dumprequest(pid_t mypid) {
   for (i=0; i<request_mbuf.nargs; i++) printf(" `%s'",argarray[i]);
   printf("\n" "variables:");
   for (i=0; i<request_mbuf.nvars; i++)
-    printf(" `%s'=`%s'",defvararray[i][0],defvararray[i][1]);
+    printf(" `%s'=`%s'",defvararray[i].key,defvararray[i].value);
   printf("\n");
   if (getenv("USERVD_SLEEP")) sleep(atoi(getenv("USERVD_SLEEP")));
 }
 
 void debug_dumpexecsettings(void) {
   printf("configuration parsed\n");
-  if (userrcfile) printf("user-rcfile: `%s'\n",userrcfile);
-  else printf("user-rcfile: <none>\n");
-  fdwantdump("required",tokv_word_requirefd,"ERROR");
-  fdwantdump("allowed",tokv_word_allowfd,"either");
-  fdwantdump("ignored",tokv_word_ignorefd,0);
-  fdwantdump("null",tokv_word_nullfd,"both");
-  fdwantdump("rejected",tokv_word_rejectfd,0);
-  printf("execute: ");
-  switch (execute) {
-  case tokv_word_reject: printf("reject"); break;
-  case tokv_word_execute: printf("`%s'",execpath); break;
-  case tokv_word_executefromdirectory: printf("from directory, `%s'",execpath); break;
-  case tokv_word_executefrompath: printf("from path"); break;
-  default: abort();
-  }
-  printf("\n");
-  truefalsedump("set-environment",setenvironment);
-  truefalsedump("suppress-args",suppressargs);
-  truefalsedump("disconnect-hup",disconnecthup);
-  truefalsedump("set-environment",setenvironment);
+  always_dumpexecsettings();
 }
 
 void debug_dumpparameter(const char *parm, char **values) {
-  printf("config parameter `%s':",parm);
-  while (*values) printf(" `%s'",*values++);
-  printf("\n");
+  always_dumpparameter(parm,values);
 }
 
 static int groupsallin(int na, const gid_t *lista,
index 09b4a6120fc6300364a0d3daf146d72c539c621d..8857c2f22cee20f0c6c26fccd539bfa0bf6840dd 100644 (file)
@@ -66,6 +66,7 @@ autovaldeftype(`readwrite')
 autovaldeftype(`string')
 autovaldeftype(`execmode')
 autovaldeftype(`ehandlemode')
+autovaldeftype(`builtinservice')
 autovaldeftype(`misc')
 autovaldeftype(`internal')
 
@@ -85,10 +86,12 @@ define(`isflagpair',`isdirectivefn(`$1',`dfg_setflag',`',
                       `lr_flag= &'makename(`$1')`; lr_flagval= 1; ')
                      isdirectivefn(`no-$1',`dfg_setflag',`',
                       `lr_flag= &'makename(`$1')`; lr_flagval= 0; ')')
+dnl `reset' is also a builtin service
 isexecmode(`reject')
-isexecmode(`execute')
+dnl `execute' is also a builtin service
 isexecmode(`execute-from-directory')
 isexecmode(`execute-from-path')
+isexecmode(`execute-builtin')
 isehandlemode(`errors-to-stderr')
 isehandlemode(`errors-to-syslog')
 isehandlemode(`errors-to-file')
@@ -101,7 +104,6 @@ isflagpair(`set-environment')
 isflagpair(`suppress-args')
 isflagpair(`disconnect-hup')
 isdirective(`cd')
-isdirective(`reset')
 isdirective(`user-rcfile')
 isdirective(`include')
 isdirectivefn(`include-ifexist',`df_include')
@@ -144,6 +146,22 @@ isparmcondition(`glob')
 isparmcondition(`range')
 isparmcondition(`grep')
 
+dnl builtin services
+define(`isbuiltinservice',`wordtypelexexec(`$1',`tokt_builtinservice',
+    `lr_bispa= bispa_'makename(`$2')`; lr_bisexec= bisexec_'makename(`$1')`; ')')
+isbuiltinservice(`environment',`none')
+isbuiltinservice(`parameter',`parameter')
+isbuiltinservice(`version',`none')
+isbuiltinservice(`toplevel',`none')
+isbuiltinservice(`override',`none')
+
+dnl builtin services that are also directive names
+define(`isdirectivebuiltinservice',
+       `wordtypelexexec(`$1',`tokt_directive|tokt_builtinservice$3',
+ `lr_dir= df_'makename(`$1')`; lr_bispa= bispa_'makename(`$2')`; lr_bisexec= bisexec_'makename(`$1')`; ')')
+isdirectivebuiltinservice(`reset',`none')
+isdirectivebuiltinservice(`execute',`none',`|tokt_execmode')
+
 dnl parameters
 define(`isparameter',`wordtypelexexec(`$1',`tokt_parameter',
                         `lr_parameter= pf_'makename(`$1')`; ')')
index 0337686c72827a52541d05fdfa6d320262423b23..2f360606017eb44ba2695832c90e79015f3d2f3e 100644 (file)
@@ -46,7 +46,7 @@ include(language.i4)
 
 typedef int directive_fnt(int dtoken);
 static directive_fnt df_reject, df_execute, df_executefrompath;
-static directive_fnt df_executefromdirectory;
+static directive_fnt df_executefromdirectory, df_executebuiltin;
 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;
@@ -81,11 +81,23 @@ static parameter_fnt pf_callingusershell, pf_serviceusershell;
  * freeparm can be used to free such an array.
  */
 
+typedef int builtinserviceparse_fnt(char ***rnewargs);
+static builtinserviceparse_fnt bispa_none, bispa_parameter;
+/* These parse the arguments to a builtin service, including the
+ * newline at the end of the line.  *rnewargs will initially be
+ * null, indicating that no arguments are to be set; the function
+ * may store a mallocd array of mallocd strings in it,
+ * containing the arguments it wishes to have set (null-pointer
+ * terminated).
+ */
+
 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 builtinserviceparse_fnt *lr_bispa;
+static builtinserviceexec_fnt *lr_bisexec;
 static parameter_fnt *lr_parameter;
 static int lr_loglevel, lr_logfacility, lr_min, lr_max, *lr_flag;
 static int lr_flagval, lr_controlend;
index 0f8e1956a12a73f6c99e21caa02c5fb46fc4c743..4b55743c1791d99a2ffab60f8bda6e43e662b5d1 100644 (file)
--- a/parser.c
+++ b/parser.c
  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
-static directive_fnt *lr_dir=0;
-static parmcondition_fnt *lr_parmcond=0;
-static parameter_fnt *lr_parameter=0;
-static int lr_loglevel, lr_logfacility, lr_min, lr_max, *lr_flag;
-static int lr_flagval, lr_controlend;
-static int lr_fdwant_readwrite;
-
 static void useless(void) { (void)yyunput; (void)useless; /* to shut up GCC ! */ }
 
 static void closeerrorfile(void) {
@@ -303,30 +296,39 @@ static int paa_1path(const char **rv) {
   *rv= string2path(cp); return 0;
 }
 
-static int pa_parameter(char ***rvalues) {
+static int pa_parameter(char ***rvalues, char **rname) {
   /* Scans a single parameter token and calls the appropriate parameter
    * function, returning tokv_error or 0 just like the parameter function.
+   * If rname is non-null then the name of the parameter (malloc'd) will
+   * be stored in it.
    */
   int token, r, i;
+  char *name;
 
   token= yylex(); if (token == tokv_error) return token;
+  name= xstrsave(yytext);
   if ((token & tokm_repres) != tokr_nonstring &&
       !memcmp(yytext,"u-",2) && strlen(yytext)>=3) {
     for (i=0;
-        i<request_mbuf.nvars && strcmp(yytext+2,defvararray[i][0]);
+        i<request_mbuf.nvars && strcmp(yytext+2,defvararray[i].key);
         i++);
     if (i>=request_mbuf.nvars) {
       *rvalues= xmalloc(sizeof(char*));
       **rvalues= 0;
     } else {
-      parm_1string(rvalues,defvararray[i][1]);
+      parm_1string(rvalues,defvararray[i].value);
     }
   } else {
-    if (!(token & tokt_parameter)) return unexpected(token,-1,"parameter name");
+    if (!(token & tokt_parameter)) {
+      free(name);
+      return unexpected(token,-1,"parameter name");
+    }
     r= (lr_parameter)(token,rvalues);
-    if (r) return r;
+    if (r) { free(name); return r; }
   }
-  debug_dumpparameter(yytext,*rvalues);
+  debug_dumpparameter(name,*rvalues);
+  if (rname) *rname= name;
+  else free(name);
   return 0;
 }
 
@@ -372,7 +374,7 @@ static int pa_condition(int *rtrue) {
     *rtrue= actrue; return 0;
   } else if (token & tokt_parmcondition) {
     r= pa_mwsp(); if (r) return r;
-    r= pa_parameter(&parmvalues); if (r) return r;
+    r= pa_parameter(&parmvalues,0); if (r) return r;
     r= (lr_parmcond)(token,parmvalues,rtrue); freecharparray(parmvalues);
     return r;
   }
@@ -684,26 +686,6 @@ int pf_serviceusershell(int ptoken, char ***rvalues) {
 
 /* Directive functions and associated `common code' functions */
 
-int df_reject(int dtoken) {
-  int r;
-  
-  r= pa_mnl(); if (r) return r;
-  execute= tokv_word_reject;
-  free(execpath); execpath= 0;
-  freecharparray(execargs); execargs= 0;
-  return 0;
-}
-
-int df_executefrompath(int dtoken) {
-  int r;
-  
-  r= pa_mnl(); if (r) return r;
-  execute= tokv_word_executefrompath;
-  free(execpath); execpath= 0;
-  freecharparray(execargs); execargs= 0;
-  return 0;
-}
-
 static int paa_pathargs(const char **rv, char ***newargs_r) {
   /* Repeated calls do _not_ overwrite newargs_r; caller must free.
    * Repeated calls _do_ overwrite string returned in rv.
@@ -742,15 +724,41 @@ error:
   return r;
 }
 
+static void execreset(void) {
+  execute= 0;
+  execbuiltin= 0;
+  free(execpath); execpath= 0;
+  freecharparray(execargs); execargs= 0;
+}
+
+int df_reject(int dtoken) {
+  int r;
+  
+  r= pa_mnl(); if (r) return r;
+  execreset();
+  execute= tokv_word_reject;
+  return 0;
+}
+
+int df_executefrompath(int dtoken) {
+  int r;
+  
+  r= pa_mnl(); if (r) return r;
+  execreset();
+  execute= tokv_word_executefrompath;
+  return 0;
+}
+
 int df_execute(int dtoken) {
   const char *rv;
   char **newargs;
   int r;
 
   r= paa_pathargs(&rv,&newargs); if (r) return r;
+  execreset();
   execute= tokv_word_execute;
-  freecharparray(execargs); execargs= newargs;
-  free(execpath); execpath= xstrsave(rv);
+  execargs= newargs;
+  execpath= xstrsave(rv);
   return 0;
 }
 
@@ -792,9 +800,51 @@ int df_executefromdirectory(int dtoken) {
                  " or link to one (mode=0%o)",fn,stab.st_mode);
     free(fn); freecharparray(newargs); return tokv_error;
   }
+  execreset();
   execute= tokv_word_executefromdirectory;
-  freecharparray(execargs); execargs= newargs;
-  free(execpath); execpath= fn;
+  execargs= newargs;
+  execpath= fn;
+  return 0;
+}
+
+static int bispa_none(char ***rnewargs) {
+  return pa_mnl();
+}
+
+static int bispa_parameter(char ***rnewargs) {
+  int r, i;
+  char **parmvalues, *name, **newargs;
+  
+  r= pa_mwsp(); if (r) return r;
+  r= pa_parameter(&parmvalues,&name); if (r) return r;
+  for (i=0; parmvalues[i]; i++);
+  newargs= xmalloc(sizeof(char*)*(i+2));
+  newargs[0]= name;
+  memcpy(newargs+1,parmvalues,sizeof(char*)*(i+1));
+  free(parmvalues);
+  r= pa_mnl(); if (r) { free(newargs); return r; }
+  *rnewargs= newargs;
+  return 0;
+}
+
+int df_executebuiltin(int dtoken) {
+  int r;
+  builtinserviceexec_fnt *bisexec;
+  char *newpath, **newargs;
+
+  r= pa_mwsp(); if (r) return r;
+  r= yylex(); if (r == tokv_error) return r;
+  if (!(r & tokt_builtinservice)) return unexpected(r,-1,"builtin service name");
+  bisexec= lr_bisexec;
+  newpath= xstrsave(yytext);
+  newargs= 0;
+  r= lr_bispa(&newargs); if (r) { free(newpath); return r; }
+
+  execreset();
+  execute= tokv_word_executebuiltin;
+  execbuiltin= bisexec;
+  execpath= newpath;
+  execargs= newargs;
   return 0;
 }
 
@@ -1009,7 +1059,7 @@ int df_includelookup(int dtoken) {
   int r, done, thisdone, cpl, c;
 
   r= pa_mwsp(); if (r) return r;
-  r= pa_parameter(&parmvalues); if (r) return r;
+  r= pa_parameter(&parmvalues,0); if (r) return r;
   r= paa_1path(&cp); if (r) { freecharparray(parmvalues); return r; }
   if (stat(cp,&stab)) {
     parseerrprint("unable to access directory `%s': %s",cp,strerror(errno));
index 8d20ce0fe801ce655d1ad99d562c77451ff8289d..109c5010dfd6deacd70e3fb68a2074f81890943e 100644 (file)
--- a/process.c
+++ b/process.c
 
 /* NB: defaults for the execution state are not set here, but in
  * the RESET_CONFIGURATION #define in daemon.h. */
-char **argarray;
-char *((*defvararray)[2]);
+struct request_msg request_mbuf;
+struct keyvaluepair *defvararray;
 struct fdstate *fdarray;
 int fdarraysize, fdarrayused;
 int restfdwantstate= tokv_word_rejectfd, restfdwantrw;
-struct request_msg request_mbuf;
+int service_ngids;
+char **argarray;
 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;
+const char **calling_groups, **service_groups;
 char *execpath, **execargs;
 int execute;
 int setenvironment, suppressargs, disconnecthup;
+builtinserviceexec_fnt *execbuiltin;
 int syslogopenfacility=-1;
 
 static FILE *swfile, *srfile;
@@ -104,6 +105,10 @@ int synchread(int fd, int ch) {
   return 0;
 }
 
+const char *defaultpath(void) {
+  return serviceuser_uid ? DEFAULTPATH_USER : DEFAULTPATH_ROOT;
+}
+
 /* General-purpose functions; these do nothing special about signals */
 
 static void blocksignals(void) {
@@ -408,7 +413,7 @@ static void send_opening(void) {
 }
 
 static void receive_request(void) {
-  int i,j, fd;
+  int i, fd;
   unsigned long ul;
 
   xfread(&request_mbuf,sizeof(request_mbuf));
@@ -439,36 +444,41 @@ static void receive_request(void) {
     fdarray[fd].iswrite= (i>=request_mbuf.nreadfds);
   }
 
-  assert(request_mbuf.nargs <= MAX_ARGSDEFVARS);
+  assert(request_mbuf.nargs <= MAX_ARGSDEFVAR);
   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();
+  assert(request_mbuf.nvars <= MAX_ARGSDEFVAR);
+  defvararray= xmalloc(sizeof(struct keyvaluepair)*request_mbuf.nvars);
+  for (i=0; i<request_mbuf.nvars; i++) {
+    defvararray[i].key= xfreadstring();
+    assert(defvararray[i].key[0]);
+    defvararray[i].value= xfreadstring();
+  }
   xfread(&ul,sizeof(ul));
   assert(ul == REQUEST_END_MAGIC);
 }
 
 static void establish_pipes(void) {
   int fd, tempfd;
-  char pipepathbuf[PIPEMAXLEN];
+  char pipepathbuf[PIPEMAXLEN+2];
   
   for (fd=0; fd<fdarrayused; fd++) {
     if (fdarray[fd].iswrite == -1) continue;
-    snyprintf(pipepathbuf,sizeof(pipepathbuf), PIPEFORMAT,
+    pipepathbuf[sizeof(pipepathbuf)-2]= 0;
+    snyprintf(pipepathbuf,sizeof(pipepathbuf),PIPEFORMAT,
              (unsigned long)request_mbuf.clientpid,(unsigned long)mypid,fd);
+    assert(!pipepathbuf[sizeof(pipepathbuf)-2]);
     tempfd= open(pipepathbuf,O_RDWR);
-    if (tempfd == -1) syscallerror("prelim open pipe");
-    if (!fdarray[fd].iswrite) {
-      fdarray[fd].holdfd= open(pipepathbuf, O_WRONLY);
-      if (fdarray[fd].holdfd == -1) syscallerror("hold open pipe");
-      fdarray[fd].realfd= open(pipepathbuf, O_RDONLY);
-    } else {
+    if (tempfd<0) syscallerror("prelim open pipe");
+    if (fdarray[fd].iswrite) {
       fdarray[fd].holdfd= -1;
       fdarray[fd].realfd= open(pipepathbuf, O_WRONLY);
+    } else {
+      fdarray[fd].holdfd= open(pipepathbuf, O_WRONLY);
+      if (fdarray[fd].holdfd<0) syscallerror("hold open pipe");
+      fdarray[fd].realfd= open(pipepathbuf, O_RDONLY);
     }
-    if (fdarray[fd].realfd == -1) syscallerror("real open pipe");
+    if (fdarray[fd].realfd<0) syscallerror("real open pipe");
     if (unlink(pipepathbuf)) syscallerror("unlink pipe");
     if (close(tempfd)) syscallerror("close prelim fd onto pipe");
   }
@@ -488,6 +498,7 @@ static void lookup_uidsgids(void) {
   serviceuser_dir= xstrsave(nondebug_serviceuserdir(pw->pw_dir));
   serviceuser_shell= xstrsave(pw->pw_shell);
   serviceuser_uid= pw->pw_uid;
+  
   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");
@@ -523,6 +534,8 @@ static void check_find_executable(void) {
     r= stat(execpath,&stab);
     if (r) syscallfailure("checking for executable in directory, `%s'",execpath);
     break;
+  case tokv_word_executebuiltin:
+    break;
   case tokv_word_executefrompath:
     if (strchr(service,'/')) {
       r= stat(service,&stab);
@@ -531,11 +544,11 @@ static void check_find_executable(void) {
       execpath= service;
     } else {
       string= getenv("PATH");
-      if (!string) failure("execute-from-path, but daemon inherited no PATH !");
+      if (!string) string= defaultpath();
       while (string) {
        delim= strchr(string,':');
        if (delim) {
-         if (delim-string > INT_MAX)
+         if (delim-string > MAX_GENERAL_STRING)
            failure("execute-from-path, but PATH component too long");
          partsize= delim-string;
          nextstring= delim+1;
@@ -586,7 +599,7 @@ static void check_fds(void) {
       fdarray[fd].realfd= open("/dev/null",
                               fdarray[fd].iswrite == -1 ? O_RDWR :
                               fdarray[fd].iswrite ? O_WRONLY : O_RDONLY);
-      if (fdarray[fd].realfd == -1)
+      if (fdarray[fd].realfd<0)
        syscallfailure("cannot open /dev/null for null fd");
       break;
     case tokv_word_requirefd:
@@ -597,7 +610,7 @@ static void check_fds(void) {
       if (fdarray[fd].realfd == -1) {
        fdarray[fd].iswrite= (fdarray[fd].wantrw == tokv_word_write);
        fdarray[fd].realfd= open("/dev/null",fdarray[fd].iswrite ? O_WRONLY : O_RDONLY);
-       if (fdarray[fd].realfd == -1)
+       if (fdarray[fd].realfd<0)
          syscallfailure("cannot open /dev/null for allowed but not provided fd");
       } else {
        if (fdarray[fd].iswrite) {
@@ -675,7 +688,7 @@ void servicerequest(int sfd) {
                    "<builtin toplevel configuration>",1);
   
   ensurelogopen(USERVD_LOGFACILITY);
-  if (r == tokv_error) failure("error encountered while parsing configuration files");
+  if (r == tokv_error) failure("error encountered while parsing configuration");
   assert(r == tokv_quit);
 
   debug_dumpexecsettings();
index 48a6e294db691bef6fcb228279c66850367369ee..abd36089ad5296b8c1692a2a413418d6f5c88618 100644 (file)
@@ -34,6 +34,7 @@
 #include "common.h"
 #include "daemon.h"
 #include "lib.h"
+#include "version.h"
 
 static void NONRETURNING serv_syscallfail(const char *msg) {
   fputs("uservd(service): ",stderr);
@@ -41,6 +42,87 @@ static void NONRETURNING serv_syscallfail(const char *msg) {
   _exit(-1);
 }
 
+static void NONRETURNING serv_checkstdoutexit(void) {
+  if (ferror(stdout) || fclose(stdout)) serv_syscallfail("write stdout");
+  _exit(0);
+}
+
+void bisexec_environment(const char *const *argv) {
+  execlp("env","env",(char*)0);
+  serv_syscallfail("execute `env'");
+}
+
+void bisexec_parameter(const char *const *argv) {
+  always_dumpparameter(execargs[0],execargs+1);
+  serv_checkstdoutexit();
+}
+  
+void bisexec_version(const char *const *argv) {
+  const unsigned char *p;
+  int i;
+  
+  printf("uservd version " VERSION "; copyright (C)1996-1997 Ian Jackson.\n"
+#ifdef DEBUG
+        "DEBUGGING VERSION"
+#else
+        "production version"
+#endif
+        " - protocol magic number %08lx\n"
+        "protocol checksum:",
+        BASE_MAGIC);
+  for (i=0, p=protocolchecksumversion; i<sizeof(protocolchecksumversion); i++, p++)
+    printf("%02x",*p);
+  printf("\n"
+        "rendezvous socket: `" RENDEZVOUSPATH "'\n"
+        "system config dir: `" SYSTEMCONFIGDIR "'\n"
+        "pipe filename format: `%s' (max length %d)\n"
+        "maximums:    fd %-10d                general string %d"
+        "             gids %-10d              override length %d\n\n"
+        "             args or variables %-10d error message %d\n"
+        "             nested inclusion %-10d  errno string reserve %d\n",
+        PIPEFORMAT, PIPEMAXLEN,
+        MAX_ALLOW_FD, MAX_GENERAL_STRING,
+        MAX_GIDS, MAX_OVERRIDE_LEN,
+        MAX_ARGSDEFVAR, ERRMSG_RESERVE_ERRNO,
+        MAX_INCLUDE_NEST, MAX_ERRMSG_LEN);
+  serv_checkstdoutexit();
+}
+
+static void NONRETURNING dumpconfig(const char *string) {
+  int nspaces, c;
+  
+  assert(*string);
+  nspaces= 0;
+  while ((c= *string++)) {
+    switch (c) {
+    case ' ': nspaces++; break;
+    case '\n': nspaces= 0; putchar('\n'); break;
+    default:
+      while (nspaces>0) { putchar(' '); nspaces--; }
+      putchar(c);
+    }
+  }
+  assert(*--string == '\n');
+  serv_checkstdoutexit();
+}
+
+void bisexec_toplevel(const char *const *argv) {
+  dumpconfig(TOPLEVEL_CONFIGURATION);
+}
+
+void bisexec_override(const char *const *argv) {
+  dumpconfig(TOPLEVEL_OVERRIDDEN_CONFIGURATION);
+}
+
+void bisexec_reset(const char *const *argv) {
+  dumpconfig(RESET_CONFIGURATION);
+}
+
+void bisexec_execute(const char *const *argv) {
+  always_dumpexecsettings();
+  serv_checkstdoutexit();
+}
+
 static void serv_resetsignal(int signo) {
   struct sigaction sig;
   
@@ -54,10 +136,6 @@ 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; }
@@ -104,7 +182,7 @@ static const struct serv_envinfo {
   { "LOGNAME",        see_logname    },
   { "HOME",           see_home       },
   { "SHELL",          see_shell      },
-  { "PATH",           see_path       },
+  { "PATH",           defaultpath    },
   { "USERV_SERVICE",  see_service    },
   { "USERV_CWD",      see_c_cwd      },
   { "USERV_USER",     see_c_logname  },
@@ -190,10 +268,10 @@ void execservice(const int synchsocket[], int clientfd) {
   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;
+    l= strlen(defvararray[i].key)+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");
+    snyprintf(envvarbuf,l,"USERV_U_%s",defvararray[i].key);
+    if (setenv(envvarbuf,defvararray[i].value,1)) serv_syscallfail("setenv defvar");
   }
 
   nargs= 0;
@@ -209,7 +287,10 @@ void execservice(const int synchsocket[], int clientfd) {
   if (!suppressargs) for (i=0; i<request_mbuf.nargs; i++) args[targ++]= argarray[i];
   args[targ]= 0;
 
-  execv(args[0],(char* const*)args);
+  if (execbuiltin)
+    execbuiltin(args);
+  else
+    execv(args[0],(char* const*)args);
 
   serv_syscallfail("exec service program");
   _exit(-1);
index dba0322fb80d5acf49d11845365b5e5292424412..0307c864042fddf3d031b34cd8ffac51cde19211 100644 (file)
--- a/spec.sgml
+++ b/spec.sgml
@@ -54,11 +54,13 @@ each other in any unexpected ways.
 <p>
 <example>
 userv <var/options/ [--] <var/service-user/ <var/service-name/ [<var/argument/ ...]
+userv <var/options/ -B|--builtin [--] <var/builtin-service/ [<var/info-argument/ ...]
 </example>
 <p>
 
 <var/service-user/ specifies which user is to provide the service.
-The user may be a login name or a numeric uid.
+The user may be a login name or a numeric uid, or <tt/-/ to indicate
+that the service user is to be the same as the calling user.
 <p>
 
 The service name is interpreted by the userv<footnote><tt/userv/ is
@@ -74,6 +76,20 @@ and the value for such an option may appear in the same argument or in
 the next.
 
 <taglist>
+<tag/<tt/-B//
+<tag/<tt/--builtin//
+<item>
+Requests that a builtin service be provided.  This is equivalent to
+using the <tt/--override/ option to specify a string consisting of
+<tt/--execute-builtin/ followed by the <var/builtin-service/
+requested, and requesting a service user of <tt/-/ (indicating the
+calling user).  Note that the <var/info argument/s supplied will be
+ignored by most services; if the builtin service being requested
+requires a <var/service-argument/ then this must be supplied to the
+client in the same argument as the <var/builtin-service/.  See <ref
+id="execdirectives"> for details of the builtin services available,
+and <ref id="optoverride"> for details of the <tt/--override/ options.
+
 <tag/<tt/-f<var/fd/[<var/modifiers/]=<var/filename///
 <tag/<tt/--file <var/fd/[<var/modifiers/]=<var/filename///
 <item>
@@ -315,7 +331,7 @@ status method <tt/stdout/ is in use.
 
 </taglist>
 
-<sect>Security-overriding options
+<sect id="optoverride">Security-overriding options
 <p>
 
 There are also some options which are available for debugging and to
@@ -817,7 +833,7 @@ error will not be caught.
 
 </taglist>
 
-<sect1>Directives for changing execution settings
+<sect1 id="execdirectives">Directives for changing execution settings
 <p>
 
 The following directives modify the execution settings; the server
@@ -867,6 +883,51 @@ directive is <em/very dangerous/, and is only provided to make the
 It is an error for the execution to fail when it is attempted (after
 all the configuration has been parsed).
 
+<tag/<tt/execute-builtin <var/service-name/ <var/service-arguments//
+<item>
+Executes the builtin service <var/service-name/.  These builtin
+services display information about the server and/or the request, and
+ignore any arguments passed from the service side except possibly to
+print them as part of their output.  They write their results to their
+standard output (i.e., wherever file descriptor 1 is directed).  The
+builtin services are:
+
+<taglist compact>
+<tag/<tt/execute//
+Displays the execution settings, defined variables,
+arguments, etc. with which the builtin service was invoked.
+
+<tag/<tt/environment//
+Displays the environment variable settings with which the builtin
+service was invoked.
+
+<tag/<tt/parameter <var/parameter///
+Displays the values of the service configuration language parameter
+specified.
+
+<tag/<tt/version//
+Displays the version string and compilation details of the uservd
+server program.
+
+<tag/<tt/reset//
+Displays the default reset configuration (evaluated when <tt/reset/ is
+found in a configuration file, or when an error is caught by
+<tt/catch-quit/).
+
+<tag/<tt/toplevel//
+Displays the top-level default configuration (the configuration data,
+evaluated by the server, which calls all the other configuration
+files).
+
+<tag/<tt/override//
+Displays the top-level override configuration (the configuration data,
+evaluated by the server, which causes all the other configuration data
+to be parsed).
+</taglist>
+
+In the future other builtin services may be defined which do more than
+just print information.
+
 <tag/<tt/set-environment//
 <tag/<tt/no-set-environment//
 <item>