Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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