Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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