mirror of
https://github.com/sheumann/hush.git
synced 2024-10-04 21:55:16 +00:00
908e362133
I've had some issues with modprobe which I reported a few months ago. This is still an issue so I decided to sort it out. The attached diff includes the changes against the unstable cvs tree that work for me. Changes are: mod_process() will report success if the module at the head of the list loads successfully. It will also report success if any module unloads successfully. The net result being that modprobe will succeed in the cases outlined below. I've also added error reporting to modprobe -r. Previously it would silently fail (but report success) if the module could not be unloaded. Andrew
562 lines
12 KiB
C
562 lines
12 KiB
C
/* vi: set sw=4 ts=4: */
|
|
/*
|
|
* Modprobe written from scratch for BusyBox
|
|
*
|
|
* Copyright (c) 2002 by Robert Griebl, griebl@gmx.de
|
|
* Copyright (c) 2003 by Andrew Dennison, andrew.dennison@motec.com.au
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
#include <sys/utsname.h>
|
|
#include <getopt.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <syslog.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include "busybox.h"
|
|
|
|
|
|
|
|
struct dep_t {
|
|
char * m_module;
|
|
char * m_options;
|
|
|
|
int m_isalias : 1;
|
|
int m_reserved : 15;
|
|
|
|
int m_depcnt : 16;
|
|
char ** m_deparr;
|
|
|
|
struct dep_t * m_next;
|
|
};
|
|
|
|
struct mod_list_t {
|
|
char * m_module;
|
|
char * m_options;
|
|
|
|
struct mod_list_t * m_prev;
|
|
struct mod_list_t * m_next;
|
|
};
|
|
|
|
|
|
static struct dep_t *depend;
|
|
static int autoclean, show_only, quiet, do_syslog, verbose;
|
|
|
|
int parse_tag_value ( char *buffer, char **ptag, char **pvalue )
|
|
{
|
|
char *tag, *value;
|
|
|
|
while ( isspace ( *buffer ))
|
|
buffer++;
|
|
tag = value = buffer;
|
|
while ( !isspace ( *value ))
|
|
value++;
|
|
*value++ = 0;
|
|
while ( isspace ( *value ))
|
|
value++;
|
|
|
|
*ptag = tag;
|
|
*pvalue = value;
|
|
|
|
return bb_strlen( tag ) && bb_strlen( value );
|
|
}
|
|
|
|
/* Jump through hoops to simulate how fgets() grabs just one line at a
|
|
* time... Don't use any stdio since modprobe gets called from a kernel
|
|
* thread and stdio junk can overflow the limited stack...
|
|
*/
|
|
static char *reads ( int fd, char *buffer, size_t len )
|
|
{
|
|
int n = read ( fd, buffer, len );
|
|
|
|
if ( n > 0 ) {
|
|
char *p;
|
|
|
|
buffer [len-1] = 0;
|
|
p = strchr ( buffer, '\n' );
|
|
|
|
if ( p ) {
|
|
off_t offset;
|
|
|
|
offset = lseek ( fd, 0L, SEEK_CUR ); // Get the current file descriptor offset
|
|
lseek ( fd, offset-n + (p-buffer) + 1, SEEK_SET ); // Set the file descriptor offset to right after the \n
|
|
|
|
p[1] = 0;
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static struct dep_t *build_dep ( void )
|
|
{
|
|
int fd;
|
|
struct utsname un;
|
|
struct dep_t *first = 0;
|
|
struct dep_t *current = 0;
|
|
char buffer[256];
|
|
char *filename = buffer;
|
|
int continuation_line = 0;
|
|
|
|
if ( uname ( &un ))
|
|
return 0;
|
|
|
|
// check for buffer overflow in following code
|
|
if ( bb_strlen ( un.release ) > ( sizeof( buffer ) - 64 )) {
|
|
return 0;
|
|
}
|
|
|
|
strcpy ( filename, "/lib/modules/" );
|
|
strcat ( filename, un.release );
|
|
strcat ( filename, "/modules.dep" );
|
|
|
|
if (( fd = open ( filename, O_RDONLY )) < 0 )
|
|
return 0;
|
|
|
|
while ( reads ( fd, buffer, sizeof( buffer ))) {
|
|
int l = bb_strlen ( buffer );
|
|
char *p = 0;
|
|
|
|
while ( isspace ( buffer [l-1] )) {
|
|
buffer [l-1] = 0;
|
|
l--;
|
|
}
|
|
|
|
if ( l == 0 ) {
|
|
continuation_line = 0;
|
|
continue;
|
|
}
|
|
|
|
if ( !continuation_line ) {
|
|
char *col = strchr ( buffer, ':' );
|
|
|
|
if ( col ) {
|
|
char *mods;
|
|
char *mod;
|
|
int ext = 0;
|
|
|
|
*col = 0;
|
|
mods = strrchr ( buffer, '/' );
|
|
|
|
if ( !mods )
|
|
mods = buffer;
|
|
else
|
|
mods++;
|
|
|
|
if (( *(col-2) == '.' ) && ( *(col-1) == 'o' ))
|
|
ext = 2;
|
|
|
|
mod = bb_xstrndup ( mods, col - mods - ext );
|
|
|
|
if ( !current ) {
|
|
first = current = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
|
|
}
|
|
else {
|
|
current-> m_next = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
|
|
current = current-> m_next;
|
|
}
|
|
current-> m_module = mod;
|
|
current-> m_options = 0;
|
|
current-> m_isalias = 0;
|
|
current-> m_depcnt = 0;
|
|
current-> m_deparr = 0;
|
|
current-> m_next = 0;
|
|
|
|
//printf ( "%s:\n", mod );
|
|
|
|
p = col + 1;
|
|
}
|
|
else
|
|
p = 0;
|
|
}
|
|
else
|
|
p = buffer;
|
|
|
|
if ( p && *p ) {
|
|
char *end = &buffer [l-1];
|
|
char *deps = strrchr ( end, '/' );
|
|
char *dep;
|
|
int ext = 0;
|
|
|
|
while ( isblank ( *end ) || ( *end == '\\' ))
|
|
end--;
|
|
|
|
deps = strrchr ( p, '/' );
|
|
|
|
if ( !deps || ( deps < p )) {
|
|
deps = p;
|
|
|
|
while ( isblank ( *deps ))
|
|
deps++;
|
|
}
|
|
else
|
|
deps++;
|
|
|
|
if (( *(end-1) == '.' ) && ( *end == 'o' ))
|
|
ext = 2;
|
|
|
|
/* Cope with blank lines */
|
|
if ((end-deps-ext+1) <= 0)
|
|
continue;
|
|
|
|
dep = bb_xstrndup ( deps, end - deps - ext + 1 );
|
|
|
|
current-> m_depcnt++;
|
|
current-> m_deparr = (char **) xrealloc ( current-> m_deparr, sizeof ( char *) * current-> m_depcnt );
|
|
current-> m_deparr [current-> m_depcnt - 1] = dep;
|
|
|
|
//printf ( " %d) %s\n", current-> m_depcnt, current-> m_deparr [current-> m_depcnt -1] );
|
|
}
|
|
|
|
if ( buffer [l-1] == '\\' )
|
|
continuation_line = 1;
|
|
else
|
|
continuation_line = 0;
|
|
}
|
|
close ( fd );
|
|
|
|
// alias parsing is not 100% correct (no correct handling of continuation lines within an alias) !
|
|
|
|
if (( fd = open ( "/etc/modules.conf", O_RDONLY )) < 0 )
|
|
if (( fd = open ( "/etc/conf.modules", O_RDONLY )) < 0 )
|
|
return first;
|
|
|
|
continuation_line = 0;
|
|
while ( reads ( fd, buffer, sizeof( buffer ))) {
|
|
int l;
|
|
char *p;
|
|
|
|
p = strchr ( buffer, '#' );
|
|
if ( p )
|
|
*p = 0;
|
|
|
|
l = bb_strlen ( buffer );
|
|
|
|
while ( l && isspace ( buffer [l-1] )) {
|
|
buffer [l-1] = 0;
|
|
l--;
|
|
}
|
|
|
|
if ( l == 0 ) {
|
|
continuation_line = 0;
|
|
continue;
|
|
}
|
|
|
|
if ( !continuation_line ) {
|
|
if (( strncmp ( buffer, "alias", 5 ) == 0 ) && isspace ( buffer [5] )) {
|
|
char *alias, *mod;
|
|
|
|
if ( parse_tag_value ( buffer + 6, &alias, &mod )) {
|
|
// fprintf ( stderr, "ALIAS: '%s' -> '%s'\n", alias, mod );
|
|
|
|
if ( !current ) {
|
|
first = current = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
|
|
}
|
|
else {
|
|
current-> m_next = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
|
|
current = current-> m_next;
|
|
}
|
|
current-> m_module = bb_xstrdup ( alias );
|
|
current-> m_isalias = 1;
|
|
|
|
if (( strcmp ( alias, "off" ) == 0 ) || ( strcmp ( alias, "null" ) == 0 )) {
|
|
current-> m_depcnt = 0;
|
|
current-> m_deparr = 0;
|
|
}
|
|
else {
|
|
current-> m_depcnt = 1;
|
|
current-> m_deparr = xmalloc ( 1 * sizeof( char * ));
|
|
current-> m_deparr[0] = bb_xstrdup ( mod );
|
|
}
|
|
current-> m_next = 0;
|
|
}
|
|
}
|
|
else if (( strncmp ( buffer, "options", 7 ) == 0 ) && isspace ( buffer [7] )) {
|
|
char *mod, *opt;
|
|
|
|
if ( parse_tag_value ( buffer + 8, &mod, &opt )) {
|
|
struct dep_t *dt;
|
|
|
|
for ( dt = first; dt; dt = dt-> m_next ) {
|
|
if ( strcmp ( dt-> m_module, mod ) == 0 )
|
|
break;
|
|
}
|
|
if ( dt ) {
|
|
dt-> m_options = xrealloc ( dt-> m_options, bb_strlen( opt ) + 1 );
|
|
strcpy ( dt-> m_options, opt );
|
|
|
|
// fprintf ( stderr, "OPTION: '%s' -> '%s'\n", dt-> m_module, dt-> m_options );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
close ( fd );
|
|
|
|
return first;
|
|
}
|
|
|
|
|
|
static int mod_process ( struct mod_list_t *list, int do_insert )
|
|
{
|
|
char lcmd [256];
|
|
int rc = 1;
|
|
|
|
while ( list ) {
|
|
if ( do_insert )
|
|
snprintf ( lcmd, sizeof( lcmd ) - 1, "insmod %s %s %s %s %s 2>/dev/null", do_syslog ? "-s" : "", autoclean ? "-k" : "", quiet ? "-q" : "", list-> m_module, list-> m_options ? list-> m_options : "" );
|
|
else
|
|
snprintf ( lcmd, sizeof( lcmd ) - 1, "rmmod %s %s 2>/dev/null", do_syslog ? "-s" : "", list-> m_module );
|
|
|
|
if ( verbose )
|
|
printf ( "%s\n", lcmd );
|
|
if ( !show_only ) {
|
|
int rc2 = system ( lcmd );
|
|
if (do_insert) rc = rc2; /* only last module matters */
|
|
else if (!rc2) rc = 0; /* success if remove any mod */
|
|
}
|
|
|
|
list = do_insert ? list-> m_prev : list-> m_next;
|
|
}
|
|
return (show_only) ? 0 : rc;
|
|
}
|
|
|
|
static void check_dep ( char *mod, struct mod_list_t **head, struct mod_list_t **tail )
|
|
{
|
|
struct mod_list_t *find;
|
|
struct dep_t *dt;
|
|
char *opt = 0;
|
|
int lm;
|
|
|
|
// remove .o extension
|
|
lm = bb_strlen ( mod );
|
|
if (( mod [lm-2] == '.' ) && ( mod [lm-1] == 'o' ))
|
|
mod [lm-2] = 0;
|
|
|
|
// check dependencies
|
|
for ( dt = depend; dt; dt = dt-> m_next ) {
|
|
if ( strcmp ( dt-> m_module, mod ) == 0 ) {
|
|
opt = dt-> m_options;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// resolve alias names
|
|
while ( dt && dt-> m_isalias ) {
|
|
if ( dt-> m_depcnt == 1 ) {
|
|
struct dep_t *adt;
|
|
|
|
for ( adt = depend; adt; adt = adt-> m_next ) {
|
|
if ( strcmp ( adt-> m_module, dt-> m_deparr [0] ) == 0 )
|
|
break;
|
|
}
|
|
if ( adt ) {
|
|
dt = adt;
|
|
mod = dt-> m_module;
|
|
if ( !opt )
|
|
opt = dt-> m_options;
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
// search for duplicates
|
|
for ( find = *head; find; find = find-> m_next ) {
|
|
if ( !strcmp ( mod, find-> m_module )) {
|
|
// found -> dequeue it
|
|
|
|
if ( find-> m_prev )
|
|
find-> m_prev-> m_next = find-> m_next;
|
|
else
|
|
*head = find-> m_next;
|
|
|
|
if ( find-> m_next )
|
|
find-> m_next-> m_prev = find-> m_prev;
|
|
else
|
|
*tail = find-> m_prev;
|
|
|
|
break; // there can be only one duplicate
|
|
}
|
|
}
|
|
|
|
if ( !find ) { // did not find a duplicate
|
|
find = (struct mod_list_t *) xmalloc ( sizeof(struct mod_list_t));
|
|
find-> m_module = mod;
|
|
find-> m_options = opt;
|
|
}
|
|
|
|
// enqueue at tail
|
|
if ( *tail )
|
|
(*tail)-> m_next = find;
|
|
find-> m_prev = *tail;
|
|
find-> m_next = 0;
|
|
|
|
if ( !*head )
|
|
*head = find;
|
|
*tail = find;
|
|
|
|
if ( dt ) {
|
|
int i;
|
|
|
|
for ( i = 0; i < dt-> m_depcnt; i++ )
|
|
check_dep ( dt-> m_deparr [i], head, tail );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static int mod_insert ( char *mod, int argc, char **argv )
|
|
{
|
|
struct mod_list_t *tail = 0;
|
|
struct mod_list_t *head = 0;
|
|
int rc;
|
|
|
|
// get dep list for module mod
|
|
check_dep ( mod, &head, &tail );
|
|
|
|
if ( head && tail ) {
|
|
if ( argc ) {
|
|
int i;
|
|
int l = 0;
|
|
|
|
// append module args
|
|
for ( i = 0; i < argc; i++ )
|
|
l += ( bb_strlen ( argv [i] ) + 1 );
|
|
|
|
head-> m_options = xrealloc ( head-> m_options, l + 1 );
|
|
head-> m_options [0] = 0;
|
|
|
|
for ( i = 0; i < argc; i++ ) {
|
|
strcat ( head-> m_options, argv [i] );
|
|
strcat ( head-> m_options, " " );
|
|
}
|
|
}
|
|
|
|
// process tail ---> head
|
|
rc = mod_process ( tail, 1 );
|
|
}
|
|
else
|
|
rc = 1;
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int mod_remove ( char *mod )
|
|
{
|
|
int rc;
|
|
static struct mod_list_t rm_a_dummy = { "-a", 0, 0 };
|
|
|
|
struct mod_list_t *head = 0;
|
|
struct mod_list_t *tail = 0;
|
|
|
|
if ( mod )
|
|
check_dep ( mod, &head, &tail );
|
|
else // autoclean
|
|
head = tail = &rm_a_dummy;
|
|
|
|
if ( head && tail )
|
|
rc = mod_process ( head, 0 ); // process head ---> tail
|
|
else
|
|
rc = 1;
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern int modprobe_main(int argc, char** argv)
|
|
{
|
|
int opt;
|
|
int remove_opt = 0;
|
|
|
|
autoclean = show_only = quiet = do_syslog = verbose = 0;
|
|
|
|
while ((opt = getopt(argc, argv, "acdklnqrst:vVC:")) != -1) {
|
|
switch(opt) {
|
|
case 'c': // no config used
|
|
case 'l': // no pattern matching
|
|
return EXIT_SUCCESS;
|
|
break;
|
|
case 'C': // no config used
|
|
case 't': // no pattern matching
|
|
bb_error_msg_and_die("-t and -C not supported");
|
|
|
|
case 'a': // ignore
|
|
case 'd': // ignore
|
|
break;
|
|
case 'k':
|
|
autoclean++;
|
|
break;
|
|
case 'n':
|
|
show_only++;
|
|
break;
|
|
case 'q':
|
|
quiet++;
|
|
break;
|
|
case 'r':
|
|
remove_opt++;
|
|
break;
|
|
case 's':
|
|
do_syslog++;
|
|
break;
|
|
case 'v':
|
|
verbose++;
|
|
break;
|
|
case 'V':
|
|
default:
|
|
bb_show_usage();
|
|
break;
|
|
}
|
|
}
|
|
|
|
depend = build_dep ( );
|
|
|
|
if ( !depend )
|
|
bb_error_msg_and_die ( "could not parse modules.dep\n" );
|
|
|
|
if (remove_opt) {
|
|
int rc = EXIT_SUCCESS;
|
|
do {
|
|
if (mod_remove ( optind < argc ?
|
|
bb_xstrdup (argv [optind]) : NULL )) {
|
|
bb_error_msg ("failed to remove module %s",
|
|
argv [optind] );
|
|
rc = EXIT_FAILURE;
|
|
}
|
|
} while ( ++optind < argc );
|
|
|
|
return rc;
|
|
}
|
|
|
|
if (optind >= argc)
|
|
bb_error_msg_and_die ( "No module or pattern provided\n" );
|
|
|
|
if ( mod_insert ( bb_xstrdup ( argv [optind] ), argc - optind - 1, argv + optind + 1 ))
|
|
bb_error_msg_and_die ( "failed to load module %s", argv [optind] );
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
|