diff --git a/osd-initiator/drivelist.c b/osd-initiator/drivelist.c index f9865a3..583ea44 100644 --- a/osd-initiator/drivelist.c +++ b/osd-initiator/drivelist.c @@ -152,7 +152,7 @@ int osd_get_drive_list(struct osd_drive_description **drives, int *num_drives) ++count; } - /* grab the hostname from the fs.conf file */ + /* For PVFS - grab the hostname from the fs.conf file */ int i=0, j; int condition=1; char *string[200]; @@ -165,7 +165,7 @@ int osd_get_drive_list(struct osd_drive_description **drives, int *num_drives) file = fopen(conf_path, "r"); line = (char *) malloc(120); - while(condition != EOF) + while(file && condition != EOF) { condition = fscanf(file, "%s", line); if(condition != EOF) @@ -183,7 +183,7 @@ int osd_get_drive_list(struct osd_drive_description **drives, int *num_drives) } } - fclose(file); + if (file) fclose(file); out: if (toplevel) diff --git a/osd-initiator/sync.c b/osd-initiator/sync.c index bc73e66..11ac9f3 100644 --- a/osd-initiator/sync.c +++ b/osd-initiator/sync.c @@ -44,6 +44,7 @@ static int check_response(int ret, struct osd_command *command, command->status, command->sense_len, command->inlen); osd_error("%s ", osd_sense_as_string(command->sense, command->sense_len)); + return 1; } else if (command->inlen > 0) { osd_debug("Successfully performed task. BUF: <<%s>>", buf); @@ -79,7 +80,7 @@ static int submit_command(int fd, struct osd_command *command, ret = osd_submit_and_wait(fd, command); osd_debug("....retrieving response"); } - check_response(ret, &command, buf); + ret = check_response(ret, command, buf); return ret; } @@ -87,7 +88,6 @@ static int submit_command(int fd, struct osd_command *command, int inquiry(int fd) { const int INQUIRY_RSP_LEN = 80; - int ret; uint8_t inquiry_rsp[INQUIRY_RSP_LEN]; struct osd_command command; @@ -140,7 +140,7 @@ int query(int fd, uint64_t pid, uint64_t cid, const uint8_t *query) } int create_osd(int fd, uint64_t pid, uint64_t requested_oid, - uint16_t num_user_objects) + uint16_t num_user_objects, uint64_t *assigned_oid) { struct osd_command command; @@ -154,6 +154,12 @@ int create_osd(int fd, uint64_t pid, uint64_t requested_oid, submit_command(fd, &command, NULL); + if (assigned_oid != NULL) { + osd_command_attr_resolve(&command); + *assigned_oid = get_ntohll(command.attr[0].val); + osd_command_attr_free(&command); + } + return 0; } @@ -248,9 +254,17 @@ int remove_member_objects(int fd, uint64_t pid, uint64_t cid) } int create_and_write_osd(int fd, uint64_t pid, uint64_t requested_oid, - const uint8_t *buf, uint64_t len, uint64_t offset) + const uint8_t *buf, uint64_t len, uint64_t offset, + uint64_t *assigned_oid) { struct osd_command command; + struct attribute_list attr = { + .type = ATTR_GET, + .page = CUR_CMD_ATTR_PG, + .number = CCAP_OID, + .len = 8, + }; + int ret; osd_debug("****** CREATE / WRITE ******"); osd_debug("PID: %llu OID: %llu BUF: %s", llu(pid), llu(requested_oid), @@ -269,9 +283,18 @@ int create_and_write_osd(int fd, uint64_t pid, uint64_t requested_oid, command.outdata = buf; command.outlen = len; - submit_command(fd, &command, NULL); + if (assigned_oid != NULL) + osd_command_attr_build(&command, &attr, 1); + + ret = submit_command(fd, &command, NULL); - return 0; + if (ret == 0 && assigned_oid != NULL) { + osd_command_attr_resolve(&command); + *assigned_oid = get_ntohll(command.attr[0].val); + osd_command_attr_free(&command); + } + + return ret; } int create_and_write_sgl_osd(int fd, uint64_t pid, uint64_t requested_oid, @@ -559,9 +582,7 @@ int read_osd(int fd, uint64_t pid, uint64_t oid, uint8_t *buf, uint64_t len, command.indata = buf; command.inlen_alloc = len; - submit_command(fd, &command, buf); - - return 0; + return submit_command(fd, &command, buf); } int read_sgl_osd(int fd, uint64_t pid, uint64_t oid, uint8_t *ddt_buf, diff --git a/osd-initiator/sync.h b/osd-initiator/sync.h index 68be07f..19d58aa 100644 --- a/osd-initiator/sync.h +++ b/osd-initiator/sync.h @@ -27,11 +27,12 @@ int inquiry(int fd); int query(int fd, uint64_t pid, uint64_t cid, const uint8_t *query); /* Create */ int create_osd(int fd, uint64_t pid, uint64_t requested_oid, - uint16_t num_user_objects); + uint16_t num_user_objects, uint64_t *assigned_oid); int create_partition(int fd, uint64_t requested_pid); int create_collection(int fd, uint64_t pid, uint64_t requested_cid); int create_and_write_osd(int fd, uint64_t pid, uint64_t requested_oid, - const uint8_t *buf, uint64_t len, uint64_t offset); + const uint8_t *buf, uint64_t len, uint64_t offset, + uint64_t *assigned_oid); int create_and_write_sgl_osd(int fd, uint64_t pid, uint64_t requested_oid, const uint8_t *buf, uint64_t len, uint64_t offset); int create_and_write_vec_osd(int fd, uint64_t pid, uint64_t requested_oid, diff --git a/osd-initiator/tests/Makefile b/osd-initiator/tests/Makefile index 84ced0a..e03ec89 100644 --- a/osd-initiator/tests/Makefile +++ b/osd-initiator/tests/Makefile @@ -3,7 +3,7 @@ # PROGSRC := sgio.c generic_iface_test.c iovec.c iospeed.c latency.c atomic.c \ - attr-all.c iospeed-ddt.c + attr-all.c iospeed-ddt.c cli.c PROGSRC += blk-iospeed.c sgl_test.c alignment.c alignment-rdma.c PROGOBJ := $(PROGSRC:.c=.o) PROGEXE := $(PROGSRC:.c=) @@ -41,7 +41,7 @@ CFLAGS += -Dsg_io_v4=sg_io_v4_v2624 all:: $(LIB) $(PROGEXE) $(MPIEXE) $(PROGEXE): %: %.o $(LIB) - $(CC) -o $@ $^ $(LIB) -lm + $(CC) -o $@ $^ $(LIB) -lm -lreadline $(MPIEXE): %: %.o $(LIB) $(MPICC) -o $@ $^ $(MPILIB) $(LIB) -lm diff --git a/osd-initiator/tests/cli.c b/osd-initiator/tests/cli.c new file mode 100644 index 0000000..596fd57 --- /dev/null +++ b/osd-initiator/tests/cli.c @@ -0,0 +1,515 @@ +/* + * cli.c -- Command Line Interface to an OSD + * derived from readline FileMan sample code + * + * Copyright (C) 2014 University of Connecticut. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "osd-util/osd-util.h" +#include "osd-util/osd-defs.h" +#include "command.h" +#include "sync.h" +#include "drivelist.h" + +extern char *getwd (); +extern char *xmalloc (); + +/* The names of functions that actually do the manipulation. */ +int com_cat(char *), com_create(char *), com_get(char *), com_help(char *); +int com_ls(char *), com_part(char *), com_put(char *), com_quit(char *); +int com_rm(char *), com_stat(char *); + +/* A structure which contains information on the commands this program + can understand. */ + +typedef struct { + const char *name; /* User printable name of the function. */ + Function *func; /* Function to call to do the job. */ + const char *usage; /* Usage for this function. */ + const char *doc; /* Documentation for this function. */ +} COMMAND; + +COMMAND commands[] = { + { "cat", com_cat, "oid", "View the contents of object oid" }, + { "create", com_create, "pid", "Create partition" }, + { "get", com_get, "oid filename [size]", "Copy OSD object to local file\n\tDefault size is 1M" }, + { "help", com_help, "", "Documentation for CLI" }, + { "ls", com_ls, "", "List objects in current partition" }, + { "part", com_part, "pid", "Change current partition" }, + { "put", com_put, "filename [oid]", "Copy local file to OSD" }, + { "quit", com_quit, "", "Quit using CLI" }, + { "rm", com_rm, "oid", "Delete object oid" }, + { "stat", com_stat, "oid", "Print out statistics on object oid" }, + { (char *)NULL, (Function *)NULL, (char *)NULL, (char *)NULL } +}; + +/* Forward declarations. */ +char *stripwhite (char *); +COMMAND *find_command (char *); +int execute_line(char *line); +void initialize_readline (void); + +/* The name of this program, as taken from argv[0]. */ +char *progname; + +/* When non-zero, this global means the user is done using this program. */ +int done; + +static char * +dupstr (s) + char *s; +{ + char *r; + + r = xmalloc (strlen (s) + 1); + strcpy (r, s); + return (r); +} + +static int connect_to_osd(void) +{ + int num_drives; + struct osd_drive_description *drives; + int ret = osd_get_drive_list(&drives, &num_drives); + if (ret < 0) { + printf("Could not get OSD drive list"); + return 0; + } + if (num_drives == 0) { + printf("no OSD drives available\n"); + return 0; + } + + int i = 0; + printf("drive %s name %s\n", drives[i].chardev, drives[i].targetname); + int fd = open(drives[i].chardev, O_RDWR); + if (fd < 0) { + printf("Could not open %s", drives[i].chardev); + return 0; + } + osd_free_drive_list(drives, num_drives); + + inquiry(fd); + + return fd; +} + +int osdfd; + +int main (argc, argv) + int argc; + char **argv; +{ + char *line, *s; + + progname = argv[0]; + argc--; + + if ((osdfd = connect_to_osd()) == 0) + return -1; + + initialize_readline (); /* Bind our completer. */ + + /* Loop reading and executing lines until the user quits. */ + for ( ; done == 0; ) + { + line = readline ("CLI> "); + + if (!line) + break; + + /* Remove leading and trailing whitespace from the line. + Then, if there is anything left, add it to the history list + and execute it. */ + s = stripwhite (line); + + if (*s) + { + add_history (s); + execute_line (s); + } + + free (line); + } + return 0; +} + +/* Execute a command line. */ +int +execute_line (char *line) +{ + register int i; + COMMAND *command; + char *word; + + /* Isolate the command word. */ + i = 0; + while (line[i] && whitespace (line[i])) + i++; + word = line + i; + + while (line[i] && !whitespace (line[i])) + i++; + + if (line[i]) + line[i++] = '\0'; + + command = find_command (word); + + if (!command) + { + fprintf (stderr, "%s: No such command for OSD CLI.\n", word); + return (-1); + } + + /* Get argument to command, if any. */ + while (whitespace (line[i])) + i++; + + word = line + i; + + /* Call the function. */ + return ((*(command->func)) (word)); +} + +/* Look up NAME as the name of a command, and return a pointer to that + command. Return a NULL pointer if NAME isn't a command name. */ +COMMAND * +find_command (name) + char *name; +{ + register int i; + + for (i = 0; commands[i].name; i++) + if (strcmp (name, commands[i].name) == 0) + return (&commands[i]); + + return ((COMMAND *)NULL); +} + +/* Strip whitespace from the start and end of STRING. Return a pointer + into STRING. */ +char * +stripwhite (string) + char *string; +{ + register char *s, *t; + + for (s = string; whitespace (*s); s++) + ; + + if (*s == 0) + return (s); + + t = s + strlen (s) - 1; + while (t > s && whitespace (*t)) + t--; + *++t = '\0'; + + return s; +} + +/* **************************************************************** */ +/* */ +/* Interface to Readline Completion */ +/* */ +/* **************************************************************** */ + +char *command_generator (char *, int); +char **cli_completion (char *, int, int); + +/* Tell the GNU Readline library how to complete. We want to try to complete + on command names if this is the first word in the line, or on filenames + if not. */ +void initialize_readline () +{ + /* Allow conditional parsing of the ~/.inputrc file. */ + rl_readline_name = "CLI"; + + /* Tell the completer that we want a crack first. */ + rl_attempted_completion_function = (CPPFunction *)cli_completion; +} + +/* Attempt to complete on the contents of TEXT. START and END bound the + region of rl_line_buffer that contains the word to complete. TEXT is + the word to complete. We can use the entire contents of rl_line_buffer + in case we want to do some simple parsing. Return the array of matches, + or NULL if there aren't any. */ +char ** +cli_completion (text, start, end) + char *text; + int start, end; +{ + char **matches; + + matches = (char **)NULL; + + /* If this word is at the start of the line, then it is a command + to complete. Otherwise it is the name of a file in the current + directory. */ + if (start == 0) + matches = completion_matches (text, command_generator); + + return (matches); +} + +/* Generator function for command completion. STATE lets us know whether + to start from scratch; without any state (i.e. STATE == 0), then we + start at the top of the list. */ +char * +command_generator (text, state) + char *text; + int state; +{ + static int list_index, len; + const char *name; + + /* If this is a new word to complete, initialize now. This includes + saving the length of TEXT for efficiency, and initializing the index + variable to 0. */ + if (!state) + { + list_index = 0; + len = strlen (text); + } + + /* Return the next name which partially matches from the command list. */ + while ((name = commands[list_index].name)) + { + list_index++; + + if (strncmp (name, text, len) == 0) + return (dupstr(name)); + } + + /* If no names matched, then return NULL. */ + return ((char *)NULL); +} + +/* Return non-zero if ARG is a valid argument for CALLER, else print + an error message and return zero. */ +static int +valid_argument (caller, arg) + char *caller, *arg; +{ + if (!arg || !*arg) + { + fprintf (stderr, "%s: Argument required.\n", caller); + return (0); + } + + return (1); +} + +/* Function which tells you that you can't do this. */ +static void unimplemented (const char *cmd) +{ + fprintf (stderr, "%s command is unimplemented.\n", cmd); +} + +/* **************************************************************** */ +/* */ +/* OSD CLI Commands */ +/* */ +/* **************************************************************** */ + +static uint64_t current_pid = PARTITION_PID_INVALID; + +/* List the object(s) named in arg. */ +int com_ls (arg) + char *arg; +{ + unimplemented ("ls"); + return 1; +} + +int com_cat (arg) + char *arg; +{ + unimplemented ("cat"); + return 1; +} + +int com_get (arg) + char *arg; +{ + char *arg1, *arg2, *arg3; + for(;*arg==' ';arg++); + arg1 = arg; + for(;*arg!=' ';arg++); + arg2 = arg; + for(;*arg2==' ';arg2++); + *arg = 0; + arg = arg2; + for(;*arg && *arg!=' ';arg++); + arg3 = arg; + for(;*arg3 && *arg3==' ';arg3++); + *arg = 0; + + uint64_t oid; + sscanf(arg1, "%" PRIx64, &oid); + + uint64_t bufsize = 1024*1024; + if (*arg3) + sscanf(arg3, "%" PRId64, &bufsize); + unsigned char *buf = malloc(bufsize); + if (current_pid == PARTITION_PID_INVALID) { + printf("Current partition is not set. Use part command\n"); + return 0; + } + int ret = read_osd(osdfd, current_pid, oid, buf, bufsize, 0); + + int fd = open(arg2, O_CREAT | O_WRONLY | O_TRUNC, 0644); + write(fd, buf, bufsize); + free(buf); + + if (ret == 0) + printf(" Copied object 0x%llx to %s\n", llu(oid), arg2 ); + + return 1; +} + +int com_put (arg) + char *arg; +{ + struct stat stat; + char *arg1, *arg2; + for(;*arg==' ';arg++); + arg1 = arg; + for(;*arg && *arg!=' ';arg++); + arg2 = arg; + for(;*arg2 && *arg2==' ';arg2++); + *arg = 0; + + if (current_pid == PARTITION_PID_INVALID) { + printf("current pid is not set\n"); + return 0; + } + + int fd = open(arg1, O_RDONLY); + fstat(fd, &stat); + + unsigned char *buf = malloc(stat.st_size); + read(fd, buf, stat.st_size); + + uint64_t requested_oid=0, assigned_oid; + if (*arg2) + sscanf(arg2, "%" PRIx64, &requested_oid); + + int ret = create_and_write_osd(osdfd, current_pid, requested_oid, buf, + stat.st_size, 0, &assigned_oid); + if (ret == 0) + printf(" Copied %s to object 0x%llx\n", arg1, llu(assigned_oid)); + + free(buf); + + return 1; +} + +int com_part (arg) + char *arg; +{ + uint64_t pid; + sscanf(arg, "%" PRIx64, &pid); + current_pid = pid; + return (1); +} + +int com_stat (arg) + char *arg; +{ + if (!valid_argument ("stat", arg)) + return (1); + return 0; +} + +int com_rm (arg) + char *arg; +{ + unimplemented ("rm"); + return (1); +} + +int com_create (arg) + char *arg; +{ + uint64_t pid; + sscanf(arg, "%" PRIx64, &pid); + create_partition(osdfd, pid); + current_pid = pid; + return (1); +} + +/* Print out help for ARG, or for all of the commands if ARG is + not present. */ +int com_help (arg) + char *arg; +{ + register int i; + int printed = 0; + + /* set tab at position 24 */ + printf("\r\t\033[0g\t\033[0g\r"); + for (i = 0; commands[i].name; i++) + { + if (!*arg || (strcmp (arg, commands[i].name) == 0)) + { + printf ("\033[1m%s\033[0m %s\t%s.\n", commands[i].name, commands[i].usage, commands[i].doc); + printed++; + } + } + /* reset tabs */ + printf("\r\033[8C\033H\033[8C\033H\r"); + + if (!printed) + { + printf ("No commands match `%s'. Possibilties are:\n", arg); + + for (i = 0; commands[i].name; i++) + { + /* Print in six columns. */ + if (printed == 6) + { + printed = 0; + printf ("\n"); + } + + printf ("%s\t", commands[i].name); + printed++; + } + + if (printed) + printf ("\n"); + } + return (0); +} + +/* The user wishes to quit using this program. Just set DONE non-zero. */ +int com_quit (arg) + char *arg; +{ + done = 1; + return (0); +} diff --git a/osd-initiator/tests/sgio.c b/osd-initiator/tests/sgio.c index 9bfa31e..03a9fc3 100644 --- a/osd-initiator/tests/sgio.c +++ b/osd-initiator/tests/sgio.c @@ -400,9 +400,9 @@ int main(int argc, char *argv[]) #if 1 /* Basic read / write seems to work */ - create_osd(fd, PID, OID, NUM_USER_OBJ); - create_osd(fd, PID, OID+1, NUM_USER_OBJ); - create_osd(fd, PID, OID+2, NUM_USER_OBJ); + create_osd(fd, PID, OID, NUM_USER_OBJ, NULL); + create_osd(fd, PID, OID+1, NUM_USER_OBJ, NULL); + create_osd(fd, PID, OID+2, NUM_USER_OBJ, NULL); remove_osd(fd, PID, OID+1); write_osd(fd, PID, OID, WRITEDATA, sizeof(WRITEDATA), OFFSET); @@ -419,8 +419,8 @@ int main(int argc, char *argv[]) #endif #if 1 /* Testing iovec list. */ - create_osd(fd, PID, OID+3, 1); - create_osd(fd, PID, OID+4, 1); + create_osd(fd, PID, OID+3, 1, NULL); + create_osd(fd, PID, OID+4, 1, NULL); iovec_write_test(fd, PID, OID+3); iovec_read_test(fd, PID, OID+4); @@ -430,28 +430,28 @@ int main(int argc, char *argv[]) #endif #if 1 /* Testing bidirectional operations. */ - create_osd(fd, PID, OID+5, 1); + create_osd(fd, PID, OID+5, 1, NULL); write_osd(fd, PID, OID+5, (const uint8_t *) "sixty-seven", 12, 0); bidi_test(fd, PID, OID+5); remove_osd(fd, PID, OID+5); #endif #if 1 /* Testing attribute API */ - create_osd(fd, PID, OID+6, 1); + create_osd(fd, PID, OID+6, 1, NULL); attr_test(fd, PID, OID+6); remove_osd(fd, PID, OID+6); #endif #if 1 /* Testing all-attribute API (listing attributes in a page */ - create_osd(fd, PID, OID+7, 1); + create_osd(fd, PID, OID+7, 1, NULL); all_attr_test(fd, PID, OID+7); remove_osd(fd, PID, OID+7); #endif #if 1 /* Testing stuff */ - create_osd(fd, PID, OID+10, NUM_USER_OBJ); - create_osd(fd, PID, OID+11, NUM_USER_OBJ); + create_osd(fd, PID, OID+10, NUM_USER_OBJ, NULL); + create_osd(fd, PID, OID+11, NUM_USER_OBJ, NULL); write_osd(fd, PID, OID, WRITEDATA, sizeof(WRITEDATA), OFFSET); read_osd(fd, PID, OID, outbuf, sizeof(WRITEDATA), OFFSET); #endif diff --git a/osd-initiator/tests/sgl_test.c b/osd-initiator/tests/sgl_test.c index 3f3e6ac..a856a84 100644 --- a/osd-initiator/tests/sgl_test.c +++ b/osd-initiator/tests/sgl_test.c @@ -209,7 +209,7 @@ static void basic_test_sgl(int fd, uint64_t pid, uint64_t oid) xbuf = malloc(100); memset(xbuf, 'Y', 100); - ret = create_and_write_osd(fd, pid, oid+1, xbuf, 100, 0); + ret = create_and_write_osd(fd, pid, oid+1, xbuf, 100, 0, NULL); assert(ret == 0); free(xbuf); @@ -368,7 +368,7 @@ static void basic_test_vec(int fd, uint64_t pid, uint64_t oid) xbuf = malloc(100); memset(xbuf, 'Y', 100); - ret = create_and_write_osd(fd, pid, oid+1, xbuf, 100, 0); + ret = create_and_write_osd(fd, pid, oid+1, xbuf, 100, 0, NULL); assert(ret == 0); free(xbuf); diff --git a/osd-util/osd-defs.h b/osd-util/osd-defs.h index a896abe..d0c9826 100644 --- a/osd-util/osd-defs.h +++ b/osd-util/osd-defs.h @@ -96,6 +96,7 @@ #define ROOT_PID 0LLU #define ROOT_OID 0LLU #define PARTITION_PID_LB 0x10000LLU +#define PARTITION_PID_INVALID 0xFFFFLLU #define PARTITION_OID 0x0LLU #define OBJECT_PID_LB 0x10000LLU #define SPONTANEOUS_COLLECTION_PID_LB 0x10000LLU