Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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