2005-10-08 19:20:51 +08:00
|
|
|
/** \file io.c
|
|
|
|
|
|
|
|
Utilities for io redirection.
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2005-10-08 19:20:51 +08:00
|
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
|
2006-02-28 21:17:16 +08:00
|
|
|
|
2005-10-08 19:20:51 +08:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <wchar.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/types.h>
|
2012-08-15 15:57:56 +08:00
|
|
|
#include <set>
|
|
|
|
#include <algorithm>
|
2006-08-10 06:53:38 +08:00
|
|
|
|
|
|
|
#ifdef HAVE_SYS_TERMIOS_H
|
|
|
|
#include <sys/termios.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_IOCTL_H
|
2005-10-08 19:20:51 +08:00
|
|
|
#include <sys/ioctl.h>
|
2006-08-10 06:53:38 +08:00
|
|
|
#endif
|
|
|
|
|
2005-10-08 19:20:51 +08:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
#if HAVE_NCURSES_H
|
|
|
|
#include <ncurses.h>
|
|
|
|
#else
|
|
|
|
#include <curses.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if HAVE_TERMIO_H
|
|
|
|
#include <termio.h>
|
|
|
|
#endif
|
|
|
|
|
2006-01-19 20:22:07 +08:00
|
|
|
#if HAVE_TERM_H
|
2005-10-08 19:20:51 +08:00
|
|
|
#include <term.h>
|
2006-01-19 20:22:07 +08:00
|
|
|
#elif HAVE_NCURSES_TERM_H
|
|
|
|
#include <ncurses/term.h>
|
|
|
|
#endif
|
2005-10-08 19:20:51 +08:00
|
|
|
|
2006-02-28 21:17:16 +08:00
|
|
|
#include "fallback.h"
|
2005-10-08 19:20:51 +08:00
|
|
|
#include "util.h"
|
2006-02-28 21:17:16 +08:00
|
|
|
|
2005-10-08 19:20:51 +08:00
|
|
|
#include "wutil.h"
|
|
|
|
#include "exec.h"
|
|
|
|
#include "common.h"
|
|
|
|
#include "io.h"
|
2006-07-20 06:55:49 +08:00
|
|
|
|
2005-10-08 19:20:51 +08:00
|
|
|
|
2012-11-19 08:30:30 +08:00
|
|
|
void io_buffer_read(io_data_t *d)
|
2005-10-08 19:20:51 +08:00
|
|
|
{
|
2012-11-19 08:30:30 +08:00
|
|
|
exec_close(d->param1.pipe_fd[1]);
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2012-11-19 08:30:30 +08:00
|
|
|
if (d->io_mode == IO_BUFFER)
|
2012-11-18 18:23:22 +08:00
|
|
|
{
|
2012-11-19 08:30:30 +08:00
|
|
|
/* if( fcntl( d->param1.pipe_fd[0], F_SETFL, 0 ) )
|
|
|
|
{
|
|
|
|
wperror( L"fcntl" );
|
|
|
|
return;
|
|
|
|
} */
|
|
|
|
debug(4, L"io_buffer_read: blocking read on fd %d", d->param1.pipe_fd[0]);
|
|
|
|
while (1)
|
2012-11-18 18:23:22 +08:00
|
|
|
{
|
2012-11-19 08:30:30 +08:00
|
|
|
char b[4096];
|
|
|
|
long l;
|
|
|
|
l=read_blocked(d->param1.pipe_fd[0], b, 4096);
|
|
|
|
if (l==0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (l<0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
exec_read_io_buffer is only called on jobs that have
|
|
|
|
exited, and will therefore never block. But a broken
|
|
|
|
pipe seems to cause some flags to reset, causing the
|
|
|
|
EOF flag to not be set. Therefore, EAGAIN is ignored
|
|
|
|
and we exit anyway.
|
|
|
|
*/
|
|
|
|
if (errno != EAGAIN)
|
|
|
|
{
|
|
|
|
debug(1,
|
|
|
|
_(L"An error occured while reading output from code block on file descriptor %d"),
|
|
|
|
d->param1.pipe_fd[0]);
|
|
|
|
wperror(L"io_buffer_read");
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2012-11-19 08:30:30 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
d->out_buffer_append(b, l);
|
|
|
|
}
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
}
|
2005-10-08 19:20:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-11-19 08:30:30 +08:00
|
|
|
io_data_t *io_buffer_create(bool is_input)
|
2005-10-08 19:20:51 +08:00
|
|
|
{
|
2012-08-15 15:57:56 +08:00
|
|
|
bool success = true;
|
2012-11-19 08:30:30 +08:00
|
|
|
io_data_t *buffer_redirect = new io_data_t;
|
|
|
|
buffer_redirect->out_buffer_create();
|
|
|
|
buffer_redirect->io_mode = IO_BUFFER;
|
|
|
|
buffer_redirect->is_input = is_input ? true : false;
|
|
|
|
buffer_redirect->fd=is_input?0:1;
|
|
|
|
|
|
|
|
if (exec_pipe(buffer_redirect->param1.pipe_fd) == -1)
|
|
|
|
{
|
|
|
|
debug(1, PIPE_ERROR);
|
|
|
|
wperror(L"pipe");
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
else if (fcntl(buffer_redirect->param1.pipe_fd[0],
|
|
|
|
F_SETFL,
|
|
|
|
O_NONBLOCK))
|
|
|
|
{
|
|
|
|
debug(1, PIPE_ERROR);
|
|
|
|
wperror(L"fcntl");
|
|
|
|
success = false;
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2012-08-15 15:57:56 +08:00
|
|
|
if (! success)
|
|
|
|
{
|
|
|
|
delete buffer_redirect;
|
|
|
|
buffer_redirect = NULL;
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2012-11-19 08:30:30 +08:00
|
|
|
return buffer_redirect;
|
2005-10-08 19:20:51 +08:00
|
|
|
}
|
|
|
|
|
2013-01-07 23:04:55 +08:00
|
|
|
void io_buffer_destroy(const shared_ptr<io_data_t> &io_buffer)
|
2005-10-08 19:20:51 +08:00
|
|
|
{
|
2006-11-12 20:16:13 +08:00
|
|
|
|
2012-11-19 08:30:30 +08:00
|
|
|
/**
|
|
|
|
If this is an input buffer, then io_read_buffer will not have
|
|
|
|
been called, and we need to close the output fd as well.
|
|
|
|
*/
|
|
|
|
if (io_buffer->is_input)
|
|
|
|
{
|
|
|
|
exec_close(io_buffer->param1.pipe_fd[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
exec_close(io_buffer->param1.pipe_fd[0]);
|
|
|
|
|
|
|
|
/*
|
|
|
|
Dont free fd for writing. This should already be free'd before
|
|
|
|
calling exec_read_io_buffer on the buffer
|
|
|
|
*/
|
2005-10-08 19:20:51 +08:00
|
|
|
}
|
|
|
|
|
2013-01-07 23:04:55 +08:00
|
|
|
void io_chain_t::remove(const shared_ptr<const io_data_t> &element)
|
2012-08-15 15:57:56 +08:00
|
|
|
{
|
|
|
|
// See if you can guess why std::find doesn't work here
|
|
|
|
for (io_chain_t::iterator iter = this->begin(); iter != this->end(); ++iter)
|
|
|
|
{
|
|
|
|
if (*iter == element)
|
|
|
|
{
|
|
|
|
this->erase(iter);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-10-08 19:20:51 +08:00
|
|
|
|
2012-08-20 05:09:39 +08:00
|
|
|
void io_chain_t::duplicate_prepend(const io_chain_t &src)
|
2005-10-08 19:20:51 +08:00
|
|
|
{
|
2013-01-07 23:04:55 +08:00
|
|
|
/* Prepend a duplicate of src before this. */
|
|
|
|
this->insert(this->begin(), src.begin(), src.end());
|
2005-10-08 19:20:51 +08:00
|
|
|
}
|
|
|
|
|
2012-08-15 15:57:56 +08:00
|
|
|
void io_chain_t::destroy()
|
2005-10-08 19:20:51 +08:00
|
|
|
{
|
2012-08-15 15:57:56 +08:00
|
|
|
this->clear();
|
2005-10-08 19:20:51 +08:00
|
|
|
}
|
|
|
|
|
2013-01-07 23:04:55 +08:00
|
|
|
void io_remove(io_chain_t &list, const shared_ptr<const io_data_t> &element)
|
2005-10-08 19:20:51 +08:00
|
|
|
{
|
2012-08-15 15:57:56 +08:00
|
|
|
list.remove(element);
|
|
|
|
}
|
2005-10-08 19:20:51 +08:00
|
|
|
|
2012-08-15 15:57:56 +08:00
|
|
|
void io_print(const io_chain_t &chain)
|
2005-10-08 19:20:51 +08:00
|
|
|
{
|
2012-08-15 15:57:56 +08:00
|
|
|
if (chain.empty())
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Empty chain %p\n", &chain);
|
|
|
|
return;
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2012-08-15 15:57:56 +08:00
|
|
|
fprintf(stderr, "Chain %p (%ld items):\n", &chain, (long)chain.size());
|
2012-11-19 08:30:30 +08:00
|
|
|
for (size_t i=0; i < chain.size(); i++)
|
|
|
|
{
|
2013-01-07 23:04:55 +08:00
|
|
|
const shared_ptr<const io_data_t> &io = chain.at(i);
|
2012-08-15 15:57:56 +08:00
|
|
|
fprintf(stderr, "\t%lu: fd:%d, input:%s, ", (unsigned long)i, io->fd, io->is_input ? "yes" : "no");
|
|
|
|
switch (io->io_mode)
|
|
|
|
{
|
2012-11-19 16:31:03 +08:00
|
|
|
case IO_FILE:
|
|
|
|
fprintf(stderr, "file (%s)\n", io->filename_cstr);
|
|
|
|
break;
|
|
|
|
case IO_PIPE:
|
|
|
|
fprintf(stderr, "pipe {%d, %d}\n", io->param1.pipe_fd[0], io->param1.pipe_fd[1]);
|
|
|
|
break;
|
|
|
|
case IO_FD:
|
|
|
|
fprintf(stderr, "FD map %d -> %d\n", io->param1.old_fd, io->fd);
|
|
|
|
break;
|
|
|
|
case IO_BUFFER:
|
|
|
|
fprintf(stderr, "buffer %p (size %lu)\n", io->out_buffer_ptr(), io->out_buffer_size());
|
|
|
|
break;
|
|
|
|
case IO_CLOSE:
|
|
|
|
fprintf(stderr, "close %d\n", io->fd);
|
|
|
|
break;
|
2012-08-15 15:57:56 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-10-08 19:20:51 +08:00
|
|
|
|
2012-11-19 08:30:30 +08:00
|
|
|
void io_duplicate_prepend(const io_chain_t &src, io_chain_t &dst)
|
2012-08-15 15:57:56 +08:00
|
|
|
{
|
2012-08-20 05:09:39 +08:00
|
|
|
return dst.duplicate_prepend(src);
|
2005-10-08 19:20:51 +08:00
|
|
|
}
|
|
|
|
|
2012-11-18 18:23:22 +08:00
|
|
|
void io_chain_destroy(io_chain_t &chain)
|
2012-08-15 15:57:56 +08:00
|
|
|
{
|
|
|
|
chain.destroy();
|
|
|
|
}
|
2006-05-15 06:29:05 +08:00
|
|
|
|
2012-08-15 15:57:56 +08:00
|
|
|
/* Return the last IO for the given fd */
|
2013-01-07 23:04:55 +08:00
|
|
|
shared_ptr<const io_data_t> io_chain_t::get_io_for_fd(int fd) const
|
2005-10-09 19:48:16 +08:00
|
|
|
{
|
2012-08-15 15:57:56 +08:00
|
|
|
size_t idx = this->size();
|
|
|
|
while (idx--)
|
|
|
|
{
|
2013-01-07 23:04:55 +08:00
|
|
|
const shared_ptr<const io_data_t> &data = this->at(idx);
|
2012-11-19 08:30:30 +08:00
|
|
|
if (data->fd == fd)
|
|
|
|
{
|
2012-08-15 15:57:56 +08:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
}
|
2013-01-07 23:04:55 +08:00
|
|
|
return shared_ptr<const io_data_t>();
|
2012-08-15 15:57:56 +08:00
|
|
|
}
|
2005-10-09 19:48:16 +08:00
|
|
|
|
2013-01-07 23:04:55 +08:00
|
|
|
shared_ptr<io_data_t> io_chain_t::get_io_for_fd(int fd)
|
2012-08-15 15:57:56 +08:00
|
|
|
{
|
|
|
|
size_t idx = this->size();
|
|
|
|
while (idx--)
|
|
|
|
{
|
2013-01-07 23:04:55 +08:00
|
|
|
const shared_ptr<io_data_t> &data = this->at(idx);
|
2012-11-19 08:30:30 +08:00
|
|
|
if (data->fd == fd)
|
|
|
|
{
|
2012-08-15 15:57:56 +08:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
}
|
2013-01-07 23:04:55 +08:00
|
|
|
return shared_ptr<io_data_t>();
|
2012-08-15 15:57:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* The old function returned the last match, so we mimic that. */
|
2013-01-07 23:04:55 +08:00
|
|
|
shared_ptr<const io_data_t> io_chain_get(const io_chain_t &src, int fd)
|
2012-08-15 15:57:56 +08:00
|
|
|
{
|
|
|
|
return src.get_io_for_fd(fd);
|
|
|
|
}
|
|
|
|
|
2013-01-07 23:04:55 +08:00
|
|
|
shared_ptr<io_data_t> io_chain_get(io_chain_t &src, int fd)
|
2012-08-15 15:57:56 +08:00
|
|
|
{
|
|
|
|
return src.get_io_for_fd(fd);
|
|
|
|
}
|
|
|
|
|
2013-01-07 23:04:55 +08:00
|
|
|
io_chain_t::io_chain_t(const shared_ptr<io_data_t> &data) :
|
|
|
|
std::vector<shared_ptr<io_data_t> >(1, data)
|
2012-08-15 15:57:56 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-01-07 23:04:55 +08:00
|
|
|
io_chain_t::io_chain_t() : std::vector<shared_ptr<io_data_t> >()
|
2012-08-15 15:57:56 +08:00
|
|
|
{
|
2005-10-09 19:48:16 +08:00
|
|
|
}
|