<commit_before>/*
  Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com>
  This file is part of GlusterFS.

  GlusterFS is GF_FREE software; you can redistribute it and/or modify
  it under the terms of the GNU Affero 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
  Affero General Public License for more details.

  You should have received a copy of the GNU Affero General Public License
  along with this program.  If not, see
  <http://www.gnu.org/licenses/>.
*/


#ifndef _CONFIG_H
#define _CONFIG_H
#include "config.h"
#endif

#include <fnmatch.h>

#include "xlator.h"
#include "glusterd.h"
#include "defaults.h"
#include "logging.h"
#include "dict.h"
#include "graph-utils.h"
#include "glusterd-mem-types.h"
#include "cli1.h"
#include "glusterd-volgen.h"


/* dispatch table for VOLUME SET
 * -----------------------------
 *
 * Format of entries:
 *
 * First field is the <key>, for the purpose of looking it up
 * in volume dictionary. Each <key> is of the format "<domain>.<specifier>".
 *
 * Second field is <voltype>.
 *
 * Third field is <option>, if its unset, it's assumed to be
 * the same as <specifier>.
 *
 * Fourth field is <value>. In this context they are used to specify
 * a default. That is, even the volume dict doesn't have a value,
 * we procced as if the default value were set for it.
 *
 * There are two type of entries: basic and special.
 *
 * - Basic entries are the ones where the <option> does _not_ start with
 *   the bang! character ('!').
 *
 *   In their case, <option> is understood as an option for an xlator of
 *   type <voltype>. Their effect is to copy over the volinfo->dict[<key>]
 *   value to all graph nodes of type <voltype> (if such a value is set).
 *
 *   You are free to add entries of this type, they will become functional
 *   just by being present in the table.
 *
 * - Special entries where the <option> starts with the bang!.
 *
 *   They are not applied to all graphs during generation, and you cannot
 *   extend them in a trivial way which could be just picked up. Better
 *   not touch them unless you know what you do.
 *
 * "NODOC" entries are not part of the public interface and are subject
 * to change at any time.
 */

struct volopt_map_entry {
        char *key;
        char *voltype;
        char *option;
        char *value;
};

static struct volopt_map_entry glusterd_volopt_map[] = {
        {"cluster.lookup-unhashed",              "cluster/distribute",        }, /* NODOC */
        {"cluster.min-free-disk",                "cluster/distribute",        }, /* NODOC */

        {"cluster.entry-change-log",             "cluster/replicate",         }, /* NODOC */
        {"cluster.read-subvolume",               "cluster/replicate",         }, /* NODOC */
        {"cluster.background-self-heal-count",   "cluster/replicate",         }, /* NODOC */
        {"cluster.metadata-self-heal",           "cluster/replicate",         }, /* NODOC */
        {"cluster.data-self-heal",               "cluster/replicate",         }, /* NODOC */
        {"cluster.entry-self-heal",              "cluster/replicate",         }, /* NODOC */
        {"cluster.strict-readdir",               "cluster/replicate",         }, /* NODOC */
        {"cluster.self-heal-window-size",        "cluster/replicate",         "data-self-heal-window-size",},
        {"cluster.data-change-log",              "cluster/replicate",         }, /* NODOC */
        {"cluster.metadata-change-log",          "cluster/replicate",         }, /* NODOC */

        {"cluster.stripe-block-size",            "cluster/stripe",            "block-size",},

        {"diagnostics.latency-measurement",      "debug/io-stats",            },
        {"diagnostics.dump-fd-stats",            "debug/io-stats",            },
        {"diagnostics.brick-log-level",          "debug/io-stats",            "log-level",},
        {"diagnostics.client-log-level",         "debug/io-stats",            },

        {"performance.cache-max-file-size",      "performance/io-cache",      "max-file-size",},
        {"performance.cache-min-file-size",      "performance/io-cache",      "min-file-size",},
        {"performance.cache-refresh-timeout",    "performance/io-cache",      "cache-timeout",},
        {"performance.cache-priority",           "performance/io-cache",      "priority",}, /* NODOC */
        {"performance.cache-size",               "performance/io-cache",      },
        {"performance.cache-size",               "performance/quick-read",    },

        {"performance.io-thread-count",          "performance/io-threads",    "thread-count",},

        {"performance.disk-usage-limit",         "performance/quota",         }, /* NODOC */
        {"performance.min-free-disk-limit",      "performance/quota",         }, /* NODOC */

        {"performance.write-behind-window-size", "performance/write-behind",  "cache-size",},

