1/*
2 * Copyright 2016, Rene Gollent, rene@gollent.com.
3 * Copyright 2016, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Distributed under the terms of the MIT License.
5 */
6
7#include "LocalTargetHostInterface.h"
8
9#include <set>
10
11#include <stdio.h>
12#include <unistd.h>
13
14#include <image.h>
15
16#include <AutoDeleter.h>
17#include <AutoLocker.h>
18#include <system_info.h>
19#include <util/KMessage.h>
20
21#include "debug_utils.h"
22
23#include "CoreFile.h"
24#include "CoreFileDebuggerInterface.h"
25#include "LocalDebuggerInterface.h"
26#include "TargetHost.h"
27
28using std::set;
29
30LocalTargetHostInterface::LocalTargetHostInterface()
31	:
32	TargetHostInterface(),
33	fTargetHost(NULL),
34	fDataPort(-1)
35{
36	SetName("Local");
37}
38
39
40LocalTargetHostInterface::~LocalTargetHostInterface()
41{
42	Close();
43
44	if (fTargetHost != NULL)
45		fTargetHost->ReleaseReference();
46}
47
48
49status_t
50LocalTargetHostInterface::Init(Settings* settings)
51{
52	char hostname[HOST_NAME_MAX + 1];
53	status_t error = gethostname(hostname, sizeof(hostname));
54	if (error != B_OK) {
55		fprintf(stderr, "gethostname() failed, defaults to localhost\n");
56		strlcpy(hostname, "localhost", sizeof(hostname));
57	}
58
59	fTargetHost = new(std::nothrow) TargetHost(hostname);
60	if (fTargetHost == NULL)
61		return B_NO_MEMORY;
62
63	team_info info;
64	error = get_team_info(B_CURRENT_TEAM, &info);
65	if (error != B_OK)
66		return error;
67
68	char buffer[128];
69	snprintf(buffer, sizeof(buffer), "LocalTargetHostInterface %" B_PRId32,
70		info.team);
71
72	fDataPort = create_port(100, buffer);
73	if (fDataPort < 0)
74		return fDataPort;
75
76	fPortWorker = spawn_thread(_PortLoop, "Local Target Host Loop",
77		B_NORMAL_PRIORITY, this);
78	if (fPortWorker < 0)
79		return fPortWorker;
80
81	resume_thread(fPortWorker);
82
83	AutoLocker<TargetHost> hostLocker(fTargetHost);
84
85	error = __start_watching_system(-1,
86		B_WATCH_SYSTEM_TEAM_CREATION | B_WATCH_SYSTEM_TEAM_DELETION,
87		fDataPort, 0);
88	if (error != B_OK)
89		return error;
90
91	int32 cookie = 0;
92	while (get_next_team_info(&cookie, &info) == B_OK) {
93		error = fTargetHost->AddTeam(info);
94		if (error != B_OK)
95			return error;
96	}
97
98	snprintf(buffer, sizeof(buffer), "Local (%s)", hostname);
99	SetName(buffer);
100
101	return B_OK;
102}
103
104
105void
106LocalTargetHostInterface::Close()
107{
108	if (fDataPort > 0) {
109		__stop_watching_system(-1,
110			B_WATCH_SYSTEM_TEAM_CREATION | B_WATCH_SYSTEM_TEAM_DELETION,
111			fDataPort, 0);
112
113		delete_port(fDataPort);
114		fDataPort = -1;
115	}
116
117	if (fPortWorker > 0) {
118		wait_for_thread(fPortWorker, NULL);
119		fPortWorker = -1;
120	}
121}
122
123
124bool
125LocalTargetHostInterface::IsLocal() const
126{
127	return true;
128}
129
130
131bool
132LocalTargetHostInterface::Connected() const
133{
134	return true;
135}
136
137
138TargetHost*
139LocalTargetHostInterface::GetTargetHost()
140{
141	return fTargetHost;
142}
143
144
145status_t
146LocalTargetHostInterface::Attach(team_id teamID, thread_id threadID,
147	DebuggerInterface*& _interface) const
148{
149	if (teamID < 0 && threadID < 0)
150		return B_BAD_VALUE;
151
152	status_t error;
153	if (teamID < 0) {
154		thread_info threadInfo;
155		error = get_thread_info(threadID, &threadInfo);
156		if (error != B_OK)
157			return error;
158
159		teamID = threadInfo.team;
160	}
161
162	LocalDebuggerInterface* interface
163		= new(std::nothrow) LocalDebuggerInterface(teamID);
164	if (interface == NULL)
165		return B_NO_MEMORY;
166
167	BReference<DebuggerInterface> interfaceReference(interface, true);
168	error = interface->Init();
169	if (error != B_OK)
170		return error;
171
172	_interface = interface;
173	interfaceReference.Detach();
174	return B_OK;
175}
176
177
178status_t
179LocalTargetHostInterface::CreateTeam(int commandLineArgc,
180	const char* const* arguments, team_id& _teamID) const
181{
182	thread_id thread = load_program(arguments, commandLineArgc, false);
183	if (thread < 0)
184		return thread;
185
186	// main thread ID == team ID.
187	_teamID = thread;
188	return B_OK;
189}
190
191
192status_t
193LocalTargetHostInterface::LoadCore(const char* coreFilePath,
194	DebuggerInterface*& _interface, thread_id& _thread) const
195{
196	// load the core file
197	CoreFile* coreFile = new(std::nothrow) CoreFile;
198	if (coreFile == NULL)
199		return B_NO_MEMORY;
200	ObjectDeleter<CoreFile> coreFileDeleter(coreFile);
201
202	status_t error = coreFile->Init(coreFilePath);
203	if (error != B_OK)
204		return error;
205
206	// create the debugger interface
207	CoreFileDebuggerInterface* interface
208		= new(std::nothrow) CoreFileDebuggerInterface(coreFile);
209	if (interface == NULL)
210		return B_NO_MEMORY;
211	coreFileDeleter.Detach();
212
213	BReference<DebuggerInterface> interfaceReference(interface, true);
214	error = interface->Init();
215	if (error != B_OK)
216		return error;
217
218	const CoreFileTeamInfo& teamInfo = coreFile->GetTeamInfo();
219	_thread = teamInfo.Id();
220	_interface = interface;
221	interfaceReference.Detach();
222
223	return B_OK;
224}
225
226
227status_t
228LocalTargetHostInterface::FindTeamByThread(thread_id thread,
229	team_id& _teamID) const
230{
231	thread_info info;
232	status_t error = get_thread_info(thread, &info);
233	if (error != B_OK)
234		return error;
235
236	_teamID = info.team;
237	return B_OK;
238}
239
240
241status_t
242LocalTargetHostInterface::_PortLoop(void* arg)
243{
244	LocalTargetHostInterface* interface = (LocalTargetHostInterface*)arg;
245	set<team_id> waitingTeams;
246
247	for (;;) {
248		status_t error;
249		bool addToWaiters;
250		char buffer[2048];
251		int32 messageCode;
252		team_id team;
253
254		ssize_t size = read_port_etc(interface->fDataPort, &messageCode,
255			buffer, sizeof(buffer), B_TIMEOUT, waitingTeams.empty()
256				? B_INFINITE_TIMEOUT : 20000);
257		if (size == B_INTERRUPTED)
258			continue;
259		else if (size == B_TIMED_OUT && !waitingTeams.empty()) {
260			for (set<team_id>::iterator it = waitingTeams.begin();
261				it != waitingTeams.end(); ++it) {
262				team = *it;
263				error = interface->_HandleTeamEvent(team,
264					B_TEAM_CREATED, addToWaiters);
265				if (error != B_OK)
266					continue;
267				else if (!addToWaiters) {
268					waitingTeams.erase(it);
269					if (waitingTeams.empty())
270						break;
271					it = waitingTeams.begin();
272				}
273			}
274			continue;
275		} else if (size < 0)
276			return size;
277
278		KMessage message;
279		size = message.SetTo(buffer);
280		if (size != B_OK)
281			continue;
282
283		if (message.What() != B_SYSTEM_OBJECT_UPDATE)
284			continue;
285
286		int32 opcode = 0;
287		if (message.FindInt32("opcode", &opcode) != B_OK)
288			continue;
289
290		team = -1;
291		if (message.FindInt32("team", &team) != B_OK)
292			continue;
293
294		error = interface->_HandleTeamEvent(team, opcode,
295			addToWaiters);
296		if (error != B_OK)
297			continue;
298		if (opcode == B_TEAM_CREATED && addToWaiters) {
299			try {
300				waitingTeams.insert(team);
301			} catch (...) {
302				continue;
303			}
304		}
305	}
306
307	return B_OK;
308}
309
310
311status_t
312LocalTargetHostInterface::_HandleTeamEvent(team_id team, int32 opcode,
313	bool& addToWaiters)
314{
315	addToWaiters = false;
316	AutoLocker<TargetHost> locker(fTargetHost);
317	switch (opcode) {
318		case B_TEAM_CREATED:
319		case B_TEAM_EXEC:
320		{
321			team_info info;
322			status_t error = get_team_info(team, &info);
323			// this team is already gone, no point in sending a notification
324			if (error == B_BAD_TEAM_ID)
325				return B_OK;
326			else if (error != B_OK)
327				return error;
328			else {
329				int32 cookie = 0;
330				image_info imageInfo;
331				addToWaiters = true;
332				while (get_next_image_info(team, &cookie, &imageInfo)
333					== B_OK) {
334					if (imageInfo.type == B_APP_IMAGE) {
335						addToWaiters = false;
336						break;
337					}
338				}
339				if (addToWaiters)
340					return B_OK;
341			}
342
343			if (opcode == B_TEAM_CREATED)
344				fTargetHost->AddTeam(info);
345			else
346				fTargetHost->UpdateTeam(info);
347			break;
348		}
349
350		case B_TEAM_DELETED:
351		{
352			fTargetHost->RemoveTeam(team);
353			break;
354		}
355
356		default:
357		{
358			break;
359		}
360	}
361
362	return B_OK;
363}
364