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