Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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