sigar/src/sigar.c

1444 lines
34 KiB
C

#include <errno.h>
#include "sigar.h"
#include "sigar_private.h"
#include "sigar_util.h"
#include "sigar_os.h"
#ifndef WIN32
#include <signal.h>
#endif
SIGAR_DECLARE(int) sigar_open(sigar_t **sigar)
{
int status = sigar_os_open(sigar);
if (status == SIGAR_OK) {
(*sigar)->pid = 0;
(*sigar)->ifconf_buf = NULL;
(*sigar)->ifconf_len = 0;
(*sigar)->log_level = -1; /* log nothing by default */
(*sigar)->log_impl = NULL;
(*sigar)->log_data = NULL;
}
return status;
}
SIGAR_DECLARE(int) sigar_close(sigar_t *sigar)
{
if (sigar->ifconf_buf) {
free(sigar->ifconf_buf);
}
return sigar_os_close(sigar);
}
#ifndef __linux__ /* linux has a special case */
SIGAR_DECLARE(sigar_pid_t) sigar_pid_get(sigar_t *sigar)
{
if (!sigar->pid) {
sigar->pid = getpid();
}
return sigar->pid;
}
#endif
SIGAR_DECLARE(int) sigar_proc_kill(sigar_pid_t pid, int signum)
{
#ifdef WIN32
int status = -1;
HANDLE proc =
OpenProcess(PROCESS_ALL_ACCESS,
TRUE, (DWORD)pid);
if (proc) {
switch (signum) {
case 0:
status = SIGAR_OK;
break;
default:
if (TerminateProcess(proc, signum)) {
status = SIGAR_OK;
}
break;
}
CloseHandle(proc);
if (status == SIGAR_OK) {
return SIGAR_OK;
}
}
return GetLastError();
#else
if (kill(pid, signum) == -1) {
return errno;
}
return SIGAR_OK;
#endif
}
static char *sigar_error_string(int err)
{
switch (err) {
case SIGAR_ENOTIMPL:
return "This function has not been implemented on this platform";
default:
return "Error string not specified yet";
}
}
SIGAR_DECLARE(char *) sigar_strerror(sigar_t *sigar, int err)
{
char *buf = NULL;
#ifdef WIN32
DWORD len;
#endif
if (err > SIGAR_OS_START_ERROR) {
if ((buf = sigar_os_error_string(err)) != NULL) {
return buf;
}
return "Unknown OS Error"; /* should never happen */
}
if (err > SIGAR_START_ERROR) {
return sigar_error_string(err);
}
#ifdef WIN32
len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
0, /* default language */
(LPTSTR)sigar->errbuf,
(DWORD)sizeof(sigar->errbuf),
NULL);
#else
#if defined(HAVE_STRERROR_R) && defined(HAVE_STRERROR_R_GLIBC)
/*
* strerror_r man page says:
* "The GNU version may, but need not, use the user supplied buffer"
*/
buf = strerror_r(err, sigar->errbuf, sizeof(sigar->errbuf));
#elif defined(HAVE_STRERROR_R)
if (strerror_r(err, sigar->errbuf, sizeof(sigar->errbuf)) < 0) {
buf = "Unknown Error";
}
#else
/* strerror() is thread safe on solaris and hpux */
buf = strerror(err);
#endif
if (buf != NULL) {
SIGAR_SSTRCPY(sigar->errbuf, buf);
}
#endif
return sigar->errbuf;
}
#include <stdio.h> /* for sprintf */
SIGAR_DECLARE(int) sigar_uptime_string(sigar_t *sigar,
sigar_uptime_t *uptime,
char *buffer,
int buflen)
{
char *ptr = buffer;
int minutes, hours, days, offset = 0;
/* XXX: get rid of sprintf and/or check for overflow */
days = uptime->uptime / (60*60*24);
if (days) {
offset += sprintf(ptr + offset, "%d day%s, ",
days, (days > 1) ? "s" : "");
}
minutes = (int)uptime->uptime / 60;
hours = minutes / 60;
hours = hours % 24;
minutes = minutes % 60;
if (hours) {
offset += sprintf(ptr + offset, "%2d:%02d",
hours, minutes);
}
else {
offset += sprintf(ptr + offset, "%d min", minutes);
}
return SIGAR_OK;
}
/* copy apr_strfsize */
SIGAR_DECLARE(char *) sigar_format_size(sigar_uint64_t size, char *buf)
{
const char ord[] = "KMGTPE";
const char *o = ord;
int remain;
if (size == SIGAR_FIELD_NOTIMPL) {
buf[0] = '-';
buf[1] = '\0';
return buf;
}
if (size < 973) {
sprintf(buf, "%3d ", (int) size);
return buf;
}
do {
remain = (int)(size & 1023);
size >>= 10;
if (size >= 973) {
++o;
continue;
}
if (size < 9 || (size == 9 && remain < 973)) {
if ((remain = ((remain * 5) + 256) / 512) >= 10) {
++size;
remain = 0;
}
sprintf(buf, "%d.%d%c", (int) size, remain, *o);
return buf;
}
if (remain >= 512) {
++size;
}
sprintf(buf, "%3d%c", (int) size, *o);
return buf;
} while (1);
}
#ifndef WIN32
#include <pwd.h>
#include <grp.h>
int sigar_user_name_get(sigar_t *sigar, int uid, char *buf, int buflen)
{
struct passwd *pw;
/* XXX cache lookup */
# ifdef HAVE_GETPWUID_R
struct passwd pwbuf;
char buffer[512];
if (getpwuid_r(uid, &pwbuf, buffer, sizeof(buffer), &pw) != 0) {
return errno;
}
# else
if ((pw = getpwuid(uid)) == NULL) {
return errno;
}
# endif
strncpy(buf, pw->pw_name, buflen);
buf[buflen-1] = '\0';
return SIGAR_OK;
}
int sigar_group_name_get(sigar_t *sigar, int gid, char *buf, int buflen)
{
struct group *gr;
/* XXX cache lookup */
# ifdef HAVE_GETGRGID_R
struct group grbuf;
char buffer[512];
if (getgrgid_r(gid, &grbuf, buffer, sizeof(buffer), &gr) != 0) {
return errno;
}
# else
if ((gr = getgrgid(gid)) == NULL) {
return errno;
}
# endif
if (gr && gr->gr_name) {
strncpy(buf, gr->gr_name, buflen);
}
else {
/* seen on linux.. apache httpd.conf has:
* Group #-1
* results in uid == -1 and gr == NULL.
* wtf getgrgid_r doesnt fail instead?
*/
sprintf(buf, "%d", gid);
}
buf[buflen-1] = '\0';
return SIGAR_OK;
}
int sigar_user_id_get(sigar_t *sigar, const char *name, int *uid)
{
/* XXX cache lookup */
struct passwd *pw;
# ifdef HAVE_GETPWNAM_R
struct passwd pwbuf;
char buf[512];
if (getpwnam_r(name, &pwbuf, buf, sizeof(buf), &pw) != 0) {
return errno;
}
# else
if (!(pw = getpwnam(name))) {
return errno;
}
# endif
*uid = (int)pw->pw_uid;
return SIGAR_OK;
}
SIGAR_DECLARE(int)
sigar_proc_cred_name_get(sigar_t *sigar, sigar_pid_t pid,
sigar_proc_cred_name_t *proccredname)
{
sigar_proc_cred_t cred;
int status = sigar_proc_cred_get(sigar, pid, &cred);
if (status != SIGAR_OK) {
return status;
}
status = sigar_user_name_get(sigar, cred.uid,
proccredname->user,
sizeof(proccredname->user));
if (status != SIGAR_OK) {
return status;
}
status = sigar_group_name_get(sigar, cred.gid,
proccredname->group,
sizeof(proccredname->group));
return status;
}
#endif /* WIN32 */
int sigar_proc_list_create(sigar_proc_list_t *proclist)
{
proclist->number = 0;
proclist->size = SIGAR_PROC_LIST_MAX;
proclist->data = malloc(sizeof(*(proclist->data)) *
proclist->size);
return SIGAR_OK;
}
int sigar_proc_list_grow(sigar_proc_list_t *proclist)
{
proclist->data = realloc(proclist->data,
sizeof(*(proclist->data)) *
(proclist->size + SIGAR_PROC_LIST_MAX));
proclist->size += SIGAR_PROC_LIST_MAX;
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_proc_list_destroy(sigar_t *sigar,
sigar_proc_list_t *proclist)
{
if (proclist->size) {
free(proclist->data);
proclist->number = proclist->size = 0;
}
return SIGAR_OK;
}
int sigar_proc_args_create(sigar_proc_args_t *procargs)
{
procargs->number = 0;
procargs->size = SIGAR_PROC_ARGS_MAX;
procargs->data = malloc(sizeof(*(procargs->data)) *
procargs->size);
return SIGAR_OK;
}
int sigar_proc_args_grow(sigar_proc_args_t *procargs)
{
procargs->data = realloc(procargs->data,
sizeof(*(procargs->data)) *
(procargs->size + SIGAR_PROC_ARGS_MAX));
procargs->size += SIGAR_PROC_ARGS_MAX;
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_proc_args_destroy(sigar_t *sigar,
sigar_proc_args_t *procargs)
{
unsigned int i;
if (procargs->size) {
for (i=0; i<procargs->number; i++) {
free(procargs->data[i]);
}
free(procargs->data);
procargs->number = procargs->size = 0;
}
return SIGAR_OK;
}
int sigar_file_system_list_create(sigar_file_system_list_t *fslist)
{
fslist->number = 0;
fslist->size = SIGAR_FS_MAX;
fslist->data = malloc(sizeof(*(fslist->data)) *
fslist->size);
return SIGAR_OK;
}
int sigar_file_system_list_grow(sigar_file_system_list_t *fslist)
{
fslist->data = realloc(fslist->data,
sizeof(*(fslist->data)) *
(fslist->size + SIGAR_FS_MAX));
fslist->size += SIGAR_FS_MAX;
return SIGAR_OK;
}
/* indexed with sigar_file_system_type_e */
static const char *fstype_names[] = {
"unknown", "none", "local", "remote", "ram", "cdrom", "swap"
};
static int sigar_common_fs_type_get(sigar_file_system_t *fsp)
{
char *type = fsp->sys_type_name;
switch (*type) {
case 'n':
if (strEQ(type, "nfs")) {
fsp->type = SIGAR_FSTYPE_NETWORK;
}
break;
case 's':
if (strEQ(type, "smbfs")) { /* samba */
fsp->type = SIGAR_FSTYPE_NETWORK;
}
else if (strEQ(type, "swap")) {
fsp->type = SIGAR_FSTYPE_SWAP;
}
break;
case 'a':
if (strEQ(type, "afs")) {
fsp->type = SIGAR_FSTYPE_NETWORK;
}
break;
case 'i':
if (strEQ(type, "iso9660")) {
fsp->type = SIGAR_FSTYPE_CDROM;
}
break;
case 'm':
if (strEQ(type, "msdos") || strEQ(type, "minix")) {
fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
}
break;
case 'h':
if (strEQ(type, "hpfs")) {
fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
}
break;
case 'v':
if (strEQ(type, "vfat")) {
fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
}
break;
}
return fsp->type;
}
void sigar_fs_type_get(sigar_file_system_t *fsp)
{
if (!(fsp->type || /* already set */
sigar_os_fs_type_get(fsp) || /* try os specifics first */
sigar_common_fs_type_get(fsp))) /* try common ones last */
{
fsp->type = SIGAR_FSTYPE_NONE;
}
if (fsp->type >= SIGAR_FSTYPE_MAX) {
fsp->type = SIGAR_FSTYPE_NONE;
}
strcpy(fsp->type_name, fstype_names[fsp->type]);
}
SIGAR_DECLARE(int)
sigar_file_system_list_destroy(sigar_t *sigar,
sigar_file_system_list_t *fslist)
{
if (fslist->size) {
free(fslist->data);
fslist->number = fslist->size = 0;
}
return SIGAR_OK;
}
int sigar_cpu_info_list_create(sigar_cpu_info_list_t *cpu_infos)
{
cpu_infos->number = 0;
cpu_infos->size = SIGAR_CPU_INFO_MAX;
cpu_infos->data = malloc(sizeof(*(cpu_infos->data)) *
cpu_infos->size);
return SIGAR_OK;
}
int sigar_cpu_info_list_grow(sigar_cpu_info_list_t *cpu_infos)
{
cpu_infos->data = realloc(cpu_infos->data,
sizeof(*(cpu_infos->data)) *
(cpu_infos->size + SIGAR_CPU_INFO_MAX));
cpu_infos->size += SIGAR_CPU_INFO_MAX;
return SIGAR_OK;
}
SIGAR_DECLARE(int)
sigar_cpu_info_list_destroy(sigar_t *sigar,
sigar_cpu_info_list_t *cpu_infos)
{
if (cpu_infos->size) {
free(cpu_infos->data);
cpu_infos->number = cpu_infos->size = 0;
}
return SIGAR_OK;
}
int sigar_cpu_list_create(sigar_cpu_list_t *cpulist)
{
cpulist->number = 0;
cpulist->size = SIGAR_CPU_INFO_MAX;
cpulist->data = malloc(sizeof(*(cpulist->data)) *
cpulist->size);
return SIGAR_OK;
}
int sigar_cpu_list_grow(sigar_cpu_list_t *cpulist)
{
cpulist->data = realloc(cpulist->data,
sizeof(*(cpulist->data)) *
(cpulist->size + SIGAR_CPU_INFO_MAX));
cpulist->size += SIGAR_CPU_INFO_MAX;
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_cpu_list_destroy(sigar_t *sigar,
sigar_cpu_list_t *cpulist)
{
if (cpulist->size) {
free(cpulist->data);
cpulist->number = cpulist->size = 0;
}
return SIGAR_OK;
}
int sigar_net_route_list_create(sigar_net_route_list_t *routelist)
{
routelist->number = 0;
routelist->size = SIGAR_NET_ROUTE_LIST_MAX;
routelist->data = malloc(sizeof(*(routelist->data)) *
routelist->size);
return SIGAR_OK;
}
int sigar_net_route_list_grow(sigar_net_route_list_t *routelist)
{
routelist->data =
realloc(routelist->data,
sizeof(*(routelist->data)) *
(routelist->size + SIGAR_NET_ROUTE_LIST_MAX));
routelist->size += SIGAR_NET_ROUTE_LIST_MAX;
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_net_route_list_destroy(sigar_t *sigar,
sigar_net_route_list_t *routelist)
{
if (routelist->size) {
free(routelist->data);
routelist->number = routelist->size = 0;
}
return SIGAR_OK;
}
int sigar_net_interface_list_create(sigar_net_interface_list_t *iflist)
{
iflist->number = 0;
iflist->size = SIGAR_NET_IFLIST_MAX;
iflist->data = malloc(sizeof(*(iflist->data)) *
iflist->size);
return SIGAR_OK;
}
int sigar_net_interface_list_grow(sigar_net_interface_list_t *iflist)
{
iflist->data = realloc(iflist->data,
sizeof(*(iflist->data)) *
(iflist->size + SIGAR_NET_IFLIST_MAX));
iflist->size += SIGAR_NET_IFLIST_MAX;
return SIGAR_OK;
}
SIGAR_DECLARE(int)
sigar_net_interface_list_destroy(sigar_t *sigar,
sigar_net_interface_list_t *iflist)
{
unsigned int i;
if (iflist->size) {
for (i=0; i<iflist->number; i++) {
free(iflist->data[i]);
}
free(iflist->data);
iflist->number = iflist->size = 0;
}
return SIGAR_OK;
}
int sigar_net_connection_list_create(sigar_net_connection_list_t *connlist)
{
connlist->number = 0;
connlist->size = SIGAR_NET_CONNLIST_MAX;
connlist->data = malloc(sizeof(*(connlist->data)) *
connlist->size);
return SIGAR_OK;
}
int sigar_net_connection_list_grow(sigar_net_connection_list_t *connlist)
{
connlist->data =
realloc(connlist->data,
sizeof(*(connlist->data)) *
(connlist->size + SIGAR_NET_CONNLIST_MAX));
connlist->size += SIGAR_NET_CONNLIST_MAX;
return SIGAR_OK;
}
SIGAR_DECLARE(int)
sigar_net_connection_list_destroy(sigar_t *sigar,
sigar_net_connection_list_t *connlist)
{
if (connlist->size) {
free(connlist->data);
connlist->number = connlist->size = 0;
}
return SIGAR_OK;
}
SIGAR_DECLARE(const char *)sigar_net_connection_type_get(int type)
{
switch (type) {
case SIGAR_NETCONN_TCP:
return "tcp";
case SIGAR_NETCONN_UDP:
return "udp";
case SIGAR_NETCONN_RAW:
return "raw";
case SIGAR_NETCONN_UNIX:
return "unix";
default:
return "unknown";
}
}
int sigar_who_list_create(sigar_who_list_t *wholist)
{
wholist->number = 0;
wholist->size = SIGAR_WHO_LIST_MAX;
wholist->data = malloc(sizeof(*(wholist->data)) *
wholist->size);
return SIGAR_OK;
}
int sigar_who_list_grow(sigar_who_list_t *wholist)
{
wholist->data = realloc(wholist->data,
sizeof(*(wholist->data)) *
(wholist->size + SIGAR_WHO_LIST_MAX));
wholist->size += SIGAR_WHO_LIST_MAX;
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_who_list_destroy(sigar_t *sigar,
sigar_who_list_t *wholist)
{
if (wholist->size) {
free(wholist->data);
wholist->number = wholist->size = 0;
}
return SIGAR_OK;
}
#ifdef WIN32
SIGAR_DECLARE(int) sigar_who_list_get(sigar_t *sigar,
sigar_who_list_t *wholist)
{
return SIGAR_ENOTIMPL;
}
#else
#ifdef __sun
# include <utmpx.h>
# define SIGAR_UTMP_FILE _UTMPX_FILE
# define ut_time ut_tv.tv_sec
#else
# include <utmp.h>
# ifdef UTMP_FILE
# define SIGAR_UTMP_FILE UTMP_FILE
# else
# define SIGAR_UTMP_FILE _PATH_UTMP
# endif
#endif
#if defined(__FreeBSD__) || defined(DARWIN)
# define ut_user ut_name
#endif
#define WHOCPY(dest, src) \
SIGAR_SSTRCPY(dest, src); \
if (sizeof(src) < sizeof(dest)) \
dest[sizeof(src)] = '\0'
int sigar_who_list_get(sigar_t *sigar,
sigar_who_list_t *wholist)
{
FILE *fp;
#ifdef __sun
struct utmpx ut;
#else
struct utmp ut;
#endif
if (!(fp = fopen(SIGAR_UTMP_FILE, "r"))) {
return errno;
}
sigar_who_list_create(wholist);
while (fread(&ut, sizeof(ut), 1, fp) == 1) {
sigar_who_t *who;
if (*ut.ut_name == '\0') {
continue;
}
#ifdef USER_PROCESS
if (ut.ut_type != USER_PROCESS) {
continue;
}
#endif
SIGAR_WHO_LIST_GROW(wholist);
who = &wholist->data[wholist->number++];
WHOCPY(who->user, ut.ut_user);
WHOCPY(who->device, ut.ut_line);
WHOCPY(who->host, ut.ut_host);
who->time = ut.ut_time;
}
fclose(fp);
return SIGAR_OK;
}
#endif
void sigar_hwaddr_format(char *buff, unsigned char *ptr)
{
sprintf(buff, "%02X:%02X:%02X:%02X:%02X:%02X",
(ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
(ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377));
}
#if !defined(WIN32) && !defined(DARWIN) && !defined(__FreeBSD__)
/* XXX: prolly will be moving these stuffs into os_net.c */
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <net/if.h>
#ifndef SIOCGIFCONF
#include <sys/sockio.h>
#endif
#if defined(_AIX) || defined(__osf__) /* good buddies */
#include <net/if_dl.h>
static void hwaddr_aix_lookup(sigar_t *sigar, sigar_net_interface_config_t *ifconfig)
{
char *ent, *end;
struct ifreq *ifr;
/* XXX: assumes sigar_net_interface_list_get has been called */
end = sigar->ifconf_buf + sigar->ifconf_len;
for (ent = sigar->ifconf_buf;
ent < end;
ent += sizeof(*ifr))
{
ifr = (struct ifreq *)ent;
if (ifr->ifr_addr.sa_family != AF_LINK) {
continue;
}
if (strEQ(ifr->ifr_name, ifconfig->name)) {
struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr;
sigar_hwaddr_format(ifconfig->hwaddr,
(unsigned char *)LLADDR(sdl));
return;
}
}
sigar_hwaddr_set_null(ifconfig);
}
#elif !defined(SIOCGIFHWADDR)
#include <net/if_arp.h>
static void hwaddr_arp_lookup(sigar_net_interface_config_t *ifconfig, int sock)
{
struct arpreq areq;
struct sockaddr_in *sa;
memset(&areq, 0, sizeof(areq));
sa = (struct sockaddr_in *)&areq.arp_pa;
sa->sin_family = AF_INET;
sa->sin_addr.s_addr = ifconfig->address;
if (ioctl(sock, SIOCGARP, &areq) < 0) {
/* ho-hum */
memset(&areq.arp_ha.sa_data, '\0', sizeof(areq.arp_ha.sa_data));
}
sigar_hwaddr_format(ifconfig->hwaddr,
(unsigned char *)areq.arp_ha.sa_data);
}
#endif
int sigar_net_interface_config_get(sigar_t *sigar, const char *name,
sigar_net_interface_config_t *ifconfig)
{
int sock;
struct ifreq ifr;
SIGAR_ZERO(ifconfig);
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
return errno;
}
SIGAR_SSTRCPY(ifconfig->name, name);
SIGAR_SSTRCPY(ifr.ifr_name, name);
#define ifr_s_addr(ifr) \
((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr
if (!ioctl(sock, SIOCGIFADDR, &ifr)) {
ifconfig->address = ifr_s_addr(ifr);
}
else {
/* if this one failed, so will everything else */
close(sock);
return errno;
}
if (!ioctl(sock, SIOCGIFNETMASK, &ifr)) {
ifconfig->netmask = ifr_s_addr(ifr);
}
if (!ioctl(sock, SIOCGIFFLAGS, &ifr)) {
ifconfig->flags = ifr.ifr_flags;
#ifdef __linux__
/*
* XXX: should just define SIGAR_IFF_*
* and test IFF_* bits on given platform.
* this is the only diff between solaris/hpux/linux
* for the flags we care about.
*
*/
if (ifconfig->flags & IFF_MULTICAST) {
ifconfig->flags |= SIGAR_IFF_MULTICAST;
}
else {
/* 0x800 == IFF_SLAVE on linux */
ifconfig->flags &= ~SIGAR_IFF_MULTICAST;
}
#endif
}
else {
/* should always be able to get flags for existing device */
/* other ioctls may fail if device is not enabled: ok */
close(sock);
return errno;
}
if (ifconfig->flags & IFF_LOOPBACK) {
ifconfig->destination = ifconfig->address;
ifconfig->broadcast = 0;
sigar_hwaddr_set_null(ifconfig);
}
else {
if (!ioctl(sock, SIOCGIFDSTADDR, &ifr)) {
ifconfig->destination = ifr_s_addr(ifr);
}
if (!ioctl(sock, SIOCGIFBRDADDR, &ifr)) {
ifconfig->broadcast = ifr_s_addr(ifr);
}
#if defined(SIOCGIFHWADDR)
if (!ioctl(sock, SIOCGIFHWADDR, &ifr)) {
sigar_hwaddr_format(ifconfig->hwaddr, ifr.ifr_hwaddr.sa_data);
}
#elif defined(_AIX) || defined(__osf__)
hwaddr_aix_lookup(sigar, ifconfig);
#else
hwaddr_arp_lookup(ifconfig, sock);
#endif
}
#ifdef __linux__
if (!ioctl(sock, SIOCGIFMTU, &ifr)) {
ifconfig->mtu = ifr.ifr_mtu;
}
#else
ifconfig->mtu = 0; /*XXX*/
#endif
if (!ioctl(sock, SIOCGIFMETRIC, &ifr)) {
ifconfig->metric = ifr.ifr_metric ? ifr.ifr_metric : 1;
}
close(sock);
return SIGAR_OK;
}
#ifdef _AIX
# define MY_SIOCGIFCONF CSIOCGIFCONF
#else
# define MY_SIOCGIFCONF SIOCGIFCONF
#endif
#ifdef __osf__
static int sigar_netif_configured(sigar_t *sigar, char *name)
{
int status;
sigar_net_interface_config_t ifconfig;
status = sigar_net_interface_config_get(sigar, name, &ifconfig);
return status == SIGAR_OK;
}
#endif
int sigar_net_interface_list_get(sigar_t *sigar,
sigar_net_interface_list_t *iflist)
{
int n, lastlen=0;
struct ifreq *ifr;
struct ifconf ifc;
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
return errno;
}
for (;;) {
if (!sigar->ifconf_buf || lastlen) {
sigar->ifconf_len += sizeof(struct ifreq) * SIGAR_NET_IFLIST_MAX;
sigar->ifconf_buf = realloc(sigar->ifconf_buf, sigar->ifconf_len);
}
ifc.ifc_len = sigar->ifconf_len;
ifc.ifc_buf = sigar->ifconf_buf;
if (ioctl(sock, MY_SIOCGIFCONF, &ifc) < 0) {
/* EINVAL should mean num_interfaces > ifc.ifc_len */
if ((errno != EINVAL) ||
(lastlen == ifc.ifc_len))
{
free(ifc.ifc_buf);
return errno;
}
}
if (ifc.ifc_len < sigar->ifconf_len) {
break; /* got em all */
}
if (ifc.ifc_len != lastlen) {
/* might be more */
lastlen = ifc.ifc_len;
continue;
}
break;
}
close(sock);
iflist->number = 0;
iflist->size = ifc.ifc_len;
iflist->data = malloc(sizeof(*(iflist->data)) *
iflist->size);
ifr = ifc.ifc_req;
for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq), ifr++) {
#if defined(_AIX) || defined(__osf__) /* pass the bourbon */
if (ifr->ifr_addr.sa_family != AF_LINK) {
/* XXX: dunno if this is right.
* otherwise end up with two 'en0' and three 'lo0'
* with the same ip address.
*/
continue;
}
# ifdef __osf__
/* weed out "sl0", "tun0" and the like */
/* XXX must be a better way to check this */
if (!sigar_netif_configured(sigar, ifr->ifr_name)) {
continue;
}
# endif
#endif
iflist->data[iflist->number++] = strdup(ifr->ifr_name);
}
return SIGAR_OK;
}
#endif /* WIN32 */
#ifndef WIN32
#include <netinet/in.h>
#endif
/* threadsafe alternative to inet_ntoa (inet_ntop4 from apr) */
SIGAR_DECLARE(int) sigar_inet_ntoa(sigar_t *sigar,
sigar_uint64_t address,
char *addr_str)
{
char *next=addr_str;
int n=0;
const unsigned char *src;
struct in_addr addr;
addr.s_addr = address;
src = (const unsigned char *)&addr.s_addr;
do {
unsigned char u = *src++;
if (u > 99) {
*next++ = '0' + u/100;
u %= 100;
*next++ = '0' + u/10;
u %= 10;
}
else if (u > 9) {
*next++ = '0' + u/10;
u %= 10;
}
*next++ = '0' + u;
*next++ = '.';
n++;
} while (n < 4);
*--next = 0;
return SIGAR_OK;
}
static int fqdn_ip_get(sigar_t *sigar, char *name)
{
int i, status;
sigar_net_interface_list_t iflist;
if ((status = sigar_net_interface_list_get(sigar, &iflist)) != SIGAR_OK) {
return status;
}
for (i=0; i<iflist.number; i++) {
sigar_net_interface_config_t ifconfig;
status = sigar_net_interface_config_get(sigar,
iflist.data[i], &ifconfig);
if ((status != SIGAR_OK) ||
(ifconfig.flags & SIGAR_IFF_LOOPBACK))
{
continue;
}
sigar_inet_ntoa(sigar, ifconfig.address, name);
sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
"[fqdn] using ip address '%s' for fqdn",
name);
break;
}
sigar_net_interface_list_destroy(sigar, &iflist);
return SIGAR_OK;
}
#ifdef WIN32
#else
#include <netdb.h>
#endif
#define IS_FQDN(name) \
strchr(name, '.')
#define H_ALIAS_MATCH(alias, name) \
(IS_FQDN(alias) && strnEQ(alias, name, strlen(name)))
#define FQDN_SET(fqdn) \
SIGAR_STRNCPY(name, fqdn, namelen)
SIGAR_DECLARE(int) sigar_fqdn_get(sigar_t *sigar, char *name, int namelen)
{
struct hostent *p;
char domain[SIGAR_FQDN_LEN + 1];
#ifdef WIN32
int status = sigar_wsa_init(sigar);
if (status != SIGAR_OK) {
return status;
}
#endif
if (gethostname(name, namelen - 1) != 0) {
sigar_log_printf(sigar, SIGAR_LOG_ERROR,
"[fqdn] gethostname failed: %s",
sigar_strerror(sigar, errno));
return errno;
}
else {
if (SIGAR_LOG_IS_DEBUG(sigar)) {
sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
"[fqdn] gethostname() returned: '%s'",
name);
}
}
/* XXX use _r versions of these functions. */
if (!(p = gethostbyname(name))) {
if (SIGAR_LOG_IS_DEBUG(sigar)) {
sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
"[fqdn] gethostbyname(%s) failed: %s",
name, sigar_strerror(sigar, errno));
}
if (!IS_FQDN(name)) {
fqdn_ip_get(sigar, name);
}
return SIGAR_OK;
}
if (IS_FQDN(p->h_name)) {
FQDN_SET(p->h_name);
sigar_log(sigar, SIGAR_LOG_DEBUG,
"[fqdn] resolved using gethostbyname.h_name");
return SIGAR_OK;
}
else {
sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
"[fqdn] unresolved using gethostbyname.h_name");
}
if (p->h_aliases) {
int i;
for (i=0; p->h_aliases[i]; i++) {
if (H_ALIAS_MATCH(p->h_aliases[i], p->h_name)) {
FQDN_SET(p->h_aliases[i]);
sigar_log(sigar, SIGAR_LOG_DEBUG,
"[fqdn] resolved using gethostbyname.h_aliases");
return SIGAR_OK;
}
}
}
sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
"[fqdn] unresolved using gethostbyname.h_aliases");
if (p->h_addr_list) {
int i,j;
for (i=0; p->h_addr_list[i]; i++) {
struct hostent *q =
gethostbyaddr(p->h_addr_list[i],
p->h_length,
p->h_addrtype);
if (IS_FQDN(q->h_name)) {
FQDN_SET(q->h_name);
sigar_log(sigar, SIGAR_LOG_DEBUG,
"[fqdn] resolved using gethostbyaddr.h_name");
return SIGAR_OK;
}
else {
for (j=0; q->h_aliases[j]; j++) {
if (H_ALIAS_MATCH(q->h_aliases[j], q->h_name)) {
FQDN_SET(q->h_aliases[j]);
sigar_log(sigar, SIGAR_LOG_DEBUG,
"[fqdn] resolved using "
"gethostbyaddr.h_aliases");
return SIGAR_OK;
}
}
}
}
}
sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
"[fqdn] unresolved using gethostbyname.h_addr_list");
#ifndef WIN32
if (!IS_FQDN(name) && /* e.g. aix gethostname is already fqdn */
(getdomainname(domain, sizeof(domain) - 1) == 0) &&
(domain[0] != '\0') &&
(domain[0] != '(')) /* linux default is "(none)" */
{
/* sprintf(name, "%s.%s", name, domain); */
char *ptr = name;
int len = strlen(name);
ptr += len;
*ptr++ = '.';
namelen -= (len+1);
SIGAR_STRNCPY(ptr, domain, namelen);
sigar_log(sigar, SIGAR_LOG_DEBUG,
"[fqdn] resolved using getdomainname");
}
else {
sigar_log(sigar, SIGAR_LOG_DEBUG,
"[fqdn] getdomainname failed");
}
#endif
if (!IS_FQDN(name)) {
fqdn_ip_get(sigar, name);
}
return SIGAR_OK;
}
#ifndef MAX_STRING_LEN
#define MAX_STRING_LEN 8192
#endif
#ifdef WIN32
/* The windows version of getPasswordNative was lifted from apr */
SIGAR_DECLARE(char *) sigar_password_get(const char *prompt)
{
static char password[MAX_STRING_LEN];
int n = 0;
int ch;
fputs(prompt, stderr);
fflush(stderr);
while ((ch = _getch()) != '\r') {
if (ch == EOF) /* EOF */ {
return NULL;
}
else if (ch == 0 || ch == 0xE0) {
/* FN Keys (0 or E0) are a sentinal for a FN code */
ch = (ch << 4) | _getch();
/* Catch {DELETE}, {<--}, Num{DEL} and Num{<--} */
if ((ch == 0xE53 || ch == 0xE4B || ch == 0x053 || ch == 0x04b) && n) {
password[--n] = '\0';
fputs("\b \b", stderr);
fflush(stderr);
}
else {
fputc('\a', stderr);
fflush(stderr);
}
}
else if ((ch == '\b' || ch == 127) && n) /* BS/DEL */ {
password[--n] = '\0';
fputs("\b \b", stderr);
fflush(stderr);
}
else if (ch == 3) /* CTRL+C */ {
/* _getch() bypasses Ctrl+C but not Ctrl+Break detection! */
fputs("^C\n", stderr);
fflush(stderr);
exit(-1);
}
else if (ch == 26) /* CTRL+Z */ {
fputs("^Z\n", stderr);
fflush(stderr);
return NULL;
}
else if (ch == 27) /* ESC */ {
fputc('\n', stderr);
fputs(prompt, stderr);
fflush(stderr);
n = 0;
}
else if ((n < sizeof(password) - 1) && !iscntrl(ch)) {
password[n++] = ch;
fputc(' ', stderr);
fflush(stderr);
}
else {
fputc('\a', stderr);
fflush(stderr);
}
}
fputc('\n', stderr);
fflush(stderr);
password[n] = '\0';
return password;
}
#else
/* linux/hpux/solaris getpass() prototype lives here */
#include <unistd.h>
#include <termios.h>
/* from apr_getpass.c */
#if defined(SIGAR_HPUX)
# define getpass termios_getpass
#elif defined(SIGAR_SOLARIS)
# define getpass getpassphrase
#endif
#ifdef SIGAR_HPUX
static char *termios_getpass(const char *prompt)
{
struct termios attr;
static char password[MAX_STRING_LEN];
unsigned int n=0;
fputs(prompt, stderr);
fflush(stderr);
if (tcgetattr(STDIN_FILENO, &attr) != 0) {
return NULL;
}
attr.c_lflag &= ~(ECHO);
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr) != 0) {
return NULL;
}
while ((password[n] = getchar()) != '\n') {
if (n < (sizeof(password) - 1) &&
(password[n] >= ' ') &&
(password[n] <= '~'))
{
n++;
}
else {
fprintf(stderr, "\n");
fputs(prompt, stderr);
fflush(stderr);
n = 0;
}
}
password[n] = '\0';
printf("\n");
if (n > (MAX_STRING_LEN - 1)) {
password[MAX_STRING_LEN - 1] = '\0';
}
attr.c_lflag |= ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &attr);
return (char *)&password;
}
#endif
SIGAR_DECLARE(char *) sigar_password_get(const char *prompt)
{
char *buf = NULL;
/* the linux version of getpass prints the prompt to the tty; ok.
* the solaris version prints the prompt to stderr; not ok.
* so print the prompt to /dev/tty ourselves if possible (always should be)
*/
FILE *tty = NULL;
if ((tty = fopen("/dev/tty", "w"))) {
fprintf(tty, "%s", prompt);
fflush(tty);
buf = getpass(tty ? "" : prompt);
fclose(tty);
}
return buf;
}
#endif /* WIN32 */