Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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