Whitespace
[amule.git] / src / amuled.cpp
blob486da5973f15f7e180d97c6d8d255d6ba63b5e94
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.
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.
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
26 #include "amule.h" // Interface declarations.
28 #include <include/common/EventIDs.h>
30 #ifdef HAVE_CONFIG_H
31 #include "config.h" // Needed for HAVE_SYS_RESOURCE_H, etc
32 #endif
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
49 #include <wx/utils.h>
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
60 #include "ClientUDPSocket.h" // Do_not_auto_remove (forward declaration not enough)
61 #include "ListenSocket.h" // Do_not_auto_remove (forward declaration not enough)
64 #ifdef HAVE_SYS_RESOURCE_H
65 #include <sys/resource.h> // Do_not_auto_remove
66 #endif
68 #ifndef __WINDOWS__
69 #ifdef HAVE_SYS_WAIT_H
70 #include <sys/wait.h> // Do_not_auto_remove
71 #endif
72 #include <wx/ffile.h>
73 #endif
75 #ifdef AMULED_APPTRAITS
76 #include <wx/unix/execute.h>
77 #endif
80 BEGIN_EVENT_TABLE(CamuleDaemonApp, wxAppConsole)
82 #ifndef ASIO_SOCKETS
84 // Socket handlers
87 // Listen Socket
88 EVT_SOCKET(ID_LISTENSOCKET_EVENT, CamuleDaemonApp::ListenSocketHandler)
90 // UDP Socket (servers)
91 EVT_SOCKET(ID_SERVERUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
92 // UDP Socket (clients)
93 EVT_SOCKET(ID_CLIENTUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
94 #endif
96 // Socket timer (TCP)
97 EVT_MULE_TIMER(ID_SERVER_RETRY_TIMER_EVENT, CamuleDaemonApp::OnTCPTimer)
99 // Core timer
100 EVT_MULE_TIMER(ID_CORE_TIMER_EVENT, CamuleDaemonApp::OnCoreTimer)
102 EVT_MULE_NOTIFY(CamuleDaemonApp::OnNotifyEvent)
104 // Async dns handling
105 EVT_MULE_INTERNAL(wxEVT_CORE_UDP_DNS_DONE, -1, CamuleDaemonApp::OnUDPDnsDone)
107 EVT_MULE_INTERNAL(wxEVT_CORE_SOURCE_DNS_DONE, -1, CamuleDaemonApp::OnSourceDnsDone)
109 EVT_MULE_INTERNAL(wxEVT_CORE_SERVER_DNS_DONE, -1, CamuleDaemonApp::OnServerDnsDone)
111 // Hash ended notifier
112 EVT_MULE_HASHING(CamuleDaemonApp::OnFinishedHashing)
113 EVT_MULE_AICH_HASHING(CamuleDaemonApp::OnFinishedAICHHashing)
115 // File completion ended notifier
116 EVT_MULE_FILE_COMPLETED(CamuleDaemonApp::OnFinishedCompletion)
118 // HTTPDownload finished
119 EVT_MULE_INTERNAL(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD, -1, CamuleDaemonApp::OnFinishedHTTPDownload)
121 // Disk space preallocation finished
122 EVT_MULE_ALLOC_FINISHED(CamuleDaemonApp::OnFinishedAllocation)
123 END_EVENT_TABLE()
125 IMPLEMENT_APP(CamuleDaemonApp)
127 #ifdef AMULED28_SOCKETS
129 * Socket handling in wxBase
132 class CSocketSet {
133 int m_count;
134 int m_fds[FD_SETSIZE], m_fd_idx[FD_SETSIZE];
135 GSocket *m_gsocks[FD_SETSIZE];
137 fd_set m_set;
138 public:
139 CSocketSet();
140 void AddSocket(GSocket *);
141 void RemoveSocket(GSocket *);
142 void FillSet(int &max_fd);
144 void Detected(void (GSocket::*func)());
146 fd_set *Set() { return &m_set; }
149 CSocketSet::CSocketSet()
151 m_count = 0;
152 for(int i = 0; i < FD_SETSIZE; i++) {
153 m_fds[i] = 0;
154 m_fd_idx[i] = 0xffff;
155 m_gsocks[i] = 0;
159 void CSocketSet::AddSocket(GSocket *socket)
161 wxASSERT(socket);
163 int fd = socket->m_fd;
165 if ( fd == -1 ) {
166 return;
169 wxASSERT( (fd > 2) && (fd < FD_SETSIZE) );
171 if ( m_gsocks[fd] ) {
172 return;
174 m_fds[m_count] = fd;
175 m_fd_idx[fd] = m_count;
176 m_gsocks[fd] = socket;
177 m_count++;
180 void CSocketSet::RemoveSocket(GSocket *socket)
182 wxASSERT(socket);
184 int fd = socket->m_fd;
186 if ( fd == -1 ) {
187 return;
190 wxASSERT( (fd > 2) && (fd < FD_SETSIZE) );
192 int i = m_fd_idx[fd];
193 if ( i == 0xffff ) {
194 return;
196 wxASSERT(m_fds[i] == fd);
197 m_fds[i] = m_fds[m_count-1];
198 m_gsocks[fd] = 0;
199 m_fds[m_count-1] = 0;
200 m_fd_idx[fd] = 0xffff;
201 m_fd_idx[m_fds[i]] = i;
202 m_count--;
205 void CSocketSet::FillSet(int &max_fd)
207 FD_ZERO(&m_set);
209 for(int i = 0; i < m_count; i++) {
210 FD_SET(m_fds[i], &m_set);
211 if ( m_fds[i] > max_fd ) {
212 max_fd = m_fds[i];
217 void CSocketSet::Detected(void (GSocket::*func)())
219 for (int i = 0; i < m_count; i++) {
220 int fd = m_fds[i];
221 if ( FD_ISSET(fd, &m_set) ) {
222 GSocket *socket = m_gsocks[fd];
223 (*socket.*func)();
228 CAmuledGSocketFuncTable::CAmuledGSocketFuncTable() : m_lock(wxMUTEX_RECURSIVE)
230 m_in_set = new CSocketSet;
231 m_out_set = new CSocketSet;
233 m_lock.Unlock();
236 void CAmuledGSocketFuncTable::AddSocket(GSocket *socket, GSocketEvent event)
238 wxMutexLocker lock(m_lock);
240 if ( event == GSOCK_INPUT ) {
241 m_in_set->AddSocket(socket);
242 } else {
243 m_out_set->AddSocket(socket);
247 void CAmuledGSocketFuncTable::RemoveSocket(GSocket *socket, GSocketEvent event)
249 wxMutexLocker lock(m_lock);
251 if ( event == GSOCK_INPUT ) {
252 m_in_set->RemoveSocket(socket);
253 } else {
254 m_out_set->RemoveSocket(socket);
258 void CAmuledGSocketFuncTable::RunSelect()
260 wxMutexLocker lock(m_lock);
262 int max_fd = -1;
263 m_in_set->FillSet(max_fd);
264 m_out_set->FillSet(max_fd);
266 struct timeval tv;
267 tv.tv_sec = 0;
268 tv.tv_usec = 10000; // 10ms
270 int result = select(max_fd + 1, m_in_set->Set(), m_out_set->Set(), 0, &tv);
271 if ( result > 0 ) {
272 m_in_set->Detected(&GSocket::Detected_Read);
273 m_out_set->Detected(&GSocket::Detected_Write);
277 GSocketGUIFunctionsTable *CDaemonAppTraits::GetSocketGUIFunctionsTable()
279 return m_table;
282 bool CAmuledGSocketFuncTable::OnInit()
284 return true;
287 void CAmuledGSocketFuncTable::OnExit()
291 bool CAmuledGSocketFuncTable::CanUseEventLoop()
294 * FIXME: (lfroen) Not sure whether it's right.
295 * I will review it later.
297 return false;
300 bool CAmuledGSocketFuncTable::Init_Socket(GSocket *)
302 return true;
305 void CAmuledGSocketFuncTable::Destroy_Socket(GSocket *)
309 void CAmuledGSocketFuncTable::Install_Callback(GSocket *sock, GSocketEvent e)
311 AddSocket(sock, e);
314 void CAmuledGSocketFuncTable::Uninstall_Callback(GSocket *sock, GSocketEvent e)
316 RemoveSocket(sock, e);
319 void CAmuledGSocketFuncTable::Enable_Events(GSocket *socket)
321 Install_Callback(socket, GSOCK_INPUT);
322 Install_Callback(socket, GSOCK_OUTPUT);
325 void CAmuledGSocketFuncTable::Disable_Events(GSocket *socket)
327 Uninstall_Callback(socket, GSOCK_INPUT);
328 Uninstall_Callback(socket, GSOCK_OUTPUT);
332 CDaemonAppTraits::CDaemonAppTraits(CAmuledGSocketFuncTable *table)
334 wxConsoleAppTraits(),
335 m_oldSignalChildAction(),
336 m_newSignalChildAction(),
337 m_table(table),
338 m_lock(wxMUTEX_RECURSIVE),
339 m_sched_delete()
341 m_lock.Unlock();
345 void CDaemonAppTraits::ScheduleForDestroy(wxObject *object)
347 wxMutexLocker lock(m_lock);
349 //delete object;
350 m_sched_delete.push_back(object);
353 void CDaemonAppTraits::RemoveFromPendingDelete(wxObject *object)
355 wxMutexLocker lock(m_lock);
357 for(std::list<wxObject *>::iterator i = m_sched_delete.begin();
358 i != m_sched_delete.end(); i++) {
359 if ( *i == object ) {
360 m_sched_delete.erase(i);
361 return;
366 void CDaemonAppTraits::DeletePending()
368 wxMutexLocker lock(m_lock);
370 while ( !m_sched_delete.empty() ) {
371 std::list<wxObject *>::iterator i = m_sched_delete.begin();
372 wxObject *object = *i;
373 delete object;
375 //m_sched_delete.erase(m_sched_delete.begin(), m_sched_delete.end());
378 wxAppTraits *CamuleDaemonApp::CreateTraits()
380 return new CDaemonAppTraits(m_table);
383 #else // AMULED28_SOCKETS
385 #ifdef AMULED_APPTRAITS
387 CDaemonAppTraits::CDaemonAppTraits()
389 wxConsoleAppTraits(),
390 m_oldSignalChildAction(),
391 m_newSignalChildAction()
395 wxAppTraits *CamuleDaemonApp::CreateTraits()
397 return new CDaemonAppTraits();
400 #endif // AMULED_APPTRAITS
402 #endif // !AMULED28_SOCKETS
404 #if defined(__WXMAC__) && !wxCHECK_VERSION(2, 9, 0)
405 #include <wx/stdpaths.h> // Do_not_auto_remove (guess)
406 static wxStandardPathsCF gs_stdPaths;
407 wxStandardPathsBase& CDaemonAppTraits::GetStandardPaths()
409 return gs_stdPaths;
411 #endif
414 #ifdef AMULED28_EVENTLOOP
416 CamuleDaemonApp::CamuleDaemonApp()
418 m_Exit(false)
419 #ifdef AMULED28_SOCKETS
420 ,m_table(new CAmuledGSocketFuncTable())
421 #endif
423 // work around problem from http://trac.wxwidgets.org/ticket/2145
424 wxPendingEventsLocker = new wxCriticalSection;
427 #endif // AMULED28_EVENTLOOP
430 #ifdef AMULED_APPTRAITS
432 static EndProcessDataMap endProcDataMap;
434 int CDaemonAppTraits::WaitForChild(wxExecuteData &execData)
436 int status = 0;
437 pid_t result = 0;
438 // Build the log message
439 wxString msg;
440 msg << wxT("WaitForChild() has been called for child process with pid `") <<
441 execData.pid <<
442 wxT("'. ");
444 if (execData.flags & wxEXEC_SYNC) {
445 result = AmuleWaitPid(execData.pid, &status, 0, &msg);
446 if (result == -1 || (!WIFEXITED(status) && !WIFSIGNALED(status))) {
447 msg << wxT(" Waiting for subprocess termination failed.");
448 AddDebugLogLineN(logGeneral, msg);
450 } else {
451 /** wxEXEC_ASYNC */
452 // Give the process a chance to start or forked child to exit
453 // 1 second is enough time to fail on "path not found"
454 wxSleep(1);
455 result = AmuleWaitPid(execData.pid, &status, WNOHANG, &msg);
456 if (result == 0) {
457 // Add a WxEndProcessData entry to the map, so that we can
458 // support process termination
459 wxEndProcessData *endProcData = new wxEndProcessData();
460 endProcData->pid = execData.pid;
461 endProcData->process = execData.process;
462 endProcData->tag = 0;
463 endProcDataMap[execData.pid] = endProcData;
465 status = execData.pid;
466 } else {
467 // if result != 0, then either waitpid() failed (result == -1)
468 // and there is nothing we can do, or the child has changed
469 // status, which means it is probably dead.
470 status = 0;
474 // Log our passage here
475 AddDebugLogLineN(logGeneral, msg);
477 return status;
481 void OnSignalChildHandler(int /*signal*/, siginfo_t *siginfo, void * /*ucontext*/)
483 // Build the log message
484 wxString msg;
485 msg << wxT("OnSignalChildHandler() has been called for child process with pid `") <<
486 siginfo->si_pid <<
487 wxT("'. ");
488 // Make sure we leave no zombies by calling waitpid()
489 int status = 0;
490 pid_t result = AmuleWaitPid(siginfo->si_pid, &status, WNOHANG, &msg);
491 if (result != 1 && result != 0 && (WIFEXITED(status) || WIFSIGNALED(status))) {
492 // Fetch the wxEndProcessData structure corresponding to this pid
493 EndProcessDataMap::iterator it = endProcDataMap.find(siginfo->si_pid);
494 if (it != endProcDataMap.end()) {
495 wxEndProcessData *endProcData = it->second;
496 // Remove this entry from the process map
497 endProcDataMap.erase(siginfo->si_pid);
498 // Save the exit code for the wxProcess object to read later
499 endProcData->exitcode = result != -1 && WIFEXITED(status) ?
500 WEXITSTATUS(status) : -1;
501 // Make things work as in wxGUI
502 wxHandleProcessTermination(endProcData);
504 // wxHandleProcessTermination() will "delete endProcData;"
505 // So we do not delete it again, ok? Do not uncomment this line.
506 //delete endProcData;
507 } else {
508 msg << wxT(" Error: the child process pid is not on the pid map.");
512 // Log our passage here
513 AddDebugLogLineN(logGeneral, msg);
517 pid_t AmuleWaitPid(pid_t pid, int *status, int options, wxString *msg)
519 *status = 0;
520 pid_t result = waitpid(pid, status, options);
521 if (result == -1) {
522 *msg << CFormat(wxT("Error: waitpid() call failed: %m."));
523 } else if (result == 0) {
524 if (options & WNOHANG) {
525 *msg << wxT("The child is alive.");
526 } else {
527 *msg << wxT("Error: waitpid() call returned 0 but "
528 "WNOHANG was not specified in options.");
530 } else {
531 if (WIFEXITED(*status)) {
532 *msg << wxT("Child has terminated with status code `") <<
533 WEXITSTATUS(*status) <<
534 wxT("'.");
535 } else if (WIFSIGNALED(*status)) {
536 *msg << wxT("Child was killed by signal `") <<
537 WTERMSIG(*status) <<
538 wxT("'.");
539 if (WCOREDUMP(*status)) {
540 *msg << wxT(" A core file has been dumped.");
542 } else if (WIFSTOPPED(*status)) {
543 *msg << wxT("Child has been stopped by signal `") <<
544 WSTOPSIG(*status) <<
545 wxT("'.");
546 #ifdef WIFCONTINUED /* Only found in recent kernels. */
547 } else if (WIFCONTINUED(*status)) {
548 *msg << wxT("Child has received `SIGCONT' and has continued execution.");
549 #endif
550 } else {
551 *msg << wxT("The program was not able to determine why the child has signaled.");
555 return result;
558 #endif // AMULED_APPTRAITS
561 #ifdef __WINDOWS__
563 // CTRL-C-Handler
564 // see http://msdn.microsoft.com/en-us/library/windows/desktop/ms685049%28v=vs.85%29.aspx
566 static BOOL CtrlHandler(DWORD fdwCtrlType)
568 switch (fdwCtrlType) {
569 case CTRL_C_EVENT:
570 case CTRL_CLOSE_EVENT:
571 case CTRL_BREAK_EVENT:
572 // handle these
573 AddLogLineNS(wxT("Received break event, exit main loop"));
574 theApp->ExitMainLoop();
575 return TRUE;
576 break;
577 case CTRL_LOGOFF_EVENT:
578 case CTRL_SHUTDOWN_EVENT:
579 default:
580 // don't handle these
581 return FALSE;
582 break;
586 #endif // __WINDOWS__
589 int CamuleDaemonApp::OnRun()
591 if (!thePrefs::AcceptExternalConnections()) {
592 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"));
593 return 0;
594 } else if (thePrefs::ECPassword().IsEmpty()) {
595 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"));
596 return 0;
599 #ifdef __WINDOWS__
600 SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE);
601 #endif // __WINDOWS__
603 #ifdef AMULED_APPTRAITS
604 // Process the return code of dead children so that we do not create
605 // zombies. wxBase does not implement wxProcess callbacks, so no one
606 // actualy calls wxHandleProcessTermination() in console applications.
607 // We do our best here.
608 DEBUG_ONLY( int ret = 0; )
609 DEBUG_ONLY( ret = ) sigaction(SIGCHLD, NULL, &m_oldSignalChildAction);
610 m_newSignalChildAction = m_oldSignalChildAction;
611 m_newSignalChildAction.sa_sigaction = OnSignalChildHandler;
612 m_newSignalChildAction.sa_flags |= SA_SIGINFO;
613 m_newSignalChildAction.sa_flags &= ~SA_RESETHAND;
614 DEBUG_ONLY( ret = ) sigaction(SIGCHLD, &m_newSignalChildAction, NULL);
615 #ifdef __DEBUG__
616 if (ret == -1) {
617 AddDebugLogLineC(logStandard, CFormat(wxT("CamuleDaemonApp::OnRun(): Installation of SIGCHLD callback with sigaction() failed: %m.")));
618 } else {
619 AddDebugLogLineN(logGeneral, wxT("CamuleDaemonApp::OnRun(): Installation of SIGCHLD callback with sigaction() succeeded."));
621 #endif
622 #endif // AMULED_APPTRAITS
624 #ifdef AMULED28_EVENTLOOP
626 while ( !m_Exit ) {
627 #ifdef AMULED28_SOCKETS
628 m_table->RunSelect();
629 ProcessPendingEvents();
630 ((CDaemonAppTraits *)GetTraits())->DeletePending();
631 #else
632 wxMilliSleep(10);
633 ProcessPendingEvents();
634 #endif
637 // ShutDown is beeing called twice. Once here and again in OnExit().
638 ShutDown();
640 return 0;
642 #else
643 return wxApp::OnRun();
644 #endif
647 bool CamuleDaemonApp::OnInit()
649 if ( !CamuleApp::OnInit() ) {
650 return false;
652 AddLogLineNS(_("amuled: OnInit - starting timer"));
653 core_timer = new CTimer(this,ID_CORE_TIMER_EVENT);
654 core_timer->Start(CORE_TIMER_PERIOD);
655 glob_prefs->GetCategory(0)->title = GetCatTitle(thePrefs::GetAllcatFilter());
656 glob_prefs->GetCategory(0)->path = thePrefs::GetIncomingDir();
658 return true;
661 int CamuleDaemonApp::InitGui(bool ,wxString &)
663 #ifndef __WINDOWS__
664 if ( !enable_daemon_fork ) {
665 return 0;
667 AddLogLineNS(_("amuled: forking to background - see you"));
668 theLogger.SetEnabledStdoutLog(false);
670 // fork to background and detach from controlling tty
671 // while redirecting stdout to /dev/null
673 for(int i_fd = 0;i_fd < 3; i_fd++) {
674 close(i_fd);
676 int fd = open("/dev/null",O_RDWR);
677 if (dup(fd)){} // prevent GCC warning
678 if (dup(fd)){}
679 pid_t pid = fork();
681 wxASSERT(pid != -1);
683 if ( pid ) {
684 exit(0);
685 } else {
686 pid = setsid();
688 // Create a Pid file with the Pid of the Child, so any daemon-manager
689 // can easily manage the process
691 if (!m_PidFile.IsEmpty()) {
692 wxString temp = CFormat(wxT("%d\n")) % pid;
693 wxFFile ff(m_PidFile, wxT("w"));
694 if (!ff.Error()) {
695 ff.Write(temp);
696 ff.Close();
697 } else {
698 AddLogLineNS(_("Cannot Create Pid File"));
703 #endif
704 return 0;
708 int CamuleDaemonApp::OnExit()
710 #ifdef AMULED28_SOCKETS
712 * Stop all socket threads before entering
713 * shutdown sequence.
715 delete listensocket;
716 listensocket = 0;
717 if (clientudp) {
718 delete clientudp;
719 clientudp = NULL;
721 #endif
723 ShutDown();
725 #ifdef AMULED_APPTRAITS
726 DEBUG_ONLY( int ret = ) sigaction(SIGCHLD, &m_oldSignalChildAction, NULL);
727 #ifdef __DEBUG__
728 if (ret == -1) {
729 AddDebugLogLineC(logStandard, CFormat(wxT("CamuleDaemonApp::OnRun(): second sigaction() failed: %m.")));
730 } else {
731 AddDebugLogLineN(logGeneral, wxT("CamuleDaemonApp::OnRun(): Uninstallation of SIGCHLD callback with sigaction() succeeded."));
733 #endif
734 #endif // AMULED_APPTRAITS
736 delete core_timer;
738 return CamuleApp::OnExit();
742 int CamuleDaemonApp::ShowAlert(wxString msg, wxString title, int flags)
744 if ( flags | wxICON_ERROR ) {
745 title = CFormat(_("ERROR: %s")) % title;
747 AddLogLineCS(title + wxT(" ") + msg);
749 return 0; // That's neither yes nor no, ok, cancel
752 // File_checked_for_headers