Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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