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.
14//
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
23//
24
25
26#include "amule.h"			// Interface declarations.
27
28#include <include/common/EventIDs.h>
29
30#ifdef HAVE_CONFIG_H
31	#include "config.h"		// Needed for HAVE_SYS_RESOURCE_H, etc
32#endif
33
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
48
49#include <wx/utils.h>
50
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
59
60#include "ClientUDPSocket.h"		// Do_not_auto_remove (forward declaration not enough)
61#include "ListenSocket.h"		// Do_not_auto_remove (forward declaration not enough)
62
63
64#ifdef HAVE_SYS_RESOURCE_H
65#include <sys/resource.h> // Do_not_auto_remove
66#endif
67
68#ifndef __WXMSW__
69	#ifdef  HAVE_SYS_WAIT_H
70		#include <sys/wait.h> // Do_not_auto_remove
71	#endif
72
73	#include <wx/unix/execute.h>
74#endif
75
76BEGIN_EVENT_TABLE(CamuleDaemonApp, wxAppConsole)
77	//
78	// Socket handlers
79	//
80
81	// Listen Socket
82	EVT_SOCKET(ID_LISTENSOCKET_EVENT, CamuleDaemonApp::ListenSocketHandler)
83
84	// UDP Socket (servers)
85	EVT_SOCKET(ID_SERVERUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
86	// UDP Socket (clients)
87	EVT_SOCKET(ID_CLIENTUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
88
89	// Socket timer (TCP)
90	EVT_MULE_TIMER(ID_SERVER_RETRY_TIMER_EVENT, CamuleDaemonApp::OnTCPTimer)
91
92	// Core timer
93	EVT_MULE_TIMER(ID_CORE_TIMER_EVENT, CamuleDaemonApp::OnCoreTimer)
94
95	EVT_MULE_NOTIFY(CamuleDaemonApp::OnNotifyEvent)
96
97	// Async dns handling
98	EVT_MULE_INTERNAL(wxEVT_CORE_UDP_DNS_DONE, -1, CamuleDaemonApp::OnUDPDnsDone)
99
100	EVT_MULE_INTERNAL(wxEVT_CORE_SOURCE_DNS_DONE, -1, CamuleDaemonApp::OnSourceDnsDone)
101
102	EVT_MULE_INTERNAL(wxEVT_CORE_SERVER_DNS_DONE, -1, CamuleDaemonApp::OnServerDnsDone)
103
104	// Hash ended notifier
105	EVT_MULE_HASHING(CamuleDaemonApp::OnFinishedHashing)
106	EVT_MULE_AICH_HASHING(CamuleDaemonApp::OnFinishedAICHHashing)
107
108	// File completion ended notifier
109	EVT_MULE_FILE_COMPLETED(CamuleDaemonApp::OnFinishedCompletion)
110
111	// HTTPDownload finished
112	EVT_MULE_INTERNAL(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD, -1, CamuleDaemonApp::OnFinishedHTTPDownload)
113
114	// Disk space preallocation finished
115	EVT_MULE_ALLOC_FINISHED(CamuleDaemonApp::OnFinishedAllocation)
116END_EVENT_TABLE()
117
118IMPLEMENT_APP(CamuleDaemonApp)
119
120#ifdef AMULED28
121/*
122 * Socket handling in wxBase
123 *
124 */
125class CSocketSet {
126		int m_count;
127		int m_fds[FD_SETSIZE], m_fd_idx[FD_SETSIZE];
128		GSocket *m_gsocks[FD_SETSIZE];
129
130		fd_set m_set;
131	public:
132		CSocketSet();
133		void AddSocket(GSocket *);
134		void RemoveSocket(GSocket *);
135		void FillSet(int &max_fd);
136
137		void Detected(void (GSocket::*func)());
138
139		fd_set *Set() { return &m_set; }
140};
141
142CSocketSet::CSocketSet()
143{
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;
149	}
150}
151
152void CSocketSet::AddSocket(GSocket *socket)
153{
154	wxASSERT(socket);
155
156	int fd = socket->m_fd;
157
158	if ( fd == -1 ) {
159		return;
160	}
161
162	wxASSERT( (fd > 2) && (fd < FD_SETSIZE) );
163
164	if ( m_gsocks[fd] ) {
165		return;
166	}
167	m_fds[m_count] = fd;
168	m_fd_idx[fd] = m_count;
169	m_gsocks[fd] = socket;
170	m_count++;
171}
172
173void CSocketSet::RemoveSocket(GSocket *socket)
174{
175	wxASSERT(socket);
176
177	int fd = socket->m_fd;
178
179	if ( fd == -1 ) {
180		return;
181	}
182
183	wxASSERT( (fd > 2) && (fd < FD_SETSIZE) );
184
185	int i = m_fd_idx[fd];
186	if ( i == 0xffff ) {
187		return;
188	}
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--;
196}
197
198void CSocketSet::FillSet(int &max_fd)
199{
200	FD_ZERO(&m_set);
201
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];
206	    }
207	}
208}
209
210void CSocketSet::Detected(void (GSocket::*func)())
211{
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)();
217		}
218	}
219}
220
221CAmuledGSocketFuncTable::CAmuledGSocketFuncTable() : m_lock(wxMUTEX_RECURSIVE)
222{
223	m_in_set = new CSocketSet;
224	m_out_set = new CSocketSet;
225
226	m_lock.Unlock();
227}
228
229void CAmuledGSocketFuncTable::AddSocket(GSocket *socket, GSocketEvent event)
230{
231	wxMutexLocker lock(m_lock);
232
233	if ( event == GSOCK_INPUT ) {
234		m_in_set->AddSocket(socket);
235	} else {
236		m_out_set->AddSocket(socket);
237	}
238}
239
240void CAmuledGSocketFuncTable::RemoveSocket(GSocket *socket, GSocketEvent event)
241{
242	wxMutexLocker lock(m_lock);
243
244	if ( event == GSOCK_INPUT ) {
245		m_in_set->RemoveSocket(socket);
246	} else {
247		m_out_set->RemoveSocket(socket);
248	}
249}
250
251void CAmuledGSocketFuncTable::RunSelect()
252{
253	wxMutexLocker lock(m_lock);
254
255	int max_fd = -1;
256	m_in_set->FillSet(max_fd);
257	m_out_set->FillSet(max_fd);
258
259	struct timeval tv;
260	tv.tv_sec = 0;
261	tv.tv_usec = 10000; // 10ms
262
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);
267	}
268}
269
270GSocketGUIFunctionsTable *CDaemonAppTraits::GetSocketGUIFunctionsTable()
271{
272	return m_table;
273}
274
275bool CAmuledGSocketFuncTable::OnInit()
276{
277	return true;
278}
279
280void CAmuledGSocketFuncTable::OnExit()
281{
282}
283
284bool CAmuledGSocketFuncTable::CanUseEventLoop()
285{
286	/*
287	 * FIXME: (lfroen) Not sure whether it's right.
288	 * I will review it later.
289	 */
290	return false;
291}
292
293bool CAmuledGSocketFuncTable::Init_Socket(GSocket *)
294{
295	return true;
296}
297
298void CAmuledGSocketFuncTable::Destroy_Socket(GSocket *)
299{
300}
301
302void CAmuledGSocketFuncTable::Install_Callback(GSocket *sock, GSocketEvent e)
303{
304	AddSocket(sock, e);
305}
306
307void CAmuledGSocketFuncTable::Uninstall_Callback(GSocket *sock, GSocketEvent e)
308{
309	RemoveSocket(sock, e);
310}
311
312void CAmuledGSocketFuncTable::Enable_Events(GSocket *socket)
313{
314	Install_Callback(socket, GSOCK_INPUT);
315	Install_Callback(socket, GSOCK_OUTPUT);
316}
317
318void CAmuledGSocketFuncTable::Disable_Events(GSocket *socket)
319{
320	Uninstall_Callback(socket, GSOCK_INPUT);
321	Uninstall_Callback(socket, GSOCK_OUTPUT);
322}
323
324#endif	// AMULED28
325
326#ifndef __WXMSW__
327
328#ifdef AMULED28
329
330CDaemonAppTraits::CDaemonAppTraits(CAmuledGSocketFuncTable *table)
331:
332wxConsoleAppTraits(),
333m_oldSignalChildAction(),
334m_newSignalChildAction(),
335m_table(table),
336m_lock(wxMUTEX_RECURSIVE),
337m_sched_delete()
338{
339	m_lock.Unlock();
340}
341
342
343void CDaemonAppTraits::ScheduleForDestroy(wxObject *object)
344{
345	wxMutexLocker lock(m_lock);
346
347	//delete object;
348	m_sched_delete.push_back(object);
349}
350
351void CDaemonAppTraits::RemoveFromPendingDelete(wxObject *object)
352{
353	wxMutexLocker lock(m_lock);
354
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;
360			}
361		}
362}
363
364void CDaemonAppTraits::DeletePending()
365{
366	wxMutexLocker lock(m_lock);
367
368	while ( !m_sched_delete.empty() ) {
369		std::list<wxObject *>::iterator i = m_sched_delete.begin();
370		wxObject *object = *i;
371		delete object;
372	}
373	//m_sched_delete.erase(m_sched_delete.begin(), m_sched_delete.end());
374}
375
376wxAppTraits *CamuleDaemonApp::CreateTraits()
377{
378	return new CDaemonAppTraits(m_table);
379}
380
381#else	// AMULED28
382
383CDaemonAppTraits::CDaemonAppTraits()
384:
385wxConsoleAppTraits(),
386m_oldSignalChildAction(),
387m_newSignalChildAction()
388{
389}
390
391wxAppTraits *CamuleDaemonApp::CreateTraits()
392{
393	return new CDaemonAppTraits();
394}
395
396#endif	// !AMULED28
397
398#endif	// __WXMSW__
399
400#if defined(__WXMAC__) && !wxCHECK_VERSION(2, 9, 0)
401#include <wx/stdpaths.h> // Do_not_auto_remove (guess)
402static wxStandardPathsCF gs_stdPaths;
403wxStandardPathsBase& CDaemonAppTraits::GetStandardPaths()
404{
405	return gs_stdPaths;
406}
407#endif
408
409
410#ifdef AMULED28
411
412CamuleDaemonApp::CamuleDaemonApp()
413:
414m_Exit(false),
415m_table(new CAmuledGSocketFuncTable())
416{
417	wxPendingEventsLocker = new wxCriticalSection;
418}
419
420#endif	// !AMULED28
421
422
423#ifndef __WXMSW__
424
425
426static EndProcessDataMap endProcDataMap;
427
428
429int CDaemonAppTraits::WaitForChild(wxExecuteData &execData)
430{
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("'. ");
438
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);
444		}
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;
459
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;
466		}
467	}
468
469	// Log our passage here
470	AddDebugLogLineN(logGeneral, msg);
471
472	return status;
473}
474
475
476void OnSignalChildHandler(int /*signal*/, siginfo_t *siginfo, void * /*ucontext*/)
477{
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);
498
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.");
504		}
505	}
506
507	// Log our passage here
508	AddDebugLogLineN(logGeneral, msg);
509}
510
511
512pid_t AmuleWaitPid(pid_t pid, int *status, int options, wxString *msg)
513{
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.");
524		}
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.");
536			}
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.");
547		}
548	}
549
550	return result;
551}
552
553
554#endif // __WXMSW__
555
556
557int CamuleDaemonApp::OnRun()
558{
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;
565	}
566
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."));
583	}
584#endif // __WXMSW__
585
586#ifdef AMULED28
587
588	while ( !m_Exit ) {
589		m_table->RunSelect();
590		ProcessPendingEvents();
591		((CDaemonAppTraits *)GetTraits())->DeletePending();
592	}
593
594	// ShutDown is beeing called twice. Once here and again in OnExit().
595	ShutDown();
596
597	return 0;
598
599#else
600
601#ifdef AMULED_DUMMY
602	return 0;
603#else
604	return wxApp::OnRun();
605#endif
606
607#endif
608}
609
610bool CamuleDaemonApp::OnInit()
611{
612	if ( !CamuleApp::OnInit() ) {
613		return false;
614	}
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();
620
621	return true;
622}
623
624int CamuleDaemonApp::InitGui(bool ,wxString &)
625{
626#ifndef __WXMSW__
627	if ( !enable_daemon_fork ) {
628		return 0;
629	}
630	AddLogLineNS(_("amuled: forking to background - see you"));
631	theLogger.SetEnabledStdoutLog(false);
632	//
633	// fork to background and detach from controlling tty
634	// while redirecting stdout to /dev/null
635	//
636	for(int i_fd = 0;i_fd < 3; i_fd++) {
637		close(i_fd);
638	}
639  	int fd = open("/dev/null",O_RDWR);
640	if (dup(fd)){}	// prevent GCC warning
641	if (dup(fd)){}
642  	pid_t pid = fork();
643
644	wxASSERT(pid != -1);
645
646  	if ( pid ) {
647  		exit(0);
648  	} else {
649		pid = setsid();
650		//
651		// Create a Pid file with the Pid of the Child, so any daemon-manager
652		// can easily manage the process
653		//
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"));
662			}
663		}
664  	}
665
666#endif
667	return 0;
668}
669
670
671int CamuleDaemonApp::OnExit()
672{
673#ifdef AMULED28
674	/*
675	 * Stop all socket threads before entering
676	 * shutdown sequence.
677	 */
678	delete listensocket;
679	listensocket = 0;
680	if (clientudp) {
681		delete clientudp;
682		clientudp = NULL;
683	}
684#endif
685
686	ShutDown();
687
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."));
694	}
695#endif // __WXMSW__
696
697	// lfroen: delete socket threads
698	if (ECServerHandler) {
699		ECServerHandler = 0;
700	}
701
702	delete core_timer;
703
704	return CamuleApp::OnExit();
705}
706
707
708int CamuleDaemonApp::ShowAlert(wxString msg, wxString title, int flags)
709{
710	if ( flags | wxICON_ERROR ) {
711		title = CFormat(_("ERROR: %s")) % title;
712	}
713	AddLogLineCS(title + wxT(" ") + msg);
714
715	return 0;	// That's neither yes nor no, ok, cancel
716}
717
718// File_checked_for_headers
719