1 /*
2 * cli.cxx
3 *
4 * Command line interpreter
5 *
6 * Copyright (C) 2006-2008 Post Increment
7 *
8 * The contents of this file are subject to the Mozilla Public License
9 * Version 1.0 (the "License"); you may not use this file except in
10 * compliance with the License. You may obtain a copy of the License at
11 * http://www.mozilla.org/MPL/
12 *
13 * Software distributed under the License is distributed on an "AS IS"
14 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
15 * the License for the specific language governing rights and limitations
16 * under the License.
17 *
18 * The Original Code is WOpenMCU
19 *
20 * The Initial Developer of the Original Code is Post Increment
21 *
22 * Contributor(s): Craig Southeren (craigs@postincrement.com)
23 * Robert Jongbloed (robertj@voxlucida.com.au)
24 *
25 * Portions of this code were written by Post Increment (http://www.postincrement.com)
26 * with the assistance of funding from US Joint Forces Command Joint Concept Development &
27 * Experimentation (J9) http://www.jfcom.mil/about/abt_j9.htm
28 *
29 * Further assistance for enhancements from Imagicle spa
30 *
31 * $Revision: 24931 $
32 * $Author: csoutheren $
33 * $Date: 2010-12-07 20:01:15 -0600 (Tue, 07 Dec 2010) $
34 */
35
36 #include <ptlib.h>
37 #include <ptclib/cli.h>
38 #include <ptclib/telnet.h>
39
40
41 ///////////////////////////////////////////////////////////////////////////////
42
Context(PCLI & cli)43 PCLI::Context::Context(PCLI & cli)
44 : m_cli(cli)
45 , m_ignoreNextEOL(false)
46 , m_thread(NULL)
47 , m_state(cli.GetUsername().IsEmpty() ? (cli.GetPassword().IsEmpty() ? e_CommandEntry : e_Password) : e_Username)
48 {
49 }
50
51
~Context()52 PCLI::Context::~Context()
53 {
54 Stop();
55 delete m_thread;
56 }
57
58
Write(const void * buf,PINDEX len)59 PBoolean PCLI::Context::Write(const void * buf, PINDEX len)
60 {
61 if (m_cli.GetNewLine().IsEmpty())
62 return PIndirectChannel::Write(buf, len);
63
64 const char * newLinePtr = m_cli.GetNewLine();
65 PINDEX newLineLen = m_cli.GetNewLine().GetLength();
66
67 PINDEX written = 0;
68
69 const char * str = (const char *)buf;
70 const char * nextline;
71 while (len > 0 && (nextline = strchr(str, '\n')) != NULL) {
72 PINDEX lineLen = nextline - str;
73
74 if (!PIndirectChannel::Write(str, lineLen))
75 return false;
76
77 written += GetLastWriteCount();
78
79 if (!PIndirectChannel::Write(newLinePtr, newLineLen))
80 return false;
81
82 written += GetLastWriteCount();
83
84 len -= lineLen+1;
85 str = nextline+1;
86 }
87
88 if (!PIndirectChannel::Write(str, len))
89 return false;
90
91 lastWriteCount = written + GetLastWriteCount();
92 return true;
93 }
94
95
Start()96 bool PCLI::Context::Start()
97 {
98 if (!IsOpen()) {
99 PTRACE(2, "PCLI\tCannot start context, not open.");
100 return false;
101 }
102
103 if (m_thread == NULL)
104 m_thread = PThread::Create(PCREATE_NOTIFIER(ThreadMain), "CLI Context");
105
106 return true;
107 }
108
109
Stop()110 void PCLI::Context::Stop()
111 {
112 Close();
113
114 if (m_thread != NULL && PThread::Current() != m_thread) {
115 m_thread->WaitForTermination(10000);
116 delete m_thread;
117 m_thread = NULL;
118 }
119 }
120
121
OnStart()122 void PCLI::Context::OnStart()
123 {
124 WritePrompt();
125 }
126
127
OnStop()128 void PCLI::Context::OnStop()
129 {
130 }
131
132
WritePrompt()133 bool PCLI::Context::WritePrompt()
134 {
135 switch (m_state) {
136 case e_Username :
137 if (!m_cli.GetUsername().IsEmpty())
138 return WriteString(m_cli.GetUsernamePrompt());
139 // Do next case
140
141 case e_Password :
142 SetLocalEcho(false);
143 if (!m_cli.GetPassword().IsEmpty())
144 return WriteString(m_cli.GetPasswordPrompt());
145 // Do next case
146
147 default :
148 return WriteString(m_cli.GetPrompt());
149 }
150 }
151
152
ReadAndProcessInput()153 bool PCLI::Context::ReadAndProcessInput()
154 {
155 if (!IsOpen())
156 return false;
157
158 int ch = ReadChar();
159 if (ch < 0) {
160 PTRACE(2, "PCLI\tRead error: " << GetErrorText(PChannel::LastReadError));
161 return false;
162 }
163
164 return ProcessInput(ch);
165 }
166
ProcessInput(const PString & str)167 bool PCLI::Context::ProcessInput(const PString & str)
168 {
169 PStringArray lines = str.Lines();
170
171 PINDEX i;
172 for (i = 0; i < lines.GetSize(); ++i) {
173 PINDEX j;
174 PString & line = lines[i];
175 for (j = 0; j < line.GetLength(); ++j)
176 if (!ProcessInput(line[j]))
177 return false;
178 if (!ProcessInput('\n'))
179 return false;
180 }
181 return true;
182 }
183
ProcessInput(int ch)184 bool PCLI::Context::ProcessInput(int ch)
185 {
186 if (ch != '\n' && ch != '\r') {
187 if (m_cli.GetEditCharacters().Find((char)ch) != P_MAX_INDEX) {
188 if (!m_commandLine.IsEmpty()) {
189 m_commandLine.Delete(m_commandLine.GetLength()-1, 1);
190 if (m_cli.GetRequireEcho() && m_state != e_Password) {
191 if (!WriteString("\b \b"))
192 return false;
193 }
194 }
195 }
196 else if (ch > 0 && ch < 256 && isprint(ch)) {
197 m_commandLine += (char)ch;
198
199 if (m_cli.GetRequireEcho() && m_state != e_Password) {
200 if (!WriteChar(ch))
201 return false;
202 }
203 }
204
205 m_ignoreNextEOL = false;
206 return true;
207 }
208
209 if (m_ignoreNextEOL) {
210 m_ignoreNextEOL = false;
211 return true;
212 }
213
214 m_ignoreNextEOL = true;
215
216 switch (m_state) {
217 case e_Username :
218 if (m_cli.GetPassword().IsEmpty()) {
219 if (m_cli.OnLogIn(m_commandLine, PString::Empty()))
220 m_state = e_CommandEntry;
221 }
222 else {
223 m_enteredUsername = m_commandLine;
224 m_state = e_Password;
225 }
226 break;
227
228 case e_Password :
229 if (!WriteString(m_cli.GetNewLine()))
230 return false;
231
232 if (m_cli.OnLogIn(m_enteredUsername, m_commandLine))
233 m_state = e_CommandEntry;
234 else
235 m_state = m_cli.GetUsername().IsEmpty() ? (m_cli.GetPassword().IsEmpty() ? e_CommandEntry : e_Password) : e_Username;
236
237 SetLocalEcho(m_state != e_Password);
238 m_enteredUsername.MakeEmpty();
239 break;
240
241 default :
242 OnCompletedLine();
243 }
244
245 m_commandLine.MakeEmpty();
246 return WritePrompt();
247 }
248
249
CheckInternalCommand(const PCaselessString & line,const PCaselessString & cmds)250 static bool CheckInternalCommand(const PCaselessString & line, const PCaselessString & cmds)
251 {
252 PINDEX pos = cmds.Find(line);
253 if (pos == P_MAX_INDEX)
254 return false;
255 char terminator = cmds[pos + line.GetLength()];
256 return terminator == '\n' || terminator == '\0';
257 }
258
259
OnCompletedLine()260 void PCLI::Context::OnCompletedLine()
261 {
262 PCaselessString line = m_commandLine.Trim();
263 if (line.IsEmpty())
264 return;
265
266 PTRACE(4, "PCLI\tProcessing command line \"" << line << '"');
267
268 if (CheckInternalCommand(line, m_cli.GetExitCommand())) {
269 Stop();
270 return;
271 }
272
273 if (line.NumCompare(m_cli.GetRepeatCommand()) == EqualTo) {
274 if (m_commandHistory.IsEmpty()) {
275 *this << m_cli.GetNoHistoryError() << endl;
276 return;
277 }
278
279 line = m_commandHistory.back();
280 }
281
282 if (CheckInternalCommand(line, m_cli.GetHistoryCommand())) {
283 unsigned cmdNum = 1;
284 for (PStringList::iterator cmd = m_commandHistory.begin(); cmd != m_commandHistory.end(); ++cmd)
285 *this << cmdNum++ << ' ' << *cmd << '\n';
286 flush();
287 return;
288 }
289
290 if (line.NumCompare(m_cli.GetHistoryCommand()) == EqualTo) {
291 PINDEX cmdNum = line.Mid(m_cli.GetHistoryCommand().GetLength()).AsUnsigned();
292 if (cmdNum <= 0 || cmdNum > m_commandHistory.GetSize()) {
293 *this << m_cli.GetNoHistoryError() << endl;
294 return;
295 }
296
297 line = m_commandHistory[cmdNum-1];
298 }
299
300 if (CheckInternalCommand(line, m_cli.GetHelpCommand()))
301 m_cli.ShowHelp(*this);
302 else {
303 Arguments args(*this, line);
304 m_state = e_ProcessingCommand;
305 m_cli.OnReceivedLine(args);
306 m_state = e_CommandEntry;
307 }
308
309 m_commandHistory += line;
310 }
311
312
ThreadMain(PThread &,INT)313 void PCLI::Context::ThreadMain(PThread &, INT)
314 {
315 PTRACE(4, "PCLI\tContext thread started");
316
317 if (IsOpen()) {
318 OnStart();
319 while (ReadAndProcessInput())
320 ;
321 OnStop();
322 }
323
324 PTRACE(4, "PCLI\tContext thread ended");
325 }
326
327
328 ///////////////////////////////////////////////////////////////////////////////
329
Arguments(Context & context,const PString & rawLine)330 PCLI::Arguments::Arguments(Context & context, const PString & rawLine)
331 : PArgList(rawLine)
332 , m_context(context)
333 {
334 }
335
336
WriteUsage()337 PCLI::Context & PCLI::Arguments::WriteUsage()
338 {
339 if (!m_usage.IsEmpty())
340 m_context << m_context.GetCLI().GetCommandUsagePrefix() << m_usage << endl;
341 return m_context;
342 }
343
344
WriteError(const PString & error)345 PCLI::Context & PCLI::Arguments::WriteError(const PString & error)
346 {
347 m_context << m_command << m_context.GetCLI().GetCommandErrorPrefix();
348 if (!error.IsEmpty())
349 m_context << error << endl;
350 return m_context;
351 }
352
353
354 ///////////////////////////////////////////////////////////////////////////////
355
PCLI(const char * prompt)356 PCLI::PCLI(const char * prompt)
357 : m_newLine("\r\n")
358 , m_requireEcho(false)
359 , m_editCharacters("\b\x7f")
360 , m_prompt(prompt != NULL ? prompt : "CLI> ")
361 , m_usernamePrompt("Username: ")
362 , m_passwordPrompt("Password: ")
363 , m_exitCommand("exit\nquit")
364 , m_helpCommand("?\nhelp")
365 , m_helpOnHelp("Use ? or 'help' to display help\n"
366 "Use ! to list history of commands\n"
367 "Use !n to repeat the n'th command\n"
368 "Use !! to repeat last command\n"
369 "\n"
370 "Command available are:")
371 , m_repeatCommand("!!")
372 , m_historyCommand("!")
373 , m_noHistoryError("No command history")
374 , m_commandUsagePrefix("Usage: ")
375 , m_commandErrorPrefix(": error: ")
376 , m_unknownCommandError("Unknown command")
377 {
378 }
379
380
~PCLI()381 PCLI::~PCLI()
382 {
383 Stop();
384 }
385
386
Start(bool runInBackground)387 bool PCLI::Start(bool runInBackground)
388 {
389 if (runInBackground) {
390 PTRACE(4, "PCLI\tStarting background contexts");
391 m_contextMutex.Wait();
392 for (ContextList_t::iterator iter = m_contextList.begin(); iter != m_contextList.end(); ++iter)
393 (*iter)->Start();
394 m_contextMutex.Signal();
395 return true;
396 }
397
398 Context * context = StartForeground();
399 if (context == NULL)
400 return false;
401
402 return RunContext(context);
403 }
404
405
StartForeground()406 PCLI::Context * PCLI::StartForeground()
407 {
408 if (m_contextList.size() != 1) {
409 PTRACE(2, "PCLI\tCan only start in foreground if have one context.");
410 return NULL;
411 }
412
413 Context * context = m_contextList.front();
414 if (!context->IsOpen()) {
415 PTRACE(2, "PCLI\tCannot start foreground processing, context not open.");
416 return NULL;
417 }
418
419 context->OnStart();
420
421 return context;
422 }
423
424
RunContext(Context * context)425 bool PCLI::RunContext(Context * context)
426 {
427 while (context->ReadAndProcessInput())
428 ;
429 return true;
430 }
431
432
Stop()433 void PCLI::Stop()
434 {
435 m_contextMutex.Wait();
436 for (ContextList_t::iterator iter = m_contextList.begin(); iter != m_contextList.end(); ++iter)
437 (*iter)->Stop();
438 m_contextMutex.Signal();
439
440 GarbageCollection();
441 }
442
443
StartContext(PChannel * channel,bool autoDelete,bool runInBackground)444 bool PCLI::StartContext(PChannel * channel, bool autoDelete, bool runInBackground)
445 {
446 PCLI::Context * context = AddContext();
447 if (context == NULL)
448 return false;
449
450 if (!context->Open(channel, autoDelete)) {
451 PTRACE(2, "PCLI\tCould not open context: " << context->GetErrorText());
452 return false;
453 }
454
455 if (runInBackground)
456 return context->Start();
457
458 return true;
459 }
460
461
StartContext(PChannel * readChannel,PChannel * writeChannel,bool autoDeleteRead,bool autoDeleteWrite,bool runInBackground)462 bool PCLI::StartContext(PChannel * readChannel,
463 PChannel * writeChannel,
464 bool autoDeleteRead,
465 bool autoDeleteWrite,
466 bool runInBackground)
467 {
468 PCLI::Context * context = AddContext();
469 if (context == NULL)
470 return false;
471
472 if (!context->Open(readChannel, writeChannel, autoDeleteRead, autoDeleteWrite)) {
473 PTRACE(2, "PCLI\tCould not open context: " << context->GetErrorText());
474 return false;
475 }
476
477 if (runInBackground)
478 return context->Start();
479
480 return true;
481 }
482
483
CreateContext()484 PCLI::Context * PCLI::CreateContext()
485 {
486 return new Context(*this);
487 }
488
489
AddContext(Context * context)490 PCLI::Context * PCLI::AddContext(Context * context)
491 {
492 if (context == NULL) {
493 context = CreateContext();
494 if (context == NULL) {
495 PTRACE(2, "PCLI\tCould not create a context!");
496 return context;
497 }
498 }
499
500 m_contextMutex.Wait();
501 m_contextList.push_back(context);
502 m_contextMutex.Signal();
503
504 return context;
505 }
506
507
RemoveContext(Context * context)508 void PCLI::RemoveContext(Context * context)
509 {
510 if (!PAssert(context != NULL, PInvalidParameter))
511 return;
512
513 context->Close();
514
515 m_contextMutex.Wait();
516
517 for (ContextList_t::iterator iter = m_contextList.begin(); iter != m_contextList.end(); ++iter) {
518 if (*iter == context) {
519 delete context;
520 m_contextList.erase(iter);
521 break;
522 }
523 }
524
525 m_contextMutex.Signal();
526 }
527
528
GarbageCollection()529 void PCLI::GarbageCollection()
530 {
531 m_contextMutex.Wait();
532
533 ContextList_t::iterator iter = m_contextList.begin();
534 while (iter != m_contextList.end()) {
535 Context * context = *iter;
536 if (context->IsProcessingCommand() || context->IsOpen())
537 ++iter;
538 else {
539 RemoveContext(context);
540 iter = m_contextList.begin();
541 }
542 }
543
544 m_contextMutex.Signal();
545 }
546
547
OnReceivedLine(Arguments & args)548 void PCLI::OnReceivedLine(Arguments & args)
549 {
550 for (PINDEX nesting = 1; nesting <= args.GetCount(); ++nesting) {
551 PString names;
552 for (PINDEX i = 0; i < nesting; ++i)
553 names &= args[i];
554
555 CommandMap_t::iterator cmd = m_commands.find(names);
556 if (cmd != m_commands.end()) {
557 args.Shift(nesting);
558 args.m_command = cmd->first;
559 args.m_usage = cmd->second.m_usage;
560 cmd->second.m_notifier(args, 0);
561 return;
562 }
563 }
564
565 args.GetContext() << GetUnknownCommandError() << endl;
566 }
567
568
OnLogIn(const PString & username,const PString & password)569 bool PCLI::OnLogIn(const PString & username, const PString & password)
570 {
571 return m_username == username && m_password == password;
572 }
573
574
Broadcast(const PString & message) const575 void PCLI::Broadcast(const PString & message) const
576 {
577 for (ContextList_t::const_iterator iter = m_contextList.begin(); iter != m_contextList.end(); ++iter)
578 **iter << message << endl;
579 PTRACE(4, "PCLI\tBroadcast \"" << message << '"');
580 }
581
582
SetCommand(const char * command,const PNotifier & notifier,const char * help,const char * usage)583 bool PCLI::SetCommand(const char * command, const PNotifier & notifier, const char * help, const char * usage)
584 {
585 if (!PAssert(command != NULL && *command != '\0' && !notifier.IsNULL(), PInvalidParameter))
586 return false;
587
588 bool good = true;
589
590 PStringArray synonymArray = PString(command).Lines();
591 for (PINDEX s = 0; s < synonymArray.GetSize(); ++s) {
592 // Normalise command to remove any duplicate spaces, should only
593 // have one as in " conf show members " -> "conf show members"
594 PStringArray nameArray = synonymArray[s].Tokenise(' ', false);
595 PString names;
596 for (PINDEX n = 0; n < nameArray.GetSize(); ++n)
597 names &= nameArray[n];
598
599 if (m_commands.find(names) != m_commands.end())
600 good = false;
601 else {
602 InternalCommand & cmd = m_commands[names];
603 cmd.m_notifier = notifier;
604 cmd.m_help = help;
605 if (usage != NULL && *usage != '\0')
606 cmd.m_usage = names & usage;
607 }
608 }
609
610 return good;
611 }
612
613
ShowHelp(Context & context)614 void PCLI::ShowHelp(Context & context)
615 {
616 PINDEX i;
617 CommandMap_t::const_iterator cmd;
618
619 PINDEX maxCommandLength = GetHelpCommand().GetLength();
620 for (cmd = m_commands.begin(); cmd != m_commands.end(); ++cmd) {
621 PINDEX len = cmd->first.GetLength();
622 if (maxCommandLength < len)
623 maxCommandLength = len;
624 }
625
626 PStringArray lines = GetHelpOnHelp().Lines();
627 for (i = 0; i < lines.GetSize(); ++i)
628 context << lines[i] << '\n';
629
630 for (cmd = m_commands.begin(); cmd != m_commands.end(); ++cmd) {
631 if (cmd->second.m_help.IsEmpty() && cmd->second.m_usage.IsEmpty())
632 context << cmd->first;
633 else {
634 context << left << setw(maxCommandLength) << cmd->first << " ";
635
636 if (cmd->second.m_help.IsEmpty())
637 context << GetCommandUsagePrefix(); // Earlier conditon says must have usage
638 else {
639 lines = cmd->second.m_help.Lines();
640 context << lines[0];
641 for (i = 1; i < lines.GetSize(); ++i)
642 context << '\n' << setw(maxCommandLength+3) << ' ' << lines[i];
643 }
644
645 lines = cmd->second.m_usage.Lines();
646 for (i = 0; i < lines.GetSize(); ++i)
647 context << '\n' << setw(maxCommandLength+5) << ' ' << lines[i];
648 }
649 context << '\n';
650 }
651
652 context.flush();
653 }
654
655
656 ///////////////////////////////////////////////////////////////////////////////
657
PCLIStandard(const char * prompt)658 PCLIStandard::PCLIStandard(const char * prompt)
659 : PCLI(prompt)
660 {
661 }
662
663
Start(bool runInBackground)664 bool PCLIStandard::Start(bool runInBackground)
665 {
666 if (m_contextList.empty())
667 StartContext(new PConsoleChannel(PConsoleChannel::StandardInput),
668 new PConsoleChannel(PConsoleChannel::StandardOutput),
669 true, true, runInBackground);
670 return PCLI::Start(runInBackground);
671 }
672
StartForeground()673 PCLI::Context * PCLIStandard::StartForeground()
674 {
675 if (m_contextList.empty())
676 StartContext(new PConsoleChannel(PConsoleChannel::StandardInput),
677 new PConsoleChannel(PConsoleChannel::StandardOutput),
678 true, true, false);
679 return PCLI::StartForeground();
680 }
681
682
683 ///////////////////////////////////////////////////////////////////////////////
684
PCLISocket(WORD port,const char * prompt,bool singleThreadForAll)685 PCLISocket::PCLISocket(WORD port, const char * prompt, bool singleThreadForAll)
686 : PCLI(prompt)
687 , m_singleThreadForAll(singleThreadForAll)
688 , m_listenSocket(port)
689 , m_thread(NULL)
690 {
691 }
692
693
~PCLISocket()694 PCLISocket::~PCLISocket()
695 {
696 Stop();
697 delete m_thread;
698 }
699
700
Start(bool runInBackground)701 bool PCLISocket::Start(bool runInBackground)
702 {
703 if (!Listen())
704 return false;
705
706 if (runInBackground) {
707 if (m_thread != NULL)
708 return true;
709 m_thread = PThread::Create(PCREATE_NOTIFIER(ThreadMain), "CLI Server");
710 return m_thread != NULL;
711 }
712
713 while (m_singleThreadForAll ? HandleSingleThreadForAll() : HandleIncoming())
714 GarbageCollection();
715 return true;
716 }
717
718
Stop()719 void PCLISocket::Stop()
720 {
721 m_listenSocket.Close();
722
723 if (m_thread != NULL && PThread::Current() != m_thread) {
724 m_thread->WaitForTermination(10000);
725 delete m_thread;
726 m_thread = NULL;
727 }
728
729 PCLI::Stop();
730 }
731
732
AddContext(Context * context)733 PCLI::Context * PCLISocket::AddContext(Context * context)
734 {
735 context = PCLI::AddContext(context);
736
737 PTCPSocket * socket = dynamic_cast<PTCPSocket *>(context->GetReadChannel());
738 if (socket != NULL) {
739 m_contextMutex.Wait();
740 m_contextBySocket[socket] = context;
741 m_contextMutex.Signal();
742 }
743
744 return context;
745 }
746
747
RemoveContext(Context * context)748 void PCLISocket::RemoveContext(Context * context)
749 {
750 if (context == NULL)
751 return;
752
753 PTCPSocket * socket = dynamic_cast<PTCPSocket *>(context->GetReadChannel());
754 if (socket != NULL) {
755 m_contextMutex.Wait();
756
757 ContextMap_t::iterator iter = m_contextBySocket.find(socket);
758 if (iter != m_contextBySocket.end())
759 m_contextBySocket.erase(iter);
760
761 m_contextMutex.Signal();
762 }
763
764 PCLI::RemoveContext(context);
765 }
766
767
Listen(WORD port)768 bool PCLISocket::Listen(WORD port)
769 {
770 if (!m_listenSocket.Listen(5, port, PSocket::CanReuseAddress)) {
771 PTRACE(2, "PCLI\tCannot open PCLI socket on port " << port
772 << ", error: " << m_listenSocket.GetErrorText());
773 return false;
774 }
775
776 PTRACE(4, "PCLI\tCLI socket opened on port " << m_listenSocket.GetPort());
777 return true;
778 }
779
780
ThreadMain(PThread &,INT)781 void PCLISocket::ThreadMain(PThread &, INT)
782 {
783 PTRACE(4, "PCLI\tServer thread started on port " << GetPort());
784
785 while (m_singleThreadForAll ? HandleSingleThreadForAll() : HandleIncoming())
786 GarbageCollection();
787
788 PTRACE(4, "PCLI\tServer thread ended for port " << GetPort());
789 }
790
791
HandleSingleThreadForAll()792 bool PCLISocket::HandleSingleThreadForAll()
793 {
794 // create list of listening sockets
795 PSocket::SelectList readList;
796 readList += m_listenSocket;
797
798 m_contextMutex.Wait();
799 for (ContextMap_t::iterator iter = m_contextBySocket.begin(); iter != m_contextBySocket.end(); ++iter)
800 readList += *iter->first;
801 m_contextMutex.Signal();
802
803 // wait for something to become available
804 if (PIPSocket::Select(readList) == PChannel::NoError) {
805 // process sockets
806 for (PSocket::SelectList::iterator socket = readList.begin(); socket != readList.end(); ++socket) {
807 if (&*socket == &m_listenSocket)
808 HandleIncoming();
809 else {
810 ContextMap_t::iterator iterContext = m_contextBySocket.find(&*socket);
811 if (iterContext != m_contextBySocket.end()) {
812 char buffer[1024];
813 if (socket->Read(buffer, sizeof(buffer)-1)) {
814 PINDEX count = socket->GetLastReadCount();
815 for (PINDEX i = 0; i < count; ++i) {
816 if (!iterContext->second->ProcessInput(buffer[i]))
817 socket->Close();
818 }
819 }
820 else
821 socket->Close();
822 }
823 }
824 }
825 }
826
827 return m_listenSocket.IsOpen();
828 }
829
830
HandleIncoming()831 bool PCLISocket::HandleIncoming()
832 {
833 PTCPSocket * socket = CreateSocket();
834 if (socket->Accept(m_listenSocket)) {
835 PTRACE(3, "PCLI\tIncoming connection from " << socket->GetPeerHostName());
836 Context * context = CreateContext();
837 if (context != NULL && context->Open(socket, true)) {
838 if (m_singleThreadForAll)
839 context->OnStart();
840 else
841 context->Start();
842 AddContext(context);
843 return true;
844 }
845 }
846
847 PTRACE(2, "PCLI\tError accepting connection: " << m_listenSocket.GetErrorText());
848 delete socket;
849 return false;
850 }
851
852
CreateSocket()853 PTCPSocket * PCLISocket::CreateSocket()
854 {
855 return new PTCPSocket();
856 }
857
858
859 ///////////////////////////////////////////////////////////////////////////////
860
PCLITelnet(WORD port,const char * prompt,bool singleThreadForAll)861 PCLITelnet::PCLITelnet(WORD port, const char * prompt, bool singleThreadForAll)
862 : PCLISocket(port, prompt, singleThreadForAll)
863 {
864 }
865
866
CreateSocket()867 PTCPSocket * PCLITelnet::CreateSocket()
868 {
869 return new PTelnetSocket();
870 }
871
872
873 ///////////////////////////////////////////////////////////////////////////////
874