152ff7e0dSBrett Lawson/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
252ff7e0dSBrett Lawson/*
36bc025baSBrett Lawson *     Copyright 2011-2020 Couchbase, Inc.
452ff7e0dSBrett Lawson *
552ff7e0dSBrett Lawson *   Licensed under the Apache License, Version 2.0 (the "License");
652ff7e0dSBrett Lawson *   you may not use this file except in compliance with the License.
752ff7e0dSBrett Lawson *   You may obtain a copy of the License at
852ff7e0dSBrett Lawson *
952ff7e0dSBrett Lawson *       http://www.apache.org/licenses/LICENSE-2.0
1052ff7e0dSBrett Lawson *
1152ff7e0dSBrett Lawson *   Unless required by applicable law or agreed to in writing, software
1252ff7e0dSBrett Lawson *   distributed under the License is distributed on an "AS IS" BASIS,
1352ff7e0dSBrett Lawson *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1452ff7e0dSBrett Lawson *   See the License for the specific language governing permissions and
1552ff7e0dSBrett Lawson *   limitations under the License.
1652ff7e0dSBrett Lawson */
1752ff7e0dSBrett Lawson
183e0810faSBrett Lawson#define LCB_IOPS_V12_NO_DEPRECATE 1 /* For Ruby */
193e0810faSBrett Lawson
2052ff7e0dSBrett Lawson#include "internal.h"
2152ff7e0dSBrett Lawson#include "plugins/io/select/select_io_opts.h"
22399d4f65SBrett Lawson#include <libcouchbase/plugins/io/bsdio-inl.c>
2352ff7e0dSBrett Lawson
24d98a05aeSBrett Lawson#ifdef LCB_EMBED_PLUGIN_LIBEVENT
25fab72700SBrett LawsonLIBCOUCHBASE_API lcb_STATUS lcb_create_libevent_io_opts(int, lcb_io_opt_t *, void *);
26d98a05aeSBrett Lawson#endif
27d98a05aeSBrett Lawson
28fab72700SBrett Lawsontypedef lcb_STATUS (*create_func_t)(int version, lcb_io_opt_t *io, void *cookie);
2952ff7e0dSBrett Lawson
3052ff7e0dSBrett Lawson#ifdef _WIN32
3152ff7e0dSBrett LawsonLIBCOUCHBASE_API
32fab72700SBrett Lawsonlcb_STATUS lcb_iocp_new_iops(int, lcb_io_opt_t *, void *);
3352ff7e0dSBrett Lawson#define DEFAULT_IOPS LCB_IO_OPS_WINIOCP
3452ff7e0dSBrett Lawson#else
3552ff7e0dSBrett Lawson#define DEFAULT_IOPS LCB_IO_OPS_LIBEVENT
3652ff7e0dSBrett Lawson#endif
3752ff7e0dSBrett Lawson
3852ff7e0dSBrett Lawsontypedef struct {
3952ff7e0dSBrett Lawson    /** The "base" name of the plugin */
4052ff7e0dSBrett Lawson    const char *base;
4152ff7e0dSBrett Lawson
4252ff7e0dSBrett Lawson    /** Corresponding type */
4352ff7e0dSBrett Lawson    lcb_io_ops_type_t iotype;
4452ff7e0dSBrett Lawson
4552ff7e0dSBrett Lawson    /** Filename */
4652ff7e0dSBrett Lawson    const char *soname;
4752ff7e0dSBrett Lawson
4852ff7e0dSBrett Lawson    /** Symbol used to initialize the plugin */
4952ff7e0dSBrett Lawson    const char *symbol;
5052ff7e0dSBrett Lawson
5152ff7e0dSBrett Lawson    /** Function to create the iops (if builtin) */
5252ff7e0dSBrett Lawson    create_func_t create;
5352ff7e0dSBrett Lawson
5452ff7e0dSBrett Lawson    /** Static buffers if reading from the environment */
5552ff7e0dSBrett Lawson    char s_soname[PATH_MAX];
5652ff7e0dSBrett Lawson    char s_symbol[256];
5752ff7e0dSBrett Lawson} plugin_info;
5852ff7e0dSBrett Lawson
5952ff7e0dSBrett Lawson#ifdef __APPLE__
60fab72700SBrett Lawson#define PLUGIN_SO(NAME) "libcouchbase_" NAME ".dylib"
6152ff7e0dSBrett Lawson#elif defined(_WIN32)
6252ff7e0dSBrett Lawson/** Trailing period intentional. See docs for LoadLibrary */
6387cb69acSBrett Lawson#if (_DEBUG && _MSC_VER)
64fab72700SBrett Lawson#define PLUGIN_SO(NAME) "libcouchbase_" NAME "_d.dll."
6587cb69acSBrett Lawson#else
66fab72700SBrett Lawson#define PLUGIN_SO(NAME) "libcouchbase_" NAME ".dll."
6787cb69acSBrett Lawson#endif /* _DEBUG */
6852ff7e0dSBrett Lawson#else
69fab72700SBrett Lawson#define PLUGIN_SO(NAME) "libcouchbase_" NAME ".so"
7052ff7e0dSBrett Lawson#endif
7152ff7e0dSBrett Lawson
72fab72700SBrett Lawson#define PLUGIN_SYMBOL(NAME) "lcb_create_" NAME "_io_opts"
7352ff7e0dSBrett Lawson
74fab72700SBrett Lawson#define BUILTIN_CORE(name, type, create)                                                                               \
75fab72700SBrett Lawson    {                                                                                                                  \
76fab72700SBrett Lawson        name, type, NULL, NULL, create, {0},                                                                           \
77fab72700SBrett Lawson        {                                                                                                              \
78fab72700SBrett Lawson            0                                                                                                          \
79fab72700SBrett Lawson        }                                                                                                              \
80fab72700SBrett Lawson    }
8152ff7e0dSBrett Lawson
82fab72700SBrett Lawson#define BUILTIN_DL(name, type)                                                                                         \
83fab72700SBrett Lawson    {                                                                                                                  \
84fab72700SBrett Lawson        name, type, PLUGIN_SO(name), PLUGIN_SYMBOL(name), NULL, {0},                                                   \
85fab72700SBrett Lawson        {                                                                                                              \
86fab72700SBrett Lawson            0                                                                                                          \
87fab72700SBrett Lawson        }                                                                                                              \
88fab72700SBrett Lawson    }
8952ff7e0dSBrett Lawson
90fab72700SBrett Lawsonstatic plugin_info builtin_plugins[] = {BUILTIN_CORE("select", LCB_IO_OPS_SELECT, lcb_create_select_io_opts),
91fab72700SBrett Lawson                                        BUILTIN_CORE("winsock", LCB_IO_OPS_WINSOCK, lcb_create_select_io_opts),
9252ff7e0dSBrett Lawson
9352ff7e0dSBrett Lawson#ifdef _WIN32
94fab72700SBrett Lawson                                        BUILTIN_CORE("iocp", LCB_IO_OPS_WINIOCP, lcb_iocp_new_iops),
9552ff7e0dSBrett Lawson#endif
9652ff7e0dSBrett Lawson
97d98a05aeSBrett Lawson#ifdef LCB_EMBED_PLUGIN_LIBEVENT
98fab72700SBrett Lawson                                        BUILTIN_CORE("libevent", LCB_IO_OPS_LIBEVENT, lcb_create_libevent_io_opts),
99d98a05aeSBrett Lawson#else
100fab72700SBrett Lawson                                        BUILTIN_DL("libevent", LCB_IO_OPS_LIBEVENT),
101d98a05aeSBrett Lawson#endif
102d98a05aeSBrett Lawson
103fab72700SBrett Lawson                                        BUILTIN_DL("libev", LCB_IO_OPS_LIBEV),
104fab72700SBrett Lawson                                        BUILTIN_DL("libuv", LCB_IO_OPS_LIBUV),
10552ff7e0dSBrett Lawson
106fab72700SBrett Lawson                                        {NULL, LCB_IO_OPS_INVALID, NULL, NULL, NULL, {0}, {0}}};
10752ff7e0dSBrett Lawson
10852ff7e0dSBrett Lawson/**
10952ff7e0dSBrett Lawson * Checks the environment for plugin information.
11052ff7e0dSBrett Lawson * Returns:
11152ff7e0dSBrett Lawson *   1  information found and valid
11252ff7e0dSBrett Lawson *   0  not found
11352ff7e0dSBrett Lawson *   -1 error
11452ff7e0dSBrett Lawson */
11552ff7e0dSBrett Lawsonstatic int get_env_plugin_info(plugin_info *info)
11652ff7e0dSBrett Lawson{
11752ff7e0dSBrett Lawson
11852ff7e0dSBrett Lawson    plugin_info *cur = NULL;
11952ff7e0dSBrett Lawson    memset(info, 0, sizeof(*info));
12052ff7e0dSBrett Lawson
121fab72700SBrett Lawson    if (!lcb_getenv_nonempty_multi(info->s_soname, sizeof(info->s_soname), "LIBCOUCHBASE_EVENT_PLUGIN_NAME",
122fab72700SBrett Lawson                                   "LCB_IOPS_NAME", NULL)) {
12352ff7e0dSBrett Lawson        return 0;
12452ff7e0dSBrett Lawson    }
12552ff7e0dSBrett Lawson
12652ff7e0dSBrett Lawson    for (cur = builtin_plugins; cur->base; cur++) {
12752ff7e0dSBrett Lawson        if (strlen(cur->base) != strlen(info->s_soname)) {
12852ff7e0dSBrett Lawson            continue;
12952ff7e0dSBrett Lawson        }
13052ff7e0dSBrett Lawson
13152ff7e0dSBrett Lawson        if (strcmp(cur->base, info->s_soname) == 0) {
13252ff7e0dSBrett Lawson            memcpy(info, cur, sizeof(*cur));
13352ff7e0dSBrett Lawson            return 1;
13452ff7e0dSBrett Lawson        }
13552ff7e0dSBrett Lawson    }
13652ff7e0dSBrett Lawson
137fab72700SBrett Lawson    if (!lcb_getenv_nonempty_multi(info->s_symbol, sizeof(info->s_symbol), "LIBCOUCHBASE_EVENT_PLUGIN_SYMBOL",
138fab72700SBrett Lawson                                   "LCB_IOPS_SYMBOL", NULL)) {
13952ff7e0dSBrett Lawson        return -1;
14052ff7e0dSBrett Lawson    }
14152ff7e0dSBrett Lawson
14252ff7e0dSBrett Lawson    info->soname = info->s_soname;
14352ff7e0dSBrett Lawson    info->symbol = info->s_symbol;
14452ff7e0dSBrett Lawson    return 1;
14552ff7e0dSBrett Lawson}
14652ff7e0dSBrett Lawson
14752ff7e0dSBrett Lawsonstatic plugin_info *find_plugin_info(lcb_io_ops_type_t iotype)
14852ff7e0dSBrett Lawson{
14952ff7e0dSBrett Lawson    plugin_info *cur;
15052ff7e0dSBrett Lawson
15152ff7e0dSBrett Lawson    if (iotype == LCB_IO_OPS_DEFAULT) {
15252ff7e0dSBrett Lawson        iotype = DEFAULT_IOPS;
15352ff7e0dSBrett Lawson    }
15452ff7e0dSBrett Lawson
15552ff7e0dSBrett Lawson    for (cur = builtin_plugins; cur->base; cur++) {
15652ff7e0dSBrett Lawson        if (cur->iotype == iotype) {
15752ff7e0dSBrett Lawson            return cur;
15852ff7e0dSBrett Lawson        }
15952ff7e0dSBrett Lawson    }
16052ff7e0dSBrett Lawson    return NULL;
16152ff7e0dSBrett Lawson}
16252ff7e0dSBrett Lawson
163fab72700SBrett Lawsonstatic void options_from_info(struct lcb_create_io_ops_st *opts, const plugin_info *info)
16452ff7e0dSBrett Lawson{
16552ff7e0dSBrett Lawson    void *cookie;
16652ff7e0dSBrett Lawson
16752ff7e0dSBrett Lawson    switch (opts->version) {
168fab72700SBrett Lawson        case 0:
169fab72700SBrett Lawson            cookie = opts->v.v0.cookie;
170fab72700SBrett Lawson            break;
171fab72700SBrett Lawson        case 1:
172fab72700SBrett Lawson            cookie = opts->v.v1.cookie;
173fab72700SBrett Lawson            break;
174fab72700SBrett Lawson        case 2:
175fab72700SBrett Lawson            cookie = opts->v.v2.cookie;
176fab72700SBrett Lawson            break;
177fab72700SBrett Lawson        default:
178fab72700SBrett Lawson            lcb_assert("unknown options version" && 0);
179fab72700SBrett Lawson            cookie = NULL;
18052ff7e0dSBrett Lawson    }
18152ff7e0dSBrett Lawson
18252ff7e0dSBrett Lawson    if (info->create) {
18352ff7e0dSBrett Lawson        opts->version = 2;
18452ff7e0dSBrett Lawson        opts->v.v2.create = info->create;
18552ff7e0dSBrett Lawson        opts->v.v2.cookie = cookie;
18652ff7e0dSBrett Lawson        return;
18752ff7e0dSBrett Lawson    }
18852ff7e0dSBrett Lawson
18952ff7e0dSBrett Lawson    opts->version = 1;
19052ff7e0dSBrett Lawson    opts->v.v1.sofile = info->soname;
19152ff7e0dSBrett Lawson    opts->v.v1.symbol = info->symbol;
19252ff7e0dSBrett Lawson    opts->v.v1.cookie = cookie;
19352ff7e0dSBrett Lawson}
19452ff7e0dSBrett Lawson
195fab72700SBrett Lawsonstatic lcb_STATUS create_v2(lcb_io_opt_t *io, const struct lcb_create_io_ops_st *options);
19652ff7e0dSBrett Lawson
19752ff7e0dSBrett Lawsonstruct plugin_st {
19852ff7e0dSBrett Lawson    void *dlhandle;
19952ff7e0dSBrett Lawson    union {
20052ff7e0dSBrett Lawson        create_func_t create;
20152ff7e0dSBrett Lawson        void *voidptr;
20252ff7e0dSBrett Lawson    } func;
20352ff7e0dSBrett Lawson};
20452ff7e0dSBrett Lawson
20552ff7e0dSBrett Lawson#ifndef _WIN32
206fab72700SBrett Lawsonstatic lcb_STATUS get_create_func(const char *image, const char *symbol, struct plugin_st *plugin, int do_warn)
20752ff7e0dSBrett Lawson{
20852ff7e0dSBrett Lawson    void *dlhandle = dlopen(image, RTLD_NOW | RTLD_LOCAL);
20952ff7e0dSBrett Lawson    if (dlhandle == NULL) {
21052ff7e0dSBrett Lawson        if (do_warn) {
211fab72700SBrett Lawson            fprintf(stderr, "[libcouchbase] dlopen of %s failed with '%s'\n", image, dlerror());
21252ff7e0dSBrett Lawson        }
21378681dc6SBrett Lawson        return LCB_ERR_DLOPEN_FAILED;
21452ff7e0dSBrett Lawson    }
21552ff7e0dSBrett Lawson
21652ff7e0dSBrett Lawson    memset(plugin, 0, sizeof(*plugin));
21752ff7e0dSBrett Lawson    plugin->func.create = NULL;
21852ff7e0dSBrett Lawson    plugin->func.voidptr = dlsym(dlhandle, symbol);
21952ff7e0dSBrett Lawson
22052ff7e0dSBrett Lawson    if (plugin->func.voidptr == NULL) {
22152ff7e0dSBrett Lawson        if (do_warn) {
222fab72700SBrett Lawson            fprintf(stderr, "[libcouchbase] dlsym (%s) -> (%s) failed: %s\n", image, symbol, dlerror());
22352ff7e0dSBrett Lawson        }
22452ff7e0dSBrett Lawson        dlclose(dlhandle);
22552ff7e0dSBrett Lawson        dlhandle = NULL;
22678681dc6SBrett Lawson        return LCB_ERR_DLSYM_FAILED;
22752ff7e0dSBrett Lawson
22852ff7e0dSBrett Lawson    } else {
22952ff7e0dSBrett Lawson        plugin->dlhandle = dlhandle;
23052ff7e0dSBrett Lawson    }
23152ff7e0dSBrett Lawson    return LCB_SUCCESS;
23252ff7e0dSBrett Lawson}
23352ff7e0dSBrett Lawson
23452ff7e0dSBrett Lawsonstatic void close_dlhandle(void *handle)
23552ff7e0dSBrett Lawson{
23652ff7e0dSBrett Lawson    dlclose(handle);
23752ff7e0dSBrett Lawson}
23852ff7e0dSBrett Lawson#else
239fab72700SBrett Lawsonstatic lcb_STATUS get_create_func(const char *image, const char *symbol, struct plugin_st *plugin, int do_warn)
24052ff7e0dSBrett Lawson{
24152ff7e0dSBrett Lawson    HMODULE hLibrary = LoadLibrary(image);
24252ff7e0dSBrett Lawson    FARPROC hFunction;
24352ff7e0dSBrett Lawson
24452ff7e0dSBrett Lawson    memset(plugin, 0, sizeof(*plugin));
24552ff7e0dSBrett Lawson
24652ff7e0dSBrett Lawson    if (!hLibrary) {
24752ff7e0dSBrett Lawson        if (do_warn) {
248fab72700SBrett Lawson            fprintf(stderr, "LoadLibrary of %s failed with code %d\n", image, (int)GetLastError());
24952ff7e0dSBrett Lawson        }
25078681dc6SBrett Lawson        return LCB_ERR_DLOPEN_FAILED;
25152ff7e0dSBrett Lawson    }
25252ff7e0dSBrett Lawson
25352ff7e0dSBrett Lawson    hFunction = GetProcAddress(hLibrary, symbol);
25452ff7e0dSBrett Lawson    if (!hFunction) {
25552ff7e0dSBrett Lawson        if (do_warn) {
256fab72700SBrett Lawson            fprintf(stderr, "GetProcAddress (%s) -> (%s) failed with code %d\n", image, symbol, (int)GetLastError());
25752ff7e0dSBrett Lawson        }
25852ff7e0dSBrett Lawson        FreeLibrary(hLibrary);
25978681dc6SBrett Lawson        return LCB_ERR_DLSYM_FAILED;
26052ff7e0dSBrett Lawson    }
26152ff7e0dSBrett Lawson
26252ff7e0dSBrett Lawson    plugin->func.create = (create_func_t)hFunction;
26352ff7e0dSBrett Lawson    plugin->dlhandle = hLibrary;
26452ff7e0dSBrett Lawson    return LCB_SUCCESS;
26552ff7e0dSBrett Lawson}
26652ff7e0dSBrett Lawson
26752ff7e0dSBrett Lawsonstatic void close_dlhandle(void *handle)
26852ff7e0dSBrett Lawson{
26952ff7e0dSBrett Lawson    FreeLibrary((HMODULE)handle);
27052ff7e0dSBrett Lawson}
27152ff7e0dSBrett Lawson#endif
27252ff7e0dSBrett Lawson
27352ff7e0dSBrett Lawsonstatic int want_dl_debug = 0; /* global variable */
274fab72700SBrett Lawsonstatic lcb_STATUS create_v1(lcb_io_opt_t *io, const struct lcb_create_io_ops_st *options);
27552ff7e0dSBrett Lawson
27652ff7e0dSBrett LawsonLIBCOUCHBASE_API
277fab72700SBrett Lawsonlcb_STATUS lcb_destroy_io_ops(lcb_io_opt_t io)
27852ff7e0dSBrett Lawson{
27952ff7e0dSBrett Lawson    if (io) {
28052ff7e0dSBrett Lawson        void *dlhandle = io->dlhandle;
28152ff7e0dSBrett Lawson        if (io->destructor) {
28252ff7e0dSBrett Lawson            io->destructor(io);
28352ff7e0dSBrett Lawson        }
28452ff7e0dSBrett Lawson        if (dlhandle) {
28552ff7e0dSBrett Lawson            close_dlhandle(dlhandle);
28652ff7e0dSBrett Lawson        }
28752ff7e0dSBrett Lawson    }
28852ff7e0dSBrett Lawson
28952ff7e0dSBrett Lawson    return LCB_SUCCESS;
29052ff7e0dSBrett Lawson}
29152ff7e0dSBrett Lawson
29252ff7e0dSBrett Lawson/**
29352ff7e0dSBrett Lawson * Note, the 'pi' is just a context variable to ensure the pointers copied
29452ff7e0dSBrett Lawson * to the options are valid. It is *not* meant to be inspected.
29552ff7e0dSBrett Lawson */
296fab72700SBrett Lawsonstatic lcb_STATUS generate_options(plugin_info *pi, const struct lcb_create_io_ops_st *user,
297fab72700SBrett Lawson                                   struct lcb_create_io_ops_st *ours, lcb_io_ops_type_t *type)
29852ff7e0dSBrett Lawson{
29952ff7e0dSBrett Lawson    if (user) {
30052ff7e0dSBrett Lawson        memcpy(ours, user, sizeof(*user));
30152ff7e0dSBrett Lawson
30252ff7e0dSBrett Lawson    } else {
30352ff7e0dSBrett Lawson        memset(ours, 0, sizeof(*ours));
30452ff7e0dSBrett Lawson        ours->version = 0;
30552ff7e0dSBrett Lawson        ours->v.v0.type = LCB_IO_OPS_DEFAULT;
30652ff7e0dSBrett Lawson    }
30752ff7e0dSBrett Lawson
30852ff7e0dSBrett Lawson    if (ours->version > 0) {
30952ff7e0dSBrett Lawson        if (type) {
31052ff7e0dSBrett Lawson            *type = LCB_IO_OPS_INVALID;
31152ff7e0dSBrett Lawson        }
31252ff7e0dSBrett Lawson        /* we don't handle non-v0 options */
31352ff7e0dSBrett Lawson        return LCB_SUCCESS;
31452ff7e0dSBrett Lawson    }
31552ff7e0dSBrett Lawson
31652ff7e0dSBrett Lawson    if (ours->v.v0.type == LCB_IO_OPS_DEFAULT) {
31752ff7e0dSBrett Lawson        int rv;
31852ff7e0dSBrett Lawson        memset(pi, 0, sizeof(*pi));
31952ff7e0dSBrett Lawson
32052ff7e0dSBrett Lawson        rv = get_env_plugin_info(pi);
32152ff7e0dSBrett Lawson        if (rv > 0) {
32252ff7e0dSBrett Lawson            options_from_info(ours, pi);
32352ff7e0dSBrett Lawson
32452ff7e0dSBrett Lawson            if (type) {
32552ff7e0dSBrett Lawson                *type = pi->iotype;
32652ff7e0dSBrett Lawson            }
32752ff7e0dSBrett Lawson
32852ff7e0dSBrett Lawson        } else if (rv < 0) {
32978681dc6SBrett Lawson            return LCB_ERR_BAD_ENVIRONMENT;
33052ff7e0dSBrett Lawson
33152ff7e0dSBrett Lawson        } else {
33252ff7e0dSBrett Lawson            plugin_info *pip = find_plugin_info(LCB_IO_OPS_DEFAULT);
33352ff7e0dSBrett Lawson            lcb_assert(pip);
33452ff7e0dSBrett Lawson
33552ff7e0dSBrett Lawson            if (type) {
33652ff7e0dSBrett Lawson                *type = pip->iotype;
33752ff7e0dSBrett Lawson            }
33852ff7e0dSBrett Lawson
33952ff7e0dSBrett Lawson            options_from_info(ours, pip);
34052ff7e0dSBrett Lawson
34152ff7e0dSBrett Lawson            /* if the plugin is dynamically loadable, we need to
34252ff7e0dSBrett Lawson             * fallback to select(2) plugin in case we cannot find the
34352ff7e0dSBrett Lawson             * create function */
34452ff7e0dSBrett Lawson            if (ours->version == 1) {
34552ff7e0dSBrett Lawson                struct plugin_st plugin;
34652ff7e0dSBrett Lawson                int want_debug;
347fab72700SBrett Lawson                lcb_STATUS ret;
34852ff7e0dSBrett Lawson
349fab72700SBrett Lawson                if (lcb_getenv_boolean_multi("LIBCOUCHBASE_DLOPEN_DEBUG", "LCB_DLOPEN_DEBUG", NULL)) {
35052ff7e0dSBrett Lawson                    want_debug = 1;
35152ff7e0dSBrett Lawson                } else {
35252ff7e0dSBrett Lawson                    want_debug = want_dl_debug;
35352ff7e0dSBrett Lawson                }
35452ff7e0dSBrett Lawson                ret = get_create_func(ours->v.v1.sofile, ours->v.v1.symbol, &plugin, want_debug);
355fab72700SBrett Lawson                if (ret != LCB_SUCCESS) {
356fab72700SBrett Lawson                    char path[PATH_MAX];
357fab72700SBrett Lawson                    /* try to look up the so-file in the libdir */
358fab72700SBrett Lawson                    snprintf(path, PATH_MAX, "%s/%s", LCB_LIBDIR, ours->v.v1.sofile);
359fab72700SBrett Lawson                    ret = get_create_func(path, ours->v.v1.symbol, &plugin, want_debug);
360fab72700SBrett Lawson                }
36152ff7e0dSBrett Lawson                if (ret != LCB_SUCCESS) {
36252ff7e0dSBrett Lawson                    if (type) {
36352ff7e0dSBrett Lawson                        *type = LCB_IO_OPS_SELECT;
36452ff7e0dSBrett Lawson                    }
36552ff7e0dSBrett Lawson                    ours->version = 2;
36652ff7e0dSBrett Lawson                    ours->v.v2.create = lcb_create_select_io_opts;
36752ff7e0dSBrett Lawson                    ours->v.v2.cookie = NULL;
36852ff7e0dSBrett Lawson                }
36952ff7e0dSBrett Lawson            }
37052ff7e0dSBrett Lawson        }
37152ff7e0dSBrett Lawson        return LCB_SUCCESS;
37252ff7e0dSBrett Lawson
37352ff7e0dSBrett Lawson    } else {
37452ff7e0dSBrett Lawson        /** Not default, ignore environment */
37552ff7e0dSBrett Lawson        plugin_info *pip = find_plugin_info(ours->v.v0.type);
37652ff7e0dSBrett Lawson        if (!pip) {
37778681dc6SBrett Lawson            return LCB_ERR_UNSUPPORTED_OPERATION;
37852ff7e0dSBrett Lawson        }
37952ff7e0dSBrett Lawson        options_from_info(ours, pip);
38052ff7e0dSBrett Lawson        if (type) {
38152ff7e0dSBrett Lawson            *type = pip->iotype;
38252ff7e0dSBrett Lawson        }
38352ff7e0dSBrett Lawson        return LCB_SUCCESS;
38452ff7e0dSBrett Lawson    }
38552ff7e0dSBrett Lawson}
38652ff7e0dSBrett Lawson
38752ff7e0dSBrett LawsonLIBCOUCHBASE_API
388fab72700SBrett Lawsonlcb_STATUS lcb_create_io_ops(lcb_io_opt_t *io, const struct lcb_create_io_ops_st *io_opts)
38952ff7e0dSBrett Lawson{
39052ff7e0dSBrett Lawson
39152ff7e0dSBrett Lawson    struct lcb_create_io_ops_st options;
392fab72700SBrett Lawson    lcb_STATUS err;
39352ff7e0dSBrett Lawson    plugin_info pi;
39452ff7e0dSBrett Lawson    memset(&options, 0, sizeof(options));
39552ff7e0dSBrett Lawson
39652ff7e0dSBrett Lawson    err = lcb_initialize_socket_subsystem();
39752ff7e0dSBrett Lawson    if (err != LCB_SUCCESS) {
39852ff7e0dSBrett Lawson        return err;
39952ff7e0dSBrett Lawson    }
40052ff7e0dSBrett Lawson
40152ff7e0dSBrett Lawson    err = generate_options(&pi, io_opts, &options, NULL);
40252ff7e0dSBrett Lawson    if (err != LCB_SUCCESS) {
40352ff7e0dSBrett Lawson        return err;
40452ff7e0dSBrett Lawson    }
40552ff7e0dSBrett Lawson
406399d4f65SBrett Lawson    if (options.version == 1) {
407399d4f65SBrett Lawson        err = create_v1(io, &options);
408399d4f65SBrett Lawson    } else if (options.version == 2) {
409399d4f65SBrett Lawson        err = create_v2(io, &options);
410399d4f65SBrett Lawson    } else {
41178681dc6SBrett Lawson        return LCB_ERR_UNSUPPORTED_OPERATION;
41252ff7e0dSBrett Lawson    }
413399d4f65SBrett Lawson
414399d4f65SBrett Lawson    if (err != LCB_SUCCESS) {
415399d4f65SBrett Lawson        return err;
416399d4f65SBrett Lawson    }
417399d4f65SBrett Lawson    /*XXX:
418399d4f65SBrett Lawson     * This block of code here because the Ruby SDK relies on undocumented
419399d4f65SBrett Lawson     * functionality of older versions of libcouchbase in which its send/recv
420399d4f65SBrett Lawson     * functions assert that the number of IOV elements passed is always going
421399d4f65SBrett Lawson     * to be 2.
422399d4f65SBrett Lawson     *
423399d4f65SBrett Lawson     * This works around the issue by patching the send/recv functions of
424399d4f65SBrett Lawson     * the ruby implementation at load-time.
425399d4f65SBrett Lawson     *
426399d4f65SBrett Lawson     * This block of code will go away once the Ruby SDK is fixed and a released
427399d4f65SBrett Lawson     * version has been out for enough time that it won't break common existing
428399d4f65SBrett Lawson     * deployments.
429399d4f65SBrett Lawson     */
430399d4f65SBrett Lawson    if (io_opts && io_opts->version == 1 && io_opts->v.v1.symbol != NULL) {
431399d4f65SBrett Lawson        if (strstr(io_opts->v.v1.symbol, "cb_create_ruby")) {
432399d4f65SBrett Lawson            wire_lcb_bsd_impl(*io);
433399d4f65SBrett Lawson        }
434399d4f65SBrett Lawson    }
435399d4f65SBrett Lawson    return LCB_SUCCESS;
43652ff7e0dSBrett Lawson}
43752ff7e0dSBrett Lawson
438fab72700SBrett Lawsonstatic lcb_STATUS create_v1(lcb_io_opt_t *io, const struct lcb_create_io_ops_st *options)
43952ff7e0dSBrett Lawson{
44052ff7e0dSBrett Lawson    struct plugin_st plugin;
44152ff7e0dSBrett Lawson    int want_debug;
442fab72700SBrett Lawson    lcb_STATUS ret;
44352ff7e0dSBrett Lawson
444fab72700SBrett Lawson    if (lcb_getenv_boolean_multi("LIBCOUCHBASE_DLOPEN_DEBUG", "LCB_DLOPEN_DEBUG", NULL)) {
44552ff7e0dSBrett Lawson        want_debug = 1;
44652ff7e0dSBrett Lawson    } else {
44752ff7e0dSBrett Lawson        want_debug = want_dl_debug;
44852ff7e0dSBrett Lawson    }
449fab72700SBrett Lawson    ret = get_create_func(options->v.v1.sofile, options->v.v1.symbol, &plugin, want_debug);
45052ff7e0dSBrett Lawson    if (ret != LCB_SUCCESS) {
45152ff7e0dSBrett Lawson        /* try to look up the symbol in the current image */
452fab72700SBrett Lawson        lcb_STATUS ret2 = get_create_func(NULL, options->v.v1.symbol, &plugin, want_debug);
45352ff7e0dSBrett Lawson        if (ret2 != LCB_SUCCESS) {
45452ff7e0dSBrett Lawson#ifndef _WIN32
45552ff7e0dSBrett Lawson            char path[PATH_MAX];
45652ff7e0dSBrett Lawson            /* try to look up the so-file in the libdir */
45752ff7e0dSBrett Lawson            snprintf(path, PATH_MAX, "%s/%s", LCB_LIBDIR, options->v.v1.sofile);
45852ff7e0dSBrett Lawson            ret2 = get_create_func(path, options->v.v1.symbol, &plugin, want_debug);
45952ff7e0dSBrett Lawson#endif
46052ff7e0dSBrett Lawson            if (ret2 != LCB_SUCCESS) {
46152ff7e0dSBrett Lawson                /* return original error to allow caller to fix it */
46252ff7e0dSBrett Lawson                return ret;
46352ff7e0dSBrett Lawson            }
46452ff7e0dSBrett Lawson        }
46552ff7e0dSBrett Lawson    }
46652ff7e0dSBrett Lawson
46752ff7e0dSBrett Lawson    ret = plugin.func.create(0, io, options->v.v1.cookie);
46852ff7e0dSBrett Lawson    if (ret != LCB_SUCCESS) {
46952ff7e0dSBrett Lawson        if (options->v.v1.sofile != NULL) {
47052ff7e0dSBrett Lawson            close_dlhandle(plugin.dlhandle);
47152ff7e0dSBrett Lawson        }
47278681dc6SBrett Lawson        return LCB_ERR_NO_MEMORY;
47352ff7e0dSBrett Lawson    } else {
47452ff7e0dSBrett Lawson        lcb_io_opt_t iop = *io;
47552ff7e0dSBrett Lawson        iop->dlhandle = plugin.dlhandle;
47652ff7e0dSBrett Lawson        /* check if plugin selected compatible version */
4773e0810faSBrett Lawson        if (iop->version < 0 || iop->version > 3) {
47852ff7e0dSBrett Lawson            lcb_destroy_io_ops(iop);
47978681dc6SBrett Lawson            return LCB_ERR_PLUGIN_VERSION_MISMATCH;
48052ff7e0dSBrett Lawson        }
48152ff7e0dSBrett Lawson    }
48252ff7e0dSBrett Lawson
48352ff7e0dSBrett Lawson    return LCB_SUCCESS;
48452ff7e0dSBrett Lawson}
48552ff7e0dSBrett Lawson
486fab72700SBrett Lawsonstatic lcb_STATUS create_v2(lcb_io_opt_t *io, const struct lcb_create_io_ops_st *options)
48752ff7e0dSBrett Lawson{
488fab72700SBrett Lawson    lcb_STATUS ret;
48952ff7e0dSBrett Lawson
49052ff7e0dSBrett Lawson    ret = options->v.v2.create(0, io, options->v.v2.cookie);
49152ff7e0dSBrett Lawson    if (ret != LCB_SUCCESS) {
49252ff7e0dSBrett Lawson        return ret;
49352ff7e0dSBrett Lawson    } else {
49452ff7e0dSBrett Lawson        lcb_io_opt_t iop = *io;
49552ff7e0dSBrett Lawson        /* check if plugin selected compatible version */
4963e0810faSBrett Lawson        if (iop->version < 0 || iop->version > 3) {
49752ff7e0dSBrett Lawson            lcb_destroy_io_ops(iop);
49878681dc6SBrett Lawson            return LCB_ERR_PLUGIN_VERSION_MISMATCH;
49952ff7e0dSBrett Lawson        }
50052ff7e0dSBrett Lawson    }
50152ff7e0dSBrett Lawson
50252ff7e0dSBrett Lawson    return LCB_SUCCESS;
50352ff7e0dSBrett Lawson}
50452ff7e0dSBrett Lawson
505fab72700SBrett Lawsonlcb_STATUS lcb_iops_cntl_handler(int mode, lcb_INSTANCE *instance, int cmd, void *arg)
50652ff7e0dSBrett Lawson{
50752ff7e0dSBrett Lawson    (void)instance;
50852ff7e0dSBrett Lawson
50952ff7e0dSBrett Lawson    switch (cmd) {
510fab72700SBrett Lawson        case LCB_CNTL_IOPS_DEFAULT_TYPES: {
511fab72700SBrett Lawson            struct lcb_create_io_ops_st options;
512fab72700SBrett Lawson            struct lcb_cntl_iops_info_st *info = arg;
513fab72700SBrett Lawson            plugin_info pi;
514fab72700SBrett Lawson
515fab72700SBrett Lawson            memset(&options, 0, sizeof(options));
516fab72700SBrett Lawson            if (mode != LCB_CNTL_GET) {
51778681dc6SBrett Lawson                return LCB_ERR_UNSUPPORTED_OPERATION;
518fab72700SBrett Lawson            }
51952ff7e0dSBrett Lawson
520fab72700SBrett Lawson            if (info->version != 0) {
52178681dc6SBrett Lawson                return LCB_ERR_INVALID_ARGUMENT;
522fab72700SBrett Lawson            }
52352ff7e0dSBrett Lawson
524fab72700SBrett Lawson            info->v.v0.os_default = DEFAULT_IOPS;
52552ff7e0dSBrett Lawson
52678681dc6SBrett Lawson            return generate_options(&pi, info->v.v0.options, &options, &info->v.v0.effective);
52752ff7e0dSBrett Lawson        }
52852ff7e0dSBrett Lawson
529fab72700SBrett Lawson        case LCB_CNTL_IOPS_DLOPEN_DEBUG: {
530fab72700SBrett Lawson            int *usr = arg;
531fab72700SBrett Lawson            if (mode == LCB_CNTL_SET) {
532fab72700SBrett Lawson                want_dl_debug = *usr;
533fab72700SBrett Lawson            } else {
534fab72700SBrett Lawson                *usr = want_dl_debug;
535fab72700SBrett Lawson            }
536fab72700SBrett Lawson            return LCB_SUCCESS;
537fab72700SBrett Lawson        }
53852ff7e0dSBrett Lawson
539fab72700SBrett Lawson        default:
54078681dc6SBrett Lawson            return LCB_ERR_INVALID_ARGUMENT;
54152ff7e0dSBrett Lawson    }
54252ff7e0dSBrett Lawson}
5433e0810faSBrett Lawson
5443e0810faSBrett Lawson/* In-library wrapper version */
5453e0810faSBrett LawsonLIBCOUCHBASE_API
546fab72700SBrett Lawsonvoid lcb_iops_wire_bsd_impl2(lcb_bsd_procs *procs, int version)
5473e0810faSBrett Lawson{
5483e0810faSBrett Lawson    wire_lcb_bsd_impl2(procs, version);
5493e0810faSBrett Lawson}
550