From b99b4ac94a4391f5a6bbeb9b8ba5eb466eca6249 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Mon, 18 Mar 2013 23:51:46 +0100 Subject: [PATCH] stdin/stdout: Restore fd flags on shutdown. The stdin/stdout code should restore the flags to the old value to avoid surprises, for example in shell scripts. This changes stdin.c and stdout.c to save the old value. It is restored in ->post_select when the buffer tree node is removed and no more I/O takes place. --- stdin.c | 30 +++++++++++++++++++++++------- stdin.h | 4 ++++ stdout.c | 27 ++++++++++++++++++++------- stdout.h | 4 ++++ 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/stdin.c b/stdin.c index fd803ae5..08bc1f9a 100644 --- a/stdin.c +++ b/stdin.c @@ -71,6 +71,12 @@ static void stdin_post_select(struct sched *s, struct task *t) sz = btr_pool_get_buffer(sit->btrp, &buf); if (sz == 0) return; + if (sit->must_set_nonblock_flag) { + ret = mark_fd_nonblocking(STDIN_FILENO); + if (ret < 0) + goto err; + sit->must_set_nonblock_flag = false; + } /* * Do not use the maximal size to avoid having only a single buffer * reference for the whole pool. This is bad because if that single @@ -84,6 +90,8 @@ static void stdin_post_select(struct sched *s, struct task *t) return; err: btr_remove_node(&sit->btrn); + /* Revert to blocking mode if necessary. */ + fcntl(STDIN_FILENO, F_SETFL, sit->fd_flags); //btr_pool_free(sit->btrp); t->error = ret; } @@ -94,8 +102,7 @@ err: * \param sit The stdin task structure. * * This fills in the pre/post select function pointers of the task structure - * given by \a sit. Moreover, the stdin file desctiptor is set to nonblocking - * mode, and a buffer tree is created. + * given by \a sit and creates a buffer tree for I/O. */ void stdin_set_defaults(struct stdin_task *sit) { @@ -105,9 +112,18 @@ void stdin_set_defaults(struct stdin_task *sit) sit->task.post_select = stdin_post_select; sit->btrp = btr_pool_new("stdin", 128 * 1024); sprintf(sit->task.status, "stdin reader"); - ret = mark_fd_nonblocking(STDIN_FILENO); - if (ret >= 0) - return; - PARA_EMERG_LOG("%s\n", para_strerror(-ret)); - exit(EXIT_FAILURE); + /* + * Both STDIN_FILENO and STDOUT_FILENO may refer to the same open file + * description (the terminal), and thus share the same file status + * flags. In order to not interfere with the stdout task, we only get + * the file status flags for STDIN here and save a copy. The nonblock + * flag is set later on the first read. + */ + ret = fcntl(STDIN_FILENO, F_GETFL); + if (ret < 0) { + PARA_EMERG_LOG("F_GETFL: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + sit->fd_flags = ret; + sit->must_set_nonblock_flag = (sit->fd_flags & O_NONBLOCK) == 0; } diff --git a/stdin.h b/stdin.h index 54710ae5..cdf0c67f 100644 --- a/stdin.h +++ b/stdin.h @@ -14,6 +14,10 @@ struct stdin_task { struct btr_node *btrn; /** Use a buffer pool to minimize memcpy due to alignment problems. */ struct btr_pool *btrp; + /** The descriptor flags of STDIN at startup. */ + int fd_flags; + /** Whether we have to set STDIN to nonblocking mode. */ + bool must_set_nonblock_flag; }; void stdin_set_defaults(struct stdin_task *sit); diff --git a/stdout.c b/stdout.c index 9c7e64e7..0cf4876d 100644 --- a/stdout.c +++ b/stdout.c @@ -64,6 +64,12 @@ static void stdout_post_select(struct sched *s, struct task *t) if (!FD_ISSET(STDOUT_FILENO, &s->wfds)) return; + if (sot->must_set_nonblock_flag) { + ret = mark_fd_nonblocking(STDOUT_FILENO); + if (ret < 0) + goto out; + sot->must_set_nonblock_flag = false; + } for (;;) { sz = btr_next_buffer(btrn, &buf); if (sz == 0) @@ -74,8 +80,11 @@ static void stdout_post_select(struct sched *s, struct task *t) btr_consume(btrn, ret); } out: - if (ret < 0) + if (ret < 0) { btr_remove_node(&sot->btrn); + /* Revert to blocking mode if necessary. */ + fcntl(STDOUT_FILENO, F_SETFL, sot->fd_flags); + } t->error = ret; } /** @@ -84,7 +93,7 @@ out: * \param sot The stdout task structure. * * This fills in the pre/post select function pointers of the task structure - * given by \a sot and sets the stdout file descriptor to nonblocking mode. + * given by \a sot. */ void stdout_set_defaults(struct stdout_task *sot) { @@ -93,9 +102,13 @@ void stdout_set_defaults(struct stdout_task *sot) sot->task.pre_select = stdout_pre_select; sot->task.post_select = stdout_post_select; sprintf(sot->task.status, "stdout"); - ret = mark_fd_nonblocking(STDOUT_FILENO); - if (ret >= 0) - return; - PARA_EMERG_LOG("%s\n", para_strerror(-ret)); - exit(EXIT_FAILURE); + + /* See stdin.c for details. */ + ret = fcntl(STDOUT_FILENO, F_GETFL); + if (ret < 0) { + PARA_EMERG_LOG("F_GETFL: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + sot->fd_flags = ret; + sot->must_set_nonblock_flag = (sot->fd_flags & O_NONBLOCK) == 0; } diff --git a/stdout.h b/stdout.h index cc1f2626..a21b7ced 100644 --- a/stdout.h +++ b/stdout.h @@ -16,6 +16,10 @@ struct stdout_task { struct task task; /** Stdout is always a leaf node in the buffer tree. */ struct btr_node *btrn; + /** The descriptor flags of STDOUT at startup. */ + int fd_flags; + /** Whether we have to set STDOUT to nonblocking mode. */ + bool must_set_nonblock_flag; }; void stdout_set_defaults(struct stdout_task *sot); -- 2.39.2