blob: 520853ffc6744a1761877bfc2b8c1d5fab6d33bc [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossman3c837132011-11-15 12:07:43 +00002 * Copyright 2011 Pierre Ossman for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19
Adam Tkac8aee1a82009-09-04 12:08:56 +000020#ifdef HAVE_CONFIG_H
21#include <config.h>
Adam Tkac49e5ce62008-11-14 12:25:34 +000022#endif
23
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000024#include <stdio.h>
25#include <string.h>
Pierre Ossman500cb6e2015-05-29 16:54:21 +020026#include <errno.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000027#ifdef _WIN32
28#include <winsock2.h>
29#define write(s,b,l) send(s,(const char*)b,l,0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000030#undef errno
31#define errno WSAGetLastError()
Peter Åstrand (astrand)11167e12015-02-05 11:10:32 +010032#include <os/winerrno.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000033#else
34#include <sys/types.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000035#include <unistd.h>
36#include <sys/time.h>
37#endif
38
Adam Tkac49e5ce62008-11-14 12:25:34 +000039/* Old systems have select() in sys/time.h */
40#ifdef HAVE_SYS_SELECT_H
41#include <sys/select.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000042#endif
43
44#include <rdr/FdOutStream.h>
45#include <rdr/Exception.h>
Pierre Ossman3c837132011-11-15 12:07:43 +000046#include <rfb/util.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000047
48
49using namespace rdr;
50
51enum { DEFAULT_BUF_SIZE = 16384 };
52
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000053FdOutStream::FdOutStream(int fd_, bool blocking_, int timeoutms_, int bufSize_)
54 : fd(fd_), blocking(blocking_), timeoutms(timeoutms_),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000055 bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
56{
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000057 ptr = start = sentUpTo = new U8[bufSize];
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000058 end = start + bufSize;
Pierre Ossman3c837132011-11-15 12:07:43 +000059
60 gettimeofday(&lastWrite, NULL);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000061}
62
63FdOutStream::~FdOutStream()
64{
65 try {
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000066 blocking = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000067 flush();
68 } catch (Exception&) {
69 }
70 delete [] start;
71}
72
73void FdOutStream::setTimeout(int timeoutms_) {
74 timeoutms = timeoutms_;
75}
76
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000077void FdOutStream::setBlocking(bool blocking_) {
78 blocking = blocking_;
79}
80
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000081int FdOutStream::length()
82{
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000083 return offset + ptr - sentUpTo;
84}
85
86int FdOutStream::bufferUsage()
87{
88 return ptr - sentUpTo;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000089}
90
Pierre Ossman3c837132011-11-15 12:07:43 +000091unsigned FdOutStream::getIdleTime()
92{
93 return rfb::msSince(&lastWrite);
94}
95
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000096void FdOutStream::flush()
97{
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000098 int timeoutms_;
99
100 if (blocking)
101 timeoutms_ = timeoutms;
102 else
103 timeoutms_ = 0;
104
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000105 while (sentUpTo < ptr) {
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000106 int n = writeWithTimeout((const void*) sentUpTo,
107 ptr - sentUpTo, timeoutms_);
108
109 // Timeout?
110 if (n == 0) {
111 // If non-blocking then we're done here
112 if (!blocking)
113 break;
114
115 // Otherwise try blocking (with possible timeout)
116 if ((timeoutms_ == 0) && (timeoutms != 0)) {
117 timeoutms_ = timeoutms;
118 break;
119 }
120
121 // Proper timeout
122 throw TimedOut();
123 }
124
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000125 sentUpTo += n;
126 offset += n;
127 }
128
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000129 // Managed to flush everything?
130 if (sentUpTo == ptr)
131 ptr = sentUpTo = start;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000132}
133
134
135int FdOutStream::overrun(int itemSize, int nItems)
136{
137 if (itemSize > bufSize)
138 throw Exception("FdOutStream overrun: max itemSize exceeded");
139
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000140 // First try to get rid of the data we have
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000141 flush();
142
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000143 // Still not enough space?
144 if (itemSize > end - ptr) {
145 // Can we shuffle things around?
146 // (don't do this if it gains us less than 25%)
147 if ((sentUpTo - start > bufSize / 4) &&
148 (itemSize < bufSize - (ptr - sentUpTo))) {
149 memmove(start, sentUpTo, ptr - sentUpTo);
150 ptr = start + (ptr - sentUpTo);
151 sentUpTo = start;
152 } else {
153 // Have to get rid of more data, so turn off non-blocking
154 // for a bit...
155 bool realBlocking;
156
157 realBlocking = blocking;
158 blocking = true;
159 flush();
160 blocking = realBlocking;
161 }
162 }
163
164 // Can we fit all the items asked for?
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000165 if (itemSize * nItems > end - ptr)
166 nItems = (end - ptr) / itemSize;
167
168 return nItems;
169}
170
171//
172// writeWithTimeout() writes up to the given length in bytes from the given
173// buffer to the file descriptor. If there is a timeout set and that timeout
174// expires, it throws a TimedOut exception. Otherwise it returns the number of
175// bytes written. It never attempts to write() unless select() indicates that
176// the fd is writable - this means it can be used on an fd which has been set
177// non-blocking. It also has to cope with the annoying possibility of both
178// select() and write() returning EINTR.
179//
180
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000181int FdOutStream::writeWithTimeout(const void* data, int length, int timeoutms)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000182{
183 int n;
184
185 do {
Pierre Ossman3b46a392016-04-29 13:37:55 +0200186 fd_set fds;
187 struct timeval tv;
188 struct timeval* tvp = &tv;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000189
Pierre Ossman3b46a392016-04-29 13:37:55 +0200190 if (timeoutms != -1) {
191 tv.tv_sec = timeoutms / 1000;
192 tv.tv_usec = (timeoutms % 1000) * 1000;
193 } else {
194 tvp = NULL;
195 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000196
Pierre Ossman3b46a392016-04-29 13:37:55 +0200197 FD_ZERO(&fds);
198 FD_SET(fd, &fds);
199 n = select(fd+1, 0, &fds, 0, tvp);
200 } while (n < 0 && errno == EINTR);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000201
Pierre Ossman3b46a392016-04-29 13:37:55 +0200202 if (n < 0)
203 throw SystemException("select", errno);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000204
Pierre Ossman3b46a392016-04-29 13:37:55 +0200205 if (n == 0)
206 return 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000207
Pierre Ossman3b46a392016-04-29 13:37:55 +0200208 do {
209 n = ::write(fd, data, length);
210 } while (n < 0 && (errno == EINTR));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000211
Pierre Ossman3b46a392016-04-29 13:37:55 +0200212 if (n < 0)
213 throw SystemException("write", errno);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000214
Pierre Ossman3c837132011-11-15 12:07:43 +0000215 gettimeofday(&lastWrite, NULL);
216
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000217 return n;
218}