Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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