Magellan Linux

Diff of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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

Legend:
Removed from v.532  
changed lines
  Added in v.3135