microcom.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * bare bones 'talk to modem' program - similar to 'cu -l $device'
  4. * inspired by mgetty's microcom
  5. *
  6. * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
  7. *
  8. * Licensed under GPLv2, see file LICENSE in this source tree.
  9. */
  10. #include "libbb.h"
  11. // set raw tty mode
  12. static void xget1(int fd, struct termios *t, struct termios *oldt)
  13. {
  14. tcgetattr(fd, oldt);
  15. *t = *oldt;
  16. cfmakeraw(t);
  17. // t->c_lflag &= ~(ISIG|ICANON|ECHO|IEXTEN);
  18. // t->c_iflag &= ~(BRKINT|IXON|ICRNL);
  19. // t->c_oflag &= ~(ONLCR);
  20. // t->c_cc[VMIN] = 1;
  21. // t->c_cc[VTIME] = 0;
  22. }
  23. static int xset1(int fd, struct termios *tio, const char *device)
  24. {
  25. int ret = tcsetattr(fd, TCSAFLUSH, tio);
  26. if (ret) {
  27. bb_perror_msg("can't tcsetattr for %s", device);
  28. }
  29. return ret;
  30. }
  31. int microcom_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  32. int microcom_main(int argc UNUSED_PARAM, char **argv)
  33. {
  34. int sfd;
  35. int nfd;
  36. struct pollfd pfd[2];
  37. struct termios tio0, tiosfd, tio;
  38. char *device_lock_file;
  39. enum {
  40. OPT_X = 1 << 0, // do not respect Ctrl-X, Ctrl-@
  41. OPT_s = 1 << 1, // baudrate
  42. OPT_d = 1 << 2, // wait for device response, ms
  43. OPT_t = 1 << 3, // timeout, ms
  44. };
  45. speed_t speed = 9600;
  46. int delay = -1;
  47. int timeout = -1;
  48. unsigned opts;
  49. // fetch options
  50. opt_complementary = "=1:s+:d+:t+"; // exactly one arg, numeric options
  51. opts = getopt32(argv, "Xs:d:t:", &speed, &delay, &timeout);
  52. // argc -= optind;
  53. argv += optind;
  54. // try to create lock file in /var/lock
  55. device_lock_file = (char *)bb_basename(argv[0]);
  56. device_lock_file = xasprintf("/var/lock/LCK..%s", device_lock_file);
  57. sfd = open(device_lock_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644);
  58. if (sfd < 0) {
  59. // device already locked -> bail out
  60. if (errno == EEXIST)
  61. bb_perror_msg_and_die("can't create '%s'", device_lock_file);
  62. // can't create lock -> don't care
  63. if (ENABLE_FEATURE_CLEAN_UP)
  64. free(device_lock_file);
  65. device_lock_file = NULL;
  66. } else {
  67. // %4d to make concurrent mgetty (if any) happy.
  68. // Mgetty treats 4-bytes lock files as binary,
  69. // not text, PID. Making 5+ char file. Brrr...
  70. fdprintf(sfd, "%4d\n", getpid());
  71. close(sfd);
  72. }
  73. // setup signals
  74. bb_signals(0
  75. + (1 << SIGHUP)
  76. + (1 << SIGINT)
  77. + (1 << SIGTERM)
  78. + (1 << SIGPIPE)
  79. , record_signo);
  80. // error exit code if we fail to open the device
  81. bb_got_signal = 1;
  82. // open device
  83. sfd = open_or_warn(argv[0], O_RDWR | O_NOCTTY | O_NONBLOCK);
  84. if (sfd < 0)
  85. goto done;
  86. fcntl(sfd, F_SETFL, O_RDWR);
  87. // put device to "raw mode"
  88. xget1(sfd, &tio, &tiosfd);
  89. // set device speed
  90. cfsetspeed(&tio, tty_value_to_baud(speed));
  91. if (xset1(sfd, &tio, argv[0]))
  92. goto done;
  93. // put stdin to "raw mode" (if stdin is a TTY),
  94. // handle one character at a time
  95. if (isatty(STDIN_FILENO)) {
  96. xget1(STDIN_FILENO, &tio, &tio0);
  97. if (xset1(STDIN_FILENO, &tio, "stdin"))
  98. goto done;
  99. }
  100. // main loop: check with poll(), then read/write bytes across
  101. pfd[0].fd = sfd;
  102. pfd[0].events = POLLIN;
  103. pfd[1].fd = STDIN_FILENO;
  104. pfd[1].events = POLLIN;
  105. bb_got_signal = 0;
  106. nfd = 2;
  107. // Not safe_poll: we want to exit on signal
  108. while (!bb_got_signal && poll(pfd, nfd, timeout) > 0) {
  109. if (nfd > 1 && pfd[1].revents) {
  110. char c;
  111. // read from stdin -> write to device
  112. if (safe_read(STDIN_FILENO, &c, 1) < 1) {
  113. // don't poll stdin anymore if we got EOF/error
  114. nfd--;
  115. goto skip_write;
  116. }
  117. // do we need special processing?
  118. if (!(opts & OPT_X)) {
  119. // ^@ sends Break
  120. if (VINTR == c) {
  121. tcsendbreak(sfd, 0);
  122. goto skip_write;
  123. }
  124. // ^X exits
  125. if (24 == c)
  126. break;
  127. }
  128. write(sfd, &c, 1);
  129. if (delay >= 0)
  130. safe_poll(pfd, 1, delay);
  131. skip_write: ;
  132. }
  133. if (pfd[0].revents) {
  134. #define iobuf bb_common_bufsiz1
  135. ssize_t len;
  136. // read from device -> write to stdout
  137. len = safe_read(sfd, iobuf, sizeof(iobuf));
  138. if (len > 0)
  139. full_write(STDOUT_FILENO, iobuf, len);
  140. else {
  141. // EOF/error -> bail out
  142. bb_got_signal = SIGHUP;
  143. break;
  144. }
  145. }
  146. }
  147. // restore device mode
  148. tcsetattr(sfd, TCSAFLUSH, &tiosfd);
  149. if (isatty(STDIN_FILENO))
  150. tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0);
  151. done:
  152. if (device_lock_file)
  153. unlink(device_lock_file);
  154. return bb_got_signal;
  155. }