c++/domain-socket-bridge/domain-socket-bridge.c
author František Kučera <franta-hg@frantovo.cz>
Sun, 20 Nov 2016 18:22:56 +0100
changeset 39 988b56d4a7b8
parent 38 6ec32ee08feb
child 40 ba40af6cf815
permissions -rw-r--r--
libevent: identifikace serverových soketů a jednotlivých spojení (pomocí čísel FD)
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@28
     6
franta-hg@28
     7
#include <event2/bufferevent.h>
franta-hg@28
     8
#include <event2/buffer.h>
franta-hg@28
     9
#include <event2/listener.h>
franta-hg@28
    10
#include <event2/util.h>
franta-hg@28
    11
#include <event2/event.h>
franta-hg@38
    12
#include <sys/socket.h>
franta-hg@29
    13
#include <sys/un.h>
franta-hg@29
    14
#include <unistd.h>
franta-hg@28
    15
franta-hg@28
    16
static const char MESSAGE[] = "Hello, World!\n";
franta-hg@28
    17
franta-hg@29
    18
static const char PATH[] = "./roura";
franta-hg@28
    19
franta-hg@31
    20
static void listener_cb(evutil_socket_t, short, void *);
franta-hg@30
    21
static void conn_read_cb(struct bufferevent *, void *);
franta-hg@28
    22
static void conn_write_cb(struct bufferevent *, void *);
franta-hg@28
    23
static void conn_event_cb(struct bufferevent *, short, void *);
franta-hg@28
    24
static void signal_cb(evutil_socket_t, short, void *);
franta-hg@38
    25
static void print_socket_info(int);
franta-hg@31
    26
franta-hg@28
    27
int main(int argc, char **argv) {
franta-hg@28
    28
	struct event_base *base;
franta-hg@31
    29
	evutil_socket_t listener;
franta-hg@31
    30
	struct event *listener_event;
franta-hg@28
    31
	struct event *signal_event;
franta-hg@28
    32
franta-hg@29
    33
	struct sockaddr_un sun;
franta-hg@28
    34
franta-hg@31
    35
	setvbuf(stdout, NULL, _IONBF, 0);
franta-hg@31
    36
franta-hg@28
    37
	base = event_base_new();
franta-hg@28
    38
	if (!base) {
franta-hg@28
    39
		fprintf(stderr, "Could not initialize libevent!\n");
franta-hg@28
    40
		return 1;
franta-hg@28
    41
	}
franta-hg@28
    42
franta-hg@29
    43
	memset(&sun, 0, sizeof (sun));
franta-hg@29
    44
	sun.sun_family = AF_UNIX;
franta-hg@29
    45
	strcpy(sun.sun_path, PATH);
franta-hg@28
    46
franta-hg@31
    47
	listener = socket(AF_UNIX, SOCK_STREAM, 0);
franta-hg@31
    48
	evutil_make_socket_nonblocking(listener);
franta-hg@28
    49
franta-hg@31
    50
	if (bind(listener, (struct sockaddr*) &sun, sizeof (sun)) < 0) {
franta-hg@29
    51
		fprintf(stderr, "Could not create domain socket: %s!\n", PATH);
franta-hg@28
    52
		return 1;
franta-hg@28
    53
	}
franta-hg@28
    54
franta-hg@31
    55
	if (listen(listener, 16) < 0) {
franta-hg@31
    56
		fprintf(stderr, "Could not listen\n");
franta-hg@31
    57
		return 1;
franta-hg@31
    58
	}
franta-hg@31
    59
franta-hg@39
    60
	// identifikátor serverového soketu (v současnosti číslo FD)
franta-hg@39
    61
	printf("Nasloucháme: sun_path = %s → socketId = %d\n", sun.sun_path, listener);
franta-hg@39
    62
franta-hg@31
    63
	listener_event = event_new(base, listener, EV_READ | EV_PERSIST, listener_cb, (void*) base);
franta-hg@31
    64
franta-hg@31
    65
	if (!listener_event) {
franta-hg@31
    66
		fprintf(stderr, "Could not do event_new\n");
franta-hg@31
    67
		return 1;
franta-hg@31
    68
	}
franta-hg@31
    69
franta-hg@31
    70
	event_add(listener_event, NULL);
franta-hg@31
    71
franta-hg@28
    72
	signal_event = evsignal_new(base, SIGINT, signal_cb, (void *) base);
franta-hg@28
    73
franta-hg@28
    74
	if (!signal_event || event_add(signal_event, NULL) < 0) {
franta-hg@28
    75
		fprintf(stderr, "Could not create/add a signal event!\n");
franta-hg@28
    76
		return 1;
franta-hg@28
    77
	}
franta-hg@28
    78
franta-hg@28
    79
	event_base_dispatch(base);
franta-hg@28
    80
franta-hg@31
    81
	event_free(listener_event);
franta-hg@28
    82
	event_free(signal_event);
franta-hg@28
    83
	event_base_free(base);
franta-hg@30
    84
franta-hg@32
    85
	// smažeme soket na disku / soubor -- jinak by program příště spadl na bind()
franta-hg@39
    86
	// TODO: co když soket někdo přesune a místo něj dá jiný soubor?
franta-hg@29
    87
	unlink(PATH);
franta-hg@28
    88
franta-hg@28
    89
	printf("done\n");
franta-hg@28
    90
	return 0;
franta-hg@28
    91
}
franta-hg@28
    92
