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 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
22 * Use is subject to license terms.
23 */
24
25#pragma ident	"%Z%%M%	%I%	%E% SMI"
26
27#include <locale.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <string.h>
32#include <unistd.h>
33#include <malloc.h>
34#include <memory.h>
35#include <sys/param.h>
36#include <sys/types.h>
37#include <sys/file.h>
38#include <fcntl.h>
39#include <bsm/devices.h>
40#define	DMPFILE	"/etc/security/device_maps"
41#define	RETRY_SLEEP	6
42#define	RETRY_COUNT	10
43#define	EINVOKE	2
44#define	EFAIL 1
45
46#if !defined(TEXT_DOMAIN)
47#define	TEXT_DOMAIN "SUNW_BSM_DMINFO"
48#endif
49
50extern off_t lseek();
51
52char	*getdmapfield();
53char	*getdmapdfield();
54static void	printdmapent();
55static void	dmapi_err();
56
57static char	*prog_name;
58
59/*
60 * printdmapent(dmapp) prints a devmap_t structure pointed to by dmapp.
61 */
62static void
63printdmapent(dmapp)
64	devmap_t *dmapp;
65{
66	(void) printf("%s:", dmapp->dmap_devname);
67	(void) printf("%s:", dmapp->dmap_devtype);
68	(void) printf("%s", dmapp->dmap_devlist);
69	(void) printf("\n");
70}
71
72
73/*
74 * dmapi_err(exit_code,err_msg) prints message pointed to by err_msg to
75 * stderr. Then prints usage message to stderr. Then exits program with
76 * exit_code.
77 *
78 */
79static void
80dmapi_err(int exit_code, char *err_msg)
81{
82	if (err_msg != NULL) {
83		(void) fprintf(stderr, "dmapinfo:%s\n", err_msg);
84	}
85	if (exit_code == EINVOKE) {
86		(void) fprintf(stderr,
87			"Usage: %s [-v] [-a] [-f filename] %s\n",
88			prog_name,
89			"[-d device ...]");
90		(void) fprintf(stderr,
91			"       %s [-v] [-a] [-f filename] %s\n",
92			prog_name,
93			"[-n name ...]");
94		(void) fprintf(stderr,
95			"       %s [-v] [-a] [-f filename] %s\n",
96			prog_name,
97			"[-t type ...]");
98		(void) fprintf(stderr,
99			"       %s [-v] [-a] [-f filename] %s\n",
100			prog_name,
101			"[-u Entry]");
102	}
103
104	exit(exit_code);
105}
106
107int
108main(int argc, char **argv)
109{
110	devmap_t *dmapp;
111	devmap_t dmap;
112	char	*mptr;
113	char	*tptr;
114	char	*nptr;
115	char	*filename = DMPFILE;
116	int	name = 0;
117	int	device = 0;
118	int	file = 0;
119	int	verbose = 0;
120	int	cntr = 0;
121	int	any = 0;
122	int	update = 0;
123	int	tp = 0;
124	int	des;
125	int	status;
126
127	/* Internationalization */
128	(void) setlocale(LC_ALL, "");
129	(void) textdomain(TEXT_DOMAIN);
130
131	/*
132	 * point prog_name to invocation name
133	 */
134	if ((tptr = strrchr(*argv, '/')) != NULL)
135		prog_name = ++tptr;
136		else
137		prog_name = *argv;
138	argc--;
139	argv++;
140	/*
141	 * parse arguments
142	 */
143	while ((argc >= 1) && (argv[0][0] == '-')) {
144		switch (argv[0][1]) {
145		case 'a':
146			any++;
147			break;
148		case 'd':
149			if ((name) || (device) || (update) || (tp)) {
150				dmapi_err(EINVOKE,
151					gettext("option conflict"));
152			}
153			device++;
154			break;
155		case 'f':
156			argc--;
157			argv++;
158			if (argc <= 0)
159				dmapi_err(EINVOKE,
160					gettext("missing file name"));
161			filename = *argv;
162			file++;
163			break;
164		case 'n':
165			if ((name) || (device) || (update) || (tp)) {
166				dmapi_err(EINVOKE,
167					gettext("option conflict"));
168			}
169			name++;
170			break;
171		case 't':
172			if ((name) || (device) || (update) || (tp)) {
173				dmapi_err(EINVOKE,
174					gettext("option conflict"));
175			}
176			tp++;
177			break;
178		case 'u':
179			if ((name) || (device) || (update) || (tp)) {
180				dmapi_err(EINVOKE,
181					gettext("option conflict"));
182			}
183			update++;
184			break;
185		case 'v':
186			verbose++;
187			break;
188		default:
189			dmapi_err(EINVOKE,
190				gettext("bad option"));
191			break;
192		}
193		argc--;
194		argv++;
195	}
196	/*
197	 * -d(device) -n(name) and -u(update) switches require at least one
198	 * argument.
199	 */
200	if (file)
201		setdmapfile(filename);
202	if ((device) || (name) || (update) || (tp)) {
203		if (argc < 1) {
204			dmapi_err(EINVOKE,
205				gettext("insufficient args for this option"));
206		}
207	}
208	if (update) {
209		/*
210		 * -u(update) switch requires only one argument
211		 */
212		if (argc != 1) {
213			dmapi_err(EINVOKE,
214				gettext("too many args for this option"));
215		}
216		/*
217		 * read entry argument from stdin into a devmap_t known as dmap
218		 */
219		if ((dmap.dmap_devname = getdmapfield(*argv)) == NULL) {
220			dmapi_err(EINVOKE,
221				gettext("Bad dmap_devname in entry argument"));
222		}
223		if ((dmap.dmap_devtype = getdmapfield(NULL)) ==
224			NULL) {
225			dmapi_err(EINVOKE,
226				gettext("Bad dmap_devtype in entry Argument"));
227		}
228		if ((dmap.dmap_devlist = getdmapfield(NULL)) ==
229			NULL) {
230			dmapi_err(EINVOKE,
231				gettext("Bad dmap_devlist in entry argument"));
232		}
233		/*
234		 * Find out how long device list is and create a buffer to
235		 * hold it.  Then copy it there. This is done since we do not
236		 * want to corrupt the existing string.
237		 */
238		cntr = strlen(dmap.dmap_devlist) + 1;
239		mptr = calloc((unsigned)cntr, sizeof (char));
240		if (mptr == NULL) {
241			if (verbose) {
242				(void) fprintf(stderr,
243					gettext(
244					"dmapinfo: Cannot calloc memory\n"));
245			}
246			exit(1);
247		}
248		(void) strcpy(mptr, dmap.dmap_devlist);
249		/*
250		 * open the device maps file for read/ write. We are not
251		 * sure we want to write to it yet but we may and this is a
252		 * easy way to get the file descriptor. We want the file
253		 * descriptor so we can lock the file.
254		 */
255		if ((des = open(filename, O_RDWR)) < 0) {
256			if (verbose) {
257				(void) fprintf(stderr,
258				gettext("dmapinfo: Cannot open %s\n"),
259				    filename);
260			}
261			exit(1);
262		}
263		cntr = 0;
264#ifdef CMW
265		while ((status = flock(des, LOCK_EX | LOCK_NB) == -1) &&
266			(cntr++ < RETRY_COUNT)) {
267			(void) sleep(RETRY_SLEEP);
268		}
269#else
270		while (((status = lockf(des, F_TLOCK, 0)) == -1) &&
271			(cntr++ < RETRY_COUNT)) {
272			(void) sleep(RETRY_SLEEP);
273		}
274#endif
275		if (status == -1) {
276			if (verbose) {
277				(void) fprintf(stderr,
278			gettext("dmapinfo: Cannot lock %s\n"), filename);
279			}
280			exit(1);
281		}
282		/*
283		 * Now that we have the device_maps file then lets check
284		 * for previous entrys with the same name.  If it already
285		 * exists then we will exit with status of 1.
286		 */
287		if (verbose) {
288			(void) fprintf(stderr,
289			gettext("dmapinfo: Checking %s for name (%s).\n"),
290				filename, dmap.dmap_devname);
291		}
292		if (getdmapnam(dmap.dmap_devname) != NULL) {
293			if (verbose) {
294				(void) fprintf(stderr,
295			gettext("dmapinfo: Device name (%s) found in %s.\n"),
296					dmap.dmap_devname, filename);
297			}
298			exit(1);
299		}
300		if (verbose) {
301			(void) fprintf(stderr,
302		gettext("dmapinfo: Device name (%s) not found in %s.\n"),
303				dmap.dmap_devname, filename);
304		}
305		/*
306		 * We now Know name does not exist and now we need to check
307		 * to see if any of the devices in the device list are in the
308		 * device maps file. If the already exist then we will exit
309		 * with a status of 1.
310		 */
311		nptr = mptr;
312		nptr = getdmapdfield(nptr);
313		while (nptr) {
314			if (verbose) {
315				(void) fprintf(stderr,
316				    gettext("dmapinfo: "
317					"Check %s for device (%s).\n"),
318				    filename, nptr);
319			}
320			if (getdmapdev(nptr) != NULL) {
321				if (verbose) {
322					(void) fprintf(stderr,
323					    gettext("dmapinfo: "
324						"Device (%s) found in %s.\n"),
325					    nptr, filename);
326				}
327				exit(1);
328			}
329			if (verbose) {
330				(void) fprintf(stderr,
331				    gettext("dmapinfo: "
332					"Device (%s) not found in %s.\n"),
333				    nptr, filename);
334			}
335			nptr = getdmapdfield(NULL);
336		}
337		/*
338		 * Good the entry is uniq. So lets find out how long it is
339		 * and add it to the end of device maps file in a pretty
340		 * way.
341		 */
342		if (verbose) {
343			(void) fprintf(stderr, "dmapinfo: Adding entry to %s\n",
344				filename);
345			printdmapent(&dmap);
346		}
347		cntr = strlen(dmap.dmap_devname);
348		cntr += strlen(dmap.dmap_devtype);
349		cntr += strlen(dmap.dmap_devlist);
350		cntr += 15;
351		tptr = calloc((unsigned)cntr, sizeof (char));
352		if (tptr == NULL) {
353			exit(1);
354		}
355		(void) strcat(tptr, dmap.dmap_devname);
356		(void) strcat(tptr, ":\\\n\t");
357		(void) strcat(tptr, dmap.dmap_devtype);
358		(void) strcat(tptr, ":\\\n\t");
359		(void) strcat(tptr, dmap.dmap_devlist);
360		(void) strcat(tptr, ":\\\n\t");
361		(void) strcat(tptr, "\n");
362		cntr = strlen(tptr);
363#ifdef CMW
364		if (lseek(des, 0L, L_XTND) == -1L) {
365			exit(1);
366		}
367#else
368		if (lseek(des, 0L, SEEK_END) == -1L) {
369			exit(1);
370		}
371#endif
372		if (write(des, tptr, cntr) == -1) {
373			exit(1);
374		}
375		if (close(des) == -1) {
376			exit(1);
377		}
378		if (verbose) {
379			(void) fprintf(stderr, "dmapinfo: Entry added to %s\n",
380				filename);
381		}
382		exit(0);
383	}
384	/*
385	 * Look for devices in device_maps file. If verbose switch is set
386	 * then print entry(s) found. If "any" switch  is set then, if any
387	 * device is found will result in a exit status of 0. If "any" switch
388	 * is not set then, if any device is not will result in a exit status
389	 * of 1.
390	 */
391	if (device) {
392		setdmapent();
393		while (argc >= 1) {
394			if ((dmapp = getdmapdev(*argv)) != NULL) {
395				if (verbose) {
396					printdmapent(dmapp);
397				}
398				cntr++;
399			} else if (any == 0) {
400				enddmapent();
401				exit(1);
402			}
403			argc--;
404			argv++;
405		}
406		enddmapent();
407		if (cntr != 0)
408			exit(0);
409		exit(1);
410	}
411	/*
412	 * Look for names in device_maps file. If verbose switch is set
413	 * then print entry(s) found. If "any" switch  is set then, if any
414	 * name is found will result in a exit status of 0. If "any" switch
415	 * is not set then, if any name is not will result in a exit status
416	 * of 1.
417	 */
418	if (name) {
419		setdmapent();
420		while (argc >= 1) {
421			if ((dmapp = getdmapnam(*argv)) != NULL) {
422				if (verbose) {
423					printdmapent(dmapp);
424				}
425				cntr++;
426			} else if (any == 0)
427				exit(1);
428			argc--;
429			argv++;
430		}
431		enddmapent();
432		if (cntr != 0)
433			exit(0);
434		exit(1);
435	}
436	/*
437	 * Read all entrys from device maps file. If verbose flag is set
438	 * then all the device maps files are printed.  This is useful for
439	 * piping to grep. Also this option used without the verbose option
440	 * is useful to check for device maps file and for at least one
441	 * entry.  If the device maps file is found and there is one entry
442	 * the return status is 0.
443	 */
444	if (tp) {
445		cntr = 0;
446		setdmapent();
447		while (argc >= 1) {
448			while ((dmapp = getdmaptype(*argv)) != 0) {
449				cntr++;
450				if (verbose) {
451					printdmapent(dmapp);
452				}
453			}
454			if ((any == 0) && (cntr == 0)) {
455				enddmapent();
456				exit(1);
457			}
458			argc--;
459			argv++;
460		}
461		enddmapent();
462		if (cntr == 0)
463			exit(1);
464		exit(0);
465	}
466	/*
467	 * Read all entrys from device maps file. If verbose flag is set
468	 * then all the device maps files are printed.  This is useful for
469	 * piping to grep. Also this option used without the verbose option
470	 * is useful to check for device maps file and for atleast one
471	 * entry.  If the device maps file is found and there is one entry
472	 * the return status is 0.
473	 */
474	cntr = 0;
475	setdmapent();
476	while ((dmapp = getdmapent()) != 0) {
477		cntr++;
478		if (verbose) {
479			printdmapent(dmapp);
480		}
481	}
482	enddmapent();
483	if (cntr == 0)
484		exit(1);
485	return (0);
486}
487