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