summaryrefslogtreecommitdiffstats
path: root/libglusterfs/src/graph.y
diff options
context:
space:
mode:
authorAnand Avati <avati@gluster.com>2010-06-07 12:37:34 +0000
committerAnand V. Avati <avati@dev.gluster.com>2010-06-07 11:58:50 -0700
commit79241696fbdebe2583298f12cbaee068ce60c655 (patch)
tree42e60d351e328fa34f17242c6c3359a8c01e8fa3 /libglusterfs/src/graph.y
parentc4ebd25a176d6d51d702b1009e261c3c27237a48 (diff)
dynamic volume changes for graph replacement
Signed-off-by: Anand V. Avati <avati@blackhole.gluster.com> Signed-off-by: Anand V. Avati <avati@dev.gluster.com> BUG: 971 (dynamic volume management) URL: http://bugs.gluster.com/cgi-bin/bugzilla3/show_bug.cgi?id=971
Diffstat (limited to 'libglusterfs/src/graph.y')
-rw-r--r--libglusterfs/src/graph.y651
1 files changed, 651 insertions, 0 deletions
diff --git a/libglusterfs/src/graph.y b/libglusterfs/src/graph.y
new file mode 100644
index 00000000000..4ac07660f95
--- /dev/null
+++ b/libglusterfs/src/graph.y
@@ -0,0 +1,651 @@
+/*
+ Copyright (c) 2006-2009 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS 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.
+
+ GlusterFS 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
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+%token VOLUME_BEGIN VOLUME_END OPTION NEWLINE SUBVOLUME ID WHITESPACE COMMENT TYPE STRING_TOK
+
+%{
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "xlator.h"
+#include "logging.h"
+
+static int new_volume (char *name);
+static int volume_type (char *type);
+static int volume_option (char *key, char *value);
+static int volume_sub (char *sub);
+static int volume_end (void);
+static void sub_error (void);
+static void type_error (void);
+static void option_error (void);
+
+#define YYSTYPE char *
+#define GF_CMD_BUFFER_LEN (8 * GF_UNIT_KB)
+
+int yyerror (const char *);
+int yylex ();
+%}
+
+
+%%
+VOLUMES: VOLUME | VOLUMES VOLUME;
+
+VOLUME: VOLUME_HEADER VOLUME_DATA VOLUME_FOOTER;
+VOLUME_HEADER: VOLUME_BEGIN WORD {if (new_volume ($2) == -1) { YYABORT; }};
+VOLUME_FOOTER: VOLUME_END {if (volume_end () == -1) { YYABORT; }};
+
+VOLUME_DATA: TYPE_LINE OPTIONS_LINE SUBVOLUME_LINE OPTIONS_LINE |
+ TYPE_LINE SUBVOLUME_LINE OPTIONS_LINE |
+ TYPE_LINE OPTIONS_LINE SUBVOLUME_LINE |
+ TYPE_LINE SUBVOLUME_LINE |
+ TYPE_LINE OPTIONS_LINE |
+ OPTIONS_LINE SUBVOLUME_LINE OPTIONS_LINE | /* error case */
+ OPTIONS_LINE; /* error case */
+
+TYPE_LINE: TYPE WORD {if (volume_type ($2) == -1) { YYABORT; }} | TYPE { type_error(); YYABORT; };
+
+SUBVOLUME_LINE: SUBVOLUME WORDS | SUBVOLUME { sub_error (); YYABORT; };
+
+OPTIONS_LINE: OPTION_LINE | OPTIONS_LINE OPTION_LINE;
+
+OPTION_LINE: OPTION WORD WORD {if (volume_option ($2, $3) == -1) { YYABORT; }} |
+ OPTION WORD { option_error (); YYABORT; } |
+ OPTION { option_error (); YYABORT; };
+
+WORDS: WORD {if (volume_sub ($1) == -1) {YYABORT; }} | WORDS WORD { if (volume_sub ($2) == -1) { YYABORT; }};
+WORD: ID | STRING_TOK ;
+%%
+
+xlator_t *curr;
+glusterfs_graph_t *construct;
+
+
+static void
+type_error (void)
+{
+ extern int yylineno;
+
+ gf_log ("parser", GF_LOG_ERROR,
+ "Volume %s, before line %d: Please specify volume type",
+ curr->name, yylineno);
+ return;
+}
+
+
+static void
+sub_error (void)
+{
+ extern int yylineno;
+
+ gf_log ("parser", GF_LOG_ERROR,
+ "Volume %s, before line %d: Please specify subvolumes",
+ curr->name, yylineno);
+ return;
+}
+
+
+static void
+option_error (void)
+{
+ extern int yylineno;
+
+ gf_log ("parser", GF_LOG_ERROR,
+ "Volume %s, before line %d: Please specify "
+ "option <key> <value>",
+ curr->name, yylineno);
+ return;
+}
+
+
+static int
+new_volume (char *name)
+{
+ extern int yylineno;
+ xlator_t *trav = NULL;
+ int ret = 0;
+
+ if (!name) {
+ gf_log ("parser", GF_LOG_DEBUG,
+ "Invalid argument name: '%s'", name);
+ ret = -1;
+ goto out;
+ }
+
+ if (curr) {
+ gf_log ("parser", GF_LOG_ERROR,
+ "new volume (%s) defintion in line %d unexpected",
+ name, yylineno);
+ ret = -1;
+ goto out;
+ }
+
+ curr = (void *) GF_CALLOC (1, sizeof (*curr),
+ gf_common_mt_xlator_t);
+
+ if (!curr) {
+ gf_log ("parser", GF_LOG_ERROR, "Out of memory");
+ ret = -1;
+ goto out;
+ }
+
+ trav = construct->first;
+
+ while (trav) {
+ if (!strcmp (name, trav->name)) {
+ gf_log ("parser", GF_LOG_ERROR,
+ "Line %d: volume '%s' defined again",
+ yylineno, name);
+ ret = -1;
+ goto out;
+ }
+ trav = trav->next;
+ }
+
+ curr->name = gf_strdup (name);
+ if (!curr->name) {
+ GF_FREE (curr);
+ ret = -1;
+ goto out;
+ }
+
+ curr->options = get_new_dict ();
+
+ if (!curr->options) {
+ GF_FREE (curr->name);
+ GF_FREE (curr);
+ ret = -1;
+ goto out;
+ }
+
+ curr->next = construct->first;
+ if (curr->next)
+ curr->next->prev = curr;
+
+ curr->graph = construct;
+
+ construct->first = curr;
+
+ construct->xl_count++;
+
+ gf_log ("parser", GF_LOG_TRACE, "New node for '%s'", name);
+
+out:
+ GF_FREE (name);
+
+ return ret;
+}
+
+
+static int
+volume_type (char *type)
+{
+ extern int yylineno;
+ int32_t ret = 0;
+
+ if (!type) {
+ gf_log ("parser", GF_LOG_DEBUG, "Invalid argument type");
+ ret = -1;
+ goto out;
+ }
+
+ ret = xlator_set_type (curr, type);
+ if (ret) {
+ gf_log ("parser", GF_LOG_ERROR,
+ "Volume '%s', line %d: type '%s' is not valid or "
+ "not found on this machine",
+ curr->name, yylineno, type);
+ ret = -1;
+ goto out;
+ }
+
+ gf_log ("parser", GF_LOG_TRACE, "Type:%s:%s", curr->name, type);
+
+out:
+ GF_FREE (type);
+
+ return 0;
+}
+
+
+static int
+volume_option (char *key, char *value)
+{
+ extern int yylineno;
+ int ret = 0;
+ char *set_value = NULL;
+
+ if (!key || !value){
+ gf_log ("parser", GF_LOG_ERROR, "Invalid argument");
+ ret = -1;
+ goto out;
+ }
+
+ set_value = gf_strdup (value);
+ ret = dict_set_dynstr (curr->options, key, set_value);
+
+ if (ret == 1) {
+ gf_log ("parser", GF_LOG_ERROR,
+ "Volume '%s', line %d: duplicate entry "
+ "('option %s') present",
+ curr->name, yylineno, key);
+ ret = -1;
+ goto out;
+ }
+
+ gf_log ("parser", GF_LOG_TRACE, "Option:%s:%s:%s",
+ curr->name, key, value);
+
+out:
+ GF_FREE (key);
+ GF_FREE (value);
+
+ return 0;
+}
+
+
+static int
+volume_sub (char *sub)
+{
+ extern int yylineno;
+ xlator_t *trav = NULL;
+ xlator_list_t *xlchild = NULL;
+ xlator_list_t *tmp = NULL;
+ xlator_list_t *xlparent = NULL;
+ int ret = 0;
+
+ if (!sub) {
+ gf_log ("parser", GF_LOG_ERROR, "Invalid subvolumes argument");
+ ret = -1;
+ goto out;
+ }
+
+ trav = construct->first;
+
+ while (trav) {
+ if (!strcmp (sub, trav->name))
+ break;
+ trav = trav->next;
+ }
+
+ if (!trav) {
+ gf_log ("parser", GF_LOG_ERROR,
+ "Volume '%s', line %d: subvolume '%s' is not defined "
+ "prior to usage",
+ curr->name, yylineno, sub);
+ ret = -1;
+ goto out;
+ }
+
+ if (trav == curr) {
+ gf_log ("parser", GF_LOG_ERROR,
+ "Volume '%s', line %d: has '%s' itself as subvolume",
+ curr->name, yylineno, sub);
+ ret = -1;
+ goto out;
+ }
+
+ xlparent = (void *) GF_CALLOC (1, sizeof (*xlparent),
+ gf_common_mt_xlator_list_t);
+
+ if (!xlparent) {
+ gf_log ("parser", GF_LOG_ERROR, "Out of memory");
+ ret = -1;
+ goto out;
+ }
+
+ xlparent->xlator = curr;
+
+ tmp = trav->parents;
+ if (tmp == NULL) {
+ trav->parents = xlparent;
+ } else {
+ while (tmp->next)
+ tmp = tmp->next;
+ tmp->next = xlparent;
+ }
+
+ xlchild = (void *) GF_CALLOC (1, sizeof(*xlchild),
+ gf_common_mt_xlator_list_t);
+ if (!xlchild) {
+ gf_log ("parser", GF_LOG_ERROR, "Out of memory");
+ ret = -1;
+ goto out;
+ }
+
+ xlchild->xlator = trav;
+
+ tmp = curr->children;
+ if (tmp == NULL) {
+ curr->children = xlchild;
+ } else {
+ while (tmp->next)
+ tmp = tmp->next;
+ tmp->next = xlchild;
+ }
+
+ gf_log ("parser", GF_LOG_TRACE, "child:%s->%s", curr->name, sub);
+
+out:
+ GF_FREE (sub);
+
+ return 0;
+}
+
+
+static int
+volume_end (void)
+{
+ if (!curr->fops) {
+ gf_log ("parser", GF_LOG_ERROR,
+ "\"type\" not specified for volume %s", curr->name);
+ return -1;
+ }
+ gf_log ("parser", GF_LOG_TRACE, "end:%s", curr->name);
+
+ curr = NULL;
+ return 0;
+}
+
+
+int
+yywrap ()
+{
+ return 1;
+}
+
+
+int
+yyerror (const char *str)
+{
+ extern char *yytext;
+ extern int yylineno;
+
+ if (curr && curr->name) {
+ if (!strcmp (yytext, "volume")) {
+ gf_log ("parser", GF_LOG_ERROR,
+ "'end-volume' not defined for volume '%s'",
+ curr->name);
+ } else if (!strcmp (yytext, "type")) {
+ gf_log ("parser", GF_LOG_ERROR,
+ "line %d: duplicate 'type' defined for "
+ "volume '%s'",
+ yylineno, curr->name);
+ } else if (!strcmp (yytext, "subvolumes")) {
+ gf_log ("parser", GF_LOG_ERROR,
+ "line %d: duplicate 'subvolumes' defined for "
+ "volume '%s'",
+ yylineno, curr->name);
+ } else if (curr) {
+ gf_log ("parser", GF_LOG_ERROR,
+ "syntax error: line %d (volume '%s'): \"%s\""
+ "\nallowed tokens are 'volume', 'type', "
+ "'subvolumes', 'option', 'end-volume'()",
+ yylineno, curr->name,
+ yytext);
+ } else {
+ gf_log ("parser", GF_LOG_ERROR,
+ "syntax error: line %d (just after volume "
+ "'%s'): \"%s\"\n(%s)",
+ yylineno, curr->name,
+ yytext,
+ "allowed tokens are 'volume', 'type', "
+ "'subvolumes', 'option', 'end-volume'");
+ }
+ } else {
+ gf_log ("parser", GF_LOG_ERROR,
+ "syntax error in line %d: \"%s\" \n"
+ "(allowed tokens are 'volume', 'type', "
+ "'subvolumes', 'option', 'end-volume')\n",
+ yylineno, yytext);
+ }
+
+ return -1;
+}
+
+
+static int
+execute_cmd (char *cmd, char **result, size_t size)
+{
+ FILE *fpp = NULL;
+ int i = 0;
+ int status = 0;
+ int character = 0;
+ char *buf = *result;
+
+ fpp = popen (cmd, "r");
+ if (!fpp) {
+ gf_log ("parser", GF_LOG_ERROR, "%s: failed to popen", cmd);
+ return -1;
+ }
+
+ while ((character = fgetc (fpp)) != EOF) {
+ if (i == size) {
+ size *= 2;
+ buf = *result = GF_REALLOC (*result, size);
+ }
+
+ buf[i++] = character;
+ }
+
+ if (i > 0) {
+ i--;
+ buf[i] = '\0';
+ }
+
+ status = pclose (fpp);
+ if (status == -1 || !WIFEXITED (status) ||
+ ((WEXITSTATUS (status)) != 0)) {
+ i = -1;
+ buf[0] = '\0';
+ }
+
+ return i;
+}
+
+
+static int
+preprocess (FILE *srcfp, FILE *dstfp)
+{
+ int ret = 0;
+ int i = 0;
+ char *cmd = NULL;
+ char *result = NULL;
+ size_t cmd_buf_size = GF_CMD_BUFFER_LEN;
+ char escaped = 0;
+ char in_backtick = 0;
+ int line = 1;
+ int column = 0;
+ int backtick_line = 0;
+ int backtick_column = 0;
+ int character = 0;
+
+
+ fseek (srcfp, 0L, SEEK_SET);
+ fseek (dstfp, 0L, SEEK_SET);
+
+ cmd = GF_CALLOC (cmd_buf_size, 1,
+ gf_common_mt_char);
+ if (cmd == NULL) {
+ gf_log ("parser", GF_LOG_ERROR, "Out of memory");
+ return -1;
+ }
+
+ result = GF_CALLOC (cmd_buf_size * 2, 1,
+ gf_common_mt_char);
+ if (result == NULL) {
+ GF_FREE (cmd);
+ gf_log ("parser", GF_LOG_ERROR, "Out of memory");
+ return -1;
+ }
+
+ while ((character = fgetc (srcfp)) != EOF) {
+ if ((character == '`') && !escaped) {
+ if (in_backtick) {
+ cmd[i] = '\0';
+ result[0] = '\0';
+
+ ret = execute_cmd (cmd, &result,
+ 2 * cmd_buf_size);
+ if (ret < 0) {
+ ret = -1;
+ goto out;
+ }
+ fwrite (result, ret, 1, dstfp);
+ } else {
+ i = 0;
+ cmd[i] = '\0';
+
+ backtick_column = column;
+ backtick_line = line;
+ }
+
+ in_backtick = !in_backtick;
+ } else {
+ if (in_backtick) {
+ if (i == cmd_buf_size) {
+ cmd_buf_size *= 2;
+ cmd = GF_REALLOC (cmd, cmd_buf_size);
+ if (cmd == NULL) {
+ return -1;
+ }
+
+ result = GF_REALLOC (result,
+ 2 * cmd_buf_size);
+ if (result == NULL) {
+ GF_FREE (cmd);
+ return -1;
+ }
+ }
+
+ cmd[i++] = character;
+ } else {
+ fputc (character, dstfp);
+ }
+ }
+
+ if (character == '\\') {
+ escaped = !escaped;
+ } else {
+ escaped = 0;
+ }
+
+ if (character == '\n') {
+ line++;
+ column = 0;
+ } else {
+ column++;
+ }
+ }
+
+ if (in_backtick) {
+ gf_log ("parser", GF_LOG_ERROR,
+ "Unterminated backtick in volume specfication file at line (%d), column (%d).",
+ line, column);
+ ret = -1;
+ }
+
+out:
+ fseek (srcfp, 0L, SEEK_SET);
+ fseek (dstfp, 0L, SEEK_SET);
+ GF_FREE (cmd);
+ GF_FREE (result);
+
+ return ret;
+}
+
+
+extern FILE *yyin;
+
+glusterfs_graph_t *
+glusterfs_graph_new ()
+{
+ glusterfs_graph_t *graph = NULL;
+
+ graph = GF_CALLOC (1, sizeof (*graph),
+ gf_common_mt_glusterfs_graph_t);
+ if (!graph)
+ return NULL;
+
+ INIT_LIST_HEAD (&graph->list);
+
+ gettimeofday (&graph->dob, NULL);
+
+ return graph;
+}
+
+
+glusterfs_graph_t *
+glusterfs_graph_construct (FILE *fp)
+{
+ int ret = 0;
+ glusterfs_graph_t *graph = NULL;
+ FILE *tmp_file = NULL;
+
+ graph = glusterfs_graph_new ();
+ if (!graph)
+ return NULL;
+
+ tmp_file = tmpfile ();
+
+ if (tmp_file == NULL) {
+ gf_log ("parser", GF_LOG_ERROR,
+ "cannot create temparory file");
+
+ glusterfs_graph_destroy (graph);
+ return NULL;
+ }
+
+ ret = preprocess (fp, tmp_file);
+ if (ret < 0) {
+ gf_log ("parser", GF_LOG_ERROR,
+ "parsing of backticks failed");
+
+ glusterfs_graph_destroy (graph);
+ fclose (tmp_file);
+ return NULL;
+ }
+
+ yyin = tmp_file;
+
+ construct = graph;
+
+ ret = yyparse ();
+
+ construct = NULL;
+
+ fclose (tmp_file);
+
+ if (ret == 1) {
+ gf_log ("parser", GF_LOG_DEBUG,
+ "parsing of volfile failed, please review it "
+ "once more");
+
+ glusterfs_graph_destroy (graph);
+ return NULL;
+ }
+
+ return graph;
+}
+