2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2008 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>
31 #include "config.h" // Needed for HAVE_SYS_RESOURCE_H, HAVE_STRERROR_R and STRERROR_R_CHAR_P, etc
34 // Include the necessary headers for select(2), properly guarded
35 #if defined HAVE_SYS_SELECT_H && !defined __IRIX__
36 # include <sys/select.h>
38 # ifdef HAVE_SYS_TIME_H
39 # include <sys/time.h>
41 # ifdef HAVE_SYS_TYPES_H
42 # include <sys/types.h>
49 // Prefer the POSIX interface to strerror_r()
50 #define _XOPEN_SOURCE 600
51 #include <string.h> // Do_not_auto_remove
55 #include "Preferences.h" // Needed for CPreferences
56 #include "PartFile.h" // Needed for CPartFile
58 #include <common/Format.h>
59 #include "InternalEvents.h" // Needed for wxEVT_*
60 #include "ThreadTasks.h"
61 #include "GuiEvents.h" // Needed for EVT_MULE_NOTIFY
62 #include "Timer.h" // Needed for EVT_MULE_TIMER
64 #include "ClientUDPSocket.h" // Do_not_auto_remove (forward declaration not enough)
65 #include "ListenSocket.h" // Do_not_auto_remove (forward declaration not enough)
69 #ifdef HAVE_SYS_RESOURCE_H
70 #include <sys/resource.h> // Do_not_auto_remove
74 #ifdef HAVE_SYS_WAIT_H
75 #include <sys/wait.h> // Do_not_auto_remove
78 #include <wx/unix/execute.h>
81 #ifndef HAVE_STRERROR_R
83 // Replacement strerror_r() function for systems that don't have any.
84 // Note that this replacement function is NOT thread-safe!
85 static int rpl_strerror_r(int errnum
, char *buf
, size_t buflen
)
87 char *tmp
= strerror(errnum
);
92 strncpy(buf
, tmp
, buflen
- 1);
93 buf
[buflen
- 1] = '\0';
94 if (strlen(tmp
) >= buflen
) {
102 # define strerror_r(errnum, buf, buflen) rpl_strerror_r(errnum, buf, buflen)
104 # ifdef STRERROR_R_CHAR_P
106 // Replacement strerror_r() function for systems that return a char*.
107 static int rpl_strerror_r(int errnum
, char *buf
, size_t buflen
)
109 char *tmp
= strerror_r(errnum
, buf
, buflen
);
113 } else if (tmp
!= buf
) {
114 strncpy(buf
, tmp
, buflen
- 1);
115 buf
[buflen
- 1] = '\0';
116 if (strlen(tmp
) >= buflen
) {
124 # define strerror_r(errnum, buf, buflen) rpl_strerror_r(errnum, buf, buflen)
128 BEGIN_EVENT_TABLE(CamuleDaemonApp
, wxAppConsole
)
134 EVT_SOCKET(ID_LISTENSOCKET_EVENT
, CamuleDaemonApp::ListenSocketHandler
)
136 // UDP Socket (servers)
137 EVT_SOCKET(ID_SERVERUDPSOCKET_EVENT
, CamuleDaemonApp::UDPSocketHandler
)
138 // UDP Socket (clients)
139 EVT_SOCKET(ID_CLIENTUDPSOCKET_EVENT
, CamuleDaemonApp::UDPSocketHandler
)
141 // Socket timer (TCP)
142 EVT_MULE_TIMER(ID_SERVER_RETRY_TIMER_EVENT
, CamuleDaemonApp::OnTCPTimer
)
145 EVT_MULE_TIMER(ID_CORE_TIMER_EVENT
, CamuleDaemonApp::OnCoreTimer
)
147 EVT_MULE_NOTIFY(CamuleDaemonApp::OnNotifyEvent
)
149 // Async dns handling
150 EVT_MULE_INTERNAL(wxEVT_CORE_UDP_DNS_DONE
, -1, CamuleDaemonApp::OnUDPDnsDone
)
152 EVT_MULE_INTERNAL(wxEVT_CORE_SOURCE_DNS_DONE
, -1, CamuleDaemonApp::OnSourceDnsDone
)
154 EVT_MULE_INTERNAL(wxEVT_CORE_SERVER_DNS_DONE
, -1, CamuleDaemonApp::OnServerDnsDone
)
156 // Hash ended notifier
157 EVT_MULE_HASHING(CamuleDaemonApp::OnFinishedHashing
)
158 EVT_MULE_AICH_HASHING(CamuleDaemonApp::OnFinishedAICHHashing
)
160 // File completion ended notifier
161 EVT_MULE_FILE_COMPLETED(CamuleDaemonApp::OnFinishedCompletion
)
163 // HTTPDownload finished
164 EVT_MULE_INTERNAL(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD
, -1, CamuleDaemonApp::OnFinishedHTTPDownload
)
166 // Disk space preallocation finished
167 EVT_MULE_ALLOC_FINISHED(CamuleDaemonApp::OnFinishedAllocation
)
170 IMPLEMENT_APP(CamuleDaemonApp
)
174 * Socket handling in wxBase
179 int m_fds
[FD_SETSIZE
], m_fd_idx
[FD_SETSIZE
];
180 GSocket
*m_gsocks
[FD_SETSIZE
];
185 void AddSocket(GSocket
*);
186 void RemoveSocket(GSocket
*);
187 void FillSet(int &max_fd
);
189 void Detected(void (GSocket::*func
)());
191 fd_set
*Set() { return &m_set
; }
194 CSocketSet::CSocketSet()
197 for(int i
= 0; i
< FD_SETSIZE
; i
++) {
199 m_fd_idx
[i
] = 0xffff;
204 void CSocketSet::AddSocket(GSocket
*socket
)
208 int fd
= socket
->m_fd
;
214 wxASSERT( (fd
> 2) && (fd
< FD_SETSIZE
) );
216 if ( m_gsocks
[fd
] ) {
220 m_fd_idx
[fd
] = m_count
;
221 m_gsocks
[fd
] = socket
;
225 void CSocketSet::RemoveSocket(GSocket
*socket
)
229 int fd
= socket
->m_fd
;
235 wxASSERT( (fd
> 2) && (fd
< FD_SETSIZE
) );
237 int i
= m_fd_idx
[fd
];
241 wxASSERT(m_fds
[i
] == fd
);
242 m_fds
[i
] = m_fds
[m_count
-1];
244 m_fds
[m_count
-1] = 0;
245 m_fd_idx
[fd
] = 0xffff;
246 m_fd_idx
[m_fds
[i
]] = i
;
250 void CSocketSet::FillSet(int &max_fd
)
254 for(int i
= 0; i
< m_count
; i
++) {
255 FD_SET(m_fds
[i
], &m_set
);
256 if ( m_fds
[i
] > max_fd
) {
262 void CSocketSet::Detected(void (GSocket::*func
)())
264 for (int i
= 0; i
< m_count
; i
++) {
266 if ( FD_ISSET(fd
, &m_set
) ) {
267 GSocket
*socket
= m_gsocks
[fd
];
273 CAmuledGSocketFuncTable::CAmuledGSocketFuncTable() : m_lock(wxMUTEX_RECURSIVE
)
275 m_in_set
= new CSocketSet
;
276 m_out_set
= new CSocketSet
;
281 void CAmuledGSocketFuncTable::AddSocket(GSocket
*socket
, GSocketEvent event
)
283 wxMutexLocker
lock(m_lock
);
285 if ( event
== GSOCK_INPUT
) {
286 m_in_set
->AddSocket(socket
);
288 m_out_set
->AddSocket(socket
);
292 void CAmuledGSocketFuncTable::RemoveSocket(GSocket
*socket
, GSocketEvent event
)
294 wxMutexLocker
lock(m_lock
);
296 if ( event
== GSOCK_INPUT
) {
297 m_in_set
->RemoveSocket(socket
);
299 m_out_set
->RemoveSocket(socket
);
303 void CAmuledGSocketFuncTable::RunSelect()
305 wxMutexLocker
lock(m_lock
);
308 m_in_set
->FillSet(max_fd
);
309 m_out_set
->FillSet(max_fd
);
313 tv
.tv_usec
= 10000; // 10ms
315 int result
= select(max_fd
+ 1, m_in_set
->Set(), m_out_set
->Set(), 0, &tv
);
317 m_in_set
->Detected(&GSocket::Detected_Read
);
318 m_out_set
->Detected(&GSocket::Detected_Write
);
322 GSocketGUIFunctionsTable
*CDaemonAppTraits::GetSocketGUIFunctionsTable()
327 bool CAmuledGSocketFuncTable::OnInit()
332 void CAmuledGSocketFuncTable::OnExit()
336 bool CAmuledGSocketFuncTable::CanUseEventLoop()
339 * FIXME: (lfroen) Not sure whether it's right.
340 * I will review it later.
345 bool CAmuledGSocketFuncTable::Init_Socket(GSocket
*)
350 void CAmuledGSocketFuncTable::Destroy_Socket(GSocket
*)
354 void CAmuledGSocketFuncTable::Install_Callback(GSocket
*sock
, GSocketEvent e
)
359 void CAmuledGSocketFuncTable::Uninstall_Callback(GSocket
*sock
, GSocketEvent e
)
361 RemoveSocket(sock
, e
);
364 void CAmuledGSocketFuncTable::Enable_Events(GSocket
*socket
)
366 Install_Callback(socket
, GSOCK_INPUT
);
367 Install_Callback(socket
, GSOCK_OUTPUT
);
370 void CAmuledGSocketFuncTable::Disable_Events(GSocket
*socket
)
372 Uninstall_Callback(socket
, GSOCK_INPUT
);
373 Uninstall_Callback(socket
, GSOCK_OUTPUT
);
382 CDaemonAppTraits::CDaemonAppTraits(CAmuledGSocketFuncTable
*table
)
384 wxConsoleAppTraits(),
385 m_oldSignalChildAction(),
386 m_newSignalChildAction(),
388 m_lock(wxMUTEX_RECURSIVE
),
395 void CDaemonAppTraits::ScheduleForDestroy(wxObject
*object
)
397 wxMutexLocker
lock(m_lock
);
400 m_sched_delete
.push_back(object
);
403 void CDaemonAppTraits::RemoveFromPendingDelete(wxObject
*object
)
405 wxMutexLocker
lock(m_lock
);
407 for(std::list
<wxObject
*>::iterator i
= m_sched_delete
.begin();
408 i
!= m_sched_delete
.end(); i
++) {
409 if ( *i
== object
) {
410 m_sched_delete
.erase(i
);
416 void CDaemonAppTraits::DeletePending()
418 wxMutexLocker
lock(m_lock
);
420 while ( !m_sched_delete
.empty() ) {
421 std::list
<wxObject
*>::iterator i
= m_sched_delete
.begin();
422 wxObject
*object
= *i
;
425 //m_sched_delete.erase(m_sched_delete.begin(), m_sched_delete.end());
428 wxAppTraits
*CamuleDaemonApp::CreateTraits()
430 return new CDaemonAppTraits(m_table
);
435 CDaemonAppTraits::CDaemonAppTraits()
437 wxConsoleAppTraits(),
438 m_oldSignalChildAction(),
439 m_newSignalChildAction()
443 wxAppTraits
*CamuleDaemonApp::CreateTraits()
445 return new CDaemonAppTraits();
452 #if defined(__WXMAC__) && !wxCHECK_VERSION(2, 9, 0)
453 #include <wx/stdpaths.h> // Do_not_auto_remove (guess)
454 static wxStandardPathsCF gs_stdPaths
;
455 wxStandardPathsBase
& CDaemonAppTraits::GetStandardPaths()
464 CamuleDaemonApp::CamuleDaemonApp()
467 m_table(new CAmuledGSocketFuncTable())
469 wxPendingEventsLocker
= new wxCriticalSection
;
478 static EndProcessDataMap endProcDataMap
;
481 int CDaemonAppTraits::WaitForChild(wxExecuteData
&execData
)
485 // Build the log message
487 msg
<< wxT("WaitForChild() has been called for child process with pid `") <<
491 if (execData
.flags
& wxEXEC_SYNC
) {
492 result
= AmuleWaitPid(execData
.pid
, &status
, 0, &msg
);
493 if (result
== -1 || (!WIFEXITED(status
) && !WIFSIGNALED(status
))) {
494 msg
<< wxT(" Waiting for subprocess termination failed.");
495 AddDebugLogLineM(false, logGeneral
, msg
);
499 // Give the process a chance to start or forked child to exit
500 // 1 second is enough time to fail on "path not found"
502 result
= AmuleWaitPid(execData
.pid
, &status
, WNOHANG
, &msg
);
504 // Add a WxEndProcessData entry to the map, so that we can
505 // support process termination
506 wxEndProcessData
*endProcData
= new wxEndProcessData();
507 endProcData
->pid
= execData
.pid
;
508 endProcData
->process
= execData
.process
;
509 endProcData
->tag
= 0;
510 endProcDataMap
[execData
.pid
] = endProcData
;
512 status
= execData
.pid
;
514 // if result != 0, then either waitpid() failed (result == -1)
515 // and there is nothing we can do, or the child has changed
516 // status, which means it is probably dead.
521 // Log our passage here
522 AddDebugLogLineM(false, logGeneral
, msg
);
528 void OnSignalChildHandler(int /*signal*/, siginfo_t
*siginfo
, void * /*ucontext*/)
530 // Build the log message
532 msg
<< wxT("OnSignalChildHandler() has been called for child process with pid `") <<
535 // Make sure we leave no zombies by calling waitpid()
537 pid_t result
= AmuleWaitPid(siginfo
->si_pid
, &status
, WNOHANG
, &msg
);
538 if (result
!= 1 && result
!= 0 && (WIFEXITED(status
) || WIFSIGNALED(status
))) {
539 // Fetch the wxEndProcessData structure corresponding to this pid
540 EndProcessDataMap::iterator it
= endProcDataMap
.find(siginfo
->si_pid
);
541 if (it
!= endProcDataMap
.end()) {
542 wxEndProcessData
*endProcData
= it
->second
;
543 // Remove this entry from the process map
544 endProcDataMap
.erase(siginfo
->si_pid
);
545 // Save the exit code for the wxProcess object to read later
546 endProcData
->exitcode
= result
!= -1 && WIFEXITED(status
) ?
547 WEXITSTATUS(status
) : -1;
548 // Make things work as in wxGUI
549 wxHandleProcessTermination(endProcData
);
551 // wxHandleProcessTermination() will "delete endProcData;"
552 // So we do not delete it again, ok? Do not uncomment this line.
553 //delete endProcData;
555 msg
<< wxT(" Error: the child process pid is not on the pid map.");
559 // Log our passage here
560 AddDebugLogLineM(false, logGeneral
, msg
);
564 pid_t
AmuleWaitPid(pid_t pid
, int *status
, int options
, wxString
*msg
)
566 // strerror_r() buffer
567 const int ERROR_BUFFER_LEN
= 256;
568 char errorBuffer
[ERROR_BUFFER_LEN
];
571 pid_t result
= waitpid(pid
, status
, options
);
573 strerror_r(errno
, errorBuffer
, ERROR_BUFFER_LEN
);
574 *msg
<< wxT("Error: waitpid() call failed: ") <<
575 char2unicode(errorBuffer
) <<
577 } else if (result
== 0) {
578 if (options
& WNOHANG
) {
579 *msg
<< wxT("The child is alive.");
581 *msg
<< wxT("Error: waitpid() call returned 0 but "
582 "WNOHANG was not specified in options.");
585 if (WIFEXITED(*status
)) {
586 *msg
<< wxT("Child has terminated with status code `") <<
587 WEXITSTATUS(*status
) <<
589 } else if (WIFSIGNALED(*status
)) {
590 *msg
<< wxT("Child was killed by signal `") <<
593 if (WCOREDUMP(*status
)) {
594 *msg
<< wxT(" A core file has been dumped.");
596 } else if (WIFSTOPPED(*status
)) {
597 *msg
<< wxT("Child has been stopped by signal `") <<
600 #ifdef WIFCONTINUED /* Only found in recent kernels. */
601 } else if (WIFCONTINUED(*status
)) {
602 *msg
<< wxT("Child has received `SIGCONT' and has continued execution.");
605 *msg
<< wxT("The program was not able to determine why the child has signaled.");
616 int CamuleDaemonApp::OnRun()
618 if (!thePrefs::AcceptExternalConnections()) {
619 AddLogLineMS(true, _("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"));
621 } else if (thePrefs::ECPassword().IsEmpty()) {
622 AddLogLineMS(true, _("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"));
627 // strerror_r() buffer
628 const int ERROR_BUFFER_LEN
= 256;
629 char errorBuffer
[ERROR_BUFFER_LEN
];
632 // Process the return code of dead children so that we do not create
633 // zombies. wxBase does not implement wxProcess callbacks, so no one
634 // actualy calls wxHandleProcessTermination() in console applications.
635 // We do our best here.
637 ret
= sigaction(SIGCHLD
, NULL
, &m_oldSignalChildAction
);
638 m_newSignalChildAction
= m_oldSignalChildAction
;
639 m_newSignalChildAction
.sa_sigaction
= OnSignalChildHandler
;
640 m_newSignalChildAction
.sa_flags
|= SA_SIGINFO
;
641 m_newSignalChildAction
.sa_flags
&= ~SA_RESETHAND
;
642 ret
= sigaction(SIGCHLD
, &m_newSignalChildAction
, NULL
);
644 strerror_r(errno
, errorBuffer
, ERROR_BUFFER_LEN
);
645 msg
<< wxT("CamuleDaemonApp::OnRun(): "
646 "Installation of SIGCHLD callback with sigaction() failed: ") <<
647 char2unicode(errorBuffer
) <<
649 AddLogLineM(true, msg
);
651 msg
<< wxT("CamuleDaemonApp::OnRun(): Installation of SIGCHLD "
652 "callback with sigaction() succeeded.");
653 AddDebugLogLineM(false, logGeneral
, msg
);
660 m_table
->RunSelect();
661 ProcessPendingEvents();
662 ((CDaemonAppTraits
*)GetTraits())->DeletePending();
665 // ShutDown is beeing called twice. Once here and again in OnExit().
675 return wxApp::OnRun();
681 bool CamuleDaemonApp::OnInit()
683 AddLogLineNS(_("amuled: OnInit - starting timer"));
684 if ( !CamuleApp::OnInit() ) {
687 core_timer
= new CTimer(this,ID_CORE_TIMER_EVENT
);
688 core_timer
->Start(CORE_TIMER_PERIOD
);
689 glob_prefs
->GetCategory(0)->title
= GetCatTitle(thePrefs::GetAllcatType());
690 glob_prefs
->GetCategory(0)->path
= thePrefs::GetIncomingDir();
695 int CamuleDaemonApp::InitGui(bool ,wxString
&)
698 if ( !enable_daemon_fork
) {
701 AddLogLineNS(_("amuled: forking to background - see you"));
702 theLogger
.SetEnabledStdoutLog(false);
704 // fork to background and detach from controlling tty
705 // while redirecting stdout to /dev/null
707 for(int i_fd
= 0;i_fd
< 3; i_fd
++) {
710 int fd
= open("/dev/null",O_RDWR
);
711 if (dup(fd
)){} // prevent GCC warning
722 // Create a Pid file with the Pid of the Child, so any daemon-manager
723 // can easily manage the process
725 if (!PidFile
.IsEmpty()) {
726 wxString temp
= wxString::Format(wxT("%d\n"), pid
);
727 wxFFile
ff(PidFile
, wxT("w"));
732 AddLogLineNS(_("Cannot Create Pid File"));
742 int CamuleDaemonApp::OnExit()
746 * Stop all socket threads before entering
760 const int ERROR_BUFFER_LEN
= 256;
761 char errorBuffer
[ERROR_BUFFER_LEN
];
763 int ret
= sigaction(SIGCHLD
, &m_oldSignalChildAction
, NULL
);
765 strerror_r(errno
, errorBuffer
, ERROR_BUFFER_LEN
);
766 msg
<< wxT("CamuleDaemonApp::OnRun(): second sigaction() failed: ") <<
767 char2unicode(errorBuffer
) <<
769 AddLogLineM(true, msg
);
771 msg
<< wxT("CamuleDaemonApp::OnRun(): Uninstallation of SIGCHLD "
772 "callback with sigaction() succeeded.");
773 AddDebugLogLineM(false, logGeneral
, msg
);
777 // lfroen: delete socket threads
778 if (ECServerHandler
) {
784 return CamuleApp::OnExit();
788 int CamuleDaemonApp::ShowAlert(wxString msg
, wxString title
, int flags
)
790 if ( flags
| wxICON_ERROR
) {
791 title
= CFormat(_("ERROR: %s")) % title
;
793 AddLogLineCS(title
+ wxT(" ") + msg
);
795 return 0; // That's neither yes nor no, ok, cancel
798 // File_checked_for_headers