Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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