blob: b535c0760264fc65f50e743bfbaa00711a6e4f32 [file] [log] [blame]
Willy Tarreau616c1cf2019-09-05 09:18:47 +02001#define _GNU_SOURCE // for POLLRDHUP
2#include <sys/socket.h>
3#include <sys/stat.h>
4#include <sys/types.h>
5#include <netinet/in.h>
6#include <netinet/tcp.h>
7#include <errno.h>
8#include <fcntl.h>
9#include <poll.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
14
15/* for OSes which don't have it */
16#ifndef POLLRDHUP
17#define POLLRDHUP 0
18#endif
19
20int verbose = 0;
21int cmd = 0;
22int cmdstep = 0;
23int zero = 0;
24int one = 1;
25int lfd = -1;
26int cfd = -1;
27int sfd = -1;
28struct sockaddr_in saddr, caddr;
29socklen_t salen, calen;
30
31void usage(const char *arg0)
32{
33 printf("Usage: %s [ arg [<action>[,...]] ] ...\n"
34 "args:\n"
35 " -h display this help\n"
36 " -v verbose mode (shows ret values)\n"
37 " -c <actions> perform <action> on client side socket\n"
38 " -s <actions> perform <action> on server side socket\n"
39 " -l <actions> perform <action> on listening socket\n"
40 "\n"
41 "actions for -c/-s/-l (multiple may be delimited by commas) :\n"
42 " acc accept on listener, implicit before first -s\n"
43 " snd send a few bytes of data\n"
44 " mor send a few bytes of data with MSG_MORE\n"
45 " rcv receive a few bytes of data\n"
46 " drn drain: receive till zero\n"
47 " shr SHUT_RD : shutdown read side\n"
48 " shw SHUT_WR : shutdown write side\n"
49 " shb SHUT_RDWR : shutdown both sides\n"
50 " lin disable lingering on the socket\n"
51 " clo close the file descriptor\n"
52 " pol poll() for any event\n"
53 "\n", arg0);
54}
55
56void die(const char *msg)
57{
58 if (msg)
59 fprintf(stderr, "%s\n", msg);
60 exit(1);
61}
62
63const char *get_errno(int ret)
64{
65 static char errmsg[100];
66
67 if (ret >= 0)
68 return "";
69
70 snprintf(errmsg, sizeof(errmsg), " (%s)", strerror(errno));
71 return errmsg;
72}
73
74void do_acc(int fd)
75{
76 int ret;
77
78 calen = sizeof(caddr);
79 ret = accept(lfd, (struct sockaddr*)&caddr, &calen);
80 if (sfd < 0)
81 sfd = ret;
82 if (verbose)
83 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
84}
85
86void do_snd(int fd)
87{
88 int ret;
89
90 ret = send(fd, "foo", 3, MSG_NOSIGNAL|MSG_DONTWAIT);
91 if (verbose)
92 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
93}
94
95void do_mor(int fd)
96{
97 int ret;
98
99 ret = send(fd, "foo", 3, MSG_NOSIGNAL|MSG_DONTWAIT|MSG_MORE);
100 if (verbose)
101 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
102}
103
104void do_rcv(int fd)
105{
106 char buf[10];
107 int ret;
108
109 ret = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
110 if (verbose)
111 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
112}
113
114void do_drn(int fd)
115{
116 char buf[16384];
117 int total = -1;
118 int ret;
119
120 while (1) {
121 ret = recv(fd, buf, sizeof(buf), 0);
122 if (ret <= 0)
123 break;
124 if (total < 0)
125 total = 0;
126 total += ret;
127 }
128
129 if (verbose)
130 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, total, get_errno(ret));
131}
132
133void do_shr(int fd)
134{
135 int ret;
136
137 ret = shutdown(fd, SHUT_RD);
138 if (verbose)
139 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
140}
141
142void do_shw(int fd)
143{
144 int ret;
145
146 ret = shutdown(fd, SHUT_WR);
147 if (verbose)
148 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
149}
150
151void do_shb(int fd)
152{
153 int ret;
154
155 ret = shutdown(fd, SHUT_RDWR);
156 if (verbose)
157 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
158}
159
160void do_lin(int fd)
161{
162 struct linger nolinger = { .l_onoff = 1, .l_linger = 0 };
163 int ret;
164
165 ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, &nolinger, sizeof(nolinger));
166 if (verbose)
167 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
168}
169
170void do_clo(int fd)
171{
172 int ret;
173
174 ret = close(fd);
175 if (verbose)
176 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
177}
178
179void do_pol(int fd)
180{
181 struct pollfd fds = { .fd = fd, .events = POLLIN|POLLOUT|POLLRDHUP, .revents=0 };
182 int ret;
183
184 ret = poll(&fds, 1, 0);
185 if (verbose) {
186 printf("cmd #%d stp #%d: %s(%d): ret=%d%s ev=%#x ", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret), ret > 0 ? fds.revents : 0);
187 if (ret > 0 && fds.revents) {
188 int flags, flag;
189 putchar('(');
190
191 for (flags = fds.revents; flags; flags ^= flag) {
192 flag = flags ^ (flags & (flags - 1)); // keep lowest bit only
193 switch (flag) {
194 case POLLIN: printf("IN"); break;
195 case POLLOUT: printf("OUT"); break;
196 case POLLPRI: printf("PRI"); break;
197 case POLLHUP: printf("HUP"); break;
198 case POLLERR: printf("ERR"); break;
199 case POLLNVAL: printf("NVAL"); break;
200#if POLLRDHUP
201 case POLLRDHUP: printf("RDHUP"); break;
202#endif
203 default: printf("???[%#x]", flag); break;
204 }
205 if (flags ^ flag)
206 putchar(' ');
207 }
208 putchar(')');
209 }
210 putchar('\n');
211 }
212}
213
214int main(int argc, char **argv)
215{
216 const char *arg0;
217 char *word, *next;
218 int fd;
219
220 /* listener */
221 lfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
222 if (lfd < 0)
223 die("socket(l)");
224
225 setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
226
227 memset(&saddr, 0, sizeof(saddr));
228 saddr.sin_family = AF_INET;
229 saddr.sin_port = htons(0);
230 salen = sizeof(saddr);
231
232 if (bind(lfd, (struct sockaddr *)&saddr, salen) < 0)
233 die("bind()");
234
235 if (listen(lfd, 1000) < 0)
236 die("listen()");
237
238 if (getsockname(lfd, (struct sockaddr *)&saddr, &salen) < 0)
239 die("getsockname()");
240
241
242 /* client */
243 cfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
244 if (cfd < 0)
245 die("socket(c)");
246
247 if (connect(cfd, (const struct sockaddr*)&saddr, salen) == -1)
248 die("connect()");
249
250 /* connection is pending in accept queue, accept() will either be
251 * explicit with "-l acc" below, or implicit on "-s <cmd>"
252 */
253
254 arg0 = argv[0];
255 if (argc < 2) {
256 usage(arg0);
257 exit(1);
258 }
259
260 write(1, "#### BEGIN ####\n", 16); // add a visible delimiter in the traces
261
262 while (argc > 1) {
263 argc--; argv++;
264 if (**argv != '-') {
265 usage(arg0);
266 exit(1);
267 }
268
269 fd = -1;
270 switch (argv[0][1]) {
271 case 'h' :
272 usage(arg0);
273 exit(0);
274 break;
275 case 'v' :
276 verbose++;
277 break;
278 case 'c' :
279 cmd++; cmdstep = 0;
280 fd = cfd;
281 break;
282 case 's' :
283 cmd++; cmdstep = 0;
284 if (sfd < 0)
285 do_acc(lfd);
286 if (sfd < 0)
287 die("accept()");
288 fd = sfd;
289 break;
290 case 'l' :
291 cmd++; cmdstep = 0;
292 fd = lfd;
293 break;
294 default : usage(arg0); exit(1); break;
295 }
296
297 if (fd >= 0) { /* an action is required */
298 if (argc < 2) {
299 usage(arg0);
300 exit(1);
301 }
302
303 for (word = argv[1]; word && *word; word = next) {
304 next = strchr(word, ',');
305 if (next)
306 *(next++) = 0;
307 cmdstep++;
308 if (strcmp(word, "acc") == 0) {
309 do_acc(fd);
310 }
311 else if (strcmp(word, "snd") == 0) {
312 do_snd(fd);
313 }
314 else if (strcmp(word, "mor") == 0) {
315 do_mor(fd);
316 }
317 else if (strcmp(word, "rcv") == 0) {
318 do_rcv(fd);
319 }
320 else if (strcmp(word, "drn") == 0) {
321 do_drn(fd);
322 }
323 else if (strcmp(word, "shb") == 0) {
324 do_shb(fd);
325 }
326 else if (strcmp(word, "shr") == 0) {
327 do_shr(fd);
328 }
329 else if (strcmp(word, "shw") == 0) {
330 do_shw(fd);
331 }
332 else if (strcmp(word, "lin") == 0) {
333 do_lin(fd);
334 }
335 else if (strcmp(word, "clo") == 0) {
336 do_clo(fd);
337 }
338 else if (strcmp(word, "pol") == 0) {
339 do_pol(fd);
340 }
341 else {
342 printf("Ignoring unknown action '%s' in step #%d of cmd #%d\n", word, cmdstep, cmd);
343 }
344 }
345 argc--; argv++;
346 }
347 }
348
349 write(1, "#### END ####\n", 14); // add a visible delimiter in the traces
350
351 if (!cmd) {
352 printf("No command was requested!\n");
353 usage(arg0);
354 exit(1);
355 }
356
357 return 0;
358}