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