Merge branch 'master' of ssh://axboe@router.home.kernel.dk/data/git/splice
[splice.git] / splice-fromnet.c
CommitLineData
beeecb44
JA
1/*
2 * Splice from network to pipe. Currently splicing from a socket is not
3 * supported, so this test case demonstrates how to use read + vmsplice/splice
4 * to achieve the desired effect. This still involves a copy, so it's
5 * not as fast as real splice from socket would be.
6 */
7#include <stdio.h>
8#include <stdlib.h>
9#include <unistd.h>
10#include <netdb.h>
11#include <unistd.h>
12#include <fcntl.h>
13#include <signal.h>
14#include <netinet/in.h>
15#include <arpa/inet.h>
16#include <string.h>
17#include <sys/time.h>
18#include <errno.h>
19
20#include "splice.h"
21
22#define PSIZE 4096
23#define MASK (PSIZE - 1)
24
25#define ALIGN(buf) (void *) (((unsigned long) (buf) + MASK) & ~MASK)
26
27static int splice_flags;
28
29static int usage(char *name)
30{
31 fprintf(stderr, "%s: [-g] port\n", name);
32 return 1;
33}
34
35static unsigned long mtime_since(struct timeval *s, struct timeval *e)
36{
37 double sec, usec;
38
39 sec = e->tv_sec - s->tv_sec;
40 usec = e->tv_usec - s->tv_usec;
41 if (sec > 0 && usec < 0) {
42 sec--;
43 usec += 1000000;
44 }
45
46 sec *= (double) 1000;
47 usec /= (double) 1000;
48
49 return sec + usec;
50}
51
52static unsigned long mtime_since_now(struct timeval *s)
53{
54 struct timeval t;
55
56 gettimeofday(&t, NULL);
57 return mtime_since(s, &t);
58}
59
60static int recv_loop(int fd, char *buf)
61{
62 unsigned long kb_recv = 0, spent;
63 struct timeval s;
64 struct iovec iov;
65 int idx = 0;
66
67 gettimeofday(&s, NULL);
68
69 do {
70 char *ptr = buf + idx * SPLICE_SIZE;
71 int ret;
72
73 ret = recv(fd, ptr, SPLICE_SIZE, MSG_WAITALL);
74 if (ret < 0) {
75 perror("recv");
76 return 1;
77 } else if (ret != SPLICE_SIZE)
78 break;
79
80 iov.iov_base = ptr;
81 iov.iov_len = SPLICE_SIZE;
82 ret = vmsplice(STDOUT_FILENO, &iov, 1, splice_flags);
83 if (ret < 0) {
84 perror("vmsplice");
85 return 1;
86 } else if (ret != SPLICE_SIZE) {
87 fprintf(stderr, "bad vmsplice %d\n", ret);
88 return 1;
89 }
90 idx = (idx + 1) & 0x01;
91 kb_recv += (SPLICE_SIZE / 1024);
92 } while (1);
93
94 spent = mtime_since_now(&s);
95 fprintf(stderr, "%lu MiB/sec (%luKiB in %lu msec)\n", kb_recv / spent, kb_recv, spent);
96
97 return 0;
98}
99
100static int parse_options(int argc, char *argv[])
101{
102 int c, index = 1;
103
104 while ((c = getopt(argc, argv, "g")) != -1) {
105 switch (c) {
106 case 'g':
107 splice_flags = SPLICE_F_GIFT;
108 index++;
109 break;
110 default:
111 return -1;
112 }
113 }
114
115 return index;
116}
117
118int main(int argc, char *argv[])
119{
120 struct sockaddr_in addr;
121 unsigned short port;
122 unsigned int len;
123 int fd, opt, sk, index;
124 char *buf;
125
126 if (check_output_pipe())
127 return usage(argv[0]);
128
129 index = parse_options(argc, argv);
130 if (index == -1 || index + 1 > argc)
131 return usage(argv[0]);
132
133 port = atoi(argv[index]);
134
135 fd = socket(PF_INET, SOCK_STREAM, 0);
136 if (fd < 0)
137 return error("socket");
138
139 opt = 1;
140 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
141 return error("setsockopt");
142
143 memset(&addr, 0, sizeof(addr));
144 addr.sin_addr.s_addr = htonl(INADDR_ANY);
145 addr.sin_port = htons(port);
146
147 if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
148 return error("bind");
149
150 if (listen(fd, 1) < 0)
151 return error("listen");
152
153 len = sizeof(addr);
154 sk = accept(fd, &addr, &len);
155 if (sk < 0)
156 return error("accept");
157
158 fprintf(stderr, "Connected\n");
159
160 buf = ALIGN(malloc(2 * SPLICE_SIZE - 1));
161
162 return recv_loop(sk, buf);
163
164 close(fd);
165 return 0;
166}