Ok, here's something I hacked up this afternoon to try and get the ball
moving on this.
It's a simple grub style ncurses application. This is my ncurses app so
it's probably even easier than what I did. It parses a grub.conf file
and let's a user pick an entry.
Right now it just prints out what the user selected. I'm not sure how
it should integrate into the existing tools. Thoughts? Integrating it
shouldn't be pretty easy.
Note: It looks quite ugly in gnome-terminal. I'm sure there's a way to
handle the different term types better in curses. It looks just fine
though on a real terminal though.
Regards,
Anthony Liguori
Email: aliguori@xxxxxxxxxx
Signed-off-by: Anthony Liguori
diff -urN xenoloader-empty/Makefile xenoloader/Makefile
--- xenoloader-empty/Makefile 1969-12-31 18:00:00.000000000 -0600
+++ xenoloader/Makefile 2005-02-03 14:47:08.000000000 -0600
@@ -0,0 +1,31 @@
+###############################################################################
+##
+## Makefile
+##
+## Copyright (C) International Business Machines Corp., 2005
+## Author(s): Anthony Liguori (aliguori@xxxxxxxxxx)
+##
+## Xen Psuedo-Boot Loader
+##
+## This library is free software; you can redistribute it and/or modify
+## it under the terms of the GNU Lesser General Public License as published
+## by the Free Software Foundation; either version 2.1 of the License, or
+## (at your option) any later version.
+##
+## This library 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 Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with this library; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+###############################################################################
+
+CFLAGS=-Wall -g
+
+all: main.o
+ $(CC) $(CFLAGS) -o xenoloader main.o -lcurses
+
+clean:; $(RM) main.o xenoloader
diff -urN xenoloader-empty/main.c xenoloader/main.c
--- xenoloader-empty/main.c 1969-12-31 18:00:00.000000000 -0600
+++ xenoloader/main.c 2005-02-03 14:56:00.000000000 -0600
@@ -0,0 +1,278 @@
+/*\
+ * xenoloader/main.c
+ *
+ * Copyright (C) International Business Machines Corp., 2005
+ * Author(s): Anthony Liguori (aliguori@xxxxxxxxxx)
+ *
+ * Xen Psuedo-Boot Loader
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+\*/
+
+#include <curses.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <malloc.h>
+
+/* simple structure representing a single grub
+ command. argv[0] is the name of the command.
+ Use CMD_REST to concatinate multiple arguments
+ while preserving original spacing */
+struct grub_cmd {
+ int argc;
+ char **argv;
+ char *line;
+};
+
+/* the x,y offset of the first entry to select */
+#define X_OFFSET 3
+#define Y_OFFSET 6
+
+/* gets the concatination of the rest of the line
+ starting at argv[i] */
+#define CMD_REST(cmd, i) ((cmd).line + ((cmd).argv[(i)] - (cmd).argv[0]))
+
+/* reads a grub configuration file and returns a null-terminated array of
+ grub commands */
+struct grub_cmd *read_grub_conf(const char *file)
+{
+ FILE *f = fopen(file, "r");
+ char buffer[4096];
+ struct grub_cmd *cmds = 0;
+ size_t capacity = 0;
+ size_t size = 0;
+
+ if (!f) return NULL;
+
+ while(fgets(buffer, sizeof(buffer), f)) {
+ char *ptr = buffer;
+ int words = 0;
+ int in_word = 0;
+
+ /* skip spaces */
+ while (isspace(*ptr)) ptr++;
+
+ /* skip comments and blank lines */
+ if (*ptr == '#' || !*ptr) continue;
+
+ /* make sure we have enough room for this command and the null terminator
*/
+ if ((size + 2) > capacity) {
+ capacity += 100;
+ cmds = realloc(cmds, capacity * sizeof(cmds[0]));
+ }
+
+ cmds[size].line = strdup(ptr);
+
+ /* count number of words */
+ for (ptr = cmds[size].line; *ptr; ptr++) {
+ if (isspace(*ptr)) {
+ in_word = 0;
+ } else if (!in_word) {
+ words++;
+ in_word = 1;
+ }
+ if (*ptr == '\r' || *ptr == '\n') *ptr = 0;
+ }
+
+ /* allocate argc/argv */
+ cmds[size].argc = words;
+ cmds[size].argv = malloc(sizeof(char*) * words);
+
+ /* duplicate a string to tokenize */
+ cmds[size].argv[0] = strdup(cmds[size].line);
+ words = 1;
+ in_word = 1;
+
+ /* tokenize */
+ for (ptr = cmds[size].argv[0]; *ptr; ptr++) {
+ if (isspace(*ptr)) {
+ if (in_word) *ptr = 0;
+ in_word = 0;
+ } else if (!in_word) {
+ cmds[size].argv[words] = ptr;
+ words++;
+ in_word = 1;
+ }
+ }
+
+ size++;
+ }
+
+ fclose(f);
+
+ /* NULL terminate */
+ cmds[size].argc = 0;
+ cmds[size].argv = 0;
+ cmds[size].line = 0;
+
+ return cmds;
+}
+
+/* quick routine that draws a box using ASCII art. There's
+ probably a better way to do this with curses */
+static void make_box(int x, int y, int width, int height)
+{
+ int i;
+
+ mvaddch(y, x, 0xda);
+ for (i = x + 1; i < x + width; i++) {
+ mvaddch(y, i, 0xc4);
+ }
+ mvaddch(y, x+width, 0xbf);
+
+ for (i = y + 1; i < y + height; i++) {
+ mvaddch(i, x, 0xb3);
+ mvaddch(i, x+width, 0xb3);
+ }
+
+ mvaddch(y+height, x, 0xc0);
+ for (i = x + 1; i < x + width; i++) {
+ mvaddch(y+height, i, 0xc4);
+ }
+ mvaddch(y+height, x+width, 0xd9);
+}
+
+/* signal handler */
+static void finish(int sig)
+{
+ endwin();
+ exit(0);
+}
+
+int main(int argc, char **argv)
+{
+ int ch;
+ int pos = 0;
+ struct grub_cmd *cmds;
+ int selected = -1;
+ int i;
+ int n = 0;
+
+ if (argc != 2) {
+ printf("Usage: %s CONFIG\n", argv[0]);
+ exit(1);
+ }
+
+ /* Read the grub file */
+ cmds = read_grub_conf(argv[1]);
+ if (!cmds) {
+ fprintf(stderr, "Error reading `%s': %m\n", argv[1]);
+ exit(1);
+ }
+
+ /* Set up curses and signal handlers */
+ signal(SIGINT, finish);
+ initscr();
+ keypad(stdscr, TRUE);
+ intrflush(stdscr, FALSE);
+ nonl();
+ cbreak();
+ noecho();
+
+ /* Display banner */
+ mvaddstr(2, 2, "XenoLoader v0.0.1");
+
+ /* Display box with choices */
+ make_box(1, Y_OFFSET - 2, 74, 10);
+ for (i = 0; cmds[i].line; i++) {
+ if (!strcmp(cmds[i].argv[0], "title")) {
+ mvaddstr(Y_OFFSET + n, X_OFFSET + 2, CMD_REST(cmds[i], 1));
+ n++;
+ }
+ }
+
+ /* Display usage text */
+ mvaddstr(18, 2, "Use the up and down keys to select which entry is
highlighted.");
+ mvaddstr(19, 2, "Press enter to boot the selected OS.");
+
+ /* Select the first choice */
+ mvaddstr(Y_OFFSET + pos, X_OFFSET, " ");
+
+ /* Input loop */
+ while (selected == -1 && (ch = getch()) != EOF) {
+ int new_pos = pos;
+ switch (ch) {
+ case KEY_UP:
+ new_pos--;
+ break;
+ case KEY_DOWN:
+ new_pos++;
+ break;
+ case '\n':
+ case '\r':
+ selected = pos;
+ break;
+ }
+
+ new_pos += n;
+ new_pos %= n;
+
+ if (new_pos != pos) {
+ mvaddstr(Y_OFFSET + new_pos, X_OFFSET, " ");
+ pos = new_pos;
+ }
+ }
+
+ /* Close the curses session */
+ endwin();
+
+ /* This is all horribly inefficient but quick to write */
+
+ /* Find the index of the selected title */
+ n = 0;
+ for (i = 0; cmds[i].line; i++) {
+ if (!strcmp(cmds[i].argv[0], "title")) {
+ if (n == selected) {
+ break;
+ }
+ n++;
+ }
+ }
+
+ if (!cmds[i].line) {
+ printf("No kernel selected\n");
+ } else {
+ const char *title = CMD_REST(cmds[i], 1);
+ const char *kernel = "";
+ const char *cli = "";
+ const char *root = "";
+
+ /* Read the options after the selected title until
+ the next title looking for the ones we care about
+ */
+ for (i++; cmds[i].line; i++) {
+ if (!strcmp(cmds[i].argv[0], "title")) {
+ break;
+ } else if (!strcmp(cmds[i].argv[0], "kernel")) {
+ kernel = cmds[i].argv[1];
+ if (cmds[i].argc > 2) {
+ cli = CMD_REST(cmds[i], 2);
+ }
+ } else if (!strcmp(cmds[i].argv[0], "root")) {
+ root = CMD_REST(cmds[i], 1);
+ }
+ }
+
+ /* We've got our info.. now what do we do with it */
+ printf("Selected kernel `%s'\n", title);
+ printf("Root is `%s'\n", root);
+ printf("Kernel image is `%s'\n", kernel);
+ printf("Kernel command line is `%s'\n", cli);
+ }
+
+ return 0;
+}
|