File: | src/checkpoint.c |
Warning: | line 182, column 4 Array access results in a null pointer dereference |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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))do { if (error_hook) error_hook (); error (0, 0, dcgettext (( (void*)0), "%s: not a valid timeout", 5), str); fatal_exit () ; } while (0); | |||
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))do { if (error_hook) error_hook (); error (0, 0, dcgettext (( (void*)0), "%s: unknown checkpoint action", 5), str); fatal_exit (); } while (0); | |||
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_CHECKPOINT10; | |||
136 | } | |||
137 | ||||
138 | static const char *checkpoint_total_format[] = { | |||
139 | "R", | |||
140 | "W", | |||
141 | "D" | |||
142 | }; | |||
143 | ||||
144 | static long | |||
145 | getwidth (FILE *fp) | |||
146 | { | |||
147 | char const *columns; | |||
148 | ||||
149 | #ifdef TIOCGWINSZ0x5413 | |||
150 | struct winsize ws; | |||
151 | if (ioctl (fileno (fp), TIOCGWINSZ0x5413, &ws) == 0 && 0 < ws.ws_col) | |||
152 | return ws.ws_col; | |||
153 | #endif | |||
154 | ||||
155 | columns = getenv ("COLUMNS"); | |||
156 | if (columns) | |||
157 | { | |||
158 | long int col = strtol (columns, NULL((void*)0), 10); | |||
159 | if (0 < col) | |||
160 | return col; | |||
161 | } | |||
162 | ||||
163 | return 80; | |||
164 | } | |||
165 | ||||
166 | static char * | |||
167 | getarg (const char *input, const char ** endp, char **argbuf, size_t *arglen) | |||
168 | { | |||
169 | if (input[0] == '{') | |||
170 | { | |||
171 | char *p = strchr (input + 1, '}'); | |||
172 | if (p) | |||
173 | { | |||
174 | size_t n = p - input; | |||
175 | if (n > *arglen) | |||
176 | { | |||
177 | *arglen = n; | |||
178 | *argbuf = xrealloc (*argbuf, *arglen); | |||
179 | } | |||
180 | n--; | |||
181 | memcpy (*argbuf, input + 1, n); | |||
182 | (*argbuf)[n] = 0; | |||
| ||||
183 | *endp = p + 1; | |||
184 | return *argbuf; | |||
185 | } | |||
186 | } | |||
187 | ||||
188 | *endp = input; | |||
189 | return NULL((void*)0); | |||
190 | } | |||
191 | ||||
192 | static int tty_cleanup; | |||
193 | ||||
194 | static const char *def_format = | |||
195 | "%{%Y-%m-%d %H:%M:%S}t: %ds, %{read,wrote}T%*\r"; | |||
196 | ||||
197 | static int | |||
198 | format_checkpoint_string (FILE *fp, size_t len, | |||
199 | const char *input, bool_Bool do_write, | |||
200 | unsigned cpn) | |||
201 | { | |||
202 | const char *opstr = do_write ? gettext ("write")dcgettext (((void*)0), "write", 5) : gettext ("read")dcgettext (((void*)0), "read", 5); | |||
203 | char uintbuf[UINTMAX_STRSIZE_BOUND(((((sizeof (uintmax_t) * 8 - (! ((__typeof__ (uintmax_t)) 0 < (__typeof__ (uintmax_t)) -1))) * 146 + 484) / 485) + (! ((__typeof__ (uintmax_t)) 0 < (__typeof__ (uintmax_t)) -1))) + 1)]; | |||
204 | char *cps = STRINGIFY_BIGINT (cpn, uintbuf)umaxtostr (cpn, uintbuf); | |||
205 | const char *ip; | |||
206 | ||||
207 | static char *argbuf = NULL((void*)0); | |||
208 | static size_t arglen = 0; | |||
209 | char *arg = NULL((void*)0); | |||
210 | ||||
211 | if (!input) | |||
212 | { | |||
213 | if (do_write) | |||
214 | /* TRANSLATORS: This is a "checkpoint of write operation", | |||
215 | *not* "Writing a checkpoint". | |||
216 | E.g. in Spanish "Punto de comprobaci@'on de escritura", | |||
217 | *not* "Escribiendo un punto de comprobaci@'on" */ | |||
218 | input = gettext ("Write checkpoint %u")dcgettext (((void*)0), "Write checkpoint %u", 5); | |||
219 | else | |||
220 | /* TRANSLATORS: This is a "checkpoint of read operation", | |||
221 | *not* "Reading a checkpoint". | |||
222 | E.g. in Spanish "Punto de comprobaci@'on de lectura", | |||
223 | *not* "Leyendo un punto de comprobaci@'on" */ | |||
224 | input = gettext ("Read checkpoint %u")dcgettext (((void*)0), "Read checkpoint %u", 5); | |||
225 | } | |||
226 | ||||
227 | for (ip = input; *ip; ip++) | |||
228 | { | |||
229 | if (*ip == '%') | |||
230 | { | |||
231 | if (*++ip == '{') | |||
232 | { | |||
233 | arg = getarg (ip, &ip, &argbuf, &arglen); | |||
234 | if (!arg) | |||
235 | { | |||
236 | fputc ('%', fp)fputc_unlocked ('%',fp); | |||
237 | fputc (*ip, fp)fputc_unlocked (*ip,fp); | |||
238 | len += 2; | |||
239 | continue; | |||
240 | } | |||
241 | } | |||
242 | switch (*ip) | |||
243 | { | |||
244 | case 'c': | |||
245 | len += format_checkpoint_string (fp, len, def_format, do_write, | |||
246 | cpn); | |||
247 | break; | |||
248 | ||||
249 | case 'u': | |||
250 | fputs (cps, fp)fputs_unlocked (cps,fp); | |||
251 | len += strlen (cps); | |||
252 | break; | |||
253 | ||||
254 | case 's': | |||
255 | fputs (opstr, fp)fputs_unlocked (opstr,fp); | |||
256 | len += strlen (opstr); | |||
257 | break; | |||
258 | ||||
259 | case 'd': | |||
260 | len += fprintf (fp, "%.0f", compute_duration ())__fprintf_chk (fp, 2 - 1, "%.0f", compute_duration ()); | |||
261 | break; | |||
262 | ||||
263 | case 'T': | |||
264 | { | |||
265 | const char **fmt = checkpoint_total_format, *fmtbuf[3]; | |||
266 | struct wordsplit ws; | |||
267 | compute_duration (); | |||
268 | ||||
269 | if (arg) | |||
270 | { | |||
271 | ws.ws_delim = ","; | |||
272 | if (wordsplit (arg, &ws, WRDSF_NOVAR0x00000040 | WRDSF_NOCMD0x00000004 | | |||
273 | WRDSF_QUOTE(0x00000200|0x00000400) | WRDSF_DELIM0x00004000)) | |||
274 | ERROR ((0, 0, _("cannot split string '%s': %s"),do { if (error_hook) error_hook (); error (0, 0, dcgettext (( (void*)0), "cannot split string '%s': %s", 5), arg, wordsplit_strerror (&ws)); exit_status = 2; } while (0) | |||
275 | arg, wordsplit_strerror (&ws)))do { if (error_hook) error_hook (); error (0, 0, dcgettext (( (void*)0), "cannot split string '%s': %s", 5), arg, wordsplit_strerror (&ws)); exit_status = 2; } while (0); | |||
276 | else | |||
277 | { | |||
278 | int i; | |||
279 | ||||
280 | for (i = 0; i < ws.ws_wordc; i++) | |||
281 | fmtbuf[i] = ws.ws_wordv[i]; | |||
282 | for (; i < 3; i++) | |||
283 | fmtbuf[i] = NULL((void*)0); | |||
284 | fmt = fmtbuf; | |||
285 | } | |||
286 | } | |||
287 | len += format_total_stats (fp, fmt, ',', 0); | |||
288 | if (arg) | |||
289 | wordsplit_free (&ws); | |||
290 | } | |||
291 | break; | |||
292 | ||||
293 | case 't': | |||
294 | { | |||
295 | struct timeval tv; | |||
296 | struct tm *tm; | |||
297 | const char *fmt = arg ? arg : "%c"; | |||
298 | ||||
299 | gettimeofday (&tv, NULL((void*)0)); | |||
300 | tm = localtime (&tv.tv_sec); | |||
301 | len += fprintftime (fp, fmt, tm, 0, tv.tv_usec * 1000); | |||
302 | } | |||
303 | break; | |||
304 | ||||
305 | case '*': | |||
306 | { | |||
307 | long w = arg ? strtol (arg, NULL((void*)0), 10) : getwidth (fp); | |||
308 | for (; w > len; len++) | |||
309 | fputc (' ', fp)fputc_unlocked (' ',fp); | |||
310 | } | |||
311 | break; | |||
312 | ||||
313 | default: | |||
314 | fputc ('%', fp)fputc_unlocked ('%',fp); | |||
315 | fputc (*ip, fp)fputc_unlocked (*ip,fp); | |||
316 | len += 2; | |||
317 | break; | |||
318 | } | |||
319 | arg = NULL((void*)0); | |||
320 | } | |||
321 | else | |||
322 | { | |||
323 | fputc (*ip, fp)fputc_unlocked (*ip,fp); | |||
324 | if (*ip == '\r') | |||
325 | { | |||
326 | len = 0; | |||
327 | tty_cleanup = 1; | |||
328 | } | |||
329 | else | |||
330 | len++; | |||
331 | } | |||
332 | } | |||
333 | fflush (fp)fflush_unlocked (fp); | |||
334 | return len; | |||
335 | } | |||
336 | ||||
337 | static FILE *tty = NULL((void*)0); | |||
338 | ||||
339 | static void | |||
340 | run_checkpoint_actions (bool_Bool do_write) | |||
341 | { | |||
342 | struct checkpoint_action *p; | |||
343 | ||||
344 | for (p = checkpoint_action; p; p = p->next) | |||
345 | { | |||
346 | switch (p->opcode) | |||
347 | { | |||
348 | case cop_dot: | |||
349 | fputc ('.', stdlis)fputc_unlocked ('.',stdlis); | |||
350 | fflush (stdlis)fflush_unlocked (stdlis); | |||
351 | break; | |||
352 | ||||
353 | case cop_bell: | |||
354 | if (!tty) | |||
355 | tty = fopen ("/dev/tty", "w"); | |||
356 | if (tty) | |||
357 | { | |||
358 | fputc ('\a', tty)fputc_unlocked ('\a',tty); | |||
359 | fflush (tty)fflush_unlocked (tty); | |||
360 | } | |||
361 | break; | |||
362 | ||||
363 | case cop_echo: | |||
364 | { | |||
365 | int n = fprintf (stderr, "%s: ", program_name)__fprintf_chk (stderr, 2 - 1, "%s: ", program_name); | |||
366 | format_checkpoint_string (stderrstderr, n, p->v.command, do_write, | |||
367 | checkpoint); | |||
368 | fputc ('\n', stderr)fputc_unlocked ('\n',stderr); | |||
369 | } | |||
370 | break; | |||
371 | ||||
372 | case cop_ttyout: | |||
373 | if (!tty) | |||
374 | tty = fopen ("/dev/tty", "w"); | |||
375 | if (tty) | |||
376 | format_checkpoint_string (tty, 0, p->v.command, do_write, | |||
377 | checkpoint); | |||
378 | break; | |||
379 | ||||
380 | case cop_sleep: | |||
381 | sleep (p->v.time); | |||
382 | break; | |||
383 | ||||
384 | case cop_exec: | |||
385 | sys_exec_checkpoint_script (p->v.command, | |||
386 | archive_name_cursor[0], | |||
387 | checkpoint); | |||
388 | break; | |||
389 | ||||
390 | case cop_totals: | |||
391 | compute_duration (); | |||
392 | print_total_stats (); | |||
393 | } | |||
394 | } | |||
395 | } | |||
396 | ||||
397 | void | |||
398 | checkpoint_flush_actions (void) | |||
399 | { | |||
400 | struct checkpoint_action *p; | |||
401 | ||||
402 | for (p = checkpoint_action; p; p = p->next) | |||
403 | { | |||
404 | switch (p->opcode) | |||
405 | { | |||
406 | case cop_ttyout: | |||
407 | if (tty && tty_cleanup) | |||
408 | { | |||
409 | long w = getwidth (tty); | |||
410 | while (w--) | |||
411 | fputc (' ', tty)fputc_unlocked (' ',tty); | |||
412 | fputc ('\r', tty)fputc_unlocked ('\r',tty); | |||
413 | fflush (tty)fflush_unlocked (tty); | |||
414 | } | |||
415 | break; | |||
416 | default: | |||
417 | /* nothing */; | |||
418 | } | |||
419 | } | |||
420 | } | |||
421 | ||||
422 | void | |||
423 | checkpoint_run (bool_Bool do_write) | |||
424 | { | |||
425 | if (checkpoint_option && !(++checkpoint % checkpoint_option)) | |||
| ||||
426 | run_checkpoint_actions (do_write); | |||
427 | } | |||
428 | ||||
429 | void | |||
430 | checkpoint_finish (void) | |||
431 | { | |||
432 | if (checkpoint_option) | |||
433 | { | |||
434 | checkpoint_flush_actions (); | |||
435 | if (tty) | |||
436 | fclose (tty); | |||
437 | } | |||
438 | } |