1943 lines
39 KiB
C
1943 lines
39 KiB
C
/* GKrellM
|
|
| Copyright (C) 1999-2007 Bill Wilson
|
|
|
|
|
| Author: Bill Wilson billw@gkrellm.net
|
|
| Latest versions might be found at: http://gkrellm.net
|
|
|
|
|
|
|
|
| GKrellM is free software: you can redistribute it and/or modify it
|
|
| under the terms of the GNU General Public License as published by
|
|
| the Free Software Foundation, either version 3 of the License, or
|
|
| (at your option) any later version.
|
|
|
|
|
| GKrellM is distributed in the hope that it will be useful, but WITHOUT
|
|
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
| or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
|
| License for more details.
|
|
|
|
|
| You should have received a copy of the GNU General Public License
|
|
| along with this program. If not, see http://www.gnu.org/licenses/
|
|
*/
|
|
|
|
|
|
#include "gkrellm.h"
|
|
#include "gkrellm-private.h"
|
|
#include "gkrellm-sysdeps.h"
|
|
|
|
#include <inttypes.h>
|
|
|
|
#if !defined(WIN32)
|
|
#include <sys/socket.h>
|
|
#include <utime.h>
|
|
#include <sys/time.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#else
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#if defined(__linux__)
|
|
#if defined(__GLIBC__) && ((__GLIBC__>2)||(__GLIBC__==2 && __GLIBC_MINOR__>=1))
|
|
#define HAVE_GETADDRINFO 1
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(__DragonFly__)
|
|
#define HAVE_GETADDRINFO 1
|
|
#endif
|
|
|
|
#if defined(__FreeBSD__)
|
|
#if __FreeBSD_version >= 400000
|
|
#define HAVE_GETADDRINFO 1
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(__OpenBSD__)
|
|
#define HAVE_GETADDRINFO 1
|
|
#endif
|
|
|
|
#if defined(__NetBSD__)
|
|
#define HAVE_GETADDRINFO 1
|
|
#endif
|
|
|
|
#if defined(__solaris__)
|
|
/* getaddrinfo is related to IPv6 stuff */
|
|
# include <netconfig.h>
|
|
# if defined(NC_INET6)
|
|
# define HAVE_GETADDRINFO 1
|
|
# endif
|
|
#endif
|
|
|
|
|
|
typedef struct
|
|
{
|
|
gchar *key;
|
|
void (*func)();
|
|
}
|
|
KeyTable;
|
|
|
|
static gint server_major_version,
|
|
server_minor_version,
|
|
server_rev_version;
|
|
|
|
static gint client_input_id,
|
|
client_fd;
|
|
static gboolean server_alive;
|
|
|
|
static gchar server_buf[4097];
|
|
static gint buf_index;
|
|
|
|
static gchar locale_decimal_point;
|
|
static gchar server_decimal_point = '.';
|
|
static gboolean need_locale_fix = FALSE;
|
|
|
|
|
|
gboolean
|
|
gkrellm_client_check_server_version(gint major, gint minor, gint rev)
|
|
{
|
|
if ( server_major_version > major
|
|
|| (server_major_version == major && server_minor_version > minor)
|
|
|| ( server_major_version == major && server_minor_version == minor
|
|
&& server_rev_version >= rev
|
|
)
|
|
)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* There can be a locale decimal point mismatch with the server.
|
|
*/
|
|
static void
|
|
locale_fix(gchar *buf)
|
|
{
|
|
gchar *s;
|
|
|
|
for (s = buf; *s; ++s)
|
|
if (*s == server_decimal_point)
|
|
*s = locale_decimal_point;
|
|
}
|
|
|
|
|
|
/* ================================================================= */
|
|
/* CPU */
|
|
|
|
typedef struct
|
|
{
|
|
gint instance;
|
|
gulong user,
|
|
nice,
|
|
sys,
|
|
idle;
|
|
}
|
|
Cpu;
|
|
|
|
static GList *cpu_list,
|
|
*instance_list;
|
|
static gint n_cpus = 1;
|
|
static gboolean nice_time_supported = TRUE;
|
|
|
|
static void
|
|
client_cpu_line_from_server(gchar *line)
|
|
{
|
|
GList *list;
|
|
Cpu *cpu = NULL;
|
|
gint n;
|
|
guint64 user, nice, sys, idle;
|
|
|
|
sscanf(line, "%d %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, &n, &user, &nice, &sys, &idle);
|
|
for (list = cpu_list; list; list = list->next)
|
|
{
|
|
cpu = (Cpu *) list->data;
|
|
if (cpu->instance == n)
|
|
break;
|
|
}
|
|
if (list)
|
|
{
|
|
cpu->user = user;
|
|
cpu->nice = nice;
|
|
cpu->sys = sys;
|
|
cpu->idle = idle;
|
|
}
|
|
}
|
|
|
|
static void
|
|
cpu_read_data(void)
|
|
{
|
|
Cpu *cpu;
|
|
GList *list;
|
|
|
|
for (list = cpu_list; list; list = list->next)
|
|
{
|
|
cpu = (Cpu *) list->data;
|
|
gkrellm_cpu_assign_data(cpu->instance, cpu->user, cpu->nice,
|
|
cpu->sys, cpu->idle);
|
|
}
|
|
}
|
|
|
|
static void
|
|
client_sys_cpu_init(void)
|
|
{
|
|
GList *list;
|
|
Cpu *cpu;
|
|
gint n;
|
|
|
|
/* Do initialization based on info received in client_cpu_setup(). Here
|
|
| we need to get the same effective work done as would be done in the
|
|
| sysdep gkrellm_sys_cpu_init() when not running in client mode.
|
|
*/
|
|
for (n = 0; n < n_cpus; ++ n)
|
|
{
|
|
cpu = g_new0(Cpu, 1);
|
|
if (instance_list && (list = g_list_nth(instance_list, n)) != NULL)
|
|
{
|
|
cpu->instance = GPOINTER_TO_INT(list->data);
|
|
gkrellm_cpu_add_instance(cpu->instance);
|
|
}
|
|
else
|
|
cpu->instance = n;
|
|
cpu_list = g_list_append(cpu_list, cpu);
|
|
}
|
|
gkrellm_cpu_set_number_of_cpus(n_cpus);
|
|
if (!nice_time_supported)
|
|
gkrellm_cpu_nice_time_unsupported();
|
|
|
|
/* Diverting the cpu_read_data function in cpu.c causes the cpu monitor
|
|
| to not call the gkrellm_sys_cpu_init() sysdep function and also to call
|
|
| our client cpu_read_data function instead of gkrellm_sys_cpu_read_data()
|
|
*/
|
|
gkrellm_cpu_client_divert(cpu_read_data);
|
|
}
|
|
|
|
static void
|
|
client_cpu_setup(gchar *line)
|
|
{
|
|
gint instance;
|
|
|
|
if (!strncmp(line, "n_cpus", 6))
|
|
sscanf(line, "%*s %d", &n_cpus);
|
|
else if (!strncmp(line, "cpu_instance", 12))
|
|
{
|
|
sscanf(line, "%*s %d", &instance);
|
|
instance_list = g_list_append(instance_list,
|
|
GINT_TO_POINTER(instance));
|
|
}
|
|
else if (!strcmp(line, "nice_time_unsupported"))
|
|
nice_time_supported = FALSE;
|
|
}
|
|
|
|
/* ================================================================= */
|
|
static struct
|
|
{
|
|
gint n_processes,
|
|
n_running,
|
|
n_users;
|
|
gulong n_forks;
|
|
gfloat load;
|
|
}
|
|
proc;
|
|
|
|
static void
|
|
client_proc_line_from_server(gchar *line)
|
|
{
|
|
if (need_locale_fix)
|
|
locale_fix(line);
|
|
sscanf(line, "%d %d %lu %f %d", &proc.n_processes, &proc.n_running,
|
|
&proc.n_forks, &proc.load, &proc.n_users);
|
|
}
|
|
|
|
static void
|
|
read_proc_data(void)
|
|
{
|
|
gkrellm_proc_assign_data(proc.n_processes, proc.n_running,
|
|
proc.n_forks, proc.load);
|
|
}
|
|
|
|
static void
|
|
read_user_data(void)
|
|
{
|
|
gkrellm_proc_assign_users(proc.n_users);
|
|
}
|
|
|
|
static void
|
|
client_sys_proc_init(void)
|
|
{
|
|
gkrellm_proc_client_divert(read_proc_data, read_user_data);
|
|
}
|
|
|
|
/* ================================================================= */
|
|
typedef struct
|
|
{
|
|
gchar *name;
|
|
gchar *subdisk_parent;
|
|
guint64 rblk,
|
|
wblk;
|
|
gboolean virtual;
|
|
}
|
|
DiskData;
|
|
|
|
static GList *disk_list;
|
|
|
|
static gboolean units_are_blocks;
|
|
|
|
static void
|
|
client_disk_line_from_server(gchar *line)
|
|
{
|
|
DiskData *disk = NULL;
|
|
GList *list;
|
|
gchar name[16], s1[32], s2[32], s3[32];
|
|
guint64 rblk, wblk;
|
|
gint n;
|
|
gboolean virtual = FALSE;
|
|
|
|
n = sscanf(line, "%15s %31s %31s %31s", name, s1, s2, s3);
|
|
if (n == 4)
|
|
{
|
|
if ( gkrellm_client_check_server_version(2, 2, 7)
|
|
&& !strcmp(s1, "virtual")
|
|
)
|
|
{
|
|
virtual = TRUE;
|
|
s1[0] = '\0';
|
|
}
|
|
rblk = strtoull(s2, NULL, 0);
|
|
wblk = strtoull(s3, NULL, 0);
|
|
}
|
|
else if (n == 3)
|
|
{
|
|
rblk = strtoull(s1, NULL, 0);
|
|
wblk = strtoull(s2, NULL, 0);
|
|
s1[0] = '\0';
|
|
}
|
|
else
|
|
return;
|
|
for (list = disk_list; list; list = list->next)
|
|
{
|
|
disk = (DiskData *) list->data;
|
|
if (!strcmp(disk->name, name))
|
|
break;
|
|
}
|
|
if (!list)
|
|
{
|
|
disk = g_new0(DiskData, 1);
|
|
disk->name = g_strdup(name);
|
|
if (s1[0]) /* I expect server to send in order */
|
|
disk->subdisk_parent = g_strdup(s1);
|
|
disk_list = g_list_append(disk_list, disk);
|
|
}
|
|
if (disk)
|
|
{
|
|
disk->rblk = rblk;
|
|
disk->wblk = wblk;
|
|
disk->virtual = virtual;
|
|
}
|
|
}
|
|
|
|
static void
|
|
read_disk_data(void)
|
|
{
|
|
GList *list;
|
|
DiskData *disk;
|
|
|
|
for (list = disk_list; list; list = list->next)
|
|
{
|
|
disk = (DiskData *) list->data;
|
|
if (disk->subdisk_parent)
|
|
gkrellm_disk_subdisk_assign_data_by_name(disk->name,
|
|
disk->subdisk_parent, disk->rblk, disk->wblk);
|
|
else
|
|
gkrellm_disk_assign_data_by_name(disk->name,
|
|
disk->rblk, disk->wblk, disk->virtual);
|
|
}
|
|
}
|
|
|
|
static gint
|
|
order_from_name(gchar *name)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
static void
|
|
client_sys_disk_init(void)
|
|
{
|
|
gkrellm_disk_client_divert(read_disk_data, NULL, order_from_name);
|
|
if (units_are_blocks)
|
|
gkrellm_disk_units_are_blocks();
|
|
|
|
/* Disk monitor config needs to know will be using assign by name
|
|
*/
|
|
gkrellm_disk_assign_data_by_name(NULL, 0, 0, FALSE);
|
|
}
|
|
|
|
static void
|
|
client_disk_setup(gchar *line)
|
|
{
|
|
if (!strcmp(line, "units_are_blocks"))
|
|
units_are_blocks = TRUE;
|
|
}
|
|
|
|
/* ================================================================= */
|
|
typedef struct
|
|
{
|
|
gchar *name;
|
|
gboolean up_event,
|
|
down_event;
|
|
|
|
gint up_time;
|
|
|
|
gulong rx,
|
|
tx;
|
|
}
|
|
NetData;
|
|
|
|
static NetData *net_timer;
|
|
|
|
static GList *net_list;
|
|
|
|
static gchar *net_timer_name;
|
|
|
|
static gboolean net_server_use_routed;
|
|
|
|
static void
|
|
client_net_line_from_server(gchar *line)
|
|
{
|
|
GList *list;
|
|
NetData *net;
|
|
gchar name[32];
|
|
guint64 rx, tx;
|
|
|
|
sscanf(line, "%31s %" PRIu64 " %" PRIu64, name, &rx, &tx);
|
|
for (list = net_list; list; list = list->next)
|
|
{
|
|
net = (NetData *) list->data;
|
|
if (!strcmp(name, net->name))
|
|
{
|
|
net->rx = rx;
|
|
net->tx = tx;
|
|
break;
|
|
}
|
|
}
|
|
if (!list)
|
|
{
|
|
net = g_new0(NetData, 1);
|
|
net->name = g_strdup(name);
|
|
net_list = g_list_append(net_list, net);
|
|
net->rx = rx;
|
|
net->tx = tx;
|
|
|
|
if (net_timer_name && !strcmp(net_timer_name, net->name))
|
|
net_timer = net;
|
|
}
|
|
}
|
|
|
|
static void
|
|
client_net_routed_line_from_server(gchar *line)
|
|
{
|
|
GList *list;
|
|
NetData *net = NULL;
|
|
gchar name[32];
|
|
gboolean routed;
|
|
|
|
sscanf(line, "%31s %d", name, &routed);
|
|
for (list = net_list; list; list = list->next)
|
|
{
|
|
net = (NetData *) list->data;
|
|
if (!strcmp(name, net->name))
|
|
break;
|
|
}
|
|
if (!list)
|
|
{
|
|
net = g_new0(NetData, 1);
|
|
net->name = g_strdup(name);
|
|
net_list = g_list_append(net_list, net);
|
|
}
|
|
if (net)
|
|
{
|
|
if (routed)
|
|
net->up_event = TRUE;
|
|
else
|
|
net->down_event = TRUE;
|
|
}
|
|
}
|
|
|
|
gint
|
|
gkrellm_client_server_get_net_timer(void)
|
|
{
|
|
return net_timer ? net_timer->up_time : 0;
|
|
}
|
|
|
|
static void
|
|
client_net_timer_line_from_server(gchar *line)
|
|
{
|
|
gchar name[32];
|
|
|
|
if (!net_timer)
|
|
return;
|
|
sscanf(line, "%s %d", name, &net_timer->up_time);
|
|
}
|
|
|
|
static void
|
|
check_net_routes(void)
|
|
{
|
|
GList *list;
|
|
NetData *net;
|
|
|
|
for (list = net_list; list; list = list->next)
|
|
{
|
|
net = (NetData *) list->data;
|
|
if (net->up_event || net->down_event)
|
|
gkrellm_net_routed_event(net->name, net->up_event);
|
|
net->up_event = net->down_event = FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
read_net_data(void)
|
|
{
|
|
GList *list;
|
|
NetData *net;
|
|
|
|
for (list = net_list; list; list = list->next)
|
|
{
|
|
net = (NetData *) list->data;
|
|
gkrellm_net_assign_data(net->name, net->rx, net->tx);
|
|
}
|
|
}
|
|
|
|
/* gkrellmd to gkrellm server/client interface always uses the net routed
|
|
| mode regardless if the server uses routed in its sysdep code.
|
|
*/
|
|
static void
|
|
client_sys_net_init(void)
|
|
{
|
|
gkrellm_net_client_divert(read_net_data, check_net_routes, NULL);
|
|
gkrellm_net_use_routed(net_server_use_routed);
|
|
|
|
if (net_timer_name)
|
|
gkrellm_net_server_has_timer();
|
|
}
|
|
|
|
static void
|
|
client_net_setup(gchar *line)
|
|
{
|
|
gchar buf[32];
|
|
|
|
/* This is the server sysdep net_use_routed value. The client <-> server
|
|
| link always uses routed mode.
|
|
*/
|
|
if (!strcmp(line, "net_use_routed"))
|
|
net_server_use_routed = TRUE;
|
|
|
|
if (!strncmp(line, "net_timer", 9))
|
|
{
|
|
sscanf(line, "%*s %31s\n", buf);
|
|
net_timer_name = g_strdup(buf);
|
|
}
|
|
}
|
|
|
|
|
|
/* ================================================================= */
|
|
#include "../src/inet.h"
|
|
|
|
static GList *inet_list;
|
|
|
|
static gboolean inet_unsupported;
|
|
|
|
static void
|
|
client_inet_line_from_server(gchar *line)
|
|
{
|
|
GList *list;
|
|
ActiveTCP tcp, *t;
|
|
gchar *ap, *aap;
|
|
#if defined(INET6) && defined(HAVE_GETADDRINFO)
|
|
struct addrinfo hints, *res;
|
|
gchar buf[NI_MAXHOST];
|
|
#else
|
|
gchar buf[128];
|
|
#endif
|
|
gint n, slen;
|
|
|
|
if (_GK.debug_level & DEBUG_INET)
|
|
g_print("inet server: %s\n", line);
|
|
if (*(line + 1) == '0')
|
|
{
|
|
n = sscanf(line + 3, "%x %127[^:]:%x", &tcp.local_port,
|
|
buf, &tcp.remote_port);
|
|
if (n != 3 || inet_aton(buf, &tcp.remote_addr) == 0)
|
|
return;
|
|
tcp.family = AF_INET;
|
|
}
|
|
#if defined(INET6) && defined(HAVE_GETADDRINFO)
|
|
else if (*(line + 1) == '6')
|
|
{
|
|
#define STR(x) #x
|
|
#define XSTR(x) STR(x)
|
|
n = sscanf(line + 3, "%x [%" XSTR(NI_MAXHOST) "[^]]]:%x",
|
|
&tcp.local_port, buf, &tcp.remote_port);
|
|
if (n != 3)
|
|
return;
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = AF_INET6;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
|
|
if (getaddrinfo(buf, NULL, &hints, &res) != 0)
|
|
return;
|
|
memcpy(&tcp.remote_addr6,
|
|
&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
|
|
sizeof(struct in6_addr));
|
|
freeaddrinfo(res);
|
|
tcp.family = AF_INET6;
|
|
}
|
|
#endif
|
|
if (*line == '+')
|
|
{
|
|
t = g_new0(ActiveTCP, 1);
|
|
*t = tcp;
|
|
inet_list = g_list_append(inet_list, t);
|
|
}
|
|
else if (*line == '-')
|
|
{
|
|
for (list = inet_list; list; list = list->next)
|
|
{
|
|
t = (ActiveTCP *) list->data;
|
|
if (t->family == AF_INET)
|
|
{
|
|
ap = (gchar *) &t->remote_addr;
|
|
aap = (gchar *) &tcp.remote_addr;
|
|
slen = sizeof(struct in_addr);
|
|
}
|
|
#if defined(INET6)
|
|
else if (t->family == AF_INET6)
|
|
{
|
|
ap = (gchar *) &t->remote_addr6;
|
|
aap = (gchar *) &tcp.remote_addr6;
|
|
slen = sizeof(struct in6_addr);
|
|
}
|
|
#endif
|
|
else
|
|
return;
|
|
if ( memcmp(aap, ap, slen) == 0
|
|
&& tcp.remote_port == t->remote_port
|
|
&& tcp.local_port == t->local_port
|
|
)
|
|
{
|
|
g_free(t);
|
|
inet_list = g_list_remove_link(inet_list, list);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
read_tcp_data(void)
|
|
{
|
|
GList *list;
|
|
ActiveTCP *tcp;
|
|
|
|
for (list = inet_list; list; list = list->next)
|
|
{
|
|
tcp = (ActiveTCP *) list->data;
|
|
gkrellm_inet_log_tcp_port_data(tcp);
|
|
}
|
|
}
|
|
|
|
static void
|
|
client_inet_reset(void)
|
|
{
|
|
gkrellm_free_glist_and_data(&inet_list);
|
|
}
|
|
|
|
static void
|
|
client_sys_inet_init(void)
|
|
{
|
|
if (inet_unsupported)
|
|
return;
|
|
gkrellm_inet_client_divert(read_tcp_data);
|
|
}
|
|
|
|
static void
|
|
client_inet_setup(gchar *line)
|
|
{
|
|
if (!strcmp(line, "inet_unsupported"))
|
|
inet_unsupported = TRUE;
|
|
}
|
|
|
|
/* ================================================================= */
|
|
struct
|
|
{
|
|
guint64 total,
|
|
used,
|
|
free,
|
|
shared,
|
|
buffers,
|
|
cached;
|
|
guint64 swap_total,
|
|
swap_used;
|
|
gulong swap_in,
|
|
swap_out;
|
|
}
|
|
mem;
|
|
|
|
static void
|
|
client_mem_line_from_server(gchar *line)
|
|
{
|
|
sscanf(line, "%" PRIu64 "%" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64,
|
|
&mem.total, &mem.used, &mem.free,
|
|
&mem.shared, &mem.buffers, &mem.cached);
|
|
}
|
|
|
|
static void
|
|
client_swap_line_from_server(gchar *line)
|
|
{
|
|
sscanf(line, "%" PRIu64 " %" PRIu64 " %lu %lu",
|
|
&mem.swap_total, &mem.swap_used,
|
|
&mem.swap_in, &mem.swap_out);
|
|
}
|
|
|
|
static void
|
|
read_mem_data(void)
|
|
{
|
|
gkrellm_mem_assign_data(mem.total, mem.used, mem.free, mem.shared,
|
|
mem.buffers, mem.cached);
|
|
}
|
|
|
|
static void
|
|
read_swap_data(void)
|
|
{
|
|
gkrellm_swap_assign_data(mem.swap_total, mem.swap_used,
|
|
mem.swap_in, mem.swap_out);
|
|
}
|
|
|
|
static void
|
|
client_sys_mem_init(void)
|
|
{
|
|
gkrellm_mem_client_divert(read_mem_data, read_swap_data);
|
|
}
|
|
|
|
/* ================================================================= */
|
|
typedef struct
|
|
{
|
|
gchar *directory,
|
|
*device,
|
|
*type;
|
|
gulong blocks,
|
|
bavail,
|
|
bfree,
|
|
bsize;
|
|
}
|
|
Mount;
|
|
|
|
static GList *mounts_list,
|
|
*fstab_list;
|
|
|
|
static gboolean fstab_modified,
|
|
mounting_unsupported;
|
|
|
|
static void
|
|
client_fstab_line_from_server(gchar *line)
|
|
{
|
|
GList *list;
|
|
Mount *m;
|
|
gchar dir[128], dev[64], type[64];
|
|
|
|
if (!strcmp(line, ".clear"))
|
|
{
|
|
for (list = fstab_list; list; list = list->next)
|
|
{
|
|
m = (Mount *) list->data;
|
|
g_free(m->directory);
|
|
g_free(m->device);
|
|
g_free(m->type);
|
|
g_free(m);
|
|
}
|
|
g_list_free(fstab_list);
|
|
fstab_list = NULL;
|
|
fstab_modified = TRUE;
|
|
}
|
|
else
|
|
{
|
|
m = g_new0(Mount, 1);
|
|
sscanf(line, "%127s %63s %63s", dir, dev, type);
|
|
m->directory = g_strdup(dir);
|
|
m->device = g_strdup(dev);
|
|
m->type = g_strdup(type);
|
|
fstab_list = g_list_append(fstab_list, m);
|
|
}
|
|
}
|
|
|
|
static void
|
|
client_mounts_line_from_server(gchar *line)
|
|
{
|
|
GList *list;
|
|
Mount *m;
|
|
gchar dir[128], dev[64], type[64];
|
|
|
|
if (!strcmp(line, ".clear"))
|
|
{
|
|
for (list = mounts_list; list; list = list->next)
|
|
{
|
|
m = (Mount *) list->data;
|
|
g_free(m->directory);
|
|
g_free(m->device);
|
|
g_free(m->type);
|
|
g_free(m);
|
|
}
|
|
g_list_free(mounts_list);
|
|
mounts_list = NULL;
|
|
}
|
|
else
|
|
{
|
|
m = g_new0(Mount, 1);
|
|
sscanf(line, "%127s %63s %63s %lu %lu %lu %lu", dir, dev, type,
|
|
&m->blocks, &m->bavail, &m->bfree, &m->bsize);
|
|
m->directory = g_strdup(dir);
|
|
m->device = g_strdup(dev);
|
|
m->type = g_strdup(type);
|
|
mounts_list = g_list_append(mounts_list, m);
|
|
}
|
|
}
|
|
|
|
static void
|
|
client_fs_line_from_server(gchar *line)
|
|
{
|
|
GList *list;
|
|
Mount *m;
|
|
gchar dir[128], dev[64];
|
|
gulong blocks, bavail, bfree, bsize;
|
|
|
|
sscanf(line, "%127s %63s %lu %lu %lu %lu", dir, dev,
|
|
&blocks, &bavail, &bfree, &bsize);
|
|
for (list = mounts_list; list; list = list->next)
|
|
{
|
|
m = (Mount *) list->data;
|
|
if (!strcmp(m->directory, dir) && !strcmp(m->device, dev))
|
|
{
|
|
m->blocks = blocks;
|
|
m->bavail = bavail;
|
|
m->bfree = bfree;
|
|
m->bsize = bsize;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_fsusage(gpointer fs, gchar *dir)
|
|
{
|
|
GList *list;
|
|
Mount *m;
|
|
|
|
for (list = mounts_list; list; list = list->next)
|
|
{
|
|
m = (Mount *) list->data;
|
|
if (!strcmp(m->directory, dir))
|
|
{
|
|
gkrellm_fs_assign_fsusage_data(fs, m->blocks, m->bavail,
|
|
m->bfree, m->bsize);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_mounts_list(void)
|
|
{
|
|
GList *list;
|
|
Mount *m;
|
|
|
|
for (list = mounts_list; list; list = list->next)
|
|
{
|
|
m = (Mount *) list->data;
|
|
gkrellm_fs_add_to_mounts_list(m->directory, m->device, m->type);
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_fstab_list(void)
|
|
{
|
|
GList *list;
|
|
Mount *m;
|
|
|
|
for (list = fstab_list; list; list = list->next)
|
|
{
|
|
m = (Mount *) list->data;
|
|
gkrellm_fs_add_to_fstab_list(m->directory, m->device,
|
|
m->type, "none" /* options NA since no mounting */ );
|
|
}
|
|
fstab_modified = FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
get_fstab_modified(void)
|
|
{
|
|
return fstab_modified;
|
|
}
|
|
|
|
static void
|
|
client_sys_fs_init(void)
|
|
{
|
|
gkrellm_fs_client_divert(get_fsusage, get_mounts_list,
|
|
get_fstab_list, get_fstab_modified);
|
|
if (mounting_unsupported)
|
|
gkrellm_fs_mounting_unsupported();
|
|
}
|
|
|
|
static void
|
|
client_fs_setup(gchar *line)
|
|
{
|
|
if (!strcmp(line, "mounting_unsupported"))
|
|
mounting_unsupported = TRUE;
|
|
}
|
|
|
|
/* ================================================================= */
|
|
typedef struct
|
|
{
|
|
gchar *path;
|
|
gint total,
|
|
new;
|
|
gpointer mbox_ptr;
|
|
}
|
|
Mailbox;
|
|
|
|
static GList *mailbox_list;
|
|
|
|
static gboolean
|
|
check_mail(Mailbox *mbox)
|
|
{
|
|
gkrellm_set_external_mbox_counts(mbox->mbox_ptr, mbox->total, mbox->new);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
client_mail_line_from_server(gchar *line)
|
|
{
|
|
Mailbox *mbox = NULL;
|
|
GList *list;
|
|
gchar path[256];
|
|
// gchar *s;
|
|
gint total, new;
|
|
|
|
if (sscanf(line, "%255s %d %d", path, &total, &new) < 3)
|
|
return;
|
|
for (list = mailbox_list; list; list = list->next)
|
|
{
|
|
mbox = (Mailbox *) list->data;
|
|
if (!strcmp(path, mbox->path))
|
|
break;
|
|
}
|
|
if (!list)
|
|
{
|
|
mbox = g_new0(Mailbox, 1);
|
|
mbox->path = g_strdup(path);
|
|
mailbox_list = g_list_append(mailbox_list, mbox);
|
|
mbox->mbox_ptr =
|
|
gkrellm_add_external_mbox(check_mail, FALSE, mbox);
|
|
gkrellm_set_external_mbox_tooltip(mbox->mbox_ptr, mbox->path);
|
|
|
|
// s = g_strdup_printf("%s:%s", _GK.server_hostname, mbox->path);
|
|
// gkrellm_set_external_mbox_tooltip(mbox->mbox_ptr, s);
|
|
// g_free(s);
|
|
}
|
|
if (mbox)
|
|
{
|
|
mbox->total = total;
|
|
mbox->new = new;
|
|
}
|
|
}
|
|
|
|
static void
|
|
client_sys_mail_init(void)
|
|
{
|
|
}
|
|
|
|
static void
|
|
client_mail_setup(gchar *line)
|
|
{
|
|
}
|
|
|
|
/* ================================================================= */
|
|
GList *battery_list;
|
|
|
|
gboolean batteries_available;
|
|
|
|
typedef struct
|
|
{
|
|
gint id;
|
|
gboolean present,
|
|
on_line,
|
|
charging;
|
|
gint percent;
|
|
gint time_left;
|
|
}
|
|
Battery;
|
|
|
|
static Battery *composite_battery;
|
|
|
|
static Battery *
|
|
battery_nth(gint n)
|
|
{
|
|
Battery *bat;
|
|
static gint n_batteries;
|
|
|
|
if (n > 10)
|
|
return NULL;
|
|
if (n < 0)
|
|
{
|
|
if (!composite_battery)
|
|
{
|
|
bat = g_new0(Battery, 1);
|
|
battery_list = g_list_prepend(battery_list, bat);
|
|
bat->id = GKRELLM_BATTERY_COMPOSITE_ID;
|
|
composite_battery = bat;
|
|
}
|
|
return composite_battery;
|
|
}
|
|
|
|
if (composite_battery)
|
|
++n;
|
|
|
|
while ((bat = (Battery *)g_list_nth_data(battery_list, n)) == NULL)
|
|
{
|
|
bat = g_new0(Battery, 1);
|
|
battery_list = g_list_append(battery_list, bat);
|
|
bat->id = n_batteries++;
|
|
}
|
|
return bat;
|
|
}
|
|
|
|
|
|
static void
|
|
client_battery_line_from_server(gchar *line)
|
|
{
|
|
Battery *bat;
|
|
gboolean present, on_line, charging;
|
|
gint percent, time_left, n = 0;
|
|
|
|
/* 2.1.9 adds 6th arg battery id number
|
|
*/
|
|
if (sscanf(line, "%d %d %d %d %d %d",
|
|
&present, &on_line, &charging, &percent, &time_left, &n) < 5)
|
|
return;
|
|
|
|
bat = battery_nth(n);
|
|
if (!bat)
|
|
return;
|
|
bat->present = present;
|
|
bat->on_line = on_line;
|
|
bat->charging = charging;
|
|
bat->percent = percent;
|
|
bat->time_left = time_left;
|
|
}
|
|
|
|
static void
|
|
read_battery_data(void)
|
|
{
|
|
GList *list;
|
|
Battery *bat;
|
|
|
|
for (list = battery_list; list; list = list->next)
|
|
{
|
|
bat = (Battery *) list->data;
|
|
gkrellm_battery_assign_data(bat->id, bat->present,
|
|
bat->on_line, bat->charging,
|
|
bat->percent, bat->time_left);
|
|
}
|
|
}
|
|
|
|
static void
|
|
client_sys_battery_init(void)
|
|
{
|
|
if (batteries_available)
|
|
gkrellm_battery_client_divert(read_battery_data);
|
|
}
|
|
|
|
static void
|
|
client_battery_setup(gchar *line)
|
|
{
|
|
if ( !strcmp(line, "apm_available")
|
|
|| !strcmp(line, "battery_available")
|
|
)
|
|
batteries_available = TRUE;
|
|
}
|
|
|
|
/* ================================================================= */
|
|
typedef struct
|
|
{
|
|
gint type;
|
|
|
|
gchar *basename;
|
|
gint id;
|
|
gint iodev;
|
|
gint inter;
|
|
|
|
gint group;
|
|
|
|
gchar *vref;
|
|
gchar *default_label;
|
|
|
|
gfloat factor;
|
|
gfloat offset;
|
|
gfloat raw_value;
|
|
}
|
|
Sensor;
|
|
|
|
static GList *sensors_list;
|
|
|
|
|
|
static void
|
|
client_sensors_line_from_server(gchar *line)
|
|
{
|
|
GList *list;
|
|
Sensor s, *sensor;
|
|
gchar basename[128];
|
|
|
|
if (need_locale_fix)
|
|
locale_fix(line);
|
|
sscanf(line, "%d \"%127[^\"]\" %d %d %d %f",
|
|
&s.type, basename, &s.id, &s.iodev, &s.inter, &s.raw_value);
|
|
for (list = sensors_list; list; list = list->next)
|
|
{
|
|
sensor = (Sensor *) list->data;
|
|
if ( sensor->type == s.type && !strcmp(sensor->basename, basename)
|
|
&& sensor->id == s.id && sensor->iodev == s.iodev
|
|
&& sensor->inter == s.inter
|
|
)
|
|
{
|
|
sensor->raw_value = s.raw_value;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
get_temperature(gchar *path, gint id, gint iodev, gint inter, gfloat *value)
|
|
{
|
|
GList *list;
|
|
Sensor *s;
|
|
|
|
for (list = sensors_list; list; list = list->next)
|
|
{
|
|
s = (Sensor *) list->data;
|
|
if ( s->type == SENSOR_TEMPERATURE && !strcmp(s->basename, path)
|
|
&& s->id == id && s->iodev == iodev && s->inter == inter
|
|
)
|
|
{
|
|
*value = s->raw_value;
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
get_fan(gchar *path, gint id, gint iodev, gint inter, gfloat *value)
|
|
{
|
|
GList *list;
|
|
Sensor *s;
|
|
|
|
for (list = sensors_list; list; list = list->next)
|
|
{
|
|
s = (Sensor *) list->data;
|
|
if ( s->type == SENSOR_FAN && !strcmp(s->basename, path)
|
|
&& s->id == id && s->iodev == iodev && s->inter == inter
|
|
)
|
|
{
|
|
*value = s->raw_value;
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
get_voltage(gchar *path, gint id, gint iodev, gint inter, gfloat *value)
|
|
{
|
|
GList *list;
|
|
Sensor *s;
|
|
|
|
for (list = sensors_list; list; list = list->next)
|
|
{
|
|
s = (Sensor *) list->data;
|
|
if ( s->type == SENSOR_VOLTAGE && !strcmp(s->basename, path)
|
|
&& s->id == id && s->iodev == iodev && s->inter == inter
|
|
)
|
|
{
|
|
*value = s->raw_value;
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
client_sys_sensors_init(void)
|
|
{
|
|
GList *list;
|
|
Sensor *s;
|
|
gpointer sr;
|
|
|
|
if (!sensors_list)
|
|
return;
|
|
|
|
gkrellm_sensors_client_divert(get_temperature, get_fan, get_voltage);
|
|
for (list = sensors_list; list; list = list->next)
|
|
{
|
|
s = (Sensor *) list->data;
|
|
|
|
/* The sysdep code in the server may be using the dir arg to get sensor
|
|
| values, but dir is no longer needed.
|
|
*/
|
|
sr = gkrellm_sensors_add_sensor(s->type, NULL, s->basename,
|
|
s->id, s->iodev, s->inter,
|
|
s->factor, s->offset,
|
|
s->vref, s->default_label);
|
|
gkrellm_sensors_set_group(sr, s->group);
|
|
}
|
|
}
|
|
|
|
static void
|
|
client_sensors_setup(gchar *line)
|
|
{
|
|
Sensor *s;
|
|
gchar basename[128], vref[32], default_label[32];
|
|
|
|
if (need_locale_fix)
|
|
locale_fix(line);
|
|
|
|
s = g_new0(Sensor, 1);
|
|
s->group = SENSOR_GROUP_MAINBOARD; /* Not in pre 2.2.0 versions */
|
|
if (sscanf(line,
|
|
"%d \"%127[^\"]\" %d %d %d %f %f \"%31[^\"]\" \"%31[^\"]\" %d",
|
|
&s->type, basename, &s->id, &s->iodev, &s->inter,
|
|
&s->factor, &s->offset, vref, default_label, &s->group) >= 9)
|
|
{
|
|
s->basename = g_strdup(basename);
|
|
if (strcmp(vref, "NONE"))
|
|
s->vref = g_strdup(vref);
|
|
if (strcmp(default_label, "NONE"))
|
|
s->default_label = g_strdup(default_label);
|
|
|
|
sensors_list = g_list_append(sensors_list, s);
|
|
}
|
|
else
|
|
g_free(s);
|
|
}
|
|
|
|
/* ================================================================= */
|
|
static time_t server_uptime;
|
|
|
|
static void
|
|
client_uptime_line_from_server(gchar *s)
|
|
{
|
|
gulong up_minutes;
|
|
|
|
sscanf(s, "%lu", &up_minutes);
|
|
server_uptime = ((time_t) up_minutes) * 60;
|
|
}
|
|
|
|
static time_t
|
|
client_read_uptime(void)
|
|
{
|
|
return server_uptime;
|
|
}
|
|
|
|
static void
|
|
client_sys_uptime_init(void)
|
|
{
|
|
gkrellm_uptime_client_divert(client_read_uptime);
|
|
}
|
|
|
|
/* ================================================================= */
|
|
static struct tm server_time;
|
|
|
|
/* clock monitor doesn't have a sysdep interface, so it needs a hook
|
|
| to get server system time when in client mode.
|
|
*/
|
|
struct tm *
|
|
gkrellm_client_server_time(void)
|
|
{
|
|
return &server_time;
|
|
}
|
|
|
|
static void
|
|
client_time_line_from_server(gchar *s)
|
|
{
|
|
struct tm *t;
|
|
|
|
t = &server_time;
|
|
sscanf(s, "%d %d %d %d %d %d %d %d %d",
|
|
&t->tm_sec, &t->tm_min, &t->tm_hour,
|
|
&t->tm_mday, &t->tm_mon, &t->tm_year,
|
|
&t->tm_wday, &t->tm_yday, &t->tm_isdst);
|
|
}
|
|
|
|
/* ================================================================= */
|
|
static void
|
|
client_server_version_setup(gchar *line)
|
|
{
|
|
sscanf(line, "%*s %d.%d.%d", &server_major_version,
|
|
&server_minor_version, &server_rev_version);
|
|
}
|
|
|
|
static void
|
|
client_hostname_setup(gchar *s)
|
|
{
|
|
g_free(_GK.server_hostname);
|
|
_GK.server_hostname = g_strdup(s);
|
|
}
|
|
|
|
static void
|
|
client_sysname_setup(gchar *s)
|
|
{
|
|
g_free(_GK.server_sysname);
|
|
_GK.server_sysname = g_strdup(s);
|
|
}
|
|
|
|
|
|
KeyTable monitor_table[] =
|
|
{
|
|
{"sensors", client_sys_sensors_init },
|
|
{"cpu", client_sys_cpu_init },
|
|
{"proc", client_sys_proc_init },
|
|
{"disk", client_sys_disk_init },
|
|
{"net", client_sys_net_init },
|
|
{"inet", client_sys_inet_init },
|
|
{"mem", client_sys_mem_init },
|
|
{"fs", client_sys_fs_init },
|
|
{"mail", client_sys_mail_init },
|
|
{"apm", client_sys_battery_init },
|
|
{"battery", client_sys_battery_init },
|
|
{"uptime", client_sys_uptime_init },
|
|
};
|
|
|
|
|
|
static gboolean setup_done; /* only one sys init */
|
|
|
|
|
|
/* Setup lines are received before monitor init functions are called, so
|
|
| for plugins must save the strings until plugins are loaded.
|
|
*/
|
|
static GList *client_plugin_setup_line_list;
|
|
|
|
static void
|
|
client_plugin_add_setup_line(gchar *line)
|
|
{
|
|
client_plugin_setup_line_list
|
|
= g_list_append(client_plugin_setup_line_list, g_strdup(line));
|
|
}
|
|
|
|
/* Plugins should call this in their gkrellm_init_plugin() function.
|
|
*/
|
|
void
|
|
gkrellm_client_plugin_get_setup(gchar *key_name,
|
|
void (*setup_func_cb)(gchar *str))
|
|
{
|
|
GList *list;
|
|
gchar *line, *s;
|
|
gint n;
|
|
|
|
if (!_GK.client_mode || !key_name || !setup_func_cb)
|
|
return;
|
|
for (list = client_plugin_setup_line_list; list; list = list->next)
|
|
{
|
|
line = (gchar *) list->data;
|
|
n = strlen(key_name);
|
|
s = line + n;
|
|
if (!strncmp(line, key_name, n) && *s == ' ')
|
|
{
|
|
while (*s == ' ')
|
|
++s;
|
|
(*setup_func_cb)(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
client_monitor_setup(gchar *line)
|
|
{
|
|
void (*func)();
|
|
gchar buf[64];
|
|
gint i;
|
|
gboolean found_builtin = FALSE;
|
|
|
|
if (!*line || setup_done)
|
|
return;
|
|
for (i = 0; i < sizeof(monitor_table) / sizeof(KeyTable); ++i)
|
|
{
|
|
if (!strcmp(line, monitor_table[i].key))
|
|
{
|
|
func = monitor_table[i].func;
|
|
(*func)();
|
|
found_builtin = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
/* The client mode init work of a plugin must be defered since they
|
|
| aren't loaded yet. Set up so they will can get an "available" flag.
|
|
*/
|
|
if (!found_builtin)
|
|
{
|
|
snprintf(buf, sizeof(buf), "%s available", line);
|
|
client_plugin_add_setup_line(buf);
|
|
}
|
|
}
|
|
|
|
static void
|
|
client_server_error(gchar *line)
|
|
{
|
|
fprintf(stderr, "gkrellmd error: %s\n", line);
|
|
exit(0);
|
|
}
|
|
|
|
|
|
static void
|
|
locale_sync(void)
|
|
{
|
|
struct lconv *lc;
|
|
|
|
lc = localeconv();
|
|
locale_decimal_point = *lc->decimal_point;
|
|
if (locale_decimal_point != server_decimal_point)
|
|
need_locale_fix = TRUE;
|
|
}
|
|
|
|
static void
|
|
client_server_decimal_point(gchar *line)
|
|
{
|
|
sscanf(line, "%c", &server_decimal_point);
|
|
locale_sync();
|
|
}
|
|
|
|
static void
|
|
client_server_io_timeout(gchar *line)
|
|
{
|
|
sscanf(line, "%d", &_GK.client_server_io_timeout);
|
|
if (_GK.client_server_io_timeout < 2)
|
|
_GK.client_server_io_timeout = 0;
|
|
}
|
|
|
|
static void
|
|
client_server_reconnect_timeout(gchar *line)
|
|
{
|
|
sscanf(line, "%d", &_GK.client_server_reconnect_timeout);
|
|
if (_GK.client_server_reconnect_timeout < 2)
|
|
_GK.client_server_reconnect_timeout = 0;
|
|
}
|
|
|
|
|
|
/* ================================================================= */
|
|
KeyTable setup_table[] =
|
|
{
|
|
{"<version>", client_server_version_setup },
|
|
{"<sensors_setup>", client_sensors_setup },
|
|
{"<hostname>", client_hostname_setup },
|
|
{"<sysname>", client_sysname_setup },
|
|
{"<cpu_setup>", client_cpu_setup },
|
|
{"<disk_setup>", client_disk_setup },
|
|
{"<inet_setup>", client_inet_setup },
|
|
{"<net_setup>", client_net_setup },
|
|
{"<fs_setup>", client_fs_setup },
|
|
{"<mail_setup>", client_mail_setup },
|
|
{"<apm_setup>", client_battery_setup },
|
|
{"<battery_setup>", client_battery_setup },
|
|
{"<time>", client_time_line_from_server},
|
|
{"<monitors>", client_monitor_setup },
|
|
{"<decimal_point>", client_server_decimal_point },
|
|
{"<error>", client_server_error },
|
|
{"<io_timeout>", client_server_io_timeout},
|
|
{"<reconnect_timeout>", client_server_reconnect_timeout},
|
|
|
|
{"<plugin_setup>", client_plugin_add_setup_line},
|
|
};
|
|
|
|
|
|
typedef struct
|
|
{
|
|
GkrellmMonitor *mon;
|
|
gchar *key_name;
|
|
void (*func_cb)(gchar *line);
|
|
}
|
|
ClientPlugin;
|
|
|
|
static GList *client_plugin_serve_data_list;
|
|
static GList *plugin_initial_update_list;
|
|
|
|
static GkrellmFunc
|
|
client_plugin_func(gchar *line)
|
|
{
|
|
GList *list;
|
|
ClientPlugin *plug;
|
|
void (*func)() = NULL;
|
|
gchar *s;
|
|
gint n;
|
|
|
|
for (list = client_plugin_serve_data_list; list; list = list->next)
|
|
{
|
|
plug = (ClientPlugin *) list->data;
|
|
if (!plug->key_name)
|
|
continue;
|
|
n = strlen(plug->key_name);
|
|
s = line + n;
|
|
if (*s == '>' && !strncmp(plug->key_name, line, n))
|
|
{
|
|
func = plug->func_cb;
|
|
break;
|
|
}
|
|
}
|
|
return func;
|
|
}
|
|
|
|
static void
|
|
client_plugin_initial_update(ClientPlugin *plug)
|
|
{
|
|
GList *list;
|
|
void (*func)(gchar *);
|
|
gchar *line, *serve_name;
|
|
|
|
func = NULL;
|
|
serve_name = g_strdup_printf("<%s>", plug->key_name);
|
|
for (list = plugin_initial_update_list; list; list = list->next)
|
|
{
|
|
line = (gchar *) list->data;
|
|
if (*line == '<')
|
|
{
|
|
func = NULL;
|
|
if (!strcmp(line, serve_name))
|
|
func = plug->func_cb;
|
|
}
|
|
else if (func)
|
|
(*func)(line);
|
|
}
|
|
g_free(serve_name);
|
|
}
|
|
|
|
void
|
|
gkrellm_client_plugin_serve_data_connect(GkrellmMonitor *mon,
|
|
gchar *key_name, void (*func_cb)(gchar *line))
|
|
{
|
|
ClientPlugin *plug;
|
|
|
|
if (!mon || !key_name || !func_cb)
|
|
return;
|
|
plug = g_new0(ClientPlugin, 1);
|
|
plug->mon = mon;
|
|
plug->key_name = g_strdup(key_name);
|
|
plug->func_cb = func_cb;
|
|
client_plugin_serve_data_list
|
|
= g_list_append(client_plugin_serve_data_list, plug);
|
|
|
|
client_plugin_initial_update(plug);
|
|
}
|
|
|
|
static gboolean
|
|
client_send_to_server(gchar *buf)
|
|
{
|
|
gint n;
|
|
|
|
if (!server_alive || client_fd < 0 || !buf)
|
|
return FALSE;
|
|
#if defined(MSG_NOSIGNAL)
|
|
n = send(client_fd, buf, strlen(buf), MSG_NOSIGNAL);
|
|
#else
|
|
n = send(client_fd, buf, strlen(buf), 0);
|
|
#endif
|
|
if (n < 0 && errno == EPIPE)
|
|
{
|
|
if (_GK.debug_level & DEBUG_CLIENT)
|
|
printf("Write on closed pipe to gkrellmd server.\n");
|
|
server_alive = FALSE;
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gkrellm_client_send_to_server(gchar *key_name, gchar *line)
|
|
{
|
|
gchar *str;
|
|
gboolean result;
|
|
|
|
if (!key_name || !line || !*line)
|
|
return FALSE;
|
|
str = g_strdup_printf("<%s>\n", key_name);
|
|
client_send_to_server(str);
|
|
g_free(str);
|
|
if (line[strlen(line) - 1] != '\n')
|
|
{
|
|
str = g_strdup_printf("%s\n", line);
|
|
result = client_send_to_server(str);
|
|
g_free(str);
|
|
}
|
|
else
|
|
result = client_send_to_server(line);
|
|
|
|
return result;
|
|
}
|
|
|
|
KeyTable update_table[] =
|
|
{
|
|
{"<cpu>", client_cpu_line_from_server},
|
|
{"<proc>", client_proc_line_from_server},
|
|
{"<disk>", client_disk_line_from_server},
|
|
{"<net>", client_net_line_from_server},
|
|
{"<net_routed>", client_net_routed_line_from_server},
|
|
{"<net_timer>", client_net_timer_line_from_server},
|
|
{"<mem>", client_mem_line_from_server},
|
|
{"<swap>", client_swap_line_from_server},
|
|
{"<fs>", client_fs_line_from_server},
|
|
{"<fs_fstab>", client_fstab_line_from_server},
|
|
{"<fs_mounts>", client_mounts_line_from_server},
|
|
{"<inet>", client_inet_line_from_server},
|
|
{"<mail>", client_mail_line_from_server},
|
|
{"<apm>", client_battery_line_from_server},
|
|
{"<battery>", client_battery_line_from_server},
|
|
{"<sensors>", client_sensors_line_from_server},
|
|
{"<time>", client_time_line_from_server},
|
|
{"<uptime>", client_uptime_line_from_server},
|
|
|
|
// {"<>", client__line_from_server},
|
|
};
|
|
|
|
|
|
|
|
static gint
|
|
getline(gint fd, gchar *buf, gint len)
|
|
{
|
|
fd_set read_fds;
|
|
struct timeval tv;
|
|
gchar *s;
|
|
gint result, n, nread = 0;
|
|
|
|
FD_ZERO(&read_fds);
|
|
FD_SET(fd, &read_fds);
|
|
tv.tv_usec = 0;
|
|
tv.tv_sec = 15;
|
|
s = buf;
|
|
*s = '\0';
|
|
for (n = 0; n < len - 1; ++n)
|
|
{
|
|
nread = 0;
|
|
result = select(fd + 1, &read_fds, NULL, NULL, &tv);
|
|
if (result <= 0 || (nread = recv(fd, s, 1, 0)) != 1)
|
|
break;
|
|
if (*s == '\n')
|
|
{
|
|
*s = '\0';
|
|
break;
|
|
}
|
|
*++s = '\0';
|
|
}
|
|
if (nread < 0 && errno != EINTR)
|
|
{
|
|
fprintf(stderr, "Broken server connection\n");
|
|
exit(0);
|
|
}
|
|
if (_GK.debug_level & DEBUG_CLIENT)
|
|
printf("%s\n", buf);
|
|
return n;
|
|
}
|
|
|
|
static void
|
|
process_server_line(KeyTable *table, gint table_size, gchar *line)
|
|
{
|
|
static void (*func)(gchar *);
|
|
gint i;
|
|
|
|
if (!*line || *line == '#')
|
|
return;
|
|
|
|
if (*line == '<')
|
|
{
|
|
func = NULL;
|
|
if (line[1] == '.')
|
|
{
|
|
server_time.tm_sec = atoi(line + 2);
|
|
return;
|
|
}
|
|
for (i = 0; i < table_size; ++i)
|
|
{
|
|
if (!strcmp(table[i].key, line))
|
|
{
|
|
func = table[i].func;
|
|
break;
|
|
}
|
|
}
|
|
if (!func)
|
|
func = client_plugin_func(line + 1);
|
|
}
|
|
else if (func)
|
|
(*func)(line);
|
|
if (!func && !setup_done)
|
|
plugin_initial_update_list
|
|
= g_list_append(plugin_initial_update_list, g_strdup(line));
|
|
}
|
|
|
|
|
|
/* Read setup info from gkrellmd server. Stuff needed before the
|
|
| client_init calls must be read here.
|
|
*/
|
|
static gboolean
|
|
read_server_setup(gint fd)
|
|
{
|
|
gchar buf[256];
|
|
gint table_size;
|
|
|
|
/* Pre 2.1.6 gkrellmd does not send <decimal_point>, so put a fallback
|
|
| locale_sync() here.
|
|
*/
|
|
locale_sync();
|
|
_GK.client_server_read_time = time(0);
|
|
table_size = sizeof(setup_table) / sizeof(KeyTable);
|
|
|
|
gkrellm_free_glist_and_data(&client_plugin_setup_line_list);
|
|
|
|
gint rs;
|
|
|
|
while (1)
|
|
{
|
|
rs=getline(fd, buf, sizeof(buf));
|
|
if(rs<0)
|
|
return FALSE;
|
|
if (!strcmp(buf, "</gkrellmd_setup>"))
|
|
break;
|
|
process_server_line(&setup_table[0], table_size, buf);
|
|
}
|
|
|
|
/* Reset any data that is not cumulative. gkrellmd sends .clear tags
|
|
| for fstab and mounts, but inet does not. So fix it here.
|
|
*/
|
|
client_inet_reset();
|
|
|
|
/* Read the initial update data
|
|
*/
|
|
table_size = sizeof(update_table) / sizeof(KeyTable);
|
|
while (1)
|
|
{
|
|
rs=getline(fd, buf, sizeof(buf));
|
|
if(rs<0)
|
|
return FALSE;
|
|
if (!strcmp(buf, "</initial_update>"))
|
|
break;
|
|
process_server_line(&update_table[0], table_size, buf);
|
|
}
|
|
setup_done = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
gkrellm_client_mode_disconnect(void)
|
|
{
|
|
if (client_fd >= 0)
|
|
close(client_fd);
|
|
client_fd = -1;
|
|
server_alive = FALSE;
|
|
gdk_input_remove(client_input_id);
|
|
client_input_id = 0;
|
|
}
|
|
|
|
static void
|
|
read_server_input(gpointer data, gint fd, GdkInputCondition condition)
|
|
{
|
|
gchar *line, *eol;
|
|
gint count, n, table_size;
|
|
|
|
n = sizeof(server_buf) - buf_index - 1;
|
|
count = recv(fd, server_buf + buf_index, n, 0);
|
|
if (count <= 0)
|
|
{
|
|
gkrellm_client_mode_disconnect();
|
|
return;
|
|
}
|
|
if (_GK.time_now > 0)
|
|
_GK.client_server_read_time = _GK.time_now;
|
|
server_buf[buf_index + count] = '\0';
|
|
line = server_buf;
|
|
table_size = sizeof(update_table) / sizeof(KeyTable);
|
|
while (*line && (eol = strchr(line, '\n')) != NULL)
|
|
{
|
|
*eol = '\0';
|
|
if (_GK.debug_level & DEBUG_CLIENT)
|
|
printf("%s\n", line);
|
|
process_server_line(&update_table[0], table_size, line);
|
|
line = eol + 1;
|
|
}
|
|
if (line != server_buf && *line)
|
|
{
|
|
buf_index = strlen(line);
|
|
memmove(server_buf, line, buf_index);
|
|
}
|
|
else if (!*line)
|
|
buf_index = 0;
|
|
else
|
|
{
|
|
buf_index += count;
|
|
if (buf_index >= sizeof(server_buf) - 2)
|
|
buf_index = 0;
|
|
}
|
|
|
|
server_buf[buf_index] = '\0';
|
|
}
|
|
|
|
|
|
gint
|
|
gkrellm_connect_to(gchar *server, gint server_port)
|
|
{
|
|
gint fd = -1;
|
|
#ifdef HAVE_GETADDRINFO
|
|
gint rv = 0;
|
|
struct addrinfo hints, *res, *res0;
|
|
gchar portnumber[6];
|
|
#else
|
|
struct hostent *addr;
|
|
struct sockaddr_in s;
|
|
#endif
|
|
|
|
#ifdef HAVE_GETADDRINFO
|
|
snprintf (portnumber, sizeof(portnumber), "%d", server_port);
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = PF_UNSPEC;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_protocol = 0;
|
|
if ((rv = getaddrinfo(server, portnumber, &hints, &res0)) != 0)
|
|
return -1;
|
|
|
|
for (res = res0; res; res = res->ai_next)
|
|
{
|
|
if ((fd = socket(res->ai_family, res->ai_socktype,
|
|
res->ai_protocol)) < 0)
|
|
continue;
|
|
if (_GK.debug_level & DEBUG_CLIENT)
|
|
printf("\t[gkrellm_connect_to: (%d,%d,%d) %s:%d]\n",
|
|
res->ai_family, res->ai_socktype,
|
|
res->ai_protocol, server, server_port);
|
|
if (connect(fd, res->ai_addr, res->ai_addrlen) >= 0)
|
|
break;
|
|
#ifdef WIN32
|
|
closesocket(fd);
|
|
#else
|
|
close(fd);
|
|
#endif
|
|
fd = -1;
|
|
}
|
|
freeaddrinfo(res0);
|
|
#else
|
|
if (_GK.debug_level & DEBUG_CLIENT)
|
|
printf("\t[gkrellm_connect_to: %s:%d]\n", server, server_port);
|
|
addr = gethostbyname(server);
|
|
if (addr)
|
|
{
|
|
fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (fd >= 0)
|
|
{
|
|
memset(&s, 0, sizeof(s));
|
|
memcpy(&s.sin_addr.s_addr, addr->h_addr, addr->h_length);
|
|
s.sin_family = AF_INET;
|
|
s.sin_port = htons(server_port);
|
|
if (connect(fd, (struct sockaddr *)&s, sizeof (s)) < 0)
|
|
{
|
|
#ifdef WIN32
|
|
closesocket(fd);
|
|
#else
|
|
close(fd);
|
|
#endif
|
|
fd = -1;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if (fd < 0)
|
|
return -1;
|
|
|
|
return fd;
|
|
}
|
|
|
|
gboolean
|
|
gkrellm_client_mode_connect(void)
|
|
{
|
|
gchar buf[128];
|
|
|
|
if (_GK.server_port == 0)
|
|
_GK.server_port = GKRELLMD_SERVER_PORT;
|
|
|
|
client_fd = gkrellm_connect_to(_GK.server, _GK.server_port);
|
|
if (client_fd < 0) {
|
|
printf("%s\n", _("Unable to connect."));
|
|
return FALSE;
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf), "gkrellm %d.%d.%d%s\n",
|
|
GKRELLM_VERSION_MAJOR, GKRELLM_VERSION_MINOR,
|
|
GKRELLM_VERSION_REV, GKRELLM_EXTRAVERSION);
|
|
send(client_fd, buf, strlen(buf), 0);
|
|
|
|
/* Initial setup lines from server are read in blocking mode.
|
|
*/
|
|
if(!read_server_setup(client_fd)){
|
|
close(client_fd);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Extra stuff not handled in read_server_setup()
|
|
*/
|
|
gkrellm_mail_local_unsupported();
|
|
|
|
/* Now switch to non blocking and set up a read handler.
|
|
*/
|
|
#ifndef WIN32
|
|
fcntl(client_fd, F_SETFL, O_NONBLOCK);
|
|
#endif
|
|
client_input_id = gdk_input_add(client_fd, GDK_INPUT_READ,
|
|
(GdkInputFunction) read_server_input, NULL);
|
|
|
|
server_alive = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static gboolean client_mode_thread_busy;
|
|
|
|
gint
|
|
gkrellm_client_server_connect_state(void)
|
|
{
|
|
if (client_mode_thread_busy) /* reconnect in progress? */
|
|
return 2;
|
|
if (_GK.client_mode && client_input_id > 0) /* Currently connected? */
|
|
return 1;
|
|
else if (_GK.client_mode)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
static gpointer
|
|
client_mode_connect_thread(void *data)
|
|
{
|
|
gkrellm_client_mode_connect();
|
|
client_mode_thread_busy = FALSE;
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
gkrellm_client_mode_connect_thread(void)
|
|
{
|
|
if (client_mode_thread_busy || !_GK.client_mode)
|
|
return;
|
|
client_mode_thread_busy = TRUE;
|
|
g_thread_create(client_mode_connect_thread, NULL, FALSE, NULL);
|
|
}
|
|
|
|
|
|
gboolean
|
|
gkrellm_client_mode(void)
|
|
{
|
|
return _GK.client_mode;
|
|
}
|