Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1868 - (hide annotations) (download)
Mon Jul 2 13:22:30 2012 UTC (11 years, 9 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 116616 byte(s)
Fix minor memory leak introduced by previous fix.
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     "/boot/grub/grub.conf",
168     "/boot/grub/menu.lst",
169 niro 1852 "/etc/grub.conf",
170 niro 1715 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 1868 char *dir = strdupa(outName);
1314     rc = chdir(dirname(dir));
1315     free(dir);
1316 niro 532 do {
1317     buf = alloca(len + 1);
1318 niro 914 rc = readlink(basename(outName), buf, len);
1319 niro 532 if (rc == len) len += 256;
1320     } while (rc == len);
1321    
1322     if (rc < 0) {
1323     fprintf(stderr, _("grubby: error readlink link %s: %s\n"),
1324     outName, strerror(errno));
1325     return 1;
1326     }
1327    
1328     outName = buf;
1329     outName[rc] = '\0';
1330     }
1331    
1332     tmpOutName = alloca(strlen(outName) + 2);
1333     sprintf(tmpOutName, "%s-", outName);
1334     out = fopen(tmpOutName, "w");
1335     if (!out) {
1336     fprintf(stderr, _("grubby: error creating %s: %s\n"), tmpOutName,
1337     strerror(errno));
1338     return 1;
1339     }
1340    
1341     if (!stat(outName, &sb)) {
1342     if (chmod(tmpOutName, sb.st_mode & ~(S_IFMT))) {
1343     fprintf(stderr, _("grubby: error setting perms on %s: %s\n"),
1344     tmpOutName, strerror(errno));
1345     fclose(out);
1346     unlink(tmpOutName);
1347     return 1;
1348     }
1349     }
1350     }
1351    
1352     line = cfg->theLines;
1353 niro 1696 struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi);
1354 niro 532 while (line) {
1355 niro 1696 if (line->type == LT_SET_VARIABLE && defaultKw &&
1356     line->numElements == 3 &&
1357     !strcmp(line->elements[1].item, defaultKw->key)) {
1358 niro 532 writeDefault(out, line->indent, line->elements[0].indent, cfg);
1359     needs &= ~MAIN_DEFAULT;
1360 niro 1696 } else if (line->type == LT_DEFAULT) {
1361     writeDefault(out, line->indent, line->elements[0].indent, cfg);
1362     needs &= ~MAIN_DEFAULT;
1363 niro 532 } else if (line->type == LT_FALLBACK) {
1364     if (cfg->fallbackImage > -1)
1365     fprintf(out, "%s%s%s%d\n", line->indent,
1366     line->elements[0].item, line->elements[0].indent,
1367     cfg->fallbackImage);
1368     } else {
1369     if (lineWrite(out, line, cfg->cfi) == -1) {
1370     fprintf(stderr, _("grubby: error writing %s: %s\n"),
1371     tmpOutName, strerror(errno));
1372     fclose(out);
1373     unlink(tmpOutName);
1374     return 1;
1375     }
1376     }
1377    
1378     line = line->next;
1379     }
1380    
1381     if (needs & MAIN_DEFAULT) {
1382     writeDefault(out, cfg->primaryIndent, "=", cfg);
1383     needs &= ~MAIN_DEFAULT;
1384     }
1385    
1386     i = 0;
1387     while ((entry = findEntryByIndex(cfg, i++))) {
1388     if (entry->skip) continue;
1389    
1390     line = entry->lines;
1391     while (line) {
1392     if (lineWrite(out, line, cfg->cfi) == -1) {
1393     fprintf(stderr, _("grubby: error writing %s: %s\n"),
1394     tmpOutName, strerror(errno));
1395     fclose(out);
1396     unlink(tmpOutName);
1397     return 1;
1398     }
1399     line = line->next;
1400     }
1401     }
1402    
1403     if (tmpOutName) {
1404     if (rename(tmpOutName, outName)) {
1405     fprintf(stderr, _("grubby: error moving %s to %s: %s\n"),
1406     tmpOutName, outName, strerror(errno));
1407     unlink(outName);
1408     return 1;
1409     }
1410     }
1411    
1412     return 0;
1413     }
1414    
1415     static int numEntries(struct grubConfig *cfg) {
1416     int i = 0;
1417     struct singleEntry * entry;
1418    
1419     entry = cfg->entries;
1420     while (entry) {
1421     if (!entry->skip)
1422     i++;
1423     entry = entry->next;
1424     }
1425     return i;
1426     }
1427    
1428 niro 1156 static char *findDiskForRoot()
1429     {
1430     int fd;
1431     char buf[65536];
1432     char *devname;
1433     char *chptr;
1434     int rc;
1435    
1436     if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {
1437     fprintf(stderr, "grubby: failed to open %s: %s\n",
1438     _PATH_MOUNTED, strerror(errno));
1439     return NULL;
1440     }
1441    
1442     rc = read(fd, buf, sizeof(buf) - 1);
1443     if (rc <= 0) {
1444     fprintf(stderr, "grubby: failed to read %s: %s\n",
1445     _PATH_MOUNTED, strerror(errno));
1446     close(fd);
1447     return NULL;
1448     }
1449     close(fd);
1450     buf[rc] = '\0';
1451     chptr = buf;
1452    
1453 niro 1841 char *foundanswer = NULL;
1454    
1455 niro 1156 while (chptr && chptr != buf+rc) {
1456     devname = chptr;
1457    
1458     /*
1459     * The first column of a mtab entry is the device, but if the entry is a
1460     * special device it won't start with /, so move on to the next line.
1461     */
1462     if (*devname != '/') {
1463     chptr = strchr(chptr, '\n');
1464     if (chptr)
1465     chptr++;
1466     continue;
1467     }
1468    
1469     /* Seek to the next space */
1470     chptr = strchr(chptr, ' ');
1471     if (!chptr) {
1472     fprintf(stderr, "grubby: error parsing %s: %s\n",
1473     _PATH_MOUNTED, strerror(errno));
1474     return NULL;
1475     }
1476    
1477     /*
1478     * The second column of a mtab entry is the mount point, we are looking
1479     * for '/' obviously.
1480     */
1481     if (*(++chptr) == '/' && *(++chptr) == ' ') {
1482 niro 1841 /* remember the last / entry in mtab */
1483     foundanswer = devname;
1484 niro 1156 }
1485    
1486     /* Next line */
1487     chptr = strchr(chptr, '\n');
1488     if (chptr)
1489     chptr++;
1490     }
1491    
1492 niro 1841 /* Return the last / entry found */
1493     if (foundanswer) {
1494     chptr = strchr(foundanswer, ' ');
1495     *chptr = '\0';
1496     return strdup(foundanswer);
1497     }
1498    
1499 niro 1156 return NULL;
1500     }
1501    
1502 niro 1736 void printEntry(struct singleEntry * entry) {
1503     int i;
1504     struct singleLine * line;
1505    
1506     for (line = entry->lines; line; line = line->next) {
1507     fprintf(stderr, "DBG: %s", line->indent);
1508     for (i = 0; i < line->numElements; i++) {
1509 niro 1801 /* Need to handle this, because we strip the quotes from
1510     * menuentry when read it. */
1511     if (line->type == LT_MENUENTRY && i == 1) {
1512     if(!isquote(*line->elements[i].item))
1513     fprintf(stderr, "\'%s\'", line->elements[i].item);
1514     else
1515     fprintf(stderr, "%s", line->elements[i].item);
1516     fprintf(stderr, "%s", line->elements[i].indent);
1517    
1518     continue;
1519     }
1520    
1521     fprintf(stderr, "%s%s",
1522 niro 1736 line->elements[i].item, line->elements[i].indent);
1523     }
1524     fprintf(stderr, "\n");
1525     }
1526     }
1527    
1528     void notSuitablePrintf(struct singleEntry * entry, const char *fmt, ...)
1529     {
1530     va_list argp;
1531    
1532     if (!debug)
1533     return;
1534    
1535     va_start(argp, fmt);
1536     fprintf(stderr, "DBG: Image entry failed: ");
1537     vfprintf(stderr, fmt, argp);
1538     printEntry(entry);
1539     va_end(argp);
1540     }
1541    
1542 niro 1745 #define beginswith(s, c) ((s) && (s)[0] == (c))
1543    
1544     static int endswith(const char *s, char c)
1545     {
1546     int slen;
1547    
1548 niro 1750 if (!s || !s[0])
1549 niro 1745 return 0;
1550     slen = strlen(s) - 1;
1551    
1552     return s[slen] == c;
1553     }
1554    
1555 niro 532 int suitableImage(struct singleEntry * entry, const char * bootPrefix,
1556     int skipRemoved, int flags) {
1557     struct singleLine * line;
1558     char * fullName;
1559     int i;
1560     char * dev;
1561     char * rootspec;
1562 niro 1156 char * rootdev;
1563 niro 532
1564 niro 1736 if (skipRemoved && entry->skip) {
1565     notSuitablePrintf(entry, "marked to skip\n");
1566     return 0;
1567     }
1568 niro 532
1569 niro 914 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1570 niro 1736 if (!line) {
1571     notSuitablePrintf(entry, "no line found\n");
1572     return 0;
1573     }
1574     if (line->numElements < 2) {
1575     notSuitablePrintf(entry, "line has only %d elements\n",
1576     line->numElements);
1577     return 0;
1578     }
1579 niro 914
1580 niro 532 if (flags & GRUBBY_BADIMAGE_OKAY) return 1;
1581    
1582     fullName = alloca(strlen(bootPrefix) +
1583     strlen(line->elements[1].item) + 1);
1584     rootspec = getRootSpecifier(line->elements[1].item);
1585 niro 1745 int rootspec_offset = rootspec ? strlen(rootspec) : 0;
1586     int hasslash = endswith(bootPrefix, '/') ||
1587     beginswith(line->elements[1].item + rootspec_offset, '/');
1588     sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/",
1589     line->elements[1].item + rootspec_offset);
1590 niro 1736 if (access(fullName, R_OK)) {
1591     notSuitablePrintf(entry, "access to %s failed\n", fullName);
1592     return 0;
1593     }
1594 niro 532 for (i = 2; i < line->numElements; i++)
1595     if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1596     if (i < line->numElements) {
1597     dev = line->elements[i].item + 5;
1598     } else {
1599     /* look for a lilo style LT_ROOT line */
1600 niro 914 line = getLineByType(LT_ROOT, entry->lines);
1601 niro 532
1602     if (line && line->numElements >= 2) {
1603     dev = line->elements[1].item;
1604     } else {
1605 niro 914 /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS.
1606     * grub+multiboot uses LT_MBMODULE for the args, so check that too.
1607     */
1608     line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines);
1609 niro 532
1610     /* failed to find one */
1611 niro 1736 if (!line) {
1612     notSuitablePrintf(entry, "no line found\n");
1613     return 0;
1614     }
1615 niro 532
1616     for (i = 1; i < line->numElements; i++)
1617     if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1618     if (i < line->numElements)
1619     dev = line->elements[i].item + 5;
1620     else {
1621 niro 1736 notSuitablePrintf(entry, "no root= entry found\n");
1622 niro 532 /* it failed too... can't find root= */
1623     return 0;
1624     }
1625     }
1626     }
1627    
1628 niro 914 dev = getpathbyspec(dev);
1629 niro 1736 if (!getpathbyspec(dev)) {
1630     notSuitablePrintf(entry, "can't find blkid entry for %s\n", dev);
1631 niro 914 return 0;
1632 niro 1736 } else
1633     dev = getpathbyspec(dev);
1634 niro 532
1635 niro 1156 rootdev = findDiskForRoot();
1636 niro 1736 if (!rootdev) {
1637     notSuitablePrintf(entry, "can't find root device\n");
1638 niro 914 return 0;
1639 niro 1736 }
1640 niro 914
1641 niro 1177 if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {
1642 niro 1736 notSuitablePrintf(entry, "uuid missing: rootdev %s, dev %s\n",
1643     getuuidbydev(rootdev), getuuidbydev(dev));
1644 niro 1177 free(rootdev);
1645     return 0;
1646     }
1647 niro 532
1648 niro 1156 if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
1649 niro 1736 notSuitablePrintf(entry, "uuid mismatch: rootdev %s, dev %s\n",
1650     getuuidbydev(rootdev), getuuidbydev(dev));
1651 niro 1156 free(rootdev);
1652 niro 914 return 0;
1653 niro 1156 }
1654 niro 532
1655 niro 1156 free(rootdev);
1656    
1657 niro 532 return 1;
1658     }
1659    
1660     /* returns the first match on or after the one pointed to by index (if index
1661     is not NULL) which is not marked as skip */
1662     struct singleEntry * findEntryByPath(struct grubConfig * config,
1663     const char * kernel, const char * prefix,
1664     int * index) {
1665     struct singleEntry * entry = NULL;
1666     struct singleLine * line;
1667     int i;
1668     char * chptr;
1669     char * rootspec = NULL;
1670     enum lineType_e checkType = LT_KERNEL;
1671    
1672     if (isdigit(*kernel)) {
1673     int * indexVars = alloca(sizeof(*indexVars) * strlen(kernel));
1674    
1675     i = 0;
1676     indexVars[i] = strtol(kernel, &chptr, 10);
1677     while (*chptr == ',') {
1678     i++;
1679     kernel = chptr + 1;
1680     indexVars[i] = strtol(kernel, &chptr, 10);
1681     }
1682    
1683     if (*chptr) {
1684     /* can't parse it, bail */
1685     return NULL;
1686     }
1687    
1688     indexVars[i + 1] = -1;
1689    
1690     i = 0;
1691     if (index) {
1692     while (i < *index) i++;
1693     if (indexVars[i] == -1) return NULL;
1694     }
1695    
1696     entry = findEntryByIndex(config, indexVars[i]);
1697     if (!entry) return NULL;
1698    
1699 niro 914 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1700 niro 532 if (!line) return NULL;
1701    
1702     if (index) *index = indexVars[i];
1703     return entry;
1704     }
1705    
1706     if (!strcmp(kernel, "DEFAULT")) {
1707     if (index && *index > config->defaultImage) {
1708     entry = NULL;
1709     } else {
1710     entry = findEntryByIndex(config, config->defaultImage);
1711     if (entry && entry->skip)
1712     entry = NULL;
1713     else if (index)
1714     *index = config->defaultImage;
1715     }
1716     } else if (!strcmp(kernel, "ALL")) {
1717     if (index)
1718     i = *index;
1719     else
1720     i = 0;
1721    
1722     while ((entry = findEntryByIndex(config, i))) {
1723     if (!entry->skip) break;
1724     i++;
1725     }
1726    
1727     if (entry && index)
1728     *index = i;
1729     } else {
1730     if (index)
1731     i = *index;
1732     else
1733     i = 0;
1734    
1735     if (!strncmp(kernel, "TITLE=", 6)) {
1736     prefix = "";
1737 niro 1801 checkType = LT_TITLE|LT_MENUENTRY;
1738 niro 532 kernel += 6;
1739     }
1740    
1741 niro 914 for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {
1742     if (entry->skip) continue;
1743 niro 532
1744 niro 914 dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);
1745 niro 532
1746 niro 914 /* check all the lines matching checkType */
1747     for (line = entry->lines; line; line = line->next) {
1748     line = getLineByType(entry->multiboot && checkType == LT_KERNEL ?
1749     LT_KERNEL|LT_MBMODULE|LT_HYPER :
1750     checkType, line);
1751     if (!line) break; /* not found in this entry */
1752 niro 532
1753 niro 1801 if (line && line->type != LT_MENUENTRY &&
1754     line->numElements >= 2) {
1755 niro 914 rootspec = getRootSpecifier(line->elements[1].item);
1756     if (!strcmp(line->elements[1].item +
1757     ((rootspec != NULL) ? strlen(rootspec) : 0),
1758     kernel + strlen(prefix)))
1759     break;
1760     }
1761 niro 1801 if(line->type == LT_MENUENTRY &&
1762     !strcmp(line->elements[1].item, kernel))
1763     break;
1764 niro 914 }
1765    
1766     /* make sure this entry has a kernel identifier; this skips
1767     * non-Linux boot entries (could find netbsd etc, though, which is
1768     * unfortunate)
1769     */
1770     if (line && getLineByType(LT_KERNEL|LT_HYPER, entry->lines))
1771     break; /* found 'im! */
1772 niro 532 }
1773    
1774     if (index) *index = i;
1775     }
1776    
1777     return entry;
1778     }
1779    
1780     struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index) {
1781     struct singleEntry * entry;
1782    
1783     entry = cfg->entries;
1784     while (index && entry) {
1785     entry = entry->next;
1786     index--;
1787     }
1788    
1789     return entry;
1790     }
1791    
1792     /* Find a good template to use for the new kernel. An entry is
1793     * good if the kernel and mkinitrd exist (even if the entry
1794     * is going to be removed). Try and use the default entry, but
1795     * if that doesn't work just take the first. If we can't find one,
1796     * bail. */
1797     struct singleEntry * findTemplate(struct grubConfig * cfg, const char * prefix,
1798     int * indexPtr, int skipRemoved, int flags) {
1799     struct singleEntry * entry, * entry2;
1800     int index;
1801    
1802     if (cfg->defaultImage > -1) {
1803     entry = findEntryByIndex(cfg, cfg->defaultImage);
1804     if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
1805     if (indexPtr) *indexPtr = cfg->defaultImage;
1806     return entry;
1807     }
1808     }
1809    
1810     index = 0;
1811     while ((entry = findEntryByIndex(cfg, index))) {
1812     if (suitableImage(entry, prefix, skipRemoved, flags)) {
1813     int j;
1814     for (j = 0; j < index; j++) {
1815     entry2 = findEntryByIndex(cfg, j);
1816     if (entry2->skip) index--;
1817     }
1818     if (indexPtr) *indexPtr = index;
1819    
1820     return entry;
1821     }
1822    
1823     index++;
1824     }
1825    
1826     fprintf(stderr, _("grubby fatal error: unable to find a suitable template\n"));
1827    
1828     return NULL;
1829     }
1830    
1831     char * findBootPrefix(void) {
1832     struct stat sb, sb2;
1833    
1834     stat("/", &sb);
1835     #ifdef __ia64__
1836     stat("/boot/efi/EFI/redhat/", &sb2);
1837     #else
1838     stat("/boot", &sb2);
1839     #endif
1840    
1841     if (sb.st_dev == sb2.st_dev)
1842     return strdup("");
1843    
1844     #ifdef __ia64__
1845     return strdup("/boot/efi/EFI/redhat/");
1846     #else
1847     return strdup("/boot");
1848     #endif
1849     }
1850    
1851     void markRemovedImage(struct grubConfig * cfg, const char * image,
1852     const char * prefix) {
1853     struct singleEntry * entry;
1854    
1855 niro 1801 if (!image)
1856     return;
1857 niro 532
1858 niro 1801 /* check and see if we're removing the default image */
1859     if (isdigit(*image)) {
1860     entry = findEntryByPath(cfg, image, prefix, NULL);
1861     if(entry)
1862     entry->skip = 1;
1863     return;
1864     }
1865    
1866 niro 532 while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
1867     entry->skip = 1;
1868     }
1869    
1870     void setDefaultImage(struct grubConfig * config, int hasNew,
1871     const char * defaultKernelPath, int newIsDefault,
1872 niro 1859 const char * prefix, int flags, int index) {
1873 niro 532 struct singleEntry * entry, * entry2, * newDefault;
1874     int i, j;
1875    
1876     if (newIsDefault) {
1877     config->defaultImage = 0;
1878     return;
1879 niro 1859 } else if ((index >= 0) && config->cfi->defaultIsIndex) {
1880     if (findEntryByIndex(config, index))
1881     config->defaultImage = index;
1882     else
1883     config->defaultImage = -1;
1884     return;
1885 niro 532 } else if (defaultKernelPath) {
1886     i = 0;
1887     if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
1888     config->defaultImage = i;
1889     } else {
1890     config->defaultImage = -1;
1891     return;
1892     }
1893     }
1894    
1895     /* defaultImage now points to what we'd like to use, but before any order
1896     changes */
1897 niro 1748 if ((config->defaultImage == DEFAULT_SAVED) ||
1898     (config->defaultImage == DEFAULT_SAVED_GRUB2))
1899 niro 532 /* default is set to saved, we don't want to change it */
1900     return;
1901    
1902     if (config->defaultImage > -1)
1903     entry = findEntryByIndex(config, config->defaultImage);
1904     else
1905     entry = NULL;
1906    
1907     if (entry && !entry->skip) {
1908     /* we can preserve the default */
1909     if (hasNew)
1910     config->defaultImage++;
1911    
1912     /* count the number of entries erased before this one */
1913     for (j = 0; j < config->defaultImage; j++) {
1914     entry2 = findEntryByIndex(config, j);
1915     if (entry2->skip) config->defaultImage--;
1916     }
1917     } else if (hasNew) {
1918     config->defaultImage = 0;
1919     } else {
1920     /* Either we just erased the default (or the default line was bad
1921     * to begin with) and didn't put a new one in. We'll use the first
1922     * valid image. */
1923     newDefault = findTemplate(config, prefix, &config->defaultImage, 1,
1924     flags);
1925     if (!newDefault)
1926     config->defaultImage = -1;
1927     }
1928     }
1929    
1930     void setFallbackImage(struct grubConfig * config, int hasNew) {
1931     struct singleEntry * entry, * entry2;
1932     int j;
1933    
1934     if (config->fallbackImage == -1) return;
1935    
1936     entry = findEntryByIndex(config, config->fallbackImage);
1937     if (!entry || entry->skip) {
1938     config->fallbackImage = -1;
1939     return;
1940     }
1941    
1942     if (hasNew)
1943     config->fallbackImage++;
1944    
1945     /* count the number of entries erased before this one */
1946     for (j = 0; j < config->fallbackImage; j++) {
1947     entry2 = findEntryByIndex(config, j);
1948     if (entry2->skip) config->fallbackImage--;
1949     }
1950     }
1951    
1952     void displayEntry(struct singleEntry * entry, const char * prefix, int index) {
1953     struct singleLine * line;
1954     char * root = NULL;
1955     int i;
1956    
1957     printf("index=%d\n", index);
1958    
1959 niro 914 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1960     if (!line) {
1961     printf("non linux entry\n");
1962     return;
1963     }
1964    
1965 niro 1849 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
1966     printf("kernel=%s\n", line->elements[1].item);
1967     else
1968     printf("kernel=%s%s\n", prefix, line->elements[1].item);
1969 niro 532
1970     if (line->numElements >= 3) {
1971     printf("args=\"");
1972     i = 2;
1973     while (i < line->numElements) {
1974     if (!strncmp(line->elements[i].item, "root=", 5)) {
1975     root = line->elements[i].item + 5;
1976     } else {
1977     printf("%s%s", line->elements[i].item,
1978     line->elements[i].indent);
1979     }
1980    
1981     i++;
1982     }
1983     printf("\"\n");
1984     } else {
1985 niro 914 line = getLineByType(LT_KERNELARGS, entry->lines);
1986 niro 532 if (line) {
1987     char * s;
1988    
1989     printf("args=\"");
1990     i = 1;
1991     while (i < line->numElements) {
1992     if (!strncmp(line->elements[i].item, "root=", 5)) {
1993     root = line->elements[i].item + 5;
1994     } else {
1995     s = line->elements[i].item;
1996    
1997     printf("%s%s", s, line->elements[i].indent);
1998     }
1999    
2000     i++;
2001     }
2002    
2003     s = line->elements[i - 1].indent;
2004     printf("\"\n");
2005     }
2006     }
2007    
2008     if (!root) {
2009 niro 914 line = getLineByType(LT_ROOT, entry->lines);
2010 niro 532 if (line && line->numElements >= 2)
2011     root=line->elements[1].item;
2012     }
2013    
2014     if (root) {
2015     char * s = alloca(strlen(root) + 1);
2016    
2017     strcpy(s, root);
2018     if (s[strlen(s) - 1] == '"')
2019     s[strlen(s) - 1] = '\0';
2020     /* make sure the root doesn't have a trailing " */
2021     printf("root=%s\n", s);
2022     }
2023    
2024 niro 914 line = getLineByType(LT_INITRD, entry->lines);
2025 niro 532
2026     if (line && line->numElements >= 2) {
2027 niro 1849 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2028     printf("initrd=");
2029     else
2030     printf("initrd=%s", prefix);
2031    
2032 niro 532 for (i = 1; i < line->numElements; i++)
2033     printf("%s%s", line->elements[i].item, line->elements[i].indent);
2034     printf("\n");
2035     }
2036 niro 1751
2037     line = getLineByType(LT_TITLE, entry->lines);
2038     if (line) {
2039     printf("title=%s\n", line->elements[1].item);
2040     } else {
2041     char * title;
2042     line = getLineByType(LT_MENUENTRY, entry->lines);
2043     title = grub2ExtractTitle(line);
2044     if (title)
2045     printf("title=%s\n", title);
2046     }
2047 niro 532 }
2048    
2049 niro 1850 int isSuseSystem(void) {
2050     const char * path;
2051     const static char default_path[] = "/etc/SuSE-release";
2052    
2053     if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2054     path = default_path;
2055    
2056     if (!access(path, R_OK))
2057     return 1;
2058     return 0;
2059     }
2060    
2061     int isSuseGrubConf(const char * path) {
2062     FILE * grubConf;
2063     char * line = NULL;
2064     size_t len = 0, res = 0;
2065    
2066     grubConf = fopen(path, "r");
2067     if (!grubConf) {
2068     dbgPrintf("Could not open SuSE configuration file '%s'\n", path);
2069     return 0;
2070     }
2071    
2072     while ((res = getline(&line, &len, grubConf)) != -1) {
2073     if (!strncmp(line, "setup", 5)) {
2074     fclose(grubConf);
2075     free(line);
2076     return 1;
2077     }
2078     }
2079    
2080     dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2081     path);
2082    
2083     fclose(grubConf);
2084     free(line);
2085     return 0;
2086     }
2087    
2088     int suseGrubConfGetLba(const char * path, int * lbaPtr) {
2089     FILE * grubConf;
2090     char * line = NULL;
2091     size_t res = 0, len = 0;
2092    
2093     if (!path) return 1;
2094     if (!lbaPtr) return 1;
2095    
2096     grubConf = fopen(path, "r");
2097     if (!grubConf) return 1;
2098    
2099     while ((res = getline(&line, &len, grubConf)) != -1) {
2100     if (line[res - 1] == '\n')
2101     line[res - 1] = '\0';
2102     else if (len > res)
2103     line[res] = '\0';
2104     else {
2105     line = realloc(line, res + 1);
2106     line[res] = '\0';
2107     }
2108    
2109     if (!strncmp(line, "setup", 5)) {
2110     if (strstr(line, "--force-lba")) {
2111     *lbaPtr = 1;
2112     } else {
2113     *lbaPtr = 0;
2114     }
2115     dbgPrintf("lba: %i\n", *lbaPtr);
2116     break;
2117     }
2118     }
2119    
2120     free(line);
2121     fclose(grubConf);
2122     return 0;
2123     }
2124    
2125     int suseGrubConfGetInstallDevice(const char * path, char ** devicePtr) {
2126     FILE * grubConf;
2127     char * line = NULL;
2128     size_t res = 0, len = 0;
2129     char * lastParamPtr = NULL;
2130     char * secLastParamPtr = NULL;
2131     char installDeviceNumber = '\0';
2132     char * bounds = NULL;
2133    
2134     if (!path) return 1;
2135     if (!devicePtr) return 1;
2136    
2137     grubConf = fopen(path, "r");
2138     if (!grubConf) return 1;
2139    
2140     while ((res = getline(&line, &len, grubConf)) != -1) {
2141     if (strncmp(line, "setup", 5))
2142     continue;
2143    
2144     if (line[res - 1] == '\n')
2145     line[res - 1] = '\0';
2146     else if (len > res)
2147     line[res] = '\0';
2148     else {
2149     line = realloc(line, res + 1);
2150     line[res] = '\0';
2151     }
2152    
2153     lastParamPtr = bounds = line + res;
2154    
2155     /* Last parameter in grub may be an optional IMAGE_DEVICE */
2156     while (!isspace(*lastParamPtr))
2157     lastParamPtr--;
2158     lastParamPtr++;
2159    
2160     secLastParamPtr = lastParamPtr - 2;
2161     dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2162    
2163     if (lastParamPtr + 3 > bounds) {
2164     dbgPrintf("lastParamPtr going over boundary");
2165     fclose(grubConf);
2166     free(line);
2167     return 1;
2168     }
2169     if (!strncmp(lastParamPtr, "(hd", 3))
2170     lastParamPtr += 3;
2171     dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2172    
2173     /*
2174     * Second last parameter will decide wether last parameter is
2175     * an IMAGE_DEVICE or INSTALL_DEVICE
2176     */
2177     while (!isspace(*secLastParamPtr))
2178     secLastParamPtr--;
2179     secLastParamPtr++;
2180    
2181     if (secLastParamPtr + 3 > bounds) {
2182     dbgPrintf("secLastParamPtr going over boundary");
2183     fclose(grubConf);
2184     free(line);
2185     return 1;
2186     }
2187     dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2188     if (!strncmp(secLastParamPtr, "(hd", 3)) {
2189     secLastParamPtr += 3;
2190     dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
2191     installDeviceNumber = *secLastParamPtr;
2192     } else {
2193     installDeviceNumber = *lastParamPtr;
2194     }
2195    
2196     *devicePtr = malloc(6);
2197     snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
2198     dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
2199     fclose(grubConf);
2200     free(line);
2201     return 0;
2202     }
2203    
2204     free(line);
2205     fclose(grubConf);
2206     return 1;
2207     }
2208    
2209     int grubGetBootFromDeviceMap(const char * device,
2210     char ** bootPtr) {
2211     FILE * deviceMap;
2212     char * line = NULL;
2213     size_t res = 0, len = 0;
2214     char * devicePtr;
2215     char * bounds = NULL;
2216     const char * path;
2217     const static char default_path[] = "/boot/grub/device.map";
2218    
2219     if (!device) return 1;
2220     if (!bootPtr) return 1;
2221    
2222     if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2223     path = default_path;
2224    
2225     dbgPrintf("opening grub device.map file from: %s\n", path);
2226     deviceMap = fopen(path, "r");
2227     if (!deviceMap)
2228     return 1;
2229    
2230     while ((res = getline(&line, &len, deviceMap)) != -1) {
2231     if (!strncmp(line, "#", 1))
2232     continue;
2233    
2234     if (line[res - 1] == '\n')
2235     line[res - 1] = '\0';
2236     else if (len > res)
2237     line[res] = '\0';
2238     else {
2239     line = realloc(line, res + 1);
2240     line[res] = '\0';
2241     }
2242    
2243     devicePtr = line;
2244     bounds = line + res;
2245    
2246     while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2247     devicePtr++;
2248     dbgPrintf("device: %s\n", devicePtr);
2249    
2250     if (!strncmp(devicePtr, device, strlen(device))) {
2251     devicePtr += strlen(device);
2252     while (isspace(*devicePtr) && ((devicePtr + 1) <= bounds))
2253     devicePtr++;
2254    
2255     *bootPtr = strdup(devicePtr);
2256     break;
2257     }
2258     }
2259    
2260     free(line);
2261     fclose(deviceMap);
2262     return 0;
2263     }
2264    
2265     int suseGrubConfGetBoot(const char * path, char ** bootPtr) {
2266     char * grubDevice;
2267    
2268     if (suseGrubConfGetInstallDevice(path, &grubDevice))
2269     dbgPrintf("error looking for grub installation device\n");
2270     else
2271     dbgPrintf("grubby installation device: %s\n", grubDevice);
2272    
2273     if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
2274     dbgPrintf("error looking for grub boot device\n");
2275     else
2276     dbgPrintf("grubby boot device: %s\n", *bootPtr);
2277    
2278     free(grubDevice);
2279     return 0;
2280     }
2281    
2282     int parseSuseGrubConf(int * lbaPtr, char ** bootPtr) {
2283     /*
2284     * This SuSE grub configuration file at this location is not your average
2285     * grub configuration file, but instead the grub commands used to setup
2286     * grub on that system.
2287     */
2288     const char * path;
2289     const static char default_path[] = "/etc/grub.conf";
2290    
2291     if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
2292     path = default_path;
2293    
2294     if (!isSuseGrubConf(path)) return 1;
2295    
2296     if (lbaPtr) {
2297     *lbaPtr = 0;
2298     if (suseGrubConfGetLba(path, lbaPtr))
2299     return 1;
2300     }
2301    
2302     if (bootPtr) {
2303     *bootPtr = NULL;
2304     suseGrubConfGetBoot(path, bootPtr);
2305     }
2306    
2307     return 0;
2308     }
2309    
2310 niro 532 int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
2311     FILE * in;
2312     char buf[1024];
2313     char * chptr;
2314     char * start;
2315     char * param;
2316    
2317 niro 926 in = fopen("/etc/conf.d/grub", "r");
2318 niro 532 if (!in) return 1;
2319    
2320     if (lbaPtr) *lbaPtr = 0;
2321     if (bootPtr) *bootPtr = NULL;
2322    
2323     while (fgets(buf, sizeof(buf), in)) {
2324     start = buf;
2325     while (isspace(*start)) start++;
2326     if (*start == '#') continue;
2327    
2328     chptr = strchr(start, '=');
2329     if (!chptr) continue;
2330     chptr--;
2331     while (*chptr && isspace(*chptr)) chptr--;
2332     chptr++;
2333     *chptr = '\0';
2334    
2335     param = chptr + 1;
2336     while (*param && isspace(*param)) param++;
2337     if (*param == '=') {
2338     param++;
2339     while (*param && isspace(*param)) param++;
2340     }
2341    
2342     chptr = param;
2343     while (*chptr && !isspace(*chptr)) chptr++;
2344     *chptr = '\0';
2345    
2346     if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
2347     *lbaPtr = 1;
2348     else if (!strcmp(start, "boot") && bootPtr)
2349     *bootPtr = strdup(param);
2350     }
2351    
2352     fclose(in);
2353    
2354     return 0;
2355     }
2356    
2357     void dumpSysconfigGrub(void) {
2358 niro 1850 char * boot = NULL;
2359 niro 532 int lba;
2360    
2361 niro 1851 if (isSuseSystem()) {
2362     if (parseSuseGrubConf(&lba, &boot)) {
2363 niro 1850 free(boot);
2364     return;
2365     }
2366     } else {
2367 niro 1851 if (parseSysconfigGrub(&lba, &boot)) {
2368 niro 1850 free(boot);
2369     return;
2370     }
2371 niro 532 }
2372 niro 1850
2373     if (lba) printf("lba\n");
2374     if (boot) {
2375     printf("boot=%s\n", boot);
2376     free(boot);
2377     }
2378 niro 532 }
2379    
2380     int displayInfo(struct grubConfig * config, char * kernel,
2381     const char * prefix) {
2382     int i = 0;
2383     struct singleEntry * entry;
2384     struct singleLine * line;
2385    
2386     entry = findEntryByPath(config, kernel, prefix, &i);
2387     if (!entry) {
2388     fprintf(stderr, _("grubby: kernel not found\n"));
2389     return 1;
2390     }
2391    
2392 niro 926 /* this is a horrible hack to support /etc/conf.d/grub; there must
2393 niro 532 be a better way */
2394     if (config->cfi == &grubConfigType) {
2395     dumpSysconfigGrub();
2396     } else {
2397 niro 914 line = getLineByType(LT_BOOT, config->theLines);
2398 niro 532 if (line && line->numElements >= 1) {
2399     printf("boot=%s\n", line->elements[1].item);
2400     }
2401    
2402 niro 914 line = getLineByType(LT_LBA, config->theLines);
2403 niro 532 if (line) printf("lba\n");
2404     }
2405    
2406     displayEntry(entry, prefix, i);
2407    
2408     i++;
2409     while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
2410     displayEntry(entry, prefix, i);
2411     i++;
2412     }
2413    
2414     return 0;
2415     }
2416    
2417 niro 914 struct singleLine * addLineTmpl(struct singleEntry * entry,
2418     struct singleLine * tmplLine,
2419     struct singleLine * prevLine,
2420     const char * val,
2421     struct configFileInfo * cfi)
2422     {
2423     struct singleLine * newLine = lineDup(tmplLine);
2424    
2425     if (val) {
2426     /* override the inherited value with our own.
2427     * This is a little weak because it only applies to elements[1]
2428     */
2429     if (newLine->numElements > 1)
2430     removeElement(newLine, 1);
2431     insertElement(newLine, val, 1, cfi);
2432    
2433     /* but try to keep the rootspec from the template... sigh */
2434     if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD)) {
2435     char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
2436     if (rootspec != NULL) {
2437     free(newLine->elements[1].item);
2438     newLine->elements[1].item =
2439     sdupprintf("%s%s", rootspec, val);
2440     }
2441     }
2442     }
2443    
2444     dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
2445     newLine->elements[0].item : "");
2446    
2447     if (!entry->lines) {
2448     /* first one on the list */
2449     entry->lines = newLine;
2450     } else if (prevLine) {
2451     /* add after prevLine */
2452     newLine->next = prevLine->next;
2453     prevLine->next = newLine;
2454     }
2455    
2456     return newLine;
2457     }
2458    
2459 niro 532 /* val may be NULL */
2460     struct singleLine * addLine(struct singleEntry * entry,
2461     struct configFileInfo * cfi,
2462 niro 914 enum lineType_e type, char * defaultIndent,
2463     const char * val) {
2464 niro 532 struct singleLine * line, * prev;
2465 niro 914 struct keywordTypes * kw;
2466     struct singleLine tmpl;
2467 niro 532
2468 niro 914 /* NB: This function shouldn't allocate items on the heap, rather on the
2469     * stack since it calls addLineTmpl which will make copies.
2470     */
2471 niro 532
2472 niro 914 if (type == LT_TITLE && cfi->titleBracketed) {
2473     /* we're doing a bracketed title (zipl) */
2474     tmpl.type = type;
2475     tmpl.numElements = 1;
2476     tmpl.elements = alloca(sizeof(*tmpl.elements));
2477     tmpl.elements[0].item = alloca(strlen(val)+3);
2478     sprintf(tmpl.elements[0].item, "[%s]", val);
2479     tmpl.elements[0].indent = "";
2480     val = NULL;
2481 niro 1696 } else if (type == LT_MENUENTRY) {
2482     char *lineend = "--class gnu-linux --class gnu --class os {";
2483     if (!val) {
2484     fprintf(stderr, "Line type LT_MENUENTRY requires a value\n");
2485     abort();
2486     }
2487     kw = getKeywordByType(type, cfi);
2488     if (!kw) {
2489     fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2490     abort();
2491     }
2492     tmpl.indent = "";
2493     tmpl.type = type;
2494     tmpl.numElements = 3;
2495     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2496     tmpl.elements[0].item = kw->key;
2497     tmpl.elements[0].indent = alloca(2);
2498     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2499     tmpl.elements[1].item = (char *)val;
2500     tmpl.elements[1].indent = alloca(2);
2501     sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
2502     tmpl.elements[2].item = alloca(strlen(lineend)+1);
2503     strcpy(tmpl.elements[2].item, lineend);
2504     tmpl.elements[2].indent = "";
2505 niro 914 } else {
2506     kw = getKeywordByType(type, cfi);
2507 niro 1696 if (!kw) {
2508     fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2509     abort();
2510     }
2511 niro 914 tmpl.type = type;
2512     tmpl.numElements = val ? 2 : 1;
2513     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2514     tmpl.elements[0].item = kw->key;
2515     tmpl.elements[0].indent = alloca(2);
2516     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2517     if (val) {
2518     tmpl.elements[1].item = (char *)val;
2519     tmpl.elements[1].indent = "";
2520     }
2521     }
2522    
2523 niro 532 /* The last non-empty line gives us the indention to us and the line
2524     to insert after. Note that comments are considered empty lines, which
2525     may not be ideal? If there are no lines or we are looking at the
2526     first line, we use defaultIndent (the first line is normally indented
2527     differently from the rest) */
2528 niro 914 for (line = entry->lines, prev = NULL; line; line = line->next) {
2529     if (line->numElements) prev = line;
2530     /* fall back on the last line if prev isn't otherwise set */
2531     if (!line->next && !prev) prev = line;
2532 niro 532 }
2533    
2534 niro 1696 struct singleLine *menuEntry;
2535     menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
2536     if (tmpl.type == LT_ENTRY_END) {
2537     if (menuEntry)
2538     tmpl.indent = menuEntry->indent;
2539     else
2540     tmpl.indent = defaultIndent ?: "";
2541     } else if (tmpl.type != LT_MENUENTRY) {
2542     if (menuEntry)
2543     tmpl.indent = "\t";
2544     else if (prev == entry->lines)
2545     tmpl.indent = defaultIndent ?: "";
2546     else
2547     tmpl.indent = prev->indent;
2548     }
2549 niro 532
2550 niro 914 return addLineTmpl(entry, &tmpl, prev, val, cfi);
2551 niro 532 }
2552    
2553     void removeLine(struct singleEntry * entry, struct singleLine * line) {
2554     struct singleLine * prev;
2555     int i;
2556    
2557     for (i = 0; i < line->numElements; i++) {
2558     free(line->elements[i].item);
2559     free(line->elements[i].indent);
2560     }
2561     free(line->elements);
2562     free(line->indent);
2563    
2564     if (line == entry->lines) {
2565     entry->lines = line->next;
2566     } else {
2567     prev = entry->lines;
2568     while (prev->next != line) prev = prev->next;
2569     prev->next = line->next;
2570     }
2571    
2572     free(line);
2573     }
2574    
2575 niro 1696 static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi)
2576     {
2577     struct singleLine newLine = {
2578     .indent = tmplLine->indent,
2579     .type = tmplLine->type,
2580     .next = tmplLine->next,
2581     };
2582     int firstQuotedItem = -1;
2583     int quoteLen = 0;
2584     int j;
2585     int element = 0;
2586     char *c;
2587    
2588     c = malloc(strlen(tmplLine->elements[0].item) + 1);
2589     strcpy(c, tmplLine->elements[0].item);
2590     insertElement(&newLine, c, element++, cfi);
2591     free(c);
2592     c = NULL;
2593    
2594     for (j = 1; j < tmplLine->numElements; j++) {
2595     if (firstQuotedItem == -1) {
2596     quoteLen += strlen(tmplLine->elements[j].item);
2597    
2598     if (isquote(tmplLine->elements[j].item[0])) {
2599     firstQuotedItem = j;
2600     quoteLen += strlen(tmplLine->elements[j].indent);
2601     } else {
2602     c = malloc(quoteLen + 1);
2603     strcpy(c, tmplLine->elements[j].item);
2604     insertElement(&newLine, c, element++, cfi);
2605     free(c);
2606     quoteLen = 0;
2607     }
2608     } else {
2609     int itemlen = strlen(tmplLine->elements[j].item);
2610     quoteLen += itemlen;
2611     quoteLen += strlen(tmplLine->elements[j].indent);
2612    
2613     if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
2614     c = malloc(quoteLen + 1);
2615     c[0] = '\0';
2616     for (int i = firstQuotedItem; i < j+1; i++) {
2617     strcat(c, tmplLine->elements[i].item);
2618     strcat(c, tmplLine->elements[i].indent);
2619     }
2620     insertElement(&newLine, c, element++, cfi);
2621     free(c);
2622    
2623     firstQuotedItem = -1;
2624     quoteLen = 0;
2625     }
2626     }
2627     }
2628     while (tmplLine->numElements)
2629     removeElement(tmplLine, 0);
2630     if (tmplLine->elements)
2631     free(tmplLine->elements);
2632    
2633     tmplLine->numElements = newLine.numElements;
2634     tmplLine->elements = newLine.elements;
2635     }
2636    
2637 niro 914 static void insertElement(struct singleLine * line,
2638     const char * item, int insertHere,
2639     struct configFileInfo * cfi)
2640     {
2641     struct keywordTypes * kw;
2642     char indent[2] = "";
2643    
2644     /* sanity check */
2645     if (insertHere > line->numElements) {
2646     dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",
2647     insertHere, line->numElements);
2648     insertHere = line->numElements;
2649     }
2650    
2651     line->elements = realloc(line->elements, (line->numElements + 1) *
2652     sizeof(*line->elements));
2653     memmove(&line->elements[insertHere+1],
2654     &line->elements[insertHere],
2655     (line->numElements - insertHere) *
2656     sizeof(*line->elements));
2657     line->elements[insertHere].item = strdup(item);
2658    
2659     kw = getKeywordByType(line->type, cfi);
2660    
2661     if (line->numElements == 0) {
2662     indent[0] = '\0';
2663     } else if (insertHere == 0) {
2664     indent[0] = kw->nextChar;
2665     } else if (kw->separatorChar != '\0') {
2666     indent[0] = kw->separatorChar;
2667     } else {
2668     indent[0] = ' ';
2669     }
2670    
2671     if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {
2672     /* move the end-of-line forward */
2673     line->elements[insertHere].indent =
2674     line->elements[insertHere-1].indent;
2675     line->elements[insertHere-1].indent = strdup(indent);
2676     } else {
2677     line->elements[insertHere].indent = strdup(indent);
2678     }
2679    
2680     line->numElements++;
2681    
2682     dbgPrintf("insertElement(%s, '%s%s', %d)\n",
2683     line->elements[0].item,
2684     line->elements[insertHere].item,
2685     line->elements[insertHere].indent,
2686     insertHere);
2687     }
2688    
2689     static void removeElement(struct singleLine * line, int removeHere) {
2690     int i;
2691    
2692     /* sanity check */
2693     if (removeHere >= line->numElements) return;
2694    
2695     dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
2696     removeHere, line->elements[removeHere].item);
2697    
2698     free(line->elements[removeHere].item);
2699    
2700     if (removeHere > 1) {
2701     /* previous argument gets this argument's post-indentation */
2702     free(line->elements[removeHere-1].indent);
2703     line->elements[removeHere-1].indent =
2704     line->elements[removeHere].indent;
2705     } else {
2706     free(line->elements[removeHere].indent);
2707     }
2708    
2709     /* now collapse the array, but don't bother to realloc smaller */
2710     for (i = removeHere; i < line->numElements - 1; i++)
2711     line->elements[i] = line->elements[i + 1];
2712    
2713     line->numElements--;
2714     }
2715    
2716 niro 532 int argMatch(const char * one, const char * two) {
2717     char * first, * second;
2718     char * chptr;
2719    
2720     first = strcpy(alloca(strlen(one) + 1), one);
2721     second = strcpy(alloca(strlen(two) + 1), two);
2722    
2723     chptr = strchr(first, '=');
2724     if (chptr) *chptr = '\0';
2725    
2726     chptr = strchr(second, '=');
2727     if (chptr) *chptr = '\0';
2728    
2729     return strcmp(first, second);
2730     }
2731    
2732     int updateActualImage(struct grubConfig * cfg, const char * image,
2733     const char * prefix, const char * addArgs,
2734     const char * removeArgs, int multibootArgs) {
2735     struct singleEntry * entry;
2736     struct singleLine * line, * rootLine;
2737     int index = 0;
2738 niro 914 int i, k;
2739 niro 532 const char ** newArgs, ** oldArgs;
2740     const char ** arg;
2741 niro 914 int useKernelArgs, useRoot;
2742 niro 532 int firstElement;
2743 niro 1304 int *usedElements;
2744 niro 914 int doreplace;
2745 niro 532
2746     if (!image) return 0;
2747    
2748     if (!addArgs) {
2749     newArgs = malloc(sizeof(*newArgs));
2750     *newArgs = NULL;
2751     } else {
2752     if (poptParseArgvString(addArgs, NULL, &newArgs)) {
2753     fprintf(stderr,
2754     _("grubby: error separating arguments '%s'\n"), addArgs);
2755     return 1;
2756     }
2757     }
2758    
2759     if (!removeArgs) {
2760     oldArgs = malloc(sizeof(*oldArgs));
2761     *oldArgs = NULL;
2762     } else {
2763     if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
2764     fprintf(stderr,
2765     _("grubby: error separating arguments '%s'\n"), removeArgs);
2766     free(newArgs);
2767     return 1;
2768     }
2769     }
2770    
2771    
2772 niro 914 useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
2773     && (!multibootArgs || cfg->cfi->mbConcatArgs));
2774 niro 532
2775 niro 914 useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
2776     && !multibootArgs);
2777 niro 532
2778 niro 914 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
2779 niro 532
2780 niro 914 if (multibootArgs && !entry->multiboot)
2781     continue;
2782 niro 532
2783 niro 914 /* Determine where to put the args. If this config supports
2784     * LT_KERNELARGS, use that. Otherwise use
2785     * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
2786     */
2787     if (useKernelArgs) {
2788     line = getLineByType(LT_KERNELARGS, entry->lines);
2789     if (!line) {
2790     /* no LT_KERNELARGS, need to add it */
2791     line = addLine(entry, cfg->cfi, LT_KERNELARGS,
2792     cfg->secondaryIndent, NULL);
2793     }
2794     firstElement = 1;
2795 niro 532
2796 niro 914 } else if (multibootArgs) {
2797     line = getLineByType(LT_HYPER, entry->lines);
2798     if (!line) {
2799     /* a multiboot entry without LT_HYPER? */
2800     continue;
2801     }
2802     firstElement = 2;
2803    
2804     } else {
2805     line = getLineByType(LT_KERNEL|LT_MBMODULE, entry->lines);
2806     if (!line) {
2807     /* no LT_KERNEL or LT_MBMODULE in this entry? */
2808     continue;
2809     }
2810     firstElement = 2;
2811 niro 532 }
2812    
2813 niro 914 /* handle the elilo case which does:
2814     * append="hypervisor args -- kernel args"
2815     */
2816     if (entry->multiboot && cfg->cfi->mbConcatArgs) {
2817     /* this is a multiboot entry, make sure there's
2818     * -- on the args line
2819     */
2820     for (i = firstElement; i < line->numElements; i++) {
2821     if (!strcmp(line->elements[i].item, "--"))
2822     break;
2823     }
2824     if (i == line->numElements) {
2825     /* assume all existing args are kernel args,
2826     * prepend -- to make it official
2827     */
2828     insertElement(line, "--", firstElement, cfg->cfi);
2829     i = firstElement;
2830     }
2831     if (!multibootArgs) {
2832     /* kernel args start after the -- */
2833     firstElement = i + 1;
2834     }
2835     } else if (cfg->cfi->mbConcatArgs) {
2836     /* this is a non-multiboot entry, remove hyper args */
2837     for (i = firstElement; i < line->numElements; i++) {
2838     if (!strcmp(line->elements[i].item, "--"))
2839     break;
2840     }
2841     if (i < line->numElements) {
2842     /* remove args up to -- */
2843     while (strcmp(line->elements[firstElement].item, "--"))
2844     removeElement(line, firstElement);
2845     /* remove -- */
2846     removeElement(line, firstElement);
2847     }
2848 niro 532 }
2849    
2850 niro 914 usedElements = calloc(line->numElements, sizeof(*usedElements));
2851 niro 532
2852 niro 914 for (k = 0, arg = newArgs; *arg; arg++, k++) {
2853    
2854     doreplace = 1;
2855 niro 532 for (i = firstElement; i < line->numElements; i++) {
2856 niro 914 if (multibootArgs && cfg->cfi->mbConcatArgs &&
2857     !strcmp(line->elements[i].item, "--"))
2858     {
2859     /* reached the end of hyper args, insert here */
2860     doreplace = 0;
2861     break;
2862     }
2863 niro 532 if (usedElements[i])
2864     continue;
2865     if (!argMatch(line->elements[i].item, *arg)) {
2866     usedElements[i]=1;
2867     break;
2868     }
2869     }
2870    
2871 niro 914 if (i < line->numElements && doreplace) {
2872     /* direct replacement */
2873 niro 532 free(line->elements[i].item);
2874     line->elements[i].item = strdup(*arg);
2875    
2876 niro 914 } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
2877     /* root= replacement */
2878     rootLine = getLineByType(LT_ROOT, entry->lines);
2879     if (rootLine) {
2880     free(rootLine->elements[1].item);
2881     rootLine->elements[1].item = strdup(*arg + 5);
2882 niro 532 } else {
2883 niro 914 rootLine = addLine(entry, cfg->cfi, LT_ROOT,
2884     cfg->secondaryIndent, *arg + 5);
2885 niro 532 }
2886 niro 914 }
2887 niro 532
2888 niro 914 else {
2889     /* insert/append */
2890     insertElement(line, *arg, i, cfg->cfi);
2891     usedElements = realloc(usedElements, line->numElements *
2892     sizeof(*usedElements));
2893     memmove(&usedElements[i + 1], &usedElements[i],
2894     line->numElements - i - 1);
2895     usedElements[i] = 1;
2896 niro 532
2897     /* if we updated a root= here even though there is a
2898     LT_ROOT available we need to remove the LT_ROOT entry
2899     (this will happen if we switch from a device to a label) */
2900     if (useRoot && !strncmp(*arg, "root=", 5)) {
2901 niro 914 rootLine = getLineByType(LT_ROOT, entry->lines);
2902     if (rootLine)
2903 niro 532 removeLine(entry, rootLine);
2904     }
2905     }
2906     }
2907    
2908     free(usedElements);
2909    
2910     for (arg = oldArgs; *arg; arg++) {
2911 niro 914 for (i = firstElement; i < line->numElements; i++) {
2912     if (multibootArgs && cfg->cfi->mbConcatArgs &&
2913     !strcmp(line->elements[i].item, "--"))
2914     /* reached the end of hyper args, stop here */
2915 niro 532 break;
2916 niro 914 if (!argMatch(line->elements[i].item, *arg)) {
2917     removeElement(line, i);
2918     break;
2919 niro 532 }
2920     }
2921 niro 914 /* handle removing LT_ROOT line too */
2922     if (useRoot && !strncmp(*arg, "root=", 5)) {
2923     rootLine = getLineByType(LT_ROOT, entry->lines);
2924     if (rootLine)
2925     removeLine(entry, rootLine);
2926     }
2927 niro 532 }
2928    
2929     if (line->numElements == 1) {
2930     /* don't need the line at all (note it has to be a
2931     LT_KERNELARGS for this to happen */
2932     removeLine(entry, line);
2933     }
2934     }
2935    
2936     free(newArgs);
2937     free(oldArgs);
2938    
2939     return 0;
2940     }
2941    
2942     int updateImage(struct grubConfig * cfg, const char * image,
2943     const char * prefix, const char * addArgs,
2944     const char * removeArgs,
2945     const char * addMBArgs, const char * removeMBArgs) {
2946     int rc = 0;
2947    
2948     if (!image) return rc;
2949    
2950     /* update the main args first... */
2951     if (addArgs || removeArgs)
2952     rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs, 0);
2953     if (rc) return rc;
2954    
2955     /* and now any multiboot args */
2956     if (addMBArgs || removeMBArgs)
2957     rc = updateActualImage(cfg, image, prefix, addMBArgs, removeMBArgs, 1);
2958     return rc;
2959     }
2960    
2961 niro 1156 int updateInitrd(struct grubConfig * cfg, const char * image,
2962     const char * prefix, const char * initrd) {
2963     struct singleEntry * entry;
2964 niro 1696 struct singleLine * line, * kernelLine, *endLine = NULL;
2965 niro 1156 int index = 0;
2966    
2967     if (!image) return 0;
2968    
2969     for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
2970     kernelLine = getLineByType(LT_KERNEL, entry->lines);
2971     if (!kernelLine) continue;
2972    
2973     line = getLineByType(LT_INITRD, entry->lines);
2974     if (line)
2975     removeLine(entry, line);
2976     if (prefix) {
2977     int prefixLen = strlen(prefix);
2978     if (!strncmp(initrd, prefix, prefixLen))
2979     initrd += prefixLen;
2980     }
2981 niro 1696 endLine = getLineByType(LT_ENTRY_END, entry->lines);
2982     if (endLine)
2983     removeLine(entry, endLine);
2984 niro 1156 line = addLine(entry, cfg->cfi, LT_INITRD, kernelLine->indent, initrd);
2985 niro 1696 if (!line)
2986     return 1;
2987     if (endLine) {
2988     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
2989     if (!line)
2990     return 1;
2991     }
2992    
2993 niro 1156 break;
2994     }
2995    
2996     return 0;
2997     }
2998    
2999 niro 532 int checkDeviceBootloader(const char * device, const unsigned char * boot) {
3000     int fd;
3001     unsigned char bootSect[512];
3002     int offset;
3003    
3004     fd = open(device, O_RDONLY);
3005     if (fd < 0) {
3006     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3007     device, strerror(errno));
3008     return 1;
3009     }
3010    
3011     if (read(fd, bootSect, 512) != 512) {
3012     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3013     device, strerror(errno));
3014     return 1;
3015     }
3016     close(fd);
3017    
3018     /* first three bytes should match, a jmp short should be in there */
3019     if (memcmp(boot, bootSect, 3))
3020     return 0;
3021    
3022 niro 1718 if (boot[1] == JMP_SHORT_OPCODE) {
3023 niro 532 offset = boot[2] + 2;
3024     } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3025     offset = (boot[3] << 8) + boot[2] + 2;
3026 niro 1718 } else if (boot[0] == JMP_SHORT_OPCODE) {
3027     offset = boot[1] + 2;
3028     /*
3029     * it looks like grub, when copying stage1 into the mbr, patches stage1
3030     * right after the JMP location, replacing other instructions such as
3031     * JMPs for NOOPs. So, relax the check a little bit by skipping those
3032     * different bytes.
3033     */
3034     if ((bootSect[offset + 1] == NOOP_OPCODE)
3035     && (bootSect[offset + 2] == NOOP_OPCODE)) {
3036     offset = offset + 3;
3037     }
3038 niro 532 } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3039     offset = (boot[2] << 8) + boot[1] + 2;
3040     } else {
3041     return 0;
3042     }
3043    
3044     if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
3045     return 0;
3046    
3047     return 2;
3048     }
3049    
3050     int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {
3051     int fd;
3052     char buf[65536];
3053     char * end;
3054     char * chptr;
3055     char * chptr2;
3056     int rc;
3057    
3058     /* it's on raid; we need to parse /proc/mdstat and check all of the
3059     *raw* devices listed in there */
3060    
3061     if (!strncmp(mdDev, "/dev/", 5))
3062     mdDev += 5;
3063    
3064     if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
3065     fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
3066     strerror(errno));
3067     return 2;
3068     }
3069    
3070     rc = read(fd, buf, sizeof(buf) - 1);
3071     if (rc < 0 || rc == (sizeof(buf) - 1)) {
3072     fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
3073     strerror(errno));
3074     close(fd);
3075     return 2;
3076     }
3077     close(fd);
3078     buf[rc] = '\0';
3079    
3080     chptr = buf;
3081     while (*chptr) {
3082     end = strchr(chptr, '\n');
3083     if (!end) break;
3084     *end = '\0';
3085    
3086     if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
3087     chptr[strlen(mdDev)] == ' ') {
3088    
3089     /* found the device */
3090     while (*chptr && *chptr != ':') chptr++;
3091     chptr++;
3092     while (*chptr && isspace(*chptr)) chptr++;
3093    
3094     /* skip the "active" bit */
3095     while (*chptr && !isspace(*chptr)) chptr++;
3096     while (*chptr && isspace(*chptr)) chptr++;
3097    
3098     /* skip the raid level */
3099     while (*chptr && !isspace(*chptr)) chptr++;
3100     while (*chptr && isspace(*chptr)) chptr++;
3101    
3102     /* everything else is partition stuff */
3103     while (*chptr) {
3104     chptr2 = chptr;
3105     while (*chptr2 && *chptr2 != '[') chptr2++;
3106     if (!*chptr2) break;
3107    
3108     /* yank off the numbers at the end */
3109     chptr2--;
3110     while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;
3111     chptr2++;
3112     *chptr2 = '\0';
3113    
3114     /* Better, now we need the /dev/ back. We're done with
3115     * everything before this point, so we can just put
3116     * the /dev/ part there. There will always be room. */
3117     memcpy(chptr - 5, "/dev/", 5);
3118     rc = checkDeviceBootloader(chptr - 5, boot);
3119     if (rc != 2) {
3120     return rc;
3121     }
3122    
3123     chptr = chptr2 + 1;
3124     /* skip the [11] bit */
3125     while (*chptr && !isspace(*chptr)) chptr++;
3126     /* and move to the next one */
3127     while (*chptr && isspace(*chptr)) chptr++;
3128     }
3129    
3130     /* we're good to go */
3131     return 2;
3132     }
3133    
3134     chptr = end + 1;
3135     }
3136    
3137     fprintf(stderr,
3138     _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
3139     mdDev);
3140     return 0;
3141     }
3142    
3143     int checkForLilo(struct grubConfig * config) {
3144     int fd;
3145     unsigned char boot[512];
3146     struct singleLine * line;
3147    
3148     for (line = config->theLines; line; line = line->next)
3149     if (line->type == LT_BOOT) break;
3150    
3151     if (!line) {
3152     fprintf(stderr,
3153     _("grubby: no boot line found in lilo configuration\n"));
3154     return 1;
3155     }
3156    
3157     if (line->numElements != 2) return 1;
3158    
3159     fd = open("/boot/boot.b", O_RDONLY);
3160     if (fd < 0) {
3161     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3162     "/boot/boot.b", strerror(errno));
3163     return 1;
3164     }
3165    
3166     if (read(fd, boot, 512) != 512) {
3167     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3168     "/boot/boot.b", strerror(errno));
3169     return 1;
3170     }
3171     close(fd);
3172    
3173     if (!strncmp("/dev/md", line->elements[1].item, 7))
3174     return checkLiloOnRaid(line->elements[1].item, boot);
3175    
3176     return checkDeviceBootloader(line->elements[1].item, boot);
3177     }
3178    
3179 niro 1696 int checkForGrub2(struct grubConfig * config) {
3180 niro 1714 if (!access("/etc/grub.d/", R_OK))
3181 niro 1696 return 2;
3182    
3183     return 1;
3184     }
3185    
3186 niro 532 int checkForGrub(struct grubConfig * config) {
3187     int fd;
3188     unsigned char bootSect[512];
3189     char * boot;
3190 niro 1851 int onSuse = isSuseSystem();
3191 niro 532
3192 niro 1851
3193     if (onSuse) {
3194     if (parseSuseGrubConf(NULL, &boot))
3195     return 0;
3196 niro 1850 } else {
3197 niro 1851 if (parseSysconfigGrub(NULL, &boot))
3198     return 0;
3199 niro 1850 }
3200 niro 532
3201     /* assume grub is not installed -- not an error condition */
3202     if (!boot)
3203     return 0;
3204    
3205     fd = open("/boot/grub/stage1", O_RDONLY);
3206     if (fd < 0)
3207     /* this doesn't exist if grub hasn't been installed */
3208     return 0;
3209    
3210     if (read(fd, bootSect, 512) != 512) {
3211     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3212     "/boot/grub/stage1", strerror(errno));
3213 niro 914 close(fd);
3214 niro 532 return 1;
3215     }
3216     close(fd);
3217    
3218 niro 1851 /* The more elaborate checks do not work on SuSE. The checks done
3219     * seem to be reasonble (at least for now), so just return success
3220     */
3221     if (onSuse)
3222 niro 1850 return 2;
3223 niro 1851
3224     return checkDeviceBootloader(boot, bootSect);
3225 niro 532 }
3226    
3227 niro 914 int checkForExtLinux(struct grubConfig * config) {
3228     int fd;
3229     unsigned char bootSect[512];
3230     char * boot;
3231     char executable[] = "/boot/extlinux/extlinux";
3232    
3233     printf("entered: checkForExtLinux()\n");
3234    
3235     if (parseSysconfigGrub(NULL, &boot))
3236     return 0;
3237    
3238     /* assume grub is not installed -- not an error condition */
3239     if (!boot)
3240     return 0;
3241    
3242     fd = open(executable, O_RDONLY);
3243     if (fd < 0)
3244     /* this doesn't exist if grub hasn't been installed */
3245     return 0;
3246    
3247     if (read(fd, bootSect, 512) != 512) {
3248     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3249     executable, strerror(errno));
3250     return 1;
3251     }
3252     close(fd);
3253    
3254     return checkDeviceBootloader(boot, bootSect);
3255     }
3256    
3257 niro 1853 int checkForYaboot(struct grubConfig * config) {
3258     /*
3259     * This is a simplistic check that we consider good enough for own puporses
3260     *
3261     * If we were to properly check if yaboot is *installed* we'd need to:
3262     * 1) get the system boot device (LT_BOOT)
3263     * 2) considering it's a raw filesystem, check if the yaboot binary matches
3264     * the content on the boot device
3265     * 3) if not, copy the binary to a temporary file and run "addnote" on it
3266     * 4) check again if binary and boot device contents match
3267     */
3268     if (!access("/etc/yaboot.conf", R_OK))
3269     return 2;
3270    
3271     return 1;
3272     }
3273    
3274 niro 1854 int checkForElilo(struct grubConfig * config) {
3275     if (!access("/etc/elilo.conf", R_OK))
3276     return 2;
3277    
3278     return 1;
3279     }
3280    
3281 niro 532 static char * getRootSpecifier(char * str) {
3282     char * idx, * rootspec = NULL;
3283    
3284     if (*str == '(') {
3285     idx = rootspec = strdup(str);
3286     while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;
3287     *(++idx) = '\0';
3288     }
3289     return rootspec;
3290     }
3291    
3292 niro 914 static char * getInitrdVal(struct grubConfig * config,
3293     const char * prefix, struct singleLine *tmplLine,
3294     const char * newKernelInitrd,
3295 niro 1844 const char ** extraInitrds, int extraInitrdCount)
3296 niro 914 {
3297     char *initrdVal, *end;
3298     int i;
3299     size_t totalSize;
3300     size_t prefixLen;
3301     char separatorChar;
3302    
3303     prefixLen = strlen(prefix);
3304     totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
3305    
3306     for (i = 0; i < extraInitrdCount; i++) {
3307     totalSize += sizeof(separatorChar);
3308     totalSize += strlen(extraInitrds[i]) - prefixLen;
3309     }
3310    
3311     initrdVal = end = malloc(totalSize);
3312    
3313     end = stpcpy (end, newKernelInitrd + prefixLen);
3314    
3315     separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
3316     for (i = 0; i < extraInitrdCount; i++) {
3317     const char *extraInitrd;
3318     int j;
3319    
3320     extraInitrd = extraInitrds[i] + prefixLen;
3321     /* Don't add entries that are already there */
3322     if (tmplLine != NULL) {
3323     for (j = 2; j < tmplLine->numElements; j++)
3324     if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
3325     break;
3326    
3327     if (j != tmplLine->numElements)
3328     continue;
3329     }
3330    
3331     *end++ = separatorChar;
3332     end = stpcpy(end, extraInitrd);
3333     }
3334    
3335     return initrdVal;
3336     }
3337    
3338 niro 532 int addNewKernel(struct grubConfig * config, struct singleEntry * template,
3339     const char * prefix,
3340 niro 1844 const char * newKernelPath, const char * newKernelTitle,
3341     const char * newKernelArgs, const char * newKernelInitrd,
3342     const char ** extraInitrds, int extraInitrdCount,
3343     const char * newMBKernel, const char * newMBKernelArgs) {
3344 niro 532 struct singleEntry * new;
3345 niro 914 struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
3346 niro 532 int needs;
3347     char * chptr;
3348    
3349     if (!newKernelPath) return 0;
3350    
3351     /* if the newKernelTitle is too long silently munge it into something
3352     * we can live with. truncating is first check, then we'll just mess with
3353     * it until it looks better */
3354     if (config->cfi->maxTitleLength &&
3355     (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
3356     char * buf = alloca(config->cfi->maxTitleLength + 7);
3357     char * numBuf = alloca(config->cfi->maxTitleLength + 1);
3358     int i = 1;
3359    
3360     sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);
3361     while (findEntryByPath(config, buf, NULL, NULL)) {
3362     sprintf(numBuf, "%d", i++);
3363     strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
3364     }
3365    
3366     newKernelTitle = buf + 6;
3367     }
3368    
3369     new = malloc(sizeof(*new));
3370     new->skip = 0;
3371     new->multiboot = 0;
3372     new->next = config->entries;
3373     new->lines = NULL;
3374     config->entries = new;
3375    
3376     /* copy/update from the template */
3377 niro 914 needs = NEED_KERNEL | NEED_TITLE;
3378     if (newKernelInitrd)
3379     needs |= NEED_INITRD;
3380 niro 532 if (newMBKernel) {
3381 niro 914 needs |= NEED_MB;
3382 niro 532 new->multiboot = 1;
3383     }
3384    
3385     if (template) {
3386 niro 914 for (masterLine = template->lines;
3387     masterLine && (tmplLine = lineDup(masterLine));
3388     lineFree(tmplLine), masterLine = masterLine->next)
3389     {
3390     dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
3391 niro 532
3392     /* skip comments */
3393     chptr = tmplLine->indent;
3394     while (*chptr && isspace(*chptr)) chptr++;
3395     if (*chptr == '#') continue;
3396    
3397 niro 914 if (tmplLine->type == LT_KERNEL &&
3398 niro 1696 tmplLine->numElements >= 2) {
3399 niro 914 if (!template->multiboot && (needs & NEED_MB)) {
3400     /* it's not a multiboot template and this is the kernel
3401     * line. Try to be intelligent about inserting the
3402     * hypervisor at the same time.
3403     */
3404     if (config->cfi->mbHyperFirst) {
3405     /* insert the hypervisor first */
3406     newLine = addLine(new, config->cfi, LT_HYPER,
3407     tmplLine->indent,
3408     newMBKernel + strlen(prefix));
3409     /* set up for adding the kernel line */
3410     free(tmplLine->indent);
3411     tmplLine->indent = strdup(config->secondaryIndent);
3412     needs &= ~NEED_MB;
3413     }
3414     if (needs & NEED_KERNEL) {
3415     /* use addLineTmpl to preserve line elements,
3416     * otherwise we could just call addLine. Unfortunately
3417     * this means making some changes to the template
3418     * such as the indent change above and the type
3419     * change below.
3420     */
3421     struct keywordTypes * mbm_kw =
3422     getKeywordByType(LT_MBMODULE, config->cfi);
3423     if (mbm_kw) {
3424     tmplLine->type = LT_MBMODULE;
3425     free(tmplLine->elements[0].item);
3426     tmplLine->elements[0].item = strdup(mbm_kw->key);
3427     }
3428     newLine = addLineTmpl(new, tmplLine, newLine,
3429     newKernelPath + strlen(prefix), config->cfi);
3430     needs &= ~NEED_KERNEL;
3431     }
3432     if (needs & NEED_MB) { /* !mbHyperFirst */
3433     newLine = addLine(new, config->cfi, LT_HYPER,
3434     config->secondaryIndent,
3435     newMBKernel + strlen(prefix));
3436     needs &= ~NEED_MB;
3437     }
3438     } else if (needs & NEED_KERNEL) {
3439     newLine = addLineTmpl(new, tmplLine, newLine,
3440     newKernelPath + strlen(prefix), config->cfi);
3441     needs &= ~NEED_KERNEL;
3442     }
3443 niro 532
3444 niro 914 } else if (tmplLine->type == LT_HYPER &&
3445     tmplLine->numElements >= 2) {
3446     if (needs & NEED_MB) {
3447     newLine = addLineTmpl(new, tmplLine, newLine,
3448     newMBKernel + strlen(prefix), config->cfi);
3449     needs &= ~NEED_MB;
3450     }
3451 niro 532
3452 niro 914 } else if (tmplLine->type == LT_MBMODULE &&
3453     tmplLine->numElements >= 2) {
3454     if (new->multiboot) {
3455     if (needs & NEED_KERNEL) {
3456     newLine = addLineTmpl(new, tmplLine, newLine,
3457     newKernelPath +
3458     strlen(prefix), config->cfi);
3459     needs &= ~NEED_KERNEL;
3460     } else if (config->cfi->mbInitRdIsModule &&
3461     (needs & NEED_INITRD)) {
3462     char *initrdVal;
3463     initrdVal = getInitrdVal(config, prefix, tmplLine,
3464     newKernelInitrd, extraInitrds,
3465     extraInitrdCount);
3466     newLine = addLineTmpl(new, tmplLine, newLine,
3467     initrdVal, config->cfi);
3468     free(initrdVal);
3469     needs &= ~NEED_INITRD;
3470     }
3471     } else if (needs & NEED_KERNEL) {
3472     /* template is multi but new is not,
3473     * insert the kernel in the first module slot
3474     */
3475     tmplLine->type = LT_KERNEL;
3476     free(tmplLine->elements[0].item);
3477     tmplLine->elements[0].item =
3478     strdup(getKeywordByType(LT_KERNEL, config->cfi)->key);
3479     newLine = addLineTmpl(new, tmplLine, newLine,
3480     newKernelPath + strlen(prefix), config->cfi);
3481     needs &= ~NEED_KERNEL;
3482     } else if (needs & NEED_INITRD) {
3483     char *initrdVal;
3484     /* template is multi but new is not,
3485     * insert the initrd in the second module slot
3486     */
3487     tmplLine->type = LT_INITRD;
3488     free(tmplLine->elements[0].item);
3489     tmplLine->elements[0].item =
3490     strdup(getKeywordByType(LT_INITRD, config->cfi)->key);
3491     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3492     newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3493     free(initrdVal);
3494     needs &= ~NEED_INITRD;
3495     }
3496 niro 532
3497     } else if (tmplLine->type == LT_INITRD &&
3498 niro 914 tmplLine->numElements >= 2) {
3499     if (needs & NEED_INITRD &&
3500     new->multiboot && !template->multiboot &&
3501     config->cfi->mbInitRdIsModule) {
3502     /* make sure we don't insert the module initrd
3503     * before the module kernel... if we don't do it here,
3504     * it will be inserted following the template.
3505     */
3506     if (!needs & NEED_KERNEL) {
3507     char *initrdVal;
3508    
3509     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3510     newLine = addLine(new, config->cfi, LT_MBMODULE,
3511     config->secondaryIndent,
3512     initrdVal);
3513     free(initrdVal);
3514     needs &= ~NEED_INITRD;
3515     }
3516     } else if (needs & NEED_INITRD) {
3517     char *initrdVal;
3518     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3519     newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3520     free(initrdVal);
3521     needs &= ~NEED_INITRD;
3522 niro 532 }
3523    
3524 niro 1696 } else if (tmplLine->type == LT_MENUENTRY &&
3525     (needs & NEED_TITLE)) {
3526     requote(tmplLine, config->cfi);
3527     char *nkt = malloc(strlen(newKernelTitle)+3);
3528     strcpy(nkt, "'");
3529     strcat(nkt, newKernelTitle);
3530     strcat(nkt, "'");
3531     newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
3532     free(nkt);
3533     needs &= ~NEED_TITLE;
3534 niro 532 } else if (tmplLine->type == LT_TITLE &&
3535 niro 914 (needs & NEED_TITLE)) {
3536     if (tmplLine->numElements >= 2) {
3537     newLine = addLineTmpl(new, tmplLine, newLine,
3538     newKernelTitle, config->cfi);
3539     needs &= ~NEED_TITLE;
3540     } else if (tmplLine->numElements == 1 &&
3541     config->cfi->titleBracketed) {
3542     /* addLineTmpl doesn't handle titleBracketed */
3543     newLine = addLine(new, config->cfi, LT_TITLE,
3544     tmplLine->indent, newKernelTitle);
3545     needs &= ~NEED_TITLE;
3546     }
3547 niro 1696 } else if (tmplLine->type == LT_ECHO) {
3548     requote(tmplLine, config->cfi);
3549 niro 1722 static const char *prefix = "'Loading ";
3550 niro 1696 if (tmplLine->numElements > 1 &&
3551 niro 1722 strstr(tmplLine->elements[1].item, prefix) &&
3552     masterLine->next && masterLine->next->type == LT_KERNEL) {
3553 niro 1696 char *newTitle = malloc(strlen(prefix) +
3554     strlen(newKernelTitle) + 2);
3555 niro 532
3556 niro 1696 strcpy(newTitle, prefix);
3557     strcat(newTitle, newKernelTitle);
3558     strcat(newTitle, "'");
3559     newLine = addLine(new, config->cfi, LT_ECHO,
3560     tmplLine->indent, newTitle);
3561     free(newTitle);
3562     } else {
3563     /* pass through other lines from the template */
3564     newLine = addLineTmpl(new, tmplLine, newLine, NULL,
3565     config->cfi);
3566     }
3567 niro 914 } else {
3568     /* pass through other lines from the template */
3569     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
3570     }
3571 niro 532 }
3572 niro 914
3573 niro 532 } else {
3574 niro 914 /* don't have a template, so start the entry with the
3575     * appropriate starting line
3576     */
3577 niro 1693 switch (config->cfi->entryStart) {
3578 niro 914 case LT_KERNEL:
3579     if (new->multiboot && config->cfi->mbHyperFirst) {
3580     /* fall through to LT_HYPER */
3581     } else {
3582     newLine = addLine(new, config->cfi, LT_KERNEL,
3583     config->primaryIndent,
3584     newKernelPath + strlen(prefix));
3585     needs &= ~NEED_KERNEL;
3586     break;
3587     }
3588    
3589     case LT_HYPER:
3590     newLine = addLine(new, config->cfi, LT_HYPER,
3591     config->primaryIndent,
3592     newMBKernel + strlen(prefix));
3593     needs &= ~NEED_MB;
3594 niro 532 break;
3595    
3596 niro 1696 case LT_MENUENTRY: {
3597     char *nkt = malloc(strlen(newKernelTitle)+3);
3598     strcpy(nkt, "'");
3599     strcat(nkt, newKernelTitle);
3600     strcat(nkt, "'");
3601     newLine = addLine(new, config->cfi, LT_MENUENTRY,
3602     config->primaryIndent, nkt);
3603     free(nkt);
3604     needs &= ~NEED_TITLE;
3605     needs |= NEED_END;
3606     break;
3607     }
3608 niro 914 case LT_TITLE:
3609     if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
3610     char * templabel;
3611     int x = 0, y = 0;
3612    
3613     templabel = strdup(newKernelTitle);
3614     while( templabel[x]){
3615     if( templabel[x] == ' ' ){
3616     y = x;
3617     while( templabel[y] ){
3618     templabel[y] = templabel[y+1];
3619     y++;
3620     }
3621     }
3622     x++;
3623     }
3624     newLine = addLine(new, config->cfi, LT_TITLE,
3625     config->primaryIndent, templabel);
3626     free(templabel);
3627     }else{
3628     newLine = addLine(new, config->cfi, LT_TITLE,
3629     config->primaryIndent, newKernelTitle);
3630     }
3631     needs &= ~NEED_TITLE;
3632     break;
3633    
3634     default:
3635     abort();
3636 niro 532 }
3637     }
3638    
3639 niro 914 /* add the remainder of the lines, i.e. those that either
3640     * weren't present in the template, or in the case of no template,
3641 niro 1693 * all the lines following the entryStart.
3642 niro 914 */
3643     if (needs & NEED_TITLE) {
3644     newLine = addLine(new, config->cfi, LT_TITLE,
3645     config->secondaryIndent,
3646     newKernelTitle);
3647     needs &= ~NEED_TITLE;
3648 niro 532 }
3649 niro 914 if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
3650     newLine = addLine(new, config->cfi, LT_HYPER,
3651     config->secondaryIndent,
3652     newMBKernel + strlen(prefix));
3653     needs &= ~NEED_MB;
3654     }
3655     if (needs & NEED_KERNEL) {
3656     newLine = addLine(new, config->cfi,
3657     (new->multiboot && getKeywordByType(LT_MBMODULE,
3658     config->cfi)) ?
3659     LT_MBMODULE : LT_KERNEL,
3660     config->secondaryIndent,
3661     newKernelPath + strlen(prefix));
3662     needs &= ~NEED_KERNEL;
3663     }
3664     if (needs & NEED_MB) {
3665     newLine = addLine(new, config->cfi, LT_HYPER,
3666     config->secondaryIndent,
3667     newMBKernel + strlen(prefix));
3668     needs &= ~NEED_MB;
3669     }
3670     if (needs & NEED_INITRD) {
3671     char *initrdVal;
3672     initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
3673     newLine = addLine(new, config->cfi,
3674     (new->multiboot && getKeywordByType(LT_MBMODULE,
3675     config->cfi)) ?
3676     LT_MBMODULE : LT_INITRD,
3677     config->secondaryIndent,
3678     initrdVal);
3679     free(initrdVal);
3680     needs &= ~NEED_INITRD;
3681     }
3682 niro 1696 if (needs & NEED_END) {
3683     newLine = addLine(new, config->cfi, LT_ENTRY_END,
3684     config->secondaryIndent, NULL);
3685     needs &= ~NEED_END;
3686     }
3687 niro 532
3688 niro 914 if (needs) {
3689     printf(_("grubby: needs=%d, aborting\n"), needs);
3690     abort();
3691     }
3692    
3693 niro 532 if (updateImage(config, "0", prefix, newKernelArgs, NULL,
3694     newMBKernelArgs, NULL)) return 1;
3695    
3696     return 0;
3697     }
3698    
3699 niro 914 static void traceback(int signum)
3700     {
3701     void *array[40];
3702     size_t size;
3703    
3704     signal(SIGSEGV, SIG_DFL);
3705     memset(array, '\0', sizeof (array));
3706     size = backtrace(array, 40);
3707    
3708     fprintf(stderr, "grubby recieved SIGSEGV! Backtrace (%ld):\n",
3709     (unsigned long)size);
3710     backtrace_symbols_fd(array, size, STDERR_FILENO);
3711     exit(1);
3712     }
3713    
3714 niro 532 int main(int argc, const char ** argv) {
3715     poptContext optCon;
3716 niro 1696 const char * grubConfig = NULL;
3717 niro 532 char * outputFile = NULL;
3718     int arg = 0;
3719     int flags = 0;
3720     int badImageOkay = 0;
3721 niro 1696 int configureGrub2 = 0;
3722 niro 532 int configureLilo = 0, configureELilo = 0, configureGrub = 0;
3723     int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
3724 niro 914 int configureExtLinux = 0;
3725 niro 532 int bootloaderProbe = 0;
3726 niro 914 int extraInitrdCount = 0;
3727 niro 532 char * updateKernelPath = NULL;
3728     char * newKernelPath = NULL;
3729     char * removeKernelPath = NULL;
3730     char * newKernelArgs = NULL;
3731     char * newKernelInitrd = NULL;
3732     char * newKernelTitle = NULL;
3733     char * newKernelVersion = NULL;
3734     char * newMBKernel = NULL;
3735     char * newMBKernelArgs = NULL;
3736     char * removeMBKernelArgs = NULL;
3737     char * removeMBKernel = NULL;
3738     char * bootPrefix = NULL;
3739     char * defaultKernel = NULL;
3740     char * removeArgs = NULL;
3741     char * kernelInfo = NULL;
3742 niro 914 char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
3743 niro 532 const char * chptr = NULL;
3744     struct configFileInfo * cfi = NULL;
3745     struct grubConfig * config;
3746     struct singleEntry * template = NULL;
3747     int copyDefault = 0, makeDefault = 0;
3748     int displayDefault = 0;
3749 niro 1720 int displayDefaultIndex = 0;
3750 niro 1721 int displayDefaultTitle = 0;
3751 niro 1859 int defaultIndex = -1;
3752 niro 532 struct poptOption options[] = {
3753     { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
3754     _("add an entry for the specified kernel"), _("kernel-path") },
3755     { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
3756     _("add an entry for the specified multiboot kernel"), NULL },
3757     { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
3758     _("default arguments for the new kernel or new arguments for "
3759     "kernel being updated"), _("args") },
3760     { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
3761     _("default arguments for the new multiboot kernel or "
3762     "new arguments for multiboot kernel being updated"), NULL },
3763     { "bad-image-okay", 0, 0, &badImageOkay, 0,
3764     _("don't sanity check images in boot entries (for testing only)"),
3765     NULL },
3766     { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
3767     _("filestystem which contains /boot directory (for testing only)"),
3768     _("bootfs") },
3769 niro 1854 #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
3770 niro 532 { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
3771 niro 1853 _("check which bootloader is installed on boot sector") },
3772 niro 532 #endif
3773     { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
3774     _("path to grub config file to update (\"-\" for stdin)"),
3775     _("path") },
3776     { "copy-default", 0, 0, &copyDefault, 0,
3777     _("use the default boot entry as a template for the new entry "
3778     "being added; if the default is not a linux image, or if "
3779     "the kernel referenced by the default image does not exist, "
3780     "the first linux entry whose kernel does exist is used as the "
3781     "template"), NULL },
3782 niro 1736 { "debug", 0, 0, &debug, 0,
3783     _("print debugging information for failures") },
3784 niro 532 { "default-kernel", 0, 0, &displayDefault, 0,
3785     _("display the path of the default kernel") },
3786 niro 1720 { "default-index", 0, 0, &displayDefaultIndex, 0,
3787     _("display the index of the default kernel") },
3788 niro 1721 { "default-title", 0, 0, &displayDefaultTitle, 0,
3789     _("display the title of the default kernel") },
3790 niro 532 { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
3791     _("configure elilo bootloader") },
3792 niro 914 { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
3793     _("configure extlinux bootloader (from syslinux)") },
3794 niro 532 { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
3795     _("configure grub bootloader") },
3796 niro 1696 { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
3797     _("configure grub2 bootloader") },
3798 niro 532 { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
3799     _("display boot information for specified kernel"),
3800     _("kernel-path") },
3801     { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
3802     _("initrd image for the new kernel"), _("initrd-path") },
3803 niro 914 { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
3804     _("auxilliary initrd image for things other than the new kernel"), _("initrd-path") },
3805 niro 532 { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
3806     _("configure lilo bootloader") },
3807     { "make-default", 0, 0, &makeDefault, 0,
3808     _("make the newly added entry the default boot entry"), NULL },
3809     { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
3810     _("path to output updated config file (\"-\" for stdout)"),
3811     _("path") },
3812     { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
3813     _("remove kernel arguments"), NULL },
3814     { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
3815     _("remove multiboot kernel arguments"), NULL },
3816     { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
3817     _("remove all entries for the specified kernel"),
3818     _("kernel-path") },
3819     { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
3820     _("remove all entries for the specified multiboot kernel"), NULL },
3821     { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
3822     _("make the first entry referencing the specified kernel "
3823     "the default"), _("kernel-path") },
3824 niro 1859 { "set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
3825     _("make the given entry index the default entry"),
3826     _("entry-index") },
3827 niro 532 { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
3828     _("configure silo bootloader") },
3829     { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
3830     _("title to use for the new kernel entry"), _("entry-title") },
3831     { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
3832     _("updated information for the specified kernel"),
3833     _("kernel-path") },
3834     { "version", 'v', 0, NULL, 'v',
3835     _("print the version of this program and exit"), NULL },
3836     { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
3837     _("configure yaboot bootloader") },
3838     { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
3839     _("configure zipl bootloader") },
3840     POPT_AUTOHELP
3841     { 0, 0, 0, 0, 0 }
3842     };
3843    
3844 niro 914 useextlinuxmenu=0;
3845    
3846     signal(SIGSEGV, traceback);
3847    
3848 niro 532 optCon = poptGetContext("grubby", argc, argv, options, 0);
3849     poptReadDefaultConfig(optCon, 1);
3850    
3851     while ((arg = poptGetNextOpt(optCon)) >= 0) {
3852     switch (arg) {
3853     case 'v':
3854     printf("grubby version %s\n", VERSION);
3855     exit(0);
3856     break;
3857 niro 914 case 'i':
3858     if (extraInitrdCount < MAX_EXTRA_INITRDS) {
3859     extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
3860     } else {
3861     fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
3862     return 1;
3863     }
3864     break;
3865 niro 532 }
3866     }
3867    
3868     if (arg < -1) {
3869     fprintf(stderr, _("grubby: bad argument %s: %s\n"),
3870     poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
3871     poptStrerror(arg));
3872     return 1;
3873     }
3874    
3875     if ((chptr = poptGetArg(optCon))) {
3876     fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
3877     return 1;
3878     }
3879    
3880 niro 1696 if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
3881 niro 914 configureYaboot + configureSilo + configureZipl +
3882     configureExtLinux ) > 1) {
3883 niro 532 fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
3884     return 1;
3885     } else if (bootloaderProbe && grubConfig) {
3886     fprintf(stderr,
3887     _("grubby: cannot specify config file with --bootloader-probe\n"));
3888     return 1;
3889 niro 1696 } else if (configureGrub2) {
3890     cfi = &grub2ConfigType;
3891 niro 532 } else if (configureLilo) {
3892     cfi = &liloConfigType;
3893     } else if (configureGrub) {
3894     cfi = &grubConfigType;
3895     } else if (configureELilo) {
3896     cfi = &eliloConfigType;
3897     } else if (configureYaboot) {
3898     cfi = &yabootConfigType;
3899     } else if (configureSilo) {
3900     cfi = &siloConfigType;
3901     } else if (configureZipl) {
3902     cfi = &ziplConfigType;
3903 niro 914 } else if (configureExtLinux) {
3904     cfi = &extlinuxConfigType;
3905     useextlinuxmenu=1;
3906 niro 532 }
3907    
3908     if (!cfi) {
3909 niro 1802 if (grub2FindConfig(&grub2ConfigType))
3910     cfi = &grub2ConfigType;
3911     else
3912 niro 532 #ifdef __ia64__
3913 niro 1802 cfi = &eliloConfigType;
3914 niro 532 #elif __powerpc__
3915 niro 1802 cfi = &yabootConfigType;
3916 niro 532 #elif __sparc__
3917 niro 1802 cfi = &siloConfigType;
3918 niro 532 #elif __s390__
3919 niro 1802 cfi = &ziplConfigType;
3920 niro 532 #elif __s390x__
3921 niro 1802 cfi = &ziplConfigtype;
3922 niro 532 #else
3923 niro 1696 cfi = &grubConfigType;
3924 niro 532 #endif
3925     }
3926    
3927 niro 1696 if (!grubConfig) {
3928     if (cfi->findConfig)
3929     grubConfig = cfi->findConfig(cfi);
3930     if (!grubConfig)
3931     grubConfig = cfi->defaultConfig;
3932     }
3933 niro 532
3934     if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||
3935 niro 1859 newKernelPath || removeKernelPath || makeDefault ||
3936     defaultKernel || displayDefaultIndex || displayDefaultTitle ||
3937     (defaultIndex >= 0))) {
3938 niro 532 fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
3939     "specified option"));
3940     return 1;
3941     }
3942    
3943     if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||
3944     removeKernelPath)) {
3945     fprintf(stderr, _("grubby: --default-kernel and --info may not "
3946     "be used when adding or removing kernels\n"));
3947     return 1;
3948     }
3949    
3950     if (newKernelPath && !newKernelTitle) {
3951     fprintf(stderr, _("grubby: kernel title must be specified\n"));
3952     return 1;
3953 niro 1156 } else if (!newKernelPath && (newKernelTitle || copyDefault ||
3954     (newKernelInitrd && !updateKernelPath)||
3955 niro 914 makeDefault || extraInitrdCount > 0)) {
3956 niro 532 fprintf(stderr, _("grubby: kernel path expected\n"));
3957     return 1;
3958     }
3959    
3960     if (newKernelPath && updateKernelPath) {
3961     fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
3962     "not be used together"));
3963     return 1;
3964     }
3965    
3966     if (makeDefault && defaultKernel) {
3967     fprintf(stderr, _("grubby: --make-default and --default-kernel "
3968     "may not be used together\n"));
3969     return 1;
3970     } else if (defaultKernel && removeKernelPath &&
3971     !strcmp(defaultKernel, removeKernelPath)) {
3972     fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));
3973     return 1;
3974     } else if (defaultKernel && newKernelPath &&
3975     !strcmp(defaultKernel, newKernelPath)) {
3976     makeDefault = 1;
3977     defaultKernel = NULL;
3978     }
3979 niro 1859 else if (defaultKernel && (defaultIndex >= 0)) {
3980     fprintf(stderr, _("grubby: --set-default and --set-default-index "
3981     "may not be used together\n"));
3982     return 1;
3983     }
3984 niro 532
3985 niro 1717 if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
3986 niro 532 fprintf(stderr, _("grubby: output file must be specified if stdin "
3987     "is used\n"));
3988     return 1;
3989     }
3990    
3991     if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
3992 niro 1859 && !kernelInfo && !bootloaderProbe && !updateKernelPath
3993     && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle
3994     && (defaultIndex == -1)) {
3995 niro 532 fprintf(stderr, _("grubby: no action specified\n"));
3996     return 1;
3997     }
3998    
3999     flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
4000    
4001     if (cfi->needsBootPrefix) {
4002     if (!bootPrefix) {
4003     bootPrefix = findBootPrefix();
4004     if (!bootPrefix) return 1;
4005     } else {
4006     /* this shouldn't end with a / */
4007     if (bootPrefix[strlen(bootPrefix) - 1] == '/')
4008     bootPrefix[strlen(bootPrefix) - 1] = '\0';
4009     }
4010     } else {
4011     bootPrefix = "";
4012     }
4013    
4014 niro 914 if (!cfi->mbAllowExtraInitRds &&
4015     extraInitrdCount > 0) {
4016     fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
4017     return 1;
4018     }
4019    
4020 niro 532 if (bootloaderProbe) {
4021 niro 1854 int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
4022     struct grubConfig * lconfig, * gconfig, * yconfig, * econfig;
4023 niro 532
4024 niro 1696 const char *grub2config = grub2FindConfig(&grub2ConfigType);
4025     if (grub2config) {
4026     gconfig = readConfig(grub2config, &grub2ConfigType);
4027     if (!gconfig)
4028     gr2c = 1;
4029     else
4030     gr2c = checkForGrub2(gconfig);
4031     }
4032    
4033 niro 1715 const char *grubconfig = grubFindConfig(&grubConfigType);
4034     if (!access(grubconfig, F_OK)) {
4035     gconfig = readConfig(grubconfig, &grubConfigType);
4036 niro 532 if (!gconfig)
4037     grc = 1;
4038     else
4039     grc = checkForGrub(gconfig);
4040     }
4041    
4042     if (!access(liloConfigType.defaultConfig, F_OK)) {
4043     lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);
4044     if (!lconfig)
4045     lrc = 1;
4046     else
4047     lrc = checkForLilo(lconfig);
4048     }
4049    
4050 niro 1854 if (!access(eliloConfigType.defaultConfig, F_OK)) {
4051     econfig = readConfig(eliloConfigType.defaultConfig,
4052     &eliloConfigType);
4053     if (!econfig)
4054     erc = 1;
4055     else
4056     erc = checkForElilo(econfig);
4057     }
4058    
4059 niro 914 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
4060     lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
4061     if (!lconfig)
4062 niro 1854 extrc = 1;
4063 niro 914 else
4064 niro 1854 extrc = checkForExtLinux(lconfig);
4065 niro 914 }
4066    
4067 niro 532
4068 niro 1853 if (!access(yabootConfigType.defaultConfig, F_OK)) {
4069     yconfig = readConfig(yabootConfigType.defaultConfig,
4070     &yabootConfigType);
4071     if (!yconfig)
4072     yrc = 1;
4073     else
4074 niro 1864 yrc = checkForYaboot(yconfig);
4075 niro 1853 }
4076    
4077 niro 1854 if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1 ||
4078 niro 1855 erc == 1)
4079 niro 1854 return 1;
4080 niro 1853
4081 niro 532 if (lrc == 2) printf("lilo\n");
4082 niro 1696 if (gr2c == 2) printf("grub2\n");
4083 niro 532 if (grc == 2) printf("grub\n");
4084 niro 1854 if (extrc == 2) printf("extlinux\n");
4085 niro 1853 if (yrc == 2) printf("yaboot\n");
4086 niro 1854 if (erc == 2) printf("elilo\n");
4087 niro 532
4088     return 0;
4089     }
4090    
4091     config = readConfig(grubConfig, cfi);
4092     if (!config) return 1;
4093    
4094     if (displayDefault) {
4095     struct singleLine * line;
4096     struct singleEntry * entry;
4097     char * rootspec;
4098    
4099     if (config->defaultImage == -1) return 0;
4100     entry = findEntryByIndex(config, config->defaultImage);
4101     if (!entry) return 0;
4102     if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
4103    
4104 niro 914 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
4105 niro 532 if (!line) return 0;
4106    
4107     rootspec = getRootSpecifier(line->elements[1].item);
4108     printf("%s%s\n", bootPrefix, line->elements[1].item +
4109     ((rootspec != NULL) ? strlen(rootspec) : 0));
4110    
4111     return 0;
4112 niro 1720
4113 niro 1721 } else if (displayDefaultTitle) {
4114     struct singleLine * line;
4115     struct singleEntry * entry;
4116    
4117     if (config->defaultImage == -1) return 0;
4118     entry = findEntryByIndex(config, config->defaultImage);
4119     if (!entry) return 0;
4120    
4121     if (!configureGrub2) {
4122     line = getLineByType(LT_TITLE, entry->lines);
4123     if (!line) return 0;
4124     printf("%s\n", line->elements[1].item);
4125    
4126     } else {
4127 niro 1746 char * title;
4128 niro 1721
4129     dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
4130     line = getLineByType(LT_MENUENTRY, entry->lines);
4131     if (!line) return 0;
4132 niro 1746 title = grub2ExtractTitle(line);
4133     if (title)
4134     printf("%s\n", title);
4135 niro 1721 }
4136     return 0;
4137    
4138 niro 1720 } else if (displayDefaultIndex) {
4139     if (config->defaultImage == -1) return 0;
4140     printf("%i\n", config->defaultImage);
4141    
4142 niro 532 } else if (kernelInfo)
4143     return displayInfo(config, kernelInfo, bootPrefix);
4144    
4145     if (copyDefault) {
4146     template = findTemplate(config, bootPrefix, NULL, 0, flags);
4147     if (!template) return 1;
4148     }
4149    
4150     markRemovedImage(config, removeKernelPath, bootPrefix);
4151     markRemovedImage(config, removeMBKernel, bootPrefix);
4152     setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
4153 niro 1859 bootPrefix, flags, defaultIndex);
4154 niro 532 setFallbackImage(config, newKernelPath != NULL);
4155     if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
4156     removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
4157 niro 1156 if (updateKernelPath && newKernelInitrd) {
4158     if (updateInitrd(config, updateKernelPath, bootPrefix,
4159     newKernelInitrd)) return 1;
4160     }
4161 niro 532 if (addNewKernel(config, template, bootPrefix, newKernelPath,
4162     newKernelTitle, newKernelArgs, newKernelInitrd,
4163 niro 1844 (const char **)extraInitrds, extraInitrdCount,
4164 niro 532 newMBKernel, newMBKernelArgs)) return 1;
4165    
4166    
4167     if (numEntries(config) == 0) {
4168     fprintf(stderr, _("grubby: doing this would leave no kernel entries. "
4169     "Not writing out new config.\n"));
4170     return 1;
4171     }
4172    
4173     if (!outputFile)
4174 niro 1696 outputFile = (char *)grubConfig;
4175 niro 532
4176     return writeConfig(config, outputFile, bootPrefix);
4177     }