/* wydawca - automatic release submission daemon Copyright (C) 2007, 2009-2013, 2017 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" struct spool_list { struct spool_list *next; struct spool spool; }; static struct spool_list *spool_list; int for_each_spool(int (*fun) (struct spool *, void *), void *data) { struct spool_list *sp; for (sp = spool_list; sp; sp = sp->next) { int rc = fun(&sp->spool, data); if (rc) return rc; } return 0; } void register_spool(struct spool *spool) { struct spool_list *sp = grecs_malloc(sizeof *sp); sp->spool = *spool; sp->next = spool_list; spool_list = sp; } static int spool_check_alias(struct spool *spool, const char *name) { if (spool->aliases && grecs_list_locate(spool->aliases, (char *)name)) return 1; return 0; } struct spool * wydawca_find_spool(const char *name) { struct spool_list *sp; for (sp = spool_list; sp; sp = sp->next) { if (strcmp(sp->spool.tag, name) == 0 || spool_check_alias(&sp->spool, name)) return &sp->spool; } return NULL; } /* Return true if NAME is a directory. If stat fails, return the error code in EC */ int test_dir(const char *name, int *ec) { struct stat st; *ec = 0; if (stat(name, &st)) { *ec = errno; return 1; } return S_ISDIR(st.st_mode) == 0; } /* Return a textual representation of a file TYPE */ const char * file_type_str(enum file_type type) { switch (type) { case file_dist: return "distributive"; case file_signature: return "detached signature"; case file_directive: return "signed upload directive"; } return "UNKNOWN"; } /* Parse file NAME: determine its type and root name and store this information in FINFO */ void parse_file_name(const char *name, struct file_info *finfo) { static struct suffix { const char *suf; unsigned len; enum file_type type; } suftab[] = { { SUF_SIG, SUF_SIG_LEN, file_signature }, { SUF_DIR, SUF_DIR_LEN, file_directive}, { "", 0, file_dist } }; int i; unsigned len = strlen(name); for (i = 0; i < sizeof suftab / sizeof suftab[0]; i++) { if (len >= suftab[i].len && memcmp(name + len - suftab[i].len, suftab[i].suf, suftab[i].len) == 0) { finfo->name = grecs_strdup(name); finfo->type = suftab[i].type; finfo->root_len = len - suftab[i].len; return; } } abort(); /* should not happen */ } void file_info_cleanup(struct file_info *finfo) { free(finfo->name); memset(finfo, 0, sizeof(*finfo)); } static int match_uid_p(uid_t uid, int uc, uid_t * uv) { int i; if (!uv) return 1; for (i = 0; i < uc; i++) if (uv[i] == uid) return 1; return 0; } int spool_cwd_add_new_file(const struct spool *spool, const char *name, int uc, uid_t *uv) { struct stat st; struct file_info finfo; if (stat(name, &st)) { wy_log(LOG_ERR, _("cannot stat file %s/%s: %s"), spool->source_dir, name, strerror(errno)); return -1; } if (!S_ISREG(st.st_mode)) { wy_log(LOG_NOTICE, _("not a regular file: %s/%s"), spool->source_dir, name); return -1; } if (!match_uid_p(st.st_uid, uc, uv)) { wy_debug(1, (_("ignoring file: %s/%s"), spool->source_dir, name)); return -1; } finfo.sb = st; parse_file_name(name, &finfo); wy_debug(1, (_("found file %s: %s, stem: %.*s"), name, file_type_str(finfo.type), finfo.root_len, finfo.name)); register_file(&finfo, spool); return 0; } int spool_add_new_file(const struct spool *spool, const char *name, int uc, uid_t * uv) { wy_debug(1, ("%s -> %s, adding %s", spool->source_dir, wy_url_printable(spool->dest_url), name)); if (chdir(spool->source_dir)) { wy_log(LOG_ERR, _("cannot chdir to %s: %s"), spool->source_dir, strerror(errno)); return -1; } return spool_cwd_add_new_file(spool, name, uc, uv); } /* Scan upload directory from the DPAIR and register all files found there, forming triplets when possible */ void scan_spool_unlocked(struct spool *spool, int uc, uid_t *uv) { DIR *dir; struct dirent *ent; wy_debug(1, ("%s -> %s", spool->source_dir, wy_url_printable(spool->dest_url))); if (chdir(spool->source_dir)) { wy_log(LOG_ERR, _("cannot chdir to %s: %s"), spool->source_dir, strerror(errno)); return; } dir = opendir("."); if (!dir) { wy_log(LOG_ERR, _("cannot open directory %s: %s"), spool->source_dir, strerror(errno)); return; } timer_start("spool"); /* FIXME: prefix spool tag with something */ timer_start(spool->tag); while ((ent = readdir(dir))) { if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; spool_cwd_add_new_file(spool, ent->d_name, uc, uv); } closedir(dir); if (count_collected_triplets() > 0) spool_commit_triplets(spool, NULL); timer_stop(spool->tag); timer_stop("spool"); } int scan_spool(struct spool *spool, int uc, uid_t * uv) { char *lockfile = wydawca_lockname(spool->tag); int rc = wydawca_lock(lockfile); switch (rc) { case LOCK_OK: scan_spool_unlocked(spool, uc, uv); wydawca_unlock(lockfile); break; case LOCK_FAILURE: wy_log(LOG_ERR, _("cannot lock spool %s"), spool->tag); break; case LOCK_RETRY: wy_log(LOG_WARNING, _("timed out while looking spool %s"), spool->tag); break; } free(lockfile); return rc; } int spool_open_dictionaries(struct spool *spool) { if (!spool->dict_inited) { int i; for (i = 0; i < dictionary_count; i++) { if (dictionary_init(spool->dictionary[i])) { wy_log(LOG_ERR, _("failed to initialize dictionary %d"), i); return -1; } } spool->dict_inited = 1; } return 0; } void spool_close_dictionaries(struct spool *spool) { int i; for (i = 0; i < NITEMS(spool->dictionary); i++) dictionary_done(spool->dictionary[i]); spool->dict_inited = 0; } /* Scan all configured update directories */ int scan_all_spools(int uidc, uid_t * uidv) { struct spool_list *sp; int rc = 0; timer_start("wydawca"); for (sp = spool_list; sp; sp = sp->next) if (enabled_spool_p(&sp->spool)) if (scan_spool(&sp->spool, uidc, uidv)) rc++; for (sp = spool_list; sp; sp = sp->next) spool_close_dictionaries(&sp->spool); timer_stop("wydawca"); return rc; } void spool_create_timers() { struct spool_list *sp; for (sp = spool_list; sp; sp = sp->next) timer_start(sp->spool.tag); }