Upstream tarball 9882
[amule.git] / src / amuled.cpp
blobbc21e4239219aee8273d42d31f711d066e157d7f
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 #define _XOPEN_SOURCE 600
51 #include <string.h> // Do_not_auto_remove
53 #include <wx/utils.h>
55 #include "Preferences.h" // Needed for CPreferences
56 #include "PartFile.h" // Needed for CPartFile
57 #include "Logger.h"
58 #include <common/Format.h>
59 #include "InternalEvents.h" // Needed for wxEVT_*
60 #include "ThreadTasks.h"
61 #include "GuiEvents.h" // Needed for EVT_MULE_NOTIFY
62 #include "Timer.h" // Needed for EVT_MULE_TIMER
64 #include "ClientUDPSocket.h" // Do_not_auto_remove (forward declaration not enough)
65 #include "ListenSocket.h" // Do_not_auto_remove (forward declaration not enough)
68 #include <errno.h>
69 #ifdef HAVE_SYS_RESOURCE_H
70 #include <sys/resource.h> // Do_not_auto_remove
71 #endif
73 #ifndef __WXMSW__
74 #ifdef HAVE_SYS_WAIT_H
75 #include <sys/wait.h> // Do_not_auto_remove
76 #endif
78 #include <wx/unix/execute.h>
79 #endif
81 #ifndef HAVE_STRERROR_R
83 // Replacement strerror_r() function for systems that don't have any.
84 // Note that this replacement function is NOT thread-safe!
85 static int rpl_strerror_r(int errnum, char *buf, size_t buflen)
87 char *tmp = strerror(errnum);
88 if (tmp == NULL) {
89 errno = EINVAL;
90 return -1;
91 } else {
92 strncpy(buf, tmp, buflen - 1);
93 buf[buflen - 1] = '\0';
94 if (strlen(tmp) >= buflen) {
95 errno = ERANGE;
96 return -1;
99 return 0;
102 # define strerror_r(errnum, buf, buflen) rpl_strerror_r(errnum, buf, buflen)
103 #else
104 # ifdef STRERROR_R_CHAR_P
106 // Replacement strerror_r() function for systems that return a char*.
107 static int rpl_strerror_r(int errnum, char *buf, size_t buflen)
109 char *tmp = strerror_r(errnum, buf, buflen);
110 if (tmp == NULL) {
111 errno = EINVAL;
112 return -1;
113 } else if (tmp != buf) {
114 strncpy(buf, tmp, buflen - 1);
115 buf[buflen - 1] = '\0';
116 if (strlen(tmp) >= buflen) {
117 errno = ERANGE;
118 return -1;
121 return 0;
124 # define strerror_r(errnum, buf, buflen) rpl_strerror_r(errnum, buf, buflen)
125 # endif
126 #endif
128 BEGIN_EVENT_TABLE(CamuleDaemonApp, wxAppConsole)
130 // Socket handlers
133 // Listen Socket
134 EVT_SOCKET(ID_LISTENSOCKET_EVENT, CamuleDaemonApp::ListenSocketHandler)
136 // UDP Socket (servers)
137 EVT_SOCKET(ID_SERVERUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
138 // UDP Socket (clients)
139 EVT_SOCKET(ID_CLIENTUDPSOCKET_EVENT, CamuleDaemonApp::UDPSocketHandler)
141 // Socket timer (TCP)
142 EVT_MULE_TIMER(ID_SERVER_RETRY_TIMER_EVENT, CamuleDaemonApp::OnTCPTimer)
144 // Core timer
145 EVT_MULE_TIMER(ID_CORE_TIMER_EVENT, CamuleDaemonApp::OnCoreTimer)
147 EVT_MULE_NOTIFY(CamuleDaemonApp::OnNotifyEvent)
149 // Async dns handling
150 EVT_MULE_INTERNAL(wxEVT_CORE_UDP_DNS_DONE, -1, CamuleDaemonApp::OnUDPDnsDone)
152 EVT_MULE_INTERNAL(wxEVT_CORE_SOURCE_DNS_DONE, -1, CamuleDaemonApp::OnSourceDnsDone)
154 EVT_MULE_INTERNAL(wxEVT_CORE_SERVER_DNS_DONE, -1, CamuleDaemonApp::OnServerDnsDone)
156 // Hash ended notifier
157 EVT_MULE_HASHING(CamuleDaemonApp::OnFinishedHashing)
158 EVT_MULE_AICH_HASHING(CamuleDaemonApp::OnFinishedAICHHashing)
160 // File completion ended notifier
161 EVT_MULE_FILE_COMPLETED(CamuleDaemonApp::OnFinishedCompletion)
163 // HTTPDownload finished
164 EVT_MULE_INTERNAL(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD, -1, CamuleDaemonApp::OnFinishedHTTPDownload)
166 // Disk space preallocation finished
167 EVT_MULE_ALLOC_FINISHED(CamuleDaemonApp::OnFinishedAllocation)
168 END_EVENT_TABLE()
170 IMPLEMENT_APP(CamuleDaemonApp)
172 #ifdef AMULED28
174 * Socket handling in wxBase
177 class CSocketSet {
178 int m_count;
179 int m_fds[FD_SETSIZE], m_fd_idx[FD_SETSIZE];
180 GSocket *m_gsocks[FD_SETSIZE];
182 fd_set m_set;
183 public:
184 CSocketSet();
185 void AddSocket(GSocket *);
186 void RemoveSocket(GSocket *);
187 void FillSet(int &max_fd);
189 void Detected(void (GSocket::*func)());
191 fd_set *Set() { return &m_set; }
194 CSocketSet::CSocketSet()
196 m_count = 0;
197 for(int i = 0; i < FD_SETSIZE; i++) {
198 m_fds[i] = 0;
199 m_fd_idx[i] = 0xffff;
200 m_gsocks[i] = 0;
204 void CSocketSet::AddSocket(GSocket *socket)
206 wxASSERT(socket);
208 int fd = socket->m_fd;
210 if ( fd == -1 ) {
211 return;
214 wxASSERT( (fd > 2) && (fd < FD_SETSIZE) );
216 if ( m_gsocks[fd] ) {
217 return;
219 m_fds[m_count] = fd;
220 m_fd_idx[fd] = m_count;
221 m_gsocks[fd] = socket;
222 m_count++;
225 void CSocketSet::RemoveSocket(GSocket *socket)
227 wxASSERT(socket);
229 int fd = socket->m_fd;
231 if ( fd == -1 ) {
232 return;
235 wxASSERT( (fd > 2) && (fd < FD_SETSIZE) );
237 int i = m_fd_idx[fd];
238 if ( i == 0xffff ) {
239 return;
241 wxASSERT(m_fds[i] == fd);
242 m_fds[i] = m_fds[m_count-1];
243 m_gsocks[fd] = 0;
244 m_fds[m_count-1] = 0;
245 m_fd_idx[fd] = 0xffff;
246 m_fd_idx[m_fds[i]] = i;
247 m_count--;
250 void CSocketSet::FillSet(int &max_fd)
252 FD_ZERO(&m_set);
254 for(int i = 0; i < m_count; i++) {
255 FD_SET(m_fds[i], &m_set);
256 if ( m_fds[i] > max_fd ) {
257 max_fd = m_fds[i];
262 void CSocketSet::Detected(void (GSocket::*func)())
264 for (int i = 0; i < m_count; i++) {
265 int fd = m_fds[i];
266 if ( FD_ISSET(fd, &m_set) ) {
267 GSocket *socket = m_gsocks[fd];
268 (*socket.*func)();
273 CAmuledGSocketFuncTable::CAmuledGSocketFuncTable() : m_lock(wxMUTEX_RECURSIVE)
275 m_in_set = new CSocketSet;
276 m_out_set = new CSocketSet;
278 m_lock.Unlock();
281 void CAmuledGSocketFuncTable::AddSocket(GSocket *socket, GSocketEvent event)
283 wxMutexLocker lock(m_lock);
285 if ( event == GSOCK_INPUT ) {
286 m_in_set->AddSocket(socket);
287 } else {
288 m_out_set->AddSocket(socket);
292 void CAmuledGSocketFuncTable::RemoveSocket(GSocket *socket, GSocketEvent event)
294 wxMutexLocker lock(m_lock);
296 if ( event == GSOCK_INPUT ) {
297 m_in_set->RemoveSocket(socket);
298 } else {
299 m_out_set->RemoveSocket(socket);
303 void CAmuledGSocketFuncTable::RunSelect()
305 wxMutexLocker lock(m_lock);
307 int max_fd = -1;
308 m_in_set->FillSet(max_fd);
309 m_out_set->FillSet(max_fd);
311 struct timeval tv;
312 tv.tv_sec = 0;
313 tv.tv_usec = 10000; // 10ms
315 int result = select(max_fd + 1, m_in_set->Set(), m_out_set->Set(), 0, &tv);
316 if ( result > 0 ) {
317 m_in_set->Detected(&GSocket::Detected_Read);
318 m_out_set->Detected(&GSocket::Detected_Write);
322 GSocketGUIFunctionsTable *CDaemonAppTraits::GetSocketGUIFunctionsTable()
324 return m_table;
327 bool CAmuledGSocketFuncTable::OnInit()
329 return true;
332 void CAmuledGSocketFuncTable::OnExit()
336 bool CAmuledGSocketFuncTable::CanUseEventLoop()
339 * FIXME: (lfroen) Not sure whether it's right.
340 * I will review it later.
342 return false;
345 bool CAmuledGSocketFuncTable::Init_Socket(GSocket *)
347 return true;
350 void CAmuledGSocketFuncTable::Destroy_Socket(GSocket *)
354 void CAmuledGSocketFuncTable::Install_Callback(GSocket *sock, GSocketEvent e)
356 AddSocket(sock, e);
359 void CAmuledGSocketFuncTable::Uninstall_Callback(GSocket *sock, GSocketEvent e)
361 RemoveSocket(sock, e);
364 void CAmuledGSocketFuncTable::Enable_Events(GSocket *socket)
366 Install_Callback(socket, GSOCK_INPUT);
367 Install_Callback(socket, GSOCK_OUTPUT);
370 void CAmuledGSocketFuncTable::Disable_Events(GSocket *socket)
372 Uninstall_Callback(socket, GSOCK_INPUT);
373 Uninstall_Callback(socket, GSOCK_OUTPUT);
376 #endif // AMULED28
378 #ifndef __WXMSW__
380 #ifdef AMULED28
382 CDaemonAppTraits::CDaemonAppTraits(CAmuledGSocketFuncTable *table)
384 wxConsoleAppTraits(),
385 m_oldSignalChildAction(),
386 m_newSignalChildAction(),
387 m_table(table),
388 m_lock(wxMUTEX_RECURSIVE),
389 m_sched_delete()
391 m_lock.Unlock();
395 void CDaemonAppTraits::ScheduleForDestroy(wxObject *object)
397 wxMutexLocker lock(m_lock);
399 //delete object;
400 m_sched_delete.push_back(object);
403 void CDaemonAppTraits::RemoveFromPendingDelete(wxObject *object)
405 wxMutexLocker lock(m_lock);
407 for(std::list<wxObject *>::iterator i = m_sched_delete.begin();
408 i != m_sched_delete.end(); i++) {
409 if ( *i == object ) {
410 m_sched_delete.erase(i);
411 return;
416 void CDaemonAppTraits::DeletePending()
418 wxMutexLocker lock(m_lock);
420 while ( !m_sched_delete.empty() ) {
421 std::list<wxObject *>::iterator i = m_sched_delete.begin();
422 wxObject *object = *i;
423 delete object;
425 //m_sched_delete.erase(m_sched_delete.begin(), m_sched_delete.end());
428 wxAppTraits *CamuleDaemonApp::CreateTraits()
430 return new CDaemonAppTraits(m_table);
433 #else // AMULED28
435 CDaemonAppTraits::CDaemonAppTraits()
437 wxConsoleAppTraits(),
438 m_oldSignalChildAction(),
439 m_newSignalChildAction()
443 wxAppTraits *CamuleDaemonApp::CreateTraits()
445 return new CDaemonAppTraits();
448 #endif // !AMULED28
450 #endif // __WXMSW__
452 #if defined(__WXMAC__) && !wxCHECK_VERSION(2, 9, 0)
453 #include <wx/stdpaths.h> // Do_not_auto_remove (guess)
454 static wxStandardPathsCF gs_stdPaths;
455 wxStandardPathsBase& CDaemonAppTraits::GetStandardPaths()
457 return gs_stdPaths;
459 #endif
462 #ifdef AMULED28
464 CamuleDaemonApp::CamuleDaemonApp()
466 m_Exit(false),
467 m_table(new CAmuledGSocketFuncTable())
469 wxPendingEventsLocker = new wxCriticalSection;
472 #endif // !AMULED28
475 #ifndef __WXMSW__
478 static EndProcessDataMap endProcDataMap;
481 int CDaemonAppTraits::WaitForChild(wxExecuteData &execData)
483 int status = 0;
484 pid_t result = 0;
485 // Build the log message
486 wxString msg;
487 msg << wxT("WaitForChild() has been called for child process with pid `") <<
488 execData.pid <<
489 wxT("'. ");
491 if (execData.flags & wxEXEC_SYNC) {
492 result = AmuleWaitPid(execData.pid, &status, 0, &msg);
493 if (result == -1 || (!WIFEXITED(status) && !WIFSIGNALED(status))) {
494 msg << wxT(" Waiting for subprocess termination failed.");
495 AddDebugLogLineM(false, logGeneral, msg);
497 } else {
498 /** wxEXEC_ASYNC */
499 // Give the process a chance to start or forked child to exit
500 // 1 second is enough time to fail on "path not found"
501 wxSleep(1);
502 result = AmuleWaitPid(execData.pid, &status, WNOHANG, &msg);
503 if (result == 0) {
504 // Add a WxEndProcessData entry to the map, so that we can
505 // support process termination
506 wxEndProcessData *endProcData = new wxEndProcessData();
507 endProcData->pid = execData.pid;
508 endProcData->process = execData.process;
509 endProcData->tag = 0;
510 endProcDataMap[execData.pid] = endProcData;
512 status = execData.pid;
513 } else {
514 // if result != 0, then either waitpid() failed (result == -1)
515 // and there is nothing we can do, or the child has changed
516 // status, which means it is probably dead.
517 status = 0;
521 // Log our passage here
522 AddDebugLogLineM(false, logGeneral, msg);
524 return status;
528 void OnSignalChildHandler(int /*signal*/, siginfo_t *siginfo, void * /*ucontext*/)
530 // Build the log message
531 wxString msg;
532 msg << wxT("OnSignalChildHandler() has been called for child process with pid `") <<
533 siginfo->si_pid <<
534 wxT("'. ");
535 // Make sure we leave no zombies by calling waitpid()
536 int status = 0;
537 pid_t result = AmuleWaitPid(siginfo->si_pid, &status, WNOHANG, &msg);
538 if (result != 1 && result != 0 && (WIFEXITED(status) || WIFSIGNALED(status))) {
539 // Fetch the wxEndProcessData structure corresponding to this pid
540 EndProcessDataMap::iterator it = endProcDataMap.find(siginfo->si_pid);
541 if (it != endProcDataMap.end()) {
542 wxEndProcessData *endProcData = it->second;
543 // Remove this entry from the process map
544 endProcDataMap.erase(siginfo->si_pid);
545 // Save the exit code for the wxProcess object to read later
546 endProcData->exitcode = result != -1 && WIFEXITED(status) ?
547 WEXITSTATUS(status) : -1;
548 // Make things work as in wxGUI
549 wxHandleProcessTermination(endProcData);
551 // wxHandleProcessTermination() will "delete endProcData;"
552 // So we do not delete it again, ok? Do not uncomment this line.
553 //delete endProcData;
554 } else {
555 msg << wxT(" Error: the child process pid is not on the pid map.");
559 // Log our passage here
560 AddDebugLogLineM(false, logGeneral, msg);
564 pid_t AmuleWaitPid(pid_t pid, int *status, int options, wxString *msg)
566 // strerror_r() buffer
567 const int ERROR_BUFFER_LEN = 256;
568 char errorBuffer[ERROR_BUFFER_LEN];
570 *status = 0;
571 pid_t result = waitpid(pid, status, options);
572 if (result == -1) {
573 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
574 *msg << wxT("Error: waitpid() call failed: ") <<
575 char2unicode(errorBuffer) <<
576 wxT(".");
577 } else if (result == 0) {
578 if (options & WNOHANG) {
579 *msg << wxT("The child is alive.");
580 } else {
581 *msg << wxT("Error: waitpid() call returned 0 but "
582 "WNOHANG was not specified in options.");
584 } else {
585 if (WIFEXITED(*status)) {
586 *msg << wxT("Child has terminated with status code `") <<
587 WEXITSTATUS(*status) <<
588 wxT("'.");
589 } else if (WIFSIGNALED(*status)) {
590 *msg << wxT("Child was killed by signal `") <<
591 WTERMSIG(*status) <<
592 wxT("'.");
593 if (WCOREDUMP(*status)) {
594 *msg << wxT(" A core file has been dumped.");
596 } else if (WIFSTOPPED(*status)) {
597 *msg << wxT("Child has been stopped by signal `") <<
598 WSTOPSIG(*status) <<
599 wxT("'.");
600 #ifdef WIFCONTINUED /* Only found in recent kernels. */
601 } else if (WIFCONTINUED(*status)) {
602 *msg << wxT("Child has received `SIGCONT' and has continued execution.");
603 #endif
604 } else {
605 *msg << wxT("The program was not able to determine why the child has signaled.");
609 return result;
613 #endif // __WXMSW__
616 int CamuleDaemonApp::OnRun()
618 if (!thePrefs::AcceptExternalConnections()) {
619 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"));
620 return 0;
621 } else if (thePrefs::ECPassword().IsEmpty()) {
622 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"));
623 return 0;
626 #ifndef __WXMSW__
627 // strerror_r() buffer
628 const int ERROR_BUFFER_LEN = 256;
629 char errorBuffer[ERROR_BUFFER_LEN];
630 wxString msg;
632 // Process the return code of dead children so that we do not create
633 // zombies. wxBase does not implement wxProcess callbacks, so no one
634 // actualy calls wxHandleProcessTermination() in console applications.
635 // We do our best here.
636 int ret = 0;
637 ret = sigaction(SIGCHLD, NULL, &m_oldSignalChildAction);
638 m_newSignalChildAction = m_oldSignalChildAction;
639 m_newSignalChildAction.sa_sigaction = OnSignalChildHandler;
640 m_newSignalChildAction.sa_flags |= SA_SIGINFO;
641 m_newSignalChildAction.sa_flags &= ~SA_RESETHAND;
642 ret = sigaction(SIGCHLD, &m_newSignalChildAction, NULL);
643 if (ret == -1) {
644 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
645 msg << wxT("CamuleDaemonApp::OnRun(): "
646 "Installation of SIGCHLD callback with sigaction() failed: ") <<
647 char2unicode(errorBuffer) <<
648 wxT(".");
649 AddLogLineM(true, msg);
650 } else {
651 msg << wxT("CamuleDaemonApp::OnRun(): Installation of SIGCHLD "
652 "callback with sigaction() succeeded.");
653 AddDebugLogLineM(false, logGeneral, msg);
655 #endif // __WXMSW__
657 #ifdef AMULED28
659 while ( !m_Exit ) {
660 m_table->RunSelect();
661 ProcessPendingEvents();
662 ((CDaemonAppTraits *)GetTraits())->DeletePending();
665 // ShutDown is beeing called twice. Once here and again in OnExit().
666 ShutDown();
668 return 0;
670 #else
672 #ifdef AMULED_DUMMY
673 return 0;
674 #else
675 return wxApp::OnRun();
676 #endif
678 #endif
681 bool CamuleDaemonApp::OnInit()
683 AddLogLineNS(_("amuled: OnInit - starting timer"));
684 if ( !CamuleApp::OnInit() ) {
685 return false;
687 core_timer = new CTimer(this,ID_CORE_TIMER_EVENT);
688 core_timer->Start(CORE_TIMER_PERIOD);
689 glob_prefs->GetCategory(0)->title = GetCatTitle(thePrefs::GetAllcatType());
690 glob_prefs->GetCategory(0)->path = thePrefs::GetIncomingDir();
692 return true;
695 int CamuleDaemonApp::InitGui(bool ,wxString &)
697 #ifndef __WXMSW__
698 if ( !enable_daemon_fork ) {
699 return 0;
701 AddLogLineNS(_("amuled: forking to background - see you"));
702 theLogger.SetEnabledStdoutLog(false);
704 // fork to background and detach from controlling tty
705 // while redirecting stdout to /dev/null
707 for(int i_fd = 0;i_fd < 3; i_fd++) {
708 close(i_fd);
710 int fd = open("/dev/null",O_RDWR);
711 if (dup(fd)){} // prevent GCC warning
712 if (dup(fd)){}
713 pid_t pid = fork();
715 wxASSERT(pid != -1);
717 if ( pid ) {
718 exit(0);
719 } else {
720 pid = setsid();
722 // Create a Pid file with the Pid of the Child, so any daemon-manager
723 // can easily manage the process
725 if (!PidFile.IsEmpty()) {
726 wxString temp = wxString::Format(wxT("%d\n"), pid);
727 wxFFile ff(PidFile, wxT("w"));
728 if (!ff.Error()) {
729 ff.Write(temp);
730 ff.Close();
731 } else {
732 AddLogLineNS(_("Cannot Create Pid File"));
737 #endif
738 return 0;
742 int CamuleDaemonApp::OnExit()
744 #ifdef AMULED28
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 #ifndef __WXMSW__
760 const int ERROR_BUFFER_LEN = 256;
761 char errorBuffer[ERROR_BUFFER_LEN];
762 wxString msg;
763 int ret = sigaction(SIGCHLD, &m_oldSignalChildAction, NULL);
764 if (ret == -1) {
765 strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
766 msg << wxT("CamuleDaemonApp::OnRun(): second sigaction() failed: ") <<
767 char2unicode(errorBuffer) <<
768 wxT(".");
769 AddLogLineM(true, msg);
770 } else {
771 msg << wxT("CamuleDaemonApp::OnRun(): Uninstallation of SIGCHLD "
772 "callback with sigaction() succeeded.");
773 AddDebugLogLineM(false, logGeneral, msg);
775 #endif // __WXMSW__
777 // lfroen: delete socket threads
778 if (ECServerHandler) {
779 ECServerHandler = 0;
782 delete core_timer;
784 return CamuleApp::OnExit();
788 int CamuleDaemonApp::ShowAlert(wxString msg, wxString title, int flags)
790 if ( flags | wxICON_ERROR ) {
791 title = CFormat(_("ERROR: %s")) % title;
793 AddLogLineCS(title + wxT(" ") + msg);
795 return 0; // That's neither yes nor no, ok, cancel
798 // File_checked_for_headers