Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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