Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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