Make grub-install check for errors from efibootmgr
[grub.git] / grub-core / osdep / unix / platform.c
1 /*
2  *  GRUB  --  GRand Unified Bootloader
3  *  Copyright (C) 2013 Free Software Foundation, Inc.
4  *
5  *  GRUB is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  GRUB is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <config.h>
20
21 #include <grub/util/install.h>
22 #include <grub/emu/hostdisk.h>
23 #include <grub/util/misc.h>
24 #include <grub/misc.h>
25 #include <grub/i18n.h>
26 #include <grub/emu/exec.h>
27 #include <sys/types.h>
28 #include <dirent.h>
29 #include <string.h>
30 #include <errno.h>
31
32 static char *
33 get_ofpathname (const char *dev)
34 {
35   size_t alloced = 4096;
36   char *ret = xmalloc (alloced);
37   size_t offset = 0;
38   int fd;
39   pid_t pid;
40
41   pid = grub_util_exec_pipe ((const char * []){ "ofpathname", dev, NULL }, &fd);
42   if (!pid)
43     goto fail;
44
45   FILE *fp = fdopen (fd, "r");
46   if (!fp)
47     goto fail;
48
49   while (!feof (fp))
50     {
51       size_t r;
52       if (alloced == offset)
53        {
54          alloced *= 2;
55          ret = xrealloc (ret, alloced);
56        }
57       r = fread (ret + offset, 1, alloced - offset, fp);
58       offset += r;
59     }
60
61   if (offset > 0 && ret[offset - 1] == '\n')
62     offset--;
63   if (offset > 0 && ret[offset - 1] == '\r')
64     offset--;
65   if (alloced == offset)
66     {
67       alloced++;
68       ret = xrealloc (ret, alloced);
69     }
70   ret[offset] = '\0';
71
72   fclose (fp);
73
74   return ret;
75
76  fail:
77   grub_util_error (_("couldn't find IEEE1275 device path for %s.\nYou will have to set `boot-device' variable manually"),
78                    dev);
79 }
80
81 static int
82 grub_install_remove_efi_entries_by_distributor (const char *efi_distributor)
83 {
84   int fd;
85   pid_t pid = grub_util_exec_pipe ((const char * []){ "efibootmgr", NULL }, &fd);
86   char *line = NULL;
87   size_t len = 0;
88   int rc;
89
90   if (!pid)
91     {
92       grub_util_warn (_("Unable to open stream from %s: %s"),
93                       "efibootmgr", strerror (errno));
94       return errno;
95     }
96
97   FILE *fp = fdopen (fd, "r");
98   if (!fp)
99     {
100       grub_util_warn (_("Unable to open stream from %s: %s"),
101                       "efibootmgr", strerror (errno));
102       return errno;
103     }
104
105   line = xmalloc (80);
106   len = 80;
107   while (1)
108     {
109       int ret;
110       char *bootnum;
111       ret = getline (&line, &len, fp);
112       if (ret == -1)
113         break;
114       if (grub_memcmp (line, "Boot", sizeof ("Boot") - 1) != 0
115           || line[sizeof ("Boot") - 1] < '0'
116           || line[sizeof ("Boot") - 1] > '9')
117         continue;
118       if (!strcasestr (line, efi_distributor))
119         continue;
120       bootnum = line + sizeof ("Boot") - 1;
121       bootnum[4] = '\0';
122       if (!verbosity)
123         rc = grub_util_exec ((const char * []){ "efibootmgr", "-q",
124               "-b", bootnum,  "-B", NULL });
125       else
126         rc = grub_util_exec ((const char * []){ "efibootmgr",
127               "-b", bootnum, "-B", NULL });
128     }
129
130   free (line);
131   return rc;
132 }
133
134 int
135 grub_install_register_efi (grub_device_t efidir_grub_dev,
136                            const char *efifile_path,
137                            const char *efi_distributor)
138 {
139   const char * efidir_disk;
140   int efidir_part;
141   int ret;
142   efidir_disk = grub_util_biosdisk_get_osdev (efidir_grub_dev->disk);
143   efidir_part = efidir_grub_dev->disk->partition ? efidir_grub_dev->disk->partition->number + 1 : 1;
144
145   if (grub_util_exec_redirect_null ((const char * []){ "efibootmgr", "--version", NULL }))
146     {
147       /* TRANSLATORS: This message is shown when required executable `%s'
148          isn't found.  */
149       grub_util_error (_("%s: not found"), "efibootmgr");
150     }
151
152   /* On Linux, we need the efivars kernel modules.  */
153 #ifdef __linux__
154   grub_util_exec ((const char * []){ "modprobe", "-q", "efivars", NULL });
155 #endif
156   /* Delete old entries from the same distributor.  */
157   ret = grub_install_remove_efi_entries_by_distributor (efi_distributor);
158   if (ret)
159     return ret;
160
161   char *efidir_part_str = xasprintf ("%d", efidir_part);
162
163   if (!verbosity)
164     ret = grub_util_exec ((const char * []){ "efibootmgr", "-q",
165           "-c", "-d", efidir_disk,
166           "-p", efidir_part_str, "-w",
167           "-L", efi_distributor, "-l", 
168           efifile_path, NULL });
169   else
170     ret = grub_util_exec ((const char * []){ "efibootmgr",
171           "-c", "-d", efidir_disk,
172           "-p", efidir_part_str, "-w",
173           "-L", efi_distributor, "-l", 
174           efifile_path, NULL });
175   free (efidir_part_str);
176   return ret;
177 }
178
179 void
180 grub_install_register_ieee1275 (int is_prep, const char *install_device,
181                                 int partno, const char *relpath)
182 {
183   char *boot_device;
184
185   if (grub_util_exec_redirect_null ((const char * []){ "ofpathname", "--version", NULL }))
186     {
187       /* TRANSLATORS: This message is shown when required executable `%s'
188          isn't found.  */
189       grub_util_error (_("%s: not found"), "ofpathname");
190     }
191
192   /* Get the Open Firmware device tree path translation.  */
193   if (!is_prep)
194     {
195       char *ptr;
196       char *ofpath;
197       const char *iptr;
198
199       ofpath = get_ofpathname (install_device);
200       boot_device = xmalloc (strlen (ofpath) + 1
201                              + sizeof ("XXXXXXXXXXXXXXXXXXXX")
202                              + 1 + strlen (relpath) + 1);
203       ptr = grub_stpcpy (boot_device, ofpath);
204       *ptr++ = ':';
205       grub_snprintf (ptr, sizeof ("XXXXXXXXXXXXXXXXXXXX"), "%d",
206                      partno);
207       ptr += strlen (ptr);
208       *ptr++ = ',';
209       for (iptr = relpath; *iptr; iptr++, ptr++)
210         {
211           if (*iptr == '/')
212             *ptr = '\\';
213           else
214             *ptr = *iptr;
215         }
216       *ptr = '\0';
217     }
218   else
219     boot_device = get_ofpathname (install_device);
220
221   if (grub_util_exec ((const char * []){ "nvsetenv", "boot-device",
222           boot_device, NULL }))
223     {
224       char *cmd = xasprintf ("setenv boot-device %s", boot_device);
225       grub_util_error (_("`nvsetenv' failed. \nYou will have to set `boot-device' variable manually.  At the IEEE1275 prompt, type:\n  %s\n"),
226                        cmd);
227       free (cmd);
228     }
229
230   free (boot_device);
231 }
232
233 void
234 grub_install_sgi_setup (const char *install_device,
235                         const char *imgfile, const char *destname)
236 {
237   grub_util_exec ((const char * []){ "dvhtool", "-d",
238         install_device, "--unix-to-vh", 
239         imgfile, destname, NULL });
240   grub_util_warn ("%s", _("You will have to set `SystemPartition' and `OSLoader' manually."));
241 }