xref: /dragonfly/lib/libc/net/nscachedcli.c (revision 89a89091)
1 /*-
2  * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided 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 AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY 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, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/lib/libc/net/nscachedcli.c,v 1.3 2006/12/04 17:08:43 ume Exp $
27  */
28 
29 #include "namespace.h"
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <sys/event.h>
33 #include <sys/uio.h>
34 #include <sys/un.h>
35 #include <assert.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include "un-namespace.h"
42 #include "nscachedcli.h"
43 
44 #define NS_DEFAULT_CACHED_IO_TIMEOUT	4
45 
46 static int safe_write(struct cached_connection_ *, const void *, size_t);
47 static int safe_read(struct cached_connection_ *, void *, size_t);
48 static int send_credentials(struct cached_connection_ *, int);
49 
50 /*
51  * safe_write writes data to the specified connection and tries to do it in
52  * the very safe manner. We ensure, that we can write to the socket with
53  * kevent. If the data_size can't be sent in one piece, then it would be
54  * splitted.
55  */
56 static int
57 safe_write(struct cached_connection_ *connection, const void *data,
58     size_t data_size)
59 {
60 	struct kevent eventlist;
61 	int nevents;
62 	size_t result;
63 	ssize_t s_result;
64 	struct timespec timeout;
65 
66 	if (data_size == 0)
67 		return (0);
68 
69 	timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT;
70 	timeout.tv_nsec = 0;
71 	result = 0;
72 	do {
73 		nevents = _kevent(connection->write_queue, NULL, 0, &eventlist,
74 		    1, &timeout);
75 		if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) {
76 			s_result = _write(connection->sockfd, data + result,
77 			    eventlist.data < data_size - result ?
78 			    eventlist.data : data_size - result);
79 			if (s_result == -1)
80 				return (-1);
81 			else
82 				result += s_result;
83 
84 			if (eventlist.flags & EV_EOF)
85 				return (result < data_size ? -1 : 0);
86 		} else
87 			return (-1);
88 	} while (result < data_size);
89 
90 	return (0);
91 }
92 
93 /*
94  * safe_read reads data from connection and tries to do it in the very safe
95  * and stable way. It uses kevent to ensure, that the data are availabe for
96  * reading. If the amount of data to be read is too large, then they would
97  * be splitted.
98  */
99 static int
100 safe_read(struct cached_connection_ *connection, void *data, size_t data_size)
101 {
102 	struct kevent eventlist;
103 	size_t result;
104 	ssize_t s_result;
105 	struct timespec timeout;
106 	int nevents;
107 
108 	if (data_size == 0)
109 		return (0);
110 
111 	timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT;
112 	timeout.tv_nsec = 0;
113 	result = 0;
114 	do {
115 		nevents = _kevent(connection->read_queue, NULL, 0, &eventlist,
116 		    1, &timeout);
117 		if (nevents == 1 && eventlist.filter == EVFILT_READ) {
118 			s_result = _read(connection->sockfd, data + result,
119 			    eventlist.data <= data_size - result ?
120 			    eventlist.data : data_size - result);
121 			if (s_result == -1)
122 				return (-1);
123 			else
124 				result += s_result;
125 
126 			if (eventlist.flags & EV_EOF)
127 				return (result < data_size ? -1 : 0);
128 		} else
129 			return (-1);
130 	} while (result < data_size);
131 
132 	return (0);
133 }
134 
135 /*
136  * Sends the credentials information to the connection along with the
137  * communication element type.
138  */
139 static int
140 send_credentials(struct cached_connection_ *connection, int type)
141 {
142 	struct kevent eventlist;
143 	int nevents;
144 	ssize_t result;
145 	int res;
146 
147 	struct msghdr cred_hdr;
148 	struct iovec iov;
149 
150 	struct {
151 		struct cmsghdr hdr;
152 		char cred[CMSG_SPACE(sizeof(struct cmsgcred))];
153 	} cmsg;
154 
155 	memset(&cmsg, 0, sizeof(cmsg));
156 	cmsg.hdr.cmsg_len =  CMSG_LEN(sizeof(struct cmsgcred));
157 	cmsg.hdr.cmsg_level = SOL_SOCKET;
158 	cmsg.hdr.cmsg_type = SCM_CREDS;
159 
160 	memset(&cred_hdr, 0, sizeof(struct msghdr));
161 	cred_hdr.msg_iov = &iov;
162 	cred_hdr.msg_iovlen = 1;
163 	cred_hdr.msg_control = (caddr_t)&cmsg;
164 	cred_hdr.msg_controllen = CMSG_SPACE(sizeof(struct cmsgcred));
165 
166 	iov.iov_base = &type;
167 	iov.iov_len = sizeof(int);
168 
169 	EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD,
170 	    NOTE_LOWAT, sizeof(int), NULL);
171 	res = _kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL);
172 
173 	nevents = _kevent(connection->write_queue, NULL, 0, &eventlist, 1,
174 	    NULL);
175 	if (nevents == 1 && eventlist.filter == EVFILT_WRITE) {
176 		result = (_sendmsg(connection->sockfd, &cred_hdr, 0) == -1) ?
177 		    -1 : 0;
178 		EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD,
179 		    0, 0, NULL);
180 		_kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL);
181 		return (result);
182 	} else
183 		return (-1);
184 }
185 
186 /*
187  * Opens the connection with the specified params. Initializes all kqueues.
188  */
189 struct cached_connection_ *
190 __open_cached_connection(struct cached_connection_params const *params)
191 {
192 	struct cached_connection_ *retval;
193 	struct kevent eventlist;
194 	struct sockaddr_un client_address;
195 	int client_address_len, client_socket;
196 	int res;
197 
198 	assert(params != NULL);
199 
200 	client_socket = _socket(PF_LOCAL, SOCK_STREAM, 0);
201 	client_address.sun_family = PF_LOCAL;
202 	strncpy(client_address.sun_path, params->socket_path,
203 	    sizeof(client_address.sun_path));
204 	client_address_len = sizeof(client_address.sun_family) +
205 	    strlen(client_address.sun_path) + 1;
206 
207 	res = _connect(client_socket, (struct sockaddr *)&client_address,
208 	    client_address_len);
209 	if (res == -1) {
210 		_close(client_socket);
211 		return (NULL);
212 	}
213 	_fcntl(client_socket, F_SETFL, O_NONBLOCK);
214 
215 	retval = malloc(sizeof(struct cached_connection_));
216 	assert(retval != NULL);
217 	memset(retval, 0, sizeof(struct cached_connection_));
218 
219 	retval->sockfd = client_socket;
220 
221 	retval->write_queue = kqueue();
222 	assert(retval->write_queue != -1);
223 
224 	EV_SET(&eventlist, retval->sockfd, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
225 	res = _kevent(retval->write_queue, &eventlist, 1, NULL, 0, NULL);
226 
227 	retval->read_queue = kqueue();
228 	assert(retval->read_queue != -1);
229 
230 	EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD, 0, 0, NULL);
231 	res = _kevent(retval->read_queue, &eventlist, 1, NULL, 0, NULL);
232 
233 	return (retval);
234 }
235 
236 void
237 __close_cached_connection(struct cached_connection_ *connection)
238 {
239 	assert(connection != NULL);
240 
241 	_close(connection->sockfd);
242 	_close(connection->read_queue);
243 	_close(connection->write_queue);
244 	free(connection);
245 }
246 
247 /*
248  * This function is very close to the cache_write function of the caching
249  * library, which is used in the caching daemon. It caches the data with the
250  * specified key in the cache entry with entry_name.
251  */
252 int
253 __cached_write(struct cached_connection_ *connection, const char *entry_name,
254     const char *key, size_t key_size, const char *data, size_t data_size)
255 {
256 	size_t name_size;
257 	int error_code;
258 	int result;
259 
260 	error_code = -1;
261 	result = 0;
262 	result = send_credentials(connection, CET_WRITE_REQUEST);
263 	if (result != 0)
264 		goto fin;
265 
266 	name_size = strlen(entry_name);
267 	result = safe_write(connection, &name_size, sizeof(size_t));
268 	if (result != 0)
269 		goto fin;
270 
271 	result = safe_write(connection, &key_size, sizeof(size_t));
272 	if (result != 0)
273 		goto fin;
274 
275 	result = safe_write(connection, &data_size, sizeof(size_t));
276 	if (result != 0)
277 		goto fin;
278 
279 	result = safe_write(connection, entry_name, name_size);
280 	if (result != 0)
281 		goto fin;
282 
283 	result = safe_write(connection, key, key_size);
284 	if (result != 0)
285 		goto fin;
286 
287 	result = safe_write(connection, data, data_size);
288 	if (result != 0)
289 		goto fin;
290 
291 	result = safe_read(connection, &error_code, sizeof(int));
292 	if (result != 0)
293 		error_code = -1;
294 
295 fin:
296 	return (error_code);
297 }
298 
299 /*
300  * This function is very close to the cache_read function of the caching
301  * library, which is used in the caching daemon. It reads cached data with the
302  * specified key from the cache entry with entry_name.
303  */
304 int
305 __cached_read(struct cached_connection_ *connection, const char *entry_name,
306     const char *key, size_t key_size, char *data, size_t *data_size)
307 {
308 	size_t name_size, result_size;
309 	int error_code, rec_error_code;
310 	int result;
311 
312 	assert(connection != NULL);
313 	result = 0;
314 	error_code = -1;
315 
316 	result = send_credentials(connection, CET_READ_REQUEST);
317 	if (result != 0)
318 		goto fin;
319 
320 	name_size = strlen(entry_name);
321 	result = safe_write(connection, &name_size, sizeof(size_t));
322 	if (result != 0)
323 		goto fin;
324 
325 	result = safe_write(connection, &key_size, sizeof(size_t));
326 	if (result != 0)
327 		goto fin;
328 
329 	result = safe_write(connection, entry_name, name_size);
330 	if (result != 0)
331 		goto fin;
332 
333 	result = safe_write(connection, key, key_size);
334 	if (result != 0)
335 		goto fin;
336 
337 	result = safe_read(connection, &rec_error_code, sizeof(int));
338 	if (result != 0)
339 		goto fin;
340 
341 	if (rec_error_code != 0) {
342 		error_code = rec_error_code;
343 		goto fin;
344 	}
345 
346 	result = safe_read(connection, &result_size, sizeof(size_t));
347 	if (result != 0)
348 		goto fin;
349 
350 	if (result_size > *data_size) {
351 		*data_size = result_size;
352 		error_code = -2;
353 		goto fin;
354 	}
355 
356 	result = safe_read(connection, data, result_size);
357 	if (result != 0)
358 		goto fin;
359 
360 	*data_size = result_size;
361 	error_code = 0;
362 
363 fin:
364 	return (error_code);
365 }
366 
367 /*
368  * Initializes the mp_write_session. For such a session the new connection
369  * would be opened. The data should be written to the session with
370  * __cached_mp_write function. The __close_cached_mp_write_session function
371  * should be used to submit session and __abandon_cached_mp_write_session - to
372  * abandon it. When the session is submitted, the whole se
373  */
374 struct cached_connection_ *
375 __open_cached_mp_write_session(struct cached_connection_params const *params,
376     const char *entry_name)
377 {
378 	struct cached_connection_ *connection, *retval;
379 	size_t name_size;
380 	int error_code;
381 	int result;
382 
383 	retval = NULL;
384 	connection = __open_cached_connection(params);
385 	if (connection == NULL)
386 		return (NULL);
387 	connection->mp_flag = 1;
388 
389 	result = send_credentials(connection, CET_MP_WRITE_SESSION_REQUEST);
390 	if (result != 0)
391 		goto fin;
392 
393 	name_size = strlen(entry_name);
394 	result = safe_write(connection, &name_size, sizeof(size_t));
395 	if (result != 0)
396 		goto fin;
397 
398 	result = safe_write(connection, entry_name, name_size);
399 	if (result != 0)
400 		goto fin;
401 
402 	result = safe_read(connection, &error_code, sizeof(int));
403 	if (result != 0)
404 		goto fin;
405 
406 	if (error_code != 0)
407 		result = error_code;
408 
409 fin:
410 	if (result != 0)
411 		__close_cached_connection(connection);
412 	else
413 		retval = connection;
414 	return (retval);
415 }
416 
417 /*
418  * Adds new portion of data to the opened write session
419  */
420 int
421 __cached_mp_write(struct cached_connection_ *ws, const char *data,
422     size_t data_size)
423 {
424 	int request, result;
425 	int error_code;
426 
427 	error_code = -1;
428 
429 	request = CET_MP_WRITE_SESSION_WRITE_REQUEST;
430 	result = safe_write(ws, &request, sizeof(int));
431 	if (result != 0)
432 		goto fin;
433 
434 	result = safe_write(ws, &data_size, sizeof(size_t));
435 	if (result != 0)
436 		goto fin;
437 
438 	result = safe_write(ws, data, data_size);
439 	if (result != 0)
440 		goto fin;
441 
442 	result = safe_read(ws, &error_code, sizeof(int));
443 	if (result != 0)
444 		error_code = -1;
445 
446 fin:
447 	return (error_code);
448 }
449 
450 /*
451  * Abandons all operations with the write session. All data, that were written
452  * to the session before, are discarded.
453  */
454 int
455 __abandon_cached_mp_write_session(struct cached_connection_ *ws)
456 {
457 	int notification;
458 	int result;
459 
460 	notification = CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION;
461 	result = safe_write(ws, &notification, sizeof(int));
462 	__close_cached_connection(ws);
463 	return (result);
464 }
465 
466 /*
467  * Gracefully closes the write session. The data, that were previously written
468  * to the session, are committed.
469  */
470 int
471 __close_cached_mp_write_session(struct cached_connection_ *ws)
472 {
473 	int notification;
474 	int result;
475 
476 	notification = CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION;
477 	result = safe_write(ws, &notification, sizeof(int));
478 	__close_cached_connection(ws);
479 	return (0);
480 }
481 
482 struct cached_connection_ *
483 __open_cached_mp_read_session(struct cached_connection_params const *params,
484 	const char *entry_name)
485 {
486 	struct cached_connection_ *connection, *retval;
487 	size_t name_size;
488 	int error_code;
489 	int result;
490 
491 	retval = NULL;
492 	connection = __open_cached_connection(params);
493 	if (connection == NULL)
494 		return (NULL);
495 	connection->mp_flag = 1;
496 
497 	result = send_credentials(connection, CET_MP_READ_SESSION_REQUEST);
498 	if (result != 0)
499 		goto fin;
500 
501 	name_size = strlen(entry_name);
502 	result = safe_write(connection, &name_size, sizeof(size_t));
503 	if (result != 0)
504 		goto fin;
505 
506 	result = safe_write(connection, entry_name, name_size);
507 	if (result != 0)
508 		goto fin;
509 
510 	result = safe_read(connection, &error_code, sizeof(int));
511 	if (result != 0)
512 		goto fin;
513 
514 	if (error_code != 0)
515 		result = error_code;
516 
517 fin:
518 	if (result != 0)
519 		__close_cached_connection(connection);
520 	else
521 		retval = connection;
522 	return (retval);
523 }
524 
525 int
526 __cached_mp_read(struct cached_connection_ *rs, char *data, size_t *data_size)
527 {
528 	size_t result_size;
529 	int error_code, rec_error_code;
530 	int request, result;
531 
532 	error_code = -1;
533 	request = CET_MP_READ_SESSION_READ_REQUEST;
534 	result = safe_write(rs, &request, sizeof(int));
535 	if (result != 0)
536 		goto fin;
537 
538 	result = safe_read(rs, &rec_error_code, sizeof(int));
539 	if (result != 0)
540 		goto fin;
541 
542 	if (rec_error_code != 0) {
543 		error_code = rec_error_code;
544 		goto fin;
545 	}
546 
547 	result = safe_read(rs, &result_size, sizeof(size_t));
548 	if (result != 0)
549 		goto fin;
550 
551 	if (result_size > *data_size) {
552 		*data_size = result_size;
553 		error_code = -2;
554 		goto fin;
555 	}
556 
557 	result = safe_read(rs, data, result_size);
558 	if (result != 0)
559 		goto fin;
560 
561 	*data_size = result_size;
562 	error_code = 0;
563 
564 fin:
565 	return (error_code);
566 }
567 
568 int
569 __close_cached_mp_read_session(struct cached_connection_ *rs)
570 {
571 
572 	__close_cached_connection(rs);
573 	return (0);
574 }
575