Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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