diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-02-24 18:45:51 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-02-24 18:45:51 +0200 |
commit | c9178a6fef0184baed0e8456bb6c6d4091b4997c (patch) | |
tree | 0c97d7aeb6235a469cd071d7d0b589f45a9d35f6 | |
parent | b1472caae9a1b6905b6bbe42e69539b29febcf5c (diff) | |
download | wydawca-c9178a6fef0184baed0e8456bb6c6d4091b4997c.tar.gz wydawca-c9178a6fef0184baed0e8456bb6c6d4091b4997c.tar.bz2 |
Implement locking
* src/lock.c: New file.
* gnulib.modules (sysexits, xgethostname): New modules.
* src/lock.c: New file.
* src/Makefile.am: Add lock.c
* src/config.c: Locking keywords.
* src/job.c: Requeue jobs if locking fails.
* src/wydawca.c (wydawca_uid): Rename to wydawca_set_uid
(main): Implement restart.
* src/process.c (scan_spool, scan_all_spools): Use locking, if configured.
* src/directive.c, src/diskio.c, src/triplet.c: Rename
wydawca_uid
* src/exec.c, src/getopt.m4, src/net.c,
src/pidfile.c: Use standard error codes from sysexits.h
* tests/etc/wydawca.rcin: Disable locking.
-rw-r--r-- | gnulib.modules | 4 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/config.c | 8 | ||||
-rw-r--r-- | src/directive.c | 4 | ||||
-rw-r--r-- | src/diskio.c | 6 | ||||
-rw-r--r-- | src/exec.c | 2 | ||||
-rw-r--r-- | src/getopt.m4 | 2 | ||||
-rw-r--r-- | src/job.c | 45 | ||||
-rw-r--r-- | src/lock.c | 239 | ||||
-rw-r--r-- | src/net.c | 14 | ||||
-rw-r--r-- | src/pidfile.c | 10 | ||||
-rw-r--r-- | src/process.c | 25 | ||||
-rw-r--r-- | src/triplet.c | 4 | ||||
-rw-r--r-- | src/wydawca.c | 50 | ||||
-rw-r--r-- | src/wydawca.h | 27 | ||||
-rw-r--r-- | tests/etc/wydawca.rcin | 2 |
16 files changed, 391 insertions, 52 deletions
diff --git a/gnulib.modules b/gnulib.modules index 1c56638..3dbc445 100644 --- a/gnulib.modules +++ b/gnulib.modules @@ -14,2 +14,3 @@ backupfile strerror +sysexits vasprintf @@ -17,2 +18,3 @@ inttostr strftime -version-etc
\ No newline at end of file +version-etc +xgethostname diff --git a/src/Makefile.am b/src/Makefile.am index 903b754..fcfdb78 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -28,2 +28,3 @@ wydawca_SOURCES=\ job.c\ + lock.c\ meta.c\ diff --git a/src/config.c b/src/config.c index facfcbc..bab1ed3 100644 --- a/src/config.c +++ b/src/config.c @@ -1239,2 +1239,10 @@ static struct gconf_keyword wydawca_kw[] = { gconf_type_string, &pidfile }, + + { "locking", NULL, N_("Enable or disable locking"), + gconf_type_bool, &enable_locking }, + { "lockdir", N_("dir"), N_("Set directory for lock files"), + gconf_type_string, &lockdir }, + { "lock-expire-time", N_("interval"), N_("Define lock expiration interval"), + gconf_type_string, &lock_expire_time, 0, cb_interval }, + { "listen", N_("socket"), N_("Listen on this address"), diff --git a/src/directive.c b/src/directive.c index 37ce241..ccea1ef 100644 --- a/src/directive.c +++ b/src/directive.c @@ -339,5 +339,5 @@ process_directives (struct file_triplet *trp, const struct spool *spool) case filename_dir: - wydawca_uid (0); + wydawca_set_uid (0); rc = verify_detached_signature (trp, spool); - wydawca_uid (TRIPLET_UID (trp)); + wydawca_set_uid (TRIPLET_UID (trp)); if (rc == 0) diff --git a/src/diskio.c b/src/diskio.c index ccff5c2..c068e49 100644 --- a/src/diskio.c +++ b/src/diskio.c @@ -126,5 +126,5 @@ create_directory (const char *base, const char *name, uid_t uid, gid_t gid) int rc; - wydawca_uid (0); + wydawca_set_uid (0); rc = create_hierarchy (dir, baselen, uid, gid); - wydawca_uid (uid); + wydawca_set_uid (uid); if (rc) @@ -649,3 +649,3 @@ dir_symlink_file (struct file_triplet *trp, const struct spool *spool, strerror (errno)); - exit (1); + exit (EX_SOFTWARE); } @@ -51,3 +51,3 @@ start_prog (int argc, const char **argv, pid_t *ppid) logmsg (LOG_CRIT, _("cannot run %s: %s"), argv[0], strerror (errno)); - exit (1); + exit (EX_UNAVAILABLE); diff --git a/src/getopt.m4 b/src/getopt.m4 index 83cc45f..2cd9a0f 100644 --- a/src/getopt.m4 +++ b/src/getopt.m4 @@ -181,3 +181,3 @@ ifelse([<$#>],3,opterr = 0;) default: - ifelse([<$#>],3,$3,[<exit(1)>]); + ifelse([<$#>],3,$3,[<exit (EX_USAGE)>]); @@ -40,2 +40,11 @@ static struct spool fake_spool = { "all spools" }; +static int wakeup; + +RETSIGTYPE +queue_signal (int sig) +{ + wakeup = 1; + signal (sig, queue_signal); +} + struct job * @@ -61,5 +70,6 @@ job_active_count () -void +int wydawca_scanner (struct job *job) { + int rc; initstats(); @@ -67,3 +77,3 @@ wydawca_scanner (struct job *job) if (job->spool == &fake_spool) - scan_all_spools (1, &job->uid); + rc = scan_all_spools (1, &job->uid); else @@ -71,3 +81,3 @@ wydawca_scanner (struct job *job) spool_create_timers (); - scan_spool (job->spool, 1, &job->uid); + rc = scan_spool (job->spool, 1, &job->uid); } @@ -76,2 +86,3 @@ wydawca_scanner (struct job *job) logstats (); + return rc; } @@ -94,4 +105,7 @@ job_start (struct job *job) { - wydawca_scanner (job); - return 0; + if (wydawca_scanner (job)) + job->state = STATE_QUEUED; + else + job->state = STATE_FINISHED; + wakeup = 1; } @@ -101,4 +115,3 @@ job_start (struct job *job) { - wydawca_scanner (job); - exit (0); + exit (wydawca_scanner (job) ? WYDAWCA_EX_AGAIN : 0); } @@ -237,11 +250,2 @@ print_status (struct job *job, int expect_term) -static int wakeup; - -RETSIGTYPE -queue_signal (int sig) -{ - wakeup = 1; - signal (sig, queue_signal); -} - void @@ -280,3 +284,10 @@ job_queue_runner () if ((job->state &= ~STATE_FINISHED) == 0) - job_remove (job); + { + if (WIFEXITED (job->exit_status) + && WEXITSTATUS (job->exit_status) == WYDAWCA_EX_AGAIN) + /* Re-queue the job */ + job->state = STATE_QUEUED; + else + job_remove (job); + } } diff --git a/src/lock.c b/src/lock.c new file mode 100644 index 0000000..d94731c --- /dev/null +++ b/src/lock.c @@ -0,0 +1,239 @@ +/* wydawca - automatic release submission daemon + Copyright (C) 2007, 2009 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" +#include "c-ctype.h" +#include "xgethostname.h" + +int enable_locking = 1; +char *lockdir; +time_t lock_expire_time; + +#define LOCKFILE_MODE 0644 + +static int +stat_check (const char *file, int fd, int links) +{ + struct stat fn_stat; + struct stat fd_stat; + int err = 0; + int localfd = -1; + + if (fd == -1) + { + localfd = open (file, O_RDONLY); + + if (localfd == -1) + return errno; + fd = localfd; + } + + /* We should always be able to stat a valid fd, so this + is an error condition. */ + if (lstat (file, &fn_stat) || fstat (fd, &fd_stat)) + err = errno; + else + { + /* If the link and stat don't report the same info, or the + file is a symlink, fail the locking. */ +#define CHK(X) if(X) err = EINVAL + + CHK (!S_ISREG (fn_stat.st_mode)); + CHK (!S_ISREG (fd_stat.st_mode)); + CHK (fn_stat.st_nlink != links); + CHK (fn_stat.st_dev != fd_stat.st_dev); + CHK (fn_stat.st_ino != fd_stat.st_ino); + CHK (fn_stat.st_mode != fd_stat.st_mode); + CHK (fn_stat.st_nlink != fd_stat.st_nlink); + CHK (fn_stat.st_uid != fd_stat.st_uid); + CHK (fn_stat.st_gid != fd_stat.st_gid); + CHK (fn_stat.st_rdev != fd_stat.st_rdev); + +#undef CHK + } + if (localfd != -1) + close (localfd); + + return err; +} + +int +_lock_internal (const char *file, const char *fname) +{ + int err = 0; + int fd; + FILE *fp; + + fd = open (fname, O_WRONLY | O_CREAT | O_EXCL, LOCKFILE_MODE); + if (fd == -1) + { + if (errno == EEXIST) + return LOCK_RETRY; + else + { + logmsg (LOG_ERR, _("cannot create lock file %s: %s"), + fname, strerror (errno)); + return LOCK_FAILURE; + } + } + close (fd); + + /* Try to link to the lockfile. */ + if (link (fname, file) == -1) + { + unlink (fname); + if (errno == EEXIST) + return LOCK_RETRY; + else + { + logmsg (LOG_ERR, _("cannot create lock file %s: %s"), + file, strerror (errno)); + return LOCK_FAILURE; + } + } + + if ((fd = open (file, O_RDWR)) == -1) + { + unlink (fname); + logmsg (LOG_ERR, _("cannot open lock file %s: %s"), + fname, strerror (errno)); + return LOCK_FAILURE; + } + + err = stat_check (fname, fd, 2); + if (err) + { + unlink (fname); + logmsg (LOG_ERR, _("lock file check failed: %s"), strerror (errno)); + return (err == EINVAL) ? LOCK_INVALID : LOCK_FAILURE; + } + + unlink (fname); + + fp = fdopen (fd, "w"); + fprintf (fp, "%lu", (unsigned long) getpid ()); + fclose (fp); + + return 0; +} + +static void +expire_stale_lock (const char *file) +{ + int stale = 0; + char buf[80]; + int fd; + int len; + + fd = open (file, O_RDONLY); + if (fd == -1) + return; + + len = read (fd, buf, sizeof (buf) - 1); + if (len > 0) + { + pid_t pid; + + buf[len] = 0; + pid = strtoul (buf, NULL, 10); + if (pid > 0) + { + /* Process is gone so we try to remove the lock. */ + if (kill (pid, 0) == -1) + stale = 1; + } + else + stale = 1; /* Corrupted file, remove the lock. */ + } + + if (!stale) + { + struct stat stbuf; + fstat (fd, &stbuf); + /* The lock has expired. */ + if ((time (NULL) - stbuf.st_mtime) > lock_expire_time) + stale = 1; + } + + close (fd); + if (stale) + unlink (file); +} + +int +wydawca_lock (const char *lockname) +{ + char *tempname = NULL; + int rc; + + if (!enable_locking) + return 0; + expire_stale_lock (lockname); + + /* build the NFS hitching-post to the lock file */ + asprintf (&tempname, "%s.%lu.%lu.%s", + lockname, + (unsigned long) getpid (), + (unsigned long) time (NULL), xgethostname ()); + if (!tempname) + return LOCK_FAILURE; + rc = _lock_internal (lockname, tempname); + free (tempname); + return rc; +} + +void +wydawca_unlock (const char *lockfile) +{ + if (enable_locking) + unlink (lockfile); +} + +static char * +fix_tagname (const char *tag) +{ + char *tagname = xstrdup (tag); + char *p; + + for (p = tagname; *p; p++) + if (!c_isalnum (*p) && *p != '_' && *p != '-') + *p = '_'; + return tagname; +} + +char * +wydawca_lockname (const char *tag) +{ + char *lockname = NULL; + char *tagname = fix_tagname (tag); + asprintf (&lockname, "%s/LCK.%s", lockdir, tagname); + if (!lockname) + xalloc_die (); + free (tagname); + return lockname; +} + +void +wydawca_lock_init () +{ + if (enable_locking) + { + if (!lockdir) + lockdir = xstrdup (LOCALSTATEDIR "/lock/" PACKAGE); + if (create_hierarchy (lockdir, 0, getuid (), getgid ())) + exit (EX_OSFILE); + } +} @@ -26,3 +26,3 @@ open_listener () logmsg (LOG_CRIT, _("listener address is not configured")); - exit (1); + exit (EX_CONFIG); } @@ -34,3 +34,3 @@ open_listener () strerror(errno)); - exit (1); + exit (EX_OSERR); } @@ -46,3 +46,3 @@ open_listener () s_un->sun_path, strerror (errno)); - exit (1); + exit (errno == EACCES ? EX_NOPERM : EX_OSERR); } @@ -56,3 +56,3 @@ open_listener () s_un->sun_path, strerror (errno)); - exit (1); + exit (EX_OSFILE); } @@ -73,3 +73,3 @@ open_listener () close (fd); - exit (1); + exit (EX_OSERR); } @@ -79,3 +79,3 @@ open_listener () close (fd); - exit (1); + exit (EX_OSERR); } @@ -157,3 +157,3 @@ handle_connection (FILE *fp) -static int reconfigure; +int reconfigure; static int terminate; diff --git a/src/pidfile.c b/src/pidfile.c index 484cabd..6da84f1 100644 --- a/src/pidfile.c +++ b/src/pidfile.c @@ -40,3 +40,3 @@ check_pidfile () if (!force_startup) - exit (1); + exit (EX_UNAVAILABLE); } @@ -52,3 +52,3 @@ check_pidfile () if (!force_startup) - exit (1); + exit (EX_UNAVAILABLE); } @@ -60,3 +60,3 @@ check_pidfile () pid); - exit (1); + exit (EX_UNAVAILABLE); } @@ -71,3 +71,3 @@ check_pidfile () strerror (errno)); - exit (1); + exit (EX_UNAVAILABLE); } @@ -81,3 +81,3 @@ check_pidfile () pidfile, strerror (errno)); - exit (1); + exit (EX_UNAVAILABLE); } diff --git a/src/process.c b/src/process.c index 1a6b01d..d04121c 100644 --- a/src/process.c +++ b/src/process.c @@ -139,3 +139,3 @@ match_uid_p (uid_t uid, int uc, uid_t *uv) void -scan_spool (const struct spool *spool, int uc, uid_t *uv) +scan_spool_unlocked (const struct spool *spool, int uc, uid_t *uv) { @@ -227,2 +227,16 @@ scan_spool (const struct spool *spool, int uc, uid_t *uv) +int +scan_spool (const struct spool *spool, int uc, uid_t *uv) +{ + char *lockfile = wydawca_lockname (spool->tag); + int rc = wydawca_lock (lockfile); + if (rc == LOCK_OK) + { + scan_spool_unlocked (spool, uc, uv); + wydawca_unlock (lockfile); + } + free (lockfile); + return rc; +} + static void @@ -236,3 +250,3 @@ close_methods (struct spool *spool) /* Scan all configured update directories */ -void +int scan_all_spools (int uidc, uid_t *uidv) @@ -240,3 +254,4 @@ scan_all_spools (int uidc, uid_t *uidv) struct spool_list *sp; - + int rc = 0; + timer_start ("wydawca"); @@ -244,3 +259,4 @@ scan_all_spools (int uidc, uid_t *uidv) if (enabled_spool_p (&sp->spool)) - scan_spool (&sp->spool, uidc, uidv); + if (scan_spool (&sp->spool, uidc, uidv)) + rc++; @@ -249,2 +265,3 @@ scan_all_spools (int uidc, uid_t *uidv) timer_stop ("wydawca"); + return rc; } diff --git a/src/triplet.c b/src/triplet.c index 717fd2a..285c831 100644 --- a/src/triplet.c +++ b/src/triplet.c @@ -201,6 +201,6 @@ triplet_processor (void *data, void *proc_data) logmsg (LOG_DEBUG, _("processing triplet `%s'"), trp->name); - if (wydawca_uid (TRIPLET_UID (trp)) == 0) + if (wydawca_set_uid (TRIPLET_UID (trp)) == 0) { process_directives (trp, spool); - wydawca_uid (0); + wydawca_set_uid (0); } diff --git a/src/wydawca.c b/src/wydawca.c index f9818ed..b121959 100644 --- a/src/wydawca.c +++ b/src/wydawca.c @@ -257,3 +257,3 @@ collect_uids (int argc, char **argv) logmsg (LOG_ERR, _("no such user: %s"), argv[i]); - exit (1); + exit (EX_NOUSER); } @@ -268,3 +268,3 @@ collect_uids (int argc, char **argv) int -wydawca_uid (uid_t uid) +wydawca_set_uid (uid_t uid) { @@ -289,3 +289,28 @@ wydawca_uid (uid_t uid) +int +wydawca_set_gid (gid_t gid) +{ + int rc; + + if (getuid () != 0) + return 0; +#if defined(HAVE_SETREGID) + rc = setregid (0, gid); +#elif defined(HAVE_SETRESGID) + rc = setresgid (-1, gid, -1); +#elif defined(HAVE_SETEGID) + rc = setegid (gid); +#else +# error "No way to reset user privileges?" +#endif + if (rc < 0) + logmsg (LOG_ERR, _("cannot switch to GID %d: %s (r=%d, e=%d)"), + gid, strerror (errno), getgid (), getegid ()); + return rc; +} + +char **x_argv; +extern int reconfigure; + void @@ -298,3 +323,3 @@ wydawca_daemon () logmsg (LOG_ERR, "%s", strerror (errno)); - exit (1); + exit (EX_OSERR); } @@ -314,2 +339,3 @@ main (int argc, char **argv) + x_argv = argv; parse_options (argc, argv); @@ -323,5 +349,6 @@ main (int argc, char **argv) if (preprocess_only) - exit (gconf_preproc_run (conffile, gconf_preprocessor)); + exit (gconf_preproc_run (conffile, gconf_preprocessor) ? EX_CONFIG : 0); - gconf_parse (conffile); + if (gconf_parse (conffile)) + exit (EX_CONFIG); @@ -348,3 +375,4 @@ main (int argc, char **argv) mail_init (); - + wydawca_lock_init (); + logmsg (LOG_NOTICE, _("wydawca (%s) started"), PACKAGE_STRING); @@ -363,2 +391,12 @@ main (int argc, char **argv) + if (reconfigure) + { + int i; + wydawca_set_uid (0); + for (i = getdtablesize (); i > 2; i--) + close (i); + remove_pidfile (); + execv (x_argv[0], x_argv); + } + exit (0); diff --git a/src/wydawca.h b/src/wydawca.h index 6384738..f093504 100644 --- a/src/wydawca.h +++ b/src/wydawca.h @@ -42,2 +42,3 @@ #include <time.h> +#include <sysexits.h> @@ -65,2 +66,4 @@ +#define WYDAWCA_EX_AGAIN 1 + /* The range of directive versions we accept (major * 100 + minor) */ @@ -321,2 +324,6 @@ extern int force_startup; +extern char *lockdir; +extern time_t lock_expire_time; +extern int enable_locking; + extern int daemon_mode; @@ -366,4 +373,5 @@ enum exec_result wydawca_exec (int argc, const char **argv, int *retcode); /* Directory scanning and registering */ -void scan_spool (const struct spool *spool, int uc, uid_t *uv); -void scan_all_spools (int, uid_t *); +int scan_spool (const struct spool *spool, int uc, uid_t *uv); +int scan_all_spools (int, uid_t *); +void spool_create_timers (void); @@ -422,3 +430,3 @@ int enabled_spool_p (const struct spool *spool); -int wydawca_uid (uid_t uid); +int wydawca_set_uid (uid_t uid); @@ -506,2 +514,3 @@ void job_queue_runner (void); void check_pidfile (void); +void remove_pidfile (void); @@ -510 +519,13 @@ void check_pidfile (void); void wydawca_listener (void); + + +#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); diff --git a/tests/etc/wydawca.rcin b/tests/etc/wydawca.rcin index 36ef7a2..bc6aaa4 100644 --- a/tests/etc/wydawca.rcin +++ b/tests/etc/wydawca.rcin @@ -20,2 +20,4 @@ umask 022; +locking no; + access-method project-owner { |