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, grub_uint64_t inguid)
188 {
189 char *type = 0;
190 grub_uint64_t diskguid = 0;
191
192 type = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_TYPE);
193
194 if (!type)
195 return grub_errno;
196
197 if (grub_strcmp (type, VDEV_TYPE_DISK) == 0)
198 {
199 grub_zfs_nvlist_lookup_uint64 (nvlist, "guid", &diskguid);
200 *bootpath = grub_zfs_nvlist_lookup_string (nvlist,
201 ZPOOL_CONFIG_PHYS_PATH);
202 *devid = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_DEVID);
203 if (!*bootpath || !*devid || (diskguid != inguid))
204 {
205 grub_free (*bootpath);
206 grub_free (*devid);
207 *bootpath = 0;
208 *devid = 0;
209 }
210 return GRUB_ERR_NONE;
211 }
212
213 if (grub_strcmp (type, VDEV_TYPE_MIRROR) == 0)
214 {
215 int nelm, i;
216
217 nelm = grub_zfs_nvlist_lookup_nvlist_array_get_nelm
218 (nvlist, ZPOOL_CONFIG_CHILDREN);
219
220 for (i = 0; i < nelm; i++)
221 {
222 char *child;
223
224 child = grub_zfs_nvlist_lookup_nvlist_array (nvlist,
225 ZPOOL_CONFIG_CHILDREN,
226 i);
227
228 get_bootpath (child, bootpath, devid, inguid);
229
230 grub_free (child);
231
232 if (*bootpath && *devid)
233 return GRUB_ERR_NONE;
234 }
235 }
236
237 return GRUB_ERR_NONE;
238 }
239
240 static const char *poolstates[] = {
241 /* TRANSLATORS: Here we speak about ZFS pools it's semi-marketing,
242 semi-technical term by Sun/Oracle and should be translated in sync with
243 other ZFS-related software and documentation. */
244 [POOL_STATE_ACTIVE] = N_("Pool state: active"),
245 [POOL_STATE_EXPORTED] = N_("Pool state: exported"),
246 [POOL_STATE_DESTROYED] = N_("Pool state: destroyed"),
247 [POOL_STATE_SPARE] = N_("Pool state: reserved for hot spare"),
248 [POOL_STATE_L2CACHE] = N_("Pool state: level 2 ARC device"),
249 [POOL_STATE_UNINITIALIZED] = N_("Pool state: uninitialized"),
250 [POOL_STATE_UNAVAIL] = N_("Pool state: unavailable"),
251 [POOL_STATE_POTENTIALLY_ACTIVE] = N_("Pool state: potentially active")
252 };
253
254 static grub_err_t
255 grub_cmd_zfsinfo (grub_command_t cmd __attribute__ ((unused)), int argc,
256 char **args)
257 {
258 grub_device_t dev;
259 char *devname;
260 grub_err_t err;
261 char *nvlist = 0;
262 char *nv = 0;
263 char *poolname;
264 grub_uint64_t guid;
265 grub_uint64_t pool_state;
266 int found;
267
268 if (argc < 1)
269 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
270
271 if (args[0][0] == '(' && args[0][grub_strlen (args[0]) - 1] == ')')
272 {
273 devname = grub_strdup (args[0] + 1);
274 if (devname)
275 devname[grub_strlen (devname) - 1] = 0;
276 }
277 else
278 devname = grub_strdup (args[0]);
279 if (!devname)
280 return grub_errno;
281
282 dev = grub_device_open (devname);
283 grub_free (devname);
284 if (!dev)
285 return grub_errno;
286
287 err = grub_zfs_fetch_nvlist (dev, &nvlist);
288
289 grub_device_close (dev);
290
291 if (err)
292 return err;
293
294 poolname = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_POOL_NAME);
295 if (!poolname)
296 grub_puts_ (N_("Pool name: unavailable"));
297 else
298 grub_printf_ (N_("Pool name: %s\n"), poolname);
299
300 found =
301 grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_GUID, &guid);
302 if (!found)
303 grub_puts_ (N_("Pool GUID: unavailable"));
304 else
305 grub_printf_ (N_("Pool GUID: %016llx\n"), (long long unsigned) guid);
306
307 found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_STATE,
308 &pool_state);
309 if (!found)
310 grub_puts_ (N_("Unable to retrieve pool state"));
311 else if (pool_state >= ARRAY_SIZE (poolstates))
312 grub_puts_ (N_("Unrecognized pool state"));
313 else
314 grub_puts_ (poolstates[pool_state]);
315
316 nv = grub_zfs_nvlist_lookup_nvlist (nvlist, ZPOOL_CONFIG_VDEV_TREE);
317
318 if (!nv)
319 /* TRANSLATORS: There are undetermined number of virtual devices
320 in a device tree, not just one.
321 */
322 grub_puts_ (N_("No virtual device tree available"));
323 else
324 print_vdev_info (nv, 1);
325
326 grub_free (nv);
327 grub_free (nvlist);
328
329 return GRUB_ERR_NONE;
330 }
331
332 static grub_err_t
333 grub_cmd_zfs_bootfs (grub_command_t cmd __attribute__ ((unused)), int argc,
334 char **args)
335 {
336 grub_device_t dev;
337 char *devname;
338 grub_err_t err;
339 char *nvlist = 0;
340 char *nv = 0;
341 char *bootpath = 0, *devid = 0;
342 char *fsname;
343 char *bootfs;
344 char *poolname;
345 char def_path[512];
346 struct grub_zfs_data * data;
347 grub_uint64_t mdnobj, guid;
348 int def = 0;
349
350 if (argc < 1)
351 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
352
353 devname = grub_file_get_device_name (args[0]);
354
355 if (grub_errno)
356 return grub_errno;
357
358 dev = grub_device_open (devname);
359 grub_free (devname);
360 if (!dev)
361 return grub_errno;
362
363 err = grub_zfs_fetch_nvlist (dev, &nvlist);
364
365 fsname = grub_strchr (args[0], ')');
366 if (fsname)
367 fsname++;
368 else
369 fsname = args[0];
370
371 if (grub_strcmp(fsname, "default") == 0)
372 ++def;
373
374 if (! def) {
375 err = grub_zfs_getmdnobj (dev, fsname, &mdnobj);
376 } else {
377 err = get_default_bootfs_obj(dev, def_path, &mdnobj);
378 }
379
380 grub_device_close (dev);
381
382 if (err)
383 return err;
384
385 poolname = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_POOL_NAME);
386 if (!poolname)
387 {
388 if (!grub_errno)
389 grub_error (GRUB_ERR_BAD_FS, "No poolname found");
390 return grub_errno;
391 }
392
393 nv = grub_zfs_nvlist_lookup_nvlist (nvlist, ZPOOL_CONFIG_VDEV_TREE);
394 grub_zfs_nvlist_lookup_uint64 (nvlist, "guid", &guid);
395
396 if (nv && guid)
397 get_bootpath (nv, &bootpath, &devid, guid);
398
399 grub_free (nv);
400 grub_free (nvlist);
401
402 bootfs = grub_xasprintf ("zfs-bootfs=%s/%llu%s%s%s%s%s%s",
403 poolname, (unsigned long long) mdnobj,
404 bootpath ? ",bootpath=\"" : "",
405 bootpath ? : "",
406 bootpath ? "\"" : "",
407 devid ? ",diskdevid=\"" : "",
408 devid ? : "",
409 devid ? "\"" : "");
410 if (!bootfs)
411 return grub_errno;
412 if (argc >= 2)
413 grub_env_set (args[1], bootfs);
414 else
415 grub_printf ("%s\n", bootfs);
416
417 if ((def) && (argc >= 3))
418 grub_env_set(args[2], def_path);
419 else if (def)
420 grub_printf("%s\n", def_path);
421
422 grub_free (bootfs);
423 grub_free (poolname);
424 grub_free (bootpath);
425 grub_free (devid);
426
427 return GRUB_ERR_NONE;
428 }
429
430
431 static grub_command_t cmd_info, cmd_bootfs;
432
433 GRUB_MOD_INIT (zfsinfo)
434 {
435 cmd_info = grub_register_command ("zfsinfo", grub_cmd_zfsinfo,
436 N_("DEVICE"),
437 N_("Print ZFS info about DEVICE."));
438 cmd_bootfs = grub_register_command ("zfs-bootfs", grub_cmd_zfs_bootfs,
439 N_("FILESYSTEM [VARIABLE]"),
440 N_("Print ZFS-BOOTFSOBJ or store it into VARIABLE"));
441 }
442
443 GRUB_MOD_FINI (zfsinfo)
444 {
445 grub_unregister_command (cmd_info);
446 grub_unregister_command (cmd_bootfs);
447 }