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