1 #include <sys/types.h>
2 #include <sys/event.h>
3 #include <sys/ioctl.h>
4 #include <sys/socket.h>
5 #include <sys/sysctl.h>
6 #include <sys/time.h>
7 #include <sys/usched.h>
8
9 #include <arpa/inet.h>
10 #include <netinet/in.h>
11
12 #include <err.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17
18 #define EVENT_MAX 128
19
20 static void mainloop(int, const struct sockaddr_in *, int);
21
22 static void
usage(const char * cmd)23 usage(const char *cmd)
24 {
25 fprintf(stderr, "%s -p port [-4 inet4] [-i n_instance] [-r] [-B]\n",
26 cmd);
27 exit(1);
28 }
29
30 static int
create_socket(const struct sockaddr_in * in,int reuseport)31 create_socket(const struct sockaddr_in *in, int reuseport)
32 {
33 int serv_s, on;
34
35 serv_s = socket(AF_INET, SOCK_STREAM, 0);
36 if (serv_s < 0)
37 err(1, "socket failed");
38
39 on = 1;
40 if (!reuseport) {
41 if (setsockopt(serv_s, SOL_SOCKET, SO_REUSEADDR,
42 &on, sizeof(on)) < 0)
43 err(1, "setsockopt(REUSEADDR) failed");
44 } else {
45 if (setsockopt(serv_s, SOL_SOCKET, SO_REUSEPORT,
46 &on, sizeof(on)) < 0)
47 err(1, "setsockopt(REUSEPORT) failed");
48 }
49
50 on = 1;
51 if (ioctl(serv_s, FIONBIO, &on, sizeof(on)) < 0)
52 err(1, "ioctl(FIONBIO) failed");
53
54 if (bind(serv_s, (const struct sockaddr *)in, sizeof(*in)) < 0)
55 err(1, "bind failed");
56
57 if (listen(serv_s, -1) < 0)
58 err(1, "listen failed");
59 return serv_s;
60 }
61
62 int
main(int argc,char * argv[])63 main(int argc, char *argv[])
64 {
65 struct sockaddr_in in;
66 int opt, ninst, serv_s, i, reuseport, bindcpu;
67 size_t prm_len;
68
69 prm_len = sizeof(ninst);
70 if (sysctlbyname("hw.ncpu", &ninst, &prm_len, NULL, 0) != 0)
71 err(2, "sysctl hw.ncpu failed");
72
73 memset(&in, 0, sizeof(in));
74 in.sin_family = AF_INET;
75 in.sin_addr.s_addr = INADDR_ANY;
76
77 bindcpu = 0;
78 reuseport = 0;
79
80 while ((opt = getopt(argc, argv, "4:Bp:i:r")) != -1) {
81 switch (opt) {
82 case '4':
83 if (inet_pton(AF_INET, optarg, &in.sin_addr) <= 0) {
84 fprintf(stderr, "invalid -4 option: %s\n",
85 optarg);
86 usage(argv[0]);
87 }
88 break;
89
90 case 'p':
91 in.sin_port = htons(atoi(optarg));
92 break;
93
94 case 'i':
95 ninst = atoi(optarg);
96 break;
97
98 case 'r':
99 reuseport = 1;
100 break;
101
102 case 'B':
103 bindcpu = 1;
104 break;
105
106 default:
107 usage(argv[0]);
108 }
109 }
110
111 if (ninst < 1 || in.sin_port == 0)
112 usage(argv[0]);
113 if (!reuseport)
114 bindcpu = 0;
115
116 serv_s = -1;
117 if (!reuseport)
118 serv_s = create_socket(&in, 0);
119
120 for (i = 1; i < ninst; ++i) {
121 pid_t pid;
122
123 pid = fork();
124 if (pid == 0) {
125 mainloop(serv_s, &in, bindcpu);
126 exit(0);
127 } else if (pid < 0) {
128 err(1, "fork failed");
129 }
130 }
131
132 mainloop(serv_s, &in, bindcpu);
133 exit(0);
134 }
135
136 static void
mainloop(int serv_s,const struct sockaddr_in * in,int bindcpu)137 mainloop(int serv_s, const struct sockaddr_in *in, int bindcpu)
138 {
139 struct kevent change_evt0[EVENT_MAX];
140 int kq, nchange;
141
142 if (serv_s < 0)
143 serv_s = create_socket(in, 1);
144
145 if (bindcpu) {
146 socklen_t cpu_len;
147 int cpu;
148
149 cpu_len = sizeof(cpu);
150 if (getsockopt(serv_s, SOL_SOCKET, SO_CPUHINT,
151 &cpu, &cpu_len) < 0)
152 err(1, "getsockopt(CPUHINT) failed");
153 if (cpu >= 0)
154 usched_set(getpid(), USCHED_SET_CPU, &cpu, sizeof(cpu));
155 }
156
157 kq = kqueue();
158 if (kq < 0)
159 err(1, "kqueue failed");
160
161 EV_SET(&change_evt0[0], serv_s, EVFILT_READ, EV_ADD, 0, 0, NULL);
162 nchange = 1;
163
164 for (;;) {
165 const struct kevent *change_evt = NULL;
166 struct kevent evt[EVENT_MAX];
167 int i, nevt;
168
169 if (nchange > 0)
170 change_evt = change_evt0;
171
172 nevt = kevent(kq, change_evt, nchange, evt, EVENT_MAX, NULL);
173 if (nevt < 0)
174 err(1, "kevent failed");
175 nchange = 0;
176
177 for (i = 0; i < nevt; ++i) {
178 if (evt[i].ident == (u_int)serv_s) {
179 while (nchange < EVENT_MAX) {
180 int s;
181
182 s = accept(serv_s, NULL, NULL);
183 if (s < 0)
184 break;
185 EV_SET(&change_evt0[nchange], s,
186 EVFILT_READ, EV_ADD, 0, 0, NULL);
187 ++nchange;
188 }
189 } else {
190 close(evt[i].ident);
191 }
192 }
193 }
194 /* NEVER REACHED */
195 }
196