diff --git a/Makefile.am b/Makefile.am index 63de8b7..07b1f88 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,12 +6,15 @@ EXTRA_DIST = m4/gnulib-cache.m4 install-exec-local: $(LN_S) -f gfcli $(DESTDIR)$(bindir)/gfcat + $(LN_S) -f gfcli $(DESTDIR)$(bindir)/gfchmod $(LN_S) -f gfcli $(DESTDIR)$(bindir)/gfcp $(LN_S) -f gfcli $(DESTDIR)$(bindir)/gfls $(LN_S) -f gfcli $(DESTDIR)$(bindir)/gfmkdir $(LN_S) -f gfcli $(DESTDIR)$(bindir)/gfrm $(LN_S) -f gfcli $(DESTDIR)$(bindir)/gfstat $(LN_S) -f gfcli $(DESTDIR)$(bindir)/gftail + $(LN_S) -f gfcli $(DESTDIR)$(bindir)/gfhead + $(LN_S) -f gfcli $(DESTDIR)$(bindir)/gfwc rpms: prep QA_RPATHS=17 rpmbuild --define '_topdir $(abs_top_builddir)/build/rpmbuild' \ @@ -25,7 +28,7 @@ prep: dist cp glusterfs-coreutils.spec build/rpmbuild/SPECS uninstall-local: - cd $(DESTDIR)$(bindir) && rm -f gfcat gfcp gfls gfmkdir gfmv gfrm gfstat gftail + cd $(DESTDIR)$(bindir) && rm -f gfcat gfcp gfls gfmkdir gfrm gfstat gftail gfwc gfhead gfchmod clean-local: rm -rf build rpmbuild *.rpm diff --git a/man/Makefile.am b/man/Makefile.am index 8f10683..35ef346 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -23,6 +23,14 @@ gfmkdir.1: $(top_builddir)/build/bin/gfmkdir $(HELP2MAN) --output=$@-t -I common_seealso.h2m \ $(top_builddir)/build/bin/$* && mv $@-t $@ +gfchmod.1: $(top_builddir)/build/bin/gfchmod + $(HELP2MAN) --output=$@-t -I common_seealso.h2m \ + $(top_builddir)/build/bin/$* && mv $@-t $@ + +gfwc.1: $(top_builddir)/build/bin/gfwc + $(HELP2MAN) --output=$@-t -I common_seealso.h2m \ + $(top_builddir)/build/bin/$* && mv $@-t $@ + gfput.1: $(top_builddir)/build/bin/gfput $(HELP2MAN) --output=$@-t $(top_builddir)/build/bin/$* \ && mv $@-t $@ @@ -35,19 +43,26 @@ gfstat.1: $(top_builddir)/build/bin/gfstat $(HELP2MAN) --output=$@-t -I common_seealso.h2m \ $(top_builddir)/build/bin/$* && mv $@-t $@ +gfhead.1: $(top_builddir)/build/bin/gfhead + $(HELP2MAN) --output=$@-t -I common_seealso.h2m \ + $(top_builddir)/build/bin/$* && mv $@-t $@ + gftail.1: $(top_builddir)/build/bin/gftail $(HELP2MAN) --output=$@-t -I common_seealso.h2m \ $(top_builddir)/build/bin/$* && mv $@-t $@ man1_MANS = gfcat.1 \ gfcli.1 \ + gfchmod.1 \ gfcp.1 \ gfls.1 \ gfmkdir.1 \ gfput.1 \ gfrm.1 \ gfstat.1 \ - gftail.1 + gfhead.1 \ + gftail.1 \ + gfwc.1 EXTRA_DIST = common_seealso.h2m diff --git a/src/Makefile.am b/src/Makefile.am index f6c44e8..7f5306d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,38 +4,47 @@ LDADD = $(top_builddir)/lib/libgnu.a all-local: $(LN_S) -f gfcli $(top_builddir)/build/bin/gfcat $(LN_S) -f gfcli $(top_builddir)/build/bin/gfcp + $(LN_S) -f gfcli $(top_builddir)/build/bin/gfchmod $(LN_S) -f gfcli $(top_builddir)/build/bin/gfls $(LN_S) -f gfcli $(top_builddir)/build/bin/gfmkdir + $(LN_S) -f gfcli $(top_builddir)/build/bin/gfwc $(LN_S) -f gfcli $(top_builddir)/build/bin/gfrm $(LN_S) -f gfcli $(top_builddir)/build/bin/gfstat $(LN_S) -f gfcli $(top_builddir)/build/bin/gftail + $(LN_S) -f gfcli $(top_builddir)/build/bin/gfhead bin_PROGRAMS = $(top_builddir)/build/bin/gfcli \ $(top_builddir)/build/bin/gfput EXTRA_DIST = glfs-cat.h \ glfs-cp.h \ + glfs-chmod.h \ glfs-cli-commands.h \ glfs-cli.h \ glfs-flock.h \ glfs-ls.h \ glfs-mkdir.h \ + glfs-wc.h \ glfs-rm.h \ glfs-stat.h \ glfs-stat-util.h \ glfs-tail.h \ + glfs-head.h \ glfs-util.h __top_builddir__build_bin_gfcli_SOURCES = glfs-cli.c \ glfs-cli-commands.c \ glfs-cat.c \ + glfs-chmod.c \ glfs-cp.c \ glfs-flock.c \ glfs-ls.c \ glfs-mkdir.c \ + glfs-wc.c \ glfs-rm.c \ glfs-stat.c \ glfs-stat-util.c \ + glfs-head.c \ glfs-tail.c \ glfs-util.c diff --git a/src/glfs-chmod.c b/src/glfs-chmod.c new file mode 100644 index 0000000..ce9db0a --- /dev/null +++ b/src/glfs-chmod.c @@ -0,0 +1,402 @@ +/** + * A utility to create a directory on a remote Gluster volume, with the ability + * to optionally create nested directories as required. + * + * Copyright (C) 2017 RedHat Inc. + * + * 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; either version 3 of the License, or + * (at your option) any later version. + * + * 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 "glfs-chmod.h" +#include "glfs-util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AUTHORS "Written by Jayadeep KM." + +/** + * Used to store the state of the program, including user supplied options. + * + * gluster_url: Struct of the parsed url supplied by the user. + * url: Full url used to find the remote file (supplied by user). + * debug: Whether to log additional debug information. + * recursive: Whether to apply chmod recursively. + */ +struct state { + struct gluster_url *gluster_url; + struct xlator_option *xlator_options; + char *url; + bool debug; + bool recursive; + char *mode; +}; + +static struct state *state; + +static struct option const long_options[] = +{ + {"debug", no_argument, NULL, 'd'}, + {"help", no_argument, NULL, 'h'}, + {"port", required_argument, NULL, 'p'}, + {"recursive", no_argument, NULL, 'R'}, + {"version", no_argument, NULL, 'v'}, + {"xlator-option", required_argument, NULL, 'o'}, + {NULL, no_argument, NULL, 0} +}; + +static void +usage () +{ + printf ("Usage: %s [OPTION]... MODE URL\n\n" + " -o, --xlator-option=OPTION specify a translator option for the \n" + " connection. Multiple options are supported\n" + " and take the form xlator.key=value.\n" + " -p, --port=PORT specify the port on which to connect\n" + " --help display this help and exit\n" + " --version output version information and exit\n\n" + "Examples:\n" + " chmod 777 glfs://localhost/groot/directory\n" + " Grant all permissions to all users for directory\n" + " chmod 444 glfs://localhost/groot/directory/subdirectory\n" + " Grant readonly permission to all users for /directory/subdirectory\n" + " chmod +w glfs://localhost/groot/directory\n" + " Grant read permission owner for directory\n", + program_invocation_name); +} + +static int +parse_options (int argc, char *argv[], bool has_connection) +{ + uint16_t port = GLUSTER_DEFAULT_PORT; + int ret = -1; + int opt = 0; + int option_index = 0; + struct xlator_option *option; + + // Reset getopt since other utilities may have called it already. + optind = 0; + while (true) { + opt = getopt_long (argc, argv, "do:p:rwx:Rv", long_options, + &option_index); + + if (opt == -1) { + break; + } + + switch (opt) { + case 'd': + state->debug = true; + break; + case 'o': + option = parse_xlator_option (optarg); + if (option == NULL) { + error (0, errno, "%s", optarg); + goto err; + } + + if (append_xlator_option (&state->xlator_options, option) == -1) { + error (0, errno, "append_xlator_option: %s", optarg); + goto err; + } + + break; + case 'p': + port = strtoport (optarg); + if (port == 0) { + goto out; + } + + break; + case 'r': case 'w': case 'x': break; + case 'R': + state->recursive = true; + break; + case 'v': + printf ("%s (%s) %s\n%s\n%s\n%s\n", + program_invocation_name, + PACKAGE_NAME, + PACKAGE_VERSION, + COPYRIGHT, + LICENSE, + AUTHORS); + ret = -2; + goto out; + case 'h': + usage (); + ret = -2; + goto out; + default: + goto err; + } + } + + if ((argc - option_index) < 3) { + error (0, 0, "missing operand"); + goto err; + } else { + state->url = strdup (argv[argc - 1]); + state->mode = strdup (argv[argc - 2]); + if (state->url == NULL) { + error (0, errno, "strdup"); + goto out; + } + if (state->mode == NULL) { + error (0, errno, "strdup"); + goto out; + } + + if (has_connection) { + state->gluster_url = gluster_url_init (); + if (state->gluster_url == NULL) { + error (0, errno, "gluster_url_init"); + goto out; + } + + state->gluster_url->path = strdup (argv[argc - 1]); + if (state->gluster_url->path == NULL) { + error (0, errno, "strdup"); + goto out; + } + + ret = 0; + goto out; + } + + ret = gluster_parse_url (argv[argc - 1], &(state->gluster_url)); + if (ret == -1) { + error (0, EINVAL, "%s", state->url); + goto err; + } + + state->gluster_url->port = port; + + // gluster_create_path will not create + // the last directory in a path if that path does not + // include a trailing slash ('/'), so add one if it does not + // exist. + char *path = state->gluster_url->path; + if (path[strlen (path) - 1] != '/') { + strncat (path, "/", strlen (path) + 2); + } + } + + goto out; + +err: + error (0, 0, "Try --help for more information."); +out: + return ret; +} + +static struct state* +init_state () +{ + struct state *state = malloc (sizeof (*state)); + + if (state == NULL) { + goto out; + } + + state->debug = false; + state->gluster_url = NULL; + state->url = NULL; + state->recursive = 0; + state->mode = NULL; + state->xlator_options = NULL; + +out: + return state; +} + +static int +chmod_with_fs (glfs_t *fs) +{ + int ret; + + mode_t mode = 0; + + size_t octal = 0; + size_t decimal = 0; + struct stat *local_stat; + + if(state->mode[0]>='0'&&state->mode[0]<='7'){ + octal = atoi(state->mode); + + while(octal!=0){ + decimal = decimal*10 + octal%10; + octal = octal / 10; + } + while(decimal!=0){ + octal = octal*8 + decimal%10; + decimal = decimal / 10; + } + mode = octal; + }else{ + local_stat =(struct stat*) malloc(sizeof(struct stat)); + ret = glfs_stat(fs,state->gluster_url->path,local_stat); + if(ret == -1) + goto err; + mode = local_stat->st_mode; + int mode_length = strlen(state->mode); + if(state->mode[0]=='+'){ + for(int mode_loop=1;mode_loopmode[mode_loop]; + switch (modeChar) { + case 'r': + mode |= S_IRUSR; break; + case 'w': + mode |= S_IWUSR; break; + case 'x': + mode |= S_IXUSR; break; + default: + ret = -2; + goto err; + } + } + }else if(state->mode[0]=='-'){ + for(int mode_loop=1;mode_loopmode[mode_loop]; + switch (modeChar) { + case 'r': + mode &= ~S_IRUSR; break; + case 'w': + mode &= ~S_IWUSR; break; + case 'x': + mode &= ~S_IXUSR; break; + default: + ret = -2; + goto err; + + } + } + }else{ + ret = -2; + goto err; + } + } + + + ret = glfs_chmod(fs,state->gluster_url->path,mode); + +err: + + if (ret == -1) { + error (0, errno, "cannot change permissions `%s'", state->url); + goto out; + } + + if (ret == -2) { + error (0, errno, "Invalid permission mode`%s'", state->url); + goto out; + } + +out: + return ret; +} + +static int +chmod_without_context () +{ + glfs_t *fs = NULL; + int ret = -1; + + ret = gluster_getfs (&fs, state->gluster_url); + if (ret == -1) { + error (0, errno, "cannot create directory `%s'", state->url); + goto out; + } + + ret = apply_xlator_options (fs, &state->xlator_options); + if (ret == -1) { + error (0, errno, "failed to apply translator options"); + goto out; + } + + if (state->debug) { + ret = glfs_set_logging (fs, "/dev/stderr", GF_LOG_DEBUG); + + if (ret == -1) { + error (0, errno, "failed to set logging level"); + goto out; + } + } + + ret = chmod_with_fs (fs); + +out: + if (fs) { + glfs_fini (fs); + } + + return ret; +} + +int +do_chmod (struct cli_context *ctx) +{ + int argc = ctx->argc; + char **argv = ctx->argv; + int ret = -1; + + state = init_state (); + if (state == NULL) { + error (0, errno, "failed to initialize state"); + goto out; + } + + state->debug = ctx->options->debug; + + if (ctx->fs) { + ret = parse_options (argc, argv, true); + if (ret != 0) { + goto out; + } + + ret = chmod_with_fs (ctx->fs); + } else { + ret = parse_options (argc, argv, false); + switch (ret) { + case -2: + // Fall through + ret = 0; + case -1: + goto out; + } + + ret = chmod_without_context (); + } + +out: + if (state) { + gluster_url_free (state->gluster_url); + free (state->url); + free (state->mode); + } + + free (state); + + return ret; +} diff --git a/src/glfs-chmod.h b/src/glfs-chmod.h new file mode 100644 index 0000000..20b2399 --- /dev/null +++ b/src/glfs-chmod.h @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2015 Facebook Inc. + * + * 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; either version 3 of the License, or + * (at your option) any later version. + * + * 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 . + * + */ + +#ifndef GLFS_CHMOD_H +#define GLFS_CHMOD_H + +#include "glfs-cli.h" + +int +do_chmod (struct cli_context *ctx); + +#endif diff --git a/src/glfs-cli.c b/src/glfs-cli.c index caac351..2e9d69c 100644 --- a/src/glfs-cli.c +++ b/src/glfs-cli.c @@ -36,11 +36,14 @@ #include "glfs-cli.h" #include "glfs-cat.h" +#include "glfs-chmod.h" #include "glfs-cp.h" #include "glfs-cli-commands.h" #include "glfs-flock.h" +#include "glfs-head.h" #include "glfs-ls.h" #include "glfs-mkdir.h" +#include "glfs-wc.h" #include "glfs-rm.h" #include "glfs-stat.h" #include "glfs-tail.h" @@ -63,22 +66,25 @@ shell_usage () { printf ("The following commands are supported:\n" "* cat\n" + "* chmod\n" "* connect\n" "* cp\n" "* disconnect\n" "* help\n" "* ls\n" "* mkdir\n" + "* wc\n" "* quit\n" "* rm\n" "* stat\n" + "* head\n" "* tail\n" "* flock\n"); return 0; } -#define NUM_CMDS 13 +#define NUM_CMDS 14 static struct cmd const cmds[] = { { .name = "connect", .execute = cli_connect }, @@ -89,9 +95,12 @@ static struct cmd const cmds[] = { .alias = "gfls", .name = "ls", .execute = do_ls }, { .alias = "gfmkdir", .name = "mkdir", .execute = do_mkdir }, { .alias = "gfmv", .name = "mv", .execute = not_implemented }, + { .alias = "gfchmod", .name = "chmod", .execute = do_chmod }, + { .alias = "gfwc", .name = "mv", .execute = do_wc }, { .name = "quit", .execute = handle_quit }, { .alias = "gfrm", .name = "rm", .execute = do_rm }, { .alias = "gfstat", .name = "stat", .execute = do_stat }, + { .alias = "gfhead", .name = "head", .execute = do_head }, { .alias = "gftail", .name = "tail", .execute = do_tail }, { .name = "flock", .execute = do_flock } }; diff --git a/src/glfs-head.c b/src/glfs-head.c new file mode 100644 index 0000000..0ac1eb1 --- /dev/null +++ b/src/glfs-head.c @@ -0,0 +1,540 @@ +/** + * A utility to read the last n bytes or lines from a file on a remote Gluster + * volume and stream it to stdout. + * + * Copyright (C) 2017 Redhat Inc. + * + * 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; either version 3 of the License, or + * (at your option) any later version. + * + * 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 "glfs-head.h" +#include "glfs-util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AUTHORS "Written by Jayadeep KM." + +static volatile int keep_running = 1; + +static void +int_handler (int value) +{ + keep_running = 0; +} + +enum head_mode +{ + BYTES, + LINES +}; + +/** + * Used to store the state of the program, including user supplied options. + * + * gluster_url: Struct of the parsed url supplied by the user. + * url: Full url used to find the remote file. + * bytes: Number of bytes to print from the end of the file. + * debug: Whether to log additional debug information. + * lines: Number of lines to print from the end of the file. + * mode: The mode the application is in (bytes vs lines). + */ +struct state { + struct gluster_url *gluster_url; + struct xlator_option *xlator_options; + char *url; + unsigned int bytes; + bool debug; + unsigned int lines; + enum head_mode mode; +}; + +static struct state *state; + +static struct option const long_options[] = +{ + {"bytes", required_argument, NULL, 'c'}, + {"debug", no_argument, NULL, 'd'}, + {"help", no_argument, NULL, 'x'}, + {"lines", required_argument, NULL, 'n'}, + {"xlator-option", required_argument, NULL, 'o'}, + {"port", required_argument, NULL, 'p'}, + {"version", no_argument, NULL, 'v'}, + {NULL, no_argument, NULL, 0} +}; + +/** + * Prints usage information. + */ +static void +usage () +{ + printf ("Usage: %s [OPTION]... URL\n" + "Print the first 10 lines (default) of the file to standard output.\n\n" + " -c, --bytes=K output the first K bytes\n" + " -n, --lines=K output the first K lines, instead of the last 10 bytes\n" + " -o, --xlator-option=OPTION specify a translator option for the\n" + " connection. Multiple options are supported\n" + " and take the form xlator.key=value.\n" + " -p, --port=PORT specify the port on which to connect\n" + " --help display this help and exit\n" + " --version output version information and exit\n\n" + "Examples:\n" + " gfhead glfs://localhost/groot/file\n" + " Head the first 10 lines of the file /file on the Gluster\n" + " volume groot on host localhost.\n" + " gfhead -c 100 glfs://localhost/groot/file\n" + " Head the first 100 bytes of the file /file on the Gluster\n" + " volume groot on host localhost.\n" + " gfcli (localhost/groot)> head /example\n" + " In the context of a shell with a connection established,\n" + " head the file example on the root of the Gluster volume\n" + " groot on localhost.\n", + program_invocation_name); +} + +/** + * Helper function that converts a char * string into an int. Returns -1 on failure. + */ +static int +strtoint (const char *str) +{ + long int raw_value; + int value = -1; + char *end; + + raw_value = strtol (str, &end, 10); + + if (str == end) { + goto out; + } + + if (raw_value > INT_MAX || raw_value < 0) { + goto out; + } + + value = (int) raw_value; + +out: + return value; +} + +/** + * Parses command line flags into a global application state. + */ +static int +parse_options (int argc, char *argv[], bool has_connection) +{ + uint16_t port = GLUSTER_DEFAULT_PORT; + int ret = -1; + int opt = 0; + int option_index = 0; + struct xlator_option *option; + + // Reset getopt since other utilities may have called it already. + optind = 0; + while (true) { + opt = getopt_long (argc, argv, "c:dfl:n:o:p:s:", long_options, &option_index); + + if (opt == -1) { + break; + } + + switch (opt) { + case 'c': + state->bytes = strtoint (optarg); + if (state->bytes == -1) { + error (0, 0, "invalid number of bytes: \"%s\"", optarg); + goto out; + } + + state->mode = BYTES; + break; + case 'd': + state->debug = true; + break; + case 'n': + state->lines = strtoint (optarg); + if (state->lines == -1) { + error (0, 0, "invalid number of lines: \"%s\"", optarg); + goto out; + } + + state->mode = LINES; + break; + case 'o': + option = parse_xlator_option (optarg); + if (option == NULL) { + error (0, errno, "%s", optarg); + goto err; + } + + if (append_xlator_option (&state->xlator_options, option) == -1) { + error (0, errno, "append_xlator_option: %s", optarg); + goto err; + } + + break; + case 'p': + port = strtoport (optarg); + if (port == 0) { + goto err; + } + + break; + case 'v': + printf ("%s (%s) %s\n%s\n%s\n%s\n", + program_invocation_name, + PACKAGE_NAME, + PACKAGE_VERSION, + COPYRIGHT, + LICENSE, + AUTHORS); + ret = -2; + goto out; + case 'x': + usage (); + ret = -2; + goto out; + default: + goto err; + } + } + + if ((argc - option_index) < 2) { + error (0, 0, "missing operand"); + goto err; + } else { + state->url = strdup (argv[argc - 1]); + if (state->url == NULL) { + error (0, errno, "strdup"); + goto out; + } + + if (has_connection) { + state->gluster_url = gluster_url_init (); + state->gluster_url->path = strdup (argv[argc -1]); + ret = 0; + goto out; + } + + ret = gluster_parse_url (argv[argc - 1], &(state->gluster_url)); + if (ret == -1) { + error (0, EINVAL, "%s", state->url); + goto err; + } + + state->gluster_url->port = port; + } + + goto out; + +err: + error (0, 0, "Try --help for more information."); +out: + return ret; +} + +/** + * Initializes the global application state. + */ +static struct state* +init_state () +{ + struct state *state = malloc (sizeof (*state)); + + if (state == NULL) { + goto out; + } + + state->bytes = 0; + state->debug = false; + state->gluster_url = NULL; + state->lines = 10; + state->mode = LINES; + state->url = NULL; + state->xlator_options = NULL; + +out: + return state; +} + +/** + * Sets the offset of the fd object based on the number of bytes. + */ +static int +head_lines (glfs_fd_t *fd) +{ + char buffer[BUFSIZE]; + size_t num_read = 0; + size_t num_written = 0; + size_t ret = 0; + size_t total_written = 0; + int dst = STDOUT_FILENO; + unsigned long lines = state->lines; + size_t lines_remaining = lines; + size_t bytes_to_write = 0; + size_t loop_chars = 0; + size_t lines_written = 0; + size_t lines_writing = 0; + + while (true) { + num_read = glfs_read (fd, buffer, BUFSIZE, 0); + if (num_read == -1) { + goto out; + } + + if (num_read == 0) { + goto out; + } + + for (num_written = 0; num_written < num_read;) { + bytes_to_write = num_read - num_written; + lines_writing = 0; + for(loop_chars=0;loop_charsbytes; + size_t bytes_remaining = bytes; + size_t bytes_to_write = 0; + + while (true) { + num_read = glfs_read (fd, buffer, BUFSIZE, 0); + if (num_read == -1) { + goto out; + } + + if (num_read == 0) { + goto out; + } + + for (num_written = 0; num_written < num_read && bytes_remaining>0;) { + bytes_remaining = bytes - total_written; + bytes_to_write = bytes_remaining; + if(num_read - num_written < bytes_to_write) + bytes_to_write = num_read - num_written; + ret = write (dst, + &buffer[num_written], + bytes_to_write); + if (ret != bytes_to_write) { + goto err; + } + + num_written += ret; + total_written += ret; + } + if(bytes_remaining<=0) + goto out; + } + +err: + ret = -1; +out: + return ret; +} + +static int +head (glfs_t *fs) +{ + glfs_fd_t *fd = NULL; + int ret; + long long size; + + fd = glfs_open (fs, state->gluster_url->path, O_RDONLY); + if (fd == NULL) { + error (0, errno, "error reading `%s'", state->url); + goto err; + } + + switch (state->mode) { + case BYTES: + ret = head_bytes (fd); + break; + case LINES: + ret = head_lines (fd); + break; + default: + error (0, 0, "unknown error"); + goto err; + } + + if (ret == -1) { + goto err; + } + + ret = gluster_read (fd, STDOUT_FILENO); + if (ret == -1) { + error (0, errno, "write error"); + goto err; + } + + goto out; +err: + ret = -1; +out: + // Disable our signal handler + // FIXME: This clobbers gfcli's signal handler. + signal (SIGINT, SIG_DFL); + + if (fd) { + if (glfs_close (fd) == -1) { + error (0, errno, "failed to close file"); + ret = -1; + } + } + + return ret; +} + +static int +do_head_without_context () +{ + glfs_t *fs = NULL; + int ret; + + ret = gluster_getfs (&fs, state->gluster_url); + if (ret == -1) { + error (0, errno, "%s", state->url); + goto out; + } + + ret = apply_xlator_options (fs, &state->xlator_options); + if (ret == -1) { + error (0, errno, "failed to apply xlator options"); + goto out; + } + + if (state->debug) { + ret = glfs_set_logging (fs, "/dev/stderr", GF_LOG_DEBUG); + + if (ret == -1) { + error (0, errno, "failed to set logging level"); + goto out; + } + } + + ret = head (fs); + +out: + if (fs) { + glfs_fini (fs); + } + + return ret; +} + +/** + * Main entry point into application (called from glfs-cli.c) + */ +int +do_head (struct cli_context *ctx) +{ + int argc = ctx->argc; + char **argv = ctx->argv; + int ret; + + state = init_state (); + if (state == NULL) { + error (0, errno, "failed to initialize state"); + goto out; + } + + if (ctx->fs) { + ret = parse_options (argc, argv, true); + if (ret != 0) { + goto out; + } + + ret = head (ctx->fs); + } else { + ret = parse_options (argc, argv, false); + if (ret == -1) { + goto out; + } + + // Valid options were passed and executed, but we don't want to + // continue with execution. + if (ret == -2) { + ret = 0; + goto out; + } + + ret = do_head_without_context (); + } + +out: + if (state) { + gluster_url_free (state->gluster_url); + free (state->url); + } + + free (state); + + return ret; +} diff --git a/src/glfs-head.h b/src/glfs-head.h new file mode 100644 index 0000000..302ca3c --- /dev/null +++ b/src/glfs-head.h @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2015 Facebook Inc. + * + * 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; either version 3 of the License, or + * (at your option) any later version. + * + * 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 . + * + */ + +#ifndef GLFS_HEAD_H +#define GLFS_HEAD_H + +#include "glfs-cli.h" + +int +do_head (struct cli_context *ctx); + +#endif diff --git a/src/glfs-wc.c b/src/glfs-wc.c new file mode 100644 index 0000000..5a84b2e --- /dev/null +++ b/src/glfs-wc.c @@ -0,0 +1,382 @@ +/** + * A utility to read a file from a remote Gluster volume and stream it to + * stdout. + * + * Copyright (C) 2017 Redhat Inc. + * + * 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; either version 3 of the License, or + * (at your option) any later version. + * + * 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 "glfs-wc.h" +#include "glfs-util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AUTHORS "Written by Jayadeep KM." + +/** + * Used to store the state of the program, including user supplied options. + * + * gluster_url: Struct of the parsed url supplied by the user. + * url: Full url used to find the remote file (supplied by user). + * debug: Whether to log additional debug information. + */ +struct state { + struct gluster_url *gluster_url; + struct xlator_option *xlator_options; + char *url; + bool debug; +}; + +static struct state *state; + +static struct option const long_options[] = +{ + {"debug", no_argument, NULL, 'd'}, + {"help", no_argument, NULL, 'x'}, + {"port", required_argument, NULL, 'p'}, + {"version", no_argument, NULL, 'v'}, + {"xlator-option", required_argument, NULL, 'o'}, + {NULL, no_argument, NULL, 0} +}; + + +static int gluster_wc(glfs_fd_t *fd,const char* filename){ + + char buffer[BUFSIZE]; + size_t num_read = 0; + size_t num_word = 0; + size_t num_newline = 0; + size_t ret = 0; + size_t total_bytes = 0; + size_t total_newline = 0; + size_t total_word = 0; + size_t num_check; + int inside_word = 0; // for dealing with multiple whitespaces + + while (true) { + num_read = glfs_read (fd, buffer, BUFSIZE, 0); + if (num_read == -1) { + goto out; + } + + if (num_read == 0) { + goto out; + } + num_word = 0; + num_newline = 0; + for(num_check=0;num_checkurl); + goto out; + } + + // don't allow concurrent reads and writes. + ret = gluster_lock (fd, F_WRLCK, false); + if (ret == -1) { + error (0, errno, "%s", state->url); + goto out; + } + + if ((ret = gluster_wc (fd,filename)) == -1) { + error (0, errno, "read error"); + goto out; + } + + ret = 0; + +out: + if (fd) { + if (glfs_close (fd) == -1) { + ret = -1; + error (0, errno, "cannot close file %s", + state->gluster_url->path); + } + } + + return ret; +} + + +static void +usage () +{ + printf ("Usage: %s [OPTION]... URL\n" + "Print newline, word, and byte counts for a file on remote Gluster volume\n\n" + " -o, --xlator-option=OPTION specify a translator option for the\n" + " connection. Multiple options are supported\n" + " and take the form xlator.key=value.\n" + " -p, --port=PORT specify the port on which to connect\n" + " --help display this help and exit\n" + " --version output version information and exit\n\n" + "Examples:\n" + " gfwc glfs://localhost/groot/path/to/file\n" + " 287 491 7601 1\n" + " gfcli (localhost/groot)> wc /file\n" + " 287 491 7601 1\n", + program_invocation_name); +} + +static int +parse_options (int argc, char *argv[], bool has_connection) +{ + uint16_t port = GLUSTER_DEFAULT_PORT; + int ret = -1; + int opt = 0; + int option_index = 0; + struct xlator_option *option; + + // Reset getopt since other utilities may have called it already. + optind = 0; + while (true) { + opt = getopt_long (argc, argv, "do:p:", long_options, + &option_index); + + if (opt == -1) { + break; + } + + switch (opt) { + case 'd': + state->debug = true; + break; + case 'o': + option = parse_xlator_option (optarg); + if (option == NULL) { + error (0, errno, "%s", optarg); + goto err; + } + + if (append_xlator_option (&state->xlator_options, option) == -1) { + error (0, errno, "append_xlator_option: %s", optarg); + goto err; + } + + break; + case 'p': + port = strtoport (optarg); + if (port == 0) { + goto out; + } + + break; + case 'v': + printf ("%s (%s) %s\n%s\n%s\n%s\n", + program_invocation_name, + PACKAGE_NAME, + PACKAGE_VERSION, + COPYRIGHT, + LICENSE, + AUTHORS); + ret = -2; + goto out; + case 'x': + usage (); + ret = -2; + goto out; + default: + goto err; + } + } + + if ((argc - option_index) < 2) { + error (0, 0, "missing operand"); + goto err; + } else { + state->url = strdup (argv[argc - 1]); + if (state->url == NULL) { + error (0, errno, "strdup"); + goto out; + } + + if (has_connection) { + state->gluster_url = gluster_url_init (); + if (state->gluster_url == NULL) { + error (0, errno, "gluster_url_init"); + goto out; + } + + state->gluster_url->path = strdup (argv[argc - 1]); + if (state->gluster_url->path == NULL) { + error (0, errno, "strdup"); + goto out; + } + + ret = 0; + goto out; + } + + ret = gluster_parse_url (argv[argc - 1], &(state->gluster_url)); + if (ret == -1) { + error (0, EINVAL, "%s", state->url); + goto err; + } + + state->gluster_url->port = port; + } + + goto out; + +err: + error (0, 0, "Try --help for more information."); +out: + return ret; +} + +static struct state* +init_state () +{ + struct state *state = malloc (sizeof (*state)); + + if (state == NULL) { + goto out; + } + + state->debug = false; + state->gluster_url = NULL; + state->url = NULL; + state->xlator_options = NULL; + +out: + return state; +} + +static int +cat_without_context () +{ + glfs_t *fs = NULL; + int ret = -1; + + ret = gluster_getfs (&fs, state->gluster_url); + if (ret == -1) { + error (0, errno, "%s", state->url); + goto out; + } + + ret = apply_xlator_options (fs, &state->xlator_options); + if (ret == -1) { + error (0, errno, "failed to apply translator options"); + goto out; + } + + if (state->debug) { + ret = glfs_set_logging (fs, "/dev/stderr", GF_LOG_DEBUG); + + if (ret == -1) { + error (0, errno, "failed to set logging level"); + goto out; + } + } + + ret = gluster_get (fs, state->gluster_url->path); + if (ret == -1) { + goto out; + } + + ret = 0; + +out: + if (fs) { + glfs_fini (fs); + } + + return ret; +} + +int +do_wc (struct cli_context *ctx) +{ + int argc = ctx->argc; + char **argv = ctx->argv; + int ret = EXIT_FAILURE; + + state = init_state (); + if (state == NULL) { + error (0, errno, "failed to initialize state"); + goto out; + } + + if (ctx->fs) { + ret = parse_options (argc, argv, true); + if (ret != 0) { + goto out; + } + + ret = gluster_get (ctx->fs, state->gluster_url->path); + } else { + state->debug = ctx->options->debug; + ret = parse_options (argc, argv, false); + switch (ret) { + case -2: + // Fall through + ret = 0; + case -1: + goto out; + } + + ret = cat_without_context (); + } + +out: + if (state) { + gluster_url_free (state->gluster_url); + free (state->url); + } + + free (state); + + return ret; +} diff --git a/src/glfs-wc.h b/src/glfs-wc.h new file mode 100644 index 0000000..55227e7 --- /dev/null +++ b/src/glfs-wc.h @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2015 Facebook Inc. + * + * 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; either version 3 of the License, or + * (at your option) any later version. + * + * 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 . + * + */ + +#ifndef GLFS_WC_H +#define GLFS_WC_H + +#include "glfs-cli.h" + +int +do_wc (struct cli_context *ctx); + +#endif /* GLFS_CP_H */