diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2020-05-17 17:09:18 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2020-05-17 17:09:18 +0300 |
commit | 49de98a30b8b5b45d30bcd4b8136a5b4cfe80fcd (patch) | |
tree | 459b3abe97d815dd40838be408998b3520ed9142 | |
parent | ebeb897b2b4f81224d541c6711b76e1c360e377a (diff) | |
download | wydawca-49de98a30b8b5b45d30bcd4b8136a5b4cfe80fcd.tar.gz wydawca-49de98a30b8b5b45d30bcd4b8136a5b4cfe80fcd.tar.bz2 |
Fix deadlock at wy_vlog
* src/stat.c: New file.
* src/wydawca.c (wy_log): Don't touch statistic counters. This causes
deadlock when wy_v?log is called from statistic-reporting module.
* src/wydawca.h (wydawca_stat_log): Remove proto.
* src/gpg.c: Increase WY_STAT_ERRORS explicitly.
* src/triplet.c: Likewise.
* src/verify.c: Likewise.
* tests/dry_run01.at: Fix expected output.
* tests/upload01.at: Likewise.
-rw-r--r-- | src/gpg.c | 3 | ||||
-rw-r--r-- | src/stat.c | 160 | ||||
-rw-r--r-- | src/triplet.c | 17 | ||||
-rw-r--r-- | src/verify.c | 18 | ||||
-rw-r--r-- | src/wydawca.c | 12 | ||||
-rw-r--r-- | src/wydawca.h | 1 | ||||
-rw-r--r-- | tests/dry_run01.at | 2 | ||||
-rw-r--r-- | tests/upload01.at | 2 |
8 files changed, 192 insertions, 23 deletions
@@ -121,10 +121,11 @@ static void remove_homedir(void) { wy_debug(2, (_("removing GNUPG home directory: %s"), temp_homedir)); - if (rmdir_r(temp_homedir)) + if (rmdir_r(temp_homedir)) { wy_log(LOG_CRIT, _("failed to remove GPG directory %s"), temp_homedir); } +} /* Create a temporary GPG home directory */ static int diff --git a/src/stat.c b/src/stat.c new file mode 100644 index 0000000..da885f6 --- /dev/null +++ b/src/stat.c @@ -0,0 +1,160 @@ +/* wydawca - automatic release submission daemon + Copyright (C) 2008-2013, 2017, 2019-2020 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> + +/* Cumulative statistic counters */ +static WY_STAT_COUNTER global_stats[WY_MAX_STAT]; +pthread_mutex_t global_stats_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t global_stats_cond = PTHREAD_COND_INITIALIZER; +static int stat_stop; +pthread_t stat_tid; + +void +wydawca_stat_add(int what, size_t n) +{ + if (what >= WY_MAX_STAT) abort(); + pthread_mutex_lock(&global_stats_mutex); + global_stats[what] += n; + pthread_mutex_unlock(&global_stats_mutex); +} + +void +wydawca_stat_notify(int final) +{ + void *ret; + pthread_mutex_lock(&global_stats_mutex); + stat_stop = final; + pthread_cond_broadcast(&global_stats_cond); + pthread_mutex_unlock(&global_stats_mutex); + if (final) + pthread_join(stat_tid, &ret); +} + +static void +stat_reset(void) +{ + int i; + + for (i = 0; i < WY_MAX_STAT; i++) + global_stats[i] = 0; +} + +struct micronent stat_report_schedule; + +static void stat_log(void); +static void stat_reset(void); + +void * +wy_thr_stat(void *ptr) +{ + wy_set_cur_thread_name("WY_stat"); + pthread_mutex_lock(&global_stats_mutex); + while (!stat_stop) { + struct timespec ts; + micron_next_time(&stat_report_schedule, &ts); + pthread_cond_timedwait(&global_stats_cond, &global_stats_mutex, &ts); + stat_log(); + notify_stat(); + stat_reset(); + } + pthread_mutex_unlock(&global_stats_mutex); + return NULL; +} + +void +wydawca_stat_init(void) +{ + pthread_create(&stat_tid, NULL, wy_thr_stat, NULL); +} + +static char *stat_name[WY_MAX_STAT] = { + N_("errors"), + N_("warnings"), + N_("bad signatures"), + N_("access violation attempts"), + N_("complete triplets"), + N_("incomplete triplets"), + N_("bad triplets"), + N_("expired triplets"), + N_("triplet successes"), + N_("files uploaded"), + N_("files archived"), + N_("symlinks created"), + N_("symlinks removed"), + N_("check failures"), +}; + +static char *stat_kwname[WY_MAX_STAT] = { + "stat:errors", + "stat:warnings", + "stat:bad_signatures", + "stat:access_violations", + "stat:complete_triplets", + "stat:incomplete_triplets", + "stat:bad_triplets", + "stat:expired_triplets", + "stat:triplet_success", + "stat:uploads", + "stat:archives", + "stat:symlinks", + "stat:rmsymlinks", + "stat:check_failures" +}; + +int +wy_stat_mask_p(unsigned long mask) +{ + int i; + + for (i = 0; i < WY_MAX_STAT; i++) + if (global_stats[i] != 0 && (mask && WY_STAT_MASK(i))) + return 1; + return 0; +} + +int +wy_stat_expansion(char **ret, char const *name, size_t len) +{ + int i; + + for (i = 0; i < WY_MAX_STAT; i++) { + if (strlen(stat_kwname[i]) == len + && memcmp(stat_kwname[i], name, len) == 0) { + size_t size = 0; + *ret = NULL; + if (grecs_asprintf(ret, &size, "%u", global_stats[i])) + return WRDSE_NOSPACE; + else + return WRDSE_OK; + } + } + return WRDSE_UNDEF; +} + +static void +stat_log(void) +{ + int i; + + if (wy_stat_mask_p(print_stats)) { + for (i = 0; i < WY_MAX_STAT; i++) + if (print_stats & WY_STAT_MASK(i)) + wy_log(LOG_INFO, "%s: %u", + gettext(stat_name[i]), global_stats[i]); + } +} + diff --git a/src/triplet.c b/src/triplet.c index 138a979..0da254e 100644 --- a/src/triplet.c +++ b/src/triplet.c @@ -383,11 +383,13 @@ remove_triplet_unlocked(struct wy_triplet *trp) if (trp->file[i].name) { if (!wy_dry_run) { if (unlinkat(trp->spool->source_fd, trp->file[i].name, 0)) { - if (errno != ENOENT) + if (errno != ENOENT) { + wydawca_stat_incr(WY_STAT_ERRORS); wy_log(LOG_ERR, _("cannot remove %s/%s: %s"), trp->spool->source_dir, trp->file[i].name, strerror(errno)); + } } else wy_log(LOG_NOTICE, _("removing %s/%s"), trp->spool->source_dir, trp->file[i].name); @@ -417,7 +419,8 @@ triplet_commit(struct wy_triplet *trp) { if (spool_open_dictionaries(trp->spool) == 0) { wy_debug(1, (_("processing triplet `%s'"), trp->name)); - process_directives(trp); + if (process_directives(trp)) + wydawca_stat_incr(WY_STAT_ERRORS); } } @@ -535,6 +538,7 @@ static void wy_ws_error(const char *fmt, ...); static inline void report_failed_string(char const *str, size_t len) { + wydawca_stat_incr(WY_STAT_ERRORS); if (len > 32) wy_log(LOG_ERR, _("failed to expand string %16.16s...%16.16s"), str, str + len - 16); @@ -1036,6 +1040,7 @@ wy_triplet_get_uploaders(struct wy_triplet *trp) rc = dictionary_lookup(dict, md, command); free(command); if (rc) { + wydawca_stat_incr(WY_STAT_ERRORS); wy_log(LOG_ERR, _("cannot get uploaders for %s"), trp->name); dictionary_close(dict, md); return NULL; @@ -1043,6 +1048,7 @@ wy_triplet_get_uploaders(struct wy_triplet *trp) nrows = dictionary_num_rows(dict); if (nrows == 0) { + wydawca_stat_incr(WY_STAT_ERRORS); wy_log(LOG_ERR, _("found no uploaders for %s"), trp->name); dictionary_close(dict, md); return NULL; @@ -1050,6 +1056,7 @@ wy_triplet_get_uploaders(struct wy_triplet *trp) ncols = dictionary_num_cols(dict); if (ncols < 4) { + wydawca_stat_incr(WY_STAT_ERRORS); wy_log(LOG_ERR, _("project-uploader dictionary error: " "too few columns (%lu)"), (unsigned long) ncols); @@ -1084,6 +1091,7 @@ wy_triplet_get_uploaders(struct wy_triplet *trp) } if (!info.name || !info.realname || !info.gpg_key || !info.email) { + wydawca_stat_incr(WY_STAT_ERRORS); wy_log(LOG_ERR, _("project-uploader dictionary error: " "malformed row %lu"), (unsigned long) i); @@ -1104,6 +1112,7 @@ wy_triplet_get_uploaders(struct wy_triplet *trp) dictionary_close(dict, md); if (!head) { + wydawca_stat_incr(WY_STAT_ERRORS); wy_log(LOG_ERR, _("no valid uploaders found for %s"), trp->name); return NULL; } @@ -1138,6 +1147,7 @@ wy_triplet_get_admins(struct wy_triplet *trp) dict = spool->dictionary[project_owner_dict]; if (dict->type == dictionary_none) { + wydawca_stat_incr(WY_STAT_ERRORS); wy_log(LOG_ERR, _("%s: dictionary %s not configured (spool %s)"), trp->name, "project_owner_dict", spool->tag); @@ -1146,6 +1156,7 @@ wy_triplet_get_admins(struct wy_triplet *trp) md = dictionary_open(dict); if (!md) { + wydawca_stat_incr(WY_STAT_ERRORS); wy_log(LOG_ERR, _("%s: failed to open dictionary %s (spool %s)"), trp->name, "project_owner_dict", spool->tag); @@ -1157,6 +1168,7 @@ wy_triplet_get_admins(struct wy_triplet *trp) rc = dictionary_lookup(dict, md, command); free(command); if (rc) { + wydawca_stat_incr(WY_STAT_ERRORS); wy_log(LOG_ERR, _("%s: cannot obtain recipient emails"), trp->name); dictionary_close(dict, md); @@ -1167,6 +1179,7 @@ wy_triplet_get_admins(struct wy_triplet *trp) ncols = dictionary_num_cols(dict); if (nrows == 0) { + wydawca_stat_incr(WY_STAT_ERRORS); wy_log(LOG_ERR, _("%s: cannot obtain recipient emails"), trp->name); return NULL; diff --git a/src/verify.c b/src/verify.c index 39626f3..c3b1845 100644 --- a/src/verify.c +++ b/src/verify.c @@ -133,6 +133,7 @@ fill_project_name(struct wy_triplet *trp) trp->blurb = blurb; if (directive_parse(trp)) { + wydawca_stat_incr(WY_STAT_ERRORS); free(blurb); trp->blurb = NULL; return 1; @@ -189,13 +190,18 @@ real_verify_directive_file(struct wy_triplet *trp) if (!wy_triplet_get_uploaders(trp)) return DIRECTIVE_BAD; - if (verify_directive_signature(trp)) { - /*FIXME: Update stats */ + switch (verify_directive_signature(trp)) { + case 0: + wy_debug(1, (_("%s: directive file signature OK"), trp->name)); + break; + case 1: wy_log(LOG_ERR, _("invalid signature for %s"), trp->name ? trp->name : "[unknown]"); return DIRECTIVE_BAD; - } else - wy_debug(1, (_("%s: directive file signature OK"), trp->name)); + case -1: + wydawca_stat_incr(WY_STAT_ERRORS); + return DIRECTIVE_BAD; + } if (wy_debug_level > 1) { int i; @@ -203,8 +209,10 @@ real_verify_directive_file(struct wy_triplet *trp) wy_log(LOG_DEBUG, "directive[%d] = %s", i, trp->directive[i]); } - if (verify_directive_format(trp)) + if (verify_directive_format(trp)) { + wydawca_stat_incr(WY_STAT_ERRORS); return DIRECTIVE_BAD; + } return DIRECTIVE_GOOD; } diff --git a/src/wydawca.c b/src/wydawca.c index 635a158..525e538 100644 --- a/src/wydawca.c +++ b/src/wydawca.c @@ -138,18 +138,6 @@ static void (*log_printer) (int prio, const char *fmt, va_list ap) = void wy_vlog(int prio, char const *fmt, va_list ap) { - switch (prio) { - case LOG_EMERG: - case LOG_ALERT: - case LOG_CRIT: - case LOG_ERR: - wydawca_stat_incr(WY_STAT_ERRORS); - break; - - case LOG_WARNING: - wydawca_stat_incr(WY_STAT_WARNINGS); - } - log_printer(prio, fmt, ap); } diff --git a/src/wydawca.h b/src/wydawca.h index fabd461..cb29ebc 100644 --- a/src/wydawca.h +++ b/src/wydawca.h @@ -365,7 +365,6 @@ extern size_t spool_count; typedef unsigned WY_STAT_COUNTER; -void wydawca_stat_log(void); void wydawca_stat_init(void); void wydawca_stat_notify(int final); void wydawca_stat_add(int what, size_t n); diff --git a/tests/dry_run01.at b/tests/dry_run01.at index 387a9da..ff7294b 100644 --- a/tests/dry_run01.at +++ b/tests/dry_run01.at @@ -63,7 +63,7 @@ sed -e 's/incomplete triplets: 1/incomplete triplets: 0/' err >&2 [wydawca: [[NOTICE]] AT_PACKAGE_TARNAME (AT_PACKAGE_NAME AT_PACKAGE_VERSION) started wydawca: [[ERR]] No public key wydawca: [[ERR]] invalid signature for conversion-1.1.tar -wydawca: [[INFO]] errors: 2 +wydawca: [[INFO]] errors: 0 wydawca: [[INFO]] warnings: 0 wydawca: [[INFO]] bad signatures: 1 wydawca: [[INFO]] access violation attempts: 1 diff --git a/tests/upload01.at b/tests/upload01.at index 279599e..be25c25 100644 --- a/tests/upload01.at +++ b/tests/upload01.at @@ -71,7 +71,7 @@ upload dir [wydawca: [[NOTICE]] AT_PACKAGE_TARNAME (AT_PACKAGE_NAME AT_PACKAGE_VERSION) started wydawca: [[ERR]] No public key wydawca: [[ERR]] invalid signature for conversion-1.1.tar -wydawca: [[INFO]] errors: 2 +wydawca: [[INFO]] errors: 0 wydawca: [[INFO]] warnings: 0 wydawca: [[INFO]] bad signatures: 1 wydawca: [[INFO]] access violation attempts: 1 |