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
65 namespace BPrivate {
66 namespace media {
67
68
69 DormantNodeManager* gDormantNodeManager;
70 // initialized by BMediaRoster.
71
72
DormantNodeManager()73 DormantNodeManager::DormantNodeManager()
74 :
75 fLock("dormant node manager locker")
76 {
77 }
78
79
~DormantNodeManager()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*
GetAddOn(media_addon_id id)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
PutAddOn(media_addon_id id)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
PutAddOnDelayed(media_addon_id id)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
RegisterAddOn(const char * path)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: %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
228 void
UnregisterAddOn(media_addon_id id)229 DormantNodeManager::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
244 status_t
FindAddOnPath(BPath * path,media_addon_id id)245 DormantNodeManager::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
261 BMediaAddOn*
_LookupAddOn(media_addon_id id)262 DormantNodeManager::_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
279 status_t
_LoadAddOn(const char * path,media_addon_id id,BMediaAddOn ** _newAddOn,image_id * _newImage)280 DormantNodeManager::_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
323 void
_UnloadAddOn(BMediaAddOn * addOn,image_id image)324 DormantNodeManager::_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