Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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