1*14843421SMatthew Ahrens#! /usr/bin/python2.4 2*14843421SMatthew Ahrens# 3*14843421SMatthew Ahrens# CDDL HEADER START 4*14843421SMatthew Ahrens# 5*14843421SMatthew Ahrens# The contents of this file are subject to the terms of the 6*14843421SMatthew Ahrens# Common Development and Distribution License (the "License"). 7*14843421SMatthew Ahrens# You may not use this file except in compliance with the License. 8*14843421SMatthew Ahrens# 9*14843421SMatthew Ahrens# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*14843421SMatthew Ahrens# or http://www.opensolaris.org/os/licensing. 11*14843421SMatthew Ahrens# See the License for the specific language governing permissions 12*14843421SMatthew Ahrens# and limitations under the License. 13*14843421SMatthew Ahrens# 14*14843421SMatthew Ahrens# When distributing Covered Code, include this CDDL HEADER in each 15*14843421SMatthew Ahrens# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*14843421SMatthew Ahrens# If applicable, add the following below this CDDL HEADER, with the 17*14843421SMatthew Ahrens# fields enclosed by brackets "[]" replaced with your own identifying 18*14843421SMatthew Ahrens# information: Portions Copyright [yyyy] [name of copyright owner] 19*14843421SMatthew Ahrens# 20*14843421SMatthew Ahrens# CDDL HEADER END 21*14843421SMatthew Ahrens# 22*14843421SMatthew Ahrens# Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23*14843421SMatthew Ahrens# Use is subject to license terms. 24*14843421SMatthew Ahrens# 25*14843421SMatthew Ahrens 26*14843421SMatthew Ahrens"""This module implements the "zfs userspace" and "zfs groupspace" subcommands. 27*14843421SMatthew AhrensThe only public interface is the zfs.userspace.do_userspace() function.""" 28*14843421SMatthew Ahrens 29*14843421SMatthew Ahrensimport zfs.util 30*14843421SMatthew Ahrensimport zfs.ioctl 31*14843421SMatthew Ahrensimport zfs.dataset 32*14843421SMatthew Ahrensimport optparse 33*14843421SMatthew Ahrensimport sys 34*14843421SMatthew Ahrensimport pwd 35*14843421SMatthew Ahrensimport grp 36*14843421SMatthew Ahrensimport errno 37*14843421SMatthew Ahrens 38*14843421SMatthew Ahrens_ = zfs.util._ 39*14843421SMatthew Ahrens 40*14843421SMatthew Ahrens# map from property name prefix -> (field name, isgroup) 41*14843421SMatthew Ahrensprops = { 42*14843421SMatthew Ahrens "userused@": ("used", False), 43*14843421SMatthew Ahrens "userquota@": ("quota", False), 44*14843421SMatthew Ahrens "groupused@": ("used", True), 45*14843421SMatthew Ahrens "groupquota@": ("quota", True), 46*14843421SMatthew Ahrens} 47*14843421SMatthew Ahrens 48*14843421SMatthew Ahrensdef skiptype(options, prop): 49*14843421SMatthew Ahrens """Return True if this property (eg "userquota@") should be skipped.""" 50*14843421SMatthew Ahrens (field, isgroup) = props[prop] 51*14843421SMatthew Ahrens if field not in options.fields: 52*14843421SMatthew Ahrens return True 53*14843421SMatthew Ahrens if isgroup and "posixgroup" not in options.types and \ 54*14843421SMatthew Ahrens "smbgroup" not in options.types: 55*14843421SMatthew Ahrens return True 56*14843421SMatthew Ahrens if not isgroup and "posixuser" not in options.types and \ 57*14843421SMatthew Ahrens "smbuser" not in options.types: 58*14843421SMatthew Ahrens return True 59*14843421SMatthew Ahrens return False 60*14843421SMatthew Ahrens 61*14843421SMatthew Ahrensdef updatemax(d, k, v): 62*14843421SMatthew Ahrens d[k] = max(d.get(k, None), v) 63*14843421SMatthew Ahrens 64*14843421SMatthew Ahrensdef new_entry(options, isgroup, domain, rid): 65*14843421SMatthew Ahrens """Return a dict("field": value) for this domain (string) + rid (int)""" 66*14843421SMatthew Ahrens 67*14843421SMatthew Ahrens if domain: 68*14843421SMatthew Ahrens idstr = "%s-%u" % (domain, rid) 69*14843421SMatthew Ahrens else: 70*14843421SMatthew Ahrens idstr = "%u" % rid 71*14843421SMatthew Ahrens 72*14843421SMatthew Ahrens (typename, mapfunc) = { 73*14843421SMatthew Ahrens (1, 1): ("SMB Group", lambda id: zfs.ioctl.sid_to_name(id, 0)), 74*14843421SMatthew Ahrens (1, 0): ("POSIX Group", lambda id: grp.getgrgid(int(id)).gr_name), 75*14843421SMatthew Ahrens (0, 1): ("SMB User", lambda id: zfs.ioctl.sid_to_name(id, 1)), 76*14843421SMatthew Ahrens (0, 0): ("POSIX User", lambda id: pwd.getpwuid(int(id)).pw_name) 77*14843421SMatthew Ahrens }[isgroup, bool(domain)] 78*14843421SMatthew Ahrens 79*14843421SMatthew Ahrens if typename.lower().replace(" ", "") not in options.types: 80*14843421SMatthew Ahrens return None 81*14843421SMatthew Ahrens 82*14843421SMatthew Ahrens v = dict() 83*14843421SMatthew Ahrens v["type"] = typename 84*14843421SMatthew Ahrens 85*14843421SMatthew Ahrens # python's getpwuid/getgrgid is confused by ephemeral uids 86*14843421SMatthew Ahrens if not options.noname and rid < 1<<31: 87*14843421SMatthew Ahrens try: 88*14843421SMatthew Ahrens v["name"] = mapfunc(idstr) 89*14843421SMatthew Ahrens except KeyError: 90*14843421SMatthew Ahrens pass 91*14843421SMatthew Ahrens 92*14843421SMatthew Ahrens if "name" not in v: 93*14843421SMatthew Ahrens v["name"] = idstr 94*14843421SMatthew Ahrens if not domain: 95*14843421SMatthew Ahrens # it's just a number, so pad it with spaces so 96*14843421SMatthew Ahrens # that it will sort numerically 97*14843421SMatthew Ahrens v["name.sort"] = "%20d" % rid 98*14843421SMatthew Ahrens # fill in default values 99*14843421SMatthew Ahrens v["used"] = "0" 100*14843421SMatthew Ahrens v["used.sort"] = 0 101*14843421SMatthew Ahrens v["quota"] = "none" 102*14843421SMatthew Ahrens v["quota.sort"] = 0 103*14843421SMatthew Ahrens return v 104*14843421SMatthew Ahrens 105*14843421SMatthew Ahrensdef process_one_raw(acct, maxfieldlen, options, prop, elem): 106*14843421SMatthew Ahrens """Update the acct and maxfieldlen dicts to incorporate the 107*14843421SMatthew Ahrens information from this elem from Dataset.userspace(prop).""" 108*14843421SMatthew Ahrens 109*14843421SMatthew Ahrens (domain, rid, value) = elem 110*14843421SMatthew Ahrens (field, isgroup) = props[prop] 111*14843421SMatthew Ahrens 112*14843421SMatthew Ahrens if options.translate and domain: 113*14843421SMatthew Ahrens try: 114*14843421SMatthew Ahrens rid = zfs.ioctl.sid_to_id("%s-%u" % (domain, rid), 115*14843421SMatthew Ahrens not isgroup) 116*14843421SMatthew Ahrens domain = None 117*14843421SMatthew Ahrens except KeyError: 118*14843421SMatthew Ahrens pass; 119*14843421SMatthew Ahrens key = (isgroup, domain, rid) 120*14843421SMatthew Ahrens 121*14843421SMatthew Ahrens try: 122*14843421SMatthew Ahrens v = acct[key] 123*14843421SMatthew Ahrens except KeyError: 124*14843421SMatthew Ahrens v = new_entry(options, isgroup, domain, rid) 125*14843421SMatthew Ahrens if not v: 126*14843421SMatthew Ahrens return 127*14843421SMatthew Ahrens acct[key] = v 128*14843421SMatthew Ahrens 129*14843421SMatthew Ahrens # Add our value to an existing value, which may be present if 130*14843421SMatthew Ahrens # options.translate is set. 131*14843421SMatthew Ahrens value = v[field + ".sort"] = value + v[field + ".sort"] 132*14843421SMatthew Ahrens 133*14843421SMatthew Ahrens if options.parsable: 134*14843421SMatthew Ahrens v[field] = str(value) 135*14843421SMatthew Ahrens else: 136*14843421SMatthew Ahrens v[field] = zfs.util.nicenum(value) 137*14843421SMatthew Ahrens for k in v.keys(): 138*14843421SMatthew Ahrens # some of the .sort fields are integers, so have no len() 139*14843421SMatthew Ahrens if isinstance(v[k], str): 140*14843421SMatthew Ahrens updatemax(maxfieldlen, k, len(v[k])) 141*14843421SMatthew Ahrens 142*14843421SMatthew Ahrensdef do_userspace(): 143*14843421SMatthew Ahrens """Implements the "zfs userspace" and "zfs groupspace" subcommands.""" 144*14843421SMatthew Ahrens 145*14843421SMatthew Ahrens def usage(msg=None): 146*14843421SMatthew Ahrens parser.print_help() 147*14843421SMatthew Ahrens if msg: 148*14843421SMatthew Ahrens print 149*14843421SMatthew Ahrens parser.exit("zfs: error: " + msg) 150*14843421SMatthew Ahrens else: 151*14843421SMatthew Ahrens parser.exit() 152*14843421SMatthew Ahrens 153*14843421SMatthew Ahrens if sys.argv[1] == "userspace": 154*14843421SMatthew Ahrens defaulttypes = "posixuser,smbuser" 155*14843421SMatthew Ahrens else: 156*14843421SMatthew Ahrens defaulttypes = "posixgroup,smbgroup" 157*14843421SMatthew Ahrens 158*14843421SMatthew Ahrens fields = ("type", "name", "used", "quota") 159*14843421SMatthew Ahrens ljustfields = ("type", "name") 160*14843421SMatthew Ahrens types = ("all", "posixuser", "smbuser", "posixgroup", "smbgroup") 161*14843421SMatthew Ahrens 162*14843421SMatthew Ahrens u = _("%s [-niHp] [-o field[,...]] [-sS field] ... \n") % sys.argv[1] 163*14843421SMatthew Ahrens u += _(" [-t type[,...]] <filesystem|snapshot>") 164*14843421SMatthew Ahrens parser = optparse.OptionParser(usage=u, prog="zfs") 165*14843421SMatthew Ahrens 166*14843421SMatthew Ahrens parser.add_option("-n", action="store_true", dest="noname", 167*14843421SMatthew Ahrens help=_("Print numeric ID instead of user/group name")) 168*14843421SMatthew Ahrens parser.add_option("-i", action="store_true", dest="translate", 169*14843421SMatthew Ahrens help=_("translate SID to posix (possibly ephemeral) ID")) 170*14843421SMatthew Ahrens parser.add_option("-H", action="store_true", dest="noheaders", 171*14843421SMatthew Ahrens help=_("no headers, tab delimited output")) 172*14843421SMatthew Ahrens parser.add_option("-p", action="store_true", dest="parsable", 173*14843421SMatthew Ahrens help=_("exact (parsable) numeric output")) 174*14843421SMatthew Ahrens parser.add_option("-o", dest="fields", metavar="field[,...]", 175*14843421SMatthew Ahrens default="type,name,used,quota", 176*14843421SMatthew Ahrens help=_("print only these fields (eg type,name,used,quota)")) 177*14843421SMatthew Ahrens parser.add_option("-s", dest="sortfields", metavar="field", 178*14843421SMatthew Ahrens type="choice", choices=fields, default=list(), 179*14843421SMatthew Ahrens action="callback", callback=zfs.util.append_with_opt, 180*14843421SMatthew Ahrens help=_("sort field")) 181*14843421SMatthew Ahrens parser.add_option("-S", dest="sortfields", metavar="field", 182*14843421SMatthew Ahrens type="choice", choices=fields, #-s sets the default 183*14843421SMatthew Ahrens action="callback", callback=zfs.util.append_with_opt, 184*14843421SMatthew Ahrens help=_("reverse sort field")) 185*14843421SMatthew Ahrens parser.add_option("-t", dest="types", metavar="type[,...]", 186*14843421SMatthew Ahrens default=defaulttypes, 187*14843421SMatthew Ahrens help=_("print only these types (eg posixuser,smbuser,posixgroup,smbgroup,all)")) 188*14843421SMatthew Ahrens 189*14843421SMatthew Ahrens (options, args) = parser.parse_args(sys.argv[2:]) 190*14843421SMatthew Ahrens if len(args) != 1: 191*14843421SMatthew Ahrens usage(_("wrong number of arguments")) 192*14843421SMatthew Ahrens dsname = args[0] 193*14843421SMatthew Ahrens 194*14843421SMatthew Ahrens options.fields = options.fields.split(",") 195*14843421SMatthew Ahrens for f in options.fields: 196*14843421SMatthew Ahrens if f not in fields: 197*14843421SMatthew Ahrens usage(_("invalid field %s") % f) 198*14843421SMatthew Ahrens 199*14843421SMatthew Ahrens options.types = options.types.split(",") 200*14843421SMatthew Ahrens for t in options.types: 201*14843421SMatthew Ahrens if t not in types: 202*14843421SMatthew Ahrens usage(_("invalid type %s") % t) 203*14843421SMatthew Ahrens 204*14843421SMatthew Ahrens if not options.sortfields: 205*14843421SMatthew Ahrens options.sortfields = [("-s", "type"), ("-s", "name")] 206*14843421SMatthew Ahrens 207*14843421SMatthew Ahrens if "all" in options.types: 208*14843421SMatthew Ahrens options.types = types[1:] 209*14843421SMatthew Ahrens 210*14843421SMatthew Ahrens ds = zfs.dataset.Dataset(dsname, types=("filesystem")) 211*14843421SMatthew Ahrens 212*14843421SMatthew Ahrens if ds.getprop("zoned") and zfs.ioctl.isglobalzone(): 213*14843421SMatthew Ahrens options.noname = True 214*14843421SMatthew Ahrens 215*14843421SMatthew Ahrens if not ds.getprop("useraccounting"): 216*14843421SMatthew Ahrens print(_("Initializing accounting information on old filesystem, please wait...")) 217*14843421SMatthew Ahrens ds.userspace_upgrade() 218*14843421SMatthew Ahrens 219*14843421SMatthew Ahrens acct = dict() 220*14843421SMatthew Ahrens maxfieldlen = dict() 221*14843421SMatthew Ahrens 222*14843421SMatthew Ahrens # gather and process accounting information 223*14843421SMatthew Ahrens for prop in props.keys(): 224*14843421SMatthew Ahrens if skiptype(options, prop): 225*14843421SMatthew Ahrens continue; 226*14843421SMatthew Ahrens for elem in ds.userspace(prop): 227*14843421SMatthew Ahrens process_one_raw(acct, maxfieldlen, options, prop, elem) 228*14843421SMatthew Ahrens 229*14843421SMatthew Ahrens # print out headers 230*14843421SMatthew Ahrens if not options.noheaders: 231*14843421SMatthew Ahrens line = str() 232*14843421SMatthew Ahrens for field in options.fields: 233*14843421SMatthew Ahrens # make sure the field header will fit 234*14843421SMatthew Ahrens updatemax(maxfieldlen, field, len(field)) 235*14843421SMatthew Ahrens 236*14843421SMatthew Ahrens if field in ljustfields: 237*14843421SMatthew Ahrens fmt = "%-*s " 238*14843421SMatthew Ahrens else: 239*14843421SMatthew Ahrens fmt = "%*s " 240*14843421SMatthew Ahrens line += fmt % (maxfieldlen[field], field.upper()) 241*14843421SMatthew Ahrens print(line) 242*14843421SMatthew Ahrens 243*14843421SMatthew Ahrens # custom sorting func 244*14843421SMatthew Ahrens def cmpkey(val): 245*14843421SMatthew Ahrens l = list() 246*14843421SMatthew Ahrens for (opt, field) in options.sortfields: 247*14843421SMatthew Ahrens try: 248*14843421SMatthew Ahrens n = val[field + ".sort"] 249*14843421SMatthew Ahrens except KeyError: 250*14843421SMatthew Ahrens n = val[field] 251*14843421SMatthew Ahrens if opt == "-S": 252*14843421SMatthew Ahrens # reverse sorting 253*14843421SMatthew Ahrens try: 254*14843421SMatthew Ahrens n = -n 255*14843421SMatthew Ahrens except TypeError: 256*14843421SMatthew Ahrens # it's a string; decompose it 257*14843421SMatthew Ahrens # into an array of integers, 258*14843421SMatthew Ahrens # each one the negative of that 259*14843421SMatthew Ahrens # character 260*14843421SMatthew Ahrens n = [-ord(c) for c in n] 261*14843421SMatthew Ahrens l.append(n) 262*14843421SMatthew Ahrens return l 263*14843421SMatthew Ahrens 264*14843421SMatthew Ahrens # print out data lines 265*14843421SMatthew Ahrens for val in sorted(acct.itervalues(), key=cmpkey): 266*14843421SMatthew Ahrens line = str() 267*14843421SMatthew Ahrens for field in options.fields: 268*14843421SMatthew Ahrens if options.noheaders: 269*14843421SMatthew Ahrens line += val[field] 270*14843421SMatthew Ahrens line += "\t" 271*14843421SMatthew Ahrens else: 272*14843421SMatthew Ahrens if field in ljustfields: 273*14843421SMatthew Ahrens fmt = "%-*s " 274*14843421SMatthew Ahrens else: 275*14843421SMatthew Ahrens fmt = "%*s " 276*14843421SMatthew Ahrens line += fmt % (maxfieldlen[field], val[field]) 277*14843421SMatthew Ahrens print(line) 278