1 /* 2 * Copyright 2013-2014, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ingo Weinhold <ingo_weinhold@gmx.de> 7 */ 8 9 10 #include <package/DaemonClient.h> 11 12 #include <time.h> 13 14 #include <Directory.h> 15 #include <Entry.h> 16 #include <package/InstallationLocationInfo.h> 17 #include <package/PackageInfo.h> 18 19 #include <package/ActivationTransaction.h> 20 #include <package/PackagesDirectoryDefs.h> 21 22 23 namespace BPackageKit { 24 namespace BPrivate { 25 26 27 // #pragma mark - BCommitTransactionResult 28 29 30 BDaemonClient::BDaemonClient() 31 : 32 fDaemonMessenger() 33 { 34 } 35 36 37 BDaemonClient::~BDaemonClient() 38 { 39 } 40 41 42 status_t 43 BDaemonClient::GetInstallationLocationInfo( 44 BPackageInstallationLocation location, BInstallationLocationInfo& _info) 45 { 46 status_t error = _InitMessenger(); 47 if (error != B_OK) 48 return error; 49 50 // send the request 51 BMessage request(B_MESSAGE_GET_INSTALLATION_LOCATION_INFO); 52 error = request.AddInt32("location", location); 53 if (error != B_OK) 54 return error; 55 56 BMessage reply; 57 fDaemonMessenger.SendMessage(&request, &reply); 58 if (reply.what != B_MESSAGE_GET_INSTALLATION_LOCATION_INFO_REPLY) 59 return B_ERROR; 60 61 // extract the location info 62 int32 baseDirectoryDevice; 63 int64 baseDirectoryNode; 64 int32 packagesDirectoryDevice; 65 int64 packagesDirectoryNode; 66 int64 changeCount; 67 BPackageInfoSet latestActivePackages; 68 BPackageInfoSet latestInactivePackages; 69 if ((error = reply.FindInt32("base directory device", &baseDirectoryDevice)) 70 != B_OK 71 || (error = reply.FindInt64("base directory node", &baseDirectoryNode)) 72 != B_OK 73 || (error = reply.FindInt32("packages directory device", 74 &packagesDirectoryDevice)) != B_OK 75 || (error = reply.FindInt64("packages directory node", 76 &packagesDirectoryNode)) != B_OK 77 || (error = _ExtractPackageInfoSet(reply, "latest active packages", 78 latestActivePackages)) != B_OK 79 || (error = _ExtractPackageInfoSet(reply, "latest inactive packages", 80 latestInactivePackages)) != B_OK 81 || (error = reply.FindInt64("change count", &changeCount)) != B_OK) { 82 return error; 83 } 84 85 BPackageInfoSet currentlyActivePackages; 86 error = _ExtractPackageInfoSet(reply, "currently active packages", 87 currentlyActivePackages); 88 if (error != B_OK && error != B_NAME_NOT_FOUND) 89 return error; 90 91 BString oldStateName; 92 error = reply.FindString("old state", &oldStateName); 93 if (error != B_OK && error != B_NAME_NOT_FOUND) 94 return error; 95 96 _info.Unset(); 97 _info.SetLocation(location); 98 _info.SetBaseDirectoryRef(node_ref(baseDirectoryDevice, baseDirectoryNode)); 99 _info.SetPackagesDirectoryRef( 100 node_ref(packagesDirectoryDevice, packagesDirectoryNode)); 101 _info.SetLatestActivePackageInfos(latestActivePackages); 102 _info.SetLatestInactivePackageInfos(latestInactivePackages); 103 _info.SetCurrentlyActivePackageInfos(currentlyActivePackages); 104 _info.SetOldStateName(oldStateName); 105 _info.SetChangeCount(changeCount); 106 107 return B_OK; 108 } 109 110 111 status_t 112 BDaemonClient::CommitTransaction(const BActivationTransaction& transaction, 113 BCommitTransactionResult& _result) 114 { 115 if (transaction.InitCheck() != B_OK) 116 return B_BAD_VALUE; 117 118 status_t error = _InitMessenger(); 119 if (error != B_OK) 120 return error; 121 122 // send the request 123 BMessage request(B_MESSAGE_COMMIT_TRANSACTION); 124 error = transaction.Archive(&request); 125 if (error != B_OK) 126 return error; 127 128 BMessage reply; 129 fDaemonMessenger.SendMessage(&request, &reply); 130 if (reply.what != B_MESSAGE_COMMIT_TRANSACTION_REPLY) 131 return B_ERROR; 132 133 // extract the result 134 int32 requestError; 135 error = reply.FindInt32("error", &requestError); 136 if (error != B_OK) 137 return error; 138 139 BString errorMessage; 140 BString errorPackage; 141 BString oldStateDirectory; 142 if (requestError == 0) { 143 error = reply.FindString("old state", &oldStateDirectory); 144 if (error != B_OK) 145 return error; 146 } else { 147 reply.FindString("error message", &errorMessage); 148 reply.FindString("error package", &errorPackage); 149 } 150 151 _result.SetTo(requestError, errorMessage, errorPackage, oldStateDirectory); 152 return B_OK; 153 // Even on error. B_OK just indicates that we have initialized _result. 154 } 155 156 157 status_t 158 BDaemonClient::CreateTransaction(BPackageInstallationLocation location, 159 BActivationTransaction& _transaction, BDirectory& _transactionDirectory) 160 { 161 // get an info for the location 162 BInstallationLocationInfo info; 163 status_t error = GetInstallationLocationInfo(location, info); 164 if (error != B_OK) 165 return error; 166 167 // open admin directory 168 entry_ref entryRef; 169 entryRef.device = info.PackagesDirectoryRef().device; 170 entryRef.directory = info.PackagesDirectoryRef().node; 171 error = entryRef.set_name(PACKAGES_DIRECTORY_ADMIN_DIRECTORY); 172 if (error != B_OK) 173 return error; 174 175 BDirectory adminDirectory; 176 error = adminDirectory.SetTo(&entryRef); 177 if (error != B_OK) 178 return error; 179 180 // create a transaction directory 181 int uniqueId = 1; 182 BString directoryName; 183 for (;; uniqueId++) { 184 directoryName.SetToFormat("transaction-%d", uniqueId); 185 if (directoryName.IsEmpty()) 186 return B_NO_MEMORY; 187 188 error = adminDirectory.CreateDirectory(directoryName, 189 &_transactionDirectory); 190 if (error == B_OK) 191 break; 192 if (error != B_FILE_EXISTS) 193 return error; 194 } 195 196 // init the transaction 197 error = _transaction.SetTo(location, info.ChangeCount(), directoryName); 198 if (error != B_OK) { 199 BEntry entry; 200 _transactionDirectory.GetEntry(&entry); 201 _transactionDirectory.Unset(); 202 if (entry.InitCheck() == B_OK) 203 entry.Remove(); 204 return error; 205 } 206 207 return B_OK; 208 } 209 210 211 status_t 212 BDaemonClient::_InitMessenger() 213 { 214 if (fDaemonMessenger.IsValid()) 215 return B_OK; 216 217 // get the package daemon's address 218 status_t error; 219 fDaemonMessenger = BMessenger(B_PACKAGE_DAEMON_APP_SIGNATURE, -1, &error); 220 return error; 221 } 222 223 224 status_t 225 BDaemonClient::_ExtractPackageInfoSet(const BMessage& message, 226 const char* field, BPackageInfoSet& _infos) 227 { 228 // get the number of items 229 type_code type; 230 int32 count; 231 if (message.GetInfo(field, &type, &count) != B_OK) { 232 // the field is missing 233 return B_OK; 234 } 235 if (type != B_MESSAGE_TYPE) 236 return B_BAD_DATA; 237 238 for (int32 i = 0; i < count; i++) { 239 BMessage archive; 240 status_t error = message.FindMessage(field, i, &archive); 241 if (error != B_OK) 242 return error; 243 244 BPackageInfo info(&archive, &error); 245 if (error != B_OK) 246 return error; 247 248 error = _infos.AddInfo(info); 249 if (error != B_OK) 250 return error; 251 } 252 253 return B_OK; 254 } 255 256 257 // #pragma mark - BCommitTransactionResult 258 259 260 BDaemonClient::BCommitTransactionResult::BCommitTransactionResult() 261 : 262 fError(B_NO_INIT), 263 fErrorMessage(), 264 fErrorPackage(), 265 fOldStateDirectory() 266 { 267 } 268 269 270 BDaemonClient::BCommitTransactionResult::BCommitTransactionResult(int32 error, 271 const BString& errorMessage, const BString& errorPackage, 272 const BString& oldStateDirectory) 273 : 274 fError(error), 275 fErrorMessage(errorMessage), 276 fErrorPackage(errorPackage), 277 fOldStateDirectory(oldStateDirectory) 278 { 279 } 280 281 282 BDaemonClient::BCommitTransactionResult::~BCommitTransactionResult() 283 { 284 } 285 286 287 void 288 BDaemonClient::BCommitTransactionResult::SetTo(int32 error, 289 const BString& errorMessage, const BString& errorPackage, 290 const BString& oldStateDirectory) 291 { 292 fError = error; 293 fErrorMessage = errorMessage; 294 fErrorPackage = errorPackage; 295 fOldStateDirectory = oldStateDirectory; 296 } 297 298 299 status_t 300 BDaemonClient::BCommitTransactionResult::Error() const 301 { 302 return fError <= 0 ? fError : B_ERROR; 303 } 304 305 306 BDaemonError 307 BDaemonClient::BCommitTransactionResult::DaemonError() const 308 { 309 return fError > 0 ? (BDaemonError)fError : B_DAEMON_OK; 310 } 311 312 313 const BString& 314 BDaemonClient::BCommitTransactionResult::ErrorMessage() const 315 { 316 return fErrorMessage; 317 } 318 319 320 const BString& 321 BDaemonClient::BCommitTransactionResult::ErrorPackage() const 322 { 323 return fErrorPackage; 324 } 325 326 327 BString 328 BDaemonClient::BCommitTransactionResult::FullErrorMessage() const 329 { 330 if (fError == 0) 331 return "no error"; 332 333 const char* errorString; 334 if (fError > 0) { 335 switch ((BDaemonError)fError) { 336 case B_DAEMON_INSTALLATION_LOCATION_BUSY: 337 errorString = "another package operation already in progress"; 338 break; 339 case B_DAEMON_CHANGE_COUNT_MISMATCH: 340 errorString = "transaction out of date"; 341 break; 342 case B_DAEMON_BAD_REQUEST: 343 errorString = "invalid transaction"; 344 break; 345 case B_DAEMON_NO_SUCH_PACKAGE: 346 errorString = "no such package"; 347 break; 348 case B_DAEMON_PACKAGE_ALREADY_EXISTS: 349 errorString = "package already exists"; 350 break; 351 case B_DAEMON_OK: 352 default: 353 errorString = "unknown error"; 354 break; 355 } 356 } else 357 errorString = strerror(fError); 358 359 BString result; 360 if (!fErrorMessage.IsEmpty()) { 361 result = fErrorMessage; 362 result << ": "; 363 } 364 365 result << errorString; 366 367 if (!fErrorPackage.IsEmpty()) 368 result << ", package: \"" << fErrorPackage << '"'; 369 370 return result; 371 } 372 373 374 const BString& 375 BDaemonClient::BCommitTransactionResult::OldStateDirectory() const 376 { 377 return fOldStateDirectory; 378 } 379 380 381 } // namespace BPrivate 382 } // namespace BPackageKit 383