Magellan Linux

Annotation of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3136 - (hide annotations) (download)
Tue Jul 7 11:09:37 2020 UTC (3 years, 10 months ago) by niro
File MIME type: text/plain
File size: 135671 byte(s)
Fix incorrect test case and --remove-args with a value.
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 niro 3025 int trueIndex, currentIndex;
1716    
1717     trueIndex = 0;
1718     currentIndex = 0;
1719    
1720     while ((entry = findEntryByIndex(cfg, currentIndex))) {
1721     if (!entry->skip) {
1722     if (trueIndex == cfg->defaultImage) {
1723     break;
1724     }
1725     trueIndex++;
1726     }
1727     currentIndex++;
1728     }
1729 niro 3002 line = getLineByType(LT_MENUENTRY, entry->lines);
1730     if (!line)
1731     line = getLineByType(LT_TITLE, entry->lines);
1732     if (line) {
1733     title = extractTitle(cfg, line);
1734     if (title)
1735     cfg->cfi->setEnv(cfg->cfi,
1736     "saved_entry", title);
1737     }
1738     }
1739 niro 3018 } else if (cfg->defaultImage >= FIRST_ENTRY_INDEX) {
1740 niro 3002 if (cfg->cfi->defaultIsIndex) {
1741     if (cfg->cfi->defaultIsVariable) {
1742     fprintf(out, "%sset default=\"%d\"\n", indent,
1743     cfg->defaultImage);
1744     } else {
1745     fprintf(out, "%sdefault%s%d\n", indent,
1746     separator, cfg->defaultImage);
1747     }
1748     } else {
1749     int image = cfg->defaultImage;
1750 niro 532
1751 niro 3002 entry = cfg->entries;
1752     while (entry && entry->skip)
1753     entry = entry->next;
1754 niro 532
1755 niro 3002 i = 0;
1756     while (entry && i < image) {
1757     entry = entry->next;
1758 niro 532
1759 niro 3002 while (entry && entry->skip)
1760     entry = entry->next;
1761     i++;
1762     }
1763 niro 532
1764 niro 3002 if (!entry)
1765     return;
1766 niro 532
1767 niro 3002 line = getLineByType(LT_TITLE, entry->lines);
1768 niro 532
1769 niro 3002 if (line && line->numElements >= 2)
1770     fprintf(out, "%sdefault%s%s\n", indent,
1771     separator, line->elements[1].item);
1772     else if (line && (line->numElements == 1)
1773     && cfg->cfi->titleBracketed) {
1774     char *title = extractTitle(cfg, line);
1775     if (title) {
1776     fprintf(out, "%sdefault%s%s\n", indent,
1777     separator, title);
1778     free(title);
1779     }
1780     }
1781 niro 2995 }
1782 niro 532 }
1783     }
1784    
1785 niro 3002 static int writeConfig(struct grubConfig *cfg, char *outName,
1786     const char *prefix)
1787     {
1788     FILE *out;
1789     struct singleLine *line;
1790     struct singleEntry *entry;
1791     char *tmpOutName;
1792     int needs = MAIN_DEFAULT;
1793     struct stat sb;
1794     int i;
1795 niro 3135 int rc = 0;
1796 niro 532
1797 niro 3002 if (!strcmp(outName, "-")) {
1798     out = stdout;
1799     tmpOutName = NULL;
1800     } else {
1801     if (!lstat(outName, &sb) && S_ISLNK(sb.st_mode)) {
1802     char *buf;
1803     int len = 256;
1804     int rc;
1805 niro 532
1806 niro 3002 /* most likely the symlink is relative, so change our
1807     directory to the dir of the symlink */
1808     char *dir = strdupa(outName);
1809     rc = chdir(dirname(dir));
1810     do {
1811     buf = alloca(len + 1);
1812     rc = readlink(basename(outName), buf, len);
1813     if (rc == len)
1814     len += 256;
1815     } while (rc == len);
1816 niro 532
1817 niro 3002 if (rc < 0) {
1818     fprintf(stderr,
1819     _
1820     ("grubby: error readlink link %s: %s\n"),
1821     outName, strerror(errno));
1822     return 1;
1823     }
1824 niro 532
1825 niro 3002 outName = buf;
1826     outName[rc] = '\0';
1827     }
1828    
1829     tmpOutName = alloca(strlen(outName) + 2);
1830     sprintf(tmpOutName, "%s-", outName);
1831     out = fopen(tmpOutName, "w");
1832     if (!out) {
1833     fprintf(stderr, _("grubby: error creating %s: %s\n"),
1834     tmpOutName, strerror(errno));
1835     return 1;
1836     }
1837    
1838     if (!stat(outName, &sb)) {
1839     if (chmod(tmpOutName, sb.st_mode & ~(S_IFMT))) {
1840     fprintf(stderr,
1841     _
1842     ("grubby: error setting perms on %s: %s\n"),
1843     tmpOutName, strerror(errno));
1844     fclose(out);
1845     unlink(tmpOutName);
1846     return 1;
1847     }
1848     }
1849 niro 532 }
1850    
1851 niro 3002 line = cfg->theLines;
1852     struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi);
1853     while (line) {
1854     if (line->type == LT_SET_VARIABLE && defaultKw &&
1855     line->numElements == 3 &&
1856     !strcmp(line->elements[1].item, defaultKw->key) &&
1857     !is_special_grub2_variable(line->elements[2].item)) {
1858     writeDefault(out, line->indent,
1859     line->elements[0].indent, cfg);
1860     needs &= ~MAIN_DEFAULT;
1861     } else if (line->type == LT_DEFAULT) {
1862     writeDefault(out, line->indent,
1863     line->elements[0].indent, cfg);
1864     needs &= ~MAIN_DEFAULT;
1865     } else if (line->type == LT_FALLBACK) {
1866     if (cfg->fallbackImage > -1)
1867     fprintf(out, "%s%s%s%d\n", line->indent,
1868     line->elements[0].item,
1869     line->elements[0].indent,
1870     cfg->fallbackImage);
1871     } else {
1872     if (lineWrite(out, line, cfg->cfi) == -1) {
1873     fprintf(stderr,
1874     _("grubby: error writing %s: %s\n"),
1875     tmpOutName, strerror(errno));
1876     fclose(out);
1877     unlink(tmpOutName);
1878     return 1;
1879     }
1880     }
1881 niro 532
1882 niro 3002 line = line->next;
1883 niro 532 }
1884    
1885 niro 3002 if (needs & MAIN_DEFAULT) {
1886     writeDefault(out, cfg->primaryIndent, "=", cfg);
1887     needs &= ~MAIN_DEFAULT;
1888     }
1889 niro 532
1890 niro 3002 i = 0;
1891     while ((entry = findEntryByIndex(cfg, i++))) {
1892     if (entry->skip)
1893     continue;
1894 niro 532
1895 niro 3002 line = entry->lines;
1896     while (line) {
1897     if (lineWrite(out, line, cfg->cfi) == -1) {
1898     fprintf(stderr,
1899     _("grubby: error writing %s: %s\n"),
1900     tmpOutName, strerror(errno));
1901     fclose(out);
1902     unlink(tmpOutName);
1903     return 1;
1904     }
1905     line = line->next;
1906     }
1907 niro 532 }
1908    
1909 niro 3002 if (tmpOutName) {
1910 niro 3135 /* write userspace buffers */
1911     if (fflush(out))
1912     rc = 1;
1913    
1914     /* purge the write-back cache with fsync() */
1915     if (fsync(fileno(out)))
1916     rc = 1;
1917    
1918     if (fclose(out))
1919     rc = 1;
1920    
1921     if (rc == 0 && rename(tmpOutName, outName)) {
1922     unlink(tmpOutName);
1923     rc = 1;
1924     }
1925    
1926     /* fsync() the destination directory after rename */
1927     if (rc == 0) {
1928     int dirfd;
1929    
1930     dirfd = open(dirname(strdupa(outName)), O_RDONLY);
1931     if (dirfd < 0)
1932     rc = 1;
1933     else if (fsync(dirfd))
1934     rc = 1;
1935    
1936     if (dirfd >= 0)
1937     close(dirfd);
1938     }
1939    
1940     if (rc == 1)
1941 niro 3002 fprintf(stderr,
1942 niro 3135 _("grubby: error flushing data: %m\n"));
1943 niro 532 }
1944    
1945 niro 3135 return rc;
1946 niro 532 }
1947    
1948 niro 3002 static int numEntries(struct grubConfig *cfg)
1949     {
1950     int i = 0;
1951     struct singleEntry *entry;
1952 niro 532
1953 niro 3002 entry = cfg->entries;
1954     while (entry) {
1955     if (!entry->skip)
1956     i++;
1957     entry = entry->next;
1958     }
1959     return i;
1960 niro 532 }
1961    
1962 niro 1156 static char *findDiskForRoot()
1963     {
1964 niro 3002 int fd;
1965     char buf[65536];
1966     char *devname;
1967     char *chptr;
1968     int rc;
1969 niro 1156
1970 niro 3002 if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {
1971     fprintf(stderr, "grubby: failed to open %s: %s\n",
1972     _PATH_MOUNTED, strerror(errno));
1973     return NULL;
1974     }
1975 niro 1156
1976 niro 3002 rc = read(fd, buf, sizeof(buf) - 1);
1977     if (rc <= 0) {
1978     fprintf(stderr, "grubby: failed to read %s: %s\n",
1979     _PATH_MOUNTED, strerror(errno));
1980     close(fd);
1981     return NULL;
1982     }
1983     close(fd);
1984     buf[rc] = '\0';
1985     chptr = buf;
1986 niro 1156
1987 niro 3002 char *foundanswer = NULL;
1988 niro 1841
1989 niro 3002 while (chptr && chptr != buf + rc) {
1990     devname = chptr;
1991 niro 1156
1992 niro 3002 /*
1993     * The first column of a mtab entry is the device, but if the
1994     * entry is a special device it won't start with /, so move
1995     * on to the next line.
1996     */
1997     if (*devname != '/') {
1998     chptr = strchr(chptr, '\n');
1999     if (chptr)
2000     chptr++;
2001     continue;
2002     }
2003 niro 1156
2004 niro 3002 /* Seek to the next space */
2005     chptr = strchr(chptr, ' ');
2006     if (!chptr) {
2007     fprintf(stderr, "grubby: error parsing %s: %s\n",
2008     _PATH_MOUNTED, strerror(errno));
2009     return NULL;
2010     }
2011 niro 1156
2012 niro 3002 /*
2013     * The second column of a mtab entry is the mount point, we
2014     * are looking for '/' obviously.
2015     */
2016     if (*(++chptr) == '/' && *(++chptr) == ' ') {
2017     /* remember the last / entry in mtab */
2018     foundanswer = devname;
2019     }
2020 niro 1156
2021 niro 3002 /* Next line */
2022     chptr = strchr(chptr, '\n');
2023     if (chptr)
2024     chptr++;
2025     }
2026 niro 1156
2027 niro 3002 /* Return the last / entry found */
2028     if (foundanswer) {
2029     chptr = strchr(foundanswer, ' ');
2030     *chptr = '\0';
2031     return strdup(foundanswer);
2032     }
2033 niro 1841
2034 niro 3002 return NULL;
2035 niro 1156 }
2036    
2037 niro 3002 void printEntry(struct singleEntry *entry, FILE * f)
2038     {
2039     int i;
2040     struct singleLine *line;
2041 niro 1736
2042 niro 3002 for (line = entry->lines; line; line = line->next) {
2043     log_message(f, "DBG: %s", line->indent);
2044     for (i = 0; i < line->numElements; i++) {
2045     /* Need to handle this, because we strip the quotes from
2046     * menuentry when read it. */
2047     if (line->type == LT_MENUENTRY && i == 1) {
2048     if (!isquote(*line->elements[i].item))
2049     log_message(f, "\'%s\'",
2050     line->elements[i].item);
2051     else
2052     log_message(f, "%s",
2053     line->elements[i].item);
2054     log_message(f, "%s", line->elements[i].indent);
2055    
2056     continue;
2057     }
2058    
2059     log_message(f, "%s%s",
2060     line->elements[i].item,
2061     line->elements[i].indent);
2062     }
2063     log_message(f, "\n");
2064 niro 1736 }
2065     }
2066    
2067 niro 3002 void notSuitablePrintf(struct singleEntry *entry, int okay, const char *fmt,
2068     ...)
2069 niro 1736 {
2070 niro 3002 static int once;
2071     va_list argp, argq;
2072 niro 1736
2073 niro 3002 va_start(argp, fmt);
2074 niro 2236
2075 niro 3002 va_copy(argq, argp);
2076     if (!once) {
2077     log_time(NULL);
2078     log_message(NULL, "command line: %s\n", saved_command_line);
2079     }
2080     log_message(NULL, "DBG: Image entry %s: ",
2081     okay ? "succeeded" : "failed");
2082     log_vmessage(NULL, fmt, argq);
2083 niro 2236
2084 niro 3002 printEntry(entry, NULL);
2085     va_end(argq);
2086    
2087     if (!debug) {
2088     once = 1;
2089     va_end(argp);
2090     return;
2091     }
2092    
2093     if (okay) {
2094     va_end(argp);
2095     return;
2096     }
2097    
2098     if (!once)
2099     log_message(stderr, "DBG: command line: %s\n",
2100     saved_command_line);
2101 niro 2236 once = 1;
2102 niro 3002 fprintf(stderr, "DBG: Image entry failed: ");
2103     vfprintf(stderr, fmt, argp);
2104     printEntry(entry, stderr);
2105 niro 2236 va_end(argp);
2106 niro 1736 }
2107    
2108 niro 1745 #define beginswith(s, c) ((s) && (s)[0] == (c))
2109    
2110     static int endswith(const char *s, char c)
2111     {
2112     int slen;
2113    
2114 niro 1750 if (!s || !s[0])
2115 niro 1745 return 0;
2116     slen = strlen(s) - 1;
2117    
2118     return s[slen] == c;
2119     }
2120    
2121 niro 3002 int suitableImage(struct singleEntry *entry, const char *bootPrefix,
2122     int skipRemoved, int flags)
2123     {
2124     struct singleLine *line;
2125     char *fullName;
2126     int i;
2127     char *dev;
2128     char *rootspec;
2129     char *rootdev;
2130 niro 532
2131 niro 3002 if (skipRemoved && entry->skip) {
2132     notSuitablePrintf(entry, 0, "marked to skip\n");
2133     return 0;
2134     }
2135 niro 532
2136 niro 3002 line =
2137     getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI | LT_KERNEL_16,
2138     entry->lines);
2139     if (!line) {
2140     notSuitablePrintf(entry, 0, "no line found\n");
2141     return 0;
2142     }
2143     if (line->numElements < 2) {
2144     notSuitablePrintf(entry, 0, "line has only %d elements\n",
2145     line->numElements);
2146     return 0;
2147     }
2148 niro 914
2149 niro 3002 if (flags & GRUBBY_BADIMAGE_OKAY) {
2150     notSuitablePrintf(entry, 1, "\n");
2151     return 1;
2152     }
2153 niro 532
2154 niro 3002 fullName = alloca(strlen(bootPrefix) +
2155     strlen(line->elements[1].item) + 1);
2156     rootspec = getRootSpecifier(line->elements[1].item);
2157     int rootspec_offset = rootspec ? strlen(rootspec) : 0;
2158     int hasslash = endswith(bootPrefix, '/') ||
2159     beginswith(line->elements[1].item + rootspec_offset, '/');
2160     sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/",
2161     line->elements[1].item + rootspec_offset);
2162     if (access(fullName, R_OK)) {
2163     notSuitablePrintf(entry, 0, "access to %s failed\n", fullName);
2164     return 0;
2165     }
2166     for (i = 2; i < line->numElements; i++)
2167     if (!strncasecmp(line->elements[i].item, "root=", 5))
2168     break;
2169     if (i < line->numElements) {
2170     dev = line->elements[i].item + 5;
2171 niro 532 } else {
2172 niro 3002 /* look for a lilo style LT_ROOT line */
2173     line = getLineByType(LT_ROOT, entry->lines);
2174 niro 532
2175 niro 3002 if (line && line->numElements >= 2) {
2176     dev = line->elements[1].item;
2177     } else {
2178     /* didn't succeed in finding a LT_ROOT, let's try
2179     * LT_KERNELARGS. grub+multiboot uses LT_MBMODULE
2180     * for the args, so check that too.
2181     */
2182     line =
2183     getLineByType(LT_KERNELARGS | LT_MBMODULE,
2184     entry->lines);
2185 niro 532
2186 niro 3002 /* failed to find one */
2187     if (!line) {
2188     notSuitablePrintf(entry, 0, "no line found\n");
2189     return 0;
2190     }
2191    
2192     for (i = 1; i < line->numElements; i++)
2193     if (!strncasecmp
2194     (line->elements[i].item, "root=", 5))
2195     break;
2196     if (i < line->numElements)
2197     dev = line->elements[i].item + 5;
2198     else {
2199     notSuitablePrintf(entry, 0,
2200     "no root= entry found\n");
2201     /* it failed too... can't find root= */
2202     return 0;
2203     }
2204     }
2205 niro 532 }
2206    
2207 niro 1736 dev = getpathbyspec(dev);
2208 niro 3002 if (!getpathbyspec(dev)) {
2209     notSuitablePrintf(entry, 0, "can't find blkid entry for %s\n",
2210     dev);
2211     return 0;
2212     } else
2213     dev = getpathbyspec(dev);
2214 niro 532
2215 niro 3002 rootdev = findDiskForRoot();
2216     if (!rootdev) {
2217     notSuitablePrintf(entry, 0, "can't find root device\n");
2218     return 0;
2219     }
2220 niro 914
2221 niro 3002 if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {
2222     notSuitablePrintf(entry, 0,
2223     "uuid missing: rootdev %s, dev %s\n",
2224     getuuidbydev(rootdev), getuuidbydev(dev));
2225     free(rootdev);
2226     return 0;
2227     }
2228 niro 532
2229 niro 3002 if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
2230     notSuitablePrintf(entry, 0,
2231     "uuid mismatch: rootdev %s, dev %s\n",
2232     getuuidbydev(rootdev), getuuidbydev(dev));
2233     free(rootdev);
2234     return 0;
2235     }
2236    
2237 niro 1156 free(rootdev);
2238 niro 3002 notSuitablePrintf(entry, 1, "\n");
2239 niro 532
2240 niro 3002 return 1;
2241 niro 532 }
2242    
2243     /* returns the first match on or after the one pointed to by index (if index
2244     is not NULL) which is not marked as skip */
2245 niro 3002 struct singleEntry *findEntryByPath(struct grubConfig *config,
2246     const char *kernel, const char *prefix,
2247     int *index)
2248     {
2249     struct singleEntry *entry = NULL;
2250     struct singleLine *line;
2251     int i;
2252     char *chptr;
2253     char *rootspec = NULL;
2254     enum lineType_e checkType = LT_KERNEL;
2255 niro 532
2256 niro 3002 if (isdigit(*kernel)) {
2257     int *indexVars = alloca(sizeof(*indexVars) * strlen(kernel));
2258 niro 532
2259 niro 3002 i = 0;
2260     indexVars[i] = strtol(kernel, &chptr, 10);
2261     while (*chptr == ',') {
2262     i++;
2263     kernel = chptr + 1;
2264     indexVars[i] = strtol(kernel, &chptr, 10);
2265     }
2266 niro 532
2267 niro 3002 if (*chptr) {
2268     /* can't parse it, bail */
2269     return NULL;
2270     }
2271 niro 532
2272 niro 3002 indexVars[i + 1] = -1;
2273 niro 2718
2274 niro 3002 i = 0;
2275     if (index) {
2276     while (i < *index) {
2277     i++;
2278     if (indexVars[i] == -1)
2279     return NULL;
2280     }
2281     }
2282    
2283     entry = findEntryByIndex(config, indexVars[i]);
2284     if (!entry)
2285     return NULL;
2286    
2287     line =
2288     getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI |
2289     LT_KERNEL_16, entry->lines);
2290     if (!line)
2291     return NULL;
2292    
2293     if (index)
2294     *index = indexVars[i];
2295     return entry;
2296 niro 532 }
2297    
2298 niro 3002 if (!strcmp(kernel, "DEFAULT")) {
2299     if (index && *index > config->defaultImage) {
2300     entry = NULL;
2301     } else {
2302     entry = findEntryByIndex(config, config->defaultImage);
2303     if (entry && entry->skip)
2304     entry = NULL;
2305     else if (index)
2306     *index = config->defaultImage;
2307     }
2308     } else if (!strcmp(kernel, "ALL")) {
2309     if (index)
2310     i = *index;
2311     else
2312     i = 0;
2313 niro 532
2314 niro 3002 while ((entry = findEntryByIndex(config, i))) {
2315     if (!entry->skip)
2316     break;
2317     i++;
2318     }
2319 niro 532
2320 niro 3002 if (entry && index)
2321     *index = i;
2322 niro 532 } else {
2323 niro 3002 if (index)
2324     i = *index;
2325     else
2326     i = 0;
2327 niro 532
2328 niro 3002 if (!strncmp(kernel, "TITLE=", 6)) {
2329     prefix = "";
2330     checkType = LT_TITLE | LT_MENUENTRY;
2331     kernel += 6;
2332     }
2333 niro 532
2334 niro 3002 for (entry = findEntryByIndex(config, i); entry;
2335     entry = entry->next, i++) {
2336     if (entry->skip)
2337     continue;
2338 niro 532
2339 niro 3002 dbgPrintf("findEntryByPath looking for %d %s in %p\n",
2340     checkType, kernel, entry);
2341 niro 532
2342 niro 3002 /* check all the lines matching checkType */
2343     for (line = entry->lines; line; line = line->next) {
2344     enum lineType_e ct = checkType;
2345     if (entry->multiboot && checkType == LT_KERNEL)
2346     ct = LT_KERNEL | LT_KERNEL_EFI |
2347     LT_MBMODULE | LT_HYPER |
2348     LT_KERNEL_16;
2349     else if (checkType & LT_KERNEL)
2350     ct = checkType | LT_KERNEL_EFI |
2351     LT_KERNEL_16;
2352     line = getLineByType(ct, line);
2353     if (!line)
2354     break; /* not found in this entry */
2355 niro 532
2356 niro 3002 if (line && line->type != LT_MENUENTRY &&
2357     line->numElements >= 2) {
2358     rootspec =
2359     getRootSpecifier(line->elements[1].
2360     item);
2361     if (!strcmp
2362     (line->elements[1].item +
2363     ((rootspec !=
2364     NULL) ? strlen(rootspec) : 0),
2365     kernel + strlen(prefix)))
2366     break;
2367     }
2368     if (line->type == LT_MENUENTRY &&
2369     !strcmp(line->elements[1].item, kernel))
2370     break;
2371     }
2372 niro 532
2373 niro 3002 /* make sure this entry has a kernel identifier; this skips
2374     * non-Linux boot entries (could find netbsd etc, though, which is
2375     * unfortunate)
2376     */
2377     if (line
2378     && getLineByType(LT_KERNEL | LT_HYPER |
2379     LT_KERNEL_EFI | LT_KERNEL_16,
2380     entry->lines))
2381     break; /* found 'im! */
2382 niro 914 }
2383    
2384 niro 3002 if (index)
2385     *index = i;
2386 niro 532 }
2387    
2388 niro 3002 return entry;
2389 niro 532 }
2390    
2391 niro 3002 struct singleEntry *findEntryByTitle(struct grubConfig *cfg, char *title,
2392     int *index)
2393     {
2394     struct singleEntry *entry;
2395     struct singleLine *line;
2396     int i;
2397     char *newtitle;
2398 niro 2252
2399 niro 3002 for (i = 0, entry = cfg->entries; entry; entry = entry->next, i++) {
2400     if (index && i < *index)
2401     continue;
2402     line = getLineByType(LT_TITLE, entry->lines);
2403     if (!line)
2404     line = getLineByType(LT_MENUENTRY, entry->lines);
2405     if (!line)
2406     continue;
2407     newtitle = grub2ExtractTitle(line);
2408     if (!newtitle)
2409     continue;
2410     if (!strcmp(title, newtitle))
2411     break;
2412     }
2413 niro 2252
2414 niro 3002 if (!entry)
2415     return NULL;
2416 niro 2252
2417 niro 3002 if (index)
2418     *index = i;
2419     return entry;
2420 niro 2252 }
2421    
2422 niro 3002 struct singleEntry *findEntryByIndex(struct grubConfig *cfg, int index)
2423     {
2424     struct singleEntry *entry;
2425 niro 532
2426 niro 3002 entry = cfg->entries;
2427     while (index && entry) {
2428     entry = entry->next;
2429     index--;
2430     }
2431 niro 532
2432 niro 3002 return entry;
2433 niro 532 }
2434    
2435     /* Find a good template to use for the new kernel. An entry is
2436     * good if the kernel and mkinitrd exist (even if the entry
2437     * is going to be removed). Try and use the default entry, but
2438     * if that doesn't work just take the first. If we can't find one,
2439     * bail. */
2440 niro 3002 struct singleEntry *findTemplate(struct grubConfig *cfg, const char *prefix,
2441     int *indexPtr, int skipRemoved, int flags)
2442     {
2443     struct singleEntry *entry, *entry2;
2444     int index;
2445 niro 532
2446 niro 3002 if (cfg->cfi->defaultIsSaved) {
2447     if (cfg->cfi->getEnv) {
2448     char *defTitle =
2449     cfg->cfi->getEnv(cfg->cfi, "saved_entry");
2450     if (defTitle) {
2451     int index = 0;
2452     if (isnumber(defTitle)) {
2453     index = atoi(defTitle);
2454     entry = findEntryByIndex(cfg, index);
2455     } else {
2456     entry =
2457     findEntryByTitle(cfg, defTitle,
2458     &index);
2459     }
2460     if (entry
2461     && suitableImage(entry, prefix, skipRemoved,
2462     flags)) {
2463     cfg->defaultImage = index;
2464     if (indexPtr)
2465     *indexPtr = index;
2466     return entry;
2467     }
2468     }
2469 niro 2258 }
2470 niro 3018 } else if (cfg->defaultImage >= FIRST_ENTRY_INDEX) {
2471 niro 3002 entry = findEntryByIndex(cfg, cfg->defaultImage);
2472 niro 2959 if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2473 niro 3002 if (indexPtr)
2474     *indexPtr = cfg->defaultImage;
2475     return entry;
2476 niro 2959 }
2477 niro 2252 }
2478 niro 532
2479 niro 3002 index = 0;
2480     while ((entry = findEntryByIndex(cfg, index))) {
2481     if (suitableImage(entry, prefix, skipRemoved, flags)) {
2482 niro 3024 int j, unmodifiedIndex;
2483    
2484     unmodifiedIndex = index;
2485    
2486     for (j = 0; j < unmodifiedIndex; j++) {
2487 niro 3002 entry2 = findEntryByIndex(cfg, j);
2488     if (entry2->skip)
2489     index--;
2490     }
2491     if (indexPtr)
2492     *indexPtr = index;
2493 niro 532
2494 niro 3002 return entry;
2495     }
2496    
2497     index++;
2498 niro 532 }
2499    
2500 niro 3002 fprintf(stderr,
2501     _("grubby fatal error: unable to find a suitable template\n"));
2502 niro 532
2503 niro 3002 return NULL;
2504 niro 532 }
2505    
2506 niro 3002 char *findBootPrefix(void)
2507     {
2508     struct stat sb, sb2;
2509 niro 532
2510 niro 3002 stat("/", &sb);
2511 niro 532 #ifdef __ia64__
2512 niro 3002 stat("/boot/efi/EFI/redhat/", &sb2);
2513 niro 532 #else
2514 niro 3002 stat("/boot", &sb2);
2515 niro 532 #endif
2516    
2517 niro 3002 if (sb.st_dev == sb2.st_dev)
2518     return strdup("");
2519 niro 532
2520     #ifdef __ia64__
2521 niro 3002 return strdup("/boot/efi/EFI/redhat/");
2522 niro 532 #else
2523 niro 3002 return strdup("/boot");
2524 niro 532 #endif
2525     }
2526    
2527 niro 3002 void markRemovedImage(struct grubConfig *cfg, const char *image,
2528     const char *prefix)
2529     {
2530     struct singleEntry *entry;
2531 niro 532
2532 niro 3002 if (!image)
2533     return;
2534 niro 532
2535 niro 3002 /* check and see if we're removing the default image */
2536     if (isdigit(*image)) {
2537     entry = findEntryByPath(cfg, image, prefix, NULL);
2538     if (entry)
2539     entry->skip = 1;
2540     return;
2541     }
2542 niro 1801
2543 niro 3002 while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
2544     entry->skip = 1;
2545 niro 532 }
2546    
2547 niro 3021 void setDefaultImage(struct grubConfig *config, int isAddingBootEntry,
2548 niro 3017 const char *defaultKernelPath, int newBootEntryIsDefault,
2549 niro 3021 const char *prefix, int flags,
2550     int newDefaultBootEntryIndex, int newBootEntryIndex)
2551 niro 3002 {
2552 niro 3021 struct singleEntry *bootEntry, *newDefault;
2553     int indexToVerify, firstKernelEntryIndex, currentLookupIndex;
2554 niro 532
2555 niro 3027 /* initialize */
2556     currentLookupIndex = FIRST_ENTRY_INDEX;
2557    
2558 niro 3021 /* handle the two cases where the user explictly picks the default
2559     * boot entry index as it would exist post-modification */
2560    
2561     /* Case 1: user chose to make the latest boot entry the default */
2562 niro 3017 if (newBootEntryIsDefault) {
2563 niro 3021 config->defaultImage = newBootEntryIndex;
2564 niro 3002 return;
2565 niro 3021 }
2566    
2567     /* Case 2: user picked an arbitrary index as the default boot entry */
2568 niro 3031 if (newDefaultBootEntryIndex >= FIRST_ENTRY_INDEX) {
2569 niro 3021 indexToVerify = newDefaultBootEntryIndex;
2570    
2571     /* user chose to make latest boot entry the default */
2572     if (newDefaultBootEntryIndex == newBootEntryIndex) {
2573     config->defaultImage = newBootEntryIndex;
2574     return;
2575     }
2576    
2577     /* the user picks the default index based on the
2578     * order of the bootloader configuration after
2579     * modification; ensure we are checking for the
2580     * existence of the correct entry */
2581     if (newBootEntryIndex < newDefaultBootEntryIndex) {
2582     if (!config->isModified)
2583     indexToVerify--;
2584     }
2585    
2586     /* verify the user selected index will exist */
2587     if (findEntryByIndex(config, indexToVerify)) {
2588 niro 3017 config->defaultImage = newDefaultBootEntryIndex;
2589 niro 3021 } else {
2590 niro 3018 config->defaultImage = NO_DEFAULT_ENTRY;
2591 niro 3021 }
2592    
2593 niro 3002 return;
2594 niro 3021 }
2595    
2596     /* handle cases where the index value may shift */
2597    
2598     /* check validity of existing default or first-entry-found
2599     selection */
2600     if (defaultKernelPath) {
2601 niro 3026 /* we must initialize this */
2602     firstKernelEntryIndex = 0;
2603 niro 3021 /* user requested first-entry-found */
2604     if (!findEntryByPath(config, defaultKernelPath,
2605     prefix, &firstKernelEntryIndex)) {
2606     /* don't change default if can't find match */
2607 niro 3018 config->defaultImage = NO_DEFAULT_ENTRY;
2608 niro 3002 return;
2609     }
2610    
2611 niro 3021 config->defaultImage = firstKernelEntryIndex;
2612 niro 3002
2613 niro 3021 /* this is where we start looking for decrement later */
2614     currentLookupIndex = config->defaultImage;
2615 niro 3002
2616 niro 3021 if (isAddingBootEntry && !config->isModified &&
2617     (newBootEntryIndex < config->defaultImage)) {
2618     /* increment because new entry added before default */
2619 niro 3002 config->defaultImage++;
2620 niro 3021 }
2621     } else {
2622 niro 3027 /* check to see if the default is stored in the environment */
2623     if (config->defaultImage < FIRST_ENTRY_INDEX) {
2624     if (config->defaultImage == DEFAULT_SAVED || config->defaultImage == DEFAULT_SAVED_GRUB2)
2625     {
2626     if (config->cfi->defaultIsSaved) {
2627     if (config->cfi->getEnv) {
2628     char *defaultTitle = config->cfi->getEnv(config->cfi, "saved_entry");
2629 niro 3002
2630 niro 3027 if (defaultTitle) {
2631     if (isnumber(defaultTitle)) {
2632     currentLookupIndex = atoi(defaultTitle);
2633     } else {
2634     findEntryByTitle(config, defaultTitle, &currentLookupIndex);
2635     }
2636     /* set the default Image to an actual index */
2637     config->defaultImage = currentLookupIndex;
2638     }
2639     }
2640     }
2641     }
2642     } else {
2643     /* use pre-existing default entry from the file*/
2644     currentLookupIndex = config->defaultImage;
2645     }
2646    
2647 niro 3021 if (isAddingBootEntry
2648     && (newBootEntryIndex <= config->defaultImage)) {
2649     config->defaultImage++;
2650    
2651     if (config->isModified) {
2652     currentLookupIndex++;
2653     }
2654 niro 3002 }
2655 niro 3021 }
2656    
2657     /* sanity check - is this entry index valid? */
2658     bootEntry = findEntryByIndex(config, currentLookupIndex);
2659    
2660     if ((bootEntry && bootEntry->skip) || !bootEntry) {
2661     /* entry is to be skipped or is invalid */
2662     if (isAddingBootEntry) {
2663     config->defaultImage = newBootEntryIndex;
2664     return;
2665     }
2666 niro 3002 newDefault =
2667     findTemplate(config, prefix, &config->defaultImage, 1,
2668     flags);
2669 niro 3021 if (!newDefault) {
2670 niro 3018 config->defaultImage = NO_DEFAULT_ENTRY;
2671 niro 3021 }
2672    
2673     return;
2674 niro 532 }
2675 niro 3021
2676     currentLookupIndex--;
2677    
2678     /* decrement index by the total number of entries deleted */
2679    
2680     for (indexToVerify = currentLookupIndex;
2681     indexToVerify >= FIRST_ENTRY_INDEX; indexToVerify--) {
2682    
2683     bootEntry = findEntryByIndex(config, indexToVerify);
2684    
2685     if (bootEntry && bootEntry->skip) {
2686     config->defaultImage--;
2687     }
2688     }
2689 niro 3002 }
2690 niro 532
2691 niro 3002 void setFallbackImage(struct grubConfig *config, int hasNew)
2692     {
2693     struct singleEntry *entry, *entry2;
2694     int j;
2695 niro 532
2696 niro 3002 if (config->fallbackImage == -1)
2697     return;
2698 niro 532
2699 niro 3002 entry = findEntryByIndex(config, config->fallbackImage);
2700     if (!entry || entry->skip) {
2701     config->fallbackImage = -1;
2702     return;
2703     }
2704    
2705 niro 532 if (hasNew)
2706 niro 3002 config->fallbackImage++;
2707    
2708 niro 532 /* count the number of entries erased before this one */
2709 niro 3002 for (j = 0; j < config->fallbackImage; j++) {
2710     entry2 = findEntryByIndex(config, j);
2711     if (entry2->skip)
2712     config->fallbackImage--;
2713 niro 532 }
2714     }
2715    
2716 niro 3029 void displayEntry(struct grubConfig *config, struct singleEntry *entry, const char *prefix, int index)
2717 niro 3002 {
2718     struct singleLine *line;
2719     char *root = NULL;
2720     int i;
2721     int j;
2722 niro 532
2723 niro 3002 printf("index=%d\n", index);
2724 niro 532
2725 niro 3002 line =
2726     getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI | LT_KERNEL_16,
2727     entry->lines);
2728     if (!line) {
2729     printf("non linux entry\n");
2730     return;
2731     }
2732 niro 532
2733 niro 3002 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2734     printf("kernel=%s\n", line->elements[1].item);
2735     else
2736     printf("kernel=%s%s\n", prefix, line->elements[1].item);
2737 niro 532
2738 niro 3002 if (line->numElements >= 3) {
2739     printf("args=\"");
2740     i = 2;
2741     while (i < line->numElements) {
2742     if (!strncmp(line->elements[i].item, "root=", 5)) {
2743     root = line->elements[i].item + 5;
2744     } else {
2745     printf("%s%s", line->elements[i].item,
2746     line->elements[i].indent);
2747     }
2748 niro 532
2749 niro 3002 i++;
2750     }
2751     printf("\"\n");
2752     } else {
2753     line = getLineByType(LT_KERNELARGS, entry->lines);
2754     if (line) {
2755     char *s;
2756 niro 532
2757 niro 3002 printf("args=\"");
2758     i = 1;
2759     while (i < line->numElements) {
2760     if (!strncmp
2761     (line->elements[i].item, "root=", 5)) {
2762     root = line->elements[i].item + 5;
2763     } else {
2764     s = line->elements[i].item;
2765 niro 914
2766 niro 3002 printf("%s%s", s,
2767     line->elements[i].indent);
2768     }
2769 niro 532
2770 niro 3002 i++;
2771     }
2772 niro 532
2773 niro 3002 s = line->elements[i - 1].indent;
2774     printf("\"\n");
2775     }
2776 niro 532 }
2777    
2778 niro 3002 if (!root) {
2779     line = getLineByType(LT_ROOT, entry->lines);
2780     if (line && line->numElements >= 2)
2781     root = line->elements[1].item;
2782     }
2783 niro 532
2784 niro 3002 if (root) {
2785     char *s = alloca(strlen(root) + 1);
2786 niro 532
2787 niro 3002 strcpy(s, root);
2788     if (s[strlen(s) - 1] == '"')
2789     s[strlen(s) - 1] = '\0';
2790     /* make sure the root doesn't have a trailing " */
2791     printf("root=%s\n", s);
2792 niro 532 }
2793    
2794 niro 3002 line =
2795     getLineByType(LT_INITRD | LT_INITRD_EFI | LT_INITRD_16,
2796     entry->lines);
2797 niro 532
2798 niro 3002 if (line && line->numElements >= 2) {
2799     if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2800     printf("initrd=");
2801     else
2802     printf("initrd=%s", prefix);
2803 niro 532
2804 niro 3002 for (i = 1; i < line->numElements; i++)
2805     printf("%s%s", line->elements[i].item,
2806     line->elements[i].indent);
2807     printf("\n");
2808     }
2809 niro 532
2810 niro 3002 line = getLineByType(LT_TITLE, entry->lines);
2811 niro 2963 if (line) {
2812 niro 3029 char *entryTitle;
2813     /* if we can extractTitle, then it's a zipl config and
2814     * if not then we go ahead with what's existed prior */
2815     entryTitle = extractTitle(config, line);
2816     if (!entryTitle) {
2817     entryTitle=line->elements[1].item;
2818     }
2819     printf("title=%s\n", entryTitle);
2820 niro 3002 } else {
2821     char *title;
2822     line = getLineByType(LT_MENUENTRY, entry->lines);
2823     if (line) {
2824     title = grub2ExtractTitle(line);
2825     if (title)
2826     printf("title=%s\n", title);
2827     }
2828 niro 2962 }
2829 niro 2708
2830 niro 3002 for (j = 0, line = entry->lines; line; line = line->next) {
2831     if ((line->type & LT_MBMODULE) && line->numElements >= 2) {
2832     if (!strncmp
2833     (prefix, line->elements[1].item, strlen(prefix)))
2834     printf("mbmodule%d=", j);
2835     else
2836     printf("mbmodule%d=%s", j, prefix);
2837 niro 2708
2838 niro 3002 for (i = 1; i < line->numElements; i++)
2839     printf("%s%s", line->elements[i].item,
2840     line->elements[i].indent);
2841     printf("\n");
2842     j++;
2843     }
2844 niro 2708 }
2845 niro 532 }
2846    
2847 niro 3002 int isSuseSystem(void)
2848     {
2849     const char *path;
2850     const static char default_path[] = "/etc/SuSE-release";
2851 niro 1850
2852 niro 3002 if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2853     path = default_path;
2854 niro 1850
2855 niro 3002 if (!access(path, R_OK))
2856     return 1;
2857     return 0;
2858 niro 1850 }
2859    
2860 niro 3002 int isSuseGrubConf(const char *path)
2861     {
2862     FILE *grubConf;
2863     char *line = NULL;
2864     size_t len = 0, res = 0;
2865 niro 1850
2866 niro 3002 grubConf = fopen(path, "r");
2867     if (!grubConf) {
2868     dbgPrintf("Could not open SuSE configuration file '%s'\n",
2869     path);
2870     return 0;
2871     }
2872 niro 1850
2873 niro 3002 while ((res = getline(&line, &len, grubConf)) != -1) {
2874     if (!strncmp(line, "setup", 5)) {
2875     fclose(grubConf);
2876     free(line);
2877     return 1;
2878     }
2879 niro 1850 }
2880    
2881 niro 3002 dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2882     path);
2883 niro 1850
2884 niro 3002 fclose(grubConf);
2885     free(line);
2886     return 0;
2887 niro 1850 }
2888    
2889 niro 3002 int suseGrubConfGetLba(const char *path, int *lbaPtr)
2890     {
2891     FILE *grubConf;
2892     char *line = NULL;
2893     size_t res = 0, len = 0;
2894 niro 1850
2895 niro 3002 if (!path)
2896     return 1;
2897     if (!lbaPtr)
2898     return 1;
2899 niro 1850
2900 niro 3002 grubConf = fopen(path, "r");
2901     if (!grubConf)
2902     return 1;
2903 niro 1850
2904 niro 3002 while ((res = getline(&line, &len, grubConf)) != -1) {
2905     if (line[res - 1] == '\n')
2906     line[res - 1] = '\0';
2907     else if (len > res)
2908     line[res] = '\0';
2909     else {
2910     line = realloc(line, res + 1);
2911     line[res] = '\0';
2912     }
2913 niro 1850
2914 niro 3002 if (!strncmp(line, "setup", 5)) {
2915     if (strstr(line, "--force-lba")) {
2916     *lbaPtr = 1;
2917     } else {
2918     *lbaPtr = 0;
2919     }
2920     dbgPrintf("lba: %i\n", *lbaPtr);
2921     break;
2922     }
2923 niro 1850 }
2924    
2925 niro 3002 free(line);
2926     fclose(grubConf);
2927     return 0;
2928 niro 1850 }
2929    
2930 niro 3002 int suseGrubConfGetInstallDevice(const char *path, char **devicePtr)
2931     {
2932     FILE *grubConf;
2933     char *line = NULL;
2934     size_t res = 0, len = 0;
2935     char *lastParamPtr = NULL;
2936     char *secLastParamPtr = NULL;
2937     char installDeviceNumber = '\0';
2938     char *bounds = NULL;
2939 niro 1850
2940 niro 3002 if (!path)
2941     return 1;
2942     if (!devicePtr)
2943     return 1;
2944 niro 1850
2945 niro 3002 grubConf = fopen(path, "r");
2946     if (!grubConf)
2947     return 1;
2948 niro 1850
2949 niro 3002 while ((res = getline(&line, &len, grubConf)) != -1) {
2950     if (strncmp(line, "setup", 5))
2951     continue;
2952 niro 1850
2953 niro 3002 if (line[res - 1] == '\n')
2954     line[res - 1] = '\0';
2955     else if (len > res)
2956     line[res] = '\0';
2957     else {
2958     line = realloc(line, res + 1);
2959     line[res] = '\0';
2960     }
2961 niro 1850
2962 niro 3002 lastParamPtr = bounds = line + res;
2963 niro 1850
2964 niro 3002 /* Last parameter in grub may be an optional IMAGE_DEVICE */
2965     while (!isspace(*lastParamPtr))
2966     lastParamPtr--;
2967     lastParamPtr++;
2968 niro 1850
2969 niro 3002 secLastParamPtr = lastParamPtr - 2;
2970     dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2971 niro 1850
2972 niro 3002 if (lastParamPtr + 3 > bounds) {
2973     dbgPrintf("lastParamPtr going over boundary");
2974     fclose(grubConf);
2975     free(line);
2976     return 1;
2977     }
2978     if (!strncmp(lastParamPtr, "(hd", 3))
2979     lastParamPtr += 3;
2980     dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2981 niro 1850
2982 niro 3002 /*
2983     * Second last parameter will decide wether last parameter is
2984     * an IMAGE_DEVICE or INSTALL_DEVICE
2985     */
2986     while (!isspace(*secLastParamPtr))
2987     secLastParamPtr--;
2988     secLastParamPtr++;
2989 niro 1850
2990 niro 3002 if (secLastParamPtr + 3 > bounds) {
2991     dbgPrintf("secLastParamPtr going over boundary");
2992     fclose(grubConf);
2993     free(line);
2994     return 1;
2995     }
2996     dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2997     if (!strncmp(secLastParamPtr, "(hd", 3)) {
2998     secLastParamPtr += 3;
2999     dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
3000     installDeviceNumber = *secLastParamPtr;
3001     } else {
3002     installDeviceNumber = *lastParamPtr;
3003     }
3004    
3005     *devicePtr = malloc(6);
3006     snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
3007     dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
3008     fclose(grubConf);
3009     free(line);
3010     return 0;
3011 niro 1850 }
3012    
3013 niro 3002 free(line);
3014 niro 1850 fclose(grubConf);
3015 niro 3002 return 1;
3016 niro 1850 }
3017    
3018 niro 3002 int grubGetBootFromDeviceMap(const char *device, char **bootPtr)
3019     {
3020     FILE *deviceMap;
3021     char *line = NULL;
3022     size_t res = 0, len = 0;
3023     char *devicePtr;
3024     char *bounds = NULL;
3025     const char *path;
3026     const static char default_path[] = "/boot/grub/device.map";
3027 niro 1850
3028 niro 3002 if (!device)
3029     return 1;
3030     if (!bootPtr)
3031     return 1;
3032 niro 1850
3033 niro 3002 if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
3034     path = default_path;
3035 niro 1850
3036 niro 3002 dbgPrintf("opening grub device.map file from: %s\n", path);
3037     deviceMap = fopen(path, "r");
3038     if (!deviceMap)
3039     return 1;
3040 niro 1850
3041 niro 3002 while ((res = getline(&line, &len, deviceMap)) != -1) {
3042     if (!strncmp(line, "#", 1))
3043     continue;
3044 niro 1850
3045 niro 3002 if (line[res - 1] == '\n')
3046     line[res - 1] = '\0';
3047     else if (len > res)
3048     line[res] = '\0';
3049     else {
3050     line = realloc(line, res + 1);
3051     line[res] = '\0';
3052     }
3053 niro 1850
3054 niro 3002 devicePtr = line;
3055     bounds = line + res;
3056 niro 1850
3057 niro 3002 while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
3058     devicePtr++;
3059     dbgPrintf("device: %s\n", devicePtr);
3060 niro 1850
3061 niro 3002 if (!strncmp(devicePtr, device, strlen(device))) {
3062     devicePtr += strlen(device);
3063     while (isspace(*devicePtr)
3064     && ((devicePtr + 1) <= bounds))
3065     devicePtr++;
3066 niro 1850
3067 niro 3002 *bootPtr = strdup(devicePtr);
3068     break;
3069     }
3070 niro 1850 }
3071    
3072 niro 3002 free(line);
3073     fclose(deviceMap);
3074     return 0;
3075 niro 1850 }
3076    
3077 niro 3002 int suseGrubConfGetBoot(const char *path, char **bootPtr)
3078     {
3079     char *grubDevice;
3080 niro 1850
3081 niro 3002 if (suseGrubConfGetInstallDevice(path, &grubDevice))
3082     dbgPrintf("error looking for grub installation device\n");
3083     else
3084     dbgPrintf("grubby installation device: %s\n", grubDevice);
3085 niro 1850
3086 niro 3002 if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
3087     dbgPrintf("error looking for grub boot device\n");
3088     else
3089     dbgPrintf("grubby boot device: %s\n", *bootPtr);
3090 niro 1850
3091 niro 3002 free(grubDevice);
3092     return 0;
3093 niro 1850 }
3094    
3095 niro 3002 int parseSuseGrubConf(int *lbaPtr, char **bootPtr)
3096     {
3097     /*
3098     * This SuSE grub configuration file at this location is not your
3099     * average grub configuration file, but instead the grub commands
3100     * used to setup grub on that system.
3101     */
3102     const char *path;
3103     const static char default_path[] = "/etc/grub.conf";
3104 niro 1850
3105 niro 3002 if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
3106     path = default_path;
3107 niro 1850
3108 niro 3002 if (!isSuseGrubConf(path))
3109     return 1;
3110 niro 1850
3111 niro 3002 if (lbaPtr) {
3112     *lbaPtr = 0;
3113     if (suseGrubConfGetLba(path, lbaPtr))
3114     return 1;
3115     }
3116 niro 1850
3117 niro 3002 if (bootPtr) {
3118     *bootPtr = NULL;
3119     suseGrubConfGetBoot(path, bootPtr);
3120     }
3121 niro 1850
3122 niro 3002 return 0;
3123 niro 1850 }
3124    
3125 niro 3002 int parseSysconfigGrub(int *lbaPtr, char **bootPtr)
3126     {
3127     FILE *in;
3128     char buf[1024];
3129     char *chptr;
3130     char *start;
3131     char *param;
3132 niro 532
3133 niro 3002 in = fopen("/etc/sysconfig/grub", "r");
3134     if (!in)
3135     return 1;
3136 niro 532
3137 niro 3002 if (lbaPtr)
3138     *lbaPtr = 0;
3139     if (bootPtr)
3140     *bootPtr = NULL;
3141 niro 532
3142 niro 3002 while (fgets(buf, sizeof(buf), in)) {
3143     start = buf;
3144     while (isspace(*start))
3145     start++;
3146     if (*start == '#')
3147     continue;
3148 niro 532
3149 niro 3002 chptr = strchr(start, '=');
3150     if (!chptr)
3151     continue;
3152     chptr--;
3153     while (*chptr && isspace(*chptr))
3154     chptr--;
3155     chptr++;
3156     *chptr = '\0';
3157 niro 532
3158 niro 3002 param = chptr + 1;
3159     while (*param && isspace(*param))
3160     param++;
3161     if (*param == '=') {
3162     param++;
3163     while (*param && isspace(*param))
3164     param++;
3165     }
3166 niro 532
3167 niro 3002 chptr = param;
3168     while (*chptr && !isspace(*chptr))
3169     chptr++;
3170     *chptr = '\0';
3171 niro 532
3172 niro 3002 if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
3173     *lbaPtr = 1;
3174     else if (!strcmp(start, "boot") && bootPtr)
3175     *bootPtr = strdup(param);
3176     }
3177 niro 532
3178 niro 3002 fclose(in);
3179 niro 532
3180 niro 3002 return 0;
3181 niro 532 }
3182    
3183 niro 3002 void dumpSysconfigGrub(void)
3184     {
3185     char *boot = NULL;
3186     int lba;
3187 niro 532
3188 niro 3002 if (isSuseSystem()) {
3189     if (parseSuseGrubConf(&lba, &boot)) {
3190     free(boot);
3191     return;
3192     }
3193     } else {
3194     if (parseSysconfigGrub(&lba, &boot)) {
3195     free(boot);
3196     return;
3197     }
3198 niro 1850 }
3199 niro 3002
3200     if (lba)
3201     printf("lba\n");
3202     if (boot) {
3203     printf("boot=%s\n", boot);
3204     free(boot);
3205 niro 1850 }
3206 niro 532 }
3207    
3208 niro 3002 int displayInfo(struct grubConfig *config, char *kernel, const char *prefix)
3209     {
3210     int i = 0;
3211     struct singleEntry *entry;
3212     struct singleLine *line;
3213 niro 532
3214 niro 3002 entry = findEntryByPath(config, kernel, prefix, &i);
3215     if (!entry) {
3216     fprintf(stderr, _("grubby: kernel not found\n"));
3217     return 1;
3218 niro 532 }
3219    
3220 niro 3002 /* this is a horrible hack to support /etc/sysconfig/grub; there must
3221     be a better way */
3222     if (config->cfi == &grubConfigType) {
3223     dumpSysconfigGrub();
3224     } else {
3225     line = getLineByType(LT_BOOT, config->theLines);
3226     if (line && line->numElements >= 1) {
3227     printf("boot=%s\n", line->elements[1].item);
3228     }
3229 niro 532
3230 niro 3002 line = getLineByType(LT_LBA, config->theLines);
3231     if (line)
3232     printf("lba\n");
3233     }
3234 niro 532
3235 niro 3029 displayEntry(config, entry, prefix, i);
3236 niro 3002
3237 niro 532 i++;
3238 niro 3002 while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
3239 niro 3029 displayEntry(config, entry, prefix, i);
3240 niro 3002 i++;
3241     }
3242 niro 532
3243 niro 3002 return 0;
3244 niro 532 }
3245    
3246 niro 3002 struct singleLine *addLineTmpl(struct singleEntry *entry,
3247     struct singleLine *tmplLine,
3248     struct singleLine *prevLine,
3249     const char *val, struct configFileInfo *cfi)
3250 niro 914 {
3251 niro 3002 struct singleLine *newLine = lineDup(tmplLine);
3252 niro 914
3253 niro 3002 if (isEfi && cfi == &grub2ConfigType) {
3254     enum lineType_e old = newLine->type;
3255     newLine->type = preferredLineType(newLine->type, cfi);
3256     if (old != newLine->type)
3257     newLine->elements[0].item =
3258     getKeyByType(newLine->type, cfi);
3259     }
3260 niro 1940
3261 niro 3002 if (val) {
3262     /* override the inherited value with our own.
3263     * This is a little weak because it only applies to elements[1]
3264     */
3265     if (newLine->numElements > 1)
3266     removeElement(newLine, 1);
3267     insertElement(newLine, val, 1, cfi);
3268 niro 914
3269 niro 3002 /* but try to keep the rootspec from the template... sigh */
3270     if (tmplLine->
3271     type & (LT_HYPER | LT_KERNEL | LT_MBMODULE | LT_INITRD |
3272     LT_KERNEL_EFI | LT_INITRD_EFI | LT_KERNEL_16 |
3273     LT_INITRD_16)) {
3274     char *rootspec =
3275     getRootSpecifier(tmplLine->elements[1].item);
3276     if (rootspec != NULL) {
3277     free(newLine->elements[1].item);
3278     newLine->elements[1].item =
3279     sdupprintf("%s%s", rootspec, val);
3280     }
3281     }
3282 niro 914 }
3283    
3284 niro 3002 dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
3285     newLine->elements[0].item : "");
3286 niro 914
3287 niro 3002 if (!entry->lines) {
3288     /* first one on the list */
3289     entry->lines = newLine;
3290     } else if (prevLine) {
3291     /* add after prevLine */
3292     newLine->next = prevLine->next;
3293     prevLine->next = newLine;
3294     }
3295 niro 914
3296 niro 3002 return newLine;
3297 niro 914 }
3298    
3299 niro 532 /* val may be NULL */
3300 niro 3002 struct singleLine *addLine(struct singleEntry *entry,
3301     struct configFileInfo *cfi,
3302     enum lineType_e type, char *defaultIndent,
3303     const char *val)
3304     {
3305     struct singleLine *line, *prev;
3306     struct keywordTypes *kw;
3307     struct singleLine tmpl;
3308 niro 532
3309 niro 3002 /* NB: This function shouldn't allocate items on the heap, rather on
3310     * the stack since it calls addLineTmpl which will make copies.
3311     */
3312     if (type == LT_TITLE && cfi->titleBracketed) {
3313     /* we're doing a bracketed title (zipl) */
3314     tmpl.type = type;
3315     tmpl.numElements = 1;
3316     tmpl.elements = alloca(sizeof(*tmpl.elements));
3317     tmpl.elements[0].item = alloca(strlen(val) + 3);
3318     sprintf(tmpl.elements[0].item, "[%s]", val);
3319     tmpl.elements[0].indent = "";
3320     val = NULL;
3321     } else if (type == LT_MENUENTRY) {
3322     char *lineend = "--class gnu-linux --class gnu --class os {";
3323     if (!val) {
3324     fprintf(stderr,
3325     "Line type LT_MENUENTRY requires a value\n");
3326     abort();
3327     }
3328     kw = getKeywordByType(type, cfi);
3329     if (!kw) {
3330     fprintf(stderr,
3331     "Looking up keyword for unknown type %d\n",
3332     type);
3333     abort();
3334     }
3335     tmpl.indent = "";
3336     tmpl.type = type;
3337     tmpl.numElements = 3;
3338     tmpl.elements =
3339     alloca(sizeof(*tmpl.elements) * tmpl.numElements);
3340     tmpl.elements[0].item = kw->key;
3341     tmpl.elements[0].indent = alloca(2);
3342     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
3343     tmpl.elements[1].item = (char *)val;
3344     tmpl.elements[1].indent = alloca(2);
3345     sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
3346     tmpl.elements[2].item = alloca(strlen(lineend) + 1);
3347     strcpy(tmpl.elements[2].item, lineend);
3348     tmpl.elements[2].indent = "";
3349     } else {
3350     kw = getKeywordByType(type, cfi);
3351     if (!kw) {
3352     fprintf(stderr,
3353     "Looking up keyword for unknown type %d\n",
3354     type);
3355     abort();
3356     }
3357     tmpl.type = type;
3358     tmpl.numElements = val ? 2 : 1;
3359     tmpl.elements =
3360     alloca(sizeof(*tmpl.elements) * tmpl.numElements);
3361     tmpl.elements[0].item = kw->key;
3362     tmpl.elements[0].indent = alloca(2);
3363     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
3364     if (val) {
3365     tmpl.elements[1].item = (char *)val;
3366     tmpl.elements[1].indent = "";
3367     }
3368 niro 1696 }
3369 niro 3002
3370     /* The last non-empty line gives us the indention to us and the line
3371     * to insert after. Note that comments are considered empty lines,
3372     * which may not be ideal? If there are no lines or we are looking at
3373     * the first line, we use defaultIndent (the first line is normally
3374     * indented differently from the rest) */
3375     for (line = entry->lines, prev = NULL; line; line = line->next) {
3376     if (line->numElements)
3377     prev = line;
3378     /* fall back on the last line if prev isn't otherwise set */
3379     if (!line->next && !prev)
3380     prev = line;
3381 niro 1696 }
3382 niro 3002
3383     struct singleLine *menuEntry;
3384     menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
3385     if (tmpl.type == LT_ENTRY_END) {
3386     if (menuEntry)
3387     tmpl.indent = menuEntry->indent;
3388     else
3389     tmpl.indent = defaultIndent ? : "";
3390     } else if (tmpl.type != LT_MENUENTRY) {
3391     if (menuEntry)
3392     tmpl.indent = "\t";
3393     else if (prev == entry->lines)
3394     tmpl.indent = defaultIndent ? : "";
3395     else
3396     tmpl.indent = prev->indent;
3397 niro 1696 }
3398 niro 914
3399 niro 3002 return addLineTmpl(entry, &tmpl, prev, val, cfi);
3400 niro 532 }
3401    
3402 niro 3002 void removeLine(struct singleEntry *entry, struct singleLine *line)
3403     {
3404     struct singleLine *prev;
3405     int i;
3406 niro 532
3407 niro 3002 for (i = 0; i < line->numElements; i++) {
3408     free(line->elements[i].item);
3409     free(line->elements[i].indent);
3410     }
3411     free(line->elements);
3412     free(line->indent);
3413 niro 532
3414 niro 3002 if (line == entry->lines) {
3415     entry->lines = line->next;
3416     } else {
3417     prev = entry->lines;
3418     while (prev->next != line)
3419     prev = prev->next;
3420     prev->next = line->next;
3421     }
3422 niro 532
3423 niro 3002 free(line);
3424 niro 532 }
3425    
3426 niro 3002 static void requote(struct singleLine *tmplLine, struct configFileInfo *cfi)
3427 niro 1696 {
3428 niro 3002 struct singleLine newLine = {
3429     .indent = tmplLine->indent,
3430     .type = tmplLine->type,
3431     .next = tmplLine->next,
3432     };
3433     int firstQuotedItem = -1;
3434     int quoteLen = 0;
3435     int j;
3436     int element = 0;
3437     char *c;
3438 niro 1696
3439 niro 3002 c = malloc(strlen(tmplLine->elements[0].item) + 1);
3440     strcpy(c, tmplLine->elements[0].item);
3441     insertElement(&newLine, c, element++, cfi);
3442     free(c);
3443     c = NULL;
3444 niro 1696
3445 niro 3002 for (j = 1; j < tmplLine->numElements; j++) {
3446     if (firstQuotedItem == -1) {
3447     quoteLen += strlen(tmplLine->elements[j].item);
3448    
3449     if (isquote(tmplLine->elements[j].item[0])) {
3450     firstQuotedItem = j;
3451     quoteLen +=
3452     strlen(tmplLine->elements[j].indent);
3453     } else {
3454     c = malloc(quoteLen + 1);
3455     strcpy(c, tmplLine->elements[j].item);
3456     insertElement(&newLine, c, element++, cfi);
3457     free(c);
3458     quoteLen = 0;
3459     }
3460     } else {
3461     int itemlen = strlen(tmplLine->elements[j].item);
3462     quoteLen += itemlen;
3463     quoteLen += strlen(tmplLine->elements[j].indent);
3464    
3465     if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
3466     c = malloc(quoteLen + 1);
3467     c[0] = '\0';
3468     for (int i = firstQuotedItem; i < j + 1; i++) {
3469     strcat(c, tmplLine->elements[i].item);
3470     strcat(c, tmplLine->elements[i].indent);
3471     }
3472     insertElement(&newLine, c, element++, cfi);
3473     free(c);
3474    
3475     firstQuotedItem = -1;
3476     quoteLen = 0;
3477     }
3478 niro 1696 }
3479     }
3480 niro 3002 while (tmplLine->numElements)
3481     removeElement(tmplLine, 0);
3482     if (tmplLine->elements)
3483     free(tmplLine->elements);
3484 niro 1696
3485 niro 3002 tmplLine->numElements = newLine.numElements;
3486     tmplLine->elements = newLine.elements;
3487 niro 1696 }
3488    
3489 niro 3002 static void insertElement(struct singleLine *line,
3490     const char *item, int insertHere,
3491     struct configFileInfo *cfi)
3492 niro 914 {
3493 niro 3002 struct keywordTypes *kw;
3494     char indent[2] = "";
3495 niro 914
3496 niro 3002 /* sanity check */
3497     if (insertHere > line->numElements) {
3498     dbgPrintf
3499     ("insertElement() adjusting insertHere from %d to %d\n",
3500     insertHere, line->numElements);
3501     insertHere = line->numElements;
3502     }
3503 niro 914
3504 niro 3002 line->elements = realloc(line->elements, (line->numElements + 1) *
3505     sizeof(*line->elements));
3506     memmove(&line->elements[insertHere + 1],
3507     &line->elements[insertHere],
3508     (line->numElements - insertHere) * sizeof(*line->elements));
3509     line->elements[insertHere].item = strdup(item);
3510 niro 914
3511 niro 3002 kw = getKeywordByType(line->type, cfi);
3512 niro 914
3513 niro 3002 if (line->numElements == 0) {
3514     indent[0] = '\0';
3515     } else if (insertHere == 0) {
3516     indent[0] = kw->nextChar;
3517     } else if (kw->separatorChar != '\0') {
3518     indent[0] = kw->separatorChar;
3519     } else {
3520     indent[0] = ' ';
3521     }
3522 niro 914
3523 niro 3002 if (insertHere > 0 && line->elements[insertHere - 1].indent[0] == '\0') {
3524     /* move the end-of-line forward */
3525     line->elements[insertHere].indent =
3526     line->elements[insertHere - 1].indent;
3527     line->elements[insertHere - 1].indent = strdup(indent);
3528     } else {
3529     line->elements[insertHere].indent = strdup(indent);
3530     }
3531 niro 914
3532 niro 3002 line->numElements++;
3533 niro 914
3534 niro 3002 dbgPrintf("insertElement(%s, '%s%s', %d)\n",
3535     line->elements[0].item,
3536     line->elements[insertHere].item,
3537     line->elements[insertHere].indent, insertHere);
3538 niro 914 }
3539    
3540 niro 3002 static void removeElement(struct singleLine *line, int removeHere)
3541     {
3542     int i;
3543 niro 914
3544 niro 3002 /* sanity check */
3545     if (removeHere >= line->numElements)
3546     return;
3547 niro 914
3548 niro 3002 dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
3549     removeHere, line->elements[removeHere].item);
3550 niro 914
3551 niro 3002 free(line->elements[removeHere].item);
3552 niro 914
3553 niro 3002 if (removeHere > 1) {
3554     /* previous argument gets this argument's post-indentation */
3555     free(line->elements[removeHere - 1].indent);
3556     line->elements[removeHere - 1].indent =
3557     line->elements[removeHere].indent;
3558     } else {
3559     free(line->elements[removeHere].indent);
3560     }
3561 niro 914
3562 niro 3002 /* now collapse the array, but don't bother to realloc smaller */
3563     for (i = removeHere; i < line->numElements - 1; i++)
3564     line->elements[i] = line->elements[i + 1];
3565 niro 914
3566 niro 3002 line->numElements--;
3567 niro 914 }
3568    
3569 niro 3136 static int argNameMatch(const char *one, const char *two)
3570 niro 3002 {
3571     char *first, *second;
3572 niro 3136 char *chptra, *chptrb;
3573     int rc;
3574 niro 532
3575 niro 3002 first = strcpy(alloca(strlen(one) + 1), one);
3576     second = strcpy(alloca(strlen(two) + 1), two);
3577 niro 532
3578 niro 3136 chptra = strchr(first, '=');
3579     if (chptra)
3580     *chptra = '\0';
3581 niro 532
3582 niro 3136 chptrb = strchr(second, '=');
3583     if (chptrb)
3584     *chptrb = '\0';
3585    
3586     rc = strcmp(first, second);
3587    
3588     if (chptra)
3589     *chptra = '=';
3590     if (chptrb)
3591     *chptrb = '=';
3592    
3593     return rc;
3594     }
3595    
3596     static int argHasValue(const char *arg)
3597     {
3598     char *chptr;
3599    
3600     chptr = strchr(arg, '=');
3601 niro 3002 if (chptr)
3602 niro 3136 return 1;
3603     return 0;
3604     }
3605 niro 532
3606 niro 3136 static int argValueMatch(const char *one, const char *two)
3607     {
3608     char *first, *second;
3609     char *chptra, *chptrb;
3610    
3611     first = strcpy(alloca(strlen(one) + 1), one);
3612     second = strcpy(alloca(strlen(two) + 1), two);
3613    
3614     chptra = strchr(first, '=');
3615     if (chptra)
3616     chptra += 1;
3617    
3618     chptrb = strchr(second, '=');
3619     if (chptrb)
3620     chptrb += 1;
3621    
3622     if (!chptra && !chptrb)
3623     return 0;
3624     else if (!chptra)
3625     return *chptrb - 0;
3626     else if (!chptrb)
3627     return 0 - *chptra;
3628     else
3629     return strcmp(chptra, chptrb);
3630 niro 532 }
3631    
3632 niro 3002 int updateActualImage(struct grubConfig *cfg, const char *image,
3633     const char *prefix, const char *addArgs,
3634     const char *removeArgs, int multibootArgs)
3635     {
3636     struct singleEntry *entry;
3637     struct singleLine *line, *rootLine;
3638     int index = 0;
3639     int i, k;
3640     const char **newArgs, **oldArgs;
3641     const char **arg;
3642     int useKernelArgs, useRoot;
3643     int firstElement;
3644     int *usedElements;
3645     int doreplace;
3646 niro 532
3647 niro 3002 if (!image)
3648     return 0;
3649 niro 532
3650 niro 3002 if (!addArgs) {
3651     newArgs = malloc(sizeof(*newArgs));
3652     *newArgs = NULL;
3653     } else {
3654     if (poptParseArgvString(addArgs, NULL, &newArgs)) {
3655     fprintf(stderr,
3656     _("grubby: error separating arguments '%s'\n"),
3657     addArgs);
3658     return 1;
3659     }
3660 niro 532 }
3661    
3662 niro 3002 if (!removeArgs) {
3663     oldArgs = malloc(sizeof(*oldArgs));
3664     *oldArgs = NULL;
3665     } else {
3666     if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
3667     fprintf(stderr,
3668     _("grubby: error separating arguments '%s'\n"),
3669     removeArgs);
3670     free(newArgs);
3671     return 1;
3672     }
3673 niro 532 }
3674    
3675 niro 3002 useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
3676     && (!multibootArgs || cfg->cfi->mbConcatArgs));
3677 niro 532
3678 niro 3002 useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
3679     && !multibootArgs);
3680 niro 532
3681 niro 3002 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3682 niro 532
3683 niro 3002 if (multibootArgs && !entry->multiboot)
3684     continue;
3685 niro 532
3686 niro 3002 /* Determine where to put the args. If this config supports
3687     * LT_KERNELARGS, use that. Otherwise use
3688     * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
3689     */
3690     if (useKernelArgs) {
3691     line = getLineByType(LT_KERNELARGS, entry->lines);
3692     if (!line) {
3693     /* no LT_KERNELARGS, need to add it */
3694     line = addLine(entry, cfg->cfi, LT_KERNELARGS,
3695     cfg->secondaryIndent, NULL);
3696     }
3697     firstElement = 1;
3698 niro 532
3699 niro 3002 } else if (multibootArgs) {
3700     line = getLineByType(LT_HYPER, entry->lines);
3701     if (!line) {
3702     /* a multiboot entry without LT_HYPER? */
3703     continue;
3704     }
3705     firstElement = 2;
3706 niro 532
3707 niro 3002 } else {
3708     line =
3709     getLineByType(LT_KERNEL | LT_MBMODULE |
3710     LT_KERNEL_EFI | LT_KERNEL_16,
3711     entry->lines);
3712     if (!line) {
3713     /* no LT_KERNEL or LT_MBMODULE in this entry? */
3714     continue;
3715     }
3716     firstElement = 2;
3717     }
3718 niro 914
3719 niro 3002 /* handle the elilo case which does:
3720     * append="hypervisor args -- kernel args"
3721 niro 914 */
3722 niro 3002 if (entry->multiboot && cfg->cfi->mbConcatArgs) {
3723     /* this is a multiboot entry, make sure there's
3724     * -- on the args line
3725     */
3726     for (i = firstElement; i < line->numElements; i++) {
3727     if (!strcmp(line->elements[i].item, "--"))
3728     break;
3729     }
3730     if (i == line->numElements) {
3731     /* assume all existing args are kernel args,
3732     * prepend -- to make it official
3733     */
3734     insertElement(line, "--", firstElement,
3735     cfg->cfi);
3736     i = firstElement;
3737     }
3738     if (!multibootArgs) {
3739     /* kernel args start after the -- */
3740     firstElement = i + 1;
3741     }
3742     } else if (cfg->cfi->mbConcatArgs) {
3743     /* this is a non-multiboot entry, remove hyper args */
3744     for (i = firstElement; i < line->numElements; i++) {
3745     if (!strcmp(line->elements[i].item, "--"))
3746     break;
3747     }
3748     if (i < line->numElements) {
3749     /* remove args up to -- */
3750     while (strcmp
3751     (line->elements[firstElement].item,
3752     "--"))
3753     removeElement(line, firstElement);
3754     /* remove -- */
3755     removeElement(line, firstElement);
3756     }
3757     }
3758 niro 532
3759 niro 3002 usedElements = calloc(line->numElements, sizeof(*usedElements));
3760 niro 532
3761 niro 3002 for (k = 0, arg = newArgs; *arg; arg++, k++) {
3762 niro 914
3763 niro 3002 doreplace = 1;
3764     for (i = firstElement; i < line->numElements; i++) {
3765     if (multibootArgs && cfg->cfi->mbConcatArgs &&
3766     !strcmp(line->elements[i].item, "--")) {
3767     /* reached the end of hyper args, insert here */
3768     doreplace = 0;
3769     break;
3770     }
3771     if (usedElements[i])
3772     continue;
3773 niro 3136 if (!argNameMatch(line->elements[i].item, *arg)) {
3774 niro 3002 usedElements[i] = 1;
3775     break;
3776     }
3777     }
3778 niro 532
3779 niro 3002 if (i < line->numElements && doreplace) {
3780     /* direct replacement */
3781     free(line->elements[i].item);
3782     line->elements[i].item = strdup(*arg);
3783 niro 532
3784 niro 3002 } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
3785     /* root= replacement */
3786     rootLine = getLineByType(LT_ROOT, entry->lines);
3787     if (rootLine) {
3788     free(rootLine->elements[1].item);
3789     rootLine->elements[1].item =
3790     strdup(*arg + 5);
3791     } else {
3792     rootLine =
3793     addLine(entry, cfg->cfi, LT_ROOT,
3794     cfg->secondaryIndent,
3795     *arg + 5);
3796     }
3797     }
3798 niro 532
3799 niro 3002 else {
3800     /* insert/append */
3801     insertElement(line, *arg, i, cfg->cfi);
3802     usedElements =
3803     realloc(usedElements,
3804     line->numElements *
3805     sizeof(*usedElements));
3806     memmove(&usedElements[i + 1], &usedElements[i],
3807     line->numElements - i - 1);
3808     usedElements[i] = 1;
3809 niro 532
3810 niro 3002 /* if we updated a root= here even though
3811     * there is a LT_ROOT available we need to
3812     * remove the LT_ROOT entry (this will happen
3813     * if we switch from a device to a label) */
3814     if (useRoot && !strncmp(*arg, "root=", 5)) {
3815     rootLine =
3816     getLineByType(LT_ROOT,
3817     entry->lines);
3818     if (rootLine)
3819     removeLine(entry, rootLine);
3820     }
3821     }
3822 niro 532 }
3823    
3824 niro 3002 free(usedElements);
3825 niro 532
3826 niro 3002 for (arg = oldArgs; *arg; arg++) {
3827     for (i = firstElement; i < line->numElements; i++) {
3828     if (multibootArgs && cfg->cfi->mbConcatArgs &&
3829     !strcmp(line->elements[i].item, "--"))
3830     /* reached the end of hyper args, stop here */
3831     break;
3832 niro 3136 if (!argNameMatch(line->elements[i].item, *arg)) {
3833     if (!argHasValue(*arg) ||
3834     !argValueMatch(line->elements[i].item, *arg)) {
3835     removeElement(line, i);
3836     break;
3837     }
3838 niro 3002 }
3839     }
3840     /* handle removing LT_ROOT line too */
3841     if (useRoot && !strncmp(*arg, "root=", 5)) {
3842     rootLine = getLineByType(LT_ROOT, entry->lines);
3843     if (rootLine)
3844     removeLine(entry, rootLine);
3845     }
3846 niro 532 }
3847    
3848 niro 3002 if (line->numElements == 1) {
3849     /* don't need the line at all (note it has to be a
3850     LT_KERNELARGS for this to happen */
3851     removeLine(entry, line);
3852     }
3853 niro 532 }
3854    
3855 niro 3002 free(newArgs);
3856     free(oldArgs);
3857 niro 532
3858 niro 3002 return 0;
3859 niro 532 }
3860    
3861 niro 3002 int updateImage(struct grubConfig *cfg, const char *image,
3862     const char *prefix, const char *addArgs,
3863     const char *removeArgs,
3864     const char *addMBArgs, const char *removeMBArgs)
3865     {
3866     int rc = 0;
3867 niro 532
3868 niro 3002 if (!image)
3869     return rc;
3870 niro 532
3871 niro 3002 /* update the main args first... */
3872     if (addArgs || removeArgs)
3873     rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs,
3874     0);
3875     if (rc)
3876     return rc;
3877 niro 532
3878 niro 3002 /* and now any multiboot args */
3879     if (addMBArgs || removeMBArgs)
3880     rc = updateActualImage(cfg, image, prefix, addMBArgs,
3881     removeMBArgs, 1);
3882     return rc;
3883 niro 532 }
3884    
3885 niro 3002 int addMBInitrd(struct grubConfig *cfg, const char *newMBKernel,
3886     const char *image, const char *prefix, const char *initrd,
3887     const char *title)
3888     {
3889     struct singleEntry *entry;
3890     struct singleLine *line, *kernelLine, *endLine = NULL;
3891     int index = 0;
3892 niro 2263
3893 niro 3002 if (!image)
3894     return 0;
3895 niro 2263
3896 niro 3002 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3897     kernelLine = getLineByType(LT_MBMODULE, entry->lines);
3898     if (!kernelLine)
3899     continue;
3900 niro 2263
3901 niro 3002 /* if title is supplied, the entry's title must match it. */
3902     if (title) {
3903     char *linetitle;
3904 niro 2962
3905 niro 3002 line =
3906     getLineByType(LT_TITLE | LT_MENUENTRY,
3907     entry->lines);
3908     if (!line)
3909     continue;
3910 niro 2960
3911 niro 3002 linetitle = extractTitle(cfg, line);
3912     if (!linetitle)
3913     continue;
3914     if (strcmp(title, linetitle)) {
3915     free(linetitle);
3916     continue;
3917     }
3918     free(linetitle);
3919     }
3920 niro 2960
3921 niro 3002 if (prefix) {
3922     int prefixLen = strlen(prefix);
3923     if (!strncmp(initrd, prefix, prefixLen))
3924     initrd += prefixLen;
3925     }
3926     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3927     if (endLine)
3928     removeLine(entry, endLine);
3929     line =
3930     addLine(entry, cfg->cfi,
3931     preferredLineType(LT_MBMODULE, cfg->cfi),
3932     kernelLine->indent, initrd);
3933     if (!line)
3934     return 1;
3935     if (endLine) {
3936     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3937     if (!line)
3938     return 1;
3939     }
3940    
3941     break;
3942 niro 2263 }
3943    
3944 niro 3002 return 0;
3945 niro 2263 }
3946    
3947 niro 3002 int updateInitrd(struct grubConfig *cfg, const char *image,
3948     const char *prefix, const char *initrd, const char *title)
3949     {
3950     struct singleEntry *entry;
3951     struct singleLine *line, *kernelLine, *endLine = NULL;
3952     int index = 0;
3953 niro 1156
3954 niro 3002 if (!image)
3955     return 0;
3956 niro 1156
3957 niro 3002 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3958     kernelLine =
3959     getLineByType(LT_KERNEL | LT_KERNEL_EFI | LT_KERNEL_16,
3960     entry->lines);
3961     if (!kernelLine)
3962     continue;
3963 niro 1156
3964 niro 3002 /* if title is supplied, the entry's title must match it. */
3965     if (title) {
3966     char *linetitle;
3967 niro 2962
3968 niro 3002 line =
3969     getLineByType(LT_TITLE | LT_MENUENTRY,
3970     entry->lines);
3971     if (!line)
3972     continue;
3973 niro 2960
3974 niro 3002 linetitle = extractTitle(cfg, line);
3975     if (!linetitle)
3976     continue;
3977     if (strcmp(title, linetitle)) {
3978     free(linetitle);
3979     continue;
3980     }
3981     free(linetitle);
3982     }
3983 niro 2960
3984 niro 3002 line =
3985     getLineByType(LT_INITRD | LT_INITRD_EFI | LT_INITRD_16,
3986     entry->lines);
3987     if (line)
3988     removeLine(entry, line);
3989     if (prefix) {
3990     int prefixLen = strlen(prefix);
3991     if (!strncmp(initrd, prefix, prefixLen))
3992     initrd += prefixLen;
3993     }
3994     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3995     if (endLine)
3996     removeLine(entry, endLine);
3997     enum lineType_e lt;
3998     switch (kernelLine->type) {
3999     case LT_KERNEL:
4000     lt = LT_INITRD;
4001     break;
4002     case LT_KERNEL_EFI:
4003     lt = LT_INITRD_EFI;
4004     break;
4005     case LT_KERNEL_16:
4006     lt = LT_INITRD_16;
4007     break;
4008     default:
4009     lt = preferredLineType(LT_INITRD, cfg->cfi);
4010     }
4011     line = addLine(entry, cfg->cfi, lt, kernelLine->indent, initrd);
4012     if (!line)
4013     return 1;
4014     if (endLine) {
4015     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
4016     if (!line)
4017     return 1;
4018     }
4019    
4020 niro 2683 break;
4021     }
4022 niro 1696
4023 niro 3002 return 0;
4024 niro 1156 }
4025    
4026 niro 3002 int checkDeviceBootloader(const char *device, const unsigned char *boot)
4027     {
4028     int fd;
4029     unsigned char bootSect[512];
4030     int offset;
4031 niro 532
4032 niro 3002 fd = open(device, O_RDONLY);
4033     if (fd < 0) {
4034     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
4035     device, strerror(errno));
4036     return 1;
4037     }
4038 niro 532
4039 niro 3002 if (read(fd, bootSect, 512) != 512) {
4040     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4041     device, strerror(errno));
4042     return 1;
4043     }
4044     close(fd);
4045 niro 532
4046 niro 3002 /* first three bytes should match, a jmp short should be in there */
4047     if (memcmp(boot, bootSect, 3))
4048     return 0;
4049 niro 532
4050 niro 3002 if (boot[1] == JMP_SHORT_OPCODE) {
4051     offset = boot[2] + 2;
4052     } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
4053     offset = (boot[3] << 8) + boot[2] + 2;
4054     } else if (boot[0] == JMP_SHORT_OPCODE) {
4055     offset = boot[1] + 2;
4056     /*
4057     * it looks like grub, when copying stage1 into the mbr,
4058     * patches stage1 right after the JMP location, replacing
4059     * other instructions such as JMPs for NOOPs. So, relax the
4060     * check a little bit by skipping those different bytes.
4061     */
4062     if ((bootSect[offset + 1] == NOOP_OPCODE)
4063     && (bootSect[offset + 2] == NOOP_OPCODE)) {
4064     offset = offset + 3;
4065     }
4066     } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
4067     offset = (boot[2] << 8) + boot[1] + 2;
4068     } else {
4069     return 0;
4070     }
4071 niro 532
4072 niro 3002 if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
4073     return 0;
4074 niro 532
4075 niro 3002 return 2;
4076 niro 532 }
4077    
4078 niro 3002 int checkLiloOnRaid(char *mdDev, const unsigned char *boot)
4079     {
4080     int fd;
4081     char buf[65536];
4082     char *end;
4083     char *chptr;
4084     char *chptr2;
4085     int rc;
4086 niro 532
4087 niro 3002 /* it's on raid; we need to parse /proc/mdstat and check all of the
4088     *raw* devices listed in there */
4089 niro 532
4090 niro 3002 if (!strncmp(mdDev, "/dev/", 5))
4091     mdDev += 5;
4092 niro 532
4093 niro 3002 if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
4094     fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
4095     strerror(errno));
4096     return 2;
4097     }
4098 niro 532
4099 niro 3002 rc = read(fd, buf, sizeof(buf) - 1);
4100     if (rc < 0 || rc == (sizeof(buf) - 1)) {
4101     fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
4102     strerror(errno));
4103     close(fd);
4104     return 2;
4105     }
4106 niro 532 close(fd);
4107 niro 3002 buf[rc] = '\0';
4108 niro 532
4109 niro 3002 chptr = buf;
4110     while (*chptr) {
4111     end = strchr(chptr, '\n');
4112     if (!end)
4113     break;
4114     *end = '\0';
4115 niro 532
4116 niro 3002 if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
4117     chptr[strlen(mdDev)] == ' ') {
4118 niro 532
4119 niro 3002 /* found the device */
4120     while (*chptr && *chptr != ':')
4121     chptr++;
4122     chptr++;
4123     while (*chptr && isspace(*chptr))
4124     chptr++;
4125 niro 532
4126 niro 3002 /* skip the "active" bit */
4127     while (*chptr && !isspace(*chptr))
4128     chptr++;
4129     while (*chptr && isspace(*chptr))
4130     chptr++;
4131 niro 532
4132 niro 3002 /* skip the raid level */
4133     while (*chptr && !isspace(*chptr))
4134     chptr++;
4135     while (*chptr && isspace(*chptr))
4136     chptr++;
4137 niro 532
4138 niro 3002 /* everything else is partition stuff */
4139     while (*chptr) {
4140     chptr2 = chptr;
4141     while (*chptr2 && *chptr2 != '[')
4142     chptr2++;
4143     if (!*chptr2)
4144     break;
4145 niro 532
4146 niro 3002 /* yank off the numbers at the end */
4147     chptr2--;
4148     while (isdigit(*chptr2) && chptr2 > chptr)
4149     chptr2--;
4150     chptr2++;
4151     *chptr2 = '\0';
4152 niro 532
4153 niro 3002 /* Better, now we need the /dev/ back. We're
4154     * done with everything before this point, so
4155     * we can just put the /dev/ part there.
4156     * There will always be room. */
4157     memcpy(chptr - 5, "/dev/", 5);
4158     rc = checkDeviceBootloader(chptr - 5, boot);
4159     if (rc != 2) {
4160     return rc;
4161     }
4162    
4163     chptr = chptr2 + 1;
4164     /* skip the [11] bit */
4165     while (*chptr && !isspace(*chptr))
4166     chptr++;
4167     /* and move to the next one */
4168     while (*chptr && isspace(*chptr))
4169     chptr++;
4170     }
4171    
4172     /* we're good to go */
4173     return 2;
4174 niro 532 }
4175    
4176 niro 3002 chptr = end + 1;
4177 niro 532 }
4178    
4179 niro 3002 fprintf(stderr,
4180     _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
4181     mdDev);
4182     return 0;
4183 niro 532 }
4184    
4185 niro 3002 int checkForLilo(struct grubConfig *config)
4186     {
4187     int fd;
4188     unsigned char boot[512];
4189     struct singleLine *line;
4190 niro 532
4191 niro 3002 for (line = config->theLines; line; line = line->next)
4192     if (line->type == LT_BOOT)
4193     break;
4194 niro 532
4195 niro 3002 if (!line) {
4196     fprintf(stderr,
4197     _
4198     ("grubby: no boot line found in lilo configuration\n"));
4199     return 1;
4200     }
4201 niro 532
4202 niro 3002 if (line->numElements != 2)
4203     return 1;
4204 niro 532
4205 niro 3002 fd = open("/boot/boot.b", O_RDONLY);
4206     if (fd < 0) {
4207     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
4208     "/boot/boot.b", strerror(errno));
4209     return 1;
4210     }
4211 niro 532
4212 niro 3002 if (read(fd, boot, 512) != 512) {
4213     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4214     "/boot/boot.b", strerror(errno));
4215     return 1;
4216     }
4217     close(fd);
4218 niro 532
4219 niro 3002 if (!strncmp("/dev/md", line->elements[1].item, 7))
4220     return checkLiloOnRaid(line->elements[1].item, boot);
4221 niro 532
4222 niro 3002 return checkDeviceBootloader(line->elements[1].item, boot);
4223 niro 532 }
4224    
4225 niro 3002 int checkForGrub2(struct grubConfig *config)
4226     {
4227     if (!access("/etc/grub.d/", R_OK))
4228     return 2;
4229 niro 1696
4230 niro 3002 return 1;
4231 niro 1696 }
4232    
4233 niro 3002 int checkForGrub(struct grubConfig *config)
4234     {
4235     int fd;
4236     unsigned char bootSect[512];
4237     char *boot;
4238     int onSuse = isSuseSystem();
4239 niro 532
4240 niro 3002 if (onSuse) {
4241     if (parseSuseGrubConf(NULL, &boot))
4242     return 0;
4243     } else {
4244     if (parseSysconfigGrub(NULL, &boot))
4245     return 0;
4246     }
4247 niro 1851
4248 niro 3002 /* assume grub is not installed -- not an error condition */
4249     if (!boot)
4250     return 0;
4251 niro 532
4252 niro 3002 fd = open("/boot/grub/stage1", O_RDONLY);
4253     if (fd < 0)
4254     /* this doesn't exist if grub hasn't been installed */
4255     return 0;
4256 niro 532
4257 niro 3002 if (read(fd, bootSect, 512) != 512) {
4258     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4259     "/boot/grub/stage1", strerror(errno));
4260     close(fd);
4261     return 1;
4262     }
4263     close(fd);
4264 niro 532
4265 niro 3002 /* The more elaborate checks do not work on SuSE. The checks done
4266     * seem to be reasonble (at least for now), so just return success
4267     */
4268     if (onSuse)
4269     return 2;
4270 niro 532
4271 niro 3002 return checkDeviceBootloader(boot, bootSect);
4272 niro 532 }
4273    
4274 niro 3002 int checkForExtLinux(struct grubConfig *config)
4275     {
4276     int fd;
4277     unsigned char bootSect[512];
4278     char *boot;
4279     char executable[] = "/boot/extlinux/extlinux";
4280 niro 914
4281 niro 3002 printf("entered: checkForExtLinux()\n");
4282 niro 914
4283 niro 3002 if (parseSysconfigGrub(NULL, &boot))
4284     return 0;
4285 niro 914
4286 niro 3002 /* assume grub is not installed -- not an error condition */
4287     if (!boot)
4288     return 0;
4289 niro 914
4290 niro 3002 fd = open(executable, O_RDONLY);
4291     if (fd < 0)
4292     /* this doesn't exist if grub hasn't been installed */
4293     return 0;
4294 niro 914
4295 niro 3002 if (read(fd, bootSect, 512) != 512) {
4296     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4297     executable, strerror(errno));
4298     return 1;
4299     }
4300     close(fd);
4301 niro 914
4302 niro 3002 return checkDeviceBootloader(boot, bootSect);
4303 niro 914 }
4304    
4305 niro 3002 int checkForYaboot(struct grubConfig *config)
4306     {
4307     /*
4308     * This is a simplistic check that we consider good enough for own puporses
4309     *
4310     * If we were to properly check if yaboot is *installed* we'd need to:
4311     * 1) get the system boot device (LT_BOOT)
4312     * 2) considering it's a raw filesystem, check if the yaboot binary matches
4313     * the content on the boot device
4314     * 3) if not, copy the binary to a temporary file and run "addnote" on it
4315     * 4) check again if binary and boot device contents match
4316     */
4317     if (!access("/etc/yaboot.conf", R_OK))
4318     return 2;
4319 niro 1853
4320 niro 3002 return 1;
4321 niro 1853 }
4322    
4323 niro 3002 int checkForElilo(struct grubConfig *config)
4324     {
4325     if (!access("/etc/elilo.conf", R_OK))
4326     return 2;
4327 niro 1854
4328 niro 3002 return 1;
4329 niro 1854 }
4330    
4331 niro 3002 static char *getRootSpecifier(char *str)
4332     {
4333     char *idx, *rootspec = NULL;
4334 niro 532
4335 niro 3002 if (*str == '(') {
4336     idx = rootspec = strdup(str);
4337     while (*idx && (*idx != ')') && (!isspace(*idx)))
4338     idx++;
4339     *(++idx) = '\0';
4340     }
4341     return rootspec;
4342 niro 532 }
4343    
4344 niro 3002 static char *getInitrdVal(struct grubConfig *config,
4345     const char *prefix, struct singleLine *tmplLine,
4346     const char *newKernelInitrd,
4347     const char **extraInitrds, int extraInitrdCount)
4348 niro 914 {
4349 niro 3002 char *initrdVal, *end;
4350     int i;
4351     size_t totalSize;
4352     size_t prefixLen;
4353     char separatorChar;
4354 niro 914
4355 niro 3002 prefixLen = strlen(prefix);
4356     totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */ ;
4357 niro 914
4358 niro 3002 for (i = 0; i < extraInitrdCount; i++) {
4359     totalSize += sizeof(separatorChar);
4360     totalSize += strlen(extraInitrds[i]) - prefixLen;
4361     }
4362 niro 914
4363 niro 3002 initrdVal = end = malloc(totalSize);
4364 niro 914
4365 niro 3002 end = stpcpy(end, newKernelInitrd + prefixLen);
4366 niro 914
4367 niro 3002 separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
4368     for (i = 0; i < extraInitrdCount; i++) {
4369     const char *extraInitrd;
4370     int j;
4371 niro 914
4372 niro 3002 extraInitrd = extraInitrds[i] + prefixLen;
4373     /* Don't add entries that are already there */
4374     if (tmplLine != NULL) {
4375     for (j = 2; j < tmplLine->numElements; j++)
4376     if (strcmp
4377     (extraInitrd,
4378     tmplLine->elements[j].item) == 0)
4379     break;
4380 niro 914
4381 niro 3002 if (j != tmplLine->numElements)
4382     continue;
4383     }
4384    
4385     *end++ = separatorChar;
4386     end = stpcpy(end, extraInitrd);
4387 niro 914 }
4388    
4389 niro 3002 return initrdVal;
4390 niro 914 }
4391    
4392 niro 3002 int addNewKernel(struct grubConfig *config, struct singleEntry *template,
4393     const char *prefix,
4394     const char *newKernelPath, const char *newKernelTitle,
4395     const char *newKernelArgs, const char *newKernelInitrd,
4396     const char **extraInitrds, int extraInitrdCount,
4397     const char *newMBKernel, const char *newMBKernelArgs,
4398 niro 3007 const char *newDevTreePath, int newIndex)
4399 niro 3002 {
4400 niro 3007 struct singleEntry *new, *entry, *prev = NULL;
4401 niro 3002 struct singleLine *newLine = NULL, *tmplLine = NULL, *masterLine = NULL;
4402     int needs;
4403 niro 3010 char *indexs;
4404 niro 3002 char *chptr;
4405 niro 3010 int rc;
4406 niro 532
4407 niro 3002 if (!newKernelPath)
4408     return 0;
4409 niro 532
4410 niro 3010 rc = asprintf(&indexs, "%d", newIndex);
4411     if (rc < 0)
4412     return 1;
4413    
4414 niro 3002 /* if the newKernelTitle is too long silently munge it into something
4415     * we can live with. truncating is first check, then we'll just mess with
4416     * it until it looks better */
4417     if (config->cfi->maxTitleLength &&
4418 niro 532 (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
4419 niro 3002 char *buf = alloca(config->cfi->maxTitleLength + 7);
4420     char *numBuf = alloca(config->cfi->maxTitleLength + 1);
4421     int i = 1;
4422 niro 532
4423 niro 3002 sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength,
4424     newKernelTitle);
4425     while (findEntryByPath(config, buf, NULL, NULL)) {
4426     sprintf(numBuf, "%d", i++);
4427     strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
4428     }
4429    
4430     newKernelTitle = buf + 6;
4431 niro 532 }
4432    
4433 niro 3002 new = malloc(sizeof(*new));
4434     new->skip = 0;
4435     new->multiboot = 0;
4436     new->lines = NULL;
4437 niro 3007 entry = config->entries;
4438     for (unsigned int i = 0; i < newIndex; i++) {
4439     if (!entry)
4440     break;
4441     prev = entry;
4442     entry = entry->next;
4443     }
4444     new->next = entry;
4445 niro 532
4446 niro 3007 if (prev)
4447     prev->next = new;
4448     else
4449     config->entries = new;
4450    
4451 niro 3002 /* copy/update from the template */
4452     needs = NEED_KERNEL | NEED_TITLE;
4453     if (newKernelInitrd)
4454     needs |= NEED_INITRD;
4455     if (newMBKernel) {
4456     needs |= NEED_MB;
4457     new->multiboot = 1;
4458     }
4459     if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))
4460     needs |= NEED_DEVTREE;
4461 niro 532
4462 niro 3002 if (template) {
4463     for (masterLine = template->lines;
4464     masterLine && (tmplLine = lineDup(masterLine));
4465     lineFree(tmplLine), masterLine = masterLine->next) {
4466     dbgPrintf("addNewKernel processing %d\n",
4467     tmplLine->type);
4468 niro 532
4469 niro 3002 /* skip comments */
4470     chptr = tmplLine->indent;
4471     while (*chptr && isspace(*chptr))
4472     chptr++;
4473     if (*chptr == '#')
4474     continue;
4475 niro 532
4476 niro 3002 if (iskernel(tmplLine->type)
4477     && tmplLine->numElements >= 2) {
4478     if (!template->multiboot && (needs & NEED_MB)) {
4479     /* it's not a multiboot template and
4480     * this is the kernel line. Try to
4481     * be intelligent about inserting the
4482     * hypervisor at the same time.
4483     */
4484     if (config->cfi->mbHyperFirst) {
4485     /* insert the hypervisor first */
4486     newLine =
4487     addLine(new, config->cfi,
4488     LT_HYPER,
4489     tmplLine->indent,
4490     newMBKernel +
4491     strlen(prefix));
4492     /* set up for adding the
4493     * kernel line */
4494     free(tmplLine->indent);
4495     tmplLine->indent =
4496     strdup(config->
4497     secondaryIndent);
4498     needs &= ~NEED_MB;
4499     }
4500     if (needs & NEED_KERNEL) {
4501     /* use addLineTmpl to
4502     * preserve line elements,
4503     * otherwise we could just
4504     * call addLine.
4505     * Unfortunately this means
4506     * making some changes to the
4507     * template such as the
4508     * indent change above and
4509     * the type change below.
4510     */
4511     struct keywordTypes *mbm_kw =
4512     getKeywordByType
4513     (LT_MBMODULE, config->cfi);
4514     if (mbm_kw) {
4515     tmplLine->type =
4516     LT_MBMODULE;
4517     free(tmplLine->
4518     elements[0].item);
4519     tmplLine->elements[0].
4520     item =
4521     strdup(mbm_kw->key);
4522     }
4523     newLine =
4524     addLineTmpl(new, tmplLine,
4525     newLine,
4526     newKernelPath +
4527     strlen(prefix),
4528     config->cfi);
4529     needs &= ~NEED_KERNEL;
4530     }
4531     if (needs & NEED_MB) { /* !mbHyperFirst */
4532     newLine =
4533     addLine(new, config->cfi,
4534     LT_HYPER,
4535     config->
4536     secondaryIndent,
4537     newMBKernel +
4538     strlen(prefix));
4539     needs &= ~NEED_MB;
4540     }
4541     } else if (needs & NEED_KERNEL) {
4542     newLine =
4543     addLineTmpl(new, tmplLine, newLine,
4544     newKernelPath +
4545     strlen(prefix),
4546     config->cfi);
4547     needs &= ~NEED_KERNEL;
4548     }
4549 niro 532
4550 niro 3002 } else if (tmplLine->type == LT_HYPER &&
4551     tmplLine->numElements >= 2) {
4552     if (needs & NEED_MB) {
4553     newLine =
4554     addLineTmpl(new, tmplLine, newLine,
4555     newMBKernel +
4556     strlen(prefix),
4557     config->cfi);
4558     needs &= ~NEED_MB;
4559     }
4560 niro 532
4561 niro 3002 } else if (tmplLine->type == LT_MBMODULE &&
4562     tmplLine->numElements >= 2) {
4563     if (new->multiboot) {
4564     if (needs & NEED_KERNEL) {
4565     newLine =
4566     addLineTmpl(new, tmplLine,
4567     newLine,
4568     newKernelPath +
4569     strlen(prefix),
4570     config->cfi);
4571     needs &= ~NEED_KERNEL;
4572     } else if (config->cfi->mbInitRdIsModule
4573     && (needs & NEED_INITRD)) {
4574     char *initrdVal;
4575     initrdVal =
4576     getInitrdVal(config, prefix,
4577     tmplLine,
4578     newKernelInitrd,
4579     extraInitrds,
4580     extraInitrdCount);
4581     newLine =
4582     addLineTmpl(new, tmplLine,
4583     newLine,
4584     initrdVal,
4585     config->cfi);
4586     free(initrdVal);
4587     needs &= ~NEED_INITRD;
4588     }
4589     } else if (needs & NEED_KERNEL) {
4590     /* template is multi but new is not,
4591     * insert the kernel in the first
4592     * module slot
4593     */
4594     tmplLine->type =
4595     preferredLineType(LT_KERNEL,
4596     config->cfi);
4597     free(tmplLine->elements[0].item);
4598     tmplLine->elements[0].item =
4599     strdup(getKeywordByType
4600     (tmplLine->type,
4601     config->cfi)->key);
4602     newLine =
4603     addLineTmpl(new, tmplLine, newLine,
4604     newKernelPath +
4605     strlen(prefix),
4606     config->cfi);
4607     needs &= ~NEED_KERNEL;
4608     } else if (needs & NEED_INITRD) {
4609     char *initrdVal;
4610     /* template is multi but new is not,
4611     * insert the initrd in the second
4612     * module slot
4613     */
4614     tmplLine->type =
4615     preferredLineType(LT_INITRD,
4616     config->cfi);
4617     free(tmplLine->elements[0].item);
4618     tmplLine->elements[0].item =
4619     strdup(getKeywordByType
4620     (tmplLine->type,
4621     config->cfi)->key);
4622     initrdVal =
4623     getInitrdVal(config, prefix,
4624     tmplLine,
4625     newKernelInitrd,
4626     extraInitrds,
4627     extraInitrdCount);
4628     newLine =
4629     addLineTmpl(new, tmplLine, newLine,
4630     initrdVal, config->cfi);
4631     free(initrdVal);
4632     needs &= ~NEED_INITRD;
4633     }
4634 niro 532
4635 niro 3002 } else if (isinitrd(tmplLine->type)
4636     && tmplLine->numElements >= 2) {
4637     if (needs & NEED_INITRD && new->multiboot
4638     && !template->multiboot
4639     && config->cfi->mbInitRdIsModule) {
4640     /* make sure we don't insert the
4641     * module initrd before the module
4642     * kernel... if we don't do it here,
4643     * it will be inserted following the
4644     * template.
4645     */
4646     if (!needs & NEED_KERNEL) {
4647     char *initrdVal;
4648 niro 532
4649 niro 3002 initrdVal =
4650     getInitrdVal(config, prefix,
4651     tmplLine,
4652     newKernelInitrd,
4653     extraInitrds,
4654     extraInitrdCount);
4655     newLine =
4656     addLine(new, config->cfi,
4657     LT_MBMODULE,
4658     config->
4659     secondaryIndent,
4660     initrdVal);
4661     free(initrdVal);
4662     needs &= ~NEED_INITRD;
4663     }
4664     } else if (needs & NEED_INITRD) {
4665     char *initrdVal;
4666     initrdVal =
4667     getInitrdVal(config, prefix,
4668     tmplLine,
4669     newKernelInitrd,
4670     extraInitrds,
4671     extraInitrdCount);
4672     newLine =
4673     addLineTmpl(new, tmplLine, newLine,
4674     initrdVal, config->cfi);
4675     free(initrdVal);
4676     needs &= ~NEED_INITRD;
4677     }
4678 niro 532
4679 niro 3002 } else if (tmplLine->type == LT_MENUENTRY &&
4680     (needs & NEED_TITLE)) {
4681     requote(tmplLine, config->cfi);
4682     char *nkt = malloc(strlen(newKernelTitle) + 3);
4683     strcpy(nkt, "'");
4684     strcat(nkt, newKernelTitle);
4685     strcat(nkt, "'");
4686     newLine =
4687     addLineTmpl(new, tmplLine, newLine, nkt,
4688     config->cfi);
4689     free(nkt);
4690     needs &= ~NEED_TITLE;
4691     } else if (tmplLine->type == LT_TITLE &&
4692     (needs & NEED_TITLE)) {
4693     if (tmplLine->numElements >= 2) {
4694     newLine =
4695     addLineTmpl(new, tmplLine, newLine,
4696     newKernelTitle,
4697     config->cfi);
4698     needs &= ~NEED_TITLE;
4699     } else if (tmplLine->numElements == 1 &&
4700     config->cfi->titleBracketed) {
4701     /* addLineTmpl doesn't handle
4702     * titleBracketed */
4703     newLine =
4704     addLine(new, config->cfi, LT_TITLE,
4705     tmplLine->indent,
4706     newKernelTitle);
4707     needs &= ~NEED_TITLE;
4708     }
4709     } else if (tmplLine->type == LT_ECHO) {
4710     requote(tmplLine, config->cfi);
4711     static const char *prefix = "'Loading ";
4712     if (tmplLine->numElements > 1 &&
4713     strstr(tmplLine->elements[1].item, prefix)
4714     && masterLine->next
4715     && iskernel(masterLine->next->type)) {
4716     char *newTitle =
4717     malloc(strlen(prefix) +
4718     strlen(newKernelTitle) + 2);
4719 niro 532
4720 niro 3002 strcpy(newTitle, prefix);
4721     strcat(newTitle, newKernelTitle);
4722     strcat(newTitle, "'");
4723     newLine =
4724     addLine(new, config->cfi, LT_ECHO,
4725     tmplLine->indent, newTitle);
4726     free(newTitle);
4727     } else {
4728     /* pass through other lines from the
4729     * template */
4730     newLine =
4731     addLineTmpl(new, tmplLine, newLine,
4732     NULL, config->cfi);
4733     }
4734     } else if (tmplLine->type == LT_DEVTREE &&
4735     tmplLine->numElements == 2
4736     && newDevTreePath) {
4737     newLine =
4738     addLineTmpl(new, tmplLine, newLine,
4739     newDevTreePath + strlen(prefix),
4740 niro 1696 config->cfi);
4741 niro 3002 needs &= ~NEED_DEVTREE;
4742     } else if (tmplLine->type == LT_ENTRY_END
4743     && needs & NEED_DEVTREE) {
4744     const char *ndtp = newDevTreePath;
4745     if (!strncmp
4746     (newDevTreePath, prefix, strlen(prefix)))
4747     ndtp += strlen(prefix);
4748     newLine = addLine(new, config->cfi, LT_DEVTREE,
4749     config->secondaryIndent,
4750     ndtp);
4751     needs &= ~NEED_DEVTREE;
4752     newLine =
4753     addLineTmpl(new, tmplLine, newLine, NULL,
4754     config->cfi);
4755     } else {
4756     /* pass through other lines from the template */
4757     newLine =
4758     addLineTmpl(new, tmplLine, newLine, NULL,
4759     config->cfi);
4760     }
4761 niro 914 }
4762    
4763 niro 3002 } else {
4764     /* don't have a template, so start the entry with the
4765     * appropriate starting line
4766     */
4767     switch (config->cfi->entryStart) {
4768     case LT_KERNEL:
4769     case LT_KERNEL_EFI:
4770     case LT_KERNEL_16:
4771     if (new->multiboot && config->cfi->mbHyperFirst) {
4772     /* fall through to LT_HYPER */
4773     } else {
4774     newLine = addLine(new, config->cfi,
4775     preferredLineType(LT_KERNEL,
4776     config->
4777     cfi),
4778     config->primaryIndent,
4779     newKernelPath +
4780     strlen(prefix));
4781     needs &= ~NEED_KERNEL;
4782     break;
4783     }
4784 niro 532
4785 niro 3002 case LT_HYPER:
4786     newLine = addLine(new, config->cfi, LT_HYPER,
4787     config->primaryIndent,
4788     newMBKernel + strlen(prefix));
4789     needs &= ~NEED_MB;
4790     break;
4791 niro 914
4792 niro 3002 case LT_MENUENTRY:{
4793     char *nkt = malloc(strlen(newKernelTitle) + 3);
4794     strcpy(nkt, "'");
4795     strcat(nkt, newKernelTitle);
4796     strcat(nkt, "'");
4797     newLine =
4798     addLine(new, config->cfi, LT_MENUENTRY,
4799     config->primaryIndent, nkt);
4800     free(nkt);
4801     needs &= ~NEED_TITLE;
4802     needs |= NEED_END;
4803     break;
4804     }
4805     case LT_TITLE:
4806     if (useextlinuxmenu != 0) { // We just need useextlinuxmenu to not be zero (set above)
4807     char *templabel;
4808     int x = 0, y = 0;
4809    
4810     templabel = strdup(newKernelTitle);
4811     while (templabel[x]) {
4812     if (templabel[x] == ' ') {
4813     y = x;
4814     while (templabel[y]) {
4815     templabel[y] =
4816     templabel[y + 1];
4817     y++;
4818     }
4819 niro 914 }
4820 niro 3002 x++;
4821 niro 914 }
4822 niro 3002 newLine = addLine(new, config->cfi, LT_TITLE,
4823     config->primaryIndent,
4824     templabel);
4825     free(templabel);
4826     } else {
4827     newLine = addLine(new, config->cfi, LT_TITLE,
4828     config->primaryIndent,
4829     newKernelTitle);
4830 niro 914 }
4831 niro 3002 needs &= ~NEED_TITLE;
4832     break;
4833    
4834     default:
4835     abort();
4836 niro 914 }
4837 niro 3002 }
4838 niro 914
4839 niro 3002 struct singleLine *endLine = NULL;
4840     endLine = getLineByType(LT_ENTRY_END, new->lines);
4841     if (endLine) {
4842     removeLine(new, endLine);
4843     needs |= NEED_END;
4844 niro 532 }
4845    
4846 niro 3002 /* add the remainder of the lines, i.e. those that either
4847     * weren't present in the template, or in the case of no template,
4848     * all the lines following the entryStart.
4849     */
4850     if (needs & NEED_TITLE) {
4851     newLine = addLine(new, config->cfi, LT_TITLE,
4852     config->secondaryIndent, newKernelTitle);
4853     needs &= ~NEED_TITLE;
4854     }
4855     if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
4856     newLine = addLine(new, config->cfi, LT_HYPER,
4857     config->secondaryIndent,
4858     newMBKernel + strlen(prefix));
4859     needs &= ~NEED_MB;
4860     }
4861     if (needs & NEED_KERNEL) {
4862     newLine = addLine(new, config->cfi,
4863     (new->multiboot
4864     && getKeywordByType(LT_MBMODULE,
4865     config->cfi))
4866     ? LT_MBMODULE : preferredLineType(LT_KERNEL,
4867     config->
4868     cfi),
4869     config->secondaryIndent,
4870     newKernelPath + strlen(prefix));
4871     needs &= ~NEED_KERNEL;
4872     }
4873     if (needs & NEED_MB) {
4874     newLine = addLine(new, config->cfi, LT_HYPER,
4875     config->secondaryIndent,
4876     newMBKernel + strlen(prefix));
4877     needs &= ~NEED_MB;
4878     }
4879     if (needs & NEED_INITRD) {
4880     char *initrdVal;
4881     initrdVal =
4882     getInitrdVal(config, prefix, NULL, newKernelInitrd,
4883     extraInitrds, extraInitrdCount);
4884     newLine =
4885     addLine(new, config->cfi,
4886     (new->multiboot
4887     && getKeywordByType(LT_MBMODULE, config->cfi))
4888     ? LT_MBMODULE : preferredLineType(LT_INITRD,
4889     config->cfi),
4890     config->secondaryIndent, initrdVal);
4891     free(initrdVal);
4892     needs &= ~NEED_INITRD;
4893     }
4894     if (needs & NEED_DEVTREE) {
4895     newLine = addLine(new, config->cfi, LT_DEVTREE,
4896     config->secondaryIndent, newDevTreePath);
4897     needs &= ~NEED_DEVTREE;
4898     }
4899 niro 2709
4900 niro 3002 /* NEEDS_END must be last on bootloaders that need it... */
4901     if (needs & NEED_END) {
4902     newLine = addLine(new, config->cfi, LT_ENTRY_END,
4903     config->secondaryIndent, NULL);
4904     needs &= ~NEED_END;
4905     }
4906 niro 2685
4907 niro 3002 if (needs) {
4908     printf(_("grubby: needs=%d, aborting\n"), needs);
4909     abort();
4910     }
4911 niro 2709
4912 niro 3010 if (updateImage(config, indexs, prefix, newKernelArgs, NULL,
4913 niro 3019 newMBKernelArgs, NULL)) {
4914     config->isModified = 1;
4915 niro 3002 return 1;
4916 niro 3019 }
4917 niro 914
4918 niro 3002 return 0;
4919 niro 532 }
4920    
4921 niro 3002 int main(int argc, const char **argv)
4922     {
4923     poptContext optCon;
4924     const char *grubConfig = NULL;
4925     char *outputFile = NULL;
4926     int arg = 0;
4927     int flags = 0;
4928     int badImageOkay = 0;
4929     int configureGrub2 = 0;
4930     int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4931     int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4932     int configureExtLinux = 0;
4933     int bootloaderProbe = 0;
4934     int extraInitrdCount = 0;
4935     char *updateKernelPath = NULL;
4936     char *newKernelPath = NULL;
4937     char *removeKernelPath = NULL;
4938     char *newKernelArgs = NULL;
4939     char *newKernelInitrd = NULL;
4940     char *newKernelTitle = NULL;
4941     char *newDevTreePath = NULL;
4942     char *newMBKernel = NULL;
4943     char *newMBKernelArgs = NULL;
4944 niro 3007 int newIndex = 0;
4945 niro 3002 char *removeMBKernelArgs = NULL;
4946     char *removeMBKernel = NULL;
4947     char *bootPrefix = NULL;
4948     char *defaultKernel = NULL;
4949     char *removeArgs = NULL;
4950     char *kernelInfo = NULL;
4951     char *extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4952     char *envPath = NULL;
4953     const char *chptr = NULL;
4954     struct configFileInfo *cfi = NULL;
4955     struct grubConfig *config;
4956     struct singleEntry *template = NULL;
4957     int copyDefault = 0, makeDefault = 0;
4958     int displayDefault = 0;
4959     int displayDefaultIndex = 0;
4960     int displayDefaultTitle = 0;
4961     int defaultIndex = -1;
4962     struct poptOption options[] = {
4963     {"add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4964     _("add an entry for the specified kernel"), _("kernel-path")},
4965     {"add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
4966     _("add an entry for the specified multiboot kernel"), NULL},
4967     {"args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
4968     _("default arguments for the new kernel or new arguments for "
4969     "kernel being updated"), _("args")},
4970     {"mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
4971     _("default arguments for the new multiboot kernel or "
4972     "new arguments for multiboot kernel being updated"), NULL},
4973     {"bad-image-okay", 0, 0, &badImageOkay, 0,
4974     _
4975     ("don't sanity check images in boot entries (for testing only)"),
4976     NULL},
4977     {"boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4978     _
4979 niro 3014 ("filesystem which contains /boot directory (for testing only)"),
4980 niro 3002 _("bootfs")},
4981 niro 1854 #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4982 niro 3002 {"bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4983     _("check which bootloader is installed on boot sector")},
4984 niro 532 #endif
4985 niro 3002 {"config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4986     _("path to grub config file to update (\"-\" for stdin)"),
4987     _("path")},
4988     {"copy-default", 0, 0, &copyDefault, 0,
4989     _("use the default boot entry as a template for the new entry "
4990     "being added; if the default is not a linux image, or if "
4991     "the kernel referenced by the default image does not exist, "
4992     "the first linux entry whose kernel does exist is used as the "
4993     "template"), NULL},
4994     {"debug", 0, 0, &debug, 0,
4995     _("print debugging information for failures")},
4996     {"default-kernel", 0, 0, &displayDefault, 0,
4997     _("display the path of the default kernel")},
4998     {"default-index", 0, 0, &displayDefaultIndex, 0,
4999     _("display the index of the default kernel")},
5000     {"default-title", 0, 0, &displayDefaultTitle, 0,
5001     _("display the title of the default kernel")},
5002     {"devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,
5003     _("device tree file for new stanza"), _("dtb-path")},
5004     {"devtreedir", 0, POPT_ARG_STRING, &newDevTreePath, 0,
5005     _("device tree directory for new stanza"), _("dtb-path")},
5006     {"elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
5007     _("configure elilo bootloader")},
5008     {"efi", 0, POPT_ARG_NONE, &isEfi, 0,
5009     _("force grub2 stanzas to use efi")},
5010     {"env", 0, POPT_ARG_STRING, &envPath, 0,
5011     _("path for environment data"),
5012     _("path")},
5013     {"extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
5014     _("configure extlinux bootloader (from syslinux)")},
5015     {"grub", 0, POPT_ARG_NONE, &configureGrub, 0,
5016     _("configure grub bootloader")},
5017     {"grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
5018     _("configure grub2 bootloader")},
5019     {"info", 0, POPT_ARG_STRING, &kernelInfo, 0,
5020     _("display boot information for specified kernel"),
5021     _("kernel-path")},
5022     {"initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
5023     _("initrd image for the new kernel"), _("initrd-path")},
5024     {"extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
5025     _
5026     ("auxiliary initrd image for things other than the new kernel"),
5027     _("initrd-path")},
5028     {"lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
5029     _("configure lilo bootloader")},
5030     {"make-default", 0, 0, &makeDefault, 0,
5031     _("make the newly added entry the default boot entry"), NULL},
5032     {"output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
5033     _("path to output updated config file (\"-\" for stdout)"),
5034     _("path")},
5035     {"remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
5036     _("remove kernel arguments"), NULL},
5037     {"remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
5038     _("remove multiboot kernel arguments"), NULL},
5039     {"remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
5040     _("remove all entries for the specified kernel"),
5041     _("kernel-path")},
5042     {"remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
5043     _("remove all entries for the specified multiboot kernel"),
5044     NULL},
5045     {"set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
5046     _("make the first entry referencing the specified kernel "
5047     "the default"), _("kernel-path")},
5048     {"set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
5049     _("make the given entry index the default entry"),
5050     _("entry-index")},
5051 niro 3007 {"set-index", 0, POPT_ARG_INT, &newIndex, 0,
5052     _("use the given index when creating a new entry"),
5053     _("entry-index")},
5054 niro 3002 {"silo", 0, POPT_ARG_NONE, &configureSilo, 0,
5055     _("configure silo bootloader")},
5056     {"title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
5057     _("title to use for the new kernel entry"), _("entry-title")},
5058     {"update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
5059     _("updated information for the specified kernel"),
5060     _("kernel-path")},
5061     {"version", 'v', 0, NULL, 'v',
5062     _("print the version of this program and exit"), NULL},
5063     {"yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
5064     _("configure yaboot bootloader")},
5065     {"zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
5066     _("configure zipl bootloader")},
5067     POPT_AUTOHELP {0, 0, 0, 0, 0}
5068     };
5069 niro 532
5070 niro 3002 useextlinuxmenu = 0;
5071 niro 914
5072 niro 3002 int i = 0;
5073     for (int j = 1; j < argc; j++)
5074     i += strlen(argv[j]) + 1;
5075     saved_command_line = malloc(i);
5076     if (!saved_command_line) {
5077     fprintf(stderr, "grubby: %m\n");
5078     exit(1);
5079     }
5080     saved_command_line[0] = '\0';
5081     for (int j = 1; j < argc; j++) {
5082     strcat(saved_command_line, argv[j]);
5083     strncat(saved_command_line, j == argc - 1 ? "" : " ", 1);
5084     }
5085 niro 2236
5086 niro 3002 optCon = poptGetContext("grubby", argc, argv, options, 0);
5087     poptReadDefaultConfig(optCon, 1);
5088 niro 532
5089 niro 3002 while ((arg = poptGetNextOpt(optCon)) >= 0) {
5090     switch (arg) {
5091     case 'v':
5092     printf("grubby version %s\n", VERSION);
5093     exit(0);
5094     break;
5095     case 'i':
5096     if (extraInitrdCount < MAX_EXTRA_INITRDS) {
5097     extraInitrds[extraInitrdCount++] =
5098     strdup(poptGetOptArg(optCon));
5099     } else {
5100     fprintf(stderr,
5101     _
5102     ("grubby: extra initrd maximum is %d\n"),
5103     extraInitrdCount);
5104     return 1;
5105     }
5106     break;
5107     }
5108     }
5109    
5110     if (arg < -1) {
5111     fprintf(stderr, _("grubby: bad argument %s: %s\n"),
5112     poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
5113     poptStrerror(arg));
5114 niro 914 return 1;
5115 niro 532 }
5116    
5117 niro 3002 if ((chptr = poptGetArg(optCon))) {
5118     fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
5119     return 1;
5120     }
5121 niro 532
5122 niro 3002 if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
5123     configureYaboot + configureSilo + configureZipl +
5124     configureExtLinux) > 1) {
5125     fprintf(stderr,
5126     _("grubby: cannot specify multiple bootloaders\n"));
5127     return 1;
5128     } else if (bootloaderProbe && grubConfig) {
5129     fprintf(stderr,
5130     _
5131     ("grubby: cannot specify config file with --bootloader-probe\n"));
5132     return 1;
5133     } else if (configureGrub2) {
5134     cfi = &grub2ConfigType;
5135     if (envPath)
5136     cfi->envFile = envPath;
5137     } else if (configureLilo) {
5138     cfi = &liloConfigType;
5139     } else if (configureGrub) {
5140     cfi = &grubConfigType;
5141     } else if (configureELilo) {
5142     cfi = &eliloConfigType;
5143     } else if (configureYaboot) {
5144     cfi = &yabootConfigType;
5145     } else if (configureSilo) {
5146     cfi = &siloConfigType;
5147     } else if (configureZipl) {
5148     cfi = &ziplConfigType;
5149     } else if (configureExtLinux) {
5150     cfi = &extlinuxConfigType;
5151     useextlinuxmenu = 1;
5152     }
5153 niro 532
5154 niro 3002 if (!cfi) {
5155     if (grub2FindConfig(&grub2ConfigType)) {
5156     cfi = &grub2ConfigType;
5157     if (envPath)
5158     cfi->envFile = envPath;
5159     } else
5160     #ifdef __ia64__
5161     cfi = &eliloConfigType;
5162     #elif __powerpc__
5163     cfi = &yabootConfigType;
5164     #elif __sparc__
5165     cfi = &siloConfigType;
5166     #elif __s390__
5167     cfi = &ziplConfigType;
5168     #elif __s390x__
5169     cfi = &ziplConfigtype;
5170     #else
5171     cfi = &grubConfigType;
5172     #endif
5173     }
5174 niro 532
5175 niro 3002 if (!grubConfig) {
5176     if (cfi->findConfig)
5177     grubConfig = cfi->findConfig(cfi);
5178     if (!grubConfig)
5179     grubConfig = cfi->defaultConfig;
5180     }
5181 niro 532
5182 niro 3002 if (bootloaderProbe && (displayDefault || kernelInfo ||
5183     newKernelPath || removeKernelPath || makeDefault
5184     || defaultKernel || displayDefaultIndex
5185     || displayDefaultTitle
5186     || (defaultIndex >= 0))) {
5187     fprintf(stderr,
5188     _("grubby: --bootloader-probe may not be used with "
5189 niro 532 "specified option"));
5190 niro 3002 return 1;
5191     }
5192 niro 532
5193 niro 3002 if ((displayDefault || kernelInfo) && (newKernelPath ||
5194     removeKernelPath)) {
5195     fprintf(stderr, _("grubby: --default-kernel and --info may not "
5196     "be used when adding or removing kernels\n"));
5197     return 1;
5198     }
5199 niro 532
5200 niro 3002 if (newKernelPath && !newKernelTitle) {
5201     fprintf(stderr, _("grubby: kernel title must be specified\n"));
5202     return 1;
5203     } else if (!newKernelPath && (copyDefault ||
5204     (newKernelInitrd && !updateKernelPath) ||
5205     makeDefault || extraInitrdCount > 0)) {
5206     fprintf(stderr, _("grubby: kernel path expected\n"));
5207     return 1;
5208     }
5209 niro 532
5210 niro 3002 if (newKernelPath && updateKernelPath) {
5211     fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
5212     "not be used together"));
5213     return 1;
5214     }
5215 niro 532
5216 niro 3002 if (makeDefault && defaultKernel) {
5217     fprintf(stderr, _("grubby: --make-default and --default-kernel "
5218     "may not be used together\n"));
5219     return 1;
5220     } else if (defaultKernel && removeKernelPath &&
5221     !strcmp(defaultKernel, removeKernelPath)) {
5222     fprintf(stderr,
5223     _("grubby: cannot make removed kernel the default\n"));
5224     return 1;
5225     } else if (defaultKernel && newKernelPath &&
5226     !strcmp(defaultKernel, newKernelPath)) {
5227     makeDefault = 1;
5228     defaultKernel = NULL;
5229     } else if (defaultKernel && (defaultIndex >= 0)) {
5230     fprintf(stderr,
5231     _("grubby: --set-default and --set-default-index "
5232 niro 532 "may not be used together\n"));
5233 niro 3002 return 1;
5234     }
5235 niro 532
5236 niro 3002 if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
5237     fprintf(stderr,
5238     _("grubby: output file must be specified if stdin "
5239     "is used\n"));
5240     return 1;
5241     }
5242 niro 532
5243 niro 3002 if (!removeKernelPath && !newKernelPath && !displayDefault
5244     && !defaultKernel && !kernelInfo && !bootloaderProbe
5245     && !updateKernelPath && !removeMBKernel && !displayDefaultIndex
5246     && !displayDefaultTitle && (defaultIndex == -1)) {
5247     fprintf(stderr, _("grubby: no action specified\n"));
5248     return 1;
5249     }
5250 niro 532
5251 niro 3002 flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
5252 niro 532
5253 niro 3002 if (cfi->needsBootPrefix) {
5254     if (!bootPrefix) {
5255     bootPrefix = findBootPrefix();
5256     if (!bootPrefix)
5257     return 1;
5258     } else {
5259     /* this shouldn't end with a / */
5260     if (bootPrefix[strlen(bootPrefix) - 1] == '/')
5261     bootPrefix[strlen(bootPrefix) - 1] = '\0';
5262     }
5263 niro 532 } else {
5264 niro 3002 bootPrefix = "";
5265 niro 532 }
5266    
5267 niro 3002 if (!cfi->mbAllowExtraInitRds && extraInitrdCount > 0) {
5268     fprintf(stderr,
5269     _("grubby: %s doesn't allow multiple initrds\n"),
5270     cfi->defaultConfig);
5271     return 1;
5272     }
5273 niro 914
5274 niro 3002 if (bootloaderProbe) {
5275     int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
5276     struct grubConfig *lconfig, *gconfig, *yconfig, *econfig;
5277 niro 532
5278 niro 3002 const char *grub2config = grub2FindConfig(&grub2ConfigType);
5279     if (grub2config) {
5280     gconfig = readConfig(grub2config, &grub2ConfigType);
5281     if (!gconfig)
5282     gr2c = 1;
5283     else
5284     gr2c = checkForGrub2(gconfig);
5285     }
5286 niro 1696
5287 niro 3002 const char *grubconfig = grubFindConfig(&grubConfigType);
5288     if (!access(grubconfig, F_OK)) {
5289     gconfig = readConfig(grubconfig, &grubConfigType);
5290     if (!gconfig)
5291     grc = 1;
5292     else
5293     grc = checkForGrub(gconfig);
5294     }
5295 niro 532
5296 niro 3002 if (!access(liloConfigType.defaultConfig, F_OK)) {
5297     lconfig =
5298     readConfig(liloConfigType.defaultConfig,
5299     &liloConfigType);
5300     if (!lconfig)
5301     lrc = 1;
5302     else
5303     lrc = checkForLilo(lconfig);
5304     }
5305 niro 532
5306 niro 3002 if (!access(eliloConfigType.defaultConfig, F_OK)) {
5307     econfig = readConfig(eliloConfigType.defaultConfig,
5308     &eliloConfigType);
5309     if (!econfig)
5310     erc = 1;
5311     else
5312     erc = checkForElilo(econfig);
5313     }
5314 niro 1854
5315 niro 3002 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
5316     lconfig =
5317     readConfig(extlinuxConfigType.defaultConfig,
5318     &extlinuxConfigType);
5319     if (!lconfig)
5320     extrc = 1;
5321     else
5322     extrc = checkForExtLinux(lconfig);
5323     }
5324 niro 914
5325 niro 3002 if (!access(yabootConfigType.defaultConfig, F_OK)) {
5326     yconfig = readConfig(yabootConfigType.defaultConfig,
5327     &yabootConfigType);
5328     if (!yconfig)
5329     yrc = 1;
5330     else
5331     yrc = checkForYaboot(yconfig);
5332     }
5333 niro 532
5334 niro 3002 if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1
5335     || erc == 1)
5336     return 1;
5337 niro 1853
5338 niro 3002 if (lrc == 2)
5339     printf("lilo\n");
5340     if (gr2c == 2)
5341     printf("grub2\n");
5342     if (grc == 2)
5343     printf("grub\n");
5344     if (extrc == 2)
5345     printf("extlinux\n");
5346     if (yrc == 2)
5347     printf("yaboot\n");
5348     if (erc == 2)
5349     printf("elilo\n");
5350 niro 1853
5351 niro 3002 return 0;
5352     }
5353 niro 532
5354 niro 3002 if (grubConfig == NULL) {
5355     printf("Could not find bootloader configuration file.\n");
5356     exit(1);
5357     }
5358 niro 532
5359 niro 3002 config = readConfig(grubConfig, cfi);
5360     if (!config)
5361     return 1;
5362 niro 2246
5363 niro 3002 if (displayDefault) {
5364     struct singleLine *line;
5365     struct singleEntry *entry;
5366     char *rootspec;
5367 niro 532
5368 niro 3018 if (config->defaultImage == NO_DEFAULT_ENTRY)
5369 niro 3002 return 0;
5370     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5371     cfi->defaultIsSaved)
5372 niro 3018 config->defaultImage = FIRST_ENTRY_INDEX;
5373 niro 3002 entry = findEntryByIndex(config, config->defaultImage);
5374     if (!entry)
5375     return 0;
5376     if (!suitableImage(entry, bootPrefix, 0, flags))
5377     return 0;
5378 niro 532
5379 niro 3002 line =
5380     getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI |
5381     LT_KERNEL_16, entry->lines);
5382     if (!line)
5383     return 0;
5384 niro 532
5385 niro 3002 rootspec = getRootSpecifier(line->elements[1].item);
5386     printf("%s%s\n", bootPrefix, line->elements[1].item +
5387     ((rootspec != NULL) ? strlen(rootspec) : 0));
5388 niro 532
5389 niro 3002 return 0;
5390 niro 532
5391 niro 3002 } else if (displayDefaultTitle) {
5392     struct singleLine *line;
5393     struct singleEntry *entry;
5394 niro 1720
5395 niro 3018 if (config->defaultImage == NO_DEFAULT_ENTRY)
5396 niro 3002 return 0;
5397     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5398     cfi->defaultIsSaved)
5399 niro 3018 config->defaultImage = FIRST_ENTRY_INDEX;
5400 niro 3002 entry = findEntryByIndex(config, config->defaultImage);
5401     if (!entry)
5402     return 0;
5403 niro 1721
5404 niro 3002 if (!configureGrub2) {
5405     char *title;
5406     line = getLineByType(LT_TITLE, entry->lines);
5407     if (!line)
5408     return 0;
5409     title = extractTitle(config, line);
5410     if (!title)
5411     return 0;
5412     printf("%s\n", title);
5413     free(title);
5414     } else {
5415     char *title;
5416 niro 1721
5417 niro 3002 dbgPrintf
5418     ("This is GRUB2, default title is embeded in menuentry\n");
5419     line = getLineByType(LT_MENUENTRY, entry->lines);
5420     if (!line)
5421     return 0;
5422     title = grub2ExtractTitle(line);
5423     if (title)
5424     printf("%s\n", title);
5425     }
5426 niro 2992 return 0;
5427 niro 1721
5428 niro 3002 } else if (displayDefaultIndex) {
5429 niro 3018 if (config->defaultImage == NO_DEFAULT_ENTRY)
5430 niro 3002 return 0;
5431     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5432     cfi->defaultIsSaved)
5433 niro 3018 config->defaultImage = FIRST_ENTRY_INDEX;
5434 niro 3002 printf("%i\n", config->defaultImage);
5435 niro 2992 return 0;
5436 niro 1721
5437 niro 3002 } else if (kernelInfo)
5438     return displayInfo(config, kernelInfo, bootPrefix);
5439 niro 1720
5440 niro 3002 if (copyDefault) {
5441     template = findTemplate(config, bootPrefix, NULL, 0, flags);
5442     if (!template)
5443     return 1;
5444     }
5445 niro 532
5446 niro 3002 markRemovedImage(config, removeKernelPath, bootPrefix);
5447     markRemovedImage(config, removeMBKernel, bootPrefix);
5448     setDefaultImage(config, newKernelPath != NULL, defaultKernel,
5449 niro 3021 makeDefault, bootPrefix, flags, defaultIndex,
5450     newIndex);
5451 niro 3002 setFallbackImage(config, newKernelPath != NULL);
5452     if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
5453     removeArgs, newMBKernelArgs, removeMBKernelArgs))
5454     return 1;
5455     if (updateKernelPath && newKernelInitrd) {
5456     if (newMBKernel) {
5457     if (addMBInitrd(config, newMBKernel, updateKernelPath,
5458 niro 2960 bootPrefix, newKernelInitrd,
5459     newKernelTitle))
5460 niro 3002 return 1;
5461     } else {
5462     if (updateInitrd(config, updateKernelPath, bootPrefix,
5463     newKernelInitrd, newKernelTitle))
5464     return 1;
5465     }
5466     }
5467     if (addNewKernel(config, template, bootPrefix, newKernelPath,
5468     newKernelTitle, newKernelArgs, newKernelInitrd,
5469     (const char **)extraInitrds, extraInitrdCount,
5470 niro 3007 newMBKernel, newMBKernelArgs, newDevTreePath,
5471     newIndex))
5472 niro 3002 return 1;
5473 niro 532
5474 niro 3002 if (numEntries(config) == 0) {
5475     fprintf(stderr,
5476     _("grubby: doing this would leave no kernel entries. "
5477     "Not writing out new config.\n"));
5478     return 1;
5479     }
5480 niro 532
5481 niro 3002 if (!outputFile)
5482     outputFile = (char *)grubConfig;
5483 niro 532
5484 niro 3002 return writeConfig(config, outputFile, bootPrefix);
5485 niro 532 }