blocxx
DescriptorUtils_noexcept.cpp
Go to the documentation of this file.
1/*******************************************************************************
2* Copyright (C) 2005, Quest Software, Inc. All rights reserved.
3* Copyright (C) 2006, Novell, Inc. All rights reserved.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7*
8* * Redistributions of source code must retain the above copyright notice,
9* this list of conditions and the following disclaimer.
10* * Redistributions in binary form must reproduce the above copyright
11* notice, this list of conditions and the following disclaimer in the
12* documentation and/or other materials provided with the distribution.
13* * Neither the name of
14* Quest Software, Inc.,
15* nor Novell, Inc.,
16* nor the names of its contributors or employees may be used to
17* endorse or promote products derived from this software without
18* specific prior written permission.
19*
20* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30* POSSIBILITY OF SUCH DAMAGE.
31*******************************************************************************/
32
33
40// THIS CODE MUST NOT THROW EXCEPTIONS. IT IS ALSO HIGHLY PREFERRED THAT
41// IT NOT RELY ON ANY LIBRARY OTHER THAN STANDARD SYSTEM LIBRARIES AND THE
42// STANDARD C++ LIBRARY, AS IT IS USED IN libowcprivman, AND WE WANT TO AVOID
43// LINKING OTHER LIBRARIES IN WITH libowcprivman.
44
45#include "blocxx/BLOCXX_config.h"
48
49#include <cstring>
50#include <sys/types.h>
51#ifdef BLOCXX_HAVE_SYS_SOCKET_H
52#include <sys/socket.h>
53#endif
54#ifndef BLOCXX_WIN32
55#include <sys/uio.h>
56#else
57#include "blocxx/WinProcessUtils.hpp"
58#endif
59
60namespace BLOCXX_NAMESPACE
61{
62
63namespace
64{
65 char const MAGIC_CHAR = '\xa5';
66
67 AutoDescriptor copy_error(char * dst, size_t dstsz, char const * src)
68 {
69 std::strncpy(dst, src, dstsz);
70 dst[dstsz - 1] = '\0';
71 return AutoDescriptor();
72 }
73}
74
75
76#ifdef BLOCXX_WIN32
77
78int passDescriptor(Descriptor streamPipe, Descriptor descriptor, ProcId targetProcessHd)
79{
80 if (streamPipe == BLOCXX_INVALID_HANDLE)
81 {
82 return -1;
83 }
84
85 DWORD targetProcessId = WinUtils::getProcessIdNT(targetProcessHd);
86
87 DWORD rc = -1;
88 HANDLE dupDescriptor = INVALID_HANDLE_VALUE;
89 HANDLE hProcess = targetProcessId == 0 ? GetCurrentProcess() : OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetProcessId);
90
91 BOOL fSuccess = DuplicateHandle(GetCurrentProcess(), descriptor,
92 hProcess, &dupDescriptor, 0,
93 FALSE, DUPLICATE_SAME_ACCESS);
94
95 CloseHandle(hProcess);
96
97 if (!fSuccess)
98 {
99 return -1;
100 }
101
102 OVERLAPPED ovl;
103 ZeroMemory(&ovl, sizeof(OVERLAPPED));
104
105 fSuccess = WriteFile(streamPipe, &dupDescriptor, sizeof(long), &rc, &ovl);
106
107 if (!fSuccess)
108 {
109 DWORD lastError = GetLastError();
110
111 if (lastError != ERROR_IO_INCOMPLETE && lastError != ERROR_IO_PENDING)
112 {
113 SetLastError(lastError);
114 return -1;
115 }
116 else
117 {
118 DWORD waitFlag = WaitForSingleObject(streamPipe, INFINITE);
119
120 if (waitFlag == WAIT_OBJECT_0)
121 {
122 GetOverlappedResult(streamPipe, &ovl, &rc, FALSE);
123 }
124 else
125 {
126 return -1;
127 }
128 }
129 }
130
131 return rc;
132}
133
134AutoDescriptor receiveDescriptor(Descriptor streamPipe, char * errbuf, size_t bufsz)
135{
136 long desc;
137 DWORD rc = -1;
138 BOOL bSuccess = FALSE;
139
140 if (streamPipe != BLOCXX_INVALID_HANDLE)
141 {
142 OVERLAPPED ovl;
143 ZeroMemory(&ovl, sizeof(OVERLAPPED));
144
145 bSuccess = ReadFile(streamPipe, &desc, sizeof(long), &rc, &ovl);
146
147 if (!bSuccess)
148 {
149 DWORD lastError = GetLastError();
150
151 if (lastError != ERROR_IO_INCOMPLETE && lastError != ERROR_IO_PENDING)
152 {
153 SetLastError(lastError);
154 return copy_error(errbuf, bufsz, "ReadFile() failed");
155 }
156 else
157 {
158 DWORD waitFlag = WaitForSingleObject(streamPipe, INFINITE);
159
160 if (waitFlag == WAIT_OBJECT_0)
161 {
162 GetOverlappedResult(streamPipe, &ovl, &rc, FALSE);
163 }
164 else
165 {
166 return copy_error(errbuf, bufsz, "WaitForSingleObject() failed");
167 }
168 }
169 }
170
171 return AutoDescriptor(reinterpret_cast<HANDLE>(desc));
172 }
173
174 return copy_error(errbuf, bufsz, "receiveDescriptor() error");
175}
176
177#else
178
179int passDescriptor(Descriptor streamPipe, Descriptor descriptor, ProcId targetProcessId)
180{
181 struct msghdr msg;
182 ::memset(&msg, 0, sizeof(msg));
183 struct iovec iov[1];
184 ::memset(iov, 0, sizeof(iov[0]));
185
186#ifdef BLOCXX_HAVE_MSGHDR_MSG_CONTROL
187
188// We need the newer CMSG_LEN() and CMSG_SPACE() macros, but few
189// implementations support them today. These two macros really need
190// an ALIGN() macro, but each implementation does this differently.
191#ifndef CMSG_LEN
192#define CMSG_LEN(size) (sizeof(struct cmsghdr) + (size))
193#endif
194
195#ifndef CMSG_SPACE
196#define CMSG_SPACE(size) (sizeof(struct cmsghdr) + (size))
197#endif
198
199 union {
200 struct cmsghdr cm;
201 char control[CMSG_SPACE(sizeof(int))];
202 } control_un;
203 ::memset(&control_un, 0, sizeof(control_un));
204 struct cmsghdr * cmptr;
205
206 msg.msg_control = control_un.control;
207 msg.msg_controllen = sizeof(control_un.control);
208
209 cmptr = CMSG_FIRSTHDR(&msg);
210 cmptr->cmsg_len = CMSG_LEN(sizeof(int));
211 cmptr->cmsg_level = SOL_SOCKET;
212 cmptr->cmsg_type = SCM_RIGHTS;
213 *(reinterpret_cast<int *>(CMSG_DATA(cmptr))) = descriptor;
214#else
215
216#ifdef BLOCXX_NCR
217 void *temp_cast = &descriptor;
218 msg.msg_accrights = static_cast<caddr_t>(temp_cast);
219#else
220 msg.msg_accrights = static_cast<caddr_t>(&descriptor);
221#endif
222
223 msg.msg_accrightslen = sizeof(int);
224#endif
225
226 msg.msg_name = 0;
227 msg.msg_namelen = 0;
228
229 char dummy[1] = { MAGIC_CHAR };
230 iov[0].iov_base = dummy;
231 iov[0].iov_len = 1;
232 msg.msg_iov = iov;
233 msg.msg_iovlen = 1;
234
235 return ::sendmsg(streamPipe, &msg, 0);
236}
237
238AutoDescriptor receiveDescriptor(Descriptor streamPipe, char * errbuf, size_t bufsz)
239{
240 struct msghdr msg;
241 struct iovec iov[1];
242
243 msg = msghdr(); // zero-init to make valgrind happy
244#ifdef BLOCXX_HAVE_MSGHDR_MSG_CONTROL
245 union {
246 struct cmsghdr cm;
247 char control[CMSG_SPACE(sizeof(int))];
248 } control_un;
249
250 msg.msg_control = control_un.control;
251 msg.msg_controllen = sizeof(control_un.control);
252#else
253 int newfd = -1;
254
255#ifdef BLOCXX_NCR
256 void *temp_cast = &newfd;
257 msg.msg_accrights = static_cast<caddr_t>(temp_cast);
258#else
259 msg.msg_accrights = static_cast<caddr_t>(&newfd);
260#endif
261
262 msg.msg_accrightslen = sizeof(int);
263#endif
264
265 msg.msg_name = 0;
266 msg.msg_namelen = 0;
267
268 char dummy[1] = { '\x7F' };
269 iov[0].iov_base = dummy;
270 iov[0].iov_len = 1;
271 msg.msg_iov = iov;
272 msg.msg_iovlen = 1;
273
274 ssize_t n = ::recvmsg(streamPipe, &msg, 0);
275 if (n == 0)
276 {
277 return copy_error(errbuf, bufsz,
278 "unexpected end of input when receiving handle");
279 }
280 if (n < 0)
281 {
282 return copy_error(errbuf, bufsz, "recvmsg() failed");
283 }
284 if (n != 1)
285 {
286 return copy_error(errbuf, bufsz, "received more than 1 byte.");
287 }
288 if (dummy[0] != MAGIC_CHAR)
289 {
290 return copy_error(errbuf, bufsz, "bad magic char when receiving handle");
291 }
292
293
294#ifdef BLOCXX_HAVE_MSGHDR_MSG_CONTROL
295 struct cmsghdr * cmptr = CMSG_FIRSTHDR(&msg);
296 if (!cmptr)
297 {
298 return copy_error(errbuf, bufsz,
299 "missing control message when receiving handle");
300 }
301 // as far as I can tell, HP-UX is just broken and sets cmptr->cmsg_len to 12. Things work anyway.
302#if !defined (BLOCXX_HPUX)
303 if (cmptr->cmsg_len != CMSG_LEN(sizeof(int)))
304 {
305 return copy_error(errbuf, bufsz,
306 "cmptr->cmsg_len != CMSG_LEN(sizeof(int)) when receiving handle");
307 }
308#endif
309 if (cmptr->cmsg_level != SOL_SOCKET)
310 {
311 return copy_error(errbuf, bufsz,
312 "control level != SOL_SOCKET when receiving handle");
313 }
314 if (cmptr->cmsg_type != SCM_RIGHTS)
315 {
316 return copy_error(errbuf, bufsz,
317 "control type != SCM_RIGHTS when receiving handle");
318 }
319 return AutoDescriptor(*(reinterpret_cast<int *>(CMSG_DATA(cmptr))));
320#else
321 if (msg.msg_accrightslen != sizeof(int))
322 {
323 return copy_error(errbuf, bufsz,
324 "bad control message when receiving handle");
325 }
326 return AutoDescriptor(newfd);
327#endif
328}
329
330#endif
331
332
333} // end namespace BLOCXX_NAMESPACE
#define BLOCXX_INVALID_HANDLE
Definition Types.hpp:136
PURPOSE: The AutoResource class template is an analog of std::auto_ptr for managing arbitrary resourc...
Taken from RFC 1321.
AutoDescriptor receiveDescriptor(Descriptor streamPipe)
Gets a Descriptor from the peer.
int passDescriptor(Descriptor streamPipe, Descriptor descriptor, ProcId targetProcessId)
Sends a Descriptor to the peer.
AutoResource< AutoDescriptorPolicy > AutoDescriptor
An analog of std::auto_ptr for descriptors.