Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1693 - (hide annotations) (download)
Fri Feb 17 23:21:08 2012 UTC (12 years, 2 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 88513 byte(s)
Rename entrySeparator to entryStart.
    
Since we're actually using it to detect the start of an entry (and
specifically not the end), name this slightly better.


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