Magellan Linux

Contents of /tags/grubby-8_40_20170627/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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