# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.

"""
Server-side of an example for sending file descriptors between processes over
UNIX sockets.  This server accepts connections on a UNIX socket and sends one
file descriptor to them, along with the name of the file it is associated with.

To run this example, run this program with two arguments: a path giving a UNIX
socket to listen on (must not exist) and a path to a file to send to clients
which connect (must exist).  For example:

    $ python sendfd.py /tmp/sendfd.sock /etc/motd

It will listen for client connections until stopped (eg, using Control-C).  Most
interesting behavior happens on the client side.

See recvfd.py for the client side of this example.
"""

if __name__ == "__main__":
    import sendfd

    raise SystemExit(sendfd.main())

import sys

from twisted.internet import reactor
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineOnlyReceiver
from twisted.python.filepath import FilePath
from twisted.python.log import startLogging


class SendFDProtocol(LineOnlyReceiver):
    def connectionMade(self):
        # Open the desired file and keep a reference to it - keeping it open
        # until we know the other side has it.  Closing it early will prevent
        # it from actually being sent.
        self.fObj = self.factory.content.open()

        # Tell the transport to send it.  It is not necessarily sent when this
        # method returns.  The reactor may need to run for a while longer before
        # that happens.
        self.transport.sendFileDescriptor(self.fObj.fileno())

        # Send along *at least* one byte, since one file descriptor was sent.
        # In this case, send along the name of the file to let the other side
        # have some idea what they're getting.
        encoding = sys.getfilesystemencoding()
        self.sendLine(self.factory.content.path.encode(encoding))

        # Give the other side a minute to deal with this.  If they don't close
        # the connection by then, we will do it for them.
        self.timeoutCall = reactor.callLater(60, self.transport.loseConnection)

    def connectionLost(self, reason):
        # Clean up the file object, it is no longer needed.
        self.fObj.close()
        self.fObj = None

        # Clean up the timeout, if necessary.
        if self.timeoutCall.active():
            self.timeoutCall.cancel()
            self.timeoutCall = None


def main():
    address = FilePath(sys.argv[1])
    content = FilePath(sys.argv[2])

    if address.exists():
        raise SystemExit("Cannot listen on an existing path")

    if not content.isfile():
        raise SystemExit("Content file must exist")

    startLogging(sys.stdout)

    serverFactory = Factory()
    serverFactory.content = content
    serverFactory.protocol = SendFDProtocol

    reactor.listenUNIX(address.path, serverFactory)
    reactor.run()
