xref: /freebsd/lib/libc/net/nscachedcli.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 
30 #include <sys/cdefs.h>
31 #include "namespace.h"
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/event.h>
35 #include <sys/uio.h>
36 #include <sys/un.h>
37 #include <assert.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include "un-namespace.h"
44 #include "nscachedcli.h"
45 
46 #define NS_DEFAULT_CACHED_IO_TIMEOUT	4
47 
48 static int safe_write(struct cached_connection_ *, const void *, size_t);
49 static int safe_read(struct cached_connection_ *, void *, size_t);
50 static int send_credentials(struct cached_connection_ *, int);
51 
52 /*
53  * safe_write writes data to the specified connection and tries to do it in
54  * the very safe manner. We ensure, that we can write to the socket with
55  * kevent. If the data_size can't be sent in one piece, then it would be
56  * splitted.
57  */
58 static int
59 safe_write(struct cached_connection_ *connection, const void *data,
60     size_t data_size)
61 {
62 	struct kevent eventlist;
63 	int nevents;
64 	size_t result;
65 	ssize_t s_result;
66 	struct timespec timeout;
67 
68 	if (data_size == 0)
69 		return (0);
70 
71 	timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT;
72 	timeout.tv_nsec = 0;
73 	result = 0;
74 	do {
75 		nevents = _kevent(connection->write_queue, NULL, 0, &eventlist,
76 		    1, &timeout);
77 		if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) {
78 			s_result = _sendto(connection->sockfd, data + result,
79 			    eventlist.data < data_size - result ?
80 			    eventlist.data : data_size - result, MSG_NOSIGNAL,
81 			    NULL, 0);
82 			if (s_result == -1)
83 				return (-1);
84 			else
85 				result += s_result;
86 
87 			if (eventlist.flags & EV_EOF)
88 				return (result < data_size ? -1 : 0);
89 		} else
90 			return (-1);
91 	} while (result < data_size);
92 
93 	return (0);
94 }
95 
96 /*
97  * safe_read reads data from connection and tries to do it in the very safe
98  * and stable way. It uses kevent to ensure, that the data are available for
99  * reading. If the amount of data to be read is too large, then they would
100  * be splitted.
101  */
102 static int
103 safe_read(struct cached_connection_ *connection, void *data, size_t data_size)
104 {
105 	struct kevent eventlist;
106 	size_t result;
107 	ssize_t s_result;
108 	struct timespec timeout;
109 	int nevents;
110 
111 	if (data_size == 0)
112 		return (0);
113 
114 	timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT;
115 	timeout.tv_nsec = 0;
116 	result = 0;
117 	do {
118 		nevents = _kevent(connection->read_queue, NULL, 0, &eventlist,
119 		    1, &timeout);
120 		if (nevents == 1 && eventlist.filter == EVFILT_READ) {
121 			s_result = _read(connection->sockfd, data + result,
122 			    eventlist.data <= data_size - result ?
123 			    eventlist.data : data_size - result);
124 			if (s_result == -1)
125 				return (-1);
126 			else
127 				result += s_result;
128 
129 			if (eventlist.flags & EV_EOF)
130 				return (result < data_size ? -1 : 0);
131 		} else
132 			return (-1);
133 	} while (result < data_size);
134 
135 	return (0);
136 }
137 
138 /*
139  * Sends the credentials information to the connection along with the
140  * communication element type.
141  */
142 static int
143 send_credentials(struct cached_connection_ *connection, int type)
144 {
145 	union {
146 		struct cmsghdr hdr;
147 		char pad[CMSG_SPACE(sizeof(struct cmsgcred))];
148 	} cmsg;
149 	struct msghdr mhdr;
150 	struct iovec iov;
151 	struct kevent eventlist;
152 	int nevents;
153 	ssize_t result;
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(&mhdr, 0, sizeof(mhdr));
161 	mhdr.msg_iov = &iov;
162 	mhdr.msg_iovlen = 1;
163 	mhdr.msg_control = &cmsg;
164 	mhdr.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 	(void)_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, &mhdr,
177 		    MSG_NOSIGNAL) == -1 ? -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 | SOCK_CLOEXEC, 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 
475 	notification = CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION;
476 	(void)safe_write(ws, &notification, sizeof(int));
477 	__close_cached_connection(ws);
478 	return (0);
479 }
480 
481 struct cached_connection_ *
482 __open_cached_mp_read_session(struct cached_connection_params const *params,
483 	const char *entry_name)
484 {
485 	struct cached_connection_ *connection, *retval;
486 	size_t name_size;
487 	int error_code;
488 	int result;
489 
490 	retval = NULL;
491 	connection = __open_cached_connection(params);
492 	if (connection == NULL)
493 		return (NULL);
494 	connection->mp_flag = 1;
495 
496 	result = send_credentials(connection, CET_MP_READ_SESSION_REQUEST);
497 	if (result != 0)
498 		goto fin;
499 
500 	name_size = strlen(entry_name);
501 	result = safe_write(connection, &name_size, sizeof(size_t));
502 	if (result != 0)
503 		goto fin;
504 
505 	result = safe_write(connection, entry_name, name_size);
506 	if (result != 0)
507 		goto fin;
508 
509 	result = safe_read(connection, &error_code, sizeof(int));
510 	if (result != 0)
511 		goto fin;
512 
513 	if (error_code != 0)
514 		result = error_code;
515 
516 fin:
517 	if (result != 0)
518 		__close_cached_connection(connection);
519 	else
520 		retval = connection;
521 	return (retval);
522 }
523 
524 int
525 __cached_mp_read(struct cached_connection_ *rs, char *data, size_t *data_size)
526 {
527 	size_t result_size;
528 	int error_code, rec_error_code;
529 	int request, result;
530 
531 	error_code = -1;
532 	request = CET_MP_READ_SESSION_READ_REQUEST;
533 	result = safe_write(rs, &request, sizeof(int));
534 	if (result != 0)
535 		goto fin;
536 
537 	result = safe_read(rs, &rec_error_code, sizeof(int));
538 	if (result != 0)
539 		goto fin;
540 
541 	if (rec_error_code != 0) {
542 		error_code = rec_error_code;
543 		goto fin;
544 	}
545 
546 	result = safe_read(rs, &result_size, sizeof(size_t));
547 	if (result != 0)
548 		goto fin;
549 
550 	if (result_size > *data_size) {
551 		*data_size = result_size;
552 		error_code = -2;
553 		goto fin;
554 	}
555 
556 	result = safe_read(rs, data, result_size);
557 	if (result != 0)
558 		goto fin;
559 
560 	*data_size = result_size;
561 	error_code = 0;
562 
563 fin:
564 	return (error_code);
565 }
566 
567 int
568 __close_cached_mp_read_session(struct cached_connection_ *rs)
569 {
570 
571 	__close_cached_connection(rs);
572 	return (0);
573 }
574