1158115Sume/*- 2158115Sume * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru> 3158115Sume * All rights reserved. 4158115Sume * 5158115Sume * Redistribution and use in source and binary forms, with or without 6158115Sume * modification, are permitted provided that the following conditions 7158115Sume * are met: 8158115Sume * 1. Redistributions of source code must retain the above copyright 9158115Sume * notice, this list of conditions and the following disclaimer. 10158115Sume * 2. Redistributions in binary form must reproduce the above copyright 11158115Sume * notice, this list of conditions and the following disclaimer in the 12158115Sume * documentation and/or other materials provided with the distribution. 13158115Sume * 14158115Sume * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15158115Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16158115Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17158115Sume * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18158115Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19158115Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20158115Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21158115Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22158115Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23158115Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24158115Sume * SUCH DAMAGE. 25158115Sume * 26158115Sume */ 27158115Sume 28158115Sume#include <sys/cdefs.h> 29158115Sume__FBSDID("$FreeBSD$"); 30158115Sume 31158115Sume#include "namespace.h" 32158115Sume#include <sys/types.h> 33158115Sume#include <sys/socket.h> 34158115Sume#include <sys/event.h> 35158115Sume#include <sys/uio.h> 36158115Sume#include <sys/un.h> 37158115Sume#include <assert.h> 38158115Sume#include <errno.h> 39158115Sume#include <fcntl.h> 40158115Sume#include <stdlib.h> 41158115Sume#include <string.h> 42158115Sume#include <unistd.h> 43158115Sume#include "un-namespace.h" 44158115Sume#include "nscachedcli.h" 45158115Sume 46158115Sume#define NS_DEFAULT_CACHED_IO_TIMEOUT 4 47158115Sume 48158115Sumestatic int safe_write(struct cached_connection_ *, const void *, size_t); 49158115Sumestatic int safe_read(struct cached_connection_ *, void *, size_t); 50158115Sumestatic int send_credentials(struct cached_connection_ *, int); 51158115Sume 52158115Sume/* 53158115Sume * safe_write writes data to the specified connection and tries to do it in 54158115Sume * the very safe manner. We ensure, that we can write to the socket with 55158115Sume * kevent. If the data_size can't be sent in one piece, then it would be 56158115Sume * splitted. 57158115Sume */ 58158115Sumestatic int 59158115Sumesafe_write(struct cached_connection_ *connection, const void *data, 60158115Sume size_t data_size) 61158115Sume{ 62158115Sume struct kevent eventlist; 63158115Sume int nevents; 64158115Sume size_t result; 65158115Sume ssize_t s_result; 66158115Sume struct timespec timeout; 67158115Sume 68158115Sume if (data_size == 0) 69158115Sume return (0); 70158115Sume 71158115Sume timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT; 72158115Sume timeout.tv_nsec = 0; 73158115Sume result = 0; 74158115Sume do { 75164882Sume nevents = _kevent(connection->write_queue, NULL, 0, &eventlist, 76158115Sume 1, &timeout); 77158115Sume if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) { 78248252Sjilles s_result = _sendto(connection->sockfd, data + result, 79158115Sume eventlist.data < data_size - result ? 80248252Sjilles eventlist.data : data_size - result, MSG_NOSIGNAL, 81248252Sjilles NULL, 0); 82158115Sume if (s_result == -1) 83158115Sume return (-1); 84158115Sume else 85158115Sume result += s_result; 86158115Sume 87158115Sume if (eventlist.flags & EV_EOF) 88158115Sume return (result < data_size ? -1 : 0); 89158115Sume } else 90158115Sume return (-1); 91158115Sume } while (result < data_size); 92158115Sume 93158115Sume return (0); 94158115Sume} 95158115Sume 96158115Sume/* 97158115Sume * safe_read reads data from connection and tries to do it in the very safe 98298830Spfg * and stable way. It uses kevent to ensure, that the data are available for 99158115Sume * reading. If the amount of data to be read is too large, then they would 100158115Sume * be splitted. 101158115Sume */ 102158115Sumestatic int 103158115Sumesafe_read(struct cached_connection_ *connection, void *data, size_t data_size) 104158115Sume{ 105158115Sume struct kevent eventlist; 106158115Sume size_t result; 107158115Sume ssize_t s_result; 108158115Sume struct timespec timeout; 109158115Sume int nevents; 110158115Sume 111158115Sume if (data_size == 0) 112158115Sume return (0); 113158115Sume 114158115Sume timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT; 115158115Sume timeout.tv_nsec = 0; 116158115Sume result = 0; 117158115Sume do { 118164882Sume nevents = _kevent(connection->read_queue, NULL, 0, &eventlist, 119158115Sume 1, &timeout); 120158115Sume if (nevents == 1 && eventlist.filter == EVFILT_READ) { 121158115Sume s_result = _read(connection->sockfd, data + result, 122158115Sume eventlist.data <= data_size - result ? 123158115Sume eventlist.data : data_size - result); 124158115Sume if (s_result == -1) 125158115Sume return (-1); 126158115Sume else 127158115Sume result += s_result; 128158115Sume 129158115Sume if (eventlist.flags & EV_EOF) 130158115Sume return (result < data_size ? -1 : 0); 131158115Sume } else 132158115Sume return (-1); 133158115Sume } while (result < data_size); 134158115Sume 135158115Sume return (0); 136158115Sume} 137158115Sume 138158115Sume/* 139158115Sume * Sends the credentials information to the connection along with the 140158115Sume * communication element type. 141158115Sume */ 142158115Sumestatic int 143158115Sumesend_credentials(struct cached_connection_ *connection, int type) 144158115Sume{ 145158115Sume struct kevent eventlist; 146158115Sume int nevents; 147158115Sume ssize_t result; 148158115Sume int res; 149158115Sume 150158115Sume struct msghdr cred_hdr; 151158115Sume struct iovec iov; 152158115Sume 153158115Sume struct { 154158115Sume struct cmsghdr hdr; 155158257Sume char cred[CMSG_SPACE(sizeof(struct cmsgcred))]; 156158115Sume } cmsg; 157158115Sume 158158115Sume memset(&cmsg, 0, sizeof(cmsg)); 159158257Sume cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct cmsgcred)); 160158115Sume cmsg.hdr.cmsg_level = SOL_SOCKET; 161158115Sume cmsg.hdr.cmsg_type = SCM_CREDS; 162158115Sume 163158115Sume memset(&cred_hdr, 0, sizeof(struct msghdr)); 164158115Sume cred_hdr.msg_iov = &iov; 165158115Sume cred_hdr.msg_iovlen = 1; 166158257Sume cred_hdr.msg_control = (caddr_t)&cmsg; 167158257Sume cred_hdr.msg_controllen = CMSG_SPACE(sizeof(struct cmsgcred)); 168158115Sume 169158115Sume iov.iov_base = &type; 170158115Sume iov.iov_len = sizeof(int); 171158115Sume 172158115Sume EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, 173158115Sume NOTE_LOWAT, sizeof(int), NULL); 174164882Sume res = _kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); 175158115Sume 176164882Sume nevents = _kevent(connection->write_queue, NULL, 0, &eventlist, 1, 177158115Sume NULL); 178158115Sume if (nevents == 1 && eventlist.filter == EVFILT_WRITE) { 179248252Sjilles result = (_sendmsg(connection->sockfd, &cred_hdr, 180248252Sjilles MSG_NOSIGNAL) == -1) ? -1 : 0; 181158115Sume EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, 182158115Sume 0, 0, NULL); 183164882Sume _kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); 184158115Sume return (result); 185158115Sume } else 186158115Sume return (-1); 187158115Sume} 188158115Sume 189158115Sume/* 190158115Sume * Opens the connection with the specified params. Initializes all kqueues. 191158115Sume */ 192158115Sumestruct cached_connection_ * 193158115Sume__open_cached_connection(struct cached_connection_params const *params) 194158115Sume{ 195158115Sume struct cached_connection_ *retval; 196158115Sume struct kevent eventlist; 197158115Sume struct sockaddr_un client_address; 198158115Sume int client_address_len, client_socket; 199158115Sume int res; 200158115Sume 201158115Sume assert(params != NULL); 202158115Sume 203255328Sjilles client_socket = _socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); 204158115Sume client_address.sun_family = PF_LOCAL; 205158115Sume strncpy(client_address.sun_path, params->socket_path, 206158115Sume sizeof(client_address.sun_path)); 207158115Sume client_address_len = sizeof(client_address.sun_family) + 208158115Sume strlen(client_address.sun_path) + 1; 209158115Sume 210158115Sume res = _connect(client_socket, (struct sockaddr *)&client_address, 211158115Sume client_address_len); 212158115Sume if (res == -1) { 213158115Sume _close(client_socket); 214158115Sume return (NULL); 215158115Sume } 216158115Sume _fcntl(client_socket, F_SETFL, O_NONBLOCK); 217158115Sume 218158115Sume retval = malloc(sizeof(struct cached_connection_)); 219158115Sume assert(retval != NULL); 220158115Sume memset(retval, 0, sizeof(struct cached_connection_)); 221158115Sume 222158115Sume retval->sockfd = client_socket; 223158115Sume 224158115Sume retval->write_queue = kqueue(); 225158115Sume assert(retval->write_queue != -1); 226158115Sume 227158115Sume EV_SET(&eventlist, retval->sockfd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); 228164882Sume res = _kevent(retval->write_queue, &eventlist, 1, NULL, 0, NULL); 229158115Sume 230158115Sume retval->read_queue = kqueue(); 231158115Sume assert(retval->read_queue != -1); 232158115Sume 233158115Sume EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD, 0, 0, NULL); 234164882Sume res = _kevent(retval->read_queue, &eventlist, 1, NULL, 0, NULL); 235158115Sume 236158115Sume return (retval); 237158115Sume} 238158115Sume 239158115Sumevoid 240158115Sume__close_cached_connection(struct cached_connection_ *connection) 241158115Sume{ 242158115Sume assert(connection != NULL); 243158115Sume 244158115Sume _close(connection->sockfd); 245158115Sume _close(connection->read_queue); 246158115Sume _close(connection->write_queue); 247158115Sume free(connection); 248158115Sume} 249158115Sume 250158115Sume/* 251158115Sume * This function is very close to the cache_write function of the caching 252158115Sume * library, which is used in the caching daemon. It caches the data with the 253158115Sume * specified key in the cache entry with entry_name. 254158115Sume */ 255158115Sumeint 256158115Sume__cached_write(struct cached_connection_ *connection, const char *entry_name, 257158115Sume const char *key, size_t key_size, const char *data, size_t data_size) 258158115Sume{ 259158115Sume size_t name_size; 260158115Sume int error_code; 261158115Sume int result; 262158115Sume 263158115Sume error_code = -1; 264158115Sume result = 0; 265158115Sume result = send_credentials(connection, CET_WRITE_REQUEST); 266158115Sume if (result != 0) 267158115Sume goto fin; 268158115Sume 269158115Sume name_size = strlen(entry_name); 270158115Sume result = safe_write(connection, &name_size, sizeof(size_t)); 271158115Sume if (result != 0) 272158115Sume goto fin; 273158115Sume 274158115Sume result = safe_write(connection, &key_size, sizeof(size_t)); 275158115Sume if (result != 0) 276158115Sume goto fin; 277158115Sume 278158115Sume result = safe_write(connection, &data_size, sizeof(size_t)); 279158115Sume if (result != 0) 280158115Sume goto fin; 281158115Sume 282158115Sume result = safe_write(connection, entry_name, name_size); 283158115Sume if (result != 0) 284158115Sume goto fin; 285158115Sume 286158115Sume result = safe_write(connection, key, key_size); 287158115Sume if (result != 0) 288158115Sume goto fin; 289158115Sume 290158115Sume result = safe_write(connection, data, data_size); 291158115Sume if (result != 0) 292158115Sume goto fin; 293158115Sume 294158115Sume result = safe_read(connection, &error_code, sizeof(int)); 295158115Sume if (result != 0) 296158115Sume error_code = -1; 297158115Sume 298158115Sumefin: 299158115Sume return (error_code); 300158115Sume} 301158115Sume 302158115Sume/* 303158115Sume * This function is very close to the cache_read function of the caching 304158115Sume * library, which is used in the caching daemon. It reads cached data with the 305158115Sume * specified key from the cache entry with entry_name. 306158115Sume */ 307158115Sumeint 308158115Sume__cached_read(struct cached_connection_ *connection, const char *entry_name, 309158115Sume const char *key, size_t key_size, char *data, size_t *data_size) 310158115Sume{ 311158115Sume size_t name_size, result_size; 312158115Sume int error_code, rec_error_code; 313158115Sume int result; 314158115Sume 315158115Sume assert(connection != NULL); 316158115Sume result = 0; 317158115Sume error_code = -1; 318158115Sume 319158115Sume result = send_credentials(connection, CET_READ_REQUEST); 320158115Sume if (result != 0) 321158115Sume goto fin; 322158115Sume 323158115Sume name_size = strlen(entry_name); 324158115Sume result = safe_write(connection, &name_size, sizeof(size_t)); 325158115Sume if (result != 0) 326158115Sume goto fin; 327158115Sume 328158115Sume result = safe_write(connection, &key_size, sizeof(size_t)); 329158115Sume if (result != 0) 330158115Sume goto fin; 331158115Sume 332158115Sume result = safe_write(connection, entry_name, name_size); 333158115Sume if (result != 0) 334158115Sume goto fin; 335158115Sume 336158115Sume result = safe_write(connection, key, key_size); 337158115Sume if (result != 0) 338158115Sume goto fin; 339158115Sume 340158115Sume result = safe_read(connection, &rec_error_code, sizeof(int)); 341158115Sume if (result != 0) 342158115Sume goto fin; 343158115Sume 344158115Sume if (rec_error_code != 0) { 345158115Sume error_code = rec_error_code; 346158115Sume goto fin; 347158115Sume } 348158115Sume 349158115Sume result = safe_read(connection, &result_size, sizeof(size_t)); 350158115Sume if (result != 0) 351158115Sume goto fin; 352158115Sume 353158115Sume if (result_size > *data_size) { 354158115Sume *data_size = result_size; 355158115Sume error_code = -2; 356158115Sume goto fin; 357158115Sume } 358158115Sume 359158115Sume result = safe_read(connection, data, result_size); 360158115Sume if (result != 0) 361158115Sume goto fin; 362158115Sume 363158115Sume *data_size = result_size; 364158115Sume error_code = 0; 365158115Sume 366158115Sumefin: 367158115Sume return (error_code); 368158115Sume} 369158115Sume 370158115Sume/* 371158115Sume * Initializes the mp_write_session. For such a session the new connection 372158115Sume * would be opened. The data should be written to the session with 373158115Sume * __cached_mp_write function. The __close_cached_mp_write_session function 374158115Sume * should be used to submit session and __abandon_cached_mp_write_session - to 375158115Sume * abandon it. When the session is submitted, the whole se 376158115Sume */ 377158115Sumestruct cached_connection_ * 378158115Sume__open_cached_mp_write_session(struct cached_connection_params const *params, 379158115Sume const char *entry_name) 380158115Sume{ 381158115Sume struct cached_connection_ *connection, *retval; 382158115Sume size_t name_size; 383158115Sume int error_code; 384158115Sume int result; 385158115Sume 386158115Sume retval = NULL; 387158115Sume connection = __open_cached_connection(params); 388158115Sume if (connection == NULL) 389158115Sume return (NULL); 390158115Sume connection->mp_flag = 1; 391158115Sume 392158115Sume result = send_credentials(connection, CET_MP_WRITE_SESSION_REQUEST); 393158115Sume if (result != 0) 394158115Sume goto fin; 395158115Sume 396158115Sume name_size = strlen(entry_name); 397158115Sume result = safe_write(connection, &name_size, sizeof(size_t)); 398158115Sume if (result != 0) 399158115Sume goto fin; 400158115Sume 401158115Sume result = safe_write(connection, entry_name, name_size); 402158115Sume if (result != 0) 403158115Sume goto fin; 404158115Sume 405158115Sume result = safe_read(connection, &error_code, sizeof(int)); 406158115Sume if (result != 0) 407158115Sume goto fin; 408158115Sume 409158115Sume if (error_code != 0) 410158115Sume result = error_code; 411158115Sume 412158115Sumefin: 413158115Sume if (result != 0) 414158115Sume __close_cached_connection(connection); 415158115Sume else 416158115Sume retval = connection; 417158115Sume return (retval); 418158115Sume} 419158115Sume 420158115Sume/* 421158115Sume * Adds new portion of data to the opened write session 422158115Sume */ 423158115Sumeint 424158115Sume__cached_mp_write(struct cached_connection_ *ws, const char *data, 425158115Sume size_t data_size) 426158115Sume{ 427158115Sume int request, result; 428158115Sume int error_code; 429158115Sume 430158115Sume error_code = -1; 431158115Sume 432158115Sume request = CET_MP_WRITE_SESSION_WRITE_REQUEST; 433158115Sume result = safe_write(ws, &request, sizeof(int)); 434158115Sume if (result != 0) 435158115Sume goto fin; 436158115Sume 437158115Sume result = safe_write(ws, &data_size, sizeof(size_t)); 438158115Sume if (result != 0) 439158115Sume goto fin; 440158115Sume 441158115Sume result = safe_write(ws, data, data_size); 442158115Sume if (result != 0) 443158115Sume goto fin; 444158115Sume 445158115Sume result = safe_read(ws, &error_code, sizeof(int)); 446158115Sume if (result != 0) 447158115Sume error_code = -1; 448158115Sume 449158115Sumefin: 450158115Sume return (error_code); 451158115Sume} 452158115Sume 453158115Sume/* 454158115Sume * Abandons all operations with the write session. All data, that were written 455158115Sume * to the session before, are discarded. 456158115Sume */ 457158115Sumeint 458158115Sume__abandon_cached_mp_write_session(struct cached_connection_ *ws) 459158115Sume{ 460158115Sume int notification; 461158115Sume int result; 462158115Sume 463158115Sume notification = CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION; 464158115Sume result = safe_write(ws, ¬ification, sizeof(int)); 465158115Sume __close_cached_connection(ws); 466158115Sume return (result); 467158115Sume} 468158115Sume 469158115Sume/* 470158115Sume * Gracefully closes the write session. The data, that were previously written 471158115Sume * to the session, are committed. 472158115Sume */ 473158115Sumeint 474158115Sume__close_cached_mp_write_session(struct cached_connection_ *ws) 475158115Sume{ 476158115Sume int notification; 477158115Sume int result; 478158115Sume 479158115Sume notification = CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION; 480158115Sume result = safe_write(ws, ¬ification, sizeof(int)); 481158115Sume __close_cached_connection(ws); 482158115Sume return (0); 483158115Sume} 484158115Sume 485158115Sumestruct cached_connection_ * 486158115Sume__open_cached_mp_read_session(struct cached_connection_params const *params, 487158115Sume const char *entry_name) 488158115Sume{ 489158115Sume struct cached_connection_ *connection, *retval; 490158115Sume size_t name_size; 491158115Sume int error_code; 492158115Sume int result; 493158115Sume 494158115Sume retval = NULL; 495158115Sume connection = __open_cached_connection(params); 496158115Sume if (connection == NULL) 497158115Sume return (NULL); 498158115Sume connection->mp_flag = 1; 499158115Sume 500158115Sume result = send_credentials(connection, CET_MP_READ_SESSION_REQUEST); 501158115Sume if (result != 0) 502158115Sume goto fin; 503158115Sume 504158115Sume name_size = strlen(entry_name); 505158115Sume result = safe_write(connection, &name_size, sizeof(size_t)); 506158115Sume if (result != 0) 507158115Sume goto fin; 508158115Sume 509158115Sume result = safe_write(connection, entry_name, name_size); 510158115Sume if (result != 0) 511158115Sume goto fin; 512158115Sume 513158115Sume result = safe_read(connection, &error_code, sizeof(int)); 514158115Sume if (result != 0) 515158115Sume goto fin; 516158115Sume 517158115Sume if (error_code != 0) 518158115Sume result = error_code; 519158115Sume 520158115Sumefin: 521158115Sume if (result != 0) 522158115Sume __close_cached_connection(connection); 523158115Sume else 524158115Sume retval = connection; 525158115Sume return (retval); 526158115Sume} 527158115Sume 528158115Sumeint 529158115Sume__cached_mp_read(struct cached_connection_ *rs, char *data, size_t *data_size) 530158115Sume{ 531158115Sume size_t result_size; 532158115Sume int error_code, rec_error_code; 533158115Sume int request, result; 534158115Sume 535158115Sume error_code = -1; 536158115Sume request = CET_MP_READ_SESSION_READ_REQUEST; 537158115Sume result = safe_write(rs, &request, sizeof(int)); 538158115Sume if (result != 0) 539158115Sume goto fin; 540158115Sume 541158115Sume result = safe_read(rs, &rec_error_code, sizeof(int)); 542158115Sume if (result != 0) 543158115Sume goto fin; 544158115Sume 545158115Sume if (rec_error_code != 0) { 546158115Sume error_code = rec_error_code; 547158115Sume goto fin; 548158115Sume } 549158115Sume 550158115Sume result = safe_read(rs, &result_size, sizeof(size_t)); 551158115Sume if (result != 0) 552158115Sume goto fin; 553158115Sume 554158115Sume if (result_size > *data_size) { 555158115Sume *data_size = result_size; 556158115Sume error_code = -2; 557158115Sume goto fin; 558158115Sume } 559158115Sume 560158115Sume result = safe_read(rs, data, result_size); 561158115Sume if (result != 0) 562158115Sume goto fin; 563158115Sume 564158115Sume *data_size = result_size; 565158115Sume error_code = 0; 566158115Sume 567158115Sumefin: 568158115Sume return (error_code); 569158115Sume} 570158115Sume 571158115Sumeint 572158115Sume__close_cached_mp_read_session(struct cached_connection_ *rs) 573158115Sume{ 574158115Sume 575158115Sume __close_cached_connection(rs); 576158115Sume return (0); 577158115Sume} 578