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