Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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