source: mainline/uspace/srv/net/socket/socket_client.c@ b648ae4

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b648ae4 was 21580dd, checked in by Lukas Mejdrech <lukas@…>, 15 years ago

Merged with network branch svn://svn.helenos.org/HelenOS/branches/network revision 4759; ipc_share_* and ipc_data_* changed to async_*; client connection in module.c returns on IPC_M_PHONE_HUNGUP; * Qemu scripts renamed to net-qe.*; (the dp8390 does not respond)

  • Property mode set to 100644
File size: 25.5 KB
RevLine 
[21580dd]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 socket
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
43#include <ipc/services.h>
44
45#include "../err.h"
46#include "../modules.h"
47
48#include "../include/in.h"
49#include "../include/socket.h"
50#include "../include/socket_errno.h"
51
52#include "../structures/dynamic_fifo.h"
53#include "../structures/int_map.h"
54
55#include "socket_messages.h"
56
57/** Initial received packet queue size.
58 */
59#define SOCKET_INITIAL_RECEIVED_SIZE 4
60
61/** Maximum received packet queue size.
62 */
63#define SOCKET_MAX_RECEIVED_SIZE 0
64
65/** Initial waiting sockets queue size.
66 */
67#define SOCKET_INITIAL_ACCEPTED_SIZE 1
68
69/** Maximum waiting sockets queue size.
70 */
71#define SOCKET_MAX_ACCEPTED_SIZE 0
72
73/** Type definition of the socket specific data.
74 * @see socket
75 */
76typedef struct socket socket_t;
77
78/** Type definition of the socket specific data pointer.
79 * @see socket
80 */
81typedef socket_t * socket_ref;
82
83/** Socket specific data.
84 * Each socket lock locks only its structure part and any number of them may be locked simultaneously.
85 */
86struct socket{
87 /** Socket identifier.
88 */
89 int socket_id;
90 /** Parent module phone.
91 */
92 int phone;
93 /** Parent module service.
94 */
95 services_t service;
96 /** Underlying protocol header size.
97 * Sending and receiving optimalization.
98 */
99 size_t header_size;
100 /** Packet data fragment size.
101 * Sending optimalization.
102 */
103 size_t data_fragment_size;
104 /** Sending safety lock.
105 * Locks the header_size and data_fragment_size attributes.
106 */
107 fibril_rwlock_t sending_lock;
108 /** Received packets queue.
109 */
110 dyn_fifo_t received;
111 /** Received packets safety lock.
112 * Used for receiving and receive notifications.
113 * Locks the received attribute.
114 */
115 fibril_mutex_t receive_lock;
116 /** Received packets signaling.
117 * Signaled upon receive notification.
118 */
119 fibril_condvar_t receive_signal;
120 /** Waiting sockets queue.
121 */
122 dyn_fifo_t accepted;
123 /** Waiting sockets safety lock.
124 * Used for accepting and accept notifications.
125 * Locks the accepted attribute.
126 */
127 fibril_mutex_t accept_lock;
128 /** Waiting sockets signaling.
129 * Signaled upon accept notification.
130 */
131 fibril_condvar_t accept_signal;
132 /** The number of blocked functions called.
133 * Used while waiting for the received packets or accepted sockets.
134 */
135 int blocked;
136};
137
138/** Sockets map.
139 * Maps socket identifiers to the socket specific data.
140 * @see int_map.h
141 */
142INT_MAP_DECLARE( sockets, socket_t );
143
144/** Socket client library global data.
145 */
146static struct socket_client_globals {
147 /** TCP module phone.
148 */
149 int tcp_phone;
150 /** UDP module phone.
151 */
152 int udp_phone;
153 /** Active sockets.
154 */
155 sockets_ref sockets;
156 /** Safety lock.
157 * Write lock is used only for adding or removing sockets.
158 * When locked for writing, no other socket locks need to be locked.
159 * When locked for reading, any other socket locks may be locked.
160 * No socket lock may be locked if this lock is unlocked.
161 */
162 fibril_rwlock_t lock;
163} socket_globals = {
164 .tcp_phone = -1,
165 .udp_phone = -1,
166 .sockets = NULL,
167 .lock = {
168 .readers = 0,
169 .writers = 0,
170 .waiters = {
171 .prev = & socket_globals.lock.waiters,
172 .next = & socket_globals.lock.waiters
173 }
174 }
175};
176
177INT_MAP_IMPLEMENT( sockets, socket_t );
178
179/** Returns the TCP module phone.
180 * Connects to the TCP module if necessary.
181 * @returns The TCP module phone.
182 */
183static int socket_get_tcp_phone( void );
184
185/** Returns the UDP module phone.
186 * Connects to the UDP module if necessary.
187 * @returns The UDP module phone.
188 */
189static int socket_get_udp_phone( void );
190
191/** Returns the active sockets.
192 * @returns The active sockets.
193 */
194static sockets_ref socket_get_sockets( void );
195
196/** Default thread for new connections.
197 * @param[in] iid The initial message identifier.
198 * @param[in] icall The initial message call structure.
199 */
200void socket_connection( ipc_callid_t iid, ipc_call_t * icall );
201
202/** Sends message to the socket parent module with specified data.
203 * @param[in] socket_id Socket identifier.
204 * @param[in] message The action message.
205 * @param[in] arg2 The second message parameter.
206 * @param[in] data The data to be sent.
207 * @param[in] datalength The data length.
208 * @returns EOK on success.
209 * @returns ENOTSOCK if the socket is not found.
210 * @returns EBADMEM if the data parameter is NULL.
211 * @returns NO_DATA if the datalength parameter is zero (0).
212 * @returns Other error codes as defined for the spcific message.
213 */
214int socket_send_data( int socket_id, ipcarg_t message, ipcarg_t arg2, const void * data, size_t datalength );
215
216/** Initializes a new socket specific data.
217 * @param[in,out] socket The socket to be initialized.
218 * @param[in] socket_id The new socket identifier.
219 * @param[in] phone The parent module phone.
220 * @param[in] service The parent module service.
221 */
222void socket_initialize( socket_ref socket, int socket_id, int phone, services_t service );
223
224/** Clears and destroys the socket.
225 * @param[in] socket The socket to be destroyed.
226 */
227void socket_destroy( socket_ref socket );
228
229/** Receives data via the socket.
230 * @param[in] message The action message.
231 * @param[in] socket_id Socket identifier.
232 * @param[out] data The data buffer to be filled.
233 * @param[in] datalength The data length.
234 * @param[in] flags Various receive flags.
235 * @param[out] fromaddr The source address. May be NULL for connected sockets.
236 * @param[in,out] addrlen The address length. The maximum address length is read. The actual address length is set. Used only if fromaddr is not NULL.
237 * @returns EOK on success.
238 * @returns ENOTSOCK if the socket is not found.
239 * @returns EBADMEM if the data parameter is NULL.
240 * @returns NO_DATA if the datalength or addrlen parameter is zero (0).
241 * @returns Other error codes as defined for the spcific message.
242 */
243int recvfrom_core( ipcarg_t message, int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen );
244
245/** Sends data via the socket to the remote address.
246 * Binds the socket to a free port if not already connected/bound.
247 * @param[in] message The action message.
248 * @param[in] socket_id Socket identifier.
249 * @param[in] data The data to be sent.
250 * @param[in] datalength The data length.
251 * @param[in] flags Various send flags.
252 * @param[in] toaddr The destination address. May be NULL for connected sockets.
253 * @param[in] addrlen The address length. Used only if toaddr is not NULL.
254 * @returns EOK on success.
255 * @returns ENOTSOCK if the socket is not found.
256 * @returns EBADMEM if the data or toaddr parameter is NULL.
257 * @returns NO_DATA if the datalength or the addrlen parameter is zero (0).
258 * @returns Other error codes as defined for the NET_SOCKET_SENDTO message.
259 */
260int sendto_core( ipcarg_t message, int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen );
261
262static int socket_get_tcp_phone( void ){
263 if( socket_globals.tcp_phone < 0 ){
264 socket_globals.tcp_phone = bind_service( SERVICE_TCP, 0, 0, SERVICE_TCP, socket_connection );
265 }
266 return socket_globals.tcp_phone;
267}
268
269static int socket_get_udp_phone( void ){
270 if( socket_globals.udp_phone < 0 ){
271 socket_globals.udp_phone = bind_service( SERVICE_UDP, 0, 0, SERVICE_UDP, socket_connection );
272 }
273 return socket_globals.udp_phone;
274}
275
276static sockets_ref socket_get_sockets( void ){
277 if( ! socket_globals.sockets ){
278 socket_globals.sockets = ( sockets_ref ) malloc( sizeof( sockets_t ));
279 if( ! socket_globals.sockets ) return NULL;
280 if( sockets_initialize( socket_globals.sockets ) != EOK ){
281 free( socket_globals.sockets );
282 socket_globals.sockets = NULL;
283 }
284 }
285 return socket_globals.sockets;
286}
287
288void socket_initialize( socket_ref socket, int socket_id, int phone, services_t service ){
289 socket->socket_id = socket_id;
290 socket->phone = phone;
291 socket->service = service;
292 dyn_fifo_initialize( & socket->received, SOCKET_INITIAL_RECEIVED_SIZE );
293 dyn_fifo_initialize( & socket->accepted, SOCKET_INITIAL_ACCEPTED_SIZE );
294 fibril_mutex_initialize( & socket->receive_lock );
295 fibril_condvar_initialize( & socket->receive_signal );
296 fibril_mutex_initialize( & socket->accept_lock );
297 fibril_condvar_initialize( & socket->accept_signal );
298 fibril_rwlock_initialize( & socket->sending_lock );
299}
300
301void socket_connection( ipc_callid_t iid, ipc_call_t * icall ){
302 ERROR_DECLARE;
303
304 ipc_callid_t callid;
305 ipc_call_t call;
306 socket_ref socket;
307 socket_ref new_socket;
308
309 while( true ){
310
311 callid = async_get_call( & call );
312 switch( IPC_GET_METHOD( call )){
313 case NET_SOCKET_RECEIVED:
314 fibril_rwlock_read_lock( & socket_globals.lock );
315 // find the socket
316 socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call ));
317 if( ! socket ){
318 ERROR_CODE = ENOTSOCK;
319 }else{
320 fibril_mutex_lock( & socket->receive_lock );
321 // push the number of received packet fragments
322 if( ! ERROR_OCCURRED( dyn_fifo_push( & socket->received, SOCKET_GET_DATA_FRAGMENTS( call ), SOCKET_MAX_RECEIVED_SIZE ))){
323 // signal the received packet
324 fibril_condvar_signal( & socket->receive_signal );
325 }
326 fibril_mutex_unlock( & socket->receive_lock );
327 }
328 fibril_rwlock_read_unlock( & socket_globals.lock );
329 break;
330 case NET_SOCKET_ACCEPTED:
331 fibril_rwlock_read_lock( & socket_globals.lock );
332 // find the socket
333 socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call ));
334 if( ! socket ){
335 ERROR_CODE = ENOTSOCK;
336 }else{
337 // create a new scoket
338 new_socket = ( socket_ref ) malloc( sizeof( socket_t ));
339 if( ! new_socket ){
340 ERROR_CODE = ENOMEM;
341 }else{
342 bzero( new_socket, sizeof( * new_socket ));
343 socket_initialize( new_socket, SOCKET_GET_NEW_SOCKET_ID( call ), socket->phone, socket->service );
344 ERROR_CODE = sockets_add( socket_get_sockets(), new_socket->socket_id, new_socket );
345 if( ERROR_CODE < 0 ){
346 free( new_socket );
347 }else{
348 // push the new socket identifier
349 fibril_mutex_lock( & socket->accept_lock );
350 if( ERROR_OCCURRED( dyn_fifo_push( & socket->accepted, new_socket->socket_id, SOCKET_MAX_ACCEPTED_SIZE ))){
351 sockets_exclude( socket_get_sockets(), new_socket->socket_id );
352 }else{
353 // signal the accepted socket
354 fibril_condvar_signal( & socket->accept_signal );
355 }
356 fibril_mutex_unlock( & socket->accept_lock );
357 ERROR_CODE = EOK;
358 }
359 }
360 }
361 fibril_rwlock_read_unlock( & socket_globals.lock );
362 break;
363 case NET_SOCKET_DATA_FRAGMENT_SIZE:
364 fibril_rwlock_read_lock( & socket_globals.lock );
365 // find the socket
366 socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call ));
367 if( ! socket ){
368 ERROR_CODE = ENOTSOCK;
369 }else{
370 fibril_rwlock_write_lock( & socket->sending_lock );
371 // set the data fragment size
372 socket->data_fragment_size = SOCKET_GET_DATA_FRAGMENT_SIZE( call );
373 fibril_rwlock_write_unlock( & socket->sending_lock );
374 ERROR_CODE = EOK;
375 }
376 fibril_rwlock_read_unlock( & socket_globals.lock );
377 break;
378 default:
379 ERROR_CODE = ENOTSUP;
380 }
381 ipc_answer_0( callid, ( ipcarg_t ) ERROR_CODE );
382 }
383}
384
385int socket( int domain, int type, int protocol ){
386 ERROR_DECLARE;
387
388 socket_ref socket;
389 int phone;
390 int socket_id;
391 services_t service;
392
393 // find the appropriate service
394 switch( domain ){
395 case PF_INET:
396 switch( type ){
397 case SOCK_STREAM:
398 if( ! protocol ) protocol = IPPROTO_TCP;
399 switch( protocol ){
400 case IPPROTO_TCP:
401 phone = socket_get_tcp_phone();
402 service = SERVICE_TCP;
403 break;
404 default:
405 return EPROTONOSUPPORT;
406 }
407 break;
408 case SOCK_DGRAM:
409 if( ! protocol ) protocol = IPPROTO_UDP;
410 switch( protocol ){
411 case IPPROTO_UDP:
412 phone = socket_get_udp_phone();
413 service = SERVICE_UDP;
414 break;
415 default:
416 return EPROTONOSUPPORT;
417 }
418 break;
419 case SOCK_RAW:
420 default:
421 return ESOCKTNOSUPPORT;
422 }
423 break;
424 // TODO IPv6
425 default:
426 return EPFNOSUPPORT;
427 }
428 // create a new socket structure
429 socket = ( socket_ref ) malloc( sizeof( socket_t ));
430 if( ! socket ) return ENOMEM;
431 bzero( socket, sizeof( * socket ));
432 // request a new socket
433 if( ERROR_OCCURRED(( int ) async_req_3_3( phone, NET_SOCKET, 0, 0, service, ( ipcarg_t * ) & socket_id, ( ipcarg_t * ) & socket->data_fragment_size, ( ipcarg_t * ) & socket->header_size ))){
434 free( socket );
435 return ERROR_CODE;
436 }
437 // finish the new socket initialization
438 socket_initialize( socket, socket_id, phone, service );
439 // store the new socket
440 fibril_rwlock_write_lock( & socket_globals.lock );
441 ERROR_CODE = sockets_add( socket_get_sockets(), socket_id, socket );
442 fibril_rwlock_write_unlock( & socket_globals.lock );
443 if( ERROR_CODE < 0 ){
444 dyn_fifo_destroy( & socket->received );
445 dyn_fifo_destroy( & socket->accepted );
446 free( socket );
447 async_msg_3( phone, NET_SOCKET_CLOSE, ( ipcarg_t ) socket_id, 0, service );
448 return ERROR_CODE;
449 }
450
451 return socket_id;
452}
453
454int socket_send_data( int socket_id, ipcarg_t message, ipcarg_t arg2, const void * data, size_t datalength ){
455 socket_ref socket;
456 aid_t message_id;
457 ipcarg_t result;
458
459 if( ! data ) return EBADMEM;
460 if( ! datalength ) return NO_DATA;
461
462 fibril_rwlock_read_lock( & socket_globals.lock );
463 // find the socket
464 socket = sockets_find( socket_get_sockets(), socket_id );
465 if( ! socket ){
466 fibril_rwlock_read_unlock( & socket_globals.lock );
467 return ENOTSOCK;
468 }
469 // request the message
470 message_id = async_send_3( socket->phone, message, ( ipcarg_t ) socket->socket_id, arg2, socket->service, NULL );
471 // send the address
472 async_data_write_start( socket->phone, data, datalength );
473 fibril_rwlock_read_unlock( & socket_globals.lock );
474 async_wait_for( message_id, & result );
475 return ( int ) result;
476}
477
478int bind( int socket_id, const struct sockaddr * my_addr, socklen_t addrlen ){
479 if( addrlen <= 0 ) return EINVAL;
480 // send the address
481 return socket_send_data( socket_id, NET_SOCKET_BIND, 0, my_addr, ( size_t ) addrlen );
482}
483
484int listen( int socket_id, int backlog ){
485 socket_ref socket;
486 int result;
487
488 if( backlog <= 0 ) return EINVAL;
489 fibril_rwlock_read_lock( & socket_globals.lock );
490 // find the socket
491 socket = sockets_find( socket_get_sockets(), socket_id );
492 if( ! socket ){
493 fibril_rwlock_read_unlock( & socket_globals.lock );
494 return ENOTSOCK;
495 }
496 // request listen backlog change
497 result = ( int ) async_req_3_0( socket->phone, NET_SOCKET_LISTEN, ( ipcarg_t ) socket->socket_id, ( ipcarg_t ) backlog, socket->service );
498 fibril_rwlock_read_unlock( & socket_globals.lock );
499 return result;
500}
501
502int accept( int socket_id, struct sockaddr * cliaddr, socklen_t * addrlen ){
503 socket_ref socket;
504 aid_t message_id;
505 int result;
506 ipc_call_t answer;
507
508 if(( ! cliaddr ) || ( ! addrlen )) return EBADMEM;
509
510 fibril_rwlock_read_lock( & socket_globals.lock );
511 // find the socket
512 socket = sockets_find( socket_get_sockets(), socket_id );
513 if( ! socket ){
514 fibril_rwlock_read_unlock( & socket_globals.lock );
515 return ENOTSOCK;
516 }
517 fibril_mutex_lock( & socket->accept_lock );
518 // wait for an accepted socket
519 ++ socket->blocked;
520 while( dyn_fifo_value( & socket->accepted ) <= 0 ){
521 fibril_rwlock_read_unlock( & socket_globals.lock );
522 fibril_condvar_wait( & socket->accept_signal, & socket->accept_lock );
523 fibril_rwlock_read_lock( & socket_globals.lock );
524 }
525 -- socket->blocked;
526 // request accept
527 message_id = async_send_3( socket->phone, NET_SOCKET_ACCEPT, ( ipcarg_t ) socket->socket_id, 0, socket->service, & answer );
528 // read address
529 async_data_read_start( socket->phone, cliaddr, * addrlen );
530 fibril_rwlock_read_unlock( & socket_globals.lock );
531 async_wait_for( message_id, ( ipcarg_t * ) & result );
532 if( result > 0 ){
533 // dequeue the accepted socket if successful
534 dyn_fifo_pop( & socket->accepted );
535 // set address length
536 * addrlen = SOCKET_GET_ADDRESS_LENGTH( answer );
537 }else if( result == ENOTSOCK ){
538 // empty the queue if no accepted sockets
539 while( dyn_fifo_pop( & socket->accepted ) > 0 );
540 }
541 fibril_mutex_unlock( & socket->accept_lock );
542 return result;
543}
544
545int connect( int socket_id, const struct sockaddr * serv_addr, socklen_t addrlen ){
546 if( ! serv_addr ) return EDESTADDRREQ;
547 if( ! addrlen ) return EDESTADDRREQ;
548 // send the address
549 return socket_send_data( socket_id, NET_SOCKET_CONNECT, 0, serv_addr, addrlen );
550}
551
552int closesocket( int socket_id ){
553 ERROR_DECLARE;
554
555 socket_ref socket;
556
557 fibril_rwlock_write_lock( & socket_globals.lock );
558 socket = sockets_find( socket_get_sockets(), socket_id );
559 if( ! socket ){
560 fibril_rwlock_write_unlock( & socket_globals.lock );
561 return ENOTSOCK;
562 }
563 if( socket->blocked ){
564 fibril_rwlock_write_unlock( & socket_globals.lock );
565 return EINPROGRESS;
566 }
567 // request close
568 ERROR_PROPAGATE(( int ) async_req_3_0( socket->phone, NET_SOCKET_CLOSE, ( ipcarg_t ) socket->socket_id, 0, socket->service ));
569 // free the socket structure
570 socket_destroy( socket );
571 fibril_rwlock_write_unlock( & socket_globals.lock );
572 return EOK;
573}
574
575void socket_destroy( socket_ref socket ){
576 int accepted_id;
577
578 // destroy all accepted sockets
579 while(( accepted_id = dyn_fifo_pop( & socket->accepted )) >= 0 ){
580 socket_destroy( sockets_find( socket_get_sockets(), accepted_id ));
581 }
582 dyn_fifo_destroy( & socket->received );
583 dyn_fifo_destroy( & socket->accepted );
584 sockets_exclude( socket_get_sockets(), socket->socket_id );
585}
586
587int send( int socket_id, void * data, size_t datalength, int flags ){
588 // without the address
589 return sendto_core( NET_SOCKET_SEND, socket_id, data, datalength, flags, NULL, 0 );
590}
591
592int sendto( int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ){
593 if( ! toaddr ) return EDESTADDRREQ;
594 if( ! addrlen ) return EDESTADDRREQ;
595 // with the address
596 return sendto_core( NET_SOCKET_SENDTO, socket_id, data, datalength, flags, toaddr, addrlen );
597}
598
599int sendto_core( ipcarg_t message, int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ){
600 socket_ref socket;
601 aid_t message_id;
602 ipcarg_t result;
603 size_t fragments;
604
605 if( ! data ) return EBADMEM;
606 if( ! datalength ) return NO_DATA;
607 fibril_rwlock_read_lock( & socket_globals.lock );
608 // find socket
609 socket = sockets_find( socket_get_sockets(), socket_id );
610 if( ! socket ){
611 fibril_rwlock_read_unlock( & socket_globals.lock );
612 return ENOTSOCK;
613 }
614 fibril_rwlock_read_lock( & socket->sending_lock );
615 // compute data fragment count
616 fragments = ( datalength + socket->header_size ) / socket->data_fragment_size;
617 if(( datalength + socket->header_size ) % socket->data_fragment_size ) ++ fragments;
618 // request send
619 message_id = async_send_5( socket->phone, message, ( ipcarg_t ) socket->socket_id, socket->data_fragment_size, socket->service, ( ipcarg_t ) flags, fragments, NULL );
620 // send the address if given
621 if(( ! toaddr ) || ( async_data_write_start( socket->phone, toaddr, addrlen ) == EOK )){
622 if( fragments == 1 ){
623 // send all if only one fragment
624 async_data_write_start( socket->phone, data, datalength );
625 }else{
626 // send the first fragment
627 async_data_write_start( socket->phone, data, socket->data_fragment_size - socket->header_size );
628 data = (( const uint8_t * ) data ) + socket->data_fragment_size - socket->header_size;
629 // send the middle fragments
630 while(( -- fragments ) > 1 ){
631 async_data_write_start( socket->phone, data, socket->data_fragment_size );
632 data = (( const uint8_t * ) data ) + socket->data_fragment_size;
633 }
634 // send the last fragment
635 async_data_write_start( socket->phone, data, ( datalength + socket->header_size ) % socket->data_fragment_size );
636 }
637 }
638 fibril_rwlock_read_unlock( & socket->sending_lock );
639 fibril_rwlock_read_unlock( & socket_globals.lock );
640 async_wait_for( message_id, & result );
641 return ( int ) result;
642}
643
644int recv( int socket_id, void * data, size_t datalength, int flags ){
645 // without the address
646 return recvfrom_core( NET_SOCKET_RECV, socket_id, data, datalength, flags, NULL, NULL );
647}
648
649int recvfrom( int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ){
650 if( ! fromaddr ) return EBADMEM;
651 if( ! addrlen ) return NO_DATA;
652 // with the address
653 return recvfrom_core( NET_SOCKET_RECVFROM, socket_id, data, datalength, flags, fromaddr, addrlen );
654}
655
656int recvfrom_core( ipcarg_t message, int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ){
657 socket_ref socket;
658 aid_t message_id;
659 int result;
660 size_t fragments;
661 size_t * lengths;
662 size_t index;
663 ipc_call_t answer;
664
665 if( ! data ) return EBADMEM;
666 if( ! datalength ) return NO_DATA;
667 if( fromaddr && ( ! addrlen )) return EINVAL;
668 fibril_rwlock_read_lock( & socket_globals.lock );
669 // find the socket
670 socket = sockets_find( socket_get_sockets(), socket_id );
671 if( ! socket ){
672 fibril_rwlock_read_unlock( & socket_globals.lock );
673 return ENOTSOCK;
674 }
675 fibril_mutex_lock( & socket->receive_lock );
676 // wait for a received packet
677 ++ socket->blocked;
678 while(( result = dyn_fifo_value( & socket->received )) <= 0 ){
679 fibril_rwlock_read_unlock( & socket_globals.lock );
680 fibril_condvar_wait( & socket->receive_signal, & socket->receive_lock );
681 fibril_rwlock_read_lock( & socket_globals.lock );
682 }
683 -- socket->blocked;
684 fragments = ( size_t ) result;
685 // prepare lengths if more fragments
686 if( fragments > 1 ){
687 lengths = ( size_t * ) malloc( sizeof( size_t ) * fragments + sizeof( size_t ));
688 if( ! lengths ){
689 fibril_mutex_unlock( & socket->receive_lock );
690 fibril_rwlock_read_unlock( & socket_globals.lock );
691 return ENOMEM;
692 }
693 // request packet data
694 message_id = async_send_4( socket->phone, message, ( ipcarg_t ) socket->socket_id, 0, socket->service, ( ipcarg_t ) flags, & answer );
695 // read the address if desired
696 if(( ! fromaddr ) || ( async_data_read_start( socket->phone, fromaddr, * addrlen ) == EOK )){
697 // read the fragment lengths
698 if( async_data_read_start( socket->phone, lengths, sizeof( int ) * ( fragments + 1 )) == EOK ){
699 if( lengths[ fragments ] <= datalength ){
700 // read all fragments if long enough
701 for( index = 0; index < fragments; ++ index ){
702 async_data_read_start( socket->phone, data, lengths[ index ] );
703 data = (( uint8_t * ) data ) + lengths[ index ];
704 }
705 }
706 }
707 }
708 free( lengths );
709 }else{
710 // request packet data
711 message_id = async_send_4( socket->phone, message, ( ipcarg_t ) socket->socket_id, 0, socket->service, ( ipcarg_t ) flags, & answer );
712 // read the address if desired
713 if(( ! fromaddr ) || ( async_data_read_start( socket->phone, fromaddr, * addrlen ) == EOK )){
714 // read all if only one fragment
715 async_data_read_start( socket->phone, data, datalength );
716 }
717 }
718 async_wait_for( message_id, ( ipcarg_t * ) & result );
719 // if successful
720 if( result == EOK ){
721 // dequeue the received packet
722 dyn_fifo_pop( & socket->received );
723 // return read data length
724 result = SOCKET_GET_READ_DATA_LENGTH( answer );
725 // set address length
726 if( fromaddr && addrlen ) * addrlen = SOCKET_GET_ADDRESS_LENGTH( answer );
727 }
728 fibril_mutex_unlock( & socket->receive_lock );
729 fibril_rwlock_read_unlock( & socket_globals.lock );
730 return result;
731}
732
733int getsockopt( int socket_id, int level, int optname, void * value, size_t * optlen ){
734 socket_ref socket;
735 aid_t message_id;
736 ipcarg_t result;
737
738 if( !( value && optlen )) return EBADMEM;
739 if( !( * optlen )) return NO_DATA;
740 fibril_rwlock_read_lock( & socket_globals.lock );
741 // find the socket
742 socket = sockets_find( socket_get_sockets(), socket_id );
743 if( ! socket ){
744 fibril_rwlock_read_unlock( & socket_globals.lock );
745 return ENOTSOCK;
746 }
747 // request option value
748 message_id = async_send_3( socket->phone, NET_SOCKET_GETSOCKOPT, ( ipcarg_t ) socket->socket_id, ( ipcarg_t ) optname, socket->service, NULL );
749 // read the length
750 if( async_data_read_start( socket->phone, optlen, sizeof( * optlen )) == EOK ){
751 // read the value
752 async_data_read_start( socket->phone, value, * optlen );
753 }
754 fibril_rwlock_read_unlock( & socket_globals.lock );
755 async_wait_for( message_id, & result );
756 return ( int ) result;
757}
758
759int setsockopt( int socket_id, int level, int optname, const void * value, size_t optlen ){
760 // send the value
761 return socket_send_data( socket_id, NET_SOCKET_SETSOCKOPT, ( ipcarg_t ) optname, value, optlen );
762
763}
764
765/** @}
766 */
Note: See TracBrowser for help on using the repository browser.