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