]> Dogcows Code - chaz/tar/blob - src/checkpoint.c
Bugfixes
[chaz/tar] / src / checkpoint.c
1 /* Checkpoint management for tar.
2
3 Copyright 2007, 2013-2014 Free Software Foundation, Inc.
4
5 This file is part of GNU tar.
6
7 GNU tar is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 GNU tar is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19
20 #include <system.h>
21 #include "common.h"
22 #include "wordsplit.h"
23 #include <sys/ioctl.h>
24 #include <termios.h>
25 #include "fprintftime.h"
26
27 enum checkpoint_opcode
28 {
29 cop_dot,
30 cop_bell,
31 cop_echo,
32 cop_ttyout,
33 cop_sleep,
34 cop_exec,
35 cop_totals
36 };
37
38 struct checkpoint_action
39 {
40 struct checkpoint_action *next;
41 enum checkpoint_opcode opcode;
42 union
43 {
44 time_t time;
45 char *command;
46 } v;
47 };
48
49 /* Checkpointing counter */
50 static unsigned checkpoint;
51
52 /* List of checkpoint actions */
53 static struct checkpoint_action *checkpoint_action, *checkpoint_action_tail;
54
55 static struct checkpoint_action *
56 alloc_action (enum checkpoint_opcode opcode)
57 {
58 struct checkpoint_action *p = xzalloc (sizeof *p);
59 if (checkpoint_action_tail)
60 checkpoint_action_tail->next = p;
61 else
62 checkpoint_action = p;
63 checkpoint_action_tail = p;
64 p->opcode = opcode;
65 return p;
66 }
67
68 static char *
69 copy_string_unquote (const char *str)
70 {
71 char *output = xstrdup (str);
72 size_t len = strlen (output);
73 if ((*output == '"' || *output == '\'')
74 && output[len-1] == *output)
75 {
76 memmove (output, output+1, len-2);
77 output[len-2] = 0;
78 }
79 unquote_string (output);
80 return output;
81 }
82
83 void
84 checkpoint_compile_action (const char *str)
85 {
86 struct checkpoint_action *act;
87
88 if (strcmp (str, ".") == 0 || strcmp (str, "dot") == 0)
89 alloc_action (cop_dot);
90 else if (strcmp (str, "bell") == 0)
91 alloc_action (cop_bell);
92 else if (strcmp (str, "echo") == 0)
93 alloc_action (cop_echo);
94 else if (strncmp (str, "echo=", 5) == 0)
95 {
96 act = alloc_action (cop_echo);
97 act->v.command = copy_string_unquote (str + 5);
98 }
99 else if (strncmp (str, "exec=", 5) == 0)
100 {
101 act = alloc_action (cop_exec);
102 act->v.command = copy_string_unquote (str + 5);
103 }
104 else if (strncmp (str, "ttyout=", 7) == 0)
105 {
106 act = alloc_action (cop_ttyout);
107 act->v.command = copy_string_unquote (str + 7);
108 }
109 else if (strncmp (str, "sleep=", 6) == 0)
110 {
111 char *p;
112 time_t n = strtoul (str+6, &p, 10);
113 if (*p)
114 FATAL_ERROR ((0, 0, _("%s: not a valid timeout"), str));
115 act = alloc_action (cop_sleep);
116 act->v.time = n;
117 }
118 else if (strcmp (str, "totals") == 0)
119 alloc_action (cop_totals);
120 else
121 FATAL_ERROR ((0, 0, _("%s: unknown checkpoint action"), str));
122 }
123
124 void
125 checkpoint_finish_compile (void)
126 {
127 if (checkpoint_option)
128 {
129 if (!checkpoint_action)
130 /* Provide a historical default */
131 checkpoint_compile_action ("echo");
132 }
133 else if (checkpoint_action)
134 /* Otherwise, set default checkpoint rate */
135 checkpoint_option = DEFAULT_CHECKPOINT;
136 }
137
138 static const char *checkpoint_total_format[] = {
139 "R",
140 "W",
141 "D"
142 };
143
144 static int
145 getwidth (FILE *fp)
146 {
147 struct winsize ws;
148
149 ws.ws_col = ws.ws_row = 0;
150 if ((ioctl (fileno (fp), TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_col == 0)
151 {
152 const char *col = getenv ("COLUMNS");
153 if (col)
154 return strtol (col, NULL, 10);
155 else
156 return 80;
157 }
158 return ws.ws_col;
159 }
160
161 static char *
162 getarg (const char *input, const char ** endp, char **argbuf, size_t *arglen)
163 {
164 if (input[0] == '{')
165 {
166 char *p = strchr (input + 1, '}');
167 if (p)
168 {
169 size_t n = p - input;
170 if (n > *arglen)
171 {
172 *arglen = n;
173 *argbuf = xrealloc (*argbuf, *arglen);
174 }
175 n--;
176 memcpy (*argbuf, input + 1, n);
177 (*argbuf)[n] = 0;
178 *endp = p + 1;
179 return *argbuf;
180 }
181 }
182
183 *endp = input;
184 return NULL;
185 }
186
187 static int tty_cleanup;
188
189 static const char *def_format =
190 "%{%Y-%m-%d %H:%M:%S}t: %ds, %{read,wrote}T%*\r";
191
192 static int
193 format_checkpoint_string (FILE *fp, size_t len,
194 const char *input, bool do_write,
195 unsigned cpn)
196 {
197 const char *opstr = do_write ? gettext ("write") : gettext ("read");
198 char uintbuf[UINTMAX_STRSIZE_BOUND];
199 char *cps = STRINGIFY_BIGINT (cpn, uintbuf);
200 const char *ip;
201
202 static char *argbuf = NULL;
203 static size_t arglen = 0;
204 char *arg = NULL;
205
206 if (!input)
207 {
208 if (do_write)
209 /* TRANSLATORS: This is a "checkpoint of write operation",
210 *not* "Writing a checkpoint".
211 E.g. in Spanish "Punto de comprobaci@'on de escritura",
212 *not* "Escribiendo un punto de comprobaci@'on" */
213 input = gettext ("Write checkpoint %u");
214 else
215 /* TRANSLATORS: This is a "checkpoint of read operation",
216 *not* "Reading a checkpoint".
217 E.g. in Spanish "Punto de comprobaci@'on de lectura",
218 *not* "Leyendo un punto de comprobaci@'on" */
219 input = gettext ("Read checkpoint %u");
220 }
221
222 for (ip = input; *ip; ip++)
223 {
224 if (*ip == '%')
225 {
226 if (*++ip == '{')
227 {
228 arg = getarg (ip, &ip, &argbuf, &arglen);
229 if (!arg)
230 {
231 fputc ('%', fp);
232 fputc (*ip, fp);
233 len += 2;
234 continue;
235 }
236 }
237 switch (*ip)
238 {
239 case 'c':
240 len += format_checkpoint_string (fp, len, def_format, do_write,
241 cpn);
242 break;
243
244 case 'u':
245 fputs (cps, fp);
246 len += strlen (cps);
247 break;
248
249 case 's':
250 fputs (opstr, fp);
251 len += strlen (opstr);
252 break;
253
254 case 'd':
255 len += fprintf (fp, "%.0f", compute_duration ());
256 break;
257
258 case 'T':
259 {
260 const char **fmt = checkpoint_total_format, *fmtbuf[3];
261 struct wordsplit ws;
262 compute_duration ();
263
264 if (arg)
265 {
266 ws.ws_delim = ",";
267 if (wordsplit (arg, &ws, WRDSF_NOVAR | WRDSF_NOCMD |
268 WRDSF_QUOTE | WRDSF_DELIM))
269 ERROR ((0, 0, _("cannot split string '%s': %s"),
270 arg, wordsplit_strerror (&ws)));
271 else
272 {
273 int i;
274
275 for (i = 0; i < ws.ws_wordc; i++)
276 fmtbuf[i] = ws.ws_wordv[i];
277 for (; i < 3; i++)
278 fmtbuf[i] = NULL;
279 fmt = fmtbuf;
280 }
281 }
282 len += format_total_stats (fp, fmt, ',', 0);
283 if (arg)
284 wordsplit_free (&ws);
285 }
286 break;
287
288 case 't':
289 {
290 struct timeval tv;
291 struct tm *tm;
292 const char *fmt = arg ? arg : "%c";
293
294 gettimeofday (&tv, NULL);
295 tm = localtime (&tv.tv_sec);
296 len += fprintftime (fp, fmt, tm, 0, tv.tv_usec * 1000);
297 }
298 break;
299
300 case '*':
301 {
302 int w = arg ? strtoul (arg, NULL, 10) : getwidth (fp);
303 for (; w > len; len++)
304 fputc (' ', fp);
305 }
306 break;
307
308 default:
309 fputc ('%', fp);
310 fputc (*ip, fp);
311 len += 2;
312 break;
313 }
314 arg = NULL;
315 }
316 else
317 {
318 fputc (*ip, fp);
319 if (*ip == '\r')
320 {
321 len = 0;
322 tty_cleanup = 1;
323 }
324 else
325 len++;
326 }
327 }
328 fflush (fp);
329 return len;
330 }
331
332 static FILE *tty = NULL;
333
334 static void
335 run_checkpoint_actions (bool do_write)
336 {
337 struct checkpoint_action *p;
338
339 for (p = checkpoint_action; p; p = p->next)
340 {
341 switch (p->opcode)
342 {
343 case cop_dot:
344 fputc ('.', stdlis);
345 fflush (stdlis);
346 break;
347
348 case cop_bell:
349 if (!tty)
350 tty = fopen ("/dev/tty", "w");
351 if (tty)
352 {
353 fputc ('\a', tty);
354 fflush (tty);
355 }
356 break;
357
358 case cop_echo:
359 {
360 int n = fprintf (stderr, "%s: ", program_name);
361 format_checkpoint_string (stderr, n, p->v.command, do_write,
362 checkpoint);
363 fputc ('\n', stderr);
364 }
365 break;
366
367 case cop_ttyout:
368 if (!tty)
369 tty = fopen ("/dev/tty", "w");
370 if (tty)
371 format_checkpoint_string (tty, 0, p->v.command, do_write,
372 checkpoint);
373 break;
374
375 case cop_sleep:
376 sleep (p->v.time);
377 break;
378
379 case cop_exec:
380 sys_exec_checkpoint_script (p->v.command,
381 archive_name_cursor[0],
382 checkpoint);
383 break;
384
385 case cop_totals:
386 compute_duration ();
387 print_total_stats ();
388 }
389 }
390 }
391
392 void
393 checkpoint_flush_actions (void)
394 {
395 struct checkpoint_action *p;
396
397 for (p = checkpoint_action; p; p = p->next)
398 {
399 switch (p->opcode)
400 {
401 case cop_ttyout:
402 if (tty && tty_cleanup)
403 {
404 int w = getwidth (tty);
405 while (w--)
406 fputc (' ', tty);
407 fputc ('\r', tty);
408 fflush (tty);
409 }
410 break;
411 default:
412 /* nothing */;
413 }
414 }
415 }
416
417 void
418 checkpoint_run (bool do_write)
419 {
420 if (checkpoint_option && !(++checkpoint % checkpoint_option))
421 run_checkpoint_actions (do_write);
422 }
423
424 void
425 checkpoint_finish (void)
426 {
427 if (checkpoint_option)
428 {
429 checkpoint_flush_actions ();
430 if (tty)
431 fclose (tty);
432 }
433 }
This page took 0.046651 seconds and 4 git commands to generate.