2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
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
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 #include "config.h" // Needed for HAVE_SYS_RESOURCE_H, etc
33 // Include the necessary headers for select(2), properly guarded
34 #if defined HAVE_SYS_SELECT_H && !defined __IRIX__
35 # include <sys/select.h>
37 # ifdef HAVE_SYS_TIME_H
38 # include <sys/time.h>
40 # ifdef HAVE_SYS_TYPES_H
41 # include <sys/types.h>
50 #include "Preferences.h" // Needed for CPreferences
51 #include "PartFile.h" // Needed for CPartFile
53 #include <common/Format.h>
54 #include "InternalEvents.h" // Needed for wxEVT_*
55 #include "ThreadTasks.h"
56 #include "GuiEvents.h" // Needed for EVT_MULE_NOTIFY
57 #include "Timer.h" // Needed for EVT_MULE_TIMER
59 #include "ClientUDPSocket.h" // Do_not_auto_remove (forward declaration not enough)
60 #include "ListenSocket.h" // Do_not_auto_remove (forward declaration not enough)
63 #ifdef HAVE_SYS_RESOURCE_H
64 #include <sys/resource.h> // Do_not_auto_remove
68 #ifdef HAVE_SYS_WAIT_H
69 #include <sys/wait.h> // Do_not_auto_remove
74 #ifdef AMULED_APPTRAITS
75 #include <wx/unix/execute.h>
79 BEGIN_EVENT_TABLE(CamuleDaemonApp
, wxAppConsole
)
87 EVT_SOCKET(ID_LISTENSOCKET_EVENT
, CamuleDaemonApp::ListenSocketHandler
)
89 // UDP Socket (servers)
90 EVT_SOCKET(ID_SERVERUDPSOCKET_EVENT
, CamuleDaemonApp::UDPSocketHandler
)
91 // UDP Socket (clients)
92 EVT_SOCKET(ID_CLIENTUDPSOCKET_EVENT
, CamuleDaemonApp::UDPSocketHandler
)
96 EVT_MULE_TIMER(ID_SERVER_RETRY_TIMER_EVENT
, CamuleDaemonApp::OnTCPTimer
)
99 EVT_MULE_TIMER(ID_CORE_TIMER_EVENT
, CamuleDaemonApp::OnCoreTimer
)
101 EVT_MULE_NOTIFY(CamuleDaemonApp::OnNotifyEvent
)
103 // Async dns handling
104 EVT_MULE_INTERNAL(wxEVT_CORE_UDP_DNS_DONE
, -1, CamuleDaemonApp::OnUDPDnsDone
)
106 EVT_MULE_INTERNAL(wxEVT_CORE_SOURCE_DNS_DONE
, -1, CamuleDaemonApp::OnSourceDnsDone
)
108 EVT_MULE_INTERNAL(wxEVT_CORE_SERVER_DNS_DONE
, -1, CamuleDaemonApp::OnServerDnsDone
)
110 // Hash ended notifier
111 EVT_MULE_HASHING(CamuleDaemonApp::OnFinishedHashing
)
112 EVT_MULE_AICH_HASHING(CamuleDaemonApp::OnFinishedAICHHashing
)
114 // File completion ended notifier
115 EVT_MULE_FILE_COMPLETED(CamuleDaemonApp::OnFinishedCompletion
)
117 // HTTPDownload finished
118 EVT_MULE_INTERNAL(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD
, -1, CamuleDaemonApp::OnFinishedHTTPDownload
)
120 // Disk space preallocation finished
121 EVT_MULE_ALLOC_FINISHED(CamuleDaemonApp::OnFinishedAllocation
)
124 IMPLEMENT_APP(CamuleDaemonApp
)
126 #ifdef AMULED28_SOCKETS
128 * Socket handling in wxBase
133 int m_fds
[FD_SETSIZE
], m_fd_idx
[FD_SETSIZE
];
134 GSocket
*m_gsocks
[FD_SETSIZE
];
139 void AddSocket(GSocket
*);
140 void RemoveSocket(GSocket
*);
141 void FillSet(int &max_fd
);
143 void Detected(void (GSocket::*func
)());
145 fd_set
*Set() { return &m_set
; }
148 CSocketSet::CSocketSet()
151 for(int i
= 0; i
< FD_SETSIZE
; i
++) {
153 m_fd_idx
[i
] = 0xffff;
158 void CSocketSet::AddSocket(GSocket
*socket
)
162 int fd
= socket
->m_fd
;
168 wxASSERT( (fd
> 2) && (fd
< FD_SETSIZE
) );
170 if ( m_gsocks
[fd
] ) {
174 m_fd_idx
[fd
] = m_count
;
175 m_gsocks
[fd
] = socket
;
179 void CSocketSet::RemoveSocket(GSocket
*socket
)
183 int fd
= socket
->m_fd
;
189 wxASSERT( (fd
> 2) && (fd
< FD_SETSIZE
) );
191 int i
= m_fd_idx
[fd
];
195 wxASSERT(m_fds
[i
] == fd
);
196 m_fds
[i
] = m_fds
[m_count
-1];
198 m_fds
[m_count
-1] = 0;
199 m_fd_idx
[fd
] = 0xffff;
200 m_fd_idx
[m_fds
[i
]] = i
;
204 void CSocketSet::FillSet(int &max_fd
)
208 for(int i
= 0; i
< m_count
; i
++) {
209 FD_SET(m_fds
[i
], &m_set
);
210 if ( m_fds
[i
] > max_fd
) {
216 void CSocketSet::Detected(void (GSocket::*func
)())
218 for (int i
= 0; i
< m_count
; i
++) {
220 if ( FD_ISSET(fd
, &m_set
) ) {
221 GSocket
*socket
= m_gsocks
[fd
];
227 CAmuledGSocketFuncTable::CAmuledGSocketFuncTable() : m_lock(wxMUTEX_RECURSIVE
)
229 m_in_set
= new CSocketSet
;
230 m_out_set
= new CSocketSet
;
235 void CAmuledGSocketFuncTable::AddSocket(GSocket
*socket
, GSocketEvent event
)
237 wxMutexLocker
lock(m_lock
);
239 if ( event
== GSOCK_INPUT
) {
240 m_in_set
->AddSocket(socket
);
242 m_out_set
->AddSocket(socket
);
246 void CAmuledGSocketFuncTable::RemoveSocket(GSocket
*socket
, GSocketEvent event
)
248 wxMutexLocker
lock(m_lock
);
250 if ( event
== GSOCK_INPUT
) {
251 m_in_set
->RemoveSocket(socket
);
253 m_out_set
->RemoveSocket(socket
);
257 void CAmuledGSocketFuncTable::RunSelect()
259 wxMutexLocker
lock(m_lock
);
262 m_in_set
->FillSet(max_fd
);
263 m_out_set
->FillSet(max_fd
);
267 tv
.tv_usec
= 10000; // 10ms
269 int result
= select(max_fd
+ 1, m_in_set
->Set(), m_out_set
->Set(), 0, &tv
);
271 m_in_set
->Detected(&GSocket::Detected_Read
);
272 m_out_set
->Detected(&GSocket::Detected_Write
);
276 GSocketGUIFunctionsTable
*CDaemonAppTraits::GetSocketGUIFunctionsTable()
281 bool CAmuledGSocketFuncTable::OnInit()
286 void CAmuledGSocketFuncTable::OnExit()
290 bool CAmuledGSocketFuncTable::CanUseEventLoop()
293 * FIXME: (lfroen) Not sure whether it's right.
294 * I will review it later.
299 bool CAmuledGSocketFuncTable::Init_Socket(GSocket
*)
304 void CAmuledGSocketFuncTable::Destroy_Socket(GSocket
*)
308 void CAmuledGSocketFuncTable::Install_Callback(GSocket
*sock
, GSocketEvent e
)
313 void CAmuledGSocketFuncTable::Uninstall_Callback(GSocket
*sock
, GSocketEvent e
)
315 RemoveSocket(sock
, e
);
318 void CAmuledGSocketFuncTable::Enable_Events(GSocket
*socket
)
320 Install_Callback(socket
, GSOCK_INPUT
);
321 Install_Callback(socket
, GSOCK_OUTPUT
);
324 void CAmuledGSocketFuncTable::Disable_Events(GSocket
*socket
)
326 Uninstall_Callback(socket
, GSOCK_INPUT
);
327 Uninstall_Callback(socket
, GSOCK_OUTPUT
);
331 CDaemonAppTraits::CDaemonAppTraits(CAmuledGSocketFuncTable
*table
)
333 wxConsoleAppTraits(),
334 m_oldSignalChildAction(),
335 m_newSignalChildAction(),
337 m_lock(wxMUTEX_RECURSIVE
),
344 void CDaemonAppTraits::ScheduleForDestroy(wxObject
*object
)
346 wxMutexLocker
lock(m_lock
);
349 m_sched_delete
.push_back(object
);
352 void CDaemonAppTraits::RemoveFromPendingDelete(wxObject
*object
)
354 wxMutexLocker
lock(m_lock
);
356 for(std::list
<wxObject
*>::iterator i
= m_sched_delete
.begin();
357 i
!= m_sched_delete
.end(); i
++) {
358 if ( *i
== object
) {
359 m_sched_delete
.erase(i
);
365 void CDaemonAppTraits::DeletePending()
367 wxMutexLocker
lock(m_lock
);
369 while ( !m_sched_delete
.empty() ) {
370 std::list
<wxObject
*>::iterator i
= m_sched_delete
.begin();
371 wxObject
*object
= *i
;
374 //m_sched_delete.erase(m_sched_delete.begin(), m_sched_delete.end());
377 wxAppTraits
*CamuleDaemonApp::CreateTraits()
379 return new CDaemonAppTraits(m_table
);
382 #else // AMULED28_SOCKETS
384 #ifdef AMULED_APPTRAITS
386 CDaemonAppTraits::CDaemonAppTraits()
388 wxConsoleAppTraits(),
389 m_oldSignalChildAction(),
390 m_newSignalChildAction()
394 wxAppTraits
*CamuleDaemonApp::CreateTraits()
396 return new CDaemonAppTraits();
399 #endif // AMULED_APPTRAITS
401 #endif // !AMULED28_SOCKETS
403 #if defined(__WXMAC__) && !wxCHECK_VERSION(2, 9, 0)
404 #include <wx/stdpaths.h> // Do_not_auto_remove (guess)
405 static wxStandardPathsCF gs_stdPaths
;
406 wxStandardPathsBase
& CDaemonAppTraits::GetStandardPaths()
413 #ifdef AMULED28_EVENTLOOP
415 CamuleDaemonApp::CamuleDaemonApp()
418 #ifdef AMULED28_SOCKETS
419 ,m_table(new CAmuledGSocketFuncTable())
422 // work around problem from http://trac.wxwidgets.org/ticket/2145
423 wxPendingEventsLocker
= new wxCriticalSection
;
426 #endif // AMULED28_EVENTLOOP
429 #ifdef AMULED_APPTRAITS
431 static EndProcessDataMap endProcDataMap
;
433 int CDaemonAppTraits::WaitForChild(wxExecuteData
&execData
)
437 // Build the log message
439 msg
<< wxT("WaitForChild() has been called for child process with pid `") <<
443 if (execData
.flags
& wxEXEC_SYNC
) {
444 result
= AmuleWaitPid(execData
.pid
, &status
, 0, &msg
);
445 if (result
== -1 || (!WIFEXITED(status
) && !WIFSIGNALED(status
))) {
446 msg
<< wxT(" Waiting for subprocess termination failed.");
447 AddDebugLogLineN(logGeneral
, msg
);
451 // Give the process a chance to start or forked child to exit
452 // 1 second is enough time to fail on "path not found"
454 result
= AmuleWaitPid(execData
.pid
, &status
, WNOHANG
, &msg
);
456 // Add a WxEndProcessData entry to the map, so that we can
457 // support process termination
458 wxEndProcessData
*endProcData
= new wxEndProcessData();
459 endProcData
->pid
= execData
.pid
;
460 endProcData
->process
= execData
.process
;
461 endProcData
->tag
= 0;
462 endProcDataMap
[execData
.pid
] = endProcData
;
464 status
= execData
.pid
;
466 // if result != 0, then either waitpid() failed (result == -1)
467 // and there is nothing we can do, or the child has changed
468 // status, which means it is probably dead.
473 // Log our passage here
474 AddDebugLogLineN(logGeneral
, msg
);
480 void OnSignalChildHandler(int /*signal*/, siginfo_t
*siginfo
, void * /*ucontext*/)
482 // Build the log message
484 msg
<< wxT("OnSignalChildHandler() has been called for child process with pid `") <<
487 // Make sure we leave no zombies by calling waitpid()
489 pid_t result
= AmuleWaitPid(siginfo
->si_pid
, &status
, WNOHANG
, &msg
);
490 if (result
!= 1 && result
!= 0 && (WIFEXITED(status
) || WIFSIGNALED(status
))) {
491 // Fetch the wxEndProcessData structure corresponding to this pid
492 EndProcessDataMap::iterator it
= endProcDataMap
.find(siginfo
->si_pid
);
493 if (it
!= endProcDataMap
.end()) {
494 wxEndProcessData
*endProcData
= it
->second
;
495 // Remove this entry from the process map
496 endProcDataMap
.erase(siginfo
->si_pid
);
497 // Save the exit code for the wxProcess object to read later
498 endProcData
->exitcode
= result
!= -1 && WIFEXITED(status
) ?
499 WEXITSTATUS(status
) : -1;
500 // Make things work as in wxGUI
501 wxHandleProcessTermination(endProcData
);
503 // wxHandleProcessTermination() will "delete endProcData;"
504 // So we do not delete it again, ok? Do not uncomment this line.
505 //delete endProcData;
507 msg
<< wxT(" Error: the child process pid is not on the pid map.");
511 // Log our passage here
512 AddDebugLogLineN(logGeneral
, msg
);
516 pid_t
AmuleWaitPid(pid_t pid
, int *status
, int options
, wxString
*msg
)
519 pid_t result
= waitpid(pid
, status
, options
);
521 *msg
<< CFormat(wxT("Error: waitpid() call failed: %m."));
522 } else if (result
== 0) {
523 if (options
& WNOHANG
) {
524 *msg
<< wxT("The child is alive.");
526 *msg
<< wxT("Error: waitpid() call returned 0 but "
527 "WNOHANG was not specified in options.");
530 if (WIFEXITED(*status
)) {
531 *msg
<< wxT("Child has terminated with status code `") <<
532 WEXITSTATUS(*status
) <<
534 } else if (WIFSIGNALED(*status
)) {
535 *msg
<< wxT("Child was killed by signal `") <<
538 if (WCOREDUMP(*status
)) {
539 *msg
<< wxT(" A core file has been dumped.");
541 } else if (WIFSTOPPED(*status
)) {
542 *msg
<< wxT("Child has been stopped by signal `") <<
545 #ifdef WIFCONTINUED /* Only found in recent kernels. */
546 } else if (WIFCONTINUED(*status
)) {
547 *msg
<< wxT("Child has received `SIGCONT' and has continued execution.");
550 *msg
<< wxT("The program was not able to determine why the child has signaled.");
557 #endif // AMULED_APPTRAITS
563 // see http://msdn.microsoft.com/en-us/library/windows/desktop/ms685049%28v=vs.85%29.aspx
565 static BOOL
CtrlHandler(DWORD fdwCtrlType
)
567 switch (fdwCtrlType
) {
569 case CTRL_CLOSE_EVENT
:
570 case CTRL_BREAK_EVENT
:
572 AddLogLineNS(wxT("Received break event, exit main loop"));
573 theApp
->ExitMainLoop();
576 case CTRL_LOGOFF_EVENT
:
577 case CTRL_SHUTDOWN_EVENT
:
579 // don't handle these
585 #endif // __WINDOWS__
588 int CamuleDaemonApp::OnRun()
590 if (!thePrefs::AcceptExternalConnections()) {
591 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 } else if (thePrefs::ECPassword().IsEmpty()) {
594 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"));
599 SetConsoleCtrlHandler((PHANDLER_ROUTINE
) CtrlHandler
, TRUE
);
600 #endif // __WINDOWS__
602 #ifdef AMULED_APPTRAITS
603 // Process the return code of dead children so that we do not create
604 // zombies. wxBase does not implement wxProcess callbacks, so no one
605 // actualy calls wxHandleProcessTermination() in console applications.
606 // We do our best here.
607 DEBUG_ONLY( int ret
= 0; )
608 DEBUG_ONLY( ret
= ) sigaction(SIGCHLD
, NULL
, &m_oldSignalChildAction
);
609 m_newSignalChildAction
= m_oldSignalChildAction
;
610 m_newSignalChildAction
.sa_sigaction
= OnSignalChildHandler
;
611 m_newSignalChildAction
.sa_flags
|= SA_SIGINFO
;
612 m_newSignalChildAction
.sa_flags
&= ~SA_RESETHAND
;
613 DEBUG_ONLY( ret
= ) sigaction(SIGCHLD
, &m_newSignalChildAction
, NULL
);
616 AddDebugLogLineC(logStandard
, CFormat(wxT("CamuleDaemonApp::OnRun(): Installation of SIGCHLD callback with sigaction() failed: %m.")));
618 AddDebugLogLineN(logGeneral
, wxT("CamuleDaemonApp::OnRun(): Installation of SIGCHLD callback with sigaction() succeeded."));
621 #endif // AMULED_APPTRAITS
623 #ifdef AMULED28_EVENTLOOP
626 #ifdef AMULED28_SOCKETS
627 m_table
->RunSelect();
628 ProcessPendingEvents();
629 ((CDaemonAppTraits
*)GetTraits())->DeletePending();
632 ProcessPendingEvents();
636 // ShutDown is beeing called twice. Once here and again in OnExit().
642 return wxApp::OnRun();
646 bool CamuleDaemonApp::OnInit()
648 if ( !CamuleApp::OnInit() ) {
651 AddLogLineNS(_("amuled: OnInit - starting timer"));
652 core_timer
= new CTimer(this,ID_CORE_TIMER_EVENT
);
653 core_timer
->Start(CORE_TIMER_PERIOD
);
654 glob_prefs
->GetCategory(0)->title
= GetCatTitle(thePrefs::GetAllcatFilter());
655 glob_prefs
->GetCategory(0)->path
= thePrefs::GetIncomingDir();
660 int CamuleDaemonApp::InitGui(bool ,wxString
&)
663 if ( !enable_daemon_fork
) {
666 AddLogLineNS(_("amuled: forking to background - see you"));
667 theLogger
.SetEnabledStdoutLog(false);
669 // fork to background and detach from controlling tty
670 // while redirecting stdout to /dev/null
672 for(int i_fd
= 0;i_fd
< 3; i_fd
++) {
675 int fd
= open("/dev/null",O_RDWR
);
676 if (dup(fd
)){} // prevent GCC warning
687 // Create a Pid file with the Pid of the Child, so any daemon-manager
688 // can easily manage the process
690 if (!m_PidFile
.IsEmpty()) {
691 wxString temp
= CFormat(wxT("%d\n")) % pid
;
692 wxFFile
ff(m_PidFile
, wxT("w"));
697 AddLogLineNS(_("Cannot Create Pid File"));
706 bool CamuleDaemonApp::Initialize(int& argc_
, wxChar
**argv_
)
708 if ( !wxAppConsole::Initialize(argc_
, argv_
) ) {
715 // if a non default locale is set,
716 // assume that the user wants his
717 // filenames in this locale too
718 encName
= wxLocale::GetSystemEncodingName().Upper();
720 // But don't consider ASCII in this case.
721 if ( !encName
.empty() ) {
722 if ( encName
== wxT("US-ASCII") ) {
723 // This means US-ASCII when returned
724 // from GetEncodingFromName().
730 // in this case, UTF-8 is used by default.
731 if ( encName
.empty() ) {
732 encName
= wxT("UTF-8");
735 static wxConvBrokenFileNames
fileconv(encName
);
736 wxConvFileName
= &fileconv
;
742 int CamuleDaemonApp::OnExit()
744 #ifdef AMULED28_SOCKETS
746 * Stop all socket threads before entering
759 #ifdef AMULED_APPTRAITS
760 DEBUG_ONLY( int ret
= ) sigaction(SIGCHLD
, &m_oldSignalChildAction
, NULL
);
763 AddDebugLogLineC(logStandard
, CFormat(wxT("CamuleDaemonApp::OnRun(): second sigaction() failed: %m.")));
765 AddDebugLogLineN(logGeneral
, wxT("CamuleDaemonApp::OnRun(): Uninstallation of SIGCHLD callback with sigaction() succeeded."));
768 #endif // AMULED_APPTRAITS
772 return CamuleApp::OnExit();
776 int CamuleDaemonApp::ShowAlert(wxString msg
, wxString title
, int flags
)
778 if ( flags
| wxICON_ERROR
) {
779 title
= CFormat(_("ERROR: %s")) % title
;
781 AddLogLineCS(title
+ wxT(" ") + msg
);
783 return 0; // That's neither yes nor no, ok, cancel
786 // File_checked_for_headers