Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2262 - (hide annotations) (download)
Mon Oct 21 14:06:22 2013 UTC (10 years, 6 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 125422 byte(s)
Add support for SUSE grub2 configuration files
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     char *envFile = info->envFile ? info->envFile : "/boot/grub2/grubenv";
279     int rc = asprintf(&s, "grub2-editenv %s list | grep '^%s='", envFile, name);
280    
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     char *envFile = info->envFile ? info->envFile : "/boot/grub2/grubenv";
344    
345 niro 2257 unquote(value);
346     value = shellEscape(value);
347     if (!value)
348     return -1;
349    
350 niro 2252 rc = asprintf(&s, "grub2-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 1156 int updateInitrd(struct grubConfig * cfg, const char * image,
3262     const char * prefix, const char * initrd) {
3263     struct singleEntry * entry;
3264 niro 1696 struct singleLine * line, * kernelLine, *endLine = NULL;
3265 niro 1156 int index = 0;
3266    
3267     if (!image) return 0;
3268    
3269     for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3270 niro 1940 kernelLine = getLineByType(LT_KERNEL|LT_KERNEL_EFI, entry->lines);
3271 niro 1156 if (!kernelLine) continue;
3272    
3273 niro 1940 line = getLineByType(LT_INITRD|LT_INITRD_EFI, entry->lines);
3274 niro 1156 if (line)
3275     removeLine(entry, line);
3276     if (prefix) {
3277     int prefixLen = strlen(prefix);
3278     if (!strncmp(initrd, prefix, prefixLen))
3279     initrd += prefixLen;
3280     }
3281 niro 1696 endLine = getLineByType(LT_ENTRY_END, entry->lines);
3282     if (endLine)
3283     removeLine(entry, endLine);
3284 niro 1940 line = addLine(entry, cfg->cfi, preferredLineType(LT_INITRD, cfg->cfi),
3285     kernelLine->indent, initrd);
3286 niro 1696 if (!line)
3287     return 1;
3288     if (endLine) {
3289     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3290     if (!line)
3291     return 1;
3292     }
3293    
3294 niro 1156 break;
3295     }
3296    
3297     return 0;
3298     }
3299    
3300 niro 532 int checkDeviceBootloader(const char * device, const unsigned char * boot) {
3301     int fd;
3302     unsigned char bootSect[512];
3303     int offset;
3304    
3305     fd = open(device, O_RDONLY);
3306     if (fd < 0) {
3307     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3308     device, strerror(errno));
3309     return 1;
3310     }
3311    
3312     if (read(fd, bootSect, 512) != 512) {
3313     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3314     device, strerror(errno));
3315     return 1;
3316     }
3317     close(fd);
3318    
3319     /* first three bytes should match, a jmp short should be in there */
3320     if (memcmp(boot, bootSect, 3))
3321     return 0;
3322    
3323 niro 1718 if (boot[1] == JMP_SHORT_OPCODE) {
3324 niro 532 offset = boot[2] + 2;
3325     } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3326     offset = (boot[3] << 8) + boot[2] + 2;
3327 niro 1718 } else if (boot[0] == JMP_SHORT_OPCODE) {
3328     offset = boot[1] + 2;
3329     /*
3330     * it looks like grub, when copying stage1 into the mbr, patches stage1
3331     * right after the JMP location, replacing other instructions such as
3332     * JMPs for NOOPs. So, relax the check a little bit by skipping those
3333     * different bytes.
3334     */
3335     if ((bootSect[offset + 1] == NOOP_OPCODE)
3336     && (bootSect[offset + 2] == NOOP_OPCODE)) {
3337     offset = offset + 3;
3338     }
3339 niro 532 } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3340     offset = (boot[2] << 8) + boot[1] + 2;
3341     } else {
3342     return 0;
3343     }
3344    
3345     if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
3346     return 0;
3347    
3348     return 2;
3349     }
3350    
3351     int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {
3352     int fd;
3353     char buf[65536];
3354     char * end;
3355     char * chptr;
3356     char * chptr2;
3357     int rc;
3358    
3359     /* it's on raid; we need to parse /proc/mdstat and check all of the
3360     *raw* devices listed in there */
3361    
3362     if (!strncmp(mdDev, "/dev/", 5))
3363     mdDev += 5;
3364    
3365     if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
3366     fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
3367     strerror(errno));
3368     return 2;
3369     }
3370    
3371     rc = read(fd, buf, sizeof(buf) - 1);
3372     if (rc < 0 || rc == (sizeof(buf) - 1)) {
3373     fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
3374     strerror(errno));
3375     close(fd);
3376     return 2;
3377     }
3378     close(fd);
3379     buf[rc] = '\0';
3380    
3381     chptr = buf;
3382     while (*chptr) {
3383     end = strchr(chptr, '\n');
3384     if (!end) break;
3385     *end = '\0';
3386    
3387     if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
3388     chptr[strlen(mdDev)] == ' ') {
3389    
3390     /* found the device */
3391     while (*chptr && *chptr != ':') chptr++;
3392     chptr++;
3393     while (*chptr && isspace(*chptr)) chptr++;
3394    
3395     /* skip the "active" bit */
3396     while (*chptr && !isspace(*chptr)) chptr++;
3397     while (*chptr && isspace(*chptr)) chptr++;
3398    
3399     /* skip the raid level */
3400     while (*chptr && !isspace(*chptr)) chptr++;
3401     while (*chptr && isspace(*chptr)) chptr++;
3402    
3403     /* everything else is partition stuff */
3404     while (*chptr) {
3405     chptr2 = chptr;
3406     while (*chptr2 && *chptr2 != '[') chptr2++;
3407     if (!*chptr2) break;
3408    
3409     /* yank off the numbers at the end */
3410     chptr2--;
3411     while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;
3412     chptr2++;
3413     *chptr2 = '\0';
3414    
3415     /* Better, now we need the /dev/ back. We're done with
3416     * everything before this point, so we can just put
3417     * the /dev/ part there. There will always be room. */
3418     memcpy(chptr - 5, "/dev/", 5);
3419     rc = checkDeviceBootloader(chptr - 5, boot);
3420     if (rc != 2) {
3421     return rc;
3422     }
3423    
3424     chptr = chptr2 + 1;
3425     /* skip the [11] bit */
3426     while (*chptr && !isspace(*chptr)) chptr++;
3427     /* and move to the next one */
3428     while (*chptr && isspace(*chptr)) chptr++;
3429     }
3430    
3431     /* we're good to go */
3432     return 2;
3433     }
3434    
3435     chptr = end + 1;
3436     }
3437    
3438     fprintf(stderr,
3439     _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
3440     mdDev);
3441     return 0;
3442     }
3443    
3444     int checkForLilo(struct grubConfig * config) {
3445     int fd;
3446     unsigned char boot[512];
3447     struct singleLine * line;
3448    
3449     for (line = config->theLines; line; line = line->next)
3450     if (line->type == LT_BOOT) break;
3451    
3452     if (!line) {
3453     fprintf(stderr,
3454     _("grubby: no boot line found in lilo configuration\n"));
3455     return 1;
3456     }
3457    
3458     if (line->numElements != 2) return 1;
3459    
3460     fd = open("/boot/boot.b", O_RDONLY);
3461     if (fd < 0) {
3462     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3463     "/boot/boot.b", strerror(errno));
3464     return 1;
3465     }
3466    
3467     if (read(fd, boot, 512) != 512) {
3468     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3469     "/boot/boot.b", strerror(errno));
3470     return 1;
3471     }
3472     close(fd);
3473    
3474     if (!strncmp("/dev/md", line->elements[1].item, 7))
3475     return checkLiloOnRaid(line->elements[1].item, boot);
3476    
3477     return checkDeviceBootloader(line->elements[1].item, boot);
3478     }
3479    
3480 niro 1696 int checkForGrub2(struct grubConfig * config) {
3481 niro 1714 if (!access("/etc/grub.d/", R_OK))
3482 niro 1696 return 2;
3483    
3484     return 1;
3485     }
3486    
3487 niro 532 int checkForGrub(struct grubConfig * config) {
3488     int fd;
3489     unsigned char bootSect[512];
3490     char * boot;
3491 niro 1851 int onSuse = isSuseSystem();
3492 niro 532
3493 niro 1851
3494     if (onSuse) {
3495     if (parseSuseGrubConf(NULL, &boot))
3496     return 0;
3497 niro 1850 } else {
3498 niro 1851 if (parseSysconfigGrub(NULL, &boot))
3499     return 0;
3500 niro 1850 }
3501 niro 532
3502     /* assume grub is not installed -- not an error condition */
3503     if (!boot)
3504     return 0;
3505    
3506     fd = open("/boot/grub/stage1", O_RDONLY);
3507     if (fd < 0)
3508     /* this doesn't exist if grub hasn't been installed */
3509     return 0;
3510    
3511     if (read(fd, bootSect, 512) != 512) {
3512     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3513     "/boot/grub/stage1", strerror(errno));
3514 niro 914 close(fd);
3515 niro 532 return 1;
3516     }
3517     close(fd);
3518    
3519 niro 1851 /* The more elaborate checks do not work on SuSE. The checks done
3520     * seem to be reasonble (at least for now), so just return success
3521     */
3522     if (onSuse)
3523 niro 1850 return 2;
3524 niro 1851
3525     return checkDeviceBootloader(boot, bootSect);
3526 niro 532 }
3527    
3528 niro 914 int checkForExtLinux(struct grubConfig * config) {
3529     int fd;
3530     unsigned char bootSect[512];
3531     char * boot;
3532     char executable[] = "/boot/extlinux/extlinux";
3533    
3534     printf("entered: checkForExtLinux()\n");
3535    
3536     if (parseSysconfigGrub(NULL, &boot))
3537     return 0;
3538    
3539     /* assume grub is not installed -- not an error condition */
3540     if (!boot)
3541     return 0;
3542    
3543     fd = open(executable, O_RDONLY);
3544     if (fd < 0)
3545     /* this doesn't exist if grub hasn't been installed */
3546     return 0;
3547    
3548     if (read(fd, bootSect, 512) != 512) {
3549     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3550     executable, strerror(errno));
3551     return 1;
3552     }
3553     close(fd);
3554    
3555     return checkDeviceBootloader(boot, bootSect);
3556     }
3557    
3558 niro 1853 int checkForYaboot(struct grubConfig * config) {
3559     /*
3560     * This is a simplistic check that we consider good enough for own puporses
3561     *
3562     * If we were to properly check if yaboot is *installed* we'd need to:
3563     * 1) get the system boot device (LT_BOOT)
3564     * 2) considering it's a raw filesystem, check if the yaboot binary matches
3565     * the content on the boot device
3566     * 3) if not, copy the binary to a temporary file and run "addnote" on it
3567     * 4) check again if binary and boot device contents match
3568     */
3569     if (!access("/etc/yaboot.conf", R_OK))
3570     return 2;
3571    
3572     return 1;
3573     }
3574    
3575 niro 1854 int checkForElilo(struct grubConfig * config) {
3576     if (!access("/etc/elilo.conf", R_OK))
3577     return 2;
3578    
3579     return 1;
3580     }
3581    
3582 niro 532 static char * getRootSpecifier(char * str) {
3583     char * idx, * rootspec = NULL;
3584    
3585     if (*str == '(') {
3586     idx = rootspec = strdup(str);
3587     while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;
3588     *(++idx) = '\0';
3589     }
3590     return rootspec;
3591     }
3592    
3593 niro 914 static char * getInitrdVal(struct grubConfig * config,
3594     const char * prefix, struct singleLine *tmplLine,
3595     const char * newKernelInitrd,
3596 niro 1844 const char ** extraInitrds, int extraInitrdCount)
3597 niro 914 {
3598     char *initrdVal, *end;
3599     int i;
3600     size_t totalSize;
3601     size_t prefixLen;
3602     char separatorChar;
3603    
3604     prefixLen = strlen(prefix);
3605     totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
3606    
3607     for (i = 0; i < extraInitrdCount; i++) {
3608     totalSize += sizeof(separatorChar);
3609     totalSize += strlen(extraInitrds[i]) - prefixLen;
3610     }
3611    
3612     initrdVal = end = malloc(totalSize);
3613    
3614     end = stpcpy (end, newKernelInitrd + prefixLen);
3615    
3616     separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
3617     for (i = 0; i < extraInitrdCount; i++) {
3618     const char *extraInitrd;
3619     int j;
3620    
3621     extraInitrd = extraInitrds[i] + prefixLen;
3622     /* Don't add entries that are already there */
3623     if (tmplLine != NULL) {
3624     for (j = 2; j < tmplLine->numElements; j++)
3625     if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
3626     break;
3627    
3628     if (j != tmplLine->numElements)
3629     continue;
3630     }
3631    
3632     *end++ = separatorChar;
3633     end = stpcpy(end, extraInitrd);
3634     }
3635    
3636     return initrdVal;
3637     }
3638    
3639 niro 532 int addNewKernel(struct grubConfig * config, struct singleEntry * template,
3640     const char * prefix,
3641 niro 1844 const char * newKernelPath, const char * newKernelTitle,
3642     const char * newKernelArgs, const char * newKernelInitrd,
3643     const char ** extraInitrds, int extraInitrdCount,
3644     const char * newMBKernel, const char * newMBKernelArgs) {
3645 niro 532 struct singleEntry * new;
3646 niro 914 struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
3647 niro 532 int needs;
3648     char * chptr;
3649    
3650     if (!newKernelPath) return 0;
3651    
3652     /* if the newKernelTitle is too long silently munge it into something
3653     * we can live with. truncating is first check, then we'll just mess with
3654     * it until it looks better */
3655     if (config->cfi->maxTitleLength &&
3656     (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
3657     char * buf = alloca(config->cfi->maxTitleLength + 7);
3658     char * numBuf = alloca(config->cfi->maxTitleLength + 1);
3659     int i = 1;
3660    
3661     sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);
3662     while (findEntryByPath(config, buf, NULL, NULL)) {
3663     sprintf(numBuf, "%d", i++);
3664     strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
3665     }
3666    
3667     newKernelTitle = buf + 6;
3668     }
3669    
3670     new = malloc(sizeof(*new));
3671     new->skip = 0;
3672     new->multiboot = 0;
3673     new->next = config->entries;
3674     new->lines = NULL;
3675     config->entries = new;
3676    
3677     /* copy/update from the template */
3678 niro 914 needs = NEED_KERNEL | NEED_TITLE;
3679     if (newKernelInitrd)
3680     needs |= NEED_INITRD;
3681 niro 532 if (newMBKernel) {
3682 niro 914 needs |= NEED_MB;
3683 niro 532 new->multiboot = 1;
3684     }
3685    
3686     if (template) {
3687 niro 914 for (masterLine = template->lines;
3688     masterLine && (tmplLine = lineDup(masterLine));
3689     lineFree(tmplLine), masterLine = masterLine->next)
3690     {
3691     dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
3692 niro 532
3693     /* skip comments */
3694     chptr = tmplLine->indent;
3695     while (*chptr && isspace(*chptr)) chptr++;
3696     if (*chptr == '#') continue;
3697    
3698 niro 1940 if (iskernel(tmplLine->type) && tmplLine->numElements >= 2) {
3699 niro 914 if (!template->multiboot && (needs & NEED_MB)) {
3700     /* it's not a multiboot template and this is the kernel
3701     * line. Try to be intelligent about inserting the
3702     * hypervisor at the same time.
3703     */
3704     if (config->cfi->mbHyperFirst) {
3705     /* insert the hypervisor first */
3706     newLine = addLine(new, config->cfi, LT_HYPER,
3707     tmplLine->indent,
3708     newMBKernel + strlen(prefix));
3709     /* set up for adding the kernel line */
3710     free(tmplLine->indent);
3711     tmplLine->indent = strdup(config->secondaryIndent);
3712     needs &= ~NEED_MB;
3713     }
3714     if (needs & NEED_KERNEL) {
3715     /* use addLineTmpl to preserve line elements,
3716     * otherwise we could just call addLine. Unfortunately
3717     * this means making some changes to the template
3718     * such as the indent change above and the type
3719     * change below.
3720     */
3721     struct keywordTypes * mbm_kw =
3722     getKeywordByType(LT_MBMODULE, config->cfi);
3723     if (mbm_kw) {
3724     tmplLine->type = LT_MBMODULE;
3725     free(tmplLine->elements[0].item);
3726     tmplLine->elements[0].item = strdup(mbm_kw->key);
3727     }
3728     newLine = addLineTmpl(new, tmplLine, newLine,
3729     newKernelPath + strlen(prefix), config->cfi);
3730     needs &= ~NEED_KERNEL;
3731     }
3732     if (needs & NEED_MB) { /* !mbHyperFirst */
3733     newLine = addLine(new, config->cfi, LT_HYPER,
3734     config->secondaryIndent,
3735     newMBKernel + strlen(prefix));
3736     needs &= ~NEED_MB;
3737     }
3738     } else if (needs & NEED_KERNEL) {
3739     newLine = addLineTmpl(new, tmplLine, newLine,
3740     newKernelPath + strlen(prefix), config->cfi);
3741     needs &= ~NEED_KERNEL;
3742     }
3743 niro 532
3744 niro 914 } else if (tmplLine->type == LT_HYPER &&
3745     tmplLine->numElements >= 2) {
3746     if (needs & NEED_MB) {
3747     newLine = addLineTmpl(new, tmplLine, newLine,
3748     newMBKernel + strlen(prefix), config->cfi);
3749     needs &= ~NEED_MB;
3750     }
3751 niro 532
3752 niro 914 } else if (tmplLine->type == LT_MBMODULE &&
3753     tmplLine->numElements >= 2) {
3754     if (new->multiboot) {
3755     if (needs & NEED_KERNEL) {
3756     newLine = addLineTmpl(new, tmplLine, newLine,
3757     newKernelPath +
3758     strlen(prefix), config->cfi);
3759     needs &= ~NEED_KERNEL;
3760     } else if (config->cfi->mbInitRdIsModule &&
3761     (needs & NEED_INITRD)) {
3762     char *initrdVal;
3763     initrdVal = getInitrdVal(config, prefix, tmplLine,
3764     newKernelInitrd, extraInitrds,
3765     extraInitrdCount);
3766     newLine = addLineTmpl(new, tmplLine, newLine,
3767     initrdVal, config->cfi);
3768     free(initrdVal);
3769     needs &= ~NEED_INITRD;
3770     }
3771     } else if (needs & NEED_KERNEL) {
3772     /* template is multi but new is not,
3773     * insert the kernel in the first module slot
3774     */
3775 niro 1940 tmplLine->type = preferredLineType(LT_KERNEL, config->cfi);
3776 niro 914 free(tmplLine->elements[0].item);
3777     tmplLine->elements[0].item =
3778 niro 1940 strdup(getKeywordByType(tmplLine->type,
3779     config->cfi)->key);
3780 niro 914 newLine = addLineTmpl(new, tmplLine, newLine,
3781 niro 1940 newKernelPath + strlen(prefix),
3782     config->cfi);
3783 niro 914 needs &= ~NEED_KERNEL;
3784     } else if (needs & NEED_INITRD) {
3785     char *initrdVal;
3786     /* template is multi but new is not,
3787     * insert the initrd in the second module slot
3788     */
3789 niro 1940 tmplLine->type = preferredLineType(LT_INITRD, config->cfi);
3790 niro 914 free(tmplLine->elements[0].item);
3791     tmplLine->elements[0].item =
3792 niro 1940 strdup(getKeywordByType(tmplLine->type,
3793     config->cfi)->key);
3794 niro 914 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3795     newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3796     free(initrdVal);
3797     needs &= ~NEED_INITRD;
3798     }
3799 niro 532
3800 niro 1940 } else if (isinitrd(tmplLine->type) && tmplLine->numElements >= 2) {
3801 niro 914 if (needs & NEED_INITRD &&
3802     new->multiboot && !template->multiboot &&
3803     config->cfi->mbInitRdIsModule) {
3804     /* make sure we don't insert the module initrd
3805     * before the module kernel... if we don't do it here,
3806     * it will be inserted following the template.
3807     */
3808     if (!needs & NEED_KERNEL) {
3809     char *initrdVal;
3810    
3811     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3812     newLine = addLine(new, config->cfi, LT_MBMODULE,
3813     config->secondaryIndent,
3814     initrdVal);
3815     free(initrdVal);
3816     needs &= ~NEED_INITRD;
3817     }
3818     } else if (needs & NEED_INITRD) {
3819     char *initrdVal;
3820     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3821     newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3822     free(initrdVal);
3823     needs &= ~NEED_INITRD;
3824 niro 532 }
3825    
3826 niro 1696 } else if (tmplLine->type == LT_MENUENTRY &&
3827     (needs & NEED_TITLE)) {
3828     requote(tmplLine, config->cfi);
3829     char *nkt = malloc(strlen(newKernelTitle)+3);
3830     strcpy(nkt, "'");
3831     strcat(nkt, newKernelTitle);
3832     strcat(nkt, "'");
3833     newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
3834     free(nkt);
3835     needs &= ~NEED_TITLE;
3836 niro 532 } else if (tmplLine->type == LT_TITLE &&
3837 niro 914 (needs & NEED_TITLE)) {
3838     if (tmplLine->numElements >= 2) {
3839     newLine = addLineTmpl(new, tmplLine, newLine,
3840     newKernelTitle, config->cfi);
3841     needs &= ~NEED_TITLE;
3842     } else if (tmplLine->numElements == 1 &&
3843     config->cfi->titleBracketed) {
3844     /* addLineTmpl doesn't handle titleBracketed */
3845     newLine = addLine(new, config->cfi, LT_TITLE,
3846     tmplLine->indent, newKernelTitle);
3847     needs &= ~NEED_TITLE;
3848     }
3849 niro 1696 } else if (tmplLine->type == LT_ECHO) {
3850     requote(tmplLine, config->cfi);
3851 niro 1722 static const char *prefix = "'Loading ";
3852 niro 1696 if (tmplLine->numElements > 1 &&
3853 niro 1722 strstr(tmplLine->elements[1].item, prefix) &&
3854 niro 1940 masterLine->next &&
3855     iskernel(masterLine->next->type)) {
3856 niro 1696 char *newTitle = malloc(strlen(prefix) +
3857     strlen(newKernelTitle) + 2);
3858 niro 532
3859 niro 1696 strcpy(newTitle, prefix);
3860     strcat(newTitle, newKernelTitle);
3861     strcat(newTitle, "'");
3862     newLine = addLine(new, config->cfi, LT_ECHO,
3863     tmplLine->indent, newTitle);
3864     free(newTitle);
3865     } else {
3866     /* pass through other lines from the template */
3867     newLine = addLineTmpl(new, tmplLine, newLine, NULL,
3868     config->cfi);
3869     }
3870 niro 914 } else {
3871     /* pass through other lines from the template */
3872     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
3873     }
3874 niro 532 }
3875 niro 914
3876 niro 532 } else {
3877 niro 914 /* don't have a template, so start the entry with the
3878     * appropriate starting line
3879     */
3880 niro 1693 switch (config->cfi->entryStart) {
3881 niro 914 case LT_KERNEL:
3882 niro 1940 case LT_KERNEL_EFI:
3883 niro 914 if (new->multiboot && config->cfi->mbHyperFirst) {
3884     /* fall through to LT_HYPER */
3885     } else {
3886 niro 1940 newLine = addLine(new, config->cfi,
3887     preferredLineType(LT_KERNEL, config->cfi),
3888 niro 914 config->primaryIndent,
3889     newKernelPath + strlen(prefix));
3890     needs &= ~NEED_KERNEL;
3891     break;
3892     }
3893    
3894     case LT_HYPER:
3895     newLine = addLine(new, config->cfi, LT_HYPER,
3896     config->primaryIndent,
3897     newMBKernel + strlen(prefix));
3898     needs &= ~NEED_MB;
3899 niro 532 break;
3900    
3901 niro 1696 case LT_MENUENTRY: {
3902     char *nkt = malloc(strlen(newKernelTitle)+3);
3903     strcpy(nkt, "'");
3904     strcat(nkt, newKernelTitle);
3905     strcat(nkt, "'");
3906     newLine = addLine(new, config->cfi, LT_MENUENTRY,
3907     config->primaryIndent, nkt);
3908     free(nkt);
3909     needs &= ~NEED_TITLE;
3910     needs |= NEED_END;
3911     break;
3912     }
3913 niro 914 case LT_TITLE:
3914     if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
3915     char * templabel;
3916     int x = 0, y = 0;
3917    
3918     templabel = strdup(newKernelTitle);
3919     while( templabel[x]){
3920     if( templabel[x] == ' ' ){
3921     y = x;
3922     while( templabel[y] ){
3923     templabel[y] = templabel[y+1];
3924     y++;
3925     }
3926     }
3927     x++;
3928     }
3929     newLine = addLine(new, config->cfi, LT_TITLE,
3930     config->primaryIndent, templabel);
3931     free(templabel);
3932     }else{
3933     newLine = addLine(new, config->cfi, LT_TITLE,
3934     config->primaryIndent, newKernelTitle);
3935     }
3936     needs &= ~NEED_TITLE;
3937     break;
3938    
3939     default:
3940     abort();
3941 niro 532 }
3942     }
3943    
3944 niro 914 /* add the remainder of the lines, i.e. those that either
3945     * weren't present in the template, or in the case of no template,
3946 niro 1693 * all the lines following the entryStart.
3947 niro 914 */
3948     if (needs & NEED_TITLE) {
3949     newLine = addLine(new, config->cfi, LT_TITLE,
3950     config->secondaryIndent,
3951     newKernelTitle);
3952     needs &= ~NEED_TITLE;
3953 niro 532 }
3954 niro 914 if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
3955     newLine = addLine(new, config->cfi, LT_HYPER,
3956     config->secondaryIndent,
3957     newMBKernel + strlen(prefix));
3958     needs &= ~NEED_MB;
3959     }
3960     if (needs & NEED_KERNEL) {
3961     newLine = addLine(new, config->cfi,
3962     (new->multiboot && getKeywordByType(LT_MBMODULE,
3963 niro 1940 config->cfi))
3964     ? LT_MBMODULE
3965     : preferredLineType(LT_KERNEL, config->cfi),
3966 niro 914 config->secondaryIndent,
3967     newKernelPath + strlen(prefix));
3968     needs &= ~NEED_KERNEL;
3969     }
3970     if (needs & NEED_MB) {
3971     newLine = addLine(new, config->cfi, LT_HYPER,
3972     config->secondaryIndent,
3973     newMBKernel + strlen(prefix));
3974     needs &= ~NEED_MB;
3975     }
3976     if (needs & NEED_INITRD) {
3977     char *initrdVal;
3978     initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
3979     newLine = addLine(new, config->cfi,
3980     (new->multiboot && getKeywordByType(LT_MBMODULE,
3981 niro 1940 config->cfi))
3982     ? LT_MBMODULE
3983     : preferredLineType(LT_INITRD, config->cfi),
3984 niro 914 config->secondaryIndent,
3985     initrdVal);
3986     free(initrdVal);
3987     needs &= ~NEED_INITRD;
3988     }
3989 niro 1696 if (needs & NEED_END) {
3990     newLine = addLine(new, config->cfi, LT_ENTRY_END,
3991     config->secondaryIndent, NULL);
3992     needs &= ~NEED_END;
3993     }
3994 niro 532
3995 niro 914 if (needs) {
3996     printf(_("grubby: needs=%d, aborting\n"), needs);
3997     abort();
3998     }
3999    
4000 niro 532 if (updateImage(config, "0", prefix, newKernelArgs, NULL,
4001     newMBKernelArgs, NULL)) return 1;
4002    
4003     return 0;
4004     }
4005    
4006 niro 914 static void traceback(int signum)
4007     {
4008     void *array[40];
4009     size_t size;
4010    
4011     signal(SIGSEGV, SIG_DFL);
4012     memset(array, '\0', sizeof (array));
4013     size = backtrace(array, 40);
4014    
4015 niro 2058 fprintf(stderr, "grubby received SIGSEGV! Backtrace (%ld):\n",
4016 niro 914 (unsigned long)size);
4017     backtrace_symbols_fd(array, size, STDERR_FILENO);
4018     exit(1);
4019     }
4020    
4021 niro 532 int main(int argc, const char ** argv) {
4022     poptContext optCon;
4023 niro 1696 const char * grubConfig = NULL;
4024 niro 532 char * outputFile = NULL;
4025     int arg = 0;
4026     int flags = 0;
4027     int badImageOkay = 0;
4028 niro 1696 int configureGrub2 = 0;
4029 niro 532 int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4030     int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4031 niro 914 int configureExtLinux = 0;
4032 niro 532 int bootloaderProbe = 0;
4033 niro 914 int extraInitrdCount = 0;
4034 niro 532 char * updateKernelPath = NULL;
4035     char * newKernelPath = NULL;
4036     char * removeKernelPath = NULL;
4037     char * newKernelArgs = NULL;
4038     char * newKernelInitrd = NULL;
4039     char * newKernelTitle = NULL;
4040     char * newKernelVersion = NULL;
4041     char * newMBKernel = NULL;
4042     char * newMBKernelArgs = NULL;
4043     char * removeMBKernelArgs = NULL;
4044     char * removeMBKernel = NULL;
4045     char * bootPrefix = NULL;
4046     char * defaultKernel = NULL;
4047     char * removeArgs = NULL;
4048     char * kernelInfo = NULL;
4049 niro 914 char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4050 niro 2252 char * envPath = NULL;
4051 niro 532 const char * chptr = NULL;
4052     struct configFileInfo * cfi = NULL;
4053     struct grubConfig * config;
4054     struct singleEntry * template = NULL;
4055     int copyDefault = 0, makeDefault = 0;
4056     int displayDefault = 0;
4057 niro 1720 int displayDefaultIndex = 0;
4058 niro 1721 int displayDefaultTitle = 0;
4059 niro 1859 int defaultIndex = -1;
4060 niro 532 struct poptOption options[] = {
4061     { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4062     _("add an entry for the specified kernel"), _("kernel-path") },
4063     { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
4064     _("add an entry for the specified multiboot kernel"), NULL },
4065     { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
4066     _("default arguments for the new kernel or new arguments for "
4067     "kernel being updated"), _("args") },
4068     { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
4069     _("default arguments for the new multiboot kernel or "
4070     "new arguments for multiboot kernel being updated"), NULL },
4071     { "bad-image-okay", 0, 0, &badImageOkay, 0,
4072     _("don't sanity check images in boot entries (for testing only)"),
4073     NULL },
4074     { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4075     _("filestystem which contains /boot directory (for testing only)"),
4076     _("bootfs") },
4077 niro 1854 #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4078 niro 532 { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4079 niro 1853 _("check which bootloader is installed on boot sector") },
4080 niro 532 #endif
4081     { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4082     _("path to grub config file to update (\"-\" for stdin)"),
4083     _("path") },
4084     { "copy-default", 0, 0, &copyDefault, 0,
4085     _("use the default boot entry as a template for the new entry "
4086     "being added; if the default is not a linux image, or if "
4087     "the kernel referenced by the default image does not exist, "
4088     "the first linux entry whose kernel does exist is used as the "
4089     "template"), NULL },
4090 niro 1736 { "debug", 0, 0, &debug, 0,
4091     _("print debugging information for failures") },
4092 niro 532 { "default-kernel", 0, 0, &displayDefault, 0,
4093     _("display the path of the default kernel") },
4094 niro 1720 { "default-index", 0, 0, &displayDefaultIndex, 0,
4095     _("display the index of the default kernel") },
4096 niro 1721 { "default-title", 0, 0, &displayDefaultTitle, 0,
4097     _("display the title of the default kernel") },
4098 niro 532 { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
4099     _("configure elilo bootloader") },
4100 niro 1940 { "efi", 0, POPT_ARG_NONE, &isEfi, 0,
4101     _("force grub2 stanzas to use efi") },
4102 niro 2252 { "env", 0, POPT_ARG_STRING, &envPath, 0,
4103     _("path for environment data"),
4104     _("path") },
4105 niro 914 { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
4106     _("configure extlinux bootloader (from syslinux)") },
4107 niro 532 { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
4108     _("configure grub bootloader") },
4109 niro 1696 { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
4110     _("configure grub2 bootloader") },
4111 niro 532 { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
4112     _("display boot information for specified kernel"),
4113     _("kernel-path") },
4114     { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
4115     _("initrd image for the new kernel"), _("initrd-path") },
4116 niro 914 { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
4117 niro 2058 _("auxiliary initrd image for things other than the new kernel"), _("initrd-path") },
4118 niro 532 { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
4119     _("configure lilo bootloader") },
4120     { "make-default", 0, 0, &makeDefault, 0,
4121     _("make the newly added entry the default boot entry"), NULL },
4122     { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
4123     _("path to output updated config file (\"-\" for stdout)"),
4124     _("path") },
4125     { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
4126     _("remove kernel arguments"), NULL },
4127     { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
4128     _("remove multiboot kernel arguments"), NULL },
4129     { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
4130     _("remove all entries for the specified kernel"),
4131     _("kernel-path") },
4132     { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
4133     _("remove all entries for the specified multiboot kernel"), NULL },
4134     { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
4135     _("make the first entry referencing the specified kernel "
4136     "the default"), _("kernel-path") },
4137 niro 1859 { "set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
4138     _("make the given entry index the default entry"),
4139     _("entry-index") },
4140 niro 532 { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
4141     _("configure silo bootloader") },
4142     { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
4143     _("title to use for the new kernel entry"), _("entry-title") },
4144     { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
4145     _("updated information for the specified kernel"),
4146     _("kernel-path") },
4147     { "version", 'v', 0, NULL, 'v',
4148     _("print the version of this program and exit"), NULL },
4149     { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
4150     _("configure yaboot bootloader") },
4151     { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
4152     _("configure zipl bootloader") },
4153     POPT_AUTOHELP
4154     { 0, 0, 0, 0, 0 }
4155     };
4156    
4157 niro 914 useextlinuxmenu=0;
4158    
4159     signal(SIGSEGV, traceback);
4160    
4161 niro 2236 int i = 0;
4162     for (int j = 1; j < argc; j++)
4163     i += strlen(argv[j]) + 1;
4164     saved_command_line = malloc(i);
4165     if (!saved_command_line) {
4166     fprintf(stderr, "grubby: %m\n");
4167     exit(1);
4168     }
4169     saved_command_line[0] = '\0';
4170     for (int j = 1; j < argc; j++) {
4171     strcat(saved_command_line, argv[j]);
4172     strncat(saved_command_line, j == argc -1 ? "" : " ", 1);
4173     }
4174    
4175 niro 532 optCon = poptGetContext("grubby", argc, argv, options, 0);
4176     poptReadDefaultConfig(optCon, 1);
4177    
4178     while ((arg = poptGetNextOpt(optCon)) >= 0) {
4179     switch (arg) {
4180     case 'v':
4181     printf("grubby version %s\n", VERSION);
4182     exit(0);
4183     break;
4184 niro 914 case 'i':
4185     if (extraInitrdCount < MAX_EXTRA_INITRDS) {
4186     extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
4187     } else {
4188     fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
4189     return 1;
4190     }
4191     break;
4192 niro 532 }
4193     }
4194    
4195     if (arg < -1) {
4196     fprintf(stderr, _("grubby: bad argument %s: %s\n"),
4197     poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
4198     poptStrerror(arg));
4199     return 1;
4200     }
4201    
4202     if ((chptr = poptGetArg(optCon))) {
4203     fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
4204     return 1;
4205     }
4206    
4207 niro 1696 if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
4208 niro 914 configureYaboot + configureSilo + configureZipl +
4209     configureExtLinux ) > 1) {
4210 niro 532 fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
4211     return 1;
4212     } else if (bootloaderProbe && grubConfig) {
4213     fprintf(stderr,
4214     _("grubby: cannot specify config file with --bootloader-probe\n"));
4215     return 1;
4216 niro 1696 } else if (configureGrub2) {
4217     cfi = &grub2ConfigType;
4218 niro 2252 if (envPath)
4219     cfi->envFile = envPath;
4220 niro 532 } else if (configureLilo) {
4221     cfi = &liloConfigType;
4222     } else if (configureGrub) {
4223     cfi = &grubConfigType;
4224     } else if (configureELilo) {
4225     cfi = &eliloConfigType;
4226     } else if (configureYaboot) {
4227     cfi = &yabootConfigType;
4228     } else if (configureSilo) {
4229     cfi = &siloConfigType;
4230     } else if (configureZipl) {
4231     cfi = &ziplConfigType;
4232 niro 914 } else if (configureExtLinux) {
4233     cfi = &extlinuxConfigType;
4234     useextlinuxmenu=1;
4235 niro 532 }
4236    
4237     if (!cfi) {
4238 niro 1802 if (grub2FindConfig(&grub2ConfigType))
4239     cfi = &grub2ConfigType;
4240     else
4241 niro 532 #ifdef __ia64__
4242 niro 1802 cfi = &eliloConfigType;
4243 niro 532 #elif __powerpc__
4244 niro 1802 cfi = &yabootConfigType;
4245 niro 532 #elif __sparc__
4246 niro 1802 cfi = &siloConfigType;
4247 niro 532 #elif __s390__
4248 niro 1802 cfi = &ziplConfigType;
4249 niro 532 #elif __s390x__
4250 niro 1802 cfi = &ziplConfigtype;
4251 niro 532 #else
4252 niro 1696 cfi = &grubConfigType;
4253 niro 532 #endif
4254     }
4255    
4256 niro 1696 if (!grubConfig) {
4257     if (cfi->findConfig)
4258     grubConfig = cfi->findConfig(cfi);
4259     if (!grubConfig)
4260     grubConfig = cfi->defaultConfig;
4261     }
4262 niro 532
4263     if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||
4264 niro 1859 newKernelPath || removeKernelPath || makeDefault ||
4265     defaultKernel || displayDefaultIndex || displayDefaultTitle ||
4266     (defaultIndex >= 0))) {
4267 niro 532 fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
4268     "specified option"));
4269     return 1;
4270     }
4271    
4272     if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||
4273     removeKernelPath)) {
4274     fprintf(stderr, _("grubby: --default-kernel and --info may not "
4275     "be used when adding or removing kernels\n"));
4276     return 1;
4277     }
4278    
4279     if (newKernelPath && !newKernelTitle) {
4280     fprintf(stderr, _("grubby: kernel title must be specified\n"));
4281     return 1;
4282 niro 1156 } else if (!newKernelPath && (newKernelTitle || copyDefault ||
4283     (newKernelInitrd && !updateKernelPath)||
4284 niro 914 makeDefault || extraInitrdCount > 0)) {
4285 niro 532 fprintf(stderr, _("grubby: kernel path expected\n"));
4286     return 1;
4287     }
4288    
4289     if (newKernelPath && updateKernelPath) {
4290     fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
4291     "not be used together"));
4292     return 1;
4293     }
4294    
4295     if (makeDefault && defaultKernel) {
4296     fprintf(stderr, _("grubby: --make-default and --default-kernel "
4297     "may not be used together\n"));
4298     return 1;
4299     } else if (defaultKernel && removeKernelPath &&
4300     !strcmp(defaultKernel, removeKernelPath)) {
4301     fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));
4302     return 1;
4303     } else if (defaultKernel && newKernelPath &&
4304     !strcmp(defaultKernel, newKernelPath)) {
4305     makeDefault = 1;
4306     defaultKernel = NULL;
4307     }
4308 niro 1859 else if (defaultKernel && (defaultIndex >= 0)) {
4309     fprintf(stderr, _("grubby: --set-default and --set-default-index "
4310     "may not be used together\n"));
4311     return 1;
4312     }
4313 niro 532
4314 niro 1717 if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
4315 niro 532 fprintf(stderr, _("grubby: output file must be specified if stdin "
4316     "is used\n"));
4317     return 1;
4318     }
4319    
4320     if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
4321 niro 1859 && !kernelInfo && !bootloaderProbe && !updateKernelPath
4322     && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle
4323     && (defaultIndex == -1)) {
4324 niro 532 fprintf(stderr, _("grubby: no action specified\n"));
4325     return 1;
4326     }
4327    
4328     flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
4329    
4330     if (cfi->needsBootPrefix) {
4331     if (!bootPrefix) {
4332     bootPrefix = findBootPrefix();
4333     if (!bootPrefix) return 1;
4334     } else {
4335     /* this shouldn't end with a / */
4336     if (bootPrefix[strlen(bootPrefix) - 1] == '/')
4337     bootPrefix[strlen(bootPrefix) - 1] = '\0';
4338     }
4339     } else {
4340     bootPrefix = "";
4341     }
4342    
4343 niro 914 if (!cfi->mbAllowExtraInitRds &&
4344     extraInitrdCount > 0) {
4345     fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
4346     return 1;
4347     }
4348    
4349 niro 532 if (bootloaderProbe) {
4350 niro 1854 int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
4351     struct grubConfig * lconfig, * gconfig, * yconfig, * econfig;
4352 niro 532
4353 niro 1696 const char *grub2config = grub2FindConfig(&grub2ConfigType);
4354     if (grub2config) {
4355     gconfig = readConfig(grub2config, &grub2ConfigType);
4356     if (!gconfig)
4357     gr2c = 1;
4358     else
4359     gr2c = checkForGrub2(gconfig);
4360     }
4361    
4362 niro 1715 const char *grubconfig = grubFindConfig(&grubConfigType);
4363     if (!access(grubconfig, F_OK)) {
4364     gconfig = readConfig(grubconfig, &grubConfigType);
4365 niro 532 if (!gconfig)
4366     grc = 1;
4367     else
4368     grc = checkForGrub(gconfig);
4369     }
4370    
4371     if (!access(liloConfigType.defaultConfig, F_OK)) {
4372     lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);
4373     if (!lconfig)
4374     lrc = 1;
4375     else
4376     lrc = checkForLilo(lconfig);
4377     }
4378    
4379 niro 1854 if (!access(eliloConfigType.defaultConfig, F_OK)) {
4380     econfig = readConfig(eliloConfigType.defaultConfig,
4381     &eliloConfigType);
4382     if (!econfig)
4383     erc = 1;
4384     else
4385     erc = checkForElilo(econfig);
4386     }
4387    
4388 niro 914 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
4389     lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
4390     if (!lconfig)
4391 niro 1854 extrc = 1;
4392 niro 914 else
4393 niro 1854 extrc = checkForExtLinux(lconfig);
4394 niro 914 }
4395    
4396 niro 532
4397 niro 1853 if (!access(yabootConfigType.defaultConfig, F_OK)) {
4398     yconfig = readConfig(yabootConfigType.defaultConfig,
4399     &yabootConfigType);
4400     if (!yconfig)
4401     yrc = 1;
4402     else
4403 niro 1864 yrc = checkForYaboot(yconfig);
4404 niro 1853 }
4405    
4406 niro 1854 if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1 ||
4407 niro 1855 erc == 1)
4408 niro 1854 return 1;
4409 niro 1853
4410 niro 532 if (lrc == 2) printf("lilo\n");
4411 niro 1696 if (gr2c == 2) printf("grub2\n");
4412 niro 532 if (grc == 2) printf("grub\n");
4413 niro 1854 if (extrc == 2) printf("extlinux\n");
4414 niro 1853 if (yrc == 2) printf("yaboot\n");
4415 niro 1854 if (erc == 2) printf("elilo\n");
4416 niro 532
4417     return 0;
4418     }
4419    
4420 niro 2246 if (grubConfig == NULL) {
4421     printf("Could not find bootloader configuration file.\n");
4422     exit(1);
4423     }
4424    
4425 niro 532 config = readConfig(grubConfig, cfi);
4426     if (!config) return 1;
4427    
4428     if (displayDefault) {
4429     struct singleLine * line;
4430     struct singleEntry * entry;
4431     char * rootspec;
4432    
4433     if (config->defaultImage == -1) return 0;
4434 niro 2255 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4435     cfi->defaultIsSaved)
4436     config->defaultImage = 0;
4437 niro 532 entry = findEntryByIndex(config, config->defaultImage);
4438     if (!entry) return 0;
4439     if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
4440    
4441 niro 1940 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI, entry->lines);
4442 niro 532 if (!line) return 0;
4443    
4444     rootspec = getRootSpecifier(line->elements[1].item);
4445     printf("%s%s\n", bootPrefix, line->elements[1].item +
4446     ((rootspec != NULL) ? strlen(rootspec) : 0));
4447    
4448     return 0;
4449 niro 1720
4450 niro 1721 } else if (displayDefaultTitle) {
4451     struct singleLine * line;
4452     struct singleEntry * entry;
4453    
4454     if (config->defaultImage == -1) return 0;
4455 niro 2255 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4456     cfi->defaultIsSaved)
4457     config->defaultImage = 0;
4458 niro 1721 entry = findEntryByIndex(config, config->defaultImage);
4459     if (!entry) return 0;
4460    
4461     if (!configureGrub2) {
4462     line = getLineByType(LT_TITLE, entry->lines);
4463     if (!line) return 0;
4464     printf("%s\n", line->elements[1].item);
4465    
4466     } else {
4467 niro 1746 char * title;
4468 niro 1721
4469     dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
4470     line = getLineByType(LT_MENUENTRY, entry->lines);
4471     if (!line) return 0;
4472 niro 1746 title = grub2ExtractTitle(line);
4473     if (title)
4474     printf("%s\n", title);
4475 niro 1721 }
4476     return 0;
4477    
4478 niro 1720 } else if (displayDefaultIndex) {
4479     if (config->defaultImage == -1) return 0;
4480 niro 2255 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4481     cfi->defaultIsSaved)
4482     config->defaultImage = 0;
4483 niro 1720 printf("%i\n", config->defaultImage);
4484 niro 2250 return 0;
4485 niro 1720
4486 niro 532 } else if (kernelInfo)
4487     return displayInfo(config, kernelInfo, bootPrefix);
4488    
4489     if (copyDefault) {
4490     template = findTemplate(config, bootPrefix, NULL, 0, flags);
4491     if (!template) return 1;
4492     }
4493    
4494     markRemovedImage(config, removeKernelPath, bootPrefix);
4495     markRemovedImage(config, removeMBKernel, bootPrefix);
4496     setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
4497 niro 1859 bootPrefix, flags, defaultIndex);
4498 niro 532 setFallbackImage(config, newKernelPath != NULL);
4499     if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
4500     removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
4501 niro 1156 if (updateKernelPath && newKernelInitrd) {
4502     if (updateInitrd(config, updateKernelPath, bootPrefix,
4503     newKernelInitrd)) return 1;
4504     }
4505 niro 532 if (addNewKernel(config, template, bootPrefix, newKernelPath,
4506     newKernelTitle, newKernelArgs, newKernelInitrd,
4507 niro 1844 (const char **)extraInitrds, extraInitrdCount,
4508 niro 532 newMBKernel, newMBKernelArgs)) return 1;
4509    
4510    
4511     if (numEntries(config) == 0) {
4512     fprintf(stderr, _("grubby: doing this would leave no kernel entries. "
4513     "Not writing out new config.\n"));
4514     return 1;
4515     }
4516    
4517     if (!outputFile)
4518 niro 1696 outputFile = (char *)grubConfig;
4519 niro 532
4520     return writeConfig(config, outputFile, bootPrefix);
4521     }