1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 1999,2000,2001,2002,2003,2004,2009 Free Software Foundation, Inc.
4 * Copyright 2008 Sun Microsystems, Inc.
5 *
6 * GRUB is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <grub/zfs/zfs.h>
21 #include <grub/device.h>
22 #include <grub/file.h>
23 #include <grub/command.h>
24 #include <grub/misc.h>
25 #include <grub/mm.h>
26 #include <grub/dl.h>
27 #include <grub/env.h>
28 #include <grub/i18n.h>
29
30 GRUB_MOD_LICENSE ("GPLv3+");
31
32 static inline void
33 print_tabs (int n)
34 {
35 int i;
36
37 for (i = 0; i < n; i++)
38 grub_printf (" ");
39 }
40
41 static grub_err_t
42 print_state (char *nvlist, int tab)
43 {
44 grub_uint64_t ival;
45 int isok = 1;
46
47 print_tabs (tab);
48
49 if (grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_REMOVED, &ival))
50 {
51 grub_puts_ (N_("Virtual device is removed"));
52 isok = 0;
53 }
54
55 if (grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_FAULTED, &ival))
56 {
57 grub_puts_ (N_("Virtual device is faulted"));
58 isok = 0;
59 }
60
61 if (grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_OFFLINE, &ival))
62 {
63 grub_puts_ (N_("Virtual device is offline"));
64 isok = 0;
65 }
66
67 if (grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_FAULTED, &ival))
68 /* TRANSLATORS: degraded doesn't mean broken but that some of
69 component are missing but virtual device as whole is still usable. */
70 grub_puts_ (N_("Virtual device is degraded"));
71
72 if (isok)
73 grub_puts_ (N_("Virtual device is online"));
74 grub_xputs ("\n");
75
76 return GRUB_ERR_NONE;
77 }
78
79 static grub_err_t
80 print_vdev_info (char *nvlist, int tab)
81 {
82 char *type = 0;
83
84 type = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_TYPE);
85
86 if (!type)
87 {
88 print_tabs (tab);
89 grub_puts_ (N_("Incorrect virtual device: no type available"));
90 return grub_errno;
91 }
92
93 if (grub_strcmp (type, VDEV_TYPE_DISK) == 0)
94 {
95 char *bootpath = 0;
96 char *path = 0;
97 char *devid = 0;
98
99 print_tabs (tab);
100 /* TRANSLATORS: The virtual devices form a tree (in graph-theoretical
101 sense). The nodes like mirror or raidz have children: member devices.
102 The "real" devices which actually store data are called "leafs"
103 (again borrowed from graph theory) and can be either disks
104 (or partitions) or files. */
105 grub_puts_ (N_("Leaf virtual device (file or disk)"));
106
107 print_state (nvlist, tab);
108
109 bootpath =
110 grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_PHYS_PATH);
111 print_tabs (tab);
112 if (!bootpath)
113 grub_puts_ (N_("Bootpath: unavailable\n"));
114 else
115 grub_printf_ (N_("Bootpath: %s\n"), bootpath);
116
117 path = grub_zfs_nvlist_lookup_string (nvlist, "path");
118 print_tabs (tab);
119 if (!path)
120 grub_puts_ (N_("Path: unavailable"));
121 else
122 grub_printf_ (N_("Path: %s\n"), path);
123
124 devid = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_DEVID);
125 print_tabs (tab);
126 if (!devid)
127 grub_puts_ (N_("Devid: unavailable"));
128 else
129 grub_printf_ (N_("Devid: %s\n"), devid);
130 grub_free (bootpath);
131 grub_free (devid);
132 grub_free (path);
133 return GRUB_ERR_NONE;
134 }
135
136 if (grub_strcmp (type, VDEV_TYPE_MIRROR) == 0)
137 {
138 int nelm, i;
139
140 nelm = grub_zfs_nvlist_lookup_nvlist_array_get_nelm
141 (nvlist, ZPOOL_CONFIG_CHILDREN);
142
143 print_tabs (tab);
144 if (nelm <= 0)
145 {
146 grub_puts_ (N_("Incorrect mirror"));
147 return GRUB_ERR_NONE;
148 }
149 grub_printf_ (N_("Mirror with %d children\n"), nelm);
150 print_state (nvlist, tab);
151 for (i = 0; i < nelm; i++)
152 {
153 char *child;
154
155 child = grub_zfs_nvlist_lookup_nvlist_array
156 (nvlist, ZPOOL_CONFIG_CHILDREN, i);
157
158 print_tabs (tab);
159 if (!child)
160 {
161 /* TRANSLATORS: it's the element carying the number %d, not
162 total element number. And the number itself is fine,
163 only the element isn't.
164 */
165 grub_printf_ (N_("Mirror element number %d isn't correct\n"), i);
166 continue;
167 }
168
169 /* TRANSLATORS: it's the element carying the number %d, not
170 total element number. This is used in enumeration
171 "Element number 1", "Element number 2", ... */
172 grub_printf_ (N_("Mirror element number %d:\n"), i);
173 print_vdev_info (child, tab + 1);
174
175 grub_free (child);
176 }
177 return GRUB_ERR_NONE;
178 }
179
180 print_tabs (tab);
181 grub_printf_ (N_("Unknown virtual device type: %s\n"), type);
182
183 return GRUB_ERR_NONE;
184 }
185
186 static grub_err_t
187 get_bootpath (char *nvlist, char **bootpath, char **devid)
188 {
189 char *type = 0;
190
191 type = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_TYPE);
192
193 if (!type)
194 return grub_errno;
195
196 if (grub_strcmp (type, VDEV_TYPE_DISK) == 0)
197 {
198 *bootpath = grub_zfs_nvlist_lookup_string (nvlist,
199 ZPOOL_CONFIG_PHYS_PATH);
200 *devid = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_DEVID);
201 if (!*bootpath || !*devid)
202 {
203 grub_free (*bootpath);
204 grub_free (*devid);
205 *bootpath = 0;
206 *devid = 0;
207 }
208 return GRUB_ERR_NONE;
209 }
210
211 if (grub_strcmp (type, VDEV_TYPE_MIRROR) == 0)
212 {
213 int nelm, i;
214
215 nelm = grub_zfs_nvlist_lookup_nvlist_array_get_nelm
216 (nvlist, ZPOOL_CONFIG_CHILDREN);
217
218 for (i = 0; i < nelm; i++)
219 {
220 char *child;
221
222 child = grub_zfs_nvlist_lookup_nvlist_array (nvlist,
223 ZPOOL_CONFIG_CHILDREN,
224 i);
225
226 get_bootpath (child, bootpath, devid);
227
228 grub_free (child);
229
230 if (*bootpath && *devid)
231 return GRUB_ERR_NONE;
232 }
233 }
234
235 return GRUB_ERR_NONE;
236 }
237
238 static const char *poolstates[] = {
239 /* TRANSLATORS: Here we speak about ZFS pools it's semi-marketing,
240 semi-technical term by Sun/Oracle and should be translated in sync with
241 other ZFS-related software and documentation. */
242 [POOL_STATE_ACTIVE] = N_("Pool state: active"),
243 [POOL_STATE_EXPORTED] = N_("Pool state: exported"),
244 [POOL_STATE_DESTROYED] = N_("Pool state: destroyed"),
245 [POOL_STATE_SPARE] = N_("Pool state: reserved for hot spare"),
246 [POOL_STATE_L2CACHE] = N_("Pool state: level 2 ARC device"),
247 [POOL_STATE_UNINITIALIZED] = N_("Pool state: uninitialized"),
248 [POOL_STATE_UNAVAIL] = N_("Pool state: unavailable"),
249 [POOL_STATE_POTENTIALLY_ACTIVE] = N_("Pool state: potentially active")
250 };
251
252 static grub_err_t
253 grub_cmd_zfsinfo (grub_command_t cmd __attribute__ ((unused)), int argc,
254 char **args)
255 {
256 grub_device_t dev;
257 char *devname;
258 grub_err_t err;
259 char *nvlist = 0;
260 char *nv = 0;
261 char *poolname;
262 grub_uint64_t guid;
263 grub_uint64_t pool_state;
264 int found;
265
266 if (argc < 1)
267 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
268
269 if (args[0][0] == '(' && args[0][grub_strlen (args[0]) - 1] == ')')
270 {
271 devname = grub_strdup (args[0] + 1);
272 if (devname)
273 devname[grub_strlen (devname) - 1] = 0;
274 }
275 else
276 devname = grub_strdup (args[0]);
277 if (!devname)
278 return grub_errno;
279
280 dev = grub_device_open (devname);
281 grub_free (devname);
282 if (!dev)
283 return grub_errno;
284
285 err = grub_zfs_fetch_nvlist (dev, &nvlist);
286
287 grub_device_close (dev);
288
289 if (err)
290 return err;
291
292 poolname = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_POOL_NAME);
293 if (!poolname)
294 grub_puts_ (N_("Pool name: unavailable"));
295 else
296 grub_printf_ (N_("Pool name: %s\n"), poolname);
297
298 found =
299 grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_GUID, &guid);
300 if (!found)
301 grub_puts_ (N_("Pool GUID: unavailable"));
302 else
303 grub_printf_ (N_("Pool GUID: %016llx\n"), (long long unsigned) guid);
304
305 found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_STATE,
306 &pool_state);
307 if (!found)
308 grub_puts_ (N_("Unable to retrieve pool state"));
309 else if (pool_state >= ARRAY_SIZE (poolstates))
310 grub_puts_ (N_("Unrecognized pool state"));
311 else
312 grub_puts_ (poolstates[pool_state]);
313
314 nv = grub_zfs_nvlist_lookup_nvlist (nvlist, ZPOOL_CONFIG_VDEV_TREE);
315
316 if (!nv)
317 /* TRANSLATORS: There are undetermined number of virtual devices
318 in a device tree, not just one.
319 */
320 grub_puts_ (N_("No virtual device tree available"));
321 else
322 print_vdev_info (nv, 1);
323
324 grub_free (nv);
325 grub_free (nvlist);
326
327 return GRUB_ERR_NONE;
328 }
329
330 static grub_err_t
331 grub_cmd_zfs_bootfs (grub_command_t cmd __attribute__ ((unused)), int argc,
332 char **args)
333 {
334 grub_device_t dev;
335 char *devname;
336 grub_err_t err;
337 char *nvlist = 0;
338 char *nv = 0;
339 char *bootpath = 0, *devid = 0;
340 char *fsname;
341 char *bootfs;
342 char *poolname;
343 grub_uint64_t mdnobj;
344
345 if (argc < 1)
346 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
347
348 devname = grub_file_get_device_name (args[0]);
349 if (grub_errno)
350 return grub_errno;
351
352 dev = grub_device_open (devname);
353 grub_free (devname);
354 if (!dev)
355 return grub_errno;
356
357 err = grub_zfs_fetch_nvlist (dev, &nvlist);
358
359 fsname = grub_strchr (args[0], ')');
360 if (fsname)
361 fsname++;
362 else
363 fsname = args[0];
364
365 if (!err)
366 err = grub_zfs_getmdnobj (dev, fsname, &mdnobj);
367
368 grub_device_close (dev);
369
370 if (err)
371 return err;
372
373 poolname = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_POOL_NAME);
374 if (!poolname)
375 {
376 if (!grub_errno)
377 grub_error (GRUB_ERR_BAD_FS, "No poolname found");
378 return grub_errno;
379 }
380
381 nv = grub_zfs_nvlist_lookup_nvlist (nvlist, ZPOOL_CONFIG_VDEV_TREE);
382
383 if (nv)
384 get_bootpath (nv, &bootpath, &devid);
385
386 grub_free (nv);
387 grub_free (nvlist);
388
389 bootfs = grub_xasprintf ("zfs-bootfs=%s/%llu%s%s%s%s%s%s",
390 poolname, (unsigned long long) mdnobj,
391 bootpath ? ",bootpath=\"" : "",
392 bootpath ? : "",
393 bootpath ? "\"" : "",
394 devid ? ",diskdevid=\"" : "",
395 devid ? : "",
396 devid ? "\"" : "");
397 if (!bootfs)
398 return grub_errno;
399 if (argc >= 2)
400 grub_env_set (args[1], bootfs);
401 else
402 grub_printf ("%s\n", bootfs);
403
404 grub_free (bootfs);
405 grub_free (poolname);
406 grub_free (bootpath);
407 grub_free (devid);
408
409 return GRUB_ERR_NONE;
410 }
411
412
413 static grub_command_t cmd_info, cmd_bootfs;
414
415 GRUB_MOD_INIT (zfsinfo)
416 {
417 cmd_info = grub_register_command ("zfsinfo", grub_cmd_zfsinfo,
418 N_("DEVICE"),
419 N_("Print ZFS info about DEVICE."));
420 cmd_bootfs = grub_register_command ("zfs-bootfs", grub_cmd_zfs_bootfs,
421 N_("FILESYSTEM [VARIABLE]"),
422 N_("Print ZFS-BOOTFSOBJ or store it into VARIABLE"));
423 }
424
425 GRUB_MOD_FINI (zfsinfo)
426 {
427 grub_unregister_command (cmd_info);
428 grub_unregister_command (cmd_bootfs);
429 }