Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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