diff -r ba40af6cf815 -r 2383ed4da507 c++/domain-socket-bridge/domain-socket-bridge.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/c++/domain-socket-bridge/domain-socket-bridge.cpp Sun Nov 20 20:40:30 2016 +0100 @@ -0,0 +1,224 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +static const char MESSAGE[] = "Hello, World!\n"; + +static const char PATH[] = "./roura"; + +static const string COMMAND_EXIT("exit\n"); + +static void listener_cb(evutil_socket_t, short, void *); +static void conn_read_cb(struct bufferevent *, void *); +static void conn_write_cb(struct bufferevent *, void *); +static void conn_event_cb(struct bufferevent *, short, void *); +static void signal_cb(evutil_socket_t, short, void *); +static void print_socket_info(int, int); + +int main(int argc, char **argv) { + printf("%4s %8s\n", "*", "STARTED"); + struct event_base *base; + evutil_socket_t listener; + struct event *listener_event; + struct event *signal_event; + + struct sockaddr_un sun; + + setvbuf(stdout, NULL, _IONBF, 0); + + base = event_base_new(); + if (!base) { + printf("%4s %8s %s\n", "*", "ERROR", "unable to initialize libevent"); + return 1; + } + + memset(&sun, 0, sizeof (sun)); + sun.sun_family = AF_UNIX; + strcpy(sun.sun_path, PATH); + + listener = socket(AF_UNIX, SOCK_STREAM, 0); + evutil_make_socket_nonblocking(listener); + + if (bind(listener, (struct sockaddr*) &sun, sizeof (sun)) < 0) { + printf("%4s %8s %s: %s\n", "*", "ERROR", "unable to create domain socket:", PATH); + return 1; + } + + if (listen(listener, 16) < 0) { + printf("%4s %8s %s\n", "*", "ERROR", "unable to listen"); + return 1; + } + + // identifikátor serverového soketu (v současnosti číslo FD) + printf("%4s %8s listening at: sun_path = %s → socketId = %d\n", "*", "SOCKET", sun.sun_path, listener); + + listener_event = event_new(base, listener, EV_READ | EV_PERSIST, listener_cb, (void*) base); + + if (!listener_event) { + printf("%4s %8s %s\n", "*", "ERROR", "unable to do event_new()"); + return 1; + } + + event_add(listener_event, NULL); + + signal_event = evsignal_new(base, SIGINT, signal_cb, (void *) base); + + if (!signal_event || event_add(signal_event, NULL) < 0) { + printf("%4s %8s %s\n", "*", "ERROR", "unable to create/add a signal event"); + return 1; + } + + event_base_dispatch(base); + + event_free(listener_event); + event_free(signal_event); + event_base_free(base); + + // smažeme soket na disku / soubor -- jinak by program příště spadl na bind() + // TODO: co když soket někdo přesune a místo něj dá jiný soubor? + unlink(PATH); + + printf("%4s %8s\n", "*", "FINISHED"); + return 0; +} + +static void listener_cb(evutil_socket_t listener, short event, void *user_data) { + struct event_base *base = (event_base *) user_data; + struct bufferevent *bev; + + struct sockaddr_storage ss; + socklen_t slen = sizeof (ss); + int fd = accept(listener, (struct sockaddr*) &ss, &slen); + + // identifikátor navázaného spojení (v současnosti číslo FD) + int * connectionId = (int*) malloc(sizeof (fd)); + *connectionId = fd; + + if (fd < 0) { + printf("%4d %8s %s\n", *connectionId, "ERROR", "unable to accept()"); + return; + } else if (fd > FD_SETSIZE) { + // FD_SETSIZE = 1024 -- Proč? Co když bude spojení víc? + printf("%4d %8s fd (%d) > FD_SETSIZE (%d)\n", *connectionId, "ERROR", fd, FD_SETSIZE); + close(fd); + return; + } + + evutil_make_socket_nonblocking(fd); + + + bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); + if (!bev) { + printf("%4d %8s %s\n", *connectionId, "ERROR", "constructing bufferevent"); + event_base_loopbreak(base); + return; + } + + + + + bufferevent_setcb(bev, conn_read_cb, conn_write_cb, conn_event_cb, (void*) connectionId); + bufferevent_enable(bev, EV_READ | EV_WRITE); + + printf("%4d %8s somebody has connected: socketId = %d → connectionId = %d\n", *connectionId, "CONN", listener, *connectionId); + print_socket_info(*connectionId, fd); + + bufferevent_write(bev, MESSAGE, strlen(MESSAGE)); +} + +static void conn_read_cb(struct bufferevent *bev, void *user_data) { + int connectionId = *((int*) user_data); + + /* This callback is invoked when there is data to read on bev. */ + struct evbuffer *input = bufferevent_get_input(bev); + struct evbuffer *output = bufferevent_get_output(bev); + struct event_base *base = bufferevent_get_base(bev); + + size_t len = evbuffer_get_length(input); + char *data = (char*) malloc(len); + evbuffer_copyout(input, data, len); + + + string dataFormated(data); + dataFormated = regex_replace(dataFormated, regex("\\n"), "\\n"); + printf("%4d %8s '%s'\n", connectionId, "IN", dataFormated.c_str()); + + if (COMMAND_EXIT.compare(data) == 0) { + struct timeval delay = {2, 123}; + printf("%4d %8s client asks us to terminate; finishing in %ld sesonds and %ld microseconds\n", connectionId, "EXIT", delay.tv_sec, delay.tv_usec); + event_base_loopexit(base, &delay); + } + + evbuffer_add(output, "echo: ", 6); + /* Copy all the data from the input buffer to the output buffer. */ + evbuffer_add_buffer(output, input); + free(data); +} + +static void conn_write_cb(struct bufferevent *bev, void *user_data) { + int connectionId = *((int*) user_data); + + struct evbuffer *output = bufferevent_get_output(bev); + if (evbuffer_get_length(output) == 0) { + printf("%4d %8s\n", connectionId, "FLUSH"); + /* nebudeme ukončovat spojení + bufferevent_free(bev); + */ + } else { + // FIXME: sem to nikdy nepřijde + size_t len = evbuffer_get_length(output); + char *data = (char*) malloc(len); + evbuffer_copyout(output, data, len); + printf("%4d %8s '%s'\n", connectionId, "OUT", data); + free(data); + } +} + +static void conn_event_cb(struct bufferevent *bev, short events, void *user_data) { + int connectionId = *((int*) user_data); + + if (events & BEV_EVENT_EOF) { + printf("%4d %8s\n", connectionId, "CLOSE"); + } else if (events & BEV_EVENT_ERROR) { + printf("%4d %8s %s\n", connectionId, "ERROR", strerror(errno)); + } else { + printf("%4d %8s\n", connectionId, "OTHER"); + // None of the other events can happen here, since we haven't enabled timeouts + } + + bufferevent_free(bev); + free(user_data); +} + +static void signal_cb(evutil_socket_t sig, short events, void *user_data) { + struct event_base *base = (event_base *) user_data; + struct timeval delay = {2, 123}; + + printf("\n%4s %8s got SIGINT (Ctrl+C); finishing in %ld sesonds and %ld microseconds\n", "*", "SIGNAL", delay.tv_sec, delay.tv_usec); + + event_base_loopexit(base, &delay); +} + +static void print_socket_info(int connectionId, int fd) { + struct ucred cr; + unsigned int cl = sizeof (cr); + + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl) == 0) { + printf("%4d %8s client identification: pid=%d, uid=%d, gid=%d\n", connectionId, "CONN", cr.pid, cr.uid, cr.gid); + } +} \ No newline at end of file