Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1720 - (hide annotations) (download)
Sat Feb 18 00:51:28 2012 UTC (12 years, 2 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 99504 byte(s)
Add new '--default-index' feature.

This displays the (numeric) index of the default entry directly
from the parsed configuration file.

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