1*6d52f363SLori Alt#! /usr/bin/python2.6 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# 22*6d52f363SLori Alt# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 2314843421SMatthew Ahrens# 2414843421SMatthew Ahrens 2514843421SMatthew Ahrens"""This module implements the "zfs userspace" and "zfs groupspace" subcommands. 2614843421SMatthew AhrensThe only public interface is the zfs.userspace.do_userspace() function.""" 2714843421SMatthew Ahrens 2814843421SMatthew Ahrensimport optparse 2914843421SMatthew Ahrensimport sys 3014843421SMatthew Ahrensimport pwd 3114843421SMatthew Ahrensimport grp 3214843421SMatthew Ahrensimport errno 33e4d060fbSSam Falknerimport solaris.misc 34842727c2SChris Kirbyimport zfs.util 35842727c2SChris Kirbyimport zfs.ioctl 36842727c2SChris Kirbyimport zfs.dataset 37842727c2SChris 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) = { 71e4d060fbSSam Falkner (1, 1): ("SMB Group", lambda id: solaris.misc.sid_to_name(id, 0)), 7214843421SMatthew Ahrens (1, 0): ("POSIX Group", lambda id: grp.getgrgid(int(id)).gr_name), 73e4d060fbSSam Falkner (0, 1): ("SMB User", lambda id: solaris.misc.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 103842727c2SChris Kirbydef process_one_raw(acct, options, prop, elem): 104842727c2SChris 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: 112e4d060fbSSam Falkner rid = solaris.misc.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") 153842727c2SChris 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 206e4d060fbSSam Falkner if ds.getprop("zoned") and solaris.misc.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 214842727c2SChris Kirby # Due to -i, we need to keep a dict, so we can potentially add 215842727c2SChris Kirby # together the posix ID and SID's usage. Grr. 216842727c2SChris 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): 221842727c2SChris 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 243842727c2SChris Kirby t = zfs.table.Table(options.fields, rjustfields) 244842727c2SChris Kirby for val in acct.itervalues(): 245842727c2SChris Kirby t.addline(cmpkey(val), val) 246842727c2SChris Kirby t.printme(not options.noheaders) 247