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 }