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