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
DwarfLoadingStateHandler()36 DwarfLoadingStateHandler::DwarfLoadingStateHandler()
37 :
38 ImageDebugLoadingStateHandler()
39 {
40 }
41
42
~DwarfLoadingStateHandler()43 DwarfLoadingStateHandler::~DwarfLoadingStateHandler()
44 {
45 }
46
47
48 bool
SupportsState(SpecificImageDebugInfoLoadingState * state)49 DwarfLoadingStateHandler::SupportsState(
50 SpecificImageDebugInfoLoadingState* state)
51 {
52 return dynamic_cast<DwarfImageDebugInfoLoadingState*>(state) != NULL;
53 }
54
55
56 void
HandleState(SpecificImageDebugInfoLoadingState * state,UserInterface * interface)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
_GetMatchingDebugInfoPackage(const BString & debugFileName,BString & _packageName)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
_GetResolvableName(const BString & debugFileName,BString & _resolvableName,BPackageVersion & _resolvableVersion)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