5 #include <asm/setup.h> /* for COMMAND_LINE_SIZE */
10 #include <sys/utsname.h>
11 #include "fstype/fstype.h"
13 #ifndef COMMAND_LINE_SIZE
14 # define COMMAND_LINE_SIZE 256
17 /* filesystems built into the kernel */
18 static Eina_List
*fs_core
;
19 /* filesystems supported by kernel modules */
20 static Eina_List
*fs_mods
;
21 /* partitions read from /proc/partitions */
22 static Eina_List
*partitions
;
23 /* systems which were found to boot */
24 static Eina_List
*systems
;
26 static void skip_until(char** str
, char c
) {
27 while (**str
&& **str
!= c
)
31 static void skip_spaces(char **str
) {
32 while (isspace(**str
))
36 /* originated from kexecboot, it is used instead of system(3) because
37 * there is no shell involved which means it *could* be a bit faster
40 static int fexecw(const char *path
, char *const argv
[], char *const envp
[]) {
42 struct sigaction ignore
, old_int
, old_quit
;
43 sigset_t masked
, oldmask
;
46 /* Block SIGCHLD and ignore SIGINT and SIGQUIT before forking
47 * restore the original signal handlers afterwards. */
49 ignore
.sa_handler
= SIG_IGN
;
50 sigemptyset(&ignore
.sa_mask
);
52 sigaction(SIGINT
, &ignore
, &old_int
);
53 sigaction(SIGQUIT
, &ignore
, &old_quit
);
56 sigaddset(&masked
, SIGCHLD
);
57 sigprocmask(SIG_BLOCK
, &masked
, &oldmask
);
62 return -1; /* can't fork */
63 else if (pid
== 0) { /* child process */
64 sigaction(SIGINT
, &old_int
, NULL
);
65 sigaction(SIGQUIT
, &old_quit
, NULL
);
66 sigprocmask(SIG_SETMASK
, &oldmask
, NULL
);
67 execve(path
, (char *const *)argv
, (char *const *)envp
);
71 /* wait for our child and store it's exit status */
72 waitpid(pid
, &status
, 0);
74 /* restore signal handlers */
75 sigaction(SIGINT
, &old_int
, NULL
);
76 sigaction(SIGQUIT
, &old_quit
, NULL
);
77 sigprocmask(SIG_SETMASK
, &oldmask
, NULL
);
82 /* returns all partions known to the system with it's full paths */
83 static Eina_List
* get_partitions() {
84 FILE *f
= fopen("/proc/partitions", "r");
89 Eina_List
*partitions
= NULL
;
94 fgets(line
, sizeof(line
), f
);
95 fgets(line
, sizeof(line
), f
);
97 while (fgets(line
, sizeof(line
), f
)) {
98 char *dev
, *q
, *p
= line
;
101 /* skip first two columns */
106 /* read number of blocks */
107 blocks
= strtol(p
, &q
, 10);
108 if (!blocks
|| p
== q
)
110 /* ignore small partitions < 10MB */
111 if (blocks
< 10 * 1024)
113 /* read partition name and remove trailing \n */
116 skip_until(&q
, '\n');
120 dev
= malloc(sstrlen("/dev/") + len
+ 1);
121 strcpy(dev
, "/dev/");
122 strncat(dev
, p
, len
);
123 partitions
= eina_list_append(partitions
, dev
);
130 static Eina_List
* get_kernel_filesystems_builtin() {
131 FILE *f
= fopen("/proc/filesystems", "r");
136 Eina_List
*fs
= NULL
;
140 while (fgets(line
, sizeof(line
), f
)) {
141 /* overwrite new line */
142 line
[strlen(line
) - 1] = '\0';
143 /* skip tab or "nodev\t" */
144 fs
= eina_list_append(fs
, strdup(line
+ (line
[0] == '\t' ? 1 : sstrlen("nodev\t"))));
151 static Eina_List
* get_kernel_filesystems_modules() {
153 #if CONFIG_SUPPORT_KERNEL_FS_MODULES
155 Eina_List
*fs
= NULL
;
158 char buf
[1024], *p
, *q
;
164 len
= snprintf(buf
, sizeof(buf
), "/lib/modules/%s/modules.dep", uts
.release
);
173 /* calculate the length of the prefix for the modules, this assumes that the
174 * modules are installed under the directroy where we found the modules.dep file.
177 len
-= sstrlen("modules.dep");
179 /* Maybe we should readdir("/lib/modules/`uname -r`/kernel/fs") instead? */
181 while (fgets(buf
, sizeof(buf
), f
)) {
182 /* skip lines which don't contain a colon */
183 if (!(p
= strchr(buf
, ':')))
185 /* we are only interested in the part before '.ko:' */
186 *(p
- sstrlen(".ko")) = '\0';
189 /* skip the prefix '/lib/modules/`uname -r`/' */
192 if (strncmp(p
, "kernel/fs/", sstrlen("kernel/fs/")))
195 q
= p
+= sstrlen("kernel/fs/");
200 /* XXX: check for duplicates? */
201 fs
= eina_list_append(fs
, strdup(p
));
208 #endif /* CONFIG_SUPPORT_KERNEL_FS_MODULES */
211 static bool is_supported_filesystem(const char *fs
) {
213 if (eina_list_search_unsorted(fs_core
, EINA_COMPARE_CB(strcmp
), fs
))
216 #if CONFIG_SUPPORT_KERNEL_FS_MODULES
217 const char *modprobe
[] = { MODPROBE
, fs
};
218 if (eina_list_search_unsorted(fs_mods
, EINA_COMPARE_CB(strcmp
), fs
) &&
219 fexecw(modprobe
[0], (char *const *)modprobe
, NULL
) == 0) {
220 debug("Successfully loaded kernel filesystem module: '%s'\n", fs
);
221 fs_mods
= eina_list_remove(fs_mods
, fs
);
222 fs_core
= eina_list_append(fs_core
, fs
);
230 static BootItem
* scan_partition(const char *dev
) {
231 BootItem
*sys
= NULL
;
232 const char *mnt
, *fs
;
233 char buf
[COMMAND_LINE_SIZE
];
236 int fd
= open(dev
, O_RDONLY
);
242 if (identify_fs(fd
, &fs
, NULL
, 0)) {
243 eprint("Couldn't identify filesystem on '%s'\n", dev
);
247 if (!is_supported_filesystem(fs
)) {
248 eprint("Unsupported filesystem '%s' on '%s'\n", fs
, dev
);
252 /* we chdir()-ed and now use relative paths */
253 mnt
= dev
+ sstrlen("/dev/");
254 if (mkdir(mnt
, 0755) && errno
!= EEXIST
) {
259 if (mount(dev
, mnt
, fs
, MS_RDONLY
, NULL
)) {
264 snprintf(buf
, sizeof buf
, "%s/%s/boot/uImage-%s.bin", MOUNTPOINT
, mnt
, machine
);
265 if (stat(buf
, &st
)) {
266 /* no uImage present now check for zImage */
267 buf
[sstrlen(MOUNTPOINT
) + sstrlen("/") + strlen(mnt
) + sstrlen("/boot/")] = 'z';
268 if (stat(buf
, &st
)) {
269 eprint("No kernel found at '%s'\n", buf
);
275 sys
= calloc(sizeof(BootItem
), 1);
278 sys
->kernel
= strdup(buf
);
280 snprintf(buf
, sizeof buf
, "%s/%s/boot/append-%s", MOUNTPOINT
, mnt
, machine
);
281 FILE *f
= fopen(buf
, "r");
283 fgets(buf
, sizeof buf
, f
);
284 sys
->cmdline
= strdup(buf
);
288 snprintf(buf
, sizeof buf
, "%s/%s/boot/bootlogo.png", MOUNTPOINT
, mnt
);
290 sys
->logo
= strdup(buf
);
292 sys
->logo
= DEFAULT_LOGO
;
299 static Eina_List
* scan_system(Eina_List
*dev_ignore
) {
307 /* check if we need to read data from /proc first */
309 partitions
= get_partitions();
311 fs_core
= get_kernel_filesystems_builtin();
313 fs_mods
= get_kernel_filesystems_modules();
315 /* XXX: doesn't handle sub directories assumes /mnt */
316 mkdir(MOUNTPOINT
, 0755);
317 /* chdir so we can use relative paths for mount(2) */
318 if (chdir(MOUNTPOINT
)) {
323 EINA_LIST_FOREACH(partitions
, l
, dev
) {
325 if (eina_list_search_unsorted(dev_ignore
, EINA_COMPARE_CB(strcmp
), dev
))
327 BootItem
*sys
= scan_partition(dev
);
329 systems
= eina_list_append(systems
, sys
);
336 /* Genereate kernel command line for use with kexec. It consist of the following:
338 * - /proc/cmdline of the running system
339 * - root=$PARTITON rootfstype=$FS
340 * - append-$MACHINE in the same directory as the kernel
343 static char* get_kernel_cmdline(BootItem
*i
) {
344 static char cmdline
[2 * COMMAND_LINE_SIZE
] = KEXEC_CMDLINE
, *s
= cmdline
+ sstrlen(KEXEC_CMDLINE
);
346 /* read the cmdline of the currently running system only once */
347 if (!cmdline
[sstrlen(KEXEC_CMDLINE
) + 1]) {
348 FILE *f
= fopen("/proc/cmdline","r");
349 if (f
&& fgets(s
, sizeof(cmdline
) - sstrlen(KEXEC_CMDLINE
), f
)) {
350 /* /proc/cmdline ends with a new line which we want to overwrite thus -1 */
356 /* append root partition, filesystem and the image specific command line */
357 int len
= snprintf(s
, sizeof(cmdline
) - (s
- cmdline
), "rootwait root=%s rootfstype=%s %s",
358 i
->dev
, i
->fs
, i
->cmdline
? i
->cmdline
: "");
359 if (len
> 0 && s
[len
- 1] == '\n')
365 static bool boot_kernel(BootItem
*i
) {
369 /* umount all filesystem we probed except the one where the kernel is located */
370 EINA_LIST_FOREACH(systems
, l
, s
) {
372 /* XXX: assumes we are still chdir()-ed */
373 umount(s
->dev
+ sstrlen("/dev/"));
377 const char *kexec_load
[] = { KEXEC
, get_kernel_cmdline(i
), "-l", i
->kernel
, NULL
};
378 if (fexecw(kexec_load
[0], (char *const *)kexec_load
, NULL
)) {
379 gui_show_error("Couldn't load kernel from '%s'", i
->kernel
);
383 const char *kexec_exec
[] = { KEXEC
, "-e", NULL
};
385 /* XXX: assumes we are still chdir()-ed */
386 umount(i
->dev
+ sstrlen("/dev/"));
388 if (execve(kexec_exec
[0], (char *const *)kexec_exec
, NULL
)) {
389 gui_show_error("Couldn't exec kernel '%s'", i
->kernel
);
393 /* can't be reached just here to silence compiler warning */
397 static void diagnostics(Eina_List
*dev_ignore
) {
402 systems
= scan_system(dev_ignore
);
406 EINA_LIST_FOREACH(partitions
, l
, p
) {
410 puts("Built in filesystems:");
412 EINA_LIST_FOREACH(fs_core
, l
, p
) {
416 #if CONFIG_SUPPORT_KERNEL_FS_MODULES
417 puts("Filesystem modules:");
419 EINA_LIST_FOREACH(fs_mods
, l
, p
) {
424 puts("Bootable images:");
426 EINA_LIST_FOREACH(systems
, l
, s
) {
427 printf("mount -t %s %s /mnt/%s\n", s
->fs
, s
->dev
, s
->dev
+ sstrlen("/dev/"));
428 printf("kexec %s'%s' -l %s\n", KEXEC_CMDLINE
,
429 get_kernel_cmdline(s
) + sstrlen(KEXEC_CMDLINE
), s
->kernel
);
430 printf("umount %s\n", s
->dev
);
431 printf("kexec -e\n\n");
432 /* XXX: assumes we are still chdir()-ed */
433 umount(s
->dev
+ sstrlen("/dev/"));
437 static void umount_all() {
441 EINA_LIST_FOREACH(systems
, l
, s
) {
442 umount(s
->dev
+ sstrlen("/dev/"));