chiark / gitweb /
Internal review up to end of p11.
[userv.git] / debug.c
diff --git a/debug.c b/debug.c
new file mode 100644 (file)
index 0000000..9174d83
--- /dev/null
+++ b/debug.c
@@ -0,0 +1,226 @@
+/*
+ * userv - ddebug.c
+ * routines which are different for -DDEBUG
+ *
+ * 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 <stdarg.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <grp.h>
+#include <sys/types.h>
+
+#include "config.h"
+#include "common.h"
+#include "daemon.h"
+#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:"");
+  *donehead= 1;
+}
+
+static void fdwantdumprw(const char *whichstr, int whichval,
+                        int rw, const char *rwstr) {
+  int donehead= 0;
+  int fd;
+  
+  for (fd=0; fd<fdarrayused; fd++) {
+    if (!(fdarray[fd].wantstate == whichval && fdarray[fd].wantrw == rw)) continue;
+    fdwantdumprwhead(&donehead,whichstr,rwstr);
+    printf(" %d",fd);
+  }
+  if (restfdwantstate == whichval && restfdwantrw == rw) {
+    fdwantdumprwhead(&donehead,whichstr,rwstr);
+    printf(" %d-",fdarrayused);
+  }
+  if (donehead) printf("\n");
+}
+
+static void fdwantdump(const char *whichstr, int whichval, const char *rwunspecstr) {
+  if (rwunspecstr) {
+    fdwantdumprw(whichstr,whichval,tokv_word_read,"read");
+    fdwantdumprw(whichstr,whichval,tokv_word_write,"write");
+    fdwantdumprw(whichstr,whichval,0,rwunspecstr);
+  } else {
+    fdwantdumprw(whichstr,whichval,0,0);
+  }
+}
+
+static void truefalsedump(const char *whichstr, int val) {
+  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;
+  
+  printf("server pid: %ld\n"
+         "client pid: %ld\n"
+         "service: `%s'\n"
+         "service user: `%s'\n"
+        "service uid: %ld\n"
+         "service user shell: `%s'\n"
+         "service user dir: `%s'\n"
+        "service groups:",
+         (long)mypid, (long)request_mbuf.clientpid,
+         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");
+  printf("\n" "arguments:");
+  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("\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);
+}
+
+void debug_dumpparameter(const char *parm, char **values) {
+  printf("config parameter `%s':",parm);
+  while (*values) printf(" `%s'",*values++);
+  printf("\n");
+}
+
+static int groupsallin(int na, const gid_t *lista,
+                       int nb, const gid_t *listb) {
+  int i,j;
+  for (i=0; i<na; i++) {
+    for (j=0; j<nb && listb[j] != lista[i]; j++);
+    if (j>=nb) return 0;
+  }
+  return 1;
+}
+
+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;
+
+  realsize= getgroups(0,0); if (realsize == -1) return -1;
+  reallist= malloc(sizeof(gid_t)*realsize); if (!reallist) return -1;
+  if (getgroups(realsize,reallist) != realsize)
+    { e= errno; free(reallist); errno= e; return -1; }
+  if (!groupsallin(wantsize,wantlist,realsize,reallist))
+    { free(reallist); errno= EPERM; return -1; }
+  if (!groupsallin(realsize,reallist,wantsize,wantlist))
+    { free(reallist); errno= EINVAL; return -1; }
+  free(reallist); return 0;
+}
+
+pid_t nondebug_fork(void) { return 0; }
+const char *nondebug_serviceuserdir(const char *ifnondebug) { return SERVICEUSERDIR; }
+
+#else
+
+void debug_dumprequest(pid_t mypid) { }
+void debug_dumpexecsettings(void) { }
+void debug_dumpparameter(const char *parm, char **values) { }
+pid_t nondebug_fork(void) { return fork(); }
+const char *nondebug_serviceuserdir(const char *ifnondebug) { return ifnondebug; }
+
+#endif