franta-hg@28: #include franta-hg@28: #include franta-hg@28: #include franta-hg@28: #include franta-hg@30: #include franta-hg@40: #include franta-hg@40: #include franta-hg@28: franta-hg@28: #include franta-hg@28: #include franta-hg@28: #include franta-hg@28: #include franta-hg@28: #include franta-hg@38: #include franta-hg@29: #include franta-hg@29: #include franta-hg@28: franta-hg@40: using namespace std; franta-hg@40: franta-hg@42: static const string HELLO_MESSAGE("Hello, World!\n"); franta-hg@42: static const string SOCKET_PATH("./roura"); franta-hg@40: static const string COMMAND_EXIT("exit\n"); franta-hg@40: franta-hg@31: static void listener_cb(evutil_socket_t, short, void *); franta-hg@30: static void conn_read_cb(struct bufferevent *, void *); franta-hg@28: static void conn_write_cb(struct bufferevent *, void *); franta-hg@28: static void conn_event_cb(struct bufferevent *, short, void *); franta-hg@28: static void signal_cb(evutil_socket_t, short, void *); franta-hg@40: static void print_socket_info(int, int); franta-hg@31: franta-hg@28: int main(int argc, char **argv) { franta-hg@40: printf("%4s %8s\n", "*", "STARTED"); franta-hg@28: struct event_base *base; franta-hg@31: evutil_socket_t listener; franta-hg@31: struct event *listener_event; franta-hg@28: struct event *signal_event; franta-hg@28: franta-hg@29: struct sockaddr_un sun; franta-hg@28: franta-hg@31: setvbuf(stdout, NULL, _IONBF, 0); franta-hg@31: franta-hg@28: base = event_base_new(); franta-hg@28: if (!base) { franta-hg@40: printf("%4s %8s %s\n", "*", "ERROR", "unable to initialize libevent"); franta-hg@28: return 1; franta-hg@28: } franta-hg@28: franta-hg@29: memset(&sun, 0, sizeof (sun)); franta-hg@29: sun.sun_family = AF_UNIX; franta-hg@42: strcpy(sun.sun_path, SOCKET_PATH.c_str()); franta-hg@28: franta-hg@31: listener = socket(AF_UNIX, SOCK_STREAM, 0); franta-hg@31: evutil_make_socket_nonblocking(listener); franta-hg@28: franta-hg@31: if (bind(listener, (struct sockaddr*) &sun, sizeof (sun)) < 0) { franta-hg@42: printf("%4s %8s %s: %s\n", "*", "ERROR", "unable to create domain socket:", SOCKET_PATH.c_str()); franta-hg@28: return 1; franta-hg@28: } franta-hg@28: franta-hg@31: if (listen(listener, 16) < 0) { franta-hg@40: printf("%4s %8s %s\n", "*", "ERROR", "unable to listen"); franta-hg@31: return 1; franta-hg@31: } franta-hg@31: franta-hg@39: // identifikátor serverového soketu (v současnosti číslo FD) franta-hg@40: printf("%4s %8s listening at: sun_path = %s → socketId = %d\n", "*", "SOCKET", sun.sun_path, listener); franta-hg@39: franta-hg@31: listener_event = event_new(base, listener, EV_READ | EV_PERSIST, listener_cb, (void*) base); franta-hg@31: franta-hg@31: if (!listener_event) { franta-hg@40: printf("%4s %8s %s\n", "*", "ERROR", "unable to do event_new()"); franta-hg@31: return 1; franta-hg@31: } franta-hg@31: franta-hg@31: event_add(listener_event, NULL); franta-hg@31: franta-hg@28: signal_event = evsignal_new(base, SIGINT, signal_cb, (void *) base); franta-hg@28: franta-hg@28: if (!signal_event || event_add(signal_event, NULL) < 0) { franta-hg@40: printf("%4s %8s %s\n", "*", "ERROR", "unable to create/add a signal event"); franta-hg@28: return 1; franta-hg@28: } franta-hg@28: franta-hg@28: event_base_dispatch(base); franta-hg@28: franta-hg@31: event_free(listener_event); franta-hg@28: event_free(signal_event); franta-hg@28: event_base_free(base); franta-hg@30: franta-hg@32: // smažeme soket na disku / soubor -- jinak by program příště spadl na bind() franta-hg@39: // TODO: co když soket někdo přesune a místo něj dá jiný soubor? franta-hg@42: unlink(SOCKET_PATH.c_str()); franta-hg@28: franta-hg@40: printf("%4s %8s\n", "*", "FINISHED"); franta-hg@28: return 0; franta-hg@28: } franta-hg@28: franta-hg@31: static void listener_cb(evutil_socket_t listener, short event, void *user_data) { franta-hg@28: struct event_base *base = (event_base *) user_data; franta-hg@28: struct bufferevent *bev; franta-hg@28: franta-hg@31: struct sockaddr_storage ss; franta-hg@31: socklen_t slen = sizeof (ss); franta-hg@31: int fd = accept(listener, (struct sockaddr*) &ss, &slen); franta-hg@40: franta-hg@40: // identifikátor navázaného spojení (v současnosti číslo FD) franta-hg@40: int * connectionId = (int*) malloc(sizeof (fd)); franta-hg@40: *connectionId = fd; franta-hg@40: franta-hg@31: if (fd < 0) { franta-hg@40: printf("%4d %8s %s\n", *connectionId, "ERROR", "unable to accept()"); franta-hg@31: return; franta-hg@31: } else if (fd > FD_SETSIZE) { franta-hg@38: // FD_SETSIZE = 1024 -- Proč? Co když bude spojení víc? franta-hg@40: printf("%4d %8s fd (%d) > FD_SETSIZE (%d)\n", *connectionId, "ERROR", fd, FD_SETSIZE); franta-hg@31: close(fd); franta-hg@31: return; franta-hg@31: } franta-hg@31: franta-hg@31: evutil_make_socket_nonblocking(fd); franta-hg@39: franta-hg@31: franta-hg@31: bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); franta-hg@28: if (!bev) { franta-hg@40: printf("%4d %8s %s\n", *connectionId, "ERROR", "constructing bufferevent"); franta-hg@28: event_base_loopbreak(base); franta-hg@28: return; franta-hg@28: } franta-hg@39: franta-hg@40: franta-hg@39: franta-hg@39: franta-hg@39: bufferevent_setcb(bev, conn_read_cb, conn_write_cb, conn_event_cb, (void*) connectionId); franta-hg@30: bufferevent_enable(bev, EV_READ | EV_WRITE); franta-hg@28: franta-hg@40: printf("%4d %8s somebody has connected: socketId = %d → connectionId = %d\n", *connectionId, "CONN", listener, *connectionId); franta-hg@40: print_socket_info(*connectionId, fd); franta-hg@28: franta-hg@42: bufferevent_write(bev, HELLO_MESSAGE.c_str(), strlen(HELLO_MESSAGE.c_str())); franta-hg@28: } franta-hg@28: franta-hg@30: static void conn_read_cb(struct bufferevent *bev, void *user_data) { franta-hg@40: int connectionId = *((int*) user_data); franta-hg@34: franta-hg@30: /* This callback is invoked when there is data to read on bev. */ franta-hg@30: struct evbuffer *input = bufferevent_get_input(bev); franta-hg@30: struct evbuffer *output = bufferevent_get_output(bev); franta-hg@33: struct event_base *base = bufferevent_get_base(bev); franta-hg@30: franta-hg@30: size_t len = evbuffer_get_length(input); franta-hg@43: char *data = (char*) malloc(len + 1); franta-hg@43: data[len] = 0; // as a string must be terminated by \0 franta-hg@30: evbuffer_copyout(input, data, len); franta-hg@31: franta-hg@43: //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: string dataFormated(data); franta-hg@40: dataFormated = regex_replace(dataFormated, regex("\\n"), "\\n"); franta-hg@40: printf("%4d %8s '%s'\n", connectionId, "IN", dataFormated.c_str()); franta-hg@40: franta-hg@40: if (COMMAND_EXIT.compare(data) == 0) { franta-hg@30: struct timeval delay = {2, 123}; franta-hg@40: 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: event_base_loopexit(base, &delay); franta-hg@30: } franta-hg@31: franta-hg@32: evbuffer_add(output, "echo: ", 6); franta-hg@30: /* Copy all the data from the input buffer to the output buffer. */ franta-hg@30: evbuffer_add_buffer(output, input); franta-hg@30: free(data); franta-hg@30: } franta-hg@30: franta-hg@28: static void conn_write_cb(struct bufferevent *bev, void *user_data) { franta-hg@40: int connectionId = *((int*) user_data); franta-hg@34: franta-hg@28: struct evbuffer *output = bufferevent_get_output(bev); franta-hg@28: if (evbuffer_get_length(output) == 0) { franta-hg@40: printf("%4d %8s\n", connectionId, "FLUSH"); franta-hg@31: /* nebudeme ukončovat spojení franta-hg@28: bufferevent_free(bev); franta-hg@31: */ franta-hg@40: } else { franta-hg@40: // FIXME: sem to nikdy nepřijde franta-hg@40: size_t len = evbuffer_get_length(output); franta-hg@40: char *data = (char*) malloc(len); franta-hg@40: evbuffer_copyout(output, data, len); franta-hg@40: printf("%4d %8s '%s'\n", connectionId, "OUT", data); franta-hg@40: free(data); franta-hg@28: } franta-hg@28: } franta-hg@28: franta-hg@28: static void conn_event_cb(struct bufferevent *bev, short events, void *user_data) { franta-hg@39: int connectionId = *((int*) user_data); franta-hg@38: franta-hg@28: if (events & BEV_EVENT_EOF) { franta-hg@40: printf("%4d %8s\n", connectionId, "CLOSE"); franta-hg@28: } else if (events & BEV_EVENT_ERROR) { franta-hg@40: printf("%4d %8s %s\n", connectionId, "ERROR", strerror(errno)); franta-hg@40: } else { franta-hg@40: printf("%4d %8s\n", connectionId, "OTHER"); franta-hg@40: // None of the other events can happen here, since we haven't enabled timeouts franta-hg@28: } franta-hg@29: franta-hg@28: bufferevent_free(bev); franta-hg@39: free(user_data); franta-hg@28: } franta-hg@28: franta-hg@28: static void signal_cb(evutil_socket_t sig, short events, void *user_data) { franta-hg@28: struct event_base *base = (event_base *) user_data; franta-hg@28: struct timeval delay = {2, 123}; franta-hg@28: franta-hg@40: 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: franta-hg@28: event_base_loopexit(base, &delay); franta-hg@28: } franta-hg@38: franta-hg@40: static void print_socket_info(int connectionId, int fd) { franta-hg@38: struct ucred cr; franta-hg@38: unsigned int cl = sizeof (cr); franta-hg@38: franta-hg@38: if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl) == 0) { franta-hg@40: printf("%4d %8s client identification: pid=%d, uid=%d, gid=%d\n", connectionId, "CONN", cr.pid, cr.uid, cr.gid); franta-hg@38: } franta-hg@38: }