Magellan Linux

Diff of /tags/grubby-8_36/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 2963 by niro, Wed Jun 29 14:41:26 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    };
226    
227    struct keywordTypes grub2Keywords[] = {
228        { "menuentry",  LT_MENUENTRY,   ' ' },
229        { "}",          LT_ENTRY_END,   ' ' },
230        { "echo",       LT_ECHO,        ' ' },
231        { "set",        LT_SET_VARIABLE,' ', '=' },
232        { "root",       LT_BOOTROOT,    ' ' },
233        { "default",    LT_DEFAULT,     ' ' },
234        { "fallback",   LT_FALLBACK,    ' ' },
235        { "linux",      LT_KERNEL,      ' ' },
236        { "linuxefi",   LT_KERNEL_EFI,  ' ' },
237        { "linux16",    LT_KERNEL_16,   ' ' },
238        { "initrd",     LT_INITRD,      ' ', ' ' },
239        { "initrdefi",  LT_INITRD_EFI,  ' ', ' ' },
240        { "initrd16",   LT_INITRD_16,   ' ', ' ' },
241        { "module",     LT_MBMODULE,    ' ' },
242        { "kernel",     LT_HYPER,       ' ' },
243        { "devicetree", LT_DEVTREE,  ' ' },
244        { NULL, 0, 0 },
245    };
246    
247    const char *grub2FindConfig(struct configFileInfo *cfi) {
248        static const char *configFiles[] = {
249     "/boot/grub/grub-efi.cfg",
250     "/boot/grub/grub.cfg",
251     NULL
252        };
253        static int i = -1;
254        static const char *grub_cfg = "/boot/grub/grub.cfg";
255        int rc = -1;
256    
257        if (i == -1) {
258     for (i = 0; configFiles[i] != NULL; i++) {
259        dbgPrintf("Checking \"%s\": ", configFiles[i]);
260        if ((rc = access(configFiles[i], R_OK))) {
261     if (errno == EACCES) {
262        printf("Unable to access bootloader configuration file "
263           "\"%s\": %m\n", configFiles[i]);
264        exit(1);
265     }
266     continue;
267        } else {
268     dbgPrintf("found\n");
269     return configFiles[i];
270        }
271     }
272        }
273    
274        /* Ubuntu renames grub2 to grub, so check for the grub.d directory
275         * that isn't in grub1, and if it exists, return the config file path
276         * that they use. */
277        if (configFiles[i] == NULL && !access("/etc/grub.d/", R_OK)) {
278     dbgPrintf("found\n");
279     return grub_cfg;
280        }
281    
282        dbgPrintf("not found\n");
283        return configFiles[i];
284    }
285    
286    /* kind of hacky.  It'll give the first 1024 bytes, ish. */
287    static char *grub2GetEnv(struct configFileInfo *info, char *name)
288    {
289        static char buf[1025];
290        char *s = NULL;
291        char *ret = NULL;
292        char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";
293        int rc = asprintf(&s, "grub-editenv %s list | grep '^%s='", envFile, name);
294    
295        if (rc < 0)
296     return NULL;
297    
298        FILE *f = popen(s, "r");
299        if (!f)
300     goto out;
301    
302        memset(buf, '\0', sizeof (buf));
303        ret = fgets(buf, 1024, f);
304        pclose(f);
305    
306        if (ret) {
307     ret += strlen(name) + 1;
308     ret[strlen(ret) - 1] = '\0';
309        }
310        dbgPrintf("grub2GetEnv(%s): %s\n", name, ret);
311    out:
312        free(s);
313        return ret;
314    }
315    
316    static int sPopCount(const char *s, const char *c)
317    {
318        int ret = 0;
319        if (!s)
320     return -1;
321        for (int i = 0; s[i] != '\0'; i++)
322     for (int j = 0; c[j] != '\0'; j++)
323        if (s[i] == c[j])
324     ret++;
325        return ret;
326    }
327    
328    static char *shellEscape(const char *s)
329    {
330        int l = strlen(s) + sPopCount(s, "'") * 2;
331    
332        char *ret = calloc(l+1, sizeof (*ret));
333        if (!ret)
334     return NULL;
335        for (int i = 0, j = 0; s[i] != '\0'; i++, j++) {
336     if (s[i] == '\'')
337        ret[j++] = '\\';
338     ret[j] = s[i];
339        }
340        return ret;
341    }
342    
343    static void unquote(char *s)
344    {
345        int l = strlen(s);
346    
347        if ((s[l-1] == '\'' && s[0] == '\'') || (s[l-1] == '"' && s[0] == '"')) {
348     memmove(s, s+1, l-2);
349     s[l-2] = '\0';
350        }
351    }
352    
353    static int grub2SetEnv(struct configFileInfo *info, char *name, char *value)
354    {
355        char *s = NULL;
356        int rc = 0;
357        char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";
358    
359        unquote(value);
360        value = shellEscape(value);
361        if (!value)
362        return -1;
363    
364        rc = asprintf(&s, "grub-editenv %s set '%s=%s'", envFile, name, value);
365        free(value);
366        if (rc <0)
367     return -1;
368    
369        dbgPrintf("grub2SetEnv(%s): %s\n", name, s);
370        rc = system(s);
371        free(s);
372        return rc;
373    }
374    
375    /* this is a gigantic hack to avoid clobbering grub2 variables... */
376    static int is_special_grub2_variable(const char *name)
377    {
378        if (!strcmp(name,"\"${next_entry}\""))
379     return 1;
380        if (!strcmp(name,"\"${prev_saved_entry}\""))
381     return 1;
382        return 0;
383    }
384    
385    int sizeOfSingleLine(struct singleLine * line) {
386      int count = 0;
387    
388      for (int i = 0; i < line->numElements; i++) {
389        int indentSize = 0;
390    
391        count = count + strlen(line->elements[i].item);
392    
393        indentSize = strlen(line->elements[i].indent);
394        if (indentSize > 0)
395          count = count + indentSize;
396        else
397          /* be extra safe and add room for whitespaces */
398          count = count + 1;
399      }
400    
401      /* room for trailing terminator */
402      count = count + 1;
403    
404      return count;
405    }
406    
407    static int isquote(char q)
408    {
409        if (q == '\'' || q == '\"')
410     return 1;
411        return 0;
412    }
413    
414    static int iskernel(enum lineType_e type) {
415        return (type == LT_KERNEL || type == LT_KERNEL_EFI || type == LT_KERNEL_16);
416    }
417    
418    static int isinitrd(enum lineType_e type) {
419        return (type == LT_INITRD || type == LT_INITRD_EFI || type == LT_INITRD_16);
420    }
421    
422    char *grub2ExtractTitle(struct singleLine * line) {
423        char * current;
424        char * current_indent;
425        int current_len;
426        int current_indent_len;
427        int i;
428    
429        /* bail out if line does not start with menuentry */
430        if (strcmp(line->elements[0].item, "menuentry"))
431          return NULL;
432    
433        i = 1;
434        current = line->elements[i].item;
435        current_len = strlen(current);
436    
437        /* if second word is quoted, strip the quotes and return single word */
438        if (isquote(*current) && isquote(current[current_len - 1])) {
439     char *tmp;
440    
441     tmp = strdup(current);
442     *(tmp + current_len - 1) = '\0';
443     return ++tmp;
444        }
445    
446        /* if no quotes, return second word verbatim */
447        if (!isquote(*current))
448     return current;
449    
450        /* second element start with a quote, so we have to find the element
451         * whose last character is also quote (assuming it's the closing one) */
452        int resultMaxSize;
453        char * result;
454        
455        resultMaxSize = sizeOfSingleLine(line);
456        result = malloc(resultMaxSize);
457        snprintf(result, resultMaxSize, "%s", ++current);
458        
459        i++;
460        for (; i < line->numElements; ++i) {
461     current = line->elements[i].item;
462     current_len = strlen(current);
463     current_indent = line->elements[i].indent;
464     current_indent_len = strlen(current_indent);
465    
466     strncat(result, current_indent, current_indent_len);
467     if (!isquote(current[current_len-1])) {
468        strncat(result, current, current_len);
469     } else {
470        strncat(result, current, current_len - 1);
471        break;
472     }
473        }
474        return result;
475    }
476    
477    struct configFileInfo grub2ConfigType = {
478        .findConfig = grub2FindConfig,
479        .getEnv = grub2GetEnv,
480        .setEnv = grub2SetEnv,
481        .keywords = grub2Keywords,
482        .defaultIsIndex = 1,
483        .defaultSupportSaved = 1,
484        .defaultIsVariable = 1,
485        .entryStart = LT_MENUENTRY,
486        .entryEnd = LT_ENTRY_END,
487        .titlePosition = 1,
488        .needsBootPrefix = 1,
489        .mbHyperFirst = 1,
490        .mbInitRdIsModule = 1,
491        .mbAllowExtraInitRds = 1,
492  };  };
493    
494  struct keywordTypes yabootKeywords[] = {  struct keywordTypes yabootKeywords[] = {
# Line 142  struct keywordTypes yabootKeywords[] = { Line 522  struct keywordTypes yabootKeywords[] = {
522      { "partition",  LT_GENERIC,    '=' },      { "partition",  LT_GENERIC,    '=' },
523      { "device",    LT_GENERIC,    '=' },      { "device",    LT_GENERIC,    '=' },
524      { "fstype",    LT_GENERIC,    '=' },      { "fstype",    LT_GENERIC,    '=' },
525      { "initrd",    LT_INITRD,    '=' },      { "initrd",    LT_INITRD,    '=', ';' },
526      { "append",    LT_KERNELARGS,  '=' },      { "append",    LT_KERNELARGS,  '=' },
527      { "boot",    LT_BOOT,    '=' },      { "boot",    LT_BOOT,    '=' },
528      { "lba",    LT_LBA,    ' ' },      { "lba",    LT_LBA,    ' ' },
# Line 162  struct keywordTypes liloKeywords[] = { Line 542  struct keywordTypes liloKeywords[] = {
542      { NULL,    0, 0 },      { NULL,    0, 0 },
543  };  };
544    
545    struct keywordTypes eliloKeywords[] = {
546        { "label",    LT_TITLE,    '=' },
547        { "root",    LT_ROOT,    '=' },
548        { "default",    LT_DEFAULT,    '=' },
549        { "image",    LT_KERNEL,    '=' },
550        { "initrd",    LT_INITRD,    '=' },
551        { "append",    LT_KERNELARGS,  '=' },
552        { "vmm",    LT_HYPER,       '=' },
553        { NULL,    0, 0 },
554    };
555    
556  struct keywordTypes siloKeywords[] = {  struct keywordTypes siloKeywords[] = {
557      { "label",    LT_TITLE,    '=' },      { "label",    LT_TITLE,    '=' },
558      { "root",    LT_ROOT,    '=' },      { "root",    LT_ROOT,    '=' },
# Line 183  struct keywordTypes ziplKeywords[] = { Line 574  struct keywordTypes ziplKeywords[] = {
574      { NULL,         0, 0 },      { NULL,         0, 0 },
575  };  };
576    
577    struct keywordTypes extlinuxKeywords[] = {
578        { "label",    LT_TITLE,    ' ' },
579        { "root",    LT_ROOT,    ' ' },
580        { "default",    LT_DEFAULT,    ' ' },
581        { "kernel",    LT_KERNEL,    ' ' },
582        { "initrd",    LT_INITRD,      ' ', ',' },
583        { "append",    LT_KERNELARGS,  ' ' },
584        { "prompt",     LT_UNKNOWN,     ' ' },
585        { NULL,    0, 0 },
586    };
587    int useextlinuxmenu;
588  struct configFileInfo eliloConfigType = {  struct configFileInfo eliloConfigType = {
589      "/boot/efi/EFI/redhat/elilo.conf",    /* defaultConfig */      .defaultConfig = "/boot/efi/EFI/redhat/elilo.conf",
590      liloKeywords,    /* keywords */      .keywords = eliloKeywords,
591      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
592      0,    /* defaultSupportSaved */      .needsBootPrefix = 1,
593      LT_KERNEL,    /* entrySeparator */      .argsInQuotes = 1,
594      1,                    /* needsBootPrefix */      .mbConcatArgs = 1,
     1,    /* argsInQuotes */  
     0,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
595  };  };
596    
597  struct configFileInfo liloConfigType = {  struct configFileInfo liloConfigType = {
598      "/etc/lilo.conf",    /* defaultConfig */      .defaultConfig = "/etc/lilo.conf",
599      liloKeywords,    /* keywords */      .keywords = liloKeywords,
600      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
601      0,    /* defaultSupportSaved */      .argsInQuotes = 1,
602      LT_KERNEL,    /* entrySeparator */      .maxTitleLength = 15,
     0,    /* needsBootPrefix */  
     1,    /* argsInQuotes */  
     15,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
603  };  };
604    
605  struct configFileInfo yabootConfigType = {  struct configFileInfo yabootConfigType = {
606      "/etc/yaboot.conf",    /* defaultConfig */      .defaultConfig = "/etc/yaboot.conf",
607      yabootKeywords,    /* keywords */      .keywords = yabootKeywords,
608      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
609      0,    /* defaultSupportSaved */      .needsBootPrefix = 1,
610      LT_KERNEL,    /* entrySeparator */      .argsInQuotes = 1,
611      1,    /* needsBootPrefix */      .maxTitleLength = 15,
612      1,    /* argsInQuotes */      .mbAllowExtraInitRds = 1,
     15,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
613  };  };
614    
615  struct configFileInfo siloConfigType = {  struct configFileInfo siloConfigType = {
616      "/etc/silo.conf",    /* defaultConfig */      .defaultConfig = "/etc/silo.conf",
617      siloKeywords,    /* keywords */      .keywords = siloKeywords,
618      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
619      0,    /* defaultSupportSaved */      .needsBootPrefix = 1,
620      LT_KERNEL,    /* entrySeparator */      .argsInQuotes = 1,
621      1,    /* needsBootPrefix */      .maxTitleLength = 15,
     1,    /* argsInQuotes */  
     15,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
622  };  };
623    
624  struct configFileInfo ziplConfigType = {  struct configFileInfo ziplConfigType = {
625      "/etc/zipl.conf",    /* defaultConfig */      .defaultConfig = "/etc/zipl.conf",
626      ziplKeywords,    /* keywords */      .keywords = ziplKeywords,
627      0,    /* defaultIsIndex */      .entryStart = LT_TITLE,
628      0,    /* defaultSupportSaved */      .argsInQuotes = 1,
629      LT_TITLE,    /* entrySeparator */      .titleBracketed = 1,
630      0,    /* needsBootPrefix */  };
631      1,    /* argsInQuotes */  
632      15,    /* maxTitleLength */  struct configFileInfo extlinuxConfigType = {
633      1,                                      /* titleBracketed */      .defaultConfig = "/boot/extlinux/extlinux.conf",
634        .keywords = extlinuxKeywords,
635        .caseInsensitive = 1,
636        .entryStart = LT_TITLE,
637        .needsBootPrefix = 1,
638        .maxTitleLength = 255,
639        .mbAllowExtraInitRds = 1,
640        .defaultIsUnquoted = 1,
641  };  };
642    
643  struct grubConfig {  struct grubConfig {
# Line 255  struct grubConfig { Line 652  struct grubConfig {
652      struct configFileInfo * cfi;      struct configFileInfo * cfi;
653  };  };
654    
655    blkid_cache blkid;
656    
657  struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index);  struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index);
658  struct singleEntry * findEntryByPath(struct grubConfig * cfg,  struct singleEntry * findEntryByPath(struct grubConfig * cfg,
659       const char * path, const char * prefix,       const char * path, const char * prefix,
660       int * index);       int * index);
661  static char * strndup(char * from, int len);  struct singleEntry * findEntryByTitle(struct grubConfig * cfg, char *title,
662          int * index);
663  static int readFile(int fd, char ** bufPtr);  static int readFile(int fd, char ** bufPtr);
664  static void lineInit(struct singleLine * line);  static void lineInit(struct singleLine * line);
665    struct singleLine * lineDup(struct singleLine * line);
666  static void lineFree(struct singleLine * line);  static void lineFree(struct singleLine * line);
667  static int lineWrite(FILE * out, struct singleLine * line,  static int lineWrite(FILE * out, struct singleLine * line,
668       struct configFileInfo * cfi);       struct configFileInfo * cfi);
669  static int getNextLine(char ** bufPtr, struct singleLine * line,  static int getNextLine(char ** bufPtr, struct singleLine * line,
670         struct configFileInfo * cfi);         struct configFileInfo * cfi);
671  static char * getRootSpecifier(char * str);  static char * getRootSpecifier(char * str);
672    static void requote(struct singleLine *line, struct configFileInfo * cfi);
673  static char * strndup(char * from, int len) {  static void insertElement(struct singleLine * line,
674      char * to;    const char * item, int insertHere,
675      struct configFileInfo * cfi);
676      to = malloc(len + 1);  static void removeElement(struct singleLine * line, int removeHere);
677      strncpy(to, from, len);  static struct keywordTypes * getKeywordByType(enum lineType_e type,
678      to[len] = '\0';        struct configFileInfo * cfi);
679    static enum lineType_e getTypeByKeyword(char * keyword,
680      return to;   struct configFileInfo * cfi);
681  }  static struct singleLine * getLineByType(enum lineType_e type,
682     struct singleLine * line);
683    static int checkForExtLinux(struct grubConfig * config);
684    struct singleLine * addLineTmpl(struct singleEntry * entry,
685                                    struct singleLine * tmplLine,
686                                    struct singleLine * prevLine,
687                                    const char * val,
688     struct configFileInfo * cfi);
689    struct singleLine *  addLine(struct singleEntry * entry,
690                                 struct configFileInfo * cfi,
691                                 enum lineType_e type, char * defaultIndent,
692                                 const char * val);
693    
694  static char * sdupprintf(const char *format, ...)  static char * sdupprintf(const char *format, ...)
695  #ifdef __GNUC__  #ifdef __GNUC__
# Line 313  static char * sdupprintf(const char *for Line 724  static char * sdupprintf(const char *for
724      return buf;      return buf;
725  }  }
726    
727    static enum lineType_e preferredLineType(enum lineType_e type,
728     struct configFileInfo *cfi) {
729        if (isEfi && cfi == &grub2ConfigType) {
730     switch (type) {
731     case LT_KERNEL:
732        return isEfiOnly ? LT_KERNEL : LT_KERNEL_EFI;
733     case LT_INITRD:
734        return isEfiOnly ? LT_INITRD : LT_INITRD_EFI;
735     default:
736        return type;
737     }
738    #if defined(__i386__) || defined(__x86_64__)
739        } else if (cfi == &grub2ConfigType) {
740     switch (type) {
741     case LT_KERNEL:
742        return LT_KERNEL_16;
743     case LT_INITRD:
744        return LT_INITRD_16;
745     default:
746        return type;
747     }
748    #endif
749        }
750        return type;
751    }
752    
753    static struct keywordTypes * getKeywordByType(enum lineType_e type,
754          struct configFileInfo * cfi) {
755        for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) {
756     if (kw->type == type)
757        return kw;
758        }
759        return NULL;
760    }
761    
762    static char *getKeyByType(enum lineType_e type, struct configFileInfo * cfi) {
763        struct keywordTypes *kt = getKeywordByType(type, cfi);
764        if (kt)
765     return kt->key;
766        return "unknown";
767    }
768    
769    static char * getpathbyspec(char *device) {
770        if (!blkid)
771            blkid_get_cache(&blkid, NULL);
772    
773        return blkid_get_devname(blkid, device, NULL);
774    }
775    
776    static char * getuuidbydev(char *device) {
777        if (!blkid)
778     blkid_get_cache(&blkid, NULL);
779    
780        return blkid_get_tag_value(blkid, "UUID", device);
781    }
782    
783    static enum lineType_e getTypeByKeyword(char * keyword,
784     struct configFileInfo * cfi) {
785        for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) {
786     if (cfi->caseInsensitive) {
787        if (!strcasecmp(keyword, kw->key))
788                    return kw->type;
789     } else {
790        if (!strcmp(keyword, kw->key))
791            return kw->type;
792     }
793        }
794        return LT_UNKNOWN;
795    }
796    
797    static struct singleLine * getLineByType(enum lineType_e type,
798     struct singleLine * line) {
799        dbgPrintf("getLineByType(%d): ", type);
800        for (; line; line = line->next) {
801     dbgPrintf("%d:%s ", line->type,
802      line->numElements ? line->elements[0].item : "(empty)");
803     if (line->type & type) break;
804        }
805        dbgPrintf(line ? "\n" : " (failed)\n");
806        return line;
807    }
808    
809  static int isBracketedTitle(struct singleLine * line) {  static int isBracketedTitle(struct singleLine * line) {
810      if ((*line->elements[0].item == '[') && (line->numElements == 1)) {      if (line->numElements == 1 && *line->elements[0].item == '[') {
811          int len = strlen(line->elements[0].item);          int len = strlen(line->elements[0].item);
812          if (*(line->elements[0].item + len - 1) == ']') {          if (*(line->elements[0].item + len - 1) == ']') {
813              /* FIXME: this is a hack... */              /* FIXME: this is a hack... */
# Line 326  static int isBracketedTitle(struct singl Line 819  static int isBracketedTitle(struct singl
819      return 0;      return 0;
820  }  }
821    
822  /* figure out if this is a entry separator */  static int isEntryStart(struct singleLine * line,
 static int isEntrySeparator(struct singleLine * line,  
823                              struct configFileInfo * cfi) {                              struct configFileInfo * cfi) {
824      if (line->type == LT_WHITESPACE)      return line->type == cfi->entryStart || line->type == LT_OTHER ||
825   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;  
826  }  }
827    
828  /* extract the title from within brackets (for zipl) */  /* extract the title from within brackets (for zipl) */
829  static char * extractTitle(struct singleLine * line) {  static char * extractTitle(struct singleLine * line) {
830      /* bracketed title... let's extract it (leaks a byte) */      /* bracketed title... let's extract it (leaks a byte) */
831      char * title;      char * title = NULL;
832      title = strdup(line->elements[0].item);      if (line->type == LT_TITLE) {
833      title++;   title = strdup(line->elements[0].item);
834      *(title + strlen(title) - 1) = '\0';   title++;
835     *(title + strlen(title) - 1) = '\0';
836        } else if (line->type == LT_MENUENTRY)
837     title = strdup(line->elements[1].item);
838        else
839     return NULL;
840      return title;      return title;
841  }  }
842    
# Line 389  static void lineInit(struct singleLine * Line 878  static void lineInit(struct singleLine *
878      line->next = NULL;      line->next = NULL;
879  }  }
880    
881  static void lineFree(struct singleLine * line) {  struct singleLine * lineDup(struct singleLine * line) {
882      int i;      struct singleLine * newLine = malloc(sizeof(*newLine));
883    
884        newLine->indent = strdup(line->indent);
885        newLine->next = NULL;
886        newLine->type = line->type;
887        newLine->numElements = line->numElements;
888        newLine->elements = malloc(sizeof(*newLine->elements) *
889           newLine->numElements);
890    
891        for (int i = 0; i < newLine->numElements; i++) {
892     newLine->elements[i].indent = strdup(line->elements[i].indent);
893     newLine->elements[i].item = strdup(line->elements[i].item);
894        }
895    
896        return newLine;
897    }
898    
899    static void lineFree(struct singleLine * line) {
900      if (line->indent) free(line->indent);      if (line->indent) free(line->indent);
901    
902      for (i = 0; i < line->numElements; i++) {      for (int i = 0; i < line->numElements; i++) {
903   free(line->elements[i].item);   free(line->elements[i].item);
904   free(line->elements[i].indent);   free(line->elements[i].indent);
905      }      }
# Line 405  static void lineFree(struct singleLine * Line 910  static void lineFree(struct singleLine *
910    
911  static int lineWrite(FILE * out, struct singleLine * line,  static int lineWrite(FILE * out, struct singleLine * line,
912       struct configFileInfo * cfi) {       struct configFileInfo * cfi) {
     int i;  
   
913      if (fprintf(out, "%s", line->indent) == -1) return -1;      if (fprintf(out, "%s", line->indent) == -1) return -1;
914    
915      for (i = 0; i < line->numElements; i++) {      for (int i = 0; i < line->numElements; i++) {
916     /* Need to handle this, because we strip the quotes from
917     * menuentry when read it. */
918     if (line->type == LT_MENUENTRY && i == 1) {
919        if(!isquote(*line->elements[i].item))
920     fprintf(out, "\'%s\'", line->elements[i].item);
921        else
922     fprintf(out, "%s", line->elements[i].item);
923        fprintf(out, "%s", line->elements[i].indent);
924    
925        continue;
926     }
927    
928   if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)   if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)
929      if (fputc('"', out) == EOF) return -1;      if (fputc('"', out) == EOF) return -1;
930    
931   if (fprintf(out, "%s", line->elements[i].item) == -1) return -1;   if (fprintf(out, "%s", line->elements[i].item) == -1) return -1;
932   if (fprintf(out, "%s", line->elements[i].indent) == -1) return -1;   if (i < line->numElements - 1)
933        if (fprintf(out, "%s", line->elements[i].indent) == -1) return -1;
934      }      }
935    
936      if (line->type == LT_KERNELARGS && cfi->argsInQuotes)      if (line->type == LT_KERNELARGS && cfi->argsInQuotes)
# Line 433  static int getNextLine(char ** bufPtr, s Line 949  static int getNextLine(char ** bufPtr, s
949      char * chptr;      char * chptr;
950      int elementsAlloced = 0;      int elementsAlloced = 0;
951      struct lineElement * element;      struct lineElement * element;
     struct keywordTypes * keywords = cfi->keywords;  
952      int first = 1;      int first = 1;
     int i;  
953    
954      lineFree(line);      lineFree(line);
955    
# Line 489  static int getNextLine(char ** bufPtr, s Line 1003  static int getNextLine(char ** bufPtr, s
1003      if (!line->numElements)      if (!line->numElements)
1004   line->type = LT_WHITESPACE;   line->type = LT_WHITESPACE;
1005      else {      else {
1006   for (i = 0; keywords[i].key; i++)   line->type = getTypeByKeyword(line->elements[0].item, cfi);
1007      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;  
               
1008              /* zipl does [title] instead of something reasonable like all              /* zipl does [title] instead of something reasonable like all
1009               * the other boot loaders.  kind of ugly */               * the other boot loaders.  kind of ugly */
1010              if (cfi->titleBracketed && isBracketedTitle(line)) {              if (cfi->titleBracketed && isBracketedTitle(line)) {
# Line 510  static int getNextLine(char ** bufPtr, s Line 1018  static int getNextLine(char ** bufPtr, s
1018      if (*line->elements[0].item == '#') {      if (*line->elements[0].item == '#') {
1019   char * fullLine;   char * fullLine;
1020   int len;   int len;
  int i;  
1021    
1022   len = strlen(line->indent);   len = strlen(line->indent);
1023   for (i = 0; i < line->numElements; i++)   for (int i = 0; i < line->numElements; i++)
1024      len += strlen(line->elements[i].item) +      len += strlen(line->elements[i].item) +
1025     strlen(line->elements[i].indent);     strlen(line->elements[i].indent);
1026    
# Line 522  static int getNextLine(char ** bufPtr, s Line 1029  static int getNextLine(char ** bufPtr, s
1029   free(line->indent);   free(line->indent);
1030   line->indent = fullLine;   line->indent = fullLine;
1031    
1032   for (i = 0; i < line->numElements; i++) {   for (int i = 0; i < line->numElements; i++) {
1033      strcat(fullLine, line->elements[i].item);      strcat(fullLine, line->elements[i].item);
1034      strcat(fullLine, line->elements[i].indent);      strcat(fullLine, line->elements[i].indent);
1035      free(line->elements[i].item);      free(line->elements[i].item);
# Line 532  static int getNextLine(char ** bufPtr, s Line 1039  static int getNextLine(char ** bufPtr, s
1039   line->type = LT_WHITESPACE;   line->type = LT_WHITESPACE;
1040   line->numElements = 0;   line->numElements = 0;
1041      }      }
1042     } else {
1043     struct keywordTypes *kw;
1044    
1045     kw = getKeywordByType(line->type, cfi);
1046    
1047     /* space isn't the only separator, we need to split
1048     * elements up more
1049     */
1050     if (!isspace(kw->separatorChar)) {
1051        char indent[2] = "";
1052        indent[0] = kw->separatorChar;
1053        for (int i = 1; i < line->numElements; i++) {
1054     char *p;
1055     int numNewElements;
1056    
1057     numNewElements = 0;
1058     p = line->elements[i].item;
1059     while (*p != '\0') {
1060     if (*p == kw->separatorChar)
1061     numNewElements++;
1062     p++;
1063     }
1064     if (line->numElements + numNewElements >= elementsAlloced) {
1065     elementsAlloced += numNewElements + 5;
1066     line->elements = realloc(line->elements,
1067        sizeof(*line->elements) * elementsAlloced);
1068     }
1069    
1070     for (int j = line->numElements; j > i; j--) {
1071     line->elements[j + numNewElements] = line->elements[j];
1072     }
1073     line->numElements += numNewElements;
1074    
1075     p = line->elements[i].item;
1076     while (*p != '\0') {
1077    
1078     while (*p != kw->separatorChar && *p != '\0') p++;
1079     if (*p == '\0') {
1080     break;
1081     }
1082    
1083     line->elements[i + 1].indent = line->elements[i].indent;
1084     line->elements[i].indent = strdup(indent);
1085     *p++ = '\0';
1086     i++;
1087     line->elements[i].item = strdup(p);
1088     }
1089        }
1090     }
1091   }   }
1092      }      }
1093    
1094      return 0;      return 0;
1095  }  }
1096    
1097    static int isnumber(const char *s)
1098    {
1099        int i;
1100        for (i = 0; s[i] != '\0'; i++)
1101     if (s[i] < '0' || s[i] > '9')
1102        return 0;
1103        return i;
1104    }
1105    
1106  static struct grubConfig * readConfig(const char * inName,  static struct grubConfig * readConfig(const char * inName,
1107        struct configFileInfo * cfi) {        struct configFileInfo * cfi) {
1108      int in;      int in;
# Line 549  static struct grubConfig * readConfig(co Line 1114  static struct grubConfig * readConfig(co
1114      struct singleLine * last = NULL, * line, * defaultLine = NULL;      struct singleLine * last = NULL, * line, * defaultLine = NULL;
1115      char * end;      char * end;
1116      struct singleEntry * entry = NULL;      struct singleEntry * entry = NULL;
1117      int i, len;      int len;
1118      char * buf;      char * buf;
1119    
1120      if (!strcmp(inName, "-")) {      if (inName == NULL) {
1121            printf("Could not find bootloader configuration\n");
1122            exit(1);
1123        } else if (!strcmp(inName, "-")) {
1124   in = 0;   in = 0;
1125      } else {      } else {
1126   if ((in = open(inName, O_RDONLY)) < 0) {   if ((in = open(inName, O_RDONLY)) < 0) {
# Line 595  static struct grubConfig * readConfig(co Line 1163  static struct grubConfig * readConfig(co
1163      cfg->secondaryIndent = strdup(line->indent);      cfg->secondaryIndent = strdup(line->indent);
1164   }   }
1165    
1166   if (isEntrySeparator(line, cfi)) {   if (isEntryStart(line, cfi) || (cfg->entries && !sawEntry)) {
1167      sawEntry = 1;      sawEntry = 1;
1168      if (!entry) {      if (!entry) {
1169   cfg->entries = malloc(sizeof(*entry));   cfg->entries = malloc(sizeof(*entry));
# Line 611  static struct grubConfig * readConfig(co Line 1179  static struct grubConfig * readConfig(co
1179      entry->next = NULL;      entry->next = NULL;
1180   }   }
1181    
1182   if (line->type == LT_DEFAULT && line->numElements == 2) {   if (line->type == LT_SET_VARIABLE) {
1183      cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;      dbgPrintf("found 'set' command (%d elements): ", line->numElements);
1184      defaultLine = line;      dbgPrintf("%s", line->indent);
1185        for (int i = 0; i < line->numElements; i++)
1186     dbgPrintf("\"%s\"%s", line->elements[i].item, line->elements[i].indent);
1187        dbgPrintf("\n");
1188        struct keywordTypes *kwType = getKeywordByType(LT_DEFAULT, cfi);
1189        if (kwType && line->numElements == 3 &&
1190        !strcmp(line->elements[1].item, kwType->key) &&
1191        !is_special_grub2_variable(line->elements[2].item)) {
1192     dbgPrintf("Line sets default config\n");
1193     cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1194     defaultLine = line;
1195        }
1196    
1197            } else if (iskernel(line->type)) {
1198        /* if by some freak chance this is multiboot and the "module"
1199         * lines came earlier in the template, make sure to use LT_HYPER
1200         * instead of LT_KERNEL now
1201         */
1202        if (entry && entry->multiboot)
1203     line->type = LT_HYPER;
1204    
1205          } else if (line->type == LT_MBMODULE) {          } else if (line->type == LT_MBMODULE) {
1206        /* go back and fix the LT_KERNEL line to indicate LT_HYPER
1207         * instead, now that we know this is a multiboot entry.
1208         * This only applies to grub, but that's the only place we
1209         * should find LT_MBMODULE lines anyway.
1210         */
1211        for (struct singleLine *l = entry->lines; l; l = l->next) {
1212     if (l->type == LT_HYPER)
1213        break;
1214     else if (iskernel(l->type)) {
1215        l->type = LT_HYPER;
1216        break;
1217     }
1218        }
1219              entry->multiboot = 1;              entry->multiboot = 1;
1220    
1221     } else if (line->type == LT_HYPER) {
1222        entry->multiboot = 1;
1223    
1224   } else if (line->type == LT_FALLBACK && line->numElements == 2) {   } else if (line->type == LT_FALLBACK && line->numElements == 2) {
1225      cfg->fallbackImage = strtol(line->elements[1].item, &end, 10);      cfg->fallbackImage = strtol(line->elements[1].item, &end, 10);
1226      if (*end) cfg->fallbackImage = -1;      if (*end) cfg->fallbackImage = -1;
1227   } else if (line->type == LT_TITLE && line->numElements > 1) {  
1228      /* make the title a single argument (undoing our parsing) */   } else if ((line->type == LT_DEFAULT && cfi->defaultIsUnquoted) ||
1229                    (line->type == LT_TITLE && line->numElements > 1)) {
1230        /* make the title/default a single argument (undoing our parsing) */
1231      len = 0;      len = 0;
1232      for (i = 1; i < line->numElements; i++) {      for (int i = 1; i < line->numElements; i++) {
1233   len += strlen(line->elements[i].item);   len += strlen(line->elements[i].item);
1234   len += strlen(line->elements[i].indent);   len += strlen(line->elements[i].indent);
1235      }      }
1236      buf = malloc(len + 1);      buf = malloc(len + 1);
1237      *buf = '\0';      *buf = '\0';
1238    
1239      for (i = 1; i < line->numElements; i++) {      for (int i = 1; i < line->numElements; i++) {
1240   strcat(buf, line->elements[i].item);   strcat(buf, line->elements[i].item);
1241   free(line->elements[i].item);   free(line->elements[i].item);
1242    
# Line 643  static struct grubConfig * readConfig(co Line 1250  static struct grubConfig * readConfig(co
1250      line->elements[line->numElements - 1].indent;      line->elements[line->numElements - 1].indent;
1251      line->elements[1].item = buf;      line->elements[1].item = buf;
1252      line->numElements = 2;      line->numElements = 2;
1253     } else if (line->type == LT_MENUENTRY && line->numElements > 3) {
1254        /* let --remove-kernel="TITLE=what" work */
1255        len = 0;
1256        char *extras;
1257        char *title;
1258    
1259        for (int i = 1; i < line->numElements; i++) {
1260     len += strlen(line->elements[i].item);
1261     len += strlen(line->elements[i].indent);
1262        }
1263        buf = malloc(len + 1);
1264        *buf = '\0';
1265    
1266        /* allocate mem for extra flags. */
1267        extras = malloc(len + 1);
1268        *extras = '\0';
1269    
1270        /* get title. */
1271        for (int i = 0; i < line->numElements; i++) {
1272     if (!strcmp(line->elements[i].item, "menuentry"))
1273        continue;
1274     if (isquote(*line->elements[i].item))
1275        title = line->elements[i].item + 1;
1276     else
1277        title = line->elements[i].item;
1278    
1279     len = strlen(title);
1280            if (isquote(title[len-1])) {
1281        strncat(buf, title,len-1);
1282        break;
1283     } else {
1284        strcat(buf, title);
1285        strcat(buf, line->elements[i].indent);
1286     }
1287        }
1288    
1289        /* get extras */
1290        int count = 0;
1291        for (int i = 0; i < line->numElements; i++) {
1292     if (count >= 2) {
1293        strcat(extras, line->elements[i].item);
1294        strcat(extras, line->elements[i].indent);
1295     }
1296    
1297     if (!strcmp(line->elements[i].item, "menuentry"))
1298        continue;
1299    
1300     /* count ' or ", there should be two in menuentry line. */
1301     if (isquote(*line->elements[i].item))
1302        count++;
1303    
1304     len = strlen(line->elements[i].item);
1305    
1306     if (isquote(line->elements[i].item[len -1]))
1307        count++;
1308    
1309     /* ok, we get the final ' or ", others are extras. */
1310                }
1311        line->elements[1].indent =
1312     line->elements[line->numElements - 2].indent;
1313        line->elements[1].item = buf;
1314        line->elements[2].indent =
1315     line->elements[line->numElements - 2].indent;
1316        line->elements[2].item = extras;
1317        line->numElements = 3;
1318   } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {   } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {
1319      /* Strip off any " which may be present; they'll be put back      /* Strip off any " which may be present; they'll be put back
1320         on write. This is one of the few (the only?) places that grubby         on write. This is one of the few (the only?) places that grubby
# Line 651  static struct grubConfig * readConfig(co Line 1323  static struct grubConfig * readConfig(co
1323      if (line->numElements >= 2) {      if (line->numElements >= 2) {
1324   int last, len;   int last, len;
1325    
1326   if (*line->elements[1].item == '"')   if (isquote(*line->elements[1].item))
1327      memcpy(line->elements[1].item, line->elements[1].item + 1,      memmove(line->elements[1].item, line->elements[1].item + 1,
1328     strlen(line->elements[1].item + 1) + 1);      strlen(line->elements[1].item + 1) + 1);
1329    
1330   last = line->numElements - 1;   last = line->numElements - 1;
1331   len = strlen(line->elements[last].item) - 1;   len = strlen(line->elements[last].item) - 1;
1332   if (line->elements[last].item[len] == '"')   if (isquote(line->elements[last].item[len]))
1333      line->elements[last].item[len] = '\0';      line->elements[last].item[len] = '\0';
1334      }      }
1335     }
1336    
1337     if (line->type == LT_DEFAULT && line->numElements == 2) {
1338        cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1339        defaultLine = line;
1340   }   }
1341    
1342   /* If we find a generic config option which should live at the   /* If we find a generic config option which should live at the
# Line 680  static struct grubConfig * readConfig(co Line 1356  static struct grubConfig * readConfig(co
1356   movedLine = 1;   movedLine = 1;
1357   continue; /* without setting 'last' */   continue; /* without setting 'last' */
1358   }   }
1359    
1360   /* If a second line of whitespace happens after a generic option   /* If a second line of whitespace happens after a generic option
1361     which was moved, drop it. */     which was moved, drop it. */
1362   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 1372  static struct grubConfig * readConfig(co
1372   entry->lines = line;   entry->lines = line;
1373      else      else
1374   last->next = line;   last->next = line;
1375        dbgPrintf("readConfig added %s to %p\n", getKeyByType(line->type, cfi), entry);
1376    
1377        /* we could have seen this outside of an entry... if so, we
1378         * ignore it like any other line we don't grok */
1379        if (line->type == LT_ENTRY_END && sawEntry)
1380     sawEntry = 0;
1381   } else {   } else {
1382      if (!cfg->theLines)      if (!cfg->theLines)
1383   cfg->theLines = line;   cfg->theLines = line;
1384      else {      else
1385   last->next = line;   last->next = line;
1386      }      dbgPrintf("readConfig added %s to cfg\n", getKeyByType(line->type, cfi));
1387   }   }
1388    
1389   last = line;   last = line;
# Line 708  static struct grubConfig * readConfig(co Line 1391  static struct grubConfig * readConfig(co
1391    
1392      free(incoming);      free(incoming);
1393    
1394        dbgPrintf("defaultLine is %s\n", defaultLine ? "set" : "unset");
1395      if (defaultLine) {      if (defaultLine) {
1396   if (cfi->defaultSupportSaved &&          if (defaultLine->numElements > 2 &&
1397        cfi->defaultSupportSaved &&
1398        !strncmp(defaultLine->elements[2].item,"\"${saved_entry}\"", 16)) {
1399     cfg->cfi->defaultIsSaved = 1;
1400     cfg->defaultImage = DEFAULT_SAVED_GRUB2;
1401     if (cfg->cfi->getEnv) {
1402        char *defTitle = cfi->getEnv(cfg->cfi, "saved_entry");
1403        if (defTitle) {
1404     int index = 0;
1405     if (isnumber(defTitle)) {
1406        index = atoi(defTitle);
1407        entry = findEntryByIndex(cfg, index);
1408     } else {
1409        entry = findEntryByTitle(cfg, defTitle, &index);
1410     }
1411     if (entry)
1412        cfg->defaultImage = index;
1413        }
1414     }
1415     } else if (cfi->defaultIsVariable) {
1416        char *value = defaultLine->elements[2].item;
1417        while (*value && (*value == '"' || *value == '\'' ||
1418        *value == ' ' || *value == '\t'))
1419     value++;
1420        cfg->defaultImage = strtol(value, &end, 10);
1421        while (*end && (*end == '"' || *end == '\'' ||
1422        *end == ' ' || *end == '\t'))
1423     end++;
1424        if (*end) cfg->defaultImage = -1;
1425     } else if (cfi->defaultSupportSaved &&
1426   !strncmp(defaultLine->elements[1].item, "saved", 5)) {   !strncmp(defaultLine->elements[1].item, "saved", 5)) {
1427      cfg->defaultImage = DEFAULT_SAVED;      cfg->defaultImage = DEFAULT_SAVED;
1428   } else if (cfi->defaultIsIndex) {   } else if (cfi->defaultIsIndex) {
1429      cfg->defaultImage = strtol(defaultLine->elements[1].item, &end, 10);      cfg->defaultImage = strtol(defaultLine->elements[1].item, &end, 10);
1430      if (*end) cfg->defaultImage = -1;      if (*end) cfg->defaultImage = -1;
1431   } else if (defaultLine->numElements >= 2) {   } else if (defaultLine->numElements >= 2) {
1432      i = 0;      int i = 0;
1433      while ((entry = findEntryByIndex(cfg, i))) {      while ((entry = findEntryByIndex(cfg, i))) {
1434   for (line = entry->lines; line; line = line->next)   for (line = entry->lines; line; line = line->next)
1435      if (line->type == LT_TITLE) break;      if (line->type == LT_TITLE) break;
# Line 730  static struct grubConfig * readConfig(co Line 1443  static struct grubConfig * readConfig(co
1443                                  extractTitle(line))) break;                                  extractTitle(line))) break;
1444                  }                  }
1445   i++;   i++;
1446     entry = NULL;
1447      }      }
1448    
1449      if (entry) cfg->defaultImage = i;      if (entry){
1450            cfg->defaultImage = i;
1451        }else{
1452            cfg->defaultImage = -1;
1453        }
1454   }   }
1455        } else if (cfg->cfi->defaultIsSaved && cfg->cfi->getEnv) {
1456     char *defTitle = cfi->getEnv(cfg->cfi, "saved_entry");
1457     if (defTitle) {
1458        int index = 0;
1459        if (isnumber(defTitle)) {
1460     index = atoi(defTitle);
1461     entry = findEntryByIndex(cfg, index);
1462        } else {
1463     entry = findEntryByTitle(cfg, defTitle, &index);
1464        }
1465        if (entry)
1466     cfg->defaultImage = index;
1467     }
1468        } else {
1469            cfg->defaultImage = 0;
1470      }      }
1471    
1472      return cfg;      return cfg;
# Line 749  static void writeDefault(FILE * out, cha Line 1482  static void writeDefault(FILE * out, cha
1482    
1483      if (cfg->defaultImage == DEFAULT_SAVED)      if (cfg->defaultImage == DEFAULT_SAVED)
1484   fprintf(out, "%sdefault%ssaved\n", indent, separator);   fprintf(out, "%sdefault%ssaved\n", indent, separator);
1485      else if (cfg->defaultImage > -1) {      else if (cfg->cfi->defaultIsSaved) {
1486     fprintf(out, "%sset default=\"${saved_entry}\"\n", indent);
1487     if (cfg->defaultImage >= 0 && cfg->cfi->setEnv) {
1488        char *title;
1489        entry = findEntryByIndex(cfg, cfg->defaultImage);
1490        line = getLineByType(LT_MENUENTRY, entry->lines);
1491        if (!line)
1492     line = getLineByType(LT_TITLE, entry->lines);
1493        if (line) {
1494     title = extractTitle(line);
1495     if (title)
1496        cfg->cfi->setEnv(cfg->cfi, "saved_entry", title);
1497        }
1498     }
1499        } else if (cfg->defaultImage > -1) {
1500   if (cfg->cfi->defaultIsIndex) {   if (cfg->cfi->defaultIsIndex) {
1501      fprintf(out, "%sdefault%s%d\n", indent, separator,      if (cfg->cfi->defaultIsVariable) {
1502      cfg->defaultImage);          fprintf(out, "%sset default=\"%d\"\n", indent,
1503     cfg->defaultImage);
1504        } else {
1505     fprintf(out, "%sdefault%s%d\n", indent, separator,
1506     cfg->defaultImage);
1507        }
1508   } else {   } else {
1509      int image = cfg->defaultImage;      int image = cfg->defaultImage;
1510    
# Line 769  static void writeDefault(FILE * out, cha Line 1521  static void writeDefault(FILE * out, cha
1521    
1522      if (!entry) return;      if (!entry) return;
1523    
1524      line = entry->lines;      line = getLineByType(LT_TITLE, entry->lines);
     while (line && line->type != LT_TITLE) line = line->next;  
1525    
1526      if (line && line->numElements >= 2)      if (line && line->numElements >= 2)
1527   fprintf(out, "%sdefault%s%s\n", indent, separator,   fprintf(out, "%sdefault%s%s\n", indent, separator,
# Line 804  static int writeConfig(struct grubConfig Line 1555  static int writeConfig(struct grubConfig
1555      int rc;      int rc;
1556    
1557      /* most likely the symlink is relative, so change our      /* most likely the symlink is relative, so change our
1558         directory to / */         directory to the dir of the symlink */
1559      rc = chdir("/");      char *dir = strdupa(outName);
1560        rc = chdir(dirname(dir));
1561      do {      do {
1562   buf = alloca(len + 1);   buf = alloca(len + 1);
1563   rc = readlink(outName, buf, len);   rc = readlink(basename(outName), buf, len);
1564   if (rc == len) len += 256;   if (rc == len) len += 256;
1565      } while (rc == len);      } while (rc == len);
1566            
# Line 843  static int writeConfig(struct grubConfig Line 1595  static int writeConfig(struct grubConfig
1595      }      }
1596    
1597      line = cfg->theLines;      line = cfg->theLines;
1598        struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi);
1599      while (line) {      while (line) {
1600   if (line->type == LT_DEFAULT) {          if (line->type == LT_SET_VARIABLE && defaultKw &&
1601     line->numElements == 3 &&
1602     !strcmp(line->elements[1].item, defaultKw->key) &&
1603     !is_special_grub2_variable(line->elements[2].item)) {
1604        writeDefault(out, line->indent, line->elements[0].indent, cfg);
1605        needs &= ~MAIN_DEFAULT;
1606     } else if (line->type == LT_DEFAULT) {
1607      writeDefault(out, line->indent, line->elements[0].indent, cfg);      writeDefault(out, line->indent, line->elements[0].indent, cfg);
1608      needs &= ~MAIN_DEFAULT;      needs &= ~MAIN_DEFAULT;
1609   } else if (line->type == LT_FALLBACK) {   } else if (line->type == LT_FALLBACK) {
# Line 912  static int numEntries(struct grubConfig Line 1671  static int numEntries(struct grubConfig
1671      return i;      return i;
1672  }  }
1673    
1674  int suitableImage(struct singleEntry * entry, const char * bootPrefix,  static char *findDiskForRoot()
1675    int skipRemoved, int flags) {  {
1676      struct singleLine * line;      int fd;
1677      char * fullName;      char buf[65536];
1678      int i;      char *devname;
1679      struct stat sb, sb2;      char *chptr;
1680      char * dev;      int rc;
     char * end;  
     char * rootspec;  
1681    
1682      line = entry->lines;      if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {
1683      while (line && line->type != LT_KERNEL) line = line->next;          fprintf(stderr, "grubby: failed to open %s: %s\n",
1684                        _PATH_MOUNTED, strerror(errno));
1685      if (!line) return 0;          return NULL;
1686      if (skipRemoved && entry->skip) return 0;      }
     if (line->numElements < 2) return 0;  
1687    
1688      if (flags & GRUBBY_BADIMAGE_OKAY) return 1;      rc = read(fd, buf, sizeof(buf) - 1);
1689        if (rc <= 0) {
1690            fprintf(stderr, "grubby: failed to read %s: %s\n",
1691                    _PATH_MOUNTED, strerror(errno));
1692            close(fd);
1693            return NULL;
1694        }
1695        close(fd);
1696        buf[rc] = '\0';
1697        chptr = buf;
1698    
1699        char *foundanswer = NULL;
1700    
1701        while (chptr && chptr != buf+rc) {
1702            devname = chptr;
1703    
1704            /*
1705             * The first column of a mtab entry is the device, but if the entry is a
1706             * special device it won't start with /, so move on to the next line.
1707             */
1708            if (*devname != '/') {
1709                chptr = strchr(chptr, '\n');
1710                if (chptr)
1711                    chptr++;
1712                continue;
1713            }
1714    
1715            /* Seek to the next space */
1716            chptr = strchr(chptr, ' ');
1717            if (!chptr) {
1718                fprintf(stderr, "grubby: error parsing %s: %s\n",
1719                        _PATH_MOUNTED, strerror(errno));
1720                return NULL;
1721            }
1722    
1723            /*
1724             * The second column of a mtab entry is the mount point, we are looking
1725             * for '/' obviously.
1726             */
1727            if (*(++chptr) == '/' && *(++chptr) == ' ') {
1728                /* remember the last / entry in mtab */
1729               foundanswer = devname;
1730            }
1731    
1732            /* Next line */
1733            chptr = strchr(chptr, '\n');
1734            if (chptr)
1735                chptr++;
1736        }
1737    
1738        /* Return the last / entry found */
1739        if (foundanswer) {
1740            chptr = strchr(foundanswer, ' ');
1741            *chptr = '\0';
1742            return strdup(foundanswer);
1743        }
1744    
1745        return NULL;
1746    }
1747    
1748    void printEntry(struct singleEntry * entry, FILE *f) {
1749        int i;
1750        struct singleLine * line;
1751    
1752        for (line = entry->lines; line; line = line->next) {
1753     log_message(f, "DBG: %s", line->indent);
1754     for (i = 0; i < line->numElements; i++) {
1755        /* Need to handle this, because we strip the quotes from
1756         * menuentry when read it. */
1757        if (line->type == LT_MENUENTRY && i == 1) {
1758     if(!isquote(*line->elements[i].item))
1759        log_message(f, "\'%s\'", line->elements[i].item);
1760     else
1761        log_message(f, "%s", line->elements[i].item);
1762     log_message(f, "%s", line->elements[i].indent);
1763    
1764     continue;
1765        }
1766        
1767        log_message(f, "%s%s",
1768        line->elements[i].item, line->elements[i].indent);
1769     }
1770     log_message(f, "\n");
1771        }
1772    }
1773    
1774    void notSuitablePrintf(struct singleEntry * entry, int okay, const char *fmt, ...)
1775    {
1776        static int once;
1777        va_list argp, argq;
1778    
1779        va_start(argp, fmt);
1780    
1781        va_copy(argq, argp);
1782        if (!once) {
1783     log_time(NULL);
1784     log_message(NULL, "command line: %s\n", saved_command_line);
1785        }
1786        log_message(NULL, "DBG: Image entry %s: ", okay ? "succeeded" : "failed");
1787        log_vmessage(NULL, fmt, argq);
1788    
1789        printEntry(entry, NULL);
1790        va_end(argq);
1791    
1792        if (!debug) {
1793     once = 1;
1794         va_end(argp);
1795     return;
1796        }
1797    
1798        if (okay) {
1799     va_end(argp);
1800     return;
1801        }
1802    
1803        if (!once)
1804     log_message(stderr, "DBG: command line: %s\n", saved_command_line);
1805        once = 1;
1806        fprintf(stderr, "DBG: Image entry failed: ");
1807        vfprintf(stderr, fmt, argp);
1808        printEntry(entry, stderr);
1809        va_end(argp);
1810    }
1811    
1812    #define beginswith(s, c) ((s) && (s)[0] == (c))
1813    
1814    static int endswith(const char *s, char c)
1815    {
1816     int slen;
1817    
1818     if (!s || !s[0])
1819     return 0;
1820     slen = strlen(s) - 1;
1821    
1822     return s[slen] == c;
1823    }
1824    
1825    int suitableImage(struct singleEntry * entry, const char * bootPrefix,
1826      int skipRemoved, int flags) {
1827        struct singleLine * line;
1828        char * fullName;
1829        int i;
1830        char * dev;
1831        char * rootspec;
1832        char * rootdev;
1833    
1834        if (skipRemoved && entry->skip) {
1835     notSuitablePrintf(entry, 0, "marked to skip\n");
1836     return 0;
1837        }
1838    
1839        line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
1840        if (!line) {
1841     notSuitablePrintf(entry, 0, "no line found\n");
1842     return 0;
1843        }
1844        if (line->numElements < 2) {
1845     notSuitablePrintf(entry, 0, "line has only %d elements\n",
1846        line->numElements);
1847     return 0;
1848        }
1849    
1850        if (flags & GRUBBY_BADIMAGE_OKAY) {
1851        notSuitablePrintf(entry, 1, "\n");
1852        return 1;
1853        }
1854    
1855      fullName = alloca(strlen(bootPrefix) +      fullName = alloca(strlen(bootPrefix) +
1856        strlen(line->elements[1].item) + 1);        strlen(line->elements[1].item) + 1);
1857      rootspec = getRootSpecifier(line->elements[1].item);      rootspec = getRootSpecifier(line->elements[1].item);
1858      sprintf(fullName, "%s%s", bootPrefix,      int rootspec_offset = rootspec ? strlen(rootspec) : 0;
1859              line->elements[1].item + ((rootspec != NULL) ?      int hasslash = endswith(bootPrefix, '/') ||
1860                                        strlen(rootspec) : 0));       beginswith(line->elements[1].item + rootspec_offset, '/');
1861      if (access(fullName, R_OK)) return 0;      sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/",
1862                line->elements[1].item + rootspec_offset);
1863        if (access(fullName, R_OK)) {
1864     notSuitablePrintf(entry, 0, "access to %s failed\n", fullName);
1865     return 0;
1866        }
1867      for (i = 2; i < line->numElements; i++)      for (i = 2; i < line->numElements; i++)
1868   if (!strncasecmp(line->elements[i].item, "root=", 5)) break;   if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1869      if (i < line->numElements) {      if (i < line->numElements) {
1870   dev = line->elements[i].item + 5;   dev = line->elements[i].item + 5;
1871      } else {      } else {
1872   /* look for a lilo style LT_ROOT line */   /* look for a lilo style LT_ROOT line */
1873   line = entry->lines;   line = getLineByType(LT_ROOT, entry->lines);
  while (line && line->type != LT_ROOT) line = line->next;  
1874    
1875   if (line && line->numElements >= 2) {   if (line && line->numElements >= 2) {
1876      dev = line->elements[1].item;      dev = line->elements[1].item;
1877   } else {   } else {
1878              int type;      /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS.
1879      /* 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.
1880      line = entry->lines;       */
1881        line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines);
             type = ((entry->multiboot) ? LT_MBMODULE : LT_KERNELARGS);  
   
     while (line && line->type != type) line = line->next;  
1882    
1883              /* failed to find one */              /* failed to find one */
1884              if (!line) return 0;              if (!line) {
1885     notSuitablePrintf(entry, 0, "no line found\n");
1886     return 0;
1887                }
1888    
1889      for (i = 1; i < line->numElements; i++)      for (i = 1; i < line->numElements; i++)
1890          if (!strncasecmp(line->elements[i].item, "root=", 5)) break;          if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1891      if (i < line->numElements)      if (i < line->numElements)
1892          dev = line->elements[i].item + 5;          dev = line->elements[i].item + 5;
1893      else {      else {
1894     notSuitablePrintf(entry, 0, "no root= entry found\n");
1895   /* it failed too...  can't find root= */   /* it failed too...  can't find root= */
1896          return 0;          return 0;
1897              }              }
1898   }   }
1899      }      }
1900    
1901      if (!strncmp(dev, "LABEL=", 6)) {      dev = getpathbyspec(dev);
1902   dev += 6;      if (!getpathbyspec(dev)) {
1903            notSuitablePrintf(entry, 0, "can't find blkid entry for %s\n", dev);
1904   /* check which device has this label */          return 0;
1905   dev = get_spec_by_volume_label(dev, &i, &i);      } else
1906   if (!dev) return 0;   dev = getpathbyspec(dev);
1907    
1908        rootdev = findDiskForRoot();
1909        if (!rootdev) {
1910            notSuitablePrintf(entry, 0, "can't find root device\n");
1911     return 0;
1912      }      }
1913    
1914      if (*dev == '/') {      if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {
1915   if (stat(dev, &sb))          notSuitablePrintf(entry, 0, "uuid missing: rootdev %s, dev %s\n",
1916      return 0;   getuuidbydev(rootdev), getuuidbydev(dev));
1917      } else {          free(rootdev);
1918   sb.st_rdev = strtol(dev, &end, 16);          return 0;
1919   if (*end) return 0;      }
1920    
1921        if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
1922            notSuitablePrintf(entry, 0, "uuid mismatch: rootdev %s, dev %s\n",
1923     getuuidbydev(rootdev), getuuidbydev(dev));
1924     free(rootdev);
1925            return 0;
1926      }      }
     stat("/", &sb2);  
1927    
1928      if (sb.st_rdev != sb2.st_dev) return 0;      free(rootdev);
1929        notSuitablePrintf(entry, 1, "\n");
1930    
1931      return 1;      return 1;
1932  }  }
# Line 1024  struct singleEntry * findEntryByPath(str Line 1960  struct singleEntry * findEntryByPath(str
1960   }   }
1961    
1962   indexVars[i + 1] = -1;   indexVars[i + 1] = -1;
1963    
1964   i = 0;   i = 0;
1965   if (index) {   if (index) {
1966      while (i < *index) i++;      while (i < *index) {
1967      if (indexVars[i] == -1) return NULL;   i++;
1968     if (indexVars[i] == -1) return NULL;
1969        }
1970   }   }
1971    
1972   entry = findEntryByIndex(config, indexVars[i]);   entry = findEntryByIndex(config, indexVars[i]);
1973   if (!entry) return NULL;   if (!entry) return NULL;
1974    
1975   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;  
   
1976   if (!line) return NULL;   if (!line) return NULL;
1977    
1978   if (index) *index = indexVars[i];   if (index) *index = indexVars[i];
# Line 1075  struct singleEntry * findEntryByPath(str Line 2010  struct singleEntry * findEntryByPath(str
2010    
2011   if (!strncmp(kernel, "TITLE=", 6)) {   if (!strncmp(kernel, "TITLE=", 6)) {
2012      prefix = "";      prefix = "";
2013      checkType = LT_TITLE;      checkType = LT_TITLE|LT_MENUENTRY;
2014      kernel += 6;      kernel += 6;
2015   }   }
2016    
2017   while ((entry = findEntryByIndex(config, i))) {   for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {
2018      line = entry->lines;      if (entry->skip) continue;
     while (line && line->type != checkType) line=line->next;  
2019    
2020        dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);
2021    
2022      if (line && line->numElements >= 2 && !entry->skip) {      /* check all the lines matching checkType */
2023                  rootspec = getRootSpecifier(line->elements[1].item);      for (line = entry->lines; line; line = line->next) {
2024          if (!strcmp(line->elements[1].item  +   enum lineType_e ct = checkType;
2025                              ((rootspec != NULL) ? strlen(rootspec) : 0),   if (entry->multiboot && checkType == LT_KERNEL)
2026                              kernel + strlen(prefix)))      ct = LT_KERNEL|LT_KERNEL_EFI|LT_MBMODULE|LT_HYPER|LT_KERNEL_16;
2027                      break;   else if (checkType & LT_KERNEL)
2028              }      ct = checkType | LT_KERNEL_EFI | LT_KERNEL_16;
2029                 line = getLineByType(ct, line);
2030              /* have to check multiboot lines too */   if (!line)
2031              if (entry->multiboot) {      break;  /* not found in this entry */
2032                  while (line && line->type != LT_MBMODULE) line = line->next;  
2033                  if (line && line->numElements >= 2 && !entry->skip) {   if (line && line->type != LT_MENUENTRY &&
2034                      rootspec = getRootSpecifier(line->elements[1].item);   line->numElements >= 2) {
2035                      if (!strcmp(line->elements[1].item  +      rootspec = getRootSpecifier(line->elements[1].item);
2036                                  ((rootspec != NULL) ? strlen(rootspec) : 0),      if (!strcmp(line->elements[1].item +
2037                                  kernel + strlen(prefix)))   ((rootspec != NULL) ? strlen(rootspec) : 0),
2038                          break;   kernel + strlen(prefix)))
2039                  }   break;
2040              }   }
2041     if(line->type == LT_MENUENTRY &&
2042     !strcmp(line->elements[1].item, kernel))
2043        break;
2044        }
2045    
2046      i++;      /* make sure this entry has a kernel identifier; this skips
2047         * non-Linux boot entries (could find netbsd etc, though, which is
2048         * unfortunate)
2049         */
2050        if (line && getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines))
2051     break; /* found 'im! */
2052   }   }
2053    
2054   if (index) *index = i;   if (index) *index = i;
2055      }      }
2056    
2057      if (!entry) return NULL;      return entry;
2058    }
2059    
2060    struct singleEntry * findEntryByTitle(struct grubConfig * cfg, char *title,
2061          int * index) {
2062        struct singleEntry * entry;
2063        struct singleLine * line;
2064        int i;
2065        char * newtitle;
2066    
2067      /* make sure this entry has a kernel identifier; this skips non-Linux      for (i = 0, entry = cfg->entries; entry; entry = entry->next, i++) {
2068         boot entries (could find netbsd etc, though, which is unfortunate) */   if (index && i < *index)
2069      line = entry->lines;      continue;
2070      while (line && line->type != LT_KERNEL) line = line->next;   line = getLineByType(LT_TITLE, entry->lines);
2071      if (!line) {   if (!line)
2072   if (!index) index = &i;      line = getLineByType(LT_MENUENTRY, entry->lines);
2073   (*index)++;   if (!line)
2074   return findEntryByPath(config, kernel, prefix, index);      continue;
2075     newtitle = grub2ExtractTitle(line);
2076     if (!newtitle)
2077        continue;
2078     if (!strcmp(title, newtitle))
2079        break;
2080      }      }
2081    
2082        if (!entry)
2083     return NULL;
2084    
2085        if (index)
2086     *index = i;
2087      return entry;      return entry;
2088  }  }
2089    
# Line 1147  struct singleEntry * findTemplate(struct Line 2109  struct singleEntry * findTemplate(struct
2109      struct singleEntry * entry, * entry2;      struct singleEntry * entry, * entry2;
2110      int index;      int index;
2111    
2112      if (cfg->defaultImage > -1) {      if (cfg->cfi->defaultIsSaved) {
2113     if (cfg->cfi->getEnv) {
2114        char *defTitle = cfg->cfi->getEnv(cfg->cfi, "saved_entry");
2115        if (defTitle) {
2116     int index = 0;
2117     if (isnumber(defTitle)) {
2118        index = atoi(defTitle);
2119        entry = findEntryByIndex(cfg, index);
2120     } else {
2121        entry = findEntryByTitle(cfg, defTitle, &index);
2122     }
2123     if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2124        cfg->defaultImage = index;
2125        if (indexPtr)
2126     *indexPtr = index;
2127        return entry;
2128     }
2129        }
2130     }
2131        } else if (cfg->defaultImage > -1) {
2132   entry = findEntryByIndex(cfg, cfg->defaultImage);   entry = findEntryByIndex(cfg, cfg->defaultImage);
2133   if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {   if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2134      if (indexPtr) *indexPtr = cfg->defaultImage;      if (indexPtr) *indexPtr = cfg->defaultImage;
# Line 1200  void markRemovedImage(struct grubConfig Line 2181  void markRemovedImage(struct grubConfig
2181        const char * prefix) {        const char * prefix) {
2182      struct singleEntry * entry;      struct singleEntry * entry;
2183    
2184      if (!image) return;      if (!image)
2185     return;
2186    
2187        /* check and see if we're removing the default image */
2188        if (isdigit(*image)) {
2189     entry = findEntryByPath(cfg, image, prefix, NULL);
2190     if(entry)
2191        entry->skip = 1;
2192     return;
2193        }
2194    
2195      while ((entry = findEntryByPath(cfg, image, prefix, NULL)))      while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
2196   entry->skip = 1;   entry->skip = 1;
# Line 1208  void markRemovedImage(struct grubConfig Line 2198  void markRemovedImage(struct grubConfig
2198    
2199  void setDefaultImage(struct grubConfig * config, int hasNew,  void setDefaultImage(struct grubConfig * config, int hasNew,
2200       const char * defaultKernelPath, int newIsDefault,       const char * defaultKernelPath, int newIsDefault,
2201       const char * prefix, int flags) {       const char * prefix, int flags, int index) {
2202      struct singleEntry * entry, * entry2, * newDefault;      struct singleEntry * entry, * entry2, * newDefault;
2203      int i, j;      int i, j;
2204    
2205      if (newIsDefault) {      if (newIsDefault) {
2206   config->defaultImage = 0;   config->defaultImage = 0;
2207   return;   return;
2208        } else if ((index >= 0) && config->cfi->defaultIsIndex) {
2209     if (findEntryByIndex(config, index))
2210        config->defaultImage = index;
2211     else
2212        config->defaultImage = -1;
2213     return;
2214      } else if (defaultKernelPath) {      } else if (defaultKernelPath) {
2215   i = 0;   i = 0;
2216   if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {   if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
# Line 1227  void setDefaultImage(struct grubConfig * Line 2223  void setDefaultImage(struct grubConfig *
2223    
2224      /* 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
2225         changes */         changes */
2226      if (config->defaultImage == DEFAULT_SAVED)      if ((config->defaultImage == DEFAULT_SAVED) ||
2227     (config->defaultImage == DEFAULT_SAVED_GRUB2))
2228        /* default is set to saved, we don't want to change it */        /* default is set to saved, we don't want to change it */
2229        return;        return;
2230    
# Line 1285  void displayEntry(struct singleEntry * e Line 2282  void displayEntry(struct singleEntry * e
2282      struct singleLine * line;      struct singleLine * line;
2283      char * root = NULL;      char * root = NULL;
2284      int i;      int i;
2285        int j;
     line = entry->lines;  
     while (line && line->type != LT_KERNEL) line = line->next;  
2286    
2287      printf("index=%d\n", index);      printf("index=%d\n", index);
2288    
2289      printf("kernel=%s\n", line->elements[1].item);      line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
2290        if (!line) {
2291            printf("non linux entry\n");
2292            return;
2293        }
2294    
2295        if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2296     printf("kernel=%s\n", line->elements[1].item);
2297        else
2298     printf("kernel=%s%s\n", prefix, line->elements[1].item);
2299    
2300      if (line->numElements >= 3) {      if (line->numElements >= 3) {
2301   printf("args=\"");   printf("args=\"");
# Line 1308  void displayEntry(struct singleEntry * e Line 2312  void displayEntry(struct singleEntry * e
2312   }   }
2313   printf("\"\n");   printf("\"\n");
2314      } else {      } else {
2315   line = entry->lines;   line = getLineByType(LT_KERNELARGS, entry->lines);
  while (line && line->type != LT_KERNELARGS) line=line->next;  
   
2316   if (line) {   if (line) {
2317      char * s;      char * s;
2318    
# Line 1334  void displayEntry(struct singleEntry * e Line 2336  void displayEntry(struct singleEntry * e
2336      }      }
2337    
2338      if (!root) {      if (!root) {
2339   line = entry->lines;   line = getLineByType(LT_ROOT, entry->lines);
  while (line && line->type != LT_ROOT) line = line->next;  
   
2340   if (line && line->numElements >= 2)   if (line && line->numElements >= 2)
2341      root=line->elements[1].item;      root=line->elements[1].item;
2342      }      }
# Line 1351  void displayEntry(struct singleEntry * e Line 2351  void displayEntry(struct singleEntry * e
2351   printf("root=%s\n", s);   printf("root=%s\n", s);
2352      }      }
2353    
2354      line = entry->lines;      line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
     while (line && line->type != LT_INITRD) line = line->next;  
2355    
2356      if (line && line->numElements >= 2) {      if (line && line->numElements >= 2) {
2357   printf("initrd=%s", prefix);   if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2358        printf("initrd=");
2359     else
2360        printf("initrd=%s", prefix);
2361    
2362   for (i = 1; i < line->numElements; i++)   for (i = 1; i < line->numElements; i++)
2363      printf("%s%s", line->elements[i].item, line->elements[i].indent);      printf("%s%s", line->elements[i].item, line->elements[i].indent);
2364   printf("\n");   printf("\n");
2365      }      }
2366    
2367        line = getLineByType(LT_TITLE, entry->lines);
2368        if (line) {
2369     printf("title=%s\n", line->elements[1].item);
2370        } else {
2371     char * title;
2372     line = getLineByType(LT_MENUENTRY, entry->lines);
2373     if (line) {
2374        title = grub2ExtractTitle(line);
2375        if (title)
2376     printf("title=%s\n", title);
2377     }
2378        }
2379    
2380        for (j = 0, line = entry->lines; line; line = line->next) {
2381     if ((line->type & LT_MBMODULE) && line->numElements >= 2) {
2382        if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2383     printf("mbmodule%d=", j);
2384        else
2385     printf("mbmodule%d=%s", j, prefix);
2386    
2387        for (i = 1; i < line->numElements; i++)
2388     printf("%s%s", line->elements[i].item, line->elements[i].indent);
2389        printf("\n");
2390        j++;
2391     }
2392        }
2393    }
2394    
2395    int isSuseSystem(void) {
2396        const char * path;
2397        const static char default_path[] = "/etc/SuSE-release";
2398    
2399        if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2400     path = default_path;
2401    
2402        if (!access(path, R_OK))
2403     return 1;
2404        return 0;
2405    }
2406    
2407    int isSuseGrubConf(const char * path) {
2408        FILE * grubConf;
2409        char * line = NULL;
2410        size_t len = 0, res = 0;
2411    
2412        grubConf = fopen(path, "r");
2413        if (!grubConf) {
2414            dbgPrintf("Could not open SuSE configuration file '%s'\n", path);
2415     return 0;
2416        }
2417    
2418        while ((res = getline(&line, &len, grubConf)) != -1) {
2419     if (!strncmp(line, "setup", 5)) {
2420        fclose(grubConf);
2421        free(line);
2422        return 1;
2423     }
2424        }
2425    
2426        dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2427          path);
2428    
2429        fclose(grubConf);
2430        free(line);
2431        return 0;
2432    }
2433    
2434    int suseGrubConfGetLba(const char * path, int * lbaPtr) {
2435        FILE * grubConf;
2436        char * line = NULL;
2437        size_t res = 0, len = 0;
2438    
2439        if (!path) return 1;
2440        if (!lbaPtr) return 1;
2441    
2442        grubConf = fopen(path, "r");
2443        if (!grubConf) return 1;
2444    
2445        while ((res = getline(&line, &len, grubConf)) != -1) {
2446     if (line[res - 1] == '\n')
2447        line[res - 1] = '\0';
2448     else if (len > res)
2449        line[res] = '\0';
2450     else {
2451        line = realloc(line, res + 1);
2452        line[res] = '\0';
2453     }
2454    
2455     if (!strncmp(line, "setup", 5)) {
2456        if (strstr(line, "--force-lba")) {
2457            *lbaPtr = 1;
2458        } else {
2459            *lbaPtr = 0;
2460        }
2461        dbgPrintf("lba: %i\n", *lbaPtr);
2462        break;
2463     }
2464        }
2465    
2466        free(line);
2467        fclose(grubConf);
2468        return 0;
2469    }
2470    
2471    int suseGrubConfGetInstallDevice(const char * path, char ** devicePtr) {
2472        FILE * grubConf;
2473        char * line = NULL;
2474        size_t res = 0, len = 0;
2475        char * lastParamPtr = NULL;
2476        char * secLastParamPtr = NULL;
2477        char installDeviceNumber = '\0';
2478        char * bounds = NULL;
2479    
2480        if (!path) return 1;
2481        if (!devicePtr) return 1;
2482    
2483        grubConf = fopen(path, "r");
2484        if (!grubConf) return 1;
2485    
2486        while ((res = getline(&line, &len, grubConf)) != -1) {
2487     if (strncmp(line, "setup", 5))
2488        continue;
2489    
2490     if (line[res - 1] == '\n')
2491        line[res - 1] = '\0';
2492     else if (len > res)
2493        line[res] = '\0';
2494     else {
2495        line = realloc(line, res + 1);
2496        line[res] = '\0';
2497     }
2498    
2499     lastParamPtr = bounds = line + res;
2500    
2501     /* Last parameter in grub may be an optional IMAGE_DEVICE */
2502     while (!isspace(*lastParamPtr))
2503        lastParamPtr--;
2504     lastParamPtr++;
2505    
2506     secLastParamPtr = lastParamPtr - 2;
2507     dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2508    
2509     if (lastParamPtr + 3 > bounds) {
2510        dbgPrintf("lastParamPtr going over boundary");
2511        fclose(grubConf);
2512        free(line);
2513        return 1;
2514     }
2515     if (!strncmp(lastParamPtr, "(hd", 3))
2516        lastParamPtr += 3;
2517     dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2518    
2519     /*
2520     * Second last parameter will decide wether last parameter is
2521     * an IMAGE_DEVICE or INSTALL_DEVICE
2522     */
2523     while (!isspace(*secLastParamPtr))
2524        secLastParamPtr--;
2525     secLastParamPtr++;
2526    
2527     if (secLastParamPtr + 3 > bounds) {
2528        dbgPrintf("secLastParamPtr going over boundary");
2529        fclose(grubConf);
2530        free(line);
2531        return 1;
2532     }
2533     dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2534     if (!strncmp(secLastParamPtr, "(hd", 3)) {
2535        secLastParamPtr += 3;
2536        dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
2537        installDeviceNumber = *secLastParamPtr;
2538     } else {
2539        installDeviceNumber = *lastParamPtr;
2540     }
2541    
2542     *devicePtr = malloc(6);
2543     snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
2544     dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
2545     fclose(grubConf);
2546     free(line);
2547     return 0;
2548        }
2549    
2550        free(line);
2551        fclose(grubConf);
2552        return 1;
2553    }
2554    
2555    int grubGetBootFromDeviceMap(const char * device,
2556         char ** bootPtr) {
2557        FILE * deviceMap;
2558        char * line = NULL;
2559        size_t res = 0, len = 0;
2560        char * devicePtr;
2561        char * bounds = NULL;
2562        const char * path;
2563        const static char default_path[] = "/boot/grub/device.map";
2564    
2565        if (!device) return 1;
2566        if (!bootPtr) return 1;
2567    
2568        if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2569     path = default_path;
2570    
2571        dbgPrintf("opening grub device.map file from: %s\n", path);
2572        deviceMap = fopen(path, "r");
2573        if (!deviceMap)
2574     return 1;
2575    
2576        while ((res = getline(&line, &len, deviceMap)) != -1) {
2577            if (!strncmp(line, "#", 1))
2578        continue;
2579    
2580     if (line[res - 1] == '\n')
2581        line[res - 1] = '\0';
2582     else if (len > res)
2583        line[res] = '\0';
2584     else {
2585        line = realloc(line, res + 1);
2586        line[res] = '\0';
2587     }
2588    
2589     devicePtr = line;
2590     bounds = line + res;
2591    
2592     while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2593        devicePtr++;
2594     dbgPrintf("device: %s\n", devicePtr);
2595    
2596     if (!strncmp(devicePtr, device, strlen(device))) {
2597        devicePtr += strlen(device);
2598        while (isspace(*devicePtr) && ((devicePtr + 1) <= bounds))
2599            devicePtr++;
2600    
2601        *bootPtr = strdup(devicePtr);
2602        break;
2603     }
2604        }
2605    
2606        free(line);
2607        fclose(deviceMap);
2608        return 0;
2609    }
2610    
2611    int suseGrubConfGetBoot(const char * path, char ** bootPtr) {
2612        char * grubDevice;
2613    
2614        if (suseGrubConfGetInstallDevice(path, &grubDevice))
2615     dbgPrintf("error looking for grub installation device\n");
2616        else
2617     dbgPrintf("grubby installation device: %s\n", grubDevice);
2618    
2619        if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
2620     dbgPrintf("error looking for grub boot device\n");
2621        else
2622     dbgPrintf("grubby boot device: %s\n", *bootPtr);
2623    
2624        free(grubDevice);
2625        return 0;
2626    }
2627    
2628    int parseSuseGrubConf(int * lbaPtr, char ** bootPtr) {
2629        /*
2630         * This SuSE grub configuration file at this location is not your average
2631         * grub configuration file, but instead the grub commands used to setup
2632         * grub on that system.
2633         */
2634        const char * path;
2635        const static char default_path[] = "/etc/grub.conf";
2636    
2637        if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
2638     path = default_path;
2639    
2640        if (!isSuseGrubConf(path)) return 1;
2641    
2642        if (lbaPtr) {
2643            *lbaPtr = 0;
2644            if (suseGrubConfGetLba(path, lbaPtr))
2645                return 1;
2646        }
2647    
2648        if (bootPtr) {
2649            *bootPtr = NULL;
2650            suseGrubConfGetBoot(path, bootPtr);
2651        }
2652    
2653        return 0;
2654  }  }
2655    
2656  int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {  int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
# Line 1369  int parseSysconfigGrub(int * lbaPtr, cha Line 2660  int parseSysconfigGrub(int * lbaPtr, cha
2660      char * start;      char * start;
2661      char * param;      char * param;
2662    
2663      in = fopen("/etc/sysconfig/grub", "r");      in = fopen("/etc/conf.d/grub", "r");
2664      if (!in) return 1;      if (!in) return 1;
2665    
2666      if (lbaPtr) *lbaPtr = 0;      if (lbaPtr) *lbaPtr = 0;
# Line 1410  int parseSysconfigGrub(int * lbaPtr, cha Line 2701  int parseSysconfigGrub(int * lbaPtr, cha
2701  }  }
2702    
2703  void dumpSysconfigGrub(void) {  void dumpSysconfigGrub(void) {
2704      char * boot;      char * boot = NULL;
2705      int lba;      int lba;
2706    
2707      if (!parseSysconfigGrub(&lba, &boot)) {      if (isSuseSystem()) {
2708   if (lba) printf("lba\n");          if (parseSuseGrubConf(&lba, &boot)) {
2709   if (boot) printf("boot=%s\n", boot);      free(boot);
2710        return;
2711     }
2712        } else {
2713            if (parseSysconfigGrub(&lba, &boot)) {
2714        free(boot);
2715        return;
2716     }
2717        }
2718    
2719        if (lba) printf("lba\n");
2720        if (boot) {
2721     printf("boot=%s\n", boot);
2722     free(boot);
2723      }      }
2724  }  }
2725    
# Line 1431  int displayInfo(struct grubConfig * conf Line 2735  int displayInfo(struct grubConfig * conf
2735   return 1;   return 1;
2736      }      }
2737    
2738      /* 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
2739         be a better way */         be a better way */
2740      if (config->cfi == &grubConfigType) {      if (config->cfi == &grubConfigType) {
2741   dumpSysconfigGrub();   dumpSysconfigGrub();
2742      } else {      } else {
2743   line = config->theLines;   line = getLineByType(LT_BOOT, config->theLines);
  while (line && line->type != LT_BOOT) line = line->next;  
2744   if (line && line->numElements >= 1) {   if (line && line->numElements >= 1) {
2745      printf("boot=%s\n", line->elements[1].item);      printf("boot=%s\n", line->elements[1].item);
2746   }   }
2747    
2748   line = config->theLines;   line = getLineByType(LT_LBA, config->theLines);
  while (line && line->type != LT_LBA) line = line->next;  
2749   if (line) printf("lba\n");   if (line) printf("lba\n");
2750      }      }
2751    
# Line 1458  int displayInfo(struct grubConfig * conf Line 2760  int displayInfo(struct grubConfig * conf
2760      return 0;      return 0;
2761  }  }
2762    
2763    struct singleLine * addLineTmpl(struct singleEntry * entry,
2764     struct singleLine * tmplLine,
2765     struct singleLine * prevLine,
2766     const char * val,
2767     struct configFileInfo * cfi)
2768    {
2769        struct singleLine * newLine = lineDup(tmplLine);
2770    
2771        if (isEfi && cfi == &grub2ConfigType) {
2772     enum lineType_e old = newLine->type;
2773     newLine->type = preferredLineType(newLine->type, cfi);
2774     if (old != newLine->type)
2775        newLine->elements[0].item = getKeyByType(newLine->type, cfi);
2776        }
2777    
2778        if (val) {
2779     /* override the inherited value with our own.
2780     * This is a little weak because it only applies to elements[1]
2781     */
2782     if (newLine->numElements > 1)
2783        removeElement(newLine, 1);
2784     insertElement(newLine, val, 1, cfi);
2785    
2786     /* but try to keep the rootspec from the template... sigh */
2787     if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD|LT_KERNEL_EFI|LT_INITRD_EFI|LT_KERNEL_16|LT_INITRD_16)) {
2788        char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
2789        if (rootspec != NULL) {
2790     free(newLine->elements[1].item);
2791     newLine->elements[1].item =
2792        sdupprintf("%s%s", rootspec, val);
2793        }
2794     }
2795        }
2796    
2797        dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
2798          newLine->elements[0].item : "");
2799    
2800        if (!entry->lines) {
2801     /* first one on the list */
2802     entry->lines = newLine;
2803        } else if (prevLine) {
2804     /* add after prevLine */
2805     newLine->next = prevLine->next;
2806     prevLine->next = newLine;
2807        }
2808    
2809        return newLine;
2810    }
2811    
2812  /* val may be NULL */  /* val may be NULL */
2813  struct singleLine *  addLine(struct singleEntry * entry,  struct singleLine *  addLine(struct singleEntry * entry,
2814       struct configFileInfo * cfi,       struct configFileInfo * cfi,
2815       enum lineType_e type, const char * defaultIndent,       enum lineType_e type, char * defaultIndent,
2816       char * val) {       const char * val) {
2817      struct singleLine * line, * prev;      struct singleLine * line, * prev;
2818      int i;      struct keywordTypes * kw;
2819        struct singleLine tmpl;
2820    
2821      for (i = 0; cfi->keywords[i].key; i++)      /* NB: This function shouldn't allocate items on the heap, rather on the
2822   if (cfi->keywords[i].type == type) break;       * stack since it calls addLineTmpl which will make copies.
2823      if (type != LT_TITLE || !cfi->titleBracketed)       */
2824          if (!cfi->keywords[i].key) abort();      if (type == LT_TITLE && cfi->titleBracketed) {
2825     /* we're doing a bracketed title (zipl) */
2826     tmpl.type = type;
2827     tmpl.numElements = 1;
2828     tmpl.elements = alloca(sizeof(*tmpl.elements));
2829     tmpl.elements[0].item = alloca(strlen(val)+3);
2830     sprintf(tmpl.elements[0].item, "[%s]", val);
2831     tmpl.elements[0].indent = "";
2832     val = NULL;
2833        } else if (type == LT_MENUENTRY) {
2834     char *lineend = "--class gnu-linux --class gnu --class os {";
2835     if (!val) {
2836        fprintf(stderr, "Line type LT_MENUENTRY requires a value\n");
2837        abort();
2838     }
2839     kw = getKeywordByType(type, cfi);
2840     if (!kw) {
2841        fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2842        abort();
2843     }
2844     tmpl.indent = "";
2845     tmpl.type = type;
2846     tmpl.numElements = 3;
2847     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2848     tmpl.elements[0].item = kw->key;
2849     tmpl.elements[0].indent = alloca(2);
2850     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2851     tmpl.elements[1].item = (char *)val;
2852     tmpl.elements[1].indent = alloca(2);
2853     sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
2854     tmpl.elements[2].item = alloca(strlen(lineend)+1);
2855     strcpy(tmpl.elements[2].item, lineend);
2856     tmpl.elements[2].indent = "";
2857        } else {
2858     kw = getKeywordByType(type, cfi);
2859     if (!kw) {
2860        fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2861        abort();
2862     }
2863     tmpl.type = type;
2864     tmpl.numElements = val ? 2 : 1;
2865     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2866     tmpl.elements[0].item = kw->key;
2867     tmpl.elements[0].indent = alloca(2);
2868     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2869     if (val) {
2870        tmpl.elements[1].item = (char *)val;
2871        tmpl.elements[1].indent = "";
2872     }
2873        }
2874    
2875      /* 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
2876         to insert after. Note that comments are considered empty lines, which         to insert after. Note that comments are considered empty lines, which
2877         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
2878         first line, we use defaultIndent (the first line is normally indented         first line, we use defaultIndent (the first line is normally indented
2879         differently from the rest) */         differently from the rest) */
2880      if (entry->lines) {      for (line = entry->lines, prev = NULL; line; line = line->next) {
2881   line = entry->lines;   if (line->numElements) prev = line;
2882   prev = NULL;   /* fall back on the last line if prev isn't otherwise set */
2883   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;  
2884      }      }
2885    
2886      if (type != LT_TITLE || !cfi->titleBracketed) {      struct singleLine *menuEntry;
2887          line->type = type;      menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
2888          line->numElements = val ? 2 : 1;      if (tmpl.type == LT_ENTRY_END) {
2889          line->elements = malloc(sizeof(*line->elements) * line->numElements);   if (menuEntry)
2890          line->elements[0].item = strdup(cfi->keywords[i].key);      tmpl.indent = menuEntry->indent;
2891          line->elements[0].indent = malloc(2);   else
2892          line->elements[0].indent[0] = cfi->keywords[i].nextChar;      tmpl.indent = defaultIndent ?: "";
2893          line->elements[0].indent[1] = '\0';      } else if (tmpl.type != LT_MENUENTRY) {
2894             if (menuEntry)
2895          if (val) {      tmpl.indent = "\t";
2896              line->elements[1].item = val;   else if (prev == entry->lines)
2897              line->elements[1].indent = strdup("");      tmpl.indent = defaultIndent ?: "";
2898          }   else
2899      } 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("");  
2900      }      }
2901    
2902      return line;      return addLineTmpl(entry, &tmpl, prev, val, cfi);
2903  }  }
2904    
2905  void removeLine(struct singleEntry * entry, struct singleLine * line) {  void removeLine(struct singleEntry * entry, struct singleLine * line) {
# Line 1553  void removeLine(struct singleEntry * ent Line 2924  void removeLine(struct singleEntry * ent
2924      free(line);      free(line);
2925  }  }
2926    
2927    static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi)
2928    {
2929        struct singleLine newLine = {
2930     .indent = tmplLine->indent,
2931     .type = tmplLine->type,
2932     .next = tmplLine->next,
2933        };
2934        int firstQuotedItem = -1;
2935        int quoteLen = 0;
2936        int j;
2937        int element = 0;
2938        char *c;
2939    
2940        c = malloc(strlen(tmplLine->elements[0].item) + 1);
2941        strcpy(c, tmplLine->elements[0].item);
2942        insertElement(&newLine, c, element++, cfi);
2943        free(c);
2944        c = NULL;
2945    
2946        for (j = 1; j < tmplLine->numElements; j++) {
2947     if (firstQuotedItem == -1) {
2948        quoteLen += strlen(tmplLine->elements[j].item);
2949        
2950        if (isquote(tmplLine->elements[j].item[0])) {
2951     firstQuotedItem = j;
2952            quoteLen += strlen(tmplLine->elements[j].indent);
2953        } else {
2954     c = malloc(quoteLen + 1);
2955     strcpy(c, tmplLine->elements[j].item);
2956     insertElement(&newLine, c, element++, cfi);
2957     free(c);
2958     quoteLen = 0;
2959        }
2960     } else {
2961        int itemlen = strlen(tmplLine->elements[j].item);
2962        quoteLen += itemlen;
2963        quoteLen += strlen(tmplLine->elements[j].indent);
2964        
2965        if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
2966     c = malloc(quoteLen + 1);
2967     c[0] = '\0';
2968     for (int i = firstQuotedItem; i < j+1; i++) {
2969        strcat(c, tmplLine->elements[i].item);
2970        strcat(c, tmplLine->elements[i].indent);
2971     }
2972     insertElement(&newLine, c, element++, cfi);
2973     free(c);
2974    
2975     firstQuotedItem = -1;
2976     quoteLen = 0;
2977        }
2978     }
2979        }
2980        while (tmplLine->numElements)
2981     removeElement(tmplLine, 0);
2982        if (tmplLine->elements)
2983     free(tmplLine->elements);
2984    
2985        tmplLine->numElements = newLine.numElements;
2986        tmplLine->elements = newLine.elements;
2987    }
2988    
2989    static void insertElement(struct singleLine * line,
2990      const char * item, int insertHere,
2991      struct configFileInfo * cfi)
2992    {
2993        struct keywordTypes * kw;
2994        char indent[2] = "";
2995    
2996        /* sanity check */
2997        if (insertHere > line->numElements) {
2998     dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",
2999      insertHere, line->numElements);
3000     insertHere = line->numElements;
3001        }
3002    
3003        line->elements = realloc(line->elements, (line->numElements + 1) *
3004         sizeof(*line->elements));
3005        memmove(&line->elements[insertHere+1],
3006        &line->elements[insertHere],
3007        (line->numElements - insertHere) *
3008        sizeof(*line->elements));
3009        line->elements[insertHere].item = strdup(item);
3010    
3011        kw = getKeywordByType(line->type, cfi);
3012    
3013        if (line->numElements == 0) {
3014     indent[0] = '\0';
3015        } else if (insertHere == 0) {
3016     indent[0] = kw->nextChar;
3017        } else if (kw->separatorChar != '\0') {
3018     indent[0] = kw->separatorChar;
3019        } else {
3020     indent[0] = ' ';
3021        }
3022    
3023        if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {
3024     /* move the end-of-line forward */
3025     line->elements[insertHere].indent =
3026        line->elements[insertHere-1].indent;
3027     line->elements[insertHere-1].indent = strdup(indent);
3028        } else {
3029     line->elements[insertHere].indent = strdup(indent);
3030        }
3031    
3032        line->numElements++;
3033    
3034        dbgPrintf("insertElement(%s, '%s%s', %d)\n",
3035          line->elements[0].item,
3036          line->elements[insertHere].item,
3037          line->elements[insertHere].indent,
3038          insertHere);
3039    }
3040    
3041    static void removeElement(struct singleLine * line, int removeHere) {
3042        int i;
3043    
3044        /* sanity check */
3045        if (removeHere >= line->numElements) return;
3046    
3047        dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
3048          removeHere, line->elements[removeHere].item);
3049    
3050        free(line->elements[removeHere].item);
3051    
3052        if (removeHere > 1) {
3053     /* previous argument gets this argument's post-indentation */
3054     free(line->elements[removeHere-1].indent);
3055     line->elements[removeHere-1].indent =
3056        line->elements[removeHere].indent;
3057        } else {
3058     free(line->elements[removeHere].indent);
3059        }
3060    
3061        /* now collapse the array, but don't bother to realloc smaller */
3062        for (i = removeHere; i < line->numElements - 1; i++)
3063     line->elements[i] = line->elements[i + 1];
3064    
3065        line->numElements--;
3066    }
3067    
3068  int argMatch(const char * one, const char * two) {  int argMatch(const char * one, const char * two) {
3069      char * first, * second;      char * first, * second;
3070      char * chptr;      char * chptr;
# Line 1575  int updateActualImage(struct grubConfig Line 3087  int updateActualImage(struct grubConfig
3087      struct singleEntry * entry;      struct singleEntry * entry;
3088      struct singleLine * line, * rootLine;      struct singleLine * line, * rootLine;
3089      int index = 0;      int index = 0;
3090      int i, j, k;      int i, k;
3091      const char ** newArgs, ** oldArgs;      const char ** newArgs, ** oldArgs;
3092      const char ** arg;      const char ** arg;
3093      const char * chptr;      int useKernelArgs, useRoot;
     int useKernelArgs = 0;  
     int useRoot = 0;  
3094      int firstElement;      int firstElement;
3095      int *usedElements, *usedArgs;      int *usedElements;
3096        int doreplace;
3097    
3098      if (!image) return 0;      if (!image) return 0;
3099    
# Line 1609  int updateActualImage(struct grubConfig Line 3120  int updateActualImage(struct grubConfig
3120   }   }
3121      }      }
3122    
     for (i = 0; cfg->cfi->keywords[i].key; i++)  
  if (cfg->cfi->keywords[i].type == LT_KERNELARGS) break;  
