1 /*
2 Copyright Red Hat, Inc. 2002-2003
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 2, or (at your option) any
7 later version.
8
9 This program is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; see the file COPYING. If not, write to the
16 Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
17 MA 02139, USA.
18 */
19 /** @file
20 * Wrapper functions around read/write/select to retry in the event
21 * of interrupts.
22 */
23
24 #include "config.h"
25
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <fcntl.h>
29 #include <errno.h>
30
31 #include "fdops.h"
32
33 /**
34 * This is a wrapper around select which will retry in the case we receive
35 * EINTR. This is necessary for _read_retry, since it wouldn't make sense
36 * to have _read_retry terminate if and only if two EINTRs were received
37 * in a row - one during the read() call, one during the select call...
38 *
39 * See select(2) for description of parameters.
40 */
41 int
42 _select_retry(int fdmax, fd_set * rfds, fd_set * wfds, fd_set * xfds,
43 struct timeval *timeout)
44 {
45 int rv;
46
47 while (1) {
48 rv = select(fdmax, rfds, wfds, xfds, timeout);
49 if (rv == -1) {
50 /* return on EBADF/EINVAL/ENOMEM; continue on EINTR/EAGAIN/ENOMEM */
51 if (errno == EINTR || errno == EAGAIN || errno == ENOMEM)
52 continue;
53 }
54 return rv;
55 }
56 }
57
58 /**
59 * Retries a write in the event of a non-blocked interrupt signal.
60 *
61 * @param fd File descriptor to which we are writing.
62 * @param buf Data buffer to send.
63 * @param count Number of bytes in buf to send.
64 * @param timeout (struct timeval) telling us how long we should retry.
65 * @return The number of bytes written to the file descriptor,
66 * or -1 on error (with errno set appropriately).
67 */
68 ssize_t
69 _write_retry(int fd, void *buf, int count, struct timeval * timeout)
70 {
71 int n, total = 0, remain = count, rv = 0;
72 fd_set wfds, xfds;
|
(1) Event var_assign_parm: |
Assigning: "tmp_buf" = "buf". |
| Also see events: |
[read_value] |
73 char *tmp_buf = (char *)buf;
74
|
(2) Event cond_true: |
Condition "total < count", taking true branch. |
75 while (total < count) {
76
77 /* Create the write FD set of 1... */
|
(3) Event cond_true: |
Condition "__i < 16UL /* sizeof (fd_set) / sizeof (__fd_mask) */", taking true branch. |
|
(4) Event loop: |
Jumping back to the beginning of the loop. |
|
(5) Event loop_begin: |
Jumped back to beginning of loop. |
|
(6) Event cond_false: |
Condition "__i < 16UL /* sizeof (fd_set) / sizeof (__fd_mask) */", taking false branch. |
|
(7) Event loop_end: |
Reached end of loop. |
78 FD_ZERO(&wfds);
79 FD_SET(fd, &wfds);
|
(8) Event cond_true: |
Condition "__i < 16UL /* sizeof (fd_set) / sizeof (__fd_mask) */", taking true branch. |
|
(9) Event loop: |
Jumping back to the beginning of the loop. |
|
(10) Event loop_begin: |
Jumped back to beginning of loop. |
|
(11) Event cond_false: |
Condition "__i < 16UL /* sizeof (fd_set) / sizeof (__fd_mask) */", taking false branch. |
|
(12) Event loop_end: |
Reached end of loop. |
80 FD_ZERO(&xfds);
81 FD_SET(fd, &xfds);
82
83 /* wait for the fd to be available for writing */
84 rv = _select_retry(fd + 1, NULL, &wfds, &xfds, timeout);
|
(13) Event cond_false: |
Condition "rv == -1", taking false branch. |
85 if (rv == -1)
86 return -1;
|
(14) Event else_branch: |
Reached else branch. |
|
(15) Event cond_false: |
Condition "rv == 0", taking false branch. |
87 else if (rv == 0) {
88 errno = ETIMEDOUT;
89 return -1;
|
(16) Event if_end: |
End of if statement. |
90 }
91
|
(17) Event cond_false: |
Condition "(xfds.fds_bits[fd / (64 /* 8 * (int)sizeof (__fd_mask) */)] & (__fd_mask)(1UL << fd % (64 /* 8 * (int)sizeof (__fd_mask) */))) != 0", taking false branch. |
92 if (FD_ISSET(fd, &xfds)) {
93 errno = EPIPE;
94 return -1;
|
(18) Event if_end: |
End of if statement. |
95 }
96
97 /*
98 * Attempt to write to fd
99 */
|
(19) Event read_value: |
Reading value "*(tmp_buf + total)" when calling "write". |
| Also see events: |
[var_assign_parm] |
100 n = write(fd, tmp_buf + total, remain);
101
102 /*
103 * When we know our fd was select()ed and we receive 0 bytes
104 * when we write, the fd was closed.
105 */
|
(20) Event cond_true: |
Condition "n == 0", taking true branch. |
|
(21) Event cond_true: |
Condition "rv == 1", taking true branch. |
106 if ((n == 0) && (rv == 1)) {
107 errno = EPIPE;
108 return -1;
109 }
110
111 if (n == -1) {
112 if ((errno == EAGAIN) || (errno == EINTR)) {
113 /*
114 * Not ready?
115 */
116 continue;
117 }
118
119 /* Other errors: EIO, EINVAL, etc */
120 return -1;
121 }
122
123 total += n;
124 remain -= n;
125 }
126
127 return total;
128 }
129
130 /**
131 * Retry reads until we (a) time out or (b) get our data. Of course, if
132 * timeout is NULL, it'll wait forever.
133 *
134 * @param sockfd File descriptor we want to read from.
135 * @param buf Preallocated buffer into which we will read data.
136 * @param count Number of bytes to read.
137 * @param timeout (struct timeval) describing how long we should retry.
138 * @return The number of bytes read on success, or -1 on failure.
139 Note that we will always return (count) or (-1).
140 */
141 ssize_t
142 _read_retry(int sockfd, void *buf, int count, struct timeval * timeout)
143 {
144 int n, total = 0, remain = count, rv = 0;
145 fd_set rfds, xfds;
146 char *tmp_buf = (char *)buf;
147
148 while (total < count) {
149 FD_ZERO(&rfds);
150 FD_SET(sockfd, &rfds);
151 FD_ZERO(&xfds);
152 FD_SET(sockfd, &xfds);
153
154 /*
155 * Select on the socket, in case it closes while we're not
156 * looking...
157 */
158 rv = _select_retry(sockfd + 1, &rfds, NULL, &xfds, timeout);
159 if (rv == -1)
160 return -1;
161 else if (rv == 0) {
162 errno = ETIMEDOUT;
163 return -1;
164 }
165
166 if (FD_ISSET(sockfd, &xfds)) {
167 errno = EPIPE;
168 return -1;
169 }
170
171 /*
172 * Attempt to read off the socket
173 */
174 n = read(sockfd, tmp_buf + total, remain);
175
176 /*
177 * When we know our socket was select()ed and we receive 0 bytes
178 * when we read, the socket was closed.
179 */
180 if ((n == 0) && (rv == 1)) {
181 errno = EPIPE;
182 return -1;
183 }
184
185 if (n == -1) {
186 if ((errno == EAGAIN) || (errno == EINTR)) {
187 /*
188 * Not ready? Wait for data to become available
189 */
190 continue;
191 }
192
193 /* Other errors: EPIPE, EINVAL, etc */
194 return -1;
195 }
196
197 total += n;
198 remain -= n;
199 }
200
201 return total;
202 }
203