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 = requiredPackage.IsEmpty() 104 ? USER_CHOICE_SKIP : USER_CHOICE_INSTALL_PACKAGE; 105 } 106 107 if (choice == USER_CHOICE_INSTALL_PACKAGE) { 108 // TODO: integrate the package installation functionality directly. 109 BString command; 110 command.SetToFormat("/bin/pkgman install -y %s", 111 requiredPackage.String()); 112 BString notification; 113 notification.SetToFormat("Installing package %s" B_UTF8_ELLIPSIS, 114 requiredPackage.String()); 115 interface->NotifyBackgroundWorkStatus(notification); 116 int error = system(command.String()); 117 if (interface->IsInteractive()) { 118 if (WIFEXITED(error)) { 119 error = WEXITSTATUS(error); 120 if (error == B_OK) 121 break; 122 message.SetToFormat("Package installation failed: %s.", 123 strerror(error)); 124 interface->NotifyUser("Error", message.String(), 125 USER_NOTIFICATION_ERROR); 126 continue; 127 } 128 } 129 break; 130 } else if (choice == USER_CHOICE_LOCATE_FILE) { 131 entry_ref ref; 132 interface->SynchronouslyAskUserForFile(&ref); 133 BPath path(&ref); 134 if (path.InitCheck() == B_OK) 135 fileState.locatedExternalInfoPath = path.Path(); 136 break; 137 } else 138 break; 139 } 140 141 fileState.state = DWARF_FILE_LOADING_STATE_USER_INPUT_PROVIDED; 142 } 143 144 145 status_t 146 DwarfLoadingStateHandler::_GetMatchingDebugInfoPackage( 147 const BString& debugFileName, BString& _packageName) 148 { 149 BString resolvableName; 150 BPackageVersion requiredVersion; 151 BPackageManager::ClientInstallationInterface clientInterface; 152 BPackageManager::UserInteractionHandler handler; 153 154 BPackageManager packageManager(B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, 155 &clientInterface, &handler); 156 packageManager.Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES 157 | BPackageManager::B_ADD_REMOTE_REPOSITORIES); 158 BObjectList<BSolverPackage> packages; 159 status_t error = _GetResolvableName(debugFileName, resolvableName, 160 requiredVersion); 161 if (error != B_OK) 162 return error; 163 164 error = packageManager.Solver()->FindPackages(resolvableName, 165 BSolver::B_FIND_IN_PROVIDES, packages); 166 if (error != B_OK) 167 return error; 168 else if (packages.CountItems() == 0) 169 return B_ENTRY_NOT_FOUND; 170 171 for (int32 i = 0; i < packages.CountItems(); i++) { 172 BSolverPackage* package = packages.ItemAt(i); 173 if (requiredVersion.Compare(package->Version()) == 0) { 174 _packageName = package->Name(); 175 return B_OK; 176 } 177 } 178 179 return B_ENTRY_NOT_FOUND; 180 } 181 182 183 status_t 184 DwarfLoadingStateHandler::_GetResolvableName(const BString& debugFileName, 185 BString& _resolvableName, BPackageVersion& _resolvableVersion) 186 { 187 BString fileName; 188 BString packageName; 189 BString packageVersion; 190 191 int32 startIndex = 0; 192 int32 endIndex = debugFileName.FindFirst('('); 193 if (endIndex < 0) 194 return B_BAD_VALUE; 195 196 debugFileName.CopyInto(fileName, 0, endIndex); 197 startIndex = endIndex + 1; 198 endIndex = debugFileName.FindFirst('-', startIndex); 199 if (endIndex < 0) 200 return B_BAD_VALUE; 201 202 debugFileName.CopyInto(packageName, startIndex, endIndex - startIndex); 203 startIndex = endIndex + 1; 204 endIndex = debugFileName.FindFirst(')', startIndex); 205 if (endIndex < 0) 206 return B_BAD_VALUE; 207 208 debugFileName.CopyInto(packageVersion, startIndex, 209 endIndex - startIndex); 210 211 _resolvableName.SetToFormat("debuginfo:%s(%s)", fileName.String(), packageName.String()); 212 213 return _resolvableVersion.SetTo(packageVersion); 214 } 215