Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3024 - (show annotations) (download)
Tue Jun 27 14:41:50 2017 UTC (6 years, 10 months ago) by niro
File MIME type: text/plain
File size: 132456 byte(s)
Fix findTemplate index logic (#1285601)
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, unmodifiedIndex;
2443
2444 unmodifiedIndex = index;
2445
2446 for (j = 0; j < unmodifiedIndex; j++) {
2447 entry2 = findEntryByIndex(cfg, j);
2448 if (entry2->skip)
2449 index--;
2450 }
2451 if (indexPtr)
2452 *indexPtr = index;
2453
2454 return entry;
2455 }
2456
2457 index++;
2458 }
2459
2460 fprintf(stderr,
2461 _("grubby fatal error: unable to find a suitable template\n"));
2462
2463 return NULL;
2464 }
2465
2466 char *findBootPrefix(void)
2467 {
2468 struct stat sb, sb2;
2469
2470 stat("/", &sb);
2471 #ifdef __ia64__
2472 stat("/boot/efi/EFI/redhat/", &sb2);
2473 #else
2474 stat("/boot", &sb2);
2475 #endif
2476
2477 if (sb.st_dev == sb2.st_dev)
2478 return strdup("");
2479
2480 #ifdef __ia64__
2481 return strdup("/boot/efi/EFI/redhat/");
2482 #else
2483 return strdup("/boot");
2484 #endif
2485 }
2486
2487 void markRemovedImage(struct grubConfig *cfg, const char *image,
2488 const char *prefix)
2489 {
2490 struct singleEntry *entry;
2491
2492 if (!image)
2493 return;
2494
2495 /* check and see if we're removing the default image */
2496 if (isdigit(*image)) {
2497 entry = findEntryByPath(cfg, image, prefix, NULL);
2498 if (entry)
2499 entry->skip = 1;
2500 return;
2501 }
2502
2503 while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
2504 entry->skip = 1;
2505 }
2506
2507 void setDefaultImage(struct grubConfig *config, int isAddingBootEntry,
2508 const char *defaultKernelPath, int newBootEntryIsDefault,
2509 const char *prefix, int flags,
2510 int newDefaultBootEntryIndex, int newBootEntryIndex)
2511 {
2512 struct singleEntry *bootEntry, *newDefault;
2513 int indexToVerify, firstKernelEntryIndex, currentLookupIndex;
2514
2515 /* handle the two cases where the user explictly picks the default
2516 * boot entry index as it would exist post-modification */
2517
2518 /* Case 1: user chose to make the latest boot entry the default */
2519 if (newBootEntryIsDefault) {
2520 config->defaultImage = newBootEntryIndex;
2521 return;
2522 }
2523
2524 /* Case 2: user picked an arbitrary index as the default boot entry */
2525 if (newDefaultBootEntryIndex >= FIRST_ENTRY_INDEX
2526 && config->cfi->defaultIsIndex) {
2527 indexToVerify = newDefaultBootEntryIndex;
2528
2529 /* user chose to make latest boot entry the default */
2530 if (newDefaultBootEntryIndex == newBootEntryIndex) {
2531 config->defaultImage = newBootEntryIndex;
2532 return;
2533 }
2534
2535 /* the user picks the default index based on the
2536 * order of the bootloader configuration after
2537 * modification; ensure we are checking for the
2538 * existence of the correct entry */
2539 if (newBootEntryIndex < newDefaultBootEntryIndex) {
2540 if (!config->isModified)
2541 indexToVerify--;
2542 }
2543
2544 /* verify the user selected index will exist */
2545 if (findEntryByIndex(config, indexToVerify)) {
2546 config->defaultImage = newDefaultBootEntryIndex;
2547 } else {
2548 config->defaultImage = NO_DEFAULT_ENTRY;
2549 }
2550
2551 return;
2552 }
2553
2554 /* handle cases where the index value may shift */
2555
2556 /* check validity of existing default or first-entry-found
2557 selection */
2558 if (defaultKernelPath) {
2559 /* user requested first-entry-found */
2560 if (!findEntryByPath(config, defaultKernelPath,
2561 prefix, &firstKernelEntryIndex)) {
2562 /* don't change default if can't find match */
2563 config->defaultImage = NO_DEFAULT_ENTRY;
2564 return;
2565 }
2566
2567 config->defaultImage = firstKernelEntryIndex;
2568
2569 /* this is where we start looking for decrement later */
2570 currentLookupIndex = config->defaultImage;
2571
2572 if (isAddingBootEntry && !config->isModified &&
2573 (newBootEntryIndex < config->defaultImage)) {
2574 /* increment because new entry added before default */
2575 config->defaultImage++;
2576 }
2577 } else {
2578 /* use pre-existing default entry */
2579 currentLookupIndex = config->defaultImage;
2580
2581 if (isAddingBootEntry
2582 && (newBootEntryIndex <= config->defaultImage)) {
2583 config->defaultImage++;
2584
2585 if (config->isModified) {
2586 currentLookupIndex++;
2587 }
2588 }
2589 }
2590
2591 /* sanity check - is this entry index valid? */
2592 bootEntry = findEntryByIndex(config, currentLookupIndex);
2593
2594 if ((bootEntry && bootEntry->skip) || !bootEntry) {
2595 /* entry is to be skipped or is invalid */
2596 if (isAddingBootEntry) {
2597 config->defaultImage = newBootEntryIndex;
2598 return;
2599 }
2600 newDefault =
2601 findTemplate(config, prefix, &config->defaultImage, 1,
2602 flags);
2603 if (!newDefault) {
2604 config->defaultImage = NO_DEFAULT_ENTRY;
2605 }
2606
2607 return;
2608 }
2609
2610 currentLookupIndex--;
2611
2612 /* decrement index by the total number of entries deleted */
2613
2614 for (indexToVerify = currentLookupIndex;
2615 indexToVerify >= FIRST_ENTRY_INDEX; indexToVerify--) {
2616
2617 bootEntry = findEntryByIndex(config, indexToVerify);
2618
2619 if (bootEntry && bootEntry->skip) {
2620 config->defaultImage--;
2621 }
2622 }
2623 }
2624
2625 void setFallbackImage(struct grubConfig *config, int hasNew)
2626 {
2627 struct singleEntry *entry, *entry2;
2628 int j;
2629
2630 if (config->fallbackImage == -1)
2631 return;
2632
2633 entry = findEntryByIndex(config, config->fallbackImage);
2634 if (!entry || entry->skip) {
2635 config->fallbackImage = -1;
2636 return;
2637 }
2638
2639 if (hasNew)
2640 config->fallbackImage++;
2641
2642 /* count the number of entries erased before this one */
2643 for (j = 0; j < config->fallbackImage; j++) {
2644 entry2 = findEntryByIndex(config, j);
2645 if (entry2->skip)
2646 config->fallbackImage--;
2647 }
2648 }
2649
2650 void displayEntry(struct singleEntry *entry, const char *prefix, int index)
2651 {
2652 struct singleLine *line;
2653 char *root = NULL;
2654 int i;
2655 int j;
2656
2657 printf("index=%d\n", index);
2658
2659 line =
2660 getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI | LT_KERNEL_16,
2661 entry->lines);
2662 if (!line) {
2663 printf("non linux entry\n");
2664 return;
2665 }
2666
2667 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2668 printf("kernel=%s\n", line->elements[1].item);
2669 else
2670 printf("kernel=%s%s\n", prefix, line->elements[1].item);
2671
2672 if (line->numElements >= 3) {
2673 printf("args=\"");
2674 i = 2;
2675 while (i < line->numElements) {
2676 if (!strncmp(line->elements[i].item, "root=", 5)) {
2677 root = line->elements[i].item + 5;
2678 } else {
2679 printf("%s%s", line->elements[i].item,
2680 line->elements[i].indent);
2681 }
2682
2683 i++;
2684 }
2685 printf("\"\n");
2686 } else {
2687 line = getLineByType(LT_KERNELARGS, entry->lines);
2688 if (line) {
2689 char *s;
2690
2691 printf("args=\"");
2692 i = 1;
2693 while (i < line->numElements) {
2694 if (!strncmp
2695 (line->elements[i].item, "root=", 5)) {
2696 root = line->elements[i].item + 5;
2697 } else {
2698 s = line->elements[i].item;
2699
2700 printf("%s%s", s,
2701 line->elements[i].indent);
2702 }
2703
2704 i++;
2705 }
2706
2707 s = line->elements[i - 1].indent;
2708 printf("\"\n");
2709 }
2710 }
2711
2712 if (!root) {
2713 line = getLineByType(LT_ROOT, entry->lines);
2714 if (line && line->numElements >= 2)
2715 root = line->elements[1].item;
2716 }
2717
2718 if (root) {
2719 char *s = alloca(strlen(root) + 1);
2720
2721 strcpy(s, root);
2722 if (s[strlen(s) - 1] == '"')
2723 s[strlen(s) - 1] = '\0';
2724 /* make sure the root doesn't have a trailing " */
2725 printf("root=%s\n", s);
2726 }
2727
2728 line =
2729 getLineByType(LT_INITRD | LT_INITRD_EFI | LT_INITRD_16,
2730 entry->lines);
2731
2732 if (line && line->numElements >= 2) {
2733 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2734 printf("initrd=");
2735 else
2736 printf("initrd=%s", prefix);
2737
2738 for (i = 1; i < line->numElements; i++)
2739 printf("%s%s", line->elements[i].item,
2740 line->elements[i].indent);
2741 printf("\n");
2742 }
2743
2744 line = getLineByType(LT_TITLE, entry->lines);
2745 if (line) {
2746 printf("title=%s\n", line->elements[1].item);
2747 } else {
2748 char *title;
2749 line = getLineByType(LT_MENUENTRY, entry->lines);
2750 if (line) {
2751 title = grub2ExtractTitle(line);
2752 if (title)
2753 printf("title=%s\n", title);
2754 }
2755 }
2756
2757 for (j = 0, line = entry->lines; line; line = line->next) {
2758 if ((line->type & LT_MBMODULE) && line->numElements >= 2) {
2759 if (!strncmp
2760 (prefix, line->elements[1].item, strlen(prefix)))
2761 printf("mbmodule%d=", j);
2762 else
2763 printf("mbmodule%d=%s", j, prefix);
2764
2765 for (i = 1; i < line->numElements; i++)
2766 printf("%s%s", line->elements[i].item,
2767 line->elements[i].indent);
2768 printf("\n");
2769 j++;
2770 }
2771 }
2772 }
2773
2774 int isSuseSystem(void)
2775 {
2776 const char *path;
2777 const static char default_path[] = "/etc/SuSE-release";
2778
2779 if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2780 path = default_path;
2781
2782 if (!access(path, R_OK))
2783 return 1;
2784 return 0;
2785 }
2786
2787 int isSuseGrubConf(const char *path)
2788 {
2789 FILE *grubConf;
2790 char *line = NULL;
2791 size_t len = 0, res = 0;
2792
2793 grubConf = fopen(path, "r");
2794 if (!grubConf) {
2795 dbgPrintf("Could not open SuSE configuration file '%s'\n",
2796 path);
2797 return 0;
2798 }
2799
2800 while ((res = getline(&line, &len, grubConf)) != -1) {
2801 if (!strncmp(line, "setup", 5)) {
2802 fclose(grubConf);
2803 free(line);
2804 return 1;
2805 }
2806 }
2807
2808 dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2809 path);
2810
2811 fclose(grubConf);
2812 free(line);
2813 return 0;
2814 }
2815
2816 int suseGrubConfGetLba(const char *path, int *lbaPtr)
2817 {
2818 FILE *grubConf;
2819 char *line = NULL;
2820 size_t res = 0, len = 0;
2821
2822 if (!path)
2823 return 1;
2824 if (!lbaPtr)
2825 return 1;
2826
2827 grubConf = fopen(path, "r");
2828 if (!grubConf)
2829 return 1;
2830
2831 while ((res = getline(&line, &len, grubConf)) != -1) {
2832 if (line[res - 1] == '\n')
2833 line[res - 1] = '\0';
2834 else if (len > res)
2835 line[res] = '\0';
2836 else {
2837 line = realloc(line, res + 1);
2838 line[res] = '\0';
2839 }
2840
2841 if (!strncmp(line, "setup", 5)) {
2842 if (strstr(line, "--force-lba")) {
2843 *lbaPtr = 1;
2844 } else {
2845 *lbaPtr = 0;
2846 }
2847 dbgPrintf("lba: %i\n", *lbaPtr);
2848 break;
2849 }
2850 }
2851
2852 free(line);
2853 fclose(grubConf);
2854 return 0;
2855 }
2856
2857 int suseGrubConfGetInstallDevice(const char *path, char **devicePtr)
2858 {
2859 FILE *grubConf;
2860 char *line = NULL;
2861 size_t res = 0, len = 0;
2862 char *lastParamPtr = NULL;
2863 char *secLastParamPtr = NULL;
2864 char installDeviceNumber = '\0';
2865 char *bounds = NULL;
2866
2867 if (!path)
2868 return 1;
2869 if (!devicePtr)
2870 return 1;
2871
2872 grubConf = fopen(path, "r");
2873 if (!grubConf)
2874 return 1;
2875
2876 while ((res = getline(&line, &len, grubConf)) != -1) {
2877 if (strncmp(line, "setup", 5))
2878 continue;
2879
2880 if (line[res - 1] == '\n')
2881 line[res - 1] = '\0';
2882 else if (len > res)
2883 line[res] = '\0';
2884 else {
2885 line = realloc(line, res + 1);
2886 line[res] = '\0';
2887 }
2888
2889 lastParamPtr = bounds = line + res;
2890
2891 /* Last parameter in grub may be an optional IMAGE_DEVICE */
2892 while (!isspace(*lastParamPtr))
2893 lastParamPtr--;
2894 lastParamPtr++;
2895
2896 secLastParamPtr = lastParamPtr - 2;
2897 dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2898
2899 if (lastParamPtr + 3 > bounds) {
2900 dbgPrintf("lastParamPtr going over boundary");
2901 fclose(grubConf);
2902 free(line);
2903 return 1;
2904 }
2905 if (!strncmp(lastParamPtr, "(hd", 3))
2906 lastParamPtr += 3;
2907 dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2908
2909 /*
2910 * Second last parameter will decide wether last parameter is
2911 * an IMAGE_DEVICE or INSTALL_DEVICE
2912 */
2913 while (!isspace(*secLastParamPtr))
2914 secLastParamPtr--;
2915 secLastParamPtr++;
2916
2917 if (secLastParamPtr + 3 > bounds) {
2918 dbgPrintf("secLastParamPtr going over boundary");
2919 fclose(grubConf);
2920 free(line);
2921 return 1;
2922 }
2923 dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2924 if (!strncmp(secLastParamPtr, "(hd", 3)) {
2925 secLastParamPtr += 3;
2926 dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
2927 installDeviceNumber = *secLastParamPtr;
2928 } else {
2929 installDeviceNumber = *lastParamPtr;
2930 }
2931
2932 *devicePtr = malloc(6);
2933 snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
2934 dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
2935 fclose(grubConf);
2936 free(line);
2937 return 0;
2938 }
2939
2940 free(line);
2941 fclose(grubConf);
2942 return 1;
2943 }
2944
2945 int grubGetBootFromDeviceMap(const char *device, char **bootPtr)
2946 {
2947 FILE *deviceMap;
2948 char *line = NULL;
2949 size_t res = 0, len = 0;
2950 char *devicePtr;
2951 char *bounds = NULL;
2952 const char *path;
2953 const static char default_path[] = "/boot/grub/device.map";
2954
2955 if (!device)
2956 return 1;
2957 if (!bootPtr)
2958 return 1;
2959
2960 if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2961 path = default_path;
2962
2963 dbgPrintf("opening grub device.map file from: %s\n", path);
2964 deviceMap = fopen(path, "r");
2965 if (!deviceMap)
2966 return 1;
2967
2968 while ((res = getline(&line, &len, deviceMap)) != -1) {
2969 if (!strncmp(line, "#", 1))
2970 continue;
2971
2972 if (line[res - 1] == '\n')
2973 line[res - 1] = '\0';
2974 else if (len > res)
2975 line[res] = '\0';
2976 else {
2977 line = realloc(line, res + 1);
2978 line[res] = '\0';
2979 }
2980
2981 devicePtr = line;
2982 bounds = line + res;
2983
2984 while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2985 devicePtr++;
2986 dbgPrintf("device: %s\n", devicePtr);
2987
2988 if (!strncmp(devicePtr, device, strlen(device))) {
2989 devicePtr += strlen(device);
2990 while (isspace(*devicePtr)
2991 && ((devicePtr + 1) <= bounds))
2992 devicePtr++;
2993
2994 *bootPtr = strdup(devicePtr);
2995 break;
2996 }
2997 }
2998
2999 free(line);
3000 fclose(deviceMap);
3001 return 0;
3002 }
3003
3004 int suseGrubConfGetBoot(const char *path, char **bootPtr)
3005 {
3006 char *grubDevice;
3007
3008 if (suseGrubConfGetInstallDevice(path, &grubDevice))
3009 dbgPrintf("error looking for grub installation device\n");
3010 else
3011 dbgPrintf("grubby installation device: %s\n", grubDevice);
3012
3013 if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
3014 dbgPrintf("error looking for grub boot device\n");
3015 else
3016 dbgPrintf("grubby boot device: %s\n", *bootPtr);
3017
3018 free(grubDevice);
3019 return 0;
3020 }
3021
3022 int parseSuseGrubConf(int *lbaPtr, char **bootPtr)
3023 {
3024 /*
3025 * This SuSE grub configuration file at this location is not your
3026 * average grub configuration file, but instead the grub commands
3027 * used to setup grub on that system.
3028 */
3029 const char *path;
3030 const static char default_path[] = "/etc/grub.conf";
3031
3032 if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
3033 path = default_path;
3034
3035 if (!isSuseGrubConf(path))
3036 return 1;
3037
3038 if (lbaPtr) {
3039 *lbaPtr = 0;
3040 if (suseGrubConfGetLba(path, lbaPtr))
3041 return 1;
3042 }
3043
3044 if (bootPtr) {
3045 *bootPtr = NULL;
3046 suseGrubConfGetBoot(path, bootPtr);
3047 }
3048
3049 return 0;
3050 }
3051
3052 int parseSysconfigGrub(int *lbaPtr, char **bootPtr)
3053 {
3054 FILE *in;
3055 char buf[1024];
3056 char *chptr;
3057 char *start;
3058 char *param;
3059
3060 in = fopen("/etc/sysconfig/grub", "r");
3061 if (!in)
3062 return 1;
3063
3064 if (lbaPtr)
3065 *lbaPtr = 0;
3066 if (bootPtr)
3067 *bootPtr = NULL;
3068
3069 while (fgets(buf, sizeof(buf), in)) {
3070 start = buf;
3071 while (isspace(*start))
3072 start++;
3073 if (*start == '#')
3074 continue;
3075
3076 chptr = strchr(start, '=');
3077 if (!chptr)
3078 continue;
3079 chptr--;
3080 while (*chptr && isspace(*chptr))
3081 chptr--;
3082 chptr++;
3083 *chptr = '\0';
3084
3085 param = chptr + 1;
3086 while (*param && isspace(*param))
3087 param++;
3088 if (*param == '=') {
3089 param++;
3090 while (*param && isspace(*param))
3091 param++;
3092 }
3093
3094 chptr = param;
3095 while (*chptr && !isspace(*chptr))
3096 chptr++;
3097 *chptr = '\0';
3098
3099 if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
3100 *lbaPtr = 1;
3101 else if (!strcmp(start, "boot") && bootPtr)
3102 *bootPtr = strdup(param);
3103 }
3104
3105 fclose(in);
3106
3107 return 0;
3108 }
3109
3110 void dumpSysconfigGrub(void)
3111 {
3112 char *boot = NULL;
3113 int lba;
3114
3115 if (isSuseSystem()) {
3116 if (parseSuseGrubConf(&lba, &boot)) {
3117 free(boot);
3118 return;
3119 }
3120 } else {
3121 if (parseSysconfigGrub(&lba, &boot)) {
3122 free(boot);
3123 return;
3124 }
3125 }
3126
3127 if (lba)
3128 printf("lba\n");
3129 if (boot) {
3130 printf("boot=%s\n", boot);
3131 free(boot);
3132 }
3133 }
3134
3135 int displayInfo(struct grubConfig *config, char *kernel, const char *prefix)
3136 {
3137 int i = 0;
3138 struct singleEntry *entry;
3139 struct singleLine *line;
3140
3141 entry = findEntryByPath(config, kernel, prefix, &i);
3142 if (!entry) {
3143 fprintf(stderr, _("grubby: kernel not found\n"));
3144 return 1;
3145 }
3146
3147 /* this is a horrible hack to support /etc/sysconfig/grub; there must
3148 be a better way */
3149 if (config->cfi == &grubConfigType) {
3150 dumpSysconfigGrub();
3151 } else {
3152 line = getLineByType(LT_BOOT, config->theLines);
3153 if (line && line->numElements >= 1) {
3154 printf("boot=%s\n", line->elements[1].item);
3155 }
3156
3157 line = getLineByType(LT_LBA, config->theLines);
3158 if (line)
3159 printf("lba\n");
3160 }
3161
3162 displayEntry(entry, prefix, i);
3163
3164 i++;
3165 while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
3166 displayEntry(entry, prefix, i);
3167 i++;
3168 }
3169
3170 return 0;
3171 }
3172
3173 struct singleLine *addLineTmpl(struct singleEntry *entry,
3174 struct singleLine *tmplLine,
3175 struct singleLine *prevLine,
3176 const char *val, struct configFileInfo *cfi)
3177 {
3178 struct singleLine *newLine = lineDup(tmplLine);
3179
3180 if (isEfi && cfi == &grub2ConfigType) {
3181 enum lineType_e old = newLine->type;
3182 newLine->type = preferredLineType(newLine->type, cfi);
3183 if (old != newLine->type)
3184 newLine->elements[0].item =
3185 getKeyByType(newLine->type, cfi);
3186 }
3187
3188 if (val) {
3189 /* override the inherited value with our own.
3190 * This is a little weak because it only applies to elements[1]
3191 */
3192 if (newLine->numElements > 1)
3193 removeElement(newLine, 1);
3194 insertElement(newLine, val, 1, cfi);
3195
3196 /* but try to keep the rootspec from the template... sigh */
3197 if (tmplLine->
3198 type & (LT_HYPER | LT_KERNEL | LT_MBMODULE | LT_INITRD |
3199 LT_KERNEL_EFI | LT_INITRD_EFI | LT_KERNEL_16 |
3200 LT_INITRD_16)) {
3201 char *rootspec =
3202 getRootSpecifier(tmplLine->elements[1].item);
3203 if (rootspec != NULL) {
3204 free(newLine->elements[1].item);
3205 newLine->elements[1].item =
3206 sdupprintf("%s%s", rootspec, val);
3207 }
3208 }
3209 }
3210
3211 dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
3212 newLine->elements[0].item : "");
3213
3214 if (!entry->lines) {
3215 /* first one on the list */
3216 entry->lines = newLine;
3217 } else if (prevLine) {
3218 /* add after prevLine */
3219 newLine->next = prevLine->next;
3220 prevLine->next = newLine;
3221 }
3222
3223 return newLine;
3224 }
3225
3226 /* val may be NULL */
3227 struct singleLine *addLine(struct singleEntry *entry,
3228 struct configFileInfo *cfi,
3229 enum lineType_e type, char *defaultIndent,
3230 const char *val)
3231 {
3232 struct singleLine *line, *prev;
3233 struct keywordTypes *kw;
3234 struct singleLine tmpl;
3235
3236 /* NB: This function shouldn't allocate items on the heap, rather on
3237 * the stack since it calls addLineTmpl which will make copies.
3238 */
3239 if (type == LT_TITLE && cfi->titleBracketed) {
3240 /* we're doing a bracketed title (zipl) */
3241 tmpl.type = type;
3242 tmpl.numElements = 1;
3243 tmpl.elements = alloca(sizeof(*tmpl.elements));
3244 tmpl.elements[0].item = alloca(strlen(val) + 3);
3245 sprintf(tmpl.elements[0].item, "[%s]", val);
3246 tmpl.elements[0].indent = "";
3247 val = NULL;
3248 } else if (type == LT_MENUENTRY) {
3249 char *lineend = "--class gnu-linux --class gnu --class os {";
3250 if (!val) {
3251 fprintf(stderr,
3252 "Line type LT_MENUENTRY requires a value\n");
3253 abort();
3254 }
3255 kw = getKeywordByType(type, cfi);
3256 if (!kw) {
3257 fprintf(stderr,
3258 "Looking up keyword for unknown type %d\n",
3259 type);
3260 abort();
3261 }
3262 tmpl.indent = "";
3263 tmpl.type = type;
3264 tmpl.numElements = 3;
3265 tmpl.elements =
3266 alloca(sizeof(*tmpl.elements) * tmpl.numElements);
3267 tmpl.elements[0].item = kw->key;
3268 tmpl.elements[0].indent = alloca(2);
3269 sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
3270 tmpl.elements[1].item = (char *)val;
3271 tmpl.elements[1].indent = alloca(2);
3272 sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
3273 tmpl.elements[2].item = alloca(strlen(lineend) + 1);
3274 strcpy(tmpl.elements[2].item, lineend);
3275 tmpl.elements[2].indent = "";
3276 } else {
3277 kw = getKeywordByType(type, cfi);
3278 if (!kw) {
3279 fprintf(stderr,
3280 "Looking up keyword for unknown type %d\n",
3281 type);
3282 abort();
3283 }
3284 tmpl.type = type;
3285 tmpl.numElements = val ? 2 : 1;
3286 tmpl.elements =
3287 alloca(sizeof(*tmpl.elements) * tmpl.numElements);
3288 tmpl.elements[0].item = kw->key;
3289 tmpl.elements[0].indent = alloca(2);
3290 sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
3291 if (val) {
3292 tmpl.elements[1].item = (char *)val;
3293 tmpl.elements[1].indent = "";
3294 }
3295 }
3296
3297 /* The last non-empty line gives us the indention to us and the line
3298 * to insert after. Note that comments are considered empty lines,
3299 * which may not be ideal? If there are no lines or we are looking at
3300 * the first line, we use defaultIndent (the first line is normally
3301 * indented differently from the rest) */
3302 for (line = entry->lines, prev = NULL; line; line = line->next) {
3303 if (line->numElements)
3304 prev = line;
3305 /* fall back on the last line if prev isn't otherwise set */
3306 if (!line->next && !prev)
3307 prev = line;
3308 }
3309
3310 struct singleLine *menuEntry;
3311 menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
3312 if (tmpl.type == LT_ENTRY_END) {
3313 if (menuEntry)
3314 tmpl.indent = menuEntry->indent;
3315 else
3316 tmpl.indent = defaultIndent ? : "";
3317 } else if (tmpl.type != LT_MENUENTRY) {
3318 if (menuEntry)
3319 tmpl.indent = "\t";
3320 else if (prev == entry->lines)
3321 tmpl.indent = defaultIndent ? : "";
3322 else
3323 tmpl.indent = prev->indent;
3324 }
3325
3326 return addLineTmpl(entry, &tmpl, prev, val, cfi);
3327 }
3328
3329 void removeLine(struct singleEntry *entry, struct singleLine *line)
3330 {
3331 struct singleLine *prev;
3332 int i;
3333
3334 for (i = 0; i < line->numElements; i++) {
3335 free(line->elements[i].item);
3336 free(line->elements[i].indent);
3337 }
3338 free(line->elements);
3339 free(line->indent);
3340
3341 if (line == entry->lines) {
3342 entry->lines = line->next;
3343 } else {
3344 prev = entry->lines;
3345 while (prev->next != line)
3346 prev = prev->next;
3347 prev->next = line->next;
3348 }
3349
3350 free(line);
3351 }
3352
3353 static void requote(struct singleLine *tmplLine, struct configFileInfo *cfi)
3354 {
3355 struct singleLine newLine = {
3356 .indent = tmplLine->indent,
3357 .type = tmplLine->type,
3358 .next = tmplLine->next,
3359 };
3360 int firstQuotedItem = -1;
3361 int quoteLen = 0;
3362 int j;
3363 int element = 0;
3364 char *c;
3365
3366 c = malloc(strlen(tmplLine->elements[0].item) + 1);
3367 strcpy(c, tmplLine->elements[0].item);
3368 insertElement(&newLine, c, element++, cfi);
3369 free(c);
3370 c = NULL;
3371
3372 for (j = 1; j < tmplLine->numElements; j++) {
3373 if (firstQuotedItem == -1) {
3374 quoteLen += strlen(tmplLine->elements[j].item);
3375
3376 if (isquote(tmplLine->elements[j].item[0])) {
3377 firstQuotedItem = j;
3378 quoteLen +=
3379 strlen(tmplLine->elements[j].indent);
3380 } else {
3381 c = malloc(quoteLen + 1);
3382 strcpy(c, tmplLine->elements[j].item);
3383 insertElement(&newLine, c, element++, cfi);
3384 free(c);
3385 quoteLen = 0;
3386 }
3387 } else {
3388 int itemlen = strlen(tmplLine->elements[j].item);
3389 quoteLen += itemlen;
3390 quoteLen += strlen(tmplLine->elements[j].indent);
3391
3392 if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
3393 c = malloc(quoteLen + 1);
3394 c[0] = '\0';
3395 for (int i = firstQuotedItem; i < j + 1; i++) {
3396 strcat(c, tmplLine->elements[i].item);
3397 strcat(c, tmplLine->elements[i].indent);
3398 }
3399 insertElement(&newLine, c, element++, cfi);
3400 free(c);
3401
3402 firstQuotedItem = -1;
3403 quoteLen = 0;
3404 }
3405 }
3406 }
3407 while (tmplLine->numElements)
3408 removeElement(tmplLine, 0);
3409 if (tmplLine->elements)
3410 free(tmplLine->elements);
3411
3412 tmplLine->numElements = newLine.numElements;
3413 tmplLine->elements = newLine.elements;
3414 }
3415
3416 static void insertElement(struct singleLine *line,
3417 const char *item, int insertHere,
3418 struct configFileInfo *cfi)
3419 {
3420 struct keywordTypes *kw;
3421 char indent[2] = "";
3422
3423 /* sanity check */
3424 if (insertHere > line->numElements) {
3425 dbgPrintf
3426 ("insertElement() adjusting insertHere from %d to %d\n",
3427 insertHere, line->numElements);
3428 insertHere = line->numElements;
3429 }
3430
3431 line->elements = realloc(line->elements, (line->numElements + 1) *
3432 sizeof(*line->elements));
3433 memmove(&line->elements[insertHere + 1],
3434 &line->elements[insertHere],
3435 (line->numElements - insertHere) * sizeof(*line->elements));
3436 line->elements[insertHere].item = strdup(item);
3437
3438 kw = getKeywordByType(line->type, cfi);
3439
3440 if (line->numElements == 0) {
3441 indent[0] = '\0';
3442 } else if (insertHere == 0) {
3443 indent[0] = kw->nextChar;
3444 } else if (kw->separatorChar != '\0') {
3445 indent[0] = kw->separatorChar;
3446 } else {
3447 indent[0] = ' ';
3448 }
3449
3450 if (insertHere > 0 && line->elements[insertHere - 1].indent[0] == '\0') {
3451 /* move the end-of-line forward */
3452 line->elements[insertHere].indent =
3453 line->elements[insertHere - 1].indent;
3454 line->elements[insertHere - 1].indent = strdup(indent);
3455 } else {
3456 line->elements[insertHere].indent = strdup(indent);
3457 }
3458
3459 line->numElements++;
3460
3461 dbgPrintf("insertElement(%s, '%s%s', %d)\n",
3462 line->elements[0].item,
3463 line->elements[insertHere].item,
3464 line->elements[insertHere].indent, insertHere);
3465 }
3466
3467 static void removeElement(struct singleLine *line, int removeHere)
3468 {
3469 int i;
3470
3471 /* sanity check */
3472 if (removeHere >= line->numElements)
3473 return;
3474
3475 dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
3476 removeHere, line->elements[removeHere].item);
3477
3478 free(line->elements[removeHere].item);
3479
3480 if (removeHere > 1) {
3481 /* previous argument gets this argument's post-indentation */
3482 free(line->elements[removeHere - 1].indent);
3483 line->elements[removeHere - 1].indent =
3484 line->elements[removeHere].indent;
3485 } else {
3486 free(line->elements[removeHere].indent);
3487 }
3488
3489 /* now collapse the array, but don't bother to realloc smaller */
3490 for (i = removeHere; i < line->numElements - 1; i++)
3491 line->elements[i] = line->elements[i + 1];
3492
3493 line->numElements--;
3494 }
3495
3496 int argMatch(const char *one, const char *two)
3497 {
3498 char *first, *second;
3499 char *chptr;
3500
3501 first = strcpy(alloca(strlen(one) + 1), one);
3502 second = strcpy(alloca(strlen(two) + 1), two);
3503
3504 chptr = strchr(first, '=');
3505 if (chptr)
3506 *chptr = '\0';
3507
3508 chptr = strchr(second, '=');
3509 if (chptr)
3510 *chptr = '\0';
3511
3512 return strcmp(first, second);
3513 }
3514
3515 int updateActualImage(struct grubConfig *cfg, const char *image,
3516 const char *prefix, const char *addArgs,
3517 const char *removeArgs, int multibootArgs)
3518 {
3519 struct singleEntry *entry;
3520 struct singleLine *line, *rootLine;
3521 int index = 0;
3522 int i, k;
3523 const char **newArgs, **oldArgs;
3524 const char **arg;
3525 int useKernelArgs, useRoot;
3526 int firstElement;
3527 int *usedElements;
3528 int doreplace;
3529
3530 if (!image)
3531 return 0;
3532
3533 if (!addArgs) {
3534 newArgs = malloc(sizeof(*newArgs));
3535 *newArgs = NULL;
3536 } else {
3537 if (poptParseArgvString(addArgs, NULL, &newArgs)) {
3538 fprintf(stderr,
3539 _("grubby: error separating arguments '%s'\n"),
3540 addArgs);
3541 return 1;
3542 }
3543 }
3544
3545 if (!removeArgs) {
3546 oldArgs = malloc(sizeof(*oldArgs));
3547 *oldArgs = NULL;
3548 } else {
3549 if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
3550 fprintf(stderr,
3551 _("grubby: error separating arguments '%s'\n"),
3552 removeArgs);
3553 free(newArgs);
3554 return 1;
3555 }
3556 }
3557
3558 useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
3559 && (!multibootArgs || cfg->cfi->mbConcatArgs));
3560
3561 useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
3562 && !multibootArgs);
3563
3564 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3565
3566 if (multibootArgs && !entry->multiboot)
3567 continue;
3568
3569 /* Determine where to put the args. If this config supports
3570 * LT_KERNELARGS, use that. Otherwise use
3571 * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
3572 */
3573 if (useKernelArgs) {
3574 line = getLineByType(LT_KERNELARGS, entry->lines);
3575 if (!line) {
3576 /* no LT_KERNELARGS, need to add it */
3577 line = addLine(entry, cfg->cfi, LT_KERNELARGS,
3578 cfg->secondaryIndent, NULL);
3579 }
3580 firstElement = 1;
3581
3582 } else if (multibootArgs) {
3583 line = getLineByType(LT_HYPER, entry->lines);
3584 if (!line) {
3585 /* a multiboot entry without LT_HYPER? */
3586 continue;
3587 }
3588 firstElement = 2;
3589
3590 } else {
3591 line =
3592 getLineByType(LT_KERNEL | LT_MBMODULE |
3593 LT_KERNEL_EFI | LT_KERNEL_16,
3594 entry->lines);
3595 if (!line) {
3596 /* no LT_KERNEL or LT_MBMODULE in this entry? */
3597 continue;
3598 }
3599 firstElement = 2;
3600 }
3601
3602 /* handle the elilo case which does:
3603 * append="hypervisor args -- kernel args"
3604 */
3605 if (entry->multiboot && cfg->cfi->mbConcatArgs) {
3606 /* this is a multiboot entry, make sure there's
3607 * -- on the args line
3608 */
3609 for (i = firstElement; i < line->numElements; i++) {
3610 if (!strcmp(line->elements[i].item, "--"))
3611 break;
3612 }
3613 if (i == line->numElements) {
3614 /* assume all existing args are kernel args,
3615 * prepend -- to make it official
3616 */
3617 insertElement(line, "--", firstElement,
3618 cfg->cfi);
3619 i = firstElement;
3620 }
3621 if (!multibootArgs) {
3622 /* kernel args start after the -- */
3623 firstElement = i + 1;
3624 }
3625 } else if (cfg->cfi->mbConcatArgs) {
3626 /* this is a non-multiboot entry, remove hyper args */
3627 for (i = firstElement; i < line->numElements; i++) {
3628 if (!strcmp(line->elements[i].item, "--"))
3629 break;
3630 }
3631 if (i < line->numElements) {
3632 /* remove args up to -- */
3633 while (strcmp
3634 (line->elements[firstElement].item,
3635 "--"))
3636 removeElement(line, firstElement);
3637 /* remove -- */
3638 removeElement(line, firstElement);
3639 }
3640 }
3641
3642 usedElements = calloc(line->numElements, sizeof(*usedElements));
3643
3644 for (k = 0, arg = newArgs; *arg; arg++, k++) {
3645
3646 doreplace = 1;
3647 for (i = firstElement; i < line->numElements; i++) {
3648 if (multibootArgs && cfg->cfi->mbConcatArgs &&
3649 !strcmp(line->elements[i].item, "--")) {
3650 /* reached the end of hyper args, insert here */
3651 doreplace = 0;
3652 break;
3653 }
3654 if (usedElements[i])
3655 continue;
3656 if (!argMatch(line->elements[i].item, *arg)) {
3657 usedElements[i] = 1;
3658 break;
3659 }
3660 }
3661
3662 if (i < line->numElements && doreplace) {
3663 /* direct replacement */
3664 free(line->elements[i].item);
3665 line->elements[i].item = strdup(*arg);
3666
3667 } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
3668 /* root= replacement */
3669 rootLine = getLineByType(LT_ROOT, entry->lines);
3670 if (rootLine) {
3671 free(rootLine->elements[1].item);
3672 rootLine->elements[1].item =
3673 strdup(*arg + 5);
3674 } else {
3675 rootLine =
3676 addLine(entry, cfg->cfi, LT_ROOT,
3677 cfg->secondaryIndent,
3678 *arg + 5);
3679 }
3680 }
3681
3682 else {
3683 /* insert/append */
3684 insertElement(line, *arg, i, cfg->cfi);
3685 usedElements =
3686 realloc(usedElements,
3687 line->numElements *
3688 sizeof(*usedElements));
3689 memmove(&usedElements[i + 1], &usedElements[i],
3690 line->numElements - i - 1);
3691 usedElements[i] = 1;
3692
3693 /* if we updated a root= here even though
3694 * there is a LT_ROOT available we need to
3695 * remove the LT_ROOT entry (this will happen
3696 * if we switch from a device to a label) */
3697 if (useRoot && !strncmp(*arg, "root=", 5)) {
3698 rootLine =
3699 getLineByType(LT_ROOT,
3700 entry->lines);
3701 if (rootLine)
3702 removeLine(entry, rootLine);
3703 }
3704 }
3705 }
3706
3707 free(usedElements);
3708
3709 for (arg = oldArgs; *arg; arg++) {
3710 for (i = firstElement; i < line->numElements; i++) {
3711 if (multibootArgs && cfg->cfi->mbConcatArgs &&
3712 !strcmp(line->elements[i].item, "--"))
3713 /* reached the end of hyper args, stop here */
3714 break;
3715 if (!argMatch(line->elements[i].item, *arg)) {
3716 removeElement(line, i);
3717 break;
3718 }
3719 }
3720 /* handle removing LT_ROOT line too */
3721 if (useRoot && !strncmp(*arg, "root=", 5)) {
3722 rootLine = getLineByType(LT_ROOT, entry->lines);
3723 if (rootLine)
3724 removeLine(entry, rootLine);
3725 }
3726 }
3727
3728 if (line->numElements == 1) {
3729 /* don't need the line at all (note it has to be a
3730 LT_KERNELARGS for this to happen */
3731 removeLine(entry, line);
3732 }
3733 }
3734
3735 free(newArgs);
3736 free(oldArgs);
3737
3738 return 0;
3739 }
3740
3741 int updateImage(struct grubConfig *cfg, const char *image,
3742 const char *prefix, const char *addArgs,
3743 const char *removeArgs,
3744 const char *addMBArgs, const char *removeMBArgs)
3745 {
3746 int rc = 0;
3747
3748 if (!image)
3749 return rc;
3750
3751 /* update the main args first... */
3752 if (addArgs || removeArgs)
3753 rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs,
3754 0);
3755 if (rc)
3756 return rc;
3757
3758 /* and now any multiboot args */
3759 if (addMBArgs || removeMBArgs)
3760 rc = updateActualImage(cfg, image, prefix, addMBArgs,
3761 removeMBArgs, 1);
3762 return rc;
3763 }
3764
3765 int addMBInitrd(struct grubConfig *cfg, const char *newMBKernel,
3766 const char *image, const char *prefix, const char *initrd,
3767 const char *title)
3768 {
3769 struct singleEntry *entry;
3770 struct singleLine *line, *kernelLine, *endLine = NULL;
3771 int index = 0;
3772
3773 if (!image)
3774 return 0;
3775
3776 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3777 kernelLine = getLineByType(LT_MBMODULE, entry->lines);
3778 if (!kernelLine)
3779 continue;
3780
3781 /* if title is supplied, the entry's title must match it. */
3782 if (title) {
3783 char *linetitle;
3784
3785 line =
3786 getLineByType(LT_TITLE | LT_MENUENTRY,
3787 entry->lines);
3788 if (!line)
3789 continue;
3790
3791 linetitle = extractTitle(cfg, line);
3792 if (!linetitle)
3793 continue;
3794 if (strcmp(title, linetitle)) {
3795 free(linetitle);
3796 continue;
3797 }
3798 free(linetitle);
3799 }
3800
3801 if (prefix) {
3802 int prefixLen = strlen(prefix);
3803 if (!strncmp(initrd, prefix, prefixLen))
3804 initrd += prefixLen;
3805 }
3806 endLine = getLineByType(LT_ENTRY_END, entry->lines);
3807 if (endLine)
3808 removeLine(entry, endLine);
3809 line =
3810 addLine(entry, cfg->cfi,
3811 preferredLineType(LT_MBMODULE, cfg->cfi),
3812 kernelLine->indent, initrd);
3813 if (!line)
3814 return 1;
3815 if (endLine) {
3816 line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3817 if (!line)
3818 return 1;
3819 }
3820
3821 break;
3822 }
3823
3824 return 0;
3825 }
3826
3827 int updateInitrd(struct grubConfig *cfg, const char *image,
3828 const char *prefix, const char *initrd, const char *title)
3829 {
3830 struct singleEntry *entry;
3831 struct singleLine *line, *kernelLine, *endLine = NULL;
3832 int index = 0;
3833
3834 if (!image)
3835 return 0;
3836
3837 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3838 kernelLine =
3839 getLineByType(LT_KERNEL | LT_KERNEL_EFI | LT_KERNEL_16,
3840 entry->lines);
3841 if (!kernelLine)
3842 continue;
3843
3844 /* if title is supplied, the entry's title must match it. */
3845 if (title) {
3846 char *linetitle;
3847
3848 line =
3849 getLineByType(LT_TITLE | LT_MENUENTRY,
3850 entry->lines);
3851 if (!line)
3852 continue;
3853
3854 linetitle = extractTitle(cfg, line);
3855 if (!linetitle)
3856 continue;
3857 if (strcmp(title, linetitle)) {
3858 free(linetitle);
3859 continue;
3860 }
3861 free(linetitle);
3862 }
3863
3864 line =
3865 getLineByType(LT_INITRD | LT_INITRD_EFI | LT_INITRD_16,
3866 entry->lines);
3867 if (line)
3868 removeLine(entry, line);
3869 if (prefix) {
3870 int prefixLen = strlen(prefix);
3871 if (!strncmp(initrd, prefix, prefixLen))
3872 initrd += prefixLen;
3873 }
3874 endLine = getLineByType(LT_ENTRY_END, entry->lines);
3875 if (endLine)
3876 removeLine(entry, endLine);
3877 enum lineType_e lt;
3878 switch (kernelLine->type) {
3879 case LT_KERNEL:
3880 lt = LT_INITRD;
3881 break;
3882 case LT_KERNEL_EFI:
3883 lt = LT_INITRD_EFI;
3884 break;
3885 case LT_KERNEL_16:
3886 lt = LT_INITRD_16;
3887 break;
3888 default:
3889 lt = preferredLineType(LT_INITRD, cfg->cfi);
3890 }
3891 line = addLine(entry, cfg->cfi, lt, kernelLine->indent, initrd);
3892 if (!line)
3893 return 1;
3894 if (endLine) {
3895 line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3896 if (!line)
3897 return 1;
3898 }
3899
3900 break;
3901 }
3902
3903 return 0;
3904 }
3905
3906 int checkDeviceBootloader(const char *device, const unsigned char *boot)
3907 {
3908 int fd;
3909 unsigned char bootSect[512];
3910 int offset;
3911
3912 fd = open(device, O_RDONLY);
3913 if (fd < 0) {
3914 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3915 device, strerror(errno));
3916 return 1;
3917 }
3918
3919 if (read(fd, bootSect, 512) != 512) {
3920 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3921 device, strerror(errno));
3922 return 1;
3923 }
3924 close(fd);
3925
3926 /* first three bytes should match, a jmp short should be in there */
3927 if (memcmp(boot, bootSect, 3))
3928 return 0;
3929
3930 if (boot[1] == JMP_SHORT_OPCODE) {
3931 offset = boot[2] + 2;
3932 } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3933 offset = (boot[3] << 8) + boot[2] + 2;
3934 } else if (boot[0] == JMP_SHORT_OPCODE) {
3935 offset = boot[1] + 2;
3936 /*
3937 * it looks like grub, when copying stage1 into the mbr,
3938 * patches stage1 right after the JMP location, replacing
3939 * other instructions such as JMPs for NOOPs. So, relax the
3940 * check a little bit by skipping those different bytes.
3941 */
3942 if ((bootSect[offset + 1] == NOOP_OPCODE)
3943 && (bootSect[offset + 2] == NOOP_OPCODE)) {
3944 offset = offset + 3;
3945 }
3946 } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3947 offset = (boot[2] << 8) + boot[1] + 2;
3948 } else {
3949 return 0;
3950 }
3951
3952 if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
3953 return 0;
3954
3955 return 2;
3956 }
3957
3958 int checkLiloOnRaid(char *mdDev, const unsigned char *boot)
3959 {
3960 int fd;
3961 char buf[65536];
3962 char *end;
3963 char *chptr;
3964 char *chptr2;
3965 int rc;
3966
3967 /* it's on raid; we need to parse /proc/mdstat and check all of the
3968 *raw* devices listed in there */
3969
3970 if (!strncmp(mdDev, "/dev/", 5))
3971 mdDev += 5;
3972
3973 if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
3974 fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
3975 strerror(errno));
3976 return 2;
3977 }
3978
3979 rc = read(fd, buf, sizeof(buf) - 1);
3980 if (rc < 0 || rc == (sizeof(buf) - 1)) {
3981 fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
3982 strerror(errno));
3983 close(fd);
3984 return 2;
3985 }
3986 close(fd);
3987 buf[rc] = '\0';
3988
3989 chptr = buf;
3990 while (*chptr) {
3991 end = strchr(chptr, '\n');
3992 if (!end)
3993 break;
3994 *end = '\0';
3995
3996 if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
3997 chptr[strlen(mdDev)] == ' ') {
3998
3999 /* found the device */
4000 while (*chptr && *chptr != ':')
4001 chptr++;
4002 chptr++;
4003 while (*chptr && isspace(*chptr))
4004 chptr++;
4005
4006 /* skip the "active" bit */
4007 while (*chptr && !isspace(*chptr))
4008 chptr++;
4009 while (*chptr && isspace(*chptr))
4010 chptr++;
4011
4012 /* skip the raid level */
4013 while (*chptr && !isspace(*chptr))
4014 chptr++;
4015 while (*chptr && isspace(*chptr))
4016 chptr++;
4017
4018 /* everything else is partition stuff */
4019 while (*chptr) {
4020 chptr2 = chptr;
4021 while (*chptr2 && *chptr2 != '[')
4022 chptr2++;
4023 if (!*chptr2)
4024 break;
4025
4026 /* yank off the numbers at the end */
4027 chptr2--;
4028 while (isdigit(*chptr2) && chptr2 > chptr)
4029 chptr2--;
4030 chptr2++;
4031 *chptr2 = '\0';
4032
4033 /* Better, now we need the /dev/ back. We're
4034 * done with everything before this point, so
4035 * we can just put the /dev/ part there.
4036 * There will always be room. */
4037 memcpy(chptr - 5, "/dev/", 5);
4038 rc = checkDeviceBootloader(chptr - 5, boot);
4039 if (rc != 2) {
4040 return rc;
4041 }
4042
4043 chptr = chptr2 + 1;
4044 /* skip the [11] bit */
4045 while (*chptr && !isspace(*chptr))
4046 chptr++;
4047 /* and move to the next one */
4048 while (*chptr && isspace(*chptr))
4049 chptr++;
4050 }
4051
4052 /* we're good to go */
4053 return 2;
4054 }
4055
4056 chptr = end + 1;
4057 }
4058
4059 fprintf(stderr,
4060 _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
4061 mdDev);
4062 return 0;
4063 }
4064
4065 int checkForLilo(struct grubConfig *config)
4066 {
4067 int fd;
4068 unsigned char boot[512];
4069 struct singleLine *line;
4070
4071 for (line = config->theLines; line; line = line->next)
4072 if (line->type == LT_BOOT)
4073 break;
4074
4075 if (!line) {
4076 fprintf(stderr,
4077 _
4078 ("grubby: no boot line found in lilo configuration\n"));
4079 return 1;
4080 }
4081
4082 if (line->numElements != 2)
4083 return 1;
4084
4085 fd = open("/boot/boot.b", O_RDONLY);
4086 if (fd < 0) {
4087 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
4088 "/boot/boot.b", strerror(errno));
4089 return 1;
4090 }
4091
4092 if (read(fd, boot, 512) != 512) {
4093 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4094 "/boot/boot.b", strerror(errno));
4095 return 1;
4096 }
4097 close(fd);
4098
4099 if (!strncmp("/dev/md", line->elements[1].item, 7))
4100 return checkLiloOnRaid(line->elements[1].item, boot);
4101
4102 return checkDeviceBootloader(line->elements[1].item, boot);
4103 }
4104
4105 int checkForGrub2(struct grubConfig *config)
4106 {
4107 if (!access("/etc/grub.d/", R_OK))
4108 return 2;
4109
4110 return 1;
4111 }
4112
4113 int checkForGrub(struct grubConfig *config)
4114 {
4115 int fd;
4116 unsigned char bootSect[512];
4117 char *boot;
4118 int onSuse = isSuseSystem();
4119
4120 if (onSuse) {
4121 if (parseSuseGrubConf(NULL, &boot))
4122 return 0;
4123 } else {
4124 if (parseSysconfigGrub(NULL, &boot))
4125 return 0;
4126 }
4127
4128 /* assume grub is not installed -- not an error condition */
4129 if (!boot)
4130 return 0;
4131
4132 fd = open("/boot/grub/stage1", O_RDONLY);
4133 if (fd < 0)
4134 /* this doesn't exist if grub hasn't been installed */
4135 return 0;
4136
4137 if (read(fd, bootSect, 512) != 512) {
4138 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4139 "/boot/grub/stage1", strerror(errno));
4140 close(fd);
4141 return 1;
4142 }
4143 close(fd);
4144
4145 /* The more elaborate checks do not work on SuSE. The checks done
4146 * seem to be reasonble (at least for now), so just return success
4147 */
4148 if (onSuse)
4149 return 2;
4150
4151 return checkDeviceBootloader(boot, bootSect);
4152 }
4153
4154 int checkForExtLinux(struct grubConfig *config)
4155 {
4156 int fd;
4157 unsigned char bootSect[512];
4158 char *boot;
4159 char executable[] = "/boot/extlinux/extlinux";
4160
4161 printf("entered: checkForExtLinux()\n");
4162
4163 if (parseSysconfigGrub(NULL, &boot))
4164 return 0;
4165
4166 /* assume grub is not installed -- not an error condition */
4167 if (!boot)
4168 return 0;
4169
4170 fd = open(executable, O_RDONLY);
4171 if (fd < 0)
4172 /* this doesn't exist if grub hasn't been installed */
4173 return 0;
4174
4175 if (read(fd, bootSect, 512) != 512) {
4176 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4177 executable, strerror(errno));
4178 return 1;
4179 }
4180 close(fd);
4181
4182 return checkDeviceBootloader(boot, bootSect);
4183 }
4184
4185 int checkForYaboot(struct grubConfig *config)
4186 {
4187 /*
4188 * This is a simplistic check that we consider good enough for own puporses
4189 *
4190 * If we were to properly check if yaboot is *installed* we'd need to:
4191 * 1) get the system boot device (LT_BOOT)
4192 * 2) considering it's a raw filesystem, check if the yaboot binary matches
4193 * the content on the boot device
4194 * 3) if not, copy the binary to a temporary file and run "addnote" on it
4195 * 4) check again if binary and boot device contents match
4196 */
4197 if (!access("/etc/yaboot.conf", R_OK))
4198 return 2;
4199
4200 return 1;
4201 }
4202
4203 int checkForElilo(struct grubConfig *config)
4204 {
4205 if (!access("/etc/elilo.conf", R_OK))
4206 return 2;
4207
4208 return 1;
4209 }
4210
4211 static char *getRootSpecifier(char *str)
4212 {
4213 char *idx, *rootspec = NULL;
4214
4215 if (*str == '(') {
4216 idx = rootspec = strdup(str);
4217 while (*idx && (*idx != ')') && (!isspace(*idx)))
4218 idx++;
4219 *(++idx) = '\0';
4220 }
4221 return rootspec;
4222 }
4223
4224 static char *getInitrdVal(struct grubConfig *config,
4225 const char *prefix, struct singleLine *tmplLine,
4226 const char *newKernelInitrd,
4227 const char **extraInitrds, int extraInitrdCount)
4228 {
4229 char *initrdVal, *end;
4230 int i;
4231 size_t totalSize;
4232 size_t prefixLen;
4233 char separatorChar;
4234
4235 prefixLen = strlen(prefix);
4236 totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */ ;
4237
4238 for (i = 0; i < extraInitrdCount; i++) {
4239 totalSize += sizeof(separatorChar);
4240 totalSize += strlen(extraInitrds[i]) - prefixLen;
4241 }
4242
4243 initrdVal = end = malloc(totalSize);
4244
4245 end = stpcpy(end, newKernelInitrd + prefixLen);
4246
4247 separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
4248 for (i = 0; i < extraInitrdCount; i++) {
4249 const char *extraInitrd;
4250 int j;
4251
4252 extraInitrd = extraInitrds[i] + prefixLen;
4253 /* Don't add entries that are already there */
4254 if (tmplLine != NULL) {
4255 for (j = 2; j < tmplLine->numElements; j++)
4256 if (strcmp
4257 (extraInitrd,
4258 tmplLine->elements[j].item) == 0)
4259 break;
4260
4261 if (j != tmplLine->numElements)
4262 continue;
4263 }
4264
4265 *end++ = separatorChar;
4266 end = stpcpy(end, extraInitrd);
4267 }
4268
4269 return initrdVal;
4270 }
4271
4272 int addNewKernel(struct grubConfig *config, struct singleEntry *template,
4273 const char *prefix,
4274 const char *newKernelPath, const char *newKernelTitle,
4275 const char *newKernelArgs, const char *newKernelInitrd,
4276 const char **extraInitrds, int extraInitrdCount,
4277 const char *newMBKernel, const char *newMBKernelArgs,
4278 const char *newDevTreePath, int newIndex)
4279 {
4280 struct singleEntry *new, *entry, *prev = NULL;
4281 struct singleLine *newLine = NULL, *tmplLine = NULL, *masterLine = NULL;
4282 int needs;
4283 char *indexs;
4284 char *chptr;
4285 int rc;
4286
4287 if (!newKernelPath)
4288 return 0;
4289
4290 rc = asprintf(&indexs, "%d", newIndex);
4291 if (rc < 0)
4292 return 1;
4293
4294 /* if the newKernelTitle is too long silently munge it into something
4295 * we can live with. truncating is first check, then we'll just mess with
4296 * it until it looks better */
4297 if (config->cfi->maxTitleLength &&
4298 (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
4299 char *buf = alloca(config->cfi->maxTitleLength + 7);
4300 char *numBuf = alloca(config->cfi->maxTitleLength + 1);
4301 int i = 1;
4302
4303 sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength,
4304 newKernelTitle);
4305 while (findEntryByPath(config, buf, NULL, NULL)) {
4306 sprintf(numBuf, "%d", i++);
4307 strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
4308 }
4309
4310 newKernelTitle = buf + 6;
4311 }
4312
4313 new = malloc(sizeof(*new));
4314 new->skip = 0;
4315 new->multiboot = 0;
4316 new->lines = NULL;
4317 entry = config->entries;
4318 for (unsigned int i = 0; i < newIndex; i++) {
4319 if (!entry)
4320 break;
4321 prev = entry;
4322 entry = entry->next;
4323 }
4324 new->next = entry;
4325
4326 if (prev)
4327 prev->next = new;
4328 else
4329 config->entries = new;
4330
4331 /* copy/update from the template */
4332 needs = NEED_KERNEL | NEED_TITLE;
4333 if (newKernelInitrd)
4334 needs |= NEED_INITRD;
4335 if (newMBKernel) {
4336 needs |= NEED_MB;
4337 new->multiboot = 1;
4338 }
4339 if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))
4340 needs |= NEED_DEVTREE;
4341
4342 if (template) {
4343 for (masterLine = template->lines;
4344 masterLine && (tmplLine = lineDup(masterLine));
4345 lineFree(tmplLine), masterLine = masterLine->next) {
4346 dbgPrintf("addNewKernel processing %d\n",
4347 tmplLine->type);
4348
4349 /* skip comments */
4350 chptr = tmplLine->indent;
4351 while (*chptr && isspace(*chptr))
4352 chptr++;
4353 if (*chptr == '#')
4354 continue;
4355
4356 if (iskernel(tmplLine->type)
4357 && tmplLine->numElements >= 2) {
4358 if (!template->multiboot && (needs & NEED_MB)) {
4359 /* it's not a multiboot template and
4360 * this is the kernel line. Try to
4361 * be intelligent about inserting the
4362 * hypervisor at the same time.
4363 */
4364 if (config->cfi->mbHyperFirst) {
4365 /* insert the hypervisor first */
4366 newLine =
4367 addLine(new, config->cfi,
4368 LT_HYPER,
4369 tmplLine->indent,
4370 newMBKernel +
4371 strlen(prefix));
4372 /* set up for adding the
4373 * kernel line */
4374 free(tmplLine->indent);
4375 tmplLine->indent =
4376 strdup(config->
4377 secondaryIndent);
4378 needs &= ~NEED_MB;
4379 }
4380 if (needs & NEED_KERNEL) {
4381 /* use addLineTmpl to
4382 * preserve line elements,
4383 * otherwise we could just
4384 * call addLine.
4385 * Unfortunately this means
4386 * making some changes to the
4387 * template such as the
4388 * indent change above and
4389 * the type change below.
4390 */
4391 struct keywordTypes *mbm_kw =
4392 getKeywordByType
4393 (LT_MBMODULE, config->cfi);
4394 if (mbm_kw) {
4395 tmplLine->type =
4396 LT_MBMODULE;
4397 free(tmplLine->
4398 elements[0].item);
4399 tmplLine->elements[0].
4400 item =
4401 strdup(mbm_kw->key);
4402 }
4403 newLine =
4404 addLineTmpl(new, tmplLine,
4405 newLine,
4406 newKernelPath +
4407 strlen(prefix),
4408 config->cfi);
4409 needs &= ~NEED_KERNEL;
4410 }
4411 if (needs & NEED_MB) { /* !mbHyperFirst */
4412 newLine =
4413 addLine(new, config->cfi,
4414 LT_HYPER,
4415 config->
4416 secondaryIndent,
4417 newMBKernel +
4418 strlen(prefix));
4419 needs &= ~NEED_MB;
4420 }
4421 } else if (needs & NEED_KERNEL) {
4422 newLine =
4423 addLineTmpl(new, tmplLine, newLine,
4424 newKernelPath +
4425 strlen(prefix),
4426 config->cfi);
4427 needs &= ~NEED_KERNEL;
4428 }
4429
4430 } else if (tmplLine->type == LT_HYPER &&
4431 tmplLine->numElements >= 2) {
4432 if (needs & NEED_MB) {
4433 newLine =
4434 addLineTmpl(new, tmplLine, newLine,
4435 newMBKernel +
4436 strlen(prefix),
4437 config->cfi);
4438 needs &= ~NEED_MB;
4439 }
4440
4441 } else if (tmplLine->type == LT_MBMODULE &&
4442 tmplLine->numElements >= 2) {
4443 if (new->multiboot) {
4444 if (needs & NEED_KERNEL) {
4445 newLine =
4446 addLineTmpl(new, tmplLine,
4447 newLine,
4448 newKernelPath +
4449 strlen(prefix),
4450 config->cfi);
4451 needs &= ~NEED_KERNEL;
4452 } else if (config->cfi->mbInitRdIsModule
4453 && (needs & NEED_INITRD)) {
4454 char *initrdVal;
4455 initrdVal =
4456 getInitrdVal(config, prefix,
4457 tmplLine,
4458 newKernelInitrd,
4459 extraInitrds,
4460 extraInitrdCount);
4461 newLine =
4462 addLineTmpl(new, tmplLine,
4463 newLine,
4464 initrdVal,
4465 config->cfi);
4466 free(initrdVal);
4467 needs &= ~NEED_INITRD;
4468 }
4469 } else if (needs & NEED_KERNEL) {
4470 /* template is multi but new is not,
4471 * insert the kernel in the first
4472 * module slot
4473 */
4474 tmplLine->type =
4475 preferredLineType(LT_KERNEL,
4476 config->cfi);
4477 free(tmplLine->elements[0].item);
4478 tmplLine->elements[0].item =
4479 strdup(getKeywordByType
4480 (tmplLine->type,
4481 config->cfi)->key);
4482 newLine =
4483 addLineTmpl(new, tmplLine, newLine,
4484 newKernelPath +
4485 strlen(prefix),
4486 config->cfi);
4487 needs &= ~NEED_KERNEL;
4488 } else if (needs & NEED_INITRD) {
4489 char *initrdVal;
4490 /* template is multi but new is not,
4491 * insert the initrd in the second
4492 * module slot
4493 */
4494 tmplLine->type =
4495 preferredLineType(LT_INITRD,
4496 config->cfi);
4497 free(tmplLine->elements[0].item);
4498 tmplLine->elements[0].item =
4499 strdup(getKeywordByType
4500 (tmplLine->type,
4501 config->cfi)->key);
4502 initrdVal =
4503 getInitrdVal(config, prefix,
4504 tmplLine,
4505 newKernelInitrd,
4506 extraInitrds,
4507 extraInitrdCount);
4508 newLine =
4509 addLineTmpl(new, tmplLine, newLine,
4510 initrdVal, config->cfi);
4511 free(initrdVal);
4512 needs &= ~NEED_INITRD;
4513 }
4514
4515 } else if (isinitrd(tmplLine->type)
4516 && tmplLine->numElements >= 2) {
4517 if (needs & NEED_INITRD && new->multiboot
4518 && !template->multiboot
4519 && config->cfi->mbInitRdIsModule) {
4520 /* make sure we don't insert the
4521 * module initrd before the module
4522 * kernel... if we don't do it here,
4523 * it will be inserted following the
4524 * template.
4525 */
4526 if (!needs & NEED_KERNEL) {
4527 char *initrdVal;
4528
4529 initrdVal =
4530 getInitrdVal(config, prefix,
4531 tmplLine,
4532 newKernelInitrd,
4533 extraInitrds,
4534 extraInitrdCount);
4535 newLine =
4536 addLine(new, config->cfi,
4537 LT_MBMODULE,
4538 config->
4539 secondaryIndent,
4540 initrdVal);
4541 free(initrdVal);
4542 needs &= ~NEED_INITRD;
4543 }
4544 } else if (needs & NEED_INITRD) {
4545 char *initrdVal;
4546 initrdVal =
4547 getInitrdVal(config, prefix,
4548 tmplLine,
4549 newKernelInitrd,
4550 extraInitrds,
4551 extraInitrdCount);
4552 newLine =
4553 addLineTmpl(new, tmplLine, newLine,
4554 initrdVal, config->cfi);
4555 free(initrdVal);
4556 needs &= ~NEED_INITRD;
4557 }
4558
4559 } else if (tmplLine->type == LT_MENUENTRY &&
4560 (needs & NEED_TITLE)) {
4561 requote(tmplLine, config->cfi);
4562 char *nkt = malloc(strlen(newKernelTitle) + 3);
4563 strcpy(nkt, "'");
4564 strcat(nkt, newKernelTitle);
4565 strcat(nkt, "'");
4566 newLine =
4567 addLineTmpl(new, tmplLine, newLine, nkt,
4568 config->cfi);
4569 free(nkt);
4570 needs &= ~NEED_TITLE;
4571 } else if (tmplLine->type == LT_TITLE &&
4572 (needs & NEED_TITLE)) {
4573 if (tmplLine->numElements >= 2) {
4574 newLine =
4575 addLineTmpl(new, tmplLine, newLine,
4576 newKernelTitle,
4577 config->cfi);
4578 needs &= ~NEED_TITLE;
4579 } else if (tmplLine->numElements == 1 &&
4580 config->cfi->titleBracketed) {
4581 /* addLineTmpl doesn't handle
4582 * titleBracketed */
4583 newLine =
4584 addLine(new, config->cfi, LT_TITLE,
4585 tmplLine->indent,
4586 newKernelTitle);
4587 needs &= ~NEED_TITLE;
4588 }
4589 } else if (tmplLine->type == LT_ECHO) {
4590 requote(tmplLine, config->cfi);
4591 static const char *prefix = "'Loading ";
4592 if (tmplLine->numElements > 1 &&
4593 strstr(tmplLine->elements[1].item, prefix)
4594 && masterLine->next
4595 && iskernel(masterLine->next->type)) {
4596 char *newTitle =
4597 malloc(strlen(prefix) +
4598 strlen(newKernelTitle) + 2);
4599
4600 strcpy(newTitle, prefix);
4601 strcat(newTitle, newKernelTitle);
4602 strcat(newTitle, "'");
4603 newLine =
4604 addLine(new, config->cfi, LT_ECHO,
4605 tmplLine->indent, newTitle);
4606 free(newTitle);
4607 } else {
4608 /* pass through other lines from the
4609 * template */
4610 newLine =
4611 addLineTmpl(new, tmplLine, newLine,
4612 NULL, config->cfi);
4613 }
4614 } else if (tmplLine->type == LT_DEVTREE &&
4615 tmplLine->numElements == 2
4616 && newDevTreePath) {
4617 newLine =
4618 addLineTmpl(new, tmplLine, newLine,
4619 newDevTreePath + strlen(prefix),
4620 config->cfi);
4621 needs &= ~NEED_DEVTREE;
4622 } else if (tmplLine->type == LT_ENTRY_END
4623 && needs & NEED_DEVTREE) {
4624 const char *ndtp = newDevTreePath;
4625 if (!strncmp
4626 (newDevTreePath, prefix, strlen(prefix)))
4627 ndtp += strlen(prefix);
4628 newLine = addLine(new, config->cfi, LT_DEVTREE,
4629 config->secondaryIndent,
4630 ndtp);
4631 needs &= ~NEED_DEVTREE;
4632 newLine =
4633 addLineTmpl(new, tmplLine, newLine, NULL,
4634 config->cfi);
4635 } else {
4636 /* pass through other lines from the template */
4637 newLine =
4638 addLineTmpl(new, tmplLine, newLine, NULL,
4639 config->cfi);
4640 }
4641 }
4642
4643 } else {
4644 /* don't have a template, so start the entry with the
4645 * appropriate starting line
4646 */
4647 switch (config->cfi->entryStart) {
4648 case LT_KERNEL:
4649 case LT_KERNEL_EFI:
4650 case LT_KERNEL_16:
4651 if (new->multiboot && config->cfi->mbHyperFirst) {
4652 /* fall through to LT_HYPER */
4653 } else {
4654 newLine = addLine(new, config->cfi,
4655 preferredLineType(LT_KERNEL,
4656 config->
4657 cfi),
4658 config->primaryIndent,
4659 newKernelPath +
4660 strlen(prefix));
4661 needs &= ~NEED_KERNEL;
4662 break;
4663 }
4664
4665 case LT_HYPER:
4666 newLine = addLine(new, config->cfi, LT_HYPER,
4667 config->primaryIndent,
4668 newMBKernel + strlen(prefix));
4669 needs &= ~NEED_MB;
4670 break;
4671
4672 case LT_MENUENTRY:{
4673 char *nkt = malloc(strlen(newKernelTitle) + 3);
4674 strcpy(nkt, "'");
4675 strcat(nkt, newKernelTitle);
4676 strcat(nkt, "'");
4677 newLine =
4678 addLine(new, config->cfi, LT_MENUENTRY,
4679 config->primaryIndent, nkt);
4680 free(nkt);
4681 needs &= ~NEED_TITLE;
4682 needs |= NEED_END;
4683 break;
4684 }
4685 case LT_TITLE:
4686 if (useextlinuxmenu != 0) { // We just need useextlinuxmenu to not be zero (set above)
4687 char *templabel;
4688 int x = 0, y = 0;
4689
4690 templabel = strdup(newKernelTitle);
4691 while (templabel[x]) {
4692 if (templabel[x] == ' ') {
4693 y = x;
4694 while (templabel[y]) {
4695 templabel[y] =
4696 templabel[y + 1];
4697 y++;
4698 }
4699 }
4700 x++;
4701 }
4702 newLine = addLine(new, config->cfi, LT_TITLE,
4703 config->primaryIndent,
4704 templabel);
4705 free(templabel);
4706 } else {
4707 newLine = addLine(new, config->cfi, LT_TITLE,
4708 config->primaryIndent,
4709 newKernelTitle);
4710 }
4711 needs &= ~NEED_TITLE;
4712 break;
4713
4714 default:
4715 abort();
4716 }
4717 }
4718
4719 struct singleLine *endLine = NULL;
4720 endLine = getLineByType(LT_ENTRY_END, new->lines);
4721 if (endLine) {
4722 removeLine(new, endLine);
4723 needs |= NEED_END;
4724 }
4725
4726 /* add the remainder of the lines, i.e. those that either
4727 * weren't present in the template, or in the case of no template,
4728 * all the lines following the entryStart.
4729 */
4730 if (needs & NEED_TITLE) {
4731 newLine = addLine(new, config->cfi, LT_TITLE,
4732 config->secondaryIndent, newKernelTitle);
4733 needs &= ~NEED_TITLE;
4734 }
4735 if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
4736 newLine = addLine(new, config->cfi, LT_HYPER,
4737 config->secondaryIndent,
4738 newMBKernel + strlen(prefix));
4739 needs &= ~NEED_MB;
4740 }
4741 if (needs & NEED_KERNEL) {
4742 newLine = addLine(new, config->cfi,
4743 (new->multiboot
4744 && getKeywordByType(LT_MBMODULE,
4745 config->cfi))
4746 ? LT_MBMODULE : preferredLineType(LT_KERNEL,
4747 config->
4748 cfi),
4749 config->secondaryIndent,
4750 newKernelPath + strlen(prefix));
4751 needs &= ~NEED_KERNEL;
4752 }
4753 if (needs & NEED_MB) {
4754 newLine = addLine(new, config->cfi, LT_HYPER,
4755 config->secondaryIndent,
4756 newMBKernel + strlen(prefix));
4757 needs &= ~NEED_MB;
4758 }
4759 if (needs & NEED_INITRD) {
4760 char *initrdVal;
4761 initrdVal =
4762 getInitrdVal(config, prefix, NULL, newKernelInitrd,
4763 extraInitrds, extraInitrdCount);
4764 newLine =
4765 addLine(new, config->cfi,
4766 (new->multiboot
4767 && getKeywordByType(LT_MBMODULE, config->cfi))
4768 ? LT_MBMODULE : preferredLineType(LT_INITRD,
4769 config->cfi),
4770 config->secondaryIndent, initrdVal);
4771 free(initrdVal);
4772 needs &= ~NEED_INITRD;
4773 }
4774 if (needs & NEED_DEVTREE) {
4775 newLine = addLine(new, config->cfi, LT_DEVTREE,
4776 config->secondaryIndent, newDevTreePath);
4777 needs &= ~NEED_DEVTREE;
4778 }
4779
4780 /* NEEDS_END must be last on bootloaders that need it... */
4781 if (needs & NEED_END) {
4782 newLine = addLine(new, config->cfi, LT_ENTRY_END,
4783 config->secondaryIndent, NULL);
4784 needs &= ~NEED_END;
4785 }
4786
4787 if (needs) {
4788 printf(_("grubby: needs=%d, aborting\n"), needs);
4789 abort();
4790 }
4791
4792 if (updateImage(config, indexs, prefix, newKernelArgs, NULL,
4793 newMBKernelArgs, NULL)) {
4794 config->isModified = 1;
4795 return 1;
4796 }
4797
4798 return 0;
4799 }
4800
4801 int main(int argc, const char **argv)
4802 {
4803 poptContext optCon;
4804 const char *grubConfig = NULL;
4805 char *outputFile = NULL;
4806 int arg = 0;
4807 int flags = 0;
4808 int badImageOkay = 0;
4809 int configureGrub2 = 0;
4810 int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4811 int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4812 int configureExtLinux = 0;
4813 int bootloaderProbe = 0;
4814 int extraInitrdCount = 0;
4815 char *updateKernelPath = NULL;
4816 char *newKernelPath = NULL;
4817 char *removeKernelPath = NULL;
4818 char *newKernelArgs = NULL;
4819 char *newKernelInitrd = NULL;
4820 char *newKernelTitle = NULL;
4821 char *newDevTreePath = NULL;
4822 char *newMBKernel = NULL;
4823 char *newMBKernelArgs = NULL;
4824 int newIndex = 0;
4825 char *removeMBKernelArgs = NULL;
4826 char *removeMBKernel = NULL;
4827 char *bootPrefix = NULL;
4828 char *defaultKernel = NULL;
4829 char *removeArgs = NULL;
4830 char *kernelInfo = NULL;
4831 char *extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4832 char *envPath = NULL;
4833 const char *chptr = NULL;
4834 struct configFileInfo *cfi = NULL;
4835 struct grubConfig *config;
4836 struct singleEntry *template = NULL;
4837 int copyDefault = 0, makeDefault = 0;
4838 int displayDefault = 0;
4839 int displayDefaultIndex = 0;
4840 int displayDefaultTitle = 0;
4841 int defaultIndex = -1;
4842 struct poptOption options[] = {
4843 {"add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4844 _("add an entry for the specified kernel"), _("kernel-path")},
4845 {"add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
4846 _("add an entry for the specified multiboot kernel"), NULL},
4847 {"args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
4848 _("default arguments for the new kernel or new arguments for "
4849 "kernel being updated"), _("args")},
4850 {"mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
4851 _("default arguments for the new multiboot kernel or "
4852 "new arguments for multiboot kernel being updated"), NULL},
4853 {"bad-image-okay", 0, 0, &badImageOkay, 0,
4854 _
4855 ("don't sanity check images in boot entries (for testing only)"),
4856 NULL},
4857 {"boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4858 _
4859 ("filesystem which contains /boot directory (for testing only)"),
4860 _("bootfs")},
4861 #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4862 {"bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4863 _("check which bootloader is installed on boot sector")},
4864 #endif
4865 {"config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4866 _("path to grub config file to update (\"-\" for stdin)"),
4867 _("path")},
4868 {"copy-default", 0, 0, &copyDefault, 0,
4869 _("use the default boot entry as a template for the new entry "
4870 "being added; if the default is not a linux image, or if "
4871 "the kernel referenced by the default image does not exist, "
4872 "the first linux entry whose kernel does exist is used as the "
4873 "template"), NULL},
4874 {"debug", 0, 0, &debug, 0,
4875 _("print debugging information for failures")},
4876 {"default-kernel", 0, 0, &displayDefault, 0,
4877 _("display the path of the default kernel")},
4878 {"default-index", 0, 0, &displayDefaultIndex, 0,
4879 _("display the index of the default kernel")},
4880 {"default-title", 0, 0, &displayDefaultTitle, 0,
4881 _("display the title of the default kernel")},
4882 {"devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4883 _("device tree file for new stanza"), _("dtb-path")},
4884 {"devtreedir", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4885 _("device tree directory for new stanza"), _("dtb-path")},
4886 {"elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
4887 _("configure elilo bootloader")},
4888 {"efi", 0, POPT_ARG_NONE, &isEfi, 0,
4889 _("force grub2 stanzas to use efi")},
4890 {"env", 0, POPT_ARG_STRING, &envPath, 0,
4891 _("path for environment data"),
4892 _("path")},
4893 {"extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
4894 _("configure extlinux bootloader (from syslinux)")},
4895 {"grub", 0, POPT_ARG_NONE, &configureGrub, 0,
4896 _("configure grub bootloader")},
4897 {"grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
4898 _("configure grub2 bootloader")},
4899 {"info", 0, POPT_ARG_STRING, &kernelInfo, 0,
4900 _("display boot information for specified kernel"),
4901 _("kernel-path")},
4902 {"initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
4903 _("initrd image for the new kernel"), _("initrd-path")},
4904 {"extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
4905 _
4906 ("auxiliary initrd image for things other than the new kernel"),
4907 _("initrd-path")},
4908 {"lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
4909 _("configure lilo bootloader")},
4910 {"make-default", 0, 0, &makeDefault, 0,
4911 _("make the newly added entry the default boot entry"), NULL},
4912 {"output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
4913 _("path to output updated config file (\"-\" for stdout)"),
4914 _("path")},
4915 {"remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
4916 _("remove kernel arguments"), NULL},
4917 {"remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
4918 _("remove multiboot kernel arguments"), NULL},
4919 {"remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
4920 _("remove all entries for the specified kernel"),
4921 _("kernel-path")},
4922 {"remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
4923 _("remove all entries for the specified multiboot kernel"),
4924 NULL},
4925 {"set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
4926 _("make the first entry referencing the specified kernel "
4927 "the default"), _("kernel-path")},
4928 {"set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
4929 _("make the given entry index the default entry"),
4930 _("entry-index")},
4931 {"set-index", 0, POPT_ARG_INT, &newIndex, 0,
4932 _("use the given index when creating a new entry"),
4933 _("entry-index")},
4934 {"silo", 0, POPT_ARG_NONE, &configureSilo, 0,
4935 _("configure silo bootloader")},
4936 {"title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
4937 _("title to use for the new kernel entry"), _("entry-title")},
4938 {"update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
4939 _("updated information for the specified kernel"),
4940 _("kernel-path")},
4941 {"version", 'v', 0, NULL, 'v',
4942 _("print the version of this program and exit"), NULL},
4943 {"yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
4944 _("configure yaboot bootloader")},
4945 {"zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
4946 _("configure zipl bootloader")},
4947 POPT_AUTOHELP {0, 0, 0, 0, 0}
4948 };
4949
4950 useextlinuxmenu = 0;
4951
4952 int i = 0;
4953 for (int j = 1; j < argc; j++)
4954 i += strlen(argv[j]) + 1;
4955 saved_command_line = malloc(i);
4956 if (!saved_command_line) {
4957 fprintf(stderr, "grubby: %m\n");
4958 exit(1);
4959 }
4960 saved_command_line[0] = '\0';
4961 for (int j = 1; j < argc; j++) {
4962 strcat(saved_command_line, argv[j]);
4963 strncat(saved_command_line, j == argc - 1 ? "" : " ", 1);
4964 }
4965
4966 optCon = poptGetContext("grubby", argc, argv, options, 0);
4967 poptReadDefaultConfig(optCon, 1);
4968
4969 while ((arg = poptGetNextOpt(optCon)) >= 0) {
4970 switch (arg) {
4971 case 'v':
4972 printf("grubby version %s\n", VERSION);
4973 exit(0);
4974 break;
4975 case 'i':
4976 if (extraInitrdCount < MAX_EXTRA_INITRDS) {
4977 extraInitrds[extraInitrdCount++] =
4978 strdup(poptGetOptArg(optCon));
4979 } else {
4980 fprintf(stderr,
4981 _
4982 ("grubby: extra initrd maximum is %d\n"),
4983 extraInitrdCount);
4984 return 1;
4985 }
4986 break;
4987 }
4988 }
4989
4990 if (arg < -1) {
4991 fprintf(stderr, _("grubby: bad argument %s: %s\n"),
4992 poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
4993 poptStrerror(arg));
4994 return 1;
4995 }
4996
4997 if ((chptr = poptGetArg(optCon))) {
4998 fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
4999 return 1;
5000 }
5001
5002 if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
5003 configureYaboot + configureSilo + configureZipl +
5004 configureExtLinux) > 1) {
5005 fprintf(stderr,
5006 _("grubby: cannot specify multiple bootloaders\n"));
5007 return 1;
5008 } else if (bootloaderProbe && grubConfig) {
5009 fprintf(stderr,
5010 _
5011 ("grubby: cannot specify config file with --bootloader-probe\n"));
5012 return 1;
5013 } else if (configureGrub2) {
5014 cfi = &grub2ConfigType;
5015 if (envPath)
5016 cfi->envFile = envPath;
5017 } else if (configureLilo) {
5018 cfi = &liloConfigType;
5019 } else if (configureGrub) {
5020 cfi = &grubConfigType;
5021 } else if (configureELilo) {
5022 cfi = &eliloConfigType;
5023 } else if (configureYaboot) {
5024 cfi = &yabootConfigType;
5025 } else if (configureSilo) {
5026 cfi = &siloConfigType;
5027 } else if (configureZipl) {
5028 cfi = &ziplConfigType;
5029 } else if (configureExtLinux) {
5030 cfi = &extlinuxConfigType;
5031 useextlinuxmenu = 1;
5032 }
5033
5034 if (!cfi) {
5035 if (grub2FindConfig(&grub2ConfigType)) {
5036 cfi = &grub2ConfigType;
5037 if (envPath)
5038 cfi->envFile = envPath;
5039 } else
5040 #ifdef __ia64__
5041 cfi = &eliloConfigType;
5042 #elif __powerpc__
5043 cfi = &yabootConfigType;
5044 #elif __sparc__
5045 cfi = &siloConfigType;
5046 #elif __s390__
5047 cfi = &ziplConfigType;
5048 #elif __s390x__
5049 cfi = &ziplConfigtype;
5050 #else
5051 cfi = &grubConfigType;
5052 #endif
5053 }
5054
5055 if (!grubConfig) {
5056 if (cfi->findConfig)
5057 grubConfig = cfi->findConfig(cfi);
5058 if (!grubConfig)
5059 grubConfig = cfi->defaultConfig;
5060 }
5061
5062 if (bootloaderProbe && (displayDefault || kernelInfo ||
5063 newKernelPath || removeKernelPath || makeDefault
5064 || defaultKernel || displayDefaultIndex
5065 || displayDefaultTitle
5066 || (defaultIndex >= 0))) {
5067 fprintf(stderr,
5068 _("grubby: --bootloader-probe may not be used with "
5069 "specified option"));
5070 return 1;
5071 }
5072
5073 if ((displayDefault || kernelInfo) && (newKernelPath ||
5074 removeKernelPath)) {
5075 fprintf(stderr, _("grubby: --default-kernel and --info may not "
5076 "be used when adding or removing kernels\n"));
5077 return 1;
5078 }
5079
5080 if (newKernelPath && !newKernelTitle) {
5081 fprintf(stderr, _("grubby: kernel title must be specified\n"));
5082 return 1;
5083 } else if (!newKernelPath && (copyDefault ||
5084 (newKernelInitrd && !updateKernelPath) ||
5085 makeDefault || extraInitrdCount > 0)) {
5086 fprintf(stderr, _("grubby: kernel path expected\n"));
5087 return 1;
5088 }
5089
5090 if (newKernelPath && updateKernelPath) {
5091 fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
5092 "not be used together"));
5093 return 1;
5094 }
5095
5096 if (makeDefault && defaultKernel) {
5097 fprintf(stderr, _("grubby: --make-default and --default-kernel "
5098 "may not be used together\n"));
5099 return 1;
5100 } else if (defaultKernel && removeKernelPath &&
5101 !strcmp(defaultKernel, removeKernelPath)) {
5102 fprintf(stderr,
5103 _("grubby: cannot make removed kernel the default\n"));
5104 return 1;
5105 } else if (defaultKernel && newKernelPath &&
5106 !strcmp(defaultKernel, newKernelPath)) {
5107 makeDefault = 1;
5108 defaultKernel = NULL;
5109 } else if (defaultKernel && (defaultIndex >= 0)) {
5110 fprintf(stderr,
5111 _("grubby: --set-default and --set-default-index "
5112 "may not be used together\n"));
5113 return 1;
5114 }
5115
5116 if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
5117 fprintf(stderr,
5118 _("grubby: output file must be specified if stdin "
5119 "is used\n"));
5120 return 1;
5121 }
5122
5123 if (!removeKernelPath && !newKernelPath && !displayDefault
5124 && !defaultKernel && !kernelInfo && !bootloaderProbe
5125 && !updateKernelPath && !removeMBKernel && !displayDefaultIndex
5126 && !displayDefaultTitle && (defaultIndex == -1)) {
5127 fprintf(stderr, _("grubby: no action specified\n"));
5128 return 1;
5129 }
5130
5131 flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
5132
5133 if (cfi->needsBootPrefix) {
5134 if (!bootPrefix) {
5135 bootPrefix = findBootPrefix();
5136 if (!bootPrefix)
5137 return 1;
5138 } else {
5139 /* this shouldn't end with a / */
5140 if (bootPrefix[strlen(bootPrefix) - 1] == '/')
5141 bootPrefix[strlen(bootPrefix) - 1] = '\0';
5142 }
5143 } else {
5144 bootPrefix = "";
5145 }
5146
5147 if (!cfi->mbAllowExtraInitRds && extraInitrdCount > 0) {
5148 fprintf(stderr,
5149 _("grubby: %s doesn't allow multiple initrds\n"),
5150 cfi->defaultConfig);
5151 return 1;
5152 }
5153
5154 if (bootloaderProbe) {
5155 int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
5156 struct grubConfig *lconfig, *gconfig, *yconfig, *econfig;
5157
5158 const char *grub2config = grub2FindConfig(&grub2ConfigType);
5159 if (grub2config) {
5160 gconfig = readConfig(grub2config, &grub2ConfigType);
5161 if (!gconfig)
5162 gr2c = 1;
5163 else
5164 gr2c = checkForGrub2(gconfig);
5165 }
5166
5167 const char *grubconfig = grubFindConfig(&grubConfigType);
5168 if (!access(grubconfig, F_OK)) {
5169 gconfig = readConfig(grubconfig, &grubConfigType);
5170 if (!gconfig)
5171 grc = 1;
5172 else
5173 grc = checkForGrub(gconfig);
5174 }
5175
5176 if (!access(liloConfigType.defaultConfig, F_OK)) {
5177 lconfig =
5178 readConfig(liloConfigType.defaultConfig,
5179 &liloConfigType);
5180 if (!lconfig)
5181 lrc = 1;
5182 else
5183 lrc = checkForLilo(lconfig);
5184 }
5185
5186 if (!access(eliloConfigType.defaultConfig, F_OK)) {
5187 econfig = readConfig(eliloConfigType.defaultConfig,
5188 &eliloConfigType);
5189 if (!econfig)
5190 erc = 1;
5191 else
5192 erc = checkForElilo(econfig);
5193 }
5194
5195 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
5196 lconfig =
5197 readConfig(extlinuxConfigType.defaultConfig,
5198 &extlinuxConfigType);
5199 if (!lconfig)
5200 extrc = 1;
5201 else
5202 extrc = checkForExtLinux(lconfig);
5203 }
5204
5205 if (!access(yabootConfigType.defaultConfig, F_OK)) {
5206 yconfig = readConfig(yabootConfigType.defaultConfig,
5207 &yabootConfigType);
5208 if (!yconfig)
5209 yrc = 1;
5210 else
5211 yrc = checkForYaboot(yconfig);
5212 }
5213
5214 if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1
5215 || erc == 1)
5216 return 1;
5217
5218 if (lrc == 2)
5219 printf("lilo\n");
5220 if (gr2c == 2)
5221 printf("grub2\n");
5222 if (grc == 2)
5223 printf("grub\n");
5224 if (extrc == 2)
5225 printf("extlinux\n");
5226 if (yrc == 2)
5227 printf("yaboot\n");
5228 if (erc == 2)
5229 printf("elilo\n");
5230
5231 return 0;
5232 }
5233
5234 if (grubConfig == NULL) {
5235 printf("Could not find bootloader configuration file.\n");
5236 exit(1);
5237 }
5238
5239 config = readConfig(grubConfig, cfi);
5240 if (!config)
5241 return 1;
5242
5243 if (displayDefault) {
5244 struct singleLine *line;
5245 struct singleEntry *entry;
5246 char *rootspec;
5247
5248 if (config->defaultImage == NO_DEFAULT_ENTRY)
5249 return 0;
5250 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5251 cfi->defaultIsSaved)
5252 config->defaultImage = FIRST_ENTRY_INDEX;
5253 entry = findEntryByIndex(config, config->defaultImage);
5254 if (!entry)
5255 return 0;
5256 if (!suitableImage(entry, bootPrefix, 0, flags))
5257 return 0;
5258
5259 line =
5260 getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI |
5261 LT_KERNEL_16, entry->lines);
5262 if (!line)
5263 return 0;
5264
5265 rootspec = getRootSpecifier(line->elements[1].item);
5266 printf("%s%s\n", bootPrefix, line->elements[1].item +
5267 ((rootspec != NULL) ? strlen(rootspec) : 0));
5268
5269 return 0;
5270
5271 } else if (displayDefaultTitle) {
5272 struct singleLine *line;
5273 struct singleEntry *entry;
5274
5275 if (config->defaultImage == NO_DEFAULT_ENTRY)
5276 return 0;
5277 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5278 cfi->defaultIsSaved)
5279 config->defaultImage = FIRST_ENTRY_INDEX;
5280 entry = findEntryByIndex(config, config->defaultImage);
5281 if (!entry)
5282 return 0;
5283
5284 if (!configureGrub2) {
5285 char *title;
5286 line = getLineByType(LT_TITLE, entry->lines);
5287 if (!line)
5288 return 0;
5289 title = extractTitle(config, line);
5290 if (!title)
5291 return 0;
5292 printf("%s\n", title);
5293 free(title);
5294 } else {
5295 char *title;
5296
5297 dbgPrintf
5298 ("This is GRUB2, default title is embeded in menuentry\n");
5299 line = getLineByType(LT_MENUENTRY, entry->lines);
5300 if (!line)
5301 return 0;
5302 title = grub2ExtractTitle(line);
5303 if (title)
5304 printf("%s\n", title);
5305 }
5306 return 0;
5307
5308 } else if (displayDefaultIndex) {
5309 if (config->defaultImage == NO_DEFAULT_ENTRY)
5310 return 0;
5311 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5312 cfi->defaultIsSaved)
5313 config->defaultImage = FIRST_ENTRY_INDEX;
5314 printf("%i\n", config->defaultImage);
5315 return 0;
5316
5317 } else if (kernelInfo)
5318 return displayInfo(config, kernelInfo, bootPrefix);
5319
5320 if (copyDefault) {
5321 template = findTemplate(config, bootPrefix, NULL, 0, flags);
5322 if (!template)
5323 return 1;
5324 }
5325
5326 markRemovedImage(config, removeKernelPath, bootPrefix);
5327 markRemovedImage(config, removeMBKernel, bootPrefix);
5328 setDefaultImage(config, newKernelPath != NULL, defaultKernel,
5329 makeDefault, bootPrefix, flags, defaultIndex,
5330 newIndex);
5331 setFallbackImage(config, newKernelPath != NULL);
5332 if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
5333 removeArgs, newMBKernelArgs, removeMBKernelArgs))
5334 return 1;
5335 if (updateKernelPath && newKernelInitrd) {
5336 if (newMBKernel) {
5337 if (addMBInitrd(config, newMBKernel, updateKernelPath,
5338 bootPrefix, newKernelInitrd,
5339 newKernelTitle))
5340 return 1;
5341 } else {
5342 if (updateInitrd(config, updateKernelPath, bootPrefix,
5343 newKernelInitrd, newKernelTitle))
5344 return 1;
5345 }
5346 }
5347 if (addNewKernel(config, template, bootPrefix, newKernelPath,
5348 newKernelTitle, newKernelArgs, newKernelInitrd,
5349 (const char **)extraInitrds, extraInitrdCount,
5350 newMBKernel, newMBKernelArgs, newDevTreePath,
5351 newIndex))
5352 return 1;
5353
5354 if (numEntries(config) == 0) {
5355 fprintf(stderr,
5356 _("grubby: doing this would leave no kernel entries. "
5357 "Not writing out new config.\n"));
5358 return 1;
5359 }
5360
5361 if (!outputFile)
5362 outputFile = (char *)grubConfig;
5363
5364 return writeConfig(config, outputFile, bootPrefix);
5365 }