franta-hg@31
    93
static void listener_cb(evutil_socket_t listener, short event, void *user_data) {
franta-hg@28
    94
	struct event_base *base = (event_base *) user_data;
franta-hg@28
    95
	struct bufferevent *bev;
franta-hg@28
    96
franta-hg@31
    97
	struct sockaddr_storage ss;
franta-hg@31
    98
	socklen_t slen = sizeof (ss);
franta-hg@31
    99
	int fd = accept(listener, (struct sockaddr*) &ss, &slen);
franta-hg@31
   100
	if (fd < 0) {
franta-hg@31
   101
		fprintf(stderr, "Unable to accept(): %d", fd);
franta-hg@31
   102
		return;
franta-hg@31
   103
	} else if (fd > FD_SETSIZE) {
franta-hg@38
   104
		// FD_SETSIZE = 1024 -- Proč? Co když bude spojení víc?
franta-hg@39
   105
		fprintf(stderr, "fd (%d) > FD_SETSIZE (%d)", fd, FD_SETSIZE);
franta-hg@31
   106
		close(fd);
franta-hg@31
   107
		return;
franta-hg@31
   108
	}
franta-hg@31
   109
franta-hg@31
   110
	evutil_make_socket_nonblocking(fd);
franta-hg@39
   111
franta-hg@38
   112
	print_socket_info(fd);
franta-hg@31
   113
franta-hg@31
   114
	bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
franta-hg@28
   115
	if (!bev) {
franta-hg@28
   116
		fprintf(stderr, "Error constructing bufferevent!");
franta-hg@28
   117
		event_base_loopbreak(base);
franta-hg@28
   118
		return;
franta-hg@28
   119
	}
franta-hg@39
   120
franta-hg@39
   121
	// identifikátor navázaného spojení (v současnosti číslo FD)
franta-hg@39
   122
	int * connectionId = (int*) malloc(sizeof (fd));
franta-hg@39
   123
	*connectionId = fd;
franta-hg@39
   124
franta-hg@39
   125
franta-hg@39
   126
	bufferevent_setcb(bev, conn_read_cb, conn_write_cb, conn_event_cb, (void*) connectionId);
franta-hg@30
   127
	bufferevent_enable(bev, EV_READ | EV_WRITE);
franta-hg@28
   128
franta-hg@39
   129
	printf("někdo se k nám připojil! socketId = %d → connectionId = %d / bev = %p\n", listener, *connectionId, bev);
franta-hg@28
   130
franta-hg@28
   131
	bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
franta-hg@28
   132
}
franta-hg@28
   133
franta-hg@30
   134