3123    
3124      if (cfg->cfi->keywords[i].key)      useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
3125   useKernelArgs = 1;       && (!multibootArgs || cfg->cfi->mbConcatArgs));
3126    
3127      for (i = 0; cfg->cfi->keywords[i].key; i++)      useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
3128   if (cfg->cfi->keywords[i].type == LT_ROOT) break;         && !multibootArgs);
3129    
3130      if (cfg->cfi->keywords[i].key)      for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
  useRoot = 1;  
3131    
3132      k = 0;   if (multibootArgs && !entry->multiboot)
3133      for (arg = newArgs; *arg; arg++)      continue;
3134          k++;  
3135      usedArgs = calloc(k, sizeof(int));   /* Determine where to put the args.  If this config supports
3136     * LT_KERNELARGS, use that.  Otherwise use
3137     * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
3138     */
3139     if (useKernelArgs) {
3140        line = getLineByType(LT_KERNELARGS, entry->lines);
3141        if (!line) {
3142     /* no LT_KERNELARGS, need to add it */
3143     line = addLine(entry, cfg->cfi, LT_KERNELARGS,
3144           cfg->secondaryIndent, NULL);
3145        }
3146        firstElement = 1;
3147    
3148      while ((entry = findEntryByPath(cfg, image, prefix, &index))) {   } else if (multibootArgs) {
3149   index++;      line = getLineByType(LT_HYPER, entry->lines);
3150        if (!line) {
3151     /* a multiboot entry without LT_HYPER? */
3152     continue;
3153        }
3154        firstElement = 2;
3155    
3156   line = entry->lines;   } else {
3157   while (line && line->type != LT_KERNEL) line = line->next;      line = getLineByType(LT_KERNEL|LT_MBMODULE|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3158   if (!line) continue;      if (!line) {
3159   firstElement = 2;   /* no LT_KERNEL or LT_MBMODULE in this entry? */
3160     continue;
3161          if (entry->multiboot && !multibootArgs) {      }
3162              /* first mb module line is the real kernel */      firstElement = 2;
3163              while (line && line->type != LT_MBMODULE) line = line->next;   }
3164              firstElement = 2;  
3165          } else if (useKernelArgs) {   /* handle the elilo case which does:
3166      while (line && line->type != LT_KERNELARGS) line = line->next;   *   append="hypervisor args -- kernel args"
3167      firstElement = 1;   */
3168     if (entry->multiboot && cfg->cfi->mbConcatArgs) {
3169        /* this is a multiboot entry, make sure there's
3170         * -- on the args line
3171         */
3172        for (i = firstElement; i < line->numElements; i++) {
3173     if (!strcmp(line->elements[i].item, "--"))
3174        break;
3175        }
3176        if (i == line->numElements) {
3177     /* assume all existing args are kernel args,
3178     * prepend -- to make it official
3179     */
3180     insertElement(line, "--", firstElement, cfg->cfi);
3181     i = firstElement;
3182        }
3183        if (!multibootArgs) {
3184     /* kernel args start after the -- */
3185     firstElement = i + 1;
3186        }
3187     } else if (cfg->cfi->mbConcatArgs) {
3188        /* this is a non-multiboot entry, remove hyper args */
3189        for (i = firstElement; i < line->numElements; i++) {
3190     if (!strcmp(line->elements[i].item, "--"))
3191        break;
3192        }
3193        if (i < line->numElements) {
3194     /* remove args up to -- */
3195     while (strcmp(line->elements[firstElement].item, "--"))
3196        removeElement(line, firstElement);
3197     /* remove -- */
3198     removeElement(line, firstElement);
3199        }
3200   }   }
3201    
3202   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);  
  }  
