flock.c revision 1.4
1/* $NetBSD: flock.c,v 1.4 2012/11/02 02:07:19 wiz 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.4 2012/11/02 02:07:19 wiz 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 47struct option flock_longopts[] = { 48 { "debug", no_argument, 0, 'd' }, 49 { "help", no_argument, 0, 'h' }, 50 { "nonblock", no_argument, 0, 'n' }, 51 { "nb", no_argument, 0, 'n' }, 52 { "close", no_argument, 0, 'o' }, 53 { "shared", no_argument, 0, 's' }, 54 { "exclusive", no_argument, 0, 'x' }, 55 { "unlock", no_argument, 0, 'u' }, 56 { "verbose", no_argument, 0, 'v' }, 57 { "command", required_argument, 0, 'c' }, 58 { "wait", required_argument, 0, 'w' }, 59 { "timeout", required_argument, 0, 'w' }, 60 { NULL, 0, 0, 0 }, 61}; 62 63static int verbose = 0; 64static sig_atomic_t timeout_expired; 65 66static __dead void usage(void) 67{ 68 fprintf(stderr, "Usage: %s [-dnosvx] [-w timeout] lockfile|lockdir [-c command]| " 69 "command ...\n\t%s [-dnsuvx] [-w timeout] lockfd\n", getprogname(), 70 getprogname()); 71 exit(EXIT_FAILURE); 72} 73 74static __dead void 75sigalrm(int sig) 76{ 77 timeout_expired++; 78} 79 80static const char * 81lock2name(int l) 82{ 83 static char buf[1024]; 84 int nb = l & LOCK_NB; 85 86 l &= ~LOCK_NB; 87 if (nb) 88 strlcpy(buf, "LOCK_NB|", sizeof(buf)); 89 else 90 buf[0] = '\0'; 91 92 switch (l) { 93 case LOCK_SH: 94 strlcat(buf, "LOCK_SH", sizeof(buf)); 95 return buf; 96 case LOCK_EX: 97 strlcat(buf, "LOCK_EX", sizeof(buf)); 98 return buf; 99 case LOCK_UN: 100 strlcat(buf, "LOCK_UN", sizeof(buf)); 101 return buf; 102 default: 103 snprintf(buf, sizeof(buf), "*%d*", l | nb); 104 return buf; 105 } 106} 107 108int 109main(int argc, char *argv[]) 110{ 111 int c; 112 int lock = LOCK_EX; 113 double timeout = 0; 114 int cls = 0; 115 int fd = -1; 116 int debug = 0; 117 char *mcargv[] = { 118 __UNCONST(_PATH_BSHELL), __UNCONST("-c"), NULL, NULL 119 }; 120 char **cmdargv = NULL; 121 122 setprogname(argv[0]); 123 124 while ((c = getopt_long(argc, argv, "+dnosuw:x", flock_longopts, NULL)) 125 != -1) 126 switch (c) { 127 case 'd': 128 debug++; 129 break; 130 case 'x': 131 lock = LOCK_EX | (lock & ~LOCK_NB); 132 break; 133 case 'n': 134 lock |= LOCK_NB; 135 break; 136 case 's': 137 lock = LOCK_SH | (lock & ~LOCK_NB); 138 break; 139 case 'u': 140 lock = LOCK_UN | (lock & ~LOCK_NB); 141 break; 142 case 'w': 143 timeout = strtod(optarg, NULL); 144 break; 145 case 'v': 146 verbose = 1; 147 break; 148 case 'o': 149 cls = 1; 150 break; 151 default: 152 usage(); 153 } 154 155 argc -= optind; 156 argv += optind; 157 158 switch (argc) { 159 case 0: 160 usage(); 161 case 1: 162 if (cls) 163 usage(); 164 fd = strtol(argv[0], NULL, 0); // XXX: error checking 165 if (debug) 166 fprintf(stderr, "descriptor %s lock %s\n", 167 argv[0], lock2name(lock)); 168 if (lock == LOCK_UN) 169 usage(); 170 default: 171 if (strcmp(argv[1], "-c") == 0 || 172 strcmp(argv[1], "--command") == 0) { 173 if (argc == 2) 174 usage(); 175 mcargv[2] = argv[2]; 176 cmdargv = mcargv; 177 } else 178 cmdargv = argv + 1; 179 180 if ((fd = open(argv[0], O_RDONLY)) == -1) { 181 if (errno != ENOENT || 182 (fd = open(argv[0], O_RDWR|O_CREAT, 0600)) == -1) 183 err(EXIT_FAILURE, "Cannot open `%s'", argv[0]); 184 } 185 if (debug) 186 fprintf(stderr, "file %s lock %s command %s ...\n", 187 argv[0], lock2name(lock), cmdargv[0]); 188 break; 189 } 190 191 if (timeout) { 192 signal(SIGALRM, sigalrm); 193 alarm((int)timeout); // XXX: User timer_create() 194 if (debug) 195 fprintf(stderr, "alarm %d\n", (int)timeout); 196 } 197 198 while (flock(fd, lock) == -1) { 199 if (errno == EINTR && timeout_expired == 0) 200 continue; 201 if (verbose) 202 err(EXIT_FAILURE, "flock(%d, %s)", fd, lock2name(lock)); 203 else 204 return EXIT_FAILURE; 205 } 206 207 if (timeout) 208 alarm(0); 209 210 if (cls) 211 (void)close(fd); 212 213 if (cmdargv != NULL) { 214 execvp(cmdargv[0], cmdargv); 215 err(EXIT_FAILURE, "execvp '%s'", cmdargv[0]); 216 } 217 return 0; 218} 219