Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1745 - (hide annotations) (download)
Sat Feb 18 01:05:52 2012 UTC (12 years, 2 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 102856 byte(s)
Include a / when one is missing in paths (#769641)


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