/* wydawca - automatic release submission daemon Copyright (C) 2007, 2009-2013, 2017, 2019-2022 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" #include char *program_name; uid_t wydawca_uid; gid_t wydawca_gid; size_t wydawca_supp_groupc; gid_t *wydawca_supp_groups; int wydawca_runas; /* Set to 1 if any of the four variables above have been set in the configuration */ char *conffile = SYSCONFDIR "/wydawca.conf"; const char *wy_version = "wydawca (" PACKAGE_STRING ")"; int wy_debug_level; int wy_dry_run; int wy_log_to_stderr = -1; /* -1 means autodetect */ int wy_log_facility = LOG_DAEMON; char *wy_syslog_tag = "wydawca"; int syslog_include_prio; /* syslog messages include priority */ int wy_log_max_severity = LOG_DEBUG; unsigned long print_stats; /* Print final statistics output */ time_t file_sweep_time = DEFAULT_FILE_SWEEP_TIME; char *tar_command_name = "tar"; int archive_signatures = 1; /* Archive sig files by default */ int lint_mode; int wy_mode = WY_MODE_NORMAL; int wy_mode_option = -1; int preprocess_only = 0; int foreground_option = -1; int foreground; time_t wakeup_interval; struct grecs_list *all_spool_aliases; char *wy_gpg_homedir; char *default_check_script; unsigned min_directive_version = MIN_DIRECTIVE_VERSION; unsigned max_directive_version = MAX_DIRECTIVE_VERSION; int inotify_enable = 1; /* Logging */ static pthread_key_t strbuf_key; static pthread_once_t strbuf_key_once = PTHREAD_ONCE_INIT; struct strbuf { char *buf; size_t size; }; static void strbuf_free(void *f) { struct strbuf *sb = f; free(sb->buf); free(sb); } static void make_strbuf_key(void) { pthread_key_create(&strbuf_key, strbuf_free); } static struct strbuf * syslog_get_strbuf(void) { struct strbuf *sb; pthread_once(&strbuf_key_once, make_strbuf_key); if ((sb = pthread_getspecific(strbuf_key)) == NULL) { sb = grecs_zalloc(sizeof(*sb)); pthread_setspecific(strbuf_key, sb); } return sb; } void syslog_printer(int prio, const char *fmt, va_list ap) { if (syslog_include_prio) { struct strbuf *sb = syslog_get_strbuf(); const char *p = wy_pritostr(prio); size_t size = strlen(p) + 3 + strlen(fmt) + 1; if (size > sb->size) { sb->size = size; sb->buf = grecs_realloc(sb->buf, sb->size); } sprintf(sb->buf, "[%s] %s", p, fmt); fmt = sb->buf; } #if HAVE_VSYSLOG vsyslog(prio, fmt, ap); #else char buf[128]; vsnprintf(buf, sizeof buf, fmt, ap); syslog(prio, "%s", buf); #endif } static pthread_mutex_t stderr_mutex = PTHREAD_MUTEX_INITIALIZER; void stderr_printer(int prio, const char *fmt, va_list ap) { const char *p = wy_pritostr(prio); pthread_mutex_lock(&stderr_mutex); fprintf(stderr, "%s: ", program_name); if (p) fprintf(stderr, "[%s] ", p); vfprintf(stderr, fmt, ap); fputc('\n', stderr); pthread_mutex_unlock(&stderr_mutex); } static void (*log_printer) (int prio, const char *fmt, va_list ap) = stderr_printer; void wy_vlog(int prio, char const *fmt, va_list ap) { if ((prio & 0x07) <= wy_log_max_severity) log_printer(prio, fmt, ap); } void wy_log(int prio, char const *fmt, ...) { va_list ap; va_start(ap, fmt); wy_vlog(prio, fmt, ap); va_end(ap); } void wy_dbg(char const *fmt, ...) { va_list ap; va_start(ap, fmt); log_printer(LOG_DEBUG, fmt, ap); va_end(ap); } void grecs_print_diag(grecs_locus_t const *locus, int err, int errcode, const char *msg) { char *locstr = NULL; if (locus) { size_t size = 0; if (locus->beg.col == 0) grecs_asprintf(&locstr, &size, "%s:%u", locus->beg.file, locus->beg.line); else if (strcmp(locus->beg.file, locus->end.file)) grecs_asprintf(&locstr, &size, "%s:%u.%u-%s:%u.%u", locus->beg.file, locus->beg.line, locus->beg.col, locus->end.file, locus->end.line, locus->end.col); else if (locus->beg.line != locus->end.line) grecs_asprintf(&locstr, &size, "%s:%u.%u-%u.%u", locus->beg.file, locus->beg.line, locus->beg.col, locus->end.line, locus->end.col); else grecs_asprintf(&locstr, &size, "%s:%u.%u-%u", locus->beg.file, locus->beg.line, locus->beg.col, locus->end.col); } if (locstr) { if (errcode) wy_log(err ? LOG_ERR : LOG_WARNING, "%s: %s: %s", locstr, msg, strerror(errcode)); else wy_log(err ? LOG_ERR : LOG_WARNING, "%s: %s", locstr, msg); free(locstr); } else { if (errcode) wy_log(err ? LOG_ERR : LOG_WARNING, "%s: %s", msg, strerror(errcode)); else wy_log(err ? LOG_ERR : LOG_WARNING, "%s", msg); } } #include "cmdline.h" void version_hook(FILE * stream) { printf("Compiled with:"); #ifdef WITH_LIBWRAP printf(" libwrap"); #endif #ifdef WITH_INOTIFY printf(" inotify"); #endif putchar('\n'); } int fatal_signals[] = { SIGHUP, SIGINT, SIGQUIT, SIGTERM, 0 }; static void sigign(int sig) { } void wy_main(void) { struct sigaction act; sigset_t sigs; int i; pthread_t tid; wy_set_cur_thread_name("wydawca"); act.sa_flags = 0; sigemptyset(&act.sa_mask); act.sa_handler = sigign; /* Block the 'fatal signals' and SIGPIPE in the handling thread */ sigemptyset(&sigs); for (i = 0; fatal_signals[i]; i++) { sigaddset(&sigs, fatal_signals[i]); sigaction(fatal_signals[i], &act, NULL); } sigaddset(&sigs, SIGPIPE); sigaddset(&sigs, SIGALRM); sigaddset(&sigs, SIGCHLD); pthread_sigmask(SIG_BLOCK, &sigs, NULL); if (wy_mode == WY_MODE_DAEMON) { if (!foreground) { if (daemon(0, 0)) { wy_log(LOG_ERR, "%s", strerror(errno)); exit(EX_OSERR); } wy_log(LOG_NOTICE, _("daemon launched")); } wydawca_stat_init(); scan_all_spools(); pidfile_update(); /* Start cleaner thread */ pthread_create(&tid, NULL, wy_thr_cleaner, NULL); /* Start listener thread */ pthread_create(&tid, NULL, wy_thr_listen, NULL); /* Unblock only the fatal signals */ sigemptyset(&sigs); for (i = 0; fatal_signals[i]; i++) { sigaddset(&sigs, fatal_signals[i]); } pthread_sigmask(SIG_UNBLOCK, &sigs, NULL); /* Wait for signal to arrive */ sigwait(&sigs, &i); wy_log(LOG_NOTICE, "shutting down on signal \"%s\"", strsignal(i)); } else { wydawca_stat_init(); scan_all_spools(); /* Unblock only the fatal signals */ sigemptyset(&sigs); for (i = 0; fatal_signals[i]; i++) { sigaddset(&sigs, fatal_signals[i]); } pthread_sigmask(SIG_UNBLOCK, &sigs, NULL); wy_triplet_wait(); } wydawca_stat_notify(1); } int main(int argc, char **argv) { struct grecs_node *tree; const char *p; program_name = argv[0]; grecs_print_diag_fun = grecs_print_diag; proginfo.print_version_hook = version_hook; config_init(); parse_options(argc, argv); wydawca_uid = getuid(); wydawca_gid = getgid(); argv += optind; argc -= optind; p = gpgme_check_version("1.1.0"); if (!p) { wy_log(LOG_CRIT, _("GPGMe version check failed")); exit(EX_UNAVAILABLE); } else wy_debug(4, (_("using GPGME version %s"), p)); if (argc) { wy_log(LOG_CRIT, _("extra arguments")); exit(EX_UNAVAILABLE); } if (preprocess_only) exit(grecs_preprocess(conffile, 0) ? EX_CONFIG : 0); tree = grecs_parse(conffile); if (!tree) exit(EX_CONFIG); config_finish(tree); modules_load(); if (lint_mode && wy_debug_level) { grecs_print_node(tree, GRECS_NODE_FLAG_DEFAULT | (wy_debug_level > 1 ? GRECS_NODE_FLAG_LOCUS : 0), stdout); fputc('\n', stdout); } grecs_tree_free(tree); if (lint_mode) exit(0); if (wy_dry_run || selected_spools()) wy_mode_option = WY_MODE_CRON; if (wy_mode_option != -1) wy_mode = wy_mode_option; if (foreground_option >= 0) foreground = foreground_option; if (wy_log_to_stderr == -1) { switch (wy_mode) { case WY_MODE_NORMAL: wy_log_to_stderr = 1; break; case WY_MODE_CRON: wy_log_to_stderr = 0; break; case WY_MODE_DAEMON: wy_log_to_stderr = foreground; break; } } grecs_log_to_stderr = wy_log_to_stderr; if (!wy_log_to_stderr) { openlog(wy_syslog_tag, LOG_PID, wy_log_facility); log_printer = syslog_printer; } if (getgid() == 0) { if (wydawca_uid == 0) { if (!force_startup) { wy_log(LOG_CRIT, _("won't run with root privileges")); exit(EX_UNAVAILABLE); } } else if (wydawca_runas && wydawca_userprivs(wydawca_uid, wydawca_gid, wydawca_supp_groups, wydawca_supp_groupc)) exit(EX_UNAVAILABLE); } wy_log(LOG_NOTICE, _("wydawca (%s) started"), PACKAGE_STRING); if (!wy_dry_run) pidfile_check(); wy_main(); dictionaries_close(); modules_close(); pidfile_remove(); wy_log(LOG_NOTICE, _("wydawca (%s) finished"), PACKAGE_STRING); exit(0); }