Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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