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_BLOB, 72 CLI_DUMP_BLOB, 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 if (cli_context->cli_mode != CLI_MODE_SCRIPT) { 254 spdk_app_stop(bserrno); 255 256 } 257 } 258 259 /* 260 * Callback for closing a blob. 261 */ 262 static void 263 close_cb(void *arg1, int bserrno) 264 { 265 struct cli_context_t *cli_context = arg1; 266 267 if (bserrno) { 268 unload_bs(cli_context, "Error in close callback", 269 bserrno); 270 return; 271 } 272 unload_bs(cli_context, "", 0); 273 } 274 275 /* 276 * Callback function for sync'ing metadata. 277 */ 278 static void 279 sync_cb(void *arg1, int bserrno) 280 { 281 struct cli_context_t *cli_context = arg1; 282 283 if (bserrno) { 284 unload_bs(cli_context, "Error in sync callback", 285 bserrno); 286 return; 287 } 288 289 spdk_blob_close(cli_context->blob, close_cb, 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_blob_resize(cli_context->blob, cli_context->num_clusters); 310 if (rc) { 311 unload_bs(cli_context, "Error in blob resize", 312 bserrno); 313 return; 314 } 315 316 total = spdk_blob_get_num_clusters(cli_context->blob); 317 printf("blob now has USED clusters of %" PRIu64 "\n", 318 total); 319 320 /* 321 * Always a good idea to sync after MD changes or the changes 322 * may be lost if things aren't closed cleanly. 323 */ 324 spdk_blob_sync_md(cli_context->blob, sync_cb, cli_context); 325 } 326 327 /* 328 * Callback function for creating a blob. 329 */ 330 static void 331 blob_create_cb(void *arg1, spdk_blob_id blobid, int bserrno) 332 { 333 struct cli_context_t *cli_context = arg1; 334 335 if (bserrno) { 336 unload_bs(cli_context, "Error in blob create callback", 337 bserrno); 338 return; 339 } 340 341 cli_context->blobid = blobid; 342 printf("New blob id %" PRIu64 "\n", cli_context->blobid); 343 344 /* if we're in script mode, we need info on all blobids for later */ 345 if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 346 g_script.blobid[g_script.blobid_idx++] = blobid; 347 } 348 349 /* We have to open the blob before we can do things like resize. */ 350 spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 351 open_now_resize_cb, cli_context); 352 } 353 354 /* 355 * Callback for get_super where we'll continue on to show blobstore info. 356 */ 357 static void 358 show_bs_cb(void *arg1, spdk_blob_id blobid, int bserrno) 359 { 360 struct cli_context_t *cli_context = arg1; 361 struct spdk_bs_type bstype; 362 uint64_t val; 363 struct spdk_bdev *bdev = NULL; 364 365 if (bserrno && bserrno != -ENOENT) { 366 unload_bs(cli_context, "Error in get_super callback", 367 bserrno); 368 return; 369 } 370 cli_context->superid = blobid; 371 372 bdev = spdk_bdev_get_by_name(cli_context->bdev_name); 373 if (bdev == NULL) { 374 unload_bs(cli_context, "Error w/bdev in get_super callback", 375 bserrno); 376 return; 377 } 378 379 printf("Blobstore Public Info:\n"); 380 printf("\tUsing bdev Product Name: %s\n", 381 spdk_bdev_get_product_name(bdev)); 382 printf("\tAPI Version: %d\n", SPDK_BS_VERSION); 383 384 if (bserrno != -ENOENT) { 385 printf("\tsuper blob ID: %" PRIu64 "\n", cli_context->superid); 386 } else { 387 printf("\tsuper blob ID: none assigned\n"); 388 } 389 390 printf("\tpage size: %" PRIu64 "\n", cli_context->page_size); 391 392 val = spdk_bs_get_cluster_size(cli_context->bs); 393 printf("\tcluster size: %" PRIu64 "\n", val); 394 395 val = spdk_bs_free_cluster_count(cli_context->bs); 396 printf("\t# free clusters: %" PRIu64 "\n", val); 397 398 bstype = spdk_bs_get_bstype(cli_context->bs); 399 spdk_trace_dump(stdout, "\tblobstore type:", &bstype, sizeof(bstype)); 400 401 /* 402 * Private info isn't accessible via the public API but 403 * may be useful for debug of blobstore based applications. 404 */ 405 printf("\nBlobstore Private Info:\n"); 406 printf("\tMetadata start (pages): %" PRIu64 "\n", 407 cli_context->bs->md_start); 408 printf("\tMetadata length (pages): %d\n", 409 cli_context->bs->md_len); 410 411 unload_bs(cli_context, "", 0); 412 } 413 414 /* 415 * Show detailed info about a particular blob. 416 */ 417 static void 418 show_blob(struct cli_context_t *cli_context) 419 { 420 uint64_t val; 421 struct spdk_xattr_names *names; 422 const void *value; 423 size_t value_len; 424 char data[BUFSIZE]; 425 unsigned int i; 426 427 printf("Blob Public Info:\n"); 428 429 printf("blob ID: %" PRIu64 "\n", cli_context->blobid); 430 431 val = spdk_blob_get_num_clusters(cli_context->blob); 432 printf("# of clusters: %" PRIu64 "\n", val); 433 434 printf("# of bytes: %" PRIu64 "\n", 435 val * spdk_bs_get_cluster_size(cli_context->bs)); 436 437 val = spdk_blob_get_num_pages(cli_context->blob); 438 printf("# of pages: %" PRIu64 "\n", val); 439 440 spdk_blob_get_xattr_names(cli_context->blob, &names); 441 442 printf("# of xattrs: %d\n", spdk_xattr_names_get_count(names)); 443 printf("xattrs:\n"); 444 for (i = 0; i < spdk_xattr_names_get_count(names); i++) { 445 spdk_blob_get_xattr_value(cli_context->blob, 446 spdk_xattr_names_get_name(names, i), 447 &value, &value_len); 448 if ((value_len + 1) > sizeof(data)) { 449 printf("FYI: adjusting size of xattr due to CLI limits.\n"); 450 value_len = sizeof(data) - 1; 451 } 452 memcpy(&data, value, value_len); 453 data[value_len] = '\0'; 454 printf("\n(%d) Name:%s\n", i, 455 spdk_xattr_names_get_name(names, i)); 456 printf("(%d) Value:\n", i); 457 spdk_trace_dump(stdout, "", value, value_len); 458 } 459 460 /* 461 * Private info isn't accessible via the public API but 462 * may be useful for debug of blobstore based applications. 463 */ 464 printf("\nBlob Private Info:\n"); 465 switch (__blob_to_data(cli_context->blob)->state) { 466 case SPDK_BLOB_STATE_DIRTY: 467 printf("state: DIRTY\n"); 468 break; 469 case SPDK_BLOB_STATE_CLEAN: 470 printf("state: CLEAN\n"); 471 break; 472 case SPDK_BLOB_STATE_LOADING: 473 printf("state: LOADING\n"); 474 break; 475 case SPDK_BLOB_STATE_SYNCING: 476 printf("state: SYNCING\n"); 477 break; 478 default: 479 printf("state: UNKNOWN\n"); 480 break; 481 } 482 printf("open ref count: %d\n", 483 __blob_to_data(cli_context->blob)->open_ref); 484 485 spdk_xattr_names_free(names); 486 } 487 488 /* 489 * Callback for getting the first blob, shared with simple blob listing as well. 490 */ 491 static void 492 blob_iter_cb(void *arg1, struct spdk_blob *blob, int bserrno) 493 { 494 struct cli_context_t *cli_context = arg1; 495 496 if (bserrno) { 497 if (bserrno == -ENOENT) { 498 /* this simply means there are no more blobs */ 499 unload_bs(cli_context, "", 0); 500 } else { 501 unload_bs(cli_context, "Error in blob iter callback", 502 bserrno); 503 } 504 return; 505 } 506 507 if (cli_context->action == CLI_LIST_BLOBS) { 508 printf("\nList BLOBS:\n"); 509 printf("Found blob with ID# %" PRIu64 "\n", 510 spdk_blob_get_id(blob)); 511 } else if (spdk_blob_get_id(blob) == cli_context->blobid) { 512 /* 513 * Found the blob we're looking for, but we need to finish 514 * iterating even after showing the info so that internally 515 * the blobstore logic will close the blob. Or we could 516 * chose to close it now, either way. 517 */ 518 cli_context->blob = blob; 519 show_blob(cli_context); 520 } 521 522 spdk_bs_iter_next(cli_context->bs, blob, blob_iter_cb, cli_context); 523 } 524 525 /* 526 * Callback for setting the super blob ID. 527 */ 528 static void 529 set_super_cb(void *arg1, int bserrno) 530 { 531 struct cli_context_t *cli_context = arg1; 532 533 if (bserrno) { 534 unload_bs(cli_context, "Error in set_super callback", 535 bserrno); 536 return; 537 } 538 539 printf("Super Blob ID has been set.\n"); 540 unload_bs(cli_context, "", 0); 541 } 542 543 /* 544 * Callback for set_xattr_open where we set or delete xattrs. 545 */ 546 static void 547 set_xattr_cb(void *cb_arg, struct spdk_blob *blob, int bserrno) 548 { 549 struct cli_context_t *cli_context = cb_arg; 550 551 if (bserrno) { 552 unload_bs(cli_context, "Error in blob open callback", 553 bserrno); 554 return; 555 } 556 cli_context->blob = blob; 557 558 if (cli_context->action == CLI_SET_XATTR) { 559 spdk_blob_set_xattr(cli_context->blob, cli_context->key, 560 cli_context->value, strlen(cli_context->value) + 1); 561 printf("Xattr has been set.\n"); 562 } else { 563 spdk_blob_remove_xattr(cli_context->blob, cli_context->key); 564 printf("Xattr has been removed.\n"); 565 } 566 567 spdk_blob_sync_md(cli_context->blob, sync_cb, cli_context); 568 } 569 570 /* 571 * Callback function for reading a blob for dumping to a file. 572 */ 573 static void 574 read_dump_cb(void *arg1, int bserrno) 575 { 576 struct cli_context_t *cli_context = arg1; 577 uint64_t bytes_written; 578 579 if (bserrno) { 580 fclose(cli_context->fp); 581 unload_bs(cli_context, "Error in read completion", 582 bserrno); 583 return; 584 } 585 586 bytes_written = fwrite(cli_context->buff, NUM_PAGES, cli_context->page_size, 587 cli_context->fp); 588 if (bytes_written != cli_context->page_size) { 589 fclose(cli_context->fp); 590 unload_bs(cli_context, "Error with fwrite", 591 bserrno); 592 return; 593 } 594 595 printf("."); 596 if (++cli_context->page_count < cli_context->blob_pages) { 597 /* perform another read */ 598 spdk_bs_io_read_blob(cli_context->blob, cli_context->channel, 599 cli_context->buff, cli_context->page_count, 600 NUM_PAGES, read_dump_cb, cli_context); 601 } else { 602 /* done reading */ 603 printf("\nFile write complete (to %s).\n", cli_context->file); 604 fclose(cli_context->fp); 605 spdk_blob_close(cli_context->blob, close_cb, cli_context); 606 } 607 } 608 609 /* 610 * Callback for write completion on the import of a file to a blob. 611 */ 612 static void 613 write_imp_cb(void *arg1, int bserrno) 614 { 615 struct cli_context_t *cli_context = arg1; 616 uint64_t bytes_read; 617 618 if (bserrno) { 619 fclose(cli_context->fp); 620 unload_bs(cli_context, "Error in write completion", 621 bserrno); 622 return; 623 } 624 625 if (cli_context->bytes_so_far < cli_context->filesize) { 626 /* perform another file read */ 627 bytes_read = fread(cli_context->buff, 1, 628 cli_context->page_size, 629 cli_context->fp); 630 cli_context->bytes_so_far += bytes_read; 631 632 /* if this read is < 1 page, fill with 0s */ 633 if (bytes_read < cli_context->page_size) { 634 uint8_t *offset = cli_context->buff + bytes_read; 635 memset(offset, 0, cli_context->page_size - bytes_read); 636 } 637 } else { 638 /* 639 * Done reading the file, fill the rest of the blob with 0s, 640 * yeah we're memsetting the same page over and over here 641 */ 642 memset(cli_context->buff, 0, cli_context->page_size); 643 } 644 if (++cli_context->page_count < cli_context->blob_pages) { 645 printf("."); 646 spdk_bs_io_write_blob(cli_context->blob, cli_context->channel, 647 cli_context->buff, cli_context->page_count, 648 NUM_PAGES, write_imp_cb, cli_context); 649 } else { 650 /* done writing */ 651 printf("\nBlob import complete (from %s).\n", cli_context->file); 652 fclose(cli_context->fp); 653 spdk_blob_close(cli_context->blob, close_cb, cli_context); 654 } 655 } 656 657 /* 658 * Callback for open blobs where we'll continue on dump a blob to a file or 659 * import a file to a blob. For dump, the resulting file will always be the 660 * full size of the blob. For import, the blob will fill with the file 661 * contents first and then 0 out the rest of the blob. 662 */ 663 static void 664 dump_imp_open_cb(void *cb_arg, struct spdk_blob *blob, int bserrno) 665 { 666 struct cli_context_t *cli_context = cb_arg; 667 668 if (bserrno) { 669 unload_bs(cli_context, "Error in blob open callback", 670 bserrno); 671 return; 672 } 673 cli_context->blob = blob; 674 675 /* 676 * We'll transfer just one page at a time to keep the buffer 677 * small. This could be bigger of course. 678 */ 679 cli_context->buff = spdk_dma_malloc(cli_context->page_size, 680 ALIGN_4K, NULL); 681 if (cli_context->buff == NULL) { 682 printf("Error in allocating memory\n"); 683 spdk_blob_close(cli_context->blob, close_cb, cli_context); 684 return; 685 } 686 printf("Working"); 687 cli_context->blob_pages = spdk_blob_get_num_pages(cli_context->blob); 688 cli_context->page_count = 0; 689 if (cli_context->action == CLI_DUMP_BLOB) { 690 cli_context->fp = fopen(cli_context->file, "w"); 691 if (cli_context->fp == NULL) { 692 printf("Error in opening file\n"); 693 spdk_blob_close(cli_context->blob, close_cb, cli_context); 694 return; 695 } 696 697 /* read a page of data from the blob */ 698 spdk_bs_io_read_blob(cli_context->blob, cli_context->channel, 699 cli_context->buff, cli_context->page_count, 700 NUM_PAGES, read_dump_cb, cli_context); 701 } else { 702 cli_context->fp = fopen(cli_context->file, "r"); 703 if (cli_context->fp == NULL) { 704 printf("Error in opening file: errno %d\n", errno); 705 spdk_blob_close(cli_context->blob, close_cb, cli_context); 706 return; 707 } 708 709 /* get the filesize then rewind read a page of data from file */ 710 fseek(cli_context->fp, 0L, SEEK_END); 711 cli_context->filesize = ftell(cli_context->fp); 712 rewind(cli_context->fp); 713 cli_context->bytes_so_far = fread(cli_context->buff, NUM_PAGES, 714 cli_context->page_size, 715 cli_context->fp); 716 717 /* if the file is < a page, fill the rest with 0s */ 718 if (cli_context->filesize < cli_context->page_size) { 719 uint8_t *offset = 720 cli_context->buff + cli_context->filesize; 721 722 memset(offset, 0, 723 cli_context->page_size - cli_context->filesize); 724 } 725 726 spdk_bs_io_write_blob(cli_context->blob, cli_context->channel, 727 cli_context->buff, cli_context->page_count, 728 NUM_PAGES, write_imp_cb, cli_context); 729 } 730 } 731 732 /* 733 * Callback function for writing a specific pattern to page 0. 734 */ 735 static void 736 write_cb(void *arg1, int bserrno) 737 { 738 struct cli_context_t *cli_context = arg1; 739 740 if (bserrno) { 741 unload_bs(cli_context, "Error in write completion", 742 bserrno); 743 return; 744 } 745 printf("."); 746 if (++cli_context->page_count < cli_context->blob_pages) { 747 spdk_bs_io_write_blob(cli_context->blob, cli_context->channel, 748 cli_context->buff, cli_context->page_count, 749 NUM_PAGES, write_cb, cli_context); 750 } else { 751 /* done writing */ 752 printf("\nBlob fill complete (with 0x%x).\n", cli_context->fill_value); 753 spdk_blob_close(cli_context->blob, close_cb, cli_context); 754 } 755 } 756 757 /* 758 * Callback function to fill a blob with a value, callback from open. 759 */ 760 static void 761 fill_blob_cb(void *arg1, struct spdk_blob *blob, int bserrno) 762 { 763 struct cli_context_t *cli_context = arg1; 764 765 if (bserrno) { 766 unload_bs(cli_context, "Error in open callback", 767 bserrno); 768 return; 769 } 770 771 cli_context->blob = blob; 772 cli_context->page_count = 0; 773 cli_context->blob_pages = spdk_blob_get_num_pages(cli_context->blob); 774 cli_context->buff = spdk_dma_malloc(cli_context->page_size, 775 ALIGN_4K, NULL); 776 if (cli_context->buff == NULL) { 777 unload_bs(cli_context, "Error in allocating memory", 778 -ENOMEM); 779 return; 780 } 781 782 memset(cli_context->buff, cli_context->fill_value, 783 cli_context->page_size); 784 printf("Working"); 785 spdk_bs_io_write_blob(cli_context->blob, cli_context->channel, 786 cli_context->buff, 787 STARTING_PAGE, NUM_PAGES, write_cb, cli_context); 788 } 789 790 /* 791 * Multiple actions require us to open the bs first so here we use 792 * a common callback to set a bunch of values and then move on to 793 * the next step saved off via function pointer. 794 */ 795 static void 796 load_bs_cb(void *arg1, struct spdk_blob_store *bs, int bserrno) 797 { 798 struct cli_context_t *cli_context = arg1; 799 800 if (bserrno) { 801 unload_bs(cli_context, "Error in load callback", 802 bserrno); 803 return; 804 } 805 806 cli_context->bs = bs; 807 cli_context->page_size = spdk_bs_get_page_size(cli_context->bs); 808 cli_context->channel = spdk_bs_alloc_io_channel(cli_context->bs); 809 if (cli_context->channel == NULL) { 810 unload_bs(cli_context, "Error in allocating channel", 811 -ENOMEM); 812 return; 813 } 814 815 switch (cli_context->action) { 816 case CLI_SET_SUPER: 817 spdk_bs_set_super(cli_context->bs, cli_context->superid, 818 set_super_cb, cli_context); 819 break; 820 case CLI_SHOW_BS: 821 spdk_bs_get_super(cli_context->bs, show_bs_cb, cli_context); 822 break; 823 case CLI_CREATE_BLOB: 824 spdk_bs_create_blob(cli_context->bs, blob_create_cb, cli_context); 825 break; 826 case CLI_SET_XATTR: 827 case CLI_REM_XATTR: 828 spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 829 set_xattr_cb, cli_context); 830 break; 831 case CLI_SHOW_BLOB: 832 case CLI_LIST_BLOBS: 833 spdk_bs_iter_first(cli_context->bs, blob_iter_cb, cli_context); 834 835 break; 836 case CLI_DUMP_BLOB: 837 case CLI_IMPORT_BLOB: 838 spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 839 dump_imp_open_cb, cli_context); 840 break; 841 case CLI_FILL: 842 spdk_bs_open_blob(cli_context->bs, cli_context->blobid, 843 fill_blob_cb, cli_context); 844 break; 845 846 default: 847 /* should never get here */ 848 exit(-1); 849 break; 850 } 851 } 852 853 /* 854 * Load the blobstore. 855 */ 856 static void 857 load_bs(struct cli_context_t *cli_context) 858 { 859 struct spdk_bdev *bdev = NULL; 860 struct spdk_bs_dev *bs_dev = NULL; 861 862 bdev = spdk_bdev_get_by_name(cli_context->bdev_name); 863 if (bdev == NULL) { 864 printf("Could not find a bdev\n"); 865 spdk_app_stop(-1); 866 return; 867 } 868 869 bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL); 870 if (bs_dev == NULL) { 871 printf("Could not create blob bdev!!\n"); 872 spdk_app_stop(-1); 873 return; 874 } 875 876 spdk_bs_load(bs_dev, NULL, load_bs_cb, cli_context); 877 } 878 879 /* 880 * Lists all the blobs on this blobstore. 881 */ 882 static void 883 list_bdevs(struct cli_context_t *cli_context) 884 { 885 struct spdk_bdev *bdev = NULL; 886 887 printf("\nList bdevs:\n"); 888 889 bdev = spdk_bdev_first(); 890 if (bdev == NULL) { 891 printf("Could not find a bdev\n"); 892 } 893 while (bdev) { 894 printf("\tbdev Name: %s\n", spdk_bdev_get_name(bdev)); 895 printf("\tbdev Product Name: %s\n", 896 spdk_bdev_get_product_name(bdev)); 897 bdev = spdk_bdev_next(bdev); 898 } 899 900 printf("\n"); 901 if (cli_context->cli_mode == CLI_MODE_CMD) { 902 spdk_app_stop(0); 903 } else { 904 cli_context->action = CLI_NONE; 905 cli_start(cli_context, NULL); 906 } 907 } 908 909 /* 910 * Callback function for initializing a blob. 911 */ 912 static void 913 bs_init_cb(void *cb_arg, struct spdk_blob_store *bs, 914 int bserrno) 915 { 916 struct cli_context_t *cli_context = cb_arg; 917 918 if (bserrno) { 919 unload_bs(cli_context, "Error in bs init callback", 920 bserrno); 921 return; 922 } 923 cli_context->bs = bs; 924 printf("blobstore init'd: (%p)\n", cli_context->bs); 925 926 unload_bs(cli_context, "", 0); 927 } 928 929 /* 930 * Initialize a new blobstore. 931 */ 932 static void 933 init_bs(struct cli_context_t *cli_context) 934 { 935 struct spdk_bdev *bdev = NULL; 936 937 bdev = spdk_bdev_get_by_name(cli_context->bdev_name); 938 if (bdev == NULL) { 939 printf("Could not find a bdev\n"); 940 spdk_app_stop(-1); 941 return; 942 } 943 printf("Init blobstore using bdev Product Name: %s\n", 944 spdk_bdev_get_product_name(bdev)); 945 946 cli_context->bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL); 947 if (cli_context->bs_dev == NULL) { 948 printf("Could not create blob bdev!!\n"); 949 spdk_app_stop(-1); 950 return; 951 } 952 953 spdk_bs_init(cli_context->bs_dev, NULL, bs_init_cb, 954 cli_context); 955 } 956 957 /* 958 * Common cmd/option parser for command and shell modes. 959 */ 960 static bool 961 cmd_parser(int argc, char **argv, struct cli_context_t *cli_context) 962 { 963 int op; 964 int cmd_chosen = 0; 965 char resp; 966 967 while ((op = getopt(argc, argv, "b:c:d:f:hil:m:n:p:r:s:ST:Xx:")) != -1) { 968 switch (op) { 969 case 'b': 970 if (strcmp(cli_context->bdev_name, "") == 0) { 971 snprintf(cli_context->bdev_name, BUFSIZE, "%s", optarg); 972 } else { 973 printf("Current setting for -b is: %s\n", cli_context->bdev_name); 974 usage(cli_context, "ERROR: -b option can only be set once.\n"); 975 } 976 break; 977 case 'c': 978 if (cli_context->app_started == false) { 979 cli_context->config_file = optarg; 980 } else { 981 usage(cli_context, "ERROR: -c option not valid during shell mode.\n"); 982 } 983 break; 984 case 'd': 985 if (argv[optind] != NULL) { 986 cmd_chosen++; 987 cli_context->action = CLI_DUMP_BLOB; 988 cli_context->blobid = atoll(optarg); 989 snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]); 990 } else { 991 usage(cli_context, "ERROR: missing parameter.\n"); 992 } 993 break; 994 case 'f': 995 if (argv[optind] != NULL) { 996 cmd_chosen++; 997 cli_context->action = CLI_FILL; 998 cli_context->blobid = atoll(optarg); 999 cli_context->fill_value = atoi(argv[optind]); 1000 } else { 1001 usage(cli_context, "ERROR: missing parameter.\n"); 1002 } 1003 break; 1004 case 'h': 1005 cmd_chosen++; 1006 cli_context->action = CLI_HELP; 1007 break; 1008 case 'i': 1009 if (cli_context->cli_mode != CLI_MODE_SCRIPT) { 1010 printf("Your entire blobstore will be destroyed. Are you sure? (y/n) "); 1011 if (scanf("%c%*c", &resp)) { 1012 if (resp == 'y' || resp == 'Y') { 1013 cmd_chosen++; 1014 cli_context->action = CLI_INIT_BS; 1015 } else { 1016 if (cli_context->cli_mode == CLI_MODE_CMD) { 1017 spdk_app_stop(0); 1018 return false; 1019 } 1020 } 1021 } 1022 } else { 1023 cmd_chosen++; 1024 cli_context->action = CLI_INIT_BS; 1025 } 1026 break; 1027 case 'r': 1028 if (argv[optind] != NULL) { 1029 cmd_chosen++; 1030 cli_context->action = CLI_REM_XATTR; 1031 cli_context->blobid = atoll(optarg); 1032 snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]); 1033 } else { 1034 usage(cli_context, "ERROR: missing parameter.\n"); 1035 } 1036 break; 1037 case 'l': 1038 if (strcmp("bdevs", optarg) == 0) { 1039 cmd_chosen++; 1040 cli_context->action = CLI_LIST_BDEVS; 1041 } else if (strcmp("blobs", optarg) == 0) { 1042 cmd_chosen++; 1043 cli_context->action = CLI_LIST_BLOBS; 1044 } else { 1045 usage(cli_context, "ERROR: invalid option for list\n"); 1046 } 1047 break; 1048 case 'm': 1049 if (argv[optind] != NULL) { 1050 cmd_chosen++; 1051 cli_context->action = CLI_IMPORT_BLOB; 1052 cli_context->blobid = atoll(optarg); 1053 snprintf(cli_context->file, BUFSIZE, "%s", argv[optind]); 1054 } else { 1055 usage(cli_context, "ERROR: missing parameter.\n"); 1056 } 1057 break; 1058 case 'n': 1059 cli_context->num_clusters = atoi(optarg); 1060 if (cli_context->num_clusters > 0) { 1061 cmd_chosen++; 1062 cli_context->action = CLI_CREATE_BLOB; 1063 } else { 1064 usage(cli_context, "ERROR: invalid option for new\n"); 1065 } 1066 break; 1067 case 'p': 1068 cmd_chosen++; 1069 cli_context->action = CLI_SET_SUPER; 1070 cli_context->superid = atoll(optarg); 1071 break; 1072 case 'S': 1073 if (cli_context->cli_mode == CLI_MODE_CMD) { 1074 cmd_chosen++; 1075 cli_context->cli_mode = CLI_MODE_SHELL; 1076 } 1077 cli_context->action = CLI_NONE; 1078 break; 1079 case 's': 1080 cmd_chosen++; 1081 if (strcmp("bs", optarg) == 0) { 1082 cli_context->action = CLI_SHOW_BS; 1083 } else { 1084 cli_context->action = CLI_SHOW_BLOB; 1085 cli_context->blobid = atoll(optarg); 1086 } 1087 break; 1088 case 'T': 1089 if (cli_context->cli_mode == CLI_MODE_CMD) { 1090 cmd_chosen++; 1091 cli_context->cli_mode = CLI_MODE_SCRIPT; 1092 if (argv[optind] && (strcmp("ignore", argv[optind]) == 0)) { 1093 g_script.ignore_errors = true; 1094 } else { 1095 g_script.ignore_errors = false; 1096 } 1097 snprintf(cli_context->script_file, BUFSIZE, "%s", optarg); 1098 } else { 1099 cli_context->action = CLI_NONE; 1100 } 1101 break; 1102 case 'X': 1103 cmd_chosen++; 1104 cli_context->action = CLI_SHELL_EXIT; 1105 break; 1106 case 'x': 1107 if (argv[optind] != NULL || argv[optind + 1] != NULL) { 1108 cmd_chosen++; 1109 cli_context->action = CLI_SET_XATTR; 1110 cli_context->blobid = atoll(optarg); 1111 snprintf(cli_context->key, BUFSIZE, "%s", argv[optind]); 1112 snprintf(cli_context->value, BUFSIZE, "%s", argv[optind + 1]); 1113 } else { 1114 usage(cli_context, "ERROR: missing parameter.\n"); 1115 } 1116 break; 1117 default: 1118 usage(cli_context, "ERROR: invalid option\n"); 1119 } 1120 /* only one actual command can be done at a time */ 1121 if (cmd_chosen > 1) { 1122 usage(cli_context, "Error: Please choose only one command\n"); 1123 } 1124 } 1125 1126 if (cli_context->cli_mode == CLI_MODE_CMD && cmd_chosen == 0) { 1127 usage(cli_context, "Error: Please choose a command.\n"); 1128 } 1129 1130 /* 1131 * We don't check the local boolean because in some modes it will have been set 1132 * on and earlier command. 1133 */ 1134 if (strcmp(cli_context->bdev_name, "") == 0) { 1135 usage(cli_context, "Error: -b option is required.\n"); 1136 cmd_chosen = 0; 1137 } 1138 1139 /* in shell mode we'll call getopt multiple times so need to reset its index */ 1140 optind = 0; 1141 return (cmd_chosen == 1); 1142 } 1143 1144 /* 1145 * In script mode, we parsed a script file at startup and saved off a bunch of cmd 1146 * lines that we now parse with each run of cli_start so we us the same cmd parser 1147 * as cmd and shell modes. 1148 */ 1149 static bool 1150 line_parser(struct cli_context_t *cli_context) 1151 { 1152 bool cmd_chosen; 1153 char *tok = NULL; 1154 int blob_num = 0; 1155 int start_idx = cli_context->argc; 1156 int i; 1157 1158 printf("\nSCRIPT NOW PROCESSING: %s\n", g_script.cmdline[g_script.cmdline_idx]); 1159 tok = strtok(g_script.cmdline[g_script.cmdline_idx], " "); 1160 while (tok != NULL) { 1161 /* 1162 * We support one replaceable token right now, a $Bn 1163 * represents the blobid that was created in position n 1164 * so fish this out now and use it here. 1165 */ 1166 cli_context->argv[cli_context->argc] = strdup(tok); 1167 if (tok[0] == '$' && tok[1] == 'B') { 1168 tok += 2; 1169 blob_num = atoi(tok); 1170 if (blob_num >= 0 && blob_num < MAX_SCRIPT_BLOBS) { 1171 cli_context->argv[cli_context->argc] = 1172 realloc(cli_context->argv[cli_context->argc], BUFSIZE); 1173 if (cli_context->argv[cli_context->argc] == NULL) { 1174 printf("ERROR: unable to realloc memory\n"); 1175 spdk_app_stop(-1); 1176 } 1177 if (g_script.blobid[blob_num] == 0) { 1178 printf("ERROR: There is no blob for $B%d\n", 1179 blob_num); 1180 } 1181 snprintf(cli_context->argv[cli_context->argc], BUFSIZE, 1182 "%" PRIu64, g_script.blobid[blob_num]); 1183 } else { 1184 printf("ERROR: Invalid token or exceeded max blobs of %d\n", 1185 MAX_SCRIPT_BLOBS); 1186 } 1187 } 1188 cli_context->argc++; 1189 tok = strtok(NULL, " "); 1190 } 1191 1192 /* call parse cmd line with user input as args */ 1193 cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context); 1194 1195 /* free strdup memory and reset arg count for next shell interaction */ 1196 for (i = start_idx; i < cli_context->argc; i++) { 1197 free(cli_context->argv[i]); 1198 cli_context->argv[i] = NULL; 1199 } 1200 cli_context->argc = 1; 1201 1202 g_script.cmdline_idx++; 1203 assert(g_script.cmdline_idx < MAX_SCRIPT_LINES); 1204 1205 if (cmd_chosen == false) { 1206 printf("ERROR: Invalid script line starting with: %s\n\n", 1207 g_script.cmdline[g_script.cmdline_idx - 1]); 1208 if (g_script.ignore_errors == false) { 1209 printf("** Aborting **\n"); 1210 cli_context->action = CLI_SHELL_EXIT; 1211 cmd_chosen = true; 1212 unload_bs(cli_context, "", 0); 1213 } else { 1214 printf("** Skipping **\n"); 1215 } 1216 } 1217 1218 return cmd_chosen; 1219 } 1220 1221 /* 1222 * For script mode, we read a series of commands from a text file and store them 1223 * in a global struct. That, along with the cli_mode that tells us we're in 1224 * script mode is what feeds the rest of the app in the same way as is it were 1225 * getting commands from shell mode. 1226 */ 1227 static void 1228 parse_script(struct cli_context_t *cli_context) 1229 { 1230 FILE *fp = NULL; 1231 size_t bufsize = BUFSIZE; 1232 int64_t bytes_in = 0; 1233 int i = 0; 1234 1235 /* initialize global script values */ 1236 for (i = 0; i < MAX_SCRIPT_BLOBS; i++) { 1237 g_script.blobid[i] = 0; 1238 } 1239 g_script.blobid_idx = 0; 1240 g_script.cmdline_idx = 0; 1241 i = 0; 1242 1243 fp = fopen(cli_context->script_file, "r"); 1244 if (fp == NULL) { 1245 printf("ERROR: unable to open script: %s\n", 1246 cli_context->script_file); 1247 cli_cleanup(cli_context); 1248 exit(-1); 1249 } 1250 1251 do { 1252 bytes_in = getline(&g_script.cmdline[i], &bufsize, fp); 1253 if (bytes_in > 0) { 1254 /* replace newline with null */ 1255 spdk_str_chomp(g_script.cmdline[i]); 1256 1257 /* ignore comments */ 1258 if (g_script.cmdline[i][0] != '#') { 1259 i++; 1260 } 1261 } 1262 } while (bytes_in != -1 && i < MAX_SCRIPT_LINES - 1); 1263 fclose(fp); 1264 1265 /* add an exit cmd in case they didn't */ 1266 g_script.cmdline[i] = realloc(g_script.cmdline[i], BUFSIZE); 1267 if (g_script.cmdline[i] == NULL) { 1268 int j; 1269 1270 for (j = 0; j < i; j++) { 1271 free(g_script.cmdline[j]); 1272 g_script.cmdline[j] = NULL; 1273 } 1274 unload_bs(cli_context, "ERROR: unable to alloc memory.\n", 0); 1275 } 1276 snprintf(g_script.cmdline[i], BUFSIZE, "%s", "-X"); 1277 g_script.max_index = i; 1278 } 1279 1280 /* 1281 * Provides for a shell interface as opposed to one shot command line. 1282 */ 1283 static bool 1284 cli_shell(void *arg1, void *arg2) 1285 { 1286 struct cli_context_t *cli_context = arg1; 1287 char *line = NULL; 1288 ssize_t buf_size = 0; 1289 ssize_t bytes_in = 0; 1290 ssize_t tok_len = 0; 1291 char *tok = NULL; 1292 bool cmd_chosen = false; 1293 int start_idx = cli_context->argc; 1294 int i; 1295 1296 printf("blob> "); 1297 bytes_in = getline(&line, &buf_size, stdin); 1298 1299 /* If getline() failed (EOF), exit the shell. */ 1300 if (bytes_in < 0) { 1301 free(line); 1302 cli_context->action = CLI_SHELL_EXIT; 1303 return true; 1304 } 1305 1306 /* parse input and update cli_context so we can use common option parser */ 1307 if (bytes_in > 0) { 1308 tok = strtok(line, " "); 1309 } 1310 while ((tok != NULL) && (cli_context->argc < MAX_ARGS)) { 1311 cli_context->argv[cli_context->argc] = strdup(tok); 1312 tok_len = strlen(tok); 1313 cli_context->argc++; 1314 tok = strtok(NULL, " "); 1315 } 1316 1317 /* replace newline on last arg with null */ 1318 if (tok_len) { 1319 spdk_str_chomp(cli_context->argv[cli_context->argc - 1]); 1320 } 1321 1322 /* call parse cmd line with user input as args */ 1323 cmd_chosen = cmd_parser(cli_context->argc, &cli_context->argv[0], cli_context); 1324 1325 /* free strdup mem & reset arg count for next shell interaction */ 1326 for (i = start_idx; i < cli_context->argc; i++) { 1327 free(cli_context->argv[i]); 1328 cli_context->argv[i] = NULL; 1329 } 1330 cli_context->argc = 1; 1331 1332 free(line); 1333 1334 return cmd_chosen; 1335 } 1336 1337 /* 1338 * This is the function we pass into the SPDK framework that gets 1339 * called first. 1340 */ 1341 static void 1342 cli_start(void *arg1, void *arg2) 1343 { 1344 struct cli_context_t *cli_context = arg1; 1345 1346 /* 1347 * If we're in script mode, we already have a list of commands so 1348 * just need to pull them out one at a time and process them. 1349 */ 1350 if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 1351 while (line_parser(cli_context) == false); 1352 } 1353 1354 /* 1355 * The initial cmd line options are parsed once before this function is 1356 * called so if there is no action, we're in shell mode and will loop 1357 * here until a a valid option is parsed and returned. 1358 */ 1359 if (cli_context->action == CLI_NONE) { 1360 while (cli_shell(cli_context, NULL) == false); 1361 } 1362 1363 /* Decide what to do next based on cmd line parsing. */ 1364 switch (cli_context->action) { 1365 case CLI_SET_SUPER: 1366 case CLI_SHOW_BS: 1367 case CLI_CREATE_BLOB: 1368 case CLI_SET_XATTR: 1369 case CLI_REM_XATTR: 1370 case CLI_SHOW_BLOB: 1371 case CLI_LIST_BLOBS: 1372 case CLI_DUMP_BLOB: 1373 case CLI_IMPORT_BLOB: 1374 case CLI_FILL: 1375 load_bs(cli_context); 1376 break; 1377 case CLI_INIT_BS: 1378 init_bs(cli_context); 1379 break; 1380 case CLI_LIST_BDEVS: 1381 list_bdevs(cli_context); 1382 break; 1383 case CLI_SHELL_EXIT: 1384 /* 1385 * Because shell mode reuses cmd mode functions, the blobstore 1386 * is loaded/unloaded with every action so we just need to 1387 * stop the framework. For this app there's no need to optimize 1388 * and keep the blobstore open while the app is in shell mode. 1389 */ 1390 spdk_app_stop(0); 1391 break; 1392 case CLI_HELP: 1393 usage(cli_context, ""); 1394 unload_complete(cli_context, 0); 1395 break; 1396 default: 1397 /* should never get here */ 1398 exit(-1); 1399 break; 1400 } 1401 } 1402 1403 int 1404 main(int argc, char **argv) 1405 { 1406 struct spdk_app_opts opts = {}; 1407 struct cli_context_t *cli_context = NULL; 1408 bool cmd_chosen; 1409 int rc = 0; 1410 1411 if (argc < 2) { 1412 usage(cli_context, "ERROR: Invalid option\n"); 1413 exit(-1); 1414 } 1415 1416 cli_context = calloc(1, sizeof(struct cli_context_t)); 1417 if (cli_context == NULL) { 1418 printf("ERROR: could not allocate context structure\n"); 1419 exit(-1); 1420 } 1421 1422 /* default to CMD mode until we've parsed the first parms */ 1423 cli_context->cli_mode = CLI_MODE_CMD; 1424 cli_context->argv[0] = strdup(argv[0]); 1425 cli_context->argc = 1; 1426 1427 /* parse command line */ 1428 cmd_chosen = cmd_parser(argc, argv, cli_context); 1429 free(cli_context->argv[0]); 1430 cli_context->argv[0] = NULL; 1431 if (cmd_chosen == false) { 1432 cli_cleanup(cli_context); 1433 exit(-1); 1434 } 1435 1436 /* after displaying help, just exit */ 1437 if (cli_context->action == CLI_HELP) { 1438 usage(cli_context, ""); 1439 cli_cleanup(cli_context); 1440 exit(-1); 1441 } 1442 1443 /* if they don't supply a conf name, use the default */ 1444 if (!cli_context->config_file) { 1445 cli_context->config_file = program_conf; 1446 } 1447 1448 /* if the config file doesn't exist, tell them how to make one */ 1449 if (access(cli_context->config_file, F_OK) == -1) { 1450 printf("Error: No config file found.\n"); 1451 printf("To create a config file named 'blobcli.conf' for your NVMe device:\n"); 1452 printf(" <path to spdk>/scripts/gen_nvme.sh > blobcli.conf\n"); 1453 printf("and then re-run the cli tool.\n"); 1454 exit(-1); 1455 } 1456 1457 /* 1458 * For script mode we keep a bunch of stuff in a global since 1459 * none if it is passed back and forth to SPDK. 1460 */ 1461 if (cli_context->cli_mode == CLI_MODE_SCRIPT) { 1462 /* 1463 * Now we'll build up the global which will direct this run of the app 1464 * as it will have a list (g_script) of all of the commands line by 1465 * line as if they were typed in on the shell at cmd line. 1466 */ 1467 parse_script(cli_context); 1468 } 1469 1470 /* Set default values in opts struct along with name and conf file. */ 1471 spdk_app_opts_init(&opts); 1472 opts.name = "blobcli"; 1473 opts.config_file = cli_context->config_file; 1474 1475 cli_context->app_started = true; 1476 rc = spdk_app_start(&opts, cli_start, cli_context, NULL); 1477 if (rc) { 1478 printf("ERROR!\n"); 1479 } 1480 1481 /* Free up memory that we allocated */ 1482 cli_cleanup(cli_context); 1483 1484 /* Gracefully close out all of the SPDK subsystems. */ 1485 spdk_app_fini(); 1486 return rc; 1487 } 1488