blob: 32245a6bc3eae5f9d8afc9793544b56a25284aaf [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>
Peter Åstrand (astrand)e2c3b602017-10-10 13:56:51 +020037#include <sys/socket.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000038#endif
39
Adam Tkac49e5ce62008-11-14 12:25:34 +000040/* Old systems have select() in sys/time.h */
41#ifdef HAVE_SYS_SELECT_H
42#include <sys/select.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000043#endif
44
45#include <rdr/FdOutStream.h>
46#include <rdr/Exception.h>
Pierre Ossman3c837132011-11-15 12:07:43 +000047#include <rfb/util.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000048
49
50using namespace rdr;
51
52enum { DEFAULT_BUF_SIZE = 16384 };
53
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000054FdOutStream::FdOutStream(int fd_, bool blocking_, int timeoutms_, int bufSize_)
55 : fd(fd_), blocking(blocking_), timeoutms(timeoutms_),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000056 bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
57{
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000058 ptr = start = sentUpTo = new U8[bufSize];
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000059 end = start + bufSize;
Pierre Ossman3c837132011-11-15 12:07:43 +000060
61 gettimeofday(&lastWrite, NULL);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000062}
63
64FdOutStream::~FdOutStream()
65{
66 try {
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000067 blocking = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000068 flush();
69 } catch (Exception&) {
70 }
71 delete [] start;
72}
73
74void FdOutStream::setTimeout(int timeoutms_) {
75 timeoutms = timeoutms_;
76}
77
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000078void FdOutStream::setBlocking(bool blocking_) {
79 blocking = blocking_;
80}
81
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000082int FdOutStream::length()
83{
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000084 return offset + ptr - sentUpTo;
85}
86
87int FdOutStream::bufferUsage()
88{
89 return ptr - sentUpTo;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000090}
91
Pierre Ossman3c837132011-11-15 12:07:43 +000092unsigned FdOutStream::getIdleTime()
93{
94 return rfb::msSince(&lastWrite);
95}
96
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000097void FdOutStream::flush()
98{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000099 while (sentUpTo < ptr) {
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000100 int n = writeWithTimeout((const void*) sentUpTo,
Pierre Ossmanc6df31d2016-04-29 13:40:13 +0200101 ptr - sentUpTo,
102 blocking? timeoutms : 0);
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000103
104 // Timeout?
Pierre Ossmanb08b3d42016-10-10 16:05:46 +0200105 if (n == 0) {
106 // If non-blocking then we're done here
107 if (!blocking)
108 break;
109
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000110 throw TimedOut();
Pierre Ossmanb08b3d42016-10-10 16:05:46 +0200111 }
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000112
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000113 sentUpTo += n;
114 offset += n;
115 }
116
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000117 // Managed to flush everything?
118 if (sentUpTo == ptr)
119 ptr = sentUpTo = start;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000120}
121
122
123int FdOutStream::overrun(int itemSize, int nItems)
124{
125 if (itemSize > bufSize)
126 throw Exception("FdOutStream overrun: max itemSize exceeded");
127
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000128 // First try to get rid of the data we have
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000129 flush();
130
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000131 // Still not enough space?
132 if (itemSize > end - ptr) {
133 // Can we shuffle things around?
134 // (don't do this if it gains us less than 25%)
135 if ((sentUpTo - start > bufSize / 4) &&
136 (itemSize < bufSize - (ptr - sentUpTo))) {
137 memmove(start, sentUpTo, ptr - sentUpTo);
138 ptr = start + (ptr - sentUpTo);
139 sentUpTo = start;
140 } else {
141 // Have to get rid of more data, so turn off non-blocking
142 // for a bit...
143 bool realBlocking;
144
145 realBlocking = blocking;
146 blocking = true;
147 flush();
148 blocking = realBlocking;
149 }
150 }
151
152 // Can we fit all the items asked for?
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000153 if (itemSize * nItems > end - ptr)
154 nItems = (end - ptr) / itemSize;
155
156 return nItems;
157}
158
159//
160// writeWithTimeout() writes up to the given length in bytes from the given
161// buffer to the file descriptor. If there is a timeout set and that timeout
162// expires, it throws a TimedOut exception. Otherwise it returns the number of
163// bytes written. It never attempts to write() unless select() indicates that
164// the fd is writable - this means it can be used on an fd which has been set
165// non-blocking. It also has to cope with the annoying possibility of both
166// select() and write() returning EINTR.
167//
168
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000169int FdOutStream::writeWithTimeout(const void* data, int length, int timeoutms)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000170{
171 int n;
172
173 do {
Pierre Ossman3b46a392016-04-29 13:37:55 +0200174 fd_set fds;
175 struct timeval tv;
176 struct timeval* tvp = &tv;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000177
Pierre Ossman3b46a392016-04-29 13:37:55 +0200178 if (timeoutms != -1) {
179 tv.tv_sec = timeoutms / 1000;
180 tv.tv_usec = (timeoutms % 1000) * 1000;
181 } else {
182 tvp = NULL;
183 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000184
Pierre Ossman3b46a392016-04-29 13:37:55 +0200185 FD_ZERO(&fds);
186 FD_SET(fd, &fds);
187 n = select(fd+1, 0, &fds, 0, tvp);
188 } while (n < 0 && errno == EINTR);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000189
Pierre Ossman3b46a392016-04-29 13:37:55 +0200190 if (n < 0)
191 throw SystemException("select", errno);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000192
Pierre Ossman3b46a392016-04-29 13:37:55 +0200193 if (n == 0)
194 return 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000195
Pierre Ossman3b46a392016-04-29 13:37:55 +0200196 do {
Peter Åstrand (astrand)e2c3b602017-10-10 13:56:51 +0200197 // select only guarantees that you can write SO_SNDLOWAT without
198 // blocking, which is normally 1. Use MSG_DONTWAIT to avoid
199 // blocking, when possible.
200#ifndef MSG_DONTWAIT
201 n = ::send(fd, (const char*)data, length, 0);
202#else
203 n = ::send(fd, (const char*)data, length, MSG_DONTWAIT);
204#endif
Pierre Ossman3b46a392016-04-29 13:37:55 +0200205 } while (n < 0 && (errno == EINTR));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000206
Pierre Ossman3b46a392016-04-29 13:37:55 +0200207 if (n < 0)
208 throw SystemException("write", errno);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000209
Pierre Ossman3c837132011-11-15 12:07:43 +0000210 gettimeofday(&lastWrite, NULL);
211
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000212 return n;
213}