Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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