Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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