/* 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);
}