]> git.proxmox.com Git - ceph.git/blame - ceph/src/spdk/examples/blob/cli/blobcli.c
import 15.2.0 Octopus source
[ceph.git] / ceph / src / spdk / examples / blob / cli / blobcli.c
CommitLineData
11fdf7f2
TL
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"
9f95a23c 52static void cli_start(void *arg1);
11fdf7f2
TL
53
54static const char *program_name = "blobcli";
55/* default name for .conf file, any name can be used however with -c switch */
56static 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 */
64enum cli_mode_type {
65 CLI_MODE_CMD,
66 CLI_MODE_SHELL,
67 CLI_MODE_SCRIPT
68};
69
70enum 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 */
100struct 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
134struct 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};
142struct cli_script_t g_script;
143
144/*
145 * Common printing of commands for CLI and shell modes.
146 */
147static void
148print_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 */
173static void
174usage(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 */
196static void
197cli_cleanup(struct cli_context_t *cli_context)
198{
199 if (cli_context->buff) {
9f95a23c 200 spdk_free(cli_context->buff);
11fdf7f2
TL
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 */
215static void
216unload_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;
9f95a23c 236 cli_start(cli_context);
11fdf7f2
TL
237 }
238}
239
240/*
241 * Unload the blobstore.
242 */
243static void
244unload_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 */
266static void
267close_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 */
282static void
283sync_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
296static void
297resize_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 */
322static void
323open_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 */
341static void
342blob_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 */
368static void
369show_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);
9f95a23c 411 spdk_log_dump(stdout, "\tblobstore type:", &bstype, sizeof(bstype));
11fdf7f2
TL
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 */
429static void
430show_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);
9f95a23c 469 spdk_log_dump(stdout, "", value, value_len);
11fdf7f2
TL
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 */
500static void
501blob_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 */
537static void
538set_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 */
555static void
556set_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 */
582static void
583read_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 */
621static void
622write_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 */
672static void
673dump_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 */
9f95a23c
TL
688 cli_context->buff = spdk_malloc(cli_context->io_unit_size, ALIGN_4K, NULL,
689 SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
11fdf7f2
TL
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 */
744static void
745write_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 */
769static void
770fill_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);
9f95a23c
TL
783 cli_context->buff = spdk_malloc(cli_context->io_unit_size, ALIGN_4K, NULL,
784 SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
11fdf7f2
TL
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 */
804static void
805load_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 */
866static void
867load_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 */
892static void
893list_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;
9f95a23c 915 cli_start(cli_context);
11fdf7f2
TL
916 }
917}
918
919/*
920 * Callback function for initializing a blob.
921 */
922static void
923bs_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 */
942static void
943init_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
967static void
968spdk_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;
9f95a23c 976 cli_start(cli_context);
11fdf7f2
TL
977 }
978}
979
980static void
981bsdump_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 */
1014static void
1015dump_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 */
1041static bool
1042cmd_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;
9f95a23c 1073 cli_context->blobid = spdk_strtoll(optarg, 10);
11fdf7f2
TL
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;
9f95a23c
TL
1083 cli_context->blobid = spdk_strtoll(optarg, 10);
1084 cli_context->fill_value = spdk_strtol(argv[optind], 10);
11fdf7f2
TL
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;
9f95a23c 1116 cli_context->blobid = spdk_strtoll(optarg, 10);
11fdf7f2
TL
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;
9f95a23c 1137 cli_context->blobid = spdk_strtoll(optarg, 10);
11fdf7f2
TL
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':
9f95a23c 1144 cli_context->num_clusters = spdk_strtol(optarg, 10);
11fdf7f2
TL
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;
9f95a23c 1155 cli_context->superid = spdk_strtoll(optarg, 10);
11fdf7f2
TL
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;
9f95a23c 1170 cli_context->blobid = spdk_strtoll(optarg, 10);
11fdf7f2
TL
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;
9f95a23c 1195 cli_context->blobid = spdk_strtoll(optarg, 10);
11fdf7f2
TL
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 */
1234static bool
1235line_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;
9f95a23c 1254 blob_num = spdk_strtol(tok, 10);
11fdf7f2
TL
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 */
1312static void
1313parse_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 */
1368static bool
1369cli_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 */
1426static void
9f95a23c 1427cli_start(void *arg1)
11fdf7f2
TL
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
1491int
1492main(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;
9f95a23c 1564 rc = spdk_app_start(&opts, cli_start, cli_context);
11fdf7f2
TL
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}