#!/usr/bin/python
"""
iproute2fs is a virtual filesystem that helps to monitor and
manage Linux network subsystem with simple UNIX tools like
`cat` or `echo`.
"""

import py9p
import sys
import getopt
import os

from threading import Thread
from cStringIO import StringIO

from cxnet.netlink.rtnl import RTNLGRP_IPV6_IFADDR
from cxnet.netlink.iproute2 import IpRoute2
from iproute2fs.vfs import Inode, Storage, v9fs
from iproute2fs.interface import InterfaceInode
from iproute2fs.routing import RoutingTableInode
from iproute2fs.playback import sync


# try to find out git version
__version__ = "0.1.1"
gitv = os.popen("git log -n 1 2>/dev/null")
commit = gitv.readline()
gitv.close()
if commit:
    __version__ += " git revision " + commit.split()[1]


class RootDir(Inode):
    """
    Root directory for iproute2fs
    """
    def __init__(self, storage):
        Inode.__init__(self, "/", self, qtype=py9p.DMDIR, storage=storage)
        self.storage = storage
        self.child_map = {
            "interfaces":   InterfacesDir,
            "routing":      RoutingTableDir,
            "README":       ReadmeInode,
            "version":      VersionInode,
        }

class ReadmeInode(Inode):
    """
    README file
    """
    data = StringIO("""
This filesystem exports rtnetlink(7) objects as files via 9P protocol.

Project repos:
    * http://projects.radlinux.org/cx/
    * https://github.com/svinota/iproute2fs

9P resources:
    * http://9p.cat-v.org/documentation/rfc/
    * https://github.com/svinota/py9p

""")

class VersionInode(Inode):
    """
    This class contains version info
    """
    data = StringIO(__version__)

class RoutingTableDir(Inode):
    """
    Representation of routing tables
    """
    def __init__(self, name, parent):
        Inode.__init__(self, name, parent, qtype=py9p.DMDIR)
        self.objects = parent.objects
        self.child_map = {
            "*":    RoutingTableInode,
        }

    def sync_children(self):
        return [ str(x) for x in self.objects['routing'].keys() ]

class InterfacesDir(Inode):
    """
    Directory, containing interface objects
    """
    def __init__(self, name, parent):
        Inode.__init__(self, name, parent, qtype=py9p.DMDIR)
        self.objects = parent.objects
        self.child_map = {
            "*":    InterfaceInode,
        }


    def sync_children(self):
        return [ x['dev'] for x in self.objects['links'].values() ]



def main(address, port, dbg, daemon):
    iproute2 = IpRoute2()
    iproute2.add_group(RTNLGRP_IPV6_IFADDR)
    storage = Storage(RootDir,iproute2)
    srv = py9p.Server(listen=(address, port), chatty=dbg, dotu=True)
    srv.mount(v9fs(storage))

    objects = {
        'links': {},
        'links-by-name': {},
        'routing': {},
    }

    # first, ask for all links, cause addresses and neighbors
    # are binded to links
    iproute2.get_all_links()
    iproute2.get_all_addrs()
    iproute2.get_all_neighbors()
    # then fill routing tables
    [ iproute2.get_all_routes(x) for x in range(256) ]
    storage.root.objects = objects
    storage.root.sync()

    s = Thread(target=sync, name="sync thread", args=(objects, iproute2, True))
    s.daemon = True
    s.start()

    srv.serve()


if __name__ == "__main__" :

    try:
        opt, args = getopt.getopt(sys.argv[1:], "Ddp:l:P:")
    except Exception, e:
        print(e)
        print("usage: [-Dd] [-P pidfile] [-p port] [-l address]")
        sys.exit(0)

    port = py9p.PORT
    address = 'localhost'
    pidfile = 'iproute2fs.pid'
    dbg = False
    daemon = False

    for i, k in opt:
        if i == "-d":
            daemon = True
        if i == "-D":
            dbg = True
        if i == "-P":
            pidfile = k
        if i == "-p":
            if k[0] == '0':
                port = int(k,8)
            else:
                port = int(k)
        if i == "-l":
            address = k

    if address[0] == '/' and port == py9p.PORT:
        port = 0600


    p = 0
    if daemon:
        p = os.fork()

    if p == 0:
        main(address, port, dbg, daemon)
    else:
        f = open(pidfile,"w")
        f.write(str(p))
        f.close()

