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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*    Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /*      All Rights Reserved   */
29 
30 #include	<sys/types.h>
31 #include	<sys/param.h>
32 #include	<stdio.h>
33 #include	<errno.h>
34 #include	<stdlib.h>
35 #include	<string.h>
36 #include	<fmtmsg.h>
37 #include	<devmgmt.h>
38 #include	<devtab.h>
39 #include	<values.h>
40 
41 
42 /*
43  *  Local definitions
44  *	TRUE		Boolean TRUE value
45  *	FALSE		Boolean FALSE value
46  *	TOKDELIMS	Char string of delimiters for lists
47  */
48 
49 #ifndef		TRUE
50 #define		TRUE		('t')
51 #endif
52 
53 #ifndef		FALSE
54 #define		FALSE		0
55 #endif
56 
57 #define		TOKDELIMS	", \t\n"
58 
59 
60 /*
61  *  Exit codes:
62  *	EX_OK		Exit code for all went well
63  *	EX_ERROR	Exit code for something failed
64  *	EX_TABLES	A table couldn't be accessed
65  *	EX_NOALLOC	Exit code for allocation failed
66  */
67 
68 #define		EX_OK		0
69 #define		EX_ERROR	1
70 #define		EX_TABLES	2
71 #define		EX_NOALLOC	3
72 
73 /*
74  *  Messages:
75  *	M_USAGE		Usage error
76  *	M_INVKEY	Invalid key specified
77  *	M_ERROR		Some strange error
78  *	M_UNABLE	A list of devices is unavailable
79  *	M_DEVTAB	Can't access device table (for reading)
80  *	M_RSVTAB	Can't access device reservation table (for r/w)
81  *	M_NODEV		A list of devices is invalid
82  */
83 
84 #define	M_USAGE		"usage: devreserv [key [devicelist [...]]]"
85 #define	M_INVKEY	"Invalid key: %s"
86 #define	M_ERROR		"Internal error, errno=%d"
87 #define	M_UNABLE	"Cannot reserve devices"
88 #define	M_DEVTAB	"Cannot open the device table: %s"
89 #define	M_RSVTAB	"Cannot open the device-reservation table: %s"
90 #define	M_NODEV		M_UNABLE
91 
92 
93 /*
94  *  Local functions and static data
95  *
96  *	buildreqlist()	Builds the list of requested devices for devreserv()
97  *	freereqlist()	Free space allocated to the list of requested devices
98  *	ndevsin()	Get number of elements in a list
99  *	stdmsg(r,l,s,m)	Standard message generation
100  *			r	Recoverability flag
101  *			l	Label
102  *			s	Severity
103  *			m	Message
104  *
105  *	lbl		Buffer for the label-component of a message
106  *	txt		Buffer for the text-component of a message
107  */
108 
109 static char  ***buildreqlist();
110 static void	freereqlist();
111 static int	ndevsin();
112 
113 #define	stdmsg(r,l,s,m)	(void) fmtmsg(MM_PRINT|MM_UTIL|r,l,s,m,MM_NULLACT,MM_NULLTAG)
114 
115 static	char	lbl[MM_MXLABELLN+1];
116 static	char	txt[MM_MXTXTLN+1];
117 
118 /*
119  *  devreserv [key [devlist [devlist [...]]]]
120  *
121  *	This command reserves sets of devices known to the OA&M device
122  *	management system.  It reserves a device from each of the device
123  *	lists presented to it, reserving them on the key (<key>).  If no
124  *	device-lists are provided, the command lists those devices reserved
125  *	on the given key (<key>).  If no key (<key>) is provided, the
126  *	command lists all devices currently reserved.
127  *
128  *  Options:  None
129  *
130  *  Arguments:
131  *	key		Key to lock the devices on
132  *	devlist		A comma-, space-, or tab-list containing devices
133  *			(pathnames or aliases).  For typical shells, space-
134  *			and tab-lists should be quoted or the separator should
135  *			be somehow escaped.
136  *
137  *  Command Values:
138  *	EX_OK		0	Device(s) successfully allocated
139  *	EX_ERROR	1	A syntax or other error occurred
140  *	EX_TABLES	2	Either the device-table or the device-
141  *				reservation table couldn't be opened as needed
142  *	EX_NOALLOC	3	The device-reservation request couldn't be
143  *				fulfilled.
144  */
145 
146 int
main(int argc,char * argv[])147 main(int argc, char *argv[])
148 {
149 
150 	/* Automatics */
151 	char		     ***reqlist;	/* * to list of lists */
152 	char		      **argp;		/* Ptr to current argument */
153 	char		      **alloclist;	/* List of allocated devices */
154 	char		      **pp;		/* Temp ptr to char ptrs */
155 	struct reservdev      **rsvd;		/* Ptr to list of rsvd devs */
156 	struct reservdev      **plk;		/* Running ptr to locks */
157 	char		       *p;		/* Temp char ptr */
158 	char		       *devtab;		/* Device table pathname */
159 	char		       *rsvtab;		/* Dev-rsv tbl pathname */
160 	int			argcount;	/* Number of args on cmd */
161 	long			lkey;		/* Key for locking (long) */
162 	int			key;		/* Key for locking */
163 	int			exitcode;	/* Value to return */
164 	int			sev;		/* Message severity */
165 	int			syntaxerr;	/* Flag, TRUE if syntax error */
166 	int			c;		/* Option character */
167 	int			i;		/* Temp counter */
168 
169 
170 	/*
171 	 * Initializations
172 	 */
173 
174 	/* Build a message label */
175 	if (p = strrchr(argv[0], '/')) p++;
176 	else p = argv[0];
177 	(void) strlcat(strcpy(lbl, "UX:"), p, sizeof(lbl));
178 
179 
180 	/*
181 	 * Allow only the text component of messages to be written
182 	 * (this will probably go away in SVR4.1)
183 	 */
184 
185 	(void) putenv("MSGVERB=text");
186 
187 
188 	/*
189 	 * Parse the options from the command line
190 	 */
191 
192 	opterr = 0;
193 	syntaxerr = FALSE;
194 	while ((c = getopt(argc, argv, "")) != EOF) switch(c) {
195 	default:
196 	    syntaxerr = FALSE;
197 	    break;
198 	}
199 
200 	/* If there's (an obvious) syntax error, write a message and quit */
201 	if (syntaxerr) {
202 	    stdmsg(MM_NRECOV, lbl, MM_ERROR, M_USAGE);
203 	    exit(EX_ERROR);
204 	}
205 
206 	/* Argument initializations */
207 	argcount = argc - optind;
208 	argp = &argv[optind];
209 
210 
211 	/*
212 	 *  devreserv
213 	 *
214 	 *  	If euid == 0, write a list of all currently allocated devices.
215 	 */
216 
217 	if (argcount == 0) {
218 
219 	    /* Get the list of reserved devices */
220 	    if (rsvd = reservdev()) {
221 
222 		/* Write the list of reserved devices with the key
223 		 * that the device was locked on.  The key should go
224 		 * in column 16, but separate it from the alias with at
225 		 * least one space */
226 
227 		exitcode = EX_OK;
228 		for (plk = rsvd ; *plk ; plk++) {
229 		    if ((i = fputs((*plk)->devname, stdout)) >= 0) do
230 			(void) fputc(' ', stdout);
231 		    while (++i < 16);
232 		    (void) fprintf(stdout, "%ld\n", (*plk)->key);
233 		}
234 
235 	    } else {
236 
237 		/* Problems getting the list of reserved devices */
238 		if (((errno == EINVAL) || (errno == EACCES)) && (rsvtab = _rsvtabpath())) {
239 		    (void) snprintf(txt, sizeof(txt), M_RSVTAB, rsvtab);
240 		    exitcode = EX_TABLES;
241 		    sev = MM_ERROR;
242 		} else {
243 		    (void) sprintf(txt, M_ERROR, errno);
244 		    exitcode = EX_ERROR;
245 		    sev = MM_HALT;
246 		}
247 		stdmsg(MM_NRECOV, lbl, sev, txt);
248 	    }
249 
250 	    /* Finished */
251 	    exit(exitcode);
252 	}
253 
254 
255 	/*
256 	 *  devreserv key
257 	 *
258 	 *  	Generate a list of the devices allocated on a specific key.
259 	 */
260 
261 	if (argcount == 1) {
262 
263 	    /* Extract the key from the command */
264 	    lkey = strtol(*argp, &p, 10);
265 	    if (*p || (lkey <= 0) || (lkey > MAXINT)) {
266 
267 		/* <key> argument invalid */
268 		(void) snprintf(txt, sizeof(txt), M_INVKEY, *argp);
269 		stdmsg(MM_NRECOV, lbl, MM_ERROR, txt);
270 		exitcode = EX_ERROR;
271 
272 	    } else {
273 
274 		key = (int) lkey;
275 
276 		/* Get the list of reserved devices ... */
277 		if (rsvd = reservdev()) {
278 
279 		    /* For each reserved device, write the alias to stdout */
280 		    exitcode = EX_OK;
281 		    for (plk = rsvd ; *plk ; plk++) {
282 			if ((*plk)->key == key) (void) puts((*plk)->devname);
283 		    }
284 
285 		} else {
286 
287 		    /* Problems getting the list of reserved devices */
288 		    if (((errno == EINVAL) || (errno == EACCES)) && (rsvtab = _rsvtabpath())) {
289 			(void) snprintf(txt, sizeof(txt), M_RSVTAB, rsvtab);
290 			exitcode = EX_TABLES;
291 			sev = MM_ERROR;
292 		    } else {
293 			(void) sprintf(txt, M_ERROR, errno);
294 			exitcode = EX_ERROR;
295 			sev = MM_HALT;
296 		    }
297 		    stdmsg(MM_NRECOV, lbl, sev, txt);
298 		}
299 	    }
300 
301 	    /* Finished */
302 	    exit(exitcode);
303 	}
304 
305 
306 	/*
307 	 *  devreserv key devlist [...]
308 	 *
309 	 *	Reserve specific devices
310 	 */
311 
312 	/* Open the device file (if there's one to be opened) */
313 	if (!_opendevtab("r")) {
314 	    if (devtab = _devtabpath()) {
315 		(void) snprintf(txt, sizeof(txt), M_DEVTAB, devtab);
316 		exitcode = EX_TABLES;
317 		sev = MM_ERROR;
318 	    } else {
319 		(void) sprintf(txt, M_ERROR, errno);
320 		exitcode = EX_ERROR;
321 		sev = MM_HALT;
322 	    }
323 	    stdmsg(MM_NRECOV, lbl, sev, txt);
324 	    exit(exitcode);
325 	}
326 
327 	/* Extract the key from the command */
328 	lkey = strtol(*argp, &p, 10);
329 	if (*p || (lkey <= 0) || (lkey > MAXINT)) {
330 	    (void) snprintf(txt, sizeof(txt), M_INVKEY, *argp);
331 	    stdmsg(MM_NRECOV, lbl, MM_ERROR, txt);
332 	    exit(EX_ERROR);
333 	}
334 
335 	key = (int) lkey;
336 	argp++;
337 
338 	/* Build the device request list from the command arguments */
339 	if (reqlist = buildreqlist(argp)) {
340 
341 	    /* Attempt to allocate the devices */
342 	    if (alloclist = devreserv(key, reqlist)) {
343 
344 		/*
345 		 * For each allocated device, write the alias to stdout
346 		 * and free the space allocated for the string.
347 		 */
348 
349 		for (pp = alloclist; *pp; pp++) {
350 		    (void) puts(*pp);
351 		    free(*pp);
352 		}
353 
354 		/* Free the list of allocated devices */
355 		free((char *) alloclist);
356 		exitcode = EX_OK;
357 	    }
358 	    else {
359 		/* Device allocation failed */
360 		if (errno == EAGAIN) {
361 		    stdmsg(MM_NRECOV, lbl, MM_ERROR, M_UNABLE);
362 		    exitcode = EX_NOALLOC;
363 		} else if (errno == ENODEV) {
364 		    stdmsg(MM_NRECOV, lbl, MM_ERROR, M_NODEV);
365 		    exitcode = EX_NOALLOC;
366 		} else {
367 		    (void) sprintf(txt, M_ERROR, errno);
368 		    stdmsg(MM_NRECOV, lbl, MM_HALT, txt);
369 		    exitcode = EX_ERROR;
370 		}
371 	    }
372 	    freereqlist(reqlist);
373 	}
374 
375 
376 	/* Exit with the appropriate code */
377 	return(exitcode);
378 }
379 
380 /*
381  * char ***buildreqlist(args)
382  *	char   **args
383  *
384  *	Build the list of lists of devices to request, as described by the
385  *	arguments on the command line.
386  *
387  *  Arguments:
388  *	char **args	The address of the first argument of the list of
389  *			lists of devices to allocate.   (This list is
390  *			terminated with a (char *) NULL.)
391  *
392  *  Returns:  char ***
393  *	A pointer to a list containing addresses of lists of pointers to
394  *	character-strings, as expected by "devreserv()"
395  *
396  *  Notes:
397  *    -	Assuming that strtok() won't return "".  If it does, the
398  *	parsing algorithm needs to be enhanced a bit to eliminate
399  *	these cases.
400  */
401 
402 static char ***
buildreqlist(args)403 buildreqlist(args)
404 	char  **args;
405 {
406 	/* Local automatic data */
407 	char	     ***addrlist;	/* Addr of space for ptrs to lists */
408 	char	     ***ppp;		/* Pointer to pointers to pointers */
409 	char	      **pp;		/* Pointer to pointers */
410 	char	      **qq;		/* Pointer to pointers */
411 	int		noerror;	/* FLAG, TRUE if all's well */
412 	int		i;		/* Counter */
413 	int		n;		/* Another counter */
414 
415 
416 	/* Count the number of lists we have to work with */
417 	i = 1;
418 	for (pp = args ; *pp ; pp++) i++;
419 
420 
421 	/* If we can allocate space for the list of lists ... */
422 	if (addrlist = (char ***) malloc(i*sizeof(char **))) {
423 
424 	    /* Parse each list, putting that list in the list of lists */
425 	    ppp = addrlist;
426 	    noerror = TRUE;
427 	    for (pp = args ; noerror && *pp ; pp++) {
428 		n = ndevsin(*pp, TOKDELIMS);
429 		if (*ppp = (char **) malloc((n+1)*sizeof(char *))) {
430 		     qq = *ppp++;
431 		     if (*qq++ = strtok(*pp, TOKDELIMS))
432 			 while (*qq++ = strtok((char *) NULL, TOKDELIMS));
433 		} else noerror = FALSE;
434 	    }
435 
436 	    /* If there was an error, clean up the malloc()s we've made */
437 	    if (!noerror) {
438 		freereqlist(addrlist);
439 		addrlist = (char ***) NULL;
440 	    }
441 	}
442 
443 	/* Return ptr to the list of addresses of lists (or NULL if none) */
444 	return(addrlist);
445 }
446 
447 /*
448  *  void freereqlist(list)
449  *	char ***list
450  *
451  *	This function frees the space allocated to the list of lists
452  *	referenced by <list>
453  *
454  *  Arguments:
455  *	char ***list	Address of the list of lists
456  *
457  *  Returns:  void
458  */
459 
460 static void
freereqlist(list)461 freereqlist(list)
462 	char ***list;
463 {
464 	char ***ppp;
465 	if (list) {
466 	    for (ppp = list ; *ppp ; ppp++) free((char *) *ppp);
467 	    free((char *) list);
468 	}
469 }
470 
471 /*
472  * int ndevsin(list, delims)
473  *	char   *list
474  *	char   *delims
475  *
476  *	This function determines how many tokens are in the list <list>.
477  *	The tokens are delimited by fields of characters in the string
478  *	<delims>.  It returns the number of tokens in the list.
479  *
480  *  Arguments:
481  *	char *list	The <delims>list of tokens to scan
482  *	char *delims	The list of delimiters that define the list
483  *
484  *  Returns: int
485  *	The number of elements in the list.
486  *
487  *  Notes:
488  *    -	This function does not recognize "null" elements.  For example,
489  *	a,b,,,,c,,d contains 4 elememts (if delims contains a ',')
490  */
491 
492 static int
ndevsin(list,delims)493 ndevsin(list, delims)
494 	char   *list;			/* List to scan */
495 	char   *delims;			/* Delimiters */
496 {
497 	char   *p;			/* Running character pointer */
498 	int	count;			/* Number of tokens seen so far */
499 	int	tokflag;		/* TRUE if we're parsing a token */
500 
501 	count = 0;			/* None seen yet */
502 	tokflag = FALSE;		/* Not in a token */
503 
504 	/* Scan the character-string containing the list of tokens */
505 	for (p = list ; *p ; p++) {
506 
507 	    /* If a delimiter, we're not in a token */
508 	    if (strchr(delims, *p)) tokflag = FALSE;
509 
510 	    /* Otherwise, if we weren't in a token, we've found one */
511 	    else if (!tokflag) {
512 		tokflag = TRUE;
513 		count++;
514 	    }
515 	}
516 
517 	/* Return the number of elements in the list */
518 	return(count);
519 }
520