Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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