Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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