Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

816 lines (710 sloc) 21.665 kb
/*
* Copyright (c) 2004-2005, 2007-2008 Hyperic, Inc.
* Copyright (c) 2009 SpringSource, Inc.
* Copyright (c) 2010 VMware, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
#ifndef WIN32
# ifdef _AIX
# define _LARGE_FILES
# else
# define _FILE_OFFSET_BITS 64
# define _LARGEFILE64_SOURCE
# endif
#endif
#include "sigar.h"
#ifndef WIN32
#if defined(__FreeBSD__) || defined(__OpenBSD__)
# include <sys/param.h>
# include <sys/mount.h>
#else
# include <sys/statvfs.h>
# define HAVE_STATVFS
#endif
#include <errno.h>
#define SIGAR_FS_BLOCKS_TO_BYTES(val, bsize) ((val * bsize) >> 1)
int sigar_statvfs(sigar_t *sigar,
const char *dirname,
sigar_file_system_usage_t *fsusage)
{
sigar_uint64_t val, bsize;
#ifdef HAVE_STATVFS
struct statvfs buf;
int status =
# if defined(__sun) && !defined(_LP64)
/* http://bugs.opensolaris.org/view_bug.do?bug_id=4462986 */
statvfs(dirname, (void *)&buf);
# else
statvfs(dirname, &buf);
# endif
#else
struct statfs buf;
int status = statfs(dirname, &buf);
#endif
if (status != 0) {
return errno;
}
#ifdef HAVE_STATVFS
bsize = buf.f_frsize / 512;
#else
bsize = buf.f_bsize / 512;
#endif
val = buf.f_blocks;
fsusage->total = SIGAR_FS_BLOCKS_TO_BYTES(val, bsize);
val = buf.f_bfree;
fsusage->free = SIGAR_FS_BLOCKS_TO_BYTES(val, bsize);
val = buf.f_bavail;
fsusage->avail = SIGAR_FS_BLOCKS_TO_BYTES(val, bsize);
fsusage->used = fsusage->total - fsusage->free;
fsusage->files = buf.f_files;
fsusage->free_files = buf.f_ffree;
return SIGAR_OK;
}
#endif
/*
* whittled down version of apr/file_info/{unix,win32}/filestat.c
* to fillin sigar_fileattrs_t
*/
#include "sigar_fileinfo.h"
#include "sigar_log.h"
#ifndef SIGAR_ZERO
#define SIGAR_ZERO(s) \
memset(s, '\0', sizeof(*(s)))
#endif
#ifdef WIN32
#include <windows.h>
sigar_uint64_t sigar_FileTimeToTime(FILETIME *ft);
#else
#include <string.h>
#endif
static const char* types[] = {
"none",
"regular",
"directory",
"character device",
"block device",
"pipe",
"symbolic link",
"socket",
"unknown"
};
SIGAR_DECLARE(const char *)
sigar_file_attrs_type_string_get(sigar_file_type_e type)
{
if ((type < SIGAR_FILETYPE_NOFILE) ||
(type > SIGAR_FILETYPE_UNKFILE))
{
type = SIGAR_FILETYPE_UNKFILE;
}
return types[type];
}
static const sigar_uint64_t perm_modes[] = {
SIGAR_UREAD, SIGAR_UWRITE, SIGAR_UEXECUTE,
SIGAR_GREAD, SIGAR_GWRITE, SIGAR_GEXECUTE,
SIGAR_WREAD, SIGAR_WWRITE, SIGAR_WEXECUTE
};
static const char perm_chars[] = "rwx";
SIGAR_DECLARE(char *)
sigar_file_attrs_permissions_string_get(sigar_uint64_t permissions,
char *str)
{
char *ptr = str;
int i=0, j=0;
for (i=0; i<9; i+=3) {
for (j=0; j<3; j++) {
if (permissions & perm_modes[i+j]) {
*ptr = perm_chars[j];
}
else {
*ptr = '-';
}
ptr++;
}
}
*ptr = '\0';
return str;
}
static const int perm_int[] = {
400, 200, 100,
40, 20, 10,
4, 2, 1
};
SIGAR_DECLARE(int)sigar_file_attrs_mode_get(sigar_uint64_t permissions)
{
int i=0;
int perms = 0;
/* no doubt there is some fancy bitshifting
* to convert, but this works fine.
*/
for (i=0; i<9; i++) {
if (permissions & perm_modes[i]) {
perms += perm_int[i];
}
}
return perms;
}
#define IS_DOTDIR(dir) \
((dir[0] == '.') && (!dir[1] || ((dir[1] == '.') && !dir[2])))
#define DIR_STAT_WARN() \
sigar_log_printf(sigar, SIGAR_LOG_WARN, \
"dir_stat: cannot stat `%s': %s", \
name, \
sigar_strerror(sigar, status))
#if defined(NETWARE)
int sigar_dir_stat_get(sigar_t *sigar,
const char *dir,
sigar_dir_stat_t *dirstats)
{
return SIGAR_ENOTIMPL;
}
int sigar_file_attrs_get(sigar_t *sigar,
const char *file,
sigar_file_attrs_t *fileattrs)
{
return SIGAR_ENOTIMPL;
}
int sigar_link_attrs_get(sigar_t *sigar,
const char *file,
sigar_file_attrs_t *fileattrs)
{
return SIGAR_ENOTIMPL;
}
#elif defined(WIN32)
#include <accctrl.h>
#include <aclapi.h>
static void fillin_fileattrs(sigar_file_attrs_t *finfo,
WIN32_FILE_ATTRIBUTE_DATA *wininfo,
int linkinfo)
{
DWORD *sizes = &wininfo->nFileSizeHigh;
finfo->atime = sigar_FileTimeToTime(&wininfo->ftLastAccessTime) / 1000;
finfo->ctime = sigar_FileTimeToTime(&wininfo->ftCreationTime) / 1000;
finfo->mtime = sigar_FileTimeToTime(&wininfo->ftLastWriteTime) / 1000;
finfo->size =
(sigar_uint64_t)sizes[1] | ((sigar_uint64_t)sizes[0] << 32);
if (linkinfo &&
(wininfo->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
finfo->type = SIGAR_FILETYPE_LNK;
}
else if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
finfo->type = SIGAR_FILETYPE_DIR;
}
else {
finfo->type = SIGAR_FILETYPE_REG;
}
}
static sigar_uint64_t convert_perms(ACCESS_MASK acc, sigar_uint64_t scope)
{
sigar_uint64_t perms = 0;
if (acc & FILE_EXECUTE) {
perms |= SIGAR_WEXECUTE;
}
if (acc & FILE_WRITE_DATA) {
perms |= SIGAR_WWRITE;
}
if (acc & FILE_READ_DATA) {
perms |= SIGAR_WREAD;
}
return (perms << scope);
}
static int get_security_info(sigar_t *sigar,
const char *file,
sigar_file_attrs_t *fileattrs)
{
DWORD retval;
PSID user = NULL, group = NULL, world = NULL;
PACL dacl = NULL;
PSECURITY_DESCRIPTOR pdesc = NULL;
SECURITY_INFORMATION sinfo =
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION;
TRUSTEE ident = {NULL, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID};
ACCESS_MASK acc;
SID_IDENTIFIER_AUTHORITY auth = SECURITY_WORLD_SID_AUTHORITY;
retval = GetNamedSecurityInfo((char *)file,
SE_FILE_OBJECT,
sinfo,
&user,
&group,
&dacl,
NULL,
&pdesc);
if (retval != ERROR_SUCCESS) {
return retval;
}
if (!AllocateAndInitializeSid(&auth, 1, SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0, &world))
{
world = NULL;
}
ident.TrusteeType = TRUSTEE_IS_USER;
ident.ptstrName = user;
if (GetEffectiveRightsFromAcl(dacl, &ident, &acc) == ERROR_SUCCESS) {
fileattrs->permissions |= convert_perms(acc, 8);
}
ident.TrusteeType = TRUSTEE_IS_GROUP;
ident.ptstrName = group;
if (GetEffectiveRightsFromAcl(dacl, &ident, &acc) == ERROR_SUCCESS) {
fileattrs->permissions |= convert_perms(acc, 4);
}
if (world) {
ident.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ident.ptstrName = world;
if (GetEffectiveRightsFromAcl(dacl, &ident, &acc) == ERROR_SUCCESS) {
fileattrs->permissions |= convert_perms(acc, 0);
}
}
if (world) {
FreeSid(world);
}
LocalFree(pdesc);
return SIGAR_OK;
}
static int fileattrs_get(sigar_t *sigar,
const char *file,
sigar_file_attrs_t *fileattrs,
int linkinfo)
{
BY_HANDLE_FILE_INFORMATION info;
WIN32_FILE_ATTRIBUTE_DATA attrs;
HANDLE handle;
DWORD flags;
SIGAR_ZERO(fileattrs);
if (!GetFileAttributesExA(file,
GetFileExInfoStandard,
&attrs))
{
return GetLastError();
}
fillin_fileattrs(fileattrs, &attrs, linkinfo);
flags = fileattrs->type == SIGAR_FILETYPE_DIR ?
FILE_FLAG_BACKUP_SEMANTICS :
FILE_ATTRIBUTE_NORMAL;
/**
* We need to set dwDesiredAccess to 0 to work in cases where GENERIC_READ can fail.
*
* see: http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx
*/
handle = CreateFile(file,
0,
0,
NULL,
OPEN_EXISTING,
flags,
NULL);
if (handle != INVALID_HANDLE_VALUE) {
if (GetFileInformationByHandle(handle, &info)) {
fileattrs->inode =
info.nFileIndexLow |
(info.nFileIndexHigh << 32);
fileattrs->device = info.dwVolumeSerialNumber;
fileattrs->nlink = info.nNumberOfLinks;
}
CloseHandle(handle);
}
get_security_info(sigar, file, fileattrs);
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_file_attrs_get(sigar_t *sigar,
const char *file,
sigar_file_attrs_t *fileattrs)
{
return fileattrs_get(sigar, file, fileattrs, 0);
}
SIGAR_DECLARE(int) sigar_link_attrs_get(sigar_t *sigar,
const char *file,
sigar_file_attrs_t *fileattrs)
{
return fileattrs_get(sigar, file, fileattrs, 1);
}
static __inline int file_type(char *file)
{
WIN32_FILE_ATTRIBUTE_DATA attrs;
if (!GetFileAttributesExA(file,
GetFileExInfoStandard,
&attrs))
{
return -1;
}
if (attrs.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
return SIGAR_FILETYPE_LNK;
}
else if (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
return SIGAR_FILETYPE_DIR;
}
else {
return SIGAR_FILETYPE_REG;
}
}
static int dir_stat_get(sigar_t *sigar,
const char *dir,
sigar_dir_stat_t *dirstats,
int recurse)
{
int status;
char name[SIGAR_PATH_MAX+1];
int len = strlen(dir);
int max = sizeof(name)-len-1;
char *ptr = name;
WIN32_FIND_DATA data;
HANDLE handle;
DWORD error;
char delim;
if (file_type((char *)dir) != SIGAR_FILETYPE_DIR) {
return ERROR_NO_MORE_FILES;
}
strncpy(name, dir, sizeof(name));
ptr += len;
if (strchr(dir, '/')) {
delim = '/';
}
else {
delim = '\\';
}
if (name[len] != delim) {
*ptr++ = delim;
len++;
max--;
}
/* e.g. "C:\sigar\*" */
name[len] = '*';
name[len+1] = '\0';
handle = FindFirstFile(name, &data);
if (handle == INVALID_HANDLE_VALUE) {
return GetLastError();
}
do {
/* skip '.' and '..' */
if (IS_DOTDIR(data.cFileName)) {
continue;
}
dirstats->disk_usage +=
(data.nFileSizeHigh * (MAXDWORD+1)) +
data.nFileSizeLow;
/* e.g. "C:\sigar\lib" */
strncpy(ptr, data.cFileName, max);
ptr[max] = '\0';
switch (file_type(name)) {
case -1:
break;
case SIGAR_FILETYPE_REG:
++dirstats->files;
break;
case SIGAR_FILETYPE_DIR:
++dirstats->subdirs;
if (recurse) {
status =
dir_stat_get(sigar, name,
dirstats, recurse);
if (status != SIGAR_OK) {
DIR_STAT_WARN();
}
}
break;
case SIGAR_FILETYPE_LNK:
++dirstats->symlinks;
break;
case SIGAR_FILETYPE_CHR:
++dirstats->chrdevs;
break;
case SIGAR_FILETYPE_BLK:
++dirstats->blkdevs;
break;
case SIGAR_FILETYPE_SOCK:
++dirstats->sockets;
break;
default:
++dirstats->total;
}
} while (FindNextFile(handle, &data));
error = GetLastError();
FindClose(handle);
if (error != ERROR_NO_MORE_FILES) {
return error;
}
dirstats->total =
dirstats->files +
dirstats->subdirs +
dirstats->symlinks +
dirstats->chrdevs +
dirstats->blkdevs +
dirstats->sockets;
return SIGAR_OK;
}
#else
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
static sigar_file_type_e filetype_from_mode(mode_t mode)
{
sigar_file_type_e type;
switch (mode & S_IFMT) {
case S_IFREG:
type = SIGAR_FILETYPE_REG; break;
case S_IFDIR:
type = SIGAR_FILETYPE_DIR; break;
case S_IFLNK:
type = SIGAR_FILETYPE_LNK; break;
case S_IFCHR:
type = SIGAR_FILETYPE_CHR; break;
case S_IFBLK:
type = SIGAR_FILETYPE_BLK; break;
#if defined(S_IFFIFO)
case S_IFFIFO:
type = SIGAR_FILETYPE_PIPE; break;
#endif
#if !defined(BEOS) && defined(S_IFSOCK)
case S_IFSOCK:
type = SIGAR_FILETYPE_SOCK; break;
#endif
default:
/* Work around missing S_IFxxx values above
* for Linux et al.
*/
#if !defined(S_IFFIFO) && defined(S_ISFIFO)
if (S_ISFIFO(mode)) {
type = SIGAR_FILETYPE_PIPE;
} else
#endif
#if !defined(BEOS) && !defined(S_IFSOCK) && defined(S_ISSOCK)
if (S_ISSOCK(mode)) {
type = SIGAR_FILETYPE_SOCK;
} else
#endif
type = SIGAR_FILETYPE_UNKFILE;
}
return type;
}
static sigar_uint64_t sigar_unix_mode2perms(mode_t mode)
{
sigar_uint64_t perms = 0;
if (mode & S_IRUSR)
perms |= SIGAR_UREAD;
if (mode & S_IWUSR)
perms |= SIGAR_UWRITE;
if (mode & S_IXUSR)
perms |= SIGAR_UEXECUTE;
if (mode & S_IRGRP)
perms |= SIGAR_GREAD;
if (mode & S_IWGRP)
perms |= SIGAR_GWRITE;
if (mode & S_IXGRP)
perms |= SIGAR_GEXECUTE;
if (mode & S_IROTH)
perms |= SIGAR_WREAD;
if (mode & S_IWOTH)
perms |= SIGAR_WWRITE;
if (mode & S_IXOTH)
perms |= SIGAR_WEXECUTE;
return perms;
}
static void copy_stat_info(sigar_file_attrs_t *fileattrs,
struct stat *info)
{
fileattrs->permissions = sigar_unix_mode2perms(info->st_mode);
fileattrs->type = filetype_from_mode(info->st_mode);
fileattrs->uid = info->st_uid;
fileattrs->gid = info->st_gid;
fileattrs->size = info->st_size;
fileattrs->inode = info->st_ino;
fileattrs->device = info->st_dev;
fileattrs->nlink = info->st_nlink;
fileattrs->atime = info->st_atime;
fileattrs->mtime = info->st_mtime;
fileattrs->ctime = info->st_ctime;
fileattrs->atime *= 1000;
fileattrs->mtime *= 1000;
fileattrs->ctime *= 1000;
}
int sigar_file_attrs_get(sigar_t *sigar,
const char *file,
sigar_file_attrs_t *fileattrs)
{
struct stat info;
if (stat(file, &info) == 0) {
copy_stat_info(fileattrs, &info);
return SIGAR_OK;
}
else {
return errno;
}
}
int sigar_link_attrs_get(sigar_t *sigar,
const char *file,
sigar_file_attrs_t *fileattrs)
{
struct stat info;
if (lstat(file, &info) == 0) {
copy_stat_info(fileattrs, &info);
return SIGAR_OK;
}
else {
return errno;
}
}
static int dir_stat_get(sigar_t *sigar,
const char *dir,
sigar_dir_stat_t *dirstats,
int recurse)
{
int status;
char name[SIGAR_PATH_MAX+1];
int len = strlen(dir);
int max = sizeof(name)-len-1;
char *ptr = name;
DIR *dirp = opendir(dir);
struct dirent *ent;
struct stat info;
#ifdef HAVE_READDIR_R
struct dirent dbuf;
#endif
if (!dirp) {
return errno;
}
strncpy(name, dir, sizeof(name));
ptr += len;
if (name[len] != '/') {
*ptr++ = '/';
len++;
max--;
}
#ifdef HAVE_READDIR_R
while (readdir_r(dirp, &dbuf, &ent) == 0) {
if (ent == NULL) {
break;
}
#else
while ((ent = readdir(dirp))) {
#endif
/* skip '.' and '..' */
if (IS_DOTDIR(ent->d_name)) {
continue;
}
strncpy(ptr, ent->d_name, max);
ptr[max] = '\0';
if (lstat(name, &info) != 0) {
continue;
}
dirstats->disk_usage += info.st_size;
switch (filetype_from_mode(info.st_mode)) {
case SIGAR_FILETYPE_REG:
++dirstats->files;
break;
case SIGAR_FILETYPE_DIR:
++dirstats->subdirs;
if (recurse) {
status =
dir_stat_get(sigar, name,
dirstats, recurse);
if (status != SIGAR_OK) {
DIR_STAT_WARN();
}
}
break;
case SIGAR_FILETYPE_LNK:
++dirstats->symlinks;
break;
case SIGAR_FILETYPE_CHR:
++dirstats->chrdevs;
break;
case SIGAR_FILETYPE_BLK:
++dirstats->blkdevs;
break;
case SIGAR_FILETYPE_SOCK:
++dirstats->sockets;
break;
default:
++dirstats->total;
}
}
dirstats->total =
dirstats->files +
dirstats->subdirs +
dirstats->symlinks +
dirstats->chrdevs +
dirstats->blkdevs +
dirstats->sockets;
closedir(dirp);
return SIGAR_OK;
}
#endif
SIGAR_DECLARE(int) sigar_dir_stat_get(sigar_t *sigar,
const char *dir,
sigar_dir_stat_t *dirstats)
{
SIGAR_ZERO(dirstats);
return dir_stat_get(sigar, dir, dirstats, 0);
}
SIGAR_DECLARE(int) sigar_dir_usage_get(sigar_t *sigar,
const char *dir,
sigar_dir_usage_t *dirusage)
{
SIGAR_ZERO(dirusage);
return dir_stat_get(sigar, dir, dirusage, 1);
}
Jump to Line
Something went wrong with that request. Please try again.