Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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