34 files changed, 1687 insertions(+), 235 deletions(-) diff --git a/Makefile.in b/Makefile.in index 01771db..23135aa 100644 --- a/Makefile.in +++ b/Makefile.in @@ -21,7 +21,12 @@ ENABLE_PS3 = @ENABLE_PS3@ # other programs INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ SHELL = @SHELL@ +STRIP = @STRIP@ # paths prefix = @prefix@ @@ -34,5 +39,6 @@ localstatedir = @localstatedir@ builddir = @builddir@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ +mandir = @mandir@ include $(top_srcdir)/rules.mk diff --git a/configure.ac b/configure.ac index cb43f8c..fa40f34 100644 --- a/configure.ac +++ b/configure.ac @@ -35,9 +35,7 @@ AC_ARG_ENABLE([ps3], [], [enable_ps3=check]) -AS_IF([test "x$enable_ps3" != xno], - [AC_SUBST([ENABLE_PS3], ["y"])], - [AC_SUBST([ENABLE_PS3], ["n"])]) +AS_IF([test "x$enable_ps3" != xno], [AC_SUBST([ENABLE_PS3], ["y"])], []) AC_ARG_WITH([twin], [AS_HELP_STRING([--with-twin], diff --git a/discover/kboot-parser.c b/discover/kboot-parser.c index e688c22..7c7cb5d 100644 --- a/discover/kboot-parser.c +++ b/discover/kboot-parser.c @@ -133,10 +133,11 @@ static int kboot_parse(struct discover_context *dc) conf = talloc_zero(dc, struct conf_context); if (!conf) - return -1; + return 0; conf->dc = dc; conf->global_options = kboot_global_options, + conf_init_global_options(conf); conf->conf_files = kboot_conf_files, conf->process_pair = kboot_process_pair; conf->parser_info = (void *)kboot_ignored_names, diff --git a/discover/parser-conf.c b/discover/parser-conf.c index 14f847d..88e96b7 100644 --- a/discover/parser-conf.c +++ b/discover/parser-conf.c @@ -121,6 +121,18 @@ int conf_param_in_list(const char *const *list, const char *param) } /** + * conf_init_global_options - Zero the global option table. + */ + +void conf_init_global_options(struct conf_context *conf) +{ + int i; + + for (i = 0; conf->global_options[i].name; i++) + conf->global_options[i].value = NULL; +} + +/** * conf_set_global_option - Set a value in the global option table. * * Check if an option (name=value) is a global option. If so, store it in @@ -136,7 +148,7 @@ int conf_set_global_option(struct conf_context *conf, const char *name, if (streq(name, conf->global_options[i].name)) { conf->global_options[i].value = talloc_strdup(conf, value); - pb_log("%s: %s:%s\n", __func__, name, value); + pb_log("%s: @%s@%s@\n", __func__, name, value); return 1; } } @@ -158,8 +170,11 @@ const char *conf_get_global_option(struct conf_context *conf, int i; for (i = 0; conf->global_options[i].name ;i++) - if (streq(name, conf->global_options[i].name)) + if (streq(name, conf->global_options[i].name)) { + pb_log("%s: @%s@%s@\n", __func__, name, + conf->global_options[i].value); return conf->global_options[i].value; + } assert(0 && "unknown global name"); return NULL; diff --git a/discover/parser-conf.h b/discover/parser-conf.h index 09015d1..3325faf 100644 --- a/discover/parser-conf.h +++ b/discover/parser-conf.h @@ -41,6 +41,7 @@ struct conf_context { int conf_parse(struct conf_context *conf); char *conf_get_param_pair(char *str, char **name_out, char **value_out, char terminator); +void conf_init_global_options(struct conf_context *conf); const char *conf_get_global_option(struct conf_context *conf, const char *name); int conf_set_global_option(struct conf_context *conf, const char *name, diff --git a/discover/parser.c b/discover/parser.c index 2b4ddd2..8f2735c 100644 --- a/discover/parser.c +++ b/discover/parser.c @@ -13,16 +13,16 @@ extern struct parser __start_parsers[], __stop_parsers[]; void iterate_parsers(struct discover_context *ctx) { struct parser *parser; + unsigned int count = 0; pb_log("trying parsers for %s\n", ctx->device_path); for (parser = __start_parsers; parser < __stop_parsers; parser++) { pb_log("\ttrying parser '%s'\n", parser->name); - /* just use a dummy device path for now */ - if (parser->parse(ctx)) - return; + count += parser->parse(ctx); } - pb_log("\tno boot_options found\n"); + if (!count) + pb_log("\tno boot_options found\n"); } static int compare_parsers(const void *a, const void *b) diff --git a/discover/paths.c b/discover/paths.c index 8e2a361..fe7a876 100644 --- a/discover/paths.c +++ b/discover/paths.c @@ -81,14 +81,6 @@ char *parse_device_path(void *alloc_ctx, if (is_prefix(dev_str, "/dev/")) dev_str += strlen("/dev/"); - /* PS3 hack: if we're reading from a ps3dx device, and we refer to - * a sdx device, remap to ps3dx */ - if (cur_dev && is_prefix(cur_dev, "/dev/ps3d") - && is_prefix(dev_str, "sd")) { - snprintf(tmp, 255, "ps3d%s", dev_str + 2); - dev_str = tmp; - } - return join_paths(alloc_ctx, "/dev", dev_str); } diff --git a/discover/pb-discover.c b/discover/pb-discover.c index d7ea0ca..bd515e3 100644 --- a/discover/pb-discover.c +++ b/discover/pb-discover.c @@ -1,5 +1,10 @@ +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + #include +#include #include #include @@ -11,6 +16,79 @@ #include "discover-server.h" #include "device-handler.h" +static void print_version(void) +{ + printf("pb-discover (" PACKAGE_NAME ") " PACKAGE_VERSION "\n"); +} + +static void print_usage(void) +{ + print_version(); + printf( +"Usage: pb-discover [-h, --help] [-l, --log log-file] [-V, --version]\n"); +} + +/** + * enum opt_value - Tri-state options variables. + */ + +enum opt_value {opt_undef = 0, opt_yes, opt_no}; + +/** + * struct opts - Values from command line options. + */ + +struct opts { + enum opt_value show_help; + const char *log_file; + enum opt_value show_version; +}; + +/** + * opts_parse - Parse the command line options. + */ + +static int opts_parse(struct opts *opts, int argc, char *argv[]) +{ + static const struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"log", required_argument, NULL, 'l'}, + {"version", no_argument, NULL, 'V'}, + { NULL, 0, NULL, 0}, + }; + static const char short_options[] = "hl:V"; + static const struct opts default_values = { + .log_file = "pb-discover.log", + }; + + *opts = default_values; + + while (1) { + int c = getopt_long(argc, argv, short_options, long_options, + NULL); + + if (c == EOF) + break; + + switch (c) { + case 'h': + opts->show_help = opt_yes; + break; + case 'l': + opts->log_file = optarg; + break; + case 'V': + opts->show_version = opt_yes; + break; + default: + opts->show_help = opt_yes; + return -1; + } + } + + return optind != argc; +} + static int running; static void sigint_handler(int __attribute__((unused)) signum) @@ -18,15 +96,31 @@ static void sigint_handler(int __attribute__((unused)) signum) running = 0; } -int main(void) +int main(int argc, char *argv[]) { struct device_handler *handler; struct discover_server *server; + struct opts opts; struct udev *udev; struct user_event *uev; FILE *log; - log = fopen("pb-discover.log", "a"); + if (opts_parse(&opts, argc, argv)) { + print_usage(); + return EXIT_FAILURE; + } + + if (opts.show_help == opt_yes) { + print_usage(); + return EXIT_SUCCESS; + } + + if (opts.show_version == opt_yes) { + print_version(); + return EXIT_SUCCESS; + } + + log = fopen(opts.log_file, "a"); assert(log); pb_log_set_stream(log); diff --git a/discover/yaboot-parser.c b/discover/yaboot-parser.c index d9f2aff..6101cd8 100644 --- a/discover/yaboot-parser.c +++ b/discover/yaboot-parser.c @@ -15,7 +15,6 @@ struct yaboot_state { struct boot_option *opt; const char *desc_image; char *desc_initrd; - int found_suse; int globals_done; const char *const *known_names; }; @@ -56,6 +55,19 @@ static void yaboot_process_pair(struct conf_context *conf, const char *name, char *value) { struct yaboot_state *state = conf->parser_info; + struct fixed_pair { + const char *image; + const char *initrd; + }; + static const struct fixed_pair suse_fp32 = { + .image = "/suseboot/vmlinux32", + .initrd = "/suseboot/initrd32", + }; + static const struct fixed_pair suse_fp64 = { + .image = "/suseboot/vmlinux64", + .initrd = "/suseboot/initrd64", + }; + const struct fixed_pair *suse_fp; /* fixup for bare values */ @@ -73,32 +85,70 @@ static void yaboot_process_pair(struct conf_context *conf, const char *name, /* image */ if (streq(name, "image")) { + const char *g_boot = conf_get_global_option(conf, "boot"); + const char *g_part = conf_get_global_option(conf, "partition"); + + /* First finish any previous image. */ + if (state->opt->boot_image_file) yaboot_finish(conf); - state->opt->boot_image_file = resolve_path(state->opt, value, - conf->dc->device_path); - state->desc_image = talloc_strdup(state->opt, value); + /* Then start the new image. */ + + if (g_boot && g_part) { + char* dev = talloc_asprintf(NULL, "%s%s", g_boot, + g_part); + + state->opt->boot_image_file = resolve_path(state->opt, + value, dev); + state->desc_image = talloc_asprintf(state->opt, + "%s%s", dev, value); + talloc_free(dev); + } else if (g_boot) { + state->opt->boot_image_file = resolve_path(state->opt, + value, g_boot); + state->desc_image = talloc_asprintf(state->opt, + "%s%s", g_boot, value); + } else { + state->opt->boot_image_file = resolve_path(state->opt, + value, conf->dc->device_path); + state->desc_image = talloc_strdup(state->opt, value); + } + return; } - if (streq(name, "image[32bit]") || streq(name, "image[64bit]")) { - state->found_suse = 1; + /* Special processing for SUSE install CD. */ + + if (streq(name, "image[32bit]")) + suse_fp = &suse_fp32; + else if (streq(name, "image[64bit]")) + suse_fp = &suse_fp64; + else + suse_fp = NULL; + + if (suse_fp) { + /* First finish any previous image. */ if (state->opt->boot_image_file) yaboot_finish(conf); + /* Then start the new image. */ + if (*value == '/') { state->opt->boot_image_file = resolve_path(state->opt, value, conf->dc->device_path); state->desc_image = talloc_strdup(state->opt, value); } else { - char *s; - asprintf(&s, "/suseboot/%s", value); state->opt->boot_image_file = resolve_path(state->opt, - s, conf->dc->device_path); - state->desc_image = talloc_strdup(state->opt, s); - free(s); + suse_fp->image, conf->dc->device_path); + state->desc_image = talloc_strdup(state->opt, + suse_fp->image); + + state->opt->initrd_file = resolve_path(state->opt, + suse_fp->initrd, conf->dc->device_path); + state->desc_initrd = talloc_asprintf(state, "initrd=%s", + suse_fp->initrd); } return; @@ -112,19 +162,28 @@ static void yaboot_process_pair(struct conf_context *conf, const char *name, /* initrd */ if (streq(name, "initrd")) { - if (!state->found_suse || (*value == '/')) { + const char *g_boot = conf_get_global_option(conf, "boot"); + const char *g_part = conf_get_global_option(conf, "partition"); + + if (g_boot && g_part) { + char* dev = talloc_asprintf(NULL, "%s%s", g_boot, + g_part); + state->opt->initrd_file = resolve_path(state->opt, - value, conf->dc->device_path); - state->desc_initrd = talloc_asprintf(state, "initrd=%s", - value); + value, dev); + state->desc_initrd = talloc_asprintf(state, + "initrd=%s%s", dev, value); + talloc_free(dev); + } else if (g_boot) { + state->opt->initrd_file = resolve_path(state->opt, + value, g_boot); + state->desc_initrd = talloc_asprintf(state, + "initrd=%s%s", g_boot, value); } else { - char *s; - asprintf(&s, "/suseboot/%s", value); state->opt->initrd_file = resolve_path(state->opt, - s, conf->dc->device_path); + value, conf->dc->device_path); state->desc_initrd = talloc_asprintf(state, "initrd=%s", - s); - free(s); + value); } return; } @@ -236,10 +295,11 @@ static int yaboot_parse(struct discover_context *dc) conf = talloc_zero(dc, struct conf_context); if (!conf) - return -1; + return 0; conf->dc = dc; conf->global_options = yaboot_global_options, + conf_init_global_options(conf); conf->conf_files = yaboot_conf_files, conf->process_pair = yaboot_process_pair; conf->finish = yaboot_finish; diff --git a/lib/system/system.c b/lib/system/system.c index 380dded..7371445 100644 --- a/lib/system/system.c +++ b/lib/system/system.c @@ -3,6 +3,7 @@ #include "config.h" #endif +#include #include #include #include @@ -19,6 +20,7 @@ const struct pb_system_apps pb_system_apps = { .cp = "/bin/cp", .kexec = "/sbin/kexec", .mount = "/bin/mount", + .shutdown = "/sbin/shutdown", .sftp = "/usr/bin/sftp", .tftp = "/usr/bin/tftp", .umount = "/bin/umount", @@ -104,13 +106,13 @@ int pb_rmdir_recursive(const char *base, const char *dir) int pb_run_cmd(const char *const *cmd_argv) { - int status; - pid_t pid; #if defined(DEBUG) enum {do_debug = 1}; #else enum {do_debug = 0}; #endif + int status; + pid_t pid; if (do_debug) { const char *const *p = cmd_argv; @@ -125,12 +127,23 @@ int pb_run_cmd(const char *const *cmd_argv) pb_log("%s: %s\n", __func__, cmd_argv[0]); pid = fork(); + if (pid == -1) { pb_log("%s: fork failed: %s\n", __func__, strerror(errno)); return -1; } if (pid == 0) { + int log = fileno(pb_log_get_stream()); + + /* Redirect child output to log. */ + + status = dup2(log, STDOUT_FILENO); + assert(status != -1); + + status = dup2(log, STDERR_FILENO); + assert(status != -1); + execvp(cmd_argv[0], (char *const *)cmd_argv); pb_log("%s: exec failed: %s\n", __func__, strerror(errno)); exit(EXIT_FAILURE); diff --git a/lib/system/system.h b/lib/system/system.h index 47c7c02..1918309 100644 --- a/lib/system/system.h +++ b/lib/system/system.h @@ -5,6 +5,7 @@ struct pb_system_apps { const char *cp; const char *kexec; const char *mount; + const char *shutdown; const char *sftp; const char *tftp; const char *umount; diff --git a/man/pb-cui.8 b/man/pb-cui.8 new file mode 100644 index 0000000..e671c20 --- /dev/null +++ b/man/pb-cui.8 @@ -0,0 +1,67 @@ +.\" Copyright (C) 2009 Sony Computer Entertainment Inc. +.\" Copyright 2009 Sony Corp. +.\" +.\" 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; version 2 of the License. +.\" +.\" 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 +.\" +.\" Maintainer's Notes: +.\" * For syntax help see the man pages for 'mdoc' and 'mdoc.samples'. +.\" * To check syntax use this: +.\" 'groff -C -mtty-char -Tutf8 -man pb-cui.8'. +.\" * To check format use this: 'less pb-cui.8'. +.\" +.Dd "" +.Dt pb-cui 8 +.Os +.\" +.Sh NAME +.\" ==== +.Nm pb-cui +.Nd Petitboot ncurses bootloader UI +.\" +.Sh SYNOPSIS +.\" ======== +.Nm +.Op Fl h, -help +.Op Fl l, -log Ar log-file +.Op Fl V, -version +.\" +.Sh DESCRIPTION +.\" =========== +pb-cui is an ncurses based interface to the Petitboot bootloader. +.Pp +Petitboot is a Linux kexec based bootloader that supports loading Linux +kernel and initrd images from any device that can be mounted by Linux. +It can also load images from the network using the +HTTP, HTTPS, NFS, SFTP, and TFTP +protocols. +.\" +.Sh OPTIONS +.\" ======= +.Bl -tag -width indent +.\" +.It Fl h, -help +Print a help message. +.\" +.It Fl l, -log Ar log-file +Log messages to the file +.Ar log-file . +The default log is a file pb-cui.log in the directory where pb-cui +is started. New messages are appended to an existing log file. +.\" +.It Fl V, -version +Display the program version number. +.El +.Sh SEE ALSO +.\" ======== +.Xr petitboot 8 , Xr pb-discover 8 , Xr pb-event 8 diff --git a/man/pb-discover.8 b/man/pb-discover.8 new file mode 100644 index 0000000..9123e0f --- /dev/null +++ b/man/pb-discover.8 @@ -0,0 +1,44 @@ +.\" Copyright (C) 2009 Sony Computer Entertainment Inc. +.\" Copyright 2009 Sony Corp. +.\" +.\" 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; version 2 of the License. +.\" +.\" 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 +.\" +.\" Maintainer's Notes: +.\" * For syntax help see the man pages for 'mdoc' and 'mdoc.samples'. +.\" * To check syntax use this: +.\" 'groff -C -mtty-char -Tutf8 -man pb-discover.8'. +.\" * To check format use this: 'less pb-discover.8'. +.\" +.Dd "" +.Dt pb-discover 8 +.Os +.\" +.Sh NAME +.\" ==== +.Nm pb-discover +.Nd The dynamic device discovery daemon of the Petitboot bootloader +.\" +.Sh SYNOPSIS +.\" ======== +.Nm +.\" +.Sh DESCRIPTION +.\" =========== +pb-discover maintains a dynamic list of boot options available to +the system. On startup, the petitboot user interface clients connect to +pb-discover daemon and receive boot option information. +.\" +.Sh SEE ALSO +.\" ======== +.Xr petitboot 8 , Xr pb-cui 8 , Xr pb-event 8 diff --git a/man/pb-event.8 b/man/pb-event.8 new file mode 100644 index 0000000..dc123e1 --- /dev/null +++ b/man/pb-event.8 @@ -0,0 +1,43 @@ +.\" Copyright (C) 2009 Sony Computer Entertainment Inc. +.\" Copyright 2009 Sony Corp. +.\" +.\" 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; version 2 of the License. +.\" +.\" 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 +.\" +.\" Maintainer's Notes: +.\" * For syntax help see the man pages for 'mdoc' and 'mdoc.samples'. +.\" * To check syntax use this: +.\" 'groff -C -mtty-char -Tutf8 -man pb-event.8'. +.\" * To check format use this: 'less pb-event.8'. +.\" +.Dd "" +.Dt pb-event 8 +.Os +.\" +.Sh NAME +.\" ==== +.Nm pb-event +.Nd Event helper for the Petitboot bootloader +.\" +.Sh SYNOPSIS +.\" ======== +.Nm +.\" +.Sh DESCRIPTION +.\" =========== +The pb-event utility is used to send user mode events to pb-discover, the +petitboot device discovery daemon. +.\" +.Sh SEE ALSO +.\" ======== +.Xr petitboot 8 , Xr pb-cui 8 , Xr pb-discover 8 diff --git a/man/petitboot.8 b/man/petitboot.8 new file mode 100644 index 0000000..9dc2222 --- /dev/null +++ b/man/petitboot.8 @@ -0,0 +1,47 @@ +.\" Copyright (C) 2009 Sony Computer Entertainment Inc. +.\" Copyright 2009 Sony Corp. +.\" +.\" 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; version 2 of the License. +.\" +.\" 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 +.\" +.\" Maintainer's Notes: +.\" * For syntax help see the man pages for 'mdoc' and 'mdoc.samples'. +.\" * To check syntax use this: +.\" 'groff -C -mtty-char -Tutf8 -man petitboot.8'. +.\" * To check format use this: 'less petitboot.8'. +.\" +.Dd "" +.Dt petitboot 8 +.Os +.\" +.Sh NAME +.\" ==== +.Nm petitboot +.Nd The Petitboot bootloader +.\" +.Sh DESCRIPTION +.\" =========== +Petitboot is a platform independent bootloader based on Linux kexec. +Petitboot can load Linux kernel and initrd images from any device that +can be mounted by Linux, and can also load images from the network +using the +HTTP, HTTPS, NFS, SFTP, and TFTP +protocols. +.Pp +Petitboot looks for bootloader configuration files on mountable devices +in the system, and can also be configured to use boot information +from a DHCP server. +.\" +.Sh SEE ALSO +.\" ======== +.Xr pb-cui 8 , Xr pb-discover 8 , Xr pb-event 8 diff --git a/rules.mk b/rules.mk index e743338..3b1dcfe 100644 --- a/rules.mk +++ b/rules.mk @@ -19,8 +19,8 @@ parser_test = test/parser-test # install targets and components daemons = $(pb_discover) parsers = event kboot yaboot -uis = $(pb_cui) $(pb_test) -tests = $(parser_test) +uis = $(pb_cui) +tests = $(parser_test) $(pb_test) utils = $(pb_event) ifeq ($(PBTWIN),y) @@ -29,6 +29,7 @@ endif # other to install artwork = background.jpg cdrom.png hdd.png usbpen.png tux.png cursor.gz +man8 = pb-cui.8 pb-discover.8 pb-event.8 petitboot.8 rules = utils/99-petitboot.rules udhcpc = utils/udhcpc @@ -48,10 +49,11 @@ discover_objs = discover/event.o discover/user-event.o discover/udev.o \ discover/parser-utils.o # client objs -ui_common_objs = ui/common/discover-client.o ui/common/loader.o \ - ui/common/ui-system.o ui/common/url.o -ncurses_objs = ui/ncurses/nc-scr.o ui/ncurses/nc-menu.o \ - ui/ncurses/nc-ked.o ui/ncurses/nc-cui.o +ui_common_objs = ui/common/discover-client.o ui/common/joystick.o \ + ui/common/loader.o ui/common/ui-system.o ui/common/timer.o \ + ui/common/url.o +ncurses_objs = ui/ncurses/nc-scr.o ui/ncurses/nc-menu.o ui/ncurses/nc-ked.o \ + ui/ncurses/nc-cui.o twin_objs = ui/twin/pb-twin.o # Makefiles @@ -68,6 +70,7 @@ client_objs = $(lib_objs) $(ui_common_objs) all: $(uis) $(daemons) $(utils) # ncurses cui +pb_cui_objs-y$(ENABLE_PS3) += ui/ncurses/pb-cui.o pb_cui_objs-$(ENABLE_PS3) += ui/ncurses/ps3-cui.o ui/common/ps3.o pb_cui_ldflags-$(ENABLE_PS3) += -lps3-utils @@ -120,13 +123,16 @@ parser-test: $(parser_test) install: all $(rules) $(udhcpc) $(INSTALL) -d $(DESTDIR)$(sbindir)/ - $(INSTALL) $(daemons) $(uis) $(utils) $(DESTDIR)$(sbindir)/ + $(INSTALL_PROGRAM) $(daemons) $(uis) $(utils) $(DESTDIR)$(sbindir)/ $(INSTALL) -d $(DESTDIR)$(pkgdatadir)/artwork/ - $(INSTALL) $(addprefix $(top_srcdir)/ui/twin/artwork/,$(artwork)) \ + $(INSTALL_DATA) $(addprefix $(top_srcdir)/ui/twin/artwork/,$(artwork)) \ $(DESTDIR)$(pkgdatadir)/artwork/ $(INSTALL) -d $(DESTDIR)$(pkgdatadir)/utils - $(INSTALL) -m 644 $(top_srcdir)/$(rules) $(DESTDIR)$(pkgdatadir)/utils - $(INSTALL) -m 644 $(top_srcdir)/$(udhcpc) $(DESTDIR)$(pkgdatadir)/utils + $(INSTALL_DATA) $(top_srcdir)/$(rules) $(DESTDIR)$(pkgdatadir)/utils + $(INSTALL_DATA) $(top_srcdir)/$(udhcpc) $(DESTDIR)$(pkgdatadir)/utils + $(INSTALL) -d $(DESTDIR)$(mandir)/man8/ + $(INSTALL_DATA) $(addprefix $(top_srcdir)/man/, $(man8)) \ + $(DESTDIR)$(mandir)/man8/ dist: $(PACKAGE)-$(VERSION).tar.gz diff --git a/ui/common/joystick.c b/ui/common/joystick.c new file mode 100644 index 0000000..94c85fe --- /dev/null +++ b/ui/common/joystick.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2009 Sony Computer Entertainment Inc. + * Copyright 2009 Sony Corp. + * + * 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; version 2 of the License. + * + * 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 + */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "log/log.h" +#include "talloc/talloc.h" +#include "joystick.h" + +/** + * pjs_process_event - Read joystick event and map to UI key code. + * + * Returns a map routine UI key code or zero. + */ + +int pjs_process_event(const struct pjs *pjs) +{ + int result; + struct js_event e; + + assert(pjs->fd); + + result = read(pjs->fd, &e, sizeof(e)); + + if (result != sizeof(e)) { + pb_log("%s: read failed: %s\n", __func__, strerror(errno)); + return 0; + } + + return pjs->map(&e); +} + +/** + * pjs_destructor - The talloc destructor for a joystick handler. + */ + +static int pjs_destructor(void *arg) +{ + struct pjs *pjs = pjs_from_arg(arg); + + close(pjs->fd); + pjs->fd = 0; + + return 0; +} + +/** + * pjs_init - Initialize the joystick event handler. + */ + +struct pjs *pjs_init(void *ctx, int (*map)(const struct js_event *)) +{ + static const char dev_name[] = "/dev/input/js0"; + struct pjs *pjs; + + pjs = talloc_zero(ctx, struct pjs); + + if (!pjs) + return NULL; + + pjs->map = map; + pjs->fd = open(dev_name, O_RDONLY | O_NONBLOCK); + + if (pjs->fd < 0) { + pb_log("%s: open %s failed: %s\n", __func__, dev_name, + strerror(errno)); + goto out_err; + } + + talloc_set_destructor(pjs, pjs_destructor); + + pb_log("%s: using %s\n", __func__, dev_name); + + return pjs; + +out_err: + close(pjs->fd); + pjs->fd = 0; + talloc_free(pjs); + return NULL; +} diff --git a/ui/common/joystick.h b/ui/common/joystick.h new file mode 100644 index 0000000..cf3fc45 --- /dev/null +++ b/ui/common/joystick.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2009 Sony Computer Entertainment Inc. + * Copyright 2009 Sony Corp. + * + * 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; version 2 of the License. + * + * 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 + */ + +#if !defined(_PB_JOYSTICK_H) +#define _PB_JOYSTICK_H + +#include + +/** + * struct pjs - Petitboot joystick event handler. + * @map: Routine to map from a Linux struct js_event to a ui key code. + */ + +struct pjs { + int fd; + int (*map)(const struct js_event *e); +}; + +struct pjs *pjs_init(void *ctx, int (*map)(const struct js_event *)); +int pjs_process_event(const struct pjs *pjs); + +static inline struct pjs *pjs_from_arg(void *arg) +{ + return arg; +} + +static inline int pjs_get_fd(const struct pjs *pjs) +{ + return pjs->fd; +} + +#endif diff --git a/ui/common/loader.c b/ui/common/loader.c index babca28..5c69533 100644 --- a/ui/common/loader.c +++ b/ui/common/loader.c @@ -227,7 +227,7 @@ enum wget_flags { static char *pb_load_wget(void *ctx, struct pb_url *url, enum wget_flags flags) { int result; - const char *argv[6]; + const char *argv[7]; const char **p; char *local; @@ -238,12 +238,15 @@ static char *pb_load_wget(void *ctx, struct pb_url *url, enum wget_flags flags) p = argv; *p++ = pb_system_apps.wget; /* 1 */ - *p++ = "-O"; /* 2 */ - *p++ = local; /* 3 */ - *p++ = url->full; /* 4 */ +#if !defined(DEBUG) + *p++ = "--quiet"; /* 2 */ +#endif + *p++ = "-O"; /* 3 */ + *p++ = local; /* 4 */ + *p++ = url->full; /* 5 */ if (flags & wget_no_check_certificate) - *p++ = "--no-check-certificate"; /* 5 */ - *p++ = NULL; /* 6 */ + *p++ = "--no-check-certificate"; /* 6 */ + *p++ = NULL; /* 7 */ result = pb_run_cmd(argv); @@ -260,16 +263,22 @@ fail: /** * pb_load_file - Loads a remote file and returns the local file path. * @ctx: The talloc context to associate with the returned string. + * @remote: The remote file URL. + * @tempfile: An optional variable pointer to be set when a temporary local + * file is created. * * Returns the local file path in a talloc'ed character string on success, * or NULL on error. */ -char *pb_load_file(void *ctx, const char *remote) +char *pb_load_file(void *ctx, const char *remote, unsigned int *tempfile) { char *local; struct pb_url *url = pb_url_parse(NULL, remote); + if (tempfile) + *tempfile = 0; + if (!url) return NULL; @@ -277,19 +286,28 @@ char *pb_load_file(void *ctx, const char *remote) case pb_url_ftp: case pb_url_http: local = pb_load_wget(ctx, url, 0); + if (tempfile && local) + *tempfile = 1; break; case pb_url_https: - local = pb_load_wget(ctx, url, - wget_no_check_certificate); + local = pb_load_wget(ctx, url, wget_no_check_certificate); + if (tempfile && local) + *tempfile = 1; break; case pb_url_nfs: local = pb_load_nfs(ctx, url); + if (tempfile && local) + *tempfile = 1; break; case pb_url_sftp: local = pb_load_sftp(ctx, url); + if (tempfile && local) + *tempfile = 1; break; case pb_url_tftp: local = pb_load_tftp(ctx, url); + if (tempfile && local) + *tempfile = 1; break; default: local = talloc_strdup(ctx, url->full); diff --git a/ui/common/loader.h b/ui/common/loader.h index b06bb43..42d4d4b 100644 --- a/ui/common/loader.h +++ b/ui/common/loader.h @@ -19,6 +19,6 @@ #if !defined(_PB_FILE_LOADER_H) #define _PB_FILE_LOADER_H -char *pb_load_file(void *ctx, const char *remote); +char *pb_load_file(void *ctx, const char *remote, unsigned int *tempfile); #endif diff --git a/ui/common/ps3.c b/ui/common/ps3.c index 5c83289..cb1c8d1 100644 --- a/ui/common/ps3.c +++ b/ui/common/ps3.c @@ -49,6 +49,11 @@ static const struct os_area_db_id id_flags = .owner = OS_AREA_DB_OWNER_PETITBOOT, /* 3 */ .key = 3, }; +static const struct os_area_db_id id_timeout = +{ + .owner = OS_AREA_DB_OWNER_PETITBOOT, /* 3 */ + .key = 4, +}; struct ps3_flash_ctx { FILE *dev; @@ -59,6 +64,8 @@ struct ps3_flash_ctx { static void ps3_flash_close(struct ps3_flash_ctx *fc) { + assert(fc->dev); + fclose(fc->dev); fc->dev = NULL; } @@ -104,19 +111,19 @@ int ps3_flash_get_values(struct ps3_flash_values *values) struct ps3_flash_ctx fc; uint64_t tmp; - memset(values, 0, sizeof(*values)); - result = ps3_flash_open(&fc, "r"); if (result) - return -1; + goto done; result = os_area_db_read(&fc.db, &fc.header, fc.dev); + ps3_flash_close(&fc); + if (result) { pb_log("%s: os_area_db_read failed: %s\n", __func__, strerror(errno)); - goto fail; + goto done; } sum = result = os_area_db_get(&fc.db, &id_default_item, &tmp); @@ -124,21 +131,25 @@ int ps3_flash_get_values(struct ps3_flash_values *values) if (!result) values->default_item = (uint32_t)tmp; - sum += result = os_area_db_get(&fc.db, &id_video_mode, &tmp); + result = os_area_db_get(&fc.db, &id_timeout, &tmp); if (!result) - values->video_mode = (uint16_t)tmp; + values->timeout = (uint8_t)tmp; + sum += result = os_area_db_get(&fc.db, &id_video_mode, &tmp); - pb_log("%s: default_item: %u\n", __func__, values->default_item); - pb_log("%s: video_mode: %u\n", __func__, values->video_mode); + if (!result) + values->video_mode = (uint16_t)tmp; - ps3_flash_close(&fc); - return !!sum; +done: + pb_log("%s: default_item: %x\n", __func__, + (unsigned int)values->default_item); + pb_log("%s: timeout: %u\n", __func__, + (unsigned int)values->timeout); + pb_log("%s: video_mode: %u\n", __func__, + (unsigned int)values->video_mode); -fail: - ps3_flash_close(&fc); - return -1; + return (result || sum) ? -1 : 0; } /** @@ -177,6 +188,8 @@ int ps3_flash_set_values(const struct ps3_flash_values *values) } } + /* timeout is currently read-only, set with ps3-bl-option */ + result = os_area_db_set_32(&fc.db, &id_default_item, values->default_item); result += os_area_db_set_16(&fc.db, &id_video_mode, diff --git a/ui/common/ps3.h b/ui/common/ps3.h index 8a7fe1c..5ba8afe 100644 --- a/ui/common/ps3.h +++ b/ui/common/ps3.h @@ -33,9 +33,14 @@ enum ps3_flash_flags { ps3_flag_telnet = 1, }; +enum ps3_timeouts { + ps3_timeout_forever = 255, +}; + /** * struct ps3_flash_values - Values from PS3 flash memory. * @default_item: The default menu item. + * @timeout: The timeout in seconds. * @video_mode: The default video_mode. * @flags: Logical OR of enum ps3_flash_flags. */ @@ -44,6 +49,13 @@ struct ps3_flash_values { uint32_t default_item; uint16_t video_mode; /* uint16_t flags; */ + uint8_t timeout; +}; + +static const struct ps3_flash_values ps3_flash_defaults = { + .default_item = 0, + .video_mode = 1, + .timeout = ps3_timeout_forever, }; int ps3_flash_get_values(struct ps3_flash_values *values); diff --git a/ui/common/timer.c b/ui/common/timer.c new file mode 100644 index 0000000..954a18a --- /dev/null +++ b/ui/common/timer.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2009 Sony Computer Entertainment Inc. + * Copyright 2009 Sony Corp. + * + * 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; version 2 of the License. + * + * 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 + */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#define _GNU_SOURCE +#include +#include +#include + +#include "log/log.h" +#include "timer.h" + +/** + * ui_timer_init - Initialize the timer for use. + * @seconds: The final timeout value in seconds. + */ + +void ui_timer_init(struct ui_timer *timer, unsigned int seconds) +{ + pb_log("%s: %u\n", __func__, seconds); + assert(!timer->disabled); + timer->timeout = seconds; +} + +/** + * ui_timer_next - Calculate the next timer interval. + */ + +static unsigned int ui_timer_next(unsigned int seconds) +{ + unsigned int next; + + if (seconds == 0) { + next = 0; + goto done; + } + + if (seconds <= 10) { + next = 1; + goto done; + } + + if (seconds <= 60) { + next = seconds % 5; + next = next ? next : 5; + goto done; + } + + next = seconds % 10; + next = next ? next : 10; + +done: + pb_log("%s: %u = %u\n", __func__, seconds, next); + return next; +} + +/** + * ui_timer_kick - Kickstart the next timer interval. + */ + +void ui_timer_kick(struct ui_timer *timer) +{ + unsigned int next; + + if(timer->disabled) + return; + + if (timer->update_display) + timer->update_display(timer, timer->timeout); + + next = ui_timer_next(timer->timeout); + timer->timeout -= next; + + if (next) { + alarm(next); + return; + } + + pb_log("%s: timed out\n", __func__); + + ui_timer_disable(timer); + timer->handle_timeout(timer); +} + +/** + * ui_timer_disable - Stop and disable the timer. + */ + +void ui_timer_disable(struct ui_timer *timer) +{ + if (timer->disabled) + return; + + pb_log("%s\n", __func__); + timer->disabled = 1; + timer->timeout = UINT_MAX; + alarm(0); +} + +/** + * ui_timer_sigalrm + * + * Called at SIGALRM. + */ + +void ui_timer_sigalrm(struct ui_timer *timer) +{ + timer->signaled = 1; +} + +/** + * ui_timer_process_sig - Process a timer signal + */ + +void ui_timer_process_sig(struct ui_timer *timer) +{ + while (timer->signaled) { + timer->signaled = 0; + ui_timer_kick(timer); + } +} diff --git a/ui/common/timer.h b/ui/common/timer.h new file mode 100644 index 0000000..781b442 --- /dev/null +++ b/ui/common/timer.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2009 Sony Computer Entertainment Inc. + * Copyright 2009 Sony Corp. + * + * 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; version 2 of the License. + * + * 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 + */ + +#if !defined(_PB_UI_TIMER_H) +#define _PB_UI_TIMER_H + +#include + +/** + * struct ui_timer - UI timeout. + */ + +struct ui_timer { + unsigned int timeout; + unsigned int disabled; + sig_atomic_t signaled; + void (*update_display)(struct ui_timer *timer, unsigned int timeout); + void (*handle_timeout)(struct ui_timer *timer); +}; + +void ui_timer_init(struct ui_timer *timer, unsigned int seconds); +void ui_timer_kick(struct ui_timer *timer); +void ui_timer_disable(struct ui_timer *timer); +void ui_timer_sigalrm(struct ui_timer *timer); +void ui_timer_process_sig(struct ui_timer *timer); + +#endif diff --git a/ui/common/ui-system.c b/ui/common/ui-system.c index 3f54191..0140f0e 100644 --- a/ui/common/ui-system.c +++ b/ui/common/ui-system.c @@ -33,46 +33,80 @@ #include "ui-system.h" /** - * run_kexec_local - Final kexec helper. + * kexec_load - kexec load helper. * @l_image: The local image file for kexec to execute. * @l_initrd: Optional local initrd file for kexec --initrd, can be NULL. * @args: Optional command line args for kexec --append, can be NULL. */ -static int run_kexec_local(const char *l_image, const char *l_initrd, +static int kexec_load(const char *l_image, const char *l_initrd, const char *args) { int result; - const char *argv[8]; + const char *argv[6]; const char **p; + char *s_initrd = NULL; + char *s_args = NULL; p = argv; - *p++ = pb_system_apps.kexec; /* 1 */ + *p++ = pb_system_apps.kexec; /* 1 */ + *p++ = "-l"; /* 2 */ if (l_initrd) { - *p++ = "--initrd"; /* 2 */ - *p++ = l_initrd; /* 3 */ + s_initrd = talloc_asprintf(NULL, "--initrd=%s", l_initrd); + assert(s_initrd); + *p++ = s_initrd; /* 3 */ } if (args) { - *p++ = "--append"; /* 4 */ - *p++ = args; /* 5 */ + s_args = talloc_asprintf(NULL, "--append=%s", args); + assert(s_args); + *p++ = s_args; /* 4 */ } - /* First try by telling kexec to run shutdown */ + *p++ = l_image; /* 5 */ + *p++ = NULL; /* 6 */ - *(p + 0) = l_image; - *(p + 1) = NULL; + result = pb_run_cmd(argv); + + if (result) + pb_log("%s: failed: (%d)\n", __func__, result); + + talloc_free(s_initrd); + talloc_free(s_args); + + return result; +} + +/** + * kexec_reboot - Helper to boot the new kernel. + * + * Must only be called after a successful call to kexec_load(). + */ + +static int kexec_reboot(void) +{ + int result; + const char *argv[4]; + const char **p; + + /* First try running shutdown. Init scripts should run 'exec -e' */ + + p = argv; + *p++ = pb_system_apps.shutdown; /* 1 */ + *p++ = "-r"; /* 2 */ + *p++ = "now"; /* 3 */ + *p++ = NULL; /* 4 */ result = pb_run_cmd(argv); - /* kexec will return zero on success */ - /* On error, force a kexec with the -f option */ + /* On error, force a kexec with the -e option */ if (result) { - *(p + 0) = "-f"; /* 6 */ - *(p + 1) = l_image; /* 7 */ - *(p + 2) = NULL; /* 8 */ + p = argv; + *p++ = pb_system_apps.kexec; /* 1 */ + *p++ = "-e"; /* 2 */ + *p++ = NULL; /* 3 */ result = pb_run_cmd(argv); } @@ -85,38 +119,51 @@ static int run_kexec_local(const char *l_image, const char *l_initrd, /** * pb_run_kexec - Run kexec with the supplied boot options. - * - * For the convenience of the user, tries to load both files before - * returning error. */ int pb_run_kexec(const struct pb_kexec_data *kd) { int result; - char *l_image; - char *l_initrd; + char *l_image = NULL; + char *l_initrd = NULL; + unsigned int clean_image = 0; + unsigned int clean_initrd = 0; pb_log("%s: image: '%s'\n", __func__, kd->image); pb_log("%s: initrd: '%s'\n", __func__, kd->initrd); pb_log("%s: args: '%s'\n", __func__, kd->args); - if (kd->image) - l_image = pb_load_file(NULL, kd->image); - else { - l_image = NULL; - pb_log("%s: error null image\n", __func__); + result = -1; + + if (kd->image) { + l_image = pb_load_file(NULL, kd->image, &clean_image); + if (!l_image) + goto no_load; + } + + if (kd->initrd) { + l_initrd = pb_load_file(NULL, kd->initrd, &clean_initrd); + if (!l_initrd) + goto no_load; } - l_initrd = kd->initrd ? pb_load_file(NULL, kd->initrd) : NULL; + if (!l_image && !l_initrd) + goto no_load; + + result = kexec_load(l_image, l_initrd, kd->args); - if (!l_image || (kd->initrd && !l_initrd)) - result = -1; - else - result = run_kexec_local(l_image, l_initrd, kd->args); +no_load: + if (clean_image) + unlink(l_image); + if (clean_initrd) + unlink(l_initrd); talloc_free(l_image); talloc_free(l_initrd); + if (!result) + result = kexec_reboot(); + return result; } diff --git a/ui/ncurses/nc-cui.c b/ui/ncurses/nc-cui.c index 2a4c971..aed5ff7 100644 --- a/ui/ncurses/nc-cui.c +++ b/ui/ncurses/nc-cui.c @@ -30,7 +30,6 @@ #include "ui/common/discover-client.h" #include "nc-cui.h" - static struct cui_opt_data *cod_from_item(struct pmenu_item *item) { return item->data; @@ -61,6 +60,35 @@ void cui_resize(struct cui *cui) } /** + * cui_make_item_name - Format the menu item name srting. + * + * Returns a talloc string. + */ + +static char *cui_make_item_name(struct pmenu_item *i, struct cui_opt_data *cod) +{ + char *name; + + assert(cod->name); + assert(cod->kd); + + name = talloc_asprintf(i, "%s:", cod->name); + + if (cod->kd->image) + name = talloc_asprintf_append(name, " %s", cod->kd->image); + + if (cod->kd->initrd) + name = talloc_asprintf_append(name, " initrd=%s", + cod->kd->initrd); + + if (cod->kd->args) + name = talloc_asprintf_append(name, " %s", cod->kd->args); + + DBGS("@%s@\n", name); + return name; +} + +/** * cui_on_exit - A generic main menu ESC callback. */ @@ -109,8 +137,8 @@ static int cui_run_kexec(struct pmenu_item *item) assert(cui->current == &cui->main->scr); assert(cui->on_kexec); - pb_log("%s: %s\n", __func__, cod->dev->name, cod->opt->name); - nc_scr_status_printf(cui->current, "Booting %s...", cod->opt->name); + pb_log("%s: %s\n", __func__, cod->name); + nc_scr_status_printf(cui->current, "Booting %s...", cod->name); def_prog_mode(); @@ -141,9 +169,10 @@ static void cui_ked_on_exit(struct ked *ked, enum ked_result ked_result, { struct cui *cui = cui_from_arg(ked->scr.ui_ctx); - if (ked_result == ked_update || ked_result == ked_boot) { + if (ked_result == ked_update) { struct pmenu_item *i = pmenu_find_selected(cui->main); struct cui_opt_data *cod = cod_from_item(i); + char *name; assert(kd); @@ -151,7 +180,13 @@ static void cui_ked_on_exit(struct ked *ked, enum ked_result ked_result, talloc_free(cod->kd); cod->kd = kd; - pb_log("%s: updating opt '%s'\n", __func__, cod->opt->name); + name = cui_make_item_name(i, cod); + pmenu_item_replace(i, name); + + /* FIXME: need to make item visible somehow */ + set_current_item(cui->main->ncm, i->nci); + + pb_log("%s: updating opt '%s'\n", __func__, cod->name); pb_log(" image '%s'\n", cod->kd->image); pb_log(" initrd '%s'\n", cod->kd->initrd); pb_log(" args '%s'\n", cod->kd->args); @@ -160,20 +195,15 @@ static void cui_ked_on_exit(struct ked *ked, enum ked_result ked_result, cui_set_current(cui, &cui->main->scr); talloc_free(ked); - - if (ked_result == ked_boot) { - struct pmenu_item *i = pmenu_find_selected(cui->main); - i->on_execute(i); - } } int cui_ked_run(struct pmenu_item *item) { struct cui *cui = cui_from_item(item); + struct cui_opt_data *cod = cod_from_item(item); struct ked *ked; - ked = ked_init(cui, cod_from_item(item)->kd, cui_ked_on_exit); - + ked = ked_init(cui, cod->kd, cui_ked_on_exit); cui_set_current(cui, &ked->scr); return 0; @@ -200,14 +230,43 @@ struct nc_scr *cui_set_current(struct cui *cui, struct nc_scr *scr) return old; } +/** + * cui_process_key - Process input on stdin. + */ + static int cui_process_key(void *arg) { struct cui *cui = cui_from_arg(arg); assert(cui->current); + + ui_timer_disable(&cui->timer); cui->current->process_key(cui->current); + + return 0; +} + +/** + * cui_process_js - Process joystick events. + */ + +static int cui_process_js(void *arg) +{ + struct cui *cui = cui_from_arg(arg); + int c; + + c = pjs_process_event(cui->pjs); + + if (c) { + ungetch(c); + cui_process_key(arg); + } + return 0; } +/** + * cui_client_process_socket - Process a socket event from the discover server. + */ static int cui_client_process_socket(void *arg) { @@ -218,6 +277,24 @@ static int cui_client_process_socket(void *arg) } /** + * cui_handle_timeout - Handle the timeout. + */ + +static void cui_handle_timeout(struct ui_timer *timer) +{ + struct cui *cui = cui_from_timer(timer); + struct pmenu_item *i = pmenu_find_selected(cui->main); + +#if defined(DEBUG) + { + struct cui_opt_data *cod = cod_from_item(i); + assert(cod && (cod->opt_hash == cui->default_item)); + } +#endif + i->on_execute(i); +} + +/** * cui_handle_resize - Handle the term resize. */ @@ -243,6 +320,46 @@ static void cui_handle_resize(struct cui *cui) } /** + * cui_on_open - Open new item callback. + */ + +void cui_on_open(struct pmenu *menu) +{ + unsigned int insert_pt; + struct pmenu_item *i; + struct cui_opt_data *cod; + + menu->scr.unpost(&menu->scr); + + /* This disconnects items array from menu. */ + + set_menu_items(menu->ncm, NULL); + + /* Insert new items at insert_pt. */ + + insert_pt = pmenu_grow(menu, 1); + i = pmenu_item_alloc(menu); + + i->on_edit = cui_ked_run; + i->on_execute = cui_run_kexec; + i->data = cod = talloc_zero(i, struct cui_opt_data); + + cod->name = talloc_asprintf(i, "User item %u:", insert_pt); + cod->kd = talloc_zero(i, struct pb_kexec_data); + + pmenu_item_setup(menu, i, insert_pt, talloc_strdup(i, cod->name)); + + /* Re-attach the items array. */ + + set_menu_items(menu->ncm, menu->items); + + menu->scr.post(&menu->scr); + set_current_item(menu->ncm, i->nci); + + i->on_edit(i); +} + +/** * cui_device_add - Client device_add callback. * * Creates menu_items for all the device boot_options and inserts those @@ -284,23 +401,11 @@ static int cui_device_add(struct device *dev, void *arg) struct pmenu_item *i; struct cui_opt_data *cod; char *name; - char *description; /* Save the item in opt->ui_info for cui_device_remove() */ opt->ui_info = i = pmenu_item_alloc(cui->main); - name = talloc_asprintf(i, "%s: %s", opt->name, - opt->description); - - description = talloc_asprintf(i, - " kexec: image='%s' initrd='%s' args='%s'", - opt->boot_image_file, - opt->initrd_file, - opt->boot_args); - - pmenu_item_setup(cui->main, i, insert_pt, name, description); - i->on_edit = cui_ked_run; i->on_execute = cui_run_kexec; i->data = cod = talloc(i, struct cui_opt_data); @@ -308,23 +413,29 @@ static int cui_device_add(struct device *dev, void *arg) cod->dev = dev; cod->opt = opt; cod->opt_hash = pb_opt_hash(dev, opt); + cod->name = opt->name; cod->kd = talloc(i, struct pb_kexec_data); cod->kd->image = talloc_strdup(cod->kd, opt->boot_image_file); cod->kd->initrd = talloc_strdup(cod->kd, opt->initrd_file); cod->kd->args = talloc_strdup(cod->kd, opt->boot_args); - insert_pt++; - - /* If this is the default_item select it. */ + name = cui_make_item_name(i, cod); + pmenu_item_setup(cui->main, i, insert_pt, name); - if (cod->opt_hash == cui->default_item) - selected = i->nci; + insert_pt++; - pb_log("%s: adding opt '%s'\n", __func__, cod->opt->name); + pb_log("%s: adding opt '%s'\n", __func__, cod->name); pb_log(" image '%s'\n", cod->kd->image); pb_log(" initrd '%s'\n", cod->kd->initrd); pb_log(" args '%s'\n", cod->kd->args); + + /* If this is the default_item select it and start timer. */ + + if (cod->opt_hash == cui->default_item) { + selected = i->nci; + ui_timer_kick(&cui->timer); + } } /* Re-attach the items array. */ @@ -340,6 +451,9 @@ static int cui_device_add(struct device *dev, void *arg) item_count(cui->main->ncm) + 1); } + /* FIXME: need to make item visible somehow */ + menu_driver(cui->main->ncm, REQ_SCR_UPAGE); + menu_driver(cui->main->ncm, REQ_SCR_DPAGE); set_current_item(cui->main->ncm, selected); if (cui->current == &cui->main->scr) @@ -375,9 +489,15 @@ static void cui_device_remove(struct device *dev, void *arg) list_for_each_entry(&dev->boot_options, opt, list) { struct pmenu_item *i = pmenu_item_from_arg(opt->ui_info); + struct cui_opt_data *cod = cod_from_item(i); - assert(pb_protocol_device_cmp(dev, cod_from_item(i)->dev)); + assert(pb_protocol_device_cmp(dev, cod->dev)); pmenu_remove(cui->main, i); + + /* If this is the default_item disable timer. */ + + if (cod->opt_hash == cui->default_item) + ui_timer_disable(&cui->timer); } /* Re-attach the items array. */ @@ -397,10 +517,6 @@ static void cui_device_remove(struct device *dev, void *arg) cui->current->post(cui->current); } -/** - * cui_client_process_socket - Process a socket event from the discover server. - */ - static struct discover_client_ops cui_client_ops = { .device_add = cui_device_add, .device_remove = cui_device_remove, @@ -417,7 +533,8 @@ static struct discover_client_ops cui_client_ops = { */ struct cui *cui_init(void* platform_info, - int (*on_kexec)(struct cui *, struct cui_opt_data *)) + int (*on_kexec)(struct cui *, struct cui_opt_data *), + int (*js_map)(const struct js_event *e)) { struct cui *cui; struct discover_client *client; @@ -434,6 +551,7 @@ struct cui *cui_init(void* platform_info, cui->c_sig = pb_cui_sig; cui->platform_info = platform_info; cui->on_kexec = on_kexec; + cui->timer.handle_timeout = cui_handle_timeout; /* Loop here for scripts that just started the server. */ @@ -462,6 +580,15 @@ struct cui *cui_init(void* platform_info, waiter_register(STDIN_FILENO, WAIT_IN, cui_process_key, cui); + if (js_map) { + + cui->pjs = pjs_init(cui, js_map); + + if (cui->pjs) + waiter_register(pjs_get_fd(cui->pjs), WAIT_IN, + cui_process_js, cui); + } + return cui; fail_client_init: @@ -501,6 +628,8 @@ int cui_run(struct cui *cui, struct pmenu *main, unsigned int default_item) if (cui->abort) break; + ui_timer_process_sig(&cui->timer); + while (cui->resize) { cui->resize = 0; cui_handle_resize(cui); diff --git a/ui/ncurses/nc-cui.h b/ui/ncurses/nc-cui.h index 668776e..94fef6b 100644 --- a/ui/ncurses/nc-cui.h +++ b/ui/ncurses/nc-cui.h @@ -21,15 +21,19 @@ #include +#include "ui/common/joystick.h" +#include "ui/common/timer.h" #include "nc-menu.h" #include "nc-ked.h" - struct cui_opt_data { + const char *name; + struct pb_kexec_data *kd; + + /* optional data */ const struct device *dev; const struct boot_option *opt; uint32_t opt_hash; - struct pb_kexec_data *kd; }; /** @@ -49,13 +53,16 @@ struct cui { sig_atomic_t resize; struct nc_scr *current; struct pmenu *main; + struct ui_timer timer; + struct pjs *pjs; void *platform_info; unsigned int default_item; int (*on_kexec)(struct cui *cui, struct cui_opt_data *cod); }; struct cui *cui_init(void* platform_info, - int (*on_kexec)(struct cui *, struct cui_opt_data *)); + int (*on_kexec)(struct cui *, struct cui_opt_data *), + int (*js_map)(const struct js_event *e)); struct nc_scr *cui_set_current(struct cui *cui, struct nc_scr *scr); int cui_run(struct cui *cui, struct pmenu *main, unsigned int default_item); int cui_ked_run(struct pmenu_item *item); @@ -65,6 +72,7 @@ int cui_ked_run(struct pmenu_item *item); void cui_abort(struct cui *cui); void cui_resize(struct cui *cui); void cui_on_exit(struct pmenu *menu); +void cui_on_open(struct pmenu *menu); int cui_run_cmd(struct pmenu_item *item); static inline struct cui *cui_from_arg(void *arg) @@ -85,4 +93,15 @@ static inline struct cui *cui_from_item(struct pmenu_item *item) return cui_from_pmenu(item->pmenu); } +static inline struct cui *cui_from_timer(struct ui_timer *timer) +{ + struct cui *cui; + + cui = (struct cui *)((char *)timer + - (size_t)&((struct cui *)0)->timer); + assert(cui->c_sig == pb_cui_sig); + + return cui; +} + #endif diff --git a/ui/ncurses/nc-ked.c b/ui/ncurses/nc-ked.c index 3bdbd6c..806d389 100644 --- a/ui/ncurses/nc-ked.c +++ b/ui/ncurses/nc-ked.c @@ -48,12 +48,15 @@ static struct ked *ked_from_arg(void *arg) * @req: An ncurses request or char to send to form_driver(). */ -static void ked_move_cursor(struct ked *ked, int req) +static int ked_move_cursor(struct ked *ked, int req) { + int result; + wchgat(ked->scr.sub_ncw, 1, ked_attr_field_selected, 0, 0); - form_driver(ked->ncf, req); + result = form_driver(ked->ncf, req); wchgat(ked->scr.sub_ncw, 1, ked->attr_cursor, 0, 0); wrefresh(ked->scr.main_ncw); + return result; } /** @@ -93,12 +96,15 @@ static void ked_insert_mode_tog(struct ked *ked) * @req: An ncurses request to send to form_driver(). */ -static void ked_move_field(struct ked *ked, int req) +static int ked_move_field(struct ked *ked, int req) { + int result; + set_field_back(current_field(ked->ncf), ked_attr_field_normal); - form_driver(ked->ncf, req); + result = form_driver(ked->ncf, req); set_field_back(current_field(ked->ncf), ked_attr_field_selected); ked_move_cursor(ked, REQ_END_FIELD); + return result; } static int ked_post(struct nc_scr *scr) @@ -143,9 +149,10 @@ static char *ked_chomp(char *s) for (; s < s_end; s++) if (*s != ' ' && *s != '\t') break; - start = s; - for (++s; s < s_end; s++) + start = end = s; + + for (; s < s_end; s++) if (*s != ' ' && *s != '\t') end = s; *(end + 1) = 0; @@ -183,41 +190,33 @@ static struct pb_kexec_data *ked_prepare_data(struct ked *ked) static void ked_process_key(struct nc_scr *scr) { struct ked *ked = ked_from_scr(scr); + struct pb_kexec_data *kd; while (1) { int c = getch(); + if (c == ERR) + return; + + /* DBGS("%d (%o)\n", c, c); */ + switch (c) { default: ked_move_cursor(ked, c); - break; - case ERR: - return; + break; /* hot keys */ - case 2: { /* CTRL-B */ - struct pb_kexec_data *kd; - - form_driver(ked->ncf, REQ_VALIDATION); - kd = ked_prepare_data(ked); - ked->on_exit(ked, ked_boot, kd); - nc_flush_keys(); - return; - } case 27: /* ESC */ ked->on_exit(ked, ked_cancel, NULL); nc_flush_keys(); return; case '\n': - case '\r': { - struct pb_kexec_data *kd; - + case '\r': form_driver(ked->ncf, REQ_VALIDATION); kd = ked_prepare_data(ked); ked->on_exit(ked, ked_update, kd); nc_flush_keys(); return; - } /* insert mode */ case KEY_IC: @@ -252,8 +251,8 @@ static void ked_process_key(struct nc_scr *scr) ked_move_cursor(ked, REQ_RIGHT_CHAR); break; case KEY_BACKSPACE: - ked_move_cursor(ked, REQ_LEFT_CHAR); - ked_move_cursor(ked, REQ_DEL_CHAR); + if (ked_move_cursor(ked, REQ_LEFT_CHAR) == E_OK) + ked_move_cursor(ked, REQ_DEL_CHAR); break; case KEY_DC: ked_move_cursor(ked, REQ_DEL_CHAR); @@ -325,7 +324,7 @@ struct ked *ked_init(void *ui_ctx, const struct pb_kexec_data *kd, ked->scr.frame.title = talloc_strdup(ked, "Petitboot Option Editor"); ked->scr.frame.help = talloc_strdup(ked, - "ESC=cancel, Enter=accept, Ctrl-b=boot"); + "ESC=cancel, Enter=accept"); ked->on_exit = on_exit; diff --git a/ui/ncurses/nc-ked.h b/ui/ncurses/nc-ked.h index 36ed4f1..62fddd6 100644 --- a/ui/ncurses/nc-ked.h +++ b/ui/ncurses/nc-ked.h @@ -20,6 +20,7 @@ #define _PB_NC_KED_H #include +#include /* This must be included before ncurses.h */ #include #include "pb-protocol/pb-protocol.h" @@ -40,13 +41,11 @@ enum ked_attr_cursor { * enum ked_result - Result code for ked:on_exit(). * @ked_cancel: The user canceled the operation. * @ked_update: The args were updated. - * @ked_boot: The user requested a boot of this item. */ enum ked_result { ked_cancel, ked_update, - ked_boot, }; /** diff --git a/ui/ncurses/nc-menu.c b/ui/ncurses/nc-menu.c index 0a76b11..f96eb82 100644 --- a/ui/ncurses/nc-menu.c +++ b/ui/ncurses/nc-menu.c @@ -91,17 +91,17 @@ struct pmenu_item *pmenu_item_alloc(struct pmenu *menu) } struct pmenu_item *pmenu_item_setup(struct pmenu *menu, struct pmenu_item *i, - unsigned int index, const char *name, - const char *description) + unsigned int index, const char *name) { assert(i); + assert(name); if (!i) return NULL; i->i_sig = pb_item_sig; i->pmenu = menu; - i->nci = new_item(name, description); + i->nci = new_item(name, NULL); if (!i->nci) { talloc_free(i); @@ -115,6 +115,67 @@ struct pmenu_item *pmenu_item_setup(struct pmenu *menu, struct pmenu_item *i, return i; } +static int pmenu_item_get_index(const struct pmenu_item *item) +{ + unsigned int i; + + for (i = 0; i < item->pmenu->item_count; i++) + if (item->pmenu->items[i] == item->nci) + return i; + + pb_log("%s: not found: %p %s\n", __func__, item, + (item ? item->nci->name.str : "(null)")); + return -1; +} + +/** + * pmenu_item_replace - Replace the menu item with a new one. + * + * Use this routine to change a menu item's text. + */ + +int pmenu_item_replace(struct pmenu_item *i, const char *name) +{ + struct pmenu *menu; + ITEM *nci; + int index; + + assert(name); + assert(i->nci); + + menu = i->pmenu; + index = pmenu_item_get_index(i); + + if (index < 0) { + assert(0 && "get_index failed"); + return -1; + } + + nci = new_item(name, NULL); + + if (!nci) { + assert(0 && "new_item failed"); + return -1; + } + + set_item_userptr(nci, i); + + menu->scr.unpost(&menu->scr); + set_menu_items(menu->ncm, NULL); + + // FIXME: need to assure item name is a talloc string. + /* talloc_free((char *)item_name(i->nci)); */ + + free_item(i->nci); + menu->items[index] = nci; + i->nci = nci; + + set_menu_items(menu->ncm, menu->items); + menu->scr.post(&menu->scr); + + return 0; +} + /** * pmenu_move_cursor - Move the cursor. * @req: An ncurses request or char to send to menu_driver(). @@ -143,7 +204,7 @@ static void pmenu_process_key(struct nc_scr *scr) if (c == ERR) return; - /* DBGS("%d (%o)\n", c, c); */ + if (1) DBGS("%d (%o)\n", c, c); if (menu->hot_key) c = menu->hot_key(menu, item, c); @@ -174,13 +235,16 @@ static void pmenu_process_key(struct nc_scr *scr) case '\t': pmenu_move_cursor(menu, REQ_DOWN_ITEM); break; - case KEY_LEFT: - case 'E': case 'e': if (item->on_edit) item->on_edit(item); break; + case 'o': + DBGS("on_open: %p\n", menu->on_open); + if (menu->on_open) + menu->on_open(menu); + break; case '\n': case '\r': if (item->on_execute) @@ -228,19 +292,6 @@ unsigned int pmenu_grow(struct pmenu *menu, unsigned int count) return tmp; } -static int pmenu_item_get_index(const struct pmenu_item *item) -{ - unsigned int i; - - for (i = 0; i < item->pmenu->item_count; i++) - if (item->pmenu->items[i] == item->nci) - return i; - - pb_log("%s: not found: %p %s\n", __func__, item, - (item ? item->nci->name.str : "(null)")); - return -1; -} - /** * pmenu_remove - Remove an item from the item array. * @@ -262,6 +313,9 @@ int pmenu_remove(struct pmenu *menu, struct pmenu_item *item) if (index < 0) return -1; + free_item(item->nci); + talloc_free(item); + /* Note that items array has a null terminator. */ menu->insert_pt--; @@ -349,7 +403,7 @@ void pmenu_delete(struct pmenu *menu) menu->scr.sig = pb_removed_sig; for (i = item_count(menu->ncm); i; i--) - free_item(menu->items[i]); + free_item(menu->items[i - 1]); free_menu(menu->ncm); delwin(menu->scr.sub_ncw); diff --git a/ui/ncurses/nc-menu.h b/ui/ncurses/nc-menu.h index b487df9..4abec6f 100644 --- a/ui/ncurses/nc-menu.h +++ b/ui/ncurses/nc-menu.h @@ -20,6 +20,7 @@ #define _PB_NC_MENU_H #include +#include /* This must be included before ncurses.h */ #include #include "log/log.h" @@ -45,7 +46,8 @@ struct pmenu_item { struct pmenu_item *pmenu_item_alloc(struct pmenu *menu); struct pmenu_item *pmenu_item_setup(struct pmenu *menu, struct pmenu_item *i, - unsigned int index, const char *name, const char *description); + unsigned int index, const char *name); +int pmenu_item_replace(struct pmenu_item *i, const char *name); void pmenu_item_delete(struct pmenu_item *item); static inline struct pmenu_item *pmenu_item_from_arg(void *arg) @@ -57,10 +59,9 @@ static inline struct pmenu_item *pmenu_item_from_arg(void *arg) } static inline struct pmenu_item *pmenu_item_init(struct pmenu *menu, - unsigned int index, const char *name, const char *description) + unsigned int index, const char *name) { - return pmenu_item_setup(menu, pmenu_item_alloc(menu), index, name, - description); + return pmenu_item_setup(menu, pmenu_item_alloc(menu), index, name); } /** @@ -77,6 +78,7 @@ struct pmenu { unsigned int insert_pt; int (*hot_key)(struct pmenu *menu, struct pmenu_item *item, int c); void (*on_exit)(struct pmenu *menu); + void (*on_open)(struct pmenu *menu); }; struct pmenu *pmenu_init(void *ui_ctx, unsigned int item_count, diff --git a/ui/ncurses/nc-scr.h b/ui/ncurses/nc-scr.h index c08fcd4..2374c20 100644 --- a/ui/ncurses/nc-scr.h +++ b/ui/ncurses/nc-scr.h @@ -19,6 +19,7 @@ #if !defined(_PB_NC_SCR_H) #define _PB_NC_SCR_H +#include /* This must be included before ncurses.h */ #include #define DBG(fmt, args...) pb_log("DBG: " fmt, ## args) diff --git a/ui/ncurses/pb-cui.c b/ui/ncurses/pb-cui.c new file mode 100644 index 0000000..972490a --- /dev/null +++ b/ui/ncurses/pb-cui.c @@ -0,0 +1,289 @@ +/* + * Petitboot generic ncurses bootloader UI + * + * Copyright (C) 2009 Sony Computer Entertainment Inc. + * Copyright 2009 Sony Corp. + * + * 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; version 2 of the License. + * + * 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 + */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "log/log.h" +#include "talloc/talloc.h" +#include "waiter/waiter.h" +#include "ui/common/discover-client.h" +#include "nc-cui.h" + +static void print_version(void) +{ + printf("pb-cui (" PACKAGE_NAME ") " PACKAGE_VERSION "\n"); +} + +static void print_usage(void) +{ + print_version(); + printf( +"Usage: pb-cui [-h, --help] [-l, --log log-file] [-V, --version]\n"); +} + +/** + * enum opt_value - Tri-state options variables. + */ + +enum opt_value {opt_undef = 0, opt_yes, opt_no}; + +/** + * struct opts - Values from command line options. + */ + +struct opts { + enum opt_value show_help; + const char *log_file; + enum opt_value show_version; +}; + +/** + * opts_parse - Parse the command line options. + */ + +static int opts_parse(struct opts *opts, int argc, char *argv[]) +{ + static const struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"log", required_argument, NULL, 'l'}, + {"version", no_argument, NULL, 'V'}, + { NULL, 0, NULL, 0}, + }; + static const char short_options[] = "hl:V"; + static const struct opts default_values = { + .log_file = "pb-cui.log", + }; + + *opts = default_values; + + while (1) { + int c = getopt_long(argc, argv, short_options, long_options, + NULL); + + if (c == EOF) + break; + + switch (c) { + case 'h': + opts->show_help = opt_yes; + break; + case 'l': + opts->log_file = optarg; + break; + case 'V': + opts->show_version = opt_yes; + break; + default: + opts->show_help = opt_yes; + return -1; + } + } + + return 0; +} + +/** + * struct pb_cui - Main cui program instance. + * @mm: Main menu. + * @svm: Set video mode menu. + */ + +struct pb_cui { + struct pmenu *mm; + struct cui *cui; +}; + +static struct pb_cui *pb_from_cui(struct cui *cui) +{ + struct pb_cui *pb; + + assert(cui->c_sig == pb_cui_sig); + pb = cui->platform_info; + assert(pb->cui->c_sig == pb_cui_sig); + return pb; +} + +/** + * pb_kexec_cb - The kexec callback. + */ + +static int pb_kexec_cb(struct cui *cui, struct cui_opt_data *cod) +{ + struct pb_cui *pb = pb_from_cui(cui); + + pb_log("%s: %s\n", __func__, cod->name); + + assert(pb->cui->current == &pb->cui->main->scr); + + return pb_run_kexec(cod->kd); +} + +/** + * pb_mm_init - Setup the main menu instance. + */ + +static struct pmenu *pb_mm_init(struct pb_cui *pb_cui) +{ + int result; + struct pmenu *m; + struct pmenu_item *i; + + m = pmenu_init(pb_cui->cui, 1, cui_on_exit); + + if (!m) { + pb_log("%s: failed\n", __func__); + return NULL; + } + + m->on_open = cui_on_open; + + m->scr.frame.title = talloc_strdup(m, "Petitboot"); + m->scr.frame.help = talloc_strdup(m, + "ESC=exit, Enter=accept, e=edit, o=open"); + m->scr.frame.status = talloc_strdup(m, "Welcome to Petitboot"); + + i = pmenu_item_init(m, 0, "Exit to Shell"); + i->on_execute = pmenu_exit_cb; + + result = pmenu_setup(m); + + if (result) { + pb_log("%s:%d: pmenu_setup failed: %s\n", __func__, __LINE__, + strerror(errno)); + goto fail_setup; + } + + menu_opts_off(m->ncm, O_SHOWDESC); + set_menu_mark(m->ncm, " *"); + set_current_item(m->ncm, i->nci); + + return m; + +fail_setup: + talloc_free(m); + return NULL; +} + +static struct pb_cui pb; + +static void sig_handler(int signum) +{ + DBGS("%d\n", signum); + + switch (signum) { + case SIGALRM: + if (pb.cui) + ui_timer_sigalrm(&pb.cui->timer); + break; + case SIGWINCH: + if (pb.cui) + cui_resize(pb.cui); + break; + default: + assert(0 && "unknown sig"); + /* fall through */ + case SIGINT: + case SIGHUP: + case SIGTERM: + if (pb.cui) + cui_abort(pb.cui); + break; + } +} + +/** + * main - cui bootloader main routine. + */ + +int main(int argc, char *argv[]) +{ + static struct sigaction sa; + static struct opts opts; + int result; + int cui_result; + FILE *log; + + result = opts_parse(&opts, argc, argv); + + if (result) { + print_usage(); + return EXIT_FAILURE; + } + + if (opts.show_help == opt_yes) { + print_usage(); + return EXIT_SUCCESS; + } + + if (opts.show_version == opt_yes) { + print_version(); + return EXIT_SUCCESS; + } + + log = fopen(opts.log_file, "a"); + assert(log); + pb_log_set_stream(log); + +#if defined(DEBUG) + pb_log_always_flush(1); +#endif + + pb_log("--- pb-cui ---\n"); + + sa.sa_handler = sig_handler; + result = sigaction(SIGALRM, &sa, NULL); + result += sigaction(SIGHUP, &sa, NULL); + result += sigaction(SIGINT, &sa, NULL); + result += sigaction(SIGTERM, &sa, NULL); + result += sigaction(SIGWINCH, &sa, NULL); + + if (result) { + pb_log("%s sigaction failed.\n", __func__); + return EXIT_FAILURE; + } + + pb.cui = cui_init(&pb, pb_kexec_cb, NULL); + + if (!pb.cui) + return EXIT_FAILURE; + + pb.mm = pb_mm_init(&pb); + ui_timer_disable(&pb.cui->timer); + + cui_result = cui_run(pb.cui, pb.mm, 0); + + pmenu_delete(pb.mm); + + talloc_free(pb.cui); + + pb_log("--- end ---\n"); + + return cui_result ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/ui/ncurses/ps3-cui.c b/ui/ncurses/ps3-cui.c index f166c88..d9a66fa 100644 --- a/ui/ncurses/ps3-cui.c +++ b/ui/ncurses/ps3-cui.c @@ -21,9 +21,7 @@ /* * TODO * removable media event - * resize after video mode change * ncurses mouse support - * timeout */ #if defined(HAVE_CONFIG_H) @@ -36,6 +34,7 @@ #include #include #include +#include #include "log/log.h" #include "talloc/talloc.h" @@ -53,7 +52,8 @@ static void print_usage(void) { print_version(); printf( -"Usage: pb-cui [-h, --help] [-l, --log log-file] [-V, --version]\n"); +"Usage: pb-cui [-h, --help] [-l, --log log-file] [-r, --reset-defaults]\n" +" [-t, --timeout] [-V, --version]\n"); } /** @@ -69,6 +69,8 @@ enum opt_value {opt_undef = 0, opt_yes, opt_no}; struct opts { enum opt_value show_help; const char *log_file; + enum opt_value reset_defaults; + enum opt_value use_timeout; enum opt_value show_version; }; @@ -79,12 +81,14 @@ struct opts { static int opts_parse(struct opts *opts, int argc, char *argv[]) { static const struct option long_options[] = { - {"help", no_argument, NULL, 'h'}, - {"log", required_argument, NULL, 'l'}, - {"version", no_argument, NULL, 'V'}, - { NULL, 0, NULL, 0}, + {"help", no_argument, NULL, 'h'}, + {"log", required_argument, NULL, 'l'}, + {"reset-defaults", no_argument, NULL, 'r'}, + {"timeout", no_argument, NULL, 't'}, + {"version", no_argument, NULL, 'V'}, + { NULL, 0, NULL, 0}, }; - static const char short_options[] = "hl:V"; + static const char short_options[] = "hl:trV"; static const struct opts default_values = { .log_file = "pb-cui.log", }; @@ -105,6 +109,12 @@ static int opts_parse(struct opts *opts, int argc, char *argv[]) case 'l': opts->log_file = optarg; break; + case 't': + opts->use_timeout = opt_yes; + break; + case 'r': + opts->reset_defaults = opt_yes; + break; case 'V': opts->show_version = opt_yes; break; @@ -114,7 +124,7 @@ static int opts_parse(struct opts *opts, int argc, char *argv[]) } } - return 0; + return optind != argc; } /** @@ -147,6 +157,83 @@ static struct ps3_cui *ps3_from_item(struct pmenu_item *item) } /** + * ps3_sixaxis_map - Map a Linux joystick event to an ncurses key code. + * + */ + +static int ps3_sixaxis_map(const struct js_event *e) +{ +#if 0 + static const int axis_map[] = { + 0, /* 0 Left thumb X */ + 0, /* 1 Left thumb Y */ + 0, /* 2 Right thumb X */ + 0, /* 3 Right thumb Y */ + 0, /* 4 nothing */ + 0, /* 5 nothing */ + 0, /* 6 nothing */ + 0, /* 7 nothing */ + 0, /* 8 Dpad Up */ + 0, /* 9 Dpad Right */ + 0, /* 10 Dpad Down */ + 0, /* 11 Dpad Left */ + 0, /* 12 L2 */ + 0, /* 13 R2 */ + 0, /* 14 L1 */ + 0, /* 15 R1 */ + 0, /* 16 Triangle */ + 0, /* 17 Circle */ + 0, /* 18 Cross */ + 0, /* 19 Square */ + 0, /* 20 nothing */ + 0, /* 21 nothing */ + 0, /* 22 nothing */ + 0, /* 23 nothing */ + 0, /* 24 nothing */ + 0, /* 25 nothing */ + 0, /* 26 nothing */ + 0, /* 27 nothing */ + }; +#endif + static const int button_map[] = { + 0, /* 0 Select */ + 0, /* 1 L3 */ + 0, /* 2 R3 */ + 0, /* 3 Start */ + KEY_UP, /* 4 Dpad Up */ + 0, /* 5 Dpad Right */ + KEY_DOWN, /* 6 Dpad Down */ + 0, /* 7 Dpad Left */ + KEY_UP, /* 8 L2 */ + KEY_DOWN, /* 9 R2 */ + KEY_HOME, /* 10 L1 */ + KEY_END, /* 11 R1 */ + 0, /* 12 Triangle */ + 0, /* 13 Circle */ + 13, /* 14 Cross */ + 0, /* 15 Square */ + 0, /* 16 PS Button */ + 0, /* 17 nothing */ + 0, /* 18 nothing */ + }; + + if (!e->value) + return 0; + + if (e->type == JS_EVENT_BUTTON + && e->number < sizeof(button_map) / sizeof(button_map[0])) + return button_map[e->number]; + +#if 0 + if (e->type == JS_EVENT_AXIS + && e->number < sizeof(axis_map) / sizeof(axis_map[0])) + return axis_map[e->number]; +#endif + + return 0; +} + +/** * ps3_set_mode - Set video mode helper. * * Runs ps3_set_video_mode(). @@ -183,22 +270,51 @@ static int ps3_svm_cb(struct pmenu_item *item) * ps3_kexec_cb - The kexec callback. * * Writes config data to PS3 flash then calls pb_run_kexec(). + * Adds a video mode arg to the kernel command line if needed. */ static int ps3_kexec_cb(struct cui *cui, struct cui_opt_data *cod) { struct ps3_cui *ps3 = ps3_from_cui(cui); + int result; + int altered_args; + char *orig_args; - pb_log("%s: %s:%s\n", __func__, cod->dev->name, cod->opt->name); + pb_log("%s: %s\n", __func__, cod->name); assert(ps3->cui->current == &ps3->cui->main->scr); - if (cui->default_item != cod->opt_hash || ps3->dirty_values) { + /* Save values to flash if needed */ + + if ((cod->opt_hash && cod->opt_hash != cui->default_item) + || ps3->dirty_values) { ps3->values.default_item = cod->opt_hash; ps3_flash_set_values(&ps3->values); } - return pb_run_kexec(cod->kd); + /* Add a default kernel video mode. */ + + if (!cod->kd->args) { + altered_args = 1; + orig_args = NULL; + cod->kd->args = talloc_asprintf(NULL, "video=ps3fb:mode:%u", + (unsigned int)ps3->values.video_mode); + } else if (!strstr(cod->kd->args, "video=")) { + altered_args = 1; + orig_args = cod->kd->args; + cod->kd->args = talloc_asprintf(NULL, "%s video=ps3fb:mode:%u", + orig_args, (unsigned int)ps3->values.video_mode); + } else + altered_args = 0; + + result = pb_run_kexec(cod->kd); + + if (altered_args) { + talloc_free(cod->kd->args); + cod->kd->args = orig_args; + } + + return result; } /** @@ -283,6 +399,21 @@ static int ps3_hot_key(struct pmenu __attribute__((unused)) *menu, } /** + * ps3_timer_update - Timer callback. + */ + +static void ps3_timer_update(struct ui_timer *timer, unsigned int timeout) +{ + struct ps3_cui *ps3 = ps3_from_cui(cui_from_timer(timer)); + + //FIXME: make scr:timer. + // nc_scr_timer_update(&ps3.mm->scr, timeout); + + nc_scr_status_printf(&ps3->mm->scr, + "Welcome to Petitboot (timeout %u sec)", timeout); +} + +/** * ps3_mm_init - Setup the main menu instance. */ @@ -291,8 +422,7 @@ static struct pmenu *ps3_mm_init(struct ps3_cui *ps3_cui) int result; struct pmenu *m; struct pmenu_item *i; - static const char *const bgo[] = - {"/usr/sbin/ps3-boot-game-os-NOT", NULL}; + static const char *const bgo[] = {"/usr/sbin/ps3-boot-game-os", NULL}; m = pmenu_init(ps3_cui->cui, 3, cui_on_exit); @@ -302,22 +432,26 @@ static struct pmenu *ps3_mm_init(struct ps3_cui *ps3_cui) } m->hot_key = ps3_hot_key; + m->on_open = cui_on_open; + +#if defined(DEBUG) + m->scr.frame.title = talloc_strdup(m, + "Petitboot PS3 (" PACKAGE_VERSION ")"); +#else m->scr.frame.title = talloc_strdup(m, "Petitboot PS3"); +#endif m->scr.frame.help = talloc_strdup(m, - "ESC=exit, Enter=accept, E,e=edit"); + "ESC=exit, Enter=accept, e=edit, o=open"); m->scr.frame.status = talloc_strdup(m, "Welcome to Petitboot"); - i = pmenu_item_init(m, 0, "Boot GameOS", - "Reboot the PS3 into the GameOS"); + i = pmenu_item_init(m, 0, "Boot GameOS"); i->on_execute = cui_run_cmd; i->data = (void *)bgo; - i = pmenu_item_init(m, 1, "Set Video Mode", - "Display a video mode selection menu"); + i = pmenu_item_init(m, 1, "Set Video Mode"); i->on_execute = ps3_mm_to_svm_cb; - i = pmenu_item_init(m, 2, "Exit to Shell", - "Exit petitboot and return to a shell prompt"); + i = pmenu_item_init(m, 2, "Exit to Shell"); i->on_execute = pmenu_exit_cb; result = pmenu_setup(m); @@ -360,53 +494,51 @@ static struct pmenu *ps3_svm_init(struct ps3_cui *ps3_cui) m->scr.frame.title = talloc_strdup(m, "Select PS3 Video Mode"); m->scr.frame.help = talloc_strdup(m, "ESC=exit, Enter=accept"); - i = pmenu_item_init(m, 0, "auto detect", - "Auto detect the best HDMI video mode"); + i = pmenu_item_init(m, 0, "auto detect"); i->on_execute = ps3_svm_cb; i->data = (void *)0; - i = pmenu_item_init(m, 1, "480i (576 x 384)", NULL); + i = pmenu_item_init(m, 1, "480i (576 x 384)"); i->on_execute = ps3_svm_cb; i->data = (void *)1; - i = pmenu_item_init(m, 2, "480p (576 x 384)", NULL); + i = pmenu_item_init(m, 2, "480p (576 x 384)"); i->on_execute = ps3_svm_cb; i->data = (void *)2; - i = pmenu_item_init(m, 3, "576i (576 x 460)", NULL); + i = pmenu_item_init(m, 3, "576i (576 x 460)"); i->on_execute = ps3_svm_cb; i->data = (void *)6; - i = pmenu_item_init(m, 4, "576p (576 x 460)", NULL); + i = pmenu_item_init(m, 4, "576p (576 x 460)"); i->on_execute = ps3_svm_cb; i->data = (void *)7; - i = pmenu_item_init(m, 5, "720p (1124 x 644)", NULL); + i = pmenu_item_init(m, 5, "720p (1124 x 644)"); i->on_execute = ps3_svm_cb; i->data = (void *)3; - i = pmenu_item_init(m, 6, "1080i (1688 x 964)", NULL); + i = pmenu_item_init(m, 6, "1080i (1688 x 964)"); i->on_execute = ps3_svm_cb; i->data = (void *)4; - i = pmenu_item_init(m, 7, "1080p (1688 x 964)", NULL); + i = pmenu_item_init(m, 7, "1080p (1688 x 964)"); i->on_execute = ps3_svm_cb; i->data = (void *)5; - i = pmenu_item_init(m, 8, "wxga (1280 x 768)", NULL); + i = pmenu_item_init(m, 8, "wxga (1280 x 768)"); i->on_execute = ps3_svm_cb; i->data = (void *)11; - i = pmenu_item_init(m, 9, "sxga (1280 x 1024)", NULL); + i = pmenu_item_init(m, 9, "sxga (1280 x 1024)"); i->on_execute = ps3_svm_cb; i->data = (void *)12; - i = pmenu_item_init(m, 10, "wuxga (1920 x 1200)", NULL); + i = pmenu_item_init(m, 10, "wuxga (1920 x 1200)"); i->on_execute = ps3_svm_cb; i->data = (void *)13; - i = pmenu_item_init(m, 11, "Return", - "Return to the main menu"); + i = pmenu_item_init(m, 11, "Return"); i->on_execute = ps3_svm_to_mm_cb; result = pmenu_setup(m); @@ -434,6 +566,10 @@ static void sig_handler(int signum) DBGS("%d\n", signum); switch (signum) { + case SIGALRM: + if (ps3.cui) + ui_timer_sigalrm(&ps3.cui->timer); + break; case SIGWINCH: if (ps3.cui) cui_resize(ps3.cui); @@ -491,8 +627,9 @@ int main(int argc, char *argv[]) pb_log("--- pb-cui ---\n"); sa.sa_handler = sig_handler; - result = sigaction(SIGINT, &sa, NULL); + result = sigaction(SIGALRM, &sa, NULL); result += sigaction(SIGHUP, &sa, NULL); + result += sigaction(SIGINT, &sa, NULL); result += sigaction(SIGTERM, &sa, NULL); result += sigaction(SIGWINCH, &sa, NULL); @@ -501,7 +638,10 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - ps3.dirty_values = ps3_flash_get_values(&ps3.values); + ps3.values = ps3_flash_defaults; + + if (opts.reset_defaults != opt_yes) + ps3.dirty_values = ps3_flash_get_values(&ps3.values); result = ps3_get_video_mode(&mode); @@ -515,7 +655,7 @@ int main(int argc, char *argv[]) if (!result && (ps3.values.video_mode != (uint16_t)mode)) ps3_set_video_mode(ps3.values.video_mode); - ps3.cui = cui_init(&ps3, ps3_kexec_cb); + ps3.cui = cui_init(&ps3, ps3_kexec_cb, ps3_sixaxis_map); if (!ps3.cui) return EXIT_FAILURE; @@ -523,6 +663,14 @@ int main(int argc, char *argv[]) ps3.mm = ps3_mm_init(&ps3); ps3.svm = ps3_svm_init(&ps3); + if (opts.use_timeout != opt_yes + || ps3.values.timeout == ps3_timeout_forever) + ui_timer_disable(&ps3.cui->timer); + else { + ps3.cui->timer.update_display = ps3_timer_update; + ui_timer_init(&ps3.cui->timer, ps3.values.timeout); + } + cui_result = cui_run(ps3.cui, ps3.mm, ps3.values.default_item); pmenu_delete(ps3.mm);