#! /usr/bin/env python # -*- coding: ISO-8859-15 -*- # PyKota Turn Key tool # # PyKota - Print Quotas for CUPS and LPRng # # (c) 2003, 2004, 2005 Jerome Alet # 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. # # $Id$ # # import sys import os import pwd import grp from pykota.tool import Tool, PyKotaToolError, crashed, N_ __doc__ = N_("""pkturnkey v%(__version__)s (c) %(__years__)s %(__author__)s A turn key tool for PyKota. When launched, this command will initialize PyKota's database with all existing print queues and some or all users. For now, no prices or limits are set, so printing is fully accounted for, but not limited. command line usage : pkturnkey [options] options : -v | --version Prints pkturnkey version number then exits. -h | --help Prints this message then exits. -d | --dousers Manages users accounts as well. -D | --dogroups Manages users groups as well. Implies -d | --dousers. -e | --emptygroups Includes empty groups. -f | --force Modifies the database instead of printing what it would do. -u | --uidmin uid Only adds users whose uid is greater than or equal to uid. You can pass an username there as well, and its uid will be used automatically. If not set, 0 will be used automatically. Implies -d | --dousers. -U | --uidmax uid Only adds users whose uid is lesser than or equal to uid. You can pass an username there as well, and its uid will be used automatically. If not set, a large value will be used automatically. Implies -d | --dousers. -g | --gidmin gid Only adds groups whose gid is greater than or equal to gid. You can pass a groupname there as well, and its gid will be used automatically. If not set, 0 will be used automatically. Implies -D | --dogroups. -G | --gidmax gid Only adds groups whose gid is lesser than or equal to gid. You can pass a groupname there as well, and its gid will be used automatically. If not set, a large value will be used automatically. Implies -D | --dogroups. examples : $ pkturnkey --dousers --uidmin jerome Will simulate the initialization of PyKota's database will all existing printers and print accounts for all users whose uid is greater than or equal to jerome's one. Won't manage any users group. To REALLY initialize the database instead of simulating it, please use the -f | --force command line switch. """) class PKTurnKey(Tool) : """A class for an initialization tool.""" def listPrinters(self) : """Returns a list of tuples (queuename, deviceuri) for all existing print queues.""" self.printInfo("Extracting all print queues.") result = os.popen("lpstat -v", "r") lines = result.readlines() result.close() printers = [] for line in lines : (begin, end) = line.split(':', 1) deviceuri = end.strip() queuename = begin.split()[-1] printers.append((queuename, deviceuri)) return printers def listUsers(self, uidmin, uidmax) : """Returns a list of users whose uids are between uidmin and uidmax.""" self.printInfo("Extracting all users whose uid is between %s and %s." % (uidmin, uidmax)) return [(entry[0], entry[3]) for entry in pwd.getpwall() if uidmin <= entry[2] <= uidmax] def listGroups(self, gidmin, gidmax, users) : """Returns a list of groups whose gids are between gidmin and gidmax.""" self.printInfo("Extracting all groups whose gid is between %s and %s." % (gidmin, gidmax)) groups = [(entry[0], entry[2], entry[3]) for entry in grp.getgrall() if gidmin <= entry[2] <= gidmax] gidusers = {} usersgid = {} for u in users : gidusers.setdefault(u[1], []).append(u[0]) usersgid.setdefault(u[0], []).append(u[1]) membership = {} for g in range(len(groups)) : (gname, gid, members) = groups[g] newmembers = {} for m in members : newmembers[m] = m try : usernames = gidusers[gid] except KeyError : pass else : for username in usernames : if not newmembers.has_key(username) : newmembers[username] = username for member in newmembers.keys() : if not usersgid.has_key(member) : del newmembers[member] membership[gname] = newmembers.keys() return membership def runCommand(self, command, dryrun) : """Launches an external command.""" self.printInfo("%s" % command) if not dryrun : os.system(command) def createPrinters(self, printers, dryrun=0) : """Creates all printers in PyKota's database.""" if printers : needswarning = [p[0] for p in printers if p[1].find("cupspykota") == -1] command = "pkprinters --add %s" % " ".join(['"%s"' % p[0] for p in printers]) self.runCommand(command, dryrun) for p in needswarning : self.printInfo(_("Printer %s is not managed by PyKota yet. Please modify printers.conf and restart CUPS.") % p, "warn") def createUsers(self, users, dryrun=0) : """Creates all users in PyKota's database.""" if users : command = "edpykota --add --noquota %s" % " ".join(['"%s"' % u for u in users]) self.runCommand(command, dryrun) def createGroups(self, groups, dryrun=0) : """Creates all groups in PyKota's database.""" if groups : commands = ["edpykota --groups --add --noquota %s" % " ".join(['"%s"' % g for g in groups.keys()])] revmembership = {} for (groupname, usernames) in groups.items() : for username in usernames : revmembership.setdefault(username, []).append(groupname) for (username, groupnames) in revmembership.items() : commands.append('edpykota --ingroups %s "%s"' % (",".join(['"%s"' % g for g in groupnames]), username)) for command in commands : self.runCommand(command, dryrun) def main(self, names, options) : """Intializes PyKota's database.""" if not self.config.isAdmin : raise PyKotaToolError, "%s : %s" % (pwd.getpwuid(os.geteuid())[0], _("You're not allowed to use this command.")) # TODO : names not used for now if not names : names = ["*"] self.printInfo(_("Please be patient...")) dryrun = not options["force"] if dryrun : self.printInfo(_("Don't worry, the database WILL NOT BE MODIFIED.")) else : self.printInfo(_("Please WORRY NOW, the database WILL BE MODIFIED.")) if options["dousers"] : if not options["uidmin"] : self.printInfo(_("System users will have a print account as well !"), "warn") uidmin = 0 else : try : uidmin = int(options["uidmin"]) except : try : uidmin = pwd.getpwnam(options["uidmin"])[2] except KeyError, msg : raise PyKotaToolError, _("Unknown username %s : %s") % (options["uidmin"], msg) if not options["uidmax"] : uidmax = sys.maxint else : try : uidmax = int(options["uidmax"]) except : try : uidmax = pwd.getpwnam(options["uidmax"])[2] except KeyError, msg : raise PyKotaToolError, _("Unknown username %s : %s") % (options["uidmax"], msg) if uidmin > uidmax : (uidmin, uidmax) = (uidmax, uidmin) users = self.listUsers(uidmin, uidmax) else : users = [] if options["dogroups"] : if not options["gidmin"] : self.printInfo(_("System groups will have a print account as well !"), "warn") gidmin = 0 else : try : gidmin = int(options["gidmin"]) except : try : gidmin = grp.getgrnam(options["gidmin"])[2] except KeyError, msg : raise PyKotaToolError, _("Unknown groupname %s : %s") % (options["gidmin"], msg) if not options["gidmax"] : gidmax = sys.maxint else : try : gidmax = int(options["gidmax"]) except : try : gidmax = grp.getgrnam(options["gidmax"])[2] except KeyError, msg : raise PyKotaToolError, _("Unknown groupname %s : %s") % (options["gidmax"], msg) if gidmin > gidmax : (gidmin, gidmax) = (gidmax, gidmin) groups = self.listGroups(gidmin, gidmax, users) if not options["emptygroups"] : for (groupname, members) in groups.items() : if not members : del groups[groupname] else : groups = [] printers = self.listPrinters() if printers : self.createPrinters(printers, dryrun) self.createUsers([entry[0] for entry in users], dryrun) self.createGroups(groups, dryrun) if dryrun : self.printInfo(_("Simulation terminated.")) else : self.printInfo(_("Database initialized !")) if __name__ == "__main__" : retcode = 0 try : short_options = "hvdDefu:U:g:G:" long_options = ["help", "version", "dousers", "dogroups", \ "emptygroups", "force", "uidmin=", "uidmax=", \ "gidmin=", "gidmax="] # Initializes the command line tool manager = PKTurnKey(doc=__doc__) manager.deferredInit() # parse and checks the command line (options, args) = manager.parseCommandline(sys.argv[1:], \ short_options, \ long_options, \ allownothing=1) # sets long options options["help"] = options["h"] or options["help"] options["version"] = options["v"] or options["version"] options["dousers"] = options["d"] or options["dousers"] options["dogroups"] = options["D"] or options["dogroups"] options["emptygroups"] = options["e"] or options["emptygroups"] options["force"] = options["f"] or options["force"] options["uidmin"] = options["u"] or options["uidmin"] options["uidmax"] = options["U"] or options["uidmax"] options["gidmin"] = options["g"] or options["gidmin"] options["gidmax"] = options["G"] or options["gidmax"] if options["uidmin"] or options["uidmax"] : if not options["dousers"] : manager.printInfo(_("The --uidmin or --uidmax command line option implies --dousers as well."), "warn") options["dousers"] = 1 if options["gidmin"] or options["gidmax"] : if not options["dogroups"] : manager.printInfo(_("The --gidmin or --gidmax command line option implies --dogroups as well."), "warn") options["dogroups"] = 1 if options["dogroups"] : if not options["dousers"] : manager.printInfo(_("The --dogroups command line option implies --dousers as well."), "warn") options["dousers"] = 1 if options["help"] : manager.display_usage_and_quit() elif options["version"] : manager.display_version_and_quit() else : retcode = manager.main(args, options) except KeyboardInterrupt : sys.stderr.write("\nInterrupted with Ctrl+C !\n") except SystemExit : pass except : try : manager.crashed("pkturnkey failed") except : crashed("pkturnkey failed") retcode = -1 try : manager.storage.close() except (TypeError, NameError, AttributeError) : pass sys.exit(retcode)