Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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