Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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