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