Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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