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 "PackageDaemon.h"
11
12#include <errno.h>
13#include <string.h>
14
15#include <Directory.h>
16#include <NodeMonitor.h>
17
18#include <AutoDeleter.h>
19#include <package/DaemonDefs.h>
20
21#include "DebugSupport.h"
22#include "Root.h"
23#include "Volume.h"
24
25
26using namespace BPackageKit::BPrivate;
27
28
29PackageDaemon::PackageDaemon(status_t* _error)
30	:
31	BServer(B_PACKAGE_DAEMON_APP_SIGNATURE, false, _error),
32	fSystemRoot(NULL),
33	fRoots(10, true),
34	fVolumeWatcher()
35{
36}
37
38
39PackageDaemon::~PackageDaemon()
40{
41	for (int32 i = 0; Root* root = fRoots.ItemAt(i); i++)
42		root->ReleaseReference();
43	fRoots.MakeEmpty(false);
44}
45
46
47status_t
48PackageDaemon::Init()
49{
50	status_t error = fVolumeWatcher.StartWatching(BMessenger(this, this));
51	if (error != B_OK) {
52		ERROR("PackageDaemon::Init(): failed to start volume watching: %s\n",
53			strerror(error));
54	}
55
56	// register all packagefs volumes
57	for (int32 cookie = 0;;) {
58		dev_t device = next_dev(&cookie);
59		if (device < 0)
60			break;
61
62		_RegisterVolume(device);
63	}
64
65	return B_OK;
66}
67
68
69void
70PackageDaemon::MessageReceived(BMessage* message)
71{
72	switch (message->what) {
73		case B_NODE_MONITOR:
74		{
75			int32 opcode;
76			if (message->FindInt32("opcode", &opcode) != B_OK)
77				break;
78
79			if (opcode == B_DEVICE_MOUNTED)
80				_HandleVolumeMounted(message);
81			else if (opcode == B_DEVICE_UNMOUNTED)
82				_HandleVolumeUnmounted(message);
83			break;
84		}
85
86		case B_MESSAGE_GET_INSTALLATION_LOCATION_INFO:
87		case B_MESSAGE_COMMIT_TRANSACTION:
88		{
89			status_t error;
90			node_ref nodeRef;
91
92			// Get the node_ref of the filesystem root to see which one it is
93			error = message->FindInt32("volume", &nodeRef.device);
94			if (error == B_OK)
95				error = message->FindInt64("root", &nodeRef.node);
96
97			if (fSystemRoot != NULL && (error != B_OK
98					|| fSystemRoot->NodeRef() == nodeRef)) {
99				fSystemRoot->HandleRequest(DetachCurrentMessage());
100			} else if (error == B_OK) {
101				Root* root = _FindRoot(nodeRef);
102				if (root != NULL) {
103					root->HandleRequest(DetachCurrentMessage());
104				}
105			}
106			break;
107		}
108
109		default:
110			BServer::MessageReceived(message);
111			break;
112	}
113}
114
115
116status_t
117PackageDaemon::_RegisterVolume(dev_t deviceID)
118{
119	// get the FS info and check whether this is a package FS volume at all
120	fs_info info;
121	status_t error = fs_stat_dev(deviceID, &info);
122	if (error != 0)
123		RETURN_ERROR(error);
124
125	if (strcmp(info.fsh_name, "packagefs") != 0)
126		RETURN_ERROR(B_BAD_VALUE);
127
128	// create a volume
129	Volume* volume = new(std::nothrow) Volume(this);
130	if (volume == NULL)
131		RETURN_ERROR(B_NO_MEMORY);
132	ObjectDeleter<Volume> volumeDeleter(volume);
133
134	node_ref rootRef;
135	error = volume->Init(node_ref(info.dev, info.root), rootRef);
136	if (error != B_OK)
137		RETURN_ERROR(error);
138
139	if (volume->MountType() == PACKAGE_FS_MOUNT_TYPE_CUSTOM) {
140// TODO: Or maybe not?
141		INFORM("skipping custom mounted volume at \"%s\"\n",
142			volume->Path().String());
143		return B_OK;
144	}
145
146	// get the root for the volume and register it
147	Root* root;
148	error = _GetOrCreateRoot(rootRef, root);
149	if (error != B_OK)
150		RETURN_ERROR(error);
151
152	error = root->RegisterVolume(volume);
153	if (error != B_OK) {
154		_PutRoot(root);
155		RETURN_ERROR(error);
156	}
157	volumeDeleter.Detach();
158
159	INFORM("volume at \"%s\" registered\n", volume->Path().String());
160
161	return B_OK;
162}
163
164
165void
166PackageDaemon::_UnregisterVolume(Volume* volume)
167{
168	volume->Unmounted();
169
170	INFORM("volume at \"%s\" unregistered\n", volume->Path().String());
171
172	Root* root = volume->GetRoot();
173	root->UnregisterVolume(volume);
174
175	_PutRoot(root);
176}
177
178
179status_t
180PackageDaemon::_GetOrCreateRoot(const node_ref& nodeRef, Root*& _root)
181{
182	Root* root = _FindRoot(nodeRef);
183	if (root != NULL) {
184		root->AcquireReference();
185	} else {
186		root = new(std::nothrow) Root;
187		if (root == NULL)
188			RETURN_ERROR(B_NO_MEMORY);
189		ObjectDeleter<Root> rootDeleter(root);
190
191		bool isSystemRoot = false;
192		if (fSystemRoot == NULL) {
193			struct stat st;
194			isSystemRoot = stat("/boot", &st) == 0
195				&& node_ref(st.st_dev, st.st_ino) == nodeRef;
196		}
197
198		status_t error = root->Init(nodeRef, isSystemRoot);
199		if (error != B_OK)
200			RETURN_ERROR(error);
201
202		if (!fRoots.AddItem(root))
203			RETURN_ERROR(B_NO_MEMORY);
204
205		rootDeleter.Detach();
206
207		if (isSystemRoot)
208			fSystemRoot = root;
209
210		INFORM("root at \"%s\" (device: %" B_PRIdDEV ", node: %" B_PRIdINO ") "
211			"registered\n", root->Path().String(), nodeRef.device,
212			nodeRef.node);
213	}
214
215	_root = root;
216	return B_OK;
217}
218
219
220Root*
221PackageDaemon::_FindRoot(const node_ref& nodeRef) const
222{
223	for (int32 i = 0; Root* root = fRoots.ItemAt(i); i++) {
224		if (root->NodeRef() == nodeRef)
225			return root;
226	}
227
228	return NULL;
229}
230
231
232void
233PackageDaemon::_PutRoot(Root* root)
234{
235	if (root->ReleaseReference() == 1) {
236		INFORM("root at \"%s\" unregistered\n", root->Path().String());
237		fRoots.RemoveItem(root, true);
238			// deletes the object
239	}
240}
241
242
243Volume*
244PackageDaemon::_FindVolume(dev_t deviceID) const
245{
246	for (int32 i = 0; Root* root = fRoots.ItemAt(i); i++) {
247		if (Volume* volume = root->FindVolume(deviceID))
248			return volume;
249	}
250
251	return NULL;
252}
253
254
255void
256PackageDaemon::_HandleVolumeMounted(const BMessage* message)
257{
258	int32 device;
259	if (message->FindInt32("new device", &device) != B_OK)
260		return;
261
262	// _RegisterVolume() also checks whether it is a package FS volume, so we
263	// don't need to bother.
264	_RegisterVolume(device);
265}
266
267
268void
269PackageDaemon::_HandleVolumeUnmounted(const BMessage* message)
270{
271	int32 device;
272	if (message->FindInt32("device", &device) != B_OK)
273		return;
274
275	if (Volume* volume = _FindVolume(device))
276		_UnregisterVolume(volume);
277}
278
279
280// #pragma mark -
281
282
283int
284main(int argc, const char* const* argv)
285{
286	status_t error;
287	PackageDaemon daemon(&error);
288	if (error == B_OK)
289		error = daemon.Init();
290	if (error != B_OK) {
291		FATAL("failed to init server application: %s\n", strerror(error));
292		return 1;
293	}
294
295	daemon.Run();
296	return 0;
297}
298