From c8b326ef70fbfc06483ae249219a1d4ab09c8bfe Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Sat, 2 Jan 2010 13:15:46 +0200 Subject: Implement distribution tarball checking. * src/config.c (event_args): New event "check-failure" (event_types): New event type ev_check_fail. (spool_kw,wydawca_kw): New keyword check-script. * src/directive.c (save_script) (stderr_redirector,run_check_script): New functions. (external_check): New function. (process_directives): Call external_check before actually moving the files. * src/gpg.c (homedir): Rename to temp_homedir, now global. * src/net.c (trim_crlf): Remove static qualifier. * src/triplet.c (hash_triplet_free): Free check_diag. (expand_triplet_full,expand_triplet_upload) (expand_triplet_sig,expand_triplet_directive): Rename to expand_triplet_ls_full,expand_triplet_ls_upload, expand_triplet_ls_sig,expand_triplet_ls_directive, correspondigly. (expand_check_diagn,expand_check_result) (expand_triplet_dist,expand_triplet_sig) (expand_triplet_dir): New functions. (triplet_meta): Renames: triplet:full => triplet:ls:full triplet:upload => triplet:ls:upload triplet:dist => triplet:ls:dist triplet:sig => triplet:ls:sig triplet:dir => triplet:ls:dir New keywords: triplet:dist, triplet:sig, triplet:dir, check:result, check:diagn. * src/wydawca.c (default_check_script): New global. (stat_name): New statistics counter "check failures". * src/wydawca.h (struct file_triplet): New members check_result, check_diag. (struct spool): New member check_script. (wydawca_stat): New value STAT_CHECK_FAIL. (notification_event): New value ev_check_fail. (default_check_script, temp_homedir): New externs. (concat_dir, copy_file, trim_crlf): New protos. * doc/wydawca.texi: Update. * configure.ac, NEWS: Version 2.0.90 --- src/config.c | 11 ++- src/directive.c | 290 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/gpg.c | 20 ++-- src/net.c | 2 +- src/triplet.c | 100 +++++++++++++++---- src/wydawca.c | 3 +- src/wydawca.h | 10 ++ 7 files changed, 401 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/src/config.c b/src/config.c index 51f2865..28734d0 100644 --- a/src/config.c +++ b/src/config.c @@ -150,6 +150,7 @@ static const char *event_args[] = { "bad-ownership", "bad-directive-signature", "bad-detached-signature", + "check-failure", NULL }; @@ -157,7 +158,8 @@ static int event_types[] = { ev_success, ev_bad_ownership, ev_bad_directive_signature, - ev_bad_detached_signature + ev_bad_detached_signature, + ev_check_fail }; ARGMATCH_VERIFY (event_args, event_types); @@ -1180,7 +1182,9 @@ static struct grecs_keyword spool_kw[] = { { "notify-event", NULL, N_("Configure notification"), grecs_type_section, NULL, offsetof(struct spool, notification), cb_notify_event, NULL, notify_event_kw }, - + { "check-script", NULL, N_("A /bin/sh script to verify the tarball"), + grecs_type_string, NULL, offsetof(struct spool, check_script) }, + { NULL } }; @@ -1457,6 +1461,9 @@ static struct grecs_keyword wydawca_kw[] = { grecs_type_section, default_dictionary, 0, cb_dictionary, NULL, dictionary_kw }, + { "check-script", NULL, N_("A /bin/sh script to verify tarballs"), + grecs_type_string, &default_check_script }, + { "spool", N_("tag: string"), N_("Define distribution spool"), grecs_type_section, NULL, 0, cb_spool, NULL, spool_kw }, diff --git a/src/directive.c b/src/directive.c index b52c18d..f648e07 100644 --- a/src/directive.c +++ b/src/directive.c @@ -300,6 +300,293 @@ verify_directive_format (struct file_triplet *trp) return 0; } + + +static char * +save_script (const char *script) +{ + char *file_name; + mode_t old_mask; + int fd; + size_t length; + + file_name = concat_dir (temp_homedir, "chkXXXXXX", NULL); + old_mask = umask (0077); + fd = mkstemp (file_name); + umask (old_mask); + if (fd == -1) + { + logmsg (LOG_CRIT, _("cannot create temporary script file %s: %s"), + file_name, strerror (errno)); + free (file_name); + return NULL; + } + + length = strlen (script); + while (length) + { + ssize_t wrb = write (fd, script, length); + if (wrb == -1) + { + logmsg (LOG_CRIT, _("error writing to temporary script file %s: %s"), + file_name, strerror (errno)); + break; + } + if (wrb == 0) + { + logmsg (LOG_CRIT, _("short write to temporary script file %s"), + file_name); + break; + } + + length -= wrb; + script += wrb; + } + close (fd); + if (length) + { + free (file_name); + return NULL; + } + return file_name; +} + +static int +stderr_redirector (const char *tag) +{ + int p[2]; + pid_t pid; + + if (pipe (p)) + { + logmsg (LOG_CRIT, "redirector pipe: %s", strerror (errno)); + return -1; + } + + pid = fork (); + if (pid == -1) + { + logmsg (LOG_CRIT, "redirector fork: %s", strerror (errno)); + return -1; + } + + if (pid == 0) + { + FILE *fp; + size_t size = 0; + char *buf = NULL; + + close (p[1]); + fp = fdopen (p[0], "r"); + if (!fp) + _exit (127); + while (getline (&buf, &size, fp) >= 0) + { + trim_crlf (buf); + logmsg (LOG_NOTICE, "%s: %s", tag, buf); + } + _exit (0); + } + + close (p[0]); + return p[1]; +} + +static int +run_check_script (const char *script, struct file_triplet *trp, + const char *descr) +{ + static char *script_file; + pid_t pid; + int status; + int p[2]; + RETSIGTYPE (*oldsig)(); + FILE *fp; + char *buf; + size_t size, total; + struct obstack stk; + + if (debug_level > 1) + logmsg (LOG_DEBUG, _("prep script: %20.20s%s"), + script, strlen (script) > 20 ? "..." : ""); + script_file = save_script (script); + if (!script_file) + return 1; + if (debug_level > 1) + logmsg (LOG_DEBUG, _("script file: %s"), script_file); + + if (pipe (p)) + { + logmsg (LOG_CRIT, "pipe: %s", strerror (errno)); + return 1; + } + oldsig = signal (SIGCHLD, SIG_DFL); + pid = fork (); + if (pid == -1) + { + logmsg (LOG_CRIT, "fork: %s", strerror (errno)); + free (script_file); + close (p[0]); + close (p[1]); + signal (SIGCHLD, oldsig); + return 1; + } + if (pid == 0) + { + int i; + int efd; + char *argv[4]; + const struct spool *spool = trp->spool; + + signal (SIGHUP, SIG_DFL); + signal (SIGTERM, SIG_DFL); + signal (SIGQUIT, SIG_DFL); + signal (SIGINT, SIG_DFL); + signal (SIGCHLD, SIG_DFL); + signal (SIGALRM, SIG_DFL); + + efd = stderr_redirector (script_file); + if (efd == -1) + _exit (127); + + for (i = getdtablesize (); i > 0; i--) + { + if (i != p[1] && i != efd) + close (i); + } + + if (p[1] != 1 && dup2 (p[1], 1) != 1) + { + logmsg (LOG_CRIT, "cannot duplicate script's stdout: %s", + strerror (errno)); + _exit (127); + } + + if (efd != 2 && dup2 (efd, 2) != 2) + { + logmsg (LOG_CRIT, "cannot duplicate script's stderr: %s", + strerror (errno)); + _exit (127); + } + + setenv ("WYDAWCA_SPOOL", spool->tag, 1); + setenv ("WYDAWCA_SOURCE", spool->source_dir, 1); + setenv ("WYDAWCA_DEST", spool->dest_dir, 1); + setenv ("WYDAWCA_URL", spool->url, 1); + setenv ("WYDAWCA_TRIPLET_BASE", trp->name, 1); + setenv ("WYDAWCA_DIST_FILE", trp->file[file_dist].name, 1); + + chdir (temp_homedir); + + argv[0] = "-sh"; + argv[1] = script_file; + argv[2] = NULL; + + execv ("/bin/sh", argv); + _exit (127); + } + + /* Master */ + free (script_file); + close (p[1]); + fp = fdopen (p[0], "r"); + buf = NULL; + size = total = 0; + obstack_init (&stk); + if (debug_level > 2) + logmsg (LOG_DEBUG, _("reading script output...")); + while (getline (&buf, &size, fp) > 0) + { + size_t len = strlen (buf); + if (debug_level > 2) + logmsg (LOG_DEBUG, _("read: %s"), buf); + obstack_grow (&stk, buf, len); + total += size; + } + obstack_1grow (&stk, 0); + if (debug_level > 2) + logmsg (LOG_DEBUG, _("bytes read: %lu"), (unsigned long)total); + + fclose (fp); + + waitpid (pid, &status, 0); + signal (SIGCHLD, oldsig); + + if (total) + trp->check_diag = xstrdup (obstack_finish (&stk)); + obstack_free (&stk, NULL); + + trp->check_result = status; + if (WIFEXITED (status)) + { + status = WEXITSTATUS (status); + if (status) + { + logmsg (LOG_ERR, "%s for triplet %s, spool %s returned %d", + descr, trp->name, trp->spool->tag, status); + return 1; + } + else if (debug_level > 2) + logmsg (LOG_DEBUG, "%s for triplet %s, spool %s returned %d", + descr, trp->name, trp->spool->tag, status); + } + else if (WIFSIGNALED (status)) + { + int sig = WTERMSIG (status); + logmsg (LOG_NOTICE, + "%s for triplet %s, spool %s terminated on signal %d", + descr, trp->name, trp->spool->tag, sig); + return 1; + } + else + { + logmsg (LOG_NOTICE, + "%s for triplet %s, spool %s terminated with unhandled status", + descr, trp->name, trp->spool->tag); + return 1; + } + return 0; +} + +static int +external_check (struct file_triplet *trp) +{ + int rc; + const struct spool *spool = trp->spool; + char *file; + + if (!trp->file[file_dist].name) + return 0; + if (!spool->check_script && !default_check_script) + return 0; + + file = concat_dir (temp_homedir, trp->file[file_dist].name, NULL); + if (copy_file (trp->file[file_dist].name, file)) + { + free (file); + return 1; + } + + rc = 0; + if (spool->check_script) + rc |= run_check_script (spool->check_script, trp, + _("spool check script")); + + if (rc == 0 && default_check_script) + rc |= run_check_script (default_check_script, trp, + _("default check script")); + + free (file); + + if (rc) + { + UPDATE_STATS (STAT_CHECK_FAIL); + notify (spool->notification, trp, ev_check_fail); + } + + return rc; +} + /* Process the directives from TRP, using given SPOOL */ int process_directives (struct file_triplet *trp, const struct spool *spool) @@ -327,6 +614,7 @@ process_directives (struct file_triplet *trp, const struct spool *spool) break; case directory_dir: + /* FIXME: Alloc it in triplet */ relative_dir = safe_file_name_alloc (val); if (!relative_dir || relative_dir[0] == '/') { @@ -340,6 +628,8 @@ process_directives (struct file_triplet *trp, const struct spool *spool) rc = verify_detached_signature (trp, spool); if (rc == 0) { + if (external_check (trp)) + return 1; if (move_file (trp, spool, file_dist, relative_dir) || move_file (trp, spool, file_signature, relative_dir)) return 1; diff --git a/src/gpg.c b/src/gpg.c index f833142..cca5048 100644 --- a/src/gpg.c +++ b/src/gpg.c @@ -33,7 +33,7 @@ } \ while (0) -static char *homedir; +char *temp_homedir; static int rmdir_r (const char *name); @@ -122,29 +122,29 @@ static void remove_homedir () { if (debug_level > 1) - logmsg (LOG_DEBUG, _("removing GNUPG home directory: %s"), homedir); - if (rmdir_r (homedir)) - logmsg (LOG_CRIT, _("failed to remove GPG directory %s"), homedir); + logmsg (LOG_DEBUG, _("removing GNUPG home directory: %s"), temp_homedir); + if (rmdir_r (temp_homedir)) + logmsg (LOG_CRIT, _("failed to remove GPG directory %s"), temp_homedir); } /* Create a temporary GPG home directory */ static int create_gpg_homedir () { - if (homedir) + if (temp_homedir) return 0; - homedir = xstrdup ("/tmp/wydawca-XXXXXX"); - if (!mkdtemp (homedir)) + temp_homedir = xstrdup ("/tmp/wydawca-XXXXXX"); + if (!mkdtemp (temp_homedir)) { logmsg (LOG_CRIT, _("cannot create GPG home directory (%s): %s"), - homedir, strerror (errno)); + temp_homedir, strerror (errno)); return 1; } atexit (remove_homedir); if (debug_level > 1) - logmsg (LOG_DEBUG, _("GNUPG home directory: %s"), homedir); - setenv ("GNUPGHOME", homedir, 1); + logmsg (LOG_DEBUG, _("GNUPG home directory: %s"), temp_homedir); + setenv ("GNUPGHOME", temp_homedir, 1); return 0; } diff --git a/src/net.c b/src/net.c index 2379cb3..e6bb4ab 100644 --- a/src/net.c +++ b/src/net.c @@ -82,7 +82,7 @@ open_listener () return fd; } -static void +void trim_crlf (char *s) { size_t len = strlen (s); diff --git a/src/triplet.c b/src/triplet.c index dbba486..f18f573 100644 --- a/src/triplet.c +++ b/src/triplet.c @@ -52,6 +52,7 @@ hash_triplet_free (void *data) free (tp->directive); free (tp->blurb); free (tp->tmp); + free (tp->check_diag); /* FIXME: free uploader list */ free (tp); @@ -368,7 +369,7 @@ format_file_data (struct file_triplet *trp, enum file_type type, char **pret) } static const char * -expand_triplet_full (struct metadef *def, void *data) +expand_triplet_ls_full (struct metadef *def, void *data) { struct file_triplet *trp = data; char *buf[FILE_TYPE_COUNT] = { NULL, NULL, NULL }; @@ -406,7 +407,7 @@ expand_triplet_full (struct metadef *def, void *data) } static const char * -expand_triplet_upload (struct metadef *def, void *data) +expand_triplet_ls_upload (struct metadef *def, void *data) { struct file_triplet *trp = data; char *buf[2] = { NULL, NULL }; @@ -439,6 +440,27 @@ expand_triplet_upload (struct metadef *def, void *data) static const char * expand_triplet_dist (struct metadef *def, void *data) +{ + struct file_triplet *trp = data; + return trp->file[file_dist].name; +} + +static const char * +expand_triplet_sig (struct metadef *def, void *data) +{ + struct file_triplet *trp = data; + return trp->file[file_signature].name; +} + +static const char * +expand_triplet_dir (struct metadef *def, void *data) +{ + struct file_triplet *trp = data; + return trp->file[file_directive].name; +} + +static const char * +expand_triplet_ls_dist (struct metadef *def, void *data) { struct file_triplet *trp = data; format_file_data (trp, file_dist, &def->storage); @@ -447,7 +469,7 @@ expand_triplet_dist (struct metadef *def, void *data) } static const char * -expand_triplet_sig (struct metadef *def, void *data) +expand_triplet_ls_sig (struct metadef *def, void *data) { struct file_triplet *trp = data; format_file_data (trp, file_signature, &def->storage); @@ -456,7 +478,7 @@ expand_triplet_sig (struct metadef *def, void *data) } static const char * -expand_triplet_directive (struct metadef *def, void *data) +expand_triplet_ls_directive (struct metadef *def, void *data) { struct file_triplet *trp = data; format_file_data (trp, file_directive, &def->storage); @@ -509,6 +531,37 @@ expand_comment (struct metadef *def, void *data) return def->value; } +static const char * +expand_check_diagn (struct metadef *def, void *data) +{ + struct file_triplet *trp = data; + return trp->check_diag; +} + +static const char * +expand_check_result (struct metadef *def, void *data) +{ + struct file_triplet *trp = data; + int status = trp->check_result; + char sbuf[INT_BUFSIZE_BOUND (uintmax_t)]; + + if (status == 0) + return def->value = "0"; + else if (WIFEXITED (status)) + { + char *p = umaxtostr (WEXITSTATUS (status), sbuf); + def->storage = xstrdup (p); + } + else if (WIFSIGNALED (status)) + { + char *p = umaxtostr (WTERMSIG (status), sbuf); + asprintf (&def->storage, "SIG+%s", p); + } + else + def->storage = "[unrecognized return code]"; + return def->value = def->storage; +} + #define DECL_EXPAND_TIMER(what) \ static const char * \ __cat2__(expand_timer_,what) (struct metadef *def, void *data) \ @@ -555,23 +608,28 @@ triplet_expand_dictionary_query (struct dictionary *dict, void *handle, } struct metadef triplet_meta[] = { - { "project", NULL, expand_project_base, NULL }, - { "url", NULL, expand_url, NULL }, - { "spool", NULL, expand_tag, NULL }, - { "dir", NULL, expand_relative_dir, NULL }, - { "dest-dir", NULL, expand_dest_dir, NULL }, - { "source-dir", NULL, expand_source_dir, NULL }, - { "triplet:full", NULL, expand_triplet_full, NULL }, - { "triplet:upload", NULL, expand_triplet_upload, NULL }, - { "triplet:dist", NULL, expand_triplet_dist, NULL }, - { "triplet:sig", NULL, expand_triplet_sig, NULL }, - { "triplet:dir", NULL, expand_triplet_directive, NULL }, - { "user", NULL, expand_user_name, NULL }, - { "user:name", NULL, expand_user_name, NULL }, - { "user:real-name", NULL, expand_user_real_name, NULL }, - { "user:email", NULL, expand_user_email, NULL }, - { "report", NULL, expand_report, NULL }, - { "comment", NULL, expand_comment, NULL }, + { "project", NULL, expand_project_base, NULL }, + { "url", NULL, expand_url, NULL }, + { "spool", NULL, expand_tag, NULL }, + { "dir", NULL, expand_relative_dir, NULL }, + { "dest-dir", NULL, expand_dest_dir, NULL }, + { "source-dir", NULL, expand_source_dir, NULL }, + { "triplet:dist", NULL, expand_triplet_dist, NULL }, + { "triplet:sig", NULL, expand_triplet_sig, NULL }, + { "triplet:dir", NULL, expand_triplet_dir, NULL }, + { "triplet:ls:full", NULL, expand_triplet_ls_full, NULL }, + { "triplet:ls:upload", NULL, expand_triplet_ls_upload, NULL }, + { "triplet:ls:dist", NULL, expand_triplet_ls_dist, NULL }, + { "triplet:ls:sig", NULL, expand_triplet_ls_sig, NULL }, + { "triplet:ls:dir", NULL, expand_triplet_ls_directive, NULL }, + { "check:result", NULL, expand_check_result, NULL }, + { "check:diagn", NULL, expand_check_diagn, NULL }, + { "user", NULL, expand_user_name, NULL }, + { "user:name", NULL, expand_user_name, NULL }, + { "user:real-name", NULL, expand_user_real_name, NULL }, + { "user:email", NULL, expand_user_email, NULL }, + { "report", NULL, expand_report, NULL }, + { "comment", NULL, expand_comment, NULL }, DECL_FULL_TIMER(wydawca), DECL_FULL_TIMER(triplet), DECL_FULL_TIMER(spool), diff --git a/src/wydawca.c b/src/wydawca.c index 5dc73e7..fc1dea2 100644 --- a/src/wydawca.c +++ b/src/wydawca.c @@ -46,7 +46,7 @@ int single_process; time_t wakeup_interval; gl_list_t all_spool_aliases; char *wydawca_gpg_homedir; - +char *default_check_script; struct grecs_sockaddr listen_sockaddr; unsigned wydawca_stat[MAX_STAT]; @@ -142,6 +142,7 @@ static char *stat_name[MAX_STAT] = { N_("files archived"), N_("symlinks created"), N_("symlinks removed"), + N_("check failures"), }; static char *stat_kwname[MAX_STAT] = { diff --git a/src/wydawca.h b/src/wydawca.h index b2e79d4..af24e47 100644 --- a/src/wydawca.h +++ b/src/wydawca.h @@ -184,6 +184,8 @@ struct file_triplet struct uploader_info *uploader; /* Special data for template formatting */ char *project; /* Triplet project name (if known) */ + int check_result; /* Result of external check */ + char *check_diag; /* External check diagnostics */ }; /* Macros to access owner UID and GID. */ @@ -233,6 +235,7 @@ struct spool struct dictionary *dictionary[dictionary_count]; struct archive_descr archive; /* Archivation data */ struct notification *notification; + char *check_script; }; @@ -251,6 +254,7 @@ enum wydawca_stat STAT_ARCHIVES, STAT_SYMLINKS, STAT_RMSYMLINKS, + STAT_CHECK_FAIL, MAX_STAT }; @@ -265,6 +269,7 @@ enum notification_event ev_bad_ownership, ev_bad_directive_signature, ev_bad_detached_signature, + ev_check_fail, MAX_EVENT }; @@ -347,6 +352,8 @@ extern struct grecs_sockaddr listen_sockaddr; extern gl_list_t all_spool_aliases; extern char *wydawca_gpg_homedir; +extern char *default_check_script; +extern char *temp_homedir; #define UPDATE_STATS(what) \ do \ @@ -477,6 +484,8 @@ rmsymlink_file (struct file_triplet *trp, const struct spool *spool, /* diskio.c */ +char *concat_dir (const char *base, const char *name, size_t *pbaselen); +int copy_file (const char *file, const char *dst_file); int dir_test_url (mu_url_t url, grecs_locus_t *locus); int dir_move_file (struct file_triplet *trp, const struct spool *spool, @@ -536,6 +545,7 @@ void remove_pidfile (void); /* net.c */ void wydawca_listener (void); +void trim_crlf (char *s); #define LOCK_OK 0 -- cgit v1.2.1