]>
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);
33 try_bind(int socket
, const struct sockaddr
*address
, socklen_t len
)
36 for (i
= 0; i
< 5; i
++) {
37 if ((status
= bind(socket
, address
, len
)) == 0) {
47 serve(const char *pidfn
, const char *port
)
49 struct addrinfo hints
, *res
;
60 * If there is a pid file already around, kill the previously running
61 * fakesmtp process. Hopefully this will reduce the race conditions
62 * that crop up when running the test suite.
65 if (stat(pidfn
, &st
) == 0) {
68 if (!(pid
= fopen(pidfn
, "r"))) {
69 fprintf(stderr
, "Cannot open %s (%s), continuing ...\n",
70 pidfn
, strerror(errno
));
72 rc
= fscanf(pid
, "%ld", &oldpid
);
76 fprintf(stderr
, "Unable to parse pid in %s,"
80 kill((pid_t
) oldpid
, SIGTERM
);
87 memset(&hints
, 0, sizeof(hints
));
89 hints
.ai_family
= PF_INET
;
90 hints
.ai_socktype
= SOCK_STREAM
;
91 hints
.ai_protocol
= IPPROTO_TCP
;
92 hints
.ai_flags
= AI_PASSIVE
;
94 rc
= getaddrinfo("127.0.0.1", port
, &hints
, &res
);
97 fprintf(stderr
, "Unable to resolve localhost/%s: %s\n",
98 port
, gai_strerror(rc
));
102 l
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
105 fprintf(stderr
, "Unable to create listening socket: %s\n",
112 if (setsockopt(l
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) == -1) {
113 fprintf(stderr
, "Unable to set SO_REUSEADDR: %s\n",
118 if (try_bind(l
, res
->ai_addr
, res
->ai_addrlen
) == -1) {
119 fprintf(stderr
, "Unable to bind socket: %s\n", strerror(errno
));
125 if (listen(l
, 1) == -1) {
126 fprintf(stderr
, "Unable to listen on socket: %s\n",
132 * Now we fork() and print out the process ID of our child
133 * for scripts to use. Once we do that, then exit.
140 fprintf(stderr
, "Unable to fork child: %s\n", strerror(errno
));
145 * Close stdin & stdout, otherwise people can
146 * think we're still doing stuff. For now leave stderr
153 /* XXX why? it's never used... */
154 printf("%ld\n", (long) child
);
159 * Now that our socket & files are set up, wait 30 seconds for
160 * a connection. If there isn't one, then exit.
163 if (!(pid
= fopen(pidfn
, "w"))) {
164 fprintf(stderr
, "Cannot open %s: %s\n",
165 pidfn
, strerror(errno
));
169 fprintf(pid
, "%ld\n", (long) getpid());
172 signal(SIGTERM
, handleterm
);
180 rc
= select(l
+ 1, &readfd
, NULL
, NULL
, &tv
);
183 fprintf(stderr
, "select() failed: %s\n", strerror(errno
));
188 * I think if we get a timeout, we should just exit quietly.
196 * Alright, got a connection! Accept it.
199 if ((conn
= accept(l
, NULL
, NULL
)) == -1) {
200 fprintf(stderr
, "Unable to accept connection: %s\n",
211 * Write a line (adding \r\n) to the client on the other end
214 putcrlf(int socket
, char *data
)
218 iov
[0].iov_base
= data
;
219 iov
[0].iov_len
= strlen(data
);
220 iov
[1].iov_base
= "\r\n";
223 /* ECONNRESET just means the client already closed its end */
224 /* MacOS X can also return EPROTOTYPE (!) here sometimes */
225 /* XXX is it useful to log errors here at all? */
226 if (writev(socket
, iov
, 2) < 0 && errno
!= ECONNRESET
&&
227 errno
!= EPROTOTYPE
) {
228 perror ("server writev");
237 handleterm(int signal
)
247 * Get rid of our pid file