Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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