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