server.c - quark - quark web server
(HTM) git clone git://git.suckless.org/quark
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) LICENSE
---
server.c (3823B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include <errno.h>
3 #include <pthread.h>
4 #include <stddef.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 #include "connection.h"
9 #include "queue.h"
10 #include "server.h"
11 #include "util.h"
12
13 struct worker_data {
14 int insock;
15 size_t nslots;
16 const struct server *srv;
17 };
18
19 static void *
20 server_worker(void *data)
21 {
22 queue_event *event = NULL;
23 struct connection *connection, *c, *newc;
24 struct worker_data *d = (struct worker_data *)data;
25 int qfd;
26 ssize_t nready;
27 size_t i;
28
29 /* allocate connections */
30 if (!(connection = calloc(d->nslots, sizeof(*connection)))) {
31 die("calloc:");
32 }
33
34 /* create event queue */
35 if ((qfd = queue_create()) < 0) {
36 exit(1);
37 }
38
39 /* add insock to the interest list (with data=NULL) */
40 if (queue_add_fd(qfd, d->insock, QUEUE_EVENT_IN, 1, NULL) < 0) {
41 exit(1);
42 }
43
44 /* allocate event array */
45 if (!(event = reallocarray(event, d->nslots, sizeof(*event)))) {
46 die("reallocarray:");
47 }
48
49 for (;;) {
50 /* wait for new activity */
51 if ((nready = queue_wait(qfd, event, d->nslots)) < 0) {
52 exit(1);
53 }
54
55 /* handle events */
56 for (i = 0; i < (size_t)nready; i++) {
57 c = queue_event_get_data(&event[i]);
58
59 if (queue_event_is_error(&event[i])) {
60 if (c != NULL) {
61 queue_rem_fd(qfd, c->fd);
62 c->res.status = 0;
63 connection_log(c);
64 connection_reset(c);
65 }
66
67 continue;
68 }
69
70 if (c == NULL) {
71 /* add new connection to the interest list */
72 if (!(newc = connection_accept(d->insock,
73 connection,
74 d->nslots))) {
75 /*
76 * the socket is either blocking
77 * or something failed.
78 * In both cases, we just carry on
79 */
80 continue;
81 }
82
83 /*
84 * add event to the interest list
85 * (we want IN, because we start
86 * with receiving the header)
87 */
88 if (queue_add_fd(qfd, newc->fd,
89 QUEUE_EVENT_IN,
90 0, newc) < 0) {
91 /* not much we can do here */
92 continue;
93 }
94 } else {
95 /* serve existing connection */
96 connection_serve(c, d->srv);
97
98 if (c->fd == 0) {
99 /* we are done */
100 memset(c, 0, sizeof(struct connection));
101 continue;
102 }
103
104 /*
105 * rearm the event based on the state
106 * we are "stuck" at
107 */
108 switch(c->state) {
109 case C_RECV_HEADER:
110 if (queue_mod_fd(qfd, c->fd,
111 QUEUE_EVENT_IN,
112 c) < 0) {
113 connection_reset(c);
114 break;
115 }
116 break;
117 case C_SEND_HEADER:
118 case C_SEND_BODY:
119 if (queue_mod_fd(qfd, c->fd,
120 QUEUE_EVENT_OUT,
121 c) < 0) {
122 connection_reset(c);
123 break;
124 }
125 break;
126 default:
127 break;
128 }
129 }
130 }
131 }
132
133 return NULL;
134 }
135
136 void
137 server_init_thread_pool(int insock, size_t nthreads, size_t nslots,
138 const struct server *srv)
139 {
140 pthread_t *thread = NULL;
141 struct worker_data *d = NULL;
142 size_t i;
143
144 /* allocate worker_data structs */
145 if (!(d = reallocarray(d, nthreads, sizeof(*d)))) {
146 die("reallocarray:");
147 }
148 for (i = 0; i < nthreads; i++) {
149 d[i].insock = insock;
150 d[i].nslots = nslots;
151 d[i].srv = srv;
152 }
153
154 /* allocate and initialize thread pool */
155 if (!(thread = reallocarray(thread, nthreads, sizeof(*thread)))) {
156 die("reallocarray:");
157 }
158 for (i = 0; i < nthreads; i++) {
159 if (pthread_create(&thread[i], NULL, server_worker, &d[i]) != 0) {
160 if (errno == EAGAIN) {
161 die("You need to run as root or have "
162 "CAP_SYS_RESOURCE set, or are trying "
163 "to create more threads than the "
164 "system can offer");
165 } else {
166 die("pthread_create:");
167 }
168 }
169 }
170
171 /* wait for threads */
172 for (i = 0; i < nthreads; i++) {
173 if ((errno = pthread_join(thread[i], NULL))) {
174 warn("pthread_join:");
175 }
176 }
177 }