#!/usr/bin/python3

"""
Displays information about how network interfaces connect.

This script displays connection-method information about the unique
network interfaces it detects.  It displays output in the form:

: interface-name,(wired|wireless),MAC

Copyright (C) 2014  Nick Daly

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 3 of the License, 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, see <http://www.gnu.org/licenses/>.
"""

import subprocess
import sys


def execute(command):
    """Execute and return a command's stdout and stderr."""
    process = subprocess.Popen(command, stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)
    stdout, stderr = process.communicate()
    return stdout.decode(), stderr.decode()


def parse_interface_and_macs():
    """Associate all interfaces with their MAC addresses.

    Parse *ifconfig* output and record each interface's MAC address.
    Also, record the interfaces that share MAC addresses.
    """
    output, _ = execute(['/sbin/ifconfig', '-a'])

    interfaces = {}
    for line in output.splitlines():
        if not line.split() or line.startswith(' '):
            continue

        line = line.split()
        interface = line[0]
        mac = line[-1]

        interfaces[interface] = {'id': interface, 'mac': mac, 'type': None}

    return interfaces


def parse_connection_type(interfaces):
    """Identify and record which interfaces are wired and wireless.

    *iwconfig* returns wireless interfaces in *stdout* and wired
    interfaces in *stderr*.  It's quite strange.
    """
    output, error = execute(['/sbin/iwconfig'])
    parse_iwconfig(interfaces, 'wired', error)
    parse_iwconfig(interfaces, 'wireless', output)


def parse_iwconfig(interfaces, type_, lines):
    """Actually parse the *iwconfig* output.

    Each *iwconfig* line that identifies an interface starts with the
    interface's name and contains data about the networks supported or
    the line ~no wireless extensions.~, if the interface is a wired
    interface.

    *iwconfig* doesn't currently appear to display interface aliases,
    so we can use its output to filter out the aliases that don't
    refer to real, physical interfaces.
    """
    for line in lines.splitlines():
        if not line.split() or line.startswith(' '):
            continue

        interface = line.split()[0]
        interfaces[interface]['type'] = type_


def main():
    """Parse and print interfaces and their types."""
    try:
        interfaces = parse_interface_and_macs()
        parse_connection_type(interfaces)
    except OSError as exception:
        print('Command not found: ifconfig or iwconfig', file=sys.stderr)
        sys.exit(1)

    for interface in interfaces.values():
        if interface['type']:
            print('{id},{type},{mac}'.format(**interface))


if __name__ == "__main__":
    main()
