diff --git a/examples/.gitignore b/examples/.gitignore index f47d81c..82bf854 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -1,3 +1,4 @@ filelistc filelistd streamfld +streamflc diff --git a/examples/Makefile.am b/examples/Makefile.am index 486c413..966fcd9 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -13,7 +13,7 @@ AM_CFLAGS = -Wall -g # where to find libsxmp libsxmp = ../lib/.libs/libsxmp.la -bin_PROGRAMS = filelistd filelistc streamfld +bin_PROGRAMS = filelistd filelistc streamfld streamflc filelistd_SOURCES = helpers.c filelistd.c filelistd_LDADD = $(LIBTDATA_LIBS) $(LIBSEXPR_LIBS) $(OPENSSL_LIBS) \ @@ -26,3 +26,7 @@ filelistc_LDADD = $(LIBTDATA_LIBS) $(LIBSEXPR_LIBS) $(OPENSSL_LIBS) \ streamfld_SOURCES = helpers.c streamfld.c streamfld_LDADD = $(LIBTDATA_LIBS) $(LIBSEXPR_LIBS) $(OPENSSL_LIBS) \ $(LIBUUID_LIBS) $(libsxmp) -lpthread + +streamflc_SOURCES = streamflc.c +streamflc_LDADD = $(LIBTDATA_LIBS) $(LIBSEXPR_LIBS) $(OPENSSL_LIBS) \ + $(LIBUUID_LIBS) $(libsxmp) -lpthread diff --git a/examples/filelistc.c b/examples/filelistc.c index 88689f7..a62a791 100644 --- a/examples/filelistc.c +++ b/examples/filelistc.c @@ -23,11 +23,8 @@ * NOTE: It was developed quite fast within one day, * btw - this the reason of some ugly code here. * - * This is a master or simply - daemon part, it will - * listen a requests and create a stream with directory entries, - * which ridden by the client. - * This implements a simple client-server topology, to see - * more advanced technics check out other examples. + * This is a client example, without builtin streams. + * It's based on the daemon own stream implementation. * * NOTE(win32): don't have a time to test it or fix it to * make it works on windows, if you can - u're welcome. diff --git a/examples/streamflc.c b/examples/streamflc.c new file mode 100644 index 0000000..c4f8009 --- /dev/null +++ b/examples/streamflc.c @@ -0,0 +1,410 @@ +/* + * Secure X Message Passing Library v2 examples. + * + * (c) Alexander Vdolainen 2016 + * + * libsxmp is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libsxmp 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 Lesser 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 ."; + * + */ + +/* + * This is an example of sxmp usage. + * NOTE: It was developed quite fast within one day, + * btw - this the reason of some ugly code here. + * + * This is a client example for filelister implemented + * using builtin streams, available since 0.4.2 + * + * NOTE(win32): don't have a time to test it or fix it to + * make it works on windows, if you can - u're welcome. + */ + +#include +#include +#define __USE_GNU +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "filelist.h" +#include "../config.h" + +#define FULL_PROGRAM_NAME "File lister client (builtin streams based)" + +struct _remote_host { + char *root_x509; + char *user_x509; + char *hostname; + char *login; + char *password; + int port; + sxhub_t *hub; + sxlink_t *link; +}; + +struct _astream { + int tid; + char *longcmd; + char *short_option; + char *description; + void (*print_head)(FILE *); + void (*print_op)(FILE *, list_head_t *); + int (*exefoo)(FILE *, struct _remote_host *, struct _astream *, const char *); +}; + +static void __dirlist_headout(FILE *); +static void __dirlist_out(FILE *, list_head_t *); +static int __dirlist(FILE *, struct _remote_host *, struct _astream *, const char *); + +struct _astream streams_source[] = { + { .tid = DIRLIST_ID, .longcmd = "dir-list", .short_option = "e", .print_head = __dirlist_headout, + .print_op = __dirlist_out, .exefoo = __dirlist, + .description = "List directory contents on remote host" }, + { .tid = 0, .longcmd = NULL, .short_option = NULL, .print_head = NULL, .print_op = NULL, + .exefoo = NULL, .description = NULL }, +}; + +/* + * dir listing from remote host goes below. + */ +/* printing */ +static void __dirlist_headout(FILE *f) +{ + fprintf(f, "%-24s | %-12s\n", "Name", "Type"); + fprintf(f, "---------------------------------------\n"); + return; +} + +static void __dirlist_out(FILE *f, list_head_t *list) +{ + char *dname = NULL, *dtype = NULL; + list_node_t *iter, *siter; + struct _nn_stream_entry_node *entry; + int i = 0; + + list_for_each_safe(list, iter, siter) { + entry = list_entry(iter, struct _nn_stream_entry_node, node); + if(!i) dname = entry->value; + else if(i == 1) dtype = entry->value; + i++; + } + fprintf(f, "%-24s | %-12s\n", dname, dtype); + return; +} + +/* command itself */ +static int __dirlist(FILE *f, struct _remote_host *remote, struct _astream *rstream, + const char *opt) +{ + sxlink_t *link = remote->link; + int stream_tid = rstream->tid; + sxstream_t *stream; + list_head_t *list; + + rstream->print_head(f); + + stream = sxstream_open(link, opt, stream_tid, SXE_O_READ | SXE_O_TRUNC); + if(!stream) { + fprintf(stderr, "Failed to open stream with %d ID (%d).\n", stream_tid, errno); + return EINVAL; + } + + while((list = sxstream_read(stream)) != NULL) + rstream->print_op(f, list); + + sxstream_close(stream); + + return 0; +} + +/* sxmp stuff below */ +static int __rpcscheck_onclient(sxlink_t *l, int ch_tid, char *description) +{ + return SXE_SUCCESS; +} + +static int __remote_host_connect(struct _remote_host *rh) +{ + int r = 0; + + sxhub_set_channelcall(rh->hub, __rpcscheck_onclient); + + /* here we go, connect to the hist i.e. create a link to it */ + rh->link = sxlink_connect(rh->hub, rh->hostname, rh->port, rh->user_x509, + rh->login, rh->password); + if(!rh->link) r = errno; + + return r; +} + +/* standard output helpers */ +static void __version_print(FILE *fso) +{ + fprintf(fso, "\nFile list example client based on builtin streams.\n"); + fprintf(fso, "Examples bundle for %s %s.\n", PACKAGE_NAME, PACKAGE_VERSION); + return; +} + +static void __help_print(FILE *fso, const char *fmtname) +{ + int i; + fprintf(fso, "\n%s\n\n", FULL_PROGRAM_NAME); + + /* usage options */ + fprintf(fso, "Usage:\n"); + for(i = 0; streams_source[i].longcmd != NULL; i++) { + fprintf(fso, "\t%s -r | --root-x509 -u | --user-x509 -H | --hostname " + " -l | --login -P | --password -%s | --%s " + " [-p | --port ]\n", fmtname, streams_source[i].short_option, + streams_source[i].longcmd); + } + + /* defaults */ + fprintf(fso, "\t%s -h | --help\n", fmtname); + fprintf(fso, "\t%s -v | --version\n", fmtname); + + /* options description */ + fprintf(fso, "\nOptions:\n"); + fprintf(fso, "\t%-33s Set pathname of the remote root X.509 public certificate.\n", "-r | --root-x509 "); + fprintf(fso, "\t%-33s Set pathname of the user X.509 public and private certificate.\n", + "-u | --user-x509 "); + fprintf(fso, "\t%-33s Hostname or IP address of the remote host.\n", "-H | --hostname "); + fprintf(fso, "\t%-33s Set remote login name.\n", "-l | --login "); + fprintf(fso, "\t%-33s Set remote user password.\n", "-P | --password "); + fprintf(fso, "\t%-33s Remote host listening port number [default: %d].\n", + "-p | --port ", DEFAULT_PORT); + for(i = 0; streams_source[i].longcmd != NULL; i++) { + fprintf(fso, "\t-%-2s| --%-26s %s.\n", streams_source[i].short_option, + streams_source[i].longcmd, streams_source[i].description); + } + fprintf(fso, "\t%-33s Show help screen.\n", "-h | --help"); + fprintf(fso, "\t%-33s Print version information.\n", "-v | --version"); + return; +} + +#define GENERIC_OPTS "hvr:u:H:p:l:P:" + +static struct option o_help = {"help", no_argument, NULL, 'h'}; +static struct option o_version = {"version", no_argument, NULL, 'v'}; +static struct option o_rootx509 = {"root-x509", required_argument, NULL, 'r'}; +static struct option o_userx509 = {"user-x509", required_argument, NULL, 'u'}; +static struct option o_hostname = {"hostname", required_argument, NULL, 'H'}; +static struct option o_port = {"port", required_argument, NULL, 'p'}; +static struct option o_login = {"login", required_argument, NULL, 'l'}; +static struct option o_password = {"password", required_argument, NULL, 'P'}; + +int main(int argc, char **argv) +{ + char *rootca, *cert, *host, *login, *password; + char *options, *argument = NULL; + int port = DEFAULT_PORT, opt, i = 0, opt_len = 9, r = 0; + struct _remote_host *rh = malloc(sizeof(struct _remote_host)); + sxhub_t *lhub; + struct _astream *src = NULL; + int (*execution)(FILE *, struct _remote_host *, struct _astream *, const char *); + static struct option *long_options; + + if(!rh) return ENOMEM; + else memset(rh, 0, sizeof(struct _remote_host)); + + rootca = cert = host = login = password = NULL; + execution = NULL; + + /* amount of builtin commands */ + for(i = 0; streams_source[i].longcmd != NULL; i++) ;; + opt_len += i; + + /* generate options string */ + if(!(options = malloc(strlen(GENERIC_OPTS) + sizeof(char)*((i*2) + 1)))) { + free(rh); + return ENOMEM; + } else memset(options, 0, strlen(GENERIC_OPTS) + sizeof(char)*((i*2) + 1)); + + strcat(options, GENERIC_OPTS); + for(i = 0; streams_source[i].longcmd != NULL; i++) { + strcat(options, streams_source[i].short_option); + strcat(options, ":"); + } + + /* generate long options */ + long_options = malloc(sizeof(struct option)*opt_len); + if(!long_options) { + free(options); + free(rh); + return ENOMEM; + } else memset(long_options, 0, sizeof(struct option)*opt_len); + + for(i = 0; i < opt_len; i++) { + switch(i) { + case 0: long_options[i] = o_help; break; + case 1: long_options[i] = o_version; break; + case 2: long_options[i] = o_rootx509; break; + case 3: long_options[i] = o_userx509; break; + case 4: long_options[i] = o_hostname; break; + case 5: long_options[i] = o_port; break; + case 6: long_options[i] = o_login; break; + case 7: long_options[i] = o_password; break; + default: + if(i == (opt_len - 1)) memset(&long_options[i], 0, sizeof(struct option)); + else { + long_options[i].name = streams_source[i - 8].longcmd; + long_options[i].has_arg = required_argument; + long_options[i].flag = NULL; + long_options[i].val = *(streams_source[i - 8].short_option); + } + break; + } + } + + r = ENOMEM; + + while(1) { + int option_index = 0; + if((opt = getopt_long(argc, argv, (const char *)options, long_options, + &option_index)) == -1) break; + + switch(opt) { + case 'h': + __help_print(stdout, argv[0]); + return 0; + break; + case 'v': + __version_print(stdout); + return 0; + break; + case 'r': + if(!(rootca = strdup(optarg))) goto __failsafe; + break; + case 'u': + if(!(cert = strdup(optarg))) goto __failsafe; + break; + case 'H': + if(!(host = strdup(optarg))) goto __failsafe; + break; + case 'l': + if(!(login = strdup(optarg))) goto __failsafe; + break; + case 'P': + if(!(password = strdup(optarg))) goto __failsafe; + break; + case 'p': + port = atoi(optarg); + break; + default: + for(i = 0; streams_source[i].longcmd != NULL; i++) { + if(*(streams_source[i].short_option) == (char)opt) { + if(!execution) { + src = &streams_source[i]; + argument = strdup(optarg); + execution = streams_source[i].exefoo; + } else { + r = EINVAL; + fprintf(stderr, "One command at once only.\n"); + __help_print(stderr, argv[0]); + __failsafe: + free(options); + if(argument) free(argument); + free(long_options); + free(rh); + return r; + } + } + } + if(!execution) { r = EINVAL; goto __failsafe; } + break; + } + } + + /* ok validate all before doing any stuff */ + if(!rootca || !cert) { + fprintf(stderr, "One or more x.509 certificates files not pointed.\n"); + __help_print(stderr, argv[0]); + return EINVAL; + } + if(!login || !password) { + fprintf(stderr, "Login or password not pointed.\n"); + __help_print(stderr, argv[0]); + return EINVAL; + } + if(port <= 0) { + fprintf(stderr, "Port number has invalid value.\n"); + __help_print(stderr, argv[0]); + return EINVAL; + } + if(!execution) { + fprintf(stderr, "No command given.\n"); + __help_print(stderr, argv[0]); + return EINVAL; + } + + /* looks good */ + sxmp_init(); /* init library */ + lhub = sxhub_create(); /* create sxhub for link creation */ + if(!lhub) { r = ENOMEM; goto __failsafe; } + r = sxhub_setsslserts(lhub, rootca, cert, cert); + if(r) goto __failsafe; + + /* ok setup our structure */ + rh->port = port; + rh->root_x509 = rootca; + rh->user_x509 = cert; + rh->hostname = host; + rh->login = login; + rh->password = password; + rh->hub = lhub; + + if((r = __remote_host_connect(rh))) { + fprintf(stderr, "Failed to create link to %s@%s:%d with %d.\n", rh->login, rh->hostname, + rh->port, r); + } else { + r = execution(stdout, rh, src, argument); + sxlink_close(rh->link); + } + + + /* free all stuff */ + free(options); + free(long_options); + if(rh->root_x509) free(rh->root_x509); + if(rh->user_x509) free(rh->user_x509); + if(rh->hostname) free(rh->hostname); + if(rh->password) free(rh->password); + if(rh->login) free(rh->login); + free(rh); + + if(lhub) sxhub_destroy(lhub); + + return r; +}