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 +