3203    
3204          usedElements = calloc(line->numElements, sizeof(int));   for (k = 0, arg = newArgs; *arg; arg++, k++) {
3205    
3206          k = 0;      doreplace = 1;
  for (arg = newArgs; *arg; arg++) {  
             if (usedArgs[k]) {  
                 k++;  
                 continue;  
             }  
3207      for (i = firstElement; i < line->numElements; i++) {      for (i = firstElement; i < line->numElements; i++) {
3208     if (multibootArgs && cfg->cfi->mbConcatArgs &&
3209        !strcmp(line->elements[i].item, "--"))
3210     {
3211        /* reached the end of hyper args, insert here */
3212        doreplace = 0;
3213        break;  
3214     }
3215                  if (usedElements[i])                  if (usedElements[i])
3216                      continue;                      continue;
3217   if (!argMatch(line->elements[i].item, *arg)) {   if (!argMatch(line->elements[i].item, *arg)) {
3218                      usedElements[i]=1;                      usedElements[i]=1;
                     usedArgs[k]=1;  
3219      break;      break;
3220                  }                  }
3221              }              }
     chptr = strchr(*arg, '=');  
3222    
3223      if (i < line->numElements) {      if (i < line->numElements && doreplace) {
3224   /* replace */   /* direct replacement */
3225   free(line->elements[i].item);   free(line->elements[i].item);
3226   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("");  
  }  
3227    
3228   free(rootLine->elements[1].item);      } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
3229   rootLine->elements[1].item = strdup(chptr + 1);   /* root= replacement */
3230      } else {   rootLine = getLineByType(LT_ROOT, entry->lines);
3231   /* append */   if (rootLine) {
3232   line->elements = realloc(line->elements,      free(rootLine->elements[1].item);
3233   (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(" ");  
3234   } else {   } else {
3235      /* First thing on this line; treat a bit differently. Note      rootLine = addLine(entry, cfg->cfi, LT_ROOT,
3236         this is only possible if we've added a LT_KERNELARGS         cfg->secondaryIndent, *arg + 5);
        entry */  
     line->elements[line->numElements].indent = strdup("");  
3237   }   }
3238        }
3239    
3240   line->numElements++;      else {
3241     /* insert/append */
3242     insertElement(line, *arg, i, cfg->cfi);
3243     usedElements = realloc(usedElements, line->numElements *
3244           sizeof(*usedElements));
3245     memmove(&usedElements[i + 1], &usedElements[i],
3246     line->numElements - i - 1);
3247     usedElements[i] = 1;
3248    
3249   /* if we updated a root= here even though there is a   /* if we updated a root= here even though there is a
3250     LT_ROOT available we need to remove the LT_ROOT entry     LT_ROOT available we need to remove the LT_ROOT entry
3251     (this will happen if we switch from a device to a label) */     (this will happen if we switch from a device to a label) */
3252   if (useRoot && !strncmp(*arg, "root=", 5)) {   if (useRoot && !strncmp(*arg, "root=", 5)) {
3253      rootLine = entry->lines;      rootLine = getLineByType(LT_ROOT, entry->lines);
3254      while (rootLine && rootLine->type != LT_ROOT)      if (rootLine)
  rootLine = rootLine->next;  
     if (rootLine) {  
3255   removeLine(entry, rootLine);   removeLine(entry, rootLine);
     }  
3256   }   }
3257      }      }
             k++;  
3258   }   }
3259    
3260          free(usedElements);          free(usedElements);
3261    
  /* 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? */  
