Magellan Linux

Annotation of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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