Path: blob/main/tools/regression/sockets/sendfile/sendfile.c
103855 views
/*-1* Copyright (c) 2006 Robert N. M. Watson2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/types.h>27#include <sys/socket.h>28#include <sys/stat.h>29#include <sys/wait.h>3031#include <netinet/in.h>3233#include <err.h>34#include <errno.h>35#include <fcntl.h>36#include <limits.h>37#include <md5.h>38#include <signal.h>39#include <stdint.h>40#include <stdio.h>41#include <stdlib.h>42#include <string.h>43#include <unistd.h>4445/*46* Simple regression test for sendfile. Creates a file sized at four pages47* and then proceeds to send it over a series of sockets, exercising a number48* of cases and performing limited validation.49*/5051#define FAIL(msg) {printf("# %s\n", msg); \52return (-1);}5354#define FAIL_ERR(msg) {printf("# %s: %s\n", msg, strerror(errno)); \55return (-1);}5657#define TEST_PORT 567858#define TEST_MAGIC 0x4440f7bb59#define TEST_PAGES 460#define TEST_SECONDS 306162struct test_header {63uint32_t th_magic;64uint32_t th_header_length;65uint32_t th_offset;66uint32_t th_length;67char th_md5[33];68};6970struct sendfile_test {71uint32_t hdr_length;72uint32_t offset;73uint32_t length;74uint32_t file_size;75};7677static int file_fd;78static char path[PATH_MAX];79static int listen_socket;80static int accept_socket;8182static int test_th(struct test_header *th, uint32_t *header_length,83uint32_t *offset, uint32_t *length);84static void signal_alarm(int signum);85static void setup_alarm(int seconds);86static void cancel_alarm(void);87static int receive_test(void);88static void run_child(void);89static int new_test_socket(int *connect_socket);90static void init_th(struct test_header *th, uint32_t header_length,91uint32_t offset, uint32_t length);92static int send_test(int connect_socket, struct sendfile_test);93static int write_test_file(size_t file_size);94static void run_parent(void);95static void cleanup(void);969798static int99test_th(struct test_header *th, uint32_t *header_length, uint32_t *offset,100uint32_t *length)101{102103if (th->th_magic != htonl(TEST_MAGIC))104FAIL("magic number not found in header")105*header_length = ntohl(th->th_header_length);106*offset = ntohl(th->th_offset);107*length = ntohl(th->th_length);108return (0);109}110111static void112signal_alarm(int signum)113{114(void)signum;115116printf("# test timeout\n");117118if (accept_socket > 0)119close(accept_socket);120if (listen_socket > 0)121close(listen_socket);122123_exit(-1);124}125126static void127setup_alarm(int seconds)128{129struct itimerval itv;130bzero(&itv, sizeof(itv));131(void)seconds;132itv.it_value.tv_sec = seconds;133134signal(SIGALRM, signal_alarm);135setitimer(ITIMER_REAL, &itv, NULL);136}137138static void139cancel_alarm(void)140{141struct itimerval itv;142bzero(&itv, sizeof(itv));143setitimer(ITIMER_REAL, &itv, NULL);144}145146static int147receive_test(void)148{149uint32_t header_length, offset, length, counter;150struct test_header th;151ssize_t len;152char buf[10240];153MD5_CTX md5ctx;154char *rxmd5;155156len = read(accept_socket, &th, sizeof(th));157if (len < 0 || (size_t)len < sizeof(th))158FAIL_ERR("read")159160if (test_th(&th, &header_length, &offset, &length) != 0)161return (-1);162163MD5Init(&md5ctx);164165counter = 0;166while (1) {167len = read(accept_socket, buf, sizeof(buf));168if (len < 0 || len == 0)169break;170counter += len;171MD5Update(&md5ctx, buf, len);172}173174rxmd5 = MD5End(&md5ctx, NULL);175176if ((counter != header_length+length) ||177memcmp(th.th_md5, rxmd5, 33) != 0)178FAIL("receive length mismatch")179180free(rxmd5);181return (0);182}183184static void185run_child(void)186{187struct sockaddr_in sin;188int rc = 0;189190listen_socket = socket(PF_INET, SOCK_STREAM, 0);191if (listen_socket < 0) {192printf("# socket: %s\n", strerror(errno));193rc = -1;194}195196if (!rc) {197bzero(&sin, sizeof(sin));198sin.sin_len = sizeof(sin);199sin.sin_family = AF_INET;200sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);201sin.sin_port = htons(TEST_PORT);202203if (bind(listen_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) {204printf("# bind: %s\n", strerror(errno));205rc = -1;206}207}208209if (!rc && listen(listen_socket, -1) < 0) {210printf("# listen: %s\n", strerror(errno));211rc = -1;212}213214if (!rc) {215accept_socket = accept(listen_socket, NULL, NULL);216setup_alarm(TEST_SECONDS);217if (receive_test() != 0)218rc = -1;219}220221cancel_alarm();222if (accept_socket > 0)223close(accept_socket);224if (listen_socket > 0)225close(listen_socket);226227_exit(rc);228}229230static int231new_test_socket(int *connect_socket)232{233struct sockaddr_in sin;234int rc = 0;235236*connect_socket = socket(PF_INET, SOCK_STREAM, 0);237if (*connect_socket < 0)238FAIL_ERR("socket")239240bzero(&sin, sizeof(sin));241sin.sin_len = sizeof(sin);242sin.sin_family = AF_INET;243sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);244sin.sin_port = htons(TEST_PORT);245246if (connect(*connect_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0)247FAIL_ERR("connect")248249return (rc);250}251252static void253init_th(struct test_header *th, uint32_t header_length, uint32_t offset,254uint32_t length)255{256bzero(th, sizeof(*th));257th->th_magic = htonl(TEST_MAGIC);258th->th_header_length = htonl(header_length);259th->th_offset = htonl(offset);260th->th_length = htonl(length);261262MD5FileChunk(path, th->th_md5, offset, length);263}264265static int266send_test(int connect_socket, struct sendfile_test test)267{268struct test_header th;269struct sf_hdtr hdtr, *hdtrp;270struct iovec headers;271char *header;272ssize_t len;273int length;274off_t off;275276len = lseek(file_fd, 0, SEEK_SET);277if (len != 0)278FAIL_ERR("lseek")279280struct stat st;281if (fstat(file_fd, &st) < 0)282FAIL_ERR("fstat")283length = st.st_size - test.offset;284if (test.length > 0 && test.length < (uint32_t)length)285length = test.length;286287init_th(&th, test.hdr_length, test.offset, length);288289len = write(connect_socket, &th, sizeof(th));290if (len != sizeof(th))291return (-1);292293if (test.hdr_length != 0) {294header = malloc(test.hdr_length);295if (header == NULL)296FAIL_ERR("malloc")297298hdtrp = &hdtr;299bzero(&headers, sizeof(headers));300headers.iov_base = header;301headers.iov_len = test.hdr_length;302bzero(&hdtr, sizeof(hdtr));303hdtr.headers = &headers;304hdtr.hdr_cnt = 1;305hdtr.trailers = NULL;306hdtr.trl_cnt = 0;307} else {308hdtrp = NULL;309header = NULL;310}311312if (sendfile(file_fd, connect_socket, test.offset, test.length,313hdtrp, &off, 0) < 0) {314if (header != NULL)315free(header);316FAIL_ERR("sendfile")317}318319if (length == 0) {320struct stat sb;321322if (fstat(file_fd, &sb) == 0)323length = sb.st_size - test.offset;324}325326if (header != NULL)327free(header);328329if (off != length)330FAIL("offset != length")331332return (0);333}334335static int336write_test_file(size_t file_size)337{338char *page_buffer;339ssize_t len;340static size_t current_file_size = 0;341342if (file_size == current_file_size)343return (0);344else if (file_size < current_file_size) {345if (ftruncate(file_fd, file_size) != 0)346FAIL_ERR("ftruncate");347current_file_size = file_size;348return (0);349}350351page_buffer = malloc(file_size);352if (page_buffer == NULL)353FAIL_ERR("malloc")354bzero(page_buffer, file_size);355356len = write(file_fd, page_buffer, file_size);357if (len < 0)358FAIL_ERR("write")359360len = lseek(file_fd, 0, SEEK_SET);361if (len < 0)362FAIL_ERR("lseek")363if (len != 0)364FAIL("len != 0")365366free(page_buffer);367current_file_size = file_size;368return (0);369}370371static void372run_parent(void)373{374int connect_socket;375int status;376int test_num;377int test_count;378int pid;379size_t desired_file_size = 0;380381const int pagesize = getpagesize();382383struct sendfile_test tests[] = {384{ .hdr_length = 0, .offset = 0, .length = 1 },385{ .hdr_length = 0, .offset = 0, .length = pagesize },386{ .hdr_length = 0, .offset = 1, .length = 1 },387{ .hdr_length = 0, .offset = 1, .length = pagesize },388{ .hdr_length = 0, .offset = pagesize, .length = pagesize },389{ .hdr_length = 0, .offset = 0, .length = 2*pagesize },390{ .hdr_length = 0, .offset = 0, .length = 0 },391{ .hdr_length = 0, .offset = pagesize, .length = 0 },392{ .hdr_length = 0, .offset = 2*pagesize, .length = 0 },393{ .hdr_length = 0, .offset = TEST_PAGES*pagesize, .length = 0 },394{ .hdr_length = 0, .offset = 0, .length = pagesize,395.file_size = 1 }396};397398test_count = sizeof(tests) / sizeof(tests[0]);399printf("1..%d\n", test_count);400401for (test_num = 1; test_num <= test_count; test_num++) {402403desired_file_size = tests[test_num - 1].file_size;404if (desired_file_size == 0)405desired_file_size = TEST_PAGES * pagesize;406if (write_test_file(desired_file_size) != 0) {407printf("not ok %d\n", test_num);408continue;409}410411pid = fork();412if (pid == -1) {413printf("not ok %d\n", test_num);414continue;415}416417if (pid == 0)418run_child();419420usleep(250000);421422if (new_test_socket(&connect_socket) != 0) {423printf("not ok %d\n", test_num);424kill(pid, SIGALRM);425close(connect_socket);426continue;427}428429if (send_test(connect_socket, tests[test_num-1]) != 0) {430printf("not ok %d\n", test_num);431kill(pid, SIGALRM);432close(connect_socket);433continue;434}435436close(connect_socket);437if (waitpid(pid, &status, 0) == pid) {438if (WIFEXITED(status) && WEXITSTATUS(status) == 0)439printf("%s %d\n", "ok", test_num);440else441printf("%s %d\n", "not ok", test_num);442}443else {444printf("not ok %d\n", test_num);445}446}447}448449static void450cleanup(void)451{452453unlink(path);454}455456int457main(int argc, char *argv[])458{459460path[0] = '\0';461462if (argc == 1) {463snprintf(path, sizeof(path), "sendfile.XXXXXXXXXXXX");464file_fd = mkstemp(path);465if (file_fd == -1)466FAIL_ERR("mkstemp");467} else if (argc == 2) {468(void)strlcpy(path, argv[1], sizeof(path));469file_fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0600);470if (file_fd == -1)471FAIL_ERR("open");472} else {473FAIL("usage: sendfile [path]");474}475476atexit(cleanup);477478run_parent();479return (0);480}481482483