/****************************************************************************** (c) 2001-2004 Patrick Caulfield patrick@debian.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "lat.h" #include "utils.h" #include "session.h" #include "connection.h" #include "circuit.h" #include "latcpcircuit.h" #include "localport.h" #include "server.h" #include "lat_messages.h" LocalPort::LocalPort(unsigned char *_service, unsigned char *_portname, unsigned char *_devname, unsigned char *_remnode, bool _queued, bool _clean, unsigned char *_password): service((char*)_service), portname((char*)_portname), devname((char*)_devname), remnode((char*)_remnode), password((char*)_password), queued(_queued), clean(_clean), slave_fd_open(false), connected(false) { debuglog(("New local port %s\n", devname.c_str())); } LocalPort::LocalPort(const LocalPort &p) { service = p.service; portname = p.portname; devname = p.devname; remnode = p.remnode; password = p.password; queued = p.queued; clean = p.clean; slave_fd_open = p.slave_fd_open; connected = p.connected; strcpy(ptyname, p.ptyname); } void LocalPort::init_port() { // A quick word of explanation here. // We keep the slave fd open after openpty because otherwise // the master would go away too (EOF). When the user connects to // the port we then close the slave fd so that we get EOF when she // disconnects. got that? if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) != 0) return; debuglog(("openpty: master_fd=%d, slave_fd=%d\n", master_fd, slave_fd)); // For ports with no service name (ie on DS90L servers) // send a request for the service if we are queued so that // by the time the user comes to use this port, we know about it. if (service == "") { debuglog(("Dummy service NODE: %s\n", remnode.c_str())); LATServer::Instance()->add_slave_node(remnode.c_str()); // PJC ??? wot's this ?? LATServer::Instance()->send_enq(remnode.c_str()); } if (service == "") LATServer::Instance()->send_enq(remnode.c_str()); // Set terminal characteristics struct termios tio; tcgetattr(master_fd, &tio); tio.c_iflag |= IGNBRK|BRKINT; tio.c_oflag &= ~ONLCR; #ifdef OCRNL tio.c_oflag &= ~OCRNL; #endif tio.c_iflag &= ~INLCR; tio.c_iflag &= ~ICRNL; tcsetattr(master_fd, TCSANOW, &tio); strcpy(ptyname, ttyname(slave_fd)); slave_fd_open = true; // Check for /dev/lat & create it if necessary struct stat st; if (stat(LAT_DIRECTORY, &st) == -1) { mkdir(LAT_DIRECTORY, 0755); } unlink(devname.c_str()); symlink(ptyname, devname.c_str()); // Make it non-blocking so we can poll it fcntl(master_fd, F_SETFL, fcntl(master_fd, F_GETFL, 0) | O_NONBLOCK); #ifdef HAVE_OPENPTY // Set it owned by "lat" if it exists. We only do this for // /dev/pts PTYs. gid_t lat_group = LATServer::Instance()->get_lat_group(); if (lat_group) { chown(ptyname, 0, lat_group); chmod(ptyname, 0660); } #endif debuglog(("made symlink %s to %s\n", devname.c_str(), ptyname)); LATServer::Instance()->add_pty(this, master_fd); connected = false; return; } LocalPort::~LocalPort() { } void LocalPort::close_and_delete() { if (slave_fd_open) { close (slave_fd); slave_fd = -1; slave_fd_open = false; } close (master_fd); LATServer::Instance()->remove_fd(master_fd); master_fd = -1; unlink(devname.c_str()); } // Disconnect the local PTY void LocalPort::restart_pty() { debuglog(("LocalPort::restart_pty()\n")); connected = false; // Close it all down so the local side gets EOF unlink(devname.c_str()); if (slave_fd_open) close (slave_fd); close (master_fd); LATServer::Instance()->set_fd_state(master_fd, true); LATServer::Instance()->remove_fd(master_fd); // Now open it all up again ready for a new connection init_port(); } // Remote end disconnects or EOF on local PTY void LocalPort::disconnect_session(int reason) { debuglog(("LocalPort::disconnect_session()\n")); // If the reason was some sort of error then send it to // the PTY if (reason >= 1) { char *msg = lat_messages::session_disconnect_msg(reason); write(master_fd, msg, strlen(msg)); write(master_fd, "\n", 1); } LATServer::Instance()->set_fd_state(master_fd, true); connected = false; restart_pty(); return; } // Connect up the session bool LocalPort::connect_session() { debuglog(("localport::connect_session: master-fd = %d\n", master_fd)); return LATServer::Instance()->make_port_connection(master_fd, this, service.c_str(), remnode.c_str(), portname.c_str(), devname.c_str(), password.c_str(), queued); } void LocalPort::do_read() { debuglog(("LocalPort::do_read(), connected: %d\n", connected)); if (!connected) { if (!connect_session()) { debuglog(("LocalPort:: do_read disabling pty reads\n")); // Disable reads on the PTY until we are connected (or it fails) LATServer::Instance()->set_fd_state(master_fd, true); close(slave_fd); slave_fd_open = false; connected = true; } else { // Service does not exist or we haven't heard of it yet. restart_pty(); } } else { debuglog(("do_read() called for LocalPort on connected socket\n")); // So stop it! LATServer::Instance()->set_fd_state(master_fd, true); } } // Show info for latcp void LocalPort::show_info(bool verbose, std::ostringstream &output) { output.setf(std::ios::left, std::ios::adjustfield); output.width(23); output << devname.c_str() << " "; output.width(15); output << service.c_str() << " "; output.width(15); output << remnode.c_str() << " "; output.width(15); output << portname.c_str() << " " << (queued?"Yes":"No ") << (clean?" 8":" ") << std::endl; }