Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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