0382f46444de05c8059b479fe26f8f17efedb033
[splice.git] / vmsplice.c
1 /*
2  * Use vmsplice to fill some user memory into a pipe. vmsplice writes
3  * to stdout, so that must be a pipe.
4  */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <limits.h>
9 #include <string.h>
10 #include <getopt.h>
11 #include <sys/poll.h>
12 #include <sys/types.h>
13
14 #include "splice.h"
15
16 #define ALIGN(buf)      (void *) (((unsigned long) (buf) + align_mask) & ~align_mask)
17
18 static int do_clear;
19 static int align_mask = 65535;
20 static int force_unalign;
21 static int splice_flags;
22
23 int do_vmsplice(int fd, void *b1, void *b2, int len)
24 {
25         struct pollfd pfd = { .fd = fd, .events = POLLOUT, };
26         struct iovec iov[] = {
27                 {
28                         .iov_base = b1,
29                         .iov_len = len / 2,
30                 },
31                 {
32                         .iov_base = b2,
33                         .iov_len = len / 2,
34                 },
35         };
36         int written, idx = 0;
37
38         while (len) {
39                 /*
40                  * in a real app you'd be more clever with poll of course,
41                  * here we are basically just blocking on output room and
42                  * not using the free time for anything interesting.
43                  */
44                 if (poll(&pfd, 1, -1) < 0)
45                         return error("poll");
46
47                 written = vmsplice(fd, &iov[idx], 2 - idx, splice_flags);
48
49                 if (written <= 0)
50                         return error("vmsplice");
51
52                 len -= written;
53                 if (written >= iov[idx].iov_len) {
54                         int extra = written - iov[idx].iov_len;
55
56                         idx++;
57                         iov[idx].iov_len -= extra;
58                         iov[idx].iov_base += extra;
59                 } else {
60                         iov[idx].iov_len -= written;
61                         iov[idx].iov_base += written;
62                 }
63         }
64
65         return 0;
66 }
67
68 static int usage(char *name)
69 {
70         fprintf(stderr, "%s: [-c(lear)] [-u(nalign)] [-g(ift)]| ...\n", name);
71         return 1;
72 }
73
74 static int parse_options(int argc, char *argv[])
75 {
76         int c, index = 1;
77
78         while ((c = getopt(argc, argv, "cug")) != -1) {
79                 switch (c) {
80                 case 'c':
81                         do_clear = 1;
82                         index++;
83                         break;
84                 case 'u':
85                         force_unalign = 1;
86                         index++;
87                         break;
88                 case 'g':
89                         splice_flags = SPLICE_F_GIFT;
90                         index++;
91                         break;
92                 default:
93                         return -1;
94                 }
95         }
96
97         return index;
98 }
99
100 int main(int argc, char *argv[])
101 {
102         unsigned char *b1, *b2;
103
104         if (parse_options(argc, argv) < 0)
105                 return usage(argv[0]);
106
107         if (check_output_pipe())
108                 return usage(argv[0]);
109
110         b1 = ALIGN(malloc(SPLICE_SIZE + align_mask));
111         b2 = ALIGN(malloc(SPLICE_SIZE + align_mask));
112
113         if (force_unalign) {
114                 b1 += 1024;
115                 b2 += 1024;
116         }
117
118         memset(b1, 0xaa, SPLICE_SIZE);
119         memset(b2, 0xbb, SPLICE_SIZE);
120
121         do {
122                 int half = SPLICE_SIZE / 2;
123
124                 /*
125                  * vmsplice the first half of the buffer into the pipe
126                  */
127                 if (do_vmsplice(STDOUT_FILENO, b1, b2, SPLICE_SIZE))
128                         break;
129
130                 /*
131                  * first half is now in pipe, but we don't quite know when
132                  * we can reuse it.
133                  */
134
135                 /*
136                  * vmsplice second half
137                  */
138                 if (do_vmsplice(STDOUT_FILENO, b1 + half, b2 + half, SPLICE_SIZE))
139                         break;
140
141                 /*
142                  * We still don't know when we can reuse the second half of
143                  * the buffer, but we do now know that all parts of the first
144                  * half have been consumed from the pipe - so we can reuse that.
145                  */
146
147                 /*
148                  * Test option - clear the first half of the buffer, should
149                  * be safe now
150                  */
151                 if (do_clear) {
152                         memset(b1, 0x00, SPLICE_SIZE);
153                         memset(b2, 0x00, SPLICE_SIZE);
154                 }
155         } while (0);
156
157         return 0;
158 }