Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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