Commit 6d05b41a authored by Michael Graff's avatar Michael Graff
Browse files

Commit socket code so far -- nowhere near done, but I don't wanna loose work.

parent 35921f41
#include "attribute.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <isc/assertions.h>
#include <isc/memcluster.h>
#include <isc/task.h>
#include <isc/thread.h>
#include <isc/result.h>
#include <isc/socket.h>
isc_memctx_t mctx = NULL;
static isc_boolean_t
my_callback(isc_task_t task, isc_event_t event)
{
char *name = event->arg;
printf("task %s (%p)\n", name, task);
fflush(stdout);
isc_event_free(&event);
return (ISC_FALSE);
}
static isc_boolean_t
my_shutdown(isc_task_t task, isc_event_t event)
{
char *name = event->arg;
printf("shutdown %s (%p)\n", name, task);
fflush(stdout);
isc_event_free(&event);
return (ISC_TRUE);
}
int
main(int argc, char *argv[])
{
isc_taskmgr_t manager = NULL;
isc_task_t t1 = NULL, t2 = NULL;
isc_task_t t3 = NULL, t4 = NULL;
isc_event_t event;
unsigned int workers;
isc_socketmgr_t socketmgr;
isc_socket_t so1, so2;
if (argc > 1)
workers = atoi(argv[1]);
else
workers = 2;
printf("%d workers\n", workers);
INSIST(isc_memctx_create(0, 0, &mctx) == ISC_R_SUCCESS);
INSIST(isc_taskmgr_create(mctx, workers, 0, &manager) ==
ISC_R_SUCCESS);
INSIST(isc_task_create(manager, my_shutdown, "1", 0, &t1) ==
ISC_R_SUCCESS);
INSIST(isc_task_create(manager, my_shutdown, "2", 0, &t2) ==
ISC_R_SUCCESS);
INSIST(isc_task_create(manager, my_shutdown, "3", 0, &t3) ==
ISC_R_SUCCESS);
INSIST(isc_task_create(manager, my_shutdown, "4", 0, &t4) ==
ISC_R_SUCCESS);
printf("task 1 = %p\n", t1);
printf("task 2 = %p\n", t2);
printf("task 3 = %p\n", t3);
printf("task 4 = %p\n", t4);
socketmgr = NULL;
INSIST(isc_socketmgr_create(mctx, &socketmgr) == ISC_R_SUCCESS);
so1 = NULL;
INSIST(isc_socket_create(socketmgr, isc_socket_udp,
&so1) == ISC_R_SUCCESS);
so2 = NULL;
INSIST(isc_socket_create(socketmgr, isc_socket_udp,
&so2) == ISC_R_SUCCESS);
sleep(2);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "2",
sizeof *event);
isc_task_send(t2, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "3",
sizeof *event);
isc_task_send(t3, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "4",
sizeof *event);
isc_task_send(t4, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "2",
sizeof *event);
isc_task_send(t2, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "3",
sizeof *event);
isc_task_send(t3, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "4",
sizeof *event);
isc_task_send(t4, &event);
isc_task_purge(t3, NULL, 0);
isc_task_detach(&t1);
isc_task_detach(&t2);
isc_task_detach(&t3);
isc_task_detach(&t4);
sleep(10);
printf("destroy\n");
isc_socket_detach(&so1);
isc_socket_detach(&so2);
isc_socketmgr_destroy(&socketmgr);
isc_taskmgr_destroy(&manager);
printf("destroyed\n");
isc_mem_stats(mctx, stdout);
isc_memctx_destroy(&mctx);
return (0);
}
#ifndef ISC_SOCKET_H
#define ISC_SOCKET_H 1
/*****
***** Module Info
*****/
/*
* Sockets
*
* Provides TCP and UDP sockets for network I/O. The sockets are event
* sources in the task system.
*
* When I/O completes, a completion event for the socket is posted to the
* event queue of the task which requested the I/O.
*
* MP:
* The module ensures appropriate synchronization of data structures it
* creates and manipulates.
*
* Clients of this module must not be holding a socket's task's lock when
* making a call that affects that socket. Failure to follow this rule
* can result in deadlock.
*
* The caller must ensure that isc_socketmgr_destroy() is called only
* once for a given manager.
*
* Reliability:
* No anticipated impact.
*
* Resources:
* <TBS>
*
* Security:
* No anticipated impact.
*
* Standards:
* None.
*/
/***
*** Imports
***/
#include <isc/boolean.h>
#include <isc/result.h>
#include <isc/task.h>
#include <isc/region.h>
#include <isc/memcluster.h>
#include <netinet/in.h>
/***
*** Types
***/
typedef struct isc_socket *isc_socket_t;
typedef struct isc_socketmgr *isc_socketmgr_t;
/*
* XXX Export this as isc/sockaddr.h
*/
typedef struct isc_sockaddr {
/*
* XXX Must be big enough for all sockaddr types we care about.
*/
union foo {
struct sockaddr_in sin;
};
} *isc_sockaddr_t;
typedef struct isc_sockevent {
struct isc_event common; /* Sender is the socket. */
isc_result_t result; /* OK, EOF, whatever else */
unsigned int n; /* bytes read or written */
struct isc_sockaddr address; /* source address */
int length; /* length of address */
} *isc_sockevent_t;
#define ISC_SOCKEVENT_ANYEVENT (0)
#define ISC_SOCKEVENT_RECVDONE (EVENT_CLASS_SOCKET + 1)
#define ISC_SOCKEVENT_SENDDONE (EVENT_CLASS_SOCKET + 2)
#define ISC_SOCKEVENT_NEWCONN (EVENT_CLASS_SOCKET + 3)
#define ISC_SOCKEVENT_CONNECTED (EVENT_CLASS_SOCKET + 4)
typedef enum {
isc_socket_udp,
isc_socket_tcp
} isc_sockettype_t;
typedef enum {
isc_sockshut_reading,
isc_sockshut_writing,
isc_sockshut_all
} isc_socketshutdown_t;
/***
*** Socket and Socket Manager Functions
***
*** Note: all Ensures conditions apply only if the result is success for
*** those functions which return an isc_result.
***/
isc_result_t
isc_socket_create(isc_socketmgr_t manager,
isc_sockettype_t type,
isc_socket_t *socketp);
/*
* Create a new 'type' socket managed by 'manager'.
*
* Requires:
*
* 'manager' is a valid manager
*
* 'socketp' is a valid pointer, and *socketp == NULL
*
* Ensures:
*
* '*socketp' is attached to the newly created socket
*
* Returns:
*
* Success
* No memory
* Unexpected error
*/
void
isc_socket_shutdown(isc_socket_t socket, isc_task_t task,
isc_socketshutdown_t how);
/*
* Shutdown 'socket' according to 'how'.
*
* Note: if 'task' is NULL, then the shutdown applies to all tasks using the
* socket.
*
* Requires:
*
* 'socket' is a valid socket.
*
* 'task' is NULL or is a valid task.
*
* Ensures:
*
* If 'how' is 'isc_sockshut_reading' or 'isc_sockshut_all' then
*
* Any pending read completion events for the task are
* removed from the task's event queue.
*
* No further read completion events will be delivered to the
* task.
*
* No further read requests may be made.
*
* If 'how' is 'isc_sockshut_writing' or 'isc_sockshut_all' then
*
* Any pending write completion events for the task are
* removed from the task's event queue.
*
* No further write completion events will be delivered to the
* task.
*
* No further write requests may be made.
*
* If 'socket' is a TCP socket, then when the last currently
* pending write completes, TCP FIN will sent to the remote peer.
*/
void
isc_socket_attach(isc_socket_t socket, isc_socket_t *socketp);
/*
* Attach *socketp to socket.
*
* Requires:
*
* 'socket' is a valid socket.
*
* 'socketp' points to a NULL socket.
*
* Ensures:
*
* *socketp is attached to socket.
*/
void
isc_socket_detach(isc_socket_t *socketp);
/*
* Detach *socketp from its socket.
*
* Notes:
*
* Detaching the last reference may cause any still-pending I/O to be
* cancelled.
*
* Requires:
*
* 'socketp' points to a valid socket.
*
* Ensures:
*
* *socketp is NULL.
*
* If '*socketp' is the last reference to the socket,
* then:
*
* The socket will be shutdown (both reading and writing)
* for all tasks.
*
* All resources used by the socket have been freed
*/
isc_result_t
net_socket_bind(isc_socket_t socket, struct isc_sockaddr *addressp,
int length);
/*
* Bind 'socket' to '*addressp'.
*
* Requires:
*
* 'socket' is a valid socket
*
* 'addressp' points to a valid isc_sockaddr.
*
* 'length' is approprate for the isc_sockaddr type.
*
* Returns:
*
* Success
* Address not available
* Address in use
* Permission denied
* Unexpected error
*/
isc_result_t
isc_socket_listen(isc_socket_t socket, int backlog,
isc_task_t task, isc_taskaction_t action, void *arg);
/*
* Listen on 'socket'. Every time a new connection request arrives,
* a NEWCONN event with action 'action' and arg 'arg' will be posted
* to the event queue for 'task'.
*
* Notes:
*
* 'backlog' is as in the UNIX system call listen().
*
* Requires:
*
* 'socket' is a valid TCP socket.
*
* 'task' is a valid task
*
* 'action' is a valid action
*
* Returns:
*
* Success
* Unexpected error
*/
void
isc_socket_hold(isc_socket_t socket);
/*
* Put a TCP listener socket on hold. No NEWCONN events will be posted
*
* Notes:
*
* While 'on hold', new connection requests will be queued or dropped
* by the operating system.
*
* Requires:
*
* 'socket' is a valid TCP socket
*
* Some task is listening on the socket.
*
*/
void
isc_socket_unhold(isc_socket_t socket);
/*
* Restore normal NEWCONN event posting.
*
* Requires:
*
* 'socket' is a valid TCP socket
*
* Some task is listening on the socket.
*
* 'socket' is holding.
*
*/
isc_result_t
isc_socket_accept(isc_socket_t s1, isc_socket_t *s2p);
/*
* Accept a connection from 's1', creating a new socket for the connection
* and attaching '*s2p' to it.
*
* Requires:
*
* 'socket' is a valid TCP socket.
*
* s2p is a valid pointer, and *s2p == NULL;
*
* Ensures:
*
* *s2p is attached to the newly created socket.
*
* Returns:
*
* Success
* No memory
* No pending connection requests
* Unexpected error
*/
isc_result_t
isc_socket_connect(isc_socket_t socket, struct isc_sockaddr *addressp,
int length, isc_task_t task, isc_taskaction_t action,
void *arg);
/*
* Connect 'socket' to peer with address *saddr. When the connection
* succeeds, or when an error occurs, a CONNECTED event with action 'action'
* and arg 'arg' will be posted to the event queue for 'task'.
*
* Requires:
*
* 'socket' is a valid TCP socket
*
* 'addressp' points to a valid isc_sockaddr
*
* 'length' is approprate for the isc_sockaddr type
*
* 'task' is a valid task
*
* 'action' is a valid action
*
* Returns:
*
* Success
* No memory
* Address not available
* Address in use
* Host unreachable
* Network unreachable
* Connection refused
* Unexpected error
*/
isc_result_t
isc_socket_getpeername(isc_socket_t socket, struct isc_sockaddr *addressp,
int *lengthp);
/*
* Get the name of the peer connected to 'socket'.
*
* Requires:
*
* 'socket' is a valid TCP socket.
*
* 'addressp' points to '*lengthp' bytes.
*
* Returns:
*
* Success
* Address buffer too small
*/
isc_result_t
isc_socket_getsockname(isc_socket_t socket, struct isc_sockaddr *addressp,
int *lengthp);
/*
* Get the name of 'socket'.
*
* Requires:
*
* 'socket' is a valid socket.
*
* 'addressp' points to '*lengthp' bytes.
*
* Returns:
*
* Success
* Address buffer too small
*/
isc_result_t
isc_socket_recv(isc_socket_t socket, isc_region_t region,
isc_boolean_t partial,
isc_task_t task, isc_taskaction_t action, void *arg);
/*
* Receive from 'socket', storing the results in region.
*
* Notes:
*
* Let 'length' refer to the length of 'region'.
*
* If 'partial' is true, then at most 'length' bytes will be read.
* Otherwise the read will not complete until exactly 'length' bytes
* have been read.
*
* The read will complete when the desired number of bytes have been
* read, if end-of-input occurs, or if an error occurs. A read done
* event with the given 'action' and 'arg' will be posted to the
* event queue of 'task'.
*
* The caller may neither read from nor write to 'region' until it
* has received the read completion event.
*
* Requires:
*
* 'socket' is a valid socket
*
* 'region' is a valid region
*
* 'task' is a valid task
*
* action != NULL and is a valid action
*
* Returns:
*
* Success
* No memory
* Unexpected error
*/
isc_result_t
isc_socket_send(isc_socket_t socket, isc_region_t region,
isc_task_t task, isc_taskaction_t action, void *arg);
/*
* Send the contents of 'region' to the socket's peer.
*
* Notes:
*
* Shutting down the requestor's task *may* result in any
* still pending writes being dropped.
*
* If 'action' is NULL, then no completion event will be posted.
*
* The caller may neither read from nor write to 'region' until it
* has received the write completion event, or all references to the
* socket have been detached.
*
* Requires:
*
* 'socket' is a valid socket
*
* 'region' is a valid region
*
* 'task' is a valid task
*
* action == NULL or is a valid action
*
* Returns:
*
* Success
* No memory
* Unexpected error
*/
/* XXX this is some of how to do a read
* generate new net_request
* generate new read-result net_event
* attach to requestor
* lock socket
* queue request
* unlock socket
*/
isc_result_t
isc_socketmgr_create(isc_memctx_t mctx, isc_socketmgr_t *managerp);
/*
* Create a socket manager.
*
* Notes:
*
* All memory will be allocated in memory context 'mctx'.
*
* Requires:
*
* 'mctx' is a valid memory context.
*