3262   for (arg = oldArgs; *arg; arg++) {   for (arg = oldArgs; *arg; arg++) {
3263      for (i = firstElement; i < line->numElements; i++)      for (i = firstElement; i < line->numElements; i++) {
3264   if (!argMatch(line->elements[i].item, *arg))   if (multibootArgs && cfg->cfi->mbConcatArgs &&
3265        !strcmp(line->elements[i].item, "--"))
3266        /* reached the end of hyper args, stop here */
3267        break;
3268     if (!argMatch(line->elements[i].item, *arg)) {
3269        removeElement(line, i);
3270      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;  
3271   }   }
3272        }
3273   free(line->elements[i].item);      /* handle removing LT_ROOT line too */
3274        if (useRoot && !strncmp(*arg, "root=", 5)) {
3275   for (j = i + 1; j < line->numElements; j++)   rootLine = getLineByType(LT_ROOT, entry->lines);
3276      line->elements[j - 1] = line->elements[j];   if (rootLine)
3277        removeLine(entry, rootLine);
  line->numElements--;  
3278      }      }
3279   }   }
3280    
# Line 1760  int updateActualImage(struct grubConfig Line 3285  int updateActualImage(struct grubConfig
3285   }   }
3286      }      }
3287    
     free(usedArgs);  
