Add standalone daemon mode, building upon the AF-indep/IPv6 debug mode.

Reorganize code by splitting the main() function into a series of functions.
Factorize common code between the old network code and the AF-indep code. There's now only one version of main().
merge-requests/1/head
Julien BLACHE 2008-04-06 15:21:47 +00:00
rodzic ef2838234e
commit 9c6fea1943
3 zmienionych plików z 579 dodań i 369 usunięć

Wyświetl plik

@ -1,3 +1,13 @@
2008-04-06 Julien Blache <jb@jblache.org>
* frontend/saned.c: add standalone daemon mode, building upon the
AF-indep/IPv6 debug mode. Reorganize code by splitting the main()
function into a series of functions. Factorize common code between
the old network code and the AF-indep code. There's now only one
version of main().
* doc/saned.man: document new -a flag, reorganize manpage
sections (separate inetd configuration under the INETD
CONFIGURATION section).
2008-04-05 Stéphane Voltz <stef.dev@free.fr> 2008-04-05 Stéphane Voltz <stef.dev@free.fr>
* backend/genesys.c backend/genesys.h backend/genesys_devices.c * backend/genesys.c backend/genesys.h backend/genesys_devices.c
backend/genesys_gl646.c backend/genesys_low.h: backend/genesys_gl646.c backend/genesys_low.h:

Wyświetl plik

@ -1,11 +1,14 @@
.TH saned 8 "30 May 2004" "@PACKAGEVERSION@" "SANE Scanner Access Now Easy" .TH saned 8 "6 April 2008" "@PACKAGEVERSION@" "SANE Scanner Access Now Easy"
.IX saned .IX saned
.SH NAME .SH NAME
saned \- SANE network daemon saned \- SANE network daemon
.SH SYNOPSIS .SH SYNOPSIS
.B saned .B saned
.RB [ -d | -s .B [ -a | -d
.RI [ n ]] .I [ n ]
.B | -s
.I [ n ]
.B ]
.SH DESCRIPTION .SH DESCRIPTION
.B saned .B saned
is the SANE (Scanner Access Now Easy) daemon that allows remote clients is the SANE (Scanner Access Now Easy) daemon that allows remote clients
@ -13,6 +16,19 @@ to access image acquisition devices available on the local host.
.SH OPTIONS .SH OPTIONS
.PP .PP
The The
.B -a
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;
.B inetd
is not required for
.B saned
operations in this mode.
.PP
The
.B -d .B -d
and and
.B -s .B -s
@ -80,10 +96,11 @@ scan-client.somedomain.firm
.PP .PP
The case of the host names does not matter, so AHost.COM is considered The case of the host names does not matter, so AHost.COM is considered
identical to ahost.com. identical to ahost.com.
.SH INETD CONFIGURATION
For For
.B saned .B saned
to work properly, it is also necessary to add a configuration line to to work properly in its default mode of operation, it is also necessary to add
a configuration line to
.IR /etc/inetd.conf . .IR /etc/inetd.conf .
Note that your inetd must support IPv6 if you Note that your inetd must support IPv6 if you
want to connect to saned over IPv6 ; xinetd and openbsd-inetd are known to want to connect to saned over IPv6 ; xinetd and openbsd-inetd are known to
@ -152,7 +169,11 @@ The official IANA short name for port 6566 is "sane-port". The older name "sane"
is now deprecated. is now deprecated.
.SH "RESTRICTIONS" .SH "RESTRICTIONS"
In addition to the control connection (port 6566) saned also uses a data In addition to the control connection (port
.B 6566
)
.B saned
also uses a data
connection. The port of this socket is selected by the operating system and connection. The port of this socket is selected by the operating system and
can't be specified by the user currently. This may be a problem if the can't be specified by the user currently. This may be a problem if the
connection must go through a firewall (packet filter). If you must use a packet connection must go through a firewall (packet filter). If you must use a packet

Wyświetl plik

