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