1 /*
2 * Copyright 2016 Jakub Klama <jceel@FreeBSD.org>
3 * All rights reserved
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted providing that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <assert.h>
32 #include <sys/queue.h>
33 #include "lib9p.h"
34 #include "lib9p_impl.h"
35 #include "fid.h"
36 #include "hashtable.h"
37 #include "log.h"
38 #include "threadpool.h"
39 #include "backend/backend.h"
40
41 int
l9p_server_init(struct l9p_server ** serverp,struct l9p_backend * backend)42 l9p_server_init(struct l9p_server **serverp, struct l9p_backend *backend)
43 {
44 struct l9p_server *server;
45
46 server = l9p_calloc(1, sizeof (*server));
47 server->ls_max_version = L9P_2000L;
48 server->ls_backend = backend;
49 LIST_INIT(&server->ls_conns);
50
51 *serverp = server;
52 return (0);
53 }
54
55 int
l9p_connection_init(struct l9p_server * server,struct l9p_connection ** conn)56 l9p_connection_init(struct l9p_server *server, struct l9p_connection **conn)
57 {
58 struct l9p_connection *newconn;
59
60 assert(server != NULL);
61 assert(conn != NULL);
62
63 newconn = calloc(1, sizeof (*newconn));
64 if (newconn == NULL)
65 return (-1);
66 newconn->lc_server = server;
67 newconn->lc_msize = L9P_DEFAULT_MSIZE;
68 if (l9p_threadpool_init(&newconn->lc_tp, L9P_NUMTHREADS)) {
69 free(newconn);
70 return (-1);
71 }
72 ht_init(&newconn->lc_files, 100);
73 ht_init(&newconn->lc_requests, 100);
74 LIST_INSERT_HEAD(&server->ls_conns, newconn, lc_link);
75 *conn = newconn;
76
77 return (0);
78 }
79
80 void
l9p_connection_free(struct l9p_connection * conn)81 l9p_connection_free(struct l9p_connection *conn)
82 {
83
84 LIST_REMOVE(conn, lc_link);
85 free(conn);
86 }
87
88 void
l9p_connection_recv(struct l9p_connection * conn,const struct iovec * iov,const size_t niov,void * aux)89 l9p_connection_recv(struct l9p_connection *conn, const struct iovec *iov,
90 const size_t niov, void *aux)
91 {
92 struct l9p_request *req;
93 int error;
94
95 req = l9p_calloc(1, sizeof (struct l9p_request));
96 req->lr_aux = aux;
97 req->lr_conn = conn;
98
99 req->lr_req_msg.lm_mode = L9P_UNPACK;
100 req->lr_req_msg.lm_niov = niov;
101 memcpy(req->lr_req_msg.lm_iov, iov, sizeof (struct iovec) * niov);
102
103 req->lr_resp_msg.lm_mode = L9P_PACK;
104
105 if (l9p_pufcall(&req->lr_req_msg, &req->lr_req, conn->lc_version) != 0) {
106 L9P_LOG(L9P_WARNING, "cannot unpack received message");
107 l9p_freefcall(&req->lr_req);
108 free(req);
109 return;
110 }
111
112 if (ht_add(&conn->lc_requests, req->lr_req.hdr.tag, req)) {
113 L9P_LOG(L9P_WARNING, "client reusing outstanding tag %d",
114 req->lr_req.hdr.tag);
115 l9p_freefcall(&req->lr_req);
116 free(req);
117 return;
118 }
119
120 error = conn->lc_lt.lt_get_response_buffer(req,
121 req->lr_resp_msg.lm_iov,
122 &req->lr_resp_msg.lm_niov,
123 conn->lc_lt.lt_aux);
124 if (error) {
125 L9P_LOG(L9P_WARNING, "cannot obtain buffers for response");
126 ht_remove(&conn->lc_requests, req->lr_req.hdr.tag);
127 l9p_freefcall(&req->lr_req);
128 free(req);
129 return;
130 }
131
132 /*
133 * NB: it's up to l9p_threadpool_run to decide whether
134 * to queue the work or to run it immediately and wait
135 * (it must do the latter for Tflush requests).
136 */
137 l9p_threadpool_run(&conn->lc_tp, req);
138 }
139
140 void
l9p_connection_close(struct l9p_connection * conn)141 l9p_connection_close(struct l9p_connection *conn)
142 {
143 struct ht_iter iter;
144 struct l9p_fid *fid;
145 struct l9p_request *req;
146
147 L9P_LOG(L9P_DEBUG, "waiting for thread pool to shut down");
148 l9p_threadpool_shutdown(&conn->lc_tp);
149
150 /* Drain pending requests (if any) */
151 L9P_LOG(L9P_DEBUG, "draining pending requests");
152 ht_iter(&conn->lc_requests, &iter);
153 while ((req = ht_next(&iter)) != NULL) {
154 #ifdef notyet
155 /* XXX would be good to know if there is anyone listening */
156 if (anyone listening) {
157 /* XXX crude - ops like Tclunk should succeed */
158 req->lr_error = EINTR;
159 l9p_respond(req, false, false);
160 } else
161 #endif
162 l9p_respond(req, true, false); /* use no-answer path */
163 ht_remove_at_iter(&iter);
164 }
165
166 /* Close opened files (if any) */
167 L9P_LOG(L9P_DEBUG, "closing opened files");
168 ht_iter(&conn->lc_files, &iter);
169 while ((fid = ht_next(&iter)) != NULL) {
170 conn->lc_server->ls_backend->freefid(
171 conn->lc_server->ls_backend->softc, fid);
172 free(fid);
173 ht_remove_at_iter(&iter);
174 }
175
176 ht_destroy(&conn->lc_requests);
177 ht_destroy(&conn->lc_files);
178 }
179
180 struct l9p_fid *
l9p_connection_alloc_fid(struct l9p_connection * conn,uint32_t fid)181 l9p_connection_alloc_fid(struct l9p_connection *conn, uint32_t fid)
182 {
183 struct l9p_fid *file;
184
185 file = l9p_calloc(1, sizeof (struct l9p_fid));
186 file->lo_fid = fid;
187 /*
188 * Note that the new fid is not marked valid yet.
189 * The insert here will fail if the fid number is
190 * in use, otherwise we have an invalid fid in the
191 * table (as desired).
192 */
193
194 if (ht_add(&conn->lc_files, fid, file) != 0) {
195 free(file);
196 return (NULL);
197 }
198
199 return (file);
200 }
201
202 void
l9p_connection_remove_fid(struct l9p_connection * conn,struct l9p_fid * fid)203 l9p_connection_remove_fid(struct l9p_connection *conn, struct l9p_fid *fid)
204 {
205 struct l9p_backend *be;
206
207 /* fid should be marked invalid by this point */
208 assert(!l9p_fid_isvalid(fid));
209
210 be = conn->lc_server->ls_backend;
211 be->freefid(be->softc, fid);
212
213 ht_remove(&conn->lc_files, fid->lo_fid);
214 free(fid);
215 }
216