Nothing to see here. Really.
[amule.git] / src / amuled.cpp
blobc34a67207db6d48206515d38a5bbf50eee133aff
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2011 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, etc
32 #endif
34 // Include the necessary headers for select(2), properly guarded
35 #if defined HAVE_SYS_SELECT_H && !defined __IRIX__
36 # include <sys/select.h>
37 #else
38 # ifdef HAVE_SYS_TIME_H
39 # include <sys/time.h>
40 # endif
41 # ifdef HAVE_SYS_TYPES_H
42 # include <sys/types.h>
43 # endif
44 # ifdef HAVE_UNISTD_H
45 # include <unistd.h>
46 # endif
47 #endif
49 #include <wx/utils.h>
51 #include "Preferences.h" // Needed for CPreferences
52 #include "PartFile.h" // Needed for CPartFile
53 #include "Logger.h"
54 #include <common/Format.h>
55 #include "InternalEvents.h" // Needed for wxEVT_*
56 #include "ThreadTasks.h"
57 #include "GuiEvents.h" // Needed for EVT_MULE_NOTIFY
58 #include "Timer.h" // Needed for EVT_MULE_TIMER
60 #include "ClientUDPSocket.h" // Do_not_auto_remove (forward declaration not enough)
61 #include "ListenSocket.h" // Do_not_auto_remove (forward declaration not enough)
64 #ifdef HAVE_SYS_RESOURCE_H
65 #include <sys/resource.h> // Do_not_auto_remove
66 #endif
68 #ifndef __WXMSW__
69 #ifdef HAVE_SYS_WAIT_H
70 #include <sys/wait.h> // Do_not_auto_remove
71 #endif
73 #include <wx/unix/execute.h>
74 #endif
76 BEGIN_EVENT_TABLE(CamuleDaemonApp, wxAppConsole)
78 // Socket handlers
81 // Listen Socket
82 EVT_SOCKET(ID_LISTENSOCKET_EVENT, CamuleDaemonApp::ListenSocketHandler)
84 // UDP Socket (servers)
85 EVT_SOCKET(ID_SERVERUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
86 // UDP Socket (clients)
87 EVT_SOCKET(ID_CLIENTUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
89 // Socket timer (TCP)
90 EVT_MULE_TIMER(ID_SERVER_RETRY_TIMER_EVENT, CamuleDaemonApp::OnTCPTimer)
92 // Core timer
93 EVT_MULE_TIMER(ID_CORE_TIMER_EVENT, CamuleDaemonApp::OnCoreTimer)
95 EVT_MULE_NOTIFY(CamuleDaemonApp::OnNotifyEvent)
97 // Async dns handling
98 EVT_MULE_INTERNAL(wxEVT_CORE_UDP_DNS_DONE, -1, CamuleDaemonApp::OnUDPDnsDone)
100 EVT_MULE_INTERNAL(wxEVT_CORE_SOURCE_DNS_DONE, -1, CamuleDaemonApp::OnSourceDnsDone)
102 EVT_MULE_INTERNAL(wxEVT_CORE_SERVER_DNS_DONE, -1, CamuleDaemonApp::OnServerDnsDone)
104 // Hash ended notifier
105 EVT_MULE_HASHING(CamuleDaemonApp::OnFinishedHashing)
106 EVT_MULE_AICH_HASHING(CamuleDaemonApp::OnFinishedAICHHashing)
108 // File completion ended notifier
109 EVT_MULE_FILE_COMPLETED(CamuleDaemonApp::OnFinishedCompletion)
111 // HTTPDownload finished
112 EVT_MULE_INTERNAL(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD, -1, CamuleDaemonApp::OnFinishedHTTPDownload)
114 // Disk space preallocation finished
115 EVT_MULE_ALLOC_FINISHED(CamuleDaemonApp::OnFinishedAllocation)
116 END_EVENT_TABLE()
118 IMPLEMENT_APP(CamuleDaemonApp)
120 #ifdef AMULED28
122 * Socket handling in wxBase
125 class CSocketSet {
126 int m_count;
127 int m_fds[FD_SETSIZE], m_fd_idx[FD_SETSIZE];
128 GSocket *m_gsocks[FD_SETSIZE];
130 fd_set m_set;
131 public:
132 CSocketSet();
133 void AddSocket(GSocket *);
134 void RemoveSocket(GSocket *);
135 void FillSet(int &max_fd);
137 void Detected(void (GSocket::*func)());
139 fd_set *Set() { return &m_set; }
142 CSocketSet::CSocketSet()
144 m_count = 0;
145 for(int i = 0; i < FD_SETSIZE; i++) {
146 m_fds[i] = 0;
147 m_fd_idx[i] = 0xffff;
148 m_gsocks[i] = 0;
152 void CSocketSet::AddSocket(GSocket *socket)
154 wxASSERT(socket);
156 int fd = socket->m_fd;
158 if ( fd == -1 ) {
159 return;
162 wxASSERT( (fd > 2) && (fd < FD_SETSIZE) );
164 if ( m_gsocks[fd] ) {
165 return;
167 m_fds[m_count] = fd;
168 m_fd_idx[fd] = m_count;
169 m_gsocks[fd] = socket;
170 m_count++;
173 void CSocketSet::RemoveSocket(GSocket *socket)
175 wxASSERT(socket);
177 int fd = socket->m_fd;
179 if ( fd == -1 ) {
180 return;
183 wxASSERT( (fd > 2) && (fd < FD_SETSIZE) );
185 int i = m_fd_idx[fd];
186 if ( i == 0xffff ) {
187 return;
189 wxASSERT(m_fds[i] == fd);
190 m_fds[i] = m_fds[m_count-1];
191 m_gsocks[fd] = 0;
192 m_fds[m_count-1] = 0;
193 m_fd_idx[fd] = 0xffff;
194 m_fd_idx[m_fds[i]] = i;
195 m_count--;
198 void CSocketSet::FillSet(int &max_fd)
200 FD_ZERO(&m_set);
202 for(int i = 0; i < m_count; i++) {
203 FD_SET(m_fds[i], &m_set);
204 if ( m_fds[i] > max_fd ) {
205 max_fd = m_fds[i];
210 void CSocketSet::Detected(void (GSocket::*func)())
212 for (int i = 0; i < m_count; i++) {
213 int fd = m_fds[i];
214 if ( FD_ISSET(fd, &m_set) ) {
215 GSocket *socket = m_gsocks[fd];
216 (*socket.*func)();
221 CAmuledGSocketFuncTable::CAmuledGSocketFuncTable() : m_lock(wxMUTEX_RECURSIVE)
223 m_in_set = new CSocketSet;
224 m_out_set = new CSocketSet;
226 m_lock.Unlock();
229 void CAmuledGSocketFuncTable::AddSocket(GSocket *socket, GSocketEvent event)
231 wxMutexLocker lock(m_lock);
233 if ( event == GSOCK_INPUT ) {
234 m_in_set->AddSocket(socket);
235 } else {
236 m_out_set->AddSocket(socket);
240 void CAmuledGSocketFuncTable::RemoveSocket(GSocket *socket, GSocketEvent event)
242 wxMutexLocker lock(m_lock);
244 if ( event == GSOCK_INPUT ) {
245 m_in_set->RemoveSocket(socket);
246 } else {
247 m_out_set->RemoveSocket(socket);
251 void CAmuledGSocketFuncTable::RunSelect()
253 wxMutexLocker lock(m_lock);
255 int max_fd = -1;
256 m_in_set->FillSet(max_fd);
257 m_out_set->FillSet(max_fd);
259 struct timeval tv;
260 tv.tv_sec = 0;
261 tv.tv_usec = 10000; // 10ms
263 int result = select(max_fd + 1, m_in_set->Set(), m_out_set->Set(), 0, &tv);
264 if ( result > 0 ) {
265 m_in_set->Detected(&GSocket::Detected_Read);
266 m_out_set->Detected(&GSocket::Detected_Write);
270 GSocketGUIFunctionsTable *CDaemonAppTraits::GetSocketGUIFunctionsTable()
272 return m_table;
275 bool CAmuledGSocketFuncTable::OnInit()
277 return true;
280 void CAmuledGSocketFuncTable::OnExit()
284 bool CAmuledGSocketFuncTable::CanUseEventLoop()
287 * FIXME: (lfroen) Not sure whether it's right.
288 * I will review it later.
290 return false;
293 bool CAmuledGSocketFuncTable::Init_Socket(GSocket *)
295 return true;
298 void CAmuledGSocketFuncTable::Destroy_Socket(GSocket *)
302 void CAmuledGSocketFuncTable::Install_Callback(GSocket *sock, GSocketEvent e)
304 AddSocket(sock, e);
307 void CAmuledGSocketFuncTable::Uninstall_Callback(GSocket *sock, GSocketEvent e)
309 RemoveSocket(sock, e);
312 void CAmuledGSocketFuncTable::Enable_Events(GSocket *socket)
314 Install_Callback(socket, GSOCK_INPUT);
315 Install_Callback(socket, GSOCK_OUTPUT);
318 void CAmuledGSocketFuncTable::Disable_Events(GSocket *socket)
320 Uninstall_Callback(socket, GSOCK_INPUT);
321 Uninstall_Callback(socket, GSOCK_OUTPUT);
324 #endif // AMULED28
326 #ifndef __WXMSW__
328 #ifdef AMULED28
330 CDaemonAppTraits::CDaemonAppTraits(CAmuledGSocketFuncTable *table)
332 wxConsoleAppTraits(),
333 m_oldSignalChildAction(),
334 m_newSignalChildAction(),
335 m_table(table),
336 m_lock(wxMUTEX_RECURSIVE),
337 m_sched_delete()
339 m_lock.Unlock();
343 void CDaemonAppTraits::ScheduleForDestroy(wxObject *object)
345 wxMutexLocker lock(m_lock);
347 //delete object;
348 m_sched_delete.push_back(object);
351 void CDaemonAppTraits::RemoveFromPendingDelete(wxObject *object)
353 wxMutexLocker lock(m_lock);
355 for(std::list<wxObject *>::iterator i = m_sched_delete.begin();
356 i != m_sched_delete.end(); i++) {
357 if ( *i == object ) {
358 m_sched_delete.erase(i);
359 return;
364 void CDaemonAppTraits::DeletePending()
366 wxMutexLocker lock(m_lock);
368 while ( !m_sched_delete.empty() ) {
369 std::list<wxObject *>::iterator i = m_sched_delete.begin();
370 wxObject *object = *i;
371 delete object;
373 //m_sched_delete.erase(m_sched_delete.begin(), m_sched_delete.end());
376 wxAppTraits *CamuleDaemonApp::CreateTraits()
378 return new CDaemonAppTraits(m_table);
381 #else // AMULED28
383 CDaemonAppTraits::CDaemonAppTraits()
385 wxConsoleAppTraits(),
386 m_oldSignalChildAction(),
387 m_newSignalChildAction()
391 wxAppTraits *CamuleDaemonApp::CreateTraits()
393 return new CDaemonAppTraits();
396 #endif // !AMULED28
398 #endif // __WXMSW__
400 #if defined(__WXMAC__) && !wxCHECK_VERSION(2, 9, 0)
401 #include <wx/stdpaths.h> // Do_not_auto_remove (guess)
402 static wxStandardPathsCF gs_stdPaths;
403 wxStandardPathsBase& CDaemonAppTraits::GetStandardPaths()
405 return gs_stdPaths;
407 #endif
410 #ifdef AMULED28
412 CamuleDaemonApp::CamuleDaemonApp()
414 m_Exit(false),
415 m_table(new CAmuledGSocketFuncTable())
417 wxPendingEventsLocker = new wxCriticalSection;
420 #endif // !AMULED28
423 #ifndef __WXMSW__
426 static EndProcessDataMap endProcDataMap;
429 int CDaemonAppTraits::WaitForChild(wxExecuteData &execData)
431 int status = 0;
432 pid_t result = 0;
433 // Build the log message
434 wxString msg;
435 msg << wxT("WaitForChild() has been called for child process with pid `") <<
436 execData.pid <<
437 wxT("'. ");
439 if (execData.flags & wxEXEC_SYNC) {
440 result = AmuleWaitPid(execData.pid, &status, 0, &msg);
441 if (result == -1 || (!WIFEXITED(status) && !WIFSIGNALED(status))) {
442 msg << wxT(" Waiting for subprocess termination failed.");
443 AddDebugLogLineN(logGeneral, msg);
445 } else {
446 /** wxEXEC_ASYNC */
447 // Give the process a chance to start or forked child to exit
448 // 1 second is enough time to fail on "path not found"
449 wxSleep(1);
450 result = AmuleWaitPid(execData.pid, &status, WNOHANG, &msg);
451 if (result == 0) {
452 // Add a WxEndProcessData entry to the map, so that we can
453 // support process termination
454 wxEndProcessData *endProcData = new wxEndProcessData();
455 endProcData->pid = execData.pid;
456 endProcData->process = execData.process;
457 endProcData->tag = 0;
458 endProcDataMap[execData.pid] = endProcData;
460 status = execData.pid;
461 } else {
462 // if result != 0, then either waitpid() failed (result == -1)
463 // and there is nothing we can do, or the child has changed
464 // status, which means it is probably dead.
465 status = 0;
469 // Log our passage here
470 AddDebugLogLineN(logGeneral, msg);
472 return status;
476 void OnSignalChildHandler(int /*signal*/, siginfo_t *siginfo, void * /*ucontext*/)
478 // Build the log message
479 wxString msg;
480 msg << wxT("OnSignalChildHandler() has been called for child process with pid `") <<
481 siginfo->si_pid <<
482 wxT("'. ");
483 // Make sure we leave no zombies by calling waitpid()
484 int status = 0;
485 pid_t result = AmuleWaitPid(siginfo->si_pid, &status, WNOHANG, &msg);
486 if (result != 1 && result != 0 && (WIFEXITED(status) || WIFSIGNALED(status))) {
487 // Fetch the wxEndProcessData structure corresponding to this pid
488 EndProcessDataMap::iterator it = endProcDataMap.find(siginfo->si_pid);
489 if (it != endProcDataMap.end()) {
490 wxEndProcessData *endProcData = it->second;
491 // Remove this entry from the process map
492 endProcDataMap.erase(siginfo->si_pid);
493 // Save the exit code for the wxProcess object to read later
494 endProcData->exitcode = result != -1 && WIFEXITED(status) ?
495 WEXITSTATUS(status) : -1;
496 // Make things work as in wxGUI
497 wxHandleProcessTermination(endProcData);
499 // wxHandleProcessTermination() will "delete endProcData;"
500 // So we do not delete it again, ok? Do not uncomment this line.
501 //delete endProcData;
502 } else {
503 msg << wxT(" Error: the child process pid is not on the pid map.");
507 // Log our passage here
508 AddDebugLogLineN(logGeneral, msg);
512 pid_t AmuleWaitPid(pid_t pid, int *status, int options, wxString *msg)
514 *status = 0;
515 pid_t result = waitpid(pid, status, options);
516 if (result == -1) {
517 *msg << CFormat(wxT("Error: waitpid() call failed: %m."));
518 } else if (result == 0) {
519 if (options & WNOHANG) {
520 *msg << wxT("The child is alive.");
521 } else {
522 *msg << wxT("Error: waitpid() call returned 0 but "
523 "WNOHANG was not specified in options.");
525 } else {
526 if (WIFEXITED(*status)) {
527 *msg << wxT("Child has terminated with status code `") <<
528 WEXITSTATUS(*status) <<
529 wxT("'.");
530 } else if (WIFSIGNALED(*status)) {
531 *msg << wxT("Child was killed by signal `") <<
532 WTERMSIG(*status) <<
533 wxT("'.");
534 if (WCOREDUMP(*status)) {
535 *msg << wxT(" A core file has been dumped.");
537 } else if (WIFSTOPPED(*status)) {
538 *msg << wxT("Child has been stopped by signal `") <<
539 WSTOPSIG(*status) <<
540 wxT("'.");
541 #ifdef WIFCONTINUED /* Only found in recent kernels. */
542 } else if (WIFCONTINUED(*status)) {
543 *msg << wxT("Child has received `SIGCONT' and has continued execution.");
544 #endif
545 } else {
546 *msg << wxT("The program was not able to determine why the child has signaled.");
550 return result;
554 #endif // __WXMSW__
557 int CamuleDaemonApp::OnRun()
559 if (!thePrefs::AcceptExternalConnections()) {
560 AddLogLineCS(_("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"));
561 return 0;
562 } else if (thePrefs::ECPassword().IsEmpty()) {
563 AddLogLineCS(_("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"));
564 return 0;
567 #ifndef __WXMSW__
568 // Process the return code of dead children so that we do not create
569 // zombies. wxBase does not implement wxProcess callbacks, so no one
570 // actualy calls wxHandleProcessTermination() in console applications.
571 // We do our best here.
572 int ret = 0;
573 ret = sigaction(SIGCHLD, NULL, &m_oldSignalChildAction);
574 m_newSignalChildAction = m_oldSignalChildAction;
575 m_newSignalChildAction.sa_sigaction = OnSignalChildHandler;
576 m_newSignalChildAction.sa_flags |= SA_SIGINFO;
577 m_newSignalChildAction.sa_flags &= ~SA_RESETHAND;
578 ret = sigaction(SIGCHLD, &m_newSignalChildAction, NULL);
579 if (ret == -1) {
580 AddDebugLogLineC(logStandard, CFormat(wxT("CamuleDaemonApp::OnRun(): Installation of SIGCHLD callback with sigaction() failed: %m.")));
581 } else {
582 AddDebugLogLineN(logGeneral, wxT("CamuleDaemonApp::OnRun(): Installation of SIGCHLD callback with sigaction() succeeded."));
584 #endif // __WXMSW__
586 #ifdef AMULED28
588 while ( !m_Exit ) {
589 m_table->RunSelect();
590 ProcessPendingEvents();
591 ((CDaemonAppTraits *)GetTraits())->DeletePending();
594 // ShutDown is beeing called twice. Once here and again in OnExit().
595 ShutDown();
597 return 0;
599 #else
601 #ifdef AMULED_DUMMY
602 return 0;
603 #else
604 return wxApp::OnRun();
605 #endif
607 #endif
610 bool CamuleDaemonApp::OnInit()
612 if ( !CamuleApp::OnInit() ) {
613 return false;
615 AddLogLineNS(_("amuled: OnInit - starting timer"));
616 core_timer = new CTimer(this,ID_CORE_TIMER_EVENT);
617 core_timer->Start(CORE_TIMER_PERIOD);
618 glob_prefs->GetCategory(0)->title = GetCatTitle(thePrefs::GetAllcatFilter());
619 glob_prefs->GetCategory(0)->path = thePrefs::GetIncomingDir();
621 return true;
624 int CamuleDaemonApp::InitGui(bool ,wxString &)
626 #ifndef __WXMSW__
627 if ( !enable_daemon_fork ) {
628 return 0;
630 AddLogLineNS(_("amuled: forking to background - see you"));
631 theLogger.SetEnabledStdoutLog(false);
633 // fork to background and detach from controlling tty
634 // while redirecting stdout to /dev/null
636 for(int i_fd = 0;i_fd < 3; i_fd++) {
637 close(i_fd);
639 int fd = open("/dev/null",O_RDWR);
640 if (dup(fd)){} // prevent GCC warning
641 if (dup(fd)){}
642 pid_t pid = fork();
644 wxASSERT(pid != -1);
646 if ( pid ) {
647 exit(0);
648 } else {
649 pid = setsid();
651 // Create a Pid file with the Pid of the Child, so any daemon-manager
652 // can easily manage the process
654 if (!m_PidFile.IsEmpty()) {
655 wxString temp = CFormat(wxT("%d\n")) % pid;
656 wxFFile ff(m_PidFile, wxT("w"));
657 if (!ff.Error()) {
658 ff.Write(temp);
659 ff.Close();
660 } else {
661 AddLogLineNS(_("Cannot Create Pid File"));
666 #endif
667 return 0;
671 int CamuleDaemonApp::OnExit()
673 #ifdef AMULED28
675 * Stop all socket threads before entering
676 * shutdown sequence.
678 delete listensocket;
679 listensocket = 0;
680 if (clientudp) {
681 delete clientudp;
682 clientudp = NULL;
684 #endif
686 ShutDown();
688 #ifndef __WXMSW__
689 int ret = sigaction(SIGCHLD, &m_oldSignalChildAction, NULL);
690 if (ret == -1) {
691 AddDebugLogLineC(logStandard, CFormat(wxT("CamuleDaemonApp::OnRun(): second sigaction() failed: %m.")));
692 } else {
693 AddDebugLogLineN(logGeneral, wxT("CamuleDaemonApp::OnRun(): Uninstallation of SIGCHLD callback with sigaction() succeeded."));
695 #endif // __WXMSW__
697 // lfroen: delete socket threads
698 if (ECServerHandler) {
699 ECServerHandler = 0;
702 delete core_timer;
704 return CamuleApp::OnExit();
708 int CamuleDaemonApp::ShowAlert(wxString msg, wxString title, int flags)
710 if ( flags | wxICON_ERROR ) {
711 title = CFormat(_("ERROR: %s")) % title;
713 AddLogLineCS(title + wxT(" ") + msg);
715 return 0; // That's neither yes nor no, ok, cancel
718 // File_checked_for_headers