c++/domain-socket-bridge/domain-socket-bridge.cpp
author František Kučera <franta-hg@frantovo.cz>
Sun, 04 Jun 2017 18:55:08 +0200
changeset 46 616e71c2d754
parent 43 b54d76467040
permissions -rw-r--r--
lpt-signal-generator: měření času stráveného v outb()
franta-hg@28
     1
#include <string.h>
franta-hg@28
     2
#include <errno.h>
franta-hg@28
     3
#include <stdio.h>
franta-hg@28
     4
#include <signal.h>
franta-hg@30
     5
#include <stdlib.h>
franta-hg@40
     6
#include <string>
franta-hg@40
     7
#include <regex>
franta-hg@28
     8
franta-hg@28
     9
#include <event2/bufferevent.h>
franta-hg@28
    10
#include <event2/buffer.h>
franta-hg@28
    11
#include <event2/listener.h>
franta-hg@28
    12
#include <event2/util.h>
franta-hg@28
    13
#include <event2/event.h>
franta-hg@38
    14
#include <sys/socket.h>
franta-hg@29
    15
#include <sys/un.h>
franta-hg@29
    16
#include <unistd.h>
franta-hg@28
    17
franta-hg@40
    18
using namespace std;
franta-hg@40
    19
franta-hg@42
    20
static const string HELLO_MESSAGE("Hello, World!\n");
franta-hg@42
    21
static const string SOCKET_PATH("./roura");
franta-hg@40
    22
static const string COMMAND_EXIT("exit\n");
franta-hg@40
    23
franta-hg@31
    24
static void listener_cb(evutil_socket_t, short, void *);
franta-hg@30
    25
static void conn_read_cb(struct bufferevent *, void *);
franta-hg@28
    26
static void conn_write_cb(struct bufferevent *, void *);
franta-hg@28
    27
static void conn_event_cb(struct bufferevent *, short, void *);
franta-hg@28
    28
static void signal_cb(evutil_socket_t, short, void *);
franta-hg@40
    29
static void print_socket_info(int, int);
franta-hg@31
    30
franta-hg@28
    31
int main(int argc, char **argv) {
franta-hg@40
    32
	printf("%4s %8s\n", "*", "STARTED");
franta-hg@28
    33
	struct event_base *base;
franta-hg@31
    34
	evutil_socket_t listener;
franta-hg@31
    35
	struct event *listener_event;
franta-hg@28
    36
	struct event *signal_event;
franta-hg@28
    37
franta-hg@29
    38
	struct sockaddr_un sun;
franta-hg@28
    39
franta-hg@31
    40
	setvbuf(stdout, NULL, _IONBF, 0);
franta-hg@31
    41
franta-hg@28
    42
	base = event_base_new();
franta-hg@28
    43
	if (!base) {
franta-hg@40
    44
		printf("%4s %8s %s\n", "*", "ERROR", "unable to initialize libevent");
franta-hg@28
    45
		return 1;
franta-hg@28
    46
	}
franta-hg@28
    47
franta-hg@29
    48
	memset(&sun, 0, sizeof (sun));
franta-hg@29
    49
	sun.sun_family = AF_UNIX;
franta-hg@42
    50
	strcpy(sun.sun_path, SOCKET_PATH.c_str());
franta-hg@28
    51
franta-hg@31
    52
	listener = socket(AF_UNIX, SOCK_STREAM, 0);
franta-hg@31
    53
	evutil_make_socket_nonblocking(listener);
franta-hg@28
    54
franta-hg@31
    55
	if (bind(listener, (struct sockaddr*) &sun, sizeof (sun)) < 0) {
franta-hg@42
    56
		printf("%4s %8s %s: %s\n", "*", "ERROR", "unable to create domain socket:", SOCKET_PATH.c_str());
franta-hg@28
    57
		return 1;
franta-hg@28
    58
	}
franta-hg@28
    59
franta-hg@31
    60
	if (listen(listener, 16) < 0) {
franta-hg@40
    61
		printf("%4s %8s %s\n", "*", "ERROR", "unable to listen");
franta-hg@31
    62
		return 1;
franta-hg@31
    63
	}
franta-hg@31
    64
franta-hg@39
    65
	// identifikátor serverového soketu (v současnosti číslo FD)
franta-hg@40
    66
	printf("%4s %8s listening at: sun_path = %s → socketId = %d\n", "*", "SOCKET", sun.sun_path, listener);
franta-hg@39
    67
franta-hg@31
    68
	listener_event = event_new(base, listener, EV_READ | EV_PERSIST, listener_cb, (void*) base);
franta-hg@31
    69
franta-hg@31
    70
	if (!listener_event) {
franta-hg@40
    71
		printf("%4s %8s %s\n", "*", "ERROR", "unable to do event_new()");
franta-hg@31
    72
		return 1;
franta-hg@31
    73
	}
franta-hg@31
    74
franta-hg@31
    75
	event_add(listener_event, NULL);
franta-hg@31
    76
franta-hg@28
    77
	signal_event = evsignal_new(base, SIGINT, signal_cb, (void *) base);
franta-hg@28
    78
franta-hg@28
    79
	if (!signal_event || event_add(signal_event, NULL) < 0) {
franta-hg@40
    80
		printf("%4s %8s %s\n", "*", "ERROR", "unable to create/add a signal event");
franta-hg@28
    81
		return 1;
franta-hg@28
    82
	}
franta-hg@28
    83
franta-hg@28
    84
	event_base_dispatch(base);
franta-hg@28
    85
franta-hg@31
    86
	event_free(listener_event);
franta-hg@28
    87
	event_free(signal_event);
franta-hg@28
    88
	event_base_free(base);
franta-hg@30
    89
franta-hg@32
    90
	// smažeme soket na disku / soubor -- jinak by program příště spadl na bind()
franta-hg@39
    91
	// TODO: co když soket někdo přesune a místo něj dá jiný soubor?
franta-hg@42
    92
	unlink(SOCKET_PATH.c_str());
franta-hg@28
    93
franta-hg@40
    94
	printf("%4s %8s\n", "*", "FINISHED");
franta-hg@28
    95
	return 0;
franta-hg@28
    96
}
franta-hg@28
    97
