1/* 2 * LTlock.c -- Lsof Test locking tests 3 * 4 * V. Abell 5 * Purdue University 6 */ 7 8 9/* 10 * Copyright 2002 Purdue Research Foundation, West Lafayette, Indiana 11 * 47907. All rights reserved. 12 * 13 * Written by V. Abell. 14 * 15 * This software is not subject to any license of the American Telephone 16 * and Telegraph Company or the Regents of the University of California. 17 * 18 * Permission is granted to anyone to use this software for any purpose on 19 * any computer system, and to alter it and redistribute it freely, subject 20 * to the following restrictions: 21 * 22 * 1. Neither the authors nor Purdue University are responsible for any 23 * consequences of the use of this software. 24 * 25 * 2. The origin of this software must not be misrepresented, either by 26 * explicit claim or by omission. Credit to the authors and Purdue 27 * University must appear in documentation and sources. 28 * 29 * 3. Altered versions must be plainly marked as such, and must not be 30 * misrepresented as being the original software. 31 * 32 * 4. This notice may not be removed or altered. 33 */ 34 35#ifndef lint 36static char copyright[] = 37"@(#) Copyright 2002 Purdue Research Foundation.\nAll rights reserved.\n"; 38#endif 39 40#include "LsofTest.h" 41#include "lsof_fields.h" 42 43 44#if defined(LT_DIAL_aix) 45/* 46 * AIX-specific items 47 */ 48 49#define USE_FCNTL 50#endif /* defined(LT_DIAL_aix) */ 51 52 53#if defined(LT_DIAL_bsdi) 54/* 55 * BSDI-specific items 56 */ 57 58#define USE_FCNTL 59#endif /* defined(LT_DIAL_bsdi) */ 60 61 62#if defined(LT_DIAL_darwin) 63/* 64 * Darwin-specific items 65 */ 66 67/* 68 * There is no Darwin USE_* definition, because lock support in lsof for 69 * Darwin is inadequate for this test. 70 */ 71#endif /* defined(LT_DIAL_darwin) */ 72 73 74#if defined(LT_DIAL_du) 75/* 76 * DEC_OSF/1|Digital_UNIX|Tru64_UNIX-specific items 77 */ 78 79#define USE_FCNTL 80#endif /* defined(LT_DIAL_du) */ 81 82 83#if defined(LT_DIAL_freebsd) 84/* 85 * FreeBSD-specific items 86 */ 87 88#define USE_FCNTL 89#endif /* defined(LT_DIAL_freebsd) */ 90 91 92#if defined(LT_DIAL_linux) 93/* 94 * Linux-specific items 95 */ 96 97#define USE_FCNTL 98#endif /* defined(LT_DIAL_linux) */ 99 100 101#if defined(LT_DIAL_netbsd) 102/* 103 * NetBSD-specific items 104 */ 105 106#define USE_FCNTL 107#endif /* defined(LT_DIAL_netbsd) */ 108 109 110#if defined(LT_DIAL_openbsd) 111/* 112 * OpenBSD-specific items 113 */ 114 115#define USE_FCNTL 116#endif /* defined(LT_DIAL_openbsd) */ 117 118 119#if defined(LT_DIAL_hpux) 120/* 121 * HP-UX-specific items 122 */ 123 124#define USE_FCNTL 125#endif /* defined(LT_DIAL_hpux) */ 126 127 128#if defined(LT_DIAL_ns) 129/* 130 * NEXTSTEP-specific items 131 */ 132 133#define USE_FLOCK 134#endif /* defined(LT_DIAL_ns) */ 135 136 137#if defined(LT_DIAL_osr) 138/* 139 * OSR-specific items 140 */ 141 142#define USE_FCNTL 143#endif /* defined(LT_DIAL_osr) */ 144 145 146#if defined(LT_DIAL_ou) 147/* 148 * OpenUNIX-specific items 149 */ 150 151#define USE_FCNTL 152#endif /* defined(LT_DIAL_ou) */ 153 154 155#if defined(LT_DIAL_openbsd) 156/* 157 * OpenBSD-specific items 158 */ 159 160#define USE_FCNTL 161#endif /* defined(LT_DIAL_openbsd) */ 162 163 164#if defined(LT_DIAL_solaris) 165/* 166 * Solaris-specific items 167 */ 168 169#define USE_FCNTL 170#endif /* defined(solaris) */ 171 172 173#if defined(LT_DIAL_uw) 174/* 175 * UnixWare-specific items 176 */ 177 178#define USE_FCNTL 179#endif /* defined(LT_DIAL_uw) */ 180 181 182#if !defined(USE_FLOCK) && !defined(USE_FCNTL) 183/* 184 * Here begins the version of this program for dialects that don't support 185 * flock() or fcntl() locking. 186 */ 187 188 189/* 190 * Main program for dialects that don't support flock() of fcntl() locking. 191 */ 192 193int 194main(argc, argv) 195 int argc; /* argument count */ 196 char *argv[]; /* arguments */ 197{ 198 char *pn; /* program name */ 199/* 200 * Get program name and issue error message. 201 */ 202 if ((pn = (char *)strrchr(argv[0], '/'))) 203 pn++; 204 else 205 pn = argv[0]; 206 (void) printf("%s ... %s\n", pn, LT_DONT_DO_TEST); 207 return(0); 208} 209#else /* defined(USE_FLOCK) || defined(USE_FCNTL) */ 210 211 212/* 213 * Local definitions 214 */ 215 216#define FULL_EX_LOCK 0 /* get a full file exclusive lock */ 217#define FULL_SH_LOCK 1 /* get a full file shared lock */ 218#define PART_EX_LOCK 2 /* get a partial file exclusive lock */ 219#define PART_SH_LOCK 3 /* get a partial file shared lock */ 220 221 222/* 223 * Globals 224 */ 225 226int Fd = -1; /* test file descriptor; open if >= 0 */ 227pid_t MyPid = (pid_t)0; /* PID of this process */ 228char *Path = (char *)NULL; /* test file path; none if NULL */ 229char *Pn = (char *)NULL; /* program name */ 230 231 232/* 233 * Local function prototypes 234 */ 235 236_PROTOTYPE(static void cleanup,(void)); 237_PROTOTYPE(static char *lkfile,(int ty)); 238_PROTOTYPE(static char *tstwlsof,(char *opt, char *xlk)); 239_PROTOTYPE(static char *unlkfile,(int ty)); 240 241 242/* 243 * Main program for dialects that support locking tests. 244 */ 245 246int 247main(argc, argv) 248 int argc; /* argument count */ 249 char *argv[]; /* arguments */ 250{ 251 char buf[2048]; /* temporary buffer */ 252 char *em; /* error message pointer */ 253 int ti; /* temporary index */ 254 char *tcp; /* temporary character pointer */ 255 int tlen; /* temporary length -- e.g., as 256 * returned by MkStrCpy() */ 257 char *tstR = (char *)NULL; /* "R" lock test result */ 258 char *tstr = (char *)NULL; /* "r" lock test result */ 259 char *tstW = (char *)NULL; /* "W" lock test result */ 260 char *tstw = (char *)NULL; /* "w" lock test result */ 261 int xv = 0; /* exit value */ 262/* 263 * Get program name and PID, issue start message, and build space prefix. 264 */ 265 if ((Pn = strrchr(argv[0], '/'))) 266 Pn++; 267 else 268 Pn = argv[0]; 269 MyPid = getpid(); 270 (void) printf("%s ... ", Pn); 271 (void) fflush(stdout); 272 (void) PrtMsg((char *)NULL, Pn); 273/* 274 * Process arguments. 275 */ 276 if (ScanArg(argc, argv, "hp:", Pn)) 277 xv = 1; 278 if (xv || LTopt_h) { 279 (void) PrtMsg ("usage: [-h] [-p path]", Pn); 280 (void) PrtMsg (" -h print help (this panel)", Pn); 281 (void) PrtMsgX(" -p path define test file path", Pn, cleanup, 282 xv); 283 } 284/* 285 * See if lsof can be executed and can access kernel memory. 286 */ 287 if ((em = IsLsofExec())) 288 (void) PrtMsgX(em, Pn, cleanup, 1); 289 if ((em = CanRdKmem())) 290 (void) PrtMsgX(em, Pn, cleanup, 1); 291/* 292 * If a path was supplied in an "-p path" option, use it. Otherwise construct 293 * a path in the CWD. 294 */ 295 if (!(Path = LTopt_p)) { 296 (void) snprintf(buf, sizeof(buf), "./config.LTlock%ld", 297 (long)MyPid); 298 buf[sizeof(buf) - 1] = '\0'; 299 Path = MkStrCpy(buf, &tlen); 300 } 301/* 302 * Fill buffer for writing to the test file. 303 */ 304 for (ti = 0; ti < sizeof(buf); ti++) { 305 buf[ti] = (char)(ti & 0xff); 306 } 307/* 308 * Open a new test file at the specified path. 309 */ 310 (void) unlink(Path); 311 if ((Fd = open(Path, O_RDWR|O_CREAT, 0600)) < 0) { 312 (void) fprintf(stderr, "ERROR!!! can't open %s\n", Path); 313 314print_file_error: 315 316 MsgStat = 1; 317 (void) snprintf(buf, sizeof(buf) - 1, " Errno %d: %s", 318 errno, strerror(errno)); 319 buf[sizeof(buf) - 1] = '\0'; 320 (void) PrtMsgX(buf, Pn, cleanup, 1); 321 } 322/* 323 * Write a buffer load at the beginning of the file. 324 */ 325 if (write(Fd, buf, sizeof(buf)) != sizeof(buf)) { 326 (void) fprintf(stderr, 327 "ERROR!!! can't write %d bytes to the beginning of %s\n", 328 (int)sizeof(buf), Path); 329 goto print_file_error; 330 } 331/* 332 * Fsync() the file. 333 */ 334 if (fsync(Fd)) { 335 (void) fprintf(stderr, "ERROR!!! can't fsync %s\n", Path); 336 goto print_file_error; 337 } 338/* 339 * Quit (with a hint) if the test file is on an NFS file system. 340 */ 341 if (!tstwlsof("-wNa", " ")) { 342 (void) printf("ERROR!!! %s is NFS-mounted.\n", Path); 343 MsgStat = 1; 344 (void) PrtMsg ("Lsof can't report lock information on files that", Pn); 345 (void) PrtMsg ("are located on file systems mounted from a remote", Pn); 346 (void) PrtMsg ("NFS server.\n", Pn); 347 (void) PrtMsg ("Hint: try using \"-p path\" to supply a path in a", Pn); 348 (void) PrtMsg ("non-NFS file system.\n", Pn); 349 (void) PrtMsgX("See 00FAQ and 00TEST for more information.", Pn, 350 cleanup, 1); 351 } 352/* 353 * Get an exclusive lock on the entire file and test it with lsof. 354 */ 355 if ((em = lkfile(FULL_EX_LOCK))) 356 (void) PrtMsgX(em, Pn, cleanup, 1); 357 if ((tstW = tstwlsof("-w", "W"))) 358 (void) PrtMsg(tstW, Pn); 359/* 360 * Get a shared lock on the entire file and test it with lsof. 361 */ 362 if ((em = unlkfile(FULL_EX_LOCK))) 363 (void) PrtMsgX(em, Pn, cleanup, 1); 364 if ((em = lkfile(FULL_SH_LOCK))) 365 (void) PrtMsgX(em, Pn, cleanup, 1); 366 if ((tstR = tstwlsof("-w", "R"))) 367 (void) PrtMsg(tstR, Pn); 368 369# if defined(USE_FLOCK) 370/* 371 * If using flock(), skip the byte lock tests. 372 */ 373 tstr = tstw = (char *)NULL; 374# endif /* defined(USE_FLOCK) */ 375 376# if defined(USE_FCNTL) 377/* 378 * If using fcntl(), do exclusive and shared byte lock tests, 379 */ 380 if ((em = unlkfile(FULL_SH_LOCK))) 381 (void) PrtMsgX(em, Pn, cleanup, 1); 382 if ((em = lkfile(PART_EX_LOCK))) 383 (void) PrtMsgX(em, Pn, cleanup, 1); 384 if ((tstw = tstwlsof("-w", "w"))) 385 (void) PrtMsg(tstw, Pn); 386 if ((em = unlkfile(PART_EX_LOCK))) 387 (void) PrtMsgX(em, Pn, cleanup, 1); 388 if ((em = lkfile(PART_SH_LOCK))) 389 (void) PrtMsgX(em, Pn, cleanup, 1); 390 if ((tstr = tstwlsof("-w", "r"))) 391 (void) PrtMsg(tstr, Pn); 392# endif /* defined(USE_FCNTL) */ 393 394/* 395 * Compute exit value and exit. 396 */ 397 if (tstr || tstR || tstw || tstW) { 398 tcp = (char *)NULL; 399 xv = 1; 400 } else { 401 tcp = "OK"; 402 xv = 0; 403 } 404 (void) PrtMsgX(tcp, Pn, cleanup, xv); 405 return(0); 406} 407 408 409/* 410 * cleanup() -- release resources 411 */ 412 413static void 414cleanup() 415{ 416 if (Fd >= 0) { 417 (void) close(Fd); 418 Fd = -1; 419 if (Path) { 420 (void) unlink(Path); 421 Path = (char *)NULL; 422 } 423 } 424} 425 426 427/* 428 * lkfile() -- lock the test file 429 */ 430 431static char * 432lkfile(ty) 433 int ty; /* a *_*_LOCK requested */ 434{ 435 char buf[2048]; /* temporary buffer */ 436 int ti; /* temporary integer */ 437 438# if defined(USE_FLOCK) 439 int flf; /* flock() function */ 440# endif /* defined(USE_FLOCK) */ 441 442# if defined(USE_FCNTL) 443 struct flock fl; /* flock control structure */ 444/* 445 * Check fcntl() lock request. 446 */ 447 (void) memset((void *)&fl, 0, sizeof(fl)); 448 switch(ty) { 449 case FULL_EX_LOCK: 450 fl.l_type = F_WRLCK; 451 break; 452 case FULL_SH_LOCK: 453 fl.l_type = F_RDLCK; 454 break; 455 case PART_EX_LOCK: 456 fl.l_type = F_WRLCK; 457 fl.l_len = (off_t)1; 458 break; 459 case PART_SH_LOCK: 460 fl.l_type = F_RDLCK; 461 fl.l_len = (off_t)1; 462 break; 463 default: 464 (void) snprintf(buf, sizeof(buf) - 1, 465 "ERROR!!! unknown lock type: %d", ty); 466 buf[sizeof(buf) - 1] = '\0'; 467 return(MkStrCpy(buf, &ti)); 468 } 469/* 470 * Lock test file with fcntl(). 471 */ 472 if (fcntl(Fd, F_SETLK, &fl) != -1) 473 return((char *)NULL); 474 (void) snprintf(buf, sizeof(buf) - 1, "ERROR!!! fcntl() lock error: %s", 475 strerror(errno)); 476 buf[sizeof(buf) - 1] = '\0'; 477 return(MkStrCpy(buf, &ti)); 478# endif /* defined(USE_FCNTL) */ 479 480# if defined(USE_FLOCK) 481/* 482 * Check flock() lock request. 483 */ 484 switch(ty) { 485 case FULL_EX_LOCK: 486 flf = LOCK_EX; 487 break; 488 case FULL_SH_LOCK: 489 flf = LOCK_SH; 490 break; 491 case PART_EX_LOCK: 492 case PART_SH_LOCK: 493 return("ERROR!!! flock() doesn't support partial locks"); 494 break; 495 default: 496 (void) snprintf(buf, sizeof(buf) - 1, 497 "ERROR!!! unknown flock() type: %d", ty); 498 buf[sizeof(buf) - 1] = '\0'; 499 return(MkStrCpy(buf, &ti)); 500 } 501/* 502 * Acquire lock. 503 */ 504 if (!flock(Fd, flf)) 505 return((char *)NULL); 506 (void) snprintf(buf, sizeof(buf) - 1, 507 "ERROR!!! flock() %s lock failed: %s", 508 (flf == LOCK_EX) ? "exclusive" : "shared", 509 strerror(errno)); 510 buf[sizeof(buf) - 1] = '\0'; 511 return(MkStrCpy(buf, &ti)); 512# endif /* defined(USE_FLOCK) */ 513 514} 515 516 517/* 518 * tstwlsof() -- test the open file with lsof 519 */ 520 521static char * 522tstwlsof(opt, xlk) 523 char *opt; /* extra lsof options */ 524 char *xlk; /* expected lock value */ 525{ 526 char buf[2048]; /* temporary buffer */ 527 LTfldo_t *cmdp; /* command pointer */ 528 LTfldo_t *devp; /* device pointer */ 529 char *cem; /* current error message pointer */ 530 int ff = 0; /* file found status */ 531 LTfldo_t *fop; /* field output pointer */ 532 LTfldo_t *inop; /* inode number pointer */ 533 LTfldo_t *lkp; /* lock pointer */ 534 LTdev_t lsofdc; /* lsof device components */ 535 int nf; /* number of fields */ 536 LTfldo_t *nmp; /* file name pointer */ 537 char *opv[4]; /* option vector for ExecLsof() */ 538 char *pem = (char *)NULL; /* previous error message pointer */ 539 pid_t pid; /* PID */ 540 int pids = 0; /* PID found status */ 541 struct stat sb; /* stat(2) buffer */ 542 LTdev_t stdc; /* stat(2) device components */ 543 char *tcp; /* temporary character pointer */ 544 int ti; /* temporary integer */ 545 LTfldo_t *typ; /* file type pointer */ 546/* 547 * Make sure there is an expected lock value. 548 */ 549 if (!xlk || !*xlk) 550 (void) PrtMsgX("ERROR!!! no expected lock value", Pn, cleanup, 1); 551/* 552 * Get test file's information. 553 */ 554 if (stat(Path, &sb)) { 555 (void) snprintf(buf, sizeof(buf) - 1, 556 "ERROR!!! can't stat(2) %s: %s", Path, strerror(errno)); 557 buf[sizeof(buf) - 1] = '\0'; 558 (void) PrtMsgX(buf, Pn, cleanup, 1); 559 } 560/* 561 * Extract components from test file's device number. 562 */ 563 if ((cem = ConvStatDev(&sb.st_dev, &stdc))) 564 (void) PrtMsgX(cem, Pn, cleanup, 1); 565/* 566 * Complete the option vector and start lsof execution. 567 */ 568 ti = 0; 569 if (opt && *opt) 570 opv[ti++] = opt; 571 572#if defined(USE_LSOF_C_OPT) 573 opv[ti++] = "-C"; 574#endif /* defined(USE_LSOF_C_OPT) */ 575 576 opv[ti++] = Path; 577 opv[ti] = (char *)NULL; 578 if ((cem = ExecLsof(opv))) 579 return(cem); 580/* 581 * Read lsof output. 582 */ 583 while (!ff && (fop = RdFrLsof(&nf, &cem))) { 584 if (cem) { 585 if (pem) 586 (void) PrtMsg(pem, Pn); 587 return(cem); 588 } 589 switch (fop->ft) { 590 case LSOF_FID_PID: 591 592 /* 593 * This is a process information line. 594 */ 595 pid = (pid_t)atoi(fop->v); 596 pids = 1; 597 cmdp = (LTfldo_t *)NULL; 598 for (fop++, ti = 1; ti < nf; fop++, ti++) { 599 switch (fop->ft) { 600 case LSOF_FID_CMD: 601 cmdp = fop; 602 break; 603 } 604 } 605 if (!cmdp || (pid != MyPid)) 606 pids = 0; 607 break; 608 case LSOF_FID_FD: 609 610 /* 611 * This is a file descriptor line. Make sure its number matches the 612 * test file's descriptor number. 613 * 614 * Scan for lock and name fields. 615 */ 616 if (!pids) 617 break; 618 for (ti = 0, tcp = fop->v; *tcp; tcp++) { 619 620 /* 621 * Convert file descriptor to a number. 622 */ 623 if (*tcp == ' ') 624 continue; 625 if (((int)*tcp < (int)'0') || ((int)*tcp > (int)'9')) { 626 ti = -1; 627 break; 628 } 629 ti = (ti * 10) + (int)*tcp - (int)'0'; 630 } 631 if (Fd != ti) 632 break; 633 devp = inop = lkp = nmp = (LTfldo_t *)NULL; 634 for (fop++, ti = 1; ti < nf; fop++, ti++) { 635 switch(fop->ft) { 636 case LSOF_FID_DEVN: 637 devp = fop; 638 break; 639 case LSOF_FID_INODE: 640 inop = fop; 641 break; 642 case LSOF_FID_LOCK: 643 lkp = fop; 644 break; 645 case LSOF_FID_NAME: 646 nmp = fop; 647 break; 648 case LSOF_FID_TYPE: 649 typ = fop; 650 break; 651 } 652 } 653 /* 654 * Check the results of the file descriptor field scan. 655 * 656 * (Don't compare path names because of symbolic link interference.) 657 */ 658 if (!devp || !inop || !nmp || !typ) 659 break; 660 if (strcasecmp(typ->v, "reg") && strcasecmp(typ->v, "vreg")) 661 break; 662 if (ConvLsofDev(devp->v, &lsofdc)) 663 break; 664 if ((stdc.maj != lsofdc.maj) 665 || (stdc.min != lsofdc.min) 666 || (stdc.unit != lsofdc.unit)) 667 break; 668 (void) snprintf(buf, sizeof(buf) - 1, "%u", 669 (unsigned int)sb.st_ino); 670 buf[sizeof(buf) - 1] = '\0'; 671 if (strcmp(inop->v, buf)) 672 break; 673 /* 674 * The specified file has been located. Check its lock status. 675 */ 676 ff = 1; 677 if (!lkp || strcmp(lkp->v, xlk)) { 678 if (pem) 679 (void) PrtMsg(pem, Pn); 680 (void) snprintf(buf, sizeof(buf) - 1, 681 "lock mismatch: expected %s, got \"%s\"", xlk, 682 lkp ? lkp->v : "(none)"); 683 pem = MkStrCpy(buf, &ti); 684 } 685 break; 686 } 687 } 688 (void) StopLsof(); 689 if (!ff) { 690 if (pem) 691 (void) PrtMsg(pem, Pn); 692 (void) snprintf(buf, sizeof(buf) - 1, 693 "lock test file %s not found by lsof", Path); 694 buf[sizeof(buf) - 1] = '\0'; 695 return(MkStrCpy(buf, &ti)); 696 } 697 return(pem); 698} 699 700 701/* 702 * unlkfile() -- unlock the test file 703 */ 704 705static char * 706unlkfile(ty) 707 int ty; /* current *_*_LOCK lock typ */ 708{ 709 char buf[2048]; /* temporary buffer */ 710 int ti; /* temporary integer */ 711 712# if defined(USE_FCNTL) 713 struct flock fl; /* flock control structure */ 714/* 715 * Check current fcntl() lock type. 716 */ 717 (void) memset((void *)&fl, 0, sizeof(fl)); 718 switch(ty) { 719 case FULL_EX_LOCK: 720 case FULL_SH_LOCK: 721 break; 722 case PART_EX_LOCK: 723 case PART_SH_LOCK: 724 fl.l_len = (off_t)1; 725 break; 726 default: 727 (void) snprintf(buf, sizeof(buf) - 1, 728 "ERROR!!! unknown unlock type: %d", ty); 729 buf[sizeof(buf) - 1] = '\0'; 730 return(MkStrCpy(buf, &ti)); 731 } 732/* 733 * Unlock test file with fcntl(). 734 */ 735 fl.l_type = F_UNLCK; 736 if (fcntl(Fd, F_SETLK, &fl) != -1) 737 return((char *)NULL); 738 (void) snprintf(buf, sizeof(buf) - 1, "ERROR!!! fcntl() unlock error: %s", 739 strerror(errno)); 740 buf[sizeof(buf) - 1] = '\0'; 741 return(MkStrCpy(buf, &ti)); 742# endif /* defined(USE_FCNTL) */ 743 744# if defined(USE_FLOCK) 745/* 746 * Check current flock() lock type. 747 */ 748 switch(ty) { 749 case FULL_EX_LOCK: 750 case FULL_SH_LOCK: 751 break; 752 default: 753 (void) snprintf(buf, sizeof(buf) - 1, 754 "ERROR!!! unknown unlock type: %s", ty); 755 buf[sizeof(buf) - 1] = '\0'; 756 return(MkStrCpy(buf, &ti)); 757 } 758/* 759 * Unlock file with flock(). 760 */ 761 if (!flock(Fd, LOCK_UN)) 762 return((char *)NULL); 763 (void) snprintf(buf, sizeof(buf) - 1, "ERROR!!! flock() unlock error: %s", 764 strerror(errno)); 765 return(MkStrCpy(buf, &ti)); 766# endif /* defined(USE_FLOCK) */ 767 768} 769#endif /* !defined(USE_FLOCK) && !defined(USE_FCNTL) */ 770