xref: /haiku/src/kits/package/DaemonClient.cpp (revision 225b6382637a7346d5378ee45a6581b4e2616055)
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