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 *
mklbl(cmd)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
main(int argc,char * argv[])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