diff -r 71503088f51b -r f880f219c566 openpkg/mutex.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/openpkg/mutex.c Tue Jul 31 12:23:42 2012 +0200
@@ -0,0 +1,260 @@
+/*
+** mutex.c -- OpenPKG Mutex Utility
+** Copyright (c) 2008-2012 OpenPKG GmbH
+**
+** This software is property of the OpenPKG GmbH, DE MUC HRB 160208.
+** All rights reserved. Licenses which grant limited permission to use,
+** copy, modify and distribute this software are available from the
+** OpenPKG GmbH.
+**
+** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
+** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+/* standard system headers */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* non-standard add-on headers */
+#include "popt.h"
+
+/* program information */
+#define PROGRAM_NAME "mutex"
+
+/* error handling constants */
+#define EXITCODE_NONE 00
+#define EXITCODE_USAGE 64
+#define EXITCODE_OSERR 71
+#define EXITCODE_CANTCREAT 73
+#define EXITCODE_TEMPFAIL 75
+#define ERRORCODE_NONE 0
+#define ERRORCODE_SYSTEM 1
+
+/* global variables */
+static const char *mutex_filename = NULL;
+static int mutex_fd = -1;
+static int mutex_keep = 0;
+static int mutex_timeout = -1;
+static volatile int mutex_timed_out = 0;
+
+/* display warning and continue or display error and exit */
+static void
+error(
+ int exitcode,
+ int errorcode,
+ const char *fmt, ...)
+{
+ va_list ap;
+ int errno_safed = errno;
+
+ va_start(ap, fmt);
+ if (exitcode != 0)
+ fprintf(stderr, PROGRAM_NAME ":ERROR: ");
+ else
+ fprintf(stderr, PROGRAM_NAME ":WARNING: ");
+ vfprintf(stderr, fmt, ap);
+ if (errorcode)
+ fprintf(stderr, ": %s", strerror(errno_safed));
+ fprintf(stderr, "\n");
+ va_end(ap);
+ if (exitcode != 0)
+ exit(exitcode);
+ return;
+}
+
+/* acquire mutex */
+static int
+mutex_acquire(int nonblock)
+{
+ static struct flock lock;
+ int flags;
+
+ /* open mutex file (and optionally create it) */
+ flags = O_RDWR|O_CREAT;
+ if (nonblock)
+ flags |= O_NONBLOCK;
+ if ((mutex_fd = open(mutex_filename, flags, 0600)) == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+ error(EXITCODE_CANTCREAT, ERRORCODE_SYSTEM, "cannot open mutex file \"%s\"",
+ mutex_filename);
+ }
+
+ /* acquire exclusive lock on the mutex file */
+ lock.l_whence = SEEK_SET; /* from current point */
+ lock.l_start = 0; /* -"- */
+ lock.l_len = 0; /* until end of file */
+ lock.l_type = F_WRLCK; /* set exclusive/read-write lock */
+ lock.l_pid = 0; /* pid not actually interesting */
+ if (fcntl(mutex_fd, nonblock ? F_SETLK : F_SETLKW, &lock) == -1) {
+ close(mutex_fd);
+ mutex_fd = -1;
+ return 0;
+ }
+
+ return 1;
+}
+
+/* release mutex */
+static void
+mutex_release(void)
+{
+ static struct flock unlock;
+
+ if (mutex_fd != -1) {
+ /* release lock on the mutex file */
+ unlock.l_whence = SEEK_SET; /* from current point */
+ unlock.l_start = 0; /* -"- */
+ unlock.l_len = 0; /* until end of file */
+ unlock.l_type = F_UNLCK; /* unlock */
+ unlock.l_pid = 0; /* pid not actually interesting */
+ fcntl(mutex_fd, F_SETLK, &unlock);
+
+ /* close mutex file */
+ close(mutex_fd);
+ mutex_fd = -1;
+ }
+ return;
+}
+
+/* destroy the mutex */
+static void
+mutex_destroy(
+ void)
+{
+ /* release lock on mutex file */
+ mutex_release();
+
+ /* optionally remove mutex file */
+ if (!mutex_keep)
+ unlink(mutex_filename);
+
+ return;
+}
+
+/* signal handler for SIGTERM */
+static void
+handler_killed(
+ int sig)
+{
+ /* destroy mutex */
+ mutex_destroy();
+
+ /* re-raise the signal */
+ signal(sig, SIG_DFL);
+ if (kill(getpid(), sig) == -1)
+ error(EXITCODE_OSERR, ERRORCODE_SYSTEM, "kill(2) failed");
+
+ return;
+}
+
+/* signal handler for SIGALRM */
+static void
+handler_timeout(
+ int sig)
+{
+ mutex_timed_out = 1;
+ return;
+}
+
+/* command line options */
+static struct poptOption options_tab[] = {
+ { "keep", 'k', POPT_ARG_NONE, &mutex_keep, 0, NULL, NULL },
+ { "timeout", 't', POPT_ARG_INT, &mutex_timeout, 0, NULL, NULL },
+ POPT_TABLEEND
+};
+
+/* main program procedure */
+int
+main(
+ int argc,
+ char *argv[])
+{
+ int status;
+ pid_t child;
+ int locked;
+ char c;
+ const char **args;
+ poptContext options_ctx;
+
+ /* process command line arguments */
+ options_ctx = poptGetContext(argv[0], argc, (const char **)argv,
+ options_tab, POPT_CONTEXT_NO_EXEC|POPT_CONTEXT_POSIXMEHARDER);
+ while ((c = poptGetNextOpt(options_ctx)) > 0)
+ ;
+ if (c < -1)
+ error(EXITCODE_USAGE, ERRORCODE_NONE, "command line option \"%s\": %s",
+ poptBadOption(options_ctx, POPT_BADOPTION_NOALIAS), poptStrerror(c));
+ mutex_filename = poptGetArg(options_ctx);
+ if (mutex_filename == NULL) {
+ fprintf(stderr,
+ "usage: %s [--keep|-k] [--timeout|-t ] []\n",
+ PROGRAM_NAME);
+ exit(EXITCODE_USAGE);
+ }
+ args = poptGetArgs(options_ctx);
+
+ /* optionally set up a timeout */
+ if (mutex_timeout > 0) {
+ struct sigaction act;
+ act.sa_handler = handler_timeout;
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGALRM, &act, NULL);
+ alarm(mutex_timeout);
+ }
+
+ /* try to acquire the mutex lock */
+ locked = mutex_acquire(1 /* non-blocking */);
+ while (!locked && !mutex_timed_out && mutex_timeout != 0)
+ locked = mutex_acquire(0 /* blocking */);
+
+ /* optionally destroy timeout */
+ if (mutex_timeout > 0)
+ alarm(0);
+
+ /* exit in case we failed to acquire the mutex lock */
+ if (!locked)
+ error(EXITCODE_TEMPFAIL, ERRORCODE_NONE, "%s: already locked", mutex_filename);
+
+ /* execute the command */
+ if (atexit(mutex_destroy) == -1)
+ error(EXITCODE_OSERR, ERRORCODE_SYSTEM, "atexit(3) failed");
+ if ((child = fork()) == -1)
+ error(EXITCODE_OSERR, ERRORCODE_SYSTEM, "fork(2) failed");
+ if (child == 0) {
+ /* the child process */
+ mutex_release();
+ execvp((char *)args[0], (char **)args);
+ error(EXITCODE_NONE, ERRORCODE_SYSTEM, "execvp(2) failed: %s", args[0]);
+ exit(1);
+ }
+ poptFreeContext(options_ctx);
+
+ /* wait for child process and return its exit status */
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTERM, handler_killed);
+ if (waitpid(child, &status, 0) == -1)
+ error(EXITCODE_OSERR, ERRORCODE_SYSTEM, "waitpid(2) failed");
+ return (WIFEXITED(status) ? WEXITSTATUS(status) : 1);
+}
+