Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3137 - (show annotations) (download)
Tue Jul 7 11:10:21 2020 UTC (3 years, 9 months ago) by niro
File MIME type: text/plain
File size: 135819 byte(s)
grubby: Make sure configure variables are set correctly.
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 int trueIndex, currentIndex;
1716
1717 trueIndex = 0;
1718 currentIndex = 0;
1719
1720 while ((entry = findEntryByIndex(cfg, currentIndex))) {
1721 if (!entry->skip) {
1722 if (trueIndex == cfg->defaultImage) {
1723 break;
1724 }
1725 trueIndex++;
1726 }
1727 currentIndex++;
1728 }
1729 line = getLineByType(LT_MENUENTRY, entry->lines);
1730 if (!line)
1731 line = getLineByType(LT_TITLE, entry->lines);
1732 if (line) {
1733 title = extractTitle(cfg, line);
1734 if (title)
1735 cfg->cfi->setEnv(cfg->cfi,
1736 "saved_entry", title);
1737 }
1738 }
1739 } else if (cfg->defaultImage >= FIRST_ENTRY_INDEX) {
1740 if (cfg->cfi->defaultIsIndex) {
1741 if (cfg->cfi->defaultIsVariable) {
1742 fprintf(out, "%sset default=\"%d\"\n", indent,
1743 cfg->defaultImage);
1744 } else {
1745 fprintf(out, "%sdefault%s%d\n", indent,
1746 separator, cfg->defaultImage);
1747 }
1748 } else {
1749 int image = cfg->defaultImage;
1750
1751 entry = cfg->entries;
1752 while (entry && entry->skip)
1753 entry = entry->next;
1754
1755 i = 0;
1756 while (entry && i < image) {
1757 entry = entry->next;
1758
1759 while (entry && entry->skip)
1760 entry = entry->next;
1761 i++;
1762 }
1763
1764 if (!entry)
1765 return;
1766
1767 line = getLineByType(LT_TITLE, entry->lines);
1768
1769 if (line && line->numElements >= 2)
1770 fprintf(out, "%sdefault%s%s\n", indent,
1771 separator, line->elements[1].item);
1772 else if (line && (line->numElements == 1)
1773 && cfg->cfi->titleBracketed) {
1774 char *title = extractTitle(cfg, line);
1775 if (title) {
1776 fprintf(out, "%sdefault%s%s\n", indent,
1777 separator, title);
1778 free(title);
1779 }
1780 }
1781 }
1782 }
1783 }
1784
1785 static int writeConfig(struct grubConfig *cfg, char *outName,
1786 const char *prefix)
1787 {
1788 FILE *out;
1789 struct singleLine *line;
1790 struct singleEntry *entry;
1791 char *tmpOutName;
1792 int needs = MAIN_DEFAULT;
1793 struct stat sb;
1794 int i;
1795 int rc = 0;
1796
1797 if (!strcmp(outName, "-")) {
1798 out = stdout;
1799 tmpOutName = NULL;
1800 } else {
1801 if (!lstat(outName, &sb) && S_ISLNK(sb.st_mode)) {
1802 char *buf;
1803 int len = 256;
1804 int rc;
1805
1806 /* most likely the symlink is relative, so change our
1807 directory to the dir of the symlink */
1808 char *dir = strdupa(outName);
1809 rc = chdir(dirname(dir));
1810 do {
1811 buf = alloca(len + 1);
1812 rc = readlink(basename(outName), buf, len);
1813 if (rc == len)
1814 len += 256;
1815 } while (rc == len);
1816
1817 if (rc < 0) {
1818 fprintf(stderr,
1819 _
1820 ("grubby: error readlink link %s: %s\n"),
1821 outName, strerror(errno));
1822 return 1;
1823 }
1824
1825 outName = buf;
1826 outName[rc] = '\0';
1827 }
1828
1829 tmpOutName = alloca(strlen(outName) + 2);
1830 sprintf(tmpOutName, "%s-", outName);
1831 out = fopen(tmpOutName, "w");
1832 if (!out) {
1833 fprintf(stderr, _("grubby: error creating %s: %s\n"),
1834 tmpOutName, strerror(errno));
1835 return 1;
1836 }
1837
1838 if (!stat(outName, &sb)) {
1839 if (chmod(tmpOutName, sb.st_mode & ~(S_IFMT))) {
1840 fprintf(stderr,
1841 _
1842 ("grubby: error setting perms on %s: %s\n"),
1843 tmpOutName, strerror(errno));
1844 fclose(out);
1845 unlink(tmpOutName);
1846 return 1;
1847 }
1848 }
1849 }
1850
1851 line = cfg->theLines;
1852 struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi);
1853 while (line) {
1854 if (line->type == LT_SET_VARIABLE && defaultKw &&
1855 line->numElements == 3 &&
1856 !strcmp(line->elements[1].item, defaultKw->key) &&
1857 !is_special_grub2_variable(line->elements[2].item)) {
1858 writeDefault(out, line->indent,
1859 line->elements[0].indent, cfg);
1860 needs &= ~MAIN_DEFAULT;
1861 } else if (line->type == LT_DEFAULT) {
1862 writeDefault(out, line->indent,
1863 line->elements[0].indent, cfg);
1864 needs &= ~MAIN_DEFAULT;
1865 } else if (line->type == LT_FALLBACK) {
1866 if (cfg->fallbackImage > -1)
1867 fprintf(out, "%s%s%s%d\n", line->indent,
1868 line->elements[0].item,
1869 line->elements[0].indent,
1870 cfg->fallbackImage);
1871 } else {
1872 if (lineWrite(out, line, cfg->cfi) == -1) {
1873 fprintf(stderr,
1874 _("grubby: error writing %s: %s\n"),
1875 tmpOutName, strerror(errno));
1876 fclose(out);
1877 unlink(tmpOutName);
1878 return 1;
1879 }
1880 }
1881
1882 line = line->next;
1883 }
1884
1885 if (needs & MAIN_DEFAULT) {
1886 writeDefault(out, cfg->primaryIndent, "=", cfg);
1887 needs &= ~MAIN_DEFAULT;
1888 }
1889
1890 i = 0;
1891 while ((entry = findEntryByIndex(cfg, i++))) {
1892 if (entry->skip)
1893 continue;
1894
1895 line = entry->lines;
1896 while (line) {
1897 if (lineWrite(out, line, cfg->cfi) == -1) {
1898 fprintf(stderr,
1899 _("grubby: error writing %s: %s\n"),
1900 tmpOutName, strerror(errno));
1901 fclose(out);
1902 unlink(tmpOutName);
1903 return 1;
1904 }
1905 line = line->next;
1906 }
1907 }
1908
1909 if (tmpOutName) {
1910 /* write userspace buffers */
1911 if (fflush(out))
1912 rc = 1;
1913
1914 /* purge the write-back cache with fsync() */
1915 if (fsync(fileno(out)))
1916 rc = 1;
1917
1918 if (fclose(out))
1919 rc = 1;
1920
1921 if (rc == 0 && rename(tmpOutName, outName)) {
1922 unlink(tmpOutName);
1923 rc = 1;
1924 }
1925
1926 /* fsync() the destination directory after rename */
1927 if (rc == 0) {
1928 int dirfd;
1929
1930 dirfd = open(dirname(strdupa(outName)), O_RDONLY);
1931 if (dirfd < 0)
1932 rc = 1;
1933 else if (fsync(dirfd))
1934 rc = 1;
1935
1936 if (dirfd >= 0)
1937 close(dirfd);
1938 }
1939
1940 if (rc == 1)
1941 fprintf(stderr,
1942 _("grubby: error flushing data: %m\n"));
1943 }
1944
1945 return rc;
1946 }
1947
1948 static int numEntries(struct grubConfig *cfg)
1949 {
1950 int i = 0;
1951 struct singleEntry *entry;
1952
1953 entry = cfg->entries;
1954 while (entry) {
1955 if (!entry->skip)
1956 i++;
1957 entry = entry->next;
1958 }
1959 return i;
1960 }
1961
1962 static char *findDiskForRoot()
1963 {
1964 int fd;
1965 char buf[65536];
1966 char *devname;
1967 char *chptr;
1968 int rc;
1969
1970 if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {
1971 fprintf(stderr, "grubby: failed to open %s: %s\n",
1972 _PATH_MOUNTED, strerror(errno));
1973 return NULL;
1974 }
1975
1976 rc = read(fd, buf, sizeof(buf) - 1);
1977 if (rc <= 0) {
1978 fprintf(stderr, "grubby: failed to read %s: %s\n",
1979 _PATH_MOUNTED, strerror(errno));
1980 close(fd);
1981 return NULL;
1982 }
1983 close(fd);
1984 buf[rc] = '\0';
1985 chptr = buf;
1986
1987 char *foundanswer = NULL;
1988
1989 while (chptr && chptr != buf + rc) {
1990 devname = chptr;
1991
1992 /*
1993 * The first column of a mtab entry is the device, but if the
1994 * entry is a special device it won't start with /, so move
1995 * on to the next line.
1996 */
1997 if (*devname != '/') {
1998 chptr = strchr(chptr, '\n');
1999 if (chptr)
2000 chptr++;
2001 continue;
2002 }
2003
2004 /* Seek to the next space */
2005 chptr = strchr(chptr, ' ');
2006 if (!chptr) {
2007 fprintf(stderr, "grubby: error parsing %s: %s\n",
2008 _PATH_MOUNTED, strerror(errno));
2009 return NULL;
2010 }
2011
2012 /*
2013 * The second column of a mtab entry is the mount point, we
2014 * are looking for '/' obviously.
2015 */
2016 if (*(++chptr) == '/' && *(++chptr) == ' ') {
2017 /* remember the last / entry in mtab */
2018 foundanswer = devname;
2019 }
2020
2021 /* Next line */
2022 chptr = strchr(chptr, '\n');
2023 if (chptr)
2024 chptr++;
2025 }
2026
2027 /* Return the last / entry found */
2028 if (foundanswer) {
2029 chptr = strchr(foundanswer, ' ');
2030 *chptr = '\0';
2031 return strdup(foundanswer);
2032 }
2033
2034 return NULL;
2035 }
2036
2037 void printEntry(struct singleEntry *entry, FILE * f)
2038 {
2039 int i;
2040 struct singleLine *line;
2041
2042 for (line = entry->lines; line; line = line->next) {
2043 log_message(f, "DBG: %s", line->indent);
2044 for (i = 0; i < line->numElements; i++) {
2045 /* Need to handle this, because we strip the quotes from
2046 * menuentry when read it. */
2047 if (line->type == LT_MENUENTRY && i == 1) {
2048 if (!isquote(*line->elements[i].item))
2049 log_message(f, "\'%s\'",
2050 line->elements[i].item);
2051 else
2052 log_message(f, "%s",
2053 line->elements[i].item);
2054 log_message(f, "%s", line->elements[i].indent);
2055
2056 continue;
2057 }
2058
2059 log_message(f, "%s%s",
2060 line->elements[i].item,
2061 line->elements[i].indent);
2062 }
2063 log_message(f, "\n");
2064 }
2065 }
2066
2067 void notSuitablePrintf(struct singleEntry *entry, int okay, const char *fmt,
2068 ...)
2069 {
2070 static int once;
2071 va_list argp, argq;
2072
2073 va_start(argp, fmt);
2074
2075 va_copy(argq, argp);
2076 if (!once) {
2077 log_time(NULL);
2078 log_message(NULL, "command line: %s\n", saved_command_line);
2079 }
2080 log_message(NULL, "DBG: Image entry %s: ",
2081 okay ? "succeeded" : "failed");
2082 log_vmessage(NULL, fmt, argq);
2083
2084 printEntry(entry, NULL);
2085 va_end(argq);
2086
2087 if (!debug) {
2088 once = 1;
2089 va_end(argp);
2090 return;
2091 }
2092
2093 if (okay) {
2094 va_end(argp);
2095 return;
2096 }
2097
2098 if (!once)
2099 log_message(stderr, "DBG: command line: %s\n",
2100 saved_command_line);
2101 once = 1;
2102 fprintf(stderr, "DBG: Image entry failed: ");
2103 vfprintf(stderr, fmt, argp);
2104 printEntry(entry, stderr);
2105 va_end(argp);
2106 }
2107
2108 #define beginswith(s, c) ((s) && (s)[0] == (c))
2109
2110 static int endswith(const char *s, char c)
2111 {
2112 int slen;
2113
2114 if (!s || !s[0])
2115 return 0;
2116 slen = strlen(s) - 1;
2117
2118 return s[slen] == c;
2119 }
2120
2121 int suitableImage(struct singleEntry *entry, const char *bootPrefix,
2122 int skipRemoved, int flags)
2123 {
2124 struct singleLine *line;
2125 char *fullName;
2126 int i;
2127 char *dev;
2128 char *rootspec;
2129 char *rootdev;
2130
2131 if (skipRemoved && entry->skip) {
2132 notSuitablePrintf(entry, 0, "marked to skip\n");
2133 return 0;
2134 }
2135
2136 line =
2137 getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI | LT_KERNEL_16,
2138 entry->lines);
2139 if (!line) {
2140 notSuitablePrintf(entry, 0, "no line found\n");
2141 return 0;
2142 }
2143 if (line->numElements < 2) {
2144 notSuitablePrintf(entry, 0, "line has only %d elements\n",
2145 line->numElements);
2146 return 0;
2147 }
2148
2149 if (flags & GRUBBY_BADIMAGE_OKAY) {
2150 notSuitablePrintf(entry, 1, "\n");
2151 return 1;
2152 }
2153
2154 fullName = alloca(strlen(bootPrefix) +
2155 strlen(line->elements[1].item) + 1);
2156 rootspec = getRootSpecifier(line->elements[1].item);
2157 int rootspec_offset = rootspec ? strlen(rootspec) : 0;
2158 int hasslash = endswith(bootPrefix, '/') ||
2159 beginswith(line->elements[1].item + rootspec_offset, '/');
2160 sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/",
2161 line->elements[1].item + rootspec_offset);
2162 if (access(fullName, R_OK)) {
2163 notSuitablePrintf(entry, 0, "access to %s failed\n", fullName);
2164 return 0;
2165 }
2166 for (i = 2; i < line->numElements; i++)
2167 if (!strncasecmp(line->elements[i].item, "root=", 5))
2168 break;
2169 if (i < line->numElements) {
2170 dev = line->elements[i].item + 5;
2171 } else {
2172 /* look for a lilo style LT_ROOT line */
2173 line = getLineByType(LT_ROOT, entry->lines);
2174
2175 if (line && line->numElements >= 2) {
2176 dev = line->elements[1].item;
2177 } else {
2178 /* didn't succeed in finding a LT_ROOT, let's try
2179 * LT_KERNELARGS. grub+multiboot uses LT_MBMODULE
2180 * for the args, so check that too.
2181 */
2182 line =
2183 getLineByType(LT_KERNELARGS | LT_MBMODULE,
2184 entry->lines);
2185
2186 /* failed to find one */
2187 if (!line) {
2188 notSuitablePrintf(entry, 0, "no line found\n");
2189 return 0;
2190 }
2191
2192 for (i = 1; i < line->numElements; i++)
2193 if (!strncasecmp
2194 (line->elements[i].item, "root=", 5))
2195 break;
2196 if (i < line->numElements)
2197 dev = line->elements[i].item + 5;
2198 else {
2199 notSuitablePrintf(entry, 0,
2200 "no root= entry found\n");
2201 /* it failed too... can't find root= */
2202 return 0;
2203 }
2204 }
2205 }
2206
2207 dev = getpathbyspec(dev);
2208 if (!getpathbyspec(dev)) {
2209 notSuitablePrintf(entry, 0, "can't find blkid entry for %s\n",
2210 dev);
2211 return 0;
2212 } else
2213 dev = getpathbyspec(dev);
2214
2215 rootdev = findDiskForRoot();
2216 if (!rootdev) {
2217 notSuitablePrintf(entry, 0, "can't find root device\n");
2218 return 0;
2219 }
2220
2221 if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {
2222 notSuitablePrintf(entry, 0,
2223 "uuid missing: rootdev %s, dev %s\n",
2224 getuuidbydev(rootdev), getuuidbydev(dev));
2225 free(rootdev);
2226 return 0;
2227 }
2228
2229 if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
2230 notSuitablePrintf(entry, 0,
2231 "uuid mismatch: rootdev %s, dev %s\n",
2232 getuuidbydev(rootdev), getuuidbydev(dev));
2233 free(rootdev);
2234 return 0;
2235 }
2236
2237 free(rootdev);
2238 notSuitablePrintf(entry, 1, "\n");
2239
2240 return 1;
2241 }
2242
2243 /* returns the first match on or after the one pointed to by index (if index
2244 is not NULL) which is not marked as skip */
2245 struct singleEntry *findEntryByPath(struct grubConfig *config,
2246 const char *kernel, const char *prefix,
2247 int *index)
2248 {
2249 struct singleEntry *entry = NULL;
2250 struct singleLine *line;
2251 int i;
2252 char *chptr;
2253 char *rootspec = NULL;
2254 enum lineType_e checkType = LT_KERNEL;
2255
2256 if (isdigit(*kernel)) {
2257 int *indexVars = alloca(sizeof(*indexVars) * strlen(kernel));
2258
2259 i = 0;
2260 indexVars[i] = strtol(kernel, &chptr, 10);
2261 while (*chptr == ',') {
2262 i++;
2263 kernel = chptr + 1;
2264 indexVars[i] = strtol(kernel, &chptr, 10);
2265 }
2266
2267 if (*chptr) {
2268 /* can't parse it, bail */
2269 return NULL;
2270 }
2271
2272 indexVars[i + 1] = -1;
2273
2274 i = 0;
2275 if (index) {
2276 while (i < *index) {
2277 i++;
2278 if (indexVars[i] == -1)
2279 return NULL;
2280 }
2281 }
2282
2283 entry = findEntryByIndex(config, indexVars[i]);
2284 if (!entry)
2285 return NULL;
2286
2287 line =
2288 getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI |
2289 LT_KERNEL_16, entry->lines);
2290 if (!line)
2291 return NULL;
2292
2293 if (index)
2294 *index = indexVars[i];
2295 return entry;
2296 }
2297
2298 if (!strcmp(kernel, "DEFAULT")) {
2299 if (index && *index > config->defaultImage) {
2300 entry = NULL;
2301 } else {
2302 entry = findEntryByIndex(config, config->defaultImage);
2303 if (entry && entry->skip)
2304 entry = NULL;
2305 else if (index)
2306 *index = config->defaultImage;
2307 }
2308 } else if (!strcmp(kernel, "ALL")) {
2309 if (index)
2310 i = *index;
2311 else
2312 i = 0;
2313
2314 while ((entry = findEntryByIndex(config, i))) {
2315 if (!entry->skip)
2316 break;
2317 i++;
2318 }
2319
2320 if (entry && index)
2321 *index = i;
2322 } else {
2323 if (index)
2324 i = *index;
2325 else
2326 i = 0;
2327
2328 if (!strncmp(kernel, "TITLE=", 6)) {
2329 prefix = "";
2330 checkType = LT_TITLE | LT_MENUENTRY;
2331 kernel += 6;
2332 }
2333
2334 for (entry = findEntryByIndex(config, i); entry;
2335 entry = entry->next, i++) {
2336 if (entry->skip)
2337 continue;
2338
2339 dbgPrintf("findEntryByPath looking for %d %s in %p\n",
2340 checkType, kernel, entry);
2341
2342 /* check all the lines matching checkType */
2343 for (line = entry->lines; line; line = line->next) {
2344 enum lineType_e ct = checkType;
2345 if (entry->multiboot && checkType == LT_KERNEL)
2346 ct = LT_KERNEL | LT_KERNEL_EFI |
2347 LT_MBMODULE | LT_HYPER |
2348 LT_KERNEL_16;
2349 else if (checkType & LT_KERNEL)
2350 ct = checkType | LT_KERNEL_EFI |
2351 LT_KERNEL_16;
2352 line = getLineByType(ct, line);
2353 if (!line)
2354 break; /* not found in this entry */
2355
2356 if (line && line->type != LT_MENUENTRY &&
2357 line->numElements >= 2) {
2358 rootspec =
2359 getRootSpecifier(line->elements[1].
2360 item);
2361 if (!strcmp
2362 (line->elements[1].item +
2363 ((rootspec !=
2364 NULL) ? strlen(rootspec) : 0),
2365 kernel + strlen(prefix)))
2366 break;
2367 }
2368 if (line->type == LT_MENUENTRY &&
2369 !strcmp(line->elements[1].item, kernel))
2370 break;
2371 }
2372
2373 /* make sure this entry has a kernel identifier; this skips
2374 * non-Linux boot entries (could find netbsd etc, though, which is
2375 * unfortunate)
2376 */
2377 if (line
2378 && getLineByType(LT_KERNEL | LT_HYPER |
2379 LT_KERNEL_EFI | LT_KERNEL_16,
2380 entry->lines))
2381 break; /* found 'im! */
2382 }
2383
2384 if (index)
2385 *index = i;
2386 }
2387
2388 return entry;
2389 }
2390
2391 struct singleEntry *findEntryByTitle(struct grubConfig *cfg, char *title,
2392 int *index)
2393 {
2394 struct singleEntry *entry;
2395 struct singleLine *line;
2396 int i;
2397 char *newtitle;
2398
2399 for (i = 0, entry = cfg->entries; entry; entry = entry->next, i++) {
2400 if (index && i < *index)
2401 continue;
2402 line = getLineByType(LT_TITLE, entry->lines);
2403 if (!line)
2404 line = getLineByType(LT_MENUENTRY, entry->lines);
2405 if (!line)
2406 continue;
2407 newtitle = grub2ExtractTitle(line);
2408 if (!newtitle)
2409 continue;
2410 if (!strcmp(title, newtitle))
2411 break;
2412 }
2413
2414 if (!entry)
2415 return NULL;
2416
2417 if (index)
2418 *index = i;
2419 return entry;
2420 }
2421
2422 struct singleEntry *findEntryByIndex(struct grubConfig *cfg, int index)
2423 {
2424 struct singleEntry *entry;
2425
2426 entry = cfg->entries;
2427 while (index && entry) {
2428 entry = entry->next;
2429 index--;
2430 }
2431
2432 return entry;
2433 }
2434
2435 /* Find a good template to use for the new kernel. An entry is
2436 * good if the kernel and mkinitrd exist (even if the entry
2437 * is going to be removed). Try and use the default entry, but
2438 * if that doesn't work just take the first. If we can't find one,
2439 * bail. */
2440 struct singleEntry *findTemplate(struct grubConfig *cfg, const char *prefix,
2441 int *indexPtr, int skipRemoved, int flags)
2442 {
2443 struct singleEntry *entry, *entry2;
2444 int index;
2445
2446 if (cfg->cfi->defaultIsSaved) {
2447 if (cfg->cfi->getEnv) {
2448 char *defTitle =
2449 cfg->cfi->getEnv(cfg->cfi, "saved_entry");
2450 if (defTitle) {
2451 int index = 0;
2452 if (isnumber(defTitle)) {
2453 index = atoi(defTitle);
2454 entry = findEntryByIndex(cfg, index);
2455 } else {
2456 entry =
2457 findEntryByTitle(cfg, defTitle,
2458 &index);
2459 }
2460 if (entry
2461 && suitableImage(entry, prefix, skipRemoved,
2462 flags)) {
2463 cfg->defaultImage = index;
2464 if (indexPtr)
2465 *indexPtr = index;
2466 return entry;
2467 }
2468 }
2469 }
2470 } else if (cfg->defaultImage >= FIRST_ENTRY_INDEX) {
2471 entry = findEntryByIndex(cfg, cfg->defaultImage);
2472 if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2473 if (indexPtr)
2474 *indexPtr = cfg->defaultImage;
2475 return entry;
2476 }
2477 }
2478
2479 index = 0;
2480 while ((entry = findEntryByIndex(cfg, index))) {
2481 if (suitableImage(entry, prefix, skipRemoved, flags)) {
2482 int j, unmodifiedIndex;
2483
2484 unmodifiedIndex = index;
2485
2486 for (j = 0; j < unmodifiedIndex; j++) {
2487 entry2 = findEntryByIndex(cfg, j);
2488 if (entry2->skip)
2489 index--;
2490 }
2491 if (indexPtr)
2492 *indexPtr = index;
2493
2494 return entry;
2495 }
2496
2497 index++;
2498 }
2499
2500 fprintf(stderr,
2501 _("grubby fatal error: unable to find a suitable template\n"));
2502
2503 return NULL;
2504 }
2505
2506 char *findBootPrefix(void)
2507 {
2508 struct stat sb, sb2;
2509
2510 stat("/", &sb);
2511 #ifdef __ia64__
2512 stat("/boot/efi/EFI/redhat/", &sb2);
2513 #else
2514 stat("/boot", &sb2);
2515 #endif
2516
2517 if (sb.st_dev == sb2.st_dev)
2518 return strdup("");
2519
2520 #ifdef __ia64__
2521 return strdup("/boot/efi/EFI/redhat/");
2522 #else
2523 return strdup("/boot");
2524 #endif
2525 }
2526
2527 void markRemovedImage(struct grubConfig *cfg, const char *image,
2528 const char *prefix)
2529 {
2530 struct singleEntry *entry;
2531
2532 if (!image)
2533 return;
2534
2535 /* check and see if we're removing the default image */
2536 if (isdigit(*image)) {
2537 entry = findEntryByPath(cfg, image, prefix, NULL);
2538 if (entry)
2539 entry->skip = 1;
2540 return;
2541 }
2542
2543 while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
2544 entry->skip = 1;
2545 }
2546
2547 void setDefaultImage(struct grubConfig *config, int isAddingBootEntry,
2548 const char *defaultKernelPath, int newBootEntryIsDefault,
2549 const char *prefix, int flags,
2550 int newDefaultBootEntryIndex, int newBootEntryIndex)
2551 {
2552 struct singleEntry *bootEntry, *newDefault;
2553 int indexToVerify, firstKernelEntryIndex, currentLookupIndex;
2554
2555 /* initialize */
2556 currentLookupIndex = FIRST_ENTRY_INDEX;
2557
2558 /* handle the two cases where the user explictly picks the default
2559 * boot entry index as it would exist post-modification */
2560
2561 /* Case 1: user chose to make the latest boot entry the default */
2562 if (newBootEntryIsDefault) {
2563 config->defaultImage = newBootEntryIndex;
2564 return;
2565 }
2566
2567 /* Case 2: user picked an arbitrary index as the default boot entry */
2568 if (newDefaultBootEntryIndex >= FIRST_ENTRY_INDEX) {
2569 indexToVerify = newDefaultBootEntryIndex;
2570
2571 /* user chose to make latest boot entry the default */
2572 if (newDefaultBootEntryIndex == newBootEntryIndex) {
2573 config->defaultImage = newBootEntryIndex;
2574 return;
2575 }
2576
2577 /* the user picks the default index based on the
2578 * order of the bootloader configuration after
2579 * modification; ensure we are checking for the
2580 * existence of the correct entry */
2581 if (newBootEntryIndex < newDefaultBootEntryIndex) {
2582 if (!config->isModified)
2583 indexToVerify--;
2584 }
2585
2586 /* verify the user selected index will exist */
2587 if (findEntryByIndex(config, indexToVerify)) {
2588 config->defaultImage = newDefaultBootEntryIndex;
2589 } else {
2590 config->defaultImage = NO_DEFAULT_ENTRY;
2591 }
2592
2593 return;
2594 }
2595
2596 /* handle cases where the index value may shift */
2597
2598 /* check validity of existing default or first-entry-found
2599 selection */
2600 if (defaultKernelPath) {
2601 /* we must initialize this */
2602 firstKernelEntryIndex = 0;
2603 /* user requested first-entry-found */
2604 if (!findEntryByPath(config, defaultKernelPath,
2605 prefix, &firstKernelEntryIndex)) {
2606 /* don't change default if can't find match */
2607 config->defaultImage = NO_DEFAULT_ENTRY;
2608 return;
2609 }
2610
2611 config->defaultImage = firstKernelEntryIndex;
2612
2613 /* this is where we start looking for decrement later */
2614 currentLookupIndex = config->defaultImage;
2615
2616 if (isAddingBootEntry && !config->isModified &&
2617 (newBootEntryIndex < config->defaultImage)) {
2618 /* increment because new entry added before default */
2619 config->defaultImage++;
2620 }
2621 } else {
2622 /* check to see if the default is stored in the environment */
2623 if (config->defaultImage < FIRST_ENTRY_INDEX) {
2624 if (config->defaultImage == DEFAULT_SAVED || config->defaultImage == DEFAULT_SAVED_GRUB2)
2625 {
2626 if (config->cfi->defaultIsSaved) {
2627 if (config->cfi->getEnv) {
2628 char *defaultTitle = config->cfi->getEnv(config->cfi, "saved_entry");
2629
2630 if (defaultTitle) {
2631 if (isnumber(defaultTitle)) {
2632 currentLookupIndex = atoi(defaultTitle);
2633 } else {
2634 findEntryByTitle(config, defaultTitle, &currentLookupIndex);
2635 }
2636 /* set the default Image to an actual index */
2637 config->defaultImage = currentLookupIndex;
2638 }
2639 }
2640 }
2641 }
2642 } else {
2643 /* use pre-existing default entry from the file*/
2644 currentLookupIndex = config->defaultImage;
2645 }
2646
2647 if (isAddingBootEntry
2648 && (newBootEntryIndex <= config->defaultImage)) {
2649 config->defaultImage++;
2650
2651 if (config->isModified) {
2652 currentLookupIndex++;
2653 }
2654 }
2655 }
2656
2657 /* sanity check - is this entry index valid? */
2658 bootEntry = findEntryByIndex(config, currentLookupIndex);
2659
2660 if ((bootEntry && bootEntry->skip) || !bootEntry) {
2661 /* entry is to be skipped or is invalid */
2662 if (isAddingBootEntry) {
2663 config->defaultImage = newBootEntryIndex;
2664 return;
2665 }
2666 newDefault =
2667 findTemplate(config, prefix, &config->defaultImage, 1,
2668 flags);
2669 if (!newDefault) {
2670 config->defaultImage = NO_DEFAULT_ENTRY;
2671 }
2672
2673 return;
2674 }
2675
2676 currentLookupIndex--;
2677
2678 /* decrement index by the total number of entries deleted */
2679
2680 for (indexToVerify = currentLookupIndex;
2681 indexToVerify >= FIRST_ENTRY_INDEX; indexToVerify--) {
2682
2683 bootEntry = findEntryByIndex(config, indexToVerify);
2684
2685 if (bootEntry && bootEntry->skip) {
2686 config->defaultImage--;
2687 }
2688 }
2689 }
2690
2691 void setFallbackImage(struct grubConfig *config, int hasNew)
2692 {
2693 struct singleEntry *entry, *entry2;
2694 int j;
2695
2696 if (config->fallbackImage == -1)
2697 return;
2698
2699 entry = findEntryByIndex(config, config->fallbackImage);
2700 if (!entry || entry->skip) {
2701 config->fallbackImage = -1;
2702 return;
2703 }
2704
2705 if (hasNew)
2706 config->fallbackImage++;
2707
2708 /* count the number of entries erased before this one */
2709 for (j = 0; j < config->fallbackImage; j++) {
2710 entry2 = findEntryByIndex(config, j);
2711 if (entry2->skip)
2712 config->fallbackImage--;
2713 }
2714 }
2715
2716 void displayEntry(struct grubConfig *config, struct singleEntry *entry, const char *prefix, int index)
2717 {
2718 struct singleLine *line;
2719 char *root = NULL;
2720 int i;
2721 int j;
2722
2723 printf("index=%d\n", index);
2724
2725 line =
2726 getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI | LT_KERNEL_16,
2727 entry->lines);
2728 if (!line) {
2729 printf("non linux entry\n");
2730 return;
2731 }
2732
2733 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2734 printf("kernel=%s\n", line->elements[1].item);
2735 else
2736 printf("kernel=%s%s\n", prefix, line->elements[1].item);
2737
2738 if (line->numElements >= 3) {
2739 printf("args=\"");
2740 i = 2;
2741 while (i < line->numElements) {
2742 if (!strncmp(line->elements[i].item, "root=", 5)) {
2743 root = line->elements[i].item + 5;
2744 } else {
2745 printf("%s%s", line->elements[i].item,
2746 line->elements[i].indent);
2747 }
2748
2749 i++;
2750 }
2751 printf("\"\n");
2752 } else {
2753 line = getLineByType(LT_KERNELARGS, entry->lines);
2754 if (line) {
2755 char *s;
2756
2757 printf("args=\"");
2758 i = 1;
2759 while (i < line->numElements) {
2760 if (!strncmp
2761 (line->elements[i].item, "root=", 5)) {
2762 root = line->elements[i].item + 5;
2763 } else {
2764 s = line->elements[i].item;
2765
2766 printf("%s%s", s,
2767 line->elements[i].indent);
2768 }
2769
2770 i++;
2771 }
2772
2773 s = line->elements[i - 1].indent;
2774 printf("\"\n");
2775 }
2776 }
2777
2778 if (!root) {
2779 line = getLineByType(LT_ROOT, entry->lines);
2780 if (line && line->numElements >= 2)
2781 root = line->elements[1].item;
2782 }
2783
2784 if (root) {
2785 char *s = alloca(strlen(root) + 1);
2786
2787 strcpy(s, root);
2788 if (s[strlen(s) - 1] == '"')
2789 s[strlen(s) - 1] = '\0';
2790 /* make sure the root doesn't have a trailing " */
2791 printf("root=%s\n", s);
2792 }
2793
2794 line =
2795 getLineByType(LT_INITRD | LT_INITRD_EFI | LT_INITRD_16,
2796 entry->lines);
2797
2798 if (line && line->numElements >= 2) {
2799 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2800 printf("initrd=");
2801 else
2802 printf("initrd=%s", prefix);
2803
2804 for (i = 1; i < line->numElements; i++)
2805 printf("%s%s", line->elements[i].item,
2806 line->elements[i].indent);
2807 printf("\n");
2808 }
2809
2810 line = getLineByType(LT_TITLE, entry->lines);
2811 if (line) {
2812 char *entryTitle;
2813 /* if we can extractTitle, then it's a zipl config and
2814 * if not then we go ahead with what's existed prior */
2815 entryTitle = extractTitle(config, line);
2816 if (!entryTitle) {
2817 entryTitle=line->elements[1].item;
2818 }
2819 printf("title=%s\n", entryTitle);
2820 } else {
2821 char *title;
2822 line = getLineByType(LT_MENUENTRY, entry->lines);
2823 if (line) {
2824 title = grub2ExtractTitle(line);
2825 if (title)
2826 printf("title=%s\n", title);
2827 }
2828 }
2829
2830 for (j = 0, line = entry->lines; line; line = line->next) {
2831 if ((line->type & LT_MBMODULE) && line->numElements >= 2) {
2832 if (!strncmp
2833 (prefix, line->elements[1].item, strlen(prefix)))
2834 printf("mbmodule%d=", j);
2835 else
2836 printf("mbmodule%d=%s", j, prefix);
2837
2838 for (i = 1; i < line->numElements; i++)
2839 printf("%s%s", line->elements[i].item,
2840 line->elements[i].indent);
2841 printf("\n");
2842 j++;
2843 }
2844 }
2845 }
2846
2847 int isSuseSystem(void)
2848 {
2849 const char *path;
2850 const static char default_path[] = "/etc/SuSE-release";
2851
2852 if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2853 path = default_path;
2854
2855 if (!access(path, R_OK))
2856 return 1;
2857 return 0;
2858 }
2859
2860 int isSuseGrubConf(const char *path)
2861 {
2862 FILE *grubConf;
2863 char *line = NULL;
2864 size_t len = 0, res = 0;
2865
2866 grubConf = fopen(path, "r");
2867 if (!grubConf) {
2868 dbgPrintf("Could not open SuSE configuration file '%s'\n",
2869 path);
2870 return 0;
2871 }
2872
2873 while ((res = getline(&line, &len, grubConf)) != -1) {
2874 if (!strncmp(line, "setup", 5)) {
2875 fclose(grubConf);
2876 free(line);
2877 return 1;
2878 }
2879 }
2880
2881 dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2882 path);
2883
2884 fclose(grubConf);
2885 free(line);
2886 return 0;
2887 }
2888
2889 int suseGrubConfGetLba(const char *path, int *lbaPtr)
2890 {
2891 FILE *grubConf;
2892 char *line = NULL;
2893 size_t res = 0, len = 0;
2894
2895 if (!path)
2896 return 1;
2897 if (!lbaPtr)
2898 return 1;
2899
2900 grubConf = fopen(path, "r");
2901 if (!grubConf)
2902 return 1;
2903
2904 while ((res = getline(&line, &len, grubConf)) != -1) {
2905 if (line[res - 1] == '\n')
2906 line[res - 1] = '\0';
2907 else if (len > res)
2908 line[res] = '\0';
2909 else {
2910 line = realloc(line, res + 1);
2911 line[res] = '\0';
2912 }
2913
2914 if (!strncmp(line, "setup", 5)) {
2915 if (strstr(line, "--force-lba")) {
2916 *lbaPtr = 1;
2917 } else {
2918 *lbaPtr = 0;
2919 }
2920 dbgPrintf("lba: %i\n", *lbaPtr);
2921 break;
2922 }
2923 }
2924
2925 free(line);
2926 fclose(grubConf);
2927 return 0;
2928 }
2929
2930 int suseGrubConfGetInstallDevice(const char *path, char **devicePtr)
2931 {
2932 FILE *grubConf;
2933 char *line = NULL;
2934 size_t res = 0, len = 0;
2935 char *lastParamPtr = NULL;
2936 char *secLastParamPtr = NULL;
2937 char installDeviceNumber = '\0';
2938 char *bounds = NULL;
2939
2940 if (!path)
2941 return 1;
2942 if (!devicePtr)
2943 return 1;
2944
2945 grubConf = fopen(path, "r");
2946 if (!grubConf)
2947 return 1;
2948
2949 while ((res = getline(&line, &len, grubConf)) != -1) {
2950 if (strncmp(line, "setup", 5))
2951 continue;
2952
2953 if (line[res - 1] == '\n')
2954 line[res - 1] = '\0';
2955 else if (len > res)
2956 line[res] = '\0';
2957 else {
2958 line = realloc(line, res + 1);
2959 line[res] = '\0';
2960 }
2961
2962 lastParamPtr = bounds = line + res;
2963
2964 /* Last parameter in grub may be an optional IMAGE_DEVICE */
2965 while (!isspace(*lastParamPtr))
2966 lastParamPtr--;
2967 lastParamPtr++;
2968
2969 secLastParamPtr = lastParamPtr - 2;
2970 dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2971
2972 if (lastParamPtr + 3 > bounds) {
2973 dbgPrintf("lastParamPtr going over boundary");
2974 fclose(grubConf);
2975 free(line);
2976 return 1;
2977 }
2978 if (!strncmp(lastParamPtr, "(hd", 3))
2979 lastParamPtr += 3;
2980 dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2981
2982 /*
2983 * Second last parameter will decide wether last parameter is
2984 * an IMAGE_DEVICE or INSTALL_DEVICE
2985 */
2986 while (!isspace(*secLastParamPtr))
2987 secLastParamPtr--;
2988 secLastParamPtr++;
2989
2990 if (secLastParamPtr + 3 > bounds) {
2991 dbgPrintf("secLastParamPtr going over boundary");
2992 fclose(grubConf);
2993 free(line);
2994 return 1;
2995 }
2996 dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2997 if (!strncmp(secLastParamPtr, "(hd", 3)) {
2998 secLastParamPtr += 3;
2999 dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
3000 installDeviceNumber = *secLastParamPtr;
3001 } else {
3002 installDeviceNumber = *lastParamPtr;
3003 }
3004
3005 *devicePtr = malloc(6);
3006 snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
3007 dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
3008 fclose(grubConf);
3009 free(line);
3010 return 0;
3011 }
3012
3013 free(line);
3014 fclose(grubConf);
3015 return 1;
3016 }
3017
3018 int grubGetBootFromDeviceMap(const char *device, char **bootPtr)
3019 {
3020 FILE *deviceMap;
3021 char *line = NULL;
3022 size_t res = 0, len = 0;
3023 char *devicePtr;
3024 char *bounds = NULL;
3025 const char *path;
3026 const static char default_path[] = "/boot/grub/device.map";
3027
3028 if (!device)
3029 return 1;
3030 if (!bootPtr)
3031 return 1;
3032
3033 if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
3034 path = default_path;
3035
3036 dbgPrintf("opening grub device.map file from: %s\n", path);
3037 deviceMap = fopen(path, "r");
3038 if (!deviceMap)
3039 return 1;
3040
3041 while ((res = getline(&line, &len, deviceMap)) != -1) {
3042 if (!strncmp(line, "#", 1))
3043 continue;
3044
3045 if (line[res - 1] == '\n')
3046 line[res - 1] = '\0';
3047 else if (len > res)
3048 line[res] = '\0';
3049 else {
3050 line = realloc(line, res + 1);
3051 line[res] = '\0';
3052 }
3053
3054 devicePtr = line;
3055 bounds = line + res;
3056
3057 while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
3058 devicePtr++;
3059 dbgPrintf("device: %s\n", devicePtr);
3060
3061 if (!strncmp(devicePtr, device, strlen(device))) {
3062 devicePtr += strlen(device);
3063 while (isspace(*devicePtr)
3064 && ((devicePtr + 1) <= bounds))
3065 devicePtr++;
3066
3067 *bootPtr = strdup(devicePtr);
3068 break;
3069 }
3070 }
3071
3072 free(line);
3073 fclose(deviceMap);
3074 return 0;
3075 }
3076
3077 int suseGrubConfGetBoot(const char *path, char **bootPtr)
3078 {
3079 char *grubDevice;
3080
3081 if (suseGrubConfGetInstallDevice(path, &grubDevice))
3082 dbgPrintf("error looking for grub installation device\n");
3083 else
3084 dbgPrintf("grubby installation device: %s\n", grubDevice);
3085
3086 if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
3087 dbgPrintf("error looking for grub boot device\n");
3088 else
3089 dbgPrintf("grubby boot device: %s\n", *bootPtr);
3090
3091 free(grubDevice);
3092 return 0;
3093 }
3094
3095 int parseSuseGrubConf(int *lbaPtr, char **bootPtr)
3096 {
3097 /*
3098 * This SuSE grub configuration file at this location is not your
3099 * average grub configuration file, but instead the grub commands
3100 * used to setup grub on that system.
3101 */
3102 const char *path;
3103 const static char default_path[] = "/etc/grub.conf";
3104
3105 if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
3106 path = default_path;
3107
3108 if (!isSuseGrubConf(path))
3109 return 1;
3110
3111 if (lbaPtr) {
3112 *lbaPtr = 0;
3113 if (suseGrubConfGetLba(path, lbaPtr))
3114 return 1;
3115 }
3116
3117 if (bootPtr) {
3118 *bootPtr = NULL;
3119 suseGrubConfGetBoot(path, bootPtr);
3120 }
3121
3122 return 0;
3123 }
3124
3125 int parseSysconfigGrub(int *lbaPtr, char **bootPtr)
3126 {
3127 FILE *in;
3128 char buf[1024];
3129 char *chptr;
3130 char *start;
3131 char *param;
3132
3133 in = fopen("/etc/sysconfig/grub", "r");
3134 if (!in)
3135 return 1;
3136
3137 if (lbaPtr)
3138 *lbaPtr = 0;
3139 if (bootPtr)
3140 *bootPtr = NULL;
3141
3142 while (fgets(buf, sizeof(buf), in)) {
3143 start = buf;
3144 while (isspace(*start))
3145 start++;
3146 if (*start == '#')
3147 continue;
3148
3149 chptr = strchr(start, '=');
3150 if (!chptr)
3151 continue;
3152 chptr--;
3153 while (*chptr && isspace(*chptr))
3154 chptr--;
3155 chptr++;
3156 *chptr = '\0';
3157
3158 param = chptr + 1;
3159 while (*param && isspace(*param))
3160 param++;
3161 if (*param == '=') {
3162 param++;
3163 while (*param && isspace(*param))
3164 param++;
3165 }
3166
3167 chptr = param;
3168 while (*chptr && !isspace(*chptr))
3169 chptr++;
3170 *chptr = '\0';
3171
3172 if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
3173 *lbaPtr = 1;
3174 else if (!strcmp(start, "boot") && bootPtr)
3175 *bootPtr = strdup(param);
3176 }
3177
3178 fclose(in);
3179
3180 return 0;
3181 }
3182
3183 void dumpSysconfigGrub(void)
3184 {
3185 char *boot = NULL;
3186 int lba;
3187
3188 if (isSuseSystem()) {
3189 if (parseSuseGrubConf(&lba, &boot)) {
3190 free(boot);
3191 return;
3192 }
3193 } else {
3194 if (parseSysconfigGrub(&lba, &boot)) {
3195 free(boot);
3196 return;
3197 }
3198 }
3199
3200 if (lba)
3201 printf("lba\n");
3202 if (boot) {
3203 printf("boot=%s\n", boot);
3204 free(boot);
3205 }
3206 }
3207
3208 int displayInfo(struct grubConfig *config, char *kernel, const char *prefix)
3209 {
3210 int i = 0;
3211 struct singleEntry *entry;
3212 struct singleLine *line;
3213
3214 entry = findEntryByPath(config, kernel, prefix, &i);
3215 if (!entry) {
3216 fprintf(stderr, _("grubby: kernel not found\n"));
3217 return 1;
3218 }
3219
3220 /* this is a horrible hack to support /etc/sysconfig/grub; there must
3221 be a better way */
3222 if (config->cfi == &grubConfigType) {
3223 dumpSysconfigGrub();
3224 } else {
3225 line = getLineByType(LT_BOOT, config->theLines);
3226 if (line && line->numElements >= 1) {
3227 printf("boot=%s\n", line->elements[1].item);
3228 }
3229
3230 line = getLineByType(LT_LBA, config->theLines);
3231 if (line)
3232 printf("lba\n");
3233 }
3234
3235 displayEntry(config, entry, prefix, i);
3236
3237 i++;
3238 while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
3239 displayEntry(config, entry, prefix, i);
3240 i++;
3241 }
3242
3243 return 0;
3244 }
3245
3246 struct singleLine *addLineTmpl(struct singleEntry *entry,
3247 struct singleLine *tmplLine,
3248 struct singleLine *prevLine,
3249 const char *val, struct configFileInfo *cfi)
3250 {
3251 struct singleLine *newLine = lineDup(tmplLine);
3252
3253 if (isEfi && cfi == &grub2ConfigType) {
3254 enum lineType_e old = newLine->type;
3255 newLine->type = preferredLineType(newLine->type, cfi);
3256 if (old != newLine->type)
3257 newLine->elements[0].item =
3258 getKeyByType(newLine->type, cfi);
3259 }
3260
3261 if (val) {
3262 /* override the inherited value with our own.
3263 * This is a little weak because it only applies to elements[1]
3264 */
3265 if (newLine->numElements > 1)
3266 removeElement(newLine, 1);
3267 insertElement(newLine, val, 1, cfi);
3268
3269 /* but try to keep the rootspec from the template... sigh */
3270 if (tmplLine->
3271 type & (LT_HYPER | LT_KERNEL | LT_MBMODULE | LT_INITRD |
3272 LT_KERNEL_EFI | LT_INITRD_EFI | LT_KERNEL_16 |
3273 LT_INITRD_16)) {
3274 char *rootspec =
3275 getRootSpecifier(tmplLine->elements[1].item);
3276 if (rootspec != NULL) {
3277 free(newLine->elements[1].item);
3278 newLine->elements[1].item =
3279 sdupprintf("%s%s", rootspec, val);
3280 }
3281 }
3282 }
3283
3284 dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
3285 newLine->elements[0].item : "");
3286
3287 if (!entry->lines) {
3288 /* first one on the list */
3289 entry->lines = newLine;
3290 } else if (prevLine) {
3291 /* add after prevLine */
3292 newLine->next = prevLine->next;
3293 prevLine->next = newLine;
3294 }
3295
3296 return newLine;
3297 }
3298
3299 /* val may be NULL */
3300 struct singleLine *addLine(struct singleEntry *entry,
3301 struct configFileInfo *cfi,
3302 enum lineType_e type, char *defaultIndent,
3303 const char *val)
3304 {
3305 struct singleLine *line, *prev;
3306 struct keywordTypes *kw;
3307 struct singleLine tmpl;
3308
3309 /* NB: This function shouldn't allocate items on the heap, rather on
3310 * the stack since it calls addLineTmpl which will make copies.
3311 */
3312 if (type == LT_TITLE && cfi->titleBracketed) {
3313 /* we're doing a bracketed title (zipl) */
3314 tmpl.type = type;
3315 tmpl.numElements = 1;
3316 tmpl.elements = alloca(sizeof(*tmpl.elements));
3317 tmpl.elements[0].item = alloca(strlen(val) + 3);
3318 sprintf(tmpl.elements[0].item, "[%s]", val);
3319 tmpl.elements[0].indent = "";
3320 val = NULL;
3321 } else if (type == LT_MENUENTRY) {
3322 char *lineend = "--class gnu-linux --class gnu --class os {";
3323 if (!val) {
3324 fprintf(stderr,
3325 "Line type LT_MENUENTRY requires a value\n");
3326 abort();
3327 }
3328 kw = getKeywordByType(type, cfi);
3329 if (!kw) {
3330 fprintf(stderr,
3331 "Looking up keyword for unknown type %d\n",
3332 type);
3333 abort();
3334 }
3335 tmpl.indent = "";
3336 tmpl.type = type;
3337 tmpl.numElements = 3;
3338 tmpl.elements =
3339 alloca(sizeof(*tmpl.elements) * tmpl.numElements);
3340 tmpl.elements[0].item = kw->key;
3341 tmpl.elements[0].indent = alloca(2);
3342 sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
3343 tmpl.elements[1].item = (char *)val;
3344 tmpl.elements[1].indent = alloca(2);
3345 sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
3346 tmpl.elements[2].item = alloca(strlen(lineend) + 1);
3347 strcpy(tmpl.elements[2].item, lineend);
3348 tmpl.elements[2].indent = "";
3349 } else {
3350 kw = getKeywordByType(type, cfi);
3351 if (!kw) {
3352 fprintf(stderr,
3353 "Looking up keyword for unknown type %d\n",
3354 type);
3355 abort();
3356 }
3357 tmpl.type = type;
3358 tmpl.numElements = val ? 2 : 1;
3359 tmpl.elements =
3360 alloca(sizeof(*tmpl.elements) * tmpl.numElements);
3361 tmpl.elements[0].item = kw->key;
3362 tmpl.elements[0].indent = alloca(2);
3363 sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
3364 if (val) {
3365 tmpl.elements[1].item = (char *)val;
3366 tmpl.elements[1].indent = "";
3367 }
3368 }
3369
3370 /* The last non-empty line gives us the indention to us and the line
3371 * to insert after. Note that comments are considered empty lines,
3372 * which may not be ideal? If there are no lines or we are looking at
3373 * the first line, we use defaultIndent (the first line is normally
3374 * indented differently from the rest) */
3375 for (line = entry->lines, prev = NULL; line; line = line->next) {
3376 if (line->numElements)
3377 prev = line;
3378 /* fall back on the last line if prev isn't otherwise set */
3379 if (!line->next && !prev)
3380 prev = line;
3381 }
3382
3383 struct singleLine *menuEntry;
3384 menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
3385 if (tmpl.type == LT_ENTRY_END) {
3386 if (menuEntry)
3387 tmpl.indent = menuEntry->indent;
3388 else
3389 tmpl.indent = defaultIndent ? : "";
3390 } else if (tmpl.type != LT_MENUENTRY) {
3391 if (menuEntry)
3392 tmpl.indent = "\t";
3393 else if (prev == entry->lines)
3394 tmpl.indent = defaultIndent ? : "";
3395 else
3396 tmpl.indent = prev->indent;
3397 }
3398
3399 return addLineTmpl(entry, &tmpl, prev, val, cfi);
3400 }
3401
3402 void removeLine(struct singleEntry *entry, struct singleLine *line)
3403 {
3404 struct singleLine *prev;
3405 int i;
3406
3407 for (i = 0; i < line->numElements; i++) {
3408 free(line->elements[i].item);
3409 free(line->elements[i].indent);
3410 }
3411 free(line->elements);
3412 free(line->indent);
3413
3414 if (line == entry->lines) {
3415 entry->lines = line->next;
3416 } else {
3417 prev = entry->lines;
3418 while (prev->next != line)
3419 prev = prev->next;
3420 prev->next = line->next;
3421 }
3422
3423 free(line);
3424 }
3425
3426 static void requote(struct singleLine *tmplLine, struct configFileInfo *cfi)
3427 {
3428 struct singleLine newLine = {
3429 .indent = tmplLine->indent,
3430 .type = tmplLine->type,
3431 .next = tmplLine->next,
3432 };
3433 int firstQuotedItem = -1;
3434 int quoteLen = 0;
3435 int j;
3436 int element = 0;
3437 char *c;
3438
3439 c = malloc(strlen(tmplLine->elements[0].item) + 1);
3440 strcpy(c, tmplLine->elements[0].item);
3441 insertElement(&newLine, c, element++, cfi);
3442 free(c);
3443 c = NULL;
3444
3445 for (j = 1; j < tmplLine->numElements; j++) {
3446 if (firstQuotedItem == -1) {
3447 quoteLen += strlen(tmplLine->elements[j].item);
3448
3449 if (isquote(tmplLine->elements[j].item[0])) {
3450 firstQuotedItem = j;
3451 quoteLen +=
3452 strlen(tmplLine->elements[j].indent);
3453 } else {
3454 c = malloc(quoteLen + 1);
3455 strcpy(c, tmplLine->elements[j].item);
3456 insertElement(&newLine, c, element++, cfi);
3457 free(c);
3458 quoteLen = 0;
3459 }
3460 } else {
3461 int itemlen = strlen(tmplLine->elements[j].item);
3462 quoteLen += itemlen;
3463 quoteLen += strlen(tmplLine->elements[j].indent);
3464
3465 if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
3466 c = malloc(quoteLen + 1);
3467 c[0] = '\0';
3468 for (int i = firstQuotedItem; i < j + 1; i++) {
3469 strcat(c, tmplLine->elements[i].item);
3470 strcat(c, tmplLine->elements[i].indent);
3471 }
3472 insertElement(&newLine, c, element++, cfi);
3473 free(c);
3474
3475 firstQuotedItem = -1;
3476 quoteLen = 0;
3477 }
3478 }
3479 }
3480 while (tmplLine->numElements)
3481 removeElement(tmplLine, 0);
3482 if (tmplLine->elements)
3483 free(tmplLine->elements);
3484
3485 tmplLine->numElements = newLine.numElements;
3486 tmplLine->elements = newLine.elements;
3487 }
3488
3489 static void insertElement(struct singleLine *line,
3490 const char *item, int insertHere,
3491 struct configFileInfo *cfi)
3492 {
3493 struct keywordTypes *kw;
3494 char indent[2] = "";
3495
3496 /* sanity check */
3497 if (insertHere > line->numElements) {
3498 dbgPrintf
3499 ("insertElement() adjusting insertHere from %d to %d\n",
3500 insertHere, line->numElements);
3501 insertHere = line->numElements;
3502 }
3503
3504 line->elements = realloc(line->elements, (line->numElements + 1) *
3505 sizeof(*line->elements));
3506 memmove(&line->elements[insertHere + 1],
3507 &line->elements[insertHere],
3508 (line->numElements - insertHere) * sizeof(*line->elements));
3509 line->elements[insertHere].item = strdup(item);
3510
3511 kw = getKeywordByType(line->type, cfi);
3512
3513 if (line->numElements == 0) {
3514 indent[0] = '\0';
3515 } else if (insertHere == 0) {
3516 indent[0] = kw->nextChar;
3517 } else if (kw->separatorChar != '\0') {
3518 indent[0] = kw->separatorChar;
3519 } else {
3520 indent[0] = ' ';
3521 }
3522
3523 if (insertHere > 0 && line->elements[insertHere - 1].indent[0] == '\0') {
3524 /* move the end-of-line forward */
3525 line->elements[insertHere].indent =
3526 line->elements[insertHere - 1].indent;
3527 line->elements[insertHere - 1].indent = strdup(indent);
3528 } else {
3529 line->elements[insertHere].indent = strdup(indent);
3530 }
3531
3532 line->numElements++;
3533
3534 dbgPrintf("insertElement(%s, '%s%s', %d)\n",
3535 line->elements[0].item,
3536 line->elements[insertHere].item,
3537 line->elements[insertHere].indent, insertHere);
3538 }
3539
3540 static void removeElement(struct singleLine *line, int removeHere)
3541 {
3542 int i;
3543
3544 /* sanity check */
3545 if (removeHere >= line->numElements)
3546 return;
3547
3548 dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
3549 removeHere, line->elements[removeHere].item);
3550
3551 free(line->elements[removeHere].item);
3552
3553 if (removeHere > 1) {
3554 /* previous argument gets this argument's post-indentation */
3555 free(line->elements[removeHere - 1].indent);
3556 line->elements[removeHere - 1].indent =
3557 line->elements[removeHere].indent;
3558 } else {
3559 free(line->elements[removeHere].indent);
3560 }
3561
3562 /* now collapse the array, but don't bother to realloc smaller */
3563 for (i = removeHere; i < line->numElements - 1; i++)
3564 line->elements[i] = line->elements[i + 1];
3565
3566 line->numElements--;
3567 }
3568
3569 static int argNameMatch(const char *one, const char *two)
3570 {
3571 char *first, *second;
3572 char *chptra, *chptrb;
3573 int rc;
3574
3575 first = strcpy(alloca(strlen(one) + 1), one);
3576 second = strcpy(alloca(strlen(two) + 1), two);
3577
3578 chptra = strchr(first, '=');
3579 if (chptra)
3580 *chptra = '\0';
3581
3582 chptrb = strchr(second, '=');
3583 if (chptrb)
3584 *chptrb = '\0';
3585
3586 rc = strcmp(first, second);
3587
3588 if (chptra)
3589 *chptra = '=';
3590 if (chptrb)
3591 *chptrb = '=';
3592
3593 return rc;
3594 }
3595
3596 static int argHasValue(const char *arg)
3597 {
3598 char *chptr;
3599
3600 chptr = strchr(arg, '=');
3601 if (chptr)
3602 return 1;
3603 return 0;
3604 }
3605
3606 static int argValueMatch(const char *one, const char *two)
3607 {
3608 char *first, *second;
3609 char *chptra, *chptrb;
3610
3611 first = strcpy(alloca(strlen(one) + 1), one);
3612 second = strcpy(alloca(strlen(two) + 1), two);
3613
3614 chptra = strchr(first, '=');
3615 if (chptra)
3616 chptra += 1;
3617
3618 chptrb = strchr(second, '=');
3619 if (chptrb)
3620 chptrb += 1;
3621
3622 if (!chptra && !chptrb)
3623 return 0;
3624 else if (!chptra)
3625 return *chptrb - 0;
3626 else if (!chptrb)
3627 return 0 - *chptra;
3628 else
3629 return strcmp(chptra, chptrb);
3630 }
3631
3632 int updateActualImage(struct grubConfig *cfg, const char *image,
3633 const char *prefix, const char *addArgs,
3634 const char *removeArgs, int multibootArgs)
3635 {
3636 struct singleEntry *entry;
3637 struct singleLine *line, *rootLine;
3638 int index = 0;
3639 int i, k;
3640 const char **newArgs, **oldArgs;
3641 const char **arg;
3642 int useKernelArgs, useRoot;
3643 int firstElement;
3644 int *usedElements;
3645 int doreplace;
3646
3647 if (!image)
3648 return 0;
3649
3650 if (!addArgs) {
3651 newArgs = malloc(sizeof(*newArgs));
3652 *newArgs = NULL;
3653 } else {
3654 if (poptParseArgvString(addArgs, NULL, &newArgs)) {
3655 fprintf(stderr,
3656 _("grubby: error separating arguments '%s'\n"),
3657 addArgs);
3658 return 1;
3659 }
3660 }
3661
3662 if (!removeArgs) {
3663 oldArgs = malloc(sizeof(*oldArgs));
3664 *oldArgs = NULL;
3665 } else {
3666 if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
3667 fprintf(stderr,
3668 _("grubby: error separating arguments '%s'\n"),
3669 removeArgs);
3670 free(newArgs);
3671 return 1;
3672 }
3673 }
3674
3675 useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
3676 && (!multibootArgs || cfg->cfi->mbConcatArgs));
3677
3678 useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
3679 && !multibootArgs);
3680
3681 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3682
3683 if (multibootArgs && !entry->multiboot)
3684 continue;
3685
3686 /* Determine where to put the args. If this config supports
3687 * LT_KERNELARGS, use that. Otherwise use
3688 * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
3689 */
3690 if (useKernelArgs) {
3691 line = getLineByType(LT_KERNELARGS, entry->lines);
3692 if (!line) {
3693 /* no LT_KERNELARGS, need to add it */
3694 line = addLine(entry, cfg->cfi, LT_KERNELARGS,
3695 cfg->secondaryIndent, NULL);
3696 }
3697 firstElement = 1;
3698
3699 } else if (multibootArgs) {
3700 line = getLineByType(LT_HYPER, entry->lines);
3701 if (!line) {
3702 /* a multiboot entry without LT_HYPER? */
3703 continue;
3704 }
3705 firstElement = 2;
3706
3707 } else {
3708 line =
3709 getLineByType(LT_KERNEL | LT_MBMODULE |
3710 LT_KERNEL_EFI | LT_KERNEL_16,
3711 entry->lines);
3712 if (!line) {
3713 /* no LT_KERNEL or LT_MBMODULE in this entry? */
3714 continue;
3715 }
3716 firstElement = 2;
3717 }
3718
3719 /* handle the elilo case which does:
3720 * append="hypervisor args -- kernel args"
3721 */
3722 if (entry->multiboot && cfg->cfi->mbConcatArgs) {
3723 /* this is a multiboot entry, make sure there's
3724 * -- on the args line
3725 */
3726 for (i = firstElement; i < line->numElements; i++) {
3727 if (!strcmp(line->elements[i].item, "--"))
3728 break;
3729 }
3730 if (i == line->numElements) {
3731 /* assume all existing args are kernel args,
3732 * prepend -- to make it official
3733 */
3734 insertElement(line, "--", firstElement,
3735 cfg->cfi);
3736 i = firstElement;
3737 }
3738 if (!multibootArgs) {
3739 /* kernel args start after the -- */
3740 firstElement = i + 1;
3741 }
3742 } else if (cfg->cfi->mbConcatArgs) {
3743 /* this is a non-multiboot entry, remove hyper args */
3744 for (i = firstElement; i < line->numElements; i++) {
3745 if (!strcmp(line->elements[i].item, "--"))
3746 break;
3747 }
3748 if (i < line->numElements) {
3749 /* remove args up to -- */
3750 while (strcmp
3751 (line->elements[firstElement].item,
3752 "--"))
3753 removeElement(line, firstElement);
3754 /* remove -- */
3755 removeElement(line, firstElement);
3756 }
3757 }
3758
3759 usedElements = calloc(line->numElements, sizeof(*usedElements));
3760
3761 for (k = 0, arg = newArgs; *arg; arg++, k++) {
3762
3763 doreplace = 1;
3764 for (i = firstElement; i < line->numElements; i++) {
3765 if (multibootArgs && cfg->cfi->mbConcatArgs &&
3766 !strcmp(line->elements[i].item, "--")) {
3767 /* reached the end of hyper args, insert here */
3768 doreplace = 0;
3769 break;
3770 }
3771 if (usedElements[i])
3772 continue;
3773 if (!argNameMatch(line->elements[i].item, *arg)) {
3774 usedElements[i] = 1;
3775 break;
3776 }
3777 }
3778
3779 if (i < line->numElements && doreplace) {
3780 /* direct replacement */
3781 free(line->elements[i].item);
3782 line->elements[i].item = strdup(*arg);
3783
3784 } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
3785 /* root= replacement */
3786 rootLine = getLineByType(LT_ROOT, entry->lines);
3787 if (rootLine) {
3788 free(rootLine->elements[1].item);
3789 rootLine->elements[1].item =
3790 strdup(*arg + 5);
3791 } else {
3792 rootLine =
3793 addLine(entry, cfg->cfi, LT_ROOT,
3794 cfg->secondaryIndent,
3795 *arg + 5);
3796 }
3797 }
3798
3799 else {
3800 /* insert/append */
3801 insertElement(line, *arg, i, cfg->cfi);
3802 usedElements =
3803 realloc(usedElements,
3804 line->numElements *
3805 sizeof(*usedElements));
3806 memmove(&usedElements[i + 1], &usedElements[i],
3807 line->numElements - i - 1);
3808 usedElements[i] = 1;
3809
3810 /* if we updated a root= here even though
3811 * there is a LT_ROOT available we need to
3812 * remove the LT_ROOT entry (this will happen
3813 * if we switch from a device to a label) */
3814 if (useRoot && !strncmp(*arg, "root=", 5)) {
3815 rootLine =
3816 getLineByType(LT_ROOT,
3817 entry->lines);
3818 if (rootLine)
3819 removeLine(entry, rootLine);
3820 }
3821 }
3822 }
3823
3824 free(usedElements);
3825
3826 for (arg = oldArgs; *arg; arg++) {
3827 for (i = firstElement; i < line->numElements; i++) {
3828 if (multibootArgs && cfg->cfi->mbConcatArgs &&
3829 !strcmp(line->elements[i].item, "--"))
3830 /* reached the end of hyper args, stop here */
3831 break;
3832 if (!argNameMatch(line->elements[i].item, *arg)) {
3833 if (!argHasValue(*arg) ||
3834 !argValueMatch(line->elements[i].item, *arg)) {
3835 removeElement(line, i);
3836 break;
3837 }
3838 }
3839 }
3840 /* handle removing LT_ROOT line too */
3841 if (useRoot && !strncmp(*arg, "root=", 5)) {
3842 rootLine = getLineByType(LT_ROOT, entry->lines);
3843 if (rootLine)
3844 removeLine(entry, rootLine);
3845 }
3846 }
3847
3848 if (line->numElements == 1) {
3849 /* don't need the line at all (note it has to be a
3850 LT_KERNELARGS for this to happen */
3851 removeLine(entry, line);
3852 }
3853 }
3854
3855 free(newArgs);
3856 free(oldArgs);
3857
3858 return 0;
3859 }
3860
3861 int updateImage(struct grubConfig *cfg, const char *image,
3862 const char *prefix, const char *addArgs,
3863 const char *removeArgs,
3864 const char *addMBArgs, const char *removeMBArgs)
3865 {
3866 int rc = 0;
3867
3868 if (!image)
3869 return rc;
3870
3871 /* update the main args first... */
3872 if (addArgs || removeArgs)
3873 rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs,
3874 0);
3875 if (rc)
3876 return rc;
3877
3878 /* and now any multiboot args */
3879 if (addMBArgs || removeMBArgs)
3880 rc = updateActualImage(cfg, image, prefix, addMBArgs,
3881 removeMBArgs, 1);
3882 return rc;
3883 }
3884
3885 int addMBInitrd(struct grubConfig *cfg, const char *newMBKernel,
3886 const char *image, const char *prefix, const char *initrd,
3887 const char *title)
3888 {
3889 struct singleEntry *entry;
3890 struct singleLine *line, *kernelLine, *endLine = NULL;
3891 int index = 0;
3892
3893 if (!image)
3894 return 0;
3895
3896 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3897 kernelLine = getLineByType(LT_MBMODULE, entry->lines);
3898 if (!kernelLine)
3899 continue;
3900
3901 /* if title is supplied, the entry's title must match it. */
3902 if (title) {
3903 char *linetitle;
3904
3905 line =
3906 getLineByType(LT_TITLE | LT_MENUENTRY,
3907 entry->lines);
3908 if (!line)
3909 continue;
3910
3911 linetitle = extractTitle(cfg, line);
3912 if (!linetitle)
3913 continue;
3914 if (strcmp(title, linetitle)) {
3915 free(linetitle);
3916 continue;
3917 }
3918 free(linetitle);
3919 }
3920
3921 if (prefix) {
3922 int prefixLen = strlen(prefix);
3923 if (!strncmp(initrd, prefix, prefixLen))
3924 initrd += prefixLen;
3925 }
3926 endLine = getLineByType(LT_ENTRY_END, entry->lines);
3927 if (endLine)
3928 removeLine(entry, endLine);
3929 line =
3930 addLine(entry, cfg->cfi,
3931 preferredLineType(LT_MBMODULE, cfg->cfi),
3932 kernelLine->indent, initrd);
3933 if (!line)
3934 return 1;
3935 if (endLine) {
3936 line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3937 if (!line)
3938 return 1;
3939 }
3940
3941 break;
3942 }
3943
3944 return 0;
3945 }
3946
3947 int updateInitrd(struct grubConfig *cfg, const char *image,
3948 const char *prefix, const char *initrd, const char *title)
3949 {
3950 struct singleEntry *entry;
3951 struct singleLine *line, *kernelLine, *endLine = NULL;
3952 int index = 0;
3953
3954 if (!image)
3955 return 0;
3956
3957 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3958 kernelLine =
3959 getLineByType(LT_KERNEL | LT_KERNEL_EFI | LT_KERNEL_16,
3960 entry->lines);
3961 if (!kernelLine)
3962 continue;
3963
3964 /* if title is supplied, the entry's title must match it. */
3965 if (title) {
3966 char *linetitle;
3967
3968 line =
3969 getLineByType(LT_TITLE | LT_MENUENTRY,
3970 entry->lines);
3971 if (!line)
3972 continue;
3973
3974 linetitle = extractTitle(cfg, line);
3975 if (!linetitle)
3976 continue;
3977 if (strcmp(title, linetitle)) {
3978 free(linetitle);
3979 continue;
3980 }
3981 free(linetitle);
3982 }
3983
3984 line =
3985 getLineByType(LT_INITRD | LT_INITRD_EFI | LT_INITRD_16,
3986 entry->lines);
3987 if (line)
3988 removeLine(entry, line);
3989 if (prefix) {
3990 int prefixLen = strlen(prefix);
3991 if (!strncmp(initrd, prefix, prefixLen))
3992 initrd += prefixLen;
3993 }
3994 endLine = getLineByType(LT_ENTRY_END, entry->lines);
3995 if (endLine)
3996 removeLine(entry, endLine);
3997 enum lineType_e lt;
3998 switch (kernelLine->type) {
3999 case LT_KERNEL:
4000 lt = LT_INITRD;
4001 break;
4002 case LT_KERNEL_EFI:
4003 lt = LT_INITRD_EFI;
4004 break;
4005 case LT_KERNEL_16:
4006 lt = LT_INITRD_16;
4007 break;
4008 default:
4009 lt = preferredLineType(LT_INITRD, cfg->cfi);
4010 }
4011 line = addLine(entry, cfg->cfi, lt, kernelLine->indent, initrd);
4012 if (!line)
4013 return 1;
4014 if (endLine) {
4015 line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
4016 if (!line)
4017 return 1;
4018 }
4019
4020 break;
4021 }
4022
4023 return 0;
4024 }
4025
4026 int checkDeviceBootloader(const char *device, const unsigned char *boot)
4027 {
4028 int fd;
4029 unsigned char bootSect[512];
4030 int offset;
4031
4032 fd = open(device, O_RDONLY);
4033 if (fd < 0) {
4034 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
4035 device, strerror(errno));
4036 return 1;
4037 }
4038
4039 if (read(fd, bootSect, 512) != 512) {
4040 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4041 device, strerror(errno));
4042 return 1;
4043 }
4044 close(fd);
4045
4046 /* first three bytes should match, a jmp short should be in there */
4047 if (memcmp(boot, bootSect, 3))
4048 return 0;
4049
4050 if (boot[1] == JMP_SHORT_OPCODE) {
4051 offset = boot[2] + 2;
4052 } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
4053 offset = (boot[3] << 8) + boot[2] + 2;
4054 } else if (boot[0] == JMP_SHORT_OPCODE) {
4055 offset = boot[1] + 2;
4056 /*
4057 * it looks like grub, when copying stage1 into the mbr,
4058 * patches stage1 right after the JMP location, replacing
4059 * other instructions such as JMPs for NOOPs. So, relax the
4060 * check a little bit by skipping those different bytes.
4061 */
4062 if ((bootSect[offset + 1] == NOOP_OPCODE)
4063 && (bootSect[offset + 2] == NOOP_OPCODE)) {
4064 offset = offset + 3;
4065 }
4066 } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
4067 offset = (boot[2] << 8) + boot[1] + 2;
4068 } else {
4069 return 0;
4070 }
4071
4072 if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
4073 return 0;
4074
4075 return 2;
4076 }
4077
4078 int checkLiloOnRaid(char *mdDev, const unsigned char *boot)
4079 {
4080 int fd;
4081 char buf[65536];
4082 char *end;
4083 char *chptr;
4084 char *chptr2;
4085 int rc;
4086
4087 /* it's on raid; we need to parse /proc/mdstat and check all of the
4088 *raw* devices listed in there */
4089
4090 if (!strncmp(mdDev, "/dev/", 5))
4091 mdDev += 5;
4092
4093 if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
4094 fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
4095 strerror(errno));
4096 return 2;
4097 }
4098
4099 rc = read(fd, buf, sizeof(buf) - 1);
4100 if (rc < 0 || rc == (sizeof(buf) - 1)) {
4101 fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
4102 strerror(errno));
4103 close(fd);
4104 return 2;
4105 }
4106 close(fd);
4107 buf[rc] = '\0';
4108
4109 chptr = buf;
4110 while (*chptr) {
4111 end = strchr(chptr, '\n');
4112 if (!end)
4113 break;
4114 *end = '\0';
4115
4116 if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
4117 chptr[strlen(mdDev)] == ' ') {
4118
4119 /* found the device */
4120 while (*chptr && *chptr != ':')
4121 chptr++;
4122 chptr++;
4123 while (*chptr && isspace(*chptr))
4124 chptr++;
4125
4126 /* skip the "active" bit */
4127 while (*chptr && !isspace(*chptr))
4128 chptr++;
4129 while (*chptr && isspace(*chptr))
4130 chptr++;
4131
4132 /* skip the raid level */
4133 while (*chptr && !isspace(*chptr))
4134 chptr++;
4135 while (*chptr && isspace(*chptr))
4136 chptr++;
4137
4138 /* everything else is partition stuff */
4139 while (*chptr) {
4140 chptr2 = chptr;
4141 while (*chptr2 && *chptr2 != '[')
4142 chptr2++;
4143 if (!*chptr2)
4144 break;
4145
4146 /* yank off the numbers at the end */
4147 chptr2--;
4148 while (isdigit(*chptr2) && chptr2 > chptr)
4149 chptr2--;
4150 chptr2++;
4151 *chptr2 = '\0';
4152
4153 /* Better, now we need the /dev/ back. We're
4154 * done with everything before this point, so
4155 * we can just put the /dev/ part there.
4156 * There will always be room. */
4157 memcpy(chptr - 5, "/dev/", 5);
4158 rc = checkDeviceBootloader(chptr - 5, boot);
4159 if (rc != 2) {
4160 return rc;
4161 }
4162
4163 chptr = chptr2 + 1;
4164 /* skip the [11] bit */
4165 while (*chptr && !isspace(*chptr))
4166 chptr++;
4167 /* and move to the next one */
4168 while (*chptr && isspace(*chptr))
4169 chptr++;
4170 }
4171
4172 /* we're good to go */
4173 return 2;
4174 }
4175
4176 chptr = end + 1;
4177 }
4178
4179 fprintf(stderr,
4180 _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
4181 mdDev);
4182 return 0;
4183 }
4184
4185 int checkForLilo(struct grubConfig *config)
4186 {
4187 int fd;
4188 unsigned char boot[512];
4189 struct singleLine *line;
4190
4191 for (line = config->theLines; line; line = line->next)
4192 if (line->type == LT_BOOT)
4193 break;
4194
4195 if (!line) {
4196 fprintf(stderr,
4197 _
4198 ("grubby: no boot line found in lilo configuration\n"));
4199 return 1;
4200 }
4201
4202 if (line->numElements != 2)
4203 return 1;
4204
4205 fd = open("/boot/boot.b", O_RDONLY);
4206 if (fd < 0) {
4207 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
4208 "/boot/boot.b", strerror(errno));
4209 return 1;
4210 }
4211
4212 if (read(fd, boot, 512) != 512) {
4213 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4214 "/boot/boot.b", strerror(errno));
4215 return 1;
4216 }
4217 close(fd);
4218
4219 if (!strncmp("/dev/md", line->elements[1].item, 7))
4220 return checkLiloOnRaid(line->elements[1].item, boot);
4221
4222 return checkDeviceBootloader(line->elements[1].item, boot);
4223 }
4224
4225 int checkForGrub2(struct grubConfig *config)
4226 {
4227 if (!access("/etc/grub.d/", R_OK))
4228 return 2;
4229
4230 return 1;
4231 }
4232
4233 int checkForGrub(struct grubConfig *config)
4234 {
4235 int fd;
4236 unsigned char bootSect[512];
4237 char *boot;
4238 int onSuse = isSuseSystem();
4239
4240 if (onSuse) {
4241 if (parseSuseGrubConf(NULL, &boot))
4242 return 0;
4243 } else {
4244 if (parseSysconfigGrub(NULL, &boot))
4245 return 0;
4246 }
4247
4248 /* assume grub is not installed -- not an error condition */
4249 if (!boot)
4250 return 0;
4251
4252 fd = open("/boot/grub/stage1", O_RDONLY);
4253 if (fd < 0)
4254 /* this doesn't exist if grub hasn't been installed */
4255 return 0;
4256
4257 if (read(fd, bootSect, 512) != 512) {
4258 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4259 "/boot/grub/stage1", strerror(errno));
4260 close(fd);
4261 return 1;
4262 }
4263 close(fd);
4264
4265 /* The more elaborate checks do not work on SuSE. The checks done
4266 * seem to be reasonble (at least for now), so just return success
4267 */
4268 if (onSuse)
4269 return 2;
4270
4271 return checkDeviceBootloader(boot, bootSect);
4272 }
4273
4274 int checkForExtLinux(struct grubConfig *config)
4275 {
4276 int fd;
4277 unsigned char bootSect[512];
4278 char *boot;
4279 char executable[] = "/boot/extlinux/extlinux";
4280
4281 printf("entered: checkForExtLinux()\n");
4282
4283 if (parseSysconfigGrub(NULL, &boot))
4284 return 0;
4285
4286 /* assume grub is not installed -- not an error condition */
4287 if (!boot)
4288 return 0;
4289
4290 fd = open(executable, O_RDONLY);
4291 if (fd < 0)
4292 /* this doesn't exist if grub hasn't been installed */
4293 return 0;
4294
4295 if (read(fd, bootSect, 512) != 512) {
4296 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4297 executable, strerror(errno));
4298 return 1;
4299 }
4300 close(fd);
4301
4302 return checkDeviceBootloader(boot, bootSect);
4303 }
4304
4305 int checkForYaboot(struct grubConfig *config)
4306 {
4307 /*
4308 * This is a simplistic check that we consider good enough for own puporses
4309 *
4310 * If we were to properly check if yaboot is *installed* we'd need to:
4311 * 1) get the system boot device (LT_BOOT)
4312 * 2) considering it's a raw filesystem, check if the yaboot binary matches
4313 * the content on the boot device
4314 * 3) if not, copy the binary to a temporary file and run "addnote" on it
4315 * 4) check again if binary and boot device contents match
4316 */
4317 if (!access("/etc/yaboot.conf", R_OK))
4318 return 2;
4319
4320 return 1;
4321 }
4322
4323 int checkForElilo(struct grubConfig *config)
4324 {
4325 if (!access("/etc/elilo.conf", R_OK))
4326 return 2;
4327
4328 return 1;
4329 }
4330
4331 static char *getRootSpecifier(char *str)
4332 {
4333 char *idx, *rootspec = NULL;
4334
4335 if (*str == '(') {
4336 idx = rootspec = strdup(str);
4337 while (*idx && (*idx != ')') && (!isspace(*idx)))
4338 idx++;
4339 *(++idx) = '\0';
4340 }
4341 return rootspec;
4342 }
4343
4344 static char *getInitrdVal(struct grubConfig *config,
4345 const char *prefix, struct singleLine *tmplLine,
4346 const char *newKernelInitrd,
4347 const char **extraInitrds, int extraInitrdCount)
4348 {
4349 char *initrdVal, *end;
4350 int i;
4351 size_t totalSize;
4352 size_t prefixLen;
4353 char separatorChar;
4354
4355 prefixLen = strlen(prefix);
4356 totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */ ;
4357
4358 for (i = 0; i < extraInitrdCount; i++) {
4359 totalSize += sizeof(separatorChar);
4360 totalSize += strlen(extraInitrds[i]) - prefixLen;
4361 }
4362
4363 initrdVal = end = malloc(totalSize);
4364
4365 end = stpcpy(end, newKernelInitrd + prefixLen);
4366
4367 separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
4368 for (i = 0; i < extraInitrdCount; i++) {
4369 const char *extraInitrd;
4370 int j;
4371
4372 extraInitrd = extraInitrds[i] + prefixLen;
4373 /* Don't add entries that are already there */
4374 if (tmplLine != NULL) {
4375 for (j = 2; j < tmplLine->numElements; j++)
4376 if (strcmp
4377 (extraInitrd,
4378 tmplLine->elements[j].item) == 0)
4379 break;
4380
4381 if (j != tmplLine->numElements)
4382 continue;
4383 }
4384
4385 *end++ = separatorChar;
4386 end = stpcpy(end, extraInitrd);
4387 }
4388
4389 return initrdVal;
4390 }
4391
4392 int addNewKernel(struct grubConfig *config, struct singleEntry *template,
4393 const char *prefix,
4394 const char *newKernelPath, const char *newKernelTitle,
4395 const char *newKernelArgs, const char *newKernelInitrd,
4396 const char **extraInitrds, int extraInitrdCount,
4397 const char *newMBKernel, const char *newMBKernelArgs,
4398 const char *newDevTreePath, int newIndex)
4399 {
4400 struct singleEntry *new, *entry, *prev = NULL;
4401 struct singleLine *newLine = NULL, *tmplLine = NULL, *masterLine = NULL;
4402 int needs;
4403 char *indexs;
4404 char *chptr;
4405 int rc;
4406
4407 if (!newKernelPath)
4408 return 0;
4409
4410 rc = asprintf(&indexs, "%d", newIndex);
4411 if (rc < 0)
4412 return 1;
4413
4414 /* if the newKernelTitle is too long silently munge it into something
4415 * we can live with. truncating is first check, then we'll just mess with
4416 * it until it looks better */
4417 if (config->cfi->maxTitleLength &&
4418 (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
4419 char *buf = alloca(config->cfi->maxTitleLength + 7);
4420 char *numBuf = alloca(config->cfi->maxTitleLength + 1);
4421 int i = 1;
4422
4423 sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength,
4424 newKernelTitle);
4425 while (findEntryByPath(config, buf, NULL, NULL)) {
4426 sprintf(numBuf, "%d", i++);
4427 strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
4428 }
4429
4430 newKernelTitle = buf + 6;
4431 }
4432
4433 new = malloc(sizeof(*new));
4434 new->skip = 0;
4435 new->multiboot = 0;
4436 new->lines = NULL;
4437 entry = config->entries;
4438 for (unsigned int i = 0; i < newIndex; i++) {
4439 if (!entry)
4440 break;
4441 prev = entry;
4442 entry = entry->next;
4443 }
4444 new->next = entry;
4445
4446 if (prev)
4447 prev->next = new;
4448 else
4449 config->entries = new;
4450
4451 /* copy/update from the template */
4452 needs = NEED_KERNEL | NEED_TITLE;
4453 if (newKernelInitrd)
4454 needs |= NEED_INITRD;
4455 if (newMBKernel) {
4456 needs |= NEED_MB;
4457 new->multiboot = 1;
4458 }
4459 if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))
4460 needs |= NEED_DEVTREE;
4461
4462 if (template) {
4463 for (masterLine = template->lines;
4464 masterLine && (tmplLine = lineDup(masterLine));
4465 lineFree(tmplLine), masterLine = masterLine->next) {
4466 dbgPrintf("addNewKernel processing %d\n",
4467 tmplLine->type);
4468
4469 /* skip comments */
4470 chptr = tmplLine->indent;
4471 while (*chptr && isspace(*chptr))
4472 chptr++;
4473 if (*chptr == '#')
4474 continue;
4475
4476 if (iskernel(tmplLine->type)
4477 && tmplLine->numElements >= 2) {
4478 if (!template->multiboot && (needs & NEED_MB)) {
4479 /* it's not a multiboot template and
4480 * this is the kernel line. Try to
4481 * be intelligent about inserting the
4482 * hypervisor at the same time.
4483 */
4484 if (config->cfi->mbHyperFirst) {
4485 /* insert the hypervisor first */
4486 newLine =
4487 addLine(new, config->cfi,
4488 LT_HYPER,
4489 tmplLine->indent,
4490 newMBKernel +
4491 strlen(prefix));
4492 /* set up for adding the
4493 * kernel line */
4494 free(tmplLine->indent);
4495 tmplLine->indent =
4496 strdup(config->
4497 secondaryIndent);
4498 needs &= ~NEED_MB;
4499 }
4500 if (needs & NEED_KERNEL) {
4501 /* use addLineTmpl to
4502 * preserve line elements,
4503 * otherwise we could just
4504 * call addLine.
4505 * Unfortunately this means
4506 * making some changes to the
4507 * template such as the
4508 * indent change above and
4509 * the type change below.
4510 */
4511 struct keywordTypes *mbm_kw =
4512 getKeywordByType
4513 (LT_MBMODULE, config->cfi);
4514 if (mbm_kw) {
4515 tmplLine->type =
4516 LT_MBMODULE;
4517 free(tmplLine->
4518 elements[0].item);
4519 tmplLine->elements[0].
4520 item =
4521 strdup(mbm_kw->key);
4522 }
4523 newLine =
4524 addLineTmpl(new, tmplLine,
4525 newLine,
4526 newKernelPath +
4527 strlen(prefix),
4528 config->cfi);
4529 needs &= ~NEED_KERNEL;
4530 }
4531 if (needs & NEED_MB) { /* !mbHyperFirst */
4532 newLine =
4533 addLine(new, config->cfi,
4534 LT_HYPER,
4535 config->
4536 secondaryIndent,
4537 newMBKernel +
4538 strlen(prefix));
4539 needs &= ~NEED_MB;
4540 }
4541 } else if (needs & NEED_KERNEL) {
4542 newLine =
4543 addLineTmpl(new, tmplLine, newLine,
4544 newKernelPath +
4545 strlen(prefix),
4546 config->cfi);
4547 needs &= ~NEED_KERNEL;
4548 }
4549
4550 } else if (tmplLine->type == LT_HYPER &&
4551 tmplLine->numElements >= 2) {
4552 if (needs & NEED_MB) {
4553 newLine =
4554 addLineTmpl(new, tmplLine, newLine,
4555 newMBKernel +
4556 strlen(prefix),
4557 config->cfi);
4558 needs &= ~NEED_MB;
4559 }
4560
4561 } else if (tmplLine->type == LT_MBMODULE &&
4562 tmplLine->numElements >= 2) {
4563 if (new->multiboot) {
4564 if (needs & NEED_KERNEL) {
4565 newLine =
4566 addLineTmpl(new, tmplLine,
4567 newLine,
4568 newKernelPath +
4569 strlen(prefix),
4570 config->cfi);
4571 needs &= ~NEED_KERNEL;
4572 } else if (config->cfi->mbInitRdIsModule
4573 && (needs & NEED_INITRD)) {
4574 char *initrdVal;
4575 initrdVal =
4576 getInitrdVal(config, prefix,
4577 tmplLine,
4578 newKernelInitrd,
4579 extraInitrds,
4580 extraInitrdCount);
4581 newLine =
4582 addLineTmpl(new, tmplLine,
4583 newLine,
4584 initrdVal,
4585 config->cfi);
4586 free(initrdVal);
4587 needs &= ~NEED_INITRD;
4588 }
4589 } else if (needs & NEED_KERNEL) {
4590 /* template is multi but new is not,
4591 * insert the kernel in the first
4592 * module slot
4593 */
4594 tmplLine->type =
4595 preferredLineType(LT_KERNEL,
4596 config->cfi);
4597 free(tmplLine->elements[0].item);
4598 tmplLine->elements[0].item =
4599 strdup(getKeywordByType
4600 (tmplLine->type,
4601 config->cfi)->key);
4602 newLine =
4603 addLineTmpl(new, tmplLine, newLine,
4604 newKernelPath +
4605 strlen(prefix),
4606 config->cfi);
4607 needs &= ~NEED_KERNEL;
4608 } else if (needs & NEED_INITRD) {
4609 char *initrdVal;
4610 /* template is multi but new is not,
4611 * insert the initrd in the second
4612 * module slot
4613 */
4614 tmplLine->type =
4615 preferredLineType(LT_INITRD,
4616 config->cfi);
4617 free(tmplLine->elements[0].item);
4618 tmplLine->elements[0].item =
4619 strdup(getKeywordByType
4620 (tmplLine->type,
4621 config->cfi)->key);
4622 initrdVal =
4623 getInitrdVal(config, prefix,
4624 tmplLine,
4625 newKernelInitrd,
4626 extraInitrds,
4627 extraInitrdCount);
4628 newLine =
4629 addLineTmpl(new, tmplLine, newLine,
4630 initrdVal, config->cfi);
4631 free(initrdVal);
4632 needs &= ~NEED_INITRD;
4633 }
4634
4635 } else if (isinitrd(tmplLine->type)
4636 && tmplLine->numElements >= 2) {
4637 if (needs & NEED_INITRD && new->multiboot
4638 && !template->multiboot
4639 && config->cfi->mbInitRdIsModule) {
4640 /* make sure we don't insert the
4641 * module initrd before the module
4642 * kernel... if we don't do it here,
4643 * it will be inserted following the
4644 * template.
4645 */
4646 if (!needs & NEED_KERNEL) {
4647 char *initrdVal;
4648
4649 initrdVal =
4650 getInitrdVal(config, prefix,
4651 tmplLine,
4652 newKernelInitrd,
4653 extraInitrds,
4654 extraInitrdCount);
4655 newLine =
4656 addLine(new, config->cfi,
4657 LT_MBMODULE,
4658 config->
4659 secondaryIndent,
4660 initrdVal);
4661 free(initrdVal);
4662 needs &= ~NEED_INITRD;
4663 }
4664 } else if (needs & NEED_INITRD) {
4665 char *initrdVal;
4666 initrdVal =
4667 getInitrdVal(config, prefix,
4668 tmplLine,
4669 newKernelInitrd,
4670 extraInitrds,
4671 extraInitrdCount);
4672 newLine =
4673 addLineTmpl(new, tmplLine, newLine,
4674 initrdVal, config->cfi);
4675 free(initrdVal);
4676 needs &= ~NEED_INITRD;
4677 }
4678
4679 } else if (tmplLine->type == LT_MENUENTRY &&
4680 (needs & NEED_TITLE)) {
4681 requote(tmplLine, config->cfi);
4682 char *nkt = malloc(strlen(newKernelTitle) + 3);
4683 strcpy(nkt, "'");
4684 strcat(nkt, newKernelTitle);
4685 strcat(nkt, "'");
4686 newLine =
4687 addLineTmpl(new, tmplLine, newLine, nkt,
4688 config->cfi);
4689 free(nkt);
4690 needs &= ~NEED_TITLE;
4691 } else if (tmplLine->type == LT_TITLE &&
4692 (needs & NEED_TITLE)) {
4693 if (tmplLine->numElements >= 2) {
4694 newLine =
4695 addLineTmpl(new, tmplLine, newLine,
4696 newKernelTitle,
4697 config->cfi);
4698 needs &= ~NEED_TITLE;
4699 } else if (tmplLine->numElements == 1 &&
4700 config->cfi->titleBracketed) {
4701 /* addLineTmpl doesn't handle
4702 * titleBracketed */
4703 newLine =
4704 addLine(new, config->cfi, LT_TITLE,
4705 tmplLine->indent,
4706 newKernelTitle);
4707 needs &= ~NEED_TITLE;
4708 }
4709 } else if (tmplLine->type == LT_ECHO) {
4710 requote(tmplLine, config->cfi);
4711 static const char *prefix = "'Loading ";
4712 if (tmplLine->numElements > 1 &&
4713 strstr(tmplLine->elements[1].item, prefix)
4714 && masterLine->next
4715 && iskernel(masterLine->next->type)) {
4716 char *newTitle =
4717 malloc(strlen(prefix) +
4718 strlen(newKernelTitle) + 2);
4719
4720 strcpy(newTitle, prefix);
4721 strcat(newTitle, newKernelTitle);
4722 strcat(newTitle, "'");
4723 newLine =
4724 addLine(new, config->cfi, LT_ECHO,
4725 tmplLine->indent, newTitle);
4726 free(newTitle);
4727 } else {
4728 /* pass through other lines from the
4729 * template */
4730 newLine =
4731 addLineTmpl(new, tmplLine, newLine,
4732 NULL, config->cfi);
4733 }
4734 } else if (tmplLine->type == LT_DEVTREE &&
4735 tmplLine->numElements == 2
4736 && newDevTreePath) {
4737 newLine =
4738 addLineTmpl(new, tmplLine, newLine,
4739 newDevTreePath + strlen(prefix),
4740 config->cfi);
4741 needs &= ~NEED_DEVTREE;
4742 } else if (tmplLine->type == LT_ENTRY_END
4743 && needs & NEED_DEVTREE) {
4744 const char *ndtp = newDevTreePath;
4745 if (!strncmp
4746 (newDevTreePath, prefix, strlen(prefix)))
4747 ndtp += strlen(prefix);
4748 newLine = addLine(new, config->cfi, LT_DEVTREE,
4749 config->secondaryIndent,
4750 ndtp);
4751 needs &= ~NEED_DEVTREE;
4752 newLine =
4753 addLineTmpl(new, tmplLine, newLine, NULL,
4754 config->cfi);
4755 } else {
4756 /* pass through other lines from the template */
4757 newLine =
4758 addLineTmpl(new, tmplLine, newLine, NULL,
4759 config->cfi);
4760 }
4761 }
4762
4763 } else {
4764 /* don't have a template, so start the entry with the
4765 * appropriate starting line
4766 */
4767 switch (config->cfi->entryStart) {
4768 case LT_KERNEL:
4769 case LT_KERNEL_EFI:
4770 case LT_KERNEL_16:
4771 if (new->multiboot && config->cfi->mbHyperFirst) {
4772 /* fall through to LT_HYPER */
4773 } else {
4774 newLine = addLine(new, config->cfi,
4775 preferredLineType(LT_KERNEL,
4776 config->
4777 cfi),
4778 config->primaryIndent,
4779 newKernelPath +
4780 strlen(prefix));
4781 needs &= ~NEED_KERNEL;
4782 break;
4783 }
4784
4785 case LT_HYPER:
4786 newLine = addLine(new, config->cfi, LT_HYPER,
4787 config->primaryIndent,
4788 newMBKernel + strlen(prefix));
4789 needs &= ~NEED_MB;
4790 break;
4791
4792 case LT_MENUENTRY:{
4793 char *nkt = malloc(strlen(newKernelTitle) + 3);
4794 strcpy(nkt, "'");
4795 strcat(nkt, newKernelTitle);
4796 strcat(nkt, "'");
4797 newLine =
4798 addLine(new, config->cfi, LT_MENUENTRY,
4799 config->primaryIndent, nkt);
4800 free(nkt);
4801 needs &= ~NEED_TITLE;
4802 needs |= NEED_END;
4803 break;
4804 }
4805 case LT_TITLE:
4806 if (useextlinuxmenu != 0) { // We just need useextlinuxmenu to not be zero (set above)
4807 char *templabel;
4808 int x = 0, y = 0;
4809
4810 templabel = strdup(newKernelTitle);
4811 while (templabel[x]) {
4812 if (templabel[x] == ' ') {
4813 y = x;
4814 while (templabel[y]) {
4815 templabel[y] =
4816 templabel[y + 1];
4817 y++;
4818 }
4819 }
4820 x++;
4821 }
4822 newLine = addLine(new, config->cfi, LT_TITLE,
4823 config->primaryIndent,
4824 templabel);
4825 free(templabel);
4826 } else {
4827 newLine = addLine(new, config->cfi, LT_TITLE,
4828 config->primaryIndent,
4829 newKernelTitle);
4830 }
4831 needs &= ~NEED_TITLE;
4832 break;
4833
4834 default:
4835 abort();
4836 }
4837 }
4838
4839 struct singleLine *endLine = NULL;
4840 endLine = getLineByType(LT_ENTRY_END, new->lines);
4841 if (endLine) {
4842 removeLine(new, endLine);
4843 needs |= NEED_END;
4844 }
4845
4846 /* add the remainder of the lines, i.e. those that either
4847 * weren't present in the template, or in the case of no template,
4848 * all the lines following the entryStart.
4849 */
4850 if (needs & NEED_TITLE) {
4851 newLine = addLine(new, config->cfi, LT_TITLE,
4852 config->secondaryIndent, newKernelTitle);
4853 needs &= ~NEED_TITLE;
4854 }
4855 if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
4856 newLine = addLine(new, config->cfi, LT_HYPER,
4857 config->secondaryIndent,
4858 newMBKernel + strlen(prefix));
4859 needs &= ~NEED_MB;
4860 }
4861 if (needs & NEED_KERNEL) {
4862 newLine = addLine(new, config->cfi,
4863 (new->multiboot
4864 && getKeywordByType(LT_MBMODULE,
4865 config->cfi))
4866 ? LT_MBMODULE : preferredLineType(LT_KERNEL,
4867 config->
4868 cfi),
4869 config->secondaryIndent,
4870 newKernelPath + strlen(prefix));
4871 needs &= ~NEED_KERNEL;
4872 }
4873 if (needs & NEED_MB) {
4874 newLine = addLine(new, config->cfi, LT_HYPER,
4875 config->secondaryIndent,
4876 newMBKernel + strlen(prefix));
4877 needs &= ~NEED_MB;
4878 }
4879 if (needs & NEED_INITRD) {
4880 char *initrdVal;
4881 initrdVal =
4882 getInitrdVal(config, prefix, NULL, newKernelInitrd,
4883 extraInitrds, extraInitrdCount);
4884 newLine =
4885 addLine(new, config->cfi,
4886 (new->multiboot
4887 && getKeywordByType(LT_MBMODULE, config->cfi))
4888 ? LT_MBMODULE : preferredLineType(LT_INITRD,
4889 config->cfi),
4890 config->secondaryIndent, initrdVal);
4891 free(initrdVal);
4892 needs &= ~NEED_INITRD;
4893 }
4894 if (needs & NEED_DEVTREE) {
4895 newLine = addLine(new, config->cfi, LT_DEVTREE,
4896 config->secondaryIndent, newDevTreePath);
4897 needs &= ~NEED_DEVTREE;
4898 }
4899
4900 /* NEEDS_END must be last on bootloaders that need it... */
4901 if (needs & NEED_END) {
4902 newLine = addLine(new, config->cfi, LT_ENTRY_END,
4903 config->secondaryIndent, NULL);
4904 needs &= ~NEED_END;
4905 }
4906
4907 if (needs) {
4908 printf(_("grubby: needs=%d, aborting\n"), needs);
4909 abort();
4910 }
4911
4912 if (updateImage(config, indexs, prefix, newKernelArgs, NULL,
4913 newMBKernelArgs, NULL)) {
4914 config->isModified = 1;
4915 return 1;
4916 }
4917
4918 return 0;
4919 }
4920
4921 int main(int argc, const char **argv)
4922 {
4923 poptContext optCon;
4924 const char *grubConfig = NULL;
4925 char *outputFile = NULL;
4926 int arg = 0;
4927 int flags = 0;
4928 int badImageOkay = 0;
4929 int configureGrub2 = 0;
4930 int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4931 int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4932 int configureExtLinux = 0;
4933 int bootloaderProbe = 0;
4934 int extraInitrdCount = 0;
4935 char *updateKernelPath = NULL;
4936 char *newKernelPath = NULL;
4937 char *removeKernelPath = NULL;
4938 char *newKernelArgs = NULL;
4939 char *newKernelInitrd = NULL;
4940 char *newKernelTitle = NULL;
4941 char *newDevTreePath = NULL;
4942 char *newMBKernel = NULL;
4943 char *newMBKernelArgs = NULL;
4944 int newIndex = 0;
4945 char *removeMBKernelArgs = NULL;
4946 char *removeMBKernel = NULL;
4947 char *bootPrefix = NULL;
4948 char *defaultKernel = NULL;
4949 char *removeArgs = NULL;
4950 char *kernelInfo = NULL;
4951 char *extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4952 char *envPath = NULL;
4953 const char *chptr = NULL;
4954 struct configFileInfo *cfi = NULL;
4955 struct grubConfig *config;
4956 struct singleEntry *template = NULL;
4957 int copyDefault = 0, makeDefault = 0;
4958 int displayDefault = 0;
4959 int displayDefaultIndex = 0;
4960 int displayDefaultTitle = 0;
4961 int defaultIndex = -1;
4962 struct poptOption options[] = {
4963 {"add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4964 _("add an entry for the specified kernel"), _("kernel-path")},
4965 {"add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
4966 _("add an entry for the specified multiboot kernel"), NULL},
4967 {"args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
4968 _("default arguments for the new kernel or new arguments for "
4969 "kernel being updated"), _("args")},
4970 {"mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
4971 _("default arguments for the new multiboot kernel or "
4972 "new arguments for multiboot kernel being updated"), NULL},
4973 {"bad-image-okay", 0, 0, &badImageOkay, 0,
4974 _
4975 ("don't sanity check images in boot entries (for testing only)"),
4976 NULL},
4977 {"boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4978 _
4979 ("filesystem which contains /boot directory (for testing only)"),
4980 _("bootfs")},
4981 #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4982 {"bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4983 _("check which bootloader is installed on boot sector")},
4984 #endif
4985 {"config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4986 _("path to grub config file to update (\"-\" for stdin)"),
4987 _("path")},
4988 {"copy-default", 0, 0, &copyDefault, 0,
4989 _("use the default boot entry as a template for the new entry "
4990 "being added; if the default is not a linux image, or if "
4991 "the kernel referenced by the default image does not exist, "
4992 "the first linux entry whose kernel does exist is used as the "
4993 "template"), NULL},
4994 {"debug", 0, 0, &debug, 0,
4995 _("print debugging information for failures")},
4996 {"default-kernel", 0, 0, &displayDefault, 0,
4997 _("display the path of the default kernel")},
4998 {"default-index", 0, 0, &displayDefaultIndex, 0,
4999 _("display the index of the default kernel")},
5000 {"default-title", 0, 0, &displayDefaultTitle, 0,
5001 _("display the title of the default kernel")},
5002 {"devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,
5003 _("device tree file for new stanza"), _("dtb-path")},
5004 {"devtreedir", 0, POPT_ARG_STRING, &newDevTreePath, 0,
5005 _("device tree directory for new stanza"), _("dtb-path")},
5006 {"elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
5007 _("configure elilo bootloader")},
5008 {"efi", 0, POPT_ARG_NONE, &isEfi, 0,
5009 _("force grub2 stanzas to use efi")},
5010 {"env", 0, POPT_ARG_STRING, &envPath, 0,
5011 _("path for environment data"),
5012 _("path")},
5013 {"extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
5014 _("configure extlinux bootloader (from syslinux)")},
5015 {"grub", 0, POPT_ARG_NONE, &configureGrub, 0,
5016 _("configure grub bootloader")},
5017 {"grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
5018 _("configure grub2 bootloader")},
5019 {"info", 0, POPT_ARG_STRING, &kernelInfo, 0,
5020 _("display boot information for specified kernel"),
5021 _("kernel-path")},
5022 {"initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
5023 _("initrd image for the new kernel"), _("initrd-path")},
5024 {"extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
5025 _
5026 ("auxiliary initrd image for things other than the new kernel"),
5027 _("initrd-path")},
5028 {"lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
5029 _("configure lilo bootloader")},
5030 {"make-default", 0, 0, &makeDefault, 0,
5031 _("make the newly added entry the default boot entry"), NULL},
5032 {"output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
5033 _("path to output updated config file (\"-\" for stdout)"),
5034 _("path")},
5035 {"remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
5036 _("remove kernel arguments"), NULL},
5037 {"remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
5038 _("remove multiboot kernel arguments"), NULL},
5039 {"remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
5040 _("remove all entries for the specified kernel"),
5041 _("kernel-path")},
5042 {"remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
5043 _("remove all entries for the specified multiboot kernel"),
5044 NULL},
5045 {"set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
5046 _("make the first entry referencing the specified kernel "
5047 "the default"), _("kernel-path")},
5048 {"set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
5049 _("make the given entry index the default entry"),
5050 _("entry-index")},
5051 {"set-index", 0, POPT_ARG_INT, &newIndex, 0,
5052 _("use the given index when creating a new entry"),
5053 _("entry-index")},
5054 {"silo", 0, POPT_ARG_NONE, &configureSilo, 0,
5055 _("configure silo bootloader")},
5056 {"title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
5057 _("title to use for the new kernel entry"), _("entry-title")},
5058 {"update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
5059 _("updated information for the specified kernel"),
5060 _("kernel-path")},
5061 {"version", 'v', 0, NULL, 'v',
5062 _("print the version of this program and exit"), NULL},
5063 {"yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
5064 _("configure yaboot bootloader")},
5065 {"zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
5066 _("configure zipl bootloader")},
5067 POPT_AUTOHELP {0, 0, 0, 0, 0}
5068 };
5069
5070 useextlinuxmenu = 0;
5071
5072 int i = 0;
5073 for (int j = 1; j < argc; j++)
5074 i += strlen(argv[j]) + 1;
5075 saved_command_line = malloc(i);
5076 if (!saved_command_line) {
5077 fprintf(stderr, "grubby: %m\n");
5078 exit(1);
5079 }
5080 saved_command_line[0] = '\0';
5081 for (int j = 1; j < argc; j++) {
5082 strcat(saved_command_line, argv[j]);
5083 strncat(saved_command_line, j == argc - 1 ? "" : " ", 1);
5084 }
5085
5086 optCon = poptGetContext("grubby", argc, argv, options, 0);
5087 poptReadDefaultConfig(optCon, 1);
5088
5089 while ((arg = poptGetNextOpt(optCon)) >= 0) {
5090 switch (arg) {
5091 case 'v':
5092 printf("grubby version %s\n", VERSION);
5093 exit(0);
5094 break;
5095 case 'i':
5096 if (extraInitrdCount < MAX_EXTRA_INITRDS) {
5097 extraInitrds[extraInitrdCount++] =
5098 strdup(poptGetOptArg(optCon));
5099 } else {
5100 fprintf(stderr,
5101 _
5102 ("grubby: extra initrd maximum is %d\n"),
5103 extraInitrdCount);
5104 return 1;
5105 }
5106 break;
5107 }
5108 }
5109
5110 if (arg < -1) {
5111 fprintf(stderr, _("grubby: bad argument %s: %s\n"),
5112 poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
5113 poptStrerror(arg));
5114 return 1;
5115 }
5116
5117 if ((chptr = poptGetArg(optCon))) {
5118 fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
5119 return 1;
5120 }
5121
5122 if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
5123 configureYaboot + configureSilo + configureZipl +
5124 configureExtLinux) > 1) {
5125 fprintf(stderr,
5126 _("grubby: cannot specify multiple bootloaders\n"));
5127 return 1;
5128 } else if (bootloaderProbe && grubConfig) {
5129 fprintf(stderr,
5130 _
5131 ("grubby: cannot specify config file with --bootloader-probe\n"));
5132 return 1;
5133 } else if (configureGrub2) {
5134 cfi = &grub2ConfigType;
5135 if (envPath)
5136 cfi->envFile = envPath;
5137 } else if (configureLilo) {
5138 cfi = &liloConfigType;
5139 } else if (configureGrub) {
5140 cfi = &grubConfigType;
5141 } else if (configureELilo) {
5142 cfi = &eliloConfigType;
5143 } else if (configureYaboot) {
5144 cfi = &yabootConfigType;
5145 } else if (configureSilo) {
5146 cfi = &siloConfigType;
5147 } else if (configureZipl) {
5148 cfi = &ziplConfigType;
5149 } else if (configureExtLinux) {
5150 cfi = &extlinuxConfigType;
5151 useextlinuxmenu = 1;
5152 }
5153
5154 if (!cfi) {
5155 if (grub2FindConfig(&grub2ConfigType)) {
5156 cfi = &grub2ConfigType;
5157 configureGrub2 = 1;
5158 if (envPath)
5159 cfi->envFile = envPath;
5160 } else {
5161 #ifdef __ia64__
5162 cfi = &eliloConfigType;
5163 configureLilo = 1;
5164 #elif defined(__powerpc__)
5165 cfi = &yabootConfigType;
5166 configureYaboot = 1;
5167 #elif defined(__sparc__)
5168 cfi = &siloConfigType;
5169 configureSilo = 1;
5170 #elif defined(__s390__) || defined(__s390x__)
5171 cfi = &ziplConfigType;
5172 configureZipl = 1;
5173 #else
5174 cfi = &grubConfigType;
5175 configureGrub = 1;
5176 #endif
5177 }
5178 }
5179
5180 if (!grubConfig) {
5181 if (cfi->findConfig)
5182 grubConfig = cfi->findConfig(cfi);
5183 if (!grubConfig)
5184 grubConfig = cfi->defaultConfig;
5185 }
5186
5187 if (bootloaderProbe && (displayDefault || kernelInfo ||
5188 newKernelPath || removeKernelPath || makeDefault
5189 || defaultKernel || displayDefaultIndex
5190 || displayDefaultTitle
5191 || (defaultIndex >= 0))) {
5192 fprintf(stderr,
5193 _("grubby: --bootloader-probe may not be used with "
5194 "specified option"));
5195 return 1;
5196 }
5197
5198 if ((displayDefault || kernelInfo) && (newKernelPath ||
5199 removeKernelPath)) {
5200 fprintf(stderr, _("grubby: --default-kernel and --info may not "
5201 "be used when adding or removing kernels\n"));
5202 return 1;
5203 }
5204
5205 if (newKernelPath && !newKernelTitle) {
5206 fprintf(stderr, _("grubby: kernel title must be specified\n"));
5207 return 1;
5208 } else if (!newKernelPath && (copyDefault ||
5209 (newKernelInitrd && !updateKernelPath) ||
5210 makeDefault || extraInitrdCount > 0)) {
5211 fprintf(stderr, _("grubby: kernel path expected\n"));
5212 return 1;
5213 }
5214
5215 if (newKernelPath && updateKernelPath) {
5216 fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
5217 "not be used together"));
5218 return 1;
5219 }
5220
5221 if (makeDefault && defaultKernel) {
5222 fprintf(stderr, _("grubby: --make-default and --default-kernel "
5223 "may not be used together\n"));
5224 return 1;
5225 } else if (defaultKernel && removeKernelPath &&
5226 !strcmp(defaultKernel, removeKernelPath)) {
5227 fprintf(stderr,
5228 _("grubby: cannot make removed kernel the default\n"));
5229 return 1;
5230 } else if (defaultKernel && newKernelPath &&
5231 !strcmp(defaultKernel, newKernelPath)) {
5232 makeDefault = 1;
5233 defaultKernel = NULL;
5234 } else if (defaultKernel && (defaultIndex >= 0)) {
5235 fprintf(stderr,
5236 _("grubby: --set-default and --set-default-index "
5237 "may not be used together\n"));
5238 return 1;
5239 }
5240
5241 if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
5242 fprintf(stderr,
5243 _("grubby: output file must be specified if stdin "
5244 "is used\n"));
5245 return 1;
5246 }
5247
5248 if (!removeKernelPath && !newKernelPath && !displayDefault
5249 && !defaultKernel && !kernelInfo && !bootloaderProbe
5250 && !updateKernelPath && !removeMBKernel && !displayDefaultIndex
5251 && !displayDefaultTitle && (defaultIndex == -1)) {
5252 fprintf(stderr, _("grubby: no action specified\n"));
5253 return 1;
5254 }
5255
5256 flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
5257
5258 if (cfi->needsBootPrefix) {
5259 if (!bootPrefix) {
5260 bootPrefix = findBootPrefix();
5261 if (!bootPrefix)
5262 return 1;
5263 } else {
5264 /* this shouldn't end with a / */
5265 if (bootPrefix[strlen(bootPrefix) - 1] == '/')
5266 bootPrefix[strlen(bootPrefix) - 1] = '\0';
5267 }
5268 } else {
5269 bootPrefix = "";
5270 }
5271
5272 if (!cfi->mbAllowExtraInitRds && extraInitrdCount > 0) {
5273 fprintf(stderr,
5274 _("grubby: %s doesn't allow multiple initrds\n"),
5275 cfi->defaultConfig);
5276 return 1;
5277 }
5278
5279 if (bootloaderProbe) {
5280 int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
5281 struct grubConfig *lconfig, *gconfig, *yconfig, *econfig;
5282
5283 const char *grub2config = grub2FindConfig(&grub2ConfigType);
5284 if (grub2config) {
5285 gconfig = readConfig(grub2config, &grub2ConfigType);
5286 if (!gconfig)
5287 gr2c = 1;
5288 else
5289 gr2c = checkForGrub2(gconfig);
5290 }
5291
5292 const char *grubconfig = grubFindConfig(&grubConfigType);
5293 if (!access(grubconfig, F_OK)) {
5294 gconfig = readConfig(grubconfig, &grubConfigType);
5295 if (!gconfig)
5296 grc = 1;
5297 else
5298 grc = checkForGrub(gconfig);
5299 }
5300
5301 if (!access(liloConfigType.defaultConfig, F_OK)) {
5302 lconfig =
5303 readConfig(liloConfigType.defaultConfig,
5304 &liloConfigType);
5305 if (!lconfig)
5306 lrc = 1;
5307 else
5308 lrc = checkForLilo(lconfig);
5309 }
5310
5311 if (!access(eliloConfigType.defaultConfig, F_OK)) {
5312 econfig = readConfig(eliloConfigType.defaultConfig,
5313 &eliloConfigType);
5314 if (!econfig)
5315 erc = 1;
5316 else
5317 erc = checkForElilo(econfig);
5318 }
5319
5320 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
5321 lconfig =
5322 readConfig(extlinuxConfigType.defaultConfig,
5323 &extlinuxConfigType);
5324 if (!lconfig)
5325 extrc = 1;
5326 else
5327 extrc = checkForExtLinux(lconfig);
5328 }
5329
5330 if (!access(yabootConfigType.defaultConfig, F_OK)) {
5331 yconfig = readConfig(yabootConfigType.defaultConfig,
5332 &yabootConfigType);
5333 if (!yconfig)
5334 yrc = 1;
5335 else
5336 yrc = checkForYaboot(yconfig);
5337 }
5338
5339 if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1
5340 || erc == 1)
5341 return 1;
5342
5343 if (lrc == 2)
5344 printf("lilo\n");
5345 if (gr2c == 2)
5346 printf("grub2\n");
5347 if (grc == 2)
5348 printf("grub\n");
5349 if (extrc == 2)
5350 printf("extlinux\n");
5351 if (yrc == 2)
5352 printf("yaboot\n");
5353 if (erc == 2)
5354 printf("elilo\n");
5355
5356 return 0;
5357 }
5358
5359 if (grubConfig == NULL) {
5360 printf("Could not find bootloader configuration file.\n");
5361 exit(1);
5362 }
5363
5364 config = readConfig(grubConfig, cfi);
5365 if (!config)
5366 return 1;
5367
5368 if (displayDefault) {
5369 struct singleLine *line;
5370 struct singleEntry *entry;
5371 char *rootspec;
5372
5373 if (config->defaultImage == NO_DEFAULT_ENTRY)
5374 return 0;
5375 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5376 cfi->defaultIsSaved)
5377 config->defaultImage = FIRST_ENTRY_INDEX;
5378 entry = findEntryByIndex(config, config->defaultImage);
5379 if (!entry)
5380 return 0;
5381 if (!suitableImage(entry, bootPrefix, 0, flags))
5382 return 0;
5383
5384 line =
5385 getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI |
5386 LT_KERNEL_16, entry->lines);
5387 if (!line)
5388 return 0;
5389
5390 rootspec = getRootSpecifier(line->elements[1].item);
5391 printf("%s%s\n", bootPrefix, line->elements[1].item +
5392 ((rootspec != NULL) ? strlen(rootspec) : 0));
5393
5394 return 0;
5395
5396 } else if (displayDefaultTitle) {
5397 struct singleLine *line;
5398 struct singleEntry *entry;
5399
5400 if (config->defaultImage == NO_DEFAULT_ENTRY)
5401 return 0;
5402 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5403 cfi->defaultIsSaved)
5404 config->defaultImage = FIRST_ENTRY_INDEX;
5405 entry = findEntryByIndex(config, config->defaultImage);
5406 if (!entry)
5407 return 0;
5408
5409 if (!configureGrub2) {
5410 char *title;
5411 line = getLineByType(LT_TITLE, entry->lines);
5412 if (!line)
5413 return 0;
5414 title = extractTitle(config, line);
5415 if (!title)
5416 return 0;
5417 printf("%s\n", title);
5418 free(title);
5419 } else {
5420 char *title;
5421
5422 dbgPrintf
5423 ("This is GRUB2, default title is embeded in menuentry\n");
5424 line = getLineByType(LT_MENUENTRY, entry->lines);
5425 if (!line)
5426 return 0;
5427 title = grub2ExtractTitle(line);
5428 if (title)
5429 printf("%s\n", title);
5430 }
5431 return 0;
5432
5433 } else if (displayDefaultIndex) {
5434 if (config->defaultImage == NO_DEFAULT_ENTRY)
5435 return 0;
5436 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5437 cfi->defaultIsSaved)
5438 config->defaultImage = FIRST_ENTRY_INDEX;
5439 printf("%i\n", config->defaultImage);
5440 return 0;
5441
5442 } else if (kernelInfo)
5443 return displayInfo(config, kernelInfo, bootPrefix);
5444
5445 if (copyDefault) {
5446 template = findTemplate(config, bootPrefix, NULL, 0, flags);
5447 if (!template)
5448 return 1;
5449 }
5450
5451 markRemovedImage(config, removeKernelPath, bootPrefix);
5452 markRemovedImage(config, removeMBKernel, bootPrefix);
5453 setDefaultImage(config, newKernelPath != NULL, defaultKernel,
5454 makeDefault, bootPrefix, flags, defaultIndex,
5455 newIndex);
5456 setFallbackImage(config, newKernelPath != NULL);
5457 if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
5458 removeArgs, newMBKernelArgs, removeMBKernelArgs))
5459 return 1;
5460 if (updateKernelPath && newKernelInitrd) {
5461 if (newMBKernel) {
5462 if (addMBInitrd(config, newMBKernel, updateKernelPath,
5463 bootPrefix, newKernelInitrd,
5464 newKernelTitle))
5465 return 1;
5466 } else {
5467 if (updateInitrd(config, updateKernelPath, bootPrefix,
5468 newKernelInitrd, newKernelTitle))
5469 return 1;
5470 }
5471 }
5472 if (addNewKernel(config, template, bootPrefix, newKernelPath,
5473 newKernelTitle, newKernelArgs, newKernelInitrd,
5474 (const char **)extraInitrds, extraInitrdCount,
5475 newMBKernel, newMBKernelArgs, newDevTreePath,
5476 newIndex))
5477 return 1;
5478
5479 if (numEntries(config) == 0) {
5480 fprintf(stderr,
5481 _("grubby: doing this would leave no kernel entries. "
5482 "Not writing out new config.\n"));
5483 return 1;
5484 }
5485
5486 if (!outputFile)
5487 outputFile = (char *)grubConfig;
5488
5489 return writeConfig(config, outputFile, bootPrefix);
5490 }