Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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