Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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