Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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