Simple Server Poll Version Main
Simple Server - Poll() Version Main function
The full source code for this version of Simple Server can be found on my GitHub page
1#include <poll.h>
2#include <sys/select.h>
3#include <sys/signal.h>
4#include <sys/wait.h>
5
6#include <errno.h>
7#include <magic.h>
8#include <signal.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <unistd.h>
12
13#include "./flags/flags.h"
14#include "./sockets/socket.h"
15#include "./client_conn/do_read.h"
16#include "./client_conn/do_write.h"
17#include "client_conn/connections.h"
18#include "debug/debug.h"
19
20#define INITIAL_SIZE 32
21
22/* load global flags variables */
23extern const uint32_t app_flags;
24extern FILE *log_ptr;
25
26static volatile sig_atomic_t running = 1;
27
28static void
29shutdown_server()
30{
31 DBG("Shutdown_server() called!\n");
32 running = 0;
33}
34
35/**
36 * @brief Configure SIGCHLD disposition to auto-reap children
37 *
38 * Sets SIGCHLD to SIG_IGN so no handler function runs on child exit, and
39 * sets the SA_NOCLDWAIT flag, which makes the kernel responsible for reaping
40 * terminated child processes. Because no handler runs, the parent's main
41 * loop is never interrupted by per-child-exit signals, which previously
42 * degraded throughput under load.
43 *
44 * @note Child exit status is discarded (not collected via wait())
45 * @note Exits program on failure to install the disposition
46 */
47static void
48setup_signal_handler()
49{
50 struct sigaction sa = {0};
51 sigemptyset(&sa.sa_mask);
52
53 sa.sa_flags = 0;
54 sa.sa_handler = shutdown_server;
55 if (sigaction(SIGTERM, &sa, NULL) == -1) {
56 perror("sigaction SIGTERM. ");
57 exit(EXIT_FAILURE);
58 }
59 if (sigaction(SIGINT, &sa, NULL) == -1) {
60 perror("sigaction SIGINT. ");
61 exit(EXIT_FAILURE);
62 }
63
64 DBG("Signal handlers setup\n");
65}
66
67/**
68 * @brief Server entry point — initialises subsystems and runs the poll() event
69 * loop.
70 *
71 * Startup sequence:
72 * 1. Install SIGCHLD disposition (auto-reap via SA_NOCLDWAIT).
73 * 2. Parse command-line flags (port, document root, debug mode).
74 * 3. Optionally daemonise (unless -d flag is set).
75 * 4. Open the libmagic MIME-type database.
76 * 5. Create IPv4 and IPv6 listening sockets (both non-blocking).
77 * 6. Allocate the parallel pollfd / Client arrays (INITIAL_SIZE=32 slots,
78 * indices 0-1 reserved for the listeners).
79 *
80 * Event loop:
81 * - poll() blocks indefinitely (timeout = -1).
82 * - EINTR is retried silently; any other poll() error is fatal.
83 * - Listener slots (i < N_LISTENERS=2): POLLIN triggers accept_new_conn().
84 * - Client slots: CLOSING state → close_conn() + index decrement.
85 * - POLLERR / POLLHUP / POLLNVAL → close_conn() + index decrement.
86 * - POLLIN + READING state → do_read().
87 * - POLLOUT + SENDING_HEADER or SENDING_BODY state → do_write().
88 *
89 * @param argc Argument count from the shell
90 * @param argv Argument vector from the shell
91 * @return EXIT_SUCCESS on clean shutdown (currently unreachable);
92 * exits via exit() on fatal errors
93 */
94int
95main(int argc, char *argv[])
96{
97 DBG("Loaded in debug mode\n");
98
99 setup_signal_handler();
100
101 if (setFlags(argc, argv) < 0) {
102 printf("incorrect flags\nWrite some nice message here\n");
103 }
104
105// There is a difference between build DEBUG and terminal display debug...should
106// rename it verbose...
107#ifndef DEBUG
108 if (!(app_flags & V_FLAG)) {
109 /*
110 * We are setting nochdir to -1 so that the daemon runs in the
111 * current working directory. Otherwise entering the correct url
112 * for a page is a pain.
113 *
114 * nullfd is set to 1 to direct stdin, stdout and stderr output
115 * do /dev/null
116 */
117 daemon(-1, 0);
118 }
119#endif
120
121 /* initialize magic */
122 magic_t magic = magic_open(MAGIC_MIME_TYPE);
123 if (magic_load(magic, NULL) != 0) {
124 magic_close(magic);
125
126 return (EXIT_FAILURE);
127 }
128
129 printf("Simple Server %d\n", getpid());
130
131 int fd_size = INITIAL_SIZE;
132 int fd_count = 0;
133
134 struct pollfd *pfds = malloc(sizeof(*pfds) * fd_size);
135 Client *clients = malloc(sizeof(Client) * fd_size);
136
137 if (!pfds || !clients) {
138 perror("malloc");
139 exit(EXIT_FAILURE);
140 }
141
142 int num_listeners = 0;
143 int listener_v4 = -1;
144 int listener_v6 = -1;
145
146 int use_v4 = (app_flags & B4_FLAG) || !(app_flags & B6_FLAG);
147 int use_v6 = (app_flags & B6_FLAG) || !(app_flags & B4_FLAG);
148
149 if (use_v4)
150 listener_v4 = get_listener_v4();
151
152 if (use_v6)
153 listener_v6 = get_listener_v6();
154
155 if (-1 != listener_v4) {
156 pfds[num_listeners].fd = listener_v4;
157 pfds[num_listeners].events = POLLIN;
158 pfds[num_listeners].revents = 0;
159 num_listeners++;
160 }
161
162 if (-1 != listener_v6) {
163 pfds[num_listeners].fd = listener_v6;
164 pfds[num_listeners].events = POLLIN;
165 pfds[num_listeners].revents = 0;
166 num_listeners++;
167 }
168
169 if (num_listeners == 0) {
170 fprintf(stderr, "no listeners could be created; exiting\n");
171 exit(EXIT_FAILURE);
172 }
173
174 fd_count = num_listeners;
175
176 // Let's get this baby spinnin!
177 while (running) {
178 DBG("Entering first for loop\n");
179 int poll_count = poll(pfds, fd_count, -1);
180 if (-1 == poll_count) {
181 if (errno == EINTR) {
182 continue;
183 }
184 perror("poll");
185 exit(EXIT_FAILURE);
186 }
187
188 for (int i = 0; i < fd_count; i++) {
189 if (i < num_listeners) {
190 if (pfds[i].revents & POLLIN) {
191 DBG("New connection found\n");
192
193 accept_new_conn(pfds[i].fd,
194 &pfds,
195 &clients,
196 &fd_count,
197 &fd_size);
198 }
199 continue; // We don't want to treat a listener
200 // as a client!
201 }
202
203 short revents = pfds[i].revents;
204 if (revents == 0) {
205 continue;
206 }
207
208 // Check if error and close if so
209 if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
210 close_conn(i, &fd_count, pfds, clients);
211 i--; // close_con will replace current value of
212 // i, decrement to get the new value
213 continue;
214 }
215
216 if ((revents & POLLIN) &&
217 (READING == clients[i].state)) {
218 DBG("Incoming from client detected\n");
219 // handle read for new request
220 do_read(i, clients, pfds, magic);
221 }
222
223 if (revents & POLLOUT &&
224 ((SENDING_HEADER == clients[i].state) ||
225 (SENDING_BODY == clients[i].state))) {
226 // handle writing
227
228 do_write(i, clients);
229 }
230
231 if (CLOSING == clients[i].state) {
232 DBG("Closing connection\n");
233 close_conn(i, &fd_count, pfds, clients);
234 i--;
235 continue;
236 }
237 }
238
239 if (log_ptr) {
240 fflush(log_ptr);
241 }
242 }
243
244 for (int i = num_listeners; i < fd_count; i++) {
245 close_conn(i, &fd_count, pfds, clients);
246 i--;
247 }
248 close(listener_v4);
249 close(listener_v6);
250
251 magic_close(magic);
252 free(pfds);
253 free(clients);
254
255 if (log_ptr) {
256 fflush(log_ptr);
257 fclose(log_ptr);
258 }
259
260 exit(EXIT_SUCCESS);
261}