franta-hg@31
    98
static void listener_cb(evutil_socket_t listener, short event, void *user_data) {
franta-hg@28
    99
	struct event_base *base = (event_base *) user_data;
franta-hg@28
   100
	struct bufferevent *bev;
franta-hg@28
   101
franta-hg@31
   102
	struct sockaddr_storage ss;
franta-hg@31
   103
	socklen_t slen = sizeof (ss);
franta-hg@31
   104
	int fd = accept(listener, (struct sockaddr*) &ss, &slen);
franta-hg@40
   105
franta-hg@40
   106
	// identifikátor navázaného spojení (v současnosti číslo FD)
franta-hg@40
   107
	int * connectionId = (int*) malloc(sizeof (fd));
franta-hg@40
   108
	*connectionId = fd;
franta-hg@40
   109
franta-hg@31
   110
	if (fd < 0) {
franta-hg@40
   111
		printf("%4d %8s %s\n", *connectionId, "ERROR", "unable to accept()");
franta-hg@31
   112
		return;
franta-hg@31
   113
	} else if (fd > FD_SETSIZE) {
franta-hg@38
   114
		// FD_SETSIZE = 1024 -- Proč? Co když bude spojení víc?
franta-hg@40
   115
		printf("%4d %8s fd (%d) > FD_SETSIZE (%d)\n", *connectionId, "ERROR", fd, FD_SETSIZE);
franta-hg@31
   116
		close(fd);
franta-hg@31
   117
		return;
franta-hg@31
   118
	}
franta-hg@31
   119
franta-hg@31
   120
	evutil_make_socket_nonblocking(fd);
franta-hg@39
   121
franta-hg@31
   122
franta-hg@31
   123
	bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
franta-hg@28
   124
	if (!bev) {
franta-hg@40
   125
		printf("%4d %8s %s\n", *connectionId, "ERROR", "constructing bufferevent");
franta-hg@28
   126
		event_base_loopbreak(base);
franta-hg@28
   127
		return;
franta-hg@28
   128
	}
franta-hg@39
   129
franta-hg@40
   130
franta-hg@39
   131
franta-hg@39
   132
franta-hg@39
   133
	bufferevent_setcb(bev, conn_read_cb, conn_write_cb, conn_event_cb, (void*) connectionId);
franta-hg@30
   134
	bufferevent_enable(bev, EV_READ | EV_WRITE);
franta-hg@28
   135
franta-hg@40
   136
	printf("%4d %8s somebody has connected: socketId = %d → connectionId = %d\n", *connectionId, "CONN", listener, *connectionId);
franta-hg@40
   137
	print_socket_info(*connectionId, fd);
franta-hg@28
   138
franta-hg@42
   139
	bufferevent_write(bev, HELLO_MESSAGE.c_str(), strlen(HELLO_MESSAGE.c_str()));
franta-hg@28
   140
}
franta-hg@28
   141
franta-hg@30
   142
