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 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 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 81 l9p_connection_free(struct l9p_connection *conn) 82 { 83 84 LIST_REMOVE(conn, lc_link); 85 free(conn); 86 } 87 88 void 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 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 * 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 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