Don't report EINTR from select as an error, retry select instead
[libav.git] / libavformat / tcp.c
1 /*
2 * TCP protocol
3 * Copyright (c) 2002 Fabrice Bellard
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21 #include "avformat.h"
22 #include <unistd.h>
23 #include "internal.h"
24 #include "network.h"
25 #include "os_support.h"
26 #if HAVE_SYS_SELECT_H
27 #include <sys/select.h>
28 #endif
29 #include <sys/time.h>
30
31 typedef struct TCPContext {
32 int fd;
33 } TCPContext;
34
35 /* return non zero if error */
36 static int tcp_open(URLContext *h, const char *uri, int flags)
37 {
38 struct addrinfo hints, *ai, *cur_ai;
39 int port, fd = -1;
40 TCPContext *s = NULL;
41 fd_set wfds;
42 int fd_max, ret;
43 struct timeval tv;
44 socklen_t optlen;
45 char hostname[1024],proto[1024],path[1024];
46 char portstr[10];
47
48 ff_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
49 &port, path, sizeof(path), uri);
50 if (strcmp(proto,"tcp") || port <= 0 || port >= 65536)
51 return AVERROR(EINVAL);
52
53 memset(&hints, 0, sizeof(hints));
54 hints.ai_family = AF_UNSPEC;
55 hints.ai_socktype = SOCK_STREAM;
56 snprintf(portstr, sizeof(portstr), "%d", port);
57 if (getaddrinfo(hostname, portstr, &hints, &ai))
58 return AVERROR(EIO);
59
60 cur_ai = ai;
61
62 restart:
63 fd = socket(cur_ai->ai_family, cur_ai->ai_socktype, cur_ai->ai_protocol);
64 if (fd < 0)
65 goto fail;
66 ff_socket_nonblock(fd, 1);
67
68 redo:
69 ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen);
70 if (ret < 0) {
71 if (ff_neterrno() == FF_NETERROR(EINTR))
72 goto redo;
73 if (ff_neterrno() != FF_NETERROR(EINPROGRESS) &&
74 ff_neterrno() != FF_NETERROR(EAGAIN))
75 goto fail;
76
77 /* wait until we are connected or until abort */
78 for(;;) {
79 if (url_interrupt_cb()) {
80 ret = AVERROR(EINTR);
81 goto fail1;
82 }
83 fd_max = fd;
84 FD_ZERO(&wfds);
85 FD_SET(fd, &wfds);
86 tv.tv_sec = 0;
87 tv.tv_usec = 100 * 1000;
88 ret = select(fd_max + 1, NULL, &wfds, NULL, &tv);
89 if (ret > 0 && FD_ISSET(fd, &wfds))
90 break;
91 }
92
93 /* test error */
94 optlen = sizeof(ret);
95 getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen);
96 if (ret != 0)
97 goto fail;
98 }
99 s = av_malloc(sizeof(TCPContext));
100 if (!s) {
101 freeaddrinfo(ai);
102 return AVERROR(ENOMEM);
103 }
104 h->priv_data = s;
105 h->is_streamed = 1;
106 s->fd = fd;
107 freeaddrinfo(ai);
108 return 0;
109
110 fail:
111 if (cur_ai->ai_next) {
112 /* Retry with the next sockaddr */
113 cur_ai = cur_ai->ai_next;
114 if (fd >= 0)
115 closesocket(fd);
116 goto restart;
117 }
118 ret = AVERROR(EIO);
119 fail1:
120 if (fd >= 0)
121 closesocket(fd);
122 freeaddrinfo(ai);
123 return ret;
124 }
125
126 static int tcp_read(URLContext *h, uint8_t *buf, int size)
127 {
128 TCPContext *s = h->priv_data;
129 int len, fd_max, ret;
130 fd_set rfds;
131 struct timeval tv;
132
133 for (;;) {
134 if (url_interrupt_cb())
135 return AVERROR(EINTR);
136 fd_max = s->fd;
137 FD_ZERO(&rfds);
138 FD_SET(s->fd, &rfds);
139 tv.tv_sec = 0;
140 tv.tv_usec = 100 * 1000;
141 ret = select(fd_max + 1, &rfds, NULL, NULL, &tv);
142 if (ret > 0 && FD_ISSET(s->fd, &rfds)) {
143 len = recv(s->fd, buf, size, 0);
144 if (len < 0) {
145 if (ff_neterrno() != FF_NETERROR(EINTR) &&
146 ff_neterrno() != FF_NETERROR(EAGAIN))
147 return AVERROR(ff_neterrno());
148 } else return len;
149 } else if (ret < 0) {
150 if (ff_neterrno() == FF_NETERROR(EINTR))
151 continue;
152 return -1;
153 }
154 }
155 }
156
157 static int tcp_write(URLContext *h, uint8_t *buf, int size)
158 {
159 TCPContext *s = h->priv_data;
160 int ret, size1, fd_max, len;
161 fd_set wfds;
162 struct timeval tv;
163
164 size1 = size;
165 while (size > 0) {
166 if (url_interrupt_cb())
167 return AVERROR(EINTR);
168 fd_max = s->fd;
169 FD_ZERO(&wfds);
170 FD_SET(s->fd, &wfds);
171 tv.tv_sec = 0;
172 tv.tv_usec = 100 * 1000;
173 ret = select(fd_max + 1, NULL, &wfds, NULL, &tv);
174 if (ret > 0 && FD_ISSET(s->fd, &wfds)) {
175 len = send(s->fd, buf, size, 0);
176 if (len < 0) {
177 if (ff_neterrno() != FF_NETERROR(EINTR) &&
178 ff_neterrno() != FF_NETERROR(EAGAIN))
179 return AVERROR(ff_neterrno());
180 continue;
181 }
182 size -= len;
183 buf += len;
184 } else if (ret < 0) {
185 if (ff_neterrno() == FF_NETERROR(EINTR))
186 continue;
187 return -1;
188 }
189 }
190 return size1 - size;
191 }
192
193 static int tcp_close(URLContext *h)
194 {
195 TCPContext *s = h->priv_data;
196 closesocket(s->fd);
197 av_free(s);
198 return 0;
199 }
200
201 static int tcp_get_file_handle(URLContext *h)
202 {
203 TCPContext *s = h->priv_data;
204 return s->fd;
205 }
206
207 URLProtocol tcp_protocol = {
208 "tcp",
209 tcp_open,
210 tcp_read,
211 tcp_write,
212 NULL, /* seek */
213 tcp_close,
214 .url_get_file_handle = tcp_get_file_handle,
215 };