#!/bin/sh -x

# This file is covered by the GNU General Public License.
# Copyright (C) 2012 Andrew V. Stepanov <stanv@altlinux.org>

# Set serial ports access settigs

# This file is part of alterator-ports-access package

# Turn on libshell verbose mode
verbose=1


# Require libshell
. shell-error
. shell-quote
. shell-config
. shell-var


# Config file
readonly CONFIG="/etc/alterator-ports-access.conf"
readonly USB_UDEV_RULE="/etc/udev/rules.d/40-alterator-ports-access.rules"


# Don't control serial ports
serial_control_off() {

  # Read present serial ports in system
  local ports=( $(udevadm info --path=/devices/platform/serial8250 --export-db --export | sed -n -e '/DEVPATH.*ttyS/ { /serial8250/d; s/^.*\///p }') )

  # Enable all serial ports
  for port in "${ports[@]}"; do
    verbose "Enable $port"
    setserial "/dev/$port" "uart" "16550A"
  done

}


# Apply rules for serial ports
serial_control_on() {

  # Include config file
  (
  . "$CONFIG"

  rule=""

  for port in "${SERIAL_ENABLED[@]}"; do
    verbose "Enable port $port"
    setserial "/dev/$port" "uart" "16550A"
  done

  for port in "${SERIAL_DISABLED[@]}"; do
    verbose "Disable port $port"
    setserial "/dev/$port" "uart" "none"
  done
  )

}


# Turn off USB control
usb_control_off() {
  cat << 'EOF' > "$USB_UDEV_RULE"
# This file is covered by the GNU General Public License,
# Copyright (C) 2012 Andrew V. Stepanov <stanv@altlinux.org>
# Part of alterator-ports-access
# Do not edit manually
# For more information see kernel/Documentation/usb/authorization.txt

ACTION!="add|change", GOTO="alterator_ports_access_end"
SUBSYSTEM!="usb", GOTO="alterator_ports_access_end"

ATTR{authorized_default}=="?*", ATTR{authorized_default}="1"
ATTR{authorized}=="?*", ATTR{authorized}="1"

LABEL="alterator_ports_access_end"
EOF
}


# Turn on USB control
usb_control_on() {

  tmpcfg="$(mktemp --tmpdir "alterator.XXXXXXX")"

  cat << 'EOF' > "$tmpcfg"
# This file is covered by the GNU General Public License,
# Copyright (C) 2012 Andrew V. Stepanov <stanv@altlinux.org>
# Part of alterator-ports-access
# Do not edit manually
# For more information see kernel/Documentation/usb/authorization.txt

ACTION!="add|change", GOTO="alterator_ports_access_end"
SUBSYSTEM!="usb", GOTO="alterator_ports_access_end"

# Always authorize USB hubs
ATTR{authorized}=="?*", ATTR{bDeviceClass}=="09", ATTR{authorized}="1", GOTO="alterator_ports_access_end"
ATTR{authorized}=="?*", ATTR{bInterfaceClass}=="09", ATTR{authorized}="1", GOTO="alterator_ports_access_end"


EOF

  # Include config file
  (
  . "$CONFIG"


  for i in "${USB_WHITE_LIST[@]}"; do
    rule="ATTR{authorized}==\"?*\", "
    add=

    vendor="$(echo "$i" | cut -d ';' -f1)"
    prodid="$(echo "$i" | cut -d ';' -f2)"
    serial="$(echo "$i" | cut -d ';' -f3)"

    if [ -n "$vendor" ]; then
      vendor="$(quote_shell "$vendor")"
      rule="$rule ATTR{idVendor}==\"$vendor\","
      add="yes"
    fi

    if [ -n "$prodid" ]; then
      prodid="$(quote_shell "$prodid")"
      rule="$rule ATTR{idProduct}==\"$prodid\","
      add="yes"
    fi

    if [ -n "$serial" ]; then
      serial="$(quote_shell "$serial")"
      rule="$rule ATTR{serial}==\"$serial\","
      add="yes"
    fi

    rule="$rule ATTR{authorized}=\"1\", GOTO=\"alterator_ports_access_end\""

    if shell_var_is_yes "$add"; then
      echo "$rule" >> "$tmpcfg"
      echo "$rule" | sed 's/ATTR{id/ATTRS{id/g;s/ATTR{serial}/ATTRS{serial}/g' >> "$tmpcfg"
    fi

  done

  # HID
  if shell_var_is_yes "$USB_ALLOW_HID"; then
  cat << 'EOF' >> "$tmpcfg"

# Un block device. Necessary step. Suppose device is already deactivated, so we need to look up his properties.
ATTR{authorized}=="?*", ATTR{authorized}="1"

# Allow HID
ATTR{authorized}=="?*", PROGRAM="alterator-ports-access", ATTR{authorized}="1", GOTO="alterator_ports_access_end"
EOF
  fi

  )


  cat << 'EOF' >> "$tmpcfg"

# Block all other USB devices
ATTR{authorized}=="?*", ATTR{authorized}="0", RUN="/bin/bash -c 'echo UNAUTHORIZED_DEVICE_INSERTED | logger -t devaccess -p crit'"

LABEL="alterator_ports_access_end"
EOF

if [ -f "$USB_UDEV_RULE" ]; then
  rm -f "$USB_UDEV_RULE"
fi

mv -f "$tmpcfg" "$USB_UDEV_RULE"

}


