12fb4439dSAlexander Pyhalov#!@PYTHON@ 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# 226d52f363SLori Alt# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 23*d561bb99SAndy Fiddaman# Copyright 2018 OmniOS Community Edition (OmniOSce) Association. 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 34e4d060fbSSam 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) = { 72e4d060fbSSam 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), 74e4d060fbSSam 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: 113e4d060fbSSam 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 207e4d060fbSSam 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) 22314843421SMatthew Ahrens 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) 245*d561bb99SAndy Fiddaman for val in acct.values(): 246842727c2SChris Kirby t.addline(cmpkey(val), val) 247842727c2SChris Kirby t.printme(not options.noheaders) 248