From: Jeremy Kerr Date: Mon, 15 Dec 2008 04:22:34 +0000 (+1100) Subject: Initial support for multiple UIs X-Git-Tag: v1.0.0~951 X-Git-Url: http://git.ozlabs.org/?p=petitboot;a=commitdiff_plain;h=32e6a41f33e5576716b351bd473a27939fe94fa1 Initial support for multiple UIs Move the device discovery code from separate udev helpers to a single process to listen on two sockets: one SOCK_DGRAM for incoming udev events, and one SOCK_STREAM for UIs to connect. Initial support for client/server infrastructure, still need to wire-up the udev messages. Signed-off-by: Jeremy Kerr --- diff --git a/Makefile.in b/Makefile.in index 0da5a4e..e537e9d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -24,6 +24,7 @@ sbindir = @sbindir@ datarootdir = @datarootdir@ datadir = @datadir@ pkgdatadir = ${datadir}/${PACKAGE} +builddir = @builddir@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ diff --git a/artwork/background.jpg b/artwork/background.jpg deleted file mode 100644 index 9d54d31..0000000 Binary files a/artwork/background.jpg and /dev/null differ diff --git a/artwork/cdrom.png b/artwork/cdrom.png deleted file mode 100644 index 8b3eeb6..0000000 Binary files a/artwork/cdrom.png and /dev/null differ diff --git a/artwork/cursor.gz b/artwork/cursor.gz deleted file mode 100644 index b73b72f..0000000 Binary files a/artwork/cursor.gz and /dev/null differ diff --git a/artwork/hdd.png b/artwork/hdd.png deleted file mode 100644 index 2b1594c..0000000 Binary files a/artwork/hdd.png and /dev/null differ diff --git a/artwork/tux.png b/artwork/tux.png deleted file mode 100644 index 99f3465..0000000 Binary files a/artwork/tux.png and /dev/null differ diff --git a/artwork/usbpen.png b/artwork/usbpen.png deleted file mode 100644 index 53a365e..0000000 Binary files a/artwork/usbpen.png and /dev/null differ diff --git a/configure.ac b/configure.ac index 5ef5aa0..2e85b26 100644 --- a/configure.ac +++ b/configure.ac @@ -30,6 +30,6 @@ AC_PROG_INSTALL PKG_CHECK_MODULES([twin], [libtwin]) -mkdir devices +mkdir -p discover ui/test ui/common lib/talloc lib/pb-protocol lib/list AC_OUTPUT diff --git a/devices.c b/devices.c deleted file mode 100644 index 77860d0..0000000 --- a/devices.c +++ /dev/null @@ -1,313 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "petitboot.h" -#include "petitboot-paths.h" -#include "devices/message.h" - -#define PBOOT_DEFAULT_ICON "tux.png" - -static const char *default_icon = artwork_pathname(PBOOT_DEFAULT_ICON); - -struct discovery_context { - /* nothing at present */ - int pad; -} _ctx; - -struct device_context { - struct discovery_context *discovery_ctx; - uint8_t action; - int device_idx; -}; - -static twin_pixmap_t *get_icon(const char *filename) -{ - /* todo: cache */ - twin_pixmap_t *icon; - - if (!filename) - filename = default_icon; - -retry: - LOG("loading icon %s ... ", filename); - icon = twin_png_to_pixmap(filename, TWIN_ARGB32); - LOG("%s\n", icon ? "ok" : "failed"); - - if (!icon && filename != default_icon) { - filename = default_icon; - LOG("reverting to default icon %s\n", filename); - goto retry; - } - - return icon; -} - -#define MAX_LEN 4096 -static char *read_string(int fd) -{ - int len; - uint32_t len_buf; - char *str, *pos; - - if (read(fd, &len_buf, sizeof(len_buf)) != sizeof(len_buf)) { - perror("read"); - return NULL; - } - - len = __be32_to_cpu(len_buf); - if (len + 1 > MAX_LEN) { - /* todo: truncate instead */ - return NULL; - } - - pos = str = malloc(len + 1); - - while (len) { - int rc = read(fd, pos, len); - if (rc <= 0) { - free(str); - LOG("read failed: %s\n", strerror(errno)); - return NULL; - } - pos += rc; - len -= rc; - } - *pos = '\0'; - - return str; -} - -static int _read_strings(int fd, void *ptr, int n_strings) -{ - char **strings = ptr; - int i, ret = TWIN_TRUE; - - for (i = 0; i < n_strings; i++) { - strings[i] = read_string(fd); - if (!strings[i]) { - ret = TWIN_FALSE; - break; - } - } - - if (!ret) - while (i-- > 0) { - free(strings[i]); - strings[i] = NULL; - } - - return ret; -} - -static void _free_strings(void *ptr, int n_strings) -{ - char **strings = ptr; - int i; - - for (i = 0; i < n_strings; i++) - free(strings[i]); - -} - -#define n_strings(x) (sizeof((x)) / sizeof(char *)) -#define read_strings(f,x) _read_strings((f), &(x), n_strings(x)) -#define free_strings(x) _free_strings(&(x), n_strings(x)) - -static int read_action(int fd, uint8_t *action) -{ - return read(fd, action, sizeof(*action)) != sizeof(*action); -} - -static int read_device(int fd, struct device_context *dev_ctx) -{ - /* name, description, icon_file */ - struct device dev; - twin_pixmap_t *icon; - int index = -1; - - if (!read_strings(fd, dev)) - return TWIN_FALSE; - - LOG("got device: '%s'\n", dev.name); - - icon = get_icon(dev.icon_file); - - if (!icon) - goto out; - - index = dev_ctx->device_idx = pboot_add_device(dev.id, dev.name, icon); - -out: - free_strings(dev); - - return index != -1; -} - -static int read_option(int fd, struct device_context *dev_ctx) -{ - struct boot_option *opt = malloc(sizeof(*opt)); - twin_pixmap_t *icon; - int index = -1; - - if (!opt) - return TWIN_FALSE; - - if (!read_strings(fd, (*opt))) - return TWIN_FALSE; - - LOG("got option: '%s'\n", opt->name); - icon = get_icon(opt->icon_file); - - if (icon) - index = pboot_add_option(dev_ctx->device_idx, opt->name, - opt->description, icon, opt); - - return index != -1; -} - -static twin_bool_t pboot_proc_client_sock(int sock, twin_file_op_t ops, - void *closure) -{ - struct device_context *dev_ctx = closure; - uint8_t action; - - if (read_action(sock, &action)) - goto out_err; - - if (action == DEV_ACTION_ADD_DEVICE) { - if (!read_device(sock, dev_ctx)) - goto out_err; - - } else if (action == DEV_ACTION_ADD_OPTION) { - if (dev_ctx->device_idx == -1) { - LOG("option, but no device has been sent?\n"); - goto out_err; - } - - if (!read_option(sock, dev_ctx)) - goto out_err; - - } else if (action == DEV_ACTION_REMOVE_DEVICE) { - char *dev_id = read_string(sock); - if (!dev_id) - goto out_err; - - LOG("remove device %s\n", dev_id); - pboot_remove_device(dev_id); - - } else { - LOG("unsupported action %d\n", action); - goto out_err; - } - - return TWIN_TRUE; - -out_err: - close(sock); - return TWIN_FALSE; -} - -static twin_bool_t pboot_proc_server_sock(int sock, twin_file_op_t ops, - void *closure) -{ - int fd; - struct discovery_context *disc_ctx = closure; - struct device_context *dev_ctx; - - fd = accept(sock, NULL, 0); - if (fd < 0) { - LOG("accept failed: %s", strerror(errno)); - return TWIN_FALSE; - } - - dev_ctx = malloc(sizeof(*dev_ctx)); - dev_ctx->discovery_ctx = disc_ctx; - dev_ctx->device_idx = -1; - dev_ctx->action = 0xff; - - twin_set_file(pboot_proc_client_sock, fd, TWIN_READ, dev_ctx); - - return TWIN_TRUE; -} - -int pboot_start_device_discovery(int udev_trigger) -{ - int sock; - struct sockaddr_un addr; - - unlink(PBOOT_DEVICE_SOCKET); - - sock = socket(PF_UNIX, SOCK_STREAM, 0); - if (sock < 0) { - perror("socket"); - return TWIN_FALSE; - } - - addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, PBOOT_DEVICE_SOCKET); - - if (bind(sock, (struct sockaddr *)&addr, sizeof(addr))) { - LOG("can't bind to %s: %s", addr.sun_path, strerror(errno)); - return TWIN_FALSE; - } - - if (listen(sock, 1)) { - LOG("can't listen on socket %s: %s", - addr.sun_path, strerror(errno)); - return TWIN_FALSE; - } - - LOG("listening on %s\n", addr.sun_path); - - twin_set_file(pboot_proc_server_sock, sock, TWIN_READ, &_ctx); - - if (udev_trigger) { - int rc = system("udevtrigger"); - if (rc) - LOG("udevtrigger failed, rc %d\n", rc); - } - - return TWIN_TRUE; -} - -void pboot_exec_option(void *data) -{ - struct boot_option *opt = data; - char *kexec_opts[10]; - int i, nr_opts = 2; - - kexec_opts[0] = "/usr/sbin/kexec"; - kexec_opts[1] = "-f"; - if (opt->initrd_file && *opt->initrd_file) { - kexec_opts[nr_opts] = malloc(10 + strlen(opt->initrd_file)); - sprintf(kexec_opts[nr_opts], "--initrd=%s", opt->initrd_file); - nr_opts++; - } - if (opt->boot_args && *opt->boot_args) { - kexec_opts[nr_opts] = malloc(10 + strlen(opt->boot_args)); - sprintf(kexec_opts[nr_opts], "--command-line=%s", - opt->boot_args); - nr_opts++; - } - - kexec_opts[nr_opts++] = opt->boot_image_file; - kexec_opts[nr_opts] = NULL; - - LOG("calling kexec:\n"); - for (i = 0; i < nr_opts; i++) { - LOG("\t'%s'\n", kexec_opts[i]); - } - fflush(stdout); - - execv(kexec_opts[0], kexec_opts); - pboot_message("kexec failed: %s", strerror(errno)); - LOG("execv() failed: %s", strerror(errno)); -} diff --git a/devices/kboot-parser.c b/devices/kboot-parser.c deleted file mode 100644 index df2e762..0000000 --- a/devices/kboot-parser.c +++ /dev/null @@ -1,288 +0,0 @@ -#define _GNU_SOURCE - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "parser.h" -#include "params.h" - -#define buf_size 1024 - -static const char *devpath; - -static int param_is_ignored(const char *param) -{ - static const char *ignored_options[] = - { "message", "timeout", "default", NULL }; - const char **str; - - for (str = ignored_options; *str; str++) - if (streq(*str, param)) - return 1; - return 0; -} - -/** - * Splits a name=value pair, with value terminated by @term (or nul). if there - * is no '=', then only the value is populated, and *name is set to NULL. The - * string is modified in place. - * - * Returns the next byte to process, or null if we've hit the end of the - * string. - * - * */ -static char *get_param_pair(char *str, char **name_out, char **value_out, - char terminator) -{ - char *sep, *tmp, *name, *value; - - /* terminate the value */ - tmp = strchr(str, terminator); - if (tmp) - *tmp = 0; - else - tmp = NULL; - - sep = strchr(str, '='); - if (!sep) { - *name_out = NULL; - *value_out = str; - return tmp ? tmp + 1 : NULL; - } - - /* terminate the name */ - *sep = 0; - - /* remove leading spaces */ - for (name = str; isspace(*name); name++); - for (value = sep + 1; isspace(*value); value++); - - /* .. and trailing ones.. */ - for (sep--; isspace(*sep); sep--) - *sep = 0; - for (sep = value + strlen(value) - 1; isspace(*sep); sep--) - *sep = 0; - - *name_out = name; - *value_out = value; - - return tmp ? tmp + 1 : NULL; -} - -struct global_option { - char *name; - char *value; -}; - - -static struct global_option global_options[] = { - { .name = "root" }, - { .name = "initrd" }, - { .name = "video" }, - { .name = NULL } -}; - -/* - * Check if an option (name=value) is a global option. If so, store it in - * the global options table, and return 1. Otherwise, return 0. - */ -static int check_for_global_option(const char *name, const char *value) -{ - int i; - - for (i = 0; global_options[i].name ;i++) { - if (!strcmp(name, global_options[i].name)) { - global_options[i].value = strdup(value); - return 1; - } - } - return 0; -} - -static char *get_global_option(const char *name) -{ - int i; - - for (i = 0; global_options[i].name ;i++) - if (!strcmp(name, global_options[i].name)) - return global_options[i].value; - - return NULL; -} - -static int parse_option(struct boot_option *opt, char *config) -{ - char *pos, *name, *value, *root, *initrd, *cmdline, *tmp; - - root = initrd = cmdline = NULL; - - /* remove quotes around the value */ - while (*config == '"' || *config == '\'') - config++; - - pos = config + strlen(config) - 1; - while (*pos == '"' || *pos == '\'') - *(pos--) = 0; - - if (!strlen(pos)) - return 0; - - pos = strchr(config, ' '); - - /* if there's no space, it's only a kernel image with no params */ - if (!pos) { - opt->boot_image_file = resolve_path(config, devpath); - opt->description = strdup(config); - return 1; - } - - *pos = 0; - opt->boot_image_file = resolve_path(config, devpath); - - cmdline = malloc(buf_size); - *cmdline = 0; - - for (pos++; pos;) { - pos = get_param_pair(pos, &name, &value, ' '); - - if (!name) { - strcat(cmdline, " "); - strcat(cmdline, value); - - } else if (streq(name, "initrd")) { - initrd = value; - - } else if (streq(name, "root")) { - root = value; - - } else { - strcat(cmdline, " "); - *(value - 1) = '='; - strcat(cmdline, name); - } - } - - if (!root) - root = get_global_option("root"); - if (!initrd) - initrd = get_global_option("initrd"); - - if (initrd) { - asprintf(&tmp, "initrd=%s %s", initrd, cmdline); - free(cmdline); - cmdline = tmp; - - opt->initrd_file = resolve_path(initrd, devpath); - } - - if (root) { - asprintf(&tmp, "root=%s %s", root, cmdline); - free(cmdline); - cmdline = tmp; - - } else if (initrd) { - /* if there's an initrd but no root, fake up /dev/ram0 */ - asprintf(&tmp, "root=/dev/ram0 %s", cmdline); - free(cmdline); - cmdline = tmp; - } - - pb_log("kboot cmdline: %s\n", cmdline); - opt->boot_args = cmdline; - - asprintf(&opt->description, "%s %s", - config, opt->boot_args); - - return 1; -} - -static void parse_buf(struct device *dev, char *buf) -{ - char *pos, *name, *value; - int sent_device = 0; - - for (pos = buf; pos;) { - struct boot_option opt; - - pos = get_param_pair(pos, &name, &value, '\n'); - - pb_log("kboot param: '%s' = '%s'\n", name, value); - - if (name == NULL || param_is_ignored(name)) - continue; - - if (*name == '#') - continue; - - if (check_for_global_option(name, value)) - continue; - - memset(&opt, 0, sizeof(opt)); - opt.name = strdup(name); - - if (parse_option(&opt, value)) - if (!sent_device++) - add_device(dev); - add_boot_option(&opt); - - free(opt.name); - } -} - -static int parse(const char *device) -{ - char *filepath, *buf; - int fd, len, rc = 0; - struct stat stat; - struct device *dev; - - devpath = device; - - filepath = resolve_path("/etc/kboot.conf", devpath); - - fd = open(filepath, O_RDONLY); - if (fd < 0) - goto out_free_path; - - if (fstat(fd, &stat)) - goto out_close; - - buf = malloc(stat.st_size + 1); - if (!buf) - goto out_close;; - - len = read(fd, buf, stat.st_size); - if (len < 0) - goto out_free_buf; - buf[len] = 0; - - dev = malloc(sizeof(*dev)); - memset(dev, 0, sizeof(*dev)); - dev->id = strdup(device); - dev->icon_file = strdup(generic_icon_file(guess_device_type())); - - parse_buf(dev, buf); - - rc = 1; - -out_free_buf: - free(buf); -out_close: - close(fd); -out_free_path: - free(filepath); - return rc; -} - -struct parser kboot_parser = { - .name = "kboot.conf parser", - .priority = 98, - .parse = parse -}; diff --git a/devices/message.h b/devices/message.h deleted file mode 100644 index 7a5d4f2..0000000 --- a/devices/message.h +++ /dev/null @@ -1,30 +0,0 @@ - -#ifndef _MESSAGE_H -#define _MESSAGE_H - -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; -}; - - -#endif /* _MESSAGE_H */ diff --git a/devices/native-parser.c b/devices/native-parser.c deleted file mode 100644 index 24713b1..0000000 --- a/devices/native-parser.c +++ /dev/null @@ -1,124 +0,0 @@ - -#include "parser.h" -#include "params.h" -#include "paths.h" - -#include -#include -#include - -const char *conf_filename = "/boot/petitboot.conf"; - -static struct boot_option *cur_opt; -static struct device *dev; -static const char *devpath; -int device_added; - -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 = resolve_path(value, devpath); - - else if (streq(name, "icon")) - opt->icon_file = resolve_path(value, devpath); - - else if (streq(name, "initrd")) - opt->initrd_file =resolve_path(value, devpath); - - 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 = resolve_path(value, devpath); -} - -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 *device) -{ - char *filepath; - int rc; - - filepath = resolve_path(conf_filename, device); - - cur_opt = NULL; - dev = malloc(sizeof(*dev)); - memset(dev, 0, sizeof(*dev)); - dev->id = strdup(device); - - rc = pm_process(filepath, section, parameter); - if (!rc) - return 0; - - 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 deleted file mode 100644 index 76a1451..0000000 --- a/devices/params.c +++ /dev/null @@ -1,595 +0,0 @@ -/* 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 deleted file mode 100644 index 02a39c9..0000000 --- a/devices/params.h +++ /dev/null @@ -1,6 +0,0 @@ - -#define BOOL int - -BOOL pm_process( char *FileName, - BOOL (*sfunc)(char *), - BOOL (*pfunc)(char *, char *) ); diff --git a/devices/parser-test.c b/devices/parser-test.c deleted file mode 100644 index 8c94d3f..0000000 --- a/devices/parser-test.c +++ /dev/null @@ -1,85 +0,0 @@ -#define _GNU_SOURCE - -#include -#include -#include -#include -#include - -#include "parser.h" -#include "paths.h" - -void pb_log(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); -} - -int mount_device(const char *dev_path) -{ - printf("[mount] %s\n", dev_path); - return 0; -} - -static int device_idx; -static int option_idx; - -int add_device(const struct device *dev) -{ - printf("[dev %2d] id: %s\n", device_idx, dev->id); - printf("[dev %2d] name: %s\n", device_idx, dev->name); - printf("[dev %2d] description: %s\n", device_idx, dev->description); - printf("[dev %2d] boot_image: %s\n", device_idx, dev->icon_file); - - device_idx++; - option_idx = 0; - return 0; -} - - -int add_boot_option(const struct boot_option *opt) -{ - if (!device_idx) { - fprintf(stderr, "Option (%s) added before device\n", - opt->name); - exit(EXIT_FAILURE); - } - - printf("[opt %2d] name: %s\n", option_idx, opt->name); - printf("[opt %2d] description: %s\n", option_idx, opt->description); - printf("[opt %2d] boot_image: %s\n", option_idx, opt->boot_image_file); - printf("[opt %2d] initrd: %s\n", option_idx, opt->initrd_file); - printf("[opt %2d] boot_args: %s\n", option_idx, opt->boot_args); - - option_idx++; - - return 0; -} - -enum generic_icon_type guess_device_type(void) -{ - return ICON_TYPE_UNKNOWN; -} - -int main(int argc, char **argv) -{ - char *mountpoint, *dev; - - if (argc != 3) { - fprintf(stderr, "usage: %s \n", argv[0]); - return EXIT_FAILURE; - } - - mountpoint = argv[1]; - dev = argv[2]; - - set_mount_base(mountpoint); - - iterate_parsers(dev, mountpoint); - - - return EXIT_SUCCESS; -} diff --git a/devices/parser-test.sh b/devices/parser-test.sh deleted file mode 100755 index 140601e..0000000 --- a/devices/parser-test.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -testdir=devices/parser-tests -default_rootdev=ps3da1 - -function test_dir() -{ - dir="$1" - rootdev=$default_rootdev - if [ -e "$dir/rootdev" ] - then - rootdev=$(cat "$dir/rootdev") - fi - ./parser-test "$dir" /dev/$rootdev 2>/dev/null | - diff -u "$dir/expected-output" - -} - -set -ex - -for test in $testdir/* -do - echo $test - test_dir "$test" -done - -echo "All tests passed" diff --git a/devices/parser-tests/001/expected-output b/devices/parser-tests/001/expected-output deleted file mode 100644 index bace9f7..0000000 --- a/devices/parser-tests/001/expected-output +++ /dev/null @@ -1,24 +0,0 @@ -[dev 0] id: /dev/ps3da1 -[dev 0] name: (null) -[dev 0] description: (null) -[dev 0] boot_image: /usr/share/petitboot/artwork/hdd.png -[opt 0] name: live -[opt 0] description: /casper/vmlinux root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet splash -- -[opt 0] boot_image: devices/parser-tests/001/ps3da1/casper/vmlinux -[opt 0] initrd: devices/parser-tests/001/ps3da1/casper/initrd.gz -[opt 0] boot_args: root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet splash -- -[opt 1] name: live_nosplash -[opt 1] description: /casper/vmlinux root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet -- -[opt 1] boot_image: devices/parser-tests/001/ps3da1/casper/vmlinux -[opt 1] initrd: devices/parser-tests/001/ps3da1/casper/initrd.gz -[opt 1] boot_args: root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet -- -[opt 2] name: driverupdates -[opt 2] description: /casper/vmlinux root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper debian-installer/driver-update=true quiet splash -- -[opt 2] boot_image: devices/parser-tests/001/ps3da1/casper/vmlinux -[opt 2] initrd: devices/parser-tests/001/ps3da1/casper/initrd.gz -[opt 2] boot_args: root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper debian-installer/driver-update=true quiet splash -- -[opt 3] name: check -[opt 3] description: /casper/vmlinux root=/dev/ram0 initrd=/casper/initrd.gz boot=casper integrity-check quiet splash -- -[opt 3] boot_image: devices/parser-tests/001/ps3da1/casper/vmlinux -[opt 3] initrd: devices/parser-tests/001/ps3da1/casper/initrd.gz -[opt 3] boot_args: root=/dev/ram0 initrd=/casper/initrd.gz boot=casper integrity-check quiet splash -- diff --git a/devices/parser-tests/001/ps3da1/etc/kboot.conf b/devices/parser-tests/001/ps3da1/etc/kboot.conf deleted file mode 100644 index 591c51b..0000000 --- a/devices/parser-tests/001/ps3da1/etc/kboot.conf +++ /dev/null @@ -1,9 +0,0 @@ -# Ubuntu feisty kboot.conf -message=/etc/kboot.msg -timeout=300 -default=live -live='/casper/vmlinux initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet splash --' -live_nosplash='/casper/vmlinux initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet --' -driverupdates='/casper/vmlinux initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper debian-installer/driver-update=true quiet splash --' -check='/casper/vmlinux initrd=/casper/initrd.gz boot=casper integrity-check quiet splash --' - diff --git a/devices/parser-tests/002/expected-output b/devices/parser-tests/002/expected-output deleted file mode 100644 index 304f15c..0000000 --- a/devices/parser-tests/002/expected-output +++ /dev/null @@ -1,9 +0,0 @@ -[dev 0] id: /dev/ps3da1 -[dev 0] name: (null) -[dev 0] description: -[dev 0] boot_image: /usr/share/petitboot/artwork/hdd.png -[opt 0] name: linux -[opt 0] description: (null) -[opt 0] boot_image: devices/parser-tests/002/ps3da1/ppc/ppc64/vmlinux -[opt 0] initrd: devices/parser-tests/002/ps3da1/ppc/ppc64/ramdisk.image.gz -[opt 0] boot_args: ro diff --git a/devices/parser-tests/002/ps3da1/etc/yaboot.conf b/devices/parser-tests/002/ps3da1/etc/yaboot.conf deleted file mode 100644 index f13b1b3..0000000 --- a/devices/parser-tests/002/ps3da1/etc/yaboot.conf +++ /dev/null @@ -1,8 +0,0 @@ -init-message = "\nWelcome to the 64-bit Yellow Dog Linux 5.0 installer!\nHit for boot options.\n\n" -timeout=6000 -default=linux - -image=/ppc/ppc64/vmlinux - label=linux - initrd=/ppc/ppc64/ramdisk.image.gz - read-only diff --git a/devices/parser-tests/003/expected-output b/devices/parser-tests/003/expected-output deleted file mode 100644 index 4f60310..0000000 --- a/devices/parser-tests/003/expected-output +++ /dev/null @@ -1,9 +0,0 @@ -[dev 0] id: /dev/ps3da1 -[dev 0] name: (null) -[dev 0] description: (null) -[dev 0] boot_image: /usr/share/petitboot/artwork/hdd.png -[opt 0] name: test -[opt 0] description: /dev/sda1:/vmlinux -[opt 0] boot_image: devices/parser-tests/003/ps3da1/vmlinux -[opt 0] initrd: (null) -[opt 0] boot_args: (null) diff --git a/devices/parser-tests/003/ps3da1/etc/kboot.conf b/devices/parser-tests/003/ps3da1/etc/kboot.conf deleted file mode 100644 index a7bb199..0000000 --- a/devices/parser-tests/003/ps3da1/etc/kboot.conf +++ /dev/null @@ -1,4 +0,0 @@ -# test remapping sda to ps3da, when mounted from a ps3da device - -test='/dev/sda1:/vmlinux' - diff --git a/devices/parser-tests/004/expected-output b/devices/parser-tests/004/expected-output deleted file mode 100644 index 76a90a2..0000000 --- a/devices/parser-tests/004/expected-output +++ /dev/null @@ -1,9 +0,0 @@ -[dev 0] id: /dev/sda1 -[dev 0] name: (null) -[dev 0] description: (null) -[dev 0] boot_image: /usr/share/petitboot/artwork/hdd.png -[opt 0] name: test -[opt 0] description: /dev/sda1:/vmlinux -[opt 0] boot_image: devices/parser-tests/004/sda1/vmlinux -[opt 0] initrd: (null) -[opt 0] boot_args: (null) diff --git a/devices/parser-tests/004/rootdev b/devices/parser-tests/004/rootdev deleted file mode 100644 index 36cfa0d..0000000 --- a/devices/parser-tests/004/rootdev +++ /dev/null @@ -1 +0,0 @@ -sda1 diff --git a/devices/parser-tests/004/sda1/etc/kboot.conf b/devices/parser-tests/004/sda1/etc/kboot.conf deleted file mode 100644 index 9755f77..0000000 --- a/devices/parser-tests/004/sda1/etc/kboot.conf +++ /dev/null @@ -1,4 +0,0 @@ -# test remapping sda to ps3da, when mounted from a plain sd device - -test='/dev/sda1:/vmlinux' - diff --git a/devices/parser-tests/005/expected-output b/devices/parser-tests/005/expected-output deleted file mode 100644 index bfaccc8..0000000 --- a/devices/parser-tests/005/expected-output +++ /dev/null @@ -1,14 +0,0 @@ -[dev 0] id: /dev/ps3da1 -[dev 0] name: (null) -[dev 0] description: (null) -[dev 0] boot_image: /usr/share/petitboot/artwork/hdd.png -[opt 0] name: test_uuid -[opt 0] description: UUID=meep:/vmlinux -[opt 0] boot_image: devices/parser-tests/005/disk/by-uuid/meep/vmlinux -[opt 0] initrd: (null) -[opt 0] boot_args: (null) -[opt 1] name: test_label -[opt 1] description: LABEL=meep:/vmlinux -[opt 1] boot_image: devices/parser-tests/005/disk/by-label/meep/vmlinux -[opt 1] initrd: (null) -[opt 1] boot_args: (null) diff --git a/devices/parser-tests/005/ps3da1/etc/kboot.conf b/devices/parser-tests/005/ps3da1/etc/kboot.conf deleted file mode 100644 index 72b7db8..0000000 --- a/devices/parser-tests/005/ps3da1/etc/kboot.conf +++ /dev/null @@ -1,6 +0,0 @@ - -# test for LABEL= and UUID= lookups - -test_uuid='UUID=meep:/vmlinux' -test_label='LABEL=meep:/vmlinux' - diff --git a/devices/parser-tests/101/expected-output b/devices/parser-tests/101/expected-output deleted file mode 100644 index 45d99a1..0000000 --- a/devices/parser-tests/101/expected-output +++ /dev/null @@ -1,24 +0,0 @@ -[dev 0] id: /dev/ps3da1 -[dev 0] name: (null) -[dev 0] description: (null) -[dev 0] boot_image: /usr/share/petitboot/artwork/hdd.png -[opt 0] name: ydl -[opt 0] description: /dev/sda1:/vmlinux-2.6.22-0.ydl.rc4 root=LABEL=/ initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img init=/sbin/init video=ps3fb:mode:3 rhgb -[opt 0] boot_image: devices/parser-tests/101/ps3da1/vmlinux-2.6.22-0.ydl.rc4 -[opt 0] initrd: devices/parser-tests/101/ps3da1/initrd-2.6.22-0.ydl.rc4.img -[opt 0] boot_args: root=LABEL=/ initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img init=/sbin/init video=ps3fb:mode:3 rhgb -[opt 1] name: ydl480i -[opt 1] description: /dev/sda1:/vmlinux-2.6.22-0.ydl.rc4 root=LABEL=/ initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img init=/sbin/init video=ps3fb:mode:1 rhgb -[opt 1] boot_image: devices/parser-tests/101/ps3da1/vmlinux-2.6.22-0.ydl.rc4 -[opt 1] initrd: devices/parser-tests/101/ps3da1/initrd-2.6.22-0.ydl.rc4.img -[opt 1] boot_args: root=LABEL=/ initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img init=/sbin/init video=ps3fb:mode:1 rhgb -[opt 2] name: ydl1080i -[opt 2] description: /dev/sda1:/vmlinux-2.6.22-0.ydl.rc4 root=LABEL=/ initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img init=/sbin/init video=ps3fb:mode:4 rhgb -[opt 2] boot_image: devices/parser-tests/101/ps3da1/vmlinux-2.6.22-0.ydl.rc4 -[opt 2] initrd: devices/parser-tests/101/ps3da1/initrd-2.6.22-0.ydl.rc4.img -[opt 2] boot_args: root=LABEL=/ initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img init=/sbin/init video=ps3fb:mode:4 rhgb -[opt 3] name: ydltext -[opt 3] description: /dev/sda1:/vmlinux-2.6.22-0.ydl.rc4 root=LABEL=/ initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img init=/sbin/init 3 -[opt 3] boot_image: devices/parser-tests/101/ps3da1/vmlinux-2.6.22-0.ydl.rc4 -[opt 3] initrd: devices/parser-tests/101/ps3da1/initrd-2.6.22-0.ydl.rc4.img -[opt 3] boot_args: root=LABEL=/ initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img init=/sbin/init 3 diff --git a/devices/parser-tests/101/ps3da1/etc/kboot.conf b/devices/parser-tests/101/ps3da1/etc/kboot.conf deleted file mode 100644 index 4a986c0..0000000 --- a/devices/parser-tests/101/ps3da1/etc/kboot.conf +++ /dev/null @@ -1,12 +0,0 @@ -# kboot.conf for ydl - -default=ydl -timeout=10 -root=/dev/sda1 -ydl='/dev/sda1:/vmlinux-2.6.22-0.ydl.rc4 initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img root=LABEL=/ init=/sbin/init video=ps3fb:mode:3 rhgb' -ydl480i='/dev/sda1:/vmlinux-2.6.22-0.ydl.rc4 initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img root=LABEL=/ init=/sbin/init video=ps3fb:mode:1 rhgb' -ydl1080i='/dev/sda1:/vmlinux-2.6.22-0.ydl.rc4 initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img root=LABEL=/ init=/sbin/init video=ps3fb:mode:4 rhgb' -ydltext='/dev/sda1:/vmlinux-2.6.22-0.ydl.rc4 initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img root=LABEL=/ init=/sbin/init 3' - - - diff --git a/devices/parser-tests/102/expected-output b/devices/parser-tests/102/expected-output deleted file mode 100644 index cc0d096..0000000 --- a/devices/parser-tests/102/expected-output +++ /dev/null @@ -1,24 +0,0 @@ -[dev 0] id: /dev/ps3da1 -[dev 0] name: (null) -[dev 0] description: (null) -[dev 0] boot_image: /usr/share/petitboot/artwork/hdd.png -[opt 0] name: live -[opt 0] description: /casper/vmlinux root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet splash -- -[opt 0] boot_image: devices/parser-tests/102/ps3da1/casper/vmlinux -[opt 0] initrd: devices/parser-tests/102/ps3da1/casper/initrd.gz -[opt 0] boot_args: root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet splash -- -[opt 1] name: live_nosplash -[opt 1] description: /casper/vmlinux root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet -- -[opt 1] boot_image: devices/parser-tests/102/ps3da1/casper/vmlinux -[opt 1] initrd: devices/parser-tests/102/ps3da1/casper/initrd.gz -[opt 1] boot_args: root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet -- -[opt 2] name: driverupdates -[opt 2] description: /casper/vmlinux root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper debian-installer/driver-update=true quiet splash -- -[opt 2] boot_image: devices/parser-tests/102/ps3da1/casper/vmlinux -[opt 2] initrd: devices/parser-tests/102/ps3da1/casper/initrd.gz -[opt 2] boot_args: root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper debian-installer/driver-update=true quiet splash -- -[opt 3] name: check -[opt 3] description: /casper/vmlinux root=/dev/ram0 initrd=/casper/initrd.gz boot=casper integrity-check quiet splash -- -[opt 3] boot_image: devices/parser-tests/102/ps3da1/casper/vmlinux -[opt 3] initrd: devices/parser-tests/102/ps3da1/casper/initrd.gz -[opt 3] boot_args: root=/dev/ram0 initrd=/casper/initrd.gz boot=casper integrity-check quiet splash -- diff --git a/devices/parser-tests/102/ps3da1/etc/kboot.conf b/devices/parser-tests/102/ps3da1/etc/kboot.conf deleted file mode 100644 index 591c51b..0000000 --- a/devices/parser-tests/102/ps3da1/etc/kboot.conf +++ /dev/null @@ -1,9 +0,0 @@ -# Ubuntu feisty kboot.conf -message=/etc/kboot.msg -timeout=300 -default=live -live='/casper/vmlinux initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet splash --' -live_nosplash='/casper/vmlinux initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet --' -driverupdates='/casper/vmlinux initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper debian-installer/driver-update=true quiet splash --' -check='/casper/vmlinux initrd=/casper/initrd.gz boot=casper integrity-check quiet splash --' - diff --git a/devices/parser.c b/devices/parser.c deleted file mode 100644 index 5e50dcb..0000000 --- a/devices/parser.c +++ /dev/null @@ -1,85 +0,0 @@ - -#include -#include -#include - -#include "parser.h" - -extern struct parser native_parser; -extern struct parser yaboot_parser; -extern struct parser kboot_parser; - -/* array of parsers, ordered by priority */ -static struct parser *parsers[] = { - &native_parser, - &yaboot_parser, - &kboot_parser, - NULL -}; - -void iterate_parsers(const char *devpath, const char *mountpoint) -{ - int i; - - pb_log("trying parsers for %s\n", devpath); - - for (i = 0; parsers[i]; i++) { - pb_log("\ttrying parser '%s'\n", parsers[i]->name); - /* just use a dummy device path for now */ - if (parsers[i]->parse(devpath)) - return; - } - pb_log("\tno boot_options found\n"); -} - -/* convenience functions for parsers */ -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); -} - -const char *generic_icon_file(enum generic_icon_type type) -{ - switch (type) { - case ICON_TYPE_DISK: - return artwork_pathname("hdd.png"); - case ICON_TYPE_USB: - return artwork_pathname("usbpen.png"); - case ICON_TYPE_OPTICAL: - return artwork_pathname("cdrom.png"); - case ICON_TYPE_NETWORK: - case ICON_TYPE_UNKNOWN: - break; - } - return artwork_pathname("hdd.png"); -} - diff --git a/devices/parser.h b/devices/parser.h deleted file mode 100644 index 9c6fb35..0000000 --- a/devices/parser.h +++ /dev/null @@ -1,46 +0,0 @@ - -#ifndef _PARSERS_H -#define _PARSERS_H - -#include -#include "message.h" - -struct parser { - char *name; - int priority; - int (*parse)(const char *device); - struct parser *next; -}; - -enum generic_icon_type { - ICON_TYPE_DISK, - ICON_TYPE_USB, - ICON_TYPE_OPTICAL, - ICON_TYPE_NETWORK, - ICON_TYPE_UNKNOWN -}; - -#define streq(a,b) (!strcasecmp((a),(b))) - -/* general functions provided by parsers.c */ -void iterate_parsers(const char *devpath, const char *mountpoint); - -void free_device(struct device *dev); -void free_boot_option(struct boot_option *opt); - -const char *generic_icon_file(enum generic_icon_type type); - -/* functions provided by udev-helper or the test wrapper */ -void pb_log(const char *fmt, ...); - -int mount_device(const char *dev_path); - -char *resolve_path(const char *path, const char *current_dev); -const char *mountpoint_for_device(const char *dev_path); - -enum generic_icon_type guess_device_type(void); - -int add_device(const struct device *dev); -int add_boot_option(const struct boot_option *opt); - -#endif /* _PARSERS_H */ diff --git a/devices/paths.c b/devices/paths.c deleted file mode 100644 index 2373c28..0000000 --- a/devices/paths.c +++ /dev/null @@ -1,141 +0,0 @@ -#define _GNU_SOURCE - -#include -#include -#include - -#include "paths.h" - -static char *mount_base; - -struct device_map { - char *dev, *mnt; -}; - -#define DEVICE_MAP_SIZE 32 -static struct device_map device_map[DEVICE_MAP_SIZE]; - -char *encode_label(const char *label) -{ - char *str, *c; - int i; - - /* the label can be expanded by up to four times */ - str = malloc(strlen(label) * 4 + 1); - c = str; - - for (i = 0; i < strlen(label); i++) { - - if (label[i] == '/' || label[i] == '\\') { - sprintf(c, "\\x%02x", label[i]); - c += 4; - continue; - } - - *(c++) = label[i]; - } - - *c = '\0'; - - return str; -} - -char *parse_device_path(const char *dev_str, const char *cur_dev) -{ - char *dev, tmp[256], *enc; - - if (!strncasecmp(dev_str, "uuid=", 5)) { - asprintf(&dev, "/dev/disk/by-uuid/%s", dev_str + 5); - return dev; - } - - if (!strncasecmp(dev_str, "label=", 6)) { - enc = encode_label(dev_str + 6); - asprintf(&dev, "/dev/disk/by-label/%s", enc); - free(enc); - return dev; - } - - /* normalise '/dev/foo' to 'foo' for easy comparisons, we'll expand - * back before returning. - */ - if (!strncmp(dev_str, "/dev/", 5)) - dev_str += 5; - - /* PS3 hack: if we're reading from a ps3dx device, and we refer to - * a sdx device, remap to ps3dx */ - if (cur_dev && !strncmp(cur_dev, "/dev/ps3d", 9) - && !strncmp(dev_str, "sd", 2)) { - snprintf(tmp, 255, "ps3d%s", dev_str + 2); - dev_str = tmp; - } - - return join_paths("/dev", dev_str); -} - -const char *mountpoint_for_device(const char *dev) -{ - int i; - - if (!strncmp(dev, "/dev/", 5)) - dev += 5; - - /* check existing entries in the map */ - for (i = 0; (i < DEVICE_MAP_SIZE) && device_map[i].dev; i++) - if (!strcmp(device_map[i].dev, dev)) - return device_map[i].mnt; - - if (i == DEVICE_MAP_SIZE) - return NULL; - - device_map[i].dev = strdup(dev); - device_map[i].mnt = join_paths(mount_base, dev); - return device_map[i].mnt; -} - -char *resolve_path(const char *path, const char *current_dev) -{ - char *ret; - const char *devpath, *sep; - - sep = strchr(path, ':'); - if (!sep) { - devpath = mountpoint_for_device(current_dev); - ret = join_paths(devpath, path); - } else { - /* parse just the device name into dev */ - char *tmp, *dev; - tmp = strndup(path, sep - path); - dev = parse_device_path(tmp, current_dev); - - devpath = mountpoint_for_device(dev); - ret = join_paths(devpath, sep + 1); - - free(dev); - free(tmp); - } - - return ret; -} - -void set_mount_base(const char *path) -{ - if (mount_base) - free(mount_base); - mount_base = strdup(path); -} - -char *join_paths(const char *a, const char *b) -{ - char *full_path; - - full_path = malloc(strlen(a) + strlen(b) + 2); - - strcpy(full_path, a); - if (b[0] != '/' && a[strlen(a) - 1] != '/') - strcat(full_path, "/"); - strcat(full_path, b); - - return full_path; -} - diff --git a/devices/paths.h b/devices/paths.h deleted file mode 100644 index 26d4ce4..0000000 --- a/devices/paths.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef PATHS_H -#define PATHS_H - -/** - * Given a string (eg /dev/sda1, sda1 or UUID=B8E53381CA9EA0E3), parse the - * device path (eg /dev/sda1). Any device descriptions read from config files - * should be parsed into the path first. - * - * The cur_dev is provided for some remapping situations. If NULL is provided, - * no remapping will be done. - * - * Returns a newly-allocated string. - */ -char *parse_device_path(const char *dev_str, const char *current_device); - -/** - * Get the mountpoint for a device. - */ -const char *mountpoint_for_device(const char *dev); - -/** - * Resolve a path given in a config file, to a path in the local filesystem. - * Paths may be of the form: - * device:path (eg /dev/sda:/boot/vmlinux) - * - * or just a path: - * /boot/vmlinux - * - in this case, the current mountpoint is used. - * - * Returns a newly-allocated string containing a full path to the file in path - */ -char *resolve_path(const char *path, const char *current_device); - - -/** - * Set the base directory for newly-created mountpoints - */ -void set_mount_base(const char *path); - -/** - * Utility function for joining two paths. Adds a / between a and b if - * required. - * - * Returns a newly-allocated string. - */ -char *join_paths(const char *a, const char *b); - -/** - * encode a disk label (or uuid) for use in a symlink. - */ -char *encode_label(const char *label); - -#endif /* PATHS_H */ diff --git a/devices/petitboot-udev-helper.c b/devices/petitboot-udev-helper.c deleted file mode 100644 index f2f15b1..0000000 --- a/devices/petitboot-udev-helper.c +++ /dev/null @@ -1,636 +0,0 @@ - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "parser.h" -#include "paths.h" -#include "petitboot-paths.h" - -/* Define below to operate without the frontend */ -#undef USE_FAKE_SOCKET - -/* Delay in seconds between polling of removable devices */ -#define REMOVABLE_SLEEP_DELAY 2 - -static FILE *logf; -static int sock; - -void pb_log(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(logf, fmt, ap); - va_end(ap); -} - -static void print_boot_option(const struct boot_option *opt) -{ - pb_log("\tname: %s\n", opt->name); - pb_log("\tdescription: %s\n", opt->description); - pb_log("\tboot_image: %s\n", opt->boot_image_file); - pb_log("\tinitrd: %s\n", opt->initrd_file); - pb_log("\tboot_args: %s\n", opt->boot_args); - -} - -static void print_device(const struct device *dev) -{ - pb_log("\tid: %s\n", dev->id); - pb_log("\tname: %s\n", dev->name); - pb_log("\tdescription: %s\n", dev->description); - pb_log("\tboot_image: %s\n", dev->icon_file); -} - -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)) { - pb_log("write failed: %s\n", strerror(errno)); - return -1; - } - return 0; - } - - len = strlen(str); - if (len > (1ull << (sizeof(len_buf) * 8 - 1))) { - pb_log("string too large\n"); - return -1; - } - - len_buf = __cpu_to_be32(len); - if (write(fd, &len_buf, sizeof(len_buf)) != sizeof(len_buf)) { - pb_log("write failed: %s\n", strerror(errno)); - return -1; - } - - while (pos < len) { - int rc = write(fd, str, len - pos); - if (rc <= 0) { - pb_log("write failed: %s\n", strerror(errno)); - return -1; - } - pos += rc; - str += rc; - } - - return 0; -} - -int add_device(const struct device *dev) -{ - int rc; - - pb_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) - pb_log("error writing device %s to socket\n", dev->name); - - return rc; -} - -int add_boot_option(const struct boot_option *opt) -{ - int rc; - - pb_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) - pb_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() -{ -#ifndef USE_FAKE_SOCKET - int fd; - struct sockaddr_un addr; - - fd = socket(PF_UNIX, SOCK_STREAM, 0); - if (fd == -1) { - pb_log("can't create socket: %s\n", strerror(errno)); - return -1; - } - - addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, PBOOT_DEVICE_SOCKET); - - if (connect(fd, (struct sockaddr *)&addr, sizeof(addr))) { - pb_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) { - pb_log("can't create output file: %s\n", strerror(errno)); - return -1; - } - sock = fd; - return 0; -#endif -} - -static int mkdir_recursive(const char *dir) -{ - char *str, *sep; - int mode = 0755; - struct stat statbuf; - - pb_log("mkdir_recursive(%s)\n", dir); - - if (!*dir) - return 0; - - if (!stat(dir, &statbuf)) { - if (!S_ISDIR(statbuf.st_mode)) { - pb_log("%s: %s exists, but isn't a directory\n", - __func__, dir); - return -1; - } - return 0; - } - - str = strdup(dir); - sep = strchr(*str == '/' ? str + 1 : str, '/'); - - while (1) { - - /* terminate the path at sep */ - if (sep) - *sep = '\0'; - pb_log("mkdir(%s)\n", str); - - if (mkdir(str, mode) && errno != EEXIST) { - pb_log("mkdir(%s): %s\n", str, strerror(errno)); - return -1; - } - - if (!sep) - break; - - /* reset dir to the full path */ - strcpy(str, dir); - sep = strchr(sep + 1, '/'); - } - - free(str); - - return 0; -} - -static void setup_device_links(const char *device) -{ - struct link { - char *env, *dir; - } *link, links[] = { - { - .env = "ID_FS_UUID", - .dir = "disk/by-uuid" - }, - { - .env = "ID_FS_LABEL", - .dir = "disk/by-label" - }, - { - .env = NULL - } - }; - - for (link = links; link->env; link++) { - char *value, *dir, *path; - - value = getenv(link->env); - if (!value) - continue; - - value = encode_label(value); - dir = join_paths(TMP_DIR, link->dir); - path = join_paths(dir, value); - - if (!mkdir_recursive(dir)) { - unlink(path); - if (symlink(mountpoint_for_device(device), path)) { - pb_log("symlink(%s): %s\n", - path, strerror(errno)); - } - } - - free(path); - free(dir); - free(value); - } -} - -int mount_device(const char *dev_path) -{ - const char *dir; - int pid, status, rc = -1; - struct stat statbuf; - - dir = mountpoint_for_device(dev_path); - - if (stat(dir, &statbuf)) { - if (mkdir(dir, 0755)) { - pb_log("couldn't create directory %s: %s\n", - dir, strerror(errno)); - goto out; - } - } else { - if (!S_ISDIR(statbuf.st_mode)) { - pb_log("mountpoint %s exists, " - "but isn't a directory\n", dir); - goto out; - } - } - - - pid = fork(); - if (pid == -1) { - pb_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) { - pb_log("%s: waitpid failed: %s\n", __FUNCTION__, - strerror(errno)); - goto out; - } - - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { - setup_device_links(dev_path); - rc = 0; - } - -out: - return rc; -} - -static int unmount_device(const char *dev_path) -{ - int pid, status, rc; - - pid = fork(); - - if (pid == -1) { - pb_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) { - pb_log("%s: waitpid failed: %s\n", __FUNCTION__, - strerror(errno)); - return -1; - } - - rc = !WIFEXITED(status) || WEXITSTATUS(status) != 0; - - return rc; -} - -static const struct device fake_boot_devices[] = -{ - { - .id = "fakeDisk0", - .name = "Hard Disk", - .icon_file = artwork_pathname("hdd.png"), - }, - { - .id = "fakeDisk1", - .name = "PinkCat Linux CD", - .icon_file = artwork_pathname("cdrom.png"), - } -}; - -static const struct boot_option fake_boot_options[] = -{ - { - .id = "fakeBoot0", - .name = "Bloobuntu Linux", - .description = "Boot Bloobuntu Linux", - .icon_file = artwork_pathname("hdd.png"), - }, - { - .id = "fakeBoot1", - .name = "Pendora Gore 6", - .description = "Boot Pendora Gora 6", - .icon_file = artwork_pathname("hdd.png"), - }, - { - .id = "fakeBoot2", - .name = "Genfoo Minux", - .description = "Boot Genfoo Minux", - .icon_file = artwork_pathname("hdd.png"), - }, - { - .id = "fakeBoot3", - .name = "PinkCat Linux", - .description = "Install PinkCat Linux - Graphical install", - .icon_file = artwork_pathname("cdrom.png"), - }, -}; - -enum generic_icon_type guess_device_type(void) -{ - const char *type = getenv("ID_TYPE"); - const char *bus = getenv("ID_BUS"); - if (type && streq(type, "cd")) - return ICON_TYPE_OPTICAL; - if (!bus) - return ICON_TYPE_UNKNOWN; - if (streq(bus, "usb")) - return ICON_TYPE_USB; - if (streq(bus, "ata") || streq(bus, "scsi")) - return ICON_TYPE_DISK; - return ICON_TYPE_UNKNOWN; -} - - -static int is_removable_device(const char *sysfs_path) -{ - char full_path[PATH_MAX]; - char buf[80]; - int fd, buf_len; - - sprintf(full_path, "/sys/%s/removable", sysfs_path); - fd = open(full_path, O_RDONLY); - pb_log(" -> removable check on %s, fd=%d\n", full_path, fd); - if (fd < 0) - return 0; - buf_len = read(fd, buf, 79); - close(fd); - if (buf_len < 0) - return 0; - buf[buf_len] = 0; - return strtol(buf, NULL, 10); -} - -static int is_ignored_device(const char *devname) -{ - static const char *ignored_devices[] = - { "/dev/ram", "/dev/loop", NULL }; - const char **dev; - - for (dev = ignored_devices; *dev; dev++) - if (!strncmp(devname, *dev, strlen(*dev))) - return 1; - - return 0; -} - -static int found_new_device(const char *dev_path) -{ - const char *mountpoint = mountpoint_for_device(dev_path); - - if (mount_device(dev_path)) { - pb_log("failed to mount %s\n", dev_path); - return EXIT_FAILURE; - } - - pb_log("mounted %s at %s\n", dev_path, mountpoint); - - iterate_parsers(dev_path, mountpoint); - - return EXIT_SUCCESS; -} - -static void detach_and_sleep(int sec) -{ - static int forked = 0; - int rc = 0; - - if (sec <= 0) - return; - - if (!forked) { - pb_log("running in background..."); - rc = fork(); - forked = 1; - } - - if (rc == 0) { - sleep(sec); - - } else if (rc == -1) { - perror("fork()"); - exit(EXIT_FAILURE); - } else { - exit(EXIT_SUCCESS); - } -} - -static int poll_device_plug(const char *dev_path, - int *optical) -{ - int rc, fd; - - /* Polling loop for optical drive */ - for (; (*optical) != 0; ) { - fd = open(dev_path, O_RDONLY|O_NONBLOCK); - if (fd < 0) - return EXIT_FAILURE; - rc = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); - close(fd); - if (rc == -1) - break; - - *optical = 1; - if (rc == CDS_DISC_OK) - return EXIT_SUCCESS; - - detach_and_sleep(REMOVABLE_SLEEP_DELAY); - } - - /* Fall back to bare open() */ - *optical = 0; - for (;;) { - fd = open(dev_path, O_RDONLY); - if (fd < 0 && errno != ENOMEDIUM) - return EXIT_FAILURE; - close(fd); - if (fd >= 0) - return EXIT_SUCCESS; - detach_and_sleep(REMOVABLE_SLEEP_DELAY); - } -} - -static int poll_device_unplug(const char *dev_path, int optical) -{ - int rc, fd; - - for (;optical;) { - fd = open(dev_path, O_RDONLY|O_NONBLOCK); - if (fd < 0) - return EXIT_FAILURE; - rc = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); - close(fd); - if (rc != CDS_DISC_OK) - return EXIT_SUCCESS; - detach_and_sleep(REMOVABLE_SLEEP_DELAY); - } - - /* Fall back to bare open() */ - for (;;) { - fd = open(dev_path, O_RDONLY); - if (fd < 0 && errno != ENOMEDIUM) - return EXIT_FAILURE; - close(fd); - if (fd < 0) - return EXIT_SUCCESS; - detach_and_sleep(REMOVABLE_SLEEP_DELAY); - } -} - -static int poll_removable_device(const char *sysfs_path, - const char *dev_path) -{ - int rc, mounted, optical = -1; - - for (;;) { - rc = poll_device_plug(dev_path, &optical); - if (rc == EXIT_FAILURE) - return rc; - rc = found_new_device(dev_path); - mounted = (rc == EXIT_SUCCESS); - - poll_device_unplug(dev_path, optical); - - remove_device(dev_path); - - /* Unmount it repeatedly, if needs be */ - while (mounted && !unmount_device(dev_path)) - ; - detach_and_sleep(1); - } -} - -int main(int argc, char **argv) -{ - char *dev_path, *action; - int rc; - - action = getenv("ACTION"); - - logf = fopen("/var/log/petitboot-udev-helpers.log", "a"); - if (!logf) - logf = stdout; - pb_log("%d started\n", getpid()); - rc = EXIT_SUCCESS; - - if (!action) { - pb_log("missing environment?\n"); - return EXIT_FAILURE; - } - - set_mount_base(TMP_DIR); - - if (connect_to_socket()) - return EXIT_FAILURE; - - if (streq(action, "fake")) { - pb_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) { - pb_log("missing environment?\n"); - return EXIT_FAILURE; - } - - if (is_ignored_device(dev_path)) - return EXIT_SUCCESS; - - if (streq(action, "add")) { - char *sysfs_path = getenv("DEVPATH"); - if (sysfs_path && is_removable_device(sysfs_path)) - rc = poll_removable_device(sysfs_path, dev_path); - else - rc = found_new_device(dev_path); - } else if (streq(action, "remove")) { - pb_log("%s removed\n", dev_path); - - remove_device(dev_path); - - /* Unmount it repeatedly, if needs be */ - while (!unmount_device(dev_path)) - ; - - } else { - pb_log("invalid action '%s'\n", action); - rc = EXIT_FAILURE; - } - return rc; -} diff --git a/devices/yaboot-cfg.c b/devices/yaboot-cfg.c deleted file mode 100644 index 6b007e4..0000000 --- a/devices/yaboot-cfg.c +++ /dev/null @@ -1,491 +0,0 @@ -/* - * cfg.c - Handling and parsing of yaboot.conf - * - * Copyright (C) 1995 Werner Almesberger - * 1996 Jakub Jelinek - * - * 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 -#include -#include -#include -#include -#include - -#define prom_printf printf -#define prom_putchar putchar -#define prom_vprintf vprintf - -/* Imported functions */ -extern int strcasecmp(const char *s1, const char *s2); - -typedef enum { - cft_strg, cft_flag, cft_end -} CONFIG_TYPE; - -typedef struct { - CONFIG_TYPE type; - char *name; - void *data; -} CONFIG; - -#define MAX_TOKEN 200 -#define MAX_VAR_NAME MAX_TOKEN -char *cfg_get_default (void); - -CONFIG cf_options[] = -{ - {cft_strg, "device", NULL}, - {cft_strg, "partition", NULL}, - {cft_strg, "default", NULL}, - {cft_strg, "timeout", NULL}, - {cft_strg, "password", NULL}, - {cft_flag, "restricted", NULL}, - {cft_strg, "message", NULL}, - {cft_strg, "root", NULL}, - {cft_strg, "ramdisk", NULL}, - {cft_flag, "read-only", NULL}, - {cft_flag, "read-write", NULL}, - {cft_strg, "append", NULL}, - {cft_strg, "initrd", NULL}, - {cft_flag, "initrd-prompt", NULL}, - {cft_strg, "initrd-size", NULL}, - {cft_flag, "pause-after", NULL}, - {cft_strg, "pause-message", NULL}, - {cft_strg, "init-code", NULL}, - {cft_strg, "init-message", NULL}, - {cft_strg, "fgcolor", NULL}, - {cft_strg, "bgcolor", NULL}, - {cft_strg, "ptypewarning", NULL}, - {cft_end, NULL, NULL}}; - -CONFIG cf_image[] = -{ - {cft_strg, "image", NULL}, - {cft_strg, "label", NULL}, - {cft_strg, "alias", NULL}, - {cft_flag, "single-key", NULL}, - {cft_flag, "restricted", NULL}, - {cft_strg, "device", NULL}, - {cft_strg, "partition", NULL}, - {cft_strg, "root", NULL}, - {cft_strg, "ramdisk", NULL}, - {cft_flag, "read-only", NULL}, - {cft_flag, "read-write", NULL}, - {cft_strg, "append", NULL}, - {cft_strg, "literal", NULL}, - {cft_strg, "initrd", NULL}, - {cft_flag, "initrd-prompt", NULL}, - {cft_strg, "initrd-size", NULL}, - {cft_flag, "pause-after", NULL}, - {cft_strg, "pause-message", NULL}, - {cft_flag, "novideo", NULL}, - {cft_strg, "sysmap", NULL}, - {cft_end, NULL, NULL}}; - -static char flag_set; -static char *last_token = NULL, *last_item = NULL, *last_value = NULL; -static int line_num; -static int back = 0; /* can go back by one char */ -static char *currp = NULL; -static char *endp = NULL; -static char *file_name = NULL; -static CONFIG *curr_table = cf_options; -static jmp_buf env; - -static struct IMAGES { - CONFIG table[sizeof (cf_image) / sizeof (cf_image[0])]; - struct IMAGES *next; -} *images = NULL; - -void cfg_error (char *msg,...) -{ - va_list ap; - - va_start (ap, msg); - prom_printf ("Config file error: "); - prom_vprintf (msg, ap); - va_end (ap); - prom_printf (" near line %d in file %s\n", line_num, file_name); - longjmp (env, 1); -} - -void cfg_warn (char *msg,...) -{ - va_list ap; - - va_start (ap, msg); - prom_printf ("Config file warning: "); - prom_vprintf (msg, ap); - va_end (ap); - prom_printf (" near line %d in file %s\n", line_num, file_name); -} - -int cfg_getc () -{ - if (currp == endp) - return EOF; - return *currp++; -} - -#define next_raw next -static int next (void) -{ - int ch; - - if (!back) - return cfg_getc (); - ch = back; - back = 0; - return ch; -} - -static void again (int ch) -{ - back = ch; -} - -static char *cfg_get_token (void) -{ - char buf[MAX_TOKEN + 1]; - char *here; - int ch, escaped; - - if (last_token) { - here = last_token; - last_token = NULL; - return here; - } - while (1) { - while (ch = next (), ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') - if (ch == '\n' || ch == '\r') - line_num++; - if (ch == EOF || ch == (int)NULL) - return NULL; - if (ch != '#') - break; - while (ch = next_raw (), (ch != '\n' && ch != '\r')) - if (ch == EOF) - return NULL; - line_num++; - } - if (ch == '=') - return strdup ("="); - if (ch == '"') { - here = buf; - while (here - buf < MAX_TOKEN) { - if ((ch = next ()) == EOF) - cfg_error ("EOF in quoted string"); - if (ch == '"') { - *here = 0; - return strdup (buf); - } - if (ch == '\\') { - ch = next (); - switch (ch) { - case '"': - case '\\': - break; - case '\n': - case '\r': - while ((ch = next ()), ch == ' ' || ch == '\t'); - if (!ch) - continue; - again (ch); - ch = ' '; - break; - case 'n': - ch = '\n'; - break; - default: - cfg_error ("Bad use of \\ in quoted string"); - } - } else if ((ch == '\n') || (ch == '\r')) - cfg_error ("newline is not allowed in quoted strings"); - *here++ = ch; - } - cfg_error ("Quoted string is too long"); - return 0; /* not reached */ - } - here = buf; - escaped = 0; - while (here - buf < MAX_TOKEN) { - if (escaped) { - if (ch == EOF) - cfg_error ("\\ precedes EOF"); - if (ch == '\n') - line_num++; - else - *here++ = ch == '\t' ? ' ' : ch; - escaped = 0; - } else { - if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '#' || - ch == '=' || ch == EOF) { - again (ch); - *here = 0; - return strdup (buf); - } - if (!(escaped = (ch == '\\'))) - *here++ = ch; - } - ch = next (); - } - cfg_error ("Token is too long"); - return 0; /* not reached */ -} - -static void cfg_return_token (char *token) -{ - last_token = token; -} - -static int cfg_next (char **item, char **value) -{ - char *this; - - if (last_item) { - *item = last_item; - *value = last_value; - last_item = NULL; - return 1; - } - *value = NULL; - if (!(*item = cfg_get_token ())) - return 0; - if (!strcmp (*item, "=")) - cfg_error ("Syntax error"); - if (!(this = cfg_get_token ())) - return 1; - if (strcmp (this, "=")) { - cfg_return_token (this); - return 1; - } - if (!(*value = cfg_get_token ())) - cfg_error ("Value expected at EOF"); - if (!strcmp (*value, "=")) - cfg_error ("Syntax error after %s", *item); - return 1; -} - -#if 0 -// The one and only call to this procedure is commented out -// below, so we don't need this unless we decide to use it again. -static void cfg_return (char *item, char *value) -{ - last_item = item; - last_value = value; -} -#endif - -static int cfg_set (char *item, char *value) -{ - CONFIG *walk; - - if (!strcasecmp (item, "image")) { - struct IMAGES **p = &images; - - while (*p) - p = &((*p)->next); - *p = (struct IMAGES *)malloc (sizeof (struct IMAGES)); - if (*p == NULL) { - prom_printf("malloc error in cfg_set\n"); - return -1; - } - (*p)->next = 0; - curr_table = ((*p)->table); - memcpy (curr_table, cf_image, sizeof (cf_image)); - } - for (walk = curr_table; walk->type != cft_end; walk++) { - if (walk->name && !strcasecmp (walk->name, item)) { - if (value && walk->type != cft_strg) - cfg_warn ("'%s' doesn't have a value", walk->name); - else if (!value && walk->type == cft_strg) - cfg_warn ("Value expected for '%s'", walk->name); - else { - if (walk->data) - cfg_warn ("Duplicate entry '%s'", walk->name); - if (walk->type == cft_flag) - walk->data = &flag_set; - else if (walk->type == cft_strg) - walk->data = value; - } - break; - } - } - if (walk->type != cft_end) - return 1; -// cfg_return (item, value); - return 0; -} - -int cfg_parse (char *cfg_file, char *buff, int len) -{ - char *item, *value; - - file_name = cfg_file; - currp = buff; - endp = currp + len; - - if (setjmp (env)) - return -1; - while (1) { - if (!cfg_next (&item, &value)) - return 0; - if (!cfg_set (item, value)) { -#if DEBUG - prom_printf("Can't set item %s to value %s\n", item, value); -#endif - } - free (item); - } -} - -static char *cfg_get_strg_i (CONFIG * table, char *item) -{ - CONFIG *walk; - - for (walk = table; walk->type != cft_end; walk++) - if (walk->name && !strcasecmp (walk->name, item)) - return walk->data; - return 0; -} - -char *cfg_get_strg (char *image, char *item) -{ - struct IMAGES *p; - char *label, *alias; - char *ret; - - if (!image) - return cfg_get_strg_i (cf_options, item); - for (p = images; p; p = p->next) { - label = cfg_get_strg_i (p->table, "label"); - if (!label) { - label = cfg_get_strg_i (p->table, "image"); - alias = strrchr (label, '/'); - if (alias) - label = alias + 1; - } - alias = cfg_get_strg_i (p->table, "alias"); - if (!strcmp (label, image) || (alias && !strcmp (alias, image))) { - ret = cfg_get_strg_i (p->table, item); - if (!ret) - ret = cfg_get_strg_i (cf_options, item); - return ret; - } - } - return 0; -} - -int cfg_get_flag (char *image, char *item) -{ - return !!cfg_get_strg (image, item); -} - -static int printl_count = 0; -static void printlabel (char *label, int defflag) -{ - int len = strlen (label); - - if (!printl_count) - prom_printf ("\n"); - prom_printf ("%s %s",defflag?"*":" ", label); - while (len++ < 25) - prom_putchar (' '); - printl_count++; - if (printl_count == 3) - printl_count = 0; -} - -void cfg_print_images (void) -{ - struct IMAGES *p; - char *label, *alias; - - char *ret = cfg_get_default();//strg_i (cf_options, "default"); - int defflag=0; - - printl_count = 0; - for (p = images; p; p = p->next) { - label = cfg_get_strg_i (p->table, "label"); - if (!label) { - label = cfg_get_strg_i (p->table, "image"); - alias = strrchr (label, '/'); - if (alias) - label = alias + 1; - } - if(!strcmp(ret,label)) - defflag=1; - else - defflag=0; - alias = cfg_get_strg_i (p->table, "alias"); - printlabel (label, defflag); - if (alias) - printlabel (alias, 0); - } - prom_printf("\n"); -} - -char *cfg_get_default (void) -{ - char *label; - char *ret = cfg_get_strg_i (cf_options, "default"); - - if (ret) - return ret; - if (!images) - return 0; - ret = cfg_get_strg_i (images->table, "label"); - if (!ret) { - ret = cfg_get_strg_i (images->table, "image"); - label = strrchr (ret, '/'); - if (label) - ret = label + 1; - } - return ret; -} - -char *cfg_next_image(char *prev) -{ - struct IMAGES *p; - char *label, *alias; - int wantnext = 0; - - if (!prev) - wantnext = 1; - - for (p = images; p; p = p->next) { - label = cfg_get_strg_i (p->table, "label"); - if (!label) { - label = cfg_get_strg_i (p->table, "image"); - alias = strrchr (label, '/'); - if (alias) - label = alias + 1; - } - if (wantnext) - return label; - if (!strcmp(prev, label)) - wantnext = 1; - } - return NULL; -} -/* - * Local variables: - * c-file-style: "k&r" - * c-basic-offset: 5 - * End: - */ diff --git a/devices/yaboot-cfg.h b/devices/yaboot-cfg.h deleted file mode 100644 index 2ab4fec..0000000 --- a/devices/yaboot-cfg.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * cfg.h - config file parsing definitions - * - * Copyright (C) 1999 Benjamin Herrenschmidt - * - * 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. - */ - -#ifndef CFG_H -#define CFG_H - -extern int cfg_parse(char *cfg_file, char *buff, int len); -extern char* cfg_get_strg(char *image, char *item); -extern int cfg_get_flag(char *image, char *item); -extern void cfg_print_images(void); -extern char* cfg_get_default(void); -extern char* cfg_next_image(char *); -#endif diff --git a/devices/yaboot-parser.c b/devices/yaboot-parser.c deleted file mode 100644 index 27b4b78..0000000 --- a/devices/yaboot-parser.c +++ /dev/null @@ -1,235 +0,0 @@ - -#include "parser.h" -#include "params.h" -#include "paths.h" -#include "yaboot-cfg.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static struct device *dev; -static char *devpath; -static char *defimage; - -char * -make_params(char *label, char *params) -{ - char *p, *q; - static char buffer[2048]; - - q = buffer; - *q = 0; - - p = cfg_get_strg(label, "literal"); - if (p) { - strcpy(q, p); - q = strchr(q, 0); - if (params) { - if (*p) - *q++ = ' '; - strcpy(q, params); - } - return buffer; - } - - p = cfg_get_strg(label, "root"); - if (p) { - strcpy (q, "root="); - strcpy (q + 5, p); - q = strchr (q, 0); - *q++ = ' '; - } - if (cfg_get_flag(label, "read-only")) { - strcpy (q, "ro "); - q += 3; - } - if (cfg_get_flag(label, "read-write")) { - strcpy (q, "rw "); - q += 3; - } - p = cfg_get_strg(label, "ramdisk"); - if (p) { - strcpy (q, "ramdisk="); - strcpy (q + 8, p); - q = strchr (q, 0); - *q++ = ' '; - } - p = cfg_get_strg(label, "initrd-size"); - if (p) { - strcpy (q, "ramdisk_size="); - strcpy (q + 13, p); - q = strchr (q, 0); - *q++ = ' '; - } - if (cfg_get_flag(label, "novideo")) { - strcpy (q, "video=ofonly"); - q = strchr (q, 0); - *q++ = ' '; - } - p = cfg_get_strg (label, "append"); - if (p) { - strcpy (q, p); - q = strchr (q, 0); - *q++ = ' '; - } - *q = 0; - if (params) - strcpy(q, params); - - return buffer; -} - -static 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); -} - -void process_image(char *label) -{ - struct boot_option opt; - char *cfgopt; - - memset(&opt, 0, sizeof(opt)); - - opt.name = label; - cfgopt = cfg_get_strg(label, "image"); - opt.boot_image_file = resolve_path(cfgopt, devpath); - if (cfgopt == defimage) - pb_log("This one is default. What do we do about it?\n"); - - cfgopt = cfg_get_strg(label, "initrd"); - if (cfgopt) - opt.initrd_file = resolve_path(cfgopt, devpath); - - opt.boot_args = make_params(label, NULL); - - add_boot_option(&opt); - - if (opt.initrd_file) - free(opt.initrd_file); -} - -static int yaboot_parse(const char *device) -{ - char *filepath; - char *conf_file; - char *tmpstr; - ssize_t conf_len; - int fd; - struct stat st; - char *label; - - devpath = strdup(device); - - filepath = resolve_path("/etc/yaboot.conf", devpath); - - fd = open(filepath, O_RDONLY); - if (fd < 0) { - free(filepath); - filepath = resolve_path("/yaboot.conf", devpath); - fd = open(filepath, O_RDONLY); - - if (fd < 0) - return 0; - } - - if (fstat(fd, &st)) { - close(fd); - return 0; - } - - conf_file = malloc(st.st_size+1); - if (!conf_file) { - close(fd); - return 0; - } - - conf_len = read(fd, conf_file, st.st_size); - if (conf_len < 0) { - close(fd); - return 0; - } - conf_file[conf_len] = 0; - - close(fd); - - if (cfg_parse(filepath, conf_file, conf_len)) { - pb_log("Error parsing yaboot.conf\n"); - return 0; - } - - free(filepath); - - dev = malloc(sizeof(*dev)); - memset(dev, 0, sizeof(*dev)); - dev->id = strdup(devpath); - if (cfg_get_strg(0, "init-message")) { - char *newline; - dev->description = strdup(cfg_get_strg(0, "init-message")); - newline = strchr(dev->description, '\n'); - if (newline) - *newline = 0; - } - dev->icon_file = strdup(generic_icon_file(guess_device_type())); - - /* If we have a 'partiton=' directive, update the default devpath - * to use that instead of the current device */ - tmpstr = cfg_get_strg(0, "partition"); - if (tmpstr) { - char *endp; - int partnr = strtol(tmpstr, &endp, 10); - if (endp != tmpstr && !*endp) { - char *new_dev, *tmp; - - new_dev = malloc(strlen(devpath) + strlen(tmpstr) + 1); - if (!new_dev) - return 0; - - strcpy(new_dev, devpath); - - /* Strip digits (partition number) from string */ - endp = new_dev + strlen(devpath) - 1; - while (isdigit(*endp)) - *(endp--) = 0; - - /* and add our own... */ - sprintf(endp + 1, "%d", partnr); - - tmp = devpath; - devpath = parse_device_path(new_dev, devpath); - free(tmp); - free(new_dev); - } - } - - defimage = cfg_get_default(); - if (!defimage) - return 0; - defimage = cfg_get_strg(defimage, "image"); - - label = cfg_next_image(NULL); - if (!label || !check_and_add_device(dev)) - return 0; - - do { - process_image(label); - } while ((label = cfg_next_image(label))); - - return 1; -} - -struct parser yaboot_parser = { - .name = "yaboot.conf parser", - .priority = 99, - .parse = yaboot_parse -}; diff --git a/discover/discover-server.c b/discover/discover-server.c new file mode 100644 index 0000000..8358f06 --- /dev/null +++ b/discover/discover-server.c @@ -0,0 +1,227 @@ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "ui/common/device.h" +#include "pb-protocol/pb-protocol.h" +#include "list/list.h" + +#include "log.h" +#include "waiter.h" + +struct discover_server { + int socket; + struct waiter *waiter; + struct list clients; +}; + +struct client { + struct list_item list; + int fd; +}; + + +static int server_destructor(void *arg) +{ + struct discover_server *server = arg; + + if (server->waiter) + waiter_unregister(server->waiter); + + if (server->socket >= 0) + close(server->socket); + + return 0; +} + +static int client_destructor(void *arg) +{ + struct client *client = arg; + + if (client->fd >= 0) + close(client->fd); + + list_remove(&client->list); + + return 0; + +} + +static void print_clients(struct discover_server *server) + __attribute__((unused)); + +static void print_clients(struct discover_server *server) +{ + struct client *client; + + printf("current clients [%p,%p,%p]:\n", + &server->clients.head, + server->clients.head.prev, + server->clients.head.next); + list_for_each_entry(&server->clients, client, list) + printf("\t[%p,%p,%p] client: %d\n", &client->list, + client->list.prev, client->list.next, + client->fd); +} + +static struct boot_option options[] = { + { + .id = "1.1", + .name = "meep one", + .description = "meep description one", + .icon_file = "meep.one.png", + .boot_args = "root=/dev/sda1", + }, +}; + +static struct device device = { + .id = "1", + .name = "meep", + .description = "meep description", + .icon_file = "meep.png", + .n_options = 1, + .options = options, +}; + +static int client_write_message(struct discover_server *server, + struct client *client, struct pb_protocol_message *message) +{ + int rc; + + rc = pb_protocol_write_message(client->fd, message); + if (rc) + talloc_free(client); + + return rc; +} + +static int write_add_message(struct discover_server *server, + struct client *client, struct device *dev) +{ + struct pb_protocol_message *message; + int len; + + len = pb_protocol_device_len(dev); + + message = pb_protocol_create_message(client, + PB_PROTOCOL_ACTION_ADD, len); + if (!message) + return -1; + + pb_protocol_serialise_device(dev, message->payload, len); + + return client_write_message(server, client, message); +} + +static int write_remove_message(struct discover_server *server, + struct client *client, char *dev_id) +{ + struct pb_protocol_message *message; + int len; + + len = strlen(dev_id) + sizeof(uint32_t); + + message = pb_protocol_create_message(client, + PB_PROTOCOL_ACTION_REMOVE, len); + if (!message) + return -1; + + pb_protocol_serialise_string(message->payload, dev_id); + + return client_write_message(server, client, message); +} + +static int discover_server_process(void *arg) +{ + struct discover_server *server = arg; + struct client *client; + int fd; + + + len = sizeof(addr); + + /* accept the incoming connection */ + fd = accept(server->socket, NULL, 0); + if (!fd) { + pb_log("accept: %s\n", strerror(errno)); + return 0; + } + + /* add to our list of clients */ + client = talloc(server, struct client); + list_add(&server->clients, &client->list); + + talloc_set_destructor(client, client_destructor); + + client->fd = fd; + + /* send existing devices to client */ + write_add_message(server, client, &device); + + sleep(2); + + write_remove_message(server, client, "1"); + + return 0; +} + +struct discover_server *discover_server_init(void) +{ + struct discover_server *server; + struct sockaddr_un addr; + + server = talloc(NULL, struct discover_server); + if (!server) + return NULL; + + server->waiter = NULL; + list_init(&server->clients); + + unlink(PB_SOCKET_PATH); + + server->socket = socket(AF_UNIX, SOCK_STREAM, 0); + if (server->socket < 0) { + pb_log("error creating server socket: %s\n", strerror(errno)); + goto out_err; + } + + talloc_set_destructor(server, server_destructor); + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, PB_SOCKET_PATH); + + if (bind(server->socket, (struct sockaddr *)&addr, sizeof(addr))) { + pb_log("error binding server socket: %s\n", strerror(errno)); + goto out_err; + } + + if (listen(server->socket, 8)) { + pb_log("server socket listen: %s\n", strerror(errno)); + goto out_err; + } + + server->waiter = waiter_register(server->socket, WAIT_IN, + discover_server_process, server); + + return server; + +out_err: + talloc_free(server); + return NULL; +} + +void discover_server_destroy(struct discover_server *server) +{ + talloc_free(server); +} + diff --git a/discover/discover-server.h b/discover/discover-server.h new file mode 100644 index 0000000..ff27f15 --- /dev/null +++ b/discover/discover-server.h @@ -0,0 +1,10 @@ +#ifndef _DISCOVER_SERVER_H +#define _DISCOVER_SERVER_H + +struct discover_server; + +struct discover_server *discover_server_init(void); + +void discover_server_destroy(struct discover_server *server); + +#endif /* _DISCOVER_SERVER_H */ diff --git a/discover/kboot-parser.c b/discover/kboot-parser.c new file mode 100644 index 0000000..df2e762 --- /dev/null +++ b/discover/kboot-parser.c @@ -0,0 +1,288 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "parser.h" +#include "params.h" + +#define buf_size 1024 + +static const char *devpath; + +static int param_is_ignored(const char *param) +{ + static const char *ignored_options[] = + { "message", "timeout", "default", NULL }; + const char **str; + + for (str = ignored_options; *str; str++) + if (streq(*str, param)) + return 1; + return 0; +} + +/** + * Splits a name=value pair, with value terminated by @term (or nul). if there + * is no '=', then only the value is populated, and *name is set to NULL. The + * string is modified in place. + * + * Returns the next byte to process, or null if we've hit the end of the + * string. + * + * */ +static char *get_param_pair(char *str, char **name_out, char **value_out, + char terminator) +{ + char *sep, *tmp, *name, *value; + + /* terminate the value */ + tmp = strchr(str, terminator); + if (tmp) + *tmp = 0; + else + tmp = NULL; + + sep = strchr(str, '='); + if (!sep) { + *name_out = NULL; + *value_out = str; + return tmp ? tmp + 1 : NULL; + } + + /* terminate the name */ + *sep = 0; + + /* remove leading spaces */ + for (name = str; isspace(*name); name++); + for (value = sep + 1; isspace(*value); value++); + + /* .. and trailing ones.. */ + for (sep--; isspace(*sep); sep--) + *sep = 0; + for (sep = value + strlen(value) - 1; isspace(*sep); sep--) + *sep = 0; + + *name_out = name; + *value_out = value; + + return tmp ? tmp + 1 : NULL; +} + +struct global_option { + char *name; + char *value; +}; + + +static struct global_option global_options[] = { + { .name = "root" }, + { .name = "initrd" }, + { .name = "video" }, + { .name = NULL } +}; + +/* + * Check if an option (name=value) is a global option. If so, store it in + * the global options table, and return 1. Otherwise, return 0. + */ +static int check_for_global_option(const char *name, const char *value) +{ + int i; + + for (i = 0; global_options[i].name ;i++) { + if (!strcmp(name, global_options[i].name)) { + global_options[i].value = strdup(value); + return 1; + } + } + return 0; +} + +static char *get_global_option(const char *name) +{ + int i; + + for (i = 0; global_options[i].name ;i++) + if (!strcmp(name, global_options[i].name)) + return global_options[i].value; + + return NULL; +} + +static int parse_option(struct boot_option *opt, char *config) +{ + char *pos, *name, *value, *root, *initrd, *cmdline, *tmp; + + root = initrd = cmdline = NULL; + + /* remove quotes around the value */ + while (*config == '"' || *config == '\'') + config++; + + pos = config + strlen(config) - 1; + while (*pos == '"' || *pos == '\'') + *(pos--) = 0; + + if (!strlen(pos)) + return 0; + + pos = strchr(config, ' '); + + /* if there's no space, it's only a kernel image with no params */ + if (!pos) { + opt->boot_image_file = resolve_path(config, devpath); + opt->description = strdup(config); + return 1; + } + + *pos = 0; + opt->boot_image_file = resolve_path(config, devpath); + + cmdline = malloc(buf_size); + *cmdline = 0; + + for (pos++; pos;) { + pos = get_param_pair(pos, &name, &value, ' '); + + if (!name) { + strcat(cmdline, " "); + strcat(cmdline, value); + + } else if (streq(name, "initrd")) { + initrd = value; + + } else if (streq(name, "root")) { + root = value; + + } else { + strcat(cmdline, " "); + *(value - 1) = '='; + strcat(cmdline, name); + } + } + + if (!root) + root = get_global_option("root"); + if (!initrd) + initrd = get_global_option("initrd"); + + if (initrd) { + asprintf(&tmp, "initrd=%s %s", initrd, cmdline); + free(cmdline); + cmdline = tmp; + + opt->initrd_file = resolve_path(initrd, devpath); + } + + if (root) { + asprintf(&tmp, "root=%s %s", root, cmdline); + free(cmdline); + cmdline = tmp; + + } else if (initrd) { + /* if there's an initrd but no root, fake up /dev/ram0 */ + asprintf(&tmp, "root=/dev/ram0 %s", cmdline); + free(cmdline); + cmdline = tmp; + } + + pb_log("kboot cmdline: %s\n", cmdline); + opt->boot_args = cmdline; + + asprintf(&opt->description, "%s %s", + config, opt->boot_args); + + return 1; +} + +static void parse_buf(struct device *dev, char *buf) +{ + char *pos, *name, *value; + int sent_device = 0; + + for (pos = buf; pos;) { + struct boot_option opt; + + pos = get_param_pair(pos, &name, &value, '\n'); + + pb_log("kboot param: '%s' = '%s'\n", name, value); + + if (name == NULL || param_is_ignored(name)) + continue; + + if (*name == '#') + continue; + + if (check_for_global_option(name, value)) + continue; + + memset(&opt, 0, sizeof(opt)); + opt.name = strdup(name); + + if (parse_option(&opt, value)) + if (!sent_device++) + add_device(dev); + add_boot_option(&opt); + + free(opt.name); + } +} + +static int parse(const char *device) +{ + char *filepath, *buf; + int fd, len, rc = 0; + struct stat stat; + struct device *dev; + + devpath = device; + + filepath = resolve_path("/etc/kboot.conf", devpath); + + fd = open(filepath, O_RDONLY); + if (fd < 0) + goto out_free_path; + + if (fstat(fd, &stat)) + goto out_close; + + buf = malloc(stat.st_size + 1); + if (!buf) + goto out_close;; + + len = read(fd, buf, stat.st_size); + if (len < 0) + goto out_free_buf; + buf[len] = 0; + + dev = malloc(sizeof(*dev)); + memset(dev, 0, sizeof(*dev)); + dev->id = strdup(device); + dev->icon_file = strdup(generic_icon_file(guess_device_type())); + + parse_buf(dev, buf); + + rc = 1; + +out_free_buf: + free(buf); +out_close: + close(fd); +out_free_path: + free(filepath); + return rc; +} + +struct parser kboot_parser = { + .name = "kboot.conf parser", + .priority = 98, + .parse = parse +}; diff --git a/discover/log.c b/discover/log.c new file mode 100644 index 0000000..189f31e --- /dev/null +++ b/discover/log.c @@ -0,0 +1,24 @@ + +#include +#include + +#include "log.h" + +static FILE *logf; + +void pb_log(const char *fmt, ...) +{ + va_list ap; + FILE *stream; + + stream = logf ? logf : stdout; + + va_start(ap, fmt); + vfprintf(stream, fmt, ap); + va_end(ap); +} + +void pb_log_set_stream(FILE *stream) +{ + logf = stream; +} diff --git a/discover/log.h b/discover/log.h new file mode 100644 index 0000000..7f9b01f --- /dev/null +++ b/discover/log.h @@ -0,0 +1,6 @@ +#ifndef _LOG_H +#define _LOG_H + +void pb_log(const char *fmt, ...); + +#endif /* _LOG_H */ diff --git a/discover/message.h b/discover/message.h new file mode 100644 index 0000000..d0b0e34 --- /dev/null +++ b/discover/message.h @@ -0,0 +1,31 @@ + +#ifndef _MESSAGE_H +#define _MESSAGE_H + +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; + } *options; + int n_options; +}; + + +#endif /* _MESSAGE_H */ diff --git a/discover/native-parser.c b/discover/native-parser.c new file mode 100644 index 0000000..24713b1 --- /dev/null +++ b/discover/native-parser.c @@ -0,0 +1,124 @@ + +#include "parser.h" +#include "params.h" +#include "paths.h" + +#include +#include +#include + +const char *conf_filename = "/boot/petitboot.conf"; + +static struct boot_option *cur_opt; +static struct device *dev; +static const char *devpath; +int device_added; + +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 = resolve_path(value, devpath); + + else if (streq(name, "icon")) + opt->icon_file = resolve_path(value, devpath); + + else if (streq(name, "initrd")) + opt->initrd_file =resolve_path(value, devpath); + + 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 = resolve_path(value, devpath); +} + +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 *device) +{ + char *filepath; + int rc; + + filepath = resolve_path(conf_filename, device); + + cur_opt = NULL; + dev = malloc(sizeof(*dev)); + memset(dev, 0, sizeof(*dev)); + dev->id = strdup(device); + + rc = pm_process(filepath, section, parameter); + if (!rc) + return 0; + + 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/discover/params.c b/discover/params.c new file mode 100644 index 0000000..76a1451 --- /dev/null +++ b/discover/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/discover/params.h b/discover/params.h new file mode 100644 index 0000000..02a39c9 --- /dev/null +++ b/discover/params.h @@ -0,0 +1,6 @@ + +#define BOOL int + +BOOL pm_process( char *FileName, + BOOL (*sfunc)(char *), + BOOL (*pfunc)(char *, char *) ); diff --git a/discover/parser.c b/discover/parser.c new file mode 100644 index 0000000..5e50dcb --- /dev/null +++ b/discover/parser.c @@ -0,0 +1,85 @@ + +#include +#include +#include + +#include "parser.h" + +extern struct parser native_parser; +extern struct parser yaboot_parser; +extern struct parser kboot_parser; + +/* array of parsers, ordered by priority */ +static struct parser *parsers[] = { + &native_parser, + &yaboot_parser, + &kboot_parser, + NULL +}; + +void iterate_parsers(const char *devpath, const char *mountpoint) +{ + int i; + + pb_log("trying parsers for %s\n", devpath); + + for (i = 0; parsers[i]; i++) { + pb_log("\ttrying parser '%s'\n", parsers[i]->name); + /* just use a dummy device path for now */ + if (parsers[i]->parse(devpath)) + return; + } + pb_log("\tno boot_options found\n"); +} + +/* convenience functions for parsers */ +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); +} + +const char *generic_icon_file(enum generic_icon_type type) +{ + switch (type) { + case ICON_TYPE_DISK: + return artwork_pathname("hdd.png"); + case ICON_TYPE_USB: + return artwork_pathname("usbpen.png"); + case ICON_TYPE_OPTICAL: + return artwork_pathname("cdrom.png"); + case ICON_TYPE_NETWORK: + case ICON_TYPE_UNKNOWN: + break; + } + return artwork_pathname("hdd.png"); +} + diff --git a/discover/parser.h b/discover/parser.h new file mode 100644 index 0000000..9c6fb35 --- /dev/null +++ b/discover/parser.h @@ -0,0 +1,46 @@ + +#ifndef _PARSERS_H +#define _PARSERS_H + +#include +#include "message.h" + +struct parser { + char *name; + int priority; + int (*parse)(const char *device); + struct parser *next; +}; + +enum generic_icon_type { + ICON_TYPE_DISK, + ICON_TYPE_USB, + ICON_TYPE_OPTICAL, + ICON_TYPE_NETWORK, + ICON_TYPE_UNKNOWN +}; + +#define streq(a,b) (!strcasecmp((a),(b))) + +/* general functions provided by parsers.c */ +void iterate_parsers(const char *devpath, const char *mountpoint); + +void free_device(struct device *dev); +void free_boot_option(struct boot_option *opt); + +const char *generic_icon_file(enum generic_icon_type type); + +/* functions provided by udev-helper or the test wrapper */ +void pb_log(const char *fmt, ...); + +int mount_device(const char *dev_path); + +char *resolve_path(const char *path, const char *current_dev); +const char *mountpoint_for_device(const char *dev_path); + +enum generic_icon_type guess_device_type(void); + +int add_device(const struct device *dev); +int add_boot_option(const struct boot_option *opt); + +#endif /* _PARSERS_H */ diff --git a/discover/paths.c b/discover/paths.c new file mode 100644 index 0000000..2373c28 --- /dev/null +++ b/discover/paths.c @@ -0,0 +1,141 @@ +#define _GNU_SOURCE + +#include +#include +#include + +#include "paths.h" + +static char *mount_base; + +struct device_map { + char *dev, *mnt; +}; + +#define DEVICE_MAP_SIZE 32 +static struct device_map device_map[DEVICE_MAP_SIZE]; + +char *encode_label(const char *label) +{ + char *str, *c; + int i; + + /* the label can be expanded by up to four times */ + str = malloc(strlen(label) * 4 + 1); + c = str; + + for (i = 0; i < strlen(label); i++) { + + if (label[i] == '/' || label[i] == '\\') { + sprintf(c, "\\x%02x", label[i]); + c += 4; + continue; + } + + *(c++) = label[i]; + } + + *c = '\0'; + + return str; +} + +char *parse_device_path(const char *dev_str, const char *cur_dev) +{ + char *dev, tmp[256], *enc; + + if (!strncasecmp(dev_str, "uuid=", 5)) { + asprintf(&dev, "/dev/disk/by-uuid/%s", dev_str + 5); + return dev; + } + + if (!strncasecmp(dev_str, "label=", 6)) { + enc = encode_label(dev_str + 6); + asprintf(&dev, "/dev/disk/by-label/%s", enc); + free(enc); + return dev; + } + + /* normalise '/dev/foo' to 'foo' for easy comparisons, we'll expand + * back before returning. + */ + if (!strncmp(dev_str, "/dev/", 5)) + dev_str += 5; + + /* PS3 hack: if we're reading from a ps3dx device, and we refer to + * a sdx device, remap to ps3dx */ + if (cur_dev && !strncmp(cur_dev, "/dev/ps3d", 9) + && !strncmp(dev_str, "sd", 2)) { + snprintf(tmp, 255, "ps3d%s", dev_str + 2); + dev_str = tmp; + } + + return join_paths("/dev", dev_str); +} + +const char *mountpoint_for_device(const char *dev) +{ + int i; + + if (!strncmp(dev, "/dev/", 5)) + dev += 5; + + /* check existing entries in the map */ + for (i = 0; (i < DEVICE_MAP_SIZE) && device_map[i].dev; i++) + if (!strcmp(device_map[i].dev, dev)) + return device_map[i].mnt; + + if (i == DEVICE_MAP_SIZE) + return NULL; + + device_map[i].dev = strdup(dev); + device_map[i].mnt = join_paths(mount_base, dev); + return device_map[i].mnt; +} + +char *resolve_path(const char *path, const char *current_dev) +{ + char *ret; + const char *devpath, *sep; + + sep = strchr(path, ':'); + if (!sep) { + devpath = mountpoint_for_device(current_dev); + ret = join_paths(devpath, path); + } else { + /* parse just the device name into dev */ + char *tmp, *dev; + tmp = strndup(path, sep - path); + dev = parse_device_path(tmp, current_dev); + + devpath = mountpoint_for_device(dev); + ret = join_paths(devpath, sep + 1); + + free(dev); + free(tmp); + } + + return ret; +} + +void set_mount_base(const char *path) +{ + if (mount_base) + free(mount_base); + mount_base = strdup(path); +} + +char *join_paths(const char *a, const char *b) +{ + char *full_path; + + full_path = malloc(strlen(a) + strlen(b) + 2); + + strcpy(full_path, a); + if (b[0] != '/' && a[strlen(a) - 1] != '/') + strcat(full_path, "/"); + strcat(full_path, b); + + return full_path; +} + diff --git a/discover/paths.h b/discover/paths.h new file mode 100644 index 0000000..26d4ce4 --- /dev/null +++ b/discover/paths.h @@ -0,0 +1,53 @@ +#ifndef PATHS_H +#define PATHS_H + +/** + * Given a string (eg /dev/sda1, sda1 or UUID=B8E53381CA9EA0E3), parse the + * device path (eg /dev/sda1). Any device descriptions read from config files + * should be parsed into the path first. + * + * The cur_dev is provided for some remapping situations. If NULL is provided, + * no remapping will be done. + * + * Returns a newly-allocated string. + */ +char *parse_device_path(const char *dev_str, const char *current_device); + +/** + * Get the mountpoint for a device. + */ +const char *mountpoint_for_device(const char *dev); + +/** + * Resolve a path given in a config file, to a path in the local filesystem. + * Paths may be of the form: + * device:path (eg /dev/sda:/boot/vmlinux) + * + * or just a path: + * /boot/vmlinux + * - in this case, the current mountpoint is used. + * + * Returns a newly-allocated string containing a full path to the file in path + */ +char *resolve_path(const char *path, const char *current_device); + + +/** + * Set the base directory for newly-created mountpoints + */ +void set_mount_base(const char *path); + +/** + * Utility function for joining two paths. Adds a / between a and b if + * required. + * + * Returns a newly-allocated string. + */ +char *join_paths(const char *a, const char *b); + +/** + * encode a disk label (or uuid) for use in a symlink. + */ +char *encode_label(const char *label); + +#endif /* PATHS_H */ diff --git a/discover/pb-discover.c b/discover/pb-discover.c new file mode 100644 index 0000000..45b6ba1 --- /dev/null +++ b/discover/pb-discover.c @@ -0,0 +1,34 @@ + +#include +#include + +#include "udev.h" +#include "discover-server.h" +#include "waiter.h" +#include "log.h" + + +int main(void) +{ + struct discover_server *server; + struct udev *udev; + + /* we look for closed sockets when we write, so ignore SIGPIPE */ + signal(SIGPIPE, SIG_IGN); + + udev = udev_init(); + if (!udev) + return EXIT_FAILURE; + + server = discover_server_init(); + if (!server) + return EXIT_FAILURE; + + for (;;) { + if (waiter_poll()) + return EXIT_FAILURE; + } + + + return EXIT_SUCCESS; +} diff --git a/discover/pb-discover.h b/discover/pb-discover.h new file mode 100644 index 0000000..a48557c --- /dev/null +++ b/discover/pb-discover.h @@ -0,0 +1,6 @@ +#ifndef _PB_DISCOVER_H +#define _PB_DISCOVER_H + +int register_waiter(int fd, int *callback(void *), void *arg); + +#endif /* _PB_DISCOVER_H */ diff --git a/discover/udev.c b/discover/udev.c new file mode 100644 index 0000000..9c1b399 --- /dev/null +++ b/discover/udev.c @@ -0,0 +1,208 @@ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "udev.h" +#include "log.h" +#include "waiter.h" +#include "pb-discover.h" + +#define PBOOT_DEVICE_SOCKET "/tmp/petitboot.udev" + +#define max(a, b) ((a) > (b) ? (a) : (b)) + +struct udev { + int socket; +}; + +static void parse_event_params(struct udev_event *event, char *buf, int len) +{ + int param_len, name_len, value_len; + struct param *param; + char *sep; + + for (; len > 0; len -= param_len + 1, buf += param_len + 1) { + + /* find the length of the whole parameter */ + param_len = strnlen(buf, len); + if (!param_len) { + /* multiple NULs? skip over */ + param_len = 1; + continue; + } + + /* find the separator */ + sep = memchr(buf, '=', param_len); + if (!sep) + continue; + + name_len = sep - buf; + value_len = param_len - name_len - 1; + + /* update the params array */ + event->params = talloc_realloc(event, event->params, + struct param, ++event->n_params); + param = &event->params[event->n_params - 1]; + + param->name = talloc_strndup(event, buf, name_len); + param->value = talloc_strndup(event, sep + 1, value_len); + } +} + +static const char *event_param(struct udev_event *event, const char *name) +{ + int i; + + for (i = 0; i < event->n_params; i++) + if (!strcasecmp(event->params[i].name, name)) + return event->params[i].value; + + return NULL; +} + +static void print_event(struct udev_event *event) +{ + const char *action, *params[] = { + "DEVNAME", "ID_TYPE", "ID_BUS", "ID_FS_UUID", "ID_FS_LABEL", + NULL, + }; + int i; + + action = event->action == UDEV_ACTION_ADD ? "add" : "remove"; + + pb_log("udev %s event:\n", action); + printf("\tdevice: %s\n", event->device); + + for (i = 0; params[i]; i++) + printf("\t%-12s => %s\n", + params[i], event_param(event, params[i])); + +} + +static void handle_udev_message(struct udev *udev, char *buf, int len) +{ + char *sep, *device; + enum udev_action action; + struct udev_event *event; + int device_len; + + /* we should see an @\0 at the head of the buffer */ + sep = strchr(buf, '@'); + if (!sep) + return; + + /* terminate the action string */ + *sep = '\0'; + len -= sep - buf + 1; + + if (!strcmp(buf, "add")) { + action = UDEV_ACTION_ADD; + + } else if (!strcmp(buf, "remove")) { + action = UDEV_ACTION_REMOVE; + + } else { + return; + } + + /* initialise the device string */ + device = sep + 1; + device_len = strnlen(device, len); + if (!device_len) + return; + + /* now we have an action and a device, we can construct an event */ + event = talloc(udev, struct udev_event); + event->action = action; + event->device = talloc_strndup(event, device, device_len); + event->n_params = 0; + event->params = NULL; + + len -= device_len + 1; + parse_event_params(event, device + device_len + 1, len); + + print_event(event); + + talloc_free(event); + + return; +} + +static int udev_process(void *arg) +{ + struct udev *udev = arg; + char buf[4096]; + int len; + + len = recvfrom(udev->socket, buf, sizeof(buf), 0, NULL, NULL); + + if (len < 0) { + pb_log("udev socket read failed: %s", strerror(errno)); + return -1; + } + + if (len == 0) + return 0; + + handle_udev_message(udev, buf, len); + + return 0; +} + +static int udev_destructor(void *p) +{ + struct udev *udev = p; + + if (udev->socket >= 0) + close(udev->socket); + + return 0; +} + +struct udev *udev_init(void) +{ + struct sockaddr_un addr; + struct udev *udev; + + unlink(PBOOT_DEVICE_SOCKET); + + udev = talloc(NULL, struct udev); + + udev->socket = socket(PF_UNIX, SOCK_DGRAM, 0); + if (udev->socket < 0) { + pb_log("Error creating udev socket: %s\n", strerror(errno)); + goto out_err; + } + + talloc_set_destructor(udev, udev_destructor); + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, PBOOT_DEVICE_SOCKET); + + if (bind(udev->socket, (struct sockaddr *)&addr, sizeof(addr))) { + pb_log("Error binding udev socket: %s\n", strerror(errno)); + goto out_err; + } + + waiter_register(udev->socket, WAIT_IN, udev_process, udev); + + return udev; + +out_err: + talloc_free(udev); + return NULL; +} + +void udev_destroy(struct udev *udev) +{ + talloc_free(udev); +} diff --git a/discover/udev.h b/discover/udev.h new file mode 100644 index 0000000..c30adc9 --- /dev/null +++ b/discover/udev.h @@ -0,0 +1,26 @@ +#ifndef _UDEV_H +#define _UDEV_H + +enum udev_action { + UDEV_ACTION_ADD, + UDEV_ACTION_REMOVE, +}; + +struct udev_event { + enum udev_action action; + char *device; + + struct param { + char *name; + char *value; + } *params; + int n_params; +}; + +struct udev; + +struct udev *udev_init(void); + +void udev_destroy(struct udev *udev); + +#endif /* _UDEV_H */ diff --git a/discover/waiter.c b/discover/waiter.c new file mode 100644 index 0000000..21dd4a5 --- /dev/null +++ b/discover/waiter.c @@ -0,0 +1,83 @@ + +#include +#include +#include + +#include + +#include "waiter.h" + +struct waiter { + int fd; + int events; + waiter_cb callback; + void *arg; +}; + +static struct waiter *waiters; +static int n_waiters; + +struct waiter *waiter_register(int fd, int events, + waiter_cb callback, void *arg) +{ + struct waiter *waiter; + + n_waiters++; + + waiters = talloc_realloc(NULL, waiters, struct waiter, n_waiters); + waiter = &waiters[n_waiters - 1]; + + waiter->fd = fd; + waiter->events = events; + waiter->callback = callback; + waiter->arg = arg; + + return 0; +} + +void waiter_remove(struct waiter *waiter) +{ + int i; + + i = waiter - waiters; + assert(i >= 0 && i < n_waiters); + + n_waiters--; + memmove(&waiters[i], &waiters[i+1], n_waiters - i); + + waiters = talloc_realloc(NULL, waiters, struct waiter, n_waiters); +} + +int waiter_poll(void) +{ + static struct pollfd *pollfds; + static int n_pollfds; + int i, rc; + + if (n_waiters > n_pollfds) { + pollfds = talloc_realloc(NULL, pollfds, + struct pollfd, n_waiters); + } + + for (i = 0; i < n_waiters; i++) { + pollfds[i].fd = waiters[i].fd; + pollfds[i].events = waiters[i].events; + pollfds[i].revents = 0; + } + + rc = poll(pollfds, n_waiters, -1); + + if (rc <= 0) + return rc; + + for (i = 0; i < n_waiters; i++) { + if (pollfds[i].revents) { + rc = waiters[i].callback(waiters[i].arg); + + if (rc) + waiter_remove(&waiters[i]); + } + } + + return 0; +} diff --git a/discover/waiter.h b/discover/waiter.h new file mode 100644 index 0000000..ff8a5ff --- /dev/null +++ b/discover/waiter.h @@ -0,0 +1,23 @@ +#ifndef _WAITER_H +#define _WAITER_H + +#include + +struct waiter; + +enum events { + WAIT_IN = POLLIN, + WAIT_OUT = POLLOUT, +}; + +typedef int (*waiter_cb)(void *); + +struct waiter *waiter_register(int fd, int events, + waiter_cb callback, void *arg); + +void waiter_remove(struct waiter *waiter); + +int waiter_poll(void); +#endif /* _WAITER_H */ + + diff --git a/discover/yaboot-cfg.c b/discover/yaboot-cfg.c new file mode 100644 index 0000000..6b007e4 --- /dev/null +++ b/discover/yaboot-cfg.c @@ -0,0 +1,491 @@ +/* + * cfg.c - Handling and parsing of yaboot.conf + * + * Copyright (C) 1995 Werner Almesberger + * 1996 Jakub Jelinek + * + * 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 +#include +#include +#include +#include +#include + +#define prom_printf printf +#define prom_putchar putchar +#define prom_vprintf vprintf + +/* Imported functions */ +extern int strcasecmp(const char *s1, const char *s2); + +typedef enum { + cft_strg, cft_flag, cft_end +} CONFIG_TYPE; + +typedef struct { + CONFIG_TYPE type; + char *name; + void *data; +} CONFIG; + +#define MAX_TOKEN 200 +#define MAX_VAR_NAME MAX_TOKEN +char *cfg_get_default (void); + +CONFIG cf_options[] = +{ + {cft_strg, "device", NULL}, + {cft_strg, "partition", NULL}, + {cft_strg, "default", NULL}, + {cft_strg, "timeout", NULL}, + {cft_strg, "password", NULL}, + {cft_flag, "restricted", NULL}, + {cft_strg, "message", NULL}, + {cft_strg, "root", NULL}, + {cft_strg, "ramdisk", NULL}, + {cft_flag, "read-only", NULL}, + {cft_flag, "read-write", NULL}, + {cft_strg, "append", NULL}, + {cft_strg, "initrd", NULL}, + {cft_flag, "initrd-prompt", NULL}, + {cft_strg, "initrd-size", NULL}, + {cft_flag, "pause-after", NULL}, + {cft_strg, "pause-message", NULL}, + {cft_strg, "init-code", NULL}, + {cft_strg, "init-message", NULL}, + {cft_strg, "fgcolor", NULL}, + {cft_strg, "bgcolor", NULL}, + {cft_strg, "ptypewarning", NULL}, + {cft_end, NULL, NULL}}; + +CONFIG cf_image[] = +{ + {cft_strg, "image", NULL}, + {cft_strg, "label", NULL}, + {cft_strg, "alias", NULL}, + {cft_flag, "single-key", NULL}, + {cft_flag, "restricted", NULL}, + {cft_strg, "device", NULL}, + {cft_strg, "partition", NULL}, + {cft_strg, "root", NULL}, + {cft_strg, "ramdisk", NULL}, + {cft_flag, "read-only", NULL}, + {cft_flag, "read-write", NULL}, + {cft_strg, "append", NULL}, + {cft_strg, "literal", NULL}, + {cft_strg, "initrd", NULL}, + {cft_flag, "initrd-prompt", NULL}, + {cft_strg, "initrd-size", NULL}, + {cft_flag, "pause-after", NULL}, + {cft_strg, "pause-message", NULL}, + {cft_flag, "novideo", NULL}, + {cft_strg, "sysmap", NULL}, + {cft_end, NULL, NULL}}; + +static char flag_set; +static char *last_token = NULL, *last_item = NULL, *last_value = NULL; +static int line_num; +static int back = 0; /* can go back by one char */ +static char *currp = NULL; +static char *endp = NULL; +static char *file_name = NULL; +static CONFIG *curr_table = cf_options; +static jmp_buf env; + +static struct IMAGES { + CONFIG table[sizeof (cf_image) / sizeof (cf_image[0])]; + struct IMAGES *next; +} *images = NULL; + +void cfg_error (char *msg,...) +{ + va_list ap; + + va_start (ap, msg); + prom_printf ("Config file error: "); + prom_vprintf (msg, ap); + va_end (ap); + prom_printf (" near line %d in file %s\n", line_num, file_name); + longjmp (env, 1); +} + +void cfg_warn (char *msg,...) +{ + va_list ap; + + va_start (ap, msg); + prom_printf ("Config file warning: "); + prom_vprintf (msg, ap); + va_end (ap); + prom_printf (" near line %d in file %s\n", line_num, file_name); +} + +int cfg_getc () +{ + if (currp == endp) + return EOF; + return *currp++; +} + +#define next_raw next +static int next (void) +{ + int ch; + + if (!back) + return cfg_getc (); + ch = back; + back = 0; + return ch; +} + +static void again (int ch) +{ + back = ch; +} + +static char *cfg_get_token (void) +{ + char buf[MAX_TOKEN + 1]; + char *here; + int ch, escaped; + + if (last_token) { + here = last_token; + last_token = NULL; + return here; + } + while (1) { + while (ch = next (), ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') + if (ch == '\n' || ch == '\r') + line_num++; + if (ch == EOF || ch == (int)NULL) + return NULL; + if (ch != '#') + break; + while (ch = next_raw (), (ch != '\n' && ch != '\r')) + if (ch == EOF) + return NULL; + line_num++; + } + if (ch == '=') + return strdup ("="); + if (ch == '"') { + here = buf; + while (here - buf < MAX_TOKEN) { + if ((ch = next ()) == EOF) + cfg_error ("EOF in quoted string"); + if (ch == '"') { + *here = 0; + return strdup (buf); + } + if (ch == '\\') { + ch = next (); + switch (ch) { + case '"': + case '\\': + break; + case '\n': + case '\r': + while ((ch = next ()), ch == ' ' || ch == '\t'); + if (!ch) + continue; + again (ch); + ch = ' '; + break; + case 'n': + ch = '\n'; + break; + default: + cfg_error ("Bad use of \\ in quoted string"); + } + } else if ((ch == '\n') || (ch == '\r')) + cfg_error ("newline is not allowed in quoted strings"); + *here++ = ch; + } + cfg_error ("Quoted string is too long"); + return 0; /* not reached */ + } + here = buf; + escaped = 0; + while (here - buf < MAX_TOKEN) { + if (escaped) { + if (ch == EOF) + cfg_error ("\\ precedes EOF"); + if (ch == '\n') + line_num++; + else + *here++ = ch == '\t' ? ' ' : ch; + escaped = 0; + } else { + if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '#' || + ch == '=' || ch == EOF) { + again (ch); + *here = 0; + return strdup (buf); + } + if (!(escaped = (ch == '\\'))) + *here++ = ch; + } + ch = next (); + } + cfg_error ("Token is too long"); + return 0; /* not reached */ +} + +static void cfg_return_token (char *token) +{ + last_token = token; +} + +static int cfg_next (char **item, char **value) +{ + char *this; + + if (last_item) { + *item = last_item; + *value = last_value; + last_item = NULL; + return 1; + } + *value = NULL; + if (!(*item = cfg_get_token ())) + return 0; + if (!strcmp (*item, "=")) + cfg_error ("Syntax error"); + if (!(this = cfg_get_token ())) + return 1; + if (strcmp (this, "=")) { + cfg_return_token (this); + return 1; + } + if (!(*value = cfg_get_token ())) + cfg_error ("Value expected at EOF"); + if (!strcmp (*value, "=")) + cfg_error ("Syntax error after %s", *item); + return 1; +} + +#if 0 +// The one and only call to this procedure is commented out +// below, so we don't need this unless we decide to use it again. +static void cfg_return (char *item, char *value) +{ + last_item = item; + last_value = value; +} +#endif + +static int cfg_set (char *item, char *value) +{ + CONFIG *walk; + + if (!strcasecmp (item, "image")) { + struct IMAGES **p = &images; + + while (*p) + p = &((*p)->next); + *p = (struct IMAGES *)malloc (sizeof (struct IMAGES)); + if (*p == NULL) { + prom_printf("malloc error in cfg_set\n"); + return -1; + } + (*p)->next = 0; + curr_table = ((*p)->table); + memcpy (curr_table, cf_image, sizeof (cf_image)); + } + for (walk = curr_table; walk->type != cft_end; walk++) { + if (walk->name && !strcasecmp (walk->name, item)) { + if (value && walk->type != cft_strg) + cfg_warn ("'%s' doesn't have a value", walk->name); + else if (!value && walk->type == cft_strg) + cfg_warn ("Value expected for '%s'", walk->name); + else { + if (walk->data) + cfg_warn ("Duplicate entry '%s'", walk->name); + if (walk->type == cft_flag) + walk->data = &flag_set; + else if (walk->type == cft_strg) + walk->data = value; + } + break; + } + } + if (walk->type != cft_end) + return 1; +// cfg_return (item, value); + return 0; +} + +int cfg_parse (char *cfg_file, char *buff, int len) +{ + char *item, *value; + + file_name = cfg_file; + currp = buff; + endp = currp + len; + + if (setjmp (env)) + return -1; + while (1) { + if (!cfg_next (&item, &value)) + return 0; + if (!cfg_set (item, value)) { +#if DEBUG + prom_printf("Can't set item %s to value %s\n", item, value); +#endif + } + free (item); + } +} + +static char *cfg_get_strg_i (CONFIG * table, char *item) +{ + CONFIG *walk; + + for (walk = table; walk->type != cft_end; walk++) + if (walk->name && !strcasecmp (walk->name, item)) + return walk->data; + return 0; +} + +char *cfg_get_strg (char *image, char *item) +{ + struct IMAGES *p; + char *label, *alias; + char *ret; + + if (!image) + return cfg_get_strg_i (cf_options, item); + for (p = images; p; p = p->next) { + label = cfg_get_strg_i (p->table, "label"); + if (!label) { + label = cfg_get_strg_i (p->table, "image"); + alias = strrchr (label, '/'); + if (alias) + label = alias + 1; + } + alias = cfg_get_strg_i (p->table, "alias"); + if (!strcmp (label, image) || (alias && !strcmp (alias, image))) { + ret = cfg_get_strg_i (p->table, item); + if (!ret) + ret = cfg_get_strg_i (cf_options, item); + return ret; + } + } + return 0; +} + +int cfg_get_flag (char *image, char *item) +{ + return !!cfg_get_strg (image, item); +} + +static int printl_count = 0; +static void printlabel (char *label, int defflag) +{ + int len = strlen (label); + + if (!printl_count) + prom_printf ("\n"); + prom_printf ("%s %s",defflag?"*":" ", label); + while (len++ < 25) + prom_putchar (' '); + printl_count++; + if (printl_count == 3) + printl_count = 0; +} + +void cfg_print_images (void) +{ + struct IMAGES *p; + char *label, *alias; + + char *ret = cfg_get_default();//strg_i (cf_options, "default"); + int defflag=0; + + printl_count = 0; + for (p = images; p; p = p->next) { + label = cfg_get_strg_i (p->table, "label"); + if (!label) { + label = cfg_get_strg_i (p->table, "image"); + alias = strrchr (label, '/'); + if (alias) + label = alias + 1; + } + if(!strcmp(ret,label)) + defflag=1; + else + defflag=0; + alias = cfg_get_strg_i (p->table, "alias"); + printlabel (label, defflag); + if (alias) + printlabel (alias, 0); + } + prom_printf("\n"); +} + +char *cfg_get_default (void) +{ + char *label; + char *ret = cfg_get_strg_i (cf_options, "default"); + + if (ret) + return ret; + if (!images) + return 0; + ret = cfg_get_strg_i (images->table, "label"); + if (!ret) { + ret = cfg_get_strg_i (images->table, "image"); + label = strrchr (ret, '/'); + if (label) + ret = label + 1; + } + return ret; +} + +char *cfg_next_image(char *prev) +{ + struct IMAGES *p; + char *label, *alias; + int wantnext = 0; + + if (!prev) + wantnext = 1; + + for (p = images; p; p = p->next) { + label = cfg_get_strg_i (p->table, "label"); + if (!label) { + label = cfg_get_strg_i (p->table, "image"); + alias = strrchr (label, '/'); + if (alias) + label = alias + 1; + } + if (wantnext) + return label; + if (!strcmp(prev, label)) + wantnext = 1; + } + return NULL; +} +/* + * Local variables: + * c-file-style: "k&r" + * c-basic-offset: 5 + * End: + */ diff --git a/discover/yaboot-cfg.h b/discover/yaboot-cfg.h new file mode 100644 index 0000000..2ab4fec --- /dev/null +++ b/discover/yaboot-cfg.h @@ -0,0 +1,30 @@ +/* + * cfg.h - config file parsing definitions + * + * Copyright (C) 1999 Benjamin Herrenschmidt + * + * 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. + */ + +#ifndef CFG_H +#define CFG_H + +extern int cfg_parse(char *cfg_file, char *buff, int len); +extern char* cfg_get_strg(char *image, char *item); +extern int cfg_get_flag(char *image, char *item); +extern void cfg_print_images(void); +extern char* cfg_get_default(void); +extern char* cfg_next_image(char *); +#endif diff --git a/discover/yaboot-parser.c b/discover/yaboot-parser.c new file mode 100644 index 0000000..27b4b78 --- /dev/null +++ b/discover/yaboot-parser.c @@ -0,0 +1,235 @@ + +#include "parser.h" +#include "params.h" +#include "paths.h" +#include "yaboot-cfg.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct device *dev; +static char *devpath; +static char *defimage; + +char * +make_params(char *label, char *params) +{ + char *p, *q; + static char buffer[2048]; + + q = buffer; + *q = 0; + + p = cfg_get_strg(label, "literal"); + if (p) { + strcpy(q, p); + q = strchr(q, 0); + if (params) { + if (*p) + *q++ = ' '; + strcpy(q, params); + } + return buffer; + } + + p = cfg_get_strg(label, "root"); + if (p) { + strcpy (q, "root="); + strcpy (q + 5, p); + q = strchr (q, 0); + *q++ = ' '; + } + if (cfg_get_flag(label, "read-only")) { + strcpy (q, "ro "); + q += 3; + } + if (cfg_get_flag(label, "read-write")) { + strcpy (q, "rw "); + q += 3; + } + p = cfg_get_strg(label, "ramdisk"); + if (p) { + strcpy (q, "ramdisk="); + strcpy (q + 8, p); + q = strchr (q, 0); + *q++ = ' '; + } + p = cfg_get_strg(label, "initrd-size"); + if (p) { + strcpy (q, "ramdisk_size="); + strcpy (q + 13, p); + q = strchr (q, 0); + *q++ = ' '; + } + if (cfg_get_flag(label, "novideo")) { + strcpy (q, "video=ofonly"); + q = strchr (q, 0); + *q++ = ' '; + } + p = cfg_get_strg (label, "append"); + if (p) { + strcpy (q, p); + q = strchr (q, 0); + *q++ = ' '; + } + *q = 0; + if (params) + strcpy(q, params); + + return buffer; +} + +static 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); +} + +void process_image(char *label) +{ + struct boot_option opt; + char *cfgopt; + + memset(&opt, 0, sizeof(opt)); + + opt.name = label; + cfgopt = cfg_get_strg(label, "image"); + opt.boot_image_file = resolve_path(cfgopt, devpath); + if (cfgopt == defimage) + pb_log("This one is default. What do we do about it?\n"); + + cfgopt = cfg_get_strg(label, "initrd"); + if (cfgopt) + opt.initrd_file = resolve_path(cfgopt, devpath); + + opt.boot_args = make_params(label, NULL); + + add_boot_option(&opt); + + if (opt.initrd_file) + free(opt.initrd_file); +} + +static int yaboot_parse(const char *device) +{ + char *filepath; + char *conf_file; + char *tmpstr; + ssize_t conf_len; + int fd; + struct stat st; + char *label; + + devpath = strdup(device); + + filepath = resolve_path("/etc/yaboot.conf", devpath); + + fd = open(filepath, O_RDONLY); + if (fd < 0) { + free(filepath); + filepath = resolve_path("/yaboot.conf", devpath); + fd = open(filepath, O_RDONLY); + + if (fd < 0) + return 0; + } + + if (fstat(fd, &st)) { + close(fd); + return 0; + } + + conf_file = malloc(st.st_size+1); + if (!conf_file) { + close(fd); + return 0; + } + + conf_len = read(fd, conf_file, st.st_size); + if (conf_len < 0) { + close(fd); + return 0; + } + conf_file[conf_len] = 0; + + close(fd); + + if (cfg_parse(filepath, conf_file, conf_len)) { + pb_log("Error parsing yaboot.conf\n"); + return 0; + } + + free(filepath); + + dev = malloc(sizeof(*dev)); + memset(dev, 0, sizeof(*dev)); + dev->id = strdup(devpath); + if (cfg_get_strg(0, "init-message")) { + char *newline; + dev->description = strdup(cfg_get_strg(0, "init-message")); + newline = strchr(dev->description, '\n'); + if (newline) + *newline = 0; + } + dev->icon_file = strdup(generic_icon_file(guess_device_type())); + + /* If we have a 'partiton=' directive, update the default devpath + * to use that instead of the current device */ + tmpstr = cfg_get_strg(0, "partition"); + if (tmpstr) { + char *endp; + int partnr = strtol(tmpstr, &endp, 10); + if (endp != tmpstr && !*endp) { + char *new_dev, *tmp; + + new_dev = malloc(strlen(devpath) + strlen(tmpstr) + 1); + if (!new_dev) + return 0; + + strcpy(new_dev, devpath); + + /* Strip digits (partition number) from string */ + endp = new_dev + strlen(devpath) - 1; + while (isdigit(*endp)) + *(endp--) = 0; + + /* and add our own... */ + sprintf(endp + 1, "%d", partnr); + + tmp = devpath; + devpath = parse_device_path(new_dev, devpath); + free(tmp); + free(new_dev); + } + } + + defimage = cfg_get_default(); + if (!defimage) + return 0; + defimage = cfg_get_strg(defimage, "image"); + + label = cfg_next_image(NULL); + if (!label || !check_and_add_device(dev)) + return 0; + + do { + process_image(label); + } while ((label = cfg_next_image(label))); + + return 1; +} + +struct parser yaboot_parser = { + .name = "yaboot.conf parser", + .priority = 99, + .parse = yaboot_parse +}; diff --git a/lib/list/list.c b/lib/list/list.c new file mode 100644 index 0000000..d9bc532 --- /dev/null +++ b/lib/list/list.c @@ -0,0 +1,24 @@ + +#include "list/list.h" + +void list_init(struct list *list) +{ + list->head.next = &list->head; + list->head.prev = &list->head; +} + +void list_add(struct list *list, struct list_item *new) +{ + new->next = list->head.next; + new->prev = &list->head; + + list->head.next->prev = new; + list->head.next = new; +} + +void list_remove(struct list_item *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; +} + diff --git a/lib/list/list.h b/lib/list/list.h new file mode 100644 index 0000000..3858cf6 --- /dev/null +++ b/lib/list/list.h @@ -0,0 +1,39 @@ +#ifndef _LIST_H +#define _LIST_H + +struct list_item { + struct list_item *prev, *next; +}; + +struct list { + struct list_item head; +}; + +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#endif + +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#define list_for_each(list, pos) \ + for (pos = (list)->head.next; pos != ((list)->head); pos = pos->next) + +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +#define list_for_each_entry(list, pos, member) \ + for (pos = list_entry((list)->head.next, typeof(*pos), member); \ + &pos->member != &(list)->head; \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +void list_init(struct list *list); + +void list_add(struct list *list, struct list_item *item); + +void list_remove(struct list_item *item); + +#endif /* _LIST_H */ diff --git a/lib/pb-protocol/pb-protocol.c b/lib/pb-protocol/pb-protocol.c new file mode 100644 index 0000000..2fd76c5 --- /dev/null +++ b/lib/pb-protocol/pb-protocol.c @@ -0,0 +1,307 @@ + +#include +#include +#include + +#include + +#include "pb-protocol.h" + + +/* Message format: + * + * 4-byte action, determines the remaining message content + * 4-byte total payload len + * - not including action and payload len header + * + * action = 0x1: device add message + * payload: + * 4-byte len, id + * 4-byte len, name + * 4-byte len, description + * 4-byte len, icon_file + * + * 4-byte option count + * for each option: + * 4-byte len, id + * 4-byte len, name + * 4-byte len, description + * 4-byte len, icon_file + * 4-byte len, boot_image_file + * 4-byte len, initrd_file + * 4-byte len, boot_args + * + * action = 0x2: device remove message + * payload: + * 4-byte len, id + */ + + +/* Write a string into the buffer, starting at pos. + * + * Returns the total length used for the write, including length header. + */ +int pb_protocol_serialise_string(char *pos, const char *str) +{ + int len = 0; + + if (str) + len = strlen(str); + + *(uint32_t *)pos = __cpu_to_be32(len); + pos += sizeof(uint32_t); + + memcpy(pos, str, len); + + return len + sizeof(uint32_t); +} + +/* Read a string from a buffer, allocating the new string as necessary. + * + * @param[in] ctx The talloc context to base the allocation on + * @param[in,out] pos Where to start reading + * @param[in,out] len The amount of data remaining in the buffer + * @param[out] str Pointer to resuling string + * @return zero on success, non-zero on failure + */ +static int read_string(void *ctx, char **pos, int *len, char **str) +{ + uint32_t str_len, read_len; + + if (*len < sizeof(uint32_t)) + return -1; + + str_len = __be32_to_cpu(*(uint32_t *)(*pos)); + read_len = sizeof(uint32_t); + + if (read_len + str_len > *len) + return -1; + + if (str_len == 0) + *str = NULL; + else + *str = talloc_strndup(ctx, *pos + read_len, str_len); + + read_len += str_len; + + /* all ok, update the caller's pointers */ + *pos += read_len; + *len -= read_len; + + return 0; +} + +char *pb_protocol_deserialise_string(void *ctx, + struct pb_protocol_message *message) +{ + char *buf, *str; + int len; + + len = message->payload_len; + buf = message->payload; + + if (read_string(ctx, &buf, &len, &str)) + return NULL; + + return str; +} + +static int optional_strlen(const char *str) +{ + if (!str) + return 0; + return strlen(str); +} + +int pb_protocol_device_len(struct device *dev) +{ + int len, i; + + len = 4 + optional_strlen(dev->id) + + 4 + optional_strlen(dev->name) + + 4 + optional_strlen(dev->description) + + 4 + optional_strlen(dev->icon_file) + + 4; + + for (i = 0; i < dev->n_options; i++) { + struct boot_option *opt = &dev->options[i]; + len += 4 + optional_strlen(opt->id) + + 4 + optional_strlen(opt->name) + + 4 + optional_strlen(opt->description) + + 4 + optional_strlen(opt->icon_file) + + 4 + optional_strlen(opt->boot_image_file) + + 4 + optional_strlen(opt->initrd_file) + + 4 + optional_strlen(opt->boot_args); + } + + return len; +} + +int pb_protocol_serialise_device(struct device *dev, char *buf, int buf_len) +{ + char *pos; + int i; + + pos = buf; + + /* construct payload into buffer */ + pos += pb_protocol_serialise_string(pos, dev->id); + pos += pb_protocol_serialise_string(pos, dev->name); + pos += pb_protocol_serialise_string(pos, dev->description); + pos += pb_protocol_serialise_string(pos, dev->icon_file); + + /* write option count */ + *(uint32_t *)pos = __cpu_to_be32(dev->n_options); + pos += sizeof(uint32_t); + + /* write each option */ + for (i = 0; i < dev->n_options; i++) { + struct boot_option *opt = &dev->options[i]; + pos += pb_protocol_serialise_string(pos, opt->id); + pos += pb_protocol_serialise_string(pos, opt->name); + pos += pb_protocol_serialise_string(pos, opt->description); + pos += pb_protocol_serialise_string(pos, opt->icon_file); + pos += pb_protocol_serialise_string(pos, opt->boot_image_file); + pos += pb_protocol_serialise_string(pos, opt->initrd_file); + pos += pb_protocol_serialise_string(pos, opt->boot_args); + } + + return 0; +} + +int pb_protocol_write_message(int fd, struct pb_protocol_message *message) +{ + int total_len, rc; + char *pos; + + total_len = sizeof(*message) + message->payload_len; + + message->payload_len = __cpu_to_be32(message->payload_len); + message->action = __cpu_to_be32(message->action); + + for (pos = (void *)message; total_len;) { + rc = write(fd, pos, total_len); + + if (rc <= 0) + break; + + total_len -= rc; + pos += rc; + } + + talloc_free(message); + + return total_len ? -1 : 0; +} + +struct pb_protocol_message *pb_protocol_create_message(void *ctx, + int action, int payload_len) +{ + struct pb_protocol_message *message; + + if (payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) + return NULL; + + message = talloc_size(ctx, sizeof(*message) + payload_len); + + /* we convert these to big-endian in write_message() */ + message->action = action; + message->payload_len = payload_len; + + return message; + +} + +struct pb_protocol_message *pb_protocol_read_message(void *ctx, int fd) +{ + struct pb_protocol_message *message, m; + int rc, len; + + /* use the stack for the initial 8-byte read */ + + rc = read(fd, &m, sizeof(m)); + if (rc != sizeof(m)) + return NULL; + + m.payload_len = __be32_to_cpu(m.payload_len); + m.action = __be32_to_cpu(m.action); + + if (m.payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) + return NULL; + + message = talloc_size(ctx, sizeof(m) + m.payload_len); + memcpy(message, &m, sizeof(m)); + + for (len = 0; len < m.payload_len;) { + rc = read(fd, message->payload + len, m.payload_len - len); + + if (rc <= 0) { + talloc_free(message); + return NULL; + } + + len += rc; + } + + return message; +} + + +struct device *pb_protocol_deserialise_device(void *ctx, + struct pb_protocol_message *message) +{ + struct device *dev; + char *pos; + int i, len; + + len = message->payload_len; + pos = message->payload; + + dev = talloc(ctx, struct device); + + if (read_string(dev, &pos, &len, &dev->id)) + goto out_err; + + if (read_string(dev, &pos, &len, &dev->name)) + goto out_err; + + if (read_string(dev, &pos, &len, &dev->description)) + goto out_err; + + if (read_string(dev, &pos, &len, &dev->icon_file)) + goto out_err; + + dev->n_options = __be32_to_cpu(*(uint32_t *)pos); + dev->options = talloc_array(dev, struct boot_option, dev->n_options); + pos += sizeof(uint32_t); + + for (i = 0; i < dev->n_options; i++) { + struct boot_option *opt = &dev->options[i]; + + if (read_string(opt, &pos, &len, &opt->id)) + goto out_err; + if (read_string(opt, &pos, &len, &opt->name)) + goto out_err; + if (read_string(opt, &pos, &len, + &opt->description)) + goto out_err; + if (read_string(opt, &pos, &len, + &opt->icon_file)) + goto out_err; + if (read_string(opt, &pos, &len, + &opt->boot_image_file)) + goto out_err; + if (read_string(opt, &pos, &len, + &opt->initrd_file)) + goto out_err; + if (read_string(opt, &pos, &len, + &opt->boot_args)) + goto out_err; + } + + return dev; + +out_err: + talloc_free(dev); + return NULL; +} diff --git a/lib/pb-protocol/pb-protocol.h b/lib/pb-protocol/pb-protocol.h new file mode 100644 index 0000000..7b557d6 --- /dev/null +++ b/lib/pb-protocol/pb-protocol.h @@ -0,0 +1,57 @@ +#ifndef _PB_PROTOCOL_H +#define _PB_PROTOCOL_H + +#include + +#define PB_SOCKET_PATH "/tmp/petitboot.ui" + +#define PB_PROTOCOL_MAX_PAYLOAD_SIZE 4096 + +enum pb_protocol_action { + PB_PROTOCOL_ACTION_ADD = 0x1, + PB_PROTOCOL_ACTION_REMOVE = 0x2, +}; + +struct pb_protocol_message { + uint32_t action; + uint32_t payload_len; + char payload[]; +}; + +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; + } *options; + int n_options; +}; + +int pb_protocol_device_len(struct device *dev); + +int pb_protocol_serialise_string(char *pos, const char *str); +char *pb_protocol_deserialise_string(void *ctx, + struct pb_protocol_message *message); + +int pb_protocol_serialise_device(struct device *dev, char *buf, int buf_len); + +int pb_protocol_write_message(int fd, struct pb_protocol_message *message); + +struct pb_protocol_message *pb_protocol_create_message(void *ctx, + int action, int payload_len); + +struct pb_protocol_message *pb_protocol_read_message(void *ctx, int fd); + +struct device *pb_protocol_deserialise_device(void *ctx, + struct pb_protocol_message *message); + +#endif /* _PB_PROTOCOL_H */ diff --git a/lib/talloc/talloc.c b/lib/talloc/talloc.c new file mode 100644 index 0000000..c660870 --- /dev/null +++ b/lib/talloc/talloc.c @@ -0,0 +1,1130 @@ +/* + Samba Unix SMB/CIFS implementation. + + Samba trivial allocation library - new interface + + NOTE: Please read talloc_guide.txt for full documentation + + Copyright (C) Andrew Tridgell 2004 + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + inspired by http://swapped.cc/halloc/ +*/ + + +#include "config.h" + +#include +#include +#include + +/* nfsim additions */ +#define HAVE_SYS_TYPES_H +#define HAVE_UNISTD_H +#define HAVE_STDARG_H +#define HAVE_STDINT_H +#define HAVE_VA_COPY + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_STDARG_H +#include +#else +#include +#endif + +#ifdef HAVE_STDINT_H +#include +#endif + +#include "talloc.h" + +/* use this to force every realloc to change the pointer, to stress test + code that might not cope */ +#define ALWAYS_REALLOC 0 + + +#define MAX_TALLOC_SIZE 0x10000000 +#define TALLOC_MAGIC 0xe814ec4f +#define TALLOC_MAGIC_FREE 0x7faebef3 +#define TALLOC_MAGIC_REFERENCE ((const char *)1) + +/* by default we abort when given a bad pointer (such as when talloc_free() is + * called on a pointer that came from malloc() */ +#ifndef TALLOC_ABORT +#define TALLOC_ABORT(reason) abort() +#endif + +#ifndef discard_const_p +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) +# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr))) +#else +# define discard_const_p(type, ptr) ((type *)(ptr)) +#endif +#endif + +/* this null_context is only used if talloc_enable_leak_report() or + talloc_enable_leak_report_full() is called, otherwise it remains + NULL +*/ +static const void *null_context; +static void *cleanup_context; + + +struct talloc_reference_handle { + struct talloc_reference_handle *next, *prev; + void *ptr; +}; + +typedef int (*talloc_destructor_t)(void *); + +struct talloc_chunk { + struct talloc_chunk *next, *prev; + struct talloc_chunk *parent, *child; + struct talloc_reference_handle *refs; + size_t size; + talloc_destructor_t destructor; + const char *name; + union { + unsigned magic; + double align_dummy; + } u; +}; + +/* panic if we get a bad magic value */ +static struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) +{ + struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, ptr)-1; + if (tc->u.magic != TALLOC_MAGIC) { + if (tc->u.magic == TALLOC_MAGIC_FREE) { + TALLOC_ABORT("Bad talloc magic value - double free"); + } else { + TALLOC_ABORT("Bad talloc magic value - unknown value"); + } + } + + return tc; +} + +/* hook into the front of the list */ +#define _TLIST_ADD(list, p) \ +do { \ + if (!(list)) { \ + (list) = (p); \ + (p)->next = (p)->prev = NULL; \ + } else { \ + (list)->prev = (p); \ + (p)->next = (list); \ + (p)->prev = NULL; \ + (list) = (p); \ + }\ +} while (0) + +/* remove an element from a list - element doesn't have to be in list. */ +#define _TLIST_REMOVE(list, p) \ +do { \ + if ((p) == (list)) { \ + (list) = (p)->next; \ + if (list) (list)->prev = NULL; \ + } else { \ + if ((p)->prev) (p)->prev->next = (p)->next; \ + if ((p)->next) (p)->next->prev = (p)->prev; \ + } \ + if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \ +} while (0) + + +/* + return the parent chunk of a pointer +*/ +static struct talloc_chunk *talloc_parent_chunk(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + while (tc->prev) tc=tc->prev; + return tc->parent; +} + +void *talloc_parent(const void *ptr) +{ + struct talloc_chunk *tc = talloc_parent_chunk(ptr); + return (void *)(tc+1); +} + +/* + Allocate a bit of memory as a child of an existing pointer +*/ +void *_talloc(const void *context, size_t size) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + context = null_context; + } + + if (size >= MAX_TALLOC_SIZE) { + return NULL; + } + + tc = malloc(sizeof(*tc)+size); + if (tc == NULL) return NULL; + + tc->size = size; + tc->u.magic = TALLOC_MAGIC; + tc->destructor = NULL; + tc->child = NULL; + tc->name = NULL; + tc->refs = NULL; + + if (context) { + struct talloc_chunk *parent = talloc_chunk_from_ptr(context); + + tc->parent = parent; + + if (parent->child) { + parent->child->parent = NULL; + } + + _TLIST_ADD(parent->child, tc); + } else { + tc->next = tc->prev = tc->parent = NULL; + } + + return (void *)(tc+1); +} + + +/* + setup a destructor to be called on free of a pointer + the destructor should return 0 on success, or -1 on failure. + if the destructor fails then the free is failed, and the memory can + be continued to be used +*/ +void talloc_set_destructor(const void *ptr, int (*destructor)(void *)) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->destructor = destructor; +} + +/* + increase the reference count on a piece of memory. +*/ +void talloc_increase_ref_count(const void *ptr) +{ + talloc_reference(null_context, ptr); +} + +/* + helper for talloc_reference() +*/ +static int talloc_reference_destructor(void *ptr) +{ + struct talloc_reference_handle *handle = ptr; + struct talloc_chunk *tc1 = talloc_chunk_from_ptr(ptr); + struct talloc_chunk *tc2 = talloc_chunk_from_ptr(handle->ptr); + if (tc1->destructor != (talloc_destructor_t)-1) { + tc1->destructor = NULL; + } + _TLIST_REMOVE(tc2->refs, handle); + talloc_free(handle); + return 0; +} + +/* + make a secondary reference to a pointer, hanging off the given context. + the pointer remains valid until both the original caller and this given + context are freed. + + the major use for this is when two different structures need to reference the + same underlying data, and you want to be able to free the two instances + separately, and in either order +*/ +void *talloc_reference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc; + struct talloc_reference_handle *handle; + if (ptr == NULL) return NULL; + + tc = talloc_chunk_from_ptr(ptr); + handle = talloc_named_const(context, sizeof(*handle), + TALLOC_MAGIC_REFERENCE); + + if (handle == NULL) return NULL; + + /* note that we hang the destructor off the handle, not the + main context as that allows the caller to still setup their + own destructor on the context if they want to */ + talloc_set_destructor(handle, talloc_reference_destructor); + handle->ptr = discard_const_p(void, ptr); + _TLIST_ADD(tc->refs, handle); + return handle->ptr; +} + +/* + remove a secondary reference to a pointer. This undo's what + talloc_reference() has done. The context and pointer arguments + must match those given to a talloc_reference() +*/ +static int talloc_unreference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + + if (context == NULL) { + context = null_context; + } + + for (h=tc->refs;h;h=h->next) { + struct talloc_chunk *p = talloc_parent_chunk(h); + if ((p==NULL && context==NULL) || p+1 == context) break; + } + if (h == NULL) { + return -1; + } + + talloc_set_destructor(h, NULL); + _TLIST_REMOVE(tc->refs, h); + talloc_free(h); + return 0; +} + +/* + remove a specific parent context from a pointer. This is a more + controlled varient of talloc_free() +*/ +int talloc_unlink(const void *context, void *ptr) +{ + struct talloc_chunk *tc_p, *new_p; + void *new_parent; + + if (ptr == NULL) { + return -1; + } + + if (context == NULL) { + context = null_context; + } + + if (talloc_unreference(context, ptr) == 0) { + return 0; + } + + if (context == NULL) { + if (talloc_parent_chunk(ptr) != NULL) { + return -1; + } + } else { + if (talloc_chunk_from_ptr(context) + != talloc_parent_chunk(ptr)) { + return -1; + } + } + + tc_p = talloc_chunk_from_ptr(ptr); + + if (tc_p->refs == NULL) { + return talloc_free(ptr); + } + + new_p = talloc_parent_chunk(tc_p->refs); + if (new_p) { + new_parent = new_p+1; + } else { + new_parent = NULL; + } + + if (talloc_unreference(new_parent, ptr) != 0) { + return -1; + } + + talloc_steal(new_parent, ptr); + + return 0; +} + +/* + add a name to an existing pointer - va_list version +*/ +static void talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) + PRINTF_ATTRIBUTE(2,0); + +static void talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = talloc_vasprintf(ptr, fmt, ap); + if (tc->name) { + talloc_set_name_const(tc->name, ".name"); + } +} + +/* + add a name to an existing pointer +*/ +void talloc_set_name(const void *ptr, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + talloc_set_name_v(ptr, fmt, ap); + va_end(ap); +} + +/* + more efficient way to add a name to a pointer - the name must point to a + true string constant +*/ +void talloc_set_name_const(const void *ptr, const char *name) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = name; +} + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +void *talloc_named(const void *context, size_t size, const char *fmt, ...) +{ + va_list ap; + void *ptr; + + ptr = _talloc(context, size); + if (ptr == NULL) return NULL; + + va_start(ap, fmt); + talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + return ptr; +} + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +void *talloc_named_const(const void *context, size_t size, const char *name) +{ + void *ptr; + + ptr = _talloc(context, size); + if (ptr == NULL) { + return NULL; + } + + talloc_set_name_const(ptr, name); + + return ptr; +} + +/* + return the name of a talloc ptr, or "UNNAMED" +*/ +const char *talloc_get_name(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + if (tc->name == TALLOC_MAGIC_REFERENCE) { + return ".reference"; + } + if (tc->name) { + return tc->name; + } + return "UNNAMED"; +} + + +/* + check if a pointer has the given name. If it does, return the pointer, + otherwise return NULL +*/ +void *talloc_check_name(const void *ptr, const char *name) +{ + const char *pname; + if (ptr == NULL) return NULL; + pname = talloc_get_name(ptr); + if (pname == name || strcmp(pname, name) == 0) { + return discard_const_p(void, ptr); + } + return NULL; +} + + +/* + this is for compatibility with older versions of talloc +*/ +void *talloc_init(const char *fmt, ...) +{ + va_list ap; + void *ptr; + + ptr = _talloc(NULL, 0); + if (ptr == NULL) return NULL; + + va_start(ap, fmt); + talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + return ptr; +} + +/* + this is a replacement for the Samba3 talloc_destroy_pool functionality. It + should probably not be used in new code. It's in here to keep the talloc + code consistent across Samba 3 and 4. +*/ +static void talloc_free_children(void *ptr) +{ + struct talloc_chunk *tc; + + if (ptr == NULL) { + return; + } + + tc = talloc_chunk_from_ptr(ptr); + + while (tc->child) { + /* we need to work out who will own an abandoned child + if it cannot be freed. In priority order, the first + choice is owner of any remaining reference to this + pointer, the second choice is our parent, and the + final choice is the null context. */ + void *child = tc->child+1; + const void *new_parent = null_context; + if (tc->child->refs) { + struct talloc_chunk *p = + talloc_parent_chunk(tc->child->refs); + if (p) new_parent = p+1; + } + if (talloc_free(child) == -1) { + if (new_parent == null_context) { + struct talloc_chunk *p = + talloc_parent_chunk(ptr); + if (p) new_parent = p+1; + } + talloc_steal(new_parent, child); + } + } +} + +/* + free a talloc pointer. This also frees all child pointers of this + pointer recursively + + return 0 if the memory is actually freed, otherwise -1. The memory + will not be freed if the ref_count is > 1 or the destructor (if + any) returns non-zero +*/ +int talloc_free(void *ptr) +{ + struct talloc_chunk *tc; + + if (ptr == NULL) { + return -1; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (tc->refs) { + talloc_reference_destructor(tc->refs); + return -1; + } + + if (tc->destructor) { + talloc_destructor_t d = tc->destructor; + if (d == (talloc_destructor_t)-1) { + return -1; + } + tc->destructor = (talloc_destructor_t)-1; + if (d(ptr) == -1) { + tc->destructor = d; + return -1; + } + tc->destructor = NULL; + } + + talloc_free_children(ptr); + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->u.magic = TALLOC_MAGIC_FREE; + + free(tc); + return 0; +} + + + +/* + A talloc version of realloc. The context argument is only used if + ptr is NULL +*/ +void *_talloc_realloc(const void *context, void *ptr, size_t size, + const char *name) +{ + struct talloc_chunk *tc; + void *new_ptr; + + /* size zero is equivalent to free() */ + if (size == 0) { + talloc_free(ptr); + return NULL; + } + + if (size >= MAX_TALLOC_SIZE) { + return NULL; + } + + /* realloc(NULL) is equavalent to malloc() */ + if (ptr == NULL) { + return talloc_named_const(context, size, name); + } + + tc = talloc_chunk_from_ptr(ptr); + + /* don't allow realloc on referenced pointers */ + if (tc->refs) { + return NULL; + } + + /* by resetting magic we catch users of the old memory */ + tc->u.magic = TALLOC_MAGIC_FREE; + +#if ALWAYS_REALLOC + new_ptr = malloc(size + sizeof(*tc)); + if (new_ptr) { + memcpy(new_ptr, tc, tc->size + sizeof(*tc)); + free(tc); + } +#else + new_ptr = realloc(tc, size + sizeof(*tc)); +#endif + if (!new_ptr) { + tc->u.magic = TALLOC_MAGIC; + return NULL; + } + + tc = new_ptr; + tc->u.magic = TALLOC_MAGIC; + if (tc->parent) { + tc->parent->child = new_ptr; + } + if (tc->child) { + tc->child->parent = new_ptr; + } + + if (tc->prev) { + tc->prev->next = tc; + } + if (tc->next) { + tc->next->prev = tc; + } + + tc->size = size; + talloc_set_name_const(tc+1, name); + + return (void *)(tc+1); +} + +/* + move a lump of memory from one talloc context to another return the + ptr on success, or NULL if it could not be transferred. + passing NULL as ptr will always return NULL with no side effects. +*/ +void *talloc_steal(const void *new_ctx, const void *ptr) +{ + struct talloc_chunk *tc, *new_tc; + + if (!ptr) { + return NULL; + } + + if (new_ctx == NULL) { + new_ctx = null_context; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (new_ctx == NULL) { + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = tc->next = tc->prev = NULL; + return discard_const_p(void, ptr); + } + + new_tc = talloc_chunk_from_ptr(new_ctx); + + if (tc == new_tc) { + return discard_const_p(void, ptr); + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = new_tc; + if (new_tc->child) new_tc->child->parent = NULL; + _TLIST_ADD(new_tc->child, tc); + + return discard_const_p(void, ptr); +} + +/* + return the total size of a talloc pool (subtree) +*/ +off_t talloc_total_size(const void *ptr) +{ + off_t total = 0; + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(ptr); + + total = tc->size; + for (c=tc->child;c;c=c->next) { + total += talloc_total_size(c+1); + } + return total; +} + +/* + return the total number of blocks in a talloc pool (subtree) +*/ +off_t talloc_total_blocks(const void *ptr) +{ + off_t total = 0; + struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); + + total++; + for (c=tc->child;c;c=c->next) { + total += talloc_total_blocks(c+1); + } + return total; +} + +/* + return the number of external references to a pointer +*/ +static int talloc_reference_count(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + int ret = 0; + + for (h=tc->refs;h;h=h->next) { + ret++; + } + return ret; +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_depth(const void *ptr, FILE *f, int depth) +{ + struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); + + for (c=tc->child;c;c=c->next) { + if (c->name == TALLOC_MAGIC_REFERENCE) { + struct talloc_reference_handle *handle = (void *)(c+1); + const char *name2 = talloc_get_name(handle->ptr); + fprintf(f, "%*sreference to: %s\n", depth*4, "", name2); + } else { + const char *name = talloc_get_name(c+1); + fprintf(f, "%*s%-30s contains %6lu bytes " + "in %3lu blocks (ref %d)\n", + depth * 4, "", name, + (unsigned long)talloc_total_size(c+1), + (unsigned long)talloc_total_blocks(c+1), + talloc_reference_count(c+1)); + talloc_report_depth(c+1, f, depth+1); + } + } + +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_full(const void *ptr, FILE *f) +{ + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) return; + + fprintf(f, "full talloc report on '%s' " + "(total %lu bytes in %lu blocks)\n", + talloc_get_name(ptr), + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr)); + + talloc_report_depth(ptr, f, 1); + fflush(f); +} + +/* + report on memory usage by all children of a pointer +*/ +void talloc_report(const void *ptr, FILE *f) +{ + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) return; + + fprintf(f, "talloc report on '%s' (total %lu bytes in %lu blocks)\n", + talloc_get_name(ptr), + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr)); + + tc = talloc_chunk_from_ptr(ptr); + + for (c=tc->child;c;c=c->next) { + fprintf(f, "\t%-30s contains %6lu bytes in %3lu blocks\n", + talloc_get_name(c+1), + (unsigned long)talloc_total_size(c+1), + (unsigned long)talloc_total_blocks(c+1)); + } + fflush(f); +} + +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null(void) +{ + if (talloc_total_size(null_context) != 0) { + talloc_report(null_context, stderr); + } +} + +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null_full(void) +{ + if (talloc_total_size(null_context) != 0) { + talloc_report_full(null_context, stderr); + } +} + +/* + enable tracking of the NULL context +*/ +void talloc_enable_null_tracking(void) +{ + if (null_context == NULL) { + null_context = talloc_named_const(NULL, 0, "null_context"); + } +} + +/* + enable leak reporting on exit +*/ +void talloc_enable_leak_report(void) +{ + talloc_enable_null_tracking(); + atexit(talloc_report_null); +} + +/* + enable full leak reporting on exit +*/ +void talloc_enable_leak_report_full(void) +{ + talloc_enable_null_tracking(); + atexit(talloc_report_null_full); +} + +/* + talloc and zero memory. +*/ +void *_talloc_zero(const void *ctx, size_t size, const char *name) +{ + void *p = talloc_named_const(ctx, size, name); + + if (p) { + memset(p, '\0', size); + } + + return p; +} + + +/* + memdup with a talloc. +*/ +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name) +{ + void *newp = talloc_named_const(t, size, name); + + if (newp) { + memcpy(newp, p, size); + } + + return newp; +} + +/* + strdup with a talloc +*/ +char *talloc_strdup(const void *t, const char *p) +{ + char *ret; + if (!p) { + return NULL; + } + ret = talloc_memdup(t, p, strlen(p) + 1); + if (ret) { + talloc_set_name_const(ret, ret); + } + return ret; +} + +/* + strndup with a talloc +*/ +char *talloc_strndup(const void *t, const char *p, size_t n) +{ + size_t len; + char *ret; + + for (len=0; lensize - 1; + len = vsnprintf(NULL, 0, fmt, ap2); + + s = talloc_realloc(NULL, s, char, s_len + len+1); + if (!s) return NULL; + + VA_COPY(ap2, ap); + + vsnprintf(s+s_len, len+1, fmt, ap2); + talloc_set_name_const(s, s); + + return s; +} + +/* + Realloc @p s to append the formatted result of @p fmt and return @p + s, which may have moved. Good for gradually accumulating output + into a string buffer. + */ +char *talloc_asprintf_append(char *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + s = talloc_vasprintf_append(s, fmt, ap); + va_end(ap); + return s; +} + +/* + alloc an array, checking for integer overflow in the array size +*/ +void *_talloc_array(const void *ctx, size_t el_size, unsigned count, + const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return talloc_named_const(ctx, el_size * count, name); +} + +/* + alloc an zero array, checking for integer overflow in the array size +*/ +void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, + const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_zero(ctx, el_size * count, name); +} + + +/* + realloc an array, checking for integer overflow in the array size +*/ +void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, + unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_realloc(ctx, ptr, el_size * count, name); +} + +/* + a function version of talloc_realloc(), so it can be passed as a function + pointer to libraries that want a realloc function (a realloc function + encapsulates all the basic capabilities of an allocation library, which is + why this is useful) +*/ +void *talloc_realloc_fn(const void *context, void *ptr, size_t size) +{ + return _talloc_realloc(context, ptr, size, NULL); +} + + +static void talloc_autofree(void) +{ + talloc_free(cleanup_context); + cleanup_context = NULL; +} + +/* + return a context which will be auto-freed on exit + this is useful for reducing the noise in leak reports +*/ +void *talloc_autofree_context(void) +{ + if (cleanup_context == NULL) { + cleanup_context = talloc_named_const(NULL, 0, + "autofree_context"); + atexit(talloc_autofree); + } + return cleanup_context; +} + +size_t talloc_get_size(const void *context) +{ + struct talloc_chunk *tc; + + if (context == NULL) + return 0; + + tc = talloc_chunk_from_ptr(context); + + return tc->size; +} diff --git a/lib/talloc/talloc.h b/lib/talloc/talloc.h new file mode 100644 index 0000000..15a1eb6 --- /dev/null +++ b/lib/talloc/talloc.h @@ -0,0 +1,137 @@ +#ifndef _TALLOC_H_ +#define _TALLOC_H_ +/* + Unix SMB/CIFS implementation. + Samba temporary memory allocation functions + + Copyright (C) Andrew Tridgell 2004-2005 + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* petitboot additions */ +#include +#include +#include + +/* this is only needed for compatibility with the old talloc */ +typedef void TALLOC_CTX; + +/* + this uses a little trick to allow __LINE__ to be stringified +*/ +#define _STRING_LINE_(s) #s +#define _STRING_LINE2_(s) _STRING_LINE_(s) +#define __LINESTR__ _STRING_LINE2_(__LINE__) +#define __location__ __FILE__ ":" __LINESTR__ + +#ifndef TALLOC_DEPRECATED +#define TALLOC_DEPRECATED 0 +#endif + +/* useful macros for creating type checked pointers */ +#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) +#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__) + +#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__) + +#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) +#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__) + +#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type) +#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type) +#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__) + +#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type) +#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__) + +#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__) + +#define malloc_p(type) (type *)malloc(sizeof(type)) +#define malloc_array_p(type, count) (type *)realloc_array(NULL, sizeof(type), count) +#define realloc_p(p, type, count) (type *)realloc_array(p, sizeof(type), count) + +#define data_blob(ptr, size) data_blob_named(ptr, size, "DATA_BLOB: "__location__) +#define data_blob_talloc(ctx, ptr, size) data_blob_talloc_named(ctx, ptr, size, "DATA_BLOB: "__location__) +#define data_blob_dup_talloc(ctx, blob) data_blob_talloc_named(ctx, (blob)->data, (blob)->length, "DATA_BLOB: "__location__) + +#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type) +#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type) + + +#if TALLOC_DEPRECATED +#define talloc_zero_p(ctx, type) talloc_zero(ctx, type) +#define talloc_p(ctx, type) talloc(ctx, type) +#define talloc_array_p(ctx, type, count) talloc_array(ctx, type, count) +#define talloc_realloc_p(ctx, p, type, count) talloc_realloc(ctx, p, type, count) +#define talloc_destroy(ctx) talloc_free(ctx) +#endif + +#ifndef PRINTF_ATTRIBUTE +#if (__GNUC__ >= 3) +/** Use gcc attribute to check printf fns. a1 is the 1-based index of + * the parameter containing the format, and a2 the index of the first + * argument. Note that some gcc 2.x versions don't handle this + * properly **/ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif +#endif + + +/* The following definitions come from talloc.c */ +void *_talloc(const void *context, size_t size); +void talloc_set_destructor(const void *ptr, int (*destructor)(void *)); +void talloc_increase_ref_count(const void *ptr); +void *talloc_reference(const void *context, const void *ptr); +int talloc_unlink(const void *context, void *ptr); +void talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +void talloc_set_name_const(const void *ptr, const char *name); +void *talloc_named(const void *context, size_t size, + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); +void *talloc_named_const(const void *context, size_t size, const char *name); +const char *talloc_get_name(const void *ptr); +void *talloc_check_name(const void *ptr, const char *name); +void talloc_report_depth(const void *ptr, FILE *f, int depth); +void *talloc_parent(const void *ptr); +void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); +int talloc_free(void *ptr); +void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name); +void *talloc_steal(const void *new_ctx, const void *ptr); +off_t talloc_total_size(const void *ptr); +off_t talloc_total_blocks(const void *ptr); +void talloc_report_full(const void *ptr, FILE *f); +void talloc_report(const void *ptr, FILE *f); +void talloc_enable_null_tracking(void); +void talloc_enable_leak_report(void); +void talloc_enable_leak_report_full(void); +void *_talloc_zero(const void *ctx, size_t size, const char *name); +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name); +char *talloc_strdup(const void *t, const char *p); +char *talloc_strndup(const void *t, const char *p, size_t n); +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); +char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +char *talloc_asprintf_append(char *s, + const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name); +void *talloc_realloc_fn(const void *context, void *ptr, size_t size); +void *talloc_autofree_context(void); +size_t talloc_get_size(const void *ctx); + +#endif + diff --git a/petitboot-paths.h b/petitboot-paths.h deleted file mode 100644 index 40ce557..0000000 --- a/petitboot-paths.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef _PATHS_H -#define _PATHS_H - -#ifndef PREFIX -#define PREFIX "/usr" -#endif - -#ifndef PKG_SHARE_DIR -#define PKG_SHARE_DIR PREFIX "/share/petitboot" -#endif - -#ifndef TMP_DIR -#define TMP_DIR "/var/tmp/mnt/" -#endif - -#define PBOOT_DEVICE_SOCKET "/var/tmp/petitboot-dev" -#define MOUNT_BIN "/bin/mount" -#define UMOUNT_BIN "/bin/umount" -#define BOOT_GAMEOS_BIN "/usr/bin/ps3-boot-game-os" - -/* at present, all default artwork strings are const. */ -#define artwork_pathname(s) (PKG_SHARE_DIR "/artwork/" s) - -#endif /* _PATHS_H */ diff --git a/petitboot.c b/petitboot.c deleted file mode 100644 index ee68930..0000000 --- a/petitboot.c +++ /dev/null @@ -1,1194 +0,0 @@ - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#undef _USE_X11 - -#include -#include -#include -#include -#include - -#include "petitboot.h" -#include "petitboot-paths.h" - -#ifdef _USE_X11 -#include -static twin_x11_t *pboot_x11; -#else -#include -static twin_fbdev_t *pboot_fbdev; -#endif - -static twin_screen_t *pboot_screen; - -#define PBOOT_INITIAL_MESSAGE \ - "keys: 0=safe 1=720p 2=1080i 3=1080p del=GameOS" - -#define PBOOT_LEFT_PANE_SIZE 160 -#define PBOOT_LEFT_PANE_COLOR 0x80000000 -#define PBOOT_LEFT_LINE_COLOR 0xff000000 - -#define PBOOT_LEFT_FOCUS_WIDTH 80 -#define PBOOT_LEFT_FOCUS_HEIGHT 80 -#define PBOOT_LEFT_FOCUS_XOFF 40 -#define PBOOT_LEFT_FOCUS_YOFF 40 -#define PBOOT_LEFT_FOCUS_XRAD (6 * TWIN_FIXED_ONE) -#define PBOOT_LEFT_FOCUS_YRAD (6 * TWIN_FIXED_ONE) - -#define PBOOT_RIGHT_FOCUS_XOFF 20 -#define PBOOT_RIGHT_FOCUS_YOFF 60 -#define PBOOT_RIGHT_FOCUS_HEIGHT 80 -#define PBOOT_RIGHT_FOCUS_XRAD (6 * TWIN_FIXED_ONE) -#define PBOOT_RIGHT_FOCUS_YRAD (6 * TWIN_FIXED_ONE) - -#define PBOOT_LEFT_ICON_WIDTH 64 -#define PBOOT_LEFT_ICON_HEIGHT 64 -#define PBOOT_LEFT_ICON_XOFF 50 -#define PBOOT_LEFT_ICON_YOFF 50 -#define PBOOT_LEFT_ICON_STRIDE 100 - -#define PBOOT_RIGHT_OPTION_LMARGIN 30 -#define PBOOT_RIGHT_OPTION_RMARGIN 30 -#define PBOOT_RIGHT_OPTION_TMARGIN 70 -#define PBOOT_RIGHT_OPTION_HEIGHT 64 -#define PBOOT_RIGHT_OPTION_STRIDE 100 -#define PBOOT_RIGHT_TITLE_TEXT_SIZE (30 * TWIN_FIXED_ONE) -#define PBOOT_RIGHT_SUBTITLE_TEXT_SIZE (18 * TWIN_FIXED_ONE) -#define PBOOT_RIGHT_TITLE_XOFFSET 80 -#define PBOOT_RIGHT_TITLE_YOFFSET 30 -#define PBOOT_RIGHT_SUBTITLE_XOFFSET 100 -#define PBOOT_RIGHT_SUBTITLE_YOFFSET 50 -#define PBOOT_RIGHT_BADGE_XOFFSET 2 -#define PBOOT_RIGHT_BADGE_YOFFSET 0 - - -#define PBOOT_RIGHT_TITLE_COLOR 0xff000000 -#define PBOOT_RIGHT_SUBTITLE_COLOR 0xff400000 - -#define PBOOT_FOCUS_COLOR 0x10404040 - -#define PBOOT_STATUS_PANE_COLOR 0x60606060 -#define PBOOT_STATUS_PANE_HEIGHT 20 -#define PBOOT_STATUS_PANE_XYMARGIN 20 -#define PBOOT_STATUS_TEXT_MARGIN 10 -#define PBOOT_STATUS_TEXT_SIZE (16 * TWIN_FIXED_ONE) -#define PBOOT_STATUS_TEXT_COLOR 0xff000000 - -typedef struct _pboot_option pboot_option_t; -typedef struct _pboot_device pboot_device_t; - -struct _pboot_option -{ - char *title; - char *subtitle; - twin_pixmap_t *badge; - twin_pixmap_t *cache; - twin_rect_t box; - void *data; -}; - -struct _pboot_device -{ - char *id; - twin_pixmap_t *badge; - twin_rect_t box; - int option_count; - pboot_option_t options[PBOOT_MAX_OPTION]; -}; - -static twin_pixmap_t *pboot_cursor; -static int pboot_cursor_hx; -static int pboot_cursor_hy; - -static pboot_device_t *pboot_devices[PBOOT_MAX_DEV]; -static int pboot_dev_count; -static int pboot_dev_sel = -1; -static int pboot_focus_lpane = 1; - -typedef struct _pboot_lpane { - twin_window_t *window; - twin_rect_t focus_box; - int focus_start; - int focus_target; - int focus_curindex; - int mouse_target; -} pboot_lpane_t; - -typedef struct _pboot_rpane { - twin_window_t *window; - twin_rect_t focus_box; - int focus_start; - int focus_target; - int focus_curindex; - int mouse_target; -} pboot_rpane_t; - -typedef struct _pboot_spane { - twin_window_t *window; - char *text; -} pboot_spane_t; - -static pboot_lpane_t *pboot_lpane; -static pboot_rpane_t *pboot_rpane; -static pboot_spane_t *pboot_spane; - -/* control to keyboard mappings for the sixaxis controller */ -uint8_t sixaxis_map[] = { - 0, /* 0 Select */ - 0, /* 1 L3 */ - 0, /* 2 R3 */ - 0, /* 3 Start */ - KEY_UP, /* 4 Dpad Up */ - KEY_RIGHT, /* 5 Dpad Right */ - KEY_DOWN, /* 6 Dpad Down */ - KEY_LEFT, /* 7 Dpad Left */ - 0, /* 8 L2 */ - 0, /* 9 R2 */ - 0, /* 10 L1 */ - 0, /* 11 R1 */ - 0, /* 12 Triangle */ - KEY_ENTER, /* 13 Circle */ - 0, /* 14 Cross */ - KEY_DELETE, /* 15 Square */ - 0, /* 16 PS Button */ - 0, /* 17 nothing */ - 0, /* 18 nothing */ -}; - - -static int pboot_vmode_change = -1; - -/* XXX move to twin */ -static inline twin_bool_t twin_rect_intersect(twin_rect_t r1, - twin_rect_t r2) -{ - return !(r1.left > r2.right || - r1.right < r2.left || - r1.top > r2.bottom || - r1.bottom < r2.top); -} - -static void pboot_draw_option_cache(pboot_device_t *dev, pboot_option_t *opt, - int index) -{ - twin_pixmap_t *px; - twin_path_t *path; - twin_fixed_t tx, ty; - - /* Create pixmap */ - px = twin_pixmap_create(TWIN_ARGB32, opt->box.right - opt->box.left, - opt->box.bottom - opt->box.top); - assert(px); - opt->cache = px; - - /* Fill background */ - twin_fill(px, 0x00000000, TWIN_SOURCE, 0, 0, px->width, px->height); - - /* Allocate a path for drawing */ - path = twin_path_create(); - assert(path); - -#if 0 - /* TEST - Bounding rectangle */ - twin_path_rectangle(path, 0, 0, - twin_int_to_fixed(px->width), - twin_int_to_fixed(px->height)); - twin_paint_path(px, PBOOT_RIGHT_TITLE_COLOR, path); - twin_path_empty(path); - twin_fill(px, 0x00000000, TWIN_SOURCE, 2, 2, - px->width - 3, px->height - 3); -#endif - - /* Draw texts */ - twin_path_set_font_size(path, PBOOT_RIGHT_TITLE_TEXT_SIZE); - twin_path_set_font_style(path, TWIN_TEXT_UNHINTED); - tx = twin_int_to_fixed(PBOOT_RIGHT_TITLE_XOFFSET); - ty = twin_int_to_fixed(PBOOT_RIGHT_TITLE_YOFFSET); - twin_path_move (path, tx, ty); - twin_path_utf8 (path, opt->title); - twin_paint_path (px, PBOOT_RIGHT_TITLE_COLOR, path); - twin_path_empty (path); - - if (opt->subtitle) { - twin_path_set_font_size(path, PBOOT_RIGHT_SUBTITLE_TEXT_SIZE); - twin_path_set_font_style(path, TWIN_TEXT_UNHINTED); - tx = twin_int_to_fixed(PBOOT_RIGHT_SUBTITLE_XOFFSET); - ty = twin_int_to_fixed(PBOOT_RIGHT_SUBTITLE_YOFFSET); - twin_path_move (path, tx, ty); - twin_path_utf8 (path, opt->subtitle); - twin_paint_path (px, PBOOT_RIGHT_SUBTITLE_COLOR, path); - twin_path_empty (path); - } - - if (opt->badge) { - twin_operand_t src; - - src.source_kind = TWIN_PIXMAP; - src.u.pixmap = opt->badge; - - twin_composite(px, PBOOT_RIGHT_BADGE_XOFFSET, - PBOOT_RIGHT_BADGE_YOFFSET, - &src, 0, 0, NULL, 0, 0, TWIN_OVER, - opt->badge->width, opt->badge->height); - } - - - /* Destroy path */ - twin_path_destroy(path); -} - -static void pboot_rpane_draw(twin_window_t *window) -{ - twin_pixmap_t *px = window->pixmap; - pboot_rpane_t *rpane = window->client_data; - pboot_device_t *dev; - twin_path_t *path; - twin_fixed_t x, y, w, h; - int i; - - /* Fill background */ - twin_fill(px, 0x00000000, TWIN_SOURCE, 0, 0, px->width, px->height); - - /* Nothing to draw, return */ - if (pboot_dev_sel < 0) - return; - - /* Create a path for use later */ - path = twin_path_create(); - assert(path); - - /* Draw focus box */ - if (rpane->focus_curindex >= 0 && - twin_rect_intersect(rpane->focus_box, px->clip)) { - x = twin_int_to_fixed(rpane->focus_box.left + 2); - y = twin_int_to_fixed(rpane->focus_box.top + 2); - w = twin_int_to_fixed(rpane->focus_box.right - - rpane->focus_box.left - 4); - h = twin_int_to_fixed(rpane->focus_box.bottom - - rpane->focus_box.top - 4); - twin_path_rounded_rectangle(path, x, y, w, h, - PBOOT_RIGHT_FOCUS_XRAD, - PBOOT_RIGHT_FOCUS_YRAD); - if (!pboot_focus_lpane) - twin_paint_path(px, PBOOT_FOCUS_COLOR, path); - else - twin_paint_stroke(px, PBOOT_FOCUS_COLOR, path, - 4 * TWIN_FIXED_ONE); - } - - /* Get device and iterate through options */ - dev = pboot_devices[pboot_dev_sel]; - for (i = 0; i < dev->option_count; i++) { - pboot_option_t *opt = &dev->options[i]; - twin_operand_t src; - - if (opt->title == NULL) - continue; - if (!twin_rect_intersect(opt->box, px->clip)) - continue; - if (opt->cache == NULL) - pboot_draw_option_cache(dev, opt, i); - - src.source_kind = TWIN_PIXMAP; - src.u.pixmap = opt->cache; - - twin_composite(px, opt->box.left, opt->box.top, - &src, 0, 0, NULL, 0, 0, TWIN_OVER, - opt->box.right - opt->box.left, - opt->box.bottom - opt->box.top); - } - - /* Destroy path */ - twin_path_destroy(path); -} - -static twin_time_t pboot_rfocus_timeout (twin_time_t now, void *closure) -{ - int dir = 1, dist, pos; - const int accel[11] = { 7, 4, 2, 1, 1, 1, 1, 1, 2, 2, 3 }; - - dist = abs(pboot_rpane->focus_target - pboot_rpane->focus_start); - dir = dist > 5 ? 5 : dist; - pos = pboot_rpane->focus_target - (int)pboot_rpane->focus_box.top; - if (pos == 0) { - return -1; - } - if (pos < 0) { - dir = -dir; - pos = -pos; - } - twin_window_damage(pboot_rpane->window, - pboot_rpane->focus_box.left, - pboot_rpane->focus_box.top, - pboot_rpane->focus_box.right, - pboot_rpane->focus_box.bottom); - - pboot_rpane->focus_box.top += dir; - pboot_rpane->focus_box.bottom += dir; - - twin_window_damage(pboot_rpane->window, - pboot_rpane->focus_box.left, - pboot_rpane->focus_box.top, - pboot_rpane->focus_box.right, - pboot_rpane->focus_box.bottom); - - twin_window_queue_paint(pboot_rpane->window); - - return accel[(pos * 10) / dist]; -} - -static void pboot_set_rfocus(int index) -{ - pboot_device_t *dev; - - if (pboot_dev_sel < 0 || pboot_dev_sel >= pboot_dev_count) - return; - dev = pboot_devices[pboot_dev_sel]; - if (index < 0 || index >= dev->option_count) - return; - - pboot_rpane->focus_start = pboot_rpane->focus_box.top; - pboot_rpane->focus_target = PBOOT_RIGHT_FOCUS_YOFF + - PBOOT_RIGHT_OPTION_STRIDE * index; - pboot_rpane->focus_curindex = index; - - twin_set_timeout(pboot_rfocus_timeout, 0, NULL); -} - -static void pboot_select_rpane(void) -{ - if (pboot_focus_lpane == 0) - return; - pboot_focus_lpane = 0; - - twin_screen_set_active(pboot_screen, pboot_rpane->window->pixmap); - - twin_window_damage(pboot_lpane->window, - pboot_lpane->focus_box.left, - pboot_lpane->focus_box.top, - pboot_lpane->focus_box.right, - pboot_lpane->focus_box.bottom); - - twin_window_damage(pboot_rpane->window, - pboot_rpane->focus_box.left, - pboot_rpane->focus_box.top, - pboot_rpane->focus_box.right, - pboot_rpane->focus_box.bottom); - - twin_window_queue_paint(pboot_lpane->window); - twin_window_queue_paint(pboot_rpane->window); - - pboot_set_rfocus(0); -} - -static void pboot_select_lpane(void) -{ - if (pboot_focus_lpane == 1) - return; - pboot_focus_lpane = 1; - - twin_screen_set_active(pboot_screen, pboot_lpane->window->pixmap); - - twin_window_damage(pboot_lpane->window, - pboot_lpane->focus_box.left, - pboot_lpane->focus_box.top, - pboot_lpane->focus_box.right, - pboot_lpane->focus_box.bottom); - - twin_window_damage(pboot_rpane->window, - pboot_rpane->focus_box.left, - pboot_rpane->focus_box.top, - pboot_rpane->focus_box.right, - pboot_rpane->focus_box.bottom); - - twin_window_queue_paint(pboot_lpane->window); - twin_window_queue_paint(pboot_rpane->window); -} - -static void pboot_rpane_mousetrack(twin_coord_t x, twin_coord_t y) -{ - pboot_device_t *dev; - pboot_option_t *opt; - int candidate = -1; - - if (pboot_dev_sel < 0 || pboot_dev_sel >= pboot_dev_count) - return; - dev = pboot_devices[pboot_dev_sel]; - - if (y < PBOOT_RIGHT_OPTION_TMARGIN) - goto miss; - candidate = (y - PBOOT_RIGHT_OPTION_TMARGIN) / - PBOOT_RIGHT_OPTION_STRIDE; - if (candidate >= dev->option_count) { - candidate = -1; - goto miss; - } - if (candidate == pboot_rpane->mouse_target) - return; - opt = &dev->options[candidate]; - if (x < opt->box.left || x > opt->box.right || - y < opt->box.top || y > opt->box.bottom) { - candidate = -1; - goto miss; - } - - /* Ok, so now, we know the mouse hit an icon that wasn't the same - * as the previous one, we trigger a focus change - */ - pboot_set_rfocus(candidate); - - miss: - pboot_rpane->mouse_target = candidate; -} - -static void pboot_choose_option(void) -{ - pboot_device_t *dev = pboot_devices[pboot_dev_sel]; - pboot_option_t *opt = &dev->options[pboot_rpane->focus_curindex]; - - LOG("Selected device %s\n", opt->title); - pboot_message("booting %s...", opt->title); - - /* Give user feedback, make sure errors and panics will be seen */ - pboot_exec_option(opt->data); -} - -static twin_bool_t pboot_rpane_event (twin_window_t *window, - twin_event_t *event) -{ - /* filter out all mouse events */ - switch(event->kind) { - case TwinEventEnter: - case TwinEventMotion: - case TwinEventLeave: - pboot_select_rpane(); - pboot_rpane_mousetrack(event->u.pointer.x, event->u.pointer.y); - return TWIN_TRUE; - case TwinEventButtonDown: - pboot_select_rpane(); - pboot_rpane_mousetrack(event->u.pointer.x, event->u.pointer.y); - pboot_choose_option(); - case TwinEventButtonUp: - return TWIN_TRUE; - case TwinEventKeyDown: - switch(event->u.key.key) { - case KEY_UP: - pboot_set_rfocus(pboot_rpane->focus_curindex - 1); - return TWIN_TRUE; - case KEY_DOWN: - pboot_set_rfocus(pboot_rpane->focus_curindex + 1); - return TWIN_TRUE; - case KEY_LEFT: - pboot_select_lpane(); - return TWIN_TRUE; - case KEY_ENTER: - pboot_choose_option(); - default: - break; - } - break; - default: - break; - } - return TWIN_FALSE; -} - - -int pboot_add_option(int devindex, const char *title, - const char *subtitle, twin_pixmap_t *badge, void *data) -{ - pboot_device_t *dev; - pboot_option_t *opt; - twin_coord_t width; - int index; - - if (devindex < 0 || devindex >= pboot_dev_count) - return -1; - dev = pboot_devices[devindex]; - - if (dev->option_count >= PBOOT_MAX_OPTION) - return -1; - index = dev->option_count++; - opt = &dev->options[index]; - - opt->title = malloc(strlen(title) + 1); - strcpy(opt->title, title); - - if (subtitle) { - opt->subtitle = malloc(strlen(subtitle) + 1); - strcpy(opt->subtitle, subtitle); - } else - opt->subtitle = NULL; - - opt->badge = badge; - opt->cache = NULL; - - width = pboot_rpane->window->pixmap->width - - (PBOOT_RIGHT_OPTION_LMARGIN + PBOOT_RIGHT_OPTION_RMARGIN); - - opt->box.left = PBOOT_RIGHT_OPTION_LMARGIN; - opt->box.right = opt->box.left + width; - opt->box.top = PBOOT_RIGHT_OPTION_TMARGIN + - index * PBOOT_RIGHT_OPTION_STRIDE; - opt->box.bottom = opt->box.top + PBOOT_RIGHT_OPTION_HEIGHT; - - opt->data = data; - return index; -} - - -static void pboot_set_device_select(int sel, int force) -{ - LOG("%s: %d -> %d\n", __FUNCTION__, pboot_dev_sel, sel); - if (!force && sel == pboot_dev_sel) - return; - if (sel >= pboot_dev_count) - return; - pboot_dev_sel = sel; - if (force) { - pboot_lpane->focus_curindex = sel; - if (sel < 0) - pboot_lpane->focus_target = 0 - PBOOT_LEFT_FOCUS_HEIGHT; - else - pboot_lpane->focus_target = PBOOT_LEFT_FOCUS_YOFF + - PBOOT_LEFT_ICON_STRIDE * sel; - pboot_rpane->focus_box.bottom = pboot_lpane->focus_target; - pboot_rpane->focus_box.bottom = pboot_rpane->focus_box.top + - PBOOT_RIGHT_FOCUS_HEIGHT; - twin_window_damage(pboot_lpane->window, - 0, 0, - pboot_lpane->window->pixmap->width, - pboot_lpane->window->pixmap->height); - twin_window_queue_paint(pboot_lpane->window); - } - pboot_rpane->focus_curindex = -1; - pboot_rpane->mouse_target = -1; - pboot_rpane->focus_box.top = -2*PBOOT_RIGHT_FOCUS_HEIGHT; - pboot_rpane->focus_box.bottom = pboot_rpane->focus_box.top + - PBOOT_RIGHT_FOCUS_HEIGHT; - twin_window_damage(pboot_rpane->window, 0, 0, - pboot_rpane->window->pixmap->width, - pboot_rpane->window->pixmap->height); - twin_window_queue_paint(pboot_rpane->window); -} - -static void pboot_create_rpane(void) -{ - pboot_rpane = calloc(1, sizeof(pboot_rpane_t)); - assert(pboot_rpane); - - pboot_rpane->window = twin_window_create(pboot_screen, TWIN_ARGB32, - TwinWindowPlain, - PBOOT_LEFT_PANE_SIZE, 0, - pboot_screen->width - - PBOOT_LEFT_PANE_SIZE, - pboot_screen->height); - assert(pboot_rpane->window); - - pboot_rpane->window->draw = pboot_rpane_draw; - pboot_rpane->window->event = pboot_rpane_event; - pboot_rpane->window->client_data = pboot_rpane; - - pboot_rpane->focus_curindex = -1; - pboot_rpane->focus_box.left = PBOOT_RIGHT_FOCUS_XOFF; - pboot_rpane->focus_box.top = -2*PBOOT_RIGHT_FOCUS_HEIGHT; - pboot_rpane->focus_box.right = pboot_rpane->window->pixmap->width - - 2 * PBOOT_RIGHT_FOCUS_XOFF; - pboot_rpane->focus_box.bottom = pboot_rpane->focus_box.top + - PBOOT_RIGHT_FOCUS_HEIGHT; - pboot_rpane->mouse_target = -1; - twin_window_show(pboot_rpane->window); - twin_window_queue_paint(pboot_rpane->window); -} - - -static twin_time_t pboot_lfocus_timeout (twin_time_t now, void *closure) -{ - int dir = 1, dist, pos; - const int accel[11] = { 7, 4, 2, 1, 1, 1, 1, 1, 2, 2, 3 }; - - dist = abs(pboot_lpane->focus_target - pboot_lpane->focus_start); - dir = dist > 2 ? 2 : dist; - pos = pboot_lpane->focus_target - (int)pboot_lpane->focus_box.top; - if (pos == 0) { - pboot_set_device_select(pboot_lpane->focus_curindex, 0); - return -1; - } - if (pos < 0) { - dir = -1; - pos = -pos; - } - twin_window_damage(pboot_lpane->window, - pboot_lpane->focus_box.left, - pboot_lpane->focus_box.top, - pboot_lpane->focus_box.right, - pboot_lpane->focus_box.bottom); - - pboot_lpane->focus_box.top += dir; - pboot_lpane->focus_box.bottom += dir; - - twin_window_damage(pboot_lpane->window, - pboot_lpane->focus_box.left, - pboot_lpane->focus_box.top, - pboot_lpane->focus_box.right, - pboot_lpane->focus_box.bottom); - - twin_window_queue_paint(pboot_lpane->window); - - return accel[(pos * 10) / dist]; -} - -static void pboot_set_lfocus(int index) -{ - if (index >= pboot_dev_count) - return; - - pboot_lpane->focus_start = pboot_lpane->focus_box.top; - - if (index < 0) - pboot_lpane->focus_target = 0 - PBOOT_LEFT_FOCUS_HEIGHT; - else - pboot_lpane->focus_target = PBOOT_LEFT_FOCUS_YOFF + - PBOOT_LEFT_ICON_STRIDE * index; - - pboot_lpane->focus_curindex = index; - - twin_set_timeout(pboot_lfocus_timeout, 0, NULL); -} - -static void pboot_lpane_mousetrack(twin_coord_t x, twin_coord_t y) -{ - int candidate = -1; - twin_coord_t icon_top; - - if (x < PBOOT_LEFT_ICON_XOFF || - x > (PBOOT_LEFT_ICON_XOFF + PBOOT_LEFT_ICON_WIDTH)) - goto miss; - if (y < PBOOT_LEFT_ICON_YOFF) - goto miss; - candidate = (y - PBOOT_LEFT_ICON_YOFF) / PBOOT_LEFT_ICON_STRIDE; - if (candidate >= pboot_dev_count) { - candidate = -1; - goto miss; - } - if (candidate == pboot_lpane->mouse_target) - return; - icon_top = PBOOT_LEFT_ICON_YOFF + - candidate * PBOOT_LEFT_ICON_STRIDE; - if (y > (icon_top + PBOOT_LEFT_ICON_HEIGHT)) { - candidate = -1; - goto miss; - } - - /* Ok, so now, we know the mouse hit an icon that wasn't the same - * as the previous one, we trigger a focus change - */ - pboot_set_lfocus(candidate); - - miss: - pboot_lpane->mouse_target = candidate; -} - -static twin_bool_t pboot_lpane_event (twin_window_t *window, - twin_event_t *event) -{ - /* filter out all mouse events */ - switch(event->kind) { - case TwinEventEnter: - case TwinEventMotion: - case TwinEventLeave: - pboot_select_lpane(); - pboot_lpane_mousetrack(event->u.pointer.x, event->u.pointer.y); - return TWIN_TRUE; - case TwinEventButtonDown: - case TwinEventButtonUp: - return TWIN_TRUE; - case TwinEventKeyDown: - switch(event->u.key.key) { - case KEY_UP: - if (pboot_lpane->focus_curindex > 0) - pboot_set_lfocus( - pboot_lpane->focus_curindex - 1); - return TWIN_TRUE; - case KEY_DOWN: - pboot_set_lfocus(pboot_lpane->focus_curindex + 1); - return TWIN_TRUE; - case KEY_RIGHT: - pboot_select_rpane(); - return TWIN_TRUE; - default: - break; - } - break; - default: - break; - } - return TWIN_FALSE; -} - -static void pboot_quit(void) -{ - kill(0, SIGINT); -} - -twin_bool_t pboot_event_filter(twin_screen_t *screen, - twin_event_t *event) -{ - switch(event->kind) { - case TwinEventEnter: - case TwinEventMotion: - case TwinEventLeave: - case TwinEventButtonDown: - case TwinEventButtonUp: - if (pboot_cursor != NULL) - twin_screen_set_cursor(pboot_screen, pboot_cursor, - pboot_cursor_hx, - pboot_cursor_hy); - break; - case TwinEventJoyButton: - /* map joystick events into key events */ - if (event->u.js.control >= sizeof(sixaxis_map)) - break; - - event->u.key.key = sixaxis_map[event->u.js.control]; - if (event->u.js.value == 0) { - event->kind = TwinEventKeyUp; - break; - } else { - event->kind = TwinEventKeyDown; - } - - /* fall through.. */ - case TwinEventKeyDown: - switch(event->u.key.key) { - /* Gross hack for video modes, need something better ! */ - case KEY_0: - pboot_vmode_change = 0; /* auto */ - pboot_quit(); - return TWIN_TRUE; - case KEY_1: - pboot_vmode_change = 3; /* 720p */ - pboot_quit(); - return TWIN_TRUE; - case KEY_2: - pboot_vmode_change = 4; /* 1080i */ - pboot_quit(); - return TWIN_TRUE; - case KEY_3: - pboot_vmode_change = 5; /* 1080p */ - pboot_quit(); - return TWIN_TRUE; - - /* Another gross hack for booting back to gameos */ - case KEY_BACKSPACE: - case KEY_DELETE: - pboot_message("booting to GameOS..."); - system(BOOT_GAMEOS_BIN); - } - case TwinEventKeyUp: - twin_screen_set_cursor(pboot_screen, NULL, 0, 0); - break; - default: - break; - } - return TWIN_FALSE; -} - -static void pboot_lpane_draw(twin_window_t *window) -{ - twin_pixmap_t *px = window->pixmap; - pboot_lpane_t *lpane = window->client_data; - twin_path_t *path; - twin_fixed_t x, y, w, h; - int i; - - /* Fill background */ - twin_fill(px, PBOOT_LEFT_PANE_COLOR, TWIN_SOURCE, - 0, 0, px->width, px->height); - - /* Create a path for use later */ - path = twin_path_create(); - assert(path); - - /* Draw right line if needed */ - if (px->clip.right > (PBOOT_LEFT_PANE_SIZE - 4)) { - x = twin_int_to_fixed(PBOOT_LEFT_PANE_SIZE - 4); - y = twin_int_to_fixed(px->height); - twin_path_rectangle(path, x, 0, 0x40000, y); - twin_paint_path(px, PBOOT_LEFT_LINE_COLOR, path); - twin_path_empty(path); - } - - /* Draw focus box */ - if (lpane->focus_curindex >= 0 && - twin_rect_intersect(lpane->focus_box, px->clip)) { - x = twin_int_to_fixed(lpane->focus_box.left + 2); - y = twin_int_to_fixed(lpane->focus_box.top + 2); - w = twin_int_to_fixed(lpane->focus_box.right - - lpane->focus_box.left - 4); - h = twin_int_to_fixed(lpane->focus_box.bottom - - lpane->focus_box.top - 4); - twin_path_rounded_rectangle(path, x, y, w, h, - PBOOT_LEFT_FOCUS_XRAD, - PBOOT_LEFT_FOCUS_YRAD); - if (pboot_focus_lpane) - twin_paint_path(px, PBOOT_FOCUS_COLOR, path); - else - twin_paint_stroke(px, PBOOT_FOCUS_COLOR, path, - 4 * TWIN_FIXED_ONE); - } - - /* Draw icons */ - for (i = 0; i < pboot_dev_count; i++) { - pboot_device_t *dev = pboot_devices[i]; - twin_operand_t src; - - if (!twin_rect_intersect(dev->box, px->clip)) - continue; - - src.source_kind = TWIN_PIXMAP; - src.u.pixmap = dev->badge; - - twin_composite(px, dev->box.left, dev->box.top, - &src, 0, 0, NULL, 0, 0, TWIN_OVER, - dev->box.right - dev->box.left, - dev->box.bottom - dev->box.top); - - } - - /* Destroy path */ - twin_path_destroy(path); -} - -static void pboot_create_lpane(void) -{ - pboot_lpane = calloc(1, sizeof(pboot_lpane_t)); - assert(pboot_lpane); - - pboot_lpane->window = twin_window_create(pboot_screen, TWIN_ARGB32, - TwinWindowPlain, - 0, 0, PBOOT_LEFT_PANE_SIZE, - pboot_screen->height); - assert(pboot_lpane->window); - - pboot_lpane->window->draw = pboot_lpane_draw; - pboot_lpane->window->event = pboot_lpane_event; - pboot_lpane->window->client_data = pboot_lpane; - pboot_lpane->focus_curindex = -1; - pboot_lpane->focus_box.left = PBOOT_LEFT_FOCUS_XOFF; - pboot_lpane->focus_box.top = -2*PBOOT_LEFT_FOCUS_HEIGHT; - pboot_lpane->focus_box.right = pboot_lpane->focus_box.left + - PBOOT_LEFT_FOCUS_WIDTH; - pboot_lpane->focus_box.bottom = pboot_lpane->focus_box.top + - PBOOT_LEFT_FOCUS_HEIGHT; - pboot_lpane->mouse_target = -1; - twin_window_show(pboot_lpane->window); - twin_window_queue_paint(pboot_lpane->window); -} - -static void pboot_spane_draw(twin_window_t *window) -{ - twin_pixmap_t *px = window->pixmap; - pboot_spane_t *spane = window->client_data; - twin_path_t *path; - twin_fixed_t tx, ty; - - /* Fill background */ - twin_fill(px, PBOOT_STATUS_PANE_COLOR, TWIN_SOURCE, - 0, 0, px->width, px->height); - - path = twin_path_create(); - assert(path); - - twin_path_set_font_size(path, PBOOT_STATUS_TEXT_SIZE); - twin_path_set_font_style(path, TWIN_TEXT_UNHINTED); - tx = twin_int_to_fixed(PBOOT_STATUS_TEXT_MARGIN); - ty = twin_int_to_fixed(PBOOT_STATUS_PANE_HEIGHT - 2); - twin_path_move (path, tx, ty); - twin_path_utf8 (path, spane->text); - twin_paint_path (px, PBOOT_STATUS_TEXT_COLOR, path); - - twin_path_destroy(path); -} - -void pboot_message(const char *fmt, ...) -{ - va_list ap; - char *msg; - - if (pboot_spane->text) - free(pboot_spane->text); - - va_start(ap, fmt); - vasprintf(&msg, fmt, ap); - va_end(ap); - - pboot_spane->text = msg; - twin_window_damage(pboot_spane->window, - 0, 0, - pboot_spane->window->pixmap->width, - pboot_spane->window->pixmap->height); - twin_window_draw(pboot_spane->window); -} - -static void pboot_create_spane(void) -{ - pboot_spane = calloc(1, sizeof(pboot_spane_t)); - assert(pboot_spane); - - pboot_spane->window = twin_window_create(pboot_screen, TWIN_ARGB32, - TwinWindowPlain, - PBOOT_LEFT_PANE_SIZE + - PBOOT_STATUS_PANE_XYMARGIN, - pboot_screen->height - - PBOOT_STATUS_PANE_HEIGHT, - pboot_screen->width - - PBOOT_LEFT_PANE_SIZE - - 2*PBOOT_STATUS_PANE_XYMARGIN, - PBOOT_STATUS_PANE_HEIGHT); - assert(pboot_spane->window); - - pboot_spane->window->draw = pboot_spane_draw; - pboot_spane->window->client_data = pboot_spane; - pboot_spane->text = strdup(PBOOT_INITIAL_MESSAGE); - twin_window_show(pboot_spane->window); - twin_window_queue_paint(pboot_spane->window); -} - -int pboot_add_device(const char *dev_id, const char *name, - twin_pixmap_t *pixmap) -{ - int index; - pboot_device_t *dev; - - if (pboot_dev_count >= PBOOT_MAX_DEV) - return -1; - - index = pboot_dev_count++; - - dev = malloc(sizeof(*dev)); - memset(dev, 0, sizeof(*dev)); - dev->id = malloc(strlen(dev_id) + 1); - strcpy(dev->id, dev_id); - dev->badge = pixmap; - dev->box.left = PBOOT_LEFT_ICON_XOFF; - dev->box.right = dev->box.left + PBOOT_LEFT_ICON_WIDTH; - dev->box.top = PBOOT_LEFT_ICON_YOFF + - PBOOT_LEFT_ICON_STRIDE * index; - dev->box.bottom = dev->box.top + PBOOT_LEFT_ICON_HEIGHT; - - pboot_devices[index] = dev; - - twin_window_damage(pboot_lpane->window, - dev->box.left, dev->box.top, - dev->box.right, dev->box.bottom); - twin_window_queue_paint(pboot_lpane->window); - - return index; -} - -int pboot_remove_device(const char *dev_id) -{ - pboot_device_t *dev = NULL; - int i, newsel = pboot_dev_sel; - - /* find the matching device */ - for (i = 0; i < pboot_dev_count; i++) { - if (!strcmp(pboot_devices[i]->id, dev_id)) { - dev = pboot_devices[i]; - break; - } - } - - if (!dev) - return TWIN_FALSE; - - memmove(pboot_devices + i, pboot_devices + i + 1, - sizeof(*pboot_devices) * (pboot_dev_count + i - 1)); - pboot_devices[--pboot_dev_count] = NULL; - - /* select the newly-focussed device */ - if (pboot_dev_sel > i) - newsel = pboot_dev_sel - 1; - else if (pboot_dev_sel == i && i >= pboot_dev_count) - newsel = pboot_dev_count - 1; - pboot_set_device_select(newsel, 1); - - /* todo: free device & options */ - - return TWIN_TRUE; -} - -static void pboot_make_background(void) -{ - twin_pixmap_t *filepic, *scaledpic; - const char *background_path; - - /* Set background pixmap */ - LOG("loading background..."); - background_path = artwork_pathname("background.jpg"); - filepic = twin_jpeg_to_pixmap(background_path, TWIN_ARGB32); - LOG("%s\n", filepic ? "ok" : "failed"); - - if (filepic == NULL) - return; - - if (pboot_screen->height == filepic->height && - pboot_screen->width == filepic->width) - scaledpic = filepic; - else { - twin_fixed_t sx, sy; - twin_operand_t srcop; - - scaledpic = twin_pixmap_create(TWIN_ARGB32, - pboot_screen->width, - pboot_screen->height); - if (scaledpic == NULL) { - twin_pixmap_destroy(filepic); - return; - } - sx = twin_fixed_div(twin_int_to_fixed(filepic->width), - twin_int_to_fixed(pboot_screen->width)); - sy = twin_fixed_div(twin_int_to_fixed(filepic->height), - twin_int_to_fixed(pboot_screen->height)); - - twin_matrix_scale(&filepic->transform, sx, sy); - srcop.source_kind = TWIN_PIXMAP; - srcop.u.pixmap = filepic; - twin_composite(scaledpic, 0, 0, &srcop, 0, 0, - NULL, 0, 0, TWIN_SOURCE, - pboot_screen->width, pboot_screen->height); - twin_pixmap_destroy(filepic); - - } - twin_screen_set_background(pboot_screen, scaledpic); -} - -#define PS3FB_IOCTL_SETMODE _IOW('r', 1, int) -#define PS3FB_IOCTL_GETMODE _IOR('r', 2, int) - -static void exitfunc(void) -{ -#ifndef _USE_X11 - if (pboot_fbdev) - twin_fbdev_destroy(pboot_fbdev); - pboot_fbdev = NULL; - if (pboot_vmode_change != -1) { - int fd = open("/dev/fb0", O_RDWR); - if (fd >= 0) - ioctl(fd, PS3FB_IOCTL_SETMODE, - (unsigned long)&pboot_vmode_change); - close(fd); - } -#endif -} - -static void sigint(int sig) -{ - exitfunc(); - syscall(__NR_exit); -} - -static void usage(const char *progname) -{ - fprintf(stderr, "Usage: %s [-u] [-h]\n", progname); -} - -int main(int argc, char **argv) -{ - int c; - int udev_trigger = 0; - - for (;;) { - c = getopt(argc, argv, "u::h"); - if (c == -1) - break; - - switch (c) { - case 'u': - udev_trigger = 1; - break; - case 'h': - usage(argv[0]); - return EXIT_SUCCESS; - default: - fprintf(stderr, "Unknown option '%c'\n", c); - usage(argv[0]); - return EXIT_FAILURE; - } - } - - atexit(exitfunc); - signal(SIGINT, sigint); - -#ifdef _USE_X11 - pboot_x11 = twin_x11_create(XOpenDisplay(0), 1024, 768); - if (pboot_x11 == NULL) { - perror("failed to create x11 screen !\n"); - return 1; - } - pboot_screen = pboot_x11->screen; -#else - /* Create screen and mouse drivers */ - pboot_fbdev = twin_fbdev_create(-1, SIGUSR1); - if (pboot_fbdev == NULL) { - perror("failed to create fbdev screen !\n"); - return 1; - } - pboot_screen = pboot_fbdev->screen; - twin_linux_mouse_create(NULL, pboot_screen); - twin_linux_js_create(pboot_screen); - - if (pboot_fbdev != NULL) { - char *cursor_path = artwork_pathname("cursor.gz"); - pboot_cursor = twin_load_X_cursor(cursor_path, 2, - &pboot_cursor_hx, - &pboot_cursor_hy); - if (pboot_cursor == NULL) - pboot_cursor = - twin_get_default_cursor(&pboot_cursor_hx, - &pboot_cursor_hy); - } -#endif - - /* Set background pixmap */ - pboot_make_background(); - - /* Init more stuffs */ - pboot_create_lpane(); - pboot_create_rpane(); - pboot_create_spane(); - - if (!pboot_start_device_discovery(udev_trigger)) { - LOG("Couldn't start device discovery!\n"); - return 1; - } - - pboot_set_lfocus(0); - twin_screen_set_active(pboot_screen, pboot_lpane->window->pixmap); - pboot_screen->event_filter = pboot_event_filter; - - /* Console switch */ -#ifndef _USE_X11 - if (pboot_fbdev) - twin_fbdev_activate(pboot_fbdev); -#endif - - /* Process events */ - twin_dispatch (); - - return 0; -} diff --git a/petitboot.h b/petitboot.h deleted file mode 100644 index 893be04..0000000 --- a/petitboot.h +++ /dev/null @@ -1,18 +0,0 @@ - -#include -#include - -#define LOG(fmt...) printf(fmt) - -#define PBOOT_MAX_DEV 16 -#define PBOOT_MAX_OPTION 16 - -int pboot_add_device(const char *dev_id, const char *name, - twin_pixmap_t *pixmap); -int pboot_add_option(int devindex, const char *title, - const char *subtitle, twin_pixmap_t *badge, void *data); -int pboot_remove_device(const char *dev_id); - -int pboot_start_device_discovery(int udev_trigger); -void pboot_exec_option(void *data); -void pboot_message(const char *fmt, ...); diff --git a/rules.mk b/rules.mk index 9239d29..f8d64c6 100644 --- a/rules.mk +++ b/rules.mk @@ -1,36 +1,56 @@ VPATH = $(srcdir) +CFLAGS += -I$(top_srcdir) -I$(top_srcdir)/lib -I$(builddir) + # we need paths to be overridable at build-time DEFS += '-DPREFIX="$(prefix)"' '-DPKG_SHARE_DIR="$(pkgdatadir)"' +#uis = ui/twin/pb-twin +uis = ui/test/pb-test parsers = native yaboot kboot artwork = background.jpg cdrom.png hdd.png usbpen.png tux.png cursor.gz -petitboot_objs = petitboot.o devices.o -parser_objs = devices/params.o devices/parser.o devices/paths.o \ - devices/yaboot-cfg.o \ - $(foreach p,$(parsers),devices/$(p)-parser.o) +talloc_objs = lib/talloc/talloc.o +list_objs = lib/list/list.o +server_objs = lib/pb-protocol/pb-protocol.o -petitboot_udev_helper_objs = devices/petitboot-udev-helper.o $(parser_objs) parser_test_objs = parser-test.o $(parser_objs) -all: petitboot petitboot-udev-helper +all: $(uis) discover/pb-discover + +# twin gui +ui/twin/pb-twin: LDFLAGS+=$(twin_LDFLAGS) +ui/twin/pb-twin: CFLAGS+=$(twin_CFLAGS) -petitboot: LDFLAGS+=$(twin_LDFLAGS) -petitboot: CFLAGS+=$(twin_CFLAGS) +pb_twin_objs = ui/twin/pb-twin.o ui/common/devices.o -petitboot: $(petitboot_objs) +ui/twin/pb-twin: $(pb_twin_objs) $(LINK.o) -o $@ $^ -petitboot-udev-helper: $(petitboot_udev_helper_objs) +# test ui +pb_test_objs = ui/test/pb-test.o ui/common/discover-client.o \ + $(talloc_objs) $(server_objs) + +ui/test/pb-test: $(pb_test_objs) $(LINK.o) -o $@ $^ -parser-test: $(parser_test_objs) +# discovery daemon +#pb_discover_objs = discover/params.o discover/parser.o discover/paths.o \ +# discover/yaboot-cfg.o \ +# $(foreach p,$(parsers),discover/$(p)-parser.o) + +pb_discover_objs = discover/pb-discover.o discover/udev.o discover/log.o \ + discover/waiter.o discover/discover-server.o \ + $(talloc_objs) $(server_objs) $(list_objs) + +discover/pb-discover: $(pb_discover_objs) $(LINK.o) -o $@ $^ -petitboot-udev-helper: CFLAGS+=-I$(top_srcdir) + +parser-test: $(parser_test_objs) + $(LINK.o) -o $@ $^ install: all $(INSTALL) -D petitboot $(DESTDIR)$(sbindir)/petitboot @@ -60,6 +80,9 @@ $(PACKAGE)-$(VERSION): clean done clean: rm -rf $(PACKAGE)-$(VERSION) - rm -f petitboot - rm -f petitboot-udev-helper - rm -f *.o devices/*.o + rm -f $(uis) + rm -f $(pb_twin_objs) $(pb_test_objs) + rm -f $(pb_discover_objs) + rm -f discover/pb-discover + rm -f ui/test/pb-test + diff --git a/test/parser-test.c b/test/parser-test.c new file mode 100644 index 0000000..8c94d3f --- /dev/null +++ b/test/parser-test.c @@ -0,0 +1,85 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include "parser.h" +#include "paths.h" + +void pb_log(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +int mount_device(const char *dev_path) +{ + printf("[mount] %s\n", dev_path); + return 0; +} + +static int device_idx; +static int option_idx; + +int add_device(const struct device *dev) +{ + printf("[dev %2d] id: %s\n", device_idx, dev->id); + printf("[dev %2d] name: %s\n", device_idx, dev->name); + printf("[dev %2d] description: %s\n", device_idx, dev->description); + printf("[dev %2d] boot_image: %s\n", device_idx, dev->icon_file); + + device_idx++; + option_idx = 0; + return 0; +} + + +int add_boot_option(const struct boot_option *opt) +{ + if (!device_idx) { + fprintf(stderr, "Option (%s) added before device\n", + opt->name); + exit(EXIT_FAILURE); + } + + printf("[opt %2d] name: %s\n", option_idx, opt->name); + printf("[opt %2d] description: %s\n", option_idx, opt->description); + printf("[opt %2d] boot_image: %s\n", option_idx, opt->boot_image_file); + printf("[opt %2d] initrd: %s\n", option_idx, opt->initrd_file); + printf("[opt %2d] boot_args: %s\n", option_idx, opt->boot_args); + + option_idx++; + + return 0; +} + +enum generic_icon_type guess_device_type(void) +{ + return ICON_TYPE_UNKNOWN; +} + +int main(int argc, char **argv) +{ + char *mountpoint, *dev; + + if (argc != 3) { + fprintf(stderr, "usage: %s \n", argv[0]); + return EXIT_FAILURE; + } + + mountpoint = argv[1]; + dev = argv[2]; + + set_mount_base(mountpoint); + + iterate_parsers(dev, mountpoint); + + + return EXIT_SUCCESS; +} diff --git a/test/parser-test.sh b/test/parser-test.sh new file mode 100755 index 0000000..140601e --- /dev/null +++ b/test/parser-test.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +testdir=devices/parser-tests +default_rootdev=ps3da1 + +function test_dir() +{ + dir="$1" + rootdev=$default_rootdev + if [ -e "$dir/rootdev" ] + then + rootdev=$(cat "$dir/rootdev") + fi + ./parser-test "$dir" /dev/$rootdev 2>/dev/null | + diff -u "$dir/expected-output" - +} + +set -ex + +for test in $testdir/* +do + echo $test + test_dir "$test" +done + +echo "All tests passed" diff --git a/test/parser/001/expected-output b/test/parser/001/expected-output new file mode 100644 index 0000000..bace9f7 --- /dev/null +++ b/test/parser/001/expected-output @@ -0,0 +1,24 @@ +[dev 0] id: /dev/ps3da1 +[dev 0] name: (null) +[dev 0] description: (null) +[dev 0] boot_image: /usr/share/petitboot/artwork/hdd.png +[opt 0] name: live +[opt 0] description: /casper/vmlinux root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet splash -- +[opt 0] boot_image: devices/parser-tests/001/ps3da1/casper/vmlinux +[opt 0] initrd: devices/parser-tests/001/ps3da1/casper/initrd.gz +[opt 0] boot_args: root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet splash -- +[opt 1] name: live_nosplash +[opt 1] description: /casper/vmlinux root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet -- +[opt 1] boot_image: devices/parser-tests/001/ps3da1/casper/vmlinux +[opt 1] initrd: devices/parser-tests/001/ps3da1/casper/initrd.gz +[opt 1] boot_args: root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet -- +[opt 2] name: driverupdates +[opt 2] description: /casper/vmlinux root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper debian-installer/driver-update=true quiet splash -- +[opt 2] boot_image: devices/parser-tests/001/ps3da1/casper/vmlinux +[opt 2] initrd: devices/parser-tests/001/ps3da1/casper/initrd.gz +[opt 2] boot_args: root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper debian-installer/driver-update=true quiet splash -- +[opt 3] name: check +[opt 3] description: /casper/vmlinux root=/dev/ram0 initrd=/casper/initrd.gz boot=casper integrity-check quiet splash -- +[opt 3] boot_image: devices/parser-tests/001/ps3da1/casper/vmlinux +[opt 3] initrd: devices/parser-tests/001/ps3da1/casper/initrd.gz +[opt 3] boot_args: root=/dev/ram0 initrd=/casper/initrd.gz boot=casper integrity-check quiet splash -- diff --git a/test/parser/001/ps3da1/etc/kboot.conf b/test/parser/001/ps3da1/etc/kboot.conf new file mode 100644 index 0000000..591c51b --- /dev/null +++ b/test/parser/001/ps3da1/etc/kboot.conf @@ -0,0 +1,9 @@ +# Ubuntu feisty kboot.conf +message=/etc/kboot.msg +timeout=300 +default=live +live='/casper/vmlinux initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet splash --' +live_nosplash='/casper/vmlinux initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet --' +driverupdates='/casper/vmlinux initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper debian-installer/driver-update=true quiet splash --' +check='/casper/vmlinux initrd=/casper/initrd.gz boot=casper integrity-check quiet splash --' + diff --git a/test/parser/002/expected-output b/test/parser/002/expected-output new file mode 100644 index 0000000..304f15c --- /dev/null +++ b/test/parser/002/expected-output @@ -0,0 +1,9 @@ +[dev 0] id: /dev/ps3da1 +[dev 0] name: (null) +[dev 0] description: +[dev 0] boot_image: /usr/share/petitboot/artwork/hdd.png +[opt 0] name: linux +[opt 0] description: (null) +[opt 0] boot_image: devices/parser-tests/002/ps3da1/ppc/ppc64/vmlinux +[opt 0] initrd: devices/parser-tests/002/ps3da1/ppc/ppc64/ramdisk.image.gz +[opt 0] boot_args: ro diff --git a/test/parser/002/ps3da1/etc/yaboot.conf b/test/parser/002/ps3da1/etc/yaboot.conf new file mode 100644 index 0000000..f13b1b3 --- /dev/null +++ b/test/parser/002/ps3da1/etc/yaboot.conf @@ -0,0 +1,8 @@ +init-message = "\nWelcome to the 64-bit Yellow Dog Linux 5.0 installer!\nHit for boot options.\n\n" +timeout=6000 +default=linux + +image=/ppc/ppc64/vmlinux + label=linux + initrd=/ppc/ppc64/ramdisk.image.gz + read-only diff --git a/test/parser/003/expected-output b/test/parser/003/expected-output new file mode 100644 index 0000000..4f60310 --- /dev/null +++ b/test/parser/003/expected-output @@ -0,0 +1,9 @@ +[dev 0] id: /dev/ps3da1 +[dev 0] name: (null) +[dev 0] description: (null) +[dev 0] boot_image: /usr/share/petitboot/artwork/hdd.png +[opt 0] name: test +[opt 0] description: /dev/sda1:/vmlinux +[opt 0] boot_image: devices/parser-tests/003/ps3da1/vmlinux +[opt 0] initrd: (null) +[opt 0] boot_args: (null) diff --git a/test/parser/003/ps3da1/etc/kboot.conf b/test/parser/003/ps3da1/etc/kboot.conf new file mode 100644 index 0000000..a7bb199 --- /dev/null +++ b/test/parser/003/ps3da1/etc/kboot.conf @@ -0,0 +1,4 @@ +# test remapping sda to ps3da, when mounted from a ps3da device + +test='/dev/sda1:/vmlinux' + diff --git a/test/parser/004/expected-output b/test/parser/004/expected-output new file mode 100644 index 0000000..76a90a2 --- /dev/null +++ b/test/parser/004/expected-output @@ -0,0 +1,9 @@ +[dev 0] id: /dev/sda1 +[dev 0] name: (null) +[dev 0] description: (null) +[dev 0] boot_image: /usr/share/petitboot/artwork/hdd.png +[opt 0] name: test +[opt 0] description: /dev/sda1:/vmlinux +[opt 0] boot_image: devices/parser-tests/004/sda1/vmlinux +[opt 0] initrd: (null) +[opt 0] boot_args: (null) diff --git a/test/parser/004/rootdev b/test/parser/004/rootdev new file mode 100644 index 0000000..36cfa0d --- /dev/null +++ b/test/parser/004/rootdev @@ -0,0 +1 @@ +sda1 diff --git a/test/parser/004/sda1/etc/kboot.conf b/test/parser/004/sda1/etc/kboot.conf new file mode 100644 index 0000000..9755f77 --- /dev/null +++ b/test/parser/004/sda1/etc/kboot.conf @@ -0,0 +1,4 @@ +# test remapping sda to ps3da, when mounted from a plain sd device + +test='/dev/sda1:/vmlinux' + diff --git a/test/parser/005/expected-output b/test/parser/005/expected-output new file mode 100644 index 0000000..bfaccc8 --- /dev/null +++ b/test/parser/005/expected-output @@ -0,0 +1,14 @@ +[dev 0] id: /dev/ps3da1 +[dev 0] name: (null) +[dev 0] description: (null) +[dev 0] boot_image: /usr/share/petitboot/artwork/hdd.png +[opt 0] name: test_uuid +[opt 0] description: UUID=meep:/vmlinux +[opt 0] boot_image: devices/parser-tests/005/disk/by-uuid/meep/vmlinux +[opt 0] initrd: (null) +[opt 0] boot_args: (null) +[opt 1] name: test_label +[opt 1] description: LABEL=meep:/vmlinux +[opt 1] boot_image: devices/parser-tests/005/disk/by-label/meep/vmlinux +[opt 1] initrd: (null) +[opt 1] boot_args: (null) diff --git a/test/parser/005/ps3da1/etc/kboot.conf b/test/parser/005/ps3da1/etc/kboot.conf new file mode 100644 index 0000000..72b7db8 --- /dev/null +++ b/test/parser/005/ps3da1/etc/kboot.conf @@ -0,0 +1,6 @@ + +# test for LABEL= and UUID= lookups + +test_uuid='UUID=meep:/vmlinux' +test_label='LABEL=meep:/vmlinux' + diff --git a/test/parser/101/expected-output b/test/parser/101/expected-output new file mode 100644 index 0000000..45d99a1 --- /dev/null +++ b/test/parser/101/expected-output @@ -0,0 +1,24 @@ +[dev 0] id: /dev/ps3da1 +[dev 0] name: (null) +[dev 0] description: (null) +[dev 0] boot_image: /usr/share/petitboot/artwork/hdd.png +[opt 0] name: ydl +[opt 0] description: /dev/sda1:/vmlinux-2.6.22-0.ydl.rc4 root=LABEL=/ initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img init=/sbin/init video=ps3fb:mode:3 rhgb +[opt 0] boot_image: devices/parser-tests/101/ps3da1/vmlinux-2.6.22-0.ydl.rc4 +[opt 0] initrd: devices/parser-tests/101/ps3da1/initrd-2.6.22-0.ydl.rc4.img +[opt 0] boot_args: root=LABEL=/ initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img init=/sbin/init video=ps3fb:mode:3 rhgb +[opt 1] name: ydl480i +[opt 1] description: /dev/sda1:/vmlinux-2.6.22-0.ydl.rc4 root=LABEL=/ initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img init=/sbin/init video=ps3fb:mode:1 rhgb +[opt 1] boot_image: devices/parser-tests/101/ps3da1/vmlinux-2.6.22-0.ydl.rc4 +[opt 1] initrd: devices/parser-tests/101/ps3da1/initrd-2.6.22-0.ydl.rc4.img +[opt 1] boot_args: root=LABEL=/ initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img init=/sbin/init video=ps3fb:mode:1 rhgb +[opt 2] name: ydl1080i +[opt 2] description: /dev/sda1:/vmlinux-2.6.22-0.ydl.rc4 root=LABEL=/ initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img init=/sbin/init video=ps3fb:mode:4 rhgb +[opt 2] boot_image: devices/parser-tests/101/ps3da1/vmlinux-2.6.22-0.ydl.rc4 +[opt 2] initrd: devices/parser-tests/101/ps3da1/initrd-2.6.22-0.ydl.rc4.img +[opt 2] boot_args: root=LABEL=/ initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img init=/sbin/init video=ps3fb:mode:4 rhgb +[opt 3] name: ydltext +[opt 3] description: /dev/sda1:/vmlinux-2.6.22-0.ydl.rc4 root=LABEL=/ initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img init=/sbin/init 3 +[opt 3] boot_image: devices/parser-tests/101/ps3da1/vmlinux-2.6.22-0.ydl.rc4 +[opt 3] initrd: devices/parser-tests/101/ps3da1/initrd-2.6.22-0.ydl.rc4.img +[opt 3] boot_args: root=LABEL=/ initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img init=/sbin/init 3 diff --git a/test/parser/101/ps3da1/etc/kboot.conf b/test/parser/101/ps3da1/etc/kboot.conf new file mode 100644 index 0000000..4a986c0 --- /dev/null +++ b/test/parser/101/ps3da1/etc/kboot.conf @@ -0,0 +1,12 @@ +# kboot.conf for ydl + +default=ydl +timeout=10 +root=/dev/sda1 +ydl='/dev/sda1:/vmlinux-2.6.22-0.ydl.rc4 initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img root=LABEL=/ init=/sbin/init video=ps3fb:mode:3 rhgb' +ydl480i='/dev/sda1:/vmlinux-2.6.22-0.ydl.rc4 initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img root=LABEL=/ init=/sbin/init video=ps3fb:mode:1 rhgb' +ydl1080i='/dev/sda1:/vmlinux-2.6.22-0.ydl.rc4 initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img root=LABEL=/ init=/sbin/init video=ps3fb:mode:4 rhgb' +ydltext='/dev/sda1:/vmlinux-2.6.22-0.ydl.rc4 initrd=/dev/sda1:/initrd-2.6.22-0.ydl.rc4.img root=LABEL=/ init=/sbin/init 3' + + + diff --git a/test/parser/102/expected-output b/test/parser/102/expected-output new file mode 100644 index 0000000..cc0d096 --- /dev/null +++ b/test/parser/102/expected-output @@ -0,0 +1,24 @@ +[dev 0] id: /dev/ps3da1 +[dev 0] name: (null) +[dev 0] description: (null) +[dev 0] boot_image: /usr/share/petitboot/artwork/hdd.png +[opt 0] name: live +[opt 0] description: /casper/vmlinux root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet splash -- +[opt 0] boot_image: devices/parser-tests/102/ps3da1/casper/vmlinux +[opt 0] initrd: devices/parser-tests/102/ps3da1/casper/initrd.gz +[opt 0] boot_args: root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet splash -- +[opt 1] name: live_nosplash +[opt 1] description: /casper/vmlinux root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet -- +[opt 1] boot_image: devices/parser-tests/102/ps3da1/casper/vmlinux +[opt 1] initrd: devices/parser-tests/102/ps3da1/casper/initrd.gz +[opt 1] boot_args: root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet -- +[opt 2] name: driverupdates +[opt 2] description: /casper/vmlinux root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper debian-installer/driver-update=true quiet splash -- +[opt 2] boot_image: devices/parser-tests/102/ps3da1/casper/vmlinux +[opt 2] initrd: devices/parser-tests/102/ps3da1/casper/initrd.gz +[opt 2] boot_args: root=/dev/ram0 initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper debian-installer/driver-update=true quiet splash -- +[opt 3] name: check +[opt 3] description: /casper/vmlinux root=/dev/ram0 initrd=/casper/initrd.gz boot=casper integrity-check quiet splash -- +[opt 3] boot_image: devices/parser-tests/102/ps3da1/casper/vmlinux +[opt 3] initrd: devices/parser-tests/102/ps3da1/casper/initrd.gz +[opt 3] boot_args: root=/dev/ram0 initrd=/casper/initrd.gz boot=casper integrity-check quiet splash -- diff --git a/test/parser/102/ps3da1/etc/kboot.conf b/test/parser/102/ps3da1/etc/kboot.conf new file mode 100644 index 0000000..591c51b --- /dev/null +++ b/test/parser/102/ps3da1/etc/kboot.conf @@ -0,0 +1,9 @@ +# Ubuntu feisty kboot.conf +message=/etc/kboot.msg +timeout=300 +default=live +live='/casper/vmlinux initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet splash --' +live_nosplash='/casper/vmlinux initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper quiet --' +driverupdates='/casper/vmlinux initrd=/casper/initrd.gz file=/cdrom/preseed/ubuntu.seed boot=casper debian-installer/driver-update=true quiet splash --' +check='/casper/vmlinux initrd=/casper/initrd.gz boot=casper integrity-check quiet splash --' + diff --git a/ui/common/discover-client.c b/ui/common/discover-client.c new file mode 100644 index 0000000..bc5a4fa --- /dev/null +++ b/ui/common/discover-client.c @@ -0,0 +1,111 @@ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "ui/common/discover-client.h" +#include "ui/common/device.h" +#include "pb-protocol/pb-protocol.h" + +struct discover_client { + int fd; + struct discover_client_ops ops; +}; + +static int discover_client_destructor(void *arg) +{ + struct discover_client *client = arg; + + if (client->fd >= 0) + close(client->fd); + + return 0; +} + +struct discover_client* discover_client_init(struct discover_client_ops *ops) +{ + struct discover_client *client; + struct sockaddr_un addr; + + client = talloc(NULL, struct discover_client); + if (!client) + return NULL; + + memcpy(&client->ops, ops, sizeof(client->ops)); + + client->fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (!client->fd < 0) { + perror("socket"); + goto out_err; + } + + talloc_set_destructor(client, discover_client_destructor); + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, PB_SOCKET_PATH); + + if (connect(client->fd, (struct sockaddr *)&addr, sizeof(addr))) { + perror("connect"); + goto out_err; + } + + return client; + +out_err: + talloc_free(client); + return NULL; +} + +int discover_client_get_fd(struct discover_client *client) +{ + return client->fd; +} + +void discover_client_destroy(struct discover_client *client) +{ + talloc_free(client); +} + +int discover_client_process(struct discover_client *client) +{ + struct pb_protocol_message *message; + struct device *dev; + char *dev_id; + + message = pb_protocol_read_message(client, client->fd); + + if (!message) + return 0; + + switch (message->action) { + case PB_PROTOCOL_ACTION_ADD: + dev = pb_protocol_deserialise_device(client, message); + if (!dev) { + printf("no device?\n"); + return 0; + } + client->ops.add_device(dev); + talloc_free(dev); + break; + case PB_PROTOCOL_ACTION_REMOVE: + dev_id = pb_protocol_deserialise_string(client, message); + if (!dev_id) { + printf("no device id?\n"); + return 0; + } + client->ops.remove_device(dev_id); + break; + default: + printf("unknown action %d\n", message->action); + } + + + return 0; +} diff --git a/ui/common/discover-client.h b/ui/common/discover-client.h new file mode 100644 index 0000000..3ce745b --- /dev/null +++ b/ui/common/discover-client.h @@ -0,0 +1,29 @@ +#ifndef _DISCOVER_CLIENT_H +#define _DISCOVER_CLIENT_H + +#include +#include "ui/common/device.h" + +struct discover_client; + +struct discover_client_ops { + int (*add_device)(struct device *); + void (*remove_device)(char *); +}; + +struct discover_client *discover_client_init(struct discover_client_ops *ops); + +int discover_client_get_fd(struct discover_client *client); + +void discover_client_destroy(struct discover_client *client); + +/** + * Process data from the server. + * + * Will read from the client socket, and call add_device on any discovered + * devices. + * + */ +int discover_client_process(struct discover_client *client); + +#endif diff --git a/ui/test/pb-test.c b/ui/test/pb-test.c new file mode 100644 index 0000000..a36e991 --- /dev/null +++ b/ui/test/pb-test.c @@ -0,0 +1,62 @@ + +#include + +#include "ui/common/discover-client.h" +#include "ui/common/device.h" + +static int print_device_add(struct device *device) +{ + int i; + + printf("new device:\n"); + printf("\tid: %s\n", device->id); + printf("\tname: %s\n", device->name); + printf("\tdesc: %s\n", device->description); + printf("\ticon: %s\n", device->icon_file); + + printf("\t%d boot options:\n", device->n_options); + for (i = 0; i < device->n_options; i++) { + struct boot_option *opt = &device->options[i]; + printf("\t\tid: %s\n", opt->id); + printf("\t\tname: %s\n", opt->name); + printf("\t\tdesc: %s\n", opt->description); + printf("\t\ticon: %s\n", opt->icon_file); + printf("\t\tboot: %s\n", opt->boot_image_file); + printf("\t\tinit: %s\n", opt->initrd_file); + printf("\t\targs: %s\n", opt->boot_args); + } + + return 0; +} + +static void print_device_remove(const char *dev_id) +{ + printf("removed device:\n"); + printf("\tid: %s\n", dev_id); +} + +struct discover_client_ops client_ops = { + .add_device = print_device_add, + .remove_device = print_device_remove, +}; + +int main(void) +{ + struct discover_client *client; + + printf("pid: %d\n", getpid()); + + client = discover_client_init(&client_ops); + if (!client) + return -1; + + for (;;) { + int rc; + + rc = discover_client_process(client); + if (rc) + break; + } + + return 0; +} diff --git a/ui/twin/artwork/background.jpg b/ui/twin/artwork/background.jpg new file mode 100644 index 0000000..9d54d31 Binary files /dev/null and b/ui/twin/artwork/background.jpg differ diff --git a/ui/twin/artwork/cdrom.png b/ui/twin/artwork/cdrom.png new file mode 100644 index 0000000..8b3eeb6 Binary files /dev/null and b/ui/twin/artwork/cdrom.png differ diff --git a/ui/twin/artwork/cursor.gz b/ui/twin/artwork/cursor.gz new file mode 100644 index 0000000..b73b72f Binary files /dev/null and b/ui/twin/artwork/cursor.gz differ diff --git a/ui/twin/artwork/hdd.png b/ui/twin/artwork/hdd.png new file mode 100644 index 0000000..2b1594c Binary files /dev/null and b/ui/twin/artwork/hdd.png differ diff --git a/ui/twin/artwork/tux.png b/ui/twin/artwork/tux.png new file mode 100644 index 0000000..99f3465 Binary files /dev/null and b/ui/twin/artwork/tux.png differ diff --git a/ui/twin/artwork/usbpen.png b/ui/twin/artwork/usbpen.png new file mode 100644 index 0000000..53a365e Binary files /dev/null and b/ui/twin/artwork/usbpen.png differ diff --git a/ui/twin/pb-twin.c b/ui/twin/pb-twin.c new file mode 100644 index 0000000..ee68930 --- /dev/null +++ b/ui/twin/pb-twin.c @@ -0,0 +1,1194 @@ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#undef _USE_X11 + +#include +#include +#include +#include +#include + +#include "petitboot.h" +#include "petitboot-paths.h" + +#ifdef _USE_X11 +#include +static twin_x11_t *pboot_x11; +#else +#include +static twin_fbdev_t *pboot_fbdev; +#endif + +static twin_screen_t *pboot_screen; + +#define PBOOT_INITIAL_MESSAGE \ + "keys: 0=safe 1=720p 2=1080i 3=1080p del=GameOS" + +#define PBOOT_LEFT_PANE_SIZE 160 +#define PBOOT_LEFT_PANE_COLOR 0x80000000 +#define PBOOT_LEFT_LINE_COLOR 0xff000000 + +#define PBOOT_LEFT_FOCUS_WIDTH 80 +#define PBOOT_LEFT_FOCUS_HEIGHT 80 +#define PBOOT_LEFT_FOCUS_XOFF 40 +#define PBOOT_LEFT_FOCUS_YOFF 40 +#define PBOOT_LEFT_FOCUS_XRAD (6 * TWIN_FIXED_ONE) +#define PBOOT_LEFT_FOCUS_YRAD (6 * TWIN_FIXED_ONE) + +#define PBOOT_RIGHT_FOCUS_XOFF 20 +#define PBOOT_RIGHT_FOCUS_YOFF 60 +#define PBOOT_RIGHT_FOCUS_HEIGHT 80 +#define PBOOT_RIGHT_FOCUS_XRAD (6 * TWIN_FIXED_ONE) +#define PBOOT_RIGHT_FOCUS_YRAD (6 * TWIN_FIXED_ONE) + +#define PBOOT_LEFT_ICON_WIDTH 64 +#define PBOOT_LEFT_ICON_HEIGHT 64 +#define PBOOT_LEFT_ICON_XOFF 50 +#define PBOOT_LEFT_ICON_YOFF 50 +#define PBOOT_LEFT_ICON_STRIDE 100 + +#define PBOOT_RIGHT_OPTION_LMARGIN 30 +#define PBOOT_RIGHT_OPTION_RMARGIN 30 +#define PBOOT_RIGHT_OPTION_TMARGIN 70 +#define PBOOT_RIGHT_OPTION_HEIGHT 64 +#define PBOOT_RIGHT_OPTION_STRIDE 100 +#define PBOOT_RIGHT_TITLE_TEXT_SIZE (30 * TWIN_FIXED_ONE) +#define PBOOT_RIGHT_SUBTITLE_TEXT_SIZE (18 * TWIN_FIXED_ONE) +#define PBOOT_RIGHT_TITLE_XOFFSET 80 +#define PBOOT_RIGHT_TITLE_YOFFSET 30 +#define PBOOT_RIGHT_SUBTITLE_XOFFSET 100 +#define PBOOT_RIGHT_SUBTITLE_YOFFSET 50 +#define PBOOT_RIGHT_BADGE_XOFFSET 2 +#define PBOOT_RIGHT_BADGE_YOFFSET 0 + + +#define PBOOT_RIGHT_TITLE_COLOR 0xff000000 +#define PBOOT_RIGHT_SUBTITLE_COLOR 0xff400000 + +#define PBOOT_FOCUS_COLOR 0x10404040 + +#define PBOOT_STATUS_PANE_COLOR 0x60606060 +#define PBOOT_STATUS_PANE_HEIGHT 20 +#define PBOOT_STATUS_PANE_XYMARGIN 20 +#define PBOOT_STATUS_TEXT_MARGIN 10 +#define PBOOT_STATUS_TEXT_SIZE (16 * TWIN_FIXED_ONE) +#define PBOOT_STATUS_TEXT_COLOR 0xff000000 + +typedef struct _pboot_option pboot_option_t; +typedef struct _pboot_device pboot_device_t; + +struct _pboot_option +{ + char *title; + char *subtitle; + twin_pixmap_t *badge; + twin_pixmap_t *cache; + twin_rect_t box; + void *data; +}; + +struct _pboot_device +{ + char *id; + twin_pixmap_t *badge; + twin_rect_t box; + int option_count; + pboot_option_t options[PBOOT_MAX_OPTION]; +}; + +static twin_pixmap_t *pboot_cursor; +static int pboot_cursor_hx; +static int pboot_cursor_hy; + +static pboot_device_t *pboot_devices[PBOOT_MAX_DEV]; +static int pboot_dev_count; +static int pboot_dev_sel = -1; +static int pboot_focus_lpane = 1; + +typedef struct _pboot_lpane { + twin_window_t *window; + twin_rect_t focus_box; + int focus_start; + int focus_target; + int focus_curindex; + int mouse_target; +} pboot_lpane_t; + +typedef struct _pboot_rpane { + twin_window_t *window; + twin_rect_t focus_box; + int focus_start; + int focus_target; + int focus_curindex; + int mouse_target; +} pboot_rpane_t; + +typedef struct _pboot_spane { + twin_window_t *window; + char *text; +} pboot_spane_t; + +static pboot_lpane_t *pboot_lpane; +static pboot_rpane_t *pboot_rpane; +static pboot_spane_t *pboot_spane; + +/* control to keyboard mappings for the sixaxis controller */ +uint8_t sixaxis_map[] = { + 0, /* 0 Select */ + 0, /* 1 L3 */ + 0, /* 2 R3 */ + 0, /* 3 Start */ + KEY_UP, /* 4 Dpad Up */ + KEY_RIGHT, /* 5 Dpad Right */ + KEY_DOWN, /* 6 Dpad Down */ + KEY_LEFT, /* 7 Dpad Left */ + 0, /* 8 L2 */ + 0, /* 9 R2 */ + 0, /* 10 L1 */ + 0, /* 11 R1 */ + 0, /* 12 Triangle */ + KEY_ENTER, /* 13 Circle */ + 0, /* 14 Cross */ + KEY_DELETE, /* 15 Square */ + 0, /* 16 PS Button */ + 0, /* 17 nothing */ + 0, /* 18 nothing */ +}; + + +static int pboot_vmode_change = -1; + +/* XXX move to twin */ +static inline twin_bool_t twin_rect_intersect(twin_rect_t r1, + twin_rect_t r2) +{ + return !(r1.left > r2.right || + r1.right < r2.left || + r1.top > r2.bottom || + r1.bottom < r2.top); +} + +static void pboot_draw_option_cache(pboot_device_t *dev, pboot_option_t *opt, + int index) +{ + twin_pixmap_t *px; + twin_path_t *path; + twin_fixed_t tx, ty; + + /* Create pixmap */ + px = twin_pixmap_create(TWIN_ARGB32, opt->box.right - opt->box.left, + opt->box.bottom - opt->box.top); + assert(px); + opt->cache = px; + + /* Fill background */ + twin_fill(px, 0x00000000, TWIN_SOURCE, 0, 0, px->width, px->height); + + /* Allocate a path for drawing */ + path = twin_path_create(); + assert(path); + +#if 0 + /* TEST - Bounding rectangle */ + twin_path_rectangle(path, 0, 0, + twin_int_to_fixed(px->width), + twin_int_to_fixed(px->height)); + twin_paint_path(px, PBOOT_RIGHT_TITLE_COLOR, path); + twin_path_empty(path); + twin_fill(px, 0x00000000, TWIN_SOURCE, 2, 2, + px->width - 3, px->height - 3); +#endif + + /* Draw texts */ + twin_path_set_font_size(path, PBOOT_RIGHT_TITLE_TEXT_SIZE); + twin_path_set_font_style(path, TWIN_TEXT_UNHINTED); + tx = twin_int_to_fixed(PBOOT_RIGHT_TITLE_XOFFSET); + ty = twin_int_to_fixed(PBOOT_RIGHT_TITLE_YOFFSET); + twin_path_move (path, tx, ty); + twin_path_utf8 (path, opt->title); + twin_paint_path (px, PBOOT_RIGHT_TITLE_COLOR, path); + twin_path_empty (path); + + if (opt->subtitle) { + twin_path_set_font_size(path, PBOOT_RIGHT_SUBTITLE_TEXT_SIZE); + twin_path_set_font_style(path, TWIN_TEXT_UNHINTED); + tx = twin_int_to_fixed(PBOOT_RIGHT_SUBTITLE_XOFFSET); + ty = twin_int_to_fixed(PBOOT_RIGHT_SUBTITLE_YOFFSET); + twin_path_move (path, tx, ty); + twin_path_utf8 (path, opt->subtitle); + twin_paint_path (px, PBOOT_RIGHT_SUBTITLE_COLOR, path); + twin_path_empty (path); + } + + if (opt->badge) { + twin_operand_t src; + + src.source_kind = TWIN_PIXMAP; + src.u.pixmap = opt->badge; + + twin_composite(px, PBOOT_RIGHT_BADGE_XOFFSET, + PBOOT_RIGHT_BADGE_YOFFSET, + &src, 0, 0, NULL, 0, 0, TWIN_OVER, + opt->badge->width, opt->badge->height); + } + + + /* Destroy path */ + twin_path_destroy(path); +} + +static void pboot_rpane_draw(twin_window_t *window) +{ + twin_pixmap_t *px = window->pixmap; + pboot_rpane_t *rpane = window->client_data; + pboot_device_t *dev; + twin_path_t *path; + twin_fixed_t x, y, w, h; + int i; + + /* Fill background */ + twin_fill(px, 0x00000000, TWIN_SOURCE, 0, 0, px->width, px->height); + + /* Nothing to draw, return */ + if (pboot_dev_sel < 0) + return; + + /* Create a path for use later */ + path = twin_path_create(); + assert(path); + + /* Draw focus box */ + if (rpane->focus_curindex >= 0 && + twin_rect_intersect(rpane->focus_box, px->clip)) { + x = twin_int_to_fixed(rpane->focus_box.left + 2); + y = twin_int_to_fixed(rpane->focus_box.top + 2); + w = twin_int_to_fixed(rpane->focus_box.right - + rpane->focus_box.left - 4); + h = twin_int_to_fixed(rpane->focus_box.bottom - + rpane->focus_box.top - 4); + twin_path_rounded_rectangle(path, x, y, w, h, + PBOOT_RIGHT_FOCUS_XRAD, + PBOOT_RIGHT_FOCUS_YRAD); + if (!pboot_focus_lpane) + twin_paint_path(px, PBOOT_FOCUS_COLOR, path); + else + twin_paint_stroke(px, PBOOT_FOCUS_COLOR, path, + 4 * TWIN_FIXED_ONE); + } + + /* Get device and iterate through options */ + dev = pboot_devices[pboot_dev_sel]; + for (i = 0; i < dev->option_count; i++) { + pboot_option_t *opt = &dev->options[i]; + twin_operand_t src; + + if (opt->title == NULL) + continue; + if (!twin_rect_intersect(opt->box, px->clip)) + continue; + if (opt->cache == NULL) + pboot_draw_option_cache(dev, opt, i); + + src.source_kind = TWIN_PIXMAP; + src.u.pixmap = opt->cache; + + twin_composite(px, opt->box.left, opt->box.top, + &src, 0, 0, NULL, 0, 0, TWIN_OVER, + opt->box.right - opt->box.left, + opt->box.bottom - opt->box.top); + } + + /* Destroy path */ + twin_path_destroy(path); +} + +static twin_time_t pboot_rfocus_timeout (twin_time_t now, void *closure) +{ + int dir = 1, dist, pos; + const int accel[11] = { 7, 4, 2, 1, 1, 1, 1, 1, 2, 2, 3 }; + + dist = abs(pboot_rpane->focus_target - pboot_rpane->focus_start); + dir = dist > 5 ? 5 : dist; + pos = pboot_rpane->focus_target - (int)pboot_rpane->focus_box.top; + if (pos == 0) { + return -1; + } + if (pos < 0) { + dir = -dir; + pos = -pos; + } + twin_window_damage(pboot_rpane->window, + pboot_rpane->focus_box.left, + pboot_rpane->focus_box.top, + pboot_rpane->focus_box.right, + pboot_rpane->focus_box.bottom); + + pboot_rpane->focus_box.top += dir; + pboot_rpane->focus_box.bottom += dir; + + twin_window_damage(pboot_rpane->window, + pboot_rpane->focus_box.left, + pboot_rpane->focus_box.top, + pboot_rpane->focus_box.right, + pboot_rpane->focus_box.bottom); + + twin_window_queue_paint(pboot_rpane->window); + + return accel[(pos * 10) / dist]; +} + +static void pboot_set_rfocus(int index) +{ + pboot_device_t *dev; + + if (pboot_dev_sel < 0 || pboot_dev_sel >= pboot_dev_count) + return; + dev = pboot_devices[pboot_dev_sel]; + if (index < 0 || index >= dev->option_count) + return; + + pboot_rpane->focus_start = pboot_rpane->focus_box.top; + pboot_rpane->focus_target = PBOOT_RIGHT_FOCUS_YOFF + + PBOOT_RIGHT_OPTION_STRIDE * index; + pboot_rpane->focus_curindex = index; + + twin_set_timeout(pboot_rfocus_timeout, 0, NULL); +} + +static void pboot_select_rpane(void) +{ + if (pboot_focus_lpane == 0) + return; + pboot_focus_lpane = 0; + + twin_screen_set_active(pboot_screen, pboot_rpane->window->pixmap); + + twin_window_damage(pboot_lpane->window, + pboot_lpane->focus_box.left, + pboot_lpane->focus_box.top, + pboot_lpane->focus_box.right, + pboot_lpane->focus_box.bottom); + + twin_window_damage(pboot_rpane->window, + pboot_rpane->focus_box.left, + pboot_rpane->focus_box.top, + pboot_rpane->focus_box.right, + pboot_rpane->focus_box.bottom); + + twin_window_queue_paint(pboot_lpane->window); + twin_window_queue_paint(pboot_rpane->window); + + pboot_set_rfocus(0); +} + +static void pboot_select_lpane(void) +{ + if (pboot_focus_lpane == 1) + return; + pboot_focus_lpane = 1; + + twin_screen_set_active(pboot_screen, pboot_lpane->window->pixmap); + + twin_window_damage(pboot_lpane->window, + pboot_lpane->focus_box.left, + pboot_lpane->focus_box.top, + pboot_lpane->focus_box.right, + pboot_lpane->focus_box.bottom); + + twin_window_damage(pboot_rpane->window, + pboot_rpane->focus_box.left, + pboot_rpane->focus_box.top, + pboot_rpane->focus_box.right, + pboot_rpane->focus_box.bottom); + + twin_window_queue_paint(pboot_lpane->window); + twin_window_queue_paint(pboot_rpane->window); +} + +static void pboot_rpane_mousetrack(twin_coord_t x, twin_coord_t y) +{ + pboot_device_t *dev; + pboot_option_t *opt; + int candidate = -1; + + if (pboot_dev_sel < 0 || pboot_dev_sel >= pboot_dev_count) + return; + dev = pboot_devices[pboot_dev_sel]; + + if (y < PBOOT_RIGHT_OPTION_TMARGIN) + goto miss; + candidate = (y - PBOOT_RIGHT_OPTION_TMARGIN) / + PBOOT_RIGHT_OPTION_STRIDE; + if (candidate >= dev->option_count) { + candidate = -1; + goto miss; + } + if (candidate == pboot_rpane->mouse_target) + return; + opt = &dev->options[candidate]; + if (x < opt->box.left || x > opt->box.right || + y < opt->box.top || y > opt->box.bottom) { + candidate = -1; + goto miss; + } + + /* Ok, so now, we know the mouse hit an icon that wasn't the same + * as the previous one, we trigger a focus change + */ + pboot_set_rfocus(candidate); + + miss: + pboot_rpane->mouse_target = candidate; +} + +static void pboot_choose_option(void) +{ + pboot_device_t *dev = pboot_devices[pboot_dev_sel]; + pboot_option_t *opt = &dev->options[pboot_rpane->focus_curindex]; + + LOG("Selected device %s\n", opt->title); + pboot_message("booting %s...", opt->title); + + /* Give user feedback, make sure errors and panics will be seen */ + pboot_exec_option(opt->data); +} + +static twin_bool_t pboot_rpane_event (twin_window_t *window, + twin_event_t *event) +{ + /* filter out all mouse events */ + switch(event->kind) { + case TwinEventEnter: + case TwinEventMotion: + case TwinEventLeave: + pboot_select_rpane(); + pboot_rpane_mousetrack(event->u.pointer.x, event->u.pointer.y); + return TWIN_TRUE; + case TwinEventButtonDown: + pboot_select_rpane(); + pboot_rpane_mousetrack(event->u.pointer.x, event->u.pointer.y); + pboot_choose_option(); + case TwinEventButtonUp: + return TWIN_TRUE; + case TwinEventKeyDown: + switch(event->u.key.key) { + case KEY_UP: + pboot_set_rfocus(pboot_rpane->focus_curindex - 1); + return TWIN_TRUE; + case KEY_DOWN: + pboot_set_rfocus(pboot_rpane->focus_curindex + 1); + return TWIN_TRUE; + case KEY_LEFT: + pboot_select_lpane(); + return TWIN_TRUE; + case KEY_ENTER: + pboot_choose_option(); + default: + break; + } + break; + default: + break; + } + return TWIN_FALSE; +} + + +int pboot_add_option(int devindex, const char *title, + const char *subtitle, twin_pixmap_t *badge, void *data) +{ + pboot_device_t *dev; + pboot_option_t *opt; + twin_coord_t width; + int index; + + if (devindex < 0 || devindex >= pboot_dev_count) + return -1; + dev = pboot_devices[devindex]; + + if (dev->option_count >= PBOOT_MAX_OPTION) + return -1; + index = dev->option_count++; + opt = &dev->options[index]; + + opt->title = malloc(strlen(title) + 1); + strcpy(opt->title, title); + + if (subtitle) { + opt->subtitle = malloc(strlen(subtitle) + 1); + strcpy(opt->subtitle, subtitle); + } else + opt->subtitle = NULL; + + opt->badge = badge; + opt->cache = NULL; + + width = pboot_rpane->window->pixmap->width - + (PBOOT_RIGHT_OPTION_LMARGIN + PBOOT_RIGHT_OPTION_RMARGIN); + + opt->box.left = PBOOT_RIGHT_OPTION_LMARGIN; + opt->box.right = opt->box.left + width; + opt->box.top = PBOOT_RIGHT_OPTION_TMARGIN + + index * PBOOT_RIGHT_OPTION_STRIDE; + opt->box.bottom = opt->box.top + PBOOT_RIGHT_OPTION_HEIGHT; + + opt->data = data; + return index; +} + + +static void pboot_set_device_select(int sel, int force) +{ + LOG("%s: %d -> %d\n", __FUNCTION__, pboot_dev_sel, sel); + if (!force && sel == pboot_dev_sel) + return; + if (sel >= pboot_dev_count) + return; + pboot_dev_sel = sel; + if (force) { + pboot_lpane->focus_curindex = sel; + if (sel < 0) + pboot_lpane->focus_target = 0 - PBOOT_LEFT_FOCUS_HEIGHT; + else + pboot_lpane->focus_target = PBOOT_LEFT_FOCUS_YOFF + + PBOOT_LEFT_ICON_STRIDE * sel; + pboot_rpane->focus_box.bottom = pboot_lpane->focus_target; + pboot_rpane->focus_box.bottom = pboot_rpane->focus_box.top + + PBOOT_RIGHT_FOCUS_HEIGHT; + twin_window_damage(pboot_lpane->window, + 0, 0, + pboot_lpane->window->pixmap->width, + pboot_lpane->window->pixmap->height); + twin_window_queue_paint(pboot_lpane->window); + } + pboot_rpane->focus_curindex = -1; + pboot_rpane->mouse_target = -1; + pboot_rpane->focus_box.top = -2*PBOOT_RIGHT_FOCUS_HEIGHT; + pboot_rpane->focus_box.bottom = pboot_rpane->focus_box.top + + PBOOT_RIGHT_FOCUS_HEIGHT; + twin_window_damage(pboot_rpane->window, 0, 0, + pboot_rpane->window->pixmap->width, + pboot_rpane->window->pixmap->height); + twin_window_queue_paint(pboot_rpane->window); +} + +static void pboot_create_rpane(void) +{ + pboot_rpane = calloc(1, sizeof(pboot_rpane_t)); + assert(pboot_rpane); + + pboot_rpane->window = twin_window_create(pboot_screen, TWIN_ARGB32, + TwinWindowPlain, + PBOOT_LEFT_PANE_SIZE, 0, + pboot_screen->width - + PBOOT_LEFT_PANE_SIZE, + pboot_screen->height); + assert(pboot_rpane->window); + + pboot_rpane->window->draw = pboot_rpane_draw; + pboot_rpane->window->event = pboot_rpane_event; + pboot_rpane->window->client_data = pboot_rpane; + + pboot_rpane->focus_curindex = -1; + pboot_rpane->focus_box.left = PBOOT_RIGHT_FOCUS_XOFF; + pboot_rpane->focus_box.top = -2*PBOOT_RIGHT_FOCUS_HEIGHT; + pboot_rpane->focus_box.right = pboot_rpane->window->pixmap->width - + 2 * PBOOT_RIGHT_FOCUS_XOFF; + pboot_rpane->focus_box.bottom = pboot_rpane->focus_box.top + + PBOOT_RIGHT_FOCUS_HEIGHT; + pboot_rpane->mouse_target = -1; + twin_window_show(pboot_rpane->window); + twin_window_queue_paint(pboot_rpane->window); +} + + +static twin_time_t pboot_lfocus_timeout (twin_time_t now, void *closure) +{ + int dir = 1, dist, pos; + const int accel[11] = { 7, 4, 2, 1, 1, 1, 1, 1, 2, 2, 3 }; + + dist = abs(pboot_lpane->focus_target - pboot_lpane->focus_start); + dir = dist > 2 ? 2 : dist; + pos = pboot_lpane->focus_target - (int)pboot_lpane->focus_box.top; + if (pos == 0) { + pboot_set_device_select(pboot_lpane->focus_curindex, 0); + return -1; + } + if (pos < 0) { + dir = -1; + pos = -pos; + } + twin_window_damage(pboot_lpane->window, + pboot_lpane->focus_box.left, + pboot_lpane->focus_box.top, + pboot_lpane->focus_box.right, + pboot_lpane->focus_box.bottom); + + pboot_lpane->focus_box.top += dir; + pboot_lpane->focus_box.bottom += dir; + + twin_window_damage(pboot_lpane->window, + pboot_lpane->focus_box.left, + pboot_lpane->focus_box.top, + pboot_lpane->focus_box.right, + pboot_lpane->focus_box.bottom); + + twin_window_queue_paint(pboot_lpane->window); + + return accel[(pos * 10) / dist]; +} + +static void pboot_set_lfocus(int index) +{ + if (index >= pboot_dev_count) + return; + + pboot_lpane->focus_start = pboot_lpane->focus_box.top; + + if (index < 0) + pboot_lpane->focus_target = 0 - PBOOT_LEFT_FOCUS_HEIGHT; + else + pboot_lpane->focus_target = PBOOT_LEFT_FOCUS_YOFF + + PBOOT_LEFT_ICON_STRIDE * index; + + pboot_lpane->focus_curindex = index; + + twin_set_timeout(pboot_lfocus_timeout, 0, NULL); +} + +static void pboot_lpane_mousetrack(twin_coord_t x, twin_coord_t y) +{ + int candidate = -1; + twin_coord_t icon_top; + + if (x < PBOOT_LEFT_ICON_XOFF || + x > (PBOOT_LEFT_ICON_XOFF + PBOOT_LEFT_ICON_WIDTH)) + goto miss; + if (y < PBOOT_LEFT_ICON_YOFF) + goto miss; + candidate = (y - PBOOT_LEFT_ICON_YOFF) / PBOOT_LEFT_ICON_STRIDE; + if (candidate >= pboot_dev_count) { + candidate = -1; + goto miss; + } + if (candidate == pboot_lpane->mouse_target) + return; + icon_top = PBOOT_LEFT_ICON_YOFF + + candidate * PBOOT_LEFT_ICON_STRIDE; + if (y > (icon_top + PBOOT_LEFT_ICON_HEIGHT)) { + candidate = -1; + goto miss; + } + + /* Ok, so now, we know the mouse hit an icon that wasn't the same + * as the previous one, we trigger a focus change + */ + pboot_set_lfocus(candidate); + + miss: + pboot_lpane->mouse_target = candidate; +} + +static twin_bool_t pboot_lpane_event (twin_window_t *window, + twin_event_t *event) +{ + /* filter out all mouse events */ + switch(event->kind) { + case TwinEventEnter: + case TwinEventMotion: + case TwinEventLeave: + pboot_select_lpane(); + pboot_lpane_mousetrack(event->u.pointer.x, event->u.pointer.y); + return TWIN_TRUE; + case TwinEventButtonDown: + case TwinEventButtonUp: + return TWIN_TRUE; + case TwinEventKeyDown: + switch(event->u.key.key) { + case KEY_UP: + if (pboot_lpane->focus_curindex > 0) + pboot_set_lfocus( + pboot_lpane->focus_curindex - 1); + return TWIN_TRUE; + case KEY_DOWN: + pboot_set_lfocus(pboot_lpane->focus_curindex + 1); + return TWIN_TRUE; + case KEY_RIGHT: + pboot_select_rpane(); + return TWIN_TRUE; + default: + break; + } + break; + default: + break; + } + return TWIN_FALSE; +} + +static void pboot_quit(void) +{ + kill(0, SIGINT); +} + +twin_bool_t pboot_event_filter(twin_screen_t *screen, + twin_event_t *event) +{ + switch(event->kind) { + case TwinEventEnter: + case TwinEventMotion: + case TwinEventLeave: + case TwinEventButtonDown: + case TwinEventButtonUp: + if (pboot_cursor != NULL) + twin_screen_set_cursor(pboot_screen, pboot_cursor, + pboot_cursor_hx, + pboot_cursor_hy); + break; + case TwinEventJoyButton: + /* map joystick events into key events */ + if (event->u.js.control >= sizeof(sixaxis_map)) + break; + + event->u.key.key = sixaxis_map[event->u.js.control]; + if (event->u.js.value == 0) { + event->kind = TwinEventKeyUp; + break; + } else { + event->kind = TwinEventKeyDown; + } + + /* fall through.. */ + case TwinEventKeyDown: + switch(event->u.key.key) { + /* Gross hack for video modes, need something better ! */ + case KEY_0: + pboot_vmode_change = 0; /* auto */ + pboot_quit(); + return TWIN_TRUE; + case KEY_1: + pboot_vmode_change = 3; /* 720p */ + pboot_quit(); + return TWIN_TRUE; + case KEY_2: + pboot_vmode_change = 4; /* 1080i */ + pboot_quit(); + return TWIN_TRUE; + case KEY_3: + pboot_vmode_change = 5; /* 1080p */ + pboot_quit(); + return TWIN_TRUE; + + /* Another gross hack for booting back to gameos */ + case KEY_BACKSPACE: + case KEY_DELETE: + pboot_message("booting to GameOS..."); + system(BOOT_GAMEOS_BIN); + } + case TwinEventKeyUp: + twin_screen_set_cursor(pboot_screen, NULL, 0, 0); + break; + default: + break; + } + return TWIN_FALSE; +} + +static void pboot_lpane_draw(twin_window_t *window) +{ + twin_pixmap_t *px = window->pixmap; + pboot_lpane_t *lpane = window->client_data; + twin_path_t *path; + twin_fixed_t x, y, w, h; + int i; + + /* Fill background */ + twin_fill(px, PBOOT_LEFT_PANE_COLOR, TWIN_SOURCE, + 0, 0, px->width, px->height); + + /* Create a path for use later */ + path = twin_path_create(); + assert(path); + + /* Draw right line if needed */ + if (px->clip.right > (PBOOT_LEFT_PANE_SIZE - 4)) { + x = twin_int_to_fixed(PBOOT_LEFT_PANE_SIZE - 4); + y = twin_int_to_fixed(px->height); + twin_path_rectangle(path, x, 0, 0x40000, y); + twin_paint_path(px, PBOOT_LEFT_LINE_COLOR, path); + twin_path_empty(path); + } + + /* Draw focus box */ + if (lpane->focus_curindex >= 0 && + twin_rect_intersect(lpane->focus_box, px->clip)) { + x = twin_int_to_fixed(lpane->focus_box.left + 2); + y = twin_int_to_fixed(lpane->focus_box.top + 2); + w = twin_int_to_fixed(lpane->focus_box.right - + lpane->focus_box.left - 4); + h = twin_int_to_fixed(lpane->focus_box.bottom - + lpane->focus_box.top - 4); + twin_path_rounded_rectangle(path, x, y, w, h, + PBOOT_LEFT_FOCUS_XRAD, + PBOOT_LEFT_FOCUS_YRAD); + if (pboot_focus_lpane) + twin_paint_path(px, PBOOT_FOCUS_COLOR, path); + else + twin_paint_stroke(px, PBOOT_FOCUS_COLOR, path, + 4 * TWIN_FIXED_ONE); + } + + /* Draw icons */ + for (i = 0; i < pboot_dev_count; i++) { + pboot_device_t *dev = pboot_devices[i]; + twin_operand_t src; + + if (!twin_rect_intersect(dev->box, px->clip)) + continue; + + src.source_kind = TWIN_PIXMAP; + src.u.pixmap = dev->badge; + + twin_composite(px, dev->box.left, dev->box.top, + &src, 0, 0, NULL, 0, 0, TWIN_OVER, + dev->box.right - dev->box.left, + dev->box.bottom - dev->box.top); + + } + + /* Destroy path */ + twin_path_destroy(path); +} + +static void pboot_create_lpane(void) +{ + pboot_lpane = calloc(1, sizeof(pboot_lpane_t)); + assert(pboot_lpane); + + pboot_lpane->window = twin_window_create(pboot_screen, TWIN_ARGB32, + TwinWindowPlain, + 0, 0, PBOOT_LEFT_PANE_SIZE, + pboot_screen->height); + assert(pboot_lpane->window); + + pboot_lpane->window->draw = pboot_lpane_draw; + pboot_lpane->window->event = pboot_lpane_event; + pboot_lpane->window->client_data = pboot_lpane; + pboot_lpane->focus_curindex = -1; + pboot_lpane->focus_box.left = PBOOT_LEFT_FOCUS_XOFF; + pboot_lpane->focus_box.top = -2*PBOOT_LEFT_FOCUS_HEIGHT; + pboot_lpane->focus_box.right = pboot_lpane->focus_box.left + + PBOOT_LEFT_FOCUS_WIDTH; + pboot_lpane->focus_box.bottom = pboot_lpane->focus_box.top + + PBOOT_LEFT_FOCUS_HEIGHT; + pboot_lpane->mouse_target = -1; + twin_window_show(pboot_lpane->window); + twin_window_queue_paint(pboot_lpane->window); +} + +static void pboot_spane_draw(twin_window_t *window) +{ + twin_pixmap_t *px = window->pixmap; + pboot_spane_t *spane = window->client_data; + twin_path_t *path; + twin_fixed_t tx, ty; + + /* Fill background */ + twin_fill(px, PBOOT_STATUS_PANE_COLOR, TWIN_SOURCE, + 0, 0, px->width, px->height); + + path = twin_path_create(); + assert(path); + + twin_path_set_font_size(path, PBOOT_STATUS_TEXT_SIZE); + twin_path_set_font_style(path, TWIN_TEXT_UNHINTED); + tx = twin_int_to_fixed(PBOOT_STATUS_TEXT_MARGIN); + ty = twin_int_to_fixed(PBOOT_STATUS_PANE_HEIGHT - 2); + twin_path_move (path, tx, ty); + twin_path_utf8 (path, spane->text); + twin_paint_path (px, PBOOT_STATUS_TEXT_COLOR, path); + + twin_path_destroy(path); +} + +void pboot_message(const char *fmt, ...) +{ + va_list ap; + char *msg; + + if (pboot_spane->text) + free(pboot_spane->text); + + va_start(ap, fmt); + vasprintf(&msg, fmt, ap); + va_end(ap); + + pboot_spane->text = msg; + twin_window_damage(pboot_spane->window, + 0, 0, + pboot_spane->window->pixmap->width, + pboot_spane->window->pixmap->height); + twin_window_draw(pboot_spane->window); +} + +static void pboot_create_spane(void) +{ + pboot_spane = calloc(1, sizeof(pboot_spane_t)); + assert(pboot_spane); + + pboot_spane->window = twin_window_create(pboot_screen, TWIN_ARGB32, + TwinWindowPlain, + PBOOT_LEFT_PANE_SIZE + + PBOOT_STATUS_PANE_XYMARGIN, + pboot_screen->height - + PBOOT_STATUS_PANE_HEIGHT, + pboot_screen->width - + PBOOT_LEFT_PANE_SIZE - + 2*PBOOT_STATUS_PANE_XYMARGIN, + PBOOT_STATUS_PANE_HEIGHT); + assert(pboot_spane->window); + + pboot_spane->window->draw = pboot_spane_draw; + pboot_spane->window->client_data = pboot_spane; + pboot_spane->text = strdup(PBOOT_INITIAL_MESSAGE); + twin_window_show(pboot_spane->window); + twin_window_queue_paint(pboot_spane->window); +} + +int pboot_add_device(const char *dev_id, const char *name, + twin_pixmap_t *pixmap) +{ + int index; + pboot_device_t *dev; + + if (pboot_dev_count >= PBOOT_MAX_DEV) + return -1; + + index = pboot_dev_count++; + + dev = malloc(sizeof(*dev)); + memset(dev, 0, sizeof(*dev)); + dev->id = malloc(strlen(dev_id) + 1); + strcpy(dev->id, dev_id); + dev->badge = pixmap; + dev->box.left = PBOOT_LEFT_ICON_XOFF; + dev->box.right = dev->box.left + PBOOT_LEFT_ICON_WIDTH; + dev->box.top = PBOOT_LEFT_ICON_YOFF + + PBOOT_LEFT_ICON_STRIDE * index; + dev->box.bottom = dev->box.top + PBOOT_LEFT_ICON_HEIGHT; + + pboot_devices[index] = dev; + + twin_window_damage(pboot_lpane->window, + dev->box.left, dev->box.top, + dev->box.right, dev->box.bottom); + twin_window_queue_paint(pboot_lpane->window); + + return index; +} + +int pboot_remove_device(const char *dev_id) +{ + pboot_device_t *dev = NULL; + int i, newsel = pboot_dev_sel; + + /* find the matching device */ + for (i = 0; i < pboot_dev_count; i++) { + if (!strcmp(pboot_devices[i]->id, dev_id)) { + dev = pboot_devices[i]; + break; + } + } + + if (!dev) + return TWIN_FALSE; + + memmove(pboot_devices + i, pboot_devices + i + 1, + sizeof(*pboot_devices) * (pboot_dev_count + i - 1)); + pboot_devices[--pboot_dev_count] = NULL; + + /* select the newly-focussed device */ + if (pboot_dev_sel > i) + newsel = pboot_dev_sel - 1; + else if (pboot_dev_sel == i && i >= pboot_dev_count) + newsel = pboot_dev_count - 1; + pboot_set_device_select(newsel, 1); + + /* todo: free device & options */ + + return TWIN_TRUE; +} + +static void pboot_make_background(void) +{ + twin_pixmap_t *filepic, *scaledpic; + const char *background_path; + + /* Set background pixmap */ + LOG("loading background..."); + background_path = artwork_pathname("background.jpg"); + filepic = twin_jpeg_to_pixmap(background_path, TWIN_ARGB32); + LOG("%s\n", filepic ? "ok" : "failed"); + + if (filepic == NULL) + return; + + if (pboot_screen->height == filepic->height && + pboot_screen->width == filepic->width) + scaledpic = filepic; + else { + twin_fixed_t sx, sy; + twin_operand_t srcop; + + scaledpic = twin_pixmap_create(TWIN_ARGB32, + pboot_screen->width, + pboot_screen->height); + if (scaledpic == NULL) { + twin_pixmap_destroy(filepic); + return; + } + sx = twin_fixed_div(twin_int_to_fixed(filepic->width), + twin_int_to_fixed(pboot_screen->width)); + sy = twin_fixed_div(twin_int_to_fixed(filepic->height), + twin_int_to_fixed(pboot_screen->height)); + + twin_matrix_scale(&filepic->transform, sx, sy); + srcop.source_kind = TWIN_PIXMAP; + srcop.u.pixmap = filepic; + twin_composite(scaledpic, 0, 0, &srcop, 0, 0, + NULL, 0, 0, TWIN_SOURCE, + pboot_screen->width, pboot_screen->height); + twin_pixmap_destroy(filepic); + + } + twin_screen_set_background(pboot_screen, scaledpic); +} + +#define PS3FB_IOCTL_SETMODE _IOW('r', 1, int) +#define PS3FB_IOCTL_GETMODE _IOR('r', 2, int) + +static void exitfunc(void) +{ +#ifndef _USE_X11 + if (pboot_fbdev) + twin_fbdev_destroy(pboot_fbdev); + pboot_fbdev = NULL; + if (pboot_vmode_change != -1) { + int fd = open("/dev/fb0", O_RDWR); + if (fd >= 0) + ioctl(fd, PS3FB_IOCTL_SETMODE, + (unsigned long)&pboot_vmode_change); + close(fd); + } +#endif +} + +static void sigint(int sig) +{ + exitfunc(); + syscall(__NR_exit); +} + +static void usage(const char *progname) +{ + fprintf(stderr, "Usage: %s [-u] [-h]\n", progname); +} + +int main(int argc, char **argv) +{ + int c; + int udev_trigger = 0; + + for (;;) { + c = getopt(argc, argv, "u::h"); + if (c == -1) + break; + + switch (c) { + case 'u': + udev_trigger = 1; + break; + case 'h': + usage(argv[0]); + return EXIT_SUCCESS; + default: + fprintf(stderr, "Unknown option '%c'\n", c); + usage(argv[0]); + return EXIT_FAILURE; + } + } + + atexit(exitfunc); + signal(SIGINT, sigint); + +#ifdef _USE_X11 + pboot_x11 = twin_x11_create(XOpenDisplay(0), 1024, 768); + if (pboot_x11 == NULL) { + perror("failed to create x11 screen !\n"); + return 1; + } + pboot_screen = pboot_x11->screen; +#else + /* Create screen and mouse drivers */ + pboot_fbdev = twin_fbdev_create(-1, SIGUSR1); + if (pboot_fbdev == NULL) { + perror("failed to create fbdev screen !\n"); + return 1; + } + pboot_screen = pboot_fbdev->screen; + twin_linux_mouse_create(NULL, pboot_screen); + twin_linux_js_create(pboot_screen); + + if (pboot_fbdev != NULL) { + char *cursor_path = artwork_pathname("cursor.gz"); + pboot_cursor = twin_load_X_cursor(cursor_path, 2, + &pboot_cursor_hx, + &pboot_cursor_hy); + if (pboot_cursor == NULL) + pboot_cursor = + twin_get_default_cursor(&pboot_cursor_hx, + &pboot_cursor_hy); + } +#endif + + /* Set background pixmap */ + pboot_make_background(); + + /* Init more stuffs */ + pboot_create_lpane(); + pboot_create_rpane(); + pboot_create_spane(); + + if (!pboot_start_device_discovery(udev_trigger)) { + LOG("Couldn't start device discovery!\n"); + return 1; + } + + pboot_set_lfocus(0); + twin_screen_set_active(pboot_screen, pboot_lpane->window->pixmap); + pboot_screen->event_filter = pboot_event_filter; + + /* Console switch */ +#ifndef _USE_X11 + if (pboot_fbdev) + twin_fbdev_activate(pboot_fbdev); +#endif + + /* Process events */ + twin_dispatch (); + + return 0; +}