aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xstandardskriver185
1 files changed, 185 insertions, 0 deletions
diff --git a/standardskriver b/standardskriver
new file mode 100755
index 0000000..60f9f59
--- /dev/null
+++ b/standardskriver
@@ -0,0 +1,185 @@
+#!/usr/bin/python
+
+# Copyright (C) 2013, John Sigurd Skogtvedt <jss@linuxavdelingen.no>
+#
+# 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 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, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+# /etc/xdg/autostart/standardskriver.desktop eksempel:
+# [Desktop Entry]
+# Type=Application
+# Exec=standardskriver
+# Name=standardskriver
+# StartupNotify=false
+
+CUSTOMER_ID_FILE = '/etc/debian-edu/itzks.school'
+CFG_FILE = '/etc/standardskriver.cfg'
+CFG_FILE_EXAMPLE = '''[settings]
+enable = yes
+order = machine groups
+delete lpoptions = yes
+
+# MAC address or IP = printer
+# hostname = printer
+# hostname.domain = printer
+# LTSP client IP = printer
+# 10.1.0.0/16 = printer
+# (globbing works)
+
+[machine]
+00:01:02:03:04:05 = printer01
+172.16.34.64 = printer02
+hostname = printer01
+hostname.domain = printer02
+
+# group name = printer
+# (globbing works)
+
+[groups]
+group1 = printer01
+group2 = printer02
+'''
+from glob import glob
+from fnmatch import fnmatchcase
+
+import sys
+import os
+import subprocess
+import re
+from socket import gethostname, getfqdn
+import netaddr
+from optparse import OptionParser
+import ConfigParser
+
+# check whose customer system we are on...
+customer_id = None
+if os.access(CUSTOMER_ID_FILE, os.R_OK):
+ with open(CUSTOMER_ID_FILE, 'r') as c_id_f:
+ # only read first line
+ customer_id = c_id_f.readline()
+ # sanitize input...
+ customer_id = re.sub(r'([A-Z0-9]+)(.*\n)', r'\1', customer_id)
+
+macaddrs = [open(a).read().replace(':', '').strip().lower() for a in glob('/sys/class/net/*/address')]
+macaddrs = [a for a in macaddrs if a]
+
+parser = OptionParser()
+parser.add_option('-n', '--dryrun', action='store_true', help='only show what would be done')
+options, args = parser.parse_args()
+
+if not os.path.exists(CFG_FILE):
+ print >>sys.stderr, 'Configuration file %s is missing.' % CFG_FILE
+ print >>sys.stderr, 'To create it, redirect the following example to %s and edit the file.' % CFG_FILE
+ print CFG_FILE_EXAMPLE
+ sys.exit(1)
+
+cfg = ConfigParser.RawConfigParser()
+# hack: mac addrs contain :, which clashes with cfg syntax
+cfg.OPTCRE = re.compile(
+ r'(?P<option>[^=\s][^=]*)' # very permissive!
+ r'\s*(?P<vi>[=])\s*' # any number of space/tab,
+ # followed by separator
+ # (=), followed
+ # by any # space/tab
+ r'(?P<value>.*)$' # everything up to eol
+ )
+cfg.readfp(open(CFG_FILE, 'rb'))
+
+if cfg.get('settings', 'enable') != "yes":
+ sys.exit(0)
+
+for x in cfg.get('settings', 'order').split():
+ if not x in ('machine', 'groups'):
+ print 'invalid value {val} in settings/order'.format(val=x)
+ sys.exit(1)
+
+hostnames = []
+hostnames.append(gethostname())
+hostnames.append(getfqdn())
+
+re_ipaddr = re.compile(r'inet addr:(\S+)')
+ipaddrs = []
+try:
+ ipaddrs.append(os.environ['SSH_CLIENT'].split()[0])
+except KeyError:
+ pass
+p = subprocess.Popen(['/sbin/ifconfig'], env={'LANG': 'C'}, stdout=subprocess.PIPE)
+for line in p.stdout:
+ m = re_ipaddr.search(line)
+ if m:
+ ipaddrs.append(m.group(1))
+p.wait()
+
+p = subprocess.Popen(['id', '-Gn'], stdout=subprocess.PIPE)
+groups = p.stdout.read().split()
+p.wait()
+#print groups
+
+def get_group_match():
+ group_sections = ['groups']
+ if customer_id:
+ group_sections.append('groups.{customer}'.format(customer=customer_id))
+ for section in group_sections:
+ try:
+ for group, printer in cfg.items(section):
+ if group.strip('@') in groups: return printer
+ except ConfigParser.NoSectionError:
+ pass
+ return None
+
+def get_machine_match():
+ machine_sections = ['machine']
+ if customer_id:
+ machine_sections.append('machine.{customer}'.format(customer=customer_id))
+ for section in machine_sections:
+ try:
+ for machine, printer in cfg.items(section):
+ if any(fnmatchcase(macaddr, machine.replace('-', '').replace(':', '')) for macaddr in macaddrs):
+ return printer
+ elif any(fnmatchcase(hostname, machine) for hostname in hostnames):
+ return printer
+ else:
+ machines = netaddr.IPSet(machine.split(','))
+ myaddrs = netaddr.IPSet(ipaddrs)
+ if machines & myaddrs:
+ return printer
+ except ConfigParser.NoSectionError:
+ pass
+ return None
+
+matches = []
+for item in cfg.get('settings', 'order').split():
+ if item == 'machine': matches.append(get_machine_match())
+ elif item == 'groups': matches.append(get_group_match())
+ else: raise ValueError('%s is not machine or groups' % item)
+
+try:
+ printer = [x for x in matches if x][0]
+except IndexError: # no match
+ if cfg.getboolean('settings', 'delete lpoptions'):
+ lpoptions_filename = os.path.expanduser('~/.cups/lpoptions')
+ if options.dryrun:
+ print 'would delete %s' % lpoptions_filename
+ else:
+ try:
+ os.unlink(lpoptions_filename)
+ except OSError:
+ pass
+ sys.exit(0)
+
+args = ['lpoptions', '-d', printer]
+if options.dryrun:
+ print 'would call %s' % (' '.join(args))
+else:
+ subprocess.call(args)