Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1156 - (hide annotations) (download)
Tue Sep 14 19:47:52 2010 UTC (13 years, 7 months ago) by niro
Original Path: trunk/mkinitrd-magellan/grubby/grubby.c
File MIME type: text/plain
File size: 92052 byte(s)
-merged with upstream:
-update initrd support
-better rootdev handling
-better support for uuids
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 532
1297 niro 1156 if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
1298     free(rootdev);
1299 niro 914 return 0;
1300 niro 1156 }
1301 niro 532
1302 niro 1156 free(rootdev);
1303    
1304 niro 532 return 1;
1305     }
1306    
1307     /* returns the first match on or after the one pointed to by index (if index
1308     is not NULL) which is not marked as skip */
1309     struct singleEntry * findEntryByPath(struct grubConfig * config,
1310     const char * kernel, const char * prefix,
1311     int * index) {
1312     struct singleEntry * entry = NULL;
1313     struct singleLine * line;
1314     int i;
1315     char * chptr;
1316     char * rootspec = NULL;
1317     enum lineType_e checkType = LT_KERNEL;
1318    
1319     if (isdigit(*kernel)) {
1320     int * indexVars = alloca(sizeof(*indexVars) * strlen(kernel));
1321    
1322     i = 0;
1323     indexVars[i] = strtol(kernel, &chptr, 10);
1324     while (*chptr == ',') {
1325     i++;
1326     kernel = chptr + 1;
1327     indexVars[i] = strtol(kernel, &chptr, 10);
1328     }
1329    
1330     if (*chptr) {
1331     /* can't parse it, bail */
1332     return NULL;
1333     }
1334    
1335     indexVars[i + 1] = -1;
1336    
1337     i = 0;
1338     if (index) {
1339     while (i < *index) i++;
1340     if (indexVars[i] == -1) return NULL;
1341     }
1342    
1343     entry = findEntryByIndex(config, indexVars[i]);
1344     if (!entry) return NULL;
1345    
1346 niro 914 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1347 niro 532 if (!line) return NULL;
1348    
1349     if (index) *index = indexVars[i];
1350     return entry;
1351     }
1352    
1353     if (!strcmp(kernel, "DEFAULT")) {
1354     if (index && *index > config->defaultImage) {
1355     entry = NULL;
1356     } else {
1357     entry = findEntryByIndex(config, config->defaultImage);
1358     if (entry && entry->skip)
1359     entry = NULL;
1360     else if (index)
1361     *index = config->defaultImage;
1362     }
1363     } else if (!strcmp(kernel, "ALL")) {
1364     if (index)
1365     i = *index;
1366     else
1367     i = 0;
1368    
1369     while ((entry = findEntryByIndex(config, i))) {
1370     if (!entry->skip) break;
1371     i++;
1372     }
1373    
1374     if (entry && index)
1375     *index = i;
1376     } else {
1377     if (index)
1378     i = *index;
1379     else
1380     i = 0;
1381    
1382     if (!strncmp(kernel, "TITLE=", 6)) {
1383     prefix = "";
1384     checkType = LT_TITLE;
1385     kernel += 6;
1386     }
1387    
1388 niro 914 for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {
1389     if (entry->skip) continue;
1390 niro 532
1391 niro 914 dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);
1392 niro 532
1393 niro 914 /* check all the lines matching checkType */
1394     for (line = entry->lines; line; line = line->next) {
1395     line = getLineByType(entry->multiboot && checkType == LT_KERNEL ?
1396     LT_KERNEL|LT_MBMODULE|LT_HYPER :
1397     checkType, line);
1398     if (!line) break; /* not found in this entry */
1399 niro 532
1400 niro 914 if (line && line->numElements >= 2) {
1401     rootspec = getRootSpecifier(line->elements[1].item);
1402     if (!strcmp(line->elements[1].item +
1403     ((rootspec != NULL) ? strlen(rootspec) : 0),
1404     kernel + strlen(prefix)))
1405     break;
1406     }
1407     }
1408    
1409     /* make sure this entry has a kernel identifier; this skips
1410     * non-Linux boot entries (could find netbsd etc, though, which is
1411     * unfortunate)
1412     */
1413     if (line && getLineByType(LT_KERNEL|LT_HYPER, entry->lines))
1414     break; /* found 'im! */
1415 niro 532 }
1416    
1417     if (index) *index = i;
1418     }
1419    
1420     return entry;
1421     }
1422    
1423     struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index) {
1424     struct singleEntry * entry;
1425    
1426     entry = cfg->entries;
1427     while (index && entry) {
1428     entry = entry->next;
1429     index--;
1430     }
1431    
1432     return entry;
1433     }
1434    
1435     /* Find a good template to use for the new kernel. An entry is
1436     * good if the kernel and mkinitrd exist (even if the entry
1437     * is going to be removed). Try and use the default entry, but
1438     * if that doesn't work just take the first. If we can't find one,
1439     * bail. */
1440     struct singleEntry * findTemplate(struct grubConfig * cfg, const char * prefix,
1441     int * indexPtr, int skipRemoved, int flags) {
1442     struct singleEntry * entry, * entry2;
1443     int index;
1444    
1445     if (cfg->defaultImage > -1) {
1446     entry = findEntryByIndex(cfg, cfg->defaultImage);
1447     if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
1448     if (indexPtr) *indexPtr = cfg->defaultImage;
1449     return entry;
1450     }
1451     }
1452    
1453     index = 0;
1454     while ((entry = findEntryByIndex(cfg, index))) {
1455     if (suitableImage(entry, prefix, skipRemoved, flags)) {
1456     int j;
1457     for (j = 0; j < index; j++) {
1458     entry2 = findEntryByIndex(cfg, j);
1459     if (entry2->skip) index--;
1460     }
1461     if (indexPtr) *indexPtr = index;
1462    
1463     return entry;
1464     }
1465    
1466     index++;
1467     }
1468    
1469     fprintf(stderr, _("grubby fatal error: unable to find a suitable template\n"));
1470    
1471     return NULL;
1472     }
1473    
1474     char * findBootPrefix(void) {
1475     struct stat sb, sb2;
1476    
1477     stat("/", &sb);
1478     #ifdef __ia64__
1479     stat("/boot/efi/EFI/redhat/", &sb2);
1480     #else
1481     stat("/boot", &sb2);
1482     #endif
1483    
1484     if (sb.st_dev == sb2.st_dev)
1485     return strdup("");
1486    
1487     #ifdef __ia64__
1488     return strdup("/boot/efi/EFI/redhat/");
1489     #else
1490     return strdup("/boot");
1491     #endif
1492     }
1493    
1494     void markRemovedImage(struct grubConfig * cfg, const char * image,
1495     const char * prefix) {
1496     struct singleEntry * entry;
1497    
1498     if (!image) return;
1499    
1500     while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
1501     entry->skip = 1;
1502     }
1503    
1504     void setDefaultImage(struct grubConfig * config, int hasNew,
1505     const char * defaultKernelPath, int newIsDefault,
1506     const char * prefix, int flags) {
1507     struct singleEntry * entry, * entry2, * newDefault;
1508     int i, j;
1509    
1510     if (newIsDefault) {
1511     config->defaultImage = 0;
1512     return;
1513     } else if (defaultKernelPath) {
1514     i = 0;
1515     if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
1516     config->defaultImage = i;
1517     } else {
1518     config->defaultImage = -1;
1519     return;
1520     }
1521     }
1522    
1523     /* defaultImage now points to what we'd like to use, but before any order
1524     changes */
1525     if (config->defaultImage == DEFAULT_SAVED)
1526     /* default is set to saved, we don't want to change it */
1527     return;
1528    
1529     if (config->defaultImage > -1)
1530     entry = findEntryByIndex(config, config->defaultImage);
1531     else
1532     entry = NULL;
1533    
1534     if (entry && !entry->skip) {
1535     /* we can preserve the default */
1536     if (hasNew)
1537     config->defaultImage++;
1538    
1539     /* count the number of entries erased before this one */
1540     for (j = 0; j < config->defaultImage; j++) {
1541     entry2 = findEntryByIndex(config, j);
1542     if (entry2->skip) config->defaultImage--;
1543     }
1544     } else if (hasNew) {
1545     config->defaultImage = 0;
1546     } else {
1547     /* Either we just erased the default (or the default line was bad
1548     * to begin with) and didn't put a new one in. We'll use the first
1549     * valid image. */
1550     newDefault = findTemplate(config, prefix, &config->defaultImage, 1,
1551     flags);
1552     if (!newDefault)
1553     config->defaultImage = -1;
1554     }
1555     }
1556    
1557     void setFallbackImage(struct grubConfig * config, int hasNew) {
1558     struct singleEntry * entry, * entry2;
1559     int j;
1560    
1561     if (config->fallbackImage == -1) return;
1562    
1563     entry = findEntryByIndex(config, config->fallbackImage);
1564     if (!entry || entry->skip) {
1565     config->fallbackImage = -1;
1566     return;
1567     }
1568    
1569     if (hasNew)
1570     config->fallbackImage++;
1571    
1572     /* count the number of entries erased before this one */
1573     for (j = 0; j < config->fallbackImage; j++) {
1574     entry2 = findEntryByIndex(config, j);
1575     if (entry2->skip) config->fallbackImage--;
1576     }
1577     }
1578    
1579     void displayEntry(struct singleEntry * entry, const char * prefix, int index) {
1580     struct singleLine * line;
1581     char * root = NULL;
1582     int i;
1583    
1584     printf("index=%d\n", index);
1585    
1586 niro 914 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1587     if (!line) {
1588     printf("non linux entry\n");
1589     return;
1590     }
1591    
1592 niro 532 printf("kernel=%s\n", line->elements[1].item);
1593    
1594     if (line->numElements >= 3) {
1595     printf("args=\"");
1596     i = 2;
1597     while (i < line->numElements) {
1598     if (!strncmp(line->elements[i].item, "root=", 5)) {
1599     root = line->elements[i].item + 5;
1600     } else {
1601     printf("%s%s", line->elements[i].item,
1602     line->elements[i].indent);
1603     }
1604    
1605     i++;
1606     }
1607     printf("\"\n");
1608     } else {
1609 niro 914 line = getLineByType(LT_KERNELARGS, entry->lines);
1610 niro 532 if (line) {
1611     char * s;
1612    
1613     printf("args=\"");
1614     i = 1;
1615     while (i < line->numElements) {
1616     if (!strncmp(line->elements[i].item, "root=", 5)) {
1617     root = line->elements[i].item + 5;
1618     } else {
1619     s = line->elements[i].item;
1620    
1621     printf("%s%s", s, line->elements[i].indent);
1622     }
1623    
1624     i++;
1625     }
1626    
1627     s = line->elements[i - 1].indent;
1628     printf("\"\n");
1629     }
1630     }
1631    
1632     if (!root) {
1633 niro 914 line = getLineByType(LT_ROOT, entry->lines);
1634 niro 532 if (line && line->numElements >= 2)
1635     root=line->elements[1].item;
1636     }
1637    
1638     if (root) {
1639     char * s = alloca(strlen(root) + 1);
1640    
1641     strcpy(s, root);
1642     if (s[strlen(s) - 1] == '"')
1643     s[strlen(s) - 1] = '\0';
1644     /* make sure the root doesn't have a trailing " */
1645     printf("root=%s\n", s);
1646     }
1647    
1648 niro 914 line = getLineByType(LT_INITRD, entry->lines);
1649 niro 532
1650     if (line && line->numElements >= 2) {
1651     printf("initrd=%s", prefix);
1652     for (i = 1; i < line->numElements; i++)
1653     printf("%s%s", line->elements[i].item, line->elements[i].indent);
1654     printf("\n");
1655     }
1656     }
1657    
1658     int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
1659     FILE * in;
1660     char buf[1024];
1661     char * chptr;
1662     char * start;
1663     char * param;
1664    
1665 niro 926 in = fopen("/etc/conf.d/grub", "r");
1666 niro 532 if (!in) return 1;
1667    
1668     if (lbaPtr) *lbaPtr = 0;
1669     if (bootPtr) *bootPtr = NULL;
1670    
1671     while (fgets(buf, sizeof(buf), in)) {
1672     start = buf;
1673     while (isspace(*start)) start++;
1674     if (*start == '#') continue;
1675    
1676     chptr = strchr(start, '=');
1677     if (!chptr) continue;
1678     chptr--;
1679     while (*chptr && isspace(*chptr)) chptr--;
1680     chptr++;
1681     *chptr = '\0';
1682    
1683     param = chptr + 1;
1684     while (*param && isspace(*param)) param++;
1685     if (*param == '=') {
1686     param++;
1687     while (*param && isspace(*param)) param++;
1688     }
1689    
1690     chptr = param;
1691     while (*chptr && !isspace(*chptr)) chptr++;
1692     *chptr = '\0';
1693    
1694     if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
1695     *lbaPtr = 1;
1696     else if (!strcmp(start, "boot") && bootPtr)
1697     *bootPtr = strdup(param);
1698     }
1699    
1700     fclose(in);
1701    
1702     return 0;
1703     }
1704    
1705     void dumpSysconfigGrub(void) {
1706     char * boot;
1707     int lba;
1708    
1709     if (!parseSysconfigGrub(&lba, &boot)) {
1710     if (lba) printf("lba\n");
1711     if (boot) printf("boot=%s\n", boot);
1712     }
1713     }
1714    
1715     int displayInfo(struct grubConfig * config, char * kernel,
1716     const char * prefix) {
1717     int i = 0;
1718     struct singleEntry * entry;
1719     struct singleLine * line;
1720    
1721     entry = findEntryByPath(config, kernel, prefix, &i);
1722     if (!entry) {
1723     fprintf(stderr, _("grubby: kernel not found\n"));
1724     return 1;
1725     }
1726    
1727 niro 926 /* this is a horrible hack to support /etc/conf.d/grub; there must
1728 niro 532 be a better way */
1729     if (config->cfi == &grubConfigType) {
1730     dumpSysconfigGrub();
1731     } else {
1732 niro 914 line = getLineByType(LT_BOOT, config->theLines);
1733 niro 532 if (line && line->numElements >= 1) {
1734     printf("boot=%s\n", line->elements[1].item);
1735     }
1736    
1737 niro 914 line = getLineByType(LT_LBA, config->theLines);
1738 niro 532 if (line) printf("lba\n");
1739     }
1740    
1741     displayEntry(entry, prefix, i);
1742    
1743     i++;
1744     while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
1745     displayEntry(entry, prefix, i);
1746     i++;
1747     }
1748    
1749     return 0;
1750     }
1751    
1752 niro 914 struct singleLine * addLineTmpl(struct singleEntry * entry,
1753     struct singleLine * tmplLine,
1754     struct singleLine * prevLine,
1755     const char * val,
1756     struct configFileInfo * cfi)
1757     {
1758     struct singleLine * newLine = lineDup(tmplLine);
1759    
1760     if (val) {
1761     /* override the inherited value with our own.
1762     * This is a little weak because it only applies to elements[1]
1763     */
1764     if (newLine->numElements > 1)
1765     removeElement(newLine, 1);
1766     insertElement(newLine, val, 1, cfi);
1767    
1768     /* but try to keep the rootspec from the template... sigh */
1769     if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD)) {
1770     char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
1771     if (rootspec != NULL) {
1772     free(newLine->elements[1].item);
1773     newLine->elements[1].item =
1774     sdupprintf("%s%s", rootspec, val);
1775     }
1776     }
1777     }
1778    
1779     dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
1780     newLine->elements[0].item : "");
1781    
1782     if (!entry->lines) {
1783     /* first one on the list */
1784     entry->lines = newLine;
1785     } else if (prevLine) {
1786     /* add after prevLine */
1787     newLine->next = prevLine->next;
1788     prevLine->next = newLine;
1789     }
1790    
1791     return newLine;
1792     }
1793    
1794 niro 532 /* val may be NULL */
1795     struct singleLine * addLine(struct singleEntry * entry,
1796     struct configFileInfo * cfi,
1797 niro 914 enum lineType_e type, char * defaultIndent,
1798     const char * val) {
1799 niro 532 struct singleLine * line, * prev;
1800 niro 914 struct keywordTypes * kw;
1801     struct singleLine tmpl;
1802 niro 532
1803 niro 914 /* NB: This function shouldn't allocate items on the heap, rather on the
1804     * stack since it calls addLineTmpl which will make copies.
1805     */
1806 niro 532
1807 niro 914 if (type == LT_TITLE && cfi->titleBracketed) {
1808     /* we're doing a bracketed title (zipl) */
1809     tmpl.type = type;
1810     tmpl.numElements = 1;
1811     tmpl.elements = alloca(sizeof(*tmpl.elements));
1812     tmpl.elements[0].item = alloca(strlen(val)+3);
1813     sprintf(tmpl.elements[0].item, "[%s]", val);
1814     tmpl.elements[0].indent = "";
1815     val = NULL;
1816     } else {
1817     kw = getKeywordByType(type, cfi);
1818     if (!kw) abort();
1819     tmpl.type = type;
1820     tmpl.numElements = val ? 2 : 1;
1821     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
1822     tmpl.elements[0].item = kw->key;
1823     tmpl.elements[0].indent = alloca(2);
1824     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
1825     if (val) {
1826     tmpl.elements[1].item = (char *)val;
1827     tmpl.elements[1].indent = "";
1828     }
1829     }
1830    
1831 niro 532 /* The last non-empty line gives us the indention to us and the line
1832     to insert after. Note that comments are considered empty lines, which
1833     may not be ideal? If there are no lines or we are looking at the
1834     first line, we use defaultIndent (the first line is normally indented
1835     differently from the rest) */
1836 niro 914 for (line = entry->lines, prev = NULL; line; line = line->next) {
1837     if (line->numElements) prev = line;
1838     /* fall back on the last line if prev isn't otherwise set */
1839     if (!line->next && !prev) prev = line;
1840 niro 532 }
1841    
1842 niro 914 if (prev == entry->lines)
1843     tmpl.indent = defaultIndent ?: "";
1844     else
1845     tmpl.indent = prev->indent;
1846 niro 532
1847 niro 914 return addLineTmpl(entry, &tmpl, prev, val, cfi);
1848 niro 532 }
1849    
1850     void removeLine(struct singleEntry * entry, struct singleLine * line) {
1851     struct singleLine * prev;
1852     int i;
1853    
1854     for (i = 0; i < line->numElements; i++) {
1855     free(line->elements[i].item);
1856     free(line->elements[i].indent);
1857     }
1858     free(line->elements);
1859     free(line->indent);
1860    
1861     if (line == entry->lines) {
1862     entry->lines = line->next;
1863     } else {
1864     prev = entry->lines;
1865     while (prev->next != line) prev = prev->next;
1866     prev->next = line->next;
1867     }
1868    
1869     free(line);
1870     }
1871    
1872 niro 914 static void insertElement(struct singleLine * line,
1873     const char * item, int insertHere,
1874     struct configFileInfo * cfi)
1875     {
1876     struct keywordTypes * kw;
1877     char indent[2] = "";
1878    
1879     /* sanity check */
1880     if (insertHere > line->numElements) {
1881     dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",
1882     insertHere, line->numElements);
1883     insertHere = line->numElements;
1884     }
1885    
1886     line->elements = realloc(line->elements, (line->numElements + 1) *
1887     sizeof(*line->elements));
1888     memmove(&line->elements[insertHere+1],
1889     &line->elements[insertHere],
1890     (line->numElements - insertHere) *
1891     sizeof(*line->elements));
1892     line->elements[insertHere].item = strdup(item);
1893    
1894     kw = getKeywordByType(line->type, cfi);
1895    
1896     if (line->numElements == 0) {
1897     indent[0] = '\0';
1898     } else if (insertHere == 0) {
1899     indent[0] = kw->nextChar;
1900     } else if (kw->separatorChar != '\0') {
1901     indent[0] = kw->separatorChar;
1902     } else {
1903     indent[0] = ' ';
1904     }
1905    
1906     if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {
1907     /* move the end-of-line forward */
1908     line->elements[insertHere].indent =
1909     line->elements[insertHere-1].indent;
1910     line->elements[insertHere-1].indent = strdup(indent);
1911     } else {
1912     line->elements[insertHere].indent = strdup(indent);
1913     }
1914    
1915     line->numElements++;
1916    
1917     dbgPrintf("insertElement(%s, '%s%s', %d)\n",
1918     line->elements[0].item,
1919     line->elements[insertHere].item,
1920     line->elements[insertHere].indent,
1921     insertHere);
1922     }
1923    
1924     static void removeElement(struct singleLine * line, int removeHere) {
1925     int i;
1926    
1927     /* sanity check */
1928     if (removeHere >= line->numElements) return;
1929    
1930     dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
1931     removeHere, line->elements[removeHere].item);
1932    
1933     free(line->elements[removeHere].item);
1934    
1935     if (removeHere > 1) {
1936     /* previous argument gets this argument's post-indentation */
1937     free(line->elements[removeHere-1].indent);
1938     line->elements[removeHere-1].indent =
1939     line->elements[removeHere].indent;
1940     } else {
1941     free(line->elements[removeHere].indent);
1942     }
1943    
1944     /* now collapse the array, but don't bother to realloc smaller */
1945     for (i = removeHere; i < line->numElements - 1; i++)
1946     line->elements[i] = line->elements[i + 1];
1947    
1948     line->numElements--;
1949     }
1950    
1951 niro 532 int argMatch(const char * one, const char * two) {
1952     char * first, * second;
1953     char * chptr;
1954    
1955     first = strcpy(alloca(strlen(one) + 1), one);
1956     second = strcpy(alloca(strlen(two) + 1), two);
1957    
1958     chptr = strchr(first, '=');
1959     if (chptr) *chptr = '\0';
1960    
1961     chptr = strchr(second, '=');
1962     if (chptr) *chptr = '\0';
1963    
1964     return strcmp(first, second);
1965     }
1966    
1967     int updateActualImage(struct grubConfig * cfg, const char * image,
1968     const char * prefix, const char * addArgs,
1969     const char * removeArgs, int multibootArgs) {
1970     struct singleEntry * entry;
1971     struct singleLine * line, * rootLine;
1972     int index = 0;
1973 niro 914 int i, k;
1974 niro 532 const char ** newArgs, ** oldArgs;
1975     const char ** arg;
1976 niro 914 int useKernelArgs, useRoot;
1977 niro 532 int firstElement;
1978     int *usedElements, *usedArgs;
1979 niro 914 int doreplace;
1980 niro 532
1981     if (!image) return 0;
1982    
1983     if (!addArgs) {
1984     newArgs = malloc(sizeof(*newArgs));
1985     *newArgs = NULL;
1986     } else {
1987     if (poptParseArgvString(addArgs, NULL, &newArgs)) {
1988     fprintf(stderr,
1989     _("grubby: error separating arguments '%s'\n"), addArgs);
1990     return 1;
1991     }
1992     }
1993    
1994     if (!removeArgs) {
1995     oldArgs = malloc(sizeof(*oldArgs));
1996     *oldArgs = NULL;
1997     } else {
1998     if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
1999     fprintf(stderr,
2000     _("grubby: error separating arguments '%s'\n"), removeArgs);
2001     free(newArgs);
2002     return 1;
2003     }
2004     }
2005    
2006    
2007 niro 914 useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
2008     && (!multibootArgs || cfg->cfi->mbConcatArgs));
2009 niro 532
2010 niro 914 useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
2011     && !multibootArgs);
2012 niro 532
2013 niro 914 for (k = 0, arg = newArgs; *arg; arg++, k++) ;
2014     usedArgs = calloc(k, sizeof(*usedArgs));
2015 niro 532
2016 niro 914 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
2017 niro 532
2018 niro 914 if (multibootArgs && !entry->multiboot)
2019     continue;
2020 niro 532
2021 niro 914 /* Determine where to put the args. If this config supports
2022     * LT_KERNELARGS, use that. Otherwise use
2023     * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
2024     */
2025     if (useKernelArgs) {
2026     line = getLineByType(LT_KERNELARGS, entry->lines);
2027     if (!line) {
2028     /* no LT_KERNELARGS, need to add it */
2029     line = addLine(entry, cfg->cfi, LT_KERNELARGS,
2030     cfg->secondaryIndent, NULL);
2031     }
2032     firstElement = 1;
2033 niro 532
2034 niro 914 } else if (multibootArgs) {
2035     line = getLineByType(LT_HYPER, entry->lines);
2036     if (!line) {
2037     /* a multiboot entry without LT_HYPER? */
2038     continue;
2039     }
2040     firstElement = 2;
2041    
2042     } else {
2043     line = getLineByType(LT_KERNEL|LT_MBMODULE, entry->lines);
2044     if (!line) {
2045     /* no LT_KERNEL or LT_MBMODULE in this entry? */
2046     continue;
2047     }
2048     firstElement = 2;
2049 niro 532 }
2050    
2051 niro 914 /* handle the elilo case which does:
2052     * append="hypervisor args -- kernel args"
2053     */
2054     if (entry->multiboot && cfg->cfi->mbConcatArgs) {
2055     /* this is a multiboot entry, make sure there's
2056     * -- on the args line
2057     */
2058     for (i = firstElement; i < line->numElements; i++) {
2059     if (!strcmp(line->elements[i].item, "--"))
2060     break;
2061     }
2062     if (i == line->numElements) {
2063     /* assume all existing args are kernel args,
2064     * prepend -- to make it official
2065     */
2066     insertElement(line, "--", firstElement, cfg->cfi);
2067     i = firstElement;
2068     }
2069     if (!multibootArgs) {
2070     /* kernel args start after the -- */
2071     firstElement = i + 1;
2072     }
2073     } else if (cfg->cfi->mbConcatArgs) {
2074     /* this is a non-multiboot entry, remove hyper args */
2075     for (i = firstElement; i < line->numElements; i++) {
2076     if (!strcmp(line->elements[i].item, "--"))
2077     break;
2078     }
2079     if (i < line->numElements) {
2080     /* remove args up to -- */
2081     while (strcmp(line->elements[firstElement].item, "--"))
2082     removeElement(line, firstElement);
2083     /* remove -- */
2084     removeElement(line, firstElement);
2085     }
2086 niro 532 }
2087    
2088 niro 914 usedElements = calloc(line->numElements, sizeof(*usedElements));
2089 niro 532
2090 niro 914 for (k = 0, arg = newArgs; *arg; arg++, k++) {
2091     if (usedArgs[k]) continue;
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     usedArgs[k]=1;
2107     break;
2108     }
2109     }
2110    
2111 niro 914 if (i < line->numElements && doreplace) {
2112     /* direct replacement */
2113 niro 532 free(line->elements[i].item);
2114     line->elements[i].item = strdup(*arg);
2115    
2116 niro 914 } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
2117     /* root= replacement */
2118     rootLine = getLineByType(LT_ROOT, entry->lines);
2119     if (rootLine) {
2120     free(rootLine->elements[1].item);
2121     rootLine->elements[1].item = strdup(*arg + 5);
2122 niro 532 } else {
2123 niro 914 rootLine = addLine(entry, cfg->cfi, LT_ROOT,
2124     cfg->secondaryIndent, *arg + 5);
2125 niro 532 }
2126 niro 914 }
2127 niro 532
2128 niro 914 else {
2129     /* insert/append */
2130     insertElement(line, *arg, i, cfg->cfi);
2131     usedElements = realloc(usedElements, line->numElements *
2132     sizeof(*usedElements));
2133     memmove(&usedElements[i + 1], &usedElements[i],
2134     line->numElements - i - 1);
2135     usedElements[i] = 1;
2136 niro 532
2137     /* if we updated a root= here even though there is a
2138     LT_ROOT available we need to remove the LT_ROOT entry
2139     (this will happen if we switch from a device to a label) */
2140     if (useRoot && !strncmp(*arg, "root=", 5)) {
2141 niro 914 rootLine = getLineByType(LT_ROOT, entry->lines);
2142     if (rootLine)
2143 niro 532 removeLine(entry, rootLine);
2144     }
2145     }
2146     }
2147    
2148     free(usedElements);
2149    
2150     for (arg = oldArgs; *arg; arg++) {
2151 niro 914 for (i = firstElement; i < line->numElements; i++) {
2152     if (multibootArgs && cfg->cfi->mbConcatArgs &&
2153     !strcmp(line->elements[i].item, "--"))
2154     /* reached the end of hyper args, stop here */
2155 niro 532 break;
2156 niro 914 if (!argMatch(line->elements[i].item, *arg)) {
2157     removeElement(line, i);
2158     break;
2159 niro 532 }
2160     }
2161 niro 914 /* handle removing LT_ROOT line too */
2162     if (useRoot && !strncmp(*arg, "root=", 5)) {
2163     rootLine = getLineByType(LT_ROOT, entry->lines);
2164     if (rootLine)
2165     removeLine(entry, rootLine);
2166     }
2167 niro 532 }
2168    
2169     if (line->numElements == 1) {
2170     /* don't need the line at all (note it has to be a
2171     LT_KERNELARGS for this to happen */
2172     removeLine(entry, line);
2173     }
2174     }
2175    
2176     free(usedArgs);
2177     free(newArgs);
2178     free(oldArgs);
2179    
2180     return 0;
2181     }
2182    
2183     int updateImage(struct grubConfig * cfg, const char * image,
2184     const char * prefix, const char * addArgs,
2185     const char * removeArgs,
2186     const char * addMBArgs, const char * removeMBArgs) {
2187     int rc = 0;
2188    
2189     if (!image) return rc;
2190    
2191     /* update the main args first... */
2192     if (addArgs || removeArgs)
2193     rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs, 0);
2194     if (rc) return rc;
2195    
2196     /* and now any multiboot args */
2197     if (addMBArgs || removeMBArgs)
2198     rc = updateActualImage(cfg, image, prefix, addMBArgs, removeMBArgs, 1);
2199     return rc;
2200     }
2201    
2202 niro 1156 int updateInitrd(struct grubConfig * cfg, const char * image,
2203     const char * prefix, const char * initrd) {
2204     struct singleEntry * entry;
2205     struct singleLine * line, * kernelLine;
2206     int index = 0;
2207    
2208     if (!image) return 0;
2209    
2210     for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
2211     kernelLine = getLineByType(LT_KERNEL, entry->lines);
2212     if (!kernelLine) continue;
2213    
2214     line = getLineByType(LT_INITRD, entry->lines);
2215     if (line)
2216     removeLine(entry, line);
2217     if (prefix) {
2218     int prefixLen = strlen(prefix);
2219     if (!strncmp(initrd, prefix, prefixLen))
2220     initrd += prefixLen;
2221     }
2222     line = addLine(entry, cfg->cfi, LT_INITRD, kernelLine->indent, initrd);
2223     if (!line) return 1;
2224     break;
2225     }
2226    
2227     return 0;
2228     }
2229    
2230 niro 532 int checkDeviceBootloader(const char * device, const unsigned char * boot) {
2231     int fd;
2232     unsigned char bootSect[512];
2233     int offset;
2234    
2235     fd = open(device, O_RDONLY);
2236     if (fd < 0) {
2237     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
2238     device, strerror(errno));
2239     return 1;
2240     }
2241    
2242     if (read(fd, bootSect, 512) != 512) {
2243     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
2244     device, strerror(errno));
2245     return 1;
2246     }
2247     close(fd);
2248    
2249     /* first three bytes should match, a jmp short should be in there */
2250     if (memcmp(boot, bootSect, 3))
2251     return 0;
2252    
2253     if (boot[1] == 0xeb) {
2254     offset = boot[2] + 2;
2255     } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
2256     offset = (boot[3] << 8) + boot[2] + 2;
2257     } else if (boot[0] == 0xeb) {
2258     offset = boot[1] + 2;
2259     } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
2260     offset = (boot[2] << 8) + boot[1] + 2;
2261     } else {
2262     return 0;
2263     }
2264    
2265     if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
2266     return 0;
2267    
2268     return 2;
2269     }
2270    
2271     int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {
2272     int fd;
2273     char buf[65536];
2274     char * end;
2275     char * chptr;
2276     char * chptr2;
2277     int rc;
2278    
2279     /* it's on raid; we need to parse /proc/mdstat and check all of the
2280     *raw* devices listed in there */
2281    
2282     if (!strncmp(mdDev, "/dev/", 5))
2283     mdDev += 5;
2284    
2285     if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
2286     fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
2287     strerror(errno));
2288     return 2;
2289     }
2290    
2291     rc = read(fd, buf, sizeof(buf) - 1);
2292     if (rc < 0 || rc == (sizeof(buf) - 1)) {
2293     fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
2294     strerror(errno));
2295     close(fd);
2296     return 2;
2297     }
2298     close(fd);
2299     buf[rc] = '\0';
2300    
2301     chptr = buf;
2302     while (*chptr) {
2303     end = strchr(chptr, '\n');
2304     if (!end) break;
2305     *end = '\0';
2306    
2307     if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
2308     chptr[strlen(mdDev)] == ' ') {
2309    
2310     /* found the device */
2311     while (*chptr && *chptr != ':') chptr++;
2312     chptr++;
2313     while (*chptr && isspace(*chptr)) chptr++;
2314    
2315     /* skip the "active" bit */
2316     while (*chptr && !isspace(*chptr)) chptr++;
2317     while (*chptr && isspace(*chptr)) chptr++;
2318    
2319     /* skip the raid level */
2320     while (*chptr && !isspace(*chptr)) chptr++;
2321     while (*chptr && isspace(*chptr)) chptr++;
2322    
2323     /* everything else is partition stuff */
2324     while (*chptr) {
2325     chptr2 = chptr;
2326     while (*chptr2 && *chptr2 != '[') chptr2++;
2327     if (!*chptr2) break;
2328    
2329     /* yank off the numbers at the end */
2330     chptr2--;
2331     while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;
2332     chptr2++;
2333     *chptr2 = '\0';
2334    
2335     /* Better, now we need the /dev/ back. We're done with
2336     * everything before this point, so we can just put
2337     * the /dev/ part there. There will always be room. */
2338     memcpy(chptr - 5, "/dev/", 5);
2339     rc = checkDeviceBootloader(chptr - 5, boot);
2340     if (rc != 2) {
2341     return rc;
2342     }
2343    
2344     chptr = chptr2 + 1;
2345     /* skip the [11] bit */
2346     while (*chptr && !isspace(*chptr)) chptr++;
2347     /* and move to the next one */
2348     while (*chptr && isspace(*chptr)) chptr++;
2349     }
2350    
2351     /* we're good to go */
2352     return 2;
2353     }
2354    
2355     chptr = end + 1;
2356     }
2357    
2358     fprintf(stderr,
2359     _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
2360     mdDev);
2361     return 0;
2362     }
2363    
2364     int checkForLilo(struct grubConfig * config) {
2365     int fd;
2366     unsigned char boot[512];
2367     struct singleLine * line;
2368    
2369     for (line = config->theLines; line; line = line->next)
2370     if (line->type == LT_BOOT) break;
2371    
2372     if (!line) {
2373     fprintf(stderr,
2374     _("grubby: no boot line found in lilo configuration\n"));
2375     return 1;
2376     }
2377    
2378     if (line->numElements != 2) return 1;
2379    
2380     fd = open("/boot/boot.b", O_RDONLY);
2381     if (fd < 0) {
2382     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
2383     "/boot/boot.b", strerror(errno));
2384     return 1;
2385     }
2386    
2387     if (read(fd, boot, 512) != 512) {
2388     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
2389     "/boot/boot.b", strerror(errno));
2390     return 1;
2391     }
2392     close(fd);
2393    
2394     if (!strncmp("/dev/md", line->elements[1].item, 7))
2395     return checkLiloOnRaid(line->elements[1].item, boot);
2396    
2397     return checkDeviceBootloader(line->elements[1].item, boot);
2398     }
2399    
2400     int checkForGrub(struct grubConfig * config) {
2401     int fd;
2402     unsigned char bootSect[512];
2403     char * boot;
2404    
2405     if (parseSysconfigGrub(NULL, &boot))
2406     return 0;
2407    
2408     /* assume grub is not installed -- not an error condition */
2409     if (!boot)
2410     return 0;
2411    
2412     fd = open("/boot/grub/stage1", O_RDONLY);
2413     if (fd < 0)
2414     /* this doesn't exist if grub hasn't been installed */
2415     return 0;
2416    
2417     if (read(fd, bootSect, 512) != 512) {
2418     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
2419     "/boot/grub/stage1", strerror(errno));
2420 niro 914 close(fd);
2421 niro 532 return 1;
2422     }
2423     close(fd);
2424    
2425     return checkDeviceBootloader(boot, bootSect);
2426     }
2427    
2428 niro 914 int checkForExtLinux(struct grubConfig * config) {
2429     int fd;
2430     unsigned char bootSect[512];
2431     char * boot;
2432     char executable[] = "/boot/extlinux/extlinux";
2433    
2434     printf("entered: checkForExtLinux()\n");
2435    
2436     if (parseSysconfigGrub(NULL, &boot))
2437     return 0;
2438    
2439     /* assume grub is not installed -- not an error condition */
2440     if (!boot)
2441     return 0;
2442    
2443     fd = open(executable, O_RDONLY);
2444     if (fd < 0)
2445     /* this doesn't exist if grub hasn't been installed */
2446     return 0;
2447    
2448     if (read(fd, bootSect, 512) != 512) {
2449     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
2450     executable, strerror(errno));
2451     return 1;
2452     }
2453     close(fd);
2454    
2455     return checkDeviceBootloader(boot, bootSect);
2456     }
2457    
2458 niro 532 static char * getRootSpecifier(char * str) {
2459     char * idx, * rootspec = NULL;
2460    
2461     if (*str == '(') {
2462     idx = rootspec = strdup(str);
2463     while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;
2464     *(++idx) = '\0';
2465     }
2466     return rootspec;
2467     }
2468    
2469 niro 914 static char * getInitrdVal(struct grubConfig * config,
2470     const char * prefix, struct singleLine *tmplLine,
2471     const char * newKernelInitrd,
2472     char ** extraInitrds, int extraInitrdCount)
2473     {
2474     char *initrdVal, *end;
2475     int i;
2476     size_t totalSize;
2477     size_t prefixLen;
2478     char separatorChar;
2479    
2480     prefixLen = strlen(prefix);
2481     totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
2482    
2483     for (i = 0; i < extraInitrdCount; i++) {
2484     totalSize += sizeof(separatorChar);
2485     totalSize += strlen(extraInitrds[i]) - prefixLen;
2486     }
2487    
2488     initrdVal = end = malloc(totalSize);
2489    
2490     end = stpcpy (end, newKernelInitrd + prefixLen);
2491    
2492     separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
2493     for (i = 0; i < extraInitrdCount; i++) {
2494     const char *extraInitrd;
2495     int j;
2496    
2497     extraInitrd = extraInitrds[i] + prefixLen;
2498     /* Don't add entries that are already there */
2499     if (tmplLine != NULL) {
2500     for (j = 2; j < tmplLine->numElements; j++)
2501     if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
2502     break;
2503    
2504     if (j != tmplLine->numElements)
2505     continue;
2506     }
2507    
2508     *end++ = separatorChar;
2509     end = stpcpy(end, extraInitrd);
2510     }
2511    
2512     return initrdVal;
2513     }
2514    
2515 niro 532 int addNewKernel(struct grubConfig * config, struct singleEntry * template,
2516     const char * prefix,
2517     char * newKernelPath, char * newKernelTitle,
2518     char * newKernelArgs, char * newKernelInitrd,
2519 niro 914 char ** extraInitrds, int extraInitrdCount,
2520 niro 532 char * newMBKernel, char * newMBKernelArgs) {
2521     struct singleEntry * new;
2522 niro 914 struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
2523 niro 532 int needs;
2524     char * chptr;
2525    
2526     if (!newKernelPath) return 0;
2527    
2528     /* if the newKernelTitle is too long silently munge it into something
2529     * we can live with. truncating is first check, then we'll just mess with
2530     * it until it looks better */
2531     if (config->cfi->maxTitleLength &&
2532     (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
2533     char * buf = alloca(config->cfi->maxTitleLength + 7);
2534     char * numBuf = alloca(config->cfi->maxTitleLength + 1);
2535     int i = 1;
2536    
2537     sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);
2538     while (findEntryByPath(config, buf, NULL, NULL)) {
2539     sprintf(numBuf, "%d", i++);
2540     strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
2541     }
2542    
2543     newKernelTitle = buf + 6;
2544     }
2545    
2546     new = malloc(sizeof(*new));
2547     new->skip = 0;
2548     new->multiboot = 0;
2549     new->next = config->entries;
2550     new->lines = NULL;
2551     config->entries = new;
2552    
2553     /* copy/update from the template */
2554 niro 914 needs = NEED_KERNEL | NEED_TITLE;
2555     if (newKernelInitrd)
2556     needs |= NEED_INITRD;
2557 niro 532 if (newMBKernel) {
2558 niro 914 needs |= NEED_MB;
2559 niro 532 new->multiboot = 1;
2560     }
2561    
2562     if (template) {
2563 niro 914 for (masterLine = template->lines;
2564     masterLine && (tmplLine = lineDup(masterLine));
2565     lineFree(tmplLine), masterLine = masterLine->next)
2566     {
2567     dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
2568 niro 532
2569     /* skip comments */
2570     chptr = tmplLine->indent;
2571     while (*chptr && isspace(*chptr)) chptr++;
2572     if (*chptr == '#') continue;
2573    
2574 niro 914 if (tmplLine->type == LT_KERNEL &&
2575     tmplLine->numElements >= 2) {
2576     if (!template->multiboot && (needs & NEED_MB)) {
2577     /* it's not a multiboot template and this is the kernel
2578     * line. Try to be intelligent about inserting the
2579     * hypervisor at the same time.
2580     */
2581     if (config->cfi->mbHyperFirst) {
2582     /* insert the hypervisor first */
2583     newLine = addLine(new, config->cfi, LT_HYPER,
2584     tmplLine->indent,
2585     newMBKernel + strlen(prefix));
2586     /* set up for adding the kernel line */
2587     free(tmplLine->indent);
2588     tmplLine->indent = strdup(config->secondaryIndent);
2589     needs &= ~NEED_MB;
2590     }
2591     if (needs & NEED_KERNEL) {
2592     /* use addLineTmpl to preserve line elements,
2593     * otherwise we could just call addLine. Unfortunately
2594     * this means making some changes to the template
2595     * such as the indent change above and the type
2596     * change below.
2597     */
2598     struct keywordTypes * mbm_kw =
2599     getKeywordByType(LT_MBMODULE, config->cfi);
2600     if (mbm_kw) {
2601     tmplLine->type = LT_MBMODULE;
2602     free(tmplLine->elements[0].item);
2603     tmplLine->elements[0].item = strdup(mbm_kw->key);
2604     }
2605     newLine = addLineTmpl(new, tmplLine, newLine,
2606     newKernelPath + strlen(prefix), config->cfi);
2607     needs &= ~NEED_KERNEL;
2608     }
2609     if (needs & NEED_MB) { /* !mbHyperFirst */
2610     newLine = addLine(new, config->cfi, LT_HYPER,
2611     config->secondaryIndent,
2612     newMBKernel + strlen(prefix));
2613     needs &= ~NEED_MB;
2614     }
2615     } else if (needs & NEED_KERNEL) {
2616     newLine = addLineTmpl(new, tmplLine, newLine,
2617     newKernelPath + strlen(prefix), config->cfi);
2618     needs &= ~NEED_KERNEL;
2619     }
2620 niro 532
2621 niro 914 } else if (tmplLine->type == LT_HYPER &&
2622     tmplLine->numElements >= 2) {
2623     if (needs & NEED_MB) {
2624     newLine = addLineTmpl(new, tmplLine, newLine,
2625     newMBKernel + strlen(prefix), config->cfi);
2626     needs &= ~NEED_MB;
2627     }
2628 niro 532
2629 niro 914 } else if (tmplLine->type == LT_MBMODULE &&
2630     tmplLine->numElements >= 2) {
2631     if (new->multiboot) {
2632     if (needs & NEED_KERNEL) {
2633     newLine = addLineTmpl(new, tmplLine, newLine,
2634     newKernelPath +
2635     strlen(prefix), config->cfi);
2636     needs &= ~NEED_KERNEL;
2637     } else if (config->cfi->mbInitRdIsModule &&
2638     (needs & NEED_INITRD)) {
2639     char *initrdVal;
2640     initrdVal = getInitrdVal(config, prefix, tmplLine,
2641     newKernelInitrd, extraInitrds,
2642     extraInitrdCount);
2643     newLine = addLineTmpl(new, tmplLine, newLine,
2644     initrdVal, config->cfi);
2645     free(initrdVal);
2646     needs &= ~NEED_INITRD;
2647     }
2648     } else if (needs & NEED_KERNEL) {
2649     /* template is multi but new is not,
2650     * insert the kernel in the first module slot
2651     */
2652     tmplLine->type = LT_KERNEL;
2653     free(tmplLine->elements[0].item);
2654     tmplLine->elements[0].item =
2655     strdup(getKeywordByType(LT_KERNEL, config->cfi)->key);
2656     newLine = addLineTmpl(new, tmplLine, newLine,
2657     newKernelPath + strlen(prefix), config->cfi);
2658     needs &= ~NEED_KERNEL;
2659     } else if (needs & NEED_INITRD) {
2660     char *initrdVal;
2661     /* template is multi but new is not,
2662     * insert the initrd in the second module slot
2663     */
2664     tmplLine->type = LT_INITRD;
2665     free(tmplLine->elements[0].item);
2666     tmplLine->elements[0].item =
2667     strdup(getKeywordByType(LT_INITRD, config->cfi)->key);
2668     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
2669     newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
2670     free(initrdVal);
2671     needs &= ~NEED_INITRD;
2672     }
2673 niro 532
2674     } else if (tmplLine->type == LT_INITRD &&
2675 niro 914 tmplLine->numElements >= 2) {
2676     if (needs & NEED_INITRD &&
2677     new->multiboot && !template->multiboot &&
2678     config->cfi->mbInitRdIsModule) {
2679     /* make sure we don't insert the module initrd
2680     * before the module kernel... if we don't do it here,
2681     * it will be inserted following the template.
2682     */
2683     if (!needs & NEED_KERNEL) {
2684     char *initrdVal;
2685    
2686     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
2687     newLine = addLine(new, config->cfi, LT_MBMODULE,
2688     config->secondaryIndent,
2689     initrdVal);
2690     free(initrdVal);
2691     needs &= ~NEED_INITRD;
2692     }
2693     } else if (needs & NEED_INITRD) {
2694     char *initrdVal;
2695     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
2696     newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
2697     free(initrdVal);
2698     needs &= ~NEED_INITRD;
2699 niro 532 }
2700    
2701     } else if (tmplLine->type == LT_TITLE &&
2702 niro 914 (needs & NEED_TITLE)) {
2703     if (tmplLine->numElements >= 2) {
2704     newLine = addLineTmpl(new, tmplLine, newLine,
2705     newKernelTitle, config->cfi);
2706     needs &= ~NEED_TITLE;
2707     } else if (tmplLine->numElements == 1 &&
2708     config->cfi->titleBracketed) {
2709     /* addLineTmpl doesn't handle titleBracketed */
2710     newLine = addLine(new, config->cfi, LT_TITLE,
2711     tmplLine->indent, newKernelTitle);
2712     needs &= ~NEED_TITLE;
2713     }
2714 niro 532
2715 niro 914 } else {
2716     /* pass through other lines from the template */
2717     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
2718     }
2719 niro 532 }
2720 niro 914
2721 niro 532 } else {
2722 niro 914 /* don't have a template, so start the entry with the
2723     * appropriate starting line
2724     */
2725     switch (config->cfi->entrySeparator) {
2726     case LT_KERNEL:
2727     if (new->multiboot && config->cfi->mbHyperFirst) {
2728     /* fall through to LT_HYPER */
2729     } else {
2730     newLine = addLine(new, config->cfi, LT_KERNEL,
2731     config->primaryIndent,
2732     newKernelPath + strlen(prefix));
2733     needs &= ~NEED_KERNEL;
2734     break;
2735     }
2736    
2737     case LT_HYPER:
2738     newLine = addLine(new, config->cfi, LT_HYPER,
2739     config->primaryIndent,
2740     newMBKernel + strlen(prefix));
2741     needs &= ~NEED_MB;
2742 niro 532 break;
2743    
2744 niro 914 case LT_TITLE:
2745     if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
2746     char * templabel;
2747     int x = 0, y = 0;
2748    
2749     templabel = strdup(newKernelTitle);
2750     while( templabel[x]){
2751     if( templabel[x] == ' ' ){
2752     y = x;
2753     while( templabel[y] ){
2754     templabel[y] = templabel[y+1];
2755     y++;
2756     }
2757     }
2758     x++;
2759     }
2760     newLine = addLine(new, config->cfi, LT_TITLE,
2761     config->primaryIndent, templabel);
2762     free(templabel);
2763     }else{
2764     newLine = addLine(new, config->cfi, LT_TITLE,
2765     config->primaryIndent, newKernelTitle);
2766     }
2767     needs &= ~NEED_TITLE;
2768     break;
2769    
2770     default:
2771     abort();
2772 niro 532 }
2773     }
2774    
2775 niro 914 /* add the remainder of the lines, i.e. those that either
2776     * weren't present in the template, or in the case of no template,
2777     * all the lines following the entrySeparator.
2778     */
2779     if (needs & NEED_TITLE) {
2780     newLine = addLine(new, config->cfi, LT_TITLE,
2781     config->secondaryIndent,
2782     newKernelTitle);
2783     needs &= ~NEED_TITLE;
2784 niro 532 }
2785 niro 914 if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
2786     newLine = addLine(new, config->cfi, LT_HYPER,
2787     config->secondaryIndent,
2788     newMBKernel + strlen(prefix));
2789     needs &= ~NEED_MB;
2790     }
2791     if (needs & NEED_KERNEL) {
2792     newLine = addLine(new, config->cfi,
2793     (new->multiboot && getKeywordByType(LT_MBMODULE,
2794     config->cfi)) ?
2795     LT_MBMODULE : LT_KERNEL,
2796     config->secondaryIndent,
2797     newKernelPath + strlen(prefix));
2798     needs &= ~NEED_KERNEL;
2799     }
2800     if (needs & NEED_MB) {
2801     newLine = addLine(new, config->cfi, LT_HYPER,
2802     config->secondaryIndent,
2803     newMBKernel + strlen(prefix));
2804     needs &= ~NEED_MB;
2805     }
2806     if (needs & NEED_INITRD) {
2807     char *initrdVal;
2808     initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
2809     newLine = addLine(new, config->cfi,
2810     (new->multiboot && getKeywordByType(LT_MBMODULE,
2811     config->cfi)) ?
2812     LT_MBMODULE : LT_INITRD,
2813     config->secondaryIndent,
2814     initrdVal);
2815     free(initrdVal);
2816     needs &= ~NEED_INITRD;
2817     }
2818 niro 532
2819 niro 914 if (needs) {
2820     printf(_("grubby: needs=%d, aborting\n"), needs);
2821     abort();
2822     }
2823    
2824 niro 532 if (updateImage(config, "0", prefix, newKernelArgs, NULL,
2825     newMBKernelArgs, NULL)) return 1;
2826    
2827     return 0;
2828     }
2829    
2830 niro 914 static void traceback(int signum)
2831     {
2832     void *array[40];
2833     size_t size;
2834    
2835     signal(SIGSEGV, SIG_DFL);
2836     memset(array, '\0', sizeof (array));
2837     size = backtrace(array, 40);
2838    
2839     fprintf(stderr, "grubby recieved SIGSEGV! Backtrace (%ld):\n",
2840     (unsigned long)size);
2841     backtrace_symbols_fd(array, size, STDERR_FILENO);
2842     exit(1);
2843     }
2844    
2845 niro 532 int main(int argc, const char ** argv) {
2846     poptContext optCon;
2847     char * grubConfig = NULL;
2848     char * outputFile = NULL;
2849     int arg = 0;
2850     int flags = 0;
2851     int badImageOkay = 0;
2852     int configureLilo = 0, configureELilo = 0, configureGrub = 0;
2853     int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
2854 niro 914 int configureExtLinux = 0;
2855 niro 532 int bootloaderProbe = 0;
2856 niro 914 int extraInitrdCount = 0;
2857 niro 532 char * updateKernelPath = NULL;
2858     char * newKernelPath = NULL;
2859     char * removeKernelPath = NULL;
2860     char * newKernelArgs = NULL;
2861     char * newKernelInitrd = NULL;
2862     char * newKernelTitle = NULL;
2863     char * newKernelVersion = NULL;
2864     char * newMBKernel = NULL;
2865     char * newMBKernelArgs = NULL;
2866     char * removeMBKernelArgs = NULL;
2867     char * removeMBKernel = NULL;
2868     char * bootPrefix = NULL;
2869     char * defaultKernel = NULL;
2870     char * removeArgs = NULL;
2871     char * kernelInfo = NULL;
2872 niro 914 char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
2873 niro 532 const char * chptr = NULL;
2874     struct configFileInfo * cfi = NULL;
2875     struct grubConfig * config;
2876     struct singleEntry * template = NULL;
2877     int copyDefault = 0, makeDefault = 0;
2878     int displayDefault = 0;
2879     struct poptOption options[] = {
2880     { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
2881     _("add an entry for the specified kernel"), _("kernel-path") },
2882     { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
2883     _("add an entry for the specified multiboot kernel"), NULL },
2884     { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
2885     _("default arguments for the new kernel or new arguments for "
2886     "kernel being updated"), _("args") },
2887     { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
2888     _("default arguments for the new multiboot kernel or "
2889     "new arguments for multiboot kernel being updated"), NULL },
2890     { "bad-image-okay", 0, 0, &badImageOkay, 0,
2891     _("don't sanity check images in boot entries (for testing only)"),
2892     NULL },
2893     { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
2894     _("filestystem which contains /boot directory (for testing only)"),
2895     _("bootfs") },
2896     #if defined(__i386__) || defined(__x86_64__)
2897     { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
2898     _("check if lilo is installed on lilo.conf boot sector") },
2899     #endif
2900     { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
2901     _("path to grub config file to update (\"-\" for stdin)"),
2902     _("path") },
2903     { "copy-default", 0, 0, &copyDefault, 0,
2904     _("use the default boot entry as a template for the new entry "
2905     "being added; if the default is not a linux image, or if "
2906     "the kernel referenced by the default image does not exist, "
2907     "the first linux entry whose kernel does exist is used as the "
2908     "template"), NULL },
2909     { "default-kernel", 0, 0, &displayDefault, 0,
2910     _("display the path of the default kernel") },
2911     { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
2912     _("configure elilo bootloader") },
2913 niro 914 { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
2914     _("configure extlinux bootloader (from syslinux)") },
2915 niro 532 { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
2916     _("configure grub bootloader") },
2917     { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
2918     _("display boot information for specified kernel"),
2919     _("kernel-path") },
2920     { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
2921     _("initrd image for the new kernel"), _("initrd-path") },
2922 niro 914 { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
2923     _("auxilliary initrd image for things other than the new kernel"), _("initrd-path") },
2924 niro 532 { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
2925     _("configure lilo bootloader") },
2926     { "make-default", 0, 0, &makeDefault, 0,
2927     _("make the newly added entry the default boot entry"), NULL },
2928     { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
2929     _("path to output updated config file (\"-\" for stdout)"),
2930     _("path") },
2931     { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
2932     _("remove kernel arguments"), NULL },
2933     { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
2934     _("remove multiboot kernel arguments"), NULL },
2935     { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
2936     _("remove all entries for the specified kernel"),
2937     _("kernel-path") },
2938     { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
2939     _("remove all entries for the specified multiboot kernel"), NULL },
2940     { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
2941     _("make the first entry referencing the specified kernel "
2942     "the default"), _("kernel-path") },
2943     { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
2944     _("configure silo bootloader") },
2945     { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
2946     _("title to use for the new kernel entry"), _("entry-title") },
2947     { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
2948     _("updated information for the specified kernel"),
2949     _("kernel-path") },
2950     { "version", 'v', 0, NULL, 'v',
2951     _("print the version of this program and exit"), NULL },
2952     { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
2953     _("configure yaboot bootloader") },
2954     { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
2955     _("configure zipl bootloader") },
2956     POPT_AUTOHELP
2957     { 0, 0, 0, 0, 0 }
2958     };
2959    
2960 niro 914 useextlinuxmenu=0;
2961    
2962     signal(SIGSEGV, traceback);
2963    
2964 niro 532 optCon = poptGetContext("grubby", argc, argv, options, 0);
2965     poptReadDefaultConfig(optCon, 1);
2966    
2967     while ((arg = poptGetNextOpt(optCon)) >= 0) {
2968     switch (arg) {
2969     case 'v':
2970     printf("grubby version %s\n", VERSION);
2971     exit(0);
2972     break;
2973 niro 914 case 'i':
2974     if (extraInitrdCount < MAX_EXTRA_INITRDS) {
2975     extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
2976     } else {
2977     fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
2978     return 1;
2979     }
2980     break;
2981 niro 532 }
2982     }
2983    
2984     if (arg < -1) {
2985     fprintf(stderr, _("grubby: bad argument %s: %s\n"),
2986     poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
2987     poptStrerror(arg));
2988     return 1;
2989     }
2990    
2991     if ((chptr = poptGetArg(optCon))) {
2992     fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
2993     return 1;
2994     }
2995    
2996     if ((configureLilo + configureGrub + configureELilo +
2997 niro 914 configureYaboot + configureSilo + configureZipl +
2998     configureExtLinux ) > 1) {
2999 niro 532 fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
3000     return 1;
3001     } else if (bootloaderProbe && grubConfig) {
3002     fprintf(stderr,
3003     _("grubby: cannot specify config file with --bootloader-probe\n"));
3004     return 1;
3005     } else if (configureLilo) {
3006     cfi = &liloConfigType;
3007     } else if (configureGrub) {
3008     cfi = &grubConfigType;
3009     } else if (configureELilo) {
3010     cfi = &eliloConfigType;
3011     } else if (configureYaboot) {
3012     cfi = &yabootConfigType;
3013     } else if (configureSilo) {
3014     cfi = &siloConfigType;
3015     } else if (configureZipl) {
3016     cfi = &ziplConfigType;
3017 niro 914 } else if (configureExtLinux) {
3018     cfi = &extlinuxConfigType;
3019     useextlinuxmenu=1;
3020 niro 532 }
3021    
3022     if (!cfi) {
3023     #ifdef __ia64__
3024     cfi = &eliloConfigType;
3025     #elif __powerpc__
3026     cfi = &yabootConfigType;
3027     #elif __sparc__
3028     cfi = &siloConfigType;
3029     #elif __s390__
3030     cfi = &ziplConfigType;
3031     #elif __s390x__
3032     cfi = &ziplConfigtype;
3033     #else
3034     cfi = &grubConfigType;
3035     #endif
3036     }
3037    
3038     if (!grubConfig)
3039     grubConfig = cfi->defaultConfig;
3040    
3041     if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||
3042     newKernelPath || removeKernelPath || makeDefault ||
3043     defaultKernel)) {
3044     fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
3045     "specified option"));
3046     return 1;
3047     }
3048    
3049     if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||
3050     removeKernelPath)) {
3051     fprintf(stderr, _("grubby: --default-kernel and --info may not "
3052     "be used when adding or removing kernels\n"));
3053     return 1;
3054     }
3055    
3056     if (newKernelPath && !newKernelTitle) {
3057     fprintf(stderr, _("grubby: kernel title must be specified\n"));
3058     return 1;
3059 niro 1156 } else if (!newKernelPath && (newKernelTitle || copyDefault ||
3060     (newKernelInitrd && !updateKernelPath)||
3061 niro 914 makeDefault || extraInitrdCount > 0)) {
3062 niro 532 fprintf(stderr, _("grubby: kernel path expected\n"));
3063     return 1;
3064     }
3065    
3066     if (newKernelPath && updateKernelPath) {
3067     fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
3068     "not be used together"));
3069     return 1;
3070     }
3071    
3072     if (makeDefault && defaultKernel) {
3073     fprintf(stderr, _("grubby: --make-default and --default-kernel "
3074     "may not be used together\n"));
3075     return 1;
3076     } else if (defaultKernel && removeKernelPath &&
3077     !strcmp(defaultKernel, removeKernelPath)) {
3078     fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));
3079     return 1;
3080     } else if (defaultKernel && newKernelPath &&
3081     !strcmp(defaultKernel, newKernelPath)) {
3082     makeDefault = 1;
3083     defaultKernel = NULL;
3084     }
3085    
3086     if (!strcmp(grubConfig, "-") && !outputFile) {
3087     fprintf(stderr, _("grubby: output file must be specified if stdin "
3088     "is used\n"));
3089     return 1;
3090     }
3091    
3092     if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
3093     && !kernelInfo && !bootloaderProbe && !updateKernelPath
3094     && !removeMBKernel) {
3095     fprintf(stderr, _("grubby: no action specified\n"));
3096     return 1;
3097     }
3098    
3099     flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
3100    
3101     if (cfi->needsBootPrefix) {
3102     if (!bootPrefix) {
3103     bootPrefix = findBootPrefix();
3104     if (!bootPrefix) return 1;
3105     } else {
3106     /* this shouldn't end with a / */
3107     if (bootPrefix[strlen(bootPrefix) - 1] == '/')
3108     bootPrefix[strlen(bootPrefix) - 1] = '\0';
3109     }
3110     } else {
3111     bootPrefix = "";
3112     }
3113    
3114 niro 914 if (!cfi->mbAllowExtraInitRds &&
3115     extraInitrdCount > 0) {
3116     fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
3117     return 1;
3118     }
3119    
3120 niro 532 if (bootloaderProbe) {
3121 niro 914 int lrc = 0, grc = 0, erc = 0;
3122 niro 532 struct grubConfig * lconfig, * gconfig;
3123    
3124     if (!access(grubConfigType.defaultConfig, F_OK)) {
3125     gconfig = readConfig(grubConfigType.defaultConfig, &grubConfigType);
3126     if (!gconfig)
3127     grc = 1;
3128     else
3129     grc = checkForGrub(gconfig);
3130     }
3131    
3132     if (!access(liloConfigType.defaultConfig, F_OK)) {
3133     lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);
3134     if (!lconfig)
3135     lrc = 1;
3136     else
3137     lrc = checkForLilo(lconfig);
3138     }
3139    
3140 niro 914 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
3141     lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
3142     if (!lconfig)
3143     erc = 1;
3144     else
3145     erc = checkForExtLinux(lconfig);
3146     }
3147    
3148 niro 532 if (lrc == 1 || grc == 1) return 1;
3149    
3150     if (lrc == 2) printf("lilo\n");
3151     if (grc == 2) printf("grub\n");
3152 niro 914 if (erc == 2) printf("extlinux\n");
3153 niro 532
3154     return 0;
3155     }
3156    
3157     config = readConfig(grubConfig, cfi);
3158     if (!config) return 1;
3159    
3160     if (displayDefault) {
3161     struct singleLine * line;
3162     struct singleEntry * entry;
3163     char * rootspec;
3164    
3165     if (config->defaultImage == -1) return 0;
3166     entry = findEntryByIndex(config, config->defaultImage);
3167     if (!entry) return 0;
3168     if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
3169    
3170 niro 914 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
3171 niro 532 if (!line) return 0;
3172    
3173     rootspec = getRootSpecifier(line->elements[1].item);
3174     printf("%s%s\n", bootPrefix, line->elements[1].item +
3175     ((rootspec != NULL) ? strlen(rootspec) : 0));
3176    
3177     return 0;
3178     } else if (kernelInfo)
3179     return displayInfo(config, kernelInfo, bootPrefix);
3180    
3181     if (copyDefault) {
3182     template = findTemplate(config, bootPrefix, NULL, 0, flags);
3183     if (!template) return 1;
3184     }
3185    
3186     markRemovedImage(config, removeKernelPath, bootPrefix);
3187     markRemovedImage(config, removeMBKernel, bootPrefix);
3188     setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
3189     bootPrefix, flags);
3190     setFallbackImage(config, newKernelPath != NULL);
3191     if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
3192     removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
3193 niro 1156 if (updateKernelPath && newKernelInitrd) {
3194     if (updateInitrd(config, updateKernelPath, bootPrefix,
3195     newKernelInitrd)) return 1;
3196     }
3197 niro 532 if (addNewKernel(config, template, bootPrefix, newKernelPath,
3198     newKernelTitle, newKernelArgs, newKernelInitrd,
3199 niro 914 extraInitrds, extraInitrdCount,
3200 niro 532 newMBKernel, newMBKernelArgs)) return 1;
3201    
3202    
3203     if (numEntries(config) == 0) {
3204     fprintf(stderr, _("grubby: doing this would leave no kernel entries. "
3205     "Not writing out new config.\n"));
3206     return 1;
3207     }
3208    
3209     if (!outputFile)
3210     outputFile = grubConfig;
3211    
3212     return writeConfig(config, outputFile, bootPrefix);
3213     }