Magellan Linux

Annotation of /tags/grubby-8_32/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2688 - (hide annotations) (download)
Wed Jul 16 10:42:34 2014 UTC (9 years, 9 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 128764 byte(s)
Always choose linux/initrd on efi-only platforms.
1 niro 914 /*
2     * grubby.c
3     *
4     * Copyright (C) 2001-2008 Red Hat, Inc.
5     * All rights reserved.
6     *
7     * This program is free software; you can redistribute it and/or modify
8     * it under the terms of the GNU General Public License as published by
9     * the Free Software Foundation; either version 2 of the License, or
10     * (at your option) any later version.
11     *
12     * This program is distributed in the hope that it will be useful,
13     * but WITHOUT ANY WARRANTY; without even the implied warranty of
14     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15     * GNU General Public License for more details.
16     *
17     * You should have received a copy of the GNU General Public License
18     * along with this program. If not, see <http://www.gnu.org/licenses/>.
19     */
20 niro 532
21 niro 914 #ifndef _GNU_SOURCE
22     #define _GNU_SOURCE
23     #endif
24 niro 532 #include <ctype.h>
25     #include <errno.h>
26     #include <fcntl.h>
27     #include <mntent.h>
28     #include <popt.h>
29     #include <stdarg.h>
30     #include <stdlib.h>
31     #include <string.h>
32     #include <sys/stat.h>
33     #include <unistd.h>
34 niro 914 #include <libgen.h>
35     #include <execinfo.h>
36     #include <signal.h>
37     #include <blkid/blkid.h>
38 niro 532
39 niro 2236 #include "log.h"
40    
41 niro 1694 #ifndef DEBUG
42 niro 914 #define DEBUG 0
43 niro 1694 #endif
44 niro 532
45 niro 914 #if DEBUG
46     #define dbgPrintf(format, args...) fprintf(stderr, format , ## args)
47     #else
48     #define dbgPrintf(format, args...)
49     #endif
50    
51 niro 1736 int debug = 0; /* Currently just for template debugging */
52    
53 niro 532 #define _(A) (A)
54    
55 niro 914 #define MAX_EXTRA_INITRDS 16 /* code segment checked by --bootloader-probe */
56 niro 532 #define CODE_SEG_SIZE 128 /* code segment checked by --bootloader-probe */
57    
58 niro 1718 #define NOOP_OPCODE 0x90
59     #define JMP_SHORT_OPCODE 0xeb
60    
61 niro 1940 int isEfi = 0;
62    
63 niro 2688 #if defined(__arch64__)
64     #define isEfiOnly 1
65     #else
66     #define isEfiOnly 0
67     #endif
68    
69 niro 2236 char *saved_command_line = NULL;
70    
71 niro 532 /* comments get lumped in with indention */
72     struct lineElement {
73     char * item;
74     char * indent;
75     };
76    
77 niro 914 enum lineType_e {
78 niro 1696 LT_WHITESPACE = 1 << 0,
79     LT_TITLE = 1 << 1,
80     LT_KERNEL = 1 << 2,
81     LT_INITRD = 1 << 3,
82     LT_HYPER = 1 << 4,
83     LT_DEFAULT = 1 << 5,
84     LT_MBMODULE = 1 << 6,
85     LT_ROOT = 1 << 7,
86     LT_FALLBACK = 1 << 8,
87     LT_KERNELARGS = 1 << 9,
88     LT_BOOT = 1 << 10,
89     LT_BOOTROOT = 1 << 11,
90     LT_LBA = 1 << 12,
91     LT_OTHER = 1 << 13,
92     LT_GENERIC = 1 << 14,
93     LT_ECHO = 1 << 16,
94     LT_MENUENTRY = 1 << 17,
95     LT_ENTRY_END = 1 << 18,
96     LT_SET_VARIABLE = 1 << 19,
97 niro 1940 LT_KERNEL_EFI = 1 << 20,
98     LT_INITRD_EFI = 1 << 21,
99 niro 2683 LT_KERNEL_16 = 1 << 22,
100     LT_INITRD_16 = 1 << 23,
101 niro 2685 LT_DEVTREE = 1 << 24,
102     LT_UNKNOWN = 1 << 25,
103 niro 914 };
104 niro 532
105     struct singleLine {
106     char * indent;
107     int numElements;
108     struct lineElement * elements;
109     struct singleLine * next;
110     enum lineType_e type;
111     };
112    
113     struct singleEntry {
114     struct singleLine * lines;
115     int skip;
116     int multiboot;
117     struct singleEntry * next;
118     };
119    
120     #define GRUBBY_BADIMAGE_OKAY (1 << 0)
121    
122     #define GRUB_CONFIG_NO_DEFAULT (1 << 0) /* don't write out default=0 */
123    
124 niro 914 /* These defines are (only) used in addNewKernel() */
125     #define NEED_KERNEL (1 << 0)
126     #define NEED_INITRD (1 << 1)
127     #define NEED_TITLE (1 << 2)
128     #define NEED_ARGS (1 << 3)
129     #define NEED_MB (1 << 4)
130 niro 1696 #define NEED_END (1 << 5)
131 niro 2685 #define NEED_DEVTREE (1 << 6)
132 niro 532
133     #define MAIN_DEFAULT (1 << 0)
134     #define DEFAULT_SAVED -2
135 niro 1748 #define DEFAULT_SAVED_GRUB2 -3
136 niro 532
137     struct keywordTypes {
138     char * key;
139     enum lineType_e type;
140     char nextChar;
141 niro 914 char separatorChar;
142     };
143 niro 532
144 niro 1696 struct configFileInfo;
145    
146     typedef const char *(*findConfigFunc)(struct configFileInfo *);
147     typedef const int (*writeLineFunc)(struct configFileInfo *,
148     struct singleLine *line);
149 niro 2252 typedef char *(*getEnvFunc)(struct configFileInfo *, char *name);
150     typedef int (*setEnvFunc)(struct configFileInfo *, char *name, char *value);
151 niro 1696
152 niro 532 struct configFileInfo {
153     char * defaultConfig;
154 niro 1696 findConfigFunc findConfig;
155     writeLineFunc writeLine;
156 niro 2252 getEnvFunc getEnv;
157     setEnvFunc setEnv;
158 niro 532 struct keywordTypes * keywords;
159 niro 2056 int caseInsensitive;
160 niro 532 int defaultIsIndex;
161 niro 1696 int defaultIsVariable;
162 niro 532 int defaultSupportSaved;
163 niro 2252 int defaultIsSaved;
164 niro 1693 enum lineType_e entryStart;
165 niro 1696 enum lineType_e entryEnd;
166 niro 532 int needsBootPrefix;
167     int argsInQuotes;
168     int maxTitleLength;
169     int titleBracketed;
170 niro 1696 int titlePosition;
171 niro 914 int mbHyperFirst;
172     int mbInitRdIsModule;
173     int mbConcatArgs;
174     int mbAllowExtraInitRds;
175 niro 2252 char *envFile;
176 niro 532 };
177    
178     struct keywordTypes grubKeywords[] = {
179     { "title", LT_TITLE, ' ' },
180     { "root", LT_BOOTROOT, ' ' },
181     { "default", LT_DEFAULT, ' ' },
182     { "fallback", LT_FALLBACK, ' ' },
183     { "kernel", LT_KERNEL, ' ' },
184 niro 914 { "initrd", LT_INITRD, ' ', ' ' },
185 niro 532 { "module", LT_MBMODULE, ' ' },
186 niro 914 { "kernel", LT_HYPER, ' ' },
187 niro 532 { NULL, 0, 0 },
188     };
189    
190 niro 1715 const char *grubFindConfig(struct configFileInfo *cfi) {
191     static const char *configFiles[] = {
192     "/boot/grub/grub.conf",
193     "/boot/grub/menu.lst",
194 niro 1852 "/etc/grub.conf",
195 niro 2262 "/boot/grub2/grub.cfg",
196     "/boot/grub2-efi/grub.cfg",
197 niro 1715 NULL
198     };
199     static int i = -1;
200    
201     if (i == -1) {
202     for (i = 0; configFiles[i] != NULL; i++) {
203     dbgPrintf("Checking \"%s\": ", configFiles[i]);
204     if (!access(configFiles[i], R_OK)) {
205     dbgPrintf("found\n");
206     return configFiles[i];
207     }
208     dbgPrintf("not found\n");
209     }
210     }
211     return configFiles[i];
212     }
213    
214 niro 532 struct configFileInfo grubConfigType = {
215 niro 1715 .findConfig = grubFindConfig,
216 niro 1692 .keywords = grubKeywords,
217     .defaultIsIndex = 1,
218     .defaultSupportSaved = 1,
219 niro 1693 .entryStart = LT_TITLE,
220 niro 1692 .needsBootPrefix = 1,
221     .mbHyperFirst = 1,
222     .mbInitRdIsModule = 1,
223     .mbAllowExtraInitRds = 1,
224 niro 532 };
225    
226 niro 1696 struct keywordTypes grub2Keywords[] = {
227     { "menuentry", LT_MENUENTRY, ' ' },
228     { "}", LT_ENTRY_END, ' ' },
229     { "echo", LT_ECHO, ' ' },
230     { "set", LT_SET_VARIABLE,' ', '=' },
231     { "root", LT_BOOTROOT, ' ' },
232     { "default", LT_DEFAULT, ' ' },
233     { "fallback", LT_FALLBACK, ' ' },
234     { "linux", LT_KERNEL, ' ' },
235 niro 1940 { "linuxefi", LT_KERNEL_EFI, ' ' },
236 niro 2683 { "linux16", LT_KERNEL_16, ' ' },
237 niro 1696 { "initrd", LT_INITRD, ' ', ' ' },
238 niro 1940 { "initrdefi", LT_INITRD_EFI, ' ', ' ' },
239 niro 2683 { "initrd16", LT_INITRD_16, ' ', ' ' },
240 niro 1696 { "module", LT_MBMODULE, ' ' },
241     { "kernel", LT_HYPER, ' ' },
242 niro 2685 { "devicetree", LT_DEVTREE, ' ' },
243 niro 1696 { NULL, 0, 0 },
244     };
245    
246     const char *grub2FindConfig(struct configFileInfo *cfi) {
247     static const char *configFiles[] = {
248     "/boot/grub/grub-efi.cfg",
249     "/boot/grub/grub.cfg",
250     NULL
251     };
252     static int i = -1;
253 niro 1714 static const char *grub_cfg = "/boot/grub/grub.cfg";
254 niro 2246 int rc = -1;
255 niro 1696
256     if (i == -1) {
257     for (i = 0; configFiles[i] != NULL; i++) {
258     dbgPrintf("Checking \"%s\": ", configFiles[i]);
259 niro 2246 if ((rc = access(configFiles[i], R_OK))) {
260     if (errno == EACCES) {
261     printf("Unable to access bootloader configuration file "
262     "\"%s\": %m\n", configFiles[i]);
263     exit(1);
264     }
265     continue;
266     } else {
267 niro 1696 dbgPrintf("found\n");
268     return configFiles[i];
269     }
270     }
271     }
272 niro 1714
273     /* Ubuntu renames grub2 to grub, so check for the grub.d directory
274     * that isn't in grub1, and if it exists, return the config file path
275     * that they use. */
276     if (configFiles[i] == NULL && !access("/etc/grub.d/", R_OK)) {
277     dbgPrintf("found\n");
278     return grub_cfg;
279     }
280    
281     dbgPrintf("not found\n");
282 niro 1696 return configFiles[i];
283     }
284    
285 niro 2252 /* kind of hacky. It'll give the first 1024 bytes, ish. */
286     static char *grub2GetEnv(struct configFileInfo *info, char *name)
287     {
288     static char buf[1025];
289     char *s = NULL;
290     char *ret = NULL;
291 niro 2682 char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";
292     int rc = asprintf(&s, "grub-editenv %s list | grep '^%s='", envFile, name);
293 niro 2252
294     if (rc < 0)
295     return NULL;
296    
297     FILE *f = popen(s, "r");
298     if (!f)
299     goto out;
300    
301     memset(buf, '\0', sizeof (buf));
302     ret = fgets(buf, 1024, f);
303     pclose(f);
304    
305     if (ret) {
306     ret += strlen(name) + 1;
307     ret[strlen(ret) - 1] = '\0';
308     }
309     dbgPrintf("grub2GetEnv(%s): %s\n", name, ret);
310     out:
311     free(s);
312     return ret;
313     }
314    
315 niro 2257 static int sPopCount(const char *s, const char *c)
316     {
317     int ret = 0;
318     if (!s)
319     return -1;
320     for (int i = 0; s[i] != '\0'; i++)
321     for (int j = 0; c[j] != '\0'; j++)
322     if (s[i] == c[j])
323     ret++;
324     return ret;
325     }
326    
327     static char *shellEscape(const char *s)
328     {
329     int l = strlen(s) + sPopCount(s, "'") * 2;
330    
331     char *ret = calloc(l+1, sizeof (*ret));
332     if (!ret)
333     return NULL;
334     for (int i = 0, j = 0; s[i] != '\0'; i++, j++) {
335     if (s[i] == '\'')
336     ret[j++] = '\\';
337     ret[j] = s[i];
338     }
339     return ret;
340     }
341    
342     static void unquote(char *s)
343     {
344     int l = strlen(s);
345    
346     if ((s[l-1] == '\'' && s[0] == '\'') || (s[l-1] == '"' && s[0] == '"')) {
347     memmove(s, s+1, l-2);
348     s[l-2] = '\0';
349     }
350     }
351    
352 niro 2252 static int grub2SetEnv(struct configFileInfo *info, char *name, char *value)
353     {
354     char *s = NULL;
355     int rc = 0;
356 niro 2682 char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";
357 niro 2252
358 niro 2257 unquote(value);
359     value = shellEscape(value);
360     if (!value)
361     return -1;
362    
363 niro 2682 rc = asprintf(&s, "grub-editenv %s set '%s=%s'", envFile, name, value);
364 niro 2257 free(value);
365 niro 2252 if (rc <0)
366     return -1;
367    
368     dbgPrintf("grub2SetEnv(%s): %s\n", name, s);
369     rc = system(s);
370     free(s);
371     return rc;
372     }
373    
374     /* this is a gigantic hack to avoid clobbering grub2 variables... */
375     static int is_special_grub2_variable(const char *name)
376     {
377     if (!strcmp(name,"\"${next_entry}\""))
378     return 1;
379     if (!strcmp(name,"\"${prev_saved_entry}\""))
380     return 1;
381     return 0;
382     }
383    
384 niro 1746 int sizeOfSingleLine(struct singleLine * line) {
385     int count = 0;
386    
387 niro 1846 for (int i = 0; i < line->numElements; i++) {
388 niro 1746 int indentSize = 0;
389    
390     count = count + strlen(line->elements[i].item);
391    
392     indentSize = strlen(line->elements[i].indent);
393     if (indentSize > 0)
394     count = count + indentSize;
395     else
396     /* be extra safe and add room for whitespaces */
397     count = count + 1;
398     }
399    
400     /* room for trailing terminator */
401     count = count + 1;
402    
403     return count;
404     }
405    
406 niro 1800 static int isquote(char q)
407     {
408     if (q == '\'' || q == '\"')
409     return 1;
410     return 0;
411     }
412    
413 niro 1940 static int iskernel(enum lineType_e type) {
414 niro 2683 return (type == LT_KERNEL || type == LT_KERNEL_EFI || type == LT_KERNEL_16);
415 niro 1940 }
416    
417     static int isinitrd(enum lineType_e type) {
418 niro 2683 return (type == LT_INITRD || type == LT_INITRD_EFI || type == LT_INITRD_16);
419 niro 1940 }
420    
421 niro 1746 char *grub2ExtractTitle(struct singleLine * line) {
422     char * current;
423     char * current_indent;
424     int current_len;
425     int current_indent_len;
426     int i;
427    
428     /* bail out if line does not start with menuentry */
429     if (strcmp(line->elements[0].item, "menuentry"))
430     return NULL;
431    
432     i = 1;
433     current = line->elements[i].item;
434     current_len = strlen(current);
435    
436     /* if second word is quoted, strip the quotes and return single word */
437 niro 1800 if (isquote(*current) && isquote(current[current_len - 1])) {
438     char *tmp;
439    
440     tmp = strdup(current);
441     *(tmp + current_len - 1) = '\0';
442     return ++tmp;
443 niro 1746 }
444    
445     /* if no quotes, return second word verbatim */
446 niro 1800 if (!isquote(*current))
447     return current;
448 niro 1746
449     /* second element start with a quote, so we have to find the element
450     * whose last character is also quote (assuming it's the closing one) */
451 niro 1800 int resultMaxSize;
452     char * result;
453    
454     resultMaxSize = sizeOfSingleLine(line);
455     result = malloc(resultMaxSize);
456     snprintf(result, resultMaxSize, "%s", ++current);
457    
458     i++;
459     for (; i < line->numElements; ++i) {
460 niro 1746 current = line->elements[i].item;
461     current_len = strlen(current);
462     current_indent = line->elements[i].indent;
463     current_indent_len = strlen(current_indent);
464    
465     strncat(result, current_indent, current_indent_len);
466 niro 1800 if (!isquote(current[current_len-1])) {
467     strncat(result, current, current_len);
468 niro 1746 } else {
469 niro 1800 strncat(result, current, current_len - 1);
470     break;
471 niro 1746 }
472     }
473 niro 1800 return result;
474 niro 1746 }
475    
476 niro 1696 struct configFileInfo grub2ConfigType = {
477     .findConfig = grub2FindConfig,
478 niro 2252 .getEnv = grub2GetEnv,
479     .setEnv = grub2SetEnv,
480 niro 1696 .keywords = grub2Keywords,
481     .defaultIsIndex = 1,
482 niro 1748 .defaultSupportSaved = 1,
483 niro 1696 .defaultIsVariable = 1,
484     .entryStart = LT_MENUENTRY,
485     .entryEnd = LT_ENTRY_END,
486     .titlePosition = 1,
487     .needsBootPrefix = 1,
488     .mbHyperFirst = 1,
489     .mbInitRdIsModule = 1,
490     .mbAllowExtraInitRds = 1,
491     };
492    
493 niro 532 struct keywordTypes yabootKeywords[] = {
494     { "label", LT_TITLE, '=' },
495     { "root", LT_ROOT, '=' },
496     { "default", LT_DEFAULT, '=' },
497     { "image", LT_KERNEL, '=' },
498     { "bsd", LT_GENERIC, '=' },
499     { "macos", LT_GENERIC, '=' },
500     { "macosx", LT_GENERIC, '=' },
501     { "magicboot", LT_GENERIC, '=' },
502     { "darwin", LT_GENERIC, '=' },
503     { "timeout", LT_GENERIC, '=' },
504     { "install", LT_GENERIC, '=' },
505     { "fstype", LT_GENERIC, '=' },
506     { "hfstype", LT_GENERIC, '=' },
507     { "delay", LT_GENERIC, '=' },
508     { "defaultos", LT_GENERIC, '=' },
509     { "init-message", LT_GENERIC, '=' },
510     { "enablecdboot", LT_GENERIC, ' ' },
511     { "enableofboot", LT_GENERIC, ' ' },
512     { "enablenetboot", LT_GENERIC, ' ' },
513     { "nonvram", LT_GENERIC, ' ' },
514     { "hide", LT_GENERIC, ' ' },
515     { "protect", LT_GENERIC, ' ' },
516     { "nobless", LT_GENERIC, ' ' },
517     { "nonvram", LT_GENERIC, ' ' },
518     { "brokenosx", LT_GENERIC, ' ' },
519     { "usemount", LT_GENERIC, ' ' },
520     { "mntpoint", LT_GENERIC, '=' },
521     { "partition", LT_GENERIC, '=' },
522     { "device", LT_GENERIC, '=' },
523     { "fstype", LT_GENERIC, '=' },
524 niro 914 { "initrd", LT_INITRD, '=', ';' },
525 niro 532 { "append", LT_KERNELARGS, '=' },
526     { "boot", LT_BOOT, '=' },
527     { "lba", LT_LBA, ' ' },
528     { NULL, 0, 0 },
529     };
530    
531     struct keywordTypes liloKeywords[] = {
532     { "label", LT_TITLE, '=' },
533     { "root", LT_ROOT, '=' },
534     { "default", LT_DEFAULT, '=' },
535     { "image", LT_KERNEL, '=' },
536     { "other", LT_OTHER, '=' },
537     { "initrd", LT_INITRD, '=' },
538     { "append", LT_KERNELARGS, '=' },
539     { "boot", LT_BOOT, '=' },
540     { "lba", LT_LBA, ' ' },
541     { NULL, 0, 0 },
542     };
543    
544 niro 914 struct keywordTypes eliloKeywords[] = {
545     { "label", LT_TITLE, '=' },
546     { "root", LT_ROOT, '=' },
547     { "default", LT_DEFAULT, '=' },
548     { "image", LT_KERNEL, '=' },
549     { "initrd", LT_INITRD, '=' },
550     { "append", LT_KERNELARGS, '=' },
551     { "vmm", LT_HYPER, '=' },
552     { NULL, 0, 0 },
553     };
554    
555 niro 532 struct keywordTypes siloKeywords[] = {
556     { "label", LT_TITLE, '=' },
557     { "root", LT_ROOT, '=' },
558     { "default", LT_DEFAULT, '=' },
559     { "image", LT_KERNEL, '=' },
560     { "other", LT_OTHER, '=' },
561     { "initrd", LT_INITRD, '=' },
562     { "append", LT_KERNELARGS, '=' },
563     { "boot", LT_BOOT, '=' },
564     { NULL, 0, 0 },
565     };
566    
567     struct keywordTypes ziplKeywords[] = {
568     { "target", LT_BOOTROOT, '=' },
569     { "image", LT_KERNEL, '=' },
570     { "ramdisk", LT_INITRD, '=' },
571     { "parameters", LT_KERNELARGS, '=' },
572     { "default", LT_DEFAULT, '=' },
573     { NULL, 0, 0 },
574     };
575    
576 niro 914 struct keywordTypes extlinuxKeywords[] = {
577     { "label", LT_TITLE, ' ' },
578     { "root", LT_ROOT, ' ' },
579     { "default", LT_DEFAULT, ' ' },
580     { "kernel", LT_KERNEL, ' ' },
581     { "initrd", LT_INITRD, ' ', ',' },
582     { "append", LT_KERNELARGS, ' ' },
583     { "prompt", LT_UNKNOWN, ' ' },
584     { NULL, 0, 0 },
585     };
586     int useextlinuxmenu;
587 niro 532 struct configFileInfo eliloConfigType = {
588 niro 1692 .defaultConfig = "/boot/efi/EFI/redhat/elilo.conf",
589     .keywords = eliloKeywords,
590 niro 1693 .entryStart = LT_KERNEL,
591 niro 1692 .needsBootPrefix = 1,
592     .argsInQuotes = 1,
593     .mbConcatArgs = 1,
594 niro 532 };
595    
596     struct configFileInfo liloConfigType = {
597 niro 1692 .defaultConfig = "/etc/lilo.conf",
598     .keywords = liloKeywords,
599 niro 1693 .entryStart = LT_KERNEL,
600 niro 1692 .argsInQuotes = 1,
601     .maxTitleLength = 15,
602 niro 532 };
603    
604     struct configFileInfo yabootConfigType = {
605 niro 1692 .defaultConfig = "/etc/yaboot.conf",
606     .keywords = yabootKeywords,
607 niro 1693 .entryStart = LT_KERNEL,
608 niro 1692 .needsBootPrefix = 1,
609     .argsInQuotes = 1,
610     .maxTitleLength = 15,
611     .mbAllowExtraInitRds = 1,
612 niro 532 };
613    
614     struct configFileInfo siloConfigType = {
615 niro 1692 .defaultConfig = "/etc/silo.conf",
616     .keywords = siloKeywords,
617 niro 1693 .entryStart = LT_KERNEL,
618 niro 1692 .needsBootPrefix = 1,
619     .argsInQuotes = 1,
620     .maxTitleLength = 15,
621 niro 532 };
622    
623     struct configFileInfo ziplConfigType = {
624 niro 1692 .defaultConfig = "/etc/zipl.conf",
625     .keywords = ziplKeywords,
626 niro 1693 .entryStart = LT_TITLE,
627 niro 1692 .argsInQuotes = 1,
628     .titleBracketed = 1,
629 niro 532 };
630    
631 niro 914 struct configFileInfo extlinuxConfigType = {
632 niro 1692 .defaultConfig = "/boot/extlinux/extlinux.conf",
633     .keywords = extlinuxKeywords,
634 niro 2056 .caseInsensitive = 1,
635 niro 1693 .entryStart = LT_TITLE,
636 niro 1692 .needsBootPrefix = 1,
637     .maxTitleLength = 255,
638     .mbAllowExtraInitRds = 1,
639 niro 914 };
640    
641 niro 532 struct grubConfig {
642     struct singleLine * theLines;
643     struct singleEntry * entries;
644     char * primaryIndent;
645     char * secondaryIndent;
646     int defaultImage; /* -1 if none specified -- this value is
647     * written out, overriding original */
648     int fallbackImage; /* just like defaultImage */
649     int flags;
650     struct configFileInfo * cfi;
651     };
652    
653 niro 1156 blkid_cache blkid;
654    
655 niro 532 struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index);
656     struct singleEntry * findEntryByPath(struct grubConfig * cfg,
657     const char * path, const char * prefix,
658     int * index);
659 niro 2252 struct singleEntry * findEntryByTitle(struct grubConfig * cfg, char *title,
660     int * index);
661 niro 532 static int readFile(int fd, char ** bufPtr);
662     static void lineInit(struct singleLine * line);
663 niro 914 struct singleLine * lineDup(struct singleLine * line);
664 niro 532 static void lineFree(struct singleLine * line);
665     static int lineWrite(FILE * out, struct singleLine * line,
666     struct configFileInfo * cfi);
667     static int getNextLine(char ** bufPtr, struct singleLine * line,
668     struct configFileInfo * cfi);
669     static char * getRootSpecifier(char * str);
670 niro 1696 static void requote(struct singleLine *line, struct configFileInfo * cfi);
671 niro 914 static void insertElement(struct singleLine * line,
672     const char * item, int insertHere,
673     struct configFileInfo * cfi);
674     static void removeElement(struct singleLine * line, int removeHere);
675     static struct keywordTypes * getKeywordByType(enum lineType_e type,
676     struct configFileInfo * cfi);
677     static enum lineType_e getTypeByKeyword(char * keyword,
678     struct configFileInfo * cfi);
679     static struct singleLine * getLineByType(enum lineType_e type,
680     struct singleLine * line);
681     static int checkForExtLinux(struct grubConfig * config);
682     struct singleLine * addLineTmpl(struct singleEntry * entry,
683     struct singleLine * tmplLine,
684     struct singleLine * prevLine,
685     const char * val,
686     struct configFileInfo * cfi);
687     struct singleLine * addLine(struct singleEntry * entry,
688     struct configFileInfo * cfi,
689     enum lineType_e type, char * defaultIndent,
690     const char * val);
691 niro 532
692     static char * sdupprintf(const char *format, ...)
693     #ifdef __GNUC__
694     __attribute__ ((format (printf, 1, 2)));
695     #else
696     ;
697     #endif
698    
699     static char * sdupprintf(const char *format, ...) {
700     char *buf = NULL;
701     char c;
702     va_list args;
703     size_t size = 0;
704     va_start(args, format);
705    
706     /* XXX requires C99 vsnprintf behavior */
707     size = vsnprintf(&c, 1, format, args) + 1;
708     if (size == -1) {
709     printf("ERROR: vsnprintf behavior is not C99\n");
710     abort();
711     }
712    
713     va_end(args);
714     va_start(args, format);
715    
716     buf = malloc(size);
717     if (buf == NULL)
718     return NULL;
719     vsnprintf(buf, size, format, args);
720     va_end (args);
721    
722     return buf;
723     }
724    
725 niro 1940 static enum lineType_e preferredLineType(enum lineType_e type,
726     struct configFileInfo *cfi) {
727     if (isEfi && cfi == &grub2ConfigType) {
728     switch (type) {
729     case LT_KERNEL:
730 niro 2688 return isEfiOnly ? LT_KERNEL : LT_KERNEL_EFI;
731 niro 2687 case LT_INITRD:
732 niro 2688 return isEfiOnly ? LT_INITRD : LT_INITRD_EFI;
733 niro 1940 default:
734     return type;
735     }
736 niro 2683 #if defined(__i386__) || defined(__x86_64__)
737     } else if (cfi == &grub2ConfigType) {
738     switch (type) {
739     case LT_KERNEL:
740     return LT_KERNEL_16;
741     case LT_INITRD:
742     return LT_INITRD_16;
743     default:
744     return type;
745     }
746     #endif
747 niro 1940 }
748     return type;
749     }
750    
751 niro 914 static struct keywordTypes * getKeywordByType(enum lineType_e type,
752     struct configFileInfo * cfi) {
753 niro 1846 for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) {
754 niro 914 if (kw->type == type)
755     return kw;
756     }
757     return NULL;
758     }
759    
760 niro 1696 static char *getKeyByType(enum lineType_e type, struct configFileInfo * cfi) {
761     struct keywordTypes *kt = getKeywordByType(type, cfi);
762     if (kt)
763     return kt->key;
764     return "unknown";
765     }
766    
767 niro 914 static char * getpathbyspec(char *device) {
768     if (!blkid)
769     blkid_get_cache(&blkid, NULL);
770    
771     return blkid_get_devname(blkid, device, NULL);
772     }
773    
774 niro 1156 static char * getuuidbydev(char *device) {
775     if (!blkid)
776     blkid_get_cache(&blkid, NULL);
777    
778     return blkid_get_tag_value(blkid, "UUID", device);
779     }
780    
781 niro 914 static enum lineType_e getTypeByKeyword(char * keyword,
782     struct configFileInfo * cfi) {
783 niro 1846 for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) {
784 niro 2056 if (cfi->caseInsensitive) {
785     if (!strcasecmp(keyword, kw->key))
786     return kw->type;
787     } else {
788     if (!strcmp(keyword, kw->key))
789     return kw->type;
790     }
791 niro 914 }
792     return LT_UNKNOWN;
793     }
794    
795     static struct singleLine * getLineByType(enum lineType_e type,
796     struct singleLine * line) {
797     dbgPrintf("getLineByType(%d): ", type);
798     for (; line; line = line->next) {
799     dbgPrintf("%d:%s ", line->type,
800     line->numElements ? line->elements[0].item : "(empty)");
801     if (line->type & type) break;
802     }
803     dbgPrintf(line ? "\n" : " (failed)\n");
804     return line;
805     }
806    
807 niro 532 static int isBracketedTitle(struct singleLine * line) {
808 niro 914 if (line->numElements == 1 && *line->elements[0].item == '[') {
809 niro 532 int len = strlen(line->elements[0].item);
810     if (*(line->elements[0].item + len - 1) == ']') {
811     /* FIXME: this is a hack... */
812     if (strcmp(line->elements[0].item, "[defaultboot]")) {
813     return 1;
814     }
815     }
816     }
817     return 0;
818     }
819    
820 niro 1693 static int isEntryStart(struct singleLine * line,
821 niro 532 struct configFileInfo * cfi) {
822 niro 1693 return line->type == cfi->entryStart || line->type == LT_OTHER ||
823 niro 914 (cfi->titleBracketed && isBracketedTitle(line));
824 niro 532 }
825    
826     /* extract the title from within brackets (for zipl) */
827     static char * extractTitle(struct singleLine * line) {
828     /* bracketed title... let's extract it (leaks a byte) */
829 niro 2252 char * title = NULL;
830     if (line->type == LT_TITLE) {
831     title = strdup(line->elements[0].item);
832     title++;
833     *(title + strlen(title) - 1) = '\0';
834     } else if (line->type == LT_MENUENTRY)
835     title = strdup(line->elements[1].item);
836     else
837     return NULL;
838 niro 532 return title;
839     }
840    
841     static int readFile(int fd, char ** bufPtr) {
842     int alloced = 0, size = 0, i = 0;
843     char * buf = NULL;
844    
845     do {
846     size += i;
847     if ((size + 1024) > alloced) {
848     alloced += 4096;
849     buf = realloc(buf, alloced + 1);
850     }
851     } while ((i = read(fd, buf + size, 1024)) > 0);
852    
853     if (i < 0) {
854     fprintf(stderr, _("error reading input: %s\n"), strerror(errno));
855     free(buf);
856     return 1;
857     }
858    
859     buf = realloc(buf, size + 2);
860     if (size == 0)
861     buf[size++] = '\n';
862     else
863     if (buf[size - 1] != '\n')
864     buf[size++] = '\n';
865     buf[size] = '\0';
866    
867     *bufPtr = buf;
868    
869     return 0;
870     }
871    
872     static void lineInit(struct singleLine * line) {
873     line->indent = NULL;
874     line->elements = NULL;
875     line->numElements = 0;
876     line->next = NULL;
877     }
878    
879 niro 914 struct singleLine * lineDup(struct singleLine * line) {
880     struct singleLine * newLine = malloc(sizeof(*newLine));
881    
882     newLine->indent = strdup(line->indent);
883     newLine->next = NULL;
884     newLine->type = line->type;
885     newLine->numElements = line->numElements;
886     newLine->elements = malloc(sizeof(*newLine->elements) *
887     newLine->numElements);
888    
889 niro 1846 for (int i = 0; i < newLine->numElements; i++) {
890 niro 914 newLine->elements[i].indent = strdup(line->elements[i].indent);
891     newLine->elements[i].item = strdup(line->elements[i].item);
892     }
893    
894     return newLine;
895     }
896    
897 niro 532 static void lineFree(struct singleLine * line) {
898     if (line->indent) free(line->indent);
899    
900 niro 1846 for (int i = 0; i < line->numElements; i++) {
901 niro 532 free(line->elements[i].item);
902     free(line->elements[i].indent);
903     }
904    
905     if (line->elements) free(line->elements);
906     lineInit(line);
907     }
908    
909     static int lineWrite(FILE * out, struct singleLine * line,
910     struct configFileInfo * cfi) {
911     if (fprintf(out, "%s", line->indent) == -1) return -1;
912    
913 niro 1846 for (int i = 0; i < line->numElements; i++) {
914 niro 1801 /* Need to handle this, because we strip the quotes from
915     * menuentry when read it. */
916     if (line->type == LT_MENUENTRY && i == 1) {
917     if(!isquote(*line->elements[i].item))
918     fprintf(out, "\'%s\'", line->elements[i].item);
919     else
920     fprintf(out, "%s", line->elements[i].item);
921     fprintf(out, "%s", line->elements[i].indent);
922    
923     continue;
924     }
925    
926 niro 532 if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)
927     if (fputc('"', out) == EOF) return -1;
928    
929     if (fprintf(out, "%s", line->elements[i].item) == -1) return -1;
930 niro 914 if (i < line->numElements - 1)
931     if (fprintf(out, "%s", line->elements[i].indent) == -1) return -1;
932 niro 532 }
933    
934     if (line->type == LT_KERNELARGS && cfi->argsInQuotes)
935     if (fputc('"', out) == EOF) return -1;
936    
937     if (fprintf(out, "\n") == -1) return -1;
938    
939     return 0;
940     }
941    
942     /* we've guaranteed that the buffer ends w/ \n\0 */
943     static int getNextLine(char ** bufPtr, struct singleLine * line,
944     struct configFileInfo * cfi) {
945     char * end;
946     char * start = *bufPtr;
947     char * chptr;
948     int elementsAlloced = 0;
949     struct lineElement * element;
950     int first = 1;
951    
952     lineFree(line);
953    
954     end = strchr(start, '\n');
955     *end = '\0';
956     *bufPtr = end + 1;
957    
958     for (chptr = start; *chptr && isspace(*chptr); chptr++) ;
959    
960     line->indent = strndup(start, chptr - start);
961     start = chptr;
962    
963     while (start < end) {
964     /* we know !isspace(*start) */
965    
966     if (elementsAlloced == line->numElements) {
967     elementsAlloced += 5;
968     line->elements = realloc(line->elements,
969     sizeof(*line->elements) * elementsAlloced);
970     }
971    
972     element = line->elements + line->numElements;
973    
974     chptr = start;
975     while (*chptr && !isspace(*chptr)) {
976     if (first && *chptr == '=') break;
977     chptr++;
978     }
979     element->item = strndup(start, chptr - start);
980     start = chptr;
981    
982     /* lilo actually accepts the pathological case of append = " foo " */
983     if (*start == '=')
984     chptr = start + 1;
985     else
986     chptr = start;
987    
988     do {
989     for (; *chptr && isspace(*chptr); chptr++);
990     if (*chptr == '=')
991     chptr = chptr + 1;
992     } while (isspace(*chptr));
993    
994     element->indent = strndup(start, chptr - start);
995     start = chptr;
996    
997     line->numElements++;
998     first = 0;
999     }
1000    
1001     if (!line->numElements)
1002     line->type = LT_WHITESPACE;
1003     else {
1004 niro 914 line->type = getTypeByKeyword(line->elements[0].item, cfi);
1005     if (line->type == LT_UNKNOWN) {
1006 niro 532 /* zipl does [title] instead of something reasonable like all
1007     * the other boot loaders. kind of ugly */
1008     if (cfi->titleBracketed && isBracketedTitle(line)) {
1009     line->type = LT_TITLE;
1010     }
1011    
1012     /* this is awkward, but we need to be able to handle keywords
1013     that begin with a # (specifically for #boot in grub.conf),
1014     but still make comments lines with no elements (everything
1015     stored in the indent */
1016     if (*line->elements[0].item == '#') {
1017     char * fullLine;
1018     int len;
1019    
1020     len = strlen(line->indent);
1021 niro 1846 for (int i = 0; i < line->numElements; i++)
1022 niro 532 len += strlen(line->elements[i].item) +
1023     strlen(line->elements[i].indent);
1024    
1025     fullLine = malloc(len + 1);
1026     strcpy(fullLine, line->indent);
1027     free(line->indent);
1028     line->indent = fullLine;
1029    
1030 niro 1846 for (int i = 0; i < line->numElements; i++) {
1031 niro 532 strcat(fullLine, line->elements[i].item);
1032     strcat(fullLine, line->elements[i].indent);
1033     free(line->elements[i].item);
1034     free(line->elements[i].indent);
1035     }
1036    
1037     line->type = LT_WHITESPACE;
1038     line->numElements = 0;
1039     }
1040 niro 914 } else {
1041     struct keywordTypes *kw;
1042    
1043     kw = getKeywordByType(line->type, cfi);
1044    
1045     /* space isn't the only separator, we need to split
1046     * elements up more
1047     */
1048     if (!isspace(kw->separatorChar)) {
1049     char indent[2] = "";
1050     indent[0] = kw->separatorChar;
1051 niro 1846 for (int i = 1; i < line->numElements; i++) {
1052 niro 914 char *p;
1053     int numNewElements;
1054    
1055     numNewElements = 0;
1056     p = line->elements[i].item;
1057     while (*p != '\0') {
1058     if (*p == kw->separatorChar)
1059     numNewElements++;
1060     p++;
1061     }
1062     if (line->numElements + numNewElements >= elementsAlloced) {
1063     elementsAlloced += numNewElements + 5;
1064     line->elements = realloc(line->elements,
1065     sizeof(*line->elements) * elementsAlloced);
1066     }
1067    
1068 niro 1846 for (int j = line->numElements; j > i; j--) {
1069 niro 914 line->elements[j + numNewElements] = line->elements[j];
1070     }
1071     line->numElements += numNewElements;
1072    
1073     p = line->elements[i].item;
1074     while (*p != '\0') {
1075    
1076     while (*p != kw->separatorChar && *p != '\0') p++;
1077     if (*p == '\0') {
1078     break;
1079     }
1080    
1081 niro 1840 line->elements[i + 1].indent = line->elements[i].indent;
1082 niro 914 line->elements[i].indent = strdup(indent);
1083     *p++ = '\0';
1084     i++;
1085     line->elements[i].item = strdup(p);
1086     }
1087     }
1088     }
1089 niro 532 }
1090     }
1091    
1092     return 0;
1093     }
1094    
1095 niro 2258 static int isnumber(const char *s)
1096     {
1097     int i;
1098     for (i = 0; s[i] != '\0'; i++)
1099     if (s[i] < '0' || s[i] > '9')
1100     return 0;
1101     return i;
1102     }
1103    
1104 niro 532 static struct grubConfig * readConfig(const char * inName,
1105     struct configFileInfo * cfi) {
1106     int in;
1107     char * incoming = NULL, * head;
1108     int rc;
1109     int sawEntry = 0;
1110     int movedLine = 0;
1111     struct grubConfig * cfg;
1112     struct singleLine * last = NULL, * line, * defaultLine = NULL;
1113     char * end;
1114     struct singleEntry * entry = NULL;
1115 niro 1846 int len;
1116 niro 532 char * buf;
1117    
1118 niro 2246 if (inName == NULL) {
1119     printf("Could not find bootloader configuration\n");
1120     exit(1);
1121     } else if (!strcmp(inName, "-")) {
1122 niro 532 in = 0;
1123     } else {
1124     if ((in = open(inName, O_RDONLY)) < 0) {
1125     fprintf(stderr, _("error opening %s for read: %s\n"),
1126     inName, strerror(errno));
1127     return NULL;
1128     }
1129     }
1130    
1131     rc = readFile(in, &incoming);
1132     close(in);
1133     if (rc) return NULL;
1134    
1135     head = incoming;
1136     cfg = malloc(sizeof(*cfg));
1137     cfg->primaryIndent = strdup("");
1138     cfg->secondaryIndent = strdup("\t");
1139     cfg->flags = GRUB_CONFIG_NO_DEFAULT;
1140     cfg->cfi = cfi;
1141     cfg->theLines = NULL;
1142     cfg->entries = NULL;
1143     cfg->fallbackImage = 0;
1144    
1145     /* copy everything we have */
1146     while (*head) {
1147     line = malloc(sizeof(*line));
1148     lineInit(line);
1149    
1150     if (getNextLine(&head, line, cfi)) {
1151     free(line);
1152     /* XXX memory leak of everything in cfg */
1153     return NULL;
1154     }
1155    
1156     if (!sawEntry && line->numElements) {
1157     free(cfg->primaryIndent);
1158     cfg->primaryIndent = strdup(line->indent);
1159     } else if (line->numElements) {
1160     free(cfg->secondaryIndent);
1161     cfg->secondaryIndent = strdup(line->indent);
1162     }
1163    
1164 niro 1727 if (isEntryStart(line, cfi) || (cfg->entries && !sawEntry)) {
1165 niro 532 sawEntry = 1;
1166     if (!entry) {
1167     cfg->entries = malloc(sizeof(*entry));
1168     entry = cfg->entries;
1169     } else {
1170     entry->next = malloc(sizeof(*entry));
1171     entry = entry->next;
1172     }
1173    
1174     entry->skip = 0;
1175     entry->multiboot = 0;
1176     entry->lines = NULL;
1177     entry->next = NULL;
1178     }
1179    
1180 niro 1696 if (line->type == LT_SET_VARIABLE) {
1181     dbgPrintf("found 'set' command (%d elements): ", line->numElements);
1182     dbgPrintf("%s", line->indent);
1183 niro 1846 for (int i = 0; i < line->numElements; i++)
1184 niro 1840 dbgPrintf("\"%s\"%s", line->elements[i].item, line->elements[i].indent);
1185 niro 1696 dbgPrintf("\n");
1186     struct keywordTypes *kwType = getKeywordByType(LT_DEFAULT, cfi);
1187     if (kwType && line->numElements == 3 &&
1188 niro 2252 !strcmp(line->elements[1].item, kwType->key) &&
1189     !is_special_grub2_variable(line->elements[2].item)) {
1190 niro 1696 dbgPrintf("Line sets default config\n");
1191     cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1192     defaultLine = line;
1193     }
1194     } else if (line->type == LT_DEFAULT && line->numElements == 2) {
1195 niro 532 cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1196     defaultLine = line;
1197 niro 914
1198 niro 1940 } else if (iskernel(line->type)) {
1199 niro 914 /* if by some freak chance this is multiboot and the "module"
1200     * lines came earlier in the template, make sure to use LT_HYPER
1201     * instead of LT_KERNEL now
1202     */
1203     if (entry->multiboot)
1204     line->type = LT_HYPER;
1205    
1206 niro 532 } else if (line->type == LT_MBMODULE) {
1207 niro 914 /* go back and fix the LT_KERNEL line to indicate LT_HYPER
1208     * instead, now that we know this is a multiboot entry.
1209     * This only applies to grub, but that's the only place we
1210     * should find LT_MBMODULE lines anyway.
1211     */
1212 niro 1846 for (struct singleLine *l = entry->lines; l; l = l->next) {
1213 niro 914 if (l->type == LT_HYPER)
1214     break;
1215 niro 1940 else if (iskernel(l->type)) {
1216 niro 914 l->type = LT_HYPER;
1217     break;
1218     }
1219     }
1220 niro 532 entry->multiboot = 1;
1221 niro 914
1222     } else if (line->type == LT_HYPER) {
1223     entry->multiboot = 1;
1224    
1225 niro 532 } else if (line->type == LT_FALLBACK && line->numElements == 2) {
1226     cfg->fallbackImage = strtol(line->elements[1].item, &end, 10);
1227     if (*end) cfg->fallbackImage = -1;
1228 niro 914
1229 niro 532 } else if (line->type == LT_TITLE && line->numElements > 1) {
1230     /* make the title a single argument (undoing our parsing) */
1231     len = 0;
1232 niro 1846 for (int i = 1; i < line->numElements; i++) {
1233 niro 532 len += strlen(line->elements[i].item);
1234     len += strlen(line->elements[i].indent);
1235     }
1236     buf = malloc(len + 1);
1237     *buf = '\0';
1238    
1239 niro 1846 for (int i = 1; i < line->numElements; i++) {
1240 niro 532 strcat(buf, line->elements[i].item);
1241     free(line->elements[i].item);
1242    
1243     if ((i + 1) != line->numElements) {
1244     strcat(buf, line->elements[i].indent);
1245     free(line->elements[i].indent);
1246     }
1247     }
1248    
1249     line->elements[1].indent =
1250     line->elements[line->numElements - 1].indent;
1251     line->elements[1].item = buf;
1252     line->numElements = 2;
1253 niro 1801 } else if (line->type == LT_MENUENTRY && line->numElements > 3) {
1254     /* let --remove-kernel="TITLE=what" work */
1255     len = 0;
1256     char *extras;
1257     char *title;
1258 niro 914
1259 niro 1846 for (int i = 1; i < line->numElements; i++) {
1260 niro 1801 len += strlen(line->elements[i].item);
1261     len += strlen(line->elements[i].indent);
1262     }
1263     buf = malloc(len + 1);
1264     *buf = '\0';
1265    
1266     /* allocate mem for extra flags. */
1267     extras = malloc(len + 1);
1268     *extras = '\0';
1269    
1270     /* get title. */
1271 niro 1846 for (int i = 0; i < line->numElements; i++) {
1272 niro 1801 if (!strcmp(line->elements[i].item, "menuentry"))
1273     continue;
1274     if (isquote(*line->elements[i].item))
1275     title = line->elements[i].item + 1;
1276     else
1277     title = line->elements[i].item;
1278    
1279     len = strlen(title);
1280     if (isquote(title[len-1])) {
1281     strncat(buf, title,len-1);
1282     break;
1283     } else {
1284     strcat(buf, title);
1285     strcat(buf, line->elements[i].indent);
1286     }
1287     }
1288    
1289     /* get extras */
1290     int count = 0;
1291 niro 1846 for (int i = 0; i < line->numElements; i++) {
1292 niro 1837 if (count >= 2) {
1293 niro 1801 strcat(extras, line->elements[i].item);
1294     strcat(extras, line->elements[i].indent);
1295     }
1296    
1297     if (!strcmp(line->elements[i].item, "menuentry"))
1298     continue;
1299    
1300     /* count ' or ", there should be two in menuentry line. */
1301     if (isquote(*line->elements[i].item))
1302     count++;
1303    
1304     len = strlen(line->elements[i].item);
1305    
1306     if (isquote(line->elements[i].item[len -1]))
1307     count++;
1308    
1309     /* ok, we get the final ' or ", others are extras. */
1310     }
1311     line->elements[1].indent =
1312     line->elements[line->numElements - 2].indent;
1313     line->elements[1].item = buf;
1314     line->elements[2].indent =
1315     line->elements[line->numElements - 2].indent;
1316     line->elements[2].item = extras;
1317     line->numElements = 3;
1318 niro 532 } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {
1319     /* Strip off any " which may be present; they'll be put back
1320     on write. This is one of the few (the only?) places that grubby
1321     canonicalizes the output */
1322    
1323     if (line->numElements >= 2) {
1324     int last, len;
1325    
1326 niro 1801 if (isquote(*line->elements[1].item))
1327 niro 914 memmove(line->elements[1].item, line->elements[1].item + 1,
1328     strlen(line->elements[1].item + 1) + 1);
1329 niro 532
1330     last = line->numElements - 1;
1331     len = strlen(line->elements[last].item) - 1;
1332 niro 1801 if (isquote(line->elements[last].item[len]))
1333 niro 532 line->elements[last].item[len] = '\0';
1334     }
1335     }
1336    
1337     /* If we find a generic config option which should live at the
1338     top of the file, move it there. Old versions of grubby were
1339     probably responsible for putting new images in the wrong
1340     place in front of it anyway. */
1341     if (sawEntry && line->type == LT_GENERIC) {
1342     struct singleLine **l = &cfg->theLines;
1343     struct singleLine **last_nonws = &cfg->theLines;
1344     while (*l) {
1345     if ((*l)->type != LT_WHITESPACE)
1346     last_nonws = &((*l)->next);
1347     l = &((*l)->next);
1348     }
1349     line->next = *last_nonws;
1350     *last_nonws = line;
1351     movedLine = 1;
1352     continue; /* without setting 'last' */
1353     }
1354 niro 914
1355 niro 532 /* If a second line of whitespace happens after a generic option
1356     which was moved, drop it. */
1357     if (movedLine && line->type == LT_WHITESPACE && last->type == LT_WHITESPACE) {
1358     lineFree(line);
1359     free(line);
1360     movedLine = 0;
1361     continue;
1362     }
1363     movedLine = 0;
1364    
1365     if (sawEntry) {
1366     if (!entry->lines)
1367     entry->lines = line;
1368     else
1369     last->next = line;
1370 niro 1696 dbgPrintf("readConfig added %s to %p\n", getKeyByType(line->type, cfi), entry);
1371    
1372     /* we could have seen this outside of an entry... if so, we
1373     * ignore it like any other line we don't grok */
1374     if (line->type == LT_ENTRY_END && sawEntry)
1375     sawEntry = 0;
1376 niro 532 } else {
1377     if (!cfg->theLines)
1378     cfg->theLines = line;
1379 niro 914 else
1380 niro 532 last->next = line;
1381 niro 1696 dbgPrintf("readConfig added %s to cfg\n", getKeyByType(line->type, cfi));
1382 niro 532 }
1383    
1384     last = line;
1385     }
1386    
1387     free(incoming);
1388    
1389 niro 1696 dbgPrintf("defaultLine is %s\n", defaultLine ? "set" : "unset");
1390 niro 532 if (defaultLine) {
1391 niro 1748 if (defaultLine->numElements > 2 &&
1392     cfi->defaultSupportSaved &&
1393     !strncmp(defaultLine->elements[2].item,"\"${saved_entry}\"", 16)) {
1394 niro 2252 cfg->cfi->defaultIsSaved = 1;
1395     cfg->defaultImage = DEFAULT_SAVED_GRUB2;
1396     if (cfg->cfi->getEnv) {
1397     char *defTitle = cfi->getEnv(cfg->cfi, "saved_entry");
1398     if (defTitle) {
1399     int index = 0;
1400 niro 2258 if (isnumber(defTitle)) {
1401     index = atoi(defTitle);
1402     entry = findEntryByIndex(cfg, index);
1403     } else {
1404     entry = findEntryByTitle(cfg, defTitle, &index);
1405     }
1406 niro 2252 if (entry)
1407     cfg->defaultImage = index;
1408     }
1409     }
1410 niro 1748 } else if (cfi->defaultIsVariable) {
1411 niro 1696 char *value = defaultLine->elements[2].item;
1412     while (*value && (*value == '"' || *value == '\'' ||
1413     *value == ' ' || *value == '\t'))
1414     value++;
1415     cfg->defaultImage = strtol(value, &end, 10);
1416     while (*end && (*end == '"' || *end == '\'' ||
1417     *end == ' ' || *end == '\t'))
1418     end++;
1419     if (*end) cfg->defaultImage = -1;
1420     } else if (cfi->defaultSupportSaved &&
1421 niro 532 !strncmp(defaultLine->elements[1].item, "saved", 5)) {
1422     cfg->defaultImage = DEFAULT_SAVED;
1423     } else if (cfi->defaultIsIndex) {
1424     cfg->defaultImage = strtol(defaultLine->elements[1].item, &end, 10);
1425     if (*end) cfg->defaultImage = -1;
1426     } else if (defaultLine->numElements >= 2) {
1427 niro 1846 int i = 0;
1428 niro 532 while ((entry = findEntryByIndex(cfg, i))) {
1429     for (line = entry->lines; line; line = line->next)
1430     if (line->type == LT_TITLE) break;
1431    
1432     if (!cfi->titleBracketed) {
1433     if (line && (line->numElements >= 2) &&
1434     !strcmp(defaultLine->elements[1].item,
1435     line->elements[1].item)) break;
1436     } else if (line) {
1437     if (!strcmp(defaultLine->elements[1].item,
1438     extractTitle(line))) break;
1439     }
1440     i++;
1441 niro 914 entry = NULL;
1442 niro 532 }
1443    
1444 niro 914 if (entry){
1445     cfg->defaultImage = i;
1446     }else{
1447     cfg->defaultImage = -1;
1448     }
1449 niro 532 }
1450 niro 2252 } else if (cfg->cfi->defaultIsSaved && cfg->cfi->getEnv) {
1451     char *defTitle = cfi->getEnv(cfg->cfi, "saved_entry");
1452     if (defTitle) {
1453     int index = 0;
1454 niro 2258 if (isnumber(defTitle)) {
1455     index = atoi(defTitle);
1456     entry = findEntryByIndex(cfg, index);
1457     } else {
1458     entry = findEntryByTitle(cfg, defTitle, &index);
1459     }
1460 niro 2252 if (entry)
1461     cfg->defaultImage = index;
1462     }
1463 niro 914 } else {
1464     cfg->defaultImage = 0;
1465 niro 532 }
1466    
1467     return cfg;
1468     }
1469    
1470     static void writeDefault(FILE * out, char * indent,
1471     char * separator, struct grubConfig * cfg) {
1472     struct singleEntry * entry;
1473     struct singleLine * line;
1474     int i;
1475    
1476     if (!cfg->defaultImage && cfg->flags == GRUB_CONFIG_NO_DEFAULT) return;
1477    
1478     if (cfg->defaultImage == DEFAULT_SAVED)
1479     fprintf(out, "%sdefault%ssaved\n", indent, separator);
1480 niro 2252 else if (cfg->cfi->defaultIsSaved) {
1481 niro 1748 fprintf(out, "%sset default=\"${saved_entry}\"\n", indent);
1482 niro 2252 if (cfg->defaultImage >= 0 && cfg->cfi->setEnv) {
1483     char *title;
1484     entry = findEntryByIndex(cfg, cfg->defaultImage);
1485     line = getLineByType(LT_MENUENTRY, entry->lines);
1486     if (!line)
1487     line = getLineByType(LT_TITLE, entry->lines);
1488     if (line) {
1489     title = extractTitle(line);
1490     if (title)
1491     cfg->cfi->setEnv(cfg->cfi, "saved_entry", title);
1492     }
1493     }
1494     } else if (cfg->defaultImage > -1) {
1495 niro 532 if (cfg->cfi->defaultIsIndex) {
1496 niro 1696 if (cfg->cfi->defaultIsVariable) {
1497     fprintf(out, "%sset default=\"%d\"\n", indent,
1498     cfg->defaultImage);
1499     } else {
1500     fprintf(out, "%sdefault%s%d\n", indent, separator,
1501     cfg->defaultImage);
1502     }
1503 niro 532 } else {
1504     int image = cfg->defaultImage;
1505    
1506     entry = cfg->entries;
1507     while (entry && entry->skip) entry = entry->next;
1508    
1509     i = 0;
1510     while (entry && i < image) {
1511     entry = entry->next;
1512    
1513     while (entry && entry->skip) entry = entry->next;
1514     i++;
1515     }
1516    
1517     if (!entry) return;
1518    
1519 niro 914 line = getLineByType(LT_TITLE, entry->lines);
1520 niro 532
1521     if (line && line->numElements >= 2)
1522     fprintf(out, "%sdefault%s%s\n", indent, separator,
1523     line->elements[1].item);
1524     else if (line && (line->numElements == 1) &&
1525     cfg->cfi->titleBracketed) {
1526     fprintf(out, "%sdefault%s%s\n", indent, separator,
1527     extractTitle(line));
1528     }
1529     }
1530     }
1531     }
1532    
1533     static int writeConfig(struct grubConfig * cfg, char * outName,
1534     const char * prefix) {
1535     FILE * out;
1536     struct singleLine * line;
1537     struct singleEntry * entry;
1538     char * tmpOutName;
1539     int needs = MAIN_DEFAULT;
1540     struct stat sb;
1541     int i;
1542    
1543     if (!strcmp(outName, "-")) {
1544     out = stdout;
1545     tmpOutName = NULL;
1546     } else {
1547     if (!lstat(outName, &sb) && S_ISLNK(sb.st_mode)) {
1548     char * buf;
1549     int len = 256;
1550     int rc;
1551    
1552     /* most likely the symlink is relative, so change our
1553 niro 914 directory to the dir of the symlink */
1554 niro 1868 char *dir = strdupa(outName);
1555 niro 1934 rc = chdir(dirname(dir));
1556 niro 532 do {
1557     buf = alloca(len + 1);
1558 niro 914 rc = readlink(basename(outName), buf, len);
1559 niro 532 if (rc == len) len += 256;
1560     } while (rc == len);
1561    
1562     if (rc < 0) {
1563     fprintf(stderr, _("grubby: error readlink link %s: %s\n"),
1564     outName, strerror(errno));
1565     return 1;
1566     }
1567    
1568     outName = buf;
1569     outName[rc] = '\0';
1570     }
1571    
1572     tmpOutName = alloca(strlen(outName) + 2);
1573     sprintf(tmpOutName, "%s-", outName);
1574     out = fopen(tmpOutName, "w");
1575     if (!out) {
1576     fprintf(stderr, _("grubby: error creating %s: %s\n"), tmpOutName,
1577     strerror(errno));
1578     return 1;
1579     }
1580    
1581     if (!stat(outName, &sb)) {
1582     if (chmod(tmpOutName, sb.st_mode & ~(S_IFMT))) {
1583     fprintf(stderr, _("grubby: error setting perms on %s: %s\n"),
1584     tmpOutName, strerror(errno));
1585     fclose(out);
1586     unlink(tmpOutName);
1587     return 1;
1588     }
1589     }
1590     }
1591    
1592     line = cfg->theLines;
1593 niro 1696 struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi);
1594 niro 532 while (line) {
1595 niro 1696 if (line->type == LT_SET_VARIABLE && defaultKw &&
1596     line->numElements == 3 &&
1597 niro 2252 !strcmp(line->elements[1].item, defaultKw->key) &&
1598     !is_special_grub2_variable(line->elements[2].item)) {
1599 niro 532 writeDefault(out, line->indent, line->elements[0].indent, cfg);
1600     needs &= ~MAIN_DEFAULT;
1601 niro 1696 } else if (line->type == LT_DEFAULT) {
1602     writeDefault(out, line->indent, line->elements[0].indent, cfg);
1603     needs &= ~MAIN_DEFAULT;
1604 niro 532 } else if (line->type == LT_FALLBACK) {
1605     if (cfg->fallbackImage > -1)
1606     fprintf(out, "%s%s%s%d\n", line->indent,
1607     line->elements[0].item, line->elements[0].indent,
1608     cfg->fallbackImage);
1609     } else {
1610     if (lineWrite(out, line, cfg->cfi) == -1) {
1611     fprintf(stderr, _("grubby: error writing %s: %s\n"),
1612     tmpOutName, strerror(errno));
1613     fclose(out);
1614     unlink(tmpOutName);
1615     return 1;
1616     }
1617     }
1618    
1619     line = line->next;
1620     }
1621    
1622     if (needs & MAIN_DEFAULT) {
1623     writeDefault(out, cfg->primaryIndent, "=", cfg);
1624     needs &= ~MAIN_DEFAULT;
1625     }
1626    
1627     i = 0;
1628     while ((entry = findEntryByIndex(cfg, i++))) {
1629     if (entry->skip) continue;
1630    
1631     line = entry->lines;
1632     while (line) {
1633     if (lineWrite(out, line, cfg->cfi) == -1) {
1634     fprintf(stderr, _("grubby: error writing %s: %s\n"),
1635     tmpOutName, strerror(errno));
1636     fclose(out);
1637     unlink(tmpOutName);
1638     return 1;
1639     }
1640     line = line->next;
1641     }
1642     }
1643    
1644     if (tmpOutName) {
1645     if (rename(tmpOutName, outName)) {
1646     fprintf(stderr, _("grubby: error moving %s to %s: %s\n"),
1647     tmpOutName, outName, strerror(errno));
1648     unlink(outName);
1649     return 1;
1650     }
1651     }
1652    
1653     return 0;
1654     }
1655    
1656     static int numEntries(struct grubConfig *cfg) {
1657     int i = 0;
1658     struct singleEntry * entry;
1659    
1660     entry = cfg->entries;
1661     while (entry) {
1662     if (!entry->skip)
1663     i++;
1664     entry = entry->next;
1665     }
1666     return i;
1667     }
1668    
1669 niro 1156 static char *findDiskForRoot()
1670     {
1671     int fd;
1672     char buf[65536];
1673     char *devname;
1674     char *chptr;
1675     int rc;
1676    
1677     if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {
1678     fprintf(stderr, "grubby: failed to open %s: %s\n",
1679     _PATH_MOUNTED, strerror(errno));
1680     return NULL;
1681     }
1682    
1683     rc = read(fd, buf, sizeof(buf) - 1);
1684     if (rc <= 0) {
1685     fprintf(stderr, "grubby: failed to read %s: %s\n",
1686     _PATH_MOUNTED, strerror(errno));
1687     close(fd);
1688     return NULL;
1689     }
1690     close(fd);
1691     buf[rc] = '\0';
1692     chptr = buf;
1693    
1694 niro 1841 char *foundanswer = NULL;
1695    
1696 niro 1156 while (chptr && chptr != buf+rc) {
1697     devname = chptr;
1698    
1699     /*
1700     * The first column of a mtab entry is the device, but if the entry is a
1701     * special device it won't start with /, so move on to the next line.
1702     */
1703     if (*devname != '/') {
1704     chptr = strchr(chptr, '\n');
1705     if (chptr)
1706     chptr++;
1707     continue;
1708     }
1709    
1710     /* Seek to the next space */
1711     chptr = strchr(chptr, ' ');
1712     if (!chptr) {
1713     fprintf(stderr, "grubby: error parsing %s: %s\n",
1714     _PATH_MOUNTED, strerror(errno));
1715     return NULL;
1716     }
1717    
1718     /*
1719     * The second column of a mtab entry is the mount point, we are looking
1720     * for '/' obviously.
1721     */
1722     if (*(++chptr) == '/' && *(++chptr) == ' ') {
1723 niro 1841 /* remember the last / entry in mtab */
1724     foundanswer = devname;
1725 niro 1156 }
1726    
1727     /* Next line */
1728     chptr = strchr(chptr, '\n');
1729     if (chptr)
1730     chptr++;
1731     }
1732    
1733 niro 1841 /* Return the last / entry found */
1734     if (foundanswer) {
1735     chptr = strchr(foundanswer, ' ');
1736     *chptr = '\0';
1737     return strdup(foundanswer);
1738     }
1739    
1740 niro 1156 return NULL;
1741     }
1742    
1743 niro 2236 void printEntry(struct singleEntry * entry, FILE *f) {
1744 niro 1736 int i;
1745     struct singleLine * line;
1746    
1747     for (line = entry->lines; line; line = line->next) {
1748 niro 2236 log_message(f, "DBG: %s", line->indent);
1749 niro 1736 for (i = 0; i < line->numElements; i++) {
1750 niro 1801 /* Need to handle this, because we strip the quotes from
1751     * menuentry when read it. */
1752     if (line->type == LT_MENUENTRY && i == 1) {
1753     if(!isquote(*line->elements[i].item))
1754 niro 2236 log_message(f, "\'%s\'", line->elements[i].item);
1755 niro 1801 else
1756 niro 2236 log_message(f, "%s", line->elements[i].item);
1757     log_message(f, "%s", line->elements[i].indent);
1758 niro 1801
1759     continue;
1760     }
1761    
1762 niro 2236 log_message(f, "%s%s",
1763 niro 1736 line->elements[i].item, line->elements[i].indent);
1764     }
1765 niro 2236 log_message(f, "\n");
1766 niro 1736 }
1767     }
1768    
1769 niro 2236 void notSuitablePrintf(struct singleEntry * entry, int okay, const char *fmt, ...)
1770 niro 1736 {
1771 niro 2236 static int once;
1772     va_list argp, argq;
1773 niro 1736
1774 niro 2236 va_start(argp, fmt);
1775    
1776     va_copy(argq, argp);
1777     if (!once) {
1778     log_time(NULL);
1779     log_message(NULL, "command line: %s\n", saved_command_line);
1780     }
1781     log_message(NULL, "DBG: Image entry %s: ", okay ? "succeeded" : "failed");
1782     log_vmessage(NULL, fmt, argq);
1783    
1784     printEntry(entry, NULL);
1785     va_end(argq);
1786    
1787     if (!debug) {
1788     once = 1;
1789     va_end(argp);
1790 niro 1736 return;
1791 niro 2236 }
1792 niro 1736
1793 niro 2236 if (okay) {
1794     va_end(argp);
1795     return;
1796     }
1797    
1798     if (!once)
1799     log_message(stderr, "DBG: command line: %s\n", saved_command_line);
1800     once = 1;
1801 niro 1736 fprintf(stderr, "DBG: Image entry failed: ");
1802     vfprintf(stderr, fmt, argp);
1803 niro 2236 printEntry(entry, stderr);
1804 niro 1736 va_end(argp);
1805     }
1806    
1807 niro 1745 #define beginswith(s, c) ((s) && (s)[0] == (c))
1808    
1809     static int endswith(const char *s, char c)
1810     {
1811     int slen;
1812    
1813 niro 1750 if (!s || !s[0])
1814 niro 1745 return 0;
1815     slen = strlen(s) - 1;
1816    
1817     return s[slen] == c;
1818     }
1819    
1820 niro 532 int suitableImage(struct singleEntry * entry, const char * bootPrefix,
1821     int skipRemoved, int flags) {
1822     struct singleLine * line;
1823     char * fullName;
1824     int i;
1825     char * dev;
1826     char * rootspec;
1827 niro 1156 char * rootdev;
1828 niro 532
1829 niro 1736 if (skipRemoved && entry->skip) {
1830 niro 2236 notSuitablePrintf(entry, 0, "marked to skip\n");
1831 niro 1736 return 0;
1832     }
1833 niro 532
1834 niro 2683 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
1835 niro 1736 if (!line) {
1836 niro 2236 notSuitablePrintf(entry, 0, "no line found\n");
1837 niro 1736 return 0;
1838     }
1839     if (line->numElements < 2) {
1840 niro 2236 notSuitablePrintf(entry, 0, "line has only %d elements\n",
1841 niro 1736 line->numElements);
1842     return 0;
1843     }
1844 niro 914
1845 niro 2236 if (flags & GRUBBY_BADIMAGE_OKAY) {
1846     notSuitablePrintf(entry, 1, "\n");
1847     return 1;
1848     }
1849 niro 532
1850     fullName = alloca(strlen(bootPrefix) +
1851     strlen(line->elements[1].item) + 1);
1852     rootspec = getRootSpecifier(line->elements[1].item);
1853 niro 1745 int rootspec_offset = rootspec ? strlen(rootspec) : 0;
1854     int hasslash = endswith(bootPrefix, '/') ||
1855     beginswith(line->elements[1].item + rootspec_offset, '/');
1856     sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/",
1857     line->elements[1].item + rootspec_offset);
1858 niro 1736 if (access(fullName, R_OK)) {
1859 niro 2236 notSuitablePrintf(entry, 0, "access to %s failed\n", fullName);
1860 niro 1736 return 0;
1861     }
1862 niro 532 for (i = 2; i < line->numElements; i++)
1863     if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1864     if (i < line->numElements) {
1865     dev = line->elements[i].item + 5;
1866     } else {
1867     /* look for a lilo style LT_ROOT line */
1868 niro 914 line = getLineByType(LT_ROOT, entry->lines);
1869 niro 532
1870     if (line && line->numElements >= 2) {
1871     dev = line->elements[1].item;
1872     } else {
1873 niro 914 /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS.
1874     * grub+multiboot uses LT_MBMODULE for the args, so check that too.
1875     */
1876     line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines);
1877 niro 532
1878     /* failed to find one */
1879 niro 1736 if (!line) {
1880 niro 2236 notSuitablePrintf(entry, 0, "no line found\n");
1881 niro 1736 return 0;
1882     }
1883 niro 532
1884     for (i = 1; i < line->numElements; i++)
1885     if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1886     if (i < line->numElements)
1887     dev = line->elements[i].item + 5;
1888     else {
1889 niro 2236 notSuitablePrintf(entry, 0, "no root= entry found\n");
1890 niro 532 /* it failed too... can't find root= */
1891     return 0;
1892     }
1893     }
1894     }
1895    
1896 niro 914 dev = getpathbyspec(dev);
1897 niro 1736 if (!getpathbyspec(dev)) {
1898 niro 2236 notSuitablePrintf(entry, 0, "can't find blkid entry for %s\n", dev);
1899 niro 914 return 0;
1900 niro 1736 } else
1901     dev = getpathbyspec(dev);
1902 niro 532
1903 niro 1156 rootdev = findDiskForRoot();
1904 niro 1736 if (!rootdev) {
1905 niro 2236 notSuitablePrintf(entry, 0, "can't find root device\n");
1906 niro 914 return 0;
1907 niro 1736 }
1908 niro 914
1909 niro 1177 if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {
1910 niro 2236 notSuitablePrintf(entry, 0, "uuid missing: rootdev %s, dev %s\n",
1911 niro 1736 getuuidbydev(rootdev), getuuidbydev(dev));
1912 niro 1177 free(rootdev);
1913     return 0;
1914     }
1915 niro 532
1916 niro 1156 if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
1917 niro 2236 notSuitablePrintf(entry, 0, "uuid mismatch: rootdev %s, dev %s\n",
1918 niro 1736 getuuidbydev(rootdev), getuuidbydev(dev));
1919 niro 1156 free(rootdev);
1920 niro 914 return 0;
1921 niro 1156 }
1922 niro 532
1923 niro 1156 free(rootdev);
1924 niro 2236 notSuitablePrintf(entry, 1, "\n");
1925 niro 1156
1926 niro 532 return 1;
1927     }
1928    
1929     /* returns the first match on or after the one pointed to by index (if index
1930     is not NULL) which is not marked as skip */
1931     struct singleEntry * findEntryByPath(struct grubConfig * config,
1932     const char * kernel, const char * prefix,
1933     int * index) {
1934     struct singleEntry * entry = NULL;
1935     struct singleLine * line;
1936     int i;
1937     char * chptr;
1938     char * rootspec = NULL;
1939     enum lineType_e checkType = LT_KERNEL;
1940    
1941     if (isdigit(*kernel)) {
1942     int * indexVars = alloca(sizeof(*indexVars) * strlen(kernel));
1943    
1944     i = 0;
1945     indexVars[i] = strtol(kernel, &chptr, 10);
1946     while (*chptr == ',') {
1947     i++;
1948     kernel = chptr + 1;
1949     indexVars[i] = strtol(kernel, &chptr, 10);
1950     }
1951    
1952     if (*chptr) {
1953     /* can't parse it, bail */
1954     return NULL;
1955     }
1956    
1957     indexVars[i + 1] = -1;
1958    
1959     i = 0;
1960     if (index) {
1961     while (i < *index) i++;
1962     if (indexVars[i] == -1) return NULL;
1963     }
1964    
1965     entry = findEntryByIndex(config, indexVars[i]);
1966     if (!entry) return NULL;
1967    
1968 niro 2683 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
1969 niro 532 if (!line) return NULL;
1970    
1971     if (index) *index = indexVars[i];
1972     return entry;
1973     }
1974    
1975     if (!strcmp(kernel, "DEFAULT")) {
1976     if (index && *index > config->defaultImage) {
1977     entry = NULL;
1978     } else {
1979     entry = findEntryByIndex(config, config->defaultImage);
1980     if (entry && entry->skip)
1981     entry = NULL;
1982     else if (index)
1983     *index = config->defaultImage;
1984     }
1985     } else if (!strcmp(kernel, "ALL")) {
1986     if (index)
1987     i = *index;
1988     else
1989     i = 0;
1990    
1991     while ((entry = findEntryByIndex(config, i))) {
1992     if (!entry->skip) break;
1993     i++;
1994     }
1995    
1996     if (entry && index)
1997     *index = i;
1998     } else {
1999     if (index)
2000     i = *index;
2001     else
2002     i = 0;
2003    
2004     if (!strncmp(kernel, "TITLE=", 6)) {
2005     prefix = "";
2006 niro 1801 checkType = LT_TITLE|LT_MENUENTRY;
2007 niro 532 kernel += 6;
2008     }
2009    
2010 niro 914 for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {
2011     if (entry->skip) continue;
2012 niro 532
2013 niro 914 dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);
2014 niro 532
2015 niro 914 /* check all the lines matching checkType */
2016     for (line = entry->lines; line; line = line->next) {
2017 niro 2052 enum lineType_e ct = checkType;
2018     if (entry->multiboot && checkType == LT_KERNEL)
2019 niro 2683 ct = LT_KERNEL|LT_KERNEL_EFI|LT_MBMODULE|LT_HYPER|LT_KERNEL_16;
2020 niro 2052 else if (checkType & LT_KERNEL)
2021 niro 2683 ct = checkType | LT_KERNEL_EFI | LT_KERNEL_16;
2022 niro 2052 line = getLineByType(ct, line);
2023     if (!line)
2024     break; /* not found in this entry */
2025 niro 532
2026 niro 1801 if (line && line->type != LT_MENUENTRY &&
2027     line->numElements >= 2) {
2028 niro 914 rootspec = getRootSpecifier(line->elements[1].item);
2029     if (!strcmp(line->elements[1].item +
2030     ((rootspec != NULL) ? strlen(rootspec) : 0),
2031     kernel + strlen(prefix)))
2032     break;
2033     }
2034 niro 1801 if(line->type == LT_MENUENTRY &&
2035     !strcmp(line->elements[1].item, kernel))
2036     break;
2037 niro 914 }
2038    
2039     /* make sure this entry has a kernel identifier; this skips
2040     * non-Linux boot entries (could find netbsd etc, though, which is
2041     * unfortunate)
2042     */
2043 niro 2683 if (line && getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines))
2044 niro 914 break; /* found 'im! */
2045 niro 532 }
2046    
2047     if (index) *index = i;
2048     }
2049    
2050     return entry;
2051     }
2052    
2053 niro 2252 struct singleEntry * findEntryByTitle(struct grubConfig * cfg, char *title,
2054     int * index) {
2055     struct singleEntry * entry;
2056     struct singleLine * line;
2057     int i;
2058     char * newtitle;
2059    
2060     for (i = 0, entry = cfg->entries; entry; entry = entry->next, i++) {
2061     if (index && i < *index)
2062     continue;
2063     line = getLineByType(LT_TITLE, entry->lines);
2064     if (!line)
2065     line = getLineByType(LT_MENUENTRY, entry->lines);
2066     if (!line)
2067     continue;
2068     newtitle = grub2ExtractTitle(line);
2069     if (!newtitle)
2070     continue;
2071     if (!strcmp(title, newtitle))
2072     break;
2073     }
2074    
2075     if (!entry)
2076     return NULL;
2077    
2078     if (index)
2079     *index = i;
2080     return entry;
2081     }
2082    
2083 niro 532 struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index) {
2084     struct singleEntry * entry;
2085    
2086     entry = cfg->entries;
2087     while (index && entry) {
2088     entry = entry->next;
2089     index--;
2090     }
2091    
2092     return entry;
2093     }
2094    
2095     /* Find a good template to use for the new kernel. An entry is
2096     * good if the kernel and mkinitrd exist (even if the entry
2097     * is going to be removed). Try and use the default entry, but
2098     * if that doesn't work just take the first. If we can't find one,
2099     * bail. */
2100     struct singleEntry * findTemplate(struct grubConfig * cfg, const char * prefix,
2101     int * indexPtr, int skipRemoved, int flags) {
2102     struct singleEntry * entry, * entry2;
2103     int index;
2104    
2105 niro 2252 if (cfg->cfi->defaultIsSaved) {
2106     if (cfg->cfi->getEnv) {
2107     char *defTitle = cfg->cfi->getEnv(cfg->cfi, "saved_entry");
2108     if (defTitle) {
2109     int index = 0;
2110 niro 2258 if (isnumber(defTitle)) {
2111     index = atoi(defTitle);
2112     entry = findEntryByIndex(cfg, index);
2113     } else {
2114     entry = findEntryByTitle(cfg, defTitle, &index);
2115     }
2116     if (entry)
2117     cfg->defaultImage = index;
2118 niro 2252 }
2119     }
2120     } else if (cfg->defaultImage > -1) {
2121 niro 532 entry = findEntryByIndex(cfg, cfg->defaultImage);
2122     if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2123     if (indexPtr) *indexPtr = cfg->defaultImage;
2124     return entry;
2125     }
2126     }
2127    
2128     index = 0;
2129     while ((entry = findEntryByIndex(cfg, index))) {
2130     if (suitableImage(entry, prefix, skipRemoved, flags)) {
2131     int j;
2132     for (j = 0; j < index; j++) {
2133     entry2 = findEntryByIndex(cfg, j);
2134     if (entry2->skip) index--;
2135     }
2136     if (indexPtr) *indexPtr = index;
2137    
2138     return entry;
2139     }
2140    
2141     index++;
2142     }
2143    
2144     fprintf(stderr, _("grubby fatal error: unable to find a suitable template\n"));
2145    
2146     return NULL;
2147     }
2148    
2149     char * findBootPrefix(void) {
2150     struct stat sb, sb2;
2151    
2152     stat("/", &sb);
2153     #ifdef __ia64__
2154     stat("/boot/efi/EFI/redhat/", &sb2);
2155     #else
2156     stat("/boot", &sb2);
2157     #endif
2158    
2159     if (sb.st_dev == sb2.st_dev)
2160     return strdup("");
2161    
2162     #ifdef __ia64__
2163     return strdup("/boot/efi/EFI/redhat/");
2164     #else
2165     return strdup("/boot");
2166     #endif
2167     }
2168    
2169     void markRemovedImage(struct grubConfig * cfg, const char * image,
2170     const char * prefix) {
2171     struct singleEntry * entry;
2172    
2173 niro 1801 if (!image)
2174     return;
2175 niro 532
2176 niro 1801 /* check and see if we're removing the default image */
2177     if (isdigit(*image)) {
2178     entry = findEntryByPath(cfg, image, prefix, NULL);
2179     if(entry)
2180     entry->skip = 1;
2181     return;
2182     }
2183    
2184 niro 532 while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
2185     entry->skip = 1;
2186     }
2187    
2188     void setDefaultImage(struct grubConfig * config, int hasNew,
2189     const char * defaultKernelPath, int newIsDefault,
2190 niro 1859 const char * prefix, int flags, int index) {
2191 niro 532 struct singleEntry * entry, * entry2, * newDefault;
2192     int i, j;
2193    
2194     if (newIsDefault) {
2195     config->defaultImage = 0;
2196     return;
2197 niro 1859 } else if ((index >= 0) && config->cfi->defaultIsIndex) {
2198     if (findEntryByIndex(config, index))
2199     config->defaultImage = index;
2200     else
2201     config->defaultImage = -1;
2202     return;
2203 niro 532 } else if (defaultKernelPath) {
2204     i = 0;
2205     if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
2206     config->defaultImage = i;
2207     } else {
2208     config->defaultImage = -1;
2209     return;
2210     }
2211     }
2212    
2213     /* defaultImage now points to what we'd like to use, but before any order
2214     changes */
2215 niro 1748 if ((config->defaultImage == DEFAULT_SAVED) ||
2216     (config->defaultImage == DEFAULT_SAVED_GRUB2))
2217 niro 532 /* default is set to saved, we don't want to change it */
2218     return;
2219    
2220     if (config->defaultImage > -1)
2221     entry = findEntryByIndex(config, config->defaultImage);
2222     else
2223     entry = NULL;
2224    
2225     if (entry && !entry->skip) {
2226     /* we can preserve the default */
2227     if (hasNew)
2228     config->defaultImage++;
2229    
2230     /* count the number of entries erased before this one */
2231     for (j = 0; j < config->defaultImage; j++) {
2232     entry2 = findEntryByIndex(config, j);
2233     if (entry2->skip) config->defaultImage--;
2234     }
2235     } else if (hasNew) {
2236     config->defaultImage = 0;
2237     } else {
2238     /* Either we just erased the default (or the default line was bad
2239     * to begin with) and didn't put a new one in. We'll use the first
2240     * valid image. */
2241     newDefault = findTemplate(config, prefix, &config->defaultImage, 1,
2242     flags);
2243     if (!newDefault)
2244     config->defaultImage = -1;
2245     }
2246     }
2247    
2248     void setFallbackImage(struct grubConfig * config, int hasNew) {
2249     struct singleEntry * entry, * entry2;
2250     int j;
2251    
2252     if (config->fallbackImage == -1) return;
2253    
2254     entry = findEntryByIndex(config, config->fallbackImage);
2255     if (!entry || entry->skip) {
2256     config->fallbackImage = -1;
2257     return;
2258     }
2259    
2260     if (hasNew)
2261     config->fallbackImage++;
2262    
2263     /* count the number of entries erased before this one */
2264     for (j = 0; j < config->fallbackImage; j++) {
2265     entry2 = findEntryByIndex(config, j);
2266     if (entry2->skip) config->fallbackImage--;
2267     }
2268     }
2269    
2270     void displayEntry(struct singleEntry * entry, const char * prefix, int index) {
2271     struct singleLine * line;
2272     char * root = NULL;
2273     int i;
2274    
2275     printf("index=%d\n", index);
2276    
2277 niro 2683 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
2278 niro 914 if (!line) {
2279     printf("non linux entry\n");
2280     return;
2281     }
2282    
2283 niro 1849 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2284     printf("kernel=%s\n", line->elements[1].item);
2285     else
2286     printf("kernel=%s%s\n", prefix, line->elements[1].item);
2287 niro 532
2288     if (line->numElements >= 3) {
2289     printf("args=\"");
2290     i = 2;
2291     while (i < line->numElements) {
2292     if (!strncmp(line->elements[i].item, "root=", 5)) {
2293     root = line->elements[i].item + 5;
2294     } else {
2295     printf("%s%s", line->elements[i].item,
2296     line->elements[i].indent);
2297     }
2298    
2299     i++;
2300     }
2301     printf("\"\n");
2302     } else {
2303 niro 914 line = getLineByType(LT_KERNELARGS, entry->lines);
2304 niro 532 if (line) {
2305     char * s;
2306    
2307     printf("args=\"");
2308     i = 1;
2309     while (i < line->numElements) {
2310     if (!strncmp(line->elements[i].item, "root=", 5)) {
2311     root = line->elements[i].item + 5;
2312     } else {
2313     s = line->elements[i].item;
2314    
2315     printf("%s%s", s, line->elements[i].indent);
2316     }
2317    
2318     i++;
2319     }
2320    
2321     s = line->elements[i - 1].indent;
2322     printf("\"\n");
2323     }
2324     }
2325    
2326     if (!root) {
2327 niro 914 line = getLineByType(LT_ROOT, entry->lines);
2328 niro 532 if (line && line->numElements >= 2)
2329     root=line->elements[1].item;
2330     }
2331    
2332     if (root) {
2333     char * s = alloca(strlen(root) + 1);
2334    
2335     strcpy(s, root);
2336     if (s[strlen(s) - 1] == '"')
2337     s[strlen(s) - 1] = '\0';
2338     /* make sure the root doesn't have a trailing " */
2339     printf("root=%s\n", s);
2340     }
2341    
2342 niro 2683 line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
2343 niro 532
2344     if (line && line->numElements >= 2) {
2345 niro 1849 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2346     printf("initrd=");
2347     else
2348     printf("initrd=%s", prefix);
2349    
2350 niro 532 for (i = 1; i < line->numElements; i++)
2351     printf("%s%s", line->elements[i].item, line->elements[i].indent);
2352     printf("\n");
2353     }
2354 niro 1751
2355     line = getLineByType(LT_TITLE, entry->lines);
2356     if (line) {
2357     printf("title=%s\n", line->elements[1].item);
2358     } else {
2359     char * title;
2360     line = getLineByType(LT_MENUENTRY, entry->lines);
2361     title = grub2ExtractTitle(line);
2362     if (title)
2363     printf("title=%s\n", title);
2364     }
2365 niro 532 }
2366    
2367 niro 1850 int isSuseSystem(void) {
2368     const char * path;
2369     const static char default_path[] = "/etc/SuSE-release";
2370    
2371     if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2372     path = default_path;
2373    
2374     if (!access(path, R_OK))
2375     return 1;
2376     return 0;
2377     }
2378    
2379     int isSuseGrubConf(const char * path) {
2380     FILE * grubConf;
2381     char * line = NULL;
2382     size_t len = 0, res = 0;
2383    
2384     grubConf = fopen(path, "r");
2385     if (!grubConf) {
2386     dbgPrintf("Could not open SuSE configuration file '%s'\n", path);
2387     return 0;
2388     }
2389    
2390     while ((res = getline(&line, &len, grubConf)) != -1) {
2391     if (!strncmp(line, "setup", 5)) {
2392     fclose(grubConf);
2393     free(line);
2394     return 1;
2395     }
2396     }
2397    
2398     dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2399     path);
2400    
2401     fclose(grubConf);
2402     free(line);
2403     return 0;
2404     }
2405    
2406     int suseGrubConfGetLba(const char * path, int * lbaPtr) {
2407     FILE * grubConf;
2408     char * line = NULL;
2409     size_t res = 0, len = 0;
2410    
2411     if (!path) return 1;
2412     if (!lbaPtr) return 1;
2413    
2414     grubConf = fopen(path, "r");
2415     if (!grubConf) return 1;
2416    
2417     while ((res = getline(&line, &len, grubConf)) != -1) {
2418     if (line[res - 1] == '\n')
2419     line[res - 1] = '\0';
2420     else if (len > res)
2421     line[res] = '\0';
2422     else {
2423     line = realloc(line, res + 1);
2424     line[res] = '\0';
2425     }
2426    
2427     if (!strncmp(line, "setup", 5)) {
2428     if (strstr(line, "--force-lba")) {
2429     *lbaPtr = 1;
2430     } else {
2431     *lbaPtr = 0;
2432     }
2433     dbgPrintf("lba: %i\n", *lbaPtr);
2434     break;
2435     }
2436     }
2437    
2438     free(line);
2439     fclose(grubConf);
2440     return 0;
2441     }
2442    
2443     int suseGrubConfGetInstallDevice(const char * path, char ** devicePtr) {
2444     FILE * grubConf;
2445     char * line = NULL;
2446     size_t res = 0, len = 0;
2447     char * lastParamPtr = NULL;
2448     char * secLastParamPtr = NULL;
2449     char installDeviceNumber = '\0';
2450     char * bounds = NULL;
2451    
2452     if (!path) return 1;
2453     if (!devicePtr) return 1;
2454    
2455     grubConf = fopen(path, "r");
2456     if (!grubConf) return 1;
2457    
2458     while ((res = getline(&line, &len, grubConf)) != -1) {
2459     if (strncmp(line, "setup", 5))
2460     continue;
2461    
2462     if (line[res - 1] == '\n')
2463     line[res - 1] = '\0';
2464     else if (len > res)
2465     line[res] = '\0';
2466     else {
2467     line = realloc(line, res + 1);
2468     line[res] = '\0';
2469     }
2470    
2471     lastParamPtr = bounds = line + res;
2472    
2473     /* Last parameter in grub may be an optional IMAGE_DEVICE */
2474     while (!isspace(*lastParamPtr))
2475     lastParamPtr--;
2476     lastParamPtr++;
2477    
2478     secLastParamPtr = lastParamPtr - 2;
2479     dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2480    
2481     if (lastParamPtr + 3 > bounds) {
2482     dbgPrintf("lastParamPtr going over boundary");
2483     fclose(grubConf);
2484     free(line);
2485     return 1;
2486     }
2487     if (!strncmp(lastParamPtr, "(hd", 3))
2488     lastParamPtr += 3;
2489     dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2490    
2491     /*
2492     * Second last parameter will decide wether last parameter is
2493     * an IMAGE_DEVICE or INSTALL_DEVICE
2494     */
2495     while (!isspace(*secLastParamPtr))
2496     secLastParamPtr--;
2497     secLastParamPtr++;
2498    
2499     if (secLastParamPtr + 3 > bounds) {
2500     dbgPrintf("secLastParamPtr going over boundary");
2501     fclose(grubConf);
2502     free(line);
2503     return 1;
2504     }
2505     dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2506     if (!strncmp(secLastParamPtr, "(hd", 3)) {
2507     secLastParamPtr += 3;
2508     dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
2509     installDeviceNumber = *secLastParamPtr;
2510     } else {
2511     installDeviceNumber = *lastParamPtr;
2512     }
2513    
2514     *devicePtr = malloc(6);
2515     snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
2516     dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
2517     fclose(grubConf);
2518     free(line);
2519     return 0;
2520     }
2521    
2522     free(line);
2523     fclose(grubConf);
2524     return 1;
2525     }
2526    
2527     int grubGetBootFromDeviceMap(const char * device,
2528     char ** bootPtr) {
2529     FILE * deviceMap;
2530     char * line = NULL;
2531     size_t res = 0, len = 0;
2532     char * devicePtr;
2533     char * bounds = NULL;
2534     const char * path;
2535     const static char default_path[] = "/boot/grub/device.map";
2536    
2537     if (!device) return 1;
2538     if (!bootPtr) return 1;
2539    
2540     if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2541     path = default_path;
2542    
2543     dbgPrintf("opening grub device.map file from: %s\n", path);
2544     deviceMap = fopen(path, "r");
2545     if (!deviceMap)
2546     return 1;
2547    
2548     while ((res = getline(&line, &len, deviceMap)) != -1) {
2549     if (!strncmp(line, "#", 1))
2550     continue;
2551    
2552     if (line[res - 1] == '\n')
2553     line[res - 1] = '\0';
2554     else if (len > res)
2555     line[res] = '\0';
2556     else {
2557     line = realloc(line, res + 1);
2558     line[res] = '\0';
2559     }
2560    
2561     devicePtr = line;
2562     bounds = line + res;
2563    
2564     while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2565     devicePtr++;
2566     dbgPrintf("device: %s\n", devicePtr);
2567    
2568     if (!strncmp(devicePtr, device, strlen(device))) {
2569     devicePtr += strlen(device);
2570     while (isspace(*devicePtr) && ((devicePtr + 1) <= bounds))
2571     devicePtr++;
2572    
2573     *bootPtr = strdup(devicePtr);
2574     break;
2575     }
2576     }
2577    
2578     free(line);
2579     fclose(deviceMap);
2580     return 0;
2581     }
2582    
2583     int suseGrubConfGetBoot(const char * path, char ** bootPtr) {
2584     char * grubDevice;
2585    
2586     if (suseGrubConfGetInstallDevice(path, &grubDevice))
2587     dbgPrintf("error looking for grub installation device\n");
2588     else
2589     dbgPrintf("grubby installation device: %s\n", grubDevice);
2590    
2591     if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
2592     dbgPrintf("error looking for grub boot device\n");
2593     else
2594     dbgPrintf("grubby boot device: %s\n", *bootPtr);
2595    
2596     free(grubDevice);
2597     return 0;
2598     }
2599    
2600     int parseSuseGrubConf(int * lbaPtr, char ** bootPtr) {
2601     /*
2602     * This SuSE grub configuration file at this location is not your average
2603     * grub configuration file, but instead the grub commands used to setup
2604     * grub on that system.
2605     */
2606     const char * path;
2607     const static char default_path[] = "/etc/grub.conf";
2608    
2609     if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
2610     path = default_path;
2611    
2612     if (!isSuseGrubConf(path)) return 1;
2613    
2614     if (lbaPtr) {
2615     *lbaPtr = 0;
2616     if (suseGrubConfGetLba(path, lbaPtr))
2617     return 1;
2618     }
2619    
2620     if (bootPtr) {
2621     *bootPtr = NULL;
2622     suseGrubConfGetBoot(path, bootPtr);
2623     }
2624    
2625     return 0;
2626     }
2627    
2628 niro 532 int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
2629     FILE * in;
2630     char buf[1024];
2631     char * chptr;
2632     char * start;
2633     char * param;
2634    
2635 niro 926 in = fopen("/etc/conf.d/grub", "r");
2636 niro 532 if (!in) return 1;
2637    
2638     if (lbaPtr) *lbaPtr = 0;
2639     if (bootPtr) *bootPtr = NULL;
2640    
2641     while (fgets(buf, sizeof(buf), in)) {
2642     start = buf;
2643     while (isspace(*start)) start++;
2644     if (*start == '#') continue;
2645    
2646     chptr = strchr(start, '=');
2647     if (!chptr) continue;
2648     chptr--;
2649     while (*chptr && isspace(*chptr)) chptr--;
2650     chptr++;
2651     *chptr = '\0';
2652    
2653     param = chptr + 1;
2654     while (*param && isspace(*param)) param++;
2655     if (*param == '=') {
2656     param++;
2657     while (*param && isspace(*param)) param++;
2658     }
2659    
2660     chptr = param;
2661     while (*chptr && !isspace(*chptr)) chptr++;
2662     *chptr = '\0';
2663    
2664     if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
2665     *lbaPtr = 1;
2666     else if (!strcmp(start, "boot") && bootPtr)
2667     *bootPtr = strdup(param);
2668     }
2669    
2670     fclose(in);
2671    
2672     return 0;
2673     }
2674    
2675     void dumpSysconfigGrub(void) {
2676 niro 1850 char * boot = NULL;
2677 niro 532 int lba;
2678    
2679 niro 1851 if (isSuseSystem()) {
2680     if (parseSuseGrubConf(&lba, &boot)) {
2681 niro 1850 free(boot);
2682     return;
2683     }
2684     } else {
2685 niro 1851 if (parseSysconfigGrub(&lba, &boot)) {
2686 niro 1850 free(boot);
2687     return;
2688     }
2689 niro 532 }
2690 niro 1850
2691     if (lba) printf("lba\n");
2692     if (boot) {
2693     printf("boot=%s\n", boot);
2694     free(boot);
2695     }
2696 niro 532 }
2697    
2698     int displayInfo(struct grubConfig * config, char * kernel,
2699     const char * prefix) {
2700     int i = 0;
2701     struct singleEntry * entry;
2702     struct singleLine * line;
2703    
2704     entry = findEntryByPath(config, kernel, prefix, &i);
2705     if (!entry) {
2706     fprintf(stderr, _("grubby: kernel not found\n"));
2707     return 1;
2708     }
2709    
2710 niro 926 /* this is a horrible hack to support /etc/conf.d/grub; there must
2711 niro 532 be a better way */
2712     if (config->cfi == &grubConfigType) {
2713     dumpSysconfigGrub();
2714     } else {
2715 niro 914 line = getLineByType(LT_BOOT, config->theLines);
2716 niro 532 if (line && line->numElements >= 1) {
2717     printf("boot=%s\n", line->elements[1].item);
2718     }
2719    
2720 niro 914 line = getLineByType(LT_LBA, config->theLines);
2721 niro 532 if (line) printf("lba\n");
2722     }
2723    
2724     displayEntry(entry, prefix, i);
2725    
2726     i++;
2727     while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
2728     displayEntry(entry, prefix, i);
2729     i++;
2730     }
2731    
2732     return 0;
2733     }
2734    
2735 niro 914 struct singleLine * addLineTmpl(struct singleEntry * entry,
2736     struct singleLine * tmplLine,
2737     struct singleLine * prevLine,
2738     const char * val,
2739     struct configFileInfo * cfi)
2740     {
2741     struct singleLine * newLine = lineDup(tmplLine);
2742    
2743 niro 1940 if (isEfi && cfi == &grub2ConfigType) {
2744     enum lineType_e old = newLine->type;
2745     newLine->type = preferredLineType(newLine->type, cfi);
2746     if (old != newLine->type)
2747     newLine->elements[0].item = getKeyByType(newLine->type, cfi);
2748     }
2749    
2750 niro 914 if (val) {
2751     /* override the inherited value with our own.
2752     * This is a little weak because it only applies to elements[1]
2753     */
2754     if (newLine->numElements > 1)
2755     removeElement(newLine, 1);
2756     insertElement(newLine, val, 1, cfi);
2757    
2758     /* but try to keep the rootspec from the template... sigh */
2759 niro 2683 if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD|LT_KERNEL_EFI|LT_INITRD_EFI|LT_KERNEL_16|LT_INITRD_16)) {
2760 niro 914 char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
2761     if (rootspec != NULL) {
2762     free(newLine->elements[1].item);
2763     newLine->elements[1].item =
2764     sdupprintf("%s%s", rootspec, val);
2765     }
2766     }
2767     }
2768    
2769     dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
2770     newLine->elements[0].item : "");
2771    
2772     if (!entry->lines) {
2773     /* first one on the list */
2774     entry->lines = newLine;
2775     } else if (prevLine) {
2776     /* add after prevLine */
2777     newLine->next = prevLine->next;
2778     prevLine->next = newLine;
2779     }
2780    
2781     return newLine;
2782     }
2783    
2784 niro 532 /* val may be NULL */
2785     struct singleLine * addLine(struct singleEntry * entry,
2786     struct configFileInfo * cfi,
2787 niro 914 enum lineType_e type, char * defaultIndent,
2788     const char * val) {
2789 niro 532 struct singleLine * line, * prev;
2790 niro 914 struct keywordTypes * kw;
2791     struct singleLine tmpl;
2792 niro 532
2793 niro 914 /* NB: This function shouldn't allocate items on the heap, rather on the
2794     * stack since it calls addLineTmpl which will make copies.
2795     */
2796     if (type == LT_TITLE && cfi->titleBracketed) {
2797     /* we're doing a bracketed title (zipl) */
2798     tmpl.type = type;
2799     tmpl.numElements = 1;
2800     tmpl.elements = alloca(sizeof(*tmpl.elements));
2801     tmpl.elements[0].item = alloca(strlen(val)+3);
2802     sprintf(tmpl.elements[0].item, "[%s]", val);
2803     tmpl.elements[0].indent = "";
2804     val = NULL;
2805 niro 1696 } else if (type == LT_MENUENTRY) {
2806     char *lineend = "--class gnu-linux --class gnu --class os {";
2807     if (!val) {
2808     fprintf(stderr, "Line type LT_MENUENTRY requires a value\n");
2809     abort();
2810     }
2811     kw = getKeywordByType(type, cfi);
2812     if (!kw) {
2813     fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2814     abort();
2815     }
2816     tmpl.indent = "";
2817     tmpl.type = type;
2818     tmpl.numElements = 3;
2819     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2820     tmpl.elements[0].item = kw->key;
2821     tmpl.elements[0].indent = alloca(2);
2822     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2823     tmpl.elements[1].item = (char *)val;
2824     tmpl.elements[1].indent = alloca(2);
2825     sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
2826     tmpl.elements[2].item = alloca(strlen(lineend)+1);
2827     strcpy(tmpl.elements[2].item, lineend);
2828     tmpl.elements[2].indent = "";
2829 niro 914 } else {
2830     kw = getKeywordByType(type, cfi);
2831 niro 1696 if (!kw) {
2832     fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2833     abort();
2834     }
2835 niro 914 tmpl.type = type;
2836     tmpl.numElements = val ? 2 : 1;
2837     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2838     tmpl.elements[0].item = kw->key;
2839     tmpl.elements[0].indent = alloca(2);
2840     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2841     if (val) {
2842     tmpl.elements[1].item = (char *)val;
2843     tmpl.elements[1].indent = "";
2844     }
2845     }
2846    
2847 niro 532 /* The last non-empty line gives us the indention to us and the line
2848     to insert after. Note that comments are considered empty lines, which
2849     may not be ideal? If there are no lines or we are looking at the
2850     first line, we use defaultIndent (the first line is normally indented
2851     differently from the rest) */
2852 niro 914 for (line = entry->lines, prev = NULL; line; line = line->next) {
2853     if (line->numElements) prev = line;
2854     /* fall back on the last line if prev isn't otherwise set */
2855     if (!line->next && !prev) prev = line;
2856 niro 532 }
2857    
2858 niro 1696 struct singleLine *menuEntry;
2859     menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
2860     if (tmpl.type == LT_ENTRY_END) {
2861     if (menuEntry)
2862     tmpl.indent = menuEntry->indent;
2863     else
2864     tmpl.indent = defaultIndent ?: "";
2865     } else if (tmpl.type != LT_MENUENTRY) {
2866     if (menuEntry)
2867     tmpl.indent = "\t";
2868     else if (prev == entry->lines)
2869     tmpl.indent = defaultIndent ?: "";
2870     else
2871     tmpl.indent = prev->indent;
2872     }
2873 niro 532
2874 niro 914 return addLineTmpl(entry, &tmpl, prev, val, cfi);
2875 niro 532 }
2876    
2877     void removeLine(struct singleEntry * entry, struct singleLine * line) {
2878     struct singleLine * prev;
2879     int i;
2880    
2881     for (i = 0; i < line->numElements; i++) {
2882     free(line->elements[i].item);
2883     free(line->elements[i].indent);
2884     }
2885     free(line->elements);
2886     free(line->indent);
2887    
2888     if (line == entry->lines) {
2889     entry->lines = line->next;
2890     } else {
2891     prev = entry->lines;
2892     while (prev->next != line) prev = prev->next;
2893     prev->next = line->next;
2894     }
2895    
2896     free(line);
2897     }
2898    
2899 niro 1696 static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi)
2900     {
2901     struct singleLine newLine = {
2902     .indent = tmplLine->indent,
2903     .type = tmplLine->type,
2904     .next = tmplLine->next,
2905     };
2906     int firstQuotedItem = -1;
2907     int quoteLen = 0;
2908     int j;
2909     int element = 0;
2910     char *c;
2911    
2912     c = malloc(strlen(tmplLine->elements[0].item) + 1);
2913     strcpy(c, tmplLine->elements[0].item);
2914     insertElement(&newLine, c, element++, cfi);
2915     free(c);
2916     c = NULL;
2917    
2918     for (j = 1; j < tmplLine->numElements; j++) {
2919     if (firstQuotedItem == -1) {
2920     quoteLen += strlen(tmplLine->elements[j].item);
2921    
2922     if (isquote(tmplLine->elements[j].item[0])) {
2923     firstQuotedItem = j;
2924     quoteLen += strlen(tmplLine->elements[j].indent);
2925     } else {
2926     c = malloc(quoteLen + 1);
2927     strcpy(c, tmplLine->elements[j].item);
2928     insertElement(&newLine, c, element++, cfi);
2929     free(c);
2930     quoteLen = 0;
2931     }
2932     } else {
2933     int itemlen = strlen(tmplLine->elements[j].item);
2934     quoteLen += itemlen;
2935     quoteLen += strlen(tmplLine->elements[j].indent);
2936    
2937     if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
2938     c = malloc(quoteLen + 1);
2939     c[0] = '\0';
2940     for (int i = firstQuotedItem; i < j+1; i++) {
2941     strcat(c, tmplLine->elements[i].item);
2942     strcat(c, tmplLine->elements[i].indent);
2943     }
2944     insertElement(&newLine, c, element++, cfi);
2945     free(c);
2946    
2947     firstQuotedItem = -1;
2948     quoteLen = 0;
2949     }
2950     }
2951     }
2952     while (tmplLine->numElements)
2953     removeElement(tmplLine, 0);
2954     if (tmplLine->elements)
2955     free(tmplLine->elements);
2956    
2957     tmplLine->numElements = newLine.numElements;
2958     tmplLine->elements = newLine.elements;
2959     }
2960    
2961 niro 914 static void insertElement(struct singleLine * line,
2962     const char * item, int insertHere,
2963     struct configFileInfo * cfi)
2964     {
2965     struct keywordTypes * kw;
2966     char indent[2] = "";
2967    
2968     /* sanity check */
2969     if (insertHere > line->numElements) {
2970     dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",
2971     insertHere, line->numElements);
2972     insertHere = line->numElements;
2973     }
2974    
2975     line->elements = realloc(line->elements, (line->numElements + 1) *
2976     sizeof(*line->elements));
2977     memmove(&line->elements[insertHere+1],
2978     &line->elements[insertHere],
2979     (line->numElements - insertHere) *
2980     sizeof(*line->elements));
2981     line->elements[insertHere].item = strdup(item);
2982    
2983     kw = getKeywordByType(line->type, cfi);
2984    
2985     if (line->numElements == 0) {
2986     indent[0] = '\0';
2987     } else if (insertHere == 0) {
2988     indent[0] = kw->nextChar;
2989     } else if (kw->separatorChar != '\0') {
2990     indent[0] = kw->separatorChar;
2991     } else {
2992     indent[0] = ' ';
2993     }
2994    
2995     if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {
2996     /* move the end-of-line forward */
2997     line->elements[insertHere].indent =
2998     line->elements[insertHere-1].indent;
2999     line->elements[insertHere-1].indent = strdup(indent);
3000     } else {
3001     line->elements[insertHere].indent = strdup(indent);
3002     }
3003    
3004     line->numElements++;
3005    
3006     dbgPrintf("insertElement(%s, '%s%s', %d)\n",
3007     line->elements[0].item,
3008     line->elements[insertHere].item,
3009     line->elements[insertHere].indent,
3010     insertHere);
3011     }
3012    
3013     static void removeElement(struct singleLine * line, int removeHere) {
3014     int i;
3015    
3016     /* sanity check */
3017     if (removeHere >= line->numElements) return;
3018    
3019     dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
3020     removeHere, line->elements[removeHere].item);
3021    
3022     free(line->elements[removeHere].item);
3023    
3024     if (removeHere > 1) {
3025     /* previous argument gets this argument's post-indentation */
3026     free(line->elements[removeHere-1].indent);
3027     line->elements[removeHere-1].indent =
3028     line->elements[removeHere].indent;
3029     } else {
3030     free(line->elements[removeHere].indent);
3031     }
3032    
3033     /* now collapse the array, but don't bother to realloc smaller */
3034     for (i = removeHere; i < line->numElements - 1; i++)
3035     line->elements[i] = line->elements[i + 1];
3036    
3037     line->numElements--;
3038     }
3039    
3040 niro 532 int argMatch(const char * one, const char * two) {
3041     char * first, * second;
3042     char * chptr;
3043    
3044     first = strcpy(alloca(strlen(one) + 1), one);
3045     second = strcpy(alloca(strlen(two) + 1), two);
3046    
3047     chptr = strchr(first, '=');
3048     if (chptr) *chptr = '\0';
3049    
3050     chptr = strchr(second, '=');
3051     if (chptr) *chptr = '\0';
3052    
3053     return strcmp(first, second);
3054     }
3055    
3056     int updateActualImage(struct grubConfig * cfg, const char * image,
3057     const char * prefix, const char * addArgs,
3058     const char * removeArgs, int multibootArgs) {
3059     struct singleEntry * entry;
3060     struct singleLine * line, * rootLine;
3061     int index = 0;
3062 niro 914 int i, k;
3063 niro 532 const char ** newArgs, ** oldArgs;
3064     const char ** arg;
3065 niro 914 int useKernelArgs, useRoot;
3066 niro 532 int firstElement;
3067 niro 1304 int *usedElements;
3068 niro 914 int doreplace;
3069 niro 532
3070     if (!image) return 0;
3071    
3072     if (!addArgs) {
3073     newArgs = malloc(sizeof(*newArgs));
3074     *newArgs = NULL;
3075     } else {
3076     if (poptParseArgvString(addArgs, NULL, &newArgs)) {
3077     fprintf(stderr,
3078     _("grubby: error separating arguments '%s'\n"), addArgs);
3079     return 1;
3080     }
3081     }
3082    
3083     if (!removeArgs) {
3084     oldArgs = malloc(sizeof(*oldArgs));
3085     *oldArgs = NULL;
3086     } else {
3087     if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
3088     fprintf(stderr,
3089     _("grubby: error separating arguments '%s'\n"), removeArgs);
3090     free(newArgs);
3091     return 1;
3092     }
3093     }
3094    
3095    
3096 niro 914 useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
3097     && (!multibootArgs || cfg->cfi->mbConcatArgs));
3098 niro 532
3099 niro 914 useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
3100     && !multibootArgs);
3101 niro 532
3102 niro 914 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3103 niro 532
3104 niro 914 if (multibootArgs && !entry->multiboot)
3105     continue;
3106 niro 532
3107 niro 914 /* Determine where to put the args. If this config supports
3108     * LT_KERNELARGS, use that. Otherwise use
3109     * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
3110     */
3111     if (useKernelArgs) {
3112     line = getLineByType(LT_KERNELARGS, entry->lines);
3113     if (!line) {
3114     /* no LT_KERNELARGS, need to add it */
3115     line = addLine(entry, cfg->cfi, LT_KERNELARGS,
3116     cfg->secondaryIndent, NULL);
3117     }
3118     firstElement = 1;
3119 niro 532
3120 niro 914 } else if (multibootArgs) {
3121     line = getLineByType(LT_HYPER, entry->lines);
3122     if (!line) {
3123     /* a multiboot entry without LT_HYPER? */
3124     continue;
3125     }
3126     firstElement = 2;
3127    
3128     } else {
3129 niro 2683 line = getLineByType(LT_KERNEL|LT_MBMODULE|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3130 niro 914 if (!line) {
3131     /* no LT_KERNEL or LT_MBMODULE in this entry? */
3132     continue;
3133     }
3134     firstElement = 2;
3135 niro 532 }
3136    
3137 niro 914 /* handle the elilo case which does:
3138     * append="hypervisor args -- kernel args"
3139     */
3140     if (entry->multiboot && cfg->cfi->mbConcatArgs) {
3141     /* this is a multiboot entry, make sure there's
3142     * -- on the args line
3143     */
3144     for (i = firstElement; i < line->numElements; i++) {
3145     if (!strcmp(line->elements[i].item, "--"))
3146     break;
3147     }
3148     if (i == line->numElements) {
3149     /* assume all existing args are kernel args,
3150     * prepend -- to make it official
3151     */
3152     insertElement(line, "--", firstElement, cfg->cfi);
3153     i = firstElement;
3154     }
3155     if (!multibootArgs) {
3156     /* kernel args start after the -- */
3157     firstElement = i + 1;
3158     }
3159     } else if (cfg->cfi->mbConcatArgs) {
3160     /* this is a non-multiboot entry, remove hyper args */
3161     for (i = firstElement; i < line->numElements; i++) {
3162     if (!strcmp(line->elements[i].item, "--"))
3163     break;
3164     }
3165     if (i < line->numElements) {
3166     /* remove args up to -- */
3167     while (strcmp(line->elements[firstElement].item, "--"))
3168     removeElement(line, firstElement);
3169     /* remove -- */
3170     removeElement(line, firstElement);
3171     }
3172 niro 532 }
3173    
3174 niro 914 usedElements = calloc(line->numElements, sizeof(*usedElements));
3175 niro 532
3176 niro 914 for (k = 0, arg = newArgs; *arg; arg++, k++) {
3177    
3178     doreplace = 1;
3179 niro 532 for (i = firstElement; i < line->numElements; i++) {
3180 niro 914 if (multibootArgs && cfg->cfi->mbConcatArgs &&
3181     !strcmp(line->elements[i].item, "--"))
3182     {
3183     /* reached the end of hyper args, insert here */
3184     doreplace = 0;
3185     break;
3186     }
3187 niro 532 if (usedElements[i])
3188     continue;
3189     if (!argMatch(line->elements[i].item, *arg)) {
3190     usedElements[i]=1;
3191     break;
3192     }
3193     }
3194    
3195 niro 914 if (i < line->numElements && doreplace) {
3196     /* direct replacement */
3197 niro 532 free(line->elements[i].item);
3198     line->elements[i].item = strdup(*arg);
3199    
3200 niro 914 } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
3201     /* root= replacement */
3202     rootLine = getLineByType(LT_ROOT, entry->lines);
3203     if (rootLine) {
3204     free(rootLine->elements[1].item);
3205     rootLine->elements[1].item = strdup(*arg + 5);
3206 niro 532 } else {
3207 niro 914 rootLine = addLine(entry, cfg->cfi, LT_ROOT,
3208     cfg->secondaryIndent, *arg + 5);
3209 niro 532 }
3210 niro 914 }
3211 niro 532
3212 niro 914 else {
3213     /* insert/append */
3214     insertElement(line, *arg, i, cfg->cfi);
3215     usedElements = realloc(usedElements, line->numElements *
3216     sizeof(*usedElements));
3217     memmove(&usedElements[i + 1], &usedElements[i],
3218     line->numElements - i - 1);
3219     usedElements[i] = 1;
3220 niro 532
3221     /* if we updated a root= here even though there is a
3222     LT_ROOT available we need to remove the LT_ROOT entry
3223     (this will happen if we switch from a device to a label) */
3224     if (useRoot && !strncmp(*arg, "root=", 5)) {
3225 niro 914 rootLine = getLineByType(LT_ROOT, entry->lines);
3226     if (rootLine)
3227 niro 532 removeLine(entry, rootLine);
3228     }
3229     }
3230     }
3231    
3232     free(usedElements);
3233    
3234     for (arg = oldArgs; *arg; arg++) {
3235 niro 914 for (i = firstElement; i < line->numElements; i++) {
3236     if (multibootArgs && cfg->cfi->mbConcatArgs &&
3237     !strcmp(line->elements[i].item, "--"))
3238     /* reached the end of hyper args, stop here */
3239 niro 532 break;
3240 niro 914 if (!argMatch(line->elements[i].item, *arg)) {
3241     removeElement(line, i);
3242     break;
3243 niro 532 }
3244     }
3245 niro 914 /* handle removing LT_ROOT line too */
3246     if (useRoot && !strncmp(*arg, "root=", 5)) {
3247     rootLine = getLineByType(LT_ROOT, entry->lines);
3248     if (rootLine)
3249     removeLine(entry, rootLine);
3250     }
3251 niro 532 }
3252    
3253     if (line->numElements == 1) {
3254     /* don't need the line at all (note it has to be a
3255     LT_KERNELARGS for this to happen */
3256     removeLine(entry, line);
3257     }
3258     }
3259    
3260     free(newArgs);
3261     free(oldArgs);
3262    
3263     return 0;
3264     }
3265    
3266     int updateImage(struct grubConfig * cfg, const char * image,
3267     const char * prefix, const char * addArgs,
3268     const char * removeArgs,
3269     const char * addMBArgs, const char * removeMBArgs) {
3270     int rc = 0;
3271    
3272     if (!image) return rc;
3273    
3274     /* update the main args first... */
3275     if (addArgs || removeArgs)
3276     rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs, 0);
3277     if (rc) return rc;
3278    
3279     /* and now any multiboot args */
3280     if (addMBArgs || removeMBArgs)
3281     rc = updateActualImage(cfg, image, prefix, addMBArgs, removeMBArgs, 1);
3282     return rc;
3283     }
3284    
3285 niro 2263 int addMBInitrd(struct grubConfig * cfg, const char *newMBKernel,
3286     const char * image, const char * prefix, const char * initrd) {
3287     struct singleEntry * entry;
3288     struct singleLine * line, * kernelLine, *endLine = NULL;
3289     int index = 0;
3290    
3291     if (!image) return 0;
3292    
3293     for (; (entry = findEntryByPath(cfg, newMBKernel, prefix, &index)); index++) {
3294     kernelLine = getLineByType(LT_MBMODULE, entry->lines);
3295     if (!kernelLine) continue;
3296    
3297     if (prefix) {
3298     int prefixLen = strlen(prefix);
3299     if (!strncmp(initrd, prefix, prefixLen))
3300     initrd += prefixLen;
3301     }
3302     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3303     if (endLine)
3304     removeLine(entry, endLine);
3305     line = addLine(entry, cfg->cfi, preferredLineType(LT_MBMODULE,cfg->cfi),
3306     kernelLine->indent, initrd);
3307     if (!line)
3308     return 1;
3309     if (endLine) {
3310     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3311     if (!line)
3312     return 1;
3313     }
3314    
3315     break;
3316     }
3317    
3318     return 0;
3319     }
3320    
3321 niro 1156 int updateInitrd(struct grubConfig * cfg, const char * image,
3322     const char * prefix, const char * initrd) {
3323     struct singleEntry * entry;
3324 niro 1696 struct singleLine * line, * kernelLine, *endLine = NULL;
3325 niro 1156 int index = 0;
3326    
3327     if (!image) return 0;
3328    
3329     for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3330 niro 2683 kernelLine = getLineByType(LT_KERNEL|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3331 niro 1156 if (!kernelLine) continue;
3332    
3333 niro 2683 line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
3334 niro 1156 if (line)
3335     removeLine(entry, line);
3336     if (prefix) {
3337     int prefixLen = strlen(prefix);
3338     if (!strncmp(initrd, prefix, prefixLen))
3339     initrd += prefixLen;
3340     }
3341 niro 1696 endLine = getLineByType(LT_ENTRY_END, entry->lines);
3342     if (endLine)
3343     removeLine(entry, endLine);
3344 niro 2683 enum lineType_e lt;
3345     switch(kernelLine->type) {
3346     case LT_KERNEL:
3347     lt = LT_INITRD;
3348     break;
3349     case LT_KERNEL_EFI:
3350     lt = LT_INITRD_EFI;
3351     break;
3352     case LT_KERNEL_16:
3353     lt = LT_INITRD_16;
3354     break;
3355     default:
3356     lt = preferredLineType(LT_INITRD, cfg->cfi);
3357     }
3358     line = addLine(entry, cfg->cfi, lt, kernelLine->indent, initrd);
3359 niro 1696 if (!line)
3360     return 1;
3361     if (endLine) {
3362     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3363     if (!line)
3364     return 1;
3365     }
3366    
3367 niro 1156 break;
3368     }
3369    
3370     return 0;
3371     }
3372    
3373 niro 532 int checkDeviceBootloader(const char * device, const unsigned char * boot) {
3374     int fd;
3375     unsigned char bootSect[512];
3376     int offset;
3377    
3378     fd = open(device, O_RDONLY);
3379     if (fd < 0) {
3380     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3381     device, strerror(errno));
3382     return 1;
3383     }
3384    
3385     if (read(fd, bootSect, 512) != 512) {
3386     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3387     device, strerror(errno));
3388     return 1;
3389     }
3390     close(fd);
3391    
3392     /* first three bytes should match, a jmp short should be in there */
3393     if (memcmp(boot, bootSect, 3))
3394     return 0;
3395    
3396 niro 1718 if (boot[1] == JMP_SHORT_OPCODE) {
3397 niro 532 offset = boot[2] + 2;
3398     } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3399     offset = (boot[3] << 8) + boot[2] + 2;
3400 niro 1718 } else if (boot[0] == JMP_SHORT_OPCODE) {
3401     offset = boot[1] + 2;
3402     /*
3403     * it looks like grub, when copying stage1 into the mbr, patches stage1
3404     * right after the JMP location, replacing other instructions such as
3405     * JMPs for NOOPs. So, relax the check a little bit by skipping those
3406     * different bytes.
3407     */
3408     if ((bootSect[offset + 1] == NOOP_OPCODE)
3409     && (bootSect[offset + 2] == NOOP_OPCODE)) {
3410     offset = offset + 3;
3411     }
3412 niro 532 } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3413     offset = (boot[2] << 8) + boot[1] + 2;
3414     } else {
3415     return 0;
3416     }
3417    
3418     if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
3419     return 0;
3420    
3421     return 2;
3422     }
3423    
3424     int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {
3425     int fd;
3426     char buf[65536];
3427     char * end;
3428     char * chptr;
3429     char * chptr2;
3430     int rc;
3431    
3432     /* it's on raid; we need to parse /proc/mdstat and check all of the
3433     *raw* devices listed in there */
3434    
3435     if (!strncmp(mdDev, "/dev/", 5))
3436     mdDev += 5;
3437    
3438     if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
3439     fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
3440     strerror(errno));
3441     return 2;
3442     }
3443    
3444     rc = read(fd, buf, sizeof(buf) - 1);
3445     if (rc < 0 || rc == (sizeof(buf) - 1)) {
3446     fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
3447     strerror(errno));
3448     close(fd);
3449     return 2;
3450     }
3451     close(fd);
3452     buf[rc] = '\0';
3453    
3454     chptr = buf;
3455     while (*chptr) {
3456     end = strchr(chptr, '\n');
3457     if (!end) break;
3458     *end = '\0';
3459    
3460     if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
3461     chptr[strlen(mdDev)] == ' ') {
3462    
3463     /* found the device */
3464     while (*chptr && *chptr != ':') chptr++;
3465     chptr++;
3466     while (*chptr && isspace(*chptr)) chptr++;
3467    
3468     /* skip the "active" bit */
3469     while (*chptr && !isspace(*chptr)) chptr++;
3470     while (*chptr && isspace(*chptr)) chptr++;
3471    
3472     /* skip the raid level */
3473     while (*chptr && !isspace(*chptr)) chptr++;
3474     while (*chptr && isspace(*chptr)) chptr++;
3475    
3476     /* everything else is partition stuff */
3477     while (*chptr) {
3478     chptr2 = chptr;
3479     while (*chptr2 && *chptr2 != '[') chptr2++;
3480     if (!*chptr2) break;
3481    
3482     /* yank off the numbers at the end */
3483     chptr2--;
3484     while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;
3485     chptr2++;
3486     *chptr2 = '\0';
3487    
3488     /* Better, now we need the /dev/ back. We're done with
3489     * everything before this point, so we can just put
3490     * the /dev/ part there. There will always be room. */
3491     memcpy(chptr - 5, "/dev/", 5);
3492     rc = checkDeviceBootloader(chptr - 5, boot);
3493     if (rc != 2) {
3494     return rc;
3495     }
3496    
3497     chptr = chptr2 + 1;
3498     /* skip the [11] bit */
3499     while (*chptr && !isspace(*chptr)) chptr++;
3500     /* and move to the next one */
3501     while (*chptr && isspace(*chptr)) chptr++;
3502     }
3503    
3504     /* we're good to go */
3505     return 2;
3506     }
3507    
3508     chptr = end + 1;
3509     }
3510    
3511     fprintf(stderr,
3512     _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
3513     mdDev);
3514     return 0;
3515     }
3516    
3517     int checkForLilo(struct grubConfig * config) {
3518     int fd;
3519     unsigned char boot[512];
3520     struct singleLine * line;
3521    
3522     for (line = config->theLines; line; line = line->next)
3523     if (line->type == LT_BOOT) break;
3524    
3525     if (!line) {
3526     fprintf(stderr,
3527     _("grubby: no boot line found in lilo configuration\n"));
3528     return 1;
3529     }
3530    
3531     if (line->numElements != 2) return 1;
3532    
3533     fd = open("/boot/boot.b", O_RDONLY);
3534     if (fd < 0) {
3535     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3536     "/boot/boot.b", strerror(errno));
3537     return 1;
3538     }
3539    
3540     if (read(fd, boot, 512) != 512) {
3541     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3542     "/boot/boot.b", strerror(errno));
3543     return 1;
3544     }
3545     close(fd);
3546    
3547     if (!strncmp("/dev/md", line->elements[1].item, 7))
3548     return checkLiloOnRaid(line->elements[1].item, boot);
3549    
3550     return checkDeviceBootloader(line->elements[1].item, boot);
3551     }
3552    
3553 niro 1696 int checkForGrub2(struct grubConfig * config) {
3554 niro 1714 if (!access("/etc/grub.d/", R_OK))
3555 niro 1696 return 2;
3556    
3557     return 1;
3558     }
3559    
3560 niro 532 int checkForGrub(struct grubConfig * config) {
3561     int fd;
3562     unsigned char bootSect[512];
3563     char * boot;
3564 niro 1851 int onSuse = isSuseSystem();
3565 niro 532
3566 niro 1851
3567     if (onSuse) {
3568     if (parseSuseGrubConf(NULL, &boot))
3569     return 0;
3570 niro 1850 } else {
3571 niro 1851 if (parseSysconfigGrub(NULL, &boot))
3572     return 0;
3573 niro 1850 }
3574 niro 532
3575     /* assume grub is not installed -- not an error condition */
3576     if (!boot)
3577     return 0;
3578    
3579     fd = open("/boot/grub/stage1", O_RDONLY);
3580     if (fd < 0)
3581     /* this doesn't exist if grub hasn't been installed */
3582     return 0;
3583    
3584     if (read(fd, bootSect, 512) != 512) {
3585     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3586     "/boot/grub/stage1", strerror(errno));
3587 niro 914 close(fd);
3588 niro 532 return 1;
3589     }
3590     close(fd);
3591    
3592 niro 1851 /* The more elaborate checks do not work on SuSE. The checks done
3593     * seem to be reasonble (at least for now), so just return success
3594     */
3595     if (onSuse)
3596 niro 1850 return 2;
3597 niro 1851
3598     return checkDeviceBootloader(boot, bootSect);
3599 niro 532 }
3600    
3601 niro 914 int checkForExtLinux(struct grubConfig * config) {
3602     int fd;
3603     unsigned char bootSect[512];
3604     char * boot;
3605     char executable[] = "/boot/extlinux/extlinux";
3606    
3607     printf("entered: checkForExtLinux()\n");
3608    
3609     if (parseSysconfigGrub(NULL, &boot))
3610     return 0;
3611    
3612     /* assume grub is not installed -- not an error condition */
3613     if (!boot)
3614     return 0;
3615    
3616     fd = open(executable, O_RDONLY);
3617     if (fd < 0)
3618     /* this doesn't exist if grub hasn't been installed */
3619     return 0;
3620    
3621     if (read(fd, bootSect, 512) != 512) {
3622     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3623     executable, strerror(errno));
3624     return 1;
3625     }
3626     close(fd);
3627    
3628     return checkDeviceBootloader(boot, bootSect);
3629     }
3630    
3631 niro 1853 int checkForYaboot(struct grubConfig * config) {
3632     /*
3633     * This is a simplistic check that we consider good enough for own puporses
3634     *
3635     * If we were to properly check if yaboot is *installed* we'd need to:
3636     * 1) get the system boot device (LT_BOOT)
3637     * 2) considering it's a raw filesystem, check if the yaboot binary matches
3638     * the content on the boot device
3639     * 3) if not, copy the binary to a temporary file and run "addnote" on it
3640     * 4) check again if binary and boot device contents match
3641     */
3642     if (!access("/etc/yaboot.conf", R_OK))
3643     return 2;
3644    
3645     return 1;
3646     }
3647    
3648 niro 1854 int checkForElilo(struct grubConfig * config) {
3649     if (!access("/etc/elilo.conf", R_OK))
3650     return 2;
3651    
3652     return 1;
3653     }
3654    
3655 niro 532 static char * getRootSpecifier(char * str) {
3656     char * idx, * rootspec = NULL;
3657    
3658     if (*str == '(') {
3659     idx = rootspec = strdup(str);
3660     while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;
3661     *(++idx) = '\0';
3662     }
3663     return rootspec;
3664     }
3665    
3666 niro 914 static char * getInitrdVal(struct grubConfig * config,
3667     const char * prefix, struct singleLine *tmplLine,
3668     const char * newKernelInitrd,
3669 niro 1844 const char ** extraInitrds, int extraInitrdCount)
3670 niro 914 {
3671     char *initrdVal, *end;
3672     int i;
3673     size_t totalSize;
3674     size_t prefixLen;
3675     char separatorChar;
3676    
3677     prefixLen = strlen(prefix);
3678     totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
3679    
3680     for (i = 0; i < extraInitrdCount; i++) {
3681     totalSize += sizeof(separatorChar);
3682     totalSize += strlen(extraInitrds[i]) - prefixLen;
3683     }
3684    
3685     initrdVal = end = malloc(totalSize);
3686    
3687     end = stpcpy (end, newKernelInitrd + prefixLen);
3688    
3689     separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
3690     for (i = 0; i < extraInitrdCount; i++) {
3691     const char *extraInitrd;
3692     int j;
3693    
3694     extraInitrd = extraInitrds[i] + prefixLen;
3695     /* Don't add entries that are already there */
3696     if (tmplLine != NULL) {
3697     for (j = 2; j < tmplLine->numElements; j++)
3698     if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
3699     break;
3700    
3701     if (j != tmplLine->numElements)
3702     continue;
3703     }
3704    
3705     *end++ = separatorChar;
3706     end = stpcpy(end, extraInitrd);
3707     }
3708    
3709     return initrdVal;
3710     }
3711    
3712 niro 532 int addNewKernel(struct grubConfig * config, struct singleEntry * template,
3713     const char * prefix,
3714 niro 1844 const char * newKernelPath, const char * newKernelTitle,
3715     const char * newKernelArgs, const char * newKernelInitrd,
3716     const char ** extraInitrds, int extraInitrdCount,
3717 niro 2685 const char * newMBKernel, const char * newMBKernelArgs,
3718     const char * newDevTreePath) {
3719 niro 532 struct singleEntry * new;
3720 niro 914 struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
3721 niro 532 int needs;
3722     char * chptr;
3723    
3724     if (!newKernelPath) return 0;
3725    
3726     /* if the newKernelTitle is too long silently munge it into something
3727     * we can live with. truncating is first check, then we'll just mess with
3728     * it until it looks better */
3729     if (config->cfi->maxTitleLength &&
3730     (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
3731     char * buf = alloca(config->cfi->maxTitleLength + 7);
3732     char * numBuf = alloca(config->cfi->maxTitleLength + 1);
3733     int i = 1;
3734    
3735     sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);
3736     while (findEntryByPath(config, buf, NULL, NULL)) {
3737     sprintf(numBuf, "%d", i++);
3738     strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
3739     }
3740    
3741     newKernelTitle = buf + 6;
3742     }
3743    
3744     new = malloc(sizeof(*new));
3745     new->skip = 0;
3746     new->multiboot = 0;
3747     new->next = config->entries;
3748     new->lines = NULL;
3749     config->entries = new;
3750    
3751     /* copy/update from the template */
3752 niro 914 needs = NEED_KERNEL | NEED_TITLE;
3753     if (newKernelInitrd)
3754     needs |= NEED_INITRD;
3755 niro 532 if (newMBKernel) {
3756 niro 914 needs |= NEED_MB;
3757 niro 532 new->multiboot = 1;
3758     }
3759 niro 2685 if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))
3760     needs |= NEED_DEVTREE;
3761 niro 532
3762     if (template) {
3763 niro 914 for (masterLine = template->lines;
3764     masterLine && (tmplLine = lineDup(masterLine));
3765     lineFree(tmplLine), masterLine = masterLine->next)
3766     {
3767     dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
3768 niro 532
3769     /* skip comments */
3770     chptr = tmplLine->indent;
3771     while (*chptr && isspace(*chptr)) chptr++;
3772     if (*chptr == '#') continue;
3773    
3774 niro 1940 if (iskernel(tmplLine->type) && tmplLine->numElements >= 2) {
3775 niro 914 if (!template->multiboot && (needs & NEED_MB)) {
3776     /* it's not a multiboot template and this is the kernel
3777     * line. Try to be intelligent about inserting the
3778     * hypervisor at the same time.
3779     */
3780     if (config->cfi->mbHyperFirst) {
3781     /* insert the hypervisor first */
3782     newLine = addLine(new, config->cfi, LT_HYPER,
3783     tmplLine->indent,
3784     newMBKernel + strlen(prefix));
3785     /* set up for adding the kernel line */
3786     free(tmplLine->indent);
3787     tmplLine->indent = strdup(config->secondaryIndent);
3788     needs &= ~NEED_MB;
3789     }
3790     if (needs & NEED_KERNEL) {
3791     /* use addLineTmpl to preserve line elements,
3792     * otherwise we could just call addLine. Unfortunately
3793     * this means making some changes to the template
3794     * such as the indent change above and the type
3795     * change below.
3796     */
3797     struct keywordTypes * mbm_kw =
3798     getKeywordByType(LT_MBMODULE, config->cfi);
3799     if (mbm_kw) {
3800     tmplLine->type = LT_MBMODULE;
3801     free(tmplLine->elements[0].item);
3802     tmplLine->elements[0].item = strdup(mbm_kw->key);
3803     }
3804     newLine = addLineTmpl(new, tmplLine, newLine,
3805     newKernelPath + strlen(prefix), config->cfi);
3806     needs &= ~NEED_KERNEL;
3807     }
3808     if (needs & NEED_MB) { /* !mbHyperFirst */
3809     newLine = addLine(new, config->cfi, LT_HYPER,
3810     config->secondaryIndent,
3811     newMBKernel + strlen(prefix));
3812     needs &= ~NEED_MB;
3813     }
3814     } else if (needs & NEED_KERNEL) {
3815     newLine = addLineTmpl(new, tmplLine, newLine,
3816     newKernelPath + strlen(prefix), config->cfi);
3817     needs &= ~NEED_KERNEL;
3818     }
3819 niro 532
3820 niro 914 } else if (tmplLine->type == LT_HYPER &&
3821     tmplLine->numElements >= 2) {
3822     if (needs & NEED_MB) {
3823     newLine = addLineTmpl(new, tmplLine, newLine,
3824     newMBKernel + strlen(prefix), config->cfi);
3825     needs &= ~NEED_MB;
3826     }
3827 niro 532
3828 niro 914 } else if (tmplLine->type == LT_MBMODULE &&
3829     tmplLine->numElements >= 2) {
3830     if (new->multiboot) {
3831     if (needs & NEED_KERNEL) {
3832     newLine = addLineTmpl(new, tmplLine, newLine,
3833     newKernelPath +
3834     strlen(prefix), config->cfi);
3835     needs &= ~NEED_KERNEL;
3836     } else if (config->cfi->mbInitRdIsModule &&
3837     (needs & NEED_INITRD)) {
3838     char *initrdVal;
3839     initrdVal = getInitrdVal(config, prefix, tmplLine,
3840     newKernelInitrd, extraInitrds,
3841     extraInitrdCount);
3842     newLine = addLineTmpl(new, tmplLine, newLine,
3843     initrdVal, config->cfi);
3844     free(initrdVal);
3845     needs &= ~NEED_INITRD;
3846     }
3847     } else if (needs & NEED_KERNEL) {
3848     /* template is multi but new is not,
3849     * insert the kernel in the first module slot
3850     */
3851 niro 1940 tmplLine->type = preferredLineType(LT_KERNEL, config->cfi);
3852 niro 914 free(tmplLine->elements[0].item);
3853     tmplLine->elements[0].item =
3854 niro 1940 strdup(getKeywordByType(tmplLine->type,
3855     config->cfi)->key);
3856 niro 914 newLine = addLineTmpl(new, tmplLine, newLine,
3857 niro 1940 newKernelPath + strlen(prefix),
3858     config->cfi);
3859 niro 914 needs &= ~NEED_KERNEL;
3860     } else if (needs & NEED_INITRD) {
3861     char *initrdVal;
3862     /* template is multi but new is not,
3863     * insert the initrd in the second module slot
3864     */
3865 niro 1940 tmplLine->type = preferredLineType(LT_INITRD, config->cfi);
3866 niro 914 free(tmplLine->elements[0].item);
3867     tmplLine->elements[0].item =
3868 niro 1940 strdup(getKeywordByType(tmplLine->type,
3869     config->cfi)->key);
3870 niro 914 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3871     newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3872     free(initrdVal);
3873     needs &= ~NEED_INITRD;
3874     }
3875 niro 532
3876 niro 1940 } else if (isinitrd(tmplLine->type) && tmplLine->numElements >= 2) {
3877 niro 914 if (needs & NEED_INITRD &&
3878     new->multiboot && !template->multiboot &&
3879     config->cfi->mbInitRdIsModule) {
3880     /* make sure we don't insert the module initrd
3881     * before the module kernel... if we don't do it here,
3882     * it will be inserted following the template.
3883     */
3884     if (!needs & NEED_KERNEL) {
3885     char *initrdVal;
3886    
3887     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3888     newLine = addLine(new, config->cfi, LT_MBMODULE,
3889     config->secondaryIndent,
3890     initrdVal);
3891     free(initrdVal);
3892     needs &= ~NEED_INITRD;
3893     }
3894     } else if (needs & NEED_INITRD) {
3895     char *initrdVal;
3896     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3897     newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3898     free(initrdVal);
3899     needs &= ~NEED_INITRD;
3900 niro 532 }
3901    
3902 niro 1696 } else if (tmplLine->type == LT_MENUENTRY &&
3903     (needs & NEED_TITLE)) {
3904     requote(tmplLine, config->cfi);
3905     char *nkt = malloc(strlen(newKernelTitle)+3);
3906     strcpy(nkt, "'");
3907     strcat(nkt, newKernelTitle);
3908     strcat(nkt, "'");
3909     newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
3910     free(nkt);
3911     needs &= ~NEED_TITLE;
3912 niro 532 } else if (tmplLine->type == LT_TITLE &&
3913 niro 914 (needs & NEED_TITLE)) {
3914     if (tmplLine->numElements >= 2) {
3915     newLine = addLineTmpl(new, tmplLine, newLine,
3916     newKernelTitle, config->cfi);
3917     needs &= ~NEED_TITLE;
3918     } else if (tmplLine->numElements == 1 &&
3919     config->cfi->titleBracketed) {
3920     /* addLineTmpl doesn't handle titleBracketed */
3921     newLine = addLine(new, config->cfi, LT_TITLE,
3922     tmplLine->indent, newKernelTitle);
3923     needs &= ~NEED_TITLE;
3924     }
3925 niro 1696 } else if (tmplLine->type == LT_ECHO) {
3926     requote(tmplLine, config->cfi);
3927 niro 1722 static const char *prefix = "'Loading ";
3928 niro 1696 if (tmplLine->numElements > 1 &&
3929 niro 1722 strstr(tmplLine->elements[1].item, prefix) &&
3930 niro 1940 masterLine->next &&
3931     iskernel(masterLine->next->type)) {
3932 niro 1696 char *newTitle = malloc(strlen(prefix) +
3933     strlen(newKernelTitle) + 2);
3934 niro 532
3935 niro 1696 strcpy(newTitle, prefix);
3936     strcat(newTitle, newKernelTitle);
3937     strcat(newTitle, "'");
3938     newLine = addLine(new, config->cfi, LT_ECHO,
3939     tmplLine->indent, newTitle);
3940     free(newTitle);
3941     } else {
3942     /* pass through other lines from the template */
3943     newLine = addLineTmpl(new, tmplLine, newLine, NULL,
3944     config->cfi);
3945     }
3946 niro 2685 } else if (tmplLine->type == LT_DEVTREE &&
3947     tmplLine->numElements == 2 && newDevTreePath) {
3948     newLine = addLineTmpl(new, tmplLine, newLine,
3949     newDevTreePath + strlen(prefix),
3950     config->cfi);
3951     needs &= ~NEED_DEVTREE;
3952     } else if (tmplLine->type == LT_ENTRY_END && needs & NEED_DEVTREE) {
3953     const char *ndtp = newDevTreePath;
3954     if (!strncmp(newDevTreePath, prefix, strlen(prefix)))
3955     ndtp += strlen(prefix);
3956     newLine = addLine(new, config->cfi, LT_DEVTREE,
3957     config->secondaryIndent,
3958     ndtp);
3959     needs &= ~NEED_DEVTREE;
3960     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
3961 niro 914 } else {
3962     /* pass through other lines from the template */
3963     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
3964     }
3965 niro 532 }
3966 niro 914
3967 niro 532 } else {
3968 niro 914 /* don't have a template, so start the entry with the
3969     * appropriate starting line
3970     */
3971 niro 1693 switch (config->cfi->entryStart) {
3972 niro 914 case LT_KERNEL:
3973 niro 1940 case LT_KERNEL_EFI:
3974 niro 2683 case LT_KERNEL_16:
3975 niro 914 if (new->multiboot && config->cfi->mbHyperFirst) {
3976     /* fall through to LT_HYPER */
3977     } else {
3978 niro 1940 newLine = addLine(new, config->cfi,
3979     preferredLineType(LT_KERNEL, config->cfi),
3980 niro 914 config->primaryIndent,
3981     newKernelPath + strlen(prefix));
3982     needs &= ~NEED_KERNEL;
3983     break;
3984     }
3985    
3986     case LT_HYPER:
3987     newLine = addLine(new, config->cfi, LT_HYPER,
3988     config->primaryIndent,
3989     newMBKernel + strlen(prefix));
3990     needs &= ~NEED_MB;
3991 niro 532 break;
3992    
3993 niro 1696 case LT_MENUENTRY: {
3994     char *nkt = malloc(strlen(newKernelTitle)+3);
3995     strcpy(nkt, "'");
3996     strcat(nkt, newKernelTitle);
3997     strcat(nkt, "'");
3998     newLine = addLine(new, config->cfi, LT_MENUENTRY,
3999     config->primaryIndent, nkt);
4000     free(nkt);
4001     needs &= ~NEED_TITLE;
4002     needs |= NEED_END;
4003     break;
4004     }
4005 niro 914 case LT_TITLE:
4006     if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
4007     char * templabel;
4008     int x = 0, y = 0;
4009    
4010     templabel = strdup(newKernelTitle);
4011     while( templabel[x]){
4012     if( templabel[x] == ' ' ){
4013     y = x;
4014     while( templabel[y] ){
4015     templabel[y] = templabel[y+1];
4016     y++;
4017     }
4018     }
4019     x++;
4020     }
4021     newLine = addLine(new, config->cfi, LT_TITLE,
4022     config->primaryIndent, templabel);
4023     free(templabel);
4024     }else{
4025     newLine = addLine(new, config->cfi, LT_TITLE,
4026     config->primaryIndent, newKernelTitle);
4027     }
4028     needs &= ~NEED_TITLE;
4029     break;
4030    
4031     default:
4032     abort();
4033 niro 532 }
4034     }
4035    
4036 niro 914 /* add the remainder of the lines, i.e. those that either
4037     * weren't present in the template, or in the case of no template,
4038 niro 1693 * all the lines following the entryStart.
4039 niro 914 */
4040     if (needs & NEED_TITLE) {
4041     newLine = addLine(new, config->cfi, LT_TITLE,
4042     config->secondaryIndent,
4043     newKernelTitle);
4044     needs &= ~NEED_TITLE;
4045 niro 532 }
4046 niro 914 if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
4047     newLine = addLine(new, config->cfi, LT_HYPER,
4048     config->secondaryIndent,
4049     newMBKernel + strlen(prefix));
4050     needs &= ~NEED_MB;
4051     }
4052     if (needs & NEED_KERNEL) {
4053     newLine = addLine(new, config->cfi,
4054     (new->multiboot && getKeywordByType(LT_MBMODULE,
4055 niro 1940 config->cfi))
4056     ? LT_MBMODULE
4057     : preferredLineType(LT_KERNEL, config->cfi),
4058 niro 914 config->secondaryIndent,
4059     newKernelPath + strlen(prefix));
4060     needs &= ~NEED_KERNEL;
4061     }
4062     if (needs & NEED_MB) {
4063     newLine = addLine(new, config->cfi, LT_HYPER,
4064     config->secondaryIndent,
4065     newMBKernel + strlen(prefix));
4066     needs &= ~NEED_MB;
4067     }
4068     if (needs & NEED_INITRD) {
4069     char *initrdVal;
4070     initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
4071     newLine = addLine(new, config->cfi,
4072     (new->multiboot && getKeywordByType(LT_MBMODULE,
4073 niro 1940 config->cfi))
4074     ? LT_MBMODULE
4075     : preferredLineType(LT_INITRD, config->cfi),
4076 niro 914 config->secondaryIndent,
4077     initrdVal);
4078     free(initrdVal);
4079     needs &= ~NEED_INITRD;
4080     }
4081 niro 2685 if (needs & NEED_DEVTREE) {
4082     newLine = addLine(new, config->cfi, LT_DEVTREE,
4083     config->secondaryIndent,
4084     newDevTreePath);
4085     needs &= ~NEED_DEVTREE;
4086     }
4087    
4088     /* NEEDS_END must be last on bootloaders that need it... */
4089 niro 1696 if (needs & NEED_END) {
4090     newLine = addLine(new, config->cfi, LT_ENTRY_END,
4091     config->secondaryIndent, NULL);
4092     needs &= ~NEED_END;
4093     }
4094 niro 914 if (needs) {
4095     printf(_("grubby: needs=%d, aborting\n"), needs);
4096     abort();
4097     }
4098    
4099 niro 532 if (updateImage(config, "0", prefix, newKernelArgs, NULL,
4100     newMBKernelArgs, NULL)) return 1;
4101    
4102     return 0;
4103     }
4104    
4105 niro 914 static void traceback(int signum)
4106     {
4107     void *array[40];
4108     size_t size;
4109    
4110     signal(SIGSEGV, SIG_DFL);
4111     memset(array, '\0', sizeof (array));
4112     size = backtrace(array, 40);
4113    
4114 niro 2058 fprintf(stderr, "grubby received SIGSEGV! Backtrace (%ld):\n",
4115 niro 914 (unsigned long)size);
4116     backtrace_symbols_fd(array, size, STDERR_FILENO);
4117     exit(1);
4118     }
4119    
4120 niro 532 int main(int argc, const char ** argv) {
4121     poptContext optCon;
4122 niro 1696 const char * grubConfig = NULL;
4123 niro 532 char * outputFile = NULL;
4124     int arg = 0;
4125     int flags = 0;
4126     int badImageOkay = 0;
4127 niro 1696 int configureGrub2 = 0;
4128 niro 532 int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4129     int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4130 niro 914 int configureExtLinux = 0;
4131 niro 532 int bootloaderProbe = 0;
4132 niro 914 int extraInitrdCount = 0;
4133 niro 532 char * updateKernelPath = NULL;
4134     char * newKernelPath = NULL;
4135     char * removeKernelPath = NULL;
4136     char * newKernelArgs = NULL;
4137     char * newKernelInitrd = NULL;
4138     char * newKernelTitle = NULL;
4139 niro 2685 char * newDevTreePath = NULL;
4140 niro 532 char * newMBKernel = NULL;
4141     char * newMBKernelArgs = NULL;
4142     char * removeMBKernelArgs = NULL;
4143     char * removeMBKernel = NULL;
4144     char * bootPrefix = NULL;
4145     char * defaultKernel = NULL;
4146     char * removeArgs = NULL;
4147     char * kernelInfo = NULL;
4148 niro 914 char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4149 niro 2252 char * envPath = NULL;
4150 niro 532 const char * chptr = NULL;
4151     struct configFileInfo * cfi = NULL;
4152     struct grubConfig * config;
4153     struct singleEntry * template = NULL;
4154     int copyDefault = 0, makeDefault = 0;
4155     int displayDefault = 0;
4156 niro 1720 int displayDefaultIndex = 0;
4157 niro 1721 int displayDefaultTitle = 0;
4158 niro 1859 int defaultIndex = -1;
4159 niro 532 struct poptOption options[] = {
4160     { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4161     _("add an entry for the specified kernel"), _("kernel-path") },
4162     { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
4163     _("add an entry for the specified multiboot kernel"), NULL },
4164     { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
4165     _("default arguments for the new kernel or new arguments for "
4166     "kernel being updated"), _("args") },
4167     { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
4168     _("default arguments for the new multiboot kernel or "
4169     "new arguments for multiboot kernel being updated"), NULL },
4170     { "bad-image-okay", 0, 0, &badImageOkay, 0,
4171     _("don't sanity check images in boot entries (for testing only)"),
4172     NULL },
4173     { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4174     _("filestystem which contains /boot directory (for testing only)"),
4175     _("bootfs") },
4176 niro 1854 #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4177 niro 532 { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4178 niro 1853 _("check which bootloader is installed on boot sector") },
4179 niro 532 #endif
4180     { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4181     _("path to grub config file to update (\"-\" for stdin)"),
4182     _("path") },
4183     { "copy-default", 0, 0, &copyDefault, 0,
4184     _("use the default boot entry as a template for the new entry "
4185     "being added; if the default is not a linux image, or if "
4186     "the kernel referenced by the default image does not exist, "
4187     "the first linux entry whose kernel does exist is used as the "
4188     "template"), NULL },
4189 niro 1736 { "debug", 0, 0, &debug, 0,
4190     _("print debugging information for failures") },
4191 niro 532 { "default-kernel", 0, 0, &displayDefault, 0,
4192     _("display the path of the default kernel") },
4193 niro 1720 { "default-index", 0, 0, &displayDefaultIndex, 0,
4194     _("display the index of the default kernel") },
4195 niro 1721 { "default-title", 0, 0, &displayDefaultTitle, 0,
4196     _("display the title of the default kernel") },
4197 niro 2685 { "devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4198     _("device tree file for new stanza"), _("dtb-path") },
4199 niro 532 { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
4200     _("configure elilo bootloader") },
4201 niro 1940 { "efi", 0, POPT_ARG_NONE, &isEfi, 0,
4202     _("force grub2 stanzas to use efi") },
4203 niro 2252 { "env", 0, POPT_ARG_STRING, &envPath, 0,
4204     _("path for environment data"),
4205     _("path") },
4206 niro 914 { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
4207     _("configure extlinux bootloader (from syslinux)") },
4208 niro 532 { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
4209     _("configure grub bootloader") },
4210 niro 1696 { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
4211     _("configure grub2 bootloader") },
4212 niro 532 { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
4213     _("display boot information for specified kernel"),
4214     _("kernel-path") },
4215     { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
4216     _("initrd image for the new kernel"), _("initrd-path") },
4217 niro 914 { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
4218 niro 2058 _("auxiliary initrd image for things other than the new kernel"), _("initrd-path") },
4219 niro 532 { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
4220     _("configure lilo bootloader") },
4221     { "make-default", 0, 0, &makeDefault, 0,
4222     _("make the newly added entry the default boot entry"), NULL },
4223     { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
4224     _("path to output updated config file (\"-\" for stdout)"),
4225     _("path") },
4226     { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
4227     _("remove kernel arguments"), NULL },
4228     { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
4229     _("remove multiboot kernel arguments"), NULL },
4230     { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
4231     _("remove all entries for the specified kernel"),
4232     _("kernel-path") },
4233     { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
4234     _("remove all entries for the specified multiboot kernel"), NULL },
4235     { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
4236     _("make the first entry referencing the specified kernel "
4237     "the default"), _("kernel-path") },
4238 niro 1859 { "set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
4239     _("make the given entry index the default entry"),
4240     _("entry-index") },
4241 niro 532 { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
4242     _("configure silo bootloader") },
4243     { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
4244     _("title to use for the new kernel entry"), _("entry-title") },
4245     { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
4246     _("updated information for the specified kernel"),
4247     _("kernel-path") },
4248     { "version", 'v', 0, NULL, 'v',
4249     _("print the version of this program and exit"), NULL },
4250     { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
4251     _("configure yaboot bootloader") },
4252     { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
4253     _("configure zipl bootloader") },
4254     POPT_AUTOHELP
4255     { 0, 0, 0, 0, 0 }
4256     };
4257    
4258 niro 914 useextlinuxmenu=0;
4259    
4260     signal(SIGSEGV, traceback);
4261    
4262 niro 2236 int i = 0;
4263     for (int j = 1; j < argc; j++)
4264     i += strlen(argv[j]) + 1;
4265     saved_command_line = malloc(i);
4266     if (!saved_command_line) {
4267     fprintf(stderr, "grubby: %m\n");
4268     exit(1);
4269     }
4270     saved_command_line[0] = '\0';
4271     for (int j = 1; j < argc; j++) {
4272     strcat(saved_command_line, argv[j]);
4273     strncat(saved_command_line, j == argc -1 ? "" : " ", 1);
4274     }
4275    
4276 niro 532 optCon = poptGetContext("grubby", argc, argv, options, 0);
4277     poptReadDefaultConfig(optCon, 1);
4278    
4279     while ((arg = poptGetNextOpt(optCon)) >= 0) {
4280     switch (arg) {
4281     case 'v':
4282     printf("grubby version %s\n", VERSION);
4283     exit(0);
4284     break;
4285 niro 914 case 'i':
4286     if (extraInitrdCount < MAX_EXTRA_INITRDS) {
4287     extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
4288     } else {
4289     fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
4290     return 1;
4291     }
4292     break;
4293 niro 532 }
4294     }
4295    
4296     if (arg < -1) {
4297     fprintf(stderr, _("grubby: bad argument %s: %s\n"),
4298     poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
4299     poptStrerror(arg));
4300     return 1;
4301     }
4302    
4303     if ((chptr = poptGetArg(optCon))) {
4304     fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
4305     return 1;
4306     }
4307    
4308 niro 1696 if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
4309 niro 914 configureYaboot + configureSilo + configureZipl +
4310     configureExtLinux ) > 1) {
4311 niro 532 fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
4312     return 1;
4313     } else if (bootloaderProbe && grubConfig) {
4314     fprintf(stderr,
4315     _("grubby: cannot specify config file with --bootloader-probe\n"));
4316     return 1;
4317 niro 1696 } else if (configureGrub2) {
4318     cfi = &grub2ConfigType;
4319 niro 2252 if (envPath)
4320     cfi->envFile = envPath;
4321 niro 532 } else if (configureLilo) {
4322     cfi = &liloConfigType;
4323     } else if (configureGrub) {
4324     cfi = &grubConfigType;
4325     } else if (configureELilo) {
4326     cfi = &eliloConfigType;
4327     } else if (configureYaboot) {
4328     cfi = &yabootConfigType;
4329     } else if (configureSilo) {
4330     cfi = &siloConfigType;
4331     } else if (configureZipl) {
4332     cfi = &ziplConfigType;
4333 niro 914 } else if (configureExtLinux) {
4334     cfi = &extlinuxConfigType;
4335     useextlinuxmenu=1;
4336 niro 532 }
4337    
4338     if (!cfi) {
4339 niro 1802 if (grub2FindConfig(&grub2ConfigType))
4340     cfi = &grub2ConfigType;
4341     else
4342 niro 532 #ifdef __ia64__
4343 niro 1802 cfi = &eliloConfigType;
4344 niro 532 #elif __powerpc__
4345 niro 1802 cfi = &yabootConfigType;
4346 niro 532 #elif __sparc__
4347 niro 1802 cfi = &siloConfigType;
4348 niro 532 #elif __s390__
4349 niro 1802 cfi = &ziplConfigType;
4350 niro 532 #elif __s390x__
4351 niro 1802 cfi = &ziplConfigtype;
4352 niro 532 #else
4353 niro 1696 cfi = &grubConfigType;
4354 niro 532 #endif
4355     }
4356    
4357 niro 1696 if (!grubConfig) {
4358     if (cfi->findConfig)
4359     grubConfig = cfi->findConfig(cfi);
4360     if (!grubConfig)
4361     grubConfig = cfi->defaultConfig;
4362     }
4363 niro 532
4364 niro 2685 if (bootloaderProbe && (displayDefault || kernelInfo ||
4365 niro 1859 newKernelPath || removeKernelPath || makeDefault ||
4366     defaultKernel || displayDefaultIndex || displayDefaultTitle ||
4367     (defaultIndex >= 0))) {
4368 niro 532 fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
4369     "specified option"));
4370     return 1;
4371     }
4372    
4373 niro 2685 if ((displayDefault || kernelInfo) && (newKernelPath ||
4374 niro 532 removeKernelPath)) {
4375     fprintf(stderr, _("grubby: --default-kernel and --info may not "
4376     "be used when adding or removing kernels\n"));
4377     return 1;
4378     }
4379    
4380     if (newKernelPath && !newKernelTitle) {
4381     fprintf(stderr, _("grubby: kernel title must be specified\n"));
4382     return 1;
4383 niro 1156 } else if (!newKernelPath && (newKernelTitle || copyDefault ||
4384     (newKernelInitrd && !updateKernelPath)||
4385 niro 914 makeDefault || extraInitrdCount > 0)) {
4386 niro 532 fprintf(stderr, _("grubby: kernel path expected\n"));
4387     return 1;
4388     }
4389    
4390     if (newKernelPath && updateKernelPath) {
4391     fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
4392     "not be used together"));
4393     return 1;
4394     }
4395    
4396     if (makeDefault && defaultKernel) {
4397     fprintf(stderr, _("grubby: --make-default and --default-kernel "
4398     "may not be used together\n"));
4399     return 1;
4400     } else if (defaultKernel && removeKernelPath &&
4401     !strcmp(defaultKernel, removeKernelPath)) {
4402     fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));
4403     return 1;
4404     } else if (defaultKernel && newKernelPath &&
4405     !strcmp(defaultKernel, newKernelPath)) {
4406     makeDefault = 1;
4407     defaultKernel = NULL;
4408     }
4409 niro 1859 else if (defaultKernel && (defaultIndex >= 0)) {
4410     fprintf(stderr, _("grubby: --set-default and --set-default-index "
4411     "may not be used together\n"));
4412     return 1;
4413     }
4414 niro 532
4415 niro 1717 if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
4416 niro 532 fprintf(stderr, _("grubby: output file must be specified if stdin "
4417     "is used\n"));
4418     return 1;
4419     }
4420    
4421     if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
4422 niro 1859 && !kernelInfo && !bootloaderProbe && !updateKernelPath
4423     && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle
4424     && (defaultIndex == -1)) {
4425 niro 532 fprintf(stderr, _("grubby: no action specified\n"));
4426     return 1;
4427     }
4428    
4429     flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
4430    
4431     if (cfi->needsBootPrefix) {
4432     if (!bootPrefix) {
4433     bootPrefix = findBootPrefix();
4434     if (!bootPrefix) return 1;
4435     } else {
4436     /* this shouldn't end with a / */
4437     if (bootPrefix[strlen(bootPrefix) - 1] == '/')
4438     bootPrefix[strlen(bootPrefix) - 1] = '\0';
4439     }
4440     } else {
4441     bootPrefix = "";
4442     }
4443    
4444 niro 914 if (!cfi->mbAllowExtraInitRds &&
4445     extraInitrdCount > 0) {
4446     fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
4447     return 1;
4448     }
4449    
4450 niro 532 if (bootloaderProbe) {
4451 niro 1854 int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
4452     struct grubConfig * lconfig, * gconfig, * yconfig, * econfig;
4453 niro 532
4454 niro 1696 const char *grub2config = grub2FindConfig(&grub2ConfigType);
4455     if (grub2config) {
4456     gconfig = readConfig(grub2config, &grub2ConfigType);
4457     if (!gconfig)
4458     gr2c = 1;
4459     else
4460     gr2c = checkForGrub2(gconfig);
4461     }
4462    
4463 niro 1715 const char *grubconfig = grubFindConfig(&grubConfigType);
4464     if (!access(grubconfig, F_OK)) {
4465     gconfig = readConfig(grubconfig, &grubConfigType);
4466 niro 532 if (!gconfig)
4467     grc = 1;
4468     else
4469     grc = checkForGrub(gconfig);
4470     }
4471    
4472     if (!access(liloConfigType.defaultConfig, F_OK)) {
4473     lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);
4474     if (!lconfig)
4475     lrc = 1;
4476     else
4477     lrc = checkForLilo(lconfig);
4478     }
4479    
4480 niro 1854 if (!access(eliloConfigType.defaultConfig, F_OK)) {
4481     econfig = readConfig(eliloConfigType.defaultConfig,
4482     &eliloConfigType);
4483     if (!econfig)
4484     erc = 1;
4485     else
4486     erc = checkForElilo(econfig);
4487     }
4488    
4489 niro 914 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
4490     lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
4491     if (!lconfig)
4492 niro 1854 extrc = 1;
4493 niro 914 else
4494 niro 1854 extrc = checkForExtLinux(lconfig);
4495 niro 914 }
4496    
4497 niro 532
4498 niro 1853 if (!access(yabootConfigType.defaultConfig, F_OK)) {
4499     yconfig = readConfig(yabootConfigType.defaultConfig,
4500     &yabootConfigType);
4501     if (!yconfig)
4502     yrc = 1;
4503     else
4504 niro 1864 yrc = checkForYaboot(yconfig);
4505 niro 1853 }
4506    
4507 niro 1854 if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1 ||
4508 niro 1855 erc == 1)
4509 niro 1854 return 1;
4510 niro 1853
4511 niro 532 if (lrc == 2) printf("lilo\n");
4512 niro 1696 if (gr2c == 2) printf("grub2\n");
4513 niro 532 if (grc == 2) printf("grub\n");
4514 niro 1854 if (extrc == 2) printf("extlinux\n");
4515 niro 1853 if (yrc == 2) printf("yaboot\n");
4516 niro 1854 if (erc == 2) printf("elilo\n");
4517 niro 532
4518     return 0;
4519     }
4520    
4521 niro 2246 if (grubConfig == NULL) {
4522     printf("Could not find bootloader configuration file.\n");
4523     exit(1);
4524     }
4525    
4526 niro 532 config = readConfig(grubConfig, cfi);
4527     if (!config) return 1;
4528    
4529     if (displayDefault) {
4530     struct singleLine * line;
4531     struct singleEntry * entry;
4532     char * rootspec;
4533    
4534     if (config->defaultImage == -1) return 0;
4535 niro 2255 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4536     cfi->defaultIsSaved)
4537     config->defaultImage = 0;
4538 niro 532 entry = findEntryByIndex(config, config->defaultImage);
4539     if (!entry) return 0;
4540     if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
4541    
4542 niro 2683 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
4543 niro 532 if (!line) return 0;
4544    
4545     rootspec = getRootSpecifier(line->elements[1].item);
4546     printf("%s%s\n", bootPrefix, line->elements[1].item +
4547     ((rootspec != NULL) ? strlen(rootspec) : 0));
4548    
4549     return 0;
4550 niro 1720
4551 niro 1721 } else if (displayDefaultTitle) {
4552     struct singleLine * line;
4553     struct singleEntry * entry;
4554    
4555     if (config->defaultImage == -1) return 0;
4556 niro 2255 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4557     cfi->defaultIsSaved)
4558     config->defaultImage = 0;
4559 niro 1721 entry = findEntryByIndex(config, config->defaultImage);
4560     if (!entry) return 0;
4561    
4562     if (!configureGrub2) {
4563     line = getLineByType(LT_TITLE, entry->lines);
4564     if (!line) return 0;
4565     printf("%s\n", line->elements[1].item);
4566    
4567     } else {
4568 niro 1746 char * title;
4569 niro 1721
4570     dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
4571     line = getLineByType(LT_MENUENTRY, entry->lines);
4572     if (!line) return 0;
4573 niro 1746 title = grub2ExtractTitle(line);
4574     if (title)
4575     printf("%s\n", title);
4576 niro 1721 }
4577     return 0;
4578    
4579 niro 1720 } else if (displayDefaultIndex) {
4580     if (config->defaultImage == -1) return 0;
4581 niro 2255 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4582     cfi->defaultIsSaved)
4583     config->defaultImage = 0;
4584 niro 1720 printf("%i\n", config->defaultImage);
4585 niro 2250 return 0;
4586 niro 1720
4587 niro 532 } else if (kernelInfo)
4588     return displayInfo(config, kernelInfo, bootPrefix);
4589    
4590     if (copyDefault) {
4591     template = findTemplate(config, bootPrefix, NULL, 0, flags);
4592     if (!template) return 1;
4593     }
4594    
4595     markRemovedImage(config, removeKernelPath, bootPrefix);
4596     markRemovedImage(config, removeMBKernel, bootPrefix);
4597     setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
4598 niro 1859 bootPrefix, flags, defaultIndex);
4599 niro 532 setFallbackImage(config, newKernelPath != NULL);
4600     if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
4601     removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
4602 niro 1156 if (updateKernelPath && newKernelInitrd) {
4603 niro 2263 if (newMBKernel) {
4604     if (addMBInitrd(config, newMBKernel, updateKernelPath,
4605     bootPrefix, newKernelInitrd))
4606     return 1;
4607     } else {
4608     if (updateInitrd(config, updateKernelPath, bootPrefix,
4609     newKernelInitrd))
4610     return 1;
4611     }
4612 niro 1156 }
4613 niro 532 if (addNewKernel(config, template, bootPrefix, newKernelPath,
4614     newKernelTitle, newKernelArgs, newKernelInitrd,
4615 niro 1844 (const char **)extraInitrds, extraInitrdCount,
4616 niro 2685 newMBKernel, newMBKernelArgs, newDevTreePath)) return 1;
4617 niro 532
4618    
4619     if (numEntries(config) == 0) {
4620     fprintf(stderr, _("grubby: doing this would leave no kernel entries. "
4621     "Not writing out new config.\n"));
4622     return 1;
4623     }
4624    
4625     if (!outputFile)
4626 niro 1696 outputFile = (char *)grubConfig;
4627 niro 532
4628     return writeConfig(config, outputFile, bootPrefix);
4629     }