rocksockserver: put NO_LOG ifdef logic into macro
[rofl0r-rocksock.git] / rocksockserver.c
blob0e2412b7443e7589f220d04b5373d8fa614b8f8f
1 /*
3 * author: rofl0r
5 * License: LGPL 2.1+ with static linking exception
8 */
10 #include <string.h>
11 #include <netdb.h>
12 #include <unistd.h>
13 #include <stddef.h>
14 #include <sys/socket.h>
15 #include <sys/select.h>
16 #include <netinet/in.h>
17 #include <time.h>
18 #include <errno.h>
19 #include <limits.h>
20 #include <stdlib.h>
22 #include "rocksockserver.h"
24 #include "endianness.h"
26 #ifndef NO_LOG
27 #include "../lib/include/logger.h"
28 #define LOGP(X) log_perror(X)
29 #else
30 #define LOGP(X) do {} while(0)
31 #endif
33 #endif
34 #include "../lib/include/strlib.h"
35 #include "../lib/include/stringptr.h"
36 #include "../lib/include/timelib.h"
38 typedef struct {
39 char* host;
40 unsigned short port;
41 #ifndef IPV4_ONLY
42 struct addrinfo* hostaddr;
43 #else
44 struct sockaddr_in hostaddr;
45 #endif
46 } rs_hostInfo;
48 int rocksockserver_resolve_host(rs_hostInfo* hostinfo) {
49 if (!hostinfo || !hostinfo->host || !hostinfo->port) return -1;
50 #ifndef IPV4_ONLY
51 char pbuf[8];
52 char* ports;
53 int ret;
54 struct addrinfo hints;
56 memset(&hints, 0, sizeof(hints));
57 hints.ai_family = AF_UNSPEC;
58 hints.ai_socktype = SOCK_STREAM;
59 hints.ai_flags = AI_PASSIVE;
60 if(!(ports = intToString(hostinfo->port, pbuf))) return -1;
62 ret = getaddrinfo(hostinfo->host, ports, &hints, &hostinfo->hostaddr);
63 if(!ret) {
64 return 0;
65 } else {
66 #ifndef NO_LOG
67 log_put(1, VARISL("error resolving: "), VARICC(gai_strerror(ret)), NULL);
68 #endif
69 return ret;
71 #else
72 memset(&hostinfo->hostaddr, 0, sizeof(struct sockaddr_in));
73 ipv4fromstring(hostinfo->host, (unsigned char*) &hostinfo->hostaddr.sin_addr);
74 hostinfo->hostaddr.sin_family = AF_INET;
75 hostinfo->hostaddr.sin_port = htons(hostinfo->port);
76 return 0;
77 #endif
80 int rocksockserver_init(rocksockserver* srv, char* listenip, unsigned short port, void* userdata) {
81 int ret = 0;
82 int yes = 1;
83 rs_hostInfo conn;
84 if(!srv || !listenip || !port) return -1;
85 conn.host = listenip;
86 conn.port = port;
87 FD_ZERO(&srv->master);
88 srv->userdata = userdata;
89 srv->sleeptime_us = 20000; // set a reasonable default value. it's a compromise between throughput and cpu usage basically.
90 ret = rocksockserver_resolve_host(&conn);
91 if(ret) return ret;
92 #ifndef IPV4_ONLY
93 struct addrinfo* p;
94 for(p = conn.hostaddr; p != NULL; p = p->ai_next) {
95 srv->listensocket = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
96 if (srv->listensocket < 0) {
97 continue;
100 // lose the pesky "address already in use" error message
101 setsockopt(srv->listensocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
103 if (bind(srv->listensocket, p->ai_addr, p->ai_addrlen) < 0) {
104 close(srv->listensocket);
105 continue;
108 break;
110 if (!p) {
111 # ifndef NO_LOG
112 log_puts(1, SPLITERAL("selectserver: failed to bind\n"));
113 # endif
114 ret = -1;
116 freeaddrinfo(conn.hostaddr);
117 if(ret == -1) return -1;
118 #else
119 srv->listensocket = socket(AF_INET, SOCK_STREAM, 0);
120 if(srv->listensocket < 0) {
121 LOGP("socket");
122 return -1;
124 setsockopt(srv->listensocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
125 if(bind(srv->listensocket, (struct sockaddr*) &conn.hostaddr, sizeof(struct sockaddr_in)) < 0) {
126 close(srv->listensocket);
127 LOGP("bind");
128 return -1;
131 #endif
132 // listen
133 if (listen(srv->listensocket, 10) == -1) {
134 LOGP("listen");
135 ret = -2;
136 } else {
137 FD_SET(srv->listensocket, &srv->master);
138 srv->maxfd = srv->listensocket;
140 return ret;
143 int rocksockserver_disconnect_client(rocksockserver* srv, int client) {
144 if(client < 0 || client > USER_MAX_FD) return -1;
145 if(FD_ISSET(client, &srv->master)) {
146 close(client);
147 FD_CLR(client, &srv->master);
148 if(client == srv->maxfd)
149 srv->maxfd--;
150 srv->numfds--;
151 return 0;
153 return 1;
156 void rocksockserver_watch_fd(rocksockserver* srv, int newfd) {
157 FD_SET(newfd, &srv->master);
158 if (newfd > srv->maxfd)
159 srv->maxfd = newfd;
162 int rocksockserver_loop(rocksockserver* srv,
163 char* buf, size_t bufsize,
164 int (*on_clientconnect) (void* userdata, struct sockaddr_storage* clientaddr, int fd),
165 int (*on_clientread) (void* userdata, int fd, size_t nread),
166 int (*on_clientwantsdata) (void* userdata, int fd),
167 int (*on_clientdisconnect) (void* userdata, int fd)
169 fd_set read_fds, write_fds;
170 int newfd, k;
171 int lastfd = 3;
172 #ifdef IS_LITTLE_ENDIAN
173 int i;
174 size_t j;
175 #endif
176 ptrdiff_t nbytes;
177 struct sockaddr_storage remoteaddr; // client address
178 socklen_t addrlen;
179 char* fdptr;
180 fd_set* setptr;
182 for(;;) {
184 read_fds = srv->master;
185 write_fds = srv->master;
187 if ((srv->numfds = select(srv->maxfd+1, &read_fds, &write_fds, NULL, NULL)) && srv->numfds == -1)
188 LOGP("select");
190 if(!srv->numfds) continue;
192 // optimization for the case searched_fd = lastfd, when we only have to handle one connection.
193 // i guess that should be the majority of cases.
194 k = lastfd;
195 setptr = &write_fds;
196 if(FD_ISSET(k, setptr)) goto gotcha;
197 setptr = &read_fds;
198 if(FD_ISSET(k, setptr)) goto gotcha;
200 nextfd:
201 setptr = &write_fds;
202 loopstart:
203 fdptr = (char*) setptr;
204 #ifdef IS_LITTLE_ENDIAN
205 for(i = 0; i * CHAR_BIT <= srv->maxfd; i+= sizeof(size_t)) { // we assume that sizeof(fd_set) is a multiple of sizeof(size_t)
206 if( *(size_t*)(fdptr + i)) {
207 for(j = 0; j <= sizeof(size_t); j++) {
208 if(fdptr[i + j]) {
209 for(k = (i + j) * CHAR_BIT; k <= srv->maxfd; k++) {
210 #else
211 for(k = 0; k <= srv->maxfd; k++) {
212 #endif
213 if(FD_ISSET(k, setptr)) {
214 gotcha:
215 srv->numfds--;
216 FD_CLR(k, setptr);
217 if(setptr == &write_fds)
218 goto handlewrite;
219 else
220 goto handleread;
223 #ifdef IS_LITTLE_ENDIAN
229 #endif
231 if(setptr == &write_fds) {
232 setptr = &read_fds;
233 goto loopstart;
234 } else {
235 #ifndef NO_LOG
236 log_puts(2, SPLITERAL("FATAL"));
237 #endif
239 printf("maxfd %d, k %d, numfds %d, set %d\n", srv->maxfd, k, srv->numfds, *(int*)(fdptr));
240 for(k = 0; k < USER_MAX_FD; k++)
241 if(FD_ISSET(k, setptr))
242 printf("bit set: %d\n", k);
244 #ifndef NO_ABORT
245 abort();
246 #else
247 exit(111);
248 #endif
251 handleread:
252 //printf("read_fd %d\n", k);
253 if (k == srv->listensocket) {
254 // new connection available
255 addrlen = sizeof(remoteaddr);
256 newfd = accept(srv->listensocket, (struct sockaddr *)&remoteaddr, &addrlen);
258 if (newfd == -1) {
259 LOGP("accept");
260 } else {
261 if(newfd >= USER_MAX_FD)
262 close(newfd); // only USER_MAX_FD connections can be handled.
263 else {
264 FD_SET(newfd, &srv->master);
265 if (newfd > srv->maxfd)
266 srv->maxfd = newfd;
267 if(on_clientconnect) on_clientconnect(srv->userdata, &remoteaddr, newfd);
270 } else {
271 if(buf && k != srv->signalfd) {
272 if ((nbytes = recv(k, buf, bufsize, 0)) <= 0) {
273 if (nbytes == 0) {
274 if(on_clientdisconnect) on_clientdisconnect(srv->userdata, k);
275 } else {
276 LOGP("recv");
278 rocksockserver_disconnect_client(srv, k);
279 } else {
280 if(on_clientread) on_clientread(srv->userdata, k, nbytes);
282 } else {
284 if(on_clientread) on_clientread(srv->userdata, k, 0);
287 goto zzz;
289 handlewrite:
291 //printf("write_fd %d\n", k);
292 if(on_clientwantsdata) on_clientwantsdata(srv->userdata, k);
294 zzz:
295 if(srv->numfds > 0) goto nextfd;
296 lastfd = k;
297 microsleep(srv->sleeptime_us);
299 return 0;