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
118static	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
135static char *
136mklbl(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
196int
197main(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