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
36 #include "Preferences.h" // Needed for CPreferences
37 #include "PartFile.h" // Needed for CPartFile
39 #include <common/Format.h>
40 #include "InternalEvents.h" // Needed for wxEVT_*
41 #include "ThreadTasks.h"
42 #include "GuiEvents.h" // Needed for EVT_MULE_NOTIFY
43 #include "Timer.h" // Needed for EVT_MULE_TIMER
45 #include "ClientUDPSocket.h" // Do_not_auto_remove (forward declaration not enough)
46 #include "ListenSocket.h" // Do_not_auto_remove (forward declaration not enough)
50 #ifdef HAVE_SYS_RESOURCE_H
51 #include <sys/resource.h> // Do_not_auto_remove
55 #ifdef HAVE_SYS_WAIT_H
56 #include <sys/wait.h> // Do_not_auto_remove
59 #include <wx/unix/execute.h>
62 BEGIN_EVENT_TABLE(CamuleDaemonApp
, wxAppConsole
)
68 EVT_SOCKET(ID_LISTENSOCKET_EVENT
, CamuleDaemonApp::ListenSocketHandler
)
70 // UDP Socket (servers)
71 EVT_SOCKET(ID_SERVERUDPSOCKET_EVENT
, CamuleDaemonApp::UDPSocketHandler
)
72 // UDP Socket (clients)
73 EVT_SOCKET(ID_CLIENTUDPSOCKET_EVENT
, CamuleDaemonApp::UDPSocketHandler
)
76 EVT_MULE_TIMER(ID_SERVER_RETRY_TIMER_EVENT
, CamuleDaemonApp::OnTCPTimer
)
79 EVT_MULE_TIMER(ID_CORE_TIMER_EVENT
, CamuleDaemonApp::OnCoreTimer
)
81 EVT_MULE_NOTIFY(CamuleDaemonApp::OnNotifyEvent
)
82 EVT_MULE_LOGGING(CamuleDaemonApp::OnLoggingEvent
)
85 EVT_MULE_INTERNAL(wxEVT_CORE_UDP_DNS_DONE
, -1, CamuleDaemonApp::OnUDPDnsDone
)
87 EVT_MULE_INTERNAL(wxEVT_CORE_SOURCE_DNS_DONE
, -1, CamuleDaemonApp::OnSourceDnsDone
)
89 EVT_MULE_INTERNAL(wxEVT_CORE_SERVER_DNS_DONE
, -1, CamuleDaemonApp::OnServerDnsDone
)
91 // Hash ended notifier
92 EVT_MULE_HASHING(CamuleDaemonApp::OnFinishedHashing
)
93 EVT_MULE_AICH_HASHING(CamuleDaemonApp::OnFinishedAICHHashing
)
95 // File completion ended notifier
96 EVT_MULE_FILE_COMPLETED(CamuleDaemonApp::OnFinishedCompletion
)
98 // HTTPDownload finished
99 EVT_MULE_INTERNAL(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD
, -1, CamuleDaemonApp::OnFinishedHTTPDownload
)
102 IMPLEMENT_APP(CamuleDaemonApp
)
105 * Socket handling in wxBase
110 int m_fds
[FD_SETSIZE
], m_fd_idx
[FD_SETSIZE
];
111 GSocket
*m_gsocks
[FD_SETSIZE
];
116 void AddSocket(GSocket
*);
117 void RemoveSocket(GSocket
*);
118 void FillSet(int &max_fd
);
120 void Detected(void (GSocket::*func
)());
122 fd_set
*Set() { return &m_set
; }
125 CSocketSet::CSocketSet()
128 for(int i
= 0; i
< FD_SETSIZE
; i
++) {
130 m_fd_idx
[i
] = 0xffff;
135 void CSocketSet::AddSocket(GSocket
*socket
)
139 int fd
= socket
->m_fd
;
145 wxASSERT( (fd
> 2) && (fd
< FD_SETSIZE
) );
147 if ( m_gsocks
[fd
] ) {
151 m_fd_idx
[fd
] = m_count
;
152 m_gsocks
[fd
] = socket
;
156 void CSocketSet::RemoveSocket(GSocket
*socket
)
160 int fd
= socket
->m_fd
;
166 wxASSERT( (fd
> 2) && (fd
< FD_SETSIZE
) );
168 int i
= m_fd_idx
[fd
];
172 wxASSERT(m_fds
[i
] == fd
);
173 m_fds
[i
] = m_fds
[m_count
-1];
175 m_fds
[m_count
-1] = 0;
176 m_fd_idx
[fd
] = 0xffff;
177 m_fd_idx
[m_fds
[i
]] = i
;
181 void CSocketSet::FillSet(int &max_fd
)
185 for(int i
= 0; i
< m_count
; i
++) {
186 FD_SET(m_fds
[i
], &m_set
);
187 if ( m_fds
[i
] > max_fd
) {
193 void CSocketSet::Detected(void (GSocket::*func
)())
195 for (int i
= 0; i
< m_count
; i
++) {
197 if ( FD_ISSET(fd
, &m_set
) ) {
198 GSocket
*socket
= m_gsocks
[fd
];
204 CAmuledGSocketFuncTable::CAmuledGSocketFuncTable() : m_lock(wxMUTEX_RECURSIVE
)
206 m_in_set
= new CSocketSet
;
207 m_out_set
= new CSocketSet
;
212 void CAmuledGSocketFuncTable::AddSocket(GSocket
*socket
, GSocketEvent event
)
214 wxMutexLocker
lock(m_lock
);
216 if ( event
== GSOCK_INPUT
) {
217 m_in_set
->AddSocket(socket
);
219 m_out_set
->AddSocket(socket
);
223 void CAmuledGSocketFuncTable::RemoveSocket(GSocket
*socket
, GSocketEvent event
)
225 wxMutexLocker
lock(m_lock
);
227 if ( event
== GSOCK_INPUT
) {
228 m_in_set
->RemoveSocket(socket
);
230 m_out_set
->RemoveSocket(socket
);
234 void CAmuledGSocketFuncTable::RunSelect()
236 wxMutexLocker
lock(m_lock
);
239 m_in_set
->FillSet(max_fd
);
240 m_out_set
->FillSet(max_fd
);
244 tv
.tv_usec
= 10000; // 10ms
246 int result
= select(max_fd
+ 1, m_in_set
->Set(), m_out_set
->Set(), 0, &tv
);
248 m_in_set
->Detected(&GSocket::Detected_Read
);
249 m_out_set
->Detected(&GSocket::Detected_Write
);
254 GSocketGUIFunctionsTable
*CDaemonAppTraits::GetSocketGUIFunctionsTable()
259 bool CAmuledGSocketFuncTable::OnInit()
264 void CAmuledGSocketFuncTable::OnExit()
268 bool CAmuledGSocketFuncTable::CanUseEventLoop()
271 * FIXME: (lfroen) Not sure whether it's right.
272 * I will review it later.
277 bool CAmuledGSocketFuncTable::Init_Socket(GSocket
*)
282 void CAmuledGSocketFuncTable::Destroy_Socket(GSocket
*)
286 void CAmuledGSocketFuncTable::Install_Callback(GSocket
*sock
, GSocketEvent e
)
291 void CAmuledGSocketFuncTable::Uninstall_Callback(GSocket
*sock
, GSocketEvent e
)
293 RemoveSocket(sock
, e
);
296 void CAmuledGSocketFuncTable::Enable_Events(GSocket
*socket
)
298 Install_Callback(socket
, GSOCK_INPUT
);
299 Install_Callback(socket
, GSOCK_OUTPUT
);
302 void CAmuledGSocketFuncTable::Disable_Events(GSocket
*socket
)
304 Uninstall_Callback(socket
, GSOCK_INPUT
);
305 Uninstall_Callback(socket
, GSOCK_OUTPUT
);
309 CDaemonAppTraits::CDaemonAppTraits(CAmuledGSocketFuncTable
*table
)
311 wxConsoleAppTraits(),
313 m_lock(wxMUTEX_RECURSIVE
),
315 m_oldSignalChildAction(),
316 m_newSignalChildAction()
322 void CDaemonAppTraits::ScheduleForDestroy(wxObject
*object
)
324 wxMutexLocker
lock(m_lock
);
327 m_sched_delete
.push_back(object
);
330 void CDaemonAppTraits::RemoveFromPendingDelete(wxObject
*object
)
332 wxMutexLocker
lock(m_lock
);
334 for(std::list
<wxObject
*>::iterator i
= m_sched_delete
.begin();
335 i
!= m_sched_delete
.end(); i
++) {
336 if ( *i
== object
) {
337 m_sched_delete
.erase(i
);
343 void CDaemonAppTraits::DeletePending()
345 wxMutexLocker
lock(m_lock
);
347 while ( !m_sched_delete
.empty() ) {
348 std::list
<wxObject
*>::iterator i
= m_sched_delete
.begin();
349 wxObject
*object
= *i
;
352 //m_sched_delete.erase(m_sched_delete.begin(), m_sched_delete.end());
357 #include <wx/stdpaths.h> // Do_not_auto_remove (guess)
358 static wxStandardPathsCF gs_stdPaths
;
359 wxStandardPathsBase
& CDaemonAppTraits::GetStandardPaths()
366 CamuleDaemonApp::CamuleDaemonApp()
369 m_table(new CAmuledGSocketFuncTable())
371 wxPendingEventsLocker
= new wxCriticalSection
;
375 wxAppTraits
*CamuleDaemonApp::CreateTraits()
377 return new CDaemonAppTraits(m_table
);
384 static EndProcessDataMap endProcDataMap
;
387 int CDaemonAppTraits::WaitForChild(wxExecuteData
&execData
)
391 // Build the log message
393 msg
<< wxT("WaitForChild() has been called for child process with pid `") <<
397 if (execData
.flags
& wxEXEC_SYNC
) {
398 result
= AmuleWaitPid(execData
.pid
, &status
, 0, &msg
);
399 if (result
== -1 || (!WIFEXITED(status
) && !WIFSIGNALED(status
))) {
400 msg
<< wxT(" Waiting for subprocess termination failed.");
401 AddDebugLogLineM(false, logGeneral
, msg
);
405 // Give the process a chance to start or forked child to exit
406 // 1 second is enough time to fail on "path not found"
408 result
= AmuleWaitPid(execData
.pid
, &status
, WNOHANG
, &msg
);
410 // Add a WxEndProcessData entry to the map, so that we can
411 // support process termination
412 wxEndProcessData
*endProcData
= new wxEndProcessData();
413 endProcData
->pid
= execData
.pid
;
414 endProcData
->process
= execData
.process
;
415 endProcData
->tag
= 0;
416 endProcDataMap
[execData
.pid
] = endProcData
;
418 status
= execData
.pid
;
420 // if result != 0, then either waitpid() failed (result == -1)
421 // and there is nothing we can do, or the child has changed
422 // status, which means it is probably dead.
427 // Log our passage here
428 AddDebugLogLineM(false, logGeneral
, msg
);
434 void OnSignalChildHandler(int /*signal*/, siginfo_t
*siginfo
, void * /*ucontext*/)
436 // Build the log message
438 msg
<< wxT("OnSignalChildHandler() has been called for child process with pid `") <<
441 // Make sure we leave no zombies by calling waitpid()
443 pid_t result
= AmuleWaitPid(siginfo
->si_pid
, &status
, WNOHANG
, &msg
);
444 if (result
!= 1 && result
!= 0 && (WIFEXITED(status
) || WIFSIGNALED(status
))) {
445 // Fetch the wxEndProcessData structure corresponding to this pid
446 EndProcessDataMap::iterator it
= endProcDataMap
.find(siginfo
->si_pid
);
447 if (it
!= endProcDataMap
.end()) {
448 wxEndProcessData
*endProcData
= it
->second
;
449 // Remove this entry from the process map
450 endProcDataMap
.erase(siginfo
->si_pid
);
451 // Save the exit code for the wxProcess object to read later
452 endProcData
->exitcode
= result
!= -1 && WIFEXITED(status
) ?
453 WEXITSTATUS(status
) : -1;
454 // Make things work as in wxGUI
455 wxHandleProcessTermination(endProcData
);
457 // wxHandleProcessTermination() will "delete endProcData;"
458 // So we do not delete it again, ok? Do not uncomment this line.
459 //delete endProcData;
461 msg
<< wxT(" Error: the child process pid is not on the pid map.");
465 // Log our passage here
466 AddDebugLogLineM(false, logGeneral
, msg
);
470 pid_t
AmuleWaitPid(pid_t pid
, int *status
, int options
, wxString
*msg
)
472 // strerror_r() buffer
473 const int ERROR_BUFFER_LEN
= 256;
474 char errorBuffer
[ERROR_BUFFER_LEN
];
477 pid_t result
= waitpid(pid
, status
, options
);
479 strerror_r(errno
, errorBuffer
, ERROR_BUFFER_LEN
);
480 *msg
<< wxT("Error: waitpid() call failed: ") <<
481 char2unicode(errorBuffer
) <<
483 } else if (result
== 0) {
484 if (options
& WNOHANG
) {
485 *msg
<< wxT("The child is alive.");
487 *msg
<< wxT("Error: waitpid() call returned 0 but "
488 "WNOHANG was not specified in options.");
491 if (WIFEXITED(*status
)) {
492 *msg
<< wxT("Child has terminated with status code `") <<
493 WEXITSTATUS(*status
) <<
495 } else if (WIFSIGNALED(*status
)) {
496 *msg
<< wxT("Child was killed by signal `") <<
499 if (WCOREDUMP(*status
)) {
500 *msg
<< wxT(" A core file has been dumped.");
502 } else if (WIFSTOPPED(*status
)) {
503 *msg
<< wxT("Child has been stopped by signal `") <<
506 #ifdef WIFCONTINUED /* Only found in recent kernels. */
507 } else if (WIFCONTINUED(*status
)) {
508 *msg
<< wxT("Child has received `SIGCONT' and has continued execution.");
511 *msg
<< wxT("The program was not able to determine why the child has signaled.");
522 int CamuleDaemonApp::OnRun()
524 if (!thePrefs::AcceptExternalConnections()) {
525 wxString warning
= _("ERROR: aMule daemon cannot be used when external connections are disabled. "
526 "To enable External Connections, use either a normal aMule, start amuled with the option --ec-config or set the key"
527 "\"AcceptExternalConnections\" to 1 in the file ~/.aMule/amule.conf");
529 AddLogLineM(true, warning
);
530 printf("\n%s\n\n", (const char*)unicode2char(warning
));
533 } else if (thePrefs::ECPassword().IsEmpty()) {
534 wxString warning
= wxT("ERROR: A valid password is required to use "
535 "external connections, and aMule daemon cannot be used without "
536 "external connections. To run aMule deamon, you must set the "
537 "\"ECPassword\" field in the file ~/.aMule/amule.conf with an "
538 "appropriate value. Execute amuled with the flag --ec-config to set the password. More information can be found at "
539 "http://wiki.amule.org");
541 AddLogLineM(true, warning
);
542 printf("\n%s\n\n", (const char*)unicode2char(warning
));
548 // strerror_r() buffer
549 const int ERROR_BUFFER_LEN
= 256;
550 char errorBuffer
[ERROR_BUFFER_LEN
];
553 // Process the return code of dead children so that we do not create
554 // zombies. wxBase does not implement wxProcess callbacks, so no one
555 // actualy calls wxHandleProcessTermination() in console applications.
556 // We do our best here.
558 ret
= sigaction(SIGCHLD
, NULL
, &m_oldSignalChildAction
);
559 m_newSignalChildAction
= m_oldSignalChildAction
;
560 m_newSignalChildAction
.sa_sigaction
= OnSignalChildHandler
;
561 m_newSignalChildAction
.sa_flags
|= SA_SIGINFO
;
562 m_newSignalChildAction
.sa_flags
&= ~SA_RESETHAND
;
563 ret
= sigaction(SIGCHLD
, &m_newSignalChildAction
, NULL
);
565 strerror_r(errno
, errorBuffer
, ERROR_BUFFER_LEN
);
566 msg
<< wxT("CamuleDaemonApp::OnRun(): "
567 "Installation of SIGCHLD callback with sigaction() failed: ") <<
568 char2unicode(errorBuffer
) <<
570 AddLogLineM(true, msg
);
572 msg
<< wxT("CamuleDaemonApp::OnRun(): Installation of SIGCHLD "
573 "callback with sigaction() succeeded.");
574 AddDebugLogLineM(false, logGeneral
, msg
);
579 m_table
->RunSelect();
580 ProcessPendingEvents();
581 ((CDaemonAppTraits
*)GetTraits())->DeletePending();
584 // ShutDown is beeing called twice. Once here and again in OnExit().
589 ret
= sigaction(SIGCHLD
, &m_oldSignalChildAction
, NULL
);
591 strerror_r(errno
, errorBuffer
, ERROR_BUFFER_LEN
);
592 msg
<< wxT("CamuleDaemonApp::OnRun(): second sigaction() failed: ") <<
593 char2unicode(errorBuffer
) <<
595 AddLogLineM(true, msg
);
597 msg
<< wxT("CamuleDaemonApp::OnRun(): Uninstallation of SIGCHLD "
598 "callback with sigaction() succeeded.");
599 AddDebugLogLineM(false, logGeneral
, msg
);
606 bool CamuleDaemonApp::OnInit()
608 printf("amuled: OnInit - starting timer\n");
609 if ( !CamuleApp::OnInit() ) {
612 core_timer
= new CTimer(this,ID_CORE_TIMER_EVENT
);
613 core_timer
->Start(300);
614 glob_prefs
->GetCategory(0)->title
= GetCatTitle(thePrefs::GetAllcatType());
615 glob_prefs
->GetCategory(0)->path
= thePrefs::GetIncomingDir();
620 int CamuleDaemonApp::InitGui(bool ,wxString
&)
623 if ( !enable_daemon_fork
) {
626 printf("amuled: forking to background - see you\n");
628 // fork to background and detouch from controlling tty
629 // while redirecting stdout to /dev/null
631 for(int i_fd
= 0;i_fd
< 3; i_fd
++) {
634 int fd
= open("/dev/null",O_RDWR
);
652 int CamuleDaemonApp::OnExit()
655 * Stop all socket threads before entering
667 // lfroen: delete socket threads
668 if (ECServerHandler
) {
674 return CamuleApp::OnExit();
678 void CamuleDaemonApp::ShowAlert(wxString msg
, wxString title
, int flags
)
680 if ( flags
| wxICON_ERROR
) {
681 title
= CFormat(_("ERROR: %s")) % title
;
684 // Ensure that alerts are always visible on the console (when possible).
685 if ((not enable_stdout_log
) and (not enable_daemon_fork
)) {
686 printf("%s\n", unicode2UTF8(title
+ wxT(" ") + msg
).data());
689 AddLogLineM(true, title
+ wxT(" ") + msg
);
693 void CamuleDaemonApp::OnLoggingEvent(CLoggingEvent
& evt
)
695 CamuleApp::AddLogLine(evt
.Message());
698 // File_checked_for_headers