Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2960 - (show annotations) (download)
Wed Jun 29 14:07:59 2016 UTC (7 years, 10 months ago) by niro
File MIME type: text/plain
File size: 130334 byte(s)
Support filtering --update-kernel= by title as well.
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 const char * title) {
3314 struct singleEntry * entry;
3315 struct singleLine * line, * kernelLine, *endLine = NULL;
3316 int index = 0;
3317
3318 if (!image) return 0;
3319
3320 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3321 kernelLine = getLineByType(LT_MBMODULE, entry->lines);
3322 if (!kernelLine) continue;
3323
3324 /* if title is supplied, the entry's title must match it. */
3325 if (title) {
3326 line = getLineByType(LT_TITLE|LT_MENUENTRY, entry->lines);
3327 char *linetitle = extractTitle(line);
3328
3329 if (!linetitle)
3330 continue;
3331 if (strcmp(title, linetitle)) {
3332 free(linetitle);
3333 continue;
3334 }
3335 free(linetitle);
3336 }
3337
3338 if (prefix) {
3339 int prefixLen = strlen(prefix);
3340 if (!strncmp(initrd, prefix, prefixLen))
3341 initrd += prefixLen;
3342 }
3343 endLine = getLineByType(LT_ENTRY_END, entry->lines);
3344 if (endLine)
3345 removeLine(entry, endLine);
3346 line = addLine(entry, cfg->cfi, preferredLineType(LT_MBMODULE,cfg->cfi),
3347 kernelLine->indent, initrd);
3348 if (!line)
3349 return 1;
3350 if (endLine) {
3351 line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3352 if (!line)
3353 return 1;
3354 }
3355
3356 break;
3357 }
3358
3359 return 0;
3360 }
3361
3362 int updateInitrd(struct grubConfig * cfg, const char * image,
3363 const char * prefix, const char * initrd, const char * title) {
3364 struct singleEntry * entry;
3365 struct singleLine * line, * kernelLine, *endLine = NULL;
3366 int index = 0;
3367
3368 if (!image) return 0;
3369
3370 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3371 kernelLine = getLineByType(LT_KERNEL|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3372 if (!kernelLine) continue;
3373
3374 /* if title is supplied, the entry's title must match it. */
3375 if (title) {
3376 line = getLineByType(LT_TITLE|LT_MENUENTRY, entry->lines);
3377 char *linetitle = extractTitle(line);
3378
3379 if (!linetitle)
3380 continue;
3381 if (strcmp(title, linetitle)) {
3382 free(linetitle);
3383 continue;
3384 }
3385 free(linetitle);
3386 }
3387
3388 line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
3389 if (line)
3390 removeLine(entry, line);
3391 if (prefix) {
3392 int prefixLen = strlen(prefix);
3393 if (!strncmp(initrd, prefix, prefixLen))
3394 initrd += prefixLen;
3395 }
3396 endLine = getLineByType(LT_ENTRY_END, entry->lines);
3397 if (endLine)
3398 removeLine(entry, endLine);
3399 enum lineType_e lt;
3400 switch(kernelLine->type) {
3401 case LT_KERNEL:
3402 lt = LT_INITRD;
3403 break;
3404 case LT_KERNEL_EFI:
3405 lt = LT_INITRD_EFI;
3406 break;
3407 case LT_KERNEL_16:
3408 lt = LT_INITRD_16;
3409 break;
3410 default:
3411 lt = preferredLineType(LT_INITRD, cfg->cfi);
3412 }
3413 line = addLine(entry, cfg->cfi, lt, kernelLine->indent, initrd);
3414 if (!line)
3415 return 1;
3416 if (endLine) {
3417 line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3418 if (!line)
3419 return 1;
3420 }
3421
3422 break;
3423 }
3424
3425 return 0;
3426 }
3427
3428 int checkDeviceBootloader(const char * device, const unsigned char * boot) {
3429 int fd;
3430 unsigned char bootSect[512];
3431 int offset;
3432
3433 fd = open(device, O_RDONLY);
3434 if (fd < 0) {
3435 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3436 device, strerror(errno));
3437 return 1;
3438 }
3439
3440 if (read(fd, bootSect, 512) != 512) {
3441 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3442 device, strerror(errno));
3443 return 1;
3444 }
3445 close(fd);
3446
3447 /* first three bytes should match, a jmp short should be in there */
3448 if (memcmp(boot, bootSect, 3))
3449 return 0;
3450
3451 if (boot[1] == JMP_SHORT_OPCODE) {
3452 offset = boot[2] + 2;
3453 } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3454 offset = (boot[3] << 8) + boot[2] + 2;
3455 } else if (boot[0] == JMP_SHORT_OPCODE) {
3456 offset = boot[1] + 2;
3457 /*
3458 * it looks like grub, when copying stage1 into the mbr, patches stage1
3459 * right after the JMP location, replacing other instructions such as
3460 * JMPs for NOOPs. So, relax the check a little bit by skipping those
3461 * different bytes.
3462 */
3463 if ((bootSect[offset + 1] == NOOP_OPCODE)
3464 && (bootSect[offset + 2] == NOOP_OPCODE)) {
3465 offset = offset + 3;
3466 }
3467 } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3468 offset = (boot[2] << 8) + boot[1] + 2;
3469 } else {
3470 return 0;
3471 }
3472
3473 if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
3474 return 0;
3475
3476 return 2;
3477 }
3478
3479 int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {
3480 int fd;
3481 char buf[65536];
3482 char * end;
3483 char * chptr;
3484 char * chptr2;
3485 int rc;
3486
3487 /* it's on raid; we need to parse /proc/mdstat and check all of the
3488 *raw* devices listed in there */
3489
3490 if (!strncmp(mdDev, "/dev/", 5))
3491 mdDev += 5;
3492
3493 if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
3494 fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
3495 strerror(errno));
3496 return 2;
3497 }
3498
3499 rc = read(fd, buf, sizeof(buf) - 1);
3500 if (rc < 0 || rc == (sizeof(buf) - 1)) {
3501 fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
3502 strerror(errno));
3503 close(fd);
3504 return 2;
3505 }
3506 close(fd);
3507 buf[rc] = '\0';
3508
3509 chptr = buf;
3510 while (*chptr) {
3511 end = strchr(chptr, '\n');
3512 if (!end) break;
3513 *end = '\0';
3514
3515 if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
3516 chptr[strlen(mdDev)] == ' ') {
3517
3518 /* found the device */
3519 while (*chptr && *chptr != ':') chptr++;
3520 chptr++;
3521 while (*chptr && isspace(*chptr)) chptr++;
3522
3523 /* skip the "active" bit */
3524 while (*chptr && !isspace(*chptr)) chptr++;
3525 while (*chptr && isspace(*chptr)) chptr++;
3526
3527 /* skip the raid level */
3528 while (*chptr && !isspace(*chptr)) chptr++;
3529 while (*chptr && isspace(*chptr)) chptr++;
3530
3531 /* everything else is partition stuff */
3532 while (*chptr) {
3533 chptr2 = chptr;
3534 while (*chptr2 && *chptr2 != '[') chptr2++;
3535 if (!*chptr2) break;
3536
3537 /* yank off the numbers at the end */
3538 chptr2--;
3539 while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;
3540 chptr2++;
3541 *chptr2 = '\0';
3542
3543 /* Better, now we need the /dev/ back. We're done with
3544 * everything before this point, so we can just put
3545 * the /dev/ part there. There will always be room. */
3546 memcpy(chptr - 5, "/dev/", 5);
3547 rc = checkDeviceBootloader(chptr - 5, boot);
3548 if (rc != 2) {
3549 return rc;
3550 }
3551
3552 chptr = chptr2 + 1;
3553 /* skip the [11] bit */
3554 while (*chptr && !isspace(*chptr)) chptr++;
3555 /* and move to the next one */
3556 while (*chptr && isspace(*chptr)) chptr++;
3557 }
3558
3559 /* we're good to go */
3560 return 2;
3561 }
3562
3563 chptr = end + 1;
3564 }
3565
3566 fprintf(stderr,
3567 _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
3568 mdDev);
3569 return 0;
3570 }
3571
3572 int checkForLilo(struct grubConfig * config) {
3573 int fd;
3574 unsigned char boot[512];
3575 struct singleLine * line;
3576
3577 for (line = config->theLines; line; line = line->next)
3578 if (line->type == LT_BOOT) break;
3579
3580 if (!line) {
3581 fprintf(stderr,
3582 _("grubby: no boot line found in lilo configuration\n"));
3583 return 1;
3584 }
3585
3586 if (line->numElements != 2) return 1;
3587
3588 fd = open("/boot/boot.b", O_RDONLY);
3589 if (fd < 0) {
3590 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3591 "/boot/boot.b", strerror(errno));
3592 return 1;
3593 }
3594
3595 if (read(fd, boot, 512) != 512) {
3596 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3597 "/boot/boot.b", strerror(errno));
3598 return 1;
3599 }
3600 close(fd);
3601
3602 if (!strncmp("/dev/md", line->elements[1].item, 7))
3603 return checkLiloOnRaid(line->elements[1].item, boot);
3604
3605 return checkDeviceBootloader(line->elements[1].item, boot);
3606 }
3607
3608 int checkForGrub2(struct grubConfig * config) {
3609 if (!access("/etc/grub.d/", R_OK))
3610 return 2;
3611
3612 return 1;
3613 }
3614
3615 int checkForGrub(struct grubConfig * config) {
3616 int fd;
3617 unsigned char bootSect[512];
3618 char * boot;
3619 int onSuse = isSuseSystem();
3620
3621
3622 if (onSuse) {
3623 if (parseSuseGrubConf(NULL, &boot))
3624 return 0;
3625 } else {
3626 if (parseSysconfigGrub(NULL, &boot))
3627 return 0;
3628 }
3629
3630 /* assume grub is not installed -- not an error condition */
3631 if (!boot)
3632 return 0;
3633
3634 fd = open("/boot/grub/stage1", O_RDONLY);
3635 if (fd < 0)
3636 /* this doesn't exist if grub hasn't been installed */
3637 return 0;
3638
3639 if (read(fd, bootSect, 512) != 512) {
3640 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3641 "/boot/grub/stage1", strerror(errno));
3642 close(fd);
3643 return 1;
3644 }
3645 close(fd);
3646
3647 /* The more elaborate checks do not work on SuSE. The checks done
3648 * seem to be reasonble (at least for now), so just return success
3649 */
3650 if (onSuse)
3651 return 2;
3652
3653 return checkDeviceBootloader(boot, bootSect);
3654 }
3655
3656 int checkForExtLinux(struct grubConfig * config) {
3657 int fd;
3658 unsigned char bootSect[512];
3659 char * boot;
3660 char executable[] = "/boot/extlinux/extlinux";
3661
3662 printf("entered: checkForExtLinux()\n");
3663
3664 if (parseSysconfigGrub(NULL, &boot))
3665 return 0;
3666
3667 /* assume grub is not installed -- not an error condition */
3668 if (!boot)
3669 return 0;
3670
3671 fd = open(executable, O_RDONLY);
3672 if (fd < 0)
3673 /* this doesn't exist if grub hasn't been installed */
3674 return 0;
3675
3676 if (read(fd, bootSect, 512) != 512) {
3677 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3678 executable, strerror(errno));
3679 return 1;
3680 }
3681 close(fd);
3682
3683 return checkDeviceBootloader(boot, bootSect);
3684 }
3685
3686 int checkForYaboot(struct grubConfig * config) {
3687 /*
3688 * This is a simplistic check that we consider good enough for own puporses
3689 *
3690 * If we were to properly check if yaboot is *installed* we'd need to:
3691 * 1) get the system boot device (LT_BOOT)
3692 * 2) considering it's a raw filesystem, check if the yaboot binary matches
3693 * the content on the boot device
3694 * 3) if not, copy the binary to a temporary file and run "addnote" on it
3695 * 4) check again if binary and boot device contents match
3696 */
3697 if (!access("/etc/yaboot.conf", R_OK))
3698 return 2;
3699
3700 return 1;
3701 }
3702
3703 int checkForElilo(struct grubConfig * config) {
3704 if (!access("/etc/elilo.conf", R_OK))
3705 return 2;
3706
3707 return 1;
3708 }
3709
3710 static char * getRootSpecifier(char * str) {
3711 char * idx, * rootspec = NULL;
3712
3713 if (*str == '(') {
3714 idx = rootspec = strdup(str);
3715 while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;
3716 *(++idx) = '\0';
3717 }
3718 return rootspec;
3719 }
3720
3721 static char * getInitrdVal(struct grubConfig * config,
3722 const char * prefix, struct singleLine *tmplLine,
3723 const char * newKernelInitrd,
3724 const char ** extraInitrds, int extraInitrdCount)
3725 {
3726 char *initrdVal, *end;
3727 int i;
3728 size_t totalSize;
3729 size_t prefixLen;
3730 char separatorChar;
3731
3732 prefixLen = strlen(prefix);
3733 totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
3734
3735 for (i = 0; i < extraInitrdCount; i++) {
3736 totalSize += sizeof(separatorChar);
3737 totalSize += strlen(extraInitrds[i]) - prefixLen;
3738 }
3739
3740 initrdVal = end = malloc(totalSize);
3741
3742 end = stpcpy (end, newKernelInitrd + prefixLen);
3743
3744 separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
3745 for (i = 0; i < extraInitrdCount; i++) {
3746 const char *extraInitrd;
3747 int j;
3748
3749 extraInitrd = extraInitrds[i] + prefixLen;
3750 /* Don't add entries that are already there */
3751 if (tmplLine != NULL) {
3752 for (j = 2; j < tmplLine->numElements; j++)
3753 if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
3754 break;
3755
3756 if (j != tmplLine->numElements)
3757 continue;
3758 }
3759
3760 *end++ = separatorChar;
3761 end = stpcpy(end, extraInitrd);
3762 }
3763
3764 return initrdVal;
3765 }
3766
3767 int addNewKernel(struct grubConfig * config, struct singleEntry * template,
3768 const char * prefix,
3769 const char * newKernelPath, const char * newKernelTitle,
3770 const char * newKernelArgs, const char * newKernelInitrd,
3771 const char ** extraInitrds, int extraInitrdCount,
3772 const char * newMBKernel, const char * newMBKernelArgs,
3773 const char * newDevTreePath) {
3774 struct singleEntry * new;
3775 struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
3776 int needs;
3777 char * chptr;
3778
3779 if (!newKernelPath) return 0;
3780
3781 /* if the newKernelTitle is too long silently munge it into something
3782 * we can live with. truncating is first check, then we'll just mess with
3783 * it until it looks better */
3784 if (config->cfi->maxTitleLength &&
3785 (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
3786 char * buf = alloca(config->cfi->maxTitleLength + 7);
3787 char * numBuf = alloca(config->cfi->maxTitleLength + 1);
3788 int i = 1;
3789
3790 sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);
3791 while (findEntryByPath(config, buf, NULL, NULL)) {
3792 sprintf(numBuf, "%d", i++);
3793 strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
3794 }
3795
3796 newKernelTitle = buf + 6;
3797 }
3798
3799 new = malloc(sizeof(*new));
3800 new->skip = 0;
3801 new->multiboot = 0;
3802 new->next = config->entries;
3803 new->lines = NULL;
3804 config->entries = new;
3805
3806 /* copy/update from the template */
3807 needs = NEED_KERNEL | NEED_TITLE;
3808 if (newKernelInitrd)
3809 needs |= NEED_INITRD;
3810 if (newMBKernel) {
3811 needs |= NEED_MB;
3812 new->multiboot = 1;
3813 }
3814 if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))
3815 needs |= NEED_DEVTREE;
3816
3817 if (template) {
3818 for (masterLine = template->lines;
3819 masterLine && (tmplLine = lineDup(masterLine));
3820 lineFree(tmplLine), masterLine = masterLine->next)
3821 {
3822 dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
3823
3824 /* skip comments */
3825 chptr = tmplLine->indent;
3826 while (*chptr && isspace(*chptr)) chptr++;
3827 if (*chptr == '#') continue;
3828
3829 if (iskernel(tmplLine->type) && tmplLine->numElements >= 2) {
3830 if (!template->multiboot && (needs & NEED_MB)) {
3831 /* it's not a multiboot template and this is the kernel
3832 * line. Try to be intelligent about inserting the
3833 * hypervisor at the same time.
3834 */
3835 if (config->cfi->mbHyperFirst) {
3836 /* insert the hypervisor first */
3837 newLine = addLine(new, config->cfi, LT_HYPER,
3838 tmplLine->indent,
3839 newMBKernel + strlen(prefix));
3840 /* set up for adding the kernel line */
3841 free(tmplLine->indent);
3842 tmplLine->indent = strdup(config->secondaryIndent);
3843 needs &= ~NEED_MB;
3844 }
3845 if (needs & NEED_KERNEL) {
3846 /* use addLineTmpl to preserve line elements,
3847 * otherwise we could just call addLine. Unfortunately
3848 * this means making some changes to the template
3849 * such as the indent change above and the type
3850 * change below.
3851 */
3852 struct keywordTypes * mbm_kw =
3853 getKeywordByType(LT_MBMODULE, config->cfi);
3854 if (mbm_kw) {
3855 tmplLine->type = LT_MBMODULE;
3856 free(tmplLine->elements[0].item);
3857 tmplLine->elements[0].item = strdup(mbm_kw->key);
3858 }
3859 newLine = addLineTmpl(new, tmplLine, newLine,
3860 newKernelPath + strlen(prefix), config->cfi);
3861 needs &= ~NEED_KERNEL;
3862 }
3863 if (needs & NEED_MB) { /* !mbHyperFirst */
3864 newLine = addLine(new, config->cfi, LT_HYPER,
3865 config->secondaryIndent,
3866 newMBKernel + strlen(prefix));
3867 needs &= ~NEED_MB;
3868 }
3869 } else if (needs & NEED_KERNEL) {
3870 newLine = addLineTmpl(new, tmplLine, newLine,
3871 newKernelPath + strlen(prefix), config->cfi);
3872 needs &= ~NEED_KERNEL;
3873 }
3874
3875 } else if (tmplLine->type == LT_HYPER &&
3876 tmplLine->numElements >= 2) {
3877 if (needs & NEED_MB) {
3878 newLine = addLineTmpl(new, tmplLine, newLine,
3879 newMBKernel + strlen(prefix), config->cfi);
3880 needs &= ~NEED_MB;
3881 }
3882
3883 } else if (tmplLine->type == LT_MBMODULE &&
3884 tmplLine->numElements >= 2) {
3885 if (new->multiboot) {
3886 if (needs & NEED_KERNEL) {
3887 newLine = addLineTmpl(new, tmplLine, newLine,
3888 newKernelPath +
3889 strlen(prefix), config->cfi);
3890 needs &= ~NEED_KERNEL;
3891 } else if (config->cfi->mbInitRdIsModule &&
3892 (needs & NEED_INITRD)) {
3893 char *initrdVal;
3894 initrdVal = getInitrdVal(config, prefix, tmplLine,
3895 newKernelInitrd, extraInitrds,
3896 extraInitrdCount);
3897 newLine = addLineTmpl(new, tmplLine, newLine,
3898 initrdVal, config->cfi);
3899 free(initrdVal);
3900 needs &= ~NEED_INITRD;
3901 }
3902 } else if (needs & NEED_KERNEL) {
3903 /* template is multi but new is not,
3904 * insert the kernel in the first module slot
3905 */
3906 tmplLine->type = preferredLineType(LT_KERNEL, config->cfi);
3907 free(tmplLine->elements[0].item);
3908 tmplLine->elements[0].item =
3909 strdup(getKeywordByType(tmplLine->type,
3910 config->cfi)->key);
3911 newLine = addLineTmpl(new, tmplLine, newLine,
3912 newKernelPath + strlen(prefix),
3913 config->cfi);
3914 needs &= ~NEED_KERNEL;
3915 } else if (needs & NEED_INITRD) {
3916 char *initrdVal;
3917 /* template is multi but new is not,
3918 * insert the initrd in the second module slot
3919 */
3920 tmplLine->type = preferredLineType(LT_INITRD, config->cfi);
3921 free(tmplLine->elements[0].item);
3922 tmplLine->elements[0].item =
3923 strdup(getKeywordByType(tmplLine->type,
3924 config->cfi)->key);
3925 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3926 newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3927 free(initrdVal);
3928 needs &= ~NEED_INITRD;
3929 }
3930
3931 } else if (isinitrd(tmplLine->type) && tmplLine->numElements >= 2) {
3932 if (needs & NEED_INITRD &&
3933 new->multiboot && !template->multiboot &&
3934 config->cfi->mbInitRdIsModule) {
3935 /* make sure we don't insert the module initrd
3936 * before the module kernel... if we don't do it here,
3937 * it will be inserted following the template.
3938 */
3939 if (!needs & NEED_KERNEL) {
3940 char *initrdVal;
3941
3942 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3943 newLine = addLine(new, config->cfi, LT_MBMODULE,
3944 config->secondaryIndent,
3945 initrdVal);
3946 free(initrdVal);
3947 needs &= ~NEED_INITRD;
3948 }
3949 } else if (needs & NEED_INITRD) {
3950 char *initrdVal;
3951 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3952 newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3953 free(initrdVal);
3954 needs &= ~NEED_INITRD;
3955 }
3956
3957 } else if (tmplLine->type == LT_MENUENTRY &&
3958 (needs & NEED_TITLE)) {
3959 requote(tmplLine, config->cfi);
3960 char *nkt = malloc(strlen(newKernelTitle)+3);
3961 strcpy(nkt, "'");
3962 strcat(nkt, newKernelTitle);
3963 strcat(nkt, "'");
3964 newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
3965 free(nkt);
3966 needs &= ~NEED_TITLE;
3967 } else if (tmplLine->type == LT_TITLE &&
3968 (needs & NEED_TITLE)) {
3969 if (tmplLine->numElements >= 2) {
3970 newLine = addLineTmpl(new, tmplLine, newLine,
3971 newKernelTitle, config->cfi);
3972 needs &= ~NEED_TITLE;
3973 } else if (tmplLine->numElements == 1 &&
3974 config->cfi->titleBracketed) {
3975 /* addLineTmpl doesn't handle titleBracketed */
3976 newLine = addLine(new, config->cfi, LT_TITLE,
3977 tmplLine->indent, newKernelTitle);
3978 needs &= ~NEED_TITLE;
3979 }
3980 } else if (tmplLine->type == LT_ECHO) {
3981 requote(tmplLine, config->cfi);
3982 static const char *prefix = "'Loading ";
3983 if (tmplLine->numElements > 1 &&
3984 strstr(tmplLine->elements[1].item, prefix) &&
3985 masterLine->next &&
3986 iskernel(masterLine->next->type)) {
3987 char *newTitle = malloc(strlen(prefix) +
3988 strlen(newKernelTitle) + 2);
3989
3990 strcpy(newTitle, prefix);
3991 strcat(newTitle, newKernelTitle);
3992 strcat(newTitle, "'");
3993 newLine = addLine(new, config->cfi, LT_ECHO,
3994 tmplLine->indent, newTitle);
3995 free(newTitle);
3996 } else {
3997 /* pass through other lines from the template */
3998 newLine = addLineTmpl(new, tmplLine, newLine, NULL,
3999 config->cfi);
4000 }
4001 } else if (tmplLine->type == LT_DEVTREE &&
4002 tmplLine->numElements == 2 && newDevTreePath) {
4003 newLine = addLineTmpl(new, tmplLine, newLine,
4004 newDevTreePath + strlen(prefix),
4005 config->cfi);
4006 needs &= ~NEED_DEVTREE;
4007 } else if (tmplLine->type == LT_ENTRY_END && needs & NEED_DEVTREE) {
4008 const char *ndtp = newDevTreePath;
4009 if (!strncmp(newDevTreePath, prefix, strlen(prefix)))
4010 ndtp += strlen(prefix);
4011 newLine = addLine(new, config->cfi, LT_DEVTREE,
4012 config->secondaryIndent,
4013 ndtp);
4014 needs &= ~NEED_DEVTREE;
4015 newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
4016 } else {
4017 /* pass through other lines from the template */
4018 newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
4019 }
4020 }
4021
4022 } else {
4023 /* don't have a template, so start the entry with the
4024 * appropriate starting line
4025 */
4026 switch (config->cfi->entryStart) {
4027 case LT_KERNEL:
4028 case LT_KERNEL_EFI:
4029 case LT_KERNEL_16:
4030 if (new->multiboot && config->cfi->mbHyperFirst) {
4031 /* fall through to LT_HYPER */
4032 } else {
4033 newLine = addLine(new, config->cfi,
4034 preferredLineType(LT_KERNEL, config->cfi),
4035 config->primaryIndent,
4036 newKernelPath + strlen(prefix));
4037 needs &= ~NEED_KERNEL;
4038 break;
4039 }
4040
4041 case LT_HYPER:
4042 newLine = addLine(new, config->cfi, LT_HYPER,
4043 config->primaryIndent,
4044 newMBKernel + strlen(prefix));
4045 needs &= ~NEED_MB;
4046 break;
4047
4048 case LT_MENUENTRY: {
4049 char *nkt = malloc(strlen(newKernelTitle)+3);
4050 strcpy(nkt, "'");
4051 strcat(nkt, newKernelTitle);
4052 strcat(nkt, "'");
4053 newLine = addLine(new, config->cfi, LT_MENUENTRY,
4054 config->primaryIndent, nkt);
4055 free(nkt);
4056 needs &= ~NEED_TITLE;
4057 needs |= NEED_END;
4058 break;
4059 }
4060 case LT_TITLE:
4061 if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
4062 char * templabel;
4063 int x = 0, y = 0;
4064
4065 templabel = strdup(newKernelTitle);
4066 while( templabel[x]){
4067 if( templabel[x] == ' ' ){
4068 y = x;
4069 while( templabel[y] ){
4070 templabel[y] = templabel[y+1];
4071 y++;
4072 }
4073 }
4074 x++;
4075 }
4076 newLine = addLine(new, config->cfi, LT_TITLE,
4077 config->primaryIndent, templabel);
4078 free(templabel);
4079 }else{
4080 newLine = addLine(new, config->cfi, LT_TITLE,
4081 config->primaryIndent, newKernelTitle);
4082 }
4083 needs &= ~NEED_TITLE;
4084 break;
4085
4086 default:
4087 abort();
4088 }
4089 }
4090
4091 struct singleLine *endLine = NULL;
4092 endLine = getLineByType(LT_ENTRY_END, new->lines);
4093 if (endLine) {
4094 removeLine(new, endLine);
4095 needs |= NEED_END;
4096 }
4097
4098 /* add the remainder of the lines, i.e. those that either
4099 * weren't present in the template, or in the case of no template,
4100 * all the lines following the entryStart.
4101 */
4102 if (needs & NEED_TITLE) {
4103 newLine = addLine(new, config->cfi, LT_TITLE,
4104 config->secondaryIndent,
4105 newKernelTitle);
4106 needs &= ~NEED_TITLE;
4107 }
4108 if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
4109 newLine = addLine(new, config->cfi, LT_HYPER,
4110 config->secondaryIndent,
4111 newMBKernel + strlen(prefix));
4112 needs &= ~NEED_MB;
4113 }
4114 if (needs & NEED_KERNEL) {
4115 newLine = addLine(new, config->cfi,
4116 (new->multiboot && getKeywordByType(LT_MBMODULE,
4117 config->cfi))
4118 ? LT_MBMODULE
4119 : preferredLineType(LT_KERNEL, config->cfi),
4120 config->secondaryIndent,
4121 newKernelPath + strlen(prefix));
4122 needs &= ~NEED_KERNEL;
4123 }
4124 if (needs & NEED_MB) {
4125 newLine = addLine(new, config->cfi, LT_HYPER,
4126 config->secondaryIndent,
4127 newMBKernel + strlen(prefix));
4128 needs &= ~NEED_MB;
4129 }
4130 if (needs & NEED_INITRD) {
4131 char *initrdVal;
4132 initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
4133 newLine = addLine(new, config->cfi,
4134 (new->multiboot && getKeywordByType(LT_MBMODULE,
4135 config->cfi))
4136 ? LT_MBMODULE
4137 : preferredLineType(LT_INITRD, config->cfi),
4138 config->secondaryIndent,
4139 initrdVal);
4140 free(initrdVal);
4141 needs &= ~NEED_INITRD;
4142 }
4143 if (needs & NEED_DEVTREE) {
4144 newLine = addLine(new, config->cfi, LT_DEVTREE,
4145 config->secondaryIndent,
4146 newDevTreePath);
4147 needs &= ~NEED_DEVTREE;
4148 }
4149
4150 /* NEEDS_END must be last on bootloaders that need it... */
4151 if (needs & NEED_END) {
4152 newLine = addLine(new, config->cfi, LT_ENTRY_END,
4153 config->secondaryIndent, NULL);
4154 needs &= ~NEED_END;
4155 }
4156
4157 if (needs) {
4158 printf(_("grubby: needs=%d, aborting\n"), needs);
4159 abort();
4160 }
4161
4162 if (updateImage(config, "0", prefix, newKernelArgs, NULL,
4163 newMBKernelArgs, NULL)) return 1;
4164
4165 return 0;
4166 }
4167
4168 static void traceback(int signum)
4169 {
4170 void *array[40];
4171 size_t size;
4172
4173 signal(SIGSEGV, SIG_DFL);
4174 memset(array, '\0', sizeof (array));
4175 size = backtrace(array, 40);
4176
4177 fprintf(stderr, "grubby received SIGSEGV! Backtrace (%ld):\n",
4178 (unsigned long)size);
4179 backtrace_symbols_fd(array, size, STDERR_FILENO);
4180 exit(1);
4181 }
4182
4183 int main(int argc, const char ** argv) {
4184 poptContext optCon;
4185 const char * grubConfig = NULL;
4186 char * outputFile = NULL;
4187 int arg = 0;
4188 int flags = 0;
4189 int badImageOkay = 0;
4190 int configureGrub2 = 0;
4191 int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4192 int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4193 int configureExtLinux = 0;
4194 int bootloaderProbe = 0;
4195 int extraInitrdCount = 0;
4196 char * updateKernelPath = NULL;
4197 char * newKernelPath = NULL;
4198 char * removeKernelPath = NULL;
4199 char * newKernelArgs = NULL;
4200 char * newKernelInitrd = NULL;
4201 char * newKernelTitle = NULL;
4202 char * newDevTreePath = NULL;
4203 char * newMBKernel = NULL;
4204 char * newMBKernelArgs = NULL;
4205 char * removeMBKernelArgs = NULL;
4206 char * removeMBKernel = NULL;
4207 char * bootPrefix = NULL;
4208 char * defaultKernel = NULL;
4209 char * removeArgs = NULL;
4210 char * kernelInfo = NULL;
4211 char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4212 char * envPath = NULL;
4213 const char * chptr = NULL;
4214 struct configFileInfo * cfi = NULL;
4215 struct grubConfig * config;
4216 struct singleEntry * template = NULL;
4217 int copyDefault = 0, makeDefault = 0;
4218 int displayDefault = 0;
4219 int displayDefaultIndex = 0;
4220 int displayDefaultTitle = 0;
4221 int defaultIndex = -1;
4222 struct poptOption options[] = {
4223 { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4224 _("add an entry for the specified kernel"), _("kernel-path") },
4225 { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
4226 _("add an entry for the specified multiboot kernel"), NULL },
4227 { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
4228 _("default arguments for the new kernel or new arguments for "
4229 "kernel being updated"), _("args") },
4230 { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
4231 _("default arguments for the new multiboot kernel or "
4232 "new arguments for multiboot kernel being updated"), NULL },
4233 { "bad-image-okay", 0, 0, &badImageOkay, 0,
4234 _("don't sanity check images in boot entries (for testing only)"),
4235 NULL },
4236 { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4237 _("filestystem which contains /boot directory (for testing only)"),
4238 _("bootfs") },
4239 #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4240 { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4241 _("check which bootloader is installed on boot sector") },
4242 #endif
4243 { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4244 _("path to grub config file to update (\"-\" for stdin)"),
4245 _("path") },
4246 { "copy-default", 0, 0, &copyDefault, 0,
4247 _("use the default boot entry as a template for the new entry "
4248 "being added; if the default is not a linux image, or if "
4249 "the kernel referenced by the default image does not exist, "
4250 "the first linux entry whose kernel does exist is used as the "
4251 "template"), NULL },
4252 { "debug", 0, 0, &debug, 0,
4253 _("print debugging information for failures") },
4254 { "default-kernel", 0, 0, &displayDefault, 0,
4255 _("display the path of the default kernel") },
4256 { "default-index", 0, 0, &displayDefaultIndex, 0,
4257 _("display the index of the default kernel") },
4258 { "default-title", 0, 0, &displayDefaultTitle, 0,
4259 _("display the title of the default kernel") },
4260 { "devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4261 _("device tree file for new stanza"), _("dtb-path") },
4262 { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
4263 _("configure elilo bootloader") },
4264 { "efi", 0, POPT_ARG_NONE, &isEfi, 0,
4265 _("force grub2 stanzas to use efi") },
4266 { "env", 0, POPT_ARG_STRING, &envPath, 0,
4267 _("path for environment data"),
4268 _("path") },
4269 { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
4270 _("configure extlinux bootloader (from syslinux)") },
4271 { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
4272 _("configure grub bootloader") },
4273 { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
4274 _("configure grub2 bootloader") },
4275 { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
4276 _("display boot information for specified kernel"),
4277 _("kernel-path") },
4278 { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
4279 _("initrd image for the new kernel"), _("initrd-path") },
4280 { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
4281 _("auxiliary initrd image for things other than the new kernel"), _("initrd-path") },
4282 { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
4283 _("configure lilo bootloader") },
4284 { "make-default", 0, 0, &makeDefault, 0,
4285 _("make the newly added entry the default boot entry"), NULL },
4286 { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
4287 _("path to output updated config file (\"-\" for stdout)"),
4288 _("path") },
4289 { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
4290 _("remove kernel arguments"), NULL },
4291 { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
4292 _("remove multiboot kernel arguments"), NULL },
4293 { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
4294 _("remove all entries for the specified kernel"),
4295 _("kernel-path") },
4296 { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
4297 _("remove all entries for the specified multiboot kernel"), NULL },
4298 { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
4299 _("make the first entry referencing the specified kernel "
4300 "the default"), _("kernel-path") },
4301 { "set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
4302 _("make the given entry index the default entry"),
4303 _("entry-index") },
4304 { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
4305 _("configure silo bootloader") },
4306 { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
4307 _("title to use for the new kernel entry"), _("entry-title") },
4308 { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
4309 _("updated information for the specified kernel"),
4310 _("kernel-path") },
4311 { "version", 'v', 0, NULL, 'v',
4312 _("print the version of this program and exit"), NULL },
4313 { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
4314 _("configure yaboot bootloader") },
4315 { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
4316 _("configure zipl bootloader") },
4317 POPT_AUTOHELP
4318 { 0, 0, 0, 0, 0 }
4319 };
4320
4321 useextlinuxmenu=0;
4322
4323 signal(SIGSEGV, traceback);
4324
4325 int i = 0;
4326 for (int j = 1; j < argc; j++)
4327 i += strlen(argv[j]) + 1;
4328 saved_command_line = malloc(i);
4329 if (!saved_command_line) {
4330 fprintf(stderr, "grubby: %m\n");
4331 exit(1);
4332 }
4333 saved_command_line[0] = '\0';
4334 for (int j = 1; j < argc; j++) {
4335 strcat(saved_command_line, argv[j]);
4336 strncat(saved_command_line, j == argc -1 ? "" : " ", 1);
4337 }
4338
4339 optCon = poptGetContext("grubby", argc, argv, options, 0);
4340 poptReadDefaultConfig(optCon, 1);
4341
4342 while ((arg = poptGetNextOpt(optCon)) >= 0) {
4343 switch (arg) {
4344 case 'v':
4345 printf("grubby version %s\n", VERSION);
4346 exit(0);
4347 break;
4348 case 'i':
4349 if (extraInitrdCount < MAX_EXTRA_INITRDS) {
4350 extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
4351 } else {
4352 fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
4353 return 1;
4354 }
4355 break;
4356 }
4357 }
4358
4359 if (arg < -1) {
4360 fprintf(stderr, _("grubby: bad argument %s: %s\n"),
4361 poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
4362 poptStrerror(arg));
4363 return 1;
4364 }
4365
4366 if ((chptr = poptGetArg(optCon))) {
4367 fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
4368 return 1;
4369 }
4370
4371 if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
4372 configureYaboot + configureSilo + configureZipl +
4373 configureExtLinux ) > 1) {
4374 fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
4375 return 1;
4376 } else if (bootloaderProbe && grubConfig) {
4377 fprintf(stderr,
4378 _("grubby: cannot specify config file with --bootloader-probe\n"));
4379 return 1;
4380 } else if (configureGrub2) {
4381 cfi = &grub2ConfigType;
4382 if (envPath)
4383 cfi->envFile = envPath;
4384 } else if (configureLilo) {
4385 cfi = &liloConfigType;
4386 } else if (configureGrub) {
4387 cfi = &grubConfigType;
4388 } else if (configureELilo) {
4389 cfi = &eliloConfigType;
4390 } else if (configureYaboot) {
4391 cfi = &yabootConfigType;
4392 } else if (configureSilo) {
4393 cfi = &siloConfigType;
4394 } else if (configureZipl) {
4395 cfi = &ziplConfigType;
4396 } else if (configureExtLinux) {
4397 cfi = &extlinuxConfigType;
4398 useextlinuxmenu=1;
4399 }
4400
4401 if (!cfi) {
4402 if (grub2FindConfig(&grub2ConfigType))
4403 cfi = &grub2ConfigType;
4404 else
4405 #ifdef __ia64__
4406 cfi = &eliloConfigType;
4407 #elif __powerpc__
4408 cfi = &yabootConfigType;
4409 #elif __sparc__
4410 cfi = &siloConfigType;
4411 #elif __s390__
4412 cfi = &ziplConfigType;
4413 #elif __s390x__
4414 cfi = &ziplConfigtype;
4415 #else
4416 cfi = &grubConfigType;
4417 #endif
4418 }
4419
4420 if (!grubConfig) {
4421 if (cfi->findConfig)
4422 grubConfig = cfi->findConfig(cfi);
4423 if (!grubConfig)
4424 grubConfig = cfi->defaultConfig;
4425 }
4426
4427 if (bootloaderProbe && (displayDefault || kernelInfo ||
4428 newKernelPath || removeKernelPath || makeDefault ||
4429 defaultKernel || displayDefaultIndex || displayDefaultTitle ||
4430 (defaultIndex >= 0))) {
4431 fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
4432 "specified option"));
4433 return 1;
4434 }
4435
4436 if ((displayDefault || kernelInfo) && (newKernelPath ||
4437 removeKernelPath)) {
4438 fprintf(stderr, _("grubby: --default-kernel and --info may not "
4439 "be used when adding or removing kernels\n"));
4440 return 1;
4441 }
4442
4443 if (newKernelPath && !newKernelTitle) {
4444 fprintf(stderr, _("grubby: kernel title must be specified\n"));
4445 return 1;
4446 } else if (!newKernelPath && (copyDefault ||
4447 (newKernelInitrd && !updateKernelPath)||
4448 makeDefault || extraInitrdCount > 0)) {
4449 fprintf(stderr, _("grubby: kernel path expected\n"));
4450 return 1;
4451 }
4452
4453 if (newKernelPath && updateKernelPath) {
4454 fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
4455 "not be used together"));
4456 return 1;
4457 }
4458
4459 if (makeDefault && defaultKernel) {
4460 fprintf(stderr, _("grubby: --make-default and --default-kernel "
4461 "may not be used together\n"));
4462 return 1;
4463 } else if (defaultKernel && removeKernelPath &&
4464 !strcmp(defaultKernel, removeKernelPath)) {
4465 fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));
4466 return 1;
4467 } else if (defaultKernel && newKernelPath &&
4468 !strcmp(defaultKernel, newKernelPath)) {
4469 makeDefault = 1;
4470 defaultKernel = NULL;
4471 }
4472 else if (defaultKernel && (defaultIndex >= 0)) {
4473 fprintf(stderr, _("grubby: --set-default and --set-default-index "
4474 "may not be used together\n"));
4475 return 1;
4476 }
4477
4478 if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
4479 fprintf(stderr, _("grubby: output file must be specified if stdin "
4480 "is used\n"));
4481 return 1;
4482 }
4483
4484 if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
4485 && !kernelInfo && !bootloaderProbe && !updateKernelPath
4486 && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle
4487 && (defaultIndex == -1)) {
4488 fprintf(stderr, _("grubby: no action specified\n"));
4489 return 1;
4490 }
4491
4492 flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
4493
4494 if (cfi->needsBootPrefix) {
4495 if (!bootPrefix) {
4496 bootPrefix = findBootPrefix();
4497 if (!bootPrefix) return 1;
4498 } else {
4499 /* this shouldn't end with a / */
4500 if (bootPrefix[strlen(bootPrefix) - 1] == '/')
4501 bootPrefix[strlen(bootPrefix) - 1] = '\0';
4502 }
4503 } else {
4504 bootPrefix = "";
4505 }
4506
4507 if (!cfi->mbAllowExtraInitRds &&
4508 extraInitrdCount > 0) {
4509 fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
4510 return 1;
4511 }
4512
4513 if (bootloaderProbe) {
4514 int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
4515 struct grubConfig * lconfig, * gconfig, * yconfig, * econfig;
4516
4517 const char *grub2config = grub2FindConfig(&grub2ConfigType);
4518 if (grub2config) {
4519 gconfig = readConfig(grub2config, &grub2ConfigType);
4520 if (!gconfig)
4521 gr2c = 1;
4522 else
4523 gr2c = checkForGrub2(gconfig);
4524 }
4525
4526 const char *grubconfig = grubFindConfig(&grubConfigType);
4527 if (!access(grubconfig, F_OK)) {
4528 gconfig = readConfig(grubconfig, &grubConfigType);
4529 if (!gconfig)
4530 grc = 1;
4531 else
4532 grc = checkForGrub(gconfig);
4533 }
4534
4535 if (!access(liloConfigType.defaultConfig, F_OK)) {
4536 lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);
4537 if (!lconfig)
4538 lrc = 1;
4539 else
4540 lrc = checkForLilo(lconfig);
4541 }
4542
4543 if (!access(eliloConfigType.defaultConfig, F_OK)) {
4544 econfig = readConfig(eliloConfigType.defaultConfig,
4545 &eliloConfigType);
4546 if (!econfig)
4547 erc = 1;
4548 else
4549 erc = checkForElilo(econfig);
4550 }
4551
4552 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
4553 lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
4554 if (!lconfig)
4555 extrc = 1;
4556 else
4557 extrc = checkForExtLinux(lconfig);
4558 }
4559
4560
4561 if (!access(yabootConfigType.defaultConfig, F_OK)) {
4562 yconfig = readConfig(yabootConfigType.defaultConfig,
4563 &yabootConfigType);
4564 if (!yconfig)
4565 yrc = 1;
4566 else
4567 yrc = checkForYaboot(yconfig);
4568 }
4569
4570 if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1 ||
4571 erc == 1)
4572 return 1;
4573
4574 if (lrc == 2) printf("lilo\n");
4575 if (gr2c == 2) printf("grub2\n");
4576 if (grc == 2) printf("grub\n");
4577 if (extrc == 2) printf("extlinux\n");
4578 if (yrc == 2) printf("yaboot\n");
4579 if (erc == 2) printf("elilo\n");
4580
4581 return 0;
4582 }
4583
4584 if (grubConfig == NULL) {
4585 printf("Could not find bootloader configuration file.\n");
4586 exit(1);
4587 }
4588
4589 config = readConfig(grubConfig, cfi);
4590 if (!config) return 1;
4591
4592 if (displayDefault) {
4593 struct singleLine * line;
4594 struct singleEntry * entry;
4595 char * rootspec;
4596
4597 if (config->defaultImage == -1) return 0;
4598 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4599 cfi->defaultIsSaved)
4600 config->defaultImage = 0;
4601 entry = findEntryByIndex(config, config->defaultImage);
4602 if (!entry) return 0;
4603 if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
4604
4605 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
4606 if (!line) return 0;
4607
4608 rootspec = getRootSpecifier(line->elements[1].item);
4609 printf("%s%s\n", bootPrefix, line->elements[1].item +
4610 ((rootspec != NULL) ? strlen(rootspec) : 0));
4611
4612 return 0;
4613
4614 } else if (displayDefaultTitle) {
4615 struct singleLine * line;
4616 struct singleEntry * entry;
4617
4618 if (config->defaultImage == -1) return 0;
4619 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4620 cfi->defaultIsSaved)
4621 config->defaultImage = 0;
4622 entry = findEntryByIndex(config, config->defaultImage);
4623 if (!entry) return 0;
4624
4625 if (!configureGrub2) {
4626 line = getLineByType(LT_TITLE, entry->lines);
4627 if (!line) return 0;
4628 printf("%s\n", line->elements[1].item);
4629
4630 } else {
4631 char * title;
4632
4633 dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
4634 line = getLineByType(LT_MENUENTRY, entry->lines);
4635 if (!line) return 0;
4636 title = grub2ExtractTitle(line);
4637 if (title)
4638 printf("%s\n", title);
4639 }
4640 return 0;
4641
4642 } else if (displayDefaultIndex) {
4643 if (config->defaultImage == -1) return 0;
4644 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4645 cfi->defaultIsSaved)
4646 config->defaultImage = 0;
4647 printf("%i\n", config->defaultImage);
4648 return 0;
4649
4650 } else if (kernelInfo)
4651 return displayInfo(config, kernelInfo, bootPrefix);
4652
4653 if (copyDefault) {
4654 template = findTemplate(config, bootPrefix, NULL, 0, flags);
4655 if (!template) return 1;
4656 }
4657
4658 markRemovedImage(config, removeKernelPath, bootPrefix);
4659 markRemovedImage(config, removeMBKernel, bootPrefix);
4660 setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
4661 bootPrefix, flags, defaultIndex);
4662 setFallbackImage(config, newKernelPath != NULL);
4663 if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
4664 removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
4665 if (updateKernelPath && newKernelInitrd) {
4666 if (newMBKernel) {
4667 if (addMBInitrd(config, newMBKernel, updateKernelPath,
4668 bootPrefix, newKernelInitrd,
4669 newKernelTitle))
4670 return 1;
4671 } else {
4672 if (updateInitrd(config, updateKernelPath, bootPrefix,
4673 newKernelInitrd, newKernelTitle))
4674 return 1;
4675 }
4676 }
4677 if (addNewKernel(config, template, bootPrefix, newKernelPath,
4678 newKernelTitle, newKernelArgs, newKernelInitrd,
4679 (const char **)extraInitrds, extraInitrdCount,
4680 newMBKernel, newMBKernelArgs, newDevTreePath)) return 1;
4681
4682
4683 if (numEntries(config) == 0) {
4684 fprintf(stderr, _("grubby: doing this would leave no kernel entries. "
4685 "Not writing out new config.\n"));
4686 return 1;
4687 }
4688
4689 if (!outputFile)
4690 outputFile = (char *)grubConfig;
4691
4692 return writeConfig(config, outputFile, bootPrefix);
4693 }