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 /* 947 * Lists all the blobs on this blobstore. 948 */ 949 static void 950 list_bdevs(struct cli_context_t *cli_context) 951 { 952 struct spdk_bdev *bdev = NULL; 953 954 printf("\nList bdevs:\n"); 955 956 bdev = spdk_bdev_first(); 957 if (bdev == NULL) { 958 printf("Could not find a bdev\n"); 959 } 960 while (bdev) { 961 printf("\tbdev Name: %s\n", spdk_bdev_get_name(bdev)); 962 printf("\tbdev Product Name: %s\n", 963 spdk_bdev_get_product_name(bdev)); 964 bdev = spdk_bdev_next(bdev); 965 } 966 967 printf("\n"); 968 if (cli_context->cli_mode == CLI_MODE_CMD) { 969 spdk_app_stop(0); 970 } else { 971 cli_context->action = CLI_NONE; 972 cli_start(cli_context); 973 } 974 } 975 976 /* 977 * Callback function for initializing a blob. 978 */ 979 static void 980 bs_init_cb(void *cb_arg, struct spdk_blob_store *bs, 981 int bserrno) 982 { 983 struct cli_context_t *cli_context = cb_arg; 984 985 if (bserrno) { 986 unload_bs(cli_context, "Error in bs init callback", 987 bserrno); 988 return; 989 } 990 cli_context->bs = bs; 991 printf("blobstore init'd: (%p)\n", cli_context->bs); 992 993 unload_bs(cli_context, "", 0); 994 } 995 996 /* 997 * Initialize a new blobstore. 998 */ 999 static void 1000 init_bs(struct cli_context_t *cli_context) 1001 { 1002 int rc; 1003 1004 printf("Init blobstore using bdev Name: %s\n", cli_context->bdev_name); 1005 1006 rc = spdk_bdev_create_bs_dev_ext(cli_context->bdev_name, base_bdev_event_cb, NULL, 1007 &cli_context->bs_dev); 1008 if (rc != 0) { 1009 printf("Could not create blob bdev, %s!!\n", spdk_strerror(-rc)); 1010 spdk_app_stop(-1); 1011 return; 1012 } 1013 1014 spdk_bs_init(cli_context->bs_dev, NULL, bs_init_cb, 1015 cli_context); 1016 } 1017 1018 static void 1019 spdk_bsdump_done(void *arg, int bserrno) 1020 { 1021 struct cli_context_t *cli_context = arg; 1022 1023 if (cli_context->cli_mode == CLI_MODE_CMD) { 1024 spdk_app_stop(0); 1025 } else { 1026 cli_context->action = CLI_NONE; 1027 cli_start(cli_context); 1028 } 1029 } 1030 1031 static void 1032 bsdump_print_xattr(FILE *fp, const char *bstype, const char *name, const void *value, 1033 size_t value_len) 1034 { 1035 if (strncmp(bstype, "BLOBFS", SPDK_BLOBSTORE_TYPE_LENGTH) == 0) { 1036 if (strcmp(name, "name") == 0) { 1037 fprintf(fp, "%.*s", (int)value_len, (char *)value); 1038 } else if (strcmp(name, "length") == 0 && value_len == sizeof(uint64_t)) { 1039 uint64_t length; 1040 1041 memcpy(&length, value, sizeof(length)); 1042 fprintf(fp, "%" PRIu64, length); 1043 } else { 1044 fprintf(fp, "?"); 1045 } 1046 } else if (strncmp(bstype, "LVOLSTORE", SPDK_BLOBSTORE_TYPE_LENGTH) == 0) { 1047 if (strcmp(name, "name") == 0) { 1048 fprintf(fp, "%s", (char *)value); 1049 } else if (strcmp(name, "uuid") == 0) { 1050 struct spdk_uuid uuid; 1051 1052 if (spdk_uuid_parse(&uuid, (const char *)value) == 0) { 1053 fprintf(fp, "%s", (const char *)value); 1054 } else { 1055 fprintf(fp, "? Invalid UUID"); 1056 } 1057 } else { 1058 fprintf(fp, "?"); 1059 } 1060 } else { 1061 fprintf(fp, "?"); 1062 } 1063 } 1064 1065 /* 1066 * Dump metadata of an existing blobstore in a human-readable format. 1067 */ 1068 static void 1069 dump_bs(struct cli_context_t *cli_context) 1070 { 1071 int rc; 1072 1073 printf("Init blobstore using bdev Name: %s\n", cli_context->bdev_name); 1074 1075 rc = spdk_bdev_create_bs_dev_ext(cli_context->bdev_name, base_bdev_event_cb, NULL, 1076 &cli_context->bs_dev); 1077 if (rc != 0) { 1078 printf("Could not create blob bdev, %s!!\n", spdk_strerror(-rc)); 1079 spdk_app_stop(-1); 1080 return; 1081 } 1082 1083 spdk_bs_dump(cli_context->bs_dev, stdout, bsdump_print_xattr, spdk_bsdump_done, cli_context); 1084 } 1085 1086 /* 1087 * Common cmd/option parser for command and shell modes. 1088 */ 1089 static bool 1090 cmd_parser(int argc, char **argv, struct cli_context_t *cli_context) 1091 { 1092 int op; 1093 int cmd_chosen = 0; 1094 char resp; 1095 1096 while ((op = getopt(argc, argv, "b:d:f:hij:l:m:n:p:r:s:w:DRST:Xx:")) != -1) { 1097 switch (op) { 1098 case 'b': 1099 if (strcmp(cli_context->bdev_name, "") == 0) { 1100 snprintf(cli_context->bdev_name, BUFSIZE, "%s", optarg); 1101 } else { 1102 printf("Current setting for -b is: %s\n", cli_context->bdev_name); 1103 usage(cli_context, "ERROR: -b option can only be set once.\n"); 1104 } 1105 break; 1106 case 'D': 1107 cmd_chosen++; 1108 cli_context->action = CLI_DUMP_BS; 1109 break; 1110 case 'd': 1111 if (argv[optind] != NULL) { 1112 cmd_chosen++; 1113 cli_context->action = CLI_DUMP_BLOB; 1114 cli_context->blobid = spdk_strtoll(optarg, 0); 1115 snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]); 1116 } else { 1117 usage(cli_context, "ERROR: missing parameter.\n"); 1118 } 1119 break; 1120 case 'f': 1121 if (argv[optind] != NULL) { 1122 cmd_chosen++; 1123 cli_context->action = CLI_FILL; 1124 cli_context->blobid = spdk_strtoll(optarg, 0); 1125 cli_context->fill_value = spdk_strtol(argv[optind], 0); 1126 } else { 1127 usage(cli_context, "ERROR: missing parameter.\n"); 1128 } 1129 break; 1130 case 'h': 1131 cmd_chosen++; 1132 cli_context->action = CLI_HELP; 1133 break; 1134 case 'i': 1135 if (cli_context->cli_mode != CLI_MODE_SCRIPT) { 1136 printf("Your entire blobstore will be destroyed. Are you sure? (y/n) "); 1137 if (scanf("%c%*c", &resp)) { 1138 if (resp == 'y' || resp == 'Y') { 1139 cmd_chosen++; 1140 cli_context->action = CLI_INIT_BS; 1141 } else { 1142 if (cli_context->cli_mode == CLI_MODE_CMD) { 1143 spdk_app_stop(0); 1144 return false; 1145 } 1146 } 1147 } 1148 } else { 1149 cmd_chosen++; 1150 cli_context->action = CLI_INIT_BS; 1151 } 1152 break; 1153 case 'j': 1154 if (cli_context->app_started == false) { 1155 cli_context->config_file = optarg; 1156 } else { 1157 usage(cli_context, "ERROR: -j option not valid during shell mode.\n"); 1158 } 1159 break; 1160 case 'r': 1161 if (argv[optind] != NULL) { 1162 cmd_chosen++; 1163 cli_context->action = CLI_REM_XATTR; 1164 cli_context->blobid = spdk_strtoll(optarg, 0); 1165 snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]); 1166 } else { 1167 usage(cli_context, "ERROR: missing parameter.\n"); 1168 } 1169 break; 1170 case 'l': 1171 if (strcmp("bdevs", optarg) == 0) { 1172 cmd_chosen++; 1173 cli_context->action = CLI_LIST_BDEVS; 1174 } else if (strcmp("blobs", optarg) == 0) { 1175 cmd_chosen++; 1176 cli_context->action = CLI_LIST_BLOBS; 1177 } else { 1178 usage(cli_context, "ERROR: invalid option for list\n"); 1179 } 1180 break; 1181 case 'm': 1182 if (argv[optind] != NULL) { 1183 cmd_chosen++; 1184 cli_context->action = CLI_IMPORT_BLOB; 1185 cli_context->blobid = spdk_strtoll(optarg, 0); 1186 snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]); 1187 } else { 1188 usage(cli_context, "ERROR: missing parameter.\n"); 1189 } 1190 break; 1191 case 'n': 1192 cli_context->num_clusters = spdk_strtol(optarg, 10); 1193 if (cli_context->num_clusters > 0) { 1194 cmd_chosen++; 1195 cli_context->action = CLI_CREATE_BLOB; 1196 } else { 1197 usage(cli_context, "ERROR: invalid option for new\n"); 1198 } 1199 break; 1200 case 'p': 1201 cmd_chosen++; 1202 cli_context->action = CLI_SET_SUPER; 1203 cli_context->superid = spdk_strtoll(optarg, 0); 1204 break; 1205 case 'R': 1206 cmd_chosen++; 1207 cli_context->action = CLI_RECOVER; 1208 break; 1209 case 'S': 1210 if (cli_context->cli_mode == CLI_MODE_CMD) { 1211 cmd_chosen++; 1212 cli_context->cli_mode = CLI_MODE_SHELL; 1213 } 1214 cli_context->action = CLI_NONE; 1215 break; 1216 case 's': 1217 cmd_chosen++; 1218 if (strcmp("bs", optarg) == 0) { 1219 cli_context->action = CLI_SHOW_BS; 1220 } else { 1221 cli_context->action = CLI_SHOW_BLOB; 1222 cli_context->blobid = spdk_strtoll(optarg, 0); 1223 } 1224 break; 1225 case 'T': 1226 if (cli_context->cli_mode == CLI_MODE_CMD) { 1227 cmd_chosen++; 1228 cli_context->cli_mode = CLI_MODE_SCRIPT; 1229 if (argv[optind] && (strcmp("ignore", argv[optind]) == 0)) { 1230 g_script.ignore_errors = true; 1231 } else { 1232 g_script.ignore_errors = false; 1233 } 1234 snprintf(cli_context->script_file, BUFSIZE, "%s", optarg); 1235 } else { 1236 cli_context->action = CLI_NONE; 1237 } 1238 break; 1239 case 'w': 1240 cmd_chosen++; 1241 cli_context->action = CLI_DELETE_BLOB; 1242 cli_context->blobid = spdk_strtoll(optarg, 0); 1243 break; 1244 case 'X': 1245 cmd_chosen++; 1246 cli_context->action = CLI_SHELL_EXIT; 1247 break; 1248 case 'x': 1249 if (argv[optind] != NULL || argv[optind + 1] != NULL) { 1250 cmd_chosen++; 1251 cli_context->action = CLI_SET_XATTR; 1252 cli_context->blobid = spdk_strtoll(optarg, 0); 1253 snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]); 1254 snprintf(cli_context->value, BUFSIZE, "%s", argv[optind + 1]); 1255 } else { 1256 usage(cli_context, "ERROR: missing parameter.\n"); 1257 } 1258 break; 1259 default: 1260 usage(cli_context, "ERROR: invalid option\n"); 1261 } 1262 /* only one actual command can be done at a time */ 1263 if (cmd_chosen > 1) { 1264 usage(cli_context, "Error: Please choose only one command\n"); 1265 } 1266 } 1267 1268 if (cli_context->cli_mode == CLI_MODE_CMD && cmd_chosen == 0) { 1269 usage(cli_context, "Error: Please choose a command.\n"); 1270 } 1271 1272 /* 1273 * We don't check the local boolean because in some modes it will have been set 1274 * on and earlier command. 1275 */ 1276 if ((strcmp(cli_context->bdev_name, "") == 0) && (cli_context->action != CLI_HELP)) { 1277 usage(cli_context, "Error: -b option is required.\n"); 1278 cmd_chosen = 0; 1279 } 1280 1281 /* in shell mode we'll call getopt multiple times so need to reset its index */ 1282 optind = 0; 1283 return (cmd_chosen == 1); 1284 } 1285 1286 /* 1287 * In script mode, we parsed a script file at startup and saved off a bunch of cmd 1288 * lines that we now parse with each run of cli_start so we us the same cmd parser 1289 * as cmd and shell modes. 1290 */ 1291 static bool 1292 line_parser(struct cli_context_t *cli_context) 1293 { 1294 bool cmd_chosen; 1295 char *tok = NULL; 1296 int blob_num = 0; 1297 int start_idx = cli_context->argc; 1298 int i; 1299 1300 printf("\nSCRIPT NOW PROCESSING: %s\n", g_script.cmdline[g_script.cmdline_idx]); 1301 tok = strtok(g_script.cmdline[g_script.cmdline_idx], " "); 1302 while (tok != NULL) { 1303 /* 1304 * We support one replaceable token right now, a $Bn 1305 * represents the blobid that was created in position n 1306 * so fish this out now and use it here. 1307 */ 1308 cli_context->argv[cli_context->argc] = strdup(tok); 1309 if (tok[0] == '$' && tok[1] == 'B') { 1310 tok += 2; 1311 blob_num = spdk_strtol(tok, 10); 1312 if (blob_num >= 0 && blob_num < MAX_SCRIPT_BLOBS) { 1313 cli_context->argv[cli_context->argc] = 1314 realloc(cli_context->argv[cli_context->argc], BUFSIZE); 1315 if (cli_context->argv[cli_context->argc] == NULL) { 1316 printf("ERROR: unable to realloc memory\n"); 1317 spdk_app_stop(-1); 1318 } 1319 if (g_script.blobid[blob_num] == 0) { 1320 printf("ERROR: There is no blob for $B%d\n", 1321 blob_num); 1322 } 1323 snprintf(cli_context->argv[cli_context->argc], BUFSIZE, 1324 "%" PRIu64, g_script.blobid[blob_num]); 1325 } else { 1326 printf("ERROR: Invalid token or exceeded max blobs of %d\n", 1327 MAX_SCRIPT_BLOBS); 1328 } 1329 } 1330 cli_context->argc++; 1331 tok = strtok(NULL, " "); 1332 } 1333 1334 /* call parse cmd line with user input as args */ 1335 cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context); 1336 1337 /* free strdup memory and reset arg count for next shell interaction */ 1338 for (i = start_idx; i < cli_context->argc; i++) { 1339 free(cli_context->argv[i]); 1340 cli_context->argv[i] = NULL; 1341 } 1342 cli_context->argc = 1; 1343 1344 g_script.cmdline_idx++; 1345 assert(g_script.cmdline_idx < MAX_SCRIPT_LINES); 1346 1347 if (cmd_chosen == false) { 1348 printf("ERROR: Invalid script line starting with: %s\n\n", 1349 g_script.cmdline[g_script.cmdline_idx - 1]); 1350 if (g_script.ignore_errors == false) { 1351 printf("** Aborting **\n"); 1352 cli_context->action = CLI_SHELL_EXIT; 1353 cmd_chosen = true; 1354 unload_bs(cli_context, "", 0); 1355 } else { 1356 printf("** Skipping **\n"); 1357 } 1358 } 1359 1360 return cmd_chosen; 1361 } 1362 1363 /* 1364 * For script mode, we read a series of commands from a text file and store them 1365 * in a global struct. That, along with the cli_mode that tells us we're in 1366 * script mode is what feeds the rest of the app in the same way as is it were 1367 * getting commands from shell mode. 1368 */ 1369 static void 1370 parse_script(struct cli_context_t *cli_context) 1371 { 1372 FILE *fp = NULL; 1373 size_t bufsize = BUFSIZE; 1374 int64_t bytes_in = 0; 1375 int i = 0; 1376 1377 /* initialize global script values */ 1378 for (i = 0; i < MAX_SCRIPT_BLOBS; i++) { 1379 g_script.blobid[i] = 0; 1380 } 1381 g_script.blobid_idx = 0; 1382 g_script.cmdline_idx = 0; 1383 i = 0; 1384 1385 fp = fopen(cli_context->script_file, "r"); 1386 if (fp == NULL) { 1387 printf("ERROR: unable to open script: %s\n", 1388 cli_context->script_file); 1389 cli_cleanup(cli_context); 1390 exit(-1); 1391 } 1392 1393 do { 1394 bytes_in = getline(&g_script.cmdline[i], &bufsize, fp); 1395 if (bytes_in > 0) { 1396 /* replace newline with null */ 1397 spdk_str_chomp(g_script.cmdline[i]); 1398 1399 /* ignore comments */ 1400 if (g_script.cmdline[i][0] != '#') { 1401 i++; 1402 } 1403 } 1404 } while (bytes_in != -1 && i < MAX_SCRIPT_LINES - 1); 1405 fclose(fp); 1406 1407 /* add an exit cmd in case they didn't */ 1408 g_script.cmdline[i] = realloc(g_script.cmdline[i], BUFSIZE); 1409 if (g_script.cmdline[i] == NULL) { 1410 int j; 1411 1412 for (j = 0; j < i; j++) { 1413 free(g_script.cmdline[j]); 1414 g_script.cmdline[j] = NULL; 1415 } 1416 unload_bs(cli_context, "ERROR: unable to alloc memory.\n", 0); 1417 } 1418 snprintf(g_script.cmdline[i], BUFSIZE, "%s", "-X"); 1419 g_script.max_index = i; 1420 } 1421 1422 /* 1423 * Provides for a shell interface as opposed to one shot command line. 1424 */ 1425 static bool 1426 cli_shell(void *arg1, void *arg2) 1427 { 1428 struct cli_context_t *cli_context = arg1; 1429 char *line = NULL; 1430 ssize_t buf_size = 0; 1431 ssize_t bytes_in = 0; 1432 ssize_t tok_len = 0; 1433 char *tok = NULL; 1434 bool cmd_chosen = false; 1435 int start_idx = cli_context->argc; 1436 int i; 1437 1438 printf("blob> "); 1439 bytes_in = getline(&line, &buf_size, stdin); 1440 1441 /* If getline() failed (EOF), exit the shell. */ 1442 if (bytes_in < 0) { 1443 free(line); 1444 cli_context->action = CLI_SHELL_EXIT; 1445 return true; 1446 } 1447 1448 /* parse input and update cli_context so we can use common option parser */ 1449 if (bytes_in > 0) { 1450 tok = strtok(line, " "); 1451 } 1452 while ((tok != NULL) && (cli_context->argc < MAX_ARGS)) { 1453 cli_context->argv[cli_context->argc] = strdup(tok); 1454 tok_len = strlen(tok); 1455 cli_context->argc++; 1456 tok = strtok(NULL, " "); 1457 } 1458 1459 /* replace newline on last arg with null */ 1460 if (tok_len) { 1461 spdk_str_chomp(cli_context->argv[cli_context->argc - 1]); 1462 } 1463 1464 /* call parse cmd line with user input as args */ 1465 cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context); 1466 1467 /* free strdup mem & reset arg count for next shell interaction */ 1468 for (i = start_idx; i < cli_context->argc; i++) { 1469 free(cli_context->argv[i]); 1470 cli_context->argv[i] = NULL; 1471 } 1472 cli_context->argc = 1; 1473 1474 free(line); 1475 1476 return cmd_chosen; 1477 } 1478 1479 /* 1480 * This is the function we pass into the SPDK framework that gets 1481 * called first. 1482 */ 1483 static void 1484 cli_start(void *arg1) 1485 { 1486 struct cli_context_t *cli_context = arg1; 1487 1488 /* 1489 * If we're in script mode, we already have a list of commands so 1490 * just need to pull them out one at a time and process them. 1491 */ 1492 if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 1493 while (line_parser(cli_context) == false); 1494 } 1495 1496 /* 1497 * The initial cmd line options are parsed once before this function is 1498 * called so if there is no action, we're in shell mode and will loop 1499 * here until a a valid option is parsed and returned. 1500 */ 1501 if (cli_context->action == CLI_NONE) { 1502 while (cli_shell(cli_context, NULL) == false); 1503 } 1504 1505 /* Decide what to do next based on cmd line parsing. */ 1506 switch (cli_context->action) { 1507 case CLI_SET_SUPER: 1508 case CLI_SHOW_BS: 1509 case CLI_CREATE_BLOB: 1510 case CLI_SET_XATTR: 1511 case CLI_REM_XATTR: 1512 case CLI_SHOW_BLOB: 1513 case CLI_LIST_BLOBS: 1514 case CLI_DUMP_BLOB: 1515 case CLI_IMPORT_BLOB: 1516 case CLI_FILL: 1517 case CLI_RECOVER: 1518 case CLI_DELETE_BLOB: 1519 load_bs(cli_context); 1520 break; 1521 case CLI_INIT_BS: 1522 init_bs(cli_context); 1523 break; 1524 case CLI_DUMP_BS: 1525 dump_bs(cli_context); 1526 break; 1527 case CLI_LIST_BDEVS: 1528 list_bdevs(cli_context); 1529 break; 1530 case CLI_SHELL_EXIT: 1531 /* 1532 * Because shell mode reuses cmd mode functions, the blobstore 1533 * is loaded/unloaded with every action so we just need to 1534 * stop the framework. For this app there's no need to optimize 1535 * and keep the blobstore open while the app is in shell mode. 1536 */ 1537 spdk_app_stop(0); 1538 break; 1539 case CLI_HELP: 1540 usage(cli_context, ""); 1541 unload_complete(cli_context, 0); 1542 break; 1543 default: 1544 /* should never get here */ 1545 exit(-1); 1546 break; 1547 } 1548 } 1549 1550 int 1551 main(int argc, char **argv) 1552 { 1553 struct spdk_app_opts opts = {}; 1554 struct cli_context_t *cli_context = NULL; 1555 bool cmd_chosen; 1556 int rc = 0; 1557 1558 if (argc < 2) { 1559 usage(cli_context, "ERROR: Invalid option\n"); 1560 exit(-1); 1561 } 1562 1563 cli_context = calloc(1, sizeof(struct cli_context_t)); 1564 if (cli_context == NULL) { 1565 printf("ERROR: could not allocate context structure\n"); 1566 exit(-1); 1567 } 1568 1569 /* default to CMD mode until we've parsed the first parms */ 1570 cli_context->cli_mode = CLI_MODE_CMD; 1571 cli_context->argv[0] = strdup(argv[0]); 1572 cli_context->argc = 1; 1573 1574 /* parse command line */ 1575 cmd_chosen = cmd_parser(argc, argv, cli_context); 1576 free(cli_context->argv[0]); 1577 cli_context->argv[0] = NULL; 1578 if (cmd_chosen == false) { 1579 cli_cleanup(cli_context); 1580 exit(-1); 1581 } 1582 1583 /* after displaying help, just exit */ 1584 if (cli_context->action == CLI_HELP) { 1585 usage(cli_context, ""); 1586 cli_cleanup(cli_context); 1587 exit(-1); 1588 } 1589 1590 /* if they don't supply a conf name, use the default */ 1591 if (!cli_context->config_file) { 1592 cli_context->config_file = program_conf; 1593 } 1594 1595 /* if the config file doesn't exist, tell them how to make one */ 1596 if (access(cli_context->config_file, F_OK) == -1) { 1597 printf("Error: No config file found.\n"); 1598 printf("To create a config file named 'blobcli.json' for your NVMe device:\n"); 1599 printf(" <path to spdk>/scripts/gen_nvme.sh --json-with-subsystems > blobcli.json\n"); 1600 printf("and then re-run the cli tool.\n"); 1601 exit(-1); 1602 } 1603 1604 /* 1605 * For script mode we keep a bunch of stuff in a global since 1606 * none if it is passed back and forth to SPDK. 1607 */ 1608 if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 1609 /* 1610 * Now we'll build up the global which will direct this run of the app 1611 * as it will have a list (g_script) of all of the commands line by 1612 * line as if they were typed in on the shell at cmd line. 1613 */ 1614 parse_script(cli_context); 1615 } 1616 1617 /* Set default values in opts struct along with name and conf file. */ 1618 spdk_app_opts_init(&opts, sizeof(opts)); 1619 opts.name = "blobcli"; 1620 opts.json_config_file = cli_context->config_file; 1621 1622 cli_context->app_started = true; 1623 rc = spdk_app_start(&opts, cli_start, cli_context); 1624 if (rc) { 1625 printf("ERROR!\n"); 1626 } 1627 1628 /* Free up memory that we allocated */ 1629 cli_cleanup(cli_context); 1630 1631 /* Gracefully close out all of the SPDK subsystems. */ 1632 spdk_app_fini(); 1633 return rc; 1634 } 1635