Magellan Linux

Annotation of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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