Skip to content

Commit e849bea

Browse files
Initial work towards various IO cleanups with an eye to fixing fish-shell#110
1 parent 2979d3b commit e849bea

File tree

10 files changed

+81
-32
lines changed

10 files changed

+81
-32
lines changed

exec.cpp

+18-12
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949
#include "expand.h"
5050
#include "signal.h"
5151

52-
5352
#include "parse_util.h"
5453

5554
/**
@@ -490,7 +489,7 @@ static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t &out_chain, s
490489
static void internal_exec_helper(parser_t &parser,
491490
const wchar_t *def,
492491
enum block_type_t block_type,
493-
io_chain_t &ios)
492+
const io_chain_t &ios)
494493
{
495494
io_chain_t morphed_chain;
496495
std::vector<int> opened_fds;
@@ -540,9 +539,10 @@ static bool can_use_posix_spawn_for_job(const job_t *job, const process_t *proce
540539

541540
/* Now see if we have a redirection involving a file. The only one we allow is /dev/null, which we assume will not fail. */
542541
bool result = true;
543-
for (size_t idx = 0; idx < job->io.size(); idx++)
542+
const io_chain_t &ios = job->io_chain();
543+
for (size_t idx = 0; idx < ios.size(); idx++)
544544
{
545-
const shared_ptr<const io_data_t> &io = job->io.at(idx);
545+
const shared_ptr<const io_data_t> &io = ios.at(idx);
546546
if (redirection_is_to_real_file(io.get()))
547547
{
548548
result = false;
@@ -621,9 +621,10 @@ void exec(parser_t &parser, job_t *j)
621621
}
622622

623623
const io_buffer_t *input_redirect = NULL;
624-
for (size_t idx = 0; idx < j->io.size(); idx++)
624+
const io_chain_t &ios = j->io_chain();
625+
for (size_t idx = 0; idx < ios.size(); idx++)
625626
{
626-
const shared_ptr<io_data_t> &io = j->io.at(idx);
627+
const shared_ptr<io_data_t> &io = ios.at(idx);
627628

628629
if ((io->io_mode == IO_BUFFER))
629630
{
@@ -770,13 +771,14 @@ void exec(parser_t &parser, job_t *j)
770771
pipe_read.reset(new io_pipe_t(p->pipe_read_fd, true));
771772
/* Record the current read in pipe_read */
772773
pipe_read->pipe_fd[0] = pipe_current_read;
773-
j->io.push_back(pipe_read);
774+
j->append_io(pipe_read);
774775
}
775776

776777
if (p->next)
777778
{
778779
pipe_write.reset(new io_pipe_t(p->pipe_write_fd, false));
779-
j->io.push_back(pipe_write);
780+
j->append_io(pipe_write);
781+
780782
}
781783

782784
/*
@@ -821,6 +823,10 @@ void exec(parser_t &parser, job_t *j)
821823
pipe_next_read = local_pipe[0];
822824
}
823825

826+
//fprintf(stderr, "before IO: ");
827+
//io_print(j->io);
828+
829+
824830
switch (p->type)
825831
{
826832
case INTERNAL_FUNCTION:
@@ -873,13 +879,13 @@ void exec(parser_t &parser, job_t *j)
873879
}
874880
else
875881
{
876-
j->io.push_back(io_buffer);
882+
j->append_io(io_buffer);
877883
}
878884
}
879885

880886
if (! exec_error)
881887
{
882-
internal_exec_helper(parser, def.c_str(), TOP, j->io);
888+
internal_exec_helper(parser, def.c_str(), TOP, j->io_chain());
883889
}
884890

885891
parser.allow_function();
@@ -915,7 +921,7 @@ void exec(parser_t &parser, job_t *j)
915921
case INTERNAL_BUILTIN:
916922
{
917923
int builtin_stdin=0;
918-
int close_stdin=0;
924+
bool close_stdin = false;
919925

920926
/*
921927
If this is the first process, check the io
@@ -959,7 +965,7 @@ void exec(parser_t &parser, job_t *j)
959965
}
960966
else
961967
{
962-
close_stdin = 1;
968+
close_stdin = true;
963969
}
964970

965971
break;

io.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,12 @@ void io_chain_t::push_back(const shared_ptr<io_data_t> &element)
202202
std::vector<shared_ptr<io_data_t> >::push_back(element);
203203
}
204204

205+
void io_chain_t::push_front(const shared_ptr<io_data_t> &element)
206+
{
207+
assert(element.get() != NULL);
208+
this->insert(this->begin(), element);
209+
}
210+
205211
void io_remove(io_chain_t &list, const shared_ptr<const io_data_t> &element)
206212
{
207213
list.remove(element);

io.h

+1
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ class io_chain_t : public std::vector<shared_ptr<io_data_t> >
188188

189189
void remove(const shared_ptr<const io_data_t> &element);
190190
void push_back(const shared_ptr<io_data_t> &element);
191+
void push_front(const shared_ptr<io_data_t> &element);
191192

192193
shared_ptr<const io_data_t> get_io_for_fd(int fd) const;
193194
shared_ptr<io_data_t> get_io_for_fd(int fd);

parser.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -1559,7 +1559,7 @@ void parser_t::parse_job_argument_list(process_t *p,
15591559

15601560
if (new_io.get() != NULL)
15611561
{
1562-
j->io.push_back(new_io);
1562+
j->append_io(new_io);
15631563
}
15641564

15651565
}
@@ -2318,7 +2318,6 @@ static bool job_should_skip_elseif(const job_t *job, const block_t *current_bloc
23182318
void parser_t::eval_job(tokenizer_t *tok)
23192319
{
23202320
ASSERT_IS_MAIN_THREAD();
2321-
job_t *j;
23222321

23232322
int start_pos = job_start_pos = tok_get_pos(tok);
23242323
long long t1=0, t2=0, t3=0;
@@ -2341,7 +2340,7 @@ void parser_t::eval_job(tokenizer_t *tok)
23412340
{
23422341
case TOK_STRING:
23432342
{
2344-
j = this->job_create();
2343+
job_t *j = this->job_create();
23452344
job_set_flag(j, JOB_FOREGROUND, 1);
23462345
job_set_flag(j, JOB_TERMINAL, job_get_flag(j, JOB_CONTROL));
23472346
job_set_flag(j, JOB_TERMINAL, job_get_flag(j, JOB_CONTROL) \

postfork.cpp

+17-9
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
/** pipe error */
3838
#define LOCAL_PIPE_ERROR "An error occurred while setting up pipe"
3939

40+
static bool log_redirections = false;
41+
4042
/* Cover for debug_safe that can take an int. The format string should expect a %s */
4143
static void debug_safe_int(int level, const char *format, int val)
4244
{
@@ -164,11 +166,11 @@ static void free_redirected_fds_from_pipes(io_chain_t &io_chain)
164166
165167
\return 0 on sucess, -1 on failiure
166168
*/
167-
static int handle_child_io(io_chain_t &io_chain)
169+
static int handle_child_io(const io_chain_t &io_chain)
168170
{
169-
171+
//fprintf(stderr, "child IO for %d\n", getpid());
170172
close_unused_internal_pipes(io_chain);
171-
free_redirected_fds_from_pipes(io_chain);
173+
//free_redirected_fds_from_pipes(io_chain);
172174
for (size_t idx = 0; idx < io_chain.size(); idx++)
173175
{
174176
io_data_t *io = io_chain.at(idx).get();
@@ -183,6 +185,7 @@ static int handle_child_io(io_chain_t &io_chain)
183185
{
184186
case IO_CLOSE:
185187
{
188+
if (log_redirections) fprintf(stderr, "%d: close %d\n", getpid(), io->fd);
186189
if (close(io->fd))
187190
{
188191
debug_safe_int(0, "Failed to close file descriptor %s", io->fd);
@@ -232,13 +235,17 @@ static int handle_child_io(io_chain_t &io_chain)
232235

233236
case IO_FD:
234237
{
238+
int old_fd = static_cast<const io_fd_t *>(io)->old_fd;
239+
if (log_redirections) fprintf(stderr, "%d: fd dup %d to %d\n", getpid(), old_fd, io->fd);
240+
235241
/*
236242
This call will sometimes fail, but that is ok,
237243
this is just a precausion.
238244
*/
239245
close(io->fd);
240246

241-
if (dup2(static_cast<const io_fd_t *>(io)->old_fd, io->fd) == -1)
247+
248+
if (dup2(old_fd, io->fd) == -1)
242249
{
243250
debug_safe_int(1, FD_ERROR, io->fd);
244251
safe_perror("dup2");
@@ -262,6 +269,7 @@ static int handle_child_io(io_chain_t &io_chain)
262269
io->pipe_fd[0],
263270
io->pipe_fd[1]);
264271
*/
272+
if (log_redirections) fprintf(stderr, "%d: %s dup %d to %d\n", getpid(), io->io_mode == IO_BUFFER ? "buffer" : "pipe", io_pipe->pipe_fd[write_pipe_idx], io->fd);
265273
if (dup2(io_pipe->pipe_fd[write_pipe_idx], io->fd) != io->fd)
266274
{
267275
debug_safe(1, LOCAL_PIPE_ERROR);
@@ -295,7 +303,7 @@ int setup_child_process(job_t *j, process_t *p)
295303

296304
if (ok)
297305
{
298-
ok = (0 == handle_child_io(j->io));
306+
ok = (0 == handle_child_io(j->io_chain()));
299307
if (p != 0 && ! ok)
300308
{
301309
exit_without_destructors(1);
@@ -436,19 +444,19 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_fil
436444
err = posix_spawnattr_setsigmask(attr, &sigmask);
437445

438446
/* Make sure that our pipes don't use an fd that the redirection itself wants to use */
439-
free_redirected_fds_from_pipes(j->io);
447+
//free_redirected_fds_from_pipes(j->io);
440448

441449
/* Close unused internal pipes */
442450
std::vector<int> files_to_close;
443-
get_unused_internal_pipes(files_to_close, j->io);
451+
get_unused_internal_pipes(files_to_close, j->io_chain());
444452
for (size_t i = 0; ! err && i < files_to_close.size(); i++)
445453
{
446454
err = posix_spawn_file_actions_addclose(actions, files_to_close.at(i));
447455
}
448456

449-
for (size_t idx = 0; idx < j->io.size(); idx++)
457+
for (size_t idx = 0; idx < j->io_chain().size(); idx++)
450458
{
451-
shared_ptr<const io_data_t> io = j->io.at(idx);
459+
shared_ptr<const io_data_t> io = j->io_chain().at(idx);
452460

453461
if (io->io_mode == IO_FD)
454462
{

proc.cpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -542,11 +542,11 @@ process_t::~process_t()
542542
job_t::job_t(job_id_t jobid) :
543543
command_str(),
544544
command_narrow(),
545+
io(),
545546
first_process(NULL),
546547
pgid(0),
547548
tmodes(),
548549
job_id(jobid),
549-
io(),
550550
flags(0)
551551
{
552552
}
@@ -876,9 +876,9 @@ static int select_try(job_t *j)
876876

877877
FD_ZERO(&fds);
878878

879-
for (size_t idx = 0; idx < j->io.size(); idx++)
879+
for (size_t idx = 0; idx < j->io_chain().size(); idx++)
880880
{
881-
const io_data_t *io = j->io.at(idx).get();
881+
const io_data_t *io = j->io_chain().at(idx).get();
882882
if (io->io_mode == IO_BUFFER)
883883
{
884884
CAST_INIT(const io_pipe_t *, io_pipe, io);
@@ -917,9 +917,9 @@ static void read_try(job_t *j)
917917
/*
918918
Find the last buffer, which is the one we want to read from
919919
*/
920-
for (size_t idx = 0; idx < j->io.size(); idx++)
920+
for (size_t idx = 0; idx < j->io_chain().size(); idx++)
921921
{
922-
io_data_t *d = j->io.at(idx).get();
922+
io_data_t *d = j->io_chain().at(idx).get();
923923
if (d->io_mode == IO_BUFFER)
924924
{
925925
buff = static_cast<io_buffer_t *>(d);

proc.h

+9-3
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ void release_job_id(job_id_t jobid);
272272
A struct represeting a job. A job is basically a pipeline of one
273273
or more processes and a couple of flags.
274274
*/
275+
class parser_t;
275276
class job_t
276277
{
277278
/**
@@ -284,6 +285,10 @@ class job_t
284285
/* narrow copy so we don't have to convert after fork */
285286
narrow_string_rep_t command_narrow;
286287

288+
/** List of all IO redirections for this job. */
289+
io_chain_t io;
290+
friend void exec(parser_t &parser, job_t *j);
291+
287292
/* No copying */
288293
job_t(const job_t &rhs);
289294
void operator=(const job_t &);
@@ -350,13 +355,14 @@ class job_t
350355
*/
351356
const job_id_t job_id;
352357

353-
/** List of all IO redirections for this job. */
354-
io_chain_t io;
355-
356358
/**
357359
Bitset containing information about the job. A combination of the JOB_* constants.
358360
*/
359361
unsigned int flags;
362+
363+
const io_chain_t &io_chain() const { return this->io; }
364+
365+
void append_io(const shared_ptr<io_data_t> & new_io) { this->io.push_back(new_io); }
360366
};
361367

362368
/**

share/functions/eval.fish

+13
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,19 @@ function eval -S -d "Evaluate parameters as a command"
1919
status --job-control full
2020
end
2121

22+
# rfish: To eval 'foo', we construct a block "begin ; foo; end <&3 3<&-"
23+
# The 'eval2_inner' is a param to 'begin' itself; I believe it does nothing.
24+
# Note the redirections are also within the quotes.
25+
#
26+
# We then pipe this to 'source 3<&0' which dup2's 3 to stdin.
27+
#
28+
# You might expect that the dup2(3, stdin) should overwrite stdin,
29+
# and therefore prevent 'source' from reading the piped-in block. This doesn't happen
30+
# because when you pipe to a builtin, we don't overwrite stdin with the read end
31+
# of the block; instead we set a separate fd in a variable 'builtin_stdin', which is
32+
# what it reads from. So builtins are magic in that, in pipes, their stdin
33+
# is not fd 0.
34+
2235
echo "begin; $argv "\n" ;end eval2_inner <&3 3<&-" | source 3<&0
2336
set -l res $status
2437

tests/test1.in

+5
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ else
9494
end
9595
echo Test 5 $sta
9696

97+
# Verify that we can turn stderr into stdout and then pipe it.
98+
# Note that the order here seems unspecified - 'errput' appears before 'output', why?
99+
echo Test redirections
100+
begin ; echo output ; echo errput 1>&2 ; end 2>&1 | tee /tmp/tee_test.txt ; cat /tmp/tee_test.txt
101+
97102
# echo tests
98103

99104
echo 'abc\ndef'

tests/test1.out

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ Test pass
1818
Test 3 pass
1919
Test 4 pass
2020
Test 5 pass
21+
Test redirections
22+
errput
23+
output
24+
errput
25+
output
2126
abc\ndef
2227
abc
2328
def

0 commit comments

Comments
 (0)