670 lines
16 KiB
C
Executable File
670 lines
16 KiB
C
Executable File
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
|
||
/* All Rights Reserved */
|
||
|
||
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
|
||
/* The copyright notice above does not evidence any */
|
||
/* actual or intended publication of such source code. */
|
||
|
||
#ident "@(#)main.c 1.3 92/07/14 SMI" /* SVr4.0 1.6.1.1 */
|
||
|
||
/*
|
||
* main.c
|
||
*
|
||
* Implements the "putdev" command.
|
||
*/
|
||
|
||
/*
|
||
* G L O B A L R E F E R E N C E S
|
||
*/
|
||
|
||
/*
|
||
* Header files needed:
|
||
* <sys/types.h> Standard UNIX(r) types
|
||
* <stdio.h> Standard I/O definitions
|
||
* <string.h> String handling definitions
|
||
* <errno.h> Error codes
|
||
* <unistd.h> Standard Identifiers
|
||
* <fmtmsg.h> Standard Message Generation definitions
|
||
* <devmgmt.h> Device Management definitions
|
||
*/
|
||
|
||
#include <sys/types.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <errno.h>
|
||
#include <unistd.h>
|
||
#include <fmtmsg.h>
|
||
#include <devmgmt.h>
|
||
|
||
|
||
/*
|
||
* Externals referenced (and not defined in header files)
|
||
* optind index to next arg on the command line (from getopt())
|
||
* opterr FLAG, TRUE tells getopt() to suppress error messages
|
||
* optarg Ptr to current option's argument
|
||
* getopt() Command-line parsing function
|
||
* malloc() Allocate a chunk of main memory
|
||
* putenv() Put a value into the environment
|
||
* exit() Exit the process
|
||
* access() Check the accessibility of a file
|
||
*/
|
||
|
||
extern int optind;
|
||
extern int opterr;
|
||
extern char *optarg;
|
||
extern int getopt();
|
||
extern void *malloc();
|
||
extern void exit();
|
||
extern int putenv();
|
||
extern int access();
|
||
|
||
|
||
/*
|
||
* Device Management Globals referenced:
|
||
* _adddevtabrec() Add a device-table entry to the device table
|
||
* _moddevtabrec() Modify a device-table entry in the dev tab
|
||
* _rmdevtabrec() Remove a device-table entry in the dev tab
|
||
* _rmdevtabattrs() Remove attributes from a device-table entry
|
||
* _validalias() Is a value a valid alias?
|
||
* _devtabpath() Get the pathname of the device table
|
||
*/
|
||
|
||
int _adddevtabrec();
|
||
int _moddevtabrec();
|
||
int _rmdevtabrec();
|
||
int _rmdevtabattrs();
|
||
int _validalias();
|
||
char *_devtabpath();
|
||
|
||
/*
|
||
* L O C A L D E F I N I T I O N S
|
||
*/
|
||
|
||
/*
|
||
* General Purpose Constants
|
||
* TRUE Boolean TRUE (if not already defined)
|
||
* FALSE Boolean FALSE (if not already defined)
|
||
* NULL Null address (if not already defined)
|
||
*/
|
||
|
||
#ifndef TRUE
|
||
#define TRUE (1)
|
||
#endif
|
||
|
||
#ifndef FALSE
|
||
#define FALSE (0)
|
||
#endif
|
||
|
||
#ifndef NULL
|
||
#define NULL (0)
|
||
#endif
|
||
|
||
|
||
/*
|
||
* Exit codes
|
||
* EX_OK All went well
|
||
* EX_ERROR Usage or internal error
|
||
* EX_DEVTAB Had trouble accessing/reading/writing the device table
|
||
* EX_EXISTS The specified alias already exists
|
||
* EX_ATTRIB One or more attributes requested for removal was not
|
||
* defined for the device
|
||
* EX_RELPATH Pathname supplied for cdevice, bdevice or pathname
|
||
* attributes was not a full pathname
|
||
*/
|
||
|
||
#define EX_OK 0
|
||
#define EX_ERROR 1
|
||
#define EX_DEVTAB 2
|
||
#define EX_EXISTS 3
|
||
#define EX_ATTRIB 4
|
||
#define EX_RELPATH 4
|
||
|
||
|
||
/*
|
||
* Error messages
|
||
*/
|
||
|
||
#define E_USAGE "usage: putdev -a alias [attribute=value [...]]\n putdev -m device attribute=value [attribute=value [...]]\n putdev -d device [attribute [...]]"
|
||
#define E_ALIASIS "Alias already exists in table: %s"
|
||
#define E_NODEV "Device does not exist in table: %s"
|
||
#define E_NOALIAS "Cannot use \"alias\" as an attribute"
|
||
#define E_NOATTR "Attribute not found: %s"
|
||
#define E_NODEVTAB "Cannot open the device table: %s"
|
||
#define E_NOMKDTAB "Cannot create a new device table: %s"
|
||
#define E_INVALIAS "Not a valid device alias: %s"
|
||
#define E_MULTIPLE "Multiple definitions of an attribute are not allowed."
|
||
#define E_INTERNAL "Internal error, errno=%d"
|
||
#define E_RELPATH "Full pathname required for cdevice,bdevice and pathname attributes."
|
||
|
||
|
||
/*
|
||
* Macros
|
||
* stdmsg(r,l,s,t) Using fmtmsg(), write a standard message to the
|
||
* standard error stream.
|
||
* Where:
|
||
* r The recoverability of the error
|
||
* l The label-component
|
||
* s The severity-component
|
||
* t The text-component
|
||
*/
|
||
|
||
#define stdmsg(r,l,s,t) (void) fmtmsg(MM_PRINT|MM_UTIL|r,l,s,t,MM_NULLACT,MM_NULLTAG)
|
||
|
||
|
||
/*
|
||
* Static data
|
||
* msg Space for message's text-component
|
||
*/
|
||
|
||
static char msg[256]; /* Space for text of message */
|
||
|
||
/*
|
||
* char *mklbl(cmd)
|
||
* char *cmd
|
||
*
|
||
* This function builds a standard label from the command used to invoke
|
||
* this process and the standard label prefix ("UX:")
|
||
*
|
||
* Arguments:
|
||
* char *cmd The command used to invoke this process.
|
||
*
|
||
* Returns: char *
|
||
* Pointer to malloc()ed space containing the standard label,
|
||
* or (char *) NULL if an error occurred.
|
||
*/
|
||
|
||
static char *
|
||
mklbl(cmd)
|
||
char *cmd;
|
||
{
|
||
/* Automatic data */
|
||
char *rtn; /* Value to return */
|
||
char *p; /* Temporary */
|
||
|
||
/* Find the 1st char of the basename of the command */
|
||
if (p = strrchr(cmd, '/')) p++;
|
||
else p = cmd;
|
||
|
||
/* Allocate and build the string value to return */
|
||
if (rtn = (char *) malloc(strlen("UX:")+strlen(p)+1)) {
|
||
(void) strcpy(rtn, "UX:");
|
||
(void) strcat(rtn, p);
|
||
}
|
||
|
||
|
||
/* Now that we've done all of that work, change the environment
|
||
* so that only the text-component is written by fmtmsg().
|
||
* (This should go away in SVR4.1)
|
||
*/
|
||
|
||
(void) putenv("MSGVERB=text");
|
||
|
||
|
||
/* Done */
|
||
return(rtn);
|
||
}
|
||
|
||
/*
|
||
* putdev -a alias [attribute=value [...]]
|
||
* putdev -m alias attribute=value [attribute=value [...]]
|
||
* putdev -d alias [attribute [...]]
|
||
*
|
||
* Modify the device-table. If -a specified, add a record for <alias>
|
||
* to the table. If -m specified, modify the attributes specified for
|
||
* the <device> specified. If -d specified, remove the specified
|
||
* attributes from the specified device or remove the specified device.
|
||
*
|
||
* Options:
|
||
* -a Add an alias description to the device table
|
||
* -m Modify an existing device description
|
||
* -d (if no attributes specified) remove the specified
|
||
* device from the device table, or (if attributes
|
||
* specified) remove the specified attributes from
|
||
* the specified device.
|
||
*
|
||
* Exit values:
|
||
* 0 All went well
|
||
* 1 Usage error (includes specifying "alias" as an
|
||
* <attribute>)
|
||
* 2 The device table file could not be opened, read
|
||
* or modified
|
||
* 3 If -a, the alias already exists. Otherwise, the
|
||
* specified device does not exist in the table
|
||
* 4 One of the specified attributes did not exist
|
||
* for the device and therefore wasn't removed
|
||
*/
|
||
|
||
main(argc, argv)
|
||
int argc; /* Argument count */
|
||
char *argv[]; /* Argument list */
|
||
{
|
||
/* Automatic data */
|
||
char **plist; /* Ptr to list of undef'nd attrs */
|
||
char *lbl; /* Ptr to label for messages */
|
||
char *alias; /* Ptr to <alias> on command-line */
|
||
char *device; /* Ptr to <device> on command-line */
|
||
char *p; /* Temp ptr to char */
|
||
int noerr; /* FLAG, TRUE if all's well */
|
||
int a_seen; /* TRUE if -a seen on command-line */
|
||
int m_seen; /* TRUE if -m seen on command-line */
|
||
int d_seen; /* TRUE if -a seen on command-line */
|
||
int optchar; /* Option extracted */
|
||
int exitcd; /* Value to return at exit */
|
||
int nattrs; /* Number of attributes on command */
|
||
|
||
|
||
/* Generate the label for messages */
|
||
lbl = mklbl(argv[0]);
|
||
|
||
/* Extract arguments - validate usage */
|
||
noerr = TRUE;
|
||
a_seen = FALSE;
|
||
m_seen = FALSE;
|
||
d_seen = FALSE;
|
||
opterr = FALSE;
|
||
while ((optchar = getopt(argc, argv, "a:d:m:")) != EOF) switch (optchar) {
|
||
|
||
case 'a':
|
||
if (!(a_seen || m_seen || d_seen)) {
|
||
a_seen = TRUE;
|
||
alias = optarg;
|
||
}
|
||
else noerr = FALSE;
|
||
break;
|
||
|
||
case 'd':
|
||
if (!(a_seen || m_seen || d_seen)) {
|
||
d_seen = TRUE;
|
||
device = optarg;
|
||
}
|
||
else noerr = FALSE;
|
||
break;
|
||
|
||
case 'm':
|
||
if (!(a_seen || m_seen || d_seen)) {
|
||
m_seen = TRUE;
|
||
device = optarg;
|
||
}
|
||
else noerr = FALSE;
|
||
break;
|
||
|
||
case '?':
|
||
default:
|
||
noerr = FALSE;
|
||
}
|
||
|
||
|
||
/* Write a usage message if we've seen a blatant error */
|
||
if (!(a_seen || m_seen || d_seen) || !noerr) {
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE);
|
||
exit(EX_ERROR);
|
||
}
|
||
|
||
|
||
/* Set up */
|
||
exitcd = EX_OK;
|
||
nattrs = argc - optind;
|
||
|
||
|
||
/* putdev -a alias [attr=value [...]] */
|
||
|
||
if (a_seen) {
|
||
|
||
/* Syntax check */
|
||
if (nattrs < 0) {
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE);
|
||
exitcd = EX_ERROR;
|
||
} else {
|
||
|
||
/* Attempt to add the new alias */
|
||
if (!(_adddevtabrec(alias, &argv[optind]))) {
|
||
|
||
/* Attempt failed. Write appropriate error message. */
|
||
|
||
switch(errno) {
|
||
|
||
/*
|
||
* EINVAL indicates that <alias> is not valid or "alias"
|
||
* was mentioned as <attr> in <attr>=<value> pair. If the
|
||
* alias is a valid alias, assume that's the problem.
|
||
*/
|
||
|
||
case EINVAL:
|
||
if (_validalias(alias))
|
||
p = E_NOALIAS;
|
||
else (void) sprintf(p=msg, E_INVALIAS, alias);
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, p);
|
||
exitcd = EX_ERROR;
|
||
break;
|
||
|
||
/*
|
||
* EEXIST indicates that the alias <alias> already exists
|
||
* in the device table.
|
||
*/
|
||
|
||
case EEXIST:
|
||
(void) sprintf(msg, E_ALIASIS, alias);
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
|
||
exitcd = EX_EXISTS;
|
||
break;
|
||
|
||
/*
|
||
* EACCES and ENOENT indicate problems reading or writing
|
||
* the device table.
|
||
*/
|
||
|
||
case EACCES:
|
||
case ENOENT:
|
||
p = _devtabpath();
|
||
if (access(p, R_OK) == 0)
|
||
(void) sprintf(msg, E_NOMKDTAB, p);
|
||
else
|
||
(void) sprintf(msg, E_NODEVTAB, p);
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
|
||
exitcd = EX_DEVTAB;
|
||
break;
|
||
|
||
/*
|
||
* EAGAIN indicates that an attribute was defined on the
|
||
* command line more than once.
|
||
*/
|
||
|
||
case EAGAIN:
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, E_MULTIPLE);
|
||
exitcd = EX_ERROR;
|
||
break;
|
||
|
||
/*
|
||
* ENXIO indicates that a relative pathname was supplied
|
||
* for the cdevice, bdevice or pathname attributes. Full
|
||
* pathnames are required for these attributes.
|
||
*/
|
||
case ENXIO:
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, E_RELPATH);
|
||
exitcd = EX_RELPATH;
|
||
break;
|
||
|
||
/*
|
||
* Some other problem (odd?)
|
||
*/
|
||
|
||
default:
|
||
(void) sprintf(msg, E_INTERNAL, errno);
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
|
||
exitcd = EX_ERROR;
|
||
}
|
||
}
|
||
}
|
||
} /* End -a case */
|
||
|
||
|
||
/* putdev -m device attr=value [...] */
|
||
|
||
else if (m_seen) {
|
||
|
||
/* Check usage */
|
||
|
||
if (nattrs <= 0) {
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE);
|
||
exitcd = EX_ERROR;
|
||
} else {
|
||
|
||
/* Attempt to modify a device's record */
|
||
if (!(_moddevtabrec(device, &argv[optind]))) {
|
||
|
||
/* Modification attempt failed */
|
||
|
||
switch(errno) {
|
||
|
||
/*
|
||
* EINVAL indicates that "alias" was used as an attribute
|
||
* in an <attr>=<value> pair.
|
||
*/
|
||
|
||
case EINVAL:
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, E_NOALIAS);
|
||
exitcd = EX_ERROR;
|
||
break;
|
||
|
||
/*
|
||
* ENODEV indicates that the device that was to
|
||
* be modified doesn't exist.
|
||
*/
|
||
|
||
case ENODEV:
|
||
(void) sprintf(msg, E_NODEV, device);
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
|
||
exitcd = EX_EXISTS;
|
||
break;
|
||
|
||
/*
|
||
* ENOENT indicates that the device-table doesn't exist.
|
||
*/
|
||
|
||
case ENOENT:
|
||
(void) sprintf(msg, E_NODEVTAB, _devtabpath());
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
|
||
exitcd = EX_DEVTAB;
|
||
break;
|
||
|
||
/*
|
||
* EACCES indicates that there was a problem reading the
|
||
* old device table or creating the new table. If the
|
||
* old table is readable, assume that we can't create the
|
||
* new table. Otherwise, assume that the old table isn't
|
||
* accessible.
|
||
*/
|
||
|
||
case EACCES:
|
||
p = _devtabpath();
|
||
if (access(p, R_OK) == 0)
|
||
(void) sprintf(msg, E_NOMKDTAB, p);
|
||
else
|
||
(void) sprintf(msg, E_NODEVTAB, p);
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
|
||
exitcd = EX_DEVTAB;
|
||
break;
|
||
|
||
/*
|
||
* EAGAIN indicates that an attribute was specified more than
|
||
* once on the command line.
|
||
*/
|
||
|
||
case EAGAIN:
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, E_MULTIPLE);
|
||
exitcd = EX_ERROR;
|
||
break;
|
||
|
||
/*
|
||
* ENXIO indicates that a relative pathname was supplied
|
||
* for the cdevice, bdevice or pathname attributes. Full
|
||
* pathnames are required for these attributes.
|
||
*/
|
||
case ENXIO:
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, E_RELPATH);
|
||
exitcd = EX_RELPATH;
|
||
break;
|
||
|
||
/*
|
||
* Some strange problem...
|
||
*/
|
||
|
||
default:
|
||
(void) sprintf(msg, E_INTERNAL, errno);
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
|
||
exitcd = EX_ERROR;
|
||
}
|
||
}
|
||
}
|
||
} /* End -m case */
|
||
|
||
else if (d_seen) {
|
||
|
||
/* putdev -d device [attr [...]] */
|
||
|
||
/* Check usage */
|
||
if (nattrs < 0) {
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE);
|
||
exitcd = EX_ERROR;
|
||
} else {
|
||
|
||
/*
|
||
* Determine case (removing a device or attributes
|
||
* to a device.
|
||
*/
|
||
|
||
if (nattrs == 0) {
|
||
|
||
/* putdev -d device */
|
||
|
||
/* Attempt to remove the specified device */
|
||
if (!(_rmdevtabrec(device))) switch(errno) {
|
||
|
||
/*
|
||
* ENODEV indicates that the named device is not
|
||
* defined in the device table.
|
||
*/
|
||
|
||
case ENODEV:
|
||
(void) sprintf(msg, E_NODEV, device);
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
|
||
exitcd = EX_EXISTS;
|
||
break;
|
||
|
||
/*
|
||
* ENOENT indicates that the device table can't
|
||
* be found.
|
||
*/
|
||
|
||
case ENOENT:
|
||
(void) sprintf(msg, E_NODEVTAB, _devtabpath());
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
|
||
exitcd = EX_DEVTAB;
|
||
break;
|
||
|
||
/*
|
||
* EACCES indicates that there was a problem reading the
|
||
* old device table or creating the new table. If the
|
||
* old table is readable, assume that we can't create the
|
||
* new table. Otherwise, assume that the old table isn't
|
||
* accessible.
|
||
*/
|
||
|
||
case EACCES:
|
||
p = _devtabpath();
|
||
if (access(p, R_OK) == 0)
|
||
(void) sprintf(msg, E_NOMKDTAB, p);
|
||
else
|
||
(void) sprintf(msg, E_NODEVTAB, p);
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
|
||
exitcd = EX_DEVTAB;
|
||
break;
|
||
|
||
/*
|
||
* Some strange problem...
|
||
*/
|
||
|
||
default:
|
||
(void) sprintf(msg, E_INTERNAL, errno);
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
|
||
exitcd = EX_ERROR;
|
||
|
||
} /* End switch */
|
||
}
|
||
else {
|
||
|
||
/* putdev -d device attr [attr [...]] */
|
||
|
||
/*
|
||
* Attempt to remove the specified attributes from the
|
||
* specified device.
|
||
*/
|
||
if (!(_rmdevtabattrs(device, &argv[optind], &plist))) switch(errno) {
|
||
|
||
/*
|
||
* EINVAL indicates that a named attribute was not
|
||
* defined for the specified device or "alias" was
|
||
* requested. If "plist" points to a list of attrs,
|
||
* the former is the problem. Otherwise, the latter
|
||
* is the problem.
|
||
*/
|
||
|
||
case EINVAL:
|
||
if (plist) {
|
||
exitcd = EX_ATTRIB;
|
||
for (; *plist; plist++) {
|
||
(void) sprintf(msg, E_NOATTR, *plist);
|
||
stdmsg(MM_RECOVER, lbl, MM_WARNING, msg);
|
||
}
|
||
} else {
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, E_NOALIAS);
|
||
exitcd = EX_ERROR;
|
||
}
|
||
break;
|
||
|
||
/*
|
||
* ENODEV indicates that the named device is not
|
||
* defined in the device table.
|
||
*/
|
||
|
||
case ENODEV:
|
||
(void) sprintf(msg, E_NODEV, device);
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
|
||
exitcd = EX_EXISTS;
|
||
break;
|
||
|
||
/*
|
||
* ENOENT indicates that the device table can't
|
||
* be found.
|
||
*/
|
||
|
||
case ENOENT:
|
||
(void) sprintf(msg, E_NODEVTAB, _devtabpath());
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
|
||
exitcd = EX_DEVTAB;
|
||
break;
|
||
|
||
/*
|
||
* EACCES indicates that there was a problem reading the
|
||
* old device table or creating the new table. If the
|
||
* old table is readable, assume that we can't create the
|
||
* new table. Otherwise, assume that the old table isn't
|
||
* accessible.
|
||
*/
|
||
|
||
case EACCES:
|
||
p = _devtabpath();
|
||
if (access(p, R_OK) == 0)
|
||
(void) sprintf(msg, E_NOMKDTAB, p);
|
||
else
|
||
(void) sprintf(msg, E_NODEVTAB, p);
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
|
||
exitcd = EX_DEVTAB;
|
||
break;
|
||
|
||
/*
|
||
* Some strange problem...
|
||
*/
|
||
|
||
default:
|
||
(void) sprintf(msg, E_INTERNAL, errno);
|
||
stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
|
||
exitcd = EX_ERROR;
|
||
|
||
} /* End switch */
|
||
|
||
} /* End "putdev -d device attr [...]" case */
|
||
|
||
} /* End passes usage-check case */
|
||
|
||
} /* End -d case */
|
||
|
||
|
||
/* Done. Return exit code (determined above) */
|
||
exit(exitcd);
|
||
|
||
#ifdef lint
|
||
return(exitcd);
|
||
#endif
|
||
} /* main() */
|