Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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