Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1717 - (hide annotations) (download)
Sat Feb 18 00:47:17 2012 UTC (12 years, 2 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 98722 byte(s)
    Don't crash if grubConfig not found.
    
    I trigger this crash on my system. It has syslinux and no grub.
    
    Signed-off-by: Christopher Li <kernel@chrisli.org>


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