/* Checkpoint management for tar.
Copyright 2007, 2013 Free Software Foundation, Inc.
This file is part of GNU tar.
GNU tar is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
GNU tar is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include
#include "common.h"
enum checkpoint_opcode
{
cop_dot,
cop_bell,
cop_echo,
cop_ttyout,
cop_sleep,
cop_exec
};
struct checkpoint_action
{
struct checkpoint_action *next;
enum checkpoint_opcode opcode;
union
{
time_t time;
char *command;
} v;
};
/* Checkpointing counter */
static unsigned checkpoint;
/* List of checkpoint actions */
static struct checkpoint_action *checkpoint_action, *checkpoint_action_tail;
static struct checkpoint_action *
alloc_action (enum checkpoint_opcode opcode)
{
struct checkpoint_action *p = xzalloc (sizeof *p);
if (checkpoint_action_tail)
checkpoint_action_tail->next = p;
else
checkpoint_action = p;
checkpoint_action_tail = p;
p->opcode = opcode;
return p;
}
static char *
copy_string_unquote (const char *str)
{
char *output = xstrdup (str);
size_t len = strlen (output);
if ((*output == '"' || *output == '\'')
&& output[len-1] == *output)
{
memmove (output, output+1, len-2);
output[len-2] = 0;
}
unquote_string (output);
return output;
}
void
checkpoint_compile_action (const char *str)
{
struct checkpoint_action *act;
if (strcmp (str, ".") == 0 || strcmp (str, "dot") == 0)
alloc_action (cop_dot);
else if (strcmp (str, "bell") == 0)
alloc_action (cop_bell);
else if (strcmp (str, "echo") == 0)
alloc_action (cop_echo);
else if (strncmp (str, "echo=", 5) == 0)
{
act = alloc_action (cop_echo);
act->v.command = copy_string_unquote (str + 5);
}
else if (strncmp (str, "exec=", 5) == 0)
{
act = alloc_action (cop_exec);
act->v.command = copy_string_unquote (str + 5);
}
else if (strncmp (str, "ttyout=", 7) == 0)
{
act = alloc_action (cop_ttyout);
act->v.command = copy_string_unquote (str + 7);
}
else if (strncmp (str, "sleep=", 6) == 0)
{
char *p;
time_t n = strtoul (str+6, &p, 10);
if (*p)
FATAL_ERROR ((0, 0, _("%s: not a valid timeout"), str));
act = alloc_action (cop_sleep);
act->v.time = n;
}
else
FATAL_ERROR ((0, 0, _("%s: unknown checkpoint action"), str));
}
void
checkpoint_finish_compile (void)
{
if (checkpoint_option)
{
if (!checkpoint_action)
/* Provide a historical default */
checkpoint_compile_action ("echo");
}
else if (checkpoint_action)
/* Otherwise, set default checkpoint rate */
checkpoint_option = DEFAULT_CHECKPOINT;
}
static char *
expand_checkpoint_string (const char *input, bool do_write, unsigned cpn)
{
const char *opstr = do_write ? gettext ("write") : gettext ("read");
size_t opstrlen = strlen (opstr);
char uintbuf[UINTMAX_STRSIZE_BOUND];
char *cps = STRINGIFY_BIGINT (cpn, uintbuf);
size_t cpslen = strlen (cps);
const char *ip;
char *op;
char *output;
size_t outlen = strlen (input); /* Initial guess */
/* Fix the initial length guess */
for (ip = input; (ip = strchr (ip, '%')) != NULL; )
{
switch (ip[1])
{
case 'u':
outlen += cpslen - 2;
break;
case 's':
outlen += opstrlen - 2;
}
ip++;
}
output = xmalloc (outlen + 1);
for (ip = input, op = output; *ip; )
{
if (*ip == '%')
{
switch (*++ip)
{
case 'u':
op = stpcpy (op, cps);
break;
case 's':
op = stpcpy (op, opstr);
break;
default:
*op++ = '%';
*op++ = *ip;
break;
}
ip++;
}
else
*op++ = *ip++;
}
*op = 0;
return output;
}
static void
run_checkpoint_actions (bool do_write)
{
struct checkpoint_action *p;
FILE *tty = NULL;
for (p = checkpoint_action; p; p = p->next)
{
switch (p->opcode)
{
case cop_dot:
fputc ('.', stdlis);
fflush (stdlis);
break;
case cop_bell:
if (!tty)
tty = fopen ("/dev/tty", "w");
if (tty)
{
fputc ('\a', tty);
fflush (tty);
}
break;
case cop_echo:
{
char *tmp;
const char *str = p->v.command;
if (!str)
{
if (do_write)
/* TRANSLATORS: This is a "checkpoint of write operation",
*not* "Writing a checkpoint".
E.g. in Spanish "Punto de comprobaci@'on de escritura",
*not* "Escribiendo un punto de comprobaci@'on" */
str = gettext ("Write checkpoint %u");
else
/* TRANSLATORS: This is a "checkpoint of read operation",
*not* "Reading a checkpoint".
E.g. in Spanish "Punto de comprobaci@'on de lectura",
*not* "Leyendo un punto de comprobaci@'on" */
str = gettext ("Read checkpoint %u");
}
tmp = expand_checkpoint_string (str, do_write, checkpoint);
WARN ((0, 0, "%s", tmp));
free (tmp);
}
break;
case cop_ttyout:
if (!tty)
tty = fopen ("/dev/tty", "w");
if (tty)
{
char *tmp = expand_checkpoint_string (p->v.command, do_write,
checkpoint);
fprintf (tty, "%s", tmp);
fflush (tty);
free (tmp);
}
break;
case cop_sleep:
sleep (p->v.time);
break;
case cop_exec:
sys_exec_checkpoint_script (p->v.command,
archive_name_cursor[0],
checkpoint);
break;
}
}
if (tty)
fclose (tty);
}
void
checkpoint_run (bool do_write)
{
if (checkpoint_option && !(++checkpoint % checkpoint_option))
run_checkpoint_actions (do_write);
}