        {"network.frame-timeout",                "protocol/client",           },
        {"network.ping-timeout",                 "protocol/client",           },
        {"network.inode-lru-limit",              "protocol/server",           }, /* NODOC */

        {"auth.allow",                           "protocol/server",           "!server-auth", "*"},
        {"auth.reject",                          "protocol/server",           "!server-auth",},

        {"performance.write-behind",             "performance/write-behind",  "!perf", "on"}, /* NODOC */
        {"performance.read-ahead",               "performance/read-ahead",    "!perf", "on"}, /* NODOC */
        {"performance.io-cache",                 "performance/io-cache",      "!perf", "on"}, /* NODOC */
        {"performance.quick-read",               "performance/quick-read",    "!perf", "on"}, /* NODOC */
        {"performance.stat-prefetch",            "performance/stat-prefetch", "!perf",},      /* NODOC */

        {NULL,                                                                }
};


#define VOLGEN_GET_NFS_DIR(path)                                        \
        do {                                                            \
                glusterd_conf_t *priv = THIS->private;                  \
                snprintf (path, PATH_MAX, "%s/nfs", priv->workdir);     \
        } while (0);                                                    \

#define VOLGEN_GET_VOLUME_DIR(path, volinfo)                            \
        do {                                                            \
                glusterd_conf_t *priv = THIS->private;                  \
                snprintf (path, PATH_MAX, "%s/vols/%s", priv->workdir,  \
                          volinfo->volname);                            \
        } while (0);                                                    \




/*********************************************
 *
 * xlator generation / graph manipulation API
 *
 *********************************************/



static xlator_t *
xlator_instantiate_va (const char *type, const char *format, va_list arg)
{
        xlator_t *xl = NULL;
        char *volname = NULL;
        int ret = 0;

        ret = gf_vasprintf (&volname, format, arg);
        if (ret < 0) {
                volname = NULL;

                goto error;
        }

        xl = GF_CALLOC (1, sizeof (*xl), gf_common_mt_xlator_t);
        if (!xl)
                goto error;
        ret = xlator_set_type_virtual (xl, type);
        if (ret)
                goto error;
        xl->options = get_new_dict();
        if (!xl->options)
                goto error;
        xl->name = volname;

        return xl;

 error:
        gf_log ("", GF_LOG_ERROR, "creating xlator of type %s failed",
                type);
        if (volname)
                GF_FREE (volname);
        if (xl)
                xlator_destroy (xl);

        return NULL;
}

#ifdef __not_used_as_of_now_
static xlator_t *
xlator_instantiate (const char *type, const char *format, ...)
{
        va_list arg;
        xlator_t *xl;

        va_start (arg, format);
        xl = xlator_instantiate_va (type, format, arg);
        va_end (arg);

        return xl;
}
#endif

static int
volgen_xlator_link (xlator_t *pxl, xlator_t *cxl)
{
        int ret = 0;

        ret = glusterfs_xlator_link (pxl, cxl);
        if (ret == -1) {
                gf_log ("", GF_LOG_ERROR,
                        "Out of memory, cannot link xlators %s <- %s",
                        pxl->name, cxl->name);
        }

        return ret;
}

static int
volgen_graph_link (glusterfs_graph_t *graph, xlator_t *xl)
{
        int ret = 0;

        /* no need to care about graph->top here */
        if (graph->first)
                ret = volgen_xlator_link (xl, graph->first);
        if (ret == -1) {
                gf_log ("", GF_LOG_ERROR, "failed to add graph entry %s",
                        xl->name);

                return -1;
        }

        return 0;
}

static xlator_t *
volgen_graph_add_as (glusterfs_graph_t *graph, const char *type,
                     const char *format, ...)
{
        va_list arg;
        xlator_t *xl = NULL;

        va_start (arg, format);
        xl = xlator_instantiate_va (type, format, arg);
        va_end (arg);

        if (!xl)
                return NULL;

        if (volgen_graph_link (graph, xl)) {
                xlator_destroy (xl);

                return NULL;
        } else
                glusterfs_graph_set_first (graph, xl);

        return xl;
}

static xlator_t *
volgen_graph_add_nolink (glusterfs_graph_t *graph, const char *type,
                         const char *format, ...)
{
        va_list arg;
        xlator_t *xl = NULL;

        va_start (arg, format);
        xl = xlator_instantiate_va (type, format, arg);
        va_end (arg);

        if (!xl)
                return NULL;

        glusterfs_graph_set_first (graph, xl);

        return xl;
}

static xlator_t *
volgen_graph_add (glusterfs_graph_t *graph, char *type, char *volname)
{
        char *shorttype = NULL;

        shorttype = strrchr (type, '/');
        GF_ASSERT (shorttype);
        shorttype++;
        GF_ASSERT (*shorttype);

        return volgen_graph_add_as (graph, type, "%s-%s", volname, shorttype);
}

