Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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