Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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