Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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