static void conn_read_cb(struct bufferevent *bev, void *user_data) {
franta-hg@39
   135
	printf("conn_read_cb: connectionId = %d\n", *((int*) user_data));
franta-hg@34
   136
franta-hg@30
   137
	/* This callback is invoked when there is data to read on bev. */
franta-hg@30
   138
	struct evbuffer *input = bufferevent_get_input(bev);
franta-hg@30
   139
	struct evbuffer *output = bufferevent_get_output(bev);
franta-hg@33
   140
	struct event_base *base = bufferevent_get_base(bev);
franta-hg@30
   141
franta-hg@30
   142
	size_t len = evbuffer_get_length(input);
franta-hg@32
   143
	char *data = (char*) malloc(len);
franta-hg@30
   144
	evbuffer_copyout(input, data, len);
franta-hg@32
   145
	printf("we got some data: %s\n", data);
franta-hg@31
   146
franta-hg@30
   147
	if (memcmp(data, "exit\n", len) == 0) {
franta-hg@30
   148
		struct timeval delay = {2, 123};
franta-hg@33
   149
		printf("Klient říká, že máme končit; ukončuji program během %ld sekund a %ld mikrosekund.\n", delay.tv_sec, delay.tv_usec);
franta-hg@30
   150
		event_base_loopexit(base, &delay);
franta-hg@30
   151
	}
franta-hg@31
   152
franta-hg@32
   153
	evbuffer_add(output, "echo: ", 6);
franta-hg@30
   154
	/* Copy all the data from the input buffer to the output buffer. */
franta-hg@30
   155
	evbuffer_add_buffer(output, input);
franta-hg@30
   156
	free(data);
franta-hg@30
   157
}
franta-hg@30
   158
franta-hg@28
   159
static void conn_write_cb(struct bufferevent *bev, void *user_data) {
franta-hg@39
   160
	printf("conn_write_cb: connectionId = %d\n", *((int*) user_data));
franta-hg@34
   161
franta-hg@28
   162
	struct evbuffer *output = bufferevent_get_output(bev);
franta-hg@28
   163
	if (evbuffer_get_length(output) == 0) {
franta-hg@28
   164
		printf("flushed answer / %p\n", bev);
franta-hg@31
   165
		/* nebudeme ukončovat spojení
franta-hg@28
   166
		bufferevent_free(bev);
franta-hg@31
   167
		 */
franta-hg@28
   168
	}
franta-hg@28
   169
}
franta-hg@28
   170
franta-hg@28
   171
static void conn_event_cb(struct bufferevent *bev, short events, void *user_data) {
franta-hg@39
   172
	int connectionId = *((int*) user_data);
franta-hg@39
   173
	printf("conn_event_cb: connectionId = %d\n", connectionId);
franta-hg@38
   174
franta-hg@28
   175
	if (events & BEV_EVENT_EOF) {
franta-hg@39
   176
		printf("Connection closed: connectionId = %d\n", connectionId);
franta-hg@28
   177
	} else if (events & BEV_EVENT_ERROR) {
franta-hg@39
   178
		printf("Got an error on the connectionId = %d: %s\n", connectionId, strerror(errno));
franta-hg@28
   179
	}
franta-hg@29
   180
franta-hg@28
   181
	// None of the other events can happen here, since we haven't enabled timeouts
franta-hg@28
   182
	bufferevent_free(bev);
franta-hg@39
   183
	free(user_data);
franta-hg@28
   184
}
franta-hg@28
   185
franta-hg@28
   186
static void signal_cb(evutil_socket_t sig, short events, void *user_data) {
franta-hg@28
   187
	struct event_base *base = (event_base *) user_data;
franta-hg@28
   188
	struct timeval delay = {2, 123};
franta-hg@28
   189
franta-hg@28
   190
	printf("Zachycen SIGINT (Ctrl+C); ukončuji program během %ld sekund a %ld mikrosekund.\n", delay.tv_sec, delay.tv_usec);
franta-hg@28
   191
franta-hg@28
   192
	event_base_loopexit(base, &delay);
franta-hg@28
   193
}
franta-hg@38
   194
franta-hg@38
   195
static void print_socket_info(int fd) {
franta-hg@38
   196
	struct ucred cr;
franta-hg@38
   197
	unsigned int cl = sizeof (cr);
franta-hg@38
   198
franta-hg@38
   199
	if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl) == 0) {
franta-hg@38
   200
		printf("připojený klient: pid=%d, uid=%d, gid=%d\n", cr.pid, cr.uid, cr.gid);
franta-hg@38
   201
	}
franta-hg@38
   202
}