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 |
} |