Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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