Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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