Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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