Goby3 3.4.0
2026.04.13
Loading...
Searching...
No Matches
pty_interface.h
Go to the documentation of this file.
1// Copyright 2020-2026:
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 <
71 const goby::middleware::Group& line_in_group, const goby::middleware::Group& line_out_group,
72 // by default publish all incoming traffic to interprocess for logging
73 PubSubLayer publish_layer,
74 // but only subscribe on interthread for outgoing traffic
75 PubSubLayer subscribe_layer, template <class> class ThreadType, bool use_indexed_groups = false>
77 : public detail::IOThread<line_in_group, line_out_group, publish_layer, subscribe_layer,
78 goby::middleware::protobuf::PTYConfig,
79 boost::asio::posix::stream_descriptor, ThreadType, use_indexed_groups>
80{
81 using Base =
82 detail::IOThread<line_in_group, line_out_group, publish_layer, subscribe_layer,
84 boost::asio::posix::stream_descriptor, ThreadType, use_indexed_groups>;
85
86 public:
90 PTYThread(const goby::middleware::protobuf::PTYConfig& config, int index = -1)
91 : Base(config, index, std::string("pty: ") + config.port())
92 {
94 this->interthread().template publish<line_in_group>(ready);
95 }
96
97 ~PTYThread() override {}
98
99 private:
100 void async_write(std::shared_ptr<const goby::middleware::protobuf::IOData> io_msg) override
101 {
102 basic_async_write(this, io_msg);
103 }
104
105 void open_socket() override;
106};
107} // namespace detail
108} // namespace io
109} // namespace middleware
110} // namespace goby
111
112template <const goby::middleware::Group& line_in_group,
113 const goby::middleware::Group& line_out_group,
115 goby::middleware::io::PubSubLayer subscribe_layer, template <class> class ThreadType,
116 bool use_indexed_groups>
117void goby::middleware::io::detail::PTYThread<line_in_group, line_out_group, publish_layer,
118 subscribe_layer, ThreadType,
119 use_indexed_groups>::open_socket()
120{
121 // remove old symlink
122 const char* pty_external_symlink = this->cfg().port().c_str();
123 struct stat stat_buffer;
124 // file exists
125 if (lstat(pty_external_symlink, &stat_buffer) == 0)
126 {
127 if (S_ISLNK(stat_buffer.st_mode) == 1)
128 {
129 if (remove(pty_external_symlink) == -1)
130 throw(goby::Exception(std::string("Could not remove existing symlink: ") +
131 pty_external_symlink));
132 }
133 else
134 {
135 throw(goby::Exception(std::string("File exists and is not symlink: ") +
136 pty_external_symlink));
137 }
138 }
139
140 // open the PTY
141 int pty_internal = posix_openpt(O_RDWR | O_NOCTTY);
142
143 if (pty_internal == -1)
144 throw(goby::Exception(std::string("Error in posix_openpt: ") + std::strerror(errno)));
145 if (grantpt(pty_internal) == -1)
146 throw(goby::Exception(std::string("Error in grantpt: ") + std::strerror(errno)));
147 if (unlockpt(pty_internal) == -1)
148 throw(goby::Exception(std::string("Error in unlockpt: ") + std::strerror(errno)));
149
150 // structure to store the port settings in
151 termios ps;
152 if (tcgetattr(pty_internal, &ps) == -1)
153 throw(goby::Exception(std::string("Unable to get attributes for pty configuration: ") +
154 strerror(errno)));
155
156 // raw mode
157 // https://man7.org/linux/man-pages/man3/cfmakeraw.3.html
158 // ps.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
159 // | INLCR | IGNCR | ICRNL | IXON);
160 // ps.c_oflag &= ~OPOST;
161 // ps.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
162 // ps.c_cflag &= ~(CSIZE | PARENB);
163 // ps.c_cflag |= CS8;
164 cfmakeraw(&ps);
165
166 switch (this->cfg().baud())
167 {
168 case 2400: cfsetspeed(&ps, B2400); break;
169 case 4800: cfsetspeed(&ps, B4800); break;
170 case 9600: cfsetspeed(&ps, B9600); break;
171 case 19200: cfsetspeed(&ps, B19200); break;
172 case 38400: cfsetspeed(&ps, B38400); break;
173 case 57600: cfsetspeed(&ps, B57600); break;
174 case 115200: cfsetspeed(&ps, B115200); break;
175 default:
176 throw(goby::Exception(std::string("Invalid baud rate: ") +
177 std::to_string(this->cfg().baud())));
178 }
179
180 // set no parity, stop bits, data bits
181 ps.c_cflag &= ~CSTOPB;
182 // no flow control
183 ps.c_cflag &= ~CRTSCTS;
184
185 if (tcsetattr(pty_internal, TCSANOW, &ps) == -1)
186 throw(goby::Exception(std::string("Unable to set pty configuration attributes ") +
187 strerror(errno)));
188
189 this->mutable_socket().assign(pty_internal);
190
191 // re-symlink to new PTY
192 char pty_external_path[256];
193 ptsname_r(pty_internal, pty_external_path, sizeof(pty_external_path));
194
195 if (symlink(pty_external_path, pty_external_symlink) == -1)
196 throw(goby::Exception(std::string("Could not create symlink: ") + pty_external_symlink));
197}
198
199#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
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.