Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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