1 /* This is a slowcat program, you can limit the number of bytes written and
2 * how often they are written.
3 * Does stdin if no args are given */
4
5 #define EX_UTILS_NO_USE_LIMIT 1
6 #define EX_UTILS_NO_USE_GET 1
7 #define EX_UTILS_NO_USE_PUT 1
8 #define EX_UTILS_NO_USE_PUTALL 1
9 #include "ex_utils.h"
10 #include "opt.h"
11
12 #include <timer_q.h>
13 #include <getopt.h>
14 #include <stdio.h>
15 #include <sys/time.h>
16 #include <time.h>
17
18 #define EX_SLOWCAT_READ_BYTES 80
19 #define EX_SLOWCAT_WRITE_BYTES 80
20 #define EX_SLOWCAT_WRITE_WAIT_SEC 1
21 #define EX_SLOWCAT_WRITE_WAIT_USEC 0 /* 500000 */
22
23 typedef struct Ex_slowcat_vars
24 {
25 unsigned int opt_read_bytes;
26 unsigned int opt_write_bytes;
27
28 unsigned int opt_write_wait_sec;
29 unsigned int opt_write_wait_usec;
30
31 int argc;
32 char **argv;
33 Vstr_base *s1;
34 int arg_count;
35 Timer_q_base *base;
36 Timer_q_node *node;
37 int fd;
38
39 unsigned int finished_reading_data : 1;
40 unsigned int finished_reading_file : 1;
41 unsigned int use_mmap : 1;
42 } Ex_slowcat_vars;
43
44 #undef MIN_T
45 #define MIN_T(x, y, z) ((((z) (x)) < ((z) (y))) ? ((z) (x)) : ((z) (y)))
46
ex_slowcat_nxt_timer_func(Ex_slowcat_vars * v)47 static void ex_slowcat_nxt_timer_func(Ex_slowcat_vars *v)
48 {
49 struct timeval tv[1];
50
51 gettimeofday(tv, NULL);
52 TIMER_Q_TIMEVAL_ADD_SECS(tv, v->opt_write_wait_sec, v->opt_write_wait_usec);
53
54 v->node = timer_q_add_node(v->base, v, tv, TIMER_Q_FLAG_NODE_DEFAULT);
55 if (!v->node)
56 errno = ENOMEM, err(EXIT_FAILURE, "timer_q_add_node");
57 }
58
ex_slowcat_timer_func(int type,void * data)59 static void ex_slowcat_timer_func(int type, void *data)
60 {
61 Ex_slowcat_vars *v = data;
62 size_t len = 0;
63 int blocks_read = FALSE;
64
65 if (type == TIMER_Q_TYPE_CALL_DEL)
66 return;
67
68 if (!v->finished_reading_data && (v->s1->len < v->opt_write_bytes))
69 {
70 if (!v->argc && !v->arg_count && v->finished_reading_file)
71 {
72 v->finished_reading_file = FALSE;
73 v->fd = STDIN_FILENO;
74 io_fd_set_o_nonblock(STDIN_FILENO);
75 }
76 else
77 {
78 if (v->finished_reading_file)
79 {
80 ASSERT(v->arg_count < v->argc);
81
82 v->finished_reading_file = FALSE;
83
84 v->fd = io_open(v->argv[v->arg_count]);
85
86 ++v->arg_count;
87
88 if (v->use_mmap &&
89 vstr_sc_mmap_fd(v->s1, v->s1->len, v->fd, 0, 0, NULL))
90 {
91 if (v->arg_count >= v->argc)
92 v->finished_reading_data = TRUE;
93 v->finished_reading_file = TRUE;
94 }
95 else
96 io_fd_set_o_nonblock(v->fd);
97 }
98 }
99
100 if (!v->finished_reading_file)
101 {
102 unsigned int ern = 0;
103
104 vstr_sc_read_len_fd(v->s1, v->s1->len, v->fd,
105 v->opt_read_bytes, &ern);
106
107 if (ern == VSTR_TYPE_SC_READ_FD_ERR_EOF)
108 {
109 if (close(v->fd) == -1)
110 err(EXIT_FAILURE, "close");
111
112 if (v->arg_count >= v->argc)
113 v->finished_reading_data = TRUE;
114 v->finished_reading_file = TRUE;
115 }
116 else if (ern)
117 {
118 if ((ern == VSTR_TYPE_SC_READ_FD_ERR_READ_ERRNO) && (errno == EAGAIN))
119 blocks_read = TRUE;
120 else
121 err(EXIT_FAILURE, "read");
122 }
123 }
124 }
125
126 len = MIN_T(v->opt_write_bytes, v->s1->len, size_t);
127 /* do a write of the right ammount */
128 if (!vstr_sc_write_fd(v->s1, 1, len, STDOUT_FILENO, NULL))
129 {
130 if (errno != EAGAIN)
131 err(EXIT_FAILURE, "write");
132
133 if ((v->s1->len > EX_MAX_W_DATA_INCORE) ||
134 (v->s1->len && v->finished_reading_data))
135 io_block(-1, STDOUT_FILENO);
136 else if (blocks_read)
137 io_block(v->fd, STDOUT_FILENO);
138 }
139 else if (!v->s1->len && blocks_read)
140 io_block(v->fd, -1);
141
142 if (v->finished_reading_data && !v->s1->len)
143 return;
144
145 if (type == TIMER_Q_TYPE_CALL_RUN_ALL)
146 return;
147
148 ex_slowcat_nxt_timer_func(v);
149 }
150
ex_slowcat_init_cmd_line(Ex_slowcat_vars * v)151 static int ex_slowcat_init_cmd_line(Ex_slowcat_vars *v)
152 {
153 char optchar = 0;
154 const char *program_name = NULL;
155 struct option long_options[] =
156 {
157 {"bytes", required_argument, NULL, 'b'},
158 {"read-bytes", required_argument, NULL, 'r'},
159 {"write-bytes", required_argument, NULL, 'w'},
160 {"help", no_argument, NULL, 'h'},
161 {"mmap", optional_argument, NULL, 1},
162 {"seconds", required_argument, NULL, 's'},
163 {"useconds", required_argument, NULL, 'u'},
164 {"version", no_argument, NULL, 'V'},
165 {NULL, 0, NULL, 0}
166 };
167 FILE *help_stdout = NULL;
168
169 help_stdout = stdout;
170
171 program_name = opt_program_name(v->argv[0], "slowcat");
172
173 while ((optchar = getopt_long(v->argc, v->argv, "b:hs:u:vHV",
174 long_options, NULL)) != EOF)
175 switch (optchar)
176 {
177 case 'b':
178 v->opt_read_bytes = v->opt_write_bytes = atoi(optarg);
179 break;
180 case 'r':
181 v->opt_read_bytes = atoi(optarg);
182 break;
183 case 'w':
184 v->opt_write_bytes = atoi(optarg);
185 break;
186
187 case '?':
188 fprintf(stderr, " The option -- %c -- is not valid.\n", optchar);
189 help_stdout = stderr;
190 case 'H':
191 case 'h':
192 fprintf(help_stdout, "\n Format: %s [-bhsuvHV] [files]\n"
193 " --bytes -b - Number of bytes to read/write at once.\n"
194 " --write-bytes -w - Number of bytes to write at once.\n"
195 " --read-bytes -r - Number of bytes to read at once.\n"
196 " --help -h - Print this message.\n"
197 " --mmap - Toggle use of mmap() to load files.\n"
198 " --seconds -s - Number of seconds to wait between write calls.\n"
199 " --useconds -u - Number of micro seconds to wait between write calls.\n"
200 " --version -v - Print the version string.\n",
201 program_name);
202 if (optchar == '?')
203 exit (EXIT_FAILURE);
204 else
205 exit (EXIT_SUCCESS);
206
207 case 's':
208 v->opt_write_wait_sec = atoi(optarg);
209 break;
210
211 case 'u':
212 v->opt_write_wait_usec = atoi(optarg);
213 break;
214
215 case 1: OPT_TOGGLE_ARG(v->use_mmap); break;
216
217 case 'v':
218 case 'V':
219 printf(" %s is version 0.7.1\n", program_name);
220 exit (EXIT_SUCCESS);
221 }
222
223 v->argc -= optind;
224 v->argv += optind;
225
226 return (optind);
227 }
228
main(int argc,char * argv[])229 int main(int argc, char *argv[])
230 { /* This is "slowcat" */
231 Vstr_base *s1 = ex_init(NULL);
232 Ex_slowcat_vars v;
233 struct timeval s_tv;
234 const struct timeval *tv = NULL;
235
236 /* init stuff... */
237 v.opt_read_bytes = EX_SLOWCAT_READ_BYTES;
238 v.opt_write_bytes = EX_SLOWCAT_WRITE_BYTES;
239 v.opt_write_wait_sec = EX_SLOWCAT_WRITE_WAIT_SEC;
240 v.opt_write_wait_usec = EX_SLOWCAT_WRITE_WAIT_USEC;
241
242 v.argc = argc;
243 v.argv = argv;
244 v.s1 = s1;
245 v.arg_count = 0;
246 v.base = NULL;
247 v.node = NULL;
248 v.finished_reading_data = FALSE;
249 v.finished_reading_file = TRUE;
250 v.use_mmap = FALSE;
251
252 /* setup code... */
253 ex_slowcat_init_cmd_line(&v);
254
255 v.base = timer_q_add_base(ex_slowcat_timer_func, TIMER_Q_FLAG_BASE_DEFAULT);
256 if (!v.base)
257 errno = ENOMEM, err(EXIT_FAILURE, "timer_q_add_base");
258
259 ex_slowcat_nxt_timer_func(&v);
260
261 io_fd_set_o_nonblock(STDOUT_FILENO);
262
263 while ((tv = timer_q_first_timeval()))
264 {
265 long wait_period = 0;
266
267 gettimeofday(&s_tv, NULL);
268
269 wait_period = timer_q_timeval_diff_usecs(tv, &s_tv);
270 if (wait_period > 0)
271 usleep(wait_period);
272
273 gettimeofday(&s_tv, NULL);
274
275 timer_q_run_norm(&s_tv);
276 }
277
278 timer_q_del_base(v.base);
279
280 exit(ex_exit(s1, NULL));
281 }
282