--- kexecboot-0.3.orig/kexecboot.h 2008-09-03 02:35:40.000000000 +0400 +++ kexecboot-0.3/kexecboot.h 2008-09-03 02:36:04.000000000 +0400 @@ -24,6 +24,10 @@ #include <string.h> #include <linux/input.h> #include <termios.h> +#include <unistd.h> +#include <signal.h> +#include <sys/wait.h> +#include <ctype.h> #include "fb.h" #include "devicescan.h" #include "res/logo-img.h" @@ -33,4 +37,14 @@ #include "res/memory-img.h" #include "res/radeon-font.h" +/* Macro for dealing with NULL strings */ +#define strlenn(s) ( (NULL != s) ? (strlen(s)) : 0 ) + +/* Tags we want from /proc/cmdline */ +char *wanted_tags[] = { + "mtdparts", + NULL +}; + + #endif --- kexecboot-0.3.orig/kexecboot.c 2008-09-03 02:35:40.000000000 +0400 +++ kexecboot-0.3/kexecboot.c 2008-09-03 03:33:05.000000000 +0400 @@ -78,20 +78,293 @@ fb_render(fb); } +/* + * Function: get_extra_cmdline() + * It gets wanted tags from original cmdline. + * Takes 2 args: + * - extra_cmdline - buffer to store cmdline parameters; + * - extra_cmdline_size - size of buffer + * (inc. terminating '\0'). + * Return values: + * - length of extra_cmdline on success (w/o term. zero); + * - -1 on error; + * - (- length of extra_cmdline - 1) on insufficient buffer space. + */ + +int get_extra_cmdline(char *const extra_cmdline, const size_t extra_cmdline_size) +{ + char *p, *t, *tag = NULL; + char line[COMMAND_LINE_SIZE]; + int i, len, sp_size; + int sum_len = 0; + int wanted_tag_found = 0; + int overflow = 0; + + const char proc_cmdline_path[] = "/proc/cmdline"; + + /* Open /proc/cmdline and read cmdline */ + FILE *f = fopen(proc_cmdline_path, "r"); + if (NULL == f) { + perror("Can't open /proc/cmdline"); + return -1; + } + + if ( NULL == fgets(line, sizeof(line), f) ) { + perror("Can't read /proc/cmdline"); + fclose(f); + return -1; + } + + fclose(f); + + /* clean up buffer before parsing */ + t = extra_cmdline; + *t = '\0'; + + p = line-1; /* because of ++p below */ + + sp_size = 0; /* size of (first) space */ + + do { + ++p; + + if ( (NULL == tag) && (isalnum(*p)) ) { + /* new tag found */ + tag = p; + } else if (tag) { + /* we are working on some tag */ + + if (isspace(*p) || ('\0' == *p) || ('=' == *p) ) { + /* end of tag or '=' found */ + + if (!wanted_tag_found) { + /* search in wanted_tags */ + for (i = 0; wanted_tags[i] != NULL; i++) { + if ( 0 == strncmp(wanted_tags[i], tag, p-tag) ) { + /* match found */ + wanted_tag_found = 1; + break; + } + } + } + + if ( ('=' != *p) && wanted_tag_found ) { + /* end of wanted tag found -> copy */ + + len = p - tag; + if ( (sum_len + len + sp_size) < extra_cmdline_size ) { + if (sp_size) { + /* prepend space when have tags already */ + *t = ' '; + ++t; + *t = '\0'; + ++sum_len; + } else { + sp_size = sizeof(char); + } + + /* NOTE: tag is not null-terminated so copy only + * len chars and terminate it directly + */ + strncpy(t, tag, len); + /* move pointer to position after copied tag */ + t += len ; + /* null-terminate */ + *t = '\0'; + /* update summary length */ + sum_len += len; + } else { + /* we have no space - skip this tag */ + overflow = 1; + } + + /* reset wanted_tag_found */ + wanted_tag_found = 0; + } + + /* reset tag */ + if (!wanted_tag_found) tag = NULL; + + } + } + + } while ('\0' != *p); + + if (overflow) return -sum_len-1; + return sum_len; +} + +/* + * Function: kexec_execw() + * (execve and wait) + * kexecboot's replace of system() call without /bin/sh invocation. + * During execution of the command, SIGCHLD will be blocked, + * and SIGINT and SIGQUIT will be ignored (like system() does). + * Takes 2 args (execve()-like): + * - path - full path to executable file + * - argv[] - array of args for executed file (command options e.g.) + * - envp[] - array of environment strings ("key=value") + * Return value: + * - command exit status on success + * - -1 on error (e.g. fork() failed) + */ +int kexec_execw(const char *path, char *const argv[], char *const envp[]) +{ + pid_t pid; + struct sigaction ignore, old_int, old_quit; + sigset_t masked, oldmask; + int status; + + /* Block SIGCHLD and ignore SIGINT and SIGQUIT */ + /* Do this before the fork() to avoid races */ + + ignore.sa_handler = SIG_IGN; + sigemptyset(&ignore.sa_mask); + ignore.sa_flags = 0; + sigaction(SIGINT, &ignore, &old_int); + sigaction(SIGQUIT, &ignore, &old_quit); + + sigemptyset(&masked); + sigaddset(&masked, SIGCHLD); + sigprocmask(SIG_BLOCK, &masked, &oldmask); + + pid = fork(); + + if (pid < 0) + /* can't fork */ + return -1; + else if (pid == 0) { + /* it is child */ + sigaction(SIGINT, &old_int, NULL); + sigaction(SIGQUIT, &old_quit, NULL); + sigprocmask(SIG_SETMASK, &oldmask, NULL); + + /* replace child with executed file */ + execve(path, (char *const *)argv, (char *const *)envp); + /* should not happens but... */ + _exit(127); + } + + /* it is parent */ + + /* wait for our child and store status */ + waitpid(pid, &status, 0); + + /* restore signal handlers */ + sigaction(SIGINT, &old_int, NULL); + sigaction(SIGQUIT, &old_quit, NULL); + sigprocmask(SIG_SETMASK, &oldmask, NULL); + + return status; +} + void start_kernel(struct boot *boot) { - //kexec --command-line="CMDLINE" -l /mnt/boot/zImage - char command[COMMAND_LINE_SIZE + 60]; - mount(boot->device, "/mnt", boot->fstype, MS_RDONLY, NULL); - if( boot->cmdline ) - sprintf(command,"/usr/sbin/kexec --command-line=\"%s root=%s rootfstype=%s\" -l %s", - boot->cmdline, boot->device, boot->fstype, boot->kernelpath); - else - sprintf(command,"kexec -l %s", boot->kernelpath); - system(command); -// puts(command); - umount("/mnt"); - system("/usr/sbin/kexec -e"); + /* we use var[] instead of *var because sizeof(var) using */ + const char mount_point[] = "/mnt"; + const char kexec_path[] = "/usr/sbin/kexec"; + + const char str_cmdline_start[] = "--command-line="; + const char str_root[] = " root="; + const char str_rootfstype[] = " rootfstype="; + const char str_rootwait[] = " rootwait"; + + /* empty environment */ + char *const envp[] = { NULL }; + + const char *kexec_load_argv[] = { NULL, "-l", NULL, NULL, NULL }; + const char *kexec_exec_argv[] = { NULL, "-e", NULL}; + + char extra_cmdline_buffer[COMMAND_LINE_SIZE]; + char *extra_cmdline, *cmdline_arg = NULL; + int n, idx; + + kexec_exec_argv[0] = kexec_path; + kexec_load_argv[0] = kexec_path; + + /* --command-line arg generation */ + idx = 2; /* kexec_load_argv current option index */ + + /* get some parts of cmdline from boot loader (e.g. mtdparts) */ + n = get_extra_cmdline( extra_cmdline_buffer, + sizeof(extra_cmdline_buffer) ); + if ( -1 == n ) { + /* clean up extra_cmdline on error */ + extra_cmdline = NULL; +/* } else if ( n < -1 ) { */ + /* Do something when we have no space to get all wanted tags */ + /* Now do nothing ;) */ + } else { + extra_cmdline = extra_cmdline_buffer; + } + + /* fill '--command-line' option */ + if (boot->cmdline || extra_cmdline || boot->device) { + /* allocate space */ + n = strlenn(str_cmdline_start) + strlenn(boot->cmdline) + + sizeof(char) + strlenn(extra_cmdline) + + strlenn(str_root) + strlenn(boot->device) + + strlenn(str_rootfstype) + strlenn(boot->fstype) + + strlenn(str_rootwait) + sizeof(char); + + cmdline_arg = (char *)malloc(n); + if (NULL == cmdline_arg) { + perror("Can't allocate memory for cmdline_arg"); + /* Should we exit? + exit(-1); + */ + } else { + strcat(cmdline_arg, str_cmdline_start); + if (extra_cmdline) + strcat(cmdline_arg, extra_cmdline); + if (boot->cmdline && extra_cmdline) + strcat(cmdline_arg, " "); + if (boot->cmdline) + strcat(cmdline_arg, boot->cmdline); + if (boot->device) { + strcat(cmdline_arg, str_root); + strcat(cmdline_arg, boot->device); + if (boot->fstype) { + strcat(cmdline_arg, str_rootfstype); + strcat(cmdline_arg, boot->fstype); + } + } + strcat(cmdline_arg, str_rootwait); + + kexec_load_argv[idx] = cmdline_arg; + ++idx; + } + } + + /* Append kernelpath as last arg of kexec */ + kexec_load_argv[idx] = boot->kernelpath; + + /* Debug + fprintf(stderr, "%s\n%s\n%s\n%s\n", kexec_load_argv[0], + kexec_load_argv[1], kexec_load_argv[2], + kexec_load_argv[3]); */ + + /* Mount boot device */ + if ( -1 == mount(boot->device, mount_point, boot->fstype, + MS_RDONLY, NULL) ) { + perror("Can't mount boot device"); + exit(-1); + } + + /* Load kernel */ + n = kexec_execw(kexec_path, (char *const *)kexec_load_argv, envp); + if (-1 == n) { + perror("Kexec can't load kernel"); + exit(-1); + } + + if (cmdline_arg) + free(cmdline_arg); + + umount(mount_point); + + /* Boot new kernel */ + execve(kexec_path, (char *const *)kexec_exec_argv, envp); } int main(int argc, char **argv) @@ -219,5 +492,6 @@ tcsetattr(fileno(stdin), TCSANOW, &old); fb_destroy(fb); start_kernel(bl->list[choice]); - return 0; + /* When we reach this point then some error was occured */ + return -1; }