/* * The olsr.org Optimized Link-State Routing daemon (olsrd) * Copyright (c) 2004, Thomas Lopatic (thomas@lopatic.de) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of olsr.org, olsrd nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Visit http://www.olsr.org for more information. * * If you find this software useful feel free to make a donation * to the project. For more information see the website or contact * the copyright holders. * * $Id: FrontendDlg.cpp,v 1.11 2007/03/27 03:01:06 tlopatic Exp $ */ #include "stdafx.h" #include "Frontend.h" #include "FrontendDlg.h" #include "TrayIcon.h" #include "Ipc.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif CFrontendDlg::CFrontendDlg(CWnd* pParent) : CDialog(CFrontendDlg::IDD, pParent) { //{{AFX_DATA_INIT(CFrontendDlg) //}}AFX_DATA_INIT Event = CreateEvent(NULL, FALSE, FALSE, "TheOlsrdShimEvent"); LogThread = NULL; NetThread = NULL; } void CFrontendDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CFrontendDlg) DDX_Control(pDX, IDC_BUTTON2, m_StopButton); DDX_Control(pDX, IDC_BUTTON1, m_StartButton); DDX_Control(pDX, IDC_TAB1, m_TabCtrl); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CFrontendDlg, CDialog) //{{AFX_MSG_MAP(CFrontendDlg) ON_BN_CLICKED(IDC_BUTTON1, OnStartButton) ON_BN_CLICKED(IDC_BUTTON2, OnStopButton) ON_BN_CLICKED(IDC_BUTTON3, OnExitButton) ON_BN_CLICKED(IDOK, OnOK) ON_BN_CLICKED(IDCANCEL, OnCancel) //}}AFX_MSG_MAP END_MESSAGE_MAP() #if 0 static void HexDump(unsigned char *Mem, int Len) { char Buff[10000]; int i, k; char *Walker = Buff; for (i = 0; i < Len; i += k) { Walker += sprintf(Walker, "%08x:", i); for (k = 0; i + k < Len && k < 16; k++) Walker += sprintf(Walker, " %02x", Mem[i + k]); while (k < 16) { Walker += sprintf(Walker, " "); k++; } Walker += sprintf(Walker, " "); for (k = 0; i + k < Len && k < 16; k++) if (Mem[i + k] < 32 || Mem[i + k] > 126) Walker += sprintf(Walker, "."); else Walker += sprintf(Walker, "%c", Mem[i + k]); Walker += sprintf(Walker, "\r\n"); } ::MessageBox(NULL, Buff, "HEXDUMP", MB_OK); } #endif // XXX - pretty inefficient void CFrontendDlg::Timeout(void) { POSITION Pos, Pos2; class NodeEntry Entry; class MprEntry MprEntry; class MidEntry MidEntry; class HnaEntry HnaEntry; Restart0: Pos = NodeList.GetHeadPosition(); while (Pos != NULL) { Entry = NodeList.GetAt(Pos); if (Entry.Timeout < Now) { NodeList.RemoveAt(Pos); goto Restart0; } Restart1: Pos2 = Entry.MprList.GetHeadPosition(); while (Pos2 != NULL) { MprEntry = Entry.MprList.GetAt(Pos2); if (MprEntry.Timeout < Now) { Entry.MprList.RemoveAt(Pos2); goto Restart1; } Entry.MprList.GetNext(Pos2); } Restart2: Pos2 = Entry.MidList.GetHeadPosition(); while (Pos2 != NULL) { MidEntry = Entry.MidList.GetAt(Pos2); if (MidEntry.Timeout < Now) { Entry.MidList.RemoveAt(Pos2); goto Restart2; } Entry.MidList.GetNext(Pos2); } Restart3: Pos2 = Entry.HnaList.GetHeadPosition(); while (Pos2 != NULL) { HnaEntry = Entry.HnaList.GetAt(Pos2); if (HnaEntry.Timeout < Now) { Entry.HnaList.RemoveAt(Pos2); goto Restart3; } Entry.HnaList.GetNext(Pos2); } NodeList.GetNext(Pos); } if( NodeList.IsEmpty() ) TrayIcon::getInstance()->setStatus( TrayIcon::ON, "No nodes found" ); else TrayIcon::getInstance()->setStatus( TrayIcon::CONNECTED, "Nodes available" ); m_TabCtrl.m_Dialog3.UpdateNodeInfo(NodeList); } unsigned int VTimeToInt(unsigned int VTime) { return ((0x10 | ((VTime & 0xf0) >> 4)) << (VTime & 0x0f)) >> 8; } void CFrontendDlg::AddMpr(unsigned int MprAddr, unsigned int NodeAddr, unsigned int VTime) { class NodeEntry NewEntry; POSITION Pos; unsigned __int64 Timeout; Timeout = Now + (unsigned __int64)VTimeToInt(VTime) * (unsigned __int64)10000000; AddNode(NodeAddr, VTime); AddNode(MprAddr, VTime); NewEntry.Addr = NodeAddr; Pos = NodeList.Find(NewEntry); if (Pos == NULL) return; class NodeEntry &OldEntry = NodeList.GetAt(Pos); OldEntry.AddMpr(MprAddr, Timeout); m_TabCtrl.m_Dialog3.UpdateNodeInfo(NodeList); } void CFrontendDlg::AddMid(unsigned int IntAddr, unsigned int NodeAddr, unsigned int VTime) { class NodeEntry NewEntry; POSITION Pos; unsigned __int64 Timeout; Timeout = Now + (unsigned __int64)VTimeToInt(VTime) * (unsigned __int64)10000000; AddNode(NodeAddr, VTime); NewEntry.Addr = NodeAddr; Pos = NodeList.Find(NewEntry); if (Pos == NULL) return; class NodeEntry &OldEntry = NodeList.GetAt(Pos); OldEntry.AddMid(IntAddr, Timeout); m_TabCtrl.m_Dialog3.UpdateNodeInfo(NodeList); } void CFrontendDlg::AddHna(unsigned int NetAddr, unsigned int NetMask, unsigned int NodeAddr, unsigned int VTime) { class NodeEntry NewEntry; POSITION Pos; unsigned __int64 Timeout; Timeout = Now + (unsigned __int64)VTimeToInt(VTime) * (unsigned __int64)10000000; AddNode(NodeAddr, VTime); NewEntry.Addr = NodeAddr; Pos = NodeList.Find(NewEntry); if (Pos == NULL) return; class NodeEntry &OldEntry = NodeList.GetAt(Pos); OldEntry.AddHna(NetAddr, NetMask, Timeout); m_TabCtrl.m_Dialog3.UpdateNodeInfo(NodeList); } void CFrontendDlg::AddNode(unsigned int NodeAddr, unsigned int VTime) { class NodeEntry NewEntry; POSITION Pos; unsigned __int64 Timeout; if (NodeAddr == LocalHost) return; Timeout = Now + (unsigned __int64)VTimeToInt(VTime) * (unsigned __int64)10000000; NewEntry.Addr = NodeAddr; Pos = NodeList.Find(NewEntry); if (Pos != NULL) { class NodeEntry &OldEntry = NodeList.GetAt(Pos); OldEntry.Timeout = Timeout; } else { NewEntry.Timeout = Timeout; NodeList.AddTail(NewEntry); } m_TabCtrl.m_Dialog3.UpdateNodeInfo(NodeList); } void CFrontendDlg::HandleOlsrTc(struct OlsrTc *Msg, int UseLq) { int Size; unsigned int *Addr; Msg->Header.SeqNo = ::ntohs(Msg->Header.SeqNo); Msg->Ansn = ::ntohs(Msg->Ansn); AddNode(Msg->Header.Orig, Msg->Header.VTime); Size = Msg->Header.Size; Size -= sizeof (struct OlsrTc); Addr = (unsigned int *)(Msg + 1); while (Size > 0) { Size -= 4; AddMpr(*Addr, Msg->Header.Orig, Msg->Header.VTime); Addr++; if (UseLq != 0) { Size -= 4; Addr++; } } } void CFrontendDlg::HandleOlsrMid(struct OlsrHeader *Msg) { int Size; unsigned int *Addr; Msg->SeqNo = ::ntohs(Msg->SeqNo); AddNode(Msg->Orig, Msg->VTime); Size = Msg->Size; Size -= sizeof (struct OlsrHeader); Addr = (unsigned int *)(Msg + 1); while (Size > 0) { Size -= 4; AddMid(*Addr, Msg->Orig, Msg->VTime); Addr++; } } void CFrontendDlg::HandleOlsrHna(struct OlsrHeader *Msg) { int Size; unsigned int *Addr; Msg->SeqNo = ::ntohs(Msg->SeqNo); AddNode(Msg->Orig, Msg->VTime); Size = Msg->Size; Size -= sizeof (struct OlsrHeader); Addr = (unsigned int *)(Msg + 1); while (Size > 0) { Size -= 8; AddHna(Addr[0], Addr[1], Msg->Orig, Msg->VTime); Addr += 2; } } void CFrontendDlg::HandleOlsrHello(struct OlsrHello *Msg, int UseLq) { int Size, LinkSize; struct OlsrHelloLink *Link; unsigned int *Addr; Msg->Header.SeqNo = ::ntohs(Msg->Header.SeqNo); AddNode(Msg->Header.Orig, Msg->Header.VTime); Size = Msg->Header.Size; Size -= sizeof (struct OlsrHello); Link = (struct OlsrHelloLink *)(Msg + 1); while (Size > 0) { Link->Size = ::ntohs(Link->Size); LinkSize = Link->Size; Size -= LinkSize; LinkSize -= sizeof (struct OlsrHelloLink); Addr = (unsigned int *)(Link + 1); while (LinkSize > 0) { LinkSize -= 4; AddNode(*Addr, Msg->Header.VTime); if ((Link->LinkCode & 0x0c) == 0x08) AddMpr(*Addr, Msg->Header.Orig, Msg->Header.VTime); Addr++; if (UseLq != 0) { LinkSize -= 4; Addr++; } } Link = (struct OlsrHelloLink *)Addr; } } void CFrontendDlg::HandleIpcRoute(struct IpcRoute *Msg) { if (Msg->Header.Size != sizeof (struct IpcRoute)) return; if (Msg->Add == 0) m_TabCtrl.m_Dialog4.DeleteRoute(Msg->Dest.v4); else m_TabCtrl.m_Dialog4.AddRoute(Msg->Dest.v4, Msg->Gate.v4, Msg->Metric, Msg->Int); } void CFrontendDlg::HandleIpcConfig(struct IpcConfig *Msg) { if (Msg->Header.Size != sizeof (struct IpcConfig)) return; Msg->HelloInt = ::ntohs(Msg->HelloInt); Msg->WiredHelloInt = ::ntohs(Msg->WiredHelloInt); Msg->TcInt = ::ntohs(Msg->TcInt); Msg->HelloHold = ::ntohs(Msg->HelloHold); Msg->TcHold = ::ntohs(Msg->TcHold); LocalHost = Msg->MainAddr.v4; } static int FullRead(SOCKET SockHand, char *Buff, int Len) { int Res; do { Res = ::recv(SockHand, Buff, Len, 0); if (Res <= 0) return -1; Len -= Res; Buff += Res; } while (Len > 0); return 0; } // XXX - we should have access to olsrd's internal data structures instead unsigned int CFrontendDlg::NetThreadFunc(void) { struct IpcHeader Header; int Res; char *Msg; for (;;) { Res = FullRead(SockHand, (char *)&Header, sizeof (struct IpcHeader)); if (Res < 0) break; Header.Size = ntohs(Header.Size); Msg = new char [Header.Size]; ::memcpy(Msg, &Header, sizeof (struct IpcHeader)); Res = FullRead(SockHand, Msg + sizeof (struct IpcHeader), Header.Size - sizeof (struct IpcHeader)); if (Res < 0) break; SYSTEMTIME SysTime; FILETIME FileTime; ::GetSystemTime(&SysTime); ::SystemTimeToFileTime(&SysTime, &FileTime); Now = *(unsigned __int64 *)&FileTime; switch (Header.Type) { case MSG_TYPE_IPC_ROUTE: HandleIpcRoute((struct IpcRoute *)Msg); break; case MSG_TYPE_IPC_CONFIG: HandleIpcConfig((struct IpcConfig *)Msg); break; case MSG_TYPE_OLSR_HELLO: HandleOlsrHello((struct OlsrHello *)Msg, 0); break; case MSG_TYPE_OLSR_TC: HandleOlsrTc((struct OlsrTc *)Msg, 0); break; case MSG_TYPE_OLSR_MID: HandleOlsrMid((struct OlsrHeader *)Msg); break; case MSG_TYPE_OLSR_HNA: HandleOlsrHna((struct OlsrHeader *)Msg); break; case MSG_TYPE_OLSR_LQ_HELLO: HandleOlsrHello((struct OlsrHello *)Msg, 1); break; case MSG_TYPE_OLSR_LQ_TC: HandleOlsrTc((struct OlsrTc *)Msg, 1); break; } delete[] Msg; // XXX - nodes are only timed out while messages keep coming in Timeout(); } AfxEndThread(0); return 0; } unsigned int CFrontendDlg::LogThreadFunc(void) { char Buff[1000]; int Len; int Left, Right; CString Line; CString Int; while (::ReadFile(OutRead, Buff, sizeof (Buff) - 1, (unsigned long *)&Len, NULL)) { if (Len == 0) break; Left = 0; for (Right = 0; Right < Len; Right++) { if (Buff[Right] != 13) Buff[Left++] = Buff[Right]; } Len = Left; Left = 0; for (Right = 0; Right < Len; Right++) { if (Buff[Right] == 10) { Buff[Right] = 0; Line += (Buff + Left); if (PipeMode == PIPE_MODE_RUN) m_TabCtrl.m_Dialog1.AddOutputLine(Line); else if (Line.GetLength() > 8 && Line[0] == 'i' && Line[1] == 'f') { Int = Line.Mid(0, 4); Int.MakeUpper(); Interfaces.Add(Int); IsWlan.Add(Line.Mid(6, 1)); Addresses.Add(Line.Mid(8)); } Line.Empty(); Left = Right + 1; } } Buff[Right] = 0; Line += (Buff + Left); } if (PipeMode == PIPE_MODE_RUN) { m_StopButton.EnableWindow(FALSE); m_StartButton.EnableWindow(TRUE); } AfxEndThread(0); return 0; } static unsigned int LogThreadStub(void *Arg) { class CFrontendDlg *This; This = (class CFrontendDlg *)Arg; return This->LogThreadFunc(); } static unsigned int NetThreadStub(void *Arg) { class CFrontendDlg *This; This = (class CFrontendDlg *)Arg; return This->NetThreadFunc(); } int CFrontendDlg::ExecutePipe(const char *CmdLine, HANDLE *InWrite, HANDLE *OutRead, HANDLE *ShimProc) { SECURITY_ATTRIBUTES SecAttr; HANDLE OutWrite, OutReadTmp; HANDLE ErrWrite; HANDLE InRead, InWriteTmp; HANDLE Proc; STARTUPINFO StartupInfo; PROCESS_INFORMATION ProcessInfo; SecAttr.nLength = sizeof (SECURITY_ATTRIBUTES); SecAttr.lpSecurityDescriptor = NULL; SecAttr.bInheritHandle = TRUE; Proc = ::GetCurrentProcess(); if (!::CreatePipe(&OutReadTmp, &OutWrite, &SecAttr, 0)) { AfxMessageBox("Cannot create stdout pipe."); return -1; } if (!::DuplicateHandle(Proc, OutReadTmp, Proc, OutRead, 0, FALSE, DUPLICATE_SAME_ACCESS)) { AfxMessageBox("Cannot duplicate temporary stdout read handle."); return -1; } if (!::CloseHandle(OutReadTmp)) { AfxMessageBox("Cannot close temporary stdout read handle."); return -1; } if (!::CreatePipe(&InRead, &InWriteTmp, &SecAttr, 0)) { AfxMessageBox("Cannot create stdin pipe."); return -1; } if (!::DuplicateHandle(Proc, InWriteTmp, Proc, InWrite, 0, FALSE, DUPLICATE_SAME_ACCESS)) { AfxMessageBox("Cannot duplicate temporary stdin write handle."); return -1; } if (!::CloseHandle(InWriteTmp)) { AfxMessageBox("Cannot close temporary stdin write handle."); return -1; } if (!::DuplicateHandle(Proc, OutWrite, Proc, &ErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS)) { AfxMessageBox("Cannot duplicate stdout write handle for stderr."); return -1; } ::memset(&StartupInfo, 0, sizeof (STARTUPINFO)); StartupInfo.cb = sizeof (STARTUPINFO); StartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; StartupInfo.hStdInput = InRead; StartupInfo.hStdOutput = OutWrite; StartupInfo.hStdError = ErrWrite; StartupInfo.wShowWindow = SW_HIDE; if (!::CreateProcess(NULL, (char *)CmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &StartupInfo, &ProcessInfo)) { AfxMessageBox("Cannot create OLSR server process."); return -1; } if (!::CloseHandle(InRead)) { AfxMessageBox("Cannot close stdin read handle."); return -1; } if (!::CloseHandle(OutWrite)) { AfxMessageBox("Cannot close stdout write handle."); return -1; } if (!::CloseHandle(ErrWrite)) { AfxMessageBox("Cannot close stderr write handle."); return -1; } *ShimProc = ProcessInfo.hProcess; return 0; } int CFrontendDlg::GetInterfaces() { char GuiPath[MAX_PATH]; CString CmdLine; CWinThread *IntThread; ::GetModuleFileName(NULL, GuiPath, MAX_PATH); CmdLine = GuiPath; CmdLine = CmdLine.Mid(0, CmdLine.ReverseFind('\\')) + "\\olsrd.exe -int"; if (ExecutePipe((const char *)CmdLine, &InWrite, &OutRead, &ShimProc) < 0) { AfxMessageBox("Cannot execute '" + CmdLine + "'."); return -1; } PipeMode = PIPE_MODE_INT; IntThread = AfxBeginThread(LogThreadStub, (void *)this); ::WaitForSingleObject((HANDLE)(*IntThread), INFINITE); return 0; } int CFrontendDlg::StartOlsrd() { WSADATA WsaData; CString CmdLine; char Path[MAX_PATH]; char TempPath[MAX_PATH]; int Try; m_TabCtrl.m_Dialog3.ClearNodeInfo(); m_TabCtrl.m_Dialog4.ClearRoutes(); if (WSAStartup(0x0202, &WsaData)) { AfxMessageBox("Cannot initialize WinSock library."); return -1; } ::GetModuleFileName(NULL, Path, MAX_PATH); CmdLine = Path; CmdLine = CmdLine.Mid(0, CmdLine.ReverseFind('\\')) + "\\Shim.exe"; ::GetTempPath(MAX_PATH - 16, Path); ::GetTempFileName(Path, "GNU", 0, TempPath); StoredTempFile = TempPath; if (m_TabCtrl.m_Dialog2.SaveConfigFile(StoredTempFile, 0) < 0) { AfxMessageBox("Cannot save temporary configuration file '" + StoredTempFile + "'."); return -1; } CmdLine += " -f " + StoredTempFile; if (ExecutePipe((const char *)CmdLine, &InWrite, &OutRead, &ShimProc) < 0) { AfxMessageBox("Cannot execute '" + CmdLine + "'."); return -1; } PipeMode = PIPE_MODE_RUN; LogThread = AfxBeginThread(LogThreadStub, (void *)this); struct sockaddr_in Addr; Addr.sin_family = AF_INET; Addr.sin_port = ::htons(1212); Addr.sin_addr.s_addr = ::inet_addr("127.0.0.1"); SockHand = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (SockHand < 0) { AfxMessageBox("Cannot create IPC socket."); ::SetEvent(Event); ::WaitForSingleObject((HANDLE)LogThread, INFINITE); LogThread = NULL; return -1; } for (Try = 0; Try < 5; Try++) { if (::connect(SockHand, (struct sockaddr *)&Addr, sizeof (struct sockaddr_in)) >= 0) break; ::Sleep(500); } if (Try == 10) { AfxMessageBox("Cannot connect to IPC port."); ::SetEvent(Event); ::WaitForSingleObject((HANDLE)LogThread, INFINITE); ::closesocket(SockHand); LogThread = NULL; return -1; } NetThread = AfxBeginThread(NetThreadStub, (void *)this); return 0; } int CFrontendDlg::StopOlsrd() { if (LogThread == NULL && NetThread == NULL) return 0; TrayIcon::getInstance()->setStatus( TrayIcon::OFF, "Off" ); ::SetEvent(Event); ::WaitForSingleObject((HANDLE)LogThread, INFINITE); ::WaitForSingleObject((HANDLE)NetThread, INFINITE); LogThread = NULL; NetThread = NULL; ::DeleteFile(StoredTempFile); return 0; } BOOL CFrontendDlg::OnInitDialog() { HICON Small, Large; CDialog::OnInitDialog(); Small = (HICON)::LoadImage(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0); Large = (HICON)::LoadImage(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), 0); SetIcon(Small, FALSE); SetIcon(Large, TRUE); GetInterfaces(); m_TabCtrl.InsertItem(0, "Settings"); m_TabCtrl.InsertItem(1, "Output"); m_TabCtrl.InsertItem(2, "Nodes"); m_TabCtrl.InsertItem(3, "Routes"); m_TabCtrl.InitTabDialogs(&Interfaces, &Addresses, &IsWlan); m_StopButton.EnableWindow(FALSE); if (!ConfigFile.IsEmpty()) { if (m_TabCtrl.m_Dialog2.OpenConfigFile(ConfigFile) < 0) AfxMessageBox("Cannot open configuration file '" + ConfigFile + "'."); else { OnStartButton(); m_TabCtrl.SetCurSel(1); m_TabCtrl.DisplayTabDialog(); } } return TRUE; } void CFrontendDlg::OnOK() { } void CFrontendDlg::OnCancel() { OnExitButton(); } void CFrontendDlg::OnStartButton() { m_StartButton.EnableWindow(FALSE); m_TabCtrl.m_Dialog1.SetFrozen(1); if (StartOlsrd() < 0) { m_TabCtrl.m_Dialog1.SetFrozen(0); m_TabCtrl.m_Dialog1.AddOutputLine(""); AfxMessageBox("Cannot start OLSR server."); m_StartButton.EnableWindow(TRUE); return; } m_TabCtrl.m_Dialog1.HandleStart(); m_StopButton.EnableWindow(TRUE); } void CFrontendDlg::OnStopButton() { if (StopOlsrd() < 0) return; m_TabCtrl.m_Dialog1.HandleStop(); m_StopButton.EnableWindow(FALSE); m_StartButton.EnableWindow(TRUE); } void CFrontendDlg::OnExitButton() { delete TrayIcon::getInstance(); if (StopOlsrd() < 0) return; m_TabCtrl.m_Dialog3.ClearNodeInfo(); m_TabCtrl.m_Dialog4.ClearRoutes(); DestroyWindow(); }