Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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