[sane-devel] [PATCH 3/3] saned: reorganize flags, remove run_mode SANED_RUN_DEBUG

luizluca at gmail.com luizluca at gmail.com
Sat Jul 22 08:43:18 UTC 2017


From: Luiz Angelo Daros de Luca <luizluca at gmail.com>

Flags like -a, -d and -s have many overlap effects. This patch restricts
the effect of flags to a simple action.

New -u (user) flag replaces -a optional argument for running saned as a different user.
The code that retrieve the user info and drop privileges migrated to runas_user().
As a side effect, PID file can be created even if getting user info fails.

New -l (listen) flag sets run_mode to standalone. It can be cancelled with -i.
New -i (inetd, default) flag sets run_mode to inetd. It is useful only to cancel -l.

New -D (daemonize) flag daemonizes saned after bind. Requires -l and it can be cancelled
by -f.
New -f (foreground) flag for running saned in foreground (useful for procd).
It can be cancelled using new -D flag.

New -o (once) make saned exit after the first client disconnects.

Flag -s (syslog) now only forces output to syslog and does not accept arguments. It can be
cancelled using -e. Previous behavior can be reproduced with '-a -d level -o -f -s'.
New -e (stderr) flag for redirecting output to stderr, instead of syslog. It can be
cancelled using -s.

Flag -d (debug) now only sets the debug level and argument is required. Previous behavior
can be reproduced with '-a -d level -o -f'.

The run_mode SANED_RUN_DEBUG and SANED_RUN_ALONE shared most of its code
path. With the new flags dealing with their difference, SANED_RUN_DEBUG is gone.

Flag '-a' still works as before but it can be replaced by '-l -D -u user'.

Current uses of -d (debug) or -s (syslog) will break.

Signed-off-by: Luiz Angelo Daros de Luca <luizluca at gmail.com>
---
 doc/saned.man    |  78 ++++++++------
 frontend/saned.c | 306 +++++++++++++++++++++++++++++++------------------------
 2 files changed, 220 insertions(+), 164 deletions(-)

diff --git a/doc/saned.man b/doc/saned.man
index 8542d254..0bddc58d 100644
--- a/doc/saned.man
+++ b/doc/saned.man
@@ -6,15 +6,21 @@ saned \- SANE network daemon
 .B saned
 .B [ \-a
 .I [ username ]
+.B ]
+.B [ \-u
+.I username
+.B ]
 .B [ \-b
 .I address
 .B ]
-.B | \-d
-.I [ n ]
-.B | \-s
-.I [ n ]
-.B | \-h
+.B [ \-l | \-i ]
+.B [ \-D | \-f ]
+.B [ \-o ]
+.B [ \-d
+.I n
 .B ]
+.B [ \-s | \-e ]
+.B [ \-h ]
 .SH DESCRIPTION
 .B saned
 is the SANE (Scanner Access Now Easy) daemon that allows remote clients
@@ -22,51 +28,63 @@ to access image acquisition devices available on the local host.
 .SH OPTIONS
 .PP
 The
-.B \-a
+.B \-l
 flag requests that
 .B saned
 run in standalone daemon mode. In this mode,
 .B saned
-will detach from the console and run in the background, listening for incoming
-client connections;
+will listening for incoming client connections;
 .B inetd
 is not required for
 .B saned
-operations in this mode. If the optional
-.B username
-is given after
-.B \-a
-,
+operations in this mode. The
+.B \-b
+flag can control which address
 .B saned
-will drop root privileges and run as this user (and group).
+will bind. The
+.B \-u
+.I username
+flag requests that
+.B saned
+drop root privileges and run as this user (and group) after bind.
+The
+.B \-B
+flag will request
+.B saned
+to detach from the console and run in the background, while 
+.B \-f
+flag will keep it attached to the console and running foreground.
+The flag
+.B \-a
+is equals to
+.B \-l \-B \-u
+.I username
+.
+.PP
+The
+.B \-e
+flag will request that 
+.B saned
+output to stderr while the
+.B \-s
+flag forces the output to syslog.
 .PP
 The
 .B \-d
-and
-.B \-s
-flags request that
+flag sets the debug level of
 .B saned
-run in debug mode (as opposed to
-.BR inetd (8)
-daemon mode).  In this mode,
-.B saned
-explicitly waits for a connection request.  When compiled with
-debugging enabled, these flags may be followed by a number to request
+. When compiled with debugging enabled, these flags may be followed by a number to request
 debug info. The larger the number, the more verbose the debug output.
 E.g.,
 .B \-d128
 will request printing of all debug info. Debug level 0 means no debug output
