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 
60 namespace BLOCXX_NAMESPACE
61 {
62 
63 namespace
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 
78 int 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 
134 AutoDescriptor 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 
179 int 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 
238 AutoDescriptor 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