diff --git a/include/lib/libzds.h b/include/lib/libzds.h index 5c06669c..91354852 100644 --- a/include/lib/libzds.h +++ b/include/lib/libzds.h @@ -192,7 +192,7 @@ #define MAX_LINE_LENGTH 512 - +#define MAX_SERVER 3 /** * @brief This structure represents the count field in an ECKD record. diff --git a/zdsfs/zdsfs.c b/zdsfs/zdsfs.c index 92069d90..87c1beae 100644 --- a/zdsfs/zdsfs.c +++ b/zdsfs/zdsfs.c @@ -21,6 +21,9 @@ #include #include #include +#include +#include +#include #ifdef HAVE_SETXATTR #include @@ -36,6 +39,8 @@ /* defaults for file and directory permissions (octal) */ #define DEF_FILE_PERM 0440 #define DEF_DIR_PERM 0550 +/* default timer interval 9 minutes, enq times out after 10 minutes */ +#define DEFAULT_KEEPALIVE_SEC 540 struct zdsfs_info { int devcount; @@ -50,11 +55,24 @@ struct zdsfs_info { size_t metasize; /* total size of meta data buffer */ size_t metaused; /* how many bytes of buffer are already filled */ time_t metatime; /* when did we create this meta data */ + char *configfile; + int restapi; + unsigned int nr_server; + int active_server; + char *server[MAX_SERVER]; + long keepalive; }; static struct zdsfs_info zdsfsinfo; static int zdsfs_create_meta_data_buffer(struct zdsfs_info *); static int zdsfs_verify_datasets(void); +static struct util_list *open_dsh; +static int timer_running; + +struct dsh_node { + struct util_list_node node; + struct dshandle *dsh; +}; struct zdsfs_file_info { struct dshandle *dsh; @@ -64,6 +82,67 @@ struct zdsfs_file_info { size_t metaread; /* how many bytes have already been read */ }; +/* Allocate and initialize a new list of struct dsh_node. */ +static struct util_list *dshlist_alloc(void) +{ + struct util_list *list; + + list = util_malloc(sizeof(struct util_list)); + util_list_init(list, struct dsh_node, node); + + return list; +} + +/* free list of struct dsh_node. */ +static void dshlist_free(struct util_list *list) +{ + struct dsh_node *s, *n; + + if (!list) + return; + + util_list_iterate_safe(list, s, n) { + util_list_remove(list, s); + free(s); + } + + free(list); +} + +/* add dsh to list */ +static void dshlist_add(struct util_list *list, struct dshandle *dsh) +{ + struct dsh_node *s; + + s = util_malloc(sizeof(struct dsh_node)); + s->dsh = dsh; + util_list_add_tail(list, s); +} + +/* Find a dsh_node. */ +static struct dsh_node *dshlist_find(struct util_list *list, struct dshandle *dsh) +{ + struct dsh_node *s; + + util_list_iterate(list, s) { + if (s->dsh == dsh) + return s; + } + + return NULL; +} + +/* Remove a dsh_node from the list. */ +void dshlist_remove(struct util_list *list, struct dshandle *dsh) +{ + struct dsh_node *p; + + p = dshlist_find(list, dsh); + if (p) { + util_list_remove(list, p); + free(p); + } +} /* normalize the given path name to a dataset name @@ -97,7 +176,47 @@ static void path_to_member_name(const char *path, char *normds, size_t size) } } +static void setup_timer(long sec) +{ + static struct itimerval timer; + memset(&timer, 0, sizeof(struct itimerval)); + timer.it_value.tv_sec = sec; + setitimer(ITIMER_REAL, &timer, NULL); +} + +static void keep_alive(int UNUSED(signal)) +{ + struct dsh_node *s; + + if (!open_dsh) { + timer_running = 0; + setup_timer(0); + return; + } + + util_list_iterate(open_dsh, s) { + lzds_rest_ping(s->dsh, + zdsfsinfo.server[zdsfsinfo.active_server]); + } + timer_running = 1; + setup_timer(zdsfsinfo.keepalive); +} + +void keepalive_start(void) +{ + struct sigaction act; + + if (!open_dsh || timer_running) + return; + + timer_running = 1; + /* Setup timer for periodic ping. */ + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = &keep_alive; + sigaction(SIGALRM, &act, NULL); + setup_timer(zdsfsinfo.keepalive); +} static int zdsfs_getattr(const char *path, struct stat *stbuf) { @@ -375,6 +494,43 @@ static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, return 0; } +/* + * walk through the serverlist and check if the URLs start with http or https + * if not attach a https:// prefix + * also check if they end with / and if not attach it + * + * afterwards ping the z/OSMF server and use the first working one + * + * return 1 if working server found 0 otherwise + */ +static int zdsfs_test_restserver(void) +{ + unsigned int i; + char *server; + char *prefix; + + for (i = 0; i < zdsfsinfo.nr_server; i++) { + server = zdsfsinfo.server[i]; + if (strncmp(server, "http", 4)) { + prefix = util_strdup("https://"); + server = util_strcat_realloc(prefix, server); + free(zdsfsinfo.server[i]); + zdsfsinfo.server[i] = server; + } + if (strncmp(server + strlen(server) - 1, "/", 1)) { + server = util_strcat_realloc(server, "/"); + zdsfsinfo.server[i] = server; + } + if (lzds_rest_ping(NULL, zdsfsinfo.server[i])) { + zdsfsinfo.active_server = i; + fprintf(stdout, "Using z/OSMF REST services on %s\n", + zdsfsinfo.server[i]); + return 1; + } + } + return 0; +} + static int zdsfs_open(const char *path, struct fuse_file_info *fi) { @@ -464,13 +620,32 @@ static int zdsfs_open(const char *path, struct fuse_file_info *fi) rc = -rc; goto error2; } + +retry: + if (zdsfsinfo.restapi && zdsfsinfo.active_server >= 0) { + rc = lzds_rest_get_enq(dsh, + zdsfsinfo.server[zdsfsinfo.active_server]); + /* if the REST server is not responding try the other */ + if (rc == ECONNREFUSED && zdsfs_test_restserver()) { + goto retry; + } else if (rc) { + lzds_dshandle_get_errorlog(dsh, &log); + lzds_errorlog_fprint(log, stderr); + rc = -rc; + goto error2; + } else { + dshlist_add(open_dsh, dsh); + /* add to open dsh list */ + keepalive_start(); + } + } rc = lzds_dshandle_open(dsh); if (rc) { fprintf(stderr, "Error when opening data set:\n"); lzds_dshandle_get_errorlog(dsh, &log); lzds_errorlog_fprint(log, stderr); rc = -rc; - goto error2; + goto error3; } zfi->is_metadata_file = 0; zfi->metaread = 0; @@ -478,6 +653,8 @@ static int zdsfs_open(const char *path, struct fuse_file_info *fi) fi->fh = (uint64_t)(unsigned long)zfi; return 0; +error3: + dshlist_remove(open_dsh, dsh); error2: lzds_dshandle_free(dsh); error1: @@ -495,7 +672,10 @@ static int zdsfs_release(const char *UNUSED(path), struct fuse_file_info *fi) return -EINVAL; zfi = (struct zdsfs_file_info *)(unsigned long)fi->fh; if (zfi->dsh) { + lzds_rest_release_enq(zfi->dsh, + zdsfsinfo.server[zdsfsinfo.active_server]); lzds_dshandle_close(zfi->dsh); + dshlist_remove(open_dsh, zfi->dsh); lzds_dshandle_free(zfi->dsh); } rc = pthread_mutex_destroy(&zfi->mutex); @@ -831,6 +1011,8 @@ enum { KEY_DEVFILE, KEY_TRACKS, KEY_SEEKBUFFER, + KEY_CONFIG, + KEY_SERVER, }; #define ZDSFS_OPT(t, p, v) { t, offsetof(struct zdsfs_info, p), v } @@ -843,9 +1025,12 @@ static const struct fuse_opt zdsfs_opts[] = { FUSE_OPT_KEY("-l %s", KEY_DEVFILE), FUSE_OPT_KEY("tracks=", KEY_TRACKS), FUSE_OPT_KEY("seekbuffer=", KEY_SEEKBUFFER), + FUSE_OPT_KEY("-c %s", KEY_CONFIG), + FUSE_OPT_KEY("restserver=", KEY_SERVER), ZDSFS_OPT("rdw", keepRDW, 1), ZDSFS_OPT("ignore_incomplete", allow_inclomplete_multi_volume, 1), ZDSFS_OPT("check_host_count", host_count, 1), + ZDSFS_OPT("restapi", restapi, 1), FUSE_OPT_END }; @@ -865,6 +1050,8 @@ static void usage(const char *progname) "ZDSFS options:\n" " -l list_file Text file that contains a list of DASD device" " nodes\n" +" -c config_file Text file that contains configuration options\n" +" for zdsfs\n" " -o rdw Keep record descriptor words in byte stream\n" " -o ignore_incomplete Continue processing even if parts of a multi" " volume\n" @@ -874,6 +1061,10 @@ static void usage(const char *progname) " size (default 1048576)\n" " -o check_host_count Stop processing if the device is used by another\n" " operating system instance\n" +" -o restapi Enable using z/OSMF REST services for coordinated\n" +" access to datasets\n" +" -o restserver=URL The URL of the z/OSMF REST server to be used for\n" +" coordinated access to datasets\n" , progname); } @@ -973,6 +1164,67 @@ static void zdsfs_process_device_file(const char *devfile) free(buffer); } +void remove_whitespace(const char *s, char *t) +{ + while (*s != '\0') { + if (!isspace(*s)) { + *t = *s; + t++; + } + s++; + } + *t = '\0'; +} + + +static void zdsfs_process_config_file(const char *config) +{ + char line[MAX_LINE_LENGTH]; + char *tmp, *key, *value; + FILE *fd; + char delimiter[] = " =#\n"; + unsigned long enabled; + + fd = fopen(config, "r"); + if (!fd) { + fprintf(stderr, "could not open file %s: %s\n", + config, strerror(errno)); + return; + } + + while (fgets(line, sizeof(line), fd)) { + /* skip empty lines */ + if (*line == '\n' || *line == '#') + continue; + + /* remove all whitespaces */ + tmp = util_malloc(strlen(line)); + remove_whitespace(line, tmp); + + key = strtok(tmp, delimiter); + if (strcmp(key, "restserver") == 0) { + if (zdsfsinfo.nr_server >= MAX_SERVER) { + free(tmp); + continue; + } + value = strtok(NULL, delimiter); + zdsfsinfo.server[zdsfsinfo.nr_server] = + util_strdup(value); + zdsfsinfo.nr_server++; + } else if (strcmp(key, "restapi") == 0) { + value = strtok(NULL, delimiter); + enabled = strtoul(value, NULL, 0); + if (enabled == 1) + zdsfsinfo.restapi = true; + } else if (strcmp(key, "keepalive") == 0) { + value = strtok(NULL, delimiter); + zdsfsinfo.keepalive = strtoul(value, NULL, 0); + } + free(tmp); + } + fclose(fd); +} + static int zdsfs_process_args(void *UNUSED(data), const char *arg, int key, struct fuse_args *outargs) { @@ -1057,6 +1309,18 @@ static int zdsfs_process_args(void *UNUSED(data), const char *arg, int key, ", program version %s\n", RELEASE_STRING); fprintf(stdout, "Copyright IBM Corp. 2013, 2017\n"); exit(0); + case KEY_CONFIG: + /* note that arg starts with "-c" */ + zdsfsinfo.configfile = util_strdup(arg + 2); + return 0; + case KEY_SERVER: + if (zdsfsinfo.nr_server >= MAX_SERVER) + return 0; + value = arg + strlen("restserver="); + zdsfsinfo.server[zdsfsinfo.nr_server] = + util_strdup(value); + zdsfsinfo.nr_server++; + return 0; default: fprintf(stderr, "Unknown argument key %x\n", key); exit(1); @@ -1069,13 +1333,18 @@ int main(int argc, char *argv[]) struct fuse_args args = FUSE_ARGS_INIT(argc, argv); int rc; + timer_running = 0; bzero(&zdsfsinfo, sizeof(zdsfsinfo)); zdsfsinfo.keepRDW = 0; zdsfsinfo.allow_inclomplete_multi_volume = 0; zdsfsinfo.tracks_per_frame = 128; zdsfsinfo.seek_buffer_size = 1048576; + zdsfsinfo.configfile = "/etc/zdsfs.conf"; + zdsfsinfo.keepalive = DEFAULT_KEEPALIVE_SEC; + zdsfsinfo.active_server = -1; rc = lzds_zdsroot_alloc(&zdsfsinfo.zdsroot); + open_dsh = dshlist_alloc(); if (rc) { fprintf(stderr, "Could not allocate internal structures\n"); exit(1); @@ -1085,6 +1354,7 @@ int main(int argc, char *argv[]) fprintf(stderr, "Failed to parse option\n"); exit(1); } + zdsfs_process_config_file(zdsfsinfo.configfile); if (!zdsfsinfo.devcount) { fprintf(stderr, "Please specify a block device\n"); @@ -1111,9 +1381,21 @@ int main(int argc, char *argv[]) if (rc) goto cleanup; + if (zdsfsinfo.restapi) { + curl_global_init(CURL_GLOBAL_DEFAULT); + zdsfs_test_restserver(); + if (zdsfsinfo.active_server < 0) { + fprintf(stderr, "Error: No z/OSMF REST Server reachable\n"); + rc = -EACCES; + goto cleanup; + } + } + rc = fuse_main(args.argc, args.argv, &rdf_oper, NULL); cleanup: + curl_global_cleanup(); + dshlist_free(open_dsh); lzds_zdsroot_free(zdsfsinfo.zdsroot); fuse_opt_free_args(&args);