checkpoint.c 8.8 KB

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