Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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