diff options
Diffstat (limited to 'libglusterfs/src/graph.y')
| -rw-r--r-- | libglusterfs/src/graph.y | 621 |
1 files changed, 621 insertions, 0 deletions
diff --git a/libglusterfs/src/graph.y b/libglusterfs/src/graph.y new file mode 100644 index 00000000000..e63febdc08b --- /dev/null +++ b/libglusterfs/src/graph.y @@ -0,0 +1,621 @@ +/* + Copyright (c) 2006-2012 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +%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 <pthread.h> + +#define RELAX_POISONING + +#include "glusterfs/xlator.h" +#include "glusterfs/graph-utils.h" +#include "glusterfs/logging.h" +#include "glusterfs/syscall.h" +#include "glusterfs/libglusterfs-messages.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 graphyyerror (const char *); +int graphyylex (); +%} + + +%% +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 graphyylineno; + + gf_msg ("parser", GF_LOG_ERROR, 0, LG_MSG_VOLFILE_PARSE_ERROR, + "Volume %s, before line %d: Please specify volume type", + curr->name, graphyylineno); + return; +} + + +static void +sub_error (void) +{ + extern int graphyylineno; + + gf_msg ("parser", GF_LOG_ERROR, 0, LG_MSG_VOLFILE_PARSE_ERROR, + "Volume %s, before line %d: Please specify subvolumes", + curr->name, graphyylineno); + return; +} + + +static void +option_error (void) +{ + extern int graphyylineno; + + gf_msg ("parser", GF_LOG_ERROR, 0, LG_MSG_VOLFILE_PARSE_ERROR, + "Volume %s, before line %d: Please specify " + "option <key> <value>", + curr->name, graphyylineno); + return; +} + + +static int +new_volume (char *name) +{ + extern int graphyylineno; + xlator_t *trav = NULL; + int ret = 0; + + if (!name) { + gf_msg_debug ("parser", 0,"Invalid argument name"); + ret = -1; + goto out; + } + + if (curr) { + gf_msg ("parser", GF_LOG_ERROR, 0, LG_MSG_INVALID_ENTRY, + "new volume (%s) definition in line %d unexpected", + name, graphyylineno); + ret = -1; + goto out; + } + + curr = (void *) GF_CALLOC (1, sizeof (*curr), + gf_common_mt_xlator_t); + + if (!curr) { + ret = -1; + goto out; + } + + trav = construct->first; + + while (trav) { + if (!strcmp (name, trav->name)) { + gf_msg ("parser", GF_LOG_ERROR, 0, + LG_MSG_VOLFILE_PARSE_ERROR, "Line %d: volume " + "'%s' defined again", graphyylineno, name); + ret = -1; + goto out; + } + trav = trav->next; + } + + curr->name = gf_strdup (name); + if (!curr->name) { + GF_FREE (curr); + ret = -1; + goto out; + } + + INIT_LIST_HEAD(&curr->volume_options); + curr->options = dict_new (); + + 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++; + curr->xl_id = construct->xl_count; + + gf_msg_trace ("parser", 0, "New node for '%s'", name); + +out: + GF_FREE (name); + + return ret; +} + + +static int +volume_type (char *type) +{ + extern int graphyylineno; + int32_t ret = 0; + + if (!type) { + gf_msg_debug ("parser", 0, "Invalid argument type"); + ret = -1; + goto out; + } + + ret = xlator_set_type (curr, type); + if (ret) { + gf_msg ("parser", GF_LOG_ERROR, 0, LG_MSG_INVALID_ENTRY, + "Volume '%s', line %d: type '%s' is not valid or " + "not found on this machine", + curr->name, graphyylineno, type); + ret = -1; + goto out; + } + + gf_msg_trace ("parser", 0, "Type:%s:%s", curr->name, type); + +out: + GF_FREE (type); + + return 0; +} + + +static int +volume_option (char *key, char *value) +{ + extern int graphyylineno; + int ret = 0; + char *set_value = NULL; + + if (!key || !value){ + gf_msg ("parser", GF_LOG_ERROR, 0, + LG_MSG_INVALID_VOLFILE_ENTRY, "Invalid argument"); + ret = -1; + goto out; + } + + set_value = gf_strdup (value); + ret = dict_set_option (curr->options, key, set_value); + + if (ret == 1) { + gf_msg ("parser", GF_LOG_ERROR, 0, + LG_MSG_INVALID_VOLFILE_ENTRY, "Volume '%s', line %d: " + "duplicate entry ('option %s') present", + curr->name, graphyylineno, key); + ret = -1; + goto out; + } + + gf_msg_trace ("parser", 0, "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 graphyylineno; + xlator_t *trav = NULL; + int ret = 0; + + if (!sub) { + gf_msg ("parser", GF_LOG_ERROR, 0, LG_MSG_INVALID_ENTRY, + "Invalid subvolumes argument"); + ret = -1; + goto out; + } + + trav = construct->first; + + while (trav) { + if (!strcmp (sub, trav->name)) + break; + trav = trav->next; + } + + if (!trav) { + gf_msg ("parser", GF_LOG_ERROR, 0, LG_MSG_SUB_VOLUME_ERROR, + "Volume '%s', line %d: subvolume '%s' is not defined " + "prior to usage",curr->name, graphyylineno, sub); + ret = -1; + goto out; + } + + if (trav == curr) { + gf_msg ("parser", GF_LOG_ERROR, 0, LG_MSG_INVALID_ENTRY, + "Volume '%s', line %d: has '%s' itself as subvolume", + curr->name, graphyylineno, sub); + ret = -1; + goto out; + } + + ret = glusterfs_xlator_link (curr, trav); + if (ret) { + ret = -1; + goto out; + } + + gf_msg_trace ("parser", 0, "child:%s->%s", curr->name, sub); + +out: + GF_FREE (sub); + + return 0; +} + + +static int +volume_end (void) +{ + if (!curr->fops) { + gf_msg ("parser", GF_LOG_ERROR, 0, LG_MSG_VOLUME_ERROR, + "\"type\" not specified for volume %s", curr->name); + return -1; + } + gf_msg_trace ("parser", 0, "end:%s", curr->name); + + curr = NULL; + return 0; +} + + +int +graphyywrap () +{ + return 1; +} + + +int +graphyyerror (const char *str) +{ + extern char *graphyytext; + extern int graphyylineno; + + if (curr && curr->name && graphyytext) { + if (!strcmp (graphyytext, "volume")) { + gf_msg ("parser", GF_LOG_ERROR, 0, + LG_MSG_VOLUME_ERROR, "'end-volume' not" + " defined for volume '%s'", curr->name); + } else if (!strcmp (graphyytext, "type")) { + gf_msg ("parser", GF_LOG_ERROR, 0, LG_MSG_VOLUME_ERROR, + "line %d: duplicate 'type' defined for " + "volume '%s'", graphyylineno, curr->name); + } else if (!strcmp (graphyytext, "subvolumes")) { + gf_msg ("parser", GF_LOG_ERROR, 0, + LG_MSG_SUB_VOLUME_ERROR, "line %d: duplicate " + "'subvolumes' defined for volume '%s'", + graphyylineno, curr->name); + } else if (curr) { + gf_msg ("parser", GF_LOG_ERROR, 0, LG_MSG_SYNTAX_ERROR, + "syntax error: line %d (volume '%s'): \"%s\"" + "\nallowed tokens are 'volume', 'type', " + "'subvolumes', 'option', 'end-volume'()", + graphyylineno, curr->name, graphyytext); + } else { + gf_msg ("parser", GF_LOG_ERROR, 0, LG_MSG_SYNTAX_ERROR, + "syntax error: line %d (just after volume " + "'%s'): \"%s\"\n(%s)", + graphyylineno, curr->name, graphyytext, + "allowed tokens are 'volume', 'type', " + "'subvolumes', 'option', 'end-volume'"); + } + } else { + gf_msg ("parser", GF_LOG_ERROR, 0, LG_MSG_SYNTAX_ERROR, + "syntax error in line %d: \"%s\"\n" + "(allowed tokens are 'volume', 'type', " + "'subvolumes', 'option', 'end-volume')\n", + graphyylineno, graphyytext); + } + + 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_msg ("parser", GF_LOG_ERROR, 0, LG_MSG_FILE_OP_FAILED, + "%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 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) { + return -1; + } + + result = GF_CALLOC (cmd_buf_size * 2, 1, + gf_common_mt_char); + if (result == NULL) { + GF_FREE (cmd); + 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'; + } + + 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) { + GF_FREE (result); + 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_msg ("parser", GF_LOG_ERROR, 0, LG_MSG_VOLUME_ERROR, + "Unterminated backtick in volume specification 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 *graphyyin; + +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); + + pthread_mutex_init(&graph->mutex, NULL); + pthread_cond_init(&graph->child_down_cond, NULL); + + gettimeofday (&graph->dob, NULL); + + return graph; +} + + +glusterfs_graph_t * +glusterfs_graph_construct (FILE *fp) +{ + int ret = 0; + int tmp_fd = -1; + glusterfs_graph_t *graph = NULL; + FILE *tmp_file = NULL; + char template[] = "/tmp/tmp.XXXXXX"; + static pthread_mutex_t graph_mutex = PTHREAD_MUTEX_INITIALIZER; + + graph = glusterfs_graph_new (); + if (!graph) + goto err; + + /* coverity[secure_temp] mkstemp uses 0600 as the mode and is safe */ + tmp_fd = mkstemp (template); + if (-1 == tmp_fd) + goto err; + + ret = sys_unlink (template); + if (ret < 0) { + gf_msg ("parser", GF_LOG_WARNING, 0, LG_MSG_FILE_OP_FAILED, + "Unable to delete file: %s", template); + } + + tmp_file = fdopen (tmp_fd, "w+b"); + if (!tmp_file) + goto err; + + ret = preprocess (fp, tmp_file); + if (ret < 0) { + gf_msg ("parser", GF_LOG_ERROR, 0, LG_MSG_BACKTICK_PARSE_FAILED, + "parsing of backticks failed"); + goto err; + } + + pthread_mutex_lock (&graph_mutex); + { + graphyyin = tmp_file; + construct = graph; + ret = yyparse (); + construct = NULL; + } + pthread_mutex_unlock (&graph_mutex); + + if (ret == 1) { + gf_msg_debug ("parser", 0, "parsing of volfile failed, please " + "review it once more"); + goto err; + } + + fclose (tmp_file); + return graph; +err: + if (tmp_file) { + fclose (tmp_file); + } else { + gf_msg ("parser", GF_LOG_ERROR, 0, LG_MSG_FILE_OP_FAILED, + "cannot create temporary file"); + if (-1 != tmp_fd) + sys_close (tmp_fd); + } + + glusterfs_graph_destroy (graph); + return NULL; +} + |
