Skip to content

Commit 7de33f4

Browse files
nfrayerfrozencemetery
authored andcommitted
emu: Add switch-root to grub-emu
If the kernel running grub emu is the same as the one we want to boot, it makes sense that we just switch-root instead of kexec the same kernel again by doing grub2-emu --switch-root Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
1 parent c3569e4 commit 7de33f4

5 files changed

Lines changed: 223 additions & 8 deletions

File tree

grub-core/kern/emu/main.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ static struct argp_option options[] = {
108108
{"verbose", 'v', 0, 0, N_("print verbose messages."), 0},
109109
{"hold", 'H', N_("SECS"), OPTION_ARG_OPTIONAL, N_("wait until a debugger will attach"), 0},
110110
{"kexec", 'X', 0, 0, N_("use kexec to boot Linux kernels via systemctl (pass twice to enable dangerous fallback to non-systemctl)."), 0},
111+
{"switch-root", 'W', 0, 0, N_("use switch-root to only switch root filesystem without restarting the kernel."), 0},
111112
{ 0, 0, 0, 0, 0, 0 }
112113
};
113114

@@ -168,7 +169,9 @@ argp_parser (int key, char *arg, struct argp_state *state)
168169
case 'X':
169170
grub_util_set_kexecute ();
170171
break;
171-
172+
case 'W':
173+
grub_util_set_switch_root ();
174+
break;
172175
case ARGP_KEY_ARG:
173176
{
174177
/* Too many arguments. */

grub-core/kern/emu/misc.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040

4141
int verbosity;
4242
int kexecute;
43+
int switchroot = 0;
4344

4445
void
4546
grub_util_warn (const char *fmt, ...)
@@ -231,3 +232,15 @@ grub_util_get_kexecute (void)
231232
{
232233
return kexecute;
233234
}
235+
236+
void
237+
grub_util_set_switch_root (void)
238+
{
239+
switchroot = 1;
240+
}
241+
242+
int
243+
grub_util_get_switch_root (void)
244+
{
245+
return switchroot;
246+
}

grub-core/loader/emu/linux.c

Lines changed: 203 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
* You should have received a copy of the GNU General Public License
1616
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
1717
*/
18-
1918
#include <grub/loader.h>
2019
#include <grub/dl.h>
2120
#include <grub/command.h>
@@ -33,6 +32,196 @@ static char *kernel_path;
3332
static char *initrd_path;
3433
static char *boot_cmdline;
3534

35+
static grub_err_t
36+
grub_switch_root (void)
37+
{
38+
char *tmp = NULL;
39+
char *options_cmd = NULL;
40+
char *options = NULL;
41+
char *subvol = NULL;
42+
char *root_uuid = NULL;
43+
char *kernel_release = NULL;
44+
grub_err_t rc = GRUB_ERR_NONE;
45+
const char *subvol_param = "subvol=";
46+
const char *kernel_release_prefix = "/boot/vmlinuz-";
47+
const char *root_prefix = "root=";
48+
const char *systemctl[] = {"systemctl", "--force", "switch-root", "/sysroot", NULL};
49+
const char *mountrootfs[] = {"mount", root_uuid, "/sysroot", options_cmd, options, NULL};
50+
const char *unamer[] = {"uname", "-r", NULL};
51+
char *uname_buf = NULL;
52+
int i = 0;
53+
54+
/* Extract the kernel release tag from kernel_path */
55+
if (!kernel_path)
56+
{
57+
rc = GRUB_ERR_BAD_ARGUMENT;
58+
grub_dprintf ("linux", "switch_root: No kernel_path found\n");
59+
goto out;
60+
}
61+
62+
if ((kernel_release = grub_xasprintf ("%s", (kernel_path + grub_strlen (kernel_release_prefix)))) == NULL)
63+
{
64+
grub_dprintf ("linux", "switch_root: Failed to allocate memory\n");
65+
rc = GRUB_ERR_BAD_ARGUMENT;
66+
goto out;
67+
}
68+
69+
70+
/* Check for kernel mismatch */
71+
/* Retrieve the current kernel relase tag */
72+
grub_util_exec_redirect (unamer, NULL, "/tmp/version");
73+
74+
grub_file_t f = grub_file_open ("/tmp/version", GRUB_FILE_TYPE_FS_SEARCH);
75+
76+
if (f == NULL)
77+
{
78+
grub_dprintf ("linux", "failed opening file.\n");
79+
rc = GRUB_ERR_FILE_NOT_FOUND;
80+
goto out;
81+
}
82+
83+
if ((uname_buf = grub_malloc (f->size)) == NULL)
84+
{
85+
grub_dprintf ("linux", "switch_root: Failed to allocate memory\n");
86+
rc = GRUB_ERR_OUT_OF_MEMORY;
87+
goto out;
88+
}
89+
90+
if (grub_file_read (f, uname_buf, f->size) < 0)
91+
{
92+
grub_dprintf ("linux", "switch_root: failed to read from file\n");
93+
rc = GRUB_ERR_FILE_READ_ERROR;
94+
goto out;
95+
}
96+
97+
grub_file_close (f);
98+
99+
if (grub_strstr (uname_buf, kernel_release) == NULL)
100+
{
101+
grub_dprintf ("linux", "switch_root: kernel mismatch, not performing switch-root ...\n");
102+
rc = GRUB_ERR_NO_KERNEL;
103+
goto out;
104+
}
105+
106+
/* Extract the root partition from boot_cmdline */
107+
if (!boot_cmdline)
108+
{
109+
rc = GRUB_ERR_BAD_ARGUMENT;
110+
goto out;
111+
}
112+
113+
tmp = grub_strdup (boot_cmdline);
114+
115+
if (tmp == NULL)
116+
{
117+
rc = GRUB_ERR_OUT_OF_MEMORY;
118+
goto out;
119+
}
120+
121+
if ((root_uuid = grub_strstr (tmp, root_prefix)) == NULL)
122+
{
123+
rc = GRUB_ERR_BAD_ARGUMENT;
124+
grub_dprintf ("linux", "switch_root: Can't find rootfs\n");
125+
goto out;
126+
}
127+
128+
root_uuid += grub_strlen (root_prefix);
129+
130+
while (root_uuid[i] != ' ' && root_uuid[i] != '\0')
131+
i++;
132+
133+
root_uuid[i] = '\0';
134+
135+
/* Allocate a new buffer holding root_uuid */
136+
root_uuid = grub_xasprintf ("%s", root_uuid);
137+
138+
if (root_uuid == NULL)
139+
{
140+
grub_dprintf ("linux", "switch_root: Failed to allocated memory\n");
141+
rc = GRUB_ERR_OUT_OF_MEMORY;
142+
goto out;
143+
}
144+
145+
/* Check for subvol parameter */
146+
grub_strcpy (tmp, boot_cmdline);
147+
148+
if ((subvol = grub_strstr(tmp, subvol_param)) != NULL)
149+
{
150+
i = 0;
151+
152+
while (subvol[i] != ' ' && subvol[i] != '\0')
153+
i++;
154+
155+
subvol[i] = '\0';
156+
157+
/* Allocate a new buffer holding subvol */
158+
subvol = grub_xasprintf("%s", subvol);
159+
160+
if (subvol == NULL)
161+
{
162+
grub_dprintf ("linux", "switch_root: Failed to allocated memory\n");
163+
rc = GRUB_ERR_OUT_OF_MEMORY;
164+
goto out;
165+
}
166+
167+
options_cmd = grub_xasprintf("%s", "-o");
168+
options = grub_xasprintf("%s", subvol);
169+
}
170+
171+
if (options == NULL)
172+
{
173+
mountrootfs[3] = NULL;
174+
}
175+
else
176+
{
177+
mountrootfs[3] = options_cmd;
178+
mountrootfs[4] = options;
179+
}
180+
181+
mountrootfs[1] = root_uuid;
182+
183+
grub_dprintf ("linux", "Executing:\n");
184+
grub_dprintf ("linux", "%s %s %s %s %s\n", mountrootfs[0], mountrootfs[1],
185+
mountrootfs[2], mountrootfs[3], mountrootfs[4]);
186+
187+
/* Mount the rootfs */
188+
rc = grub_util_exec (mountrootfs);
189+
190+
if (rc != GRUB_ERR_NONE)
191+
{
192+
grub_dprintf ("linux", "switch_root: Failed.\n");
193+
rc = GRUB_ERR_INVALID_COMMAND;
194+
goto out;
195+
}
196+
197+
grub_dprintf ("linux", "Done.\n");
198+
199+
grub_dprintf ("linux", "%s %s %s %s\n", systemctl[0], systemctl[1],
200+
systemctl[2], systemctl[3]);
201+
202+
/* Switch root */
203+
rc = grub_util_exec (systemctl);
204+
205+
if (rc != GRUB_ERR_NONE)
206+
{
207+
grub_dprintf ("linux", "switch_root: Failed.\n");
208+
rc = GRUB_ERR_INVALID_COMMAND;
209+
goto out;
210+
}
211+
212+
grub_dprintf ("linux", "Done.\n");
213+
214+
out:
215+
grub_free (tmp);
216+
grub_free (options_cmd);
217+
grub_free (options);
218+
grub_free (subvol);
219+
grub_free (root_uuid);
220+
grub_free (uname_buf);
221+
grub_free (kernel_release);
222+
return rc;
223+
}
224+
36225
static grub_err_t
37226
grub_linux_boot (void)
38227
{
@@ -51,12 +240,20 @@ grub_linux_boot (void)
51240
else
52241
initrd_param = grub_xasprintf ("%s", "");
53242

54-
grub_dprintf ("linux", "%serforming 'kexec -la %s %s %s'\n",
55-
(kexecute) ? "P" : "Not p",
56-
kernel_path, initrd_param, boot_cmdline);
243+
if (grub_util_get_switch_root() == 1)
244+
{
245+
rc = grub_switch_root();
246+
if (rc != GRUB_ERR_NONE)
247+
grub_fatal (N_("Failed to execute switch_root\n"));
248+
}
249+
else if (kexecute)
250+
{
251+
grub_dprintf ("linux", "%serforming 'kexec -la %s %s %s'\n",
252+
(kexecute) ? "P" : "Not p",
253+
kernel_path, initrd_param, boot_cmdline);
57254

58-
if (kexecute)
59-
rc = grub_util_exec (kexec);
255+
rc = grub_util_exec (kexec);
256+
}
60257

61258
grub_free (initrd_param);
62259

include/grub/emu/exec.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ grub_util_exec_redirect_all (const char *const *argv, const char *stdin_file,
3636
int
3737
EXPORT_FUNC(grub_util_exec) (const char *const *argv);
3838
int
39-
grub_util_exec_redirect (const char *const *argv, const char *stdin_file,
39+
EXPORT_FUNC(grub_util_exec_redirect) (const char *const *argv, const char *stdin_file,
4040
const char *stdout_file);
4141
int
4242
grub_util_exec_redirect_null (const char *const *argv);

include/grub/emu/misc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ void EXPORT_FUNC(grub_util_error) (const char *fmt, ...) __attribute__ ((format
5959

6060
void EXPORT_FUNC(grub_util_set_kexecute) (void);
6161
int EXPORT_FUNC(grub_util_get_kexecute) (void) WARN_UNUSED_RESULT;
62+
void EXPORT_FUNC(grub_util_set_switch_root) (void);
63+
int EXPORT_FUNC(grub_util_get_switch_root) (void);
6264

6365
grub_uint64_t EXPORT_FUNC (grub_util_get_cpu_time_ms) (void);
6466

0 commit comments

Comments
 (0)