Upstream tarball 9452
[amule.git] / src / amuled.cpp
blob02aeaa26f0525f0102b4ce1cb45a9610812160b2
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, etc
39 #endif
41 // Include the necessary headers for select(2), properly guarded
42 #if defined HAVE_SYS_SELECT_H && !defined __IRIX__
43 # include <sys/select.h>
44 #else
45 # ifdef HAVE_SYS_TIME_H
46 # include <sys/time.h>
47 # endif
48 # ifdef HAVE_SYS_TYPES_H
49 # include <sys/types.h>
50 # endif
51 # ifdef HAVE_UNISTD_H
52 # include <unistd.h>
53 # endif
54 #endif
56 // Prefer the POSIX interface to strerror_r()
57 #define _XOPEN_SOURCE 600
58 #include <string.h> // Do_not_auto_remove
60 #include <wx/utils.h>
62 #include "Preferences.h" // Needed for CPreferences
63 #include "PartFile.h" // Needed for CPartFile
64 #include "Logger.h"
65 #include <common/Format.h>
66 #include "InternalEvents.h" // Needed for wxEVT_*
67 #include "ThreadTasks.h"
68 #include "GuiEvents.h" // Needed for EVT_MULE_NOTIFY
69 #include "Timer.h" // Needed for EVT_MULE_TIMER
71 #include "ClientUDPSocket.h" // Do_not_auto_remove (forward declaration not enough)
72 #include "ListenSocket.h" // Do_not_auto_remove (forward declaration not enough)
75 #include <errno.h>
76 #ifdef HAVE_SYS_RESOURCE_H
77 #include <sys/resource.h> // Do_not_auto_remove
78 #endif
80 #ifndef __WXMSW__
81 #ifdef HAVE_SYS_WAIT_H
82 #include <sys/wait.h> // Do_not_auto_remove
83 #endif
85 #include <wx/unix/execute.h>
86 #endif
88 #ifndef HAVE_STRERROR_R
90 // Replacement strerror_r() function for systems that don't have any.
91 // Note that this replacement function is NOT thread-safe!
92 static int rpl_strerror_r(int errnum, char *buf, size_t buflen)
94 char *tmp = strerror(errnum);
95 if (tmp == NULL) {
96 errno = EINVAL;
97 return -1;
98 } else {
99 strncpy(buf, tmp, buflen - 1);
100 buf[buflen - 1] = '\0';
101 if (strlen(tmp) >= buflen) {
102 errno = ERANGE;
103 return -1;
106 return 0;
109 # define strerror_r(errnum, buf, buflen) rpl_strerror_r(errnum, buf, buflen)
110 #else
111 # ifdef STRERROR_R_CHAR_P
113 // Replacement strerror_r() function for systems that return a char*.
114 static int rpl_strerror_r(int errnum, char *buf, size_t buflen)
116 char *tmp = strerror_r(errnum, buf, buflen);
117 if (tmp == NULL) {
118 errno = EINVAL;
119 return -1;
120 } else if (tmp != buf) {
121 strncpy(buf, tmp, buflen - 1);
122 buf[buflen - 1] = '\0';
123 if (strlen(tmp) >= buflen) {
124 errno = ERANGE;
125 return -1;
128 return 0;
131 # define strerror_r(errnum, buf, buflen) rpl_strerror_r(errnum, buf, buflen)
132 # endif
133 #endif
135 BEGIN_EVENT_TABLE(CamuleDaemonApp, wxAppConsole)
137 // Socket handlers
140 // Listen Socket
141 EVT_SOCKET(ID_LISTENSOCKET_EVENT, CamuleDaemonApp::ListenSocketHandler)
143 // UDP Socket (servers)
144 EVT_SOCKET(ID_SERVERUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
145 // UDP Socket (clients)
146 EVT_SOCKET(ID_CLIENTUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
148 // Socket timer (TCP)
149 EVT_MULE_TIMER(ID_SERVER_RETRY_TIMER_EVENT, CamuleDaemonApp::OnTCPTimer)
151 // Core timer
152 EVT_MULE_TIMER(ID_CORE_TIMER_EVENT, CamuleDaemonApp::OnCoreTimer)
154 EVT_MULE_NOTIFY(CamuleDaemonApp::OnNotifyEvent)
156 // Async dns handling
157 EVT_MULE_INTERNAL(wxEVT_CORE_UDP_DNS_DONE, -1, CamuleDaemonApp::OnUDPDnsDone)
159 EVT_MULE_INTERNAL(wxEVT_CORE_SOURCE_DNS_DONE, -1, CamuleDaemonApp::OnSourceDnsDone)
161 EVT_MULE_INTERNAL(wxEVT_CORE_SERVER_DNS_DONE, -1, CamuleDaemonApp::OnServerDnsDone)
163 // Hash ended notifier
164 EVT_MULE_HASHING(CamuleDaemonApp::OnFinishedHashing)
165 EVT_MULE_AICH_HASHING(CamuleDaemonApp::OnFinishedAICHHashing)
167 // File completion ended notifier
168 EVT_MULE_FILE_COMPLETED(CamuleDaemonApp::OnFinishedCompletion)
170 // HTTPDownload finished
171 EVT_MULE_INTERNAL(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD, -1, CamuleDaemonApp::OnFinishedHTTPDownload)
173 // Disk space preallocation finished
174 EVT_MULE_ALLOC_FINISHED(CamuleDaemonApp::OnFinishedAllocation)
175 END_EVENT_TABLE()
177 IMPLEMENT_APP(CamuleDaemonApp)
180 * Socket handling in wxBase
183 class CSocketSet {
184 int m_count;
185 int m_fds[FD_SETSIZE], m_fd_idx[FD_SETSIZE];
186 GSocket *m_gsocks[FD_SETSIZE];
188 fd_set m_set;
189 public:
190 CSocketSet();
191 void AddSocket(GSocket *);
192 void RemoveSocket(GSocket *);
193 void FillSet(int &max_fd);
195 void Detected(void (GSocket::*func)());
197 fd_set *Set() { return &m_set; }
200 CSocketSet::CSocketSet()
202 m_count = 0;
203 for(int i = 0; i < FD_SETSIZE; i++) {
204 m_fds[i] = 0;
205 m_fd_idx[i] = 0xffff;
206 m_gsocks[i] = 0;
210 void CSocketSet::AddSocket(GSocket *socket)
212 wxASSERT(socket);
214 int fd = socket->m_fd;
216 if ( fd == -1 ) {
217 return;
220 wxASSERT( (fd > 2) && (fd < FD_SETSIZE) );
222 if ( m_gsocks[fd] ) {
223 return;
225 m_fds[m_count] = fd;
226 m_fd_idx[fd] = m_count;
227 m_gsocks[fd] = socket;
228 m_count++;
231 void CSocketSet::RemoveSocket(GSocket *socket)
233 wxASSERT(socket);
235 int fd = socket->m_fd;
237 if ( fd == -1 ) {
238 return;
241 wxASSERT( (fd > 2) && (fd < FD_SETSIZE) );
243 int i = m_fd_idx[fd];
244 if ( i == 0xffff ) {
245 return;
247 wxASSERT(m_fds[i] == fd);
248 m_fds[i] = m_fds[m_count-1];
249 m_gsocks[fd] = 0;
250 m_fds[m_count-1] = 0;
251 m_fd_idx[fd] = 0xffff;
252 m_fd_idx[m_fds[i]] = i;
253 m_count--;
256 void CSocketSet::FillSet(int &max_fd)
258 FD_ZERO(&m_set);
260 for(int i = 0; i < m_count; i++) {
261 FD_SET(m_fds[i], &m_set);
262 if ( m_fds[i] > max_fd ) {
263 max_fd = m_fds[i];
268 void CSocketSet::Detected(void (GSocket::*func)())
270 for (int i = 0; i < m_count; i++) {
271 int fd = m_fds[i];
272 if ( FD_ISSET(fd, &m_set) ) {
273 GSocket *socket = m_gsocks[fd];
274 (*socket.*func)();
279 CAmuledGSocketFuncTable::CAmuledGSocketFuncTable() : m_lock(wxMUTEX_RECURSIVE)
281 m_in_set = new CSocketSet;
282 m_out_set = new CSocketSet;
284 m_lock.Unlock();
287 void CAmuledGSocketFuncTable::AddSocket(GSocket *socket, GSocketEvent event)
289 wxMutexLocker lock(m_lock);
291 if ( event == GSOCK_INPUT ) {
292 m_in_set->AddSocket(socket);
293 } else {
294 m_out_set->AddSocket(socket);
298 void CAmuledGSocketFuncTable::RemoveSocket(GSocket *socket, GSocketEvent event)
300 wxMutexLocker lock(m_lock);
302 if ( event == GSOCK_INPUT ) {
303 m_in_set->RemoveSocket(socket);
304 } else {
305 m_out_set->RemoveSocket(socket);
309 void CAmuledGSocketFuncTable::RunSelect()
311 #ifndef TODO_MSW
312 // This is why it doesn't work on Windows
313 wxMutexLocker lock(m_lock);
315 int max_fd = -1;
316 m_in_set->FillSet(max_fd);
317 m_out_set->FillSet(max_fd);
319 struct timeval tv;
320 tv.tv_sec = 0;
321 tv.tv_usec = 10000; // 10ms
323 int result = select(max_fd + 1, m_in_set->Set(), m_out_set->Set(), 0, &tv);
324 if ( result > 0 ) {
325 m_in_set->Detected(&GSocket::Detected_Read);
326 m_out_set->Detected(&GSocket::Detected_Write);
329 #endif
332 GSocketGUIFunctionsTable *CDaemonAppTraits::GetSocketGUIFunctionsTable()
334 return m_table;
337 bool CAmuledGSocketFuncTable::OnInit()
339 return true;
342 void CAmuledGSocketFuncTable::OnExit()
346 bool CAmuledGSocketFuncTable::CanUseEventLoop()
349 * FIXME: (lfroen) Not sure whether it's right.
350 * I will review it later.
352 return false;
355 bool CAmuledGSocketFuncTable::Init_Socket(GSocket *)
357 return true;
360 void CAmuledGSocketFuncTable::Destroy_Socket(GSocket *)
364 void CAmuledGSocketFuncTable::Install_Callback(GSocket *sock, GSocketEvent e)
366 AddSocket(sock, e);
369 void CAmuledGSocketFuncTable::Uninstall_Callback(GSocket *sock, GSocketEvent e)
371 RemoveSocket(sock, e);
374 void CAmuledGSocketFuncTable::Enable_Events(GSocket *socket)
376 Install_Callback(socket, GSOCK_INPUT);
377 Install_Callback(socket, GSOCK_OUTPUT);
380 void CAmuledGSocketFuncTable::Disable_Events(GSocket *socket)
382 Uninstall_Callback(socket, GSOCK_INPUT);
383 Uninstall_Callback(socket, GSOCK_OUTPUT);
387 CDaemonAppTraits::CDaemonAppTraits(CAmuledGSocketFuncTable *table)
389 wxConsoleAppTraits(),
390 m_table(table),
391 m_lock(wxMUTEX_RECURSIVE),
392 m_sched_delete()
393 #ifndef TODO_MSW
394 ,m_oldSignalChildAction(),
395 m_newSignalChildAction()
396 #endif
398 m_lock.Unlock();
402 void CDaemonAppTraits::ScheduleForDestroy(wxObject *object)
404 wxMutexLocker lock(m_lock);
406 //delete object;
407 m_sched_delete.push_back(object);
410 void CDaemonAppTraits::RemoveFromPendingDelete(wxObject *object)
412 wxMutexLocker lock(m_lock);
414 for(std::list<wxObject *>::iterator i = m_sched_delete.begin();
415 i != m_sched_delete.end(); i++) {
416 if ( *i == object ) {
417 m_sched_delete.erase(i);
418 return;
423 void CDaemonAppTraits::DeletePending()
425 wxMutexLocker lock(m_lock);
427 while ( !m_sched_delete.empty() ) {
428 std::list<wxObject *>::iterator i = m_sched_delete.begin();
429 wxObject *object = *i;
430 delete object;
432 //m_sched_delete.erase(m_sched_delete.begin(), m_sched_delete.end());
436 #ifdef __WXMAC__
437 #include <wx/stdpaths.h> // Do_not_auto_remove (guess)
438 static wxStandardPathsCF gs_stdPaths;
439 wxStandardPathsBase& CDaemonAppTraits::GetStandardPaths()
441 return gs_stdPaths;
443 #endif
446 CamuleDaemonApp::CamuleDaemonApp()
448 m_Exit(false),
449 m_table(new CAmuledGSocketFuncTable())
451 wxPendingEventsLocker = new wxCriticalSection;
455 wxAppTraits *CamuleDaemonApp::CreateTraits()
457 return new CDaemonAppTraits(m_table);
461 #ifndef __WXMSW__
464 static EndProcessDataMap endProcDataMap;
467 int CDaemonAppTraits::WaitForChild(wxExecuteData &execData)
469 int status = 0;
470 pid_t result = 0;
471 // Build the log message
472 wxString msg;
473 msg << wxT("WaitForChild() has been called for child process with pid `") <<
474 execData.pid <<
475 wxT("'. ");
477 if (execData.flags & wxEXEC_SYNC) {
478 result = AmuleWaitPid(execData.pid, &status, 0, &msg);
479 if (result == -1 || (!WIFEXITED(status) && !WIFSIGNALED(status))) {
480 msg << wxT(" Waiting for subprocess termination failed.");
481 AddDebugLogLineM(false, logGeneral, msg);
483 } else {
484 /** wxEXEC_ASYNC */
485 // Give the process a chance to start or forked child to exit
486 // 1 second is enough time to fail on "path not found"
487 wxSleep(1);
488 result = AmuleWaitPid(execData.pid, &status, WNOHANG, &msg);
489 if (result == 0) {
490 // Add a WxEndProcessData entry to the map, so that we can
491 // support process termination
492 wxEndProcessData *endProcData = new wxEndProcessData();
493 endProcData->pid = execData.pid;
494 endProcData->process = execData.process;
495 endProcData->tag = 0;
496 endProcDataMap[execData.pid] = endProcData;
498 status = execData.pid;
499 } else {
500 // if result != 0, then either waitpid() failed (result == -1)
501 // and there is nothing we can do, or the child has changed
502 // status, which means it is probably dead.
503 status = 0;
507 // Log our passage here
508 AddDebugLogLineM(false, logGeneral, msg);
510 return status;
514 void OnSignalChildHandler(int /*signal*/, siginfo_t *siginfo, void * /*ucontext*/)
516 // Build the log message
517 wxString msg;
518 msg << wxT("OnSignalChildHandler() has been called for child process with pid `") <<
519 siginfo->si_pid <<
520 wxT("'. ");
521 // Make sure we leave no zombies by calling waitpid()
522 int status = 0;
523 pid_t result = AmuleWaitPid(siginfo->si_pid, &status, WNOHANG, &msg);
524 if (result != 1 && result != 0 && (WIFEXITED(status) || WIFSIGNALED(status))) {
525 // Fetch the wxEndProcessData structure corresponding to this pid
526 EndProcessDataMap::iterator it = endProcDataMap.find(siginfo->si_pid);
527 if (it != endProcDataMap.end()) {
528 wxEndProcessData *endProcData = it->second;
529 // Remove this entry from the process map
530 endProcDataMap.erase(siginfo->si_pid);
531 // Save the exit code for the wxProcess object to read later
532 endProcData->exitcode = result != -1 && WIFEXITED(status) ?
533 WEXITSTATUS(status) : -1;
534 // Make things work as in wxGUI
535 wxHandleProcessTermination(endProcData);
537 // wxHandleProcessTermination() will "delete endProcData;"
538 // So we do not delete it again, ok? Do not uncomment this line.
539 //delete endProcData;
540 } else {
541 msg << wxT(" Error: the child process pid is not on the pid map.");
545 // Log our passage here
546 AddDebugLogLineM(false, logGeneral, msg);
550 pid_t AmuleWaitPid(pid_t pid, int *status, int options, wxString *msg)
552 // strerror_r() buffer
553 const int ERROR_BUFFER_LEN = 256;
554 char errorBuffer[ERROR_BUFFER_LEN];
556 *status = 0;
557 pid_t result = waitpid(pid, status, options);
558 if (result == -1) {
559 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
560 *msg << wxT("Error: waitpid() call failed: ") <<
561 char2unicode(errorBuffer) <<
562 wxT(".");
563 } else if (result == 0) {
564 if (options & WNOHANG) {
565 *msg << wxT("The child is alive.");
566 } else {
567 *msg << wxT("Error: waitpid() call returned 0 but "
568 "WNOHANG was not specified in options.");
570 } else {
571 if (WIFEXITED(*status)) {
572 *msg << wxT("Child has terminated with status code `") <<
573 WEXITSTATUS(*status) <<
574 wxT("'.");
575 } else if (WIFSIGNALED(*status)) {
576 *msg << wxT("Child was killed by signal `") <<
577 WTERMSIG(*status) <<
578 wxT("'.");
579 if (WCOREDUMP(*status)) {
580 *msg << wxT(" A core file has been dumped.");
582 } else if (WIFSTOPPED(*status)) {
583 *msg << wxT("Child has been stopped by signal `") <<
584 WSTOPSIG(*status) <<
585 wxT("'.");
586 #ifdef WIFCONTINUED /* Only found in recent kernels. */
587 } else if (WIFCONTINUED(*status)) {
588 *msg << wxT("Child has received `SIGCONT' and has continued execution.");
589 #endif
590 } else {
591 *msg << wxT("The program was not able to determine why the child has signaled.");
595 return result;
599 #endif // __WXMSW__
602 int CamuleDaemonApp::OnRun()
604 if (!thePrefs::AcceptExternalConnections()) {
605 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"));
606 return 0;
607 } else if (thePrefs::ECPassword().IsEmpty()) {
608 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"));
609 return 0;
612 #ifndef __WXMSW__
613 // strerror_r() buffer
614 const int ERROR_BUFFER_LEN = 256;
615 char errorBuffer[ERROR_BUFFER_LEN];
616 wxString msg;
618 // Process the return code of dead children so that we do not create
619 // zombies. wxBase does not implement wxProcess callbacks, so no one
620 // actualy calls wxHandleProcessTermination() in console applications.
621 // We do our best here.
622 int ret = 0;
623 ret = sigaction(SIGCHLD, NULL, &m_oldSignalChildAction);
624 m_newSignalChildAction = m_oldSignalChildAction;
625 m_newSignalChildAction.sa_sigaction = OnSignalChildHandler;
626 m_newSignalChildAction.sa_flags |= SA_SIGINFO;
627 m_newSignalChildAction.sa_flags &= ~SA_RESETHAND;
628 ret = sigaction(SIGCHLD, &m_newSignalChildAction, NULL);
629 if (ret == -1) {
630 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
631 msg << wxT("CamuleDaemonApp::OnRun(): "
632 "Installation of SIGCHLD callback with sigaction() failed: ") <<
633 char2unicode(errorBuffer) <<
634 wxT(".");
635 AddLogLineM(true, msg);
636 } else {
637 msg << wxT("CamuleDaemonApp::OnRun(): Installation of SIGCHLD "
638 "callback with sigaction() succeeded.");
639 AddDebugLogLineM(false, logGeneral, msg);
641 #endif // __WXMSW__
643 while ( !m_Exit ) {
644 m_table->RunSelect();
645 ProcessPendingEvents();
646 ((CDaemonAppTraits *)GetTraits())->DeletePending();
649 // ShutDown is beeing called twice. Once here and again in OnExit().
650 ShutDown();
652 #ifndef __WXMSW__
653 msg.Empty();
654 ret = sigaction(SIGCHLD, &m_oldSignalChildAction, NULL);
655 if (ret == -1) {
656 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
657 msg << wxT("CamuleDaemonApp::OnRun(): second sigaction() failed: ") <<
658 char2unicode(errorBuffer) <<
659 wxT(".");
660 AddLogLineM(true, msg);
661 } else {
662 msg << wxT("CamuleDaemonApp::OnRun(): Uninstallation of SIGCHLD "
663 "callback with sigaction() succeeded.");
664 AddDebugLogLineM(false, logGeneral, msg);
666 #endif // __WXMSW__
668 return 0;
671 bool CamuleDaemonApp::OnInit()
673 AddLogLineNS(_("amuled: OnInit - starting timer"));
674 if ( !CamuleApp::OnInit() ) {
675 return false;
677 core_timer = new CTimer(this,ID_CORE_TIMER_EVENT);
678 core_timer->Start(300);
679 glob_prefs->GetCategory(0)->title = GetCatTitle(thePrefs::GetAllcatType());
680 glob_prefs->GetCategory(0)->path = thePrefs::GetIncomingDir();
682 return true;
685 int CamuleDaemonApp::InitGui(bool ,wxString &)
687 #ifndef __WXMSW__
688 if ( !enable_daemon_fork ) {
689 return 0;
691 AddLogLineNS(_("amuled: forking to background - see you"));
692 theLogger.SetEnabledStdoutLog(false);
694 // fork to background and detouch from controlling tty
695 // while redirecting stdout to /dev/null
697 for(int i_fd = 0;i_fd < 3; i_fd++) {
698 close(i_fd);
700 int fd = open("/dev/null",O_RDWR);
701 dup(fd);
702 dup(fd);
703 int pid = fork();
705 wxASSERT(pid != -1);
707 if ( pid ) {
708 exit(0);
709 } else {
710 setsid();
713 #endif
714 return 0;
718 int CamuleDaemonApp::OnExit()
721 * Stop all socket threads before entering
722 * shutdown sequence.
724 delete listensocket;
725 listensocket = 0;
726 if (clientudp) {
727 delete clientudp;
728 clientudp = NULL;
731 ShutDown();
733 // lfroen: delete socket threads
734 if (ECServerHandler) {
735 ECServerHandler = 0;
738 delete core_timer;
740 return CamuleApp::OnExit();
744 void CamuleDaemonApp::ShowAlert(wxString msg, wxString title, int flags)
746 if ( flags | wxICON_ERROR ) {
747 title = CFormat(_("ERROR: %s")) % title;
749 AddLogLineCS(title + wxT(" ") + msg);
752 // File_checked_for_headers