Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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