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