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