Mon, 28 Jan 2013 17:37:18 +0100
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 }