blob: 8fe9df94bcd51a79b37939c98028f5273d4cf79e [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
Peter Åstrand (astrand)3a1db162017-10-16 11:11:45 +02003 * Copyright 2017 Peter Astrand <astrand@cendio.se> for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00004 *
5 * This is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This software is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this software; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
18 * USA.
19 */
20
Adam Tkac8aee1a82009-09-04 12:08:56 +000021#ifdef HAVE_CONFIG_H
22#include <config.h>
Adam Tkac49e5ce62008-11-14 12:25:34 +000023#endif
24
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000025#include <stdio.h>
26#include <string.h>
Pierre Ossman500cb6e2015-05-29 16:54:21 +020027#include <errno.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000028#ifdef _WIN32
29#include <winsock2.h>
30#define write(s,b,l) send(s,(const char*)b,l,0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000031#undef errno
32#define errno WSAGetLastError()
Peter Åstrand (astrand)11167e12015-02-05 11:10:32 +010033#include <os/winerrno.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000034#else
35#include <sys/types.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000036#include <unistd.h>
37#include <sys/time.h>
Peter Åstrand (astrand)e2c3b602017-10-10 13:56:51 +020038#include <sys/socket.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000039#endif
40
Adam Tkac49e5ce62008-11-14 12:25:34 +000041/* Old systems have select() in sys/time.h */
42#ifdef HAVE_SYS_SELECT_H
43#include <sys/select.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000044#endif
45
46#include <rdr/FdOutStream.h>
47#include <rdr/Exception.h>
Pierre Ossman3c837132011-11-15 12:07:43 +000048#include <rfb/util.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000049
50
51using namespace rdr;
52
53enum { DEFAULT_BUF_SIZE = 16384 };
54
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000055FdOutStream::FdOutStream(int fd_, bool blocking_, int timeoutms_, int bufSize_)
56 : fd(fd_), blocking(blocking_), timeoutms(timeoutms_),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000057 bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
58{
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000059 ptr = start = sentUpTo = new U8[bufSize];
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000060 end = start + bufSize;
Pierre Ossman3c837132011-11-15 12:07:43 +000061
62 gettimeofday(&lastWrite, NULL);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000063}
64
65FdOutStream::~FdOutStream()
66{
67 try {
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000068 blocking = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000069 flush();
70 } catch (Exception&) {
71 }
72 delete [] start;
73}
74
75void FdOutStream::setTimeout(int timeoutms_) {
76 timeoutms = timeoutms_;
77}
78
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000079void FdOutStream::setBlocking(bool blocking_) {
80 blocking = blocking_;
81}
82
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000083int FdOutStream::length()
84{
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000085 return offset + ptr - sentUpTo;
86}
87
88int FdOutStream::bufferUsage()
89{
90 return ptr - sentUpTo;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000091}
92
Pierre Ossman3c837132011-11-15 12:07:43 +000093unsigned FdOutStream::getIdleTime()
94{
95 return rfb::msSince(&lastWrite);
96}
97
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000098void FdOutStream::flush()
99{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000100 while (sentUpTo < ptr) {
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000101 int n = writeWithTimeout((const void*) sentUpTo,
Pierre Ossmanc6df31d2016-04-29 13:40:13 +0200102 ptr - sentUpTo,
103 blocking? timeoutms : 0);
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000104
105 // Timeout?
Pierre Ossmanb08b3d42016-10-10 16:05:46 +0200106 if (n == 0) {
107 // If non-blocking then we're done here
108 if (!blocking)
109 break;
110
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000111 throw TimedOut();
Pierre Ossmanb08b3d42016-10-10 16:05:46 +0200112 }
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000113
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000114 sentUpTo += n;
115 offset += n;
116 }
117
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000118 // Managed to flush everything?
119 if (sentUpTo == ptr)
120 ptr = sentUpTo = start;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000121}
122
123
124int FdOutStream::overrun(int itemSize, int nItems)
125{
126 if (itemSize > bufSize)
127 throw Exception("FdOutStream overrun: max itemSize exceeded");
128
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000129 // First try to get rid of the data we have
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000130 flush();
131
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000132 // Still not enough space?
133 if (itemSize > end - ptr) {
134 // Can we shuffle things around?
135 // (don't do this if it gains us less than 25%)
136 if ((sentUpTo - start > bufSize / 4) &&
137 (itemSize < bufSize - (ptr - sentUpTo))) {
138 memmove(start, sentUpTo, ptr - sentUpTo);
139 ptr = start + (ptr - sentUpTo);
140 sentUpTo = start;
141 } else {
142 // Have to get rid of more data, so turn off non-blocking
143 // for a bit...
144 bool realBlocking;
145
146 realBlocking = blocking;
147 blocking = true;
148 flush();
149 blocking = realBlocking;
150 }
151 }
152
153 // Can we fit all the items asked for?
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000154 if (itemSize * nItems > end - ptr)
155 nItems = (end - ptr) / itemSize;
156
157 return nItems;
158}
159
160//
161// writeWithTimeout() writes up to the given length in bytes from the given
162// buffer to the file descriptor. If there is a timeout set and that timeout
163// expires, it throws a TimedOut exception. Otherwise it returns the number of
164// bytes written. It never attempts to write() unless select() indicates that
165// the fd is writable - this means it can be used on an fd which has been set
166// non-blocking. It also has to cope with the annoying possibility of both
167// select() and write() returning EINTR.
168//
169
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000170int FdOutStream::writeWithTimeout(const void* data, int length, int timeoutms)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000171{
172 int n;
173
174 do {
Pierre Ossman3b46a392016-04-29 13:37:55 +0200175 fd_set fds;
176 struct timeval tv;
177 struct timeval* tvp = &tv;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000178
Pierre Ossman3b46a392016-04-29 13:37:55 +0200179 if (timeoutms != -1) {
180 tv.tv_sec = timeoutms / 1000;
181 tv.tv_usec = (timeoutms % 1000) * 1000;
182 } else {
183 tvp = NULL;
184 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000185
Pierre Ossman3b46a392016-04-29 13:37:55 +0200186 FD_ZERO(&fds);
187 FD_SET(fd, &fds);
188 n = select(fd+1, 0, &fds, 0, tvp);
189 } while (n < 0 && errno == EINTR);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000190
Pierre Ossman3b46a392016-04-29 13:37:55 +0200191 if (n < 0)
192 throw SystemException("select", errno);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000193
Pierre Ossman3b46a392016-04-29 13:37:55 +0200194 if (n == 0)
195 return 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000196
Pierre Ossman3b46a392016-04-29 13:37:55 +0200197 do {
Peter Åstrand (astrand)e2c3b602017-10-10 13:56:51 +0200198 // select only guarantees that you can write SO_SNDLOWAT without
199 // blocking, which is normally 1. Use MSG_DONTWAIT to avoid
200 // blocking, when possible.
201#ifndef MSG_DONTWAIT
202 n = ::send(fd, (const char*)data, length, 0);
203#else
204 n = ::send(fd, (const char*)data, length, MSG_DONTWAIT);
205#endif
Pierre Ossman3b46a392016-04-29 13:37:55 +0200206 } while (n < 0 && (errno == EINTR));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000207
Pierre Ossman3b46a392016-04-29 13:37:55 +0200208 if (n < 0)
209 throw SystemException("write", errno);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000210
Pierre Ossman3c837132011-11-15 12:07:43 +0000211 gettimeofday(&lastWrite, NULL);
212
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000213 return n;
214}