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