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