Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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