xref: /haiku/src/servers/package/Root.cpp (revision 579f1dbca962a2a03df54f69fdc6e9423f91f20e)
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 "Root.h"
11 
12 #include <Directory.h>
13 #include <Entry.h>
14 #include <package/PackageDefs.h>
15 #include <Path.h>
16 
17 #include <AutoDeleter.h>
18 #include <AutoLocker.h>
19 
20 #include <package/DaemonDefs.h>
21 
22 #include "DebugSupport.h"
23 
24 
25 using namespace BPackageKit::BPrivate;
26 
27 
28 // #pragma mark - VolumeJob
29 
30 
31 struct Root::VolumeJob : public Job {
32 	VolumeJob(Volume* volume, void (Root::*method)(Volume*))
33 		:
34 		fVolume(volume),
35 		fMethod(method)
36 	{
37 	}
38 
39 	virtual void Do()
40 	{
41 		(fVolume->GetRoot()->*fMethod)(fVolume);
42 	}
43 
44 private:
45 	Volume*	fVolume;
46 	void	(Root::*fMethod)(Volume*);
47 };
48 
49 
50 // #pragma mark - RequestJob
51 
52 
53 struct Root::RequestJob : public Job {
54 	RequestJob(Root* root, BMessage* message)
55 		:
56 		fRoot(root),
57 		fMessage(message)
58 	{
59 	}
60 
61 	virtual void Do()
62 	{
63 		fRoot->_HandleRequest(fMessage.Get());
64 	}
65 
66 private:
67 	Root*					fRoot;
68 	ObjectDeleter<BMessage>	fMessage;
69 };
70 
71 
72 // #pragma mark - Root
73 
74 
75 Root::Root()
76 	:
77 	fLock("packagefs root"),
78 	fNodeRef(),
79 	fPath(),
80 	fSystemVolume(NULL),
81 	fCommonVolume(NULL),
82 	fHomeVolume(NULL),
83 	fJobQueue(),
84 	fJobRunner(-1)
85 {
86 }
87 
88 
89 Root::~Root()
90 {
91 	fJobQueue.Close();
92 
93 	if (fJobRunner >= 0)
94 		wait_for_thread(fJobRunner, NULL);
95 }
96 
97 
98 status_t
99 Root::Init(const node_ref& nodeRef)
100 {
101 	fNodeRef = nodeRef;
102 
103 	// init members and spawn job runner thread
104 	status_t error = fJobQueue.Init();
105 	if (error != B_OK)
106 		RETURN_ERROR(error);
107 
108 	error = fLock.InitCheck();
109 	if (error != B_OK)
110 		RETURN_ERROR(error);
111 
112 	fJobRunner = spawn_thread(&_JobRunnerEntry, "job runner", B_NORMAL_PRIORITY,
113 		this);
114 	if (fJobRunner < 0)
115 		RETURN_ERROR(fJobRunner);
116 
117 	// get the path
118 	BDirectory directory;
119 	error = directory.SetTo(&fNodeRef);
120 	if (error != B_OK) {
121 		ERROR("Root::Init(): failed to open directory: %s\n", strerror(error));
122 		RETURN_ERROR(error);
123 	}
124 
125 	BEntry entry;
126 	error = directory.GetEntry(&entry);
127 
128 	BPath path;
129 	if (error == B_OK)
130 		error = entry.GetPath(&path);
131 
132 	if (error != B_OK) {
133 		ERROR("Root::Init(): failed to get directory path: %s\n",
134 			strerror(error));
135 		RETURN_ERROR(error);
136 	}
137 
138 	fPath = path.Path();
139 	if (fPath.IsEmpty())
140 		RETURN_ERROR(B_NO_MEMORY);
141 
142 	resume_thread(fJobRunner);
143 
144 	return B_OK;
145 }
146 
147 
148 status_t
149 Root::RegisterVolume(Volume* volume)
150 {
151 	AutoLocker<BLocker> locker(fLock);
152 
153 	Volume** volumeToSet = _GetVolume(volume->MountType());
154 	if (volumeToSet == NULL)
155 		return B_BAD_VALUE;
156 
157 	if (*volumeToSet != NULL) {
158 		ERROR("Root::RegisterVolume(): can't register volume at \"%s\", since "
159 			"there's already volume at \"%s\" with the same type.\n",
160 			volume->Path().String(), (*volumeToSet)->Path().String());
161 		return B_BAD_VALUE;
162 	}
163 
164 	*volumeToSet = volume;
165 	volume->SetRoot(this);
166 
167 	// queue a job for reading the volume's packages
168 	status_t error = _QueueJob(
169 		new(std::nothrow) VolumeJob(volume, &Root::_InitPackages));
170 	if (error != B_OK) {
171 		volume->SetRoot(NULL);
172 		*volumeToSet = NULL;
173 		return error;
174 	}
175 
176 	return B_OK;
177 }
178 
179 
180 void
181 Root::UnregisterVolume(Volume* volume)
182 {
183 	AutoLocker<BLocker> locker(fLock);
184 
185 	Volume** volumeToSet = _GetVolume(volume->MountType());
186 	if (volumeToSet == NULL || *volumeToSet != volume) {
187 		ERROR("Root::UnregisterVolume(): can't unregister unknown volume at "
188 			"\"%s.\n", volume->Path().String());
189 		return;
190 	}
191 
192 	*volumeToSet = NULL;
193 
194 	// Use the job queue to delete the volume to make sure there aren't any
195 	// pending jobs that reference the volume.
196 	_QueueJob(new(std::nothrow) VolumeJob(volume, &Root::_DeleteVolume));
197 }
198 
199 
200 Volume*
201 Root::FindVolume(dev_t deviceID) const
202 {
203 	AutoLocker<BLocker> locker(fLock);
204 
205 	Volume* volumes[] = { fSystemVolume, fCommonVolume, fHomeVolume };
206 	for (size_t i = 0; i < sizeof(volumes) / sizeof(volumes[0]); i++) {
207 		Volume* volume = volumes[i];
208 		if (volume != NULL && volume->DeviceID() == deviceID)
209 			return volume;
210 	}
211 
212 	return NULL;
213 }
214 
215 
216 void
217 Root::HandleRequest(BMessage* message)
218 {
219 	RequestJob* job = new(std::nothrow) RequestJob(this, message);
220 	if (job == NULL) {
221 		delete message;
222 		return;
223 	}
224 
225 	_QueueJob(job);
226 }
227 
228 
229 void
230 Root::VolumeNodeMonitorEventOccurred(Volume* volume)
231 {
232 	_QueueJob(
233 		new(std::nothrow) VolumeJob(volume, &Root::_ProcessNodeMonitorEvents));
234 }
235 
236 
237 void
238 Root::LastReferenceReleased()
239 {
240 }
241 
242 
243 Volume**
244 Root::_GetVolume(PackageFSMountType mountType)
245 {
246 	switch (mountType) {
247 		case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
248 			return &fSystemVolume;
249 		case PACKAGE_FS_MOUNT_TYPE_COMMON:
250 			return &fCommonVolume;
251 		case PACKAGE_FS_MOUNT_TYPE_HOME:
252 			return &fHomeVolume;
253 		case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
254 		default:
255 			return NULL;
256 	}
257 }
258 
259 
260 Volume*
261 Root::_GetVolume(BPackageInstallationLocation location)
262 {
263 	switch ((BPackageInstallationLocation)location) {
264 		case B_PACKAGE_INSTALLATION_LOCATION_SYSTEM:
265 			return fSystemVolume;
266 		case B_PACKAGE_INSTALLATION_LOCATION_COMMON:
267 			return fCommonVolume;
268 		case B_PACKAGE_INSTALLATION_LOCATION_HOME:
269 			return fHomeVolume;
270 		default:
271 			return NULL;
272 	}
273 }
274 
275 
276 Volume*
277 Root::_NextVolumeFor(Volume* volume)
278 {
279 	if (volume == NULL)
280 		return NULL;
281 
282 	PackageFSMountType mountType = volume->MountType();
283 
284 	do {
285 		switch (mountType) {
286 			case PACKAGE_FS_MOUNT_TYPE_HOME:
287 				mountType = PACKAGE_FS_MOUNT_TYPE_COMMON;
288 				break;
289 			case PACKAGE_FS_MOUNT_TYPE_COMMON:
290 				mountType = PACKAGE_FS_MOUNT_TYPE_SYSTEM;
291 				break;
292 			case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
293 			case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
294 			default:
295 				return NULL;
296 		}
297 
298 		volume = *_GetVolume(mountType);
299 	} while (volume == NULL);
300 
301 	return volume;
302 }
303 
304 
305 void
306 Root::_InitPackages(Volume* volume)
307 {
308 	if (volume->InitPackages(this) == B_OK) {
309 		AutoLocker<BLocker> locker(fLock);
310 		Volume* nextVolume = _NextVolumeFor(volume);
311 		Volume* nextNextVolume = _NextVolumeFor(nextVolume);
312 		locker.Unlock();
313 
314 		volume->InitialVerify(nextVolume, nextNextVolume);
315 	}
316 }
317 
318 
319 void
320 Root::_DeleteVolume(Volume* volume)
321 {
322 	delete volume;
323 }
324 
325 
326 void
327 Root::_ProcessNodeMonitorEvents(Volume* volume)
328 {
329 	volume->ProcessPendingNodeMonitorEvents();
330 
331 	if (volume->HasPendingPackageActivationChanges())
332 		volume->ProcessPendingPackageActivationChanges();
333 }
334 
335 
336 void
337 Root::_HandleRequest(BMessage* message)
338 {
339 	int32 location;
340 	if (message->FindInt32("location", &location) != B_OK
341 		|| location < 0
342 		|| location >= B_PACKAGE_INSTALLATION_LOCATION_ENUM_COUNT) {
343 		return;
344 	}
345 
346 	// get the volume and let it handle the message
347 	AutoLocker<BLocker> locker(fLock);
348 	Volume* volume = _GetVolume((BPackageInstallationLocation)location);
349 	locker.Unlock();
350 
351 	if (volume != NULL) {
352 		switch (message->what) {
353 			case B_MESSAGE_GET_INSTALLATION_LOCATION_INFO:
354 				volume->HandleGetLocationInfoRequest(message);
355 				break;
356 			case B_MESSAGE_COMMIT_TRANSACTION:
357 				volume->HandleCommitTransactionRequest(message);
358 				break;
359 		}
360 	}
361 }
362 
363 
364 status_t
365 Root::_QueueJob(Job* job)
366 {
367 	if (job == NULL)
368 		return B_NO_MEMORY;
369 
370 	BReference<Job> jobReference(job, true);
371 	if (!fJobQueue.QueueJob(job)) {
372 		// job queue already closed
373 		return B_BAD_VALUE;
374 	}
375 
376 	return B_OK;
377 }
378 
379 
380 /*static*/ status_t
381 Root::_JobRunnerEntry(void* data)
382 {
383 	return ((Root*)data)->_JobRunner();
384 }
385 
386 
387 status_t
388 Root::_JobRunner()
389 {
390 	while (Job* job = fJobQueue.DequeueJob()) {
391 		job->Do();
392 		job->ReleaseReference();
393 	}
394 
395 	return B_OK;
396 }
397