Magellan Linux

Annotation of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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