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 2983 by niro, Thu Jun 30 10:27:31 2016 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_WHITESPACE   = 1 << 0,
79         LT_BOOTROOT, LT_LBA, LT_MBMODULE, LT_OTHER, LT_GENERIC };      LT_TITLE        = 1 << 1,
80        LT_KERNEL       = 1 << 2,
81        LT_INITRD       = 1 << 3,
82        LT_HYPER        = 1 << 4,
83        LT_DEFAULT      = 1 << 5,
84        LT_MBMODULE     = 1 << 6,
85        LT_ROOT         = 1 << 7,
86        LT_FALLBACK     = 1 << 8,
87        LT_KERNELARGS   = 1 << 9,
88        LT_BOOT         = 1 << 10,
89        LT_BOOTROOT     = 1 << 11,
90        LT_LBA          = 1 << 12,
91        LT_OTHER        = 1 << 13,
92        LT_GENERIC      = 1 << 14,
93        LT_ECHO    = 1 << 16,
94        LT_MENUENTRY    = 1 << 17,
95        LT_ENTRY_END    = 1 << 18,
96        LT_SET_VARIABLE = 1 << 19,
97        LT_KERNEL_EFI   = 1 << 20,
98        LT_INITRD_EFI   = 1 << 21,
99        LT_KERNEL_16    = 1 << 22,
100        LT_INITRD_16    = 1 << 23,
101        LT_DEVTREE      = 1 << 24,
102        LT_UNKNOWN      = 1 << 25,
103    };
104    
105  struct singleLine {  struct singleLine {
106      char * indent;      char * indent;
# Line 61  struct singleEntry { Line 121  struct singleEntry {
121    
122  #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 */
123    
124  #define KERNEL_KERNEL    (1 << 0)  /* These defines are (only) used in addNewKernel() */
125  #define KERNEL_INITRD    (1 << 2)  #define NEED_KERNEL  (1 << 0)
126  #define KERNEL_TITLE    (1 << 3)  #define NEED_INITRD  (1 << 1)
127  #define KERNEL_ARGS    (1 << 4)  #define NEED_TITLE   (1 << 2)
128  #define KERNEL_MB           (1 << 5)  #define NEED_ARGS    (1 << 3)
129    #define NEED_MB      (1 << 4)
130    #define NEED_END     (1 << 5)
131    #define NEED_DEVTREE (1 << 6)
132    
133  #define MAIN_DEFAULT    (1 << 0)  #define MAIN_DEFAULT    (1 << 0)
134  #define DEFAULT_SAVED       -2  #define DEFAULT_SAVED       -2
135    #define DEFAULT_SAVED_GRUB2 -3
136    
137  struct keywordTypes {  struct keywordTypes {
138      char * key;      char * key;
139      enum lineType_e type;      enum lineType_e type;
140      char nextChar;      char nextChar;
141  } ;      char separatorChar;
142    };
143    
144    struct configFileInfo;
145    
146    typedef const char *(*findConfigFunc)(struct configFileInfo *);
147    typedef const int (*writeLineFunc)(struct configFileInfo *,
148     struct singleLine *line);
149    typedef char *(*getEnvFunc)(struct configFileInfo *, char *name);
150    typedef int (*setEnvFunc)(struct configFileInfo *, char *name, char *value);
151    
152  struct configFileInfo {  struct configFileInfo {
153      char * defaultConfig;      char * defaultConfig;
154        findConfigFunc findConfig;
155        writeLineFunc writeLine;
156        getEnvFunc getEnv;
157        setEnvFunc setEnv;
158      struct keywordTypes * keywords;      struct keywordTypes * keywords;
159        int caseInsensitive;
160      int defaultIsIndex;      int defaultIsIndex;
161        int defaultIsVariable;
162      int defaultSupportSaved;      int defaultSupportSaved;
163      enum lineType_e entrySeparator;      int defaultIsSaved;
164        int defaultIsUnquoted;
165        enum lineType_e entryStart;
166        enum lineType_e entryEnd;
167      int needsBootPrefix;      int needsBootPrefix;
168      int argsInQuotes;      int argsInQuotes;
169      int maxTitleLength;      int maxTitleLength;
170      int titleBracketed;      int titleBracketed;
171        int titlePosition;
172        int mbHyperFirst;
173        int mbInitRdIsModule;
174        int mbConcatArgs;
175        int mbAllowExtraInitRds;
176        char *envFile;
177  };  };
178    
179  struct keywordTypes grubKeywords[] = {  struct keywordTypes grubKeywords[] = {
# Line 94  struct keywordTypes grubKeywords[] = { Line 182  struct keywordTypes grubKeywords[] = {
182      { "default",    LT_DEFAULT,    ' ' },      { "default",    LT_DEFAULT,    ' ' },
183      { "fallback",   LT_FALLBACK,    ' ' },      { "fallback",   LT_FALLBACK,    ' ' },
184      { "kernel",    LT_KERNEL,    ' ' },      { "kernel",    LT_KERNEL,    ' ' },
185      { "initrd",    LT_INITRD,    ' ' },      { "initrd",    LT_INITRD,    ' ', ' ' },
186      { "module",     LT_MBMODULE,    ' ' },      { "module",     LT_MBMODULE,    ' ' },
187        { "kernel",     LT_HYPER,       ' ' },
188      { NULL,    0, 0 },      { NULL,    0, 0 },
189  };  };
190    
191    const char *grubFindConfig(struct configFileInfo *cfi) {
192        static const char *configFiles[] = {
193     "/boot/grub/grub.conf",
194     "/boot/grub/menu.lst",
195     "/etc/grub.conf",
196     "/boot/grub2/grub.cfg",
197     "/boot/grub2-efi/grub.cfg",
198     NULL
199        };
200        static int i = -1;
201    
202        if (i == -1) {
203     for (i = 0; configFiles[i] != NULL; i++) {
204        dbgPrintf("Checking \"%s\": ", configFiles[i]);
205        if (!access(configFiles[i], R_OK)) {
206     dbgPrintf("found\n");
207     return configFiles[i];
208        }
209        dbgPrintf("not found\n");
210     }
211        }
212        return configFiles[i];
213    }
214    
215  struct configFileInfo grubConfigType = {  struct configFileInfo grubConfigType = {
216      "/boot/grub/grub.conf",    /* defaultConfig */      .findConfig = grubFindConfig,
217      grubKeywords,    /* keywords */      .keywords = grubKeywords,
218      1,    /* defaultIsIndex */      .defaultIsIndex = 1,
219      1,    /* defaultSupportSaved */      .defaultSupportSaved = 1,
220      LT_TITLE,    /* entrySeparator */      .entryStart = LT_TITLE,
221      1,    /* needsBootPrefix */      .needsBootPrefix = 1,
222      0,    /* argsInQuotes */      .mbHyperFirst = 1,
223      0,    /* maxTitleLength */      .mbInitRdIsModule = 1,
224      0,                                      /* titleBracketed */      .mbAllowExtraInitRds = 1,
225        .titlePosition = 1,
226    };
227    
228    struct keywordTypes grub2Keywords[] = {
229        { "menuentry",  LT_MENUENTRY,   ' ' },
230        { "}",          LT_ENTRY_END,   ' ' },
231        { "echo",       LT_ECHO,        ' ' },
232        { "set",        LT_SET_VARIABLE,' ', '=' },
233        { "root",       LT_BOOTROOT,    ' ' },
234        { "default",    LT_DEFAULT,     ' ' },
235        { "fallback",   LT_FALLBACK,    ' ' },
236        { "linux",      LT_KERNEL,      ' ' },
237        { "linuxefi",   LT_KERNEL_EFI,  ' ' },
238        { "linux16",    LT_KERNEL_16,   ' ' },
239        { "initrd",     LT_INITRD,      ' ', ' ' },
240        { "initrdefi",  LT_INITRD_EFI,  ' ', ' ' },
241        { "initrd16",   LT_INITRD_16,   ' ', ' ' },
242        { "module",     LT_MBMODULE,    ' ' },
243        { "kernel",     LT_HYPER,       ' ' },
244        { "devicetree", LT_DEVTREE,  ' ' },
245        { NULL, 0, 0 },
246    };
247    
248    const char *grub2FindConfig(struct configFileInfo *cfi) {
249        static const char *configFiles[] = {
250     "/boot/grub/grub-efi.cfg",
251     "/boot/grub/grub.cfg",
252     NULL
253        };
254        static int i = -1;
255        static const char *grub_cfg = "/boot/grub/grub.cfg";
256        int rc = -1;
257    
258        if (i == -1) {
259     for (i = 0; configFiles[i] != NULL; i++) {
260        dbgPrintf("Checking \"%s\": ", configFiles[i]);
261        if ((rc = access(configFiles[i], R_OK))) {
262     if (errno == EACCES) {
263        printf("Unable to access bootloader configuration file "
264           "\"%s\": %m\n", configFiles[i]);
265        exit(1);
266     }
267     continue;
268        } else {
269     dbgPrintf("found\n");
270     return configFiles[i];
271        }
272     }
273        }
274    
275        /* Ubuntu renames grub2 to grub, so check for the grub.d directory
276         * that isn't in grub1, and if it exists, return the config file path
277         * that they use. */
278        if (configFiles[i] == NULL && !access("/etc/grub.d/", R_OK)) {
279     dbgPrintf("found\n");
280     return grub_cfg;
281        }
282    
283        dbgPrintf("not found\n");
284        return configFiles[i];
285    }
286    
287    /* kind of hacky.  It'll give the first 1024 bytes, ish. */
288    static char *grub2GetEnv(struct configFileInfo *info, char *name)
289    {
290        static char buf[1025];
291        char *s = NULL;
292        char *ret = NULL;
293        char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";
294        int rc = asprintf(&s, "grub-editenv %s list | grep '^%s='", envFile, name);
295    
296        if (rc < 0)
297     return NULL;
298    
299        FILE *f = popen(s, "r");
300        if (!f)
301     goto out;
302    
303        memset(buf, '\0', sizeof (buf));
304        ret = fgets(buf, 1024, f);
305        pclose(f);
306    
307        if (ret) {
308     ret += strlen(name) + 1;
309     ret[strlen(ret) - 1] = '\0';
310        }
311        dbgPrintf("grub2GetEnv(%s): %s\n", name, ret);
312    out:
313        free(s);
314        return ret;
315    }
316    
317    static int sPopCount(const char *s, const char *c)
318    {
319        int ret = 0;
320        if (!s)
321     return -1;
322        for (int i = 0; s[i] != '\0'; i++)
323     for (int j = 0; c[j] != '\0'; j++)
324        if (s[i] == c[j])
325     ret++;
326        return ret;
327    }
328    
329    static char *shellEscape(const char *s)
330    {
331        int l = strlen(s) + sPopCount(s, "'") * 2;
332    
333        char *ret = calloc(l+1, sizeof (*ret));
334        if (!ret)
335     return NULL;
336        for (int i = 0, j = 0; s[i] != '\0'; i++, j++) {
337     if (s[i] == '\'')
338        ret[j++] = '\\';
339     ret[j] = s[i];
340        }
341        return ret;
342    }
343    
344    static void unquote(char *s)
345    {
346        int l = strlen(s);
347    
348        if ((s[l-1] == '\'' && s[0] == '\'') || (s[l-1] == '"' && s[0] == '"')) {
349     memmove(s, s+1, l-2);
350     s[l-2] = '\0';
351        }
352    }
353    
354    static int grub2SetEnv(struct configFileInfo *info, char *name, char *value)
355    {
356        char *s = NULL;
357        int rc = 0;
358        char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";
359    
360        unquote(value);
361        value = shellEscape(value);
362        if (!value)
363        return -1;
364    
365        rc = asprintf(&s, "grub-editenv %s set '%s=%s'", envFile, name, value);
366        free(value);
367        if (rc <0)
368     return -1;
369    
370        dbgPrintf("grub2SetEnv(%s): %s\n", name, s);
371        rc = system(s);
372        free(s);
373        return rc;
374    }
375    
376    /* this is a gigantic hack to avoid clobbering grub2 variables... */
377    static int is_special_grub2_variable(const char *name)
378    {
379        if (!strcmp(name,"\"${next_entry}\""))
380     return 1;
381        if (!strcmp(name,"\"${prev_saved_entry}\""))
382     return 1;
383        return 0;
384    }
385    
386    int sizeOfSingleLine(struct singleLine * line) {
387      int count = 0;
388    
389      for (int i = 0; i < line->numElements; i++) {
390        int indentSize = 0;
391    
392        count = count + strlen(line->elements[i].item);
393    
394        indentSize = strlen(line->elements[i].indent);
395        if (indentSize > 0)
396          count = count + indentSize;
397        else
398          /* be extra safe and add room for whitespaces */
399          count = count + 1;
400      }
401    
402      /* room for trailing terminator */
403      count = count + 1;
404    
405      return count;
406    }
407    
408    static int isquote(char q)
409    {
410        if (q == '\'' || q == '\"')
411     return 1;
412        return 0;
413    }
414    
415    static int iskernel(enum lineType_e type) {
416        return (type == LT_KERNEL || type == LT_KERNEL_EFI || type == LT_KERNEL_16);
417    }
418    
419    static int isinitrd(enum lineType_e type) {
420        return (type == LT_INITRD || type == LT_INITRD_EFI || type == LT_INITRD_16);
421    }
422    
423    char *grub2ExtractTitle(struct singleLine * line) {
424        char * current;
425        char * current_indent;
426        int current_len;
427        int current_indent_len;
428        int i;
429    
430        /* bail out if line does not start with menuentry */
431        if (strcmp(line->elements[0].item, "menuentry"))
432          return NULL;
433    
434        i = 1;
435        current = line->elements[i].item;
436        current_len = strlen(current);
437    
438        /* if second word is quoted, strip the quotes and return single word */
439        if (isquote(*current) && isquote(current[current_len - 1])) {
440     char *tmp;
441    
442     tmp = strdup(current);
443     *(tmp + current_len - 1) = '\0';
444     return ++tmp;
445        }
446    
447        /* if no quotes, return second word verbatim */
448        if (!isquote(*current))
449     return current;
450    
451        /* second element start with a quote, so we have to find the element
452         * whose last character is also quote (assuming it's the closing one) */
453        int resultMaxSize;
454        char * result;
455        
456        resultMaxSize = sizeOfSingleLine(line);
457        result = malloc(resultMaxSize);
458        snprintf(result, resultMaxSize, "%s", ++current);
459        
460        i++;
461        for (; i < line->numElements; ++i) {
462     current = line->elements[i].item;
463     current_len = strlen(current);
464     current_indent = line->elements[i].indent;
465     current_indent_len = strlen(current_indent);
466    
467     strncat(result, current_indent, current_indent_len);
468     if (!isquote(current[current_len-1])) {
469        strncat(result, current, current_len);
470     } else {
471        strncat(result, current, current_len - 1);
472        break;
473     }
474        }
475        return result;
476    }
477    
478    struct configFileInfo grub2ConfigType = {
479        .findConfig = grub2FindConfig,
480        .getEnv = grub2GetEnv,
481        .setEnv = grub2SetEnv,
482        .keywords = grub2Keywords,
483        .defaultIsIndex = 1,
484        .defaultSupportSaved = 1,
485        .defaultIsVariable = 1,
486        .entryStart = LT_MENUENTRY,
487        .entryEnd = LT_ENTRY_END,
488        .titlePosition = 1,
489        .needsBootPrefix = 1,
490        .mbHyperFirst = 1,
491        .mbInitRdIsModule = 1,
492        .mbAllowExtraInitRds = 1,
493  };  };
494    
495  struct keywordTypes yabootKeywords[] = {  struct keywordTypes yabootKeywords[] = {
# Line 142  struct keywordTypes yabootKeywords[] = { Line 523  struct keywordTypes yabootKeywords[] = {
523      { "partition",  LT_GENERIC,    '=' },      { "partition",  LT_GENERIC,    '=' },
524      { "device",    LT_GENERIC,    '=' },      { "device",    LT_GENERIC,    '=' },
525      { "fstype",    LT_GENERIC,    '=' },      { "fstype",    LT_GENERIC,    '=' },
526      { "initrd",    LT_INITRD,    '=' },      { "initrd",    LT_INITRD,    '=', ';' },
527      { "append",    LT_KERNELARGS,  '=' },      { "append",    LT_KERNELARGS,  '=' },
528      { "boot",    LT_BOOT,    '=' },      { "boot",    LT_BOOT,    '=' },
529      { "lba",    LT_LBA,    ' ' },      { "lba",    LT_LBA,    ' ' },
# Line 162  struct keywordTypes liloKeywords[] = { Line 543  struct keywordTypes liloKeywords[] = {
543      { NULL,    0, 0 },      { NULL,    0, 0 },
544  };  };
545    
546    struct keywordTypes eliloKeywords[] = {
547        { "label",    LT_TITLE,    '=' },
548        { "root",    LT_ROOT,    '=' },
549        { "default",    LT_DEFAULT,    '=' },
550        { "image",    LT_KERNEL,    '=' },
551        { "initrd",    LT_INITRD,    '=' },
552        { "append",    LT_KERNELARGS,  '=' },
553        { "vmm",    LT_HYPER,       '=' },
554        { NULL,    0, 0 },
555    };
556    
557  struct keywordTypes siloKeywords[] = {  struct keywordTypes siloKeywords[] = {
558      { "label",    LT_TITLE,    '=' },      { "label",    LT_TITLE,    '=' },
559      { "root",    LT_ROOT,    '=' },      { "root",    LT_ROOT,    '=' },
# Line 183  struct keywordTypes ziplKeywords[] = { Line 575  struct keywordTypes ziplKeywords[] = {
575      { NULL,         0, 0 },      { NULL,         0, 0 },
576  };  };
577    
578    struct keywordTypes extlinuxKeywords[] = {
579        { "label",    LT_TITLE,    ' ' },
580        { "root",    LT_ROOT,    ' ' },
581        { "default",    LT_DEFAULT,    ' ' },
582        { "kernel",    LT_KERNEL,    ' ' },
583        { "initrd",    LT_INITRD,      ' ', ',' },
584        { "append",    LT_KERNELARGS,  ' ' },
585        { "prompt",     LT_UNKNOWN,     ' ' },
586        { "fdt",        LT_DEVTREE,     ' ' },
587        { "fdtdir",     LT_DEVTREE,     ' ' },
588        { NULL,    0, 0 },
589    };
590    int useextlinuxmenu;
591  struct configFileInfo eliloConfigType = {  struct configFileInfo eliloConfigType = {
592      "/boot/efi/EFI/redhat/elilo.conf",    /* defaultConfig */      .defaultConfig = "/boot/efi/EFI/redhat/elilo.conf",
593      liloKeywords,    /* keywords */      .keywords = eliloKeywords,
594      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
595      0,    /* defaultSupportSaved */      .needsBootPrefix = 1,
596      LT_KERNEL,    /* entrySeparator */      .argsInQuotes = 1,
597      1,                    /* needsBootPrefix */      .mbConcatArgs = 1,
598      1,    /* argsInQuotes */      .titlePosition = 1,
     0,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
599  };  };
600    
601  struct configFileInfo liloConfigType = {  struct configFileInfo liloConfigType = {
602      "/etc/lilo.conf",    /* defaultConfig */      .defaultConfig = "/etc/lilo.conf",
603      liloKeywords,    /* keywords */      .keywords = liloKeywords,
604      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
605      0,    /* defaultSupportSaved */      .argsInQuotes = 1,
606      LT_KERNEL,    /* entrySeparator */      .maxTitleLength = 15,
607      0,    /* needsBootPrefix */      .titlePosition = 1,
     1,    /* argsInQuotes */  
     15,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
608  };  };
609    
610  struct configFileInfo yabootConfigType = {  struct configFileInfo yabootConfigType = {
611      "/etc/yaboot.conf",    /* defaultConfig */      .defaultConfig = "/etc/yaboot.conf",
612      yabootKeywords,    /* keywords */      .keywords = yabootKeywords,
613      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
614      0,    /* defaultSupportSaved */      .needsBootPrefix = 1,
615      LT_KERNEL,    /* entrySeparator */      .argsInQuotes = 1,
616      1,    /* needsBootPrefix */      .maxTitleLength = 15,
617      1,    /* argsInQuotes */      .mbAllowExtraInitRds = 1,
618      15,    /* maxTitleLength */      .titlePosition = 1,
     0,                                      /* titleBracketed */  
619  };  };
620    
621  struct configFileInfo siloConfigType = {  struct configFileInfo siloConfigType = {
622      "/etc/silo.conf",    /* defaultConfig */      .defaultConfig = "/etc/silo.conf",
623      siloKeywords,    /* keywords */      .keywords = siloKeywords,
624      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
625      0,    /* defaultSupportSaved */      .needsBootPrefix = 1,
626      LT_KERNEL,    /* entrySeparator */      .argsInQuotes = 1,
627      1,    /* needsBootPrefix */      .maxTitleLength = 15,
628      1,    /* argsInQuotes */      .titlePosition = 1,
     15,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
629  };  };
630    
631  struct configFileInfo ziplConfigType = {  struct configFileInfo ziplConfigType = {
632      "/etc/zipl.conf",    /* defaultConfig */      .defaultConfig = "/etc/zipl.conf",
633      ziplKeywords,    /* keywords */      .keywords = ziplKeywords,
634      0,    /* defaultIsIndex */      .entryStart = LT_TITLE,
635      0,    /* defaultSupportSaved */      .argsInQuotes = 1,
636      LT_TITLE,    /* entrySeparator */      .titleBracketed = 1,
637      0,    /* needsBootPrefix */  };
638      1,    /* argsInQuotes */  
639      15,    /* maxTitleLength */  struct configFileInfo extlinuxConfigType = {
640      1,                                      /* titleBracketed */      .defaultConfig = "/boot/extlinux/extlinux.conf",
641        .keywords = extlinuxKeywords,
642        .caseInsensitive = 1,
643        .entryStart = LT_TITLE,
644        .needsBootPrefix = 1,
645        .maxTitleLength = 255,
646        .mbAllowExtraInitRds = 1,
647        .defaultIsUnquoted = 1,
648        .titlePosition = 1,
649  };  };
650    
651  struct grubConfig {  struct grubConfig {
# Line 255  struct grubConfig { Line 660  struct grubConfig {
660      struct configFileInfo * cfi;      struct configFileInfo * cfi;
661  };  };
662    
663    blkid_cache blkid;
664    
665  struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index);  struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index);
666  struct singleEntry * findEntryByPath(struct grubConfig * cfg,  struct singleEntry * findEntryByPath(struct grubConfig * cfg,
667       const char * path, const char * prefix,       const char * path, const char * prefix,
668       int * index);       int * index);
669  static char * strndup(char * from, int len);  struct singleEntry * findEntryByTitle(struct grubConfig * cfg, char *title,
670          int * index);
671  static int readFile(int fd, char ** bufPtr);  static int readFile(int fd, char ** bufPtr);
672  static void lineInit(struct singleLine * line);  static void lineInit(struct singleLine * line);
673    struct singleLine * lineDup(struct singleLine * line);
674  static void lineFree(struct singleLine * line);  static void lineFree(struct singleLine * line);
675  static int lineWrite(FILE * out, struct singleLine * line,  static int lineWrite(FILE * out, struct singleLine * line,
676       struct configFileInfo * cfi);       struct configFileInfo * cfi);
677  static int getNextLine(char ** bufPtr, struct singleLine * line,  static int getNextLine(char ** bufPtr, struct singleLine * line,
678         struct configFileInfo * cfi);         struct configFileInfo * cfi);
679  static char * getRootSpecifier(char * str);  static char * getRootSpecifier(char * str);
680    static void requote(struct singleLine *line, struct configFileInfo * cfi);
681  static char * strndup(char * from, int len) {  static void insertElement(struct singleLine * line,
682      char * to;    const char * item, int insertHere,
683      struct configFileInfo * cfi);
684      to = malloc(len + 1);  static void removeElement(struct singleLine * line, int removeHere);
685      strncpy(to, from, len);  static struct keywordTypes * getKeywordByType(enum lineType_e type,
686      to[len] = '\0';        struct configFileInfo * cfi);
687    static enum lineType_e getTypeByKeyword(char * keyword,
688      return to;   struct configFileInfo * cfi);
689  }  static struct singleLine * getLineByType(enum lineType_e type,
690     struct singleLine * line);
691    static int checkForExtLinux(struct grubConfig * config);
692    struct singleLine * addLineTmpl(struct singleEntry * entry,
693                                    struct singleLine * tmplLine,
694                                    struct singleLine * prevLine,
695                                    const char * val,
696     struct configFileInfo * cfi);
697    struct singleLine *  addLine(struct singleEntry * entry,
698                                 struct configFileInfo * cfi,
699                                 enum lineType_e type, char * defaultIndent,
700                                 const char * val);
701    
702  static char * sdupprintf(const char *format, ...)  static char * sdupprintf(const char *format, ...)
703  #ifdef __GNUC__  #ifdef __GNUC__
# Line 313  static char * sdupprintf(const char *for Line 732  static char * sdupprintf(const char *for
732      return buf;      return buf;
733  }  }
734    
735    static enum lineType_e preferredLineType(enum lineType_e type,
736     struct configFileInfo *cfi) {
737        if (isEfi && cfi == &grub2ConfigType) {
738     switch (type) {
739     case LT_KERNEL:
740        return isEfiOnly ? LT_KERNEL : LT_KERNEL_EFI;
741     case LT_INITRD:
742        return isEfiOnly ? LT_INITRD : LT_INITRD_EFI;
743     default:
744        return type;
745     }
746    #if defined(__i386__) || defined(__x86_64__)
747        } else if (cfi == &grub2ConfigType) {
748     switch (type) {
749     case LT_KERNEL:
750        return LT_KERNEL_16;
751     case LT_INITRD:
752        return LT_INITRD_16;
753     default:
754        return type;
755     }
756    #endif
757        }
758        return type;
759    }
760    
761    static struct keywordTypes * getKeywordByType(enum lineType_e type,
762          struct configFileInfo * cfi) {
763        for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) {
764     if (kw->type == type)
765        return kw;
766        }
767        return NULL;
768    }
769    
770    static char *getKeyByType(enum lineType_e type, struct configFileInfo * cfi) {
771        struct keywordTypes *kt = getKeywordByType(type, cfi);
772        if (kt)
773     return kt->key;
774        return "unknown";
775    }
776    
777    static char * getpathbyspec(char *device) {
778        if (!blkid)
779            blkid_get_cache(&blkid, NULL);
780    
781        return blkid_get_devname(blkid, device, NULL);
782    }
783    
784    static char * getuuidbydev(char *device) {
785        if (!blkid)
786     blkid_get_cache(&blkid, NULL);
787    
788        return blkid_get_tag_value(blkid, "UUID", device);
789    }
790    
791    static enum lineType_e getTypeByKeyword(char * keyword,
792     struct configFileInfo * cfi) {
793        for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) {
794     if (cfi->caseInsensitive) {
795        if (!strcasecmp(keyword, kw->key))
796                    return kw->type;
797     } else {
798        if (!strcmp(keyword, kw->key))
799            return kw->type;
800     }
801        }
802        return LT_UNKNOWN;
803    }
804    
805    static struct singleLine * getLineByType(enum lineType_e type,
806     struct singleLine * line) {
807        dbgPrintf("getLineByType(%d): ", type);
808        for (; line; line = line->next) {
809     dbgPrintf("%d:%s ", line->type,
810      line->numElements ? line->elements[0].item : "(empty)");
811     if (line->type & type) break;
812        }
813        dbgPrintf(line ? "\n" : " (failed)\n");
814        return line;
815    }
816    
817  static int isBracketedTitle(struct singleLine * line) {  static int isBracketedTitle(struct singleLine * line) {
818      if ((*line->elements[0].item == '[') && (line->numElements == 1)) {      if (line->numElements == 1 && *line->elements[0].item == '[') {
819          int len = strlen(line->elements[0].item);          int len = strlen(line->elements[0].item);
820          if (*(line->elements[0].item + len - 1) == ']') {          if (*(line->elements[0].item + len - 1) == ']') {
821              /* FIXME: this is a hack... */              /* FIXME: this is a hack... */
# Line 326  static int isBracketedTitle(struct singl Line 827  static int isBracketedTitle(struct singl
827      return 0;      return 0;
828  }  }
829    
830  /* figure out if this is a entry separator */  static int isEntryStart(struct singleLine * line,
 static int isEntrySeparator(struct singleLine * line,  
831                              struct configFileInfo * cfi) {                              struct configFileInfo * cfi) {
832      if (line->type == LT_WHITESPACE)      return line->type == cfi->entryStart || line->type == LT_OTHER ||
833   return 0;   (cfi->titleBracketed && isBracketedTitle(line));
     if (line->type == cfi->entrySeparator)  
         return 1;  
     if (line->type == LT_OTHER)  
         return 1;  
     if (cfi->titleBracketed && isBracketedTitle(line)) {  
         return 1;  
     }  
     return 0;  
834  }  }
835    
836  /* extract the title from within brackets (for zipl) */  /* extract the title from within brackets (for zipl) */
837  static char * extractTitle(struct singleLine * line) {  static char * extractTitle(struct grubConfig *cfg, struct singleLine * line) {
838      /* bracketed title... let's extract it (leaks a byte) */      /* bracketed title... let's extract it */
839      char * title;      char * title = NULL;
840      title = strdup(line->elements[0].item);      if (line->type == LT_TITLE) {
841      title++;   char *tmp = line->elements[cfg->cfi->titlePosition].item;
842      *(title + strlen(title) - 1) = '\0';   if (cfg->cfi->titleBracketed) {
843        tmp++;
844        title = strdup(tmp);
845        *(title + strlen(title) - 1) = '\0';
846     } else {
847        title = strdup(tmp);
848     }
849        } else if (line->type == LT_MENUENTRY)
850     title = strdup(line->elements[1].item);
851        else
852     return NULL;
853      return title;      return title;
854  }  }
855    
# Line 389  static void lineInit(struct singleLine * Line 891  static void lineInit(struct singleLine *
891      line->next = NULL;      line->next = NULL;
892  }  }
893    
894  static void lineFree(struct singleLine * line) {  struct singleLine * lineDup(struct singleLine * line) {
895      int i;      struct singleLine * newLine = malloc(sizeof(*newLine));
896    
897        newLine->indent = strdup(line->indent);
898        newLine->next = NULL;
899        newLine->type = line->type;
900        newLine->numElements = line->numElements;
901        newLine->elements = malloc(sizeof(*newLine->elements) *
902           newLine->numElements);
903    
904        for (int i = 0; i < newLine->numElements; i++) {
905     newLine->elements[i].indent = strdup(line->elements[i].indent);
906     newLine->elements[i].item = strdup(line->elements[i].item);
907        }
908    
909        return newLine;
910    }
911    
912    static void lineFree(struct singleLine * line) {
913      if (line->indent) free(line->indent);      if (line->indent) free(line->indent);
914    
915      for (i = 0; i < line->numElements; i++) {      for (int i = 0; i < line->numElements; i++) {
916   free(line->elements[i].item);   free(line->elements[i].item);
917   free(line->elements[i].indent);   free(line->elements[i].indent);
918      }      }
# Line 405  static void lineFree(struct singleLine * Line 923  static void lineFree(struct singleLine *
923    
924  static int lineWrite(FILE * out, struct singleLine * line,  static int lineWrite(FILE * out, struct singleLine * line,
925       struct configFileInfo * cfi) {       struct configFileInfo * cfi) {
     int i;  
   
926      if (fprintf(out, "%s", line->indent) == -1) return -1;      if (fprintf(out, "%s", line->indent) == -1) return -1;
927    
928      for (i = 0; i < line->numElements; i++) {      for (int i = 0; i < line->numElements; i++) {
929     /* Need to handle this, because we strip the quotes from
930     * menuentry when read it. */
931     if (line->type == LT_MENUENTRY && i == 1) {
932        if(!isquote(*line->elements[i].item))
933     fprintf(out, "\'%s\'", line->elements[i].item);
934        else
935     fprintf(out, "%s", line->elements[i].item);
936        fprintf(out, "%s", line->elements[i].indent);
937    
938        continue;
939     }
940    
941   if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)   if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)
942      if (fputc('"', out) == EOF) return -1;      if (fputc('"', out) == EOF) return -1;
943    
944   if (fprintf(out, "%s", line->elements[i].item) == -1) return -1;   if (fprintf(out, "%s", line->elements[i].item) == -1) return -1;
945   if (fprintf(out, "%s", line->elements[i].indent) == -1) return -1;   if (i < line->numElements - 1)
946        if (fprintf(out, "%s", line->elements[i].indent) == -1) return -1;
947      }      }
948    
949      if (line->type == LT_KERNELARGS && cfi->argsInQuotes)      if (line->type == LT_KERNELARGS && cfi->argsInQuotes)
# Line 433  static int getNextLine(char ** bufPtr, s Line 962  static int getNextLine(char ** bufPtr, s
962      char * chptr;      char * chptr;
963      int elementsAlloced = 0;      int elementsAlloced = 0;
964      struct lineElement * element;      struct lineElement * element;
     struct keywordTypes * keywords = cfi->keywords;  
965      int first = 1;      int first = 1;
     int i;  
966    
967      lineFree(line);      lineFree(line);
968    
# Line 489  static int getNextLine(char ** bufPtr, s Line 1016  static int getNextLine(char ** bufPtr, s
1016      if (!line->numElements)      if (!line->numElements)
1017   line->type = LT_WHITESPACE;   line->type = LT_WHITESPACE;
1018      else {      else {
1019   for (i = 0; keywords[i].key; i++)   line->type = getTypeByKeyword(line->elements[0].item, cfi);
1020      if (!strcmp(line->elements[0].item, keywords[i].key)) break;   if (line->type == LT_UNKNOWN) {
   
  if (keywords[i].key) {  
     line->type = keywords[i].type;  
  } else {  
     line->type = LT_UNKNOWN;  
               
1021              /* zipl does [title] instead of something reasonable like all              /* zipl does [title] instead of something reasonable like all
1022               * the other boot loaders.  kind of ugly */               * the other boot loaders.  kind of ugly */
1023              if (cfi->titleBracketed && isBracketedTitle(line)) {              if (cfi->titleBracketed && isBracketedTitle(line)) {
# Line 510  static int getNextLine(char ** bufPtr, s Line 1031  static int getNextLine(char ** bufPtr, s
1031      if (*line->elements[0].item == '#') {      if (*line->elements[0].item == '#') {
1032   char * fullLine;   char * fullLine;
1033   int len;   int len;
  int i;  
1034    
1035   len = strlen(line->indent);   len = strlen(line->indent);
1036   for (i = 0; i < line->numElements; i++)   for (int i = 0; i < line->numElements; i++)
1037      len += strlen(line->elements[i].item) +      len += strlen(line->elements[i].item) +
1038     strlen(line->elements[i].indent);     strlen(line->elements[i].indent);
1039    
# Line 522  static int getNextLine(char ** bufPtr, s Line 1042  static int getNextLine(char ** bufPtr, s
1042   free(line->indent);   free(line->indent);
1043   line->indent = fullLine;   line->indent = fullLine;
1044    
1045   for (i = 0; i < line->numElements; i++) {   for (int i = 0; i < line->numElements; i++) {
1046      strcat(fullLine, line->elements[i].item);      strcat(fullLine, line->elements[i].item);
1047      strcat(fullLine, line->elements[i].indent);      strcat(fullLine, line->elements[i].indent);
1048      free(line->elements[i].item);      free(line->elements[i].item);
# Line 532  static int getNextLine(char ** bufPtr, s Line 1052  static int getNextLine(char ** bufPtr, s
1052   line->type = LT_WHITESPACE;   line->type = LT_WHITESPACE;
1053   line->numElements = 0;   line->numElements = 0;
1054      }      }
1055     } else {
1056     struct keywordTypes *kw;
1057    
1058     kw = getKeywordByType(line->type, cfi);
1059    
1060     /* space isn't the only separator, we need to split
1061     * elements up more
1062     */
1063     if (!isspace(kw->separatorChar)) {
1064        char indent[2] = "";
1065        indent[0] = kw->separatorChar;
1066        for (int i = 1; i < line->numElements; i++) {
1067     char *p;
1068     int numNewElements;
1069    
1070     numNewElements = 0;
1071     p = line->elements[i].item;
1072     while (*p != '\0') {
1073     if (*p == kw->separatorChar)
1074     numNewElements++;
1075     p++;
1076     }
1077     if (line->numElements + numNewElements >= elementsAlloced) {
1078     elementsAlloced += numNewElements + 5;
1079     line->elements = realloc(line->elements,
1080        sizeof(*line->elements) * elementsAlloced);
1081     }
1082    
1083     for (int j = line->numElements; j > i; j--) {
1084     line->elements[j + numNewElements] = line->elements[j];
1085     }
1086     line->numElements += numNewElements;
1087    
1088     p = line->elements[i].item;
1089     while (*p != '\0') {
1090    
1091     while (*p != kw->separatorChar && *p != '\0') p++;
1092     if (*p == '\0') {
1093     break;
1094     }
1095    
1096     line->elements[i + 1].indent = line->elements[i].indent;
1097     line->elements[i].indent = strdup(indent);
1098     *p++ = '\0';
1099     i++;
1100     line->elements[i].item = strdup(p);
1101     }
1102        }
1103     }
1104   }   }
1105      }      }
1106    
1107      return 0;      return 0;
1108  }  }
1109    
1110    static int isnumber(const char *s)
1111    {
1112        int i;
1113        for (i = 0; s[i] != '\0'; i++)
1114     if (s[i] < '0' || s[i] > '9')
1115        return 0;
1116        return i;
1117    }
1118    
1119  static struct grubConfig * readConfig(const char * inName,  static struct grubConfig * readConfig(const char * inName,
1120        struct configFileInfo * cfi) {        struct configFileInfo * cfi) {
1121      int in;      int in;
# Line 549  static struct grubConfig * readConfig(co Line 1127  static struct grubConfig * readConfig(co
1127      struct singleLine * last = NULL, * line, * defaultLine = NULL;      struct singleLine * last = NULL, * line, * defaultLine = NULL;
1128      char * end;      char * end;
1129      struct singleEntry * entry = NULL;      struct singleEntry * entry = NULL;
1130      int i, len;      int len;
1131      char * buf;      char * buf;
1132    
1133      if (!strcmp(inName, "-")) {      if (inName == NULL) {
1134            printf("Could not find bootloader configuration\n");
1135            exit(1);
1136        } else if (!strcmp(inName, "-")) {
1137   in = 0;   in = 0;
1138      } else {      } else {
1139   if ((in = open(inName, O_RDONLY)) < 0) {   if ((in = open(inName, O_RDONLY)) < 0) {
# Line 595  static struct grubConfig * readConfig(co Line 1176  static struct grubConfig * readConfig(co
1176      cfg->secondaryIndent = strdup(line->indent);      cfg->secondaryIndent = strdup(line->indent);
1177   }   }
1178    
1179   if (isEntrySeparator(line, cfi)) {   if (isEntryStart(line, cfi) || (cfg->entries && !sawEntry)) {
1180      sawEntry = 1;      sawEntry = 1;
1181      if (!entry) {      if (!entry) {
1182   cfg->entries = malloc(sizeof(*entry));   cfg->entries = malloc(sizeof(*entry));
# Line 611  static struct grubConfig * readConfig(co Line 1192  static struct grubConfig * readConfig(co
1192      entry->next = NULL;      entry->next = NULL;
1193   }   }
1194    
1195   if (line->type == LT_DEFAULT && line->numElements == 2) {   if (line->type == LT_SET_VARIABLE) {
1196      cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;      dbgPrintf("found 'set' command (%d elements): ", line->numElements);
1197      defaultLine = line;      dbgPrintf("%s", line->indent);
1198        for (int i = 0; i < line->numElements; i++)
1199     dbgPrintf("\"%s\"%s", line->elements[i].item, line->elements[i].indent);
1200        dbgPrintf("\n");
1201        struct keywordTypes *kwType = getKeywordByType(LT_DEFAULT, cfi);
1202        if (kwType && line->numElements == 3 &&
1203        !strcmp(line->elements[1].item, kwType->key) &&
1204        !is_special_grub2_variable(line->elements[2].item)) {
1205     dbgPrintf("Line sets default config\n");
1206     cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1207     defaultLine = line;
1208        }
1209    
1210            } else if (iskernel(line->type)) {
1211        /* if by some freak chance this is multiboot and the "module"
1212         * lines came earlier in the template, make sure to use LT_HYPER
1213         * instead of LT_KERNEL now
1214         */
1215        if (entry && entry->multiboot)
1216     line->type = LT_HYPER;
1217    
1218          } else if (line->type == LT_MBMODULE) {          } else if (line->type == LT_MBMODULE) {
1219        /* go back and fix the LT_KERNEL line to indicate LT_HYPER
1220         * instead, now that we know this is a multiboot entry.
1221         * This only applies to grub, but that's the only place we
1222         * should find LT_MBMODULE lines anyway.
1223         */
1224        for (struct singleLine *l = entry->lines; l; l = l->next) {
1225     if (l->type == LT_HYPER)
1226        break;
1227     else if (iskernel(l->type)) {
1228        l->type = LT_HYPER;
1229        break;
1230     }
1231        }
1232              entry->multiboot = 1;              entry->multiboot = 1;
1233    
1234     } else if (line->type == LT_HYPER) {
1235        entry->multiboot = 1;
1236    
1237   } else if (line->type == LT_FALLBACK && line->numElements == 2) {   } else if (line->type == LT_FALLBACK && line->numElements == 2) {
1238      cfg->fallbackImage = strtol(line->elements[1].item, &end, 10);      cfg->fallbackImage = strtol(line->elements[1].item, &end, 10);
1239      if (*end) cfg->fallbackImage = -1;      if (*end) cfg->fallbackImage = -1;
1240   } else if (line->type == LT_TITLE && line->numElements > 1) {  
1241      /* make the title a single argument (undoing our parsing) */   } else if ((line->type == LT_DEFAULT && cfi->defaultIsUnquoted) ||
1242                    (line->type == LT_TITLE && line->numElements > 1)) {
1243        /* make the title/default a single argument (undoing our parsing) */
1244      len = 0;      len = 0;
1245      for (i = 1; i < line->numElements; i++) {      for (int i = 1; i < line->numElements; i++) {
1246   len += strlen(line->elements[i].item);   len += strlen(line->elements[i].item);
1247   len += strlen(line->elements[i].indent);   len += strlen(line->elements[i].indent);
1248      }      }
1249      buf = malloc(len + 1);      buf = malloc(len + 1);
1250      *buf = '\0';      *buf = '\0';
1251    
1252      for (i = 1; i < line->numElements; i++) {      for (int i = 1; i < line->numElements; i++) {
1253   strcat(buf, line->elements[i].item);   strcat(buf, line->elements[i].item);
1254   free(line->elements[i].item);   free(line->elements[i].item);
1255    
# Line 643  static struct grubConfig * readConfig(co Line 1263  static struct grubConfig * readConfig(co
1263      line->elements[line->numElements - 1].indent;      line->elements[line->numElements - 1].indent;
1264      line->elements[1].item = buf;      line->elements[1].item = buf;
1265      line->numElements = 2;      line->numElements = 2;
1266     } else if (line->type == LT_MENUENTRY && line->numElements > 3) {
1267        /* let --remove-kernel="TITLE=what" work */
1268        len = 0;
1269        char *extras;
1270        char *title;
1271    
1272        for (int i = 1; i < line->numElements; i++) {
1273     len += strlen(line->elements[i].item);
1274     len += strlen(line->elements[i].indent);
1275        }
1276        buf = malloc(len + 1);
1277        *buf = '\0';
1278    
1279        /* allocate mem for extra flags. */
1280        extras = malloc(len + 1);
1281        *extras = '\0';
1282    
1283        /* get title. */
1284        for (int i = 0; i < line->numElements; i++) {
1285     if (!strcmp(line->elements[i].item, "menuentry"))
1286        continue;
1287     if (isquote(*line->elements[i].item))
1288        title = line->elements[i].item + 1;
1289     else
1290        title = line->elements[i].item;
1291    
1292     len = strlen(title);
1293            if (isquote(title[len-1])) {
1294        strncat(buf, title,len-1);
1295        break;
1296     } else {
1297        strcat(buf, title);
1298        strcat(buf, line->elements[i].indent);
1299     }
1300        }
1301    
1302        /* get extras */
1303        int count = 0;
1304        for (int i = 0; i < line->numElements; i++) {
1305     if (count >= 2) {
1306        strcat(extras, line->elements[i].item);
1307        strcat(extras, line->elements[i].indent);
1308     }
1309    
1310     if (!strcmp(line->elements[i].item, "menuentry"))
1311        continue;
1312    
1313     /* count ' or ", there should be two in menuentry line. */
1314     if (isquote(*line->elements[i].item))
1315        count++;
1316    
1317     len = strlen(line->elements[i].item);
1318    
1319     if (isquote(line->elements[i].item[len -1]))
1320        count++;
1321    
1322     /* ok, we get the final ' or ", others are extras. */
1323                }
1324        line->elements[1].indent =
1325     line->elements[line->numElements - 2].indent;
1326        line->elements[1].item = buf;
1327        line->elements[2].indent =
1328     line->elements[line->numElements - 2].indent;
1329        line->elements[2].item = extras;
1330        line->numElements = 3;
1331   } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {   } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {
1332      /* Strip off any " which may be present; they'll be put back      /* Strip off any " which may be present; they'll be put back
1333         on write. This is one of the few (the only?) places that grubby         on write. This is one of the few (the only?) places that grubby
# Line 651  static struct grubConfig * readConfig(co Line 1336  static struct grubConfig * readConfig(co
1336      if (line->numElements >= 2) {      if (line->numElements >= 2) {
1337   int last, len;   int last, len;
1338    
1339   if (*line->elements[1].item == '"')   if (isquote(*line->elements[1].item))
1340      memcpy(line->elements[1].item, line->elements[1].item + 1,      memmove(line->elements[1].item, line->elements[1].item + 1,
1341     strlen(line->elements[1].item + 1) + 1);      strlen(line->elements[1].item + 1) + 1);
1342    
1343   last = line->numElements - 1;   last = line->numElements - 1;
1344   len = strlen(line->elements[last].item) - 1;   len = strlen(line->elements[last].item) - 1;
1345   if (line->elements[last].item[len] == '"')   if (isquote(line->elements[last].item[len]))
1346      line->elements[last].item[len] = '\0';      line->elements[last].item[len] = '\0';
1347      }      }
1348     }
1349    
1350     if (line->type == LT_DEFAULT && line->numElements == 2) {
1351        cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1352        defaultLine = line;
1353   }   }
1354    
1355   /* If we find a generic config option which should live at the   /* If we find a generic config option which should live at the
# Line 680  static struct grubConfig * readConfig(co Line 1369  static struct grubConfig * readConfig(co
1369   movedLine = 1;   movedLine = 1;
1370   continue; /* without setting 'last' */   continue; /* without setting 'last' */
1371   }   }
1372    
1373   /* If a second line of whitespace happens after a generic option   /* If a second line of whitespace happens after a generic option
1374     which was moved, drop it. */     which was moved, drop it. */
1375   if (movedLine && line->type == LT_WHITESPACE && last->type == LT_WHITESPACE) {   if (movedLine && line->type == LT_WHITESPACE && last->type == LT_WHITESPACE) {
# Line 695  static struct grubConfig * readConfig(co Line 1385  static struct grubConfig * readConfig(co
1385   entry->lines = line;   entry->lines = line;
1386      else      else
1387   last->next = line;   last->next = line;
1388        dbgPrintf("readConfig added %s to %p\n", getKeyByType(line->type, cfi), entry);
1389    
1390        /* we could have seen this outside of an entry... if so, we
1391         * ignore it like any other line we don't grok */
1392        if (line->type == LT_ENTRY_END && sawEntry)
1393     sawEntry = 0;
1394   } else {   } else {
1395      if (!cfg->theLines)      if (!cfg->theLines)
1396   cfg->theLines = line;   cfg->theLines = line;
1397      else {      else
1398   last->next = line;   last->next = line;
1399      }      dbgPrintf("readConfig added %s to cfg\n", getKeyByType(line->type, cfi));
1400   }   }
1401    
1402   last = line;   last = line;
# Line 708  static struct grubConfig * readConfig(co Line 1404  static struct grubConfig * readConfig(co
1404    
1405      free(incoming);      free(incoming);
1406    
1407        dbgPrintf("defaultLine is %s\n", defaultLine ? "set" : "unset");
1408      if (defaultLine) {      if (defaultLine) {
1409   if (cfi->defaultSupportSaved &&          if (defaultLine->numElements > 2 &&
1410        cfi->defaultSupportSaved &&
1411        !strncmp(defaultLine->elements[2].item,"\"${saved_entry}\"", 16)) {
1412     cfg->cfi->defaultIsSaved = 1;
1413     cfg->defaultImage = DEFAULT_SAVED_GRUB2;
1414     if (cfg->cfi->getEnv) {
1415        char *defTitle = cfi->getEnv(cfg->cfi, "saved_entry");
1416        if (defTitle) {
1417     int index = 0;
1418     if (isnumber(defTitle)) {
1419        index = atoi(defTitle);
1420        entry = findEntryByIndex(cfg, index);
1421     } else {
1422        entry = findEntryByTitle(cfg, defTitle, &index);
1423     }
1424     if (entry)
1425        cfg->defaultImage = index;
1426        }
1427     }
1428     } else if (cfi->defaultIsVariable) {
1429        char *value = defaultLine->elements[2].item;
1430        while (*value && (*value == '"' || *value == '\'' ||
1431        *value == ' ' || *value == '\t'))
1432     value++;
1433        cfg->defaultImage = strtol(value, &end, 10);
1434        while (*end && (*end == '"' || *end == '\'' ||
1435        *end == ' ' || *end == '\t'))
1436     end++;
1437        if (*end) cfg->defaultImage = -1;
1438     } else if (cfi->defaultSupportSaved &&
1439   !strncmp(defaultLine->elements[1].item, "saved", 5)) {   !strncmp(defaultLine->elements[1].item, "saved", 5)) {
1440      cfg->defaultImage = DEFAULT_SAVED;      cfg->defaultImage = DEFAULT_SAVED;
1441   } else if (cfi->defaultIsIndex) {   } else if (cfi->defaultIsIndex) {
1442      cfg->defaultImage = strtol(defaultLine->elements[1].item, &end, 10);      cfg->defaultImage = strtol(defaultLine->elements[1].item, &end, 10);
1443      if (*end) cfg->defaultImage = -1;      if (*end) cfg->defaultImage = -1;
1444   } else if (defaultLine->numElements >= 2) {   } else if (defaultLine->numElements >= 2) {
1445      i = 0;      int i = 0;
1446      while ((entry = findEntryByIndex(cfg, i))) {      while ((entry = findEntryByIndex(cfg, i))) {
1447   for (line = entry->lines; line; line = line->next)   for (line = entry->lines; line; line = line->next)
1448      if (line->type == LT_TITLE) break;      if (line->type == LT_TITLE) break;
# Line 727  static struct grubConfig * readConfig(co Line 1453  static struct grubConfig * readConfig(co
1453                                  line->elements[1].item)) break;                                  line->elements[1].item)) break;
1454                  } else if (line) {                  } else if (line) {
1455                      if (!strcmp(defaultLine->elements[1].item,                      if (!strcmp(defaultLine->elements[1].item,
1456                                  extractTitle(line))) break;                                  extractTitle(cfg, line))) break;
1457                  }                  }
1458   i++;   i++;
1459     entry = NULL;
1460      }      }
1461    
1462      if (entry) cfg->defaultImage = i;      if (entry){
1463            cfg->defaultImage = i;
1464        }else{
1465            cfg->defaultImage = -1;
1466        }
1467     }
1468        } else if (cfg->cfi->defaultIsSaved && cfg->cfi->getEnv) {
1469     char *defTitle = cfi->getEnv(cfg->cfi, "saved_entry");
1470     if (defTitle) {
1471        int index = 0;
1472        if (isnumber(defTitle)) {
1473     index = atoi(defTitle);
1474     entry = findEntryByIndex(cfg, index);
1475        } else {
1476     entry = findEntryByTitle(cfg, defTitle, &index);
1477        }
1478        if (entry)
1479     cfg->defaultImage = index;
1480   }   }
1481        } else {
1482            cfg->defaultImage = 0;
1483      }      }
1484    
1485      return cfg;      return cfg;
# Line 749  static void writeDefault(FILE * out, cha Line 1495  static void writeDefault(FILE * out, cha
1495    
1496      if (cfg->defaultImage == DEFAULT_SAVED)      if (cfg->defaultImage == DEFAULT_SAVED)
1497   fprintf(out, "%sdefault%ssaved\n", indent, separator);   fprintf(out, "%sdefault%ssaved\n", indent, separator);
1498      else if (cfg->defaultImage > -1) {      else if (cfg->cfi->defaultIsSaved) {
1499     fprintf(out, "%sset default=\"${saved_entry}\"\n", indent);
1500     if (cfg->defaultImage >= 0 && cfg->cfi->setEnv) {
1501        char *title;
1502        entry = findEntryByIndex(cfg, cfg->defaultImage);
1503        line = getLineByType(LT_MENUENTRY, entry->lines);
1504        if (!line)
1505     line = getLineByType(LT_TITLE, entry->lines);
1506        if (line) {
1507     title = extractTitle(cfg, line);
1508     if (title)
1509        cfg->cfi->setEnv(cfg->cfi, "saved_entry", title);
1510        }
1511     }
1512        } else if (cfg->defaultImage > -1) {
1513   if (cfg->cfi->defaultIsIndex) {   if (cfg->cfi->defaultIsIndex) {
1514      fprintf(out, "%sdefault%s%d\n", indent, separator,      if (cfg->cfi->defaultIsVariable) {
1515      cfg->defaultImage);          fprintf(out, "%sset default=\"%d\"\n", indent,
1516     cfg->defaultImage);
1517        } else {
1518     fprintf(out, "%sdefault%s%d\n", indent, separator,
1519     cfg->defaultImage);
1520        }
1521   } else {   } else {
1522      int image = cfg->defaultImage;      int image = cfg->defaultImage;
1523    
# Line 769  static void writeDefault(FILE * out, cha Line 1534  static void writeDefault(FILE * out, cha
1534    
1535      if (!entry) return;      if (!entry) return;
1536    
1537      line = entry->lines;      line = getLineByType(LT_TITLE, entry->lines);
     while (line && line->type != LT_TITLE) line = line->next;  
1538    
1539      if (line && line->numElements >= 2)      if (line && line->numElements >= 2)
1540   fprintf(out, "%sdefault%s%s\n", indent, separator,   fprintf(out, "%sdefault%s%s\n", indent, separator,
# Line 778  static void writeDefault(FILE * out, cha Line 1542  static void writeDefault(FILE * out, cha
1542              else if (line && (line->numElements == 1) &&              else if (line && (line->numElements == 1) &&
1543                       cfg->cfi->titleBracketed) {                       cfg->cfi->titleBracketed) {
1544   fprintf(out, "%sdefault%s%s\n", indent, separator,   fprintf(out, "%sdefault%s%s\n", indent, separator,
1545                          extractTitle(line));                          extractTitle(cfg, line));
1546              }              }
1547   }   }
1548      }      }
# Line 804  static int writeConfig(struct grubConfig Line 1568  static int writeConfig(struct grubConfig
1568      int rc;      int rc;
1569    
1570      /* most likely the symlink is relative, so change our      /* most likely the symlink is relative, so change our
1571         directory to / */         directory to the dir of the symlink */
1572      rc = chdir("/");      char *dir = strdupa(outName);
1573        rc = chdir(dirname(dir));
1574      do {      do {
1575   buf = alloca(len + 1);   buf = alloca(len + 1);
1576   rc = readlink(outName, buf, len);   rc = readlink(basename(outName), buf, len);
1577   if (rc == len) len += 256;   if (rc == len) len += 256;
1578      } while (rc == len);      } while (rc == len);
1579            
# Line 843  static int writeConfig(struct grubConfig Line 1608  static int writeConfig(struct grubConfig
1608      }      }
1609    
1610      line = cfg->theLines;      line = cfg->theLines;
1611        struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi);
1612      while (line) {      while (line) {
1613   if (line->type == LT_DEFAULT) {          if (line->type == LT_SET_VARIABLE && defaultKw &&
1614     line->numElements == 3 &&
1615     !strcmp(line->elements[1].item, defaultKw->key) &&
1616     !is_special_grub2_variable(line->elements[2].item)) {
1617        writeDefault(out, line->indent, line->elements[0].indent, cfg);
1618        needs &= ~MAIN_DEFAULT;
1619     } else if (line->type == LT_DEFAULT) {
1620      writeDefault(out, line->indent, line->elements[0].indent, cfg);      writeDefault(out, line->indent, line->elements[0].indent, cfg);
1621      needs &= ~MAIN_DEFAULT;      needs &= ~MAIN_DEFAULT;
1622   } else if (line->type == LT_FALLBACK) {   } else if (line->type == LT_FALLBACK) {
# Line 912  static int numEntries(struct grubConfig Line 1684  static int numEntries(struct grubConfig
1684      return i;      return i;
1685  }  }
1686    
1687  int suitableImage(struct singleEntry * entry, const char * bootPrefix,  static char *findDiskForRoot()
1688    int skipRemoved, int flags) {  {
1689      struct singleLine * line;      int fd;
1690      char * fullName;      char buf[65536];
1691      int i;      char *devname;
1692      struct stat sb, sb2;      char *chptr;
1693      char * dev;      int rc;
     char * end;  
     char * rootspec;  
1694    
1695      line = entry->lines;      if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {
1696      while (line && line->type != LT_KERNEL) line = line->next;          fprintf(stderr, "grubby: failed to open %s: %s\n",
1697                        _PATH_MOUNTED, strerror(errno));
1698      if (!line) return 0;          return NULL;
1699      if (skipRemoved && entry->skip) return 0;      }
1700      if (line->numElements < 2) return 0;  
1701        rc = read(fd, buf, sizeof(buf) - 1);
1702        if (rc <= 0) {
1703            fprintf(stderr, "grubby: failed to read %s: %s\n",
1704                    _PATH_MOUNTED, strerror(errno));
1705            close(fd);
1706            return NULL;
1707        }
1708        close(fd);
1709        buf[rc] = '\0';
1710        chptr = buf;
1711    
1712        char *foundanswer = NULL;
1713    
1714        while (chptr && chptr != buf+rc) {
1715            devname = chptr;
1716    
1717            /*
1718             * The first column of a mtab entry is the device, but if the entry is a
1719             * special device it won't start with /, so move on to the next line.
1720             */
1721            if (*devname != '/') {
1722                chptr = strchr(chptr, '\n');
1723                if (chptr)
1724                    chptr++;
1725                continue;
1726            }
1727    
1728            /* Seek to the next space */
1729            chptr = strchr(chptr, ' ');
1730            if (!chptr) {
1731                fprintf(stderr, "grubby: error parsing %s: %s\n",
1732                        _PATH_MOUNTED, strerror(errno));
1733                return NULL;
1734            }
1735    
1736            /*
1737             * The second column of a mtab entry is the mount point, we are looking
1738             * for '/' obviously.
1739             */
1740            if (*(++chptr) == '/' && *(++chptr) == ' ') {
1741                /* remember the last / entry in mtab */
1742               foundanswer = devname;
1743            }
1744    
1745            /* Next line */
1746            chptr = strchr(chptr, '\n');
1747            if (chptr)
1748                chptr++;
1749        }
1750    
1751        /* Return the last / entry found */
1752        if (foundanswer) {
1753            chptr = strchr(foundanswer, ' ');
1754            *chptr = '\0';
1755            return strdup(foundanswer);
1756        }
1757    
1758        return NULL;
1759    }
1760    
1761    void printEntry(struct singleEntry * entry, FILE *f) {
1762        int i;
1763        struct singleLine * line;
1764    
1765        for (line = entry->lines; line; line = line->next) {
1766     log_message(f, "DBG: %s", line->indent);
1767     for (i = 0; i < line->numElements; i++) {
1768        /* Need to handle this, because we strip the quotes from
1769         * menuentry when read it. */
1770        if (line->type == LT_MENUENTRY && i == 1) {
1771     if(!isquote(*line->elements[i].item))
1772        log_message(f, "\'%s\'", line->elements[i].item);
1773     else
1774        log_message(f, "%s", line->elements[i].item);
1775     log_message(f, "%s", line->elements[i].indent);
1776    
1777     continue;
1778        }
1779        
1780        log_message(f, "%s%s",
1781        line->elements[i].item, line->elements[i].indent);
1782     }
1783     log_message(f, "\n");
1784        }
1785    }
1786    
1787    void notSuitablePrintf(struct singleEntry * entry, int okay, const char *fmt, ...)
1788    {
1789        static int once;
1790        va_list argp, argq;
1791    
1792        va_start(argp, fmt);
1793    
1794        va_copy(argq, argp);
1795        if (!once) {
1796     log_time(NULL);
1797     log_message(NULL, "command line: %s\n", saved_command_line);
1798        }
1799        log_message(NULL, "DBG: Image entry %s: ", okay ? "succeeded" : "failed");
1800        log_vmessage(NULL, fmt, argq);
1801    
1802        printEntry(entry, NULL);
1803        va_end(argq);
1804    
1805        if (!debug) {
1806     once = 1;
1807         va_end(argp);
1808     return;
1809        }
1810    
1811        if (okay) {
1812     va_end(argp);
1813     return;
1814        }
1815    
1816        if (!once)
1817     log_message(stderr, "DBG: command line: %s\n", saved_command_line);
1818        once = 1;
1819        fprintf(stderr, "DBG: Image entry failed: ");
1820        vfprintf(stderr, fmt, argp);
1821        printEntry(entry, stderr);
1822        va_end(argp);
1823    }
1824    
1825    #define beginswith(s, c) ((s) && (s)[0] == (c))
1826    
1827    static int endswith(const char *s, char c)
1828    {
1829     int slen;
1830    
1831     if (!s || !s[0])
1832     return 0;
1833     slen = strlen(s) - 1;
1834    
1835     return s[slen] == c;
1836    }
1837    
1838    int suitableImage(struct singleEntry * entry, const char * bootPrefix,
1839      int skipRemoved, int flags) {
1840        struct singleLine * line;
1841        char * fullName;
1842        int i;
1843        char * dev;
1844        char * rootspec;
1845        char * rootdev;
1846    
1847      if (flags & GRUBBY_BADIMAGE_OKAY) return 1;      if (skipRemoved && entry->skip) {
1848     notSuitablePrintf(entry, 0, "marked to skip\n");
1849     return 0;
1850        }
1851    
1852        line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
1853        if (!line) {
1854     notSuitablePrintf(entry, 0, "no line found\n");
1855     return 0;
1856        }
1857        if (line->numElements < 2) {
1858     notSuitablePrintf(entry, 0, "line has only %d elements\n",
1859        line->numElements);
1860     return 0;
1861        }
1862    
1863        if (flags & GRUBBY_BADIMAGE_OKAY) {
1864        notSuitablePrintf(entry, 1, "\n");
1865        return 1;
1866        }
1867    
1868      fullName = alloca(strlen(bootPrefix) +      fullName = alloca(strlen(bootPrefix) +
1869        strlen(line->elements[1].item) + 1);        strlen(line->elements[1].item) + 1);
1870      rootspec = getRootSpecifier(line->elements[1].item);      rootspec = getRootSpecifier(line->elements[1].item);
1871      sprintf(fullName, "%s%s", bootPrefix,      int rootspec_offset = rootspec ? strlen(rootspec) : 0;
1872              line->elements[1].item + ((rootspec != NULL) ?      int hasslash = endswith(bootPrefix, '/') ||
1873                                        strlen(rootspec) : 0));       beginswith(line->elements[1].item + rootspec_offset, '/');
1874      if (access(fullName, R_OK)) return 0;      sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/",
1875                line->elements[1].item + rootspec_offset);
1876        if (access(fullName, R_OK)) {
1877     notSuitablePrintf(entry, 0, "access to %s failed\n", fullName);
1878     return 0;
1879        }
1880      for (i = 2; i < line->numElements; i++)      for (i = 2; i < line->numElements; i++)
1881   if (!strncasecmp(line->elements[i].item, "root=", 5)) break;   if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1882      if (i < line->numElements) {      if (i < line->numElements) {
1883   dev = line->elements[i].item + 5;   dev = line->elements[i].item + 5;
1884      } else {      } else {
1885   /* look for a lilo style LT_ROOT line */   /* look for a lilo style LT_ROOT line */
1886   line = entry->lines;   line = getLineByType(LT_ROOT, entry->lines);
  while (line && line->type != LT_ROOT) line = line->next;  
1887    
1888   if (line && line->numElements >= 2) {   if (line && line->numElements >= 2) {
1889      dev = line->elements[1].item;      dev = line->elements[1].item;
1890   } else {   } else {
1891              int type;      /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS.
1892      /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS */       * grub+multiboot uses LT_MBMODULE for the args, so check that too.
1893      line = entry->lines;       */
1894        line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines);
             type = ((entry->multiboot) ? LT_MBMODULE : LT_KERNELARGS);  
   
     while (line && line->type != type) line = line->next;  
1895    
1896              /* failed to find one */              /* failed to find one */
1897              if (!line) return 0;              if (!line) {
1898     notSuitablePrintf(entry, 0, "no line found\n");
1899     return 0;
1900                }
1901    
1902      for (i = 1; i < line->numElements; i++)      for (i = 1; i < line->numElements; i++)
1903          if (!strncasecmp(line->elements[i].item, "root=", 5)) break;          if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1904      if (i < line->numElements)      if (i < line->numElements)
1905          dev = line->elements[i].item + 5;          dev = line->elements[i].item + 5;
1906      else {      else {
1907     notSuitablePrintf(entry, 0, "no root= entry found\n");
1908   /* it failed too...  can't find root= */   /* it failed too...  can't find root= */
1909          return 0;          return 0;
1910              }              }
1911   }   }
1912      }      }
1913    
1914      if (!strncmp(dev, "LABEL=", 6)) {      dev = getpathbyspec(dev);
1915   dev += 6;      if (!getpathbyspec(dev)) {
1916            notSuitablePrintf(entry, 0, "can't find blkid entry for %s\n", dev);
1917   /* check which device has this label */          return 0;
1918   dev = get_spec_by_volume_label(dev, &i, &i);      } else
1919   if (!dev) return 0;   dev = getpathbyspec(dev);
1920    
1921        rootdev = findDiskForRoot();
1922        if (!rootdev) {
1923            notSuitablePrintf(entry, 0, "can't find root device\n");
1924     return 0;
1925      }      }
1926    
1927      if (*dev == '/') {      if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {
1928   if (stat(dev, &sb))          notSuitablePrintf(entry, 0, "uuid missing: rootdev %s, dev %s\n",
1929      return 0;   getuuidbydev(rootdev), getuuidbydev(dev));
1930      } else {          free(rootdev);
1931   sb.st_rdev = strtol(dev, &end, 16);          return 0;
1932   if (*end) return 0;      }
1933    
1934        if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
1935            notSuitablePrintf(entry, 0, "uuid mismatch: rootdev %s, dev %s\n",
1936     getuuidbydev(rootdev), getuuidbydev(dev));
1937     free(rootdev);
1938            return 0;
1939      }      }
     stat("/", &sb2);  
1940    
1941      if (sb.st_rdev != sb2.st_dev) return 0;      free(rootdev);
1942        notSuitablePrintf(entry, 1, "\n");
1943    
1944      return 1;      return 1;
1945  }  }
# Line 1024  struct singleEntry * findEntryByPath(str Line 1973  struct singleEntry * findEntryByPath(str
1973   }   }
1974    
1975   indexVars[i + 1] = -1;   indexVars[i + 1] = -1;
1976    
1977   i = 0;   i = 0;
1978   if (index) {   if (index) {
1979      while (i < *index) i++;      while (i < *index) {
1980      if (indexVars[i] == -1) return NULL;   i++;
1981     if (indexVars[i] == -1) return NULL;
1982        }
1983   }   }
1984    
1985   entry = findEntryByIndex(config, indexVars[i]);   entry = findEntryByIndex(config, indexVars[i]);
1986   if (!entry) return NULL;   if (!entry) return NULL;
1987    
1988   line = entry->lines;   line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
  while (line && line->type != LT_KERNEL)  
     line = line->next;  
   
1989   if (!line) return NULL;   if (!line) return NULL;
1990    
1991   if (index) *index = indexVars[i];   if (index) *index = indexVars[i];
# Line 1075  struct singleEntry * findEntryByPath(str Line 2023  struct singleEntry * findEntryByPath(str
2023    
2024   if (!strncmp(kernel, "TITLE=", 6)) {   if (!strncmp(kernel, "TITLE=", 6)) {
2025      prefix = "";      prefix = "";
2026      checkType = LT_TITLE;      checkType = LT_TITLE|LT_MENUENTRY;
2027      kernel += 6;      kernel += 6;
2028   }   }
2029    
2030   while ((entry = findEntryByIndex(config, i))) {   for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {
2031      line = entry->lines;      if (entry->skip) continue;
     while (line && line->type != checkType) line=line->next;  
2032    
2033        dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);
2034    
2035      if (line && line->numElements >= 2 && !entry->skip) {      /* check all the lines matching checkType */
2036                  rootspec = getRootSpecifier(line->elements[1].item);      for (line = entry->lines; line; line = line->next) {
2037          if (!strcmp(line->elements[1].item  +   enum lineType_e ct = checkType;
2038                              ((rootspec != NULL) ? strlen(rootspec) : 0),   if (entry->multiboot && checkType == LT_KERNEL)
2039                              kernel + strlen(prefix)))      ct = LT_KERNEL|LT_KERNEL_EFI|LT_MBMODULE|LT_HYPER|LT_KERNEL_16;
2040                      break;   else if (checkType & LT_KERNEL)
2041              }      ct = checkType | LT_KERNEL_EFI | LT_KERNEL_16;
2042                 line = getLineByType(ct, line);
2043              /* have to check multiboot lines too */   if (!line)
2044              if (entry->multiboot) {      break;  /* not found in this entry */
2045                  while (line && line->type != LT_MBMODULE) line = line->next;  
2046                  if (line && line->numElements >= 2 && !entry->skip) {   if (line && line->type != LT_MENUENTRY &&
2047                      rootspec = getRootSpecifier(line->elements[1].item);   line->numElements >= 2) {
2048                      if (!strcmp(line->elements[1].item  +      rootspec = getRootSpecifier(line->elements[1].item);
2049                                  ((rootspec != NULL) ? strlen(rootspec) : 0),      if (!strcmp(line->elements[1].item +
2050                                  kernel + strlen(prefix)))   ((rootspec != NULL) ? strlen(rootspec) : 0),
2051                          break;   kernel + strlen(prefix)))
2052                  }   break;
2053              }   }
2054     if(line->type == LT_MENUENTRY &&
2055     !strcmp(line->elements[1].item, kernel))
2056        break;
2057        }
2058    
2059      i++;      /* make sure this entry has a kernel identifier; this skips
2060         * non-Linux boot entries (could find netbsd etc, though, which is
2061         * unfortunate)
2062         */
2063        if (line && getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines))
2064     break; /* found 'im! */
2065   }   }
2066    
2067   if (index) *index = i;   if (index) *index = i;
2068      }      }
2069    
2070      if (!entry) return NULL;      return entry;
2071    }
2072    
2073      /* make sure this entry has a kernel identifier; this skips non-Linux  struct singleEntry * findEntryByTitle(struct grubConfig * cfg, char *title,
2074         boot entries (could find netbsd etc, though, which is unfortunate) */        int * index) {
2075      line = entry->lines;      struct singleEntry * entry;
2076      while (line && line->type != LT_KERNEL) line = line->next;      struct singleLine * line;
2077      if (!line) {      int i;
2078   if (!index) index = &i;      char * newtitle;
2079   (*index)++;  
2080   return findEntryByPath(config, kernel, prefix, index);      for (i = 0, entry = cfg->entries; entry; entry = entry->next, i++) {
2081     if (index && i < *index)
2082        continue;
2083     line = getLineByType(LT_TITLE, entry->lines);
2084     if (!line)
2085        line = getLineByType(LT_MENUENTRY, entry->lines);
2086     if (!line)
2087        continue;
2088     newtitle = grub2ExtractTitle(line);
2089     if (!newtitle)
2090        continue;
2091     if (!strcmp(title, newtitle))
2092        break;
2093      }      }
2094    
2095        if (!entry)
2096     return NULL;
2097    
2098        if (index)
2099     *index = i;
2100      return entry;      return entry;
2101  }  }
2102    
# Line 1147  struct singleEntry * findTemplate(struct Line 2122  struct singleEntry * findTemplate(struct
2122      struct singleEntry * entry, * entry2;      struct singleEntry * entry, * entry2;
2123      int index;      int index;
2124    
2125      if (cfg->defaultImage > -1) {      if (cfg->cfi->defaultIsSaved) {
2126     if (cfg->cfi->getEnv) {
2127        char *defTitle = cfg->cfi->getEnv(cfg->cfi, "saved_entry");
2128        if (defTitle) {
2129     int index = 0;
2130     if (isnumber(defTitle)) {
2131        index = atoi(defTitle);
2132        entry = findEntryByIndex(cfg, index);
2133     } else {
2134        entry = findEntryByTitle(cfg, defTitle, &index);
2135     }
2136     if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2137        cfg->defaultImage = index;
2138        if (indexPtr)
2139     *indexPtr = index;
2140        return entry;
2141     }
2142        }
2143     }
2144        } else if (cfg->defaultImage > -1) {
2145   entry = findEntryByIndex(cfg, cfg->defaultImage);   entry = findEntryByIndex(cfg, cfg->defaultImage);
2146   if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {   if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2147      if (indexPtr) *indexPtr = cfg->defaultImage;      if (indexPtr) *indexPtr = cfg->defaultImage;
# Line 1200  void markRemovedImage(struct grubConfig Line 2194  void markRemovedImage(struct grubConfig
2194        const char * prefix) {        const char * prefix) {
2195      struct singleEntry * entry;      struct singleEntry * entry;
2196    
2197      if (!image) return;      if (!image)
2198     return;
2199    
2200        /* check and see if we're removing the default image */
2201        if (isdigit(*image)) {
2202     entry = findEntryByPath(cfg, image, prefix, NULL);
2203     if(entry)
2204        entry->skip = 1;
2205     return;
2206        }
2207    
2208      while ((entry = findEntryByPath(cfg, image, prefix, NULL)))      while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
2209   entry->skip = 1;   entry->skip = 1;
# Line 1208  void markRemovedImage(struct grubConfig Line 2211  void markRemovedImage(struct grubConfig
2211    
2212  void setDefaultImage(struct grubConfig * config, int hasNew,  void setDefaultImage(struct grubConfig * config, int hasNew,
2213       const char * defaultKernelPath, int newIsDefault,       const char * defaultKernelPath, int newIsDefault,
2214       const char * prefix, int flags) {       const char * prefix, int flags, int index) {
2215      struct singleEntry * entry, * entry2, * newDefault;      struct singleEntry * entry, * entry2, * newDefault;
2216      int i, j;      int i, j;
2217    
2218      if (newIsDefault) {      if (newIsDefault) {
2219   config->defaultImage = 0;   config->defaultImage = 0;
2220   return;   return;
2221        } else if ((index >= 0) && config->cfi->defaultIsIndex) {
2222     if (findEntryByIndex(config, index))
2223        config->defaultImage = index;
2224     else
2225        config->defaultImage = -1;
2226     return;
2227      } else if (defaultKernelPath) {      } else if (defaultKernelPath) {
2228   i = 0;   i = 0;
2229   if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {   if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
# Line 1227  void setDefaultImage(struct grubConfig * Line 2236  void setDefaultImage(struct grubConfig *
2236    
2237      /* defaultImage now points to what we'd like to use, but before any order      /* defaultImage now points to what we'd like to use, but before any order
2238         changes */         changes */
2239      if (config->defaultImage == DEFAULT_SAVED)      if ((config->defaultImage == DEFAULT_SAVED) ||
2240     (config->defaultImage == DEFAULT_SAVED_GRUB2))
2241        /* default is set to saved, we don't want to change it */        /* default is set to saved, we don't want to change it */
2242        return;        return;
2243    
# Line 1285  void displayEntry(struct singleEntry * e Line 2295  void displayEntry(struct singleEntry * e
2295      struct singleLine * line;      struct singleLine * line;
2296      char * root = NULL;      char * root = NULL;
2297      int i;      int i;
2298        int j;
     line = entry->lines;  
     while (line && line->type != LT_KERNEL) line = line->next;  
2299    
2300      printf("index=%d\n", index);      printf("index=%d\n", index);
2301    
2302      printf("kernel=%s\n", line->elements[1].item);      line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
2303        if (!line) {
2304            printf("non linux entry\n");
2305            return;
2306        }
2307    
2308        if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2309     printf("kernel=%s\n", line->elements[1].item);
2310        else
2311     printf("kernel=%s%s\n", prefix, line->elements[1].item);
2312    
2313      if (line->numElements >= 3) {      if (line->numElements >= 3) {
2314   printf("args=\"");   printf("args=\"");
# Line 1308  void displayEntry(struct singleEntry * e Line 2325  void displayEntry(struct singleEntry * e
2325   }   }
2326   printf("\"\n");   printf("\"\n");
2327      } else {      } else {
2328   line = entry->lines;   line = getLineByType(LT_KERNELARGS, entry->lines);
  while (line && line->type != LT_KERNELARGS) line=line->next;  
   
2329   if (line) {   if (line) {
2330      char * s;      char * s;
2331    
# Line 1334  void displayEntry(struct singleEntry * e Line 2349  void displayEntry(struct singleEntry * e
2349      }      }
2350    
2351      if (!root) {      if (!root) {
2352   line = entry->lines;   line = getLineByType(LT_ROOT, entry->lines);
  while (line && line->type != LT_ROOT) line = line->next;  
   
2353   if (line && line->numElements >= 2)   if (line && line->numElements >= 2)
2354      root=line->elements[1].item;      root=line->elements[1].item;
2355      }      }
# Line 1351  void displayEntry(struct singleEntry * e Line 2364  void displayEntry(struct singleEntry * e
2364   printf("root=%s\n", s);   printf("root=%s\n", s);
2365      }      }
2366    
2367      line = entry->lines;      line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
     while (line && line->type != LT_INITRD) line = line->next;  
2368    
2369      if (line && line->numElements >= 2) {      if (line && line->numElements >= 2) {
2370   printf("initrd=%s", prefix);   if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2371        printf("initrd=");
2372     else
2373        printf("initrd=%s", prefix);
2374    
2375   for (i = 1; i < line->numElements; i++)   for (i = 1; i < line->numElements; i++)
2376      printf("%s%s", line->elements[i].item, line->elements[i].indent);      printf("%s%s", line->elements[i].item, line->elements[i].indent);
2377   printf("\n");   printf("\n");
2378      }      }
2379    
2380        line = getLineByType(LT_TITLE, entry->lines);
2381        if (line) {
2382     printf("title=%s\n", line->elements[1].item);
2383        } else {
2384     char * title;
2385     line = getLineByType(LT_MENUENTRY, entry->lines);
2386     if (line) {
2387        title = grub2ExtractTitle(line);
2388        if (title)
2389     printf("title=%s\n", title);
2390     }
2391        }
2392    
2393        for (j = 0, line = entry->lines; line; line = line->next) {
2394     if ((line->type & LT_MBMODULE) && line->numElements >= 2) {
2395        if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2396     printf("mbmodule%d=", j);
2397        else
2398     printf("mbmodule%d=%s", j, prefix);
2399    
2400        for (i = 1; i < line->numElements; i++)
2401     printf("%s%s", line->elements[i].item, line->elements[i].indent);
2402        printf("\n");
2403        j++;
2404     }
2405        }
2406    }
2407    
2408    int isSuseSystem(void) {
2409        const char * path;
2410        const static char default_path[] = "/etc/SuSE-release";
2411    
2412        if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2413     path = default_path;
2414    
2415        if (!access(path, R_OK))
2416     return 1;
2417        return 0;
2418    }
2419    
2420    int isSuseGrubConf(const char * path) {
2421        FILE * grubConf;
2422        char * line = NULL;
2423        size_t len = 0, res = 0;
2424    
2425        grubConf = fopen(path, "r");
2426        if (!grubConf) {
2427            dbgPrintf("Could not open SuSE configuration file '%s'\n", path);
2428     return 0;
2429        }
2430    
2431        while ((res = getline(&line, &len, grubConf)) != -1) {
2432     if (!strncmp(line, "setup", 5)) {
2433        fclose(grubConf);
2434        free(line);
2435        return 1;
2436     }
2437        }
2438    
2439        dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2440          path);
2441    
2442        fclose(grubConf);
2443        free(line);
2444        return 0;
2445    }
2446    
2447    int suseGrubConfGetLba(const char * path, int * lbaPtr) {
2448        FILE * grubConf;
2449        char * line = NULL;
2450        size_t res = 0, len = 0;
2451    
2452        if (!path) return 1;
2453        if (!lbaPtr) return 1;
2454    
2455        grubConf = fopen(path, "r");
2456        if (!grubConf) return 1;
2457    
2458        while ((res = getline(&line, &len, grubConf)) != -1) {
2459     if (line[res - 1] == '\n')
2460        line[res - 1] = '\0';
2461     else if (len > res)
2462        line[res] = '\0';
2463     else {
2464        line = realloc(line, res + 1);
2465        line[res] = '\0';
2466     }
2467    
2468     if (!strncmp(line, "setup", 5)) {
2469        if (strstr(line, "--force-lba")) {
2470            *lbaPtr = 1;
2471        } else {
2472            *lbaPtr = 0;
2473        }
2474        dbgPrintf("lba: %i\n", *lbaPtr);
2475        break;
2476     }
2477        }
2478    
2479        free(line);
2480        fclose(grubConf);
2481        return 0;
2482    }
2483    
2484    int suseGrubConfGetInstallDevice(const char * path, char ** devicePtr) {
2485        FILE * grubConf;
2486        char * line = NULL;
2487        size_t res = 0, len = 0;
2488        char * lastParamPtr = NULL;
2489        char * secLastParamPtr = NULL;
2490        char installDeviceNumber = '\0';
2491        char * bounds = NULL;
2492    
2493        if (!path) return 1;
2494        if (!devicePtr) return 1;
2495    
2496        grubConf = fopen(path, "r");
2497        if (!grubConf) return 1;
2498    
2499        while ((res = getline(&line, &len, grubConf)) != -1) {
2500     if (strncmp(line, "setup", 5))
2501        continue;
2502    
2503     if (line[res - 1] == '\n')
2504        line[res - 1] = '\0';
2505     else if (len > res)
2506        line[res] = '\0';
2507     else {
2508        line = realloc(line, res + 1);
2509        line[res] = '\0';
2510     }
2511    
2512     lastParamPtr = bounds = line + res;
2513    
2514     /* Last parameter in grub may be an optional IMAGE_DEVICE */
2515     while (!isspace(*lastParamPtr))
2516        lastParamPtr--;
2517     lastParamPtr++;
2518    
2519     secLastParamPtr = lastParamPtr - 2;
2520     dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2521    
2522     if (lastParamPtr + 3 > bounds) {
2523        dbgPrintf("lastParamPtr going over boundary");
2524        fclose(grubConf);
2525        free(line);
2526        return 1;
2527     }
2528     if (!strncmp(lastParamPtr, "(hd", 3))
2529        lastParamPtr += 3;
2530     dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2531    
2532     /*
2533     * Second last parameter will decide wether last parameter is
2534     * an IMAGE_DEVICE or INSTALL_DEVICE
2535     */
2536     while (!isspace(*secLastParamPtr))
2537        secLastParamPtr--;
2538     secLastParamPtr++;
2539    
2540     if (secLastParamPtr + 3 > bounds) {
2541        dbgPrintf("secLastParamPtr going over boundary");
2542        fclose(grubConf);
2543        free(line);
2544        return 1;
2545     }
2546     dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2547     if (!strncmp(secLastParamPtr, "(hd", 3)) {
2548        secLastParamPtr += 3;
2549        dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
2550        installDeviceNumber = *secLastParamPtr;
2551     } else {
2552        installDeviceNumber = *lastParamPtr;
2553     }
2554    
2555     *devicePtr = malloc(6);
2556     snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
2557     dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
2558     fclose(grubConf);
2559     free(line);
2560     return 0;
2561        }
2562    
2563        free(line);
2564        fclose(grubConf);
2565        return 1;
2566    }
2567    
2568    int grubGetBootFromDeviceMap(const char * device,
2569         char ** bootPtr) {
2570        FILE * deviceMap;
2571        char * line = NULL;
2572        size_t res = 0, len = 0;
2573        char * devicePtr;
2574        char * bounds = NULL;
2575        const char * path;
2576        const static char default_path[] = "/boot/grub/device.map";
2577    
2578        if (!device) return 1;
2579        if (!bootPtr) return 1;
2580    
2581        if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2582     path = default_path;
2583    
2584        dbgPrintf("opening grub device.map file from: %s\n", path);
2585        deviceMap = fopen(path, "r");
2586        if (!deviceMap)
2587     return 1;
2588    
2589        while ((res = getline(&line, &len, deviceMap)) != -1) {
2590            if (!strncmp(line, "#", 1))
2591        continue;
2592    
2593     if (line[res - 1] == '\n')
2594        line[res - 1] = '\0';
2595     else if (len > res)
2596        line[res] = '\0';
2597     else {
2598        line = realloc(line, res + 1);
2599        line[res] = '\0';
2600     }
2601    
2602     devicePtr = line;
2603     bounds = line + res;
2604    
2605     while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2606        devicePtr++;
2607     dbgPrintf("device: %s\n", devicePtr);
2608    
2609     if (!strncmp(devicePtr, device, strlen(device))) {
2610        devicePtr += strlen(device);
2611        while (isspace(*devicePtr) && ((devicePtr + 1) <= bounds))
2612            devicePtr++;
2613    
2614        *bootPtr = strdup(devicePtr);
2615        break;
2616     }
2617        }
2618    
2619        free(line);
2620        fclose(deviceMap);
2621        return 0;
2622    }
2623    
2624    int suseGrubConfGetBoot(const char * path, char ** bootPtr) {
2625        char * grubDevice;
2626    
2627        if (suseGrubConfGetInstallDevice(path, &grubDevice))
2628     dbgPrintf("error looking for grub installation device\n");
2629        else
2630     dbgPrintf("grubby installation device: %s\n", grubDevice);
2631    
2632        if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
2633     dbgPrintf("error looking for grub boot device\n");
2634        else
2635     dbgPrintf("grubby boot device: %s\n", *bootPtr);
2636    
2637        free(grubDevice);
2638        return 0;
2639    }
2640    
2641    int parseSuseGrubConf(int * lbaPtr, char ** bootPtr) {
2642        /*
2643         * This SuSE grub configuration file at this location is not your average
2644         * grub configuration file, but instead the grub commands used to setup
2645         * grub on that system.
2646         */
2647        const char * path;
2648        const static char default_path[] = "/etc/grub.conf";
2649    
2650        if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
2651     path = default_path;
2652    
2653        if (!isSuseGrubConf(path)) return 1;
2654    
2655        if (lbaPtr) {
2656            *lbaPtr = 0;
2657            if (suseGrubConfGetLba(path, lbaPtr))
2658                return 1;
2659        }
2660    
2661        if (bootPtr) {
2662            *bootPtr = NULL;
2663            suseGrubConfGetBoot(path, bootPtr);
2664        }
2665    
2666        return 0;
2667  }  }
2668    
2669  int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {  int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
# Line 1369  int parseSysconfigGrub(int * lbaPtr, cha Line 2673  int parseSysconfigGrub(int * lbaPtr, cha
2673      char * start;      char * start;
2674      char * param;      char * param;
2675    
2676      in = fopen("/etc/sysconfig/grub", "r");      in = fopen("/etc/conf.d/grub", "r");
2677      if (!in) return 1;      if (!in) return 1;
2678    
2679      if (lbaPtr) *lbaPtr = 0;      if (lbaPtr) *lbaPtr = 0;
# Line 1410  int parseSysconfigGrub(int * lbaPtr, cha Line 2714  int parseSysconfigGrub(int * lbaPtr, cha
2714  }  }
2715    
2716  void dumpSysconfigGrub(void) {  void dumpSysconfigGrub(void) {
2717      char * boot;      char * boot = NULL;
2718      int lba;      int lba;
2719    
2720      if (!parseSysconfigGrub(&lba, &boot)) {      if (isSuseSystem()) {
2721   if (lba) printf("lba\n");          if (parseSuseGrubConf(&lba, &boot)) {
2722   if (boot) printf("boot=%s\n", boot);      free(boot);
2723        return;
2724     }
2725        } else {
2726            if (parseSysconfigGrub(&lba, &boot)) {
2727        free(boot);
2728        return;
2729     }
2730        }
2731    
2732        if (lba) printf("lba\n");
2733        if (boot) {
2734     printf("boot=%s\n", boot);
2735     free(boot);
2736      }      }
2737  }  }
2738    
# Line 1431  int displayInfo(struct grubConfig * conf Line 2748  int displayInfo(struct grubConfig * conf
2748   return 1;   return 1;
2749      }      }
2750    
2751      /* this is a horrible hack to support /etc/sysconfig/grub; there must      /* this is a horrible hack to support /etc/conf.d/grub; there must
2752         be a better way */         be a better way */
2753      if (config->cfi == &grubConfigType) {      if (config->cfi == &grubConfigType) {
2754   dumpSysconfigGrub();   dumpSysconfigGrub();
2755      } else {      } else {
2756   line = config->theLines;   line = getLineByType(LT_BOOT, config->theLines);
  while (line && line->type != LT_BOOT) line = line->next;  
2757   if (line && line->numElements >= 1) {   if (line && line->numElements >= 1) {
2758      printf("boot=%s\n", line->elements[1].item);      printf("boot=%s\n", line->elements[1].item);
2759   }   }
2760    
2761   line = config->theLines;   line = getLineByType(LT_LBA, config->theLines);
  while (line && line->type != LT_LBA) line = line->next;  
2762   if (line) printf("lba\n");   if (line) printf("lba\n");
2763      }      }
2764    
# Line 1458  int displayInfo(struct grubConfig * conf Line 2773  int displayInfo(struct grubConfig * conf
2773      return 0;      return 0;
2774  }  }
2775    
2776    struct singleLine * addLineTmpl(struct singleEntry * entry,
2777     struct singleLine * tmplLine,
2778     struct singleLine * prevLine,
2779     const char * val,
2780     struct configFileInfo * cfi)
2781    {
2782        struct singleLine * newLine = lineDup(tmplLine);
2783    
2784        if (isEfi && cfi == &grub2ConfigType) {
2785     enum lineType_e old = newLine->type;
2786     newLine->type = preferredLineType(newLine->type, cfi);
2787     if (old != newLine->type)
2788        newLine->elements[0].item = getKeyByType(newLine->type, cfi);
2789        }
2790    
2791        if (val) {
2792     /* override the inherited value with our own.
2793     * This is a little weak because it only applies to elements[1]
2794     */
2795     if (newLine->numElements > 1)
2796        removeElement(newLine, 1);
2797     insertElement(newLine, val, 1, cfi);
2798    
2799     /* but try to keep the rootspec from the template... sigh */
2800     if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD|LT_KERNEL_EFI|LT_INITRD_EFI|LT_KERNEL_16|LT_INITRD_16)) {
2801        char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
2802        if (rootspec != NULL) {
2803     free(newLine->elements[1].item);
2804     newLine->elements[1].item =
2805        sdupprintf("%s%s", rootspec, val);
2806        }
2807     }
2808        }
2809    
2810        dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
2811          newLine->elements[0].item : "");
2812    
2813        if (!entry->lines) {
2814     /* first one on the list */
2815     entry->lines = newLine;
2816        } else if (prevLine) {
2817     /* add after prevLine */
2818     newLine->next = prevLine->next;
2819     prevLine->next = newLine;
2820        }
2821    
2822        return newLine;
2823    }
2824    
2825  /* val may be NULL */  /* val may be NULL */
2826  struct singleLine *  addLine(struct singleEntry * entry,  struct singleLine *  addLine(struct singleEntry * entry,
2827       struct configFileInfo * cfi,       struct configFileInfo * cfi,
2828       enum lineType_e type, const char * defaultIndent,       enum lineType_e type, char * defaultIndent,
2829       char * val) {       const char * val) {
2830      struct singleLine * line, * prev;      struct singleLine * line, * prev;
2831      int i;      struct keywordTypes * kw;
2832        struct singleLine tmpl;
2833    
2834      for (i = 0; cfi->keywords[i].key; i++)      /* NB: This function shouldn't allocate items on the heap, rather on the
2835   if (cfi->keywords[i].type == type) break;       * stack since it calls addLineTmpl which will make copies.
2836      if (type != LT_TITLE || !cfi->titleBracketed)       */
2837          if (!cfi->keywords[i].key) abort();      if (type == LT_TITLE && cfi->titleBracketed) {
2838     /* we're doing a bracketed title (zipl) */
2839     tmpl.type = type;
2840     tmpl.numElements = 1;
2841     tmpl.elements = alloca(sizeof(*tmpl.elements));
2842     tmpl.elements[0].item = alloca(strlen(val)+3);
2843     sprintf(tmpl.elements[0].item, "[%s]", val);
2844     tmpl.elements[0].indent = "";
2845     val = NULL;
2846        } else if (type == LT_MENUENTRY) {
2847     char *lineend = "--class gnu-linux --class gnu --class os {";
2848     if (!val) {
2849        fprintf(stderr, "Line type LT_MENUENTRY requires a value\n");
2850        abort();
2851     }
2852     kw = getKeywordByType(type, cfi);
2853     if (!kw) {
2854        fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2855        abort();
2856     }
2857     tmpl.indent = "";
2858     tmpl.type = type;
2859     tmpl.numElements = 3;
2860     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2861     tmpl.elements[0].item = kw->key;
2862     tmpl.elements[0].indent = alloca(2);
2863     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2864     tmpl.elements[1].item = (char *)val;
2865     tmpl.elements[1].indent = alloca(2);
2866     sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
2867     tmpl.elements[2].item = alloca(strlen(lineend)+1);
2868     strcpy(tmpl.elements[2].item, lineend);
2869     tmpl.elements[2].indent = "";
2870        } else {
2871     kw = getKeywordByType(type, cfi);
2872     if (!kw) {
2873        fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2874        abort();
2875     }
2876     tmpl.type = type;
2877     tmpl.numElements = val ? 2 : 1;
2878     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2879     tmpl.elements[0].item = kw->key;
2880     tmpl.elements[0].indent = alloca(2);
2881     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2882     if (val) {
2883        tmpl.elements[1].item = (char *)val;
2884        tmpl.elements[1].indent = "";
2885     }
2886        }
2887    
2888      /* The last non-empty line gives us the indention to us and the line      /* The last non-empty line gives us the indention to us and the line
2889         to insert after. Note that comments are considered empty lines, which         to insert after. Note that comments are considered empty lines, which
2890         may not be ideal? If there are no lines or we are looking at the         may not be ideal? If there are no lines or we are looking at the
2891         first line, we use defaultIndent (the first line is normally indented         first line, we use defaultIndent (the first line is normally indented
2892         differently from the rest) */         differently from the rest) */
2893      if (entry->lines) {      for (line = entry->lines, prev = NULL; line; line = line->next) {
2894   line = entry->lines;   if (line->numElements) prev = line;
2895   prev = NULL;   /* fall back on the last line if prev isn't otherwise set */
2896   while (line) {   if (!line->next && !prev) prev = line;
     if (line->numElements) prev = line;  
     line = line->next;  
  }  
  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;  
   
  if (prev == entry->lines)  
     line->indent = strdup(defaultIndent);  
  else  
     line->indent = strdup(prev->indent);  
     } else {  
  line = malloc(sizeof(*line));  
  line->indent = strdup(defaultIndent);  
  line->next = NULL;  
2897      }      }
2898    
2899      if (type != LT_TITLE || !cfi->titleBracketed) {      struct singleLine *menuEntry;
2900          line->type = type;      menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
2901          line->numElements = val ? 2 : 1;      if (tmpl.type == LT_ENTRY_END) {
2902          line->elements = malloc(sizeof(*line->elements) * line->numElements);   if (menuEntry)
2903          line->elements[0].item = strdup(cfi->keywords[i].key);      tmpl.indent = menuEntry->indent;
2904          line->elements[0].indent = malloc(2);   else
2905          line->elements[0].indent[0] = cfi->keywords[i].nextChar;      tmpl.indent = defaultIndent ?: "";
2906          line->elements[0].indent[1] = '\0';      } else if (tmpl.type != LT_MENUENTRY) {
2907             if (menuEntry)
2908          if (val) {      tmpl.indent = "\t";
2909              line->elements[1].item = val;   else if (prev == entry->lines)
2910              line->elements[1].indent = strdup("");      tmpl.indent = defaultIndent ?: "";
2911          }   else
2912      } else {      tmpl.indent = prev->indent;
         /* we're doing the title of a bracketed title (zipl) */  
         line->type = type;  
         line->numElements = 1;  
         line->elements = malloc(sizeof(*line->elements) * line->numElements);  
   
         line->elements[0].item = malloc(strlen(val) + 3);  
         sprintf(line->elements[0].item, "[%s]", val);  
         line->elements[0].indent = strdup("");  
2913      }      }
2914    
2915      return line;      return addLineTmpl(entry, &tmpl, prev, val, cfi);
2916  }  }
2917    
2918  void removeLine(struct singleEntry * entry, struct singleLine * line) {  void removeLine(struct singleEntry * entry, struct singleLine * line) {
# Line 1553  void removeLine(struct singleEntry * ent Line 2937  void removeLine(struct singleEntry * ent
2937      free(line);      free(line);
2938  }  }
2939    
2940    static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi)
2941    {
2942        struct singleLine newLine = {
2943     .indent = tmplLine->indent,
2944     .type = tmplLine->type,
2945     .next = tmplLine->next,
2946        };
2947        int firstQuotedItem = -1;
2948        int quoteLen = 0;
2949        int j;
2950        int element = 0;
2951        char *c;
2952    
2953        c = malloc(strlen(tmplLine->elements[0].item) + 1);
2954        strcpy(c, tmplLine->elements[0].item);
2955        insertElement(&newLine, c, element++, cfi);
2956        free(c);
2957        c = NULL;
2958    
2959        for (j = 1; j < tmplLine->numElements; j++) {
2960     if (firstQuotedItem == -1) {
2961        quoteLen += strlen(tmplLine->elements[j].item);
2962        
2963        if (isquote(tmplLine->elements[j].item[0])) {
2964     firstQuotedItem = j;
2965            quoteLen += strlen(tmplLine->elements[j].indent);
2966        } else {
2967     c = malloc(quoteLen + 1);
2968     strcpy(c, tmplLine->elements[j].item);
2969     insertElement(&newLine, c, element++, cfi);
2970     free(c);
2971     quoteLen = 0;
2972        }
2973     } else {
2974        int itemlen = strlen(tmplLine->elements[j].item);
2975        quoteLen += itemlen;
2976        quoteLen += strlen(tmplLine->elements[j].indent);
2977        
2978        if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
2979     c = malloc(quoteLen + 1);
2980     c[0] = '\0';
2981     for (int i = firstQuotedItem; i < j+1; i++) {
2982        strcat(c, tmplLine->elements[i].item);
2983        strcat(c, tmplLine->elements[i].indent);
2984     }
2985     insertElement(&newLine, c, element++, cfi);
2986     free(c);
2987    
2988     firstQuotedItem = -1;
2989     quoteLen = 0;
2990        }
2991     }
2992        }
2993        while (tmplLine->numElements)
2994     removeElement(tmplLine, 0);
2995        if (tmplLine->elements)
2996     free(tmplLine->elements);
2997    
2998        tmplLine->numElements = newLine.numElements;
2999        tmplLine->elements = newLine.elements;
3000    }
3001    
3002    static void insertElement(struct singleLine * line,
3003      const char * item, int insertHere,
3004      struct configFileInfo * cfi)
3005    {
3006        struct keywordTypes * kw;
3007        char indent[2] = "";
3008    
3009        /* sanity check */
3010        if (insertHere > line->numElements) {
3011     dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",
3012      insertHere, line->numElements);
3013     insertHere = line->numElements;
3014        }
3015    
3016        line->elements = realloc(line->elements, (line->numElements + 1) *
3017         sizeof(*line->elements));
3018        memmove(&line->elements[insertHere+1],
3019        &line->elements[insertHere],
3020        (line->numElements - insertHere) *
3021        sizeof(*line->elements));
3022        line->elements[insertHere].item = strdup(item);
3023    
3024        kw = getKeywordByType(line->type, cfi);
3025    
3026        if (line->numElements == 0) {
3027     indent[0] = '\0';
3028        } else if (insertHere == 0) {
3029     indent[0] = kw->nextChar;
3030        } else if (kw->separatorChar != '\0') {
3031     indent[0] = kw->separatorChar;
3032        } else {
3033     indent[0] = ' ';
3034        }
3035    
3036        if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {
3037     /* move the end-of-line forward */
3038     line->elements[insertHere].indent =
3039        line->elements[insertHere-1].indent;
3040     line->elements[insertHere-1].indent = strdup(indent);
3041        } else {
3042     line->elements[insertHere].indent = strdup(indent);
3043        }
3044    
3045        line->numElements++;
3046    
3047        dbgPrintf("insertElement(%s, '%s%s', %d)\n",
3048          line->elements[0].item,
3049          line->elements[insertHere].item,
3050          line->elements[insertHere].indent,
3051          insertHere);
3052    }
3053    
3054    static void removeElement(struct singleLine * line, int removeHere) {
3055        int i;
3056    
3057        /* sanity check */
3058        if (removeHere >= line->numElements) return;
3059    
3060        dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
3061          removeHere, line->elements[removeHere].item);
3062    
3063        free(line->elements[removeHere].item);
3064    
3065        if (removeHere > 1) {
3066     /* previous argument gets this argument's post-indentation */
3067     free(line->elements[removeHere-1].indent);
3068     line->elements[removeHere-1].indent =
3069        line->elements[removeHere].indent;
3070        } else {
3071     free(line->elements[removeHere].indent);
3072        }
3073    
3074        /* now collapse the array, but don't bother to realloc smaller */
3075        for (i = removeHere; i < line->numElements - 1; i++)
3076     line->elements[i] = line->elements[i + 1];
3077    
3078        line->numElements--;
3079    }
3080    
3081  int argMatch(const char * one, const char * two) {  int argMatch(const char * one, const char * two) {
3082      char * first, * second;      char * first, * second;
3083      char * chptr;      char * chptr;
# Line 1575  int updateActualImage(struct grubConfig Line 3100  int updateActualImage(struct grubConfig
3100      struct singleEntry * entry;      struct singleEntry * entry;
3101      struct singleLine * line, * rootLine;      struct singleLine * line, * rootLine;
3102      int index = 0;      int index = 0;
3103      int i, j, k;      int i, k;
3104      const char ** newArgs, ** oldArgs;      const char ** newArgs, ** oldArgs;
3105      const char ** arg;      const char ** arg;
3106      const char * chptr;      int useKernelArgs, useRoot;
     int useKernelArgs = 0;  
     int useRoot = 0;  
3107      int firstElement;      int firstElement;
3108      int *usedElements, *usedArgs;      int *usedElements;
3109        int doreplace;
3110    
3111      if (!image) return 0;      if (!image) return 0;
3112    
# Line 1609  int updateActualImage(struct grubConfig Line 3133  int updateActualImage(struct grubConfig
3133   }   }
3134      }      }
3135    
     for (i = 0; cfg->cfi->keywords[i].key; i++)  
  if (cfg->cfi->keywords[i].type == LT_KERNELARGS) break;  
