From 76c362fa3be7c65e5c50b9400cbc52a8cc81ed3b Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Thu, 5 Dec 2013 20:36:05 +0100 Subject: tests: Add a simple echo server --- tests/CMakeLists.txt | 2 + tests/echo_srv.c | 330 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 332 insertions(+) create mode 100644 tests/echo_srv.c diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ab55d07..f3f8d40 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,6 +6,8 @@ include_directories( ${CMOCKA_INCLUDE_DIR} ) +add_executable(echo_srv echo_srv.c) + add_cmocka_test(testsuite testsuite.c ${CMOCKA_LIBRARY} ${SWRAP_REQUIRED_LIBRARIES}) if (OSX) set_property( diff --git a/tests/echo_srv.c b/tests/echo_srv.c new file mode 100644 index 0000000..13e2bc3 --- /dev/null +++ b/tests/echo_srv.c @@ -0,0 +1,330 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef PIDFILE +#define PIDFILE "echo_srv.pid" +#endif /* PIDFILE */ + +#define DFL_PORT 7 +#define BACKLOG 5 + +#ifndef BUFSIZE +#define BUFSIZE 4194304 +#endif /* BUFSIZE */ + +struct echo_srv_opts { + int port; + int socktype; + bool daemon; + char *bind; + const char *pidfile; +}; + +static int pidfile(const char *path) +{ + int err; + int fd; + char pid_str[32]; + + fd = open(path, O_RDONLY, 0644); + err = errno; + if (fd != -1) { + close(fd); + return EEXIST; + } else if (err != ENOENT) { + return err; + } + + fd = open(path, O_CREAT | O_WRONLY | O_EXCL, 0644); + err = errno; + if (fd == -1) { + return err; + } + + memset(pid_str, 0, sizeof(pid_str)); + snprintf(pid_str, sizeof(pid_str) -1, "%u\n", (unsigned int) getpid()); + write(fd, pid_str, strlen(pid_str)); + + return 0; +} + +static int become_daemon(struct echo_srv_opts *opts) +{ + int ret; + pid_t child_pid; + int fd; + + if (getppid() == 1) { + return 0; + } + + child_pid = fork(); + if (child_pid == -1) { + ret = errno; + perror("fork"); + return ret; + } else if (child_pid > 0) { + exit(0); + } + + /* If a working directory was defined, go there */ +#ifdef WORKING_DIR + chdir(WORKING_DIR); +#endif + + ret = pidfile(opts->pidfile); + if (ret != 0) { + fprintf(stderr, "Cannot create pidfile %s: %s\n", + opts->pidfile, strerror(ret)); + return ret; + } + + ret = setsid(); + if (ret == -1) { + ret = errno; + perror("setsid"); + return ret; + } + + for (fd = getdtablesize(); fd >= 0; --fd) { + close(fd); + } + + fd = open("/dev/null", O_RDWR); + if (fd == -1) { + ret = errno; + perror("open"); + return ret; + } + dup(fd); + dup(fd); + + umask(0177); + return 0; +} + +/* Returns 0 on success, errno on failure. If successful, + * sock is a ready to use socket */ +static int setup_srv(struct echo_srv_opts *opts, int *_sock) +{ + struct addrinfo hints; + struct addrinfo *res, *ri; + char svc[6]; + int ret; + int sock; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = opts->socktype; + hints.ai_flags = AI_PASSIVE; + + snprintf(svc, sizeof(svc), "%d", opts->port); + + ret = getaddrinfo(opts->bind, svc, &hints, &res); + if (ret != 0) { + return errno; + } + + for (ri = res; ri != NULL; ri = ri->ai_next) { + sock = socket(ri->ai_family, ri->ai_socktype, + ri->ai_protocol); + if (sock == -1) { + ret = errno; + freeaddrinfo(res); + perror("socket"); + return ret; + } + + ret = bind(sock, ri->ai_addr, ri->ai_addrlen); + if (ret == 0) { + break; + } + + close(sock); + } + freeaddrinfo(res); + + if (ri == NULL) { + fprintf(stderr, "Could not bind\n"); + return EFAULT; + } + + if (opts->socktype == SOCK_STREAM) { + ret = listen(sock, BACKLOG); + if (ret == -1) { + ret = errno; + perror("listen"); + return ret; + } + } + + *_sock = sock; + return 0; +} + +static void echo_tcp(int sock) +{ + int client_sock; + struct sockaddr_storage css; + socklen_t addrlen = sizeof(css); + ssize_t bret; + char buf[BUFSIZE]; + + client_sock = accept(sock, (struct sockaddr *) &css, &addrlen); + if (client_sock == -1) { + perror("accept"); + return; + } + + while (1) { + bret = recv(client_sock, buf, BUFSIZE, 0); + if (bret == -1) { + close(client_sock); + perror("recv"); + continue; + } else if (bret == 0) { + break; + } + + bret = send(client_sock, buf, bret, 0); + if (bret == -1) { + close(client_sock); + perror("send"); + continue; + } + } + + close(client_sock); +} + +static void echo_udp(int sock) +{ + struct sockaddr_storage css; + socklen_t addrlen = sizeof(css); + ssize_t bret; + char buf[BUFSIZE]; + + while (1) { + bret = recvfrom(sock, buf, BUFSIZE, 0, + (struct sockaddr *) &css, &addrlen); + if (bret == -1) { + perror("recvfrom"); + continue; + } + + bret = sendto(sock, buf, bret, 0, + (struct sockaddr *) &css, addrlen); + if (bret == -1) { + perror("sendto"); + continue; + } + } +} + +static void echo(int sock, struct echo_srv_opts *opts) +{ + switch (opts->socktype) { + case SOCK_STREAM: + echo_tcp(sock); + return; + case SOCK_DGRAM: + echo_udp(sock); + return; + default: + fprintf(stderr, "Unsupported protocol\n"); + return; + } +} + +int main(int argc, char **argv) +{ + int ret; + int sock; + struct echo_srv_opts opts; + int opt; + int optindex; + static struct option long_options[] = { + {"tcp", no_argument, 0, 't' }, + {"udp", no_argument, 0, 'u' }, + {"bind-addr", required_argument, 0, 'b' }, + {"port", required_argument, 0, 'p' }, + {"daemon", no_argument, 0, 'D' }, + {"pid", required_argument, 0, 0 }, + {0, 0, 0, 0 } + }; + + opts.port = DFL_PORT; + opts.socktype = SOCK_STREAM; + opts.bind = NULL; + opts.pidfile = PIDFILE; + opts.daemon = false; + + while ((opt = getopt_long(argc, argv, "Dutp:b:", + long_options, &optindex)) != -1) { + switch (opt) { + case 0: + if (optindex == 5) { + opts.pidfile = optarg; + } + break; + case 'p': + opts.port = atoi(optarg); + break; + case 'b': + opts.bind = optarg; + break; + case 'u': + opts.socktype = SOCK_DGRAM; + break; + case 't': + opts.socktype = SOCK_STREAM; + break; + case 'D': + opts.daemon = true; + break; + default: /* '?' */ + fprintf(stderr, "Usage: %s [-p port] [-u] [-t] [--pid pidfile]\n", + argv[0]); + ret = 1; + goto done; + } + } + + if (opts.daemon) { + ret = become_daemon(&opts); + if (ret != 0) { + fprintf(stderr, "Cannot become daemon: %s\n", strerror(ret)); + goto done; + } + } + + ret = setup_srv(&opts, &sock); + if (ret != 0) { + fprintf(stderr, "Cannot setup server: %s\n", strerror(ret)); + goto done; + } + + echo(sock, &opts); + close(sock); + + if (opts.daemon) { + unlink(opts.pidfile); + } + + ret = 0; +done: + return ret; +} -- cgit