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 int
25 openfile(owner, filename, flags)
26 	int owner;
27 	char *filename;
28 	int flags;
29 {
30 	int fd;
31 
32 	if (owner)
33 		flags |= O_CREAT;
34 	fd = open(filename, flags, 0640);
35 	if (fd >= 0)
36 		return fd;
37 	fprintf(stderr, "%d: %ld: owner=%d, open(%s) failed\n",
38 		(int) pid, (long) time(NULL), owner, filename);
39 	return 1;
40 }
41 
42 int
43 wrbuf(fd)
44 	int fd;
45 {
46 	int r;
47 
48 	if (noio)
49 		return 0;
50 	r = write(fd, iobuf, sizeof(iobuf));
51 	if (sizeof(iobuf) == r)
52 		return 0;
53 	fprintf(stderr, "%d: %ld: owner=1, write(%s)=fail\n",
54 		(int) pid, (long) time(NULL), iobuf);
55 	return 1;
56 }
57 
58 int
59 rdbuf(fd, xbuf)
60 	int fd;
61 	const char *xbuf;
62 {
63 	int r;
64 
65 	if (noio)
66 		return 0;
67 	r = read(fd, iobuf, sizeof(iobuf));
68 	if (sizeof(iobuf) != r)
69 	{
70 		fprintf(stderr, "%d: %ld: owner=0, read()=fail\n",
71 			(int) pid, (long) time(NULL));
72 		return 1;
73 	}
74 	if (strncmp(iobuf, xbuf, strlen(xbuf)))
75 	{
76 		fprintf(stderr, "%d: %ld: owner=0, read=%s expected=%s\n",
77 			(int) pid, (long) time(NULL), iobuf, xbuf);
78 		return 1;
79 	}
80 	return 0;
81 }
82 
83 /*
84 **  LOCKTEST -- test of file locking
85 **
86 **	Parameters:
87 **		owner -- create file?
88 **		filename -- name of file.
89 **		flags -- flags for open(2)
90 **		delay -- how long to keep file locked?
91 **
92 **	Returns:
93 **		0 on success
94 **		!= 0 on failure.
95 */
96 
97 #define DBGPRINTR(str)	\
98 	do	\
99 	{	\
100 		fprintf(stderr, "%d: %ld: owner=0, ", (int) pid,	\
101 			(long) time(NULL));	\
102 		fprintf(stderr, str, filename, shared ? "RD" : "EX");	\
103 	} while (0)
104 
105 int
106 locktestwr(filename, flags, delay)
107 	char *filename;
108 	int flags;
109 	int delay;
110 {
111 	int fd;
112 	bool locked;
113 
114 	fd = openfile(1, filename, flags);
115 	if (fd < 0)
116 		return errno;
117 	locked = lockfile(fd, filename, "[owner]", LOCK_EX);
118 	if (!locked)
119 	{
120 		fprintf(stderr, "%d: %ld: owner=1, lock(%s) failed\n",
121 			(int) pid, (long) time(NULL), filename);
122 		return 1;
123 	}
124 	else
125 		fprintf(stderr, "%d: %ld: owner=1, lock(%s) ok\n",
126 			(int) pid, (long) time(NULL), filename);
127 
128 	sm_strlcpy(iobuf, FIRSTLINE, sizeof(iobuf));
129 	if (wrbuf(fd))
130 		return 1;
131 	sleep(delay);
132 	sm_strlcpy(iobuf, LASTLINE, sizeof(iobuf));
133 	if (wrbuf(fd))
134 		return 1;
135 	locked = lockfile(fd, filename, "[owner]", LOCK_UN);
136 	if (!locked)
137 	{
138 		fprintf(stderr, "%d: %ld: owner=1, unlock(%s) failed\n",
139 			(int) pid, (long) time(NULL), filename);
140 		return 1;
141 	}
142 	fprintf(stderr, "%d: %ld: owner=1, unlock(%s) done\n",
143 		(int) pid, (long) time(NULL), filename);
144 	if (fd > 0)
145 	{
146 		close(fd);
147 		fd = -1;
148 	}
149 	return 0;
150 }
151 
152 long
153 chklck(fd)
154 	int fd;
155 {
156 #if !HASFLOCK
157 	int action, i;
158 	struct flock lfd;
159 
160 	(void) memset(&lfd, '\0', sizeof lfd);
161 	lfd.l_type = F_RDLCK;
162 	action = F_GETLK;
163 	while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR)
164 		continue;
165 	if (i < 0)
166 		return (long)i;
167 	if (F_WRLCK == lfd.l_type)
168 		return (long)lfd.l_pid;
169 	return 0L;
170 #else /* !HASFLOCK */
171 	fprintf(stderr, "%d: %ld: flock: no lock test\n",
172 		(int) pid, (long) time(NULL));
173 	return -1L;
174 #endif /* !HASFLOCK */
175 }
176 
177 int
178 locktestrd(filename, flags, delay, shared)
179 	char *filename;
180 	int flags;
181 	int delay;
182 	int shared;
183 {
184 	int fd, cnt;
185 	int lt;
186 	bool locked;
187 
188 	fd = openfile(0, filename, flags);
189 	if (fd < 0)
190 		return errno;
191 	if (chk)
192 	{
193 		long locked;
194 
195 		locked = chklck(fd);
196 		if (locked > 0)
197 			fprintf(stderr, "%d: %ld: file=%s status=locked pid=%ld\n",
198 				 (int) pid, (long) time(NULL), filename, locked);
199 		else if (0 == locked)
200 			fprintf(stderr, "%d: %ld: file=%s status=not_locked\n",
201 				 (int) pid, (long) time(NULL), filename);
202 		else
203 			fprintf(stderr, "%d: %ld: file=%s status=unknown\n",
204 				 (int) pid, (long) time(NULL), filename);
205 		goto end;
206 	}
207 
208 	if (shared)
209 		lt = LOCK_SH;
210 	else
211 		lt = LOCK_EX;
212 
213 	for (cnt = 0; cnt < delay - 2; cnt++)
214 	{
215 		/* try to get lock: should fail (nonblocking) */
216 		locked = lockfile(fd, filename, "[client]", lt|LOCK_NB);
217 		if (locked)
218 		{
219 			DBGPRINTR("lock(%s)=%s succeeded\n");
220 			return 1;
221 		}
222 		sleep(1);
223 	}
224 	if (delay > 0)
225 		sleep(2);
226 	locked = lockfile(fd, filename, "[client]", lt);
227 	if (!locked)
228 	{
229 		DBGPRINTR("lock(%s)=%s failed\n");
230 		return 1;
231 	}
232 	DBGPRINTR("lock(%s)=%s ok\n");
233 	if (rdbuf(fd, FIRSTLINE))
234 		return 1;
235 	if (rdbuf(fd, LASTLINE))
236 		return 1;
237 	sleep(1);
238 	locked = lockfile(fd, filename, "[client]", LOCK_UN);
239 	if (!locked)
240 	{
241 		DBGPRINTR("unlock(%s)=%s failed\n");
242 		return 1;
243 	}
244 	DBGPRINTR("unlock(%s)=%s done\n");
245 
246   end:
247 	if (fd > 0)
248 	{
249 		close(fd);
250 		fd = -1;
251 	}
252 	return 0;
253 }
254 
255 static void
256 usage(prg)
257 	const char *prg;
258 {
259 	fprintf(stderr, "usage: %s [options]\n"
260 		"-f filename	use filename\n"
261 		"-i		do not perform I/O\n"
262 		"-n		do not try non-blocking locking first\n"
263 		"-R		only start reader process\n"
264 		"-r		use shared locking for reader\n"
265 		"-s delay	sleep delay seconds before unlocking\n"
266 		"-W		only start writer process\n"
267 		, prg);
268 }
269 
270 int
271 main(argc, argv)
272 	int argc;
273 	char *argv[];
274 {
275 	int ch, delay, r, status, flags, shared, nb, reader, writer;
276 	char *filename;
277 	pid_t fpid;
278 	extern char *optarg;
279 
280 	delay = 5;
281 	filename = "testlock";
282 	flags = O_RDWR;
283 	shared = nb = noio = reader = writer = chk = 0;
284 #define OPTIONS	"cf:inRrs:W"
285 	while ((ch = getopt(argc, argv, OPTIONS)) != -1)
286 	{
287 		switch ((char) ch)
288 		{
289 		  case 'c':
290 			chk = 1;
291 			break;
292 
293 		  case 'f':
294 			filename = optarg;
295 			break;
296 
297 		  case 'i':
298 			noio = 1;
299 			break;
300 
301 		  case 'n':
302 			nb = 0;
303 			break;
304 
305 		  case 'R':
306 			reader = 1;
307 			break;
308 
309 		  case 'r':
310 			shared = 1;
311 			break;
312 
313 		  case 's':
314 			delay = atoi(optarg);
315 			break;
316 
317 		  case 'W':
318 			writer = 1;
319 			break;
320 
321 		  default:
322 			usage(argv[0]);
323 			exit(69);
324 			break;
325 		}
326 	}
327 
328 	fpid = -1;
329 	if (0 == reader && 0 == writer && (fpid = fork()) < 0)
330 	{
331 		perror("fork failed\n");
332 		return 1;
333 	}
334 
335 	r = 0;
336 	if (reader || fpid == 0)
337 	{
338 		/* give the parent the chance to setup data */
339 		pid = getpid();
340 		sleep(1);
341 		r = locktestrd(filename, flags, nb ? delay : 0, shared);
342 	}
343 	if (writer || fpid > 0)
344 	{
345 		fpid = getpid();
346 		r = locktestwr(filename, flags, delay);
347 		(void) wait(&status);
348 	}
349 	/* (void) unlink(filename); */
350 	return r;
351 }
352