1 /* 2 * Copyright 2013, 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 activePackages; 68 BPackageInfoSet inactivePackages; 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, "active packages", 78 activePackages)) != B_OK 79 || (error = _ExtractPackageInfoSet(reply, "inactive packages", 80 inactivePackages)) != B_OK 81 || (error = reply.FindInt64("change count", &changeCount)) != B_OK) { 82 return error; 83 } 84 85 _info.Unset(); 86 _info.SetLocation(location); 87 _info.SetBaseDirectoryRef(node_ref(baseDirectoryDevice, baseDirectoryNode)); 88 _info.SetPackagesDirectoryRef( 89 node_ref(packagesDirectoryDevice, packagesDirectoryNode)); 90 _info.SetActivePackageInfos(activePackages); 91 _info.SetInactivePackageInfos(inactivePackages); 92 _info.SetChangeCount(changeCount); 93 94 return B_OK; 95 } 96 97 98 status_t 99 BDaemonClient::CommitTransaction(const BActivationTransaction& transaction, 100 BCommitTransactionResult& _result) 101 { 102 status_t error = _CommitTransaction(transaction, _result); 103 // B_OK just indicates that _result has been initialized. 104 if (error != B_OK) 105 _result.SetTo(error, BString(), BString(), BString()); 106 107 return _result.Error(); 108 } 109 110 111 status_t 112 BDaemonClient::CreateTransaction(BPackageInstallationLocation location, 113 BActivationTransaction& _transaction, BDirectory& _transactionDirectory) 114 { 115 // get an info for the location 116 BInstallationLocationInfo info; 117 status_t error = GetInstallationLocationInfo(location, info); 118 if (error != B_OK) 119 return error; 120 121 // open admin directory 122 entry_ref entryRef; 123 entryRef.device = info.PackagesDirectoryRef().device; 124 entryRef.directory = info.PackagesDirectoryRef().node; 125 error = entryRef.set_name(PACKAGES_DIRECTORY_ADMIN_DIRECTORY); 126 if (error != B_OK) 127 return error; 128 129 BDirectory adminDirectory; 130 error = adminDirectory.SetTo(&entryRef); 131 if (error != B_OK) 132 return error; 133 134 // create a transaction directory 135 int uniqueId = 1; 136 BString directoryName; 137 for (;; uniqueId++) { 138 directoryName.SetToFormat("transaction-%d", uniqueId); 139 if (directoryName.IsEmpty()) 140 return B_NO_MEMORY; 141 142 error = adminDirectory.CreateDirectory(directoryName, 143 &_transactionDirectory); 144 if (error == B_OK) 145 break; 146 if (error != B_FILE_EXISTS) 147 return error; 148 } 149 150 // init the transaction 151 error = _transaction.SetTo(location, info.ChangeCount(), directoryName); 152 if (error != B_OK) { 153 BEntry entry; 154 _transactionDirectory.GetEntry(&entry); 155 _transactionDirectory.Unset(); 156 if (entry.InitCheck() == B_OK) 157 entry.Remove(); 158 return error; 159 } 160 161 return B_OK; 162 } 163 164 165 status_t 166 BDaemonClient::_InitMessenger() 167 { 168 if (fDaemonMessenger.IsValid()) 169 return B_OK; 170 171 // get the package daemon's address 172 status_t error; 173 fDaemonMessenger = BMessenger(B_PACKAGE_DAEMON_APP_SIGNATURE, -1, &error); 174 return error; 175 } 176 177 178 status_t 179 BDaemonClient::_ExtractPackageInfoSet(const BMessage& message, 180 const char* field, BPackageInfoSet& _infos) 181 { 182 // get the number of items 183 type_code type; 184 int32 count; 185 if (message.GetInfo(field, &type, &count) != B_OK) { 186 // the field is missing 187 return B_OK; 188 } 189 if (type != B_MESSAGE_TYPE) 190 return B_BAD_DATA; 191 192 for (int32 i = 0; i < count; i++) { 193 BMessage archive; 194 status_t error = message.FindMessage(field, i, &archive); 195 if (error != B_OK) 196 return error; 197 198 BPackageInfo info(&archive, &error); 199 if (error != B_OK) 200 return error; 201 202 error = _infos.AddInfo(info); 203 if (error != B_OK) 204 return error; 205 } 206 207 return B_OK; 208 } 209 210 211 status_t 212 BDaemonClient::_CommitTransaction(const BActivationTransaction& transaction, 213 BCommitTransactionResult& _result) 214 { 215 if (transaction.InitCheck() != B_OK) 216 return B_BAD_VALUE; 217 218 status_t error = _InitMessenger(); 219 if (error != B_OK) 220 return error; 221 222 // send the request 223 BMessage request(B_MESSAGE_COMMIT_TRANSACTION); 224 if ((error = request.AddInt32("location", transaction.Location())) != B_OK 225 || (error = request.AddInt64("change count", 226 transaction.ChangeCount())) != B_OK 227 || (error = request.AddString("transaction", 228 transaction.TransactionDirectoryName())) != B_OK 229 || (error = request.AddStrings("activate", 230 transaction.PackagesToActivate())) != B_OK 231 || (error = request.AddStrings("deactivate", 232 transaction.PackagesToDeactivate())) != B_OK) { 233 return error; 234 } 235 236 BMessage reply; 237 fDaemonMessenger.SendMessage(&request, &reply); 238 if (reply.what != B_MESSAGE_COMMIT_TRANSACTION_REPLY) 239 return B_ERROR; 240 241 // extract the result 242 int32 requestError; 243 error = reply.FindInt32("error", &requestError); 244 if (error != B_OK) 245 return error; 246 247 BString errorMessage; 248 BString errorPackage; 249 BString oldStateDirectory; 250 if (requestError == 0) { 251 error = reply.FindString("old state", &oldStateDirectory); 252 if (error != B_OK) 253 return error; 254 } else { 255 reply.FindString("error message", &errorMessage); 256 reply.FindString("error package", &errorPackage); 257 } 258 259 _result.SetTo(requestError, errorMessage, errorPackage, oldStateDirectory); 260 return B_OK; 261 // Even on error. B_OK just indicates that we have initialized _result. 262 } 263 264 265 // #pragma mark - BCommitTransactionResult 266 267 268 BDaemonClient::BCommitTransactionResult::BCommitTransactionResult() 269 : 270 fError(B_NO_INIT), 271 fErrorMessage(), 272 fErrorPackage(), 273 fOldStateDirectory() 274 { 275 } 276 277 278 BDaemonClient::BCommitTransactionResult::BCommitTransactionResult(int32 error, 279 const BString& errorMessage, const BString& errorPackage, 280 const BString& oldStateDirectory) 281 : 282 fError(error), 283 fErrorMessage(errorMessage), 284 fErrorPackage(errorPackage), 285 fOldStateDirectory(oldStateDirectory) 286 { 287 } 288 289 290 BDaemonClient::BCommitTransactionResult::~BCommitTransactionResult() 291 { 292 } 293 294 295 void 296 BDaemonClient::BCommitTransactionResult::SetTo(int32 error, 297 const BString& errorMessage, const BString& errorPackage, 298 const BString& oldStateDirectory) 299 { 300 fError = error; 301 fErrorMessage = errorMessage; 302 fErrorPackage = errorPackage; 303 fOldStateDirectory = oldStateDirectory; 304 } 305 306 307 status_t 308 BDaemonClient::BCommitTransactionResult::Error() const 309 { 310 return fError <= 0 ? fError : B_ERROR; 311 } 312 313 314 BDaemonError 315 BDaemonClient::BCommitTransactionResult::DaemonError() const 316 { 317 return fError > 0 ? (BDaemonError)fError : B_DAEMON_OK; 318 } 319 320 321 const BString& 322 BDaemonClient::BCommitTransactionResult::ErrorMessage() const 323 { 324 return fErrorMessage; 325 } 326 327 328 const BString& 329 BDaemonClient::BCommitTransactionResult::ErrorPackage() const 330 { 331 return fErrorPackage; 332 } 333 334 335 BString 336 BDaemonClient::BCommitTransactionResult::FullErrorMessage() const 337 { 338 if (fError == 0) 339 return "no error"; 340 341 const char* errorString; 342 if (fError > 0) { 343 switch ((BDaemonError)fError) { 344 case B_DAEMON_CHANGE_COUNT_MISMATCH: 345 errorString = "transaction out of date"; 346 break; 347 case B_DAEMON_BAD_REQUEST: 348 errorString = "invalid transaction"; 349 break; 350 case B_DAEMON_NO_SUCH_PACKAGE: 351 errorString = "no such package"; 352 break; 353 case B_DAEMON_PACKAGE_ALREADY_EXISTS: 354 errorString = "package already exists"; 355 break; 356 case B_DAEMON_OK: 357 default: 358 errorString = "unknown error"; 359 break; 360 } 361 } else 362 errorString = strerror(fError); 363 364 BString result; 365 if (!fErrorMessage.IsEmpty()) { 366 result = fErrorMessage; 367 result << ": "; 368 } 369 370 result << errorString; 371 372 if (!fErrorPackage.IsEmpty()) 373 result << ", package: \"" << fErrorPackage << '"'; 374 375 return result; 376 } 377 378 379 const BString& 380 BDaemonClient::BCommitTransactionResult::OldStateDirectory() const 381 { 382 return fOldStateDirectory; 383 } 384 385 386 } // namespace BPrivate 387 } // namespace BPackageKit 388