Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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