/* @! # 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); }