1 /* Checkpoint management for tar.
3 Copyright 2007, 2013 Free Software Foundation, Inc.
5 This file is part of GNU tar.
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.
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.
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/>. */
22 #include "wordsplit.h"
23 #include <sys/ioctl.h>
24 #include "fprintftime.h"
26 enum checkpoint_opcode
37 struct checkpoint_action
39 struct checkpoint_action *next;
40 enum checkpoint_opcode opcode;
48 /* Checkpointing counter */
49 static unsigned checkpoint;
51 /* List of checkpoint actions */
52 static struct checkpoint_action *checkpoint_action, *checkpoint_action_tail;
54 static struct checkpoint_action *
55 alloc_action (enum checkpoint_opcode opcode)
57 struct checkpoint_action *p = xzalloc (sizeof *p);
58 if (checkpoint_action_tail)
59 checkpoint_action_tail->next = p;
61 checkpoint_action = p;
62 checkpoint_action_tail = p;
68 copy_string_unquote (const char *str)
70 char *output = xstrdup (str);
71 size_t len = strlen (output);
72 if ((*output == '"' || *output == '\'')
73 && output[len-1] == *output)
75 memmove (output, output+1, len-2);
78 unquote_string (output);
83 checkpoint_compile_action (const char *str)
85 struct checkpoint_action *act;
87 if (strcmp (str, ".") == 0 || strcmp (str, "dot") == 0)
88 alloc_action (cop_dot);
89 else if (strcmp (str, "bell") == 0)
90 alloc_action (cop_bell);
91 else if (strcmp (str, "echo") == 0)
92 alloc_action (cop_echo);
93 else if (strncmp (str, "echo=", 5) == 0)
95 act = alloc_action (cop_echo);
96 act->v.command = copy_string_unquote (str + 5);
98 else if (strncmp (str, "exec=", 5) == 0)
100 act = alloc_action (cop_exec);
101 act->v.command = copy_string_unquote (str + 5);
103 else if (strncmp (str, "ttyout=", 7) == 0)
105 act = alloc_action (cop_ttyout);
106 act->v.command = copy_string_unquote (str + 7);
108 else if (strncmp (str, "sleep=", 6) == 0)
111 time_t n = strtoul (str+6, &p, 10);
113 FATAL_ERROR ((0, 0, _("%s: not a valid timeout"), str));
114 act = alloc_action (cop_sleep);
117 else if (strcmp (str, "totals") == 0)
118 alloc_action (cop_totals);
120 FATAL_ERROR ((0, 0, _("%s: unknown checkpoint action"), str));
124 checkpoint_finish_compile (void)
126 if (checkpoint_option)
128 if (!checkpoint_action)
129 /* Provide a historical default */
130 checkpoint_compile_action ("echo");
132 else if (checkpoint_action)
133 /* Otherwise, set default checkpoint rate */
134 checkpoint_option = DEFAULT_CHECKPOINT;
137 static const char *checkpoint_total_format[] = {
148 ws.ws_col = ws.ws_row = 0;
149 if ((ioctl (fileno (fp), TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_col == 0)
151 const char *col = getenv ("COLUMNS");
153 return strtol (col, NULL, 10);
161 getarg (const char *input, const char ** endp, char **argbuf, size_t *arglen)
165 char *p = strchr (input + 1, '}');
168 size_t n = p - input;
172 *argbuf = xrealloc (*argbuf, *arglen);
175 memcpy (*argbuf, input + 1, n);
186 static int tty_cleanup;
188 static const char *def_format =
189 "%{%Y-%m-%d %H:%M:%S}t: %ds, %{read,wrote}T%*\r";
192 format_checkpoint_string (FILE *fp, size_t len,
193 const char *input, bool do_write,
196 const char *opstr = do_write ? gettext ("write") : gettext ("read");
197 char uintbuf[UINTMAX_STRSIZE_BOUND];
198 char *cps = STRINGIFY_BIGINT (cpn, uintbuf);
201 static char *argbuf = NULL;
202 static size_t arglen = 0;
208 /* TRANSLATORS: This is a "checkpoint of write operation",
209 *not* "Writing a checkpoint".
210 E.g. in Spanish "Punto de comprobaci@'on de escritura",
211 *not* "Escribiendo un punto de comprobaci@'on" */
212 input = gettext ("Write checkpoint %u");
214 /* TRANSLATORS: This is a "checkpoint of read operation",
215 *not* "Reading a checkpoint".
216 E.g. in Spanish "Punto de comprobaci@'on de lectura",
217 *not* "Leyendo un punto de comprobaci@'on" */
218 input = gettext ("Read checkpoint %u");
221 for (ip = input; *ip; ip++)
227 arg = getarg (ip, &ip, &argbuf, &arglen);
239 len += format_checkpoint_string (fp, len, def_format, do_write,
250 len += strlen (opstr);
254 len += fprintf (fp, "%.0f", compute_duration ());
259 const char **fmt = checkpoint_total_format, *fmtbuf[3];
266 if (wordsplit (arg, &ws, WRDSF_NOVAR | WRDSF_NOCMD |
267 WRDSF_QUOTE | WRDSF_DELIM))
268 ERROR ((0, 0, _("cannot split string '%s': %s"),
269 arg, wordsplit_strerror (&ws)));
274 for (i = 0; i < ws.ws_wordc; i++)
275 fmtbuf[i] = ws.ws_wordv[i];
281 len += format_total_stats (fp, fmt, ',', 0);
283 wordsplit_free (&ws);
291 const char *fmt = arg ? arg : "%c";
293 gettimeofday (&tv, NULL);
294 tm = localtime (&tv.tv_sec);
295 len += fprintftime (fp, fmt, tm, 0, tv.tv_usec * 1000);
301 int w = arg ? strtoul (arg, NULL, 10) : getwidth (fp);
302 for (; w > len; len++)
331 static FILE *tty = NULL;
334 run_checkpoint_actions (bool do_write)
336 struct checkpoint_action *p;
338 for (p = checkpoint_action; p; p = p->next)
349 tty = fopen ("/dev/tty", "w");
359 int n = fprintf (stderr, "%s: ", program_name);
360 format_checkpoint_string (stderr, n, p->v.command, do_write,
362 fputc ('\n', stderr);
368 tty = fopen ("/dev/tty", "w");
370 format_checkpoint_string (tty, 0, p->v.command, do_write,
379 sys_exec_checkpoint_script (p->v.command,
380 archive_name_cursor[0],
386 print_total_stats ();
392 checkpoint_flush_actions (void)
394 struct checkpoint_action *p;
396 for (p = checkpoint_action; p; p = p->next)
401 if (tty && tty_cleanup)
403 int w = getwidth (tty);
417 checkpoint_run (bool do_write)
419 if (checkpoint_option && !(++checkpoint % checkpoint_option))
420 run_checkpoint_actions (do_write);
424 checkpoint_finish (void)
426 if (checkpoint_option)
428 checkpoint_flush_actions ();