Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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