terom@35: #include terom@35: #include terom@35: #include terom@35: #include terom@37: #include terom@35: terom@35: #include "cache.h" terom@35: #include "cache_engines.h" terom@35: #include "common.h" terom@35: terom@35: #define LINE_LENGTH 1024 terom@35: #define REQ_COUNT 8 terom@35: terom@35: static struct cache *cache = NULL; terom@35: static struct cache_engine *fs_engine = NULL; terom@35: static struct cache_req_ctx { terom@35: struct cache_req *req; terom@35: int index; terom@35: terom@35: int pipe_read; terom@35: int pipe_write; terom@35: terom@35: } req_list[REQ_COUNT]; terom@35: static int quit = 0; terom@35: terom@35: void usage (char *argv_0) { terom@35: err_exit("Usage: %s [-h] ", argv_0); terom@35: } terom@35: terom@35: const char *cache_state_str (enum cache_req_state state) { terom@35: switch (state) { terom@35: case CACHE_STATE_INVALID: return "INVALID"; terom@36: terom@35: case CACHE_STATE_LOOKUP: return "LOOKUP"; terom@36: case CACHE_STATE_HIT: return "HIT"; terom@36: case CACHE_STATE_MISS: return "MISS"; terom@36: terom@36: case CACHE_STATE_OPEN_READ: return "OPEN_READ"; terom@36: case CACHE_STATE_READ: return "READ"; terom@36: terom@36: case CACHE_STATE_OPEN_WRITE: return "OPEN_WRITE"; terom@35: case CACHE_STATE_WRITE: return "WRITE"; terom@35: case CACHE_STATE_WRITE_PAUSE: return "WRITE_PAUSE"; terom@36: terom@35: case CACHE_STATE_ERROR: return "ERROR"; terom@35: default: return "???"; terom@35: } terom@35: } terom@35: /* terom@35: const char *cache_event_str (enum cache_req_event event) { terom@35: switch (event) { terom@35: case CACHE_EVENT_HIT: return "HIT"; terom@35: case CACHE_EVENT_MISS: return "MISS"; terom@35: case CACHE_EVENT_BEGIN_WRITE: return "BEGIN_WRITE"; terom@35: case CACHE_EVENT_BEGIN_READ: return "BEGIN_READ"; terom@35: case CACHE_EVENT_PAUSE_WRITE: return "PAUSE_WRITE"; terom@35: case CACHE_EVENT_RESUME_WRITE: return "RESUME_WRITE"; terom@35: case CACHE_EVENT_DATA_AVAILABLE:return "DATA_AVAILABLE"; terom@35: case CACHE_EVENT_DONE: return "DONE"; terom@35: case CACHE_EVENT_ERROR: return "ERROR"; terom@35: default: return "???"; terom@35: } terom@35: } terom@35: */ terom@35: terom@35: //int cache_cb (struct cache_req *req, enum cache_req_event event, void *arg) { terom@35: int cache_cb (struct cache_req *req, void *arg) { terom@35: struct cache_req_ctx *ctx = arg; terom@35: terom@35: const struct cache_key *key = cache_req_key(ctx->req); terom@35: terom@35: INFO("Request %d (%*s) event: state = %s", terom@35: ctx->index, terom@35: (int) key->length, key->buf, terom@35: cache_state_str(cache_req_state(req)) terom@35: ); terom@35: terom@35: return 0; terom@35: } terom@35: terom@35: void cmd_help (int index, char *cmd_name); terom@35: void cmd_quit (int index, char *cmd_name); terom@35: void cmd_req (int index, char *key_buf); terom@35: void cmd_status (int index, char *key); terom@35: void cmd_available (int index, char *unused); terom@37: void cmd_read (int index, char *unused); terom@35: void cmd_write (int index, char *unused); terom@35: void cmd_push (int index, char *data); terom@37: void cmd_pull (int index, char *unused); terom@35: void cmd_done (int index, char *unused); terom@35: void cmd_release (int index, char *unused); terom@35: terom@35: struct cmd { terom@35: char *name; terom@35: void (*func)(int index, char *str); terom@35: char *usage; terom@35: terom@35: } _commands[] = { terom@35: { "quit", &cmd_quit, "quit" }, terom@35: { "help", &cmd_help, "help [ ]" }, terom@35: { "req", &cmd_req, "req " }, terom@35: { "status", &cmd_status, "status " }, terom@35: { "available",&cmd_available, "available " }, terom@37: { "read", &cmd_read, "read " }, terom@35: { "write", &cmd_write, "write []" }, terom@35: { "push", &cmd_push, "push " }, terom@37: { "pull", &cmd_pull, "pull " }, terom@35: { "done", &cmd_done, "done " }, terom@35: { "release", &cmd_release, "release " }, terom@35: { NULL, NULL, NULL } terom@35: }; terom@35: terom@35: terom@35: void cmd_help (int index, char *cmd_name) { terom@35: struct cmd *cmd; terom@35: terom@35: if (cmd_name == NULL) { terom@35: printf("Available commands: "); terom@35: terom@35: for (cmd = _commands; cmd->func != NULL; cmd++) { terom@35: printf("%s ", cmd->name); terom@35: } terom@35: terom@35: printf("\n"); terom@35: terom@35: } else { terom@35: for (cmd = _commands; cmd->func != NULL; cmd++) { terom@35: if (strcmp(cmd->name, cmd_name) == 0) terom@35: break; terom@35: } terom@35: terom@35: if (cmd->func) { terom@35: INFO("%s", cmd->usage); terom@35: terom@35: } else { terom@35: ERROR("Unknown command: '%s'", cmd_name); terom@35: } terom@35: } terom@35: terom@35: error: terom@35: return; terom@35: } terom@35: terom@35: void cmd_quit (int index, char *cmd_name) { terom@35: quit = 1; terom@35: } terom@35: terom@35: void cmd_req (int index, char *key_buf) { terom@35: struct cache_key key; terom@35: terom@35: if (index < 0 || index >= REQ_COUNT) terom@35: ERROR("index is out of range"); terom@35: terom@35: if (!key_buf) terom@35: ERROR("No key given"); terom@35: terom@35: key.buf = key_buf; terom@35: key.length = 0; terom@35: terom@35: if ((req_list[index].req = cache_req(cache, &key, &cache_cb, &req_list[index])) == NULL) terom@35: ERROR("req_list failed"); terom@35: terom@35: INFO("Request opened: %s", cache_state_str(cache_req_state(req_list[index].req))); terom@35: terom@35: error: terom@35: return; terom@35: } terom@35: terom@35: void cmd_status (int index, char *unused) { terom@35: const struct cache_key *key; terom@35: terom@35: if (index < 0 || index >= REQ_COUNT) terom@35: ERROR("index is out of range"); terom@35: terom@35: if (unused) terom@35: ERROR("Too many arguments"); terom@35: terom@35: key = cache_req_key(req_list[index].req); terom@35: terom@35: INFO("Request %d (%*s) status: %s", index, (int) key->length, key->buf, cache_state_str(cache_req_state(req_list[index].req))); terom@35: terom@35: error: terom@35: return; terom@35: terom@35: } terom@35: terom@35: void cmd_available (int index, char *unused) { terom@35: const struct cache_key *key; terom@35: size_t size, offset, available; terom@35: terom@35: if (index < 0 || index >= REQ_COUNT) terom@35: ERROR("index is out of range"); terom@35: terom@35: if (unused) terom@35: ERROR("Too many arguments"); terom@35: terom@35: key = cache_req_key(req_list[index].req); terom@35: terom@35: if (cache_req_available(req_list[index].req, &size, &offset, &available)) terom@35: ERROR("cache_req_available failed"); terom@35: terom@35: INFO("Request %d (%*s) available: %zu/%zu, %zu to read", terom@35: index, terom@35: (int) key->length, key->buf, terom@35: offset, size, available terom@35: ); terom@35: terom@35: error: terom@35: return; terom@35: terom@35: } terom@35: terom@37: void cmd_read (int index, char *unused) { terom@37: const struct cache_key *key; terom@37: terom@37: if (index < 0 || index >= REQ_COUNT) terom@37: ERROR("index is out of range"); terom@37: terom@37: key = cache_req_key(req_list[index].req); terom@37: terom@37: INFO("Request %d (%*s): beginning read", terom@37: index, (int) key->length, key->buf terom@37: ); terom@37: terom@37: if (cache_req_begin_read(req_list[index].req)) terom@37: ERROR("cache_req_begin_read failed"); terom@37: terom@37: error: terom@37: return; terom@37: } terom@35: terom@35: void cmd_write (int index, char *hint_str) { terom@35: size_t hint; terom@35: terom@35: const struct cache_key *key; terom@35: terom@35: if (index < 0 || index >= REQ_COUNT) terom@35: ERROR("index is out of range"); terom@35: terom@35: if (hint_str) terom@35: hint = atoi(hint_str); terom@35: else terom@35: hint = 0; terom@35: terom@35: key = cache_req_key(req_list[index].req); terom@35: terom@35: INFO("Request %d (%*s): beginning write", terom@35: index, (int) key->length, key->buf terom@35: ); terom@35: terom@35: if (cache_req_begin_write(req_list[index].req, hint)) terom@35: ERROR("cache_req_begin_write failed"); terom@35: terom@35: error: terom@35: return; terom@35: } terom@35: terom@35: void cmd_push (int index, char *data) { terom@35: size_t data_length, length; terom@35: const struct cache_key *key; terom@35: terom@35: data_length = data ? strlen(data) : 0; terom@35: terom@35: if (data) { terom@35: // write to the pipe terom@35: if ((length = write(req_list[index].pipe_write, data, data_length)) == -1) terom@35: PERROR("write"); terom@35: terom@35: if (length != data_length) terom@35: PWARNING("Only %zu/%zu bytes written to pipe!", length, data_length); terom@35: } terom@35: terom@35: // unknown size terom@35: length = 0; terom@35: terom@35: key = cache_req_key(req_list[index].req); terom@35: terom@35: if (cache_req_push(req_list[index].req, req_list[index].pipe_read, &length)) terom@35: ERROR("cache_req_push failed"); terom@35: terom@35: INFO("Request %d (%*s): pushed %zu/%zu bytes", terom@35: index, (int) key->length, key->buf, terom@35: length, data_length terom@35: ); terom@35: terom@35: error: terom@35: return; terom@35: } terom@35: terom@37: void cmd_pull (int index, char *unused) { terom@37: size_t data_length, length; terom@37: const struct cache_key *key; terom@37: terom@37: char buf[LINE_LENGTH]; terom@37: terom@37: // unknown size terom@37: length = 0; terom@37: terom@37: key = cache_req_key(req_list[index].req); terom@37: terom@37: if (cache_req_pull(req_list[index].req, req_list[index].pipe_write, &length)) terom@37: ERROR("cache_req_pull failed"); terom@37: terom@37: assert(length < LINE_LENGTH); terom@37: terom@37: // read from the pipe terom@37: if ((data_length = read(req_list[index].pipe_read, buf, length)) == -1) terom@37: PERROR("read"); terom@37: terom@37: // terminate terom@37: buf[data_length] = '\0'; terom@37: terom@37: if (length != data_length) terom@37: PWARNING("Only %zu/%zu bytes read from pipe!", data_length, length); terom@37: terom@37: INFO("Request %d (%*s): pulled %zu/%zu bytes: %s", terom@37: index, (int) key->length, key->buf, terom@37: data_length, length, terom@37: buf terom@37: ); terom@37: terom@37: error: terom@37: return; terom@37: } terom@37: terom@35: void cmd_done (int index, char *unused) { terom@35: const struct cache_key *key; terom@35: terom@35: key = cache_req_key(req_list[index].req); terom@35: terom@35: if (cache_req_done(req_list[index].req)) terom@35: ERROR("cache_req_done failed"); terom@35: terom@35: INFO("Request %d (%*s): done", terom@35: index, (int) key->length, key->buf terom@35: ); terom@35: terom@35: error: terom@35: return; terom@35: } terom@35: terom@35: void cmd_release (int index, char *unused) { terom@35: const struct cache_key *key; terom@35: terom@35: key = cache_req_key(req_list[index].req); terom@35: terom@35: cache_req_release(req_list[index].req); terom@35: terom@35: INFO("Request %d (%*s): released", terom@35: index, (int) key->length, key->buf terom@35: ); terom@35: terom@35: req_list[index].req = NULL; terom@35: } terom@35: terom@35: int main (int argc, char **argv) { terom@35: int pipefds[2]; terom@35: char line[LINE_LENGTH]; terom@35: terom@35: // init req_list terom@35: for (int index = 0; index < REQ_COUNT; index++) { terom@35: req_list[index].index = index; terom@35: terom@35: if (pipe(pipefds)) terom@35: PFATAL("pipe"); terom@35: terom@35: req_list[index].pipe_read = pipefds[0]; terom@35: req_list[index].pipe_write = pipefds[1]; terom@35: } terom@35: terom@35: // parse arguments terom@35: int opt; terom@35: char *cache_dir; terom@35: terom@35: while ((opt = getopt(argc, argv, "h")) != -1) { terom@35: switch (opt) { terom@35: terom@35: case 'h': terom@35: default: terom@35: usage(argv[0]); terom@35: } terom@35: } terom@35: terom@35: // the cache_dir argument terom@35: if (optind >= argc) terom@35: usage(argv[0]); terom@35: terom@35: cache_dir = argv[optind]; terom@35: terom@35: // create the fs_engine terom@35: if ((fs_engine = cache_engine_fs(cache_dir)) == NULL) terom@35: FATAL("failed to initialize fs cache engine"); terom@35: terom@35: // create the cache terom@35: if ((cache = cache_open(fs_engine)) == NULL) terom@35: FATAL("failed to open the cache"); terom@35: terom@35: INFO("Filesystem cache opened at '%s'", cache_dir); terom@35: terom@35: // enter the interactive loop terom@35: do { terom@35: printf(" > "); terom@35: terom@35: terom@35: if (fgets(line, LINE_LENGTH, stdin) == NULL) terom@35: quit = 1; terom@35: else { terom@35: char *line_ptr = line; terom@35: char *cmd_name = NULL, *str = NULL, *nl = NULL; terom@35: int index = 0; terom@35: terom@35: // strip the newline terom@35: if ((nl = strchr(line, '\n')) != NULL) terom@35: *nl = '\0'; terom@35: terom@35: // parse the command/args terom@35: if (line_ptr) terom@35: cmd_name = strsep(&line_ptr, " "); terom@35: terom@35: if (strlen(cmd_name) == 0) terom@35: cmd_name = NULL; terom@35: terom@35: if (line_ptr) terom@35: index = atoi(strsep(&line_ptr, " ")); terom@35: terom@35: if (line_ptr) terom@35: str = strsep(&line_ptr, " "); terom@35: terom@35: if (!cmd_name) { terom@35: continue; terom@35: } terom@35: terom@35: struct cmd *cmd; terom@35: terom@35: for (cmd = _commands; cmd->func != NULL; cmd++) { terom@35: if (strcmp(cmd->name, cmd_name) == 0) terom@35: break; terom@35: } terom@35: terom@35: if (!cmd->func) { terom@35: INFO("Unknown command"); terom@35: terom@35: continue; terom@35: } terom@35: terom@35: cmd->func(index, str); terom@35: } terom@35: terom@35: printf("\n"); terom@35: terom@35: } while (!quit); terom@35: terom@35: return 0; terom@35: } terom@35: