Magellan Linux

Annotation of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1751 - (hide annotations) (download)
Sat Feb 18 01:10:10 2012 UTC (12 years, 8 months ago) by niro
File MIME type: text/plain
File size: 105142 byte(s)
Try to display title when printing information with '--info'

Signed-off-by: Cleber Rosa <crosa@redhat.com>


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