-at all. The default value is 2. If flag
-.B \-d
-is used, the debug messages will be printed to stderr while
-.B \-s
-requests using syslog.
+at all. The default value is 2.
 .PP
 The
-.B \-b
+.B \-o
 flag requests that
 .B saned
-bind to a specific address.
+exits after the first client disconnects. Useful for debugging.
 .PP
 If
 .B saned
diff --git a/frontend/saned.c b/frontend/saned.c
index 6b97e914..93afd612 100644
--- a/frontend/saned.c
+++ b/frontend/saned.c
@@ -251,6 +251,8 @@ static Wire wire;
 static int num_handles;
 static int debug;
 static int run_mode;
+static int run_foreground;
+static int run_once;
 static Handle *handle;
 static char *bind_addr;
 static union
@@ -298,9 +300,10 @@ static SANE_Bool log_to_syslog = SANE_TRUE;
 static int process_request (Wire * w);
 
 #define SANED_RUN_INETD  0
-#define SANED_RUN_DEBUG  1
-#define SANED_RUN_ALONE  2
+#define SANED_RUN_ALONE  1
 
+#define SANED_EXEC_FOREGROUND 0
+#define SANED_EXEC_BACKGROUND 1
 
 #define DBG_ERR  1
 #define DBG_WARN 2
@@ -2964,6 +2967,114 @@ do_bindings (int *nfds, struct pollfd **fds)
 
 
 static void
