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 }