Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1931 - (hide annotations) (download)
Mon Oct 1 12:06:26 2012 UTC (11 years, 7 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 116707 byte(s)
Handle linuxefi and initrdefi lines correctly in grub2.
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     rc = chdir(dirname(dir));
1317     free(dir);
1318 niro 532 do {
1319     buf = alloca(len + 1);
1320 niro 914 rc = readlink(basename(outName), buf, len);
1321 niro 532 if (rc == len) len += 256;
1322     } while (rc == len);
1323    
1324     if (rc < 0) {
1325     fprintf(stderr, _("grubby: error readlink link %s: %s\n"),
1326     outName, strerror(errno));
1327     return 1;
1328     }
1329    
1330     outName = buf;
1331     outName[rc] = '\0';
1332     }
1333    
1334     tmpOutName = alloca(strlen(outName) + 2);
1335     sprintf(tmpOutName, "%s-", outName);
1336     out = fopen(tmpOutName, "w");
1337     if (!out) {
1338     fprintf(stderr, _("grubby: error creating %s: %s\n"), tmpOutName,
1339     strerror(errno));
1340     return 1;
1341     }
1342    
1343     if (!stat(outName, &sb)) {
1344     if (chmod(tmpOutName, sb.st_mode & ~(S_IFMT))) {
1345     fprintf(stderr, _("grubby: error setting perms on %s: %s\n"),
1346     tmpOutName, strerror(errno));
1347     fclose(out);
1348     unlink(tmpOutName);
1349     return 1;
1350     }
1351     }
1352     }
1353    
1354     line = cfg->theLines;
1355 niro 1696 struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi);
1356 niro 532 while (line) {
1357 niro 1696 if (line->type == LT_SET_VARIABLE && defaultKw &&
1358     line->numElements == 3 &&
1359     !strcmp(line->elements[1].item, defaultKw->key)) {
1360 niro 532 writeDefault(out, line->indent, line->elements[0].indent, cfg);
1361     needs &= ~MAIN_DEFAULT;
1362 niro 1696 } else if (line->type == LT_DEFAULT) {
1363     writeDefault(out, line->indent, line->elements[0].indent, cfg);
1364     needs &= ~MAIN_DEFAULT;
1365 niro 532 } else if (line->type == LT_FALLBACK) {
1366     if (cfg->fallbackImage > -1)
1367     fprintf(out, "%s%s%s%d\n", line->indent,
1368     line->elements[0].item, line->elements[0].indent,
1369     cfg->fallbackImage);
1370     } else {
1371     if (lineWrite(out, line, cfg->cfi) == -1) {
1372     fprintf(stderr, _("grubby: error writing %s: %s\n"),
1373     tmpOutName, strerror(errno));
1374     fclose(out);
1375     unlink(tmpOutName);
1376     return 1;
1377     }
1378     }
1379    
1380     line = line->next;
1381     }
1382    
1383     if (needs & MAIN_DEFAULT) {
1384     writeDefault(out, cfg->primaryIndent, "=", cfg);
1385     needs &= ~MAIN_DEFAULT;
1386     }
1387    
1388     i = 0;
1389     while ((entry = findEntryByIndex(cfg, i++))) {
1390     if (entry->skip) continue;
1391    
1392     line = entry->lines;
1393     while (line) {
1394     if (lineWrite(out, line, cfg->cfi) == -1) {
1395     fprintf(stderr, _("grubby: error writing %s: %s\n"),
1396     tmpOutName, strerror(errno));
1397     fclose(out);
1398     unlink(tmpOutName);
1399     return 1;
1400     }
1401     line = line->next;
1402     }
1403     }
1404    
1405     if (tmpOutName) {
1406     if (rename(tmpOutName, outName)) {
1407     fprintf(stderr, _("grubby: error moving %s to %s: %s\n"),
1408     tmpOutName, outName, strerror(errno));
1409     unlink(outName);
1410     return 1;
1411     }
1412     }
1413    
1414     return 0;
1415     }
1416    
1417     static int numEntries(struct grubConfig *cfg) {
1418     int i = 0;
1419     struct singleEntry * entry;
1420    
1421     entry = cfg->entries;
1422     while (entry) {
1423     if (!entry->skip)
1424     i++;
1425     entry = entry->next;
1426     }
1427     return i;
1428     }
1429    
1430 niro 1156 static char *findDiskForRoot()
1431     {
1432     int fd;
1433     char buf[65536];
1434     char *devname;
1435     char *chptr;
1436     int rc;
1437    
1438     if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {
1439     fprintf(stderr, "grubby: failed to open %s: %s\n",
1440     _PATH_MOUNTED, strerror(errno));
1441     return NULL;
1442     }
1443    
1444     rc = read(fd, buf, sizeof(buf) - 1);
1445     if (rc <= 0) {
1446     fprintf(stderr, "grubby: failed to read %s: %s\n",
1447     _PATH_MOUNTED, strerror(errno));
1448     close(fd);
1449     return NULL;
1450     }
1451     close(fd);
1452     buf[rc] = '\0';
1453     chptr = buf;
1454    
1455 niro 1841 char *foundanswer = NULL;
1456    
1457 niro 1156 while (chptr && chptr != buf+rc) {
1458     devname = chptr;
1459    
1460     /*
1461     * The first column of a mtab entry is the device, but if the entry is a
1462     * special device it won't start with /, so move on to the next line.
1463     */
1464     if (*devname != '/') {
1465     chptr = strchr(chptr, '\n');
1466     if (chptr)
1467     chptr++;
1468     continue;
1469     }
1470    
1471     /* Seek to the next space */
1472     chptr = strchr(chptr, ' ');
1473     if (!chptr) {
1474     fprintf(stderr, "grubby: error parsing %s: %s\n",
1475     _PATH_MOUNTED, strerror(errno));
1476     return NULL;
1477     }
1478    
1479     /*
1480     * The second column of a mtab entry is the mount point, we are looking
1481     * for '/' obviously.
1482     */
1483     if (*(++chptr) == '/' && *(++chptr) == ' ') {
1484 niro 1841 /* remember the last / entry in mtab */
1485     foundanswer = devname;
1486 niro 1156 }
1487    
1488     /* Next line */
1489     chptr = strchr(chptr, '\n');
1490     if (chptr)
1491     chptr++;
1492     }
1493    
1494 niro 1841 /* Return the last / entry found */
1495     if (foundanswer) {
1496     chptr = strchr(foundanswer, ' ');
1497     *chptr = '\0';
1498     return strdup(foundanswer);
1499     }
1500    
1501 niro 1156 return NULL;
1502     }
1503    
1504 niro 1736 void printEntry(struct singleEntry * entry) {
1505     int i;
1506     struct singleLine * line;
1507    
1508     for (line = entry->lines; line; line = line->next) {
1509     fprintf(stderr, "DBG: %s", line->indent);
1510     for (i = 0; i < line->numElements; i++) {
1511 niro 1801 /* Need to handle this, because we strip the quotes from
1512     * menuentry when read it. */
1513     if (line->type == LT_MENUENTRY && i == 1) {
1514     if(!isquote(*line->elements[i].item))
1515     fprintf(stderr, "\'%s\'", line->elements[i].item);
1516     else
1517     fprintf(stderr, "%s", line->elements[i].item);
1518     fprintf(stderr, "%s", line->elements[i].indent);
1519    
1520     continue;
1521     }
1522    
1523     fprintf(stderr, "%s%s",
1524 niro 1736 line->elements[i].item, line->elements[i].indent);
1525     }
1526     fprintf(stderr, "\n");
1527     }
1528     }
1529    
1530     void notSuitablePrintf(struct singleEntry * entry, const char *fmt, ...)
1531     {
1532     va_list argp;
1533    
1534     if (!debug)
1535     return;
1536    
1537     va_start(argp, fmt);
1538     fprintf(stderr, "DBG: Image entry failed: ");
1539     vfprintf(stderr, fmt, argp);
1540     printEntry(entry);
1541     va_end(argp);
1542     }
1543    
1544 niro 1745 #define beginswith(s, c) ((s) && (s)[0] == (c))
1545    
1546     static int endswith(const char *s, char c)
1547     {
1548     int slen;
1549    
1550 niro 1750 if (!s || !s[0])
1551 niro 1745 return 0;
1552     slen = strlen(s) - 1;
1553    
1554     return s[slen] == c;
1555     }
1556    
1557 niro 532 int suitableImage(struct singleEntry * entry, const char * bootPrefix,
1558     int skipRemoved, int flags) {
1559     struct singleLine * line;
1560     char * fullName;
1561     int i;
1562     char * dev;
1563     char * rootspec;
1564 niro 1156 char * rootdev;
1565 niro 532
1566 niro 1736 if (skipRemoved && entry->skip) {
1567     notSuitablePrintf(entry, "marked to skip\n");
1568     return 0;
1569     }
1570 niro 532
1571 niro 914 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1572 niro 1736 if (!line) {
1573     notSuitablePrintf(entry, "no line found\n");
1574     return 0;
1575     }
1576     if (line->numElements < 2) {
1577     notSuitablePrintf(entry, "line has only %d elements\n",
1578     line->numElements);
1579     return 0;
1580     }
1581 niro 914
1582 niro 532 if (flags & GRUBBY_BADIMAGE_OKAY) return 1;
1583    
1584     fullName = alloca(strlen(bootPrefix) +
1585     strlen(line->elements[1].item) + 1);
1586     rootspec = getRootSpecifier(line->elements[1].item);
1587 niro 1745 int rootspec_offset = rootspec ? strlen(rootspec) : 0;
1588     int hasslash = endswith(bootPrefix, '/') ||
1589     beginswith(line->elements[1].item + rootspec_offset, '/');
1590     sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/",
1591     line->elements[1].item + rootspec_offset);
1592 niro 1736 if (access(fullName, R_OK)) {
1593     notSuitablePrintf(entry, "access to %s failed\n", fullName);
1594     return 0;
1595     }
1596 niro 532 for (i = 2; i < line->numElements; i++)
1597     if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1598     if (i < line->numElements) {
1599     dev = line->elements[i].item + 5;
1600     } else {
1601     /* look for a lilo style LT_ROOT line */
1602 niro 914 line = getLineByType(LT_ROOT, entry->lines);
1603 niro 532
1604     if (line && line->numElements >= 2) {
1605     dev = line->elements[1].item;
1606     } else {
1607 niro 914 /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS.
1608     * grub+multiboot uses LT_MBMODULE for the args, so check that too.
1609     */
1610     line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines);
1611 niro 532
1612     /* failed to find one */
1613 niro 1736 if (!line) {
1614     notSuitablePrintf(entry, "no line found\n");
1615     return 0;
1616     }
1617 niro 532
1618     for (i = 1; i < line->numElements; i++)
1619     if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1620     if (i < line->numElements)
1621     dev = line->elements[i].item + 5;
1622     else {
1623 niro 1736 notSuitablePrintf(entry, "no root= entry found\n");
1624 niro 532 /* it failed too... can't find root= */
1625     return 0;
1626     }
1627     }
1628     }
1629    
1630 niro 914 dev = getpathbyspec(dev);
1631 niro 1736 if (!getpathbyspec(dev)) {
1632     notSuitablePrintf(entry, "can't find blkid entry for %s\n", dev);
1633 niro 914 return 0;
1634 niro 1736 } else
1635     dev = getpathbyspec(dev);
1636 niro 532
1637 niro 1156 rootdev = findDiskForRoot();
1638 niro 1736 if (!rootdev) {
1639     notSuitablePrintf(entry, "can't find root device\n");
1640 niro 914 return 0;
1641 niro 1736 }
1642 niro 914
1643 niro 1177 if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {
1644 niro 1736 notSuitablePrintf(entry, "uuid missing: rootdev %s, dev %s\n",
1645     getuuidbydev(rootdev), getuuidbydev(dev));
1646 niro 1177 free(rootdev);
1647     return 0;
1648     }
1649 niro 532
1650 niro 1156 if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
1651 niro 1736 notSuitablePrintf(entry, "uuid mismatch: rootdev %s, dev %s\n",
1652     getuuidbydev(rootdev), getuuidbydev(dev));
1653 niro 1156 free(rootdev);
1654 niro 914 return 0;
1655 niro 1156 }
1656 niro 532
1657 niro 1156 free(rootdev);
1658    
1659 niro 532 return 1;
1660     }
1661    
1662     /* returns the first match on or after the one pointed to by index (if index
1663     is not NULL) which is not marked as skip */
1664     struct singleEntry * findEntryByPath(struct grubConfig * config,
1665     const char * kernel, const char * prefix,
1666     int * index) {
1667     struct singleEntry * entry = NULL;
1668     struct singleLine * line;
1669     int i;
1670     char * chptr;
1671     char * rootspec = NULL;
1672     enum lineType_e checkType = LT_KERNEL;
1673    
1674     if (isdigit(*kernel)) {
1675     int * indexVars = alloca(sizeof(*indexVars) * strlen(kernel));
1676    
1677     i = 0;
1678     indexVars[i] = strtol(kernel, &chptr, 10);
1679     while (*chptr == ',') {
1680     i++;
1681     kernel = chptr + 1;
1682     indexVars[i] = strtol(kernel, &chptr, 10);
1683     }
1684    
1685     if (*chptr) {
1686     /* can't parse it, bail */
1687     return NULL;
1688     }
1689    
1690     indexVars[i + 1] = -1;
1691    
1692     i = 0;
1693     if (index) {
1694     while (i < *index) i++;
1695     if (indexVars[i] == -1) return NULL;
1696     }
1697    
1698     entry = findEntryByIndex(config, indexVars[i]);
1699     if (!entry) return NULL;
1700    
1701 niro 914 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1702 niro 532 if (!line) return NULL;
1703    
1704     if (index) *index = indexVars[i];
1705     return entry;
1706     }
1707    
1708     if (!strcmp(kernel, "DEFAULT")) {
1709     if (index && *index > config->defaultImage) {
1710     entry = NULL;
1711     } else {
1712     entry = findEntryByIndex(config, config->defaultImage);
1713     if (entry && entry->skip)
1714     entry = NULL;
1715     else if (index)
1716     *index = config->defaultImage;
1717     }
1718     } else if (!strcmp(kernel, "ALL")) {
1719     if (index)
1720     i = *index;
1721     else
1722     i = 0;
1723    
1724     while ((entry = findEntryByIndex(config, i))) {
1725     if (!entry->skip) break;
1726     i++;
1727     }
1728    
1729     if (entry && index)
1730     *index = i;
1731     } else {
1732     if (index)
1733     i = *index;
1734     else
1735     i = 0;
1736    
1737     if (!strncmp(kernel, "TITLE=", 6)) {
1738     prefix = "";
1739 niro 1801 checkType = LT_TITLE|LT_MENUENTRY;
1740 niro 532 kernel += 6;
1741     }
1742    
1743 niro 914 for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {
1744     if (entry->skip) continue;
1745 niro 532
1746 niro 914 dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);
1747 niro 532
1748 niro 914 /* check all the lines matching checkType */
1749     for (line = entry->lines; line; line = line->next) {
1750     line = getLineByType(entry->multiboot && checkType == LT_KERNEL ?
1751     LT_KERNEL|LT_MBMODULE|LT_HYPER :
1752     checkType, line);
1753     if (!line) break; /* not found in this entry */
1754 niro 532
1755 niro 1801 if (line && line->type != LT_MENUENTRY &&
1756     line->numElements >= 2) {
1757 niro 914 rootspec = getRootSpecifier(line->elements[1].item);
1758     if (!strcmp(line->elements[1].item +
1759     ((rootspec != NULL) ? strlen(rootspec) : 0),
1760     kernel + strlen(prefix)))
1761     break;
1762     }
1763 niro 1801 if(line->type == LT_MENUENTRY &&
1764     !strcmp(line->elements[1].item, kernel))
1765     break;
1766 niro 914 }
1767    
1768     /* make sure this entry has a kernel identifier; this skips
1769     * non-Linux boot entries (could find netbsd etc, though, which is
1770     * unfortunate)
1771     */
1772     if (line && getLineByType(LT_KERNEL|LT_HYPER, entry->lines))
1773     break; /* found 'im! */
1774 niro 532 }
1775    
1776     if (index) *index = i;
1777     }
1778    
1779     return entry;
1780     }
1781    
1782     struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index) {
1783     struct singleEntry * entry;
1784    
1785     entry = cfg->entries;
1786     while (index && entry) {
1787     entry = entry->next;
1788     index--;
1789     }
1790    
1791     return entry;
1792     }
1793    
1794     /* Find a good template to use for the new kernel. An entry is
1795     * good if the kernel and mkinitrd exist (even if the entry
1796     * is going to be removed). Try and use the default entry, but
1797     * if that doesn't work just take the first. If we can't find one,
1798     * bail. */
1799     struct singleEntry * findTemplate(struct grubConfig * cfg, const char * prefix,
1800     int * indexPtr, int skipRemoved, int flags) {
1801     struct singleEntry * entry, * entry2;
1802     int index;
1803    
1804     if (cfg->defaultImage > -1) {
1805     entry = findEntryByIndex(cfg, cfg->defaultImage);
1806     if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
1807     if (indexPtr) *indexPtr = cfg->defaultImage;
1808     return entry;
1809     }
1810     }
1811    
1812     index = 0;
1813     while ((entry = findEntryByIndex(cfg, index))) {
1814     if (suitableImage(entry, prefix, skipRemoved, flags)) {
1815     int j;
1816     for (j = 0; j < index; j++) {
1817     entry2 = findEntryByIndex(cfg, j);
1818     if (entry2->skip) index--;
1819     }
1820     if (indexPtr) *indexPtr = index;
1821    
1822     return entry;
1823     }
1824    
1825     index++;
1826     }
1827    
1828     fprintf(stderr, _("grubby fatal error: unable to find a suitable template\n"));
1829    
1830     return NULL;
1831     }
1832    
1833     char * findBootPrefix(void) {
1834     struct stat sb, sb2;
1835    
1836     stat("/", &sb);
1837     #ifdef __ia64__
1838     stat("/boot/efi/EFI/redhat/", &sb2);
1839     #else
1840     stat("/boot", &sb2);
1841     #endif
1842    
1843     if (sb.st_dev == sb2.st_dev)
1844     return strdup("");
1845    
1846     #ifdef __ia64__
1847     return strdup("/boot/efi/EFI/redhat/");
1848     #else
1849     return strdup("/boot");
1850     #endif
1851     }
1852    
1853     void markRemovedImage(struct grubConfig * cfg, const char * image,
1854     const char * prefix) {
1855     struct singleEntry * entry;
1856    
1857 niro 1801 if (!image)
1858     return;
1859 niro 532
1860 niro 1801 /* check and see if we're removing the default image */
1861     if (isdigit(*image)) {
1862     entry = findEntryByPath(cfg, image, prefix, NULL);
1863     if(entry)
1864     entry->skip = 1;
1865     return;
1866     }
1867    
1868 niro 532 while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
1869     entry->skip = 1;
1870     }
1871    
1872     void setDefaultImage(struct grubConfig * config, int hasNew,
1873     const char * defaultKernelPath, int newIsDefault,
1874 niro 1859 const char * prefix, int flags, int index) {
1875 niro 532 struct singleEntry * entry, * entry2, * newDefault;
1876     int i, j;
1877    
1878     if (newIsDefault) {
1879     config->defaultImage = 0;
1880     return;
1881 niro 1859 } else if ((index >= 0) && config->cfi->defaultIsIndex) {
1882     if (findEntryByIndex(config, index))
1883     config->defaultImage = index;
1884     else
1885     config->defaultImage = -1;
1886     return;
1887 niro 532 } else if (defaultKernelPath) {
1888     i = 0;
1889     if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
1890     config->defaultImage = i;
1891     } else {
1892     config->defaultImage = -1;
1893     return;
1894     }
1895     }
1896    
1897     /* defaultImage now points to what we'd like to use, but before any order
1898     changes */
1899 niro 1748 if ((config->defaultImage == DEFAULT_SAVED) ||
1900     (config->defaultImage == DEFAULT_SAVED_GRUB2))
1901 niro 532 /* default is set to saved, we don't want to change it */
1902     return;
1903    
1904     if (config->defaultImage > -1)
1905     entry = findEntryByIndex(config, config->defaultImage);
1906     else
1907     entry = NULL;
1908    
1909     if (entry && !entry->skip) {
1910     /* we can preserve the default */
1911     if (hasNew)
1912     config->defaultImage++;
1913    
1914     /* count the number of entries erased before this one */
1915     for (j = 0; j < config->defaultImage; j++) {
1916     entry2 = findEntryByIndex(config, j);
1917     if (entry2->skip) config->defaultImage--;
1918     }
1919     } else if (hasNew) {
1920     config->defaultImage = 0;
1921     } else {
1922     /* Either we just erased the default (or the default line was bad
1923     * to begin with) and didn't put a new one in. We'll use the first
1924     * valid image. */
1925     newDefault = findTemplate(config, prefix, &config->defaultImage, 1,
1926     flags);
1927     if (!newDefault)
1928     config->defaultImage = -1;
1929     }
1930     }
1931    
1932     void setFallbackImage(struct grubConfig * config, int hasNew) {
1933     struct singleEntry * entry, * entry2;
1934     int j;
1935    
1936     if (config->fallbackImage == -1) return;
1937    
1938     entry = findEntryByIndex(config, config->fallbackImage);
1939     if (!entry || entry->skip) {
1940     config->fallbackImage = -1;
1941     return;
1942     }
1943    
1944     if (hasNew)
1945     config->fallbackImage++;
1946    
1947     /* count the number of entries erased before this one */
1948     for (j = 0; j < config->fallbackImage; j++) {
1949     entry2 = findEntryByIndex(config, j);
1950     if (entry2->skip) config->fallbackImage--;
1951     }
1952     }
1953    
1954     void displayEntry(struct singleEntry * entry, const char * prefix, int index) {
1955     struct singleLine * line;
1956     char * root = NULL;
1957     int i;
1958    
1959     printf("index=%d\n", index);
1960    
1961 niro 914 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1962     if (!line) {
1963     printf("non linux entry\n");
1964     return;
1965     }
1966    
1967 niro 1849 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
1968     printf("kernel=%s\n", line->elements[1].item);
1969     else
1970     printf("kernel=%s%s\n", prefix, line->elements[1].item);
1971 niro 532
1972     if (line->numElements >= 3) {
1973     printf("args=\"");
1974     i = 2;
1975     while (i < line->numElements) {
1976     if (!strncmp(line->elements[i].item, "root=", 5)) {
1977     root = line->elements[i].item + 5;
1978     } else {
1979     printf("%s%s", line->elements[i].item,
1980     line->elements[i].indent);
1981     }
1982    
1983     i++;
1984     }
1985     printf("\"\n");
1986     } else {
1987 niro 914 line = getLineByType(LT_KERNELARGS, entry->lines);
1988 niro 532 if (line) {
1989     char * s;
1990    
1991     printf("args=\"");
1992     i = 1;
1993     while (i < line->numElements) {
1994     if (!strncmp(line->elements[i].item, "root=", 5)) {
1995     root = line->elements[i].item + 5;
1996     } else {
1997     s = line->elements[i].item;
1998    
1999     printf("%s%s", s, line->elements[i].indent);
2000     }
2001    
2002     i++;
2003     }
2004    
2005     s = line->elements[i - 1].indent;
2006     printf("\"\n");
2007     }
2008     }
2009    
2010     if (!root) {
2011 niro 914 line = getLineByType(LT_ROOT, entry->lines);
2012 niro 532 if (line && line->numElements >= 2)
2013     root=line->elements[1].item;
2014     }
2015    
2016     if (root) {
2017     char * s = alloca(strlen(root) + 1);
2018    
2019     strcpy(s, root);
2020     if (s[strlen(s) - 1] == '"')
2021     s[strlen(s) - 1] = '\0';
2022     /* make sure the root doesn't have a trailing " */
2023     printf("root=%s\n", s);
2024     }
2025    
2026 niro 914 line = getLineByType(LT_INITRD, entry->lines);
2027 niro 532
2028     if (line && line->numElements >= 2) {
2029 niro 1849 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2030     printf("initrd=");
2031     else
2032     printf("initrd=%s", prefix);
2033    
2034 niro 532 for (i = 1; i < line->numElements; i++)
2035     printf("%s%s", line->elements[i].item, line->elements[i].indent);
2036     printf("\n");
2037     }
2038 niro 1751
2039     line = getLineByType(LT_TITLE, entry->lines);
2040     if (line) {
2041     printf("title=%s\n", line->elements[1].item);
2042     } else {
2043     char * title;
2044     line = getLineByType(LT_MENUENTRY, entry->lines);
2045     title = grub2ExtractTitle(line);
2046     if (title)
2047     printf("title=%s\n", title);
2048     }
2049 niro 532 }
2050    
2051 niro 1850 int isSuseSystem(void) {
2052     const char * path;
2053     const static char default_path[] = "/etc/SuSE-release";
2054    
2055     if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2056     path = default_path;
2057    
2058     if (!access(path, R_OK))
2059     return 1;
2060     return 0;
2061     }
2062    
2063     int isSuseGrubConf(const char * path) {
2064     FILE * grubConf;
2065     char * line = NULL;
2066     size_t len = 0, res = 0;
2067    
2068     grubConf = fopen(path, "r");
2069     if (!grubConf) {
2070     dbgPrintf("Could not open SuSE configuration file '%s'\n", path);
2071     return 0;
2072     }
2073    
2074     while ((res = getline(&line, &len, grubConf)) != -1) {
2075     if (!strncmp(line, "setup", 5)) {
2076     fclose(grubConf);
2077     free(line);
2078     return 1;
2079     }
2080     }
2081    
2082     dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2083     path);
2084    
2085     fclose(grubConf);
2086     free(line);
2087     return 0;
2088     }
2089    
2090     int suseGrubConfGetLba(const char * path, int * lbaPtr) {
2091     FILE * grubConf;
2092     char * line = NULL;
2093     size_t res = 0, len = 0;
2094    
2095     if (!path) return 1;
2096     if (!lbaPtr) return 1;
2097    
2098     grubConf = fopen(path, "r");
2099     if (!grubConf) return 1;
2100    
2101     while ((res = getline(&line, &len, grubConf)) != -1) {
2102     if (line[res - 1] == '\n')
2103     line[res - 1] = '\0';
2104     else if (len > res)
2105     line[res] = '\0';
2106     else {
2107     line = realloc(line, res + 1);
2108     line[res] = '\0';
2109     }
2110    
2111     if (!strncmp(line, "setup", 5)) {
2112     if (strstr(line, "--force-lba")) {
2113     *lbaPtr = 1;
2114     } else {
2115     *lbaPtr = 0;
2116     }
2117     dbgPrintf("lba: %i\n", *lbaPtr);
2118     break;
2119     }
2120     }
2121    
2122     free(line);
2123     fclose(grubConf);
2124     return 0;
2125     }
2126    
2127     int suseGrubConfGetInstallDevice(const char * path, char ** devicePtr) {
2128     FILE * grubConf;
2129     char * line = NULL;
2130     size_t res = 0, len = 0;
2131     char * lastParamPtr = NULL;
2132     char * secLastParamPtr = NULL;
2133     char installDeviceNumber = '\0';
2134     char * bounds = NULL;
2135    
2136     if (!path) return 1;
2137     if (!devicePtr) return 1;
2138    
2139     grubConf = fopen(path, "r");
2140     if (!grubConf) return 1;
2141    
2142     while ((res = getline(&line, &len, grubConf)) != -1) {
2143     if (strncmp(line, "setup", 5))
2144     continue;
2145    
2146     if (line[res - 1] == '\n')
2147     line[res - 1] = '\0';
2148     else if (len > res)
2149     line[res] = '\0';
2150     else {
2151     line = realloc(line, res + 1);
2152     line[res] = '\0';
2153     }
2154    
2155     lastParamPtr = bounds = line + res;
2156    
2157     /* Last parameter in grub may be an optional IMAGE_DEVICE */
2158     while (!isspace(*lastParamPtr))
2159     lastParamPtr--;
2160     lastParamPtr++;
2161    
2162     secLastParamPtr = lastParamPtr - 2;
2163     dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2164    
2165     if (lastParamPtr + 3 > bounds) {
2166     dbgPrintf("lastParamPtr going over boundary");
2167     fclose(grubConf);
2168     free(line);
2169     return 1;
2170     }
2171     if (!strncmp(lastParamPtr, "(hd", 3))
2172     lastParamPtr += 3;
2173     dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2174    
2175     /*
2176     * Second last parameter will decide wether last parameter is
2177     * an IMAGE_DEVICE or INSTALL_DEVICE
2178     */
2179     while (!isspace(*secLastParamPtr))
2180     secLastParamPtr--;
2181     secLastParamPtr++;
2182    
2183     if (secLastParamPtr + 3 > bounds) {
2184     dbgPrintf("secLastParamPtr going over boundary");
2185     fclose(grubConf);
2186     free(line);
2187     return 1;
2188     }
2189     dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2190     if (!strncmp(secLastParamPtr, "(hd", 3)) {
2191     secLastParamPtr += 3;
2192     dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
2193     installDeviceNumber = *secLastParamPtr;
2194     } else {
2195     installDeviceNumber = *lastParamPtr;
2196     }
2197    
2198     *devicePtr = malloc(6);
2199     snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
2200     dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
2201     fclose(grubConf);
2202     free(line);
2203     return 0;
2204     }
2205    
2206     free(line);
2207     fclose(grubConf);
2208     return 1;
2209     }
2210    
2211     int grubGetBootFromDeviceMap(const char * device,
2212     char ** bootPtr) {
2213     FILE * deviceMap;
2214     char * line = NULL;
2215     size_t res = 0, len = 0;
2216     char * devicePtr;
2217     char * bounds = NULL;
2218     const char * path;
2219     const static char default_path[] = "/boot/grub/device.map";
2220    
2221     if (!device) return 1;
2222     if (!bootPtr) return 1;
2223    
2224     if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2225     path = default_path;
2226    
2227     dbgPrintf("opening grub device.map file from: %s\n", path);
2228     deviceMap = fopen(path, "r");
2229     if (!deviceMap)
2230     return 1;
2231    
2232     while ((res = getline(&line, &len, deviceMap)) != -1) {
2233     if (!strncmp(line, "#", 1))
2234     continue;
2235    
2236     if (line[res - 1] == '\n')
2237     line[res - 1] = '\0';
2238     else if (len > res)
2239     line[res] = '\0';
2240     else {
2241     line = realloc(line, res + 1);
2242     line[res] = '\0';
2243     }
2244    
2245     devicePtr = line;
2246     bounds = line + res;
2247    
2248     while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2249     devicePtr++;
2250     dbgPrintf("device: %s\n", devicePtr);
2251    
2252     if (!strncmp(devicePtr, device, strlen(device))) {
2253     devicePtr += strlen(device);
2254     while (isspace(*devicePtr) && ((devicePtr + 1) <= bounds))
2255     devicePtr++;
2256    
2257     *bootPtr = strdup(devicePtr);
2258     break;
2259     }
2260     }
2261    
2262     free(line);
2263     fclose(deviceMap);
2264     return 0;
2265     }
2266    
2267     int suseGrubConfGetBoot(const char * path, char ** bootPtr) {
2268     char * grubDevice;
2269    
2270     if (suseGrubConfGetInstallDevice(path, &grubDevice))
2271     dbgPrintf("error looking for grub installation device\n");
2272     else
2273     dbgPrintf("grubby installation device: %s\n", grubDevice);
2274    
2275     if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
2276     dbgPrintf("error looking for grub boot device\n");
2277     else
2278     dbgPrintf("grubby boot device: %s\n", *bootPtr);
2279    
2280     free(grubDevice);
2281     return 0;
2282     }
2283    
2284     int parseSuseGrubConf(int * lbaPtr, char ** bootPtr) {
2285     /*
2286     * This SuSE grub configuration file at this location is not your average
2287     * grub configuration file, but instead the grub commands used to setup
2288     * grub on that system.
2289     */
2290     const char * path;
2291     const static char default_path[] = "/etc/grub.conf";
2292    
2293     if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
2294     path = default_path;
2295    
2296     if (!isSuseGrubConf(path)) return 1;
2297    
2298     if (lbaPtr) {
2299     *lbaPtr = 0;
2300     if (suseGrubConfGetLba(path, lbaPtr))
2301     return 1;
2302     }
2303    
2304     if (bootPtr) {
2305     *bootPtr = NULL;
2306     suseGrubConfGetBoot(path, bootPtr);
2307     }
2308    
2309     return 0;
2310     }
2311    
2312 niro 532 int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
2313     FILE * in;
2314     char buf[1024];
2315     char * chptr;
2316     char * start;
2317     char * param;
2318    
2319 niro 926 in = fopen("/etc/conf.d/grub", "r");
2320 niro 532 if (!in) return 1;
2321    
2322     if (lbaPtr) *lbaPtr = 0;
2323     if (bootPtr) *bootPtr = NULL;
2324    
2325     while (fgets(buf, sizeof(buf), in)) {
2326     start = buf;
2327     while (isspace(*start)) start++;
2328     if (*start == '#') continue;
2329    
2330     chptr = strchr(start, '=');
2331     if (!chptr) continue;
2332     chptr--;
2333     while (*chptr && isspace(*chptr)) chptr--;
2334     chptr++;
2335     *chptr = '\0';
2336    
2337     param = chptr + 1;
2338     while (*param && isspace(*param)) param++;
2339     if (*param == '=') {
2340     param++;
2341     while (*param && isspace(*param)) param++;
2342     }
2343    
2344     chptr = param;
2345     while (*chptr && !isspace(*chptr)) chptr++;
2346     *chptr = '\0';
2347    
2348     if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
2349     *lbaPtr = 1;
2350     else if (!strcmp(start, "boot") && bootPtr)
2351     *bootPtr = strdup(param);
2352     }
2353    
2354     fclose(in);
2355    
2356     return 0;
2357     }
2358    
2359     void dumpSysconfigGrub(void) {
2360 niro 1850 char * boot = NULL;
2361 niro 532 int lba;
2362    
2363 niro 1851 if (isSuseSystem()) {
2364     if (parseSuseGrubConf(&lba, &boot)) {
2365 niro 1850 free(boot);
2366     return;
2367     }
2368     } else {
2369 niro 1851 if (parseSysconfigGrub(&lba, &boot)) {
2370 niro 1850 free(boot);
2371     return;
2372     }
2373 niro 532 }
2374 niro 1850
2375     if (lba) printf("lba\n");
2376     if (boot) {
2377     printf("boot=%s\n", boot);
2378     free(boot);
2379     }
2380 niro 532 }
2381    
2382     int displayInfo(struct grubConfig * config, char * kernel,
2383     const char * prefix) {
2384     int i = 0;
2385     struct singleEntry * entry;
2386     struct singleLine * line;
2387    
2388     entry = findEntryByPath(config, kernel, prefix, &i);
2389     if (!entry) {
2390     fprintf(stderr, _("grubby: kernel not found\n"));
2391     return 1;
2392     }
2393    
2394 niro 926 /* this is a horrible hack to support /etc/conf.d/grub; there must
2395 niro 532 be a better way */
2396     if (config->cfi == &grubConfigType) {
2397     dumpSysconfigGrub();
2398     } else {
2399 niro 914 line = getLineByType(LT_BOOT, config->theLines);
2400 niro 532 if (line && line->numElements >= 1) {
2401     printf("boot=%s\n", line->elements[1].item);
2402     }
2403    
2404 niro 914 line = getLineByType(LT_LBA, config->theLines);
2405 niro 532 if (line) printf("lba\n");
2406     }
2407    
2408     displayEntry(entry, prefix, i);
2409    
2410     i++;
2411     while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
2412     displayEntry(entry, prefix, i);
2413     i++;
2414     }
2415    
2416     return 0;
2417     }
2418    
2419 niro 914 struct singleLine * addLineTmpl(struct singleEntry * entry,
2420     struct singleLine * tmplLine,
2421     struct singleLine * prevLine,
2422     const char * val,
2423     struct configFileInfo * cfi)
2424     {
2425     struct singleLine * newLine = lineDup(tmplLine);
2426    
2427     if (val) {
2428     /* override the inherited value with our own.
2429     * This is a little weak because it only applies to elements[1]
2430     */
2431     if (newLine->numElements > 1)
2432     removeElement(newLine, 1);
2433     insertElement(newLine, val, 1, cfi);
2434    
2435     /* but try to keep the rootspec from the template... sigh */
2436     if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD)) {
2437     char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
2438     if (rootspec != NULL) {
2439     free(newLine->elements[1].item);
2440     newLine->elements[1].item =
2441     sdupprintf("%s%s", rootspec, val);
2442     }
2443     }
2444     }
2445    
2446     dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
2447     newLine->elements[0].item : "");
2448    
2449     if (!entry->lines) {
2450     /* first one on the list */
2451     entry->lines = newLine;
2452     } else if (prevLine) {
2453     /* add after prevLine */
2454     newLine->next = prevLine->next;
2455     prevLine->next = newLine;
2456     }
2457    
2458     return newLine;
2459     }
2460    
2461 niro 532 /* val may be NULL */
2462     struct singleLine * addLine(struct singleEntry * entry,
2463     struct configFileInfo * cfi,
2464 niro 914 enum lineType_e type, char * defaultIndent,
2465     const char * val) {
2466 niro 532 struct singleLine * line, * prev;
2467 niro 914 struct keywordTypes * kw;
2468     struct singleLine tmpl;
2469 niro 532
2470 niro 914 /* NB: This function shouldn't allocate items on the heap, rather on the
2471     * stack since it calls addLineTmpl which will make copies.
2472     */
2473 niro 532
2474 niro 914 if (type == LT_TITLE && cfi->titleBracketed) {
2475     /* we're doing a bracketed title (zipl) */
2476     tmpl.type = type;
2477     tmpl.numElements = 1;
2478     tmpl.elements = alloca(sizeof(*tmpl.elements));
2479     tmpl.elements[0].item = alloca(strlen(val)+3);
2480     sprintf(tmpl.elements[0].item, "[%s]", val);
2481     tmpl.elements[0].indent = "";
2482     val = NULL;
2483 niro 1696 } else if (type == LT_MENUENTRY) {
2484     char *lineend = "--class gnu-linux --class gnu --class os {";
2485     if (!val) {
2486     fprintf(stderr, "Line type LT_MENUENTRY requires a value\n");
2487     abort();
2488     }
2489     kw = getKeywordByType(type, cfi);
2490     if (!kw) {
2491     fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2492     abort();
2493     }
2494     tmpl.indent = "";
2495     tmpl.type = type;
2496     tmpl.numElements = 3;
2497     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2498     tmpl.elements[0].item = kw->key;
2499     tmpl.elements[0].indent = alloca(2);
2500     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2501     tmpl.elements[1].item = (char *)val;
2502     tmpl.elements[1].indent = alloca(2);
2503     sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
2504     tmpl.elements[2].item = alloca(strlen(lineend)+1);
2505     strcpy(tmpl.elements[2].item, lineend);
2506     tmpl.elements[2].indent = "";
2507 niro 914 } else {
2508     kw = getKeywordByType(type, cfi);
2509 niro 1696 if (!kw) {
2510     fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2511     abort();
2512     }
2513 niro 914 tmpl.type = type;
2514     tmpl.numElements = val ? 2 : 1;
2515     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2516     tmpl.elements[0].item = kw->key;
2517     tmpl.elements[0].indent = alloca(2);
2518     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2519     if (val) {
2520     tmpl.elements[1].item = (char *)val;
2521     tmpl.elements[1].indent = "";
2522     }
2523     }
2524    
2525 niro 532 /* The last non-empty line gives us the indention to us and the line
2526     to insert after. Note that comments are considered empty lines, which
2527     may not be ideal? If there are no lines or we are looking at the
2528     first line, we use defaultIndent (the first line is normally indented
2529     differently from the rest) */
2530 niro 914 for (line = entry->lines, prev = NULL; line; line = line->next) {
2531     if (line->numElements) prev = line;
2532     /* fall back on the last line if prev isn't otherwise set */
2533     if (!line->next && !prev) prev = line;
2534 niro 532 }
2535    
2536 niro 1696 struct singleLine *menuEntry;
2537     menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
2538     if (tmpl.type == LT_ENTRY_END) {
2539     if (menuEntry)
2540     tmpl.indent = menuEntry->indent;
2541     else
2542     tmpl.indent = defaultIndent ?: "";
2543     } else if (tmpl.type != LT_MENUENTRY) {
2544     if (menuEntry)
2545     tmpl.indent = "\t";
2546     else if (prev == entry->lines)
2547     tmpl.indent = defaultIndent ?: "";
2548     else
2549     tmpl.indent = prev->indent;
2550     }
2551 niro 532
2552 niro 914 return addLineTmpl(entry, &tmpl, prev, val, cfi);
2553 niro 532 }
2554    
2555     void removeLine(struct singleEntry * entry, struct singleLine * line) {
2556     struct singleLine * prev;
2557     int i;
2558    
2559     for (i = 0; i < line->numElements; i++) {
2560     free(line->elements[i].item);
2561     free(line->elements[i].indent);
2562     }
2563     free(line->elements);
2564     free(line->indent);
2565    
2566     if (line == entry->lines) {
2567     entry->lines = line->next;
2568     } else {
2569     prev = entry->lines;
2570     while (prev->next != line) prev = prev->next;
2571     prev->next = line->next;
2572     }
2573    
2574     free(line);
2575     }
2576    
2577 niro 1696 static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi)
2578     {
2579     struct singleLine newLine = {
2580     .indent = tmplLine->indent,
2581     .type = tmplLine->type,
2582     .next = tmplLine->next,
2583     };
2584     int firstQuotedItem = -1;
2585     int quoteLen = 0;
2586     int j;
2587     int element = 0;
2588     char *c;
2589    
2590     c = malloc(strlen(tmplLine->elements[0].item) + 1);
2591     strcpy(c, tmplLine->elements[0].item);
2592     insertElement(&newLine, c, element++, cfi);
2593     free(c);
2594     c = NULL;
2595    
2596     for (j = 1; j < tmplLine->numElements; j++) {
2597     if (firstQuotedItem == -1) {
2598     quoteLen += strlen(tmplLine->elements[j].item);
2599    
2600     if (isquote(tmplLine->elements[j].item[0])) {
2601     firstQuotedItem = j;
2602     quoteLen += strlen(tmplLine->elements[j].indent);
2603     } else {
2604     c = malloc(quoteLen + 1);
2605     strcpy(c, tmplLine->elements[j].item);
2606     insertElement(&newLine, c, element++, cfi);
2607     free(c);
2608     quoteLen = 0;
2609     }
2610     } else {
2611     int itemlen = strlen(tmplLine->elements[j].item);
2612     quoteLen += itemlen;
2613     quoteLen += strlen(tmplLine->elements[j].indent);
2614    
2615     if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
2616     c = malloc(quoteLen + 1);
2617     c[0] = '\0';
2618     for (int i = firstQuotedItem; i < j+1; i++) {
2619     strcat(c, tmplLine->elements[i].item);
2620     strcat(c, tmplLine->elements[i].indent);
2621     }
2622     insertElement(&newLine, c, element++, cfi);
2623     free(c);
2624    
2625     firstQuotedItem = -1;
2626     quoteLen = 0;
2627     }
2628     }
2629     }
2630     while (tmplLine->numElements)
2631     removeElement(tmplLine, 0);
2632     if (tmplLine->elements)
2633     free(tmplLine->elements);
2634    
2635     tmplLine->numElements = newLine.numElements;
2636     tmplLine->elements = newLine.elements;
2637     }
2638    
2639 niro 914 static void insertElement(struct singleLine * line,
2640     const char * item, int insertHere,
2641     struct configFileInfo * cfi)
2642     {
2643     struct keywordTypes * kw;
2644     char indent[2] = "";
2645    
2646     /* sanity check */
2647     if (insertHere > line->numElements) {
2648     dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",
2649     insertHere, line->numElements);
2650     insertHere = line->numElements;
2651     }
2652    
2653     line->elements = realloc(line->elements, (line->numElements + 1) *
2654     sizeof(*line->elements));
2655     memmove(&line->elements[insertHere+1],
2656     &line->elements[insertHere],
2657     (line->numElements - insertHere) *
2658     sizeof(*line->elements));
2659     line->elements[insertHere].item = strdup(item);
2660    
2661     kw = getKeywordByType(line->type, cfi);
2662    
2663     if (line->numElements == 0) {
2664     indent[0] = '\0';
2665     } else if (insertHere == 0) {
2666     indent[0] = kw->nextChar;
2667     } else if (kw->separatorChar != '\0') {
2668     indent[0] = kw->separatorChar;
2669     } else {
2670     indent[0] = ' ';
2671     }
2672    
2673     if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {
2674     /* move the end-of-line forward */
2675     line->elements[insertHere].indent =
2676     line->elements[insertHere-1].indent;
2677     line->elements[insertHere-1].indent = strdup(indent);
2678     } else {
2679     line->elements[insertHere].indent = strdup(indent);
2680     }
2681    
2682     line->numElements++;
2683    
2684     dbgPrintf("insertElement(%s, '%s%s', %d)\n",
2685     line->elements[0].item,
2686     line->elements[insertHere].item,
2687     line->elements[insertHere].indent,
2688     insertHere);
2689     }
2690    
2691     static void removeElement(struct singleLine * line, int removeHere) {
2692     int i;
2693    
2694     /* sanity check */
2695     if (removeHere >= line->numElements) return;
2696    
2697     dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
2698     removeHere, line->elements[removeHere].item);
2699    
2700     free(line->elements[removeHere].item);
2701    
2702     if (removeHere > 1) {
2703     /* previous argument gets this argument's post-indentation */
2704     free(line->elements[removeHere-1].indent);
2705     line->elements[removeHere-1].indent =
2706     line->elements[removeHere].indent;
2707     } else {
2708     free(line->elements[removeHere].indent);
2709     }
2710    
2711     /* now collapse the array, but don't bother to realloc smaller */
2712     for (i = removeHere; i < line->numElements - 1; i++)
2713     line->elements[i] = line->elements[i + 1];
2714    
2715     line->numElements--;
2716     }
2717    
2718 niro 532 int argMatch(const char * one, const char * two) {
2719     char * first, * second;
2720     char * chptr;
2721    
2722     first = strcpy(alloca(strlen(one) + 1), one);
2723     second = strcpy(alloca(strlen(two) + 1), two);
2724    
2725     chptr = strchr(first, '=');
2726     if (chptr) *chptr = '\0';
2727    
2728     chptr = strchr(second, '=');
2729     if (chptr) *chptr = '\0';
2730    
2731     return strcmp(first, second);
2732     }
2733    
2734     int updateActualImage(struct grubConfig * cfg, const char * image,
2735     const char * prefix, const char * addArgs,
2736     const char * removeArgs, int multibootArgs) {
2737     struct singleEntry * entry;
2738     struct singleLine * line, * rootLine;
2739     int index = 0;
2740 niro 914 int i, k;
2741 niro 532 const char ** newArgs, ** oldArgs;
2742     const char ** arg;
2743 niro 914 int useKernelArgs, useRoot;
2744 niro 532 int firstElement;
2745 niro 1304 int *usedElements;
2746 niro 914 int doreplace;
2747 niro 532
2748     if (!image) return 0;
2749    
2750     if (!addArgs) {
2751     newArgs = malloc(sizeof(*newArgs));
2752     *newArgs = NULL;
2753     } else {
2754     if (poptParseArgvString(addArgs, NULL, &newArgs)) {
2755     fprintf(stderr,
2756     _("grubby: error separating arguments '%s'\n"), addArgs);
2757     return 1;
2758     }
2759     }
2760    
2761     if (!removeArgs) {
2762     oldArgs = malloc(sizeof(*oldArgs));
2763     *oldArgs = NULL;
2764     } else {
2765     if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
2766     fprintf(stderr,
2767     _("grubby: error separating arguments '%s'\n"), removeArgs);
2768     free(newArgs);
2769     return 1;
2770     }
2771     }
2772    
2773    
2774 niro 914 useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
2775     && (!multibootArgs || cfg->cfi->mbConcatArgs));
2776 niro 532
2777 niro 914 useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
2778     && !multibootArgs);
2779 niro 532
2780 niro 914 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
2781 niro 532
2782 niro 914 if (multibootArgs && !entry->multiboot)
2783     continue;
2784 niro 532
2785 niro 914 /* Determine where to put the args. If this config supports
2786     * LT_KERNELARGS, use that. Otherwise use
2787     * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
2788     */
2789     if (useKernelArgs) {
2790     line = getLineByType(LT_KERNELARGS, entry->lines);
2791     if (!line) {
2792     /* no LT_KERNELARGS, need to add it */
2793     line = addLine(entry, cfg->cfi, LT_KERNELARGS,
2794     cfg->secondaryIndent, NULL);
2795     }
2796     firstElement = 1;
2797 niro 532
2798 niro 914 } else if (multibootArgs) {
2799     line = getLineByType(LT_HYPER, entry->lines);
2800     if (!line) {
2801     /* a multiboot entry without LT_HYPER? */
2802     continue;
2803     }
2804     firstElement = 2;
2805    
2806     } else {
2807     line = getLineByType(LT_KERNEL|LT_MBMODULE, entry->lines);
2808     if (!line) {
2809     /* no LT_KERNEL or LT_MBMODULE in this entry? */
2810     continue;
2811     }
2812     firstElement = 2;
2813 niro 532 }
2814    
2815 niro 914 /* handle the elilo case which does:
2816     * append="hypervisor args -- kernel args"
2817     */
2818     if (entry->multiboot && cfg->cfi->mbConcatArgs) {
2819     /* this is a multiboot entry, make sure there's
2820     * -- on the args line
2821     */
2822     for (i = firstElement; i < line->numElements; i++) {
2823     if (!strcmp(line->elements[i].item, "--"))
2824     break;
2825     }
2826     if (i == line->numElements) {
2827     /* assume all existing args are kernel args,
2828     * prepend -- to make it official
2829     */
2830     insertElement(line, "--", firstElement, cfg->cfi);
2831     i = firstElement;
2832     }
2833     if (!multibootArgs) {
2834     /* kernel args start after the -- */
2835     firstElement = i + 1;
2836     }
2837     } else if (cfg->cfi->mbConcatArgs) {
2838     /* this is a non-multiboot entry, remove hyper args */
2839     for (i = firstElement; i < line->numElements; i++) {
2840     if (!strcmp(line->elements[i].item, "--"))
2841     break;
2842     }
2843     if (i < line->numElements) {
2844     /* remove args up to -- */
2845     while (strcmp(line->elements[firstElement].item, "--"))
2846     removeElement(line, firstElement);
2847     /* remove -- */
2848     removeElement(line, firstElement);
2849     }
2850 niro 532 }
2851    
2852 niro 914 usedElements = calloc(line->numElements, sizeof(*usedElements));
2853 niro 532
2854 niro 914 for (k = 0, arg = newArgs; *arg; arg++, k++) {
2855    
2856     doreplace = 1;
2857 niro 532 for (i = firstElement; i < line->numElements; i++) {
2858 niro 914 if (multibootArgs && cfg->cfi->mbConcatArgs &&
2859     !strcmp(line->elements[i].item, "--"))
2860     {
2861     /* reached the end of hyper args, insert here */
2862     doreplace = 0;
2863     break;
2864     }
2865 niro 532 if (usedElements[i])
2866     continue;
2867     if (!argMatch(line->elements[i].item, *arg)) {
2868     usedElements[i]=1;
2869     break;
2870     }
2871     }
2872    
2873 niro 914 if (i < line->numElements && doreplace) {
2874     /* direct replacement */
2875 niro 532 free(line->elements[i].item);
2876     line->elements[i].item = strdup(*arg);
2877    
2878 niro 914 } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
2879     /* root= replacement */
2880     rootLine = getLineByType(LT_ROOT, entry->lines);
2881     if (rootLine) {
2882     free(rootLine->elements[1].item);
2883     rootLine->elements[1].item = strdup(*arg + 5);
2884 niro 532 } else {
2885 niro 914 rootLine = addLine(entry, cfg->cfi, LT_ROOT,
2886     cfg->secondaryIndent, *arg + 5);
2887 niro 532 }
2888 niro 914 }
2889 niro 532
2890 niro 914 else {
2891     /* insert/append */
2892     insertElement(line, *arg, i, cfg->cfi);
2893     usedElements = realloc(usedElements, line->numElements *
2894     sizeof(*usedElements));
2895     memmove(&usedElements[i + 1], &usedElements[i],
2896     line->numElements - i - 1);
2897     usedElements[i] = 1;
2898 niro 532
2899     /* if we updated a root= here even though there is a
2900     LT_ROOT available we need to remove the LT_ROOT entry
2901     (this will happen if we switch from a device to a label) */
2902     if (useRoot && !strncmp(*arg, "root=", 5)) {
2903 niro 914 rootLine = getLineByType(LT_ROOT, entry->lines);
2904     if (rootLine)
2905 niro 532 removeLine(entry, rootLine);
2906     }
2907     }
2908     }
2909    
2910     free(usedElements);
2911    
2912     for (arg = oldArgs; *arg; arg++) {
2913 niro 914 for (i = firstElement; i < line->numElements; i++) {
2914     if (multibootArgs && cfg->cfi->mbConcatArgs &&
2915     !strcmp(line->elements[i].item, "--"))
2916     /* reached the end of hyper args, stop here */
2917 niro 532 break;
2918 niro 914 if (!argMatch(line->elements[i].item, *arg)) {
2919     removeElement(line, i);
2920     break;
2921 niro 532 }
2922     }
2923 niro 914 /* handle removing LT_ROOT line too */
2924     if (useRoot && !strncmp(*arg, "root=", 5)) {
2925     rootLine = getLineByType(LT_ROOT, entry->lines);
2926     if (rootLine)
2927     removeLine(entry, rootLine);
2928     }
2929 niro 532 }
2930    
2931     if (line->numElements == 1) {
2932     /* don't need the line at all (note it has to be a
2933     LT_KERNELARGS for this to happen */
2934     removeLine(entry, line);
2935     }
2936     }
2937    
2938     free(newArgs);
2939     free(oldArgs);
2940    
2941     return 0;
2942     }
2943    
2944     int updateImage(struct grubConfig * cfg, const char * image,
2945     const char * prefix, const char * addArgs,
2946     const char * removeArgs,
2947     const char * addMBArgs, const char * removeMBArgs) {
2948     int rc = 0;
2949    
2950     if (!image) return rc;
2951    
2952     /* update the main args first... */
2953     if (addArgs || removeArgs)
2954     rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs, 0);
2955     if (rc) return rc;
2956    
2957     /* and now any multiboot args */
2958     if (addMBArgs || removeMBArgs)
2959     rc = updateActualImage(cfg, image, prefix, addMBArgs, removeMBArgs, 1);
2960     return rc;
2961     }
2962    
2963 niro 1156 int updateInitrd(struct grubConfig * cfg, const char * image,
2964     const char * prefix, const char * initrd) {
2965     struct singleEntry * entry;
2966 niro 1696 struct singleLine * line, * kernelLine, *endLine = NULL;
2967 niro 1156 int index = 0;
2968    
2969     if (!image) return 0;
2970    
2971     for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
2972     kernelLine = getLineByType(LT_KERNEL, entry->lines);
2973     if (!kernelLine) continue;
2974    
2975     line = getLineByType(LT_INITRD, entry->lines);
2976     if (line)
2977     removeLine(entry, line);
2978     if (prefix) {
2979     int prefixLen = strlen(prefix);
2980     if (!strncmp(initrd, prefix, prefixLen))
2981     initrd += prefixLen;
2982     }
2983 niro 1696 endLine = getLineByType(LT_ENTRY_END, entry->lines);
2984     if (endLine)
2985     removeLine(entry, endLine);
2986 niro 1156 line = addLine(entry, cfg->cfi, LT_INITRD, kernelLine->indent, initrd);
2987 niro 1696 if (!line)
2988     return 1;
2989     if (endLine) {
2990     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
2991     if (!line)
2992     return 1;
2993     }
2994    
2995 niro 1156 break;
2996     }
2997    
2998     return 0;
2999     }
3000    
3001 niro 532 int checkDeviceBootloader(const char * device, const unsigned char * boot) {
3002     int fd;
3003     unsigned char bootSect[512];
3004     int offset;
3005    
3006     fd = open(device, O_RDONLY);
3007     if (fd < 0) {
3008     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3009     device, strerror(errno));
3010     return 1;
3011     }
3012    
3013     if (read(fd, bootSect, 512) != 512) {
3014     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3015     device, strerror(errno));
3016     return 1;
3017     }
3018     close(fd);
3019    
3020     /* first three bytes should match, a jmp short should be in there */
3021     if (memcmp(boot, bootSect, 3))
3022     return 0;
3023    
3024 niro 1718 if (boot[1] == JMP_SHORT_OPCODE) {
3025 niro 532 offset = boot[2] + 2;
3026     } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3027     offset = (boot[3] << 8) + boot[2] + 2;
3028 niro 1718 } else if (boot[0] == JMP_SHORT_OPCODE) {
3029     offset = boot[1] + 2;
3030     /*
3031     * it looks like grub, when copying stage1 into the mbr, patches stage1
3032     * right after the JMP location, replacing other instructions such as
3033     * JMPs for NOOPs. So, relax the check a little bit by skipping those
3034     * different bytes.
3035     */
3036     if ((bootSect[offset + 1] == NOOP_OPCODE)
3037     && (bootSect[offset + 2] == NOOP_OPCODE)) {
3038     offset = offset + 3;
3039     }
3040 niro 532 } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3041     offset = (boot[2] << 8) + boot[1] + 2;
3042     } else {
3043     return 0;
3044     }
3045    
3046     if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
3047     return 0;
3048    
3049     return 2;
3050     }
3051    
3052     int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {
3053     int fd;
3054     char buf[65536];
3055     char * end;
3056     char * chptr;
3057     char * chptr2;
3058     int rc;
3059    
3060     /* it's on raid; we need to parse /proc/mdstat and check all of the
3061     *raw* devices listed in there */
3062    
3063     if (!strncmp(mdDev, "/dev/", 5))
3064     mdDev += 5;
3065    
3066     if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
3067     fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
3068     strerror(errno));
3069     return 2;
3070     }
3071    
3072     rc = read(fd, buf, sizeof(buf) - 1);
3073     if (rc < 0 || rc == (sizeof(buf) - 1)) {
3074     fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
3075     strerror(errno));
3076     close(fd);
3077     return 2;
3078     }
3079     close(fd);
3080     buf[rc] = '\0';
3081    
3082     chptr = buf;
3083     while (*chptr) {
3084     end = strchr(chptr, '\n');
3085     if (!end) break;
3086     *end = '\0';
3087    
3088     if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
3089     chptr[strlen(mdDev)] == ' ') {
3090    
3091     /* found the device */
3092     while (*chptr && *chptr != ':') chptr++;
3093     chptr++;
3094     while (*chptr && isspace(*chptr)) chptr++;
3095    
3096     /* skip the "active" bit */
3097     while (*chptr && !isspace(*chptr)) chptr++;
3098     while (*chptr && isspace(*chptr)) chptr++;
3099    
3100     /* skip the raid level */
3101     while (*chptr && !isspace(*chptr)) chptr++;
3102     while (*chptr && isspace(*chptr)) chptr++;
3103    
3104     /* everything else is partition stuff */
3105     while (*chptr) {
3106     chptr2 = chptr;
3107     while (*chptr2 && *chptr2 != '[') chptr2++;
3108     if (!*chptr2) break;
3109    
3110     /* yank off the numbers at the end */
3111     chptr2--;
3112     while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;
3113     chptr2++;
3114     *chptr2 = '\0';
3115    
3116     /* Better, now we need the /dev/ back. We're done with
3117     * everything before this point, so we can just put
3118     * the /dev/ part there. There will always be room. */
3119     memcpy(chptr - 5, "/dev/", 5);
3120     rc = checkDeviceBootloader(chptr - 5, boot);
3121     if (rc != 2) {
3122     return rc;
3123     }
3124    
3125     chptr = chptr2 + 1;
3126     /* skip the [11] bit */
3127     while (*chptr && !isspace(*chptr)) chptr++;
3128     /* and move to the next one */
3129     while (*chptr && isspace(*chptr)) chptr++;
3130     }
3131    
3132     /* we're good to go */
3133     return 2;
3134     }
3135    
3136     chptr = end + 1;
3137     }
3138    
3139     fprintf(stderr,
3140     _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
3141     mdDev);
3142     return 0;
3143     }
3144    
3145     int checkForLilo(struct grubConfig * config) {
3146     int fd;
3147     unsigned char boot[512];
3148     struct singleLine * line;
3149    
3150     for (line = config->theLines; line; line = line->next)
3151     if (line->type == LT_BOOT) break;
3152    
3153     if (!line) {
3154     fprintf(stderr,
3155     _("grubby: no boot line found in lilo configuration\n"));
3156     return 1;
3157     }
3158    
3159     if (line->numElements != 2) return 1;
3160    
3161     fd = open("/boot/boot.b", O_RDONLY);
3162     if (fd < 0) {
3163     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3164     "/boot/boot.b", strerror(errno));
3165     return 1;
3166     }
3167    
3168     if (read(fd, boot, 512) != 512) {
3169     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3170     "/boot/boot.b", strerror(errno));
3171     return 1;
3172     }
3173     close(fd);
3174    
3175     if (!strncmp("/dev/md", line->elements[1].item, 7))
3176     return checkLiloOnRaid(line->elements[1].item, boot);
3177    
3178     return checkDeviceBootloader(line->elements[1].item, boot);
3179     }
3180    
3181 niro 1696 int checkForGrub2(struct grubConfig * config) {
3182 niro 1714 if (!access("/etc/grub.d/", R_OK))
3183 niro 1696 return 2;
3184    
3185     return 1;
3186     }
3187    
3188 niro 532 int checkForGrub(struct grubConfig * config) {
3189     int fd;
3190     unsigned char bootSect[512];
3191     char * boot;
3192 niro 1851 int onSuse = isSuseSystem();
3193 niro 532
3194 niro 1851
3195     if (onSuse) {
3196     if (parseSuseGrubConf(NULL, &boot))
3197     return 0;
3198 niro 1850 } else {
3199 niro 1851 if (parseSysconfigGrub(NULL, &boot))
3200     return 0;
3201 niro 1850 }
3202 niro 532
3203     /* assume grub is not installed -- not an error condition */
3204     if (!boot)
3205     return 0;
3206    
3207     fd = open("/boot/grub/stage1", O_RDONLY);
3208     if (fd < 0)
3209     /* this doesn't exist if grub hasn't been installed */
3210     return 0;
3211    
3212     if (read(fd, bootSect, 512) != 512) {
3213     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3214     "/boot/grub/stage1", strerror(errno));
3215 niro 914 close(fd);
3216 niro 532 return 1;
3217     }
3218     close(fd);
3219    
3220 niro 1851 /* The more elaborate checks do not work on SuSE. The checks done
3221     * seem to be reasonble (at least for now), so just return success
3222     */
3223     if (onSuse)
3224 niro 1850 return 2;
3225 niro 1851
3226     return checkDeviceBootloader(boot, bootSect);
3227 niro 532 }
3228    
3229 niro 914 int checkForExtLinux(struct grubConfig * config) {
3230     int fd;
3231     unsigned char bootSect[512];
3232     char * boot;
3233     char executable[] = "/boot/extlinux/extlinux";
3234    
3235     printf("entered: checkForExtLinux()\n");
3236    
3237     if (parseSysconfigGrub(NULL, &boot))
3238     return 0;
3239    
3240     /* assume grub is not installed -- not an error condition */
3241     if (!boot)
3242     return 0;
3243    
3244     fd = open(executable, O_RDONLY);
3245     if (fd < 0)
3246     /* this doesn't exist if grub hasn't been installed */
3247     return 0;
3248    
3249     if (read(fd, bootSect, 512) != 512) {
3250     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3251     executable, strerror(errno));
3252     return 1;
3253     }
3254     close(fd);
3255    
3256     return checkDeviceBootloader(boot, bootSect);
3257     }
3258    
3259 niro 1853 int checkForYaboot(struct grubConfig * config) {
3260     /*
3261     * This is a simplistic check that we consider good enough for own puporses
3262     *
3263     * If we were to properly check if yaboot is *installed* we'd need to:
3264     * 1) get the system boot device (LT_BOOT)
3265     * 2) considering it's a raw filesystem, check if the yaboot binary matches
3266     * the content on the boot device
3267     * 3) if not, copy the binary to a temporary file and run "addnote" on it
3268     * 4) check again if binary and boot device contents match
3269     */
3270     if (!access("/etc/yaboot.conf", R_OK))
3271     return 2;
3272    
3273     return 1;
3274     }
3275    
3276 niro 1854 int checkForElilo(struct grubConfig * config) {
3277     if (!access("/etc/elilo.conf", R_OK))
3278     return 2;
3279    
3280     return 1;
3281     }
3282    
3283 niro 532 static char * getRootSpecifier(char * str) {
3284     char * idx, * rootspec = NULL;
3285    
3286     if (*str == '(') {
3287     idx = rootspec = strdup(str);
3288     while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;
3289     *(++idx) = '\0';
3290     }
3291     return rootspec;
3292     }
3293    
3294 niro 914 static char * getInitrdVal(struct grubConfig * config,
3295     const char * prefix, struct singleLine *tmplLine,
3296     const char * newKernelInitrd,
3297 niro 1844 const char ** extraInitrds, int extraInitrdCount)
3298 niro 914 {
3299     char *initrdVal, *end;
3300     int i;
3301     size_t totalSize;
3302     size_t prefixLen;
3303     char separatorChar;
3304    
3305     prefixLen = strlen(prefix);
3306     totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
3307    
3308     for (i = 0; i < extraInitrdCount; i++) {
3309     totalSize += sizeof(separatorChar);
3310     totalSize += strlen(extraInitrds[i]) - prefixLen;
3311     }
3312    
3313     initrdVal = end = malloc(totalSize);
3314    
3315     end = stpcpy (end, newKernelInitrd + prefixLen);
3316    
3317     separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
3318     for (i = 0; i < extraInitrdCount; i++) {
3319     const char *extraInitrd;
3320     int j;
3321    
3322     extraInitrd = extraInitrds[i] + prefixLen;
3323     /* Don't add entries that are already there */
3324     if (tmplLine != NULL) {
3325     for (j = 2; j < tmplLine->numElements; j++)
3326     if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
3327     break;
3328    
3329     if (j != tmplLine->numElements)
3330     continue;
3331     }
3332    
3333     *end++ = separatorChar;
3334     end = stpcpy(end, extraInitrd);
3335     }
3336    
3337     return initrdVal;
3338     }
3339    
3340 niro 532 int addNewKernel(struct grubConfig * config, struct singleEntry * template,
3341     const char * prefix,
3342 niro 1844 const char * newKernelPath, const char * newKernelTitle,
3343     const char * newKernelArgs, const char * newKernelInitrd,
3344     const char ** extraInitrds, int extraInitrdCount,
3345     const char * newMBKernel, const char * newMBKernelArgs) {
3346 niro 532 struct singleEntry * new;
3347 niro 914 struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
3348 niro 532 int needs;
3349     char * chptr;
3350    
3351     if (!newKernelPath) return 0;
3352    
3353     /* if the newKernelTitle is too long silently munge it into something
3354     * we can live with. truncating is first check, then we'll just mess with
3355     * it until it looks better */
3356     if (config->cfi->maxTitleLength &&
3357     (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
3358     char * buf = alloca(config->cfi->maxTitleLength + 7);
3359     char * numBuf = alloca(config->cfi->maxTitleLength + 1);
3360     int i = 1;
3361    
3362     sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);
3363     while (findEntryByPath(config, buf, NULL, NULL)) {
3364     sprintf(numBuf, "%d", i++);
3365     strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
3366     }
3367    
3368     newKernelTitle = buf + 6;
3369     }
3370    
3371     new = malloc(sizeof(*new));
3372     new->skip = 0;
3373     new->multiboot = 0;
3374     new->next = config->entries;
3375     new->lines = NULL;
3376     config->entries = new;
3377    
3378     /* copy/update from the template */
3379 niro 914 needs = NEED_KERNEL | NEED_TITLE;
3380     if (newKernelInitrd)
3381     needs |= NEED_INITRD;
3382 niro 532 if (newMBKernel) {
3383 niro 914 needs |= NEED_MB;
3384 niro 532 new->multiboot = 1;
3385     }
3386    
3387     if (template) {
3388 niro 914 for (masterLine = template->lines;
3389     masterLine && (tmplLine = lineDup(masterLine));
3390     lineFree(tmplLine), masterLine = masterLine->next)
3391     {
3392     dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
3393 niro 532
3394     /* skip comments */
3395     chptr = tmplLine->indent;
3396     while (*chptr && isspace(*chptr)) chptr++;
3397     if (*chptr == '#') continue;
3398    
3399 niro 914 if (tmplLine->type == LT_KERNEL &&
3400 niro 1696 tmplLine->numElements >= 2) {
3401 niro 914 if (!template->multiboot && (needs & NEED_MB)) {
3402     /* it's not a multiboot template and this is the kernel
3403     * line. Try to be intelligent about inserting the
3404     * hypervisor at the same time.
3405     */
3406     if (config->cfi->mbHyperFirst) {
3407     /* insert the hypervisor first */
3408     newLine = addLine(new, config->cfi, LT_HYPER,
3409     tmplLine->indent,
3410     newMBKernel + strlen(prefix));
3411     /* set up for adding the kernel line */
3412     free(tmplLine->indent);
3413     tmplLine->indent = strdup(config->secondaryIndent);
3414     needs &= ~NEED_MB;
3415     }
3416     if (needs & NEED_KERNEL) {
3417     /* use addLineTmpl to preserve line elements,
3418     * otherwise we could just call addLine. Unfortunately
3419     * this means making some changes to the template
3420     * such as the indent change above and the type
3421     * change below.
3422     */
3423     struct keywordTypes * mbm_kw =
3424     getKeywordByType(LT_MBMODULE, config->cfi);
3425     if (mbm_kw) {
3426     tmplLine->type = LT_MBMODULE;
3427     free(tmplLine->elements[0].item);
3428     tmplLine->elements[0].item = strdup(mbm_kw->key);
3429     }
3430     newLine = addLineTmpl(new, tmplLine, newLine,
3431     newKernelPath + strlen(prefix), config->cfi);
3432     needs &= ~NEED_KERNEL;
3433     }
3434     if (needs & NEED_MB) { /* !mbHyperFirst */
3435     newLine = addLine(new, config->cfi, LT_HYPER,
3436     config->secondaryIndent,
3437     newMBKernel + strlen(prefix));
3438     needs &= ~NEED_MB;
3439     }
3440     } else if (needs & NEED_KERNEL) {
3441     newLine = addLineTmpl(new, tmplLine, newLine,
3442     newKernelPath + strlen(prefix), config->cfi);
3443     needs &= ~NEED_KERNEL;
3444     }
3445 niro 532
3446 niro 914 } else if (tmplLine->type == LT_HYPER &&
3447     tmplLine->numElements >= 2) {
3448     if (needs & NEED_MB) {
3449     newLine = addLineTmpl(new, tmplLine, newLine,
3450     newMBKernel + strlen(prefix), config->cfi);
3451     needs &= ~NEED_MB;
3452     }
3453 niro 532
3454 niro 914 } else if (tmplLine->type == LT_MBMODULE &&
3455     tmplLine->numElements >= 2) {
3456     if (new->multiboot) {
3457     if (needs & NEED_KERNEL) {
3458     newLine = addLineTmpl(new, tmplLine, newLine,
3459     newKernelPath +
3460     strlen(prefix), config->cfi);
3461     needs &= ~NEED_KERNEL;
3462     } else if (config->cfi->mbInitRdIsModule &&
3463     (needs & NEED_INITRD)) {
3464     char *initrdVal;
3465     initrdVal = getInitrdVal(config, prefix, tmplLine,
3466     newKernelInitrd, extraInitrds,
3467     extraInitrdCount);
3468     newLine = addLineTmpl(new, tmplLine, newLine,
3469     initrdVal, config->cfi);
3470     free(initrdVal);
3471     needs &= ~NEED_INITRD;
3472     }
3473     } else if (needs & NEED_KERNEL) {
3474     /* template is multi but new is not,
3475     * insert the kernel in the first module slot
3476     */
3477     tmplLine->type = LT_KERNEL;
3478     free(tmplLine->elements[0].item);
3479     tmplLine->elements[0].item =
3480     strdup(getKeywordByType(LT_KERNEL, config->cfi)->key);
3481     newLine = addLineTmpl(new, tmplLine, newLine,
3482     newKernelPath + strlen(prefix), config->cfi);
3483     needs &= ~NEED_KERNEL;
3484     } else if (needs & NEED_INITRD) {
3485     char *initrdVal;
3486     /* template is multi but new is not,
3487     * insert the initrd in the second module slot
3488     */
3489     tmplLine->type = LT_INITRD;
3490     free(tmplLine->elements[0].item);
3491     tmplLine->elements[0].item =
3492     strdup(getKeywordByType(LT_INITRD, config->cfi)->key);
3493     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3494     newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3495     free(initrdVal);
3496     needs &= ~NEED_INITRD;
3497     }
3498 niro 532
3499     } else if (tmplLine->type == LT_INITRD &&
3500 niro 914 tmplLine->numElements >= 2) {
3501     if (needs & NEED_INITRD &&
3502     new->multiboot && !template->multiboot &&
3503     config->cfi->mbInitRdIsModule) {
3504     /* make sure we don't insert the module initrd
3505     * before the module kernel... if we don't do it here,
3506     * it will be inserted following the template.
3507     */
3508     if (!needs & NEED_KERNEL) {
3509     char *initrdVal;
3510    
3511     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3512     newLine = addLine(new, config->cfi, LT_MBMODULE,
3513     config->secondaryIndent,
3514     initrdVal);
3515     free(initrdVal);
3516     needs &= ~NEED_INITRD;
3517     }
3518     } else if (needs & NEED_INITRD) {
3519     char *initrdVal;
3520     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3521     newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3522     free(initrdVal);
3523     needs &= ~NEED_INITRD;
3524 niro 532 }
3525    
3526 niro 1696 } else if (tmplLine->type == LT_MENUENTRY &&
3527     (needs & NEED_TITLE)) {
3528     requote(tmplLine, config->cfi);
3529     char *nkt = malloc(strlen(newKernelTitle)+3);
3530     strcpy(nkt, "'");
3531     strcat(nkt, newKernelTitle);
3532     strcat(nkt, "'");
3533     newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
3534     free(nkt);
3535     needs &= ~NEED_TITLE;
3536 niro 532 } else if (tmplLine->type == LT_TITLE &&
3537 niro 914 (needs & NEED_TITLE)) {
3538     if (tmplLine->numElements >= 2) {
3539     newLine = addLineTmpl(new, tmplLine, newLine,
3540     newKernelTitle, config->cfi);
3541     needs &= ~NEED_TITLE;
3542     } else if (tmplLine->numElements == 1 &&
3543     config->cfi->titleBracketed) {
3544     /* addLineTmpl doesn't handle titleBracketed */
3545     newLine = addLine(new, config->cfi, LT_TITLE,
3546     tmplLine->indent, newKernelTitle);
3547     needs &= ~NEED_TITLE;
3548     }
3549 niro 1696 } else if (tmplLine->type == LT_ECHO) {
3550     requote(tmplLine, config->cfi);
3551 niro 1722 static const char *prefix = "'Loading ";
3552 niro 1696 if (tmplLine->numElements > 1 &&
3553 niro 1722 strstr(tmplLine->elements[1].item, prefix) &&
3554     masterLine->next && masterLine->next->type == LT_KERNEL) {
3555 niro 1696 char *newTitle = malloc(strlen(prefix) +
3556     strlen(newKernelTitle) + 2);
3557 niro 532
3558 niro 1696 strcpy(newTitle, prefix);
3559     strcat(newTitle, newKernelTitle);
3560     strcat(newTitle, "'");
3561     newLine = addLine(new, config->cfi, LT_ECHO,
3562     tmplLine->indent, newTitle);
3563     free(newTitle);
3564     } else {
3565     /* pass through other lines from the template */
3566     newLine = addLineTmpl(new, tmplLine, newLine, NULL,
3567     config->cfi);
3568     }
3569 niro 914 } else {
3570     /* pass through other lines from the template */
3571     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
3572     }
3573 niro 532 }
3574 niro 914
3575 niro 532 } else {
3576 niro 914 /* don't have a template, so start the entry with the
3577     * appropriate starting line
3578     */
3579 niro 1693 switch (config->cfi->entryStart) {
3580 niro 914 case LT_KERNEL:
3581     if (new->multiboot && config->cfi->mbHyperFirst) {
3582     /* fall through to LT_HYPER */
3583     } else {
3584     newLine = addLine(new, config->cfi, LT_KERNEL,
3585     config->primaryIndent,
3586     newKernelPath + strlen(prefix));
3587     needs &= ~NEED_KERNEL;
3588     break;
3589     }
3590    
3591     case LT_HYPER:
3592     newLine = addLine(new, config->cfi, LT_HYPER,
3593     config->primaryIndent,
3594     newMBKernel + strlen(prefix));
3595     needs &= ~NEED_MB;
3596 niro 532 break;
3597    
3598 niro 1696 case LT_MENUENTRY: {
3599     char *nkt = malloc(strlen(newKernelTitle)+3);
3600     strcpy(nkt, "'");
3601     strcat(nkt, newKernelTitle);
3602     strcat(nkt, "'");
3603     newLine = addLine(new, config->cfi, LT_MENUENTRY,
3604     config->primaryIndent, nkt);
3605     free(nkt);
3606     needs &= ~NEED_TITLE;
3607     needs |= NEED_END;
3608     break;
3609     }
3610 niro 914 case LT_TITLE:
3611     if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
3612     char * templabel;
3613     int x = 0, y = 0;
3614    
3615     templabel = strdup(newKernelTitle);
3616     while( templabel[x]){
3617     if( templabel[x] == ' ' ){
3618     y = x;
3619     while( templabel[y] ){
3620     templabel[y] = templabel[y+1];
3621     y++;
3622     }
3623     }
3624     x++;
3625     }
3626     newLine = addLine(new, config->cfi, LT_TITLE,
3627     config->primaryIndent, templabel);
3628     free(templabel);
3629     }else{
3630     newLine = addLine(new, config->cfi, LT_TITLE,
3631     config->primaryIndent, newKernelTitle);
3632     }
3633     needs &= ~NEED_TITLE;
3634     break;
3635    
3636     default:
3637     abort();
3638 niro 532 }
3639     }
3640    
3641 niro 914 /* add the remainder of the lines, i.e. those that either
3642     * weren't present in the template, or in the case of no template,
3643 niro 1693 * all the lines following the entryStart.
3644 niro 914 */
3645     if (needs & NEED_TITLE) {
3646     newLine = addLine(new, config->cfi, LT_TITLE,
3647     config->secondaryIndent,
3648     newKernelTitle);
3649     needs &= ~NEED_TITLE;
3650 niro 532 }
3651 niro 914 if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
3652     newLine = addLine(new, config->cfi, LT_HYPER,
3653     config->secondaryIndent,
3654     newMBKernel + strlen(prefix));
3655     needs &= ~NEED_MB;
3656     }
3657     if (needs & NEED_KERNEL) {
3658     newLine = addLine(new, config->cfi,
3659     (new->multiboot && getKeywordByType(LT_MBMODULE,
3660     config->cfi)) ?
3661     LT_MBMODULE : LT_KERNEL,
3662     config->secondaryIndent,
3663     newKernelPath + strlen(prefix));
3664     needs &= ~NEED_KERNEL;
3665     }
3666     if (needs & NEED_MB) {
3667     newLine = addLine(new, config->cfi, LT_HYPER,
3668     config->secondaryIndent,
3669     newMBKernel + strlen(prefix));
3670     needs &= ~NEED_MB;
3671     }
3672     if (needs & NEED_INITRD) {
3673     char *initrdVal;
3674     initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
3675     newLine = addLine(new, config->cfi,
3676     (new->multiboot && getKeywordByType(LT_MBMODULE,
3677     config->cfi)) ?
3678     LT_MBMODULE : LT_INITRD,
3679     config->secondaryIndent,
3680     initrdVal);
3681     free(initrdVal);
3682     needs &= ~NEED_INITRD;
3683     }
3684 niro 1696 if (needs & NEED_END) {
3685     newLine = addLine(new, config->cfi, LT_ENTRY_END,
3686     config->secondaryIndent, NULL);
3687     needs &= ~NEED_END;
3688     }
3689 niro 532
3690 niro 914 if (needs) {
3691     printf(_("grubby: needs=%d, aborting\n"), needs);
3692     abort();
3693     }
3694    
3695 niro 532 if (updateImage(config, "0", prefix, newKernelArgs, NULL,
3696     newMBKernelArgs, NULL)) return 1;
3697    
3698     return 0;
3699     }
3700    
3701 niro 914 static void traceback(int signum)
3702     {
3703     void *array[40];
3704     size_t size;
3705    
3706     signal(SIGSEGV, SIG_DFL);
3707     memset(array, '\0', sizeof (array));
3708     size = backtrace(array, 40);
3709    
3710     fprintf(stderr, "grubby recieved SIGSEGV! Backtrace (%ld):\n",
3711     (unsigned long)size);
3712     backtrace_symbols_fd(array, size, STDERR_FILENO);
3713     exit(1);
3714     }
3715    
3716 niro 532 int main(int argc, const char ** argv) {
3717     poptContext optCon;
3718 niro 1696 const char * grubConfig = NULL;
3719 niro 532 char * outputFile = NULL;
3720     int arg = 0;
3721     int flags = 0;
3722     int badImageOkay = 0;
3723 niro 1696 int configureGrub2 = 0;
3724 niro 532 int configureLilo = 0, configureELilo = 0, configureGrub = 0;
3725     int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
3726 niro 914 int configureExtLinux = 0;
3727 niro 532 int bootloaderProbe = 0;
3728 niro 914 int extraInitrdCount = 0;
3729 niro 532 char * updateKernelPath = NULL;
3730     char * newKernelPath = NULL;
3731     char * removeKernelPath = NULL;
3732     char * newKernelArgs = NULL;
3733     char * newKernelInitrd = NULL;
3734     char * newKernelTitle = NULL;
3735     char * newKernelVersion = NULL;
3736     char * newMBKernel = NULL;
3737     char * newMBKernelArgs = NULL;
3738     char * removeMBKernelArgs = NULL;
3739     char * removeMBKernel = NULL;
3740     char * bootPrefix = NULL;
3741     char * defaultKernel = NULL;
3742     char * removeArgs = NULL;
3743     char * kernelInfo = NULL;
3744 niro 914 char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
3745 niro 532 const char * chptr = NULL;
3746     struct configFileInfo * cfi = NULL;
3747     struct grubConfig * config;
3748     struct singleEntry * template = NULL;
3749     int copyDefault = 0, makeDefault = 0;
3750     int displayDefault = 0;
3751 niro 1720 int displayDefaultIndex = 0;
3752 niro 1721 int displayDefaultTitle = 0;
3753 niro 1859 int defaultIndex = -1;
3754 niro 532 struct poptOption options[] = {
3755     { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
3756     _("add an entry for the specified kernel"), _("kernel-path") },
3757     { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
3758     _("add an entry for the specified multiboot kernel"), NULL },
3759     { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
3760     _("default arguments for the new kernel or new arguments for "
3761     "kernel being updated"), _("args") },
3762     { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
3763     _("default arguments for the new multiboot kernel or "
3764     "new arguments for multiboot kernel being updated"), NULL },
3765     { "bad-image-okay", 0, 0, &badImageOkay, 0,
3766     _("don't sanity check images in boot entries (for testing only)"),
3767     NULL },
3768     { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
3769     _("filestystem which contains /boot directory (for testing only)"),
3770     _("bootfs") },
3771 niro 1854 #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
3772 niro 532 { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
3773 niro 1853 _("check which bootloader is installed on boot sector") },
3774 niro 532 #endif
3775     { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
3776     _("path to grub config file to update (\"-\" for stdin)"),
3777     _("path") },
3778     { "copy-default", 0, 0, &copyDefault, 0,
3779     _("use the default boot entry as a template for the new entry "
3780     "being added; if the default is not a linux image, or if "
3781     "the kernel referenced by the default image does not exist, "
3782     "the first linux entry whose kernel does exist is used as the "
3783     "template"), NULL },
3784 niro 1736 { "debug", 0, 0, &debug, 0,
3785     _("print debugging information for failures") },
3786 niro 532 { "default-kernel", 0, 0, &displayDefault, 0,
3787     _("display the path of the default kernel") },
3788 niro 1720 { "default-index", 0, 0, &displayDefaultIndex, 0,
3789     _("display the index of the default kernel") },
3790 niro 1721 { "default-title", 0, 0, &displayDefaultTitle, 0,
3791     _("display the title of the default kernel") },
3792 niro 532 { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
3793     _("configure elilo bootloader") },
3794 niro 914 { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
3795     _("configure extlinux bootloader (from syslinux)") },
3796 niro 532 { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
3797     _("configure grub bootloader") },
3798 niro 1696 { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
3799     _("configure grub2 bootloader") },
3800 niro 532 { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
3801     _("display boot information for specified kernel"),
3802     _("kernel-path") },
3803     { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
3804     _("initrd image for the new kernel"), _("initrd-path") },
3805 niro 914 { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
3806     _("auxilliary initrd image for things other than the new kernel"), _("initrd-path") },
3807 niro 532 { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
3808     _("configure lilo bootloader") },
3809     { "make-default", 0, 0, &makeDefault, 0,
3810     _("make the newly added entry the default boot entry"), NULL },
3811     { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
3812     _("path to output updated config file (\"-\" for stdout)"),
3813     _("path") },
3814     { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
3815     _("remove kernel arguments"), NULL },
3816     { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
3817     _("remove multiboot kernel arguments"), NULL },
3818     { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
3819     _("remove all entries for the specified kernel"),
3820     _("kernel-path") },
3821     { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
3822     _("remove all entries for the specified multiboot kernel"), NULL },
3823     { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
3824     _("make the first entry referencing the specified kernel "
3825     "the default"), _("kernel-path") },
3826 niro 1859 { "set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
3827     _("make the given entry index the default entry"),
3828     _("entry-index") },
3829 niro 532 { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
3830     _("configure silo bootloader") },
3831     { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
3832     _("title to use for the new kernel entry"), _("entry-title") },
3833     { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
3834     _("updated information for the specified kernel"),
3835     _("kernel-path") },
3836     { "version", 'v', 0, NULL, 'v',
3837     _("print the version of this program and exit"), NULL },
3838     { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
3839     _("configure yaboot bootloader") },
3840     { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
3841     _("configure zipl bootloader") },
3842     POPT_AUTOHELP
3843     { 0, 0, 0, 0, 0 }
3844     };
3845    
3846 niro 914 useextlinuxmenu=0;
3847    
3848     signal(SIGSEGV, traceback);
3849    
3850 niro 532 optCon = poptGetContext("grubby", argc, argv, options, 0);
3851     poptReadDefaultConfig(optCon, 1);
3852    
3853     while ((arg = poptGetNextOpt(optCon)) >= 0) {
3854     switch (arg) {
3855     case 'v':
3856     printf("grubby version %s\n", VERSION);
3857     exit(0);
3858     break;
3859 niro 914 case 'i':
3860     if (extraInitrdCount < MAX_EXTRA_INITRDS) {
3861     extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
3862     } else {
3863     fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
3864     return 1;
3865     }
3866     break;
3867 niro 532 }
3868     }
3869    
3870     if (arg < -1) {
3871     fprintf(stderr, _("grubby: bad argument %s: %s\n"),
3872     poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
3873     poptStrerror(arg));
3874     return 1;
3875     }
3876    
3877     if ((chptr = poptGetArg(optCon))) {
3878     fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
3879     return 1;
3880     }
3881    
3882 niro 1696 if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
3883 niro 914 configureYaboot + configureSilo + configureZipl +
3884     configureExtLinux ) > 1) {
3885 niro 532 fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
3886     return 1;
3887     } else if (bootloaderProbe && grubConfig) {
3888     fprintf(stderr,
3889     _("grubby: cannot specify config file with --bootloader-probe\n"));
3890     return 1;
3891 niro 1696 } else if (configureGrub2) {
3892     cfi = &grub2ConfigType;
3893 niro 532 } else if (configureLilo) {
3894     cfi = &liloConfigType;
3895     } else if (configureGrub) {
3896     cfi = &grubConfigType;
3897     } else if (configureELilo) {
3898     cfi = &eliloConfigType;
3899     } else if (configureYaboot) {
3900     cfi = &yabootConfigType;
3901     } else if (configureSilo) {
3902     cfi = &siloConfigType;
3903     } else if (configureZipl) {
3904     cfi = &ziplConfigType;
3905 niro 914 } else if (configureExtLinux) {
3906     cfi = &extlinuxConfigType;
3907     useextlinuxmenu=1;
3908 niro 532 }
3909    
3910     if (!cfi) {
3911 niro 1802 if (grub2FindConfig(&grub2ConfigType))
3912     cfi = &grub2ConfigType;
3913     else
3914 niro 532 #ifdef __ia64__
3915 niro 1802 cfi = &eliloConfigType;
3916 niro 532 #elif __powerpc__
3917 niro 1802 cfi = &yabootConfigType;
3918 niro 532 #elif __sparc__
3919 niro 1802 cfi = &siloConfigType;
3920 niro 532 #elif __s390__
3921 niro 1802 cfi = &ziplConfigType;
3922 niro 532 #elif __s390x__
3923 niro 1802 cfi = &ziplConfigtype;
3924 niro 532 #else
3925 niro 1696 cfi = &grubConfigType;
3926 niro 532 #endif
3927     }
3928    
3929 niro 1696 if (!grubConfig) {
3930     if (cfi->findConfig)
3931     grubConfig = cfi->findConfig(cfi);
3932     if (!grubConfig)
3933     grubConfig = cfi->defaultConfig;
3934     }
3935 niro 532
3936     if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||
3937 niro 1859 newKernelPath || removeKernelPath || makeDefault ||
3938     defaultKernel || displayDefaultIndex || displayDefaultTitle ||
3939     (defaultIndex >= 0))) {
3940 niro 532 fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
3941     "specified option"));
3942     return 1;
3943     }
3944    
3945     if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||
3946     removeKernelPath)) {
3947     fprintf(stderr, _("grubby: --default-kernel and --info may not "
3948     "be used when adding or removing kernels\n"));
3949     return 1;
3950     }
3951    
3952     if (newKernelPath && !newKernelTitle) {
3953     fprintf(stderr, _("grubby: kernel title must be specified\n"));
3954     return 1;
3955 niro 1156 } else if (!newKernelPath && (newKernelTitle || copyDefault ||
3956     (newKernelInitrd && !updateKernelPath)||
3957 niro 914 makeDefault || extraInitrdCount > 0)) {
3958 niro 532 fprintf(stderr, _("grubby: kernel path expected\n"));
3959     return 1;
3960     }
3961    
3962     if (newKernelPath && updateKernelPath) {
3963     fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
3964     "not be used together"));
3965     return 1;
3966     }
3967    
3968     if (makeDefault && defaultKernel) {
3969     fprintf(stderr, _("grubby: --make-default and --default-kernel "
3970     "may not be used together\n"));
3971     return 1;
3972     } else if (defaultKernel && removeKernelPath &&
3973     !strcmp(defaultKernel, removeKernelPath)) {
3974     fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));
3975     return 1;
3976     } else if (defaultKernel && newKernelPath &&
3977     !strcmp(defaultKernel, newKernelPath)) {
3978     makeDefault = 1;
3979     defaultKernel = NULL;
3980     }
3981 niro 1859 else if (defaultKernel && (defaultIndex >= 0)) {
3982     fprintf(stderr, _("grubby: --set-default and --set-default-index "
3983     "may not be used together\n"));
3984     return 1;
3985     }
3986 niro 532
3987 niro 1717 if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
3988 niro 532 fprintf(stderr, _("grubby: output file must be specified if stdin "
3989     "is used\n"));
3990     return 1;
3991     }
3992    
3993     if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
3994 niro 1859 && !kernelInfo && !bootloaderProbe && !updateKernelPath
3995     && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle
3996     && (defaultIndex == -1)) {
3997 niro 532 fprintf(stderr, _("grubby: no action specified\n"));
3998     return 1;
3999     }
4000    
4001     flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
4002    
4003     if (cfi->needsBootPrefix) {
4004     if (!bootPrefix) {
4005     bootPrefix = findBootPrefix();
4006     if (!bootPrefix) return 1;
4007     } else {
4008     /* this shouldn't end with a / */
4009     if (bootPrefix[strlen(bootPrefix) - 1] == '/')
4010     bootPrefix[strlen(bootPrefix) - 1] = '\0';
4011     }
4012     } else {
4013     bootPrefix = "";
4014     }
4015    
4016 niro 914 if (!cfi->mbAllowExtraInitRds &&
4017     extraInitrdCount > 0) {
4018     fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
4019     return 1;
4020     }
4021    
4022 niro 532 if (bootloaderProbe) {
4023 niro 1854 int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
4024     struct grubConfig * lconfig, * gconfig, * yconfig, * econfig;
4025 niro 532
4026 niro 1696 const char *grub2config = grub2FindConfig(&grub2ConfigType);
4027     if (grub2config) {
4028     gconfig = readConfig(grub2config, &grub2ConfigType);
4029     if (!gconfig)
4030     gr2c = 1;
4031     else
4032     gr2c = checkForGrub2(gconfig);
4033     }
4034    
4035 niro 1715 const char *grubconfig = grubFindConfig(&grubConfigType);
4036     if (!access(grubconfig, F_OK)) {
4037     gconfig = readConfig(grubconfig, &grubConfigType);
4038 niro 532 if (!gconfig)
4039     grc = 1;
4040     else
4041     grc = checkForGrub(gconfig);
4042     }
4043    
4044     if (!access(liloConfigType.defaultConfig, F_OK)) {
4045     lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);
4046     if (!lconfig)
4047     lrc = 1;
4048     else
4049     lrc = checkForLilo(lconfig);
4050     }
4051    
4052 niro 1854 if (!access(eliloConfigType.defaultConfig, F_OK)) {
4053     econfig = readConfig(eliloConfigType.defaultConfig,
4054     &eliloConfigType);
4055     if (!econfig)
4056     erc = 1;
4057     else
4058     erc = checkForElilo(econfig);
4059     }
4060    
4061 niro 914 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
4062     lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
4063     if (!lconfig)
4064 niro 1854 extrc = 1;
4065 niro 914 else
4066 niro 1854 extrc = checkForExtLinux(lconfig);
4067 niro 914 }
4068    
4069 niro 532
4070 niro 1853 if (!access(yabootConfigType.defaultConfig, F_OK)) {
4071     yconfig = readConfig(yabootConfigType.defaultConfig,
4072     &yabootConfigType);
4073     if (!yconfig)
4074     yrc = 1;
4075     else
4076 niro 1864 yrc = checkForYaboot(yconfig);
4077 niro 1853 }
4078    
4079 niro 1854 if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1 ||
4080 niro 1855 erc == 1)
4081 niro 1854 return 1;
4082 niro 1853
4083 niro 532 if (lrc == 2) printf("lilo\n");
4084 niro 1696 if (gr2c == 2) printf("grub2\n");
4085 niro 532 if (grc == 2) printf("grub\n");
4086 niro 1854 if (extrc == 2) printf("extlinux\n");
4087 niro 1853 if (yrc == 2) printf("yaboot\n");
4088 niro 1854 if (erc == 2) printf("elilo\n");
4089 niro 532
4090     return 0;
4091     }
4092    
4093     config = readConfig(grubConfig, cfi);
4094     if (!config) return 1;
4095    
4096     if (displayDefault) {
4097     struct singleLine * line;
4098     struct singleEntry * entry;
4099     char * rootspec;
4100    
4101     if (config->defaultImage == -1) return 0;
4102     entry = findEntryByIndex(config, config->defaultImage);
4103     if (!entry) return 0;
4104     if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
4105    
4106 niro 914 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
4107 niro 532 if (!line) return 0;
4108    
4109     rootspec = getRootSpecifier(line->elements[1].item);
4110     printf("%s%s\n", bootPrefix, line->elements[1].item +
4111     ((rootspec != NULL) ? strlen(rootspec) : 0));
4112    
4113     return 0;
4114 niro 1720
4115 niro 1721 } else if (displayDefaultTitle) {
4116     struct singleLine * line;
4117     struct singleEntry * entry;
4118    
4119     if (config->defaultImage == -1) return 0;
4120     entry = findEntryByIndex(config, config->defaultImage);
4121     if (!entry) return 0;
4122    
4123     if (!configureGrub2) {
4124     line = getLineByType(LT_TITLE, entry->lines);
4125     if (!line) return 0;
4126     printf("%s\n", line->elements[1].item);
4127    
4128     } else {
4129 niro 1746 char * title;
4130 niro 1721
4131     dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
4132     line = getLineByType(LT_MENUENTRY, entry->lines);
4133     if (!line) return 0;
4134 niro 1746 title = grub2ExtractTitle(line);
4135     if (title)
4136     printf("%s\n", title);
4137 niro 1721 }
4138     return 0;
4139    
4140 niro 1720 } else if (displayDefaultIndex) {
4141     if (config->defaultImage == -1) return 0;
4142     printf("%i\n", config->defaultImage);
4143    
4144 niro 532 } else if (kernelInfo)
4145     return displayInfo(config, kernelInfo, bootPrefix);
4146    
4147     if (copyDefault) {
4148     template = findTemplate(config, bootPrefix, NULL, 0, flags);
4149     if (!template) return 1;
4150     }
4151    
4152     markRemovedImage(config, removeKernelPath, bootPrefix);
4153     markRemovedImage(config, removeMBKernel, bootPrefix);
4154     setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
4155 niro 1859 bootPrefix, flags, defaultIndex);
4156 niro 532 setFallbackImage(config, newKernelPath != NULL);
4157     if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
4158     removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
4159 niro 1156 if (updateKernelPath && newKernelInitrd) {
4160     if (updateInitrd(config, updateKernelPath, bootPrefix,
4161     newKernelInitrd)) return 1;
4162     }
4163 niro 532 if (addNewKernel(config, template, bootPrefix, newKernelPath,
4164     newKernelTitle, newKernelArgs, newKernelInitrd,
4165 niro 1844 (const char **)extraInitrds, extraInitrdCount,
4166 niro 532 newMBKernel, newMBKernelArgs)) return 1;
4167    
4168    
4169     if (numEntries(config) == 0) {
4170     fprintf(stderr, _("grubby: doing this would leave no kernel entries. "
4171     "Not writing out new config.\n"));
4172     return 1;
4173     }
4174    
4175     if (!outputFile)
4176 niro 1696 outputFile = (char *)grubConfig;
4177 niro 532
4178     return writeConfig(config, outputFile, bootPrefix);
4179     }