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 */