afs: Improve error diagnostics.
[paraslash.git] / stdout.c
1 /* Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
2
3 /** \file stdout.c Functions that deal with writing to stdout. */
4
5 #include "para.h"
6 #include "list.h"
7 #include "sched.h"
8 #include "fd.h"
9 #include "error.h"
10 #include "stdout.h"
11 #include "buffer_tree.h"
12
13 /* Add STDOUT_FILENO to the write fd set if there is input data available. */
14 static void stdout_pre_select(struct sched *s, void *context)
15 {
16         struct stdout_task *sot = context;
17         int ret;
18
19         ret = btr_node_status(sot->btrn, 0, BTR_NT_LEAF);
20         if (ret > 0)
21                 para_fd_set(STDOUT_FILENO, &s->wfds, &s->max_fileno);
22         else if (ret < 0)
23                 sched_min_delay(s);
24 }
25
26 /*
27  * This function writes input data from the buffer tree to stdout if
28  * STDOUT_FILENO is writable.
29  */
30 static int stdout_post_select(struct sched *s, void *context)
31 {
32         struct stdout_task *sot = context;
33         struct btr_node *btrn = sot->btrn;
34         int ret;
35         char *buf;
36         size_t sz;
37
38         ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
39         if (ret < 0)
40                 goto out;
41         if (ret == 0)
42                 return 0;
43         if (!FD_ISSET(STDOUT_FILENO, &s->wfds))
44                 return 0;
45
46         if (sot->must_set_nonblock_flag) {
47                 ret = mark_fd_nonblocking(STDOUT_FILENO);
48                 if (ret < 0)
49                         goto out;
50                 sot->must_set_nonblock_flag = false;
51         }
52         for (;;) {
53                 sz = btr_next_buffer(btrn, &buf);
54                 if (sz == 0)
55                         break;
56                 ret = xwrite(STDOUT_FILENO, buf, sz);
57                 if (ret <= 0)
58                         break;
59                 btr_consume(btrn, ret);
60         }
61 out:
62         if (ret < 0) {
63                 btr_remove_node(&sot->btrn);
64                 /* Revert to blocking mode if necessary. */
65                 fcntl(STDOUT_FILENO, F_SETFL, sot->fd_flags);
66         }
67         return ret;
68 }
69
70 /**
71  * Register a stdout task structure.
72  *
73  * \param sot The stdout task structure to register.
74  * \param s The task will be added to this scheduler's task list.
75  *
76  * This sets up \a sot and registers a task with \a sot as context pointer.
77  */
78 void stdout_task_register(struct stdout_task *sot, struct sched *s)
79 {
80         int ret;
81         struct task_info ti = {
82                 .pre_select = stdout_pre_select,
83                 .post_select = stdout_post_select,
84                 .context = sot,
85                 .name = "stdout",
86         };
87
88         /* See \ref stdin.c for details. */
89         ret = fcntl(STDOUT_FILENO, F_GETFL);
90         if (ret < 0) {
91                 PARA_EMERG_LOG("F_GETFL: %s\n", strerror(errno));
92                 exit(EXIT_FAILURE);
93         }
94         sot->fd_flags = ret;
95         sot->must_set_nonblock_flag = (sot->fd_flags & O_NONBLOCK) == 0
96                 && !isatty(STDOUT_FILENO);
97         sot->task = task_register(&ti, s);
98 }