Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1714 - (hide annotations) (download)
Sat Feb 18 00:32:14 2012 UTC (12 years, 2 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 98209 byte(s)
    Make grubby to recognize Ubuntu's spin of Grub2
    
    We are looking to use grubby as a bootloader entries
    manager for the autotest project:
    
    http://autotest.kernel.org/
    
    The project aims to cover more distros than just
    red hat based ones, so this small patch fixes the
    tool under Ubuntu. They have chosen to name grub2
    grub, in all directory references. Also, they don't
    make symlinks of grub config files on /etc. So,
    adapt the code to take into consideration this
    behavior.
    
    Changes from v1:
      * Removed nested set of {}
      * Made the return in case /etc/grub.d is found
        indented
      * Moved the dbgPrint("not found\n") statement
        before the last return. Also, put a new
        dbgPrint("found\n") if we've found a suitable
        grub2 config file ubuntu/debian style.
    
    Signed-off-by: Lucas Meneghel Rodrigues <lmr@redhat.com>


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