openpkg/mutex.c

Mon, 28 Jan 2013 17:37:18 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Mon, 28 Jan 2013 17:37:18 +0100
changeset 758
a2c6460cfb16
permissions
-rw-r--r--

Correct socket error reporting improvement with IPv6 portable code,
after helpful recommendation by Saúl Ibarra Corretgé on OSips devlist.

     1 /*
     2 **  mutex.c -- OpenPKG Mutex Utility
     3 **  Copyright (c) 2008-2012 OpenPKG GmbH <http://openpkg.com/>
     4 **
     5 **  This software is property of the OpenPKG GmbH, DE MUC HRB 160208.
     6 **  All rights reserved. Licenses which grant limited permission to use,
     7 **  copy, modify and distribute this software are available from the
     8 **  OpenPKG GmbH.
     9 **
    10 **  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
    11 **  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    12 **  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
    13 **  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
    14 **  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    15 **  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    16 **  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    17 **  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    18 **  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    19 **  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
    20 **  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    21 **  SUCH DAMAGE.
    22 */
    24 /*  standard system headers  */
    25 #include <stdio.h>
    26 #include <stdlib.h>
    27 #include <stdarg.h>
    28 #include <string.h>
    29 #include <unistd.h>
    30 #include <errno.h>
    31 #include <fcntl.h>
    32 #include <signal.h>
    33 #include <sys/types.h>
    34 #include <sys/wait.h>
    36 /*  non-standard add-on headers */
    37 #include "popt.h"
    39 /*  program information  */
    40 #define PROGRAM_NAME         "mutex"
    42 /*  error handling constants  */
    43 #define EXITCODE_NONE        00
    44 #define EXITCODE_USAGE       64
    45 #define EXITCODE_OSERR       71
    46 #define EXITCODE_CANTCREAT   73
    47 #define EXITCODE_TEMPFAIL    75
    48 #define ERRORCODE_NONE       0
    49 #define ERRORCODE_SYSTEM     1
    51 /*  global variables  */
    52 static const char           *mutex_filename  = NULL;
    53 static int                   mutex_fd        = -1;
    54 static int                   mutex_keep      = 0;
    55 static int                   mutex_timeout   = -1;
    56 static volatile int          mutex_timed_out = 0;
    58 /*  display warning and continue or display error and exit  */
    59 static void
    60 error(
    61     int exitcode,
    62     int errorcode,
    63     const char *fmt, ...)
    64 {
    65     va_list ap;
    66     int errno_safed = errno;
    68     va_start(ap, fmt);
    69     if (exitcode != 0)
    70         fprintf(stderr, PROGRAM_NAME ":ERROR: ");
    71     else
    72         fprintf(stderr, PROGRAM_NAME ":WARNING: ");
    73     vfprintf(stderr, fmt, ap);
    74     if (errorcode)
    75        fprintf(stderr, ": %s", strerror(errno_safed));
    76     fprintf(stderr, "\n");
    77     va_end(ap);
    78     if (exitcode != 0)
    79         exit(exitcode);
    80     return;
    81 }
    83 /*  acquire mutex */
    84 static int
    85 mutex_acquire(int nonblock)
    86 {
    87     static struct flock lock;
    88     int flags;
    90     /* open mutex file (and optionally create it) */
    91     flags = O_RDWR|O_CREAT;
    92     if (nonblock)
    93         flags |= O_NONBLOCK;
    94     if ((mutex_fd = open(mutex_filename, flags, 0600)) == -1) {
    95         if (errno == EAGAIN || errno == EINTR)
    96             return 0;
    97         error(EXITCODE_CANTCREAT, ERRORCODE_SYSTEM, "cannot open mutex file \"%s\"",
    98             mutex_filename);
    99     }
   101     /* acquire exclusive lock on the mutex file */
   102     lock.l_whence = SEEK_SET; /* from current point */
   103     lock.l_start  = 0;        /* -"- */
   104     lock.l_len    = 0;        /* until end of file */
   105     lock.l_type   = F_WRLCK;  /* set exclusive/read-write lock */
   106     lock.l_pid    = 0;        /* pid not actually interesting */
   107     if (fcntl(mutex_fd, nonblock ? F_SETLK : F_SETLKW, &lock) == -1) {
   108         close(mutex_fd);
   109         mutex_fd = -1;
   110         return 0;
   111     }
   113     return 1;
   114 }
   116 /*  release mutex  */
   117 static void
   118 mutex_release(void)
   119 {
   120     static struct flock unlock;
   122     if (mutex_fd != -1) {
   123         /* release lock on the mutex file */
   124         unlock.l_whence  = SEEK_SET; /* from current point */
   125         unlock.l_start   = 0;        /* -"- */
   126         unlock.l_len     = 0;        /* until end of file */
   127         unlock.l_type    = F_UNLCK;  /* unlock */
   128         unlock.l_pid     = 0;        /* pid not actually interesting */
   129         fcntl(mutex_fd, F_SETLK, &unlock);
   131         /* close mutex file */
   132         close(mutex_fd);
   133         mutex_fd = -1;
   134     }
   135     return;
   136 }
   138 /*  destroy the mutex  */
   139 static void
   140 mutex_destroy(
   141     void)
   142 {
   143     /* release lock on mutex file */
   144     mutex_release();
   146     /* optionally remove mutex file */
   147     if (!mutex_keep)
   148         unlink(mutex_filename);
   150     return;
   151 }
   153 /*  signal handler for SIGTERM  */
   154 static void
   155 handler_killed(
   156     int sig)
   157 {
   158     /* destroy mutex */
   159     mutex_destroy();
   161     /* re-raise the signal */
   162     signal(sig, SIG_DFL);
   163     if (kill(getpid(), sig) == -1)
   164         error(EXITCODE_OSERR, ERRORCODE_SYSTEM, "kill(2) failed");
   166     return;
   167 }
   169 /*  signal handler for SIGALRM  */
   170 static void
   171 handler_timeout(
   172     int sig)
   173 {
   174     mutex_timed_out = 1;
   175     return;
   176 }
   178 /*  command line options  */
   179 static struct poptOption options_tab[] = {
   180     { "keep",    'k', POPT_ARG_NONE, &mutex_keep,    0, NULL, NULL },
   181     { "timeout", 't', POPT_ARG_INT,  &mutex_timeout, 0, NULL, NULL },
   182     POPT_TABLEEND
   183 };
   185 /*  main program procedure  */
   186 int
   187 main(
   188     int argc,
   189     char *argv[])
   190 {
   191     int status;
   192     pid_t child;
   193     int locked;
   194     char c;
   195     const char **args;
   196     poptContext options_ctx;
   198     /* process command line arguments */
   199     options_ctx = poptGetContext(argv[0], argc, (const char **)argv,
   200         options_tab, POPT_CONTEXT_NO_EXEC|POPT_CONTEXT_POSIXMEHARDER);
   201     while ((c = poptGetNextOpt(options_ctx)) > 0)
   202         ;
   203     if (c < -1)
   204         error(EXITCODE_USAGE, ERRORCODE_NONE, "command line option \"%s\": %s",
   205             poptBadOption(options_ctx, POPT_BADOPTION_NOALIAS), poptStrerror(c));
   206     mutex_filename = poptGetArg(options_ctx);
   207     if (mutex_filename == NULL) {
   208         fprintf(stderr,
   209             "usage: %s [--keep|-k] [--timeout|-t <seconds>] <mutex-file> <command> [<arguments>]\n",
   210             PROGRAM_NAME);
   211         exit(EXITCODE_USAGE);
   212     }
   213     args = poptGetArgs(options_ctx);
   215     /* optionally set up a timeout */
   216     if (mutex_timeout > 0) {
   217         struct sigaction act;
   218         act.sa_handler = handler_timeout;
   219         act.sa_flags = 0;
   220         sigemptyset(&act.sa_mask);
   221         sigaction(SIGALRM, &act, NULL);
   222         alarm(mutex_timeout);
   223     }
   225     /* try to acquire the mutex lock */
   226     locked = mutex_acquire(1 /* non-blocking */);
   227     while (!locked && !mutex_timed_out && mutex_timeout != 0)
   228         locked = mutex_acquire(0 /* blocking */);
   230     /* optionally destroy timeout */
   231     if (mutex_timeout > 0)
   232         alarm(0);
   234     /* exit in case we failed to acquire the mutex lock */
   235     if (!locked)
   236         error(EXITCODE_TEMPFAIL, ERRORCODE_NONE, "%s: already locked", mutex_filename);
   238     /* execute the command */
   239     if (atexit(mutex_destroy) == -1)
   240         error(EXITCODE_OSERR, ERRORCODE_SYSTEM, "atexit(3) failed");
   241     if ((child = fork()) == -1)
   242         error(EXITCODE_OSERR, ERRORCODE_SYSTEM, "fork(2) failed");
   243     if (child == 0) {
   244         /* the child process */
   245         mutex_release();
   246         execvp((char *)args[0], (char **)args);
   247         error(EXITCODE_NONE, ERRORCODE_SYSTEM, "execvp(2) failed: %s", args[0]);
   248         exit(1);
   249     }
   250     poptFreeContext(options_ctx);
   252     /* wait for child process and return its exit status */
   253     signal(SIGINT,  SIG_IGN);
   254     signal(SIGQUIT, SIG_IGN);
   255     signal(SIGTERM, handler_killed);
   256     if (waitpid(child, &status, 0) == -1)
   257         error(EXITCODE_OSERR, ERRORCODE_SYSTEM, "waitpid(2) failed");
   258     return (WIFEXITED(status) ? WEXITSTATUS(status) : 1);
   259 }

mercurial