Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1750 - (hide annotations) (download)
Sat Feb 18 01:09:51 2012 UTC (12 years, 2 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 104863 byte(s)
Fix endswith() to correctly test its input for validity.


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