Merge branch 'vendor/GREP'
[dragonfly.git] / usr.bin / shlock / shlock.c
1 /*
2  * Copyright (c) 2005 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Joerg Sonnenberger <joerg@bec.de>.
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  * 
34  * $DragonFly: src/usr.bin/shlock/shlock.c,v 1.1 2005/07/23 19:47:15 joerg Exp $
35  */
36
37 #include <sys/types.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <libgen.h>
42 #include <limits.h>
43 #include <signal.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48
49 #define BUFSIZE         16
50
51 static int      create_lock(const char *, pid_t, int, int);
52 static int      check_lock(const char *, int, int);
53 static void     usage(void);
54
55 int
56 main(int argc, char **argv)
57 {
58         int ch, debug = 0, uucpstyle = 0;
59         const char *file = NULL;
60         char *endptr;
61         pid_t pid = -1;
62         long tmp_pid;
63
64         while ((ch = getopt(argc, argv, "df:p:u")) != -1) {
65                 switch (ch) {
66                 case 'd':
67                         debug = 1;
68                         break;
69                 case 'f':
70                         file = optarg;
71                         break;
72                 case 'p':
73                         errno = 0;
74                         tmp_pid = strtol(optarg, &endptr, 10);
75                         if (*endptr != '\0' || errno ||
76                             tmp_pid < 1 || (pid = tmp_pid) != tmp_pid)
77                                 errx(1, "invalid pid specified");
78                         break;
79                 case 'u':
80                         uucpstyle = 1;
81                         break;
82                 default:
83                         usage();
84                 }
85         }
86         argc -= optind;
87         argv += optind;
88
89         if (argc != 0)
90                 usage();
91
92         if (file == NULL)
93                 usage();
94
95         if (pid != -1)
96                 return(create_lock(file, pid, uucpstyle, debug));
97         else
98                 return(check_lock(file, uucpstyle, debug));
99 }
100
101 static int
102 create_lock(const char *file, pid_t pid, int uucpstyle, int debug)
103 {
104         char buf[BUFSIZE], tmpf[PATH_MAX];
105         char *dir;
106         int fd, ret;
107
108         ret = snprintf(buf, sizeof(buf), "%ld\n", (long)pid);
109         if (ret >= (int)sizeof(buf) || ret == -1)
110                 err(1, "snprintf() failed"); /* Must not happen. */
111
112         if ((dir = dirname(file)) == NULL)
113                 err(1, "dirname() failed");
114
115         ret = snprintf(tmpf, sizeof(tmpf), "%s/shlock%ld", dir, (long)getpid());
116         if (ret >= (int)sizeof(tmpf) || ret == -1)
117                 err(1, "snprintf failed");
118
119         if (debug) {
120                 printf("%s: trying lock file %s for process %ld\n",
121                        getprogname(), file, (long)pid);
122         }
123
124         while ((fd = open(tmpf, O_RDWR | O_CREAT | O_EXCL, 0644)) == -1){
125                 if (errno != EEXIST)
126                         err(1, "could not create tempory lock file");
127                 if (debug)
128                         warnx("temporary lock file %s existed already", tmpf);
129                 if (unlink(tmpf) && errno != ENOENT) {
130                         err(1, "could not remove old temporary lock file %s",
131                             tmpf);
132                 }
133                 /* Try again. */
134         }
135
136         if ((uucpstyle && write(fd, &pid, sizeof(pid)) != sizeof(pid)) ||
137             (!uucpstyle && write(fd, buf, strlen(buf)) != (int)strlen(buf))) {
138                 warn("could not write PID to temporary lock file");
139                 close(fd);
140
141                 if (unlink(tmpf))
142                         err(1, "could not remove temporary lock file %s", tmpf);
143
144                 return(1);              
145         }
146
147         close(fd);
148
149         while (link(tmpf, file)) {
150                 if (errno != EEXIST) {
151                         if (unlink(tmpf)) {
152                                 err(1,
153                                     "could not remove temporary lock file %s",
154                                     tmpf);
155                         }
156                         err(1, "could not create lock file");
157                 }
158                 if (check_lock(file, uucpstyle, debug) == 0) {
159                         if (unlink(tmpf)) {
160                                 err(1,
161                                     "could not remove temporary lock file %s",
162                                     tmpf);
163                         }
164                         return(1); /* Lock file is valid. */
165                 }
166                 if (unlink(file) == 0) {
167                         printf("%s: stale lock file removed\n", getprogname());
168                         continue;
169                 }
170                 if (unlink(tmpf)) {
171                         err(1, "could not remove temporary lock file %s",
172                             tmpf);
173                 }
174                 err(1, "could not remove stale lock file");
175         }
176
177         if (debug)
178                 printf("%s: lock successfully obtained\n", getprogname());
179
180         if (unlink(tmpf))
181                 warn("could not remove temporary lock file %s", tmpf);
182
183         return(0);
184 }
185
186 static int
187 check_lock(const char *file, int uucpstyle, int debug)
188 {
189         char buf[BUFSIZE];
190         int fd;
191         ssize_t len;
192         pid_t pid;
193
194         if ((fd = open(file, O_RDONLY)) == -1) {
195                 switch (errno) {
196                 case ENOENT:
197                         return(1); /* File doesn't exist. */
198                 default:
199                         /*
200                          * Something went wrong, bail out as
201                          * if the lock existed.
202                          */
203                         err(1, "could not open lock file");
204                 }
205         }
206
207         len = read(fd, buf, uucpstyle ? sizeof(pid_t) : sizeof(buf));
208         close(fd);
209
210         if (len < 0) {
211                 if (debug)
212                         warn("could not read lock file");
213                 return(1);
214         }
215         if (len == 0) {
216                 if (debug)
217                         warnx("found empty lock file");
218                 return(1);
219         }
220         if (uucpstyle) {
221                 if (len != sizeof(pid_t)) {
222                         if (debug)
223                                 warnx("invalid lock file format");
224                         return(1);
225                 }
226                 memcpy(&pid, buf, sizeof(pid_t));
227         } else {
228                 char *endptr;
229                 long tmp_pid;
230
231                 if (len == BUFSIZE) {
232                         if (debug)
233                                 warnx("invalid lock file format");
234                         return(1);
235                 }
236
237                 buf[BUFSIZE] = '\0';
238                 errno = 0;
239                 tmp_pid = strtol(buf, &endptr, 10);
240                 if ((*endptr != '\0' && *endptr != '\n') || errno ||
241                     tmp_pid < 1 || (pid = tmp_pid) != tmp_pid) {
242                         if (debug)
243                                 warnx("invalid lock file format");
244                         return(1);
245                 }
246         }
247
248         if (kill(pid, 0) == 0)
249                 return(0); /* Process is alive. */
250
251         switch (errno) {
252         case ESRCH:
253                 return(1); /* Process is dead. */
254         case EPERM:
255                 return(0); /* Process is alive. */
256         default:
257                 return(0); /* Something else, assume alive. */
258         }
259 }
260
261 static void
262 usage(void)
263 {
264         fprintf(stderr, "%s [-u] [-d] [-p pid] -f file\n", getprogname());
265         exit(1);
266 }