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