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