Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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