Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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