From 6643cc5ddd6c1835acffed13ffdf83c41078f8d0 Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Sun, 22 Jun 2008 22:57:19 +0300 Subject: [PATCH] Added SILC Local Network Stream API Allows simple interprocess communication (IPC) style communication between processes in local machine. The implementation uses TCP socket connection in the local machine. --- TODO | 2 - lib/silcutil/Makefile.ad | 2 + lib/silcutil/silclocalnetstream.c | 177 ++++++++++++++++ lib/silcutil/silclocalnetstream.h | 105 ++++++++++ lib/silcutil/silcruntime.h.in | 1 + lib/silcutil/tests/Makefile.am | 6 +- lib/silcutil/tests/test_silclocalnetstream.c | 205 +++++++++++++++++++ 7 files changed, 494 insertions(+), 4 deletions(-) create mode 100644 lib/silcutil/silclocalnetstream.c create mode 100644 lib/silcutil/silclocalnetstream.h create mode 100644 lib/silcutil/tests/test_silclocalnetstream.c diff --git a/TODO b/TODO index 0cb16895..e7693efc 100644 --- a/TODO +++ b/TODO @@ -16,8 +16,6 @@ Runtime library, lib/silcutil/ o Add SILC Zip API, compression. - o Unix socket support to Socket Stream API (local socket stream). - o file removing, chmod, rmmod, etc. chdir, rmdir, stat, etc. to lib/silcutil/silcfileutil.h. diff --git a/lib/silcutil/Makefile.ad b/lib/silcutil/Makefile.ad index 972b83d6..2d565d81 100644 --- a/lib/silcutil/Makefile.ad +++ b/lib/silcutil/Makefile.ad @@ -69,6 +69,7 @@ libsilcutil_la_SOURCES = \ silcrand.c \ silcglobal.c \ silcbufferstream.c \ + silclocalnetstream.c \ silcxml.c include_HEADERS = \ @@ -126,6 +127,7 @@ include_HEADERS = \ silcruntime.h \ silcdir.h \ silcbufferstream.h \ + silclocalnetstream.h \ silcxml.h SILC_EXTRA_DIST = diff --git a/lib/silcutil/silclocalnetstream.c b/lib/silcutil/silclocalnetstream.c new file mode 100644 index 00000000..d93e909f --- /dev/null +++ b/lib/silcutil/silclocalnetstream.c @@ -0,0 +1,177 @@ +/* + + silclocalnetstream.c + + Author: Pekka Riikonen + + Copyright (C) 2008 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#include + +/************************** Types and definitions ***************************/ + +/* Local net listener context */ +typedef struct { + SilcNetListener listener; + char *filepath; + SilcNetCallback callback; + void *context; +} *SilcLocalNetListener; + +/************************ Static utility functions **************************/ + +/* Connection accept callback */ + +static void silc_local_net_accept(SilcResult result, SilcStream stream, + void *context) +{ + SilcLocalNetListener listener = context; + const char *remote_ip; + + if (!silc_socket_stream_get_info(stream, NULL, NULL, &remote_ip, NULL)) { + silc_stream_destroy(stream); + return; + } + + /* Make sure the connection comes from local host */ + if (strcmp(remote_ip, "127.0.0.1")) { + SILC_LOG_DEBUG(("Connection is coming from %s, will not accept", + remote_ip)); + silc_stream_destroy(stream); + return; + } + + listener->callback(result, stream, listener->context); +} + +/****************************** Public API **********************************/ + +/* Create listener */ + +SilcNetListener silc_local_net_create_listener(const char *filepath, + SilcSchedule schedule, + SilcNetCallback callback, + void *context) +{ + SilcLocalNetListener listener; + SilcUInt16 *local_port; + const char *addr = "127.0.0.1"; + char port[8]; + + SILC_LOG_DEBUG(("Creating local network stream listener %s", filepath)); + + if (!filepath || !callback) { + silc_set_errno(SILC_ERR_INVALID_ARGUMENT); + return NULL; + } + + /* Make sure file doesn't exist */ + if (silc_file_stat(filepath, FALSE, NULL)) { + silc_set_errno(SILC_ERR_ALREADY_EXISTS); + return NULL; + } + + listener = silc_calloc(1, sizeof(*listener)); + if (!listener) + return NULL; + listener->callback = callback; + listener->context = context; + + listener->filepath = silc_strdup(filepath); + if (!listener->filepath) { + silc_free(listener); + return NULL; + } + + /* Create local TCP listener */ + listener->listener = + silc_net_tcp_create_listener(&addr, 1, 0, TRUE, FALSE, schedule, + silc_local_net_accept, listener); + if (!listener) { + silc_free(listener); + return NULL; + } + + /* Get the bound port */ + local_port = silc_net_listener_get_port(listener->listener, NULL); + if (!local_port) { + silc_net_close_listener(listener->listener); + silc_free(listener); + return NULL; + } + + /* Create the file */ + silc_snprintf(port, sizeof(port), "%d", *local_port); + if (silc_file_writefile(filepath, port, strlen(port) + 1)) { + silc_free(local_port); + silc_net_close_listener(listener->listener); + silc_free(listener); + return NULL; + } + + SILC_LOG_DEBUG(("Created local network stream listener %p", listener)); + + silc_free(local_port); + + return (SilcNetListener)listener; +} + +/* Close listener */ + +void silc_local_net_close_listener(SilcNetListener local_listener) +{ + SilcLocalNetListener listener = (SilcLocalNetListener)local_listener; + + SILC_LOG_DEBUG(("Closing local network stream listener %p, %s", + listener, listener->filepath)); + + unlink(listener->filepath); + silc_net_close_listener(listener->listener); + silc_free(listener); +} + +/* Connect to the local network listener */ + +SilcAsyncOperation silc_local_net_connect(const char *filepath, + SilcSchedule schedule, + SilcNetCallback callback, + void *context) +{ + unsigned char *port_data; + int port; + + SILC_LOG_DEBUG(("Connecting to local network listner %s", filepath)); + + if (!filepath) { + silc_set_errno(SILC_ERR_INVALID_ARGUMENT); + if (callback) + callback(silc_errno, NULL, context); + return NULL; + } + + /* Read the port from file */ + port_data = silc_file_readfile(filepath, NULL, NULL); + if (!port_data) { + if (callback) + callback(silc_errno, NULL, context); + return NULL; + } + port = atoi(port_data); + + silc_free(port_data); + + /* Connect */ + return silc_net_tcp_connect("127.0.0.1", "127.0.0.1", port, schedule, + callback, context); +} diff --git a/lib/silcutil/silclocalnetstream.h b/lib/silcutil/silclocalnetstream.h new file mode 100644 index 00000000..87f62d20 --- /dev/null +++ b/lib/silcutil/silclocalnetstream.h @@ -0,0 +1,105 @@ +/* + + silclocalnetstream.h + + Author: Pekka Riikonen + + Copyright (C) 2008 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +/****h* silcutil/Local Network Stream Interface + * + * DESCRIPTION + * + * Local network stream interface enables two or more processes to communicate + * with each other in the local machine using local network start. The + * interface provides a form of interprocess communication (IPC) using network + * sockets. + * + ***/ + +#ifndef SILCLOCALNETSTREAM_H +#define SILCLOCALNETSTREAM_H + +/****f* silcutil/silc_local_net_create_listener + * + * SYNOPSIS + * + * SilcNetListener silc_local_net_create_listener(const char *filepath, + * SilcSchedule schedule, + * SilcNetCallback callback, + * void *context); + * + * DESCRIPTION + * + * Creates a local network stream listener and returns a network server. + * The `filepath' is a local filepath that must be used by the clients to + * connect to the server. + * + * The `callback' will be called when a client connects to the listener + * with the `context'. The returned listener must be closed by calling + * silc_local_net_close_listener. + * + * Clients can connect to the listener by calling the + * silc_local_net_connect. + * + * Returns NULL on error and set silc_errno. If `schedule' is NULL this + * will call silc_schedule_get_global to try to get global scheduler. + * + ***/ +SilcNetListener silc_local_net_create_listener(const char *filepath, + SilcSchedule schedule, + SilcNetCallback callback, + void *context); + +/****f* silcutil/silc_local_net_close_listener + * + * SYNOPSIS + * + * void silc_local_net_close_listener(SilcNetListener local_listener); + * + * DESCRIPTION + * + * Closes the local network stream listener. + * + ***/ +void silc_local_net_close_listener(SilcNetListener local_listener); + +/****f* silcutil/silc_local_net_connect + * + * SYNOPSIS + * + * SilcAsyncOperation silc_local_net_connect(const char *filepath, + * SilcSchedule schedule, + * SilcNetCallback callback, + * void *context); + * + * DESCRIPTION + * + * Connects to the local network server at the provided `filepath'. If + * the `filepath' does not exist or is not valid local network listener + * the connection will fail. + * + * The `callback' with `context' will be called once the connection has + * been created. + * + * If `schedule' is NULL this will call silc_schedule_get_global to try + * to get global scheduler. + * + ***/ +SilcAsyncOperation silc_local_net_connect(const char *filepath, + SilcSchedule schedule, + SilcNetCallback callback, + void *context); + +#endif /* SILCLOCALNETSTREAM_H */ diff --git a/lib/silcutil/silcruntime.h.in b/lib/silcutil/silcruntime.h.in index 2ad645e8..257b4dd5 100644 --- a/lib/silcutil/silcruntime.h.in +++ b/lib/silcutil/silcruntime.h.in @@ -240,6 +240,7 @@ extern "C" { #include #include #include +#include #include #include #include diff --git a/lib/silcutil/tests/Makefile.am b/lib/silcutil/tests/Makefile.am index bc693704..78323eda 100644 --- a/lib/silcutil/tests/Makefile.am +++ b/lib/silcutil/tests/Makefile.am @@ -24,7 +24,8 @@ check_PROGRAMS = \ test_silcatomic test_silcmutex test_silctime test_silcthread \ test_silcdll test_silcenv test_silctimer test_silcbitops \ test_silcregex test_silcbuffmt test_silcdir test_silcthreadqueue \ - test_silcrand test_silcglobal test_silcbufferstream test_silcxml + test_silcrand test_silcglobal test_silcbufferstream test_silcxml \ + test_silclocalnetstream TESTS = test_silcstrutil test_silcstringprep test_silchashtable \ test_silclist test_silcfsm test_silcasync test_silcschedule \ @@ -32,7 +33,8 @@ TESTS = test_silcstrutil test_silcstringprep test_silchashtable \ test_silcatomic test_silctime test_silcthread \ test_silcdll test_silcenv test_silctimer test_silcbitops \ test_silcregex test_silcbuffmt test_silcdir test_silcthreadqueue \ - test_silcrand test_silcglobal test_silcbufferstream + test_silcrand test_silcglobal test_silcbufferstream \ + test_silclocalnetstream LIBS = $(SILC_COMMON_LIBS) LDADD = -L.. -L../.. -lsrt diff --git a/lib/silcutil/tests/test_silclocalnetstream.c b/lib/silcutil/tests/test_silclocalnetstream.c new file mode 100644 index 00000000..010936e4 --- /dev/null +++ b/lib/silcutil/tests/test_silclocalnetstream.c @@ -0,0 +1,205 @@ +/* SILC Local Net Stream API tests */ + +#include "silcruntime.h" + +SilcSchedule schedule; + +typedef struct { + SilcFSM fsm; + SilcFSMEventStruct sema; + SilcFSMThreadStruct thread; + SilcNetListener server; + SilcStream client_stream; + SilcResult client_status; + SilcStream server_stream; + SilcResult server_status; + SilcBool success; +} *Foo; + +SILC_FSM_STATE(test_st_start); +SILC_FSM_STATE(test_st_second); +SILC_FSM_STATE(test_st_finish); + +SILC_FSM_STATE(test_st_connect); +SILC_FSM_STATE(test_st_connected); + +static void test_accept_connection(SilcResult status, SilcStream stream, + void *context) +{ + Foo f = context; + SILC_LOG_DEBUG(("Accepted new connection")); + f->client_status = status; + f->client_stream = stream; + SILC_FSM_EVENT_SIGNAL(&f->sema); +} + +static void test_connected(SilcResult status, SilcStream stream, + void *context) +{ + Foo f = context; + SILC_LOG_DEBUG(("Connected to server")); + f->server_status = status; + f->server_stream = stream; + SILC_FSM_CALL_CONTINUE(&f->thread); +} + +SILC_FSM_STATE(test_st_connect) +{ + Foo f = fsm_context; + + SILC_LOG_DEBUG(("test_st_connect")); + SILC_LOG_DEBUG(("Connecting to server")); + + silc_fsm_next(fsm, test_st_connected); + SILC_FSM_CALL(silc_local_net_connect("local_net", + silc_fsm_get_schedule(fsm), + test_connected, f)); +} + +SILC_FSM_STATE(test_st_connected) +{ + Foo f = fsm_context; + const char *host, *ip; + SilcUInt16 port; + + SILC_LOG_DEBUG(("test_st_connected")); + + if (f->server_status != SILC_OK) { + SILC_LOG_DEBUG(("Creating connection failed")); + return SILC_FSM_FINISH; + } + + silc_socket_stream_get_info(f->server_stream, NULL, &host, &ip, &port); + SILC_LOG_DEBUG(("Connected to server %s, %s:%d", host, ip, port)); + + return SILC_FSM_FINISH; +} + +SILC_FSM_STATE(test_st_start) +{ + Foo f = fsm_context; + + SILC_LOG_DEBUG(("test_st_start")); + + SILC_LOG_DEBUG(("Creating local network listener")); + f->server = silc_local_net_create_listener("local_net", + silc_fsm_get_schedule(fsm), + test_accept_connection, f); + if (!f->server) { + /** Creating network listener failed */ + SILC_LOG_DEBUG(("Listener creation failed")); + silc_fsm_next(fsm, test_st_finish); + return SILC_FSM_CONTINUE; + } + + /* Create thread to connect to the listener */ + silc_fsm_thread_init(&f->thread, fsm, f, NULL, NULL, FALSE); + silc_fsm_start(&f->thread, test_st_connect); + + /** Start waiting connection */ + SILC_LOG_DEBUG(("Start waiting for incoming connections")); + silc_fsm_event_init(&f->sema, fsm); + silc_fsm_next(fsm, test_st_second); + return SILC_FSM_CONTINUE; +} + +SILC_FSM_STATE(test_st_second) +{ + Foo f = fsm_context; + const char *ip, *host; + SilcUInt16 port; + + SILC_LOG_DEBUG(("test_st_second")); + + SILC_FSM_EVENT_WAIT(&f->sema); + + if (f->client_status != SILC_OK) { + /** Accepting new connection failed */ + SILC_LOG_DEBUG(("Accepting failed %d", f->client_status)); + silc_fsm_next(fsm, test_st_finish); + return SILC_FSM_CONTINUE; + } + + silc_socket_stream_get_info(f->client_stream, NULL, &host, &ip, &port); + SILC_LOG_DEBUG(("Accepted new connection %s, %s:%d", host, ip, port)); + + /** Wait thread to terminate */ + f->success = TRUE; + silc_fsm_next(fsm, test_st_finish); + SILC_FSM_THREAD_WAIT(&f->thread); +} + +SILC_FSM_STATE(test_st_finish) +{ + Foo f = fsm_context; + + SILC_LOG_DEBUG(("test_st_finish")); + + if (f->server_stream) { + silc_stream_close(f->server_stream); + silc_stream_destroy(f->server_stream); + } + if (f->client_stream) { + silc_stream_close(f->client_stream); + silc_stream_destroy(f->client_stream); + } + + SILC_LOG_DEBUG(("Closing network listener")); + if (f->server) + silc_local_net_close_listener(f->server); + + SILC_LOG_DEBUG(("Finish machine")); + return SILC_FSM_FINISH; +} + +static void destructor(SilcFSM fsm, void *fsm_context, + void *destructor_context) +{ + SILC_LOG_DEBUG(("FSM destructor, stopping scheduler")); + silc_fsm_free(fsm); + silc_schedule_stop(schedule); +} + +int main(int argc, char **argv) +{ + SilcBool success = FALSE; + SilcFSM fsm; + Foo f; + + if (argc > 1 && !strcmp(argv[1], "-d")) { + silc_log_debug(TRUE); + silc_log_debug_hexdump(TRUE); + silc_log_set_debug_string("*net*,*stream*,*errno*"); + } + + SILC_LOG_DEBUG(("Allocating scheduler")); + schedule = silc_schedule_init(0, NULL, NULL, NULL); + + f = silc_calloc(1, sizeof(*f)); + if (!f) + goto err; + + SILC_LOG_DEBUG(("Allocating FSM context")); + fsm = silc_fsm_alloc(f, destructor, NULL, schedule); + if (!fsm) + goto err; + silc_fsm_start(fsm, test_st_start); + f->fsm = fsm; + + SILC_LOG_DEBUG(("Running scheduler")); + silc_schedule(schedule); + + if (!f->success) + goto err; + + silc_schedule_uninit(schedule); + silc_free(f); + + success = TRUE; + + err: + SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE")); + fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE"); + + return !success; +} -- 2.24.0