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 } 434