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