1 #include "AddOnMonitorHandler.h" 2 #include <Directory.h> 3 4 #ifndef ADD_ON_STABLE_SECONDS 5 #define ADD_ON_STABLE_SECONDS 15 6 #endif 7 8 /* 9 * public functions 10 */ 11 12 AddOnMonitorHandler::AddOnMonitorHandler(const char * name) 13 : NodeMonitorHandler(name) 14 { 15 } 16 17 18 AddOnMonitorHandler::~AddOnMonitorHandler() 19 { 20 } 21 22 23 /* virtual */ void 24 AddOnMonitorHandler::MessageReceived(BMessage * msg) 25 { 26 if (msg->what == B_PULSE) { 27 HandlePulse(); 28 } 29 inherited::MessageReceived(msg); 30 } 31 32 33 /* virtual */ status_t 34 AddOnMonitorHandler::AddDirectory(const node_ref * nref) 35 { 36 // ignore directories added twice 37 std::list<add_on_directory_info>::iterator diter = directories.begin(); 38 for( ; diter != directories.end() ; diter++) { 39 if (diter->nref == *nref) { 40 return B_OK; 41 } 42 } 43 44 BDirectory directory(nref); 45 status_t status = directory.InitCheck(); 46 if (status != B_OK) { 47 return status; 48 } 49 50 add_on_directory_info dir_info; 51 dir_info.nref = *nref; 52 directories.push_back(dir_info); 53 54 status = watch_node(nref, B_WATCH_DIRECTORY, this); 55 if (status != B_OK) { 56 directories.pop_back(); 57 return status; 58 } 59 60 BEntry entry; 61 while (directory.GetNextEntry(&entry, true) == B_OK) { 62 add_on_entry_info entry_info; 63 if (entry.GetName(entry_info.name) != B_OK) { 64 continue; // discard and proceed 65 } 66 if (entry.GetNodeRef(&entry_info.nref) != B_OK) { 67 continue; // discard and proceed 68 } 69 entry_info.dir_nref = *nref; 70 pending_entries.push_back(entry_info); 71 } 72 73 return B_OK; 74 } 75 76 77 /* 78 * AddOnMonitorHandler hooks 79 */ 80 81 /* virtual */ void 82 AddOnMonitorHandler::AddOnCreated(const add_on_entry_info * entry_info) 83 { 84 85 } 86 87 88 /* virtual */ void 89 AddOnMonitorHandler::AddOnEnabled(const add_on_entry_info * entry_info) 90 { 91 92 } 93 94 95 /* virtual */ void 96 AddOnMonitorHandler::AddOnDisabled(const add_on_entry_info * entry_info) 97 { 98 99 } 100 101 102 /* virtual */ void 103 AddOnMonitorHandler::AddOnRemoved(const add_on_entry_info * entry_info) 104 { 105 106 } 107 108 109 /* 110 * NodeMonitorHandler hooks 111 */ 112 113 114 /* virtual */ void 115 AddOnMonitorHandler::EntryCreated(const char *name, ino_t directory, 116 dev_t device, ino_t node) 117 { 118 add_on_entry_info entry_info; 119 strncpy(entry_info.name, name, sizeof(entry_info.name)); 120 make_node_ref(device, node, &entry_info.nref); 121 make_node_ref(device, directory, &entry_info.dir_nref); 122 pending_entries.push_back(entry_info); 123 } 124 125 126 /* virtual */ void 127 AddOnMonitorHandler::EntryRemoved(ino_t directory, dev_t device, ino_t node) 128 { 129 node_ref entry_nref; 130 make_node_ref(device, node, &entry_nref); 131 132 // Search pending entries first, which can simply be discarded 133 // We might have this entry in the pending list multiple times, 134 // so we search entire list through, even after finding one. 135 std::list<add_on_entry_info>::iterator eiter = pending_entries.begin(); 136 while (eiter != pending_entries.end()) { 137 if (eiter->nref == entry_nref) { 138 eiter = pending_entries.erase(eiter); 139 } else { 140 eiter++; 141 } 142 } 143 144 add_on_entry_info info; 145 node_ref dir_nref; 146 make_node_ref(device, directory, &dir_nref); 147 148 // find the entry's info, and the entry's directory info 149 std::list<add_on_directory_info>::iterator diter = directories.begin(); 150 for( ; diter != directories.end() ; diter++) { 151 if (diter->nref == dir_nref) { 152 std::list<add_on_entry_info>::iterator eiter = diter->entries.begin(); 153 for( ; eiter != diter->entries.end() ; eiter++) { 154 info = *eiter; 155 if (eiter->nref == entry_nref) { 156 info = *eiter; 157 diter->entries.erase(eiter); 158 break; 159 } 160 } 161 break; 162 } 163 } 164 165 // if it was not found, we're done 166 if (diter == directories.end()) { 167 return; 168 } 169 170 // Start at the top again, and search until the directory we found 171 // the old add_on in. If we find a add_on with the same name then 172 // the old add_on was not enabled. So we deallocate the old 173 // add_on and return. 174 std::list<add_on_directory_info>::iterator diter2 = directories.begin(); 175 for( ; diter2 != diter ; diter2++) { 176 std::list<add_on_entry_info>::iterator eiter = diter2->entries.begin(); 177 for( ; eiter != diter2->entries.end() ; eiter++) { 178 if (strcmp(eiter->name, info.name) == 0) { 179 AddOnRemoved(&info); 180 return; 181 } 182 } 183 } 184 185 // The active plugin was removed. We need to disable and 186 // then subsequently deallocate it. 187 AddOnDisabled(&info); 188 AddOnRemoved(&info); 189 190 // Continue searching for a add_on below us. If we find a add_on 191 // with the same name, we must enable it. 192 for (diter++ ; diter != directories.end() ; diter++) { 193 std::list<add_on_entry_info>::iterator eiter = diter->entries.begin(); 194 for( ; eiter != diter->entries.end() ; eiter++) { 195 if (strcmp(eiter->name, info.name) == 0) { 196 AddOnEnabled(&*eiter); 197 return; 198 } 199 } 200 } 201 } 202 203 204 /* virtual */ void 205 AddOnMonitorHandler::EntryMoved(const char *name, ino_t from_directory, 206 ino_t to_directory, dev_t device, ino_t node) 207 { 208 node_ref from_nref; 209 make_node_ref(device, from_directory, &from_nref); 210 std::list<add_on_directory_info>::iterator from_iter = directories.begin(); 211 for ( ; from_iter != directories.end() ; from_iter++) { 212 if (from_iter->nref == from_nref) { 213 break; 214 } 215 } 216 217 node_ref to_nref; 218 make_node_ref(device, to_directory, &to_nref); 219 std::list<add_on_directory_info>::iterator to_iter = directories.begin(); 220 for ( ; to_iter != directories.end() ; to_iter++) { 221 if (to_iter->nref == to_nref) { 222 break; 223 } 224 } 225 226 if ((from_iter == directories.end()) && (to_iter == directories.end())) { 227 // huh? whatever... 228 return; 229 } 230 231 add_on_entry_info info; 232 node_ref entry_nref; 233 make_node_ref(device, node, &entry_nref); 234 235 if (to_iter == directories.end()) { 236 // moved out of our view 237 std::list<add_on_entry_info>::iterator eiter = from_iter->entries.begin(); 238 for( ; eiter != from_iter->entries.end() ; eiter++) { 239 if (entry_nref == eiter->nref) { 240 // save the info and remove the entry 241 info = *eiter; 242 from_iter->entries.erase(eiter); 243 break; 244 } 245 } 246 247 if (eiter == from_iter->entries.end()) { 248 // we don't know anything about this entry yet.. ignore it 249 return; 250 } 251 252 // if the name is the same, save the information about this 253 // add_on into former_entries for later use 254 if (strcmp(info.name, name) == 0) { 255 former_entries.push_back(info); 256 } 257 258 // Start at the top again, and search until the from directory. 259 // If we find a add_on with the same name then the moved add_on 260 // was not enabled. So we are done. 261 std::list<add_on_directory_info>::iterator diter = directories.begin(); 262 for( ; diter != from_iter ; diter++) { 263 std::list<add_on_entry_info>::iterator eiter2 = diter->entries.begin(); 264 for( ; eiter2 != diter->entries.end() ; eiter2++) { 265 if (strcmp(eiter2->name, info.name) == 0) { 266 return; 267 } 268 } 269 } 270 271 // finally disable the add_on 272 AddOnDisabled(&info); 273 274 // Continue searching for a add_on below us. If we find a add_on 275 // with the same name, we must enable it. 276 for (from_iter++ ; from_iter != directories.end() ; from_iter++) { 277 std::list<add_on_entry_info>::iterator eiter2 = from_iter->entries.begin(); 278 for( ; eiter2 != from_iter->entries.end() ; eiter2++) { 279 if (strcmp(eiter2->name, info.name) == 0) { 280 AddOnEnabled(&*eiter2); 281 return; 282 } 283 } 284 } 285 286 // finally, if the new name is different, destroy the addon 287 if (strcmp(info.name, name) != 0) { 288 AddOnRemoved(&info); 289 } 290 291 // done 292 return; 293 } 294 295 if (from_iter == directories.end()) { 296 // moved into our view 297 std::list<add_on_entry_info>::iterator eiter = former_entries.begin(); 298 for( ; eiter != former_entries.end() ; eiter++) { 299 if (entry_nref == eiter->nref) { 300 // save the info and remove the entry 301 info = *eiter; 302 former_entries.erase(eiter); 303 break; 304 } 305 } 306 307 if (eiter != former_entries.end()) { 308 if (strcmp(info.name, name) != 0) { 309 // name changed on the way in, remove the old one 310 AddOnRemoved(&info); 311 } 312 } 313 314 // update the info 315 strncpy(info.name, name, sizeof(info.name)); 316 info.nref = entry_nref; 317 info.dir_nref = to_nref; 318 319 if (eiter == former_entries.end()) { 320 // this add_on was not seen before 321 AddOnCreated(&info); 322 } 323 324 // Start at the top again, and search until the to directory. 325 // If we find a add_on with the same name then the moved add_on 326 // is not to be enabled. So we are done. 327 std::list<add_on_directory_info>::iterator diter = directories.begin(); 328 for( ; diter != to_iter ; diter++) { 329 std::list<add_on_entry_info>::iterator eiter2 = diter->entries.begin(); 330 for( ; eiter2 != diter->entries.end() ; eiter2++) { 331 if (strcmp(eiter2->name, info.name) == 0) { 332 return; 333 } 334 } 335 } 336 337 // The new add_on should be enabled, but first we check to see 338 // if there is an add_on below us. If we find one, we disable it. 339 bool shadowing = false; 340 for (diter++ ; diter != directories.end() ; diter++) { 341 std::list<add_on_entry_info>::iterator eiter2 = diter->entries.begin(); 342 for( ; eiter2 != diter->entries.end() ; eiter2++) { 343 if (strcmp(eiter2->name, info.name) == 0) { 344 AddOnDisabled(&*eiter2); 345 shadowing = true; 346 break; 347 } 348 } 349 if (shadowing) { 350 break; 351 } 352 } 353 354 // enable the new add_on 355 AddOnEnabled(&info); 356 357 // put the new entry into the target directory 358 to_iter->entries.push_back(info); 359 360 // done 361 return; 362 } 363 364 std::list<add_on_entry_info>::iterator eiter = from_iter->entries.begin(); 365 for( ; eiter != from_iter->entries.end() ; eiter++) { 366 if (entry_nref == eiter->nref) { 367 // save the old info and remove the entry 368 info = *eiter; 369 from_iter->entries.erase(eiter); 370 break; 371 } 372 } 373 374 if (strcmp(info.name, name) == 0) { 375 // same name moved in heirarchy 376 debugger("add_on moved inside the heirarchy"); 377 } else { 378 // check to see if it was formerly enabled 379 bool was_enabled = true; 380 std::list<add_on_directory_info>::iterator old_iter = directories.begin(); 381 for( ; old_iter != from_iter ; old_iter++) { 382 std::list<add_on_entry_info>::iterator eiter2 = old_iter->entries.begin(); 383 for( ; eiter2 != old_iter->entries.end() ; eiter2++) { 384 if (strcmp(eiter2->name, info.name) == 0) { 385 was_enabled = false; 386 break; 387 } 388 } 389 if (!was_enabled) { 390 break; 391 } 392 } 393 394 // if it was enabled, disable it and enable the one under us, if it exists 395 if (was_enabled) { 396 AddOnDisabled(&info); 397 bool done = false; 398 for( ; old_iter != directories.end() ; old_iter++) { 399 std::list<add_on_entry_info>::iterator eiter2 = old_iter->entries.begin(); 400 for( ; eiter2 != old_iter->entries.end() ; eiter2++) { 401 if (strcmp(eiter2->name, info.name) == 0) { 402 AddOnEnabled(&*eiter2); 403 done = true; 404 break; 405 } 406 } 407 if (done) { 408 break; 409 } 410 } 411 } 412 413 // kaboom! 414 AddOnRemoved(&info); 415 416 // set up new addon info 417 strncpy(info.name, name, sizeof(info.name)); 418 info.dir_nref = to_nref; 419 420 // presto! 421 AddOnCreated(&info); 422 423 // check to see if we are newly enabled 424 bool is_enabled = true; 425 std::list<add_on_directory_info>::iterator new_iter = directories.begin(); 426 for( ; new_iter != to_iter ; new_iter++) { 427 std::list<add_on_entry_info>::iterator eiter2 = new_iter->entries.begin(); 428 for( ; eiter2 != new_iter->entries.end() ; eiter2++) { 429 if (strcmp(eiter2->name, info.name) == 0) { 430 is_enabled = false; 431 break; 432 } 433 } 434 if (!is_enabled) { 435 break; 436 } 437 } 438 439 // if it is newly enabled, check under us for an enabled one, and disable that first 440 if (is_enabled) { 441 bool done = false; 442 for( ; new_iter != directories.end() ; new_iter++) { 443 std::list<add_on_entry_info>::iterator eiter2 = new_iter->entries.begin(); 444 for( ; eiter2 != new_iter->entries.end() ; eiter2++) { 445 if (strcmp(eiter2->name, info.name) == 0) { 446 AddOnDisabled(&*eiter2); 447 done = true; 448 break; 449 } 450 } 451 if (done) { 452 break; 453 } 454 } 455 AddOnEnabled(&info); 456 } 457 } 458 459 // put the new entry into the target directory 460 to_iter->entries.push_back(info); 461 } 462 463 464 /* 465 * process pending entries 466 */ 467 468 void 469 AddOnMonitorHandler::HandlePulse() 470 { 471 BDirectory directory; 472 std::list<add_on_entry_info>::iterator iter = pending_entries.begin(); 473 while (iter != pending_entries.end()) { 474 add_on_entry_info info = *iter; 475 476 node_ref dir_nref; 477 if ((directory.GetNodeRef(&dir_nref) != B_OK) || 478 (dir_nref != info.dir_nref)) { 479 if (directory.SetTo(&info.dir_nref) != B_OK) { 480 // invalid directory, discard this pending entry 481 iter = pending_entries.erase(iter); 482 continue; 483 } 484 dir_nref = info.dir_nref; 485 } 486 487 struct stat st; 488 if (directory.GetStatFor(info.name, &st) != B_OK) { 489 // invalid file name, discard this pending entry 490 iter = pending_entries.erase(iter); 491 continue; 492 } 493 494 // stat units are seconds, real_time_clock units are seconds 495 if (real_time_clock() - st.st_mtime < ADD_ON_STABLE_SECONDS) { 496 // entry not stable, skip the entry for this pulse 497 iter++; 498 continue; 499 } 500 501 // we are going to deal with the stable entry, so remove it 502 iter = pending_entries.erase(iter); 503 504 // put the new entry into the directory info 505 std::list<add_on_directory_info>::iterator diter = directories.begin(); 506 for( ; diter != directories.end() ; diter++) { 507 if (diter->nref == dir_nref) { 508 diter->entries.push_back(info); 509 break; 510 } 511 } 512 513 // report it 514 AddOnCreated(&info); 515 516 // Start at the top again, and search until the directory we put 517 // the new add_on in. If we find a add_on with the same name then 518 // the new add_on should not be enabled. 519 bool enabled = true; 520 std::list<add_on_directory_info>::iterator diter2 = directories.begin(); 521 for( ; diter2 != diter ; diter2++) { 522 std::list<add_on_entry_info>::iterator eiter = diter2->entries.begin(); 523 for( ; eiter != diter2->entries.end() ; eiter++) { 524 if (strcmp(eiter->name, info.name) == 0) { 525 enabled = false; 526 break; 527 } 528 } 529 if (!enabled) { 530 break; 531 } 532 } 533 if (!enabled) { 534 // if we are not enabled, go on to the next pending entry 535 continue; 536 } 537 538 // The new add_on should be enabled, but first we check to see 539 // if there is an add_on below us. If we find one, we disable it. 540 bool shadowing = false; 541 for (diter++ ; diter != directories.end() ; diter++) { 542 std::list<add_on_entry_info>::iterator eiter = diter->entries.begin(); 543 for( ; eiter != diter->entries.end() ; eiter++) { 544 if (strcmp(eiter->name, info.name) == 0) { 545 AddOnDisabled(&*eiter); 546 shadowing = true; 547 break; 548 } 549 } 550 if (shadowing) { 551 break; 552 } 553 } 554 555 // finally, enable the new entry 556 AddOnEnabled(&info); 557 } 558 } 559