1 // ----------------------------------------------------------------------------
2 // arq_io.cxx
3 //
4 // support for ARQ server/client system such as pskmail and fl_arq
5 //
6 // Copyright (C) 2006-2017
7 //		Dave Freese, W1HKJ
8 // Copyright (C) 2008-2013
9 //		Stelios Bounanos, M0GLD
10 // Copyright (C) 2009-2013
11 //		John Douyere, VK2ETA
12 // Copyright (c) 2013
13 //		Remi Chateauneu, F4ECW
14 //
15 // This file is part of fldigi.
16 //
17 // Fldigi is free software: you can redistribute it and/or modify
18 // it under the terms of the GNU General Public License as published by
19 // the Free Software Foundation, either version 3 of the License, or
20 // (at your option) any later version.
21 //
22 // Fldigi is distributed in the hope that it will be useful,
23 // but WITHOUT ANY WARRANTY; without even the implied warranty of
24 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 // GNU General Public License for more details.
26 //
27 // You should have received a copy of the GNU General Public License
28 // along with fldigi.  If not, see <http://www.gnu.org/licenses/>.
29 // ----------------------------------------------------------------------------
30 
31 
32 #include <config.h>
33 
34 #ifdef __MINGW32__
35 #  include "compat.h"
36 #endif
37 
38 #include <fstream>
39 #include <sstream>
40 #include <string>
41 #include <cstdlib>
42 #include <ctime>
43 #include <errno.h>
44 
45 #include <sys/types.h>
46 #if !defined(__WIN32__) && !defined(__APPLE__)
47 #  include <sys/ipc.h>
48 #  include <sys/msg.h>
49 #endif
50 
51 #include <signal.h>
52 
53 #include "main.h"
54 #include "configuration.h"
55 #include "fl_digi.h"
56 #include "trx.h"
57 #include "arq_io.h"
58 
59 #include "threads.h"
60 #include "socket.h"
61 #include "debug.h"
62 #include "qrunner.h"
63 
64 #include <FL/Fl.H>
65 #include <FL/fl_ask.H>
66 
67 LOG_FILE_SOURCE(debug::LOG_ARQCONTROL);
68 
69 using namespace std;
70 
71 // =====================================================================
72 static pthread_t arq_thread;
73 static pthread_mutex_t arq_mutex = PTHREAD_MUTEX_INITIALIZER;
74 static pthread_mutex_t arq_rx_mutex = PTHREAD_MUTEX_INITIALIZER;
75 static pthread_mutex_t tosend_mutex = PTHREAD_MUTEX_INITIALIZER;
76 
77 static void *arq_loop(void *args);
78 
79 static bool arq_exit = false;
80 static bool arq_enabled;
81 static bool abort_flag = false;
82 
83 /// Any access to shared variables must be protected.
84 static string tosend = "";   // Protected by tosend_mutex
85 //static string enroute = "";  // Protected by tosend_mutex
86 
87 static string arqtext = "";  // Protected by arq_rx_mutex
88 static string txstring = ""; // Protected by arq_rx_mutex
89 bool arq_text_available = false; // Protected by arq_rx_mutex
90 								 // Beware 'arq_text_available' is accessed by other modules.
91 
92 // =====================================================================
93 
94 static const char *asc[128] = {
95 	"<NUL>", "<SOH>", "<STX>", "<ETX>",
96 	"<EOT>", "<ENQ>", "<ACK>", "<BEL>",
97 	"<BS>",  "<TAB>", "\n",  "<VT>",
98 	"<FF>",  "",  "<SO>",  "<SI>",
99 	"<DLE>", "<DC1>", "<DC2>", "<DC3>",
100 	"<DC4>", "<NAK>", "<SYN>", "<ETB>",
101 	"<CAN>", "<EM>",  "<SUB>", "<ESC>",
102 	"<FS>",  "<GS>",  "<RS>",  "<US>",
103 	" ",     "!",     "\"",    "#",
104 	"$",     "%",     "&",     "\'",
105 	"(",     ")",     "*",     "+",
106 	",",     "-",     ".",     "/",
107 	"0",     "1",     "2",     "3",
108 	"4",     "5",     "6",     "7",
109 	"8",     "9",     ":",     ";",
110 	"<",     "=",     ">",     "?",
111 	"@",     "A",     "B",     "C",
112 	"D",     "E",     "F",     "G",
113 	"H",     "I",     "J",     "K",
114 	"L",     "M",     "N",     "O",
115 	"P",     "Q",     "R",     "S",
116 	"T",     "U",     "V",     "W",
117 	"X",     "Y",     "Z",     "[",
118 	"\\",    "]",     "^",     "_",
119 	"`",     "a",     "b",     "c",
120 	"d",     "e",     "f",     "g",
121 	"h",     "i",     "j",     "k",
122 	"l",     "m",     "n",     "o",
123 	"p",     "q",     "r",     "s",
124 	"t",     "u",     "v",     "w",
125 	"x",     "y",     "z",     "{",
126 	"|",     "}",     "~",     "<DEL>"
127 };
128 
noctrl(string src)129 string noctrl(string src)
130 {
131 	static string retstr;
132 	retstr.clear();
133 	char hexstr[10];
134 	int c;
135 	for (size_t i = 0; i < src.length(); i++)  {
136 		c = src[i];
137 		if ( c > 0 && c < 128)
138 			retstr.append(asc[c]);
139 		else {
140 			snprintf(hexstr, sizeof(hexstr), "<%0X>", c & 0xFF);
141 			retstr.append(hexstr);
142 		}
143 	}
144 	return retstr;
145 }
146 
147 //======================================================================
148 
149 extern void parse_arqtext(string &toparse);
150 
set_button(Fl_Button * button,bool value)151 static void set_button(Fl_Button* button, bool value)
152 {
153 	button->value(value);
154 	button->do_callback();
155 }
156 
ParseMode(string src)157 void ParseMode(string src)
158 {
159 LOG_INFO("%s", src.c_str());
160 	if ((src.find("XMTTUNE") != string::npos) ||
161 		(src.find("PTTTUNE") != string::npos)) {
162 		int msecs = 100;
163 		if (src.length() > 7) {
164 			int ret = sscanf( src.substr(7, src.length() - 7).c_str(), "%d", &msecs);
165 			if (ret != 1 || msecs < 10 || msecs > 20000) msecs = 100;
166 		}
167 //		if (debug_pskmail)
168 			LOG_INFO("%s %5.2f sec", "ARQ set ptt-tune on", msecs/1000.0);
169 		REQ_SYNC(&waterfall::set_XmtRcvBtn, wf, true);
170 		REQ_SYNC(trx_tune);
171 		MilliSleep(msecs);
172 //		if (debug_pskmail)
173 			LOG_INFO("%s", "ARQ set ptt-tune off");
174 		REQ_SYNC(&waterfall::set_XmtRcvBtn, wf, false);
175 		REQ_SYNC(trx_receive);
176 	}
177 	else for (size_t i = 0; i < NUM_MODES; ++i) {
178 		if (strlen(mode_info[i].pskmail_name) > 0) {
179 			if (src == mode_info[i].pskmail_name) {
180 				if (active_modem->get_mode() == mode_info[i].mode) {
181 					LOG_INFO("Active modem already set to %s", src.c_str());
182 				} else {
183 					REQ_SYNC(init_modem_sync, mode_info[i].mode, 0);
184 					MilliSleep(100);
185 //				AbortARQ();
186 //				if (debug_pskmail)
187 					LOG_INFO("Modem set to %s", mode_info[i].pskmail_name);
188 				}
189 				break;
190 			}
191 		}
192 	}
193 	WriteARQ('\002');
194 }
195 
ParseRSID(string src)196 void ParseRSID(string src)
197 {
198 	if (src == "ON") {
199 //		if (debug_pskmail)
200 			LOG_INFO("%s", "RsID turned ON");
201 		REQ(set_button, btnRSID, 1);
202 	}
203 	if (src == "OFF") {
204 //		if (debug_pskmail)
205 			LOG_INFO("%s", "RsID turned OFF");
206 		REQ(set_button, btnRSID, 0);
207 	}
208 }
209 
210 
ParseTxRSID(string src)211 void ParseTxRSID(string src)
212 {
213 	if (src == "ON") {
214 //		if (debug_pskmail)
215 			LOG_INFO("%s", "TxRsID turned ON");
216 		REQ(set_button, btnTxRSID, 1);
217 	}
218 	if (src == "OFF") {
219 //		if (debug_pskmail)
220 			LOG_INFO("%s", "TxRsID turned OFF");
221 		REQ(set_button, btnTxRSID, 0);
222 	}
223 }
224 
parse_arqtext(string & toparse)225 void parse_arqtext(string &toparse)
226 {
227 	static	string strCmdText;
228 	static	string strSubCmd;
229 	unsigned long int idxCmd, idxCmdEnd, idxSubCmd, idxSubCmdEnd;
230 
231 	if (toparse.empty()) return;
232 
233 	LOG_VERBOSE("parsing: %s", noctrl(toparse).c_str());
234 
235 
236 	idxCmd = toparse.find("<cmd>");
237 	idxCmdEnd = toparse.find("</cmd>");
238 
239 	while ( idxCmd != string::npos && idxCmdEnd != string::npos && idxCmdEnd > idxCmd ) {
240 		LOG_VERBOSE("Parsing: %s", noctrl(toparse.substr(idxCmd, idxCmdEnd - idxCmd + 6)).c_str());
241 
242 		strCmdText = toparse.substr(idxCmd + 5, idxCmdEnd - idxCmd - 5);
243 		if (strCmdText == "server" && mailserver == false && mailclient == false) {
244 			mailserver = true;
245 			mailclient = false;
246 			string PskMailLogName;
247 			PskMailLogName.assign(PskMailDir);
248 			PskMailLogName.append("gMFSK.log");
249 			Maillogfile = new cLogfile(PskMailLogName.c_str());
250 			Maillogfile->log_to_file_start();
251 			REQ(set_button, wf->xmtlock, 1);
252 			if (progdefaults.PSKmailSweetSpot)
253 				active_modem->set_freq(progdefaults.PSKsweetspot);
254 			active_modem->set_freqlock(true);
255 			LOG_INFO("%s", "ARQ is set to pskmail server");
256 		} else if (strCmdText == "client" && mailclient == false && mailserver == false) {
257 			mailclient = true;
258 			mailserver = false;
259 			string PskMailLogName;
260 			PskMailLogName.assign(PskMailDir);
261 			PskMailLogName.append("gMFSK.log");
262 			Maillogfile = new cLogfile(PskMailLogName.c_str());
263 			Maillogfile->log_to_file_start();
264 			REQ(set_button, wf->xmtlock, 0);
265 			active_modem->set_freqlock(false);
266 			LOG_INFO("%s", "ARQ is set to pskmail client");
267 		} else if (strCmdText == "normal") {
268 			mailserver = false;
269 			mailclient = false;
270 			if (Maillogfile) {
271 				delete Maillogfile;
272 				Maillogfile = 0;
273 			}
274 			REQ(set_button, wf->xmtlock, 0);
275 			active_modem->set_freqlock(false);
276 			LOG_INFO("%s", "ARQ is reset to normal ops");
277 		} else if ((idxSubCmd = strCmdText.find("<mode>")) != string::npos) {
278 			idxSubCmdEnd = strCmdText.find("</mode>");
279 			if (	idxSubCmdEnd != string::npos &&
280 				idxSubCmdEnd > idxSubCmd ) {
281 				strSubCmd = strCmdText.substr(idxSubCmd + 6, idxSubCmdEnd - idxSubCmd - 6);
282 				LOG_INFO("%s %s", "ARQ mode ", strSubCmd.c_str());
283 				ParseMode(strSubCmd);
284 			}
285 		} else if ((idxSubCmd = strCmdText.find("<rsid>")) != string::npos) {
286 			idxSubCmdEnd = strCmdText.find("</rsid>");
287 			if (	idxSubCmdEnd != string::npos &&
288 				idxSubCmdEnd > idxSubCmd ) {
289 				strSubCmd = strCmdText.substr(idxSubCmd + 6, idxSubCmdEnd - idxSubCmd - 6);
290 				ParseRSID(strSubCmd);
291 				LOG_INFO("%s %s", "ARQ rsid ", strSubCmd.c_str());
292 			}
293 		} else if ((idxSubCmd = strCmdText.find("<txrsid>")) != string::npos) {
294 			idxSubCmdEnd = strCmdText.find("</txrsid>");
295 			if (	idxSubCmdEnd != string::npos &&
296 				idxSubCmdEnd > idxSubCmd ) {
297 				strSubCmd = strCmdText.substr(idxSubCmd + 8, idxSubCmdEnd - idxSubCmd - 8);
298 				ParseTxRSID(strSubCmd);
299 				LOG_INFO("%s %s", "ARQ txrsid ", strSubCmd.c_str());
300 			}
301 		} else if (strCmdText == "abort") {
302 			LOG_INFO("%s", "Abort current ARQ ops");
303 			abort_flag = true;
304 		}
305 
306 		toparse.erase(idxCmd, idxCmdEnd - idxCmd + 6);
307 		while (toparse[0] == '\n' || toparse[0] == '\r') toparse.erase(0, 1);
308 
309 		idxCmd = toparse.find("<cmd>");
310 		idxCmdEnd = toparse.find("</cmd>");
311 	}
312 	if (!toparse.empty())
313 		LOG_VERBOSE("Remaining text: %s", noctrl(toparse).c_str());
314 }
315 
316 #define TIMEOUT 180 // 3 minutes
317 
318 //======================================================================
319 // Gmfsk ARQ file i/o used only on Linux
320 //======================================================================
321 // checkTLF
322 // look for files named
323 //	TLFfldigi ==> tlfio is true and
324 //			  ==> mailclient is true
325 // in $HOME
326 
checkTLF()327 void checkTLF() {
328 	static	string TLFfile;
329 	static	string TLFlogname;
330 	ifstream testFile;
331 
332 	tlfio = mailserver = mailclient = false;
333 
334 	TLFfile.assign(PskMailDir);
335 	TLFfile.append("TLFfldigi");
336 
337 	testFile.open(TLFfile.c_str());
338 	if (testFile.is_open()) {
339 		testFile.close();
340 		mailclient = true;
341 		tlfio = true;
342 		TLFlogname.assign(PskMailDir);
343 		TLFlogname.append("gMFSK.log");
344 		Maillogfile = new cLogfile(TLFlogname.c_str());
345 		Maillogfile->log_to_file_start();
346 	}
347 }
348 
TLF_arqRx()349 static bool TLF_arqRx()
350 {
351 	/// The mutex is automatically unlocked when returning.
352 #if defined(__WIN32__) || defined(__APPLE__)
353 	return false;
354 #else
355 	time_t start_time, prog_time;
356 	static char mailline[1000];
357 	static string sAutoFile("");
358 	sAutoFile.assign(PskMailDir);
359 	sAutoFile.append("gmfsk_autofile");
360 
361 	ifstream autofile(sAutoFile.c_str());
362 	if(autofile) {
363 		time(&start_time);
364 		while (!autofile.eof()) {
365 			memset(mailline, 0, sizeof(mailline));
366 			autofile.getline(mailline, 998); // leave space for "\n" and null byte
367 			txstring.append(mailline);
368 			txstring.append("\n");
369 			time(&prog_time);
370 			if (prog_time - start_time > TIMEOUT) {
371 				LOG_ERROR("TLF file I/O failure");
372 				autofile.close();
373 				std::remove (sAutoFile.c_str());
374 				return false;
375 			}
376 		}
377 		autofile.close();
378 		std::remove (sAutoFile.c_str());
379 
380 		parse_arqtext(txstring);
381 
382 		if (abort_flag) {
383 			AbortARQ();
384 			abort_flag = false;
385 			return true;
386 		}
387 
388 		if (!txstring.empty()) {
389 			guard_lock arq_rx_lock(&arq_rx_mutex);
390 			if (arqtext.empty()) {
391 				arqtext = txstring;
392 				if (mailserver && progdefaults.PSKmailSweetSpot)
393 					active_modem->set_freq(progdefaults.PSKsweetspot);
394 				arq_text_available = true;
395 				active_modem->set_stopflag(false);
396 				start_tx();
397 			} else {
398 				arqtext.append(txstring);
399 				active_modem->set_stopflag(false);
400 			}
401 			txstring.clear();
402 		}
403 	}
404 	return true;
405 #endif
406 }
407 
408 //======================================================================
409 // Auto transmit of file contained in WRAP_auto_dir
410 //======================================================================
WRAP_auto_arqRx()411 bool WRAP_auto_arqRx()
412 {
413 	time_t start_time, prog_time;
414 	static char mailline[1000];
415 	static string sAutoFile("");
416 
417 	ifstream autofile;
418 
419 	if (sAutoFile.empty()) {
420 		sAutoFile.assign(FLMSG_WRAP_auto_dir);
421 		sAutoFile.append("wrap_auto_file");
422 		autofile.open(sAutoFile.c_str());
423 		if (!autofile) {
424 			sAutoFile.assign(WRAP_auto_dir);
425 			sAutoFile.append("wrap_auto_file");
426 			autofile.open(sAutoFile.c_str());
427 		}
428 	} else
429 		autofile.open(sAutoFile.c_str());
430 
431 	if(autofile) {
432 		/// Mutex is unlocked when leaving the block.
433 		guard_lock arq_rx_lock(&arq_rx_mutex);
434 		txstring.clear();
435 		time(&start_time);
436 		while (!autofile.eof()) {
437 			memset(mailline,0,1000);
438 			autofile.getline(mailline, 998); // leave space for "\n" and null byte
439 			txstring.append(mailline);
440 			txstring.append("\n");
441 			time(&prog_time);
442 			if (prog_time - start_time > TIMEOUT) {
443 				LOG_ERROR("autowrap file I/O failure");
444 				autofile.close();
445 				std::remove (sAutoFile.c_str());
446 				return false;
447 			}
448 		}
449 		autofile.close();
450 		std::remove (sAutoFile.c_str());
451 
452 		if (!txstring.empty()) {
453 			arqtext.assign("\n....start\n");
454 			arqtext.append(txstring);
455 			arqtext.append("\n......end\n");
456 			arq_text_available = true;
457 			LOG_DEBUG("%s", arqtext.c_str());
458 			start_tx();
459 			txstring.clear();
460 			return true;
461 		}
462 	}
463 	return false;
464 }
465 
466 //======================================================================
467 // Socket ARQ i/o used on all platforms
468 //======================================================================
469 
470 #define ARQLOOP_TIMING 50 // 100 // msec
471 #define CLIENT_TIMEOUT 5 // timeout after 5 secs
472 
473 struct ARQCLIENT { Socket sock; time_t keep_alive; };
474 static string errstring;
475 
476 static pthread_t* arq_socket_thread = 0;
477 ARQ_SOCKET_Server* ARQ_SOCKET_Server::inst = 0;
478 static std::vector<ARQCLIENT *> arqclient; // Protected by arq_mutex
479 
480 void arq_run(Socket);
481 
ARQ_SOCKET_Server()482 ARQ_SOCKET_Server::ARQ_SOCKET_Server()
483 {
484 	server_socket = new Socket;
485 	arq_socket_thread = new pthread_t;
486 	run = true;
487 }
488 
~ARQ_SOCKET_Server()489 ARQ_SOCKET_Server::~ARQ_SOCKET_Server()
490 {
491 	run = false;
492 	if (arq_socket_thread) {
493 		CANCEL_THREAD(*arq_socket_thread);
494 		pthread_join(*arq_socket_thread, NULL);
495 		delete arq_socket_thread;
496 		arq_socket_thread = 0;
497 	}
498 	delete server_socket;
499 }
500 
start(const char * node,const char * service)501 bool ARQ_SOCKET_Server::start(const char* node, const char* service)
502 {
503 	if (inst) return false;
504 
505 	inst = new ARQ_SOCKET_Server;
506 
507 	try {
508 		inst->server_socket->open(Address(node, service));
509 		inst->server_socket->bind();
510 #ifdef __WIN32__
511 		inst->server_socket->listen();
512 		inst->server_socket->set_timeout(0.1);
513 #endif
514 	}
515 	catch (const SocketException& e) {
516 		errstring.assign("Could not start ARQ server (");
517 		errstring.append(e.what()).append(")");
518 		if (e.error() == EADDRINUSE)
519 			errstring.append("\nMultiple instances of fldigi??");
520 		LOG_ERROR("%s", errstring.c_str());
521 
522 		delete arq_socket_thread;
523 		arq_socket_thread = 0;
524 		delete inst;
525 		inst = 0;
526 		return false;
527 	}
528 
529 	return !pthread_create(arq_socket_thread, NULL, thread_func, NULL);
530 }
531 
532 bool server_stopped = false;
533 
stop(void)534 void ARQ_SOCKET_Server::stop(void)
535 {
536 	if (!inst)
537 		return;
538 	inst->run = false;
539 	SET_THREAD_CANCEL();
540 	MilliSleep(50);
541 
542 #if !defined(__WOE32__) && !defined(__APPLE__)
543 	int timeout = 10;
544 	while(!server_stopped) {
545 		MilliSleep(100);
546 		Fl::awake();
547 		if (--timeout == 0) break;
548 	}
549 #endif
550 
551 	delete inst;
552 	inst = 0;
553 
554 }
555 
thread_func(void *)556 void* ARQ_SOCKET_Server::thread_func(void*)
557 {
558 	SET_THREAD_ID(ARQSOCKET_TID);
559 
560 	SET_THREAD_CANCEL();
561 
562 	// On POSIX we block indefinitely and are interrupted by a signal.
563 	// On WIN32 we block for a short time and test for cancellation.
564 	while (inst->run) {
565 		try {
566 #ifdef __WIN32__
567 			if (inst->server_socket->wait(0))
568 				arq_run(inst->server_socket->accept());
569 #else
570 			arq_run(inst->server_socket->accept());
571 			TEST_THREAD_CANCEL();
572 #endif
573 		}
574 		catch (const SocketException& e) {
575 			if (e.error() != EINTR) {
576 				errstring = e.what();
577 				LOG_ERROR("%s", errstring.c_str());
578 				break;
579 			}
580 		}
581 		catch (...) {
582 			break;
583 		}
584 	}
585 
586 	{
587 		/// Mutex is unlocked when leaving the block.
588 		guard_lock arq_lock(&arq_mutex);
589 
590 		if (!arqclient.empty()) {
591 			for (vector<ARQCLIENT *>::iterator p = arqclient.begin();
592 					p < arqclient.end();
593 					p++) {
594 				(*p)->sock.close();
595 				arqclient.erase(p);
596 			}
597 		}
598 	}
599 
600 	inst->server_socket->close();
601 	server_stopped = true;
602 	return NULL;
603 }
604 
arq_reset()605 void arq_reset()
606 {
607 	/// Mutex is unlocked when returning from function
608 	guard_lock arq_rx_lock(&arq_rx_mutex);
609 	arqmode = mailserver = mailclient = false;
610 //	txstring.clear();
611 //	arqtext.clear();
612 }
613 
arq_run(Socket s)614 void arq_run(Socket s)
615 {
616 	/// Mutex is unlocked when returning from function
617 	guard_lock arq_lock(&arq_mutex);
618 	struct timeval t = { 0, 20000 };
619 	s.set_timeout(t);
620 	s.set_nonblocking();
621 	ARQCLIENT *client = new ARQCLIENT;
622 	client->sock = s;
623 	client->keep_alive = time(0);
624 	arqclient.push_back(client);
625 	arqmode = true;
626 	vector<ARQCLIENT *>::iterator p = arqclient.begin();
627 	ostringstream outs;
628 	outs << "Clients: ";
629 	while (p != arqclient.end()) {
630 		outs << (*p)->sock.fd() << " ";
631 		p++;
632 	}
633 	LOG_INFO("%s", outs.str().c_str());
634 }
635 
WriteARQsocket(unsigned char * data,size_t len)636 void WriteARQsocket(unsigned char* data, size_t len)
637 {
638 	/// Mutex is unlocked when returning from function
639 	guard_lock arq_lock(&arq_mutex);
640 	if (arqclient.empty()) return;
641 	static string instr;
642 	instr.clear();
643 
644 	string outs = "";
645 	for (unsigned int i = 0; i < len; i++)
646 		outs += asc[data[i] & 0x7F];
647 	LOG_INFO("%s", outs.c_str());
648 
649 	vector<ARQCLIENT *>::iterator p;
650 	for (p = arqclient.begin(); p < arqclient.end(); p++) {
651 		try {
652 			(*p)->sock.wait(1);
653 			(*p)->sock.send(data, len);
654 			(*p)->keep_alive = time(0);
655 			p++;
656 		}
657 		catch (const SocketException& e) {
658 			LOG_INFO("closing socket fd %d %s", (*p)->sock.fd(), e.what());
659 			try {
660 				(*p)->sock.close();
661 			} catch (const SocketException& e) {
662 				LOG_ERROR("Socket error on # %d, %d: %s", (*p)->sock.fd(), e.error(), e.what());
663 			}
664 			arqclient.erase(p);
665 		}
666 	}
667 
668 	if (arqclient.empty()) arq_reset();
669 }
670 
test_arq_clients()671 void test_arq_clients()
672 {
673 /// Mutex is unlocked when returning from function
674 	guard_lock arq_lock(&arq_mutex);
675 	if (arqclient.empty()) return;
676 	static string instr;
677 	instr.clear();
678 	vector<ARQCLIENT *>::iterator p;
679 	p = arqclient.begin();
680 	time_t now;
681 	size_t ret;
682 	while (p < arqclient.end()) {
683 		if (difftime(now = time(0), (*p)->keep_alive) > CLIENT_TIMEOUT) {
684 			try {
685 				(*p)->sock.wait(1);
686 				ret = (*p)->sock.send("\0", 1);
687 				if (ret <= 0) {
688 					LOG_INFO("closing inactive socket %d", (int)((*p)->sock.fd()));
689 					(*p)->sock.close();
690 					arqclient.erase(p); // sets p to next iterator
691 				} else {
692 					(*p)->keep_alive = now;
693 					p++;
694 				}
695 			}
696 			catch (const SocketException& e) {
697 				LOG_INFO("socket %d timed out, error %d, %s", (*p)->sock.fd(), e.error(), e.what());
698 				try {
699 					(*p)->sock.close();
700 				} catch (const SocketException& e) {
701 					LOG_ERROR("Socket error on # %d, %d: %s", (*p)->sock.fd(), e.error(), e.what());
702 				}
703 				arqclient.erase(p);
704 			}
705 		} else {
706 			p++;
707 		}
708 	}
709 	if (arqclient.empty()) arq_reset();
710 }
711 
Socket_arqRx()712 bool Socket_arqRx()
713 {
714 	{
715 		/// Mutex is unlocked when leaving block
716 		guard_lock arq_lock(&arq_mutex);
717 		if (arqclient.empty()) return false;
718 
719 		static string instr;
720 		vector<ARQCLIENT *>::iterator p = arqclient.begin();
721 		size_t n = 0;
722 		instr.clear();
723 		while (p != arqclient.end()) {
724 			try {
725 				(*p)->sock.wait(0);
726 				while ( (n = (*p)->sock.recv(instr)) > 0) {
727 					txstring.append(instr);
728 					LOG_VERBOSE("%s", txstring.c_str());
729 				}
730 				p++;
731 			}
732 			catch (const SocketException& e) {
733 				txstring.clear();
734 				LOG_INFO("closing socket fd %d, %d: %s", (*p)->sock.fd(), e.error(), e.what());
735 				try {
736 					(*p)->sock.close();
737 				} catch (const SocketException& e) {
738 					LOG_ERROR("socket error on # %d, %d: %s", (*p)->sock.fd(), e.error(), e.what());
739 				}
740 				arqclient.erase(p);
741 			}
742 		}
743 
744 		if (arqclient.empty()) arq_reset();
745 
746 	}
747 
748 	if (!txstring.empty()) parse_arqtext(txstring);
749 
750 	if (abort_flag) {
751 		AbortARQ();
752 		abort_flag = false;
753 		return true;
754 	}
755 
756 	{
757 		/// Mutex is unlocked when leaving block
758 		guard_lock arq_rx_lock(&arq_rx_mutex);
759 
760 		if (txstring.empty()) return false;
761 
762 		arqtext.append(txstring);
763 
764 		if (mailserver && progdefaults.PSKmailSweetSpot)
765 			active_modem->set_freq(progdefaults.PSKsweetspot);
766 
767 		if (trx_state != STATE_TX)
768 			start_tx();
769 
770 		txstring.clear();
771 
772 		arq_text_available = true;
773 		active_modem->set_stopflag(false);
774 
775 	}
776 	return true;
777 }
778 
779 //======================================================================
780 // Implementation using thread vice the fldigi timeout facility
781 //======================================================================
782 
WriteARQ(unsigned char data)783 void WriteARQ(unsigned char data)
784 {
785 	if (active_modem->get_mode() == MODE_FSQ) return;
786 	guard_lock tosend_lock(&tosend_mutex);
787 	tosend += data;
788 }
789 
WriteARQ(const char * data)790 void WriteARQ(const char *data)
791 {
792 	if (active_modem->get_mode() == MODE_FSQ) return;
793 	guard_lock tosend_lock(&tosend_mutex);
794 	tosend.append(data);
795 }
796 
arq_loop(void * args)797 static void *arq_loop(void *args)
798 {
799 	SET_THREAD_ID(ARQ_TID);
800 
801 	for (;;) {
802 		/* see if we are being canceled */
803 		if (arq_exit)
804 			break;
805 
806 		test_arq_clients();
807 
808 		{
809 			/// Mutex is unlocked when exiting block
810 			guard_lock tosend_lock(&tosend_mutex);
811 //			enroute.clear();
812 			if (!tosend.empty()) {
813 //				enroute = tosend;
814 				WriteARQsocket((unsigned char*)tosend.c_str(), tosend.length());
815 				tosend.clear();
816 			}
817 
818 //			if (!enroute.empty()) {
819 //				WriteARQsocket((unsigned char*)enroute.c_str(), enroute.length());
820 //			}
821 		}
822 		if (arq_exit) break;
823 
824 		// order of precedence; Socket, Wrap autofile, TLF autofile
825 		if (!Socket_arqRx())
826 			if (!WRAP_auto_arqRx())
827 				TLF_arqRx();
828 
829 		MilliSleep(ARQLOOP_TIMING);
830 
831 	}
832 	// exit the arq thread
833 	return NULL;
834 }
835 
arq_state(void)836 bool arq_state(void)
837 {
838 	return arq_enabled;
839 }
840 
arq_init()841 void arq_init()
842 {
843 	arq_enabled = false;
844 
845 	txstring.clear();
846 	arqclient.clear();
847 
848 	if (!ARQ_SOCKET_Server::start( progdefaults.arq_address.c_str(), progdefaults.arq_port.c_str() )) {
849 		arq_enabled = false;
850 		return;
851 	}
852 
853 	if (pthread_create(&arq_thread, NULL, arq_loop, NULL) < 0) {
854 		LOG_ERROR("arq init: pthread_create failed");
855 		arq_enabled = false;
856 		return;
857 	}
858 
859 	arq_enabled = true;
860 }
861 
arq_close(void)862 void arq_close(void)
863 {
864 	if (!arq_enabled) return;
865 
866 	ARQ_SOCKET_Server::stop();
867 
868 	// tell the arq thread to kill it self
869 	{
870 		guard_lock arqclose(&tosend_mutex);
871 		arq_exit = true;
872 	}
873 // and then wait for it to die
874 	pthread_join(arq_thread, NULL);
875 
876 	arq_enabled = false;
877 	LOG_INFO("ARQ closed");
878 	if(data_io_enabled == ARQ_IO)
879 		data_io_enabled = DISABLED_IO ;
880 	arq_exit = false;
881 
882 }
883 
arq_get_char()884 int arq_get_char()
885 {
886 	/// Mutex is unlocked when returning from function
887 	guard_lock arq_rx_lock(&arq_rx_mutex);
888 	int c = 0;
889 	if (arq_text_available) {
890 		if (!arqtext.empty()) {
891 			c = arqtext[0] & 0xFF;
892 			arqtext.erase(0,1);
893 		} else {
894 			arq_text_available = false;
895 			c = GET_TX_CHAR_ETX;
896 		}
897 	}
898 	return c;
899 }
900 
flush_arq_tx_buffer(void)901 void flush_arq_tx_buffer(void)
902 {
903 	guard_lock arq_rx_lock(&arq_rx_mutex);
904     arq_text_available = false;
905 //    arqtext.clear();
906 }
907 
908 //======================================================================
909 // following function used if the T/R button is pressed to stop a transmission
910 // that is servicing the ARQ text buffer.  It allows the ARQ client to reset
911 // itself properly
912 //======================================================================
AbortARQ()913 void AbortARQ() {
914 	/// Mutex is unlocked when returning from function
915 	guard_lock arq_lock(&arq_rx_mutex);
916 	arqtext.clear();
917 	txstring.clear();
918 	arq_text_available = false;
919 }
920 
921 //======================================================================
922 // Special notification for PSKMAIL: new mode marked only, in following
923 // format: "<DC2><Mode:newmode>", with <DC2> = '0x12'.
924 //======================================================================
pskmail_notify_rsid(trx_mode mode)925 void pskmail_notify_rsid(trx_mode mode)
926 {
927 	static char buf[64];
928 	memset(buf, 0, sizeof(buf));
929 	int n = snprintf(buf, sizeof(buf),
930 					 "\x12<Mode:%s>\n",
931 					 mode_info[mode].name);
932 	if (n > 0 && n < (int)sizeof(buf)) {
933 		WriteARQ((const char *)buf);
934 		REQ(&FTextBase::addstr, ReceiveText, buf, FTextBase::CTRL);
935 		LOG_INFO("%s", buf);
936 	}
937 }
938 
939 //======================================================================
940 // Special notification for PSKMAIL: signal to noise measured by decoder
941 // format "<DC2><s2n: CC, A.a, D.d>"
942 // where CC = count, A.a = average s/n, D.d = Std dev of s/n
943 //======================================================================
pskmail_notify_s2n(double s2n_ncount,double s2n_avg,double s2n_stddev)944 void pskmail_notify_s2n(double s2n_ncount, double s2n_avg, double s2n_stddev)
945 {
946 	static char buf[64];
947 	memset(buf, 0, sizeof(buf));
948 	int n = snprintf(buf, sizeof(buf),
949 					 "\x12<s2n: %1.0f, %1.1f, %1.1f>\n",
950 					 s2n_ncount, s2n_avg, s2n_stddev);
951 	if (n > 0 && n < (int)sizeof(buf)) {
952 		WriteARQ((const char *)buf);
953 		REQ(&FTextBase::addstr, ReceiveText, buf, FTextBase::CTRL);
954 		LOG_INFO("%s", buf);
955 	}
956 }
957 
958