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 /* 582 * Callback function for reading a blob for dumping to a file. 583 */ 584 static void 585 read_dump_cb(void *arg1, int bserrno) 586 { 587 struct cli_context_t *cli_context = arg1; 588 uint64_t bytes_written; 589 590 if (bserrno) { 591 fclose(cli_context->fp); 592 unload_bs(cli_context, "Error in read completion", 593 bserrno); 594 return; 595 } 596 597 bytes_written = fwrite(cli_context->buff, NUM_IO_UNITS, cli_context->io_unit_size, 598 cli_context->fp); 599 if (bytes_written != cli_context->io_unit_size) { 600 fclose(cli_context->fp); 601 unload_bs(cli_context, "Error with fwrite", 602 bserrno); 603 return; 604 } 605 606 printf("."); 607 if (++cli_context->io_unit_count < cli_context->blob_io_units) { 608 /* perform another read */ 609 spdk_blob_io_read(cli_context->blob, cli_context->channel, 610 cli_context->buff, cli_context->io_unit_count, 611 NUM_IO_UNITS, read_dump_cb, cli_context); 612 } else { 613 /* done reading */ 614 printf("\nFile write complete (to %s).\n", cli_context->file); 615 fclose(cli_context->fp); 616 spdk_blob_close(cli_context->blob, close_cb, cli_context); 617 } 618 } 619 620 /* 621 * Callback for write completion on the import of a file to a blob. 622 */ 623 static void 624 write_imp_cb(void *arg1, int bserrno) 625 { 626 struct cli_context_t *cli_context = arg1; 627 uint64_t bytes_read; 628 629 if (bserrno) { 630 fclose(cli_context->fp); 631 unload_bs(cli_context, "Error in write completion", 632 bserrno); 633 return; 634 } 635 636 if (cli_context->bytes_so_far < cli_context->filesize) { 637 /* perform another file read */ 638 bytes_read = fread(cli_context->buff, 1, 639 cli_context->io_unit_size, 640 cli_context->fp); 641 cli_context->bytes_so_far += bytes_read; 642 643 /* if this read is < 1 io_unit, fill with 0s */ 644 if (bytes_read < cli_context->io_unit_size) { 645 uint8_t *offset = cli_context->buff + bytes_read; 646 memset(offset, 0, cli_context->io_unit_size - bytes_read); 647 } 648 } else { 649 /* 650 * Done reading the file, fill the rest of the blob with 0s, 651 * yeah we're memsetting the same io_unit over and over here 652 */ 653 memset(cli_context->buff, 0, cli_context->io_unit_size); 654 } 655 if (++cli_context->io_unit_count < cli_context->blob_io_units) { 656 printf("."); 657 spdk_blob_io_write(cli_context->blob, cli_context->channel, 658 cli_context->buff, cli_context->io_unit_count, 659 NUM_IO_UNITS, write_imp_cb, cli_context); 660 } else { 661 /* done writing */ 662 printf("\nBlob import complete (from %s).\n", cli_context->file); 663 fclose(cli_context->fp); 664 spdk_blob_close(cli_context->blob, close_cb, cli_context); 665 } 666 } 667 668 /* 669 * Callback for open blobs where we'll continue on dump a blob to a file or 670 * import a file to a blob. For dump, the resulting file will always be the 671 * full size of the blob. For import, the blob will fill with the file 672 * contents first and then 0 out the rest of the blob. 673 */ 674 static void 675 dump_imp_open_cb(void *cb_arg, struct spdk_blob *blob, int bserrno) 676 { 677 struct cli_context_t *cli_context = cb_arg; 678 679 if (bserrno) { 680 unload_bs(cli_context, "Error in blob open callback", 681 bserrno); 682 return; 683 } 684 cli_context->blob = blob; 685 686 /* 687 * We'll transfer just one io_unit at a time to keep the buffer 688 * small. This could be bigger of course. 689 */ 690 cli_context->buff = spdk_malloc(cli_context->io_unit_size, ALIGN_4K, NULL, 691 SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); 692 if (cli_context->buff == NULL) { 693 printf("Error in allocating memory\n"); 694 spdk_blob_close(cli_context->blob, close_cb, cli_context); 695 return; 696 } 697 printf("Working"); 698 cli_context->blob_io_units = spdk_blob_get_num_io_units(cli_context->blob); 699 cli_context->io_unit_count = 0; 700 if (cli_context->action == CLI_DUMP_BLOB) { 701 cli_context->fp = fopen(cli_context->file, "w"); 702 if (cli_context->fp == NULL) { 703 printf("Error in opening file\n"); 704 spdk_blob_close(cli_context->blob, close_cb, cli_context); 705 return; 706 } 707 708 if (cli_context->blob->active.num_clusters == 0) { 709 fclose(cli_context->fp); 710 spdk_blob_close(cli_context->blob, close_cb, cli_context); 711 return; 712 } 713 714 /* read a io_unit of data from the blob */ 715 spdk_blob_io_read(cli_context->blob, cli_context->channel, 716 cli_context->buff, cli_context->io_unit_count, 717 NUM_IO_UNITS, read_dump_cb, cli_context); 718 } else { 719 cli_context->fp = fopen(cli_context->file, "r"); 720 if (cli_context->fp == NULL) { 721 printf("Error in opening file: errno %d\n", errno); 722 spdk_blob_close(cli_context->blob, close_cb, cli_context); 723 return; 724 } 725 726 /* get the filesize then rewind read a io_unit of data from file */ 727 fseek(cli_context->fp, 0L, SEEK_END); 728 cli_context->filesize = ftell(cli_context->fp); 729 rewind(cli_context->fp); 730 cli_context->bytes_so_far = fread(cli_context->buff, NUM_IO_UNITS, 731 cli_context->io_unit_size, 732 cli_context->fp); 733 734 /* if the file is < a io_unit, fill the rest with 0s */ 735 if (cli_context->filesize < cli_context->io_unit_size) { 736 uint8_t *offset = 737 cli_context->buff + cli_context->filesize; 738 739 memset(offset, 0, 740 cli_context->io_unit_size - cli_context->filesize); 741 } 742 743 spdk_blob_io_write(cli_context->blob, cli_context->channel, 744 cli_context->buff, cli_context->io_unit_count, 745 NUM_IO_UNITS, write_imp_cb, cli_context); 746 } 747 } 748 749 /* 750 * Callback function for writing a specific pattern to io_unit 0. 751 */ 752 static void 753 write_cb(void *arg1, int bserrno) 754 { 755 struct cli_context_t *cli_context = arg1; 756 757 if (bserrno) { 758 unload_bs(cli_context, "Error in write completion", 759 bserrno); 760 return; 761 } 762 printf("."); 763 if (++cli_context->io_unit_count < cli_context->blob_io_units) { 764 spdk_blob_io_write(cli_context->blob, cli_context->channel, 765 cli_context->buff, cli_context->io_unit_count, 766 NUM_IO_UNITS, write_cb, cli_context); 767 } else { 768 /* done writing */ 769 printf("\nBlob fill complete (with 0x%x).\n", cli_context->fill_value); 770 spdk_blob_close(cli_context->blob, close_cb, cli_context); 771 } 772 } 773 774 /* 775 * Callback function to fill a blob with a value, callback from open. 776 */ 777 static void 778 fill_blob_cb(void *arg1, struct spdk_blob *blob, int bserrno) 779 { 780 struct cli_context_t *cli_context = arg1; 781 782 if (bserrno) { 783 unload_bs(cli_context, "Error in open callback", 784 bserrno); 785 return; 786 } 787 788 cli_context->blob = blob; 789 cli_context->io_unit_count = 0; 790 cli_context->blob_io_units = spdk_blob_get_num_io_units(cli_context->blob); 791 cli_context->buff = spdk_malloc(cli_context->io_unit_size, ALIGN_4K, NULL, 792 SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); 793 if (cli_context->buff == NULL) { 794 unload_bs(cli_context, "Error in allocating memory", 795 -ENOMEM); 796 return; 797 } 798 799 memset(cli_context->buff, cli_context->fill_value, 800 cli_context->io_unit_size); 801 printf("Working"); 802 spdk_blob_io_write(cli_context->blob, cli_context->channel, 803 cli_context->buff, 804 STARTING_IO_UNIT, NUM_IO_UNITS, write_cb, cli_context); 805 } 806 807 /* 808 * Callback for deleting a blob 809 */ 810 static void 811 delete_blob_cb(void *arg1, int bserrno) 812 { 813 struct cli_context_t *cli_context = arg1; 814 815 if (bserrno) { 816 unload_bs(cli_context, "Error in delete_blob callback", 817 bserrno); 818 return; 819 } 820 821 printf("Blob 0x%lx has been deleted.\n", cli_context->blobid); 822 unload_bs(cli_context, "", 0); 823 } 824 825 /* 826 * Multiple actions require us to open the bs first so here we use 827 * a common callback to set a bunch of values and then move on to 828 * the next step saved off via function pointer. 829 */ 830 static void 831 load_bs_cb(void *arg1, struct spdk_blob_store *bs, int bserrno) 832 { 833 struct cli_context_t *cli_context = arg1; 834 835 if (bserrno) { 836 unload_bs(cli_context, "Error in load callback", 837 bserrno); 838 return; 839 } 840 841 cli_context->bs = bs; 842 cli_context->page_size = spdk_bs_get_page_size(cli_context->bs); 843 cli_context->io_unit_size = spdk_bs_get_io_unit_size(cli_context->bs); 844 cli_context->channel = spdk_bs_alloc_io_channel(cli_context->bs); 845 if (cli_context->channel == NULL) { 846 unload_bs(cli_context, "Error in allocating channel", 847 -ENOMEM); 848 return; 849 } 850 851 switch (cli_context->action) { 852 case CLI_SET_SUPER: 853 spdk_bs_set_super(cli_context->bs, cli_context->superid, 854 set_super_cb, cli_context); 855 break; 856 case CLI_SHOW_BS: 857 spdk_bs_get_super(cli_context->bs, show_bs_cb, cli_context); 858 break; 859 case CLI_CREATE_BLOB: 860 spdk_bs_create_blob(cli_context->bs, blob_create_cb, cli_context); 861 break; 862 case CLI_SET_XATTR: 863 case CLI_REM_XATTR: 864 spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 865 set_xattr_cb, cli_context); 866 break; 867 case CLI_SHOW_BLOB: 868 case CLI_LIST_BLOBS: 869 spdk_bs_iter_first(cli_context->bs, blob_iter_cb, cli_context); 870 871 break; 872 case CLI_DUMP_BLOB: 873 case CLI_IMPORT_BLOB: 874 spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 875 dump_imp_open_cb, cli_context); 876 break; 877 case CLI_FILL: 878 spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 879 fill_blob_cb, cli_context); 880 break; 881 case CLI_RECOVER: 882 unload_bs(cli_context, "", 0); 883 break; 884 case CLI_DELETE_BLOB: 885 spdk_bs_delete_blob(cli_context->bs, cli_context->blobid, 886 delete_blob_cb, cli_context); 887 break; 888 889 default: 890 /* should never get here */ 891 exit(-1); 892 break; 893 } 894 } 895 896 static void 897 base_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, 898 void *event_ctx) 899 { 900 printf("Unsupported bdev event: type %d on bdev %s\n", type, spdk_bdev_get_name(bdev)); 901 } 902 903 /* 904 * Load the blobstore. 905 */ 906 static void 907 load_bs(struct cli_context_t *cli_context) 908 { 909 struct spdk_bs_dev *bs_dev = NULL; 910 int rc; 911 struct spdk_bs_opts opts = {}; 912 struct spdk_bs_opts *optsp = NULL; 913 914 rc = spdk_bdev_create_bs_dev_ext(cli_context->bdev_name, base_bdev_event_cb, 915 NULL, &bs_dev); 916 if (rc != 0) { 917 printf("Could not create blob bdev, %s!!\n", spdk_strerror(-rc)); 918 spdk_app_stop(-1); 919 return; 920 } 921 922 if (cli_context->action == CLI_RECOVER) { 923 spdk_bs_opts_init(&opts, sizeof(opts)); 924 opts.force_recover = true; 925 optsp = &opts; 926 } 927 928 spdk_bs_load(bs_dev, optsp, load_bs_cb, cli_context); 929 } 930 931 /* 932 * Lists all the blobs on this blobstore. 933 */ 934 static void 935 list_bdevs(struct cli_context_t *cli_context) 936 { 937 struct spdk_bdev *bdev = NULL; 938 939 printf("\nList bdevs:\n"); 940 941 bdev = spdk_bdev_first(); 942 if (bdev == NULL) { 943 printf("Could not find a bdev\n"); 944 } 945 while (bdev) { 946 printf("\tbdev Name: %s\n", spdk_bdev_get_name(bdev)); 947 printf("\tbdev Product Name: %s\n", 948 spdk_bdev_get_product_name(bdev)); 949 bdev = spdk_bdev_next(bdev); 950 } 951 952 printf("\n"); 953 if (cli_context->cli_mode == CLI_MODE_CMD) { 954 spdk_app_stop(0); 955 } else { 956 cli_context->action = CLI_NONE; 957 cli_start(cli_context); 958 } 959 } 960 961 /* 962 * Callback function for initializing a blob. 963 */ 964 static void 965 bs_init_cb(void *cb_arg, struct spdk_blob_store *bs, 966 int bserrno) 967 { 968 struct cli_context_t *cli_context = cb_arg; 969 970 if (bserrno) { 971 unload_bs(cli_context, "Error in bs init callback", 972 bserrno); 973 return; 974 } 975 cli_context->bs = bs; 976 printf("blobstore init'd: (%p)\n", cli_context->bs); 977 978 unload_bs(cli_context, "", 0); 979 } 980 981 /* 982 * Initialize a new blobstore. 983 */ 984 static void 985 init_bs(struct cli_context_t *cli_context) 986 { 987 int rc; 988 989 printf("Init blobstore using bdev Name: %s\n", cli_context->bdev_name); 990 991 rc = spdk_bdev_create_bs_dev_ext(cli_context->bdev_name, base_bdev_event_cb, NULL, 992 &cli_context->bs_dev); 993 if (rc != 0) { 994 printf("Could not create blob bdev, %s!!\n", spdk_strerror(-rc)); 995 spdk_app_stop(-1); 996 return; 997 } 998 999 spdk_bs_init(cli_context->bs_dev, NULL, bs_init_cb, 1000 cli_context); 1001 } 1002 1003 static void 1004 spdk_bsdump_done(void *arg, int bserrno) 1005 { 1006 struct cli_context_t *cli_context = arg; 1007 1008 if (cli_context->cli_mode == CLI_MODE_CMD) { 1009 spdk_app_stop(0); 1010 } else { 1011 cli_context->action = CLI_NONE; 1012 cli_start(cli_context); 1013 } 1014 } 1015 1016 static void 1017 bsdump_print_xattr(FILE *fp, const char *bstype, const char *name, const void *value, 1018 size_t value_len) 1019 { 1020 if (strncmp(bstype, "BLOBFS", SPDK_BLOBSTORE_TYPE_LENGTH) == 0) { 1021 if (strcmp(name, "name") == 0) { 1022 fprintf(fp, "%.*s", (int)value_len, (char *)value); 1023 } else if (strcmp(name, "length") == 0 && value_len == sizeof(uint64_t)) { 1024 uint64_t length; 1025 1026 memcpy(&length, value, sizeof(length)); 1027 fprintf(fp, "%" PRIu64, length); 1028 } else { 1029 fprintf(fp, "?"); 1030 } 1031 } else if (strncmp(bstype, "LVOLSTORE", SPDK_BLOBSTORE_TYPE_LENGTH) == 0) { 1032 if (strcmp(name, "name") == 0) { 1033 fprintf(fp, "%s", (char *)value); 1034 } else if (strcmp(name, "uuid") == 0) { 1035 struct spdk_uuid uuid; 1036 1037 if (spdk_uuid_parse(&uuid, (const char *)value) == 0) { 1038 fprintf(fp, "%s", (const char *)value); 1039 } else { 1040 fprintf(fp, "? Invalid UUID"); 1041 } 1042 } else { 1043 fprintf(fp, "?"); 1044 } 1045 } else { 1046 fprintf(fp, "?"); 1047 } 1048 } 1049 1050 /* 1051 * Dump metadata of an existing blobstore in a human-readable format. 1052 */ 1053 static void 1054 dump_bs(struct cli_context_t *cli_context) 1055 { 1056 int rc; 1057 1058 printf("Init blobstore using bdev Name: %s\n", cli_context->bdev_name); 1059 1060 rc = spdk_bdev_create_bs_dev_ext(cli_context->bdev_name, base_bdev_event_cb, NULL, 1061 &cli_context->bs_dev); 1062 if (rc != 0) { 1063 printf("Could not create blob bdev, %s!!\n", spdk_strerror(-rc)); 1064 spdk_app_stop(-1); 1065 return; 1066 } 1067 1068 spdk_bs_dump(cli_context->bs_dev, stdout, bsdump_print_xattr, spdk_bsdump_done, cli_context); 1069 } 1070 1071 /* 1072 * Common cmd/option parser for command and shell modes. 1073 */ 1074 static bool 1075 cmd_parser(int argc, char **argv, struct cli_context_t *cli_context) 1076 { 1077 int op; 1078 int cmd_chosen = 0; 1079 char resp; 1080 1081 while ((op = getopt(argc, argv, "b:d:f:hij:l:m:n:p:r:s:w:DRST:Xx:")) != -1) { 1082 switch (op) { 1083 case 'b': 1084 if (strcmp(cli_context->bdev_name, "") == 0) { 1085 snprintf(cli_context->bdev_name, BUFSIZE, "%s", optarg); 1086 } else { 1087 printf("Current setting for -b is: %s\n", cli_context->bdev_name); 1088 usage(cli_context, "ERROR: -b option can only be set once.\n"); 1089 } 1090 break; 1091 case 'D': 1092 cmd_chosen++; 1093 cli_context->action = CLI_DUMP_BS; 1094 break; 1095 case 'd': 1096 if (argv[optind] != NULL) { 1097 cmd_chosen++; 1098 cli_context->action = CLI_DUMP_BLOB; 1099 cli_context->blobid = spdk_strtoll(optarg, 0); 1100 snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]); 1101 } else { 1102 usage(cli_context, "ERROR: missing parameter.\n"); 1103 } 1104 break; 1105 case 'f': 1106 if (argv[optind] != NULL) { 1107 cmd_chosen++; 1108 cli_context->action = CLI_FILL; 1109 cli_context->blobid = spdk_strtoll(optarg, 0); 1110 cli_context->fill_value = spdk_strtol(argv[optind], 0); 1111 } else { 1112 usage(cli_context, "ERROR: missing parameter.\n"); 1113 } 1114 break; 1115 case 'h': 1116 cmd_chosen++; 1117 cli_context->action = CLI_HELP; 1118 break; 1119 case 'i': 1120 if (cli_context->cli_mode != CLI_MODE_SCRIPT) { 1121 printf("Your entire blobstore will be destroyed. Are you sure? (y/n) "); 1122 if (scanf("%c%*c", &resp)) { 1123 if (resp == 'y' || resp == 'Y') { 1124 cmd_chosen++; 1125 cli_context->action = CLI_INIT_BS; 1126 } else { 1127 if (cli_context->cli_mode == CLI_MODE_CMD) { 1128 spdk_app_stop(0); 1129 return false; 1130 } 1131 } 1132 } 1133 } else { 1134 cmd_chosen++; 1135 cli_context->action = CLI_INIT_BS; 1136 } 1137 break; 1138 case 'j': 1139 if (cli_context->app_started == false) { 1140 cli_context->config_file = optarg; 1141 } else { 1142 usage(cli_context, "ERROR: -j option not valid during shell mode.\n"); 1143 } 1144 break; 1145 case 'r': 1146 if (argv[optind] != NULL) { 1147 cmd_chosen++; 1148 cli_context->action = CLI_REM_XATTR; 1149 cli_context->blobid = spdk_strtoll(optarg, 0); 1150 snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]); 1151 } else { 1152 usage(cli_context, "ERROR: missing parameter.\n"); 1153 } 1154 break; 1155 case 'l': 1156 if (strcmp("bdevs", optarg) == 0) { 1157 cmd_chosen++; 1158 cli_context->action = CLI_LIST_BDEVS; 1159 } else if (strcmp("blobs", optarg) == 0) { 1160 cmd_chosen++; 1161 cli_context->action = CLI_LIST_BLOBS; 1162 } else { 1163 usage(cli_context, "ERROR: invalid option for list\n"); 1164 } 1165 break; 1166 case 'm': 1167 if (argv[optind] != NULL) { 1168 cmd_chosen++; 1169 cli_context->action = CLI_IMPORT_BLOB; 1170 cli_context->blobid = spdk_strtoll(optarg, 0); 1171 snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]); 1172 } else { 1173 usage(cli_context, "ERROR: missing parameter.\n"); 1174 } 1175 break; 1176 case 'n': 1177 cli_context->num_clusters = spdk_strtol(optarg, 10); 1178 if (cli_context->num_clusters > 0) { 1179 cmd_chosen++; 1180 cli_context->action = CLI_CREATE_BLOB; 1181 } else { 1182 usage(cli_context, "ERROR: invalid option for new\n"); 1183 } 1184 break; 1185 case 'p': 1186 cmd_chosen++; 1187 cli_context->action = CLI_SET_SUPER; 1188 cli_context->superid = spdk_strtoll(optarg, 0); 1189 break; 1190 case 'R': 1191 cmd_chosen++; 1192 cli_context->action = CLI_RECOVER; 1193 break; 1194 case 'S': 1195 if (cli_context->cli_mode == CLI_MODE_CMD) { 1196 cmd_chosen++; 1197 cli_context->cli_mode = CLI_MODE_SHELL; 1198 } 1199 cli_context->action = CLI_NONE; 1200 break; 1201 case 's': 1202 cmd_chosen++; 1203 if (strcmp("bs", optarg) == 0) { 1204 cli_context->action = CLI_SHOW_BS; 1205 } else { 1206 cli_context->action = CLI_SHOW_BLOB; 1207 cli_context->blobid = spdk_strtoll(optarg, 0); 1208 } 1209 break; 1210 case 'T': 1211 if (cli_context->cli_mode == CLI_MODE_CMD) { 1212 cmd_chosen++; 1213 cli_context->cli_mode = CLI_MODE_SCRIPT; 1214 if (argv[optind] && (strcmp("ignore", argv[optind]) == 0)) { 1215 g_script.ignore_errors = true; 1216 } else { 1217 g_script.ignore_errors = false; 1218 } 1219 snprintf(cli_context->script_file, BUFSIZE, "%s", optarg); 1220 } else { 1221 cli_context->action = CLI_NONE; 1222 } 1223 break; 1224 case 'w': 1225 cmd_chosen++; 1226 cli_context->action = CLI_DELETE_BLOB; 1227 cli_context->blobid = spdk_strtoll(optarg, 0); 1228 break; 1229 case 'X': 1230 cmd_chosen++; 1231 cli_context->action = CLI_SHELL_EXIT; 1232 break; 1233 case 'x': 1234 if (argv[optind] != NULL || argv[optind + 1] != NULL) { 1235 cmd_chosen++; 1236 cli_context->action = CLI_SET_XATTR; 1237 cli_context->blobid = spdk_strtoll(optarg, 0); 1238 snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]); 1239 snprintf(cli_context->value, BUFSIZE, "%s", argv[optind + 1]); 1240 } else { 1241 usage(cli_context, "ERROR: missing parameter.\n"); 1242 } 1243 break; 1244 default: 1245 usage(cli_context, "ERROR: invalid option\n"); 1246 } 1247 /* only one actual command can be done at a time */ 1248 if (cmd_chosen > 1) { 1249 usage(cli_context, "Error: Please choose only one command\n"); 1250 } 1251 } 1252 1253 if (cli_context->cli_mode == CLI_MODE_CMD && cmd_chosen == 0) { 1254 usage(cli_context, "Error: Please choose a command.\n"); 1255 } 1256 1257 /* 1258 * We don't check the local boolean because in some modes it will have been set 1259 * on and earlier command. 1260 */ 1261 if ((strcmp(cli_context->bdev_name, "") == 0) && (cli_context->action != CLI_HELP)) { 1262 usage(cli_context, "Error: -b option is required.\n"); 1263 cmd_chosen = 0; 1264 } 1265 1266 /* in shell mode we'll call getopt multiple times so need to reset its index */ 1267 optind = 0; 1268 return (cmd_chosen == 1); 1269 } 1270 1271 /* 1272 * In script mode, we parsed a script file at startup and saved off a bunch of cmd 1273 * lines that we now parse with each run of cli_start so we us the same cmd parser 1274 * as cmd and shell modes. 1275 */ 1276 static bool 1277 line_parser(struct cli_context_t *cli_context) 1278 { 1279 bool cmd_chosen; 1280 char *tok = NULL; 1281 int blob_num = 0; 1282 int start_idx = cli_context->argc; 1283 int i; 1284 1285 printf("\nSCRIPT NOW PROCESSING: %s\n", g_script.cmdline[g_script.cmdline_idx]); 1286 tok = strtok(g_script.cmdline[g_script.cmdline_idx], " "); 1287 while (tok != NULL) { 1288 /* 1289 * We support one replaceable token right now, a $Bn 1290 * represents the blobid that was created in position n 1291 * so fish this out now and use it here. 1292 */ 1293 cli_context->argv[cli_context->argc] = strdup(tok); 1294 if (tok[0] == '$' && tok[1] == 'B') { 1295 tok += 2; 1296 blob_num = spdk_strtol(tok, 10); 1297 if (blob_num >= 0 && blob_num < MAX_SCRIPT_BLOBS) { 1298 cli_context->argv[cli_context->argc] = 1299 realloc(cli_context->argv[cli_context->argc], BUFSIZE); 1300 if (cli_context->argv[cli_context->argc] == NULL) { 1301 printf("ERROR: unable to realloc memory\n"); 1302 spdk_app_stop(-1); 1303 } 1304 if (g_script.blobid[blob_num] == 0) { 1305 printf("ERROR: There is no blob for $B%d\n", 1306 blob_num); 1307 } 1308 snprintf(cli_context->argv[cli_context->argc], BUFSIZE, 1309 "%" PRIu64, g_script.blobid[blob_num]); 1310 } else { 1311 printf("ERROR: Invalid token or exceeded max blobs of %d\n", 1312 MAX_SCRIPT_BLOBS); 1313 } 1314 } 1315 cli_context->argc++; 1316 tok = strtok(NULL, " "); 1317 } 1318 1319 /* call parse cmd line with user input as args */ 1320 cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context); 1321 1322 /* free strdup memory and reset arg count for next shell interaction */ 1323 for (i = start_idx; i < cli_context->argc; i++) { 1324 free(cli_context->argv[i]); 1325 cli_context->argv[i] = NULL; 1326 } 1327 cli_context->argc = 1; 1328 1329 g_script.cmdline_idx++; 1330 assert(g_script.cmdline_idx < MAX_SCRIPT_LINES); 1331 1332 if (cmd_chosen == false) { 1333 printf("ERROR: Invalid script line starting with: %s\n\n", 1334 g_script.cmdline[g_script.cmdline_idx - 1]); 1335 if (g_script.ignore_errors == false) { 1336 printf("** Aborting **\n"); 1337 cli_context->action = CLI_SHELL_EXIT; 1338 cmd_chosen = true; 1339 unload_bs(cli_context, "", 0); 1340 } else { 1341 printf("** Skipping **\n"); 1342 } 1343 } 1344 1345 return cmd_chosen; 1346 } 1347 1348 /* 1349 * For script mode, we read a series of commands from a text file and store them 1350 * in a global struct. That, along with the cli_mode that tells us we're in 1351 * script mode is what feeds the rest of the app in the same way as is it were 1352 * getting commands from shell mode. 1353 */ 1354 static void 1355 parse_script(struct cli_context_t *cli_context) 1356 { 1357 FILE *fp = NULL; 1358 size_t bufsize = BUFSIZE; 1359 int64_t bytes_in = 0; 1360 int i = 0; 1361 1362 /* initialize global script values */ 1363 for (i = 0; i < MAX_SCRIPT_BLOBS; i++) { 1364 g_script.blobid[i] = 0; 1365 } 1366 g_script.blobid_idx = 0; 1367 g_script.cmdline_idx = 0; 1368 i = 0; 1369 1370 fp = fopen(cli_context->script_file, "r"); 1371 if (fp == NULL) { 1372 printf("ERROR: unable to open script: %s\n", 1373 cli_context->script_file); 1374 cli_cleanup(cli_context); 1375 exit(-1); 1376 } 1377 1378 do { 1379 bytes_in = getline(&g_script.cmdline[i], &bufsize, fp); 1380 if (bytes_in > 0) { 1381 /* replace newline with null */ 1382 spdk_str_chomp(g_script.cmdline[i]); 1383 1384 /* ignore comments */ 1385 if (g_script.cmdline[i][0] != '#') { 1386 i++; 1387 } 1388 } 1389 } while (bytes_in != -1 && i < MAX_SCRIPT_LINES - 1); 1390 fclose(fp); 1391 1392 /* add an exit cmd in case they didn't */ 1393 g_script.cmdline[i] = realloc(g_script.cmdline[i], BUFSIZE); 1394 if (g_script.cmdline[i] == NULL) { 1395 int j; 1396 1397 for (j = 0; j < i; j++) { 1398 free(g_script.cmdline[j]); 1399 g_script.cmdline[j] = NULL; 1400 } 1401 unload_bs(cli_context, "ERROR: unable to alloc memory.\n", 0); 1402 } 1403 snprintf(g_script.cmdline[i], BUFSIZE, "%s", "-X"); 1404 g_script.max_index = i; 1405 } 1406 1407 /* 1408 * Provides for a shell interface as opposed to one shot command line. 1409 */ 1410 static bool 1411 cli_shell(void *arg1, void *arg2) 1412 { 1413 struct cli_context_t *cli_context = arg1; 1414 char *line = NULL; 1415 ssize_t buf_size = 0; 1416 ssize_t bytes_in = 0; 1417 ssize_t tok_len = 0; 1418 char *tok = NULL; 1419 bool cmd_chosen = false; 1420 int start_idx = cli_context->argc; 1421 int i; 1422 1423 printf("blob> "); 1424 bytes_in = getline(&line, &buf_size, stdin); 1425 1426 /* If getline() failed (EOF), exit the shell. */ 1427 if (bytes_in < 0) { 1428 free(line); 1429 cli_context->action = CLI_SHELL_EXIT; 1430 return true; 1431 } 1432 1433 /* parse input and update cli_context so we can use common option parser */ 1434 if (bytes_in > 0) { 1435 tok = strtok(line, " "); 1436 } 1437 while ((tok != NULL) && (cli_context->argc < MAX_ARGS)) { 1438 cli_context->argv[cli_context->argc] = strdup(tok); 1439 tok_len = strlen(tok); 1440 cli_context->argc++; 1441 tok = strtok(NULL, " "); 1442 } 1443 1444 /* replace newline on last arg with null */ 1445 if (tok_len) { 1446 spdk_str_chomp(cli_context->argv[cli_context->argc - 1]); 1447 } 1448 1449 /* call parse cmd line with user input as args */ 1450 cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context); 1451 1452 /* free strdup mem & reset arg count for next shell interaction */ 1453 for (i = start_idx; i < cli_context->argc; i++) { 1454 free(cli_context->argv[i]); 1455 cli_context->argv[i] = NULL; 1456 } 1457 cli_context->argc = 1; 1458 1459 free(line); 1460 1461 return cmd_chosen; 1462 } 1463 1464 /* 1465 * This is the function we pass into the SPDK framework that gets 1466 * called first. 1467 */ 1468 static void 1469 cli_start(void *arg1) 1470 { 1471 struct cli_context_t *cli_context = arg1; 1472 1473 /* 1474 * If we're in script mode, we already have a list of commands so 1475 * just need to pull them out one at a time and process them. 1476 */ 1477 if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 1478 while (line_parser(cli_context) == false); 1479 } 1480 1481 /* 1482 * The initial cmd line options are parsed once before this function is 1483 * called so if there is no action, we're in shell mode and will loop 1484 * here until a a valid option is parsed and returned. 1485 */ 1486 if (cli_context->action == CLI_NONE) { 1487 while (cli_shell(cli_context, NULL) == false); 1488 } 1489 1490 /* Decide what to do next based on cmd line parsing. */ 1491 switch (cli_context->action) { 1492 case CLI_SET_SUPER: 1493 case CLI_SHOW_BS: 1494 case CLI_CREATE_BLOB: 1495 case CLI_SET_XATTR: 1496 case CLI_REM_XATTR: 1497 case CLI_SHOW_BLOB: 1498 case CLI_LIST_BLOBS: 1499 case CLI_DUMP_BLOB: 1500 case CLI_IMPORT_BLOB: 1501 case CLI_FILL: 1502 case CLI_RECOVER: 1503 case CLI_DELETE_BLOB: 1504 load_bs(cli_context); 1505 break; 1506 case CLI_INIT_BS: 1507 init_bs(cli_context); 1508 break; 1509 case CLI_DUMP_BS: 1510 dump_bs(cli_context); 1511 break; 1512 case CLI_LIST_BDEVS: 1513 list_bdevs(cli_context); 1514 break; 1515 case CLI_SHELL_EXIT: 1516 /* 1517 * Because shell mode reuses cmd mode functions, the blobstore 1518 * is loaded/unloaded with every action so we just need to 1519 * stop the framework. For this app there's no need to optimize 1520 * and keep the blobstore open while the app is in shell mode. 1521 */ 1522 spdk_app_stop(0); 1523 break; 1524 case CLI_HELP: 1525 usage(cli_context, ""); 1526 unload_complete(cli_context, 0); 1527 break; 1528 default: 1529 /* should never get here */ 1530 exit(-1); 1531 break; 1532 } 1533 } 1534 1535 int 1536 main(int argc, char **argv) 1537 { 1538 struct spdk_app_opts opts = {}; 1539 struct cli_context_t *cli_context = NULL; 1540 bool cmd_chosen; 1541 int rc = 0; 1542 1543 if (argc < 2) { 1544 usage(cli_context, "ERROR: Invalid option\n"); 1545 exit(-1); 1546 } 1547 1548 cli_context = calloc(1, sizeof(struct cli_context_t)); 1549 if (cli_context == NULL) { 1550 printf("ERROR: could not allocate context structure\n"); 1551 exit(-1); 1552 } 1553 1554 /* default to CMD mode until we've parsed the first parms */ 1555 cli_context->cli_mode = CLI_MODE_CMD; 1556 cli_context->argv[0] = strdup(argv[0]); 1557 cli_context->argc = 1; 1558 1559 /* parse command line */ 1560 cmd_chosen = cmd_parser(argc, argv, cli_context); 1561 free(cli_context->argv[0]); 1562 cli_context->argv[0] = NULL; 1563 if (cmd_chosen == false) { 1564 cli_cleanup(cli_context); 1565 exit(-1); 1566 } 1567 1568 /* after displaying help, just exit */ 1569 if (cli_context->action == CLI_HELP) { 1570 usage(cli_context, ""); 1571 cli_cleanup(cli_context); 1572 exit(-1); 1573 } 1574 1575 /* if they don't supply a conf name, use the default */ 1576 if (!cli_context->config_file) { 1577 cli_context->config_file = program_conf; 1578 } 1579 1580 /* if the config file doesn't exist, tell them how to make one */ 1581 if (access(cli_context->config_file, F_OK) == -1) { 1582 printf("Error: No config file found.\n"); 1583 printf("To create a config file named 'blobcli.json' for your NVMe device:\n"); 1584 printf(" <path to spdk>/scripts/gen_nvme.sh --json-with-subsystems > blobcli.json\n"); 1585 printf("and then re-run the cli tool.\n"); 1586 exit(-1); 1587 } 1588 1589 /* 1590 * For script mode we keep a bunch of stuff in a global since 1591 * none if it is passed back and forth to SPDK. 1592 */ 1593 if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 1594 /* 1595 * Now we'll build up the global which will direct this run of the app 1596 * as it will have a list (g_script) of all of the commands line by 1597 * line as if they were typed in on the shell at cmd line. 1598 */ 1599 parse_script(cli_context); 1600 } 1601 1602 /* Set default values in opts struct along with name and conf file. */ 1603 spdk_app_opts_init(&opts, sizeof(opts)); 1604 opts.name = "blobcli"; 1605 opts.json_config_file = cli_context->config_file; 1606 1607 cli_context->app_started = true; 1608 rc = spdk_app_start(&opts, cli_start, cli_context); 1609 if (rc) { 1610 printf("ERROR!\n"); 1611 } 1612 1613 /* Free up memory that we allocated */ 1614 cli_cleanup(cli_context); 1615 1616 /* Gracefully close out all of the SPDK subsystems. */ 1617 spdk_app_fini(); 1618 return rc; 1619 } 1620