3136    
3137      if (cfg->cfi->keywords[i].key)      useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
3138   useKernelArgs = 1;       && (!multibootArgs || cfg->cfi->mbConcatArgs));
3139    
3140      for (i = 0; cfg->cfi->keywords[i].key; i++)      useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
3141   if (cfg->cfi->keywords[i].type == LT_ROOT) break;         && !multibootArgs);
3142    
3143      if (cfg->cfi->keywords[i].key)      for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
  useRoot = 1;  
3144    
3145      k = 0;   if (multibootArgs && !entry->multiboot)
3146      for (arg = newArgs; *arg; arg++)      continue;
3147          k++;  
3148      usedArgs = calloc(k, sizeof(int));   /* Determine where to put the args.  If this config supports
3149     * LT_KERNELARGS, use that.  Otherwise use
3150     * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
3151     */
3152     if (useKernelArgs) {
3153        line = getLineByType(LT_KERNELARGS, entry->lines);
3154        if (!line) {
3155     /* no LT_KERNELARGS, need to add it */
3156     line = addLine(entry, cfg->cfi, LT_KERNELARGS,
3157           cfg->secondaryIndent, NULL);
3158        }
3159        firstElement = 1;
3160    
3161      while ((entry = findEntryByPath(cfg, image, prefix, &index))) {   } else if (multibootArgs) {
3162   index++;      line = getLineByType(LT_HYPER, entry->lines);
3163        if (!line) {
3164     /* a multiboot entry without LT_HYPER? */
3165     continue;
3166        }
3167        firstElement = 2;
3168    
3169   line = entry->lines;   } else {
3170   while (line && line->type != LT_KERNEL) line = line->next;      line = getLineByType(LT_KERNEL|LT_MBMODULE|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3171   if (!line) continue;      if (!line) {
3172   firstElement = 2;   /* no LT_KERNEL or LT_MBMODULE in this entry? */
3173     continue;
3174          if (entry->multiboot && !multibootArgs) {      }
3175              /* first mb module line is the real kernel */      firstElement = 2;
3176              while (line && line->type != LT_MBMODULE) line = line->next;   }
3177              firstElement = 2;  
3178          } else if (useKernelArgs) {   /* handle the elilo case which does:
3179      while (line && line->type != LT_KERNELARGS) line = line->next;   *   append="hypervisor args -- kernel args"
3180      firstElement = 1;   */
3181     if (entry->multiboot && cfg->cfi->mbConcatArgs) {
3182        /* this is a multiboot entry, make sure there's
3183         * -- on the args line
3184         */
3185        for (i = firstElement; i < line->numElements; i++) {
3186     if (!strcmp(line->elements[i].item, "--"))
3187        break;
3188        }
3189        if (i == line->numElements) {
3190     /* assume all existing args are kernel args,
3191     * prepend -- to make it official
3192     */
3193     insertElement(line, "--", firstElement, cfg->cfi);
3194     i = firstElement;
3195        }
3196        if (!multibootArgs) {
3197     /* kernel args start after the -- */
3198     firstElement = i + 1;
3199        }
3200     } else if (cfg->cfi->mbConcatArgs) {
3201        /* this is a non-multiboot entry, remove hyper args */
3202        for (i = firstElement; i < line->numElements; i++) {
3203     if (!strcmp(line->elements[i].item, "--"))
3204        break;
3205        }
3206        if (i < line->numElements) {
3207     /* remove args up to -- */
3208     while (strcmp(line->elements[firstElement].item, "--"))
3209        removeElement(line, firstElement);
3210     /* remove -- */
3211     removeElement(line, firstElement);
3212        }
3213   }   }
3214    
3215   if (!line && useKernelArgs) {          usedElements = calloc(line->numElements, sizeof(*usedElements));
     /* no append in there, need to add it */  
     line = addLine(entry, cfg->cfi, LT_KERNELARGS, NULL, NULL);  
  }  
3216    
3217          usedElements = calloc(line->numElements, sizeof(int));   for (k = 0, arg = newArgs; *arg; arg++, k++) {
3218    
3219          k = 0;      doreplace = 1;
  for (arg = newArgs; *arg; arg++) {  
             if (usedArgs[k]) {  
                 k++;  
                 continue;  
             }  
3220      for (i = firstElement; i < line->numElements; i++) {      for (i = firstElement; i < line->numElements; i++) {
3221     if (multibootArgs && cfg->cfi->mbConcatArgs &&
3222        !strcmp(line->elements[i].item, "--"))
3223     {
3224        /* reached the end of hyper args, insert here */
3225        doreplace = 0;
3226        break;  
3227     }
3228                  if (usedElements[i])                  if (usedElements[i])
3229                      continue;                      continue;
3230   if (!argMatch(line->elements[i].item, *arg)) {   if (!argMatch(line->elements[i].item, *arg)) {
3231                      usedElements[i]=1;                      usedElements[i]=1;
                     usedArgs[k]=1;  
3232      break;      break;
3233                  }                  }
3234              }              }
     chptr = strchr(*arg, '=');  
3235    
3236      if (i < line->numElements) {      if (i < line->numElements && doreplace) {
3237   /* replace */   /* direct replacement */
3238   free(line->elements[i].item);   free(line->elements[i].item);
3239   line->elements[i].item = strdup(*arg);   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("");  
  }  
3240    
3241   free(rootLine->elements[1].item);      } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
3242   rootLine->elements[1].item = strdup(chptr + 1);   /* root= replacement */
3243      } else {   rootLine = getLineByType(LT_ROOT, entry->lines);
3244   /* append */   if (rootLine) {
3245   line->elements = realloc(line->elements,      free(rootLine->elements[1].item);
3246   (line->numElements + 1) * sizeof(*line->elements));      rootLine->elements[1].item = strdup(*arg + 5);
  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(" ");  
3247   } else {   } else {
3248      /* First thing on this line; treat a bit differently. Note      rootLine = addLine(entry, cfg->cfi, LT_ROOT,
3249         this is only possible if we've added a LT_KERNELARGS         cfg->secondaryIndent, *arg + 5);
        entry */  
     line->elements[line->numElements].indent = strdup("");  
3250   }   }
3251        }
3252    
3253   line->numElements++;      else {
3254     /* insert/append */
3255     insertElement(line, *arg, i, cfg->cfi);
3256     usedElements = realloc(usedElements, line->numElements *
3257           sizeof(*usedElements));
3258     memmove(&usedElements[i + 1], &usedElements[i],
3259     line->numElements - i - 1);
3260     usedElements[i] = 1;
3261    
3262   /* if we updated a root= here even though there is a   /* if we updated a root= here even though there is a
3263     LT_ROOT available we need to remove the LT_ROOT entry     LT_ROOT available we need to remove the LT_ROOT entry
3264     (this will happen if we switch from a device to a label) */     (this will happen if we switch from a device to a label) */
3265   if (useRoot && !strncmp(*arg, "root=", 5)) {   if (useRoot && !strncmp(*arg, "root=", 5)) {
3266      rootLine = entry->lines;      rootLine = getLineByType(LT_ROOT, entry->lines);
3267      while (rootLine && rootLine->type != LT_ROOT)      if (rootLine)
  rootLine = rootLine->next;  
     if (rootLine) {  
3268   removeLine(entry, rootLine);   removeLine(entry, rootLine);
     }  
3269   }   }
3270      }      }
             k++;  
3271   }   }
3272    
3273          free(usedElements);          free(usedElements);
3274    
  /* no arguments to remove (i.e. no append line) */  
  if (!line) continue;  
   
  /* this won't remove an LT_ROOT item properly (but then again,  
    who cares? */  
