Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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