Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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