xref: /haiku/src/servers/package/PackageDaemon.cpp (revision 22440f4105cafc95cc1d49f9bc65bb395c527d86)
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 			if (fSystemRoot != NULL)
87 				fSystemRoot->HandleRequest(DetachCurrentMessage());
88 			break;
89 		}
90 
91 		default:
92 			BServer::MessageReceived(message);
93 			break;
94 	}
95 }
96 
97 
98 status_t
99 PackageDaemon::_RegisterVolume(dev_t deviceID)
100 {
101 	// get the FS info and check whether this is a package FS volume at all
102 	fs_info info;
103 	status_t error = fs_stat_dev(deviceID, &info);
104 	if (error != 0)
105 		RETURN_ERROR(error);
106 
107 	if (strcmp(info.fsh_name, "packagefs") != 0)
108 		RETURN_ERROR(B_BAD_VALUE);
109 
110 	// create a volume
111 	Volume* volume = new(std::nothrow) Volume(this);
112 	if (volume == NULL)
113 		RETURN_ERROR(B_NO_MEMORY);
114 	ObjectDeleter<Volume> volumeDeleter(volume);
115 
116 	node_ref rootRef;
117 	error = volume->Init(node_ref(info.dev, info.root), rootRef);
118 	if (error != B_OK)
119 		RETURN_ERROR(error);
120 
121 	if (volume->MountType() == PACKAGE_FS_MOUNT_TYPE_CUSTOM) {
122 // TODO: Or maybe not?
123 		INFORM("skipping custom mounted volume at \"%s\"\n",
124 			volume->Path().String());
125 		return B_OK;
126 	}
127 
128 	// get the root for the volume and register it
129 	Root* root;
130 	error = _GetOrCreateRoot(rootRef, root);
131 	if (error != B_OK)
132 		RETURN_ERROR(error);
133 
134 	error = root->RegisterVolume(volume);
135 	if (error != B_OK) {
136 		_PutRoot(root);
137 		RETURN_ERROR(error);
138 	}
139 	volumeDeleter.Detach();
140 
141 	INFORM("volume at \"%s\" registered\n", volume->Path().String());
142 
143 	return B_OK;
144 }
145 
146 
147 void
148 PackageDaemon::_UnregisterVolume(Volume* volume)
149 {
150 	volume->Unmounted();
151 
152 	INFORM("volume at \"%s\" unregistered\n", volume->Path().String());
153 
154 	Root* root = volume->GetRoot();
155 	root->UnregisterVolume(volume);
156 
157 	_PutRoot(root);
158 }
159 
160 
161 status_t
162 PackageDaemon::_GetOrCreateRoot(const node_ref& nodeRef, Root*& _root)
163 {
164 	Root* root = _FindRoot(nodeRef);
165 	if (root != NULL) {
166 		root->AcquireReference();
167 	} else {
168 		root = new(std::nothrow) Root;
169 		if (root == NULL)
170 			RETURN_ERROR(B_NO_MEMORY);
171 		ObjectDeleter<Root> rootDeleter(root);
172 
173 		bool isSystemRoot = false;
174 		if (fSystemRoot == NULL) {
175 			struct stat st;
176 			isSystemRoot = stat("/boot", &st) == 0
177 				&& node_ref(st.st_dev, st.st_ino) == nodeRef;
178 		}
179 
180 		status_t error = root->Init(nodeRef, isSystemRoot);
181 		if (error != B_OK)
182 			RETURN_ERROR(error);
183 
184 		if (!fRoots.AddItem(root))
185 			RETURN_ERROR(B_NO_MEMORY);
186 
187 		rootDeleter.Detach();
188 
189 		if (isSystemRoot)
190 			fSystemRoot = root;
191 
192 		INFORM("root at \"%s\" (device: %" B_PRIdDEV ", node: %" B_PRIdINO ") "
193 			"registered\n", root->Path().String(), nodeRef.device,
194 			nodeRef.node);
195 	}
196 
197 	_root = root;
198 	return B_OK;
199 }
200 
201 
202 Root*
203 PackageDaemon::_FindRoot(const node_ref& nodeRef) const
204 {
205 	for (int32 i = 0; Root* root = fRoots.ItemAt(i); i++) {
206 		if (root->NodeRef() == nodeRef)
207 			return root;
208 	}
209 
210 	return NULL;
211 }
212 
213 
214 void
215 PackageDaemon::_PutRoot(Root* root)
216 {
217 	if (root->ReleaseReference() == 1) {
218 		INFORM("root at \"%s\" unregistered\n", root->Path().String());
219 		fRoots.RemoveItem(root, true);
220 			// deletes the object
221 	}
222 }
223 
224 
225 Volume*
226 PackageDaemon::_FindVolume(dev_t deviceID) const
227 {
228 	for (int32 i = 0; Root* root = fRoots.ItemAt(i); i++) {
229 		if (Volume* volume = root->FindVolume(deviceID))
230 			return volume;
231 	}
232 
233 	return NULL;
234 }
235 
236 
237 void
238 PackageDaemon::_HandleVolumeMounted(const BMessage* message)
239 {
240 	int32 device;
241 	if (message->FindInt32("new device", &device) != B_OK)
242 		return;
243 
244 	// _RegisterVolume() also checks whether it is a package FS volume, so we
245 	// don't need to bother.
246 	_RegisterVolume(device);
247 }
248 
249 
250 void
251 PackageDaemon::_HandleVolumeUnmounted(const BMessage* message)
252 {
253 	int32 device;
254 	if (message->FindInt32("device", &device) != B_OK)
255 		return;
256 
257 	if (Volume* volume = _FindVolume(device))
258 		_UnregisterVolume(volume);
259 }
260 
261 
262 // #pragma mark -
263 
264 
265 int
266 main(int argc, const char* const* argv)
267 {
268 	status_t error;
269 	PackageDaemon daemon(&error);
270 	if (error == B_OK)
271 		error = daemon.Init();
272 	if (error != B_OK) {
273 		FATAL("failed to init server application: %s\n", strerror(error));
274 		return 1;
275 	}
276 
277 	daemon.Run();
278 	return 0;
279 }
280