Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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