Magellan Linux

Annotation of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3135 - (hide annotations) (download)
Tue Jul 7 11:08:26 2020 UTC (3 years, 10 months ago) by niro
File MIME type: text/plain
File size: 134851 byte(s)
Be more thorough about flushing our config file when writing.
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 3002 int argMatch(const char *one, const char *two)
3570     {
3571     char *first, *second;
3572     char *chptr;
3573 niro 532
3574 niro 3002 first = strcpy(alloca(strlen(one) + 1), one);
3575     second = strcpy(alloca(strlen(two) + 1), two);
3576 niro 532
3577 niro 3002 chptr = strchr(first, '=');
3578     if (chptr)
3579     *chptr = '\0';
3580 niro 532
3581 niro 3002 chptr = strchr(second, '=');
3582     if (chptr)
3583     *chptr = '\0';
3584 niro 532
3585 niro 3002 return strcmp(first, second);
3586 niro 532 }
3587    
3588 niro 3002 int updateActualImage(struct grubConfig *cfg, const char *image,
3589     const char *prefix, const char *addArgs,
3590     const char *removeArgs, int multibootArgs)
3591     {
3592     struct singleEntry *entry;
3593     struct singleLine *line, *rootLine;
3594     int index = 0;
3595     int i, k;
3596     const char **newArgs, **oldArgs;
3597     const char **arg;
3598     int useKernelArgs, useRoot;
3599     int firstElement;
3600     int *usedElements;
3601     int doreplace;
3602 niro 532
3603 niro 3002 if (!image)
3604     return 0;
3605 niro 532
3606 niro 3002 if (!addArgs) {
3607     newArgs = malloc(sizeof(*newArgs));
3608     *newArgs = NULL;
3609     } else {
3610     if (poptParseArgvString(addArgs, NULL, &newArgs)) {
3611     fprintf(stderr,
3612     _("grubby: error separating arguments '%s'\n"),
3613     addArgs);
3614     return 1;
3615     }
3616 niro 532 }
3617    
3618 niro 3002 if (!removeArgs) {
3619     oldArgs = malloc(sizeof(*oldArgs));
3620     *oldArgs = NULL;
3621     } else {
3622     if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
3623     fprintf(stderr,
3624     _("grubby: error separating arguments '%s'\n"),
3625     removeArgs);
3626     free(newArgs);
3627     return 1;
3628     }
3629 niro 532 }
3630    
3631 niro 3002 useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
3632     && (!multibootArgs || cfg->cfi->mbConcatArgs));
3633 niro 532
3634 niro 3002 useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
3635     && !multibootArgs);
3636 niro 532
3637 niro 3002 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3638 niro 532
3639 niro 3002 if (multibootArgs && !entry->multiboot)
3640     continue;
3641 niro 532
3642 niro 3002 /* Determine where to put the args. If this config supports
3643     * LT_KERNELARGS, use that. Otherwise use
3644     * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
3645     */
3646     if (useKernelArgs) {
3647     line = getLineByType(LT_KERNELARGS, entry->lines);
3648     if (!line) {
3649     /* no LT_KERNELARGS, need to add it */
3650     line = addLine(entry, cfg->cfi, LT_KERNELARGS,
3651     cfg->secondaryIndent, NULL);
3652     }
3653     firstElement = 1;
3654 niro 532
3655 niro 3002 } else if (multibootArgs) {
3656     line = getLineByType(LT_HYPER, entry->lines);
3657     if (!line) {
3658     /* a multiboot entry without LT_HYPER? */
3659     continue;
3660     }
3661     firstElement = 2;
3662 niro 532
3663 niro 3002 } else {
3664     line =
3665     getLineByType(LT_KERNEL | LT_MBMODULE |
3666     LT_KERNEL_EFI | LT_KERNEL_16,
3667     entry->lines);
3668     if (!line) {
3669     /* no LT_KERNEL or LT_MBMODULE in this entry? */
3670     continue;
3671     }
3672     firstElement = 2;
3673     }
3674 niro 914
3675 niro 3002 /* handle the elilo case which does:
3676     * append="hypervisor args -- kernel args"
3677 niro 914 */
3678 niro 3002 if (entry->multiboot && cfg->cfi->mbConcatArgs) {
3679     /* this is a multiboot entry, make sure there's
3680     * -- on the args line
3681     */
3682     for (i = firstElement; i < line->numElements; i++) {
3683     if (!strcmp(line->elements[i].item, "--"))
3684     break;
3685     }
3686     if (i == line->numElements) {
3687     /* assume all existing args are kernel args,
3688     * prepend -- to make it official
3689     */
3690     insertElement(line, "--", firstElement,
3691     cfg->cfi);
3692     i = firstElement;
3693     }
3694     if (!multibootArgs) {
3695     /* kernel args start after the -- */
3696     firstElement = i + 1;
3697     }
3698     } else if (cfg->cfi->mbConcatArgs) {
3699     /* this is a non-multiboot entry, remove hyper args */
3700     for (i = firstElement; i < line->numElements; i++) {
3701     if (!strcmp(line->elements[i].item, "--"))
3702     break;
3703     }
3704     if (i < line->numElements) {
3705     /* remove args up to -- */
3706     while (strcmp
3707     (line->elements[firstElement].item,
3708     "--"))
3709     removeElement(line, firstElement);
3710     /* remove -- */
3711     removeElement(line, firstElement);
3712     }
3713     }
3714 niro 532
3715 niro 3002 usedElements = calloc(line->numElements, sizeof(*usedElements));
3716 niro 532
3717 niro 3002 for (k = 0, arg = newArgs; *arg; arg++, k++) {
3718 niro 914
3719 niro 3002 doreplace = 1;
3720     for (i = firstElement; i < line->numElements; i++) {
3721     if (multibootArgs && cfg->cfi->mbConcatArgs &&
3722     !strcmp(line->elements[i].item, "--")) {
3723     /* reached the end of hyper args, insert here */
3724     doreplace = 0;
3725     break;
3726     }
3727     if (usedElements[i])
3728     continue;
3729     if (!argMatch(line->elements[i].item, *arg)) {
3730     usedElements[i] = 1;
3731     break;
3732     }
3733     }
3734 niro 532
3735 niro 3002 if (i < line->numElements && doreplace) {
3736     /* direct replacement */
3737     free(line->elements[i].item);
3738     line->elements[i].item = strdup(*arg);
3739 niro 532
3740 niro 3002 } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
3741     /* root= replacement */
3742     rootLine = getLineByType(LT_ROOT, entry->lines);
3743     if (rootLine) {
3744     free(rootLine->elements[1].item);
3745     rootLine->elements[1].item =
3746     strdup(*arg + 5);
3747     } else {
3748     rootLine =
3749     addLine(entry, cfg->cfi, LT_ROOT,
3750     cfg->secondaryIndent,
3751     *arg + 5);
3752     }
3753     }
3754 niro 532
3755 niro 3002 else {
3756     /* insert/append */
3757     insertElement(line, *arg, i, cfg->cfi);
3758     usedElements =
3759     realloc(usedElements,
3760     line->numElements *
3761     sizeof(*usedElements));
3762     memmove(&usedElements[i + 1], &usedElements[i],
3763     line->numElements - i - 1);
3764     usedElements[i] = 1;
3765 niro 532
3766 niro 3002 /* if we updated a root= here even though
3767     * there is a LT_ROOT available we need to
3768     * remove the LT_ROOT entry (this will happen
3769     * if we switch from a device to a label) */
3770     if (useRoot && !strncmp(*arg, "root=", 5)) {
3771     rootLine =
3772     getLineByType(LT_ROOT,
3773     entry->lines);
3774     if (rootLine)
3775     removeLine(entry, rootLine);
3776     }
3777     }
3778 niro 532 }
3779    
3780 niro 3002 free(usedElements);
3781 niro 532
3782 niro 3002 for (arg = oldArgs; *arg; arg++) {
3783     for (i = firstElement; i < line->numElements; i++) {
3784     if (multibootArgs && cfg->cfi->mbConcatArgs &&
3785     !strcmp(line->elements[i].item, "--"))
3786     /* reached the end of hyper args, stop here */
3787     break;
3788     if (!argMatch(line->elements[i].item, *arg)) {
3789     removeElement(line, i);
3790     break;
3791     }
3792     }
3793     /* handle removing LT_ROOT line too */
3794     if (useRoot && !strncmp(*arg, "root=", 5)) {
3795     rootLine = getLineByType(LT_ROOT, entry->lines);
3796     if (rootLine)
3797     removeLine(entry, rootLine);
3798     }
3799 niro 532 }
3800    
3801 niro 3002 if (line->numElements == 1) {
3802     /* don't need the line at all (note it has to be a
3803     LT_KERNELARGS for this to happen */
3804     removeLine(entry, line);
3805     }
3806 niro 532 }
3807    
3808 niro 3002 free(newArgs);
3809     free(oldArgs);
3810 niro 532
3811 niro 3002 return 0;
3812 niro 532 }
3813    
3814 niro 3002 int updateImage(struct grubConfig *cfg, const char *image,
3815     const char *prefix, const char *addArgs,
3816     const char *removeArgs,
3817     const char *addMBArgs, const char *removeMBArgs)
3818     {
3819     int rc = 0;
3820 niro 532
3821 niro 3002 if (!image)
3822     return rc;
3823 niro 532
3824 niro 3002 /* update the main args first... */
3825     if (addArgs || removeArgs)
3826     rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs,
3827     0);
3828     if (rc)
3829     return rc;
3830 niro 532
3831 niro 3002 /* and now any multiboot args */
3832     if (addMBArgs || removeMBArgs)
3833     rc = updateActualImage(cfg, image, prefix, addMBArgs,
3834     removeMBArgs, 1);
3835     return rc;
3836 niro 532 }
3837    
3838 niro 3002 int addMBInitrd(struct grubConfig *cfg, const char *newMBKernel,
3839     const char *image, const char *prefix, const char *initrd,
3840     const char *title)
3841     {
3842     struct singleEntry *entry;
3843     struct singleLine *line, *kernelLine, *endLine = NULL;
3844     int index = 0;
3845 niro 2263
3846 niro 3002 if (!image)
3847     return 0;
3848 niro 2263
3849 niro 3002 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3850     kernelLine = getLineByType(LT_MBMODULE, entry->lines);
3851     if (!kernelLine)
3852     continue;
3853 niro 2263
3854 niro 3002 /* if title is supplied, the entry's title must match it. */
3855     if (title) {
3856     char *linetitle;
3857 niro 2962
3858 niro 3002 line =
3859     getLineByType(LT_TITLE | LT_MENUENTRY,
3860     entry->lines);
3861     if (!line)
3862     continue;
3863 niro 2960
3864 niro 3002 linetitle = extractTitle(cfg, line);
3865     if (!linetitle)
3866     continue;
3867     if (strcmp(title, linetitle)) {
3868     free(linetitle);
3869     continue;
3870     }
3871     free(linetitle);
3872     }
3873 niro 2960
3874 niro 3002 if (prefix) {
3875     int prefixLen = strlen(prefix);
3876     if (!strncmp(initrd, prefix, prefixLen))
3877     initrd += prefixLen;
3878     }
3879     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3880     if (endLine)
3881     removeLine(entry, endLine);
3882     line =
3883     addLine(entry, cfg->cfi,
3884     preferredLineType(LT_MBMODULE, cfg->cfi),
3885     kernelLine->indent, initrd);
3886     if (!line)
3887     return 1;
3888     if (endLine) {
3889     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3890     if (!line)
3891     return 1;
3892     }
3893    
3894     break;
3895 niro 2263 }
3896    
3897 niro 3002 return 0;
3898 niro 2263 }
3899    
3900 niro 3002 int updateInitrd(struct grubConfig *cfg, const char *image,
3901     const char *prefix, const char *initrd, const char *title)
3902     {
3903     struct singleEntry *entry;
3904     struct singleLine *line, *kernelLine, *endLine = NULL;
3905     int index = 0;
3906 niro 1156
3907 niro 3002 if (!image)
3908     return 0;
3909 niro 1156
3910 niro 3002 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3911     kernelLine =
3912     getLineByType(LT_KERNEL | LT_KERNEL_EFI | LT_KERNEL_16,
3913     entry->lines);
3914     if (!kernelLine)
3915     continue;
3916 niro 1156
3917 niro 3002 /* if title is supplied, the entry's title must match it. */
3918     if (title) {
3919     char *linetitle;
3920 niro 2962
3921 niro 3002 line =
3922     getLineByType(LT_TITLE | LT_MENUENTRY,
3923     entry->lines);
3924     if (!line)
3925     continue;
3926 niro 2960
3927 niro 3002 linetitle = extractTitle(cfg, line);
3928     if (!linetitle)
3929     continue;
3930     if (strcmp(title, linetitle)) {
3931     free(linetitle);
3932     continue;
3933     }
3934     free(linetitle);
3935     }
3936 niro 2960
3937 niro 3002 line =
3938     getLineByType(LT_INITRD | LT_INITRD_EFI | LT_INITRD_16,
3939     entry->lines);
3940     if (line)
3941     removeLine(entry, line);
3942     if (prefix) {
3943     int prefixLen = strlen(prefix);
3944     if (!strncmp(initrd, prefix, prefixLen))
3945     initrd += prefixLen;
3946     }
3947     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3948     if (endLine)
3949     removeLine(entry, endLine);
3950     enum lineType_e lt;
3951     switch (kernelLine->type) {
3952     case LT_KERNEL:
3953     lt = LT_INITRD;
3954     break;
3955     case LT_KERNEL_EFI:
3956     lt = LT_INITRD_EFI;
3957     break;
3958     case LT_KERNEL_16:
3959     lt = LT_INITRD_16;
3960     break;
3961     default:
3962     lt = preferredLineType(LT_INITRD, cfg->cfi);
3963     }
3964     line = addLine(entry, cfg->cfi, lt, kernelLine->indent, initrd);
3965     if (!line)
3966     return 1;
3967     if (endLine) {
3968     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3969     if (!line)
3970     return 1;
3971     }
3972    
3973 niro 2683 break;
3974     }
3975 niro 1696
3976 niro 3002 return 0;
3977 niro 1156 }
3978    
3979 niro 3002 int checkDeviceBootloader(const char *device, const unsigned char *boot)
3980     {
3981     int fd;
3982     unsigned char bootSect[512];
3983     int offset;
3984 niro 532
3985 niro 3002 fd = open(device, O_RDONLY);
3986     if (fd < 0) {
3987     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3988     device, strerror(errno));
3989     return 1;
3990     }
3991 niro 532
3992 niro 3002 if (read(fd, bootSect, 512) != 512) {
3993     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3994     device, strerror(errno));
3995     return 1;
3996     }
3997     close(fd);
3998 niro 532
3999 niro 3002 /* first three bytes should match, a jmp short should be in there */
4000     if (memcmp(boot, bootSect, 3))
4001     return 0;
4002 niro 532
4003 niro 3002 if (boot[1] == JMP_SHORT_OPCODE) {
4004     offset = boot[2] + 2;
4005     } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
4006     offset = (boot[3] << 8) + boot[2] + 2;
4007     } else if (boot[0] == JMP_SHORT_OPCODE) {
4008     offset = boot[1] + 2;
4009     /*
4010     * it looks like grub, when copying stage1 into the mbr,
4011     * patches stage1 right after the JMP location, replacing
4012     * other instructions such as JMPs for NOOPs. So, relax the
4013     * check a little bit by skipping those different bytes.
4014     */
4015     if ((bootSect[offset + 1] == NOOP_OPCODE)
4016     && (bootSect[offset + 2] == NOOP_OPCODE)) {
4017     offset = offset + 3;
4018     }
4019     } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
4020     offset = (boot[2] << 8) + boot[1] + 2;
4021     } else {
4022     return 0;
4023     }
4024 niro 532
4025 niro 3002 if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
4026     return 0;
4027 niro 532
4028 niro 3002 return 2;
4029 niro 532 }
4030    
4031 niro 3002 int checkLiloOnRaid(char *mdDev, const unsigned char *boot)
4032     {
4033     int fd;
4034     char buf[65536];
4035     char *end;
4036     char *chptr;
4037     char *chptr2;
4038     int rc;
4039 niro 532
4040 niro 3002 /* it's on raid; we need to parse /proc/mdstat and check all of the
4041     *raw* devices listed in there */
4042 niro 532
4043 niro 3002 if (!strncmp(mdDev, "/dev/", 5))
4044     mdDev += 5;
4045 niro 532
4046 niro 3002 if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
4047     fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
4048     strerror(errno));
4049     return 2;
4050     }
4051 niro 532
4052 niro 3002 rc = read(fd, buf, sizeof(buf) - 1);
4053     if (rc < 0 || rc == (sizeof(buf) - 1)) {
4054     fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
4055     strerror(errno));
4056     close(fd);
4057     return 2;
4058     }
4059 niro 532 close(fd);
4060 niro 3002 buf[rc] = '\0';
4061 niro 532
4062 niro 3002 chptr = buf;
4063     while (*chptr) {
4064     end = strchr(chptr, '\n');
4065     if (!end)
4066     break;
4067     *end = '\0';
4068 niro 532
4069 niro 3002 if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
4070     chptr[strlen(mdDev)] == ' ') {
4071 niro 532
4072 niro 3002 /* found the device */
4073     while (*chptr && *chptr != ':')
4074     chptr++;
4075     chptr++;
4076     while (*chptr && isspace(*chptr))
4077     chptr++;
4078 niro 532
4079 niro 3002 /* skip the "active" bit */
4080     while (*chptr && !isspace(*chptr))
4081     chptr++;
4082     while (*chptr && isspace(*chptr))
4083     chptr++;
4084 niro 532
4085 niro 3002 /* skip the raid level */
4086     while (*chptr && !isspace(*chptr))
4087     chptr++;
4088     while (*chptr && isspace(*chptr))
4089     chptr++;
4090 niro 532
4091 niro 3002 /* everything else is partition stuff */
4092     while (*chptr) {
4093     chptr2 = chptr;
4094     while (*chptr2 && *chptr2 != '[')
4095     chptr2++;
4096     if (!*chptr2)
4097     break;
4098 niro 532
4099 niro 3002 /* yank off the numbers at the end */
4100     chptr2--;
4101     while (isdigit(*chptr2) && chptr2 > chptr)
4102     chptr2--;
4103     chptr2++;
4104     *chptr2 = '\0';
4105 niro 532
4106 niro 3002 /* Better, now we need the /dev/ back. We're
4107     * done with everything before this point, so
4108     * we can just put the /dev/ part there.
4109     * There will always be room. */
4110     memcpy(chptr - 5, "/dev/", 5);
4111     rc = checkDeviceBootloader(chptr - 5, boot);
4112     if (rc != 2) {
4113     return rc;
4114     }
4115    
4116     chptr = chptr2 + 1;
4117     /* skip the [11] bit */
4118     while (*chptr && !isspace(*chptr))
4119     chptr++;
4120     /* and move to the next one */
4121     while (*chptr && isspace(*chptr))
4122     chptr++;
4123     }
4124    
4125     /* we're good to go */
4126     return 2;
4127 niro 532 }
4128    
4129 niro 3002 chptr = end + 1;
4130 niro 532 }
4131    
4132 niro 3002 fprintf(stderr,
4133     _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
4134     mdDev);
4135     return 0;
4136 niro 532 }
4137    
4138 niro 3002 int checkForLilo(struct grubConfig *config)
4139     {
4140     int fd;
4141     unsigned char boot[512];
4142     struct singleLine *line;
4143 niro 532
4144 niro 3002 for (line = config->theLines; line; line = line->next)
4145     if (line->type == LT_BOOT)
4146     break;
4147 niro 532
4148 niro 3002 if (!line) {
4149     fprintf(stderr,
4150     _
4151     ("grubby: no boot line found in lilo configuration\n"));
4152     return 1;
4153     }
4154 niro 532
4155 niro 3002 if (line->numElements != 2)
4156     return 1;
4157 niro 532
4158 niro 3002 fd = open("/boot/boot.b", O_RDONLY);
4159     if (fd < 0) {
4160     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
4161     "/boot/boot.b", strerror(errno));
4162     return 1;
4163     }
4164 niro 532
4165 niro 3002 if (read(fd, boot, 512) != 512) {
4166     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4167     "/boot/boot.b", strerror(errno));
4168     return 1;
4169     }
4170     close(fd);
4171 niro 532
4172 niro 3002 if (!strncmp("/dev/md", line->elements[1].item, 7))
4173     return checkLiloOnRaid(line->elements[1].item, boot);
4174 niro 532
4175 niro 3002 return checkDeviceBootloader(line->elements[1].item, boot);
4176 niro 532 }
4177    
4178 niro 3002 int checkForGrub2(struct grubConfig *config)
4179     {
4180     if (!access("/etc/grub.d/", R_OK))
4181     return 2;
4182 niro 1696
4183 niro 3002 return 1;
4184 niro 1696 }
4185    
4186 niro 3002 int checkForGrub(struct grubConfig *config)
4187     {
4188     int fd;
4189     unsigned char bootSect[512];
4190     char *boot;
4191     int onSuse = isSuseSystem();
4192 niro 532
4193 niro 3002 if (onSuse) {
4194     if (parseSuseGrubConf(NULL, &boot))
4195     return 0;
4196     } else {
4197     if (parseSysconfigGrub(NULL, &boot))
4198     return 0;
4199     }
4200 niro 1851
4201 niro 3002 /* assume grub is not installed -- not an error condition */
4202     if (!boot)
4203     return 0;
4204 niro 532
4205 niro 3002 fd = open("/boot/grub/stage1", O_RDONLY);
4206     if (fd < 0)
4207     /* this doesn't exist if grub hasn't been installed */
4208     return 0;
4209 niro 532
4210 niro 3002 if (read(fd, bootSect, 512) != 512) {
4211     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4212     "/boot/grub/stage1", strerror(errno));
4213     close(fd);
4214     return 1;
4215     }
4216     close(fd);
4217 niro 532
4218 niro 3002 /* The more elaborate checks do not work on SuSE. The checks done
4219     * seem to be reasonble (at least for now), so just return success
4220     */
4221     if (onSuse)
4222     return 2;
4223 niro 532
4224 niro 3002 return checkDeviceBootloader(boot, bootSect);
4225 niro 532 }
4226    
4227 niro 3002 int checkForExtLinux(struct grubConfig *config)
4228     {
4229     int fd;
4230     unsigned char bootSect[512];
4231     char *boot;
4232     char executable[] = "/boot/extlinux/extlinux";
4233 niro 914
4234 niro 3002 printf("entered: checkForExtLinux()\n");
4235 niro 914
4236 niro 3002 if (parseSysconfigGrub(NULL, &boot))
4237     return 0;
4238 niro 914
4239 niro 3002 /* assume grub is not installed -- not an error condition */
4240     if (!boot)
4241     return 0;
4242 niro 914
4243 niro 3002 fd = open(executable, O_RDONLY);
4244     if (fd < 0)
4245     /* this doesn't exist if grub hasn't been installed */
4246     return 0;
4247 niro 914
4248 niro 3002 if (read(fd, bootSect, 512) != 512) {
4249     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4250     executable, strerror(errno));
4251     return 1;
4252     }
4253     close(fd);
4254 niro 914
4255 niro 3002 return checkDeviceBootloader(boot, bootSect);
4256 niro 914 }
4257    
4258 niro 3002 int checkForYaboot(struct grubConfig *config)
4259     {
4260     /*
4261     * This is a simplistic check that we consider good enough for own puporses
4262     *
4263     * If we were to properly check if yaboot is *installed* we'd need to:
4264     * 1) get the system boot device (LT_BOOT)
4265     * 2) considering it's a raw filesystem, check if the yaboot binary matches
4266     * the content on the boot device
4267     * 3) if not, copy the binary to a temporary file and run "addnote" on it
4268     * 4) check again if binary and boot device contents match
4269     */
4270     if (!access("/etc/yaboot.conf", R_OK))
4271     return 2;
4272 niro 1853
4273 niro 3002 return 1;
4274 niro 1853 }
4275    
4276 niro 3002 int checkForElilo(struct grubConfig *config)
4277     {
4278     if (!access("/etc/elilo.conf", R_OK))
4279     return 2;
4280 niro 1854
4281 niro 3002 return 1;
4282 niro 1854 }
4283    
4284 niro 3002 static char *getRootSpecifier(char *str)
4285     {
4286     char *idx, *rootspec = NULL;
4287 niro 532
4288 niro 3002 if (*str == '(') {
4289     idx = rootspec = strdup(str);
4290     while (*idx && (*idx != ')') && (!isspace(*idx)))
4291     idx++;
4292     *(++idx) = '\0';
4293     }
4294     return rootspec;
4295 niro 532 }
4296    
4297 niro 3002 static char *getInitrdVal(struct grubConfig *config,
4298     const char *prefix, struct singleLine *tmplLine,
4299     const char *newKernelInitrd,
4300     const char **extraInitrds, int extraInitrdCount)
4301 niro 914 {
4302 niro 3002 char *initrdVal, *end;
4303     int i;
4304     size_t totalSize;
4305     size_t prefixLen;
4306     char separatorChar;
4307 niro 914
4308 niro 3002 prefixLen = strlen(prefix);
4309     totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */ ;
4310 niro 914
4311 niro 3002 for (i = 0; i < extraInitrdCount; i++) {
4312     totalSize += sizeof(separatorChar);
4313     totalSize += strlen(extraInitrds[i]) - prefixLen;
4314     }
4315 niro 914
4316 niro 3002 initrdVal = end = malloc(totalSize);
4317 niro 914
4318 niro 3002 end = stpcpy(end, newKernelInitrd + prefixLen);
4319 niro 914
4320 niro 3002 separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
4321     for (i = 0; i < extraInitrdCount; i++) {
4322     const char *extraInitrd;
4323     int j;
4324 niro 914
4325 niro 3002 extraInitrd = extraInitrds[i] + prefixLen;
4326     /* Don't add entries that are already there */
4327     if (tmplLine != NULL) {
4328     for (j = 2; j < tmplLine->numElements; j++)
4329     if (strcmp
4330     (extraInitrd,
4331     tmplLine->elements[j].item) == 0)
4332     break;
4333 niro 914
4334 niro 3002 if (j != tmplLine->numElements)
4335     continue;
4336     }
4337    
4338     *end++ = separatorChar;
4339     end = stpcpy(end, extraInitrd);
4340 niro 914 }
4341    
4342 niro 3002 return initrdVal;
4343 niro 914 }
4344    
4345 niro 3002 int addNewKernel(struct grubConfig *config, struct singleEntry *template,
4346     const char *prefix,
4347     const char *newKernelPath, const char *newKernelTitle,
4348     const char *newKernelArgs, const char *newKernelInitrd,
4349     const char **extraInitrds, int extraInitrdCount,
4350     const char *newMBKernel, const char *newMBKernelArgs,
4351 niro 3007 const char *newDevTreePath, int newIndex)
4352 niro 3002 {
4353 niro 3007 struct singleEntry *new, *entry, *prev = NULL;
4354 niro 3002 struct singleLine *newLine = NULL, *tmplLine = NULL, *masterLine = NULL;
4355     int needs;
4356 niro 3010 char *indexs;
4357 niro 3002 char *chptr;
4358 niro 3010 int rc;
4359 niro 532
4360 niro 3002 if (!newKernelPath)
4361     return 0;
4362 niro 532
4363 niro 3010 rc = asprintf(&indexs, "%d", newIndex);
4364     if (rc < 0)
4365     return 1;
4366    
4367 niro 3002 /* if the newKernelTitle is too long silently munge it into something
4368     * we can live with. truncating is first check, then we'll just mess with
4369     * it until it looks better */
4370     if (config->cfi->maxTitleLength &&
4371 niro 532 (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
4372 niro 3002 char *buf = alloca(config->cfi->maxTitleLength + 7);
4373     char *numBuf = alloca(config->cfi->maxTitleLength + 1);
4374     int i = 1;
4375 niro 532
4376 niro 3002 sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength,
4377     newKernelTitle);
4378     while (findEntryByPath(config, buf, NULL, NULL)) {
4379     sprintf(numBuf, "%d", i++);
4380     strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
4381     }
4382    
4383     newKernelTitle = buf + 6;
4384 niro 532 }
4385    
4386 niro 3002 new = malloc(sizeof(*new));
4387     new->skip = 0;
4388     new->multiboot = 0;
4389     new->lines = NULL;
4390 niro 3007 entry = config->entries;
4391     for (unsigned int i = 0; i < newIndex; i++) {
4392     if (!entry)
4393     break;
4394     prev = entry;
4395     entry = entry->next;
4396     }
4397     new->next = entry;
4398 niro 532
4399 niro 3007 if (prev)
4400     prev->next = new;
4401     else
4402     config->entries = new;
4403    
4404 niro 3002 /* copy/update from the template */
4405     needs = NEED_KERNEL | NEED_TITLE;
4406     if (newKernelInitrd)
4407     needs |= NEED_INITRD;
4408     if (newMBKernel) {
4409     needs |= NEED_MB;
4410     new->multiboot = 1;
4411     }
4412     if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))
4413     needs |= NEED_DEVTREE;
4414 niro 532
4415 niro 3002 if (template) {
4416     for (masterLine = template->lines;
4417     masterLine && (tmplLine = lineDup(masterLine));
4418     lineFree(tmplLine), masterLine = masterLine->next) {
4419     dbgPrintf("addNewKernel processing %d\n",
4420     tmplLine->type);
4421 niro 532
4422 niro 3002 /* skip comments */
4423     chptr = tmplLine->indent;
4424     while (*chptr && isspace(*chptr))
4425     chptr++;
4426     if (*chptr == '#')
4427     continue;
4428 niro 532
4429 niro 3002 if (iskernel(tmplLine->type)
4430     && tmplLine->numElements >= 2) {
4431     if (!template->multiboot && (needs & NEED_MB)) {
4432     /* it's not a multiboot template and
4433     * this is the kernel line. Try to
4434     * be intelligent about inserting the
4435     * hypervisor at the same time.
4436     */
4437     if (config->cfi->mbHyperFirst) {
4438     /* insert the hypervisor first */
4439     newLine =
4440     addLine(new, config->cfi,
4441     LT_HYPER,
4442     tmplLine->indent,
4443     newMBKernel +
4444     strlen(prefix));
4445     /* set up for adding the
4446     * kernel line */
4447     free(tmplLine->indent);
4448     tmplLine->indent =
4449     strdup(config->
4450     secondaryIndent);
4451     needs &= ~NEED_MB;
4452     }
4453     if (needs & NEED_KERNEL) {
4454     /* use addLineTmpl to
4455     * preserve line elements,
4456     * otherwise we could just
4457     * call addLine.
4458     * Unfortunately this means
4459     * making some changes to the
4460     * template such as the
4461     * indent change above and
4462     * the type change below.
4463     */
4464     struct keywordTypes *mbm_kw =
4465     getKeywordByType
4466     (LT_MBMODULE, config->cfi);
4467     if (mbm_kw) {
4468     tmplLine->type =
4469     LT_MBMODULE;
4470     free(tmplLine->
4471     elements[0].item);
4472     tmplLine->elements[0].
4473     item =
4474     strdup(mbm_kw->key);
4475     }
4476     newLine =
4477     addLineTmpl(new, tmplLine,
4478     newLine,
4479     newKernelPath +
4480     strlen(prefix),
4481     config->cfi);
4482     needs &= ~NEED_KERNEL;
4483     }
4484     if (needs & NEED_MB) { /* !mbHyperFirst */
4485     newLine =
4486     addLine(new, config->cfi,
4487     LT_HYPER,
4488     config->
4489     secondaryIndent,
4490     newMBKernel +
4491     strlen(prefix));
4492     needs &= ~NEED_MB;
4493     }
4494     } else if (needs & NEED_KERNEL) {
4495     newLine =
4496     addLineTmpl(new, tmplLine, newLine,
4497     newKernelPath +
4498     strlen(prefix),
4499     config->cfi);
4500     needs &= ~NEED_KERNEL;
4501     }
4502 niro 532
4503 niro 3002 } else if (tmplLine->type == LT_HYPER &&
4504     tmplLine->numElements >= 2) {
4505     if (needs & NEED_MB) {
4506     newLine =
4507     addLineTmpl(new, tmplLine, newLine,
4508     newMBKernel +
4509     strlen(prefix),
4510     config->cfi);
4511     needs &= ~NEED_MB;
4512     }
4513 niro 532
4514 niro 3002 } else if (tmplLine->type == LT_MBMODULE &&
4515     tmplLine->numElements >= 2) {
4516     if (new->multiboot) {
4517     if (needs & NEED_KERNEL) {
4518     newLine =
4519     addLineTmpl(new, tmplLine,
4520     newLine,
4521     newKernelPath +
4522     strlen(prefix),
4523     config->cfi);
4524     needs &= ~NEED_KERNEL;
4525     } else if (config->cfi->mbInitRdIsModule
4526     && (needs & NEED_INITRD)) {
4527     char *initrdVal;
4528     initrdVal =
4529     getInitrdVal(config, prefix,
4530     tmplLine,
4531     newKernelInitrd,
4532     extraInitrds,
4533     extraInitrdCount);
4534     newLine =
4535     addLineTmpl(new, tmplLine,
4536     newLine,
4537     initrdVal,
4538     config->cfi);
4539     free(initrdVal);
4540     needs &= ~NEED_INITRD;
4541     }
4542     } else if (needs & NEED_KERNEL) {
4543     /* template is multi but new is not,
4544     * insert the kernel in the first
4545     * module slot
4546     */
4547     tmplLine->type =
4548     preferredLineType(LT_KERNEL,
4549     config->cfi);
4550     free(tmplLine->elements[0].item);
4551     tmplLine->elements[0].item =
4552     strdup(getKeywordByType
4553     (tmplLine->type,
4554     config->cfi)->key);
4555     newLine =
4556     addLineTmpl(new, tmplLine, newLine,
4557     newKernelPath +
4558     strlen(prefix),
4559     config->cfi);
4560     needs &= ~NEED_KERNEL;
4561     } else if (needs & NEED_INITRD) {
4562     char *initrdVal;
4563     /* template is multi but new is not,
4564     * insert the initrd in the second
4565     * module slot
4566     */
4567     tmplLine->type =
4568     preferredLineType(LT_INITRD,
4569     config->cfi);
4570     free(tmplLine->elements[0].item);
4571     tmplLine->elements[0].item =
4572     strdup(getKeywordByType
4573     (tmplLine->type,
4574     config->cfi)->key);
4575     initrdVal =
4576     getInitrdVal(config, prefix,
4577     tmplLine,
4578     newKernelInitrd,
4579     extraInitrds,
4580     extraInitrdCount);
4581     newLine =
4582     addLineTmpl(new, tmplLine, newLine,
4583     initrdVal, config->cfi);
4584     free(initrdVal);
4585     needs &= ~NEED_INITRD;
4586     }
4587 niro 532
4588 niro 3002 } else if (isinitrd(tmplLine->type)
4589     && tmplLine->numElements >= 2) {
4590     if (needs & NEED_INITRD && new->multiboot
4591     && !template->multiboot
4592     && config->cfi->mbInitRdIsModule) {
4593     /* make sure we don't insert the
4594     * module initrd before the module
4595     * kernel... if we don't do it here,
4596     * it will be inserted following the
4597     * template.
4598     */
4599     if (!needs & NEED_KERNEL) {
4600     char *initrdVal;
4601 niro 532
4602 niro 3002 initrdVal =
4603     getInitrdVal(config, prefix,
4604     tmplLine,
4605     newKernelInitrd,
4606     extraInitrds,
4607     extraInitrdCount);
4608     newLine =
4609     addLine(new, config->cfi,
4610     LT_MBMODULE,
4611     config->
4612     secondaryIndent,
4613     initrdVal);
4614     free(initrdVal);
4615     needs &= ~NEED_INITRD;
4616     }
4617     } else if (needs & NEED_INITRD) {
4618     char *initrdVal;
4619     initrdVal =
4620     getInitrdVal(config, prefix,
4621     tmplLine,
4622     newKernelInitrd,
4623     extraInitrds,
4624     extraInitrdCount);
4625     newLine =
4626     addLineTmpl(new, tmplLine, newLine,
4627     initrdVal, config->cfi);
4628     free(initrdVal);
4629     needs &= ~NEED_INITRD;
4630     }
4631 niro 532
4632 niro 3002 } else if (tmplLine->type == LT_MENUENTRY &&
4633     (needs & NEED_TITLE)) {
4634     requote(tmplLine, config->cfi);
4635     char *nkt = malloc(strlen(newKernelTitle) + 3);
4636     strcpy(nkt, "'");
4637     strcat(nkt, newKernelTitle);
4638     strcat(nkt, "'");
4639     newLine =
4640     addLineTmpl(new, tmplLine, newLine, nkt,
4641     config->cfi);
4642     free(nkt);
4643     needs &= ~NEED_TITLE;
4644     } else if (tmplLine->type == LT_TITLE &&
4645     (needs & NEED_TITLE)) {
4646     if (tmplLine->numElements >= 2) {
4647     newLine =
4648     addLineTmpl(new, tmplLine, newLine,
4649     newKernelTitle,
4650     config->cfi);
4651     needs &= ~NEED_TITLE;
4652     } else if (tmplLine->numElements == 1 &&
4653     config->cfi->titleBracketed) {
4654     /* addLineTmpl doesn't handle
4655     * titleBracketed */
4656     newLine =
4657     addLine(new, config->cfi, LT_TITLE,
4658     tmplLine->indent,
4659     newKernelTitle);
4660     needs &= ~NEED_TITLE;
4661     }
4662     } else if (tmplLine->type == LT_ECHO) {
4663     requote(tmplLine, config->cfi);
4664     static const char *prefix = "'Loading ";
4665     if (tmplLine->numElements > 1 &&
4666     strstr(tmplLine->elements[1].item, prefix)
4667     && masterLine->next
4668     && iskernel(masterLine->next->type)) {
4669     char *newTitle =
4670     malloc(strlen(prefix) +
4671     strlen(newKernelTitle) + 2);
4672 niro 532
4673 niro 3002 strcpy(newTitle, prefix);
4674     strcat(newTitle, newKernelTitle);
4675     strcat(newTitle, "'");
4676     newLine =
4677     addLine(new, config->cfi, LT_ECHO,
4678     tmplLine->indent, newTitle);
4679     free(newTitle);
4680     } else {
4681     /* pass through other lines from the
4682     * template */
4683     newLine =
4684     addLineTmpl(new, tmplLine, newLine,
4685     NULL, config->cfi);
4686     }
4687     } else if (tmplLine->type == LT_DEVTREE &&
4688     tmplLine->numElements == 2
4689     && newDevTreePath) {
4690     newLine =
4691     addLineTmpl(new, tmplLine, newLine,
4692     newDevTreePath + strlen(prefix),
4693 niro 1696 config->cfi);
4694 niro 3002 needs &= ~NEED_DEVTREE;
4695     } else if (tmplLine->type == LT_ENTRY_END
4696     && needs & NEED_DEVTREE) {
4697     const char *ndtp = newDevTreePath;
4698     if (!strncmp
4699     (newDevTreePath, prefix, strlen(prefix)))
4700     ndtp += strlen(prefix);
4701     newLine = addLine(new, config->cfi, LT_DEVTREE,
4702     config->secondaryIndent,
4703     ndtp);
4704     needs &= ~NEED_DEVTREE;
4705     newLine =
4706     addLineTmpl(new, tmplLine, newLine, NULL,
4707     config->cfi);
4708     } else {
4709     /* pass through other lines from the template */
4710     newLine =
4711     addLineTmpl(new, tmplLine, newLine, NULL,
4712     config->cfi);
4713     }
4714 niro 914 }
4715    
4716 niro 3002 } else {
4717     /* don't have a template, so start the entry with the
4718     * appropriate starting line
4719     */
4720     switch (config->cfi->entryStart) {
4721     case LT_KERNEL:
4722     case LT_KERNEL_EFI:
4723     case LT_KERNEL_16:
4724     if (new->multiboot && config->cfi->mbHyperFirst) {
4725     /* fall through to LT_HYPER */
4726     } else {
4727     newLine = addLine(new, config->cfi,
4728     preferredLineType(LT_KERNEL,
4729     config->
4730     cfi),
4731     config->primaryIndent,
4732     newKernelPath +
4733     strlen(prefix));
4734     needs &= ~NEED_KERNEL;
4735     break;
4736     }
4737 niro 532
4738 niro 3002 case LT_HYPER:
4739     newLine = addLine(new, config->cfi, LT_HYPER,
4740     config->primaryIndent,
4741     newMBKernel + strlen(prefix));
4742     needs &= ~NEED_MB;
4743     break;
4744 niro 914
4745 niro 3002 case LT_MENUENTRY:{
4746     char *nkt = malloc(strlen(newKernelTitle) + 3);
4747     strcpy(nkt, "'");
4748     strcat(nkt, newKernelTitle);
4749     strcat(nkt, "'");
4750     newLine =
4751     addLine(new, config->cfi, LT_MENUENTRY,
4752     config->primaryIndent, nkt);
4753     free(nkt);
4754     needs &= ~NEED_TITLE;
4755     needs |= NEED_END;
4756     break;
4757     }
4758     case LT_TITLE:
4759     if (useextlinuxmenu != 0) { // We just need useextlinuxmenu to not be zero (set above)
4760     char *templabel;
4761     int x = 0, y = 0;
4762    
4763     templabel = strdup(newKernelTitle);
4764     while (templabel[x]) {
4765     if (templabel[x] == ' ') {
4766     y = x;
4767     while (templabel[y]) {
4768     templabel[y] =
4769     templabel[y + 1];
4770     y++;
4771     }
4772 niro 914 }
4773 niro 3002 x++;
4774 niro 914 }
4775 niro 3002 newLine = addLine(new, config->cfi, LT_TITLE,
4776     config->primaryIndent,
4777     templabel);
4778     free(templabel);
4779     } else {
4780     newLine = addLine(new, config->cfi, LT_TITLE,
4781     config->primaryIndent,
4782     newKernelTitle);
4783 niro 914 }
4784 niro 3002 needs &= ~NEED_TITLE;
4785     break;
4786    
4787     default:
4788     abort();
4789 niro 914 }
4790 niro 3002 }
4791 niro 914
4792 niro 3002 struct singleLine *endLine = NULL;
4793     endLine = getLineByType(LT_ENTRY_END, new->lines);
4794     if (endLine) {
4795     removeLine(new, endLine);
4796     needs |= NEED_END;
4797 niro 532 }
4798    
4799 niro 3002 /* add the remainder of the lines, i.e. those that either
4800     * weren't present in the template, or in the case of no template,
4801     * all the lines following the entryStart.
4802     */
4803     if (needs & NEED_TITLE) {
4804     newLine = addLine(new, config->cfi, LT_TITLE,
4805     config->secondaryIndent, newKernelTitle);
4806     needs &= ~NEED_TITLE;
4807     }
4808     if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
4809     newLine = addLine(new, config->cfi, LT_HYPER,
4810     config->secondaryIndent,
4811     newMBKernel + strlen(prefix));
4812     needs &= ~NEED_MB;
4813     }
4814     if (needs & NEED_KERNEL) {
4815     newLine = addLine(new, config->cfi,
4816     (new->multiboot
4817     && getKeywordByType(LT_MBMODULE,
4818     config->cfi))
4819     ? LT_MBMODULE : preferredLineType(LT_KERNEL,
4820     config->
4821     cfi),
4822     config->secondaryIndent,
4823     newKernelPath + strlen(prefix));
4824     needs &= ~NEED_KERNEL;
4825     }
4826     if (needs & NEED_MB) {
4827     newLine = addLine(new, config->cfi, LT_HYPER,
4828     config->secondaryIndent,
4829     newMBKernel + strlen(prefix));
4830     needs &= ~NEED_MB;
4831     }
4832     if (needs & NEED_INITRD) {
4833     char *initrdVal;
4834     initrdVal =
4835     getInitrdVal(config, prefix, NULL, newKernelInitrd,
4836     extraInitrds, extraInitrdCount);
4837     newLine =
4838     addLine(new, config->cfi,
4839     (new->multiboot
4840     && getKeywordByType(LT_MBMODULE, config->cfi))
4841     ? LT_MBMODULE : preferredLineType(LT_INITRD,
4842     config->cfi),
4843     config->secondaryIndent, initrdVal);
4844     free(initrdVal);
4845     needs &= ~NEED_INITRD;
4846     }
4847     if (needs & NEED_DEVTREE) {
4848     newLine = addLine(new, config->cfi, LT_DEVTREE,
4849     config->secondaryIndent, newDevTreePath);
4850     needs &= ~NEED_DEVTREE;
4851     }
4852 niro 2709
4853 niro 3002 /* NEEDS_END must be last on bootloaders that need it... */
4854     if (needs & NEED_END) {
4855     newLine = addLine(new, config->cfi, LT_ENTRY_END,
4856     config->secondaryIndent, NULL);
4857     needs &= ~NEED_END;
4858     }
4859 niro 2685
4860 niro 3002 if (needs) {
4861     printf(_("grubby: needs=%d, aborting\n"), needs);
4862     abort();
4863     }
4864 niro 2709
4865 niro 3010 if (updateImage(config, indexs, prefix, newKernelArgs, NULL,
4866 niro 3019 newMBKernelArgs, NULL)) {
4867     config->isModified = 1;
4868 niro 3002 return 1;
4869 niro 3019 }
4870 niro 914
4871 niro 3002 return 0;
4872 niro 532 }
4873    
4874 niro 3002 int main(int argc, const char **argv)
4875     {
4876     poptContext optCon;
4877     const char *grubConfig = NULL;
4878     char *outputFile = NULL;
4879     int arg = 0;
4880     int flags = 0;
4881     int badImageOkay = 0;
4882     int configureGrub2 = 0;
4883     int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4884     int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4885     int configureExtLinux = 0;
4886     int bootloaderProbe = 0;
4887     int extraInitrdCount = 0;
4888     char *updateKernelPath = NULL;
4889     char *newKernelPath = NULL;
4890     char *removeKernelPath = NULL;
4891     char *newKernelArgs = NULL;
4892     char *newKernelInitrd = NULL;
4893     char *newKernelTitle = NULL;
4894     char *newDevTreePath = NULL;
4895     char *newMBKernel = NULL;
4896     char *newMBKernelArgs = NULL;
4897 niro 3007 int newIndex = 0;
4898 niro 3002 char *removeMBKernelArgs = NULL;
4899     char *removeMBKernel = NULL;
4900     char *bootPrefix = NULL;
4901     char *defaultKernel = NULL;
4902     char *removeArgs = NULL;
4903     char *kernelInfo = NULL;
4904     char *extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4905     char *envPath = NULL;
4906     const char *chptr = NULL;
4907     struct configFileInfo *cfi = NULL;
4908     struct grubConfig *config;
4909     struct singleEntry *template = NULL;
4910     int copyDefault = 0, makeDefault = 0;
4911     int displayDefault = 0;
4912     int displayDefaultIndex = 0;
4913     int displayDefaultTitle = 0;
4914     int defaultIndex = -1;
4915     struct poptOption options[] = {
4916     {"add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4917     _("add an entry for the specified kernel"), _("kernel-path")},
4918     {"add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
4919     _("add an entry for the specified multiboot kernel"), NULL},
4920     {"args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
4921     _("default arguments for the new kernel or new arguments for "
4922     "kernel being updated"), _("args")},
4923     {"mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
4924     _("default arguments for the new multiboot kernel or "
4925     "new arguments for multiboot kernel being updated"), NULL},
4926     {"bad-image-okay", 0, 0, &badImageOkay, 0,
4927     _
4928     ("don't sanity check images in boot entries (for testing only)"),
4929     NULL},
4930     {"boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4931     _
4932 niro 3014 ("filesystem which contains /boot directory (for testing only)"),
4933 niro 3002 _("bootfs")},
4934 niro 1854 #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4935 niro 3002 {"bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4936     _("check which bootloader is installed on boot sector")},
4937 niro 532 #endif
4938 niro 3002 {"config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4939     _("path to grub config file to update (\"-\" for stdin)"),
4940     _("path")},
4941     {"copy-default", 0, 0, &copyDefault, 0,
4942     _("use the default boot entry as a template for the new entry "
4943     "being added; if the default is not a linux image, or if "
4944     "the kernel referenced by the default image does not exist, "
4945     "the first linux entry whose kernel does exist is used as the "
4946     "template"), NULL},
4947     {"debug", 0, 0, &debug, 0,
4948     _("print debugging information for failures")},
4949     {"default-kernel", 0, 0, &displayDefault, 0,
4950     _("display the path of the default kernel")},
4951     {"default-index", 0, 0, &displayDefaultIndex, 0,
4952     _("display the index of the default kernel")},
4953     {"default-title", 0, 0, &displayDefaultTitle, 0,
4954     _("display the title of the default kernel")},
4955     {"devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4956     _("device tree file for new stanza"), _("dtb-path")},
4957     {"devtreedir", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4958     _("device tree directory for new stanza"), _("dtb-path")},
4959     {"elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
4960     _("configure elilo bootloader")},
4961     {"efi", 0, POPT_ARG_NONE, &isEfi, 0,
4962     _("force grub2 stanzas to use efi")},
4963     {"env", 0, POPT_ARG_STRING, &envPath, 0,
4964     _("path for environment data"),
4965     _("path")},
4966     {"extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
4967     _("configure extlinux bootloader (from syslinux)")},
4968     {"grub", 0, POPT_ARG_NONE, &configureGrub, 0,
4969     _("configure grub bootloader")},
4970     {"grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
4971     _("configure grub2 bootloader")},
4972     {"info", 0, POPT_ARG_STRING, &kernelInfo, 0,
4973     _("display boot information for specified kernel"),
4974     _("kernel-path")},
4975     {"initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
4976     _("initrd image for the new kernel"), _("initrd-path")},
4977     {"extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
4978     _
4979     ("auxiliary initrd image for things other than the new kernel"),
4980     _("initrd-path")},
4981     {"lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
4982     _("configure lilo bootloader")},
4983     {"make-default", 0, 0, &makeDefault, 0,
4984     _("make the newly added entry the default boot entry"), NULL},
4985     {"output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
4986     _("path to output updated config file (\"-\" for stdout)"),
4987     _("path")},
4988     {"remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
4989     _("remove kernel arguments"), NULL},
4990     {"remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
4991     _("remove multiboot kernel arguments"), NULL},
4992     {"remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
4993     _("remove all entries for the specified kernel"),
4994     _("kernel-path")},
4995     {"remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
4996     _("remove all entries for the specified multiboot kernel"),
4997     NULL},
4998     {"set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
4999     _("make the first entry referencing the specified kernel "
5000     "the default"), _("kernel-path")},
5001     {"set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
5002     _("make the given entry index the default entry"),
5003     _("entry-index")},
5004 niro 3007 {"set-index", 0, POPT_ARG_INT, &newIndex, 0,
5005     _("use the given index when creating a new entry"),
5006     _("entry-index")},
5007 niro 3002 {"silo", 0, POPT_ARG_NONE, &configureSilo, 0,
5008     _("configure silo bootloader")},
5009     {"title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
5010     _("title to use for the new kernel entry"), _("entry-title")},
5011     {"update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
5012     _("updated information for the specified kernel"),
5013     _("kernel-path")},
5014     {"version", 'v', 0, NULL, 'v',
5015     _("print the version of this program and exit"), NULL},
5016     {"yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
5017     _("configure yaboot bootloader")},
5018     {"zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
5019     _("configure zipl bootloader")},
5020     POPT_AUTOHELP {0, 0, 0, 0, 0}
5021     };
5022 niro 532
5023 niro 3002 useextlinuxmenu = 0;
5024 niro 914
5025 niro 3002 int i = 0;
5026     for (int j = 1; j < argc; j++)
5027     i += strlen(argv[j]) + 1;
5028     saved_command_line = malloc(i);
5029     if (!saved_command_line) {
5030     fprintf(stderr, "grubby: %m\n");
5031     exit(1);
5032     }
5033     saved_command_line[0] = '\0';
5034     for (int j = 1; j < argc; j++) {
5035     strcat(saved_command_line, argv[j]);
5036     strncat(saved_command_line, j == argc - 1 ? "" : " ", 1);
5037     }
5038 niro 2236
5039 niro 3002 optCon = poptGetContext("grubby", argc, argv, options, 0);
5040     poptReadDefaultConfig(optCon, 1);
5041 niro 532
5042 niro 3002 while ((arg = poptGetNextOpt(optCon)) >= 0) {
5043     switch (arg) {
5044     case 'v':
5045     printf("grubby version %s\n", VERSION);
5046     exit(0);
5047     break;
5048     case 'i':
5049     if (extraInitrdCount < MAX_EXTRA_INITRDS) {
5050     extraInitrds[extraInitrdCount++] =
5051     strdup(poptGetOptArg(optCon));
5052     } else {
5053     fprintf(stderr,
5054     _
5055     ("grubby: extra initrd maximum is %d\n"),
5056     extraInitrdCount);
5057     return 1;
5058     }
5059     break;
5060     }
5061     }
5062    
5063     if (arg < -1) {
5064     fprintf(stderr, _("grubby: bad argument %s: %s\n"),
5065     poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
5066     poptStrerror(arg));
5067 niro 914 return 1;
5068 niro 532 }
5069    
5070 niro 3002 if ((chptr = poptGetArg(optCon))) {
5071     fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
5072     return 1;
5073     }
5074 niro 532
5075 niro 3002 if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
5076     configureYaboot + configureSilo + configureZipl +
5077     configureExtLinux) > 1) {
5078     fprintf(stderr,
5079     _("grubby: cannot specify multiple bootloaders\n"));
5080     return 1;
5081     } else if (bootloaderProbe && grubConfig) {
5082     fprintf(stderr,
5083     _
5084     ("grubby: cannot specify config file with --bootloader-probe\n"));
5085     return 1;
5086     } else if (configureGrub2) {
5087     cfi = &grub2ConfigType;
5088     if (envPath)
5089     cfi->envFile = envPath;
5090     } else if (configureLilo) {
5091     cfi = &liloConfigType;
5092     } else if (configureGrub) {
5093     cfi = &grubConfigType;
5094     } else if (configureELilo) {
5095     cfi = &eliloConfigType;
5096     } else if (configureYaboot) {
5097     cfi = &yabootConfigType;
5098     } else if (configureSilo) {
5099     cfi = &siloConfigType;
5100     } else if (configureZipl) {
5101     cfi = &ziplConfigType;
5102     } else if (configureExtLinux) {
5103     cfi = &extlinuxConfigType;
5104     useextlinuxmenu = 1;
5105     }
5106 niro 532
5107 niro 3002 if (!cfi) {
5108     if (grub2FindConfig(&grub2ConfigType)) {
5109     cfi = &grub2ConfigType;
5110     if (envPath)
5111     cfi->envFile = envPath;
5112     } else
5113     #ifdef __ia64__
5114     cfi = &eliloConfigType;
5115     #elif __powerpc__
5116     cfi = &yabootConfigType;
5117     #elif __sparc__
5118     cfi = &siloConfigType;
5119     #elif __s390__
5120     cfi = &ziplConfigType;
5121     #elif __s390x__
5122     cfi = &ziplConfigtype;
5123     #else
5124     cfi = &grubConfigType;
5125     #endif
5126     }
5127 niro 532
5128 niro 3002 if (!grubConfig) {
5129     if (cfi->findConfig)
5130     grubConfig = cfi->findConfig(cfi);
5131     if (!grubConfig)
5132     grubConfig = cfi->defaultConfig;
5133     }
5134 niro 532
5135 niro 3002 if (bootloaderProbe && (displayDefault || kernelInfo ||
5136     newKernelPath || removeKernelPath || makeDefault
5137     || defaultKernel || displayDefaultIndex
5138     || displayDefaultTitle
5139     || (defaultIndex >= 0))) {
5140     fprintf(stderr,
5141     _("grubby: --bootloader-probe may not be used with "
5142 niro 532 "specified option"));
5143 niro 3002 return 1;
5144     }
5145 niro 532
5146 niro 3002 if ((displayDefault || kernelInfo) && (newKernelPath ||
5147     removeKernelPath)) {
5148     fprintf(stderr, _("grubby: --default-kernel and --info may not "
5149     "be used when adding or removing kernels\n"));
5150     return 1;
5151     }
5152 niro 532
5153 niro 3002 if (newKernelPath && !newKernelTitle) {
5154     fprintf(stderr, _("grubby: kernel title must be specified\n"));
5155     return 1;
5156     } else if (!newKernelPath && (copyDefault ||
5157     (newKernelInitrd && !updateKernelPath) ||
5158     makeDefault || extraInitrdCount > 0)) {
5159     fprintf(stderr, _("grubby: kernel path expected\n"));
5160     return 1;
5161     }
5162 niro 532
5163 niro 3002 if (newKernelPath && updateKernelPath) {
5164     fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
5165     "not be used together"));
5166     return 1;
5167     }
5168 niro 532
5169 niro 3002 if (makeDefault && defaultKernel) {
5170     fprintf(stderr, _("grubby: --make-default and --default-kernel "
5171     "may not be used together\n"));
5172     return 1;
5173     } else if (defaultKernel && removeKernelPath &&
5174     !strcmp(defaultKernel, removeKernelPath)) {
5175     fprintf(stderr,
5176     _("grubby: cannot make removed kernel the default\n"));
5177     return 1;
5178     } else if (defaultKernel && newKernelPath &&
5179     !strcmp(defaultKernel, newKernelPath)) {
5180     makeDefault = 1;
5181     defaultKernel = NULL;
5182     } else if (defaultKernel && (defaultIndex >= 0)) {
5183     fprintf(stderr,
5184     _("grubby: --set-default and --set-default-index "
5185 niro 532 "may not be used together\n"));
5186 niro 3002 return 1;
5187     }
5188 niro 532
5189 niro 3002 if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
5190     fprintf(stderr,
5191     _("grubby: output file must be specified if stdin "
5192     "is used\n"));
5193     return 1;
5194     }
5195 niro 532
5196 niro 3002 if (!removeKernelPath && !newKernelPath && !displayDefault
5197     && !defaultKernel && !kernelInfo && !bootloaderProbe
5198     && !updateKernelPath && !removeMBKernel && !displayDefaultIndex
5199     && !displayDefaultTitle && (defaultIndex == -1)) {
5200     fprintf(stderr, _("grubby: no action specified\n"));
5201     return 1;
5202     }
5203 niro 532
5204 niro 3002 flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
5205 niro 532
5206 niro 3002 if (cfi->needsBootPrefix) {
5207     if (!bootPrefix) {
5208     bootPrefix = findBootPrefix();
5209     if (!bootPrefix)
5210     return 1;
5211     } else {
5212     /* this shouldn't end with a / */
5213     if (bootPrefix[strlen(bootPrefix) - 1] == '/')
5214     bootPrefix[strlen(bootPrefix) - 1] = '\0';
5215     }
5216 niro 532 } else {
5217 niro 3002 bootPrefix = "";
5218 niro 532 }
5219    
5220 niro 3002 if (!cfi->mbAllowExtraInitRds && extraInitrdCount > 0) {
5221     fprintf(stderr,
5222     _("grubby: %s doesn't allow multiple initrds\n"),
5223     cfi->defaultConfig);
5224     return 1;
5225     }
5226 niro 914
5227 niro 3002 if (bootloaderProbe) {
5228     int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
5229     struct grubConfig *lconfig, *gconfig, *yconfig, *econfig;
5230 niro 532
5231 niro 3002 const char *grub2config = grub2FindConfig(&grub2ConfigType);
5232     if (grub2config) {
5233     gconfig = readConfig(grub2config, &grub2ConfigType);
5234     if (!gconfig)
5235     gr2c = 1;
5236     else
5237     gr2c = checkForGrub2(gconfig);
5238     }
5239 niro 1696
5240 niro 3002 const char *grubconfig = grubFindConfig(&grubConfigType);
5241     if (!access(grubconfig, F_OK)) {
5242     gconfig = readConfig(grubconfig, &grubConfigType);
5243     if (!gconfig)
5244     grc = 1;
5245     else
5246     grc = checkForGrub(gconfig);
5247     }
5248 niro 532
5249 niro 3002 if (!access(liloConfigType.defaultConfig, F_OK)) {
5250     lconfig =
5251     readConfig(liloConfigType.defaultConfig,
5252     &liloConfigType);
5253     if (!lconfig)
5254     lrc = 1;
5255     else
5256     lrc = checkForLilo(lconfig);
5257     }
5258 niro 532
5259 niro 3002 if (!access(eliloConfigType.defaultConfig, F_OK)) {
5260     econfig = readConfig(eliloConfigType.defaultConfig,
5261     &eliloConfigType);
5262     if (!econfig)
5263     erc = 1;
5264     else
5265     erc = checkForElilo(econfig);
5266     }
5267 niro 1854
5268 niro 3002 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
5269     lconfig =
5270     readConfig(extlinuxConfigType.defaultConfig,
5271     &extlinuxConfigType);
5272     if (!lconfig)
5273     extrc = 1;
5274     else
5275     extrc = checkForExtLinux(lconfig);
5276     }
5277 niro 914
5278 niro 3002 if (!access(yabootConfigType.defaultConfig, F_OK)) {
5279     yconfig = readConfig(yabootConfigType.defaultConfig,
5280     &yabootConfigType);
5281     if (!yconfig)
5282     yrc = 1;
5283     else
5284     yrc = checkForYaboot(yconfig);
5285     }
5286 niro 532
5287 niro 3002 if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1
5288     || erc == 1)
5289     return 1;
5290 niro 1853
5291 niro 3002 if (lrc == 2)
5292     printf("lilo\n");
5293     if (gr2c == 2)
5294     printf("grub2\n");
5295     if (grc == 2)
5296     printf("grub\n");
5297     if (extrc == 2)
5298     printf("extlinux\n");
5299     if (yrc == 2)
5300     printf("yaboot\n");
5301     if (erc == 2)
5302     printf("elilo\n");
5303 niro 1853
5304 niro 3002 return 0;
5305     }
5306 niro 532
5307 niro 3002 if (grubConfig == NULL) {
5308     printf("Could not find bootloader configuration file.\n");
5309     exit(1);
5310     }
5311 niro 532
5312 niro 3002 config = readConfig(grubConfig, cfi);
5313     if (!config)
5314     return 1;
5315 niro 2246
5316 niro 3002 if (displayDefault) {
5317     struct singleLine *line;
5318     struct singleEntry *entry;
5319     char *rootspec;
5320 niro 532
5321 niro 3018 if (config->defaultImage == NO_DEFAULT_ENTRY)
5322 niro 3002 return 0;
5323     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5324     cfi->defaultIsSaved)
5325 niro 3018 config->defaultImage = FIRST_ENTRY_INDEX;
5326 niro 3002 entry = findEntryByIndex(config, config->defaultImage);
5327     if (!entry)
5328     return 0;
5329     if (!suitableImage(entry, bootPrefix, 0, flags))
5330     return 0;
5331 niro 532
5332 niro 3002 line =
5333     getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI |
5334     LT_KERNEL_16, entry->lines);
5335     if (!line)
5336     return 0;
5337 niro 532
5338 niro 3002 rootspec = getRootSpecifier(line->elements[1].item);
5339     printf("%s%s\n", bootPrefix, line->elements[1].item +
5340     ((rootspec != NULL) ? strlen(rootspec) : 0));
5341 niro 532
5342 niro 3002 return 0;
5343 niro 532
5344 niro 3002 } else if (displayDefaultTitle) {
5345     struct singleLine *line;
5346     struct singleEntry *entry;
5347 niro 1720
5348 niro 3018 if (config->defaultImage == NO_DEFAULT_ENTRY)
5349 niro 3002 return 0;
5350     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5351     cfi->defaultIsSaved)
5352 niro 3018 config->defaultImage = FIRST_ENTRY_INDEX;
5353 niro 3002 entry = findEntryByIndex(config, config->defaultImage);
5354     if (!entry)
5355     return 0;
5356 niro 1721
5357 niro 3002 if (!configureGrub2) {
5358     char *title;
5359     line = getLineByType(LT_TITLE, entry->lines);
5360     if (!line)
5361     return 0;
5362     title = extractTitle(config, line);
5363     if (!title)
5364     return 0;
5365     printf("%s\n", title);
5366     free(title);
5367     } else {
5368     char *title;
5369 niro 1721
5370 niro 3002 dbgPrintf
5371     ("This is GRUB2, default title is embeded in menuentry\n");
5372     line = getLineByType(LT_MENUENTRY, entry->lines);
5373     if (!line)
5374     return 0;
5375     title = grub2ExtractTitle(line);
5376     if (title)
5377     printf("%s\n", title);
5378     }
5379 niro 2992 return 0;
5380 niro 1721
5381 niro 3002 } else if (displayDefaultIndex) {
5382 niro 3018 if (config->defaultImage == NO_DEFAULT_ENTRY)
5383 niro 3002 return 0;
5384     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5385     cfi->defaultIsSaved)
5386 niro 3018 config->defaultImage = FIRST_ENTRY_INDEX;
5387 niro 3002 printf("%i\n", config->defaultImage);
5388 niro 2992 return 0;
5389 niro 1721
5390 niro 3002 } else if (kernelInfo)
5391     return displayInfo(config, kernelInfo, bootPrefix);
5392 niro 1720
5393 niro 3002 if (copyDefault) {
5394     template = findTemplate(config, bootPrefix, NULL, 0, flags);
5395     if (!template)
5396     return 1;
5397     }
5398 niro 532
5399 niro 3002 markRemovedImage(config, removeKernelPath, bootPrefix);
5400     markRemovedImage(config, removeMBKernel, bootPrefix);
5401     setDefaultImage(config, newKernelPath != NULL, defaultKernel,
5402 niro 3021 makeDefault, bootPrefix, flags, defaultIndex,
5403     newIndex);
5404 niro 3002 setFallbackImage(config, newKernelPath != NULL);
5405     if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
5406     removeArgs, newMBKernelArgs, removeMBKernelArgs))
5407     return 1;
5408     if (updateKernelPath && newKernelInitrd) {
5409     if (newMBKernel) {
5410     if (addMBInitrd(config, newMBKernel, updateKernelPath,
5411 niro 2960 bootPrefix, newKernelInitrd,
5412     newKernelTitle))
5413 niro 3002 return 1;
5414     } else {
5415     if (updateInitrd(config, updateKernelPath, bootPrefix,
5416     newKernelInitrd, newKernelTitle))
5417     return 1;
5418     }
5419     }
5420     if (addNewKernel(config, template, bootPrefix, newKernelPath,
5421     newKernelTitle, newKernelArgs, newKernelInitrd,
5422     (const char **)extraInitrds, extraInitrdCount,
5423 niro 3007 newMBKernel, newMBKernelArgs, newDevTreePath,
5424     newIndex))
5425 niro 3002 return 1;
5426 niro 532
5427 niro 3002 if (numEntries(config) == 0) {
5428     fprintf(stderr,
5429     _("grubby: doing this would leave no kernel entries. "
5430     "Not writing out new config.\n"));
5431     return 1;
5432     }
5433 niro 532
5434 niro 3002 if (!outputFile)
5435     outputFile = (char *)grubConfig;
5436 niro 532
5437 niro 3002 return writeConfig(config, outputFile, bootPrefix);
5438 niro 532 }