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 <MediaDebug.h>
60#include <MediaMisc.h>
61#include <ServerInterface.h>
62#include <DataExchange.h>
63
64
65namespace BPrivate {
66namespace media {
67
68
69DormantNodeManager* gDormantNodeManager;
70	// initialized by BMediaRoster.
71
72
73DormantNodeManager::DormantNodeManager()
74	:
75	fLock("dormant node manager locker")
76{
77}
78
79
80DormantNodeManager::~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
95BMediaAddOn*
96DormantNodeManager::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
154void
155DormantNodeManager::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
183void
184DormantNodeManager::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
195media_addon_id
196DormantNodeManager::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: %s\n", strerror(status));
217		return 0;
218	}
219
220	TRACE("DormantNodeManager::RegisterAddon finished with id %" B_PRId32 "\n",
221		reply.add_on_id);
222
223	return reply.add_on_id;
224}
225
226
227//!	For use by media_addon_server only
228void
229DormantNodeManager::UnregisterAddOn(media_addon_id id)
230{
231	TRACE("DormantNodeManager::UnregisterAddon id %" B_PRId32 "\n", id);
232	ASSERT(id > 0);
233
234	port_id port = find_port(MEDIA_SERVER_PORT_NAME);
235	if (port < 0)
236		return;
237
238	server_unregister_add_on_command msg;
239	msg.add_on_id = id;
240	write_port(port, SERVER_UNREGISTER_ADD_ON, &msg, sizeof(msg));
241}
242
243
244status_t
245DormantNodeManager::FindAddOnPath(BPath* path, media_addon_id id)
246{
247	server_get_add_on_ref_request request;
248	request.add_on_id = id;
249
250	server_get_add_on_ref_reply reply;
251	status_t status = QueryServer(SERVER_GET_ADD_ON_REF, &request,
252		sizeof(request), &reply, sizeof(reply));
253	if (status != B_OK)
254		return status;
255
256	entry_ref ref = reply.ref;
257	return path->SetTo(&ref);
258}
259
260
261BMediaAddOn*
262DormantNodeManager::_LookupAddOn(media_addon_id id)
263{
264	BAutolock _(fLock);
265
266	AddOnMap::iterator found = fAddOnMap.find(id);
267	if (found == fAddOnMap.end())
268		return NULL;
269
270	loaded_add_on_info& info = found->second;
271
272	ASSERT(id == info.add_on->AddonID());
273	info.use_count++;
274
275	return info.add_on;
276}
277
278
279status_t
280DormantNodeManager::_LoadAddOn(const char* path, media_addon_id id,
281	BMediaAddOn** _newAddOn, image_id* _newImage)
282{
283	image_id image = load_add_on(path);
284	if (image < 0) {
285		ERROR("DormantNodeManager::LoadAddon: loading \"%s\" failed: %s\n",
286			path, strerror(image));
287		return image;
288	}
289
290	BMediaAddOn* (*makeAddOn)(image_id);
291	status_t status = get_image_symbol(image, "make_media_addon",
292		B_SYMBOL_TYPE_TEXT, (void**)&makeAddOn);
293	if (status != B_OK) {
294		ERROR("DormantNodeManager::LoadAddon: loading failed, function not "
295			"found: %s\n", strerror(status));
296		unload_add_on(image);
297		return status;
298	}
299
300	BMediaAddOn* addOn = makeAddOn(image);
301	if (addOn == NULL) {
302		ERROR("DormantNodeManager::LoadAddon: creating BMediaAddOn failed\n");
303		unload_add_on(image);
304		return B_ERROR;
305	}
306
307	ASSERT(addOn->ImageID() == image);
308		// this should be true for a well behaving add-ons
309
310	// We are a friend class of BMediaAddOn and initialize these member
311	// variables
312	addOn->fAddon = id;
313	addOn->fImage = image;
314
315	// everything ok
316	*_newAddOn = addOn;
317	*_newImage = image;
318
319	return B_OK;
320}
321
322
323void
324DormantNodeManager::_UnloadAddOn(BMediaAddOn* addOn, image_id image)
325{
326	ASSERT(addOn != NULL);
327	ASSERT(addOn->ImageID() == image);
328		// if this fails, something bad happened to the add-on
329
330	delete addOn;
331	unload_add_on(image);
332}
333
334
335}	// namespace media
336}	// namespace BPrivate
337