Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1696 - (hide annotations) (download)
Fri Feb 17 23:46:24 2012 UTC (12 years, 2 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 97866 byte(s)
Add grub2.cfg support to grubby.


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