Upstream tarball 9401
[amule.git] / src / amuled.cpp
blob37336478b884ff2c6bfd2d6aa4c26f9ab9f32268
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 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.
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
26 #include "amule.h" // Interface declarations.
28 #include <include/common/EventIDs.h>
30 // amuled doesn't run on Windows.
31 // To make it at least compile some code has to be excluded.
32 // For a real port all usages of TODO_MSW have to be resolved !
33 #ifdef __WXMSW__
34 #define TODO_MSW 1
35 #endif
37 #ifdef HAVE_CONFIG_H
38 #include "config.h" // Needed for HAVE_SYS_RESOURCE_H, HAVE_STRERROR_R and STRERROR_R_CHAR_P
39 #endif
41 // Prefer the POSIX interface to strerror_r()
42 #define _XOPEN_SOURCE 600
43 #include <string.h> // Do_not_auto_remove
45 #include <wx/utils.h>
47 #include "Preferences.h" // Needed for CPreferences
48 #include "PartFile.h" // Needed for CPartFile
49 #include "Logger.h"
50 #include <common/Format.h>
51 #include "InternalEvents.h" // Needed for wxEVT_*
52 #include "ThreadTasks.h"
53 #include "GuiEvents.h" // Needed for EVT_MULE_NOTIFY
54 #include "Timer.h" // Needed for EVT_MULE_TIMER
56 #include "ClientUDPSocket.h" // Do_not_auto_remove (forward declaration not enough)
57 #include "ListenSocket.h" // Do_not_auto_remove (forward declaration not enough)
60 #include <errno.h>
61 #ifdef HAVE_SYS_RESOURCE_H
62 #include <sys/resource.h> // Do_not_auto_remove
63 #endif
65 #ifndef __WXMSW__
66 #ifdef HAVE_SYS_WAIT_H
67 #include <sys/wait.h> // Do_not_auto_remove
68 #endif
70 #include <wx/unix/execute.h>
71 #endif
73 #ifndef HAVE_STRERROR_R
75 // Replacement strerror_r() function for systems that don't have any.
76 // Note that this replacement function is NOT thread-safe!
77 static int rpl_strerror_r(int errnum, char *buf, size_t buflen)
79 char *tmp = strerror(errnum);
80 if (tmp == NULL) {
81 errno = EINVAL;
82 return -1;
83 } else {
84 strncpy(buf, tmp, buflen - 1);
85 buf[buflen - 1] = '\0';
86 if (strlen(tmp) >= buflen) {
87 errno = ERANGE;
88 return -1;
91 return 0;
94 # define strerror_r(errnum, buf, buflen) rpl_strerror_r(errnum, buf, buflen)
95 #else
96 # ifdef STRERROR_R_CHAR_P
98 // Replacement strerror_r() function for systems that return a char*.
99 static int rpl_strerror_r(int errnum, char *buf, size_t buflen)
101 char *tmp = strerror_r(errnum, buf, buflen);
102 if (tmp == NULL) {
103 errno = EINVAL;
104 return -1;
105 } else if (tmp != buf) {
106 strncpy(buf, tmp, buflen - 1);
107 buf[buflen - 1] = '\0';
108 if (strlen(tmp) >= buflen) {
109 errno = ERANGE;
110 return -1;
113 return 0;
116 # define strerror_r(errnum, buf, buflen) rpl_strerror_r(errnum, buf, buflen)
117 # endif
118 #endif
120 BEGIN_EVENT_TABLE(CamuleDaemonApp, wxAppConsole)
122 // Socket handlers
125 // Listen Socket
126 EVT_SOCKET(ID_LISTENSOCKET_EVENT, CamuleDaemonApp::ListenSocketHandler)
128 // UDP Socket (servers)
129 EVT_SOCKET(ID_SERVERUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
130 // UDP Socket (clients)
131 EVT_SOCKET(ID_CLIENTUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
133 // Socket timer (TCP)
134 EVT_MULE_TIMER(ID_SERVER_RETRY_TIMER_EVENT, CamuleDaemonApp::OnTCPTimer)
136 // Core timer
137 EVT_MULE_TIMER(ID_CORE_TIMER_EVENT, CamuleDaemonApp::OnCoreTimer)
139 EVT_MULE_NOTIFY(CamuleDaemonApp::OnNotifyEvent)
141 // Async dns handling
142 EVT_MULE_INTERNAL(wxEVT_CORE_UDP_DNS_DONE, -1, CamuleDaemonApp::OnUDPDnsDone)
144 EVT_MULE_INTERNAL(wxEVT_CORE_SOURCE_DNS_DONE, -1, CamuleDaemonApp::OnSourceDnsDone)
146 EVT_MULE_INTERNAL(wxEVT_CORE_SERVER_DNS_DONE, -1, CamuleDaemonApp::OnServerDnsDone)
148 // Hash ended notifier
149 EVT_MULE_HASHING(CamuleDaemonApp::OnFinishedHashing)
150 EVT_MULE_AICH_HASHING(CamuleDaemonApp::OnFinishedAICHHashing)
152 // File completion ended notifier
153 EVT_MULE_FILE_COMPLETED(CamuleDaemonApp::OnFinishedCompletion)
155 // HTTPDownload finished
156 EVT_MULE_INTERNAL(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD, -1, CamuleDaemonApp::OnFinishedHTTPDownload)
158 // Disk space preallocation finished
159 EVT_MULE_ALLOC_FINISHED(CamuleDaemonApp::OnFinishedAllocation)
160 END_EVENT_TABLE()
162 IMPLEMENT_APP(CamuleDaemonApp)
165 * Socket handling in wxBase
168 class CSocketSet {
169 int m_count;
170 int m_fds[FD_SETSIZE], m_fd_idx[FD_SETSIZE];
171 GSocket *m_gsocks[FD_SETSIZE];
173 fd_set m_set;
174 public:
175 CSocketSet();
176 void AddSocket(GSocket *);
177 void RemoveSocket(GSocket *);
178 void FillSet(int &max_fd);
180 void Detected(void (GSocket::*func)());
182 fd_set *Set() { return &m_set; }
185 CSocketSet::CSocketSet()
187 m_count = 0;
188 for(int i = 0; i < FD_SETSIZE; i++) {
189 m_fds[i] = 0;
190 m_fd_idx[i] = 0xffff;
191 m_gsocks[i] = 0;
195 void CSocketSet::AddSocket(GSocket *socket)
197 wxASSERT(socket);
199 int fd = socket->m_fd;
201 if ( fd == -1 ) {
202 return;
205 wxASSERT( (fd > 2) && (fd < FD_SETSIZE) );
207 if ( m_gsocks[fd] ) {
208 return;
210 m_fds[m_count] = fd;
211 m_fd_idx[fd] = m_count;
212 m_gsocks[fd] = socket;
213 m_count++;
216 void CSocketSet::RemoveSocket(GSocket *socket)
218 wxASSERT(socket);
220 int fd = socket->m_fd;
222 if ( fd == -1 ) {
223 return;
226 wxASSERT( (fd > 2) && (fd < FD_SETSIZE) );
228 int i = m_fd_idx[fd];
229 if ( i == 0xffff ) {
230 return;
232 wxASSERT(m_fds[i] == fd);
233 m_fds[i] = m_fds[m_count-1];
234 m_gsocks[fd] = 0;
235 m_fds[m_count-1] = 0;
236 m_fd_idx[fd] = 0xffff;
237 m_fd_idx[m_fds[i]] = i;
238 m_count--;
241 void CSocketSet::FillSet(int &max_fd)
243 FD_ZERO(&m_set);
245 for(int i = 0; i < m_count; i++) {
246 FD_SET(m_fds[i], &m_set);
247 if ( m_fds[i] > max_fd ) {
248 max_fd = m_fds[i];
253 void CSocketSet::Detected(void (GSocket::*func)())
255 for (int i = 0; i < m_count; i++) {
256 int fd = m_fds[i];
257 if ( FD_ISSET(fd, &m_set) ) {
258 GSocket *socket = m_gsocks[fd];
259 (*socket.*func)();
264 CAmuledGSocketFuncTable::CAmuledGSocketFuncTable() : m_lock(wxMUTEX_RECURSIVE)
266 m_in_set = new CSocketSet;
267 m_out_set = new CSocketSet;
269 m_lock.Unlock();
272 void CAmuledGSocketFuncTable::AddSocket(GSocket *socket, GSocketEvent event)
274 wxMutexLocker lock(m_lock);
276 if ( event == GSOCK_INPUT ) {
277 m_in_set->AddSocket(socket);
278 } else {
279 m_out_set->AddSocket(socket);
283 void CAmuledGSocketFuncTable::RemoveSocket(GSocket *socket, GSocketEvent event)
285 wxMutexLocker lock(m_lock);
287 if ( event == GSOCK_INPUT ) {
288 m_in_set->RemoveSocket(socket);
289 } else {
290 m_out_set->RemoveSocket(socket);
294 void CAmuledGSocketFuncTable::RunSelect()
296 #ifndef TODO_MSW
297 // This is why it doesn't work on Windows
298 wxMutexLocker lock(m_lock);
300 int max_fd = -1;
301 m_in_set->FillSet(max_fd);
302 m_out_set->FillSet(max_fd);
304 struct timeval tv;
305 tv.tv_sec = 0;
306 tv.tv_usec = 10000; // 10ms
308 int result = select(max_fd + 1, m_in_set->Set(), m_out_set->Set(), 0, &tv);
309 if ( result > 0 ) {
310 m_in_set->Detected(&GSocket::Detected_Read);
311 m_out_set->Detected(&GSocket::Detected_Write);
314 #endif
317 GSocketGUIFunctionsTable *CDaemonAppTraits::GetSocketGUIFunctionsTable()
319 return m_table;
322 bool CAmuledGSocketFuncTable::OnInit()
324 return true;
327 void CAmuledGSocketFuncTable::OnExit()
331 bool CAmuledGSocketFuncTable::CanUseEventLoop()
334 * FIXME: (lfroen) Not sure whether it's right.
335 * I will review it later.
337 return false;
340 bool CAmuledGSocketFuncTable::Init_Socket(GSocket *)
342 return true;
345 void CAmuledGSocketFuncTable::Destroy_Socket(GSocket *)
349 void CAmuledGSocketFuncTable::Install_Callback(GSocket *sock, GSocketEvent e)
351 AddSocket(sock, e);
354 void CAmuledGSocketFuncTable::Uninstall_Callback(GSocket *sock, GSocketEvent e)
356 RemoveSocket(sock, e);
359 void CAmuledGSocketFuncTable::Enable_Events(GSocket *socket)
361 Install_Callback(socket, GSOCK_INPUT);
362 Install_Callback(socket, GSOCK_OUTPUT);
365 void CAmuledGSocketFuncTable::Disable_Events(GSocket *socket)
367 Uninstall_Callback(socket, GSOCK_INPUT);
368 Uninstall_Callback(socket, GSOCK_OUTPUT);
372 CDaemonAppTraits::CDaemonAppTraits(CAmuledGSocketFuncTable *table)
374 wxConsoleAppTraits(),
375 m_table(table),
376 m_lock(wxMUTEX_RECURSIVE),
377 m_sched_delete()
378 #ifndef TODO_MSW
379 ,m_oldSignalChildAction(),
380 m_newSignalChildAction()
381 #endif
383 m_lock.Unlock();
387 void CDaemonAppTraits::ScheduleForDestroy(wxObject *object)
389 wxMutexLocker lock(m_lock);
391 //delete object;
392 m_sched_delete.push_back(object);
395 void CDaemonAppTraits::RemoveFromPendingDelete(wxObject *object)
397 wxMutexLocker lock(m_lock);
399 for(std::list<wxObject *>::iterator i = m_sched_delete.begin();
400 i != m_sched_delete.end(); i++) {
401 if ( *i == object ) {
402 m_sched_delete.erase(i);
403 return;
408 void CDaemonAppTraits::DeletePending()
410 wxMutexLocker lock(m_lock);
412 while ( !m_sched_delete.empty() ) {
413 std::list<wxObject *>::iterator i = m_sched_delete.begin();
414 wxObject *object = *i;
415 delete object;
417 //m_sched_delete.erase(m_sched_delete.begin(), m_sched_delete.end());
421 #ifdef __WXMAC__
422 #include <wx/stdpaths.h> // Do_not_auto_remove (guess)
423 static wxStandardPathsCF gs_stdPaths;
424 wxStandardPathsBase& CDaemonAppTraits::GetStandardPaths()
426 return gs_stdPaths;
428 #endif
431 CamuleDaemonApp::CamuleDaemonApp()
433 m_Exit(false),
434 m_table(new CAmuledGSocketFuncTable())
436 wxPendingEventsLocker = new wxCriticalSection;
440 wxAppTraits *CamuleDaemonApp::CreateTraits()
442 return new CDaemonAppTraits(m_table);
446 #ifndef __WXMSW__
449 static EndProcessDataMap endProcDataMap;
452 int CDaemonAppTraits::WaitForChild(wxExecuteData &execData)
454 int status = 0;
455 pid_t result = 0;
456 // Build the log message
457 wxString msg;
458 msg << wxT("WaitForChild() has been called for child process with pid `") <<
459 execData.pid <<
460 wxT("'. ");
462 if (execData.flags & wxEXEC_SYNC) {
463 result = AmuleWaitPid(execData.pid, &status, 0, &msg);
464 if (result == -1 || (!WIFEXITED(status) && !WIFSIGNALED(status))) {
465 msg << wxT(" Waiting for subprocess termination failed.");
466 AddDebugLogLineM(false, logGeneral, msg);
468 } else {
469 /** wxEXEC_ASYNC */
470 // Give the process a chance to start or forked child to exit
471 // 1 second is enough time to fail on "path not found"
472 wxSleep(1);
473 result = AmuleWaitPid(execData.pid, &status, WNOHANG, &msg);
474 if (result == 0) {
475 // Add a WxEndProcessData entry to the map, so that we can
476 // support process termination
477 wxEndProcessData *endProcData = new wxEndProcessData();
478 endProcData->pid = execData.pid;
479 endProcData->process = execData.process;
480 endProcData->tag = 0;
481 endProcDataMap[execData.pid] = endProcData;
483 status = execData.pid;
484 } else {
485 // if result != 0, then either waitpid() failed (result == -1)
486 // and there is nothing we can do, or the child has changed
487 // status, which means it is probably dead.
488 status = 0;
492 // Log our passage here
493 AddDebugLogLineM(false, logGeneral, msg);
495 return status;
499 void OnSignalChildHandler(int /*signal*/, siginfo_t *siginfo, void * /*ucontext*/)
501 // Build the log message
502 wxString msg;
503 msg << wxT("OnSignalChildHandler() has been called for child process with pid `") <<
504 siginfo->si_pid <<
505 wxT("'. ");
506 // Make sure we leave no zombies by calling waitpid()
507 int status = 0;
508 pid_t result = AmuleWaitPid(siginfo->si_pid, &status, WNOHANG, &msg);
509 if (result != 1 && result != 0 && (WIFEXITED(status) || WIFSIGNALED(status))) {
510 // Fetch the wxEndProcessData structure corresponding to this pid
511 EndProcessDataMap::iterator it = endProcDataMap.find(siginfo->si_pid);
512 if (it != endProcDataMap.end()) {
513 wxEndProcessData *endProcData = it->second;
514 // Remove this entry from the process map
515 endProcDataMap.erase(siginfo->si_pid);
516 // Save the exit code for the wxProcess object to read later
517 endProcData->exitcode = result != -1 && WIFEXITED(status) ?
518 WEXITSTATUS(status) : -1;
519 // Make things work as in wxGUI
520 wxHandleProcessTermination(endProcData);
522 // wxHandleProcessTermination() will "delete endProcData;"
523 // So we do not delete it again, ok? Do not uncomment this line.
524 //delete endProcData;
525 } else {
526 msg << wxT(" Error: the child process pid is not on the pid map.");
530 // Log our passage here
531 AddDebugLogLineM(false, logGeneral, msg);
535 pid_t AmuleWaitPid(pid_t pid, int *status, int options, wxString *msg)
537 // strerror_r() buffer
538 const int ERROR_BUFFER_LEN = 256;
539 char errorBuffer[ERROR_BUFFER_LEN];
541 *status = 0;
542 pid_t result = waitpid(pid, status, options);
543 if (result == -1) {
544 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
545 *msg << wxT("Error: waitpid() call failed: ") <<
546 char2unicode(errorBuffer) <<
547 wxT(".");
548 } else if (result == 0) {
549 if (options & WNOHANG) {
550 *msg << wxT("The child is alive.");
551 } else {
552 *msg << wxT("Error: waitpid() call returned 0 but "
553 "WNOHANG was not specified in options.");
555 } else {
556 if (WIFEXITED(*status)) {
557 *msg << wxT("Child has terminated with status code `") <<
558 WEXITSTATUS(*status) <<
559 wxT("'.");
560 } else if (WIFSIGNALED(*status)) {
561 *msg << wxT("Child was killed by signal `") <<
562 WTERMSIG(*status) <<
563 wxT("'.");
564 if (WCOREDUMP(*status)) {
565 *msg << wxT(" A core file has been dumped.");
567 } else if (WIFSTOPPED(*status)) {
568 *msg << wxT("Child has been stopped by signal `") <<
569 WSTOPSIG(*status) <<
570 wxT("'.");
571 #ifdef WIFCONTINUED /* Only found in recent kernels. */
572 } else if (WIFCONTINUED(*status)) {
573 *msg << wxT("Child has received `SIGCONT' and has continued execution.");
574 #endif
575 } else {
576 *msg << wxT("The program was not able to determine why the child has signaled.");
580 return result;
584 #endif // __WXMSW__
587 int CamuleDaemonApp::OnRun()
589 if (!thePrefs::AcceptExternalConnections()) {
590 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"));
591 return 0;
592 } else if (thePrefs::ECPassword().IsEmpty()) {
593 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"));
594 return 0;
597 #ifndef __WXMSW__
598 // strerror_r() buffer
599 const int ERROR_BUFFER_LEN = 256;
600 char errorBuffer[ERROR_BUFFER_LEN];
601 wxString msg;
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 int ret = 0;
608 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 ret = sigaction(SIGCHLD, &m_newSignalChildAction, NULL);
614 if (ret == -1) {
615 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
616 msg << wxT("CamuleDaemonApp::OnRun(): "
617 "Installation of SIGCHLD callback with sigaction() failed: ") <<
618 char2unicode(errorBuffer) <<
619 wxT(".");
620 AddLogLineM(true, msg);
621 } else {
622 msg << wxT("CamuleDaemonApp::OnRun(): Installation of SIGCHLD "
623 "callback with sigaction() succeeded.");
624 AddDebugLogLineM(false, logGeneral, msg);
626 #endif // __WXMSW__
628 while ( !m_Exit ) {
629 m_table->RunSelect();
630 ProcessPendingEvents();
631 ((CDaemonAppTraits *)GetTraits())->DeletePending();
634 // ShutDown is beeing called twice. Once here and again in OnExit().
635 ShutDown();
637 #ifndef __WXMSW__
638 msg.Empty();
639 ret = sigaction(SIGCHLD, &m_oldSignalChildAction, NULL);
640 if (ret == -1) {
641 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
642 msg << wxT("CamuleDaemonApp::OnRun(): second sigaction() failed: ") <<
643 char2unicode(errorBuffer) <<
644 wxT(".");
645 AddLogLineM(true, msg);
646 } else {
647 msg << wxT("CamuleDaemonApp::OnRun(): Uninstallation of SIGCHLD "
648 "callback with sigaction() succeeded.");
649 AddDebugLogLineM(false, logGeneral, msg);
651 #endif // __WXMSW__
653 return 0;
656 bool CamuleDaemonApp::OnInit()
658 AddLogLineNS(_("amuled: OnInit - starting timer"));
659 if ( !CamuleApp::OnInit() ) {
660 return false;
662 core_timer = new CTimer(this,ID_CORE_TIMER_EVENT);
663 core_timer->Start(300);
664 glob_prefs->GetCategory(0)->title = GetCatTitle(thePrefs::GetAllcatType());
665 glob_prefs->GetCategory(0)->path = thePrefs::GetIncomingDir();
667 return true;
670 int CamuleDaemonApp::InitGui(bool ,wxString &)
672 #ifndef __WXMSW__
673 if ( !enable_daemon_fork ) {
674 return 0;
676 AddLogLineNS(_("amuled: forking to background - see you"));
677 theLogger.SetEnabledStdoutLog(false);
679 // fork to background and detouch from controlling tty
680 // while redirecting stdout to /dev/null
682 for(int i_fd = 0;i_fd < 3; i_fd++) {
683 close(i_fd);
685 int fd = open("/dev/null",O_RDWR);
686 dup(fd);
687 dup(fd);
688 int pid = fork();
690 wxASSERT(pid != -1);
692 if ( pid ) {
693 exit(0);
694 } else {
695 setsid();
698 #endif
699 return 0;
703 int CamuleDaemonApp::OnExit()
706 * Stop all socket threads before entering
707 * shutdown sequence.
709 delete listensocket;
710 listensocket = 0;
711 if (clientudp) {
712 delete clientudp;
713 clientudp = NULL;
716 ShutDown();
718 // lfroen: delete socket threads
719 if (ECServerHandler) {
720 ECServerHandler = 0;
723 delete core_timer;
725 return CamuleApp::OnExit();
729 void CamuleDaemonApp::ShowAlert(wxString msg, wxString title, int flags)
731 if ( flags | wxICON_ERROR ) {
732 title = CFormat(_("ERROR: %s")) % title;
734 AddLogLineCS(title + wxT(" ") + msg);
737 // File_checked_for_headers