From 8498d23e339b560678bd209bb82203a16a9c96d8 Mon Sep 17 00:00:00 2001 From: Jayadeep KM Date: Sun, 19 Mar 2017 01:00:48 +0530 Subject: [PATCH 01/10] started implementing wc --- Makefile.am | 3 +- src/Makefile.am | 3 + src/glfs-cli.c | 4 +- src/glfs-wc.c | 330 ++++++++++++++++++++++++++++++++++++++++++++++++ src/glfs-wc.h | 27 ++++ 5 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 src/glfs-wc.c create mode 100644 src/glfs-wc.h diff --git a/Makefile.am b/Makefile.am index 63de8b7..6fbf8bf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,6 +12,7 @@ install-exec-local: $(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)/gfwc rpms: prep QA_RPATHS=17 rpmbuild --define '_topdir $(abs_top_builddir)/build/rpmbuild' \ @@ -25,7 +26,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 clean-local: rm -rf build rpmbuild *.rpm diff --git a/src/Makefile.am b/src/Makefile.am index f6c44e8..2e91eed 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,6 +6,7 @@ all-local: $(LN_S) -f gfcli $(top_builddir)/build/bin/gfcp $(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 @@ -20,6 +21,7 @@ EXTRA_DIST = glfs-cat.h \ glfs-flock.h \ glfs-ls.h \ glfs-mkdir.h \ + glfs-wc.h \ glfs-rm.h \ glfs-stat.h \ glfs-stat-util.h \ @@ -33,6 +35,7 @@ __top_builddir__build_bin_gfcli_SOURCES = glfs-cli.c \ glfs-flock.c \ glfs-ls.c \ glfs-mkdir.c \ + glfs-wc.c \ glfs-rm.c \ glfs-stat.c \ glfs-stat-util.c \ diff --git a/src/glfs-cli.c b/src/glfs-cli.c index caac351..fd951e4 100644 --- a/src/glfs-cli.c +++ b/src/glfs-cli.c @@ -41,6 +41,7 @@ #include "glfs-flock.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" @@ -69,6 +70,7 @@ shell_usage () "* help\n" "* ls\n" "* mkdir\n" + "* wc\n" "* quit\n" "* rm\n" "* stat\n" @@ -88,7 +90,7 @@ static struct cmd const cmds[] = { .name = "help", .execute = shell_usage }, { .alias = "gfls", .name = "ls", .execute = do_ls }, { .alias = "gfmkdir", .name = "mkdir", .execute = do_mkdir }, - { .alias = "gfmv", .name = "mv", .execute = not_implemented }, + { .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 }, diff --git a/src/glfs-wc.c b/src/glfs-wc.c new file mode 100644 index 0000000..f427a7e --- /dev/null +++ b/src/glfs-wc.c @@ -0,0 +1,330 @@ +/** + * A utility to read a file from a remote Gluster volume and stream it to + * stdout. + * + * 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 . + * + */ + +#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_get (glfs_t *fs, const char *filename) { + glfs_fd_t *fd = NULL; + int ret = -1; + + fd = glfs_open (fs, filename, O_RDONLY); + if (fd == NULL) { + error (0, errno, "%s", state->url); + 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_read (fd, STDOUT_FILENO)) == -1) { + error (0, errno, "write 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 */ From 79a77018356490076b63d7ddc2e16fed88e0254f Mon Sep 17 00:00:00 2001 From: Jayadeep KM Date: Sun, 19 Mar 2017 01:49:15 +0530 Subject: [PATCH 02/10] wc basic implementation --- src/glfs-wc.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/src/glfs-wc.c b/src/glfs-wc.c index f427a7e..dd07395 100644 --- a/src/glfs-wc.c +++ b/src/glfs-wc.c @@ -62,6 +62,58 @@ static struct option const long_options[] = {NULL, no_argument, NULL, 0} }; + +static int gluster_wc(glfs_fd_t *fd){ + + 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_check Date: Sun, 19 Mar 2017 02:05:08 +0530 Subject: [PATCH 03/10] fixed wordlength for multiple continous whitespaces --- src/glfs-cli.c | 1 + src/glfs-wc.c | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/glfs-cli.c b/src/glfs-cli.c index fd951e4..099af58 100644 --- a/src/glfs-cli.c +++ b/src/glfs-cli.c @@ -90,6 +90,7 @@ static struct cmd const cmds[] = { .name = "help", .execute = shell_usage }, { .alias = "gfls", .name = "ls", .execute = do_ls }, { .alias = "gfmkdir", .name = "mkdir", .execute = do_mkdir }, + { .alias = "gfmv", .name = "mv", .execute = not_implemented }, { .alias = "gfwc", .name = "mv", .execute = do_wc }, { .name = "quit", .execute = handle_quit }, { .alias = "gfrm", .name = "rm", .execute = do_rm }, diff --git a/src/glfs-wc.c b/src/glfs-wc.c index dd07395..5a84b2e 100644 --- a/src/glfs-wc.c +++ b/src/glfs-wc.c @@ -2,7 +2,7 @@ * A utility to read a file from a remote Gluster volume and stream it to * stdout. * - * Copyright (C) 2015 Facebook Inc. + * 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 @@ -63,7 +63,7 @@ static struct option const long_options[] = }; -static int gluster_wc(glfs_fd_t *fd){ +static int gluster_wc(glfs_fd_t *fd,const char* filename){ char buffer[BUFSIZE]; size_t num_read = 0; @@ -92,7 +92,7 @@ static int gluster_wc(glfs_fd_t *fd){ num_newline++; if(buffer[num_check]==' '||buffer[num_check]=='\t'||buffer[num_check]=='\n'){ inside_word = 0; - }else{ + }else if(inside_word==0){ inside_word = 1; num_word++; } @@ -108,8 +108,7 @@ static int gluster_wc(glfs_fd_t *fd){ ret = -1; out: - - printf("%d %d %d\n",total_newline,total_word,total_bytes); + printf("%d %d %d %s\n",total_newline,total_word,total_bytes,filename+1); return ret; } @@ -132,7 +131,7 @@ gluster_get (glfs_t *fs, const char *filename) { goto out; } - if ((ret = gluster_wc (fd)) == -1) { + if ((ret = gluster_wc (fd,filename)) == -1) { error (0, errno, "read error"); goto out; } From 607cd788d5b0bb6adf3e9155f7cddbd939dcd136 Mon Sep 17 00:00:00 2001 From: Jayadeep KM Date: Sun, 19 Mar 2017 02:24:03 +0530 Subject: [PATCH 04/10] added man page for wc command --- man/Makefile.am | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/man/Makefile.am b/man/Makefile.am index 8f10683..6d4421c 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -23,6 +23,10 @@ gfmkdir.1: $(top_builddir)/build/bin/gfmkdir $(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 $@ @@ -47,7 +51,8 @@ man1_MANS = gfcat.1 \ gfput.1 \ gfrm.1 \ gfstat.1 \ - gftail.1 + gftail.1 \ + gfwc.1 EXTRA_DIST = common_seealso.h2m From db1b2aeb99eb8a770e14eae9e85ae43356277248 Mon Sep 17 00:00:00 2001 From: Jayadeep KM Date: Sun, 19 Mar 2017 02:37:59 +0530 Subject: [PATCH 05/10] started implementing head --- Makefile.am | 3 +- man/Makefile.am | 5 + src/Makefile.am | 3 + src/glfs-cli.c | 5 +- src/glfs-head.c | 610 ++++++++++++++++++++++++++++++++++++++++++++++++ src/glfs-head.h | 27 +++ 6 files changed, 651 insertions(+), 2 deletions(-) create mode 100644 src/glfs-head.c create mode 100644 src/glfs-head.h diff --git a/Makefile.am b/Makefile.am index 6fbf8bf..7ee5b40 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,6 +12,7 @@ install-exec-local: $(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 @@ -26,7 +27,7 @@ prep: dist cp glusterfs-coreutils.spec build/rpmbuild/SPECS uninstall-local: - cd $(DESTDIR)$(bindir) && rm -f gfcat gfcp gfls gfmkdir gfrm gfstat gftail gfwc + cd $(DESTDIR)$(bindir) && rm -f gfcat gfcp gfls gfmkdir gfrm gfstat gftail gfwc gfhead clean-local: rm -rf build rpmbuild *.rpm diff --git a/man/Makefile.am b/man/Makefile.am index 6d4421c..3b225e1 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -39,6 +39,10 @@ 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 $@ @@ -51,6 +55,7 @@ man1_MANS = gfcat.1 \ gfput.1 \ gfrm.1 \ gfstat.1 \ + gfhead.1 \ gftail.1 \ gfwc.1 diff --git a/src/Makefile.am b/src/Makefile.am index 2e91eed..b027eb0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,6 +10,7 @@ all-local: $(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 @@ -26,6 +27,7 @@ EXTRA_DIST = glfs-cat.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 \ @@ -39,6 +41,7 @@ __top_builddir__build_bin_gfcli_SOURCES = glfs-cli.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-cli.c b/src/glfs-cli.c index 099af58..9ae34ed 100644 --- a/src/glfs-cli.c +++ b/src/glfs-cli.c @@ -39,6 +39,7 @@ #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" @@ -74,13 +75,14 @@ shell_usage () "* 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 }, @@ -95,6 +97,7 @@ static struct cmd const cmds[] = { .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..36a4a88 --- /dev/null +++ b/src/glfs-head.c @@ -0,0 +1,610 @@ +/** + * 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-tail.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 tail_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. + * follow: Whether to continue tailing the output of the file as new data appears. + * lines: Number of lines to print from the end of the file. + * sleep_interval: Length of time to sleep in between polling the file for changes. + * 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; + bool follow; + unsigned int lines; + unsigned long int sleep_interval; + enum tail_mode mode; +}; + +static struct state *state; + +static struct option const long_options[] = +{ + {"bytes", required_argument, NULL, 'c'}, + {"debug", no_argument, NULL, 'd'}, + {"follow", no_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'x'}, + {"lines", required_argument, NULL, 'n'}, + {"xlator-option", required_argument, NULL, 'o'}, + {"port", required_argument, NULL, 'p'}, + {"sleep-internal", required_argument, NULL, 's'}, + {"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 last K bytes\n" + " -f, --follow output appended data as the file grows\n" + " -n, --lines=K output the last K lines, instead of the last 10\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" + " -s, --sleep-internal=N with -f, sleep for approximately N \n" + " microseconds (default is 50,000)\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 last 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 last 100 bytes of the file /file on the Gluster\n" + " volume groot on host localhost.\n" + " gfhead -f glfs://localhost/groot/file\n" + " Head the last 10 lines of the file /file on the Gluster\n" + " volume groot on host localhost, following the file\n" + " until an interrupt is received.\n" + " gfcli (localhost/groot)> tail /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 'f': + state->follow = 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 's': + state->sleep_interval = strtoul (optarg, NULL, 10); + if (state->sleep_interval == 0 || errno == ERANGE) { + error (0, 0, "invalid sleep interval: \"%s\"", optarg); + 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 (); + 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->follow = false; + state->gluster_url = NULL; + state->lines = 10; + state->mode = LINES; + state->sleep_interval = 500000; + 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 +tail_bytes (glfs_fd_t *fd, struct stat *statbuf) +{ + int ret; + long long size = (long long) statbuf->st_size; + long long offset = size - state->bytes; + + if (offset < 0) { + offset = 0; + } + + ret = glfs_lseek (fd, offset, SEEK_SET); + if (ret == -1) { + error (0, errno, "seek error"); + } + + return ret; +} + +/** + * Sets the offset of the fd object based on the number of newlines. + */ +static int +tail_lines (glfs_fd_t *fd, struct stat *statbuf) +{ + int ret = 0; + int newline_count = 0; + ssize_t num_read; + char buffer[BUFSIZE]; + long long offset; + long long size = (long long) statbuf->st_size; + + offset = size - BUFSIZE; + if (offset < 0) { + offset = 0; + } + + if (state->lines == 0) { + offset = size; + goto finished; + } + + while (true) { + ret = glfs_lseek (fd, offset, SEEK_SET); + if (ret == -1) { + error (0, errno, "seek error"); + goto err; + } + + num_read = glfs_read (fd, &buffer, BUFSIZE, 0); + if (num_read == -1) { + error (0, errno, "read error"); + goto err; + } + + for (int i = num_read - 1; i >= 0; i--) { + if (buffer[i] == '\n') { + newline_count++; + } + + if (newline_count == (state->lines + 1)) { + offset += i + 1; + goto finished; + } + } + + if ((offset - BUFSIZE) < 0) { + offset -= num_read; + break; + } else { + offset -= BUFSIZE; + } + } + + num_read = glfs_read (fd, &buffer, BUFSIZE, 0); + if (num_read == -1) { + error (0, errno, "read error"); + goto err; + } + + for (int i = num_read - 1; i >= 0; i--) { + if (buffer[i] == '\n') { + newline_count++; + } + + if (newline_count == (state->lines + 1)) { + offset += i + 1; + goto finished; + } + } + + if (newline_count < state->lines) { + offset = 0; + } + + if (offset < 0) { + offset = 0; + } + +finished: + ret = glfs_lseek (fd, offset, SEEK_SET); + if (ret == -1) { + error (0, errno, "seek error"); + } + + goto out; + +err: + ret = -1; +out: + return ret; +} + +static int +tail (glfs_t *fs) +{ + glfs_fd_t *fd = NULL; + int ret; + struct stat statbuf; + long long size; + + ret = glfs_stat (fs, state->gluster_url->path, &statbuf); + if (ret == -1) { + error (0, errno, "cannot open `%s' for reading", state->url); + goto err; + } + + fd = glfs_open (fs, state->gluster_url->path, O_RDONLY); + if (fd == NULL) { + error (0, errno, "error reading `%s'", state->url); + goto err; + } + + size = (long long) statbuf.st_size; + + switch (state->mode) { + case BYTES: + ret = tail_bytes (fd, &statbuf); + break; + case LINES: + ret = tail_lines (fd, &statbuf); + 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; + } + + if (state->follow) { + // Use our SIGINT handler to break out of follow functionality + signal (SIGINT, int_handler); + + while (keep_running) { + usleep (state->sleep_interval); + + ret = glfs_stat (fs, state->gluster_url->path, &statbuf); + if (ret == -1) { + error (0, errno, "cannot open `%s' for reading", state->url); + goto err; + } + + if (statbuf.st_size == size) { + continue; + } + + if (statbuf.st_size < size) { + error (0, 0, "file truncated: %s", + state->gluster_url->path); + size = statbuf.st_size; + glfs_lseek (fd, 0, SEEK_SET); + } + + ret = gluster_read (fd, STDOUT_FILENO); + if (ret == -1) { + error (0, errno, "read error: %s", + state->gluster_url->path); + 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 = tail (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 = tail (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 From aa66fc82fbf54b0b5badbf0f6bcbb1e28a04f924 Mon Sep 17 00:00:00 2001 From: Jayadeep KM Date: Sun, 19 Mar 2017 03:20:56 +0530 Subject: [PATCH 06/10] implemented head command --- src/glfs-head.c | 258 ++++++++++++++++++------------------------------ 1 file changed, 94 insertions(+), 164 deletions(-) diff --git a/src/glfs-head.c b/src/glfs-head.c index 36a4a88..0ac1eb1 100644 --- a/src/glfs-head.c +++ b/src/glfs-head.c @@ -21,7 +21,7 @@ #include -#include "glfs-tail.h" +#include "glfs-head.h" #include "glfs-util.h" #include @@ -49,7 +49,7 @@ int_handler (int value) keep_running = 0; } -enum tail_mode +enum head_mode { BYTES, LINES @@ -62,9 +62,7 @@ enum tail_mode * 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. - * follow: Whether to continue tailing the output of the file as new data appears. * lines: Number of lines to print from the end of the file. - * sleep_interval: Length of time to sleep in between polling the file for changes. * mode: The mode the application is in (bytes vs lines). */ struct state { @@ -73,10 +71,8 @@ struct state { char *url; unsigned int bytes; bool debug; - bool follow; unsigned int lines; - unsigned long int sleep_interval; - enum tail_mode mode; + enum head_mode mode; }; static struct state *state; @@ -85,12 +81,10 @@ static struct option const long_options[] = { {"bytes", required_argument, NULL, 'c'}, {"debug", no_argument, NULL, 'd'}, - {"follow", no_argument, NULL, 'f'}, {"help", no_argument, NULL, 'x'}, {"lines", required_argument, NULL, 'n'}, {"xlator-option", required_argument, NULL, 'o'}, {"port", required_argument, NULL, 'p'}, - {"sleep-internal", required_argument, NULL, 's'}, {"version", no_argument, NULL, 'v'}, {NULL, no_argument, NULL, 0} }; @@ -103,29 +97,22 @@ 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 last K bytes\n" - " -f, --follow output appended data as the file grows\n" - " -n, --lines=K output the last K lines, instead of the last 10\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" - " -s, --sleep-internal=N with -f, sleep for approximately N \n" - " microseconds (default is 50,000)\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 last 10 lines of the file /file on the Gluster\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 last 100 bytes of the file /file on the Gluster\n" + " Head the first 100 bytes of the file /file on the Gluster\n" " volume groot on host localhost.\n" - " gfhead -f glfs://localhost/groot/file\n" - " Head the last 10 lines of the file /file on the Gluster\n" - " volume groot on host localhost, following the file\n" - " until an interrupt is received.\n" - " gfcli (localhost/groot)> tail /example\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", @@ -192,9 +179,6 @@ parse_options (int argc, char *argv[], bool has_connection) case 'd': state->debug = true; break; - case 'f': - state->follow = true; - break; case 'n': state->lines = strtoint (optarg); if (state->lines == -1) { @@ -223,14 +207,6 @@ parse_options (int argc, char *argv[], bool has_connection) goto err; } - break; - case 's': - state->sleep_interval = strtoul (optarg, NULL, 10); - if (state->sleep_interval == 0 || errno == ERANGE) { - error (0, 0, "invalid sleep interval: \"%s\"", optarg); - goto out; - } - break; case 'v': printf ("%s (%s) %s\n%s\n%s\n%s\n", @@ -299,11 +275,9 @@ init_state () state->bytes = 0; state->debug = false; - state->follow = false; state->gluster_url = NULL; state->lines = 10; state->mode = LINES; - state->sleep_interval = 500000; state->url = NULL; state->xlator_options = NULL; @@ -315,112 +289,110 @@ init_state () * Sets the offset of the fd object based on the number of bytes. */ static int -tail_bytes (glfs_fd_t *fd, struct stat *statbuf) +head_lines (glfs_fd_t *fd) { - int ret; - long long size = (long long) statbuf->st_size; - long long offset = size - state->bytes; - - if (offset < 0) { - offset = 0; - } - - ret = glfs_lseek (fd, offset, SEEK_SET); - if (ret == -1) { - error (0, errno, "seek error"); - } - - return ret; -} - -/** - * Sets the offset of the fd object based on the number of newlines. - */ -static int -tail_lines (glfs_fd_t *fd, struct stat *statbuf) -{ - int ret = 0; - int newline_count = 0; - ssize_t num_read; char buffer[BUFSIZE]; - long long offset; - long long size = (long long) statbuf->st_size; - - offset = size - BUFSIZE; - if (offset < 0) { - offset = 0; - } - - if (state->lines == 0) { - offset = size; - goto finished; - } + 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) { - ret = glfs_lseek (fd, offset, SEEK_SET); - if (ret == -1) { - error (0, errno, "seek error"); - goto err; + num_read = glfs_read (fd, buffer, BUFSIZE, 0); + if (num_read == -1) { + goto out; } - num_read = glfs_read (fd, &buffer, BUFSIZE, 0); - if (num_read == -1) { - error (0, errno, "read error"); - goto err; + if (num_read == 0) { + goto out; } - for (int i = num_read - 1; i >= 0; i--) { - if (buffer[i] == '\n') { - newline_count++; + for (num_written = 0; num_written < num_read;) { + bytes_to_write = num_read - num_written; + lines_writing = 0; + for(loop_chars=0;loop_charslines + 1)) { - offset += i + 1; - goto finished; + ret = write (dst, + &buffer[num_written], + bytes_to_write ); + if (ret != bytes_to_write) { + goto err; } - } - if ((offset - BUFSIZE) < 0) { - offset -= num_read; - break; - } else { - offset -= BUFSIZE; + num_written += ret; + total_written += ret; + lines_written +=lines_writing; + if(lines_written==lines) + goto out; } + if(lines_written==lines) + goto out; } - num_read = glfs_read (fd, &buffer, BUFSIZE, 0); - if (num_read == -1) { - error (0, errno, "read error"); - goto err; - } +err: + ret = -1; +out: + return ret; +} - for (int i = num_read - 1; i >= 0; i--) { - if (buffer[i] == '\n') { - newline_count++; - } - if (newline_count == (state->lines + 1)) { - offset += i + 1; - goto finished; +static int +head_bytes (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 bytes = state->bytes; + 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 (newline_count < state->lines) { - offset = 0; - } + if (num_read == 0) { + goto out; + } - if (offset < 0) { - offset = 0; - } + 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; + } -finished: - ret = glfs_lseek (fd, offset, SEEK_SET); - if (ret == -1) { - error (0, errno, "seek error"); + num_written += ret; + total_written += ret; + } + if(bytes_remaining<=0) + goto out; } - goto out; - err: ret = -1; out: @@ -428,33 +400,24 @@ tail_lines (glfs_fd_t *fd, struct stat *statbuf) } static int -tail (glfs_t *fs) +head (glfs_t *fs) { glfs_fd_t *fd = NULL; int ret; - struct stat statbuf; long long size; - ret = glfs_stat (fs, state->gluster_url->path, &statbuf); - if (ret == -1) { - error (0, errno, "cannot open `%s' for reading", state->url); - goto err; - } - fd = glfs_open (fs, state->gluster_url->path, O_RDONLY); if (fd == NULL) { error (0, errno, "error reading `%s'", state->url); goto err; } - size = (long long) statbuf.st_size; - switch (state->mode) { case BYTES: - ret = tail_bytes (fd, &statbuf); + ret = head_bytes (fd); break; case LINES: - ret = tail_lines (fd, &statbuf); + ret = head_lines (fd); break; default: error (0, 0, "unknown error"); @@ -471,39 +434,6 @@ tail (glfs_t *fs) goto err; } - if (state->follow) { - // Use our SIGINT handler to break out of follow functionality - signal (SIGINT, int_handler); - - while (keep_running) { - usleep (state->sleep_interval); - - ret = glfs_stat (fs, state->gluster_url->path, &statbuf); - if (ret == -1) { - error (0, errno, "cannot open `%s' for reading", state->url); - goto err; - } - - if (statbuf.st_size == size) { - continue; - } - - if (statbuf.st_size < size) { - error (0, 0, "file truncated: %s", - state->gluster_url->path); - size = statbuf.st_size; - glfs_lseek (fd, 0, SEEK_SET); - } - - ret = gluster_read (fd, STDOUT_FILENO); - if (ret == -1) { - error (0, errno, "read error: %s", - state->gluster_url->path); - goto err; - } - } - } - goto out; err: ret = -1; @@ -549,7 +479,7 @@ do_head_without_context () } } - ret = tail (fs); + ret = head (fs); out: if (fs) { @@ -581,7 +511,7 @@ do_head (struct cli_context *ctx) goto out; } - ret = tail (ctx->fs); + ret = head (ctx->fs); } else { ret = parse_options (argc, argv, false); if (ret == -1) { From b036b584ea94624890c38108750339d2b94a8d7c Mon Sep 17 00:00:00 2001 From: Jayadeep KM Date: Sun, 19 Mar 2017 05:04:07 +0530 Subject: [PATCH 07/10] started chmod --- Makefile.am | 3 +- man/Makefile.am | 5 + src/Makefile.am | 3 + src/glfs-chmod.c | 325 +++++++++++++++++++++++++++++++++++++++++++++++ src/glfs-chmod.h | 27 ++++ src/glfs-cli.c | 3 + 6 files changed, 365 insertions(+), 1 deletion(-) create mode 100644 src/glfs-chmod.c create mode 100644 src/glfs-chmod.h diff --git a/Makefile.am b/Makefile.am index 7ee5b40..07b1f88 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,6 +6,7 @@ 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 @@ -27,7 +28,7 @@ prep: dist cp glusterfs-coreutils.spec build/rpmbuild/SPECS uninstall-local: - cd $(DESTDIR)$(bindir) && rm -f gfcat gfcp gfls gfmkdir gfrm gfstat gftail gfwc gfhead + 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 3b225e1..35ef346 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -23,6 +23,10 @@ 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 $@ @@ -49,6 +53,7 @@ gftail.1: $(top_builddir)/build/bin/gftail man1_MANS = gfcat.1 \ gfcli.1 \ + gfchmod.1 \ gfcp.1 \ gfls.1 \ gfmkdir.1 \ diff --git a/src/Makefile.am b/src/Makefile.am index b027eb0..7f5306d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,6 +4,7 @@ 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 @@ -17,6 +18,7 @@ bin_PROGRAMS = $(top_builddir)/build/bin/gfcli \ EXTRA_DIST = glfs-cat.h \ glfs-cp.h \ + glfs-chmod.h \ glfs-cli-commands.h \ glfs-cli.h \ glfs-flock.h \ @@ -33,6 +35,7 @@ EXTRA_DIST = glfs-cat.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 \ diff --git a/src/glfs-chmod.c b/src/glfs-chmod.c new file mode 100644 index 0000000..4ff6a69 --- /dev/null +++ b/src/glfs-chmod.c @@ -0,0 +1,325 @@ +/** + * 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. + * parents: Whether all parent directories in the path are created. + */ +struct state { + struct gluster_url *gluster_url; + struct xlator_option *xlator_options; + char *url; + bool debug; + bool parents; +}; + +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 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", + 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: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': + state->parents = 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 '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; + + // 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->parents = false; + state->url = NULL; + state->xlator_options = NULL; + +out: + return state; +} + +static int +mkdir_with_fs (glfs_t *fs) +{ + int ret; + mode_t mode = get_default_dir_mode_perm (); + + if (state->parents) { + ret = gluster_create_path (fs, state->gluster_url->path, mode); + } else { + ret = glfs_mkdir (fs, state->gluster_url->path, mode); + } + + if (ret == -1) { + error (0, errno, "cannot create directory `%s'", state->url); + goto out; + } + +out: + return ret; +} + +static int +mkdir_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 = mkdir_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 = mkdir_with_fs (ctx->fs); + } else { + ret = parse_options (argc, argv, false); + switch (ret) { + case -2: + // Fall through + ret = 0; + case -1: + goto out; + } + + ret = mkdir_without_context (); + } + +out: + if (state) { + gluster_url_free (state->gluster_url); + free (state->url); + } + + 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 9ae34ed..2e9d69c 100644 --- a/src/glfs-cli.c +++ b/src/glfs-cli.c @@ -36,6 +36,7 @@ #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" @@ -65,6 +66,7 @@ shell_usage () { printf ("The following commands are supported:\n" "* cat\n" + "* chmod\n" "* connect\n" "* cp\n" "* disconnect\n" @@ -93,6 +95,7 @@ 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 }, From cf5a55032a067257fda5396741c3cc6271130074 Mon Sep 17 00:00:00 2001 From: Jayadeep KM Date: Sun, 19 Mar 2017 05:52:33 +0530 Subject: [PATCH 08/10] chmod basic implementation --- src/glfs-chmod.c | 54 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/src/glfs-chmod.c b/src/glfs-chmod.c index 4ff6a69..81985aa 100644 --- a/src/glfs-chmod.c +++ b/src/glfs-chmod.c @@ -43,14 +43,15 @@ * 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. - * parents: Whether all parent directories in the path are created. + * recursive: Whether to apply chmod recursively. */ struct state { struct gluster_url *gluster_url; struct xlator_option *xlator_options; char *url; bool debug; - bool parents; + bool recursive; + char *mode; }; static struct state *state; @@ -127,7 +128,7 @@ parse_options (int argc, char *argv[], bool has_connection) break; case 'r': - state->parents = true; + state->recursive = true; break; case 'v': printf ("%s (%s) %s\n%s\n%s\n%s\n", @@ -148,15 +149,20 @@ parse_options (int argc, char *argv[], bool has_connection) } } - if ((argc - option_index) < 2) { + 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 (); @@ -212,8 +218,9 @@ init_state () state->debug = false; state->gluster_url = NULL; - state->parents = false; state->url = NULL; + state->recursive = 0; + state->mode = NULL; state->xlator_options = NULL; out: @@ -221,19 +228,33 @@ init_state () } static int -mkdir_with_fs (glfs_t *fs) +chmod_with_fs (glfs_t *fs) { int ret; - mode_t mode = get_default_dir_mode_perm (); - if (state->parents) { - ret = gluster_create_path (fs, state->gluster_url->path, mode); - } else { - ret = glfs_mkdir (fs, state->gluster_url->path, mode); + mode_t mode = 0; + + size_t octal = 0; + size_t decimal = 0; + + if(mode!=NULL){ + 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; } + ret = glfs_chmod(fs,state->gluster_url->path,mode); + if (ret == -1) { - error (0, errno, "cannot create directory `%s'", state->url); + error (0, errno, "cannot change permissions `%s'", state->url); goto out; } @@ -242,7 +263,7 @@ mkdir_with_fs (glfs_t *fs) } static int -mkdir_without_context () +chmod_without_context () { glfs_t *fs = NULL; int ret = -1; @@ -268,7 +289,7 @@ mkdir_without_context () } } - ret = mkdir_with_fs (fs); + ret = chmod_with_fs (fs); out: if (fs) { @@ -299,7 +320,7 @@ do_chmod (struct cli_context *ctx) goto out; } - ret = mkdir_with_fs (ctx->fs); + ret = chmod_with_fs (ctx->fs); } else { ret = parse_options (argc, argv, false); switch (ret) { @@ -310,13 +331,14 @@ do_chmod (struct cli_context *ctx) goto out; } - ret = mkdir_without_context (); + ret = chmod_without_context (); } out: if (state) { gluster_url_free (state->gluster_url); free (state->url); + free (state->mode); } free (state); From 5297e4fa20f6961f3870d3ca642ef136b74e13be Mon Sep 17 00:00:00 2001 From: Jayadeep KM Date: Sun, 19 Mar 2017 06:36:52 +0530 Subject: [PATCH 09/10] added +r,+x,+w,-r,-w,-x support for chmod --- src/glfs-chmod.c | 56 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/src/glfs-chmod.c b/src/glfs-chmod.c index 81985aa..a15e36c 100644 --- a/src/glfs-chmod.c +++ b/src/glfs-chmod.c @@ -59,8 +59,9 @@ static struct state *state; static struct option const long_options[] = { {"debug", no_argument, NULL, 'd'}, - {"help", no_argument, NULL, 'x'}, + {"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} @@ -96,7 +97,7 @@ parse_options (int argc, char *argv[], bool has_connection) // Reset getopt since other utilities may have called it already. optind = 0; while (true) { - opt = getopt_long (argc, argv, "do:p:rv", long_options, + opt = getopt_long (argc, argv, "do:p:rwx:Rv", long_options, &option_index); if (opt == -1) { @@ -127,7 +128,8 @@ parse_options (int argc, char *argv[], bool has_connection) } break; - case 'r': + case 'r': case 'w': case 'x': break; + case 'R': state->recursive = true; break; case 'v': @@ -140,7 +142,7 @@ parse_options (int argc, char *argv[], bool has_connection) AUTHORS); ret = -2; goto out; - case 'x': + case 'h': usage (); ret = -2; goto out; @@ -236,8 +238,9 @@ chmod_with_fs (glfs_t *fs) size_t octal = 0; size_t decimal = 0; + struct stat *local_stat; - if(mode!=NULL){ + if(state->mode[0]>='0'&&state->mode[0]<='7'){ octal = atoi(state->mode); while(octal!=0){ @@ -249,15 +252,58 @@ chmod_with_fs (glfs_t *fs) 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; + if(state->mode[0]=='+'){ + switch (state->mode[1]) { + 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]=='-'){ + switch (state->mode[1]) { + 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; } From be5b2617c7d393a8bb2da6acb6612e154dee297a Mon Sep 17 00:00:00 2001 From: Jayadeep KM Date: Sun, 19 Mar 2017 06:58:54 +0530 Subject: [PATCH 10/10] added chmod support for multiple permissions (+rw, -rx etc) --- src/glfs-chmod.c | 51 ++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/src/glfs-chmod.c b/src/glfs-chmod.c index a15e36c..ce9db0a 100644 --- a/src/glfs-chmod.c +++ b/src/glfs-chmod.c @@ -81,7 +81,9 @@ usage () " 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", + " 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); } @@ -258,30 +260,37 @@ chmod_with_fs (glfs_t *fs) if(ret == -1) goto err; mode = local_stat->st_mode; + int mode_length = strlen(state->mode); if(state->mode[0]=='+'){ - switch (state->mode[1]) { - case 'r': - mode |= S_IRUSR; break; - case 'w': - mode |= S_IWUSR; break; - case 'x': - mode |= S_IXUSR; break; - default: - ret = -2; - goto err; + 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]=='-'){ - switch (state->mode[1]) { - case 'r': - mode &= ~S_IRUSR; break; - case 'w': - mode &= ~S_IWUSR; break; - case 'x': - mode &= ~S_IXUSR; break; - default: - ret = -2; - goto err; + 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;