Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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