+runas_user (char *user)
+{
+  uid_t runas_uid = 0;
+  gid_t runas_gid = 0;
+  struct passwd *pwent;
+  gid_t *grplist = NULL;
+  struct group *grp;
+  int ngroups = 0;
+  int ret;
+
+  pwent = getpwnam(user);
+
+  if (pwent == NULL)
+    {
+      DBG (DBG_ERR, "FATAL ERROR: user %s not found on system\n", user);
+      bail_out (1);
+    }
+
+  runas_uid = pwent->pw_uid;
+  runas_gid = pwent->pw_gid;
+
+  /* Get group list for runas_uid */
+  grplist = (gid_t *)malloc(sizeof(gid_t));
+
+  if (grplist == NULL)
+    {
+      DBG (DBG_ERR, "FATAL ERROR: cannot allocate memory for group list\n");
+
+      exit (1);
+    }
+
+  ngroups = 1;
+  grplist[0] = runas_gid;
+
+  setgrent();
+  while ((grp = getgrent()) != NULL)
+    {
+      int i = 0;
+
+      /* Already added current group */
+      if (grp->gr_gid == runas_gid)
+	continue;
+
+      while (grp->gr_mem[i])
+	{
+	  if (strcmp(grp->gr_mem[i], user) == 0)
+	    {
+	      int need_to_add = 1, j;
+
+	      /* Make sure its not already in list */
+	      for (j = 0; j < ngroups; j++)
+		{
+		  if (grp->gr_gid == grplist[i])
+		    need_to_add = 0;
+		}
+	      if (need_to_add)
+		{
+		  grplist = (gid_t *)realloc(grplist,
+					     sizeof(gid_t)*ngroups+1);
+		  if (grplist == NULL)
+		    {
+		      DBG (DBG_ERR, "FATAL ERROR: cannot reallocate memory for group list\n");
+
+		      exit (1);
+		    }
+		  grplist[ngroups++] = grp->gr_gid;
+		}
+	    }
+	  i++;
+	}
+    }
+  endgrent();
+
+  /* Drop privileges if requested */
+  if (runas_uid > 0)
+    {
+      ret = setgroups(ngroups, grplist);
+      if (ret < 0)
+	{
+	  DBG (DBG_ERR, "FATAL ERROR: could not set group list: %s\n", strerror(errno));
+
+	  exit (1);
+	}
+
+      free(grplist);
+
+      ret = setegid (runas_gid);
+      if (ret < 0)
+	{
+	  DBG (DBG_ERR, "FATAL ERROR: setegid to gid %d failed: %s\n", runas_gid, strerror (errno));
+
+	  exit (1);
+	}
+
+      ret = seteuid (runas_uid);
+      if (ret < 0)
+	{
+	  DBG (DBG_ERR, "FATAL ERROR: seteuid to uid %d failed: %s\n", runas_uid, strerror (errno));
+
+	  exit (1);
+	}
+
+      DBG (DBG_WARN, "Dropped privileges to uid %d gid %d\n", runas_uid, runas_gid);
+    }
+}
+
+
+static void
 run_standalone (char *user)
 {
   struct pollfd *fds = NULL;
@@ -2973,84 +3084,12 @@ run_standalone (char *user)
   int i;
   int ret;
 
-  uid_t runas_uid = 0;
-  gid_t runas_gid = 0;
-  struct passwd *pwent;
-  gid_t *grplist = NULL;
-  struct group *grp;
-  int ngroups = 0;
   FILE *pidfile;
 
   do_bindings (&nfds, &fds);
 
-  if (run_mode != SANED_RUN_DEBUG)
+  if (run_foreground == SANE_FALSE)
     {
-      if (user)
-	{
-	  pwent = getpwnam(user);
-
-	  if (pwent == NULL)
-	    {
-	      DBG (DBG_ERR, "FATAL ERROR: user %s not found on system\n", user);
-	      bail_out (1);
-	    }
-
-	  runas_uid = pwent->pw_uid;
-	  runas_gid = pwent->pw_gid;
-
-	  /* Get group list for runas_uid */
-          grplist = (gid_t *)malloc(sizeof(gid_t));
-
-	  if (grplist == NULL)
-	    {
-	      DBG (DBG_ERR, "FATAL ERROR: cannot allocate memory for group list\n");
-
-	      exit (1);
-	    }
-
-          ngroups = 1;
-          grplist[0] = runas_gid;
-
-          setgrent();
-          while ((grp = getgrent()) != NULL)
-	    {
-              int i = 0;
-
-              /* Already added current group */
-              if (grp->gr_gid == runas_gid)
-                continue;
-
-              while (grp->gr_mem[i])
-		{
-                  if (strcmp(grp->gr_mem[i], user) == 0)
-                    {
-                      int need_to_add = 1, j;
-
-                      /* Make sure its not already in list */
-                      for (j = 0; j < ngroups; j++)
-                        {
-                          if (grp->gr_gid == grplist[i])
-                            need_to_add = 0;
-			}
-                      if (need_to_add)
-                        {
-                          grplist = (gid_t *)realloc(grplist,
-                                                     sizeof(gid_t)*ngroups+1);
-                          if (grplist == NULL)
-			    {
-			      DBG (DBG_ERR, "FATAL ERROR: cannot reallocate memory for group list\n");
-
-			      exit (1);
-			    }
-                          grplist[ngroups++] = grp->gr_gid;
-                        }
-                    }
-                  i++;
-                }
-	    }
-          endgrent();
-	}
-
       DBG (DBG_MSG, "run_standalone: daemonizing now\n");
 
       fd = open ("/dev/null", O_RDWR);
@@ -3093,42 +3132,13 @@ run_standalone (char *user)
 
       setsid ();
 
-      /* Drop privileges if requested */
-      if (runas_uid > 0)
-	{
-	  ret = setgroups(ngroups, grplist);
-	  if (ret < 0)
-	    {
-	      DBG (DBG_ERR, "FATAL ERROR: could not set group list: %s\n", strerror(errno));
-
-	      exit (1);
-	    }
-
-	  free(grplist);
-
-	  ret = setegid (runas_gid);
-	  if (ret < 0)
-	    {
-	      DBG (DBG_ERR, "FATAL ERROR: setegid to gid %d failed: %s\n", runas_gid, strerror (errno));
-
-	      exit (1);
-	    }
-
-	  ret = seteuid (runas_uid);
-	  if (ret < 0)
-	    {
-	      DBG (DBG_ERR, "FATAL ERROR: seteuid to uid %d failed: %s\n", runas_uid, strerror (errno));
-
-	      exit (1);
-	    }
-
-	  DBG (DBG_WARN, "Dropped privileges to uid %d gid %d\n", runas_uid, runas_gid);
-	}
-
       signal(SIGINT, sig_int_term_handler);
       signal(SIGTERM, sig_int_term_handler);
     }
 
+  if (user)
+    runas_user(user);
+
 #ifdef WITH_AVAHI
   DBG (DBG_INFO, "run_standalone: spawning Avahi process\n");
   saned_avahi (fds, nfds);
@@ -3187,13 +3197,13 @@ run_standalone (char *user)
 	      continue;
 	    }
 
-	  if (run_mode == SANED_RUN_DEBUG)
-	    break; /* We have the only connection we're going to handle */
-	  else
-	    handle_client (fd);
+	  handle_client (fd);
+
+	  if (run_once == SANE_TRUE)
+	    break; /* We have handled the only connection we're going to handle */
 	}
 
-      if (run_mode == SANED_RUN_DEBUG)
+      if (run_once == SANE_TRUE)
 	break;
     }
 
@@ -3201,14 +3211,6 @@ run_standalone (char *user)
     close (fdp->fd);
 
   free (fds);
