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.

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

mercurial