Upstream tarball 10002
[amule.git] / src / amuled.cpp
blobcb65d2d433941d0c947db6907cc77e72e5bacb7d
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, 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 // Prefer the POSIX interface to strerror_r()
50 #ifndef _XOPEN_SOURCE
51 #define _XOPEN_SOURCE 600
52 #endif
53 #include <string.h> // Do_not_auto_remove
55 #include <wx/utils.h>
57 #include "Preferences.h" // Needed for CPreferences
58 #include "PartFile.h" // Needed for CPartFile
59 #include "Logger.h"
60 #include <common/Format.h>
61 #include "InternalEvents.h" // Needed for wxEVT_*
62 #include "ThreadTasks.h"
63 #include "GuiEvents.h" // Needed for EVT_MULE_NOTIFY
64 #include "Timer.h" // Needed for EVT_MULE_TIMER
66 #include "ClientUDPSocket.h" // Do_not_auto_remove (forward declaration not enough)
67 #include "ListenSocket.h" // Do_not_auto_remove (forward declaration not enough)
70 #include <errno.h>
71 #ifdef HAVE_SYS_RESOURCE_H
72 #include <sys/resource.h> // Do_not_auto_remove
73 #endif
75 #ifndef __WXMSW__
76 #ifdef HAVE_SYS_WAIT_H
77 #include <sys/wait.h> // Do_not_auto_remove
78 #endif
80 #include <wx/unix/execute.h>
81 #endif
83 #ifndef HAVE_STRERROR_R
85 // Replacement strerror_r() function for systems that don't have any.
86 // Note that this replacement function is NOT thread-safe!
87 static int rpl_strerror_r(int errnum, char *buf, size_t buflen)
89 char *tmp = strerror(errnum);
90 if (tmp == NULL) {
91 errno = EINVAL;
92 return -1;
93 } else {
94 strncpy(buf, tmp, buflen - 1);
95 buf[buflen - 1] = '\0';
96 if (strlen(tmp) >= buflen) {
97 errno = ERANGE;
98 return -1;
101 return 0;
104 # define strerror_r(errnum, buf, buflen) rpl_strerror_r(errnum, buf, buflen)
105 #else
106 # ifdef STRERROR_R_CHAR_P
108 // Replacement strerror_r() function for systems that return a char*.
109 static int rpl_strerror_r(int errnum, char *buf, size_t buflen)
111 char *tmp = strerror_r(errnum, buf, buflen);
112 if (tmp == NULL) {
113 errno = EINVAL;
114 return -1;
115 } else if (tmp != buf) {
116 strncpy(buf, tmp, buflen - 1);
117 buf[buflen - 1] = '\0';
118 if (strlen(tmp) >= buflen) {
119 errno = ERANGE;
120 return -1;
123 return 0;
126 # define strerror_r(errnum, buf, buflen) rpl_strerror_r(errnum, buf, buflen)
127 # endif
128 #endif
130 BEGIN_EVENT_TABLE(CamuleDaemonApp, wxAppConsole)
132 // Socket handlers
135 // Listen Socket
136 EVT_SOCKET(ID_LISTENSOCKET_EVENT, CamuleDaemonApp::ListenSocketHandler)
138 // UDP Socket (servers)
139 EVT_SOCKET(ID_SERVERUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
140 // UDP Socket (clients)
141 EVT_SOCKET(ID_CLIENTUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
143 // Socket timer (TCP)
144 EVT_MULE_TIMER(ID_SERVER_RETRY_TIMER_EVENT, CamuleDaemonApp::OnTCPTimer)
146 // Core timer
147 EVT_MULE_TIMER(ID_CORE_TIMER_EVENT, CamuleDaemonApp::OnCoreTimer)
149 EVT_MULE_NOTIFY(CamuleDaemonApp::OnNotifyEvent)
151 // Async dns handling
152 EVT_MULE_INTERNAL(wxEVT_CORE_UDP_DNS_DONE, -1, CamuleDaemonApp::OnUDPDnsDone)
154 EVT_MULE_INTERNAL(wxEVT_CORE_SOURCE_DNS_DONE, -1, CamuleDaemonApp::OnSourceDnsDone)
156 EVT_MULE_INTERNAL(wxEVT_CORE_SERVER_DNS_DONE, -1, CamuleDaemonApp::OnServerDnsDone)
158 // Hash ended notifier
159 EVT_MULE_HASHING(CamuleDaemonApp::OnFinishedHashing)
160 EVT_MULE_AICH_HASHING(CamuleDaemonApp::OnFinishedAICHHashing)
162 // File completion ended notifier
163 EVT_MULE_FILE_COMPLETED(CamuleDaemonApp::OnFinishedCompletion)
165 // HTTPDownload finished
166 EVT_MULE_INTERNAL(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD, -1, CamuleDaemonApp::OnFinishedHTTPDownload)
168 // Disk space preallocation finished
169 EVT_MULE_ALLOC_FINISHED(CamuleDaemonApp::OnFinishedAllocation)
170 END_EVENT_TABLE()
172 IMPLEMENT_APP(CamuleDaemonApp)
174 #ifdef AMULED28
176 * Socket handling in wxBase
179 class CSocketSet {
180 int m_count;
181 int m_fds[FD_SETSIZE], m_fd_idx[FD_SETSIZE];
182 GSocket *m_gsocks[FD_SETSIZE];
184 fd_set m_set;
185 public:
186 CSocketSet();
187 void AddSocket(GSocket *);
188 void RemoveSocket(GSocket *);
189 void FillSet(int &max_fd);
191 void Detected(void (GSocket::*func)());
193 fd_set *Set() { return &m_set; }
196 CSocketSet::CSocketSet()
198 m_count = 0;
199 for(int i = 0; i < FD_SETSIZE; i++) {
200 m_fds[i] = 0;
201 m_fd_idx[i] = 0xffff;
202 m_gsocks[i] = 0;
206 void CSocketSet::AddSocket(GSocket *socket)
208 wxASSERT(socket);
210 int fd = socket->m_fd;
212 if ( fd == -1 ) {
213 return;
216 wxASSERT( (fd > 2) && (fd < FD_SETSIZE) );
218 if ( m_gsocks[fd] ) {
219 return;
221 m_fds[m_count] = fd;
222 m_fd_idx[fd] = m_count;
223 m_gsocks[fd] = socket;
224 m_count++;
227 void CSocketSet::RemoveSocket(GSocket *socket)
229 wxASSERT(socket);
231 int fd = socket->m_fd;
233 if ( fd == -1 ) {
234 return;
237 wxASSERT( (fd > 2) && (fd < FD_SETSIZE) );
239 int i = m_fd_idx[fd];
240 if ( i == 0xffff ) {
241 return;
243 wxASSERT(m_fds[i] == fd);
244 m_fds[i] = m_fds[m_count-1];
245 m_gsocks[fd] = 0;
246 m_fds[m_count-1] = 0;
247 m_fd_idx[fd] = 0xffff;
248 m_fd_idx[m_fds[i]] = i;
249 m_count--;
252 void CSocketSet::FillSet(int &max_fd)
254 FD_ZERO(&m_set);
256 for(int i = 0; i < m_count; i++) {
257 FD_SET(m_fds[i], &m_set);
258 if ( m_fds[i] > max_fd ) {
259 max_fd = m_fds[i];
264 void CSocketSet::Detected(void (GSocket::*func)())
266 for (int i = 0; i < m_count; i++) {
267 int fd = m_fds[i];
268 if ( FD_ISSET(fd, &m_set) ) {
269 GSocket *socket = m_gsocks[fd];
270 (*socket.*func)();
275 CAmuledGSocketFuncTable::CAmuledGSocketFuncTable() : m_lock(wxMUTEX_RECURSIVE)
277 m_in_set = new CSocketSet;
278 m_out_set = new CSocketSet;
280 m_lock.Unlock();
283 void CAmuledGSocketFuncTable::AddSocket(GSocket *socket, GSocketEvent event)
285 wxMutexLocker lock(m_lock);
287 if ( event == GSOCK_INPUT ) {
288 m_in_set->AddSocket(socket);
289 } else {
290 m_out_set->AddSocket(socket);
294 void CAmuledGSocketFuncTable::RemoveSocket(GSocket *socket, GSocketEvent event)
296 wxMutexLocker lock(m_lock);
298 if ( event == GSOCK_INPUT ) {
299 m_in_set->RemoveSocket(socket);
300 } else {
301 m_out_set->RemoveSocket(socket);
305 void CAmuledGSocketFuncTable::RunSelect()
307 wxMutexLocker lock(m_lock);
309 int max_fd = -1;
310 m_in_set->FillSet(max_fd);
311 m_out_set->FillSet(max_fd);
313 struct timeval tv;
314 tv.tv_sec = 0;
315 tv.tv_usec = 10000; // 10ms
317 int result = select(max_fd + 1, m_in_set->Set(), m_out_set->Set(), 0, &tv);
318 if ( result > 0 ) {
319 m_in_set->Detected(&GSocket::Detected_Read);
320 m_out_set->Detected(&GSocket::Detected_Write);
324 GSocketGUIFunctionsTable *CDaemonAppTraits::GetSocketGUIFunctionsTable()
326 return m_table;
329 bool CAmuledGSocketFuncTable::OnInit()
331 return true;
334 void CAmuledGSocketFuncTable::OnExit()
338 bool CAmuledGSocketFuncTable::CanUseEventLoop()
341 * FIXME: (lfroen) Not sure whether it's right.
342 * I will review it later.
344 return false;
347 bool CAmuledGSocketFuncTable::Init_Socket(GSocket *)
349 return true;
352 void CAmuledGSocketFuncTable::Destroy_Socket(GSocket *)
356 void CAmuledGSocketFuncTable::Install_Callback(GSocket *sock, GSocketEvent e)
358 AddSocket(sock, e);
361 void CAmuledGSocketFuncTable::Uninstall_Callback(GSocket *sock, GSocketEvent e)
363 RemoveSocket(sock, e);
366 void CAmuledGSocketFuncTable::Enable_Events(GSocket *socket)
368 Install_Callback(socket, GSOCK_INPUT);
369 Install_Callback(socket, GSOCK_OUTPUT);
372 void CAmuledGSocketFuncTable::Disable_Events(GSocket *socket)
374 Uninstall_Callback(socket, GSOCK_INPUT);
375 Uninstall_Callback(socket, GSOCK_OUTPUT);
378 #endif // AMULED28
380 #ifndef __WXMSW__
382 #ifdef AMULED28
384 CDaemonAppTraits::CDaemonAppTraits(CAmuledGSocketFuncTable *table)
386 wxConsoleAppTraits(),
387 m_oldSignalChildAction(),
388 m_newSignalChildAction(),
389 m_table(table),
390 m_lock(wxMUTEX_RECURSIVE),
391 m_sched_delete()
393 m_lock.Unlock();
397 void CDaemonAppTraits::ScheduleForDestroy(wxObject *object)
399 wxMutexLocker lock(m_lock);
401 //delete object;
402 m_sched_delete.push_back(object);
405 void CDaemonAppTraits::RemoveFromPendingDelete(wxObject *object)
407 wxMutexLocker lock(m_lock);
409 for(std::list<wxObject *>::iterator i = m_sched_delete.begin();
410 i != m_sched_delete.end(); i++) {
411 if ( *i == object ) {
412 m_sched_delete.erase(i);
413 return;
418 void CDaemonAppTraits::DeletePending()
420 wxMutexLocker lock(m_lock);
422 while ( !m_sched_delete.empty() ) {
423 std::list<wxObject *>::iterator i = m_sched_delete.begin();
424 wxObject *object = *i;
425 delete object;
427 //m_sched_delete.erase(m_sched_delete.begin(), m_sched_delete.end());
430 wxAppTraits *CamuleDaemonApp::CreateTraits()
432 return new CDaemonAppTraits(m_table);
435 #else // AMULED28
437 CDaemonAppTraits::CDaemonAppTraits()
439 wxConsoleAppTraits(),
440 m_oldSignalChildAction(),
441 m_newSignalChildAction()
445 wxAppTraits *CamuleDaemonApp::CreateTraits()
447 return new CDaemonAppTraits();
450 #endif // !AMULED28
452 #endif // __WXMSW__
454 #if defined(__WXMAC__) && !wxCHECK_VERSION(2, 9, 0)
455 #include <wx/stdpaths.h> // Do_not_auto_remove (guess)
456 static wxStandardPathsCF gs_stdPaths;
457 wxStandardPathsBase& CDaemonAppTraits::GetStandardPaths()
459 return gs_stdPaths;
461 #endif
464 #ifdef AMULED28
466 CamuleDaemonApp::CamuleDaemonApp()
468 m_Exit(false),
469 m_table(new CAmuledGSocketFuncTable())
471 wxPendingEventsLocker = new wxCriticalSection;
474 #endif // !AMULED28
477 #ifndef __WXMSW__
480 static EndProcessDataMap endProcDataMap;
483 int CDaemonAppTraits::WaitForChild(wxExecuteData &execData)
485 int status = 0;
486 pid_t result = 0;
487 // Build the log message
488 wxString msg;
489 msg << wxT("WaitForChild() has been called for child process with pid `") <<
490 execData.pid <<
491 wxT("'. ");
493 if (execData.flags & wxEXEC_SYNC) {
494 result = AmuleWaitPid(execData.pid, &status, 0, &msg);
495 if (result == -1 || (!WIFEXITED(status) && !WIFSIGNALED(status))) {
496 msg << wxT(" Waiting for subprocess termination failed.");
497 AddDebugLogLineM(false, logGeneral, msg);
499 } else {
500 /** wxEXEC_ASYNC */
501 // Give the process a chance to start or forked child to exit
502 // 1 second is enough time to fail on "path not found"
503 wxSleep(1);
504 result = AmuleWaitPid(execData.pid, &status, WNOHANG, &msg);
505 if (result == 0) {
506 // Add a WxEndProcessData entry to the map, so that we can
507 // support process termination
508 wxEndProcessData *endProcData = new wxEndProcessData();
509 endProcData->pid = execData.pid;
510 endProcData->process = execData.process;
511 endProcData->tag = 0;
512 endProcDataMap[execData.pid] = endProcData;
514 status = execData.pid;
515 } else {
516 // if result != 0, then either waitpid() failed (result == -1)
517 // and there is nothing we can do, or the child has changed
518 // status, which means it is probably dead.
519 status = 0;
523 // Log our passage here
524 AddDebugLogLineM(false, logGeneral, msg);
526 return status;
530 void OnSignalChildHandler(int /*signal*/, siginfo_t *siginfo, void * /*ucontext*/)
532 // Build the log message
533 wxString msg;
534 msg << wxT("OnSignalChildHandler() has been called for child process with pid `") <<
535 siginfo->si_pid <<
536 wxT("'. ");
537 // Make sure we leave no zombies by calling waitpid()
538 int status = 0;
539 pid_t result = AmuleWaitPid(siginfo->si_pid, &status, WNOHANG, &msg);
540 if (result != 1 && result != 0 && (WIFEXITED(status) || WIFSIGNALED(status))) {
541 // Fetch the wxEndProcessData structure corresponding to this pid
542 EndProcessDataMap::iterator it = endProcDataMap.find(siginfo->si_pid);
543 if (it != endProcDataMap.end()) {
544 wxEndProcessData *endProcData = it->second;
545 // Remove this entry from the process map
546 endProcDataMap.erase(siginfo->si_pid);
547 // Save the exit code for the wxProcess object to read later
548 endProcData->exitcode = result != -1 && WIFEXITED(status) ?
549 WEXITSTATUS(status) : -1;
550 // Make things work as in wxGUI
551 wxHandleProcessTermination(endProcData);
553 // wxHandleProcessTermination() will "delete endProcData;"
554 // So we do not delete it again, ok? Do not uncomment this line.
555 //delete endProcData;
556 } else {
557 msg << wxT(" Error: the child process pid is not on the pid map.");
561 // Log our passage here
562 AddDebugLogLineM(false, logGeneral, msg);
566 pid_t AmuleWaitPid(pid_t pid, int *status, int options, wxString *msg)
568 // strerror_r() buffer
569 const int ERROR_BUFFER_LEN = 256;
570 char errorBuffer[ERROR_BUFFER_LEN];
572 *status = 0;
573 pid_t result = waitpid(pid, status, options);
574 if (result == -1) {
575 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
576 *msg << wxT("Error: waitpid() call failed: ") <<
577 char2unicode(errorBuffer) <<
578 wxT(".");
579 } else if (result == 0) {
580 if (options & WNOHANG) {
581 *msg << wxT("The child is alive.");
582 } else {
583 *msg << wxT("Error: waitpid() call returned 0 but "
584 "WNOHANG was not specified in options.");
586 } else {
587 if (WIFEXITED(*status)) {
588 *msg << wxT("Child has terminated with status code `") <<
589 WEXITSTATUS(*status) <<
590 wxT("'.");
591 } else if (WIFSIGNALED(*status)) {
592 *msg << wxT("Child was killed by signal `") <<
593 WTERMSIG(*status) <<
594 wxT("'.");
595 if (WCOREDUMP(*status)) {
596 *msg << wxT(" A core file has been dumped.");
598 } else if (WIFSTOPPED(*status)) {
599 *msg << wxT("Child has been stopped by signal `") <<
600 WSTOPSIG(*status) <<
601 wxT("'.");
602 #ifdef WIFCONTINUED /* Only found in recent kernels. */
603 } else if (WIFCONTINUED(*status)) {
604 *msg << wxT("Child has received `SIGCONT' and has continued execution.");
605 #endif
606 } else {
607 *msg << wxT("The program was not able to determine why the child has signaled.");
611 return result;
615 #endif // __WXMSW__
618 int CamuleDaemonApp::OnRun()
620 if (!thePrefs::AcceptExternalConnections()) {
621 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"));
622 return 0;
623 } else if (thePrefs::ECPassword().IsEmpty()) {
624 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"));
625 return 0;
628 #ifndef __WXMSW__
629 // strerror_r() buffer
630 const int ERROR_BUFFER_LEN = 256;
631 char errorBuffer[ERROR_BUFFER_LEN];
632 wxString msg;
634 // Process the return code of dead children so that we do not create
635 // zombies. wxBase does not implement wxProcess callbacks, so no one
636 // actualy calls wxHandleProcessTermination() in console applications.
637 // We do our best here.
638 int ret = 0;
639 ret = sigaction(SIGCHLD, NULL, &m_oldSignalChildAction);
640 m_newSignalChildAction = m_oldSignalChildAction;
641 m_newSignalChildAction.sa_sigaction = OnSignalChildHandler;
642 m_newSignalChildAction.sa_flags |= SA_SIGINFO;
643 m_newSignalChildAction.sa_flags &= ~SA_RESETHAND;
644 ret = sigaction(SIGCHLD, &m_newSignalChildAction, NULL);
645 if (ret == -1) {
646 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
647 msg << wxT("CamuleDaemonApp::OnRun(): "
648 "Installation of SIGCHLD callback with sigaction() failed: ") <<
649 char2unicode(errorBuffer) <<
650 wxT(".");
651 AddLogLineM(true, msg);
652 } else {
653 msg << wxT("CamuleDaemonApp::OnRun(): Installation of SIGCHLD "
654 "callback with sigaction() succeeded.");
655 AddDebugLogLineM(false, logGeneral, msg);
657 #endif // __WXMSW__
659 #ifdef AMULED28
661 while ( !m_Exit ) {
662 m_table->RunSelect();
663 ProcessPendingEvents();
664 ((CDaemonAppTraits *)GetTraits())->DeletePending();
667 // ShutDown is beeing called twice. Once here and again in OnExit().
668 ShutDown();
670 return 0;
672 #else
674 #ifdef AMULED_DUMMY
675 return 0;
676 #else
677 return wxApp::OnRun();
678 #endif
680 #endif
683 bool CamuleDaemonApp::OnInit()
685 if ( !CamuleApp::OnInit() ) {
686 return false;
688 AddLogLineNS(_("amuled: OnInit - starting timer"));
689 core_timer = new CTimer(this,ID_CORE_TIMER_EVENT);
690 core_timer->Start(CORE_TIMER_PERIOD);
691 glob_prefs->GetCategory(0)->title = GetCatTitle(thePrefs::GetAllcatType());
692 glob_prefs->GetCategory(0)->path = thePrefs::GetIncomingDir();
694 return true;
697 int CamuleDaemonApp::InitGui(bool ,wxString &)
699 #ifndef __WXMSW__
700 if ( !enable_daemon_fork ) {
701 return 0;
703 AddLogLineNS(_("amuled: forking to background - see you"));
704 theLogger.SetEnabledStdoutLog(false);
706 // fork to background and detach from controlling tty
707 // while redirecting stdout to /dev/null
709 for(int i_fd = 0;i_fd < 3; i_fd++) {
710 close(i_fd);
712 int fd = open("/dev/null",O_RDWR);
713 if (dup(fd)){} // prevent GCC warning
714 if (dup(fd)){}
715 pid_t pid = fork();
717 wxASSERT(pid != -1);
719 if ( pid ) {
720 exit(0);
721 } else {
722 pid = setsid();
724 // Create a Pid file with the Pid of the Child, so any daemon-manager
725 // can easily manage the process
727 if (!m_PidFile.IsEmpty()) {
728 wxString temp = wxString::Format(wxT("%d\n"), pid);
729 wxFFile ff(m_PidFile, wxT("w"));
730 if (!ff.Error()) {
731 ff.Write(temp);
732 ff.Close();
733 } else {
734 AddLogLineNS(_("Cannot Create Pid File"));
739 #endif
740 return 0;
744 int CamuleDaemonApp::OnExit()
746 #ifdef AMULED28
748 * Stop all socket threads before entering
749 * shutdown sequence.
751 delete listensocket;
752 listensocket = 0;
753 if (clientudp) {
754 delete clientudp;
755 clientudp = NULL;
757 #endif
759 ShutDown();
761 #ifndef __WXMSW__
762 const int ERROR_BUFFER_LEN = 256;
763 char errorBuffer[ERROR_BUFFER_LEN];
764 wxString msg;
765 int ret = sigaction(SIGCHLD, &m_oldSignalChildAction, NULL);
766 if (ret == -1) {
767 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
768 msg << wxT("CamuleDaemonApp::OnRun(): second sigaction() failed: ") <<
769 char2unicode(errorBuffer) <<
770 wxT(".");
771 AddLogLineM(true, msg);
772 } else {
773 msg << wxT("CamuleDaemonApp::OnRun(): Uninstallation of SIGCHLD "
774 "callback with sigaction() succeeded.");
775 AddDebugLogLineM(false, logGeneral, msg);
777 #endif // __WXMSW__
779 // lfroen: delete socket threads
780 if (ECServerHandler) {
781 ECServerHandler = 0;
784 delete core_timer;
786 return CamuleApp::OnExit();
790 int CamuleDaemonApp::ShowAlert(wxString msg, wxString title, int flags)
792 if ( flags | wxICON_ERROR ) {
793 title = CFormat(_("ERROR: %s")) % title;
795 AddLogLineCS(title + wxT(" ") + msg);
797 return 0; // That's neither yes nor no, ok, cancel
800 // File_checked_for_headers