xref: /haiku/src/kits/debugger/debug_info/loading_state_handlers/DwarfLoadingStateHandler.cpp (revision 9a6a20d4689307142a7ed26a1437ba47e244e73f)
1 /*
2  * Copyright 2014, Rene Gollent, rene@gollent.com.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "DwarfLoadingStateHandler.h"
8 
9 #include <sys/wait.h>
10 
11 #include <Entry.h>
12 #include <InterfaceDefs.h>
13 #include <Path.h>
14 #include <package/solver/Solver.h>
15 #include <package/solver/SolverPackage.h>
16 
17 #include "AutoDeleter.h"
18 #include "DwarfFile.h"
19 #include "DwarfImageDebugInfoLoadingState.h"
20 #include "package/manager/PackageManager.h"
21 #include "Tracing.h"
22 #include "UserInterface.h"
23 
24 
25 using namespace BPackageKit;
26 using BPackageKit::BManager::BPrivate::BPackageManager;
27 
28 
29 enum {
30 	USER_CHOICE_INSTALL_PACKAGE = 0,
31 	USER_CHOICE_LOCATE_FILE ,
32 	USER_CHOICE_SKIP
33 };
34 
35 
36 DwarfLoadingStateHandler::DwarfLoadingStateHandler()
37 	:
38 	ImageDebugLoadingStateHandler()
39 {
40 }
41 
42 
43 DwarfLoadingStateHandler::~DwarfLoadingStateHandler()
44 {
45 }
46 
47 
48 bool
49 DwarfLoadingStateHandler::SupportsState(
50 	SpecificImageDebugInfoLoadingState* state)
51 {
52 	return dynamic_cast<DwarfImageDebugInfoLoadingState*>(state) != NULL;
53 }
54 
55 
56 void
57 DwarfLoadingStateHandler::HandleState(
58 	SpecificImageDebugInfoLoadingState* state, UserInterface* interface)
59 {
60 	DwarfImageDebugInfoLoadingState* dwarfState
61 		= dynamic_cast<DwarfImageDebugInfoLoadingState*>(state);
62 
63 	if (dwarfState == NULL) {
64 		ERROR("DwarfLoadingStateHandler::HandleState() passed "
65 			"non-dwarf state object %p.", state);
66 		return;
67 	}
68 
69 	DwarfFileLoadingState& fileState = dwarfState->GetFileState();
70 
71 	if (!interface->IsInteractive()) {
72 		fileState.state = DWARF_FILE_LOADING_STATE_USER_INPUT_PROVIDED;
73 		return;
74 	}
75 
76 	BString requiredPackage;
77 	try {
78 		// Package Kit may throw exceptions.
79 		_GetMatchingDebugInfoPackage(fileState.externalInfoFileName,
80 			requiredPackage);
81 	} catch (...) {
82 		requiredPackage = BString();
83 	}
84 
85 	// loop so that the user has a chance to retry or locate the file manually
86 	// in case package installation fails, e.g. due to transient download
87 	// issues.
88 	for (;;) {
89 		int32 choice;
90 		BString message;
91 		if (requiredPackage.IsEmpty()) {
92 			message.SetToFormat("The debug information file '%s' for "
93 				"image '%s' is missing. Would you like to locate the file "
94 				"manually?", fileState.externalInfoFileName.String(),
95 				fileState.dwarfFile->Name());
96 			choice = interface->SynchronouslyAskUser("Debug info missing",
97 				message.String(), "Locate", "Skip", NULL);
98 			if (choice == 0)
99 				choice = USER_CHOICE_LOCATE_FILE;
100 			else if (choice == 1)
101 				choice = USER_CHOICE_SKIP;
102 		} else {
103 			message.SetToFormat("The debug information file '%s' for "
104 				"image '%s' is missing, but can be found in the package "
105 				"'%s'. Would you like to install it, or locate the file "
106 				"manually?", fileState.externalInfoFileName.String(),
107 				fileState.dwarfFile->Name(), requiredPackage.String());
108 			choice = interface->SynchronouslyAskUser("Debug info missing",
109 				message.String(), "Install", "Locate", "Skip");
110 		}
111 
112 		if (choice == USER_CHOICE_INSTALL_PACKAGE) {
113 			// TODO: integrate the package installation functionality directly.
114 			BString command;
115 			command.SetToFormat("/bin/pkgman install -y %s",
116 				requiredPackage.String());
117 			BString notification;
118 			notification.SetToFormat("Installing package %s" B_UTF8_ELLIPSIS,
119 				requiredPackage.String());
120 			interface->NotifyBackgroundWorkStatus(notification);
121 			int error = system(command.String());
122 			if (interface->IsInteractive()) {
123 				if (WIFEXITED(error)) {
124 					error = WEXITSTATUS(error);
125 					if (error == B_OK)
126 						break;
127 					message.SetToFormat("Package installation failed: %s.",
128 						strerror(error));
129 					interface->NotifyUser("Error", message.String(),
130 						USER_NOTIFICATION_ERROR);
131 					continue;
132 				}
133 			}
134 			break;
135 		} else if (choice == USER_CHOICE_LOCATE_FILE) {
136 			entry_ref ref;
137 			interface->SynchronouslyAskUserForFile(&ref);
138 			BPath path(&ref);
139 			if (path.InitCheck() == B_OK)
140 				fileState.locatedExternalInfoPath = path.Path();
141 			break;
142 		} else
143 			break;
144 	}
145 
146 	fileState.state = DWARF_FILE_LOADING_STATE_USER_INPUT_PROVIDED;
147 }
148 
149 
150 status_t
151 DwarfLoadingStateHandler::_GetMatchingDebugInfoPackage(
152 	const BString& debugFileName, BString& _packageName)
153 {
154 	BString resolvableName;
155 	BPackageVersion requiredVersion;
156 	BPackageManager::ClientInstallationInterface clientInterface;
157 	BPackageManager::UserInteractionHandler handler;
158 
159 	BPackageManager packageManager(B_PACKAGE_INSTALLATION_LOCATION_SYSTEM,
160 		&clientInterface, &handler);
161 	packageManager.Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES
162 		| BPackageManager::B_ADD_REMOTE_REPOSITORIES);
163 	BObjectList<BSolverPackage> packages;
164 	status_t error = _GetResolvableName(debugFileName, resolvableName,
165 		requiredVersion);
166 	if (error != B_OK)
167 		return error;
168 
169 	error = packageManager.Solver()->FindPackages(resolvableName,
170 		BSolver::B_FIND_IN_PROVIDES, packages);
171 	if (error != B_OK)
172 		return error;
173 	else if (packages.CountItems() == 0)
174 		return B_ENTRY_NOT_FOUND;
175 
176 	for (int32 i = 0; i < packages.CountItems(); i++) {
177 		BSolverPackage* package = packages.ItemAt(i);
178 		if (requiredVersion.Compare(package->Version()) == 0) {
179 			_packageName = package->Name();
180 			return B_OK;
181 		}
182 	}
183 
184 	return B_ENTRY_NOT_FOUND;
185 }
186 
187 
188 status_t
189 DwarfLoadingStateHandler::_GetResolvableName(const BString& debugFileName,
190 	BString& _resolvableName, BPackageVersion& _resolvableVersion)
191 {
192 	BString fileName;
193 	BString packageName;
194 	BString packageVersion;
195 
196 	int32 startIndex = 0;
197 	int32 endIndex = debugFileName.FindFirst('(');
198 	if (endIndex < 0)
199 		return B_BAD_VALUE;
200 
201 	debugFileName.CopyInto(fileName, 0, endIndex);
202 	startIndex = endIndex + 1;
203 	endIndex = debugFileName.FindFirst('-', startIndex);
204 	if (endIndex < 0)
205 		return B_BAD_VALUE;
206 
207 	debugFileName.CopyInto(packageName, startIndex, endIndex - startIndex);
208 	startIndex = endIndex + 1;
209 	endIndex = debugFileName.FindFirst(')', startIndex);
210 	if (endIndex < 0)
211 		return B_BAD_VALUE;
212 
213 	debugFileName.CopyInto(packageVersion, startIndex,
214 		endIndex - startIndex);
215 
216 	_resolvableName.SetToFormat("debuginfo:%s(%s)", fileName.String(), packageName.String());
217 
218 	return _resolvableVersion.SetTo(packageVersion);
219 }
220