/* wydawca - automatic release submission daemon Copyright (C) 2007-2013 Sergey Poznyakoff This program 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. This program 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 this program. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "grecs.h" #include "wordsplit.h" #define SP(s) ((s) ? (s) : "NONE") #define WYDAWCA_EX_AGAIN 1 /* The range of directive versions we accept (major * 100 + minor) */ #define MIN_DIRECTIVE_VERSION 101 #define MAX_DIRECTIVE_VERSION 102 /* Default modes for mkdir and creat commands: rely on the umask value */ #define MKDIR_PERMISSIONS 0777 #define CREAT_PERMISSIONS 0666 #define SUF_SIG ".sig" #define SUF_SIG_LEN (sizeof (SUF_SIG) - 1) #define SUF_DIR ".directive.asc" #define SUF_DIR_LEN (sizeof (SUF_DIR) - 1) #define NITEMS(a) (sizeof(a)/sizeof((a)[0])) enum dictionary_id { project_uploader_dict, /* Contains names, gpg-keys, emails and real names of the project uploaders */ project_owner_dict, /* Contains names and emails of the project owners */ dictionary_count }; enum dictionary_type { dictionary_none, /* Undefined or no dictionary */ dictionary_sql, /* Use SQL database */ dictionary_builtin, /* Use built-in facilities */ dictionary_external /* Invoke an external program */ }; struct dictionary { enum dictionary_id id; enum dictionary_type type; /* Dictionary type */ char *query; /* Query template */ int parmc; /* Number of entries in paramv */ char **parmv; /* Parameters. The semantics differs depending on type. For SQL: 0 - Identifier of the SQL struct to use; */ int init_passed; /* Initialization count */ char *result; /* Result storage */ size_t result_size; /* Size of result */ unsigned ncol; /* Number of columns per row */ unsigned nrow; /* Number of rows */ void *storage; }; enum backup_type { no_backups, /* Don't make backups */ simple_backups, /* Make only simple backups */ numbered_existing_backups, /* Make numbered backups for files that already have such backups and simple backups for the rest */ numbered_backups, /* Make only numbered backups */ }; extern char const *simple_backup_suffix; char *find_backup_file_name(char const *, enum backup_type); /* Archive types */ enum archive_type { archive_none, /* No archivation requested */ archive_directory, /* Archive by moving files to a separate directory hierarchy */ archive_tar /* Archive by appending to a tar file (tar -r) */ }; struct archive_descr { enum archive_type type; /* Archivation type */ char *name; /* Directory name if type==archive_directory, archive file name if type==archive_tar */ enum backup_type backup_type; /* Requested backup type if type == archive_directory */ }; /* Type of file in a triplet */ enum file_type { file_dist, /* Something to be distributed */ file_signature, /* Detached signature (.sig) */ file_directive, /* Directive (.directive.asc) */ }; #define FILE_TYPE_COUNT (file_directive+1) /* Part of a triplet */ struct file_info { enum file_type type; /* Part type */ char *name; /* File name */ unsigned root_len; /* Length of root part in name */ struct stat sb; }; /* File triplet */ struct wy_triplet { char *name; /* Triplet base name */ struct file_info file[FILE_TYPE_COUNT]; /* Components */ unsigned version; /* Protocol version */ const struct spool *spool; /* Owning spool */ char *relative_dir; /* Directory relative to spool->dest_dir */ char **directive; /* Decoded directive pairs (key: value\0) */ char *blurb; /* Block of directives: directive[i] points here */ char *tmp; /* Temporary storage */ size_t tmpsize; /* Size of memory allocated in tmp */ struct txtacc *acc; /* Text accumulator for string allocation */ /* Triplets are joined in two doubly-linked lists: 1) a cronological list, with prev pointing to a triplet older than this one, and next pointing to a triplet newer than it: */ struct wy_triplet *prev, *next; /* 2) "job queue", a list of triplets processed by the same job: */ struct wy_triplet *jq_prev, *jq_next; /* The job queue is used only when triplets are processed by the inotify watcher. In that case, the job member points to the job processing this request. When the job terminates, all requests belonging to it are removed from the table to avoid them being processed by subsequent jobs. If started as a cron job, or awoken by a TCP listener, both job and jq_prev, jq_next are NULL. */ struct job *job; /* User data */ struct wy_user *uploader_list; struct wy_user *uploader; /* Admin data */ struct wy_user *admin_list; /* 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. */ /* The directive file is always present in the triplet */ #define TRIPLET_UID(t) ((t)->file[file_directive].sb.st_uid) /* GID is filled in after the triplet is finalized and verified */ #define TRIPLET_GID(t) ((t)->file[file_directive].sb.st_gid) typedef struct wy_url *wy_url_t; wy_url_t wy_url_create(const char *str); void wy_url_free(wy_url_t url); const char *wy_url_path(wy_url_t url); const char *wy_url_scheme(wy_url_t url); const char *wy_url_printable(wy_url_t url); struct virt_tab { int (*test_url) (wy_url_t url, grecs_locus_t * loc); int (*move_file) (struct wy_triplet * trp, enum file_type file_id); int (*archive_file) (struct wy_triplet * trp, const char *file_name); int (*symlink_file) (struct wy_triplet * trp, const char *wanted_src, const char *wanted_dst); int (*rmsymlink_file) (struct wy_triplet * trp, const char *file_name); }; /* An upload spool. This structure contains all data necessary for releasing files from source to destination */ struct spool { char *tag; struct grecs_list *aliases; char *url; /* Download URL */ char *source_dir; /* Source directory */ wy_url_t dest_url; /* Destination URL */ const char *dest_dir; /* Directory part of the above */ struct virt_tab vtab; /* Virtual method table */ int inotify_enable; time_t file_sweep_time; /* Remove invalid/unprocessed files after this amount of time */ struct dictionary *dictionary[dictionary_count]; int dict_inited; struct archive_descr archive; /* Archivation data */ struct notification *notification; char *check_script; }; #define ASGN_SPOOL(spool, trp, faction) do { \ spool = (trp)->spool; \ if (!spool) { \ wy_log (LOG_CRIT, \ _("INTERNAL ERROR at %s:%d: " \ "spool not defined for %s"), \ __FILE__, __LINE__, (trp)->name); \ faction; \ } \ } while (0) enum wydawca_stat { STAT_ERRORS, STAT_WARNINGS, STAT_BAD_SIGNATURE, 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_FAIL, MAX_STAT }; #define STAT_MASK(c) (1<<(c)) #define STAT_MASK_NONE 0 #define STAT_MASK_ALL (STAT_MASK(MAX_STAT) - 1) int wy_cb_statistics(enum grecs_callback_command cmd, grecs_node_t *node, void *varptr, void *cb_data); struct notification { struct notification *next; enum wy_event ev; int statmask; char *modname; void *modcfg; grecs_node_t *modnode; }; void notify(struct notification *, struct wy_triplet *, enum wy_event); void notify_stats(void); void notify_flush(struct spool *sp); char *meta_expand_string(const char *string, struct wy_metadef *def, void *data, struct dictionary *dict, void *handle); void meta_free(struct wy_metadef *def); struct wy_metadef *metadef_join(struct wy_metadef *a, struct wy_metadef *b); /* Modules */ struct module { struct module *next; char *name; char *path; grecs_locus_t locus; grecs_node_t *modinit; void *handle; int (*open) (grecs_node_t *); void *(*config) (grecs_node_t *); int (*notify) (void *, int, struct wy_triplet *); void (*flush) (void *); void (*close) (void); }; int cb_module(enum grecs_callback_command cmd, grecs_node_t *node, void *varptr, void *cb_data); void modules_load(void); void modules_close(void); void modules_help(void); int module_set_init(const char *name, grecs_node_t *node); extern struct grecs_list *module_load_path, *module_prepend_load_path; void module_notify(const char *name, void *modcfg, enum wy_event ev, struct wy_triplet *tpl); void module_flush(const char *name, void *modcfg); /* Global variables */ extern char *program_name; extern uid_t wydawca_uid; extern gid_t wydawca_gid; extern size_t wydawca_supp_groupc; extern gid_t *wydawca_supp_groups; extern char *conffile; /* Configuration file name */ extern int syslog_include_prio; /* Syslog priority indication */ extern time_t file_sweep_time; /* Unlink stale file after this amount of time */ extern char *tar_command_name; /* Name of the tar command */ extern unsigned wydawca_stat[MAX_STAT]; extern unsigned long print_stats; extern int archive_signatures; extern char *pidfile; extern int force_startup; extern char *lockdir; extern time_t lock_expire_time; extern time_t lock_timeout; extern int enable_locking; extern int daemon_mode; extern time_t wakeup_interval; extern int foreground; extern int single_process; extern struct grecs_sockaddr listen_sockaddr; extern struct grecs_list *all_spool_aliases; extern char *default_check_script; extern char *temp_homedir; extern unsigned min_directive_version; extern unsigned max_directive_version; extern struct spool fake_spool; extern struct spool inotify_spool; extern int inotify_enable; extern struct notification *default_notification; #define UPDATE_STATS(what) do { \ if (what >= MAX_STAT) abort(); \ wydawca_stat[what]++; \ } while (0) int stat_mask_p(unsigned long mask); struct wy_metadef *make_stat_expansion(size_t count); void initstats(void); void logstats(void); /* Utility functions */ char *safe_file_name(char *file_name); char *safe_file_name_alloc(const char *file_name); size_t trim_length(const char *str); size_t trim(char *str); int test_dir(const char *name, int *ec); char *create_directory(const char *base, const char *name); int create_hierarchy(char *dir, size_t baselen); void parse_config(void); void log_output(int prio, const char *prog, FILE * fp); enum exec_result { exec_success, /* Command executed and returned 0 */ exec_fail, /* Command executed and returned not 0 */ exec_error /* Command failed to execute */ }; enum exec_result wydawca_exec(int argc, const char **argv, int *retcode); /* Directory scanning and registering */ int scan_spool(struct spool *spool, int uc, uid_t * uv); int scan_all_spools(int, uid_t *); void spool_create_timers(void); int spool_add_new_file(const struct spool *spool, const char *name, int uc, uid_t * uv); int spool_cwd_add_new_file(const struct spool *spool, const char *name, int uc, uid_t * uv); int spool_open_dictionaries(struct spool *spool); void spool_close_dictionaries(struct spool *spool); void parse_file_name(const char *name, struct file_info *finfo); void file_info_cleanup(struct file_info *finfo); int for_each_spool(int (*fun) (struct spool *, void *), void *data); void register_spool(struct spool *spool); struct spool *wydawca_find_spool(const char *name); void register_file(struct file_info *finfo, const struct spool *spool); void spool_commit_triplets(struct spool *, struct wy_triplet *); struct wy_triplet *link_processable_triplets(void); size_t count_collected_triplets(void); char *triplet_expand_dictionary_query(struct dictionary *dict, void *handle, struct wy_triplet const *trp); void triplet_remove_file(struct spool *spool, const char *name); time_t triplet_sweep(void); int remove_triplet(struct wy_triplet *trp, int check); /* General-purpose dictionary support */ struct dictionary *dictionary_new(enum dictionary_id id, enum dictionary_type type); int dictionary_init(struct dictionary *dict); int dictionary_done(struct dictionary *dict); void *dictionary_open(struct dictionary *dict); int dictionary_close(struct dictionary *dict, void *handle); int dictionary_lookup(struct dictionary *dict, void *handle, const char *cmd); void dictionary_copy_result(struct dictionary *dict, const char *res, size_t size); const char *dictionary_result(struct dictionary *dict, void *handle, unsigned nrow, unsigned ncol); int dictionary_quote_string(struct dictionary *dict, void *handle, const char *input, char **poutput, size_t *psize); unsigned dictionary_num_rows(struct dictionary *dict); unsigned dictionary_num_cols(struct dictionary *dict); /* Verification functions */ int verify_directive_file(struct wy_triplet *trp, int noath); int verify_directive_signature(struct wy_triplet *trp); int verify_detached_signature(struct wy_triplet *trp); int fill_project_name(struct wy_triplet *trp); struct wy_user *uploader_find_frp(struct wy_user *list, const char *frp); /* Directive file support */ int directive_parse(struct wy_triplet *trp); int directive_get_value(struct wy_triplet *trp, const char *key, const char **pval); int directive_pack_version(const char *val, unsigned *pversion); int directive_unpack_version(unsigned version, char **pbuf, size_t * psize); int directive_version_in_range_p(struct wy_triplet *trp, unsigned from, unsigned to); int verify_directive_format(struct wy_triplet *trp); int directive_first(struct wy_triplet *trp, const char **pkey, const char **pval); int directive_next(struct wy_triplet *trp, int n, const char **pkey, const char **pval); int process_directives(struct wy_triplet *trp); int enabled_spool_p(const struct spool *spool); int selected_spools(void); char *triplet_strdup(struct wy_triplet *tp, const char *str); int parse_time_interval(const char *str, time_t * pint, const char **endp); /* config.c */ void config_init(void); void config_finish(struct grecs_node *); void config_help(void); int wy_assert_string_arg(grecs_locus_t *, enum grecs_callback_command, const grecs_value_t *); grecs_value_t *get_arg(grecs_value_t *value, unsigned n, int type); int wy_strtofac(const char *str); int wy_strtopri(const char *str); const char *wy_pritostr(int pri); const char *wy_factostr(int fac); /* vtab.c */ int url_to_vtab(wy_url_t url, struct virt_tab *vtab); int move_file(struct wy_triplet *trp, enum file_type file_id); int archive_file(struct wy_triplet *trp, const char *file_name); int symlink_file(struct wy_triplet *trp, const char *wanted_src, const char *wanted_dst); int rmsymlink_file(struct wy_triplet *trp, const char *file_name); /* 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(wy_url_t url, grecs_locus_t * locus); int dir_move_file(struct wy_triplet *trp, enum file_type file_id); int dir_archive_file(struct wy_triplet *trp, const char *reldir); int dir_symlink_file(struct wy_triplet *trp, const char *wanted_src, const char *wanted_dst); int dir_rmsymlink_file(struct wy_triplet *trp, const char *file_name); /* null.c */ int null_move_file(struct wy_triplet *trp, enum file_type file_id); int null_archive_file(struct wy_triplet *trp, const char *file_name); int null_symlink_file(struct wy_triplet *trp, const char *wanted_src, const char *wanted_dst); int null_rmsymlink_file(struct wy_triplet *trp, const char *file_name); /* timer.c */ typedef struct timer_slot *wydawca_timer_t; wydawca_timer_t timer_get(const char *name); wydawca_timer_t timer_start(const char *name); wydawca_timer_t timer_stop(const char *name); wydawca_timer_t timer_reset(const char *name); double timer_get_real(wydawca_timer_t t); double timer_get_user(wydawca_timer_t t); double timer_get_system(wydawca_timer_t t); char *timer_format_time(double t); size_t timer_get_count(void); void timer_fill_meta(struct wy_metadef *def, size_t num); void timer_free_meta(struct wy_metadef *def, size_t num); void report_init(void); void report_add(const char *fmt, ...); void report_finish(void); extern char *report_string; /* job.c */ void schedule_job(struct spool *spool, uid_t uid, struct wy_triplet *tp); void job_init(void); void job_queue_runner(time_t min_timeout); void triplet_jq_unlink(struct wy_triplet *tp); /* profile.c */ void check_pidfile(void); void remove_pidfile(void); /* net.c */ void wydawca_listener(void); void trim_crlf(char *s); #define LOCK_OK 0 #define LOCK_CONFLICT 1 #define LOCK_RETRY 2 #define LOCK_INVALID 3 #define LOCK_FAILURE 4 char *wydawca_lockname(const char *tag); int wydawca_lock(const char *lockname); void wydawca_unlock(const char *lockname); void wydawca_lock_init(void); /* tcpwrap.h */ extern struct grecs_keyword tcpwrapper_kw[]; int tcpwrap_access(int fd); /* userprivs.c */ int wydawca_userprivs(uid_t uid, gid_t gid, gid_t * grplist, size_t ngrp); int push_dir(const char *dirname); int pop_dir(void); char *getcwd_alloc(void); struct txtacc *txtacc_create(void); void txtacc_free(struct txtacc *acc); void txtacc_free_string(struct txtacc *acc, char *str); void txtacc_grow(struct txtacc *acc, const char *buf, size_t size); #define txtacc_1grow(acc, c) do { \ char __ch = c; \ txtacc_grow (acc, &__ch, 1); \ } while (0) char *txtacc_finish(struct txtacc *acc, int steal); #ifdef WITH_INOTIFY int watcher_init(void); int watcher_run(int); #else # define watcher_init() -1 # define watcher_run(c) #endif