1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2017 Intel Corporation. 3 * All rights reserved. 4 * Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. 5 */ 6 7 #include "spdk/stdinc.h" 8 9 #include "spdk/bdev.h" 10 #include "spdk/env.h" 11 #include "spdk/event.h" 12 #include "spdk/blob_bdev.h" 13 #include "spdk/blob.h" 14 #include "spdk/log.h" 15 #include "spdk/version.h" 16 #include "spdk/string.h" 17 #include "spdk/uuid.h" 18 19 /* 20 * The following is not a public header file, but the CLI does expose 21 * some internals of blobstore for dev/debug purposes so we 22 * include it here. 23 */ 24 #include "../lib/blob/blobstore.h" 25 static void cli_start(void *arg1); 26 27 static const char *program_name = "blobcli"; 28 /* default name for .json file, any name can be used however with -j switch */ 29 static const char *program_conf = "blobcli.json"; 30 31 /* 32 * CMD mode runs one command at a time which can be annoying as the init takes 33 * a few seconds, so the shell mode, invoked with -S, does the init once and gives 34 * the user an interactive shell instead. With script mode init is also done just 35 * once. 36 */ 37 enum cli_mode_type { 38 CLI_MODE_CMD, 39 CLI_MODE_SHELL, 40 CLI_MODE_SCRIPT 41 }; 42 43 enum cli_action_type { 44 CLI_NONE, 45 CLI_IMPORT_BLOB, 46 CLI_DUMP_BLOB, 47 CLI_FILL, 48 CLI_REM_XATTR, 49 CLI_SET_XATTR, 50 CLI_SET_SUPER, 51 CLI_SHOW_BS, 52 CLI_SHOW_BLOB, 53 CLI_CREATE_BLOB, 54 CLI_LIST_BDEVS, 55 CLI_LIST_BLOBS, 56 CLI_INIT_BS, 57 CLI_DUMP_BS, 58 CLI_SHELL_EXIT, 59 CLI_HELP, 60 CLI_RECOVER, 61 CLI_DELETE_BLOB, 62 }; 63 64 #define BUFSIZE 255 65 #define MAX_ARGS 16 66 #define ALIGN_4K 4096 67 #define STARTING_IO_UNIT 0 68 #define NUM_IO_UNITS 1 69 70 /* 71 * The CLI uses the SPDK app framework so is async and callback driven. A 72 * pointer to this structure is passed to SPDK calls and returned in the 73 * callbacks for easy access to all the info we may need. 74 */ 75 struct cli_context_t { 76 struct spdk_blob_store *bs; 77 struct spdk_blob *blob; 78 struct spdk_bs_dev *bs_dev; 79 spdk_blob_id blobid; 80 spdk_blob_id superid; 81 struct spdk_io_channel *channel; 82 uint8_t *buff; 83 uint64_t page_size; 84 uint64_t io_unit_size; 85 uint64_t io_unit_count; 86 uint64_t blob_io_units; 87 uint64_t bytes_so_far; 88 FILE *fp; 89 enum cli_action_type action; 90 char key[BUFSIZE + 1]; 91 char value[BUFSIZE + 1]; 92 char file[BUFSIZE + 1]; 93 uint64_t filesize; 94 int fill_value; 95 char bdev_name[BUFSIZE]; 96 int rc; 97 int num_clusters; 98 enum cli_mode_type cli_mode; 99 const char *config_file; 100 int argc; 101 char *argv[MAX_ARGS]; 102 bool app_started; 103 char script_file[BUFSIZE + 1]; 104 }; 105 106 /* we store a bunch of stuff in a global struct for use by scripting mode */ 107 #define MAX_SCRIPT_LINES 64 108 #define MAX_SCRIPT_BLOBS 16 109 struct cli_script_t { 110 spdk_blob_id blobid[MAX_SCRIPT_BLOBS]; 111 int blobid_idx; 112 int max_index; 113 int cmdline_idx; 114 bool ignore_errors; 115 char *cmdline[MAX_SCRIPT_LINES]; 116 }; 117 struct cli_script_t g_script; 118 119 /* 120 * Common printing of commands for CLI and shell modes. 121 */ 122 static void 123 print_cmds(void) 124 { 125 printf("\nCommands include:\n"); 126 printf("\t-b bdev - name of the block device to use (example: Nvme0n1)\n"); 127 printf("\t-d <blobid> filename - dump contents of a blob to a file\n"); 128 printf("\t-D - dump metadata contents of an existing blobstore\n"); 129 printf("\t-f <blobid> value - fill a blob with a decimal value\n"); 130 printf("\t-h - this help screen\n"); 131 printf("\t-i - initialize a blobstore\n"); 132 printf("\t-l bdevs | blobs - list either available bdevs or existing blobs\n"); 133 printf("\t-m <blobid> filename - import contents of a file to a blob\n"); 134 printf("\t-n <# clusters> - create new blob\n"); 135 printf("\t-p <blobid> - set the superblob to the ID provided\n"); 136 printf("\t-r <blobid> name - remove xattr name/value pair\n"); 137 printf("\t-R - recover the blobstore: like fsck for the blobstore\n"); 138 printf("\t-s <blobid> | bs - show blob info or blobstore info\n"); 139 printf("\t-S - enter interactive shell mode\n"); 140 printf("\t-T <filename> - automated script mode\n"); 141 printf("\t-w <blobid> - delete (whack) a blob\n"); 142 printf("\t-x <blobid> name value - set xattr name/value pair\n"); 143 printf("\t-X - exit when in interactive shell mode\n"); 144 printf("\n"); 145 } 146 147 /* 148 * Prints usage and relevant error message. 149 */ 150 static void 151 usage(struct cli_context_t *cli_context, char *msg) 152 { 153 if (msg) { 154 printf("%s", msg); 155 } 156 157 if (!cli_context || cli_context->cli_mode == CLI_MODE_CMD) { 158 printf("Version %s\n", SPDK_VERSION_STRING); 159 printf("Usage: %s [-j SPDK josn_config_file] Command\n", program_name); 160 printf("\n%s is a command line tool for interacting with blobstore\n", 161 program_name); 162 printf("on the underlying device specified in the conf file passed\n"); 163 printf("in as a command line option.\n"); 164 } 165 if (!cli_context || cli_context->cli_mode != CLI_MODE_SCRIPT) { 166 print_cmds(); 167 } 168 } 169 170 /* 171 * Free up memory that we allocated. 172 */ 173 static void 174 cli_cleanup(struct cli_context_t *cli_context) 175 { 176 if (cli_context->buff) { 177 spdk_free(cli_context->buff); 178 } 179 if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 180 int i; 181 182 for (i = 0; i <= g_script.max_index; i++) { 183 free(g_script.cmdline[i]); 184 } 185 } 186 free(cli_context); 187 } 188 189 /* 190 * Callback routine for the blobstore unload. 191 */ 192 static void 193 unload_complete(void *cb_arg, int bserrno) 194 { 195 struct cli_context_t *cli_context = cb_arg; 196 197 if (bserrno) { 198 printf("Error %d unloading the bobstore\n", bserrno); 199 cli_context->rc = bserrno; 200 } 201 202 /* 203 * Quit if we're in cmd mode or exiting shell mode, otherwise 204 * clear the action field and start the main function again. 205 */ 206 if (cli_context->cli_mode == CLI_MODE_CMD || 207 cli_context->action == CLI_SHELL_EXIT) { 208 spdk_app_stop(cli_context->rc); 209 } else { 210 /* when action is CLI_NONE, we know we need to remain in the shell */ 211 cli_context->bs = NULL; 212 cli_context->action = CLI_NONE; 213 cli_start(cli_context); 214 } 215 } 216 217 /* 218 * Unload the blobstore. 219 */ 220 static void 221 unload_bs(struct cli_context_t *cli_context, char *msg, int bserrno) 222 { 223 if (bserrno) { 224 printf("%s (err %d)\n", msg, bserrno); 225 cli_context->rc = bserrno; 226 } 227 228 if (cli_context->bs) { 229 if (cli_context->channel) { 230 spdk_bs_free_io_channel(cli_context->channel); 231 cli_context->channel = NULL; 232 } 233 spdk_bs_unload(cli_context->bs, unload_complete, cli_context); 234 } else if (cli_context->cli_mode != CLI_MODE_SCRIPT) { 235 spdk_app_stop(bserrno); 236 237 } 238 } 239 240 /* 241 * Callback for closing a blob. 242 */ 243 static void 244 close_cb(void *arg1, int bserrno) 245 { 246 struct cli_context_t *cli_context = arg1; 247 248 if (bserrno) { 249 unload_bs(cli_context, "Error in close callback", 250 bserrno); 251 return; 252 } 253 unload_bs(cli_context, "", 0); 254 } 255 256 /* 257 * Callback function for syncing metadata. 258 */ 259 static void 260 sync_cb(void *arg1, int bserrno) 261 { 262 struct cli_context_t *cli_context = arg1; 263 264 if (bserrno) { 265 unload_bs(cli_context, "Error in sync callback", 266 bserrno); 267 return; 268 } 269 270 spdk_blob_close(cli_context->blob, close_cb, cli_context); 271 } 272 273 static void 274 resize_cb(void *cb_arg, int bserrno) 275 { 276 struct cli_context_t *cli_context = cb_arg; 277 uint64_t total = 0; 278 279 if (bserrno) { 280 unload_bs(cli_context, "Error in blob resize", 281 bserrno); 282 return; 283 } 284 285 total = spdk_blob_get_num_clusters(cli_context->blob); 286 printf("blob now has USED clusters of %" PRIu64 "\n", 287 total); 288 289 /* 290 * Always a good idea to sync after MD changes or the changes 291 * may be lost if things aren't closed cleanly. 292 */ 293 spdk_blob_sync_md(cli_context->blob, sync_cb, cli_context); 294 } 295 296 /* 297 * Callback function for opening a blob after creating. 298 */ 299 static void 300 open_now_resize_cb(void *cb_arg, struct spdk_blob *blob, int bserrno) 301 { 302 struct cli_context_t *cli_context = cb_arg; 303 304 if (bserrno) { 305 unload_bs(cli_context, "Error in open completion", 306 bserrno); 307 return; 308 } 309 cli_context->blob = blob; 310 311 spdk_blob_resize(cli_context->blob, cli_context->num_clusters, 312 resize_cb, cli_context); 313 } 314 315 /* 316 * Callback function for creating a blob. 317 */ 318 static void 319 blob_create_cb(void *arg1, spdk_blob_id blobid, int bserrno) 320 { 321 struct cli_context_t *cli_context = arg1; 322 323 if (bserrno) { 324 unload_bs(cli_context, "Error in blob create callback", 325 bserrno); 326 return; 327 } 328 329 cli_context->blobid = blobid; 330 printf("New blob id 0x%" PRIx64 "\n", cli_context->blobid); 331 332 /* if we're in script mode, we need info on all blobids for later */ 333 if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 334 g_script.blobid[g_script.blobid_idx++] = blobid; 335 } 336 337 /* We have to open the blob before we can do things like resize. */ 338 spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 339 open_now_resize_cb, cli_context); 340 } 341 342 /* 343 * Callback for get_super where we'll continue on to show blobstore info. 344 */ 345 static void 346 show_bs_cb(void *arg1, spdk_blob_id blobid, int bserrno) 347 { 348 struct cli_context_t *cli_context = arg1; 349 struct spdk_bs_type bstype; 350 uint64_t val; 351 struct spdk_bdev *bdev = NULL; 352 353 if (bserrno && bserrno != -ENOENT) { 354 unload_bs(cli_context, "Error in get_super callback", 355 bserrno); 356 return; 357 } 358 cli_context->superid = blobid; 359 360 bdev = spdk_bdev_get_by_name(cli_context->bdev_name); 361 if (bdev == NULL) { 362 unload_bs(cli_context, "Error w/bdev in get_super callback", 363 bserrno); 364 return; 365 } 366 367 printf("Blobstore Public Info:\n"); 368 printf("\tUsing bdev Product Name: %s\n", 369 spdk_bdev_get_product_name(bdev)); 370 printf("\tAPI Version: %d\n", SPDK_BS_VERSION); 371 372 if (bserrno != -ENOENT) { 373 printf("\tsuper blob ID: 0x%" PRIx64 "\n", cli_context->superid); 374 } else { 375 printf("\tsuper blob ID: none assigned\n"); 376 } 377 378 printf("\tmd page size: %" PRIu64 "\n", cli_context->page_size); 379 printf("\tio unit size: %" PRIu64 "\n", cli_context->io_unit_size); 380 381 val = spdk_bs_get_cluster_size(cli_context->bs); 382 printf("\tcluster size: %" PRIu64 "\n", val); 383 384 val = spdk_bs_free_cluster_count(cli_context->bs); 385 printf("\t# free clusters: %" PRIu64 "\n", val); 386 387 bstype = spdk_bs_get_bstype(cli_context->bs); 388 spdk_log_dump(stdout, "\tblobstore type:", &bstype, sizeof(bstype)); 389 390 /* 391 * Private info isn't accessible via the public API but 392 * may be useful for debug of blobstore based applications. 393 */ 394 printf("\nBlobstore Private Info:\n"); 395 printf("\tMetadata start (pages): %" PRIu64 "\n", 396 cli_context->bs->md_start); 397 printf("\tMetadata length (pages): %d\n", 398 cli_context->bs->md_len); 399 400 unload_bs(cli_context, "", 0); 401 } 402 403 /* 404 * Show detailed info about a particular blob. 405 */ 406 static void 407 show_blob(struct cli_context_t *cli_context) 408 { 409 uint64_t val; 410 struct spdk_xattr_names *names; 411 const void *value; 412 size_t value_len; 413 unsigned int i; 414 415 printf("Blob Public Info:\n"); 416 417 printf("blob ID: 0x%" PRIx64 "\n", cli_context->blobid); 418 419 val = spdk_blob_get_num_clusters(cli_context->blob); 420 printf("# of clusters: %" PRIu64 "\n", val); 421 422 printf("# of bytes: %" PRIu64 "\n", 423 val * spdk_bs_get_cluster_size(cli_context->bs)); 424 425 val = spdk_blob_get_num_io_units(cli_context->blob); 426 printf("# of io units: %" PRIu64 "\n", val); 427 428 spdk_blob_get_xattr_names(cli_context->blob, &names); 429 430 printf("# of xattrs: %d\n", spdk_xattr_names_get_count(names)); 431 printf("xattrs:\n"); 432 for (i = 0; i < spdk_xattr_names_get_count(names); i++) { 433 spdk_blob_get_xattr_value(cli_context->blob, 434 spdk_xattr_names_get_name(names, i), 435 &value, &value_len); 436 if (value_len > BUFSIZE) { 437 printf("FYI: adjusting size of xattr due to CLI limits.\n"); 438 value_len = BUFSIZE + 1; 439 } 440 printf("\n(%d) Name:%s\n", i, 441 spdk_xattr_names_get_name(names, i)); 442 printf("(%d) Value:\n", i); 443 spdk_log_dump(stdout, "", value, value_len - 1); 444 } 445 446 /* 447 * Private info isn't accessible via the public API but 448 * may be useful for debug of blobstore based applications. 449 */ 450 printf("\nBlob Private Info:\n"); 451 switch (cli_context->blob->state) { 452 case SPDK_BLOB_STATE_DIRTY: 453 printf("state: DIRTY\n"); 454 break; 455 case SPDK_BLOB_STATE_CLEAN: 456 printf("state: CLEAN\n"); 457 break; 458 case SPDK_BLOB_STATE_LOADING: 459 printf("state: LOADING\n"); 460 break; 461 default: 462 printf("state: UNKNOWN\n"); 463 break; 464 } 465 printf("open ref count: %d\n", 466 cli_context->blob->open_ref); 467 468 spdk_xattr_names_free(names); 469 } 470 471 /* 472 * Callback for getting the first blob, shared with simple blob listing as well. 473 */ 474 static void 475 blob_iter_cb(void *arg1, struct spdk_blob *blob, int bserrno) 476 { 477 struct cli_context_t *cli_context = arg1; 478 479 if (bserrno) { 480 if (bserrno == -ENOENT) { 481 /* this simply means there are no more blobs */ 482 unload_bs(cli_context, "", 0); 483 } else { 484 unload_bs(cli_context, "Error in blob iter callback", 485 bserrno); 486 } 487 return; 488 } 489 490 if (cli_context->action == CLI_LIST_BLOBS) { 491 printf("\nList BLOBS:\n"); 492 printf("Found blob with ID# 0x%" PRIx64 "\n", 493 spdk_blob_get_id(blob)); 494 } else if (spdk_blob_get_id(blob) == cli_context->blobid) { 495 /* 496 * Found the blob we're looking for, but we need to finish 497 * iterating even after showing the info so that internally 498 * the blobstore logic will close the blob. Or we could 499 * chose to close it now, either way. 500 */ 501 cli_context->blob = blob; 502 show_blob(cli_context); 503 } 504 505 spdk_bs_iter_next(cli_context->bs, blob, blob_iter_cb, cli_context); 506 } 507 508 /* 509 * Callback for setting the super blob ID. 510 */ 511 static void 512 set_super_cb(void *arg1, int bserrno) 513 { 514 struct cli_context_t *cli_context = arg1; 515 516 if (bserrno) { 517 unload_bs(cli_context, "Error in set_super callback", 518 bserrno); 519 return; 520 } 521 522 printf("Super Blob ID has been set.\n"); 523 unload_bs(cli_context, "", 0); 524 } 525 526 /* 527 * Callback for set_xattr_open where we set or delete xattrs. 528 */ 529 static void 530 set_xattr_cb(void *cb_arg, struct spdk_blob *blob, int bserrno) 531 { 532 struct cli_context_t *cli_context = cb_arg; 533 534 if (bserrno) { 535 unload_bs(cli_context, "Error in blob open callback", 536 bserrno); 537 return; 538 } 539 cli_context->blob = blob; 540 541 if (cli_context->action == CLI_SET_XATTR) { 542 spdk_blob_set_xattr(cli_context->blob, cli_context->key, 543 cli_context->value, strlen(cli_context->value) + 1); 544 printf("Xattr has been set.\n"); 545 } else { 546 spdk_blob_remove_xattr(cli_context->blob, cli_context->key); 547 printf("Xattr has been removed.\n"); 548 } 549 550 spdk_blob_sync_md(cli_context->blob, sync_cb, cli_context); 551 } 552 553 static void __read_dump_cb(void *arg1); 554 555 /* 556 * Callback function for reading a blob for dumping to a file. 557 */ 558 static void 559 read_dump_cb(void *arg1, int bserrno) 560 { 561 struct cli_context_t *cli_context = arg1; 562 uint64_t bytes_written; 563 564 if (bserrno) { 565 fclose(cli_context->fp); 566 unload_bs(cli_context, "Error in read completion", 567 bserrno); 568 return; 569 } 570 571 bytes_written = fwrite(cli_context->buff, NUM_IO_UNITS, cli_context->io_unit_size, 572 cli_context->fp); 573 if (bytes_written != cli_context->io_unit_size) { 574 fclose(cli_context->fp); 575 unload_bs(cli_context, "Error with fwrite", 576 bserrno); 577 return; 578 } 579 580 /* This completion may have occurred in the context of a read to 581 * an unallocated cluster. So we can't issue the next read here, or 582 * we risk overflowing the stack. So use spdk_thread_send_msg() to 583 * make sure we unwind before doing the next read. 584 */ 585 spdk_thread_send_msg(spdk_get_thread(), __read_dump_cb, cli_context); 586 } 587 588 static void 589 __read_dump_cb(void *arg1) 590 { 591 struct cli_context_t *cli_context = arg1; 592 593 printf("."); 594 if (++cli_context->io_unit_count < cli_context->blob_io_units) { 595 /* perform another read */ 596 spdk_blob_io_read(cli_context->blob, cli_context->channel, 597 cli_context->buff, cli_context->io_unit_count, 598 NUM_IO_UNITS, read_dump_cb, cli_context); 599 } else { 600 /* done reading */ 601 printf("\nFile write complete (to %s).\n", cli_context->file); 602 fclose(cli_context->fp); 603 spdk_blob_close(cli_context->blob, close_cb, cli_context); 604 } 605 } 606 607 /* 608 * Callback for write completion on the import of a file to a blob. 609 */ 610 static void 611 write_imp_cb(void *arg1, int bserrno) 612 { 613 struct cli_context_t *cli_context = arg1; 614 uint64_t bytes_read; 615 616 if (bserrno) { 617 fclose(cli_context->fp); 618 unload_bs(cli_context, "Error in write completion", 619 bserrno); 620 return; 621 } 622 623 if (cli_context->bytes_so_far < cli_context->filesize) { 624 /* perform another file read */ 625 bytes_read = fread(cli_context->buff, 1, 626 cli_context->io_unit_size, 627 cli_context->fp); 628 cli_context->bytes_so_far += bytes_read; 629 630 /* if this read is < 1 io_unit, fill with 0s */ 631 if (bytes_read < cli_context->io_unit_size) { 632 uint8_t *offset = cli_context->buff + bytes_read; 633 memset(offset, 0, cli_context->io_unit_size - bytes_read); 634 } 635 } else { 636 /* 637 * Done reading the file, fill the rest of the blob with 0s, 638 * yeah we're memsetting the same io_unit over and over here 639 */ 640 memset(cli_context->buff, 0, cli_context->io_unit_size); 641 } 642 if (++cli_context->io_unit_count < cli_context->blob_io_units) { 643 printf("."); 644 spdk_blob_io_write(cli_context->blob, cli_context->channel, 645 cli_context->buff, cli_context->io_unit_count, 646 NUM_IO_UNITS, write_imp_cb, cli_context); 647 } else { 648 /* done writing */ 649 printf("\nBlob import complete (from %s).\n", cli_context->file); 650 fclose(cli_context->fp); 651 spdk_blob_close(cli_context->blob, close_cb, cli_context); 652 } 653 } 654 655 /* 656 * Callback for open blobs where we'll continue on dump a blob to a file or 657 * import a file to a blob. For dump, the resulting file will always be the 658 * full size of the blob. For import, the blob will fill with the file 659 * contents first and then 0 out the rest of the blob. 660 */ 661 static void 662 dump_imp_open_cb(void *cb_arg, struct spdk_blob *blob, int bserrno) 663 { 664 struct cli_context_t *cli_context = cb_arg; 665 666 if (bserrno) { 667 unload_bs(cli_context, "Error in blob open callback", 668 bserrno); 669 return; 670 } 671 cli_context->blob = blob; 672 673 /* 674 * We'll transfer just one io_unit at a time to keep the buffer 675 * small. This could be bigger of course. 676 */ 677 cli_context->buff = spdk_malloc(cli_context->io_unit_size, ALIGN_4K, NULL, 678 SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); 679 if (cli_context->buff == NULL) { 680 printf("Error in allocating memory\n"); 681 spdk_blob_close(cli_context->blob, close_cb, cli_context); 682 return; 683 } 684 printf("Working"); 685 cli_context->blob_io_units = spdk_blob_get_num_io_units(cli_context->blob); 686 cli_context->io_unit_count = 0; 687 if (cli_context->action == CLI_DUMP_BLOB) { 688 cli_context->fp = fopen(cli_context->file, "w"); 689 if (cli_context->fp == NULL) { 690 printf("Error in opening file\n"); 691 spdk_blob_close(cli_context->blob, close_cb, cli_context); 692 return; 693 } 694 695 if (cli_context->blob->active.num_clusters == 0) { 696 fclose(cli_context->fp); 697 spdk_blob_close(cli_context->blob, close_cb, cli_context); 698 return; 699 } 700 701 /* read a io_unit of data from the blob */ 702 spdk_blob_io_read(cli_context->blob, cli_context->channel, 703 cli_context->buff, cli_context->io_unit_count, 704 NUM_IO_UNITS, read_dump_cb, cli_context); 705 } else { 706 cli_context->fp = fopen(cli_context->file, "r"); 707 if (cli_context->fp == NULL) { 708 printf("Error in opening file: errno %d\n", errno); 709 spdk_blob_close(cli_context->blob, close_cb, cli_context); 710 return; 711 } 712 713 /* get the filesize then rewind read a io_unit of data from file */ 714 fseek(cli_context->fp, 0L, SEEK_END); 715 cli_context->filesize = ftell(cli_context->fp); 716 rewind(cli_context->fp); 717 cli_context->bytes_so_far = fread(cli_context->buff, NUM_IO_UNITS, 718 cli_context->io_unit_size, 719 cli_context->fp); 720 721 /* if the file is < a io_unit, fill the rest with 0s */ 722 if (cli_context->filesize < cli_context->io_unit_size) { 723 uint8_t *offset = 724 cli_context->buff + cli_context->filesize; 725 726 memset(offset, 0, 727 cli_context->io_unit_size - cli_context->filesize); 728 } 729 730 spdk_blob_io_write(cli_context->blob, cli_context->channel, 731 cli_context->buff, cli_context->io_unit_count, 732 NUM_IO_UNITS, write_imp_cb, cli_context); 733 } 734 } 735 736 /* 737 * Callback function for writing a specific pattern to io_unit 0. 738 */ 739 static void 740 write_cb(void *arg1, int bserrno) 741 { 742 struct cli_context_t *cli_context = arg1; 743 744 if (bserrno) { 745 unload_bs(cli_context, "Error in write completion", 746 bserrno); 747 return; 748 } 749 printf("."); 750 if (++cli_context->io_unit_count < cli_context->blob_io_units) { 751 spdk_blob_io_write(cli_context->blob, cli_context->channel, 752 cli_context->buff, cli_context->io_unit_count, 753 NUM_IO_UNITS, write_cb, cli_context); 754 } else { 755 /* done writing */ 756 printf("\nBlob fill complete (with 0x%x).\n", cli_context->fill_value); 757 spdk_blob_close(cli_context->blob, close_cb, cli_context); 758 } 759 } 760 761 /* 762 * Callback function to fill a blob with a value, callback from open. 763 */ 764 static void 765 fill_blob_cb(void *arg1, struct spdk_blob *blob, int bserrno) 766 { 767 struct cli_context_t *cli_context = arg1; 768 769 if (bserrno) { 770 unload_bs(cli_context, "Error in open callback", 771 bserrno); 772 return; 773 } 774 775 cli_context->blob = blob; 776 cli_context->io_unit_count = 0; 777 cli_context->blob_io_units = spdk_blob_get_num_io_units(cli_context->blob); 778 cli_context->buff = spdk_malloc(cli_context->io_unit_size, ALIGN_4K, NULL, 779 SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); 780 if (cli_context->buff == NULL) { 781 unload_bs(cli_context, "Error in allocating memory", 782 -ENOMEM); 783 return; 784 } 785 786 memset(cli_context->buff, cli_context->fill_value, 787 cli_context->io_unit_size); 788 printf("Working"); 789 spdk_blob_io_write(cli_context->blob, cli_context->channel, 790 cli_context->buff, 791 STARTING_IO_UNIT, NUM_IO_UNITS, write_cb, cli_context); 792 } 793 794 /* 795 * Callback for deleting a blob 796 */ 797 static void 798 delete_blob_cb(void *arg1, int bserrno) 799 { 800 struct cli_context_t *cli_context = arg1; 801 802 if (bserrno) { 803 unload_bs(cli_context, "Error in delete_blob callback", 804 bserrno); 805 return; 806 } 807 808 printf("Blob 0x%lx has been deleted.\n", cli_context->blobid); 809 unload_bs(cli_context, "", 0); 810 } 811 812 /* 813 * Multiple actions require us to open the bs first so here we use 814 * a common callback to set a bunch of values and then move on to 815 * the next step saved off via function pointer. 816 */ 817 static void 818 load_bs_cb(void *arg1, struct spdk_blob_store *bs, int bserrno) 819 { 820 struct cli_context_t *cli_context = arg1; 821 822 if (bserrno) { 823 unload_bs(cli_context, "Error in load callback", 824 bserrno); 825 return; 826 } 827 828 cli_context->bs = bs; 829 cli_context->page_size = spdk_bs_get_page_size(cli_context->bs); 830 cli_context->io_unit_size = spdk_bs_get_io_unit_size(cli_context->bs); 831 cli_context->channel = spdk_bs_alloc_io_channel(cli_context->bs); 832 if (cli_context->channel == NULL) { 833 unload_bs(cli_context, "Error in allocating channel", 834 -ENOMEM); 835 return; 836 } 837 838 switch (cli_context->action) { 839 case CLI_SET_SUPER: 840 spdk_bs_set_super(cli_context->bs, cli_context->superid, 841 set_super_cb, cli_context); 842 break; 843 case CLI_SHOW_BS: 844 spdk_bs_get_super(cli_context->bs, show_bs_cb, cli_context); 845 break; 846 case CLI_CREATE_BLOB: 847 spdk_bs_create_blob(cli_context->bs, blob_create_cb, cli_context); 848 break; 849 case CLI_SET_XATTR: 850 case CLI_REM_XATTR: 851 spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 852 set_xattr_cb, cli_context); 853 break; 854 case CLI_SHOW_BLOB: 855 case CLI_LIST_BLOBS: 856 spdk_bs_iter_first(cli_context->bs, blob_iter_cb, cli_context); 857 858 break; 859 case CLI_DUMP_BLOB: 860 case CLI_IMPORT_BLOB: 861 spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 862 dump_imp_open_cb, cli_context); 863 break; 864 case CLI_FILL: 865 spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 866 fill_blob_cb, cli_context); 867 break; 868 case CLI_RECOVER: 869 unload_bs(cli_context, "", 0); 870 break; 871 case CLI_DELETE_BLOB: 872 spdk_bs_delete_blob(cli_context->bs, cli_context->blobid, 873 delete_blob_cb, cli_context); 874 break; 875 876 default: 877 /* should never get here */ 878 exit(-1); 879 break; 880 } 881 } 882 883 static void 884 base_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, 885 void *event_ctx) 886 { 887 printf("Unsupported bdev event: type %d on bdev %s\n", type, spdk_bdev_get_name(bdev)); 888 } 889 890 /* 891 * Load the blobstore. 892 */ 893 static void 894 load_bs(struct cli_context_t *cli_context) 895 { 896 struct spdk_bs_dev *bs_dev = NULL; 897 int rc; 898 struct spdk_bs_opts opts = {}; 899 struct spdk_bs_opts *optsp = NULL; 900 901 rc = spdk_bdev_create_bs_dev_ext(cli_context->bdev_name, base_bdev_event_cb, 902 NULL, &bs_dev); 903 if (rc != 0) { 904 printf("Could not create blob bdev, %s!!\n", spdk_strerror(-rc)); 905 spdk_app_stop(-1); 906 return; 907 } 908 909 if (cli_context->action == CLI_RECOVER) { 910 spdk_bs_opts_init(&opts, sizeof(opts)); 911 opts.force_recover = true; 912 optsp = &opts; 913 } 914 915 spdk_bs_load(bs_dev, optsp, load_bs_cb, cli_context); 916 } 917 918 static int 919 print_bdev(void *ctx, struct spdk_bdev *bdev) 920 { 921 uint32_t *count = ctx; 922 923 (*count)++; 924 925 printf("\tbdev Name: %s\n", spdk_bdev_get_name(bdev)); 926 printf("\tbdev Product Name: %s\n", 927 spdk_bdev_get_product_name(bdev)); 928 return 0; 929 } 930 931 /* 932 * Lists all the blobs on this blobstore. 933 */ 934 static void 935 list_bdevs(struct cli_context_t *cli_context) 936 { 937 uint32_t count = 0; 938 939 printf("\nList bdevs:\n"); 940 941 spdk_for_each_bdev(&count, print_bdev); 942 943 if (count == 0) { 944 printf("Could not find a bdev\n"); 945 } 946 947 printf("\n"); 948 if (cli_context->cli_mode == CLI_MODE_CMD) { 949 spdk_app_stop(0); 950 } else { 951 cli_context->action = CLI_NONE; 952 cli_start(cli_context); 953 } 954 } 955 956 /* 957 * Callback function for initializing a blob. 958 */ 959 static void 960 bs_init_cb(void *cb_arg, struct spdk_blob_store *bs, 961 int bserrno) 962 { 963 struct cli_context_t *cli_context = cb_arg; 964 965 if (bserrno) { 966 unload_bs(cli_context, "Error in bs init callback", 967 bserrno); 968 return; 969 } 970 cli_context->bs = bs; 971 printf("blobstore init'd: (%p)\n", cli_context->bs); 972 973 unload_bs(cli_context, "", 0); 974 } 975 976 /* 977 * Initialize a new blobstore. 978 */ 979 static void 980 init_bs(struct cli_context_t *cli_context) 981 { 982 int rc; 983 984 printf("Init blobstore using bdev Name: %s\n", cli_context->bdev_name); 985 986 rc = spdk_bdev_create_bs_dev_ext(cli_context->bdev_name, base_bdev_event_cb, NULL, 987 &cli_context->bs_dev); 988 if (rc != 0) { 989 printf("Could not create blob bdev, %s!!\n", spdk_strerror(-rc)); 990 spdk_app_stop(-1); 991 return; 992 } 993 994 spdk_bs_init(cli_context->bs_dev, NULL, bs_init_cb, 995 cli_context); 996 } 997 998 static void 999 spdk_bsdump_done(void *arg, int bserrno) 1000 { 1001 struct cli_context_t *cli_context = arg; 1002 1003 if (cli_context->cli_mode == CLI_MODE_CMD) { 1004 spdk_app_stop(0); 1005 } else { 1006 cli_context->action = CLI_NONE; 1007 cli_start(cli_context); 1008 } 1009 } 1010 1011 static void 1012 bsdump_print_xattr(FILE *fp, const char *bstype, const char *name, const void *value, 1013 size_t value_len) 1014 { 1015 if (strncmp(bstype, "BLOBFS", SPDK_BLOBSTORE_TYPE_LENGTH) == 0) { 1016 if (strcmp(name, "name") == 0) { 1017 fprintf(fp, "%.*s", (int)value_len, (char *)value); 1018 } else if (strcmp(name, "length") == 0 && value_len == sizeof(uint64_t)) { 1019 uint64_t length; 1020 1021 memcpy(&length, value, sizeof(length)); 1022 fprintf(fp, "%" PRIu64, length); 1023 } else { 1024 fprintf(fp, "?"); 1025 } 1026 } else if (strncmp(bstype, "LVOLSTORE", SPDK_BLOBSTORE_TYPE_LENGTH) == 0) { 1027 if (strcmp(name, "name") == 0) { 1028 fprintf(fp, "%s", (char *)value); 1029 } else if (strcmp(name, "uuid") == 0) { 1030 struct spdk_uuid uuid; 1031 1032 if (spdk_uuid_parse(&uuid, (const char *)value) == 0) { 1033 fprintf(fp, "%s", (const char *)value); 1034 } else { 1035 fprintf(fp, "? Invalid UUID"); 1036 } 1037 } else { 1038 fprintf(fp, "?"); 1039 } 1040 } else { 1041 fprintf(fp, "?"); 1042 } 1043 } 1044 1045 /* 1046 * Dump metadata of an existing blobstore in a human-readable format. 1047 */ 1048 static void 1049 dump_bs(struct cli_context_t *cli_context) 1050 { 1051 int rc; 1052 1053 printf("Init blobstore using bdev Name: %s\n", cli_context->bdev_name); 1054 1055 rc = spdk_bdev_create_bs_dev_ext(cli_context->bdev_name, base_bdev_event_cb, NULL, 1056 &cli_context->bs_dev); 1057 if (rc != 0) { 1058 printf("Could not create blob bdev, %s!!\n", spdk_strerror(-rc)); 1059 spdk_app_stop(-1); 1060 return; 1061 } 1062 1063 spdk_bs_dump(cli_context->bs_dev, stdout, bsdump_print_xattr, spdk_bsdump_done, cli_context); 1064 } 1065 1066 /* 1067 * Common cmd/option parser for command and shell modes. 1068 */ 1069 static bool 1070 cmd_parser(int argc, char **argv, struct cli_context_t *cli_context) 1071 { 1072 int op; 1073 int cmd_chosen = 0; 1074 char resp; 1075 1076 while ((op = getopt(argc, argv, "b:d:f:hij:l:m:n:p:r:s:w:DRST:Xx:")) != -1) { 1077 switch (op) { 1078 case 'b': 1079 if (strcmp(cli_context->bdev_name, "") == 0) { 1080 snprintf(cli_context->bdev_name, BUFSIZE, "%s", optarg); 1081 } else { 1082 printf("Current setting for -b is: %s\n", cli_context->bdev_name); 1083 usage(cli_context, "ERROR: -b option can only be set once.\n"); 1084 } 1085 break; 1086 case 'D': 1087 cmd_chosen++; 1088 cli_context->action = CLI_DUMP_BS; 1089 break; 1090 case 'd': 1091 if (argv[optind] != NULL) { 1092 cmd_chosen++; 1093 cli_context->action = CLI_DUMP_BLOB; 1094 cli_context->blobid = spdk_strtoll(optarg, 0); 1095 snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]); 1096 } else { 1097 usage(cli_context, "ERROR: missing parameter.\n"); 1098 } 1099 break; 1100 case 'f': 1101 if (argv[optind] != NULL) { 1102 cmd_chosen++; 1103 cli_context->action = CLI_FILL; 1104 cli_context->blobid = spdk_strtoll(optarg, 0); 1105 cli_context->fill_value = spdk_strtol(argv[optind], 0); 1106 } else { 1107 usage(cli_context, "ERROR: missing parameter.\n"); 1108 } 1109 break; 1110 case 'h': 1111 cmd_chosen++; 1112 cli_context->action = CLI_HELP; 1113 break; 1114 case 'i': 1115 if (cli_context->cli_mode != CLI_MODE_SCRIPT) { 1116 printf("Your entire blobstore will be destroyed. Are you sure? (y/n) "); 1117 if (scanf("%c%*c", &resp)) { 1118 if (resp == 'y' || resp == 'Y') { 1119 cmd_chosen++; 1120 cli_context->action = CLI_INIT_BS; 1121 } else { 1122 if (cli_context->cli_mode == CLI_MODE_CMD) { 1123 spdk_app_stop(0); 1124 return false; 1125 } 1126 } 1127 } 1128 } else { 1129 cmd_chosen++; 1130 cli_context->action = CLI_INIT_BS; 1131 } 1132 break; 1133 case 'j': 1134 if (cli_context->app_started == false) { 1135 cli_context->config_file = optarg; 1136 } else { 1137 usage(cli_context, "ERROR: -j option not valid during shell mode.\n"); 1138 } 1139 break; 1140 case 'r': 1141 if (argv[optind] != NULL) { 1142 cmd_chosen++; 1143 cli_context->action = CLI_REM_XATTR; 1144 cli_context->blobid = spdk_strtoll(optarg, 0); 1145 snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]); 1146 } else { 1147 usage(cli_context, "ERROR: missing parameter.\n"); 1148 } 1149 break; 1150 case 'l': 1151 if (strcmp("bdevs", optarg) == 0) { 1152 cmd_chosen++; 1153 cli_context->action = CLI_LIST_BDEVS; 1154 } else if (strcmp("blobs", optarg) == 0) { 1155 cmd_chosen++; 1156 cli_context->action = CLI_LIST_BLOBS; 1157 } else { 1158 usage(cli_context, "ERROR: invalid option for list\n"); 1159 } 1160 break; 1161 case 'm': 1162 if (argv[optind] != NULL) { 1163 cmd_chosen++; 1164 cli_context->action = CLI_IMPORT_BLOB; 1165 cli_context->blobid = spdk_strtoll(optarg, 0); 1166 snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]); 1167 } else { 1168 usage(cli_context, "ERROR: missing parameter.\n"); 1169 } 1170 break; 1171 case 'n': 1172 cli_context->num_clusters = spdk_strtol(optarg, 10); 1173 if (cli_context->num_clusters > 0) { 1174 cmd_chosen++; 1175 cli_context->action = CLI_CREATE_BLOB; 1176 } else { 1177 usage(cli_context, "ERROR: invalid option for new\n"); 1178 } 1179 break; 1180 case 'p': 1181 cmd_chosen++; 1182 cli_context->action = CLI_SET_SUPER; 1183 cli_context->superid = spdk_strtoll(optarg, 0); 1184 break; 1185 case 'R': 1186 cmd_chosen++; 1187 cli_context->action = CLI_RECOVER; 1188 break; 1189 case 'S': 1190 if (cli_context->cli_mode == CLI_MODE_CMD) { 1191 cmd_chosen++; 1192 cli_context->cli_mode = CLI_MODE_SHELL; 1193 } 1194 cli_context->action = CLI_NONE; 1195 break; 1196 case 's': 1197 cmd_chosen++; 1198 if (strcmp("bs", optarg) == 0) { 1199 cli_context->action = CLI_SHOW_BS; 1200 } else { 1201 cli_context->action = CLI_SHOW_BLOB; 1202 cli_context->blobid = spdk_strtoll(optarg, 0); 1203 } 1204 break; 1205 case 'T': 1206 if (cli_context->cli_mode == CLI_MODE_CMD) { 1207 cmd_chosen++; 1208 cli_context->cli_mode = CLI_MODE_SCRIPT; 1209 if (argv[optind] && (strcmp("ignore", argv[optind]) == 0)) { 1210 g_script.ignore_errors = true; 1211 } else { 1212 g_script.ignore_errors = false; 1213 } 1214 snprintf(cli_context->script_file, BUFSIZE, "%s", optarg); 1215 } else { 1216 cli_context->action = CLI_NONE; 1217 } 1218 break; 1219 case 'w': 1220 cmd_chosen++; 1221 cli_context->action = CLI_DELETE_BLOB; 1222 cli_context->blobid = spdk_strtoll(optarg, 0); 1223 break; 1224 case 'X': 1225 cmd_chosen++; 1226 cli_context->action = CLI_SHELL_EXIT; 1227 break; 1228 case 'x': 1229 if (argv[optind] != NULL || argv[optind + 1] != NULL) { 1230 cmd_chosen++; 1231 cli_context->action = CLI_SET_XATTR; 1232 cli_context->blobid = spdk_strtoll(optarg, 0); 1233 snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]); 1234 snprintf(cli_context->value, BUFSIZE, "%s", argv[optind + 1]); 1235 } else { 1236 usage(cli_context, "ERROR: missing parameter.\n"); 1237 } 1238 break; 1239 default: 1240 usage(cli_context, "ERROR: invalid option\n"); 1241 } 1242 /* only one actual command can be done at a time */ 1243 if (cmd_chosen > 1) { 1244 usage(cli_context, "Error: Please choose only one command\n"); 1245 } 1246 } 1247 1248 if (cli_context->cli_mode == CLI_MODE_CMD && cmd_chosen == 0) { 1249 usage(cli_context, "Error: Please choose a command.\n"); 1250 } 1251 1252 /* 1253 * We don't check the local boolean because in some modes it will have been set 1254 * on and earlier command. 1255 */ 1256 if ((strcmp(cli_context->bdev_name, "") == 0) && (cli_context->action != CLI_HELP)) { 1257 usage(cli_context, "Error: -b option is required.\n"); 1258 cmd_chosen = 0; 1259 } 1260 1261 /* in shell mode we'll call getopt multiple times so need to reset its index */ 1262 optind = 0; 1263 return (cmd_chosen == 1); 1264 } 1265 1266 /* 1267 * In script mode, we parsed a script file at startup and saved off a bunch of cmd 1268 * lines that we now parse with each run of cli_start so we us the same cmd parser 1269 * as cmd and shell modes. 1270 */ 1271 static bool 1272 line_parser(struct cli_context_t *cli_context) 1273 { 1274 bool cmd_chosen; 1275 char *tok = NULL; 1276 char *sp = NULL; 1277 int blob_num = 0; 1278 int start_idx = cli_context->argc; 1279 int i; 1280 1281 printf("\nSCRIPT NOW PROCESSING: %s\n", g_script.cmdline[g_script.cmdline_idx]); 1282 tok = strtok_r(g_script.cmdline[g_script.cmdline_idx], " ", &sp); 1283 while (tok != NULL) { 1284 /* 1285 * We support one replaceable token right now, a $Bn 1286 * represents the blobid that was created in position n 1287 * so fish this out now and use it here. 1288 */ 1289 cli_context->argv[cli_context->argc] = strdup(tok); 1290 if (tok[0] == '$' && tok[1] == 'B') { 1291 tok += 2; 1292 blob_num = spdk_strtol(tok, 10); 1293 if (blob_num >= 0 && blob_num < MAX_SCRIPT_BLOBS) { 1294 cli_context->argv[cli_context->argc] = 1295 realloc(cli_context->argv[cli_context->argc], BUFSIZE); 1296 if (cli_context->argv[cli_context->argc] == NULL) { 1297 printf("ERROR: unable to realloc memory\n"); 1298 spdk_app_stop(-1); 1299 } 1300 if (g_script.blobid[blob_num] == 0) { 1301 printf("ERROR: There is no blob for $B%d\n", 1302 blob_num); 1303 } 1304 snprintf(cli_context->argv[cli_context->argc], BUFSIZE, 1305 "%" PRIu64, g_script.blobid[blob_num]); 1306 } else { 1307 printf("ERROR: Invalid token or exceeded max blobs of %d\n", 1308 MAX_SCRIPT_BLOBS); 1309 } 1310 } 1311 cli_context->argc++; 1312 tok = strtok_r(NULL, " ", &sp); 1313 } 1314 1315 /* call parse cmd line with user input as args */ 1316 cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context); 1317 1318 /* free strdup memory and reset arg count for next shell interaction */ 1319 for (i = start_idx; i < cli_context->argc; i++) { 1320 free(cli_context->argv[i]); 1321 cli_context->argv[i] = NULL; 1322 } 1323 cli_context->argc = 1; 1324 1325 g_script.cmdline_idx++; 1326 assert(g_script.cmdline_idx < MAX_SCRIPT_LINES); 1327 1328 if (cmd_chosen == false) { 1329 printf("ERROR: Invalid script line starting with: %s\n\n", 1330 g_script.cmdline[g_script.cmdline_idx - 1]); 1331 if (g_script.ignore_errors == false) { 1332 printf("** Aborting **\n"); 1333 cli_context->action = CLI_SHELL_EXIT; 1334 cmd_chosen = true; 1335 unload_bs(cli_context, "", 0); 1336 } else { 1337 printf("** Skipping **\n"); 1338 } 1339 } 1340 1341 return cmd_chosen; 1342 } 1343 1344 /* 1345 * For script mode, we read a series of commands from a text file and store them 1346 * in a global struct. That, along with the cli_mode that tells us we're in 1347 * script mode is what feeds the rest of the app in the same way as is it were 1348 * getting commands from shell mode. 1349 */ 1350 static void 1351 parse_script(struct cli_context_t *cli_context) 1352 { 1353 FILE *fp = NULL; 1354 size_t bufsize = BUFSIZE; 1355 int64_t bytes_in = 0; 1356 int i = 0; 1357 1358 /* initialize global script values */ 1359 for (i = 0; i < MAX_SCRIPT_BLOBS; i++) { 1360 g_script.blobid[i] = 0; 1361 } 1362 g_script.blobid_idx = 0; 1363 g_script.cmdline_idx = 0; 1364 i = 0; 1365 1366 fp = fopen(cli_context->script_file, "r"); 1367 if (fp == NULL) { 1368 printf("ERROR: unable to open script: %s\n", 1369 cli_context->script_file); 1370 cli_cleanup(cli_context); 1371 exit(-1); 1372 } 1373 1374 do { 1375 bytes_in = getline(&g_script.cmdline[i], &bufsize, fp); 1376 if (bytes_in > 0) { 1377 /* replace newline with null */ 1378 spdk_str_chomp(g_script.cmdline[i]); 1379 1380 /* ignore comments */ 1381 if (g_script.cmdline[i][0] != '#') { 1382 i++; 1383 } 1384 } 1385 } while (bytes_in != -1 && i < MAX_SCRIPT_LINES - 1); 1386 fclose(fp); 1387 1388 /* add an exit cmd in case they didn't */ 1389 g_script.cmdline[i] = realloc(g_script.cmdline[i], BUFSIZE); 1390 if (g_script.cmdline[i] == NULL) { 1391 int j; 1392 1393 for (j = 0; j < i; j++) { 1394 free(g_script.cmdline[j]); 1395 g_script.cmdline[j] = NULL; 1396 } 1397 unload_bs(cli_context, "ERROR: unable to alloc memory.\n", 0); 1398 } 1399 snprintf(g_script.cmdline[i], BUFSIZE, "%s", "-X"); 1400 g_script.max_index = i; 1401 } 1402 1403 /* 1404 * Provides for a shell interface as opposed to one shot command line. 1405 */ 1406 static bool 1407 cli_shell(void *arg1, void *arg2) 1408 { 1409 struct cli_context_t *cli_context = arg1; 1410 char *line = NULL; 1411 ssize_t buf_size = 0; 1412 ssize_t bytes_in = 0; 1413 ssize_t tok_len = 0; 1414 char *tok = NULL; 1415 char *sp = NULL; 1416 bool cmd_chosen = false; 1417 int start_idx = cli_context->argc; 1418 int i; 1419 1420 printf("blob> "); 1421 bytes_in = getline(&line, &buf_size, stdin); 1422 1423 /* If getline() failed (EOF), exit the shell. */ 1424 if (bytes_in < 0) { 1425 free(line); 1426 cli_context->action = CLI_SHELL_EXIT; 1427 return true; 1428 } 1429 1430 /* parse input and update cli_context so we can use common option parser */ 1431 if (bytes_in > 0) { 1432 tok = strtok_r(line, " ", &sp); 1433 } 1434 while ((tok != NULL) && (cli_context->argc < MAX_ARGS)) { 1435 cli_context->argv[cli_context->argc] = strdup(tok); 1436 tok_len = strlen(tok); 1437 cli_context->argc++; 1438 tok = strtok_r(NULL, " ", &sp); 1439 } 1440 1441 /* replace newline on last arg with null */ 1442 if (tok_len) { 1443 spdk_str_chomp(cli_context->argv[cli_context->argc - 1]); 1444 } 1445 1446 /* call parse cmd line with user input as args */ 1447 cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context); 1448 1449 /* free strdup mem & reset arg count for next shell interaction */ 1450 for (i = start_idx; i < cli_context->argc; i++) { 1451 free(cli_context->argv[i]); 1452 cli_context->argv[i] = NULL; 1453 } 1454 cli_context->argc = 1; 1455 1456 free(line); 1457 1458 return cmd_chosen; 1459 } 1460 1461 /* 1462 * This is the function we pass into the SPDK framework that gets 1463 * called first. 1464 */ 1465 static void 1466 cli_start(void *arg1) 1467 { 1468 struct cli_context_t *cli_context = arg1; 1469 1470 /* 1471 * If we're in script mode, we already have a list of commands so 1472 * just need to pull them out one at a time and process them. 1473 */ 1474 if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 1475 while (line_parser(cli_context) == false); 1476 } 1477 1478 /* 1479 * The initial cmd line options are parsed once before this function is 1480 * called so if there is no action, we're in shell mode and will loop 1481 * here until a a valid option is parsed and returned. 1482 */ 1483 if (cli_context->action == CLI_NONE) { 1484 while (cli_shell(cli_context, NULL) == false); 1485 } 1486 1487 /* Decide what to do next based on cmd line parsing. */ 1488 switch (cli_context->action) { 1489 case CLI_SET_SUPER: 1490 case CLI_SHOW_BS: 1491 case CLI_CREATE_BLOB: 1492 case CLI_SET_XATTR: 1493 case CLI_REM_XATTR: 1494 case CLI_SHOW_BLOB: 1495 case CLI_LIST_BLOBS: 1496 case CLI_DUMP_BLOB: 1497 case CLI_IMPORT_BLOB: 1498 case CLI_FILL: 1499 case CLI_RECOVER: 1500 case CLI_DELETE_BLOB: 1501 load_bs(cli_context); 1502 break; 1503 case CLI_INIT_BS: 1504 init_bs(cli_context); 1505 break; 1506 case CLI_DUMP_BS: 1507 dump_bs(cli_context); 1508 break; 1509 case CLI_LIST_BDEVS: 1510 list_bdevs(cli_context); 1511 break; 1512 case CLI_SHELL_EXIT: 1513 /* 1514 * Because shell mode reuses cmd mode functions, the blobstore 1515 * is loaded/unloaded with every action so we just need to 1516 * stop the framework. For this app there's no need to optimize 1517 * and keep the blobstore open while the app is in shell mode. 1518 */ 1519 spdk_app_stop(0); 1520 break; 1521 case CLI_HELP: 1522 usage(cli_context, ""); 1523 unload_complete(cli_context, 0); 1524 break; 1525 default: 1526 /* should never get here */ 1527 exit(-1); 1528 break; 1529 } 1530 } 1531 1532 int 1533 main(int argc, char **argv) 1534 { 1535 struct spdk_app_opts opts = {}; 1536 struct cli_context_t *cli_context = NULL; 1537 bool cmd_chosen; 1538 int rc = 0; 1539 1540 if (argc < 2) { 1541 usage(cli_context, "ERROR: Invalid option\n"); 1542 exit(-1); 1543 } 1544 1545 cli_context = calloc(1, sizeof(struct cli_context_t)); 1546 if (cli_context == NULL) { 1547 printf("ERROR: could not allocate context structure\n"); 1548 exit(-1); 1549 } 1550 1551 /* default to CMD mode until we've parsed the first parms */ 1552 cli_context->cli_mode = CLI_MODE_CMD; 1553 cli_context->argv[0] = strdup(argv[0]); 1554 cli_context->argc = 1; 1555 1556 /* parse command line */ 1557 cmd_chosen = cmd_parser(argc, argv, cli_context); 1558 free(cli_context->argv[0]); 1559 cli_context->argv[0] = NULL; 1560 if (cmd_chosen == false) { 1561 cli_cleanup(cli_context); 1562 exit(-1); 1563 } 1564 1565 /* after displaying help, just exit */ 1566 if (cli_context->action == CLI_HELP) { 1567 usage(cli_context, ""); 1568 cli_cleanup(cli_context); 1569 exit(-1); 1570 } 1571 1572 /* if they don't supply a conf name, use the default */ 1573 if (!cli_context->config_file) { 1574 cli_context->config_file = program_conf; 1575 } 1576 1577 /* if the config file doesn't exist, tell them how to make one */ 1578 if (access(cli_context->config_file, F_OK) == -1) { 1579 printf("Error: No config file found.\n"); 1580 printf("To create a config file named 'blobcli.json' for your NVMe device:\n"); 1581 printf(" <path to spdk>/scripts/gen_nvme.sh --json-with-subsystems > blobcli.json\n"); 1582 printf("and then re-run the cli tool.\n"); 1583 exit(-1); 1584 } 1585 1586 /* 1587 * For script mode we keep a bunch of stuff in a global since 1588 * none if it is passed back and forth to SPDK. 1589 */ 1590 if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 1591 /* 1592 * Now we'll build up the global which will direct this run of the app 1593 * as it will have a list (g_script) of all of the commands line by 1594 * line as if they were typed in on the shell at cmd line. 1595 */ 1596 parse_script(cli_context); 1597 } 1598 1599 /* Set default values in opts struct along with name and conf file. */ 1600 spdk_app_opts_init(&opts, sizeof(opts)); 1601 opts.name = "blobcli"; 1602 opts.json_config_file = cli_context->config_file; 1603 opts.rpc_addr = NULL; 1604 1605 cli_context->app_started = true; 1606 rc = spdk_app_start(&opts, cli_start, cli_context); 1607 if (rc) { 1608 printf("ERROR!\n"); 1609 } 1610 1611 /* Free up memory that we allocated */ 1612 cli_cleanup(cli_context); 1613 1614 /* Gracefully close out all of the SPDK subsystems. */ 1615 spdk_app_fini(); 1616 return rc; 1617 } 1618