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