Goby3  3.1.5a
2024.05.23
pty_interface.h
Go to the documentation of this file.
1 // Copyright 2020-2023:
2 // GobySoft, LLC (2013-)
3 // Community contributors (see AUTHORS file)
4 // File authors:
5 // Toby Schneider <toby@gobysoft.org>
6 //
7 //
8 // This file is part of the Goby Underwater Autonomy Project Libraries
9 // ("The Goby Libraries").
10 //
11 // The Goby Libraries are free software: you can redistribute them and/or modify
12 // them under the terms of the GNU Lesser General Public License as published by
13 // the Free Software Foundation, either version 2.1 of the License, or
14 // (at your option) any later version.
15 //
16 // The Goby Libraries are distributed in the hope that they will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU Lesser General Public License for more details.
20 //
21 // You should have received a copy of the GNU Lesser General Public License
22 // along with Goby. If not, see <http://www.gnu.org/licenses/>.
23 
24 #ifndef GOBY_MIDDLEWARE_IO_DETAIL_PTY_INTERFACE_H
25 #define GOBY_MIDDLEWARE_IO_DETAIL_PTY_INTERFACE_H
26 
27 #include <errno.h> // for errno
28 #include <fcntl.h> // for O_NOCTTY, O_RDWR
29 #include <memory> // for shared_ptr
30 #include <stdio.h> // for remove
31 #include <stdlib.h> // for grantpt, posix_o...
32 #include <string.h> // for strerror
33 #include <string> // for operator+, string
34 #include <sys/stat.h> // for lstat, stat, S_I...
35 #include <termios.h> // for cfsetspeed, cfma...
36 #include <unistd.h> // for symlink
37 
38 #include <boost/asio/posix/stream_descriptor.hpp> // for stream_descriptor
39 
40 #include "goby/exception.h" // for Exception
41 #include "goby/middleware/io/detail/io_interface.h" // for PubSubLayer, IOT...
42 #include "goby/middleware/protobuf/pty_config.pb.h" // for PTYConfig
43 
44 namespace goby
45 {
46 namespace middleware
47 {
48 class Group;
49 }
50 } // namespace goby
51 namespace goby
52 {
53 namespace middleware
54 {
55 namespace protobuf
56 {
57 class IOData;
58 }
59 } // namespace middleware
60 } // namespace goby
61 
62 namespace goby
63 {
64 namespace middleware
65 {
66 namespace io
67 {
68 namespace detail
69 {
70 template <const goby::middleware::Group& line_in_group,
71  const goby::middleware::Group& line_out_group,
72  // by default publish all incoming traffic to interprocess for logging
73  PubSubLayer publish_layer = PubSubLayer::INTERPROCESS,
74  // but only subscribe on interthread for outgoing traffic
75  PubSubLayer subscribe_layer = PubSubLayer::INTERTHREAD,
76  template <class> class ThreadType = goby::middleware::SimpleThread,
77  bool use_indexed_groups = false>
78 class PTYThread
79  : public detail::IOThread<line_in_group, line_out_group, publish_layer, subscribe_layer,
80  goby::middleware::protobuf::PTYConfig,
81  boost::asio::posix::stream_descriptor, ThreadType, use_indexed_groups>
82 {
83  using Base =
84  detail::IOThread<line_in_group, line_out_group, publish_layer, subscribe_layer,
86  boost::asio::posix::stream_descriptor, ThreadType, use_indexed_groups>;
87 
88  public:
93  : Base(config, index, std::string("pty: ") + config.port())
94  {
96  this->interthread().template publish<line_in_group>(ready);
97  }
98 
99  ~PTYThread() override {}
100 
101  private:
102  void async_write(std::shared_ptr<const goby::middleware::protobuf::IOData> io_msg) override
103  {
104  basic_async_write(this, io_msg);
105  }
106 
107  void open_socket() override;
108 };
109 } // namespace detail
110 } // namespace io
111 } // namespace middleware
112 } // namespace goby
113 
114 template <const goby::middleware::Group& line_in_group,
115  const goby::middleware::Group& line_out_group,
116  goby::middleware::io::PubSubLayer publish_layer,
117  goby::middleware::io::PubSubLayer subscribe_layer, template <class> class ThreadType,
118  bool use_indexed_groups>
119 void goby::middleware::io::detail::PTYThread<line_in_group, line_out_group, publish_layer,
120  subscribe_layer, ThreadType,
121  use_indexed_groups>::open_socket()
122 {
123  // remove old symlink
124  const char* pty_external_symlink = this->cfg().port().c_str();
125  struct stat stat_buffer;
126  // file exists
127  if (lstat(pty_external_symlink, &stat_buffer) == 0)
128  {
129  if (S_ISLNK(stat_buffer.st_mode) == 1)
130  {
131  if (remove(pty_external_symlink) == -1)
132  throw(goby::Exception(std::string("Could not remove existing symlink: ") +
133  pty_external_symlink));
134  }
135  else
136  {
137  throw(goby::Exception(std::string("File exists and is not symlink: ") +
138  pty_external_symlink));
139  }
140  }
141 
142  // open the PTY
143  int pty_internal = posix_openpt(O_RDWR | O_NOCTTY);
144 
145  if (pty_internal == -1)
146  throw(goby::Exception(std::string("Error in posix_openpt: ") + std::strerror(errno)));
147  if (grantpt(pty_internal) == -1)
148  throw(goby::Exception(std::string("Error in grantpt: ") + std::strerror(errno)));
149  if (unlockpt(pty_internal) == -1)
150  throw(goby::Exception(std::string("Error in unlockpt: ") + std::strerror(errno)));
151 
152  // structure to store the port settings in
153  termios ps;
154  if (tcgetattr(pty_internal, &ps) == -1)
155  throw(goby::Exception(std::string("Unable to get attributes for pty configuration: ") +
156  strerror(errno)));
157 
158  // raw mode
159  // https://man7.org/linux/man-pages/man3/cfmakeraw.3.html
160  // ps.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
161  // | INLCR | IGNCR | ICRNL | IXON);
162  // ps.c_oflag &= ~OPOST;
163  // ps.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
164  // ps.c_cflag &= ~(CSIZE | PARENB);
165  // ps.c_cflag |= CS8;
166  cfmakeraw(&ps);
167 
168  switch (this->cfg().baud())
169  {
170  case 2400: cfsetspeed(&ps, B2400); break;
171  case 4800: cfsetspeed(&ps, B4800); break;
172  case 9600: cfsetspeed(&ps, B9600); break;
173  case 19200: cfsetspeed(&ps, B19200); break;
174  case 38400: cfsetspeed(&ps, B38400); break;
175  case 57600: cfsetspeed(&ps, B57600); break;
176  case 115200: cfsetspeed(&ps, B115200); break;
177  default:
178  throw(goby::Exception(std::string("Invalid baud rate: ") +
179  std::to_string(this->cfg().baud())));
180  }
181 
182  // set no parity, stop bits, data bits
183  ps.c_cflag &= ~CSTOPB;
184  // no flow control
185  ps.c_cflag &= ~CRTSCTS;
186 
187  if (tcsetattr(pty_internal, TCSANOW, &ps) == -1)
188  throw(goby::Exception(std::string("Unable to set pty configuration attributes ") +
189  strerror(errno)));
190 
191  this->mutable_socket().assign(pty_internal);
192 
193  // re-symlink to new PTY
194  char pty_external_path[256];
195  ptsname_r(pty_internal, pty_external_path, sizeof(pty_external_path));
196 
197  if (symlink(pty_external_path, pty_external_symlink) == -1)
198  throw(goby::Exception(std::string("Could not create symlink: ") + pty_external_symlink));
199 }
200 
201 #endif
goby
The global namespace for the Goby project.
Definition: acomms_constants.h:33
goby::acomms::abc::protobuf::config
extern ::google::protobuf::internal::ExtensionIdentifier< ::goby::acomms::protobuf::DriverConfig, ::google::protobuf::internal::MessageTypeTraits< ::goby::acomms::abc::protobuf::Config >, 11, false > config
Definition: abc_driver.pb.h:203
goby::middleware::Thread< goby::middleware::protobuf::PTYConfig, InterVehicleForwarder< InterProcessForwarder< InterThreadTransporter > > >::index
int index() const
Definition: thread.h:145
detail
detail namespace with internal helper functions
Definition: json.hpp:246
goby::middleware::SimpleThread
Implements Thread for a three layer middleware setup ([ intervehicle [ interprocess [ interthread ] ]...
Definition: simple_thread.h:43
pty_config.pb.h
goby::middleware::io::PubSubLayer
PubSubLayer
Definition: io_transporters.h:38
to_string
NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL &j)
user-defined to_string function for JSON values
Definition: json.hpp:24301
goby::middleware::io::detail::PTYThread::~PTYThread
~PTYThread() override
Definition: pty_interface.h:99
goby::middleware::io::detail::IOThread< line_in_group, line_out_group, PubSubLayer::INTERPROCESS, PubSubLayer::INTERTHREAD, goby::middleware::protobuf::PTYConfig, boost::asio::posix::stream_descriptor, goby::middleware::SimpleThread, false >::basic_async_write
friend void basic_async_write(IOThreadImplementation *this_thread, std::shared_ptr< const goby::middleware::protobuf::IOData > io_msg)
Definition: io_interface.h:261
goby::middleware::protobuf::PTYConfig
Definition: pty_config.pb.h:74
goby::middleware::Group
Class for grouping publications in the Goby middleware. Analogous to "topics" in ROS,...
Definition: group.h:59
goby::middleware::io::detail::PTYThread
Definition: pty_interface.h:78
goby::middleware::SimpleThread< goby::middleware::protobuf::PTYConfig >::interthread
InterThreadTransporter & interthread()
Access the transporter on the interthread layer (this is the innermost transporter)
Definition: simple_thread.h:96
goby::middleware::io::detail::PTYThread::PTYThread
PTYThread(const goby::middleware::protobuf::PTYConfig &config, int index=-1)
Constructs the thread.
Definition: pty_interface.h:92
io_interface.h
goby::middleware::io::detail::IOThread
Definition: io_interface.h:79
goby::middleware::io::PubSubLayer::INTERPROCESS
@ INTERPROCESS
goby::Exception
simple exception class for goby applications
Definition: exception.h:34
exception.h
goby::middleware::io::ThreadState::SUBSCRIPTIONS_COMPLETE
@ SUBSCRIPTIONS_COMPLETE
goby::middleware::Group::c_str
constexpr const char * c_str() const
Access the group's string value as a C string.
Definition: group.h:80
goby::middleware::io::PubSubLayer::INTERTHREAD
@ INTERTHREAD