Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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