tinywebd

view src/main.c @ 0:9c9e24956d99

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 14 Apr 2015 02:13:29 +0300
parents
children f425a9805d17
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <sys/select.h>
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <arpa/inet.h>
12 struct client {
13 int s;
14 char *rcvbuf;
15 int bufsz;
16 struct client *next;
17 };
19 int start_server(void);
20 int accept_conn(int lis);
21 int handle_client(struct client *c);
23 static int lis;
24 static int port = 8080;
25 static struct client *clist;
27 int main(int argc, char **argv)
28 {
29 if((lis = start_server()) == -1) {
30 return 1;
31 }
33 for(;;) {
34 struct client *c, dummy;
35 int maxfd = lis;
36 fd_set rdset;
38 FD_ZERO(&rdset);
39 FD_SET(lis, &rdset);
41 c = clist;
42 while(c) {
43 if(c->s > maxfd) {
44 maxfd = c->s;
45 }
46 FD_SET(c->s, &rdset);
47 c = c->next;
48 }
50 while(select(maxfd + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR);
52 c = clist;
53 while(c) {
54 if(FD_ISSET(c->s, &rdset)) {
55 handle_client(c);
56 }
57 c = c->next;
58 }
60 if(FD_ISSET(lis, &rdset)) {
61 accept_conn(lis);
62 }
64 dummy.next = clist;
65 c = &dummy;
67 while(c->next) {
68 struct client *n = c->next;
70 if(n->s == -1) {
71 /* marked for removal */
72 c->next = n->next;
73 free(n);
74 } else {
75 c = c->next;
76 }
77 }
78 }
79 }
81 int start_server(void)
82 {
83 int s;
84 struct sockaddr_in sa;
86 if((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
87 perror("failed to create listening socket");
88 return -1;
89 }
90 fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
92 memset(&sa, 0, sizeof sa);
93 sa.sin_family = AF_INET;
94 sa.sin_addr.s_addr = INADDR_ANY;
95 sa.sin_port = htons(port);
97 if(bind(s, (struct sockaddr*)&sa, sizeof sa) == -1) {
98 fprintf(stderr, "failed to bind socket to port %d: %s\n", port, strerror(errno));
99 return -1;
100 }
101 listen(s, 16);
103 return s;
104 }
106 int accept_conn(int lis)
107 {
108 int s;
109 struct client *c;
110 struct sockaddr_in addr;
111 socklen_t addr_sz = sizeof addr;
113 if((s = accept(lis, (struct sockaddr*)&addr, &addr_sz)) == -1) {
114 perror("failed to accept incoming connection");
115 return -1;
116 }
117 fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
119 if(!(c = malloc(sizeof *c))) {
120 perror("failed to allocate memory while accepting connection");
121 return -1;
122 }
123 c->s = s;
124 c->rcvbuf = 0;
125 c->bufsz = 0;
126 c->next = clist;
127 clist = c;
128 return 0;
129 }
131 int handle_client(struct client *c)
132 {
133 static char buf[2048];
134 int rdsz;
136 while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) {
137 if(c->rcvbuf) {
138 int newsz = c->bufsz + rdsz;
139 char *newbuf = realloc(buf, newsz);
140 if(!newbuf) {
141 fprintf(stderr, "failed to allocate %d byte buffer\n", newsz);
142 /* TODO http error */
143 goto drop;
144 }
146 memcpy(newbuf + c->bufsz, buf, rdsz);
148 c->rcvbuf = newbuf;
149 c->bufsz = newsz;
150 }
151 }
153 /* TODO: parse header, drop on invalid, determine if the request
154 * is complete and process
155 */
157 if(rdsz == -1 && errno != EAGAIN) {
158 drop:
159 close(c->s);
160 c->s = -1;
161 free(c->rcvbuf);
162 }
163 return 0;
164 }