Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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