Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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