Magellan Linux

Annotation of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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