123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834 |
- #include <sys/param.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include <fcntl.h>
- #include <netdb.h>
- #include <errno.h>
- #include <unistd.h>
- #include <stdarg.h>
- #include <sys/file.h>
- #include <syslog.h>
- #include <glib.h>
-
- #include "config.h"
- #ifdef HAVE_LIBUTIL_H
- #include <libutil.h>
- #endif
- #include "util.h"
- #include "cfg_file.h"
-
- sig_atomic_t do_reopen_log = 0;
-
- int
- event_make_socket_nonblocking (int fd)
- {
- if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
- return -1;
- }
- return 0;
- }
-
- static int
- make_socket_ai (struct addrinfo *ai)
- {
- struct linger linger;
- int fd, on = 1, r;
- int serrno;
-
- /* Create listen socket */
- fd = socket(AF_INET, SOCK_STREAM, 0);
- if (fd == -1) {
- return (-1);
- }
-
- if (event_make_socket_nonblocking(fd) < 0)
- goto out;
-
- if (fcntl(fd, F_SETFD, 1) == -1) {
- goto out;
- }
-
- setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on));
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
- linger.l_onoff = 1;
- linger.l_linger = 5;
- setsockopt(fd, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger));
-
- r = bind(fd, ai->ai_addr, ai->ai_addrlen);
-
- if (r == -1) {
- if (errno != EINPROGRESS) {
- goto out;
- }
- }
-
- return (fd);
-
- out:
- serrno = errno;
- close(fd);
- errno = serrno;
- return (-1);
- }
-
- int
- make_socket (const char *address, u_short port)
- {
- int fd;
- struct addrinfo ai, *aitop = NULL;
- char strport[NI_MAXSERV];
- int ai_result;
-
- memset (&ai, 0, sizeof (ai));
- ai.ai_family = AF_INET;
- ai.ai_socktype = SOCK_STREAM;
- ai.ai_flags = AI_PASSIVE;
- snprintf (strport, sizeof (strport), "%d", port);
- if ((ai_result = getaddrinfo (address, strport, &ai, &aitop)) != 0) {
- return (-1);
- }
-
- fd = make_socket_ai (aitop);
-
- freeaddrinfo (aitop);
-
- return (fd);
- }
-
- int
- make_unix_socket (const char *path, struct sockaddr_un *addr)
- {
- size_t len = strlen (path);
- int sock;
-
- if (len > sizeof (addr->sun_path) - 1) return -1;
-
- #ifdef FREEBSD
- addr->sun_len = sizeof (struct sockaddr_un);
- #endif
-
- addr->sun_family = AF_UNIX;
-
- strncpy (addr->sun_path, path, len);
-
- sock = socket (PF_LOCAL, SOCK_STREAM, 0);
-
- if (sock != -1) {
- if (bind (sock, (struct sockaddr *) addr, sizeof (struct sockaddr_un)) == -1) return -1;
- }
-
- return sock;
- }
-
- void
- read_cmd_line (int argc, char **argv, struct config_file *cfg)
- {
- int ch;
- while ((ch = getopt(argc, argv, "hfc:")) != -1) {
- switch (ch) {
- case 'f':
- cfg->no_fork = 1;
- break;
- case 'c':
- if (optarg && cfg->cfg_name) {
- cfg->cfg_name = memory_pool_strdup (cfg->cfg_pool, optarg);
- }
- break;
- case 'h':
- case '?':
- default:
- /* Show help message and exit */
- printf ("Rspamd version " RVERSION "\n"
- "Usage: rspamd [-h] [-n] [-f] [-c config_file]\n"
- "-h: This help message\n"
- "-f: Do not daemonize main process\n"
- "-c: Specify config file (./rspamd.conf is used by default)\n");
- exit (0);
- break;
- }
- }
- }
-
- int
- write_pid (struct rspamd_main *main)
- {
- pid_t pid;
- main->pfh = pidfile_open (main->cfg->pid_file, 0644, &pid);
-
- if (main->pfh == NULL) {
- return -1;
- }
-
- pidfile_write (main->pfh);
-
- return 0;
- }
-
- void
- init_signals (struct sigaction *signals, sig_t sig_handler)
- {
- /* Setting up signal handlers */
- /* SIGUSR1 - reopen config file */
- /* SIGUSR2 - worker is ready for accept */
- sigemptyset(&signals->sa_mask);
- sigaddset(&signals->sa_mask, SIGTERM);
- sigaddset(&signals->sa_mask, SIGINT);
- sigaddset(&signals->sa_mask, SIGHUP);
- sigaddset(&signals->sa_mask, SIGCHLD);
- sigaddset(&signals->sa_mask, SIGUSR1);
- sigaddset(&signals->sa_mask, SIGUSR2);
-
-
- signals->sa_handler = sig_handler;
- sigaction (SIGTERM, signals, NULL);
- sigaction (SIGINT, signals, NULL);
- sigaction (SIGHUP, signals, NULL);
- sigaction (SIGCHLD, signals, NULL);
- sigaction (SIGUSR1, signals, NULL);
- sigaction (SIGUSR2, signals, NULL);
- }
-
- void
- pass_signal_worker (struct workq *workers, int signo)
- {
- struct rspamd_worker *cur;
- TAILQ_FOREACH (cur, workers, next) {
- kill (cur->pid, signo);
- }
- }
-
- void convert_to_lowercase (char *str, unsigned int size)
- {
- while (size--) {
- *str = tolower (*str);
- str ++;
- }
- }
-
- #ifndef HAVE_SETPROCTITLE
-
- static char *title_buffer = 0;
- static size_t title_buffer_size = 0;
- static char *title_progname, *title_progname_full;
-
- int
- setproctitle (const char *fmt, ...)
- {
- if (!title_buffer || !title_buffer_size) {
- errno = ENOMEM;
- return -1;
- }
-
- memset (title_buffer, '\0', title_buffer_size);
-
- ssize_t written;
-
- if (fmt) {
- ssize_t written2;
- va_list ap;
-
- written = snprintf (title_buffer, title_buffer_size, "%s: ", title_progname);
- if (written < 0 || (size_t) written >= title_buffer_size)
- return -1;
-
- va_start (ap, fmt);
- written2 =
- vsnprintf (title_buffer + written,
- title_buffer_size - written, fmt, ap);
- va_end (ap);
- if (written2 < 0
- || (size_t) written2 >= title_buffer_size - written)
- return -1;
- } else {
- written =
- snprintf (title_buffer, title_buffer_size, "%s",
- title_progname);
- if (written < 0 || (size_t) written >= title_buffer_size)
- return -1;
- }
-
- written = strlen (title_buffer);
- memset (title_buffer + written, '\0', title_buffer_size - written);
-
- return 0;
- }
-
- /*
- It has to be _init function, because __attribute__((constructor))
- functions gets called without arguments.
- */
-
- int
- init_title (int argc, char *argv[], char *envp[])
- {
- char *begin_of_buffer = 0, *end_of_buffer = 0;
- int i;
-
- for (i = 0; i < argc; ++i) {
- if (!begin_of_buffer)
- begin_of_buffer = argv[i];
- if (!end_of_buffer || end_of_buffer + 1 == argv[i])
- end_of_buffer = argv[i] + strlen (argv[i]);
- }
-
- for (i = 0; envp[i]; ++i) {
- if (!begin_of_buffer)
- begin_of_buffer = envp[i];
- if (!end_of_buffer || end_of_buffer + 1 == envp[i])
- end_of_buffer = envp[i] + strlen(envp[i]);
- }
-
- if (!end_of_buffer)
- return 0;
-
- char **new_environ = g_malloc ((i + 1) * sizeof (envp[0]));
-
- if (!new_environ)
- return 0;
-
- for (i = 0; envp[i]; ++i) {
- if (!(new_environ[i] = g_strdup (envp[i])))
- goto cleanup_enomem;
- }
- new_environ[i] = 0;
-
- if (program_invocation_name) {
- title_progname_full = g_strdup (program_invocation_name);
-
- if (!title_progname_full)
- goto cleanup_enomem;
-
- char *p = strrchr (title_progname_full, '/');
-
- if (p)
- title_progname = p + 1;
- else
- title_progname = title_progname_full;
-
- program_invocation_name = title_progname_full;
- program_invocation_short_name = title_progname;
- }
-
- environ = new_environ;
- title_buffer = begin_of_buffer;
- title_buffer_size = end_of_buffer - begin_of_buffer;
-
- return 0;
-
- cleanup_enomem:
- for (--i; i >= 0; --i) {
- g_free (new_environ[i]);
- }
- g_free (new_environ);
- return 0;
- }
- #endif
-
- #ifndef HAVE_PIDFILE
- extern char * __progname;
- static int _pidfile_remove (struct pidfh *pfh, int freeit);
-
- static int
- pidfile_verify (struct pidfh *pfh)
- {
- struct stat sb;
-
- if (pfh == NULL || pfh->pf_fd == -1)
- return (-1);
- /*
- * Check remembered descriptor.
- */
- if (fstat (pfh->pf_fd, &sb) == -1)
- return (errno);
- if (sb.st_dev != pfh->pf_dev || sb.st_ino != pfh->pf_ino)
- return -1;
- return 0;
- }
-
- static int
- pidfile_read (const char *path, pid_t *pidptr)
- {
- char buf[16], *endptr;
- int error, fd, i;
-
- fd = open (path, O_RDONLY);
- if (fd == -1)
- return (errno);
-
- i = read (fd, buf, sizeof(buf) - 1);
- error = errno; /* Remember errno in case close() wants to change it. */
- close (fd);
- if (i == -1)
- return error;
- else if (i == 0)
- return EAGAIN;
- buf[i] = '\0';
-
- *pidptr = strtol (buf, &endptr, 10);
- if (endptr != &buf[i])
- return EINVAL;
-
- return 0;
- }
-
- struct pidfh *
- pidfile_open (const char *path, mode_t mode, pid_t *pidptr)
- {
- struct pidfh *pfh;
- struct stat sb;
- int error, fd, len, count;
- struct timespec rqtp;
-
- pfh = g_malloc (sizeof(*pfh));
- if (pfh == NULL)
- return NULL;
-
- if (path == NULL)
- len = snprintf (pfh->pf_path, sizeof(pfh->pf_path),
- "/var/run/%s.pid", __progname);
- else
- len = snprintf (pfh->pf_path, sizeof(pfh->pf_path),
- "%s", path);
- if (len >= (int)sizeof (pfh->pf_path)) {
- g_free (pfh);
- errno = ENAMETOOLONG;
- return NULL;
- }
-
- /*
- * Open the PID file and obtain exclusive lock.
- * We truncate PID file here only to remove old PID immediatelly,
- * PID file will be truncated again in pidfile_write(), so
- * pidfile_write() can be called multiple times.
- */
- fd = open (pfh->pf_path,
- O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, mode);
- flock (fd, LOCK_EX | LOCK_NB);
- if (fd == -1) {
- count = 0;
- rqtp.tv_sec = 0;
- rqtp.tv_nsec = 5000000;
- if (errno == EWOULDBLOCK && pidptr != NULL) {
- again:
- errno = pidfile_read (pfh->pf_path, pidptr);
- if (errno == 0)
- errno = EEXIST;
- else if (errno == EAGAIN) {
- if (++count <= 3) {
- nanosleep (&rqtp, 0);
- goto again;
- }
- }
- }
- g_free (pfh);
- return NULL;
- }
- /*
- * Remember file information, so in pidfile_write() we are sure we write
- * to the proper descriptor.
- */
- if (fstat (fd, &sb) == -1) {
- error = errno;
- unlink (pfh->pf_path);
- close (fd);
- g_free (pfh);
- errno = error;
- return NULL;
- }
-
- pfh->pf_fd = fd;
- pfh->pf_dev = sb.st_dev;
- pfh->pf_ino = sb.st_ino;
-
- return pfh;
- }
-
- int
- pidfile_write (struct pidfh *pfh)
- {
- char pidstr[16];
- int error, fd;
-
- /*
- * Check remembered descriptor, so we don't overwrite some other
- * file if pidfile was closed and descriptor reused.
- */
- errno = pidfile_verify (pfh);
- if (errno != 0) {
- /*
- * Don't close descriptor, because we are not sure if it's ours.
- */
- return -1;
- }
- fd = pfh->pf_fd;
-
- /*
- * Truncate PID file, so multiple calls of pidfile_write() are allowed.
- */
- if (ftruncate (fd, 0) == -1) {
- error = errno;
- _pidfile_remove (pfh, 0);
- errno = error;
- return -1;
- }
-
- snprintf (pidstr, sizeof(pidstr), "%u", getpid ());
- if (pwrite (fd, pidstr, strlen (pidstr), 0) != (ssize_t)strlen (pidstr)) {
- error = errno;
- _pidfile_remove (pfh, 0);
- errno = error;
- return -1;
- }
-
- return 0;
- }
-
- int
- pidfile_close (struct pidfh *pfh)
- {
- int error;
-
- error = pidfile_verify (pfh);
- if (error != 0) {
- errno = error;
- return -1;
- }
-
- if (close (pfh->pf_fd) == -1)
- error = errno;
- g_free (pfh);
- if (error != 0) {
- errno = error;
- return -1;
- }
- return 0;
- }
-
- static int
- _pidfile_remove (struct pidfh *pfh, int freeit)
- {
- int error;
-
- error = pidfile_verify (pfh);
- if (error != 0) {
- errno = error;
- return -1;
- }
-
- if (unlink (pfh->pf_path) == -1)
- error = errno;
- if (flock (pfh->pf_fd, LOCK_UN) == -1) {
- if (error == 0)
- error = errno;
- }
- if (close (pfh->pf_fd) == -1) {
- if (error == 0)
- error = errno;
- }
- if (freeit)
- g_free (pfh);
- else
- pfh->pf_fd = -1;
- if (error != 0) {
- errno = error;
- return -1;
- }
- return 0;
- }
-
- int
- pidfile_remove (struct pidfh *pfh)
- {
-
- return (_pidfile_remove (pfh, 1));
- }
- #endif
-
- /*
- * Functions for parsing expressions
- */
-
- struct expression_stack {
- char op;
- struct expression_stack *next;
- };
-
- /*
- * Push operand or operator to stack
- */
- static struct expression_stack*
- push_expression_stack (memory_pool_t *pool, struct expression_stack *head, char op)
- {
- struct expression_stack *new;
- new = memory_pool_alloc (pool, sizeof (struct expression_stack));
- new->op = op;
- new->next = head;
- return new;
- }
-
- /*
- * Delete symbol from stack, return pointer to operand or operator (casted to void* )
- */
- static char
- delete_expression_stack (struct expression_stack **head)
- {
- struct expression_stack *cur;
- char res;
-
- if(*head == NULL) return 0;
-
- cur = *head;
- res = cur->op;
-
- *head = cur->next;
- return res;
- }
-
- /*
- * Return operation priority
- */
- static int
- logic_priority (char a)
- {
- switch (a) {
- case '!':
- return 3;
- case '|':
- case '&':
- return 2;
- case '(':
- return 1;
- default:
- return 0;
- }
- }
-
- /*
- * Return 0 if symbol is not operation symbol (operand)
- * Return 1 if symbol is operation symbol
- */
- static int
- is_operation_symbol (char a)
- {
- switch (a) {
- case '!':
- case '&':
- case '|':
- case '(':
- case ')':
- return 1;
- default:
- return 0;
- }
- }
-
- static void
- insert_expression (memory_pool_t *pool, struct expression **head, int type, char op, void *operand)
- {
- struct expression *new, *cur;
-
- new = memory_pool_alloc (pool, sizeof (struct expression));
- new->type = type;
- if (new->type == EXPR_OPERAND) {
- new->content.operand = operand;
- }
- else {
- new->content.operation = op;
- }
- new->next = NULL;
-
- if (!*head) {
- *head = new;
- }
- else {
- cur = *head;
- while (cur->next) {
- cur = cur->next;
- }
- cur->next = new;
- }
- }
-
- /*
- * Make inverse polish record for specified expression
- * Memory is allocated from given pool
- */
- struct expression*
- parse_expression (memory_pool_t *pool, char *line)
- {
- struct expression *expr = NULL;
- struct expression_stack *stack = NULL;
- char *p, *c, *str, op, in_regexp = 0;
-
- if (line == NULL || pool == NULL) {
- return NULL;
- }
-
- p = line;
- c = p;
- while (*p) {
- if (is_operation_symbol (*p) && !in_regexp) {
- if (c != p) {
- /* Copy operand */
- str = memory_pool_alloc (pool, p - c + 1);
- strlcpy (str, c, (p - c + 1));
- insert_expression (pool, &expr, EXPR_OPERAND, 0, str);
- }
- if (*p == ')') {
- if (stack == NULL) {
- return NULL;
- }
- /* Pop all operators from stack to nearest '(' or to head */
- while (stack->op != '(') {
- op = delete_expression_stack (&stack);
- if (op != '(') {
- insert_expression (pool, &expr, EXPR_OPERATION, op, NULL);
- }
- }
- }
- else if (*p == '(') {
- /* Push it to stack */
- stack = push_expression_stack (pool, stack, *p);
- }
- else {
- if (stack == NULL) {
- stack = push_expression_stack (pool, stack, *p);
- }
- /* Check priority of logic operation */
- else {
- if (logic_priority (stack->op) < logic_priority (*p)) {
- stack = push_expression_stack (pool, stack, *p);
- }
- else {
- /* Pop all operations that have higher priority than this one */
- while((stack != NULL) && (logic_priority (stack->op) >= logic_priority (*p))) {
- op = delete_expression_stack (&stack);
- if (op != '(') {
- insert_expression (pool, &expr, EXPR_OPERATION, op, NULL);
- }
- }
- stack = push_expression_stack (pool, stack, *p);
- }
- }
- }
- c = p + 1;
- }
- if (*p == '/' && (p == line || *(p - 1) != '\\')) {
- in_regexp = !in_regexp;
- }
- p++;
- }
- /* Write last operand if it exists */
- if (c != p) {
- /* Copy operand */
- str = memory_pool_alloc (pool, p - c + 1);
- strlcpy (str, c, (p - c + 1));
- insert_expression (pool, &expr, EXPR_OPERAND, 0, str);
- }
- /* Pop everything from stack */
- while(stack != NULL) {
- op = delete_expression_stack (&stack);
- if (op != '(') {
- insert_expression (pool, &expr, EXPR_OPERATION, op, NULL);
- }
- }
-
- return expr;
- }
-
- /* Logging utility functions */
- int
- open_log (struct config_file *cfg)
- {
- switch (cfg->log_type) {
- case RSPAMD_LOG_CONSOLE:
- /* Do nothing with console */
- return 0;
- case RSPAMD_LOG_SYSLOG:
- openlog ("rspamd", LOG_NDELAY | LOG_PID, cfg->log_facility);
- return 0;
- case RSPAMD_LOG_FILE:
- cfg->log_fd = open (cfg->log_file, O_CREAT | O_WRONLY | O_APPEND);
- if (cfg->log_fd == -1) {
- msg_err ("open_log: cannot open desired log file: %s, %m", cfg->log_file);
- return -1;
- }
- return 0;
- }
- }
-
- int
- reopen_log (struct config_file *cfg)
- {
- do_reopen_log = 0;
- switch (cfg->log_type) {
- case RSPAMD_LOG_CONSOLE:
- /* Do nothing with console */
- return 0;
- case RSPAMD_LOG_SYSLOG:
- closelog ();
- break;
- case RSPAMD_LOG_FILE:
- close (cfg->log_fd);
- break;
- }
- return open_log (cfg);
- }
-
- void
- syslog_log_function (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer arg)
- {
- struct config_file *cfg = (struct config_file *)arg;
- if (do_reopen_log) {
- reopen_log (cfg);
- }
-
- if (log_level <= cfg->log_level) {
- if (log_level >= G_LOG_LEVEL_DEBUG) {
- syslog (LOG_DEBUG, message);
- }
- else if (log_level >= G_LOG_LEVEL_INFO) {
- syslog (LOG_INFO, message);
- }
- else if (log_level >= G_LOG_LEVEL_WARNING) {
- syslog (LOG_WARNING, message);
- }
- else if (log_level >= G_LOG_LEVEL_CRITICAL) {
- syslog (LOG_ERR, message);
- }
- }
- }
-
- void
- file_log_function (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer arg)
- {
- struct config_file *cfg = (struct config_file *)arg;
- char tmpbuf[128];
- int r;
- struct iovec out[3];
-
- if (cfg->log_fd == -1) {
- return;
- }
-
- if (do_reopen_log) {
- reopen_log (cfg);
- }
-
- if (log_level <= cfg->log_level) {
- r = snprintf (tmpbuf, sizeof (tmpbuf), "#%d: %d rspamd ", (int)getpid (), (int)time (NULL));
- out[0].iov_base = tmpbuf;
- out[0].iov_len = r;
- out[1].iov_base = (char *)message;
- out[1].iov_len = strlen (message);
- out[2].iov_base = "\r\n";
- out[2].iov_len = 2;
-
- writev (cfg->log_fd, out, sizeof (out) / sizeof (out[0]));
- }
- }
- /*
- * vi:ts=4
- */
|