[PATCH]: vmsplice2 update
[splice.git] / vmsplice2.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 PAGE_SIZE       4096
17 #define PAGE_MASK       (PAGE_SIZE - 1)
18
19 static int alloc_stack;
20 static int cross_page;
21 static int force_align;
22 static int full_page;
23 static int gift;
24
25 int do_vmsplice(int fd, struct iovec *iov, unsigned long nr_vecs)
26 {
27         struct pollfd pfd = { .fd = fd, .events = POLLOUT, };
28         long written;
29
30         while (nr_vecs) {
31                 /*
32                  * in a real app you'd be more clever with poll of course,
33                  * here we are basically just blocking on output room and
34                  * not using the free time for anything interesting.
35                  */
36                 if (poll(&pfd, 1, -1) < 0)
37                         return error("poll");
38
39                 written = vmsplice(fd, iov, nr_vecs, gift ? SPLICE_F_GIFT : 0);
40
41                 if (written <= 0)
42                         return error("vmsplice");
43
44                 while (written) {
45                         int this_len = iov->iov_len;
46
47                         if (this_len > written)
48                                 this_len = written;
49
50                         iov->iov_len -= this_len;
51                         if (!iov->iov_len) {
52                                 nr_vecs--;
53                                 iov++;
54                         }
55
56                         written -= this_len;
57                 }
58         }
59
60         return 0;
61 }
62
63 static int usage(char *name)
64 {
65         fprintf(stderr, "%s [-s(tack)] [-c(ross page)] [-a(lign)] [-f(ull page)] [-g(ift)] | ...\n", name);
66         return 1;
67 }
68
69 static int parse_options(int argc, char *argv[])
70 {
71         int c, index = 1;
72
73         while ((c = getopt(argc, argv, "scafg")) != -1) {
74                 switch (c) {
75                 case 's':
76                         alloc_stack = 1;
77                         index++;
78                         break;
79                 case 'c':
80                         cross_page = 1;
81                         index++;
82                         break;
83                 case 'a':
84                         force_align = 1;
85                         index++;
86                         break;
87                 case 'f':
88                         full_page = 1;
89                         index++;
90                         break;
91                 case 'g':
92                         gift = 1;
93                         index++;
94                         break;
95                 default:
96                         return -1;
97                 }
98         }
99
100         if (cross_page && force_align) {
101                 fprintf(stderr, "Can't get both aligning and cross page spanning\n");
102                 return -1;
103         }
104
105         return index;
106 }
107
108 #define ASIZE   (3 * PAGE_SIZE)
109 #define S1      "header header header header header header header header "
110 #define S2      "body body body body body body body body body body body "
111 #define S3      "footer footer footer footer footer footer footer footer"
112
113 static void check_address(void *addr, void *start, void *end, int len,char *msg)
114 {
115         if (addr < start || (addr + len - 1) > end)
116                 fprintf(stderr, "%s: bad: %p < %p < %p false\n", msg, start, addr, end);
117 }
118
119 int main(int argc, char *argv[])
120 {
121         struct iovec vecs[3];
122         char stack1[ASIZE], stack2[ASIZE], stack3[ASIZE];
123         char *h_s, *h_e, *b_s, *b_e, *f_s, *f_e;
124         char *h, *b, *f;
125         int i;
126
127         if (check_output_pipe())
128                 return usage(argv[0]);
129
130         if (parse_options(argc, argv) < 0)
131                 return usage(argv[0]);
132
133         if (alloc_stack) {
134                 h = stack1;
135                 b = stack2;
136                 f = stack3;
137         } else {
138                 h = malloc(ASIZE);
139                 b = malloc(ASIZE);
140                 f = malloc(ASIZE);
141         }
142
143         memset(h, 0, ASIZE);
144         memset(b, 0, ASIZE);
145         memset(f, 0, ASIZE);
146
147         h_s = h;
148         h_e = h_s + 2 * PAGE_SIZE - 1;
149         b_s = b;
150         b_e = b_s + 2 * PAGE_SIZE - 1;
151         f_s = f;
152         f_e = f_s + 2 * PAGE_SIZE - 1;
153
154         if (force_align || cross_page) {
155                 /* align forward to start of 2nd page */
156                 unsigned long off;
157
158                 off = PAGE_SIZE - ((unsigned long) h & PAGE_MASK);
159                 h += off;
160                 off = PAGE_SIZE - ((unsigned long) b & PAGE_MASK);
161                 b += off;
162                 off = PAGE_SIZE - ((unsigned long) f & PAGE_MASK);
163                 f += off;
164                 if (cross_page) {
165                         /* this puts half the string in both pages */
166                         h -= strlen(S1) / 2;
167                         b -= strlen(S2) / 2;
168                         f -= strlen(S3) / 2;
169                 }
170         }
171
172         strcpy(h, S1);
173         strcpy(b, S2);
174         strcpy(f, S3);
175
176         vecs[0].iov_base = h;
177         vecs[1].iov_base = b;
178         vecs[2].iov_base = f;
179         if (!full_page) {
180                 vecs[0].iov_len = strlen(vecs[0].iov_base);
181                 vecs[1].iov_len = strlen(vecs[1].iov_base);
182                 vecs[2].iov_len = strlen(vecs[2].iov_base);
183         } else {
184                 vecs[0].iov_len = PAGE_SIZE;
185                 vecs[1].iov_len = PAGE_SIZE;
186                 vecs[2].iov_len = PAGE_SIZE;
187         }
188
189         check_address(h, h_s, h_e, vecs[0].iov_len, "header");
190         check_address(b, b_s, b_e, vecs[1].iov_len, "body");
191         check_address(f, f_s, f_e, vecs[2].iov_len, "footer");
192
193         return do_vmsplice(STDOUT_FILENO, vecs, 3);
194 }