Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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