Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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