static void conn_read_cb(struct bufferevent *bev, void *user_data) {
franta-hg@40
   143
	int connectionId = *((int*) user_data);
franta-hg@34
   144
franta-hg@30
   145
	/* This callback is invoked when there is data to read on bev. */
franta-hg@30
   146
	struct evbuffer *input = bufferevent_get_input(bev);
franta-hg@30
   147
	struct evbuffer *output = bufferevent_get_output(bev);
franta-hg@33
   148
	struct event_base *base = bufferevent_get_base(bev);
franta-hg@30
   149
franta-hg@30
   150
	size_t len = evbuffer_get_length(input);
franta-hg@43
   151
	char *data = (char*) malloc(len + 1);
franta-hg@43
   152
	data[len] = 0; // as a string must be terminated by \0 
franta-hg@30
   153
	evbuffer_copyout(input, data, len);
franta-hg@31
   154
franta-hg@43
   155
	//TODO: there might already be \0 in the byte stream, only the part before the first \0 will be formatted as a string
franta-hg@40
   156
	string dataFormated(data);
franta-hg@40
   157
	dataFormated = regex_replace(dataFormated, regex("\\n"), "\\n");
franta-hg@40
   158
	printf("%4d %8s '%s'\n", connectionId, "IN", dataFormated.c_str());
franta-hg@40
   159
franta-hg@40
   160
	if (COMMAND_EXIT.compare(data) == 0) {
franta-hg@30
   161
		struct timeval delay = {2, 123};
franta-hg@40
   162
		printf("%4d %8s client asks us to terminate; finishing in %ld sesonds and %ld microseconds\n", connectionId, "EXIT", delay.tv_sec, delay.tv_usec);
franta-hg@30
   163
		event_base_loopexit(base, &delay);
franta-hg@30
   164
	}
franta-hg@31
   165
franta-hg@32
   166
	evbuffer_add(output, "echo: ", 6);
franta-hg@30
   167
	/* Copy all the data from the input buffer to the output buffer. */
franta-hg@30
   168
	evbuffer_add_buffer(output, input);
franta-hg@30
   169
	free(data);
franta-hg@30
   170
}
franta-hg@30
   171
franta-hg@28
   172
static void conn_write_cb(struct bufferevent *bev, void *user_data) {
franta-hg@40
   173
	int connectionId = *((int*) user_data);
franta-hg@34
   174
franta-hg@28
   175
	struct evbuffer *output = bufferevent_get_output(bev);
franta-hg@28
   176
	if (evbuffer_get_length(output) == 0) {
franta-hg@40
   177
		printf("%4d %8s\n", connectionId, "FLUSH");
franta-hg@31
   178
		/* nebudeme ukončovat spojení
franta-hg@28
   179
		bufferevent_free(bev);
franta-hg@31
   180
		 */
franta-hg@40
   181
	} else {
franta-hg@40
   182
		// FIXME: sem to nikdy nepřijde
franta-hg@40
   183
		size_t len = evbuffer_get_length(output);
franta-hg@40
   184
		char *data = (char*) malloc(len);
franta-hg@40
   185
		evbuffer_copyout(output, data, len);
franta-hg@40
   186
		printf("%4d %8s '%s'\n", connectionId, "OUT", data);
franta-hg@40
   187
		free(data);
franta-hg@28
   188
	}
franta-hg@28
   189
}
franta-hg@28
   190
franta-hg@28
   191
static void conn_event_cb(struct bufferevent *bev, short events, void *user_data) {
franta-hg@39
   192
	int connectionId = *((int*) user_data);
franta-hg@38
   193
franta-hg@28
   194
	if (events & BEV_EVENT_EOF) {
franta-hg@40
   195
		printf("%4d %8s\n", connectionId, "CLOSE");
franta-hg@28
   196
	} else if (events & BEV_EVENT_ERROR) {
franta-hg@40
   197
		printf("%4d %8s %s\n", connectionId, "ERROR", strerror(errno));
franta-hg@40
   198
	} else {
franta-hg@40
   199
		printf("%4d %8s\n", connectionId, "OTHER");
franta-hg@40
   200
		// None of the other events can happen here, since we haven't enabled timeouts
franta-hg@28
   201
	}
franta-hg@29
   202
franta-hg@28
   203
	bufferevent_free(bev);
franta-hg@39
   204
	free(user_data);
franta-hg@28
   205
}
franta-hg@28
   206
franta-hg@28
   207
static void signal_cb(evutil_socket_t sig, short events, void *user_data) {
franta-hg@28
   208
	struct event_base *base = (event_base *) user_data;
franta-hg@28
   209
	struct timeval delay = {2, 123};
franta-hg@28
   210
franta-hg@40
   211
	printf("\n%4s %8s got SIGINT (Ctrl+C); finishing in %ld sesonds and %ld microseconds\n", "*", "SIGNAL", delay.tv_sec, delay.tv_usec);
franta-hg@28
   212
franta-hg@28
   213
	event_base_loopexit(base, &delay);
franta-hg@28
   214
}
franta-hg@38
   215
franta-hg@40
   216
static void print_socket_info(int connectionId, int fd) {
franta-hg@38
   217
	struct ucred cr;
franta-hg@38
   218
	unsigned int cl = sizeof (cr);
franta-hg@38
   219
franta-hg@38
   220
	if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl) == 0) {
franta-hg@40
   221
		printf("%4d %8s client identification: pid=%d, uid=%d, gid=%d\n", connectionId, "CONN", cr.pid, cr.uid, cr.gid);
franta-hg@38
   222
	}
franta-hg@38
   223
}