3275   for (arg = oldArgs; *arg; arg++) {   for (arg = oldArgs; *arg; arg++) {
3276      for (i = firstElement; i < line->numElements; i++)      for (i = firstElement; i < line->numElements; i++) {
3277   if (!argMatch(line->elements[i].item, *arg))   if (multibootArgs && cfg->cfi->mbConcatArgs &&
3278        !strcmp(line->elements[i].item, "--"))
3279        /* reached the end of hyper args, stop here */
3280        break;
3281     if (!argMatch(line->elements[i].item, *arg)) {
3282        removeElement(line, i);
3283      break;      break;
   
     if (i < line->numElements) {  
  /* if this isn't the first argument the previous argument  
    gets this arguments post-indention */  
  if (i > firstElement) {  
     free(line->elements[i - 1].indent);  
     line->elements[i - 1].indent = line->elements[i].indent;  
3284   }   }
3285        }
3286   free(line->elements[i].item);      /* handle removing LT_ROOT line too */
3287        if (useRoot && !strncmp(*arg, "root=", 5)) {
3288   for (j = i + 1; j < line->numElements; j++)   rootLine = getLineByType(LT_ROOT, entry->lines);
3289      line->elements[j - 1] = line->elements[j];   if (rootLine)
3290        removeLine(entry, rootLine);
  line->numElements--;  
3291      }      }
3292   }   }
3293    
# Line 1760  int updateActualImage(struct grubConfig Line 3298  int updateActualImage(struct grubConfig
3298   }   }
3299      }      }
3300    
     free(usedArgs);  
