diff options
| author | Jörg Frings-Fürst <debian@jff.email> | 2025-06-09 14:27:29 +0200 | 
|---|---|---|
| committer | Jörg Frings-Fürst <debian@jff.email> | 2025-06-09 14:27:29 +0200 | 
| commit | 652efae78c00b812033ea162d76cd13bd40dcab6 (patch) | |
| tree | 7c139f4d2a28061607cd7e2269693df993e5d60a /frontend | |
| parent | bfa2ae8e43fcbab696f272fffd164d0637e965c5 (diff) | |
New upstream version 1.4.0upstream/1.4.0upstream
Diffstat (limited to 'frontend')
| -rw-r--r-- | frontend/saned.c | 395 | ||||
| -rw-r--r-- | frontend/scanimage.c | 2 | 
2 files changed, 237 insertions, 160 deletions
| diff --git a/frontend/saned.c b/frontend/saned.c index d71d428..5ca81cc 100644 --- a/frontend/saned.c +++ b/frontend/saned.c @@ -79,6 +79,9 @@  #include <arpa/inet.h>  #include <sys/wait.h> +#ifdef HAVE_PIDFD_OPEN +#include <sys/pidfd.h> +#endif  #include <pwd.h>  #include <grp.h> @@ -168,8 +171,6 @@ poll (struct pollfd *ufds, unsigned int nfds, int timeout)  # define SANED_SERVICE_DNS "_sane-port._tcp"  # define SANED_NAME "saned" -pid_t avahi_pid = -1; -  char *avahi_svc_name;  static AvahiClient *avahi_client = NULL; @@ -222,13 +223,36 @@ static AvahiEntryGroup *avahi_group = NULL;  # define PATH_MAX 1024  #endif +/* Linked list of child processes */ +enum saned_child_type { +  SANED_CHILD_CLIENT, +  SANED_CHILD_AVAHI, +};  struct saned_child { +  enum saned_child_type type;    pid_t pid; +  int pidfd;    struct saned_child *next;  };  struct saned_child *children;  int numchildren; +/* Linked list of fds to be polled */ +enum saned_fd_type { +  SANED_FD_LISTENER, +  SANED_FD_PROCESS, +}; +struct saned_fd +{ +  enum saned_fd_type type; +  int fd; +  short interesting_events; +  struct saned_fd *next; +}; +struct saned_fd *saned_fds; +int num_saned_fds; + +  #define SANED_CONFIG_FILE "saned.conf"  #define SANED_PID_FILE    "/var/run/saned.pid" @@ -254,6 +278,7 @@ static int debug;  static int run_mode;  static int run_foreground;  static int run_once; +static int allow_network;  static int data_connect_timeout = 4000;  static Handle *handle;  static char *bind_addr; @@ -1869,7 +1894,7 @@ process_request (Wire * w)  	reply.status =  	  sane_get_devices ((const SANE_Device ***) &reply.device_list, -			    SANE_TRUE); +			    !allow_network);  	sanei_w_reply (w, (WireCodecFunc) sanei_w_get_devices_reply, &reply);        }        break; @@ -2294,67 +2319,103 @@ process_request (Wire * w)    return 0;  } -  static int -wait_child (pid_t pid, int *status, int options) +add_fd (int fd, enum saned_fd_type type, short interesting_events)  { -  struct saned_child *c; -  struct saned_child *p = NULL; -  int ret; - -  ret = waitpid(pid, status, options); - -  if (ret <= 0) -    return ret; +  struct saned_fd *f; -#if WITH_AVAHI -  if ((avahi_pid > 0) && (ret == avahi_pid)) -    { -      avahi_pid = -1; -      numchildren--; -      return ret; -    } -#endif /* WITH_AVAHI */ +  f = (struct saned_fd *) malloc (sizeof(struct saned_fd)); +  if (f == NULL) +    goto fail; -  for (c = children; (c != NULL) && (c->next != NULL); p = c, c = c->next) -    { -      if (c->pid == ret) -	{ -	  if (c == children) -	    children = c->next; -	  else if (p != NULL) -	    p->next = c->next; +  f->type = type; +  f->fd = fd; +  f->interesting_events = interesting_events; +  f->next = saned_fds; -	  free(c); +  saned_fds = f; +  num_saned_fds++; -	  numchildren--; +  return fd; -	  break; -	} -    } +fail: +  DBG (DBG_ERR, "add_fd: cannot manage fd, %s\n", strerror(errno)); +  close(fd); +  return -1; +} -  return ret; +static void +close_fds(unsigned type_mask, int specific_fd) +{ +  struct saned_fd *f, **nextp; + +  for (nextp = &saned_fds; (f = *nextp); /* */) { +    if (type_mask & (1 << f->type) || +	specific_fd == f->fd) { +      close(f->fd); +      *nextp = f->next; +      num_saned_fds--; +      free(f); +    } else +      nextp = &f->next; +  }  } -static int -add_child (pid_t pid) +static void +add_child (pid_t pid, enum saned_child_type type)  {    struct saned_child *c;    c = (struct saned_child *) malloc (sizeof(struct saned_child)); -    if (c == NULL) -    { -      DBG (DBG_ERR, "add_child: out of memory\n"); -      return -1; -    } +    goto fail; +  c->type = type;    c->pid = pid; +  c->pidfd = -1;    c->next = children; +#ifdef HAVE_PIDFD_OPEN +  c->pidfd = pidfd_open(pid, 0); +  if (c->pidfd == -1) +    DBG (DBG_DBG, "add_child: could not open pidfd for child process, %s\n", strerror(errno)); +  else +    add_fd(c->pidfd, SANED_FD_PROCESS, POLLIN); +#endif +    children = c; +  numchildren++; +  return; -  return 0; +fail: +  /* If the child process cannot be managed, kill it now. */ +  DBG (DBG_ERR, "add_child: cannot manage child process, %s\n", strerror(errno)); +  kill(pid, SIGTERM); +} + + +static int +wait_child (pid_t pid, int *status, int options) +{ +  struct saned_child *c, **nextp; +  int ret; + +  ret = waitpid(pid, status, options); + +  if (ret <= 0) +    return ret; + +  for (nextp = &children; (c = *nextp); /* */) { +    if (c->pid == ret) { +      *nextp = c->next; +      numchildren--; +      close_fds(0, c->pidfd); +      free(c); +      break; +    } else +      nextp = &c->next; +  } +  return ret;  } @@ -2383,7 +2444,7 @@ handle_connection (int fd)      p = getprotobyname ("tcp");      if (p == 0)        { -	DBG (DBG_WARN, "handle_connection: cannot look up `tcp' protocol number"); +	DBG (DBG_WARN, "handle_connection: cannot look up `tcp' protocol number\n");        }      else        level = p->p_proto; @@ -2391,7 +2452,7 @@ handle_connection (int fd)  # endif	/* SOL_TCP */    if (level == -1        || setsockopt (wire.io.fd, level, TCP_NODELAY, &on, sizeof (on))) -    DBG (DBG_WARN, "handle_connection: failed to put socket in TCP_NODELAY mode (%s)", +    DBG (DBG_WARN, "handle_connection: failed to put socket in TCP_NODELAY mode (%s)\n",  	 strerror (errno));  #endif /* !TCP_NODELAY */ @@ -2433,8 +2494,8 @@ handle_client (int fd)    else if (pid > 0)      {        /* parent */ -      add_child (pid);        close(fd); +      add_child (pid, SANED_CHILD_CLIENT);      }    else      { @@ -2445,20 +2506,28 @@ handle_client (int fd)  }  static void +kill_children(int sig) +{ +  struct saned_child *c; + +  /* The only type of child we kill is Avahi. */ +  for (c = children; c; c = c->next) +    if (c->type == SANED_CHILD_AVAHI) +      kill(c->pid, sig); +} + +static void  bail_out (int error)  {    DBG (DBG_ERR, "%sbailing out, waiting for children...\n", (error) ? "FATAL ERROR; " : ""); -#if WITH_AVAHI -  if (avahi_pid > 0) -    kill (avahi_pid, SIGTERM); -#endif /* WITH_AVAHI */ - +  kill_children(SIGTERM);    while (numchildren > 0)      wait_child (-1, NULL, 0);    DBG (DBG_ERR, "bail_out: all children exited\n"); +  close_fds(-1, -1);    exit ((error) ? 1 : 0);  } @@ -2480,7 +2549,7 @@ sig_int_term_handler (int signum)  #if WITH_AVAHI  static void -saned_avahi (struct pollfd *fds, int nfds); +saned_avahi (void);  static void  saned_create_avahi_services (AvahiClient *c); @@ -2493,16 +2562,16 @@ saned_avahi_group_callback (AvahiEntryGroup *g, AvahiEntryGroupState state, void  static void -saned_avahi (struct pollfd *fds, int nfds) +saned_avahi (void)  { -  struct pollfd *fdp = NULL; +  int avahi_pid;    int error;    avahi_pid = fork ();    if (avahi_pid > 0)      { -      numchildren++; +      add_child(avahi_pid, SANED_CHILD_AVAHI);        return;      }    else if (avahi_pid < 0) @@ -2514,11 +2583,8 @@ saned_avahi (struct pollfd *fds, int nfds)    signal (SIGINT, NULL);    signal (SIGTERM, NULL); -  /* Close network fds */ -  for (fdp = fds; nfds > 0; nfds--, fdp++) -    close (fdp->fd); - -  free(fds); +  /* Close parent fds */ +  close_fds(-1, -1);    avahi_svc_name = avahi_strdup(SANED_NAME); @@ -2853,17 +2919,15 @@ read_config (void)  #ifdef SANED_USES_AF_INDEP  static void -do_bindings_family (int family, int *nfds, struct pollfd **fds, struct addrinfo *res) +do_bindings_family (int family, struct addrinfo *res)  {    struct addrinfo *resp; -  struct pollfd *fdp;    short sane_port;    int fd = -1;    int on = 1;    int i;    sane_port = bind_port; -  fdp = *fds;    for (resp = res, i = 0; resp != NULL; resp = resp->ai_next, i++)      { @@ -2953,23 +3017,15 @@ do_bindings_family (int family, int *nfds, struct pollfd **fds, struct addrinfo  	    }  	} -      fdp->fd = fd; -      fdp->events = POLLIN; - -      (*nfds)++; -      fdp++; +      add_fd(fd, SANED_FD_LISTENER, POLLIN);      } - -  *fds = fdp;  }  static void -do_bindings (int *nfds, struct pollfd **fds) +do_bindings (void)  {    struct addrinfo *res; -  struct addrinfo *resp;    struct addrinfo hints; -  struct pollfd *fdp;    int err;    DBG (DBG_DBG, "do_bindings: trying to get port for service \"%s\" (getaddrinfo)\n", SANED_SERVICE_NAME); @@ -2994,31 +3050,15 @@ do_bindings (int *nfds, struct pollfd **fds)  	}      } -  for (resp = res, *nfds = 0; resp != NULL; resp = resp->ai_next, (*nfds)++) -    ; - -  *fds = malloc (*nfds * sizeof (struct pollfd)); - -  if (fds == NULL) -    { -      DBG (DBG_ERR, "do_bindings: not enough memory for fds\n"); -      freeaddrinfo (res); -      bail_out (1); -    } - -  fdp = *fds; -  *nfds = 0; -    /* bind IPv6 first, IPv4 second */  #ifdef ENABLE_IPV6 -  do_bindings_family (AF_INET6, nfds, &fdp, res); +  do_bindings_family (AF_INET6, res);  #endif -  do_bindings_family (AF_INET, nfds, &fdp, res); +  do_bindings_family (AF_INET, res); -  resp = NULL;    freeaddrinfo (res); -  if (*nfds <= 0) +  if (res == NULL)      {        DBG (DBG_ERR, "do_bindings: couldn't bind an address. Exiting.\n");        bail_out (1); @@ -3028,7 +3068,7 @@ do_bindings (int *nfds, struct pollfd **fds)  #else /* !SANED_USES_AF_INDEP */  static void -do_bindings (int *nfds, struct pollfd **fds) +do_bindings (void)  {    struct sockaddr_in sin;    struct servent *serv; @@ -3052,15 +3092,6 @@ do_bindings (int *nfds, struct pollfd **fds)        DBG (DBG_WARN, "do_bindings: to your /etc/services file (or equivalent). Proceeding anyway.\n");      } -  *nfds = 1; -  *fds = malloc (*nfds * sizeof (struct pollfd)); - -  if (fds == NULL) -    { -      DBG (DBG_ERR, "do_bindings: not enough memory for fds\n"); -      bail_out (1); -    } -    memset (&sin, 0, sizeof (sin));    sin.sin_family = AF_INET; @@ -3075,24 +3106,23 @@ do_bindings (int *nfds, struct pollfd **fds)    DBG (DBG_DBG, "do_bindings: setsockopt ()\n");    if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on))) -    DBG (DBG_ERR, "do_bindings: failed to put socket in SO_REUSEADDR mode (%s)", strerror (errno)); +    DBG (DBG_ERR, "do_bindings: failed to put socket in SO_REUSEADDR mode (%s)\n", strerror (errno));    DBG (DBG_DBG, "do_bindings: bind ()\n");    if (bind (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0)      { -      DBG (DBG_ERR, "do_bindings: bind failed: %s", strerror (errno)); +      DBG (DBG_ERR, "do_bindings: bind failed: %s\n", strerror (errno));        bail_out (1);      }    DBG (DBG_DBG, "do_bindings: listen ()\n");    if (listen (fd, 1) < 0)      { -      DBG (DBG_ERR, "do_bindings: listen failed: %s", strerror (errno)); +      DBG (DBG_ERR, "do_bindings: listen failed: %s\n", strerror (errno));        bail_out (1);      } -  (*fds)->fd = fd; -  (*fds)->events = POLLIN; +  add_fd(fd, SANED_FD_LISTENER, POLLIN);  }  #endif /* SANED_USES_AF_INDEP */ @@ -3209,16 +3239,16 @@ runas_user (char *user)  static void  run_standalone (char *user)  { -  struct pollfd *fds = NULL; -  struct pollfd *fdp = NULL; -  int nfds; +  struct pollfd *poll_set = NULL; +  int poll_set_valid = SANE_FALSE; +  int running = SANE_TRUE;    int fd = -1;    int i;    int ret;    FILE *pidfile; -  do_bindings (&nfds, &fds); +  do_bindings ();    if (run_foreground == SANE_FALSE)      { @@ -3254,7 +3284,11 @@ run_standalone (char *user)        else  	DBG (DBG_ERR, "Could not write PID file: %s\n", strerror (errno)); -      chdir ("/"); +      if (chdir ("/") != 0) +	{ +	  DBG (DBG_ERR, "Could not change to root directory: %s\n", strerror (errno)); +	  exit(1); +	}        dup2 (fd, STDIN_FILENO);        dup2 (fd, STDOUT_FILENO); @@ -3263,26 +3297,55 @@ run_standalone (char *user)        close (fd);        setsid (); - -      signal(SIGINT, sig_int_term_handler); -      signal(SIGTERM, sig_int_term_handler);      } +  signal(SIGINT, sig_int_term_handler); +  signal(SIGTERM, sig_int_term_handler); +    if (user)      runas_user(user);  #if WITH_AVAHI    DBG (DBG_INFO, "run_standalone: spawning Avahi process\n"); -  saned_avahi (fds, nfds); +  saned_avahi ();    /* NOT REACHED (Avahi process) */  #endif /* WITH_AVAHI */    DBG (DBG_MSG, "run_standalone: waiting for control connection\n"); -  while (1) +  while (running)      { -      ret = poll (fds, nfds, 500); +      struct saned_child *child; +      struct saned_fd *sfd; +      int timeout_needed = SANE_FALSE; +      int do_rebind = SANE_FALSE; +      int do_reap; + +      for (child = children; child; child = child->next) +	if (child->pidfd == -1) +	  timeout_needed = SANE_TRUE; +      do_reap = timeout_needed; + +      if (!poll_set_valid) +	{ +	  void *new_poll_set = realloc(poll_set, num_saned_fds * sizeof *poll_set); +	  if (new_poll_set == NULL && num_saned_fds != 0) +	    { +	      DBG (DBG_ERR, "run_standalone: poll set allocation failed: %s\n", strerror (errno)); +	      free(poll_set); +	      bail_out (1); +	    } +	  poll_set = (struct pollfd *) new_poll_set; +	  for (sfd = saned_fds, i = 0; sfd; sfd = sfd->next, i++) { +	    poll_set[i].fd = sfd->fd; +	    poll_set[i].events = sfd->interesting_events; +	  } +	  assert(i == num_saned_fds); +	  poll_set_valid = SANE_TRUE; +	} + +      ret = poll (poll_set, num_saned_fds, timeout_needed ? 500 : -1);        if (ret < 0)  	{  	  if (errno == EINTR) @@ -3290,59 +3353,67 @@ run_standalone (char *user)  	  else  	    {  	      DBG (DBG_ERR, "run_standalone: poll failed: %s\n", strerror (errno)); -	      free (fds); +	      close_fds(-1, -1);  	      bail_out (1);  	    }  	} -      /* Wait for children */ -      while (wait_child (-1, NULL, WNOHANG) > 0) -	; - -      if (ret == 0) -	continue; - -      for (i = 0, fdp = fds; i < nfds; i++, fdp++) +      /* Do not allow fd list to change while iterating over poll events, otherwise +       * we shall have to look them up each time. */ +      for (sfd = saned_fds, i = 0; ret != 0 && i < num_saned_fds; i++, sfd = sfd->next)  	{ -	  /* Error on an fd */ -	  if (fdp->revents & (POLLERR | POLLHUP | POLLNVAL)) -	    { -	      for (i = 0, fdp = fds; i < nfds; i++, fdp++) -		close (fdp->fd); - -	      free (fds); - -	      DBG (DBG_WARN, "run_standalone: invalid fd in set, attempting to re-bind\n"); +	  struct pollfd *pfd = poll_set + i; -	      /* Reopen sockets */ -	      do_bindings (&nfds, &fds); +	  assert(sfd); +	  assert(sfd->fd == pfd->fd); -	      break; -	    } -	  else if (! (fdp->revents & POLLIN)) +	  if (pfd->revents == 0)  	    continue; +	  else +	    ret--; -	  fd = accept (fdp->fd, 0, 0); -	  if (fd < 0) -	    { -	      DBG (DBG_ERR, "run_standalone: accept failed: %s", strerror (errno)); -	      continue; +	  switch (sfd->type) { +	  case SANED_FD_LISTENER: +	    if (pfd->revents & (POLLERR | POLLHUP | POLLNVAL)) +	      do_rebind = SANE_TRUE; +	    else if (pfd->revents & POLLIN) +	      { +		fd = accept (sfd->fd, 0, 0); +		if (fd < 0 && errno != EAGAIN) +		  DBG (DBG_ERR, "run_standalone: accept failed: %s\n", strerror (errno)); +		else if (fd >= 0) +		  { +		    handle_client (fd); +		    if (run_once == SANE_TRUE) +		      running = SANE_FALSE; /* We have handled the only connection we're going to handle */ +		  } +	      } +	    break; +	  case SANED_FD_PROCESS: +	    if (pfd->revents & POLLIN) { +	      do_reap = SANE_TRUE; +	      poll_set_valid = SANE_FALSE; /* We will expect to drop a pidfd */  	    } +	  } +        } -	  handle_client (fd); - -	  if (run_once == SANE_TRUE) -	    break; /* We have handled the only connection we're going to handle */ +      if (do_rebind) +        { +	  DBG (DBG_WARN, "run_standalone: invalid fd in set, attempting to re-bind\n"); +	  close_fds(1 << SANED_FD_LISTENER, -1); +	  do_bindings (); +	  poll_set_valid = SANE_FALSE;  	} -      if (run_once == SANE_TRUE) -	break; +      if (do_reap) +	while (wait_child (-1, NULL, WNOHANG) > 0);      } -  for (i = 0, fdp = fds; i < nfds; i++, fdp++) -    close (fdp->fd); - -  free (fds); +  free(poll_set); +  close_fds(-1, -1); +  kill_children(SIGTERM); +  while (numchildren > 0) +    wait_child (-1, NULL, 0);  } @@ -3389,7 +3460,7 @@ run_inetd (char __sane_unused__ *sock)            if (fd == -1)        	    { -              DBG (DBG_ERR, "run_inetd: duplicating fd failed: %s", strerror (errno)); +              DBG (DBG_ERR, "run_inetd: duplicating fd failed: %s\n", strerror (errno));                return;              }          } @@ -3399,7 +3470,7 @@ run_inetd (char __sane_unused__ *sock)        dave_null = open ("/dev/null", O_RDWR);        if (dave_null < 0)          { -          DBG (DBG_ERR, "run_inetd: could not open /dev/null: %s", strerror (errno)); +          DBG (DBG_ERR, "run_inetd: could not open /dev/null: %s\n", strerror (errno));            return;          } @@ -3436,6 +3507,7 @@ static void usage(char *me, int err)         "  -a, --alone[=user]	        equal to `-l -D -u user'\n"         "  -l, --listen		        run in standalone mode (listen for connection)\n"         "  -u, --user=user	        run as `user'\n" +       "  -n, --allow-network	        allow saned to use network scanners\n"         "  -D, --daemonize	        run in background\n"         "  -o, --once		        exit after first client disconnects\n"         "  -d, --debug=level	        set debug level `level' (default is 2)\n" @@ -3457,6 +3529,7 @@ static struct option long_options[] =    {"alone",             optional_argument,      0, 'a'},    {"listen",            no_argument,            0, 'l'},    {"user",              required_argument,      0, 'u'}, +  {"allow-network",     no_argument,            0, 'n'},    {"daemonize",         no_argument,            0, 'D'},    {"once",              no_argument,            0, 'o'},    {"debug",             required_argument,      0, 'd'}, @@ -3488,8 +3561,9 @@ main (int argc, char *argv[])    run_mode = SANED_RUN_INETD;    run_foreground = SANE_TRUE;    run_once = SANE_FALSE; +  allow_network = SANE_FALSE; -  while((c = getopt_long(argc, argv,"ha::lu:Dod:eb:p:B:", long_options, &long_index )) != -1) +  while((c = getopt_long(argc, argv,"ha::lu:nDod:eb:p:B:", long_options, &long_index )) != -1)      {        switch(c) {        case 'a': @@ -3504,6 +3578,9 @@ main (int argc, char *argv[])        case 'u':  	user = optarg;  	break; +      case 'n': +	allow_network = SANE_TRUE; +	break;        case 'D':  	run_foreground = SANE_FALSE;  	break; diff --git a/frontend/scanimage.c b/frontend/scanimage.c index ff53157..d79052c 100644 --- a/frontend/scanimage.c +++ b/frontend/scanimage.c @@ -2372,7 +2372,7 @@ Parameters are separated by a blank from single-character options (e.g.\n\                             %%n (newline)\n\  -b, --batch[=FORMAT]       working in batch mode, FORMAT is `out%%d.pnm' `out%%d.tif'\n\                             `out%%d.png' or `out%%d.jpg' by default depending on --format\n\ -                           This option is incompatible with --output-file."); +                           This option is incompatible with --output-file.\n");        printf ("\      --batch-start=#        page number to start naming files with\n\      --batch-count=#        how many pages to scan in batch mode\n\ | 
