source: mainline/uspace/lib/c/generic/net/socket_client.c@ eb221e5

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since eb221e5 was 228e490, checked in by Martin Decky <martin@…>, 15 years ago

initial modifications for supporting declarative IPC interfaces

  • Property mode set to 100644
File size: 32.8 KB
Line 
1/*
2 * Copyright (c) 2009 Lukas Mejdrech
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 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libc
30 * @{
31 */
32
33/** @file
34 * Socket application program interface (API) implementation.
35 * @see socket.h for more information.
36 * This is a part of the network application library.
37 */
38
39#include <assert.h>
40#include <async.h>
41#include <fibril_synch.h>
42#include <stdint.h>
43#include <stdlib.h>
44#include <errno.h>
45
46#include <ipc/services.h>
47#include <ipc/socket.h>
48
49#include <net/modules.h>
50#include <net/in.h>
51#include <net/socket.h>
52#include <adt/dynamic_fifo.h>
53#include <adt/int_map.h>
54
55/** Initial received packet queue size. */
56#define SOCKET_INITIAL_RECEIVED_SIZE 4
57
58/** Maximum received packet queue size. */
59#define SOCKET_MAX_RECEIVED_SIZE 0
60
61/** Initial waiting sockets queue size. */
62#define SOCKET_INITIAL_ACCEPTED_SIZE 1
63
64/** Maximum waiting sockets queue size. */
65#define SOCKET_MAX_ACCEPTED_SIZE 0
66
67/** Default timeout for connections in microseconds. */
68#define SOCKET_CONNECT_TIMEOUT (1 * 1000 * 1000)
69
70/**
71 * Maximum number of random attempts to find a new socket identifier before
72 * switching to the sequence.
73 */
74#define SOCKET_ID_TRIES 100
75
76/** Type definition of the socket specific data.
77 * @see socket
78 */
79typedef struct socket socket_t;
80
81/** Socket specific data.
82 *
83 * Each socket lock locks only its structure part and any number of them may be
84 * locked simultaneously.
85 */
86struct socket {
87 /** Socket identifier. */
88 int socket_id;
89 /** Parent module phone. */
90 int phone;
91 /** Parent module service. */
92 services_t service;
93
94 /**
95 * Underlying protocol header size.
96 * Sending and receiving optimalization.
97 */
98 size_t header_size;
99
100 /** Packet data fragment size. Sending optimization. */
101 size_t data_fragment_size;
102
103 /**
104 * Sending safety lock.
105 * Locks the header_size and data_fragment_size attributes.
106 */
107 fibril_rwlock_t sending_lock;
108
109 /** Received packets queue. */
110 dyn_fifo_t received;
111
112 /**
113 * Received packets safety lock.
114 * Used for receiving and receive notifications.
115 * Locks the received attribute.
116 */
117 fibril_mutex_t receive_lock;
118
119 /** Received packets signaling. Signaled upon receive notification. */
120 fibril_condvar_t receive_signal;
121 /** Waiting sockets queue. */
122 dyn_fifo_t accepted;
123
124 /**
125 * Waiting sockets safety lock.
126 * Used for accepting and accept notifications.
127 * Locks the accepted attribute.
128 */
129 fibril_mutex_t accept_lock;
130
131 /** Waiting sockets signaling. Signaled upon accept notification. */
132 fibril_condvar_t accept_signal;
133
134 /**
135 * The number of blocked functions called.
136 * Used while waiting for the received packets or accepted sockets.
137 */
138 int blocked;
139};
140
141/** Sockets map.
142 * Maps socket identifiers to the socket specific data.
143 * @see int_map.h
144 */
145INT_MAP_DECLARE(sockets, socket_t);
146
147/** Socket client library global data. */
148static struct socket_client_globals {
149 /** TCP module phone. */
150 int tcp_phone;
151 /** UDP module phone. */
152 int udp_phone;
153
154// /** The last socket identifier.
155// */
156// int last_id;
157
158 /** Active sockets. */
159 sockets_t *sockets;
160
161 /** Safety lock.
162 * Write lock is used only for adding or removing sockets.
163 * When locked for writing, no other socket locks need to be locked.
164 * When locked for reading, any other socket locks may be locked.
165 * No socket lock may be locked if this lock is unlocked.
166 */
167 fibril_rwlock_t lock;
168} socket_globals = {
169 .tcp_phone = -1,
170 .udp_phone = -1,
171// .last_id = 0,
172 .sockets = NULL,
173 .lock = FIBRIL_RWLOCK_INITIALIZER(socket_globals.lock)
174};
175
176INT_MAP_IMPLEMENT(sockets, socket_t);
177
178/** Returns the active sockets.
179 *
180 * @return The active sockets.
181 */
182static sockets_t *socket_get_sockets(void)
183{
184 if (!socket_globals.sockets) {
185 socket_globals.sockets =
186 (sockets_t *) malloc(sizeof(sockets_t));
187 if (!socket_globals.sockets)
188 return NULL;
189
190 if (sockets_initialize(socket_globals.sockets) != EOK) {
191 free(socket_globals.sockets);
192 socket_globals.sockets = NULL;
193 }
194
195 srand(task_get_id());
196 }
197
198 return socket_globals.sockets;
199}
200
201/** Default thread for new connections.
202 *
203 * @param[in] iid The initial message identifier.
204 * @param[in] icall The initial message call structure.
205 */
206static void socket_connection(ipc_callid_t iid, ipc_call_t * icall)
207{
208 ipc_callid_t callid;
209 ipc_call_t call;
210 socket_t *socket;
211 int rc;
212
213loop:
214 callid = async_get_call(&call);
215
216 switch (IPC_GET_IMETHOD(call)) {
217 case NET_SOCKET_RECEIVED:
218 case NET_SOCKET_ACCEPTED:
219 case NET_SOCKET_DATA_FRAGMENT_SIZE:
220 fibril_rwlock_read_lock(&socket_globals.lock);
221
222 // find the socket
223 socket = sockets_find(socket_get_sockets(),
224 SOCKET_GET_SOCKET_ID(call));
225 if (!socket) {
226 rc = ENOTSOCK;
227 fibril_rwlock_read_unlock(&socket_globals.lock);
228 break;
229 }
230
231 switch (IPC_GET_IMETHOD(call)) {
232 case NET_SOCKET_RECEIVED:
233 fibril_mutex_lock(&socket->receive_lock);
234 // push the number of received packet fragments
235 rc = dyn_fifo_push(&socket->received,
236 SOCKET_GET_DATA_FRAGMENTS(call),
237 SOCKET_MAX_RECEIVED_SIZE);
238 if (rc == EOK) {
239 // signal the received packet
240 fibril_condvar_signal(&socket->receive_signal);
241 }
242 fibril_mutex_unlock(&socket->receive_lock);
243 break;
244
245 case NET_SOCKET_ACCEPTED:
246 // push the new socket identifier
247 fibril_mutex_lock(&socket->accept_lock);
248 rc = dyn_fifo_push(&socket->accepted, 1,
249 SOCKET_MAX_ACCEPTED_SIZE);
250 if (rc == EOK) {
251 // signal the accepted socket
252 fibril_condvar_signal(&socket->accept_signal);
253 }
254 fibril_mutex_unlock(&socket->accept_lock);
255 break;
256
257 default:
258 rc = ENOTSUP;
259 }
260
261 if ((SOCKET_GET_DATA_FRAGMENT_SIZE(call) > 0) &&
262 (SOCKET_GET_DATA_FRAGMENT_SIZE(call) !=
263 socket->data_fragment_size)) {
264 fibril_rwlock_write_lock(&socket->sending_lock);
265
266 // set the data fragment size
267 socket->data_fragment_size =
268 SOCKET_GET_DATA_FRAGMENT_SIZE(call);
269
270 fibril_rwlock_write_unlock(&socket->sending_lock);
271 }
272
273 fibril_rwlock_read_unlock(&socket_globals.lock);
274 break;
275
276 default:
277 rc = ENOTSUP;
278 }
279
280 ipc_answer_0(callid, (sysarg_t) rc);
281 goto loop;
282}
283
284/** Returns the TCP module phone.
285 *
286 * Connects to the TCP module if necessary.
287 *
288 * @return The TCP module phone.
289 * @return Other error codes as defined for the
290 * bind_service_timeout() function.
291 */
292static int socket_get_tcp_phone(void)
293{
294 if (socket_globals.tcp_phone < 0) {
295 socket_globals.tcp_phone = bind_service_timeout(SERVICE_TCP,
296 0, 0, SERVICE_TCP, socket_connection,
297 SOCKET_CONNECT_TIMEOUT);
298 }
299
300 return socket_globals.tcp_phone;
301}
302
303/** Returns the UDP module phone.
304 *
305 * Connects to the UDP module if necessary.
306 *
307 * @return The UDP module phone.
308 * @return Other error codes as defined for the
309 * bind_service_timeout() function.
310 */
311static int socket_get_udp_phone(void)
312{
313 if (socket_globals.udp_phone < 0) {
314 socket_globals.udp_phone = bind_service_timeout(SERVICE_UDP,
315 0, 0, SERVICE_UDP, socket_connection,
316 SOCKET_CONNECT_TIMEOUT);
317 }
318
319 return socket_globals.udp_phone;
320}
321
322/** Tries to find a new free socket identifier.
323 *
324 * @return The new socket identifier.
325 * @return ELIMIT if there is no socket identifier available.
326 */
327static int socket_generate_new_id(void)
328{
329 sockets_t *sockets;
330 int socket_id = 0;
331 int count;
332
333 sockets = socket_get_sockets();
334 count = 0;
335// socket_id = socket_globals.last_id;
336
337 do {
338 if (count < SOCKET_ID_TRIES) {
339 socket_id = rand() % INT_MAX;
340 ++count;
341 } else if (count == SOCKET_ID_TRIES) {
342 socket_id = 1;
343 ++count;
344 // only this branch for last_id
345 } else {
346 if (socket_id < INT_MAX) {
347 ++socket_id;
348/* } else if(socket_globals.last_id) {
349 * socket_globals.last_id = 0;
350 * socket_id = 1;
351 */ } else {
352 return ELIMIT;
353 }
354 }
355 } while (sockets_find(sockets, socket_id));
356
357// last_id = socket_id
358 return socket_id;
359}
360
361/** Initializes a new socket specific data.
362 *
363 * @param[in,out] socket The socket to be initialized.
364 * @param[in] socket_id The new socket identifier.
365 * @param[in] phone The parent module phone.
366 * @param[in] service The parent module service.
367 */
368static void
369socket_initialize(socket_t *socket, int socket_id, int phone,
370 services_t service)
371{
372 socket->socket_id = socket_id;
373 socket->phone = phone;
374 socket->service = service;
375 dyn_fifo_initialize(&socket->received, SOCKET_INITIAL_RECEIVED_SIZE);
376 dyn_fifo_initialize(&socket->accepted, SOCKET_INITIAL_ACCEPTED_SIZE);
377 fibril_mutex_initialize(&socket->receive_lock);
378 fibril_condvar_initialize(&socket->receive_signal);
379 fibril_mutex_initialize(&socket->accept_lock);
380 fibril_condvar_initialize(&socket->accept_signal);
381 fibril_rwlock_initialize(&socket->sending_lock);
382}
383
384/** Creates a new socket.
385 *
386 * @param[in] domain The socket protocol family.
387 * @param[in] type Socket type.
388 * @param[in] protocol Socket protocol.
389 * @return The socket identifier on success.
390 * @return EPFNOTSUPPORT if the protocol family is not supported.
391 * @return ESOCKNOTSUPPORT if the socket type is not supported.
392 * @return EPROTONOSUPPORT if the protocol is not supported.
393 * @return ENOMEM if there is not enough memory left.
394 * @return ELIMIT if there was not a free socket identifier found
395 * this time.
396 * @return Other error codes as defined for the NET_SOCKET message.
397 * @return Other error codes as defined for the
398 * bind_service_timeout() function.
399 */
400int socket(int domain, int type, int protocol)
401{
402 socket_t *socket;
403 int phone;
404 int socket_id;
405 services_t service;
406 sysarg_t fragment_size;
407 sysarg_t header_size;
408 int rc;
409
410 // find the appropriate service
411 switch (domain) {
412 case PF_INET:
413 switch (type) {
414 case SOCK_STREAM:
415 if (!protocol)
416 protocol = IPPROTO_TCP;
417
418 switch (protocol) {
419 case IPPROTO_TCP:
420 phone = socket_get_tcp_phone();
421 service = SERVICE_TCP;
422 break;
423 default:
424 return EPROTONOSUPPORT;
425 }
426
427 break;
428
429 case SOCK_DGRAM:
430 if (!protocol)
431 protocol = IPPROTO_UDP;
432
433 switch (protocol) {
434 case IPPROTO_UDP:
435 phone = socket_get_udp_phone();
436 service = SERVICE_UDP;
437 break;
438 default:
439 return EPROTONOSUPPORT;
440 }
441
442 break;
443
444 case SOCK_RAW:
445 default:
446 return ESOCKTNOSUPPORT;
447 }
448
449 break;
450
451 case PF_INET6:
452 default:
453 return EPFNOSUPPORT;
454 }
455
456 if (phone < 0)
457 return phone;
458
459 // create a new socket structure
460 socket = (socket_t *) malloc(sizeof(socket_t));
461 if (!socket)
462 return ENOMEM;
463
464 bzero(socket, sizeof(*socket));
465 fibril_rwlock_write_lock(&socket_globals.lock);
466
467 // request a new socket
468 socket_id = socket_generate_new_id();
469 if (socket_id <= 0) {
470 fibril_rwlock_write_unlock(&socket_globals.lock);
471 free(socket);
472 return socket_id;
473 }
474
475 rc = (int) async_req_3_3(phone, NET_SOCKET, socket_id, 0, service, NULL,
476 &fragment_size, &header_size);
477 if (rc != EOK) {
478 fibril_rwlock_write_unlock(&socket_globals.lock);
479 free(socket);
480 return rc;
481 }
482
483 socket->data_fragment_size = (size_t) fragment_size;
484 socket->header_size = (size_t) header_size;
485
486 // finish the new socket initialization
487 socket_initialize(socket, socket_id, phone, service);
488 // store the new socket
489 rc = sockets_add(socket_get_sockets(), socket_id, socket);
490
491 fibril_rwlock_write_unlock(&socket_globals.lock);
492 if (rc < 0) {
493 dyn_fifo_destroy(&socket->received);
494 dyn_fifo_destroy(&socket->accepted);
495 free(socket);
496 async_msg_3(phone, NET_SOCKET_CLOSE, (sysarg_t) socket_id, 0,
497 service);
498 return rc;
499 }
500
501 return socket_id;
502}
503
504/** Sends message to the socket parent module with specified data.
505 *
506 * @param[in] socket_id Socket identifier.
507 * @param[in] message The action message.
508 * @param[in] arg2 The second message parameter.
509 * @param[in] data The data to be sent.
510 * @param[in] datalength The data length.
511 * @return EOK on success.
512 * @return ENOTSOCK if the socket is not found.
513 * @return EBADMEM if the data parameter is NULL.
514 * @return NO_DATA if the datalength parameter is zero (0).
515 * @return Other error codes as defined for the spcific message.
516 */
517static int
518socket_send_data(int socket_id, sysarg_t message, sysarg_t arg2,
519 const void *data, size_t datalength)
520{
521 socket_t *socket;
522 aid_t message_id;
523 sysarg_t result;
524
525 if (!data)
526 return EBADMEM;
527
528 if (!datalength)
529 return NO_DATA;
530
531 fibril_rwlock_read_lock(&socket_globals.lock);
532
533 // find the socket
534 socket = sockets_find(socket_get_sockets(), socket_id);
535 if (!socket) {
536 fibril_rwlock_read_unlock(&socket_globals.lock);
537 return ENOTSOCK;
538 }
539
540 // request the message
541 message_id = async_send_3(socket->phone, message,
542 (sysarg_t) socket->socket_id, arg2, socket->service, NULL);
543 // send the address
544 async_data_write_start(socket->phone, data, datalength);
545
546 fibril_rwlock_read_unlock(&socket_globals.lock);
547 async_wait_for(message_id, &result);
548 return (int) result;
549}
550
551/** Binds the socket to a port address.
552 *
553 * @param[in] socket_id Socket identifier.
554 * @param[in] my_addr The port address.
555 * @param[in] addrlen The address length.
556 * @return EOK on success.
557 * @return ENOTSOCK if the socket is not found.
558 * @return EBADMEM if the my_addr parameter is NULL.
559 * @return NO_DATA if the addlen parameter is zero.
560 * @return Other error codes as defined for the NET_SOCKET_BIND
561 * message.
562 */
563int bind(int socket_id, const struct sockaddr * my_addr, socklen_t addrlen)
564{
565 if (addrlen <= 0)
566 return EINVAL;
567
568 // send the address
569 return socket_send_data(socket_id, NET_SOCKET_BIND, 0, my_addr,
570 (size_t) addrlen);
571}
572
573/** Sets the number of connections waiting to be accepted.
574 *
575 * @param[in] socket_id Socket identifier.
576 * @param[in] backlog The maximum number of waiting sockets to be accepted.
577 * @return EOK on success.
578 * @return EINVAL if the backlog parameter is not positive (<=0).
579 * @return ENOTSOCK if the socket is not found.
580 * @return Other error codes as defined for the NET_SOCKET_LISTEN
581 * message.
582 */
583int listen(int socket_id, int backlog)
584{
585 socket_t *socket;
586 int result;
587
588 if (backlog <= 0)
589 return EINVAL;
590
591 fibril_rwlock_read_lock(&socket_globals.lock);
592
593 // find the socket
594 socket = sockets_find(socket_get_sockets(), socket_id);
595 if (!socket) {
596 fibril_rwlock_read_unlock(&socket_globals.lock);
597 return ENOTSOCK;
598 }
599
600 // request listen backlog change
601 result = (int) async_req_3_0(socket->phone, NET_SOCKET_LISTEN,
602 (sysarg_t) socket->socket_id, (sysarg_t) backlog, socket->service);
603
604 fibril_rwlock_read_unlock(&socket_globals.lock);
605 return result;
606}
607
608/** Accepts waiting socket.
609 *
610 * Blocks until such a socket exists.
611 *
612 * @param[in] socket_id Socket identifier.
613 * @param[out] cliaddr The remote client address.
614 * @param[in] addrlen The address length.
615 * @return EOK on success.
616 * @return EBADMEM if the cliaddr or addrlen parameter is NULL.
617 * @return EINVAL if the backlog parameter is not positive (<=0).
618 * @return ENOTSOCK if the socket is not found.
619 * @return Other error codes as defined for the NET_SOCKET_ACCEPT
620 * message.
621 */
622int accept(int socket_id, struct sockaddr * cliaddr, socklen_t * addrlen)
623{
624 socket_t *socket;
625 socket_t *new_socket;
626 aid_t message_id;
627 sysarg_t ipc_result;
628 int result;
629 ipc_call_t answer;
630
631 if (!cliaddr || !addrlen)
632 return EBADMEM;
633
634 fibril_rwlock_write_lock(&socket_globals.lock);
635
636 // find the socket
637 socket = sockets_find(socket_get_sockets(), socket_id);
638 if (!socket) {
639 fibril_rwlock_write_unlock(&socket_globals.lock);
640 return ENOTSOCK;
641 }
642
643 fibril_mutex_lock(&socket->accept_lock);
644
645 // wait for an accepted socket
646 ++ socket->blocked;
647 while (dyn_fifo_value(&socket->accepted) <= 0) {
648 fibril_rwlock_write_unlock(&socket_globals.lock);
649 fibril_condvar_wait(&socket->accept_signal, &socket->accept_lock);
650 // drop the accept lock to avoid deadlock
651 fibril_mutex_unlock(&socket->accept_lock);
652 fibril_rwlock_write_lock(&socket_globals.lock);
653 fibril_mutex_lock(&socket->accept_lock);
654 }
655 -- socket->blocked;
656
657 // create a new scoket
658 new_socket = (socket_t *) malloc(sizeof(socket_t));
659 if (!new_socket) {
660 fibril_mutex_unlock(&socket->accept_lock);
661 fibril_rwlock_write_unlock(&socket_globals.lock);
662 return ENOMEM;
663 }
664 bzero(new_socket, sizeof(*new_socket));
665 socket_id = socket_generate_new_id();
666 if (socket_id <= 0) {
667 fibril_mutex_unlock(&socket->accept_lock);
668 fibril_rwlock_write_unlock(&socket_globals.lock);
669 free(new_socket);
670 return socket_id;
671 }
672 socket_initialize(new_socket, socket_id, socket->phone,
673 socket->service);
674 result = sockets_add(socket_get_sockets(), new_socket->socket_id,
675 new_socket);
676 if (result < 0) {
677 fibril_mutex_unlock(&socket->accept_lock);
678 fibril_rwlock_write_unlock(&socket_globals.lock);
679 free(new_socket);
680 return result;
681 }
682
683 // request accept
684 message_id = async_send_5(socket->phone, NET_SOCKET_ACCEPT,
685 (sysarg_t) socket->socket_id, 0, socket->service, 0,
686 new_socket->socket_id, &answer);
687
688 // read address
689 ipc_data_read_start(socket->phone, cliaddr, *addrlen);
690 fibril_rwlock_write_unlock(&socket_globals.lock);
691 async_wait_for(message_id, &ipc_result);
692 result = (int) ipc_result;
693 if (result > 0) {
694 if (result != socket_id)
695 result = EINVAL;
696
697 // dequeue the accepted socket if successful
698 dyn_fifo_pop(&socket->accepted);
699 // set address length
700 *addrlen = SOCKET_GET_ADDRESS_LENGTH(answer);
701 new_socket->data_fragment_size =
702 SOCKET_GET_DATA_FRAGMENT_SIZE(answer);
703 } else if (result == ENOTSOCK) {
704 // empty the queue if no accepted sockets
705 while (dyn_fifo_pop(&socket->accepted) > 0)
706 ;
707 }
708
709 fibril_mutex_unlock(&socket->accept_lock);
710 return result;
711}
712
713/** Connects socket to the remote server.
714 *
715 * @param[in] socket_id Socket identifier.
716 * @param[in] serv_addr The remote server address.
717 * @param[in] addrlen The address length.
718 * @return EOK on success.
719 * @return EBADMEM if the serv_addr parameter is NULL.
720 * @return NO_DATA if the addlen parameter is zero.
721 * @return ENOTSOCK if the socket is not found.
722 * @return Other error codes as defined for the NET_SOCKET_CONNECT
723 * message.
724 */
725int connect(int socket_id, const struct sockaddr *serv_addr, socklen_t addrlen)
726{
727 if (!serv_addr)
728 return EDESTADDRREQ;
729
730 if (!addrlen)
731 return EDESTADDRREQ;
732
733 // send the address
734 return socket_send_data(socket_id, NET_SOCKET_CONNECT, 0, serv_addr,
735 addrlen);
736}
737
738/** Clears and destroys the socket.
739 *
740 * @param[in] socket The socket to be destroyed.
741 */
742static void socket_destroy(socket_t *socket)
743{
744 int accepted_id;
745
746 // destroy all accepted sockets
747 while ((accepted_id = dyn_fifo_pop(&socket->accepted)) >= 0)
748 socket_destroy(sockets_find(socket_get_sockets(), accepted_id));
749
750 dyn_fifo_destroy(&socket->received);
751 dyn_fifo_destroy(&socket->accepted);
752 sockets_exclude(socket_get_sockets(), socket->socket_id);
753}
754
755/** Closes the socket.
756 *
757 * @param[in] socket_id Socket identifier.
758 * @return EOK on success.
759 * @return ENOTSOCK if the socket is not found.
760 * @return EINPROGRESS if there is another blocking function in
761 * progress.
762 * @return Other error codes as defined for the NET_SOCKET_CLOSE
763 * message.
764 */
765int closesocket(int socket_id)
766{
767 socket_t *socket;
768 int rc;
769
770 fibril_rwlock_write_lock(&socket_globals.lock);
771
772 socket = sockets_find(socket_get_sockets(), socket_id);
773 if (!socket) {
774 fibril_rwlock_write_unlock(&socket_globals.lock);
775 return ENOTSOCK;
776 }
777 if (socket->blocked) {
778 fibril_rwlock_write_unlock(&socket_globals.lock);
779 return EINPROGRESS;
780 }
781
782 // request close
783 rc = (int) async_req_3_0(socket->phone, NET_SOCKET_CLOSE,
784 (sysarg_t) socket->socket_id, 0, socket->service);
785 if (rc != EOK) {
786 fibril_rwlock_write_unlock(&socket_globals.lock);
787 return rc;
788 }
789 // free the socket structure
790 socket_destroy(socket);
791
792 fibril_rwlock_write_unlock(&socket_globals.lock);
793 return EOK;
794}
795
796/** Sends data via the socket to the remote address.
797 *
798 * Binds the socket to a free port if not already connected/bound.
799 *
800 * @param[in] message The action message.
801 * @param[in] socket_id Socket identifier.
802 * @param[in] data The data to be sent.
803 * @param[in] datalength The data length.
804 * @param[in] flags Various send flags.
805 * @param[in] toaddr The destination address. May be NULL for connected
806 * sockets.
807 * @param[in] addrlen The address length. Used only if toaddr is not NULL.
808 * @return EOK on success.
809 * @return ENOTSOCK if the socket is not found.
810 * @return EBADMEM if the data or toaddr parameter is NULL.
811 * @return NO_DATA if the datalength or the addrlen parameter is
812 * zero (0).
813 * @return Other error codes as defined for the NET_SOCKET_SENDTO
814 * message.
815 */
816static int
817sendto_core(sysarg_t message, int socket_id, const void *data,
818 size_t datalength, int flags, const struct sockaddr *toaddr,
819 socklen_t addrlen)
820{
821 socket_t *socket;
822 aid_t message_id;
823 sysarg_t result;
824 size_t fragments;
825 ipc_call_t answer;
826
827 if (!data)
828 return EBADMEM;
829
830 if (!datalength)
831 return NO_DATA;
832
833 fibril_rwlock_read_lock(&socket_globals.lock);
834
835 // find socket
836 socket = sockets_find(socket_get_sockets(), socket_id);
837 if (!socket) {
838 fibril_rwlock_read_unlock(&socket_globals.lock);
839 return ENOTSOCK;
840 }
841
842 fibril_rwlock_read_lock(&socket->sending_lock);
843
844 // compute data fragment count
845 if (socket->data_fragment_size > 0) {
846 fragments = (datalength + socket->header_size) /
847 socket->data_fragment_size;
848 if ((datalength + socket->header_size) %
849 socket->data_fragment_size)
850 ++fragments;
851 } else {
852 fragments = 1;
853 }
854
855 // request send
856 message_id = async_send_5(socket->phone, message,
857 (sysarg_t) socket->socket_id,
858 (fragments == 1 ? datalength : socket->data_fragment_size),
859 socket->service, (sysarg_t) flags, fragments, &answer);
860
861 // send the address if given
862 if (!toaddr ||
863 (async_data_write_start(socket->phone, toaddr, addrlen) == EOK)) {
864 if (fragments == 1) {
865 // send all if only one fragment
866 async_data_write_start(socket->phone, data, datalength);
867 } else {
868 // send the first fragment
869 async_data_write_start(socket->phone, data,
870 socket->data_fragment_size - socket->header_size);
871 data = ((const uint8_t *) data) +
872 socket->data_fragment_size - socket->header_size;
873
874 // send the middle fragments
875 while (--fragments > 1) {
876 async_data_write_start(socket->phone, data,
877 socket->data_fragment_size);
878 data = ((const uint8_t *) data) +
879 socket->data_fragment_size;
880 }
881
882 // send the last fragment
883 async_data_write_start(socket->phone, data,
884 (datalength + socket->header_size) %
885 socket->data_fragment_size);
886 }
887 }
888
889 async_wait_for(message_id, &result);
890
891 if ((SOCKET_GET_DATA_FRAGMENT_SIZE(answer) > 0) &&
892 (SOCKET_GET_DATA_FRAGMENT_SIZE(answer) !=
893 socket->data_fragment_size)) {
894 // set the data fragment size
895 socket->data_fragment_size =
896 SOCKET_GET_DATA_FRAGMENT_SIZE(answer);
897 }
898
899 fibril_rwlock_read_unlock(&socket->sending_lock);
900 fibril_rwlock_read_unlock(&socket_globals.lock);
901 return (int) result;
902}
903
904/** Sends data via the socket.
905 *
906 * @param[in] socket_id Socket identifier.
907 * @param[in] data The data to be sent.
908 * @param[in] datalength The data length.
909 * @param[in] flags Various send flags.
910 * @return EOK on success.
911 * @return ENOTSOCK if the socket is not found.
912 * @return EBADMEM if the data parameter is NULL.
913 * @return NO_DATA if the datalength parameter is zero.
914 * @return Other error codes as defined for the NET_SOCKET_SEND
915 * message.
916 */
917int send(int socket_id, void *data, size_t datalength, int flags)
918{
919 // without the address
920 return sendto_core(NET_SOCKET_SEND, socket_id, data, datalength, flags,
921 NULL, 0);
922}
923
924/** Sends data via the socket to the remote address.
925 *
926 * Binds the socket to a free port if not already connected/bound.
927 *
928 * @param[in] socket_id Socket identifier.
929 * @param[in] data The data to be sent.
930 * @param[in] datalength The data length.
931 * @param[in] flags Various send flags.
932 * @param[in] toaddr The destination address.
933 * @param[in] addrlen The address length.
934 * @return EOK on success.
935 * @return ENOTSOCK if the socket is not found.
936 * @return EBADMEM if the data or toaddr parameter is NULL.
937 * @return NO_DATA if the datalength or the addrlen parameter is
938 * zero.
939 * @return Other error codes as defined for the NET_SOCKET_SENDTO
940 * message.
941 */
942int
943sendto(int socket_id, const void *data, size_t datalength, int flags,
944 const struct sockaddr *toaddr, socklen_t addrlen)
945{
946 if (!toaddr)
947 return EDESTADDRREQ;
948
949 if (!addrlen)
950 return EDESTADDRREQ;
951
952 // with the address
953 return sendto_core(NET_SOCKET_SENDTO, socket_id, data, datalength,
954 flags, toaddr, addrlen);
955}
956
957/** Receives data via the socket.
958 *
959 * @param[in] message The action message.
960 * @param[in] socket_id Socket identifier.
961 * @param[out] data The data buffer to be filled.
962 * @param[in] datalength The data length.
963 * @param[in] flags Various receive flags.
964 * @param[out] fromaddr The source address. May be NULL for connected sockets.
965 * @param[in,out] addrlen The address length. The maximum address length is
966 * read. The actual address length is set. Used only if
967 * fromaddr is not NULL.
968 * @return EOK on success.
969 * @return ENOTSOCK if the socket is not found.
970 * @return EBADMEM if the data parameter is NULL.
971 * @return NO_DATA if the datalength or addrlen parameter is zero.
972 * @return Other error codes as defined for the spcific message.
973 */
974static int
975recvfrom_core(sysarg_t message, int socket_id, void *data, size_t datalength,
976 int flags, struct sockaddr *fromaddr, socklen_t *addrlen)
977{
978 socket_t *socket;
979 aid_t message_id;
980 sysarg_t ipc_result;
981 int result;
982 size_t fragments;
983 size_t *lengths;
984 size_t index;
985 ipc_call_t answer;
986
987 if (!data)
988 return EBADMEM;
989
990 if (!datalength)
991 return NO_DATA;
992
993 if (fromaddr && !addrlen)
994 return EINVAL;
995
996 fibril_rwlock_read_lock(&socket_globals.lock);
997
998 // find the socket
999 socket = sockets_find(socket_get_sockets(), socket_id);
1000 if (!socket) {
1001 fibril_rwlock_read_unlock(&socket_globals.lock);
1002 return ENOTSOCK;
1003 }
1004
1005 fibril_mutex_lock(&socket->receive_lock);
1006 // wait for a received packet
1007 ++socket->blocked;
1008 while ((result = dyn_fifo_value(&socket->received)) <= 0) {
1009 fibril_rwlock_read_unlock(&socket_globals.lock);
1010 fibril_condvar_wait(&socket->receive_signal,
1011 &socket->receive_lock);
1012
1013 // drop the receive lock to avoid deadlock
1014 fibril_mutex_unlock(&socket->receive_lock);
1015 fibril_rwlock_read_lock(&socket_globals.lock);
1016 fibril_mutex_lock(&socket->receive_lock);
1017 }
1018 --socket->blocked;
1019 fragments = (size_t) result;
1020
1021 // prepare lengths if more fragments
1022 if (fragments > 1) {
1023 lengths = (size_t *) malloc(sizeof(size_t) * fragments +
1024 sizeof(size_t));
1025 if (!lengths) {
1026 fibril_mutex_unlock(&socket->receive_lock);
1027 fibril_rwlock_read_unlock(&socket_globals.lock);
1028 return ENOMEM;
1029 }
1030
1031 // request packet data
1032 message_id = async_send_4(socket->phone, message,
1033 (sysarg_t) socket->socket_id, 0, socket->service,
1034 (sysarg_t) flags, &answer);
1035
1036 // read the address if desired
1037 if(!fromaddr ||
1038 (async_data_read_start(socket->phone, fromaddr,
1039 *addrlen) == EOK)) {
1040 // read the fragment lengths
1041 if (async_data_read_start(socket->phone, lengths,
1042 sizeof(int) * (fragments + 1)) == EOK) {
1043 if (lengths[fragments] <= datalength) {
1044
1045 // read all fragments if long enough
1046 for (index = 0; index < fragments;
1047 ++index) {
1048 async_data_read_start(
1049 socket->phone, data,
1050 lengths[index]);
1051 data = ((uint8_t *) data) +
1052 lengths[index];
1053 }
1054 }
1055 }
1056 }
1057
1058 free(lengths);
1059 } else {
1060 // request packet data
1061 message_id = async_send_4(socket->phone, message,
1062 (sysarg_t) socket->socket_id, 0, socket->service,
1063 (sysarg_t) flags, &answer);
1064
1065 // read the address if desired
1066 if (!fromaddr ||
1067 (async_data_read_start(socket->phone, fromaddr,
1068 *addrlen) == EOK)) {
1069 // read all if only one fragment
1070 async_data_read_start(socket->phone, data, datalength);
1071 }
1072 }
1073
1074 async_wait_for(message_id, &ipc_result);
1075 result = (int) ipc_result;
1076 if (result == EOK) {
1077 // dequeue the received packet
1078 dyn_fifo_pop(&socket->received);
1079 // return read data length
1080 result = SOCKET_GET_READ_DATA_LENGTH(answer);
1081 // set address length
1082 if (fromaddr && addrlen)
1083 *addrlen = SOCKET_GET_ADDRESS_LENGTH(answer);
1084 }
1085
1086 fibril_mutex_unlock(&socket->receive_lock);
1087 fibril_rwlock_read_unlock(&socket_globals.lock);
1088 return result;
1089}
1090
1091/** Receives data via the socket.
1092 *
1093 * @param[in] socket_id Socket identifier.
1094 * @param[out] data The data buffer to be filled.
1095 * @param[in] datalength The data length.
1096 * @param[in] flags Various receive flags.
1097 * @return EOK on success.
1098 * @return ENOTSOCK if the socket is not found.
1099 * @return EBADMEM if the data parameter is NULL.
1100 * @return NO_DATA if the datalength parameter is zero.
1101 * @return Other error codes as defined for the NET_SOCKET_RECV
1102 * message.
1103 */
1104int recv(int socket_id, void *data, size_t datalength, int flags)
1105{
1106 // without the address
1107 return recvfrom_core(NET_SOCKET_RECV, socket_id, data, datalength,
1108 flags, NULL, NULL);
1109}
1110
1111/** Receives data via the socket.
1112 *
1113 * @param[in] socket_id Socket identifier.
1114 * @param[out] data The data buffer to be filled.
1115 * @param[in] datalength The data length.
1116 * @param[in] flags Various receive flags.
1117 * @param[out] fromaddr The source address.
1118 * @param[in,out] addrlen The address length. The maximum address length is
1119 * read. The actual address length is set.
1120 * @return EOK on success.
1121 * @return ENOTSOCK if the socket is not found.
1122 * @return EBADMEM if the data or fromaddr parameter is NULL.
1123 * @return NO_DATA if the datalength or addrlen parameter is zero.
1124 * @return Other error codes as defined for the NET_SOCKET_RECVFROM
1125 * message.
1126 */
1127int
1128recvfrom(int socket_id, void *data, size_t datalength, int flags,
1129 struct sockaddr *fromaddr, socklen_t *addrlen)
1130{
1131 if (!fromaddr)
1132 return EBADMEM;
1133
1134 if (!addrlen)
1135 return NO_DATA;
1136
1137 // with the address
1138 return recvfrom_core(NET_SOCKET_RECVFROM, socket_id, data, datalength,
1139 flags, fromaddr, addrlen);
1140}
1141
1142/** Gets socket option.
1143 *
1144 * @param[in] socket_id Socket identifier.
1145 * @param[in] level The socket options level.
1146 * @param[in] optname The socket option to be get.
1147 * @param[out] value The value buffer to be filled.
1148 * @param[in,out] optlen The value buffer length. The maximum length is read.
1149 * The actual length is set.
1150 * @return EOK on success.
1151 * @return ENOTSOCK if the socket is not found.
1152 * @return EBADMEM if the value or optlen parameter is NULL.
1153 * @return NO_DATA if the optlen parameter is zero.
1154 * @return Other error codes as defined for the
1155 * NET_SOCKET_GETSOCKOPT message.
1156 */
1157int
1158getsockopt(int socket_id, int level, int optname, void *value, size_t *optlen)
1159{
1160 socket_t *socket;
1161 aid_t message_id;
1162 sysarg_t result;
1163
1164 if (!value || !optlen)
1165 return EBADMEM;
1166
1167 if (!*optlen)
1168 return NO_DATA;
1169
1170 fibril_rwlock_read_lock(&socket_globals.lock);
1171
1172 // find the socket
1173 socket = sockets_find(socket_get_sockets(), socket_id);
1174 if (!socket) {
1175 fibril_rwlock_read_unlock(&socket_globals.lock);
1176 return ENOTSOCK;
1177 }
1178
1179 // request option value
1180 message_id = async_send_3(socket->phone, NET_SOCKET_GETSOCKOPT,
1181 (sysarg_t) socket->socket_id, (sysarg_t) optname, socket->service,
1182 NULL);
1183
1184 // read the length
1185 if (async_data_read_start(socket->phone, optlen,
1186 sizeof(*optlen)) == EOK) {
1187 // read the value
1188 async_data_read_start(socket->phone, value, *optlen);
1189 }
1190
1191 fibril_rwlock_read_unlock(&socket_globals.lock);
1192 async_wait_for(message_id, &result);
1193 return (int) result;
1194}
1195
1196/** Sets socket option.
1197 *
1198 * @param[in] socket_id Socket identifier.
1199 * @param[in] level The socket options level.
1200 * @param[in] optname The socket option to be set.
1201 * @param[in] value The value to be set.
1202 * @param[in] optlen The value length.
1203 * @return EOK on success.
1204 * @return ENOTSOCK if the socket is not found.
1205 * @return EBADMEM if the value parameter is NULL.
1206 * @return NO_DATA if the optlen parameter is zero.
1207 * @return Other error codes as defined for the
1208 * NET_SOCKET_SETSOCKOPT message.
1209 */
1210int
1211setsockopt(int socket_id, int level, int optname, const void *value,
1212 size_t optlen)
1213{
1214 // send the value
1215 return socket_send_data(socket_id, NET_SOCKET_SETSOCKOPT,
1216 (sysarg_t) optname, value, optlen);
1217}
1218
1219/** @}
1220 */
Note: See TracBrowser for help on using the repository browser.