Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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