Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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