/* XXX Seems there is no such generic routine?
 * Maybe should put to xlator.c ??
 */
static int
xlator_set_option (xlator_t *xl, char *key, char *value)
{
        char *dval     = NULL;

        dval = gf_strdup (value);
        if (!dval) {
                gf_log ("", GF_LOG_ERROR,
                        "failed to set xlator opt: %s[%s] = %s",
                        xl->name, key, value);

                return -1;
        }

        return dict_set_dynstr (xl->options, key, dval);
}

static inline xlator_t *
first_of (glusterfs_graph_t *graph)
{
        return (xlator_t *)graph->first;
}




/**************************
 *
 * Volume generation engine
 *
 **************************/


typedef int (*volgen_opthandler_t) (glusterfs_graph_t *graph,
                                    struct volopt_map_entry *vme,
                                    void *param);

struct opthandler_data {
        glusterfs_graph_t *graph;
        volgen_opthandler_t handler;
        struct volopt_map_entry *vme;
        gf_boolean_t found;
        gf_boolean_t data_t_fake;
        int rv;
        void *param;
};

static void
process_option (dict_t *dict, char *key, data_t *value, void *param)
{
        struct opthandler_data *data = param;
        struct volopt_map_entry vme = {0,};

        if (data->rv)
                return;
        if (fnmatch (data->vme->key, key, 0) != 0)
                return;

        data->found = _gf_true;

        vme.key = key;
        vme.voltype = data->vme->voltype;
        vme.option = data->vme->option;
        if (!vme.option) {
                vme.option = strrchr (key, '.');
                if (vme.option)
                        vme.option++;
                else
                        vme.option = key;
        }
        if (data->data_t_fake)
                vme.value = (char *)value;
        else
                vme.value = value->data;

        data->rv = data->handler (data->graph, &vme, data->param);
}

static int
volgen_graph_set_options_generic (glusterfs_graph_t *graph, dict_t *dict,
                                  void *param, volgen_opthandler_t handler)
{
        struct volopt_map_entry *vme = NULL;
        struct opthandler_data data = {0,};

        data.graph = graph;
        data.handler = handler;
        data.param = param;

        for (vme = glusterd_volopt_map; vme->key; vme++) {
                data.vme = vme;
                data.found = _gf_false;
                data.data_t_fake = _gf_false;

                dict_foreach (dict, process_option, &data);
                if (data.rv)
                        return data.rv;

                if (data.found)
                        continue;

                /* check for default value */

                if (vme->value) {
                        /* stupid hack to be able to reuse dict iterator
                         * in this context
                         */
                        data.data_t_fake = _gf_true;
                        process_option (NULL, vme->key, (data_t *)vme->value,
                                        &data);
                        if (data.rv)
                                return data.rv;
                }
        }

        return 0;
}

static int
basic_option_handler (glusterfs_graph_t *graph, struct volopt_map_entry *vme,
                      void *param)
{
        xlator_t *trav;
        int ret = 0;

        if (vme->option[0] == '!')
                return 0;

        for (trav = first_of (graph); trav; trav = trav->next) {
                if (strcmp (trav->type, vme->voltype) != 0)
                        continue;

                ret = xlator_set_option (trav, vme->option, vme->value);
                if (ret)
                        return -1;
        }

        return 0;
}

static int
volgen_graph_set_options (glusterfs_graph_t *graph, dict_t *dict)
{
        return volgen_graph_set_options_generic (graph, dict, NULL,
                                                 &basic_option_handler);
}

static int
optget_option_handler (glusterfs_graph_t *graph, struct volopt_map_entry *vme,
                       void *param)
{
        struct volopt_map_entry *vme2 = param;

        if (strcmp (vme->key, vme2->key) == 0)
                vme2->value = vme->value;

        return 0;
}

/* This getter considers defaults also. */
int
glusterd_volinfo_get (glusterd_volinfo_t *volinfo, char *key, char **value)
{
        struct volopt_map_entry vme = {0,};
        int ret = 0;

        vme.key = key;

        ret = volgen_graph_set_options_generic (NULL, volinfo->dict, &vme,
                                                &optget_option_handler);
        if (ret) {
                gf_log ("", GF_LOG_ERROR, "Out of memory");

                return -1;
        }

        *value = vme.value;

        return 0;
}

