openpkg/mutex.c

changeset 428
f880f219c566
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/openpkg/mutex.c	Tue Jul 31 12:23:42 2012 +0200
     1.3 @@ -0,0 +1,260 @@
     1.4 +/*
     1.5 +**  mutex.c -- OpenPKG Mutex Utility
     1.6 +**  Copyright (c) 2008-2012 OpenPKG GmbH <http://openpkg.com/>
     1.7 +**
     1.8 +**  This software is property of the OpenPKG GmbH, DE MUC HRB 160208.
     1.9 +**  All rights reserved. Licenses which grant limited permission to use,
    1.10 +**  copy, modify and distribute this software are available from the
    1.11 +**  OpenPKG GmbH.
    1.12 +**
    1.13 +**  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
    1.14 +**  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    1.15 +**  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
    1.16 +**  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
    1.17 +**  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.18 +**  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    1.19 +**  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    1.20 +**  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    1.21 +**  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    1.22 +**  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
    1.23 +**  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    1.24 +**  SUCH DAMAGE.
    1.25 +*/
    1.26 +
    1.27 +/*  standard system headers  */
    1.28 +#include <stdio.h>
    1.29 +#include <stdlib.h>
    1.30 +#include <stdarg.h>
    1.31 +#include <string.h>
    1.32 +#include <unistd.h>
    1.33 +#include <errno.h>
    1.34 +#include <fcntl.h>
    1.35 +#include <signal.h>
    1.36 +#include <sys/types.h>
    1.37 +#include <sys/wait.h>
    1.38 +
    1.39 +/*  non-standard add-on headers */
    1.40 +#include "popt.h"
    1.41 +
    1.42 +/*  program information  */
    1.43 +#define PROGRAM_NAME         "mutex"
    1.44 +
    1.45 +/*  error handling constants  */
    1.46 +#define EXITCODE_NONE        00
    1.47 +#define EXITCODE_USAGE       64
    1.48 +#define EXITCODE_OSERR       71
    1.49 +#define EXITCODE_CANTCREAT   73
    1.50 +#define EXITCODE_TEMPFAIL    75
    1.51 +#define ERRORCODE_NONE       0
    1.52 +#define ERRORCODE_SYSTEM     1
    1.53 +
    1.54 +/*  global variables  */
    1.55 +static const char           *mutex_filename  = NULL;
    1.56 +static int                   mutex_fd        = -1;
    1.57 +static int                   mutex_keep      = 0;
    1.58 +static int                   mutex_timeout   = -1;
    1.59 +static volatile int          mutex_timed_out = 0;
    1.60 +
    1.61 +/*  display warning and continue or display error and exit  */
    1.62 +static void
    1.63 +error(
    1.64 +    int exitcode,
    1.65 +    int errorcode,
    1.66 +    const char *fmt, ...)
    1.67 +{
    1.68 +    va_list ap;
    1.69 +    int errno_safed = errno;
    1.70 +
    1.71 +    va_start(ap, fmt);
    1.72 +    if (exitcode != 0)
    1.73 +        fprintf(stderr, PROGRAM_NAME ":ERROR: ");
    1.74 +    else
    1.75 +        fprintf(stderr, PROGRAM_NAME ":WARNING: ");
    1.76 +    vfprintf(stderr, fmt, ap);
    1.77 +    if (errorcode)
    1.78 +       fprintf(stderr, ": %s", strerror(errno_safed));
    1.79 +    fprintf(stderr, "\n");
    1.80 +    va_end(ap);
    1.81 +    if (exitcode != 0)
    1.82 +        exit(exitcode);
    1.83 +    return;
    1.84 +}
    1.85 +
    1.86 +/*  acquire mutex */
    1.87 +static int
    1.88 +mutex_acquire(int nonblock)
    1.89 +{
    1.90 +    static struct flock lock;
    1.91 +    int flags;
    1.92 +
    1.93 +    /* open mutex file (and optionally create it) */
    1.94 +    flags = O_RDWR|O_CREAT;
    1.95 +    if (nonblock)
    1.96 +        flags |= O_NONBLOCK;
    1.97 +    if ((mutex_fd = open(mutex_filename, flags, 0600)) == -1) {
    1.98 +        if (errno == EAGAIN || errno == EINTR)
    1.99 +            return 0;
   1.100 +        error(EXITCODE_CANTCREAT, ERRORCODE_SYSTEM, "cannot open mutex file \"%s\"",
   1.101 +            mutex_filename);
   1.102 +    }
   1.103 +
   1.104 +    /* acquire exclusive lock on the mutex file */
   1.105 +    lock.l_whence = SEEK_SET; /* from current point */
   1.106 +    lock.l_start  = 0;        /* -"- */
   1.107 +    lock.l_len    = 0;        /* until end of file */
   1.108 +    lock.l_type   = F_WRLCK;  /* set exclusive/read-write lock */
   1.109 +    lock.l_pid    = 0;        /* pid not actually interesting */
   1.110 +    if (fcntl(mutex_fd, nonblock ? F_SETLK : F_SETLKW, &lock) == -1) {
   1.111 +        close(mutex_fd);
   1.112 +        mutex_fd = -1;
   1.113 +        return 0;
   1.114 +    }
   1.115 +
   1.116 +    return 1;
   1.117 +}
   1.118 +
   1.119 +/*  release mutex  */
   1.120 +static void
   1.121 +mutex_release(void)
   1.122 +{
   1.123 +    static struct flock unlock;
   1.124 +
   1.125 +    if (mutex_fd != -1) {
   1.126 +        /* release lock on the mutex file */
   1.127 +        unlock.l_whence  = SEEK_SET; /* from current point */
   1.128 +        unlock.l_start   = 0;        /* -"- */
   1.129 +        unlock.l_len     = 0;        /* until end of file */
   1.130 +        unlock.l_type    = F_UNLCK;  /* unlock */
   1.131 +        unlock.l_pid     = 0;        /* pid not actually interesting */
   1.132 +        fcntl(mutex_fd, F_SETLK, &unlock);
   1.133 +
   1.134 +        /* close mutex file */
   1.135 +        close(mutex_fd);
   1.136 +        mutex_fd = -1;
   1.137 +    }
   1.138 +    return;
   1.139 +}
   1.140 +
   1.141 +/*  destroy the mutex  */
   1.142 +static void
   1.143 +mutex_destroy(
   1.144 +    void)
   1.145 +{
   1.146 +    /* release lock on mutex file */
   1.147 +    mutex_release();
   1.148 +
   1.149 +    /* optionally remove mutex file */
   1.150 +    if (!mutex_keep)
   1.151 +        unlink(mutex_filename);
   1.152 +
   1.153 +    return;
   1.154 +}
   1.155 +
   1.156 +/*  signal handler for SIGTERM  */
   1.157 +static void
   1.158 +handler_killed(
   1.159 +    int sig)
   1.160 +{
   1.161 +    /* destroy mutex */
   1.162 +    mutex_destroy();
   1.163 +
   1.164 +    /* re-raise the signal */
   1.165 +    signal(sig, SIG_DFL);
   1.166 +    if (kill(getpid(), sig) == -1)
   1.167 +        error(EXITCODE_OSERR, ERRORCODE_SYSTEM, "kill(2) failed");
   1.168 +
   1.169 +    return;
   1.170 +}
   1.171 +
   1.172 +/*  signal handler for SIGALRM  */
   1.173 +static void
   1.174 +handler_timeout(
   1.175 +    int sig)
   1.176 +{
   1.177 +    mutex_timed_out = 1;
   1.178 +    return;
   1.179 +}
   1.180 +
   1.181 +/*  command line options  */
   1.182 +static struct poptOption options_tab[] = {
   1.183 +    { "keep",    'k', POPT_ARG_NONE, &mutex_keep,    0, NULL, NULL },
   1.184 +    { "timeout", 't', POPT_ARG_INT,  &mutex_timeout, 0, NULL, NULL },
   1.185 +    POPT_TABLEEND
   1.186 +};
   1.187 +
   1.188 +/*  main program procedure  */
   1.189 +int
   1.190 +main(
   1.191 +    int argc,
   1.192 +    char *argv[])
   1.193 +{
   1.194 +    int status;
   1.195 +    pid_t child;
   1.196 +    int locked;
   1.197 +    char c;
   1.198 +    const char **args;
   1.199 +    poptContext options_ctx;
   1.200 +
   1.201 +    /* process command line arguments */
   1.202 +    options_ctx = poptGetContext(argv[0], argc, (const char **)argv,
   1.203 +        options_tab, POPT_CONTEXT_NO_EXEC|POPT_CONTEXT_POSIXMEHARDER);
   1.204 +    while ((c = poptGetNextOpt(options_ctx)) > 0)
   1.205 +        ;
   1.206 +    if (c < -1)
   1.207 +        error(EXITCODE_USAGE, ERRORCODE_NONE, "command line option \"%s\": %s",
   1.208 +            poptBadOption(options_ctx, POPT_BADOPTION_NOALIAS), poptStrerror(c));
   1.209 +    mutex_filename = poptGetArg(options_ctx);
   1.210 +    if (mutex_filename == NULL) {
   1.211 +        fprintf(stderr,
   1.212 +            "usage: %s [--keep|-k] [--timeout|-t <seconds>] <mutex-file> <command> [<arguments>]\n",
   1.213 +            PROGRAM_NAME);
   1.214 +        exit(EXITCODE_USAGE);
   1.215 +    }
   1.216 +    args = poptGetArgs(options_ctx);
   1.217 +
   1.218 +    /* optionally set up a timeout */
   1.219 +    if (mutex_timeout > 0) {
   1.220 +        struct sigaction act;
   1.221 +        act.sa_handler = handler_timeout;
   1.222 +        act.sa_flags = 0;
   1.223 +        sigemptyset(&act.sa_mask);
   1.224 +        sigaction(SIGALRM, &act, NULL);
   1.225 +        alarm(mutex_timeout);
   1.226 +    }
   1.227 +
   1.228 +    /* try to acquire the mutex lock */
   1.229 +    locked = mutex_acquire(1 /* non-blocking */);
   1.230 +    while (!locked && !mutex_timed_out && mutex_timeout != 0)
   1.231 +        locked = mutex_acquire(0 /* blocking */);
   1.232 +
   1.233 +    /* optionally destroy timeout */
   1.234 +    if (mutex_timeout > 0)
   1.235 +        alarm(0);
   1.236 +
   1.237 +    /* exit in case we failed to acquire the mutex lock */
   1.238 +    if (!locked)
   1.239 +        error(EXITCODE_TEMPFAIL, ERRORCODE_NONE, "%s: already locked", mutex_filename);
   1.240 +
   1.241 +    /* execute the command */
   1.242 +    if (atexit(mutex_destroy) == -1)
   1.243 +        error(EXITCODE_OSERR, ERRORCODE_SYSTEM, "atexit(3) failed");
   1.244 +    if ((child = fork()) == -1)
   1.245 +        error(EXITCODE_OSERR, ERRORCODE_SYSTEM, "fork(2) failed");
   1.246 +    if (child == 0) {
   1.247 +        /* the child process */
   1.248 +        mutex_release();
   1.249 +        execvp((char *)args[0], (char **)args);
   1.250 +        error(EXITCODE_NONE, ERRORCODE_SYSTEM, "execvp(2) failed: %s", args[0]);
   1.251 +        exit(1);
   1.252 +    }
   1.253 +    poptFreeContext(options_ctx);
   1.254 +
   1.255 +    /* wait for child process and return its exit status */
   1.256 +    signal(SIGINT,  SIG_IGN);
   1.257 +    signal(SIGQUIT, SIG_IGN);
   1.258 +    signal(SIGTERM, handler_killed);
   1.259 +    if (waitpid(child, &status, 0) == -1)
   1.260 +        error(EXITCODE_OSERR, ERRORCODE_SYSTEM, "waitpid(2) failed");
   1.261 +    return (WIFEXITED(status) ? WEXITSTATUS(status) : 1);
   1.262 +}
   1.263 +

mercurial