1// 2// This file is part of the aMule Project. 3// 4// Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org ) 5// 6// Any parts of this program derived from the xMule, lMule or eMule project, 7// or contributed by third-party developers are copyrighted by their 8// respective authors. 9// 10// This program is free software; you can redistribute it and/or modify 11// it under the terms of the GNU General Public License as published by 12// the Free Software Foundation; either version 2 of the License, or 13// (at your option) any later version. 14// 15// This program is distributed in the hope that it will be useful, 16// but WITHOUT ANY WARRANTY; without even the implied warranty of 17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18// GNU General Public License for more details. 19// 20// You should have received a copy of the GNU General Public License 21// along with this program; if not, write to the Free Software 22// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 23// 24 25 26#include "amule.h" // Interface declarations. 27 28#include <include/common/EventIDs.h> 29 30#ifdef HAVE_CONFIG_H 31 #include "config.h" // Needed for HAVE_SYS_RESOURCE_H, etc 32#endif 33 34// Include the necessary headers for select(2), properly guarded 35#if defined HAVE_SYS_SELECT_H && !defined __IRIX__ 36# include <sys/select.h> 37#else 38# ifdef HAVE_SYS_TIME_H 39# include <sys/time.h> 40# endif 41# ifdef HAVE_SYS_TYPES_H 42# include <sys/types.h> 43# endif 44# ifdef HAVE_UNISTD_H 45# include <unistd.h> 46# endif 47#endif 48 49#include <wx/utils.h> 50 51#include "Preferences.h" // Needed for CPreferences 52#include "PartFile.h" // Needed for CPartFile 53#include "Logger.h" 54#include <common/Format.h> 55#include "InternalEvents.h" // Needed for wxEVT_* 56#include "ThreadTasks.h" 57#include "GuiEvents.h" // Needed for EVT_MULE_NOTIFY 58#include "Timer.h" // Needed for EVT_MULE_TIMER 59 60#include "ClientUDPSocket.h" // Do_not_auto_remove (forward declaration not enough) 61#include "ListenSocket.h" // Do_not_auto_remove (forward declaration not enough) 62 63 64#ifdef HAVE_SYS_RESOURCE_H 65#include <sys/resource.h> // Do_not_auto_remove 66#endif 67 68#ifndef __WXMSW__ 69 #ifdef HAVE_SYS_WAIT_H 70 #include <sys/wait.h> // Do_not_auto_remove 71 #endif 72 73 #include <wx/unix/execute.h> 74#endif 75 76BEGIN_EVENT_TABLE(CamuleDaemonApp, wxAppConsole) 77 // 78 // Socket handlers 79 // 80 81 // Listen Socket 82 EVT_SOCKET(ID_LISTENSOCKET_EVENT, CamuleDaemonApp::ListenSocketHandler) 83 84 // UDP Socket (servers) 85 EVT_SOCKET(ID_SERVERUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler) 86 // UDP Socket (clients) 87 EVT_SOCKET(ID_CLIENTUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler) 88 89 // Socket timer (TCP) 90 EVT_MULE_TIMER(ID_SERVER_RETRY_TIMER_EVENT, CamuleDaemonApp::OnTCPTimer) 91 92 // Core timer 93 EVT_MULE_TIMER(ID_CORE_TIMER_EVENT, CamuleDaemonApp::OnCoreTimer) 94 95 EVT_MULE_NOTIFY(CamuleDaemonApp::OnNotifyEvent) 96 97 // Async dns handling 98 EVT_MULE_INTERNAL(wxEVT_CORE_UDP_DNS_DONE, -1, CamuleDaemonApp::OnUDPDnsDone) 99 100 EVT_MULE_INTERNAL(wxEVT_CORE_SOURCE_DNS_DONE, -1, CamuleDaemonApp::OnSourceDnsDone) 101 102 EVT_MULE_INTERNAL(wxEVT_CORE_SERVER_DNS_DONE, -1, CamuleDaemonApp::OnServerDnsDone) 103 104 // Hash ended notifier 105 EVT_MULE_HASHING(CamuleDaemonApp::OnFinishedHashing) 106 EVT_MULE_AICH_HASHING(CamuleDaemonApp::OnFinishedAICHHashing) 107 108 // File completion ended notifier 109 EVT_MULE_FILE_COMPLETED(CamuleDaemonApp::OnFinishedCompletion) 110 111 // HTTPDownload finished 112 EVT_MULE_INTERNAL(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD, -1, CamuleDaemonApp::OnFinishedHTTPDownload) 113 114 // Disk space preallocation finished 115 EVT_MULE_ALLOC_FINISHED(CamuleDaemonApp::OnFinishedAllocation) 116END_EVENT_TABLE() 117 118IMPLEMENT_APP(CamuleDaemonApp) 119 120#ifdef AMULED28 121/* 122 * Socket handling in wxBase 123 * 124 */ 125class CSocketSet { 126 int m_count; 127 int m_fds[FD_SETSIZE], m_fd_idx[FD_SETSIZE]; 128 GSocket *m_gsocks[FD_SETSIZE]; 129 130 fd_set m_set; 131 public: 132 CSocketSet(); 133 void AddSocket(GSocket *); 134 void RemoveSocket(GSocket *); 135 void FillSet(int &max_fd); 136 137 void Detected(void (GSocket::*func)()); 138 139 fd_set *Set() { return &m_set; } 140}; 141 142CSocketSet::CSocketSet() 143{ 144 m_count = 0; 145 for(int i = 0; i < FD_SETSIZE; i++) { 146 m_fds[i] = 0; 147 m_fd_idx[i] = 0xffff; 148 m_gsocks[i] = 0; 149 } 150} 151 152void CSocketSet::AddSocket(GSocket *socket) 153{ 154 wxASSERT(socket); 155 156 int fd = socket->m_fd; 157 158 if ( fd == -1 ) { 159 return; 160 } 161 162 wxASSERT( (fd > 2) && (fd < FD_SETSIZE) ); 163 164 if ( m_gsocks[fd] ) { 165 return; 166 } 167 m_fds[m_count] = fd; 168 m_fd_idx[fd] = m_count; 169 m_gsocks[fd] = socket; 170 m_count++; 171} 172 173void CSocketSet::RemoveSocket(GSocket *socket) 174{ 175 wxASSERT(socket); 176 177 int fd = socket->m_fd; 178 179 if ( fd == -1 ) { 180 return; 181 } 182 183 wxASSERT( (fd > 2) && (fd < FD_SETSIZE) ); 184 185 int i = m_fd_idx[fd]; 186 if ( i == 0xffff ) { 187 return; 188 } 189 wxASSERT(m_fds[i] == fd); 190 m_fds[i] = m_fds[m_count-1]; 191 m_gsocks[fd] = 0; 192 m_fds[m_count-1] = 0; 193 m_fd_idx[fd] = 0xffff; 194 m_fd_idx[m_fds[i]] = i; 195 m_count--; 196} 197 198void CSocketSet::FillSet(int &max_fd) 199{ 200 FD_ZERO(&m_set); 201 202 for(int i = 0; i < m_count; i++) { 203 FD_SET(m_fds[i], &m_set); 204 if ( m_fds[i] > max_fd ) { 205 max_fd = m_fds[i]; 206 } 207 } 208} 209 210void CSocketSet::Detected(void (GSocket::*func)()) 211{ 212 for (int i = 0; i < m_count; i++) { 213 int fd = m_fds[i]; 214 if ( FD_ISSET(fd, &m_set) ) { 215 GSocket *socket = m_gsocks[fd]; 216 (*socket.*func)(); 217 } 218 } 219} 220 221CAmuledGSocketFuncTable::CAmuledGSocketFuncTable() : m_lock(wxMUTEX_RECURSIVE) 222{ 223 m_in_set = new CSocketSet; 224 m_out_set = new CSocketSet; 225 226 m_lock.Unlock(); 227} 228 229void CAmuledGSocketFuncTable::AddSocket(GSocket *socket, GSocketEvent event) 230{ 231 wxMutexLocker lock(m_lock); 232 233 if ( event == GSOCK_INPUT ) { 234 m_in_set->AddSocket(socket); 235 } else { 236 m_out_set->AddSocket(socket); 237 } 238} 239 240void CAmuledGSocketFuncTable::RemoveSocket(GSocket *socket, GSocketEvent event) 241{ 242 wxMutexLocker lock(m_lock); 243 244 if ( event == GSOCK_INPUT ) { 245 m_in_set->RemoveSocket(socket); 246 } else { 247 m_out_set->RemoveSocket(socket); 248 } 249} 250 251void CAmuledGSocketFuncTable::RunSelect() 252{ 253 wxMutexLocker lock(m_lock); 254 255 int max_fd = -1; 256 m_in_set->FillSet(max_fd); 257 m_out_set->FillSet(max_fd); 258 259 struct timeval tv; 260 tv.tv_sec = 0; 261 tv.tv_usec = 10000; // 10ms 262 263 int result = select(max_fd + 1, m_in_set->Set(), m_out_set->Set(), 0, &tv); 264 if ( result > 0 ) { 265 m_in_set->Detected(&GSocket::Detected_Read); 266 m_out_set->Detected(&GSocket::Detected_Write); 267 } 268} 269 270GSocketGUIFunctionsTable *CDaemonAppTraits::GetSocketGUIFunctionsTable() 271{ 272 return m_table; 273} 274 275bool CAmuledGSocketFuncTable::OnInit() 276{ 277 return true; 278} 279 280void CAmuledGSocketFuncTable::OnExit() 281{ 282} 283 284bool CAmuledGSocketFuncTable::CanUseEventLoop() 285{ 286 /* 287 * FIXME: (lfroen) Not sure whether it's right. 288 * I will review it later. 289 */ 290 return false; 291} 292 293bool CAmuledGSocketFuncTable::Init_Socket(GSocket *) 294{ 295 return true; 296} 297 298void CAmuledGSocketFuncTable::Destroy_Socket(GSocket *) 299{ 300} 301 302void CAmuledGSocketFuncTable::Install_Callback(GSocket *sock, GSocketEvent e) 303{ 304 AddSocket(sock, e); 305} 306 307void CAmuledGSocketFuncTable::Uninstall_Callback(GSocket *sock, GSocketEvent e) 308{ 309 RemoveSocket(sock, e); 310} 311 312void CAmuledGSocketFuncTable::Enable_Events(GSocket *socket) 313{ 314 Install_Callback(socket, GSOCK_INPUT); 315 Install_Callback(socket, GSOCK_OUTPUT); 316} 317 318void CAmuledGSocketFuncTable::Disable_Events(GSocket *socket) 319{ 320 Uninstall_Callback(socket, GSOCK_INPUT); 321 Uninstall_Callback(socket, GSOCK_OUTPUT); 322} 323 324#endif // AMULED28 325 326#ifndef __WXMSW__ 327 328#ifdef AMULED28 329 330CDaemonAppTraits::CDaemonAppTraits(CAmuledGSocketFuncTable *table) 331: 332wxConsoleAppTraits(), 333m_oldSignalChildAction(), 334m_newSignalChildAction(), 335m_table(table), 336m_lock(wxMUTEX_RECURSIVE), 337m_sched_delete() 338{ 339 m_lock.Unlock(); 340} 341 342 343void CDaemonAppTraits::ScheduleForDestroy(wxObject *object) 344{ 345 wxMutexLocker lock(m_lock); 346 347 //delete object; 348 m_sched_delete.push_back(object); 349} 350 351void CDaemonAppTraits::RemoveFromPendingDelete(wxObject *object) 352{ 353 wxMutexLocker lock(m_lock); 354 355 for(std::list<wxObject *>::iterator i = m_sched_delete.begin(); 356 i != m_sched_delete.end(); i++) { 357 if ( *i == object ) { 358 m_sched_delete.erase(i); 359 return; 360 } 361 } 362} 363 364void CDaemonAppTraits::DeletePending() 365{ 366 wxMutexLocker lock(m_lock); 367 368 while ( !m_sched_delete.empty() ) { 369 std::list<wxObject *>::iterator i = m_sched_delete.begin(); 370 wxObject *object = *i; 371 delete object; 372 } 373 //m_sched_delete.erase(m_sched_delete.begin(), m_sched_delete.end()); 374} 375 376wxAppTraits *CamuleDaemonApp::CreateTraits() 377{ 378 return new CDaemonAppTraits(m_table); 379} 380 381#else // AMULED28 382 383CDaemonAppTraits::CDaemonAppTraits() 384: 385wxConsoleAppTraits(), 386m_oldSignalChildAction(), 387m_newSignalChildAction() 388{ 389} 390 391wxAppTraits *CamuleDaemonApp::CreateTraits() 392{ 393 return new CDaemonAppTraits(); 394} 395 396#endif // !AMULED28 397 398#endif // __WXMSW__ 399 400#if defined(__WXMAC__) && !wxCHECK_VERSION(2, 9, 0) 401#include <wx/stdpaths.h> // Do_not_auto_remove (guess) 402static wxStandardPathsCF gs_stdPaths; 403wxStandardPathsBase& CDaemonAppTraits::GetStandardPaths() 404{ 405 return gs_stdPaths; 406} 407#endif 408 409 410#ifdef AMULED28 411 412CamuleDaemonApp::CamuleDaemonApp() 413: 414m_Exit(false), 415m_table(new CAmuledGSocketFuncTable()) 416{ 417 wxPendingEventsLocker = new wxCriticalSection; 418} 419 420#endif // !AMULED28 421 422 423#ifndef __WXMSW__ 424 425 426static EndProcessDataMap endProcDataMap; 427 428 429int CDaemonAppTraits::WaitForChild(wxExecuteData &execData) 430{ 431 int status = 0; 432 pid_t result = 0; 433 // Build the log message 434 wxString msg; 435 msg << wxT("WaitForChild() has been called for child process with pid `") << 436 execData.pid << 437 wxT("'. "); 438 439 if (execData.flags & wxEXEC_SYNC) { 440 result = AmuleWaitPid(execData.pid, &status, 0, &msg); 441 if (result == -1 || (!WIFEXITED(status) && !WIFSIGNALED(status))) { 442 msg << wxT(" Waiting for subprocess termination failed."); 443 AddDebugLogLineN(logGeneral, msg); 444 } 445 } else { 446 /** wxEXEC_ASYNC */ 447 // Give the process a chance to start or forked child to exit 448 // 1 second is enough time to fail on "path not found" 449 wxSleep(1); 450 result = AmuleWaitPid(execData.pid, &status, WNOHANG, &msg); 451 if (result == 0) { 452 // Add a WxEndProcessData entry to the map, so that we can 453 // support process termination 454 wxEndProcessData *endProcData = new wxEndProcessData(); 455 endProcData->pid = execData.pid; 456 endProcData->process = execData.process; 457 endProcData->tag = 0; 458 endProcDataMap[execData.pid] = endProcData; 459 460 status = execData.pid; 461 } else { 462 // if result != 0, then either waitpid() failed (result == -1) 463 // and there is nothing we can do, or the child has changed 464 // status, which means it is probably dead. 465 status = 0; 466 } 467 } 468 469 // Log our passage here 470 AddDebugLogLineN(logGeneral, msg); 471 472 return status; 473} 474 475 476void OnSignalChildHandler(int /*signal*/, siginfo_t *siginfo, void * /*ucontext*/) 477{ 478 // Build the log message 479 wxString msg; 480 msg << wxT("OnSignalChildHandler() has been called for child process with pid `") << 481 siginfo->si_pid << 482 wxT("'. "); 483 // Make sure we leave no zombies by calling waitpid() 484 int status = 0; 485 pid_t result = AmuleWaitPid(siginfo->si_pid, &status, WNOHANG, &msg); 486 if (result != 1 && result != 0 && (WIFEXITED(status) || WIFSIGNALED(status))) { 487 // Fetch the wxEndProcessData structure corresponding to this pid 488 EndProcessDataMap::iterator it = endProcDataMap.find(siginfo->si_pid); 489 if (it != endProcDataMap.end()) { 490 wxEndProcessData *endProcData = it->second; 491 // Remove this entry from the process map 492 endProcDataMap.erase(siginfo->si_pid); 493 // Save the exit code for the wxProcess object to read later 494 endProcData->exitcode = result != -1 && WIFEXITED(status) ? 495 WEXITSTATUS(status) : -1; 496 // Make things work as in wxGUI 497 wxHandleProcessTermination(endProcData); 498 499 // wxHandleProcessTermination() will "delete endProcData;" 500 // So we do not delete it again, ok? Do not uncomment this line. 501 //delete endProcData; 502 } else { 503 msg << wxT(" Error: the child process pid is not on the pid map."); 504 } 505 } 506 507 // Log our passage here 508 AddDebugLogLineN(logGeneral, msg); 509} 510 511 512pid_t AmuleWaitPid(pid_t pid, int *status, int options, wxString *msg) 513{ 514 *status = 0; 515 pid_t result = waitpid(pid, status, options); 516 if (result == -1) { 517 *msg << CFormat(wxT("Error: waitpid() call failed: %m.")); 518 } else if (result == 0) { 519 if (options & WNOHANG) { 520 *msg << wxT("The child is alive."); 521 } else { 522 *msg << wxT("Error: waitpid() call returned 0 but " 523 "WNOHANG was not specified in options."); 524 } 525 } else { 526 if (WIFEXITED(*status)) { 527 *msg << wxT("Child has terminated with status code `") << 528 WEXITSTATUS(*status) << 529 wxT("'."); 530 } else if (WIFSIGNALED(*status)) { 531 *msg << wxT("Child was killed by signal `") << 532 WTERMSIG(*status) << 533 wxT("'."); 534 if (WCOREDUMP(*status)) { 535 *msg << wxT(" A core file has been dumped."); 536 } 537 } else if (WIFSTOPPED(*status)) { 538 *msg << wxT("Child has been stopped by signal `") << 539 WSTOPSIG(*status) << 540 wxT("'."); 541#ifdef WIFCONTINUED /* Only found in recent kernels. */ 542 } else if (WIFCONTINUED(*status)) { 543 *msg << wxT("Child has received `SIGCONT' and has continued execution."); 544#endif 545 } else { 546 *msg << wxT("The program was not able to determine why the child has signaled."); 547 } 548 } 549 550 return result; 551} 552 553 554#endif // __WXMSW__ 555 556 557int CamuleDaemonApp::OnRun() 558{ 559 if (!thePrefs::AcceptExternalConnections()) { 560 AddLogLineCS(_("ERROR: aMule daemon cannot be used when external connections are disabled. To enable External Connections, use either a normal aMule, start amuled with the option --ec-config or set the key \"AcceptExternalConnections\" to 1 in the file ~/.aMule/amule.conf")); 561 return 0; 562 } else if (thePrefs::ECPassword().IsEmpty()) { 563 AddLogLineCS(_("ERROR: A valid password is required to use external connections, and aMule daemon cannot be used without external connections. To run aMule deamon, you must set the \"ECPassword\" field in the file ~/.aMule/amule.conf with an appropriate value. Execute amuled with the flag --ec-config to set the password. More information can be found at http://wiki.amule.org")); 564 return 0; 565 } 566 567#ifndef __WXMSW__ 568 // Process the return code of dead children so that we do not create 569 // zombies. wxBase does not implement wxProcess callbacks, so no one 570 // actualy calls wxHandleProcessTermination() in console applications. 571 // We do our best here. 572 int ret = 0; 573 ret = sigaction(SIGCHLD, NULL, &m_oldSignalChildAction); 574 m_newSignalChildAction = m_oldSignalChildAction; 575 m_newSignalChildAction.sa_sigaction = OnSignalChildHandler; 576 m_newSignalChildAction.sa_flags |= SA_SIGINFO; 577 m_newSignalChildAction.sa_flags &= ~SA_RESETHAND; 578 ret = sigaction(SIGCHLD, &m_newSignalChildAction, NULL); 579 if (ret == -1) { 580 AddDebugLogLineC(logStandard, CFormat(wxT("CamuleDaemonApp::OnRun(): Installation of SIGCHLD callback with sigaction() failed: %m."))); 581 } else { 582 AddDebugLogLineN(logGeneral, wxT("CamuleDaemonApp::OnRun(): Installation of SIGCHLD callback with sigaction() succeeded.")); 583 } 584#endif // __WXMSW__ 585 586#ifdef AMULED28 587 588 while ( !m_Exit ) { 589 m_table->RunSelect(); 590 ProcessPendingEvents(); 591 ((CDaemonAppTraits *)GetTraits())->DeletePending(); 592 } 593 594 // ShutDown is beeing called twice. Once here and again in OnExit(). 595 ShutDown(); 596 597 return 0; 598 599#else 600 601#ifdef AMULED_DUMMY 602 return 0; 603#else 604 return wxApp::OnRun(); 605#endif 606 607#endif 608} 609 610bool CamuleDaemonApp::OnInit() 611{ 612 if ( !CamuleApp::OnInit() ) { 613 return false; 614 } 615 AddLogLineNS(_("amuled: OnInit - starting timer")); 616 core_timer = new CTimer(this,ID_CORE_TIMER_EVENT); 617 core_timer->Start(CORE_TIMER_PERIOD); 618 glob_prefs->GetCategory(0)->title = GetCatTitle(thePrefs::GetAllcatFilter()); 619 glob_prefs->GetCategory(0)->path = thePrefs::GetIncomingDir(); 620 621 return true; 622} 623 624int CamuleDaemonApp::InitGui(bool ,wxString &) 625{ 626#ifndef __WXMSW__ 627 if ( !enable_daemon_fork ) { 628 return 0; 629 } 630 AddLogLineNS(_("amuled: forking to background - see you")); 631 theLogger.SetEnabledStdoutLog(false); 632 // 633 // fork to background and detach from controlling tty 634 // while redirecting stdout to /dev/null 635 // 636 for(int i_fd = 0;i_fd < 3; i_fd++) { 637 close(i_fd); 638 } 639 int fd = open("/dev/null",O_RDWR); 640 if (dup(fd)){} // prevent GCC warning 641 if (dup(fd)){} 642 pid_t pid = fork(); 643 644 wxASSERT(pid != -1); 645 646 if ( pid ) { 647 exit(0); 648 } else { 649 pid = setsid(); 650 // 651 // Create a Pid file with the Pid of the Child, so any daemon-manager 652 // can easily manage the process 653 // 654 if (!m_PidFile.IsEmpty()) { 655 wxString temp = CFormat(wxT("%d\n")) % pid; 656 wxFFile ff(m_PidFile, wxT("w")); 657 if (!ff.Error()) { 658 ff.Write(temp); 659 ff.Close(); 660 } else { 661 AddLogLineNS(_("Cannot Create Pid File")); 662 } 663 } 664 } 665 666#endif 667 return 0; 668} 669 670 671int CamuleDaemonApp::OnExit() 672{ 673#ifdef AMULED28 674 /* 675 * Stop all socket threads before entering 676 * shutdown sequence. 677 */ 678 delete listensocket; 679 listensocket = 0; 680 if (clientudp) { 681 delete clientudp; 682 clientudp = NULL; 683 } 684#endif 685 686 ShutDown(); 687 688#ifndef __WXMSW__ 689 int ret = sigaction(SIGCHLD, &m_oldSignalChildAction, NULL); 690 if (ret == -1) { 691 AddDebugLogLineC(logStandard, CFormat(wxT("CamuleDaemonApp::OnRun(): second sigaction() failed: %m."))); 692 } else { 693 AddDebugLogLineN(logGeneral, wxT("CamuleDaemonApp::OnRun(): Uninstallation of SIGCHLD callback with sigaction() succeeded.")); 694 } 695#endif // __WXMSW__ 696 697 // lfroen: delete socket threads 698 if (ECServerHandler) { 699 ECServerHandler = 0; 700 } 701 702 delete core_timer; 703 704 return CamuleApp::OnExit(); 705} 706 707 708int CamuleDaemonApp::ShowAlert(wxString msg, wxString title, int flags) 709{ 710 if ( flags | wxICON_ERROR ) { 711 title = CFormat(_("ERROR: %s")) % title; 712 } 713 AddLogLineCS(title + wxT(" ") + msg); 714 715 return 0; // That's neither yes nor no, ok, cancel 716} 717 718// File_checked_for_headers 719