Magellan Linux

Annotation of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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