@ -1,8 +1,8 @@
/* sane - Scanner Access Now Easy. /* sane - Scanner Access Now Easy.
Copyright (C) 1997 Andreas Beck Copyright (C) 1997 Andreas Beck
Copyright (C) 2001 - 2004 Henning Meier-Geinitz Copyright (C) 2001 - 2004 Henning Meier-Geinitz
Copyright (C) 2003 Julien BLACHE <jb@jblache.org> Copyright (C) 2003, 2008 Julien BLACHE <jb@jblache.org>
AF-independent + IPv6 code AF-independent + IPv6 code, standalone mode
This file is part of the SANE package. This file is part of the SANE package.
@ -76,13 +76,14 @@
#include <sys/types.h> #include <sys/types.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#ifdef SANED_USES_AF_INDEP #include <sys/wait.h>
#if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL) #if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
# include <sys/poll.h> # include <sys/poll.h>
#else #else
/* /*
* This replacement poll() using select() is only designed to cover * This replacement poll() using select() is only designed to cover
* our needs in main(). It should probably be extended... * our needs in run_standalone(). It should probably be extended...
*/ */
struct pollfd struct pollfd
{ {
@ -92,34 +93,44 @@ struct pollfd
}; };
#define POLLIN 0x0001 #define POLLIN 0x0001
#define POLLERR 0x0002
int
poll (struct pollfd *ufds, unsigned int nfds, int timeout);
int int
poll (struct pollfd *ufds, unsigned int nfds, int timeout) poll (struct pollfd *ufds, unsigned int nfds, int timeout)
{ {
struct pollfd *fdp; struct pollfd *fdp;
fd_set fds; fd_set rfds;
fd_set efds;
struct timeval tv;
int maxfd = 0; int maxfd = 0;
unsigned int i; unsigned int i;
int ret; int ret;
/* unused */ tv.tv_sec = timeout / 1000;
timeout = timeout; tv.tv_usec = (timeout - tv.tv_sec * 1000) * 1000;
FD_ZERO (&fds); FD_ZERO (&rfds);
FD_ZERO (&efds);
for (i = 0, fdp = ufds; i < nfds; i++, fdp++) for (i = 0, fdp = ufds; i < nfds; i++, fdp++)
{ {
fdp->revents = 0;
if (fdp->events & POLLIN) if (fdp->events & POLLIN)
{ FD_SET (fdp->fd, &rfds);
FD_SET (fdp->fd, &fds);
FD_SET (fdp->fd, &efds);
maxfd = (fdp->fd > maxfd) ? fdp->fd : maxfd; maxfd = (fdp->fd > maxfd) ? fdp->fd : maxfd;
} }
}
maxfd++; maxfd++;
ret = select (maxfd, &fds, NULL, NULL, NULL); ret = select (maxfd, &rfds, NULL, &efds, &tv);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -127,14 +138,16 @@ poll (struct pollfd *ufds, unsigned int nfds, int timeout)
for (i = 0, fdp = ufds; i < nfds; i++, fdp++) for (i = 0, fdp = ufds; i < nfds; i++, fdp++)
{ {
if (fdp->events & POLLIN) if (fdp->events & POLLIN)
if (FD_ISSET (fdp->fd, &fds)) if (FD_ISSET (fdp->fd, &rfds))
fdp->revents = POLLIN; fdp->revents |= POLLIN;
if (FD_ISSET (fdp->fd, &efds))
fdp->revents |= POLLERR;
} }
return ret; return ret;
} }
#endif /* HAVE_SYS_POLL_H && HAVE_POLL */ #endif /* HAVE_SYS_POLL_H && HAVE_POLL */
#endif /* SANED_USES_AF_INDEP */
#include "../include/sane/sane.h" #include "../include/sane/sane.h"
#include "../include/sane/sanei.h" #include "../include/sane/sanei.h"
@ -169,8 +182,19 @@ poll (struct pollfd *ufds, unsigned int nfds, int timeout)
# define MAXHOSTNAMELEN 120 # define MAXHOSTNAMELEN 120
#endif #endif
struct saned_child {
pid_t pid;
struct saned_child *next;
};
struct saned_child *children;
int numchildren;
#define SANED_CONFIG_FILE "saned.conf" #define SANED_CONFIG_FILE "saned.conf"
#define SANED_SERVICE_NAME "sane-port"
#define SANED_SERVICE_PORT 6566
#define SANED_SERVICE_PORT_S "6566"
typedef struct typedef struct
{ {
u_int inuse:1; /* is this handle in use? */ u_int inuse:1; /* is this handle in use? */
@ -186,6 +210,7 @@ static int can_authorize;
static Wire wire; static Wire wire;
static int num_handles; static int num_handles;
static int debug; static int debug;
static int run_mode;
static Handle *handle; static Handle *handle;
static union static union
{ {
@ -220,6 +245,11 @@ static SANE_Bool log_to_syslog = SANE_TRUE;
/* forward declarations: */ /* forward declarations: */
static void process_request (Wire * w); static void process_request (Wire * w);
#define SANED_RUN_INETD 0
#define SANED_RUN_DEBUG 1
#define SANED_RUN_ALONE 2
#define DBG_ERR 1 #define DBG_ERR 1
#define DBG_WARN 2 #define DBG_WARN 2
#define DBG_MSG 3 #define DBG_MSG 3
@ -399,6 +429,7 @@ quit (int signum)
if (handle) if (handle)
free (handle); free (handle);
DBG (DBG_WARN, "quit: exiting\n"); DBG (DBG_WARN, "quit: exiting\n");
if (log_to_syslog)
closelog (); closelog ();
exit (EXIT_SUCCESS); /* This is a nowait-daemon. */ exit (EXIT_SUCCESS); /* This is a nowait-daemon. */
} }
@ -2035,56 +2066,178 @@ process_request (Wire * w)
} }
} }
#ifdef SANED_USES_AF_INDEP
int static int
main (int argc, char *argv[]) wait_child (pid_t pid, int *status, int options)
{
struct saned_child *c;
struct saned_child *p = NULL;
int ret;
ret = waitpid(pid, status, options);
if (ret <= 0)
return ret;
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;
free(c);
numchildren--;
break;
}
}
return ret;
}
static int
add_child (pid_t pid)
{
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;
}
c->pid = pid;
c->next = children;
children = c;
return 0;
}
static void
handle_connection (int fd)
{ {
int fd = -1;
int on = 1;
#ifdef TCP_NODELAY #ifdef TCP_NODELAY
int level = -1; int level = -1;
#endif #endif
debug = DBG_WARN; DBG (DBG_DBG, "handle_connection: processing client connection\n");
openlog ("saned", LOG_PID | LOG_CONS, LOG_DAEMON);
prog_name = strrchr (argv[0], '/'); wire.io.fd = fd;
if (prog_name)
++prog_name;
else
prog_name = argv[0];
byte_order.w = 0; signal (SIGALRM, quit);
byte_order.ch = 1; signal (SIGPIPE, quit);
sanei_w_init (&wire, sanei_codec_bin_init); #ifdef TCP_NODELAY
wire.io.read = read; # ifdef SOL_TCP
wire.io.write = write; level = SOL_TCP;
# else /* !SOL_TCP */
if (argc == 2 && /* Look up the protocol level in the protocols database. */
(strncmp (argv[1], "-d", 2) == 0 || strncmp (argv[1], "-s", 2) == 0)) {
struct protoent *p;
p = getprotobyname ("tcp");
if (p == 0)
{
DBG (DBG_WARN, "handle_connection: cannot look up `tcp' protocol number");
}
else
level = p->p_proto;
}
# 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)",
strerror (errno));
#endif /* !TCP_NODELAY */
if (init (&wire) < 0)
quit (0);
while (1)
{
reset_watchdog ();
process_request (&wire);
}
}
static void
handle_client (int fd)
{
pid_t pid;
DBG (DBG_DBG, "handle_client: spawning child process\n");
pid = fork ();
if (pid == 0)
{
/* child */
handle_connection (fd);
}
else if (pid > 0)
{
/* parent */
add_child (pid);
}
else
{
/* FAILED */
DBG (DBG_ERR, "handle_client: fork() failed: %s\n", strerror (errno));
}
}
static void
bail_out (int error)
{
DBG (DBG_ERR, "%sbailing out, waiting for children...\n", (error) ? "FATAL ERROR; " : "");
while (numchildren > 0)
wait_child (-1, NULL, 0);
DBG (DBG_ERR, "bail_out: all children exited\n");
exit (1);
}
void
sig_int_term_handler (int signum);
void
sig_int_term_handler (int signum)
{
/* unused */
signum = signum;
signal (SIGINT, NULL);
signal (SIGTERM, NULL);
bail_out (0);
}
#ifdef SANED_USES_AF_INDEP
static void
do_bindings (int *nfds, struct pollfd **fds)
{ {
/* don't operate in daemon mode: wait for connection request: */
struct addrinfo *res; struct addrinfo *res;
struct addrinfo *resp; struct addrinfo *resp;
struct addrinfo hints; struct addrinfo hints;
struct pollfd *fds = NULL;
struct pollfd *fdp = NULL; struct pollfd *fdp = NULL;
int nfds;
int err; int err;
int i; int i;
short sane_port; short sane_port;
int family; int family;
int fd = -1;
int on = 1;
if (argv[1][2]) DBG (DBG_DBG, "do_bindings: trying to get port for service \"%s\" (getaddrinfo)\n", SANED_SERVICE_NAME);
debug = atoi (argv[1] + 2);
if (strncmp (argv[1], "-d", 2) == 0)
log_to_syslog = SANE_FALSE;
DBG (DBG_WARN, "main: starting debug mode (level %d)\n", debug);
DBG (DBG_DBG,
"main: trying to get port for service `sane-port' (getaddrinfo)\n");
memset (&hints, 0, sizeof (struct addrinfo)); memset (&hints, 0, sizeof (struct addrinfo));
@ -2092,34 +2245,33 @@ main (int argc, char *argv[])
hints.ai_flags = AI_PASSIVE; hints.ai_flags = AI_PASSIVE;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
err = getaddrinfo (NULL, "sane-port", &hints, &res); err = getaddrinfo (NULL, SANED_SERVICE_NAME, &hints, &res);
if (err) if (err)
{ {
DBG (DBG_WARN, "main: \"sane-port\" service unknown on your host; you should add\n"); DBG (DBG_WARN, "do_bindings: \" %s \" service unknown on your host; you should add\n", SANED_SERVICE_NAME);
DBG (DBG_WARN, "main: sane-port 6566/tcp saned # SANE network scanner daemon\n"); DBG (DBG_WARN, "do_bindings: %s %d/tcp saned # SANE network scanner daemon\n", SANED_SERVICE_NAME, SANED_SERVICE_PORT);
DBG (DBG_WARN, "main: to your /etc/services file (or equivalent). Proceeding anyway.\n"); DBG (DBG_WARN, "do_bindings: to your /etc/services file (or equivalent). Proceeding anyway.\n");
err = getaddrinfo (NULL, "6566", &hints, &res); err = getaddrinfo (NULL, SANED_SERVICE_PORT_S, &hints, &res);
if (err) if (err)
{ {
DBG (DBG_ERR, "main: getaddrinfo() failed even with numeric port: %s\n", DBG (DBG_ERR, "do_bindings: getaddrinfo() failed even with numeric port: %s\n", gai_strerror (err));
gai_strerror (err)); bail_out (1);
exit (1);
} }
} }
for (resp = res, nfds = 0; resp != NULL; resp = resp->ai_next, nfds++) for (resp = res, *nfds = 0; resp != NULL; resp = resp->ai_next, (*nfds)++)
; ;
fds = malloc (nfds * sizeof (struct pollfd)); *fds = malloc (*nfds * sizeof (struct pollfd));
if (fds == NULL) if (fds == NULL)
{ {
DBG (DBG_ERR, "main: not enough memory for fds\n"); DBG (DBG_ERR, "do_bindings: not enough memory for fds\n");
freeaddrinfo (res); freeaddrinfo (res);
exit (1); bail_out (1);
} }
for (resp = res, i = 0, fdp = fds; resp != NULL; resp = resp->ai_next, i++, fdp++) for (resp = res, i = 0, fdp = *fds; resp != NULL; resp = resp->ai_next, i++, fdp++)
{ {
if (resp->ai_family == AF_INET) if (resp->ai_family == AF_INET)
{ {
@ -2136,28 +2288,26 @@ main (int argc, char *argv[])
else else
{ {
fdp--; fdp--;
nfds--; (*nfds)--;
continue; continue;
} }
DBG (DBG_DBG, "main: [%d] socket () using IPv%d\n", i, family); DBG (DBG_DBG, "do_bindings: [%d] socket () using IPv%d\n", i, family);
if ((fd = socket (resp->ai_family, SOCK_STREAM, 0)) < 0) if ((fd = socket (resp->ai_family, SOCK_STREAM, 0)) < 0)
{ {
DBG (DBG_ERR, "main: [%d] socket failed: %s\n", i, DBG (DBG_ERR, "do_bindings: [%d] socket failed: %s\n", i, strerror (errno));
strerror (errno));
nfds--;
fdp--; fdp--;
(*nfds)--;
continue; continue;
} }
DBG (DBG_DBG, "main: [%d] setsockopt ()\n", i); DBG (DBG_DBG, "do_bindings: [%d] setsockopt ()\n", i);
if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on))) if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)))
DBG (DBG_ERR, "main: [%d] failed to put socket in SO_REUSEADDR mode (%s)\n", DBG (DBG_ERR, "do_bindings: [%d] failed to put socket in SO_REUSEADDR mode (%s)\n", i, strerror (errno));
i, strerror (errno));
DBG (DBG_DBG, "main: [%d] bind () to port %d\n", i, sane_port); DBG (DBG_DBG, "do_bindings: [%d] bind () to port %d\n", i, sane_port);
if (bind (fd, resp->ai_addr, resp->ai_addrlen) < 0) if (bind (fd, resp->ai_addr, resp->ai_addrlen) < 0)
{ {
/* /*
@ -2168,20 +2318,25 @@ main (int argc, char *argv[])
* is not functional on this machine. * is not functional on this machine.
* In any case, a bind() call returning an error is not necessarily fatal. * In any case, a bind() call returning an error is not necessarily fatal.
*/ */
DBG (DBG_ERR, "main: [%d] bind failed: %s\n", i, strerror (errno)); DBG (DBG_WARN, "do_bindings: [%d] bind failed: %s\n", i, strerror (errno));
close (fd); close (fd);
nfds--;
fdp--; fdp--;
(*nfds)--;
continue; continue;
} }
DBG (DBG_DBG, "main: [%d] listen ()\n", i); DBG (DBG_DBG, "do_bindings: [%d] listen ()\n", i);
if (listen (fd, 1) < 0) if (listen (fd, 1) < 0)
{ {
DBG (DBG_ERR, "main: [%d] listen failed: %s\n", i, strerror (errno)); DBG (DBG_ERR, "do_bindings: [%d] listen failed: %s\n", i, strerror (errno));
exit (1);
close (fd);
fdp--;
(*nfds)--;
continue;
} }
fdp->fd = fd; fdp->fd = fd;
@ -2191,121 +2346,212 @@ main (int argc, char *argv[])
resp = NULL; resp = NULL;
freeaddrinfo (res); freeaddrinfo (res);
if (nfds <= 0) if (*nfds <= 0)
{ {
DBG (DBG_ERR, "main: couldn't bind an address. Exiting.\n"); DBG (DBG_ERR, "do_bindings: couldn't bind an address. Exiting.\n");
bail_out (1);
}
}
#else /* !SANED_USES_AF_INDEP */
static void
do_bindings (int *nfds, struct pollfd **fds)
{
struct sockaddr_in sin;
struct servent *serv;
short port;
int fd = -1;
int on = 1;
DBG (DBG_DBG, "do_bindings: trying to get port for service \"%s\" (getservbyname)\n", SANED_SERVICE_PORT);
serv = getservbyname (SANED_SERVICE_NAME, "tcp");
if (serv)
{
port = serv->s_port;
DBG (DBG_MSG, "main: port is %d\n", ntohs (port));
}
else
{
port = htons (SANED_SERVICE_PORT);
DBG (DBG_WARN, "do_bindings: \"%s\" service unknown on your host; you should add\n", SANED_SERVICE_NAME);
DBG (DBG_WARN, "do_bindings: %s %d/tcp saned # SANE network scanner daemon\n", SANED_SERVICE_NAME, SANED_SERVICE_PORT);
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;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = port;
DBG (DBG_DBG, "do_bindings: socket ()\n");
fd = socket (AF_INET, SOCK_STREAM, 0);
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_DBG, "do_bindings: bind ()\n");
if (bind (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0)
{
DBG (DBG_ERR, "do_bindings: bind failed: %s", 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));
bail_out (1);
}
(*fds)->fd = fd;
(*fds)->events = POLLIN;
}
#endif /* SANED_USES_AF_INDEP */
static void
run_standalone (int argc, char **argv)
{
struct pollfd *fds = NULL;
struct pollfd *fdp = NULL;
int nfds;
int fd;
int i;
int ret;
/* Unused in this function */
argc = argc;
argv = argv;
do_bindings (&nfds, &fds);
if (run_mode != SANED_RUN_DEBUG)
{
DBG (DBG_MSG, "run_standalone: daemonizing now\n");
if (daemon (0, 0) != 0)
{
DBG (DBG_ERR, "FATAL ERROR: cannot daemonize: %s\n", strerror (errno));
exit (1); exit (1);
} }
DBG (DBG_MSG, "main: waiting for control connection\n"); signal(SIGINT, sig_int_term_handler);
signal(SIGTERM, sig_int_term_handler);
}
DBG (DBG_MSG, "run_standalone: waiting for control connection\n");
while (1) while (1)
{ {
if (poll (fds, nfds, -1) < 0) ret = poll (fds, nfds, 500);
if (ret < 0)
{ {
if (errno == EINTR) if (errno == EINTR)
continue; continue;
else else
{ {
DBG (DBG_ERR, "main: poll failed: %s\n", strerror (errno)); DBG (DBG_ERR, "run_standalone: poll failed: %s\n", strerror (errno));
free (fds); free (fds);
exit (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++) for (i = 0, fdp = fds; i < nfds; i++, fdp++)
{ {
/* Error on an fd */
if (! (fdp->revents & POLLIN)) if (! (fdp->revents & POLLIN))
continue;
wire.io.fd = accept (fdp->fd, 0, 0);
if (wire.io.fd < 0)
{ {
DBG (DBG_ERR, "main: accept failed: %s", strerror (errno)); for (i = 0, fdp = fds; i < nfds; i++, fdp++)
close (fdp->fd);
free (fds); free (fds);
exit (1);
DBG (DBG_WARN, "run_standalone: invalid fd in set, attempting to re-bind\n");
/* Reopen sockets */
do_bindings (&nfds, &fds);
break;
}
fd = accept (fdp->fd, 0, 0);
if (fd < 0)
{
DBG (DBG_ERR, "run_standalone: accept failed: %s", strerror (errno));
continue;
}
if (run_mode == SANED_RUN_DEBUG)
{
handle_connection (fd);
break;
}
else
handle_client (fd);
}
if (run_mode == SANED_RUN_DEBUG)
break;
} }
for (i = 0, fdp = fds; i < nfds; i++, fdp++) for (i = 0, fdp = fds; i < nfds; i++, fdp++)
close (fdp->fd); close (fdp->fd);
free (fds); free (fds);
}
break;
} static void
break; run_inetd (int argc, char **argv)
} {
} int fd = 1;
else
/* use filedescriptor opened by inetd: */ #ifndef HAVE_OS2_H
#ifdef HAVE_OS2_H /* Unused in this function */
argc = argc;
argv = argv;
#else
/* under OS/2, the socket handle is passed as argument on the command /* under OS/2, the socket handle is passed as argument on the command
line; the socket handle is relative to IBM TCP/IP, so a call line; the socket handle is relative to IBM TCP/IP, so a call
to impsockethandle() is required to add it to the EMX runtime */ to impsockethandle() is required to add it to the EMX runtime */
if (argc == 2) if (argc == 2)
{ {
wire.io.fd = _impsockhandle (atoi (argv[1]), 0); fd = _impsockhandle (atoi (argv[1]), 0);
if (wire.io.fd == -1) if (fd == -1)
perror ("impsockhandle"); perror ("impsockhandle");
} }
else
#endif /* HAVE_OS2_H */ #endif /* HAVE_OS2_H */
wire.io.fd = 1;
signal (SIGALRM, quit); handle_connection(fd);
signal (SIGPIPE, quit);
#ifdef TCP_NODELAY
# ifdef SOL_TCP
level = SOL_TCP;
# else /* !SOL_TCP */
/* Look up the protocol level in the protocols database. */
{
struct protoent *p;
p = getprotobyname ("tcp");
if (p == 0)
{
DBG (DBG_WARN, "main: cannot look up `tcp' protocol number");
}
else
level = p->p_proto;
}
# endif /* SOL_TCP */
if (level == -1
|| setsockopt (wire.io.fd, level, TCP_NODELAY, &on, sizeof (on)))
DBG (DBG_WARN, "main: failed to put socket in TCP_NODELAY mode (%s)",
strerror (errno));
#endif /* !TCP_NODELAY */
/* define the version string depending on which network code is used */
#ifdef ENABLE_IPV6
DBG (DBG_WARN, "saned (AF-indep+IPv6) from %s ready\n", PACKAGE_STRING);
#else
DBG (DBG_WARN, "saned (AF-indep) from %s ready\n", PACKAGE_STRING);
#endif /* ENABLE_IPV6 */
if (init (&wire) < 0)
quit (0);
while (1)
{
reset_watchdog ();
process_request (&wire);
}
} }
#else /* !SANED_USES_AF_INDEP */
int int
main (int argc, char *argv[]) main (int argc, char *argv[])
{ {
int fd, on = 1;
#ifdef TCP_NODELAY
int level = -1;
#endif
debug = DBG_WARN; debug = DBG_WARN;
openlog ("saned", LOG_PID | LOG_CONS, LOG_DAEMON);
prog_name = strrchr (argv[0], '/'); prog_name = strrchr (argv[0], '/');
if (prog_name) if (prog_name)
@ -2313,6 +2559,33 @@ main (int argc, char *argv[])
else else
prog_name = argv[0]; prog_name = argv[0];
numchildren = 0;
run_mode = SANED_RUN_INETD;
if (argc == 2)
{
if (strncmp (argv[1], "-a", 2) == 0)
run_mode = SANED_RUN_ALONE;
else if (strncmp (argv[1], "-d", 2) == 0)
{
run_mode = SANED_RUN_DEBUG;
log_to_syslog = SANE_FALSE;
}
else if (strncmp (argv[1], "-s", 2) == 0)
run_mode = SANED_RUN_DEBUG;
}
if (run_mode == SANED_RUN_DEBUG)
{
if (argv[1][2])
debug = atoi (argv[1] + 2);
DBG (DBG_WARN, "main: starting debug mode (level %d)\n", debug);
}
if (log_to_syslog)
openlog ("saned", LOG_PID | LOG_CONS, LOG_DAEMON);
byte_order.w = 0; byte_order.w = 0;
byte_order.ch = 1; byte_order.ch = 1;
@ -2320,121 +2593,27 @@ main (int argc, char *argv[])
wire.io.read = read; wire.io.read = read;
wire.io.write = write; wire.io.write = write;
if (argc == 2 && /* define the version string depending on which network code is used */
(strncmp (argv[1], "-d", 2) == 0 || strncmp (argv[1], "-s", 2) == 0)) #ifdef SANED_USES_AF_INDEP
{ # ifdef ENABLE_IPV6
/* don't operate in daemon mode: wait for connection request: */ DBG (DBG_WARN, "saned (AF-indep+IPv6) from %s starting up\n", PACKAGE_STRING);
struct sockaddr_in sin; # else
struct servent *serv; DBG (DBG_WARN, "saned (AF-indep) from %s starting up\n", PACKAGE_STRING);
short port; # endif /* ENABLE_IPV6 */
#else
if (argv[1][2])
debug = atoi (argv[1] + 2);
if (strncmp (argv[1], "-d", 2) == 0)
log_to_syslog = SANE_FALSE;
DBG (DBG_WARN, "main: starting debug mode (level %d)\n", debug);
memset (&sin, 0, sizeof (sin));
DBG (DBG_DBG,
"main: trying to get port for service `sane-port' (getservbyname)\n");
serv = getservbyname ("sane-port", "tcp");
if (serv)
{
port = serv->s_port;
DBG (DBG_MSG, "main: port is %d\n", ntohs (port));
}
else
{
port = htons (6566);
DBG (DBG_WARN, "main: \"sane-port\" service unknown on your host; you should add\n");
DBG (DBG_WARN, "main: sane-port 6566/tcp saned # SANE network scanner daemon\n");
DBG (DBG_WARN, "main: to your /etc/services file (or equivalent). Proceeding anyway.\n");
}
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = port;
DBG (DBG_DBG, "main: socket ()\n");
fd = socket (AF_INET, SOCK_STREAM, 0);
DBG (DBG_DBG, "main: setsockopt ()\n");
if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)))
DBG (DBG_ERR, "failed to put socket in SO_REUSEADDR mode (%s)",
strerror (errno));
DBG (DBG_DBG, "main: bind ()\n");
if (bind (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0)
{
DBG (DBG_ERR, "main: bind failed: %s", strerror (errno));
exit (1);
}
DBG (DBG_DBG, "main: listen ()\n");
if (listen (fd, 1) < 0)
{
DBG (DBG_ERR, "main: listen failed: %s", strerror (errno));
exit (1);
}
DBG (DBG_MSG, "main: waiting for control connection\n");
wire.io.fd = accept (fd, 0, 0);
if (wire.io.fd < 0)
{
DBG (DBG_ERR, "main: accept failed: %s", strerror (errno));
exit (1);
}
close (fd);
}
else
/* use filedescriptor opened by inetd: */
#ifdef HAVE_OS2_H
/* under OS/2, the socket handle is passed as argument on the command
line; the socket handle is relative to IBM TCP/IP, so a call
to impsockethandle() is required to add it to the EMX runtime */
if (argc == 2)
{
wire.io.fd = _impsockhandle (atoi (argv[1]), 0);
if (wire.io.fd == -1)
perror ("impsockhandle");
}
else
#endif /* HAVE_OS2_H */
wire.io.fd = 1;
signal (SIGALRM, quit);
signal (SIGPIPE, quit);
#ifdef TCP_NODELAY
# ifdef SOL_TCP
level = SOL_TCP;
# else /* !SOL_TCP */
/* Look up the protocol level in the protocols database. */
{
struct protoent *p;
p = getprotobyname ("tcp");
if (p == 0)
{
DBG (DBG_WARN, "main: cannot look up `tcp' protocol number");
}
else
level = p->p_proto;
}
# endif /* SOL_TCP */
if (level == -1
|| setsockopt (wire.io.fd, level, TCP_NODELAY, &on, sizeof (on)))
DBG (DBG_WARN, "main: failed to put socket in TCP_NODELAY mode (%s)",
strerror (errno));
#endif /* !TCP_NODELAY */
DBG (DBG_WARN, "saned from %s ready\n", PACKAGE_STRING); DBG (DBG_WARN, "saned from %s ready\n", PACKAGE_STRING);
if (init (&wire) < 0)
quit (0);
while (1)
{
reset_watchdog ();
process_request (&wire);
}
}
#endif /* SANED_USES_AF_INDEP */ #endif /* SANED_USES_AF_INDEP */
if ((run_mode == SANED_RUN_ALONE) || (run_mode == SANED_RUN_DEBUG))
{
run_standalone(argc, argv);
}
else
{
run_inetd(argc, argv);
}
DBG (DBG_WARN, "saned exiting\n");
return 0;
}