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/CommitTransactionResult.h>
17#include <package/InstallationLocationInfo.h>
18#include <package/PackageInfo.h>
19
20#include <package/ActivationTransaction.h>
21#include <package/PackagesDirectoryDefs.h>
22
23
24namespace BPackageKit {
25namespace BPrivate {
26
27
28BDaemonClient::BDaemonClient()
29	:
30	fDaemonMessenger()
31{
32}
33
34
35BDaemonClient::~BDaemonClient()
36{
37}
38
39
40status_t
41BDaemonClient::GetInstallationLocationInfo(
42	BPackageInstallationLocation location, BInstallationLocationInfo& _info)
43{
44	status_t error = _InitMessenger();
45	if (error != B_OK)
46		return error;
47
48	BMessage request(B_MESSAGE_GET_INSTALLATION_LOCATION_INFO);
49	error = request.AddInt32("location", location);
50	if (error != B_OK)
51		return error;
52
53	// Get our filesystem root node. If we are in a chroot this is not the same
54	// as the package_daemon root node, so we must provide it.
55	struct stat st;
56	if (stat("/boot", &st) == 0) {
57		error = request.AddInt32("volume", st.st_dev);
58		if (error != B_OK)
59			return error;
60		error = request.AddInt64("root", st.st_ino);
61		if (error != B_OK)
62			return error;
63	}
64
65	// send the request
66	BMessage reply;
67	fDaemonMessenger.SendMessage(&request, &reply);
68	if (reply.what != B_MESSAGE_GET_INSTALLATION_LOCATION_INFO_REPLY)
69		return B_ERROR;
70
71	// extract the location info
72	int32 baseDirectoryDevice;
73	int64 baseDirectoryNode;
74	int32 packagesDirectoryDevice;
75	int64 packagesDirectoryNode;
76	int64 changeCount;
77	BPackageInfoSet latestActivePackages;
78	BPackageInfoSet latestInactivePackages;
79	if ((error = reply.FindInt32("base directory device", &baseDirectoryDevice))
80			!= B_OK
81		|| (error = reply.FindInt64("base directory node", &baseDirectoryNode))
82			!= B_OK
83		|| (error = reply.FindInt32("packages directory device",
84			&packagesDirectoryDevice)) != B_OK
85		|| (error = reply.FindInt64("packages directory node",
86			&packagesDirectoryNode)) != B_OK
87		|| (error = _ExtractPackageInfoSet(reply, "latest active packages",
88			latestActivePackages)) != B_OK
89		|| (error = _ExtractPackageInfoSet(reply, "latest inactive packages",
90			latestInactivePackages)) != B_OK
91		|| (error = reply.FindInt64("change count", &changeCount)) != B_OK) {
92		return error;
93	}
94
95	BPackageInfoSet currentlyActivePackages;
96	error = _ExtractPackageInfoSet(reply, "currently active packages",
97		currentlyActivePackages);
98	if (error != B_OK && error != B_NAME_NOT_FOUND)
99		return error;
100
101	BString oldStateName;
102	error = reply.FindString("old state", &oldStateName);
103	if (error != B_OK && error != B_NAME_NOT_FOUND)
104		return error;
105
106	_info.Unset();
107	_info.SetLocation(location);
108	_info.SetBaseDirectoryRef(node_ref(baseDirectoryDevice, baseDirectoryNode));
109	_info.SetPackagesDirectoryRef(
110		node_ref(packagesDirectoryDevice, packagesDirectoryNode));
111	_info.SetLatestActivePackageInfos(latestActivePackages);
112	_info.SetLatestInactivePackageInfos(latestInactivePackages);
113	_info.SetCurrentlyActivePackageInfos(currentlyActivePackages);
114	_info.SetOldStateName(oldStateName);
115	_info.SetChangeCount(changeCount);
116
117	return B_OK;
118}
119
120
121status_t
122BDaemonClient::CommitTransaction(const BActivationTransaction& transaction,
123	BCommitTransactionResult& _result)
124{
125	if (transaction.InitCheck() != B_OK)
126		return B_BAD_VALUE;
127
128	status_t error = _InitMessenger();
129	if (error != B_OK)
130		return error;
131
132	// send the request
133	BMessage request(B_MESSAGE_COMMIT_TRANSACTION);
134	error = transaction.Archive(&request);
135	if (error != B_OK)
136		return error;
137
138	BMessage reply;
139	fDaemonMessenger.SendMessage(&request, &reply);
140	if (reply.what != B_MESSAGE_COMMIT_TRANSACTION_REPLY)
141		return B_ERROR;
142
143	// extract the result
144	return _result.ExtractFromMessage(reply);
145}
146
147
148status_t
149BDaemonClient::CreateTransaction(BPackageInstallationLocation location,
150	BActivationTransaction& _transaction, BDirectory& _transactionDirectory)
151{
152	// get an info for the location
153	BInstallationLocationInfo info;
154	status_t error = GetInstallationLocationInfo(location, info);
155	if (error != B_OK)
156		return error;
157
158	// open admin directory
159	entry_ref entryRef;
160	entryRef.device = info.PackagesDirectoryRef().device;
161	entryRef.directory = info.PackagesDirectoryRef().node;
162	error = entryRef.set_name(PACKAGES_DIRECTORY_ADMIN_DIRECTORY);
163	if (error != B_OK)
164		return error;
165
166	BDirectory adminDirectory;
167	error = adminDirectory.SetTo(&entryRef);
168	if (error != B_OK)
169		return error;
170
171	// create a transaction directory
172	int uniqueId = 1;
173	BString directoryName;
174	for (;; uniqueId++) {
175		directoryName.SetToFormat("transaction-%d", uniqueId);
176		if (directoryName.IsEmpty())
177			return B_NO_MEMORY;
178
179		error = adminDirectory.CreateDirectory(directoryName,
180			&_transactionDirectory);
181		if (error == B_OK)
182			break;
183		if (error != B_FILE_EXISTS)
184			return error;
185	}
186
187	// init the transaction
188	error = _transaction.SetTo(location, info.ChangeCount(), directoryName);
189	if (error != B_OK) {
190		BEntry entry;
191		_transactionDirectory.GetEntry(&entry);
192		_transactionDirectory.Unset();
193		if (entry.InitCheck() == B_OK)
194			entry.Remove();
195		return error;
196	}
197
198	return B_OK;
199}
200
201
202status_t
203BDaemonClient::_InitMessenger()
204{
205	if (fDaemonMessenger.IsValid())
206		return B_OK;
207
208		// get the package daemon's address
209	status_t error;
210	fDaemonMessenger = BMessenger(B_PACKAGE_DAEMON_APP_SIGNATURE, -1, &error);
211	return error;
212}
213
214
215status_t
216BDaemonClient::_ExtractPackageInfoSet(const BMessage& message,
217	const char* field, BPackageInfoSet& _infos)
218{
219	// get the number of items
220	type_code type;
221	int32 count;
222	if (message.GetInfo(field, &type, &count) != B_OK) {
223		// the field is missing
224		return B_OK;
225	}
226	if (type != B_MESSAGE_TYPE)
227		return B_BAD_DATA;
228
229	for (int32 i = 0; i < count; i++) {
230		BMessage archive;
231		status_t error = message.FindMessage(field, i, &archive);
232		if (error != B_OK)
233			return error;
234
235		BPackageInfo info(&archive, &error);
236		if (error != B_OK)
237			return error;
238
239		error = _infos.AddInfo(info);
240		if (error != B_OK)
241			return error;
242	}
243
244	return B_OK;
245}
246
247
248}	// namespace BPrivate
249}	// namespace BPackageKit
250