~martin-decky/helenos/rcu

« back to all changes in this revision

Viewing changes to uspace/srv/net/socket/socket_client.c

  • Committer: Pavel Rimsky
  • Date: 2010-02-20 20:54:53 UTC
  • mfrom: (292 head)
  • mto: This revision was merged to the branch mainline in revision 296.
  • Revision ID: pavel@pavel-laptop-20100220205453-70sim280j709dgp3
Synchronize with head.

Show diffs side-by-side

added added

removed removed

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