Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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