]>
diplodocus.org Git - nmh/blob - test/server.c
2 * server.c - Utilities for fake servers used by the nmh test suite
4 * This code is Copyright (c) 2014, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
15 #include <sys/socket.h>
16 #include <netinet/in.h>
17 #include <sys/types.h>
18 #include <sys/select.h>
23 static const char *PIDFN
= NULL
;
25 static void killpidfile(void);
26 static void handleterm(int);
29 try_bind(int socket
, const struct sockaddr
*address
, socklen_t len
)
32 for (i
= 0; i
< 5; i
++) {
33 if ((status
= bind(socket
, address
, len
)) == 0) {
43 serve(const char *pidfn
, const char *port
)
45 struct addrinfo hints
, *res
;
56 * If there is a pid file already around, kill the previously running
57 * fakesmtp process. Hopefully this will reduce the race conditions
58 * that crop up when running the test suite.
61 if (stat(pidfn
, &st
) == 0) {
64 if (!(pid
= fopen(pidfn
, "r"))) {
65 fprintf(stderr
, "Cannot open %s (%s), continuing ...\n",
66 pidfn
, strerror(errno
));
68 rc
= fscanf(pid
, "%ld", &oldpid
);
72 fprintf(stderr
, "Unable to parse pid in %s,"
76 kill((pid_t
) oldpid
, SIGTERM
);
83 memset(&hints
, 0, sizeof(hints
));
85 hints
.ai_family
= PF_INET
;
86 hints
.ai_socktype
= SOCK_STREAM
;
87 hints
.ai_protocol
= IPPROTO_TCP
;
88 hints
.ai_flags
= AI_PASSIVE
;
90 rc
= getaddrinfo("127.0.0.1", port
, &hints
, &res
);
93 fprintf(stderr
, "Unable to resolve localhost/%s: %s\n",
94 port
, gai_strerror(rc
));
98 l
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
101 fprintf(stderr
, "Unable to create listening socket: %s\n",
108 if (setsockopt(l
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) == -1) {
109 fprintf(stderr
, "Unable to set SO_REUSEADDR: %s\n",
114 if (try_bind(l
, res
->ai_addr
, res
->ai_addrlen
) == -1) {
115 fprintf(stderr
, "Unable to bind socket: %s\n", strerror(errno
));
121 if (listen(l
, 1) == -1) {
122 fprintf(stderr
, "Unable to listen on socket: %s\n",
128 * Now we fork() and print out the process ID of our child
129 * for scripts to use. Once we do that, then exit.
136 fprintf(stderr
, "Unable to fork child: %s\n", strerror(errno
));
141 * Close stdin & stdout, otherwise people can
142 * think we're still doing stuff. For now leave stderr
149 /* XXX why? it's never used... */
150 printf("%ld\n", (long) child
);
155 * Now that our socket & files are set up, wait 30 seconds for
156 * a connection. If there isn't one, then exit.
159 if (!(pid
= fopen(pidfn
, "w"))) {
160 fprintf(stderr
, "Cannot open %s: %s\n",
161 pidfn
, strerror(errno
));
165 fprintf(pid
, "%ld\n", (long) getpid());
168 signal(SIGTERM
, handleterm
);
176 rc
= select(l
+ 1, &readfd
, NULL
, NULL
, &tv
);
179 fprintf(stderr
, "select() failed: %s\n", strerror(errno
));
184 * I think if we get a timeout, we should just exit quietly.
192 * Alright, got a connection! Accept it.
195 if ((conn
= accept(l
, NULL
, NULL
)) == -1) {
196 fprintf(stderr
, "Unable to accept connection: %s\n",
207 * Write a line (adding \r\n) to the client on the other end
210 putcrlf(int socket
, char *data
)
214 iov
[0].iov_base
= data
;
215 iov
[0].iov_len
= strlen(data
);
216 iov
[1].iov_base
= "\r\n";
219 /* ECONNRESET just means the client already closed its end */
220 /* XXX is it useful to log errors here at all? */
221 if (writev(socket
, iov
, 2) < 0 && errno
!= ECONNRESET
) {
231 handleterm(int signal
)
241 * Get rid of our pid file