#!/usr/bin/python # # Minimal Printserver, forwards a printer device to a tcp port (usually 9100) # # TODO: # * add read for bidirectional comm ? # * add writeonly opts # # Copyright 2006, Canonical Ltd. # # 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, or (at your option) 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. # # You should have received a copy of the GNU General Public License # along with this program. If not, you can find it on the World Wide # Web at http://www.gnu.org/copyleft/gpl.html, or write to the Free # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, # MA 02110-1301, USA. # #Serial redirection code adapted from work by: #(C)2002-2003 Chris Liechti #redirect data from a TCP/IP connection to a serial port and vice versa #requires Python 2.2 'cause socket.sendall is used # """ usage: jetpipe [options] Note: no security measures are implemeted. Anyone can remotely connect to this service over the network. Only one connection at once is supported. If the connection is terminaed it waits for the next connect. """ import os import sys import socket import serial import threading import getopt import resource # Resource usage information. class Redirector: def __init__(self, devicename, socket): self.socket = socket if devicename[:8] == '/dev/tty': # This should catch regular serial and USB serial self.device = serial.Serial(devicename) self.device.baudrate = 9600 self.device.bytesize = 8 self.device.parity = 'N' self.device.stopbits = 1 self.device.timeout = 1 #required so that the reader thread can exit self.device.rtscts = False self.device.xonxoff = False self.devicetype = 'S' else: self.device = open(devicename, 'wb') self.devicetype = 'P' def shortcut(self): """connect the serial port to the tcp port by copying everything from one side to the other""" self.writer() def writer(self): """loop forever and copy socket->serial""" print 'in writer loop' self.alive = True while self.alive: try: data = self.socket.recv(1024) if not data: break except socket.error, msg: print "error receiving from socket: ", msg try: self.device.write(data) except IOError: pass if self.devicetype == 'P': try: self.device.flush() # parallel device except IOError: pass self.device.close() self.alive = False if __name__ == '__main__': #parse command line options try: opts, args = getopt.getopt(sys.argv[1:], "hb:p:rs:xy:", ["help", "baud=", "rtscts", "xonxoff"]) except getopt.GetoptError: # print help information and exit: print >>sys.stderr, __doc__ sys.exit(2) for o, a in opts: if o in ("-h", "--help"): #help text usage() sys.exit() elif o in ("-b", "--baud"): #specified baudrate try: baudrate = int(a) except ValueError: raise ValueError, "Baudrate must be a integer number" elif o in ("-y", "--bytesize"): #specified bytesize bytesize = int(a) elif o in ("-p", "--parity"): #specified parity parity = a elif o in ("-s", "--stopbits"): #specified stopbits stopbits = int(a) elif o in ("-r", "--rtscts"): rtscts = True elif o in ("-x", "--xonxoff"): xonxoff = True devicename = args[0] port = args[1] # do the UNIX double-fork magic, see Stevens' "Advanced # Programming in the UNIX Environment" for details (ISBN 0201563177) # try: # do the UNIX double-fork magic, see Stevens' "Advanced try: pid = os.fork() if pid > 0: # exit first parent sys.exit(0) except OSError, e: print >>sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror) sys.exit(1) # decouple from parent environment os.chdir("/") os.setsid() os.umask(0) # do second fork try: pid = os.fork() if pid > 0: sys.exit(0) except OSError, e: print >>sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror) sys.exit(1) maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] if (maxfd == resource.RLIM_INFINITY): maxfd = MAXFD # Iterate through and close all file descriptors. for fd in range(0, maxfd): try: os.close(fd) except OSError: # ERROR, fd wasn't open to begin with (ignored) pass # Redirect the standard I/O file descriptors to the specified file. Since # the daemon has no controlling terminal, most daemons redirect stdin, # stdout, and stderr to /dev/null. This is done to prevent side-effects # from reads and writes to the standard I/O file descriptors. # This call to open is guaranteed to return the lowest file descriptor, # which will be 0 (stdin), since it was closed above. os.open('/dev/null', os.O_RDWR) # standard input (0) # Duplicate standard input to standard output and standard error. os.dup2(0, 1) # standard output (1) os.dup2(0, 2) # standard error (2) print devicename, port srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.bind( ('', int(port)) ) srv.listen(1) while 1: try: print "Waiting for connection...." connection, addr = srv.accept() print 'Connected by', addr #enter console->serial loop redir = Redirector(devicename, connection) if redir.devicetype == 'S': if locals().has_key('baudrate'): redir.device.baudrate = baudrate if locals().has_key('bytesize'): redir.device.bytesize = bytesize if locals().has_key('parity'): redir.device.parity = parity if locals().has_key('stopbits'): redir.device.stopbits = stopbits if locals().has_key('rtscts'): redir.device.rtscts = rtscts if locals().has_key('xonxoff'): redir.device.xonxoff = xonxoff try: redir.device.open() except serial.SerialException, e: print "Could not open serial port %s: %s" % (devicename.port, e) sys.exit(1) redir.shortcut() print 'Disconnected' connection.close() except socket.error, msg: print msg print "\n--- exit ---"