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