1/* ----------------------------------------------------------------------- * 2 * 3 * Copyright 2003-2005 H. Peter Anvin - All Rights Reserved 4 * 5 * Permission is hereby granted, free of charge, to any person 6 * obtaining a copy of this software and associated documentation 7 * files (the "Software"), to deal in the Software without 8 * restriction, including without limitation the rights to use, 9 * copy, modify, merge, publish, distribute, sublicense, and/or 10 * sell copies of the Software, and to permit persons to whom 11 * the Software is furnished to do so, subject to the following 12 * conditions: 13 * 14 * The above copyright notice and this permission notice shall 15 * be included in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 * OTHER DEALINGS IN THE SOFTWARE. 25 * 26 * ----------------------------------------------------------------------- */ 27 28#include <errno.h> 29#include <stdio.h> 30#include <stdlib.h> 31#include <unistd.h> 32#include <getopt.h> 33#include <signal.h> 34#include <ctype.h> 35#include <string.h> 36#include <paths.h> 37#include <sysexits.h> 38#include <sys/types.h> 39#include <sys/file.h> 40#include <sys/time.h> 41#include <sys/wait.h> 42 43#define PACKAGE_STRING "util-linux-ng 2.18" 44#define _(x) (x) 45 46static const struct option long_options[] = { 47 { "shared", 0, NULL, 's' }, 48 { "exclusive", 0, NULL, 'x' }, 49 { "unlock", 0, NULL, 'u' }, 50 { "nonblocking", 0, NULL, 'n' }, 51 { "nb", 0, NULL, 'n' }, 52 { "timeout", 1, NULL, 'w' }, 53 { "wait", 1, NULL, 'w' }, 54 { "close", 0, NULL, 'o' }, 55 { "help", 0, NULL, 'h' }, 56 { "version", 0, NULL, 'V' }, 57 { 0, 0, 0, 0 } 58}; 59 60const char *program; 61 62static void usage(int ex) 63{ 64 fputs("flock (" PACKAGE_STRING ")\n", stderr); 65 fprintf(stderr, 66 _("Usage: %1$s [-sxun][-w #] fd#\n" 67 " %1$s [-sxon][-w #] file [-c] command...\n" 68 " %1$s [-sxon][-w #] directory [-c] command...\n" 69 " -s --shared Get a shared lock\n" 70 " -x --exclusive Get an exclusive lock\n" 71 " -u --unlock Remove a lock\n" 72 " -n --nonblock Fail rather than wait\n" 73 " -w --timeout Wait for a limited amount of time\n" 74 " -o --close Close file descriptor before running command\n" 75 " -c --command Run a single command string through the shell\n" 76 " -h --help Display this text\n" 77 " -V --version Display version\n"), 78 program); 79 exit(ex); 80} 81 82 83static sig_atomic_t timeout_expired = 0; 84 85static void timeout_handler(int sig) 86{ 87 (void)sig; 88 89 timeout_expired = 1; 90} 91 92 93static char * strtotimeval(const char *str, struct timeval *tv) 94{ 95 char *s; 96 long fs; /* Fractional seconds */ 97 int i; 98 99 tv->tv_sec = strtol(str, &s, 10); 100 fs = 0; 101 102 if ( *s == '.' ) { 103 s++; 104 105 for ( i = 0 ; i < 6 ; i++ ) { 106 if ( !isdigit(*s) ) 107 break; 108 109 fs *= 10; 110 fs += *s++ - '0'; 111 } 112 113 for ( ; i < 6; i++ ) 114 fs *= 10; 115 116 while ( isdigit(*s) ) 117 s++; 118 } 119 120 tv->tv_usec = fs; 121 return s; 122} 123 124int main(int argc, char *argv[]) 125{ 126 struct itimerval timeout, old_timer; 127 int have_timeout = 0; 128 int type = LOCK_EX; 129 int block = 0; 130 int fd = -1; 131 int opt, ix; 132 int do_close = 0; 133 int err; 134 int status; 135 char *eon; 136 char **cmd_argv = NULL, *sh_c_argv[4]; 137 const char *filename = NULL; 138 struct sigaction sa, old_sa; 139 140 program = argv[0]; 141 142 if ( argc < 2 ) 143 usage(EX_USAGE); 144 145 memset(&timeout, 0, sizeof timeout); 146 147 optopt = 0; 148 while ( (opt = getopt_long(argc, argv, "+sexnouw:hV?", long_options, &ix)) != EOF ) { 149 switch(opt) { 150 case 's': 151 type = LOCK_SH; 152 break; 153 case 'e': 154 case 'x': 155 type = LOCK_EX; 156 break; 157 case 'u': 158 type = LOCK_UN; 159 break; 160 case 'o': 161 do_close = 1; 162 break; 163 case 'n': 164 block = LOCK_NB; 165 break; 166 case 'w': 167 have_timeout = 1; 168 eon = strtotimeval(optarg, &timeout.it_value); 169 if ( *eon ) 170 usage(EX_USAGE); 171 break; 172 case 'V': 173 printf("flock (%s)\n", PACKAGE_STRING); 174 exit(0); 175 default: 176 /* optopt will be set if this was an unrecognized option, i.e. *not* 'h' or '?' */ 177 usage(optopt ? EX_USAGE : 0); 178 break; 179 } 180 } 181 182 if ( argc > optind+1 ) { 183 /* Run command */ 184 185 if ( !strcmp(argv[optind+1], "-c") || 186 !strcmp(argv[optind+1], "--command") ) { 187 188 if ( argc != optind+3 ) { 189 fprintf(stderr, _("%s: %s requires exactly one command argument\n"), 190 program, argv[optind+1]); 191 exit(EX_USAGE); 192 } 193 194 cmd_argv = sh_c_argv; 195 196 cmd_argv[0] = getenv("SHELL"); 197 if ( !cmd_argv[0] || !*cmd_argv[0] ) 198 cmd_argv[0] = _PATH_BSHELL; 199 200 cmd_argv[1] = "-c"; 201 cmd_argv[2] = argv[optind+2]; 202 cmd_argv[3] = 0; 203 } else { 204 cmd_argv = &argv[optind+1]; 205 } 206 207 filename = argv[optind]; 208 fd = open(filename, O_RDONLY|O_NOCTTY|O_CREAT, 0666); 209 /* Linux doesn't like O_CREAT on a directory, even though it should be a 210 no-op */ 211 if (fd < 0 && errno == EISDIR) 212 fd = open(filename, O_RDONLY|O_NOCTTY); 213 214 if ( fd < 0 ) { 215 err = errno; 216 fprintf(stderr, _("%s: cannot open lock file %s: %s\n"), 217 program, argv[optind], strerror(err)); 218 exit((err == ENOMEM||err == EMFILE||err == ENFILE) ? EX_OSERR : 219 (err == EROFS||err == ENOSPC) ? EX_CANTCREAT : 220 EX_NOINPUT); 221 } 222 223 } else if (optind < argc) { 224 /* Use provided file descriptor */ 225 226 fd = (int)strtol(argv[optind], &eon, 10); 227 if ( *eon || !argv[optind] ) { 228 fprintf(stderr, _("%s: bad number: %s\n"), program, argv[optind]); 229 exit(EX_USAGE); 230 } 231 232 } else { 233 /* Bad options */ 234 235 fprintf(stderr, _("%s: requires file descriptor, file or directory\n"), 236 program); 237 exit(EX_USAGE); 238 } 239 240 241 if ( have_timeout ) { 242 if ( timeout.it_value.tv_sec == 0 && 243 timeout.it_value.tv_usec == 0 ) { 244 /* -w 0 is equivalent to -n; this has to be special-cased 245 because setting an itimer to zero means disabled! */ 246 247 have_timeout = 0; 248 block = LOCK_NB; 249 } else { 250 memset(&sa, 0, sizeof sa); 251 252 sa.sa_handler = timeout_handler; 253 sa.sa_flags = SA_RESETHAND; 254 sigaction(SIGALRM, &sa, &old_sa); 255 256 setitimer(ITIMER_REAL, &timeout, &old_timer); 257 } 258 } 259 260 while ( flock(fd, type|block) ) { 261 switch( (err = errno) ) { 262 case EWOULDBLOCK: /* -n option set and failed to lock */ 263 exit(1); 264 case EINTR: /* Signal received */ 265 if ( timeout_expired ) 266 exit(1); /* -w option set and failed to lock */ 267 continue; /* otherwise try again */ 268 default: /* Other errors */ 269 if ( filename ) 270 fprintf(stderr, "%s: %s: %s\n", program, filename, strerror(err)); 271 else 272 fprintf(stderr, "%s: %d: %s\n", program, fd, strerror(err)); 273 exit((err == ENOLCK||err == ENOMEM) ? EX_OSERR : EX_DATAERR); 274 } 275 } 276 277 if ( have_timeout ) { 278 setitimer(ITIMER_REAL, &old_timer, NULL); /* Cancel itimer */ 279 sigaction(SIGALRM, &old_sa, NULL); /* Cancel signal handler */ 280 } 281 282 status = 0; 283 284 if ( cmd_argv ) { 285 pid_t w, f; 286 287 /* Clear any inherited settings */ 288 signal(SIGCHLD, SIG_DFL); 289 f = fork(); 290 291 if ( f < 0 ) { 292 err = errno; 293 fprintf(stderr, _("%s: fork failed: %s\n"), program, strerror(err)); 294 exit(EX_OSERR); 295 } else if ( f == 0 ) { 296 if ( do_close ) 297 close(fd); 298 err = errno; 299 execvp(cmd_argv[0], cmd_argv); 300 /* execvp() failed */ 301 fprintf(stderr, "%s: %s: %s\n", program, cmd_argv[0], strerror(err)); 302 _exit((err == ENOMEM) ? EX_OSERR: EX_UNAVAILABLE); 303 } else { 304 do { 305 w = waitpid(f, &status, 0); 306 if (w == -1 && errno != EINTR) 307 break; 308 } while ( w != f ); 309 310 if (w == -1) { 311 err = errno; 312 status = EXIT_FAILURE; 313 fprintf(stderr, "%s: waitpid failed: %s\n", program, strerror(err)); 314 } else if ( WIFEXITED(status) ) 315 status = WEXITSTATUS(status); 316 else if ( WIFSIGNALED(status) ) 317 status = WTERMSIG(status) + 128; 318 else 319 status = EX_OSERR; /* WTF? */ 320 } 321 } 322 323 return status; 324} 325 326