Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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