Merge branch 't/stdin_stdout_fixes'
authorAndre Noll <maan@systemlinux.org>
Tue, 11 Jun 2013 18:41:49 +0000 (20:41 +0200)
committerAndre Noll <maan@systemlinux.org>
Tue, 11 Jun 2013 18:43:36 +0000 (20:43 +0200)
Was cooking for almost a month.

b99b4a stdin/stdout: Restore fd flags on shutdown.

Conflicts:
stdin.c
stdout.c

1  2 
NEWS
stdin.c
stdout.c

diff --combined NEWS
index a93fb2e3315839de57daef32cf5166cd3852579c,f323df1dda766dadfdc7fe559e6a39fa42824d58..07481964e2400d42caeb0db78bf3d66bbbe44509
--- 1/NEWS
--- 2/NEWS
+++ b/NEWS
@@@ -2,12 -2,6 +2,14 @@@
  0.?.? (to be announced) "spectral gravity"
  ------------------------------------------
  
 +      - UTF8 support for para_gui and the mp3 audio format handler.
 +      - Scheduler improvements and fixes.
 +      - The obsolete gettimeofday() function has been replaced
 +        by clock_gettime() on systems which support it.
 +      - Speed and usability improvements for para_gui.
++      - para_client now restores the fd flags of stdin and stdout
++        on shutdown
 +
  -----------------------------------------
  0.4.12 (2012-12-20) "volatile relativity"
  -----------------------------------------
diff --combined stdin.c
index b25a0ba013fb03b19cb757d07ff483c9389a0d70,08bc1f9a4be9c2964ae1c882cf6e34cd11685a19..20b9250e9b7fbf3c252e20dff8891bfeeb5112dd
+++ b/stdin.c
@@@ -33,6 -33,7 +33,6 @@@ static void stdin_pre_select(struct sch
        struct stdin_task *sit = container_of(t, struct stdin_task, task);
        int ret;
  
 -      t->error = 0;
        ret = btr_node_status(sit->btrn, 0, BTR_NT_ROOT);
        if (ret < 0)
                sched_min_delay(s);
   * appears to be readable, data is read from stdin and fed into the buffer
   * tree.
   */
 -static void stdin_post_select(struct sched *s, struct task *t)
 +static int stdin_post_select(struct sched *s, struct task *t)
  {
        struct stdin_task *sit = container_of(t, struct stdin_task, task);
        ssize_t ret;
        size_t sz, n;
        char *buf = NULL;
  
 -      t->error = 0;
        ret = btr_node_status(sit->btrn, 0, BTR_NT_ROOT);
        if (ret < 0)
                goto err;
        if (ret == 0)
 -              return;
 +              return 0;
        sz = btr_pool_get_buffer(sit->btrp, &buf);
        if (sz == 0)
 -              return;
 +              return 0;
+       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
        if (n > 0)
                btr_add_output_pool(sit->btrp, n, sit->btrn);
        if (ret >= 0)
 -              return;
 +              return 0;
  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;
 +      return ret;
  }
  
  /**
   * \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)
  {
        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 --combined stdout.c
index abf3d06f17a64249671ffbfb0470aa995a599578,0cf4876db49125efe83e733e6c5f5c7ee921aa45..cf33bf6d0b67cf49625d27b4d602dec35922d32d
+++ b/stdout.c
@@@ -30,6 -30,7 +30,6 @@@ static void stdout_pre_select(struct sc
        struct stdout_task *sot = container_of(t, struct stdout_task, task);
        int ret;
  
 -      t->error = 0;
        ret = btr_node_status(sot->btrn, 0, BTR_NT_LEAF);
        if (ret > 0)
                para_fd_set(STDOUT_FILENO, &s->wfds, &s->max_fileno);
@@@ -46,7 -47,7 +46,7 @@@
   * This function writes input data from the buffer tree to stdout if \p
   * STDOUT_FILENO is writable.
   */
 -static void stdout_post_select(struct sched *s, struct task *t)
 +static int stdout_post_select(struct sched *s, struct task *t)
  {
        struct stdout_task *sot = container_of(t, struct stdout_task, task);
        struct btr_node *btrn = sot->btrn;
        char *buf;
        size_t sz;
  
 -      t->error = 0;
        ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
        if (ret < 0)
                goto out;
        if (ret == 0)
 -              return;
 +              return 0;
        if (!FD_ISSET(STDOUT_FILENO, &s->wfds))
 -              return;
 +              return 0;
  
+       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)
                btr_consume(btrn, ret);
        }
  out:
-       if (ret < 0)
+       if (ret < 0) {
                btr_remove_node(&sot->btrn);
 -      t->error = ret;
+               /* Revert to blocking mode if necessary. */
+               fcntl(STDOUT_FILENO, F_SETFL, sot->fd_flags);
+       }
 +      return ret;
  }
  /**
   * Initialize a stdout task structure with default values.
@@@ -82,7 -93,7 +91,7 @@@
   * \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)
  {
        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;
  }