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