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# 22*e4d060fbSSam Falkner# Copyright 2010 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*e4d060fbSSam Falknerimport solaris.misc 35842727c2SChris Kirbyimport zfs.util 36842727c2SChris Kirbyimport zfs.ioctl 37842727c2SChris Kirbyimport zfs.dataset 38842727c2SChris Kirbyimport zfs.table 3914843421SMatthew Ahrens 4014843421SMatthew Ahrens_ = zfs.util._ 4114843421SMatthew Ahrens 4214843421SMatthew Ahrens# map from property name prefix -> (field name, isgroup) 4314843421SMatthew Ahrensprops = { 4414843421SMatthew Ahrens "userused@": ("used", False), 4514843421SMatthew Ahrens "userquota@": ("quota", False), 4614843421SMatthew Ahrens "groupused@": ("used", True), 4714843421SMatthew Ahrens "groupquota@": ("quota", True), 4814843421SMatthew Ahrens} 4914843421SMatthew Ahrens 5014843421SMatthew Ahrensdef skiptype(options, prop): 5114843421SMatthew Ahrens """Return True if this property (eg "userquota@") should be skipped.""" 5214843421SMatthew Ahrens (field, isgroup) = props[prop] 5314843421SMatthew Ahrens if field not in options.fields: 5414843421SMatthew Ahrens return True 5514843421SMatthew Ahrens if isgroup and "posixgroup" not in options.types and \ 5614843421SMatthew Ahrens "smbgroup" not in options.types: 5714843421SMatthew Ahrens return True 5814843421SMatthew Ahrens if not isgroup and "posixuser" not in options.types and \ 5914843421SMatthew Ahrens "smbuser" not in options.types: 6014843421SMatthew Ahrens return True 6114843421SMatthew Ahrens return False 6214843421SMatthew Ahrens 6314843421SMatthew Ahrensdef new_entry(options, isgroup, domain, rid): 6414843421SMatthew Ahrens """Return a dict("field": value) for this domain (string) + rid (int)""" 6514843421SMatthew Ahrens 6614843421SMatthew Ahrens if domain: 6714843421SMatthew Ahrens idstr = "%s-%u" % (domain, rid) 6814843421SMatthew Ahrens else: 6914843421SMatthew Ahrens idstr = "%u" % rid 7014843421SMatthew Ahrens 7114843421SMatthew Ahrens (typename, mapfunc) = { 72*e4d060fbSSam Falkner (1, 1): ("SMB Group", lambda id: solaris.misc.sid_to_name(id, 0)), 7314843421SMatthew Ahrens (1, 0): ("POSIX Group", lambda id: grp.getgrgid(int(id)).gr_name), 74*e4d060fbSSam Falkner (0, 1): ("SMB User", lambda id: solaris.misc.sid_to_name(id, 1)), 7514843421SMatthew Ahrens (0, 0): ("POSIX User", lambda id: pwd.getpwuid(int(id)).pw_name) 7614843421SMatthew Ahrens }[isgroup, bool(domain)] 7714843421SMatthew Ahrens 7814843421SMatthew Ahrens if typename.lower().replace(" ", "") not in options.types: 7914843421SMatthew Ahrens return None 8014843421SMatthew Ahrens 8114843421SMatthew Ahrens v = dict() 8214843421SMatthew Ahrens v["type"] = typename 8314843421SMatthew Ahrens 8414843421SMatthew Ahrens # python's getpwuid/getgrgid is confused by ephemeral uids 8514843421SMatthew Ahrens if not options.noname and rid < 1<<31: 8614843421SMatthew Ahrens try: 8714843421SMatthew Ahrens v["name"] = mapfunc(idstr) 8814843421SMatthew Ahrens except KeyError: 8914843421SMatthew Ahrens pass 9014843421SMatthew Ahrens 9114843421SMatthew Ahrens if "name" not in v: 9214843421SMatthew Ahrens v["name"] = idstr 9314843421SMatthew Ahrens if not domain: 9414843421SMatthew Ahrens # it's just a number, so pad it with spaces so 9514843421SMatthew Ahrens # that it will sort numerically 9614843421SMatthew Ahrens v["name.sort"] = "%20d" % rid 9714843421SMatthew Ahrens # fill in default values 9814843421SMatthew Ahrens v["used"] = "0" 9914843421SMatthew Ahrens v["used.sort"] = 0 10014843421SMatthew Ahrens v["quota"] = "none" 10114843421SMatthew Ahrens v["quota.sort"] = 0 10214843421SMatthew Ahrens return v 10314843421SMatthew Ahrens 104842727c2SChris Kirbydef process_one_raw(acct, options, prop, elem): 105842727c2SChris Kirby """Update the acct dict to incorporate the 10614843421SMatthew Ahrens information from this elem from Dataset.userspace(prop).""" 10714843421SMatthew Ahrens 10814843421SMatthew Ahrens (domain, rid, value) = elem 10914843421SMatthew Ahrens (field, isgroup) = props[prop] 11014843421SMatthew Ahrens 11114843421SMatthew Ahrens if options.translate and domain: 11214843421SMatthew Ahrens try: 113*e4d060fbSSam Falkner rid = solaris.misc.sid_to_id("%s-%u" % (domain, rid), 11414843421SMatthew Ahrens not isgroup) 11514843421SMatthew Ahrens domain = None 11614843421SMatthew Ahrens except KeyError: 11714843421SMatthew Ahrens pass; 11814843421SMatthew Ahrens key = (isgroup, domain, rid) 11914843421SMatthew Ahrens 12014843421SMatthew Ahrens try: 12114843421SMatthew Ahrens v = acct[key] 12214843421SMatthew Ahrens except KeyError: 12314843421SMatthew Ahrens v = new_entry(options, isgroup, domain, rid) 12414843421SMatthew Ahrens if not v: 12514843421SMatthew Ahrens return 12614843421SMatthew Ahrens acct[key] = v 12714843421SMatthew Ahrens 12814843421SMatthew Ahrens # Add our value to an existing value, which may be present if 12914843421SMatthew Ahrens # options.translate is set. 13014843421SMatthew Ahrens value = v[field + ".sort"] = value + v[field + ".sort"] 13114843421SMatthew Ahrens 13214843421SMatthew Ahrens if options.parsable: 13314843421SMatthew Ahrens v[field] = str(value) 13414843421SMatthew Ahrens else: 13514843421SMatthew Ahrens v[field] = zfs.util.nicenum(value) 13614843421SMatthew Ahrens 13714843421SMatthew Ahrensdef do_userspace(): 13814843421SMatthew Ahrens """Implements the "zfs userspace" and "zfs groupspace" subcommands.""" 13914843421SMatthew Ahrens 14014843421SMatthew Ahrens def usage(msg=None): 14114843421SMatthew Ahrens parser.print_help() 14214843421SMatthew Ahrens if msg: 14314843421SMatthew Ahrens print 14414843421SMatthew Ahrens parser.exit("zfs: error: " + msg) 14514843421SMatthew Ahrens else: 14614843421SMatthew Ahrens parser.exit() 14714843421SMatthew Ahrens 14814843421SMatthew Ahrens if sys.argv[1] == "userspace": 14914843421SMatthew Ahrens defaulttypes = "posixuser,smbuser" 15014843421SMatthew Ahrens else: 15114843421SMatthew Ahrens defaulttypes = "posixgroup,smbgroup" 15214843421SMatthew Ahrens 15314843421SMatthew Ahrens fields = ("type", "name", "used", "quota") 154842727c2SChris Kirby rjustfields = ("used", "quota") 15514843421SMatthew Ahrens types = ("all", "posixuser", "smbuser", "posixgroup", "smbgroup") 15614843421SMatthew Ahrens 15714843421SMatthew Ahrens u = _("%s [-niHp] [-o field[,...]] [-sS field] ... \n") % sys.argv[1] 15814843421SMatthew Ahrens u += _(" [-t type[,...]] <filesystem|snapshot>") 15914843421SMatthew Ahrens parser = optparse.OptionParser(usage=u, prog="zfs") 16014843421SMatthew Ahrens 16114843421SMatthew Ahrens parser.add_option("-n", action="store_true", dest="noname", 16214843421SMatthew Ahrens help=_("Print numeric ID instead of user/group name")) 16314843421SMatthew Ahrens parser.add_option("-i", action="store_true", dest="translate", 16414843421SMatthew Ahrens help=_("translate SID to posix (possibly ephemeral) ID")) 16514843421SMatthew Ahrens parser.add_option("-H", action="store_true", dest="noheaders", 16614843421SMatthew Ahrens help=_("no headers, tab delimited output")) 16714843421SMatthew Ahrens parser.add_option("-p", action="store_true", dest="parsable", 16814843421SMatthew Ahrens help=_("exact (parsable) numeric output")) 16914843421SMatthew Ahrens parser.add_option("-o", dest="fields", metavar="field[,...]", 17014843421SMatthew Ahrens default="type,name,used,quota", 17114843421SMatthew Ahrens help=_("print only these fields (eg type,name,used,quota)")) 17214843421SMatthew Ahrens parser.add_option("-s", dest="sortfields", metavar="field", 17314843421SMatthew Ahrens type="choice", choices=fields, default=list(), 17414843421SMatthew Ahrens action="callback", callback=zfs.util.append_with_opt, 17514843421SMatthew Ahrens help=_("sort field")) 17614843421SMatthew Ahrens parser.add_option("-S", dest="sortfields", metavar="field", 17714843421SMatthew Ahrens type="choice", choices=fields, #-s sets the default 17814843421SMatthew Ahrens action="callback", callback=zfs.util.append_with_opt, 17914843421SMatthew Ahrens help=_("reverse sort field")) 18014843421SMatthew Ahrens parser.add_option("-t", dest="types", metavar="type[,...]", 18114843421SMatthew Ahrens default=defaulttypes, 18214843421SMatthew Ahrens help=_("print only these types (eg posixuser,smbuser,posixgroup,smbgroup,all)")) 18314843421SMatthew Ahrens 18414843421SMatthew Ahrens (options, args) = parser.parse_args(sys.argv[2:]) 18514843421SMatthew Ahrens if len(args) != 1: 18614843421SMatthew Ahrens usage(_("wrong number of arguments")) 18714843421SMatthew Ahrens dsname = args[0] 18814843421SMatthew Ahrens 18914843421SMatthew Ahrens options.fields = options.fields.split(",") 19014843421SMatthew Ahrens for f in options.fields: 19114843421SMatthew Ahrens if f not in fields: 19214843421SMatthew Ahrens usage(_("invalid field %s") % f) 19314843421SMatthew Ahrens 19414843421SMatthew Ahrens options.types = options.types.split(",") 19514843421SMatthew Ahrens for t in options.types: 19614843421SMatthew Ahrens if t not in types: 19714843421SMatthew Ahrens usage(_("invalid type %s") % t) 19814843421SMatthew Ahrens 19914843421SMatthew Ahrens if not options.sortfields: 20014843421SMatthew Ahrens options.sortfields = [("-s", "type"), ("-s", "name")] 20114843421SMatthew Ahrens 20214843421SMatthew Ahrens if "all" in options.types: 20314843421SMatthew Ahrens options.types = types[1:] 20414843421SMatthew Ahrens 20514843421SMatthew Ahrens ds = zfs.dataset.Dataset(dsname, types=("filesystem")) 20614843421SMatthew Ahrens 207*e4d060fbSSam Falkner if ds.getprop("zoned") and solaris.misc.isglobalzone(): 20814843421SMatthew Ahrens options.noname = True 20914843421SMatthew Ahrens 21014843421SMatthew Ahrens if not ds.getprop("useraccounting"): 21114843421SMatthew Ahrens print(_("Initializing accounting information on old filesystem, please wait...")) 21214843421SMatthew Ahrens ds.userspace_upgrade() 21314843421SMatthew Ahrens 21414843421SMatthew Ahrens # gather and process accounting information 215842727c2SChris Kirby # Due to -i, we need to keep a dict, so we can potentially add 216842727c2SChris Kirby # together the posix ID and SID's usage. Grr. 217842727c2SChris Kirby acct = dict() 21814843421SMatthew Ahrens for prop in props.keys(): 21914843421SMatthew Ahrens if skiptype(options, prop): 22014843421SMatthew Ahrens continue; 22114843421SMatthew Ahrens for elem in ds.userspace(prop): 222842727c2SChris Kirby process_one_raw(acct, options, prop, elem) 223842727c2SChris Kirby 22414843421SMatthew Ahrens def cmpkey(val): 22514843421SMatthew Ahrens l = list() 22614843421SMatthew Ahrens for (opt, field) in options.sortfields: 22714843421SMatthew Ahrens try: 22814843421SMatthew Ahrens n = val[field + ".sort"] 22914843421SMatthew Ahrens except KeyError: 23014843421SMatthew Ahrens n = val[field] 23114843421SMatthew Ahrens if opt == "-S": 23214843421SMatthew Ahrens # reverse sorting 23314843421SMatthew Ahrens try: 23414843421SMatthew Ahrens n = -n 23514843421SMatthew Ahrens except TypeError: 23614843421SMatthew Ahrens # it's a string; decompose it 23714843421SMatthew Ahrens # into an array of integers, 23814843421SMatthew Ahrens # each one the negative of that 23914843421SMatthew Ahrens # character 24014843421SMatthew Ahrens n = [-ord(c) for c in n] 24114843421SMatthew Ahrens l.append(n) 24214843421SMatthew Ahrens return l 24314843421SMatthew Ahrens 244842727c2SChris Kirby t = zfs.table.Table(options.fields, rjustfields) 245842727c2SChris Kirby for val in acct.itervalues(): 246842727c2SChris Kirby t.addline(cmpkey(val), val) 247842727c2SChris Kirby t.printme(not options.noheaders) 248