Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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