diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2007-08-22 13:23:03 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2007-08-22 13:23:03 +0000 |
commit | 180ca1d87d2bf69d9dbb0acb76422e9ae15f930c (patch) | |
tree | b14651206aaec8e03fdfdce9e04433068fa62648 | |
parent | 708a28a2f5bd2384e4c254a47d069ec4d9ef697e (diff) | |
download | wydawca-180ca1d87d2bf69d9dbb0acb76422e9ae15f930c.tar.gz wydawca-180ca1d87d2bf69d9dbb0acb76422e9ae15f930c.tar.bz2 |
Implement all directives
git-svn-id: file:///svnroot/wydawca/trunk@284 6bb4bd81-ecc2-4fd4-a2d4-9571d19c0d33
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/config.c | 62 | ||||
-rw-r--r-- | src/directive.c | 162 | ||||
-rw-r--r-- | src/diskio.c | 305 | ||||
-rw-r--r-- | src/exec.c | 155 | ||||
-rw-r--r-- | src/gpg.c | 159 | ||||
-rw-r--r-- | src/register.c | 152 | ||||
-rw-r--r-- | src/triplet.c | 108 | ||||
-rw-r--r-- | src/verify.c | 34 | ||||
-rw-r--r-- | src/wydawca.c | 2 | ||||
-rw-r--r-- | src/wydawca.h | 206 | ||||
-rw-r--r-- | src/wydawca.rc | 1 |
12 files changed, 753 insertions, 597 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index c93b9bd..f421330 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,8 +19,9 @@ EXTRA_PROGRAMS=wydawca wydawca_SOURCES=\ config.c\ directive.c\ - gpg.c\ diskio.c\ + exec.c\ + gpg.c\ method.c\ process.c\ sql.c\ @@ -29,6 +30,7 @@ wydawca_SOURCES=\ verify.c\ wydawca.c\ wydawca.h + LDADD=../lib/libgsc.a ../gnu/libgnu.a @SQLLIB@ @GPGMELIB@ INCLUDES = -I../lib -I../gnu AM_CPPFLAGS=-DSYSCONFDIR=\"$(sysconfdir)\"
\ No newline at end of file diff --git a/src/config.c b/src/config.c index 94dbde5..ae7c974 100644 --- a/src/config.c +++ b/src/config.c @@ -134,6 +134,12 @@ safe_file_name (char *file_name) return file_name; } +char * +safe_file_name_alloc (const char *file_name) +{ + return safe_file_name (xstrdup (file_name)); +} + static void cfg_syslog_tag (gsc_config_file_t *file, char *kw, char *val, void *unused) @@ -146,8 +152,11 @@ cfg_syslog_facility (gsc_config_file_t *file, char *kw, char *val, void *unused) { if (gsc_str_to_syslog_facility (val, &log_facility)) - file->error_msg (file->file_name, file->line, "Unknown facility `%s'", - val); + { + file->error_msg (file->file_name, file->line, "Unknown facility `%s'", + val); + file->error_count++; + } } static void @@ -171,10 +180,14 @@ cfg_file_sweep_time (gsc_config_file_t *file, char *kw, char *val, void *data) time_t interval; const char *endp; - if (parse_time_interval (val, &interval, &endp)) - file->error_msg (file->file_name, file->line, - "unrecognized time format (near `%s')", - endp); + if (parse_time_interval (val, &interval, &endp)) + { + file->error_msg (file->file_name, file->line, + "unrecognized time format (near `%s')", + endp); + file->error_count++; + } + if (data) dp->file_sweep_time = interval; else @@ -204,6 +217,7 @@ _cfg_access_method (gsc_config_file_t *file, char *kw, char *val, void *data) { file->error_msg (file->file_name, file->line, "access method not specified"); + file->error_count++; return; } ap->type = str_to_access_method (word); @@ -212,6 +226,7 @@ _cfg_access_method (gsc_config_file_t *file, char *kw, char *val, void *data) case method_none: file->error_msg (file->file_name, file->line, "unknown access method: %s", word); + file->error_count++; return; case method_sql: @@ -292,6 +307,7 @@ cfg_archive (gsc_config_file_t *file, char *kw, char *val, void *data) if (*type == 0) { file->error_msg (file->file_name, file->line, "missing archive type"); + file->error_count++; return; } @@ -308,6 +324,7 @@ cfg_archive (gsc_config_file_t *file, char *kw, char *val, void *data) if (*word == 0) { file->error_msg (file->file_name, file->line, "missing file name"); + file->error_count++; dp->archive.type = archive_none; return; } @@ -332,6 +349,7 @@ cfg_archive (gsc_config_file_t *file, char *kw, char *val, void *data) { file->error_msg (file->file_name, file->line, "invalid backup type `%s'", word); + file->error_count++; dp->archive.backup_type = no_backups; } if (*val) @@ -348,12 +366,16 @@ cfg_archive (gsc_config_file_t *file, char *kw, char *val, void *data) "ignoring VERSION_CONTROL environment variable: " "invalid value"); dp->archive.backup_type = no_backups; + file->error_count++; } } } else - file->error_msg (file->file_name, file->line, - "unknown archive type: %s", word); + { + file->error_msg (file->file_name, file->line, + "unknown archive type: %s", word); + file->error_count++; + } } } @@ -373,7 +395,8 @@ cfg_directory (gsc_config_file_t *file, char *kw, char *val, void *unused) }; if (val) - file->error_msg (file->file_name, file->line, "junk in line ignored"); + file->error_msg (file->file_name, file->line, + "warning: junk in line ignored"); memset (&dpair, 0, sizeof dpair); dpair.file_sweep_time = file_sweep_time; @@ -394,6 +417,7 @@ cfg_directory (gsc_config_file_t *file, char *kw, char *val, void *unused) file->error_msg (file->file_name, file->line, "%s is not a directory", dpair.source_dir); + file->error_count++; } else if (test_dir (dpair.dest_dir, &ec)) { @@ -405,6 +429,7 @@ cfg_directory (gsc_config_file_t *file, char *kw, char *val, void *unused) file->error_msg (file->file_name, file->line, "%s is not a directory", dpair.dest_dir); + file->error_count++; } else if (dpair.verify_method.type != method_none && dpair.gpg_key_method.type != method_none) @@ -412,14 +437,6 @@ cfg_directory (gsc_config_file_t *file, char *kw, char *val, void *unused) } static void -cfg_unlink_invalid_files (gsc_config_file_t *file, char *kw, char *val, - void *unused) -{ - if (get_bool (val, &unlink_invalid_files)) - file->error_msg (file->file_name, file->line, "invalid boolean value"); -} - -static void cfg_sql_host (gsc_config_file_t *file, char *kw, char *val, void *data) { struct sqlconn *pconn = data; @@ -440,12 +457,14 @@ cfg_sql_host (gsc_config_file_t *file, char *kw, char *val, void *data) { file->error_msg (file->file_name, file->line, "invalid port number (near %s)", end); + file->error_count++; return; } if (n == 0 || n > USHRT_MAX) { file->error_msg (file->file_name, file->line, "port number out of range 1..%d", USHRT_MAX); + file->error_count++; return; } pconn->port = n; @@ -492,7 +511,11 @@ cfg_sql (gsc_config_file_t *file, char *kw, char *val, void *unused) memset (&sql, 0, sizeof sql); if (!val) - file->error_msg (file->file_name, file->line, "missing block identifier"); + { + file->error_msg (file->file_name, file->line, + "missing block identifier"); + file->error_count++; + } else sql.ident = xstrdup (val); @@ -510,6 +533,7 @@ cfg_umask (gsc_config_file_t *file, char *kw, char *val, void *unused) { file->error_msg (file->file_name, file->line, "invalid umask (near %s)", p); + file->error_count++; return; } umask (m); @@ -524,6 +548,7 @@ cfg_tar_program (gsc_config_file_t *file, char *kw, char *val, void *unused) { file->error_msg (file->file_name, file->line, "must be an absolute file name"); + file->error_count++; return; } @@ -537,7 +562,6 @@ static struct gsc_config_keyword kw_handler[] = { { "syslog-tag", cfg_syslog_tag }, { "syslog-facility", cfg_syslog_facility }, { "directory", cfg_directory }, - { "unlink-invalid-files", cfg_unlink_invalid_files }, { "file-sweep-time", cfg_file_sweep_time }, { "sql", cfg_sql }, { "verify-user", cfg_verify_user }, diff --git a/src/directive.c b/src/directive.c index 0e06502..ee52dd7 100644 --- a/src/directive.c +++ b/src/directive.c @@ -19,56 +19,56 @@ /* Directive file support */ int -directive_parse (struct file_register *reg) +directive_parse (struct file_triplet *trp) { size_t dcount, i; char *p; if (debug_level > 2) logmsg (LOG_DEBUG, "%s: parsing directive blurb: %s", - reg->file[file_directive].name, reg->blurb); + trp->file[file_directive].name, trp->blurb); dcount = 0; - for (p = reg->blurb; *p; p++) + for (p = trp->blurb; *p; p++) if (*p == '\n') dcount++; - reg->directive = xcalloc (dcount + 1, sizeof reg->directive[0]); - p = reg->blurb; + trp->directive = xcalloc (dcount + 1, sizeof trp->directive[0]); + p = trp->blurb; for (i = 0; i < dcount; ) { - reg->directive[i] = p; + trp->directive[i] = p; p = strchr (p, '\n'); if (p) *p++ = 0; - if (trim (reg->directive[i]) == 0) /* ignore empty lines */ + if (trim (trp->directive[i]) == 0) /* ignore empty lines */ continue; - if (strchr (reg->directive[i], ':') == NULL) + if (strchr (trp->directive[i], ':') == NULL) { logmsg (LOG_ERR, "%s: invalid line: %s", - reg->file[file_directive].name, reg->directive[i]); - free (reg->directive); - reg->directive = NULL; + trp->file[file_directive].name, trp->directive[i]); + free (trp->directive); + trp->directive = NULL; return 1; } i++; if (!p) break; } - reg->directive[i] = NULL; + trp->directive[i] = NULL; return 0; } int -directive_get_value (struct file_register *reg, const char *key, +directive_get_value (struct file_triplet *trp, const char *key, const char **pval) { int keylen = strlen (key); int i; - for (i = 0; reg->directive[i]; i++) + for (i = 0; trp->directive[i]; i++) { - char *str = reg->directive[i]; + char *str = trp->directive[i]; int len = strlen (str); if (len > keylen && memcmp (str, key, keylen) == 0 && str[keylen] == ':') { @@ -84,25 +84,25 @@ directive_get_value (struct file_register *reg, const char *key, } static int -_directive_seq_get (int n, struct file_register *reg, +_directive_seq_get (int n, struct file_triplet *trp, const char **pkey, const char **pval) { char *p; size_t len; - if (reg->directive[n] == NULL) + if (trp->directive[n] == NULL) return 0; - p = strchr (reg->directive[n], ':'); - len = p - reg->directive[n]; - if (len + 1 > reg->tmpsize) + p = strchr (trp->directive[n], ':'); + len = p - trp->directive[n]; + if (len + 1 > trp->tmpsize) { - reg->tmpsize = len + 1; - reg->tmp = x2realloc (reg->tmp, ®->tmpsize); + trp->tmpsize = len + 1; + trp->tmp = x2realloc (trp->tmp, &trp->tmpsize); } - memcpy (reg->tmp, reg->directive[n], len); - reg->tmp[len] = 0; - *pkey = reg->tmp; + memcpy (trp->tmp, trp->directive[n], len); + trp->tmp[len] = 0; + *pkey = trp->tmp; for (p++; *p && isspace (*p); p++) ; if (pval) @@ -111,18 +111,18 @@ _directive_seq_get (int n, struct file_register *reg, } int -directive_first (struct file_register *reg, +directive_first (struct file_triplet *trp, const char **pkey, const char **pval) { int n = 0; - return _directive_seq_get (n, reg, pkey, pval); + return _directive_seq_get (n, trp, pkey, pval); } int -directive_next (struct file_register *reg, int n, +directive_next (struct file_triplet *trp, int n, const char **pkey, const char **pval) { - return _directive_seq_get (n, reg, pkey, pval); + return _directive_seq_get (n, trp, pkey, pval); } int @@ -144,23 +144,23 @@ directive_pack_version (const char *val, unsigned *pversion) } int -directive_version_in_range_p (struct file_register *reg, +directive_version_in_range_p (struct file_triplet *trp, unsigned from, unsigned to) { const char *val; int version; - if (directive_get_value (reg, "version", &val)) + if (directive_get_value (trp, "version", &val)) { logmsg (LOG_ERR, "%s: missing `version' directive", - reg->file[file_directive].name); + trp->file[file_directive].name); return 0; } if (directive_pack_version (val, &version)) { logmsg (LOG_ERR, "%s: unparsable version: %s", - reg->file[file_directive].name, val); + trp->file[file_directive].name, val); return 0; } @@ -168,7 +168,7 @@ directive_version_in_range_p (struct file_register *reg, return 1; logmsg (LOG_ERR, "%s: version %s is not in the allowed range", - reg->file[file_directive].name, val); + trp->file[file_directive].name, val); return 0; } @@ -213,18 +213,18 @@ find_directive (const char *key) } int -verify_directive_format (struct file_register *reg) +verify_directive_format (struct file_triplet *trp) { int n, dnum; const char *key; - if (!directive_version_in_range_p (reg, MIN_DIRECTIVE_VERSION, + if (!directive_version_in_range_p (trp, MIN_DIRECTIVE_VERSION, MAX_DIRECTIVE_VERSION)) return 1; dnum = 0; - for (n = directive_first (reg, &key, NULL); n; - n = directive_next (reg, n, &key, NULL)) + for (n = directive_first (trp, &key, NULL); n; + n = directive_next (trp, n, &key, NULL)) { if (strcmp (key, "comment") == 0) continue; @@ -235,7 +235,7 @@ verify_directive_format (struct file_register *reg) if (strcmp (key, "version")) { logmsg (LOG_ERR, "%s:%d: expected `%s' but found `%s'", - reg->file[file_directive].name, n, "version", key); + trp->file[file_directive].name, n, "version", key); return 1; } break; @@ -244,7 +244,7 @@ verify_directive_format (struct file_register *reg) if (strcmp (key, "directory")) { logmsg (LOG_ERR, "%s:%d: expected `%s' but found `%s'", - reg->file[file_directive].name, n, "directory", key); + trp->file[file_directive].name, n, "directory", key); return 1; } break; @@ -253,25 +253,25 @@ verify_directive_format (struct file_register *reg) if (find_directive (key) == unknown_dir) { logmsg (LOG_ERR, "%s:%d: unknown directive `%s'", - reg->file[file_directive].name, n, key); + trp->file[file_directive].name, n, key); return 1; } } } - if (reg->file[file_dist].name && reg->file[file_signature].name) + if (trp->file[file_dist].name && trp->file[file_signature].name) { const char *filename; - if (directive_get_value (reg, "filename", &filename)) + if (directive_get_value (trp, "filename", &filename)) { logmsg (LOG_ERR, "%s: missing `filename' directive", - reg->file[file_directive].name); + trp->file[file_directive].name); return 1; } - if (strcmp (filename, reg->file[file_dist].name)) + if (strcmp (filename, trp->file[file_dist].name)) { logmsg (LOG_ERR, "%s: filename %s does not match actual name", - reg->file[file_dist].name, filename); + trp->file[file_dist].name, filename); return 1; } } @@ -279,15 +279,14 @@ verify_directive_format (struct file_register *reg) } int -process_directives (struct file_register *reg, struct directory_pair *dpair) +process_directives (struct file_triplet *trp, struct directory_pair *dpair) { int n; const char *key, *val; - char *directory = NULL; - const char *relative_dir; + char *relative_dir; - for (n = directive_first (reg, &key, &val); n; - n = directive_next (reg, n, &key, &val)) + for (n = directive_first (trp, &key, &val); n; + n = directive_next (trp, n, &key, &val)) { enum directive d = find_directive (key); switch (d) @@ -298,23 +297,16 @@ process_directives (struct file_register *reg, struct directory_pair *dpair) case comment_dir: logmsg (LOG_NOTICE, "%s: COMMENT: %s", - reg->file[file_directive].name, val); + trp->file[file_directive].name, val); break; case directory_dir: - directory = create_directory (dpair->dest_dir, val, - reg->file[file_dist].uid, - reg->gid); - if (!directory) - return 1; - relative_dir = val; + relative_dir = safe_file_name_alloc (val); break; case filename_dir: - if (move_file (reg, file_dist, directory, - relative_dir, &dpair->archive) - || move_file (reg, file_signature, directory, - relative_dir, &dpair->archive)) + if (move_file (trp, dpair, file_dist, relative_dir) + || move_file (trp, dpair, file_signature, relative_dir)) return 1; break; @@ -322,21 +314,53 @@ process_directives (struct file_register *reg, struct directory_pair *dpair) /* Already processed */ break; - case rmsymlink_dir: case archive_dir: + if (archive_file (trp, dpair, val, relative_dir)) + return 1; + break; + case symlink_dir: + { + int argc; + char **argv; + int rc = 0; + + if (rc = argcv_get (val, NULL, NULL, &argc, &argv)) + { + logmsg (LOG_ERR, "cannot parse symlink value `%s': %s", + val, strerror (rc)); + return 1; + } + + if (argc != 2) + { + rc = 1; + logmsg (LOG_ERR, + "wrong number of arguments to %s directive: `%s'", + key, val); + } + else + rc = symlink_file (trp, dpair, relative_dir, argv[0], argv[1]); + + argcv_free (argc, argv); + if (rc) + return 1; + } + break; + + case rmsymlink_dir: logmsg (LOG_CRIT, "%s: %s directive is not supported yet", - reg->file[file_directive].name, key); + trp->file[file_directive].name, key); } } - free (directory); - - if (!dry_run_mode && unlink (reg->file[file_directive].name)) + if (!dry_run_mode && unlink (trp->file[file_directive].name)) { logmsg (LOG_CRIT, "%s: cannot unlink directive file: %s", - reg->file[file_directive].name, strerror (errno)); + trp->file[file_directive].name, strerror (errno)); } - + + free (relative_dir); + return 0; } diff --git a/src/diskio.c b/src/diskio.c index 333c07e..6eaffa0 100644 --- a/src/diskio.c +++ b/src/diskio.c @@ -15,8 +15,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "wydawca.h" -#include <sys/types.h> -#include <sys/wait.h> +#include "save-cwd.h" #if defined USE_SENDFILE # if defined __linux__ && defined HAVE_SYS_SENDFILE_H # include <sys/sendfile.h> @@ -25,6 +24,16 @@ # endif #endif +int +sub_dir_p (char *arg, char *dir) +{ + int dlen = strlen (dir); + + return strlen (arg) > dlen + && memcmp (dir, arg, dlen) == 0 + && arg[dlen] == '/'; +} + char * concat_dir (const char *base, const char *name, size_t *pbaselen) { @@ -255,9 +264,6 @@ do_move_file (const char *file, const char *dst_file, uid_t uid, gid_t gid) int tar_append_file (const char *archive, const char *file) { - FILE *fp; - pid_t pid, npid; - int i, status; int rc; const char *argv[6]; @@ -272,73 +278,19 @@ tar_append_file (const char *archive, const char *file) argv[3] = "-r"; argv[4] = file; argv[5] = NULL; - - fp = start_prog (6, argv, &pid); - if (!fp) - { - logmsg (LOG_CRIT, - "cannot start archiver program `%s'", tar_command_name); - return 1; - } - - for (i = 0; i < 5 && (npid = waitpid (pid, &status, WNOHANG)) == 0; i++) - sleep (1); - switch (npid) + switch (wydawca_exec (6, argv, NULL)) { - case -1: - logmsg (LOG_CRIT, - "cannot execute archiver program `%s'; waitpid failed: %s", - tar_command_name, strerror (errno)); - fclose (fp); - return 1; + case exec_success: + return 0; - case 0: - logmsg (LOG_CRIT, - "cannot execute archiver program `%s': " - "program did not respond within 5 seconds: %s", - tar_command_name, strerror (errno)); - kill (pid, SIGKILL); - fclose (fp); - return 1; - - default: + case exec_fail: + case exec_error: + logmsg (LOG_ERR, "cannot archive %s", file); break; } - if (WIFEXITED (status)) - { - rc = WEXITSTATUS (status); - if (rc) - { - logmsg (LOG_ERR, "cannot archive %s: %s finished with code %d", - file, tar_command_name, rc); - log_output (LOG_ERR, argv[0], fp); - } - else if (debug_level > 1) - log_output (LOG_DEBUG, argv[0], fp); - } - else - { - rc = 1; - if (WIFSIGNALED (status)) - logmsg (LOG_ERR, - "cannot archive %s: " - "%s terminated on signal %d", - file, tar_command_name, WTERMSIG (status)); - else if (WIFSTOPPED (status)) - logmsg (LOG_ERR, - "cannot archive %s: " - "%s stopped on signal %d", - file, tar_command_name, WTERMSIG (status)); - else - logmsg (LOG_ERR, - "cannot archive %s: " - "%s terminated with unrecognized status", - file, tar_command_name); - } - fclose (fp); - return rc; + return 1; } int @@ -346,7 +298,7 @@ backup_file (const char *dst_file, const char *dst_dir, const char *file, struct archive_descr *archive, uid_t uid, gid_t gid, const char *reldir) { - int rc; + int rc = 0; char *adir; char *file_name; @@ -413,9 +365,9 @@ backup_file (const char *dst_file, const char *dst_dir, const char *file, } int -archive_file (const char *dst_file, const char *dst_dir, const char *file, - struct archive_descr *archive, uid_t uid, gid_t gid, - const char *reldir) +do_archive_file (const char *dst_file, const char *dst_dir, const char *file, + struct archive_descr *archive, uid_t uid, gid_t gid, + const char *reldir) { switch (archive->type) { @@ -431,28 +383,219 @@ archive_file (const char *dst_file, const char *dst_dir, const char *file, return 0; } - int -move_file (struct file_register *reg, enum file_type file_id, - const char *dst_dir, const char *reldir, - struct archive_descr *archive) +move_file (struct file_triplet *trp, struct directory_pair *dpair, + enum file_type file_id, const char *reldir) { - char *dst_file = concat_dir (dst_dir, reg->file[file_id].name, NULL); + char *dst_file; int rc = 0; + char *dst_dir = create_directory (dpair->dest_dir, reldir, + TRIPLET_UID (trp), TRIPLET_GID (trp)); + + if (!dst_dir) + return 1; + dst_file = concat_dir (dst_dir, trp->file[file_id].name, NULL); if (debug_level) - logmsg (LOG_DEBUG, "installing %s to %s", reg->file[file_id].name, + logmsg (LOG_DEBUG, "installing %s to %s", trp->file[file_id].name, dst_dir); if (access (dst_file, F_OK) == 0) - archive_file (dst_file, dst_dir, reg->file[file_id].name, - archive, reg->file[file_id].uid, reg->gid, reldir); + rc = do_archive_file (dst_file, dst_dir, trp->file[file_id].name, + &dpair->archive, + TRIPLET_UID (trp), TRIPLET_GID (trp), reldir); - if (!dry_run_mode) - rc = do_move_file (reg->file[file_id].name, dst_file, - reg->file[file_id].uid, reg->gid); + if (!dry_run_mode && rc == 0) + rc = do_move_file (trp->file[file_id].name, dst_file, + TRIPLET_UID (trp), TRIPLET_GID (trp)); free (dst_file); + free (dst_dir); return rc; } +int +archive_file (struct file_triplet *trp, struct directory_pair *dpair, + const char *file_name, const char *reldir) +{ + char *dst_file; + int rc = 0; + char *dst_dir = create_directory (dpair->dest_dir, reldir, + TRIPLET_UID (trp), TRIPLET_GID (trp)); + + if (!dst_dir) + return 1; + + dst_file = safe_file_name (concat_dir (dst_dir, file_name, NULL)); + if (!sub_dir_p (dst_file, dpair->dest_dir)) + { + logmsg (LOG_ERR, "file to be archived `%s' does not lie under `%s'", + dst_file, dpair->dest_dir); + free (dst_file); + free (dst_dir); + return 1; + } + + if (access (dst_file, F_OK) == 0) + { + if (debug_level) + logmsg (LOG_DEBUG, "Archiving file `%s'", dst_file); + rc = do_archive_file (dst_file, dst_dir, file_name, &dpair->archive, + TRIPLET_UID (trp), TRIPLET_GID (trp), reldir); + } + else + logmsg (LOG_NOTICE, "Nothing to archive: file `%s' does not exist", + dst_file); + + free (dst_file); + free (dst_dir); + return rc; +} + +int +symlink_file (struct file_triplet *trp, struct directory_pair *dpair, + const char *reldir, + const char *wanted_src, const char *wanted_dst) +{ + char *dst_file; + int rc = 0; + struct saved_cwd cwd; + char *dst_dir = create_directory (dpair->dest_dir, reldir, + TRIPLET_UID (trp), TRIPLET_GID (trp)); + char *src, *dst; + + if (!dst_dir) + return 1; + + if (save_cwd (&cwd)) + { + logmsg (LOG_ERR, "cannot save current directory: %s", + strerror (errno)); + return 1; + } + + src = safe_file_name_alloc (wanted_src); + if (!sub_dir_p (src, dpair->dest_dir)) + { + logmsg (LOG_ERR, "symlink source `%s' does not lie under `%s'", + src, dpair->dest_dir); + free (src); + return 1; + } + + dst = safe_file_name_alloc (wanted_dst); + if (!sub_dir_p (dst, dpair->dest_dir)) + { + logmsg (LOG_ERR, "symlink destination `%s' does not lie under `%s'", + dst, dpair->dest_dir); + free (src); + free (dst); + return 1; + } + + if (debug_level) + logmsg (LOG_DEBUG, "symlinking %s to %s in directory %s", + src, dst, dst_dir); + + if (!dry_run_mode) + { + char *p = strrchr (dst, '/'); + if (p > dst) + { + char *dir; + + *p = 0; + dir = create_directory (dpair->dest_dir, dst, + TRIPLET_UID (trp), TRIPLET_GID (trp)); + if (!dir) + rc = 1; + else + free (dir); + *p = '/'; + } + + if (rc == 0) + { + rc = link (src, dst); + if (rc) + logmsg (LOG_ERR, + "symlinking %s to %s in directory %s failed: %s", + src, dst, dst_dir, strerror (errno)); + } + } + + if (restore_cwd (&cwd)) + { + logmsg (LOG_EMERG, "cannot restore current directory: %s", + strerror (errno)); + exit (1); + } + + free (src); + free (dst); + free (dst_dir); + return rc; +} + +static int +do_rmsymlink_file (const char *dst_file) +{ + struct stat st; + + if (debug_level) + logmsg (LOG_DEBUG, "Removing symbolic link %s", dst_file); + + if (stat (dst_file, &st)) + { + if (errno == ENOENT) + { + logmsg (LOG_NOTICE, "Symlink `%s' does not exist", dst_file); + return 0; + } + if (!S_ISLNK (st.st_mode)) + { + logmsg (LOG_ERR, "Refusing to unlink %s: is not a symlink", + dst_file); + return 1; + } + } + if (dry_run_mode) + return 0; + if (unlink (dst_file)) + { + logmsg (LOG_ERR, "Cannot unlink %s: %s", dst_file, strerror (errno)); + return 1; + } + return 0; +} + +int +rmsymlink_file (struct file_triplet *trp, struct directory_pair *dpair, + const char *reldir, const char *file_name) +{ + char *dst_file; + int rc = 0; + char *dst_dir = create_directory (dpair->dest_dir, reldir, + TRIPLET_UID (trp), TRIPLET_GID (trp)); + + if (!dst_dir) + return 1; + + dst_file = safe_file_name (concat_dir (dst_dir, file_name, NULL)); + if (!sub_dir_p (dst_file, dpair->dest_dir)) + { + logmsg (LOG_ERR, "refusing to remove a symlink `%s' that is not " + "located under `%s'", + dst_file, dpair->dest_dir); + free (dst_file); + free (dst_dir); + return 1; + } + + rc = do_rmsymlink_file (dst_file); + + free (dst_file); + free (dst_dir); + return rc; +} + diff --git a/src/exec.c b/src/exec.c new file mode 100644 index 0000000..1f9e27a --- /dev/null +++ b/src/exec.c @@ -0,0 +1,155 @@ +/* wydawca - FTP release synchronisation daemon + Copyright (C) 2007 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 <http://www.gnu.org/licenses/>. */ + +#include "wydawca.h" +#include "save-cwd.h" +#include <gpgme.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> + +static FILE * +start_prog (int argc, const char **argv, pid_t *ppid) +{ + int p[2]; + FILE *fp; + pid_t pid; + int i; + + pipe (p); + switch (pid = fork ()) + { + case 0: + /* Child process */ + + if (p[1] != 1) + dup2 (p[1], 1); + if (p[1] != 1) + dup2 (p[1], 2); + close (p[0]); + + /* Close unneded descripitors */ + for (i = getmaxfd (); i > 2; i--) + close (i); + + execvp (argv[0], (char**) argv); + logmsg (LOG_CRIT, "cannot run %s: %s", argv[0], strerror (errno)); + exit (1); + + case -1: + logmsg (LOG_CRIT, "cannot run `%s': fork failed: %s", + argv[0], strerror (errno)); + return NULL; + + default: + /* Master process */ + close (p[1]); + fp = fdopen (p[0], "r"); + if (!fp) + logmsg (LOG_ERR, "cannot fdopen: %s", strerror (errno)); + *ppid = pid; + } + return fp; +} + +void +log_output (int prio, const char *prog, FILE *fp) +{ + size_t size = 0; + char *buf = NULL; + + logmsg (prio, "%s output follows:", prog); + while (getline (&buf, &size, fp) > 0) + logmsg (prio, "%s", buf); + logmsg (prio, "end of %s output", prog); + free (buf); +} + +enum exec_result +wydawca_exec (int argc, const char **argv, int *retcode) +{ + FILE *fp; + pid_t pid, npid; + int status; + int i; + enum exec_result res; + + fp = start_prog (5, argv, &pid); + if (!fp) + { + logmsg (LOG_CRIT, "cannot start %s", argv[0]); + return exec_error; + } + + for (i = 0; i < 5 && (npid = waitpid (pid, &status, WNOHANG)) == 0; i++) + sleep (1); + + switch (npid) + { + case -1: + logmsg (LOG_CRIT, "cannot execute %s: waitpid failed: %s", + argv[0], strerror (errno)); + fclose (fp); + return exec_error; + + case 0: + logmsg (LOG_CRIT, + "cannot execute %s: the process did not respond " + "within 5 seconds: %s", + argv[0], strerror (errno)); + kill (pid, SIGKILL); + fclose (fp); + return exec_error; + + default: + break; + } + + if (WIFEXITED (status)) + { + int rc = WEXITSTATUS (status); |