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