]> Dogcows Code - chaz/tar/blob - src/checkpoint.c
Improve checkpoint interface.
[chaz/tar] / src / checkpoint.c
1 /* Checkpoint management for tar.
2
3 Copyright 2007, 2013 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 "fprintftime.h"
25
26 enum checkpoint_opcode
27 {
28 cop_dot,
29 cop_bell,
30 cop_echo,
31 cop_ttyout,
32 cop_sleep,
33 cop_exec,
34 cop_totals
35 };
36
37 struct checkpoint_action
38 {
39 struct checkpoint_action *next;
40 enum checkpoint_opcode opcode;
41 union
42 {
43 time_t time;
44 char *command;
45 } v;
46 };
47
48 /* Checkpointing counter */
49 static unsigned checkpoint;
50
51 /* List of checkpoint actions */
52 static struct checkpoint_action *checkpoint_action, *checkpoint_action_tail;
53
54 static struct checkpoint_action *
55 alloc_action (enum checkpoint_opcode opcode)
56 {
57 struct checkpoint_action *p = xzalloc (sizeof *p);
58 if (checkpoint_action_tail)
59 checkpoint_action_tail->next = p;
60 else
61 checkpoint_action = p;
62 checkpoint_action_tail = p;
63 p->opcode = opcode;
64 return p;
65 }
66
67 static char *
68 copy_string_unquote (const char *str)
69 {
70 char *output = xstrdup (str);
71 size_t len = strlen (output);
72 if ((*output == '"' || *output == '\'')
73 && output[len-1] == *output)
74 {
75 memmove (output, output+1, len-2);
76 output[len-2] = 0;
77 }
78 unquote_string (output);
79 return output;
80 }
81
82 void
83 checkpoint_compile_action (const char *str)
84 {
85 struct checkpoint_action *act;
86
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)
94 {
95 act = alloc_action (cop_echo);
96 act->v.command = copy_string_unquote (str + 5);
97 }
98 else if (strncmp (str, "exec=", 5) == 0)
99 {
100 act = alloc_action (cop_exec);
101 act->v.command = copy_string_unquote (str + 5);
102 }
103 else if (strncmp (str, "ttyout=", 7) == 0)
104 {
105 act = alloc_action (cop_ttyout);
106 act->v.command = copy_string_unquote (str + 7);
107 }
108 else if (strncmp (str, "sleep=", 6) == 0)
109 {
110 char *p;
111 time_t n = strtoul (str+6, &p, 10);
112 if (*p)
113 FATAL_ERROR ((0, 0, _("%s: not a valid timeout"), str));
114 act = alloc_action (cop_sleep);
115 act->v.time = n;
116 }
117 else if (strcmp (str, "totals") == 0)
118 alloc_action (cop_totals);
119 else
120 FATAL_ERROR ((0, 0, _("%s: unknown checkpoint action"), str));
121 }
122
123 void
124 checkpoint_finish_compile (void)
125 {
126 if (checkpoint_option)
127 {
128 if (!checkpoint_action)
129 /* Provide a historical default */
130 checkpoint_compile_action ("echo");
131 }
132 else if (checkpoint_action)
133 /* Otherwise, set default checkpoint rate */
134 checkpoint_option = DEFAULT_CHECKPOINT;
135 }
136
137 static const char *checkpoint_total_format[] = {
138 "R",
139 "W",
140 "D"
141 };
142
143 static int
144 getwidth (FILE *fp)
145 {
146 struct winsize ws;
147
148 ws.ws_col = ws.ws_row = 0;
149 if ((ioctl (fileno (fp), TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_col == 0)
150 {
151 const char *col = getenv ("COLUMNS");
152 if (col)
153 return strtol (col, NULL, 10);
154 else
155 return 80;
156 }
157 return ws.ws_col;
158 }
159
160 static char *
161 getarg (const char *input, const char ** endp, char **argbuf, size_t *arglen)
162 {
163 if (input[0] == '{')
164 {
165 char *p = strchr (input + 1, '}');
166 if (p)
167 {
168 size_t n = p - input;
169 if (n > *arglen)
170 {
171 *arglen = n;
172 *argbuf = xrealloc (*argbuf, *arglen);
173 }
174 n--;
175 memcpy (*argbuf, input + 1, n);
176 (*argbuf)[n] = 0;
177 *endp = p + 1;
178 return *argbuf;
179 }
180 }
181
182 *endp = input;
183 return NULL;
184 }
185
186 static int tty_cleanup;
187
188 static const char *def_format = "%s: %t: %T%*\r";
189
190 static int
191 format_checkpoint_string (FILE *fp, size_t len,
192 const char *input, bool do_write,
193 unsigned cpn)
194 {
195 const char *opstr = do_write ? gettext ("write") : gettext ("read");
196 char uintbuf[UINTMAX_STRSIZE_BOUND];
197 char *cps = STRINGIFY_BIGINT (cpn, uintbuf);
198 const char *ip;
199
200 static char *argbuf = NULL;
201 static size_t arglen = 0;
202 char *arg = NULL;
203
204 if (!input)
205 {
206 if (do_write)
207 /* TRANSLATORS: This is a "checkpoint of write operation",
208 *not* "Writing a checkpoint".
209 E.g. in Spanish "Punto de comprobaci@'on de escritura",
210 *not* "Escribiendo un punto de comprobaci@'on" */
211 input = gettext ("Write checkpoint %u");
212 else
213 /* TRANSLATORS: This is a "checkpoint of read operation",
214 *not* "Reading a checkpoint".
215 E.g. in Spanish "Punto de comprobaci@'on de lectura",
216 *not* "Leyendo un punto de comprobaci@'on" */
217 input = gettext ("Read checkpoint %u");
218 }
219
220 for (ip = input; *ip; ip++)
221 {
222 if (*ip == '%')
223 {
224 if (*++ip == '{')
225 {
226 arg = getarg (ip, &ip, &argbuf, &arglen);
227 if (!arg)
228 {
229 fputc ('%', fp);
230 fputc (*ip, fp);
231 len += 2;
232 continue;
233 }
234 }
235 switch (*ip)
236 {
237 case 'c':
238 len += format_checkpoint_string (fp, len, def_format, do_write,
239 cpn);
240 break;
241
242 case 'u':
243 fputs (cps, fp);
244 len += strlen (cps);
245 break;
246
247 case 's':
248 fputs (opstr, fp);
249 len += strlen (opstr);
250 break;
251
252 case 'd':
253 len += fprintf (fp, "%.0f", compute_duration ());
254 break;
255
256 case 'T':
257 compute_duration ();
258 len += format_total_stats (fp, checkpoint_total_format, ',', 0);
259 break;
260
261 case 't':
262 {
263 struct timeval tv;
264 struct tm *tm;
265 const char *fmt = arg ? arg : "%c";
266
267 gettimeofday (&tv, NULL);
268 tm = localtime (&tv.tv_sec);
269 len += fprintftime (fp, fmt, tm, 0, tv.tv_usec * 1000);
270 }
271 break;
272
273 case '*':
274 {
275 int w = arg ? strtoul (arg, NULL, 10) : getwidth (fp);
276 for (; w > len; len++)
277 fputc (' ', fp);
278 }
279 break;
280
281 default:
282 fputc ('%', fp);
283 fputc (*ip, fp);
284 len += 2;
285 break;
286 }
287 arg = NULL;
288 }
289 else
290 {
291 fputc (*ip, fp);
292 if (*ip == '\r')
293 {
294 len = 0;
295 tty_cleanup = 1;
296 }
297 else
298 len++;
299 }
300 }
301 fflush (fp);
302 return len;
303 }
304
305 static FILE *tty = NULL;
306
307 static void
308 run_checkpoint_actions (bool do_write)
309 {
310 struct checkpoint_action *p;
311
312 for (p = checkpoint_action; p; p = p->next)
313 {
314 switch (p->opcode)
315 {
316 case cop_dot:
317 fputc ('.', stdlis);
318 fflush (stdlis);
319 break;
320
321 case cop_bell:
322 if (!tty)
323 tty = fopen ("/dev/tty", "w");
324 if (tty)
325 {
326 fputc ('\a', tty);
327 fflush (tty);
328 }
329 break;
330
331 case cop_echo:
332 {
333 int n = fprintf (stderr, "%s: ", program_name);
334 format_checkpoint_string (stderr, n, p->v.command, do_write,
335 checkpoint);
336 fputc ('\n', stderr);
337 }
338 break;
339
340 case cop_ttyout:
341 if (!tty)
342 tty = fopen ("/dev/tty", "w");
343 if (tty)
344 format_checkpoint_string (tty, 0, p->v.command, do_write,
345 checkpoint);
346 break;
347
348 case cop_sleep:
349 sleep (p->v.time);
350 break;
351
352 case cop_exec:
353 sys_exec_checkpoint_script (p->v.command,
354 archive_name_cursor[0],
355 checkpoint);
356 break;
357
358 case cop_totals:
359 compute_duration ();
360 print_total_stats ();
361 }
362 }
363 }
364
365 static void
366 finish_checkpoint_actions (void)
367 {
368 struct checkpoint_action *p;
369
370 for (p = checkpoint_action; p; p = p->next)
371 {
372 switch (p->opcode)
373 {
374 case cop_ttyout:
375 if (tty && tty_cleanup)
376 {
377 int w = getwidth (tty);
378 while (w--)
379 fputc (' ', tty);
380 fputc ('\r', tty);
381 }
382 break;
383 default:
384 /* nothing */;
385 }
386 }
387 if (tty)
388 fclose (tty);
389 }
390
391 void
392 checkpoint_run (bool do_write)
393 {
394 if (checkpoint_option && !(++checkpoint % checkpoint_option))
395 run_checkpoint_actions (do_write);
396 }
397
398 void
399 checkpoint_finish (void)
400 {
401 if (checkpoint_option)
402 finish_checkpoint_actions ();
403 }
This page took 0.044525 seconds and 4 git commands to generate.