Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1946 - (show annotations) (download)
Mon Oct 1 12:44:49 2012 UTC (11 years, 6 months ago) by niro
File MIME type: text/plain
File size: 117998 byte(s)
tagged 'grubby-8_19'
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 line = getLineByType(entry->multiboot && checkType == LT_KERNEL
1777 ? LT_KERNEL|LT_KERNEL_EFI|LT_MBMODULE|LT_HYPER
1778 : checkType, line);
1779 if (!line) break; /* not found in this entry */
1780
1781 if (line && line->type != LT_MENUENTRY &&
1782 line->numElements >= 2) {
1783 rootspec = getRootSpecifier(line->elements[1].item);
1784 if (!strcmp(line->elements[1].item +
1785 ((rootspec != NULL) ? strlen(rootspec) : 0),
1786 kernel + strlen(prefix)))
1787 break;
1788 }
1789 if(line->type == LT_MENUENTRY &&
1790 !strcmp(line->elements[1].item, kernel))
1791 break;
1792 }
1793
1794 /* make sure this entry has a kernel identifier; this skips
1795 * non-Linux boot entries (could find netbsd etc, though, which is
1796 * unfortunate)
1797 */
1798 if (line && getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI, entry->lines))
1799 break; /* found 'im! */
1800 }
1801
1802 if (index) *index = i;
1803 }
1804
1805 return entry;
1806 }
1807
1808 struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index) {
1809 struct singleEntry * entry;
1810
1811 entry = cfg->entries;
1812 while (index && entry) {
1813 entry = entry->next;
1814 index--;
1815 }
1816
1817 return entry;
1818 }
1819
1820 /* Find a good template to use for the new kernel. An entry is
1821 * good if the kernel and mkinitrd exist (even if the entry
1822 * is going to be removed). Try and use the default entry, but
1823 * if that doesn't work just take the first. If we can't find one,
1824 * bail. */
1825 struct singleEntry * findTemplate(struct grubConfig * cfg, const char * prefix,
1826 int * indexPtr, int skipRemoved, int flags) {
1827 struct singleEntry * entry, * entry2;
1828 int index;
1829
1830 if (cfg->defaultImage > -1) {
1831 entry = findEntryByIndex(cfg, cfg->defaultImage);
1832 if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
1833 if (indexPtr) *indexPtr = cfg->defaultImage;
1834 return entry;
1835 }
1836 }
1837
1838 index = 0;
1839 while ((entry = findEntryByIndex(cfg, index))) {
1840 if (suitableImage(entry, prefix, skipRemoved, flags)) {
1841 int j;
1842 for (j = 0; j < index; j++) {
1843 entry2 = findEntryByIndex(cfg, j);
1844 if (entry2->skip) index--;
1845 }
1846 if (indexPtr) *indexPtr = index;
1847
1848 return entry;
1849 }
1850
1851 index++;
1852 }
1853
1854 fprintf(stderr, _("grubby fatal error: unable to find a suitable template\n"));
1855
1856 return NULL;
1857 }
1858
1859 char * findBootPrefix(void) {
1860 struct stat sb, sb2;
1861
1862 stat("/", &sb);
1863 #ifdef __ia64__
1864 stat("/boot/efi/EFI/redhat/", &sb2);
1865 #else
1866 stat("/boot", &sb2);
1867 #endif
1868
1869 if (sb.st_dev == sb2.st_dev)
1870 return strdup("");
1871
1872 #ifdef __ia64__
1873 return strdup("/boot/efi/EFI/redhat/");
1874 #else
1875 return strdup("/boot");
1876 #endif
1877 }
1878
1879 void markRemovedImage(struct grubConfig * cfg, const char * image,
1880 const char * prefix) {
1881 struct singleEntry * entry;
1882
1883 if (!image)
1884 return;
1885
1886 /* check and see if we're removing the default image */
1887 if (isdigit(*image)) {
1888 entry = findEntryByPath(cfg, image, prefix, NULL);
1889 if(entry)
1890 entry->skip = 1;
1891 return;
1892 }
1893
1894 while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
1895 entry->skip = 1;
1896 }
1897
1898 void setDefaultImage(struct grubConfig * config, int hasNew,
1899 const char * defaultKernelPath, int newIsDefault,
1900 const char * prefix, int flags, int index) {
1901 struct singleEntry * entry, * entry2, * newDefault;
1902 int i, j;
1903
1904 if (newIsDefault) {
1905 config->defaultImage = 0;
1906 return;
1907 } else if ((index >= 0) && config->cfi->defaultIsIndex) {
1908 if (findEntryByIndex(config, index))
1909 config->defaultImage = index;
1910 else
1911 config->defaultImage = -1;
1912 return;
1913 } else if (defaultKernelPath) {
1914 i = 0;
1915 if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
1916 config->defaultImage = i;
1917 } else {
1918 config->defaultImage = -1;
1919 return;
1920 }
1921 }
1922
1923 /* defaultImage now points to what we'd like to use, but before any order
1924 changes */
1925 if ((config->defaultImage == DEFAULT_SAVED) ||
1926 (config->defaultImage == DEFAULT_SAVED_GRUB2))
1927 /* default is set to saved, we don't want to change it */
1928 return;
1929
1930 if (config->defaultImage > -1)
1931 entry = findEntryByIndex(config, config->defaultImage);
1932 else
1933 entry = NULL;
1934
1935 if (entry && !entry->skip) {
1936 /* we can preserve the default */
1937 if (hasNew)
1938 config->defaultImage++;
1939
1940 /* count the number of entries erased before this one */
1941 for (j = 0; j < config->defaultImage; j++) {
1942 entry2 = findEntryByIndex(config, j);
1943 if (entry2->skip) config->defaultImage--;
1944 }
1945 } else if (hasNew) {
1946 config->defaultImage = 0;
1947 } else {
1948 /* Either we just erased the default (or the default line was bad
1949 * to begin with) and didn't put a new one in. We'll use the first
1950 * valid image. */
1951 newDefault = findTemplate(config, prefix, &config->defaultImage, 1,
1952 flags);
1953 if (!newDefault)
1954 config->defaultImage = -1;
1955 }
1956 }
1957
1958 void setFallbackImage(struct grubConfig * config, int hasNew) {
1959 struct singleEntry * entry, * entry2;
1960 int j;
1961
1962 if (config->fallbackImage == -1) return;
1963
1964 entry = findEntryByIndex(config, config->fallbackImage);
1965 if (!entry || entry->skip) {
1966 config->fallbackImage = -1;
1967 return;
1968 }
1969
1970 if (hasNew)
1971 config->fallbackImage++;
1972
1973 /* count the number of entries erased before this one */
1974 for (j = 0; j < config->fallbackImage; j++) {
1975 entry2 = findEntryByIndex(config, j);
1976 if (entry2->skip) config->fallbackImage--;
1977 }
1978 }
1979
1980 void displayEntry(struct singleEntry * entry, const char * prefix, int index) {
1981 struct singleLine * line;
1982 char * root = NULL;
1983 int i;
1984
1985 printf("index=%d\n", index);
1986
1987 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI, entry->lines);
1988 if (!line) {
1989 printf("non linux entry\n");
1990 return;
1991 }
1992
1993 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
1994 printf("kernel=%s\n", line->elements[1].item);
1995 else
1996 printf("kernel=%s%s\n", prefix, line->elements[1].item);
1997
1998 if (line->numElements >= 3) {
1999 printf("args=\"");
2000 i = 2;
2001 while (i < line->numElements) {
2002 if (!strncmp(line->elements[i].item, "root=", 5)) {
2003 root = line->elements[i].item + 5;
2004 } else {
2005 printf("%s%s", line->elements[i].item,
2006 line->elements[i].indent);
2007 }
2008
2009 i++;
2010 }
2011 printf("\"\n");
2012 } else {
2013 line = getLineByType(LT_KERNELARGS, entry->lines);
2014 if (line) {
2015 char * s;
2016
2017 printf("args=\"");
2018 i = 1;
2019 while (i < line->numElements) {
2020 if (!strncmp(line->elements[i].item, "root=", 5)) {
2021 root = line->elements[i].item + 5;
2022 } else {
2023 s = line->elements[i].item;
2024
2025 printf("%s%s", s, line->elements[i].indent);
2026 }
2027
2028 i++;
2029 }
2030
2031 s = line->elements[i - 1].indent;
2032 printf("\"\n");
2033 }
2034 }
2035
2036 if (!root) {
2037 line = getLineByType(LT_ROOT, entry->lines);
2038 if (line && line->numElements >= 2)
2039 root=line->elements[1].item;
2040 }
2041
2042 if (root) {
2043 char * s = alloca(strlen(root) + 1);
2044
2045 strcpy(s, root);
2046 if (s[strlen(s) - 1] == '"')
2047 s[strlen(s) - 1] = '\0';
2048 /* make sure the root doesn't have a trailing " */
2049 printf("root=%s\n", s);
2050 }
2051
2052 line = getLineByType(LT_INITRD|LT_INITRD_EFI, entry->lines);
2053
2054 if (line && line->numElements >= 2) {
2055 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2056 printf("initrd=");
2057 else
2058 printf("initrd=%s", prefix);
2059
2060 for (i = 1; i < line->numElements; i++)
2061 printf("%s%s", line->elements[i].item, line->elements[i].indent);
2062 printf("\n");
2063 }
2064
2065 line = getLineByType(LT_TITLE, entry->lines);
2066 if (line) {
2067 printf("title=%s\n", line->elements[1].item);
2068 } else {
2069 char * title;
2070 line = getLineByType(LT_MENUENTRY, entry->lines);
2071 title = grub2ExtractTitle(line);
2072 if (title)
2073 printf("title=%s\n", title);
2074 }
2075 }
2076
2077 int isSuseSystem(void) {
2078 const char * path;
2079 const static char default_path[] = "/etc/SuSE-release";
2080
2081 if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2082 path = default_path;
2083
2084 if (!access(path, R_OK))
2085 return 1;
2086 return 0;
2087 }
2088
2089 int isSuseGrubConf(const char * path) {
2090 FILE * grubConf;
2091 char * line = NULL;
2092 size_t len = 0, res = 0;
2093
2094 grubConf = fopen(path, "r");
2095 if (!grubConf) {
2096 dbgPrintf("Could not open SuSE configuration file '%s'\n", path);
2097 return 0;
2098 }
2099
2100 while ((res = getline(&line, &len, grubConf)) != -1) {
2101 if (!strncmp(line, "setup", 5)) {
2102 fclose(grubConf);
2103 free(line);
2104 return 1;
2105 }
2106 }
2107
2108 dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2109 path);
2110
2111 fclose(grubConf);
2112 free(line);
2113 return 0;
2114 }
2115
2116 int suseGrubConfGetLba(const char * path, int * lbaPtr) {
2117 FILE * grubConf;
2118 char * line = NULL;
2119 size_t res = 0, len = 0;
2120
2121 if (!path) return 1;
2122 if (!lbaPtr) return 1;
2123
2124 grubConf = fopen(path, "r");
2125 if (!grubConf) return 1;
2126
2127 while ((res = getline(&line, &len, grubConf)) != -1) {
2128 if (line[res - 1] == '\n')
2129 line[res - 1] = '\0';
2130 else if (len > res)
2131 line[res] = '\0';
2132 else {
2133 line = realloc(line, res + 1);
2134 line[res] = '\0';
2135 }
2136
2137 if (!strncmp(line, "setup", 5)) {
2138 if (strstr(line, "--force-lba")) {
2139 *lbaPtr = 1;
2140 } else {
2141 *lbaPtr = 0;
2142 }
2143 dbgPrintf("lba: %i\n", *lbaPtr);
2144 break;
2145 }
2146 }
2147
2148 free(line);
2149 fclose(grubConf);
2150 return 0;
2151 }
2152
2153 int suseGrubConfGetInstallDevice(const char * path, char ** devicePtr) {
2154 FILE * grubConf;
2155 char * line = NULL;
2156 size_t res = 0, len = 0;
2157 char * lastParamPtr = NULL;
2158 char * secLastParamPtr = NULL;
2159 char installDeviceNumber = '\0';
2160 char * bounds = NULL;
2161
2162 if (!path) return 1;
2163 if (!devicePtr) return 1;
2164
2165 grubConf = fopen(path, "r");
2166 if (!grubConf) return 1;
2167
2168 while ((res = getline(&line, &len, grubConf)) != -1) {
2169 if (strncmp(line, "setup", 5))
2170 continue;
2171
2172 if (line[res - 1] == '\n')
2173 line[res - 1] = '\0';
2174 else if (len > res)
2175 line[res] = '\0';
2176 else {
2177 line = realloc(line, res + 1);
2178 line[res] = '\0';
2179 }
2180
2181 lastParamPtr = bounds = line + res;
2182
2183 /* Last parameter in grub may be an optional IMAGE_DEVICE */
2184 while (!isspace(*lastParamPtr))
2185 lastParamPtr--;
2186 lastParamPtr++;
2187
2188 secLastParamPtr = lastParamPtr - 2;
2189 dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2190
2191 if (lastParamPtr + 3 > bounds) {
2192 dbgPrintf("lastParamPtr going over boundary");
2193 fclose(grubConf);
2194 free(line);
2195 return 1;
2196 }
2197 if (!strncmp(lastParamPtr, "(hd", 3))
2198 lastParamPtr += 3;
2199 dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2200
2201 /*
2202 * Second last parameter will decide wether last parameter is
2203 * an IMAGE_DEVICE or INSTALL_DEVICE
2204 */
2205 while (!isspace(*secLastParamPtr))
2206 secLastParamPtr--;
2207 secLastParamPtr++;
2208
2209 if (secLastParamPtr + 3 > bounds) {
2210 dbgPrintf("secLastParamPtr going over boundary");
2211 fclose(grubConf);
2212 free(line);
2213 return 1;
2214 }
2215 dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2216 if (!strncmp(secLastParamPtr, "(hd", 3)) {
2217 secLastParamPtr += 3;
2218 dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
2219 installDeviceNumber = *secLastParamPtr;
2220 } else {
2221 installDeviceNumber = *lastParamPtr;
2222 }
2223
2224 *devicePtr = malloc(6);
2225 snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
2226 dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
2227 fclose(grubConf);
2228 free(line);
2229 return 0;
2230 }
2231
2232 free(line);
2233 fclose(grubConf);
2234 return 1;
2235 }
2236
2237 int grubGetBootFromDeviceMap(const char * device,
2238 char ** bootPtr) {
2239 FILE * deviceMap;
2240 char * line = NULL;
2241 size_t res = 0, len = 0;
2242 char * devicePtr;
2243 char * bounds = NULL;
2244 const char * path;
2245 const static char default_path[] = "/boot/grub/device.map";
2246
2247 if (!device) return 1;
2248 if (!bootPtr) return 1;
2249
2250 if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2251 path = default_path;
2252
2253 dbgPrintf("opening grub device.map file from: %s\n", path);
2254 deviceMap = fopen(path, "r");
2255 if (!deviceMap)
2256 return 1;
2257
2258 while ((res = getline(&line, &len, deviceMap)) != -1) {
2259 if (!strncmp(line, "#", 1))
2260 continue;
2261
2262 if (line[res - 1] == '\n')
2263 line[res - 1] = '\0';
2264 else if (len > res)
2265 line[res] = '\0';
2266 else {
2267 line = realloc(line, res + 1);
2268 line[res] = '\0';
2269 }
2270
2271 devicePtr = line;
2272 bounds = line + res;
2273
2274 while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2275 devicePtr++;
2276 dbgPrintf("device: %s\n", devicePtr);
2277
2278 if (!strncmp(devicePtr, device, strlen(device))) {
2279 devicePtr += strlen(device);
2280 while (isspace(*devicePtr) && ((devicePtr + 1) <= bounds))
2281 devicePtr++;
2282
2283 *bootPtr = strdup(devicePtr);
2284 break;
2285 }
2286 }
2287
2288 free(line);
2289 fclose(deviceMap);
2290 return 0;
2291 }
2292
2293 int suseGrubConfGetBoot(const char * path, char ** bootPtr) {
2294 char * grubDevice;
2295
2296 if (suseGrubConfGetInstallDevice(path, &grubDevice))
2297 dbgPrintf("error looking for grub installation device\n");
2298 else
2299 dbgPrintf("grubby installation device: %s\n", grubDevice);
2300
2301 if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
2302 dbgPrintf("error looking for grub boot device\n");
2303 else
2304 dbgPrintf("grubby boot device: %s\n", *bootPtr);
2305
2306 free(grubDevice);
2307 return 0;
2308 }
2309
2310 int parseSuseGrubConf(int * lbaPtr, char ** bootPtr) {
2311 /*
2312 * This SuSE grub configuration file at this location is not your average
2313 * grub configuration file, but instead the grub commands used to setup
2314 * grub on that system.
2315 */
2316 const char * path;
2317 const static char default_path[] = "/etc/grub.conf";
2318
2319 if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
2320 path = default_path;
2321
2322 if (!isSuseGrubConf(path)) return 1;
2323
2324 if (lbaPtr) {
2325 *lbaPtr = 0;
2326 if (suseGrubConfGetLba(path, lbaPtr))
2327 return 1;
2328 }
2329
2330 if (bootPtr) {
2331 *bootPtr = NULL;
2332 suseGrubConfGetBoot(path, bootPtr);
2333 }
2334
2335 return 0;
2336 }
2337
2338 int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
2339 FILE * in;
2340 char buf[1024];
2341 char * chptr;
2342 char * start;
2343 char * param;
2344
2345 in = fopen("/etc/conf.d/grub", "r");
2346 if (!in) return 1;
2347
2348 if (lbaPtr) *lbaPtr = 0;
2349 if (bootPtr) *bootPtr = NULL;
2350
2351 while (fgets(buf, sizeof(buf), in)) {
2352 start = buf;
2353 while (isspace(*start)) start++;
2354 if (*start == '#') continue;
2355
2356 chptr = strchr(start, '=');
2357 if (!chptr) continue;
2358 chptr--;
2359 while (*chptr && isspace(*chptr)) chptr--;
2360 chptr++;
2361 *chptr = '\0';
2362
2363 param = chptr + 1;
2364 while (*param && isspace(*param)) param++;
2365 if (*param == '=') {
2366 param++;
2367 while (*param && isspace(*param)) param++;
2368 }
2369
2370 chptr = param;
2371 while (*chptr && !isspace(*chptr)) chptr++;
2372 *chptr = '\0';
2373
2374 if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
2375 *lbaPtr = 1;
2376 else if (!strcmp(start, "boot") && bootPtr)
2377 *bootPtr = strdup(param);
2378 }
2379
2380 fclose(in);
2381
2382 return 0;
2383 }
2384
2385 void dumpSysconfigGrub(void) {
2386 char * boot = NULL;
2387 int lba;
2388
2389 if (isSuseSystem()) {
2390 if (parseSuseGrubConf(&lba, &boot)) {
2391 free(boot);
2392 return;
2393 }
2394 } else {
2395 if (parseSysconfigGrub(&lba, &boot)) {
2396 free(boot);
2397 return;
2398 }
2399 }
2400
2401 if (lba) printf("lba\n");
2402 if (boot) {
2403 printf("boot=%s\n", boot);
2404 free(boot);
2405 }
2406 }
2407
2408 int displayInfo(struct grubConfig * config, char * kernel,
2409 const char * prefix) {
2410 int i = 0;
2411 struct singleEntry * entry;
2412 struct singleLine * line;
2413
2414 entry = findEntryByPath(config, kernel, prefix, &i);
2415 if (!entry) {
2416 fprintf(stderr, _("grubby: kernel not found\n"));
2417 return 1;
2418 }
2419
2420 /* this is a horrible hack to support /etc/conf.d/grub; there must
2421 be a better way */
2422 if (config->cfi == &grubConfigType) {
2423 dumpSysconfigGrub();
2424 } else {
2425 line = getLineByType(LT_BOOT, config->theLines);
2426 if (line && line->numElements >= 1) {
2427 printf("boot=%s\n", line->elements[1].item);
2428 }
2429
2430 line = getLineByType(LT_LBA, config->theLines);
2431 if (line) printf("lba\n");
2432 }
2433
2434 displayEntry(entry, prefix, i);
2435
2436 i++;
2437 while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
2438 displayEntry(entry, prefix, i);
2439 i++;
2440 }
2441
2442 return 0;
2443 }
2444
2445 struct singleLine * addLineTmpl(struct singleEntry * entry,
2446 struct singleLine * tmplLine,
2447 struct singleLine * prevLine,
2448 const char * val,
2449 struct configFileInfo * cfi)
2450 {
2451 struct singleLine * newLine = lineDup(tmplLine);
2452
2453 if (isEfi && cfi == &grub2ConfigType) {
2454 enum lineType_e old = newLine->type;
2455 newLine->type = preferredLineType(newLine->type, cfi);
2456 if (old != newLine->type)
2457 newLine->elements[0].item = getKeyByType(newLine->type, cfi);
2458 }
2459
2460 if (val) {
2461 /* override the inherited value with our own.
2462 * This is a little weak because it only applies to elements[1]
2463 */
2464 if (newLine->numElements > 1)
2465 removeElement(newLine, 1);
2466 insertElement(newLine, val, 1, cfi);
2467
2468 /* but try to keep the rootspec from the template... sigh */
2469 if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD|LT_KERNEL_EFI|LT_INITRD_EFI)) {
2470 char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
2471 if (rootspec != NULL) {
2472 free(newLine->elements[1].item);
2473 newLine->elements[1].item =
2474 sdupprintf("%s%s", rootspec, val);
2475 }
2476 }
2477 }
2478
2479 dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
2480 newLine->elements[0].item : "");
2481
2482 if (!entry->lines) {
2483 /* first one on the list */
2484 entry->lines = newLine;
2485 } else if (prevLine) {
2486 /* add after prevLine */
2487 newLine->next = prevLine->next;
2488 prevLine->next = newLine;
2489 }
2490
2491 return newLine;
2492 }
2493
2494 /* val may be NULL */
2495 struct singleLine * addLine(struct singleEntry * entry,
2496 struct configFileInfo * cfi,
2497 enum lineType_e type, char * defaultIndent,
2498 const char * val) {
2499 struct singleLine * line, * prev;
2500 struct keywordTypes * kw;
2501 struct singleLine tmpl;
2502
2503 /* NB: This function shouldn't allocate items on the heap, rather on the
2504 * stack since it calls addLineTmpl which will make copies.
2505 */
2506 if (type == LT_TITLE && cfi->titleBracketed) {
2507 /* we're doing a bracketed title (zipl) */
2508 tmpl.type = type;
2509 tmpl.numElements = 1;
2510 tmpl.elements = alloca(sizeof(*tmpl.elements));
2511 tmpl.elements[0].item = alloca(strlen(val)+3);
2512 sprintf(tmpl.elements[0].item, "[%s]", val);
2513 tmpl.elements[0].indent = "";
2514 val = NULL;
2515 } else if (type == LT_MENUENTRY) {
2516 char *lineend = "--class gnu-linux --class gnu --class os {";
2517 if (!val) {
2518 fprintf(stderr, "Line type LT_MENUENTRY requires a value\n");
2519 abort();
2520 }
2521 kw = getKeywordByType(type, cfi);
2522 if (!kw) {
2523 fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2524 abort();
2525 }
2526 tmpl.indent = "";
2527 tmpl.type = type;
2528 tmpl.numElements = 3;
2529 tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2530 tmpl.elements[0].item = kw->key;
2531 tmpl.elements[0].indent = alloca(2);
2532 sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2533 tmpl.elements[1].item = (char *)val;
2534 tmpl.elements[1].indent = alloca(2);
2535 sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
2536 tmpl.elements[2].item = alloca(strlen(lineend)+1);
2537 strcpy(tmpl.elements[2].item, lineend);
2538 tmpl.elements[2].indent = "";
2539 } else {
2540 kw = getKeywordByType(type, cfi);
2541 if (!kw) {
2542 fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2543 abort();
2544 }
2545 tmpl.type = type;
2546 tmpl.numElements = val ? 2 : 1;
2547 tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2548 tmpl.elements[0].item = kw->key;
2549 tmpl.elements[0].indent = alloca(2);
2550 sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2551 if (val) {
2552 tmpl.elements[1].item = (char *)val;
2553 tmpl.elements[1].indent = "";
2554 }
2555 }
2556
2557 /* The last non-empty line gives us the indention to us and the line
2558 to insert after. Note that comments are considered empty lines, which
2559 may not be ideal? If there are no lines or we are looking at the
2560 first line, we use defaultIndent (the first line is normally indented
2561 differently from the rest) */
2562 for (line = entry->lines, prev = NULL; line; line = line->next) {
2563 if (line->numElements) prev = line;
2564 /* fall back on the last line if prev isn't otherwise set */
2565 if (!line->next && !prev) prev = line;
2566 }
2567
2568 struct singleLine *menuEntry;
2569 menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
2570 if (tmpl.type == LT_ENTRY_END) {
2571 if (menuEntry)
2572 tmpl.indent = menuEntry->indent;
2573 else
2574 tmpl.indent = defaultIndent ?: "";
2575 } else if (tmpl.type != LT_MENUENTRY) {
2576 if (menuEntry)
2577 tmpl.indent = "\t";
2578 else if (prev == entry->lines)
2579 tmpl.indent = defaultIndent ?: "";
2580 else
2581 tmpl.indent = prev->indent;
2582 }
2583
2584 return addLineTmpl(entry, &tmpl, prev, val, cfi);
2585 }
2586
2587 void removeLine(struct singleEntry * entry, struct singleLine * line) {
2588 struct singleLine * prev;
2589 int i;
2590
2591 for (i = 0; i < line->numElements; i++) {
2592 free(line->elements[i].item);
2593 free(line->elements[i].indent);
2594 }
2595 free(line->elements);
2596 free(line->indent);
2597
2598 if (line == entry->lines) {
2599 entry->lines = line->next;
2600 } else {
2601 prev = entry->lines;
2602 while (prev->next != line) prev = prev->next;
2603 prev->next = line->next;
2604 }
2605
2606 free(line);
2607 }
2608
2609 static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi)
2610 {
2611 struct singleLine newLine = {
2612 .indent = tmplLine->indent,
2613 .type = tmplLine->type,
2614 .next = tmplLine->next,
2615 };
2616 int firstQuotedItem = -1;
2617 int quoteLen = 0;
2618 int j;
2619 int element = 0;
2620 char *c;
2621
2622 c = malloc(strlen(tmplLine->elements[0].item) + 1);
2623 strcpy(c, tmplLine->elements[0].item);
2624 insertElement(&newLine, c, element++, cfi);
2625 free(c);
2626 c = NULL;
2627
2628 for (j = 1; j < tmplLine->numElements; j++) {
2629 if (firstQuotedItem == -1) {
2630 quoteLen += strlen(tmplLine->elements[j].item);
2631
2632 if (isquote(tmplLine->elements[j].item[0])) {
2633 firstQuotedItem = j;
2634 quoteLen += strlen(tmplLine->elements[j].indent);
2635 } else {
2636 c = malloc(quoteLen + 1);
2637 strcpy(c, tmplLine->elements[j].item);
2638 insertElement(&newLine, c, element++, cfi);
2639 free(c);
2640 quoteLen = 0;
2641 }
2642 } else {
2643 int itemlen = strlen(tmplLine->elements[j].item);
2644 quoteLen += itemlen;
2645 quoteLen += strlen(tmplLine->elements[j].indent);
2646
2647 if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
2648 c = malloc(quoteLen + 1);
2649 c[0] = '\0';
2650 for (int i = firstQuotedItem; i < j+1; i++) {
2651 strcat(c, tmplLine->elements[i].item);
2652 strcat(c, tmplLine->elements[i].indent);
2653 }
2654 insertElement(&newLine, c, element++, cfi);
2655 free(c);
2656
2657 firstQuotedItem = -1;
2658 quoteLen = 0;
2659 }
2660 }
2661 }
2662 while (tmplLine->numElements)
2663 removeElement(tmplLine, 0);
2664 if (tmplLine->elements)
2665 free(tmplLine->elements);
2666
2667 tmplLine->numElements = newLine.numElements;
2668 tmplLine->elements = newLine.elements;
2669 }
2670
2671 static void insertElement(struct singleLine * line,
2672 const char * item, int insertHere,
2673 struct configFileInfo * cfi)
2674 {
2675 struct keywordTypes * kw;
2676 char indent[2] = "";
2677
2678 /* sanity check */
2679 if (insertHere > line->numElements) {
2680 dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",
2681 insertHere, line->numElements);
2682 insertHere = line->numElements;
2683 }
2684
2685 line->elements = realloc(line->elements, (line->numElements + 1) *
2686 sizeof(*line->elements));
2687 memmove(&line->elements[insertHere+1],
2688 &line->elements[insertHere],
2689 (line->numElements - insertHere) *
2690 sizeof(*line->elements));
2691 line->elements[insertHere].item = strdup(item);
2692
2693 kw = getKeywordByType(line->type, cfi);
2694
2695 if (line->numElements == 0) {
2696 indent[0] = '\0';
2697 } else if (insertHere == 0) {
2698 indent[0] = kw->nextChar;
2699 } else if (kw->separatorChar != '\0') {
2700 indent[0] = kw->separatorChar;
2701 } else {
2702 indent[0] = ' ';
2703 }
2704
2705 if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {
2706 /* move the end-of-line forward */
2707 line->elements[insertHere].indent =
2708 line->elements[insertHere-1].indent;
2709 line->elements[insertHere-1].indent = strdup(indent);
2710 } else {
2711 line->elements[insertHere].indent = strdup(indent);
2712 }
2713
2714 line->numElements++;
2715
2716 dbgPrintf("insertElement(%s, '%s%s', %d)\n",
2717 line->elements[0].item,
2718 line->elements[insertHere].item,
2719 line->elements[insertHere].indent,
2720 insertHere);
2721 }
2722
2723 static void removeElement(struct singleLine * line, int removeHere) {
2724 int i;
2725
2726 /* sanity check */
2727 if (removeHere >= line->numElements) return;
2728
2729 dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
2730 removeHere, line->elements[removeHere].item);
2731
2732 free(line->elements[removeHere].item);
2733
2734 if (removeHere > 1) {
2735 /* previous argument gets this argument's post-indentation */
2736 free(line->elements[removeHere-1].indent);
2737 line->elements[removeHere-1].indent =
2738 line->elements[removeHere].indent;
2739 } else {
2740 free(line->elements[removeHere].indent);
2741 }
2742
2743 /* now collapse the array, but don't bother to realloc smaller */
2744 for (i = removeHere; i < line->numElements - 1; i++)
2745 line->elements[i] = line->elements[i + 1];
2746
2747 line->numElements--;
2748 }
2749
2750 int argMatch(const char * one, const char * two) {
2751 char * first, * second;
2752 char * chptr;
2753
2754 first = strcpy(alloca(strlen(one) + 1), one);
2755 second = strcpy(alloca(strlen(two) + 1), two);
2756
2757 chptr = strchr(first, '=');
2758 if (chptr) *chptr = '\0';
2759
2760 chptr = strchr(second, '=');
2761 if (chptr) *chptr = '\0';
2762
2763 return strcmp(first, second);
2764 }
2765
2766 int updateActualImage(struct grubConfig * cfg, const char * image,
2767 const char * prefix, const char * addArgs,
2768 const char * removeArgs, int multibootArgs) {
2769 struct singleEntry * entry;
2770 struct singleLine * line, * rootLine;
2771 int index = 0;
2772 int i, k;
2773 const char ** newArgs, ** oldArgs;
2774 const char ** arg;
2775 int useKernelArgs, useRoot;
2776 int firstElement;
2777 int *usedElements;
2778 int doreplace;
2779
2780 if (!image) return 0;
2781
2782 if (!addArgs) {
2783 newArgs = malloc(sizeof(*newArgs));
2784 *newArgs = NULL;
2785 } else {
2786 if (poptParseArgvString(addArgs, NULL, &newArgs)) {
2787 fprintf(stderr,
2788 _("grubby: error separating arguments '%s'\n"), addArgs);
2789 return 1;
2790 }
2791 }
2792
2793 if (!removeArgs) {
2794 oldArgs = malloc(sizeof(*oldArgs));
2795 *oldArgs = NULL;
2796 } else {
2797 if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
2798 fprintf(stderr,
2799 _("grubby: error separating arguments '%s'\n"), removeArgs);
2800 free(newArgs);
2801 return 1;
2802 }
2803 }
2804
2805
2806 useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
2807 && (!multibootArgs || cfg->cfi->mbConcatArgs));
2808
2809 useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
2810 && !multibootArgs);
2811
2812 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
2813
2814 if (multibootArgs && !entry->multiboot)
2815 continue;
2816
2817 /* Determine where to put the args. If this config supports
2818 * LT_KERNELARGS, use that. Otherwise use
2819 * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
2820 */
2821 if (useKernelArgs) {
2822 line = getLineByType(LT_KERNELARGS, entry->lines);
2823 if (!line) {
2824 /* no LT_KERNELARGS, need to add it */
2825 line = addLine(entry, cfg->cfi, LT_KERNELARGS,
2826 cfg->secondaryIndent, NULL);
2827 }
2828 firstElement = 1;
2829
2830 } else if (multibootArgs) {
2831 line = getLineByType(LT_HYPER, entry->lines);
2832 if (!line) {
2833 /* a multiboot entry without LT_HYPER? */
2834 continue;
2835 }
2836 firstElement = 2;
2837
2838 } else {
2839 line = getLineByType(LT_KERNEL|LT_MBMODULE|LT_KERNEL_EFI, entry->lines);
2840 if (!line) {
2841 /* no LT_KERNEL or LT_MBMODULE in this entry? */
2842 continue;
2843 }
2844 firstElement = 2;
2845 }
2846
2847 /* handle the elilo case which does:
2848 * append="hypervisor args -- kernel args"
2849 */
2850 if (entry->multiboot && cfg->cfi->mbConcatArgs) {
2851 /* this is a multiboot entry, make sure there's
2852 * -- on the args line
2853 */
2854 for (i = firstElement; i < line->numElements; i++) {
2855 if (!strcmp(line->elements[i].item, "--"))
2856 break;
2857 }
2858 if (i == line->numElements) {
2859 /* assume all existing args are kernel args,
2860 * prepend -- to make it official
2861 */
2862 insertElement(line, "--", firstElement, cfg->cfi);
2863 i = firstElement;
2864 }
2865 if (!multibootArgs) {
2866 /* kernel args start after the -- */
2867 firstElement = i + 1;
2868 }
2869 } else if (cfg->cfi->mbConcatArgs) {
2870 /* this is a non-multiboot entry, remove hyper args */
2871 for (i = firstElement; i < line->numElements; i++) {
2872 if (!strcmp(line->elements[i].item, "--"))
2873 break;
2874 }
2875 if (i < line->numElements) {
2876 /* remove args up to -- */
2877 while (strcmp(line->elements[firstElement].item, "--"))
2878 removeElement(line, firstElement);
2879 /* remove -- */
2880 removeElement(line, firstElement);
2881 }
2882 }
2883
2884 usedElements = calloc(line->numElements, sizeof(*usedElements));
2885
2886 for (k = 0, arg = newArgs; *arg; arg++, k++) {
2887
2888 doreplace = 1;
2889 for (i = firstElement; i < line->numElements; i++) {
2890 if (multibootArgs && cfg->cfi->mbConcatArgs &&
2891 !strcmp(line->elements[i].item, "--"))
2892 {
2893 /* reached the end of hyper args, insert here */
2894 doreplace = 0;
2895 break;
2896 }
2897 if (usedElements[i])
2898 continue;
2899 if (!argMatch(line->elements[i].item, *arg)) {
2900 usedElements[i]=1;
2901 break;
2902 }
2903 }
2904
2905 if (i < line->numElements && doreplace) {
2906 /* direct replacement */
2907 free(line->elements[i].item);
2908 line->elements[i].item = strdup(*arg);
2909
2910 } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
2911 /* root= replacement */
2912 rootLine = getLineByType(LT_ROOT, entry->lines);
2913 if (rootLine) {
2914 free(rootLine->elements[1].item);
2915 rootLine->elements[1].item = strdup(*arg + 5);
2916 } else {
2917 rootLine = addLine(entry, cfg->cfi, LT_ROOT,
2918 cfg->secondaryIndent, *arg + 5);
2919 }
2920 }
2921
2922 else {
2923 /* insert/append */
2924 insertElement(line, *arg, i, cfg->cfi);
2925 usedElements = realloc(usedElements, line->numElements *
2926 sizeof(*usedElements));
2927 memmove(&usedElements[i + 1], &usedElements[i],
2928 line->numElements - i - 1);
2929 usedElements[i] = 1;
2930
2931 /* if we updated a root= here even though there is a
2932 LT_ROOT available we need to remove the LT_ROOT entry
2933 (this will happen if we switch from a device to a label) */
2934 if (useRoot && !strncmp(*arg, "root=", 5)) {
2935 rootLine = getLineByType(LT_ROOT, entry->lines);
2936 if (rootLine)
2937 removeLine(entry, rootLine);
2938 }
2939 }
2940 }
2941
2942 free(usedElements);
2943
2944 for (arg = oldArgs; *arg; arg++) {
2945 for (i = firstElement; i < line->numElements; i++) {
2946 if (multibootArgs && cfg->cfi->mbConcatArgs &&
2947 !strcmp(line->elements[i].item, "--"))
2948 /* reached the end of hyper args, stop here */
2949 break;
2950 if (!argMatch(line->elements[i].item, *arg)) {
2951 removeElement(line, i);
2952 break;
2953 }
2954 }
2955 /* handle removing LT_ROOT line too */
2956 if (useRoot && !strncmp(*arg, "root=", 5)) {
2957 rootLine = getLineByType(LT_ROOT, entry->lines);
2958 if (rootLine)
2959 removeLine(entry, rootLine);
2960 }
2961 }
2962
2963 if (line->numElements == 1) {
2964 /* don't need the line at all (note it has to be a
2965 LT_KERNELARGS for this to happen */
2966 removeLine(entry, line);
2967 }
2968 }
2969
2970 free(newArgs);
2971 free(oldArgs);
2972
2973 return 0;
2974 }
2975
2976 int updateImage(struct grubConfig * cfg, const char * image,
2977 const char * prefix, const char * addArgs,
2978 const char * removeArgs,
2979 const char * addMBArgs, const char * removeMBArgs) {
2980 int rc = 0;
2981
2982 if (!image) return rc;
2983
2984 /* update the main args first... */
2985 if (addArgs || removeArgs)
2986 rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs, 0);
2987 if (rc) return rc;
2988
2989 /* and now any multiboot args */
2990 if (addMBArgs || removeMBArgs)
2991 rc = updateActualImage(cfg, image, prefix, addMBArgs, removeMBArgs, 1);
2992 return rc;
2993 }
2994
2995 int updateInitrd(struct grubConfig * cfg, const char * image,
2996 const char * prefix, const char * initrd) {
2997 struct singleEntry * entry;
2998 struct singleLine * line, * kernelLine, *endLine = NULL;
2999 int index = 0;
3000
3001 if (!image) return 0;
3002
3003 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3004 kernelLine = getLineByType(LT_KERNEL|LT_KERNEL_EFI, entry->lines);
3005 if (!kernelLine) continue;
3006
3007 line = getLineByType(LT_INITRD|LT_INITRD_EFI, entry->lines);
3008 if (line)
3009 removeLine(entry, line);
3010 if (prefix) {
3011 int prefixLen = strlen(prefix);
3012 if (!strncmp(initrd, prefix, prefixLen))
3013 initrd += prefixLen;
3014 }
3015 endLine = getLineByType(LT_ENTRY_END, entry->lines);
3016 if (endLine)
3017 removeLine(entry, endLine);
3018 line = addLine(entry, cfg->cfi, preferredLineType(LT_INITRD, cfg->cfi),
3019 kernelLine->indent, initrd);
3020 if (!line)
3021 return 1;
3022 if (endLine) {
3023 line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3024 if (!line)
3025 return 1;
3026 }
3027
3028 break;
3029 }
3030
3031 return 0;
3032 }
3033
3034 int checkDeviceBootloader(const char * device, const unsigned char * boot) {
3035 int fd;
3036 unsigned char bootSect[512];
3037 int offset;
3038
3039 fd = open(device, O_RDONLY);
3040 if (fd < 0) {
3041 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3042 device, strerror(errno));
3043 return 1;
3044 }
3045
3046 if (read(fd, bootSect, 512) != 512) {
3047 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3048 device, strerror(errno));
3049 return 1;
3050 }
3051 close(fd);
3052
3053 /* first three bytes should match, a jmp short should be in there */
3054 if (memcmp(boot, bootSect, 3))
3055 return 0;
3056
3057 if (boot[1] == JMP_SHORT_OPCODE) {
3058 offset = boot[2] + 2;
3059 } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3060 offset = (boot[3] << 8) + boot[2] + 2;
3061 } else if (boot[0] == JMP_SHORT_OPCODE) {
3062 offset = boot[1] + 2;
3063 /*
3064 * it looks like grub, when copying stage1 into the mbr, patches stage1
3065 * right after the JMP location, replacing other instructions such as
3066 * JMPs for NOOPs. So, relax the check a little bit by skipping those
3067 * different bytes.
3068 */
3069 if ((bootSect[offset + 1] == NOOP_OPCODE)
3070 && (bootSect[offset + 2] == NOOP_OPCODE)) {
3071 offset = offset + 3;
3072 }
3073 } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3074 offset = (boot[2] << 8) + boot[1] + 2;
3075 } else {
3076 return 0;
3077 }
3078
3079 if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
3080 return 0;
3081
3082 return 2;
3083 }
3084
3085 int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {
3086 int fd;
3087 char buf[65536];
3088 char * end;
3089 char * chptr;
3090 char * chptr2;
3091 int rc;
3092
3093 /* it's on raid; we need to parse /proc/mdstat and check all of the
3094 *raw* devices listed in there */
3095
3096 if (!strncmp(mdDev, "/dev/", 5))
3097 mdDev += 5;
3098
3099 if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
3100 fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
3101 strerror(errno));
3102 return 2;
3103 }
3104
3105 rc = read(fd, buf, sizeof(buf) - 1);
3106 if (rc < 0 || rc == (sizeof(buf) - 1)) {
3107 fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
3108 strerror(errno));
3109 close(fd);
3110 return 2;
3111 }
3112 close(fd);
3113 buf[rc] = '\0';
3114
3115 chptr = buf;
3116 while (*chptr) {
3117 end = strchr(chptr, '\n');
3118 if (!end) break;
3119 *end = '\0';
3120
3121 if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
3122 chptr[strlen(mdDev)] == ' ') {
3123
3124 /* found the device */
3125 while (*chptr && *chptr != ':') chptr++;
3126 chptr++;
3127 while (*chptr && isspace(*chptr)) chptr++;
3128
3129 /* skip the "active" bit */
3130 while (*chptr && !isspace(*chptr)) chptr++;
3131 while (*chptr && isspace(*chptr)) chptr++;
3132
3133 /* skip the raid level */
3134 while (*chptr && !isspace(*chptr)) chptr++;
3135 while (*chptr && isspace(*chptr)) chptr++;
3136
3137 /* everything else is partition stuff */
3138 while (*chptr) {
3139 chptr2 = chptr;
3140 while (*chptr2 && *chptr2 != '[') chptr2++;
3141 if (!*chptr2) break;
3142
3143 /* yank off the numbers at the end */
3144 chptr2--;
3145 while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;
3146 chptr2++;
3147 *chptr2 = '\0';
3148
3149 /* Better, now we need the /dev/ back. We're done with
3150 * everything before this point, so we can just put
3151 * the /dev/ part there. There will always be room. */
3152 memcpy(chptr - 5, "/dev/", 5);
3153 rc = checkDeviceBootloader(chptr - 5, boot);
3154 if (rc != 2) {
3155 return rc;
3156 }
3157
3158 chptr = chptr2 + 1;
3159 /* skip the [11] bit */
3160 while (*chptr && !isspace(*chptr)) chptr++;
3161 /* and move to the next one */
3162 while (*chptr && isspace(*chptr)) chptr++;
3163 }
3164
3165 /* we're good to go */
3166 return 2;
3167 }
3168
3169 chptr = end + 1;
3170 }
3171
3172 fprintf(stderr,
3173 _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
3174 mdDev);
3175 return 0;
3176 }
3177
3178 int checkForLilo(struct grubConfig * config) {
3179 int fd;
3180 unsigned char boot[512];
3181 struct singleLine * line;
3182
3183 for (line = config->theLines; line; line = line->next)
3184 if (line->type == LT_BOOT) break;
3185
3186 if (!line) {
3187 fprintf(stderr,
3188 _("grubby: no boot line found in lilo configuration\n"));
3189 return 1;
3190 }
3191
3192 if (line->numElements != 2) return 1;
3193
3194 fd = open("/boot/boot.b", O_RDONLY);
3195 if (fd < 0) {
3196 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3197 "/boot/boot.b", strerror(errno));
3198 return 1;
3199 }
3200
3201 if (read(fd, boot, 512) != 512) {
3202 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3203 "/boot/boot.b", strerror(errno));
3204 return 1;
3205 }
3206 close(fd);
3207
3208 if (!strncmp("/dev/md", line->elements[1].item, 7))
3209 return checkLiloOnRaid(line->elements[1].item, boot);
3210
3211 return checkDeviceBootloader(line->elements[1].item, boot);
3212 }
3213
3214 int checkForGrub2(struct grubConfig * config) {
3215 if (!access("/etc/grub.d/", R_OK))
3216 return 2;
3217
3218 return 1;
3219 }
3220
3221 int checkForGrub(struct grubConfig * config) {
3222 int fd;
3223 unsigned char bootSect[512];
3224 char * boot;
3225 int onSuse = isSuseSystem();
3226
3227
3228 if (onSuse) {
3229 if (parseSuseGrubConf(NULL, &boot))
3230 return 0;
3231 } else {
3232 if (parseSysconfigGrub(NULL, &boot))
3233 return 0;
3234 }
3235
3236 /* assume grub is not installed -- not an error condition */
3237 if (!boot)
3238 return 0;
3239
3240 fd = open("/boot/grub/stage1", O_RDONLY);
3241 if (fd < 0)
3242 /* this doesn't exist if grub hasn't been installed */
3243 return 0;
3244
3245 if (read(fd, bootSect, 512) != 512) {
3246 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3247 "/boot/grub/stage1", strerror(errno));
3248 close(fd);
3249 return 1;
3250 }
3251 close(fd);
3252
3253 /* The more elaborate checks do not work on SuSE. The checks done
3254 * seem to be reasonble (at least for now), so just return success
3255 */
3256 if (onSuse)
3257 return 2;
3258
3259 return checkDeviceBootloader(boot, bootSect);
3260 }
3261
3262 int checkForExtLinux(struct grubConfig * config) {
3263 int fd;
3264 unsigned char bootSect[512];
3265 char * boot;
3266 char executable[] = "/boot/extlinux/extlinux";
3267
3268 printf("entered: checkForExtLinux()\n");
3269
3270 if (parseSysconfigGrub(NULL, &boot))
3271 return 0;
3272
3273 /* assume grub is not installed -- not an error condition */
3274 if (!boot)
3275 return 0;
3276
3277 fd = open(executable, O_RDONLY);
3278 if (fd < 0)
3279 /* this doesn't exist if grub hasn't been installed */
3280 return 0;
3281
3282 if (read(fd, bootSect, 512) != 512) {
3283 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3284 executable, strerror(errno));
3285 return 1;
3286 }
3287 close(fd);
3288
3289 return checkDeviceBootloader(boot, bootSect);
3290 }
3291
3292 int checkForYaboot(struct grubConfig * config) {
3293 /*
3294 * This is a simplistic check that we consider good enough for own puporses
3295 *
3296 * If we were to properly check if yaboot is *installed* we'd need to:
3297 * 1) get the system boot device (LT_BOOT)
3298 * 2) considering it's a raw filesystem, check if the yaboot binary matches
3299 * the content on the boot device
3300 * 3) if not, copy the binary to a temporary file and run "addnote" on it
3301 * 4) check again if binary and boot device contents match
3302 */
3303 if (!access("/etc/yaboot.conf", R_OK))
3304 return 2;
3305
3306 return 1;
3307 }
3308
3309 int checkForElilo(struct grubConfig * config) {
3310 if (!access("/etc/elilo.conf", R_OK))
3311 return 2;
3312
3313 return 1;
3314 }
3315
3316 static char * getRootSpecifier(char * str) {
3317 char * idx, * rootspec = NULL;
3318
3319 if (*str == '(') {
3320 idx = rootspec = strdup(str);
3321 while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;
3322 *(++idx) = '\0';
3323 }
3324 return rootspec;
3325 }
3326
3327 static char * getInitrdVal(struct grubConfig * config,
3328 const char * prefix, struct singleLine *tmplLine,
3329 const char * newKernelInitrd,
3330 const char ** extraInitrds, int extraInitrdCount)
3331 {
3332 char *initrdVal, *end;
3333 int i;
3334 size_t totalSize;
3335 size_t prefixLen;
3336 char separatorChar;
3337
3338 prefixLen = strlen(prefix);
3339 totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
3340
3341 for (i = 0; i < extraInitrdCount; i++) {
3342 totalSize += sizeof(separatorChar);
3343 totalSize += strlen(extraInitrds[i]) - prefixLen;
3344 }
3345
3346 initrdVal = end = malloc(totalSize);
3347
3348 end = stpcpy (end, newKernelInitrd + prefixLen);
3349
3350 separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
3351 for (i = 0; i < extraInitrdCount; i++) {
3352 const char *extraInitrd;
3353 int j;
3354
3355 extraInitrd = extraInitrds[i] + prefixLen;
3356 /* Don't add entries that are already there */
3357 if (tmplLine != NULL) {
3358 for (j = 2; j < tmplLine->numElements; j++)
3359 if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
3360 break;
3361
3362 if (j != tmplLine->numElements)
3363 continue;
3364 }
3365
3366 *end++ = separatorChar;
3367 end = stpcpy(end, extraInitrd);
3368 }
3369
3370 return initrdVal;
3371 }
3372
3373 int addNewKernel(struct grubConfig * config, struct singleEntry * template,
3374 const char * prefix,
3375 const char * newKernelPath, const char * newKernelTitle,
3376 const char * newKernelArgs, const char * newKernelInitrd,
3377 const char ** extraInitrds, int extraInitrdCount,
3378 const char * newMBKernel, const char * newMBKernelArgs) {
3379 struct singleEntry * new;
3380 struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
3381 int needs;
3382 char * chptr;
3383
3384 if (!newKernelPath) return 0;
3385
3386 /* if the newKernelTitle is too long silently munge it into something
3387 * we can live with. truncating is first check, then we'll just mess with
3388 * it until it looks better */
3389 if (config->cfi->maxTitleLength &&
3390 (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
3391 char * buf = alloca(config->cfi->maxTitleLength + 7);
3392 char * numBuf = alloca(config->cfi->maxTitleLength + 1);
3393 int i = 1;
3394
3395 sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);
3396 while (findEntryByPath(config, buf, NULL, NULL)) {
3397 sprintf(numBuf, "%d", i++);
3398 strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
3399 }
3400
3401 newKernelTitle = buf + 6;
3402 }
3403
3404 new = malloc(sizeof(*new));
3405 new->skip = 0;
3406 new->multiboot = 0;
3407 new->next = config->entries;
3408 new->lines = NULL;
3409 config->entries = new;
3410
3411 /* copy/update from the template */
3412 needs = NEED_KERNEL | NEED_TITLE;
3413 if (newKernelInitrd)
3414 needs |= NEED_INITRD;
3415 if (newMBKernel) {
3416 needs |= NEED_MB;
3417 new->multiboot = 1;
3418 }
3419
3420 if (template) {
3421 for (masterLine = template->lines;
3422 masterLine && (tmplLine = lineDup(masterLine));
3423 lineFree(tmplLine), masterLine = masterLine->next)
3424 {
3425 dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
3426
3427 /* skip comments */
3428 chptr = tmplLine->indent;
3429 while (*chptr && isspace(*chptr)) chptr++;
3430 if (*chptr == '#') continue;
3431
3432 if (iskernel(tmplLine->type) && tmplLine->numElements >= 2) {
3433 if (!template->multiboot && (needs & NEED_MB)) {
3434 /* it's not a multiboot template and this is the kernel
3435 * line. Try to be intelligent about inserting the
3436 * hypervisor at the same time.
3437 */
3438 if (config->cfi->mbHyperFirst) {
3439 /* insert the hypervisor first */
3440 newLine = addLine(new, config->cfi, LT_HYPER,
3441 tmplLine->indent,
3442 newMBKernel + strlen(prefix));
3443 /* set up for adding the kernel line */
3444 free(tmplLine->indent);
3445 tmplLine->indent = strdup(config->secondaryIndent);
3446 needs &= ~NEED_MB;
3447 }
3448 if (needs & NEED_KERNEL) {
3449 /* use addLineTmpl to preserve line elements,
3450 * otherwise we could just call addLine. Unfortunately
3451 * this means making some changes to the template
3452 * such as the indent change above and the type
3453 * change below.
3454 */
3455 struct keywordTypes * mbm_kw =
3456 getKeywordByType(LT_MBMODULE, config->cfi);
3457 if (mbm_kw) {
3458 tmplLine->type = LT_MBMODULE;
3459 free(tmplLine->elements[0].item);
3460 tmplLine->elements[0].item = strdup(mbm_kw->key);
3461 }
3462 newLine = addLineTmpl(new, tmplLine, newLine,
3463 newKernelPath + strlen(prefix), config->cfi);
3464 needs &= ~NEED_KERNEL;
3465 }
3466 if (needs & NEED_MB) { /* !mbHyperFirst */
3467 newLine = addLine(new, config->cfi, LT_HYPER,
3468 config->secondaryIndent,
3469 newMBKernel + strlen(prefix));
3470 needs &= ~NEED_MB;
3471 }
3472 } else if (needs & NEED_KERNEL) {
3473 newLine = addLineTmpl(new, tmplLine, newLine,
3474 newKernelPath + strlen(prefix), config->cfi);
3475 needs &= ~NEED_KERNEL;
3476 }
3477
3478 } else if (tmplLine->type == LT_HYPER &&
3479 tmplLine->numElements >= 2) {
3480 if (needs & NEED_MB) {
3481 newLine = addLineTmpl(new, tmplLine, newLine,
3482 newMBKernel + strlen(prefix), config->cfi);
3483 needs &= ~NEED_MB;
3484 }
3485
3486 } else if (tmplLine->type == LT_MBMODULE &&
3487 tmplLine->numElements >= 2) {
3488 if (new->multiboot) {
3489 if (needs & NEED_KERNEL) {
3490 newLine = addLineTmpl(new, tmplLine, newLine,
3491 newKernelPath +
3492 strlen(prefix), config->cfi);
3493 needs &= ~NEED_KERNEL;
3494 } else if (config->cfi->mbInitRdIsModule &&
3495 (needs & NEED_INITRD)) {
3496 char *initrdVal;
3497 initrdVal = getInitrdVal(config, prefix, tmplLine,
3498 newKernelInitrd, extraInitrds,
3499 extraInitrdCount);
3500 newLine = addLineTmpl(new, tmplLine, newLine,
3501 initrdVal, config->cfi);
3502 free(initrdVal);
3503 needs &= ~NEED_INITRD;
3504 }
3505 } else if (needs & NEED_KERNEL) {
3506 /* template is multi but new is not,
3507 * insert the kernel in the first module slot
3508 */
3509 tmplLine->type = preferredLineType(LT_KERNEL, config->cfi);
3510 free(tmplLine->elements[0].item);
3511 tmplLine->elements[0].item =
3512 strdup(getKeywordByType(tmplLine->type,
3513 config->cfi)->key);
3514 newLine = addLineTmpl(new, tmplLine, newLine,
3515 newKernelPath + strlen(prefix),
3516 config->cfi);
3517 needs &= ~NEED_KERNEL;
3518 } else if (needs & NEED_INITRD) {
3519 char *initrdVal;
3520 /* template is multi but new is not,
3521 * insert the initrd in the second module slot
3522 */
3523 tmplLine->type = preferredLineType(LT_INITRD, config->cfi);
3524 free(tmplLine->elements[0].item);
3525 tmplLine->elements[0].item =
3526 strdup(getKeywordByType(tmplLine->type,
3527 config->cfi)->key);
3528 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3529 newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3530 free(initrdVal);
3531 needs &= ~NEED_INITRD;
3532 }
3533
3534 } else if (isinitrd(tmplLine->type) && tmplLine->numElements >= 2) {
3535 if (needs & NEED_INITRD &&
3536 new->multiboot && !template->multiboot &&
3537 config->cfi->mbInitRdIsModule) {
3538 /* make sure we don't insert the module initrd
3539 * before the module kernel... if we don't do it here,
3540 * it will be inserted following the template.
3541 */
3542 if (!needs & NEED_KERNEL) {
3543 char *initrdVal;
3544
3545 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3546 newLine = addLine(new, config->cfi, LT_MBMODULE,
3547 config->secondaryIndent,
3548 initrdVal);
3549 free(initrdVal);
3550 needs &= ~NEED_INITRD;
3551 }
3552 } else if (needs & NEED_INITRD) {
3553 char *initrdVal;
3554 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3555 newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3556 free(initrdVal);
3557 needs &= ~NEED_INITRD;
3558 }
3559
3560 } else if (tmplLine->type == LT_MENUENTRY &&
3561 (needs & NEED_TITLE)) {
3562 requote(tmplLine, config->cfi);
3563 char *nkt = malloc(strlen(newKernelTitle)+3);
3564 strcpy(nkt, "'");
3565 strcat(nkt, newKernelTitle);
3566 strcat(nkt, "'");
3567 newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
3568 free(nkt);
3569 needs &= ~NEED_TITLE;
3570 } else if (tmplLine->type == LT_TITLE &&
3571 (needs & NEED_TITLE)) {
3572 if (tmplLine->numElements >= 2) {
3573 newLine = addLineTmpl(new, tmplLine, newLine,
3574 newKernelTitle, config->cfi);
3575 needs &= ~NEED_TITLE;
3576 } else if (tmplLine->numElements == 1 &&
3577 config->cfi->titleBracketed) {
3578 /* addLineTmpl doesn't handle titleBracketed */
3579 newLine = addLine(new, config->cfi, LT_TITLE,
3580 tmplLine->indent, newKernelTitle);
3581 needs &= ~NEED_TITLE;
3582 }
3583 } else if (tmplLine->type == LT_ECHO) {
3584 requote(tmplLine, config->cfi);
3585 static const char *prefix = "'Loading ";
3586 if (tmplLine->numElements > 1 &&
3587 strstr(tmplLine->elements[1].item, prefix) &&
3588 masterLine->next &&
3589 iskernel(masterLine->next->type)) {
3590 char *newTitle = malloc(strlen(prefix) +
3591 strlen(newKernelTitle) + 2);
3592
3593 strcpy(newTitle, prefix);
3594 strcat(newTitle, newKernelTitle);
3595 strcat(newTitle, "'");
3596 newLine = addLine(new, config->cfi, LT_ECHO,
3597 tmplLine->indent, newTitle);
3598 free(newTitle);
3599 } else {
3600 /* pass through other lines from the template */
3601 newLine = addLineTmpl(new, tmplLine, newLine, NULL,
3602 config->cfi);
3603 }
3604 } else {
3605 /* pass through other lines from the template */
3606 newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
3607 }
3608 }
3609
3610 } else {
3611 /* don't have a template, so start the entry with the
3612 * appropriate starting line
3613 */
3614 switch (config->cfi->entryStart) {
3615 case LT_KERNEL:
3616 case LT_KERNEL_EFI:
3617 if (new->multiboot && config->cfi->mbHyperFirst) {
3618 /* fall through to LT_HYPER */
3619 } else {
3620 newLine = addLine(new, config->cfi,
3621 preferredLineType(LT_KERNEL, config->cfi),
3622 config->primaryIndent,
3623 newKernelPath + strlen(prefix));
3624 needs &= ~NEED_KERNEL;
3625 break;
3626 }
3627
3628 case LT_HYPER:
3629 newLine = addLine(new, config->cfi, LT_HYPER,
3630 config->primaryIndent,
3631 newMBKernel + strlen(prefix));
3632 needs &= ~NEED_MB;
3633 break;
3634
3635 case LT_MENUENTRY: {
3636 char *nkt = malloc(strlen(newKernelTitle)+3);
3637 strcpy(nkt, "'");
3638 strcat(nkt, newKernelTitle);
3639 strcat(nkt, "'");
3640 newLine = addLine(new, config->cfi, LT_MENUENTRY,
3641 config->primaryIndent, nkt);
3642 free(nkt);
3643 needs &= ~NEED_TITLE;
3644 needs |= NEED_END;
3645 break;
3646 }
3647 case LT_TITLE:
3648 if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
3649 char * templabel;
3650 int x = 0, y = 0;
3651
3652 templabel = strdup(newKernelTitle);
3653 while( templabel[x]){
3654 if( templabel[x] == ' ' ){
3655 y = x;
3656 while( templabel[y] ){
3657 templabel[y] = templabel[y+1];
3658 y++;
3659 }
3660 }
3661 x++;
3662 }
3663 newLine = addLine(new, config->cfi, LT_TITLE,
3664 config->primaryIndent, templabel);
3665 free(templabel);
3666 }else{
3667 newLine = addLine(new, config->cfi, LT_TITLE,
3668 config->primaryIndent, newKernelTitle);
3669 }
3670 needs &= ~NEED_TITLE;
3671 break;
3672
3673 default:
3674 abort();
3675 }
3676 }
3677
3678 /* add the remainder of the lines, i.e. those that either
3679 * weren't present in the template, or in the case of no template,
3680 * all the lines following the entryStart.
3681 */
3682 if (needs & NEED_TITLE) {
3683 newLine = addLine(new, config->cfi, LT_TITLE,
3684 config->secondaryIndent,
3685 newKernelTitle);
3686 needs &= ~NEED_TITLE;
3687 }
3688 if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
3689 newLine = addLine(new, config->cfi, LT_HYPER,
3690 config->secondaryIndent,
3691 newMBKernel + strlen(prefix));
3692 needs &= ~NEED_MB;
3693 }
3694 if (needs & NEED_KERNEL) {
3695 newLine = addLine(new, config->cfi,
3696 (new->multiboot && getKeywordByType(LT_MBMODULE,
3697 config->cfi))
3698 ? LT_MBMODULE
3699 : preferredLineType(LT_KERNEL, config->cfi),
3700 config->secondaryIndent,
3701 newKernelPath + strlen(prefix));
3702 needs &= ~NEED_KERNEL;
3703 }
3704 if (needs & NEED_MB) {
3705 newLine = addLine(new, config->cfi, LT_HYPER,
3706 config->secondaryIndent,
3707 newMBKernel + strlen(prefix));
3708 needs &= ~NEED_MB;
3709 }
3710 if (needs & NEED_INITRD) {
3711 char *initrdVal;
3712 initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
3713 newLine = addLine(new, config->cfi,
3714 (new->multiboot && getKeywordByType(LT_MBMODULE,
3715 config->cfi))
3716 ? LT_MBMODULE
3717 : preferredLineType(LT_INITRD, config->cfi),
3718 config->secondaryIndent,
3719 initrdVal);
3720 free(initrdVal);
3721 needs &= ~NEED_INITRD;
3722 }
3723 if (needs & NEED_END) {
3724 newLine = addLine(new, config->cfi, LT_ENTRY_END,
3725 config->secondaryIndent, NULL);
3726 needs &= ~NEED_END;
3727 }
3728
3729 if (needs) {
3730 printf(_("grubby: needs=%d, aborting\n"), needs);
3731 abort();
3732 }
3733
3734 if (updateImage(config, "0", prefix, newKernelArgs, NULL,
3735 newMBKernelArgs, NULL)) return 1;
3736
3737 return 0;
3738 }
3739
3740 static void traceback(int signum)
3741 {
3742 void *array[40];
3743 size_t size;
3744
3745 signal(SIGSEGV, SIG_DFL);
3746 memset(array, '\0', sizeof (array));
3747 size = backtrace(array, 40);
3748
3749 fprintf(stderr, "grubby recieved SIGSEGV! Backtrace (%ld):\n",
3750 (unsigned long)size);
3751 backtrace_symbols_fd(array, size, STDERR_FILENO);
3752 exit(1);
3753 }
3754
3755 int main(int argc, const char ** argv) {
3756 poptContext optCon;
3757 const char * grubConfig = NULL;
3758 char * outputFile = NULL;
3759 int arg = 0;
3760 int flags = 0;
3761 int badImageOkay = 0;
3762 int configureGrub2 = 0;
3763 int configureLilo = 0, configureELilo = 0, configureGrub = 0;
3764 int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
3765 int configureExtLinux = 0;
3766 int bootloaderProbe = 0;
3767 int extraInitrdCount = 0;
3768 char * updateKernelPath = NULL;
3769 char * newKernelPath = NULL;
3770 char * removeKernelPath = NULL;
3771 char * newKernelArgs = NULL;
3772 char * newKernelInitrd = NULL;
3773 char * newKernelTitle = NULL;
3774 char * newKernelVersion = NULL;
3775 char * newMBKernel = NULL;
3776 char * newMBKernelArgs = NULL;
3777 char * removeMBKernelArgs = NULL;
3778 char * removeMBKernel = NULL;
3779 char * bootPrefix = NULL;
3780 char * defaultKernel = NULL;
3781 char * removeArgs = NULL;
3782 char * kernelInfo = NULL;
3783 char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
3784 const char * chptr = NULL;
3785 struct configFileInfo * cfi = NULL;
3786 struct grubConfig * config;
3787 struct singleEntry * template = NULL;
3788 int copyDefault = 0, makeDefault = 0;
3789 int displayDefault = 0;
3790 int displayDefaultIndex = 0;
3791 int displayDefaultTitle = 0;
3792 int defaultIndex = -1;
3793 struct poptOption options[] = {
3794 { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
3795 _("add an entry for the specified kernel"), _("kernel-path") },
3796 { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
3797 _("add an entry for the specified multiboot kernel"), NULL },
3798 { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
3799 _("default arguments for the new kernel or new arguments for "
3800 "kernel being updated"), _("args") },
3801 { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
3802 _("default arguments for the new multiboot kernel or "
3803 "new arguments for multiboot kernel being updated"), NULL },
3804 { "bad-image-okay", 0, 0, &badImageOkay, 0,
3805 _("don't sanity check images in boot entries (for testing only)"),
3806 NULL },
3807 { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
3808 _("filestystem which contains /boot directory (for testing only)"),
3809 _("bootfs") },
3810 #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
3811 { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
3812 _("check which bootloader is installed on boot sector") },
3813 #endif
3814 { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
3815 _("path to grub config file to update (\"-\" for stdin)"),
3816 _("path") },
3817 { "copy-default", 0, 0, &copyDefault, 0,
3818 _("use the default boot entry as a template for the new entry "
3819 "being added; if the default is not a linux image, or if "
3820 "the kernel referenced by the default image does not exist, "
3821 "the first linux entry whose kernel does exist is used as the "
3822 "template"), NULL },
3823 { "debug", 0, 0, &debug, 0,
3824 _("print debugging information for failures") },
3825 { "default-kernel", 0, 0, &displayDefault, 0,
3826 _("display the path of the default kernel") },
3827 { "default-index", 0, 0, &displayDefaultIndex, 0,
3828 _("display the index of the default kernel") },
3829 { "default-title", 0, 0, &displayDefaultTitle, 0,
3830 _("display the title of the default kernel") },
3831 { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
3832 _("configure elilo bootloader") },
3833 { "efi", 0, POPT_ARG_NONE, &isEfi, 0,
3834 _("force grub2 stanzas to use efi") },
3835 { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
3836 _("configure extlinux bootloader (from syslinux)") },
3837 { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
3838 _("configure grub bootloader") },
3839 { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
3840 _("configure grub2 bootloader") },
3841 { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
3842 _("display boot information for specified kernel"),
3843 _("kernel-path") },
3844 { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
3845 _("initrd image for the new kernel"), _("initrd-path") },
3846 { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
3847 _("auxilliary initrd image for things other than the new kernel"), _("initrd-path") },
3848 { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
3849 _("configure lilo bootloader") },
3850 { "make-default", 0, 0, &makeDefault, 0,
3851 _("make the newly added entry the default boot entry"), NULL },
3852 { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
3853 _("path to output updated config file (\"-\" for stdout)"),
3854 _("path") },
3855 { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
3856 _("remove kernel arguments"), NULL },
3857 { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
3858 _("remove multiboot kernel arguments"), NULL },
3859 { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
3860 _("remove all entries for the specified kernel"),
3861 _("kernel-path") },
3862 { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
3863 _("remove all entries for the specified multiboot kernel"), NULL },
3864 { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
3865 _("make the first entry referencing the specified kernel "
3866 "the default"), _("kernel-path") },
3867 { "set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
3868 _("make the given entry index the default entry"),
3869 _("entry-index") },
3870 { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
3871 _("configure silo bootloader") },
3872 { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
3873 _("title to use for the new kernel entry"), _("entry-title") },
3874 { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
3875 _("updated information for the specified kernel"),
3876 _("kernel-path") },
3877 { "version", 'v', 0, NULL, 'v',
3878 _("print the version of this program and exit"), NULL },
3879 { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
3880 _("configure yaboot bootloader") },
3881 { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
3882 _("configure zipl bootloader") },
3883 POPT_AUTOHELP
3884 { 0, 0, 0, 0, 0 }
3885 };
3886
3887 useextlinuxmenu=0;
3888
3889 signal(SIGSEGV, traceback);
3890
3891 optCon = poptGetContext("grubby", argc, argv, options, 0);
3892 poptReadDefaultConfig(optCon, 1);
3893
3894 while ((arg = poptGetNextOpt(optCon)) >= 0) {
3895 switch (arg) {
3896 case 'v':
3897 printf("grubby version %s\n", VERSION);
3898 exit(0);
3899 break;
3900 case 'i':
3901 if (extraInitrdCount < MAX_EXTRA_INITRDS) {
3902 extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
3903 } else {
3904 fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
3905 return 1;
3906 }
3907 break;
3908 }
3909 }
3910
3911 if (arg < -1) {
3912 fprintf(stderr, _("grubby: bad argument %s: %s\n"),
3913 poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
3914 poptStrerror(arg));
3915 return 1;
3916 }
3917
3918 if ((chptr = poptGetArg(optCon))) {
3919 fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
3920 return 1;
3921 }
3922
3923 if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
3924 configureYaboot + configureSilo + configureZipl +
3925 configureExtLinux ) > 1) {
3926 fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
3927 return 1;
3928 } else if (bootloaderProbe && grubConfig) {
3929 fprintf(stderr,
3930 _("grubby: cannot specify config file with --bootloader-probe\n"));
3931 return 1;
3932 } else if (configureGrub2) {
3933 cfi = &grub2ConfigType;
3934 } else if (configureLilo) {
3935 cfi = &liloConfigType;
3936 } else if (configureGrub) {
3937 cfi = &grubConfigType;
3938 } else if (configureELilo) {
3939 cfi = &eliloConfigType;
3940 } else if (configureYaboot) {
3941 cfi = &yabootConfigType;
3942 } else if (configureSilo) {
3943 cfi = &siloConfigType;
3944 } else if (configureZipl) {
3945 cfi = &ziplConfigType;
3946 } else if (configureExtLinux) {
3947 cfi = &extlinuxConfigType;
3948 useextlinuxmenu=1;
3949 }
3950
3951 if (!cfi) {
3952 if (grub2FindConfig(&grub2ConfigType))
3953 cfi = &grub2ConfigType;
3954 else
3955 #ifdef __ia64__
3956 cfi = &eliloConfigType;
3957 #elif __powerpc__
3958 cfi = &yabootConfigType;
3959 #elif __sparc__
3960 cfi = &siloConfigType;
3961 #elif __s390__
3962 cfi = &ziplConfigType;
3963 #elif __s390x__
3964 cfi = &ziplConfigtype;
3965 #else
3966 cfi = &grubConfigType;
3967 #endif
3968 }
3969
3970 if (!grubConfig) {
3971 if (cfi->findConfig)
3972 grubConfig = cfi->findConfig(cfi);
3973 if (!grubConfig)
3974 grubConfig = cfi->defaultConfig;
3975 }
3976
3977 if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||
3978 newKernelPath || removeKernelPath || makeDefault ||
3979 defaultKernel || displayDefaultIndex || displayDefaultTitle ||
3980 (defaultIndex >= 0))) {
3981 fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
3982 "specified option"));
3983 return 1;
3984 }
3985
3986 if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||
3987 removeKernelPath)) {
3988 fprintf(stderr, _("grubby: --default-kernel and --info may not "
3989 "be used when adding or removing kernels\n"));
3990 return 1;
3991 }
3992
3993 if (newKernelPath && !newKernelTitle) {
3994 fprintf(stderr, _("grubby: kernel title must be specified\n"));
3995 return 1;
3996 } else if (!newKernelPath && (newKernelTitle || copyDefault ||
3997 (newKernelInitrd && !updateKernelPath)||
3998 makeDefault || extraInitrdCount > 0)) {
3999 fprintf(stderr, _("grubby: kernel path expected\n"));
4000 return 1;
4001 }
4002
4003 if (newKernelPath && updateKernelPath) {
4004 fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
4005 "not be used together"));
4006 return 1;
4007 }
4008
4009 if (makeDefault && defaultKernel) {
4010 fprintf(stderr, _("grubby: --make-default and --default-kernel "
4011 "may not be used together\n"));
4012 return 1;
4013 } else if (defaultKernel && removeKernelPath &&
4014 !strcmp(defaultKernel, removeKernelPath)) {
4015 fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));
4016 return 1;
4017 } else if (defaultKernel && newKernelPath &&
4018 !strcmp(defaultKernel, newKernelPath)) {
4019 makeDefault = 1;
4020 defaultKernel = NULL;
4021 }
4022 else if (defaultKernel && (defaultIndex >= 0)) {
4023 fprintf(stderr, _("grubby: --set-default and --set-default-index "
4024 "may not be used together\n"));
4025 return 1;
4026 }
4027
4028 if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
4029 fprintf(stderr, _("grubby: output file must be specified if stdin "
4030 "is used\n"));
4031 return 1;
4032 }
4033
4034 if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
4035 && !kernelInfo && !bootloaderProbe && !updateKernelPath
4036 && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle
4037 && (defaultIndex == -1)) {
4038 fprintf(stderr, _("grubby: no action specified\n"));
4039 return 1;
4040 }
4041
4042 flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
4043
4044 if (cfi->needsBootPrefix) {
4045 if (!bootPrefix) {
4046 bootPrefix = findBootPrefix();
4047 if (!bootPrefix) return 1;
4048 } else {
4049 /* this shouldn't end with a / */
4050 if (bootPrefix[strlen(bootPrefix) - 1] == '/')
4051 bootPrefix[strlen(bootPrefix) - 1] = '\0';
4052 }
4053 } else {
4054 bootPrefix = "";
4055 }
4056
4057 if (!cfi->mbAllowExtraInitRds &&
4058 extraInitrdCount > 0) {
4059 fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
4060 return 1;
4061 }
4062
4063 if (bootloaderProbe) {
4064 int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
4065 struct grubConfig * lconfig, * gconfig, * yconfig, * econfig;
4066
4067 const char *grub2config = grub2FindConfig(&grub2ConfigType);
4068 if (grub2config) {
4069 gconfig = readConfig(grub2config, &grub2ConfigType);
4070 if (!gconfig)
4071 gr2c = 1;
4072 else
4073 gr2c = checkForGrub2(gconfig);
4074 }
4075
4076 const char *grubconfig = grubFindConfig(&grubConfigType);
4077 if (!access(grubconfig, F_OK)) {
4078 gconfig = readConfig(grubconfig, &grubConfigType);
4079 if (!gconfig)
4080 grc = 1;
4081 else
4082 grc = checkForGrub(gconfig);
4083 }
4084
4085 if (!access(liloConfigType.defaultConfig, F_OK)) {
4086 lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);
4087 if (!lconfig)
4088 lrc = 1;
4089 else
4090 lrc = checkForLilo(lconfig);
4091 }
4092
4093 if (!access(eliloConfigType.defaultConfig, F_OK)) {
4094 econfig = readConfig(eliloConfigType.defaultConfig,
4095 &eliloConfigType);
4096 if (!econfig)
4097 erc = 1;
4098 else
4099 erc = checkForElilo(econfig);
4100 }
4101
4102 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
4103 lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
4104 if (!lconfig)
4105 extrc = 1;
4106 else
4107 extrc = checkForExtLinux(lconfig);
4108 }
4109
4110
4111 if (!access(yabootConfigType.defaultConfig, F_OK)) {
4112 yconfig = readConfig(yabootConfigType.defaultConfig,
4113 &yabootConfigType);
4114 if (!yconfig)
4115 yrc = 1;
4116 else
4117 yrc = checkForYaboot(yconfig);
4118 }
4119
4120 if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1 ||
4121 erc == 1)
4122 return 1;
4123
4124 if (lrc == 2) printf("lilo\n");
4125 if (gr2c == 2) printf("grub2\n");
4126 if (grc == 2) printf("grub\n");
4127 if (extrc == 2) printf("extlinux\n");
4128 if (yrc == 2) printf("yaboot\n");
4129 if (erc == 2) printf("elilo\n");
4130
4131 return 0;
4132 }
4133
4134 config = readConfig(grubConfig, cfi);
4135 if (!config) return 1;
4136
4137 if (displayDefault) {
4138 struct singleLine * line;
4139 struct singleEntry * entry;
4140 char * rootspec;
4141
4142 if (config->defaultImage == -1) return 0;
4143 entry = findEntryByIndex(config, config->defaultImage);
4144 if (!entry) return 0;
4145 if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
4146
4147 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI, entry->lines);
4148 if (!line) return 0;
4149
4150 rootspec = getRootSpecifier(line->elements[1].item);
4151 printf("%s%s\n", bootPrefix, line->elements[1].item +
4152 ((rootspec != NULL) ? strlen(rootspec) : 0));
4153
4154 return 0;
4155
4156 } else if (displayDefaultTitle) {
4157 struct singleLine * line;
4158 struct singleEntry * entry;
4159
4160 if (config->defaultImage == -1) return 0;
4161 entry = findEntryByIndex(config, config->defaultImage);
4162 if (!entry) return 0;
4163
4164 if (!configureGrub2) {
4165 line = getLineByType(LT_TITLE, entry->lines);
4166 if (!line) return 0;
4167 printf("%s\n", line->elements[1].item);
4168
4169 } else {
4170 char * title;
4171
4172 dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
4173 line = getLineByType(LT_MENUENTRY, entry->lines);
4174 if (!line) return 0;
4175 title = grub2ExtractTitle(line);
4176 if (title)
4177 printf("%s\n", title);
4178 }
4179 return 0;
4180
4181 } else if (displayDefaultIndex) {
4182 if (config->defaultImage == -1) return 0;
4183 printf("%i\n", config->defaultImage);
4184
4185 } else if (kernelInfo)
4186 return displayInfo(config, kernelInfo, bootPrefix);
4187
4188 if (copyDefault) {
4189 template = findTemplate(config, bootPrefix, NULL, 0, flags);
4190 if (!template) return 1;
4191 }
4192
4193 markRemovedImage(config, removeKernelPath, bootPrefix);
4194 markRemovedImage(config, removeMBKernel, bootPrefix);
4195 setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
4196 bootPrefix, flags, defaultIndex);
4197 setFallbackImage(config, newKernelPath != NULL);
4198 if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
4199 removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
4200 if (updateKernelPath && newKernelInitrd) {
4201 if (updateInitrd(config, updateKernelPath, bootPrefix,
4202 newKernelInitrd)) return 1;
4203 }
4204 if (addNewKernel(config, template, bootPrefix, newKernelPath,
4205 newKernelTitle, newKernelArgs, newKernelInitrd,
4206 (const char **)extraInitrds, extraInitrdCount,
4207 newMBKernel, newMBKernelArgs)) return 1;
4208
4209
4210 if (numEntries(config) == 0) {
4211 fprintf(stderr, _("grubby: doing this would leave no kernel entries. "
4212 "Not writing out new config.\n"));
4213 return 1;
4214 }
4215
4216 if (!outputFile)
4217 outputFile = (char *)grubConfig;
4218
4219 return writeConfig(config, outputFile, bootPrefix);
4220 }