/*
@! # TEST TYPE(S): Concurrency, Load stress
@! # TESTCASE DESCRIPTION:
@! # Purpose: to send packets from the file to echo protocol on remote
@! # machine and read the echoing packets back and compare them
@! # Design: Connect to echo protocol on the remote machine
@! # read from the file and send the file to remote machine
@! # read the echoing packets and store them in a file
@! # repeat until file exhausted.
@! # compare result
@! #
@! # SPEC. EXEC. REQS: May require multiple of this test to run
@! # to target machines from multiple machine in order
@! # to create stress condition
@! # echoes <REMOTE HOST> <echofile> <number of processes>
*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <fcntl.h>
#include "test.h"
#include "netdefs.h"
#if INET6
char *TCID = "echoes6";
#else
char *TCID = "echoes";
#endif
int TST_TOTAL = 1;
void echofile(struct servent *, struct addrinfo *, char *, char *);
int checkfile(char *, char *);
void cleanup(int);
int main(int argc, char *argv[], char *env[])
{
unsigned int finish, i, j, k;
int gai, wait_stat;
pid_t pid;
struct addrinfo hints, *hp;
struct servent *sp;
struct {
char resultfile[FILENAME_MAX + 1];
pid_t pid;
} echo_struc[200];
memset(&hints, 0, sizeof(hints));
hints.ai_family = PFI;
hints.ai_socktype = SOCK_STREAM;
if (argc != 4)
tst_brkm(TBROK, NULL, "usage: remote-addr file num-procs");
if ((sp = getservbyname("echo", "tcp")) == NULL)
tst_brkm(TBROK | TERRNO, NULL, "getservbyname failed");
if ((gai = getaddrinfo(argv[1], NULL, &hints, &hp)) != 0)
tst_brkm(TBROK, NULL, "unknown subject address %s: %s\n",
argv[1], gai_strerror(gai));
if (!hp || !hp->ai_addr || hp->ai_addr->sa_family != AFI)
tst_brkm(TBROK, NULL, "getaddrinfo failed");
i = (unsigned int)strtol(argv[3], NULL, 10);
j = 0;
while (i-- > 0) {
switch (pid = fork()) {
case 0:
snprintf(echo_struc[j].resultfile,
FILENAME_MAX, "%s%u", argv[2], j);
echofile(sp, hp, echo_struc[j].resultfile, argv[2]);
break;
case -1:
tst_resm(TBROK | TERRNO, "fork failed");
break;
default:
echo_struc[j].pid = pid;
j++;
break;
}
}
finish = (unsigned int)strtol(argv[3], NULL, 10);
i = finish;
/* Consume all operating threads until we're done... */
while (finish != 0) {
if ((pid = wait(&wait_stat)) == -1)
tst_resm(TFAIL | TERRNO, "wait failed");
if (wait_stat == 0) {
for (j = 0; j < i; j++) {
if (echo_struc[j].pid == pid) {
finish--;
j = i;
}
}
} else {
tst_resm(TFAIL, "wait(2) status was non-zero");
if (WIFEXITED(wait_stat)) {
tst_resm(TINFO, "exit status: %d",
WEXITSTATUS(wait_stat));
} else if (WIFSIGNALED(wait_stat)) {
tst_resm(TINFO, "signaled: %d",
WTERMSIG(wait_stat));
}
for (k = 0; k < i; k++) {
if (kill(echo_struc[k].pid, 0) == 0) {
kill(echo_struc[k].pid, 9);
}
}
}
}
tst_exit();
}
/* XXX (garrcoop): This shouldn't use libltp as it's a forked process. */
void
echofile(struct servent *sp, struct addrinfo *ai, char *resultfile,
char *srcfile)
{
int n;
int port;
char wr_buffer[BUFSIZ];
char rd_buffer[BUFSIZ];
sai_t sa;
#ifdef DEBUG
sa_t address;
socklen_t addrlen;
#endif
int s;
int finish;
int fdw, fdr;
int nread, nwrite;
int count;
pid_t pid;
#ifdef DEBUG
printf("Creating socket .....\n");
#endif
pid = getpid();
if ((s = socket(AFI, SOCK_STREAM, 0)) < 0) {
tst_resm(TBROK, "Failed to create listener socket (pid=%d)",
pid);
cleanup(s);
tst_exit();
}
port = sp->s_port;
/*
* TODO: Old code did something of the form:
*
* struct hostent *hp;
*
* hp = gethostbyname(argv[1]);
*
* ...
*
* struct in_addr hostaddr;
*
* memcpy(&hostaddr,hp->h_addr_list[0],sizeof(struct in_addr));
*
* This is all fine and dandy, but gethostbyname has been deprecated
* for some time, and doesn't work too well with IPV6 (from what I've
* read), so I have to push it over to getaddrinfo. getaddrinfo isn't
* a 1:1 mapping though, so I have to do some work to shoehorn the old
* code to fit the new code.
*
* Some notes (from a test app)...
*
* (gdb) set args 127.0.0.1
* (gdb) list
* 33 for (int i = 1; i < argc; i++) {
* 34
* 35 gai = getaddrinfo(argv[i], NULL, &hints, &ai);
* 36 hp = gethostbyname(argv[i]);
* 37
* 38 if (gai != 0) {
* 39 printf("Error: %s\n", gai_strerror(gai));
* 40 error = 2;
* 41 } else {
* 42 printf("Host IP: 0x%x\n", ai->ai_addr);
* (gdb) p *hp
* $16 = {h_name = 0x1a60198 "127.0.0.1", h_aliases = 0x1a60190, h_addrtype = 2,
* h_length = 4, h_addr_list = 0x1a60180}
* (gdb) p *hp->h_addr_list
* $14 = 0x1a60170 "\177"
* (gdb) p *ai
* $15 = {ai_flags = 0, ai_family = 2, ai_socktype = 1, ai_protocol = 6,
* ai_addrlen = 16, ai_addr = 0x1a600b0, ai_canonname = 0x0,
* ai_next = 0x1a600d0}
*
* If one continues down this path, SIGPIPE will get tossed at the first
* write(2), as opposed to Connection refused (the old code). So I'm not
* passing in the correct info to connect(2).
*
* That and using -DDEBUG with the getpeername(3) call below always fails
* (that alone should be a sufficient to note that my sockaddr* data is
* skewed).
*
* For now let's just mark it broken.
*
*/
//tst_resm(TBROK, "FIX ME GARRETT!");
//tst_exit();
memset((char *)&sa, 0, sizeof(sa));
memcpy(&sa, ai->ai_addr, ai->ai_addrlen);
#if INET6
sa.sin6_port = port;
#else
sa.sin_port = port;
#endif
if (connect(s, (sa_t *) & sa, sizeof(sa)) == -1) {
tst_resm(TBROK | TERRNO,
"failed to create connector socket (pid=%d)", pid);
cleanup(s);
tst_exit();
}
#ifdef DEBUG
addrlen = sizeof(struct sockaddr);
/* printf("addrlen=%d\n", addrlen); */
/* printf("ai->ai_addr=%s\n", inet_ntoa(ai->ai_addr)); */
if (getsockname(s, &address, &addrlen) == -1) {
tst_resm(TBROK | TERRNO, "getsockname call failed (pid=%d)",
pid);
cleanup(s);
tst_exit();
}
printf("local port is: %d\n", port);
if (getpeername(s, &address, &addrlen) == -1) {
tst_resm(TBROK | TERRNO, "getpeername call failed (pid=%d)",
pid);
cleanup(s);
tst_exit();
}
tst_resm(TINFO, "The remote port is: %d\n", port);
#endif
if ((fdr = open(srcfile, O_RDONLY)) < 0) {
tst_resm(TBROK | TERRNO,
"failed to open input file (pid=%d)", pid);
cleanup(s);
tst_exit();
}
if ((fdw = creat(resultfile, 0644)) < 0) {
tst_resm(TBROK | TERRNO,
"failed to create a temporary file (pid=%d)", pid);
cleanup(s);
tst_exit();
}
#if DEBUG
tst_resm(TINFO, "creat(resultfile,...) done.");
#endif
finish = FALSE;
count = 0;
while (finish == FALSE) {
if ((nwrite = read(fdr, wr_buffer, BUFSIZ)) == -1) {
tst_resm(TFAIL | TERRNO,
"failed to read from file (pid=%d)", pid);
cleanup(s);
}
#if DEBUG
tst_resm(TINFO, "Read %d bytes from file", nwrite);
#endif
if (nwrite == 0)
finish = TRUE;
else {
count++;
if ((n = write(s, wr_buffer, nwrite)) != nwrite) {
tst_resm(TFAIL | TERRNO,
"failed to write to socket (pid=%d)",
pid);
cleanup(s);
}
#ifdef DEBUG
tst_resm(TINFO, "Writing %d bytes to remote socket",
count);
#endif
while (nwrite != 0) {
nread = read(s, rd_buffer, BUFSIZ);
if (nread == -1) {
printf("read size: %d\n", n);
tst_resm(TFAIL | TERRNO,
"failed to read from socket [2nd "
"time] (pid=%d)", pid);
cleanup(s);
}
#ifdef DEBUG
printf("Reading ....... %d\n", count);
#endif
n = write(fdw, rd_buffer, nread);
if (n != nread) {
tst_resm(TFAIL | TERRNO,
"ERROR during write to result "
"file (pid=%d); read amount: %d",
pid, n);
cleanup(s);
}
nwrite -= nread;
}
} /* end of else */
} /* end of while */
if ((n = close(s)) == -1) {
tst_brkm(TBROK | TERRNO, NULL,
"failed to cleanly close socket (pid=%d)", pid);
}
if ((n = close(fdr)) == -1) {
tst_brkm(TBROK | TERRNO, NULL,
"failed to cleanly close input file (pid=%d)", pid);
}
if ((n = close(fdw)) == -1) {
tst_brkm(TBROK | TERRNO, NULL,
"failed to cleanly close temp file (pid=%d)", pid);
}
if (checkfile(srcfile, resultfile) != TRUE) {
tst_brkm(TFAIL, NULL,
"Input file and output file are not equal (pid=%d)",
pid);
}
tst_resm(TINFO, "Finish .... (pid=%d)", pid);
tst_exit();
}
int checkfile(char *file1, char *file2)
{
off_t n;
struct stat buffer;
stat(file1, &buffer);
n = buffer.st_size;
#ifdef DEBUG
printf("%s size=%lu\n", file1, n);
#endif
stat(file2, &buffer);
#ifdef DEBUG
printf("%s size=%lu\n", file2, buffer.st_size);
#endif
if (n != buffer.st_size)
return FALSE;
else
return TRUE;
}
void cleanup(int s)
{
close(s);
}