1 /* 2 * Copyright (C) 2008 Paul Armstrong. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <ctype.h> 27 #include <err.h> 28 #include <errno.h> 29 #include <limits.h> 30 #include <locale.h> 31 #include <fcntl.h> 32 #include <signal.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <unistd.h> 38 #include <wait.h> 39 40 #define SLEEP_TIME 1 /* Sleep time between parent checks */ 41 42 #if !defined(TEXT_DOMAIN) 43 #define TEXT_DOMAIN "SYS_TEST" 44 #endif 45 46 /* globals for use in signal handlers */ 47 int pid = 0; 48 boolean_t lock_existed = B_FALSE; 49 char *file_name = NULL; 50 51 static void 52 usage(void) 53 { 54 (void) fprintf(stderr, 55 gettext("Usage: lockf -l|-t|-w -f /path/to/lock | -u pid\n")); 56 exit(EXIT_FAILURE); 57 } 58 59 /*ARGSUSED*/ 60 static void 61 print_pid(int signal_number) 62 { 63 (void) printf("%d\n", pid); 64 exit(EXIT_SUCCESS); 65 } 66 67 /*ARGSUSED*/ 68 static void 69 cleanup(int signal_handler) 70 { 71 if (!lock_existed) { 72 if (-1 == unlink(file_name)) 73 perror(file_name); 74 } 75 exit(EXIT_SUCCESS); 76 } 77 78 int 79 main(int argc, char **argv) 80 { 81 boolean_t arg_err = B_FALSE; 82 boolean_t lock = B_FALSE, test = B_FALSE; 83 boolean_t unlock = B_FALSE, wait_lock = B_FALSE; 84 extern char *optarg; 85 extern int optind; 86 int calling_pid; 87 int child_status; 88 int filedes; 89 int flag; 90 int return_code; 91 mode_t mode = S_IRUSR | S_IWUSR; 92 struct stat *statbuf; 93 94 (void) setlocale(LC_ALL, ""); 95 (void) textdomain(TEXT_DOMAIN); 96 97 while ((flag = getopt(argc, argv, "f:ltu:w")) != -1) { 98 switch (flag) { 99 case 'f': 100 file_name = optarg; 101 break; 102 case 'l': 103 if (unlock || wait_lock || test) 104 arg_err = B_TRUE; 105 lock = B_TRUE; 106 break; 107 case 't': 108 if (unlock || lock) 109 arg_err = B_TRUE; 110 test = B_TRUE; 111 break; 112 case 'u': 113 if (lock || test) 114 arg_err = B_TRUE; 115 unlock = B_TRUE; 116 pid = atoi(optarg); 117 break; 118 case 'w': 119 if (unlock || lock || test) 120 arg_err = B_TRUE; 121 wait_lock = B_TRUE; 122 lock = B_TRUE; 123 break; 124 default: 125 usage(); 126 } 127 } 128 129 if (1 == argc) 130 usage(); 131 132 argc -= optind; 133 argv -= optind; 134 135 if (1 <= argc) 136 usage(); 137 138 if (arg_err) { 139 (void) fprintf(stderr, 140 gettext("Use only one of -l, -t, -u or -w\n")); 141 usage(); 142 } 143 144 if ((lock || test) && (NULL == file_name)) { 145 (void) fprintf(stderr, 146 gettext("-l, -t and -w require -f\n")); 147 usage(); 148 } 149 if (unlock && pid <= 0) { 150 (void) fprintf(stderr, 151 gettext("-u requires a valid PID\n")); 152 usage(); 153 } 154 155 if (unlock) { 156 if (-1 == kill(pid, SIGINT)) { 157 perror("kill"); 158 return (EXIT_FAILURE); 159 } else { 160 return (EXIT_SUCCESS); 161 } 162 } 163 164 /* Make test cheap, do it before the fork and return immediately */ 165 if (test) { 166 statbuf = malloc(sizeof (struct stat)); 167 if (NULL == statbuf) { 168 perror("malloc"); 169 exit(EXIT_FAILURE); 170 } 171 if (stat(file_name, statbuf) == -1) { 172 switch (errno) { 173 case ENOENT: 174 exit(EXIT_SUCCESS); 175 /*NOTREACHED*/ 176 case EACCES: 177 err(EXIT_FAILURE, 178 gettext("Bad permissions on lockfile")); 179 /*NOTREACHED*/ 180 case ENAMETOOLONG: 181 err(EXIT_FAILURE, "lockfile name too long"); 182 } 183 } 184 if ((filedes = open(file_name, O_WRONLY, mode)) == -1) { 185 perror(file_name); 186 return (EXIT_FAILURE); 187 } 188 return_code = lockf(filedes, F_TEST, 0); 189 if (return_code < 0) 190 return (EXIT_FAILURE); 191 return (EXIT_SUCCESS); 192 } 193 194 calling_pid = getppid(); 195 if (1 == calling_pid) 196 err(EXIT_FAILURE, "Calling process seems to have died early"); 197 198 if ((pid = fork()) < 0) { 199 perror("fork"); 200 return (EXIT_FAILURE); 201 } 202 203 if (pid != 0) { 204 (void) signal(SIGUSR1, print_pid); 205 206 if (waitpid(pid, &child_status, 0) != pid) { 207 perror("waitpid"); 208 exit(EXIT_FAILURE); 209 } 210 211 return (EXIT_FAILURE); 212 } else { 213 214 (void) close(STDIN_FILENO); 215 (void) close(STDOUT_FILENO); 216 217 return_code = chdir("/"); 218 if (return_code < 0) { 219 perror("chdir"); 220 return (EXIT_FAILURE); 221 } 222 223 /* Other users shouldn't be trying to write to our lock file */ 224 (void) umask(077); 225 226 /* 227 * Note if the file existed beforehand so we can avoid deleting 228 * it when we clean up 229 */ 230 if ((filedes = 231 open(file_name, O_WRONLY | O_CREAT | O_EXCL, mode)) == -1) { 232 233 lock_existed = B_TRUE; 234 235 if ((filedes = 236 open(file_name, O_WRONLY | O_CREAT, mode)) == -1) { 237 perror(file_name); 238 exit(EXIT_FAILURE); 239 } 240 } 241 242 (void) signal(SIGINT, cleanup); 243 (void) signal(SIGQUIT, cleanup); 244 (void) signal(SIGABRT, cleanup); 245 (void) signal(SIGTERM, cleanup); 246 247 if (wait_lock) { 248 return_code = lockf(filedes, F_LOCK, 0); 249 } else { 250 return_code = lockf(filedes, F_TLOCK, 0); 251 } 252 253 if (return_code < 0) 254 return (EXIT_FAILURE); 255 256 /* Once we have a lock (or not), then kill the parent */ 257 if (-1 == kill(getppid(), SIGUSR1)) { 258 perror("kill"); 259 return (EXIT_FAILURE); 260 } 261 262 while (calling_pid) { 263 (void) sleep(SLEEP_TIME); 264 if (-1 == kill(calling_pid, 0)) 265 calling_pid = 0; 266 } 267 268 cleanup(NULL); 269 270 return (EXIT_SUCCESS); 271 } 272 }