Misc. mdoc fixes.
[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                 free(tmpf);
145                 return(1);              
146         }
147
148         close(fd);
149
150         while (link(tmpf, file)) {
151                 if (errno != EEXIST) {
152                         if (unlink(tmpf)) {
153                                 err(1,
154                                     "could not remove temporary lock file %s",
155                                     tmpf);
156                         }
157                         err(1, "could not create lock file");
158                 }
159                 if (check_lock(file, uucpstyle, debug) == 0) {
160                         if (unlink(tmpf)) {
161                                 err(1,
162                                     "could not remove temporary lock file %s",
163                                     tmpf);
164                         }
165                         return(1); /* Lock file is valid. */
166                 }
167                 if (unlink(file) == 0) {
168                         printf("%s: stale lock file removed\n", getprogname());
169                         continue;
170                 }
171                 if (unlink(tmpf)) {
172                         err(1, "could not remove temporary lock file %s",
173                             tmpf);
174                 }
175                 err(1, "could not remove stale lock file");
176         }
177
178         if (debug)
179                 printf("%s: lock successfully obtained\n", getprogname());
180
181         if (unlink(tmpf))
182                 warn("could not remove temporary lock file %s", tmpf);
183
184         return(0);
185 }
186
187 static int
188 check_lock(const char *file, int uucpstyle, int debug)
189 {
190         char buf[BUFSIZE];
191         int fd;
192         ssize_t len;
193         pid_t pid;
194
195         if ((fd = open(file, O_RDONLY)) == -1) {
196                 switch (errno) {
197                 case ENOENT:
198                         return(1); /* File doesn't exist. */
199                 default:
200                         /*
201                          * Something went wrong, bail out as
202                          * if the lock existed.
203                          */
204                         err(1, "could not open lock file");
205                 }
206         }
207
208         len = read(fd, buf, uucpstyle ? sizeof(pid_t) : sizeof(buf));
209         close(fd);
210
211         if (len < 0) {
212                 if (debug)
213                         warn("could not read lock file");
214                 return(1);
215         }
216         if (len == 0) {
217                 if (debug)
218                         warnx("found empty lock file");
219                 return(1);
220         }
221         if (uucpstyle) {
222                 if (len != sizeof(pid_t)) {
223                         if (debug)
224                                 warnx("invalid lock file format");
225                         return(1);
226                 }
227                 memcpy(&pid, buf, sizeof(pid_t));
228         } else {
229                 char *endptr;
230                 long tmp_pid;
231
232                 if (len == BUFSIZE) {
233                         if (debug)
234                                 warnx("invalid lock file format");
235                         return(1);
236                 }
237
238                 buf[BUFSIZE] = '\0';
239                 errno = 0;
240                 tmp_pid = strtol(buf, &endptr, 10);
241                 if ((*endptr != '\0' && *endptr != '\n') || errno ||
242                     tmp_pid < 1 || (pid = tmp_pid) != tmp_pid) {
243                         if (debug)
244                                 warnx("invalid lock file format");
245                         return(1);
246                 }
247         }
248
249         if (kill(pid, 0) == 0)
250                 return(0); /* Process is alive. */
251
252         switch (errno) {
253         case ESRCH:
254                 return(1); /* Process is dead. */
255         case EPERM:
256                 return(0); /* Process is alive. */
257         default:
258                 return(0); /* Something else, assume alive. */
259         }
260 }
261
262 static void
263 usage(void)
264 {
265         fprintf(stderr, "%s [-u] [-d] [-p pid] -f file\n", getprogname());
266         exit(1);
267 }