int
glusterd_check_option_exists (char *key)
{
        dict_t *dict = NULL;
        struct volopt_map_entry vme = {0,};
        int ret = 0;

        vme.key = key;

        /* We are getting a bit anal here to avoid typing
         * fnmatch one more time. Orthogonality foremost!
         * The internal logic of looking up in the volopt_map table
         * should be coded exactly once.
         *
         * [[Ha-ha-ha, so now if I ever change the internals then I'll
         * have to update the fnmatch in this comment also :P ]]
         */
        dict = get_new_dict ();
        if (!dict || dict_set_str (dict, key, ""))
                goto oom;

        ret = volgen_graph_set_options_generic (NULL, dict, &vme,
                                                &optget_option_handler);
        dict_destroy (dict);
        if (ret)
                goto oom;

        return !!vme.value;

 oom:
        gf_log ("", GF_LOG_ERROR, "Out of memory");

        return -1;
}

static int
volgen_graph_merge_sub (glusterfs_graph_t *dgraph, glusterfs_graph_t *sgraph)
{
        xlator_t *trav = NULL;

        GF_ASSERT (dgraph->first);

        if (volgen_xlator_link (first_of (dgraph), first_of (sgraph)) == -1)
                return -1;

        for (trav = first_of (dgraph); trav->next; trav = trav->next);

        trav->next = sgraph->first;
        trav->next->prev = trav;
        dgraph->xl_count += sgraph->xl_count;

        return 0;
}

static int
volgen_write_volfile (glusterfs_graph_t *graph, char *filename)
{
        char *ftmp = NULL;
        FILE *f = NULL;

        if (gf_asprintf (&ftmp, "%s.tmp", filename) == -1) {
                ftmp = NULL;

                goto error;
        }

        f = fopen (ftmp, "w");
        if (!f)
                goto error;

        if (glusterfs_graph_print_file (f, graph) == -1)
                goto error;

        if (fclose (f) == -1)
                goto error;

        if (rename (ftmp, filename) == -1)
                goto error;

        GF_FREE (ftmp);

        return 0;

 error:

        if (ftmp)
                GF_FREE (ftmp);
        if (f)
                fclose (f);

        gf_log ("", GF_LOG_ERROR, "failed to create volfile %s", filename);

        return -1;
}

static void
volgen_graph_free (glusterfs_graph_t *graph)
{
        xlator_t *trav = NULL;
        xlator_t *trav_old = NULL;

        for (trav = first_of (graph) ;; trav = trav->next) {
                if (trav_old)
                        xlator_destroy (trav_old);

                trav_old = trav;

                if (!trav)
                        break;
        }
}

static int
build_graph_generic (glusterfs_graph_t *graph, glusterd_volinfo_t *volinfo,
                     dict_t *mod_dict, void *param,
                     int (*builder) (glusterfs_graph_t *graph,
                                     glusterd_volinfo_t *volinfo,
                                     dict_t *set_dict, void *param))
{
        dict_t *set_dict = NULL;
        int ret = 0;

        if (mod_dict) {
                set_dict = dict_copy (volinfo->dict, NULL);
                if (!set_dict)
                        return -1;
                dict_copy (mod_dict, set_dict);
                /* XXX dict_copy swallows errors */
        } else
                set_dict = volinfo->dict;

        ret = builder (graph, volinfo, set_dict, param);
        if (!ret)
                ret = volgen_graph_set_options (graph, set_dict);

        if (mod_dict)
                dict_destroy (set_dict);

        return ret;
}

static void
get_vol_transport_type (glusterd_volinfo_t *volinfo, char *tt)
{
        volinfo->transport_type == GF_TRANSPORT_RDMA ?
        strcpy (tt, "rdma"):
        strcpy (tt, "tcp");
}

static int
server_auth_option_handler (glusterfs_graph_t *graph,
                            struct volopt_map_entry *vme, void *param)
{
        xlator_t *xl = NULL;
        xlator_list_t *trav = NULL;
        char *aa = NULL;
        int   ret   = 0;

        if (strcmp (vme->option, "!server-auth") != 0)
                return 0;

        xl = first_of (graph);

