Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2957 - (hide annotations) (download)
Wed Jun 29 14:06:18 2016 UTC (7 years, 10 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 129517 byte(s)
grubby: fix initrd updating when multiboot exist
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 niro 2718
1964 niro 532 i = 0;
1965     if (index) {
1966 niro 2718 while (i < *index) {
1967     i++;
1968     if (indexVars[i] == -1) return NULL;
1969     }
1970 niro 532 }
1971    
1972     entry = findEntryByIndex(config, indexVars[i]);
1973     if (!entry) return NULL;
1974    
1975 niro 2683 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
1976 niro 532 if (!line) return NULL;
1977    
1978     if (index) *index = indexVars[i];
1979     return entry;
1980     }
1981    
1982     if (!strcmp(kernel, "DEFAULT")) {
1983     if (index && *index > config->defaultImage) {
1984     entry = NULL;
1985     } else {
1986     entry = findEntryByIndex(config, config->defaultImage);
1987     if (entry && entry->skip)
1988     entry = NULL;
1989     else if (index)
1990     *index = config->defaultImage;
1991     }
1992     } else if (!strcmp(kernel, "ALL")) {
1993     if (index)
1994     i = *index;
1995     else
1996     i = 0;
1997    
1998     while ((entry = findEntryByIndex(config, i))) {
1999     if (!entry->skip) break;
2000     i++;
2001     }
2002    
2003     if (entry && index)
2004     *index = i;
2005     } else {
2006     if (index)
2007     i = *index;
2008     else
2009     i = 0;
2010    
2011     if (!strncmp(kernel, "TITLE=", 6)) {
2012     prefix = "";
2013 niro 1801 checkType = LT_TITLE|LT_MENUENTRY;
2014 niro 532 kernel += 6;
2015     }
2016    
2017 niro 914 for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {
2018     if (entry->skip) continue;
2019 niro 532
2020 niro 914 dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);
2021 niro 532
2022 niro 914 /* check all the lines matching checkType */
2023     for (line = entry->lines; line; line = line->next) {
2024 niro 2052 enum lineType_e ct = checkType;
2025     if (entry->multiboot && checkType == LT_KERNEL)
2026 niro 2683 ct = LT_KERNEL|LT_KERNEL_EFI|LT_MBMODULE|LT_HYPER|LT_KERNEL_16;
2027 niro 2052 else if (checkType & LT_KERNEL)
2028 niro 2683 ct = checkType | LT_KERNEL_EFI | LT_KERNEL_16;
2029 niro 2052 line = getLineByType(ct, line);
2030     if (!line)
2031     break; /* not found in this entry */
2032 niro 532
2033 niro 1801 if (line && line->type != LT_MENUENTRY &&
2034     line->numElements >= 2) {
2035 niro 914 rootspec = getRootSpecifier(line->elements[1].item);
2036     if (!strcmp(line->elements[1].item +
2037     ((rootspec != NULL) ? strlen(rootspec) : 0),
2038     kernel + strlen(prefix)))
2039     break;
2040     }
2041 niro 1801 if(line->type == LT_MENUENTRY &&
2042     !strcmp(line->elements[1].item, kernel))
2043     break;
2044 niro 914 }
2045    
2046     /* make sure this entry has a kernel identifier; this skips
2047     * non-Linux boot entries (could find netbsd etc, though, which is
2048     * unfortunate)
2049     */
2050 niro 2683 if (line && getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines))
2051 niro 914 break; /* found 'im! */
2052 niro 532 }
2053    
2054     if (index) *index = i;
2055     }
2056    
2057     return entry;
2058     }
2059    
2060 niro 2252 struct singleEntry * findEntryByTitle(struct grubConfig * cfg, char *title,
2061     int * index) {
2062     struct singleEntry * entry;
2063     struct singleLine * line;
2064     int i;
2065     char * newtitle;
2066    
2067     for (i = 0, entry = cfg->entries; entry; entry = entry->next, i++) {
2068     if (index && i < *index)
2069     continue;
2070     line = getLineByType(LT_TITLE, entry->lines);
2071     if (!line)
2072     line = getLineByType(LT_MENUENTRY, entry->lines);
2073     if (!line)
2074     continue;
2075     newtitle = grub2ExtractTitle(line);
2076     if (!newtitle)
2077     continue;
2078     if (!strcmp(title, newtitle))
2079     break;
2080     }
2081    
2082     if (!entry)
2083     return NULL;
2084    
2085     if (index)
2086     *index = i;
2087     return entry;
2088     }
2089    
2090 niro 532 struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index) {
2091     struct singleEntry * entry;
2092    
2093     entry = cfg->entries;
2094     while (index && entry) {
2095     entry = entry->next;
2096     index--;
2097     }
2098    
2099     return entry;
2100     }
2101    
2102     /* Find a good template to use for the new kernel. An entry is
2103     * good if the kernel and mkinitrd exist (even if the entry
2104     * is going to be removed). Try and use the default entry, but
2105     * if that doesn't work just take the first. If we can't find one,
2106     * bail. */
2107     struct singleEntry * findTemplate(struct grubConfig * cfg, const char * prefix,
2108     int * indexPtr, int skipRemoved, int flags) {
2109     struct singleEntry * entry, * entry2;
2110     int index;
2111    
2112 niro 2252 if (cfg->cfi->defaultIsSaved) {
2113     if (cfg->cfi->getEnv) {
2114     char *defTitle = cfg->cfi->getEnv(cfg->cfi, "saved_entry");
2115     if (defTitle) {
2116     int index = 0;
2117 niro 2258 if (isnumber(defTitle)) {
2118     index = atoi(defTitle);
2119     entry = findEntryByIndex(cfg, index);
2120     } else {
2121     entry = findEntryByTitle(cfg, defTitle, &index);
2122     }
2123     if (entry)
2124     cfg->defaultImage = index;
2125 niro 2252 }
2126     }
2127     } else if (cfg->defaultImage > -1) {
2128 niro 532 entry = findEntryByIndex(cfg, cfg->defaultImage);
2129     if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2130     if (indexPtr) *indexPtr = cfg->defaultImage;
2131     return entry;
2132     }
2133     }
2134    
2135     index = 0;
2136     while ((entry = findEntryByIndex(cfg, index))) {
2137     if (suitableImage(entry, prefix, skipRemoved, flags)) {
2138     int j;
2139     for (j = 0; j < index; j++) {
2140     entry2 = findEntryByIndex(cfg, j);
2141     if (entry2->skip) index--;
2142     }
2143     if (indexPtr) *indexPtr = index;
2144    
2145     return entry;
2146     }
2147    
2148     index++;
2149     }
2150    
2151     fprintf(stderr, _("grubby fatal error: unable to find a suitable template\n"));
2152    
2153     return NULL;
2154     }
2155    
2156     char * findBootPrefix(void) {
2157     struct stat sb, sb2;
2158    
2159     stat("/", &sb);
2160     #ifdef __ia64__
2161     stat("/boot/efi/EFI/redhat/", &sb2);
2162     #else
2163     stat("/boot", &sb2);
2164     #endif
2165    
2166     if (sb.st_dev == sb2.st_dev)
2167     return strdup("");
2168    
2169     #ifdef __ia64__
2170     return strdup("/boot/efi/EFI/redhat/");
2171     #else
2172     return strdup("/boot");
2173     #endif
2174     }
2175    
2176     void markRemovedImage(struct grubConfig * cfg, const char * image,
2177     const char * prefix) {
2178     struct singleEntry * entry;
2179    
2180 niro 1801 if (!image)
2181     return;
2182 niro 532
2183 niro 1801 /* check and see if we're removing the default image */
2184     if (isdigit(*image)) {
2185     entry = findEntryByPath(cfg, image, prefix, NULL);
2186     if(entry)
2187     entry->skip = 1;
2188     return;
2189     }
2190    
2191 niro 532 while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
2192     entry->skip = 1;
2193     }
2194    
2195     void setDefaultImage(struct grubConfig * config, int hasNew,
2196     const char * defaultKernelPath, int newIsDefault,
2197 niro 1859 const char * prefix, int flags, int index) {
2198 niro 532 struct singleEntry * entry, * entry2, * newDefault;
2199     int i, j;
2200    
2201     if (newIsDefault) {
2202     config->defaultImage = 0;
2203     return;
2204 niro 1859 } else if ((index >= 0) && config->cfi->defaultIsIndex) {
2205     if (findEntryByIndex(config, index))
2206     config->defaultImage = index;
2207     else
2208     config->defaultImage = -1;
2209     return;
2210 niro 532 } else if (defaultKernelPath) {
2211     i = 0;
2212     if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
2213     config->defaultImage = i;
2214     } else {
2215     config->defaultImage = -1;
2216     return;
2217     }
2218     }
2219    
2220     /* defaultImage now points to what we'd like to use, but before any order
2221     changes */
2222 niro 1748 if ((config->defaultImage == DEFAULT_SAVED) ||
2223     (config->defaultImage == DEFAULT_SAVED_GRUB2))
2224 niro 532 /* default is set to saved, we don't want to change it */
2225     return;
2226    
2227     if (config->defaultImage > -1)
2228     entry = findEntryByIndex(config, config->defaultImage);
2229     else
2230     entry = NULL;
2231    
2232     if (entry && !entry->skip) {
2233     /* we can preserve the default */
2234     if (hasNew)
2235     config->defaultImage++;
2236    
2237     /* count the number of entries erased before this one */
2238     for (j = 0; j < config->defaultImage; j++) {
2239     entry2 = findEntryByIndex(config, j);
2240     if (entry2->skip) config->defaultImage--;
2241     }
2242     } else if (hasNew) {
2243     config->defaultImage = 0;
2244     } else {
2245     /* Either we just erased the default (or the default line was bad
2246     * to begin with) and didn't put a new one in. We'll use the first
2247     * valid image. */
2248     newDefault = findTemplate(config, prefix, &config->defaultImage, 1,
2249     flags);
2250     if (!newDefault)
2251     config->defaultImage = -1;
2252     }
2253     }
2254    
2255     void setFallbackImage(struct grubConfig * config, int hasNew) {
2256     struct singleEntry * entry, * entry2;
2257     int j;
2258    
2259     if (config->fallbackImage == -1) return;
2260    
2261     entry = findEntryByIndex(config, config->fallbackImage);
2262     if (!entry || entry->skip) {
2263     config->fallbackImage = -1;
2264     return;
2265     }
2266    
2267     if (hasNew)
2268     config->fallbackImage++;
2269    
2270     /* count the number of entries erased before this one */
2271     for (j = 0; j < config->fallbackImage; j++) {
2272     entry2 = findEntryByIndex(config, j);
2273     if (entry2->skip) config->fallbackImage--;
2274     }
2275     }
2276    
2277     void displayEntry(struct singleEntry * entry, const char * prefix, int index) {
2278     struct singleLine * line;
2279     char * root = NULL;
2280     int i;
2281 niro 2708 int j;
2282 niro 532
2283     printf("index=%d\n", index);
2284    
2285 niro 2683 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
2286 niro 914 if (!line) {
2287     printf("non linux entry\n");
2288     return;
2289     }
2290    
2291 niro 1849 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2292     printf("kernel=%s\n", line->elements[1].item);
2293     else
2294     printf("kernel=%s%s\n", prefix, line->elements[1].item);
2295 niro 532
2296     if (line->numElements >= 3) {
2297     printf("args=\"");
2298     i = 2;
2299     while (i < line->numElements) {
2300     if (!strncmp(line->elements[i].item, "root=", 5)) {
2301     root = line->elements[i].item + 5;
2302     } else {
2303     printf("%s%s", line->elements[i].item,
2304     line->elements[i].indent);
2305     }
2306    
2307     i++;
2308     }
2309     printf("\"\n");
2310     } else {
2311 niro 914 line = getLineByType(LT_KERNELARGS, entry->lines);
2312 niro 532 if (line) {
2313     char * s;
2314    
2315     printf("args=\"");
2316     i = 1;
2317     while (i < line->numElements) {
2318     if (!strncmp(line->elements[i].item, "root=", 5)) {
2319     root = line->elements[i].item + 5;
2320     } else {
2321     s = line->elements[i].item;
2322    
2323     printf("%s%s", s, line->elements[i].indent);
2324     }
2325    
2326     i++;
2327     }
2328    
2329     s = line->elements[i - 1].indent;
2330     printf("\"\n");
2331     }
2332     }
2333    
2334     if (!root) {
2335 niro 914 line = getLineByType(LT_ROOT, entry->lines);
2336 niro 532 if (line && line->numElements >= 2)
2337     root=line->elements[1].item;
2338     }
2339    
2340     if (root) {
2341     char * s = alloca(strlen(root) + 1);
2342    
2343     strcpy(s, root);
2344     if (s[strlen(s) - 1] == '"')
2345     s[strlen(s) - 1] = '\0';
2346     /* make sure the root doesn't have a trailing " */
2347     printf("root=%s\n", s);
2348     }
2349    
2350 niro 2683 line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
2351 niro 532
2352     if (line && line->numElements >= 2) {
2353 niro 1849 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2354     printf("initrd=");
2355     else
2356     printf("initrd=%s", prefix);
2357    
2358 niro 532 for (i = 1; i < line->numElements; i++)
2359     printf("%s%s", line->elements[i].item, line->elements[i].indent);
2360     printf("\n");
2361     }
2362 niro 1751
2363     line = getLineByType(LT_TITLE, entry->lines);
2364     if (line) {
2365     printf("title=%s\n", line->elements[1].item);
2366     } else {
2367     char * title;
2368     line = getLineByType(LT_MENUENTRY, entry->lines);
2369     title = grub2ExtractTitle(line);
2370     if (title)
2371     printf("title=%s\n", title);
2372     }
2373 niro 2708
2374     for (j = 0, line = entry->lines; line; line = line->next) {
2375     if ((line->type & LT_MBMODULE) && line->numElements >= 2) {
2376     if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2377     printf("mbmodule%d=", j);
2378     else
2379     printf("mbmodule%d=%s", j, prefix);
2380    
2381     for (i = 1; i < line->numElements; i++)
2382     printf("%s%s", line->elements[i].item, line->elements[i].indent);
2383     printf("\n");
2384     j++;
2385     }
2386     }
2387 niro 532 }
2388    
2389 niro 1850 int isSuseSystem(void) {
2390     const char * path;
2391     const static char default_path[] = "/etc/SuSE-release";
2392    
2393     if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2394     path = default_path;
2395    
2396     if (!access(path, R_OK))
2397     return 1;
2398     return 0;
2399     }
2400    
2401     int isSuseGrubConf(const char * path) {
2402     FILE * grubConf;
2403     char * line = NULL;
2404     size_t len = 0, res = 0;
2405    
2406     grubConf = fopen(path, "r");
2407     if (!grubConf) {
2408     dbgPrintf("Could not open SuSE configuration file '%s'\n", path);
2409     return 0;
2410     }
2411    
2412     while ((res = getline(&line, &len, grubConf)) != -1) {
2413     if (!strncmp(line, "setup", 5)) {
2414     fclose(grubConf);
2415     free(line);
2416     return 1;
2417     }
2418     }
2419    
2420     dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2421     path);
2422    
2423     fclose(grubConf);
2424     free(line);
2425     return 0;
2426     }
2427    
2428     int suseGrubConfGetLba(const char * path, int * lbaPtr) {
2429     FILE * grubConf;
2430     char * line = NULL;
2431     size_t res = 0, len = 0;
2432    
2433     if (!path) return 1;
2434     if (!lbaPtr) return 1;
2435    
2436     grubConf = fopen(path, "r");
2437     if (!grubConf) return 1;
2438    
2439     while ((res = getline(&line, &len, grubConf)) != -1) {
2440     if (line[res - 1] == '\n')
2441     line[res - 1] = '\0';
2442     else if (len > res)
2443     line[res] = '\0';
2444     else {
2445     line = realloc(line, res + 1);
2446     line[res] = '\0';
2447     }
2448    
2449     if (!strncmp(line, "setup", 5)) {
2450     if (strstr(line, "--force-lba")) {
2451     *lbaPtr = 1;
2452     } else {
2453     *lbaPtr = 0;
2454     }
2455     dbgPrintf("lba: %i\n", *lbaPtr);
2456     break;
2457     }
2458     }
2459    
2460     free(line);
2461     fclose(grubConf);
2462     return 0;
2463     }
2464    
2465     int suseGrubConfGetInstallDevice(const char * path, char ** devicePtr) {
2466     FILE * grubConf;
2467     char * line = NULL;
2468     size_t res = 0, len = 0;
2469     char * lastParamPtr = NULL;
2470     char * secLastParamPtr = NULL;
2471     char installDeviceNumber = '\0';
2472     char * bounds = NULL;
2473    
2474     if (!path) return 1;
2475     if (!devicePtr) return 1;
2476    
2477     grubConf = fopen(path, "r");
2478     if (!grubConf) return 1;
2479    
2480     while ((res = getline(&line, &len, grubConf)) != -1) {
2481     if (strncmp(line, "setup", 5))
2482     continue;
2483    
2484     if (line[res - 1] == '\n')
2485     line[res - 1] = '\0';
2486     else if (len > res)
2487     line[res] = '\0';
2488     else {
2489     line = realloc(line, res + 1);
2490     line[res] = '\0';
2491     }
2492    
2493     lastParamPtr = bounds = line + res;
2494    
2495     /* Last parameter in grub may be an optional IMAGE_DEVICE */
2496     while (!isspace(*lastParamPtr))
2497     lastParamPtr--;
2498     lastParamPtr++;
2499    
2500     secLastParamPtr = lastParamPtr - 2;
2501     dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2502    
2503     if (lastParamPtr + 3 > bounds) {
2504     dbgPrintf("lastParamPtr going over boundary");
2505     fclose(grubConf);
2506     free(line);
2507     return 1;
2508     }
2509     if (!strncmp(lastParamPtr, "(hd", 3))
2510     lastParamPtr += 3;
2511     dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2512    
2513     /*
2514     * Second last parameter will decide wether last parameter is
2515     * an IMAGE_DEVICE or INSTALL_DEVICE
2516     */
2517     while (!isspace(*secLastParamPtr))
2518     secLastParamPtr--;
2519     secLastParamPtr++;
2520    
2521     if (secLastParamPtr + 3 > bounds) {
2522     dbgPrintf("secLastParamPtr going over boundary");
2523     fclose(grubConf);
2524     free(line);
2525     return 1;
2526     }
2527     dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2528     if (!strncmp(secLastParamPtr, "(hd", 3)) {
2529     secLastParamPtr += 3;
2530     dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
2531     installDeviceNumber = *secLastParamPtr;
2532     } else {
2533     installDeviceNumber = *lastParamPtr;
2534     }
2535    
2536     *devicePtr = malloc(6);
2537     snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
2538     dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
2539     fclose(grubConf);
2540     free(line);
2541     return 0;
2542     }
2543    
2544     free(line);
2545     fclose(grubConf);
2546     return 1;
2547     }
2548    
2549     int grubGetBootFromDeviceMap(const char * device,
2550     char ** bootPtr) {
2551     FILE * deviceMap;
2552     char * line = NULL;
2553     size_t res = 0, len = 0;
2554     char * devicePtr;
2555     char * bounds = NULL;
2556     const char * path;
2557     const static char default_path[] = "/boot/grub/device.map";
2558    
2559     if (!device) return 1;
2560     if (!bootPtr) return 1;
2561    
2562     if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2563     path = default_path;
2564    
2565     dbgPrintf("opening grub device.map file from: %s\n", path);
2566     deviceMap = fopen(path, "r");
2567     if (!deviceMap)
2568     return 1;
2569    
2570     while ((res = getline(&line, &len, deviceMap)) != -1) {
2571     if (!strncmp(line, "#", 1))
2572     continue;
2573    
2574     if (line[res - 1] == '\n')
2575     line[res - 1] = '\0';
2576     else if (len > res)
2577     line[res] = '\0';
2578     else {
2579     line = realloc(line, res + 1);
2580     line[res] = '\0';
2581     }
2582    
2583     devicePtr = line;
2584     bounds = line + res;
2585    
2586     while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2587     devicePtr++;
2588     dbgPrintf("device: %s\n", devicePtr);
2589    
2590     if (!strncmp(devicePtr, device, strlen(device))) {
2591     devicePtr += strlen(device);
2592     while (isspace(*devicePtr) && ((devicePtr + 1) <= bounds))
2593     devicePtr++;
2594    
2595     *bootPtr = strdup(devicePtr);
2596     break;
2597     }
2598     }
2599    
2600     free(line);
2601     fclose(deviceMap);
2602     return 0;
2603     }
2604    
2605     int suseGrubConfGetBoot(const char * path, char ** bootPtr) {
2606     char * grubDevice;
2607    
2608     if (suseGrubConfGetInstallDevice(path, &grubDevice))
2609     dbgPrintf("error looking for grub installation device\n");
2610     else
2611     dbgPrintf("grubby installation device: %s\n", grubDevice);
2612    
2613     if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
2614     dbgPrintf("error looking for grub boot device\n");
2615     else
2616     dbgPrintf("grubby boot device: %s\n", *bootPtr);
2617    
2618     free(grubDevice);
2619     return 0;
2620     }
2621    
2622     int parseSuseGrubConf(int * lbaPtr, char ** bootPtr) {
2623     /*
2624     * This SuSE grub configuration file at this location is not your average
2625     * grub configuration file, but instead the grub commands used to setup
2626     * grub on that system.
2627     */
2628     const char * path;
2629     const static char default_path[] = "/etc/grub.conf";
2630    
2631     if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
2632     path = default_path;
2633    
2634     if (!isSuseGrubConf(path)) return 1;
2635    
2636     if (lbaPtr) {
2637     *lbaPtr = 0;
2638     if (suseGrubConfGetLba(path, lbaPtr))
2639     return 1;
2640     }
2641    
2642     if (bootPtr) {
2643     *bootPtr = NULL;
2644     suseGrubConfGetBoot(path, bootPtr);
2645     }
2646    
2647     return 0;
2648     }
2649    
2650 niro 532 int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
2651     FILE * in;
2652     char buf[1024];
2653     char * chptr;
2654     char * start;
2655     char * param;
2656    
2657 niro 926 in = fopen("/etc/conf.d/grub", "r");
2658 niro 532 if (!in) return 1;
2659    
2660     if (lbaPtr) *lbaPtr = 0;
2661     if (bootPtr) *bootPtr = NULL;
2662    
2663     while (fgets(buf, sizeof(buf), in)) {
2664     start = buf;
2665     while (isspace(*start)) start++;
2666     if (*start == '#') continue;
2667    
2668     chptr = strchr(start, '=');
2669     if (!chptr) continue;
2670     chptr--;
2671     while (*chptr && isspace(*chptr)) chptr--;
2672     chptr++;
2673     *chptr = '\0';
2674    
2675     param = chptr + 1;
2676     while (*param && isspace(*param)) param++;
2677     if (*param == '=') {
2678     param++;
2679     while (*param && isspace(*param)) param++;
2680     }
2681    
2682     chptr = param;
2683     while (*chptr && !isspace(*chptr)) chptr++;
2684     *chptr = '\0';
2685    
2686     if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
2687     *lbaPtr = 1;
2688     else if (!strcmp(start, "boot") && bootPtr)
2689     *bootPtr = strdup(param);
2690     }
2691    
2692     fclose(in);
2693    
2694     return 0;
2695     }
2696    
2697     void dumpSysconfigGrub(void) {
2698 niro 1850 char * boot = NULL;
2699 niro 532 int lba;
2700    
2701 niro 1851 if (isSuseSystem()) {
2702     if (parseSuseGrubConf(&lba, &boot)) {
2703 niro 1850 free(boot);
2704     return;
2705     }
2706     } else {
2707 niro 1851 if (parseSysconfigGrub(&lba, &boot)) {
2708 niro 1850 free(boot);
2709     return;
2710     }
2711 niro 532 }
2712 niro 1850
2713     if (lba) printf("lba\n");
2714     if (boot) {
2715     printf("boot=%s\n", boot);
2716     free(boot);
2717     }
2718 niro 532 }
2719    
2720     int displayInfo(struct grubConfig * config, char * kernel,
2721     const char * prefix) {
2722     int i = 0;
2723     struct singleEntry * entry;
2724     struct singleLine * line;
2725    
2726     entry = findEntryByPath(config, kernel, prefix, &i);
2727     if (!entry) {
2728     fprintf(stderr, _("grubby: kernel not found\n"));
2729     return 1;
2730     }
2731    
2732 niro 926 /* this is a horrible hack to support /etc/conf.d/grub; there must
2733 niro 532 be a better way */
2734     if (config->cfi == &grubConfigType) {
2735     dumpSysconfigGrub();
2736     } else {
2737 niro 914 line = getLineByType(LT_BOOT, config->theLines);
2738 niro 532 if (line && line->numElements >= 1) {
2739     printf("boot=%s\n", line->elements[1].item);
2740     }
2741    
2742 niro 914 line = getLineByType(LT_LBA, config->theLines);
2743 niro 532 if (line) printf("lba\n");
2744     }
2745    
2746     displayEntry(entry, prefix, i);
2747    
2748     i++;
2749     while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
2750     displayEntry(entry, prefix, i);
2751     i++;
2752     }
2753    
2754     return 0;
2755     }
2756    
2757 niro 914 struct singleLine * addLineTmpl(struct singleEntry * entry,
2758     struct singleLine * tmplLine,
2759     struct singleLine * prevLine,
2760     const char * val,
2761     struct configFileInfo * cfi)
2762     {
2763     struct singleLine * newLine = lineDup(tmplLine);
2764    
2765 niro 1940 if (isEfi && cfi == &grub2ConfigType) {
2766     enum lineType_e old = newLine->type;
2767     newLine->type = preferredLineType(newLine->type, cfi);
2768     if (old != newLine->type)
2769     newLine->elements[0].item = getKeyByType(newLine->type, cfi);
2770     }
2771    
2772 niro 914 if (val) {
2773     /* override the inherited value with our own.
2774     * This is a little weak because it only applies to elements[1]
2775     */
2776     if (newLine->numElements > 1)
2777     removeElement(newLine, 1);
2778     insertElement(newLine, val, 1, cfi);
2779    
2780     /* but try to keep the rootspec from the template... sigh */
2781 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)) {
2782 niro 914 char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
2783     if (rootspec != NULL) {
2784     free(newLine->elements[1].item);
2785     newLine->elements[1].item =
2786     sdupprintf("%s%s", rootspec, val);
2787     }
2788     }
2789     }
2790    
2791     dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
2792     newLine->elements[0].item : "");
2793    
2794     if (!entry->lines) {
2795     /* first one on the list */
2796     entry->lines = newLine;
2797     } else if (prevLine) {
2798     /* add after prevLine */
2799     newLine->next = prevLine->next;
2800     prevLine->next = newLine;
2801     }
2802    
2803     return newLine;
2804     }
2805    
2806 niro 532 /* val may be NULL */
2807     struct singleLine * addLine(struct singleEntry * entry,
2808     struct configFileInfo * cfi,
2809 niro 914 enum lineType_e type, char * defaultIndent,
2810     const char * val) {
2811 niro 532 struct singleLine * line, * prev;
2812 niro 914 struct keywordTypes * kw;
2813     struct singleLine tmpl;
2814 niro 532
2815 niro 914 /* NB: This function shouldn't allocate items on the heap, rather on the
2816     * stack since it calls addLineTmpl which will make copies.
2817     */
2818     if (type == LT_TITLE && cfi->titleBracketed) {
2819     /* we're doing a bracketed title (zipl) */
2820     tmpl.type = type;
2821     tmpl.numElements = 1;
2822     tmpl.elements = alloca(sizeof(*tmpl.elements));
2823     tmpl.elements[0].item = alloca(strlen(val)+3);
2824     sprintf(tmpl.elements[0].item, "[%s]", val);
2825     tmpl.elements[0].indent = "";
2826     val = NULL;
2827 niro 1696 } else if (type == LT_MENUENTRY) {
2828     char *lineend = "--class gnu-linux --class gnu --class os {";
2829     if (!val) {
2830     fprintf(stderr, "Line type LT_MENUENTRY requires a value\n");
2831     abort();
2832     }
2833     kw = getKeywordByType(type, cfi);
2834     if (!kw) {
2835     fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2836     abort();
2837     }
2838     tmpl.indent = "";
2839     tmpl.type = type;
2840     tmpl.numElements = 3;
2841     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2842     tmpl.elements[0].item = kw->key;
2843     tmpl.elements[0].indent = alloca(2);
2844     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2845     tmpl.elements[1].item = (char *)val;
2846     tmpl.elements[1].indent = alloca(2);
2847     sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
2848     tmpl.elements[2].item = alloca(strlen(lineend)+1);
2849     strcpy(tmpl.elements[2].item, lineend);
2850     tmpl.elements[2].indent = "";
2851 niro 914 } else {
2852     kw = getKeywordByType(type, cfi);
2853 niro 1696 if (!kw) {
2854     fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2855     abort();
2856     }
2857 niro 914 tmpl.type = type;
2858     tmpl.numElements = val ? 2 : 1;
2859     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2860     tmpl.elements[0].item = kw->key;
2861     tmpl.elements[0].indent = alloca(2);
2862     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2863     if (val) {
2864     tmpl.elements[1].item = (char *)val;
2865     tmpl.elements[1].indent = "";
2866     }
2867     }
2868    
2869 niro 532 /* The last non-empty line gives us the indention to us and the line
2870     to insert after. Note that comments are considered empty lines, which
2871     may not be ideal? If there are no lines or we are looking at the
2872     first line, we use defaultIndent (the first line is normally indented
2873     differently from the rest) */
2874 niro 914 for (line = entry->lines, prev = NULL; line; line = line->next) {
2875     if (line->numElements) prev = line;
2876     /* fall back on the last line if prev isn't otherwise set */
2877     if (!line->next && !prev) prev = line;
2878 niro 532 }
2879    
2880 niro 1696 struct singleLine *menuEntry;
2881     menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
2882     if (tmpl.type == LT_ENTRY_END) {
2883     if (menuEntry)
2884     tmpl.indent = menuEntry->indent;
2885     else
2886     tmpl.indent = defaultIndent ?: "";
2887     } else if (tmpl.type != LT_MENUENTRY) {
2888     if (menuEntry)
2889     tmpl.indent = "\t";
2890     else if (prev == entry->lines)
2891     tmpl.indent = defaultIndent ?: "";
2892     else
2893     tmpl.indent = prev->indent;
2894     }
2895 niro 532
2896 niro 914 return addLineTmpl(entry, &tmpl, prev, val, cfi);
2897 niro 532 }
2898    
2899     void removeLine(struct singleEntry * entry, struct singleLine * line) {
2900     struct singleLine * prev;
2901     int i;
2902    
2903     for (i = 0; i < line->numElements; i++) {
2904     free(line->elements[i].item);
2905     free(line->elements[i].indent);
2906     }
2907     free(line->elements);
2908     free(line->indent);
2909    
2910     if (line == entry->lines) {
2911     entry->lines = line->next;
2912     } else {
2913     prev = entry->lines;
2914     while (prev->next != line) prev = prev->next;
2915     prev->next = line->next;
2916     }
2917    
2918     free(line);
2919     }
2920    
2921 niro 1696 static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi)
2922     {
2923     struct singleLine newLine = {
2924     .indent = tmplLine->indent,
2925     .type = tmplLine->type,
2926     .next = tmplLine->next,
2927     };
2928     int firstQuotedItem = -1;
2929     int quoteLen = 0;
2930     int j;
2931     int element = 0;
2932     char *c;
2933    
2934     c = malloc(strlen(tmplLine->elements[0].item) + 1);
2935     strcpy(c, tmplLine->elements[0].item);
2936     insertElement(&newLine, c, element++, cfi);
2937     free(c);
2938     c = NULL;
2939    
2940     for (j = 1; j < tmplLine->numElements; j++) {
2941     if (firstQuotedItem == -1) {
2942     quoteLen += strlen(tmplLine->elements[j].item);
2943    
2944     if (isquote(tmplLine->elements[j].item[0])) {
2945     firstQuotedItem = j;
2946     quoteLen += strlen(tmplLine->elements[j].indent);
2947     } else {
2948     c = malloc(quoteLen + 1);
2949     strcpy(c, tmplLine->elements[j].item);
2950     insertElement(&newLine, c, element++, cfi);
2951     free(c);
2952     quoteLen = 0;
2953     }
2954     } else {
2955     int itemlen = strlen(tmplLine->elements[j].item);
2956     quoteLen += itemlen;
2957     quoteLen += strlen(tmplLine->elements[j].indent);
2958    
2959     if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
2960     c = malloc(quoteLen + 1);
2961     c[0] = '\0';
2962     for (int i = firstQuotedItem; i < j+1; i++) {
2963     strcat(c, tmplLine->elements[i].item);
2964     strcat(c, tmplLine->elements[i].indent);
2965     }
2966     insertElement(&newLine, c, element++, cfi);
2967     free(c);
2968    
2969     firstQuotedItem = -1;
2970     quoteLen = 0;
2971     }
2972     }
2973     }
2974     while (tmplLine->numElements)
2975     removeElement(tmplLine, 0);
2976     if (tmplLine->elements)
2977     free(tmplLine->elements);
2978    
2979     tmplLine->numElements = newLine.numElements;
2980     tmplLine->elements = newLine.elements;
2981     }
2982    
2983 niro 914 static void insertElement(struct singleLine * line,
2984     const char * item, int insertHere,
2985     struct configFileInfo * cfi)
2986     {
2987     struct keywordTypes * kw;
2988     char indent[2] = "";
2989    
2990     /* sanity check */
2991     if (insertHere > line->numElements) {
2992     dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",
2993     insertHere, line->numElements);
2994     insertHere = line->numElements;
2995     }
2996    
2997     line->elements = realloc(line->elements, (line->numElements + 1) *
2998     sizeof(*line->elements));
2999     memmove(&line->elements[insertHere+1],
3000     &line->elements[insertHere],
3001     (line->numElements - insertHere) *
3002     sizeof(*line->elements));
3003     line->elements[insertHere].item = strdup(item);
3004    
3005     kw = getKeywordByType(line->type, cfi);
3006    
3007     if (line->numElements == 0) {
3008     indent[0] = '\0';
3009     } else if (insertHere == 0) {
3010     indent[0] = kw->nextChar;
3011     } else if (kw->separatorChar != '\0') {
3012     indent[0] = kw->separatorChar;
3013     } else {
3014     indent[0] = ' ';
3015     }
3016    
3017     if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {
3018     /* move the end-of-line forward */
3019     line->elements[insertHere].indent =
3020     line->elements[insertHere-1].indent;
3021     line->elements[insertHere-1].indent = strdup(indent);
3022     } else {
3023     line->elements[insertHere].indent = strdup(indent);
3024     }
3025    
3026     line->numElements++;
3027    
3028     dbgPrintf("insertElement(%s, '%s%s', %d)\n",
3029     line->elements[0].item,
3030     line->elements[insertHere].item,
3031     line->elements[insertHere].indent,
3032     insertHere);
3033     }
3034    
3035     static void removeElement(struct singleLine * line, int removeHere) {
3036     int i;
3037    
3038     /* sanity check */
3039     if (removeHere >= line->numElements) return;
3040    
3041     dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
3042     removeHere, line->elements[removeHere].item);
3043    
3044     free(line->elements[removeHere].item);
3045    
3046     if (removeHere > 1) {
3047     /* previous argument gets this argument's post-indentation */
3048     free(line->elements[removeHere-1].indent);
3049     line->elements[removeHere-1].indent =
3050     line->elements[removeHere].indent;
3051     } else {
3052     free(line->elements[removeHere].indent);
3053     }
3054    
3055     /* now collapse the array, but don't bother to realloc smaller */
3056     for (i = removeHere; i < line->numElements - 1; i++)
3057     line->elements[i] = line->elements[i + 1];
3058    
3059     line->numElements--;
3060     }
3061    
3062 niro 532 int argMatch(const char * one, const char * two) {
3063     char * first, * second;
3064     char * chptr;
3065    
3066     first = strcpy(alloca(strlen(one) + 1), one);
3067     second = strcpy(alloca(strlen(two) + 1), two);
3068    
3069     chptr = strchr(first, '=');
3070     if (chptr) *chptr = '\0';
3071    
3072     chptr = strchr(second, '=');
3073     if (chptr) *chptr = '\0';
3074    
3075     return strcmp(first, second);
3076     }
3077    
3078     int updateActualImage(struct grubConfig * cfg, const char * image,
3079     const char * prefix, const char * addArgs,
3080     const char * removeArgs, int multibootArgs) {
3081     struct singleEntry * entry;
3082     struct singleLine * line, * rootLine;
3083     int index = 0;
3084 niro 914 int i, k;
3085 niro 532 const char ** newArgs, ** oldArgs;
3086     const char ** arg;
3087 niro 914 int useKernelArgs, useRoot;
3088 niro 532 int firstElement;
3089 niro 1304 int *usedElements;
3090 niro 914 int doreplace;
3091 niro 532
3092     if (!image) return 0;
3093    
3094     if (!addArgs) {
3095     newArgs = malloc(sizeof(*newArgs));
3096     *newArgs = NULL;
3097     } else {
3098     if (poptParseArgvString(addArgs, NULL, &newArgs)) {
3099     fprintf(stderr,
3100     _("grubby: error separating arguments '%s'\n"), addArgs);
3101     return 1;
3102     }
3103     }
3104    
3105     if (!removeArgs) {
3106     oldArgs = malloc(sizeof(*oldArgs));
3107     *oldArgs = NULL;
3108     } else {
3109     if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
3110     fprintf(stderr,
3111     _("grubby: error separating arguments '%s'\n"), removeArgs);
3112     free(newArgs);
3113     return 1;
3114     }
3115     }
3116    
3117    
3118 niro 914 useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
3119     && (!multibootArgs || cfg->cfi->mbConcatArgs));
3120 niro 532
3121 niro 914 useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
3122     && !multibootArgs);
3123 niro 532
3124 niro 914 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3125 niro 532
3126 niro 914 if (multibootArgs && !entry->multiboot)
3127     continue;
3128 niro 532
3129 niro 914 /* Determine where to put the args. If this config supports
3130     * LT_KERNELARGS, use that. Otherwise use
3131     * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
3132     */
3133     if (useKernelArgs) {
3134     line = getLineByType(LT_KERNELARGS, entry->lines);
3135     if (!line) {
3136     /* no LT_KERNELARGS, need to add it */
3137     line = addLine(entry, cfg->cfi, LT_KERNELARGS,
3138     cfg->secondaryIndent, NULL);
3139     }
3140     firstElement = 1;
3141 niro 532
3142 niro 914 } else if (multibootArgs) {
3143     line = getLineByType(LT_HYPER, entry->lines);
3144     if (!line) {
3145     /* a multiboot entry without LT_HYPER? */
3146     continue;
3147     }
3148     firstElement = 2;
3149    
3150     } else {
3151 niro 2683 line = getLineByType(LT_KERNEL|LT_MBMODULE|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3152 niro 914 if (!line) {
3153     /* no LT_KERNEL or LT_MBMODULE in this entry? */
3154     continue;
3155     }
3156     firstElement = 2;
3157 niro 532 }
3158    
3159 niro 914 /* handle the elilo case which does:
3160     * append="hypervisor args -- kernel args"
3161     */
3162     if (entry->multiboot && cfg->cfi->mbConcatArgs) {
3163     /* this is a multiboot entry, make sure there's
3164     * -- on the args line
3165     */
3166     for (i = firstElement; i < line->numElements; i++) {
3167     if (!strcmp(line->elements[i].item, "--"))
3168     break;
3169     }
3170     if (i == line->numElements) {
3171     /* assume all existing args are kernel args,
3172     * prepend -- to make it official
3173     */
3174     insertElement(line, "--", firstElement, cfg->cfi);
3175     i = firstElement;
3176     }
3177     if (!multibootArgs) {
3178     /* kernel args start after the -- */
3179     firstElement = i + 1;
3180     }
3181     } else if (cfg->cfi->mbConcatArgs) {
3182     /* this is a non-multiboot entry, remove hyper args */
3183     for (i = firstElement; i < line->numElements; i++) {
3184     if (!strcmp(line->elements[i].item, "--"))
3185     break;
3186     }
3187     if (i < line->numElements) {
3188     /* remove args up to -- */
3189     while (strcmp(line->elements[firstElement].item, "--"))
3190     removeElement(line, firstElement);
3191     /* remove -- */
3192     removeElement(line, firstElement);
3193     }
3194 niro 532 }
3195    
3196 niro 914 usedElements = calloc(line->numElements, sizeof(*usedElements));
3197 niro 532
3198 niro 914 for (k = 0, arg = newArgs; *arg; arg++, k++) {
3199    
3200     doreplace = 1;
3201 niro 532 for (i = firstElement; i < line->numElements; i++) {
3202 niro 914 if (multibootArgs && cfg->cfi->mbConcatArgs &&
3203     !strcmp(line->elements[i].item, "--"))
3204     {
3205     /* reached the end of hyper args, insert here */
3206     doreplace = 0;
3207     break;
3208     }
3209 niro 532 if (usedElements[i])
3210     continue;
3211     if (!argMatch(line->elements[i].item, *arg)) {
3212     usedElements[i]=1;
3213     break;
3214     }
3215     }
3216    
3217 niro 914 if (i < line->numElements && doreplace) {
3218     /* direct replacement */
3219 niro 532 free(line->elements[i].item);
3220     line->elements[i].item = strdup(*arg);
3221    
3222 niro 914 } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
3223     /* root= replacement */
3224     rootLine = getLineByType(LT_ROOT, entry->lines);
3225     if (rootLine) {
3226     free(rootLine->elements[1].item);
3227     rootLine->elements[1].item = strdup(*arg + 5);
3228 niro 532 } else {
3229 niro 914 rootLine = addLine(entry, cfg->cfi, LT_ROOT,
3230     cfg->secondaryIndent, *arg + 5);
3231 niro 532 }
3232 niro 914 }
3233 niro 532
3234 niro 914 else {
3235     /* insert/append */
3236     insertElement(line, *arg, i, cfg->cfi);
3237     usedElements = realloc(usedElements, line->numElements *
3238     sizeof(*usedElements));
3239     memmove(&usedElements[i + 1], &usedElements[i],
3240     line->numElements - i - 1);
3241     usedElements[i] = 1;
3242 niro 532
3243     /* if we updated a root= here even though there is a
3244     LT_ROOT available we need to remove the LT_ROOT entry
3245     (this will happen if we switch from a device to a label) */
3246     if (useRoot && !strncmp(*arg, "root=", 5)) {
3247 niro 914 rootLine = getLineByType(LT_ROOT, entry->lines);
3248     if (rootLine)
3249 niro 532 removeLine(entry, rootLine);
3250     }
3251     }
3252     }
3253    
3254     free(usedElements);
3255    
3256     for (arg = oldArgs; *arg; arg++) {
3257 niro 914 for (i = firstElement; i < line->numElements; i++) {
3258     if (multibootArgs && cfg->cfi->mbConcatArgs &&
3259     !strcmp(line->elements[i].item, "--"))
3260     /* reached the end of hyper args, stop here */
3261 niro 532 break;
3262 niro 914 if (!argMatch(line->elements[i].item, *arg)) {
3263     removeElement(line, i);
3264     break;
3265 niro 532 }
3266     }
3267 niro 914 /* handle removing LT_ROOT line too */
3268     if (useRoot && !strncmp(*arg, "root=", 5)) {
3269     rootLine = getLineByType(LT_ROOT, entry->lines);
3270     if (rootLine)
3271     removeLine(entry, rootLine);
3272     }
3273 niro 532 }
3274    
3275     if (line->numElements == 1) {
3276     /* don't need the line at all (note it has to be a
3277     LT_KERNELARGS for this to happen */
3278     removeLine(entry, line);
3279     }
3280     }
3281    
3282     free(newArgs);
3283     free(oldArgs);
3284    
3285     return 0;
3286     }
3287    
3288     int updateImage(struct grubConfig * cfg, const char * image,
3289     const char * prefix, const char * addArgs,
3290     const char * removeArgs,
3291     const char * addMBArgs, const char * removeMBArgs) {
3292     int rc = 0;
3293    
3294     if (!image) return rc;
3295    
3296     /* update the main args first... */
3297     if (addArgs || removeArgs)
3298     rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs, 0);
3299     if (rc) return rc;
3300    
3301     /* and now any multiboot args */
3302     if (addMBArgs || removeMBArgs)
3303     rc = updateActualImage(cfg, image, prefix, addMBArgs, removeMBArgs, 1);
3304     return rc;
3305     }
3306    
3307 niro 2263 int addMBInitrd(struct grubConfig * cfg, const char *newMBKernel,
3308     const char * image, const char * prefix, const char * initrd) {
3309     struct singleEntry * entry;
3310     struct singleLine * line, * kernelLine, *endLine = NULL;
3311     int index = 0;
3312    
3313     if (!image) return 0;
3314    
3315 niro 2957 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3316 niro 2263 kernelLine = getLineByType(LT_MBMODULE, entry->lines);
3317     if (!kernelLine) continue;
3318    
3319     if (prefix) {
3320     int prefixLen = strlen(prefix);
3321     if (!strncmp(initrd, prefix, prefixLen))
3322     initrd += prefixLen;
3323     }
3324     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3325     if (endLine)
3326     removeLine(entry, endLine);
3327     line = addLine(entry, cfg->cfi, preferredLineType(LT_MBMODULE,cfg->cfi),
3328     kernelLine->indent, initrd);
3329     if (!line)
3330     return 1;
3331     if (endLine) {
3332     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3333     if (!line)
3334     return 1;
3335     }
3336    
3337     break;
3338     }
3339    
3340     return 0;
3341     }
3342    
3343 niro 1156 int updateInitrd(struct grubConfig * cfg, const char * image,
3344     const char * prefix, const char * initrd) {
3345     struct singleEntry * entry;
3346 niro 1696 struct singleLine * line, * kernelLine, *endLine = NULL;
3347 niro 1156 int index = 0;
3348    
3349     if (!image) return 0;
3350    
3351     for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3352 niro 2683 kernelLine = getLineByType(LT_KERNEL|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3353 niro 1156 if (!kernelLine) continue;
3354    
3355 niro 2683 line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
3356 niro 1156 if (line)
3357     removeLine(entry, line);
3358     if (prefix) {
3359     int prefixLen = strlen(prefix);
3360     if (!strncmp(initrd, prefix, prefixLen))
3361     initrd += prefixLen;
3362     }
3363 niro 1696 endLine = getLineByType(LT_ENTRY_END, entry->lines);
3364     if (endLine)
3365     removeLine(entry, endLine);
3366 niro 2683 enum lineType_e lt;
3367     switch(kernelLine->type) {
3368     case LT_KERNEL:
3369     lt = LT_INITRD;
3370     break;
3371     case LT_KERNEL_EFI:
3372     lt = LT_INITRD_EFI;
3373     break;
3374     case LT_KERNEL_16:
3375     lt = LT_INITRD_16;
3376     break;
3377     default:
3378     lt = preferredLineType(LT_INITRD, cfg->cfi);
3379     }
3380     line = addLine(entry, cfg->cfi, lt, kernelLine->indent, initrd);
3381 niro 1696 if (!line)
3382     return 1;
3383     if (endLine) {
3384     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3385     if (!line)
3386     return 1;
3387     }
3388    
3389 niro 1156 break;
3390     }
3391    
3392     return 0;
3393     }
3394    
3395 niro 532 int checkDeviceBootloader(const char * device, const unsigned char * boot) {
3396     int fd;
3397     unsigned char bootSect[512];
3398     int offset;
3399    
3400     fd = open(device, O_RDONLY);
3401     if (fd < 0) {
3402     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3403     device, strerror(errno));
3404     return 1;
3405     }
3406    
3407     if (read(fd, bootSect, 512) != 512) {
3408     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3409     device, strerror(errno));
3410     return 1;
3411     }
3412     close(fd);
3413    
3414     /* first three bytes should match, a jmp short should be in there */
3415     if (memcmp(boot, bootSect, 3))
3416     return 0;
3417    
3418 niro 1718 if (boot[1] == JMP_SHORT_OPCODE) {
3419 niro 532 offset = boot[2] + 2;
3420     } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3421     offset = (boot[3] << 8) + boot[2] + 2;
3422 niro 1718 } else if (boot[0] == JMP_SHORT_OPCODE) {
3423     offset = boot[1] + 2;
3424     /*
3425     * it looks like grub, when copying stage1 into the mbr, patches stage1
3426     * right after the JMP location, replacing other instructions such as
3427     * JMPs for NOOPs. So, relax the check a little bit by skipping those
3428     * different bytes.
3429     */
3430     if ((bootSect[offset + 1] == NOOP_OPCODE)
3431     && (bootSect[offset + 2] == NOOP_OPCODE)) {
3432     offset = offset + 3;
3433     }
3434 niro 532 } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3435     offset = (boot[2] << 8) + boot[1] + 2;
3436     } else {
3437     return 0;
3438     }
3439    
3440     if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
3441     return 0;
3442    
3443     return 2;
3444     }
3445    
3446     int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {
3447     int fd;
3448     char buf[65536];
3449     char * end;
3450     char * chptr;
3451     char * chptr2;
3452     int rc;
3453    
3454     /* it's on raid; we need to parse /proc/mdstat and check all of the
3455     *raw* devices listed in there */
3456    
3457     if (!strncmp(mdDev, "/dev/", 5))
3458     mdDev += 5;
3459    
3460     if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
3461     fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
3462     strerror(errno));
3463     return 2;
3464     }
3465    
3466     rc = read(fd, buf, sizeof(buf) - 1);
3467     if (rc < 0 || rc == (sizeof(buf) - 1)) {
3468     fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
3469     strerror(errno));
3470     close(fd);
3471     return 2;
3472     }
3473     close(fd);
3474     buf[rc] = '\0';
3475    
3476     chptr = buf;
3477     while (*chptr) {
3478     end = strchr(chptr, '\n');
3479     if (!end) break;
3480     *end = '\0';
3481    
3482     if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
3483     chptr[strlen(mdDev)] == ' ') {
3484    
3485     /* found the device */
3486     while (*chptr && *chptr != ':') chptr++;
3487     chptr++;
3488     while (*chptr && isspace(*chptr)) chptr++;
3489    
3490     /* skip the "active" bit */
3491     while (*chptr && !isspace(*chptr)) chptr++;
3492     while (*chptr && isspace(*chptr)) chptr++;
3493    
3494     /* skip the raid level */
3495     while (*chptr && !isspace(*chptr)) chptr++;
3496     while (*chptr && isspace(*chptr)) chptr++;
3497    
3498     /* everything else is partition stuff */
3499     while (*chptr) {
3500     chptr2 = chptr;
3501     while (*chptr2 && *chptr2 != '[') chptr2++;
3502     if (!*chptr2) break;
3503    
3504     /* yank off the numbers at the end */
3505     chptr2--;
3506     while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;
3507     chptr2++;
3508     *chptr2 = '\0';
3509    
3510     /* Better, now we need the /dev/ back. We're done with
3511     * everything before this point, so we can just put
3512     * the /dev/ part there. There will always be room. */
3513     memcpy(chptr - 5, "/dev/", 5);
3514     rc = checkDeviceBootloader(chptr - 5, boot);
3515     if (rc != 2) {
3516     return rc;
3517     }
3518    
3519     chptr = chptr2 + 1;
3520     /* skip the [11] bit */
3521     while (*chptr && !isspace(*chptr)) chptr++;
3522     /* and move to the next one */
3523     while (*chptr && isspace(*chptr)) chptr++;
3524     }
3525    
3526     /* we're good to go */
3527     return 2;
3528     }
3529    
3530     chptr = end + 1;
3531     }
3532    
3533     fprintf(stderr,
3534     _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
3535     mdDev);
3536     return 0;
3537     }
3538    
3539     int checkForLilo(struct grubConfig * config) {
3540     int fd;
3541     unsigned char boot[512];
3542     struct singleLine * line;
3543    
3544     for (line = config->theLines; line; line = line->next)
3545     if (line->type == LT_BOOT) break;
3546    
3547     if (!line) {
3548     fprintf(stderr,
3549     _("grubby: no boot line found in lilo configuration\n"));
3550     return 1;
3551     }
3552    
3553     if (line->numElements != 2) return 1;
3554    
3555     fd = open("/boot/boot.b", O_RDONLY);
3556     if (fd < 0) {
3557     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3558     "/boot/boot.b", strerror(errno));
3559     return 1;
3560     }
3561    
3562     if (read(fd, boot, 512) != 512) {
3563     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3564     "/boot/boot.b", strerror(errno));
3565     return 1;
3566     }
3567     close(fd);
3568    
3569     if (!strncmp("/dev/md", line->elements[1].item, 7))
3570     return checkLiloOnRaid(line->elements[1].item, boot);
3571    
3572     return checkDeviceBootloader(line->elements[1].item, boot);
3573     }
3574    
3575 niro 1696 int checkForGrub2(struct grubConfig * config) {
3576 niro 1714 if (!access("/etc/grub.d/", R_OK))
3577 niro 1696 return 2;
3578    
3579     return 1;
3580     }
3581    
3582 niro 532 int checkForGrub(struct grubConfig * config) {
3583     int fd;
3584     unsigned char bootSect[512];
3585     char * boot;
3586 niro 1851 int onSuse = isSuseSystem();
3587 niro 532
3588 niro 1851
3589     if (onSuse) {
3590     if (parseSuseGrubConf(NULL, &boot))
3591     return 0;
3592 niro 1850 } else {
3593 niro 1851 if (parseSysconfigGrub(NULL, &boot))
3594     return 0;
3595 niro 1850 }
3596 niro 532
3597     /* assume grub is not installed -- not an error condition */
3598     if (!boot)
3599     return 0;
3600    
3601     fd = open("/boot/grub/stage1", O_RDONLY);
3602     if (fd < 0)
3603     /* this doesn't exist if grub hasn't been installed */
3604     return 0;
3605    
3606     if (read(fd, bootSect, 512) != 512) {
3607     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3608     "/boot/grub/stage1", strerror(errno));
3609 niro 914 close(fd);
3610 niro 532 return 1;
3611     }
3612     close(fd);
3613    
3614 niro 1851 /* The more elaborate checks do not work on SuSE. The checks done
3615     * seem to be reasonble (at least for now), so just return success
3616     */
3617     if (onSuse)
3618 niro 1850 return 2;
3619 niro 1851
3620     return checkDeviceBootloader(boot, bootSect);
3621 niro 532 }
3622    
3623 niro 914 int checkForExtLinux(struct grubConfig * config) {
3624     int fd;
3625     unsigned char bootSect[512];
3626     char * boot;
3627     char executable[] = "/boot/extlinux/extlinux";
3628    
3629     printf("entered: checkForExtLinux()\n");
3630    
3631     if (parseSysconfigGrub(NULL, &boot))
3632     return 0;
3633    
3634     /* assume grub is not installed -- not an error condition */
3635     if (!boot)
3636     return 0;
3637    
3638     fd = open(executable, O_RDONLY);
3639     if (fd < 0)
3640     /* this doesn't exist if grub hasn't been installed */
3641     return 0;
3642    
3643     if (read(fd, bootSect, 512) != 512) {
3644     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3645     executable, strerror(errno));
3646     return 1;
3647     }
3648     close(fd);
3649    
3650     return checkDeviceBootloader(boot, bootSect);
3651     }
3652    
3653 niro 1853 int checkForYaboot(struct grubConfig * config) {
3654     /*
3655     * This is a simplistic check that we consider good enough for own puporses
3656     *
3657     * If we were to properly check if yaboot is *installed* we'd need to:
3658     * 1) get the system boot device (LT_BOOT)
3659     * 2) considering it's a raw filesystem, check if the yaboot binary matches
3660     * the content on the boot device
3661     * 3) if not, copy the binary to a temporary file and run "addnote" on it
3662     * 4) check again if binary and boot device contents match
3663     */
3664     if (!access("/etc/yaboot.conf", R_OK))
3665     return 2;
3666    
3667     return 1;
3668     }
3669    
3670 niro 1854 int checkForElilo(struct grubConfig * config) {
3671     if (!access("/etc/elilo.conf", R_OK))
3672     return 2;
3673    
3674     return 1;
3675     }
3676    
3677 niro 532 static char * getRootSpecifier(char * str) {
3678     char * idx, * rootspec = NULL;
3679    
3680     if (*str == '(') {
3681     idx = rootspec = strdup(str);
3682     while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;
3683     *(++idx) = '\0';
3684     }
3685     return rootspec;
3686     }
3687    
3688 niro 914 static char * getInitrdVal(struct grubConfig * config,
3689     const char * prefix, struct singleLine *tmplLine,
3690     const char * newKernelInitrd,
3691 niro 1844 const char ** extraInitrds, int extraInitrdCount)
3692 niro 914 {
3693     char *initrdVal, *end;
3694     int i;
3695     size_t totalSize;
3696     size_t prefixLen;
3697     char separatorChar;
3698    
3699     prefixLen = strlen(prefix);
3700     totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
3701    
3702     for (i = 0; i < extraInitrdCount; i++) {
3703     totalSize += sizeof(separatorChar);
3704     totalSize += strlen(extraInitrds[i]) - prefixLen;
3705     }
3706    
3707     initrdVal = end = malloc(totalSize);
3708    
3709     end = stpcpy (end, newKernelInitrd + prefixLen);
3710    
3711     separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
3712     for (i = 0; i < extraInitrdCount; i++) {
3713     const char *extraInitrd;
3714     int j;
3715    
3716     extraInitrd = extraInitrds[i] + prefixLen;
3717     /* Don't add entries that are already there */
3718     if (tmplLine != NULL) {
3719     for (j = 2; j < tmplLine->numElements; j++)
3720     if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
3721     break;
3722    
3723     if (j != tmplLine->numElements)
3724     continue;
3725     }
3726    
3727     *end++ = separatorChar;
3728     end = stpcpy(end, extraInitrd);
3729     }
3730    
3731     return initrdVal;
3732     }
3733    
3734 niro 532 int addNewKernel(struct grubConfig * config, struct singleEntry * template,
3735     const char * prefix,
3736 niro 1844 const char * newKernelPath, const char * newKernelTitle,
3737     const char * newKernelArgs, const char * newKernelInitrd,
3738     const char ** extraInitrds, int extraInitrdCount,
3739 niro 2685 const char * newMBKernel, const char * newMBKernelArgs,
3740     const char * newDevTreePath) {
3741 niro 532 struct singleEntry * new;
3742 niro 914 struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
3743 niro 532 int needs;
3744     char * chptr;
3745    
3746     if (!newKernelPath) return 0;
3747    
3748     /* if the newKernelTitle is too long silently munge it into something
3749     * we can live with. truncating is first check, then we'll just mess with
3750     * it until it looks better */
3751     if (config->cfi->maxTitleLength &&
3752     (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
3753     char * buf = alloca(config->cfi->maxTitleLength + 7);
3754     char * numBuf = alloca(config->cfi->maxTitleLength + 1);
3755     int i = 1;
3756    
3757     sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);
3758     while (findEntryByPath(config, buf, NULL, NULL)) {
3759     sprintf(numBuf, "%d", i++);
3760     strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
3761     }
3762    
3763     newKernelTitle = buf + 6;
3764     }
3765    
3766     new = malloc(sizeof(*new));
3767     new->skip = 0;
3768     new->multiboot = 0;
3769     new->next = config->entries;
3770     new->lines = NULL;
3771     config->entries = new;
3772    
3773     /* copy/update from the template */
3774 niro 914 needs = NEED_KERNEL | NEED_TITLE;
3775     if (newKernelInitrd)
3776     needs |= NEED_INITRD;
3777 niro 532 if (newMBKernel) {
3778 niro 914 needs |= NEED_MB;
3779 niro 532 new->multiboot = 1;
3780     }
3781 niro 2685 if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))
3782     needs |= NEED_DEVTREE;
3783 niro 532
3784     if (template) {
3785 niro 914 for (masterLine = template->lines;
3786     masterLine && (tmplLine = lineDup(masterLine));
3787     lineFree(tmplLine), masterLine = masterLine->next)
3788     {
3789     dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
3790 niro 532
3791     /* skip comments */
3792     chptr = tmplLine->indent;
3793     while (*chptr && isspace(*chptr)) chptr++;
3794     if (*chptr == '#') continue;
3795    
3796 niro 1940 if (iskernel(tmplLine->type) && tmplLine->numElements >= 2) {
3797 niro 914 if (!template->multiboot && (needs & NEED_MB)) {
3798     /* it's not a multiboot template and this is the kernel
3799     * line. Try to be intelligent about inserting the
3800     * hypervisor at the same time.
3801     */
3802     if (config->cfi->mbHyperFirst) {
3803     /* insert the hypervisor first */
3804     newLine = addLine(new, config->cfi, LT_HYPER,
3805     tmplLine->indent,
3806     newMBKernel + strlen(prefix));
3807     /* set up for adding the kernel line */
3808     free(tmplLine->indent);
3809     tmplLine->indent = strdup(config->secondaryIndent);
3810     needs &= ~NEED_MB;
3811     }
3812     if (needs & NEED_KERNEL) {
3813     /* use addLineTmpl to preserve line elements,
3814     * otherwise we could just call addLine. Unfortunately
3815     * this means making some changes to the template
3816     * such as the indent change above and the type
3817     * change below.
3818     */
3819     struct keywordTypes * mbm_kw =
3820     getKeywordByType(LT_MBMODULE, config->cfi);
3821     if (mbm_kw) {
3822     tmplLine->type = LT_MBMODULE;
3823     free(tmplLine->elements[0].item);
3824     tmplLine->elements[0].item = strdup(mbm_kw->key);
3825     }
3826     newLine = addLineTmpl(new, tmplLine, newLine,
3827     newKernelPath + strlen(prefix), config->cfi);
3828     needs &= ~NEED_KERNEL;
3829     }
3830     if (needs & NEED_MB) { /* !mbHyperFirst */
3831     newLine = addLine(new, config->cfi, LT_HYPER,
3832     config->secondaryIndent,
3833     newMBKernel + strlen(prefix));
3834     needs &= ~NEED_MB;
3835     }
3836     } else if (needs & NEED_KERNEL) {
3837     newLine = addLineTmpl(new, tmplLine, newLine,
3838     newKernelPath + strlen(prefix), config->cfi);
3839     needs &= ~NEED_KERNEL;
3840     }
3841 niro 532
3842 niro 914 } else if (tmplLine->type == LT_HYPER &&
3843     tmplLine->numElements >= 2) {
3844     if (needs & NEED_MB) {
3845     newLine = addLineTmpl(new, tmplLine, newLine,
3846     newMBKernel + strlen(prefix), config->cfi);
3847     needs &= ~NEED_MB;
3848     }
3849 niro 532
3850 niro 914 } else if (tmplLine->type == LT_MBMODULE &&
3851     tmplLine->numElements >= 2) {
3852     if (new->multiboot) {
3853     if (needs & NEED_KERNEL) {
3854     newLine = addLineTmpl(new, tmplLine, newLine,
3855     newKernelPath +
3856     strlen(prefix), config->cfi);
3857     needs &= ~NEED_KERNEL;
3858     } else if (config->cfi->mbInitRdIsModule &&
3859     (needs & NEED_INITRD)) {
3860     char *initrdVal;
3861     initrdVal = getInitrdVal(config, prefix, tmplLine,
3862     newKernelInitrd, extraInitrds,
3863     extraInitrdCount);
3864     newLine = addLineTmpl(new, tmplLine, newLine,
3865     initrdVal, config->cfi);
3866     free(initrdVal);
3867     needs &= ~NEED_INITRD;
3868     }
3869     } else if (needs & NEED_KERNEL) {
3870     /* template is multi but new is not,
3871     * insert the kernel in the first module slot
3872     */
3873 niro 1940 tmplLine->type = preferredLineType(LT_KERNEL, config->cfi);
3874 niro 914 free(tmplLine->elements[0].item);
3875     tmplLine->elements[0].item =
3876 niro 1940 strdup(getKeywordByType(tmplLine->type,
3877     config->cfi)->key);
3878 niro 914 newLine = addLineTmpl(new, tmplLine, newLine,
3879 niro 1940 newKernelPath + strlen(prefix),
3880     config->cfi);
3881 niro 914 needs &= ~NEED_KERNEL;
3882     } else if (needs & NEED_INITRD) {
3883     char *initrdVal;
3884     /* template is multi but new is not,
3885     * insert the initrd in the second module slot
3886     */
3887 niro 1940 tmplLine->type = preferredLineType(LT_INITRD, config->cfi);
3888 niro 914 free(tmplLine->elements[0].item);
3889     tmplLine->elements[0].item =
3890 niro 1940 strdup(getKeywordByType(tmplLine->type,
3891     config->cfi)->key);
3892 niro 914 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3893     newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3894     free(initrdVal);
3895     needs &= ~NEED_INITRD;
3896     }
3897 niro 532
3898 niro 1940 } else if (isinitrd(tmplLine->type) && tmplLine->numElements >= 2) {
3899 niro 914 if (needs & NEED_INITRD &&
3900     new->multiboot && !template->multiboot &&
3901     config->cfi->mbInitRdIsModule) {
3902     /* make sure we don't insert the module initrd
3903     * before the module kernel... if we don't do it here,
3904     * it will be inserted following the template.
3905     */
3906     if (!needs & NEED_KERNEL) {
3907     char *initrdVal;
3908    
3909     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3910     newLine = addLine(new, config->cfi, LT_MBMODULE,
3911     config->secondaryIndent,
3912     initrdVal);
3913     free(initrdVal);
3914     needs &= ~NEED_INITRD;
3915     }
3916     } else if (needs & NEED_INITRD) {
3917     char *initrdVal;
3918     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3919     newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3920     free(initrdVal);
3921     needs &= ~NEED_INITRD;
3922 niro 532 }
3923    
3924 niro 1696 } else if (tmplLine->type == LT_MENUENTRY &&
3925     (needs & NEED_TITLE)) {
3926     requote(tmplLine, config->cfi);
3927     char *nkt = malloc(strlen(newKernelTitle)+3);
3928     strcpy(nkt, "'");
3929     strcat(nkt, newKernelTitle);
3930     strcat(nkt, "'");
3931     newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
3932     free(nkt);
3933     needs &= ~NEED_TITLE;
3934 niro 532 } else if (tmplLine->type == LT_TITLE &&
3935 niro 914 (needs & NEED_TITLE)) {
3936     if (tmplLine->numElements >= 2) {
3937     newLine = addLineTmpl(new, tmplLine, newLine,
3938     newKernelTitle, config->cfi);
3939     needs &= ~NEED_TITLE;
3940     } else if (tmplLine->numElements == 1 &&
3941     config->cfi->titleBracketed) {
3942     /* addLineTmpl doesn't handle titleBracketed */
3943     newLine = addLine(new, config->cfi, LT_TITLE,
3944     tmplLine->indent, newKernelTitle);
3945     needs &= ~NEED_TITLE;
3946     }
3947 niro 1696 } else if (tmplLine->type == LT_ECHO) {
3948     requote(tmplLine, config->cfi);
3949 niro 1722 static const char *prefix = "'Loading ";
3950 niro 1696 if (tmplLine->numElements > 1 &&
3951 niro 1722 strstr(tmplLine->elements[1].item, prefix) &&
3952 niro 1940 masterLine->next &&
3953     iskernel(masterLine->next->type)) {
3954 niro 1696 char *newTitle = malloc(strlen(prefix) +
3955     strlen(newKernelTitle) + 2);
3956 niro 532
3957 niro 1696 strcpy(newTitle, prefix);
3958     strcat(newTitle, newKernelTitle);
3959     strcat(newTitle, "'");
3960     newLine = addLine(new, config->cfi, LT_ECHO,
3961     tmplLine->indent, newTitle);
3962     free(newTitle);
3963     } else {
3964     /* pass through other lines from the template */
3965     newLine = addLineTmpl(new, tmplLine, newLine, NULL,
3966     config->cfi);
3967     }
3968 niro 2685 } else if (tmplLine->type == LT_DEVTREE &&
3969     tmplLine->numElements == 2 && newDevTreePath) {
3970     newLine = addLineTmpl(new, tmplLine, newLine,
3971     newDevTreePath + strlen(prefix),
3972     config->cfi);
3973     needs &= ~NEED_DEVTREE;
3974     } else if (tmplLine->type == LT_ENTRY_END && needs & NEED_DEVTREE) {
3975     const char *ndtp = newDevTreePath;
3976     if (!strncmp(newDevTreePath, prefix, strlen(prefix)))
3977     ndtp += strlen(prefix);
3978     newLine = addLine(new, config->cfi, LT_DEVTREE,
3979     config->secondaryIndent,
3980     ndtp);
3981     needs &= ~NEED_DEVTREE;
3982     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
3983 niro 914 } else {
3984     /* pass through other lines from the template */
3985     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
3986     }
3987 niro 532 }
3988 niro 914
3989 niro 532 } else {
3990 niro 914 /* don't have a template, so start the entry with the
3991     * appropriate starting line
3992     */
3993 niro 1693 switch (config->cfi->entryStart) {
3994 niro 914 case LT_KERNEL:
3995 niro 1940 case LT_KERNEL_EFI:
3996 niro 2683 case LT_KERNEL_16:
3997 niro 914 if (new->multiboot && config->cfi->mbHyperFirst) {
3998     /* fall through to LT_HYPER */
3999     } else {
4000 niro 1940 newLine = addLine(new, config->cfi,
4001     preferredLineType(LT_KERNEL, config->cfi),
4002 niro 914 config->primaryIndent,
4003     newKernelPath + strlen(prefix));
4004     needs &= ~NEED_KERNEL;
4005     break;
4006     }
4007    
4008     case LT_HYPER:
4009     newLine = addLine(new, config->cfi, LT_HYPER,
4010     config->primaryIndent,
4011     newMBKernel + strlen(prefix));
4012     needs &= ~NEED_MB;
4013 niro 532 break;
4014    
4015 niro 1696 case LT_MENUENTRY: {
4016     char *nkt = malloc(strlen(newKernelTitle)+3);
4017     strcpy(nkt, "'");
4018     strcat(nkt, newKernelTitle);
4019     strcat(nkt, "'");
4020     newLine = addLine(new, config->cfi, LT_MENUENTRY,
4021     config->primaryIndent, nkt);
4022     free(nkt);
4023     needs &= ~NEED_TITLE;
4024     needs |= NEED_END;
4025     break;
4026     }
4027 niro 914 case LT_TITLE:
4028     if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
4029     char * templabel;
4030     int x = 0, y = 0;
4031    
4032     templabel = strdup(newKernelTitle);
4033     while( templabel[x]){
4034     if( templabel[x] == ' ' ){
4035     y = x;
4036     while( templabel[y] ){
4037     templabel[y] = templabel[y+1];
4038     y++;
4039     }
4040     }
4041     x++;
4042     }
4043     newLine = addLine(new, config->cfi, LT_TITLE,
4044     config->primaryIndent, templabel);
4045     free(templabel);
4046     }else{
4047     newLine = addLine(new, config->cfi, LT_TITLE,
4048     config->primaryIndent, newKernelTitle);
4049     }
4050     needs &= ~NEED_TITLE;
4051     break;
4052    
4053     default:
4054     abort();
4055 niro 532 }
4056     }
4057    
4058 niro 2709 struct singleLine *endLine = NULL;
4059     endLine = getLineByType(LT_ENTRY_END, new->lines);
4060     if (endLine) {
4061     removeLine(new, endLine);
4062     needs |= NEED_END;
4063     }
4064    
4065 niro 914 /* add the remainder of the lines, i.e. those that either
4066     * weren't present in the template, or in the case of no template,
4067 niro 1693 * all the lines following the entryStart.
4068 niro 914 */
4069     if (needs & NEED_TITLE) {
4070     newLine = addLine(new, config->cfi, LT_TITLE,
4071     config->secondaryIndent,
4072     newKernelTitle);
4073     needs &= ~NEED_TITLE;
4074 niro 532 }
4075 niro 914 if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
4076     newLine = addLine(new, config->cfi, LT_HYPER,
4077     config->secondaryIndent,
4078     newMBKernel + strlen(prefix));
4079     needs &= ~NEED_MB;
4080     }
4081     if (needs & NEED_KERNEL) {
4082     newLine = addLine(new, config->cfi,
4083     (new->multiboot && getKeywordByType(LT_MBMODULE,
4084 niro 1940 config->cfi))
4085     ? LT_MBMODULE
4086     : preferredLineType(LT_KERNEL, config->cfi),
4087 niro 914 config->secondaryIndent,
4088     newKernelPath + strlen(prefix));
4089     needs &= ~NEED_KERNEL;
4090     }
4091     if (needs & NEED_MB) {
4092     newLine = addLine(new, config->cfi, LT_HYPER,
4093     config->secondaryIndent,
4094     newMBKernel + strlen(prefix));
4095     needs &= ~NEED_MB;
4096     }
4097     if (needs & NEED_INITRD) {
4098     char *initrdVal;
4099     initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
4100     newLine = addLine(new, config->cfi,
4101     (new->multiboot && getKeywordByType(LT_MBMODULE,
4102 niro 1940 config->cfi))
4103     ? LT_MBMODULE
4104     : preferredLineType(LT_INITRD, config->cfi),
4105 niro 914 config->secondaryIndent,
4106     initrdVal);
4107     free(initrdVal);
4108     needs &= ~NEED_INITRD;
4109     }
4110 niro 2685 if (needs & NEED_DEVTREE) {
4111     newLine = addLine(new, config->cfi, LT_DEVTREE,
4112     config->secondaryIndent,
4113     newDevTreePath);
4114     needs &= ~NEED_DEVTREE;
4115     }
4116    
4117     /* NEEDS_END must be last on bootloaders that need it... */
4118 niro 1696 if (needs & NEED_END) {
4119     newLine = addLine(new, config->cfi, LT_ENTRY_END,
4120     config->secondaryIndent, NULL);
4121     needs &= ~NEED_END;
4122     }
4123 niro 2709
4124 niro 914 if (needs) {
4125     printf(_("grubby: needs=%d, aborting\n"), needs);
4126     abort();
4127     }
4128    
4129 niro 532 if (updateImage(config, "0", prefix, newKernelArgs, NULL,
4130     newMBKernelArgs, NULL)) return 1;
4131    
4132     return 0;
4133     }
4134    
4135 niro 914 static void traceback(int signum)
4136     {
4137     void *array[40];
4138     size_t size;
4139    
4140     signal(SIGSEGV, SIG_DFL);
4141     memset(array, '\0', sizeof (array));
4142     size = backtrace(array, 40);
4143    
4144 niro 2058 fprintf(stderr, "grubby received SIGSEGV! Backtrace (%ld):\n",
4145 niro 914 (unsigned long)size);
4146     backtrace_symbols_fd(array, size, STDERR_FILENO);
4147     exit(1);
4148     }
4149    
4150 niro 532 int main(int argc, const char ** argv) {
4151     poptContext optCon;
4152 niro 1696 const char * grubConfig = NULL;
4153 niro 532 char * outputFile = NULL;
4154     int arg = 0;
4155     int flags = 0;
4156     int badImageOkay = 0;
4157 niro 1696 int configureGrub2 = 0;
4158 niro 532 int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4159     int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4160 niro 914 int configureExtLinux = 0;
4161 niro 532 int bootloaderProbe = 0;
4162 niro 914 int extraInitrdCount = 0;
4163 niro 532 char * updateKernelPath = NULL;
4164     char * newKernelPath = NULL;
4165     char * removeKernelPath = NULL;
4166     char * newKernelArgs = NULL;
4167     char * newKernelInitrd = NULL;
4168     char * newKernelTitle = NULL;
4169 niro 2685 char * newDevTreePath = NULL;
4170 niro 532 char * newMBKernel = NULL;
4171     char * newMBKernelArgs = NULL;
4172     char * removeMBKernelArgs = NULL;
4173     char * removeMBKernel = NULL;
4174     char * bootPrefix = NULL;
4175     char * defaultKernel = NULL;
4176     char * removeArgs = NULL;
4177     char * kernelInfo = NULL;
4178 niro 914 char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4179 niro 2252 char * envPath = NULL;
4180 niro 532 const char * chptr = NULL;
4181     struct configFileInfo * cfi = NULL;
4182     struct grubConfig * config;
4183     struct singleEntry * template = NULL;
4184     int copyDefault = 0, makeDefault = 0;
4185     int displayDefault = 0;
4186 niro 1720 int displayDefaultIndex = 0;
4187 niro 1721 int displayDefaultTitle = 0;
4188 niro 1859 int defaultIndex = -1;
4189 niro 532 struct poptOption options[] = {
4190     { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4191     _("add an entry for the specified kernel"), _("kernel-path") },
4192     { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
4193     _("add an entry for the specified multiboot kernel"), NULL },
4194     { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
4195     _("default arguments for the new kernel or new arguments for "
4196     "kernel being updated"), _("args") },
4197     { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
4198     _("default arguments for the new multiboot kernel or "
4199     "new arguments for multiboot kernel being updated"), NULL },
4200     { "bad-image-okay", 0, 0, &badImageOkay, 0,
4201     _("don't sanity check images in boot entries (for testing only)"),
4202     NULL },
4203     { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4204     _("filestystem which contains /boot directory (for testing only)"),
4205     _("bootfs") },
4206 niro 1854 #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4207 niro 532 { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4208 niro 1853 _("check which bootloader is installed on boot sector") },
4209 niro 532 #endif
4210     { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4211     _("path to grub config file to update (\"-\" for stdin)"),
4212     _("path") },
4213     { "copy-default", 0, 0, &copyDefault, 0,
4214     _("use the default boot entry as a template for the new entry "
4215     "being added; if the default is not a linux image, or if "
4216     "the kernel referenced by the default image does not exist, "
4217     "the first linux entry whose kernel does exist is used as the "
4218     "template"), NULL },
4219 niro 1736 { "debug", 0, 0, &debug, 0,
4220     _("print debugging information for failures") },
4221 niro 532 { "default-kernel", 0, 0, &displayDefault, 0,
4222     _("display the path of the default kernel") },
4223 niro 1720 { "default-index", 0, 0, &displayDefaultIndex, 0,
4224     _("display the index of the default kernel") },
4225 niro 1721 { "default-title", 0, 0, &displayDefaultTitle, 0,
4226     _("display the title of the default kernel") },
4227 niro 2685 { "devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4228     _("device tree file for new stanza"), _("dtb-path") },
4229 niro 532 { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
4230     _("configure elilo bootloader") },
4231 niro 1940 { "efi", 0, POPT_ARG_NONE, &isEfi, 0,
4232     _("force grub2 stanzas to use efi") },
4233 niro 2252 { "env", 0, POPT_ARG_STRING, &envPath, 0,
4234     _("path for environment data"),
4235     _("path") },
4236 niro 914 { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
4237     _("configure extlinux bootloader (from syslinux)") },
4238 niro 532 { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
4239     _("configure grub bootloader") },
4240 niro 1696 { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
4241     _("configure grub2 bootloader") },
4242 niro 532 { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
4243     _("display boot information for specified kernel"),
4244     _("kernel-path") },
4245     { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
4246     _("initrd image for the new kernel"), _("initrd-path") },
4247 niro 914 { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
4248 niro 2058 _("auxiliary initrd image for things other than the new kernel"), _("initrd-path") },
4249 niro 532 { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
4250     _("configure lilo bootloader") },
4251     { "make-default", 0, 0, &makeDefault, 0,
4252     _("make the newly added entry the default boot entry"), NULL },
4253     { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
4254     _("path to output updated config file (\"-\" for stdout)"),
4255     _("path") },
4256     { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
4257     _("remove kernel arguments"), NULL },
4258     { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
4259     _("remove multiboot kernel arguments"), NULL },
4260     { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
4261     _("remove all entries for the specified kernel"),
4262     _("kernel-path") },
4263     { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
4264     _("remove all entries for the specified multiboot kernel"), NULL },
4265     { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
4266     _("make the first entry referencing the specified kernel "
4267     "the default"), _("kernel-path") },
4268 niro 1859 { "set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
4269     _("make the given entry index the default entry"),
4270     _("entry-index") },
4271 niro 532 { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
4272     _("configure silo bootloader") },
4273     { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
4274     _("title to use for the new kernel entry"), _("entry-title") },
4275     { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
4276     _("updated information for the specified kernel"),
4277     _("kernel-path") },
4278     { "version", 'v', 0, NULL, 'v',
4279     _("print the version of this program and exit"), NULL },
4280     { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
4281     _("configure yaboot bootloader") },
4282     { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
4283     _("configure zipl bootloader") },
4284     POPT_AUTOHELP
4285     { 0, 0, 0, 0, 0 }
4286     };
4287    
4288 niro 914 useextlinuxmenu=0;
4289    
4290     signal(SIGSEGV, traceback);
4291    
4292 niro 2236 int i = 0;
4293     for (int j = 1; j < argc; j++)
4294     i += strlen(argv[j]) + 1;
4295     saved_command_line = malloc(i);
4296     if (!saved_command_line) {
4297     fprintf(stderr, "grubby: %m\n");
4298     exit(1);
4299     }
4300     saved_command_line[0] = '\0';
4301     for (int j = 1; j < argc; j++) {
4302     strcat(saved_command_line, argv[j]);
4303     strncat(saved_command_line, j == argc -1 ? "" : " ", 1);
4304     }
4305    
4306 niro 532 optCon = poptGetContext("grubby", argc, argv, options, 0);
4307     poptReadDefaultConfig(optCon, 1);
4308    
4309     while ((arg = poptGetNextOpt(optCon)) >= 0) {
4310     switch (arg) {
4311     case 'v':
4312     printf("grubby version %s\n", VERSION);
4313     exit(0);
4314     break;
4315 niro 914 case 'i':
4316     if (extraInitrdCount < MAX_EXTRA_INITRDS) {
4317     extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
4318     } else {
4319     fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
4320     return 1;
4321     }
4322     break;
4323 niro 532 }
4324     }
4325    
4326     if (arg < -1) {
4327     fprintf(stderr, _("grubby: bad argument %s: %s\n"),
4328     poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
4329     poptStrerror(arg));
4330     return 1;
4331     }
4332    
4333     if ((chptr = poptGetArg(optCon))) {
4334     fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
4335     return 1;
4336     }
4337    
4338 niro 1696 if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
4339 niro 914 configureYaboot + configureSilo + configureZipl +
4340     configureExtLinux ) > 1) {
4341 niro 532 fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
4342     return 1;
4343     } else if (bootloaderProbe && grubConfig) {
4344     fprintf(stderr,
4345     _("grubby: cannot specify config file with --bootloader-probe\n"));
4346     return 1;
4347 niro 1696 } else if (configureGrub2) {
4348     cfi = &grub2ConfigType;
4349 niro 2252 if (envPath)
4350     cfi->envFile = envPath;
4351 niro 532 } else if (configureLilo) {
4352     cfi = &liloConfigType;
4353     } else if (configureGrub) {
4354     cfi = &grubConfigType;
4355     } else if (configureELilo) {
4356     cfi = &eliloConfigType;
4357     } else if (configureYaboot) {
4358     cfi = &yabootConfigType;
4359     } else if (configureSilo) {
4360     cfi = &siloConfigType;
4361     } else if (configureZipl) {
4362     cfi = &ziplConfigType;
4363 niro 914 } else if (configureExtLinux) {
4364     cfi = &extlinuxConfigType;
4365     useextlinuxmenu=1;
4366 niro 532 }
4367    
4368     if (!cfi) {
4369 niro 1802 if (grub2FindConfig(&grub2ConfigType))
4370     cfi = &grub2ConfigType;
4371     else
4372 niro 532 #ifdef __ia64__
4373 niro 1802 cfi = &eliloConfigType;
4374 niro 532 #elif __powerpc__
4375 niro 1802 cfi = &yabootConfigType;
4376 niro 532 #elif __sparc__
4377 niro 1802 cfi = &siloConfigType;
4378 niro 532 #elif __s390__
4379 niro 1802 cfi = &ziplConfigType;
4380 niro 532 #elif __s390x__
4381 niro 1802 cfi = &ziplConfigtype;
4382 niro 532 #else
4383 niro 1696 cfi = &grubConfigType;
4384 niro 532 #endif
4385     }
4386    
4387 niro 1696 if (!grubConfig) {
4388     if (cfi->findConfig)
4389     grubConfig = cfi->findConfig(cfi);
4390     if (!grubConfig)
4391     grubConfig = cfi->defaultConfig;
4392     }
4393 niro 532
4394 niro 2685 if (bootloaderProbe && (displayDefault || kernelInfo ||
4395 niro 1859 newKernelPath || removeKernelPath || makeDefault ||
4396     defaultKernel || displayDefaultIndex || displayDefaultTitle ||
4397     (defaultIndex >= 0))) {
4398 niro 532 fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
4399     "specified option"));
4400     return 1;
4401     }
4402    
4403 niro 2685 if ((displayDefault || kernelInfo) && (newKernelPath ||
4404 niro 532 removeKernelPath)) {
4405     fprintf(stderr, _("grubby: --default-kernel and --info may not "
4406     "be used when adding or removing kernels\n"));
4407     return 1;
4408     }
4409    
4410     if (newKernelPath && !newKernelTitle) {
4411     fprintf(stderr, _("grubby: kernel title must be specified\n"));
4412     return 1;
4413 niro 1156 } else if (!newKernelPath && (newKernelTitle || copyDefault ||
4414     (newKernelInitrd && !updateKernelPath)||
4415 niro 914 makeDefault || extraInitrdCount > 0)) {
4416 niro 532 fprintf(stderr, _("grubby: kernel path expected\n"));
4417     return 1;
4418     }
4419    
4420     if (newKernelPath && updateKernelPath) {
4421     fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
4422     "not be used together"));
4423     return 1;
4424     }
4425    
4426     if (makeDefault && defaultKernel) {
4427     fprintf(stderr, _("grubby: --make-default and --default-kernel "
4428     "may not be used together\n"));
4429     return 1;
4430     } else if (defaultKernel && removeKernelPath &&
4431     !strcmp(defaultKernel, removeKernelPath)) {
4432     fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));
4433     return 1;
4434     } else if (defaultKernel && newKernelPath &&
4435     !strcmp(defaultKernel, newKernelPath)) {
4436     makeDefault = 1;
4437     defaultKernel = NULL;
4438     }
4439 niro 1859 else if (defaultKernel && (defaultIndex >= 0)) {
4440     fprintf(stderr, _("grubby: --set-default and --set-default-index "
4441     "may not be used together\n"));
4442     return 1;
4443     }
4444 niro 532
4445 niro 1717 if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
4446 niro 532 fprintf(stderr, _("grubby: output file must be specified if stdin "
4447     "is used\n"));
4448     return 1;
4449     }
4450    
4451     if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
4452 niro 1859 && !kernelInfo && !bootloaderProbe && !updateKernelPath
4453     && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle
4454     && (defaultIndex == -1)) {
4455 niro 532 fprintf(stderr, _("grubby: no action specified\n"));
4456     return 1;
4457     }
4458    
4459     flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
4460    
4461     if (cfi->needsBootPrefix) {
4462     if (!bootPrefix) {
4463     bootPrefix = findBootPrefix();
4464     if (!bootPrefix) return 1;
4465     } else {
4466     /* this shouldn't end with a / */
4467     if (bootPrefix[strlen(bootPrefix) - 1] == '/')
4468     bootPrefix[strlen(bootPrefix) - 1] = '\0';
4469     }
4470     } else {
4471     bootPrefix = "";
4472     }
4473    
4474 niro 914 if (!cfi->mbAllowExtraInitRds &&
4475     extraInitrdCount > 0) {
4476     fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
4477     return 1;
4478     }
4479    
4480 niro 532 if (bootloaderProbe) {
4481 niro 1854 int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
4482     struct grubConfig * lconfig, * gconfig, * yconfig, * econfig;
4483 niro 532
4484 niro 1696 const char *grub2config = grub2FindConfig(&grub2ConfigType);
4485     if (grub2config) {
4486     gconfig = readConfig(grub2config, &grub2ConfigType);
4487     if (!gconfig)
4488     gr2c = 1;
4489     else
4490     gr2c = checkForGrub2(gconfig);
4491     }
4492    
4493 niro 1715 const char *grubconfig = grubFindConfig(&grubConfigType);
4494     if (!access(grubconfig, F_OK)) {
4495     gconfig = readConfig(grubconfig, &grubConfigType);
4496 niro 532 if (!gconfig)
4497     grc = 1;
4498     else
4499     grc = checkForGrub(gconfig);
4500     }
4501    
4502     if (!access(liloConfigType.defaultConfig, F_OK)) {
4503     lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);
4504     if (!lconfig)
4505     lrc = 1;
4506     else
4507     lrc = checkForLilo(lconfig);
4508     }
4509    
4510 niro 1854 if (!access(eliloConfigType.defaultConfig, F_OK)) {
4511     econfig = readConfig(eliloConfigType.defaultConfig,
4512     &eliloConfigType);
4513     if (!econfig)
4514     erc = 1;
4515     else
4516     erc = checkForElilo(econfig);
4517     }
4518    
4519 niro 914 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
4520     lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
4521     if (!lconfig)
4522 niro 1854 extrc = 1;
4523 niro 914 else
4524 niro 1854 extrc = checkForExtLinux(lconfig);
4525 niro 914 }
4526    
4527 niro 532
4528 niro 1853 if (!access(yabootConfigType.defaultConfig, F_OK)) {
4529     yconfig = readConfig(yabootConfigType.defaultConfig,
4530     &yabootConfigType);
4531     if (!yconfig)
4532     yrc = 1;
4533     else
4534 niro 1864 yrc = checkForYaboot(yconfig);
4535 niro 1853 }
4536    
4537 niro 1854 if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1 ||
4538 niro 1855 erc == 1)
4539 niro 1854 return 1;
4540 niro 1853
4541 niro 532 if (lrc == 2) printf("lilo\n");
4542 niro 1696 if (gr2c == 2) printf("grub2\n");
4543 niro 532 if (grc == 2) printf("grub\n");
4544 niro 1854 if (extrc == 2) printf("extlinux\n");
4545 niro 1853 if (yrc == 2) printf("yaboot\n");
4546 niro 1854 if (erc == 2) printf("elilo\n");
4547 niro 532
4548     return 0;
4549     }
4550    
4551 niro 2246 if (grubConfig == NULL) {
4552     printf("Could not find bootloader configuration file.\n");
4553     exit(1);
4554     }
4555    
4556 niro 532 config = readConfig(grubConfig, cfi);
4557     if (!config) return 1;
4558    
4559     if (displayDefault) {
4560     struct singleLine * line;
4561     struct singleEntry * entry;
4562     char * rootspec;
4563    
4564     if (config->defaultImage == -1) return 0;
4565 niro 2255 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4566     cfi->defaultIsSaved)
4567     config->defaultImage = 0;
4568 niro 532 entry = findEntryByIndex(config, config->defaultImage);
4569     if (!entry) return 0;
4570     if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
4571    
4572 niro 2683 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
4573 niro 532 if (!line) return 0;
4574    
4575     rootspec = getRootSpecifier(line->elements[1].item);
4576     printf("%s%s\n", bootPrefix, line->elements[1].item +
4577     ((rootspec != NULL) ? strlen(rootspec) : 0));
4578    
4579     return 0;
4580 niro 1720
4581 niro 1721 } else if (displayDefaultTitle) {
4582     struct singleLine * line;
4583     struct singleEntry * entry;
4584    
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 1721 entry = findEntryByIndex(config, config->defaultImage);
4590     if (!entry) return 0;
4591    
4592     if (!configureGrub2) {
4593     line = getLineByType(LT_TITLE, entry->lines);
4594     if (!line) return 0;
4595     printf("%s\n", line->elements[1].item);
4596    
4597     } else {
4598 niro 1746 char * title;
4599 niro 1721
4600     dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
4601     line = getLineByType(LT_MENUENTRY, entry->lines);
4602     if (!line) return 0;
4603 niro 1746 title = grub2ExtractTitle(line);
4604     if (title)
4605     printf("%s\n", title);
4606 niro 1721 }
4607     return 0;
4608    
4609 niro 1720 } else if (displayDefaultIndex) {
4610     if (config->defaultImage == -1) return 0;
4611 niro 2255 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4612     cfi->defaultIsSaved)
4613     config->defaultImage = 0;
4614 niro 1720 printf("%i\n", config->defaultImage);
4615 niro 2250 return 0;
4616 niro 1720
4617 niro 532 } else if (kernelInfo)
4618     return displayInfo(config, kernelInfo, bootPrefix);
4619    
4620     if (copyDefault) {
4621     template = findTemplate(config, bootPrefix, NULL, 0, flags);
4622     if (!template) return 1;
4623     }
4624    
4625     markRemovedImage(config, removeKernelPath, bootPrefix);
4626     markRemovedImage(config, removeMBKernel, bootPrefix);
4627     setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
4628 niro 1859 bootPrefix, flags, defaultIndex);
4629 niro 532 setFallbackImage(config, newKernelPath != NULL);
4630     if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
4631     removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
4632 niro 1156 if (updateKernelPath && newKernelInitrd) {
4633 niro 2263 if (newMBKernel) {
4634     if (addMBInitrd(config, newMBKernel, updateKernelPath,
4635     bootPrefix, newKernelInitrd))
4636     return 1;
4637     } else {
4638     if (updateInitrd(config, updateKernelPath, bootPrefix,
4639     newKernelInitrd))
4640     return 1;
4641     }
4642 niro 1156 }
4643 niro 532 if (addNewKernel(config, template, bootPrefix, newKernelPath,
4644     newKernelTitle, newKernelArgs, newKernelInitrd,
4645 niro 1844 (const char **)extraInitrds, extraInitrdCount,
4646 niro 2685 newMBKernel, newMBKernelArgs, newDevTreePath)) return 1;
4647 niro 532
4648    
4649     if (numEntries(config) == 0) {
4650     fprintf(stderr, _("grubby: doing this would leave no kernel entries. "
4651     "Not writing out new config.\n"));
4652     return 1;
4653     }
4654    
4655     if (!outputFile)
4656 niro 1696 outputFile = (char *)grubConfig;
4657 niro 532
4658     return writeConfig(config, outputFile, bootPrefix);
4659     }