|
7 | 7 | * Copyright (C) 2015,2016,2017,2018 Jonathan Naylor, G4KLX |
8 | 8 | * Copyright (C) 2016 Mathis Schmieder, DB9MAT |
9 | 9 | * Copyright (C) 2016 Colin Durbridge, G4EML |
10 | | - * Copyright (C) 2018,2024 Bryan Biedenkapp, N2PLL |
| 10 | + * Copyright (C) 2018,2024,2025 Bryan Biedenkapp, N2PLL |
11 | 11 | * |
12 | 12 | */ |
13 | 13 | #include "Globals.h" |
14 | 14 |
|
| 15 | +#if defined(NATIVE_SDR) |
| 16 | +#include "sdr/port/PseudoPTYPort.h" |
| 17 | + |
| 18 | +#include <sys/types.h> |
| 19 | +#include <unistd.h> |
| 20 | +#include <signal.h> |
| 21 | +#include <fcntl.h> |
| 22 | +#include <pwd.h> |
| 23 | +#include <cstdio> |
| 24 | +#include <cassert> |
| 25 | +#include <cstdlib> |
| 26 | +#include <cstring> |
| 27 | + |
| 28 | +#include <zmq.hpp> |
| 29 | +#endif |
| 30 | + |
| 31 | +// --------------------------------------------------------------------------- |
| 32 | +// Macros |
| 33 | +// --------------------------------------------------------------------------- |
| 34 | + |
| 35 | +#if defined(NATIVE_SDR) |
| 36 | +#define IS(s) (::strcmp(argv[i], s) == 0) |
| 37 | +#endif |
| 38 | + |
15 | 39 | // --------------------------------------------------------------------------- |
16 | 40 | // Globals Variables |
17 | 41 | // --------------------------------------------------------------------------- |
@@ -60,6 +84,29 @@ CWIdTX cwIdTX; |
60 | 84 | SerialPort serial; |
61 | 85 | IO io; |
62 | 86 |
|
| 87 | +#if defined(NATIVE_SDR) |
| 88 | +std::string g_progExe = std::string(__EXE_NAME__); |
| 89 | + |
| 90 | +std::string m_zmqRx = std::string("ipc:///tmp/dvm-rx.ipc"); |
| 91 | +std::string m_zmqTx = std::string("ipc:///tmp/dvm-tx.ipc"); |
| 92 | + |
| 93 | +std::string m_ptyPort = std::string("/dev/ptmx"); |
| 94 | + |
| 95 | +std::string g_logFileName = std::string("dsp.log"); |
| 96 | + |
| 97 | +bool g_debug = false; |
| 98 | + |
| 99 | +int g_signal = 0; |
| 100 | +bool g_killed = false; |
| 101 | + |
| 102 | +bool g_daemon = false; |
| 103 | + |
| 104 | +extern sdr::port::PseudoPTYPort* m_serialPort; |
| 105 | + |
| 106 | +extern zmq::socket_t m_zmqSocketTx; |
| 107 | +extern zmq::socket_t m_zmqSocketRx; |
| 108 | +#endif |
| 109 | + |
63 | 110 | // --------------------------------------------------------------------------- |
64 | 111 | // Global Functions |
65 | 112 | // --------------------------------------------------------------------------- |
@@ -278,3 +325,263 @@ int main() |
278 | 325 | loop(); |
279 | 326 | } |
280 | 327 | #endif // defined(STM32F4XX) |
| 328 | + |
| 329 | +#if defined(NATIVE_SDR) |
| 330 | +void fatal(const char* message) |
| 331 | +{ |
| 332 | + ::fprintf(stderr, "%s: %s\n", g_progExe.c_str(), message); |
| 333 | + exit(EXIT_FAILURE); |
| 334 | +} |
| 335 | + |
| 336 | +void usage(const char* message, const char* arg) |
| 337 | +{ |
| 338 | + ::fprintf(stdout, "" DESCRIPTION " (built %s)\r\n", __BUILD__); |
| 339 | + ::fprintf(stdout, "Copyright (c) 2025 Bryan Biedenkapp, N2PLL and DVMProject (https://github.com/dvmproject) Authors.\n"); |
| 340 | + ::fprintf(stdout, "Portions Copyright (c) 2015-2021 by Jonathan Naylor, G4KLX and others\n\n"); |
| 341 | + if (message != nullptr) { |
| 342 | + ::fprintf(stderr, "%s: ", g_progExe.c_str()); |
| 343 | + ::fprintf(stderr, message, arg); |
| 344 | + ::fprintf(stderr, "\n\n"); |
| 345 | + } |
| 346 | + |
| 347 | + ::fprintf(stdout, |
| 348 | + "usage: %s [-bdvh]" |
| 349 | + " [--syslog]" |
| 350 | + " [-r <ZeroMQ Rx IPC Endpoint>] [-t <ZeroMQ Tx IPC Endpoint>]" |
| 351 | + " [-p <PTY port>]" |
| 352 | + " [-l <log filename>]\n\n" |
| 353 | + " -b background process\n" |
| 354 | + "\n" |
| 355 | + " -d enable debug\n" |
| 356 | + " -v show version information\n" |
| 357 | + " -h show this screen\n" |
| 358 | + "\n" |
| 359 | + " --syslog force logging to syslog\n" |
| 360 | + "\n" |
| 361 | + " -r ZeroMQ Rx IPC Endpoint\n" |
| 362 | + " -t ZeroMQ Tx IPC Endpoint\n" |
| 363 | + "\n" |
| 364 | + " -p PTY Port\n" |
| 365 | + "\n" |
| 366 | + " -l Log Filename\n" |
| 367 | + "\n" |
| 368 | + " -- stop handling options\n", |
| 369 | + g_progExe.c_str()); |
| 370 | + exit(EXIT_FAILURE); |
| 371 | +} |
| 372 | + |
| 373 | +int checkArgs(int argc, char* argv[]) |
| 374 | +{ |
| 375 | + int i, p = 0; |
| 376 | + |
| 377 | + // iterate through arguments |
| 378 | + for (i = 1; i <= argc; i++) |
| 379 | + { |
| 380 | + if (argv[i] == nullptr) { |
| 381 | + break; |
| 382 | + } |
| 383 | + |
| 384 | + if (*argv[i] != '-') { |
| 385 | + continue; |
| 386 | + } |
| 387 | + else if (IS("--")) { |
| 388 | + ++p; |
| 389 | + break; |
| 390 | + } |
| 391 | + else if (IS("-r")) { |
| 392 | + if ((argc - 1) <= 0) |
| 393 | + usage("error: %s", "must specify the ZeroMQ Rx IPC Endpoint"); |
| 394 | + m_zmqRx = std::string(argv[++i]); |
| 395 | + |
| 396 | + if (m_zmqRx == "") |
| 397 | + usage("error: %s", "IPC endpoint cannot be blank!"); |
| 398 | + |
| 399 | + p += 2; |
| 400 | + } |
| 401 | + else if (IS("-t")) { |
| 402 | + if ((argc - 1) <= 0) |
| 403 | + usage("error: %s", "must specify the ZeroMQ Tx IPC Endpoint"); |
| 404 | + m_zmqTx = std::string(argv[++i]); |
| 405 | + |
| 406 | + if (m_zmqTx == "") |
| 407 | + usage("error: %s", "IPC endpoint cannot be blank!"); |
| 408 | + |
| 409 | + p += 2; |
| 410 | + } |
| 411 | + else if (IS("-p")) { |
| 412 | + if ((argc - 1) <= 0) |
| 413 | + usage("error: %s", "must specify the PTY port"); |
| 414 | + m_ptyPort = std::string(argv[++i]); |
| 415 | + |
| 416 | + if (m_ptyPort == "") |
| 417 | + usage("error: %s", "PTY port cannot be blank!"); |
| 418 | + |
| 419 | + p += 2; |
| 420 | + } |
| 421 | + else if (IS("-l")) { |
| 422 | + if ((argc - 1) <= 0) |
| 423 | + usage("error: %s", "must specify the log filename"); |
| 424 | + g_logFileName = std::string(argv[++i]); |
| 425 | + |
| 426 | + if (g_logFileName == "") |
| 427 | + usage("error: %s", "log filename cannot be blank!"); |
| 428 | + |
| 429 | + p += 2; |
| 430 | + } |
| 431 | + else if (IS("-b")) { |
| 432 | + ++p; |
| 433 | + g_daemon = true; |
| 434 | + } |
| 435 | + else if (IS("-d")) { |
| 436 | + ++p; |
| 437 | + g_debug = true; |
| 438 | + } |
| 439 | + else if (IS("--syslog")) { |
| 440 | + g_useSyslog = true; |
| 441 | + } |
| 442 | + else if (IS("-v")) { |
| 443 | + ::fprintf(stdout, "" DESCRIPTION " (built %s)\r\n", __BUILD__); |
| 444 | + ::fprintf(stdout, "Copyright (c) 2022 Bryan Biedenkapp, N2PLL and DVMProject (https://github.com/dvmproject) Authors.\r\n"); |
| 445 | + ::fprintf(stdout, "Portions Copyright (c) 2015-2021 by Jonathan Naylor, G4KLX and others\r\n"); |
| 446 | + if (argc == 2) |
| 447 | + exit(EXIT_SUCCESS); |
| 448 | + } |
| 449 | + else if (IS("-h")) { |
| 450 | + usage(nullptr, nullptr); |
| 451 | + if (argc == 2) |
| 452 | + exit(EXIT_SUCCESS); |
| 453 | + } |
| 454 | + else { |
| 455 | + usage("unrecognized option `%s'", argv[i]); |
| 456 | + } |
| 457 | + } |
| 458 | + |
| 459 | + if (p < 0 || p > argc) { |
| 460 | + p = 0; |
| 461 | + } |
| 462 | + |
| 463 | + return ++p; |
| 464 | +} |
| 465 | + |
| 466 | +static void sigHandler(int signum) |
| 467 | +{ |
| 468 | + g_killed = true; |
| 469 | + g_signal = signum; |
| 470 | +} |
| 471 | + |
| 472 | +// --------------------------------------------------------------------------- |
| 473 | +// Program Entry Point |
| 474 | +// --------------------------------------------------------------------------- |
| 475 | + |
| 476 | +int main(int argc, char** argv) |
| 477 | +{ |
| 478 | + m_zmqRx = std::string("ipc:///tmp/dvm-rx.ipc"); |
| 479 | + m_zmqTx = std::string("ipc:///tmp/dvm-tx.ipc"); |
| 480 | + |
| 481 | + if (argv[0] != nullptr && *argv[0] != 0) |
| 482 | + g_progExe = std::string(argv[0]); |
| 483 | + |
| 484 | + if (argc > 1) { |
| 485 | + // check arguments |
| 486 | + int i = checkArgs(argc, argv); |
| 487 | + if (i < argc) { |
| 488 | + argc -= i; |
| 489 | + argv += i; |
| 490 | + } |
| 491 | + else { |
| 492 | + argc--; |
| 493 | + argv++; |
| 494 | + } |
| 495 | + } |
| 496 | + |
| 497 | + ::signal(SIGINT, sigHandler); |
| 498 | + ::signal(SIGTERM, sigHandler); |
| 499 | + ::signal(SIGHUP, sigHandler); |
| 500 | + |
| 501 | + // initialize system logging |
| 502 | + bool ret = ::LogInitialise(".", g_logFileName.c_str(), 1U, 1U); |
| 503 | + if (!ret) { |
| 504 | + ::fprintf(stderr, "unable to open the log file\n"); |
| 505 | + return 1; |
| 506 | + } |
| 507 | + |
| 508 | + // handle POSIX process forking |
| 509 | + if (g_daemon) { |
| 510 | + // create new process |
| 511 | + pid_t pid = ::fork(); |
| 512 | + if (pid == -1) { |
| 513 | + ::fprintf(stderr, "%s: Couldn't fork() , exiting\n", g_progExe.c_str()); |
| 514 | + ::LogFinalise(); |
| 515 | + return EXIT_FAILURE; |
| 516 | + } |
| 517 | + else if (pid != 0) { |
| 518 | + ::LogFinalise(); |
| 519 | + exit(EXIT_SUCCESS); |
| 520 | + } |
| 521 | + |
| 522 | + // create new session and process group |
| 523 | + if (::setsid() == -1) { |
| 524 | + ::fprintf(stderr, "%s: Couldn't setsid(), exiting\n", g_progExe.c_str()); |
| 525 | + ::LogFinalise(); |
| 526 | + return EXIT_FAILURE; |
| 527 | + } |
| 528 | + |
| 529 | + // set the working directory to the root directory |
| 530 | + if (::chdir("/") == -1) { |
| 531 | + ::fprintf(stderr, "%s: Couldn't cd /, exiting\n", g_progExe.c_str()); |
| 532 | + ::LogFinalise(); |
| 533 | + return EXIT_FAILURE; |
| 534 | + } |
| 535 | + |
| 536 | + ::close(STDIN_FILENO); |
| 537 | + ::close(STDOUT_FILENO); |
| 538 | + ::close(STDERR_FILENO); |
| 539 | + } |
| 540 | + |
| 541 | + do { |
| 542 | + g_signal = 0; |
| 543 | + |
| 544 | + { |
| 545 | + ::LogInfo("" DESCRIPTION " (built %s)", __BUILD__); |
| 546 | + ::LogInfo("Copyright (c) 2017-2025 Bryan Biedenkapp, N2PLL and DVMProject (https://github.com/dvmproject) Authors."); |
| 547 | + ::LogInfo("Portions Copyright (c) 2015-2021 by Jonathan Naylor, G4KLX and others"); |
| 548 | + |
| 549 | + ::LogInfoEx(LOG_DSP, "DSP is performing initialization and warmup"); |
| 550 | + setup(); |
| 551 | + |
| 552 | + ::LogInfoEx(LOG_DSP, "DSP is up and running"); |
| 553 | + while (!g_killed) { |
| 554 | + loop(); |
| 555 | + ::usleep(1); |
| 556 | + } |
| 557 | + } |
| 558 | + |
| 559 | + if (g_signal == 2) |
| 560 | + ::LogInfoEx(LOG_DSP, "Exited on receipt of SIGINT"); |
| 561 | + |
| 562 | + if (g_signal == 15) |
| 563 | + ::LogInfoEx(LOG_DSP, "Exited on receipt of SIGTERM"); |
| 564 | + |
| 565 | + if (g_signal == 1) |
| 566 | + ::LogInfoEx(LOG_DSP, "Restarting on receipt of SIGHUP"); |
| 567 | + } while (g_signal == 1); |
| 568 | + |
| 569 | + ::LogInfoEx(LOG_DSP, "DSP is shutting down"); |
| 570 | + |
| 571 | + if (m_serialPort != nullptr) { |
| 572 | + m_serialPort->close(); |
| 573 | + delete m_serialPort; |
| 574 | + } |
| 575 | + |
| 576 | + try |
| 577 | + { |
| 578 | + m_zmqSocketTx.close(); |
| 579 | + m_zmqSocketRx.close(); |
| 580 | + } |
| 581 | + catch(const zmq::error_t& zmqE) { /* stub */ } |
| 582 | + catch(const std::exception& e) { /* stub */ } |
| 583 | + |
| 584 | + ::LogFinalise(); |
| 585 | + return 0; |
| 586 | +} |
| 587 | +#endif // defined(NATIVE_SDR) |
0 commit comments