Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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