xref: /illumos-gate/usr/src/cmd/devmgmt/cmds/putdev.c (revision 113f4232)
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 
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 int
197 main(int argc, char *argv[])
198 {
199 	/* Automatic data */
200 	char	      **plist;		/* Ptr to list of undef'nd attrs */
201 	char	       *lbl;		/* Ptr to label for messages */
202 	char	       *alias;		/* Ptr to <alias> on command-line */
203 	char	       *device;		/* Ptr to <device> on command-line */
204 	char	       *p;		/* Temp ptr to char */
205 	int		noerr;		/* FLAG, TRUE if all's well */
206 	int		a_seen;		/* TRUE if -a seen on command-line */
207 	int		m_seen;		/* TRUE if -m seen on command-line */
208 	int		d_seen;		/* TRUE if -a seen on command-line */
209 	int		optchar;	/* Option extracted */
210 	int		exitcd;		/* Value to return at exit */
211 	int		nattrs;		/* Number of attributes on command */
212 
213 
214 	/* Generate the label for messages */
215 	lbl = mklbl(argv[0]);
216 
217 	/* Extract arguments - validate usage */
218 	noerr = TRUE;
219 	a_seen = FALSE;
220 	m_seen = FALSE;
221 	d_seen = FALSE;
222 	opterr = FALSE;
223 	while ((optchar = getopt(argc, argv, "a:d:m:")) != EOF) switch (optchar) {
224 
225 	case 'a':
226 	    if (!(a_seen || m_seen || d_seen)) {
227 		a_seen = TRUE;
228 		alias = optarg;
229 	    }
230 	    else noerr = FALSE;
231 	    break;
232 
233 	case 'd':
234 	    if (!(a_seen || m_seen || d_seen)) {
235 		d_seen = TRUE;
236 		device = optarg;
237 	    }
238 	    else noerr = FALSE;
239 	    break;
240 
241 	case 'm':
242 	    if (!(a_seen || m_seen || d_seen)) {
243 		m_seen = TRUE;
244 		device = optarg;
245 	    }
246 	    else noerr = FALSE;
247 	    break;
248 
249 	case '?':
250 	default:
251 	    noerr = FALSE;
252 	}
253 
254 
255 	/* Write a usage message if we've seen a blatant error */
256 	if (!(a_seen || m_seen || d_seen) || !noerr) {
257 	    stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE);
258 	    exit(EX_ERROR);
259 	}
260 
261 
262 	/* Set up */
263 	exitcd = EX_OK;
264 	nattrs = argc - optind;
265 
266 
267 	/*  putdev -a alias [attr=value [...]] */
268 
269 	if (a_seen) {
270 
271 	    /* Syntax check */
272 	    if (nattrs < 0) {
273 		stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE);
274 		exitcd = EX_ERROR;
275 	    } else {
276 
277 		/* Attempt to add the new alias */
278 		if (!(_adddevtabrec(alias, &argv[optind]))) {
279 
280 		    /* Attempt failed.  Write appropriate error message. */
281 
282 		    switch(errno) {
283 
284 		    /*
285 		     * EINVAL indicates that <alias> is not valid or "alias"
286 		     * was mentioned as <attr> in <attr>=<value> pair.  If the
287 		     * alias is a valid alias, assume that's the problem.
288 		     */
289 
290 		    case EINVAL:
291 			if (_validalias(alias))
292 			    p = E_NOALIAS;
293 			else (void) snprintf(p=msg, sizeof(msg), E_INVALIAS, alias);
294 			stdmsg(MM_NRECOV, lbl, MM_ERROR, p);
295 			exitcd = EX_ERROR;
296 			break;
297 
298 		    /*
299 		     * EEXIST indicates that the alias <alias> already exists
300 		     * in the device table.
301 		     */
302 
303 		    case EEXIST:
304 			(void) snprintf(msg, sizeof(msg), E_ALIASIS, alias);
305 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
306 			exitcd = EX_EXISTS;
307 			break;
308 
309 		    /*
310 		     * EACCES and ENOENT indicate problems reading or writing
311 		     * the device table.
312 		     */
313 
314 		    case EACCES:
315 		    case ENOENT:
316 	                p = _devtabpath();
317 			if (access(p, R_OK) == 0)
318 			    (void) snprintf(msg, sizeof(msg), E_NOMKDTAB, p);
319 			else
320 			    (void) snprintf(msg, sizeof(msg), E_NODEVTAB, p);
321 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
322 			exitcd = EX_DEVTAB;
323 			break;
324 
325 		    /*
326 		     * EAGAIN indicates that an attribute was defined on the
327 		     * command line more than once.
328 		     */
329 
330 		    case EAGAIN:
331 			stdmsg(MM_NRECOV, lbl, MM_ERROR, E_MULTIPLE);
332 			exitcd = EX_ERROR;
333 			break;
334 
335 		    /*
336 		     * ENXIO indicates that a relative pathname was supplied
337 		     * for the cdevice, bdevice or pathname attributes.  Full
338 		     * pathnames are required for these attributes.
339 		     */
340 		    case ENXIO:
341 			stdmsg(MM_NRECOV, lbl, MM_ERROR, E_RELPATH);
342 			exitcd = EX_RELPATH;
343 			break;
344 
345 		    /*
346 		     * Some other problem (odd?)
347 		     */
348 
349 		    default:
350 			(void) sprintf(msg, E_INTERNAL, errno);
351 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
352 			exitcd = EX_ERROR;
353 		    }
354 		}
355 	    }
356 	}   /* End -a case */
357 
358 
359 	/* putdev -m device attr=value [...] */
360 
361 	else if (m_seen) {
362 
363 	    /* Check usage */
364 
365 	    if (nattrs <= 0) {
366 		stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE);
367 		exitcd = EX_ERROR;
368 	    } else {
369 
370 		/* Attempt to modify a device's record */
371 		if (!(_moddevtabrec(device, &argv[optind]))) {
372 
373 		    /* Modification attempt failed */
374 
375 		    switch(errno) {
376 
377 		    /*
378 		     * EINVAL indicates that "alias" was used as an attribute
379 		     * in an <attr>=<value> pair.
380 		     */
381 
382 		    case EINVAL:
383 			stdmsg(MM_NRECOV, lbl, MM_ERROR, E_NOALIAS);
384 			exitcd = EX_ERROR;
385 			break;
386 
387 		    /*
388 		     * ENODEV indicates that the device that was to
389 		     * be modified doesn't exist.
390 		     */
391 
392 		    case ENODEV:
393 			(void) snprintf(msg, sizeof(msg), E_NODEV, device);
394 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
395 			exitcd = EX_EXISTS;
396 			break;
397 
398 		    /*
399 		     * ENOENT indicates that the device-table doesn't exist.
400 		     */
401 
402 		    case ENOENT:
403 			(void) snprintf(msg, sizeof(msg), E_NODEVTAB, _devtabpath());
404 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
405 			exitcd = EX_DEVTAB;
406 			break;
407 
408 		    /*
409 		     * EACCES indicates that there was a problem reading the
410 		     * old device table or creating the new table.  If the
411 		     * old table is readable, assume that we can't create the
412 		     * new table.  Otherwise, assume that the old table isn't
413 		     * accessible.
414 		     */
415 
416 		    case EACCES:
417 	                p = _devtabpath();
418 			if (access(p, R_OK) == 0)
419 			    (void) snprintf(msg, sizeof(msg), E_NOMKDTAB, p);
420 			else
421 			    (void) snprintf(msg, sizeof(msg), E_NODEVTAB, p);
422 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
423 			exitcd = EX_DEVTAB;
424 			break;
425 
426 		    /*
427 		     * EAGAIN indicates that an attribute was specified more than
428 		     * once on the command line.
429 		     */
430 
431 		    case EAGAIN:
432 			stdmsg(MM_NRECOV, lbl, MM_ERROR, E_MULTIPLE);
433 			exitcd = EX_ERROR;
434 			break;
435 
436 		    /*
437 		     * ENXIO indicates that a relative pathname was supplied
438 		     * for the cdevice, bdevice or pathname attributes.  Full
439 		     * pathnames are required for these attributes.
440 		     */
441 		    case ENXIO:
442 			stdmsg(MM_NRECOV, lbl, MM_ERROR, E_RELPATH);
443 			exitcd = EX_RELPATH;
444 			break;
445 
446 		    /*
447 		     * Some strange problem...
448 		     */
449 
450 		    default:
451 			(void) sprintf(msg, E_INTERNAL, errno);
452 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
453 			exitcd = EX_ERROR;
454 		    }
455 		}
456 	    }
457 	}   /* End -m case */
458 
459 	else if (d_seen) {
460 
461 	    /* putdev -d device [attr [...]] */
462 
463 	    /* Check usage */
464 	    if (nattrs < 0) {
465 		stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE);
466 		exitcd = EX_ERROR;
467 	    } else {
468 
469 		/*
470 		 * Determine case (removing a device or attributes
471 		 * to a device.
472 		 */
473 
474 		if (nattrs == 0) {
475 
476 		    /* putdev -d device */
477 
478 		    /* Attempt to remove the specified device */
479 		    if (!(_rmdevtabrec(device))) switch(errno) {
480 
481 			/*
482 			 * ENODEV indicates that the named device is not
483 			 * defined in the device table.
484 			 */
485 
486 		    case ENODEV:
487 			(void) snprintf(msg, sizeof(msg), E_NODEV, device);
488 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
489 			exitcd = EX_EXISTS;
490 			break;
491 
492 			/*
493 			 * ENOENT indicates that the device table can't
494 			 * be found.
495 			 */
496 
497 		    case ENOENT:
498 			(void) snprintf(msg, sizeof(msg), E_NODEVTAB, _devtabpath());
499 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
500 			exitcd = EX_DEVTAB;
501 			break;
502 
503 			/*
504 			 * EACCES indicates that there was a problem reading the
505 			 * old device table or creating the new table.  If the
506 			 * old table is readable, assume that we can't create the
507 			 * new table.  Otherwise, assume that the old table isn't
508 			 * accessible.
509 			 */
510 
511 		    case EACCES:
512 			p = _devtabpath();
513 			if (access(p, R_OK) == 0)
514 			    (void) snprintf(msg, sizeof(msg), E_NOMKDTAB, p);
515 			else
516 			    (void) snprintf(msg, sizeof(msg), E_NODEVTAB, p);
517 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
518 			exitcd = EX_DEVTAB;
519 			break;
520 
521 			/*
522 			 * Some strange problem...
523 			 */
524 
525 		    default:
526 			(void) sprintf(msg, E_INTERNAL, errno);
527 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
528 			exitcd = EX_ERROR;
529 
530 		    }   /* End switch */
531 		}
532 		else {
533 
534 		    /* putdev -d device attr [attr [...]] */
535 
536 		    /*
537 		     * Attempt to remove the specified attributes from the
538 		     * specified device.
539 		     */
540 		    if (!(_rmdevtabattrs(device, &argv[optind], &plist))) switch(errno) {
541 
542 			/*
543 			 * EINVAL indicates that a named attribute was not
544 			 * defined for the specified device or "alias" was
545 			 * requested.  If "plist" points to a list of attrs,
546 			 * the former is the problem.  Otherwise, the latter
547 			 * is the problem.
548 			 */
549 
550 		    case EINVAL:
551 			if (plist) {
552 			    exitcd = EX_ATTRIB;
553 			    for (; *plist; plist++) {
554 				(void) snprintf(msg, sizeof(msg), E_NOATTR, *plist);
555 				stdmsg(MM_RECOVER, lbl, MM_WARNING, msg);
556 			    }
557 			} else {
558 			    stdmsg(MM_NRECOV, lbl, MM_ERROR, E_NOALIAS);
559 			    exitcd = EX_ERROR;
560 			}
561 			break;
562 
563 			/*
564 			 * ENODEV indicates that the named device is not
565 			 * defined in the device table.
566 			 */
567 
568 		    case ENODEV:
569 			(void) snprintf(msg, sizeof(msg), E_NODEV, device);
570 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
571 			exitcd = EX_EXISTS;
572 			break;
573 
574 			/*
575 			 * ENOENT indicates that the device table can't
576 			 * be found.
577 			 */
578 
579 		    case ENOENT:
580 			(void) snprintf(msg, sizeof(msg), E_NODEVTAB, _devtabpath());
581 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
582 			exitcd = EX_DEVTAB;
583 			break;
584 
585 			/*
586 			 * EACCES indicates that there was a problem reading the
587 			 * old device table or creating the new table.  If the
588 			 * old table is readable, assume that we can't create the
589 			 * new table.  Otherwise, assume that the old table isn't
590 			 * accessible.
591 			 */
592 
593 		    case EACCES:
594 			p = _devtabpath();
595 			if (access(p, R_OK) == 0)
596 			    (void) snprintf(msg, sizeof(msg), E_NOMKDTAB, p);
597 			else
598 			    (void) snprintf(msg, sizeof(msg), E_NODEVTAB, p);
599 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
600 			exitcd = EX_DEVTAB;
601 			break;
602 
603 			/*
604 			 * Some strange problem...
605 			 */
606 
607 		    default:
608 			(void) sprintf(msg, E_INTERNAL, errno);
609 			stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
610 			exitcd = EX_ERROR;
611 
612 		    }  /* End switch */
613 
614 		}   /* End "putdev -d device attr [...]" case */
615 
616 	    }   /* End passes usage-check case */
617 
618 	}   /* End -d case */
619 
620 
621 	/* Done.  Return exit code (determined above) */
622 	return(exitcd);
623 }  /* main() */
624