1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28/*
29 * lockfs
30 *	user interface to lockfs functionality
31 */
32#include <sys/types.h>
33#include <stdio.h>
34#include <string.h>
35#include <unistd.h>
36#include <fcntl.h>
37#include <sys/mntent.h>
38#include <sys/mnttab.h>
39#include <errno.h>
40#include <sys/lockfs.h>
41#include <sys/filio.h>
42
43#define	bzero(s, n)	memset(s, 0, n);
44
45/*
46 * command line processing
47 */
48extern char	*optarg;
49extern int	optind;
50extern int	opterr;
51
52extern void exit();
53
54static void exitusage();
55static void printstatusline(char *, char *, char *);
56static void printstatus(char *);
57static void flushfs(char *);
58static void lockfs(char *);
59static void getmntnames();
60static void getcmdnames(int, char **, int);
61
62/*
63 * -a = all
64 * -v = verbose
65 */
66int all		= 0;
67int verbose	= 0;
68
69/*
70 * exitstatus
71 *	0 all ok
72 *	1 internal error
73 *	2 system call error
74 */
75int exitstatus	= 0;
76
77/*
78 * list of filenames
79 */
80struct filename {
81	struct filename	*fn_next;
82	char		*fn_name;
83};
84struct filename	*fnanchor	= 0;
85
86/*
87 * default request is `file system lock status'
88 * default lock type is `unlock'
89 * -wnduhfe changes them
90 */
91int request	= _FIOLFSS;
92ushort_t	lock	= LOCKFS_ULOCK;
93
94/*
95 * default comment is null
96 *	-c changes it
97 */
98caddr_t comment	= 0;
99ulong_t	comlen	= 0;
100
101/*
102 * for prettyprint
103 */
104int firsttime	= 0;
105
106/*
107 * no unlocks printed
108 */
109int no_unlocks_printed	= 0;
110
111/*
112 * file system was modified during hlock/wlock/elock
113 */
114#define	LOCKWARN(FN, S)	\
115{ \
116	if (verbose) \
117		printf("WARNING: %s was modified while %s locked\n", FN, S); \
118	exitstatus = 2; \
119}
120
121/*
122 * forward reference
123 */
124char	*malloc();
125
126int
127main(int argc, char *argv[])
128{
129	int		c;
130	struct filename	*fnp;
131
132	exitstatus = 0;
133
134	/*
135	 * process command line
136	 */
137	opterr = 0;
138	optarg = 0;
139
140	while ((c = getopt(argc, argv, "vfwnduheac:")) != -1)
141		switch (c) {
142		case 'v':
143			verbose = 1;
144			break;
145		case 'f':
146			request = _FIOFFS;
147			break;
148		case 'w':
149			lock    = LOCKFS_WLOCK;
150			request = _FIOLFS;
151			break;
152		case 'n':
153			lock    = LOCKFS_NLOCK;
154			request = _FIOLFS;
155			break;
156		case 'd':
157			lock    = LOCKFS_DLOCK;
158			request = _FIOLFS;
159			break;
160		case 'h':
161			lock    = LOCKFS_HLOCK;
162			request = _FIOLFS;
163			break;
164		case 'e':
165			lock	= LOCKFS_ELOCK;
166			request = _FIOLFS;
167			break;
168		case 'u':
169			lock    = LOCKFS_ULOCK;
170			request = _FIOLFS;
171			break;
172		case 'a':
173			all = 1;
174			break;
175		case 'c':
176			comment = optarg;
177			comlen  = strlen(optarg)+1;
178			request = _FIOLFS;
179			break;
180		default:
181			exitusage();
182			break;
183		}
184
185	if (argc == 1) {
186		no_unlocks_printed = 1;
187		all = 1;
188	}
189
190	if (all)
191		/*
192		 * use /etc/mtab
193		 */
194		getmntnames();
195	else
196		/*
197		 * use command line
198		 */
199		getcmdnames(argc, argv, optind);
200
201	/*
202	 * for each filename, doit
203	 */
204	for (fnp = fnanchor; fnp; fnp = fnp->fn_next) {
205		switch (request) {
206		case _FIOLFSS:
207			printstatus(fnp->fn_name);
208			break;
209		case _FIOLFS:
210			lockfs(fnp->fn_name);
211			break;
212		case _FIOFFS:
213			flushfs(fnp->fn_name);
214			break;
215		default:
216			break;
217		}
218	}
219
220	/*
221	 * all done
222	 */
223	return (exitstatus);
224}
225/*
226 * exitusage
227 *	bad command line, give hint
228 */
229void
230exitusage()
231{
232	printf("usage: lockfs [-dfhnuw] [-c string] [-a] [file system ...]\n");
233	exit(1);
234}
235/*
236 * printstatusline
237 * 	prettyprint the status line
238 */
239void
240printstatusline(char *fn, char *locktype, char *comment)
241{
242	if (firsttime++ == 0)
243		printf("%-20s %-10s %s\n", "Filesystem", "Locktype", "Comment");
244	printf("%-20s %-10s %s\n", fn, locktype, comment);
245}
246/*
247 * printstatus
248 *	get and prettyprint file system lock status
249 */
250void
251printstatus(char *fn)
252{
253	int		fd;
254	int		fsmod	= 0;
255	char		*locktype;
256	char		commentbuffer[LOCKFS_MAXCOMMENTLEN+1];
257	struct lockfs	lf;
258
259	fd = open64(fn, O_RDONLY);
260	if (fd == -1) {
261		if (errno == EIO)
262			printstatusline(fn, "EIO", "May be hard locked");
263		else
264			perror(fn);
265		exitstatus = 2;
266		return;
267	}
268
269	bzero((caddr_t)&lf, sizeof (struct lockfs));
270
271	lf.lf_flags   = LOCKFS_MOD;
272	lf.lf_comlen  = LOCKFS_MAXCOMMENTLEN;
273	lf.lf_comment = commentbuffer;
274
275	if (ioctl(fd, _FIOLFSS, &lf) == -1) {
276		perror(fn);
277		close(fd);
278		exitstatus = 2;
279		return;
280	}
281	switch (lf.lf_lock) {
282	case LOCKFS_ULOCK:
283		if (no_unlocks_printed)
284			goto out;
285		if (LOCKFS_IS_BUSY(&lf))
286			locktype = "(unlock)";
287		else
288			locktype = "unlock";
289		break;
290	case LOCKFS_WLOCK:
291		if (LOCKFS_IS_BUSY(&lf))
292			locktype = "(write)";
293		else {
294			locktype = "write";
295			fsmod = LOCKFS_IS_MOD(&lf);
296		}
297		break;
298	case LOCKFS_NLOCK:
299		if (LOCKFS_IS_BUSY(&lf))
300			locktype = "(name)";
301		else
302			locktype = "name";
303		break;
304	case LOCKFS_DLOCK:
305		locktype = "delete";
306		if (LOCKFS_IS_BUSY(&lf))
307			locktype = "(delete)";
308		else
309			locktype = "delete";
310		break;
311	case LOCKFS_HLOCK:
312		if (LOCKFS_IS_BUSY(&lf))
313			locktype = "(hard)";
314		else {
315			locktype = "hard";
316			fsmod = LOCKFS_IS_MOD(&lf);
317		}
318		break;
319	case LOCKFS_ELOCK:
320		if (LOCKFS_IS_BUSY(&lf))
321			locktype = "(error)";
322		else {
323			locktype = "error";
324			fsmod = LOCKFS_IS_MOD(&lf);
325		}
326		break;
327	default:
328		if (LOCKFS_IS_BUSY(&lf))
329			locktype = "(unknown)";
330		else
331			locktype = "unknown";
332		break;
333	}
334	lf.lf_comment[lf.lf_comlen] = '\0';
335	printstatusline(fn, locktype, lf.lf_comment);
336	if (fsmod)
337		LOCKWARN(fn, locktype);
338out:
339	close(fd);
340}
341/*
342 * flushfs
343 *	push and invalidate at least the data that is *currently* dirty
344 */
345void
346flushfs(char *fn)
347{
348	int		fd;
349
350	fd = open64(fn, O_RDONLY);
351	if (fd == -1) {
352		perror(fn);
353		exitstatus = 2;
354		return;
355	}
356
357	if (ioctl(fd, _FIOFFS, NULL) == -1) {
358		perror(fn);
359		close(fd);
360		exitstatus = 2;
361		return;
362	}
363	close(fd);
364}
365/*
366 * lockfs
367 *	lock the file system
368 */
369void
370lockfs(char *fn)
371{
372	int		fd;
373	struct lockfs	lf;
374
375	fd = open64(fn, O_RDONLY);
376	if (fd == -1) {
377		perror(fn);
378		exitstatus = 2;
379		return;
380	}
381
382	bzero((caddr_t)&lf, sizeof (struct lockfs));
383
384	lf.lf_flags = LOCKFS_MOD;
385	if (ioctl(fd, _FIOLFSS, &lf) == -1) {
386		perror(fn);
387		close(fd);
388		exitstatus = 2;
389		return;
390	}
391
392	if (!LOCKFS_IS_BUSY(&lf) && LOCKFS_IS_MOD(&lf)) {
393		if (LOCKFS_IS_HLOCK(&lf))
394			LOCKWARN(fn, "hard");
395		if (LOCKFS_IS_ELOCK(&lf))
396			LOCKWARN(fn, "error");
397		if (LOCKFS_IS_WLOCK(&lf))
398			LOCKWARN(fn, "write");
399	}
400
401	lf.lf_lock	= lock;
402	lf.lf_flags	= 0;
403	lf.lf_key	= lf.lf_key;
404	lf.lf_comment	= comment;
405	lf.lf_comlen	= (comment) ? strlen(comment)+1 : 0;
406
407	if (ioctl(fd, _FIOLFS, &lf) == -1) {
408		perror(fn);
409		close(fd);
410		exitstatus = 2;
411		return;
412	}
413	close(fd);
414}
415/*
416 * getmntnames
417 *	file names from /etc/mtab
418 */
419void
420getmntnames()
421{
422	int		fnlen;
423	struct filename	*fnp;
424	struct filename	*fnpc;
425	FILE		*mnttab;
426	struct mnttab	mnt, *mntp = &mnt;
427
428	fnpc = fnanchor;
429
430	if ((mnttab = fopen(MNTTAB, "r")) == NULL) {
431		fprintf(stderr, "Can't open %s\n", MNTTAB);
432		perror(MNTTAB);
433		exit(32);
434	}
435	while ((getmntent(mnttab, mntp)) == 0) {
436		if (strcmp(mntp->mnt_fstype, MNTTYPE_UFS) != 0)
437			continue;
438		fnlen = strlen(mntp->mnt_mountp) + 1;
439		fnp = (struct filename *)malloc(sizeof (struct filename));
440		fnp->fn_name = malloc((uint_t)fnlen);
441		strcpy(fnp->fn_name, mntp->mnt_mountp);
442		fnp->fn_next = NULL;
443		if (fnpc)
444			fnpc->fn_next = fnp;
445		else
446			fnanchor = fnp;
447		fnpc = fnp;
448	}
449	fclose(mnttab);
450}
451/*
452 * getcmdnames
453 *	file names from command line
454 */
455void
456getcmdnames(int argc, char **argv, int i)
457{
458	struct filename	*fnp;
459	struct filename	*fnpc;
460
461	for (fnpc = fnanchor; i < argc; ++i) {
462		fnp = (struct filename *)malloc(sizeof (struct filename));
463		fnp->fn_name = *(argv+i);
464		fnp->fn_next = NULL;
465		if (fnpc)
466			fnpc->fn_next = fnp;
467		else
468			fnanchor = fnp;
469		fnpc = fnp;
470	}
471}
472