Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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