Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2992 - (hide annotations) (download)
Thu Jun 30 10:35:14 2016 UTC (7 years, 10 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 131623 byte(s)
Add a bunch of tests for various --default-{kernel,title,index}.
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     static void writeDefault(FILE * out, char * indent,
1515     char * separator, struct grubConfig * cfg) {
1516     struct singleEntry * entry;
1517     struct singleLine * line;
1518     int i;
1519    
1520     if (!cfg->defaultImage && cfg->flags == GRUB_CONFIG_NO_DEFAULT) return;
1521    
1522     if (cfg->defaultImage == DEFAULT_SAVED)
1523     fprintf(out, "%sdefault%ssaved\n", indent, separator);
1524 niro 2252 else if (cfg->cfi->defaultIsSaved) {
1525 niro 1748 fprintf(out, "%sset default=\"${saved_entry}\"\n", indent);
1526 niro 2252 if (cfg->defaultImage >= 0 && cfg->cfi->setEnv) {
1527     char *title;
1528     entry = findEntryByIndex(cfg, cfg->defaultImage);
1529     line = getLineByType(LT_MENUENTRY, entry->lines);
1530     if (!line)
1531     line = getLineByType(LT_TITLE, entry->lines);
1532     if (line) {
1533 niro 2980 title = extractTitle(cfg, line);
1534 niro 2252 if (title)
1535     cfg->cfi->setEnv(cfg->cfi, "saved_entry", title);
1536     }
1537     }
1538     } else if (cfg->defaultImage > -1) {
1539 niro 532 if (cfg->cfi->defaultIsIndex) {
1540 niro 1696 if (cfg->cfi->defaultIsVariable) {
1541     fprintf(out, "%sset default=\"%d\"\n", indent,
1542     cfg->defaultImage);
1543     } else {
1544     fprintf(out, "%sdefault%s%d\n", indent, separator,
1545     cfg->defaultImage);
1546     }
1547 niro 532 } else {
1548     int image = cfg->defaultImage;
1549    
1550     entry = cfg->entries;
1551     while (entry && entry->skip) entry = entry->next;
1552    
1553     i = 0;
1554     while (entry && i < image) {
1555     entry = entry->next;
1556    
1557     while (entry && entry->skip) entry = entry->next;
1558     i++;
1559     }
1560    
1561     if (!entry) return;
1562    
1563 niro 914 line = getLineByType(LT_TITLE, entry->lines);
1564 niro 532
1565     if (line && line->numElements >= 2)
1566     fprintf(out, "%sdefault%s%s\n", indent, separator,
1567     line->elements[1].item);
1568     else if (line && (line->numElements == 1) &&
1569     cfg->cfi->titleBracketed) {
1570     fprintf(out, "%sdefault%s%s\n", indent, separator,
1571 niro 2980 extractTitle(cfg, line));
1572 niro 532 }
1573     }
1574     }
1575     }
1576    
1577     static int writeConfig(struct grubConfig * cfg, char * outName,
1578     const char * prefix) {
1579     FILE * out;
1580     struct singleLine * line;
1581     struct singleEntry * entry;
1582     char * tmpOutName;
1583     int needs = MAIN_DEFAULT;
1584     struct stat sb;
1585     int i;
1586    
1587     if (!strcmp(outName, "-")) {
1588     out = stdout;
1589     tmpOutName = NULL;
1590     } else {
1591     if (!lstat(outName, &sb) && S_ISLNK(sb.st_mode)) {
1592     char * buf;
1593     int len = 256;
1594     int rc;
1595    
1596     /* most likely the symlink is relative, so change our
1597 niro 914 directory to the dir of the symlink */
1598 niro 1868 char *dir = strdupa(outName);
1599 niro 1934 rc = chdir(dirname(dir));
1600 niro 532 do {
1601     buf = alloca(len + 1);
1602 niro 914 rc = readlink(basename(outName), buf, len);
1603 niro 532 if (rc == len) len += 256;
1604     } while (rc == len);
1605    
1606     if (rc < 0) {
1607     fprintf(stderr, _("grubby: error readlink link %s: %s\n"),
1608     outName, strerror(errno));
1609     return 1;
1610     }
1611    
1612     outName = buf;
1613     outName[rc] = '\0';
1614     }
1615    
1616     tmpOutName = alloca(strlen(outName) + 2);
1617     sprintf(tmpOutName, "%s-", outName);
1618     out = fopen(tmpOutName, "w");
1619     if (!out) {
1620     fprintf(stderr, _("grubby: error creating %s: %s\n"), tmpOutName,
1621     strerror(errno));
1622     return 1;
1623     }
1624    
1625     if (!stat(outName, &sb)) {
1626     if (chmod(tmpOutName, sb.st_mode & ~(S_IFMT))) {
1627     fprintf(stderr, _("grubby: error setting perms on %s: %s\n"),
1628     tmpOutName, strerror(errno));
1629     fclose(out);
1630     unlink(tmpOutName);
1631     return 1;
1632     }
1633     }
1634     }
1635    
1636     line = cfg->theLines;
1637 niro 1696 struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi);
1638 niro 532 while (line) {
1639 niro 1696 if (line->type == LT_SET_VARIABLE && defaultKw &&
1640     line->numElements == 3 &&
1641 niro 2252 !strcmp(line->elements[1].item, defaultKw->key) &&
1642     !is_special_grub2_variable(line->elements[2].item)) {
1643 niro 532 writeDefault(out, line->indent, line->elements[0].indent, cfg);
1644     needs &= ~MAIN_DEFAULT;
1645 niro 1696 } else if (line->type == LT_DEFAULT) {
1646     writeDefault(out, line->indent, line->elements[0].indent, cfg);
1647     needs &= ~MAIN_DEFAULT;
1648 niro 532 } else if (line->type == LT_FALLBACK) {
1649     if (cfg->fallbackImage > -1)
1650     fprintf(out, "%s%s%s%d\n", line->indent,
1651     line->elements[0].item, line->elements[0].indent,
1652     cfg->fallbackImage);
1653     } else {
1654     if (lineWrite(out, line, cfg->cfi) == -1) {
1655     fprintf(stderr, _("grubby: error writing %s: %s\n"),
1656     tmpOutName, strerror(errno));
1657     fclose(out);
1658     unlink(tmpOutName);
1659     return 1;
1660     }
1661     }
1662    
1663     line = line->next;
1664     }
1665    
1666     if (needs & MAIN_DEFAULT) {
1667     writeDefault(out, cfg->primaryIndent, "=", cfg);
1668     needs &= ~MAIN_DEFAULT;
1669     }
1670    
1671     i = 0;
1672     while ((entry = findEntryByIndex(cfg, i++))) {
1673     if (entry->skip) continue;
1674    
1675     line = entry->lines;
1676     while (line) {
1677     if (lineWrite(out, line, cfg->cfi) == -1) {
1678     fprintf(stderr, _("grubby: error writing %s: %s\n"),
1679     tmpOutName, strerror(errno));
1680     fclose(out);
1681     unlink(tmpOutName);
1682     return 1;
1683     }
1684     line = line->next;
1685     }
1686     }
1687    
1688     if (tmpOutName) {
1689     if (rename(tmpOutName, outName)) {
1690     fprintf(stderr, _("grubby: error moving %s to %s: %s\n"),
1691     tmpOutName, outName, strerror(errno));
1692     unlink(outName);
1693     return 1;
1694     }
1695     }
1696    
1697     return 0;
1698     }
1699    
1700     static int numEntries(struct grubConfig *cfg) {
1701     int i = 0;
1702     struct singleEntry * entry;
1703    
1704     entry = cfg->entries;
1705     while (entry) {
1706     if (!entry->skip)
1707     i++;
1708     entry = entry->next;
1709     }
1710     return i;
1711     }
1712    
1713 niro 1156 static char *findDiskForRoot()
1714     {
1715     int fd;
1716     char buf[65536];
1717     char *devname;
1718     char *chptr;
1719     int rc;
1720    
1721     if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {
1722     fprintf(stderr, "grubby: failed to open %s: %s\n",
1723     _PATH_MOUNTED, strerror(errno));
1724     return NULL;
1725     }
1726    
1727     rc = read(fd, buf, sizeof(buf) - 1);
1728     if (rc <= 0) {
1729     fprintf(stderr, "grubby: failed to read %s: %s\n",
1730     _PATH_MOUNTED, strerror(errno));
1731     close(fd);
1732     return NULL;
1733     }
1734     close(fd);
1735     buf[rc] = '\0';
1736     chptr = buf;
1737    
1738 niro 1841 char *foundanswer = NULL;
1739    
1740 niro 1156 while (chptr && chptr != buf+rc) {
1741     devname = chptr;
1742    
1743     /*
1744     * The first column of a mtab entry is the device, but if the entry is a
1745     * special device it won't start with /, so move on to the next line.
1746     */
1747     if (*devname != '/') {
1748     chptr = strchr(chptr, '\n');
1749     if (chptr)
1750     chptr++;
1751     continue;
1752     }
1753    
1754     /* Seek to the next space */
1755     chptr = strchr(chptr, ' ');
1756     if (!chptr) {
1757     fprintf(stderr, "grubby: error parsing %s: %s\n",
1758     _PATH_MOUNTED, strerror(errno));
1759     return NULL;
1760     }
1761    
1762     /*
1763     * The second column of a mtab entry is the mount point, we are looking
1764     * for '/' obviously.
1765     */
1766     if (*(++chptr) == '/' && *(++chptr) == ' ') {
1767 niro 1841 /* remember the last / entry in mtab */
1768     foundanswer = devname;
1769 niro 1156 }
1770    
1771     /* Next line */
1772     chptr = strchr(chptr, '\n');
1773     if (chptr)
1774     chptr++;
1775     }
1776    
1777 niro 1841 /* Return the last / entry found */
1778     if (foundanswer) {
1779     chptr = strchr(foundanswer, ' ');
1780     *chptr = '\0';
1781     return strdup(foundanswer);
1782     }
1783    
1784 niro 1156 return NULL;
1785     }
1786    
1787 niro 2236 void printEntry(struct singleEntry * entry, FILE *f) {
1788 niro 1736 int i;
1789     struct singleLine * line;
1790    
1791     for (line = entry->lines; line; line = line->next) {
1792 niro 2236 log_message(f, "DBG: %s", line->indent);
1793 niro 1736 for (i = 0; i < line->numElements; i++) {
1794 niro 1801 /* Need to handle this, because we strip the quotes from
1795     * menuentry when read it. */
1796     if (line->type == LT_MENUENTRY && i == 1) {
1797     if(!isquote(*line->elements[i].item))
1798 niro 2236 log_message(f, "\'%s\'", line->elements[i].item);
1799 niro 1801 else
1800 niro 2236 log_message(f, "%s", line->elements[i].item);
1801     log_message(f, "%s", line->elements[i].indent);
1802 niro 1801
1803     continue;
1804     }
1805    
1806 niro 2236 log_message(f, "%s%s",
1807 niro 1736 line->elements[i].item, line->elements[i].indent);
1808     }
1809 niro 2236 log_message(f, "\n");
1810 niro 1736 }
1811     }
1812    
1813 niro 2236 void notSuitablePrintf(struct singleEntry * entry, int okay, const char *fmt, ...)
1814 niro 1736 {
1815 niro 2236 static int once;
1816     va_list argp, argq;
1817 niro 1736
1818 niro 2236 va_start(argp, fmt);
1819    
1820     va_copy(argq, argp);
1821     if (!once) {
1822     log_time(NULL);
1823     log_message(NULL, "command line: %s\n", saved_command_line);
1824     }
1825     log_message(NULL, "DBG: Image entry %s: ", okay ? "succeeded" : "failed");
1826     log_vmessage(NULL, fmt, argq);
1827    
1828     printEntry(entry, NULL);
1829     va_end(argq);
1830    
1831     if (!debug) {
1832     once = 1;
1833     va_end(argp);
1834 niro 1736 return;
1835 niro 2236 }
1836 niro 1736
1837 niro 2236 if (okay) {
1838     va_end(argp);
1839     return;
1840     }
1841    
1842     if (!once)
1843     log_message(stderr, "DBG: command line: %s\n", saved_command_line);
1844     once = 1;
1845 niro 1736 fprintf(stderr, "DBG: Image entry failed: ");
1846     vfprintf(stderr, fmt, argp);
1847 niro 2236 printEntry(entry, stderr);
1848 niro 1736 va_end(argp);
1849     }
1850    
1851 niro 1745 #define beginswith(s, c) ((s) && (s)[0] == (c))
1852    
1853     static int endswith(const char *s, char c)
1854     {
1855     int slen;
1856    
1857 niro 1750 if (!s || !s[0])
1858 niro 1745 return 0;
1859     slen = strlen(s) - 1;
1860    
1861     return s[slen] == c;
1862     }
1863    
1864 niro 532 int suitableImage(struct singleEntry * entry, const char * bootPrefix,
1865     int skipRemoved, int flags) {
1866     struct singleLine * line;
1867     char * fullName;
1868     int i;
1869     char * dev;
1870     char * rootspec;
1871 niro 1156 char * rootdev;
1872 niro 532
1873 niro 1736 if (skipRemoved && entry->skip) {
1874 niro 2236 notSuitablePrintf(entry, 0, "marked to skip\n");
1875 niro 1736 return 0;
1876     }
1877 niro 532
1878 niro 2683 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
1879 niro 1736 if (!line) {
1880 niro 2236 notSuitablePrintf(entry, 0, "no line found\n");
1881 niro 1736 return 0;
1882     }
1883     if (line->numElements < 2) {
1884 niro 2236 notSuitablePrintf(entry, 0, "line has only %d elements\n",
1885 niro 1736 line->numElements);
1886     return 0;
1887     }
1888 niro 914
1889 niro 2236 if (flags & GRUBBY_BADIMAGE_OKAY) {
1890     notSuitablePrintf(entry, 1, "\n");
1891     return 1;
1892     }
1893 niro 532
1894     fullName = alloca(strlen(bootPrefix) +
1895     strlen(line->elements[1].item) + 1);
1896     rootspec = getRootSpecifier(line->elements[1].item);
1897 niro 1745 int rootspec_offset = rootspec ? strlen(rootspec) : 0;
1898     int hasslash = endswith(bootPrefix, '/') ||
1899     beginswith(line->elements[1].item + rootspec_offset, '/');
1900     sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/",
1901     line->elements[1].item + rootspec_offset);
1902 niro 1736 if (access(fullName, R_OK)) {
1903 niro 2236 notSuitablePrintf(entry, 0, "access to %s failed\n", fullName);
1904 niro 1736 return 0;
1905     }
1906 niro 532 for (i = 2; i < line->numElements; i++)
1907     if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1908     if (i < line->numElements) {
1909     dev = line->elements[i].item + 5;
1910     } else {
1911     /* look for a lilo style LT_ROOT line */
1912 niro 914 line = getLineByType(LT_ROOT, entry->lines);
1913 niro 532
1914     if (line && line->numElements >= 2) {
1915     dev = line->elements[1].item;
1916     } else {
1917 niro 914 /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS.
1918     * grub+multiboot uses LT_MBMODULE for the args, so check that too.
1919     */
1920     line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines);
1921 niro 532
1922     /* failed to find one */
1923 niro 1736 if (!line) {
1924 niro 2236 notSuitablePrintf(entry, 0, "no line found\n");
1925 niro 1736 return 0;
1926     }
1927 niro 532
1928     for (i = 1; i < line->numElements; i++)
1929     if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1930     if (i < line->numElements)
1931     dev = line->elements[i].item + 5;
1932     else {
1933 niro 2236 notSuitablePrintf(entry, 0, "no root= entry found\n");
1934 niro 532 /* it failed too... can't find root= */
1935     return 0;
1936     }
1937     }
1938     }
1939    
1940 niro 914 dev = getpathbyspec(dev);
1941 niro 1736 if (!getpathbyspec(dev)) {
1942 niro 2236 notSuitablePrintf(entry, 0, "can't find blkid entry for %s\n", dev);
1943 niro 914 return 0;
1944 niro 1736 } else
1945     dev = getpathbyspec(dev);
1946 niro 532
1947 niro 1156 rootdev = findDiskForRoot();
1948 niro 1736 if (!rootdev) {
1949 niro 2236 notSuitablePrintf(entry, 0, "can't find root device\n");
1950 niro 914 return 0;
1951 niro 1736 }
1952 niro 914
1953 niro 1177 if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {
1954 niro 2236 notSuitablePrintf(entry, 0, "uuid missing: rootdev %s, dev %s\n",
1955 niro 1736 getuuidbydev(rootdev), getuuidbydev(dev));
1956 niro 1177 free(rootdev);
1957     return 0;
1958     }
1959 niro 532
1960 niro 1156 if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
1961 niro 2236 notSuitablePrintf(entry, 0, "uuid mismatch: rootdev %s, dev %s\n",
1962 niro 1736 getuuidbydev(rootdev), getuuidbydev(dev));
1963 niro 1156 free(rootdev);
1964 niro 914 return 0;
1965 niro 1156 }
1966 niro 532
1967 niro 1156 free(rootdev);
1968 niro 2236 notSuitablePrintf(entry, 1, "\n");
1969 niro 1156
1970 niro 532 return 1;
1971     }
1972    
1973     /* returns the first match on or after the one pointed to by index (if index
1974     is not NULL) which is not marked as skip */
1975     struct singleEntry * findEntryByPath(struct grubConfig * config,
1976     const char * kernel, const char * prefix,
1977     int * index) {
1978     struct singleEntry * entry = NULL;
1979     struct singleLine * line;
1980     int i;
1981     char * chptr;
1982     char * rootspec = NULL;
1983     enum lineType_e checkType = LT_KERNEL;
1984    
1985     if (isdigit(*kernel)) {
1986     int * indexVars = alloca(sizeof(*indexVars) * strlen(kernel));
1987    
1988     i = 0;
1989     indexVars[i] = strtol(kernel, &chptr, 10);
1990     while (*chptr == ',') {
1991     i++;
1992     kernel = chptr + 1;
1993     indexVars[i] = strtol(kernel, &chptr, 10);
1994     }
1995    
1996     if (*chptr) {
1997     /* can't parse it, bail */
1998     return NULL;
1999     }
2000    
2001     indexVars[i + 1] = -1;
2002 niro 2718
2003 niro 532 i = 0;
2004     if (index) {
2005 niro 2718 while (i < *index) {
2006     i++;
2007     if (indexVars[i] == -1) return NULL;
2008     }
2009 niro 532 }
2010    
2011     entry = findEntryByIndex(config, indexVars[i]);
2012     if (!entry) return NULL;
2013    
2014 niro 2683 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
2015 niro 532 if (!line) return NULL;
2016    
2017     if (index) *index = indexVars[i];
2018     return entry;
2019     }
2020    
2021     if (!strcmp(kernel, "DEFAULT")) {
2022     if (index && *index > config->defaultImage) {
2023     entry = NULL;
2024     } else {
2025     entry = findEntryByIndex(config, config->defaultImage);
2026     if (entry && entry->skip)
2027     entry = NULL;
2028     else if (index)
2029     *index = config->defaultImage;
2030     }
2031     } else if (!strcmp(kernel, "ALL")) {
2032     if (index)
2033     i = *index;
2034     else
2035     i = 0;
2036    
2037     while ((entry = findEntryByIndex(config, i))) {
2038     if (!entry->skip) break;
2039     i++;
2040     }
2041    
2042     if (entry && index)
2043     *index = i;
2044     } else {
2045     if (index)
2046     i = *index;
2047     else
2048     i = 0;
2049    
2050     if (!strncmp(kernel, "TITLE=", 6)) {
2051     prefix = "";
2052 niro 1801 checkType = LT_TITLE|LT_MENUENTRY;
2053 niro 532 kernel += 6;
2054     }
2055    
2056 niro 914 for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {
2057     if (entry->skip) continue;
2058 niro 532
2059 niro 914 dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);
2060 niro 532
2061 niro 914 /* check all the lines matching checkType */
2062     for (line = entry->lines; line; line = line->next) {
2063 niro 2052 enum lineType_e ct = checkType;
2064     if (entry->multiboot && checkType == LT_KERNEL)
2065 niro 2683 ct = LT_KERNEL|LT_KERNEL_EFI|LT_MBMODULE|LT_HYPER|LT_KERNEL_16;
2066 niro 2052 else if (checkType & LT_KERNEL)
2067 niro 2683 ct = checkType | LT_KERNEL_EFI | LT_KERNEL_16;
2068 niro 2052 line = getLineByType(ct, line);
2069     if (!line)
2070     break; /* not found in this entry */
2071 niro 532
2072 niro 1801 if (line && line->type != LT_MENUENTRY &&
2073     line->numElements >= 2) {
2074 niro 914 rootspec = getRootSpecifier(line->elements[1].item);
2075     if (!strcmp(line->elements[1].item +
2076     ((rootspec != NULL) ? strlen(rootspec) : 0),
2077     kernel + strlen(prefix)))
2078     break;
2079     }
2080 niro 1801 if(line->type == LT_MENUENTRY &&
2081     !strcmp(line->elements[1].item, kernel))
2082     break;
2083 niro 914 }
2084    
2085     /* make sure this entry has a kernel identifier; this skips
2086     * non-Linux boot entries (could find netbsd etc, though, which is
2087     * unfortunate)
2088     */
2089 niro 2683 if (line && getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines))
2090 niro 914 break; /* found 'im! */
2091 niro 532 }
2092    
2093     if (index) *index = i;
2094     }
2095    
2096     return entry;
2097     }
2098    
2099 niro 2252 struct singleEntry * findEntryByTitle(struct grubConfig * cfg, char *title,
2100     int * index) {
2101     struct singleEntry * entry;
2102     struct singleLine * line;
2103     int i;
2104     char * newtitle;
2105    
2106     for (i = 0, entry = cfg->entries; entry; entry = entry->next, i++) {
2107     if (index && i < *index)
2108     continue;
2109     line = getLineByType(LT_TITLE, entry->lines);
2110     if (!line)
2111     line = getLineByType(LT_MENUENTRY, entry->lines);
2112     if (!line)
2113     continue;
2114     newtitle = grub2ExtractTitle(line);
2115     if (!newtitle)
2116     continue;
2117     if (!strcmp(title, newtitle))
2118     break;
2119     }
2120    
2121     if (!entry)
2122     return NULL;
2123    
2124     if (index)
2125     *index = i;
2126     return entry;
2127     }
2128    
2129 niro 532 struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index) {
2130     struct singleEntry * entry;
2131    
2132     entry = cfg->entries;
2133     while (index && entry) {
2134     entry = entry->next;
2135     index--;
2136     }
2137    
2138     return entry;
2139     }
2140    
2141     /* Find a good template to use for the new kernel. An entry is
2142     * good if the kernel and mkinitrd exist (even if the entry
2143     * is going to be removed). Try and use the default entry, but
2144     * if that doesn't work just take the first. If we can't find one,
2145     * bail. */
2146     struct singleEntry * findTemplate(struct grubConfig * cfg, const char * prefix,
2147     int * indexPtr, int skipRemoved, int flags) {
2148     struct singleEntry * entry, * entry2;
2149     int index;
2150    
2151 niro 2252 if (cfg->cfi->defaultIsSaved) {
2152     if (cfg->cfi->getEnv) {
2153     char *defTitle = cfg->cfi->getEnv(cfg->cfi, "saved_entry");
2154     if (defTitle) {
2155     int index = 0;
2156 niro 2258 if (isnumber(defTitle)) {
2157     index = atoi(defTitle);
2158     entry = findEntryByIndex(cfg, index);
2159     } else {
2160     entry = findEntryByTitle(cfg, defTitle, &index);
2161     }
2162 niro 2959 if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2163 niro 2258 cfg->defaultImage = index;
2164 niro 2959 if (indexPtr)
2165     *indexPtr = index;
2166     return entry;
2167     }
2168 niro 2252 }
2169     }
2170     } else if (cfg->defaultImage > -1) {
2171 niro 532 entry = findEntryByIndex(cfg, cfg->defaultImage);
2172     if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2173     if (indexPtr) *indexPtr = cfg->defaultImage;
2174     return entry;
2175     }
2176     }
2177    
2178     index = 0;
2179     while ((entry = findEntryByIndex(cfg, index))) {
2180     if (suitableImage(entry, prefix, skipRemoved, flags)) {
2181     int j;
2182     for (j = 0; j < index; j++) {
2183     entry2 = findEntryByIndex(cfg, j);
2184     if (entry2->skip) index--;
2185     }
2186     if (indexPtr) *indexPtr = index;
2187    
2188     return entry;
2189     }
2190    
2191     index++;
2192     }
2193    
2194     fprintf(stderr, _("grubby fatal error: unable to find a suitable template\n"));
2195    
2196     return NULL;
2197     }
2198    
2199     char * findBootPrefix(void) {
2200     struct stat sb, sb2;
2201    
2202     stat("/", &sb);
2203     #ifdef __ia64__
2204     stat("/boot/efi/EFI/redhat/", &sb2);
2205     #else
2206     stat("/boot", &sb2);
2207     #endif
2208    
2209     if (sb.st_dev == sb2.st_dev)
2210     return strdup("");
2211    
2212     #ifdef __ia64__
2213     return strdup("/boot/efi/EFI/redhat/");
2214     #else
2215     return strdup("/boot");
2216     #endif
2217     }
2218    
2219     void markRemovedImage(struct grubConfig * cfg, const char * image,
2220     const char * prefix) {
2221     struct singleEntry * entry;
2222    
2223 niro 1801 if (!image)
2224     return;
2225 niro 532
2226 niro 1801 /* check and see if we're removing the default image */
2227     if (isdigit(*image)) {
2228     entry = findEntryByPath(cfg, image, prefix, NULL);
2229     if(entry)
2230     entry->skip = 1;
2231     return;
2232     }
2233    
2234 niro 532 while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
2235     entry->skip = 1;
2236     }
2237    
2238     void setDefaultImage(struct grubConfig * config, int hasNew,
2239     const char * defaultKernelPath, int newIsDefault,
2240 niro 1859 const char * prefix, int flags, int index) {
2241 niro 532 struct singleEntry * entry, * entry2, * newDefault;
2242     int i, j;
2243    
2244     if (newIsDefault) {
2245     config->defaultImage = 0;
2246     return;
2247 niro 1859 } else if ((index >= 0) && config->cfi->defaultIsIndex) {
2248     if (findEntryByIndex(config, index))
2249     config->defaultImage = index;
2250     else
2251     config->defaultImage = -1;
2252     return;
2253 niro 532 } else if (defaultKernelPath) {
2254     i = 0;
2255     if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
2256     config->defaultImage = i;
2257     } else {
2258     config->defaultImage = -1;
2259     return;
2260     }
2261     }
2262    
2263     /* defaultImage now points to what we'd like to use, but before any order
2264     changes */
2265 niro 1748 if ((config->defaultImage == DEFAULT_SAVED) ||
2266     (config->defaultImage == DEFAULT_SAVED_GRUB2))
2267 niro 532 /* default is set to saved, we don't want to change it */
2268     return;
2269    
2270     if (config->defaultImage > -1)
2271     entry = findEntryByIndex(config, config->defaultImage);
2272     else
2273     entry = NULL;
2274    
2275     if (entry && !entry->skip) {
2276     /* we can preserve the default */
2277     if (hasNew)
2278     config->defaultImage++;
2279    
2280     /* count the number of entries erased before this one */
2281     for (j = 0; j < config->defaultImage; j++) {
2282     entry2 = findEntryByIndex(config, j);
2283     if (entry2->skip) config->defaultImage--;
2284     }
2285     } else if (hasNew) {
2286     config->defaultImage = 0;
2287     } else {
2288     /* Either we just erased the default (or the default line was bad
2289     * to begin with) and didn't put a new one in. We'll use the first
2290     * valid image. */
2291     newDefault = findTemplate(config, prefix, &config->defaultImage, 1,
2292     flags);
2293     if (!newDefault)
2294     config->defaultImage = -1;
2295     }
2296     }
2297    
2298     void setFallbackImage(struct grubConfig * config, int hasNew) {
2299     struct singleEntry * entry, * entry2;
2300     int j;
2301    
2302     if (config->fallbackImage == -1) return;
2303    
2304     entry = findEntryByIndex(config, config->fallbackImage);
2305     if (!entry || entry->skip) {
2306     config->fallbackImage = -1;
2307     return;
2308     }
2309    
2310     if (hasNew)
2311     config->fallbackImage++;
2312    
2313     /* count the number of entries erased before this one */
2314     for (j = 0; j < config->fallbackImage; j++) {
2315     entry2 = findEntryByIndex(config, j);
2316     if (entry2->skip) config->fallbackImage--;
2317     }
2318     }
2319    
2320     void displayEntry(struct singleEntry * entry, const char * prefix, int index) {
2321     struct singleLine * line;
2322     char * root = NULL;
2323     int i;
2324 niro 2708 int j;
2325 niro 532
2326     printf("index=%d\n", index);
2327    
2328 niro 2683 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
2329 niro 914 if (!line) {
2330     printf("non linux entry\n");
2331     return;
2332     }
2333    
2334 niro 1849 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2335     printf("kernel=%s\n", line->elements[1].item);
2336     else
2337     printf("kernel=%s%s\n", prefix, line->elements[1].item);
2338 niro 532
2339     if (line->numElements >= 3) {
2340     printf("args=\"");
2341     i = 2;
2342     while (i < line->numElements) {
2343     if (!strncmp(line->elements[i].item, "root=", 5)) {
2344     root = line->elements[i].item + 5;
2345     } else {
2346     printf("%s%s", line->elements[i].item,
2347     line->elements[i].indent);
2348     }
2349    
2350     i++;
2351     }
2352     printf("\"\n");
2353     } else {
2354 niro 914 line = getLineByType(LT_KERNELARGS, entry->lines);
2355 niro 532 if (line) {
2356     char * s;
2357    
2358     printf("args=\"");
2359     i = 1;
2360     while (i < line->numElements) {
2361     if (!strncmp(line->elements[i].item, "root=", 5)) {
2362     root = line->elements[i].item + 5;
2363     } else {
2364     s = line->elements[i].item;
2365    
2366     printf("%s%s", s, line->elements[i].indent);
2367     }
2368    
2369     i++;
2370     }
2371    
2372     s = line->elements[i - 1].indent;
2373     printf("\"\n");
2374     }
2375     }
2376    
2377     if (!root) {
2378 niro 914 line = getLineByType(LT_ROOT, entry->lines);
2379 niro 532 if (line && line->numElements >= 2)
2380     root=line->elements[1].item;
2381     }
2382    
2383     if (root) {
2384     char * s = alloca(strlen(root) + 1);
2385    
2386     strcpy(s, root);
2387     if (s[strlen(s) - 1] == '"')
2388     s[strlen(s) - 1] = '\0';
2389     /* make sure the root doesn't have a trailing " */
2390     printf("root=%s\n", s);
2391     }
2392    
2393 niro 2683 line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
2394 niro 532
2395     if (line && line->numElements >= 2) {
2396 niro 1849 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2397     printf("initrd=");
2398     else
2399     printf("initrd=%s", prefix);
2400    
2401 niro 532 for (i = 1; i < line->numElements; i++)
2402     printf("%s%s", line->elements[i].item, line->elements[i].indent);
2403     printf("\n");
2404     }
2405 niro 1751
2406     line = getLineByType(LT_TITLE, entry->lines);
2407     if (line) {
2408     printf("title=%s\n", line->elements[1].item);
2409     } else {
2410     char * title;
2411     line = getLineByType(LT_MENUENTRY, entry->lines);
2412 niro 2963 if (line) {
2413 niro 2962 title = grub2ExtractTitle(line);
2414     if (title)
2415     printf("title=%s\n", title);
2416     }
2417 niro 1751 }
2418 niro 2708
2419     for (j = 0, line = entry->lines; line; line = line->next) {
2420     if ((line->type & LT_MBMODULE) && line->numElements >= 2) {
2421     if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2422     printf("mbmodule%d=", j);
2423     else
2424     printf("mbmodule%d=%s", j, prefix);
2425    
2426     for (i = 1; i < line->numElements; i++)
2427     printf("%s%s", line->elements[i].item, line->elements[i].indent);
2428     printf("\n");
2429     j++;
2430     }
2431     }
2432 niro 532 }
2433    
2434 niro 1850 int isSuseSystem(void) {
2435     const char * path;
2436     const static char default_path[] = "/etc/SuSE-release";
2437    
2438     if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2439     path = default_path;
2440    
2441     if (!access(path, R_OK))
2442     return 1;
2443     return 0;
2444     }
2445    
2446     int isSuseGrubConf(const char * path) {
2447     FILE * grubConf;
2448     char * line = NULL;
2449     size_t len = 0, res = 0;
2450    
2451     grubConf = fopen(path, "r");
2452     if (!grubConf) {
2453     dbgPrintf("Could not open SuSE configuration file '%s'\n", path);
2454     return 0;
2455     }
2456    
2457     while ((res = getline(&line, &len, grubConf)) != -1) {
2458     if (!strncmp(line, "setup", 5)) {
2459     fclose(grubConf);
2460     free(line);
2461     return 1;
2462     }
2463     }
2464    
2465     dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2466     path);
2467    
2468     fclose(grubConf);
2469     free(line);
2470     return 0;
2471     }
2472    
2473     int suseGrubConfGetLba(const char * path, int * lbaPtr) {
2474     FILE * grubConf;
2475     char * line = NULL;
2476     size_t res = 0, len = 0;
2477    
2478     if (!path) return 1;
2479     if (!lbaPtr) return 1;
2480    
2481     grubConf = fopen(path, "r");
2482     if (!grubConf) return 1;
2483    
2484     while ((res = getline(&line, &len, grubConf)) != -1) {
2485     if (line[res - 1] == '\n')
2486     line[res - 1] = '\0';
2487     else if (len > res)
2488     line[res] = '\0';
2489     else {
2490     line = realloc(line, res + 1);
2491     line[res] = '\0';
2492     }
2493    
2494     if (!strncmp(line, "setup", 5)) {
2495     if (strstr(line, "--force-lba")) {
2496     *lbaPtr = 1;
2497     } else {
2498     *lbaPtr = 0;
2499     }
2500     dbgPrintf("lba: %i\n", *lbaPtr);
2501     break;
2502     }
2503     }
2504    
2505     free(line);
2506     fclose(grubConf);
2507     return 0;
2508     }
2509    
2510     int suseGrubConfGetInstallDevice(const char * path, char ** devicePtr) {
2511     FILE * grubConf;
2512     char * line = NULL;
2513     size_t res = 0, len = 0;
2514     char * lastParamPtr = NULL;
2515     char * secLastParamPtr = NULL;
2516     char installDeviceNumber = '\0';
2517     char * bounds = NULL;
2518    
2519     if (!path) return 1;
2520     if (!devicePtr) return 1;
2521    
2522     grubConf = fopen(path, "r");
2523     if (!grubConf) return 1;
2524    
2525     while ((res = getline(&line, &len, grubConf)) != -1) {
2526     if (strncmp(line, "setup", 5))
2527     continue;
2528    
2529     if (line[res - 1] == '\n')
2530     line[res - 1] = '\0';
2531     else if (len > res)
2532     line[res] = '\0';
2533     else {
2534     line = realloc(line, res + 1);
2535     line[res] = '\0';
2536     }
2537    
2538     lastParamPtr = bounds = line + res;
2539    
2540     /* Last parameter in grub may be an optional IMAGE_DEVICE */
2541     while (!isspace(*lastParamPtr))
2542     lastParamPtr--;
2543     lastParamPtr++;
2544    
2545     secLastParamPtr = lastParamPtr - 2;
2546     dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2547    
2548     if (lastParamPtr + 3 > bounds) {
2549     dbgPrintf("lastParamPtr going over boundary");
2550     fclose(grubConf);
2551     free(line);
2552     return 1;
2553     }
2554     if (!strncmp(lastParamPtr, "(hd", 3))
2555     lastParamPtr += 3;
2556     dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2557    
2558     /*
2559     * Second last parameter will decide wether last parameter is
2560     * an IMAGE_DEVICE or INSTALL_DEVICE
2561     */
2562     while (!isspace(*secLastParamPtr))
2563     secLastParamPtr--;
2564     secLastParamPtr++;
2565    
2566     if (secLastParamPtr + 3 > bounds) {
2567     dbgPrintf("secLastParamPtr going over boundary");
2568     fclose(grubConf);
2569     free(line);
2570     return 1;
2571     }
2572     dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2573     if (!strncmp(secLastParamPtr, "(hd", 3)) {
2574     secLastParamPtr += 3;
2575     dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
2576     installDeviceNumber = *secLastParamPtr;
2577     } else {
2578     installDeviceNumber = *lastParamPtr;
2579     }
2580    
2581     *devicePtr = malloc(6);
2582     snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
2583     dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
2584     fclose(grubConf);
2585     free(line);
2586     return 0;
2587     }
2588    
2589     free(line);
2590     fclose(grubConf);
2591     return 1;
2592     }
2593    
2594     int grubGetBootFromDeviceMap(const char * device,
2595     char ** bootPtr) {
2596     FILE * deviceMap;
2597     char * line = NULL;
2598     size_t res = 0, len = 0;
2599     char * devicePtr;
2600     char * bounds = NULL;
2601     const char * path;
2602     const static char default_path[] = "/boot/grub/device.map";
2603    
2604     if (!device) return 1;
2605     if (!bootPtr) return 1;
2606    
2607     if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2608     path = default_path;
2609    
2610     dbgPrintf("opening grub device.map file from: %s\n", path);
2611     deviceMap = fopen(path, "r");
2612     if (!deviceMap)
2613     return 1;
2614    
2615     while ((res = getline(&line, &len, deviceMap)) != -1) {
2616     if (!strncmp(line, "#", 1))
2617     continue;
2618    
2619     if (line[res - 1] == '\n')
2620     line[res - 1] = '\0';
2621     else if (len > res)
2622     line[res] = '\0';
2623     else {
2624     line = realloc(line, res + 1);
2625     line[res] = '\0';
2626     }
2627    
2628     devicePtr = line;
2629     bounds = line + res;
2630    
2631     while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2632     devicePtr++;
2633     dbgPrintf("device: %s\n", devicePtr);
2634    
2635     if (!strncmp(devicePtr, device, strlen(device))) {
2636     devicePtr += strlen(device);
2637     while (isspace(*devicePtr) && ((devicePtr + 1) <= bounds))
2638     devicePtr++;
2639    
2640     *bootPtr = strdup(devicePtr);
2641     break;
2642     }
2643     }
2644    
2645     free(line);
2646     fclose(deviceMap);
2647     return 0;
2648     }
2649    
2650     int suseGrubConfGetBoot(const char * path, char ** bootPtr) {
2651     char * grubDevice;
2652    
2653     if (suseGrubConfGetInstallDevice(path, &grubDevice))
2654     dbgPrintf("error looking for grub installation device\n");
2655     else
2656     dbgPrintf("grubby installation device: %s\n", grubDevice);
2657    
2658     if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
2659     dbgPrintf("error looking for grub boot device\n");
2660     else
2661     dbgPrintf("grubby boot device: %s\n", *bootPtr);
2662    
2663     free(grubDevice);
2664     return 0;
2665     }
2666    
2667     int parseSuseGrubConf(int * lbaPtr, char ** bootPtr) {
2668     /*
2669     * This SuSE grub configuration file at this location is not your average
2670     * grub configuration file, but instead the grub commands used to setup
2671     * grub on that system.
2672     */
2673     const char * path;
2674     const static char default_path[] = "/etc/grub.conf";
2675    
2676     if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
2677     path = default_path;
2678    
2679     if (!isSuseGrubConf(path)) return 1;
2680    
2681     if (lbaPtr) {
2682     *lbaPtr = 0;
2683     if (suseGrubConfGetLba(path, lbaPtr))
2684     return 1;
2685     }
2686    
2687     if (bootPtr) {
2688     *bootPtr = NULL;
2689     suseGrubConfGetBoot(path, bootPtr);
2690     }
2691    
2692     return 0;
2693     }
2694    
2695 niro 532 int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
2696     FILE * in;
2697     char buf[1024];
2698     char * chptr;
2699     char * start;
2700     char * param;
2701    
2702 niro 926 in = fopen("/etc/conf.d/grub", "r");
2703 niro 532 if (!in) return 1;
2704    
2705     if (lbaPtr) *lbaPtr = 0;
2706     if (bootPtr) *bootPtr = NULL;
2707    
2708     while (fgets(buf, sizeof(buf), in)) {
2709     start = buf;
2710     while (isspace(*start)) start++;
2711     if (*start == '#') continue;
2712    
2713     chptr = strchr(start, '=');
2714     if (!chptr) continue;
2715     chptr--;
2716     while (*chptr && isspace(*chptr)) chptr--;
2717     chptr++;
2718     *chptr = '\0';
2719    
2720     param = chptr + 1;
2721     while (*param && isspace(*param)) param++;
2722     if (*param == '=') {
2723     param++;
2724     while (*param && isspace(*param)) param++;
2725     }
2726    
2727     chptr = param;
2728     while (*chptr && !isspace(*chptr)) chptr++;
2729     *chptr = '\0';
2730    
2731     if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
2732     *lbaPtr = 1;
2733     else if (!strcmp(start, "boot") && bootPtr)
2734     *bootPtr = strdup(param);
2735     }
2736    
2737     fclose(in);
2738    
2739     return 0;
2740     }
2741    
2742     void dumpSysconfigGrub(void) {
2743 niro 1850 char * boot = NULL;
2744 niro 532 int lba;
2745    
2746 niro 1851 if (isSuseSystem()) {
2747     if (parseSuseGrubConf(&lba, &boot)) {
2748 niro 1850 free(boot);
2749     return;
2750     }
2751     } else {
2752 niro 1851 if (parseSysconfigGrub(&lba, &boot)) {
2753 niro 1850 free(boot);
2754     return;
2755     }
2756 niro 532 }
2757 niro 1850
2758     if (lba) printf("lba\n");
2759     if (boot) {
2760     printf("boot=%s\n", boot);
2761     free(boot);
2762     }
2763 niro 532 }
2764    
2765     int displayInfo(struct grubConfig * config, char * kernel,
2766     const char * prefix) {
2767     int i = 0;
2768     struct singleEntry * entry;
2769     struct singleLine * line;
2770    
2771     entry = findEntryByPath(config, kernel, prefix, &i);
2772     if (!entry) {
2773     fprintf(stderr, _("grubby: kernel not found\n"));
2774     return 1;
2775     }
2776    
2777 niro 926 /* this is a horrible hack to support /etc/conf.d/grub; there must
2778 niro 532 be a better way */
2779     if (config->cfi == &grubConfigType) {
2780     dumpSysconfigGrub();
2781     } else {
2782 niro 914 line = getLineByType(LT_BOOT, config->theLines);
2783 niro 532 if (line && line->numElements >= 1) {
2784     printf("boot=%s\n", line->elements[1].item);
2785     }
2786    
2787 niro 914 line = getLineByType(LT_LBA, config->theLines);
2788 niro 532 if (line) printf("lba\n");
2789     }
2790    
2791     displayEntry(entry, prefix, i);
2792    
2793     i++;
2794     while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
2795     displayEntry(entry, prefix, i);
2796     i++;
2797     }
2798    
2799     return 0;
2800     }
2801    
2802 niro 914 struct singleLine * addLineTmpl(struct singleEntry * entry,
2803     struct singleLine * tmplLine,
2804     struct singleLine * prevLine,
2805     const char * val,
2806     struct configFileInfo * cfi)
2807     {
2808     struct singleLine * newLine = lineDup(tmplLine);
2809    
2810 niro 1940 if (isEfi && cfi == &grub2ConfigType) {
2811     enum lineType_e old = newLine->type;
2812     newLine->type = preferredLineType(newLine->type, cfi);
2813     if (old != newLine->type)
2814     newLine->elements[0].item = getKeyByType(newLine->type, cfi);
2815     }
2816    
2817 niro 914 if (val) {
2818     /* override the inherited value with our own.
2819     * This is a little weak because it only applies to elements[1]
2820     */
2821     if (newLine->numElements > 1)
2822     removeElement(newLine, 1);
2823     insertElement(newLine, val, 1, cfi);
2824    
2825     /* but try to keep the rootspec from the template... sigh */
2826 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)) {
2827 niro 914 char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
2828     if (rootspec != NULL) {
2829     free(newLine->elements[1].item);
2830     newLine->elements[1].item =
2831     sdupprintf("%s%s", rootspec, val);
2832     }
2833     }
2834     }
2835    
2836     dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
2837     newLine->elements[0].item : "");
2838    
2839     if (!entry->lines) {
2840     /* first one on the list */
2841     entry->lines = newLine;
2842     } else if (prevLine) {
2843     /* add after prevLine */
2844     newLine->next = prevLine->next;
2845     prevLine->next = newLine;
2846     }
2847    
2848     return newLine;
2849     }
2850    
2851 niro 532 /* val may be NULL */
2852     struct singleLine * addLine(struct singleEntry * entry,
2853     struct configFileInfo * cfi,
2854 niro 914 enum lineType_e type, char * defaultIndent,
2855     const char * val) {
2856 niro 532 struct singleLine * line, * prev;
2857 niro 914 struct keywordTypes * kw;
2858     struct singleLine tmpl;
2859 niro 532
2860 niro 914 /* NB: This function shouldn't allocate items on the heap, rather on the
2861     * stack since it calls addLineTmpl which will make copies.
2862     */
2863     if (type == LT_TITLE && cfi->titleBracketed) {
2864     /* we're doing a bracketed title (zipl) */
2865     tmpl.type = type;
2866     tmpl.numElements = 1;
2867     tmpl.elements = alloca(sizeof(*tmpl.elements));
2868     tmpl.elements[0].item = alloca(strlen(val)+3);
2869     sprintf(tmpl.elements[0].item, "[%s]", val);
2870     tmpl.elements[0].indent = "";
2871     val = NULL;
2872 niro 1696 } else if (type == LT_MENUENTRY) {
2873     char *lineend = "--class gnu-linux --class gnu --class os {";
2874     if (!val) {
2875     fprintf(stderr, "Line type LT_MENUENTRY requires a value\n");
2876     abort();
2877     }
2878     kw = getKeywordByType(type, cfi);
2879     if (!kw) {
2880     fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2881     abort();
2882     }
2883     tmpl.indent = "";
2884     tmpl.type = type;
2885     tmpl.numElements = 3;
2886     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2887     tmpl.elements[0].item = kw->key;
2888     tmpl.elements[0].indent = alloca(2);
2889     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2890     tmpl.elements[1].item = (char *)val;
2891     tmpl.elements[1].indent = alloca(2);
2892     sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
2893     tmpl.elements[2].item = alloca(strlen(lineend)+1);
2894     strcpy(tmpl.elements[2].item, lineend);
2895     tmpl.elements[2].indent = "";
2896 niro 914 } else {
2897     kw = getKeywordByType(type, cfi);
2898 niro 1696 if (!kw) {
2899     fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2900     abort();
2901     }
2902 niro 914 tmpl.type = type;
2903     tmpl.numElements = val ? 2 : 1;
2904     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2905     tmpl.elements[0].item = kw->key;
2906     tmpl.elements[0].indent = alloca(2);
2907     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2908     if (val) {
2909     tmpl.elements[1].item = (char *)val;
2910     tmpl.elements[1].indent = "";
2911     }
2912     }
2913    
2914 niro 532 /* The last non-empty line gives us the indention to us and the line
2915     to insert after. Note that comments are considered empty lines, which
2916     may not be ideal? If there are no lines or we are looking at the
2917     first line, we use defaultIndent (the first line is normally indented
2918     differently from the rest) */
2919 niro 914 for (line = entry->lines, prev = NULL; line; line = line->next) {
2920     if (line->numElements) prev = line;
2921     /* fall back on the last line if prev isn't otherwise set */
2922     if (!line->next && !prev) prev = line;
2923 niro 532 }
2924    
2925 niro 1696 struct singleLine *menuEntry;
2926     menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
2927     if (tmpl.type == LT_ENTRY_END) {
2928     if (menuEntry)
2929     tmpl.indent = menuEntry->indent;
2930     else
2931     tmpl.indent = defaultIndent ?: "";
2932     } else if (tmpl.type != LT_MENUENTRY) {
2933     if (menuEntry)
2934     tmpl.indent = "\t";
2935     else if (prev == entry->lines)
2936     tmpl.indent = defaultIndent ?: "";
2937     else
2938     tmpl.indent = prev->indent;
2939     }
2940 niro 532
2941 niro 914 return addLineTmpl(entry, &tmpl, prev, val, cfi);
2942 niro 532 }
2943    
2944     void removeLine(struct singleEntry * entry, struct singleLine * line) {
2945     struct singleLine * prev;
2946     int i;
2947    
2948     for (i = 0; i < line->numElements; i++) {
2949     free(line->elements[i].item);
2950     free(line->elements[i].indent);
2951     }
2952     free(line->elements);
2953     free(line->indent);
2954    
2955     if (line == entry->lines) {
2956     entry->lines = line->next;
2957     } else {
2958     prev = entry->lines;
2959     while (prev->next != line) prev = prev->next;
2960     prev->next = line->next;
2961     }
2962    
2963     free(line);
2964     }
2965    
2966 niro 1696 static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi)
2967     {
2968     struct singleLine newLine = {
2969     .indent = tmplLine->indent,
2970     .type = tmplLine->type,
2971     .next = tmplLine->next,
2972     };
2973     int firstQuotedItem = -1;
2974     int quoteLen = 0;
2975     int j;
2976     int element = 0;
2977     char *c;
2978    
2979     c = malloc(strlen(tmplLine->elements[0].item) + 1);
2980     strcpy(c, tmplLine->elements[0].item);
2981     insertElement(&newLine, c, element++, cfi);
2982     free(c);
2983     c = NULL;
2984    
2985     for (j = 1; j < tmplLine->numElements; j++) {
2986     if (firstQuotedItem == -1) {
2987     quoteLen += strlen(tmplLine->elements[j].item);
2988    
2989     if (isquote(tmplLine->elements[j].item[0])) {
2990     firstQuotedItem = j;
2991     quoteLen += strlen(tmplLine->elements[j].indent);
2992     } else {
2993     c = malloc(quoteLen + 1);
2994     strcpy(c, tmplLine->elements[j].item);
2995     insertElement(&newLine, c, element++, cfi);
2996     free(c);
2997     quoteLen = 0;
2998     }
2999     } else {
3000     int itemlen = strlen(tmplLine->elements[j].item);
3001     quoteLen += itemlen;
3002     quoteLen += strlen(tmplLine->elements[j].indent);
3003    
3004     if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
3005     c = malloc(quoteLen + 1);
3006     c[0] = '\0';
3007     for (int i = firstQuotedItem; i < j+1; i++) {
3008     strcat(c, tmplLine->elements[i].item);
3009     strcat(c, tmplLine->elements[i].indent);
3010     }
3011     insertElement(&newLine, c, element++, cfi);
3012     free(c);
3013    
3014     firstQuotedItem = -1;
3015     quoteLen = 0;
3016     }
3017     }
3018     }
3019     while (tmplLine->numElements)
3020     removeElement(tmplLine, 0);
3021     if (tmplLine->elements)
3022     free(tmplLine->elements);
3023    
3024     tmplLine->numElements = newLine.numElements;
3025     tmplLine->elements = newLine.elements;
3026     }
3027    
3028 niro 914 static void insertElement(struct singleLine * line,
3029     const char * item, int insertHere,
3030     struct configFileInfo * cfi)
3031     {
3032     struct keywordTypes * kw;
3033     char indent[2] = "";
3034    
3035     /* sanity check */
3036     if (insertHere > line->numElements) {
3037     dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",
3038     insertHere, line->numElements);
3039     insertHere = line->numElements;
3040     }
3041    
3042     line->elements = realloc(line->elements, (line->numElements + 1) *
3043     sizeof(*line->elements));
3044     memmove(&line->elements[insertHere+1],
3045     &line->elements[insertHere],
3046     (line->numElements - insertHere) *
3047     sizeof(*line->elements));
3048     line->elements[insertHere].item = strdup(item);
3049    
3050     kw = getKeywordByType(line->type, cfi);
3051    
3052     if (line->numElements == 0) {
3053     indent[0] = '\0';
3054     } else if (insertHere == 0) {
3055     indent[0] = kw->nextChar;
3056     } else if (kw->separatorChar != '\0') {
3057     indent[0] = kw->separatorChar;
3058     } else {
3059     indent[0] = ' ';
3060     }
3061    
3062     if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {
3063     /* move the end-of-line forward */
3064     line->elements[insertHere].indent =
3065     line->elements[insertHere-1].indent;
3066     line->elements[insertHere-1].indent = strdup(indent);
3067     } else {
3068     line->elements[insertHere].indent = strdup(indent);
3069     }
3070    
3071     line->numElements++;
3072    
3073     dbgPrintf("insertElement(%s, '%s%s', %d)\n",
3074     line->elements[0].item,
3075     line->elements[insertHere].item,
3076     line->elements[insertHere].indent,
3077     insertHere);
3078     }
3079    
3080     static void removeElement(struct singleLine * line, int removeHere) {
3081     int i;
3082    
3083     /* sanity check */
3084     if (removeHere >= line->numElements) return;
3085    
3086     dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
3087     removeHere, line->elements[removeHere].item);
3088    
3089     free(line->elements[removeHere].item);
3090    
3091     if (removeHere > 1) {
3092     /* previous argument gets this argument's post-indentation */
3093     free(line->elements[removeHere-1].indent);
3094     line->elements[removeHere-1].indent =
3095     line->elements[removeHere].indent;
3096     } else {
3097     free(line->elements[removeHere].indent);
3098     }
3099    
3100     /* now collapse the array, but don't bother to realloc smaller */
3101     for (i = removeHere; i < line->numElements - 1; i++)
3102     line->elements[i] = line->elements[i + 1];
3103    
3104     line->numElements--;
3105     }
3106    
3107 niro 532 int argMatch(const char * one, const char * two) {
3108     char * first, * second;
3109     char * chptr;
3110    
3111     first = strcpy(alloca(strlen(one) + 1), one);
3112     second = strcpy(alloca(strlen(two) + 1), two);
3113    
3114     chptr = strchr(first, '=');
3115     if (chptr) *chptr = '\0';
3116    
3117     chptr = strchr(second, '=');
3118     if (chptr) *chptr = '\0';
3119    
3120     return strcmp(first, second);
3121     }
3122    
3123     int updateActualImage(struct grubConfig * cfg, const char * image,
3124     const char * prefix, const char * addArgs,
3125     const char * removeArgs, int multibootArgs) {
3126     struct singleEntry * entry;
3127     struct singleLine * line, * rootLine;
3128     int index = 0;
3129 niro 914 int i, k;
3130 niro 532 const char ** newArgs, ** oldArgs;
3131     const char ** arg;
3132 niro 914 int useKernelArgs, useRoot;
3133 niro 532 int firstElement;
3134 niro 1304 int *usedElements;
3135 niro 914 int doreplace;
3136 niro 532
3137     if (!image) return 0;
3138    
3139     if (!addArgs) {
3140     newArgs = malloc(sizeof(*newArgs));
3141     *newArgs = NULL;
3142     } else {
3143     if (poptParseArgvString(addArgs, NULL, &newArgs)) {
3144     fprintf(stderr,
3145     _("grubby: error separating arguments '%s'\n"), addArgs);
3146     return 1;
3147     }
3148     }
3149    
3150     if (!removeArgs) {
3151     oldArgs = malloc(sizeof(*oldArgs));
3152     *oldArgs = NULL;
3153     } else {
3154     if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
3155     fprintf(stderr,
3156     _("grubby: error separating arguments '%s'\n"), removeArgs);
3157     free(newArgs);
3158     return 1;
3159     }
3160     }
3161    
3162    
3163 niro 914 useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
3164     && (!multibootArgs || cfg->cfi->mbConcatArgs));
3165 niro 532
3166 niro 914 useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
3167     && !multibootArgs);
3168 niro 532
3169 niro 914 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3170 niro 532
3171 niro 914 if (multibootArgs && !entry->multiboot)
3172     continue;
3173 niro 532
3174 niro 914 /* Determine where to put the args. If this config supports
3175     * LT_KERNELARGS, use that. Otherwise use
3176     * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
3177     */
3178     if (useKernelArgs) {
3179     line = getLineByType(LT_KERNELARGS, entry->lines);
3180     if (!line) {
3181     /* no LT_KERNELARGS, need to add it */
3182     line = addLine(entry, cfg->cfi, LT_KERNELARGS,
3183     cfg->secondaryIndent, NULL);
3184     }
3185     firstElement = 1;
3186 niro 532
3187 niro 914 } else if (multibootArgs) {
3188     line = getLineByType(LT_HYPER, entry->lines);
3189     if (!line) {
3190     /* a multiboot entry without LT_HYPER? */
3191     continue;
3192     }
3193     firstElement = 2;
3194    
3195     } else {
3196 niro 2683 line = getLineByType(LT_KERNEL|LT_MBMODULE|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3197 niro 914 if (!line) {
3198     /* no LT_KERNEL or LT_MBMODULE in this entry? */
3199     continue;
3200     }
3201     firstElement = 2;
3202 niro 532 }
3203    
3204 niro 914 /* handle the elilo case which does:
3205     * append="hypervisor args -- kernel args"
3206     */
3207     if (entry->multiboot && cfg->cfi->mbConcatArgs) {
3208     /* this is a multiboot entry, make sure there's
3209     * -- on the args line
3210     */
3211     for (i = firstElement; i < line->numElements; i++) {
3212     if (!strcmp(line->elements[i].item, "--"))
3213     break;
3214     }
3215     if (i == line->numElements) {
3216     /* assume all existing args are kernel args,
3217     * prepend -- to make it official
3218     */
3219     insertElement(line, "--", firstElement, cfg->cfi);
3220     i = firstElement;
3221     }
3222     if (!multibootArgs) {
3223     /* kernel args start after the -- */
3224     firstElement = i + 1;
3225     }
3226     } else if (cfg->cfi->mbConcatArgs) {
3227     /* this is a non-multiboot entry, remove hyper args */
3228     for (i = firstElement; i < line->numElements; i++) {
3229     if (!strcmp(line->elements[i].item, "--"))
3230     break;
3231     }
3232     if (i < line->numElements) {
3233     /* remove args up to -- */
3234     while (strcmp(line->elements[firstElement].item, "--"))
3235     removeElement(line, firstElement);
3236     /* remove -- */
3237     removeElement(line, firstElement);
3238     }
3239 niro 532 }
3240    
3241 niro 914 usedElements = calloc(line->numElements, sizeof(*usedElements));
3242 niro 532
3243 niro 914 for (k = 0, arg = newArgs; *arg; arg++, k++) {
3244    
3245     doreplace = 1;
3246 niro 532 for (i = firstElement; i < line->numElements; i++) {
3247 niro 914 if (multibootArgs && cfg->cfi->mbConcatArgs &&
3248     !strcmp(line->elements[i].item, "--"))
3249     {
3250     /* reached the end of hyper args, insert here */
3251     doreplace = 0;
3252     break;
3253     }
3254 niro 532 if (usedElements[i])
3255     continue;
3256     if (!argMatch(line->elements[i].item, *arg)) {
3257     usedElements[i]=1;
3258     break;
3259     }
3260     }
3261    
3262 niro 914 if (i < line->numElements && doreplace) {
3263     /* direct replacement */
3264 niro 532 free(line->elements[i].item);
3265     line->elements[i].item = strdup(*arg);
3266    
3267 niro 914 } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
3268     /* root= replacement */
3269     rootLine = getLineByType(LT_ROOT, entry->lines);
3270     if (rootLine) {
3271     free(rootLine->elements[1].item);
3272     rootLine->elements[1].item = strdup(*arg + 5);
3273 niro 532 } else {
3274 niro 914 rootLine = addLine(entry, cfg->cfi, LT_ROOT,
3275     cfg->secondaryIndent, *arg + 5);
3276 niro 532 }
3277 niro 914 }
3278 niro 532
3279 niro 914 else {
3280     /* insert/append */
3281     insertElement(line, *arg, i, cfg->cfi);
3282     usedElements = realloc(usedElements, line->numElements *
3283     sizeof(*usedElements));
3284     memmove(&usedElements[i + 1], &usedElements[i],
3285     line->numElements - i - 1);
3286     usedElements[i] = 1;
3287 niro 532
3288     /* if we updated a root= here even though there is a
3289     LT_ROOT available we need to remove the LT_ROOT entry
3290     (this will happen if we switch from a device to a label) */
3291     if (useRoot && !strncmp(*arg, "root=", 5)) {
3292 niro 914 rootLine = getLineByType(LT_ROOT, entry->lines);
3293     if (rootLine)
3294 niro 532 removeLine(entry, rootLine);
3295     }
3296     }
3297     }
3298    
3299     free(usedElements);
3300    
3301     for (arg = oldArgs; *arg; arg++) {
3302 niro 914 for (i = firstElement; i < line->numElements; i++) {
3303     if (multibootArgs && cfg->cfi->mbConcatArgs &&
3304     !strcmp(line->elements[i].item, "--"))
3305     /* reached the end of hyper args, stop here */
3306 niro 532 break;
3307 niro 914 if (!argMatch(line->elements[i].item, *arg)) {
3308     removeElement(line, i);
3309     break;
3310 niro 532 }
3311     }
3312 niro 914 /* handle removing LT_ROOT line too */
3313     if (useRoot && !strncmp(*arg, "root=", 5)) {
3314     rootLine = getLineByType(LT_ROOT, entry->lines);
3315     if (rootLine)
3316     removeLine(entry, rootLine);
3317     }
3318 niro 532 }
3319    
3320     if (line->numElements == 1) {
3321     /* don't need the line at all (note it has to be a
3322     LT_KERNELARGS for this to happen */
3323     removeLine(entry, line);
3324     }
3325     }
3326    
3327     free(newArgs);
3328     free(oldArgs);
3329    
3330     return 0;
3331     }
3332    
3333     int updateImage(struct grubConfig * cfg, const char * image,
3334     const char * prefix, const char * addArgs,
3335     const char * removeArgs,
3336     const char * addMBArgs, const char * removeMBArgs) {
3337     int rc = 0;
3338    
3339     if (!image) return rc;
3340    
3341     /* update the main args first... */
3342     if (addArgs || removeArgs)
3343     rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs, 0);
3344     if (rc) return rc;
3345    
3346     /* and now any multiboot args */
3347     if (addMBArgs || removeMBArgs)
3348     rc = updateActualImage(cfg, image, prefix, addMBArgs, removeMBArgs, 1);
3349     return rc;
3350     }
3351    
3352 niro 2263 int addMBInitrd(struct grubConfig * cfg, const char *newMBKernel,
3353 niro 2960 const char * image, const char * prefix, const char * initrd,
3354     const char * title) {
3355 niro 2263 struct singleEntry * entry;
3356     struct singleLine * line, * kernelLine, *endLine = NULL;
3357     int index = 0;
3358    
3359     if (!image) return 0;
3360    
3361 niro 2957 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3362 niro 2263 kernelLine = getLineByType(LT_MBMODULE, entry->lines);
3363     if (!kernelLine) continue;
3364    
3365 niro 2960 /* if title is supplied, the entry's title must match it. */
3366     if (title) {
3367 niro 2962 char *linetitle;
3368    
3369 niro 2960 line = getLineByType(LT_TITLE|LT_MENUENTRY, entry->lines);
3370 niro 2962 if (!line)
3371     continue;
3372 niro 2960
3373 niro 2980 linetitle = extractTitle(cfg, line);
3374 niro 2960 if (!linetitle)
3375     continue;
3376     if (strcmp(title, linetitle)) {
3377     free(linetitle);
3378     continue;
3379     }
3380     free(linetitle);
3381     }
3382    
3383 niro 2263 if (prefix) {
3384     int prefixLen = strlen(prefix);
3385     if (!strncmp(initrd, prefix, prefixLen))
3386     initrd += prefixLen;
3387     }
3388     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3389     if (endLine)
3390     removeLine(entry, endLine);
3391     line = addLine(entry, cfg->cfi, preferredLineType(LT_MBMODULE,cfg->cfi),
3392     kernelLine->indent, initrd);
3393     if (!line)
3394     return 1;
3395     if (endLine) {
3396     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3397     if (!line)
3398     return 1;
3399     }
3400    
3401     break;
3402     }
3403    
3404     return 0;
3405     }
3406    
3407 niro 1156 int updateInitrd(struct grubConfig * cfg, const char * image,
3408 niro 2960 const char * prefix, const char * initrd, const char * title) {
3409 niro 1156 struct singleEntry * entry;
3410 niro 1696 struct singleLine * line, * kernelLine, *endLine = NULL;
3411 niro 1156 int index = 0;
3412    
3413     if (!image) return 0;
3414    
3415     for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3416 niro 2683 kernelLine = getLineByType(LT_KERNEL|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3417 niro 1156 if (!kernelLine) continue;
3418    
3419 niro 2960 /* if title is supplied, the entry's title must match it. */
3420     if (title) {
3421 niro 2962 char *linetitle;
3422    
3423 niro 2960 line = getLineByType(LT_TITLE|LT_MENUENTRY, entry->lines);
3424 niro 2962 if (!line)
3425     continue;
3426 niro 2960
3427 niro 2980 linetitle = extractTitle(cfg, line);
3428 niro 2960 if (!linetitle)
3429     continue;
3430     if (strcmp(title, linetitle)) {
3431     free(linetitle);
3432     continue;
3433     }
3434     free(linetitle);
3435     }
3436    
3437 niro 2683 line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
3438 niro 1156 if (line)
3439     removeLine(entry, line);
3440     if (prefix) {
3441     int prefixLen = strlen(prefix);
3442     if (!strncmp(initrd, prefix, prefixLen))
3443     initrd += prefixLen;
3444     }
3445 niro 1696 endLine = getLineByType(LT_ENTRY_END, entry->lines);
3446     if (endLine)
3447     removeLine(entry, endLine);
3448 niro 2683 enum lineType_e lt;
3449     switch(kernelLine->type) {
3450     case LT_KERNEL:
3451     lt = LT_INITRD;
3452     break;
3453     case LT_KERNEL_EFI:
3454     lt = LT_INITRD_EFI;
3455     break;
3456     case LT_KERNEL_16:
3457     lt = LT_INITRD_16;
3458     break;
3459     default:
3460     lt = preferredLineType(LT_INITRD, cfg->cfi);
3461     }
3462     line = addLine(entry, cfg->cfi, lt, kernelLine->indent, initrd);
3463 niro 1696 if (!line)
3464     return 1;
3465     if (endLine) {
3466     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3467     if (!line)
3468     return 1;
3469     }
3470    
3471 niro 1156 break;
3472     }
3473    
3474     return 0;
3475     }
3476    
3477 niro 532 int checkDeviceBootloader(const char * device, const unsigned char * boot) {
3478     int fd;
3479     unsigned char bootSect[512];
3480     int offset;
3481    
3482     fd = open(device, O_RDONLY);
3483     if (fd < 0) {
3484     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3485     device, strerror(errno));
3486     return 1;
3487     }
3488    
3489     if (read(fd, bootSect, 512) != 512) {
3490     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3491     device, strerror(errno));
3492     return 1;
3493     }
3494     close(fd);
3495    
3496     /* first three bytes should match, a jmp short should be in there */
3497     if (memcmp(boot, bootSect, 3))
3498     return 0;
3499    
3500 niro 1718 if (boot[1] == JMP_SHORT_OPCODE) {
3501 niro 532 offset = boot[2] + 2;
3502     } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3503     offset = (boot[3] << 8) + boot[2] + 2;
3504 niro 1718 } else if (boot[0] == JMP_SHORT_OPCODE) {
3505     offset = boot[1] + 2;
3506     /*
3507     * it looks like grub, when copying stage1 into the mbr, patches stage1
3508     * right after the JMP location, replacing other instructions such as
3509     * JMPs for NOOPs. So, relax the check a little bit by skipping those
3510     * different bytes.
3511     */
3512     if ((bootSect[offset + 1] == NOOP_OPCODE)
3513     && (bootSect[offset + 2] == NOOP_OPCODE)) {
3514     offset = offset + 3;
3515     }
3516 niro 532 } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3517     offset = (boot[2] << 8) + boot[1] + 2;
3518     } else {
3519     return 0;
3520     }
3521    
3522     if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
3523     return 0;
3524    
3525     return 2;
3526     }
3527    
3528     int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {
3529     int fd;
3530     char buf[65536];
3531     char * end;
3532     char * chptr;
3533     char * chptr2;
3534     int rc;
3535    
3536     /* it's on raid; we need to parse /proc/mdstat and check all of the
3537     *raw* devices listed in there */
3538    
3539     if (!strncmp(mdDev, "/dev/", 5))
3540     mdDev += 5;
3541    
3542     if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
3543     fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
3544     strerror(errno));
3545     return 2;
3546     }
3547    
3548     rc = read(fd, buf, sizeof(buf) - 1);
3549     if (rc < 0 || rc == (sizeof(buf) - 1)) {
3550     fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
3551     strerror(errno));
3552     close(fd);
3553     return 2;
3554     }
3555     close(fd);
3556     buf[rc] = '\0';
3557    
3558     chptr = buf;
3559     while (*chptr) {
3560     end = strchr(chptr, '\n');
3561     if (!end) break;
3562     *end = '\0';
3563    
3564     if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
3565     chptr[strlen(mdDev)] == ' ') {
3566    
3567     /* found the device */
3568     while (*chptr && *chptr != ':') chptr++;
3569     chptr++;
3570     while (*chptr && isspace(*chptr)) chptr++;
3571    
3572     /* skip the "active" bit */
3573     while (*chptr && !isspace(*chptr)) chptr++;
3574     while (*chptr && isspace(*chptr)) chptr++;
3575    
3576     /* skip the raid level */
3577     while (*chptr && !isspace(*chptr)) chptr++;
3578     while (*chptr && isspace(*chptr)) chptr++;
3579    
3580     /* everything else is partition stuff */
3581     while (*chptr) {
3582     chptr2 = chptr;
3583     while (*chptr2 && *chptr2 != '[') chptr2++;
3584     if (!*chptr2) break;
3585    
3586     /* yank off the numbers at the end */
3587     chptr2--;
3588     while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;
3589     chptr2++;
3590     *chptr2 = '\0';
3591    
3592     /* Better, now we need the /dev/ back. We're done with
3593     * everything before this point, so we can just put
3594     * the /dev/ part there. There will always be room. */
3595     memcpy(chptr - 5, "/dev/", 5);
3596     rc = checkDeviceBootloader(chptr - 5, boot);
3597     if (rc != 2) {
3598     return rc;
3599     }
3600    
3601     chptr = chptr2 + 1;
3602     /* skip the [11] bit */
3603     while (*chptr && !isspace(*chptr)) chptr++;
3604     /* and move to the next one */
3605     while (*chptr && isspace(*chptr)) chptr++;
3606     }
3607    
3608     /* we're good to go */
3609     return 2;
3610     }
3611    
3612     chptr = end + 1;
3613     }
3614    
3615     fprintf(stderr,
3616     _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
3617     mdDev);
3618     return 0;
3619     }
3620    
3621     int checkForLilo(struct grubConfig * config) {
3622     int fd;
3623     unsigned char boot[512];
3624     struct singleLine * line;
3625    
3626     for (line = config->theLines; line; line = line->next)
3627     if (line->type == LT_BOOT) break;
3628    
3629     if (!line) {
3630     fprintf(stderr,
3631     _("grubby: no boot line found in lilo configuration\n"));
3632     return 1;
3633     }
3634    
3635     if (line->numElements != 2) return 1;
3636    
3637     fd = open("/boot/boot.b", O_RDONLY);
3638     if (fd < 0) {
3639     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3640     "/boot/boot.b", strerror(errno));
3641     return 1;
3642     }
3643    
3644     if (read(fd, boot, 512) != 512) {
3645     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3646     "/boot/boot.b", strerror(errno));
3647     return 1;
3648     }
3649     close(fd);
3650    
3651     if (!strncmp("/dev/md", line->elements[1].item, 7))
3652     return checkLiloOnRaid(line->elements[1].item, boot);
3653    
3654     return checkDeviceBootloader(line->elements[1].item, boot);
3655     }
3656    
3657 niro 1696 int checkForGrub2(struct grubConfig * config) {
3658 niro 1714 if (!access("/etc/grub.d/", R_OK))
3659 niro 1696 return 2;
3660    
3661     return 1;
3662     }
3663    
3664 niro 532 int checkForGrub(struct grubConfig * config) {
3665     int fd;
3666     unsigned char bootSect[512];
3667     char * boot;
3668 niro 1851 int onSuse = isSuseSystem();
3669 niro 532
3670 niro 1851
3671     if (onSuse) {
3672     if (parseSuseGrubConf(NULL, &boot))
3673     return 0;
3674 niro 1850 } else {
3675 niro 1851 if (parseSysconfigGrub(NULL, &boot))
3676     return 0;
3677 niro 1850 }
3678 niro 532
3679     /* assume grub is not installed -- not an error condition */
3680     if (!boot)
3681     return 0;
3682    
3683     fd = open("/boot/grub/stage1", O_RDONLY);
3684     if (fd < 0)
3685     /* this doesn't exist if grub hasn't been installed */
3686     return 0;
3687    
3688     if (read(fd, bootSect, 512) != 512) {
3689     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3690     "/boot/grub/stage1", strerror(errno));
3691 niro 914 close(fd);
3692 niro 532 return 1;
3693     }
3694     close(fd);
3695    
3696 niro 1851 /* The more elaborate checks do not work on SuSE. The checks done
3697     * seem to be reasonble (at least for now), so just return success
3698     */
3699     if (onSuse)
3700 niro 1850 return 2;
3701 niro 1851
3702     return checkDeviceBootloader(boot, bootSect);
3703 niro 532 }
3704    
3705 niro 914 int checkForExtLinux(struct grubConfig * config) {
3706     int fd;
3707     unsigned char bootSect[512];
3708     char * boot;
3709     char executable[] = "/boot/extlinux/extlinux";
3710    
3711     printf("entered: checkForExtLinux()\n");
3712    
3713     if (parseSysconfigGrub(NULL, &boot))
3714     return 0;
3715    
3716     /* assume grub is not installed -- not an error condition */
3717     if (!boot)
3718     return 0;
3719    
3720     fd = open(executable, O_RDONLY);
3721     if (fd < 0)
3722     /* this doesn't exist if grub hasn't been installed */
3723     return 0;
3724    
3725     if (read(fd, bootSect, 512) != 512) {
3726     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3727     executable, strerror(errno));
3728     return 1;
3729     }
3730     close(fd);
3731    
3732     return checkDeviceBootloader(boot, bootSect);
3733     }
3734    
3735 niro 1853 int checkForYaboot(struct grubConfig * config) {
3736     /*
3737     * This is a simplistic check that we consider good enough for own puporses
3738     *
3739     * If we were to properly check if yaboot is *installed* we'd need to:
3740     * 1) get the system boot device (LT_BOOT)
3741     * 2) considering it's a raw filesystem, check if the yaboot binary matches
3742     * the content on the boot device
3743     * 3) if not, copy the binary to a temporary file and run "addnote" on it
3744     * 4) check again if binary and boot device contents match
3745     */
3746     if (!access("/etc/yaboot.conf", R_OK))
3747     return 2;
3748    
3749     return 1;
3750     }
3751    
3752 niro 1854 int checkForElilo(struct grubConfig * config) {
3753     if (!access("/etc/elilo.conf", R_OK))
3754     return 2;
3755    
3756     return 1;
3757     }
3758    
3759 niro 532 static char * getRootSpecifier(char * str) {
3760     char * idx, * rootspec = NULL;
3761    
3762     if (*str == '(') {
3763     idx = rootspec = strdup(str);
3764     while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;
3765     *(++idx) = '\0';
3766     }
3767     return rootspec;
3768     }
3769    
3770 niro 914 static char * getInitrdVal(struct grubConfig * config,
3771     const char * prefix, struct singleLine *tmplLine,
3772     const char * newKernelInitrd,
3773 niro 1844 const char ** extraInitrds, int extraInitrdCount)
3774 niro 914 {
3775     char *initrdVal, *end;
3776     int i;
3777     size_t totalSize;
3778     size_t prefixLen;
3779     char separatorChar;
3780    
3781     prefixLen = strlen(prefix);
3782     totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
3783    
3784     for (i = 0; i < extraInitrdCount; i++) {
3785     totalSize += sizeof(separatorChar);
3786     totalSize += strlen(extraInitrds[i]) - prefixLen;
3787     }
3788    
3789     initrdVal = end = malloc(totalSize);
3790    
3791     end = stpcpy (end, newKernelInitrd + prefixLen);
3792    
3793     separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
3794     for (i = 0; i < extraInitrdCount; i++) {
3795     const char *extraInitrd;
3796     int j;
3797    
3798     extraInitrd = extraInitrds[i] + prefixLen;
3799     /* Don't add entries that are already there */
3800     if (tmplLine != NULL) {
3801     for (j = 2; j < tmplLine->numElements; j++)
3802     if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
3803     break;
3804    
3805     if (j != tmplLine->numElements)
3806     continue;
3807     }
3808    
3809     *end++ = separatorChar;
3810     end = stpcpy(end, extraInitrd);
3811     }
3812    
3813     return initrdVal;
3814     }
3815    
3816 niro 532 int addNewKernel(struct grubConfig * config, struct singleEntry * template,
3817     const char * prefix,
3818 niro 1844 const char * newKernelPath, const char * newKernelTitle,
3819     const char * newKernelArgs, const char * newKernelInitrd,
3820     const char ** extraInitrds, int extraInitrdCount,
3821 niro 2685 const char * newMBKernel, const char * newMBKernelArgs,
3822     const char * newDevTreePath) {
3823 niro 532 struct singleEntry * new;
3824 niro 914 struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
3825 niro 532 int needs;
3826     char * chptr;
3827    
3828     if (!newKernelPath) return 0;
3829    
3830     /* if the newKernelTitle is too long silently munge it into something
3831     * we can live with. truncating is first check, then we'll just mess with
3832     * it until it looks better */
3833     if (config->cfi->maxTitleLength &&
3834     (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
3835     char * buf = alloca(config->cfi->maxTitleLength + 7);
3836     char * numBuf = alloca(config->cfi->maxTitleLength + 1);
3837     int i = 1;
3838    
3839     sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);
3840     while (findEntryByPath(config, buf, NULL, NULL)) {
3841     sprintf(numBuf, "%d", i++);
3842     strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
3843     }
3844    
3845     newKernelTitle = buf + 6;
3846     }
3847    
3848     new = malloc(sizeof(*new));
3849     new->skip = 0;
3850     new->multiboot = 0;
3851     new->next = config->entries;
3852     new->lines = NULL;
3853     config->entries = new;
3854    
3855     /* copy/update from the template */
3856 niro 914 needs = NEED_KERNEL | NEED_TITLE;
3857     if (newKernelInitrd)
3858     needs |= NEED_INITRD;
3859 niro 532 if (newMBKernel) {
3860 niro 914 needs |= NEED_MB;
3861 niro 532 new->multiboot = 1;
3862     }
3863 niro 2685 if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))
3864     needs |= NEED_DEVTREE;
3865 niro 532
3866     if (template) {
3867 niro 914 for (masterLine = template->lines;
3868     masterLine && (tmplLine = lineDup(masterLine));
3869     lineFree(tmplLine), masterLine = masterLine->next)
3870     {
3871     dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
3872 niro 532
3873     /* skip comments */
3874     chptr = tmplLine->indent;
3875     while (*chptr && isspace(*chptr)) chptr++;
3876     if (*chptr == '#') continue;
3877    
3878 niro 1940 if (iskernel(tmplLine->type) && tmplLine->numElements >= 2) {
3879 niro 914 if (!template->multiboot && (needs & NEED_MB)) {
3880     /* it's not a multiboot template and this is the kernel
3881     * line. Try to be intelligent about inserting the
3882     * hypervisor at the same time.
3883     */
3884     if (config->cfi->mbHyperFirst) {
3885     /* insert the hypervisor first */
3886     newLine = addLine(new, config->cfi, LT_HYPER,
3887     tmplLine->indent,
3888     newMBKernel + strlen(prefix));
3889     /* set up for adding the kernel line */
3890     free(tmplLine->indent);
3891     tmplLine->indent = strdup(config->secondaryIndent);
3892     needs &= ~NEED_MB;
3893     }
3894     if (needs & NEED_KERNEL) {
3895     /* use addLineTmpl to preserve line elements,
3896     * otherwise we could just call addLine. Unfortunately
3897     * this means making some changes to the template
3898     * such as the indent change above and the type
3899     * change below.
3900     */
3901     struct keywordTypes * mbm_kw =
3902     getKeywordByType(LT_MBMODULE, config->cfi);
3903     if (mbm_kw) {
3904     tmplLine->type = LT_MBMODULE;
3905     free(tmplLine->elements[0].item);
3906     tmplLine->elements[0].item = strdup(mbm_kw->key);
3907     }
3908     newLine = addLineTmpl(new, tmplLine, newLine,
3909     newKernelPath + strlen(prefix), config->cfi);
3910     needs &= ~NEED_KERNEL;
3911     }
3912     if (needs & NEED_MB) { /* !mbHyperFirst */
3913     newLine = addLine(new, config->cfi, LT_HYPER,
3914     config->secondaryIndent,
3915     newMBKernel + strlen(prefix));
3916     needs &= ~NEED_MB;
3917     }
3918     } else if (needs & NEED_KERNEL) {
3919     newLine = addLineTmpl(new, tmplLine, newLine,
3920     newKernelPath + strlen(prefix), config->cfi);
3921     needs &= ~NEED_KERNEL;
3922     }
3923 niro 532
3924 niro 914 } else if (tmplLine->type == LT_HYPER &&
3925     tmplLine->numElements >= 2) {
3926     if (needs & NEED_MB) {
3927     newLine = addLineTmpl(new, tmplLine, newLine,
3928     newMBKernel + strlen(prefix), config->cfi);
3929     needs &= ~NEED_MB;
3930     }
3931 niro 532
3932 niro 914 } else if (tmplLine->type == LT_MBMODULE &&
3933     tmplLine->numElements >= 2) {
3934     if (new->multiboot) {
3935     if (needs & NEED_KERNEL) {
3936     newLine = addLineTmpl(new, tmplLine, newLine,
3937     newKernelPath +
3938     strlen(prefix), config->cfi);
3939     needs &= ~NEED_KERNEL;
3940     } else if (config->cfi->mbInitRdIsModule &&
3941     (needs & NEED_INITRD)) {
3942     char *initrdVal;
3943     initrdVal = getInitrdVal(config, prefix, tmplLine,
3944     newKernelInitrd, extraInitrds,
3945     extraInitrdCount);
3946     newLine = addLineTmpl(new, tmplLine, newLine,
3947     initrdVal, config->cfi);
3948     free(initrdVal);
3949     needs &= ~NEED_INITRD;
3950     }
3951     } else if (needs & NEED_KERNEL) {
3952     /* template is multi but new is not,
3953     * insert the kernel in the first module slot
3954     */
3955 niro 1940 tmplLine->type = preferredLineType(LT_KERNEL, config->cfi);
3956 niro 914 free(tmplLine->elements[0].item);
3957     tmplLine->elements[0].item =
3958 niro 1940 strdup(getKeywordByType(tmplLine->type,
3959     config->cfi)->key);
3960 niro 914 newLine = addLineTmpl(new, tmplLine, newLine,
3961 niro 1940 newKernelPath + strlen(prefix),
3962     config->cfi);
3963 niro 914 needs &= ~NEED_KERNEL;
3964     } else if (needs & NEED_INITRD) {
3965     char *initrdVal;
3966     /* template is multi but new is not,
3967     * insert the initrd in the second module slot
3968     */
3969 niro 1940 tmplLine->type = preferredLineType(LT_INITRD, config->cfi);
3970 niro 914 free(tmplLine->elements[0].item);
3971     tmplLine->elements[0].item =
3972 niro 1940 strdup(getKeywordByType(tmplLine->type,
3973     config->cfi)->key);
3974 niro 914 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3975     newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3976     free(initrdVal);
3977     needs &= ~NEED_INITRD;
3978     }
3979 niro 532
3980 niro 1940 } else if (isinitrd(tmplLine->type) && tmplLine->numElements >= 2) {
3981 niro 914 if (needs & NEED_INITRD &&
3982     new->multiboot && !template->multiboot &&
3983     config->cfi->mbInitRdIsModule) {
3984     /* make sure we don't insert the module initrd
3985     * before the module kernel... if we don't do it here,
3986     * it will be inserted following the template.
3987     */
3988     if (!needs & NEED_KERNEL) {
3989     char *initrdVal;
3990    
3991     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3992     newLine = addLine(new, config->cfi, LT_MBMODULE,
3993     config->secondaryIndent,
3994     initrdVal);
3995     free(initrdVal);
3996     needs &= ~NEED_INITRD;
3997     }
3998     } else if (needs & NEED_INITRD) {
3999     char *initrdVal;
4000     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
4001     newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
4002     free(initrdVal);
4003     needs &= ~NEED_INITRD;
4004 niro 532 }
4005    
4006 niro 1696 } else if (tmplLine->type == LT_MENUENTRY &&
4007     (needs & NEED_TITLE)) {
4008     requote(tmplLine, config->cfi);
4009     char *nkt = malloc(strlen(newKernelTitle)+3);
4010     strcpy(nkt, "'");
4011     strcat(nkt, newKernelTitle);
4012     strcat(nkt, "'");
4013     newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
4014     free(nkt);
4015     needs &= ~NEED_TITLE;
4016 niro 532 } else if (tmplLine->type == LT_TITLE &&
4017 niro 914 (needs & NEED_TITLE)) {
4018     if (tmplLine->numElements >= 2) {
4019     newLine = addLineTmpl(new, tmplLine, newLine,
4020     newKernelTitle, config->cfi);
4021     needs &= ~NEED_TITLE;
4022     } else if (tmplLine->numElements == 1 &&
4023     config->cfi->titleBracketed) {
4024     /* addLineTmpl doesn't handle titleBracketed */
4025     newLine = addLine(new, config->cfi, LT_TITLE,
4026     tmplLine->indent, newKernelTitle);
4027     needs &= ~NEED_TITLE;
4028     }
4029 niro 1696 } else if (tmplLine->type == LT_ECHO) {
4030     requote(tmplLine, config->cfi);
4031 niro 1722 static const char *prefix = "'Loading ";
4032 niro 1696 if (tmplLine->numElements > 1 &&
4033 niro 1722 strstr(tmplLine->elements[1].item, prefix) &&
4034 niro 1940 masterLine->next &&
4035     iskernel(masterLine->next->type)) {
4036 niro 1696 char *newTitle = malloc(strlen(prefix) +
4037     strlen(newKernelTitle) + 2);
4038 niro 532
4039 niro 1696 strcpy(newTitle, prefix);
4040     strcat(newTitle, newKernelTitle);
4041     strcat(newTitle, "'");
4042     newLine = addLine(new, config->cfi, LT_ECHO,
4043     tmplLine->indent, newTitle);
4044     free(newTitle);
4045     } else {
4046     /* pass through other lines from the template */
4047     newLine = addLineTmpl(new, tmplLine, newLine, NULL,
4048     config->cfi);
4049     }
4050 niro 2685 } else if (tmplLine->type == LT_DEVTREE &&
4051     tmplLine->numElements == 2 && newDevTreePath) {
4052     newLine = addLineTmpl(new, tmplLine, newLine,
4053     newDevTreePath + strlen(prefix),
4054     config->cfi);
4055     needs &= ~NEED_DEVTREE;
4056     } else if (tmplLine->type == LT_ENTRY_END && needs & NEED_DEVTREE) {
4057     const char *ndtp = newDevTreePath;
4058     if (!strncmp(newDevTreePath, prefix, strlen(prefix)))
4059     ndtp += strlen(prefix);
4060     newLine = addLine(new, config->cfi, LT_DEVTREE,
4061     config->secondaryIndent,
4062     ndtp);
4063     needs &= ~NEED_DEVTREE;
4064     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
4065 niro 914 } else {
4066     /* pass through other lines from the template */
4067     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
4068     }
4069 niro 532 }
4070 niro 914
4071 niro 532 } else {
4072 niro 914 /* don't have a template, so start the entry with the
4073     * appropriate starting line
4074     */
4075 niro 1693 switch (config->cfi->entryStart) {
4076 niro 914 case LT_KERNEL:
4077 niro 1940 case LT_KERNEL_EFI:
4078 niro 2683 case LT_KERNEL_16:
4079 niro 914 if (new->multiboot && config->cfi->mbHyperFirst) {
4080     /* fall through to LT_HYPER */
4081     } else {
4082 niro 1940 newLine = addLine(new, config->cfi,
4083     preferredLineType(LT_KERNEL, config->cfi),
4084 niro 914 config->primaryIndent,
4085     newKernelPath + strlen(prefix));
4086     needs &= ~NEED_KERNEL;
4087     break;
4088     }
4089    
4090     case LT_HYPER:
4091     newLine = addLine(new, config->cfi, LT_HYPER,
4092     config->primaryIndent,
4093     newMBKernel + strlen(prefix));
4094     needs &= ~NEED_MB;
4095 niro 532 break;
4096    
4097 niro 1696 case LT_MENUENTRY: {
4098     char *nkt = malloc(strlen(newKernelTitle)+3);
4099     strcpy(nkt, "'");
4100     strcat(nkt, newKernelTitle);
4101     strcat(nkt, "'");
4102     newLine = addLine(new, config->cfi, LT_MENUENTRY,
4103     config->primaryIndent, nkt);
4104     free(nkt);
4105     needs &= ~NEED_TITLE;
4106     needs |= NEED_END;
4107     break;
4108     }
4109 niro 914 case LT_TITLE:
4110     if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
4111     char * templabel;
4112     int x = 0, y = 0;
4113    
4114     templabel = strdup(newKernelTitle);
4115     while( templabel[x]){
4116     if( templabel[x] == ' ' ){
4117     y = x;
4118     while( templabel[y] ){
4119     templabel[y] = templabel[y+1];
4120     y++;
4121     }
4122     }
4123     x++;
4124     }
4125     newLine = addLine(new, config->cfi, LT_TITLE,
4126     config->primaryIndent, templabel);
4127     free(templabel);
4128     }else{
4129     newLine = addLine(new, config->cfi, LT_TITLE,
4130     config->primaryIndent, newKernelTitle);
4131     }
4132     needs &= ~NEED_TITLE;
4133     break;
4134    
4135     default:
4136     abort();
4137 niro 532 }
4138     }
4139    
4140 niro 2709 struct singleLine *endLine = NULL;
4141     endLine = getLineByType(LT_ENTRY_END, new->lines);
4142     if (endLine) {
4143     removeLine(new, endLine);
4144     needs |= NEED_END;
4145     }
4146    
4147 niro 914 /* add the remainder of the lines, i.e. those that either
4148     * weren't present in the template, or in the case of no template,
4149 niro 1693 * all the lines following the entryStart.
4150 niro 914 */
4151     if (needs & NEED_TITLE) {
4152     newLine = addLine(new, config->cfi, LT_TITLE,
4153     config->secondaryIndent,
4154     newKernelTitle);
4155     needs &= ~NEED_TITLE;
4156 niro 532 }
4157 niro 914 if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
4158     newLine = addLine(new, config->cfi, LT_HYPER,
4159     config->secondaryIndent,
4160     newMBKernel + strlen(prefix));
4161     needs &= ~NEED_MB;
4162     }
4163     if (needs & NEED_KERNEL) {
4164     newLine = addLine(new, config->cfi,
4165     (new->multiboot && getKeywordByType(LT_MBMODULE,
4166 niro 1940 config->cfi))
4167     ? LT_MBMODULE
4168     : preferredLineType(LT_KERNEL, config->cfi),
4169 niro 914 config->secondaryIndent,
4170     newKernelPath + strlen(prefix));
4171     needs &= ~NEED_KERNEL;
4172     }
4173     if (needs & NEED_MB) {
4174     newLine = addLine(new, config->cfi, LT_HYPER,
4175     config->secondaryIndent,
4176     newMBKernel + strlen(prefix));
4177     needs &= ~NEED_MB;
4178     }
4179     if (needs & NEED_INITRD) {
4180     char *initrdVal;
4181     initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
4182     newLine = addLine(new, config->cfi,
4183     (new->multiboot && getKeywordByType(LT_MBMODULE,
4184 niro 1940 config->cfi))
4185     ? LT_MBMODULE
4186     : preferredLineType(LT_INITRD, config->cfi),
4187 niro 914 config->secondaryIndent,
4188     initrdVal);
4189     free(initrdVal);
4190     needs &= ~NEED_INITRD;
4191     }
4192 niro 2685 if (needs & NEED_DEVTREE) {
4193     newLine = addLine(new, config->cfi, LT_DEVTREE,
4194     config->secondaryIndent,
4195     newDevTreePath);
4196     needs &= ~NEED_DEVTREE;
4197     }
4198    
4199     /* NEEDS_END must be last on bootloaders that need it... */
4200 niro 1696 if (needs & NEED_END) {
4201     newLine = addLine(new, config->cfi, LT_ENTRY_END,
4202     config->secondaryIndent, NULL);
4203     needs &= ~NEED_END;
4204     }
4205 niro 2709
4206 niro 914 if (needs) {
4207     printf(_("grubby: needs=%d, aborting\n"), needs);
4208     abort();
4209     }
4210    
4211 niro 532 if (updateImage(config, "0", prefix, newKernelArgs, NULL,
4212     newMBKernelArgs, NULL)) return 1;
4213    
4214     return 0;
4215     }
4216    
4217     int main(int argc, const char ** argv) {
4218     poptContext optCon;
4219 niro 1696 const char * grubConfig = NULL;
4220 niro 532 char * outputFile = NULL;
4221     int arg = 0;
4222     int flags = 0;
4223     int badImageOkay = 0;
4224 niro 1696 int configureGrub2 = 0;
4225 niro 532 int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4226     int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4227 niro 914 int configureExtLinux = 0;
4228 niro 532 int bootloaderProbe = 0;
4229 niro 914 int extraInitrdCount = 0;
4230 niro 532 char * updateKernelPath = NULL;
4231     char * newKernelPath = NULL;
4232     char * removeKernelPath = NULL;
4233     char * newKernelArgs = NULL;
4234     char * newKernelInitrd = NULL;
4235     char * newKernelTitle = NULL;
4236 niro 2685 char * newDevTreePath = NULL;
4237 niro 532 char * newMBKernel = NULL;
4238     char * newMBKernelArgs = NULL;
4239     char * removeMBKernelArgs = NULL;
4240     char * removeMBKernel = NULL;
4241     char * bootPrefix = NULL;
4242     char * defaultKernel = NULL;
4243     char * removeArgs = NULL;
4244     char * kernelInfo = NULL;
4245 niro 914 char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4246 niro 2252 char * envPath = NULL;
4247 niro 532 const char * chptr = NULL;
4248     struct configFileInfo * cfi = NULL;
4249     struct grubConfig * config;
4250     struct singleEntry * template = NULL;
4251     int copyDefault = 0, makeDefault = 0;
4252     int displayDefault = 0;
4253 niro 1720 int displayDefaultIndex = 0;
4254 niro 1721 int displayDefaultTitle = 0;
4255 niro 1859 int defaultIndex = -1;
4256 niro 532 struct poptOption options[] = {
4257     { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4258     _("add an entry for the specified kernel"), _("kernel-path") },
4259     { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
4260     _("add an entry for the specified multiboot kernel"), NULL },
4261     { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
4262     _("default arguments for the new kernel or new arguments for "
4263     "kernel being updated"), _("args") },
4264     { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
4265     _("default arguments for the new multiboot kernel or "
4266     "new arguments for multiboot kernel being updated"), NULL },
4267     { "bad-image-okay", 0, 0, &badImageOkay, 0,
4268     _("don't sanity check images in boot entries (for testing only)"),
4269     NULL },
4270     { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4271     _("filestystem which contains /boot directory (for testing only)"),
4272     _("bootfs") },
4273 niro 1854 #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4274 niro 532 { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4275 niro 1853 _("check which bootloader is installed on boot sector") },
4276 niro 532 #endif
4277     { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4278     _("path to grub config file to update (\"-\" for stdin)"),
4279     _("path") },
4280     { "copy-default", 0, 0, &copyDefault, 0,
4281     _("use the default boot entry as a template for the new entry "
4282     "being added; if the default is not a linux image, or if "
4283     "the kernel referenced by the default image does not exist, "
4284     "the first linux entry whose kernel does exist is used as the "
4285     "template"), NULL },
4286 niro 1736 { "debug", 0, 0, &debug, 0,
4287     _("print debugging information for failures") },
4288 niro 532 { "default-kernel", 0, 0, &displayDefault, 0,
4289     _("display the path of the default kernel") },
4290 niro 1720 { "default-index", 0, 0, &displayDefaultIndex, 0,
4291     _("display the index of the default kernel") },
4292 niro 1721 { "default-title", 0, 0, &displayDefaultTitle, 0,
4293     _("display the title of the default kernel") },
4294 niro 2685 { "devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4295     _("device tree file for new stanza"), _("dtb-path") },
4296 niro 2972 { "devtreedir", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4297     _("device tree directory for new stanza"), _("dtb-path") },
4298 niro 532 { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
4299     _("configure elilo bootloader") },
4300 niro 1940 { "efi", 0, POPT_ARG_NONE, &isEfi, 0,
4301     _("force grub2 stanzas to use efi") },
4302 niro 2252 { "env", 0, POPT_ARG_STRING, &envPath, 0,
4303     _("path for environment data"),
4304     _("path") },
4305 niro 914 { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
4306     _("configure extlinux bootloader (from syslinux)") },
4307 niro 532 { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
4308     _("configure grub bootloader") },
4309 niro 1696 { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
4310     _("configure grub2 bootloader") },
4311 niro 532 { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
4312     _("display boot information for specified kernel"),
4313     _("kernel-path") },
4314     { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
4315     _("initrd image for the new kernel"), _("initrd-path") },
4316 niro 914 { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
4317 niro 2058 _("auxiliary initrd image for things other than the new kernel"), _("initrd-path") },
4318 niro 532 { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
4319     _("configure lilo bootloader") },
4320     { "make-default", 0, 0, &makeDefault, 0,
4321     _("make the newly added entry the default boot entry"), NULL },
4322     { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
4323     _("path to output updated config file (\"-\" for stdout)"),
4324     _("path") },
4325     { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
4326     _("remove kernel arguments"), NULL },
4327     { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
4328     _("remove multiboot kernel arguments"), NULL },
4329     { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
4330     _("remove all entries for the specified kernel"),
4331     _("kernel-path") },
4332     { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
4333     _("remove all entries for the specified multiboot kernel"), NULL },
4334     { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
4335     _("make the first entry referencing the specified kernel "
4336     "the default"), _("kernel-path") },
4337 niro 1859 { "set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
4338     _("make the given entry index the default entry"),
4339     _("entry-index") },
4340 niro 532 { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
4341     _("configure silo bootloader") },
4342     { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
4343     _("title to use for the new kernel entry"), _("entry-title") },
4344     { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
4345     _("updated information for the specified kernel"),
4346     _("kernel-path") },
4347     { "version", 'v', 0, NULL, 'v',
4348     _("print the version of this program and exit"), NULL },
4349     { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
4350     _("configure yaboot bootloader") },
4351     { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
4352     _("configure zipl bootloader") },
4353     POPT_AUTOHELP
4354     { 0, 0, 0, 0, 0 }
4355     };
4356    
4357 niro 914 useextlinuxmenu=0;
4358    
4359 niro 2236 int i = 0;
4360     for (int j = 1; j < argc; j++)
4361     i += strlen(argv[j]) + 1;
4362     saved_command_line = malloc(i);
4363     if (!saved_command_line) {
4364     fprintf(stderr, "grubby: %m\n");
4365     exit(1);
4366     }
4367     saved_command_line[0] = '\0';
4368     for (int j = 1; j < argc; j++) {
4369     strcat(saved_command_line, argv[j]);
4370     strncat(saved_command_line, j == argc -1 ? "" : " ", 1);
4371     }
4372    
4373 niro 532 optCon = poptGetContext("grubby", argc, argv, options, 0);
4374     poptReadDefaultConfig(optCon, 1);
4375    
4376     while ((arg = poptGetNextOpt(optCon)) >= 0) {
4377     switch (arg) {
4378     case 'v':
4379     printf("grubby version %s\n", VERSION);
4380     exit(0);
4381     break;
4382 niro 914 case 'i':
4383     if (extraInitrdCount < MAX_EXTRA_INITRDS) {
4384     extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
4385     } else {
4386     fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
4387     return 1;
4388     }
4389     break;
4390 niro 532 }
4391     }
4392    
4393     if (arg < -1) {
4394     fprintf(stderr, _("grubby: bad argument %s: %s\n"),
4395     poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
4396     poptStrerror(arg));
4397     return 1;
4398     }
4399    
4400     if ((chptr = poptGetArg(optCon))) {
4401     fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
4402     return 1;
4403     }
4404    
4405 niro 1696 if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
4406 niro 914 configureYaboot + configureSilo + configureZipl +
4407     configureExtLinux ) > 1) {
4408 niro 532 fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
4409     return 1;
4410     } else if (bootloaderProbe && grubConfig) {
4411     fprintf(stderr,
4412     _("grubby: cannot specify config file with --bootloader-probe\n"));
4413     return 1;
4414 niro 1696 } else if (configureGrub2) {
4415     cfi = &grub2ConfigType;
4416 niro 2252 if (envPath)
4417     cfi->envFile = envPath;
4418 niro 532 } else if (configureLilo) {
4419     cfi = &liloConfigType;
4420     } else if (configureGrub) {
4421     cfi = &grubConfigType;
4422     } else if (configureELilo) {
4423     cfi = &eliloConfigType;
4424     } else if (configureYaboot) {
4425     cfi = &yabootConfigType;
4426     } else if (configureSilo) {
4427     cfi = &siloConfigType;
4428     } else if (configureZipl) {
4429     cfi = &ziplConfigType;
4430 niro 914 } else if (configureExtLinux) {
4431     cfi = &extlinuxConfigType;
4432     useextlinuxmenu=1;
4433 niro 532 }
4434    
4435     if (!cfi) {
4436 niro 2986 if (grub2FindConfig(&grub2ConfigType)) {
4437 niro 1802 cfi = &grub2ConfigType;
4438 niro 2986 if (envPath)
4439     cfi->envFile = envPath;
4440     } else
4441 niro 532 #ifdef __ia64__
4442 niro 1802 cfi = &eliloConfigType;
4443 niro 532 #elif __powerpc__
4444 niro 1802 cfi = &yabootConfigType;
4445 niro 532 #elif __sparc__
4446 niro 1802 cfi = &siloConfigType;
4447 niro 532 #elif __s390__
4448 niro 1802 cfi = &ziplConfigType;
4449 niro 532 #elif __s390x__
4450 niro 1802 cfi = &ziplConfigtype;
4451 niro 532 #else
4452 niro 1696 cfi = &grubConfigType;
4453 niro 532 #endif
4454     }
4455    
4456 niro 1696 if (!grubConfig) {
4457     if (cfi->findConfig)
4458     grubConfig = cfi->findConfig(cfi);
4459     if (!grubConfig)
4460     grubConfig = cfi->defaultConfig;
4461     }
4462 niro 532
4463 niro 2685 if (bootloaderProbe && (displayDefault || kernelInfo ||
4464 niro 1859 newKernelPath || removeKernelPath || makeDefault ||
4465     defaultKernel || displayDefaultIndex || displayDefaultTitle ||
4466     (defaultIndex >= 0))) {
4467 niro 532 fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
4468     "specified option"));
4469     return 1;
4470     }
4471    
4472 niro 2685 if ((displayDefault || kernelInfo) && (newKernelPath ||
4473 niro 532 removeKernelPath)) {
4474     fprintf(stderr, _("grubby: --default-kernel and --info may not "
4475     "be used when adding or removing kernels\n"));
4476     return 1;
4477     }
4478    
4479     if (newKernelPath && !newKernelTitle) {
4480     fprintf(stderr, _("grubby: kernel title must be specified\n"));
4481     return 1;
4482 niro 2960 } else if (!newKernelPath && (copyDefault ||
4483 niro 1156 (newKernelInitrd && !updateKernelPath)||
4484 niro 914 makeDefault || extraInitrdCount > 0)) {
4485 niro 532 fprintf(stderr, _("grubby: kernel path expected\n"));
4486     return 1;
4487     }
4488    
4489     if (newKernelPath && updateKernelPath) {
4490     fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
4491     "not be used together"));
4492     return 1;
4493     }
4494    
4495     if (makeDefault && defaultKernel) {
4496     fprintf(stderr, _("grubby: --make-default and --default-kernel "
4497     "may not be used together\n"));
4498     return 1;
4499     } else if (defaultKernel && removeKernelPath &&
4500     !strcmp(defaultKernel, removeKernelPath)) {
4501     fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));
4502     return 1;
4503     } else if (defaultKernel && newKernelPath &&
4504     !strcmp(defaultKernel, newKernelPath)) {
4505     makeDefault = 1;
4506     defaultKernel = NULL;
4507     }
4508 niro 1859 else if (defaultKernel && (defaultIndex >= 0)) {
4509     fprintf(stderr, _("grubby: --set-default and --set-default-index "
4510     "may not be used together\n"));
4511     return 1;
4512     }
4513 niro 532
4514 niro 1717 if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
4515 niro 532 fprintf(stderr, _("grubby: output file must be specified if stdin "
4516     "is used\n"));
4517     return 1;
4518     }
4519    
4520     if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
4521 niro 1859 && !kernelInfo && !bootloaderProbe && !updateKernelPath
4522     && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle
4523     && (defaultIndex == -1)) {
4524 niro 532 fprintf(stderr, _("grubby: no action specified\n"));
4525     return 1;
4526     }
4527    
4528     flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
4529    
4530     if (cfi->needsBootPrefix) {
4531     if (!bootPrefix) {
4532     bootPrefix = findBootPrefix();
4533     if (!bootPrefix) return 1;
4534     } else {
4535     /* this shouldn't end with a / */
4536     if (bootPrefix[strlen(bootPrefix) - 1] == '/')
4537     bootPrefix[strlen(bootPrefix) - 1] = '\0';
4538     }
4539     } else {
4540     bootPrefix = "";
4541     }
4542    
4543 niro 914 if (!cfi->mbAllowExtraInitRds &&
4544     extraInitrdCount > 0) {
4545     fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
4546     return 1;
4547     }
4548    
4549 niro 532 if (bootloaderProbe) {
4550 niro 1854 int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
4551     struct grubConfig * lconfig, * gconfig, * yconfig, * econfig;
4552 niro 532
4553 niro 1696 const char *grub2config = grub2FindConfig(&grub2ConfigType);
4554     if (grub2config) {
4555     gconfig = readConfig(grub2config, &grub2ConfigType);
4556     if (!gconfig)
4557     gr2c = 1;
4558     else
4559     gr2c = checkForGrub2(gconfig);
4560     }
4561    
4562 niro 1715 const char *grubconfig = grubFindConfig(&grubConfigType);
4563     if (!access(grubconfig, F_OK)) {
4564     gconfig = readConfig(grubconfig, &grubConfigType);
4565 niro 532 if (!gconfig)
4566     grc = 1;
4567     else
4568     grc = checkForGrub(gconfig);
4569     }
4570    
4571     if (!access(liloConfigType.defaultConfig, F_OK)) {
4572     lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);
4573     if (!lconfig)
4574     lrc = 1;
4575     else
4576     lrc = checkForLilo(lconfig);
4577     }
4578    
4579 niro 1854 if (!access(eliloConfigType.defaultConfig, F_OK)) {
4580     econfig = readConfig(eliloConfigType.defaultConfig,
4581     &eliloConfigType);
4582     if (!econfig)
4583     erc = 1;
4584     else
4585     erc = checkForElilo(econfig);
4586     }
4587    
4588 niro 914 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
4589     lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
4590     if (!lconfig)
4591 niro 1854 extrc = 1;
4592 niro 914 else
4593 niro 1854 extrc = checkForExtLinux(lconfig);
4594 niro 914 }
4595    
4596 niro 532
4597 niro 1853 if (!access(yabootConfigType.defaultConfig, F_OK)) {
4598     yconfig = readConfig(yabootConfigType.defaultConfig,
4599     &yabootConfigType);
4600     if (!yconfig)
4601     yrc = 1;
4602     else
4603 niro 1864 yrc = checkForYaboot(yconfig);
4604 niro 1853 }
4605    
4606 niro 1854 if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1 ||
4607 niro 1855 erc == 1)
4608 niro 1854 return 1;
4609 niro 1853
4610 niro 532 if (lrc == 2) printf("lilo\n");
4611 niro 1696 if (gr2c == 2) printf("grub2\n");
4612 niro 532 if (grc == 2) printf("grub\n");
4613 niro 1854 if (extrc == 2) printf("extlinux\n");
4614 niro 1853 if (yrc == 2) printf("yaboot\n");
4615 niro 1854 if (erc == 2) printf("elilo\n");
4616 niro 532
4617     return 0;
4618     }
4619    
4620 niro 2246 if (grubConfig == NULL) {
4621     printf("Could not find bootloader configuration file.\n");
4622     exit(1);
4623     }
4624    
4625 niro 532 config = readConfig(grubConfig, cfi);
4626     if (!config) return 1;
4627    
4628     if (displayDefault) {
4629     struct singleLine * line;
4630     struct singleEntry * entry;
4631     char * rootspec;
4632    
4633     if (config->defaultImage == -1) return 0;
4634 niro 2255 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4635     cfi->defaultIsSaved)
4636     config->defaultImage = 0;
4637 niro 532 entry = findEntryByIndex(config, config->defaultImage);
4638     if (!entry) return 0;
4639     if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
4640    
4641 niro 2683 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
4642 niro 532 if (!line) return 0;
4643    
4644     rootspec = getRootSpecifier(line->elements[1].item);
4645     printf("%s%s\n", bootPrefix, line->elements[1].item +
4646     ((rootspec != NULL) ? strlen(rootspec) : 0));
4647    
4648     return 0;
4649 niro 1720
4650 niro 1721 } else if (displayDefaultTitle) {
4651     struct singleLine * line;
4652     struct singleEntry * entry;
4653    
4654 niro 2992 if (config->defaultImage == -1)
4655     return 0;
4656 niro 2255 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4657     cfi->defaultIsSaved)
4658     config->defaultImage = 0;
4659 niro 1721 entry = findEntryByIndex(config, config->defaultImage);
4660 niro 2992 if (!entry)
4661     return 0;
4662 niro 1721
4663     if (!configureGrub2) {
4664 niro 2992 char *title;
4665     line = getLineByType(LT_TITLE, entry->lines);
4666     if (!line)
4667     return 0;
4668     title = extractTitle(config, line);
4669     if (!title)
4670     return 0;
4671     printf("%s\n", title);
4672     free(title);
4673 niro 1721 } else {
4674 niro 2992 char * title;
4675 niro 1721
4676 niro 2992 dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
4677     line = getLineByType(LT_MENUENTRY, entry->lines);
4678     if (!line)
4679     return 0;
4680     title = grub2ExtractTitle(line);
4681     if (title)
4682     printf("%s\n", title);
4683 niro 1721 }
4684     return 0;
4685    
4686 niro 1720 } else if (displayDefaultIndex) {
4687     if (config->defaultImage == -1) return 0;
4688 niro 2255 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4689     cfi->defaultIsSaved)
4690     config->defaultImage = 0;
4691 niro 1720 printf("%i\n", config->defaultImage);
4692 niro 2250 return 0;
4693 niro 1720
4694 niro 532 } else if (kernelInfo)
4695     return displayInfo(config, kernelInfo, bootPrefix);
4696    
4697     if (copyDefault) {
4698     template = findTemplate(config, bootPrefix, NULL, 0, flags);
4699     if (!template) return 1;
4700     }
4701    
4702     markRemovedImage(config, removeKernelPath, bootPrefix);
4703     markRemovedImage(config, removeMBKernel, bootPrefix);
4704     setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
4705 niro 1859 bootPrefix, flags, defaultIndex);
4706 niro 532 setFallbackImage(config, newKernelPath != NULL);
4707     if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
4708     removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
4709 niro 1156 if (updateKernelPath && newKernelInitrd) {
4710 niro 2263 if (newMBKernel) {
4711     if (addMBInitrd(config, newMBKernel, updateKernelPath,
4712 niro 2960 bootPrefix, newKernelInitrd,
4713     newKernelTitle))
4714 niro 2263 return 1;
4715     } else {
4716     if (updateInitrd(config, updateKernelPath, bootPrefix,
4717 niro 2960 newKernelInitrd, newKernelTitle))
4718 niro 2263 return 1;
4719     }
4720 niro 1156 }
4721 niro 532 if (addNewKernel(config, template, bootPrefix, newKernelPath,
4722     newKernelTitle, newKernelArgs, newKernelInitrd,
4723 niro 1844 (const char **)extraInitrds, extraInitrdCount,
4724 niro 2685 newMBKernel, newMBKernelArgs, newDevTreePath)) return 1;
4725 niro 532
4726    
4727     if (numEntries(config) == 0) {
4728     fprintf(stderr, _("grubby: doing this would leave no kernel entries. "
4729     "Not writing out new config.\n"));
4730     return 1;
4731     }
4732    
4733     if (!outputFile)
4734 niro 1696 outputFile = (char *)grubConfig;
4735 niro 532
4736     return writeConfig(config, outputFile, bootPrefix);
4737     }