Upstream tarball 20080524
[amule.git] / src / amuled.cpp
blob1151a3a7014c07830231c8f5396d3c601b742004
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 #ifdef HAVE_CONFIG_H
31 #include "config.h" // Needed for HAVE_SYS_RESOURCE_H, HAVE_STRERROR_R and STRERROR_R_CHAR_P
32 #endif
34 // Prefer the POSIX interface to strerror_r()
35 #define _XOPEN_SOURCE 600
36 #include <string.h> // Do_not_auto_remove
38 #include <wx/utils.h>
40 #include "Preferences.h" // Needed for CPreferences
41 #include "PartFile.h" // Needed for CPartFile
42 #include "Logger.h"
43 #include <common/Format.h>
44 #include "InternalEvents.h" // Needed for wxEVT_*
45 #include "ThreadTasks.h"
46 #include "GuiEvents.h" // Needed for EVT_MULE_NOTIFY
47 #include "Timer.h" // Needed for EVT_MULE_TIMER
49 #include "ClientUDPSocket.h" // Do_not_auto_remove (forward declaration not enough)
50 #include "ListenSocket.h" // Do_not_auto_remove (forward declaration not enough)
53 #include <errno.h>
54 #ifdef HAVE_SYS_RESOURCE_H
55 #include <sys/resource.h> // Do_not_auto_remove
56 #endif
58 #ifndef __WXMSW__
59 #ifdef HAVE_SYS_WAIT_H
60 #include <sys/wait.h> // Do_not_auto_remove
61 #endif
63 #include <wx/unix/execute.h>
64 #endif
66 #ifndef HAVE_STRERROR_R
68 // Replacement strerror_r() function for systems that don't have any.
69 // Note that this replacement function is NOT thread-safe!
70 static int rpl_strerror_r(int errnum, char *buf, size_t buflen)
72 char *tmp = strerror(errnum);
73 if (tmp == NULL) {
74 errno = EINVAL;
75 return -1;
76 } else {
77 strncpy(buf, tmp, buflen - 1);
78 buf[buflen - 1] = '\0';
79 if (strlen(tmp) >= buflen) {
80 errno = ERANGE;
81 return -1;
84 return 0;
87 # define strerror_r(errnum, buf, buflen) rpl_strerror_r(errnum, buf, buflen)
88 #else
89 # ifdef STRERROR_R_CHAR_P
91 // Replacement strerror_r() function for systems that return a char*.
92 static int rpl_strerror_r(int errnum, char *buf, size_t buflen)
94 char *tmp = strerror_r(errnum, buf, buflen);
95 if (tmp == NULL) {
96 errno = EINVAL;
97 return -1;
98 } else if (tmp != buf) {
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 # endif
111 #endif
113 BEGIN_EVENT_TABLE(CamuleDaemonApp, wxAppConsole)
115 // Socket handlers
118 // Listen Socket
119 EVT_SOCKET(ID_LISTENSOCKET_EVENT, CamuleDaemonApp::ListenSocketHandler)
121 // UDP Socket (servers)
122 EVT_SOCKET(ID_SERVERUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
123 // UDP Socket (clients)
124 EVT_SOCKET(ID_CLIENTUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
126 // Socket timer (TCP)
127 EVT_MULE_TIMER(ID_SERVER_RETRY_TIMER_EVENT, CamuleDaemonApp::OnTCPTimer)
129 // Core timer
130 EVT_MULE_TIMER(ID_CORE_TIMER_EVENT, CamuleDaemonApp::OnCoreTimer)
132 EVT_MULE_NOTIFY(CamuleDaemonApp::OnNotifyEvent)
133 EVT_MULE_LOGGING(CamuleDaemonApp::OnLoggingEvent)
135 // Async dns handling
136 EVT_MULE_INTERNAL(wxEVT_CORE_UDP_DNS_DONE, -1, CamuleDaemonApp::OnUDPDnsDone)
138 EVT_MULE_INTERNAL(wxEVT_CORE_SOURCE_DNS_DONE, -1, CamuleDaemonApp::OnSourceDnsDone)
140 EVT_MULE_INTERNAL(wxEVT_CORE_SERVER_DNS_DONE, -1, CamuleDaemonApp::OnServerDnsDone)
142 // Hash ended notifier
143 EVT_MULE_HASHING(CamuleDaemonApp::OnFinishedHashing)
144 EVT_MULE_AICH_HASHING(CamuleDaemonApp::OnFinishedAICHHashing)
146 // File completion ended notifier
147 EVT_MULE_FILE_COMPLETED(CamuleDaemonApp::OnFinishedCompletion)
149 // HTTPDownload finished
150 EVT_MULE_INTERNAL(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD, -1, CamuleDaemonApp::OnFinishedHTTPDownload)
152 // Disk space preallocation finished
153 EVT_MULE_ALLOC_FINISHED(CamuleDaemonApp::OnFinishedAllocation)
154 END_EVENT_TABLE()
156 IMPLEMENT_APP(CamuleDaemonApp)
159 * Socket handling in wxBase
162 class CSocketSet {
163 int m_count;
164 int m_fds[FD_SETSIZE], m_fd_idx[FD_SETSIZE];
165 GSocket *m_gsocks[FD_SETSIZE];
167 fd_set m_set;
168 public:
169 CSocketSet();
170 void AddSocket(GSocket *);
171 void RemoveSocket(GSocket *);
172 void FillSet(int &max_fd);
174 void Detected(void (GSocket::*func)());
176 fd_set *Set() { return &m_set; }
179 CSocketSet::CSocketSet()
181 m_count = 0;
182 for(int i = 0; i < FD_SETSIZE; i++) {
183 m_fds[i] = 0;
184 m_fd_idx[i] = 0xffff;
185 m_gsocks[i] = 0;
189 void CSocketSet::AddSocket(GSocket *socket)
191 wxASSERT(socket);
193 int fd = socket->m_fd;
195 if ( fd == -1 ) {
196 return;
199 wxASSERT( (fd > 2) && (fd < FD_SETSIZE) );
201 if ( m_gsocks[fd] ) {
202 return;
204 m_fds[m_count] = fd;
205 m_fd_idx[fd] = m_count;
206 m_gsocks[fd] = socket;
207 m_count++;
210 void CSocketSet::RemoveSocket(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 int i = m_fd_idx[fd];
223 if ( i == 0xffff ) {
224 return;
226 wxASSERT(m_fds[i] == fd);
227 m_fds[i] = m_fds[m_count-1];
228 m_gsocks[fd] = 0;
229 m_fds[m_count-1] = 0;
230 m_fd_idx[fd] = 0xffff;
231 m_fd_idx[m_fds[i]] = i;
232 m_count--;
235 void CSocketSet::FillSet(int &max_fd)
237 FD_ZERO(&m_set);
239 for(int i = 0; i < m_count; i++) {
240 FD_SET(m_fds[i], &m_set);
241 if ( m_fds[i] > max_fd ) {
242 max_fd = m_fds[i];
247 void CSocketSet::Detected(void (GSocket::*func)())
249 for (int i = 0; i < m_count; i++) {
250 int fd = m_fds[i];
251 if ( FD_ISSET(fd, &m_set) ) {
252 GSocket *socket = m_gsocks[fd];
253 (*socket.*func)();
258 CAmuledGSocketFuncTable::CAmuledGSocketFuncTable() : m_lock(wxMUTEX_RECURSIVE)
260 m_in_set = new CSocketSet;
261 m_out_set = new CSocketSet;
263 m_lock.Unlock();
266 void CAmuledGSocketFuncTable::AddSocket(GSocket *socket, GSocketEvent event)
268 wxMutexLocker lock(m_lock);
270 if ( event == GSOCK_INPUT ) {
271 m_in_set->AddSocket(socket);
272 } else {
273 m_out_set->AddSocket(socket);
277 void CAmuledGSocketFuncTable::RemoveSocket(GSocket *socket, GSocketEvent event)
279 wxMutexLocker lock(m_lock);
281 if ( event == GSOCK_INPUT ) {
282 m_in_set->RemoveSocket(socket);
283 } else {
284 m_out_set->RemoveSocket(socket);
288 void CAmuledGSocketFuncTable::RunSelect()
290 wxMutexLocker lock(m_lock);
292 int max_fd = -1;
293 m_in_set->FillSet(max_fd);
294 m_out_set->FillSet(max_fd);
296 struct timeval tv;
297 tv.tv_sec = 0;
298 tv.tv_usec = 10000; // 10ms
300 int result = select(max_fd + 1, m_in_set->Set(), m_out_set->Set(), 0, &tv);
301 if ( result > 0 ) {
302 m_in_set->Detected(&GSocket::Detected_Read);
303 m_out_set->Detected(&GSocket::Detected_Write);
308 GSocketGUIFunctionsTable *CDaemonAppTraits::GetSocketGUIFunctionsTable()
310 return m_table;
313 bool CAmuledGSocketFuncTable::OnInit()
315 return true;
318 void CAmuledGSocketFuncTable::OnExit()
322 bool CAmuledGSocketFuncTable::CanUseEventLoop()
325 * FIXME: (lfroen) Not sure whether it's right.
326 * I will review it later.
328 return false;
331 bool CAmuledGSocketFuncTable::Init_Socket(GSocket *)
333 return true;
336 void CAmuledGSocketFuncTable::Destroy_Socket(GSocket *)
340 void CAmuledGSocketFuncTable::Install_Callback(GSocket *sock, GSocketEvent e)
342 AddSocket(sock, e);
345 void CAmuledGSocketFuncTable::Uninstall_Callback(GSocket *sock, GSocketEvent e)
347 RemoveSocket(sock, e);
350 void CAmuledGSocketFuncTable::Enable_Events(GSocket *socket)
352 Install_Callback(socket, GSOCK_INPUT);
353 Install_Callback(socket, GSOCK_OUTPUT);
356 void CAmuledGSocketFuncTable::Disable_Events(GSocket *socket)
358 Uninstall_Callback(socket, GSOCK_INPUT);
359 Uninstall_Callback(socket, GSOCK_OUTPUT);
363 CDaemonAppTraits::CDaemonAppTraits(CAmuledGSocketFuncTable *table)
365 wxConsoleAppTraits(),
366 m_table(table),
367 m_lock(wxMUTEX_RECURSIVE),
368 m_sched_delete(),
369 m_oldSignalChildAction(),
370 m_newSignalChildAction()
372 m_lock.Unlock();
376 void CDaemonAppTraits::ScheduleForDestroy(wxObject *object)
378 wxMutexLocker lock(m_lock);
380 //delete object;
381 m_sched_delete.push_back(object);
384 void CDaemonAppTraits::RemoveFromPendingDelete(wxObject *object)
386 wxMutexLocker lock(m_lock);
388 for(std::list<wxObject *>::iterator i = m_sched_delete.begin();
389 i != m_sched_delete.end(); i++) {
390 if ( *i == object ) {
391 m_sched_delete.erase(i);
392 return;
397 void CDaemonAppTraits::DeletePending()
399 wxMutexLocker lock(m_lock);
401 while ( !m_sched_delete.empty() ) {
402 std::list<wxObject *>::iterator i = m_sched_delete.begin();
403 wxObject *object = *i;
404 delete object;
406 //m_sched_delete.erase(m_sched_delete.begin(), m_sched_delete.end());
410 #ifdef __WXMAC__
411 #include <wx/stdpaths.h> // Do_not_auto_remove (guess)
412 static wxStandardPathsCF gs_stdPaths;
413 wxStandardPathsBase& CDaemonAppTraits::GetStandardPaths()
415 return gs_stdPaths;
417 #endif
420 CamuleDaemonApp::CamuleDaemonApp()
422 m_Exit(false),
423 m_table(new CAmuledGSocketFuncTable())
425 wxPendingEventsLocker = new wxCriticalSection;
429 wxAppTraits *CamuleDaemonApp::CreateTraits()
431 return new CDaemonAppTraits(m_table);
435 #ifndef __WXMSW__
438 static EndProcessDataMap endProcDataMap;
441 int CDaemonAppTraits::WaitForChild(wxExecuteData &execData)
443 int status = 0;
444 pid_t result = 0;
445 // Build the log message
446 wxString msg;
447 msg << wxT("WaitForChild() has been called for child process with pid `") <<
448 execData.pid <<
449 wxT("'. ");
451 if (execData.flags & wxEXEC_SYNC) {
452 result = AmuleWaitPid(execData.pid, &status, 0, &msg);
453 if (result == -1 || (!WIFEXITED(status) && !WIFSIGNALED(status))) {
454 msg << wxT(" Waiting for subprocess termination failed.");
455 AddDebugLogLineM(false, logGeneral, msg);
457 } else {
458 /** wxEXEC_ASYNC */
459 // Give the process a chance to start or forked child to exit
460 // 1 second is enough time to fail on "path not found"
461 wxSleep(1);
462 result = AmuleWaitPid(execData.pid, &status, WNOHANG, &msg);
463 if (result == 0) {
464 // Add a WxEndProcessData entry to the map, so that we can
465 // support process termination
466 wxEndProcessData *endProcData = new wxEndProcessData();
467 endProcData->pid = execData.pid;
468 endProcData->process = execData.process;
469 endProcData->tag = 0;
470 endProcDataMap[execData.pid] = endProcData;
472 status = execData.pid;
473 } else {
474 // if result != 0, then either waitpid() failed (result == -1)
475 // and there is nothing we can do, or the child has changed
476 // status, which means it is probably dead.
477 status = 0;
481 // Log our passage here
482 AddDebugLogLineM(false, logGeneral, msg);
484 return status;
488 void OnSignalChildHandler(int /*signal*/, siginfo_t *siginfo, void * /*ucontext*/)
490 // Build the log message
491 wxString msg;
492 msg << wxT("OnSignalChildHandler() has been called for child process with pid `") <<
493 siginfo->si_pid <<
494 wxT("'. ");
495 // Make sure we leave no zombies by calling waitpid()
496 int status = 0;
497 pid_t result = AmuleWaitPid(siginfo->si_pid, &status, WNOHANG, &msg);
498 if (result != 1 && result != 0 && (WIFEXITED(status) || WIFSIGNALED(status))) {
499 // Fetch the wxEndProcessData structure corresponding to this pid
500 EndProcessDataMap::iterator it = endProcDataMap.find(siginfo->si_pid);
501 if (it != endProcDataMap.end()) {
502 wxEndProcessData *endProcData = it->second;
503 // Remove this entry from the process map
504 endProcDataMap.erase(siginfo->si_pid);
505 // Save the exit code for the wxProcess object to read later
506 endProcData->exitcode = result != -1 && WIFEXITED(status) ?
507 WEXITSTATUS(status) : -1;
508 // Make things work as in wxGUI
509 wxHandleProcessTermination(endProcData);
511 // wxHandleProcessTermination() will "delete endProcData;"
512 // So we do not delete it again, ok? Do not uncomment this line.
513 //delete endProcData;
514 } else {
515 msg << wxT(" Error: the child process pid is not on the pid map.");
519 // Log our passage here
520 AddDebugLogLineM(false, logGeneral, msg);
524 pid_t AmuleWaitPid(pid_t pid, int *status, int options, wxString *msg)
526 // strerror_r() buffer
527 const int ERROR_BUFFER_LEN = 256;
528 char errorBuffer[ERROR_BUFFER_LEN];
530 *status = 0;
531 pid_t result = waitpid(pid, status, options);
532 if (result == -1) {
533 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
534 *msg << wxT("Error: waitpid() call failed: ") <<
535 char2unicode(errorBuffer) <<
536 wxT(".");
537 } else if (result == 0) {
538 if (options & WNOHANG) {
539 *msg << wxT("The child is alive.");
540 } else {
541 *msg << wxT("Error: waitpid() call returned 0 but "
542 "WNOHANG was not specified in options.");
544 } else {
545 if (WIFEXITED(*status)) {
546 *msg << wxT("Child has terminated with status code `") <<
547 WEXITSTATUS(*status) <<
548 wxT("'.");
549 } else if (WIFSIGNALED(*status)) {
550 *msg << wxT("Child was killed by signal `") <<
551 WTERMSIG(*status) <<
552 wxT("'.");
553 if (WCOREDUMP(*status)) {
554 *msg << wxT(" A core file has been dumped.");
556 } else if (WIFSTOPPED(*status)) {
557 *msg << wxT("Child has been stopped by signal `") <<
558 WSTOPSIG(*status) <<
559 wxT("'.");
560 #ifdef WIFCONTINUED /* Only found in recent kernels. */
561 } else if (WIFCONTINUED(*status)) {
562 *msg << wxT("Child has received `SIGCONT' and has continued execution.");
563 #endif
564 } else {
565 *msg << wxT("The program was not able to determine why the child has signaled.");
569 return result;
573 #endif // __WXMSW__
576 int CamuleDaemonApp::OnRun()
578 if (!thePrefs::AcceptExternalConnections()) {
579 wxString warning = _("ERROR: aMule daemon cannot be used when external connections are disabled. "
580 "To enable External Connections, use either a normal aMule, start amuled with the option --ec-config or set the key"
581 "\"AcceptExternalConnections\" to 1 in the file ~/.aMule/amule.conf");
583 AddLogLineM(true, warning);
584 printf("\n%s\n\n", (const char*)unicode2char(warning));
586 return 0;
587 } else if (thePrefs::ECPassword().IsEmpty()) {
588 wxString warning = wxT("ERROR: A valid password is required to use "
589 "external connections, and aMule daemon cannot be used without "
590 "external connections. To run aMule deamon, you must set the "
591 "\"ECPassword\" field in the file ~/.aMule/amule.conf with an "
592 "appropriate value. Execute amuled with the flag --ec-config to set the password. More information can be found at "
593 "http://wiki.amule.org");
595 AddLogLineM(true, warning);
596 printf("\n%s\n\n", (const char*)unicode2char(warning));
598 return 0;
601 #ifndef __WXMSW__
602 // strerror_r() buffer
603 const int ERROR_BUFFER_LEN = 256;
604 char errorBuffer[ERROR_BUFFER_LEN];
605 wxString msg;
607 // Process the return code of dead children so that we do not create
608 // zombies. wxBase does not implement wxProcess callbacks, so no one
609 // actualy calls wxHandleProcessTermination() in console applications.
610 // We do our best here.
611 int ret = 0;
612 ret = sigaction(SIGCHLD, NULL, &m_oldSignalChildAction);
613 m_newSignalChildAction = m_oldSignalChildAction;
614 m_newSignalChildAction.sa_sigaction = OnSignalChildHandler;
615 m_newSignalChildAction.sa_flags |= SA_SIGINFO;
616 m_newSignalChildAction.sa_flags &= ~SA_RESETHAND;
617 ret = sigaction(SIGCHLD, &m_newSignalChildAction, NULL);
618 if (ret == -1) {
619 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
620 msg << wxT("CamuleDaemonApp::OnRun(): "
621 "Installation of SIGCHLD callback with sigaction() failed: ") <<
622 char2unicode(errorBuffer) <<
623 wxT(".");
624 AddLogLineM(true, msg);
625 } else {
626 msg << wxT("CamuleDaemonApp::OnRun(): Installation of SIGCHLD "
627 "callback with sigaction() succeeded.");
628 AddDebugLogLineM(false, logGeneral, msg);
630 #endif // __WXMSW__
632 while ( !m_Exit ) {
633 m_table->RunSelect();
634 ProcessPendingEvents();
635 ((CDaemonAppTraits *)GetTraits())->DeletePending();
638 // ShutDown is beeing called twice. Once here and again in OnExit().
639 ShutDown();
641 #ifndef __WXMSW__
642 msg.Empty();
643 ret = sigaction(SIGCHLD, &m_oldSignalChildAction, NULL);
644 if (ret == -1) {
645 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
646 msg << wxT("CamuleDaemonApp::OnRun(): second sigaction() failed: ") <<
647 char2unicode(errorBuffer) <<
648 wxT(".");
649 AddLogLineM(true, msg);
650 } else {
651 msg << wxT("CamuleDaemonApp::OnRun(): Uninstallation of SIGCHLD "
652 "callback with sigaction() succeeded.");
653 AddDebugLogLineM(false, logGeneral, msg);
655 #endif // __WXMSW__
657 return 0;
660 bool CamuleDaemonApp::OnInit()
662 printf("amuled: OnInit - starting timer\n");
663 if ( !CamuleApp::OnInit() ) {
664 return false;
666 core_timer = new CTimer(this,ID_CORE_TIMER_EVENT);
667 core_timer->Start(300);
668 glob_prefs->GetCategory(0)->title = GetCatTitle(thePrefs::GetAllcatType());
669 glob_prefs->GetCategory(0)->path = thePrefs::GetIncomingDir();
671 return true;
674 int CamuleDaemonApp::InitGui(bool ,wxString &)
676 #ifndef __WXMSW__
677 if ( !enable_daemon_fork ) {
678 return 0;
680 printf("amuled: forking to background - see you\n");
682 // fork to background and detouch from controlling tty
683 // while redirecting stdout to /dev/null
685 for(int i_fd = 0;i_fd < 3; i_fd++) {
686 close(i_fd);
688 int fd = open("/dev/null",O_RDWR);
689 dup(fd);
690 dup(fd);
691 int pid = fork();
693 wxASSERT(pid != -1);
695 if ( pid ) {
696 exit(0);
697 } else {
698 setsid();
701 #endif
702 return 0;
706 int CamuleDaemonApp::OnExit()
709 * Stop all socket threads before entering
710 * shutdown sequence.
712 delete listensocket;
713 listensocket = 0;
714 if (clientudp) {
715 delete clientudp;
716 clientudp = NULL;
719 ShutDown();
721 // lfroen: delete socket threads
722 if (ECServerHandler) {
723 ECServerHandler = 0;
726 delete core_timer;
728 return CamuleApp::OnExit();
732 void CamuleDaemonApp::ShowAlert(wxString msg, wxString title, int flags)
734 if ( flags | wxICON_ERROR ) {
735 title = CFormat(_("ERROR: %s")) % title;
738 // Ensure that alerts are always visible on the console (when possible).
739 if ((not enable_stdout_log) and (not enable_daemon_fork)) {
740 printf("%s\n", unicode2UTF8(title + wxT(" ") + msg).data());
743 AddLogLineM(true, title + wxT(" ") + msg);
747 void CamuleDaemonApp::OnLoggingEvent(CLoggingEvent& evt)
749 CamuleApp::AddLogLine(evt.Message());
752 // File_checked_for_headers