3288      free(newArgs);      free(newArgs);
3289      free(oldArgs);      free(oldArgs);
3290    
# Line 1786  int updateImage(struct grubConfig * cfg, Line 3310  int updateImage(struct grubConfig * cfg,
3310      return rc;      return rc;
3311  }  }
3312    
3313    int addMBInitrd(struct grubConfig * cfg, const char *newMBKernel,
3314     const char * image, const char * prefix, const char * initrd,
3315     const char * title) {
3316        struct singleEntry * entry;
3317        struct singleLine * line, * kernelLine, *endLine = NULL;
3318        int index = 0;
3319    
3320        if (!image) return 0;
3321    
3322        for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3323            kernelLine = getLineByType(LT_MBMODULE, entry->lines);
3324            if (!kernelLine) continue;
3325    
3326     /* if title is supplied, the entry's title must match it. */
3327     if (title) {
3328        char *linetitle;
3329    
3330        line = getLineByType(LT_TITLE|LT_MENUENTRY, entry->lines);
3331        if (!line)
3332     continue;
3333    
3334        linetitle = extractTitle(line);
3335        if (!linetitle)
3336     continue;
3337        if (strcmp(title, linetitle)) {
3338     free(linetitle);
3339     continue;
3340        }
3341        free(linetitle);
3342     }
3343    
3344            if (prefix) {
3345                int prefixLen = strlen(prefix);
3346                if (!strncmp(initrd, prefix, prefixLen))
3347                    initrd += prefixLen;
3348            }
3349     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3350     if (endLine)
3351        removeLine(entry, endLine);
3352            line = addLine(entry, cfg->cfi, preferredLineType(LT_MBMODULE,cfg->cfi),
3353     kernelLine->indent, initrd);
3354            if (!line)
3355        return 1;
3356     if (endLine) {
3357        line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3358                if (!line)
3359     return 1;
3360     }
3361    
3362            break;
3363        }
3364    
3365        return 0;
3366    }
3367    
3368    int updateInitrd(struct grubConfig * cfg, const char * image,
3369                     const char * prefix, const char * initrd, const char * title) {
3370        struct singleEntry * entry;
3371        struct singleLine * line, * kernelLine, *endLine = NULL;
3372        int index = 0;
3373    
3374        if (!image) return 0;
3375    
3376        for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3377            kernelLine = getLineByType(LT_KERNEL|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3378            if (!kernelLine) continue;
3379    
3380     /* if title is supplied, the entry's title must match it. */
3381     if (title) {
3382        char *linetitle;
3383    
3384        line = getLineByType(LT_TITLE|LT_MENUENTRY, entry->lines);
3385        if (!line)
3386     continue;
3387    
3388        linetitle = extractTitle(line);
3389        if (!linetitle)
3390     continue;
3391        if (strcmp(title, linetitle)) {
3392     free(linetitle);
3393     continue;
3394        }
3395        free(linetitle);
3396     }
3397    
3398            line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
3399            if (line)
3400                removeLine(entry, line);
3401            if (prefix) {
3402                int prefixLen = strlen(prefix);
3403                if (!strncmp(initrd, prefix, prefixLen))
3404                    initrd += prefixLen;
3405            }
3406     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3407     if (endLine)
3408        removeLine(entry, endLine);
3409     enum lineType_e lt;
3410     switch(kernelLine->type) {
3411        case LT_KERNEL:
3412            lt = LT_INITRD;
3413     break;
3414        case LT_KERNEL_EFI:
3415            lt = LT_INITRD_EFI;
3416     break;
3417        case LT_KERNEL_16:
3418            lt = LT_INITRD_16;
3419     break;
3420        default:
3421            lt = preferredLineType(LT_INITRD, cfg->cfi);
3422     }
3423            line = addLine(entry, cfg->cfi, lt, kernelLine->indent, initrd);
3424            if (!line)
3425        return 1;
3426     if (endLine) {
3427        line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3428                if (!line)
3429     return 1;
3430     }
3431    
3432            break;
3433        }
3434    
3435        return 0;
3436    }
3437    
3438  int checkDeviceBootloader(const char * device, const unsigned char * boot) {  int checkDeviceBootloader(const char * device, const unsigned char * boot) {
3439      int fd;      int fd;
3440      unsigned char bootSect[512];      unsigned char bootSect[512];
# Line 1809  int checkDeviceBootloader(const char * d Line 3458  int checkDeviceBootloader(const char * d
3458      if (memcmp(boot, bootSect, 3))      if (memcmp(boot, bootSect, 3))
3459   return 0;   return 0;
3460    
3461      if (boot[1] == 0xeb) {      if (boot[1] == JMP_SHORT_OPCODE) {
3462   offset = boot[2] + 2;   offset = boot[2] + 2;
3463      } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {      } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3464   offset = (boot[3] << 8) + boot[2] + 2;   offset = (boot[3] << 8) + boot[2] + 2;
3465      } else if (boot[0] == 0xeb) {      } else if (boot[0] == JMP_SHORT_OPCODE) {
3466   offset = boot[1] + 2;        offset = boot[1] + 2;
3467            /*
3468     * it looks like grub, when copying stage1 into the mbr, patches stage1
3469     * right after the JMP location, replacing other instructions such as
3470     * JMPs for NOOPs. So, relax the check a little bit by skipping those
3471     * different bytes.
3472     */
3473          if ((bootSect[offset + 1] == NOOP_OPCODE)
3474      && (bootSect[offset + 2] == NOOP_OPCODE)) {
3475     offset = offset + 3;
3476          }
3477      } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {      } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3478   offset = (boot[2] << 8) + boot[1] + 2;   offset = (boot[2] << 8) + boot[1] + 2;
3479      } else {      } else {
# Line 1956  int checkForLilo(struct grubConfig * con Line 3615  int checkForLilo(struct grubConfig * con
3615      return checkDeviceBootloader(line->elements[1].item, boot);      return checkDeviceBootloader(line->elements[1].item, boot);
3616  }  }
3617    
3618    int checkForGrub2(struct grubConfig * config) {
3619        if (!access("/etc/grub.d/", R_OK))
3620     return 2;
3621    
3622        return 1;
3623    }
3624    
3625  int checkForGrub(struct grubConfig * config) {  int checkForGrub(struct grubConfig * config) {
3626      int fd;      int fd;
3627      unsigned char bootSect[512];      unsigned char bootSect[512];
3628      char * boot;      char * boot;
3629        int onSuse = isSuseSystem();
3630    
3631      if (parseSysconfigGrub(NULL, &boot))  
3632   return 0;      if (onSuse) {
3633     if (parseSuseGrubConf(NULL, &boot))
3634        return 0;
3635        } else {
3636     if (parseSysconfigGrub(NULL, &boot))
3637        return 0;
3638        }
3639    
3640      /* assume grub is not installed -- not an error condition */      /* assume grub is not installed -- not an error condition */
3641      if (!boot)      if (!boot)
# Line 1976  int checkForGrub(struct grubConfig * con Line 3649  int checkForGrub(struct grubConfig * con
3649      if (read(fd, bootSect, 512) != 512) {      if (read(fd, bootSect, 512) != 512) {
3650   fprintf(stderr, _("grubby: unable to read %s: %s\n"),   fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3651   "/boot/grub/stage1", strerror(errno));   "/boot/grub/stage1", strerror(errno));
3652     close(fd);
3653     return 1;
3654        }
3655        close(fd);
3656    
3657        /* The more elaborate checks do not work on SuSE. The checks done
3658         * seem to be reasonble (at least for now), so just return success
3659         */
3660        if (onSuse)
3661     return 2;
3662    
3663        return checkDeviceBootloader(boot, bootSect);
3664    }
3665    
3666    int checkForExtLinux(struct grubConfig * config) {
3667        int fd;
3668        unsigned char bootSect[512];
3669        char * boot;
3670        char executable[] = "/boot/extlinux/extlinux";
3671    
3672        printf("entered: checkForExtLinux()\n");
3673    
3674        if (parseSysconfigGrub(NULL, &boot))
3675     return 0;
3676    
3677        /* assume grub is not installed -- not an error condition */
3678        if (!boot)
3679     return 0;
3680    
3681        fd = open(executable, O_RDONLY);
3682        if (fd < 0)
3683     /* this doesn't exist if grub hasn't been installed */
3684     return 0;
3685    
3686        if (read(fd, bootSect, 512) != 512) {
3687     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3688     executable, strerror(errno));
3689   return 1;   return 1;
3690      }      }
3691      close(fd);      close(fd);
# Line 1983  int checkForGrub(struct grubConfig * con Line 3693  int checkForGrub(struct grubConfig * con
3693      return checkDeviceBootloader(boot, bootSect);      return checkDeviceBootloader(boot, bootSect);
3694  }  }
3695    
3696    int checkForYaboot(struct grubConfig * config) {
3697        /*
3698         * This is a simplistic check that we consider good enough for own puporses
3699         *
3700         * If we were to properly check if yaboot is *installed* we'd need to:
3701         * 1) get the system boot device (LT_BOOT)
3702         * 2) considering it's a raw filesystem, check if the yaboot binary matches
3703         *    the content on the boot device
3704         * 3) if not, copy the binary to a temporary file and run "addnote" on it
3705         * 4) check again if binary and boot device contents match
3706         */
3707        if (!access("/etc/yaboot.conf", R_OK))
3708     return 2;
3709    
3710        return 1;
3711    }
3712    
3713    int checkForElilo(struct grubConfig * config) {
3714        if (!access("/etc/elilo.conf", R_OK))
3715     return 2;
3716    
3717        return 1;
3718    }
3719    
3720  static char * getRootSpecifier(char * str) {  static char * getRootSpecifier(char * str) {
3721      char * idx, * rootspec = NULL;      char * idx, * rootspec = NULL;
3722    
# Line 1994  static char * getRootSpecifier(char * st Line 3728  static char * getRootSpecifier(char * st
3728      return rootspec;      return rootspec;
3729  }  }
3730    
3731    static char * getInitrdVal(struct grubConfig * config,
3732       const char * prefix, struct singleLine *tmplLine,
3733       const char * newKernelInitrd,
3734       const char ** extraInitrds, int extraInitrdCount)
3735    {
3736        char *initrdVal, *end;
3737        int i;
3738        size_t totalSize;
3739        size_t prefixLen;
3740        char separatorChar;
3741    
3742        prefixLen = strlen(prefix);
3743        totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
3744    
3745        for (i = 0; i < extraInitrdCount; i++) {
3746     totalSize += sizeof(separatorChar);
3747     totalSize += strlen(extraInitrds[i]) - prefixLen;
3748        }
3749    
3750        initrdVal = end = malloc(totalSize);
3751    
3752        end = stpcpy (end, newKernelInitrd + prefixLen);
3753    
3754        separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
3755        for (i = 0; i < extraInitrdCount; i++) {
3756     const char *extraInitrd;
3757     int j;
3758    
3759     extraInitrd = extraInitrds[i] + prefixLen;
3760     /* Don't add entries that are already there */
3761     if (tmplLine != NULL) {
3762        for (j = 2; j < tmplLine->numElements; j++)
3763     if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
3764        break;
3765    
3766        if (j != tmplLine->numElements)
3767     continue;
3768     }
3769    
3770     *end++ = separatorChar;
3771     end = stpcpy(end, extraInitrd);
3772        }
3773    
3774        return initrdVal;
3775    }
3776    
3777  int addNewKernel(struct grubConfig * config, struct singleEntry * template,  int addNewKernel(struct grubConfig * config, struct singleEntry * template,
3778           const char * prefix,           const char * prefix,
3779   char * newKernelPath, char * newKernelTitle,   const char * newKernelPath, const char * newKernelTitle,
3780   char * newKernelArgs, char * newKernelInitrd,   const char * newKernelArgs, const char * newKernelInitrd,
3781                   char * newMBKernel, char * newMBKernelArgs) {   const char ** extraInitrds, int extraInitrdCount,
3782                     const char * newMBKernel, const char * newMBKernelArgs,
3783     const char * newDevTreePath) {
3784      struct singleEntry * new;      struct singleEntry * new;
3785      struct singleLine * newLine = NULL, * tmplLine = NULL, * lastLine = NULL;      struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
3786      int needs;      int needs;
     char * indent = NULL;  
     char * rootspec = NULL;  
3787      char * chptr;      char * chptr;
     int i;  
     enum lineType_e type;  
3788    
3789      if (!newKernelPath) return 0;      if (!newKernelPath) return 0;
3790    
# Line 2036  int addNewKernel(struct grubConfig * con Line 3814  int addNewKernel(struct grubConfig * con
3814      config->entries = new;      config->entries = new;
3815    
3816      /* copy/update from the template */      /* copy/update from the template */
3817      needs = KERNEL_KERNEL | KERNEL_INITRD | KERNEL_TITLE;      needs = NEED_KERNEL | NEED_TITLE;
3818        if (newKernelInitrd)
3819     needs |= NEED_INITRD;
3820      if (newMBKernel) {      if (newMBKernel) {
3821          needs |= KERNEL_MB;          needs |= NEED_MB;
3822          new->multiboot = 1;          new->multiboot = 1;
3823      }      }
3824        if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))
3825     needs |= NEED_DEVTREE;
3826    
3827      if (template) {      if (template) {
3828   for (tmplLine = template->lines; tmplLine; tmplLine = tmplLine->next) {   for (masterLine = template->lines;
3829      /* remember the indention level; we may need it for new lines */       masterLine && (tmplLine = lineDup(masterLine));
3830      if (tmplLine->numElements)       lineFree(tmplLine), masterLine = masterLine->next)
3831   indent = tmplLine->indent;   {
3832        dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
3833    
3834      /* skip comments */      /* skip comments */
3835      chptr = tmplLine->indent;      chptr = tmplLine->indent;
3836      while (*chptr && isspace(*chptr)) chptr++;      while (*chptr && isspace(*chptr)) chptr++;
3837      if (*chptr == '#') continue;      if (*chptr == '#') continue;
3838    
3839      /* we don't need an initrd here */      if (iskernel(tmplLine->type) && tmplLine->numElements >= 2) {
3840      if (tmplLine->type == LT_INITRD && !newKernelInitrd) continue;   if (!template->multiboot && (needs & NEED_MB)) {
3841        /* it's not a multiboot template and this is the kernel
3842              if (tmplLine->type == LT_KERNEL &&       * line.  Try to be intelligent about inserting the
3843                  !template->multiboot && (needs & KERNEL_MB)) {       * hypervisor at the same time.
3844                  struct singleLine *l;       */
3845                  needs &= ~ KERNEL_MB;      if (config->cfi->mbHyperFirst) {
3846     /* insert the hypervisor first */
3847                  l = addLine(new, config->cfi, LT_KERNEL,   newLine = addLine(new, config->cfi, LT_HYPER,
3848                                    config->secondaryIndent,    tmplLine->indent,
3849                                    newMBKernel + strlen(prefix));    newMBKernel + strlen(prefix));
3850                     /* set up for adding the kernel line */
3851                  tmplLine = lastLine;   free(tmplLine->indent);
3852                  if (!new->lines) {   tmplLine->indent = strdup(config->secondaryIndent);
3853                      new->lines = l;   needs &= ~NEED_MB;
3854                  } else {      }
3855                      newLine->next = l;      if (needs & NEED_KERNEL) {
3856                      newLine = l;   /* use addLineTmpl to preserve line elements,
3857                  }   * otherwise we could just call addLine.  Unfortunately
3858                  continue;   * this means making some changes to the template
3859              } else if (tmplLine->type == LT_KERNEL &&   * such as the indent change above and the type
3860                         template->multiboot && !new->multiboot) {   * change below.
3861                  continue; /* don't need multiboot kernel here */   */
3862              }   struct keywordTypes * mbm_kw =
3863        getKeywordByType(LT_MBMODULE, config->cfi);
3864      if (!new->lines) {   if (mbm_kw) {
3865   newLine = malloc(sizeof(*newLine));      tmplLine->type = LT_MBMODULE;
3866   new->lines = newLine;      free(tmplLine->elements[0].item);
3867      } else {      tmplLine->elements[0].item = strdup(mbm_kw->key);
3868   newLine->next = malloc(sizeof(*newLine));   }
3869   newLine = newLine->next;   newLine = addLineTmpl(new, tmplLine, newLine,
3870      }        newKernelPath + strlen(prefix), config->cfi);
3871     needs &= ~NEED_KERNEL;
3872        }
3873        if (needs & NEED_MB) { /* !mbHyperFirst */
3874     newLine = addLine(new, config->cfi, LT_HYPER,
3875      config->secondaryIndent,
3876      newMBKernel + strlen(prefix));
3877     needs &= ~NEED_MB;
3878        }
3879     } else if (needs & NEED_KERNEL) {
3880        newLine = addLineTmpl(new, tmplLine, newLine,
3881      newKernelPath + strlen(prefix), config->cfi);
3882        needs &= ~NEED_KERNEL;
3883     }
3884    
3885        } else if (tmplLine->type == LT_HYPER &&
3886           tmplLine->numElements >= 2) {
3887     if (needs & NEED_MB) {
3888        newLine = addLineTmpl(new, tmplLine, newLine,
3889      newMBKernel + strlen(prefix), config->cfi);
3890        needs &= ~NEED_MB;
3891     }
3892    
3893      newLine->indent = strdup(tmplLine->indent);      } else if (tmplLine->type == LT_MBMODULE &&
3894      newLine->next = NULL;         tmplLine->numElements >= 2) {
3895      newLine->type = tmplLine->type;   if (new->multiboot) {
3896      newLine->numElements = tmplLine->numElements;      if (needs & NEED_KERNEL) {
3897      newLine->elements = malloc(sizeof(*newLine->elements) *   newLine = addLineTmpl(new, tmplLine, newLine,
3898      newLine->numElements);        newKernelPath +
3899      for (i = 0; i < newLine->numElements; i++) {        strlen(prefix), config->cfi);
3900   newLine->elements[i].item = strdup(tmplLine->elements[i].item);   needs &= ~NEED_KERNEL;
3901   newLine->elements[i].indent =      } else if (config->cfi->mbInitRdIsModule &&
3902   strdup(tmplLine->elements[i].indent);         (needs & NEED_INITRD)) {
3903      }   char *initrdVal;
3904     initrdVal = getInitrdVal(config, prefix, tmplLine,
3905              lastLine = tmplLine;   newKernelInitrd, extraInitrds,
3906      if (tmplLine->type == LT_KERNEL && tmplLine->numElements >= 2) {   extraInitrdCount);
3907                  char * repl;   newLine = addLineTmpl(new, tmplLine, newLine,
3908                  if (!template->multiboot) {        initrdVal, config->cfi);
3909                      needs &= ~KERNEL_KERNEL;   free(initrdVal);
3910                      repl = newKernelPath;   needs &= ~NEED_INITRD;
3911                  } else {      }
3912                      needs &= ~KERNEL_MB;   } else if (needs & NEED_KERNEL) {
3913                      repl = newMBKernel;      /* template is multi but new is not,
3914                  }       * insert the kernel in the first module slot
3915                  if (new->multiboot && !template->multiboot) {       */
3916                      free(newLine->elements[0].item);      tmplLine->type = preferredLineType(LT_KERNEL, config->cfi);
3917                      newLine->elements[0].item = strdup("module");      free(tmplLine->elements[0].item);
3918                      newLine->type = LT_MBMODULE;      tmplLine->elements[0].item =
3919                  }   strdup(getKeywordByType(tmplLine->type,
3920   free(newLine->elements[1].item);   config->cfi)->key);
3921                  rootspec = getRootSpecifier(tmplLine->elements[1].item);      newLine = addLineTmpl(new, tmplLine, newLine,
3922                  if (rootspec != NULL) {    newKernelPath + strlen(prefix),
3923                      newLine->elements[1].item = sdupprintf("%s%s",    config->cfi);
3924                                                             rootspec,      needs &= ~NEED_KERNEL;
3925                                                             repl +   } else if (needs & NEED_INITRD) {
3926                                                             strlen(prefix));      char *initrdVal;
3927                  } else {      /* template is multi but new is not,
3928                      newLine->elements[1].item = strdup(repl +       * insert the initrd in the second module slot
3929                                                         strlen(prefix));       */
3930                  }      tmplLine->type = preferredLineType(LT_INITRD, config->cfi);
3931              } else if (tmplLine->type == LT_MBMODULE &&      free(tmplLine->elements[0].item);
3932                         tmplLine->numElements >= 2 && (needs & KERNEL_KERNEL)) {      tmplLine->elements[0].item =
3933                  needs &= ~KERNEL_KERNEL;   strdup(getKeywordByType(tmplLine->type,
3934                  if (!new->multiboot && template->multiboot) {   config->cfi)->key);
3935                      free(newLine->elements[0].item);      initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3936                      newLine->elements[0].item = strdup("kernel");      newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3937                      newLine->type = LT_KERNEL;      free(initrdVal);
3938                  }      needs &= ~NEED_INITRD;
3939   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;  
3940    
3941   for (i = 1; i < newLine->numElements; i++) {      } else if (isinitrd(tmplLine->type) && tmplLine->numElements >= 2) {
3942      free(newLine->elements[i].item);   if (needs & NEED_INITRD &&
3943      free(newLine->elements[i].indent);      new->multiboot && !template->multiboot &&
3944        config->cfi->mbInitRdIsModule) {
3945        /* make sure we don't insert the module initrd
3946         * before the module kernel... if we don't do it here,
3947         * it will be inserted following the template.
3948         */
3949        if (!needs & NEED_KERNEL) {
3950     char *initrdVal;
3951    
3952     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3953     newLine = addLine(new, config->cfi, LT_MBMODULE,
3954      config->secondaryIndent,
3955      initrdVal);
3956     free(initrdVal);
3957     needs &= ~NEED_INITRD;
3958        }
3959     } else if (needs & NEED_INITRD) {
3960        char *initrdVal;
3961        initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3962        newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3963        free(initrdVal);
3964        needs &= ~NEED_INITRD;
3965   }   }
3966    
3967   newLine->elements[1].item = strdup(newKernelTitle);      } else if (tmplLine->type == LT_MENUENTRY &&
3968   newLine->elements[1].indent = strdup("");         (needs & NEED_TITLE)) {
3969   newLine->numElements = 2;   requote(tmplLine, config->cfi);
3970     char *nkt = malloc(strlen(newKernelTitle)+3);
3971     strcpy(nkt, "'");
3972     strcat(nkt, newKernelTitle);
3973     strcat(nkt, "'");
3974     newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
3975     free(nkt);
3976     needs &= ~NEED_TITLE;
3977      } else if (tmplLine->type == LT_TITLE &&      } else if (tmplLine->type == LT_TITLE &&
3978                         config->cfi->titleBracketed &&         (needs & NEED_TITLE)) {
3979                         tmplLine->numElements == 1) {   if (tmplLine->numElements >= 2) {
3980                  needs &= ~KERNEL_TITLE;      newLine = addLineTmpl(new, tmplLine, newLine,
3981                  free(newLine->elements[0].item);    newKernelTitle, config->cfi);
3982                  free(newLine->elements[0].indent);      needs &= ~NEED_TITLE;
3983                  newLine->elements = malloc(sizeof(*newLine->elements) *   } else if (tmplLine->numElements == 1 &&
3984                                             newLine->numElements);     config->cfi->titleBracketed) {
3985        /* addLineTmpl doesn't handle titleBracketed */
3986                  newLine->elements[0].item = malloc(strlen(newKernelTitle) + 3);      newLine = addLine(new, config->cfi, LT_TITLE,
3987                  sprintf(newLine->elements[0].item, "[%s]", newKernelTitle);        tmplLine->indent, newKernelTitle);
3988                  newLine->elements[0].indent = strdup("");      needs &= ~NEED_TITLE;
3989                  newLine->numElements = 1;   }
3990              }      } else if (tmplLine->type == LT_ECHO) {
3991        requote(tmplLine, config->cfi);
3992        static const char *prefix = "'Loading ";
3993        if (tmplLine->numElements > 1 &&
3994        strstr(tmplLine->elements[1].item, prefix) &&
3995        masterLine->next &&
3996        iskernel(masterLine->next->type)) {
3997     char *newTitle = malloc(strlen(prefix) +
3998     strlen(newKernelTitle) + 2);
3999    
4000     strcpy(newTitle, prefix);
4001     strcat(newTitle, newKernelTitle);
4002     strcat(newTitle, "'");
4003     newLine = addLine(new, config->cfi, LT_ECHO,
4004     tmplLine->indent, newTitle);
4005     free(newTitle);
4006        } else {
4007     /* pass through other lines from the template */
4008     newLine = addLineTmpl(new, tmplLine, newLine, NULL,
4009     config->cfi);
4010        }
4011        } else if (tmplLine->type == LT_DEVTREE &&
4012           tmplLine->numElements == 2 && newDevTreePath) {
4013            newLine = addLineTmpl(new, tmplLine, newLine,
4014          newDevTreePath + strlen(prefix),
4015          config->cfi);
4016     needs &= ~NEED_DEVTREE;
4017        } else if (tmplLine->type == LT_ENTRY_END && needs & NEED_DEVTREE) {
4018     const char *ndtp = newDevTreePath;
4019     if (!strncmp(newDevTreePath, prefix, strlen(prefix)))
4020        ndtp += strlen(prefix);
4021     newLine = addLine(new, config->cfi, LT_DEVTREE,
4022      config->secondaryIndent,
4023      ndtp);
4024     needs &= ~NEED_DEVTREE;
4025     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
4026        } else {
4027     /* pass through other lines from the template */
4028     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
4029        }
4030   }   }
4031    
4032      } else {      } else {
4033   for (i = 0; config->cfi->keywords[i].key; i++) {   /* don't have a template, so start the entry with the
4034      if ((config->cfi->keywords[i].type == config->cfi->entrySeparator) || (config->cfi->keywords[i].type == LT_OTHER))   * appropriate starting line
4035     */
4036     switch (config->cfi->entryStart) {
4037        case LT_KERNEL:
4038        case LT_KERNEL_EFI:
4039        case LT_KERNEL_16:
4040     if (new->multiboot && config->cfi->mbHyperFirst) {
4041        /* fall through to LT_HYPER */
4042     } else {
4043        newLine = addLine(new, config->cfi,
4044              preferredLineType(LT_KERNEL, config->cfi),
4045          config->primaryIndent,
4046          newKernelPath + strlen(prefix));
4047        needs &= ~NEED_KERNEL;
4048        break;
4049     }
4050    
4051        case LT_HYPER:
4052     newLine = addLine(new, config->cfi, LT_HYPER,
4053      config->primaryIndent,
4054      newMBKernel + strlen(prefix));
4055     needs &= ~NEED_MB;
4056   break;   break;
         }  
4057    
4058   switch (config->cfi->keywords[i].type) {      case LT_MENUENTRY: {
4059      case LT_KERNEL:  needs &= ~KERNEL_KERNEL,   char *nkt = malloc(strlen(newKernelTitle)+3);
4060       chptr = newKernelPath + strlen(prefix);   strcpy(nkt, "'");
4061       type = LT_KERNEL; break;   strcat(nkt, newKernelTitle);
4062      case LT_TITLE:   needs &= ~KERNEL_TITLE, chptr = newKernelTitle;   strcat(nkt, "'");
4063       type = LT_TITLE; break;          newLine = addLine(new, config->cfi, LT_MENUENTRY,
4064      default:        config->primaryIndent, nkt);
4065                  /* zipl strikes again */   free(nkt);
4066                  if (config->cfi->titleBracketed) {   needs &= ~NEED_TITLE;
4067                      needs &= ~KERNEL_TITLE;   needs |= NEED_END;
4068                      chptr = newKernelTitle;   break;
4069                      type = LT_TITLE;      }
4070                      break;      case LT_TITLE:
4071                  } else {   if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
4072                      abort();   char * templabel;
4073                  }   int x = 0, y = 0;
4074   }  
4075     templabel = strdup(newKernelTitle);
4076     while( templabel[x]){
4077     if( templabel[x] == ' ' ){
4078     y = x;
4079     while( templabel[y] ){
4080     templabel[y] = templabel[y+1];
4081     y++;
4082     }
4083     }
4084     x++;
4085     }
4086     newLine = addLine(new, config->cfi, LT_TITLE,
4087      config->primaryIndent, templabel);
4088     free(templabel);
4089     }else{
4090     newLine = addLine(new, config->cfi, LT_TITLE,
4091      config->primaryIndent, newKernelTitle);
4092     }
4093     needs &= ~NEED_TITLE;
4094     break;
4095    
4096   newLine = addLine(new, config->cfi, type, config->primaryIndent, chptr);      default:
4097   new->lines = newLine;   abort();
4098     }
4099      }      }
4100    
4101      if (new->multiboot) {      struct singleLine *endLine = NULL;
4102          if (needs & KERNEL_MB)      endLine = getLineByType(LT_ENTRY_END, new->lines);
4103              newLine = addLine(new, config->cfi, LT_KERNEL,      if (endLine) {
4104                                config->secondaryIndent,      removeLine(new, endLine);
4105                                newMBKernel + strlen(prefix));      needs |= NEED_END;
4106          if (needs & KERNEL_KERNEL)      }
4107              newLine = addLine(new, config->cfi, LT_MBMODULE,  
4108                                config->secondaryIndent,      /* add the remainder of the lines, i.e. those that either
4109                                newKernelPath + strlen(prefix));       * weren't present in the template, or in the case of no template,
4110          /* don't need to check for title as it's guaranteed to have been       * all the lines following the entryStart.
4111           * done as we only do multiboot with grub which uses title as       */
4112           * a separator */      if (needs & NEED_TITLE) {
4113          if (needs & KERNEL_INITRD && newKernelInitrd)   newLine = addLine(new, config->cfi, LT_TITLE,
4114              newLine = addLine(new, config->cfi, LT_MBMODULE,    config->secondaryIndent,
4115                                config->secondaryIndent,    newKernelTitle);
4116                                newKernelInitrd + strlen(prefix));   needs &= ~NEED_TITLE;
4117      } else {      }
4118          if (needs & KERNEL_KERNEL)      if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
4119              newLine = addLine(new, config->cfi, LT_KERNEL,   newLine = addLine(new, config->cfi, LT_HYPER,
4120                                config->secondaryIndent,    config->secondaryIndent,
4121                                newKernelPath + strlen(prefix));    newMBKernel + strlen(prefix));
4122          if (needs & KERNEL_TITLE)   needs &= ~NEED_MB;
4123              newLine = addLine(new, config->cfi, LT_TITLE,      }
4124                                config->secondaryIndent,      if (needs & NEED_KERNEL) {
4125                                newKernelTitle);   newLine = addLine(new, config->cfi,
4126          if (needs & KERNEL_INITRD && newKernelInitrd)    (new->multiboot && getKeywordByType(LT_MBMODULE,
4127              newLine = addLine(new, config->cfi, LT_INITRD,        config->cfi))
4128                                config->secondaryIndent,     ? LT_MBMODULE
4129                                newKernelInitrd + strlen(prefix));   : preferredLineType(LT_KERNEL, config->cfi),
4130      config->secondaryIndent,
4131      newKernelPath + strlen(prefix));
4132     needs &= ~NEED_KERNEL;
4133        }
4134        if (needs & NEED_MB) {
4135     newLine = addLine(new, config->cfi, LT_HYPER,
4136      config->secondaryIndent,
4137      newMBKernel + strlen(prefix));
4138     needs &= ~NEED_MB;
4139        }
4140        if (needs & NEED_INITRD) {
4141     char *initrdVal;
4142     initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
4143     newLine = addLine(new, config->cfi,
4144      (new->multiboot && getKeywordByType(LT_MBMODULE,
4145          config->cfi))
4146       ? LT_MBMODULE
4147       : preferredLineType(LT_INITRD, config->cfi),
4148      config->secondaryIndent,
4149      initrdVal);
4150     free(initrdVal);
4151     needs &= ~NEED_INITRD;
4152        }
4153        if (needs & NEED_DEVTREE) {
4154     newLine = addLine(new, config->cfi, LT_DEVTREE,
4155      config->secondaryIndent,
4156      newDevTreePath);
4157     needs &= ~NEED_DEVTREE;
4158        }
4159    
4160        /* NEEDS_END must be last on bootloaders that need it... */
4161        if (needs & NEED_END) {
4162     newLine = addLine(new, config->cfi, LT_ENTRY_END,
4163     config->secondaryIndent, NULL);
4164     needs &= ~NEED_END;
4165        }
4166    
4167        if (needs) {
4168     printf(_("grubby: needs=%d, aborting\n"), needs);
4169     abort();
4170      }      }
4171    
4172      if (updateImage(config, "0", prefix, newKernelArgs, NULL,      if (updateImage(config, "0", prefix, newKernelArgs, NULL,
# Line 2274  int addNewKernel(struct grubConfig * con Line 4175  int addNewKernel(struct grubConfig * con
4175      return 0;      return 0;
4176  }  }
4177    
4178    static void traceback(int signum)
4179    {
4180        void *array[40];
4181        size_t size;
4182    
4183        signal(SIGSEGV, SIG_DFL);
4184        memset(array, '\0', sizeof (array));
4185        size = backtrace(array, 40);
4186    
4187        fprintf(stderr, "grubby received SIGSEGV!  Backtrace (%ld):\n",
4188                (unsigned long)size);
4189        backtrace_symbols_fd(array, size, STDERR_FILENO);
4190        exit(1);
4191    }
4192    
4193  int main(int argc, const char ** argv) {  int main(int argc, const char ** argv) {
4194      poptContext optCon;      poptContext optCon;
4195      char * grubConfig = NULL;      const char * grubConfig = NULL;
4196      char * outputFile = NULL;      char * outputFile = NULL;
4197      int arg = 0;      int arg = 0;
4198      int flags = 0;      int flags = 0;
4199      int badImageOkay = 0;      int badImageOkay = 0;
4200        int configureGrub2 = 0;
4201      int configureLilo = 0, configureELilo = 0, configureGrub = 0;      int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4202      int configureYaboot = 0, configureSilo = 0, configureZipl = 0;      int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4203        int configureExtLinux = 0;
4204      int bootloaderProbe = 0;      int bootloaderProbe = 0;
4205        int extraInitrdCount = 0;
4206      char * updateKernelPath = NULL;      char * updateKernelPath = NULL;
4207      char * newKernelPath = NULL;      char * newKernelPath = NULL;
4208      char * removeKernelPath = NULL;      char * removeKernelPath = NULL;
4209      char * newKernelArgs = NULL;      char * newKernelArgs = NULL;
4210      char * newKernelInitrd = NULL;      char * newKernelInitrd = NULL;
4211      char * newKernelTitle = NULL;      char * newKernelTitle = NULL;
4212      char * newKernelVersion = NULL;      char * newDevTreePath = NULL;
4213      char * newMBKernel = NULL;      char * newMBKernel = NULL;
4214      char * newMBKernelArgs = NULL;      char * newMBKernelArgs = NULL;
4215      char * removeMBKernelArgs = NULL;      char * removeMBKernelArgs = NULL;
# Line 2299  int main(int argc, const char ** argv) { Line 4218  int main(int argc, const char ** argv) {
4218      char * defaultKernel = NULL;      char * defaultKernel = NULL;
4219      char * removeArgs = NULL;      char * removeArgs = NULL;
4220      char * kernelInfo = NULL;      char * kernelInfo = NULL;
4221        char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4222        char * envPath = NULL;
4223      const char * chptr = NULL;      const char * chptr = NULL;
4224      struct configFileInfo * cfi = NULL;      struct configFileInfo * cfi = NULL;
4225      struct grubConfig * config;      struct grubConfig * config;
4226      struct singleEntry * template = NULL;      struct singleEntry * template = NULL;
4227      int copyDefault = 0, makeDefault = 0;      int copyDefault = 0, makeDefault = 0;
4228      int displayDefault = 0;      int displayDefault = 0;
4229        int displayDefaultIndex = 0;
4230        int displayDefaultTitle = 0;
4231        int defaultIndex = -1;
4232      struct poptOption options[] = {      struct poptOption options[] = {
4233   { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,   { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4234      _("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 4246  int main(int argc, const char ** argv) {
4246   { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,   { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4247      _("filestystem which contains /boot directory (for testing only)"),      _("filestystem which contains /boot directory (for testing only)"),
4248      _("bootfs") },      _("bootfs") },
4249  #if defined(__i386__) || defined(__x86_64__)  #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4250   { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,   { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4251      _("check if lilo is installed on lilo.conf boot sector") },      _("check which bootloader is installed on boot sector") },
4252  #endif  #endif
4253   { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,   { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4254      _("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 4259  int main(int argc, const char ** argv) {
4259        "the kernel referenced by the default image does not exist, "        "the kernel referenced by the default image does not exist, "
4260        "the first linux entry whose kernel does exist is used as the "        "the first linux entry whose kernel does exist is used as the "
4261        "template"), NULL },        "template"), NULL },
4262     { "debug", 0, 0, &debug, 0,
4263        _("print debugging information for failures") },
4264   { "default-kernel", 0, 0, &displayDefault, 0,   { "default-kernel", 0, 0, &displayDefault, 0,
4265      _("display the path of the default kernel") },      _("display the path of the default kernel") },
4266     { "default-index", 0, 0, &displayDefaultIndex, 0,
4267        _("display the index of the default kernel") },
4268     { "default-title", 0, 0, &displayDefaultTitle, 0,
4269        _("display the title of the default kernel") },
4270     { "devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4271        _("device tree file for new stanza"), _("dtb-path") },
4272   { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,   { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
4273      _("configure elilo bootloader") },      _("configure elilo bootloader") },
4274     { "efi", 0, POPT_ARG_NONE, &isEfi, 0,
4275        _("force grub2 stanzas to use efi") },
4276     { "env", 0, POPT_ARG_STRING, &envPath, 0,
4277        _("path for environment data"),
4278        _("path") },
4279     { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
4280        _("configure extlinux bootloader (from syslinux)") },
4281   { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,   { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
4282      _("configure grub bootloader") },      _("configure grub bootloader") },
4283     { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
4284        _("configure grub2 bootloader") },
4285   { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,   { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
4286      _("display boot information for specified kernel"),      _("display boot information for specified kernel"),
4287      _("kernel-path") },      _("kernel-path") },
4288   { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,   { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
4289      _("initrd image for the new kernel"), _("initrd-path") },      _("initrd image for the new kernel"), _("initrd-path") },
4290     { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
4291        _("auxiliary initrd image for things other than the new kernel"), _("initrd-path") },
4292   { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,   { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
4293      _("configure lilo bootloader") },      _("configure lilo bootloader") },
4294   { "make-default", 0, 0, &makeDefault, 0,   { "make-default", 0, 0, &makeDefault, 0,
# Line 2365  int main(int argc, const char ** argv) { Line 4308  int main(int argc, const char ** argv) {
4308   { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,   { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
4309      _("make the first entry referencing the specified kernel "      _("make the first entry referencing the specified kernel "
4310        "the default"), _("kernel-path") },        "the default"), _("kernel-path") },
4311     { "set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
4312        _("make the given entry index the default entry"),
4313        _("entry-index") },
4314   { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,   { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
4315      _("configure silo bootloader") },      _("configure silo bootloader") },
4316   { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,   { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
# Line 2382  int main(int argc, const char ** argv) { Line 4328  int main(int argc, const char ** argv) {
4328   { 0, 0, 0, 0, 0 }   { 0, 0, 0, 0, 0 }
4329      };      };
4330    
4331        useextlinuxmenu=0;
4332    
4333        signal(SIGSEGV, traceback);
4334    
4335        int i = 0;
4336        for (int j = 1; j < argc; j++)
4337     i += strlen(argv[j]) + 1;
4338        saved_command_line = malloc(i);
4339        if (!saved_command_line) {
4340     fprintf(stderr, "grubby: %m\n");
4341     exit(1);
4342        }
4343        saved_command_line[0] = '\0';
4344        for (int j = 1; j < argc; j++) {
4345     strcat(saved_command_line, argv[j]);
4346     strncat(saved_command_line, j == argc -1 ? "" : " ", 1);
4347        }
4348    
4349      optCon = poptGetContext("grubby", argc, argv, options, 0);      optCon = poptGetContext("grubby", argc, argv, options, 0);
4350      poptReadDefaultConfig(optCon, 1);      poptReadDefaultConfig(optCon, 1);
4351    
# Line 2391  int main(int argc, const char ** argv) { Line 4355  int main(int argc, const char ** argv) {
4355      printf("grubby version %s\n", VERSION);      printf("grubby version %s\n", VERSION);
4356      exit(0);      exit(0);
4357      break;      break;
4358      case 'i':
4359        if (extraInitrdCount < MAX_EXTRA_INITRDS) {
4360         extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
4361        } else {
4362     fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
4363     return 1;
4364        }
4365        break;
4366   }   }
4367      }      }
4368    
# Line 2406  int main(int argc, const char ** argv) { Line 4378  int main(int argc, const char ** argv) {
4378   return 1;   return 1;
4379      }      }
4380    
4381      if ((configureLilo + configureGrub + configureELilo +      if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
4382   configureYaboot + configureSilo + configureZipl) > 1) {   configureYaboot + configureSilo + configureZipl +
4383     configureExtLinux ) > 1) {
4384   fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));   fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
4385   return 1;   return 1;
4386      } else if (bootloaderProbe && grubConfig) {      } else if (bootloaderProbe && grubConfig) {
4387   fprintf(stderr,   fprintf(stderr,
4388      _("grubby: cannot specify config file with --bootloader-probe\n"));      _("grubby: cannot specify config file with --bootloader-probe\n"));
4389   return 1;   return 1;
4390        } else if (configureGrub2) {
4391     cfi = &grub2ConfigType;
4392     if (envPath)
4393        cfi->envFile = envPath;
4394      } else if (configureLilo) {      } else if (configureLilo) {
4395   cfi = &liloConfigType;   cfi = &liloConfigType;
4396      } else if (configureGrub) {      } else if (configureGrub) {
# Line 2426  int main(int argc, const char ** argv) { Line 4403  int main(int argc, const char ** argv) {
4403          cfi = &siloConfigType;          cfi = &siloConfigType;
4404      } else if (configureZipl) {      } else if (configureZipl) {
4405          cfi = &ziplConfigType;          cfi = &ziplConfigType;
4406        } else if (configureExtLinux) {
4407     cfi = &extlinuxConfigType;
4408     useextlinuxmenu=1;
4409      }      }
4410    
4411      if (!cfi) {      if (!cfi) {
4412            if (grub2FindConfig(&grub2ConfigType))
4413        cfi = &grub2ConfigType;
4414     else
4415        #ifdef __ia64__        #ifdef __ia64__
4416   cfi = &eliloConfigType;      cfi = &eliloConfigType;
4417        #elif __powerpc__        #elif __powerpc__
4418   cfi = &yabootConfigType;      cfi = &yabootConfigType;
4419        #elif __sparc__        #elif __sparc__
4420          cfi = &siloConfigType;              cfi = &siloConfigType;
4421        #elif __s390__        #elif __s390__
4422          cfi = &ziplConfigType;              cfi = &ziplConfigType;
4423        #elif __s390x__        #elif __s390x__
4424          cfi = &ziplConfigtype;              cfi = &ziplConfigtype;
4425        #else        #else
4426   cfi = &grubConfigType;      cfi = &grubConfigType;
4427        #endif        #endif
4428      }      }
4429    
4430      if (!grubConfig)      if (!grubConfig) {
4431   grubConfig = cfi->defaultConfig;   if (cfi->findConfig)
4432        grubConfig = cfi->findConfig(cfi);
4433     if (!grubConfig)
4434        grubConfig = cfi->defaultConfig;
4435        }
4436    
4437      if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||      if (bootloaderProbe && (displayDefault || kernelInfo ||
4438    newKernelPath || removeKernelPath || makeDefault ||      newKernelPath || removeKernelPath || makeDefault ||
4439    defaultKernel)) {      defaultKernel || displayDefaultIndex || displayDefaultTitle ||
4440        (defaultIndex >= 0))) {
4441   fprintf(stderr, _("grubby: --bootloader-probe may not be used with "   fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
4442    "specified option"));    "specified option"));
4443   return 1;   return 1;
4444      }      }
4445    
4446      if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||      if ((displayDefault || kernelInfo) && (newKernelPath ||
4447     removeKernelPath)) {     removeKernelPath)) {
4448   fprintf(stderr, _("grubby: --default-kernel and --info may not "   fprintf(stderr, _("grubby: --default-kernel and --info may not "
4449    "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 4453  int main(int argc, const char ** argv) {
4453      if (newKernelPath && !newKernelTitle) {      if (newKernelPath && !newKernelTitle) {
4454   fprintf(stderr, _("grubby: kernel title must be specified\n"));   fprintf(stderr, _("grubby: kernel title must be specified\n"));
4455   return 1;   return 1;
4456      } else if (!newKernelPath && (newKernelTitle  || newKernelInitrd ||      } else if (!newKernelPath && (copyDefault ||
4457    newKernelInitrd || copyDefault     ||    (newKernelInitrd && !updateKernelPath)||
4458    makeDefault)) {    makeDefault || extraInitrdCount > 0)) {
4459   fprintf(stderr, _("grubby: kernel path expected\n"));   fprintf(stderr, _("grubby: kernel path expected\n"));
4460   return 1;   return 1;
4461      }      }
# Line 2491  int main(int argc, const char ** argv) { Line 4479  int main(int argc, const char ** argv) {
4479   makeDefault = 1;   makeDefault = 1;
4480   defaultKernel = NULL;   defaultKernel = NULL;
4481      }      }
4482        else if (defaultKernel && (defaultIndex >= 0)) {
4483     fprintf(stderr, _("grubby: --set-default and --set-default-index "
4484      "may not be used together\n"));
4485     return 1;
4486        }
4487    
4488      if (!strcmp(grubConfig, "-") && !outputFile) {      if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
4489   fprintf(stderr, _("grubby: output file must be specified if stdin "   fprintf(stderr, _("grubby: output file must be specified if stdin "
4490   "is used\n"));   "is used\n"));
4491   return 1;   return 1;
4492      }      }
4493    
4494      if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel      if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
4495   && !kernelInfo && !bootloaderProbe && !updateKernelPath   && !kernelInfo && !bootloaderProbe && !updateKernelPath
4496          && !removeMBKernel) {   && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle
4497     && (defaultIndex == -1)) {
4498   fprintf(stderr, _("grubby: no action specified\n"));   fprintf(stderr, _("grubby: no action specified\n"));
4499   return 1;   return 1;
4500      }      }
# Line 2520  int main(int argc, const char ** argv) { Line 4514  int main(int argc, const char ** argv) {
4514   bootPrefix = "";   bootPrefix = "";
4515      }      }
4516    
4517        if (!cfi->mbAllowExtraInitRds &&
4518     extraInitrdCount > 0) {
4519     fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
4520     return 1;
4521        }
4522    
4523      if (bootloaderProbe) {      if (bootloaderProbe) {
4524   int lrc = 0, grc = 0;   int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
4525   struct grubConfig * lconfig, * gconfig;   struct grubConfig * lconfig, * gconfig, * yconfig, * econfig;
4526    
4527     const char *grub2config = grub2FindConfig(&grub2ConfigType);
4528     if (grub2config) {
4529        gconfig = readConfig(grub2config, &grub2ConfigType);
4530        if (!gconfig)
4531     gr2c = 1;
4532        else
4533     gr2c = checkForGrub2(gconfig);
4534     }
4535    
4536   if (!access(grubConfigType.defaultConfig, F_OK)) {   const char *grubconfig = grubFindConfig(&grubConfigType);
4537      gconfig = readConfig(grubConfigType.defaultConfig, &grubConfigType);   if (!access(grubconfig, F_OK)) {
4538        gconfig = readConfig(grubconfig, &grubConfigType);
4539      if (!gconfig)      if (!gconfig)
4540   grc = 1;   grc = 1;
4541      else      else
# Line 2540  int main(int argc, const char ** argv) { Line 4550  int main(int argc, const char ** argv) {
4550   lrc = checkForLilo(lconfig);   lrc = checkForLilo(lconfig);
4551   }   }
4552    
4553   if (lrc == 1 || grc == 1) return 1;   if (!access(eliloConfigType.defaultConfig, F_OK)) {
4554        econfig = readConfig(eliloConfigType.defaultConfig,
4555     &eliloConfigType);
4556        if (!econfig)
4557     erc = 1;
4558        else
4559     erc = checkForElilo(econfig);
4560     }
4561    
4562     if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
4563        lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
4564        if (!lconfig)
4565     extrc = 1;
4566        else
4567     extrc = checkForExtLinux(lconfig);
4568     }
4569    
4570    
4571     if (!access(yabootConfigType.defaultConfig, F_OK)) {
4572        yconfig = readConfig(yabootConfigType.defaultConfig,
4573     &yabootConfigType);
4574        if (!yconfig)
4575     yrc = 1;
4576        else
4577     yrc = checkForYaboot(yconfig);
4578     }
4579    
4580     if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1 ||
4581     erc == 1)
4582        return 1;
4583    
4584   if (lrc == 2) printf("lilo\n");   if (lrc == 2) printf("lilo\n");
4585     if (gr2c == 2) printf("grub2\n");
4586   if (grc == 2) printf("grub\n");   if (grc == 2) printf("grub\n");
4587     if (extrc == 2) printf("extlinux\n");
4588     if (yrc == 2) printf("yaboot\n");
4589     if (erc == 2) printf("elilo\n");
4590    
4591   return 0;   return 0;
4592      }      }
4593    
4594        if (grubConfig == NULL) {
4595     printf("Could not find bootloader configuration file.\n");
4596     exit(1);
4597        }
4598    
4599      config = readConfig(grubConfig, cfi);      config = readConfig(grubConfig, cfi);
4600      if (!config) return 1;      if (!config) return 1;
4601    
# Line 2557  int main(int argc, const char ** argv) { Line 4605  int main(int argc, const char ** argv) {
4605          char * rootspec;          char * rootspec;
4606    
4607   if (config->defaultImage == -1) return 0;   if (config->defaultImage == -1) return 0;
4608     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4609     cfi->defaultIsSaved)
4610        config->defaultImage = 0;
4611   entry = findEntryByIndex(config, config->defaultImage);   entry = findEntryByIndex(config, config->defaultImage);
4612   if (!entry) return 0;   if (!entry) return 0;
4613   if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;   if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
4614    
4615   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;  
4616   if (!line) return 0;   if (!line) return 0;
4617    
4618          rootspec = getRootSpecifier(line->elements[1].item);          rootspec = getRootSpecifier(line->elements[1].item);
# Line 2570  int main(int argc, const char ** argv) { Line 4620  int main(int argc, const char ** argv) {
4620                 ((rootspec != NULL) ? strlen(rootspec) : 0));                 ((rootspec != NULL) ? strlen(rootspec) : 0));
4621    
4622   return 0;   return 0;
4623    
4624        } else if (displayDefaultTitle) {
4625     struct singleLine * line;
4626     struct singleEntry * entry;
4627    
4628     if (config->defaultImage == -1) return 0;
4629     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4630     cfi->defaultIsSaved)
4631        config->defaultImage = 0;
4632     entry = findEntryByIndex(config, config->defaultImage);
4633     if (!entry) return 0;
4634    
4635     if (!configureGrub2) {
4636      line = getLineByType(LT_TITLE, entry->lines);
4637      if (!line) return 0;
4638      printf("%s\n", line->elements[1].item);
4639    
4640     } else {
4641      char * title;
4642    
4643      dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
4644      line = getLineByType(LT_MENUENTRY, entry->lines);
4645      if (!line) return 0;
4646      title = grub2ExtractTitle(line);
4647      if (title)
4648        printf("%s\n", title);
4649     }
4650     return 0;
4651    
4652        } else if (displayDefaultIndex) {
4653            if (config->defaultImage == -1) return 0;
4654     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4655     cfi->defaultIsSaved)
4656        config->defaultImage = 0;
4657            printf("%i\n", config->defaultImage);
4658            return 0;
4659    
4660      } else if (kernelInfo)      } else if (kernelInfo)
4661   return displayInfo(config, kernelInfo, bootPrefix);   return displayInfo(config, kernelInfo, bootPrefix);
4662    
# Line 2581  int main(int argc, const char ** argv) { Line 4668  int main(int argc, const char ** argv) {
4668      markRemovedImage(config, removeKernelPath, bootPrefix);      markRemovedImage(config, removeKernelPath, bootPrefix);
4669      markRemovedImage(config, removeMBKernel, bootPrefix);      markRemovedImage(config, removeMBKernel, bootPrefix);
4670      setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,      setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
4671      bootPrefix, flags);      bootPrefix, flags, defaultIndex);
4672      setFallbackImage(config, newKernelPath != NULL);      setFallbackImage(config, newKernelPath != NULL);
4673      if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,      if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
4674                      removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;                      removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
4675        if (updateKernelPath && newKernelInitrd) {
4676        if (newMBKernel) {
4677        if (addMBInitrd(config, newMBKernel, updateKernelPath,
4678     bootPrefix, newKernelInitrd,
4679     newKernelTitle))
4680        return 1;
4681        } else {
4682        if (updateInitrd(config, updateKernelPath, bootPrefix,
4683     newKernelInitrd, newKernelTitle))
4684     return 1;
4685        }
4686        }
4687      if (addNewKernel(config, template, bootPrefix, newKernelPath,      if (addNewKernel(config, template, bootPrefix, newKernelPath,
4688                       newKernelTitle, newKernelArgs, newKernelInitrd,                       newKernelTitle, newKernelArgs, newKernelInitrd,
4689                       newMBKernel, newMBKernelArgs)) return 1;                       (const char **)extraInitrds, extraInitrdCount,
4690                         newMBKernel, newMBKernelArgs, newDevTreePath)) return 1;
4691            
4692    
4693      if (numEntries(config) == 0) {      if (numEntries(config) == 0) {
# Line 2597  int main(int argc, const char ** argv) { Line 4697  int main(int argc, const char ** argv) {
4697      }      }
4698    
4699      if (!outputFile)      if (!outputFile)
4700   outputFile = grubConfig;   outputFile = (char *)grubConfig;
4701    
4702      return writeConfig(config, outputFile, bootPrefix);      return writeConfig(config, outputFile, bootPrefix);
4703  }  }

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