1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2014-2020 Couchbase, Inc.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #define LCB_IOPS_V12_NO_DEPRECATE
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include "iotable.h"
23 #include "connect.h" /* prototypes for iotable functions */
24 
25 #define GET_23_FIELD(iops, fld) ((iops)->version == 2 ? (iops)->v.v2.fld : (iops)->v.v3.fld)
26 
27 struct W_1to3_st {
28     lcb_ioC_write2_callback callback;
29     void *udata;
30     unsigned int refcount;
31     unsigned int last_error;
32 };
33 
W_1to3_callback(lcb_sockdata_t *sd, lcb_io_writebuf_t *wb, int status)34 static void W_1to3_callback(lcb_sockdata_t *sd, lcb_io_writebuf_t *wb, int status)
35 {
36     struct W_1to3_st *ott = (struct W_1to3_st *)(void *)wb->buffer.root;
37     lcb_ioC_wbfree_fn wbfree;
38 
39     wb->buffer.root = NULL;
40     wb->buffer.ringbuffer = NULL;
41 
42     if (sd->parent->version >= 2) {
43         wbfree = GET_23_FIELD(sd->parent, iot->u_io.completion.wbfree);
44     } else {
45         wbfree = sd->parent->v.v1.release_writebuf;
46     }
47 
48     wbfree(sd->parent, sd, wb);
49 
50     if (status != 0 && ott->last_error == 0) {
51         ott->last_error = sd->parent->v.v0.error;
52     }
53 
54     if (--ott->refcount == 0) {
55         ott->callback(sd, ott->last_error, ott->udata);
56         free(ott);
57     }
58 }
59 
W_1to3_write(lcb_io_opt_t iops, lcb_sockdata_t *sd, struct lcb_iovec_st *iov, lcb_size_t niov, void *uarg, lcb_ioC_write2_callback cb)60 static int W_1to3_write(lcb_io_opt_t iops, lcb_sockdata_t *sd, struct lcb_iovec_st *iov, lcb_size_t niov, void *uarg,
61                         lcb_ioC_write2_callback cb)
62 {
63     unsigned int ii = 0;
64     struct W_1to3_st *ott;
65     lcb_ioC_write_fn start_write;
66     lcb_ioC_wballoc_fn wballoc;
67 
68     if (niov == 0) {
69         return -1;
70     }
71     /** Schedule IOV writes, two at a time... */
72     ott = malloc(sizeof(*ott));
73     ott->callback = cb;
74     ott->udata = uarg;
75     ott->refcount = 0;
76     ott->last_error = 0;
77 
78     if (iops->version >= 2) {
79         start_write = GET_23_FIELD(iops, iot->u_io.completion.write);
80         wballoc = GET_23_FIELD(iops, iot->u_io.completion.wballoc);
81     } else {
82         start_write = iops->v.v1.start_write;
83         wballoc = iops->v.v1.create_writebuf;
84     }
85 
86     while (ii < niov) {
87         int jj = 0;
88         lcb_io_writebuf_t *wb;
89 
90         wb = wballoc(iops, sd);
91         wb->buffer.root = (char *)ott;
92         wb->buffer.ringbuffer = NULL;
93 
94         for (jj = 0; jj < 2 && ii < niov; ii++, jj++) {
95             wb->buffer.iov[jj] = iov[ii];
96         }
97         ott->refcount++;
98         start_write(iops, sd, wb, W_1to3_callback);
99     }
100     return 0;
101 }
102 
103 struct R_1to3_st {
104     lcb_ioC_read2_callback callback;
105     void *uarg;
106 };
107 
R_1to3_callback(lcb_sockdata_t *sd, lcb_ssize_t nread)108 static void R_1to3_callback(lcb_sockdata_t *sd, lcb_ssize_t nread)
109 {
110     struct lcb_buf_info *bi = &sd->read_buffer;
111     struct R_1to3_st *st = (void *)bi->root;
112     bi->root = NULL;
113     st->callback(sd, nread, st->uarg);
114     free(st);
115 }
116 
R_1to3_read(lcb_io_opt_t io, lcb_sockdata_t *sd, lcb_IOV *iov, lcb_size_t niov, void *uarg, lcb_ioC_read2_callback callback)117 static int R_1to3_read(lcb_io_opt_t io, lcb_sockdata_t *sd, lcb_IOV *iov, lcb_size_t niov, void *uarg,
118                        lcb_ioC_read2_callback callback)
119 {
120     unsigned ii;
121     int rv;
122     struct R_1to3_st *st;
123     struct lcb_buf_info *bi = &sd->read_buffer;
124     lcb_ioC_read_fn rdstart;
125 
126     st = calloc(1, sizeof(*st));
127     st->callback = callback;
128     st->uarg = uarg;
129 
130     for (ii = 0; ii < 2 && ii < niov; ii++) {
131         bi->iov[ii] = iov[ii];
132     }
133 
134     for (; ii < 2; ii++) {
135         bi->iov[ii].iov_base = NULL;
136         bi->iov[ii].iov_len = 0;
137     }
138 
139     bi->root = (void *)st;
140     if (io->version >= 2) {
141         rdstart = GET_23_FIELD(io, iot->u_io.completion.read);
142     } else {
143         rdstart = io->v.v1.start_read;
144     }
145 
146     rv = rdstart(io, sd, R_1to3_callback);
147     return rv;
148 }
149 
dummy_bsd_chkclosed(lcb_io_opt_t io, lcb_socket_t s, int f)150 static int dummy_bsd_chkclosed(lcb_io_opt_t io, lcb_socket_t s, int f)
151 {
152     (void)io;
153     (void)s;
154     (void)f;
155     return LCB_IO_SOCKCHECK_STATUS_UNKNOWN;
156 }
dummy_comp_chkclosed(lcb_io_opt_t io, lcb_sockdata_t *s, int f)157 static int dummy_comp_chkclosed(lcb_io_opt_t io, lcb_sockdata_t *s, int f)
158 {
159     (void)io;
160     (void)s;
161     (void)f;
162     return LCB_IO_SOCKCHECK_STATUS_UNKNOWN;
163 }
164 
init_v23_table(lcbio_TABLE *table, lcb_io_opt_t io)165 static void init_v23_table(lcbio_TABLE *table, lcb_io_opt_t io)
166 {
167     lcb_io_procs_fn fn = GET_23_FIELD(io, get_procs);
168     fn(LCB_IOPROCS_VERSION, &table->loop, &table->timer, &table->u_io.v0.io, &table->u_io.v0.ev,
169        &table->u_io.completion, &table->model);
170 
171     table->p = io;
172     if (table->model == LCB_IOMODEL_COMPLETION) {
173         if (!table->u_io.completion.write2) {
174             table->u_io.completion.write2 = W_1to3_write;
175         }
176 
177         if (!table->u_io.completion.read2) {
178             table->u_io.completion.read2 = R_1to3_read;
179         }
180         lcb_assert(table->u_io.completion.read2);
181         lcb_assert(table->u_io.completion.write2);
182     }
183 
184     if (table->model == LCB_IOMODEL_COMPLETION && IOT_V1(table).is_closed == NULL) {
185         IOT_V1(table).is_closed = dummy_comp_chkclosed;
186     }
187 
188     if (table->model == LCB_IOMODEL_EVENT && IOT_V0IO(table).is_closed == NULL) {
189         IOT_V0IO(table).is_closed = dummy_bsd_chkclosed;
190     }
191 }
192 
lcbio_table_new(lcb_io_opt_t io)193 lcbio_TABLE *lcbio_table_new(lcb_io_opt_t io)
194 {
195     lcbio_TABLE *table = calloc(1, sizeof(*table));
196     table->p = io;
197     table->refcount = 1;
198 
199     if (io->version == 2) {
200         io->v.v2.iot = table;
201         init_v23_table(table, io);
202         return table;
203 
204     } else if (io->version == 3) {
205         /* V3 exists exclusively for back-compat. We need to use a few tricks to
206          * determine if we are really v3, or if we've been 'overidden' somehow.
207          *
208          * To do this, we treat the padding fields (specifically, the event
209          * scheduling parts of the padding fields) as sentinel values. The
210          * built-in plugins should initialize this to NULL. If a client
211          * (e.g. Python SDK) overrides this, the field will no longer be
212          * NULL and will be a sign that the event fields have been
213          * used by a non-getprocs-aware client.
214          */
215 
216         io->v.v3.iot = table;
217         if (io->v.v0.create_event == NULL) {
218             init_v23_table(table, io);
219             return table;
220         }
221     }
222 
223     table->timer.create = io->v.v0.create_timer;
224     table->timer.destroy = io->v.v0.destroy_timer;
225     table->timer.cancel = io->v.v0.delete_timer;
226     table->timer.schedule = io->v.v0.update_timer;
227     table->loop.start = io->v.v0.run_event_loop;
228     table->loop.stop = io->v.v0.stop_event_loop;
229 
230     if (io->version == 0 || io->version == 3) {
231         lcb_ev_procs *ev = &table->u_io.v0.ev;
232         lcb_bsd_procs *bsd = &table->u_io.v0.io;
233 
234         table->model = LCB_IOMODEL_EVENT;
235         ev->create = io->v.v0.create_event;
236         ev->destroy = io->v.v0.destroy_event;
237         ev->cancel = io->v.v0.delete_event;
238         ev->watch = io->v.v0.update_event;
239         bsd->socket0 = io->v.v0.socket;
240         bsd->connect0 = io->v.v0.connect;
241         bsd->close = io->v.v0.close;
242         bsd->recv = io->v.v0.recv;
243         bsd->recvv = io->v.v0.recvv;
244         bsd->send = io->v.v0.send;
245         bsd->sendv = io->v.v0.sendv;
246         bsd->is_closed = dummy_bsd_chkclosed;
247 
248     } else {
249         lcb_completion_procs *cp = &table->u_io.completion;
250 
251         table->model = LCB_IOMODEL_COMPLETION;
252         cp->socket = io->v.v1.create_socket;
253         cp->close = io->v.v1.close_socket;
254         cp->connect = io->v.v1.start_connect;
255         cp->read = io->v.v1.start_read;
256         cp->write = io->v.v1.start_write;
257         cp->wballoc = io->v.v1.create_writebuf;
258         cp->nameinfo = io->v.v1.get_nameinfo;
259         cp->write2 = W_1to3_write;
260         cp->read2 = R_1to3_read;
261         cp->is_closed = dummy_comp_chkclosed;
262     }
263 
264     return table;
265 }
266 
lcbio_table_unref(lcbio_TABLE *table)267 void lcbio_table_unref(lcbio_TABLE *table)
268 {
269     if (--table->refcount) {
270         return;
271     }
272 
273     if (table->dtor) {
274         table->dtor(table);
275         return;
276     }
277 
278     if (table->p && table->p->v.base.need_cleanup) {
279         lcb_destroy_io_ops(table->p);
280     }
281 
282     free(table);
283 }
284 
lcbio_table_ref(lcbio_TABLE *table)285 void lcbio_table_ref(lcbio_TABLE *table)
286 {
287     ++table->refcount;
288 }
289