diff options
Diffstat (limited to 'src/net.c')
-rw-r--r-- | src/net.c | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/src/net.c b/src/net.c new file mode 100644 index 0000000..efb6225 --- /dev/null +++ b/src/net.c @@ -0,0 +1,227 @@ +/* wydawca - automatic release submission daemon + Copyright (C) 2007, 2009 Sergey Poznyakoff + + Wydawca 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 3 of the License, or (at your + option) any later version. + + Wydawca 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 wydawca. If not, see <http://www.gnu.org/licenses/>. */ + +#include "wydawca.h" + +static int +open_listener () +{ + int fd; + + if (listen_sockaddr.sa == NULL) + { + logmsg (LOG_CRIT, _("listener address is not configured")); + exit (1); + } + + fd = socket (listen_sockaddr.sa->sa_family, SOCK_STREAM, 0); + if (fd == -1) + { + logmsg (LOG_CRIT, _("cannot create socket: %s"), + strerror(errno)); + exit (1); + } + if (listen_sockaddr.sa->sa_family == AF_UNIX) + { + struct stat st; + struct sockaddr_un *s_un = (struct sockaddr_un *) listen_sockaddr.sa; + if (stat (s_un->sun_path, &st)) + { + if (errno != ENOENT) + { + logmsg (LOG_CRIT, _("%s: cannot stat socket: %s"), + s_un->sun_path, strerror (errno)); + exit (1); + } + } + else + { + /* FIXME: Check permissions? */ + if (!S_ISSOCK (st.st_mode)) + { + logmsg (LOG_CRIT, _("%s: not a socket"), + s_un->sun_path, strerror (errno)); + exit (1); + } + unlink (s_un->sun_path); + } + /* FIXME: Setup umask */ + } + else + { + int yes = 1; + setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (void *) &yes, sizeof(yes)); + } + + if (bind (fd, listen_sockaddr.sa, listen_sockaddr.len) < 0) + { + logmsg (LOG_CRIT, _("cannot bind to local address: %s"), + strerror (errno)); + close (fd); + exit (1); + } + if (listen (fd, 8) == -1) + { + logmsg (LOG_CRIT, "listen: %s", strerror (errno)); + close (fd); + exit (1); + } + + return fd; +} + +static void +trim_crlf (char *s) +{ + size_t len = strlen (s); + if (len > 0 && s[len-1] == '\n') + { + s[--len] = 0; + if (len > 0 && s[len-1] == '\r') + s[--len] = 0; + } +} + +void +handle_connection (FILE *fp) +{ + char *buf = NULL; + size_t buflen = 0; + const struct spool *spool; + char *p; + struct passwd *pw; + + if (getline (&buf, &buflen, fp) <= 0) + return; + trim_crlf (buf); + if (debug_level) + logmsg (LOG_DEBUG, "recv: %s", buf); + spool = wydawca_find_spool (buf); + if (!spool) + { + fprintf (fp, "- Unknown service name\r\n"); + free (buf); + return; + } + else if (spool->url) + fprintf (fp, "+ OK, URL %s\r\n", spool->url); + else + fprintf (fp, "+ OK, spool %s\r\n", spool->tag); + + if (getline (&buf, &buflen, fp) <= 0) + { + logmsg (LOG_ERR, "protocol error"); + free (buf); + return; + } + + if (debug_level) + logmsg (LOG_DEBUG, "recv: %s", buf); + + p = strchr (buf, ' '); + if (p) + { + *p++ = 0; + while (*p && (*p == ' ' || *p == '\t')) + p++; + } + else + p = ""; + + pw = getpwnam (buf); + if (pw) + schedule_job (spool, pw->pw_uid); + else + logmsg (LOG_ERR, "no such user: %s", buf); + free (buf); +} + +static int reconfigure; +static int terminate; + +RETSIGTYPE +sig_hup (int sig) +{ + reconfigure = 1; + terminate = 1; +} + +RETSIGTYPE +sig_term (int sig) +{ + terminate = 1; +} + +void +wydawca_listener () +{ + int ctlfd = open_listener (); + + job_init (); + signal (SIGHUP, sig_hup); + signal (SIGTERM, sig_term); + signal (SIGQUIT, sig_term); + signal (SIGINT, sig_term); + while (!terminate) + { + int fd; + FILE *fp; + int rc; + fd_set rset; + struct timeval to, *pto; + union { + struct sockaddr sa; + struct sockaddr_in s_in; + struct sockaddr_un s_un; + } addr; + socklen_t len; + + job_queue_runner (); + FD_ZERO (&rset); + FD_SET (ctlfd, &rset); + + if (wakeup_interval) + { + to.tv_sec = wakeup_interval; + to.tv_usec = 0; + *pto = to; + } + else + pto = NULL; + + rc = select (ctlfd + 1, &rset, NULL, NULL, pto); + if (rc == 0) + continue; + else if (rc < 0) + { + if (errno == EINTR) + continue; + logmsg (LOG_ERR, "select: %s", strerror (errno)); + break; + } + + len = sizeof (addr); + fd = accept (ctlfd, (struct sockaddr*) &addr, &len); + if (fd == -1) + continue; + /* FIXME: Check if the connection is allowed */ + fp = fdopen (fd, "r+"); + handle_connection (fp); + fclose (fp); + } +} + + |