Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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