aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2009-02-24 18:45:51 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2009-02-24 18:45:51 +0200
commitc9178a6fef0184baed0e8456bb6c6d4091b4997c (patch)
tree0c97d7aeb6235a469cd071d7d0b589f45a9d35f6
parentb1472caae9a1b6905b6bbe42e69539b29febcf5c (diff)
downloadwydawca-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.modules4
-rw-r--r--src/Makefile.am1
-rw-r--r--src/config.c8
-rw-r--r--src/directive.c4
-rw-r--r--src/diskio.c6
-rw-r--r--src/exec.c2
-rw-r--r--src/getopt.m42
-rw-r--r--src/job.c45
-rw-r--r--src/lock.c239
-rw-r--r--src/net.c14
-rw-r--r--src/pidfile.c10
-rw-r--r--src/process.c25
-rw-r--r--src/triplet.c4
-rw-r--r--src/wydawca.c50
-rw-r--r--src/wydawca.h27
-rw-r--r--tests/etc/wydawca.rcin2
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);
}
diff --git a/src/exec.c b/src/exec.c
index eb4c8c4..7293ca1 100644
--- a/src/exec.c
+++ b/src/exec.c
@@ -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)>]);
diff --git a/src/job.c b/src/job.c
index 545d270..2c177cb 100644
--- a/src/job.c
+++ b/src/job.c
@@ -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);
+ }
+}
diff --git a/src/net.c b/src/net.c
index 03f9a5a..3874827 100644
--- a/src/net.c
+++ b/src/net.c
@@ -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 {

Return to:

Send suggestions and report system problems to the System administrator.