1 karl 1.22 //%2006////////////////////////////////////////////////////////////////////////
|
2 mike 1.2 //
|
3 karl 1.17 // Copyright (c) 2000, 2001, 2002 BMC Software; Hewlett-Packard Development
4 // Company, L.P.; IBM Corp.; The Open Group; Tivoli Systems.
5 // Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L.P.;
|
6 karl 1.14 // IBM Corp.; EMC Corporation, The Open Group.
|
7 karl 1.17 // Copyright (c) 2004 BMC Software; Hewlett-Packard Development Company, L.P.;
8 // IBM Corp.; EMC Corporation; VERITAS Software Corporation; The Open Group.
|
9 karl 1.18 // Copyright (c) 2005 Hewlett-Packard Development Company, L.P.; IBM Corp.;
10 // EMC Corporation; VERITAS Software Corporation; The Open Group.
|
11 karl 1.22 // Copyright (c) 2006 Hewlett-Packard Development Company, L.P.; IBM Corp.;
12 // EMC Corporation; Symantec Corporation; The Open Group.
|
13 mike 1.2 //
14 // Permission is hereby granted, free of charge, to any person obtaining a copy
|
15 kumpf 1.4 // of this software and associated documentation files (the "Software"), to
16 // deal in the Software without restriction, including without limitation the
17 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
18 mike 1.2 // sell copies of the Software, and to permit persons to whom the Software is
19 // furnished to do so, subject to the following conditions:
|
20 karl 1.22 //
|
21 kumpf 1.4 // THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
|
22 mike 1.2 // ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
23 // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
24 kumpf 1.4 // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
25 // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
26 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
27 mike 1.2 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30 //==============================================================================
31 //
32 //%/////////////////////////////////////////////////////////////////////////////
33
34 #include "Socket.h"
|
35 mike 1.23 #include "Network.h"
36 #include <cctype>
37 #include <cstring>
38 #include <Pegasus/Common/Sharable.h>
|
39 thilo.boehm 1.25 #include <Pegasus/Common/Logger.h>
40 #include <Pegasus/Common/System.h>
|
41 kumpf 1.28 #include <Pegasus/Common/Tracer.h>
|
42 kumpf 1.32.4.2 #include <Pegasus/Common/Threads.h>
|
43 mike 1.23
|
44 kumpf 1.28 PEGASUS_NAMESPACE_BEGIN
45
46 static Uint32 _socketInterfaceRefCount = 0;
47
48 Boolean Socket::timedConnect(
49 SocketHandle socket,
50 sockaddr* address,
51 int addressLength,
52 Uint32 timeoutMilliseconds)
53 {
54 int connectResult;
|
55 kumpf 1.32.4.2 #ifdef PEGASUS_OS_TYPE_WINDOWS
56 connectResult = ::connect(socket, address, addressLength);
57 #else
58 Boolean connectionAlreadyRefused = false;
59 Uint32 maxConnectAttempts = 100;
60 // Retry the connect() until it succeeds or it fails with an error other
61 // than EINTR, EAGAIN (for Linux), or the first ECONNREFUSED (for HP-UX).
62 while (((connectResult = ::connect(socket, address, addressLength)) == -1)
63 && (maxConnectAttempts-- > 0)
64 && ((errno == EINTR) || (errno == EAGAIN) ||
65 ((errno == ECONNREFUSED) && !connectionAlreadyRefused)))
66 {
67 if (errno == ECONNREFUSED)
68 {
69 connectionAlreadyRefused = true;
70 }
71 Threads::sleep(1);
72 }
73 #endif
|
74 kumpf 1.28
75 if (connectResult == 0)
76 {
77 return true;
78 }
|
79 mike 1.2
|
80 kumpf 1.28 if (getSocketError() == PEGASUS_NETWORK_EINPROGRESS)
81 {
82 PEG_TRACE((TRC_HTTP, Tracer::LEVEL4,
83 "Connection to server in progress. Waiting up to %u milliseconds "
84 "for the socket to become connected.",
85 timeoutMilliseconds));
86
87 fd_set fdwrite;
88 FD_ZERO(&fdwrite);
89 FD_SET(socket, &fdwrite);
90 struct timeval timeoutValue =
91 { timeoutMilliseconds/1000, timeoutMilliseconds%1000*1000 };
92 int selectResult = -1;
|
93 mike 1.2
|
94 venkat.puvvada 1.32.4.1 #ifdef PEGASUS_OS_TYPE_WINDOWS
95 PEGASUS_RETRY_SYSTEM_CALL(
96 select(FD_SETSIZE, NULL, &fdwrite, &fdwrite, &timeoutValue),
97 selectResult);
98 #else
|
99 kumpf 1.28 PEGASUS_RETRY_SYSTEM_CALL(
100 select(FD_SETSIZE, NULL, &fdwrite, NULL, &timeoutValue),
101 selectResult);
|
102 venkat.puvvada 1.32.4.1 #endif
|
103 kumpf 1.28 if (selectResult == 0)
104 {
|
105 marek 1.32 PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL1,
|
106 kumpf 1.28 "select() timed out waiting for the socket connection to be "
107 "established.");
108 return false;
109 }
110 else if (selectResult > 0)
111 {
112 int optval;
113 SocketLength optlen = sizeof(int);
114 getsockopt(socket, SOL_SOCKET, SO_ERROR, (char*)&optval, &optlen);
115 if (optval == 0)
116 {
117 PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4,
118 "Connection with server established.");
119 return true;
120 }
121 else
122 {
|
123 marek 1.32 PEG_TRACE((TRC_HTTP, Tracer::LEVEL1,
|
124 kumpf 1.28 "Did not connect, getsockopt() returned optval = %d",
125 optval));
126 return false;
127 }
128 }
129 else
130 {
|
131 marek 1.32 PEG_TRACE((TRC_HTTP, Tracer::LEVEL1,
|
132 kumpf 1.28 "select() returned error code %d",
133 getSocketError()));
134 return false;
135 }
136 }
|
137 mike 1.2
|
138 marek 1.32 PEG_TRACE((TRC_HTTP, Tracer::LEVEL1,
|
139 kumpf 1.28 "connect() returned error code %d",
140 getSocketError()));
141 return false;
142 }
|
143 mike 1.2
|
144 mike 1.23 Sint32 Socket::read(SocketHandle socket, void* ptr, Uint32 size)
|
145 mike 1.2 {
146 #ifdef PEGASUS_OS_TYPE_WINDOWS
147 return ::recv(socket, (char*)ptr, size, 0);
148 #else
|
149 mike 1.23 int status;
150 PEGASUS_RETRY_SYSTEM_CALL(::read(socket, (char*)ptr, size), status);
151 return status;
|
152 mday 1.11 #endif
|
153 mike 1.2 }
154
|
155 mike 1.23 Sint32 Socket::write(SocketHandle socket, const void* ptr, Uint32 size)
|
156 mike 1.2 {
157 #ifdef PEGASUS_OS_TYPE_WINDOWS
158 return ::send(socket, (const char*)ptr, size, 0);
159 #else
|
160 mike 1.23 int status;
161 PEGASUS_RETRY_SYSTEM_CALL(::write(socket, (char*)ptr, size), status);
162 return status;
|
163 mday 1.11 #endif
|
164 mike 1.2 }
165
|
166 kumpf 1.26 Sint32 Socket::timedWrite(
167 SocketHandle socket,
168 const void* ptr,
169 Uint32 size,
170 Uint32 socketWriteTimeout)
|
171 marek 1.24 {
172 Sint32 bytesWritten = 0;
173 Sint32 totalBytesWritten = 0;
174 Boolean socketTimedOut = false;
175 int selreturn = 0;
176 while (1)
177 {
178 #ifdef PEGASUS_OS_TYPE_WINDOWS
179 PEGASUS_RETRY_SYSTEM_CALL(
180 ::send(socket, (const char*)ptr, size, 0), bytesWritten);
181 #else
182 PEGASUS_RETRY_SYSTEM_CALL(
183 ::write(socket, (char*)ptr, size), bytesWritten);
184 #endif
|
185 kumpf 1.26 // Some data written this cycle ?
|
186 marek 1.24 // Add it to the total amount of written data.
187 if (bytesWritten > 0)
188 {
189 totalBytesWritten += bytesWritten;
190 socketTimedOut = false;
191 }
192
193 // All data written ? return amount of data written
194 if ((Uint32)bytesWritten == size)
195 {
196 return totalBytesWritten;
197 }
198 // If data has been written partially, we resume writing data
199 // this also accounts for the case of a signal interrupt
200 // (i.e. errno = EINTR)
201 if (bytesWritten > 0)
202 {
203 size -= bytesWritten;
204 ptr = (void *)((char *)ptr + bytesWritten);
205 continue;
206 }
207 marek 1.24 // Something went wrong
208 if (bytesWritten == PEGASUS_SOCKET_ERROR)
209 {
210 // if we already waited for the socket to get ready, bail out
|
211 kumpf 1.26 if (socketTimedOut) return bytesWritten;
|
212 marek 1.24 #ifdef PEGASUS_OS_TYPE_WINDOWS
213 if (WSAGetLastError() == WSAEWOULDBLOCK)
214 #else
215 if (errno == EAGAIN || errno == EWOULDBLOCK)
216 #endif
217 {
218 fd_set fdwrite;
219 // max. timeout seconds waiting for the socket to get ready
220 struct timeval tv = { socketWriteTimeout, 0 };
221 FD_ZERO(&fdwrite);
222 FD_SET(socket, &fdwrite);
223 selreturn = select(FD_SETSIZE, NULL, &fdwrite, NULL, &tv);
224 if (selreturn == 0) socketTimedOut = true; // ran out of time
|
225 kumpf 1.26 continue;
|
226 marek 1.24 }
227 return bytesWritten;
228 }
229 }
230 }
231
|
232 kumpf 1.29 void Socket::close(SocketHandle& socket)
|
233 mike 1.2 {
|
234 kumpf 1.29 if (socket != PEGASUS_INVALID_SOCKET)
|
235 mike 1.23 {
236 #ifdef PEGASUS_OS_TYPE_WINDOWS
|
237 kumpf 1.26 if (!closesocket(socket))
|
238 kumpf 1.29 {
239 socket = PEGASUS_INVALID_SOCKET;
240 }
|
241 mike 1.23 #else
|
242 kumpf 1.26 int status;
243 PEGASUS_RETRY_SYSTEM_CALL(::close(socket), status);
|
244 mike 1.23
|
245 kumpf 1.26 if (status == 0)
|
246 kumpf 1.29 {
247 socket = PEGASUS_INVALID_SOCKET;
248 }
|
249 mike 1.23 #endif
250 }
|
251 mike 1.2 }
252
|
253 mike 1.23 void Socket::disableBlocking(SocketHandle socket)
|
254 mike 1.2 {
255 #ifdef PEGASUS_OS_TYPE_WINDOWS
|
256 kumpf 1.28 unsigned long flag = 1; // Use "flag = 0" to enable blocking
|
257 mike 1.2 ioctlsocket(socket, FIONBIO, &flag);
|
258 carson.hovey 1.31 #elif PEGASUS_OS_VMS
259 int flag=1; // Use "flag = 0" to enable blocking
260 ioctl(socket, FIONBIO, &flag);
|
261 mike 1.2 #else
262 int flags = fcntl(socket, F_GETFL, 0);
|
263 kumpf 1.28 flags |= O_NONBLOCK; // Use "flags &= ~O_NONBLOCK" to enable blocking
|
264 mike 1.2 fcntl(socket, F_SETFL, flags);
265 #endif
266 }
267
268 void Socket::initializeInterface()
269 {
270 #ifdef PEGASUS_OS_TYPE_WINDOWS
271 if (_socketInterfaceRefCount == 0)
272 {
|
273 david.dillard 1.20 WSADATA tmp;
|
274 mike 1.2
|
275 kumpf 1.30 int err = WSAStartup(0x202, &tmp);
276 if (err != 0)
277 {
278 throw Exception(MessageLoaderParms(
279 "Common.Socket.WSASTARTUP_FAILED.WINDOWS",
280 "WSAStartup failed with error $0.",
281 err));
282 }
|
283 mike 1.2 }
284
285 _socketInterfaceRefCount++;
286 #endif
287 }
288
289 void Socket::uninitializeInterface()
290 {
291 #ifdef PEGASUS_OS_TYPE_WINDOWS
292 _socketInterfaceRefCount--;
293
294 if (_socketInterfaceRefCount == 0)
|
295 david.dillard 1.20 WSACleanup();
|
296 mike 1.2 #endif
297 }
298
|
299 thilo.boehm 1.25 //------------------------------------------------------------------------------
300 //
301 // _setTCPNoDelay()
302 //
303 //------------------------------------------------------------------------------
304
305 inline void _setTCPNoDelay(SocketHandle socket)
306 {
307 // This function disables "Nagle's Algorithm" also known as "the TCP delay
308 // algorithm", which causes read operations to obtain whatever data is
|
309 kumpf 1.26 // already in the input queue and then wait a little longer to see if
310 // more data arrives. This algorithm optimizes the case in which data is
311 // sent in only one direction but severely impairs performance of round
312 // trip servers. Disabling TCP delay is a standard technique for round
|
313 thilo.boehm 1.25 // trip servers.
314
315 int opt = 1;
316 setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*)&opt, sizeof(opt));
317 }
318 //------------------------------------------------------------------------------
319 //
320 // _setInformIfNewTCPIP()
321 //
322 //------------------------------------------------------------------------------
323 inline void _setInformIfNewTCPIP(SocketHandle socket)
324 {
325 #ifdef PEGASUS_OS_ZOS
326 // This function enables the notification of the CIM Server that a new
|
327 kumpf 1.26 // TCPIP transport layer is active. This is needed to be aware of a
|
328 thilo.boehm 1.25 // restart of the transport layer. When this option is in effect,
329 // the accetp(), select(), and read() request will receive an errno=EIO.
330 // Once this happens, the socket should be closed and create a new.
|
331 kumpf 1.26
|
332 thilo.boehm 1.25 int NewTcpipOn = 1;
|
333 kumpf 1.26 setibmsockopt(
334 socket,
335 SOL_SOCKET,
336 SO_EioIfNewTP,
337 (char*)&NewTcpipOn,
338 sizeof(NewTcpipOn));
|
339 thilo.boehm 1.25 #endif
340 }
341
342
343 SocketHandle Socket::createSocket(int domain, int type, int protocol)
344 {
345 SocketHandle newSocket;
346
|
347 kumpf 1.26 if (domain == AF_UNIX)
|
348 thilo.boehm 1.25 {
|
349 kumpf 1.26 return socket(domain,type,protocol);
|
350 thilo.boehm 1.25 }
351
352 bool sendTcpipMsg = true;
353
|
354 kumpf 1.26 while (1)
|
355 thilo.boehm 1.25 {
356 newSocket = socket(domain,type,protocol);
357
|
358 kumpf 1.27 if ((newSocket != PEGASUS_INVALID_SOCKET) ||
359 (getSocketError() != PEGASUS_NETWORK_TRYAGAIN))
360 {
361 break;
362 }
363
364 #ifdef PEGASUS_OS_ZOS
|
365 thilo.boehm 1.25 // The program should wait for transport layer to become ready.
366
|
367 kumpf 1.27 if (sendTcpipMsg)
|
368 thilo.boehm 1.25 {
|
369 kumpf 1.27 Logger::put_l(
370 Logger::STANDARD_LOG, System::CIMSERVER, Logger::INFORMATION,
371 "Common.Socket.WAIT_FOR_TCPIP",
372 "TCP/IP temporary unavailable.");
373 sendTcpipMsg = false;
374 }
|
375 thilo.boehm 1.25
|
376 kumpf 1.27 System::sleep(30);
377 #endif
|
378 thilo.boehm 1.25 } // wait for the transport layer become ready.
379
380 // Is the socket in an unrecoverable error ?
381 if (newSocket == PEGASUS_INVALID_SOCKET)
382 {
383 // return immediate
|
384 kumpf 1.26 return PEGASUS_INVALID_SOCKET;
385 }
386 else
387 {
|
388 thilo.boehm 1.25 // set aditional socket options
389 _setTCPNoDelay(newSocket);
390 _setInformIfNewTCPIP(newSocket);
391
|
392 kumpf 1.26 return newSocket;
|
393 thilo.boehm 1.25 }
394 }
395
|
396 mike 1.2 PEGASUS_NAMESPACE_END
|