Magellan Linux

Annotation of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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