Magellan Linux

Annotation of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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