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 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: %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 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 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* 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 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 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