drvsubr.c revision 1ca932730d3439e527d5fe4a15444600d0df7e7e
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/sysmacros.h>
#include <libintl.h>
#include <wait.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <grp.h>
#include "addrem.h"
#include "errmsg.h"
#include "plcysubr.h"
static char *add_rem_lock; /* lock file */
static char *tmphold; /* temperary file for updating */
char *file_name);
static int is_blank(char *);
/*ARGSUSED*/
void
{
switch (err) {
case MP_FOPEN_ERR:
break;
case MP_FCLOSE_ERR:
break;
case MP_IGNORING_LINE_ERR:
break;
case MP_ALLOC_ERR:
break;
case MP_NVLIST_ERR:
break;
case MP_CANT_FIND_USER_ERR:
break;
case MP_CANT_FIND_GROUP_ERR:
break;
}
}
/*
* open file
* for each entry in list
* where list entries are separated by <list_separator>
* append entry : driver_name <entry_separator> entry
* close file
*/
int
char *driver_name,
char *entry_list,
char *filename,
char list_separator,
char *entry_separator)
{
int i, len;
int fpint;
char *current_head, *previous_head;
filename);
return (ERROR);
}
return (ERROR);
}
err_exit();
}
/*
* get one entry at a time from list and append to <filename> file
*/
do {
for (i = 0; i <= len; i++)
one_entry[i] = 0;
line[i] = 0;
filename);
}
} while (*current_head != '\0');
return (NOERR);
}
/*
* open file
* read thru file, deleting all entries if first
* entry = driver_name
* close
* if error, leave original file intact with message
* assumption : drvconfig has been modified to work with clone
* entries in /etc/minor_perm as driver:mummble NOT
* clone:driver mummble
* this implementation will NOT find clone entries
* clone:driver mummble
* match:
* delete just the matching entry
*
*/
int
char *oldfile,
char *driver_name,
char *marker,
char *match)
{
int rv, i;
int drvr_found = 0;
/*
* check if match is specified and if it equals " "
* this is a special case handling as we do a strstr(3STRING)
* to match an entry. By default all entries are space separated
* and without this check all entries of the file could get deleted.
*/
return (ERROR);
}
return (ERROR);
}
/*
* Build filename for temporary file
*/
}
/*
* Set gid so we preserve group attribute. Ideally we wouldn't
* assume a gid of "sys" but we can't undo the damage on already
* installed systems unless we force the issue.
*/
}
newfile);
return (ERROR);
}
/* copy the whole line into dup */
break;
}
/* cut off comments starting with '#' */
*cp = '\0';
/* ignore comment or blank lines */
oldfile);
}
continue;
}
/* get the driver name */
break;
}
drv[i] = '\0';
}
oldfile);
}
} else {
drvr_found++;
if (match) { /* Just delete one entry */
/* for now delete just minor_perm and aliases */
} else {
EOF) {
oldfile);
}
}
}
}
} /* end of else */
} /* end of while */
/* Make sure that the file is on disk */
else
/* no matching driver found */
if (!drvr_found ||
rv = NONE_FOUND;
}
/*
* if error, leave original file, delete new file
* if noerr, replace original file with new file
*/
return (ERROR);
}
return (ERROR);
}
} else {
/*
* since there's an error, leave file alone; remove
* new file
*/
}
return (ERROR);
}
return (rv);
}
/*
* wrapper for call to get_name_to_major_entry(): given driver name,
* retrieve major number.
*/
int
{
return (ERROR);
else
return (major);
}
/*
* wrapper for call to get_name_to_major_entry(): given major number,
* retrieve driver name.
*/
int
{
if (major < 0)
return (ERROR);
}
/*
* return pointer to cached name_to_major file - reads file into
* cache if this has not already been done. Since there may be
* requests for multiple name_to_major files (rem_name_to_major,
* name_to_major), this routine keeps a list of cached files.
*/
static int
{
struct n_to_m_cache {
char *file;
char **cached_file;
int size;
struct n_to_m_cache *next;
};
struct n_to_m_cache *ptr;
int maj;
int size = 0;
/*
* see if the file is already cached - either
* rem_name_to_major or name_to_major
*/
break;
}
filename);
return (ERROR);
}
/* cut off comments starting with '#' */
*cp = '\0';
/* ignore comment or blank lines */
continue;
/* sanity-check */
continue;
}
}
/* allocate struct to cache the file */
sizeof (struct n_to_m_cache));
return (ERROR);
}
/* allocate space to cache contents of file */
return (ERROR);
}
/*
* now fill the cache
* the cache is an array of char pointers indexed by major
* number
*/
/* cut off comments starting with '#' */
*cp = '\0';
/* ignore comment or blank lines */
continue;
/* sanity-check */
continue;
}
return (ERROR);
}
}
/* link the cache struct into the list of cached files */
return (ERROR);
}
}
/* return value pointer to contents of file */
/* return size */
}
/*
* Using get_cached_n_to_m_file(), retrieve maximum major number
* found in the specificed file (name_to_major/rem_name_to_major).
*
* The return value is actually the size of the internal cache including 0.
*/
int
get_max_major(char *file_name)
{
char **n_to_m_cache = NULL;
}
/*
* searching name_to_major: if major_no == UNIQUE then the caller wants to
* use the driver name as the key. Otherwise, the caller wants to use
* the major number as a key.
*
* This routine caches the contents of the name_to_major file on
* first call. And it could be generalized to deal with other
* config files if necessary.
*/
static int
{
int maj;
char **n_to_m_cache = NULL;
int size = 0;
int ret = NOT_UNIQUE;
/*
* read the file in - we cache it in case caller wants to
* do multiple lookups
*/
return (ERROR);
/* search with driver name as key */
break;
}
}
/* search with major number as key */
} else {
/*
* Bugid 1254588, drvconfig dump core after loading driver
* with major number bigger than entries defined in
* /etc/name_to_major.
*/
return (UNIQUE);
} else
}
return (ret);
}
/*
* given pointer to member n in space separated list, return pointer
* to member n+1, return member n
*/
char *
char *prev_member,
char *current_entry,
char separator)
{
char *ptr;
ptr = prev_member;
/* skip white space */
ptr++;
/* read thru the current entry */
*current_entry++ = *ptr++;
}
*current_entry = '\0';
ptr++; /* skip over comma */
/* skip white space */
ptr++;
}
return (ptr);
}
/*ARGSUSED0*/
static void
signal_rtn(int sig)
{
exit_unlock();
}
void
enter_lock(void)
{
int fd;
/*
* Setup handler to clean lock file in case user terminates
* the command.
*/
/*
* attempt to create the lock file
*/
} else {
}
exit(1);
}
}
void
err_exit(void)
{
/* release memory allocated for moddir */
exit_unlock();
exit(1);
}
void
cleanup_moddir(void)
{
struct drvmod_dir *walk_ptr;
}
}
void
exit_unlock(void)
{
}
}
}
/*
* error adding driver; need to back out any changes to files.
* check flag to see which files need entries removed
* entry removal based on driver name
*/
void
int c_flag,
char *driver_name)
{
if (c_flag & CLEAN_NAM_MAJ) {
}
}
if (c_flag & CLEAN_DRV_ALIAS) {
}
}
if (c_flag & CLEAN_DRV_CLASSES) {
ERROR) {
}
}
if (c_flag & CLEAN_MINOR_PERM) {
}
}
/*
* There's no point in removing entries from files that don't
* exist. Prevent error messages by checking for file existence
* first.
*/
if ((c_flag & CLEAN_DEV_POLICY) != 0 &&
}
}
if ((c_flag & CLEAN_DRV_PRIV) != 0 &&
ERROR) {
}
}
}
int
int m_flag,
int i_flag)
{
/*
* If neither i_flag nor m_flag are specified no need to check the
* files for access permissions
*/
return (NOERR);
/* check minor_perm file : exits and is writable */
if (m_flag) {
return (ERROR);
}
}
/* check driver_aliases file : exits and is writable */
if (i_flag) {
return (ERROR);
}
}
return (NOERR);
}
int
check_name_to_major(int mode)
{
/* check name_to_major file : exists and is writable */
return (ERROR);
}
return (NOERR);
}
/*
* All this stuff is to support a server installing
* drivers on diskless clients. When on the server
* need to prepend the basedir
*/
int
build_filenames(char *basedir)
{
int len;
} else {
if ((driver_aliases == NULL) ||
(driver_classes == NULL) ||
(minor_perm == NULL) ||
(name_to_major == NULL) ||
(rem_name_to_major == NULL) ||
(add_rem_lock == NULL) ||
(devfs_root == NULL) ||
(device_policy == NULL) ||
(extra_privs == NULL)) {
return (ERROR);
}
}
return (NOERR);
}
static int
{
int waitstat;
int exit_status;
/* child */
return (ERROR);
} else if (pid == -1) {
/* fork failed */
return (ERROR);
} else {
/* parent */
do {
return (exit_status);
}
}
/*
* check that major_num doesn't exceed maximum on this machine
* do this here to support add_drv on server for diskless clients
*/
int
char *driver_name,
char *aliases,
char *classes,
int cleanup_flag,
int verbose_flag)
{
int max_dev;
int n = 0;
char *cmdline[MAX_CMD_LINE];
char maj_num[128];
char *previous;
char *current;
int exec_status;
int len;
return (ERROR);
}
return (ERROR);
}
/* bind major number and driver name */
/* build command line */
if (verbose_flag) {
cmdline[n++] = "-v";
}
cmdline[n++] = "-b";
if (classes) {
cmdline[n++] = "-c";
}
cmdline[n++] = "-i";
cmdline[n++] = driver_name;
cmdline[n++] = "-m";
do {
cmdline[n++] = "-a";
return (ERROR);
}
cmdline[n++], ' ');
} while (*current != '\0');
}
cmdline[n] = (char *)0;
if (exec_status == NOERR)
return (NOERR);
return (ERROR);
}
void
{
int n = 0;
char *cmdline[MAX_CMD_LINE];
int exec_status;
/* build command line */
if (verbose_flag) {
cmdline[n++] = "-v";
}
cmdline[n++] = "-i";
cmdline[n++] = driver_name;
cmdline[n] = (char *)0;
if (exec_status != NOERR) {
/* no clean : name and major number are bound */
}
}
void
{
do {
/*
* If we are at the end of the list of loaded modules
* then set *mod = -1 and return
*/
*mod = -1;
return;
}
}
int
create_reconfig(char *basedir)
{
} else {
}
return (ERROR);
(void) fclose(reconfig_fp);
return (NOERR);
}
/*
* update_minor_entry:
* open file
* for each entry in list
* where list entries are separated by <list_separator>
* modify entry : driver_name <entry_separator> entry
* close file
*
*/
int
{
int match = 0;
extern void bzero();
return (ERROR);
}
/*
* Build filename for temporary file
*/
}
/*
* Set gid so we preserve group attribute. Ideally we wouldn't
* assume a gid of "sys" but we can't undo the damage on already
* installed systems unless we force the issue.
*/
}
newfile);
return (ERROR);
}
}
/* copy the whole line into dup */
break;
}
/* cut off comments starting with '#' */
*cp = '\0';
/* ignore comment or blank lines */
}
continue;
}
/* get the driver name */
minor_perm, line);
break;
}
/*
* get the minor name; place the NULL character at the
* end of the driver name, then make the drv_minor
* point to the first character of the minor name.
* the line missing ':' must be treated as a broken one.
*/
minor_perm, line);
break;
}
drv[i] = '\0';
/*
* compare both of the driver name and the minor name.
* then the new line should be written to the file if
* both of them match
*/
/* if it has a comment, keep it */
cp++; /* skip a terminator */
} else {
}
match = 1;
}
/* update the file */
}
}
if (!match) {
/* add the new entry */
}
}
/*
* if error, leave original file, delete new file
* if noerr, replace original file with new file
*/
return (ERROR);
(void) unlink(minor_perm);
}
return (ERROR);
}
} else {
/*
* since there's an error, leave file alone; remove
* new file
*/
}
return (ERROR);
}
return (NOERR);
}
/*
* list_entry:
* open file
* read thru file, listing all entries if first entry = driver_name
* close
*/
void
char *oldfile,
char *driver_name,
char *marker)
{
int i;
return;
}
/* cut off comments starting with '#' */
*cp = '\0';
/* ignore comment or blank lines */
continue;
/* sanity-check */
}
drv[i] = '\0';
}
}
}
}
static boolean_t
{
/*
* Check the token here. According to IEEE1275 Open Firmware Boot
* Standard, the name is composed of 1 to 31 letters,
* digits and punctuation characters from the set ",._+-", and
* uppercase and lowercase characters are considered distinct.
* (ie. token := [a-zA-Z0-9,._+-]+, length(token) <= 31)
* However, since either the definition of driver or aliase names is
* not known well, only '#' is avoided explicitly. (the kernel lexical
* analyzer treats it as a start of a comment)
*/
return (B_FALSE);
return (B_TRUE);
}
/*
* check each entry in perm_list for:
* 4 arguments
* permission arg is in valid range
* permlist entries separated by comma
*/
int
check_perm_opts(char *perm_list)
{
char *current_head;
char *previous_head;
char *one_entry;
int intperm;
if (len == 0) {
return (ERROR);
}
return (ERROR);
}
while (*current_head != '\0') {
for (i = 0; i <= len; i++)
one_entry[i] = 0;
if (scan_stat < 4) {
"-m", one_entry);
}
if (scan_stat > 4) {
"-m", one_entry);
}
}
}
return (status);
}
/*
* check each alias :
* alias list members separated by white space
* cannot exist as driver name in /etc/name_to_major
* cannot exist as driver or alias name in /etc/driver_aliases
*/
int
aliases_unique(char *aliases)
{
char *current_head;
char *previous_head;
char *one_entry;
int i, len;
int is_unique;
return (ERROR);
}
do {
for (i = 0; i <= len; i++)
one_entry[i] = 0;
return (ERROR);
}
return (ERROR);
}
return (ERROR);
}
"-i", one_entry);
return (ERROR);
}
} while (*current_head != '\0');
return (NOERR);
}
int
char *driver_name,
char *aliases)
{
/* make call to update the aliases file */
}
int
unique_drv_alias(char *drv_alias)
{
/* cut off comments starting with '#' */
*cp = '\0';
/* ignore comment or blank lines */
continue;
/* sanity-check */
}
}
return (status);
} else {
return (ERROR);
}
}
/*
* search for driver_name in first field of file file_name
* searching name_to_major and driver_aliases: name separated from rest of
* line by blank
* if there return
* else return
*/
int
int *is_unique)
{
int ret;
} else {
/* XXX */
/* check alias file for name collision */
} else {
*is_unique = NOT_UNIQUE;
else
}
}
return (ret);
}
int
check_space_within_quote(char *str)
{
register int i;
register int len;
int quoted = 0;
if (*str == '"') {
if (quoted == 0)
quoted++;
else
quoted--;
return (ERROR);
}
return (0);
}
/*
* get major number
* write driver_name major_num to name_to_major file
* major_num returned in major_num
*/
int
{
char *num_list;
int new_maj = -1;
int max_dev = 0;
/*
* if driver_name already in rem_name_to_major
* delete entry from rem_nam_to_major
* put entry into name_to_major
*/
have_rem_n2m = 1;
}
if (have_rem_n2m) {
== ERROR)
return (ERROR);
/*
* found a match in rem_name_to_major
*/
char scratch[FILENAME_MAX];
/*
* If there is a match in /etc/rem_name_to_major then
* be paranoid: is that major number already in
* /etc/name_to_major (potentially under another name)?
*/
/*
* nuke the rem_name_to_major entry-- it
* isn't helpful.
*/
(void) delete_entry(rem_name_to_major,
} else {
"%d", is_unique);
return (ERROR);
}
return (ERROR);
}
/* found matching entry : no errors */
return (NOERR);
}
}
}
/*
* Bugid: 1264079
* In a server case (with -b option), we can't use modctl() to find
* the maximum major number, we need to dig thru client's
* /etc/name_to_major and /etc/rem_name_to_major for the max_dev.
*
* if (server)
* get maximum major number thru (rem_)name_to_major file on client
* else
* get maximum major number allowable on current system using modctl
*/
if (server) {
max_dev = 0;
tmp = 0;
/* If rem_name_to_major exists, we need to check it too */
if (have_rem_n2m) {
/*
* If name_to_major is missing, we can get max_dev from
* /etc/rem_name_to_major. If both missing, bail out!
*/
return (ERROR);
}
/* guard against bigger maj_num in rem_name_to_major */
} else {
/*
* If we can't get major from name_to_major file
* and there is no /etc/rem_name_to_major file,
* then we don't have a max_dev, bail out quick!
*/
return (ERROR);
}
/*
* In case there is no more slack in current name_to_major
* table, provide at least 1 extra entry so the add_drv can
* succeed. Since only one add_drv process is allowed at one
* time, and hence max_dev will be re-calculated each time
* add_drv is ran, we don't need to worry about adding more
* than 1 extra slot for max_dev.
*/
max_dev++;
} else {
return (ERROR);
}
}
/*
* max_dev is really how many slots the kernel has allocated for
* devices... [0 , maxdev-1], not the largest available device num.
*/
return (ERROR);
}
/*
* Populate the num_list array
*/
return (ERROR);
}
if (have_rem_n2m) {
return (ERROR);
}
/* find first free major number */
for (i = 0; i < max_dev; i++) {
if (num_list[i] != 1) {
new_maj = i;
break;
}
}
if (new_maj == -1) {
return (ERROR);
}
return (ERROR);
}
return (NOERR);
}
int
{
/*
* Read through the file, marking each major number found
* order is not relevant
*/
return (ERROR);
}
/* cut off comments starting with '#' */
*cp = '\0';
/* ignore comment or blank lines */
continue;
/* sanity-check */
return (ERROR);
}
if (dnum > L_MAXMAJ32) {
continue;
}
/*
* cast down to a major_t; we can be sure this is safe because
* of the above range-check.
*/
if (drv_majnum >= *nelems) {
/*
* Allocate some more space, up to drv_majnum + 1 so
* we can accomodate 0 through drv_majnum.
*
* Note that in the failure case, we leak all of the
* old contents of array. It's ok, since we just
* wind up exiting immediately anyway.
*/
return (ERROR);
}
}
}
return (0);
}
int
{
' ', " "));
}
/*
* is_blank() returns 1 (true) if a line specified is composed of
* whitespace characters only. otherwise, it returns 0 (false).
*
* Note. the argument (line) must be null-terminated.
*/
static int
{
return (0);
return (1);
}