        for (trav = xl->children; trav; trav = trav->next) {
                ret = gf_asprintf (&aa, "auth.addr.%s.%s", trav->xlator->name,
                                   vme->key);
                if (ret != -1) {
                        ret = xlator_set_option (xl, aa, vme->value);
                        GF_FREE (aa);
                }<commit_msg>glusterd-volgen: fix the auth.allow/auth.reject keys<commit_after>/*
  Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com>
  This file is part of GlusterFS.

  GlusterFS is GF_FREE software; you can redistribute it and/or modify
  it under the terms of the GNU Affero 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
  Affero General Public License for more details.

  You should have received a copy of the GNU Affero General Public License
  along with this program.  If not, see
  <http://www.gnu.org/licenses/>.
*/


#ifndef _CONFIG_H
#define _CONFIG_H
#include "config.h"
#endif

#include <fnmatch.h>

#include "xlator.h"
#include "glusterd.h"
#include "defaults.h"
#include "logging.h"
#include "dict.h"
#include "graph-utils.h"
#include "glusterd-mem-types.h"
#include "cli1.h"
#include "glusterd-volgen.h"


/* dispatch table for VOLUME SET
 * -----------------------------
 *
 * Format of entries:
 *
 * First field is the <key>, for the purpose of looking it up
 * in volume dictionary. Each <key> is of the format "<domain>.<specifier>".
 *
 * Second field is <voltype>.
 *
 * Third field is <option>, if its unset, it's assumed to be
 * the same as <specifier>.
 *
 * Fourth field is <value>. In this context they are used to specify
 * a default. That is, even the volume dict doesn't have a value,
 * we procced as if the default value were set for it.
 *
 * There are two type of entries: basic and special.
 *
 * - Basic entries are the ones where the <option> does _not_ start with
 *   the bang! character ('!').
 *
 *   In their case, <option> is understood as an option for an xlator of
 *   type <voltype>. Their effect is to copy over the volinfo->dict[<key>]
 *   value to all graph nodes of type <voltype> (if such a value is set).
 *
 *   You are free to add entries of this type, they will become functional
 *   just by being present in the table.
 *
 * - Special entries where the <option> starts with the bang!.
 *
 *   They are not applied to all graphs during generation, and you cannot
 *   extend them in a trivial way which could be just picked up. Better
 *   not touch them unless you know what you do.
 *
 * "NODOC" entries are not part of the public interface and are subject
 * to change at any time.
 */

struct volopt_map_entry {
        char *key;
        char *voltype;
        char *option;
        char *value;
};

static struct volopt_map_entry glusterd_volopt_map[] = {
        {"cluster.lookup-unhashed",              "cluster/distribute",        }, /* NODOC */
        {"cluster.min-free-disk",                "cluster/distribute",        }, /* NODOC */

        {"cluster.entry-change-log",             "cluster/replicate",         }, /* NODOC */
        {"cluster.read-subvolume",               "cluster/replicate",         }, /* NODOC */
        {"cluster.background-self-heal-count",   "cluster/replicate",         }, /* NODOC */
        {"cluster.metadata-self-heal",           "cluster/replicate",         }, /* NODOC */
        {"cluster.data-self-heal",               "cluster/replicate",         }, /* NODOC */
        {"cluster.entry-self-heal",              "cluster/replicate",         }, /* NODOC */
        {"cluster.strict-readdir",               "cluster/replicate",         }, /* NODOC */
        {"cluster.self-heal-window-size",        "cluster/replicate",         "data-self-heal-window-size",},
        {"cluster.data-change-log",              "cluster/replicate",         }, /* NODOC */
        {"cluster.metadata-change-log",          "cluster/replicate",         }, /* NODOC */

        {"cluster.stripe-block-size",            "cluster/stripe",            "block-size",},

        {"diagnostics.latency-measurement",      "debug/io-stats",            },
        {"diagnostics.dump-fd-stats",            "debug/io-stats",            },
        {"diagnostics.brick-log-level",          "debug/io-stats",            "log-level",},
        {"diagnostics.client-log-level",         "debug/io-stats",            },

        {"performance.cache-max-file-size",      "performance/io-cache",      "max-file-size",},
        {"performance.cache-min-file-size",      "performance/io-cache",      "min-file-size",},
        {"performance.cache-refresh-timeout",    "performance/io-cache",      "cache-timeout",},
        {"performance.cache-priority",           "performance/io-cache",      "priority",}, /* NODOC */
        {"performance.cache-size",               "performance/io-cache",      },
        {"performance.cache-size",               "performance/quick-read",    },

        {"performance.io-thread-count",          "performance/io-threads",    "thread-count",},

        {"performance.disk-usage-limit",         "performance/quota",         }, /* NODOC */
        {"performance.min-free-disk-limit",      "performance/quota",         }, /* NODOC */

        {"performance.write-behind-window-size", "performance/write-behind",  "cache-size",},

        {"network.frame-timeout",                "protocol/client",           },
        {"network.ping-timeout",                 "protocol/client",           },
        {"network.inode-lru-limit",              "protocol/server",           }, /* NODOC */

        {"auth.allow",                           "protocol/server",           "!server-auth", "*"},
        {"auth.reject",                          "protocol/server",           "!server-auth",},

        {"performance.write-behind",             "performance/write-behind",  "!perf", "on"}, /* NODOC */
        {"performance.read-ahead",               "performance/read-ahead",    "!perf", "on"}, /* NODOC */
        {"performance.io-cache",                 "performance/io-cache",      "!perf", "on"}, /* NODOC */
        {"performance.quick-read",               "performance/quick-read",    "!perf", "on"}, /* NODOC */
        {"performance.stat-prefetch",            "performance/stat-prefetch", "!perf",},      /* NODOC */

        {NULL,                                                                }
};


#define VOLGEN_GET_NFS_DIR(path)                                        \
        do {                                                            \
                glusterd_conf_t *priv = THIS->private;                  \
                snprintf (path, PATH_MAX, "%s/nfs", priv->workdir);     \
        } while (0);                                                    \

#define VOLGEN_GET_VOLUME_DIR(path, volinfo)                            \
        do {                                                            \
                glusterd_conf_t *priv = THIS->private;                  \
                snprintf (path, PATH_MAX, "%s/vols/%s", priv->workdir,  \
                          volinfo->volname);                            \
        } while (0);                                                    \




/*********************************************
 *
 * xlator generation / graph manipulation API
 *
 *********************************************/



static xlator_t *
xlator_instantiate_va (const char *type, const char *format, va_list arg)
{
        xlator_t *xl = NULL;
        char *volname = NULL;
        int ret = 0;

        ret = gf_vasprintf (&volname, format, arg);
        if (ret < 0) {
                volname = NULL;

                goto error;
        }

        xl = GF_CALLOC (1, sizeof (*xl), gf_common_mt_xlator_t);
        if (!xl)
                goto error;
        ret = xlator_set_type_virtual (xl, type);
        if (ret)
                goto error;
        xl->options = get_new_dict();
        if (!xl->options)
                goto error;
        xl->name = volname;

        return xl;

 error:
        gf_log ("", GF_LOG_ERROR, "creating xlator of type %s failed",
                type);
        if (volname)
                GF_FREE (volname);
        if (xl)
                xlator_destroy (xl);

        return NULL;
}

#ifdef __not_used_as_of_now_
static xlator_t *
xlator_instantiate (const char *type, const char *format, ...)
{
        va_list arg;
        xlator_t *xl;

        va_start (arg, format);
        xl = xlator_instantiate_va (type, format, arg);
        va_end (arg);

        return xl;
}
#endif

static int
volgen_xlator_link (xlator_t *pxl, xlator_t *cxl)
{
        int ret = 0;

        ret = glusterfs_xlator_link (pxl, cxl);
        if (ret == -1) {
                gf_log ("", GF_LOG_ERROR,
                        "Out of memory, cannot link xlators %s <- %s",
                        pxl->name, cxl->name);
        }

        return ret;
}

static int
volgen_graph_link (glusterfs_graph_t *graph, xlator_t *xl)
{
        int ret = 0;

        /* no need to care about graph->top here */
        if (graph->first)
                ret = volgen_xlator_link (xl, graph->first);
        if (ret == -1) {
                gf_log ("", GF_LOG_ERROR, "failed to add graph entry %s",
                        xl->name);

                return -1;
        }

        return 0;
}

static xlator_t *
volgen_graph_add_as (glusterfs_graph_t *graph, const char *type,
                     const char *format, ...)
{
        va_list arg;
        xlator_t *xl = NULL;

        va_start (arg, format);
        xl = xlator_instantiate_va (type, format, arg);
        va_end (arg);

        if (!xl)
                return NULL;

        if (volgen_graph_link (graph, xl)) {
                xlator_destroy (xl);

                return NULL;
        } else
                glusterfs_graph_set_first (graph, xl);

        return xl;
}

static xlator_t *
volgen_graph_add_nolink (glusterfs_graph_t *graph, const char *type,
                         const char *format, ...)
{
        va_list arg;
        xlator_t *xl = NULL;

        va_start (arg, format);
        xl = xlator_instantiate_va (type, format, arg);
        va_end (arg);

        if (!xl)
                return NULL;

        glusterfs_graph_set_first (graph, xl);

        return xl;
}

static xlator_t *
volgen_graph_add (glusterfs_graph_t *graph, char *type, char *volname)
{
        char *shorttype = NULL;

        shorttype = strrchr (type, '/');
        GF_ASSERT (shorttype);
        shorttype++;
        GF_ASSERT (*shorttype);

        return volgen_graph_add_as (graph, type, "%s-%s", volname, shorttype);
}

/* XXX Seems there is no such generic routine?
 * Maybe should put to xlator.c ??
 */
static int
xlator_set_option (xlator_t *xl, char *key, char *value)
{
        char *dval     = NULL;

        dval = gf_strdup (value);
        if (!dval) {
                gf_log ("", GF_LOG_ERROR,
                        "failed to set xlator opt: %s[%s] = %s",
                        xl->name, key, value);

                return -1;
        }

        return dict_set_dynstr (xl->options, key, dval);
}

static inline xlator_t *
first_of (glusterfs_graph_t *graph)
{
        return (xlator_t *)graph->first;
}




/**************************
 *
 * Volume generation engine
 *
 **************************/


typedef int (*volgen_opthandler_t) (glusterfs_graph_t *graph,
                                    struct volopt_map_entry *vme,
                                    void *param);

struct opthandler_data {
        glusterfs_graph_t *graph;
        volgen_opthandler_t handler;
        struct volopt_map_entry *vme;
        gf_boolean_t found;
        gf_boolean_t data_t_fake;
        int rv;
        void *param;
};

static void
process_option (dict_t *dict, char *key, data_t *value, void *param)
{
        struct opthandler_data *data = param;
        struct volopt_map_entry vme = {0,};

        if (data->rv)
                return;
        if (fnmatch (data->vme->key, key, 0) != 0)
                return;

        data->found = _gf_true;

        vme.key = key;
        vme.voltype = data->vme->voltype;
        vme.option = data->vme->option;
        if (!vme.option) {
                vme.option = strrchr (key, '.');
                if (vme.option)
                        vme.option++;
                else
                        vme.option = key;
        }
        if (data->data_t_fake)
                vme.value = (char *)value;
        else
                vme.value = value->data;

        data->rv = data->handler (data->graph, &vme, data->param);
}

static int
volgen_graph_set_options_generic (glusterfs_graph_t *graph, dict_t *dict,
                                  void *param, volgen_opthandler_t handler)
{
        struct volopt_map_entry *vme = NULL;
        struct opthandler_data data = {0,};

        data.graph = graph;
        data.handler = handler;
        data.param = param;

        for (vme = glusterd_volopt_map; vme->key; vme++) {
                data.vme = vme;
                data.found = _gf_false;
                data.data_t_fake = _gf_false;

                dict_foreach (dict, process_option, &data);
                if (data.rv)
                        return data.rv;

                if (data.found)
                        continue;

                /* check for default value */

                if (vme->value) {
                        /* stupid hack to be able to reuse dict iterator
                         * in this context
                         */
                        data.data_t_fake = _gf_true;
                        process_option (NULL, vme->key, (data_t *)vme->value,
                                        &data);
                        if (data.rv)
                                return data.rv;
                }
        }

        return 0;
}

static int
basic_option_handler (glusterfs_graph_t *graph, struct volopt_map_entry *vme,
                      void *param)
{
        xlator_t *trav;
        int ret = 0;

        if (vme->option[0] == '!')
                return 0;

        for (trav = first_of (graph); trav; trav = trav->next) {
                if (strcmp (trav->type, vme->voltype) != 0)
                        continue;

                ret = xlator_set_option (trav, vme->option, vme->value);
                if (ret)
                        return -1;
        }

        return 0;
}

static int
volgen_graph_set_options (glusterfs_graph_t *graph, dict_t *dict)
{
        return volgen_graph_set_options_generic (graph, dict, NULL,
                                                 &basic_option_handler);
}

static int
optget_option_handler (glusterfs_graph_t *graph, struct volopt_map_entry *vme,
                       void *param)
{
        struct volopt_map_entry *vme2 = param;

        if (strcmp (vme->key, vme2->key) == 0)
                vme2->value = vme->value;

        return 0;
}

/* This getter considers defaults also. */
int
glusterd_volinfo_get (glusterd_volinfo_t *volinfo, char *key, char **value)
{
        struct volopt_map_entry vme = {0,};
        int ret = 0;

        vme.key = key;

        ret = volgen_graph_set_options_generic (NULL, volinfo->dict, &vme,
                                                &optget_option_handler);
        if (ret) {
                gf_log ("", GF_LOG_ERROR, "Out of memory");

                return -1;
        }

        *value = vme.value;

        return 0;
}

int
glusterd_check_option_exists (char *key)
{
        dict_t *dict = NULL;
        struct volopt_map_entry vme = {0,};
        int ret = 0;

        vme.key = key;

        /* We are getting a bit anal here to avoid typing
         * fnmatch one more time. Orthogonality foremost!
         * The internal logic of looking up in the volopt_map table
         * should be coded exactly once.
         *
         * [[Ha-ha-ha, so now if I ever change the internals then I'll
         * have to update the fnmatch in this comment also :P ]]
         */
        dict = get_new_dict ();
        if (!dict || dict_set_str (dict, key, ""))
                goto oom;

        ret = volgen_graph_set_options_generic (NULL, dict, &vme,
                                                &optget_option_handler);
        dict_destroy (dict);
        if (ret)
                goto oom;

        return !!vme.value;

 oom:
        gf_log ("", GF_LOG_ERROR, "Out of memory");

        return -1;
}

static int
volgen_graph_merge_sub (glusterfs_graph_t *dgraph, glusterfs_graph_t *sgraph)
{
        xlator_t *trav = NULL;

        GF_ASSERT (dgraph->first);

        if (volgen_xlator_link (first_of (dgraph), first_of (sgraph)) == -1)
                return -1;

        for (trav = first_of (dgraph); trav->next; trav = trav->next);

        trav->next = sgraph->first;
        trav->next->prev = trav;
        dgraph->xl_count += sgraph->xl_count;

        return 0;
}

static int
volgen_write_volfile (glusterfs_graph_t *graph, char *filename)
{
        char *ftmp = NULL;
        FILE *f = NULL;

        if (gf_asprintf (&ftmp, "%s.tmp", filename) == -1) {
                ftmp = NULL;

                goto error;
        }

        f = fopen (ftmp, "w");
        if (!f)
                goto error;

        if (glusterfs_graph_print_file (f, graph) == -1)
                goto error;

        if (fclose (f) == -1)
                goto error;

        if (rename (ftmp, filename) == -1)
                goto error;

        GF_FREE (ftmp);

        return 0;

 error:

        if (ftmp)
                GF_FREE (ftmp);
        if (f)
                fclose (f);

        gf_log ("", GF_LOG_ERROR, "failed to create volfile %s", filename);

        return -1;
}

static void
volgen_graph_free (glusterfs_graph_t *graph)
{
        xlator_t *trav = NULL;
        xlator_t *trav_old = NULL;

        for (trav = first_of (graph) ;; trav = trav->next) {
                if (trav_old)
                        xlator_destroy (trav_old);

                trav_old = trav;

                if (!trav)
                        break;
        }
}

static int
build_graph_generic (glusterfs_graph_t *graph, glusterd_volinfo_t *volinfo,
                     dict_t *mod_dict, void *param,
                     int (*builder) (glusterfs_graph_t *graph,
                                     glusterd_volinfo_t *volinfo,
                                     dict_t *set_dict, void *param))
{
        dict_t *set_dict = NULL;
        int ret = 0;

        if (mod_dict) {
                set_dict = dict_copy (volinfo->dict, NULL);
                if (!set_dict)
                        return -1;
                dict_copy (mod_dict, set_dict);
                /* XXX dict_copy swallows errors */
        } else
                set_dict = volinfo->dict;

        ret = builder (graph, volinfo, set_dict, param);
        if (!ret)
                ret = volgen_graph_set_options (graph, set_dict);

        if (mod_dict)
                dict_destroy (set_dict);

        return ret;
}

static void
get_vol_transport_type (glusterd_volinfo_t *volinfo, char *tt)
{
        volinfo->transport_type == GF_TRANSPORT_RDMA ?
        strcpy (tt, "rdma"):
        strcpy (tt, "tcp");
}

static int
server_auth_option_handler (glusterfs_graph_t *graph,
                            struct volopt_map_entry *vme, void *param)
{
        xlator_t *xl = NULL;
        xlator_list_t *trav = NULL;
        char *aa = NULL;
        int   ret   = 0;
        char *key = NULL;

        if (strcmp (vme->option, "!server-auth") != 0)
                return 0;

        xl = first_of (graph);

        /* from 'auth.allow' -> 'allow', and 'auth.reject' -> 'reject' */
        key = strchr (vme->key, '.') + 1;

        for (trav = xl->children; trav; trav = trav->next) {
                ret = gf_asprintf (&aa, "auth.addr.%s.%s", trav->xlator->name,
                                   key);
                if (ret != -1) {
                        ret = xlator_set_option (xl, aa, vme->value);
                        GF_FREE (aa);
                }
{'fraction_non_alphanumeric': 0.12251163961243236, 'fraction_numerical': 0.0028438404429344407, 'mean_word_length': 1.55208734746307, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 173, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 50, 'profanity_word_list.json': 0, 'sexual_word_list.json': 2, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4507109', 'n_tokens_mistral': 12319, 'n_tokens_neox': 11598, 'n_words': 3077}