stdin/stdout: Restore fd flags on shutdown.
authorAndre Noll <maan@systemlinux.org>
Mon, 18 Mar 2013 22:51:46 +0000 (23:51 +0100)
committerAndre Noll <maan@systemlinux.org>
Sat, 4 May 2013 22:48:04 +0000 (00:48 +0200)
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
stdin.h
stdout.c
stdout.h

diff --git a/stdin.c b/stdin.c
index fd803ae..08bc1f9 100644 (file)
--- 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 54710ae..cdf0c67 100644 (file)
--- 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);
index 9c7e64e..0cf4876 100644 (file)
--- 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;
 }
index cc1f262..a21b7ce 100644 (file)
--- 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);