Magellan Linux

Contents of /tags/grubby-8_36/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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