Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1694 - (hide annotations) (download)
Fri Feb 17 23:23:07 2012 UTC (12 years, 2 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 88534 byte(s)
Tweak some make options.

Some functions are only used in dbgPrintf() arguments. As such,
errors/warnings on unused functions need to be disabled.

Also, use -std=gnu99.

Also, add a debug target.


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