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