]>
diplodocus.org Git - nmh/blob - test/server.c
1 /* server.c - Utilities for fake servers used by the nmh test suite
3 * This code is Copyright (c) 2014, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
14 #include <sys/socket.h>
15 #include <netinet/in.h>
16 #include <sys/types.h>
17 #include <sys/select.h>
22 static const char *PIDFN
= NULL
;
24 static void killpidfile(void);
25 static void handleterm(int);
32 try_bind(int socket
, const struct sockaddr
*address
, socklen_t len
)
35 for (i
= 0; i
< 5; i
++) {
36 if ((status
= bind(socket
, address
, len
)) == 0) {
46 serve(const char *pidfn
, const char *port
)
48 struct addrinfo hints
, *res
;
59 * If there is a pid file already around, kill the previously running
60 * fakesmtp process. Hopefully this will reduce the race conditions
61 * that crop up when running the test suite.
64 if (stat(pidfn
, &st
) == 0) {
67 if (!(pid
= fopen(pidfn
, "r"))) {
68 fprintf(stderr
, "Cannot open %s (%s), continuing ...\n",
69 pidfn
, strerror(errno
));
71 rc
= fscanf(pid
, "%ld", &oldpid
);
75 fprintf(stderr
, "Unable to parse pid in %s,"
79 kill((pid_t
) oldpid
, SIGTERM
);
86 memset(&hints
, 0, sizeof(hints
));
88 hints
.ai_family
= PF_INET
;
89 hints
.ai_socktype
= SOCK_STREAM
;
90 hints
.ai_protocol
= IPPROTO_TCP
;
91 hints
.ai_flags
= AI_PASSIVE
;
93 rc
= getaddrinfo("127.0.0.1", port
, &hints
, &res
);
96 fprintf(stderr
, "Unable to resolve localhost/%s: %s\n",
97 port
, gai_strerror(rc
));
101 l
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
104 fprintf(stderr
, "Unable to create listening socket: %s\n",
111 if (setsockopt(l
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) == -1) {
112 fprintf(stderr
, "Unable to set SO_REUSEADDR: %s\n",
117 if (try_bind(l
, res
->ai_addr
, res
->ai_addrlen
) == -1) {
118 fprintf(stderr
, "Unable to bind socket: %s\n", strerror(errno
));
124 if (listen(l
, 1) == -1) {
125 fprintf(stderr
, "Unable to listen on socket: %s\n",
131 * Now we fork() and print out the process ID of our child
132 * for scripts to use. Once we do that, then exit.
139 fprintf(stderr
, "Unable to fork child: %s\n", strerror(errno
));
144 * Close stdin & stdout, otherwise people can
145 * think we're still doing stuff. For now leave stderr
152 /* XXX why? it's never used... */
153 printf("%ld\n", (long) child
);
158 * Now that our socket & files are set up, wait 30 seconds for
159 * a connection. If there isn't one, then exit.
162 if (!(pid
= fopen(pidfn
, "w"))) {
163 fprintf(stderr
, "Cannot open %s: %s\n",
164 pidfn
, strerror(errno
));
168 fprintf(pid
, "%ld\n", (long) getpid());
171 signal(SIGTERM
, handleterm
);
179 rc
= select(l
+ 1, &readfd
, NULL
, NULL
, &tv
);
182 fprintf(stderr
, "select() failed: %s\n", strerror(errno
));
187 * I think if we get a timeout, we should just exit quietly.
195 * Alright, got a connection! Accept it.
198 if ((conn
= accept(l
, NULL
, NULL
)) == -1) {
199 fprintf(stderr
, "Unable to accept connection: %s\n",
210 * Write a line (adding \r\n) to the client on the other end
213 putcrlf(int socket
, char *data
)
217 iov
[0].iov_base
= data
;
218 iov
[0].iov_len
= strlen(data
);
219 iov
[1].iov_base
= "\r\n";
222 /* ECONNRESET just means the client already closed its end */
223 /* MacOS X can also return EPROTOTYPE (!) here sometimes */
224 /* XXX is it useful to log errors here at all? */
225 if (writev(socket
, iov
, 2) < 0 && errno
!= ECONNRESET
&&
226 errno
!= EPROTOTYPE
) {
227 perror ("server writev");
236 handleterm(int signal
)
246 * Get rid of our pid file