1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5#include <pal.h>
6#include <unistd.h>
7#include <fcntl.h>
8#include <sys/types.h>
9#include <sys/stat.h>
10#include <limits.h>
11#include <pal_assert.h>
12#include "twowaypipe.h"
13
14// Creates a server side of the pipe.
15// Id is used to create pipes names and uniquely identify the pipe on the machine.
16// true - success, false - failure (use GetLastError() for more details)
17bool TwoWayPipe::CreateServer(const ProcessDescriptor& pd)
18{
19 _ASSERTE(m_state == NotInitialized);
20 if (m_state != NotInitialized)
21 return false;
22
23 PAL_GetTransportPipeName(m_inPipeName, pd.m_Pid, pd.m_ApplicationGroupId, "in");
24 PAL_GetTransportPipeName(m_outPipeName, pd.m_Pid, pd.m_ApplicationGroupId, "out");
25
26 unlink(m_inPipeName);
27
28 if (mkfifo(m_inPipeName, S_IRWXU) == -1)
29 {
30 return false;
31 }
32
33
34 unlink(m_outPipeName);
35
36 if (mkfifo(m_outPipeName, S_IRWXU) == -1)
37 {
38 unlink(m_inPipeName);
39 return false;
40 }
41
42 m_state = Created;
43 return true;
44}
45
46// Connects to a previously opened server side of the pipe.
47// Id is used to locate the pipe on the machine.
48// true - success, false - failure (use GetLastError() for more details)
49bool TwoWayPipe::Connect(const ProcessDescriptor& pd)
50{
51 _ASSERTE(m_state == NotInitialized);
52 if (m_state != NotInitialized)
53 return false;
54
55 //"in" and "out" are switched deliberately, because we're on the client
56 PAL_GetTransportPipeName(m_inPipeName, pd.m_Pid, pd.m_ApplicationGroupId, "out");
57 PAL_GetTransportPipeName(m_outPipeName, pd.m_Pid, pd.m_ApplicationGroupId, "in");
58
59 // Pipe opening order is reversed compared to WaitForConnection()
60 // in order to avaid deadlock.
61 m_outboundPipe = open(m_outPipeName, O_WRONLY);
62 if (m_outboundPipe == INVALID_PIPE)
63 {
64 return false;
65 }
66
67 m_inboundPipe = open(m_inPipeName, O_RDONLY);
68 if (m_inboundPipe == INVALID_PIPE)
69 {
70 close(m_outboundPipe);
71 m_outboundPipe = INVALID_PIPE;
72 return false;
73 }
74
75 m_state = ClientConnected;
76 return true;
77
78}
79
80// Waits for incoming client connections, assumes GetState() == Created
81// true - success, false - failure (use GetLastError() for more details)
82bool TwoWayPipe::WaitForConnection()
83{
84 _ASSERTE(m_state == Created);
85 if (m_state != Created)
86 return false;
87
88 m_inboundPipe = open(m_inPipeName, O_RDONLY);
89 if (m_inboundPipe == INVALID_PIPE)
90 {
91 return false;
92 }
93
94 m_outboundPipe = open(m_outPipeName, O_WRONLY);
95 if (m_outboundPipe == INVALID_PIPE)
96 {
97 close(m_inboundPipe);
98 m_inboundPipe = INVALID_PIPE;
99 return false;
100 }
101
102 m_state = ServerConnected;
103 return true;
104}
105
106// Reads data from pipe. Returns number of bytes read or a negative number in case of an error.
107// use GetLastError() for more details
108// UNIXTODO - mjm 9/6/15 - does not set last error on failure
109int TwoWayPipe::Read(void *buffer, DWORD bufferSize)
110{
111 _ASSERTE(m_state == ServerConnected || m_state == ClientConnected);
112
113 int totalBytesRead = 0;
114 int bytesRead;
115 int cb = bufferSize;
116
117 while ((bytesRead = (int)read(m_inboundPipe, buffer, cb)) > 0)
118 {
119 totalBytesRead += bytesRead;
120 _ASSERTE(totalBytesRead <= bufferSize);
121 if (totalBytesRead >= bufferSize)
122 {
123 break;
124 }
125
126 buffer = (char*)buffer + bytesRead;
127 cb -= bytesRead;
128 }
129
130 return bytesRead == -1 ? -1 : totalBytesRead;
131}
132
133// Writes data to pipe. Returns number of bytes written or a negative number in case of an error.
134// use GetLastError() for more details
135// UNIXTODO - mjm 9/6/15 - does not set last error on failure
136int TwoWayPipe::Write(const void *data, DWORD dataSize)
137{
138 _ASSERTE(m_state == ServerConnected || m_state == ClientConnected);
139
140 int totalBytesWritten = 0;
141 int bytesWritten;
142 int cb = dataSize;
143
144 while ((bytesWritten = (int)write(m_outboundPipe, data, cb)) > 0)
145 {
146 totalBytesWritten += bytesWritten;
147 _ASSERTE(totalBytesWritten <= dataSize);
148 if (totalBytesWritten >= dataSize)
149 {
150 break;
151 }
152
153 data = (char*)data + bytesWritten;
154 cb -= bytesWritten;
155 }
156
157 return bytesWritten == -1 ? -1 : totalBytesWritten;
158}
159
160// Disconnect server or client side of the pipe.
161// true - success, false - failure (use GetLastError() for more details)
162bool TwoWayPipe::Disconnect()
163{
164 // IMPORTANT NOTE: This function must not call any signal unsafe functions
165 // since it is called from signal handlers.
166 // That includes ASSERT and TRACE macros.
167
168 if (m_state == ServerConnected || m_state == Created)
169 {
170 unlink(m_inPipeName);
171 unlink(m_outPipeName);
172 }
173
174 m_state = NotInitialized;
175 return true;
176}
177
178// Used by debugger side (RS) to cleanup the target (LS) named pipes
179// and semaphores when the debugger detects the debuggee process exited.
180void TwoWayPipe::CleanupTargetProcess()
181{
182 unlink(m_inPipeName);
183 unlink(m_outPipeName);
184}
185