114843421SMatthew Ahrens#! /usr/bin/python2.4 214843421SMatthew Ahrens# 314843421SMatthew Ahrens# CDDL HEADER START 414843421SMatthew Ahrens# 514843421SMatthew Ahrens# The contents of this file are subject to the terms of the 614843421SMatthew Ahrens# Common Development and Distribution License (the "License"). 714843421SMatthew Ahrens# You may not use this file except in compliance with the License. 814843421SMatthew Ahrens# 914843421SMatthew Ahrens# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 1014843421SMatthew Ahrens# or http://www.opensolaris.org/os/licensing. 1114843421SMatthew Ahrens# See the License for the specific language governing permissions 1214843421SMatthew Ahrens# and limitations under the License. 1314843421SMatthew Ahrens# 1414843421SMatthew Ahrens# When distributing Covered Code, include this CDDL HEADER in each 1514843421SMatthew Ahrens# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1614843421SMatthew Ahrens# If applicable, add the following below this CDDL HEADER, with the 1714843421SMatthew Ahrens# fields enclosed by brackets "[]" replaced with your own identifying 1814843421SMatthew Ahrens# information: Portions Copyright [yyyy] [name of copyright owner] 1914843421SMatthew Ahrens# 2014843421SMatthew Ahrens# CDDL HEADER END 2114843421SMatthew Ahrens# 2214843421SMatthew Ahrens# Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2314843421SMatthew Ahrens# Use is subject to license terms. 2414843421SMatthew Ahrens# 2514843421SMatthew Ahrens 2614843421SMatthew Ahrens"""This module implements the "zfs userspace" and "zfs groupspace" subcommands. 2714843421SMatthew AhrensThe only public interface is the zfs.userspace.do_userspace() function.""" 2814843421SMatthew Ahrens 2914843421SMatthew Ahrensimport optparse 3014843421SMatthew Ahrensimport sys 3114843421SMatthew Ahrensimport pwd 3214843421SMatthew Ahrensimport grp 3314843421SMatthew Ahrensimport errno 34*842727c2SChris Kirbyimport zfs.util 35*842727c2SChris Kirbyimport zfs.ioctl 36*842727c2SChris Kirbyimport zfs.dataset 37*842727c2SChris Kirbyimport zfs.table 3814843421SMatthew Ahrens 3914843421SMatthew Ahrens_ = zfs.util._ 4014843421SMatthew Ahrens 4114843421SMatthew Ahrens# map from property name prefix -> (field name, isgroup) 4214843421SMatthew Ahrensprops = { 4314843421SMatthew Ahrens "userused@": ("used", False), 4414843421SMatthew Ahrens "userquota@": ("quota", False), 4514843421SMatthew Ahrens "groupused@": ("used", True), 4614843421SMatthew Ahrens "groupquota@": ("quota", True), 4714843421SMatthew Ahrens} 4814843421SMatthew Ahrens 4914843421SMatthew Ahrensdef skiptype(options, prop): 5014843421SMatthew Ahrens """Return True if this property (eg "userquota@") should be skipped.""" 5114843421SMatthew Ahrens (field, isgroup) = props[prop] 5214843421SMatthew Ahrens if field not in options.fields: 5314843421SMatthew Ahrens return True 5414843421SMatthew Ahrens if isgroup and "posixgroup" not in options.types and \ 5514843421SMatthew Ahrens "smbgroup" not in options.types: 5614843421SMatthew Ahrens return True 5714843421SMatthew Ahrens if not isgroup and "posixuser" not in options.types and \ 5814843421SMatthew Ahrens "smbuser" not in options.types: 5914843421SMatthew Ahrens return True 6014843421SMatthew Ahrens return False 6114843421SMatthew Ahrens 6214843421SMatthew Ahrensdef new_entry(options, isgroup, domain, rid): 6314843421SMatthew Ahrens """Return a dict("field": value) for this domain (string) + rid (int)""" 6414843421SMatthew Ahrens 6514843421SMatthew Ahrens if domain: 6614843421SMatthew Ahrens idstr = "%s-%u" % (domain, rid) 6714843421SMatthew Ahrens else: 6814843421SMatthew Ahrens idstr = "%u" % rid 6914843421SMatthew Ahrens 7014843421SMatthew Ahrens (typename, mapfunc) = { 7114843421SMatthew Ahrens (1, 1): ("SMB Group", lambda id: zfs.ioctl.sid_to_name(id, 0)), 7214843421SMatthew Ahrens (1, 0): ("POSIX Group", lambda id: grp.getgrgid(int(id)).gr_name), 7314843421SMatthew Ahrens (0, 1): ("SMB User", lambda id: zfs.ioctl.sid_to_name(id, 1)), 7414843421SMatthew Ahrens (0, 0): ("POSIX User", lambda id: pwd.getpwuid(int(id)).pw_name) 7514843421SMatthew Ahrens }[isgroup, bool(domain)] 7614843421SMatthew Ahrens 7714843421SMatthew Ahrens if typename.lower().replace(" ", "") not in options.types: 7814843421SMatthew Ahrens return None 7914843421SMatthew Ahrens 8014843421SMatthew Ahrens v = dict() 8114843421SMatthew Ahrens v["type"] = typename 8214843421SMatthew Ahrens 8314843421SMatthew Ahrens # python's getpwuid/getgrgid is confused by ephemeral uids 8414843421SMatthew Ahrens if not options.noname and rid < 1<<31: 8514843421SMatthew Ahrens try: 8614843421SMatthew Ahrens v["name"] = mapfunc(idstr) 8714843421SMatthew Ahrens except KeyError: 8814843421SMatthew Ahrens pass 8914843421SMatthew Ahrens 9014843421SMatthew Ahrens if "name" not in v: 9114843421SMatthew Ahrens v["name"] = idstr 9214843421SMatthew Ahrens if not domain: 9314843421SMatthew Ahrens # it's just a number, so pad it with spaces so 9414843421SMatthew Ahrens # that it will sort numerically 9514843421SMatthew Ahrens v["name.sort"] = "%20d" % rid 9614843421SMatthew Ahrens # fill in default values 9714843421SMatthew Ahrens v["used"] = "0" 9814843421SMatthew Ahrens v["used.sort"] = 0 9914843421SMatthew Ahrens v["quota"] = "none" 10014843421SMatthew Ahrens v["quota.sort"] = 0 10114843421SMatthew Ahrens return v 10214843421SMatthew Ahrens 103*842727c2SChris Kirbydef process_one_raw(acct, options, prop, elem): 104*842727c2SChris Kirby """Update the acct dict to incorporate the 10514843421SMatthew Ahrens information from this elem from Dataset.userspace(prop).""" 10614843421SMatthew Ahrens 10714843421SMatthew Ahrens (domain, rid, value) = elem 10814843421SMatthew Ahrens (field, isgroup) = props[prop] 10914843421SMatthew Ahrens 11014843421SMatthew Ahrens if options.translate and domain: 11114843421SMatthew Ahrens try: 11214843421SMatthew Ahrens rid = zfs.ioctl.sid_to_id("%s-%u" % (domain, rid), 11314843421SMatthew Ahrens not isgroup) 11414843421SMatthew Ahrens domain = None 11514843421SMatthew Ahrens except KeyError: 11614843421SMatthew Ahrens pass; 11714843421SMatthew Ahrens key = (isgroup, domain, rid) 11814843421SMatthew Ahrens 11914843421SMatthew Ahrens try: 12014843421SMatthew Ahrens v = acct[key] 12114843421SMatthew Ahrens except KeyError: 12214843421SMatthew Ahrens v = new_entry(options, isgroup, domain, rid) 12314843421SMatthew Ahrens if not v: 12414843421SMatthew Ahrens return 12514843421SMatthew Ahrens acct[key] = v 12614843421SMatthew Ahrens 12714843421SMatthew Ahrens # Add our value to an existing value, which may be present if 12814843421SMatthew Ahrens # options.translate is set. 12914843421SMatthew Ahrens value = v[field + ".sort"] = value + v[field + ".sort"] 13014843421SMatthew Ahrens 13114843421SMatthew Ahrens if options.parsable: 13214843421SMatthew Ahrens v[field] = str(value) 13314843421SMatthew Ahrens else: 13414843421SMatthew Ahrens v[field] = zfs.util.nicenum(value) 13514843421SMatthew Ahrens 13614843421SMatthew Ahrensdef do_userspace(): 13714843421SMatthew Ahrens """Implements the "zfs userspace" and "zfs groupspace" subcommands.""" 13814843421SMatthew Ahrens 13914843421SMatthew Ahrens def usage(msg=None): 14014843421SMatthew Ahrens parser.print_help() 14114843421SMatthew Ahrens if msg: 14214843421SMatthew Ahrens print 14314843421SMatthew Ahrens parser.exit("zfs: error: " + msg) 14414843421SMatthew Ahrens else: 14514843421SMatthew Ahrens parser.exit() 14614843421SMatthew Ahrens 14714843421SMatthew Ahrens if sys.argv[1] == "userspace": 14814843421SMatthew Ahrens defaulttypes = "posixuser,smbuser" 14914843421SMatthew Ahrens else: 15014843421SMatthew Ahrens defaulttypes = "posixgroup,smbgroup" 15114843421SMatthew Ahrens 15214843421SMatthew Ahrens fields = ("type", "name", "used", "quota") 153*842727c2SChris Kirby rjustfields = ("used", "quota") 15414843421SMatthew Ahrens types = ("all", "posixuser", "smbuser", "posixgroup", "smbgroup") 15514843421SMatthew Ahrens 15614843421SMatthew Ahrens u = _("%s [-niHp] [-o field[,...]] [-sS field] ... \n") % sys.argv[1] 15714843421SMatthew Ahrens u += _(" [-t type[,...]] <filesystem|snapshot>") 15814843421SMatthew Ahrens parser = optparse.OptionParser(usage=u, prog="zfs") 15914843421SMatthew Ahrens 16014843421SMatthew Ahrens parser.add_option("-n", action="store_true", dest="noname", 16114843421SMatthew Ahrens help=_("Print numeric ID instead of user/group name")) 16214843421SMatthew Ahrens parser.add_option("-i", action="store_true", dest="translate", 16314843421SMatthew Ahrens help=_("translate SID to posix (possibly ephemeral) ID")) 16414843421SMatthew Ahrens parser.add_option("-H", action="store_true", dest="noheaders", 16514843421SMatthew Ahrens help=_("no headers, tab delimited output")) 16614843421SMatthew Ahrens parser.add_option("-p", action="store_true", dest="parsable", 16714843421SMatthew Ahrens help=_("exact (parsable) numeric output")) 16814843421SMatthew Ahrens parser.add_option("-o", dest="fields", metavar="field[,...]", 16914843421SMatthew Ahrens default="type,name,used,quota", 17014843421SMatthew Ahrens help=_("print only these fields (eg type,name,used,quota)")) 17114843421SMatthew Ahrens parser.add_option("-s", dest="sortfields", metavar="field", 17214843421SMatthew Ahrens type="choice", choices=fields, default=list(), 17314843421SMatthew Ahrens action="callback", callback=zfs.util.append_with_opt, 17414843421SMatthew Ahrens help=_("sort field")) 17514843421SMatthew Ahrens parser.add_option("-S", dest="sortfields", metavar="field", 17614843421SMatthew Ahrens type="choice", choices=fields, #-s sets the default 17714843421SMatthew Ahrens action="callback", callback=zfs.util.append_with_opt, 17814843421SMatthew Ahrens help=_("reverse sort field")) 17914843421SMatthew Ahrens parser.add_option("-t", dest="types", metavar="type[,...]", 18014843421SMatthew Ahrens default=defaulttypes, 18114843421SMatthew Ahrens help=_("print only these types (eg posixuser,smbuser,posixgroup,smbgroup,all)")) 18214843421SMatthew Ahrens 18314843421SMatthew Ahrens (options, args) = parser.parse_args(sys.argv[2:]) 18414843421SMatthew Ahrens if len(args) != 1: 18514843421SMatthew Ahrens usage(_("wrong number of arguments")) 18614843421SMatthew Ahrens dsname = args[0] 18714843421SMatthew Ahrens 18814843421SMatthew Ahrens options.fields = options.fields.split(",") 18914843421SMatthew Ahrens for f in options.fields: 19014843421SMatthew Ahrens if f not in fields: 19114843421SMatthew Ahrens usage(_("invalid field %s") % f) 19214843421SMatthew Ahrens 19314843421SMatthew Ahrens options.types = options.types.split(",") 19414843421SMatthew Ahrens for t in options.types: 19514843421SMatthew Ahrens if t not in types: 19614843421SMatthew Ahrens usage(_("invalid type %s") % t) 19714843421SMatthew Ahrens 19814843421SMatthew Ahrens if not options.sortfields: 19914843421SMatthew Ahrens options.sortfields = [("-s", "type"), ("-s", "name")] 20014843421SMatthew Ahrens 20114843421SMatthew Ahrens if "all" in options.types: 20214843421SMatthew Ahrens options.types = types[1:] 20314843421SMatthew Ahrens 20414843421SMatthew Ahrens ds = zfs.dataset.Dataset(dsname, types=("filesystem")) 20514843421SMatthew Ahrens 20614843421SMatthew Ahrens if ds.getprop("zoned") and zfs.ioctl.isglobalzone(): 20714843421SMatthew Ahrens options.noname = True 20814843421SMatthew Ahrens 20914843421SMatthew Ahrens if not ds.getprop("useraccounting"): 21014843421SMatthew Ahrens print(_("Initializing accounting information on old filesystem, please wait...")) 21114843421SMatthew Ahrens ds.userspace_upgrade() 21214843421SMatthew Ahrens 21314843421SMatthew Ahrens # gather and process accounting information 214*842727c2SChris Kirby # Due to -i, we need to keep a dict, so we can potentially add 215*842727c2SChris Kirby # together the posix ID and SID's usage. Grr. 216*842727c2SChris Kirby acct = dict() 21714843421SMatthew Ahrens for prop in props.keys(): 21814843421SMatthew Ahrens if skiptype(options, prop): 21914843421SMatthew Ahrens continue; 22014843421SMatthew Ahrens for elem in ds.userspace(prop): 221*842727c2SChris Kirby process_one_raw(acct, options, prop, elem) 22214843421SMatthew Ahrens 22314843421SMatthew Ahrens def cmpkey(val): 22414843421SMatthew Ahrens l = list() 22514843421SMatthew Ahrens for (opt, field) in options.sortfields: 22614843421SMatthew Ahrens try: 22714843421SMatthew Ahrens n = val[field + ".sort"] 22814843421SMatthew Ahrens except KeyError: 22914843421SMatthew Ahrens n = val[field] 23014843421SMatthew Ahrens if opt == "-S": 23114843421SMatthew Ahrens # reverse sorting 23214843421SMatthew Ahrens try: 23314843421SMatthew Ahrens n = -n 23414843421SMatthew Ahrens except TypeError: 23514843421SMatthew Ahrens # it's a string; decompose it 23614843421SMatthew Ahrens # into an array of integers, 23714843421SMatthew Ahrens # each one the negative of that 23814843421SMatthew Ahrens # character 23914843421SMatthew Ahrens n = [-ord(c) for c in n] 24014843421SMatthew Ahrens l.append(n) 24114843421SMatthew Ahrens return l 24214843421SMatthew Ahrens 243*842727c2SChris Kirby t = zfs.table.Table(options.fields, rjustfields) 244*842727c2SChris Kirby for val in acct.itervalues(): 245*842727c2SChris Kirby t.addline(cmpkey(val), val) 246*842727c2SChris Kirby t.printme(not options.noheaders) 247