Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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