Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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