forked from dzeban/c10k
-
Notifications
You must be signed in to change notification settings - Fork 0
/
blocking_forking.c
131 lines (111 loc) · 3.55 KB
/
blocking_forking.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include "config.h"
#include "http_handler.h"
#include "logging.h"
static void sigchld_handler(int sig, siginfo_t *si, void *unused)
{
// Signals are not queued, meaning that SIGCHLD from multiple child
// processes will be discarded. To prevent zombies, we have to call waitpid
// in a loop for all pids
while (waitpid(-1, NULL, WNOHANG) > 0);
}
int main(int argc, const char *argv[])
{
int sock_listen;
int rc = EXIT_SUCCESS;
struct sockaddr_in address, peer_address;
socklen_t peer_address_len;
struct sigaction sa;
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(PORT);
address.sin_addr.s_addr = htonl(INADDR_ANY);
// Setup signal handler to catch SIGCHLD and avoid zombie children
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = sigchld_handler;
if (sigaction(SIGCHLD, &sa, NULL)) {
perror("sigaction");
rc = EXIT_FAILURE;
goto exit_rc;
}
// Make listen socket binded to port 8282
sock_listen = socket(AF_INET, SOCK_STREAM, 0);
if (sock_listen < 0) {
perror("socket");
rc = EXIT_FAILURE;
goto exit_rc;
}
int yes = 1;
if (setsockopt(sock_listen, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
perror("setsockopt");
rc = EXIT_FAILURE;
goto exit_socket;
}
if (bind(sock_listen, (struct sockaddr *)&address, sizeof(address))) {
perror("bind");
rc = EXIT_FAILURE;
goto exit_socket;
}
if (listen(sock_listen, BACKLOG)) {
perror("listen");
rc = EXIT_FAILURE;
goto exit_socket;
}
while (1) {
int sock_client;
pid_t child;
sock_client = accept(sock_listen, (struct sockaddr *)&peer_address, &peer_address_len);
if (sock_client < 0) {
// "accept" is a "slow" system call, if signal handling happened
// while accept was working we'll receive EINTR. And it's ok.
// On Linux it can be automatically restarted by providing
// SA_RESTART to signal handler and NOT specifying SO_RCVTIMEO.
// But we opt to make more portable and restart it by hand.
if (errno != EINTR) {
perror("accept");
}
continue;
}
debug("Accept from %s, sock %d\n", inet_ntoa(peer_address.sin_addr), sock_client);
child = fork();
switch (child) {
case 0: // Child process
if (close(sock_listen)) {
perror("close sock_listen");
}
http_handler_loop(sock_client);
if (close(sock_client)) {
perror("close");
}
debug("About to exit from %d\n", getpid());
exit(EXIT_SUCCESS);
break;
case -1: // Error
perror("fork");
if (close(sock_client)) {
perror("close sock_client on error");
}
break;
default: // Parent
// Parent process
if (close(sock_client)) {
perror("close sock_client in parent");
}
break;
}
}
exit_socket:
close(sock_listen);
exit_rc:
exit(rc);
}