#! /usr/bin/python2.6
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
# Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
#
"Solaris-specific library wrappers and functions"
from ctypes import *
import copy
import gettext
import errno
import platform
import os
try:
_ = gettext.translation("SUNW_OST_OSLIB", "/usr/lib/locale",
fallback=True).gettext
except:
try:
import solaris.misc
_ = solaris.misc.gettext
except:
_ = lambda x : x
(LIBZFS_INIT_FAILURE,
) = range(1)
_msgs = {
LIBZFS_INIT_FAILURE: _('libzfs initialization failure')
}
#
# From sys/fstyp.h
#
FSTYPSZ = 16
MAX_PATH = 1024
PATH_MAX = MAX_PATH
_libc = CDLL('libc.so', use_errno=True)
_platwidth = platform.architecture()[0][:2]
_libc.fopen.restype = c_void_p
_libc.free.argtypes = [c_void_p]
_libc.free.restype = None
_statvfs_syscall = 'statvfs' + ('64' if _platwidth == '32' else '')
# print 'platwidth = `%s\' | _syscall = `%s\'' % (_platwidth, _syscall)
_statvfs = getattr(_libc, _statvfs_syscall)
def ctype2dict(ctype_struct_instance):
dictresult = {}
# A very important side-effect of calling getattr() here is
# that the ctypes API creates copies of the data we're
# retrieving from the "structure". If it did not do that,
# calling libc APIs that use static data areas would cause
# the resulting Python strings to refer to the same storage
# area.
for x in [x for (x, y) in ctype_struct_instance._fields_]:
dictresult[x] = getattr(ctype_struct_instance, x)
return dictresult
# ==============================[ statvfs ]===============================
class StatVFS_Result(Structure):
"""From the manpage:
int statvfs(const char *restrict path, struct statvfs *restrict buf);
u_long f_bsize; /* preferred file system block size */
u_long f_frsize; /* fundamental filesystem block
(size if supported) */
fsblkcnt_t f_blocks; /* total # of blocks on file system
in units of f_frsize */
fsblkcnt_t f_bfree; /* total # of free blocks */
fsblkcnt_t f_bavail; /* # of free blocks avail to
non-privileged user */
fsfilcnt_t f_files; /* total # of file nodes (inodes) */
fsfilcnt_t f_ffree; /* total # of free file nodes */
fsfilcnt_t f_favail; /* # of inodes avail to
non-privileged user*/
u_long f_fsid; /* file system id (dev for now) */
char f_basetype[FSTYPSZ]; /* target fs type name,
null-terminated */
u_long f_flag; /* bit mask of flags */
u_long f_namemax; /* maximum file name length */
char f_fstr[32]; /* file system specific string */
if not 64-bit process
u_long f_filler[16]; /* reserved for future expansion */
endif
We always use the 64-bit statvfs variant (statvfs64 in 32-bit
processes and statvfs in 64-bit processes)
"""
_fields_ = [("f_bsize", c_ulong),
("f_frsize", c_ulong),
("f_blocks", c_ulonglong),
("f_bfree", c_ulonglong),
("f_bavail", c_ulonglong),
("f_files", c_ulonglong),
("f_ffree", c_ulonglong),
("f_favail", c_ulonglong),
("f_fsid", c_ulong),
("f_basetype", c_char * FSTYPSZ),
("f_flag", c_ulong),
("f_namemax", c_ulong),
("f_fstr", c_char * 32),
("f_filler", c_int * 16)]
def statvfs(path):
"""Returns a dictionary whose members are the result of the Solaris
statvfs() call"""
result = StatVFS_Result()
if _statvfs(path, pointer(result)) != 0:
raise IOError(get_errno(), os.strerror(get_errno()))
return ctype2dict(result)
# ===========================[ mnttab functions ]============================
class SolarisMntTab(Structure):
"Python ctype expression of the Solaris mnttab structure"
_fields_ = [('mnt_special', c_char_p),
('mnt_mountp', c_char_p),
('mnt_fstype', c_char_p),
('mnt_mntopts', c_char_p),
('mnt_time', c_char_p)]
class SolarisExtMntTab(Structure):
"Python ctype expression of the Solaris extmnttab structure"
_fields_ = [('mnt_special', c_char_p),
('mnt_mountp', c_char_p),
('mnt_fstype', c_char_p),
('mnt_mntopts', c_char_p),
('mnt_time', c_char_p),
('mnt_major', c_uint),
('mnt_minor', c_uint)]
def mnttab_err_decode(code):
"""Decodes the following error codes from mnttab.h:
#define MNT_TOOLONG 1 /* entry exceeds MNT_LINE_MAX */
#define MNT_TOOMANY 2 /* too many fields in line */
#define MNT_TOOFEW 3 /* too few fields in line */
"""
if code == 1:
return 'Entry exceeds 1024 characters'
elif code == 2:
return 'Too many fields in line'
elif code == 3:
return 'Too few fields in line'
else:
return 'Unknown mnttab error'
def mnttab_open(mtab='/etc/mnttab'):
global _mnttab_FILE
_mnttab_FILE = c_void_p(_libc.fopen(mtab, 'r'))
if _mnttab_FILE.value is None:
raise IOError(get_errno(), mtab + ': ' +
os.strerror(get_errno()))
def getmntent():
"""Returns the next mnttab entry as a dictionary whose keys
are:
mnt_special
mnt_mountp
mnt_fstype
mnt_mntopts
mnt_time
or None if there are no more entries
"""
mntent = SolarisMntTab()
r = _libc.getmntent(_mnttab_FILE, byref(mntent))
if r < 0: # EOF
return None
elif r > 0: # Error
raise IOError(r, mnttab_err_decode(r))
return ctype2dict(mntent)
def getmntany(**attrs):
"""Returns a mnttab entry matching the attributes passed in, or
None if no entry matches.
"""
mntent = SolarisMntTab()
mntmatch = SolarisMntTab()
for x in attrs.keys():
mntent.__setattr__(x, attrs[x])
r = _libc.getmntany(_mnttab_FILE, byref(mntmatch), byref(mntent))
if r < 0: # EOF
return None
elif r > 0:
raise IOError(r, mnttab_err_decode(r))
return ctype2dict(mntmatch)
def getextmntent():
"""Returns the next extmnttab entry as a dictionary whose keys
are:
mnt_special
mnt_mountp
mnt_fstype
mnt_mntopts
mnt_time
mnt_major
mnt_minor
or None if there are no more entries.
"""
extmnt = SolarisExtMntTab()
r = _libc.getextmntent(_mnttab_FILE, byref(extmnt), sizeof(extmnt))
if r < 0: # EOF
return None
elif r > 0:
raise IOError(r, mnttab_err_decode(r))
return ctype2dict(extmnt)
def resetmnttab(reopen=False):
"""Rewinds the mnttab file to the beginning
if reopen is True, the mnttab file is closed, then reopened
"""
if reopen is True:
mnttab_close()
mnttab_open()
else:
_libc.rewind(_mnttab_FILE)
def mnttab_close():
_libc.fclose(_mnttab_FILE)
# =========================[libefi wrappers/utilities]=======================
EFI_PART_NAME_LEN = 36
class Sol_uuid(Structure):
_fields_ = [('time_low', c_uint),
('time_mid', c_ushort),
('time_hi_and_version', c_ushort),
('clock_seq_hi_and_reserved', c_ubyte),
('clock_seq_low', c_ubyte),
('node_addr', c_ubyte * 6)
]
def __init__(self, guid_string):
super(Sol_uuid, self).__init__()
try:
self.__parse_guid(guid_string)
except ValueError:
self.time_low = 0
self.time_mid = 0
self.time_hi_and_version = 0
self.clock_seq_hi_and_reserved = 0
self.clock_seq_low = 0
for idx in range(6):
self.node_addr[idx] = 0
def __parse_guid(self, guid_string):
segments = guid_string.split('-')
if (len(segments) != 5 or
len(segments[0]) != 8 or
len(segments[1]) != 4 or
len(segments[2]) != 4 or
len(segments[3]) != 4 or
len(segments[4]) != 12):
return
self.time_low = int(segments[0], 16)
self.time_mid = int(segments[1], 16)
self.time_hi_and_version = int(segments[2], 16)
self.clock_seq_hi_and_reserved = int(segments[3][0:2], 16)
self.clock_seq_low = int(segments[3][2:], 16)
for idx in range(6):
self.node_addr[idx] = int(segments[4][idx * 2:(idx + 1) * 2], 16)
def __eq__(self, b):
if not isinstance(b, Sol_uuid):
return False
if not (self.time_low == b.time_low and
self.time_mid == b.time_mid and
self.time_hi_and_version == b.time_hi_and_version and
self.clock_seq_hi_and_reserved == b.clock_seq_hi_and_reserved and
self.clock_seq_low == b.clock_seq_low):
return False
for idx in range(6):
if self.node_addr[idx] != b.node_addr[idx]:
return False
return True
def __str__(self):
s = '%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X'
s = s % (self.time_low, self.time_mid, self.time_hi_and_version,
self.clock_seq_hi_and_reserved, self.clock_seq_low,
self.node_addr[0], self.node_addr[1], self.node_addr[2],
self.node_addr[3], self.node_addr[4], self.node_addr[5])
return s
class Dk_part(Structure):
_fields_ = [('p_start', c_ulonglong),
('p_size', c_ulonglong),
('p_guid', Sol_uuid),
('p_tag', c_ushort),
('p_flag', c_ushort),
('p_name', c_char * EFI_PART_NAME_LEN),
('p_uguid', Sol_uuid),
('p_resv', c_uint * 8)
]
class Dk_gpt_t(Structure):
_fields_ = [('efi_version', c_uint),
('efi_nparts', c_uint),
('efi_part_size', c_uint),
('efi_lbasize', c_uint),
('efi_last_lba', c_ulonglong),
('efi_first_u_lba', c_ulonglong),
('efi_last_u_lba', c_ulonglong),
('efi_disk_uguid', Sol_uuid),
('efi_flags', c_uint),
('efi_reserved1', c_uint),
('efi_altern_lba', c_ulonglong),
('efi_reserved', c_uint * 12),
('efi_parts', Dk_part)
]
_libefi = CDLL('libefi.so', use_errno=True)
_libefi.efi_alloc_and_read.argtypes = [c_int, POINTER(POINTER(Dk_gpt_t))]
def is_gpt_disk(wholedisk):
"""Returns True if the character device node 'wholedisk' is a GPT-
partitioned disk.
"""
diskfd = os.open(wholedisk, os.O_RDONLY)
datap = pointer(c_void_p())
rval = _libefi.efi_alloc_and_read(diskfd, cast(datap,
POINTER(POINTER(Dk_gpt_t))))
os.close(diskfd)
if rval >= 0:
_libc.free(datap.contents)
return True
return False
def find_gpt_partition(wholedisk, part_guid):
"""
"""
import sys
diskfd = os.open(wholedisk, os.O_RDONLY)
datap = pointer(c_void_p())
rval = _libefi.efi_alloc_and_read(diskfd, cast(datap, POINTER(POINTER(Dk_gpt_t))))
os.close(diskfd)
partno = -1
if rval >= 0:
dkgpt = cast(datap, POINTER(POINTER(Dk_gpt_t))).contents.contents
# Now comes the tricky part. Since Dk_gpt_t was declared with a single
# partition's worth of storage in efi_parts, we need to declare a new
# type that can hold all of the partitions actually stored in the dk_gpt_t
# returned from efi_alloc_and_read():
mapit = (lambda x: x if x[0] != 'efi_parts' else
('efi_parts', Dk_part * dkgpt.efi_nparts))
field_list = map(mapit, Dk_gpt_t._fields_)
class Rightsized_dk_gpt_t(Structure):
_fields_ = field_list
newdkgpt = Rightsized_dk_gpt_t()
memmove(pointer(newdkgpt), datap.contents, sizeof(newdkgpt))
_libc.free(datap.contents)
# Now look for the desired partition guid:
target_guid = Sol_uuid(part_guid)
for idx in range(newdkgpt.efi_nparts):
if target_guid == newdkgpt.efi_parts[idx].p_guid:
partno = idx
break
return partno
# =======================[libfdisk and DOS partitioning]=====================
class Ipart(Structure):
"""
struct ipart {
unsigned char bootid; /* bootable or not */
unsigned char beghead; /* beginning head, sector, cylinder */
unsigned char begsect; /* begcyl is a 10-bit number. High 2 bits */
unsigned char begcyl; /* are in begsect. */
unsigned char systid; /* OS type */
unsigned char endhead; /* ending head, sector, cylinder */
unsigned char endsect; /* endcyl is a 10-bit number. High 2 bits */
unsigned char endcyl; /* are in endsect. */
uint32_t relsect; /* first sector relative to start of disk */
uint32_t numsect; /* number of sectors in partition */
};
"""
_fields_ = [('bootid', c_ubyte),
('beghead', c_ubyte),
('begsect', c_ubyte),
('begcyl', c_ubyte),
('systid', c_ubyte),
('endhead', c_ubyte),
('endsect', c_ubyte),
('endcyl', c_ubyte),
('relsect', c_uint),
('numsect', c_uint)
]
class Fdisk_disk_geom(Structure):
"""
typedef struct fdisk_disk_geom {
ushort_t phys_cyl;
ushort_t phys_sec;
ushort_t phys_heads;
ushort_t alt_cyl;
ushort_t virt_cyl;
ushort_t virt_sec;
ushort_t virt_heads;
ushort_t sectsize;
} fdisk_disk_geom_t;
"""
_fields_ = [('phys_cyl', c_ushort),
('phys_sec', c_ushort),
('phys_heads', c_ushort),
('alt_cyl', c_ushort),
('virt_cyl', c_ushort),
('virt_sec', c_ushort),
('virt_heads', c_ushort),
('sectsize', c_ushort)
]
class Logical_drive(Structure):
pass
Logical_drive._fields_ = [
('parts', Ipart * 2),
('abs_secnum', c_uint32),
('logdrive_offset', c_uint32),
('numsect', c_uint32),
('begcyl', c_uint32),
('endcyl', c_uint32),
('modified', c_int),
('next', POINTER(Logical_drive)),
('sorted_next', POINTER(Logical_drive))
]
# on x86, the max is 32:
MAX_EXT_PARTS = 32
class Libfdisk_ext_part(Structure):
_fields_ = [('disk_geom', Fdisk_disk_geom),
('mtable', POINTER(Ipart)),
('device_name', c_char * PATH_MAX),
('dev_fd', c_int),
('op_flag', c_int),
('ld_head', POINTER(Logical_drive)),
('sorted_ld_head', POINTER(Logical_drive)),
('ext_beg_cyl', c_uint),
('ext_end_cyl', c_uint),
('ext_beg_sec', c_uint),
('ext_end_sec', c_uint),
('logical_drive_count', c_int),
('first_ebr_is_null', c_int),
('corrupt_logical_drives', c_int),
('invalid_bb_sig', c_byte * MAX_EXT_PARTS)
]
# from libfdisk.h:
FDISK_READ_DISK = 1
try:
_libfdisk = CDLL('libfdisk.so', use_errno=True)
_libfdisk.libfdisk_init.argtypes = [POINTER(POINTER(Libfdisk_ext_part)),
c_char_p, POINTER(Ipart), c_int]
_libfdisk.libfdisk_fini.argtypes = [POINTER(POINTER(Libfdisk_ext_part))]
_libfdisk.fdisk_get_part_info.argtypes = [POINTER(Libfdisk_ext_part),
c_int, POINTER(c_ubyte),
POINTER(c_uint32),
POINTER(c_uint32)]
except OSError:
_libfdisk = None
def libfdisk_find_part_by_id(wholedisk, part_id):
"""Searches the main and extended partition tables for the specified
partition id (0 < id < 256).
"""
if not _libfdisk:
return -1
extpart = cast(pointer(c_void_p()), POINTER(POINTER(Libfdisk_ext_part)))
# XXX -
# If this is needed on large sector disks, read the first 512 bytes from
# the block device node and pass the ipart array to libfdisk_init().
rval = _libfdisk.libfdisk_init(extpart, wholedisk, None, FDISK_READ_DISK)
if rval != 0:
return -1
ext_part = extpart.contents.contents
mbrtable = cast(ext_part.mtable, POINTER(Ipart * 4)).contents
part_index = -1
# First check the 4-slot main partition table
for idx in range(4):
if part_id == mbrtable[idx].systid:
part_index = idx + 1 # The partition number must be 1-based
break
if part_index == -1 and ext_part.logical_drive_count > 0:
relsect = c_uint32()
partsize = c_uint32()
systid = c_ubyte()
toprange = min(MAX_EXT_PARTS, 5 + ext_part.logical_drive_count)
for idx in range(5, toprange):
rval = _libfdisk.fdisk_get_part_info(extpart.contents, idx,
byref(systid), byref(relsect), byref(partsize))
if rval != 0:
continue
if systid.value == part_id:
part_index = idx
break
_libfdisk.libfdisk_fini(extpart)
return part_index
# ==============================[devlink walking]============================
_libdi = CDLL('libdevinfo.so', use_errno=True)
_libdi.di_devlink_init.restype = c_void_p
_libdi.di_devlink_init.argtypes = [c_char_p, c_int]
_libdi.di_devlink_path.restype = c_char_p
_libdi.di_devlink_path.argtypes = [c_void_p]
# The walker function returns an int and takes 2 void *'s: the di_devlink_t,
# and the arg
_devlink_walkfunc_type = CFUNCTYPE(c_int, c_void_p, c_void_p)
_libdi.di_devlink_walk.argtypes = [c_void_p, c_char_p, c_char_p, c_int,
c_void_p, _devlink_walkfunc_type]
# from <libdevinfo.h>:
DI_MAKE_LINK = 1
DI_PRIMARY_LINK = 1
DI_SECONDARY_LINK = 2
DI_WALK_CONTINUE = 0
DI_WALK_TERMINATE = -3
def di_devlink_init(drvname=None, flags=0):
"""Initialize the libdevinfo devlink interfaces"""
hdl = c_void_p(_libdi.di_devlink_init(drvname, flags))
if hdl.value is None:
raise IOError(get_errno(), os.strerror(get_errno()))
return hdl
def di_devlink_path(dl):
"""Return a string that is the path corresponding to the devlink
passed in
"""
r = _libdi.di_devlink_path(dl)
if r is None:
raise IOError(get_errno(), os.strerror(get_errno()))
return r
def di_devlink_walk(hdl, pattern, path, flags, walk_arg, walkfunc):
"""Conduct a walk of all devlinks that patch the given pattern and
that pertain to the given path. Note that since ctypes passes
arguments to callbacks as 'int's, those arguments must be converted
into a useful Python type (passing a string, then reconstituting
a list from that string, for example).
"""
wf = _devlink_walkfunc_type(walkfunc)
r = _libdi.di_devlink_walk(hdl, pattern, path, flags, walk_arg, wf)
if r < 0:
raise IOError(get_errno(), os.strerror(get_errno()))
def di_devlink_fini(hdl):
"""Performs cleanup after use of the devlink interfaces"""
_libdi.di_devlink_fini(hdl)
# ==========================[ libdevinfo subset ]=============================
_minor_walkfunc_type = CFUNCTYPE(c_int, c_void_p, c_void_p, c_void_p)
_libdi.di_prom_init.restype = c_void_p
_libdi.di_prom_init.argtypes = None
_libdi.di_init.restype = c_void_p
_libdi.di_init.argtypes = [c_char_p, c_int]
_libdi.di_fini.restype = None
_libdi.di_fini.argtypes = [c_void_p]
_libdi.di_prom_fini.restype = None
_libdi.di_prom_fini.argtypes = [c_void_p]
_libdi.di_devfs_path.restype = c_void_p
_libdi.di_devfs_path.argtypes = [c_void_p]
_libdi.di_devfs_path_free.argtypes = [c_void_p]
_libdi.di_minor_spectype.argtypes = [c_void_p]
_libdi.di_minor_name.restype = c_char_p
_libdi.di_minor_name.argtypes = [c_void_p]
_libdi.di_minor_next.restype = c_void_p
_libdi.di_minor_next.argtypes = [c_void_p, c_void_p]
_libdi.di_walk_minor.argtypes = [c_void_p, c_char_p, c_int, c_void_p,
_minor_walkfunc_type]
_libdi.di_prop_lookup_strings.argtypes = [c_ulong, c_void_p,
c_char_p, c_void_p]
_libdi.di_prom_prop_lookup_bytes.argtypes = [c_void_p, c_void_p,
c_char_p,
POINTER(POINTER(c_ubyte))]
class struct_boot_dev(Structure):
_fields_ = [('bootdev_element', c_char_p),
('bootdev_trans', POINTER(c_char_p))]
pp_struct_boot_dev = POINTER(POINTER(struct_boot_dev))
_libdi.devfs_bootdev_get_list.argtypes = [c_char_p,
POINTER(pp_struct_boot_dev)]
_libdi.devfs_bootdev_free_list.argtypes = [pp_struct_boot_dev]
_libdi.devfs_bootdev_free_list.restype = None
_libdi.devfs_bootdev_set_list.argtypes = [c_void_p, c_uint]
_libdi.di_child_node.argtypes = [ c_void_p ]
_libdi.di_child_node.restype = c_void_p
_libdi.di_node_name.argtypes = [ c_void_p ]
_libdi.di_node_name.restype = c_char_p
_libdi.di_sibling_node.argtypes = [ c_void_p ]
_libdi.di_sibling_node.restype = c_void_p
S_IFBLK = 0x6000 # from <sys/stat.h>
DDI_NT_BLOCK = "ddi_block" # from <sys/sunddi.h>
# from <sys/devinfo_impl.h>:
DIIOC = (0xdf << 8)
DINFOSUBTREE = (DIIOC | 0x01)
DINFOMINOR = (DIIOC | 0x02)
DINFOPROP = (DIIOC | 0x04)
DINFOPATH = (DIIOC | 0x08)
DINFOCPYALL = (DINFOSUBTREE | DINFOPROP | DINFOMINOR)
# from src/lib/libdevinfo/device_info.h
# error return values for libdevinfo functions
libdevinfo_errdict = {
-1: 'operation not successful',
-2: 'invalid argument',
-3: 'out of memory',
-4: 'permission denied - not root',
-5: 'operation not supported',
-6: 'exceeded maximum size of property value',
-7: 'request not found'
}
# The devfs_bootdev_get/set_list APIs are NOT public. DO NOT USE RELY
# ON THEIR STABILITY. There are no man pages for them FOR A REASON.
# perform no translation on the input device path
BOOTDEV_LITERAL = 0x1
# convert the input device path only a prom device path; not an alias
BOOTDEV_PROMDEV = 0x2
# overwrite the existing entry in boot-device - default is to prepend
BOOTDEV_OVERWRITE = 0x4
# from <sys/ddipropdefs.h>
DDI_DEV_T_ANY = -2
DDI_DEV_T_NONE = -1
def _di_minor_spectype(minor):
"""Returns the type (block or char) of the special node whose minor
info is passed in
"""
return _libdi.di_minor_spectype(minor)
def di_prom_init():
"""Initialize the prom for property access.
"""
hdl = c_void_p(_libdi.di_prom_init())
if hdl.value is None:
raise IOError(get_errno(), os.strerror(get_errno()))
return hdl
def di_init(path='/', flags=(DINFOPATH | DINFOCPYALL)):
"""Initialize the device tree snapshot, starting from the device path
specified. flags are (DINFOPATH|DINFOCPYALL) by default.
"""
hdl = c_void_p(_libdi.di_init(path, flags))
if hdl.value is None:
raise IOError(get_errno(), os.strerror(get_errno()))
return hdl
def di_minor_is_block(minor):
return (True if _di_minor_spectype(minor) == S_IFBLK else False)
def di_minor_name(minor):
"Returns the string name of the minor passed in"
return _libdi.di_minor_name(minor)
def di_devfs_path(node):
"Returns the string name of the node passed in"
r = c_void_p(_libdi.di_devfs_path(node))
if r.value is None:
raise IOError(get_errno(), os.strerror(get_errno()))
rs = c_char_p(r.value).value
_libdi.di_devfs_path_free(r)
return rs
def di_minor_next(node, minor=None):
"""Returns the next minor node, relative to the minor passed in.
If minor isn't specified, or is passed in as None, the first minor
associated with the node is returned. None is returned when no
more minors exist.
"""
r = c_void_p(_libdi.di_minor_next(node, minor))
if r.value is None:
e = get_errno()
# NXIO is a special case-- an indicator of no more minors
if e == errno.ENXIO:
return None
else:
raise IOError(e, os.strerror(e))
return r
def di_walk_minor(rootnode, minortype, walkarg, walkfunc, flag=0):
"""Perform a walk of all minor nodes attached to device nodes
in a subtree rooted at `root'. walkargs should be a simple Python
type, since it will need to be reconstituted in the walkfunc callback.
"""
wf = _minor_walkfunc_type(walkfunc)
r = _libdi.di_walk_minor(rootnode, minortype, flag, walkarg, wf)
print r
if r < 0:
raise IOError(get_errno(), os.strerror(get_errno()))
def di_fini(hdl):
_libdi.di_fini(hdl)
def di_prom_fini(hdl):
_libdi.di_prom_fini(hdl)
def devfs_bootdev_get_list():
bdl = pp_struct_boot_dev()
rv = _libdi.devfs_bootdev_get_list('/', pointer(bdl))
# No exception is raised on error, because there is no errno
# value that accompanies the failure
if rv < 0 or bool(bdl) is False:
return None
i = 0
bootdevs = []
while not bool(bdl[i]) is False:
physical = bdl[i][0].bootdev_element
j = 0
logicals = []
while not bool(bdl[i][0].bootdev_trans[j]) is False:
logicals.append(bdl[i][0].bootdev_trans[j])
j += 1
bootdevs.append((physical, tuple(logicals)))
i += 1
_libdi.devfs_bootdev_free_list(bdl)
return tuple(bootdevs)
def devfs_bootdev_set_list(list_of_devices, options=0):
"""Set the boot device list to the given list.
"""
list_string = '\0'.join(list_of_devices) + '\0\0'
rv = _libdi.devfs_bootdev_set_list(list_string, options)
return rv
def di_prop_lookup_strings(dev, node, prop_name, prop_data):
rv = _libdi.di_prop_lookup_strings(dev, node, prop_name, prop_data)
if rv < 0:
raise IOError(get_errno(), os.strerror(get_errno()))
return rv
def di_find_root_prop(propname):
return di_find_prop(propname, '/')
def di_find_prop(propname, root):
hdl = di_init(root, DINFOPROP)
propval = None
try:
# This is a bit tricky, since di_prop_lookup_strings returns ONE
# string with several embedded NUL terminators (if multiple strings
# are returned). To deal with this in ctypes, we use a pointer to
# a c_void_p as the last parameter and manually pull out each string
# To pull out each string, we cast the c_void_p to a c_char_p, which
# automatically gets us the first string (ctypes extracts it for us)
# To get subsequent strings, we need to manually index the pointer,
# adding the length of the previous string + 1 (since len() includes
# the NUL and one to get us past it).
value = pointer(c_void_p())
rv = di_prop_lookup_strings(DDI_DEV_T_ANY, hdl, propname,
value)
stringvalue = cast(value.contents, c_char_p)
if rv == 1:
propval = stringvalue.value
elif rv > 1:
stringlen = len(stringvalue.value)
propval = [stringvalue.value]
rv -= 1
while rv > 0:
value.contents.value += stringlen + 1
stringvalue = cast(value.contents, c_char_p)
propval.append(stringvalue.value)
stringlen = len(stringvalue.value)
rv -= 1
except IOError as e:
if e.errno == errno.ENXIO:
propval = None
else:
raise
finally:
di_fini(hdl)
return propval
def di_prom_prop_lookup_bytes(phdl, nodehdl, propname, charpp):
"""
"""
rv = _libdi.di_prom_prop_lookup_bytes(phdl, nodehdl, propname, charpp)
if rv < 0:
raise IOError(get_errno(), os.strerror(get_errno()))
return rv
def di_child_node(node):
"""
"""
rv = _libdi.di_child_node(node)
return c_void_p(rv)
def di_sibling_node(node):
"""
"""
rv = _libdi.di_sibling_node(node)
return c_void_p(rv)
def di_node_name(node):
"""
"""
rv = _libdi.di_node_name(node)
return rv
def _di_locate_child(node, propname):
"""
"""
child = di_child_node(node)
while child:
if propname == di_node_name(child):
return child
child = di_sibling_node(child)
return None
def di_find_prom_prop_bool(propname, root):
"""
"""
hdl = di_init('/', DINFOCPYALL)
phdl = di_prom_init()
propval = None
prop_components = root.split('/')
node = hdl
try:
for component in prop_components:
if component == '':
continue
node = _di_locate_child(node, component)
value = POINTER(c_ubyte)()
rv = di_prom_prop_lookup_bytes(phdl, node, propname, byref(value))
propval = True
except IOError as e:
propval = False
finally:
di_prom_fini(phdl)
di_fini(hdl)
return propval
# ==============================[ libfstyp ]==================================
_libfstyp = CDLL('libfstyp.so.1', use_errno=True)
_libfstyp.fstyp_init.argtypes = [c_int, c_uint64, c_char_p, c_void_p]
_libfstyp.fstyp_ident.argtypes = [c_void_p, c_char_p, c_void_p]
_libfstyp.fstyp_fini.argtypes = [c_void_p]
_libfstyp.fstyp_strerror.argtypes = [c_void_p, c_int]
_libfstyp.fstyp_strerror.restype = c_char_p
def fstyp_init(fd, offset=0, module_dir=None):
"Returns a handle that can be used with other fstyp functions"
handle = c_void_p()
r = _libfstyp.fstyp_init(fd, offset, module_dir, byref(handle))
if r != 0:
raise IOError(r, _libfstyp.fstyp_strerror(r))
return handle
def fstyp_ident(handle, name=None):
result = c_char_p(0)
r = _libfstyp.fstyp_ident(handle, name, byref(result))
if r == 1: # No Match Found
return None
elif r != 0:
raise IOError(r, _libfstyp.fstyp_strerror(handle, r))
return result.value
def fstyp_fini(handle):
_libfstyp.fstyp_fini(handle)
# ================================[ isalist ]=================================
SI_MACHINE = 5
SI_PLATFORM = 513
SI_ISALIST = 514 # return supported isa list
SYSINFO_LEN = 257 # max buffer size, as per manpage
_libc.sysinfo.argtypes = [c_int, c_char_p, c_long]
_libc.sysinfo.restype = c_int
_isalist_cache = None
def isalist():
"Returns a list of ISAs supported on the currently-running system"
global _isalist_cache
if not _isalist_cache is None:
return _isalist_cache
b = create_string_buffer(SYSINFO_LEN)
r = _libc.sysinfo(SI_ISALIST, b, SYSINFO_LEN)
if r < 0:
raise OSError(get_errno(), os.strerror(get_errno()))
_isalist_cache = b.value.split()
return _isalist_cache
_platform_name_cache = None
def platform_name():
global _platform_name_cache
if not _platform_name_cache is None:
return _platform_name_cache
b = create_string_buffer(SYSINFO_LEN)
r = _libc.sysinfo(SI_PLATFORM, b, SYSINFO_LEN)
if r < 0:
raise OSError(get_errno(), os.strerror(get_errno()))
_platform_name_cache = b.value
return b.value
_machine_name_cache = None
def machine_name():
global _machine_name_cache
if not _machine_name_cache is None:
return _machine_name_cache
b = create_string_buffer(SYSINFO_LEN)
r = _libc.sysinfo(SI_MACHINE, b, SYSINFO_LEN)
if r < 0:
raise OSError(get_errno(), os.strerror(get_errno()))
_machine_name_cache = b.value
return b.value
# =================================[ libscf ]=================================
SCF_STATE_STRING_ONLINE = 'online'
_libscf = CDLL('libscf.so.1', use_errno=True)
_libscf.smf_get_state.argtypes = [c_char_p]
_libscf.smf_get_state.restype = c_void_p # Why c_void_p ? See comment below.
_libscf.smf_enable_instance.argtypes = [c_char_p, c_int]
_libscf.smf_enable_instance.restype = c_int
_libscf.scf_error.argtypes = None
_libscf.scf_error.restype = c_int
_libscf.scf_strerror.argtypes = [c_int]
_libscf.scf_strerror.restype = c_char_p
class SCFError(Exception):
"Wrap for smf errors"
def __init__(self, errcode, errstring):
self.errno = errcode
self.strerror = errstring
def smf_get_state(svc):
"Returns the current state of the specified service"
# Need to store the result in a c_void_p because if a c_char_p were
# specified as the return type, it would be automatically converted
# to a Python string and we'd love the original pointer value, which
# we need for the call to free()
alloced_str = c_void_p(_libscf.smf_get_state(svc))
if alloced_str.value == 0:
return None
result = c_char_p(alloced_str.value).value
_libc.free(alloced_str)
return result
def smf_enable_instance(svc, flags=0):
"Enables the given smf service with the specified flags"
ret = _libscf.smf_enable_instance(svc, flags)
if ret == -1:
scferr = _libscf.scf_error()
raise SCFError(scferr, _libscf.scf_strerror(scferr))
else:
return ret
# =================================[ libzfs ]=================================
LIBZFS_PROPLEN_MAX = 1024
_libzfs = CDLL('libzfs.so.1', use_errno=True)
_libzfs.libzfs_init.restype = c_void_p
_libzfs.libzfs_fini.argtypes = [c_void_p]
_libzfs.libzfs_fini.restype = None
_libzfs.libzfs_error_description.argtypes = [c_void_p]
_libzfs.libzfs_error_description.restype = c_char_p
_libzfs.zfs_open.restype = c_void_p
_libzfs.zfs_open.argtypes = [c_void_p, c_char_p, c_int]
_libzfs.zfs_close.argtypes = [c_void_p]
_libzfs.zfs_close.restype = None
zif_callback = CFUNCTYPE(c_int, c_void_p, c_void_p)
_libzfs.zfs_iter_filesystems.argtypes = [c_void_p, zif_callback, c_void_p]
_libzfs.zfs_get_type.argtypes = [c_void_p]
_libzfs.zfs_get_name.restype = c_char_p
_libzfs.zfs_get_name.argtypes = [c_void_p]
_libzfs.zfs_name_valid.argtypes = [c_char_p, c_int]
_libzfs.zpool_open.restype = c_void_p
_libzfs.zpool_open.argtypes = [c_void_p, c_char_p]
_libzfs.zpool_close.restype = None
_libzfs.zpool_close.argtypes = [c_void_p]
_libzfs.zpool_get_physpath.argtypes = [c_void_p, c_char_p, c_int]
_libzfs.zpool_get_prop.argtypes = [c_void_p, c_int, c_char_p, c_int,
POINTER(c_int)]
_libzfs.zpool_set_prop.argtypes = [c_void_p, c_char_p, c_char_p]
_libzfs.zfs_prop_get.argtypes = [ c_void_p, c_int, c_char_p, c_int,
POINTER(c_int), c_char_p, c_int,
c_int ]
# zprop_source_t values:
ZPROP_SRC_NONE = 0x1
ZPROP_SRC_DEFAULT = 0x2
ZPROP_SRC_TEMPORARY = 0x4
ZPROP_SRC_LOCAL = 0x8
ZPROP_SRC_INHERITED = 0x10
ZPROP_SRC_RECEIVED = 0x20
ZFS_TYPE_FILESYSTEM = 1
ZFS_TYPE_SNAPSHOT = 2
ZFS_TYPE_VOLUME = 4
ZFS_TYPE_POOL = 8
# ZFS_PROP values:
(ZFS_PROP_TYPE,
ZFS_PROP_CREATION,
ZFS_PROP_USED,
ZFS_PROP_AVAILABLE,
ZFS_PROP_REFERENCED,
ZFS_PROP_COMPRESSRATIO,
ZFS_PROP_MOUNTED,
ZFS_PROP_ORIGIN,
ZFS_PROP_QUOTA,
ZFS_PROP_RESERVATION,
ZFS_PROP_VOLSIZE,
ZFS_PROP_VOLBLOCKSIZE,
ZFS_PROP_RECORDSIZE,
ZFS_PROP_MOUNTPOINT,
ZFS_PROP_SHARENFS,
ZFS_PROP_CHECKSUM,
ZFS_PROP_COMPRESSION,
ZFS_PROP_ATIME,
ZFS_PROP_DEVICES,
ZFS_PROP_EXEC,
ZFS_PROP_SETUID,
ZFS_PROP_READONLY,
ZFS_PROP_ZONED,
ZFS_PROP_SNAPDIR,
ZFS_PROP_ACLMODE,
ZFS_PROP_ACLINHERIT,
ZFS_PROP_CREATETXG, # not exposed to the user
ZFS_PROP_NAME, # not exposed to the user
ZFS_PROP_CANMOUNT,
ZFS_PROP_ISCSIOPTIONS, # not exposed to the user
ZFS_PROP_XATTR,
ZFS_PROP_NUMCLONES, # not exposed to the user
ZFS_PROP_COPIES,
ZFS_PROP_VERSION,
ZFS_PROP_UTF8ONLY,
ZFS_PROP_NORMALIZE,
ZFS_PROP_CASE,
ZFS_PROP_VSCAN,
ZFS_PROP_NBMAND,
ZFS_PROP_SHARESMB,
ZFS_PROP_REFQUOTA,
ZFS_PROP_REFRESERVATION,
ZFS_PROP_GUID,
ZFS_PROP_PRIMARYCACHE,
ZFS_PROP_SECONDARYCACHE,
ZFS_PROP_USEDSNAP,
ZFS_PROP_USEDDS,
ZFS_PROP_USEDCHILD,
ZFS_PROP_USEDREFRESERV,
ZFS_PROP_USERACCOUNTING, # not exposed to the user
ZFS_PROP_STMF_SHAREINFO, # not exposed to the user
ZFS_PROP_DEFER_DESTROY,
ZFS_PROP_USERREFS,
ZFS_PROP_LOGBIAS,
ZFS_PROP_UNIQUE, # not exposed to the user
ZFS_PROP_OBJSETID, # not exposed to the user
ZFS_PROP_DEDUP,
ZFS_PROP_MLSLABEL,
ZFS_PROP_SYNC,
ZFS_PROP_HASRECVD, # not exposed to the user
ZFS_PROP_ENCRYPTION,
ZFS_PROP_KEYSOURCE,
ZFS_PROP_KEYSTATUS,
ZFS_PROP_SALT, # not exposed to the user
ZFS_PROP_REKEYDATE,
ZFS_PROP_RSTCHOWN,
ZFS_PROP_TMPMOUNTED,
ZFS_PROP_SHARE2, # not exposed to the user
ZFS_PROP_SHADOW,
ZFS_PROP_USERUSED,
ZFS_PROP_USERQUOTA,
ZFS_PROP_GROUPUSED,
ZFS_PROP_GROUPQUOTA) = range(73)
# ZPOOL_PROP values:
(ZPOOL_PROP_NAME,
ZPOOL_PROP_SIZE,
ZPOOL_PROP_CAPACITY,
ZPOOL_PROP_ALTROOT,
ZPOOL_PROP_HEALTH,
ZPOOL_PROP_GUID,
ZPOOL_PROP_VERSION,
ZPOOL_PROP_BOOTFS,
ZPOOL_PROP_DELEGATION,
ZPOOL_PROP_AUTOREPLACE,
ZPOOL_PROP_CACHEFILE,
ZPOOL_PROP_FAILUREMODE,
ZPOOL_PROP_LISTSNAPS,
ZPOOL_PROP_AUTOEXPAND,
ZPOOL_PROP_DEDUPDITTO,
ZPOOL_PROP_DEDUPRATIO,
ZPOOL_PROP_FREE,
ZPOOL_PROP_ALLOCATED,
ZPOOL_PROP_READONLY) = range(19)
def libzfs_init():
hdl = _libzfs.libzfs_init()
if hdl is None:
raise IOError(0, _msgs[LIBZFS_INIT_FAILURE])
return c_void_p(hdl)
def libzfs_error_description(lzfsh):
return _libzfs.libzfs_error_description(lzfsh)
def zfs_open(lzfsh, zfsname, type=ZFS_TYPE_FILESYSTEM):
hdl = _libzfs.zfs_open(lzfsh, zfsname, type)
if hdl is None:
raise IOError(0, libzfs_error_description(lzfsh))
return c_void_p(hdl)
def zfs_close(zfsh):
_libzfs.zfs_close(zfsh)
def zfs_get_type(zfsh):
return _libzfs.zfs_get_type(zfsh)
def zfs_get_name(zfsh):
return _libzfs.zfs_get_name(zfsh)
def zfs_name_valid(beName, type):
ret = _libzfs.zfs_name_valid(beName, type)
return False if ret is 0 else True
def zpool_open(lzfsh, poolname):
hdl = _libzfs.zpool_open(lzfsh, poolname)
if hdl is None:
raise IOError(0, libzfs_error_description(lzfsh))
return c_void_p(hdl)
def zpool_close(zph):
_libzfs.zpool_close(zph)
def zpool_get_physpath(lzfsh, zph):
buf = create_string_buffer(MAX_PATH)
ret = _libzfs.zpool_get_physpath(zph, buf, MAX_PATH)
if not ret is 0:
raise IOError(0, libzfs_error_description(lzfsh))
return buf.value.split()
def zpool_get_prop(lzfsh, zph, propid, get_source=False):
buf = create_string_buffer(LIBZFS_PROPLEN_MAX)
if get_source is True:
src = c_int()
srcp = pointer(src)
else:
srcp = None
ret = _libzfs.zpool_get_prop(zph, propid, buf, LIBZFS_PROPLEN_MAX,
srcp)
if not ret is 0:
raise IOError(0, libzfs_error_description(lzfsh))
if get_source is True:
return [buf.value, src.value]
else:
return buf.value
def zpool_set_prop(zph, propname, propval):
return _libzfs.zpool_set_prop(zph, propname, propval)
def zfs_get_prop_string(lzfsh, zfs_hdl, propid, get_source=False):
buf = create_string_buffer(LIBZFS_PROPLEN_MAX)
if get_source is True:
src = c_int()
srcp = pointer(src)
else:
srcp = None
ret = _libzfs.zfs_prop_get(zfs_hdl, propid, buf,
LIBZFS_PROPLEN_MAX, srcp, c_char_p(0), 0, 1)
if not ret is 0:
raise IOError(0, libzfs_error_description(lzfsh))
if get_source is True:
return [buf.value, src.value]
else:
return buf.value
def libzfs_fini(handle):
_libzfs.libzfs_fini(handle)
__all__ = ["statvfs",
"mnttab_open",
"mnttab_close",
"getmntent",
"getmntany",
"getextmntent",
"resetmnttab",
"DI_MAKE_LINK",
"DI_PRIMARY_LINK",
"DI_SECONDARY_LINK",
"DI_WALK_CONTINUE",
"DI_WALK_TERMINATE",
"di_devlink_init",
"di_devlink_path",
"di_devlink_walk",
"di_devlink_fini",
"DDI_NT_BLOCK",
"DINFOPATH",
"DINFOCPYALL",
"di_init",
"di_minor_is_block",
"di_minor_name",
"di_devfs_path",
"di_minor_next",
"di_walk_minor",
"di_fini",
"devfs_bootdev_get_list",
"fstyp_init",
"fstyp_ident",
"fstyp_fini",
"isalist",
"platform_name",
"machine_name",
"ZFS_TYPE_FILESYSTEM",
"ZFS_TYPE_POOL",
"ZPOOL_PROP_BOOTFS",
"libzfs_init",
"libzfs_fini",
"zfs_open",
"zfs_close",
"zpool_open",
"zpool_close",
"zfs_get_type",
"zfs_get_name",
"zfs_name_valid",
"zpool_get_physpath",
"zpool_get_prop",
"zpool_set_prop"]