[ltp] ibm_acpi support in GkrellM
Stanislav Likavcan
linux-thinkpad@linux-thinkpad.org
Fri, 06 May 2005 13:23:07 +0200
This is a multi-part message in MIME format.
--------------090009040302070302030304
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=ISO-8859-1
Hi everyone,
I have just added a support of our favorite a widely used :-) ibm_acpi
kernel module into Sensors build panel of GkrellM. GkrellM now
supports temperature and fan speed reading and threshold evaluating.
Installation:
- download gkrellm-2.2.5 sources from http://www.gkrellm.net/
- unpack sources package:
tar -zxvf gkrellm-2.2.5.tar.gz
- copy attached linux.c file into gkrellm-2.2.5/src/sysdeps/linux.c
cp linux.c gkrellm-2.2.5/src/sysdeps/linux.c
- compile
cd gkrellm-2.2.5 ; make
- run & enjoy (you shold also configure it throgh GUI)
./src/gkrellm &
- install (optional):
make install
Hope you will find it useful :-)
Stano
PS: Please, drop me en e-mail if you find any error. Any suggestions are
welcomed.
--------------090009040302070302030304
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
name="patch-linux.c"
Content-Disposition: inline;
filename="patch-linux.c"
--- gkrellm-2.2.5/src/sysdeps/linux.c 2005-05-05 13:47:03.000000000 +0200
+++ gkrellm-2.2.5/src/sysdeps/linux.c_orig 2005-03-16 21:19:32.000000000 +0100
@@ -1883,19 +1883,6 @@
#define THERMAL_INTERFACE 1
#define THERMAL_ZONE_INTERFACE 2
#define NVIDIA_SETTINGS_INTERFACE 3
-#define IBM_ACPI_INTERFACE 4
-
-#define IBM_ACPI_FAN_FILE "/proc/acpi/ibm/fan"
-#define IBM_ACPI_THERMAL "/proc/acpi/ibm/thermal"
-#define IBM_ACPI_CPU_TEMP 0
-#define IBM_ACPI_PCI_TEMP 1
-#define IBM_ACPI_HDD_TEMP 2
-#define IBM_ACPI_GPU_TEMP 3
-#define IBM_ACPI_BAT1_TEMP 4
-#define IBM_ACPI_NA1_TEMP 5
-#define IBM_ACPI_BAT2_TEMP 6
-#define IBM_ACPI_NA2_TEMP 7
-#define IBM_ACPI_FAN 8
typedef struct
{
@@ -2031,7 +2018,7 @@
FILE *f;
gchar buf[64], units[32];
gint n;
- gfloat T, t[5], ibm_acpi_temp[8];;
+ gfloat T, t[5];
gboolean result = FALSE;
if ( interface == THERMAL_INTERFACE
@@ -2058,29 +2045,6 @@
return result;
}
- if (interface == IBM_ACPI_INTERFACE)
- {
- f = fopen(sensor_path, "r");
- if (f)
- {
- fgets(buf, sizeof(buf), f);
- sscanf(buf, "temperatures: %f %f %f %f %f %f %f %f",
- &ibm_acpi_temp[IBM_ACPI_CPU_TEMP],
- &ibm_acpi_temp[IBM_ACPI_PCI_TEMP],
- &ibm_acpi_temp[IBM_ACPI_HDD_TEMP],
- &ibm_acpi_temp[IBM_ACPI_GPU_TEMP],
- &ibm_acpi_temp[IBM_ACPI_BAT1_TEMP],
- &ibm_acpi_temp[IBM_ACPI_NA1_TEMP],
- &ibm_acpi_temp[IBM_ACPI_BAT2_TEMP],
- &ibm_acpi_temp[IBM_ACPI_NA2_TEMP]);
-
- *temp = ibm_acpi_temp[iodev];
- result = TRUE;
- }
- fclose(f);
- return result;
- }
-
if (interface == HDDTEMP_INTERFACE)
{
gkrellm_sys_sensors_hddtemp_check();
@@ -2143,27 +2107,10 @@
gkrellm_sys_sensors_get_fan(gchar *sensor_path, gint id,
gint iodev, gint interface, gfloat *fan)
{
- FILE *f;
- gchar buf[64];
- gint n;
- gfloat t[4], T;
- gboolean result = FALSE;
-
- if (interface == IBM_ACPI_INTERFACE)
- {
- f = fopen(sensor_path, "r");
- if (f)
- {
- fgets(buf, sizeof(buf), f);
- fgets(buf, sizeof(buf), f);
- sscanf(buf, "speed: %f", &T);
- *fan = T;
- result = TRUE;
- }
- fclose(f);
- return result;
- }
-
+ FILE *f;
+ gchar buf[64];
+ gint n;
+ gfloat t[4];
if (interface == MBMON_INTERFACE)
{
@@ -2422,7 +2369,6 @@
gboolean
gkrellm_sys_sensors_init(void)
{
- FILE *f;
GDir *dir, *chip_dir;
VoltDefault *voltdefault;
gint id = 0;
@@ -2479,55 +2425,6 @@
gkrellm_sys_sensors_hddtemp_check();
gkrellm_sys_sensors_mbmon_check(TRUE);
- /*
- * IBM ACPI Temperature Sensors
- */
- if ((f = fopen(IBM_ACPI_THERMAL, "r")) != NULL)
- {
- gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE,
- IBM_ACPI_THERMAL, "IBM ACPI CPU",
- id, IBM_ACPI_CPU_TEMP, IBM_ACPI_INTERFACE,
- 1.0, 0.0, NULL, "CPU");
-
- gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE,
- IBM_ACPI_THERMAL, "IBM ACPI Mini PCI Module",
- id, IBM_ACPI_PCI_TEMP, IBM_ACPI_INTERFACE,
- 1.0, 0.0, NULL, "PCI");
-
- gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE,
- IBM_ACPI_THERMAL, "IBM ACPI HDD",
- id, IBM_ACPI_HDD_TEMP, IBM_ACPI_INTERFACE,
- 1.0, 0.0, NULL, "HDD");
-
- gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE,
- IBM_ACPI_THERMAL, "IBM ACPI GPU",
- id, IBM_ACPI_GPU_TEMP, IBM_ACPI_INTERFACE,
- 1.0, 0.0, NULL, "GPU");
-
- gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE,
- IBM_ACPI_THERMAL, "IBM ACPI Battery 1",
- id, IBM_ACPI_BAT1_TEMP, IBM_ACPI_INTERFACE,
- 1.0, 0.0, NULL, "BAT1");
-
- gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE,
- IBM_ACPI_THERMAL, "IBM ACPI Battery 2",
- id, IBM_ACPI_BAT2_TEMP, IBM_ACPI_INTERFACE,
- 1.0, 0.0, NULL, "BAT2");
- fclose(f);
- }
-
- /*
- * IBM ACPI Cooling Fan Sensor
- */
- if ((f = fopen(IBM_ACPI_FAN_FILE, "r")) != NULL)
- {
- gkrellm_sensors_add_sensor(SENSOR_FAN,
- IBM_ACPI_FAN_FILE, "IBM ACPI Fan Sensor",
- id, IBM_ACPI_FAN, IBM_ACPI_INTERFACE,
- 1.0, 0.0, NULL, "Fan");
- fclose(f);
- }
-
if (gkrellm_sys_sensors_get_temperature("GPUCoreTemp", id, 0,
NVIDIA_SETTINGS_INTERFACE, NULL))
{
--------------090009040302070302030304
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
name="linux.c"
Content-Disposition: inline;
filename="linux.c"
/* GKrellM
| Copyright (C) 1999-2004 Bill Wilson
|
| Author: Bill Wilson bill@gkrellm.net
| Latest versions might be found at: http://gkrellm.net
|
| This program is free software which I release under the GNU General Public
| License. You may redistribute and/or modify this program under the terms
| of that license as published by the Free Software Foundation; either
| version 2 of the License, or (at your option) any later version.
|
| This program 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. Version 2 is in the
| COPYRIGHT file in the top level directory of this distribution.
|
| To get a copy of the GNU General Puplic License, write to the Free Software
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <limits.h>
#include <errno.h>
#include <locale.h>
#include <sys/types.h>
#include <dirent.h>
static gboolean need_locale_fix,
have_diskstats,
have_partition_stats,
have_sysfs,
have_sysfs_stats,
have_sysfs_sensors;
static gchar locale_decimal_point;
static gboolean kernel_2_4,
kernel_2_6;
static gint os_major,
os_minor,
os_rev;
static gboolean
os_release(gint major, gint minor, gint rev)
{
if ( os_major > major
|| (os_major == major && os_minor > minor)
|| (os_major == os_major && os_minor == minor && os_rev >= rev)
)
return TRUE;
return FALSE;
}
void
gkrellm_sys_main_init(void)
{
FILE *f;
gchar buf[1024];
if ((f = fopen("/proc/sys/kernel/osrelease", "r")) != NULL)
{
if (fgets(buf, sizeof(buf), f) != NULL)
sscanf(buf, "%d.%d.%d", &os_major, &os_minor, &os_rev);
fclose(f);
kernel_2_4 = os_release(2, 4, 0);
kernel_2_6 = os_release(2, 6, 0);
}
/* Various stats are in sysfs since 2.5.47, but it may not be mounted.
*/
if ((f = fopen("/proc/mounts", "r")) != NULL)
{
while (fgets(buf, sizeof(buf), f) != NULL)
if (strstr(buf, "sysfs"))
{
have_sysfs = TRUE;
break;
}
fclose(f);
}
}
void
gkrellm_sys_main_cleanup(void)
{
}
/* Linux /proc always reports floats with '.' decimal points, but sscanf()
| for some locales needs commas in place of periods. So, if current
| locale doesn't use periods, must insert the correct decimal point char.
*/
static void
locale_fix(gchar *buf)
{
gchar *s;
for (s = buf; *s; ++s)
if (*s == '.')
*s = locale_decimal_point;
}
/* ===================================================================== */
/* CPU, disk, and swap monitor interfaces might all get data from /proc/stat
| (depending on kernel version) so they will share reading of /proc/stat.
*/
#define PROC_STAT_FILE "/proc/stat"
static gint n_cpus;
static gulong swapin,
swapout;
/* CPU, and Disk monitors call this in their update routine.
| Whoever calls it first will read the data for everyone.
|
| /proc/stat has cpu entries like:
| cpu total_user total_nice total_sys total_idle
| cpu0 cpu0_user cpu0_nice cpu0_sys cpu0_idle
| ...
| cpuN cpuN_user cpuN_nice cpuN_sys cpuN_idle
| where ticks for cpu are jiffies * smp_num_cpus
| and ticks for cpu[i] are jiffies (1/CLK_TCK)
*/
static void
linux_read_proc_stat(void)
{
static FILE *f;
gint n, i, ncpu;
gchar *s, buf[1024];
gulong rblk[4], wblk[4], user, nice, sys, idle, iowait;
static gint data_read_tick = -1;
n = gkrellm_get_timer_ticks();
if (data_read_tick == n)
return; /* Just one read per tick (multiple monitors call this) */
if (!f && (f = fopen(PROC_STAT_FILE, "r")) == NULL)
return;
data_read_tick = n;
gkrellm_disk_reset_composite();
ncpu = 0;
while ((fgets(buf, sizeof(buf), f)) != NULL)
{
if (buf[0] == 'c' && buf[1] == 'p')
{
n = sscanf(buf, "%*s %lu %lu %lu %lu %lu",
&user, &nice, &sys, &idle, &iowait);
if (n == 5) /* iowait is new in kernel 2.5 */
idle += iowait;
if (n_cpus > 1)
{
if (ncpu == 0)
gkrellm_cpu_assign_composite_data(user, nice, sys, idle);
else
gkrellm_cpu_assign_data(ncpu - 1, user, nice, sys, idle);
}
/* If have cpu and cpu0 on single cpu machines, won't use cpu0.
*/
else if (ncpu == 0)
gkrellm_cpu_assign_data(0, user, nice, sys, idle);
++ncpu;
continue;
}
if (!kernel_2_6 && !strncmp("swap", buf, 4))
{
sscanf(buf + 5, "%lu %lu", &swapin, &swapout);
continue;
}
if (buf[0] != 'd')
continue;
if (!strncmp("disk_rblk", buf, 9))
{
s = buf + 10;
for (i = 0; i < 4; ++i)
rblk[i] = strtoul(s, &s, 0);
}
else if (!strncmp("disk_wblk", buf, 9))
{
s = buf + 10;
for (i = 0; i < 4; ++ i)
{
wblk[i] = strtoul(s, &s, 0);
gkrellm_disk_assign_data_nth(i, 512 * rblk[i], 512 * wblk[i]);
}
}
else if (!strncmp("disk_io:", buf, 8)) /* Kernel 2.4 only */
{
gint major, i_disk;
gulong rblk, wblk, rb1, rb2, wb1, wb2;
s = strtok(buf + 9, " \t\n");
while (s)
{
/* disk_io lines in 2.4.x kernels have had 2 formats */
n = sscanf(s, "(%d,%d):(%*d,%lu,%lu,%lu,%lu)",
&major, &i_disk, &rb1, &rb2, &wb1, &wb2);
if (n == 6) /* patched as of 2.4.0-test1-ac9 */
{ /* (major,disk):(total_io,rio,rblk,wio,wblk) */
rblk = rb2;
wblk = wb2;
}
else /* 2.3.99-pre8 to 2.4.0-testX */
{ /* (major,disk):(rio,rblk,wio,wblk) */
rblk = rb1;
wblk = wb1;
}
/* floppys and CDroms don't show up in /proc/partitions.
*/
if (have_partition_stats)
{
gchar name[32];
name[0] = '\0';
if (major == 2)
sprintf(name, "fd%d", i_disk);
else if (major == 11)
sprintf(name, "scd%d", i_disk);
if (name[0])
gkrellm_disk_assign_data_by_name(name,
512 * rblk, 512 * wblk);
}
else
gkrellm_disk_assign_data_by_device(major, i_disk,
512 * rblk, 512 * wblk);
s = strtok(NULL, " \t\n");
}
}
}
rewind(f);
}
/* ===================================================================== */
/* CPU monitor interface */
void
gkrellm_sys_cpu_read_data(void)
{
/* One routine reads cpu, disk, and swap data. All three monitors will
| call it, but only the first call per timer tick will do the work.
*/
linux_read_proc_stat();
}
gboolean
gkrellm_sys_cpu_init(void)
{
FILE *f;
gchar buf[1024];
if ((f = fopen(PROC_STAT_FILE, "r")) == NULL)
return FALSE;
while (fgets(buf, sizeof(buf), f))
{
if (strncmp(buf, "cpu", 3) != 0)
continue;
++n_cpus;
}
fclose(f);
/* If multiple CPUs, the first one will be a composite. Report only real.
*/
if (n_cpus > 1)
--n_cpus;
gkrellm_cpu_set_number_of_cpus(n_cpus);
return TRUE;
}
/* ===================================================================== */
/* Disk monitor interface */
#define PROC_PARTITIONS_FILE "/proc/partitions"
#define PROC_DISKSTATS_FILE "/proc/diskstats"
#include <linux/major.h>
#if ! defined (SCSI_DISK0_MAJOR)
#define SCSI_DISK0_MAJOR 8
#endif
#if ! defined (MD_MAJOR)
#define MD_MAJOR 9
#endif
#if !defined(IDE4_MAJOR)
#define IDE4_MAJOR 56
#endif
#if !defined(IDE5_MAJOR)
#define IDE5_MAJOR 57
#endif
#if !defined(IDE6_MAJOR)
#define IDE6_MAJOR 88
#endif
#if !defined(IDE7_MAJOR)
#define IDE7_MAJOR 89
#endif
#if !defined(IDE8_MAJOR)
#define IDE8_MAJOR 90
#endif
#if !defined(IDE9_MAJOR)
#define IDE9_MAJOR 91
#endif
#if !defined(DAC960_MAJOR)
#define DAC960_MAJOR 48
#endif
#if !defined(COMPAQ_SMART2_MAJOR)
#define COMPAQ_SMART2_MAJOR 72
#endif
#if !defined(COMPAQ_CISS_MAJOR)
#define COMPAQ_CISS_MAJOR 104
#endif
#if !defined(LVM_BLK_MAJOR)
#define LVM_BLK_MAJOR 58
#endif
#if !defined(NBD_MAJOR)
#define NBD_MAJOR 43
#endif
struct _disk_name_map
{
gchar *name;
gint major;
gint minor_mod;
gchar suffix_base;
};
/* Disk charts will appear in GKrellM in the same order as this table.
*/
static struct _disk_name_map
disk_name_map[] =
{
{"hd", IDE0_MAJOR, 64, 'a' }, /* 3: hda, hdb */
{"hd", IDE1_MAJOR, 64, 'c' }, /* 22: hdc, hdd */
{"hd", IDE2_MAJOR, 64, 'e' }, /* 33: hde, hdf */
{"hd", IDE3_MAJOR, 64, 'g' }, /* 34: hdg, hdh */
{"hd", IDE4_MAJOR, 64, 'i' }, /* 56: hdi, hdj */
{"hd", IDE5_MAJOR, 64, 'k' }, /* 57: hdk, hdl */
{"hd", IDE6_MAJOR, 64, 'm' }, /* 88: hdm, hdn */
{"hd", IDE7_MAJOR, 64, 'o' }, /* 89: hdo, hdp */
{"hd", IDE8_MAJOR, 64, 'q' }, /* 90: hdq, hdr */
{"hd", IDE9_MAJOR, 64, 's' }, /* 91: hds, hdt */
{"sd", SCSI_DISK0_MAJOR, 16, 'a' }, /* 8: sda-sdh */
{"sg", SCSI_GENERIC_MAJOR, 16, '0' }, /* 21: sg0-sg16 */
{"scd", SCSI_CDROM_MAJOR, 16, '0' }, /* 11: scd0-scd16 */
{"sr", SCSI_CDROM_MAJOR, 16, '0' }, /* 11: sr0-sr16 */
{"md", MD_MAJOR, 0, '0' }, /* 9: md0-md3 */
{"c0d", DAC960_MAJOR, 32, '0' }, /* 48: c0d0-c0d31 */
{"c1d", DAC960_MAJOR + 1, 32, '0' }, /* 49: c1d0-c1d31 */
{"c2d", DAC960_MAJOR + 2, 32, '0' }, /* 50: c2d0-c2d31 */
{"c3d", DAC960_MAJOR + 3, 32, '0' }, /* 51: c3d0-c3d31 */
{"c4d", DAC960_MAJOR + 4, 32, '0' }, /* 52: c4d0-c4d31 */
{"c5d", DAC960_MAJOR + 5, 32, '0' }, /* 53: c5d0-c5d31 */
{"c6d", DAC960_MAJOR + 6, 32, '0' }, /* 54: c6d0-c6d31 */
{"c7d", DAC960_MAJOR + 7, 32, '0' }, /* 55: c7d0-c7d31 */
{"cs0d", COMPAQ_SMART2_MAJOR, 16, '0' }, /* 72: c0d0-c0d15 */
{"cs1d", COMPAQ_SMART2_MAJOR + 1, 16, '0' }, /* 73: c1d0-c1d15 */
{"cs2d", COMPAQ_SMART2_MAJOR + 2, 16, '0' }, /* 74: c2d0-c2d15 */
{"cs3d", COMPAQ_SMART2_MAJOR + 3, 16, '0' }, /* 75: c3d0-c3d15 */
{"cs4d", COMPAQ_SMART2_MAJOR + 4, 16, '0' }, /* 76: c4d0-c4d15 */
{"cs5d", COMPAQ_SMART2_MAJOR + 5, 16, '0' }, /* 77: c5d0-c5d15 */
{"cs6d", COMPAQ_SMART2_MAJOR + 6, 16, '0' }, /* 78: c6d0-c6d15 */
{"cs7d", COMPAQ_SMART2_MAJOR + 7, 16, '0' }, /* 79: c7d0-c7d15 */
{"cc0d", COMPAQ_CISS_MAJOR, 16, '0' }, /* 104: c0d0-c0d15 */
{"cc1d", COMPAQ_CISS_MAJOR + 1, 16, '0' }, /* 105: c1d0-c1d15 */
{"cc2d", COMPAQ_CISS_MAJOR + 2, 16, '0' }, /* 106: c2d0-c2d15 */
{"cc3d", COMPAQ_CISS_MAJOR + 3, 16, '0' }, /* 107: c3d0-c3d15 */
{"cc4d", COMPAQ_CISS_MAJOR + 4, 16, '0' }, /* 108: c4d0-c4d15 */
{"cc5d", COMPAQ_CISS_MAJOR + 5, 16, '0' }, /* 109: c5d0-c5d15 */
{"cc6d", COMPAQ_CISS_MAJOR + 6, 16, '0' }, /* 110: c6d0-c6d15 */
{"cc7d", COMPAQ_CISS_MAJOR + 7, 16, '0' }, /* 111: c7d0-c7d15 */
{"fd", FLOPPY_MAJOR, 0, '0' } /* 2: fd0-fd3 */
};
static gboolean
disk_major_ok(gint major)
{
gint i;
for (i = 0; i < sizeof(disk_name_map) / sizeof(struct _disk_name_map); ++i)
{
if (major == disk_name_map[i].major)
return TRUE;
}
return FALSE;
}
gchar *
gkrellm_sys_disk_name_from_device(gint device_number, gint unit_number,
gint *order)
{
struct _disk_name_map *dm = NULL;
gint i;
gchar suffix;
static gchar name[32];
for (i = 0; i < sizeof(disk_name_map) / sizeof(struct _disk_name_map); ++i)
{
if (device_number != disk_name_map[i].major)
continue;
dm = &disk_name_map[i];
break;
}
if (dm)
{
suffix = dm->suffix_base + unit_number;
sprintf(name, "%s%c", dm->name, suffix);
}
else
sprintf(name, "(%d,%d)", device_number, unit_number);
*order = i;
return name;
}
gint
gkrellm_sys_disk_order_from_name(gchar *name)
{
struct _disk_name_map *dm, *dm_next;
gint i, len, table_size;
gchar suffix;
table_size = sizeof(disk_name_map) / sizeof(struct _disk_name_map);
for (i = 0; i < table_size; ++i)
{
dm = &disk_name_map[i];
len = strlen(dm->name);
if (strncmp(dm->name, name, len))
continue;
suffix = name[len]; /* So far looked at only for "hd" series */
if (i < table_size - 1)
{
dm_next = &disk_name_map[i + 1];
if ( !strcmp(dm_next->name, dm->name)
&& dm_next->suffix_base <= suffix
)
continue;
}
break;
}
if (i >= table_size)
i = -1;
return i;
}
/* Given a /proc/partitions or /proc/diskstats line disk name in "partition",
| make "disk" have the whole disk name (eg hda) and "partition" have the
| partition (eg hda1) or NULL if not a partition. For simple names,
| "disk" is expected to initially have the whole disk name from the
| previous call (or NULL if this is the first call per /proc file parse).
*/
static gboolean
disk_get_device_name(gint major, gint minor, gchar *disk, gchar *partition)
{
struct _disk_name_map *dm = NULL;
gint i, unit = 0;
gchar *p, *d;
for (p = partition; *p; ++p)
if (*p == '/')
break;
if (!*p)
{ /* Have a simple name like hda, hda1, sda, ... */
d = disk;
p = partition;
while (*d && *p && *d++ == *p++)
;
if (d == disk || *d || *p < '0' || *p > '9')
{
strcpy(disk, partition);
partition[0] = '\0';
}
return TRUE;
}
/* Have a devfs name like ide/host0/bus0/target0/lun0/part1, so construct
| a name based on major, minor numbers and the disk_name_map[].
*/
for (i = 0; i < sizeof(disk_name_map) / sizeof(struct _disk_name_map); ++i)
{
if (major != disk_name_map[i].major)
continue;
dm = &disk_name_map[i];
if (dm->minor_mod > 0 && minor >= dm->minor_mod)
{
unit = minor / dm->minor_mod;
minor = minor % dm->minor_mod;
}
sprintf(disk, "%s%c", dm->name, dm->suffix_base + unit);
if (minor > 0)
sprintf(partition, "%s%d", disk, minor);
else
partition[0] = '\0';
return TRUE;
}
return FALSE;
}
/* Kernels >= 2.5.69 have /proc/diskstats which can be more efficient to
| read than getting stats from sysfs. See:
| /usr/src/linux/Documentation/iostats.txt
| But gkrellm calls this only for 2.6+ kernels since there were some
| format incompatible /proc/diskstats patches for 2.4.
*/
static void
linux_read_proc_diskstats(void)
{
static FILE *f;
gchar buf[1024], part[64], disk[64];
gint major, minor, n;
gulong rd, wr, rd1, wr1;
gboolean inactivity_override;
static gboolean initial_read = TRUE;
if (!f && (f = fopen(PROC_DISKSTATS_FILE, "r")) == NULL)
return;
disk[0] = '\0';
part[0] = '\0';
while ((fgets(buf, sizeof(buf), f)) != NULL)
{
/* major minor name rio rmerge rsect ruse wio wmerge wsect wuse
| running use aveq
| --or for a partition--
| major minor name rio rsect wio wsect
*/
n = sscanf(buf, "%d %d %64s %*d %lu %lu %lu %*d %*d %lu",
&major, &minor, part, &rd, &rd1, &wr, &wr1);
if (n == 7)
{
rd = rd1;
wr = wr1;
}
/* Make sure all real disks get reported (so they will be added to the
| disk monitor in order) the first time this function is called.
| Use disk_major_ok() instead of simply initial_read until I'm sure
| I'm testing for all the right "major" exclusions.
| Note: disk_get_device_name() assumes "part[]" retains results from
| previous calls and that disk/subdisk parsing will be in order
| (ie hda will be encountered before hda1).
*/
inactivity_override = initial_read ? disk_major_ok(major) : FALSE;
if ( (n != 7 && n != 6)
|| (rd == 0 && wr == 0 && !inactivity_override)
|| major == LVM_BLK_MAJOR || major == NBD_MAJOR
|| major == RAMDISK_MAJOR || major == LOOP_MAJOR
|| !disk_get_device_name(major, minor, disk, part)
)
continue;
if (part[0] == '\0')
gkrellm_disk_assign_data_by_name(disk, 512 * rd, 512 * wr);
else
gkrellm_disk_subdisk_assign_data_by_name(part, disk,
512 * rd, 512 * wr);
}
rewind(f);
initial_read = FALSE;
}
/* /proc/partitions can have diskstats in 2.4 kernels or in 2.5+ it's just
| a list of disk names which can be used to get names to look for in sysfs.
| But, with gkrellm 2.1.15 and for 2.6+kernels /proc/diskstats is
| used instead of sysfs.
*/
static void
linux_read_proc_partitions_or_sysfs(void)
{
FILE *f, *sf;
gchar buf[1024], part[64], disk[64], sysfspath[128];
gint major, minor, n;
gulong sectors, rd, wr;
if ((f = fopen(PROC_PARTITIONS_FILE, "r")) != NULL)
{
fgets(buf, sizeof(buf), f); /* header */
fgets(buf, sizeof(buf), f);
disk[0] = '\0';
part[0] = '\0';
while ((fgets(buf, sizeof(buf), f)) != NULL)
{
if (have_partition_stats)
{
/* major minor #blocks name
| rio rmerge rsect ruse wio wmerge wsect wuse running use aveq
*/
n = sscanf(buf, "%d %d %lu %64s %*d %*d %lu %*d %*d %*d %lu",
&major, &minor, §ors, part, &rd, &wr);
if ( n < 6 || sectors <= 1 || major == LVM_BLK_MAJOR
|| !disk_get_device_name(major, minor, disk, part)
)
continue;
}
if (have_sysfs_stats)
{
n = sscanf(buf, "%d %d %lu %64s",
&major, &minor, §ors, part);
if ( n < 4 || sectors <= 1 || major == LVM_BLK_MAJOR
|| !disk_get_device_name(major, minor, disk, part)
)
continue;
if (part[0] == '\0')
sprintf(sysfspath, "/sys/block/%s/stat", disk);
else
sprintf(sysfspath, "/sys/block/%s/%s/stat", disk, part);
if ((sf = fopen(sysfspath, "r")) != NULL)
{
fgets(buf, sizeof(buf), sf);
fclose(sf);
if (part[0] == '\0')
n = sscanf(buf,"%*d %*d %lu %*d %*d %*d %lu",&rd, &wr);
else
n = sscanf(buf,"%*d %lu %*d %lu", &rd, &wr);
if (n < 2)
continue;
}
}
if (part[0] == '\0')
gkrellm_disk_assign_data_by_name(disk, 512 * rd, 512 * wr);
else
gkrellm_disk_subdisk_assign_data_by_name(part, disk,
512 * rd, 512 * wr);
}
fclose(f);
}
}
void
gkrellm_sys_disk_read_data(void)
{
/* If have_partition_stats, still need to get floppy and CDrom data
| from /proc/stat
*/
if (!have_sysfs_stats && !have_diskstats)
linux_read_proc_stat();
if (have_diskstats)
linux_read_proc_diskstats();
else if (have_partition_stats || have_sysfs_stats)
linux_read_proc_partitions_or_sysfs();
}
gboolean
gkrellm_sys_disk_init(void)
{
FILE *f = NULL;
gchar buf[1024];
/* There were some incompatible /proc/diskstats patches for 2.4
*/
if (os_release(2,6,0) && (f = fopen(PROC_DISKSTATS_FILE, "r")) != NULL)
have_diskstats = TRUE;
else if ((f = fopen(PROC_PARTITIONS_FILE, "r")) != NULL)
{
if (fgets(buf, sizeof(buf), f))
{
if (strstr(buf, "rsect"))
have_partition_stats = TRUE;
else
{
if (have_sysfs)
have_sysfs_stats = TRUE;
}
}
}
if (f)
fclose(f);
if (_GK.debug_level & DEBUG_SYSDEP)
printf("diskstats=%d partition_stats=%d sysfs_stats=%d\n",
have_diskstats, have_partition_stats, have_sysfs_stats);
return TRUE;
}
/* ===================================================================== */
/* Proc monitor interface */
#include <utmp.h>
#include <paths.h>
#define PROC_LOADAVG_FILE "/proc/loadavg"
void
gkrellm_sys_proc_read_data(void)
{
FILE *f;
gchar buf[160];
gint n_running = 0, n_processes = 0;
gulong n_forks = 0;
gfloat fload = 0;
if ((f = fopen(PROC_LOADAVG_FILE, "r")) != NULL)
{
/* sscanf(buf, "%f") might fail to convert because for some locales
| commas are used for decimal points.
*/
fgets(buf, sizeof(buf), f);
if (need_locale_fix)
locale_fix(buf);
sscanf(buf,"%f %*f %*f %d/%d %lu", &fload,
&n_running, &n_processes, &n_forks);
fclose(f);
gkrellm_proc_assign_data(n_processes, n_running, n_forks, fload);
}
}
void
gkrellm_sys_proc_read_users(void)
{
struct utmp *ut;
struct stat s;
static time_t utmp_mtime;
gint n_users = 0;
if (stat(_PATH_UTMP, &s) == 0 && s.st_mtime != utmp_mtime)
{
setutent();
while ((ut = getutent()) != NULL)
if (ut->ut_type == USER_PROCESS && ut->ut_name[0] != '\0')
++n_users;
endutent();
utmp_mtime = s.st_mtime;
gkrellm_proc_assign_users(n_users);
}
}
gboolean
gkrellm_sys_proc_init(void)
{
struct lconv *lc;
lc = localeconv();
locale_decimal_point = *lc->decimal_point;
if (locale_decimal_point != '.')
need_locale_fix = TRUE;
return TRUE;
}
/* ===================================================================== */
/* Inet monitor interface */
#include "../inet.h"
#define PROC_NET_TCP_FILE "/proc/net/tcp"
#if defined(INET6)
#define PROC_NET_TCP6_FILE "/proc/net/tcp6"
#endif
void
gkrellm_sys_inet_read_tcp_data(void)
{
FILE *f;
ActiveTCP tcp;
gchar buf[512];
gint tcp_status;
gulong addr;
if ((f = fopen(PROC_NET_TCP_FILE, "r")) != NULL)
{
fgets(buf, sizeof(buf), f); /* header */
while (fgets(buf, sizeof(buf), f))
{
sscanf(buf, "%*d: %*x:%x %lx:%x %x", &tcp.local_port,
&addr, &tcp.remote_port, &tcp_status);
tcp.remote_addr.s_addr = (uint32_t) addr;
tcp.family = AF_INET;
if (tcp_status != TCP_ALIVE)
continue;
gkrellm_inet_log_tcp_port_data(&tcp);
}
fclose(f);
}
#if defined(INET6)
if ((f = fopen(PROC_NET_TCP6_FILE, "r")) != NULL)
{
fgets(buf, sizeof(buf), f); /* Waste first line */
while (fgets(buf, sizeof(buf), f))
{
sscanf(buf, "%*d: %*x:%x %8x%8x%8x%8x:%x %x",
&tcp.local_port,
&tcp.remote_addr6.s6_addr32[0],
&tcp.remote_addr6.s6_addr32[1],
&tcp.remote_addr6.s6_addr32[2],
&tcp.remote_addr6.s6_addr32[3],
&tcp.remote_port, &tcp_status);
tcp.family = AF_INET6;
if (tcp_status != TCP_ALIVE)
continue;
gkrellm_inet_log_tcp_port_data(&tcp);
}
fclose(f);
}
#endif /* INET6 */
}
gboolean
gkrellm_sys_inet_init(void)
{
return TRUE;
}
/* ===================================================================== */
/* Net monitor interface */
#define PROC_NET_DEV_FILE "/proc/net/dev"
#define PROC_NET_ROUTE_FILE "/proc/net/route"
typedef struct
{
gchar *name;
gboolean cur_up,
up;
}
NetUp;
static GList *net_routed_list;
static gint rx_bytes_index,
tx_bytes_index,
rx_packets_index,
tx_packets_index;
void
gkrellm_sys_net_check_routes(void)
{
static FILE *f;
GList *list;
NetUp *net;
gchar *s;
gchar buf[512];
for (list = net_routed_list; list; list = list->next)
((NetUp *) list->data)->cur_up = FALSE;
if (f || (f = fopen(PROC_NET_ROUTE_FILE, "r")) != NULL)
{
fgets(buf, sizeof(buf), f); /* Waste the first line */
while (fgets(buf, sizeof(buf), f))
{
if ( ((s = strtok(buf, " \t\n")) == NULL)
|| !strncmp(s, "dummy", 5)
|| (*s == '*' && *(s+1) == '\0')
)
continue;
for (list = net_routed_list; list; list = list->next)
{
net = (NetUp *) list->data;
if (!strcmp(net->name, s))
{
net->cur_up = TRUE;
break;
}
}
if (!list)
{
net = g_new0(NetUp, 1);
net_routed_list = g_list_append(net_routed_list, net);
net->name = g_strdup(s);
net->cur_up = TRUE;
}
}
rewind(f);
}
for (list = net_routed_list; list; list = list->next)
{
net = (NetUp *) list->data;
if (net->up && !net->cur_up)
gkrellm_net_routed_event(net->name, FALSE);
else if (!net->up && net->cur_up)
gkrellm_net_routed_event(net->name, TRUE);
net->up = net->cur_up;
}
}
/* I read both the bytes (kernel 2.2.x) and packets (all kernels). Some
| net drivers for 2.2.x do not update the bytes counters.
*/
void
gkrellm_sys_net_read_data(void)
{
static FILE *f;
gchar buf[512];
gchar *name, *s, *s1;
gint i;
gulong rx, tx;
gulong rx_packets = 0,
tx_packets = 0;
guint64 ll;
if (!f && (f = fopen(PROC_NET_DEV_FILE, "r")) == NULL)
return;
fgets(buf, sizeof(buf), f); /* 2 line header */
fgets(buf, sizeof(buf), f);
while (fgets(buf, sizeof(buf), f))
{
/* Virtual net interfaces have a colon in the name, and a colon seps
| the name from data, + might be no space between data and name!
| Eg. this is possible -> eth2:0:11249029 0 ...
| So, replace the colon that seps data from the name with a space.
*/
s = strchr(buf, (int) ':');
if (s)
{
s1 = strchr(s + 1, (int) ':');
if (s1)
*s1 = ' ';
else
*s = ' ';
}
if ((name = strtok(buf, " \t\n")) == NULL) /* Get name of interface */
{
fclose(f);
f = NULL;
return;
}
if (!strncmp(name, "dummy", 5))
continue;
rx = tx = 0;
for (i = 1; (s = strtok(NULL, " \t\n")) != NULL; ++i)
{
if (i == rx_bytes_index)
{
rx = strtoul(s, NULL, 0);
/* Can have 32 bit library / 64 bit kernel mismatch.
| If so, just using the 32 low bits of a long long is OK.
*/
if ( rx == ULONG_MAX && errno == ERANGE
&& sscanf(s, "%Ld", &ll) == 1
)
rx = (gulong) ll;
}
else if (i == tx_bytes_index)
{
tx = strtoul(s, NULL, 0);
if ( tx == ULONG_MAX && errno == ERANGE
&& sscanf(s, "%Ld", &ll) == 1
)
tx = (gulong) ll;
}
else if (i == rx_packets_index)
rx_packets = strtoul(s, NULL, 0);
else if (i == tx_packets_index)
tx_packets = strtoul(s, NULL, 0);
if (i > tx_bytes_index && i > tx_packets_index)
break;
}
if (rx == 0 && tx == 0)
{
rx = rx_packets;
tx = tx_packets;
}
gkrellm_net_assign_data(name, rx, tx);
}
rewind(f);
}
gboolean
gkrellm_sys_net_isdn_online(void)
{
gint f = 0;
gchar buffer[BUFSIZ], *p, *end;
if ( (f = open("/dev/isdninfo", O_RDONLY)) == -1
&& (f = open("/dev/isdn/isdninfo", O_RDONLY)) == -1
)
{
if (_GK.debug_level & DEBUG_NET)
printf("sys_net_isdn__online: no /dev/isdninfo?\n");
return FALSE;
}
memset(buffer, 0, BUFSIZ);
if (read(f, buffer, BUFSIZ) <= 0)
{
close(f);
return FALSE;
}
close(f);
if ((p = strstr(buffer, "flags:")) == NULL)
return FALSE;
for(p += 6; *p; )
{
if (isspace(*p))
{
p++;
continue;
}
for (end = p; *end && !isspace(*end); end++)
;
if (*end == '\0' || *end == '\t')
break;
else
*end = 0;
if (!strcmp(p, "?") || !strcmp(p, "0"))
{
p = end+1;
continue;
}
return TRUE; /* ISDN is online */
}
return FALSE; /* ISDN is off line */
}
static const char *delim = " :|\t\n";
static void
get_io_indices(void)
{
FILE *f;
gchar *s;
gchar buf[184];
gint i;
if ((f = fopen(PROC_NET_DEV_FILE, "r")))
{
fgets(buf, sizeof(buf), f); /* Waste the first line. */
fgets(buf, sizeof(buf), f); /* Look for "units" in this line */
s = strtok(buf, delim);
for (i = 0; s; ++i)
{
if (strcmp(s, "bytes") == 0)
{
if (rx_bytes_index == 0)
rx_bytes_index = i;
else
tx_bytes_index = i;
}
if (strcmp(s, "packets") == 0)
{
if (rx_packets_index == 0)
rx_packets_index = i;
else
tx_packets_index = i;
}
s = strtok(NULL, delim);
}
fclose(f);
}
if (_GK.debug_level & DEBUG_NET)
printf(_("rx_bytes=%d tx_bytes=%d rx_packets=%d tx_packets=%d\n"),
rx_bytes_index, tx_bytes_index,
rx_packets_index, tx_packets_index);
}
gboolean
gkrellm_sys_net_init(void)
{
get_io_indices();
gkrellm_net_set_lock_directory("/var/lock");
gkrellm_net_add_timer_type_ppp("ppp0");
gkrellm_net_add_timer_type_ippp("ippp0");
gkrellm_net_use_routed(TRUE /* Always TRUE from sysdep code */);
return TRUE;
}
/* ===================================================================== */
/* Memory/Swap monitor interface */
#define PROC_MEMINFO_FILE "/proc/meminfo"
#define PROC_VMSTAT_FILE "/proc/vmstat"
static guint64 swap_total, swap_used;
/* Kernels >= 2.5.x have tagged formats only in kb units.
*/
static void
tagged_format_meminfo(gchar *buf, guint64 *mint)
{
sscanf(buf,"%Lu", mint);
*mint *= 1024;
}
void
gkrellm_sys_mem_read_data(void)
{
FILE *f;
gchar buf[160];
gboolean using_tagged = FALSE;
guint64 total, used, x_used, free, shared, buffers, cached;
/* Default 0, so we don't get arbitrary values if not found,
| e.g. MemShared is not present in 2.6.
*/
total = used = x_used = free = shared = buffers = cached = 0LL;
if ((f = fopen(PROC_MEMINFO_FILE, "r")) == NULL)
return;
while ((fgets(buf, sizeof(buf), f)) != NULL)
{
if (buf[0] == 'M')
{
if (!strncmp(buf, "Mem:", 4))
sscanf(buf + 5, "%Lu %Lu %Lu %Lu %Lu %Lu",
&total, &x_used, &free,
&shared, &buffers, &cached);
else if (!strncmp(buf, "MemTotal:", 9))
{
tagged_format_meminfo(buf + 10, &total);
using_tagged = TRUE;
}
else if (!strncmp(buf, "MemFree:", 8))
tagged_format_meminfo(buf + 9, &free);
else if (!strncmp(buf, "MemShared:", 10))
tagged_format_meminfo(buf + 11, &shared);
}
else if (buf[0] == 'S')
{
if (!strncmp(buf, "Swap:", 5))
sscanf(buf + 6,"%Lu %Lu", &swap_total, &swap_used);
else if (!strncmp(buf, "SwapTotal:", 10))
tagged_format_meminfo(buf + 11, &swap_total);
else if (!strncmp(buf, "SwapFree:", 9))
tagged_format_meminfo(buf + 10, &swap_used);
}
else if (buf[0] == 'B' && !strncmp(buf, "Buffers:", 8))
tagged_format_meminfo(buf + 9, &buffers);
else if (buf[0] == 'C' && !strncmp(buf, "Cached:", 7))
tagged_format_meminfo(buf + 8, &cached);
}
fclose(f);
if (using_tagged)
{
x_used = total - free;
swap_used = swap_total - swap_used;
}
used = x_used - buffers - cached;
gkrellm_mem_assign_data(total, used, free, shared, buffers, cached);
}
/* Kernel >= 2.6 swap page in/out is in /proc/vmstat.
| Kernel <= 2.4 swap page in/out is in /proc/stat read in read_proc_stat().
| Swap total/used for all kernels is read in mem_read_data() above.
*/
void
gkrellm_sys_swap_read_data(void)
{
static FILE *f;
gchar buf[128];
if (!f && kernel_2_6)
f = fopen(PROC_VMSTAT_FILE, "r");
if (f)
{
while (fgets(buf, sizeof(buf), f) != NULL)
{
if (buf[0] != 'p' || buf[1] != 's')
continue;
sscanf(buf, "pswpin %lu", &swapin);
if (fgets(buf, sizeof(buf), f) == NULL)
break;
sscanf(buf, "pswpout %lu", &swapout);
}
rewind(f);
}
gkrellm_swap_assign_data(swap_total, swap_used, swapin, swapout);
}
gboolean
gkrellm_sys_mem_init(void)
{
return TRUE;
}
/* ===================================================================== */
/* FS monitor interface */
#include <sys/vfs.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <linux/cdrom.h>
#include <mntent.h>
#define PROC_MOUNTS_FILE "/proc/mounts"
/* A list of mounted file systems can be read from /proc/mounts or
| /etc/mtab (getmntent). Using /proc/mounts eliminates disk accesses,
| but for some reason /proc/mounts reports a "." for the mounted
| directory for smbfs types. So I use /proc/mounts with a fallback
| to using getmntent().
*/
#if !defined (_PATH_MOUNTED)
#define _PATH_MOUNTED "/etc/mtab"
#endif
static void
fix_fstab_name(gchar *buf)
{
gchar *rp,
*wp;
if (buf[0] == '\0')
return;
rp = buf;
wp = buf;
do /* This loop same as in libc6 getmntent() */
if (rp[0] == '\\' && rp[1] == '0' && rp[2] == '4' && rp[3] == '0')
{
*wp++ = ' '; /* \040 is a SPACE. */
rp += 3;
}
else if (rp[0] == '\\' && rp[1] == '0' && rp[2] == '1' && rp[3] == '2')
{
*wp++ = '\t'; /* \012 is a TAB. */
rp += 3;
}
else if (rp[0] == '\\' && rp[1] == '\\')
{
*wp++ = '\\'; /* \\ is a \ */
rp += 1;
}
else
*wp++ = *rp;
while (*rp++ != '\0');
}
gboolean
gkrellm_sys_fs_fstab_modified(void)
{
struct stat s;
static time_t fstab_mtime;
gint modified = FALSE;
if (stat("/etc/fstab", &s) == 0 && s.st_mtime != fstab_mtime)
modified = TRUE;
fstab_mtime = s.st_mtime;
return modified;
}
void
gkrellm_sys_fs_get_fstab_list(void)
{
FILE *f;
gchar buf[1024], *s;
gchar dev[64], dir[128], type[64], opt[128];
if ((f = fopen("/etc/fstab", "r")) == NULL)
return;
while (fgets(buf, sizeof(buf), f))
{
s = buf;
while (*s == ' ' || *s == '\t')
++s;
if (*s == '\0' || *s == '#' || *s == '\n')
continue;
dev[0] = dir[0] = type[0] = opt[0] = '\0';
sscanf(s, "%64s %128s %64s %128s", dev, dir, type, opt);
fix_fstab_name(dev);
fix_fstab_name(dir);
fix_fstab_name(type);
fix_fstab_name(opt);
if ( type[0] == '\0'
|| !strcmp(type, "devpts")
|| !strcmp(type, "swap")
|| !strcmp(type, "proc")
|| !strcmp(type, "sysfs")
|| !strcmp(type, "usbdevfs")
|| !strcmp(type, "usbfs")
|| !strcmp(type, "ignore")
)
continue;
gkrellm_fs_add_to_fstab_list(dir, dev, type, opt);
}
fclose(f);
}
static void
getmntent_fallback(gchar *dir, gint dirsize, gchar *dev)
{
FILE *f;
struct mntent *mnt;
if ((f = setmntent(_PATH_MOUNTED, "r")) == NULL)
return;
while ((mnt = getmntent(f)) != NULL)
{
if (!strcmp(dev, mnt->mnt_fsname))
{
snprintf(dir, dirsize, "%s", mnt->mnt_dir);
break;
}
}
endmntent(f);
}
void
gkrellm_sys_fs_get_mounts_list(void)
{
FILE *f;
gchar *s, buf[128], dev[64], dir[128], type[32];
if ((f = fopen(PROC_MOUNTS_FILE, "r")) == NULL)
return;
while (fgets(buf, sizeof(buf), f))
{
sscanf(buf, "%63s %127s %31s", dev, dir, type);
if ( !strcmp(type, "devpts")
|| !strcmp(type, "proc")
|| !strcmp(type, "usbdevfs")
|| !strcmp(type, "usbfs")
|| !strcmp(type, "sysfs")
)
continue;
/* Strip trailing / from the directory.
*/
s = strrchr(dir, (int) '/');
if (s && s != dir && *(s+1) == '\0')
*s = '\0';
if (dir[0] == '.')
getmntent_fallback(dir, sizeof(dir), dev);
gkrellm_fs_add_to_mounts_list(dir, dev, type);
}
fclose(f);
}
void
gkrellm_sys_fs_get_fsusage(gpointer fs, gchar *dir)
{
struct statfs st;
if (!statfs(dir, &st))
gkrellm_fs_assign_fsusage_data(fs,
(glong) st.f_blocks, (glong) st.f_bavail,
(glong) st.f_bfree, (glong) st.f_bsize);
else
gkrellm_fs_assign_fsusage_data(fs, 0, 0, 0, 0);
}
static void
eject_linux_cdrom(gchar *device)
{
#if defined(CDROMEJECT)
gint d;
if ((d = open(device, O_RDONLY|O_NONBLOCK)) >= 0)
{
ioctl(d, CDROMEJECT);
close(d);
}
#endif
}
gboolean
gkrellm_sys_fs_init(void)
{
gchar *eject_command = NULL,
*close_command = NULL;
#if defined(WEXITSTATUS)
gint n;
n = system("eject -d > /dev/null 2>&1");
if (WEXITSTATUS(n) == 0)
{
eject_command = "eject %s";
close_command = "eject -t %s";
}
#endif
gkrellm_fs_setup_eject(eject_command, close_command,
eject_linux_cdrom, NULL);
return TRUE;
}
/* ===================================================================== */
/* Battery monitor interface */
/* ---------------------------- */
/* ACPI battery interface */
#define ACPI_BATTERY_DIR "/proc/acpi/battery/"
#define ACPI_AC_ADAPTOR_DIR "/proc/acpi/ac_adapter/"
typedef struct
{
gint id;
gchar *info,
*state;
gint full_cap;
gboolean got_full_cap,
full_cap_bug;
}
BatteryFile;
static gchar *acpi_ac_state_file;
static GList *acpi_battery_list;
#if !defined(F_OK)
#define F_OK 0
#endif
static gchar *
get_acpi_battery_file(gchar *dir, gchar *subdir, gchar *name)
{
gchar *path;
/* dir is expected to have trailing '/'
*/
path = g_strconcat(dir, subdir, "/", name, NULL);
if (!access(path, F_OK))
return path;
g_free(path);
return NULL;
}
static gboolean
setup_acpi_battery(gchar *bat)
{
BatteryFile *bf;
gchar *info;
static gint id;
info = g_strconcat(ACPI_BATTERY_DIR, bat, "/info", NULL);
if (_GK.debug_level & DEBUG_BATTERY)
printf("setup_acpi_battery: %s\n", info);
if (!access(info, F_OK))
{
bf = g_new0(BatteryFile, 1);
bf->id = id++;
bf->info = info;
bf->state = get_acpi_battery_file(ACPI_BATTERY_DIR, bat, "state");
if (!bf->state)
bf->state = get_acpi_battery_file(ACPI_BATTERY_DIR, bat, "status");
acpi_battery_list = g_list_append(acpi_battery_list, bf);
if (_GK.debug_level & DEBUG_BATTERY)
printf("setup_acpi_battery: %s\n",
bf->state ? bf->state : "no state");
return TRUE;
}
g_free(info);
return FALSE;
}
static gboolean
setup_ac_adapter(gchar **state, gchar *ac)
{
gchar *path;
path = get_acpi_battery_file(ACPI_AC_ADAPTOR_DIR, ac, "state");
if (!path)
path = get_acpi_battery_file(ACPI_AC_ADAPTOR_DIR, ac, "status");
*state = path;
if (_GK.debug_level & DEBUG_BATTERY)
printf("setup_ac_adaptor: %s\n", path ? path : "no state");
return path ? TRUE : FALSE;
}
static void
acpi_setup(void)
{
DIR *d;
struct dirent *de;
if ((d = opendir(ACPI_BATTERY_DIR)) == NULL)
return;
while ((de = readdir(d)) != NULL)
{
if ( strcmp(de->d_name, ".")
&& strcmp(de->d_name, "..")
)
setup_acpi_battery(de->d_name);
}
closedir(d);
if (!acpi_battery_list)
return;
if ((d = opendir(ACPI_AC_ADAPTOR_DIR)) != NULL)
{
while ((de = readdir(d)) != NULL)
{
if ( strcmp(de->d_name, ".")
&& strcmp(de->d_name, "..")
&& setup_ac_adapter(&acpi_ac_state_file, de->d_name)
)
break;
}
closedir(d);
}
}
static gboolean
fgets_lower_case(gchar *buf, gint len, FILE *f)
{
guchar *s;
if (!fgets(buf, len, f))
return FALSE;
s = (guchar *) buf;
if (isupper(*s))
while (*s)
{
if (isupper(*s))
*s = tolower(*s);
++s;
}
return TRUE;
}
static gboolean
acpi_battery_data(BatteryFile *bf)
{
FILE *f;
gchar buf[128], s1[32];
gboolean on_line, charging, available, have_charging_state;
gint percent = 0, time_left = -1;
gint cur_cap = 0, cur_rate = 0;
extern gint gkrellm_battery_full_cap_fallback(void);
if (!bf->got_full_cap) /* get battery capacity */
{
if (_GK.debug_level & DEBUG_BATTERY)
printf("getting full capacity: %s\n", bf->info);
if ((f = fopen(bf->info, "r")) == NULL)
return FALSE;
bf->got_full_cap = TRUE;
while (fgets_lower_case(buf, sizeof(buf), f))
{
/* present: {yes|no}
| design capacity: 53280 mWh
| last full capacity: 51282 mWh
| battery technology: rechargeable
| design voltage: 14800 mV
| design capacity warning: 5120 mWh
| design capacity low: 0 mWh
| capacity granularity 1: 1480 mWh
| capacity granularity 2: 1480 mWh
| model number: 6000
| serial number: 1
| battery type: 4
| OEM info: XXXX
*/
if (sscanf(buf, "design capacity: %d", &bf->full_cap) == 1)
if (_GK.debug_level & DEBUG_BATTERY)
printf("%s: %d <- %s", bf->info, bf->full_cap, buf);
if (sscanf(buf, "last full capacity: %d", &bf->full_cap) == 1)
if (_GK.debug_level & DEBUG_BATTERY)
printf("%s: %d <- %s", bf->info, bf->full_cap, buf);
}
fclose(f);
}
if (bf->full_cap == 0)
{
bf->full_cap = gkrellm_battery_full_cap_fallback();
bf->full_cap_bug = TRUE;
}
on_line = FALSE;
if ( acpi_ac_state_file
&& (f = fopen(acpi_ac_state_file, "r")) != NULL
)
{
while (fgets_lower_case(buf, sizeof(buf), f))
{
/* state: {on-line|off-line}
*/
if ( ( sscanf (buf, "state: %s", s1) == 1
|| sscanf (buf, "status: %s", s1) == 1
)
&& !strcmp(s1, "on-line")
)
on_line = TRUE;
}
fclose(f);
}
if ((f = fopen(bf->state, "r")) == NULL)
return FALSE;
charging = FALSE;
available = FALSE;
have_charging_state = FALSE;
while (fgets_lower_case(buf, sizeof(buf), f))
{
/* present: {yes|no}
| capacity state: ok
| charging state: {charging|discharging|unknown}
| present rate: 15000 mW
| remaining capacity: 31282 mWh
| present voltage: 16652 mV
*/
if (sscanf(buf, "charging state: %s", s1) == 1)
{
have_charging_state = TRUE;
if ( (!strcmp(s1, "unknown") && on_line)
|| !strcmp(s1, "charging")
)
charging = TRUE;
continue;
}
if (sscanf(buf, "remaining capacity: %d", &cur_cap) == 1)
{
if (_GK.debug_level & DEBUG_BATTERY)
printf("%s: %d <- %s", bf->state, cur_cap, buf);
continue;
}
if (sscanf(buf, "present rate: %d", &cur_rate) == 1)
continue;
if ( sscanf(buf, "present: %s", s1) == 1
&& !strcmp(s1, "yes")
)
available = TRUE;
}
fclose(f);
if (_GK.debug_level & DEBUG_BATTERY)
printf(
"Battery: on_line=%d charging=%d have_charging=%d state_file=%d\n",
on_line, charging, have_charging_state,
acpi_ac_state_file ? TRUE : FALSE);
if (!acpi_ac_state_file && charging) /* Assumption for buggy ACPI */
on_line = TRUE;
if (!have_charging_state && on_line) /* Another buggy ACPI */
charging = TRUE;
if (charging)
bf->got_full_cap = FALSE; /* reread full_cap */
percent = cur_cap * 100 / bf->full_cap;
if (percent > 100)
{
percent = 100;
if (bf->full_cap_bug)
bf->full_cap = cur_cap;
else
bf->got_full_cap = FALSE; /* reread full_cap */
}
if (cur_rate > 0)
{
if (charging)
time_left = 60 * (bf->full_cap - cur_cap) / cur_rate;
else
time_left = 60 * cur_cap / cur_rate;
}
if (_GK.debug_level & DEBUG_BATTERY)
{
printf("Battery %d: percent=%d time_left=%d cur_cap=%d full_cap=%d\n",
bf->id, percent, time_left, cur_cap, bf->full_cap);
printf(" available=%d on_line=%d charging=%d fc_bug=%d\n",
available, on_line, charging, bf->full_cap_bug);
}
gkrellm_battery_assign_data(bf->id, available, on_line, charging,
percent, time_left);
return TRUE;
}
/* ---------------------------- */
/* APM battery interface */
#define PROC_APM_FILE "/proc/apm"
/* From: arch/i386/kernel/apm.c
|
| 0) Linux driver version (this will change if format changes)
| 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2.
| 2) APM flags from APM Installation Check (0x00):
| bit 0: APM_16_BIT_SUPPORT
| bit 1: APM_32_BIT_SUPPORT
| bit 2: APM_IDLE_SLOWS_CLOCK
| bit 3: APM_BIOS_DISABLED
| bit 4: APM_BIOS_DISENGAGED
| 3) AC line status
| 0x00: Off-line
| 0x01: On-line
| 0x02: On backup power (BIOS >= 1.1 only)
| 0xff: Unknown
| 4) Battery status
| 0x00: High
| 0x01: Low
| 0x02: Critical
| 0x03: Charging
| 0x04: Selected battery not present (BIOS >= 1.2 only)
| 0xff: Unknown
| 5) Battery flag
| bit 0: High
| bit 1: Low
| bit 2: Critical
| bit 3: Charging
| bit 7: No system battery
| 0xff: Unknown
| 6) Remaining battery life (percentage of charge):
| 0-100: valid
| -1: Unknown
| 7) Remaining battery life (time units):
| Number of remaining minutes or seconds
| -1: Unknown
| 8) min = minutes; sec = seconds
*/
#define APM_BIOS_VERSION(major, minor) \
( bios_major > (major) \
|| (bios_major == (major) && bios_minor >= (minor)) \
)
/* AC line status values */
#define APM_ON_LINE 1
/* Battery status values */
#define APM_CHARGING 3
#define APM_NOT_PRESENT 4
/* Battery flag bits */
#define APM_NO_BATTERY 0x80
#define APM_UNKNOWN 0xFF
static void
apm_battery_assign(gint id, gint bios_major, gint bios_minor,
gint ac_line_status, gint battery_status, gint battery_flag,
gint percent, gint time_left, gchar *units)
{
gboolean available, on_line, charging;
if ( (battery_flag != APM_UNKNOWN && (battery_flag & APM_NO_BATTERY))
|| (battery_status == APM_UNKNOWN && ac_line_status == APM_UNKNOWN)
|| (battery_status == APM_NOT_PRESENT && APM_BIOS_VERSION(1,2))
)
available = FALSE;
else
available = TRUE;
if (ac_line_status != APM_UNKNOWN)
on_line = (ac_line_status == APM_ON_LINE) ? TRUE : FALSE;
else
on_line = (battery_status == APM_CHARGING) ? FALSE : TRUE;
if (battery_status != APM_UNKNOWN)
charging= (battery_status == APM_CHARGING) ? TRUE : FALSE;
else
charging = (ac_line_status == APM_ON_LINE) ? TRUE : FALSE;
if (!strcmp(units, "sec") && time_left > 0)
time_left /= 60;
gkrellm_battery_assign_data(id, available, on_line, charging,
percent, time_left);
}
static gboolean
apm_battery_data(void)
{
FILE *f;
gchar buf[128], units[32];
gint percent = 0, time_left = 0;
gint ac_line_status, battery_status, battery_flag;
gint bios_major, bios_minor;
gint id, n_batteries = 1;
if ((f = fopen(PROC_APM_FILE, "r")) == NULL)
return FALSE;
fgets(buf, sizeof(buf), f);
sscanf(buf, "%*s %d.%d %*x %x %x %x %d%% %d %32s\n",
&bios_major, &bios_minor,
&ac_line_status, &battery_status, &battery_flag,
&percent, &time_left, units);
/* If have APM dual battery patch, next line will be number of batteries.
*/
if (fgets(buf, sizeof(buf), f))
sscanf(buf, "%d\n", &n_batteries);
if (n_batteries < 2)
apm_battery_assign(0, bios_major, bios_minor, ac_line_status,
battery_status, battery_flag, percent, time_left, units);
else
{
apm_battery_assign(GKRELLM_BATTERY_COMPOSITE_ID,
bios_major, bios_minor, ac_line_status,
battery_status, battery_flag, percent, time_left, units);
while (n_batteries-- > 0 && fgets(buf, sizeof(buf), f))
{
sscanf(buf, "%d %x %x %d%% %d %32s\n", &id,
&battery_status, &battery_flag,
&percent, &time_left, units);
apm_battery_assign(id - 1, bios_major, bios_minor, ac_line_status,
battery_status, battery_flag, percent, time_left, units);
}
}
fclose(f);
return TRUE;
}
void
gkrellm_sys_battery_read_data(void)
{
GList *list;
if (acpi_battery_list)
for (list = acpi_battery_list; list; list = list->next)
acpi_battery_data((BatteryFile *)(list->data));
else
apm_battery_data();
}
gboolean
gkrellm_sys_battery_init()
{
acpi_setup();
return TRUE;
}
/* ===================================================================== */
/* Uptime monitor interface */
/* Calculating an uptime based on system time has a fuzzy meaning for
| laptops since /proc/uptime does not include time system has been
| sleeping. So, read /proc/uptime always.
*/
time_t
gkrellm_sys_uptime_read_uptime(void)
{
FILE *f;
gulong l = 0;
if ((f = fopen("/proc/uptime", "r")) != NULL)
{
fscanf(f, "%lu", &l);
fclose(f);
}
return (time_t) l;
}
gboolean
gkrellm_sys_uptime_init(void)
{
return TRUE;
}
/* ===================================================================== */
/* Sensor monitor interface */
/* ------- Linux ------------------------------------------------------- */
#define THERMAL_ZONE_DIR "/proc/acpi/thermal_zone"
#define THERMAL_DIR "/proc/acpi/thermal"
#define SENSORS_DIR "/proc/sys/dev/sensors"
#define SYSFS_I2C_DIR "/sys/bus/i2c/devices"
/* mbmon and hddtemp sensor interfaces are handled in sensors-common.c
*/
#define LM_SENSOR_INTERFACE 0
#define THERMAL_INTERFACE 1
#define THERMAL_ZONE_INTERFACE 2
#define NVIDIA_SETTINGS_INTERFACE 3
#define IBM_ACPI_INTERFACE 4
#define IBM_ACPI_FAN_FILE "/proc/acpi/ibm/fan"
#define IBM_ACPI_THERMAL "/proc/acpi/ibm/thermal"
#define IBM_ACPI_CPU_TEMP 0
#define IBM_ACPI_PCI_TEMP 1
#define IBM_ACPI_HDD_TEMP 2
#define IBM_ACPI_GPU_TEMP 3
#define IBM_ACPI_BAT1_TEMP 4
#define IBM_ACPI_NA1_TEMP 5
#define IBM_ACPI_BAT2_TEMP 6
#define IBM_ACPI_NA2_TEMP 7
#define IBM_ACPI_FAN 8
typedef struct
{
gchar *name;
gfloat factor;
gfloat offset;
gchar *vref;
}
VoltDefault;
/* Tables of voltage correction factors and offsets derived from the
| compute lines in sensors.conf. See the README file.
*/
/* "lm78-*" "lm78-j-*" "lm79-*" "w83781d-*" "sis5595-*" "as99127f-*" */
/* Values from LM78/LM79 data sheets */
static VoltDefault voltdefault0[] =
{
{ "Vcor1", 1.0, 0, NULL },
{ "Vcor2", 1.0, 0, NULL },
{ "+3.3V", 1.0, 0, NULL },
{ "+5V", 1.68, 0, NULL }, /* in3 ((6.8/10)+1)*@ */
{ "+12V", 4.0, 0, NULL }, /* in4 ((30/10)+1)*@ */
{ "-12V", -4.0, 0, NULL }, /* in5 -(240/60)*@ */
{ "-5V", -1.667, 0, NULL } /* in6 -(100/60)*@ */
};
/* "w83782d-*" "w83783s-*" "w83627hf-*" */
static VoltDefault voltdefault1[] =
{
{ "Vcor1", 1.0, 0, NULL },
{ "Vcor2", 1.0, 0, NULL },
{ "+3.3V", 1.0, 0, NULL },
{ "+5V", 1.68, 0, NULL }, /* in3 ((6.8/10)+1)*@ */
{ "+12V", 3.80, 0, NULL }, /* in4 ((28/10)+1)*@ */
{ "-12V", 5.14 , -14.91, NULL }, /* in5 (5.14 * @) - 14.91 */
{ "-5V", 3.14 , -7.71, NULL }, /* in6 (3.14 * @) - 7.71 */
{ "V5SB", 1.68, 0, NULL }, /* in7 ((6.8/10)+1)*@ */
{ "VBat", 1.0, 0, NULL }
};
/* lm80-* */
static VoltDefault voltdefault2[] =
{
{ "+5V", 2.633, 0, NULL }, /* in0 (24/14.7 + 1) * @ */
{ "VTT", 1.0, 0, NULL },
{ "+3.3V", 1.737, 0, NULL }, /* in2 (22.1/30 + 1) * @ */
{ "Vcore", 1.474, 0, NULL }, /* in3 (2.8/1.9) * @ */
{ "+12V", 6.316, 0, NULL }, /* in4 (160/30.1 + 1) * @ */
{ "-12V", 5.482, -4.482, "in0" }, /* in5 (160/35.7)*(@ - in0) + @ */
{ "-5V", 3.222, -2.222, "in0" } /* in6 (36/16.2)*(@ - in0) + @ */
};
/* gl518sm-* */
static VoltDefault voltdefault3[] =
{
{ "+5V", 1.0, 0, NULL }, /* vdd */
{ "+3.3V", 1.0, 0, NULL }, /* vin1 */
{ "+12V", 4.192, 0, NULL }, /* vin2 (197/47)*@ */
{ "Vcore", 1.0, 0, NULL } /* vin3 */
};
/* gl520sm-* */
static VoltDefault voltdefault4[] =
{
{ "+5V", 1.0, 0, NULL }, /* vdd */
{ "+3.3V", 1.0, 0, NULL }, /* vin1 */
{ "+12V", 4.192, 0, NULL }, /* vin2 (197/47)*@ */
{ "Vcore", 1.0, 0, NULL }, /* vin3 */
{ "-12V", 5.0, -4.0, "vdd" } /* vin4 (5*@)-(4*vdd) */
};
/* via686a-* */
static VoltDefault voltdefault5[] =
{
{ "Vcor", 1.02, 0, NULL }, /* in0 */
{ "+2.5V", 1.0, 0, NULL }, /* in1 */
{ "I/O", 1.02, 0, NULL }, /* in2 */
{ "+5V", 1.009, 0, NULL }, /* in3 */
{ "+12V", 1.04, 0, NULL } /* in4 */
};
/* mtp008-* */
static VoltDefault voltdefault6[] =
{
{ "Vcor1", 1.0, 0, NULL }, /* in0 */
{ "+3.3V", 1.0, 0, NULL }, /* in1 */
{ "+12V", 3.8, 0, NULL }, /* in2 @ * 38 / 10*/
{ "Vcor2", 1.0, 0, NULL }, /* in3 */
{ "?", 1.0, 0, NULL }, /* in4 */
{ "-12V", 5.143, -16.944, NULL }, /* in5 (@ * 36 - 118.61) / 7*/
{ "Vtt", 1.0, 0, NULL } /* in6 */
};
/* adm1025-* adm9240-* lm87-* lm81-* ds1780-* */
static VoltDefault voltdefault7[] =
{
{ "2.5V", 1.0, 0, NULL }, /* in0 */
{ "Vccp", 1.0, 0, NULL }, /* in1 */
{ "3.3V", 1.0, 0, NULL }, /* in2 */
{ "5V", 1.0, 0, NULL }, /* in3 */
{ "12V", 1.0, 0, NULL }, /* in4 */
{ "Vcc", 1.0, 0, NULL } /* in5 */
};
/* it87-* it8705-* it8712 */
static VoltDefault voltdefault8[] =
{
{ "Vcor1", 1.0, 0, NULL },
{ "Vcor2", 1.0, 0, NULL },
{ "+3.3V", 2.0, 0, NULL }, /* in2 (1 + 1)*@ */
{ "+5V", 1.68, 0, NULL }, /* in3 ((6.8/10)+1)*@ */
{ "+12V", 4.0, 0, NULL }, /* in4 ((30/10)+1)*@ */
{ "-12V", 7.67, -27.36, NULL }, /* in5 (7.67 * @) - 27.36 */
{ "-5V", 4.33, -13.64, NULL }, /* in6 (4.33 * @) - 13.64 */
{ "Stby", 1.68, 0, NULL }, /* in7 ((6.8/10)+1)*@ */
{ "Vbat", 1.0, 0, NULL } /* in8 */
};
/* fscpos */
static VoltDefault voltdefault9[] =
{
{ "+12V", 1.0, 0, NULL },
{ "+5V", 1.0, 0, NULL },
{ "+3.3V", 1.0, 0, NULL }
};
gboolean
gkrellm_sys_sensors_get_temperature(gchar *sensor_path, gint id,
gint iodev, gint interface, gfloat *temp)
{
FILE *f;
gchar buf[64], units[32];
gint n;
gfloat T, t[5], ibm_acpi_temp[8];;
gboolean result = FALSE;
if ( interface == THERMAL_INTERFACE
|| interface == THERMAL_ZONE_INTERFACE
)
{
f = fopen(sensor_path, "r");
if (f)
{
while (fgets(buf, sizeof(buf), f) != NULL)
{
if (need_locale_fix)
locale_fix(buf);
if ((n = sscanf(buf, "temperature: %f %31s", &T, units)) > 0)
{
if (n == 2 && !strcmp(units, "dK"))
T = T / 10.0 - 273.0;
*temp = T;
result = TRUE;
}
}
fclose(f);
}
return result;
}
if (interface == IBM_ACPI_INTERFACE)
{
f = fopen(sensor_path, "r");
if (f)
{
fgets(buf, sizeof(buf), f);
sscanf(buf, "temperatures: %f %f %f %f %f %f %f %f",
&ibm_acpi_temp[IBM_ACPI_CPU_TEMP],
&ibm_acpi_temp[IBM_ACPI_PCI_TEMP],
&ibm_acpi_temp[IBM_ACPI_HDD_TEMP],
&ibm_acpi_temp[IBM_ACPI_GPU_TEMP],
&ibm_acpi_temp[IBM_ACPI_BAT1_TEMP],
&ibm_acpi_temp[IBM_ACPI_NA1_TEMP],
&ibm_acpi_temp[IBM_ACPI_BAT2_TEMP],
&ibm_acpi_temp[IBM_ACPI_NA2_TEMP]);
*temp = ibm_acpi_temp[iodev];
result = TRUE;
}
fclose(f);
return result;
}
if (interface == HDDTEMP_INTERFACE)
{
gkrellm_sys_sensors_hddtemp_check();
return gkrellm_sys_sensors_hddtemp_get_value(sensor_path, temp);
}
if (interface == MBMON_INTERFACE)
{
gkrellm_sys_sensors_mbmon_check(FALSE);
return gkrellm_sys_sensors_mbmon_get_value(sensor_path, temp);
}
if (interface == NVIDIA_SETTINGS_INTERFACE)
{
#if GLIB_CHECK_VERSION(2,0,0)
char *args[] = { "nvidia-settings", "-q", sensor_path, NULL };
char *output = NULL;
result = g_spawn_sync(NULL, args, NULL,
G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL,
NULL, NULL, &output, NULL, NULL, NULL);
if(result && output)
{
gfloat dummy;
if(!temp) temp = &dummy;
result = (sscanf(output, " Attribute %*s %*s %f", temp) == 1);
}
g_free(output);
return result;
#else
return FALSE;
#endif
}
if ((f = fopen(sensor_path, "r")) == NULL)
return FALSE;
fgets(buf, sizeof(buf), f);
fclose(f);
if (!have_sysfs_sensors && need_locale_fix)
locale_fix(buf);
n = sscanf(buf, "%f %f %f %f %f", &t[0], &t[1], &t[2], &t[3], &t[4]);
if (n < 1)
return FALSE;
T = t[n - 1];
if (have_sysfs_sensors)
T /= 1000.0;
else if (T > 254.0) /* Bogus read from BIOS if CHAR_MAX */
return FALSE;
if (temp)
*temp = T;
return TRUE;
}
gboolean
gkrellm_sys_sensors_get_fan(gchar *sensor_path, gint id,
gint iodev, gint interface, gfloat *fan)
{
FILE *f;
gchar buf[64];
gint n;
gfloat t[4], T;
gboolean result = FALSE;
if (interface == IBM_ACPI_INTERFACE)
{
f = fopen(sensor_path, "r");
if (f)
{
fgets(buf, sizeof(buf), f);
fgets(buf, sizeof(buf), f);
sscanf(buf, "speed: %f", &T);
*fan = T;
result = TRUE;
}
fclose(f);
return result;
}
if (interface == MBMON_INTERFACE)
{
gkrellm_sys_sensors_mbmon_check(FALSE);
return gkrellm_sys_sensors_mbmon_get_value(sensor_path, fan);
}
if ((f = fopen(sensor_path, "r")) == NULL)
return FALSE;
fgets(buf, sizeof(buf), f);
fclose(f);
if (!have_sysfs_sensors && need_locale_fix)
locale_fix(buf);
n = sscanf(buf, "%f %f %f %f", &t[0], &t[1], &t[2], &t[3]);
if (n < 1)
return FALSE;
if (fan)
*fan = t[n - 1];
return TRUE;
}
gboolean
gkrellm_sys_sensors_get_voltage(gchar *sensor_path, gint id,
gint iodev, gint interface, gfloat *volt)
{
FILE *f;
gchar buf[64];
gfloat V, t[3];
gint n;
if (interface == MBMON_INTERFACE)
{
gkrellm_sys_sensors_mbmon_check(FALSE);
return gkrellm_sys_sensors_mbmon_get_value(sensor_path, volt);
}
if ((f = fopen(sensor_path, "r")) == NULL)
return FALSE;
fgets(buf, sizeof(buf), f);
fclose(f);
if (!have_sysfs_sensors && need_locale_fix)
locale_fix(buf);
n = sscanf(buf, "%f %f %f", &t[0], &t[1], &t[2]);
if (n < 1)
return FALSE;
V = t[n - 1];
if (have_sysfs_sensors)
V /= 1000.0;
else if (V > 254.0) /* Bogus read from BIOS if CHAR_MAX */
return FALSE;
if (volt)
*volt = V;
return TRUE;
}
static void
get_volt_default(gchar *chip_name, VoltDefault **vdf, gint *vdfsize)
{
if (!strncmp(chip_name, "it87", 4))
{
*vdf = &voltdefault8[0];
*vdfsize = sizeof (voltdefault8) / sizeof (VoltDefault);
}
else if ( !strncmp(chip_name, "adm1025", 7)
|| !strncmp(chip_name, "adm9240", 7)
|| !strncmp(chip_name, "lm87", 4)
|| !strncmp(chip_name, "lm81", 4)
|| !strncmp(chip_name, "ds1780", 6)
)
{
*vdf = &voltdefault7[0];
*vdfsize = sizeof (voltdefault7) / sizeof (VoltDefault);
}
else if (!strncmp(chip_name, "mtp008", 6))
{
*vdf = &voltdefault6[0];
*vdfsize = sizeof (voltdefault6) / sizeof (VoltDefault);
}
else if (!strncmp(chip_name, "via686", 6))
{
*vdf = &voltdefault5[0];
*vdfsize = sizeof (voltdefault5) / sizeof (VoltDefault);
}
else if (!strncmp(chip_name, "gl520", 5))
{
*vdf = &voltdefault4[0];
*vdfsize = sizeof (voltdefault4) / sizeof (VoltDefault);
}
else if (!strncmp(chip_name, "gl518", 5))
{
*vdf = &voltdefault3[0];
*vdfsize = sizeof (voltdefault3) / sizeof (VoltDefault);
}
else if (!strncmp(chip_name, "lm80", 4))
{
*vdf = &voltdefault2[0];
*vdfsize = sizeof (voltdefault2) / sizeof (VoltDefault);
}
else if (!strncmp(chip_name, "w83", 3) && strncmp(chip_name, "w83781", 6))
{
*vdf = &voltdefault1[0];
*vdfsize = sizeof (voltdefault1) / sizeof (VoltDefault);
}
else if (!strncmp(chip_name, "fscpos", 6))
{
*vdf = &voltdefault9[0];
*vdfsize = sizeof (voltdefault9) / sizeof (VoltDefault);
}
else
{
*vdf = &voltdefault0[0];
*vdfsize = sizeof (voltdefault0) / sizeof (VoltDefault);
}
}
static gchar *
sysfs_get_chip_name(gchar *dir)
{
gchar *name, buf[256], *p, *chip;
FILE *f;
name = g_strdup_printf("%s/%s", dir, "name");
f = fopen(name, "r");
g_free(name);
if (!f)
return NULL;
buf[0] = '\0';
fscanf(f, "%255[^\n]", buf);
fclose(f);
if (buf[0] == '\0')
return NULL;
if ((p = strchr(buf, ' ')) != NULL) /* Remove when 2.6.0 is out */
{ /* "w83627hf chip" -> "w83627hf" */
*p++ = '\0';
if (strcmp(p, "chip") && strcmp(p, "subclient"))
return NULL;
}
chip = g_strdup(buf);
for (p = chip; *p; p++)
*p = tolower(*p);
return chip;
}
static void
sysfs_sensors_init(void)
{
GDir *dir, *chip_dir;
VoltDefault *voltdefault;
gint id = 0;
gint type, voltdefaultsize;
gfloat factor, offset;
gchar *name, *bus_name, *default_label, *vref,
*id_name, *chip_name, *s, *d, *sensor_path;
gchar path[256], buf[256];
if (!have_sysfs)
return;
if ((dir = g_dir_open(SYSFS_I2C_DIR, 0, NULL)) == NULL)
return;
while ((bus_name = (gchar *) g_dir_read_name(dir)) != NULL)
{
snprintf(path, sizeof(path), "%s/%s", SYSFS_I2C_DIR, bus_name);
if ((chip_dir = g_dir_open(path, 0, NULL)) == NULL)
continue;
if ((chip_name = sysfs_get_chip_name(path)) == NULL)
{
g_dir_close(chip_dir);
continue;
}
have_sysfs_sensors = TRUE;
if (_GK.debug_level & DEBUG_SENSORS)
printf("sysfs sensors dir: %s\n", path);
get_volt_default(chip_name, &voltdefault, &voltdefaultsize);
while ((name = (gchar *) g_dir_read_name(chip_dir)) != NULL)
{
snprintf(buf, sizeof(buf), "%s", name);
if ((s = strstr(buf, "_input")) == NULL || s - buf > 6)
continue;
d = s + 6; /* Can have xxxN_input, xxx_inputN, or xxx_input */
while (isdigit(*d))
*s++ = *d++;
*s = '\0';
while (isdigit(*(s-1)))
--s;
id = atoi(s);
if (!strncmp(buf, "temp", 4))
type = SENSOR_TEMPERATURE;
else if (!strncmp(buf, "fan", 3))
type = SENSOR_FAN;
else if (!strncmp(buf, "in", 2))
type = SENSOR_VOLTAGE;
else
continue;
factor = 1.0;
offset = 0;
default_label = vref = NULL;
if (type == SENSOR_VOLTAGE)
{
if (id < voltdefaultsize)
{
default_label = voltdefault[id].name;
factor = voltdefault[id].factor;
offset = voltdefault[id].offset;
vref = voltdefault[id].vref;
}
else
default_label = buf;
}
id_name = g_strdup_printf("%s-%s/%s", chip_name, bus_name, buf);
sensor_path = g_strdup_printf("%s/%s", path, name);
gkrellm_sensors_add_sensor(type, sensor_path, id_name,
id, 0, 0,
factor, offset, vref, default_label);
if (_GK.debug_level & DEBUG_SENSORS)
printf("%s %s %d %d\n",
sensor_path, id_name, id, type);
g_free(id_name);
g_free(sensor_path);
}
g_free(chip_name);
g_dir_close(chip_dir);
}
g_dir_close(dir);
}
/* Eventually, move this function from sensors.c to here where
| it belongs.
*/
extern void gkrellm_sensors_linux_name_fix(gchar *chip_name);
/* lm_sensors can have "-isa-" or "-i2c-" embedded in /proc chip names so
| they are of form w83627hf-isa-0290. Sensors from sysfs are derived from
| a bus directory "0-0290" and "name" file with content like "w83627hf"
| which leads to chip names like "w83627hf-0-0290" without the i2c or
| isa. To have a chance at config compatibility between the two,
| remove the "i2c" and replace the "isa" with "0" in lm_sensors name
| when generating the id_name. I don't want to do any munging on the
| sysfs names because I can't guess if the lm_sensors names had "i2c" or
| "isa", and besides, probably sysfs will eventually be more common
| so I'll just make its names the standard now (gkrellm 2.1.20).
*/
gboolean
gkrellm_sys_sensors_init(void)
{
FILE *f;
GDir *dir, *chip_dir;
VoltDefault *voltdefault;
gint id = 0;
gint type, voltdefaultsize;
gfloat factor, offset;
gchar *name, *chip_name, *fixed_chip_name, *path, *default_label;
gchar *vref, *sensor_path, *sensor_name, id_name[128];
struct lconv *lc;
lc = localeconv();
locale_decimal_point = *lc->decimal_point;
if (locale_decimal_point != '.')
need_locale_fix = TRUE;
if ((dir = g_dir_open(THERMAL_ZONE_DIR, 0, NULL)) != NULL)
{
while ((name = (gchar *) g_dir_read_name(dir)) != NULL)
{
path = g_build_filename(THERMAL_ZONE_DIR, name,
"temperature", NULL);
if (g_file_test(path, G_FILE_TEST_IS_REGULAR))
{
snprintf(id_name, sizeof(id_name), "thermal_zone/%s", name);
gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE,
path, id_name,
id, 0, THERMAL_ZONE_INTERFACE,
1.0, 0.0, NULL, name);
}
g_free(path);
}
g_dir_close(dir);
}
if ((dir = g_dir_open(THERMAL_DIR, 0, NULL)) != NULL)
{
while ((name = (gchar *) g_dir_read_name(dir)) != NULL)
{
path = g_build_filename(THERMAL_DIR, name, "status", NULL);
if (g_file_test(path, G_FILE_TEST_IS_REGULAR))
{
snprintf(id_name, sizeof(id_name), "thermal/%s", name);
gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE,
path, id_name,
id, 0, THERMAL_INTERFACE,
1.0, 0.0, NULL, name);
}
g_free(path);
}
g_dir_close(dir);
}
/* Do intial daemon reads to get sensors loaded into sensors.c
*/
gkrellm_sys_sensors_hddtemp_check();
gkrellm_sys_sensors_mbmon_check(TRUE);
/*
* IBM ACPI Temperature Sensors
*/
if ((f = fopen(IBM_ACPI_THERMAL, "r")) != NULL)
{
gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE,
IBM_ACPI_THERMAL, "IBM ACPI CPU",
id, IBM_ACPI_CPU_TEMP, IBM_ACPI_INTERFACE,
1.0, 0.0, NULL, "CPU");
gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE,
IBM_ACPI_THERMAL, "IBM ACPI Mini PCI Module",
id, IBM_ACPI_PCI_TEMP, IBM_ACPI_INTERFACE,
1.0, 0.0, NULL, "PCI");
gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE,
IBM_ACPI_THERMAL, "IBM ACPI HDD",
id, IBM_ACPI_HDD_TEMP, IBM_ACPI_INTERFACE,
1.0, 0.0, NULL, "HDD");
gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE,
IBM_ACPI_THERMAL, "IBM ACPI GPU",
id, IBM_ACPI_GPU_TEMP, IBM_ACPI_INTERFACE,
1.0, 0.0, NULL, "GPU");
gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE,
IBM_ACPI_THERMAL, "IBM ACPI Battery 1",
id, IBM_ACPI_BAT1_TEMP, IBM_ACPI_INTERFACE,
1.0, 0.0, NULL, "BAT1");
gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE,
IBM_ACPI_THERMAL, "IBM ACPI Battery 2",
id, IBM_ACPI_BAT2_TEMP, IBM_ACPI_INTERFACE,
1.0, 0.0, NULL, "BAT2");
fclose(f);
}
/*
* IBM ACPI Cooling Fan Sensor
*/
if ((f = fopen(IBM_ACPI_FAN_FILE, "r")) != NULL)
{
gkrellm_sensors_add_sensor(SENSOR_FAN,
IBM_ACPI_FAN_FILE, "IBM ACPI Fan Sensor",
id, IBM_ACPI_FAN, IBM_ACPI_INTERFACE,
1.0, 0.0, NULL, "Fan");
fclose(f);
}
if (gkrellm_sys_sensors_get_temperature("GPUCoreTemp", id, 0,
NVIDIA_SETTINGS_INTERFACE, NULL))
{
gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE,
"GPUCoreTemp", "nVidia GPU Core",
id, 0, NVIDIA_SETTINGS_INTERFACE,
1.0, 0.0, NULL, "GPU C");
}
if (gkrellm_sys_sensors_get_temperature("GPUAmbientTemp", id, 0,
NVIDIA_SETTINGS_INTERFACE, NULL))
{
gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE,
"GPUAmbientTemp", "nVidia GPU Ambient",
id, 0, NVIDIA_SETTINGS_INTERFACE,
1.0, 0.0, NULL, "GPU A");
}
if ((dir = g_dir_open(SENSORS_DIR, 0, NULL)) == NULL)
{
sysfs_sensors_init();
return TRUE;
}
while ((chip_name = (gchar *) g_dir_read_name(dir)) != NULL)
{
fixed_chip_name = g_strdup(chip_name);
gkrellm_sensors_linux_name_fix(fixed_chip_name);
path = g_build_filename(SENSORS_DIR, chip_name, NULL);
chip_dir = g_dir_open(path, 0, NULL);
if (!chip_dir)
{
g_free(path);
continue;
}
if (_GK.debug_level & DEBUG_SENSORS)
printf("lm_sensors dir: %s\n", path);
get_volt_default(chip_name, &voltdefault, &voltdefaultsize);
while ((sensor_name = (gchar *) g_dir_read_name(chip_dir)) != NULL)
{
if (!strncmp(sensor_name, "temp", 4))
type = SENSOR_TEMPERATURE;
else if ( !strncmp(sensor_name, "fan", 3)
&& ( !sensor_name[3]
|| (isdigit(sensor_name[3]) && !sensor_name[4])
)
)
type = SENSOR_FAN;
else if (!strncmp(sensor_name, "in", 2) && isdigit(sensor_name[2]))
{
type = SENSOR_VOLTAGE;
id = sensor_name[2] - '0';
}
else if (!strncmp(sensor_name, "vi", 2) && isdigit(sensor_name[3]))
{
type = SENSOR_VOLTAGE;
id = sensor_name[3] - '0';
}
else if (!strcmp(sensor_name, "vdd"))
{
type = SENSOR_VOLTAGE;
id = 0;
}
else
continue;
factor = 1.0;
offset = 0;
default_label = vref = NULL;
if (type == SENSOR_VOLTAGE)
{
if (id < voltdefaultsize)
{
default_label = voltdefault[id].name;
factor = voltdefault[id].factor;
offset = voltdefault[id].offset;
vref = voltdefault[id].vref;
}
else
default_label = sensor_name;
}
sensor_path = g_strdup_printf("%s/%s", path, sensor_name);
snprintf(id_name, sizeof(id_name), "%s/%s",
fixed_chip_name, sensor_name);
gkrellm_sensors_add_sensor(type, sensor_path, id_name,
id, 0, 0,
factor, offset, vref, default_label);
if (_GK.debug_level & DEBUG_SENSORS)
printf("%s %s %d %d\n",
sensor_path, id_name, id, type);
g_free(sensor_path);
}
g_dir_close(chip_dir);
g_free(path);
g_free(fixed_chip_name);
}
g_dir_close(dir);
return TRUE;
}
--------------090009040302070302030304--