1 /******************************************************************************
2 (c) 2001-2008 Christine Caulfield christine.caulfield@googlemail.com
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13 ******************************************************************************/
14
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <stdio.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <termios.h>
21 #include <string.h>
22 #include <list>
23 #include <queue>
24 #include <string>
25 #include <map>
26 #include <sstream>
27 #include <iomanip>
28
29 #include "lat.h"
30 #include "utils.h"
31 #include "session.h"
32 #include "connection.h"
33 #include "circuit.h"
34 #include "latcpcircuit.h"
35 #include "localport.h"
36 #include "server.h"
37 #include "lat_messages.h"
38
LocalPort(unsigned char * _service,unsigned char * _portname,unsigned char * _devname,unsigned char * _remnode,bool _queued,bool _clean,unsigned char * _password)39 LocalPort::LocalPort(unsigned char *_service, unsigned char *_portname,
40 unsigned char *_devname, unsigned char *_remnode,
41 bool _queued, bool _clean, unsigned char *_password):
42 service((char*)_service),
43 portname((char*)_portname),
44 devname((char*)_devname),
45 remnode((char*)_remnode),
46 password((char*)_password),
47 queued(_queued),
48 clean(_clean),
49 slave_fd_open(false),
50 connected(false)
51 {
52 debuglog(("New local port %s\n", devname.c_str()));
53 }
54
LocalPort(const LocalPort & p)55 LocalPort::LocalPort(const LocalPort &p)
56 {
57 service = p.service;
58 portname = p.portname;
59 devname = p.devname;
60 remnode = p.remnode;
61 password = p.password;
62 queued = p.queued;
63 clean = p.clean;
64 slave_fd_open = p.slave_fd_open;
65 connected = p.connected;
66 strcpy(ptyname, p.ptyname);
67 }
68
69
init_port()70 void LocalPort::init_port()
71 {
72 // A quick word of explanation here.
73 // We keep the slave fd open after openpty because otherwise
74 // the master would go away too (EOF). When the user connects to
75 // the port we then close the slave fd so that we get EOF when she
76 // disconnects. got that?
77
78 if (openpty(&master_fd,
79 &slave_fd, NULL, NULL, NULL) != 0)
80 return;
81
82 debuglog(("openpty: master_fd=%d, slave_fd=%d\n", master_fd, slave_fd));
83
84 // For ports with no service name (ie on DS90L servers)
85 // send a request for the service if we are queued so that
86 // by the time the user comes to use this port, we know about it.
87 if (service == "")
88 {
89 debuglog(("Dummy service NODE: %s\n", remnode.c_str()));
90 LATServer::Instance()->add_slave_node(remnode.c_str());
91 // CC ??? wot's this ?? LATServer::Instance()->send_enq(remnode.c_str());
92 }
93
94 if (service == "")
95 LATServer::Instance()->send_enq(remnode.c_str());
96
97 // Set terminal characteristics
98 struct termios tio;
99 tcgetattr(master_fd, &tio);
100 tio.c_iflag |= IGNBRK|BRKINT;
101 tio.c_oflag &= ~ONLCR;
102 #ifdef OCRNL
103 tio.c_oflag &= ~OCRNL;
104 #endif
105 tio.c_iflag &= ~INLCR;
106 tio.c_iflag &= ~ICRNL;
107 tcsetattr(master_fd, TCSANOW, &tio);
108
109 strcpy(ptyname, ttyname(slave_fd));
110 slave_fd_open = true;
111
112 // Check for /dev/lat & create it if necessary
113 struct stat st;
114 if (stat(LAT_DIRECTORY, &st) == -1)
115 {
116 mkdir(LAT_DIRECTORY, 0755);
117 }
118 unlink(devname.c_str());
119 symlink(ptyname, devname.c_str());
120
121 // Make it non-blocking so we can poll it
122 fcntl(master_fd, F_SETFL, fcntl(master_fd, F_GETFL, 0) | O_NONBLOCK);
123
124 #ifdef HAVE_OPENPTY
125 // Set it owned by "lat" if it exists. We only do this for
126 // /dev/pts PTYs.
127 gid_t lat_group = LATServer::Instance()->get_lat_group();
128 if (lat_group)
129 {
130 chown(ptyname, 0, lat_group);
131 chmod(ptyname, 0660);
132 }
133 #endif
134
135 debuglog(("made symlink %s to %s\n", devname.c_str(), ptyname));
136 LATServer::Instance()->add_pty(this, master_fd);
137 connected = false;
138 return;
139 }
140
~LocalPort()141 LocalPort::~LocalPort()
142 {
143 }
144
close_and_delete()145 void LocalPort::close_and_delete()
146 {
147 if (slave_fd_open)
148 {
149 close (slave_fd);
150 slave_fd = -1;
151 slave_fd_open = false;
152 }
153 close (master_fd);
154 LATServer::Instance()->remove_fd(master_fd);
155 master_fd = -1;
156 unlink(devname.c_str());
157 }
158
159 // Disconnect the local PTY
restart_pty()160 void LocalPort::restart_pty()
161 {
162 debuglog(("LocalPort::restart_pty()\n"));
163 connected = false;
164
165 // Close it all down so the local side gets EOF
166 unlink(devname.c_str());
167
168 if (slave_fd_open) close (slave_fd);
169 close (master_fd);
170 LATServer::Instance()->set_fd_state(master_fd, true);
171 LATServer::Instance()->remove_fd(master_fd);
172
173 // Now open it all up again ready for a new connection
174 init_port();
175 }
176
177
178 // Remote end disconnects or EOF on local PTY
disconnect_session(int reason)179 void LocalPort::disconnect_session(int reason)
180 {
181 debuglog(("LocalPort::disconnect_session()\n"));
182 // If the reason was some sort of error then send it to
183 // the PTY
184 if (reason >= 1)
185 {
186 const char *msg = lat_messages::session_disconnect_msg(reason);
187 write(master_fd, msg, strlen(msg));
188 write(master_fd, "\n", 1);
189 }
190 LATServer::Instance()->set_fd_state(master_fd, true);
191 connected = false;
192 restart_pty();
193 return;
194 }
195
196
197 // Connect up the session
connect_session()198 bool LocalPort::connect_session()
199 {
200 debuglog(("localport::connect_session: master-fd = %d\n", master_fd));
201 return LATServer::Instance()->make_port_connection(master_fd, this,
202 service.c_str(), remnode.c_str(),
203 portname.c_str(), devname.c_str(),
204 password.c_str(), queued);
205 }
206
207
do_read()208 void LocalPort::do_read()
209 {
210 debuglog(("LocalPort::do_read(), connected: %d\n", connected));
211 if (!connected)
212 {
213 if (!connect_session())
214 {
215 debuglog(("LocalPort:: do_read disabling pty reads\n"));
216 // Disable reads on the PTY until we are connected (or it fails)
217 LATServer::Instance()->set_fd_state(master_fd, true);
218
219 close(slave_fd);
220 slave_fd_open = false;
221 connected = true;
222 }
223 else
224 {
225 // Service does not exist or we haven't heard of it yet.
226 restart_pty();
227 }
228 }
229 else
230 {
231 debuglog(("do_read() called for LocalPort on connected socket\n"));
232 // So stop it!
233 LATServer::Instance()->set_fd_state(master_fd, true);
234 }
235 }
236
237 // Show info for latcp
show_info(bool verbose,std::ostringstream & output)238 void LocalPort::show_info(bool verbose, std::ostringstream &output)
239 {
240 output.setf(std::ios::left, std::ios::adjustfield);
241
242 output.width(23);
243 output << devname.c_str() << " ";
244
245 output.width(15);
246 output << service.c_str() << " ";
247
248 output.width(15);
249 output << remnode.c_str() << " ";
250
251 output.width(15);
252 output << portname.c_str() << " " << (queued?"Yes":"No ") << (clean?" 8":" ") << std::endl;
253 }
254