xref: /haiku/src/kits/media/DormantNodeManager.cpp (revision 24159a0c7d6d6dcba9f2a0c1a7c08d2c8167f21b)
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 /* This is a management class for dormant media nodes.
31  * It is private to the media kit and only accessed by the BMediaRoster class
32  * and the media_addon_server.
33  * It handles loading/unloading of dormant nodes.
34  *
35  * Dormant media nodes can be instantiated on demand. The reside on harddisk in the
36  * directories /boot/beos/system/add-ons/media and /boot/home/config/add-ons/media
37  * Multiple media nodes can be included in one file, they can be accessed using the
38  * BMediaAddOn that each file implements.
39  * The BMediaAddOn allows getting a list of supported flavors. Each flavor represents
40  * a media node.
41  * The media_addon_server does the initial scanning of files and getting the list
42  * of supported flavors. It uses the flavor_info to do this, and reports the list
43  * of flavors to the media_server packed into individual dormant_media_node
44  * structures.
45  */
46 
47 #include <stdio.h>
48 #include <string.h>
49 #include <Locker.h>
50 #include <Path.h>
51 #include <Entry.h>
52 #include <MediaAddOn.h>
53 #include "debug.h"
54 #include "PortPool.h"
55 #include "MediaMisc.h"
56 #include "ServerInterface.h"
57 #include "DataExchange.h"
58 #include "DormantNodeManager.h"
59 
60 static BPrivate::media::DormantNodeManager manager;
61 BPrivate::media::DormantNodeManager *_DormantNodeManager = &manager;
62 
63 namespace BPrivate {
64 namespace media {
65 
66 DormantNodeManager::DormantNodeManager()
67 {
68 	fLock = new BLocker("dormant node manager locker");
69 	fAddonmap = new Map<media_addon_id,loaded_addon_info>;
70 }
71 
72 
73 DormantNodeManager::~DormantNodeManager()
74 {
75 	delete fLock;
76 
77 	// force unloading all currently loaded images
78 	loaded_addon_info *info;
79 	for (fAddonmap->Rewind(); fAddonmap->GetNext(&info); ) {
80 		ERROR("Forcing unload of add-on id %ld with usecount %ld\n", info->addon->AddonID(), info->usecount);
81 		UnloadAddon(info->addon, info->image);
82 	}
83 
84 	delete fAddonmap;
85 }
86 
87 
88 BMediaAddOn *
89 DormantNodeManager::TryGetAddon(media_addon_id id)
90 {
91 	loaded_addon_info *info;
92 	BMediaAddOn *addon;
93 
94 	fLock->Lock();
95 	if (fAddonmap->Get(id, &info)) {
96 		info->usecount += 1;
97 		addon = info->addon;
98 		ASSERT(id == addon->AddonID());
99 	} else {
100 		addon = NULL;
101 	}
102 	fLock->Unlock();
103 	return addon;
104 }
105 
106 
107 BMediaAddOn *
108 DormantNodeManager::GetAddon(media_addon_id id)
109 {
110 	BMediaAddOn *addon;
111 
112 	TRACE("DormantNodeManager::GetAddon, id %ld\n",id);
113 
114 	// first try to use a already loaded add-on
115 	addon = TryGetAddon(id);
116 	if (addon)
117 		return addon;
118 
119 	// Be careful, we avoid locking here!
120 
121 	// ok, it's not loaded, try to get the path
122 	BPath path;
123 	if (B_OK != FindAddonPath(&path, id)) {
124 		ERROR("DormantNodeManager::GetAddon: can't find path for add-on %ld\n",id);
125 		return NULL;
126 	}
127 
128 	// try to load it
129 	BMediaAddOn *newaddon;
130 	image_id image;
131 	if (B_OK != LoadAddon(&newaddon, &image, path.Path(), id)) {
132 		ERROR("DormantNodeManager::GetAddon: can't load add-on %ld from path %s\n",id, path.Path());
133 		return NULL;
134 	}
135 
136 	// ok, we successfully loaded it. Now lock and insert it into the map,
137 	// or unload it if the map already contains one that was loaded by another
138 	// thread at the same time
139 	fLock->Lock();
140 	addon = TryGetAddon(id);
141 	if (addon) {
142 		UnloadAddon(newaddon, image);
143 	} else {
144 		// we use the loaded one
145 		addon = newaddon;
146 		// and save it into the list
147 		loaded_addon_info info;
148 		info.addon = newaddon;
149 		info.image = image;
150 		info.usecount = 1;
151 		fAddonmap->Insert(id, info);
152 	}
153 	fLock->Unlock();
154 	ASSERT(addon->AddonID() == id);
155 	return addon;
156 }
157 
158 void
159 DormantNodeManager::PutAddonDelayed(media_addon_id id)
160 {
161 	// Called from a node destructor of the loaded media-add-on.
162 	// We must make sure that the media-add-on stays in memory
163 	// a couple of seconds longer.
164 
165 	UNIMPLEMENTED();
166 }
167 
168 void
169 DormantNodeManager::PutAddon(media_addon_id id)
170 {
171 	loaded_addon_info *info;
172 	BMediaAddOn *addon = 0; /* avoid compiler warning */
173 	image_id image = 0; /* avoid compiler warning */
174 	bool unload;
175 
176 	TRACE("DormantNodeManager::PutAddon, id %ld\n",id);
177 
178 	fLock->Lock();
179 	if (!fAddonmap->Get(id, &info)) {
180 		ERROR("DormantNodeManager::PutAddon: failed to find add-on %ld\n",id);
181 		fLock->Unlock();
182 		return;
183 	}
184 	info->usecount -= 1;
185 	unload = (info->usecount == 0);
186 	if (unload) {
187 		addon	= info->addon;
188 		image	= info->image;
189 		fAddonmap->Remove(id);
190 	}
191 	fLock->Unlock();
192 
193 	if (unload)
194 		UnloadAddon(addon, image);
195 }
196 
197 // For use by media_addon_server only
198 media_addon_id
199 DormantNodeManager::RegisterAddon(const char *path)
200 {
201 	server_register_mediaaddon_request msg;
202 	server_register_mediaaddon_reply reply;
203 	port_id port;
204 	status_t rv;
205 	int32 code;
206 	entry_ref tempref;
207 
208 	TRACE("DormantNodeManager::RegisterAddon, path %s\n",path);
209 
210 	rv = get_ref_for_path(path, &tempref);
211 	if (rv != B_OK) {
212 		ERROR("DormantNodeManager::RegisterAddon failed, couldn't get ref for path %s\n",path);
213 		return 0;
214 	}
215 	msg.ref = tempref;
216 	port = find_port(MEDIA_SERVER_PORT_NAME);
217 	if (port <= B_OK) {
218 		ERROR("DormantNodeManager::RegisterAddon failed, couldn't find media server\n");
219 		return 0;
220 	}
221 	msg.reply_port = _PortPool->GetPort();
222 	rv = write_port(port, SERVER_REGISTER_MEDIAADDON, &msg, sizeof(msg));
223 	if (rv != B_OK) {
224 		_PortPool->PutPort(msg.reply_port);
225 		ERROR("DormantNodeManager::RegisterAddon failed, couldn't talk to media server\n");
226 		return 0;
227 	}
228 	rv = read_port(msg.reply_port, &code, &reply, sizeof(reply));
229 	_PortPool->PutPort(msg.reply_port);
230 	if (rv < B_OK) {
231 		ERROR("DormantNodeManager::RegisterAddon failed, couldn't talk to media server (2)\n");
232 		return 0;
233 	}
234 
235 	TRACE("DormantNodeManager::RegisterAddon finished with id %ld\n",reply.addonid);
236 
237 	return reply.addonid;
238 }
239 
240 // For use by media_addon_server only
241 void
242 DormantNodeManager::UnregisterAddon(media_addon_id id)
243 {
244 	ASSERT(id > 0);
245 	server_unregister_mediaaddon_command msg;
246 
247 	TRACE("DormantNodeManager::UnregisterAddon id %ld\n",id);
248 
249 	port_id port;
250 	port = find_port(MEDIA_SERVER_PORT_NAME);
251 	if (port <= B_OK)
252 		return;
253 	msg.addonid = id;
254 	write_port(port, SERVER_UNREGISTER_MEDIAADDON, &msg, sizeof(msg));
255 }
256 
257 
258 status_t
259 DormantNodeManager::FindAddonPath(BPath *path, media_addon_id id)
260 {
261 	server_get_mediaaddon_ref_request msg;
262 	server_get_mediaaddon_ref_reply reply;
263 	port_id port;
264 	entry_ref tempref;
265 	status_t rv;
266 	int32 code;
267 	port = find_port(MEDIA_SERVER_PORT_NAME);
268 	if (port <= B_OK)
269 		return B_ERROR;
270 	msg.addonid = id;
271 	msg.reply_port = _PortPool->GetPort();
272 	rv = write_port(port, SERVER_GET_MEDIAADDON_REF, &msg, sizeof(msg));
273 	if (rv != B_OK) {
274 		_PortPool->PutPort(msg.reply_port);
275 		return B_ERROR;
276 	}
277 	rv = read_port(msg.reply_port, &code, &reply, sizeof(reply));
278 	_PortPool->PutPort(msg.reply_port);
279 	if (rv < B_OK)
280 		return B_ERROR;
281 
282 	tempref = reply.ref;
283 	return path->SetTo(&tempref);
284 }
285 
286 
287 status_t
288 DormantNodeManager::LoadAddon(BMediaAddOn **newaddon, image_id *newimage, const char *path, media_addon_id id)
289 {
290 	BMediaAddOn *(*make_addon)(image_id you);
291 	BMediaAddOn *addon;
292 	image_id image;
293 	status_t rv;
294 
295 	image = load_add_on(path);
296 	if (image < B_OK) {
297 		ERROR("DormantNodeManager::LoadAddon: loading failed, error %lx (%s), path %s\n", image, strerror(image), path);
298 		return B_ERROR;
299 	}
300 
301 	rv = get_image_symbol(image, "make_media_addon", B_SYMBOL_TYPE_TEXT, (void**)&make_addon);
302 	if (rv < B_OK) {
303 		ERROR("DormantNodeManager::LoadAddon: loading failed, function not found, error %lx (%s)\n", rv, strerror(rv));
304 		unload_add_on(image);
305 		return B_ERROR;
306 	}
307 
308 	addon = make_addon(image);
309 	if (addon == 0) {
310 		ERROR("DormantNodeManager::LoadAddon: creating BMediaAddOn failed\n");
311 		unload_add_on(image);
312 		return B_ERROR;
313 	}
314 
315 	ASSERT(addon->ImageID() == image); // this should be true for a well behaving add-on
316 
317 	// everything ok
318 	*newaddon = addon;
319 	*newimage = image;
320 
321 	// we are a friend class of BMediaAddOn and initialize these member variables
322 	addon->fAddon = id;
323 	addon->fImage = image;
324 
325 	return B_OK;
326 }
327 
328 
329 void
330 DormantNodeManager::UnloadAddon(BMediaAddOn *addon, image_id image)
331 {
332 	ASSERT(addon);
333 	ASSERT(addon->ImageID() == image); // if this failes, something bad happened to the add-on
334 	delete addon;
335 	unload_add_on(image);
336 }
337 
338 }; // namespace media
339 }; // namespace BPrivate
340 
341