9 #include <event2/bufferevent.h>
10 #include <event2/buffer.h>
11 #include <event2/listener.h>
12 #include <event2/util.h>
13 #include <event2/event.h>
14 #include <sys/socket.h>
20 static const string HELLO_MESSAGE("Hello, World!\n");
21 static const string SOCKET_PATH("./roura");
22 static const string COMMAND_EXIT("exit\n");
24 static void listener_cb(evutil_socket_t, short, void *);
25 static void conn_read_cb(struct bufferevent *, void *);
26 static void conn_write_cb(struct bufferevent *, void *);
27 static void conn_event_cb(struct bufferevent *, short, void *);
28 static void signal_cb(evutil_socket_t, short, void *);
29 static void print_socket_info(int, int);
31 int main(int argc, char **argv) {
32 printf("%4s %8s\n", "*", "STARTED");
33 struct event_base *base;
34 evutil_socket_t listener;
35 struct event *listener_event;
36 struct event *signal_event;
38 struct sockaddr_un sun;
40 setvbuf(stdout, NULL, _IONBF, 0);
42 base = event_base_new();
44 printf("%4s %8s %s\n", "*", "ERROR", "unable to initialize libevent");
48 memset(&sun, 0, sizeof (sun));
49 sun.sun_family = AF_UNIX;
50 strcpy(sun.sun_path, SOCKET_PATH.c_str());
52 listener = socket(AF_UNIX, SOCK_STREAM, 0);
53 evutil_make_socket_nonblocking(listener);
55 if (bind(listener, (struct sockaddr*) &sun, sizeof (sun)) < 0) {
56 printf("%4s %8s %s: %s\n", "*", "ERROR", "unable to create domain socket:", SOCKET_PATH.c_str());
60 if (listen(listener, 16) < 0) {
61 printf("%4s %8s %s\n", "*", "ERROR", "unable to listen");
65 // identifikátor serverového soketu (v současnosti číslo FD)
66 printf("%4s %8s listening at: sun_path = %s → socketId = %d\n", "*", "SOCKET", sun.sun_path, listener);
68 listener_event = event_new(base, listener, EV_READ | EV_PERSIST, listener_cb, (void*) base);
70 if (!listener_event) {
71 printf("%4s %8s %s\n", "*", "ERROR", "unable to do event_new()");
75 event_add(listener_event, NULL);
77 signal_event = evsignal_new(base, SIGINT, signal_cb, (void *) base);
79 if (!signal_event || event_add(signal_event, NULL) < 0) {
80 printf("%4s %8s %s\n", "*", "ERROR", "unable to create/add a signal event");
84 event_base_dispatch(base);
86 event_free(listener_event);
87 event_free(signal_event);
88 event_base_free(base);
90 // smažeme soket na disku / soubor -- jinak by program příště spadl na bind()
91 // TODO: co když soket někdo přesune a místo něj dá jiný soubor?
92 unlink(SOCKET_PATH.c_str());
94 printf("%4s %8s\n", "*", "FINISHED");
98 static void listener_cb(evutil_socket_t listener, short event, void *user_data) {
99 struct event_base *base = (event_base *) user_data;
100 struct bufferevent *bev;
102 struct sockaddr_storage ss;
103 socklen_t slen = sizeof (ss);
104 int fd = accept(listener, (struct sockaddr*) &ss, &slen);
106 // identifikátor navázaného spojení (v současnosti číslo FD)
107 int * connectionId = (int*) malloc(sizeof (fd));
111 printf("%4d %8s %s\n", *connectionId, "ERROR", "unable to accept()");
113 } else if (fd > FD_SETSIZE) {
114 // FD_SETSIZE = 1024 -- Proč? Co když bude spojení víc?
115 printf("%4d %8s fd (%d) > FD_SETSIZE (%d)\n", *connectionId, "ERROR", fd, FD_SETSIZE);
120 evutil_make_socket_nonblocking(fd);
123 bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
125 printf("%4d %8s %s\n", *connectionId, "ERROR", "constructing bufferevent");
126 event_base_loopbreak(base);
133 bufferevent_setcb(bev, conn_read_cb, conn_write_cb, conn_event_cb, (void*) connectionId);
134 bufferevent_enable(bev, EV_READ | EV_WRITE);
136 printf("%4d %8s somebody has connected: socketId = %d → connectionId = %d\n", *connectionId, "CONN", listener, *connectionId);
137 print_socket_info(*connectionId, fd);
139 bufferevent_write(bev, HELLO_MESSAGE.c_str(), strlen(HELLO_MESSAGE.c_str()));
142 static void conn_read_cb(struct bufferevent *bev, void *user_data) {
143 int connectionId = *((int*) user_data);
145 /* This callback is invoked when there is data to read on bev. */
146 struct evbuffer *input = bufferevent_get_input(bev);
147 struct evbuffer *output = bufferevent_get_output(bev);
148 struct event_base *base = bufferevent_get_base(bev);
150 size_t len = evbuffer_get_length(input);
151 char *data = (char*) malloc(len + 1);
152 data[len] = 0; // as a string must be terminated by \0
153 evbuffer_copyout(input, data, len);
155 //TODO: there might already be \0 in the byte stream, only the part before the first \0 will be formatted as a string
156 string dataFormated(data);
157 dataFormated = regex_replace(dataFormated, regex("\\n"), "\\n");
158 printf("%4d %8s '%s'\n", connectionId, "IN", dataFormated.c_str());
160 if (COMMAND_EXIT.compare(data) == 0) {
161 struct timeval delay = {2, 123};
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);
163 event_base_loopexit(base, &delay);
166 evbuffer_add(output, "echo: ", 6);
167 /* Copy all the data from the input buffer to the output buffer. */
168 evbuffer_add_buffer(output, input);
172 static void conn_write_cb(struct bufferevent *bev, void *user_data) {
173 int connectionId = *((int*) user_data);
175 struct evbuffer *output = bufferevent_get_output(bev);
176 if (evbuffer_get_length(output) == 0) {
177 printf("%4d %8s\n", connectionId, "FLUSH");
178 /* nebudeme ukončovat spojení
179 bufferevent_free(bev);
182 // FIXME: sem to nikdy nepřijde
183 size_t len = evbuffer_get_length(output);
184 char *data = (char*) malloc(len);
185 evbuffer_copyout(output, data, len);
186 printf("%4d %8s '%s'\n", connectionId, "OUT", data);
191 static void conn_event_cb(struct bufferevent *bev, short events, void *user_data) {
192 int connectionId = *((int*) user_data);
194 if (events & BEV_EVENT_EOF) {
195 printf("%4d %8s\n", connectionId, "CLOSE");
196 } else if (events & BEV_EVENT_ERROR) {
197 printf("%4d %8s %s\n", connectionId, "ERROR", strerror(errno));
199 printf("%4d %8s\n", connectionId, "OTHER");
200 // None of the other events can happen here, since we haven't enabled timeouts
203 bufferevent_free(bev);
207 static void signal_cb(evutil_socket_t sig, short events, void *user_data) {
208 struct event_base *base = (event_base *) user_data;
209 struct timeval delay = {2, 123};
211 printf("\n%4s %8s got SIGINT (Ctrl+C); finishing in %ld sesonds and %ld microseconds\n", "*", "SIGNAL", delay.tv_sec, delay.tv_usec);
213 event_base_loopexit(base, &delay);
216 static void print_socket_info(int connectionId, int fd) {
218 unsigned int cl = sizeof (cr);
220 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl) == 0) {
221 printf("%4d %8s client identification: pid=%d, uid=%d, gid=%d\n", connectionId, "CONN", cr.pid, cr.uid, cr.gid);