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