Goby3 3.2.3
2025.05.13
Loading...
Searching...
No Matches
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
44namespace goby
45{
46namespace middleware
47{
48class Group;
49}
50} // namespace goby
51namespace goby
52{
53namespace middleware
54{
55namespace protobuf
56{
57class IOData;
58}
59} // namespace middleware
60} // namespace goby
61
62namespace goby
63{
64namespace middleware
65{
66namespace io
67{
68namespace detail
69{
70template <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
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>
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:
92 PTYThread(const goby::middleware::protobuf::PTYConfig& config, int index = -1)
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
114template <const goby::middleware::Group& line_in_group,
115 const goby::middleware::Group& line_out_group,
117 goby::middleware::io::PubSubLayer subscribe_layer, template <class> class ThreadType,
118 bool use_indexed_groups>
119void 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
simple exception class for goby applications
Definition exception.h:35
Class for grouping publications in the Goby middleware. Analogous to "topics" in ROS,...
Definition group.h:60
constexpr const char * c_str() const
Access the group's string value as a C string.
Definition group.h:80
Implements Thread for a three layer middleware setup ([ intervehicle [ interprocess [ interthread ] ]...
friend void basic_async_write(IOThreadImplementation *this_thread, std::shared_ptr< const goby::middleware::protobuf::IOData > io_msg)
PTYThread(const goby::middleware::protobuf::PTYConfig &config, int index=-1)
Constructs the thread.
detail namespace with internal helper functions
Definition json.hpp:247
The global namespace for the Goby project.
STL namespace.