xref: /illumos-gate/usr/src/cmd/devmgmt/cmds/devreserv.c (revision 367731f049bfcb9d9d460279bdeece464fbd5ef9)
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  *  Local definitions
43  *	TRUE		Boolean TRUE value
44  *	FALSE		Boolean FALSE value
45  *	TOKDELIMS	Char string of delimiters for lists
46  */
47 
48 #ifndef		TRUE
49 #define		TRUE		('t')
50 #endif
51 
52 #ifndef		FALSE
53 #define		FALSE		0
54 #endif
55 
56 #define		TOKDELIMS	", \t\n"
57 
58 
59 /*
60  *  Exit codes:
61  *	EX_OK		Exit code for all went well
62  *	EX_ERROR	Exit code for something failed
63  *	EX_TABLES	A table couldn't be accessed
64  *	EX_NOALLOC	Exit code for allocation failed
65  */
66 
67 #define		EX_OK		0
68 #define		EX_ERROR	1
69 #define		EX_TABLES	2
70 #define		EX_NOALLOC	3
71 
72 /*
73  *  Messages:
74  *	M_USAGE		Usage error
75  *	M_INVKEY	Invalid key specified
76  *	M_ERROR		Some strange error
77  *	M_UNABLE	A list of devices is unavailable
78  *	M_DEVTAB	Can't access device table (for reading)
79  *	M_RSVTAB	Can't access device reservation table (for r/w)
80  *	M_NODEV		A list of devices is invalid
81  */
82 
83 #define	M_USAGE		"usage: devreserv [key [devicelist [...]]]"
84 #define	M_INVKEY	"Invalid key: %s"
85 #define	M_ERROR		"Internal error, errno=%d"
86 #define	M_UNABLE	"Cannot reserve devices"
87 #define	M_DEVTAB	"Cannot open the device table: %s"
88 #define	M_RSVTAB	"Cannot open the device-reservation table: %s"
89 #define	M_NODEV		M_UNABLE
90 
91 
92 /*
93  *  Local functions and static data
94  *
95  *	buildreqlist()	Builds the list of requested devices for devreserv()
96  *	freereqlist()	Free space allocated to the list of requested devices
97  *	ndevsin()	Get number of elements in a list
98  *	stdmsg(r,l,s,m)	Standard message generation
99  *			r	Recoverability flag
100  *			l	Label
101  *			s	Severity
102  *			m	Message
103  *
104  *	lbl		Buffer for the label-component of a message
105  *	txt		Buffer for the text-component of a message
106  */
107 
108 static char  ***buildreqlist();
109 static void	freereqlist();
110 static int	ndevsin();
111 
112 #define	stdmsg(r, l, s, m)	\
113 	(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 	const char		*errstr;
169 
170 
171 	/*
172 	 * Initializations
173 	 */
174 
175 	/* Build a message label */
176 	if (p = strrchr(argv[0], '/')) p++;
177 	else p = argv[0];
178 	(void) strlcat(strcpy(lbl, "UX:"), p, sizeof (lbl));
179 
180 
181 	/*
182 	 * Allow only the text component of messages to be written
183 	 * (this will probably go away in SVR4.1)
184 	 */
185 
186 	(void) putenv("MSGVERB=text");
187 
188 
189 	/*
190 	 * Parse the options from the command line
191 	 */
192 
193 	opterr = 0;
194 	syntaxerr = FALSE;
195 	while ((c = getopt(argc, argv, "")) != EOF)
196 		switch (c) {
197 		default:
198 			syntaxerr = FALSE;
199 			break;
200 		}
201 
202 	/* If there's (an obvious) syntax error, write a message and quit */
203 	if (syntaxerr) {
204 		stdmsg(MM_NRECOV, lbl, MM_ERROR, M_USAGE);
205 		exit(EX_ERROR);
206 	}
207 
208 	/* Argument initializations */
209 	argcount = argc - optind;
210 	argp = &argv[optind];
211 
212 
213 	/*
214 	 *  devreserv
215 	 *
216 	 *	If euid == 0, write a list of all currently allocated devices.
217 	 */
218 
219 	if (argcount == 0) {
220 
221 		/* Get the list of reserved devices */
222 		if (rsvd = reservdev()) {
223 
224 			/*
225 			 * Write the list of reserved devices with the key
226 			 * that the device was locked on.  The key should go
227 			 * in column 16, but separate it from the alias with at
228 			 * least one space
229 			 */
230 
231 			exitcode = EX_OK;
232 			for (plk = rsvd; *plk; plk++) {
233 				if ((i = fputs((*plk)->devname, stdout)) >= 0)
234 					do {
235 						(void) fputc(' ', stdout);
236 					} while (++i < 16);
237 				(void) fprintf(stdout, "%ld\n", (*plk)->key);
238 			}
239 
240 		} else {
241 
242 			/* Problems getting the list of reserved devices */
243 			if (((errno == EINVAL) || (errno == EACCES)) &&
244 			    (rsvtab = _rsvtabpath())) {
245 				(void) snprintf(txt, sizeof (txt), M_RSVTAB,
246 				    rsvtab);
247 				exitcode = EX_TABLES;
248 				sev = MM_ERROR;
249 			} else {
250 				(void) sprintf(txt, M_ERROR, errno);
251 				exitcode = EX_ERROR;
252 				sev = MM_HALT;
253 			}
254 			stdmsg(MM_NRECOV, lbl, sev, txt);
255 		}
256 
257 		/* Finished */
258 		exit(exitcode);
259 	}
260 
261 
262 	/*
263 	 *  devreserv key
264 	 *
265 	 *	Generate a list of the devices allocated on a specific key.
266 	 */
267 
268 	if (argcount == 1) {
269 
270 		/* Extract the key from the command */
271 		lkey = strtonum(*argp, 1, MAXINT, &errstr);
272 		if (errstr != NULL) {
273 
274 			/* <key> argument invalid */
275 			(void) snprintf(txt, sizeof (txt), M_INVKEY, *argp);
276 			stdmsg(MM_NRECOV, lbl, MM_ERROR, txt);
277 			exitcode = EX_ERROR;
278 
279 		} else {
280 
281 			key = (int)lkey;
282 
283 			/* Get the list of reserved devices ... */
284 			if (rsvd = reservdev()) {
285 
286 				/*
287 				 * For each reserved device, write the alias
288 				 * to stdout
289 				 */
290 				exitcode = EX_OK;
291 				for (plk = rsvd; *plk; plk++) {
292 					if ((*plk)->key == key)
293 						(void) puts((*plk)->devname);
294 				}
295 
296 			} else {
297 
298 				/*
299 				 * Problems getting the list of reserved
300 				 * devices
301 				 */
302 				if (((errno == EINVAL) || (errno == EACCES)) &&
303 				    (rsvtab = _rsvtabpath())) {
304 					(void) snprintf(txt, sizeof (txt),
305 					    M_RSVTAB, rsvtab);
306 					exitcode = EX_TABLES;
307 					sev = MM_ERROR;
308 				} else {
309 					(void) sprintf(txt, M_ERROR, errno);
310 					exitcode = EX_ERROR;
311 					sev = MM_HALT;
312 				}
313 				stdmsg(MM_NRECOV, lbl, sev, txt);
314 			}
315 		}
316 
317 		/* Finished */
318 		exit(exitcode);
319 	}
320 
321 
322 	/*
323 	 *  devreserv key devlist [...]
324 	 *
325 	 *	Reserve specific devices
326 	 */
327 
328 	/* Open the device file (if there's one to be opened) */
329 	if (!_opendevtab("r")) {
330 		if (devtab = _devtabpath()) {
331 			(void) snprintf(txt, sizeof (txt), M_DEVTAB, devtab);
332 			exitcode = EX_TABLES;
333 			sev = MM_ERROR;
334 		} else {
335 			(void) sprintf(txt, M_ERROR, errno);
336 			exitcode = EX_ERROR;
337 			sev = MM_HALT;
338 		}
339 		stdmsg(MM_NRECOV, lbl, sev, txt);
340 		exit(exitcode);
341 	}
342 
343 	/* Extract the key from the command */
344 	lkey = strtonum(*argp, 1, MAXINT, &errstr);
345 	if (errstr != NULL) {
346 		(void) snprintf(txt, sizeof (txt), M_INVKEY, *argp);
347 		stdmsg(MM_NRECOV, lbl, MM_ERROR, txt);
348 		exit(EX_ERROR);
349 	}
350 
351 	key = (int)lkey;
352 	argp++;
353 
354 	/* Build the device request list from the command arguments */
355 	if (reqlist = buildreqlist(argp)) {
356 
357 		/* Attempt to allocate the devices */
358 		if (alloclist = devreserv(key, reqlist)) {
359 
360 			/*
361 			 * For each allocated device, write the alias to stdout
362 			 * and free the space allocated for the string.
363 			 */
364 
365 			for (pp = alloclist; *pp; pp++) {
366 				(void) puts(*pp);
367 				free(*pp);
368 			}
369 
370 			/* Free the list of allocated devices */
371 			free(alloclist);
372 			exitcode = EX_OK;
373 		} else {
374 			/* Device allocation failed */
375 			if (errno == EAGAIN) {
376 				stdmsg(MM_NRECOV, lbl, MM_ERROR, M_UNABLE);
377 				exitcode = EX_NOALLOC;
378 			} else if (errno == ENODEV) {
379 				stdmsg(MM_NRECOV, lbl, MM_ERROR, M_NODEV);
380 				exitcode = EX_NOALLOC;
381 			} else {
382 				(void) sprintf(txt, M_ERROR, errno);
383 				stdmsg(MM_NRECOV, lbl, MM_HALT, txt);
384 				exitcode = EX_ERROR;
385 			}
386 		}
387 		freereqlist(reqlist);
388 	}
389 
390 
391 	/* Exit with the appropriate code */
392 	return (exitcode);
393 }
394 
395 /*
396  * char ***buildreqlist(args)
397  *	char   **args
398  *
399  *	Build the list of lists of devices to request, as described by the
400  *	arguments on the command line.
401  *
402  *  Arguments:
403  *	char **args	The address of the first argument of the list of
404  *			lists of devices to allocate.   (This list is
405  *			terminated with a (char *) NULL.)
406  *
407  *  Returns:  char ***
408  *	A pointer to a list containing addresses of lists of pointers to
409  *	character-strings, as expected by "devreserv()"
410  *
411  *  Notes:
412  *    -	Assuming that strtok() won't return "".  If it does, the
413  *	parsing algorithm needs to be enhanced a bit to eliminate
414  *	these cases.
415  */
416 
417 static char ***
buildreqlist(char ** args)418 buildreqlist(char **args)
419 {
420 	/* Local automatic data */
421 	char		***addrlist;	/* Addr of space for ptrs to lists */
422 	char		***ppp;		/* Pointer to pointers to pointers */
423 	char		**pp;		/* Pointer to pointers */
424 	char		**qq;		/* Pointer to pointers */
425 	int		noerror;	/* FLAG, TRUE if all's well */
426 	int		i;		/* Counter */
427 	int		n;		/* Another counter */
428 
429 
430 	/* Count the number of lists we have to work with */
431 	i = 1;
432 	for (pp = args; *pp; pp++)
433 		i++;
434 
435 
436 	/* If we can allocate space for the list of lists ... */
437 	if (addrlist = malloc(i * sizeof (char **))) {
438 
439 		/* Parse each list, putting that list in the list of lists */
440 		ppp = addrlist;
441 		noerror = TRUE;
442 		for (pp = args; noerror && *pp; pp++) {
443 			n = ndevsin(*pp, TOKDELIMS);
444 			if (*ppp = malloc((n + 1) * sizeof (char *))) {
445 				qq = *ppp++;
446 				if (*qq++ = strtok(*pp, TOKDELIMS))
447 					while (*qq++ = strtok(NULL, TOKDELIMS))
448 						;
449 			} else {
450 				noerror = FALSE;
451 			}
452 		}
453 
454 		/* If there was an error, clean up the malloc()s we've made */
455 		if (!noerror) {
456 			freereqlist(addrlist);
457 			addrlist = NULL;
458 		}
459 	}
460 
461 	/* Return ptr to the list of addresses of lists (or NULL if none) */
462 	return (addrlist);
463 }
464 
465 /*
466  *  void freereqlist(list)
467  *	char ***list
468  *
469  *	This function frees the space allocated to the list of lists
470  *	referenced by <list>
471  *
472  *  Arguments:
473  *	char ***list	Address of the list of lists
474  *
475  *  Returns:  void
476  */
477 
478 static void
freereqlist(char *** list)479 freereqlist(char ***list)
480 {
481 	char ***ppp;
482 	if (list) {
483 		for (ppp = list; *ppp; ppp++)
484 			free(*ppp);
485 		free(list);
486 	}
487 }
488 
489 /*
490  * int ndevsin(list, delims)
491  *	char   *list
492  *	char   *delims
493  *
494  *	This function determines how many tokens are in the list <list>.
495  *	The tokens are delimited by fields of characters in the string
496  *	<delims>.  It returns the number of tokens in the list.
497  *
498  *  Arguments:
499  *	char *list	The <delims>list of tokens to scan
500  *	char *delims	The list of delimiters that define the list
501  *
502  *  Returns: int
503  *	The number of elements in the list.
504  *
505  *  Notes:
506  *    -	This function does not recognize "null" elements.  For example,
507  *	a,b,,,,c,,d contains 4 elememts (if delims contains a ',')
508  */
509 
510 static int
ndevsin(char * list,char * delims)511 ndevsin(char *list, char *delims)
512 {
513 	char   *p;			/* Running character pointer */
514 	int	count;			/* Number of tokens seen so far */
515 	int	tokflag;		/* TRUE if we're parsing a token */
516 
517 	count = 0;			/* None seen yet */
518 	tokflag = FALSE;		/* Not in a token */
519 
520 	/* Scan the character-string containing the list of tokens */
521 	for (p = list; *p; p++) {
522 
523 		/* If a delimiter, we're not in a token */
524 		if (strchr(delims, *p)) {
525 			tokflag = FALSE;
526 
527 		} else if (!tokflag) {
528 			/*
529 			 * Otherwise, if we weren't in a token,
530 			 * we've found one
531 			 */
532 			tokflag = TRUE;
533 			count++;
534 		}
535 	}
536 
537 	/* Return the number of elements in the list */
538 	return (count);
539 }
540