/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fmtmsg.h>
#include <devmgmt.h>
#include <devtab.h>
#include <values.h>
/*
* Local definitions
* TRUE Boolean TRUE value
* FALSE Boolean FALSE value
* TOKDELIMS Char string of delimiters for lists
*/
#ifndef TRUE
#define TRUE ('t')
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define TOKDELIMS ", \t\n"
/*
* Exit codes:
* EX_OK Exit code for all went well
* EX_ERROR Exit code for something failed
* EX_TABLES A table couldn't be accessed
* EX_NOALLOC Exit code for allocation failed
*/
#define EX_OK 0
#define EX_ERROR 1
#define EX_TABLES 2
#define EX_NOALLOC 3
/*
* Messages:
* M_USAGE Usage error
* M_INVKEY Invalid key specified
* M_ERROR Some strange error
* M_UNABLE A list of devices is unavailable
* M_DEVTAB Can't access device table (for reading)
* M_RSVTAB Can't access device reservation table (for r/w)
* M_NODEV A list of devices is invalid
*/
#define M_USAGE "usage: devreserv [key [devicelist [...]]]"
#define M_INVKEY "Invalid key: %s"
#define M_ERROR "Internal error, errno=%d"
#define M_UNABLE "Cannot reserve devices"
#define M_DEVTAB "Cannot open the device table: %s"
#define M_RSVTAB "Cannot open the device-reservation table: %s"
#define M_NODEV M_UNABLE
/*
* Local functions and static data
*
* buildreqlist() Builds the list of requested devices for devreserv()
* freereqlist() Free space allocated to the list of requested devices
* ndevsin() Get number of elements in a list
* stdmsg(r,l,s,m) Standard message generation
* r Recoverability flag
* l Label
* s Severity
* m Message
*
* lbl Buffer for the label-component of a message
* txt Buffer for the text-component of a message
*/
static char ***buildreqlist();
static void freereqlist();
static int ndevsin();
#define stdmsg(r,l,s,m) (void) fmtmsg(MM_PRINT|MM_UTIL|r,l,s,m,MM_NULLACT,MM_NULLTAG)
static char lbl[MM_MXLABELLN+1];
static char txt[MM_MXTXTLN+1];
/*
* devreserv [key [devlist [devlist [...]]]]
*
* This command reserves sets of devices known to the OA&M device
* management system. It reserves a device from each of the device
* lists presented to it, reserving them on the key (<key>). If no
* device-lists are provided, the command lists those devices reserved
* on the given key (<key>). If no key (<key>) is provided, the
* command lists all devices currently reserved.
*
* Options: None
*
* Arguments:
* key Key to lock the devices on
* devlist A comma-, space-, or tab-list containing devices
* (pathnames or aliases). For typical shells, space-
* and tab-lists should be quoted or the separator should
* be somehow escaped.
*
* Command Values:
* EX_OK 0 Device(s) successfully allocated
* EX_ERROR 1 A syntax or other error occurred
* EX_TABLES 2 Either the device-table or the device-
* reservation table couldn't be opened as needed
* EX_NOALLOC 3 The device-reservation request couldn't be
* fulfilled.
*/
int
main(int argc, char *argv[])
{
/* Automatics */
char ***reqlist; /* * to list of lists */
char **argp; /* Ptr to current argument */
char **alloclist; /* List of allocated devices */
char **pp; /* Temp ptr to char ptrs */
struct reservdev **rsvd; /* Ptr to list of rsvd devs */
struct reservdev **plk; /* Running ptr to locks */
char *p; /* Temp char ptr */
char *devtab; /* Device table pathname */
char *rsvtab; /* Dev-rsv tbl pathname */
int argcount; /* Number of args on cmd */
long lkey; /* Key for locking (long) */
int key; /* Key for locking */
int exitcode; /* Value to return */
int sev; /* Message severity */
int syntaxerr; /* Flag, TRUE if syntax error */
int c; /* Option character */
int i; /* Temp counter */
/*
* Initializations
*/
/* Build a message label */
if (p = strrchr(argv[0], '/')) p++;
else p = argv[0];
(void) strlcat(strcpy(lbl, "UX:"), p, sizeof(lbl));
/*
* Allow only the text component of messages to be written
* (this will probably go away in SVR4.1)
*/
(void) putenv("MSGVERB=text");
/*
* Parse the options from the command line
*/
opterr = 0;
syntaxerr = FALSE;
while ((c = getopt(argc, argv, "")) != EOF) switch(c) {
default:
syntaxerr = FALSE;
break;
}
/* If there's (an obvious) syntax error, write a message and quit */
if (syntaxerr) {
stdmsg(MM_NRECOV, lbl, MM_ERROR, M_USAGE);
exit(EX_ERROR);
}
/* Argument initializations */
argcount = argc - optind;
argp = &argv[optind];
/*
* devreserv
*
* If euid == 0, write a list of all currently allocated devices.
*/
if (argcount == 0) {
/* Get the list of reserved devices */
if (rsvd = reservdev()) {
/* Write the list of reserved devices with the key
* that the device was locked on. The key should go
* in column 16, but separate it from the alias with at
* least one space */
exitcode = EX_OK;
for (plk = rsvd ; *plk ; plk++) {
if ((i = fputs((*plk)->devname, stdout)) >= 0) do
(void) fputc(' ', stdout);
while (++i < 16);
(void) fprintf(stdout, "%ld\n", (*plk)->key);
}
} else {
/* Problems getting the list of reserved devices */
if (((errno == EINVAL) || (errno == EACCES)) && (rsvtab = _rsvtabpath())) {
(void) snprintf(txt, sizeof(txt), M_RSVTAB, rsvtab);
exitcode = EX_TABLES;
sev = MM_ERROR;
} else {
(void) sprintf(txt, M_ERROR, errno);
exitcode = EX_ERROR;
sev = MM_HALT;
}
stdmsg(MM_NRECOV, lbl, sev, txt);
}
/* Finished */
exit(exitcode);
}
/*
* devreserv key
*
* Generate a list of the devices allocated on a specific key.
*/
if (argcount == 1) {
/* Extract the key from the command */
lkey = strtol(*argp, &p, 10);
if (*p || (lkey <= 0) || (lkey > MAXINT)) {
/* <key> argument invalid */
(void) snprintf(txt, sizeof(txt), M_INVKEY, *argp);
stdmsg(MM_NRECOV, lbl, MM_ERROR, txt);
exitcode = EX_ERROR;
} else {
key = (int) lkey;
/* Get the list of reserved devices ... */
if (rsvd = reservdev()) {
/* For each reserved device, write the alias to stdout */
exitcode = EX_OK;
for (plk = rsvd ; *plk ; plk++) {
if ((*plk)->key == key) (void) puts((*plk)->devname);
}
} else {
/* Problems getting the list of reserved devices */
if (((errno == EINVAL) || (errno == EACCES)) && (rsvtab = _rsvtabpath())) {
(void) snprintf(txt, sizeof(txt), M_RSVTAB, rsvtab);
exitcode = EX_TABLES;
sev = MM_ERROR;
} else {
(void) sprintf(txt, M_ERROR, errno);
exitcode = EX_ERROR;
sev = MM_HALT;
}
stdmsg(MM_NRECOV, lbl, sev, txt);
}
}
/* Finished */
exit(exitcode);
}
/*
* devreserv key devlist [...]
*
* Reserve specific devices
*/
/* Open the device file (if there's one to be opened) */
if (!_opendevtab("r")) {
if (devtab = _devtabpath()) {
(void) snprintf(txt, sizeof(txt), M_DEVTAB, devtab);
exitcode = EX_TABLES;
sev = MM_ERROR;
} else {
(void) sprintf(txt, M_ERROR, errno);
exitcode = EX_ERROR;
sev = MM_HALT;
}
stdmsg(MM_NRECOV, lbl, sev, txt);
exit(exitcode);
}
/* Extract the key from the command */
lkey = strtol(*argp, &p, 10);
if (*p || (lkey <= 0) || (lkey > MAXINT)) {
(void) snprintf(txt, sizeof(txt), M_INVKEY, *argp);
stdmsg(MM_NRECOV, lbl, MM_ERROR, txt);
exit(EX_ERROR);
}
key = (int) lkey;
argp++;
/* Build the device request list from the command arguments */
if (reqlist = buildreqlist(argp)) {
/* Attempt to allocate the devices */
if (alloclist = devreserv(key, reqlist)) {
/*
* For each allocated device, write the alias to stdout
* and free the space allocated for the string.
*/
for (pp = alloclist; *pp; pp++) {
(void) puts(*pp);
free(*pp);
}
/* Free the list of allocated devices */
free((char *) alloclist);
exitcode = EX_OK;
}
else {
/* Device allocation failed */
if (errno == EAGAIN) {
stdmsg(MM_NRECOV, lbl, MM_ERROR, M_UNABLE);
exitcode = EX_NOALLOC;
} else if (errno == ENODEV) {
stdmsg(MM_NRECOV, lbl, MM_ERROR, M_NODEV);
exitcode = EX_NOALLOC;
} else {
(void) sprintf(txt, M_ERROR, errno);
stdmsg(MM_NRECOV, lbl, MM_HALT, txt);
exitcode = EX_ERROR;
}
}
freereqlist(reqlist);
}
/* Exit with the appropriate code */
return(exitcode);
}
/*
* char ***buildreqlist(args)
* char **args
*
* Build the list of lists of devices to request, as described by the
* arguments on the command line.
*
* Arguments:
* char **args The address of the first argument of the list of
* lists of devices to allocate. (This list is
* terminated with a (char *) NULL.)
*
* Returns: char ***
* A pointer to a list containing addresses of lists of pointers to
* character-strings, as expected by "devreserv()"
*
* Notes:
* - Assuming that strtok() won't return "". If it does, the
* parsing algorithm needs to be enhanced a bit to eliminate
* these cases.
*/
static char ***
buildreqlist(args)
char **args;
{
/* Local automatic data */
char ***addrlist; /* Addr of space for ptrs to lists */
char ***ppp; /* Pointer to pointers to pointers */
char **pp; /* Pointer to pointers */
char **qq; /* Pointer to pointers */
int noerror; /* FLAG, TRUE if all's well */
int i; /* Counter */
int n; /* Another counter */
/* Count the number of lists we have to work with */
i = 1;
for (pp = args ; *pp ; pp++) i++;
/* If we can allocate space for the list of lists ... */
if (addrlist = (char ***) malloc(i*sizeof(char **))) {
/* Parse each list, putting that list in the list of lists */
ppp = addrlist;
noerror = TRUE;
for (pp = args ; noerror && *pp ; pp++) {
n = ndevsin(*pp, TOKDELIMS);
if (*ppp = (char **) malloc((n+1)*sizeof(char *))) {
qq = *ppp++;
if (*qq++ = strtok(*pp, TOKDELIMS))
while (*qq++ = strtok((char *) NULL, TOKDELIMS));
} else noerror = FALSE;
}
/* If there was an error, clean up the malloc()s we've made */
if (!noerror) {
freereqlist(addrlist);
addrlist = (char ***) NULL;
}
}
/* Return ptr to the list of addresses of lists (or NULL if none) */
return(addrlist);
}
/*
* void freereqlist(list)
* char ***list
*
* This function frees the space allocated to the list of lists
* referenced by <list>
*
* Arguments:
* char ***list Address of the list of lists
*
* Returns: void
*/
static void
freereqlist(list)
char ***list;
{
char ***ppp;
if (list) {
for (ppp = list ; *ppp ; ppp++) free((char *) *ppp);
free((char *) list);
}
}
/*
* int ndevsin(list, delims)
* char *list
* char *delims
*
* This function determines how many tokens are in the list <list>.
* The tokens are delimited by fields of characters in the string
* <delims>. It returns the number of tokens in the list.
*
* Arguments:
* char *list The <delims>list of tokens to scan
* char *delims The list of delimiters that define the list
*
* Returns: int
* The number of elements in the list.
*
* Notes:
* - This function does not recognize "null" elements. For example,
* a,b,,,,c,,d contains 4 elememts (if delims contains a ',')
*/
static int
ndevsin(list, delims)
char *list; /* List to scan */
char *delims; /* Delimiters */
{
char *p; /* Running character pointer */
int count; /* Number of tokens seen so far */
int tokflag; /* TRUE if we're parsing a token */
count = 0; /* None seen yet */
tokflag = FALSE; /* Not in a token */
/* Scan the character-string containing the list of tokens */
for (p = list ; *p ; p++) {
/* If a delimiter, we're not in a token */
if (strchr(delims, *p)) tokflag = FALSE;
/* Otherwise, if we weren't in a token, we've found one */
else if (!tokflag) {
tokflag = TRUE;
count++;
}
}
/* Return the number of elements in the list */
return(count);
}