xref: /haiku/src/servers/package/PackageDaemon.cpp (revision 52f7c9389475e19fc21487b38064b4390eeb6fea)
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 	for (int32 i = 0; Root* root = fRoots.ItemAt(i); i++)
42 		root->ReleaseReference();
43 	fRoots.MakeEmpty(false);
44 }
45 
46 
47 status_t
48 PackageDaemon::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 
69 void
70 PackageDaemon::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 
116 status_t
117 PackageDaemon::_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 
165 void
166 PackageDaemon::_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 
179 status_t
180 PackageDaemon::_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 
220 Root*
221 PackageDaemon::_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 
232 void
233 PackageDaemon::_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 
243 Volume*
244 PackageDaemon::_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 
255 void
256 PackageDaemon::_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 
268 void
269 PackageDaemon::_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 
283 int
284 main(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