Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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