1*10b9d77bSPawel Jakub Dawidek#! /usr/bin/python2.6 28fc25799SMartin Matuska# 38fc25799SMartin Matuska# CDDL HEADER START 48fc25799SMartin Matuska# 58fc25799SMartin Matuska# The contents of this file are subject to the terms of the 68fc25799SMartin Matuska# Common Development and Distribution License (the "License"). 78fc25799SMartin Matuska# You may not use this file except in compliance with the License. 88fc25799SMartin Matuska# 98fc25799SMartin Matuska# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 108fc25799SMartin Matuska# or http://www.opensolaris.org/os/licensing. 118fc25799SMartin Matuska# See the License for the specific language governing permissions 128fc25799SMartin Matuska# and limitations under the License. 138fc25799SMartin Matuska# 148fc25799SMartin Matuska# When distributing Covered Code, include this CDDL HEADER in each 158fc25799SMartin Matuska# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 168fc25799SMartin Matuska# If applicable, add the following below this CDDL HEADER, with the 178fc25799SMartin Matuska# fields enclosed by brackets "[]" replaced with your own identifying 188fc25799SMartin Matuska# information: Portions Copyright [yyyy] [name of copyright owner] 198fc25799SMartin Matuska# 208fc25799SMartin Matuska# CDDL HEADER END 218fc25799SMartin Matuska# 22*10b9d77bSPawel Jakub Dawidek# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 238fc25799SMartin Matuska# 248fc25799SMartin Matuska 258fc25799SMartin Matuska"""This module implements the "zfs userspace" and "zfs groupspace" subcommands. 268fc25799SMartin MatuskaThe only public interface is the zfs.userspace.do_userspace() function.""" 278fc25799SMartin Matuska 288fc25799SMartin Matuskaimport optparse 298fc25799SMartin Matuskaimport sys 308fc25799SMartin Matuskaimport pwd 318fc25799SMartin Matuskaimport grp 328fc25799SMartin Matuskaimport errno 33*10b9d77bSPawel Jakub Dawidekimport solaris.misc 34*10b9d77bSPawel Jakub Dawidekimport zfs.util 35*10b9d77bSPawel Jakub Dawidekimport zfs.ioctl 36*10b9d77bSPawel Jakub Dawidekimport zfs.dataset 37*10b9d77bSPawel Jakub Dawidekimport zfs.table 388fc25799SMartin Matuska 398fc25799SMartin Matuska_ = zfs.util._ 408fc25799SMartin Matuska 418fc25799SMartin Matuska# map from property name prefix -> (field name, isgroup) 428fc25799SMartin Matuskaprops = { 438fc25799SMartin Matuska "userused@": ("used", False), 448fc25799SMartin Matuska "userquota@": ("quota", False), 458fc25799SMartin Matuska "groupused@": ("used", True), 468fc25799SMartin Matuska "groupquota@": ("quota", True), 478fc25799SMartin Matuska} 488fc25799SMartin Matuska 498fc25799SMartin Matuskadef skiptype(options, prop): 508fc25799SMartin Matuska """Return True if this property (eg "userquota@") should be skipped.""" 518fc25799SMartin Matuska (field, isgroup) = props[prop] 528fc25799SMartin Matuska if field not in options.fields: 538fc25799SMartin Matuska return True 548fc25799SMartin Matuska if isgroup and "posixgroup" not in options.types and \ 558fc25799SMartin Matuska "smbgroup" not in options.types: 568fc25799SMartin Matuska return True 578fc25799SMartin Matuska if not isgroup and "posixuser" not in options.types and \ 588fc25799SMartin Matuska "smbuser" not in options.types: 598fc25799SMartin Matuska return True 608fc25799SMartin Matuska return False 618fc25799SMartin Matuska 628fc25799SMartin Matuskadef new_entry(options, isgroup, domain, rid): 638fc25799SMartin Matuska """Return a dict("field": value) for this domain (string) + rid (int)""" 648fc25799SMartin Matuska 658fc25799SMartin Matuska if domain: 668fc25799SMartin Matuska idstr = "%s-%u" % (domain, rid) 678fc25799SMartin Matuska else: 688fc25799SMartin Matuska idstr = "%u" % rid 698fc25799SMartin Matuska 708fc25799SMartin Matuska (typename, mapfunc) = { 71*10b9d77bSPawel Jakub Dawidek (1, 1): ("SMB Group", lambda id: solaris.misc.sid_to_name(id, 0)), 728fc25799SMartin Matuska (1, 0): ("POSIX Group", lambda id: grp.getgrgid(int(id)).gr_name), 73*10b9d77bSPawel Jakub Dawidek (0, 1): ("SMB User", lambda id: solaris.misc.sid_to_name(id, 1)), 748fc25799SMartin Matuska (0, 0): ("POSIX User", lambda id: pwd.getpwuid(int(id)).pw_name) 758fc25799SMartin Matuska }[isgroup, bool(domain)] 768fc25799SMartin Matuska 778fc25799SMartin Matuska if typename.lower().replace(" ", "") not in options.types: 788fc25799SMartin Matuska return None 798fc25799SMartin Matuska 808fc25799SMartin Matuska v = dict() 818fc25799SMartin Matuska v["type"] = typename 828fc25799SMartin Matuska 838fc25799SMartin Matuska # python's getpwuid/getgrgid is confused by ephemeral uids 848fc25799SMartin Matuska if not options.noname and rid < 1<<31: 858fc25799SMartin Matuska try: 868fc25799SMartin Matuska v["name"] = mapfunc(idstr) 878fc25799SMartin Matuska except KeyError: 888fc25799SMartin Matuska pass 898fc25799SMartin Matuska 908fc25799SMartin Matuska if "name" not in v: 918fc25799SMartin Matuska v["name"] = idstr 928fc25799SMartin Matuska if not domain: 938fc25799SMartin Matuska # it's just a number, so pad it with spaces so 948fc25799SMartin Matuska # that it will sort numerically 958fc25799SMartin Matuska v["name.sort"] = "%20d" % rid 968fc25799SMartin Matuska # fill in default values 978fc25799SMartin Matuska v["used"] = "0" 988fc25799SMartin Matuska v["used.sort"] = 0 998fc25799SMartin Matuska v["quota"] = "none" 1008fc25799SMartin Matuska v["quota.sort"] = 0 1018fc25799SMartin Matuska return v 1028fc25799SMartin Matuska 103*10b9d77bSPawel Jakub Dawidekdef process_one_raw(acct, options, prop, elem): 104*10b9d77bSPawel Jakub Dawidek """Update the acct dict to incorporate the 1058fc25799SMartin Matuska information from this elem from Dataset.userspace(prop).""" 1068fc25799SMartin Matuska 1078fc25799SMartin Matuska (domain, rid, value) = elem 1088fc25799SMartin Matuska (field, isgroup) = props[prop] 1098fc25799SMartin Matuska 1108fc25799SMartin Matuska if options.translate and domain: 1118fc25799SMartin Matuska try: 112*10b9d77bSPawel Jakub Dawidek rid = solaris.misc.sid_to_id("%s-%u" % (domain, rid), 1138fc25799SMartin Matuska not isgroup) 1148fc25799SMartin Matuska domain = None 1158fc25799SMartin Matuska except KeyError: 1168fc25799SMartin Matuska pass; 1178fc25799SMartin Matuska key = (isgroup, domain, rid) 1188fc25799SMartin Matuska 1198fc25799SMartin Matuska try: 1208fc25799SMartin Matuska v = acct[key] 1218fc25799SMartin Matuska except KeyError: 1228fc25799SMartin Matuska v = new_entry(options, isgroup, domain, rid) 1238fc25799SMartin Matuska if not v: 1248fc25799SMartin Matuska return 1258fc25799SMartin Matuska acct[key] = v 1268fc25799SMartin Matuska 1278fc25799SMartin Matuska # Add our value to an existing value, which may be present if 1288fc25799SMartin Matuska # options.translate is set. 1298fc25799SMartin Matuska value = v[field + ".sort"] = value + v[field + ".sort"] 1308fc25799SMartin Matuska 1318fc25799SMartin Matuska if options.parsable: 1328fc25799SMartin Matuska v[field] = str(value) 1338fc25799SMartin Matuska else: 1348fc25799SMartin Matuska v[field] = zfs.util.nicenum(value) 1358fc25799SMartin Matuska 1368fc25799SMartin Matuskadef do_userspace(): 1378fc25799SMartin Matuska """Implements the "zfs userspace" and "zfs groupspace" subcommands.""" 1388fc25799SMartin Matuska 1398fc25799SMartin Matuska def usage(msg=None): 1408fc25799SMartin Matuska parser.print_help() 1418fc25799SMartin Matuska if msg: 1428fc25799SMartin Matuska print 1438fc25799SMartin Matuska parser.exit("zfs: error: " + msg) 1448fc25799SMartin Matuska else: 1458fc25799SMartin Matuska parser.exit() 1468fc25799SMartin Matuska 1478fc25799SMartin Matuska if sys.argv[1] == "userspace": 1488fc25799SMartin Matuska defaulttypes = "posixuser,smbuser" 1498fc25799SMartin Matuska else: 1508fc25799SMartin Matuska defaulttypes = "posixgroup,smbgroup" 1518fc25799SMartin Matuska 1528fc25799SMartin Matuska fields = ("type", "name", "used", "quota") 153*10b9d77bSPawel Jakub Dawidek rjustfields = ("used", "quota") 1548fc25799SMartin Matuska types = ("all", "posixuser", "smbuser", "posixgroup", "smbgroup") 1558fc25799SMartin Matuska 1568fc25799SMartin Matuska u = _("%s [-niHp] [-o field[,...]] [-sS field] ... \n") % sys.argv[1] 1578fc25799SMartin Matuska u += _(" [-t type[,...]] <filesystem|snapshot>") 1588fc25799SMartin Matuska parser = optparse.OptionParser(usage=u, prog="zfs") 1598fc25799SMartin Matuska 1608fc25799SMartin Matuska parser.add_option("-n", action="store_true", dest="noname", 1618fc25799SMartin Matuska help=_("Print numeric ID instead of user/group name")) 1628fc25799SMartin Matuska parser.add_option("-i", action="store_true", dest="translate", 1638fc25799SMartin Matuska help=_("translate SID to posix (possibly ephemeral) ID")) 1648fc25799SMartin Matuska parser.add_option("-H", action="store_true", dest="noheaders", 1658fc25799SMartin Matuska help=_("no headers, tab delimited output")) 1668fc25799SMartin Matuska parser.add_option("-p", action="store_true", dest="parsable", 1678fc25799SMartin Matuska help=_("exact (parsable) numeric output")) 1688fc25799SMartin Matuska parser.add_option("-o", dest="fields", metavar="field[,...]", 1698fc25799SMartin Matuska default="type,name,used,quota", 1708fc25799SMartin Matuska help=_("print only these fields (eg type,name,used,quota)")) 1718fc25799SMartin Matuska parser.add_option("-s", dest="sortfields", metavar="field", 1728fc25799SMartin Matuska type="choice", choices=fields, default=list(), 1738fc25799SMartin Matuska action="callback", callback=zfs.util.append_with_opt, 1748fc25799SMartin Matuska help=_("sort field")) 1758fc25799SMartin Matuska parser.add_option("-S", dest="sortfields", metavar="field", 1768fc25799SMartin Matuska type="choice", choices=fields, #-s sets the default 1778fc25799SMartin Matuska action="callback", callback=zfs.util.append_with_opt, 1788fc25799SMartin Matuska help=_("reverse sort field")) 1798fc25799SMartin Matuska parser.add_option("-t", dest="types", metavar="type[,...]", 1808fc25799SMartin Matuska default=defaulttypes, 1818fc25799SMartin Matuska help=_("print only these types (eg posixuser,smbuser,posixgroup,smbgroup,all)")) 1828fc25799SMartin Matuska 1838fc25799SMartin Matuska (options, args) = parser.parse_args(sys.argv[2:]) 1848fc25799SMartin Matuska if len(args) != 1: 1858fc25799SMartin Matuska usage(_("wrong number of arguments")) 1868fc25799SMartin Matuska dsname = args[0] 1878fc25799SMartin Matuska 1888fc25799SMartin Matuska options.fields = options.fields.split(",") 1898fc25799SMartin Matuska for f in options.fields: 1908fc25799SMartin Matuska if f not in fields: 1918fc25799SMartin Matuska usage(_("invalid field %s") % f) 1928fc25799SMartin Matuska 1938fc25799SMartin Matuska options.types = options.types.split(",") 1948fc25799SMartin Matuska for t in options.types: 1958fc25799SMartin Matuska if t not in types: 1968fc25799SMartin Matuska usage(_("invalid type %s") % t) 1978fc25799SMartin Matuska 1988fc25799SMartin Matuska if not options.sortfields: 1998fc25799SMartin Matuska options.sortfields = [("-s", "type"), ("-s", "name")] 2008fc25799SMartin Matuska 2018fc25799SMartin Matuska if "all" in options.types: 2028fc25799SMartin Matuska options.types = types[1:] 2038fc25799SMartin Matuska 2048fc25799SMartin Matuska ds = zfs.dataset.Dataset(dsname, types=("filesystem")) 2058fc25799SMartin Matuska 206*10b9d77bSPawel Jakub Dawidek if ds.getprop("jailed") and solaris.misc.isglobalzone(): 2078fc25799SMartin Matuska options.noname = True 2088fc25799SMartin Matuska 2098fc25799SMartin Matuska if not ds.getprop("useraccounting"): 2108fc25799SMartin Matuska print(_("Initializing accounting information on old filesystem, please wait...")) 2118fc25799SMartin Matuska ds.userspace_upgrade() 2128fc25799SMartin Matuska 2138fc25799SMartin Matuska # gather and process accounting information 214*10b9d77bSPawel Jakub Dawidek # Due to -i, we need to keep a dict, so we can potentially add 215*10b9d77bSPawel Jakub Dawidek # together the posix ID and SID's usage. Grr. 216*10b9d77bSPawel Jakub Dawidek acct = dict() 2178fc25799SMartin Matuska for prop in props.keys(): 2188fc25799SMartin Matuska if skiptype(options, prop): 2198fc25799SMartin Matuska continue; 2208fc25799SMartin Matuska for elem in ds.userspace(prop): 221*10b9d77bSPawel Jakub Dawidek process_one_raw(acct, options, prop, elem) 2228fc25799SMartin Matuska 2238fc25799SMartin Matuska def cmpkey(val): 2248fc25799SMartin Matuska l = list() 2258fc25799SMartin Matuska for (opt, field) in options.sortfields: 2268fc25799SMartin Matuska try: 2278fc25799SMartin Matuska n = val[field + ".sort"] 2288fc25799SMartin Matuska except KeyError: 2298fc25799SMartin Matuska n = val[field] 2308fc25799SMartin Matuska if opt == "-S": 2318fc25799SMartin Matuska # reverse sorting 2328fc25799SMartin Matuska try: 2338fc25799SMartin Matuska n = -n 2348fc25799SMartin Matuska except TypeError: 2358fc25799SMartin Matuska # it's a string; decompose it 2368fc25799SMartin Matuska # into an array of integers, 2378fc25799SMartin Matuska # each one the negative of that 2388fc25799SMartin Matuska # character 2398fc25799SMartin Matuska n = [-ord(c) for c in n] 2408fc25799SMartin Matuska l.append(n) 2418fc25799SMartin Matuska return l 2428fc25799SMartin Matuska 243*10b9d77bSPawel Jakub Dawidek t = zfs.table.Table(options.fields, rjustfields) 244*10b9d77bSPawel Jakub Dawidek for val in acct.itervalues(): 245*10b9d77bSPawel Jakub Dawidek t.addline(cmpkey(val), val) 246*10b9d77bSPawel Jakub Dawidek t.printme(not options.noheaders) 247