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