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