Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1746 - (hide annotations) (download)
Sat Feb 18 01:06:20 2012 UTC (12 years, 2 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 104452 byte(s)
Refactor grub2 title extraction, making it a function

'--default-title' now uses that functions, but others such as '--info'
could, IMHO, also display the title and thus, use the refactored
function.

Signed-off-by: Cleber Rosa <crosa@redhat.com>


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