-
-  if (run_mode == SANED_RUN_DEBUG)
-    {
-      if (fd > 0)
-	handle_connection (fd);
-
-      bail_out(0);
-    }
 }
 
 
@@ -3299,12 +3301,17 @@ static void usage(char *me, int err)
   fprintf (stderr,
        "Usage: %s [OPTIONS]\n\n"
        " Options:\n\n"
-       "  -a, --alone[=user]	run standalone and fork in background as `user'\n"
-       "  -d, --debug[=level]	run foreground with output to stderr\n"
-       "			and debug level `level' (default is 2)\n"
-       "  -s, --syslog[=level]	run foreground with output to syslog\n"
-       "			and debug level `level' (default is 2)\n"
-       "  -b, --bind=addr	bind address `addr'\n"
+       "  -a, --alone[=user]	equals to `-l -D -u user'\n"
+       "  -l, --listen		run in standalone mode (listen for connection)\n"
+       "  -i, --inetd		run in inetd mode (default)\n"
+       "  -u, --user=user	run as `user'\n"
+       "  -D, --daemonize	run in background\n"
+       "  -f, --foreground	run in foreground (default)\n"
+       "  -o, --once		exit after first client disconnects\n"
+       "  -d, --debug=level	set debug level `level' (default is 2)\n"
+       "  -s, --syslog		output to syslog (default)\n"
+       "  -e, --stderr		output to stderr\n"
+       "  -b, --bind=addr	bind address `addr' (default all interfaces)\n"
        "  -h, --help		show this help message and exit\n", me);
 
   exit(err);
@@ -3317,8 +3324,15 @@ static struct option long_options[] =
 /* These options set a flag. */
   {"help",	no_argument,		0, 'h'},
   {"alone",	optional_argument,	0, 'a'},
-  {"debug",	optional_argument,	0, 'd'},
-  {"syslog",	optional_argument,	0, 's'},
+  {"listen",	no_argument,		0, 'l'},
+  {"inetd",	no_argument,		0, 'i'},
+  {"user",	required_argument,	0, 'u'},
+  {"daemonize", no_argument,		0, 'D'},
+  {"foreground",no_argument,		0, 'f'},
+  {"once",	no_argument,		0, 'o'},
+  {"debug",	required_argument,	0, 'd'},
+  {"syslog",	no_argument,		0, 's'},
+  {"stderr",	no_argument,		0, 'e'},
   {"bind",	required_argument,	0, 'b'},
   {0,		0,			0,  0 }
 };
@@ -3342,20 +3356,44 @@ main (int argc, char *argv[])
 
   numchildren = 0;
   run_mode = SANED_RUN_INETD;
+  run_foreground = SANE_TRUE;
+  run_once = SANE_FALSE;
 
-  while((c = getopt_long(argc, argv,"ha::d::s::b:", long_options, &long_index )) != -1)
+  while((c = getopt_long(argc, argv,"ha::liu:Dfod:seb:", long_options, &long_index )) != -1)
     {
       switch(c) {
       case 'a':
 	run_mode = SANED_RUN_ALONE;
+	run_foreground = SANE_FALSE;
+	if (optarg)
+	  user = optarg;
+	break;
+      case 'l':
+	run_mode = SANED_RUN_ALONE;
+	break;
+      case 'i':
+	run_mode = SANED_RUN_INETD;
+	break;
+      case 'u':
 	user = optarg;
 	break;
+      case 'D':
+	run_foreground = SANE_FALSE;
+	break;
+      case 'f':
+	run_foreground = SANE_TRUE;
+        break;
+      case 'o':
+	run_once = SANE_TRUE;
+	break;
       case 'd':
-	log_to_syslog = SANE_FALSE;
+	debug = atoi(optarg);
+	break;
       case 's':
-	run_mode = SANED_RUN_DEBUG;
-	if(optarg)
-	  debug = atoi(optarg);
+	log_to_syslog = SANE_TRUE;
+	break;
+      case 'e':
+	log_to_syslog = SANE_FALSE;
 	break;
       case 'b':
 	bind_addr = optarg;
@@ -3405,7 +3443,7 @@ main (int argc, char *argv[])
       DBG (DBG_WARN, "saned from %s ready\n", PACKAGE_STRING);
     }
 
-  if ((run_mode == SANED_RUN_ALONE) || (run_mode == SANED_RUN_DEBUG))
+  if (run_mode == SANED_RUN_ALONE)
     {
       run_standalone(user);
     }
-- 
2.11.0




More information about the sane-devel mailing list