/* 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 . */ #include "wydawca.h" static int open_listener () { int fd; if (listen_sockaddr.sa == NULL) { logmsg (LOG_CRIT, _("listener address is not configured")); exit (EX_CONFIG); } fd = socket (listen_sockaddr.sa->sa_family, SOCK_STREAM, 0); if (fd == -1) { logmsg (LOG_CRIT, _("cannot create socket: %s"), strerror(errno)); exit (EX_OSERR); } 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 (errno == EACCES ? EX_NOPERM : EX_OSERR); } } else { /* FIXME: Check permissions? */ if (!S_ISSOCK (st.st_mode)) { logmsg (LOG_CRIT, _("%s: not a socket"), s_un->sun_path); exit (EX_OSFILE); } 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 (EX_OSERR); } if (listen (fd, 8) == -1) { logmsg (LOG_CRIT, "listen: %s", strerror (errno)); close (fd); exit (EX_OSERR); } 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 *in, FILE *out) { char *buf = NULL; size_t buflen = 0; const struct spool *spool; char *p; struct passwd *pw; if (getline (&buf, &buflen, in) <= 0) return; trim_crlf (buf); if (debug_level) logmsg (LOG_DEBUG, "recv: %s", buf); spool = wydawca_find_spool (buf); if (!spool) { if (all_spool_aliases && gl_list_search (all_spool_aliases, buf)) fprintf (out, "+ OK, all spools\r\n"); else { fprintf (out, "- Unknown service name\r\n"); free (buf); return; } } else if (spool->url) fprintf (out, "+ OK, URL %s\r\n", spool->url); else fprintf (out, "+ OK, spool %s\r\n", spool->tag); if (getline (&buf, &buflen, in) < 0) { logmsg (LOG_ERR, "protocol error"); free (buf); return; } trim_crlf (buf); 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); } 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 *in, *out; 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: Use Mailutils ACLs? */ #ifdef WITH_LIBWRAP if (!tcpwrap_access(fd)) { close(fd); continue; } #endif in = fdopen (fd, "r"); setlinebuf (in); out = fdopen (fd, "w"); setlinebuf (out); handle_connection (in, out); fclose (in); fclose (out); } }