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
openfile(owner,filename,flags)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
wrbuf(fd)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
rdbuf(fd,xbuf)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
locktestwr(filename,flags,delay)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
chklck(fd)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
locktestrd(filename,flags,delay,shared)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
usage(prg)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
main(argc,argv)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 }
434