Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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