From f60d0b2e7dbd9d85980866c68d0f87b6bc823663 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Mon, 2 Apr 2007 10:31:10 +1000 Subject: Initial gitification of petitboot Start a git repo for petitboot, sources from Ben Herrenschmidt and Jeremy Kerr. Signed-off-by: Jeremy Kerr Signed-off-by: Benjamin Herrenschmidt --- devices/Makefile | 9 + devices/message.h | 26 +++ devices/native-parser.c | 136 +++++++++++ devices/params.c | 595 ++++++++++++++++++++++++++++++++++++++++++++++++ devices/params.h | 6 + devices/udev-helper.c | 430 ++++++++++++++++++++++++++++++++++ devices/udev-helper.h | 27 +++ 7 files changed, 1229 insertions(+) create mode 100644 devices/Makefile create mode 100644 devices/message.h create mode 100644 devices/native-parser.c create mode 100644 devices/params.c create mode 100644 devices/params.h create mode 100644 devices/udev-helper.c create mode 100644 devices/udev-helper.h (limited to 'devices') diff --git a/devices/Makefile b/devices/Makefile new file mode 100644 index 0000000..ec9d87c --- /dev/null +++ b/devices/Makefile @@ -0,0 +1,9 @@ + +CC=gcc +CFLAGS=-Wall -g -O2 + +parsers = native-parser.o + +all: udev-helper + +udev-helper: udev-helper.o params.o $(parsers) diff --git a/devices/message.h b/devices/message.h new file mode 100644 index 0000000..2e8bbba --- /dev/null +++ b/devices/message.h @@ -0,0 +1,26 @@ + +enum device_action { + DEV_ACTION_ADD_DEVICE = 0, + DEV_ACTION_ADD_OPTION = 1, + DEV_ACTION_REMOVE_DEVICE = 2, + DEV_ACTION_REMOVE_OPTION = 3 +}; + +struct device { + char *id; + char *name; + char *description; + char *icon_file; +}; + +struct boot_option { + char *id; + char *name; + char *description; + char *icon_file; + char *boot_image_file; + char *initrd_file; + char *boot_args; +}; + + diff --git a/devices/native-parser.c b/devices/native-parser.c new file mode 100644 index 0000000..f47fdca --- /dev/null +++ b/devices/native-parser.c @@ -0,0 +1,136 @@ + +#include "udev-helper.h" +#include "params.h" + +#include +#include +#include + +const char *conf_filename = "/boot/petitboot.conf"; + +static struct boot_option *cur_opt; +static struct device *dev; +static const char *mountpoint; +int device_added; + +static char *prepend_mountpoint(const char *path) +{ + char *full_path; + + full_path = malloc(strlen(path) + strlen(mountpoint) + 2); + + strcpy(full_path, mountpoint); + if (path[0] != '/') + strcat(full_path, "/"); + strcat(full_path, path); + + return full_path; +} + +int check_and_add_device(struct device *dev) +{ + if (!dev->icon_file) + dev->icon_file = strdup(generic_icon_file(guess_device_type())); + + return !add_device(dev); +} + +static int section(char *section_name) +{ + if (!device_added++ && !check_and_add_device(dev)) + return 0; + + if (cur_opt) { + add_boot_option(cur_opt); + free_boot_option(cur_opt); + } + + cur_opt = malloc(sizeof(*cur_opt)); + memset(cur_opt, 0, sizeof(*cur_opt)); + return 1; +} + + +static void set_boot_option_parameter(struct boot_option *opt, + const char *name, const char *value) +{ + if (streq(name, "name")) + opt->name = strdup(value); + + else if (streq(name, "description")) + opt->description = strdup(value); + + else if (streq(name, "image")) + opt->boot_image_file = prepend_mountpoint(value); + + else if (streq(name, "icon")) + opt->icon_file = prepend_mountpoint(value); + + else if (streq(name, "initrd")) + opt->initrd_file = prepend_mountpoint(value); + + else if (streq(name, "args")) + opt->boot_args = strdup(value); + + else + fprintf(stderr, "Unknown parameter %s\n", name); +} + +static void set_device_parameter(struct device *dev, + const char *name, const char *value) +{ + if (streq(name, "name")) + dev->name = strdup(value); + + else if (streq(name, "description")) + dev->description = strdup(value); + + else if (streq(name, "icon")) + dev->icon_file = prepend_mountpoint(value); +} + +static int parameter(char *param_name, char *param_value) +{ + if (cur_opt) + set_boot_option_parameter(cur_opt, param_name, param_value); + else + set_device_parameter(dev, param_name, param_value); + return 1; +} + + +int parse(const char *devicepath, const char *_mountpoint) +{ + char *filepath; + + mountpoint = _mountpoint; + + filepath = prepend_mountpoint(conf_filename); + + cur_opt = NULL; + dev = malloc(sizeof(*dev)); + memset(dev, 0, sizeof(*dev)); + dev->id = strdup(devicepath); + + pm_process(filepath, section, parameter); + + if (cur_opt) { + add_boot_option(cur_opt); + free_boot_option(cur_opt); + } + + cur_opt = NULL; + + free(filepath); + + return 1; +} + +struct parser native_parser = { + .name = "native petitboot parser", + .priority = 100, + .parse = parse +}; + + + diff --git a/devices/params.c b/devices/params.c new file mode 100644 index 0000000..76a1451 --- /dev/null +++ b/devices/params.c @@ -0,0 +1,595 @@ +/* This modules is based on the params.c module from Samba, written by Karl Auer + and much modifed by Christopher Hertel. */ + +/* + * 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., + * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include "params.h" + +#define new_array(type, num) ((type *)_new_array(sizeof(type), (num))) +#define realloc_array(ptr, type, num) \ + ((type *)_realloc_array((ptr), sizeof(type), (num))) + +#define rprintf(x, ...) do { fprintf(stderr, ##__VA_ARGS__); \ + fprintf(stderr, "\n"); } while (0) +#define rsyserr(x, y, ...) do { fprintf(stderr, ##__VA_ARGS__); \ + fprintf(stderr, "\n"); } while (0) + +#define MALLOC_MAX 0x40000000 +#define False 0 +#define True 1 + +void *_new_array(unsigned int size, unsigned long num) +{ + if (num >= MALLOC_MAX/size) + return NULL; + return malloc(size * num); +} + +void *_realloc_array(void *ptr, unsigned int size, unsigned long num) +{ + if (num >= MALLOC_MAX/size) + return NULL; + /* No realloc should need this, but just in case... */ + if (!ptr) + return malloc(size * num); + return realloc(ptr, size * num); +} + + +/* -------------------------------------------------------------------------- ** + * + * Module name: params + * + * -------------------------------------------------------------------------- ** + * + * This module performs lexical analysis and initial parsing of a + * Windows-like parameter file. It recognizes and handles four token + * types: section-name, parameter-name, parameter-value, and + * end-of-file. Comments and line continuation are handled + * internally. + * + * The entry point to the module is function pm_process(). This + * function opens the source file, calls the Parse() function to parse + * the input, and then closes the file when either the EOF is reached + * or a fatal error is encountered. + * + * A sample parameter file might look like this: + * + * [section one] + * parameter one = value string + * parameter two = another value + * [section two] + * new parameter = some value or t'other + * + * The parameter file is divided into sections by section headers: + * section names enclosed in square brackets (eg. [section one]). + * Each section contains parameter lines, each of which consist of a + * parameter name and value delimited by an equal sign. Roughly, the + * syntax is: + * + * :== {
} EOF + * + *
:==
{ } + * + *
:== '[' NAME ']' + * + * :== NAME '=' VALUE '\n' + * + * Blank lines and comment lines are ignored. Comment lines are lines + * beginning with either a semicolon (';') or a pound sign ('#'). + * + * All whitespace in section names and parameter names is compressed + * to single spaces. Leading and trailing whitespace is stipped from + * both names and values. + * + * Only the first equals sign in a parameter line is significant. + * Parameter values may contain equals signs, square brackets and + * semicolons. Internal whitespace is retained in parameter values, + * with the exception of the '\r' character, which is stripped for + * historic reasons. Parameter names may not start with a left square + * bracket, an equal sign, a pound sign, or a semicolon, because these + * are used to identify other tokens. + * + * -------------------------------------------------------------------------- ** + */ + +/* -------------------------------------------------------------------------- ** + * Constants... + */ + +#define BUFR_INC 1024 + + +/* -------------------------------------------------------------------------- ** + * Variables... + * + * bufr - pointer to a global buffer. This is probably a kludge, + * but it was the nicest kludge I could think of (for now). + * bSize - The size of the global buffer . + */ + +static char *bufr = NULL; +static int bSize = 0; + +/* -------------------------------------------------------------------------- ** + * Functions... + */ + +static int EatWhitespace( FILE *InFile ) + /* ------------------------------------------------------------------------ ** + * Scan past whitespace (see ctype(3C)) and return the first non-whitespace + * character, or newline, or EOF. + * + * Input: InFile - Input source. + * + * Output: The next non-whitespace character in the input stream. + * + * Notes: Because the config files use a line-oriented grammar, we + * explicitly exclude the newline character from the list of + * whitespace characters. + * - Note that both EOF (-1) and the nul character ('\0') are + * considered end-of-file markers. + * + * ------------------------------------------------------------------------ ** + */ + { + int c; + + for( c = getc( InFile ); isspace( c ) && ('\n' != c); c = getc( InFile ) ) + ; + return( c ); + } /* EatWhitespace */ + +static int EatComment( FILE *InFile ) + /* ------------------------------------------------------------------------ ** + * Scan to the end of a comment. + * + * Input: InFile - Input source. + * + * Output: The character that marks the end of the comment. Normally, + * this will be a newline, but it *might* be an EOF. + * + * Notes: Because the config files use a line-oriented grammar, we + * explicitly exclude the newline character from the list of + * whitespace characters. + * - Note that both EOF (-1) and the nul character ('\0') are + * considered end-of-file markers. + * + * ------------------------------------------------------------------------ ** + */ + { + int c; + + for( c = getc( InFile ); ('\n'!=c) && (EOF!=c) && (c>0); c = getc( InFile ) ) + ; + return( c ); + } /* EatComment */ + +static int Continuation( char *line, int pos ) + /* ------------------------------------------------------------------------ ** + * Scan backards within a string to discover if the last non-whitespace + * character is a line-continuation character ('\\'). + * + * Input: line - A pointer to a buffer containing the string to be + * scanned. + * pos - This is taken to be the offset of the end of the + * string. This position is *not* scanned. + * + * Output: The offset of the '\\' character if it was found, or -1 to + * indicate that it was not. + * + * ------------------------------------------------------------------------ ** + */ + { + pos--; + while( (pos >= 0) && isspace(((unsigned char *)line)[pos]) ) + pos--; + + return( ((pos >= 0) && ('\\' == line[pos])) ? pos : -1 ); + } /* Continuation */ + + +static BOOL Section( FILE *InFile, BOOL (*sfunc)(char *) ) + /* ------------------------------------------------------------------------ ** + * Scan a section name, and pass the name to function sfunc(). + * + * Input: InFile - Input source. + * sfunc - Pointer to the function to be called if the section + * name is successfully read. + * + * Output: True if the section name was read and True was returned from + * . False if failed or if a lexical error was + * encountered. + * + * ------------------------------------------------------------------------ ** + */ + { + int c; + int i; + int end; + char *func = "params.c:Section() -"; + + i = 0; /* is the offset of the next free byte in bufr[] and */ + end = 0; /* is the current "end of string" offset. In most */ + /* cases these will be the same, but if the last */ + /* character written to bufr[] is a space, then */ + /* will be one less than . */ + + c = EatWhitespace( InFile ); /* We've already got the '['. Scan */ + /* past initial white space. */ + + while( (EOF != c) && (c > 0) ) + { + + /* Check that the buffer is big enough for the next character. */ + if( i > (bSize - 2) ) + { + bSize += BUFR_INC; + bufr = realloc_array( bufr, char, bSize ); + if( NULL == bufr ) + { + rprintf(FERROR, "%s Memory re-allocation failure.", func); + return( False ); + } + } + + /* Handle a single character. */ + switch( c ) + { + case ']': /* Found the closing bracket. */ + bufr[end] = '\0'; + if( 0 == end ) /* Don't allow an empty name. */ + { + rprintf(FERROR, "%s Empty section name in configuration file.\n", func ); + return( False ); + } + if( !sfunc( bufr ) ) /* Got a valid name. Deal with it. */ + return( False ); + (void)EatComment( InFile ); /* Finish off the line. */ + return( True ); + + case '\n': /* Got newline before closing ']'. */ + i = Continuation( bufr, i ); /* Check for line continuation. */ + if( i < 0 ) + { + bufr[end] = '\0'; + rprintf(FERROR, "%s Badly formed line in configuration file: %s\n", + func, bufr ); + return( False ); + } + end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i); + c = getc( InFile ); /* Continue with next line. */ + break; + + default: /* All else are a valid name chars. */ + if( isspace( c ) ) /* One space per whitespace region. */ + { + bufr[end] = ' '; + i = end + 1; + c = EatWhitespace( InFile ); + } + else /* All others copy verbatim. */ + { + bufr[i++] = c; + end = i; + c = getc( InFile ); + } + } + } + + /* We arrive here if we've met the EOF before the closing bracket. */ + rprintf(FERROR, "%s Unexpected EOF in the configuration file: %s\n", func, bufr ); + return( False ); + } /* Section */ + +static BOOL Parameter( FILE *InFile, BOOL (*pfunc)(char *, char *), int c ) + /* ------------------------------------------------------------------------ ** + * Scan a parameter name and value, and pass these two fields to pfunc(). + * + * Input: InFile - The input source. + * pfunc - A pointer to the function that will be called to + * process the parameter, once it has been scanned. + * c - The first character of the parameter name, which + * would have been read by Parse(). Unlike a comment + * line or a section header, there is no lead-in + * character that can be discarded. + * + * Output: True if the parameter name and value were scanned and processed + * successfully, else False. + * + * Notes: This function is in two parts. The first loop scans the + * parameter name. Internal whitespace is compressed, and an + * equal sign (=) terminates the token. Leading and trailing + * whitespace is discarded. The second loop scans the parameter + * value. When both have been successfully identified, they are + * passed to pfunc() for processing. + * + * ------------------------------------------------------------------------ ** + */ + { + int i = 0; /* Position within bufr. */ + int end = 0; /* bufr[end] is current end-of-string. */ + int vstart = 0; /* Starting position of the parameter value. */ + char *func = "params.c:Parameter() -"; + + /* Read the parameter name. */ + while( 0 == vstart ) /* Loop until we've found the start of the value. */ + { + + if( i > (bSize - 2) ) /* Ensure there's space for next char. */ + { + bSize += BUFR_INC; + bufr = realloc_array( bufr, char, bSize ); + if( NULL == bufr ) + { + rprintf(FERROR, "%s Memory re-allocation failure.", func) ; + return( False ); + } + } + + switch( c ) + { + case '=': /* Equal sign marks end of param name. */ + if( 0 == end ) /* Don't allow an empty name. */ + { + rprintf(FERROR, "%s Invalid parameter name in config. file.\n", func ); + return( False ); + } + bufr[end++] = '\0'; /* Mark end of string & advance. */ + i = end; /* New string starts here. */ + vstart = end; /* New string is parameter value. */ + bufr[i] = '\0'; /* New string is nul, for now. */ + break; + + case '\n': /* Find continuation char, else error. */ + i = Continuation( bufr, i ); + if( i < 0 ) + { + bufr[end] = '\0'; + rprintf(FERROR, "%s Ignoring badly formed line in configuration file: %s\n", + func, bufr ); + return( True ); + } + end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i); + c = getc( InFile ); /* Read past eoln. */ + break; + + case '\0': /* Shouldn't have EOF within param name. */ + case EOF: + bufr[i] = '\0'; + rprintf(FERROR, "%s Unexpected end-of-file at: %s\n", func, bufr ); + return( True ); + + default: + if( isspace( c ) ) /* One ' ' per whitespace region. */ + { + bufr[end] = ' '; + i = end + 1; + c = EatWhitespace( InFile ); + } + else /* All others verbatim. */ + { + bufr[i++] = c; + end = i; + c = getc( InFile ); + } + } + } + + /* Now parse the value. */ + c = EatWhitespace( InFile ); /* Again, trim leading whitespace. */ + while( (EOF !=c) && (c > 0) ) + { + + if( i > (bSize - 2) ) /* Make sure there's enough room. */ + { + bSize += BUFR_INC; + bufr = realloc_array( bufr, char, bSize ); + if( NULL == bufr ) + { + rprintf(FERROR, "%s Memory re-allocation failure.", func) ; + return( False ); + } + } + + switch( c ) + { + case '\r': /* Explicitly remove '\r' because the older */ + c = getc( InFile ); /* version called fgets_slash() which also */ + break; /* removes them. */ + + case '\n': /* Marks end of value unless there's a '\'. */ + i = Continuation( bufr, i ); + if( i < 0 ) + c = 0; + else + { + for( end = i; (end >= 0) && isspace(((unsigned char *) bufr)[end]); end-- ) + ; + c = getc( InFile ); + } + break; + + default: /* All others verbatim. Note that spaces do */ + bufr[i++] = c; /* not advance . This allows trimming */ + if( !isspace( c ) ) /* of whitespace at the end of the line. */ + end = i; + c = getc( InFile ); + break; + } + } + bufr[end] = '\0'; /* End of value. */ + + return( pfunc( bufr, &bufr[vstart] ) ); /* Pass name & value to pfunc(). */ + } /* Parameter */ + +static BOOL Parse( FILE *InFile, + BOOL (*sfunc)(char *), + BOOL (*pfunc)(char *, char *) ) + /* ------------------------------------------------------------------------ ** + * Scan & parse the input. + * + * Input: InFile - Input source. + * sfunc - Function to be called when a section name is scanned. + * See Section(). + * pfunc - Function to be called when a parameter is scanned. + * See Parameter(). + * + * Output: True if the file was successfully scanned, else False. + * + * Notes: The input can be viewed in terms of 'lines'. There are four + * types of lines: + * Blank - May contain whitespace, otherwise empty. + * Comment - First non-whitespace character is a ';' or '#'. + * The remainder of the line is ignored. + * Section - First non-whitespace character is a '['. + * Parameter - The default case. + * + * ------------------------------------------------------------------------ ** + */ + { + int c; + + c = EatWhitespace( InFile ); + while( (EOF != c) && (c > 0) ) + { + switch( c ) + { + case '\n': /* Blank line. */ + c = EatWhitespace( InFile ); + break; + + case ';': /* Comment line. */ + case '#': + c = EatComment( InFile ); + break; + + case '[': /* Section Header. */ + if (!sfunc) return True; + if( !Section( InFile, sfunc ) ) + return( False ); + c = EatWhitespace( InFile ); + break; + + case '\\': /* Bogus backslash. */ + c = EatWhitespace( InFile ); + break; + + default: /* Parameter line. */ + if( !Parameter( InFile, pfunc, c ) ) + return( False ); + c = EatWhitespace( InFile ); + break; + } + } + return( True ); + } /* Parse */ + +static FILE *OpenConfFile( char *FileName ) + /* ------------------------------------------------------------------------ ** + * Open a configuration file. + * + * Input: FileName - The pathname of the config file to be opened. + * + * Output: A pointer of type (FILE *) to the opened file, or NULL if the + * file could not be opened. + * + * ------------------------------------------------------------------------ ** + */ + { + FILE *OpenedFile; + char *func = "params.c:OpenConfFile() -"; + + if( NULL == FileName || 0 == *FileName ) + { + rprintf(FERROR,"%s No configuration filename specified.\n", func); + return( NULL ); + } + + OpenedFile = fopen( FileName, "r" ); + if( NULL == OpenedFile ) + { + rsyserr(FERROR, errno, "unable to open configuration file \"%s\"", + FileName); + } + + return( OpenedFile ); + } /* OpenConfFile */ + +BOOL pm_process( char *FileName, + BOOL (*sfunc)(char *), + BOOL (*pfunc)(char *, char *) ) + /* ------------------------------------------------------------------------ ** + * Process the named parameter file. + * + * Input: FileName - The pathname of the parameter file to be opened. + * sfunc - A pointer to a function that will be called when + * a section name is discovered. + * pfunc - A pointer to a function that will be called when + * a parameter name and value are discovered. + * + * Output: TRUE if the file was successfully parsed, else FALSE. + * + * ------------------------------------------------------------------------ ** + */ + { + int result; + FILE *InFile; + char *func = "params.c:pm_process() -"; + + InFile = OpenConfFile( FileName ); /* Open the config file. */ + if( NULL == InFile ) + return( False ); + + if( NULL != bufr ) /* If we already have a buffer */ + result = Parse( InFile, sfunc, pfunc ); /* (recursive call), then just */ + /* use it. */ + + else /* If we don't have a buffer */ + { /* allocate one, then parse, */ + bSize = BUFR_INC; /* then free. */ + bufr = new_array( char, bSize ); + if( NULL == bufr ) + { + rprintf(FERROR,"%s memory allocation failure.\n", func); + fclose(InFile); + return( False ); + } + result = Parse( InFile, sfunc, pfunc ); + free( bufr ); + bufr = NULL; + bSize = 0; + } + + fclose(InFile); + + if( !result ) /* Generic failure. */ + { + rprintf(FERROR,"%s Failed. Error returned from params.c:parse().\n", func); + return( False ); + } + + return( True ); /* Generic success. */ + } /* pm_process */ + +/* -------------------------------------------------------------------------- */ + diff --git a/devices/params.h b/devices/params.h new file mode 100644 index 0000000..02a39c9 --- /dev/null +++ b/devices/params.h @@ -0,0 +1,6 @@ + +#define BOOL int + +BOOL pm_process( char *FileName, + BOOL (*sfunc)(char *), + BOOL (*pfunc)(char *, char *) ); diff --git a/devices/udev-helper.c b/devices/udev-helper.c new file mode 100644 index 0000000..f936086 --- /dev/null +++ b/devices/udev-helper.c @@ -0,0 +1,430 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev-helper.h" + +#define parser_dir "." + +#define tmp_dir "/var/tmp/petitboot" +#define socket_file "/var/tmp/petitboot-dev" +#define mount_bin "/bin/mount" +#define umount_bin "/bin/umount" + +extern struct parser native_parser; +static FILE *logf; +static int sock; + +/* array of parsers, ordered by priority */ +static struct parser *parsers[] = { + &native_parser, + NULL +}; + +#define log(...) fprintf(logf, __VA_ARGS__) + +static void iterate_parsers(const char *devpath, const char *mountpoint) +{ + int i; + + log("trying parsers for %s@%s\n", devpath, mountpoint); + + for (i = 0; parsers[i]; i++) { + log("\ttrying parser '%s'\n", parsers[i]->name); + /* just use a dummy device path for now */ + if (parsers[i]->parse(devpath, mountpoint)) + return; + } + log("\tno boot_options found\n"); +} + +static void print_boot_option(const struct boot_option *opt) +{ + log("\tname: %s\n", opt->name); + log("\tdescription: %s\n", opt->description); + log("\tboot_image: %s\n", opt->boot_image_file); + log("\tboot_args: %s\n", opt->boot_args); + +} + +static void print_device(const struct device *dev) +{ + log("\tid: %s\n", dev->name); + log("\tname: %s\n", dev->name); + log("\tdescription: %s\n", dev->description); + log("\tboot_image: %s\n", dev->icon_file); +} + + +void free_device(struct device *dev) +{ + if (!dev) + return; + if (dev->id) + free(dev->id); + if (dev->name) + free(dev->name); + if (dev->description) + free(dev->description); + if (dev->icon_file) + free(dev->icon_file); + free(dev); +} + +void free_boot_option(struct boot_option *opt) +{ + if (!opt) + return; + if (opt->name) + free(opt->name); + if (opt->description) + free(opt->description); + if (opt->icon_file) + free(opt->icon_file); + if (opt->boot_image_file) + free(opt->boot_image_file); + if (opt->initrd_file) + free(opt->initrd_file); + if (opt->boot_args) + free(opt->boot_args); + free(opt); +} + +static int write_action(int fd, enum device_action action) +{ + uint8_t action_buf = action; + return write(fd, &action_buf, sizeof(action_buf)) != sizeof(action_buf); +} + +static int write_string(int fd, const char *str) +{ + int len, pos = 0; + uint32_t len_buf; + + if (!str) { + len_buf = 0; + if (write(fd, &len_buf, sizeof(len_buf)) != sizeof(len_buf)) { + log("write failed: %s\n", strerror(errno)); + return -1; + } + return 0; + } + + len = strlen(str); + if (len > (1ull << (sizeof(len_buf) * 8 - 1))) { + log("string too large\n"); + return -1; + } + + len_buf = __cpu_to_be32(len); + if (write(fd, &len_buf, sizeof(len_buf)) != sizeof(len_buf)) { + log("write failed: %s\n", strerror(errno)); + return -1; + } + + while (pos < len) { + int rc = write(fd, str, len - pos); + if (rc <= 0) { + log("write failed: %s\n", strerror(errno)); + return -1; + } + pos += rc; + str += rc; + } + + return 0; +} + +int add_device(const struct device *dev) +{ + int rc; + + log("device added:\n"); + print_device(dev); + rc = write_action(sock, DEV_ACTION_ADD_DEVICE) || + write_string(sock, dev->id) || + write_string(sock, dev->name) || + write_string(sock, dev->description) || + write_string(sock, dev->icon_file); + + if (rc) + log("error writing device %s to socket\n", dev->name); + + return rc; +} + +int add_boot_option(const struct boot_option *opt) +{ + int rc; + + log("boot option added:\n"); + print_boot_option(opt); + + rc = write_action(sock, DEV_ACTION_ADD_OPTION) || + write_string(sock, opt->id) || + write_string(sock, opt->name) || + write_string(sock, opt->description) || + write_string(sock, opt->icon_file) || + write_string(sock, opt->boot_image_file) || + write_string(sock, opt->initrd_file) || + write_string(sock, opt->boot_args); + + if (rc) + log("error writing boot option %s to socket\n", opt->name); + + return rc; +} + +int remove_device(const char *dev_path) +{ + return write_action(sock, DEV_ACTION_REMOVE_DEVICE) || + write_string(sock, dev_path); +} + +int connect_to_socket() +{ +#if 1 + int fd; + struct sockaddr_un addr; + + fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + log("can't create socket: %s\n", strerror(errno)); + return -1; + } + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, socket_file); + + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr))) { + log("can't connect to %s: %s\n", + addr.sun_path, strerror(errno)); + return -1; + } + sock = fd; + + return 0; +#else + int fd; + fd = open("./debug_socket", O_WRONLY | O_CREAT, 0640); + if (fd < 0) { + log("can't create output file: %s\n", strerror(errno)); + return -1; + } + sock = fd; + return 0; +#endif +} + +#define template "mnt-XXXXXX" + +static int mount_device(const char *dev_path, char *mount_path) +{ + char *dir; + int pid, status, rc = -1; + + /* create a unique mountpoint */ + dir = malloc(strlen(tmp_dir) + 2 + strlen(template)); + sprintf(dir, "%s/%s", tmp_dir, template); + + if (!mkdtemp(dir)) { + log("failed to create temporary directory in %s: %s", + tmp_dir, strerror(errno)); + goto out; + } + + pid = fork(); + if (pid == -1) { + log("%s: fork failed: %s\n", __FUNCTION__, strerror(errno)); + goto out; + } + + if (pid == 0) { + execl(mount_bin, mount_bin, dev_path, dir, "-o", "ro", NULL); + exit(EXIT_FAILURE); + } + + if (waitpid(pid, &status, 0) == -1) { + log("%s: waitpid failed: %s\n", __FUNCTION__, strerror(errno)); + goto out; + } + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + strcpy(mount_path, dir); + rc = 0; + } + +out: + free(dir); + return rc; +} + +static int unmount_device(const char *dev_path) +{ + int pid, status, rc; + + pid = fork(); + + if (pid == -1) { + log("%s: fork failed: %s\n", __FUNCTION__, strerror(errno)); + return -1; + } + + if (pid == 0) { + execl(umount_bin, umount_bin, dev_path, NULL); + exit(EXIT_FAILURE); + } + + if (waitpid(pid, &status, 0) == -1) { + log("%s: waitpid failed: %s\n", __FUNCTION__, strerror(errno)); + return -1; + } + + rc = !WIFEXITED(status) || WEXITSTATUS(status) != 0; + + return rc; +} + +const char *generic_icon_file(enum generic_icon_type type) +{ + switch (type) { + case ICON_TYPE_DISK: + return "artwork/hdd.png"; + case ICON_TYPE_USB: + return "artwork/usbpen.png"; + case ICON_TYPE_OPTICAL: + return "artwork/cdrom.png"; + case ICON_TYPE_NETWORK: + case ICON_TYPE_UNKNOWN: + break; + } + return "artwork/hdd.png"; +} + +static const struct device fake_boot_devices[] = +{ + { + .id = "fakeDisk0", + .name = "Hard Disk", + .icon_file = "artwork/hdd.png", + }, + { + .id = "fakeDisk1", + .name = "PinkCat Linux CD", + .icon_file = "artwork/cdrom.png", + } +}; + +static const struct boot_option fake_boot_options[] = +{ + { + .id = "fakeBoot0", + .name = "Bloobuntu Linux", + .description = "Boot Bloobuntu Linux", + .icon_file = "artwork/hdd.png", + }, + { + .id = "fakeBoot1", + .name = "Pendora Gore 6", + .description = "Boot Pendora Gora 6", + .icon_file = "artwork/hdd.png", + }, + { + .id = "fakeBoot2", + .name = "Genfoo Minux", + .description = "Boot Genfoo Minux", + .icon_file = "artwork/hdd.png", + }, + { + .id = "fakeBoot3", + .name = "PinkCat Linux", + .description = "Install PinkCat Linux - Graphical install", + .icon_file = "artwork/cdrom.png", + }, +}; + +enum generic_icon_type guess_device_type(void) +{ + const char *bus = getenv("ID_BUS"); + if (streq(bus, "usb")) + return ICON_TYPE_USB; + if (streq(bus, "ata") || streq(bus, "scsi")) + return ICON_TYPE_DISK; + return ICON_TYPE_UNKNOWN; +} + +int main(int argc, char **argv) +{ + char mountpoint[PATH_MAX]; + char *dev_path, *action; + int rc; + + /*if (fork()) + return EXIT_SUCCESS; + */ + action = getenv("ACTION"); + + logf = stdout; + rc = EXIT_SUCCESS; + + if (!action) { + log("missing environment?\n"); + return EXIT_FAILURE; + } + + if (connect_to_socket()) + return EXIT_FAILURE; + + if (streq(action, "fake")) { + log("fake mode"); + + add_device(&fake_boot_devices[0]); + add_boot_option(&fake_boot_options[0]); + add_boot_option(&fake_boot_options[1]); + add_boot_option(&fake_boot_options[2]); + add_device(&fake_boot_devices[1]); + add_boot_option(&fake_boot_options[3]); + + return EXIT_SUCCESS; + } + + dev_path = getenv("DEVNAME"); + if (!dev_path) { + log("missing environment?\n"); + return EXIT_FAILURE; + } + + if (streq(action, "add")) { + if (mount_device(dev_path, mountpoint)) { + log("failed to mount %s\n", dev_path); + return EXIT_FAILURE; + } + + log("mounted %s at %s\n", dev_path, mountpoint); + + iterate_parsers(dev_path, mountpoint); + + } else if (streq(action, "remove")) { + log("%s removed\n", dev_path); + + remove_device(dev_path); + + unmount_device(dev_path); + + } else { + log("invalid action '%s'\n", action); + rc = EXIT_FAILURE; + } + return rc; +} diff --git a/devices/udev-helper.h b/devices/udev-helper.h new file mode 100644 index 0000000..ea5400c --- /dev/null +++ b/devices/udev-helper.h @@ -0,0 +1,27 @@ + +#include "message.h" + +int add_device(const struct device *dev); + +int add_boot_option(const struct boot_option *opt); +void free_boot_option(struct boot_option *opt); + +struct parser { + char *name; + int priority; + int (*parse)(const char *devicepath, const char *mountpoint); + struct parser *next; +}; + +enum generic_icon_type { + ICON_TYPE_DISK, + ICON_TYPE_USB, + ICON_TYPE_OPTICAL, + ICON_TYPE_NETWORK, + ICON_TYPE_UNKNOWN +}; + +enum generic_icon_type guess_device_type(void); +const char *generic_icon_file(enum generic_icon_type type); + +#define streq(a,b) (!strcasecmp((a),(b))) -- cgit v1.2.1