# ENTRY POINT


# http://wiki.bash-hackers.org/howto/getopts_tutorial
while getopts ":seuh" opt; do
  case $opt in
    u)
      verbose "Update USB ports configuration"
      USB="yes"
      ;;
    s)
      verbose "Update serial ports configuration"
      SERIAL="yes"
      ;;
    e)
      verbose "Disable USB control, enable all USB devices"
      USB_CTRL_OFF="yes"
      ;;
    h)
      verbose "Use: $PROG [-s] [-u] [-e]"
      verbose "-s   update serial ports status"
      verbose "-e   disable USB control without touch config, enable all USB devices"
      verbose "-u   update USB ports status"
      verbose "Config file: $CONFIG"
      exit
      ;;
    \?)
      fatal "Invalid option: -$OPTARG"
      ;;
  esac
done

# Test correct invocation
if [ -z "$USB" -a -z "$SERIAL" -a -z "$USB_CTRL_OFF" ]; then
  message "Use: $PROG -h for help."
  exit
fi

# Config file exist
if ! [ -f "$CONFIG" -a -r "$CONFIG" ]; then
  fatal "Can't find configuration file $CONFIG"
fi

# Include config file
. "$CONFIG"

# Configure USB ports
if shell_var_is_yes "$USB"; then
  verbose "Configure USB ports"

  if shell_var_is_yes "$USB_CONTROL"; then
    verbose "USB control is on"
    usb_control_on
  else
    verbose "USB control is off"
    usb_control_off
    USB_CTRL_OFF=yes
  fi

  # Apply new udev rules
  verbose "Apply authorized_default"
  udevadm trigger --verbose --subsystem-match="usb" --attr-match="authorized_default"
  verbose "Apply authorized"
  udevadm trigger --verbose --subsystem-match="usb" --attr-match="authorized"
fi

# Configure serial ports
if shell_var_is_yes "$SERIAL"; then
  verbose "Configure serial ports"

  if shell_var_is_yes "$SERIAL_CONTROL"; then
    verbose "Serial control is on"
    serial_control_on
  else
    verbose "Serial control is off"
    serial_control_off
  fi
fi


if shell_var_is_yes "$USB_CTRL_OFF"; then

  for i in $(find /sys -name authorized); do
    [ -n "${e##*usb*}" ] && continue
    echo 1 > "$i"
  done
  for dev in $(ls /sys/bus/usb/devices); do
	  echo $dev > /sys/bus/usb/drivers_probe
  done

fi

# vim: autoindent tabstop=2 shiftwidth=2 expandtab softtabstop=2 filetype=sh
