Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1746 - (show annotations) (download)
Sat Feb 18 01:06:20 2012 UTC (12 years, 2 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 104452 byte(s)
Refactor grub2 title extraction, making it a function

'--default-title' now uses that functions, but others such as '--info'
could, IMHO, also display the title and thus, use the refactored
function.

Signed-off-by: Cleber Rosa <crosa@redhat.com>


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