script: add --echo

It some cases it makes sense to disable ECHO flag also when script
used in pipe. This new option allows to keep full control in user's
hands.

Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
Karel Zak 2019-11-25 12:39:52 +01:00
parent a06b278bd3
commit 1eee1acb24
5 changed files with 51 additions and 23 deletions

View File

@ -76,14 +76,14 @@ struct ul_pty {
struct timeval next_callback_time;
unsigned int isterm:1, /* is stdin terminal? */
keep_slave_echo:1; /* keep ECHO on stdin */
slave_echo:1; /* keep ECHO on stdin */
};
void ul_pty_init_debug(int mask);
struct ul_pty *ul_new_pty(int is_stdin_tty);
void ul_free_pty(struct ul_pty *pty);
void ul_pty_keep_slave_echo(struct ul_pty *pty, int enable);
void ul_pty_slave_echo(struct ul_pty *pty, int enable);
int ul_pty_get_delivered_signal(struct ul_pty *pty);
void ul_pty_set_callback_data(struct ul_pty *pty, void *data);

View File

@ -71,10 +71,10 @@ void ul_free_pty(struct ul_pty *pty)
free(pty);
}
void ul_pty_keep_slave_echo(struct ul_pty *pty, int enable)
void ul_pty_slave_echo(struct ul_pty *pty, int enable)
{
assert(pty);
pty->keep_slave_echo = enable ? 1 : 0;
pty->slave_echo = enable ? 1 : 0;
}
int ul_pty_get_delivered_signal(struct ul_pty *pty)
@ -172,20 +172,28 @@ int ul_pty_setup(struct ul_pty *pty)
/* set the current terminal to raw mode; pty_cleanup() reverses this change on exit */
slave_attrs = pty->stdin_attrs;
cfmakeraw(&slave_attrs);
slave_attrs.c_lflag &= ~ECHO;
if (pty->slave_echo)
slave_attrs.c_lflag |= ECHO;
else
slave_attrs.c_lflag &= ~ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &slave_attrs);
} else {
DBG(SETUP, ul_debugobj(pty, "create for non-terminal"));
rc = openpty(&pty->master, &pty->slave, NULL, NULL, NULL);
if (!rc) {
tcgetattr(pty->slave, &slave_attrs);
if (!pty->keep_slave_echo) {
slave_attrs.c_lflag &= ~ECHO;
tcsetattr(pty->slave, TCSANOW, &slave_attrs);
}
} else
rc = openpty(&pty->master, &pty->slave, NULL, NULL, NULL);
if (rc)
goto done;
tcgetattr(pty->slave, &slave_attrs);
if (pty->slave_echo)
slave_attrs.c_lflag |= ECHO;
else
slave_attrs.c_lflag &= ~ECHO;
tcsetattr(pty->slave, TCSANOW, &slave_attrs);
}
sigfillset(&ourset);

View File

@ -88,6 +88,13 @@ rather than an interactive shell. This makes it easy for a script to capture
the output of a program that behaves differently when its stdout is not a
tty.
.TP
\fB\-E\fR, \fB\-\-echo\fR \fIwhen\fR
This option controls the ECHO flag for pseudoterminal within the session. The
supported modes are always, never, or auto. The default is "auto" -- in this
case, ECHO is disabled if the current standard input is a terminal to avoid
double-echo, and enabled if standard input is not terminal (for example pipe:
"echo date | script") to avoid missing input in the session log.
.TP
\fB\-e\fR, \fB\-\-return\fR
Return the exit code of the child process. Uses the same format as bash
termination on signal termination exit code is 128+n. The exit code of

View File

@ -208,6 +208,7 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" -e, --return return exit code of the child process\n"), out);
fputs(_(" -f, --flush run flush after each write\n"), out);
fputs(_(" --force use output file even when it is a link\n"), out);
fputs(_(" -E, --echo <when> echo input (auto, always or never)\n"), out);
fputs(_(" -o, --output-limit <size> terminate if output files exceed size\n"), out);
fputs(_(" -q, --quiet be quiet\n"), out);
@ -720,7 +721,7 @@ int main(int argc, char **argv)
.in = { .ident = 'I' },
};
struct ul_pty_callbacks *cb;
int ch, format = 0, caught_signal = 0, rc = 0;
int ch, format = 0, caught_signal = 0, rc = 0, echo = 0;
const char *outfile = NULL, *infile = NULL;
const char *timingfile = NULL, *shell = NULL, *command = NULL;
@ -729,6 +730,7 @@ int main(int argc, char **argv)
static const struct option longopts[] = {
{"append", no_argument, NULL, 'a'},
{"command", required_argument, NULL, 'c'},
{"echo", required_argument, NULL, 'E'},
{"return", no_argument, NULL, 'e'},
{"flush", no_argument, NULL, 'f'},
{"force", no_argument, NULL, FORCE_OPTION,},
@ -765,7 +767,14 @@ int main(int argc, char **argv)
script_init_debug();
ON_DBG(PTY, ul_pty_init_debug(0xFFFF));
while ((ch = getopt_long(argc, argv, "aB:c:efI:O:o:qm:T:t::Vh", longopts, NULL)) != -1) {
/* The default is to keep ECHO flag when stdin is not terminal. We need
* it to make stdin (in case of "echo foo | script") log-able and
* visiable on terminal, and for backward compatibility.
*/
ctl.isterm = isatty(STDIN_FILENO);
echo = ctl.isterm ? 0 : 1;
while ((ch = getopt_long(argc, argv, "aB:c:eE:fI:O:o:qm:T:t::Vh", longopts, NULL)) != -1) {
err_exclusive_options(ch, longopts, excl, excl_st);
@ -776,6 +785,16 @@ int main(int argc, char **argv)
case 'c':
command = optarg;
break;
case 'E':
if (strcmp(optarg, "auto") == 0)
; /* keep default */
else if (strcmp(optarg, "never") == 0)
echo = 0;
else if (strcmp(optarg, "always") == 0)
echo = 1;
else
errx(EXIT_FAILURE, _("unssuported echo mode: '%s'"), optarg);
break;
case 'e':
ctl.rc_wanted = 1;
break;
@ -870,17 +889,11 @@ int main(int argc, char **argv)
if (!shell)
shell = _PATH_BSHELL;
ctl.isterm = isatty(STDIN_FILENO);
ctl.pty = ul_new_pty(ctl.isterm);
if (!ctl.pty)
err(EXIT_FAILURE, "failed to allocate PTY handler");
if (!ctl.isterm)
/* We keep ECHO flag for 'echo "date" | script' otherwise the
* input is no visible (output is completely comtroled by
* shell/command, we do not write anything there).
*/
ul_pty_keep_slave_echo(ctl.pty, 1);
ul_pty_slave_echo(ctl.pty, echo);
ul_pty_set_callback_data(ctl.pty, (void *) &ctl);
cb = ul_pty_get_callbacks(ctl.pty);

View File

@ -291,7 +291,7 @@ main(int argc, char *argv[])
if (!isatty(STDIN_FILENO))
/* We keep ECHO flag for compatibility with script(1) */
ul_pty_keep_slave_echo(ss.pty, 1);
ul_pty_slave_echo(ss.pty, 1);
if (ul_pty_setup(ss.pty))
err(EXIT_FAILURE, _("failed to create pseudo-terminal"));