Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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