xref: /haiku/src/kits/media/DormantNodeManager.cpp (revision e1caa56b1e49e83f71ba7998969efbf3bcdbf5dc)
1 /*
2  * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files or portions
6  * thereof (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so, subject
10  * to the following conditions:
11  *
12  *  * Redistributions of source code must retain the above copyright notice,
13  *    this list of conditions and the following disclaimer.
14  *
15  *  * Redistributions in binary form must reproduce the above copyright notice
16  *    in the  binary, as well as this list of conditions and the following
17  *    disclaimer in the documentation and/or other materials provided with
18  *    the distribution.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26  * THE SOFTWARE.
27  *
28  */
29 
30 
31 /*!	This is a management class for dormant media nodes.
32 	It is private to the media kit and only accessed by the BMediaRoster class
33 	and the media_addon_server.
34 	It handles loading/unloading of dormant nodes.
35 
36 	Dormant media nodes can be instantiated on demand. The reside on harddisk in
37 	the directories /boot/beos/system/add-ons/media
38 	and /boot/home/config/add-ons/media.
39 	Multiple media nodes can be included in one file, they can be accessed using
40 	the BMediaAddOn that each file implements.
41 	The BMediaAddOn allows getting a list of supported flavors. Each flavor
42 	represents a media node.
43 	The media_addon_server does the initial scanning of files and getting the
44 	list of supported flavors. It uses the flavor_info to do this, and reports
45 	the list of flavors to the media_server packed into individual
46 	dormant_media_node structures.
47 */
48 
49 
50 #include "DormantNodeManager.h"
51 
52 #include <stdio.h>
53 #include <string.h>
54 
55 #include <Autolock.h>
56 #include <Entry.h>
57 #include <Path.h>
58 
59 #include <debug.h>
60 #include <MediaMisc.h>
61 #include <ServerInterface.h>
62 #include <DataExchange.h>
63 
64 
65 namespace BPrivate {
66 namespace media {
67 
68 
69 DormantNodeManager* gDormantNodeManager;
70 	// initialized by BMediaRoster.
71 
72 
73 DormantNodeManager::DormantNodeManager()
74 	:
75 	fLock("dormant node manager locker")
76 {
77 }
78 
79 
80 DormantNodeManager::~DormantNodeManager()
81 {
82 	// force unloading all currently loaded images
83 
84 	AddOnMap::iterator iterator = fAddOnMap.begin();
85 	for (; iterator != fAddOnMap.end(); iterator++) {
86 		loaded_add_on_info& info = iterator->second;
87 
88 		ERROR("Forcing unload of add-on id %" B_PRId32 " with usecount %"
89 			B_PRId32 "\n", info.add_on->AddonID(), info.use_count);
90 		_UnloadAddOn(info.add_on, info.image);
91 	}
92 }
93 
94 
95 BMediaAddOn*
96 DormantNodeManager::GetAddOn(media_addon_id id)
97 {
98 	TRACE("DormantNodeManager::GetAddon, id %" B_PRId32 "\n", id);
99 
100 	// first try to use a already loaded add-on
101 	BMediaAddOn* addOn = _LookupAddOn(id);
102 	if (addOn != NULL)
103 		return addOn;
104 
105 	// Be careful, we avoid locking here!
106 
107 	// ok, it's not loaded, try to get the path
108 	BPath path;
109 	if (FindAddOnPath(&path, id) != B_OK) {
110 		ERROR("DormantNodeManager::GetAddon: can't find path for add-on %"
111 			B_PRId32 "\n", id);
112 		return NULL;
113 	}
114 
115 	// try to load it
116 	BMediaAddOn* newAddOn;
117 	image_id image;
118 	if (_LoadAddOn(path.Path(), id, &newAddOn, &image) != B_OK) {
119 		ERROR("DormantNodeManager::GetAddon: can't load add-on %" B_PRId32
120 			" from path %s\n",id, path.Path());
121 		return NULL;
122 	}
123 
124 	// ok, we successfully loaded it. Now lock and insert it into the map,
125 	// or unload it if the map already contains one that was loaded by another
126 	// thread at the same time
127 
128 	BAutolock _(fLock);
129 
130 	addOn = _LookupAddOn(id);
131 	if (addOn == NULL) {
132 		// we use the loaded one
133 		addOn = newAddOn;
134 
135 		// and save it into the list
136 		loaded_add_on_info info;
137 		info.add_on = newAddOn;
138 		info.image = image;
139 		info.use_count = 1;
140 		try {
141 			fAddOnMap.insert(std::make_pair(id, info));
142 		} catch (std::bad_alloc& exception) {
143 			_UnloadAddOn(newAddOn, image);
144 			return NULL;
145 		}
146 	} else
147 		_UnloadAddOn(newAddOn, image);
148 
149 	ASSERT(addOn->AddonID() == id);
150 	return addOn;
151 }
152 
153 
154 void
155 DormantNodeManager::PutAddOn(media_addon_id id)
156 {
157 	TRACE("DormantNodeManager::PutAddon, id %" B_PRId32 "\n", id);
158 
159 	BAutolock locker(fLock);
160 
161 	AddOnMap::iterator found = fAddOnMap.find(id);
162 	if (found == fAddOnMap.end()) {
163 		ERROR("DormantNodeManager::PutAddon: failed to find add-on %" B_PRId32
164 			"\n", id);
165 		return;
166 	}
167 
168 	loaded_add_on_info& info = found->second;
169 
170 	if (--info.use_count == 0) {
171 		// unload add-on
172 
173 		BMediaAddOn* addOn = info.add_on;
174 		image_id image = info.image;
175 		fAddOnMap.erase(found);
176 
177 		locker.Unlock();
178 		_UnloadAddOn(addOn, image);
179 	}
180 }
181 
182 
183 void
184 DormantNodeManager::PutAddOnDelayed(media_addon_id id)
185 {
186 	// Called from a node destructor of the loaded media-add-on.
187 	// We must make sure that the media-add-on stays in memory
188 	// a couple of seconds longer.
189 
190 	UNIMPLEMENTED();
191 }
192 
193 
194 //!	For use by media_addon_server only
195 media_addon_id
196 DormantNodeManager::RegisterAddOn(const char* path)
197 {
198 	TRACE("DormantNodeManager::RegisterAddon, path %s\n", path);
199 
200 	entry_ref ref;
201 	status_t status = get_ref_for_path(path, &ref);
202 	if (status != B_OK) {
203 		ERROR("DormantNodeManager::RegisterAddon failed, couldn't get ref "
204 			"for path %s: %s\n", path, strerror(status));
205 		return 0;
206 	}
207 
208 	server_register_add_on_request request;
209 	request.ref = ref;
210 
211 	server_register_add_on_reply reply;
212 	status = QueryServer(SERVER_REGISTER_ADD_ON, &request, sizeof(request),
213 		&reply, sizeof(reply));
214 	if (status != B_OK) {
215 		ERROR("DormantNodeManager::RegisterAddon failed, couldn't talk to "
216 			"media server\n");
217 		return 0;
218 	}
219 
220 	if (status < B_OK) {
221 		ERROR("DormantNodeManager::RegisterAddon failed, couldn't talk to "
222 			"media server: %s\n", strerror(status));
223 		return 0;
224 	}
225 
226 	TRACE("DormantNodeManager::RegisterAddon finished with id %" B_PRId32 "\n",
227 		reply.add_on_id);
228 
229 	return reply.add_on_id;
230 }
231 
232 
233 //!	For use by media_addon_server only
234 void
235 DormantNodeManager::UnregisterAddOn(media_addon_id id)
236 {
237 	TRACE("DormantNodeManager::UnregisterAddon id %" B_PRId32 "\n", id);
238 	ASSERT(id > 0);
239 
240 	port_id port = find_port(MEDIA_SERVER_PORT_NAME);
241 	if (port < 0)
242 		return;
243 
244 	server_unregister_add_on_command msg;
245 	msg.add_on_id = id;
246 	write_port(port, SERVER_UNREGISTER_ADD_ON, &msg, sizeof(msg));
247 }
248 
249 
250 status_t
251 DormantNodeManager::FindAddOnPath(BPath* path, media_addon_id id)
252 {
253 	server_get_add_on_ref_request request;
254 	request.add_on_id = id;
255 
256 	server_get_add_on_ref_reply reply;
257 	status_t status = QueryServer(SERVER_GET_ADD_ON_REF, &request,
258 		sizeof(request), &reply, sizeof(reply));
259 	if (status != B_OK)
260 		return status;
261 
262 	entry_ref ref = reply.ref;
263 	return path->SetTo(&ref);
264 }
265 
266 
267 BMediaAddOn*
268 DormantNodeManager::_LookupAddOn(media_addon_id id)
269 {
270 	BAutolock _(fLock);
271 
272 	AddOnMap::iterator found = fAddOnMap.find(id);
273 	if (found == fAddOnMap.end())
274 		return NULL;
275 
276 	loaded_add_on_info& info = found->second;
277 
278 	ASSERT(id == info.add_on->AddonID());
279 	info.use_count++;
280 
281 	return info.add_on;
282 }
283 
284 
285 status_t
286 DormantNodeManager::_LoadAddOn(const char* path, media_addon_id id,
287 	BMediaAddOn** _newAddOn, image_id* _newImage)
288 {
289 	image_id image = load_add_on(path);
290 	if (image < 0) {
291 		ERROR("DormantNodeManager::LoadAddon: loading \"%s\" failed: %s\n",
292 			path, strerror(image));
293 		return image;
294 	}
295 
296 	BMediaAddOn* (*makeAddOn)(image_id);
297 	status_t status = get_image_symbol(image, "make_media_addon",
298 		B_SYMBOL_TYPE_TEXT, (void**)&makeAddOn);
299 	if (status != B_OK) {
300 		ERROR("DormantNodeManager::LoadAddon: loading failed, function not "
301 			"found: %s\n", strerror(status));
302 		unload_add_on(image);
303 		return status;
304 	}
305 
306 	BMediaAddOn* addOn = makeAddOn(image);
307 	if (addOn == NULL) {
308 		ERROR("DormantNodeManager::LoadAddon: creating BMediaAddOn failed\n");
309 		unload_add_on(image);
310 		return B_ERROR;
311 	}
312 
313 	ASSERT(addOn->ImageID() == image);
314 		// this should be true for a well behaving add-ons
315 
316 	// We are a friend class of BMediaAddOn and initialize these member
317 	// variables
318 	addOn->fAddon = id;
319 	addOn->fImage = image;
320 
321 	// everything ok
322 	*_newAddOn = addOn;
323 	*_newImage = image;
324 
325 	return B_OK;
326 }
327 
328 
329 void
330 DormantNodeManager::_UnloadAddOn(BMediaAddOn* addOn, image_id image)
331 {
332 	ASSERT(addOn != NULL);
333 	ASSERT(addOn->ImageID() == image);
334 		// if this fails, something bad happened to the add-on
335 
336 	delete addOn;
337 	unload_add_on(image);
338 }
339 
340 
341 }	// namespace media
342 }	// namespace BPrivate
343