Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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