395 lines
10 KiB
C
395 lines
10 KiB
C
|
#include <erl_driver.h>
|
||
|
#include <ei.h>
|
||
|
#include <ctype.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "sigar.h"
|
||
|
#include "sigar_fileinfo.h"
|
||
|
#include "sigar_format.h"
|
||
|
#include "sigar_ptql.h"
|
||
|
|
||
|
typedef struct {
|
||
|
ErlDrvPort port;
|
||
|
sigar_t *sigar;
|
||
|
} sigar_drv_t;
|
||
|
|
||
|
static ErlDrvData start(ErlDrvPort port, char *cmd) {
|
||
|
sigar_drv_t *sd = (sigar_drv_t *)driver_alloc(sizeof(*sd));
|
||
|
int status;
|
||
|
|
||
|
status = sigar_open(&sd->sigar);
|
||
|
if (status != SIGAR_OK) {
|
||
|
sd->sigar = NULL;
|
||
|
driver_failure_posix(port, status);
|
||
|
}
|
||
|
|
||
|
sd->port = port;
|
||
|
|
||
|
return (ErlDrvData)sd;
|
||
|
}
|
||
|
|
||
|
static void stop(ErlDrvData handle) {
|
||
|
sigar_drv_t *driver_data = (sigar_drv_t *)handle;
|
||
|
if (driver_data->sigar) {
|
||
|
sigar_close(driver_data->sigar);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef SIGAR_64BIT
|
||
|
#define str2pid(value) strtoull(value, NULL, 10)
|
||
|
#else
|
||
|
#define str2pid(value) strtoul(value, NULL, 10)
|
||
|
#endif
|
||
|
|
||
|
static sigar_pid_t esigar_pid_get(sigar_t *sigar, char *pid)
|
||
|
{
|
||
|
if (isdigit(*pid)) {
|
||
|
return str2pid(pid);
|
||
|
}
|
||
|
else if ((*pid == '$') && (*(pid + 1) == '$')) {
|
||
|
return sigar_pid_get(sigar);
|
||
|
}
|
||
|
else {
|
||
|
/* XXX cache queries */
|
||
|
sigar_ptql_query_t *query;
|
||
|
sigar_ptql_error_t error;
|
||
|
int status =
|
||
|
sigar_ptql_query_create(&query, (char *)pid, &error);
|
||
|
|
||
|
if (status == SIGAR_OK) {
|
||
|
sigar_pid_t qpid;
|
||
|
|
||
|
status = sigar_ptql_query_find_process(sigar, query, &qpid);
|
||
|
sigar_ptql_query_destroy(query);
|
||
|
if (status == SIGAR_OK) {
|
||
|
return qpid;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void esigar_to_strlist(ei_x_buff *x,
|
||
|
char **data, unsigned long number)
|
||
|
{
|
||
|
unsigned long i;
|
||
|
|
||
|
ei_x_encode_list_header(x, number);
|
||
|
for (i=0; i<number; i++) {
|
||
|
ei_x_encode_string(x, data[i]);
|
||
|
}
|
||
|
ei_x_encode_empty_list(x);
|
||
|
}
|
||
|
|
||
|
typedef void (*esigar_encoder_func_t)(ei_x_buff *x, void *data);
|
||
|
|
||
|
static void esigar_to_list(ei_x_buff *x,
|
||
|
char *data, unsigned long number, int size,
|
||
|
esigar_encoder_func_t encoder)
|
||
|
{
|
||
|
unsigned long i;
|
||
|
|
||
|
ei_x_encode_list_header(x, number);
|
||
|
for (i=0; i<number; i++, data += size) {
|
||
|
encoder(x, data);
|
||
|
}
|
||
|
ei_x_encode_empty_list(x);
|
||
|
}
|
||
|
|
||
|
#define ESIGAR_NEW(x) \
|
||
|
ei_x_new_with_version(x)
|
||
|
|
||
|
#define ESIGAR_OK(x) \
|
||
|
ei_x_encode_tuple_header(x, 2); \
|
||
|
ei_x_encode_atom(x, "ok")
|
||
|
|
||
|
#define ESIGAR_ERROR(x, sigar, status) \
|
||
|
ei_x_encode_tuple_header(x, 2); \
|
||
|
ei_x_encode_atom(x, "error"); \
|
||
|
ei_x_encode_string(x, sigar_strerror(sigar, status))
|
||
|
|
||
|
#define ESIGAR_SEND(p, x) \
|
||
|
driver_output(port, (x)->buff, (x)->index); \
|
||
|
ei_x_free(x)
|
||
|
|
||
|
#define esigar_encode_long(x, k, v) \
|
||
|
ei_x_encode_tuple_header(x, 2); \
|
||
|
ei_x_encode_atom(x, k); \
|
||
|
ei_x_encode_long(x, v)
|
||
|
|
||
|
#define esigar_encode_ulonglong(x, k, v) \
|
||
|
ei_x_encode_tuple_header(x, 2); \
|
||
|
ei_x_encode_atom(x, k); \
|
||
|
ei_x_encode_ulonglong(x, v)
|
||
|
|
||
|
#define esigar_encode_char(x, k, v) \
|
||
|
ei_x_encode_tuple_header(x, 2); \
|
||
|
ei_x_encode_atom(x, k); \
|
||
|
ei_x_encode_char(x, v)
|
||
|
|
||
|
#define esigar_encode_string(x, k, v) \
|
||
|
ei_x_encode_tuple_header(x, 2); \
|
||
|
ei_x_encode_atom(x, k); \
|
||
|
ei_x_encode_string(x, v)
|
||
|
|
||
|
#define esigar_encode_double(x, k, v) \
|
||
|
ei_x_encode_tuple_header(x, 2); \
|
||
|
ei_x_encode_atom(x, k); \
|
||
|
ei_x_encode_double(x, v)
|
||
|
|
||
|
static void esigar_encode_net_address(ei_x_buff *x, const char *key,
|
||
|
sigar_net_address_t *address)
|
||
|
{
|
||
|
char buf[SIGAR_INET6_ADDRSTRLEN];
|
||
|
sigar_net_address_to_string(NULL, address, buf);
|
||
|
esigar_encode_string(x, key, buf);
|
||
|
}
|
||
|
|
||
|
#define esigar_encode_netaddr(x, k, v) \
|
||
|
esigar_encode_net_address(x, k, &v)
|
||
|
|
||
|
static void esigar_notimpl(ErlDrvPort port, sigar_t *sigar, int cmd)
|
||
|
{
|
||
|
ei_x_buff x;
|
||
|
|
||
|
ESIGAR_NEW(&x);
|
||
|
ESIGAR_ERROR(&x, sigar, SIGAR_ENOTIMPL);
|
||
|
ESIGAR_SEND(port, &x);
|
||
|
}
|
||
|
|
||
|
#include "../priv/gen/sigar_drv_gen.c"
|
||
|
|
||
|
static void esigar_loadavg_get(ErlDrvPort port, sigar_t *sigar)
|
||
|
{
|
||
|
int status;
|
||
|
ei_x_buff x;
|
||
|
sigar_loadavg_t loadavg;
|
||
|
|
||
|
ESIGAR_NEW(&x);
|
||
|
|
||
|
if ((status = sigar_loadavg_get(sigar, &loadavg)) == SIGAR_OK) {
|
||
|
ESIGAR_OK(&x);
|
||
|
|
||
|
ei_x_encode_list_header(&x, 3);
|
||
|
ei_x_encode_double(&x, loadavg.loadavg[0]);
|
||
|
ei_x_encode_double(&x, loadavg.loadavg[1]);
|
||
|
ei_x_encode_double(&x, loadavg.loadavg[2]);
|
||
|
ei_x_encode_empty_list(&x);
|
||
|
}
|
||
|
else {
|
||
|
ESIGAR_ERROR(&x, sigar, status);
|
||
|
}
|
||
|
|
||
|
ESIGAR_SEND(port, &x);
|
||
|
}
|
||
|
|
||
|
static void esigar_net_connection_list_get(ErlDrvPort port, sigar_t *sigar,
|
||
|
unsigned int flags)
|
||
|
{
|
||
|
int status;
|
||
|
ei_x_buff x;
|
||
|
sigar_net_connection_list_t list;
|
||
|
|
||
|
ei_x_new_with_version(&x);
|
||
|
|
||
|
if ((status = sigar_net_connection_list_get(sigar, &list, flags) == SIGAR_OK)) {
|
||
|
ESIGAR_OK(&x);
|
||
|
|
||
|
esigar_to_list(&x,
|
||
|
(char *)&list.data[0], list.number,
|
||
|
sizeof(*list.data),
|
||
|
(esigar_encoder_func_t)esigar_encode_net_connection);
|
||
|
|
||
|
sigar_net_connection_list_destroy(sigar, &list);
|
||
|
}
|
||
|
else {
|
||
|
ESIGAR_ERROR(&x, sigar, status);
|
||
|
}
|
||
|
|
||
|
ESIGAR_SEND(port, &x);
|
||
|
}
|
||
|
|
||
|
static void esigar_net_interface_list_get(ErlDrvPort port, sigar_t *sigar)
|
||
|
{
|
||
|
int status;
|
||
|
ei_x_buff x;
|
||
|
sigar_net_interface_list_t list;
|
||
|
|
||
|
ei_x_new_with_version(&x);
|
||
|
|
||
|
if ((status = sigar_net_interface_list_get(sigar, &list) == SIGAR_OK)) {
|
||
|
ESIGAR_OK(&x);
|
||
|
|
||
|
esigar_to_strlist(&x, list.data, list.number);
|
||
|
sigar_net_interface_list_destroy(sigar, &list);
|
||
|
}
|
||
|
else {
|
||
|
ESIGAR_ERROR(&x, sigar, status);
|
||
|
}
|
||
|
|
||
|
ESIGAR_SEND(port, &x);
|
||
|
}
|
||
|
|
||
|
static void esigar_file_system_list_get(ErlDrvPort port, sigar_t *sigar)
|
||
|
{
|
||
|
int status;
|
||
|
ei_x_buff x;
|
||
|
sigar_file_system_list_t list;
|
||
|
|
||
|
ei_x_new_with_version(&x);
|
||
|
|
||
|
if ((status = sigar_file_system_list_get(sigar, &list) == SIGAR_OK)) {
|
||
|
ESIGAR_OK(&x);
|
||
|
|
||
|
esigar_to_list(&x,
|
||
|
(char *)&list.data[0], list.number,
|
||
|
sizeof(*list.data),
|
||
|
(esigar_encoder_func_t)esigar_encode_file_system);
|
||
|
|
||
|
sigar_file_system_list_destroy(sigar, &list);
|
||
|
}
|
||
|
else {
|
||
|
ESIGAR_ERROR(&x, sigar, status);
|
||
|
}
|
||
|
|
||
|
ESIGAR_SEND(port, &x);
|
||
|
}
|
||
|
|
||
|
static void esigar_net_route_list_get(ErlDrvPort port, sigar_t *sigar)
|
||
|
{
|
||
|
int status;
|
||
|
ei_x_buff x;
|
||
|
sigar_net_route_list_t list;
|
||
|
|
||
|
ei_x_new_with_version(&x);
|
||
|
|
||
|
if ((status = sigar_net_route_list_get(sigar, &list) == SIGAR_OK)) {
|
||
|
ESIGAR_OK(&x);
|
||
|
|
||
|
esigar_to_list(&x,
|
||
|
(char *)&list.data[0], list.number,
|
||
|
sizeof(*list.data),
|
||
|
(esigar_encoder_func_t)esigar_encode_net_route);
|
||
|
|
||
|
sigar_net_route_list_destroy(sigar, &list);
|
||
|
}
|
||
|
else {
|
||
|
ESIGAR_ERROR(&x, sigar, status);
|
||
|
}
|
||
|
|
||
|
ESIGAR_SEND(port, &x);
|
||
|
}
|
||
|
|
||
|
static void esigar_cpu_info_list_get(ErlDrvPort port, sigar_t *sigar)
|
||
|
{
|
||
|
int status;
|
||
|
ei_x_buff x;
|
||
|
sigar_cpu_info_list_t list;
|
||
|
|
||
|
ei_x_new_with_version(&x);
|
||
|
|
||
|
if ((status = sigar_cpu_info_list_get(sigar, &list) == SIGAR_OK)) {
|
||
|
ESIGAR_OK(&x);
|
||
|
|
||
|
esigar_to_list(&x,
|
||
|
(char *)&list.data[0], list.number,
|
||
|
sizeof(*list.data),
|
||
|
(esigar_encoder_func_t)esigar_encode_cpu_info);
|
||
|
|
||
|
sigar_cpu_info_list_destroy(sigar, &list);
|
||
|
}
|
||
|
else {
|
||
|
ESIGAR_ERROR(&x, sigar, status);
|
||
|
}
|
||
|
|
||
|
ESIGAR_SEND(port, &x);
|
||
|
}
|
||
|
|
||
|
static void esigar_who_list_get(ErlDrvPort port, sigar_t *sigar)
|
||
|
{
|
||
|
int status;
|
||
|
ei_x_buff x;
|
||
|
sigar_who_list_t list;
|
||
|
|
||
|
ei_x_new_with_version(&x);
|
||
|
|
||
|
if ((status = sigar_who_list_get(sigar, &list) == SIGAR_OK)) {
|
||
|
ESIGAR_OK(&x);
|
||
|
|
||
|
esigar_to_list(&x,
|
||
|
(char *)&list.data[0], list.number,
|
||
|
sizeof(*list.data),
|
||
|
(esigar_encoder_func_t)esigar_encode_who);
|
||
|
|
||
|
sigar_who_list_destroy(sigar, &list);
|
||
|
}
|
||
|
else {
|
||
|
ESIGAR_ERROR(&x, sigar, status);
|
||
|
}
|
||
|
|
||
|
ESIGAR_SEND(port, &x);
|
||
|
}
|
||
|
|
||
|
static void outputv(ErlDrvData handle, ErlIOVec *ev) {
|
||
|
sigar_drv_t *sd = (sigar_drv_t *)handle;
|
||
|
sigar_t *sigar = sd->sigar;
|
||
|
ErlDrvPort port = sd->port;
|
||
|
ErlDrvBinary *data = ev->binv[1];
|
||
|
int cmd = data->orig_bytes[0];
|
||
|
|
||
|
switch(cmd) {
|
||
|
case ESIGAR_NET_CONNECTION_LIST:
|
||
|
esigar_net_connection_list_get(port, sigar,
|
||
|
data->orig_bytes[1]);
|
||
|
break;
|
||
|
case ESIGAR_NET_INTERFACE_LIST:
|
||
|
esigar_net_interface_list_get(port, sigar);
|
||
|
break;
|
||
|
case ESIGAR_NET_ROUTE_LIST:
|
||
|
esigar_net_route_list_get(port, sigar);
|
||
|
break;
|
||
|
case ESIGAR_FILE_SYSTEM_LIST:
|
||
|
esigar_file_system_list_get(port, sigar);
|
||
|
break;
|
||
|
case ESIGAR_CPU_INFO_LIST:
|
||
|
esigar_cpu_info_list_get(port, sigar);
|
||
|
break;
|
||
|
case ESIGAR_WHO_LIST:
|
||
|
esigar_who_list_get(port, sigar);
|
||
|
break;
|
||
|
case ESIGAR_LOADAVG:
|
||
|
esigar_loadavg_get(port, sigar);
|
||
|
break;
|
||
|
default:
|
||
|
esigar_dispatch(port, sigar, cmd, &data->orig_bytes[1]);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static ErlDrvEntry sigar_driver_entry = {
|
||
|
NULL, /* init */
|
||
|
start, /* startup */
|
||
|
stop, /* shutdown */
|
||
|
NULL, /* output */
|
||
|
NULL, /* ready_input */
|
||
|
NULL, /* ready_output */
|
||
|
"sigar_drv", /* name of the driver */
|
||
|
NULL, /* finish */
|
||
|
NULL, /* handle */
|
||
|
NULL, /* control */
|
||
|
NULL, /* timeout */
|
||
|
outputv, /* outputv */
|
||
|
NULL, /* ready_async */
|
||
|
NULL, /* flush */
|
||
|
NULL, /* call */
|
||
|
NULL, /* event */
|
||
|
ERL_DRV_EXTENDED_MARKER, /* ERL_DRV_EXTENDED_MARKER */
|
||
|
ERL_DRV_EXTENDED_MAJOR_VERSION, /* ERL_DRV_EXTENDED_MAJOR_VERSION */
|
||
|
ERL_DRV_EXTENDED_MAJOR_VERSION, /* ERL_DRV_EXTENDED_MINOR_VERSION */
|
||
|
ERL_DRV_FLAG_USE_PORT_LOCKING /* ERL_DRV_FLAGs */
|
||
|
};
|
||
|
|
||
|
DRIVER_INIT(sigar_driver) {
|
||
|
return &sigar_driver_entry;
|
||
|
}
|