]>
Commit | Line | Data |
---|---|---|
bf78d5b2 RM |
1 | /* |
2 | * GRUB -- GRand Unified Bootloader | |
54207d4b | 3 | * Copyright (C) 1999,2000,2001,2002,2003,2004,2009 Free Software Foundation, Inc. |
bf78d5b2 | 4 | * Copyright 2008 Sun Microsystems, Inc. |
bf78d5b2 | 5 | * |
54207d4b | 6 | * GRUB is free software; you can redistribute it and/or modify |
bf78d5b2 RM |
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 | * | |
54207d4b | 11 | * GRUB is distributed in the hope that it will be useful, |
bf78d5b2 RM |
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 | |
54207d4b | 17 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. |
bf78d5b2 RM |
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> | |
6e0632e2 | 28 | #include <grub/i18n.h> |
bf78d5b2 | 29 | |
e745cf0c VS |
30 | GRUB_MOD_LICENSE ("GPLv3+"); |
31 | ||
bf78d5b2 RM |
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); | |
bf78d5b2 RM |
48 | |
49 | if (grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_REMOVED, &ival)) | |
50 | { | |
9c4b5c13 | 51 | grub_puts_ (N_("Virtual device is removed")); |
bf78d5b2 RM |
52 | isok = 0; |
53 | } | |
54 | ||
55 | if (grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_FAULTED, &ival)) | |
56 | { | |
9c4b5c13 | 57 | grub_puts_ (N_("Virtual device is faulted")); |
bf78d5b2 RM |
58 | isok = 0; |
59 | } | |
60 | ||
61 | if (grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_OFFLINE, &ival)) | |
62 | { | |
9c4b5c13 | 63 | grub_puts_ (N_("Virtual device is offline")); |
bf78d5b2 RM |
64 | isok = 0; |
65 | } | |
66 | ||
67 | if (grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_FAULTED, &ival)) | |
9c4b5c13 VS |
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")); | |
bf78d5b2 RM |
71 | |
72 | if (isok) | |
9c4b5c13 | 73 | grub_puts_ (N_("Virtual device is online")); |
6e0632e2 | 74 | grub_xputs ("\n"); |
bf78d5b2 RM |
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); | |
9c4b5c13 | 89 | grub_puts_ (N_("Incorrect virtual device: no type available")); |
bf78d5b2 RM |
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); | |
0c7d99c7 VS |
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. */ | |
9c4b5c13 | 105 | grub_puts_ (N_("Leaf virtual device (file or disk)")); |
bf78d5b2 RM |
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) | |
6e0632e2 | 113 | grub_puts_ (N_("Bootpath: unavailable\n")); |
bf78d5b2 | 114 | else |
6e0632e2 | 115 | grub_printf_ (N_("Bootpath: %s\n"), bootpath); |
bf78d5b2 RM |
116 | |
117 | path = grub_zfs_nvlist_lookup_string (nvlist, "path"); | |
118 | print_tabs (tab); | |
119 | if (!path) | |
6e0632e2 | 120 | grub_puts_ (N_("Path: unavailable")); |
bf78d5b2 | 121 | else |
6e0632e2 | 122 | grub_printf_ (N_("Path: %s\n"), path); |
bf78d5b2 RM |
123 | |
124 | devid = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_DEVID); | |
125 | print_tabs (tab); | |
126 | if (!devid) | |
6e0632e2 | 127 | grub_puts_ (N_("Devid: unavailable")); |
bf78d5b2 | 128 | else |
6e0632e2 | 129 | grub_printf_ (N_("Devid: %s\n"), devid); |
bf78d5b2 RM |
130 | grub_free (bootpath); |
131 | grub_free (devid); | |
132 | grub_free (path); | |
133 | return GRUB_ERR_NONE; | |
134 | } | |
53618046 MM |
135 | char is_mirror=(grub_strcmp(type,VDEV_TYPE_MIRROR) == 0); |
136 | char is_raidz=(grub_strcmp(type,VDEV_TYPE_RAIDZ) == 0); | |
bf78d5b2 | 137 | |
53618046 | 138 | if (is_mirror || is_raidz) |
bf78d5b2 RM |
139 | { |
140 | int nelm, i; | |
141 | ||
142 | nelm = grub_zfs_nvlist_lookup_nvlist_array_get_nelm | |
143 | (nvlist, ZPOOL_CONFIG_CHILDREN); | |
144 | ||
53618046 MM |
145 | if(is_mirror){ |
146 | grub_puts_ (N_("This VDEV is a mirror")); | |
147 | } | |
148 | else if(is_raidz){ | |
149 | grub_uint64_t parity; | |
150 | grub_zfs_nvlist_lookup_uint64(nvlist,"nparity",&parity); | |
151 | grub_printf_ (N_("This VDEV is a RAIDZ%llu\n"),(unsigned long long)parity); | |
152 | } | |
bf78d5b2 RM |
153 | print_tabs (tab); |
154 | if (nelm <= 0) | |
155 | { | |
53618046 | 156 | grub_puts_ (N_("Incorrect VDEV")); |
bf78d5b2 RM |
157 | return GRUB_ERR_NONE; |
158 | } | |
53618046 | 159 | grub_printf_ (N_("VDEV with %d children\n"), nelm); |
bf78d5b2 | 160 | print_state (nvlist, tab); |
bf78d5b2 RM |
161 | for (i = 0; i < nelm; i++) |
162 | { | |
163 | char *child; | |
164 | ||
165 | child = grub_zfs_nvlist_lookup_nvlist_array | |
166 | (nvlist, ZPOOL_CONFIG_CHILDREN, i); | |
167 | ||
168 | print_tabs (tab); | |
169 | if (!child) | |
170 | { | |
8822a8a0 VS |
171 | /* TRANSLATORS: it's the element carying the number %d, not |
172 | total element number. And the number itself is fine, | |
173 | only the element isn't. | |
174 | */ | |
53618046 | 175 | grub_printf_ (N_("VDEV element number %d isn't correct\n"), i); |
bf78d5b2 RM |
176 | continue; |
177 | } | |
178 | ||
8822a8a0 VS |
179 | /* TRANSLATORS: it's the element carying the number %d, not |
180 | total element number. This is used in enumeration | |
181 | "Element number 1", "Element number 2", ... */ | |
53618046 | 182 | grub_printf_ (N_("VDEV element number %d:\n"), i); |
bf78d5b2 RM |
183 | print_vdev_info (child, tab + 1); |
184 | ||
185 | grub_free (child); | |
186 | } | |
e5c63d9d | 187 | return GRUB_ERR_NONE; |
bf78d5b2 RM |
188 | } |
189 | ||
190 | print_tabs (tab); | |
9c4b5c13 | 191 | grub_printf_ (N_("Unknown virtual device type: %s\n"), type); |
bf78d5b2 RM |
192 | |
193 | return GRUB_ERR_NONE; | |
194 | } | |
195 | ||
196 | static grub_err_t | |
197 | get_bootpath (char *nvlist, char **bootpath, char **devid) | |
198 | { | |
199 | char *type = 0; | |
200 | ||
201 | type = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_TYPE); | |
202 | ||
203 | if (!type) | |
204 | return grub_errno; | |
205 | ||
206 | if (grub_strcmp (type, VDEV_TYPE_DISK) == 0) | |
207 | { | |
208 | *bootpath = grub_zfs_nvlist_lookup_string (nvlist, | |
209 | ZPOOL_CONFIG_PHYS_PATH); | |
210 | *devid = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_DEVID); | |
211 | if (!*bootpath || !*devid) | |
212 | { | |
213 | grub_free (*bootpath); | |
214 | grub_free (*devid); | |
215 | *bootpath = 0; | |
216 | *devid = 0; | |
217 | } | |
218 | return GRUB_ERR_NONE; | |
219 | } | |
220 | ||
221 | if (grub_strcmp (type, VDEV_TYPE_MIRROR) == 0) | |
222 | { | |
223 | int nelm, i; | |
224 | ||
225 | nelm = grub_zfs_nvlist_lookup_nvlist_array_get_nelm | |
226 | (nvlist, ZPOOL_CONFIG_CHILDREN); | |
227 | ||
228 | for (i = 0; i < nelm; i++) | |
229 | { | |
230 | char *child; | |
231 | ||
232 | child = grub_zfs_nvlist_lookup_nvlist_array (nvlist, | |
233 | ZPOOL_CONFIG_CHILDREN, | |
234 | i); | |
235 | ||
236 | get_bootpath (child, bootpath, devid); | |
237 | ||
238 | grub_free (child); | |
239 | ||
240 | if (*bootpath && *devid) | |
241 | return GRUB_ERR_NONE; | |
242 | } | |
243 | } | |
244 | ||
245 | return GRUB_ERR_NONE; | |
246 | } | |
247 | ||
6e0632e2 | 248 | static const char *poolstates[] = { |
40211ab8 VS |
249 | /* TRANSLATORS: Here we speak about ZFS pools it's semi-marketing, |
250 | semi-technical term by Sun/Oracle and should be translated in sync with | |
251 | other ZFS-related software and documentation. */ | |
6e0632e2 VS |
252 | [POOL_STATE_ACTIVE] = N_("Pool state: active"), |
253 | [POOL_STATE_EXPORTED] = N_("Pool state: exported"), | |
254 | [POOL_STATE_DESTROYED] = N_("Pool state: destroyed"), | |
255 | [POOL_STATE_SPARE] = N_("Pool state: reserved for hot spare"), | |
256 | [POOL_STATE_L2CACHE] = N_("Pool state: level 2 ARC device"), | |
257 | [POOL_STATE_UNINITIALIZED] = N_("Pool state: uninitialized"), | |
258 | [POOL_STATE_UNAVAIL] = N_("Pool state: unavailable"), | |
259 | [POOL_STATE_POTENTIALLY_ACTIVE] = N_("Pool state: potentially active") | |
bf78d5b2 RM |
260 | }; |
261 | ||
262 | static grub_err_t | |
263 | grub_cmd_zfsinfo (grub_command_t cmd __attribute__ ((unused)), int argc, | |
264 | char **args) | |
265 | { | |
266 | grub_device_t dev; | |
267 | char *devname; | |
268 | grub_err_t err; | |
269 | char *nvlist = 0; | |
270 | char *nv = 0; | |
271 | char *poolname; | |
272 | grub_uint64_t guid; | |
273 | grub_uint64_t pool_state; | |
274 | int found; | |
275 | ||
276 | if (argc < 1) | |
d61386e2 | 277 | return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); |
bf78d5b2 RM |
278 | |
279 | if (args[0][0] == '(' && args[0][grub_strlen (args[0]) - 1] == ')') | |
280 | { | |
281 | devname = grub_strdup (args[0] + 1); | |
282 | if (devname) | |
283 | devname[grub_strlen (devname) - 1] = 0; | |
284 | } | |
285 | else | |
286 | devname = grub_strdup (args[0]); | |
287 | if (!devname) | |
288 | return grub_errno; | |
289 | ||
290 | dev = grub_device_open (devname); | |
291 | grub_free (devname); | |
292 | if (!dev) | |
293 | return grub_errno; | |
294 | ||
295 | err = grub_zfs_fetch_nvlist (dev, &nvlist); | |
296 | ||
297 | grub_device_close (dev); | |
298 | ||
299 | if (err) | |
300 | return err; | |
301 | ||
302 | poolname = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_POOL_NAME); | |
303 | if (!poolname) | |
6e0632e2 | 304 | grub_puts_ (N_("Pool name: unavailable")); |
bf78d5b2 | 305 | else |
6e0632e2 | 306 | grub_printf_ (N_("Pool name: %s\n"), poolname); |
bf78d5b2 RM |
307 | |
308 | found = | |
309 | grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_GUID, &guid); | |
310 | if (!found) | |
6e0632e2 | 311 | grub_puts_ (N_("Pool GUID: unavailable")); |
bf78d5b2 | 312 | else |
6e0632e2 | 313 | grub_printf_ (N_("Pool GUID: %016llx\n"), (long long unsigned) guid); |
bf78d5b2 RM |
314 | |
315 | found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_STATE, | |
316 | &pool_state); | |
317 | if (!found) | |
6e0632e2 | 318 | grub_puts_ (N_("Unable to retrieve pool state")); |
bf78d5b2 | 319 | else if (pool_state >= ARRAY_SIZE (poolstates)) |
6e0632e2 | 320 | grub_puts_ (N_("Unrecognized pool state")); |
bf78d5b2 | 321 | else |
6e0632e2 | 322 | grub_puts_ (poolstates[pool_state]); |
bf78d5b2 RM |
323 | |
324 | nv = grub_zfs_nvlist_lookup_nvlist (nvlist, ZPOOL_CONFIG_VDEV_TREE); | |
325 | ||
326 | if (!nv) | |
67093bc0 VS |
327 | /* TRANSLATORS: There are undetermined number of virtual devices |
328 | in a device tree, not just one. | |
329 | */ | |
9c4b5c13 | 330 | grub_puts_ (N_("No virtual device tree available")); |
bf78d5b2 RM |
331 | else |
332 | print_vdev_info (nv, 1); | |
333 | ||
334 | grub_free (nv); | |
335 | grub_free (nvlist); | |
336 | ||
337 | return GRUB_ERR_NONE; | |
338 | } | |
339 | ||
340 | static grub_err_t | |
341 | grub_cmd_zfs_bootfs (grub_command_t cmd __attribute__ ((unused)), int argc, | |
342 | char **args) | |
343 | { | |
344 | grub_device_t dev; | |
345 | char *devname; | |
346 | grub_err_t err; | |
347 | char *nvlist = 0; | |
348 | char *nv = 0; | |
349 | char *bootpath = 0, *devid = 0; | |
350 | char *fsname; | |
351 | char *bootfs; | |
352 | char *poolname; | |
353 | grub_uint64_t mdnobj; | |
354 | ||
355 | if (argc < 1) | |
d61386e2 | 356 | return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); |
bf78d5b2 RM |
357 | |
358 | devname = grub_file_get_device_name (args[0]); | |
359 | if (grub_errno) | |
360 | return grub_errno; | |
361 | ||
362 | dev = grub_device_open (devname); | |
363 | grub_free (devname); | |
364 | if (!dev) | |
365 | return grub_errno; | |
366 | ||
367 | err = grub_zfs_fetch_nvlist (dev, &nvlist); | |
368 | ||
369 | fsname = grub_strchr (args[0], ')'); | |
370 | if (fsname) | |
371 | fsname++; | |
372 | else | |
373 | fsname = args[0]; | |
374 | ||
375 | if (!err) | |
376 | err = grub_zfs_getmdnobj (dev, fsname, &mdnobj); | |
377 | ||
378 | grub_device_close (dev); | |
379 | ||
380 | if (err) | |
381 | return err; | |
382 | ||
383 | poolname = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_POOL_NAME); | |
384 | if (!poolname) | |
385 | { | |
386 | if (!grub_errno) | |
387 | grub_error (GRUB_ERR_BAD_FS, "No poolname found"); | |
388 | return grub_errno; | |
389 | } | |
390 | ||
391 | nv = grub_zfs_nvlist_lookup_nvlist (nvlist, ZPOOL_CONFIG_VDEV_TREE); | |
392 | ||
393 | if (nv) | |
394 | get_bootpath (nv, &bootpath, &devid); | |
395 | ||
396 | grub_free (nv); | |
397 | grub_free (nvlist); | |
398 | ||
4fbf1852 | 399 | bootfs = grub_xasprintf ("zfs-bootfs=%s/%llu%s%s%s%s%s%s", |
b3ff6ff0 | 400 | poolname, (unsigned long long) mdnobj, |
4fbf1852 VS |
401 | bootpath ? ",bootpath=\"" : "", |
402 | bootpath ? : "", | |
403 | bootpath ? "\"" : "", | |
404 | devid ? ",diskdevid=\"" : "", | |
405 | devid ? : "", | |
406 | devid ? "\"" : ""); | |
b3ff6ff0 VS |
407 | if (!bootfs) |
408 | return grub_errno; | |
bf78d5b2 RM |
409 | if (argc >= 2) |
410 | grub_env_set (args[1], bootfs); | |
411 | else | |
412 | grub_printf ("%s\n", bootfs); | |
413 | ||
414 | grub_free (bootfs); | |
415 | grub_free (poolname); | |
416 | grub_free (bootpath); | |
417 | grub_free (devid); | |
418 | ||
419 | return GRUB_ERR_NONE; | |
420 | } | |
421 | ||
422 | ||
423 | static grub_command_t cmd_info, cmd_bootfs; | |
424 | ||
425 | GRUB_MOD_INIT (zfsinfo) | |
426 | { | |
427 | cmd_info = grub_register_command ("zfsinfo", grub_cmd_zfsinfo, | |
6e0632e2 VS |
428 | N_("DEVICE"), |
429 | N_("Print ZFS info about DEVICE.")); | |
bf78d5b2 | 430 | cmd_bootfs = grub_register_command ("zfs-bootfs", grub_cmd_zfs_bootfs, |
6e0632e2 | 431 | N_("FILESYSTEM [VARIABLE]"), |
67093bc0 | 432 | N_("Print ZFS-BOOTFSOBJ or store it into VARIABLE")); |
bf78d5b2 RM |
433 | } |
434 | ||
435 | GRUB_MOD_FINI (zfsinfo) | |
436 | { | |
437 | grub_unregister_command (cmd_info); | |
438 | grub_unregister_command (cmd_bootfs); | |
439 | } |