3301      free(newArgs);      free(newArgs);
3302      free(oldArgs);      free(oldArgs);
3303    
# Line 1786  int updateImage(struct grubConfig * cfg, Line 3323  int updateImage(struct grubConfig * cfg,
3323      return rc;      return rc;
3324  }  }
3325    
3326    int addMBInitrd(struct grubConfig * cfg, const char *newMBKernel,
3327     const char * image, const char * prefix, const char * initrd,
3328     const char * title) {
3329        struct singleEntry * entry;
3330        struct singleLine * line, * kernelLine, *endLine = NULL;
3331        int index = 0;
3332    
3333        if (!image) return 0;
3334    
3335        for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3336            kernelLine = getLineByType(LT_MBMODULE, entry->lines);
3337            if (!kernelLine) continue;
3338    
3339     /* if title is supplied, the entry's title must match it. */
3340     if (title) {
3341        char *linetitle;
3342    
3343        line = getLineByType(LT_TITLE|LT_MENUENTRY, entry->lines);
3344        if (!line)
3345     continue;
3346    
3347        linetitle = extractTitle(cfg, line);
3348        if (!linetitle)
3349     continue;
3350        if (strcmp(title, linetitle)) {
3351     free(linetitle);
3352     continue;
3353        }
3354        free(linetitle);
3355     }
3356    
3357            if (prefix) {
3358                int prefixLen = strlen(prefix);
3359                if (!strncmp(initrd, prefix, prefixLen))
3360                    initrd += prefixLen;
3361            }
3362     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3363     if (endLine)
3364        removeLine(entry, endLine);
3365            line = addLine(entry, cfg->cfi, preferredLineType(LT_MBMODULE,cfg->cfi),
3366     kernelLine->indent, initrd);
3367            if (!line)
3368        return 1;
3369     if (endLine) {
3370        line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3371                if (!line)
3372     return 1;
3373     }
3374    
3375            break;
3376        }
3377    
3378        return 0;
3379    }
3380    
3381    int updateInitrd(struct grubConfig * cfg, const char * image,
3382                     const char * prefix, const char * initrd, const char * title) {
3383        struct singleEntry * entry;
3384        struct singleLine * line, * kernelLine, *endLine = NULL;
3385        int index = 0;
3386    
3387        if (!image) return 0;
3388    
3389        for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3390            kernelLine = getLineByType(LT_KERNEL|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3391            if (!kernelLine) continue;
3392    
3393     /* if title is supplied, the entry's title must match it. */
3394     if (title) {
3395        char *linetitle;
3396    
3397        line = getLineByType(LT_TITLE|LT_MENUENTRY, entry->lines);
3398        if (!line)
3399     continue;
3400    
3401        linetitle = extractTitle(cfg, line);
3402        if (!linetitle)
3403     continue;
3404        if (strcmp(title, linetitle)) {
3405     free(linetitle);
3406     continue;
3407        }
3408        free(linetitle);
3409     }
3410    
3411            line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
3412            if (line)
3413                removeLine(entry, line);
3414            if (prefix) {
3415                int prefixLen = strlen(prefix);
3416                if (!strncmp(initrd, prefix, prefixLen))
3417                    initrd += prefixLen;
3418            }
3419     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3420     if (endLine)
3421        removeLine(entry, endLine);
3422     enum lineType_e lt;
3423     switch(kernelLine->type) {
3424        case LT_KERNEL:
3425            lt = LT_INITRD;
3426     break;
3427        case LT_KERNEL_EFI:
3428            lt = LT_INITRD_EFI;
3429     break;
3430        case LT_KERNEL_16:
3431            lt = LT_INITRD_16;
3432     break;
3433        default:
3434            lt = preferredLineType(LT_INITRD, cfg->cfi);
3435     }
3436            line = addLine(entry, cfg->cfi, lt, kernelLine->indent, initrd);
3437            if (!line)
3438        return 1;
3439     if (endLine) {
3440        line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3441                if (!line)
3442     return 1;
3443     }
3444    
3445            break;
3446        }
3447    
3448        return 0;
3449    }
3450    
3451  int checkDeviceBootloader(const char * device, const unsigned char * boot) {  int checkDeviceBootloader(const char * device, const unsigned char * boot) {
3452      int fd;      int fd;
3453      unsigned char bootSect[512];      unsigned char bootSect[512];
# Line 1809  int checkDeviceBootloader(const char * d Line 3471  int checkDeviceBootloader(const char * d
3471      if (memcmp(boot, bootSect, 3))      if (memcmp(boot, bootSect, 3))
3472   return 0;   return 0;
3473    
3474      if (boot[1] == 0xeb) {      if (boot[1] == JMP_SHORT_OPCODE) {
3475   offset = boot[2] + 2;   offset = boot[2] + 2;
3476      } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {      } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3477   offset = (boot[3] << 8) + boot[2] + 2;   offset = (boot[3] << 8) + boot[2] + 2;
3478      } else if (boot[0] == 0xeb) {      } else if (boot[0] == JMP_SHORT_OPCODE) {
3479   offset = boot[1] + 2;        offset = boot[1] + 2;
3480            /*
3481     * it looks like grub, when copying stage1 into the mbr, patches stage1
3482     * right after the JMP location, replacing other instructions such as
3483     * JMPs for NOOPs. So, relax the check a little bit by skipping those
3484     * different bytes.
3485     */
3486          if ((bootSect[offset + 1] == NOOP_OPCODE)
3487      && (bootSect[offset + 2] == NOOP_OPCODE)) {
3488     offset = offset + 3;
3489          }
3490      } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {      } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3491   offset = (boot[2] << 8) + boot[1] + 2;   offset = (boot[2] << 8) + boot[1] + 2;
3492      } else {      } else {
# Line 1956  int checkForLilo(struct grubConfig * con Line 3628  int checkForLilo(struct grubConfig * con
3628      return checkDeviceBootloader(line->elements[1].item, boot);      return checkDeviceBootloader(line->elements[1].item, boot);
3629  }  }
3630    
3631    int checkForGrub2(struct grubConfig * config) {
3632        if (!access("/etc/grub.d/", R_OK))
3633     return 2;
3634    
3635        return 1;
3636    }
3637    
3638  int checkForGrub(struct grubConfig * config) {  int checkForGrub(struct grubConfig * config) {
3639      int fd;      int fd;
3640      unsigned char bootSect[512];      unsigned char bootSect[512];
3641      char * boot;      char * boot;
3642        int onSuse = isSuseSystem();
3643    
3644      if (parseSysconfigGrub(NULL, &boot))  
3645   return 0;      if (onSuse) {
3646     if (parseSuseGrubConf(NULL, &boot))
3647        return 0;
3648        } else {
3649     if (parseSysconfigGrub(NULL, &boot))
3650        return 0;
3651        }
3652    
3653      /* assume grub is not installed -- not an error condition */      /* assume grub is not installed -- not an error condition */
3654      if (!boot)      if (!boot)
# Line 1976  int checkForGrub(struct grubConfig * con Line 3662  int checkForGrub(struct grubConfig * con
3662      if (read(fd, bootSect, 512) != 512) {      if (read(fd, bootSect, 512) != 512) {
3663   fprintf(stderr, _("grubby: unable to read %s: %s\n"),   fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3664   "/boot/grub/stage1", strerror(errno));   "/boot/grub/stage1", strerror(errno));
3665     close(fd);
3666     return 1;
3667        }
3668        close(fd);
3669    
3670        /* The more elaborate checks do not work on SuSE. The checks done
3671         * seem to be reasonble (at least for now), so just return success
3672         */
3673        if (onSuse)
3674     return 2;
3675    
3676        return checkDeviceBootloader(boot, bootSect);
3677    }
3678    
3679    int checkForExtLinux(struct grubConfig * config) {
3680        int fd;
3681        unsigned char bootSect[512];
3682        char * boot;
3683        char executable[] = "/boot/extlinux/extlinux";
3684    
3685        printf("entered: checkForExtLinux()\n");
3686    
3687        if (parseSysconfigGrub(NULL, &boot))
3688     return 0;
3689    
3690        /* assume grub is not installed -- not an error condition */
3691        if (!boot)
3692     return 0;
3693    
3694        fd = open(executable, O_RDONLY);
3695        if (fd < 0)
3696     /* this doesn't exist if grub hasn't been installed */
3697     return 0;
3698    
3699        if (read(fd, bootSect, 512) != 512) {
3700     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3701     executable, strerror(errno));
3702   return 1;   return 1;
3703      }      }
3704      close(fd);      close(fd);
# Line 1983  int checkForGrub(struct grubConfig * con Line 3706  int checkForGrub(struct grubConfig * con
3706      return checkDeviceBootloader(boot, bootSect);      return checkDeviceBootloader(boot, bootSect);
3707  }  }
3708    
3709    int checkForYaboot(struct grubConfig * config) {
3710        /*
3711         * This is a simplistic check that we consider good enough for own puporses
3712         *
3713         * If we were to properly check if yaboot is *installed* we'd need to:
3714         * 1) get the system boot device (LT_BOOT)
3715         * 2) considering it's a raw filesystem, check if the yaboot binary matches
3716         *    the content on the boot device
3717         * 3) if not, copy the binary to a temporary file and run "addnote" on it
3718         * 4) check again if binary and boot device contents match
3719         */
3720        if (!access("/etc/yaboot.conf", R_OK))
3721     return 2;
3722    
3723        return 1;
3724    }
3725    
3726    int checkForElilo(struct grubConfig * config) {
3727        if (!access("/etc/elilo.conf", R_OK))
3728     return 2;
3729    
3730        return 1;
3731    }
3732    
3733  static char * getRootSpecifier(char * str) {  static char * getRootSpecifier(char * str) {
3734      char * idx, * rootspec = NULL;      char * idx, * rootspec = NULL;
3735    
# Line 1994  static char * getRootSpecifier(char * st Line 3741  static char * getRootSpecifier(char * st
3741      return rootspec;      return rootspec;
3742  }  }
3743    
3744    static char * getInitrdVal(struct grubConfig * config,
3745       const char * prefix, struct singleLine *tmplLine,
3746       const char * newKernelInitrd,
3747       const char ** extraInitrds, int extraInitrdCount)
3748    {
3749        char *initrdVal, *end;
3750        int i;
3751        size_t totalSize;
3752        size_t prefixLen;
3753        char separatorChar;
3754    
3755        prefixLen = strlen(prefix);
3756        totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
3757    
3758        for (i = 0; i < extraInitrdCount; i++) {
3759     totalSize += sizeof(separatorChar);
3760     totalSize += strlen(extraInitrds[i]) - prefixLen;
3761        }
3762    
3763        initrdVal = end = malloc(totalSize);
3764    
3765        end = stpcpy (end, newKernelInitrd + prefixLen);
3766    
3767        separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
3768        for (i = 0; i < extraInitrdCount; i++) {
3769     const char *extraInitrd;
3770     int j;
3771    
3772     extraInitrd = extraInitrds[i] + prefixLen;
3773     /* Don't add entries that are already there */
3774     if (tmplLine != NULL) {
3775        for (j = 2; j < tmplLine->numElements; j++)
3776     if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
3777        break;
3778    
3779        if (j != tmplLine->numElements)
3780     continue;
3781     }
3782    
3783     *end++ = separatorChar;
3784     end = stpcpy(end, extraInitrd);
3785        }
3786    
3787        return initrdVal;
3788    }
3789    
3790  int addNewKernel(struct grubConfig * config, struct singleEntry * template,  int addNewKernel(struct grubConfig * config, struct singleEntry * template,
3791           const char * prefix,           const char * prefix,
3792   char * newKernelPath, char * newKernelTitle,   const char * newKernelPath, const char * newKernelTitle,
3793   char * newKernelArgs, char * newKernelInitrd,   const char * newKernelArgs, const char * newKernelInitrd,
3794                   char * newMBKernel, char * newMBKernelArgs) {   const char ** extraInitrds, int extraInitrdCount,
3795                     const char * newMBKernel, const char * newMBKernelArgs,
3796     const char * newDevTreePath) {
3797      struct singleEntry * new;      struct singleEntry * new;
3798      struct singleLine * newLine = NULL, * tmplLine = NULL, * lastLine = NULL;      struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
3799      int needs;      int needs;
     char * indent = NULL;  
     char * rootspec = NULL;  
3800      char * chptr;      char * chptr;
     int i;  
     enum lineType_e type;  
3801    
3802      if (!newKernelPath) return 0;      if (!newKernelPath) return 0;
3803    
# Line 2036  int addNewKernel(struct grubConfig * con Line 3827  int addNewKernel(struct grubConfig * con
3827      config->entries = new;      config->entries = new;
3828    
3829      /* copy/update from the template */      /* copy/update from the template */
3830      needs = KERNEL_KERNEL | KERNEL_INITRD | KERNEL_TITLE;      needs = NEED_KERNEL | NEED_TITLE;
3831        if (newKernelInitrd)
3832     needs |= NEED_INITRD;
3833      if (newMBKernel) {      if (newMBKernel) {
3834          needs |= KERNEL_MB;          needs |= NEED_MB;
3835          new->multiboot = 1;          new->multiboot = 1;
3836      }      }
3837        if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))
3838     needs |= NEED_DEVTREE;
3839    
3840      if (template) {      if (template) {
3841   for (tmplLine = template->lines; tmplLine; tmplLine = tmplLine->next) {   for (masterLine = template->lines;
3842      /* remember the indention level; we may need it for new lines */       masterLine && (tmplLine = lineDup(masterLine));
3843      if (tmplLine->numElements)       lineFree(tmplLine), masterLine = masterLine->next)
3844   indent = tmplLine->indent;   {
3845        dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
3846    
3847      /* skip comments */      /* skip comments */
3848      chptr = tmplLine->indent;      chptr = tmplLine->indent;
3849      while (*chptr && isspace(*chptr)) chptr++;      while (*chptr && isspace(*chptr)) chptr++;
3850      if (*chptr == '#') continue;      if (*chptr == '#') continue;
3851    
3852      /* we don't need an initrd here */      if (iskernel(tmplLine->type) && tmplLine->numElements >= 2) {
3853      if (tmplLine->type == LT_INITRD && !newKernelInitrd) continue;   if (!template->multiboot && (needs & NEED_MB)) {
3854        /* it's not a multiboot template and this is the kernel
3855              if (tmplLine->type == LT_KERNEL &&       * line.  Try to be intelligent about inserting the
3856                  !template->multiboot && (needs & KERNEL_MB)) {       * hypervisor at the same time.
3857                  struct singleLine *l;       */
3858                  needs &= ~ KERNEL_MB;      if (config->cfi->mbHyperFirst) {
3859     /* insert the hypervisor first */
3860                  l = addLine(new, config->cfi, LT_KERNEL,   newLine = addLine(new, config->cfi, LT_HYPER,
3861                                    config->secondaryIndent,    tmplLine->indent,
3862                                    newMBKernel + strlen(prefix));    newMBKernel + strlen(prefix));
3863                     /* set up for adding the kernel line */
3864                  tmplLine = lastLine;   free(tmplLine->indent);
3865                  if (!new->lines) {   tmplLine->indent = strdup(config->secondaryIndent);
3866                      new->lines = l;   needs &= ~NEED_MB;
3867                  } else {      }
3868                      newLine->next = l;      if (needs & NEED_KERNEL) {
3869                      newLine = l;   /* use addLineTmpl to preserve line elements,
3870                  }   * otherwise we could just call addLine.  Unfortunately
3871                  continue;   * this means making some changes to the template
3872              } else if (tmplLine->type == LT_KERNEL &&   * such as the indent change above and the type
3873                         template->multiboot && !new->multiboot) {   * change below.
3874                  continue; /* don't need multiboot kernel here */   */
3875              }   struct keywordTypes * mbm_kw =
3876        getKeywordByType(LT_MBMODULE, config->cfi);
3877      if (!new->lines) {   if (mbm_kw) {
3878   newLine = malloc(sizeof(*newLine));      tmplLine->type = LT_MBMODULE;
3879   new->lines = newLine;      free(tmplLine->elements[0].item);
3880      } else {      tmplLine->elements[0].item = strdup(mbm_kw->key);
3881   newLine->next = malloc(sizeof(*newLine));   }
3882   newLine = newLine->next;   newLine = addLineTmpl(new, tmplLine, newLine,
3883      }        newKernelPath + strlen(prefix), config->cfi);
3884     needs &= ~NEED_KERNEL;
3885        }
3886        if (needs & NEED_MB) { /* !mbHyperFirst */
3887     newLine = addLine(new, config->cfi, LT_HYPER,
3888      config->secondaryIndent,
3889      newMBKernel + strlen(prefix));
3890     needs &= ~NEED_MB;
3891        }
3892     } else if (needs & NEED_KERNEL) {
3893        newLine = addLineTmpl(new, tmplLine, newLine,
3894      newKernelPath + strlen(prefix), config->cfi);
3895        needs &= ~NEED_KERNEL;
3896     }
3897    
3898        } else if (tmplLine->type == LT_HYPER &&
3899           tmplLine->numElements >= 2) {
3900     if (needs & NEED_MB) {
3901        newLine = addLineTmpl(new, tmplLine, newLine,
3902      newMBKernel + strlen(prefix), config->cfi);
3903        needs &= ~NEED_MB;
3904     }
3905    
3906      newLine->indent = strdup(tmplLine->indent);      } else if (tmplLine->type == LT_MBMODULE &&
3907      newLine->next = NULL;         tmplLine->numElements >= 2) {
3908      newLine->type = tmplLine->type;   if (new->multiboot) {
3909      newLine->numElements = tmplLine->numElements;      if (needs & NEED_KERNEL) {
3910      newLine->elements = malloc(sizeof(*newLine->elements) *   newLine = addLineTmpl(new, tmplLine, newLine,
3911      newLine->numElements);        newKernelPath +
3912      for (i = 0; i < newLine->numElements; i++) {        strlen(prefix), config->cfi);
3913   newLine->elements[i].item = strdup(tmplLine->elements[i].item);   needs &= ~NEED_KERNEL;
3914   newLine->elements[i].indent =      } else if (config->cfi->mbInitRdIsModule &&
3915   strdup(tmplLine->elements[i].indent);         (needs & NEED_INITRD)) {
3916      }   char *initrdVal;
3917     initrdVal = getInitrdVal(config, prefix, tmplLine,
3918              lastLine = tmplLine;   newKernelInitrd, extraInitrds,
3919      if (tmplLine->type == LT_KERNEL && tmplLine->numElements >= 2) {   extraInitrdCount);
3920                  char * repl;   newLine = addLineTmpl(new, tmplLine, newLine,
3921                  if (!template->multiboot) {        initrdVal, config->cfi);
3922                      needs &= ~KERNEL_KERNEL;   free(initrdVal);
3923                      repl = newKernelPath;   needs &= ~NEED_INITRD;
3924                  } else {      }
3925                      needs &= ~KERNEL_MB;   } else if (needs & NEED_KERNEL) {
3926                      repl = newMBKernel;      /* template is multi but new is not,
3927                  }       * insert the kernel in the first module slot
3928                  if (new->multiboot && !template->multiboot) {       */
3929                      free(newLine->elements[0].item);      tmplLine->type = preferredLineType(LT_KERNEL, config->cfi);
3930                      newLine->elements[0].item = strdup("module");      free(tmplLine->elements[0].item);
3931                      newLine->type = LT_MBMODULE;      tmplLine->elements[0].item =
3932                  }   strdup(getKeywordByType(tmplLine->type,
3933   free(newLine->elements[1].item);   config->cfi)->key);
3934                  rootspec = getRootSpecifier(tmplLine->elements[1].item);      newLine = addLineTmpl(new, tmplLine, newLine,
3935                  if (rootspec != NULL) {    newKernelPath + strlen(prefix),
3936                      newLine->elements[1].item = sdupprintf("%s%s",    config->cfi);
3937                                                             rootspec,      needs &= ~NEED_KERNEL;
3938                                                             repl +   } else if (needs & NEED_INITRD) {
3939                                                             strlen(prefix));      char *initrdVal;
3940                  } else {      /* template is multi but new is not,
3941                      newLine->elements[1].item = strdup(repl +       * insert the initrd in the second module slot
3942                                                         strlen(prefix));       */
3943                  }      tmplLine->type = preferredLineType(LT_INITRD, config->cfi);
3944              } else if (tmplLine->type == LT_MBMODULE &&      free(tmplLine->elements[0].item);
3945                         tmplLine->numElements >= 2 && (needs & KERNEL_KERNEL)) {      tmplLine->elements[0].item =
3946                  needs &= ~KERNEL_KERNEL;   strdup(getKeywordByType(tmplLine->type,
3947                  if (!new->multiboot && template->multiboot) {   config->cfi)->key);
3948                      free(newLine->elements[0].item);      initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3949                      newLine->elements[0].item = strdup("kernel");      newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3950                      newLine->type = LT_KERNEL;      free(initrdVal);
3951                  }      needs &= ~NEED_INITRD;
3952   free(newLine->elements[1].item);   }
                 rootspec = getRootSpecifier(tmplLine->elements[1].item);  
                 if (rootspec != NULL) {  
                     newLine->elements[1].item = sdupprintf("%s%s",  
                                                            rootspec,  
                                                            newKernelPath +  
                                                            strlen(prefix));  
                 } else {  
                     newLine->elements[1].item = strdup(newKernelPath +  
                                                        strlen(prefix));  
                 }  
     } else if (tmplLine->type == LT_INITRD &&  
     tmplLine->numElements >= 2) {  
  needs &= ~KERNEL_INITRD;  
  free(newLine->elements[1].item);  
                 if (new->multiboot && !template->multiboot) {  
                     free(newLine->elements[0].item);  
                     newLine->elements[0].item = strdup("module");  
                     newLine->type = LT_MBMODULE;  
                 }  
                 rootspec = getRootSpecifier(tmplLine->elements[1].item);  
                 if (rootspec != NULL) {  
                     newLine->elements[1].item = sdupprintf("%s%s",  
                                                            rootspec,  
                                                            newKernelInitrd +  
                                                            strlen(prefix));  
                 } else {  
                     newLine->elements[1].item = strdup(newKernelInitrd +  
                                                        strlen(prefix));  
                 }  
             } else if (tmplLine->type == LT_MBMODULE &&  
                        tmplLine->numElements >= 2 && (needs & KERNEL_INITRD)) {  
  needs &= ~KERNEL_INITRD;  
                 if (!new->multiboot && template->multiboot) {  
                     free(newLine->elements[0].item);  
                     newLine->elements[0].item = strdup("initrd");  
                     newLine->type = LT_INITRD;  
                 }  
  free(newLine->elements[1].item);  
                 rootspec = getRootSpecifier(tmplLine->elements[1].item);  
                 if (rootspec != NULL) {  
                     newLine->elements[1].item = sdupprintf("%s%s",  
                                                            rootspec,  
                                                            newKernelInitrd +  
                                                            strlen(prefix));  
                 } else {  
                     newLine->elements[1].item = strdup(newKernelInitrd +  
                                                        strlen(prefix));  
                 }  
     } else if (tmplLine->type == LT_TITLE &&  
     tmplLine->numElements >= 2) {  
  needs &= ~KERNEL_TITLE;  
3953    
3954   for (i = 1; i < newLine->numElements; i++) {      } else if (isinitrd(tmplLine->type) && tmplLine->numElements >= 2) {
3955      free(newLine->elements[i].item);   if (needs & NEED_INITRD &&
3956      free(newLine->elements[i].indent);      new->multiboot && !template->multiboot &&
3957        config->cfi->mbInitRdIsModule) {
3958        /* make sure we don't insert the module initrd
3959         * before the module kernel... if we don't do it here,
3960         * it will be inserted following the template.
3961         */
3962        if (!needs & NEED_KERNEL) {
3963     char *initrdVal;
3964    
3965     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3966     newLine = addLine(new, config->cfi, LT_MBMODULE,
3967      config->secondaryIndent,
3968      initrdVal);
3969     free(initrdVal);
3970     needs &= ~NEED_INITRD;
3971        }
3972     } else if (needs & NEED_INITRD) {
3973        char *initrdVal;
3974        initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3975        newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3976        free(initrdVal);
3977        needs &= ~NEED_INITRD;
3978   }   }
3979    
3980   newLine->elements[1].item = strdup(newKernelTitle);      } else if (tmplLine->type == LT_MENUENTRY &&
3981   newLine->elements[1].indent = strdup("");         (needs & NEED_TITLE)) {
3982   newLine->numElements = 2;   requote(tmplLine, config->cfi);
3983     char *nkt = malloc(strlen(newKernelTitle)+3);
3984     strcpy(nkt, "'");
3985     strcat(nkt, newKernelTitle);
3986     strcat(nkt, "'");
3987     newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
3988     free(nkt);
3989     needs &= ~NEED_TITLE;
3990      } else if (tmplLine->type == LT_TITLE &&      } else if (tmplLine->type == LT_TITLE &&
3991                         config->cfi->titleBracketed &&         (needs & NEED_TITLE)) {
3992                         tmplLine->numElements == 1) {   if (tmplLine->numElements >= 2) {
3993                  needs &= ~KERNEL_TITLE;      newLine = addLineTmpl(new, tmplLine, newLine,
3994                  free(newLine->elements[0].item);    newKernelTitle, config->cfi);
3995                  free(newLine->elements[0].indent);      needs &= ~NEED_TITLE;
3996                  newLine->elements = malloc(sizeof(*newLine->elements) *   } else if (tmplLine->numElements == 1 &&
3997                                             newLine->numElements);     config->cfi->titleBracketed) {
3998        /* addLineTmpl doesn't handle titleBracketed */
3999                  newLine->elements[0].item = malloc(strlen(newKernelTitle) + 3);      newLine = addLine(new, config->cfi, LT_TITLE,
4000                  sprintf(newLine->elements[0].item, "[%s]", newKernelTitle);        tmplLine->indent, newKernelTitle);
4001                  newLine->elements[0].indent = strdup("");      needs &= ~NEED_TITLE;
4002                  newLine->numElements = 1;   }
4003              }      } else if (tmplLine->type == LT_ECHO) {
4004        requote(tmplLine, config->cfi);
4005        static const char *prefix = "'Loading ";
4006        if (tmplLine->numElements > 1 &&
4007        strstr(tmplLine->elements[1].item, prefix) &&
4008        masterLine->next &&
4009        iskernel(masterLine->next->type)) {
4010     char *newTitle = malloc(strlen(prefix) +
4011     strlen(newKernelTitle) + 2);
4012    
4013     strcpy(newTitle, prefix);
4014     strcat(newTitle, newKernelTitle);
4015     strcat(newTitle, "'");
4016     newLine = addLine(new, config->cfi, LT_ECHO,
4017     tmplLine->indent, newTitle);
4018     free(newTitle);
4019        } else {
4020     /* pass through other lines from the template */
4021     newLine = addLineTmpl(new, tmplLine, newLine, NULL,
4022     config->cfi);
4023        }
4024        } else if (tmplLine->type == LT_DEVTREE &&
4025           tmplLine->numElements == 2 && newDevTreePath) {
4026            newLine = addLineTmpl(new, tmplLine, newLine,
4027          newDevTreePath + strlen(prefix),
4028          config->cfi);
4029     needs &= ~NEED_DEVTREE;
4030        } else if (tmplLine->type == LT_ENTRY_END && needs & NEED_DEVTREE) {
4031     const char *ndtp = newDevTreePath;
4032     if (!strncmp(newDevTreePath, prefix, strlen(prefix)))
4033        ndtp += strlen(prefix);
4034     newLine = addLine(new, config->cfi, LT_DEVTREE,
4035      config->secondaryIndent,
4036      ndtp);
4037     needs &= ~NEED_DEVTREE;
4038     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
4039        } else {
4040     /* pass through other lines from the template */
4041     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
4042        }
4043   }   }
4044    
4045      } else {      } else {
4046   for (i = 0; config->cfi->keywords[i].key; i++) {   /* don't have a template, so start the entry with the
4047      if ((config->cfi->keywords[i].type == config->cfi->entrySeparator) || (config->cfi->keywords[i].type == LT_OTHER))   * appropriate starting line
4048     */
4049     switch (config->cfi->entryStart) {
4050        case LT_KERNEL:
4051        case LT_KERNEL_EFI:
4052        case LT_KERNEL_16:
4053     if (new->multiboot && config->cfi->mbHyperFirst) {
4054        /* fall through to LT_HYPER */
4055     } else {
4056        newLine = addLine(new, config->cfi,
4057              preferredLineType(LT_KERNEL, config->cfi),
4058          config->primaryIndent,
4059          newKernelPath + strlen(prefix));
4060        needs &= ~NEED_KERNEL;
4061        break;
4062     }
4063    
4064        case LT_HYPER:
4065     newLine = addLine(new, config->cfi, LT_HYPER,
4066      config->primaryIndent,
4067      newMBKernel + strlen(prefix));
4068     needs &= ~NEED_MB;
4069   break;   break;
         }  
4070    
4071   switch (config->cfi->keywords[i].type) {      case LT_MENUENTRY: {
4072      case LT_KERNEL:  needs &= ~KERNEL_KERNEL,   char *nkt = malloc(strlen(newKernelTitle)+3);
4073       chptr = newKernelPath + strlen(prefix);   strcpy(nkt, "'");
4074       type = LT_KERNEL; break;   strcat(nkt, newKernelTitle);
4075      case LT_TITLE:   needs &= ~KERNEL_TITLE, chptr = newKernelTitle;   strcat(nkt, "'");
4076       type = LT_TITLE; break;          newLine = addLine(new, config->cfi, LT_MENUENTRY,
4077      default:        config->primaryIndent, nkt);
4078                  /* zipl strikes again */   free(nkt);
4079                  if (config->cfi->titleBracketed) {   needs &= ~NEED_TITLE;
4080                      needs &= ~KERNEL_TITLE;   needs |= NEED_END;
4081                      chptr = newKernelTitle;   break;
4082                      type = LT_TITLE;      }
4083                      break;      case LT_TITLE:
4084                  } else {   if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
4085                      abort();   char * templabel;
4086                  }   int x = 0, y = 0;
4087   }  
4088     templabel = strdup(newKernelTitle);
4089     while( templabel[x]){
4090     if( templabel[x] == ' ' ){
4091     y = x;
4092     while( templabel[y] ){
4093     templabel[y] = templabel[y+1];
4094     y++;
4095     }
4096     }
4097     x++;
4098     }
4099     newLine = addLine(new, config->cfi, LT_TITLE,
4100      config->primaryIndent, templabel);
4101     free(templabel);
4102     }else{
4103     newLine = addLine(new, config->cfi, LT_TITLE,
4104      config->primaryIndent, newKernelTitle);
4105     }
4106     needs &= ~NEED_TITLE;
4107     break;
4108    
4109   newLine = addLine(new, config->cfi, type, config->primaryIndent, chptr);      default:
4110   new->lines = newLine;   abort();
4111     }
4112      }      }
4113    
4114      if (new->multiboot) {      struct singleLine *endLine = NULL;
4115          if (needs & KERNEL_MB)      endLine = getLineByType(LT_ENTRY_END, new->lines);
4116              newLine = addLine(new, config->cfi, LT_KERNEL,      if (endLine) {
4117                                config->secondaryIndent,      removeLine(new, endLine);
4118                                newMBKernel + strlen(prefix));      needs |= NEED_END;
4119          if (needs & KERNEL_KERNEL)      }
4120              newLine = addLine(new, config->cfi, LT_MBMODULE,  
4121                                config->secondaryIndent,      /* add the remainder of the lines, i.e. those that either
4122                                newKernelPath + strlen(prefix));       * weren't present in the template, or in the case of no template,
4123          /* don't need to check for title as it's guaranteed to have been       * all the lines following the entryStart.
4124           * done as we only do multiboot with grub which uses title as       */
4125           * a separator */      if (needs & NEED_TITLE) {
4126          if (needs & KERNEL_INITRD && newKernelInitrd)   newLine = addLine(new, config->cfi, LT_TITLE,
4127              newLine = addLine(new, config->cfi, LT_MBMODULE,    config->secondaryIndent,
4128                                config->secondaryIndent,    newKernelTitle);
4129                                newKernelInitrd + strlen(prefix));   needs &= ~NEED_TITLE;
4130      } else {      }
4131          if (needs & KERNEL_KERNEL)      if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
4132              newLine = addLine(new, config->cfi, LT_KERNEL,   newLine = addLine(new, config->cfi, LT_HYPER,
4133                                config->secondaryIndent,    config->secondaryIndent,
4134                                newKernelPath + strlen(prefix));    newMBKernel + strlen(prefix));
4135          if (needs & KERNEL_TITLE)   needs &= ~NEED_MB;
4136              newLine = addLine(new, config->cfi, LT_TITLE,      }
4137                                config->secondaryIndent,      if (needs & NEED_KERNEL) {
4138                                newKernelTitle);   newLine = addLine(new, config->cfi,
4139          if (needs & KERNEL_INITRD && newKernelInitrd)    (new->multiboot && getKeywordByType(LT_MBMODULE,
4140              newLine = addLine(new, config->cfi, LT_INITRD,        config->cfi))
4141                                config->secondaryIndent,     ? LT_MBMODULE
4142                                newKernelInitrd + strlen(prefix));   : preferredLineType(LT_KERNEL, config->cfi),
4143      config->secondaryIndent,
4144      newKernelPath + strlen(prefix));
4145     needs &= ~NEED_KERNEL;
4146        }
4147        if (needs & NEED_MB) {
4148     newLine = addLine(new, config->cfi, LT_HYPER,
4149      config->secondaryIndent,
4150      newMBKernel + strlen(prefix));
4151     needs &= ~NEED_MB;
4152        }
4153        if (needs & NEED_INITRD) {
4154     char *initrdVal;
4155     initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
4156     newLine = addLine(new, config->cfi,
4157      (new->multiboot && getKeywordByType(LT_MBMODULE,
4158          config->cfi))
4159       ? LT_MBMODULE
4160       : preferredLineType(LT_INITRD, config->cfi),
4161      config->secondaryIndent,
4162      initrdVal);
4163     free(initrdVal);
4164     needs &= ~NEED_INITRD;
4165        }
4166        if (needs & NEED_DEVTREE) {
4167     newLine = addLine(new, config->cfi, LT_DEVTREE,
4168      config->secondaryIndent,
4169      newDevTreePath);
4170     needs &= ~NEED_DEVTREE;
4171        }
4172    
4173        /* NEEDS_END must be last on bootloaders that need it... */
4174        if (needs & NEED_END) {
4175     newLine = addLine(new, config->cfi, LT_ENTRY_END,
4176     config->secondaryIndent, NULL);
4177     needs &= ~NEED_END;
4178        }
4179    
4180        if (needs) {
4181     printf(_("grubby: needs=%d, aborting\n"), needs);
4182     abort();
4183      }      }
4184    
4185      if (updateImage(config, "0", prefix, newKernelArgs, NULL,      if (updateImage(config, "0", prefix, newKernelArgs, NULL,
# Line 2274  int addNewKernel(struct grubConfig * con Line 4188  int addNewKernel(struct grubConfig * con
4188      return 0;      return 0;
4189  }  }
4190    
4191    static void traceback(int signum)
4192    {
4193        void *array[40];
4194        size_t size;
4195    
4196        signal(SIGSEGV, SIG_DFL);
4197        memset(array, '\0', sizeof (array));
4198        size = backtrace(array, 40);
4199    
4200        fprintf(stderr, "grubby received SIGSEGV!  Backtrace (%ld):\n",
4201                (unsigned long)size);
4202        backtrace_symbols_fd(array, size, STDERR_FILENO);
4203        exit(1);
4204    }
4205    
4206  int main(int argc, const char ** argv) {  int main(int argc, const char ** argv) {
4207      poptContext optCon;      poptContext optCon;
4208      char * grubConfig = NULL;      const char * grubConfig = NULL;
4209      char * outputFile = NULL;      char * outputFile = NULL;
4210      int arg = 0;      int arg = 0;
4211      int flags = 0;      int flags = 0;
4212      int badImageOkay = 0;      int badImageOkay = 0;
4213        int configureGrub2 = 0;
4214      int configureLilo = 0, configureELilo = 0, configureGrub = 0;      int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4215      int configureYaboot = 0, configureSilo = 0, configureZipl = 0;      int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4216        int configureExtLinux = 0;
4217      int bootloaderProbe = 0;      int bootloaderProbe = 0;
4218        int extraInitrdCount = 0;
4219      char * updateKernelPath = NULL;      char * updateKernelPath = NULL;
4220      char * newKernelPath = NULL;      char * newKernelPath = NULL;
4221      char * removeKernelPath = NULL;      char * removeKernelPath = NULL;
4222      char * newKernelArgs = NULL;      char * newKernelArgs = NULL;
4223      char * newKernelInitrd = NULL;      char * newKernelInitrd = NULL;
4224      char * newKernelTitle = NULL;      char * newKernelTitle = NULL;
4225      char * newKernelVersion = NULL;      char * newDevTreePath = NULL;
4226      char * newMBKernel = NULL;      char * newMBKernel = NULL;
4227      char * newMBKernelArgs = NULL;      char * newMBKernelArgs = NULL;
4228      char * removeMBKernelArgs = NULL;      char * removeMBKernelArgs = NULL;
# Line 2299  int main(int argc, const char ** argv) { Line 4231  int main(int argc, const char ** argv) {
4231      char * defaultKernel = NULL;      char * defaultKernel = NULL;
4232      char * removeArgs = NULL;      char * removeArgs = NULL;
4233      char * kernelInfo = NULL;      char * kernelInfo = NULL;
4234        char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4235        char * envPath = NULL;
4236      const char * chptr = NULL;      const char * chptr = NULL;
4237      struct configFileInfo * cfi = NULL;      struct configFileInfo * cfi = NULL;
4238      struct grubConfig * config;      struct grubConfig * config;
4239      struct singleEntry * template = NULL;      struct singleEntry * template = NULL;
4240      int copyDefault = 0, makeDefault = 0;      int copyDefault = 0, makeDefault = 0;
4241      int displayDefault = 0;      int displayDefault = 0;
4242        int displayDefaultIndex = 0;
4243        int displayDefaultTitle = 0;
4244        int defaultIndex = -1;
4245      struct poptOption options[] = {      struct poptOption options[] = {
4246   { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,   { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4247      _("add an entry for the specified kernel"), _("kernel-path") },      _("add an entry for the specified kernel"), _("kernel-path") },
# Line 2322  int main(int argc, const char ** argv) { Line 4259  int main(int argc, const char ** argv) {
4259   { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,   { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4260      _("filestystem which contains /boot directory (for testing only)"),      _("filestystem which contains /boot directory (for testing only)"),
4261      _("bootfs") },      _("bootfs") },
4262  #if defined(__i386__) || defined(__x86_64__)  #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4263   { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,   { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4264      _("check if lilo is installed on lilo.conf boot sector") },      _("check which bootloader is installed on boot sector") },
4265  #endif  #endif
4266   { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,   { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4267      _("path to grub config file to update (\"-\" for stdin)"),      _("path to grub config file to update (\"-\" for stdin)"),
# Line 2335  int main(int argc, const char ** argv) { Line 4272  int main(int argc, const char ** argv) {
4272        "the kernel referenced by the default image does not exist, "        "the kernel referenced by the default image does not exist, "
4273        "the first linux entry whose kernel does exist is used as the "        "the first linux entry whose kernel does exist is used as the "
4274        "template"), NULL },        "template"), NULL },
4275     { "debug", 0, 0, &debug, 0,
4276        _("print debugging information for failures") },
4277   { "default-kernel", 0, 0, &displayDefault, 0,   { "default-kernel", 0, 0, &displayDefault, 0,
4278      _("display the path of the default kernel") },      _("display the path of the default kernel") },
4279     { "default-index", 0, 0, &displayDefaultIndex, 0,
4280        _("display the index of the default kernel") },
4281     { "default-title", 0, 0, &displayDefaultTitle, 0,
4282        _("display the title of the default kernel") },
4283     { "devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4284        _("device tree file for new stanza"), _("dtb-path") },
4285     { "devtreedir", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4286        _("device tree directory for new stanza"), _("dtb-path") },
4287   { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,   { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
4288      _("configure elilo bootloader") },      _("configure elilo bootloader") },
4289     { "efi", 0, POPT_ARG_NONE, &isEfi, 0,
4290        _("force grub2 stanzas to use efi") },
4291     { "env", 0, POPT_ARG_STRING, &envPath, 0,
4292        _("path for environment data"),
4293        _("path") },
4294     { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
4295        _("configure extlinux bootloader (from syslinux)") },
4296   { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,   { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
4297      _("configure grub bootloader") },      _("configure grub bootloader") },
4298     { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
4299        _("configure grub2 bootloader") },
4300   { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,   { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
4301      _("display boot information for specified kernel"),      _("display boot information for specified kernel"),
4302      _("kernel-path") },      _("kernel-path") },
4303   { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,   { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
4304      _("initrd image for the new kernel"), _("initrd-path") },      _("initrd image for the new kernel"), _("initrd-path") },
4305     { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
4306        _("auxiliary initrd image for things other than the new kernel"), _("initrd-path") },
4307   { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,   { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
4308      _("configure lilo bootloader") },      _("configure lilo bootloader") },
4309   { "make-default", 0, 0, &makeDefault, 0,   { "make-default", 0, 0, &makeDefault, 0,
# Line 2365  int main(int argc, const char ** argv) { Line 4323  int main(int argc, const char ** argv) {
4323   { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,   { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
4324      _("make the first entry referencing the specified kernel "      _("make the first entry referencing the specified kernel "
4325        "the default"), _("kernel-path") },        "the default"), _("kernel-path") },
4326     { "set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
4327        _("make the given entry index the default entry"),
4328        _("entry-index") },
4329   { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,   { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
4330      _("configure silo bootloader") },      _("configure silo bootloader") },
4331   { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,   { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
# Line 2382  int main(int argc, const char ** argv) { Line 4343  int main(int argc, const char ** argv) {
4343   { 0, 0, 0, 0, 0 }   { 0, 0, 0, 0, 0 }
4344      };      };
4345    
4346        useextlinuxmenu=0;
4347    
4348        signal(SIGSEGV, traceback);
4349    
4350        int i = 0;
4351        for (int j = 1; j < argc; j++)
4352     i += strlen(argv[j]) + 1;
4353        saved_command_line = malloc(i);
4354        if (!saved_command_line) {
4355     fprintf(stderr, "grubby: %m\n");
4356     exit(1);
4357        }
4358        saved_command_line[0] = '\0';
4359        for (int j = 1; j < argc; j++) {
4360     strcat(saved_command_line, argv[j]);
4361     strncat(saved_command_line, j == argc -1 ? "" : " ", 1);
4362        }
4363    
4364      optCon = poptGetContext("grubby", argc, argv, options, 0);      optCon = poptGetContext("grubby", argc, argv, options, 0);
4365      poptReadDefaultConfig(optCon, 1);      poptReadDefaultConfig(optCon, 1);
4366    
# Line 2391  int main(int argc, const char ** argv) { Line 4370  int main(int argc, const char ** argv) {
4370      printf("grubby version %s\n", VERSION);      printf("grubby version %s\n", VERSION);
4371      exit(0);      exit(0);
4372      break;      break;
4373      case 'i':
4374        if (extraInitrdCount < MAX_EXTRA_INITRDS) {
4375         extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
4376        } else {
4377     fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
4378     return 1;
4379        }
4380        break;
4381   }   }
4382      }      }
4383    
# Line 2406  int main(int argc, const char ** argv) { Line 4393  int main(int argc, const char ** argv) {
4393   return 1;   return 1;
4394      }      }
4395    
4396      if ((configureLilo + configureGrub + configureELilo +      if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
4397   configureYaboot + configureSilo + configureZipl) > 1) {   configureYaboot + configureSilo + configureZipl +
4398     configureExtLinux ) > 1) {
4399   fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));   fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
4400   return 1;   return 1;
4401      } else if (bootloaderProbe && grubConfig) {      } else if (bootloaderProbe && grubConfig) {
4402   fprintf(stderr,   fprintf(stderr,
4403      _("grubby: cannot specify config file with --bootloader-probe\n"));      _("grubby: cannot specify config file with --bootloader-probe\n"));
4404   return 1;   return 1;
4405        } else if (configureGrub2) {
4406     cfi = &grub2ConfigType;
4407     if (envPath)
4408        cfi->envFile = envPath;
4409      } else if (configureLilo) {      } else if (configureLilo) {
4410   cfi = &liloConfigType;   cfi = &liloConfigType;
4411      } else if (configureGrub) {      } else if (configureGrub) {
# Line 2426  int main(int argc, const char ** argv) { Line 4418  int main(int argc, const char ** argv) {
4418          cfi = &siloConfigType;          cfi = &siloConfigType;
4419      } else if (configureZipl) {      } else if (configureZipl) {
4420          cfi = &ziplConfigType;          cfi = &ziplConfigType;
4421        } else if (configureExtLinux) {
4422     cfi = &extlinuxConfigType;
4423     useextlinuxmenu=1;
4424      }      }
4425    
4426      if (!cfi) {      if (!cfi) {
4427            if (grub2FindConfig(&grub2ConfigType))
4428        cfi = &grub2ConfigType;
4429     else
4430        #ifdef __ia64__        #ifdef __ia64__
4431   cfi = &eliloConfigType;      cfi = &eliloConfigType;
4432        #elif __powerpc__        #elif __powerpc__
4433   cfi = &yabootConfigType;      cfi = &yabootConfigType;
4434        #elif __sparc__        #elif __sparc__
4435          cfi = &siloConfigType;              cfi = &siloConfigType;
4436        #elif __s390__        #elif __s390__
4437          cfi = &ziplConfigType;              cfi = &ziplConfigType;
4438        #elif __s390x__        #elif __s390x__
4439          cfi = &ziplConfigtype;              cfi = &ziplConfigtype;
4440        #else        #else
4441   cfi = &grubConfigType;      cfi = &grubConfigType;
4442        #endif        #endif
4443      }      }
4444    
4445      if (!grubConfig)      if (!grubConfig) {
4446   grubConfig = cfi->defaultConfig;   if (cfi->findConfig)
4447        grubConfig = cfi->findConfig(cfi);
4448     if (!grubConfig)
4449        grubConfig = cfi->defaultConfig;
4450        }
4451    
4452      if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||      if (bootloaderProbe && (displayDefault || kernelInfo ||
4453    newKernelPath || removeKernelPath || makeDefault ||      newKernelPath || removeKernelPath || makeDefault ||
4454    defaultKernel)) {      defaultKernel || displayDefaultIndex || displayDefaultTitle ||
4455        (defaultIndex >= 0))) {
4456   fprintf(stderr, _("grubby: --bootloader-probe may not be used with "   fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
4457    "specified option"));    "specified option"));
4458   return 1;   return 1;
4459      }      }
4460    
4461      if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||      if ((displayDefault || kernelInfo) && (newKernelPath ||
4462     removeKernelPath)) {     removeKernelPath)) {
4463   fprintf(stderr, _("grubby: --default-kernel and --info may not "   fprintf(stderr, _("grubby: --default-kernel and --info may not "
4464    "be used when adding or removing kernels\n"));    "be used when adding or removing kernels\n"));
# Line 2465  int main(int argc, const char ** argv) { Line 4468  int main(int argc, const char ** argv) {
4468      if (newKernelPath && !newKernelTitle) {      if (newKernelPath && !newKernelTitle) {
4469   fprintf(stderr, _("grubby: kernel title must be specified\n"));   fprintf(stderr, _("grubby: kernel title must be specified\n"));
4470   return 1;   return 1;
4471      } else if (!newKernelPath && (newKernelTitle  || newKernelInitrd ||      } else if (!newKernelPath && (copyDefault ||
4472    newKernelInitrd || copyDefault     ||    (newKernelInitrd && !updateKernelPath)||
4473    makeDefault)) {    makeDefault || extraInitrdCount > 0)) {
4474   fprintf(stderr, _("grubby: kernel path expected\n"));   fprintf(stderr, _("grubby: kernel path expected\n"));
4475   return 1;   return 1;
4476      }      }
# Line 2491  int main(int argc, const char ** argv) { Line 4494  int main(int argc, const char ** argv) {
4494   makeDefault = 1;   makeDefault = 1;
4495   defaultKernel = NULL;   defaultKernel = NULL;
4496      }      }
4497        else if (defaultKernel && (defaultIndex >= 0)) {
4498     fprintf(stderr, _("grubby: --set-default and --set-default-index "
4499      "may not be used together\n"));
4500     return 1;
4501        }
4502    
4503      if (!strcmp(grubConfig, "-") && !outputFile) {      if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
4504   fprintf(stderr, _("grubby: output file must be specified if stdin "   fprintf(stderr, _("grubby: output file must be specified if stdin "
4505   "is used\n"));   "is used\n"));
4506   return 1;   return 1;
4507      }      }
4508    
4509      if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel      if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
4510   && !kernelInfo && !bootloaderProbe && !updateKernelPath   && !kernelInfo && !bootloaderProbe && !updateKernelPath
4511          && !removeMBKernel) {   && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle
4512     && (defaultIndex == -1)) {
4513   fprintf(stderr, _("grubby: no action specified\n"));   fprintf(stderr, _("grubby: no action specified\n"));
4514   return 1;   return 1;
4515      }      }
# Line 2520  int main(int argc, const char ** argv) { Line 4529  int main(int argc, const char ** argv) {
4529   bootPrefix = "";   bootPrefix = "";
4530      }      }
4531    
4532        if (!cfi->mbAllowExtraInitRds &&
4533     extraInitrdCount > 0) {
4534     fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
4535     return 1;
4536        }
4537    
4538      if (bootloaderProbe) {      if (bootloaderProbe) {
4539   int lrc = 0, grc = 0;   int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
4540   struct grubConfig * lconfig, * gconfig;   struct grubConfig * lconfig, * gconfig, * yconfig, * econfig;
4541    
4542     const char *grub2config = grub2FindConfig(&grub2ConfigType);
4543     if (grub2config) {
4544        gconfig = readConfig(grub2config, &grub2ConfigType);
4545        if (!gconfig)
4546     gr2c = 1;
4547        else
4548     gr2c = checkForGrub2(gconfig);
4549     }
4550    
4551   if (!access(grubConfigType.defaultConfig, F_OK)) {   const char *grubconfig = grubFindConfig(&grubConfigType);
4552      gconfig = readConfig(grubConfigType.defaultConfig, &grubConfigType);   if (!access(grubconfig, F_OK)) {
4553        gconfig = readConfig(grubconfig, &grubConfigType);
4554      if (!gconfig)      if (!gconfig)
4555   grc = 1;   grc = 1;
4556      else      else
# Line 2540  int main(int argc, const char ** argv) { Line 4565  int main(int argc, const char ** argv) {
4565   lrc = checkForLilo(lconfig);   lrc = checkForLilo(lconfig);
4566   }   }
4567    
4568   if (lrc == 1 || grc == 1) return 1;   if (!access(eliloConfigType.defaultConfig, F_OK)) {
4569        econfig = readConfig(eliloConfigType.defaultConfig,
4570     &eliloConfigType);
4571        if (!econfig)
4572     erc = 1;
4573        else
4574     erc = checkForElilo(econfig);
4575     }
4576    
4577     if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
4578        lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
4579        if (!lconfig)
4580     extrc = 1;
4581        else
4582     extrc = checkForExtLinux(lconfig);
4583     }
4584    
4585    
4586     if (!access(yabootConfigType.defaultConfig, F_OK)) {
4587        yconfig = readConfig(yabootConfigType.defaultConfig,
4588     &yabootConfigType);
4589        if (!yconfig)
4590     yrc = 1;
4591        else
4592     yrc = checkForYaboot(yconfig);
4593     }
4594    
4595     if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1 ||
4596     erc == 1)
4597        return 1;
4598    
4599   if (lrc == 2) printf("lilo\n");   if (lrc == 2) printf("lilo\n");
4600     if (gr2c == 2) printf("grub2\n");
4601   if (grc == 2) printf("grub\n");   if (grc == 2) printf("grub\n");
4602     if (extrc == 2) printf("extlinux\n");
4603     if (yrc == 2) printf("yaboot\n");
4604     if (erc == 2) printf("elilo\n");
4605    
4606   return 0;   return 0;
4607      }      }
4608    
4609        if (grubConfig == NULL) {
4610     printf("Could not find bootloader configuration file.\n");
4611     exit(1);
4612        }
4613    
4614      config = readConfig(grubConfig, cfi);      config = readConfig(grubConfig, cfi);
4615      if (!config) return 1;      if (!config) return 1;
4616    
# Line 2557  int main(int argc, const char ** argv) { Line 4620  int main(int argc, const char ** argv) {
4620          char * rootspec;          char * rootspec;
4621    
4622   if (config->defaultImage == -1) return 0;   if (config->defaultImage == -1) return 0;
4623     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4624     cfi->defaultIsSaved)
4625        config->defaultImage = 0;
4626   entry = findEntryByIndex(config, config->defaultImage);   entry = findEntryByIndex(config, config->defaultImage);
4627   if (!entry) return 0;   if (!entry) return 0;
4628   if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;   if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
4629    
4630   line = entry->lines;   line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
  while (line && line->type != LT_KERNEL) line = line->next;  
