]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/libsmutil/t-lockfile.c
Merge commit '850ef5ae11d69ea3381bd310f564f025fc8caea3'
[FreeBSD/FreeBSD.git] / contrib / sendmail / libsmutil / t-lockfile.c
1 /*
2  * Copyright (c) 2005 Proofpoint, Inc. and its suppliers.
3  *      All rights reserved.
4  *
5  * By using this file, you agree to the terms and conditions set
6  * forth in the LICENSE file which can be found at the top level of
7  * the sendmail distribution.
8  *
9  */
10
11 #include <sm/gen.h>
12 SM_IDSTR(id, "@(#)$Id: t-lockfile.c,v 1.2 2013-11-22 20:51:50 ca Exp $")
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <sendmail.h>
16
17 #define IOBUFSZ 64
18 char iobuf[IOBUFSZ];
19 #define FIRSTLINE       "first line\n"
20 #define LASTLINE        "last line\n"
21 static int noio, chk;
22 static pid_t pid;
23
24 /*
25 **  OPENFILE -- open a file
26 **
27 **      Parameters:
28 **              owner -- create file?
29 **              filename -- name of file.
30 **              flags -- flags for open(2)
31 **
32 **      Returns:
33 **              >=0 fd
34 **              <0 on failure.
35 */
36
37 static int
38 openfile(owner, filename, flags)
39         int owner;
40         char *filename;
41         int flags;
42 {
43         int fd;
44
45         if (owner)
46                 flags |= O_CREAT;
47         fd = open(filename, flags, 0640);
48         if (fd >= 0)
49                 return fd;
50         fprintf(stderr, "%d: %ld: owner=%d, open(%s) failed\n",
51                 (int) pid, (long) time(NULL), owner, filename);
52         return -1;
53 }
54
55 /*
56 **  WRBUF -- write iobuf to fd
57 **
58 **      Parameters:
59 **              fd -- file descriptor.
60 **
61 **      Returns:
62 **              ==0 write was ok
63 **              !=0 on failure.
64 */
65
66 static int
67 wrbuf(fd)
68         int fd;
69 {
70         int r;
71
72         if (noio)
73                 return 0;
74         r = write(fd, iobuf, sizeof(iobuf));
75         if (sizeof(iobuf) == r)
76                 return 0;
77         fprintf(stderr, "%d: %ld: owner=1, write(%s)=fail\n",
78                 (int) pid, (long) time(NULL), iobuf);
79         return 1;
80 }
81
82 /*
83 **  RDBUF -- read from fd
84 **
85 **      Parameters:
86 **              fd -- file descriptor.
87 **              xbuf -- expected content.
88 **
89 **      Returns:
90 **              ==0 read was ok and content matches
91 **              !=0 otherwise
92 */
93
94 static int
95 rdbuf(fd, xbuf)
96         int fd;
97         const char *xbuf;
98 {
99         int r;
100
101         if (noio)
102                 return 0;
103         r = read(fd, iobuf, sizeof(iobuf));
104         if (sizeof(iobuf) != r)
105         {
106                 fprintf(stderr, "%d: %ld: owner=0, read()=fail\n",
107                         (int) pid, (long) time(NULL));
108                 return 1;
109         }
110         if (strncmp(iobuf, xbuf, strlen(xbuf)))
111         {
112                 fprintf(stderr, "%d: %ld: owner=0, read=%s expected=%s\n",
113                         (int) pid, (long) time(NULL), iobuf, xbuf);
114                 return 1;
115         }
116         return 0;
117 }
118
119 /*
120 **  LOCKTESTWR -- test WR/EX file locking
121 **
122 **      Parameters:
123 **              owner -- create file?
124 **              filename -- name of file.
125 **              flags -- flags for open(2)
126 **              delay -- how long to keep file locked?
127 **
128 **      Returns:
129 **              0 on success
130 **              != 0 on failure.
131 */
132
133 #define DBGPRINTR(str)  \
134         do      \
135         {       \
136                 fprintf(stderr, "%d: %ld: owner=0, ", (int) pid,        \
137                         (long) time(NULL));     \
138                 fprintf(stderr, str, filename, shared ? "RD" : "EX");   \
139         } while (0)
140
141 static int
142 locktestwr(filename, flags, delay)
143         char *filename;
144         int flags;
145         int delay;
146 {
147         int fd;
148         bool locked;
149
150         fd = openfile(1, filename, flags);
151         if (fd < 0)
152                 return errno;
153         locked = lockfile(fd, filename, "[owner]", LOCK_EX);
154         if (!locked)
155         {
156                 fprintf(stderr, "%d: %ld: owner=1, lock(%s) failed\n",
157                         (int) pid, (long) time(NULL), filename);
158                 return 1;
159         }
160         else
161                 fprintf(stderr, "%d: %ld: owner=1, lock(%s) ok\n",
162                         (int) pid, (long) time(NULL), filename);
163
164         sm_strlcpy(iobuf, FIRSTLINE, sizeof(iobuf));
165         if (wrbuf(fd))
166                 return 1;
167         if (delay > 0)
168                 sleep(delay);
169         sm_strlcpy(iobuf, LASTLINE, sizeof(iobuf));
170         if (wrbuf(fd))
171                 return 1;
172         locked = lockfile(fd, filename, "[owner]", LOCK_UN);
173         if (!locked)
174         {
175                 fprintf(stderr, "%d: %ld: owner=1, unlock(%s) failed\n",
176                         (int) pid, (long) time(NULL), filename);
177                 return 1;
178         }
179         fprintf(stderr, "%d: %ld: owner=1, unlock(%s) done\n",
180                 (int) pid, (long) time(NULL), filename);
181         if (fd > 0)
182         {
183                 close(fd);
184                 fd = -1;
185         }
186         return 0;
187 }
188
189 /*
190 **  CHKLCK -- check whether fd is locked (only for fcntl())
191 **
192 **      Parameters:
193 **              owner -- create file?
194 **              filename -- name of file.
195 **              flags -- flags for open(2)
196 **              delay -- how long to keep file locked?
197 **
198 **      Returns:
199 **              0 if not locked
200 **              >0 pid of process which holds a WR lock
201 **              <0 error
202 */
203
204 static long
205 chklck(fd)
206         int fd;
207 {
208 #if !HASFLOCK
209         int action, i;
210         struct flock lfd;
211
212         (void) memset(&lfd, '\0', sizeof lfd);
213         lfd.l_type = F_RDLCK;
214         action = F_GETLK;
215         while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR)
216                 continue;
217         if (i < 0)
218                 return (long)i;
219         if (F_WRLCK == lfd.l_type)
220                 return (long)lfd.l_pid;
221         return 0L;
222 #else /* !HASFLOCK */
223         fprintf(stderr, "%d: %ld: flock(): no lock test\n",
224                 (int) pid, (long) time(NULL));
225         return -1L;
226 #endif /* !HASFLOCK */
227 }
228
229 /*
230 **  LOCKTESTRD -- test file locking for reading
231 **
232 **      Parameters:
233 **              filename -- name of file.
234 **              flags -- flags for open(2)
235 **              delay -- how long is file locked by owner?
236 **              shared -- LOCK_{EX/SH}
237 **
238 **      Returns:
239 **              0 on success
240 **              != 0 on failure.
241 */
242
243 static int
244 locktestrd(filename, flags, delay, shared)
245         char *filename;
246         int flags;
247         int delay;
248         int shared;
249 {
250         int fd, cnt;
251         int lt;
252         bool locked;
253
254         fd = openfile(0, filename, flags);
255         if (fd < 0)
256                 return errno;
257         if (chk)
258         {
259                 long locked;
260
261                 locked = chklck(fd);
262                 if (locked > 0)
263                         fprintf(stderr, "%d: %ld: file=%s status=locked pid=%ld\n",
264                                  (int) pid, (long) time(NULL), filename, locked);
265                 else if (0 == locked)
266                         fprintf(stderr, "%d: %ld: file=%s status=not_locked\n",
267                                  (int) pid, (long) time(NULL), filename);
268                 else
269                         fprintf(stderr, "%d: %ld: file=%s status=unknown\n",
270                                  (int) pid, (long) time(NULL), filename);
271                 goto end;
272         }
273
274         if (shared)
275                 lt = LOCK_SH;
276         else
277                 lt = LOCK_EX;
278
279         for (cnt = 0; cnt < delay - 2; cnt++)
280         {
281                 /* try to get lock: should fail (nonblocking) */
282                 locked = lockfile(fd, filename, "[client]", lt|LOCK_NB);
283                 if (locked)
284                 {
285                         DBGPRINTR("lock(%s)=%s succeeded\n");
286                         return 1;
287                 }
288                 sleep(1);
289         }
290         if (delay > 0)
291                 sleep(2);
292         locked = lockfile(fd, filename, "[client]", lt);
293         if (!locked)
294         {
295                 DBGPRINTR("lock(%s)=%s failed\n");
296                 return 1;
297         }
298         DBGPRINTR("lock(%s)=%s ok\n");
299         if (rdbuf(fd, FIRSTLINE))
300                 return 1;
301         if (rdbuf(fd, LASTLINE))
302                 return 1;
303         sleep(1);
304         locked = lockfile(fd, filename, "[client]", LOCK_UN);
305         if (!locked)
306         {
307                 DBGPRINTR("unlock(%s)=%s failed\n");
308                 return 1;
309         }
310         DBGPRINTR("unlock(%s)=%s done\n");
311
312   end:
313         if (fd > 0)
314         {
315                 close(fd);
316                 fd = -1;
317         }
318         return 0;
319 }
320
321 /*
322 **  USAGE -- show usage
323 **
324 **      Parameters:
325 **              prg -- name of program
326 **
327 **      Returns:
328 **              nothing.
329 */
330
331 static void
332 usage(prg)
333         const char *prg;
334 {
335         fprintf(stderr, "usage: %s [options]\n"
336                 "-f filename    use filename\n"
337                 "-i             do not perform I/O\n"
338                 "-n             do not try non-blocking locking first\n"
339                 "-R             only start reader process\n"
340                 "-r             use shared locking for reader\n"
341                 "-s delay       sleep delay seconds before unlocking\n"
342                 "-W             only start writer process\n"
343 #if !HASFLOCK
344                 "uses fcntl()\n"
345 #else
346                 "uses flock()\n"
347 #endif
348
349                 , prg);
350 }
351
352 int
353 main(argc, argv)
354         int argc;
355         char *argv[];
356 {
357         int ch, delay, r, status, flags, shared, nb, reader, writer;
358         char *filename;
359         pid_t fpid;
360         extern char *optarg;
361
362         delay = 5;
363         filename = "testlock";
364         flags = O_RDWR;
365         shared = nb = noio = reader = writer = chk = 0;
366 #define OPTIONS "cf:inRrs:W"
367         while ((ch = getopt(argc, argv, OPTIONS)) != -1)
368         {
369                 switch ((char) ch)
370                 {
371                   case 'c':
372                         chk = 1;
373                         break;
374
375                   case 'f':
376                         filename = optarg;
377                         break;
378
379                   case 'i':
380                         noio = 1;
381                         break;
382
383                   case 'n':
384                         nb = 0;
385                         break;
386
387                   case 'R':
388                         reader = 1;
389                         break;
390
391                   case 'r':
392                         shared = 1;
393                         break;
394
395                   case 's':
396                         delay = atoi(optarg);
397                         break;
398
399                   case 'W':
400                         writer = 1;
401                         break;
402
403                   default:
404                         usage(argv[0]);
405                         exit(69);
406                         break;
407                 }
408         }
409
410         fpid = -1;
411         if (0 == reader && 0 == writer && (fpid = fork()) < 0)
412         {
413                 perror("fork failed\n");
414                 return 1;
415         }
416
417         r = 0;
418         if (reader || fpid == 0)
419         {
420                 /* give the parent the chance to set up data */
421                 pid = getpid();
422                 sleep(1);
423                 r = locktestrd(filename, flags, nb ? delay : 0, shared);
424         }
425         if (writer || fpid > 0)
426         {
427                 fpid = getpid();
428                 r = locktestwr(filename, flags, delay);
429                 (void) wait(&status);
430         }
431         /* (void) unlink(filename); */
432         return r;
433 }