[Intel-gfx] [i-g-t PATCH 1/4] lib/igt_core: Add igt_exec helpers
Petri Latvala
petri.latvala at intel.com
Tue May 9 10:18:16 UTC 2017
On Thu, Apr 20, 2017 at 11:13:45AM +0300, Abdiel Janulgue wrote:
> Support executing external processes with the goal of capturing its
> standard streams to the igt logging infrastructure in addition to its
> exit status.
>
> Cc: Daniel Vetter <daniel.vetter at ffwll.ch>
> Cc: Petri Latvala <petri.latvala at intel.com>
> Signed-off-by: Abdiel Janulgue <abdiel.janulgue at linux.intel.com>
> ---
> lib/igt_core.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> lib/igt_core.h | 3 ++
> 2 files changed, 154 insertions(+)
>
> diff --git a/lib/igt_core.c b/lib/igt_core.c
> index 403b942..8a7ba0d 100644
> --- a/lib/igt_core.c
> +++ b/lib/igt_core.c
> @@ -2073,3 +2073,154 @@ FILE *__igt_fopen_data(const char* igt_srcdir, const char* igt_datadir,
>
> return fp;
> }
> +
> +struct output_pipe {
> + int output_fd;
> + int save_fd;
> + int read_fd;
> + int write_fd;
> + bool redirected;
> + enum igt_log_level log_level;
> +};
> +
> +static bool redirect_output(struct output_pipe *p, int output_fd,
> + enum igt_log_level level)
> +{
> + int fds[2], flags;
> +
> + if (pipe(fds) == -1)
> + return false;
If this succeeds....
> +
> + if ((flags = fcntl(fds[0], F_GETFL) == -1))
> + return false;
> +
> + flags |= O_NONBLOCK;
> + if (fcntl(fds[0], F_SETFL, flags) == -1)
> + return false;
> +
> + /* save output */
> + if ((p->save_fd = dup(output_fd)) == -1)
> + return false;
> +
> + /* Redirect output to our buffer */
> + if (dup2(fds[1], output_fd) == -1)
> + return false;
.... and these don't, the pipe fds are never closed.
Same for the save_fd if the dup2 fails, if the caller of this function
doesn't call unredirect_output, as the return value might direct it.
> +
> + p->output_fd = output_fd;
> + p->read_fd = fds[0];
> + p->write_fd = fds[1];
> + p->redirected = true;
> + p->log_level = level;
> +
> + return true;
> +}
> +
> +static bool unredirect_output(struct output_pipe *p)
> +{
> + close(p->write_fd);
> + if (dup2(p->save_fd, p->output_fd) == -1)
> + return false;
> + close(p->save_fd);
> +
> + p->redirected = false;
> +
> + return true;
> +}
> +
> +/**
> + * igt_exec:
> + *
> + * Executes the shell command specified in @command and capture its stdout and
> + * stderr to igt_log or igt_warn respectively.
> + *
> + * Returns: The exit status of the executed process. -1 for failure.
> + */
> +int igt_exec(const char *command)
> +{
> +#define OUT 0
> +#define ERR 1
> + struct output_pipe op[2];
> + int i, sel_fd, status;
> + fd_set fds;
> + struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 };
> + char buf[PIPE_BUF];
> +
> + if (!redirect_output(&op[OUT], STDOUT_FILENO, IGT_LOG_INFO))
> + return -1;
> + if (!redirect_output(&op[ERR], STDERR_FILENO, IGT_LOG_WARN))
> + return -1;
If redirect_output for stderr returns false, unredirect_output for
stdout will not get called.
> +
> + if ((status = system(command)) == -1)
> + return -1;
> +
> + FD_ZERO(&fds);
> + FD_SET(op[OUT].read_fd, &fds);
> + FD_SET(op[ERR].read_fd, &fds);
> +
> + sel_fd = max(op[OUT].read_fd, op[ERR].read_fd);
> + if (select(sel_fd + 1, &fds, NULL, NULL, &timeout) == -1)
> + return -1;
> +
> + for (i = 0; i < ARRAY_SIZE(op); i++) {
> + struct output_pipe *current = &op[i];
> +
> + if (!FD_ISSET(current->read_fd, &fds)) {
> + close(current->read_fd);
> + if (!unredirect_output(current))
> + return -1;
> + continue;
> + }
> +
> + memset(buf, 0, sizeof(buf));
> + while (read(current->read_fd, buf, sizeof(buf)) > 0) {
> + if (current->redirected) {
> + if (!unredirect_output(current))
> + return -1;
> + }
> + igt_log(IGT_LOG_DOMAIN, current->log_level,
> + "[cmd] %s", buf);
> + memset(buf, 0, sizeof(buf));
> + }
> + close(current->read_fd);
> + }
Unredirect_output calls for both pipes need to be called on all exit
paths.
In redirect_output you set only the read fd of the pipe() pair to
O_NONBLOCK. That will make the executed command block on its writes
indefinitely if it prints more than whatsitnow, 64kB?
> +
> + return WEXITSTATUS(status);
> +}
> +
> +/**
> + * igt_exec_quiet:
> + * Similar to igt_exec(), except redirect output to /dev/null
> + *
> + * Returns: The exit status of the executed process. -1 for failure.
> + */
> +int igt_exec_quiet(const char *command)
> +{
> + int stderr_fd_copy, stdout_fd_copy, status, nullfd;
> +
> + /* redirect */
> + if ((nullfd = open("/dev/null", O_WRONLY)) == -1)
> + return -1;
> + if ((stdout_fd_copy = dup(STDOUT_FILENO)) == -1)
> + return -1;
> + if ((stderr_fd_copy = dup(STDERR_FILENO)) == -1)
> + return -1;
> +
> + if (dup2(nullfd, STDOUT_FILENO) == -1)
> + return -1;
> + if (dup2(nullfd, STDERR_FILENO) == -1)
> + return -1;
> +
> + if ((status = system(command)) == -1)
> + return -1;
> +
> + /* restore */
> + if (dup2(stdout_fd_copy, STDOUT_FILENO) == -1)
> + return -1;
> + if (dup2(stderr_fd_copy, STDERR_FILENO) == -1)
> + return -1;
> +
> + close(stdout_fd_copy);
> + close(stderr_fd_copy);
Same fd leaks here, all exit paths should close the opened fds.
--
Petri Latvala
> +
> + return WEXITSTATUS(status);
> +}
> diff --git a/lib/igt_core.h b/lib/igt_core.h
> index 51b98d8..6d4cf60 100644
> --- a/lib/igt_core.h
> +++ b/lib/igt_core.h
> @@ -918,4 +918,7 @@ FILE *__igt_fopen_data(const char* igt_srcdir, const char* igt_datadir,
> #define igt_fopen_data(filename) \
> __igt_fopen_data(IGT_SRCDIR, IGT_DATADIR, filename)
>
> +int igt_exec(const char *command);
> +int igt_exec_quiet(const char *command);
> +
> #endif /* IGT_CORE_H */
> --
> 2.7.4
>
More information about the Intel-gfx
mailing list