4631   if (!line) return 0;   if (!line) return 0;
4632    
4633          rootspec = getRootSpecifier(line->elements[1].item);          rootspec = getRootSpecifier(line->elements[1].item);
# Line 2570  int main(int argc, const char ** argv) { Line 4635  int main(int argc, const char ** argv) {
4635                 ((rootspec != NULL) ? strlen(rootspec) : 0));                 ((rootspec != NULL) ? strlen(rootspec) : 0));
4636    
4637   return 0;   return 0;
4638    
4639        } else if (displayDefaultTitle) {
4640     struct singleLine * line;
4641     struct singleEntry * entry;
4642    
4643     if (config->defaultImage == -1) return 0;
4644     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4645     cfi->defaultIsSaved)
4646        config->defaultImage = 0;
4647     entry = findEntryByIndex(config, config->defaultImage);
4648     if (!entry) return 0;
4649    
4650     if (!configureGrub2) {
4651      line = getLineByType(LT_TITLE, entry->lines);
4652      if (!line) return 0;
4653      printf("%s\n", line->elements[1].item);
4654    
4655     } else {
4656      char * title;
4657    
4658      dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
4659      line = getLineByType(LT_MENUENTRY, entry->lines);
4660      if (!line) return 0;
4661      title = grub2ExtractTitle(line);
4662      if (title)
4663        printf("%s\n", title);
4664     }
4665     return 0;
4666    
4667        } else if (displayDefaultIndex) {
4668            if (config->defaultImage == -1) return 0;
4669     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4670     cfi->defaultIsSaved)
4671        config->defaultImage = 0;
4672            printf("%i\n", config->defaultImage);
4673            return 0;
4674    
4675      } else if (kernelInfo)      } else if (kernelInfo)
4676   return displayInfo(config, kernelInfo, bootPrefix);   return displayInfo(config, kernelInfo, bootPrefix);
4677    
# Line 2581  int main(int argc, const char ** argv) { Line 4683  int main(int argc, const char ** argv) {
4683      markRemovedImage(config, removeKernelPath, bootPrefix);      markRemovedImage(config, removeKernelPath, bootPrefix);
4684      markRemovedImage(config, removeMBKernel, bootPrefix);      markRemovedImage(config, removeMBKernel, bootPrefix);
4685      setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,      setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
4686      bootPrefix, flags);      bootPrefix, flags, defaultIndex);
4687      setFallbackImage(config, newKernelPath != NULL);      setFallbackImage(config, newKernelPath != NULL);
4688      if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,      if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
4689                      removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;                      removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
4690        if (updateKernelPath && newKernelInitrd) {
4691        if (newMBKernel) {
4692        if (addMBInitrd(config, newMBKernel, updateKernelPath,
4693     bootPrefix, newKernelInitrd,
4694     newKernelTitle))
4695        return 1;
4696        } else {
4697        if (updateInitrd(config, updateKernelPath, bootPrefix,
4698     newKernelInitrd, newKernelTitle))
4699     return 1;
4700        }
4701        }
4702      if (addNewKernel(config, template, bootPrefix, newKernelPath,      if (addNewKernel(config, template, bootPrefix, newKernelPath,
4703                       newKernelTitle, newKernelArgs, newKernelInitrd,                       newKernelTitle, newKernelArgs, newKernelInitrd,
4704                       newMBKernel, newMBKernelArgs)) return 1;                       (const char **)extraInitrds, extraInitrdCount,
4705                         newMBKernel, newMBKernelArgs, newDevTreePath)) return 1;
4706            
4707    
4708      if (numEntries(config) == 0) {      if (numEntries(config) == 0) {
# Line 2597  int main(int argc, const char ** argv) { Line 4712  int main(int argc, const char ** argv) {
4712      }      }
4713    
4714      if (!outputFile)      if (!outputFile)
4715   outputFile = grubConfig;   outputFile = (char *)grubConfig;
4716    
4717      return writeConfig(config, outputFile, bootPrefix);      return writeConfig(config, outputFile, bootPrefix);
4718  }  }

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