Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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