xref: /illumos-gate/usr/src/cmd/devmgmt/cmds/putdev.c (revision 7c478bd9)
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 2002-2003 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 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 /*
34  *	Implements the "putdev" command.
35  */
36 #include	<sys/types.h>
37 #include	<stdio.h>
38 #include	<stdlib.h>
39 #include	<string.h>
40 #include	<errno.h>
41 #include	<unistd.h>
42 #include	<fmtmsg.h>
43 #include	<devmgmt.h>
44 #include	<devtab.h>
45 
46 
47 /*
48  * General Purpose Constants
49  *	TRUE		Boolean TRUE (if not already defined)
50  *	FALSE		Boolean FALSE (if not already defined)
51  *	NULL		Null address (if not already defined)
52  */
53 
54 #ifndef	TRUE
55 #define	TRUE	(1)
56 #endif
57 
58 #ifndef	FALSE
59 #define	FALSE	(0)
60 #endif
61 
62 /*
63  * Exit codes
64  *	EX_OK		All went well
65  *	EX_ERROR	Usage or internal error
66  *	EX_DEVTAB	Had trouble accessing/reading/writing the device table
67  *	EX_EXISTS	The specified alias already exists
68  *	EX_ATTRIB	One or more attributes requested for removal was not
69  *			defined for the device
70  *	EX_RELPATH	Pathname supplied for cdevice, bdevice or pathname
71  *			attributes was not a full pathname
72  */
73 
74 #define	EX_OK		0
75 #define	EX_ERROR	1
76 #define	EX_DEVTAB	2
77 #define	EX_EXISTS	3
78 #define	EX_ATTRIB	4
79 #define	EX_RELPATH	4
80 
81 
82 /*
83  * Error messages
84  */
85 
86 #define	E_USAGE		"usage: putdev -a alias [attribute=value [...]]\n       putdev -m device attribute=value [attribute=value [...]]\n       putdev -d device [attribute [...]]"
87 #define	E_ALIASIS	"Alias already exists in table: %s"
88 #define	E_NODEV		"Device does not exist in table: %s"
89 #define	E_NOALIAS	"Cannot use \"alias\" as an attribute"
90 #define	E_NOATTR	"Attribute not found: %s"
91 #define	E_NODEVTAB	"Cannot open the device table: %s"
92 #define	E_NOMKDTAB	"Cannot create a new device table: %s"
93 #define	E_INVALIAS	"Not a valid device alias: %s"
94 #define E_MULTIPLE	"Multiple definitions of an attribute are not allowed."
95 #define	E_INTERNAL	"Internal error, errno=%d"
96 #define	E_RELPATH	"Full pathname required for cdevice,bdevice and pathname attributes."
97 
98 
99 /*
100  * Macros
101  *	stdmsg(r,l,s,t)	    Using fmtmsg(), write a standard message to the
102  *			    standard error stream.
103  *			    Where:
104  *				r   The recoverability of the error
105  *				l   The label-component
106  *				s   The severity-component
107  *				t   The text-component
108  */
109 
110 #define stdmsg(r,l,s,t) (void) fmtmsg(MM_PRINT|MM_UTIL|r,l,s,t,MM_NULLACT,MM_NULLTAG)
111 
112 
113 /*
114  * Static data
115  *	msg		Space for message's text-component
116  */
117 
118 static	char		msg[256];	/* Space for text of message */
119 
120 /*
121  * char *mklbl(cmd)
122  *	char   *cmd
123  *
124  *	This function builds a standard label from the command used to invoke
125  *	this process and the standard label prefix ("UX:")
126  *
127  * Arguments:
128  *	char *cmd	The command used to invoke this process.
129  *
130  * Returns:  char *
131  *	Pointer to malloc()ed space containing the standard label,
132  *	or (char *) NULL if an error occurred.
133  */
134 
135 static char *
136 mklbl(cmd)
137 	char   *cmd;
138 {
139 	/* Automatic data */
140 	char   *rtn;		/* Value to return */
141 	char   *p;		/* Temporary */
142 
143 	/* Find the 1st char of the basename of the command */
144 	if (p = strrchr(cmd, '/')) p++;
145 	else p = cmd;
146 
147 	/* Allocate and build the string value to return */
148 	if (rtn = (char *) malloc(strlen("UX:")+strlen(p)+1)) {
149 	    (void) strcpy(rtn, "UX:");
150 	    (void) strcat(rtn, p);
151 	}
152 
153 
154 	/* Now that we've done all of that work, change the environment
155 	 * so that only the text-component is written by fmtmsg().
156 	 * (This should go away in SVR4.1)
157 	 */
158 
159 	(void) putenv("MSGVERB=text");
160 
161 
162 	/* Done */
163 	return(rtn);
164 }
165 
166 /*
167  * putdev -a alias [attribute=value [...]]
168  * putdev -m alias attribute=value [attribute=value [...]]
169  * putdev -d alias [attribute [...]]
170  *
171  * 	Modify the device-table.  If -a specified, add a record for <alias>
172  * 	to the table.  If -m specified, modify the attributes specified for
173  *	the <device> specified.  If -d specified, remove the specified
174  *	attributes from the specified device or remove the specified device.
175  *
176  * Options:
177  *	-a		Add an alias description to the device table
178  *	-m		Modify an existing device description
179  *	-d		(if no attributes specified) remove the specified
180  *			device from the device table, or (if attributes
181  *			specified) remove the specified attributes from
182  *			the specified device.
183  *
184  * Exit values:
185  *	0		All went well
186  *	1		Usage error (includes specifying "alias" as an
187  *			<attribute>)
188  *	2		The device table file could not be opened, read
189  *			or modified
190  *	3		If -a, the alias already exists.  Otherwise, the
191  *			specified device does not exist in the table
192  *	4		One of the specified attributes did not exist
193  *			for the device and therefore wasn't removed
194  */
195 
196 main(argc, argv)
197 	int	argc;			/* Argument count */
198 	char   *argv[];			/* Argument list */
199 {
200 	/* Automatic data */
201 	char	      **plist;		/* Ptr to list of undef'nd attrs */
202 	char	       *lbl;		/* Ptr to label for messages */
203 	char	       *alias;		/* Ptr to <alias> on command-line */
204 	char	       *device;		/* Ptr to <device> on command-line */
205 	char	       *p;		/* Temp ptr to char */
206 	int		noerr;		/* FLAG, TRUE if all's well */
207 	int		a_seen;		/* TRUE if -a seen on command-line */
208 	int		m_seen;		/* TRUE if -m seen on command-line */
209 	int		d_seen;		/* TRUE if -a seen on command-line */
210 	int		optchar;	/* Option extracted */
211 	int		exitcd;		/* Value to return at exit */
212 	int		nattrs;		/* Number of attributes on command */
213 
214 
215 	/* Generate the label for messages */
216 	lbl = mklbl(argv[0]);
217 
218 	/* Extract arguments - validate usage */
219 	noerr = TRUE;
220 	a_seen = FALSE;
221 	m_seen = FALSE;
222 	d_seen = FALSE;
223 	opterr = FALSE;
224 	while ((optchar = getopt(argc, argv, "a:d:m:")) != EOF) switch (optchar) {
225 
226 	case 'a':
227 	    if (!(a_seen || m_seen || d_seen)) {
228 		a_seen = TRUE;
229 		alias = optarg;
230 	    }
231 	    else noerr = FALSE;
232 	    break;
233 
234 	case 'd':
235 	    if (!(a_seen || m_seen || d_seen)) {
236 		d_seen = TRUE;
237 		device = optarg;
238 	    }
239 	    else noerr = FALSE;
240 	    break;
241 
242 	case 'm':
243 	    if (!(a_seen || m_seen || d_seen)) {
244 		m_seen = TRUE;
245 		device = optarg;
246 	    }
247 	    else noerr = FALSE;
248 	    break;
249 
250 	case '?':
251 	default:
252 	    noerr = FALSE;
253 	}
254 
255 
256 	/* Write a usage message if we've seen a blatant error */
257 	if (!(a_seen || m_seen || d_seen) || !noerr) {
258 	    stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE);
259 	    exit(EX_ERROR);
260 	}
261 
262 
263 	/* Set up */
264 	exitcd = EX_OK;
265 	nattrs = argc - optind;
266 
267 
268 	/*  putdev -a alias [attr=value [...]] */
269 
270 	if (a_seen) {
271 
272 	    /* Syntax check */
273 	    if (nattrs < 0) {
274 		stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE);
275 		exitcd = EX_ERROR;
276 	    } else {
277 
278 		/* Attempt to add the new alias */
279 		if (!(_adddevtabrec(alias, &argv[optind]))) {
280 
281 		    /* Attempt failed.  Write appropriate error message. */
282 
283 		    switch(errno) {
284 
285 		    /*
286 		     * EINVAL indicates that <alias> is not valid or "alias"
287 		     * was mentioned as <attr> in <attr>=<value> pair.  If the
288 		     * alias is a valid alias, assume that's the problem.
289 		     */
290 
291 		    case EINVAL:
292 			if (_validalias(alias))
293 			    p = E_NOALIAS;
294 			else (void) snprintf(p=msg, sizeof(msg), E_INVALIAS, alias);
295 			stdmsg(MM_NRECOV, lbl, MM_ERROR, p);
296 			exitcd = EX_ERROR;
297 			break;
298 
299 		    /*
300 		     * EEXIST indicates that the alias <alias> already exists
301 		     * in the device table.
302 		     */
303 
304 		    case EEXIST:
305 			(void) snprintf(msg, sizeof(msg), E_ALIASIS, alias);
306 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
307 			exitcd = EX_EXISTS;
308 			break;
309 
310 		    /*
311 		     * EACCES and ENOENT indicate problems reading or writing
312 		     * the device table.
313 		     */
314 
315 		    case EACCES:
316 		    case ENOENT:
317 	                p = _devtabpath();
318 			if (access(p, R_OK) == 0)
319 			    (void) snprintf(msg, sizeof(msg), E_NOMKDTAB, p);
320 			else
321 			    (void) snprintf(msg, sizeof(msg), E_NODEVTAB, p);
322 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
323 			exitcd = EX_DEVTAB;
324 			break;
325 
326 		    /*
327 		     * EAGAIN indicates that an attribute was defined on the
328 		     * command line more than once.
329 		     */
330 
331 		    case EAGAIN:
332 			stdmsg(MM_NRECOV, lbl, MM_ERROR, E_MULTIPLE);
333 			exitcd = EX_ERROR;
334 			break;
335 
336 		    /*
337 		     * ENXIO indicates that a relative pathname was supplied
338 		     * for the cdevice, bdevice or pathname attributes.  Full
339 		     * pathnames are required for these attributes.
340 		     */
341 		    case ENXIO:
342 			stdmsg(MM_NRECOV, lbl, MM_ERROR, E_RELPATH);
343 			exitcd = EX_RELPATH;
344 			break;
345 
346 		    /*
347 		     * Some other problem (odd?)
348 		     */
349 
350 		    default:
351 			(void) sprintf(msg, E_INTERNAL, errno);
352 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
353 			exitcd = EX_ERROR;
354 		    }
355 		}
356 	    }
357 	}   /* End -a case */
358 
359 
360 	/* putdev -m device attr=value [...] */
361 
362 	else if (m_seen) {
363 
364 	    /* Check usage */
365 
366 	    if (nattrs <= 0) {
367 		stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE);
368 		exitcd = EX_ERROR;
369 	    } else {
370 
371 		/* Attempt to modify a device's record */
372 		if (!(_moddevtabrec(device, &argv[optind]))) {
373 
374 		    /* Modification attempt failed */
375 
376 		    switch(errno) {
377 
378 		    /*
379 		     * EINVAL indicates that "alias" was used as an attribute
380 		     * in an <attr>=<value> pair.
381 		     */
382 
383 		    case EINVAL:
384 			stdmsg(MM_NRECOV, lbl, MM_ERROR, E_NOALIAS);
385 			exitcd = EX_ERROR;
386 			break;
387 
388 		    /*
389 		     * ENODEV indicates that the device that was to
390 		     * be modified doesn't exist.
391 		     */
392 
393 		    case ENODEV:
394 			(void) snprintf(msg, sizeof(msg), E_NODEV, device);
395 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
396 			exitcd = EX_EXISTS;
397 			break;
398 
399 		    /*
400 		     * ENOENT indicates that the device-table doesn't exist.
401 		     */
402 
403 		    case ENOENT:
404 			(void) snprintf(msg, sizeof(msg), E_NODEVTAB, _devtabpath());
405 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
406 			exitcd = EX_DEVTAB;
407 			break;
408 
409 		    /*
410 		     * EACCES indicates that there was a problem reading the
411 		     * old device table or creating the new table.  If the
412 		     * old table is readable, assume that we can't create the
413 		     * new table.  Otherwise, assume that the old table isn't
414 		     * accessible.
415 		     */
416 
417 		    case EACCES:
418 	                p = _devtabpath();
419 			if (access(p, R_OK) == 0)
420 			    (void) snprintf(msg, sizeof(msg), E_NOMKDTAB, p);
421 			else
422 			    (void) snprintf(msg, sizeof(msg), E_NODEVTAB, p);
423 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
424 			exitcd = EX_DEVTAB;
425 			break;
426 
427 		    /*
428 		     * EAGAIN indicates that an attribute was specified more than
429 		     * once on the command line.
430 		     */
431 
432 		    case EAGAIN:
433 			stdmsg(MM_NRECOV, lbl, MM_ERROR, E_MULTIPLE);
434 			exitcd = EX_ERROR;
435 			break;
436 
437 		    /*
438 		     * ENXIO indicates that a relative pathname was supplied
439 		     * for the cdevice, bdevice or pathname attributes.  Full
440 		     * pathnames are required for these attributes.
441 		     */
442 		    case ENXIO:
443 			stdmsg(MM_NRECOV, lbl, MM_ERROR, E_RELPATH);
444 			exitcd = EX_RELPATH;
445 			break;
446 
447 		    /*
448 		     * Some strange problem...
449 		     */
450 
451 		    default:
452 			(void) sprintf(msg, E_INTERNAL, errno);
453 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
454 			exitcd = EX_ERROR;
455 		    }
456 		}
457 	    }
458 	}   /* End -m case */
459 
460 	else if (d_seen) {
461 
462 	    /* putdev -d device [attr [...]] */
463 
464 	    /* Check usage */
465 	    if (nattrs < 0) {
466 		stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE);
467 		exitcd = EX_ERROR;
468 	    } else {
469 
470 		/*
471 		 * Determine case (removing a device or attributes
472 		 * to a device.
473 		 */
474 
475 		if (nattrs == 0) {
476 
477 		    /* putdev -d device */
478 
479 		    /* Attempt to remove the specified device */
480 		    if (!(_rmdevtabrec(device))) switch(errno) {
481 
482 			/*
483 			 * ENODEV indicates that the named device is not
484 			 * defined in the device table.
485 			 */
486 
487 		    case ENODEV:
488 			(void) snprintf(msg, sizeof(msg), E_NODEV, device);
489 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
490 			exitcd = EX_EXISTS;
491 			break;
492 
493 			/*
494 			 * ENOENT indicates that the device table can't
495 			 * be found.
496 			 */
497 
498 		    case ENOENT:
499 			(void) snprintf(msg, sizeof(msg), E_NODEVTAB, _devtabpath());
500 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
501 			exitcd = EX_DEVTAB;
502 			break;
503 
504 			/*
505 			 * EACCES indicates that there was a problem reading the
506 			 * old device table or creating the new table.  If the
507 			 * old table is readable, assume that we can't create the
508 			 * new table.  Otherwise, assume that the old table isn't
509 			 * accessible.
510 			 */
511 
512 		    case EACCES:
513 			p = _devtabpath();
514 			if (access(p, R_OK) == 0)
515 			    (void) snprintf(msg, sizeof(msg), E_NOMKDTAB, p);
516 			else
517 			    (void) snprintf(msg, sizeof(msg), E_NODEVTAB, p);
518 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
519 			exitcd = EX_DEVTAB;
520 			break;
521 
522 			/*
523 			 * Some strange problem...
524 			 */
525 
526 		    default:
527 			(void) sprintf(msg, E_INTERNAL, errno);
528 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
529 			exitcd = EX_ERROR;
530 
531 		    }   /* End switch */
532 		}
533 		else {
534 
535 		    /* putdev -d device attr [attr [...]] */
536 
537 		    /*
538 		     * Attempt to remove the specified attributes from the
539 		     * specified device.
540 		     */
541 		    if (!(_rmdevtabattrs(device, &argv[optind], &plist))) switch(errno) {
542 
543 			/*
544 			 * EINVAL indicates that a named attribute was not
545 			 * defined for the specified device or "alias" was
546 			 * requested.  If "plist" points to a list of attrs,
547 			 * the former is the problem.  Otherwise, the latter
548 			 * is the problem.
549 			 */
550 
551 		    case EINVAL:
552 			if (plist) {
553 			    exitcd = EX_ATTRIB;
554 			    for (; *plist; plist++) {
555 				(void) snprintf(msg, sizeof(msg), E_NOATTR, *plist);
556 				stdmsg(MM_RECOVER, lbl, MM_WARNING, msg);
557 			    }
558 			} else {
559 			    stdmsg(MM_NRECOV, lbl, MM_ERROR, E_NOALIAS);
560 			    exitcd = EX_ERROR;
561 			}
562 			break;
563 
564 			/*
565 			 * ENODEV indicates that the named device is not
566 			 * defined in the device table.
567 			 */
568 
569 		    case ENODEV:
570 			(void) snprintf(msg, sizeof(msg), E_NODEV, device);
571 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
572 			exitcd = EX_EXISTS;
573 			break;
574 
575 			/*
576 			 * ENOENT indicates that the device table can't
577 			 * be found.
578 			 */
579 
580 		    case ENOENT:
581 			(void) snprintf(msg, sizeof(msg), E_NODEVTAB, _devtabpath());
582 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
583 			exitcd = EX_DEVTAB;
584 			break;
585 
586 			/*
587 			 * EACCES indicates that there was a problem reading the
588 			 * old device table or creating the new table.  If the
589 			 * old table is readable, assume that we can't create the
590 			 * new table.  Otherwise, assume that the old table isn't
591 			 * accessible.
592 			 */
593 
594 		    case EACCES:
595 			p = _devtabpath();
596 			if (access(p, R_OK) == 0)
597 			    (void) snprintf(msg, sizeof(msg), E_NOMKDTAB, p);
598 			else
599 			    (void) snprintf(msg, sizeof(msg), E_NODEVTAB, p);
600 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
601 			exitcd = EX_DEVTAB;
602 			break;
603 
604 			/*
605 			 * Some strange problem...
606 			 */
607 
608 		    default:
609 			(void) sprintf(msg, E_INTERNAL, errno);
610 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
611 			exitcd = EX_ERROR;
612 
613 		    }  /* End switch */
614 
615 		}   /* End "putdev -d device attr [...]" case */
616 
617 	    }   /* End passes usage-check case */
618 
619 	}   /* End -d case */
620 
621 
622 	/* Done.  Return exit code (determined above) */
623 	return(exitcd);
624 }  /* main() */
625