Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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