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 BString requiredPackage; 72 _GetMatchingDebugInfoPackage(fileState.externalInfoFileName, 73 requiredPackage); 74 75 // loop so that the user has a chance to retry or locate the file manually 76 // in case package installation fails, e.g. due to transient download 77 // issues. 78 for (;;) { 79 int32 choice; 80 BString message; 81 if (interface->IsInteractive()) { 82 if (requiredPackage.IsEmpty()) { 83 message.SetToFormat("The debug information file '%s' for " 84 "image '%s' is missing. Would you like to locate the file " 85 "manually?", fileState.externalInfoFileName.String(), 86 fileState.dwarfFile->Name()); 87 choice = interface->SynchronouslyAskUser("Debug info missing", 88 message.String(), "Locate", "Skip", NULL); 89 if (choice == 0) 90 choice = USER_CHOICE_LOCATE_FILE; 91 else if (choice == 1) 92 choice = USER_CHOICE_SKIP; 93 } else { 94 message.SetToFormat("The debug information file '%s' for " 95 "image '%s' is missing, but can be found in the package " 96 "'%s'. Would you like to install it, or locate the file " 97 "manually?", fileState.externalInfoFileName.String(), 98 fileState.dwarfFile->Name(), requiredPackage.String()); 99 choice = interface->SynchronouslyAskUser("Debug info missing", 100 message.String(), "Install", "Locate", "Skip"); 101 } 102 } else 103 choice = USER_CHOICE_SKIP; 104 105 if (choice == USER_CHOICE_INSTALL_PACKAGE) { 106 // TODO: integrate the package installation functionality directly. 107 BString command; 108 command.SetToFormat("/bin/pkgman install -y %s", 109 requiredPackage.String()); 110 BString notification; 111 notification.SetToFormat("Installing package %s" B_UTF8_ELLIPSIS, 112 requiredPackage.String()); 113 interface->NotifyBackgroundWorkStatus(notification); 114 int error = system(command.String()); 115 if (interface->IsInteractive()) { 116 if (WIFEXITED(error)) { 117 error = WEXITSTATUS(error); 118 if (error == B_OK) 119 break; 120 message.SetToFormat("Package installation failed: %s.", 121 strerror(error)); 122 interface->NotifyUser("Error", message.String(), 123 USER_NOTIFICATION_ERROR); 124 continue; 125 } 126 } 127 break; 128 } else if (choice == USER_CHOICE_LOCATE_FILE) { 129 entry_ref ref; 130 interface->SynchronouslyAskUserForFile(&ref); 131 BPath path(&ref); 132 if (path.InitCheck() == B_OK) 133 fileState.locatedExternalInfoPath = path.Path(); 134 break; 135 } else 136 break; 137 } 138 139 fileState.state = DWARF_FILE_LOADING_STATE_USER_INPUT_PROVIDED; 140 } 141 142 143 status_t 144 DwarfLoadingStateHandler::_GetMatchingDebugInfoPackage( 145 const BString& debugFileName, BString& _packageName) 146 { 147 BString resolvableName; 148 BPackageVersion requiredVersion; 149 BPackageManager::ClientInstallationInterface clientInterface; 150 BPackageManager::UserInteractionHandler handler; 151 152 BPackageManager packageManager(B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, 153 &clientInterface, &handler); 154 packageManager.Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES 155 | BPackageManager::B_ADD_REMOTE_REPOSITORIES); 156 BObjectList<BSolverPackage> packages; 157 status_t error = _GetResolvableName(debugFileName, resolvableName, 158 requiredVersion); 159 if (error != B_OK) 160 return error; 161 162 error = packageManager.Solver()->FindPackages(resolvableName, 163 BSolver::B_FIND_IN_PROVIDES, packages); 164 if (error != B_OK) 165 return error; 166 else if (packages.CountItems() == 0) 167 return B_ENTRY_NOT_FOUND; 168 169 for (int32 i = 0; i < packages.CountItems(); i++) { 170 BSolverPackage* package = packages.ItemAt(i); 171 if (requiredVersion.Compare(package->Version()) == 0) { 172 _packageName = package->Name(); 173 return B_OK; 174 } 175 } 176 177 return B_ENTRY_NOT_FOUND; 178 } 179 180 181 status_t 182 DwarfLoadingStateHandler::_GetResolvableName(const BString& debugFileName, 183 BString& _resolvableName, BPackageVersion& _resolvableVersion) 184 { 185 BString fileName; 186 BString packageName; 187 BString packageVersion; 188 189 int32 startIndex = 0; 190 int32 endIndex = debugFileName.FindFirst('('); 191 if (endIndex < 0) 192 return B_BAD_VALUE; 193 194 debugFileName.CopyInto(fileName, 0, endIndex); 195 startIndex = endIndex + 1; 196 endIndex = debugFileName.FindFirst('-', startIndex); 197 if (endIndex < 0) 198 return B_BAD_VALUE; 199 200 debugFileName.CopyInto(packageName, startIndex, endIndex - startIndex); 201 startIndex = endIndex + 1; 202 endIndex = debugFileName.FindFirst(')', startIndex); 203 if (endIndex < 0) 204 return B_BAD_VALUE; 205 206 debugFileName.CopyInto(packageVersion, startIndex, 207 endIndex - startIndex); 208 209 _resolvableName.SetToFormat("debuginfo:%s(%s)", fileName.String(), packageName.String()); 210 211 return _resolvableVersion.SetTo(packageVersion); 212 } 213