Remove do-nothing command and add warning about it
[amule.git] / src / amuled.cpp
blobf631230a503039a7ca22c8ed1be85b467a1162fb
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.
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 #include "config.h" // Needed for HAVE_SYS_RESOURCE_H, etc
33 // Include the necessary headers for select(2), properly guarded
34 #if defined HAVE_SYS_SELECT_H && !defined __IRIX__
35 # include <sys/select.h>
36 #else
37 # ifdef HAVE_SYS_TIME_H
38 # include <sys/time.h>
39 # endif
40 # ifdef HAVE_SYS_TYPES_H
41 # include <sys/types.h>
42 # endif
43 # ifdef HAVE_UNISTD_H
44 # include <unistd.h>
45 # endif
46 #endif
48 #include <wx/utils.h>
50 #include "Preferences.h" // Needed for CPreferences
51 #include "PartFile.h" // Needed for CPartFile
52 #include "Logger.h"
53 #include <common/Format.h>
54 #include "InternalEvents.h" // Needed for wxEVT_*
55 #include "ThreadTasks.h"
56 #include "GuiEvents.h" // Needed for EVT_MULE_NOTIFY
57 #include "Timer.h" // Needed for EVT_MULE_TIMER
59 #include "ClientUDPSocket.h" // Do_not_auto_remove (forward declaration not enough)
60 #include "ListenSocket.h" // Do_not_auto_remove (forward declaration not enough)
63 #ifdef HAVE_SYS_RESOURCE_H
64 #include <sys/resource.h> // Do_not_auto_remove
65 #endif
67 #ifndef __WINDOWS__
68 #ifdef HAVE_SYS_WAIT_H
69 #include <sys/wait.h> // Do_not_auto_remove
70 #endif
71 #include <wx/ffile.h>
72 #endif
74 #ifdef AMULED_APPTRAITS
75 #include <wx/unix/execute.h>
76 #endif
79 BEGIN_EVENT_TABLE(CamuleDaemonApp, wxAppConsole)
81 #ifndef ASIO_SOCKETS
83 // Socket handlers
86 // Listen Socket
87 EVT_SOCKET(ID_LISTENSOCKET_EVENT, CamuleDaemonApp::ListenSocketHandler)
89 // UDP Socket (servers)
90 EVT_SOCKET(ID_SERVERUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
91 // UDP Socket (clients)
92 EVT_SOCKET(ID_CLIENTUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
93 #endif
95 // Socket timer (TCP)
96 EVT_MULE_TIMER(ID_SERVER_RETRY_TIMER_EVENT, CamuleDaemonApp::OnTCPTimer)
98 // Core timer
99 EVT_MULE_TIMER(ID_CORE_TIMER_EVENT, CamuleDaemonApp::OnCoreTimer)
101 EVT_MULE_NOTIFY(CamuleDaemonApp::OnNotifyEvent)
103 // Async dns handling
104 EVT_MULE_INTERNAL(wxEVT_CORE_UDP_DNS_DONE, -1, CamuleDaemonApp::OnUDPDnsDone)
106 EVT_MULE_INTERNAL(wxEVT_CORE_SOURCE_DNS_DONE, -1, CamuleDaemonApp::OnSourceDnsDone)
108 EVT_MULE_INTERNAL(wxEVT_CORE_SERVER_DNS_DONE, -1, CamuleDaemonApp::OnServerDnsDone)
110 // Hash ended notifier
111 EVT_MULE_HASHING(CamuleDaemonApp::OnFinishedHashing)
112 EVT_MULE_AICH_HASHING(CamuleDaemonApp::OnFinishedAICHHashing)
114 // File completion ended notifier
115 EVT_MULE_FILE_COMPLETED(CamuleDaemonApp::OnFinishedCompletion)
117 // HTTPDownload finished
118 EVT_MULE_INTERNAL(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD, -1, CamuleDaemonApp::OnFinishedHTTPDownload)
120 // Disk space preallocation finished
121 EVT_MULE_ALLOC_FINISHED(CamuleDaemonApp::OnFinishedAllocation)
122 END_EVENT_TABLE()
124 IMPLEMENT_APP(CamuleDaemonApp)
126 #ifdef AMULED28_SOCKETS
128 * Socket handling in wxBase
131 class CSocketSet {
132 int m_count;
133 int m_fds[FD_SETSIZE], m_fd_idx[FD_SETSIZE];
134 GSocket *m_gsocks[FD_SETSIZE];
136 fd_set m_set;
137 public:
138 CSocketSet();
139 void AddSocket(GSocket *);
140 void RemoveSocket(GSocket *);
141 void FillSet(int &max_fd);
143 void Detected(void (GSocket::*func)());
145 fd_set *Set() { return &m_set; }
148 CSocketSet::CSocketSet()
150 m_count = 0;
151 for(int i = 0; i < FD_SETSIZE; i++) {
152 m_fds[i] = 0;
153 m_fd_idx[i] = 0xffff;
154 m_gsocks[i] = 0;
158 void CSocketSet::AddSocket(GSocket *socket)
160 wxASSERT(socket);
162 int fd = socket->m_fd;
164 if ( fd == -1 ) {
165 return;
168 wxASSERT( (fd > 2) && (fd < FD_SETSIZE) );
170 if ( m_gsocks[fd] ) {
171 return;
173 m_fds[m_count] = fd;
174 m_fd_idx[fd] = m_count;
175 m_gsocks[fd] = socket;
176 m_count++;
179 void CSocketSet::RemoveSocket(GSocket *socket)
181 wxASSERT(socket);
183 int fd = socket->m_fd;
185 if ( fd == -1 ) {
186 return;
189 wxASSERT( (fd > 2) && (fd < FD_SETSIZE) );
191 int i = m_fd_idx[fd];
192 if ( i == 0xffff ) {
193 return;
195 wxASSERT(m_fds[i] == fd);
196 m_fds[i] = m_fds[m_count-1];
197 m_gsocks[fd] = 0;
198 m_fds[m_count-1] = 0;
199 m_fd_idx[fd] = 0xffff;
200 m_fd_idx[m_fds[i]] = i;
201 m_count--;
204 void CSocketSet::FillSet(int &max_fd)
206 FD_ZERO(&m_set);
208 for(int i = 0; i < m_count; i++) {
209 FD_SET(m_fds[i], &m_set);
210 if ( m_fds[i] > max_fd ) {
211 max_fd = m_fds[i];
216 void CSocketSet::Detected(void (GSocket::*func)())
218 for (int i = 0; i < m_count; i++) {
219 int fd = m_fds[i];
220 if ( FD_ISSET(fd, &m_set) ) {
221 GSocket *socket = m_gsocks[fd];
222 (*socket.*func)();
227 CAmuledGSocketFuncTable::CAmuledGSocketFuncTable() : m_lock(wxMUTEX_RECURSIVE)
229 m_in_set = new CSocketSet;
230 m_out_set = new CSocketSet;
232 m_lock.Unlock();
235 void CAmuledGSocketFuncTable::AddSocket(GSocket *socket, GSocketEvent event)
237 wxMutexLocker lock(m_lock);
239 if ( event == GSOCK_INPUT ) {
240 m_in_set->AddSocket(socket);
241 } else {
242 m_out_set->AddSocket(socket);
246 void CAmuledGSocketFuncTable::RemoveSocket(GSocket *socket, GSocketEvent event)
248 wxMutexLocker lock(m_lock);
250 if ( event == GSOCK_INPUT ) {
251 m_in_set->RemoveSocket(socket);
252 } else {
253 m_out_set->RemoveSocket(socket);
257 void CAmuledGSocketFuncTable::RunSelect()
259 wxMutexLocker lock(m_lock);
261 int max_fd = -1;
262 m_in_set->FillSet(max_fd);
263 m_out_set->FillSet(max_fd);
265 struct timeval tv;
266 tv.tv_sec = 0;
267 tv.tv_usec = 10000; // 10ms
269 int result = select(max_fd + 1, m_in_set->Set(), m_out_set->Set(), 0, &tv);
270 if ( result > 0 ) {
271 m_in_set->Detected(&GSocket::Detected_Read);
272 m_out_set->Detected(&GSocket::Detected_Write);
276 GSocketGUIFunctionsTable *CDaemonAppTraits::GetSocketGUIFunctionsTable()
278 return m_table;
281 bool CAmuledGSocketFuncTable::OnInit()
283 return true;
286 void CAmuledGSocketFuncTable::OnExit()
290 bool CAmuledGSocketFuncTable::CanUseEventLoop()
293 * FIXME: (lfroen) Not sure whether it's right.
294 * I will review it later.
296 return false;
299 bool CAmuledGSocketFuncTable::Init_Socket(GSocket *)
301 return true;
304 void CAmuledGSocketFuncTable::Destroy_Socket(GSocket *)
308 void CAmuledGSocketFuncTable::Install_Callback(GSocket *sock, GSocketEvent e)
310 AddSocket(sock, e);
313 void CAmuledGSocketFuncTable::Uninstall_Callback(GSocket *sock, GSocketEvent e)
315 RemoveSocket(sock, e);
318 void CAmuledGSocketFuncTable::Enable_Events(GSocket *socket)
320 Install_Callback(socket, GSOCK_INPUT);
321 Install_Callback(socket, GSOCK_OUTPUT);
324 void CAmuledGSocketFuncTable::Disable_Events(GSocket *socket)
326 Uninstall_Callback(socket, GSOCK_INPUT);
327 Uninstall_Callback(socket, GSOCK_OUTPUT);
331 CDaemonAppTraits::CDaemonAppTraits(CAmuledGSocketFuncTable *table)
333 wxConsoleAppTraits(),
334 m_oldSignalChildAction(),
335 m_newSignalChildAction(),
336 m_table(table),
337 m_lock(wxMUTEX_RECURSIVE),
338 m_sched_delete()
340 m_lock.Unlock();
344 void CDaemonAppTraits::ScheduleForDestroy(wxObject *object)
346 wxMutexLocker lock(m_lock);
348 //delete object;
349 m_sched_delete.push_back(object);
352 void CDaemonAppTraits::RemoveFromPendingDelete(wxObject *object)
354 wxMutexLocker lock(m_lock);
356 for(std::list<wxObject *>::iterator i = m_sched_delete.begin();
357 i != m_sched_delete.end(); i++) {
358 if ( *i == object ) {
359 m_sched_delete.erase(i);
360 return;
365 void CDaemonAppTraits::DeletePending()
367 wxMutexLocker lock(m_lock);
369 while ( !m_sched_delete.empty() ) {
370 std::list<wxObject *>::iterator i = m_sched_delete.begin();
371 wxObject *object = *i;
372 delete object;
374 //m_sched_delete.erase(m_sched_delete.begin(), m_sched_delete.end());
377 wxAppTraits *CamuleDaemonApp::CreateTraits()
379 return new CDaemonAppTraits(m_table);
382 #else // AMULED28_SOCKETS
384 #ifdef AMULED_APPTRAITS
386 CDaemonAppTraits::CDaemonAppTraits()
388 wxConsoleAppTraits(),
389 m_oldSignalChildAction(),
390 m_newSignalChildAction()
394 wxAppTraits *CamuleDaemonApp::CreateTraits()
396 return new CDaemonAppTraits();
399 #endif // AMULED_APPTRAITS
401 #endif // !AMULED28_SOCKETS
403 #if defined(__WXMAC__) && !wxCHECK_VERSION(2, 9, 0)
404 #include <wx/stdpaths.h> // Do_not_auto_remove (guess)
405 static wxStandardPathsCF gs_stdPaths;
406 wxStandardPathsBase& CDaemonAppTraits::GetStandardPaths()
408 return gs_stdPaths;
410 #endif
413 #ifdef AMULED28_EVENTLOOP
415 CamuleDaemonApp::CamuleDaemonApp()
417 m_Exit(false)
418 #ifdef AMULED28_SOCKETS
419 ,m_table(new CAmuledGSocketFuncTable())
420 #endif
422 // work around problem from http://trac.wxwidgets.org/ticket/2145
423 wxPendingEventsLocker = new wxCriticalSection;
426 #endif // AMULED28_EVENTLOOP
429 #ifdef AMULED_APPTRAITS
431 static EndProcessDataMap endProcDataMap;
433 int CDaemonAppTraits::WaitForChild(wxExecuteData &execData)
435 int status = 0;
436 pid_t result = 0;
437 // Build the log message
438 wxString msg;
439 msg << wxT("WaitForChild() has been called for child process with pid `") <<
440 execData.pid <<
441 wxT("'. ");
443 if (execData.flags & wxEXEC_SYNC) {
444 result = AmuleWaitPid(execData.pid, &status, 0, &msg);
445 if (result == -1 || (!WIFEXITED(status) && !WIFSIGNALED(status))) {
446 msg << wxT(" Waiting for subprocess termination failed.");
447 AddDebugLogLineN(logGeneral, msg);
449 } else {
450 /** wxEXEC_ASYNC */
451 // Give the process a chance to start or forked child to exit
452 // 1 second is enough time to fail on "path not found"
453 wxSleep(1);
454 result = AmuleWaitPid(execData.pid, &status, WNOHANG, &msg);
455 if (result == 0) {
456 // Add a WxEndProcessData entry to the map, so that we can
457 // support process termination
458 wxEndProcessData *endProcData = new wxEndProcessData();
459 endProcData->pid = execData.pid;
460 endProcData->process = execData.process;
461 endProcData->tag = 0;
462 endProcDataMap[execData.pid] = endProcData;
464 status = execData.pid;
465 } else {
466 // if result != 0, then either waitpid() failed (result == -1)
467 // and there is nothing we can do, or the child has changed
468 // status, which means it is probably dead.
469 status = 0;
473 // Log our passage here
474 AddDebugLogLineN(logGeneral, msg);
476 return status;
480 void OnSignalChildHandler(int /*signal*/, siginfo_t *siginfo, void * /*ucontext*/)
482 // Build the log message
483 wxString msg;
484 msg << wxT("OnSignalChildHandler() has been called for child process with pid `") <<
485 siginfo->si_pid <<
486 wxT("'. ");
487 // Make sure we leave no zombies by calling waitpid()
488 int status = 0;
489 pid_t result = AmuleWaitPid(siginfo->si_pid, &status, WNOHANG, &msg);
490 if (result != 1 && result != 0 && (WIFEXITED(status) || WIFSIGNALED(status))) {
491 // Fetch the wxEndProcessData structure corresponding to this pid
492 EndProcessDataMap::iterator it = endProcDataMap.find(siginfo->si_pid);
493 if (it != endProcDataMap.end()) {
494 wxEndProcessData *endProcData = it->second;
495 // Remove this entry from the process map
496 endProcDataMap.erase(siginfo->si_pid);
497 // Save the exit code for the wxProcess object to read later
498 endProcData->exitcode = result != -1 && WIFEXITED(status) ?
499 WEXITSTATUS(status) : -1;
500 // Make things work as in wxGUI
501 wxHandleProcessTermination(endProcData);
503 // wxHandleProcessTermination() will "delete endProcData;"
504 // So we do not delete it again, ok? Do not uncomment this line.
505 //delete endProcData;
506 } else {
507 msg << wxT(" Error: the child process pid is not on the pid map.");
511 // Log our passage here
512 AddDebugLogLineN(logGeneral, msg);
516 pid_t AmuleWaitPid(pid_t pid, int *status, int options, wxString *msg)
518 *status = 0;
519 pid_t result = waitpid(pid, status, options);
520 if (result == -1) {
521 *msg << CFormat(wxT("Error: waitpid() call failed: %m."));
522 } else if (result == 0) {
523 if (options & WNOHANG) {
524 *msg << wxT("The child is alive.");
525 } else {
526 *msg << wxT("Error: waitpid() call returned 0 but "
527 "WNOHANG was not specified in options.");
529 } else {
530 if (WIFEXITED(*status)) {
531 *msg << wxT("Child has terminated with status code `") <<
532 WEXITSTATUS(*status) <<
533 wxT("'.");
534 } else if (WIFSIGNALED(*status)) {
535 *msg << wxT("Child was killed by signal `") <<
536 WTERMSIG(*status) <<
537 wxT("'.");
538 if (WCOREDUMP(*status)) {
539 *msg << wxT(" A core file has been dumped.");
541 } else if (WIFSTOPPED(*status)) {
542 *msg << wxT("Child has been stopped by signal `") <<
543 WSTOPSIG(*status) <<
544 wxT("'.");
545 #ifdef WIFCONTINUED /* Only found in recent kernels. */
546 } else if (WIFCONTINUED(*status)) {
547 *msg << wxT("Child has received `SIGCONT' and has continued execution.");
548 #endif
549 } else {
550 *msg << wxT("The program was not able to determine why the child has signaled.");
554 return result;
557 #endif // AMULED_APPTRAITS
560 #ifdef __WINDOWS__
562 // CTRL-C-Handler
563 // see http://msdn.microsoft.com/en-us/library/windows/desktop/ms685049%28v=vs.85%29.aspx
565 static BOOL CtrlHandler(DWORD fdwCtrlType)
567 switch (fdwCtrlType) {
568 case CTRL_C_EVENT:
569 case CTRL_CLOSE_EVENT:
570 case CTRL_BREAK_EVENT:
571 // handle these
572 AddLogLineNS(wxT("Received break event, exit main loop"));
573 theApp->ExitMainLoop();
574 return TRUE;
575 break;
576 case CTRL_LOGOFF_EVENT:
577 case CTRL_SHUTDOWN_EVENT:
578 default:
579 // don't handle these
580 return FALSE;
581 break;
585 #endif // __WINDOWS__
588 int CamuleDaemonApp::OnRun()
590 if (!thePrefs::AcceptExternalConnections()) {
591 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"));
592 return 0;
593 } else if (thePrefs::ECPassword().IsEmpty()) {
594 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"));
595 return 0;
598 #ifdef __WINDOWS__
599 SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE);
600 #endif // __WINDOWS__
602 #ifdef AMULED_APPTRAITS
603 // Process the return code of dead children so that we do not create
604 // zombies. wxBase does not implement wxProcess callbacks, so no one
605 // actualy calls wxHandleProcessTermination() in console applications.
606 // We do our best here.
607 DEBUG_ONLY( int ret = 0; )
608 DEBUG_ONLY( ret = ) sigaction(SIGCHLD, NULL, &m_oldSignalChildAction);
609 m_newSignalChildAction = m_oldSignalChildAction;
610 m_newSignalChildAction.sa_sigaction = OnSignalChildHandler;
611 m_newSignalChildAction.sa_flags |= SA_SIGINFO;
612 m_newSignalChildAction.sa_flags &= ~SA_RESETHAND;
613 DEBUG_ONLY( ret = ) sigaction(SIGCHLD, &m_newSignalChildAction, NULL);
614 #ifdef __DEBUG__
615 if (ret == -1) {
616 AddDebugLogLineC(logStandard, CFormat(wxT("CamuleDaemonApp::OnRun(): Installation of SIGCHLD callback with sigaction() failed: %m.")));
617 } else {
618 AddDebugLogLineN(logGeneral, wxT("CamuleDaemonApp::OnRun(): Installation of SIGCHLD callback with sigaction() succeeded."));
620 #endif
621 #endif // AMULED_APPTRAITS
623 #ifdef AMULED28_EVENTLOOP
625 while ( !m_Exit ) {
626 #ifdef AMULED28_SOCKETS
627 m_table->RunSelect();
628 ProcessPendingEvents();
629 ((CDaemonAppTraits *)GetTraits())->DeletePending();
630 #else
631 wxMilliSleep(10);
632 ProcessPendingEvents();
633 #endif
636 // ShutDown is beeing called twice. Once here and again in OnExit().
637 ShutDown();
639 return 0;
641 #else
642 return wxApp::OnRun();
643 #endif
646 bool CamuleDaemonApp::OnInit()
648 if ( !CamuleApp::OnInit() ) {
649 return false;
651 AddLogLineNS(_("amuled: OnInit - starting timer"));
652 core_timer = new CTimer(this,ID_CORE_TIMER_EVENT);
653 core_timer->Start(CORE_TIMER_PERIOD);
654 glob_prefs->GetCategory(0)->title = GetCatTitle(thePrefs::GetAllcatFilter());
655 glob_prefs->GetCategory(0)->path = thePrefs::GetIncomingDir();
657 return true;
660 int CamuleDaemonApp::InitGui(bool ,wxString &)
662 #ifndef __WINDOWS__
663 if ( !enable_daemon_fork ) {
664 return 0;
666 AddLogLineNS(_("amuled: forking to background - see you"));
667 theLogger.SetEnabledStdoutLog(false);
669 // fork to background and detach from controlling tty
670 // while redirecting stdout to /dev/null
672 for(int i_fd = 0;i_fd < 3; i_fd++) {
673 close(i_fd);
675 int fd = open("/dev/null",O_RDWR);
676 if (dup(fd)){} // prevent GCC warning
677 if (dup(fd)){}
678 pid_t pid = fork();
680 wxASSERT(pid != -1);
682 if ( pid ) {
683 exit(0);
684 } else {
685 pid = setsid();
687 // Create a Pid file with the Pid of the Child, so any daemon-manager
688 // can easily manage the process
690 if (!m_PidFile.IsEmpty()) {
691 wxString temp = CFormat(wxT("%d\n")) % pid;
692 wxFFile ff(m_PidFile, wxT("w"));
693 if (!ff.Error()) {
694 ff.Write(temp);
695 ff.Close();
696 } else {
697 AddLogLineNS(_("Cannot Create Pid File"));
702 #endif
703 return 0;
706 bool CamuleDaemonApp::Initialize(int& argc_, wxChar **argv_)
708 if ( !wxAppConsole::Initialize(argc_, argv_) ) {
709 return false;
712 #ifdef __UNIX__
713 wxString encName;
714 #if wxUSE_INTL
715 // if a non default locale is set,
716 // assume that the user wants his
717 // filenames in this locale too
718 encName = wxLocale::GetSystemEncodingName().Upper();
720 // But don't consider ASCII in this case.
721 if ( !encName.empty() ) {
722 if ( encName == wxT("US-ASCII") ) {
723 // This means US-ASCII when returned
724 // from GetEncodingFromName().
725 encName.clear();
728 #endif // wxUSE_INTL
730 // in this case, UTF-8 is used by default.
731 if ( encName.empty() ) {
732 encName = wxT("UTF-8");
735 static wxConvBrokenFileNames fileconv(encName);
736 wxConvFileName = &fileconv;
737 #endif // __UNIX__
739 return true;
742 int CamuleDaemonApp::OnExit()
744 #ifdef AMULED28_SOCKETS
746 * Stop all socket threads before entering
747 * shutdown sequence.
749 delete listensocket;
750 listensocket = 0;
751 if (clientudp) {
752 delete clientudp;
753 clientudp = NULL;
755 #endif
757 ShutDown();
759 #ifdef AMULED_APPTRAITS
760 DEBUG_ONLY( int ret = ) sigaction(SIGCHLD, &m_oldSignalChildAction, NULL);
761 #ifdef __DEBUG__
762 if (ret == -1) {
763 AddDebugLogLineC(logStandard, CFormat(wxT("CamuleDaemonApp::OnRun(): second sigaction() failed: %m.")));
764 } else {
765 AddDebugLogLineN(logGeneral, wxT("CamuleDaemonApp::OnRun(): Uninstallation of SIGCHLD callback with sigaction() succeeded."));
767 #endif
768 #endif // AMULED_APPTRAITS
770 delete core_timer;
772 return CamuleApp::OnExit();
776 int CamuleDaemonApp::ShowAlert(wxString msg, wxString title, int flags)
778 if ( flags | wxICON_ERROR ) {
779 title = CFormat(_("ERROR: %s")) % title;
781 AddLogLineCS(title + wxT(" ") + msg);
783 return 0; // That's neither yes nor no, ok, cancel
786 // File_checked_for_headers