1 /* $NetBSD: flock.c,v 1.11 2014/08/18 09:16:35 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2012 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Christos Zoulas. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __RCSID("$NetBSD: flock.c,v 1.11 2014/08/18 09:16:35 christos Exp $"); 35 36 #include <stdio.h> 37 #include <string.h> 38 #include <fcntl.h> 39 #include <stdlib.h> 40 #include <signal.h> 41 #include <unistd.h> 42 #include <err.h> 43 #include <errno.h> 44 #include <getopt.h> 45 #include <paths.h> 46 #include <limits.h> 47 #include <time.h> 48 49 static struct option flock_longopts[] = { 50 { "debug", no_argument, 0, 'd' }, 51 { "help", no_argument, 0, 'h' }, 52 { "nonblock", no_argument, 0, 'n' }, 53 { "nb", no_argument, 0, 'n' }, 54 { "close", no_argument, 0, 'o' }, 55 { "shared", no_argument, 0, 's' }, 56 { "exclusive", no_argument, 0, 'x' }, 57 { "unlock", no_argument, 0, 'u' }, 58 { "verbose", no_argument, 0, 'v' }, 59 { "command", required_argument, 0, 'c' }, 60 { "wait", required_argument, 0, 'w' }, 61 { "timeout", required_argument, 0, 'w' }, 62 { NULL, 0, 0, 0 }, 63 }; 64 65 static sig_atomic_t timeout_expired; 66 67 static __dead __printflike(1, 2) void 68 usage(const char *fmt, ...) 69 { 70 if (fmt) { 71 va_list ap; 72 va_start(ap, fmt); 73 fprintf(stderr, "%s: ", getprogname()); 74 vfprintf(stderr, fmt, ap); 75 fputc('\n', stderr); 76 va_end(ap); 77 } 78 79 fprintf(stderr, "Usage: %s [-dnosvx] [-w timeout] lockfile|lockdir " 80 "[-c command]|command ...\n\t%s [-dnsuvx] [-w timeout] lockfd\n", 81 getprogname(), getprogname()); 82 exit(EXIT_FAILURE); 83 } 84 85 static void 86 sigalrm(int sig) 87 { 88 timeout_expired++; 89 } 90 91 static const char * 92 lock2name(int l) 93 { 94 static char buf[1024]; 95 int nb = l & LOCK_NB; 96 97 l &= ~LOCK_NB; 98 if (nb) 99 strlcpy(buf, "LOCK_NB|", sizeof(buf)); 100 else 101 buf[0] = '\0'; 102 103 switch (l) { 104 case LOCK_SH: 105 strlcat(buf, "LOCK_SH", sizeof(buf)); 106 return buf; 107 case LOCK_EX: 108 strlcat(buf, "LOCK_EX", sizeof(buf)); 109 return buf; 110 case LOCK_UN: 111 strlcat(buf, "LOCK_UN", sizeof(buf)); 112 return buf; 113 default: 114 snprintf(buf, sizeof(buf), "*%d*", l | nb); 115 return buf; 116 } 117 } 118 119 static char 120 lockchar(int l) 121 { 122 switch (l & ~LOCK_NB) { 123 case LOCK_SH: 124 return 's'; 125 case LOCK_EX: 126 return 'x'; 127 case LOCK_UN: 128 return 'u'; 129 default: 130 return '*'; 131 } 132 } 133 134 static char * 135 cmdline(char **av) 136 { 137 char *v = NULL; 138 while (*av) 139 if (v) { 140 if (asprintf(&v, "%s %s", v, *av++) < 0) 141 err(EXIT_FAILURE, "malloc"); 142 } else { 143 if ((v = strdup(*av++)) == NULL) 144 err(EXIT_FAILURE, "strdup"); 145 } 146 return v; 147 } 148 149 int 150 main(int argc, char *argv[]) 151 { 152 int c; 153 int lock = 0; 154 double timeout = 0; 155 int cls = 0; 156 int fd = -1; 157 int debug = 0; 158 int verbose = 0; 159 long l; 160 char *mcargv[] = { 161 __UNCONST(_PATH_BSHELL), __UNCONST("-c"), NULL, NULL 162 }; 163 char **cmdargv = NULL, *v; 164 #if !defined(__minix) 165 timer_t tm; 166 #else 167 struct itimerval it; 168 #endif /* !defined(__minix) */ 169 170 setprogname(argv[0]); 171 172 while ((c = getopt_long(argc, argv, "+dnosuvw:x", flock_longopts, NULL)) 173 != -1) 174 switch (c) { 175 case 'd': 176 debug++; 177 break; 178 case 'x': 179 #define T(l) (lock & ~LOCK_NB) != (l) && (lock & ~LOCK_NB) != 0 180 if (T(LOCK_EX)) 181 goto badlock; 182 lock |= LOCK_EX; 183 break; 184 case 'n': 185 lock |= LOCK_NB; 186 break; 187 case 's': 188 if (T(LOCK_SH)) 189 goto badlock; 190 lock |= LOCK_SH; 191 break; 192 case 'u': 193 if (T(LOCK_UN)) 194 goto badlock; 195 lock |= LOCK_UN; 196 break; 197 case 'w': 198 timeout = strtod(optarg, NULL); 199 break; 200 case 'v': 201 verbose = 1; 202 break; 203 case 'o': 204 cls = 1; 205 break; 206 default: 207 usage("Invalid option '%c'", c); 208 badlock: 209 usage("-%c can't be used with -%c", c, lockchar(lock)); 210 } 211 212 argc -= optind; 213 argv += optind; 214 215 if ((lock & ~LOCK_NB) == 0) 216 lock |= LOCK_EX; /* default to exclusive like linux */ 217 218 switch (argc) { 219 case 0: 220 usage("Missing lock file argument"); 221 case 1: 222 if (cls) 223 usage("Close is not valid for descriptors"); 224 errno = 0; 225 l = strtol(argv[0], &v, 0); 226 if ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) 227 err(EXIT_FAILURE, "Bad file descriptor `%s'", argv[0]); 228 if (l > INT_MAX || l < 0 || *v) 229 errx(EXIT_FAILURE, "Bad file descriptor `%s'", argv[0]); 230 fd = (int)l; 231 if (debug) { 232 fprintf(stderr, "descriptor %s lock %s\n", 233 argv[0], lock2name(lock)); 234 } 235 break; 236 237 default: 238 if ((lock & LOCK_NB) == LOCK_UN) 239 usage("Unlock is only valid for descriptors"); 240 if (strcmp(argv[1], "-c") == 0 || 241 strcmp(argv[1], "--command") == 0) { 242 if (argc == 2) 243 usage("Missing argument to %s", strcmp(argv[1], 244 "-c") == 0 ? "-c" : "--command"); 245 mcargv[2] = argv[2]; 246 cmdargv = mcargv; 247 } else 248 cmdargv = argv + 1; 249 250 if ((fd = open(argv[0], O_RDONLY)) == -1) { 251 if (errno != ENOENT || 252 (fd = open(argv[0], O_RDWR|O_CREAT, 0600)) == -1) 253 err(EXIT_FAILURE, "Cannot open `%s'", argv[0]); 254 } 255 if (debug) { 256 fprintf(stderr, "file %s lock %s command %s ...\n", 257 argv[0], lock2name(lock), v = cmdline(cmdargv)); 258 free(v); 259 } 260 break; 261 } 262 263 if (timeout) { 264 #if !defined(__minix) 265 struct sigevent ev; 266 struct itimerspec it; 267 #endif /* !defined(__minix) */ 268 struct sigaction sa; 269 270 #if !defined(__minix) 271 timespecclear(&it.it_interval); 272 it.it_value.tv_sec = timeout; 273 it.it_value.tv_nsec = (timeout - it.it_value.tv_sec) * 274 1000000000; 275 276 memset(&ev, 0, sizeof(ev)); 277 ev.sigev_notify = SIGEV_SIGNAL; 278 ev.sigev_signo = SIGALRM; 279 280 if (timer_create(CLOCK_REALTIME, &ev, &tm) == -1) 281 err(EXIT_FAILURE, "timer_create"); 282 283 if (timer_settime(tm, TIMER_RELTIME, &it, NULL) == -1) 284 err(EXIT_FAILURE, "timer_settime"); 285 #else 286 memset(&it.it_interval, 0, sizeof(it.it_interval)); 287 it.it_value.tv_sec = timeout; 288 it.it_value.tv_usec = (timeout - it.it_value.tv_sec) * 1000000; 289 290 if (setitimer(ITIMER_REAL, &it, NULL) == -1) 291 err(EXIT_FAILURE, "setitimer"); 292 293 memset(&it, 0, sizeof(it)); /* for the reset later */ 294 #endif /* !defined(__minix) */ 295 296 memset(&sa, 0, sizeof(sa)); 297 sa.sa_handler = sigalrm; 298 sigemptyset(&sa.sa_mask); 299 sa.sa_flags = 0; 300 if (sigaction(SIGALRM, &sa, NULL) == -1) 301 err(EXIT_FAILURE, "sigaction"); 302 303 if (debug) 304 fprintf(stderr, "alarm %g\n", timeout); 305 } 306 307 while (flock(fd, lock) == -1) { 308 if (errno == EINTR && timeout_expired == 0) 309 continue; 310 if (verbose) 311 err(EXIT_FAILURE, "flock(%d, %s)", fd, lock2name(lock)); 312 else 313 return EXIT_FAILURE; 314 } 315 316 if (timeout) 317 #if !defined(__minix) 318 timer_delete(tm); 319 #else 320 setitimer(ITIMER_REAL, &it, NULL); 321 #endif /* !defined(__minix) */ 322 323 if (cls) 324 (void)close(fd); 325 326 if (cmdargv != NULL) { 327 execvp(cmdargv[0], cmdargv); 328 err(EXIT_FAILURE, "execvp '%s'", v = cmdline(cmdargv)); 329 free(v); 330 } 331 return 0; 332 } 333