2018-05-12 16:03:00 +00:00
|
|
|
|
/*
|
2009-07-28 21:44:22 +00:00
|
|
|
|
*
|
|
|
|
|
* Conky, a system monitor, based on torsmo
|
2007-08-10 19:53:44 +00:00
|
|
|
|
*
|
|
|
|
|
* Please see COPYING for details
|
|
|
|
|
*
|
|
|
|
|
* Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
|
2007-08-10 20:38:58 +00:00
|
|
|
|
* Copyright (c) 2007 Toni Spets
|
2018-05-12 16:03:00 +00:00
|
|
|
|
* Copyright (c) 2005-2018 Brenden Matthews, Philip Kovacs, et. al.
|
2008-02-20 20:30:45 +00:00
|
|
|
|
* (see AUTHORS)
|
2007-08-10 19:53:44 +00:00
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* This program 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.
|
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2008-02-20 20:30:45 +00:00
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2005-08-05 01:06:17 +00:00
|
|
|
|
*
|
2008-12-09 23:35:49 +00:00
|
|
|
|
*/
|
2005-08-05 01:06:17 +00:00
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
#include "linux.h"
|
2005-07-20 00:30:40 +00:00
|
|
|
|
#include <ctype.h>
|
2018-05-12 16:03:00 +00:00
|
|
|
|
#include <dirent.h>
|
2005-07-20 00:30:40 +00:00
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include <sys/stat.h>
|
2018-05-12 16:03:00 +00:00
|
|
|
|
#include <sys/sysinfo.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include "common.h"
|
|
|
|
|
#include "conky.h"
|
|
|
|
|
#include "diskio.h"
|
|
|
|
|
#include "logging.h"
|
|
|
|
|
#include "net_stat.h"
|
|
|
|
|
#include "proc.h"
|
|
|
|
|
#include "temphelper.h"
|
2007-01-15 00:02:38 +00:00
|
|
|
|
#ifndef HAVE_CLOCK_GETTIME
|
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
#endif
|
2005-07-20 00:30:40 +00:00
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
// #include <assert.h>
|
|
|
|
|
#include <time.h>
|
2011-10-02 12:43:44 +00:00
|
|
|
|
#include <unordered_map>
|
2010-06-20 17:09:13 +00:00
|
|
|
|
#include "setting.hh"
|
2005-07-20 00:30:40 +00:00
|
|
|
|
#include "top.h"
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
#include <arpa/inet.h>
|
2005-07-20 00:30:40 +00:00
|
|
|
|
#include <linux/sockios.h>
|
|
|
|
|
#include <net/if.h>
|
2018-05-12 16:03:00 +00:00
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
#include <sys/socket.h>
|
2008-03-24 03:38:28 +00:00
|
|
|
|
#ifdef _NET_IF_H
|
|
|
|
|
#define _LINUX_IF_H
|
|
|
|
|
#endif
|
2008-03-22 19:06:09 +00:00
|
|
|
|
#include <linux/route.h>
|
2005-07-20 00:30:40 +00:00
|
|
|
|
#include <math.h>
|
2009-12-11 21:54:11 +00:00
|
|
|
|
#include <pthread.h>
|
2018-12-08 22:45:17 +00:00
|
|
|
|
#include <atomic>
|
|
|
|
|
#include <mutex>
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2009-05-14 22:52:18 +00:00
|
|
|
|
/* The following ifdefs were adapted from gkrellm */
|
|
|
|
|
#include <linux/major.h>
|
|
|
|
|
|
|
|
|
|
#if !defined(MD_MAJOR)
|
|
|
|
|
#define MD_MAJOR 9
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if !defined(LVM_BLK_MAJOR)
|
|
|
|
|
#define LVM_BLK_MAJOR 58
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if !defined(NBD_MAJOR)
|
|
|
|
|
#define NBD_MAJOR 43
|
|
|
|
|
#endif
|
|
|
|
|
|
2012-12-23 20:57:29 +00:00
|
|
|
|
#if !defined(DM_MAJOR)
|
|
|
|
|
#define DM_MAJOR 253
|
|
|
|
|
#endif
|
|
|
|
|
|
2010-02-20 16:59:51 +00:00
|
|
|
|
#ifdef BUILD_WLAN
|
2007-08-07 12:51:08 +00:00
|
|
|
|
#include <iwlib.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2009-10-04 16:22:08 +00:00
|
|
|
|
struct sysfs {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int fd;
|
|
|
|
|
int arg;
|
|
|
|
|
char devtype[256];
|
|
|
|
|
char type[64];
|
|
|
|
|
float factor, offset;
|
2009-10-04 16:22:08 +00:00
|
|
|
|
};
|
|
|
|
|
|
2018-08-13 12:09:24 +00:00
|
|
|
|
/* To be used inside upspeed/f downspeed/f as ${gw_iface} variable */
|
2018-08-06 21:48:07 +00:00
|
|
|
|
char e_iface[50];
|
|
|
|
|
|
2018-08-13 12:09:24 +00:00
|
|
|
|
/* To use ${iface X} where X is a number and will
|
|
|
|
|
* return the current X NIC name */
|
|
|
|
|
static const unsigned int iface_len = 64U;
|
2018-08-13 18:40:30 +00:00
|
|
|
|
char interfaces_arr[iface_len][iface_len] = {""};
|
2018-08-13 12:09:24 +00:00
|
|
|
|
|
2005-11-27 06:56:35 +00:00
|
|
|
|
#define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
|
|
|
|
|
#define LONGSTAT_TEMPL "%*s %llu %llu %llu "
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
static conky::simple_config_setting<bool> top_cpu_separate("top_cpu_separate",
|
|
|
|
|
false, true);
|
2010-06-20 17:09:13 +00:00
|
|
|
|
|
2008-02-20 20:30:45 +00:00
|
|
|
|
/* This flag tells the linux routines to use the /proc system where possible,
|
|
|
|
|
* even if other api's are available, e.g. sysinfo() or getloadavg().
|
|
|
|
|
* the reason for this is to allow for /proc-based distributed monitoring.
|
|
|
|
|
* using a flag in this manner creates less confusing code. */
|
2006-12-23 06:01:16 +00:00
|
|
|
|
static int prefer_proc = 0;
|
|
|
|
|
|
2018-08-04 19:01:28 +00:00
|
|
|
|
/* To tell 'print_sysfs_sensor' whether to print the temperature
|
|
|
|
|
* in int or float */
|
|
|
|
|
static const char *temp2 = "empty";
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
void prepare_update(void) {}
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int update_uptime(void) {
|
2006-12-23 06:01:16 +00:00
|
|
|
|
#ifdef HAVE_SYSINFO
|
2018-05-12 16:03:00 +00:00
|
|
|
|
if (!prefer_proc) {
|
|
|
|
|
struct sysinfo s_info;
|
2008-02-20 20:30:45 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
sysinfo(&s_info);
|
|
|
|
|
info.uptime = (double)s_info.uptime;
|
|
|
|
|
} else
|
2005-07-20 00:30:40 +00:00
|
|
|
|
#endif
|
2018-05-12 16:03:00 +00:00
|
|
|
|
{
|
|
|
|
|
static int rep = 0;
|
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
|
|
if (!(fp = open_file("/proc/uptime", &rep))) {
|
|
|
|
|
info.uptime = 0.0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (fscanf(fp, "%lf", &info.uptime) <= 0) info.uptime = 0;
|
|
|
|
|
fclose(fp);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int check_mount(struct text_object *obj) {
|
|
|
|
|
int ret = 0;
|
|
|
|
|
FILE *mtab;
|
|
|
|
|
|
|
|
|
|
if (!obj->data.s) return 0;
|
|
|
|
|
|
|
|
|
|
if ((mtab = fopen("/proc/mounts", "r"))) {
|
|
|
|
|
char buf1[256], buf2[129];
|
|
|
|
|
|
|
|
|
|
while (fgets(buf1, 256, mtab)) {
|
|
|
|
|
sscanf(buf1, "%*s %128s", buf2);
|
|
|
|
|
if (!strcmp(obj->data.s, buf2)) {
|
|
|
|
|
ret = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fclose(mtab);
|
|
|
|
|
} else {
|
|
|
|
|
NORM_ERR("Could not open mtab");
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
2007-08-05 04:47:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-02-20 20:30:45 +00:00
|
|
|
|
/* these things are also in sysinfo except Buffers:
|
|
|
|
|
* (that's why I'm reading them from proc) */
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int update_meminfo(void) {
|
|
|
|
|
FILE *meminfo_fp;
|
|
|
|
|
static int rep = 0;
|
|
|
|
|
|
|
|
|
|
/* unsigned int a; */
|
|
|
|
|
char buf[256];
|
|
|
|
|
|
|
|
|
|
unsigned long long shmem = 0, sreclaimable = 0;
|
|
|
|
|
|
|
|
|
|
info.mem = info.memwithbuffers = info.memmax = info.memdirty = info.swap =
|
|
|
|
|
info.swapfree = info.swapmax = info.bufmem = info.buffers = info.cached =
|
|
|
|
|
info.memfree = info.memeasyfree = 0;
|
|
|
|
|
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) { return 0; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
while (!feof(meminfo_fp)) {
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (fgets(buf, 255, meminfo_fp) == nullptr) { break; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
if (strncmp(buf, "MemTotal:", 9) == 0) {
|
|
|
|
|
sscanf(buf, "%*s %llu", &info.memmax);
|
|
|
|
|
} else if (strncmp(buf, "MemFree:", 8) == 0) {
|
|
|
|
|
sscanf(buf, "%*s %llu", &info.memfree);
|
|
|
|
|
} else if (strncmp(buf, "SwapTotal:", 10) == 0) {
|
|
|
|
|
sscanf(buf, "%*s %llu", &info.swapmax);
|
|
|
|
|
} else if (strncmp(buf, "SwapFree:", 9) == 0) {
|
|
|
|
|
sscanf(buf, "%*s %llu", &info.swapfree);
|
|
|
|
|
} else if (strncmp(buf, "Buffers:", 8) == 0) {
|
|
|
|
|
sscanf(buf, "%*s %llu", &info.buffers);
|
|
|
|
|
} else if (strncmp(buf, "Cached:", 7) == 0) {
|
|
|
|
|
sscanf(buf, "%*s %llu", &info.cached);
|
|
|
|
|
} else if (strncmp(buf, "Dirty:", 6) == 0) {
|
|
|
|
|
sscanf(buf, "%*s %llu", &info.memdirty);
|
|
|
|
|
} else if (strncmp(buf, "Shmem:", 6) == 0) {
|
|
|
|
|
sscanf(buf, "%*s %llu", &shmem);
|
|
|
|
|
} else if (strncmp(buf, "SReclaimable:", 13) == 0) {
|
|
|
|
|
sscanf(buf, "%*s %llu", &sreclaimable);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info.mem = info.memwithbuffers = info.memmax - info.memfree;
|
|
|
|
|
info.memeasyfree = info.memfree;
|
|
|
|
|
info.swap = info.swapmax - info.swapfree;
|
|
|
|
|
|
|
|
|
|
/* Reclaimable memory: does not include shared memory, which is part of cached
|
|
|
|
|
but unreclaimable. Includes the reclaimable part of the Slab cache though.
|
|
|
|
|
Note: when shared memory is swapped out, shmem decreases and swapfree
|
|
|
|
|
decreases - we want this.
|
|
|
|
|
*/
|
|
|
|
|
info.bufmem = (info.cached - shmem) + info.buffers + sreclaimable;
|
|
|
|
|
|
|
|
|
|
/* Now (info.mem - info.bufmem) is the *really used* (aka unreclaimable)
|
|
|
|
|
memory. When this value reaches the size of the physical RAM, and swap is
|
|
|
|
|
full or non-present, OOM happens. Therefore this is the value users want to
|
|
|
|
|
monitor, regarding their RAM.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
fclose(meminfo_fp);
|
|
|
|
|
return 0;
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
void print_laptop_mode(struct text_object *obj, char *p,
|
|
|
|
|
unsigned int p_max_size) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
FILE *fp;
|
|
|
|
|
int val = -1;
|
2008-03-22 20:13:51 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
(void)obj;
|
2009-11-16 00:15:39 +00:00
|
|
|
|
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
if (fscanf(fp, "%d\n", &val) <= 0) val = 0;
|
|
|
|
|
fclose(fp);
|
|
|
|
|
}
|
|
|
|
|
snprintf(p, p_max_size, "%d", val);
|
2008-03-22 20:13:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-03-22 21:10:43 +00:00
|
|
|
|
/* my system says:
|
|
|
|
|
* # cat /sys/block/sda/queue/scheduler
|
|
|
|
|
* noop [anticipatory] cfq
|
|
|
|
|
*/
|
2018-12-08 22:45:17 +00:00
|
|
|
|
void print_ioscheduler(struct text_object *obj, char *p,
|
|
|
|
|
unsigned int p_max_size) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
FILE *fp;
|
|
|
|
|
char buf[128];
|
|
|
|
|
|
|
|
|
|
if (!obj->data.s) goto out_fail;
|
|
|
|
|
|
|
|
|
|
snprintf(buf, 127, "/sys/block/%s/queue/scheduler", obj->data.s);
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if ((fp = fopen(buf, "r")) == nullptr) goto out_fail;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
while (fscanf(fp, "%127s", buf) == 1) {
|
|
|
|
|
if (buf[0] == '[') {
|
|
|
|
|
buf[strlen(buf) - 1] = '\0';
|
|
|
|
|
snprintf(p, p_max_size, "%s", buf + 1);
|
|
|
|
|
fclose(fp);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fclose(fp);
|
2009-11-16 21:27:53 +00:00
|
|
|
|
out_fail:
|
2018-08-02 15:15:16 +00:00
|
|
|
|
snprintf(p, p_max_size, "%s", "n/a");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
return;
|
2008-03-22 21:10:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
class gw_info_s {
|
|
|
|
|
public:
|
|
|
|
|
gw_info_s() : iface(nullptr), ip(nullptr), count(0) {}
|
2018-05-12 16:03:00 +00:00
|
|
|
|
char *iface;
|
|
|
|
|
char *ip;
|
2018-12-08 22:45:17 +00:00
|
|
|
|
std::atomic<int> count;
|
|
|
|
|
std::mutex mutex;
|
|
|
|
|
|
|
|
|
|
void reset() {
|
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
|
free_and_zero(iface);
|
|
|
|
|
free_and_zero(ip);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
static gw_info_s gw_info;
|
2009-10-13 20:44:56 +00:00
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
char *save_set_string(char *x, char *y) {
|
|
|
|
|
if (x != nullptr && strcmp((char *)x, (char *)y)) {
|
|
|
|
|
free_and_zero(x);
|
|
|
|
|
x = strndup("multiple", text_buffer_size.get(*state));
|
|
|
|
|
} else if (x == nullptr && y != nullptr) {
|
|
|
|
|
x = strndup(y, text_buffer_size.get(*state));
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
2018-12-08 22:45:17 +00:00
|
|
|
|
return x;
|
|
|
|
|
}
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
void update_gateway_info_failure(const char *reason) {
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (reason != nullptr) { perror(reason); }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
// 2 pointers to 1 location causes a crash when we try to free them both
|
2018-12-19 16:11:24 +00:00
|
|
|
|
std::unique_lock<std::mutex> lock(gw_info.mutex);
|
2018-12-08 22:45:17 +00:00
|
|
|
|
free_and_zero(gw_info.iface);
|
|
|
|
|
free_and_zero(gw_info.ip);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
gw_info.iface = strndup("failed", text_buffer_size.get(*state));
|
|
|
|
|
gw_info.ip = strndup("failed", text_buffer_size.get(*state));
|
2008-06-04 08:09:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-12-17 01:06:34 +00:00
|
|
|
|
/* Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT */
|
|
|
|
|
#define RT_ENTRY_FORMAT "%63s %lx %lx %x %*d %*d %*d %lx %*d %*d %*d\n"
|
|
|
|
|
|
2018-08-13 12:09:24 +00:00
|
|
|
|
FILE *check_procroute() {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
FILE *fp;
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if ((fp = fopen("/proc/net/route", "r")) == nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
update_gateway_info_failure("fopen()");
|
2018-08-13 12:09:24 +00:00
|
|
|
|
return nullptr;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* skip over the table header line, which is always present */
|
|
|
|
|
if (fscanf(fp, "%*[^\n]\n") < 0) {
|
|
|
|
|
fclose(fp);
|
2018-08-13 12:09:24 +00:00
|
|
|
|
return nullptr;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-13 12:09:24 +00:00
|
|
|
|
return fp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int update_gateway_info2(void) {
|
|
|
|
|
FILE *fp;
|
|
|
|
|
char iface[iface_len];
|
|
|
|
|
unsigned long dest;
|
|
|
|
|
unsigned long gate;
|
|
|
|
|
unsigned long mask;
|
|
|
|
|
unsigned int flags;
|
|
|
|
|
unsigned int x = 1;
|
|
|
|
|
unsigned int z = 1;
|
|
|
|
|
int strcmpreturn;
|
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
if ((fp = check_procroute()) != nullptr) {
|
2018-08-13 12:09:24 +00:00
|
|
|
|
while (!feof(fp)) {
|
|
|
|
|
strcmpreturn = 1;
|
2018-12-08 22:45:17 +00:00
|
|
|
|
if (fscanf(fp, RT_ENTRY_FORMAT, iface, &dest, &gate, &flags, &mask) !=
|
|
|
|
|
5) {
|
2018-08-13 12:09:24 +00:00
|
|
|
|
update_gateway_info_failure("fscanf()");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!(dest || mask) && ((flags & RTF_GATEWAY) || !gate)) {
|
|
|
|
|
snprintf(e_iface, 49, "%s", iface);
|
|
|
|
|
}
|
|
|
|
|
if (1U == x) {
|
|
|
|
|
snprintf(interfaces_arr[x++], iface_len - 1, "%s", iface);
|
|
|
|
|
continue;
|
|
|
|
|
} else if (0 == strcmp(iface, interfaces_arr[x - 1])) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
for (z = 1; z < iface_len - 1 && strcmpreturn == 1; z++) {
|
|
|
|
|
strcmpreturn = strcmp(iface, interfaces_arr[z]);
|
|
|
|
|
}
|
|
|
|
|
if (strcmpreturn == 1) {
|
|
|
|
|
snprintf(interfaces_arr[x++], iface_len - 1, "%s", iface);
|
|
|
|
|
}
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
2018-08-13 12:09:24 +00:00
|
|
|
|
fclose(fp);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int update_gateway_info(void) {
|
|
|
|
|
FILE *fp;
|
|
|
|
|
struct in_addr ina;
|
|
|
|
|
char iface[iface_len];
|
|
|
|
|
unsigned long dest, gate, mask;
|
|
|
|
|
unsigned int flags;
|
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
gw_info.reset();
|
2018-08-13 12:09:24 +00:00
|
|
|
|
gw_info.count = 0;
|
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
if ((fp = check_procroute()) != nullptr) {
|
2018-08-13 12:09:24 +00:00
|
|
|
|
while (!feof(fp)) {
|
2018-12-08 22:45:17 +00:00
|
|
|
|
if (fscanf(fp, RT_ENTRY_FORMAT, iface, &dest, &gate, &flags, &mask) !=
|
|
|
|
|
5) {
|
2018-08-13 12:09:24 +00:00
|
|
|
|
update_gateway_info_failure("fscanf()");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!(dest || mask) && ((flags & RTF_GATEWAY) || !gate)) {
|
|
|
|
|
gw_info.count++;
|
|
|
|
|
snprintf(e_iface, 49, "%s", iface);
|
2018-12-19 16:11:24 +00:00
|
|
|
|
std::unique_lock<std::mutex> lock(gw_info.mutex);
|
2018-12-08 22:45:17 +00:00
|
|
|
|
gw_info.iface = save_set_string(gw_info.iface, iface);
|
2018-08-13 12:09:24 +00:00
|
|
|
|
ina.s_addr = gate;
|
2018-12-08 22:45:17 +00:00
|
|
|
|
gw_info.ip = save_set_string(gw_info.ip, inet_ntoa(ina));
|
2018-08-13 12:09:24 +00:00
|
|
|
|
}
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
2018-08-13 12:09:24 +00:00
|
|
|
|
fclose(fp);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
return 0;
|
2008-03-22 19:06:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
void free_gateway_info(struct text_object *obj) {
|
|
|
|
|
(void)obj;
|
2018-12-08 22:45:17 +00:00
|
|
|
|
gw_info.reset();
|
2009-10-13 20:44:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int gateway_exists(struct text_object *obj) {
|
|
|
|
|
(void)obj;
|
|
|
|
|
return !!gw_info.count;
|
2009-10-13 20:44:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
void print_gateway_iface(struct text_object *obj, char *p,
|
|
|
|
|
unsigned int p_max_size) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
(void)obj;
|
2018-12-08 22:45:17 +00:00
|
|
|
|
std::lock_guard<std::mutex> lock(gw_info.mutex);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
snprintf(p, p_max_size, "%s", gw_info.iface);
|
2009-10-13 20:44:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
void print_gateway_iface2(struct text_object *obj, char *p,
|
|
|
|
|
unsigned int p_max_size) {
|
2018-08-13 12:09:24 +00:00
|
|
|
|
long int z = 0;
|
|
|
|
|
unsigned int x = 1;
|
|
|
|
|
unsigned int found = 0;
|
|
|
|
|
char buf[iface_len * iface_len] = {""};
|
|
|
|
|
char *buf_ptr = buf;
|
|
|
|
|
|
|
|
|
|
if (0 == strcmp(obj->data.s, "")) {
|
|
|
|
|
for (; x < iface_len - 1; x++) {
|
2018-12-08 22:45:17 +00:00
|
|
|
|
if (0 == strcmp("", interfaces_arr[x])) { break; }
|
2018-08-13 12:09:24 +00:00
|
|
|
|
buf_ptr += snprintf(buf_ptr, iface_len - 1, "%s, ", interfaces_arr[x]);
|
|
|
|
|
found = 1;
|
|
|
|
|
}
|
|
|
|
|
if (1 == found) {
|
|
|
|
|
--buf_ptr;
|
|
|
|
|
*(--buf_ptr) = '\0';
|
|
|
|
|
}
|
|
|
|
|
snprintf(p, p_max_size, "%s", buf);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
z = strtol(obj->data.s, (char **)NULL, 10);
|
2018-12-08 22:45:17 +00:00
|
|
|
|
if ((iface_len - 1) > z) { snprintf(p, p_max_size, "%s", interfaces_arr[z]); }
|
2018-08-13 12:09:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
void print_gateway_ip(struct text_object *obj, char *p,
|
|
|
|
|
unsigned int p_max_size) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
(void)obj;
|
2018-12-08 22:45:17 +00:00
|
|
|
|
std::lock_guard<std::mutex> lock(gw_info.mutex);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
snprintf(p, p_max_size, "%s", gw_info.ip);
|
2009-10-13 20:44:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-12-10 20:13:29 +00:00
|
|
|
|
/**
|
|
|
|
|
* Parses information from /proc/net/dev and stores them in ???
|
|
|
|
|
*
|
|
|
|
|
* For the output format of /proc/net/dev @see http://linux.die.net/man/5/proc
|
|
|
|
|
*
|
|
|
|
|
* @return always returns 0. May change in the future, e.g. returning non zero
|
|
|
|
|
* if some error happened
|
|
|
|
|
**/
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int update_net_stats(void) {
|
2018-08-06 22:09:20 +00:00
|
|
|
|
update_gateway_info();
|
2018-08-13 18:40:30 +00:00
|
|
|
|
update_gateway_info2();
|
2018-05-12 16:03:00 +00:00
|
|
|
|
FILE *net_dev_fp;
|
|
|
|
|
static int rep = 0;
|
|
|
|
|
/* variably to notify the parts averaging the download speed, that this
|
|
|
|
|
* is the first call ever to this function. This variable can't be used
|
|
|
|
|
* to decide if this is the first time an interface was parsed as there
|
|
|
|
|
* are many interfaces, which can be activated and deactivated at arbitrary
|
|
|
|
|
* times */
|
|
|
|
|
static char first = 1;
|
|
|
|
|
|
|
|
|
|
// FIXME: arbitrary size chosen to keep code simple.
|
2018-08-13 18:40:30 +00:00
|
|
|
|
int i;
|
|
|
|
|
int i2;
|
|
|
|
|
unsigned int curtmp1;
|
|
|
|
|
unsigned int curtmp2;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
unsigned int k;
|
|
|
|
|
struct ifconf conf;
|
|
|
|
|
char buf[256];
|
|
|
|
|
double delta;
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2010-02-20 16:59:51 +00:00
|
|
|
|
#ifdef BUILD_WLAN
|
2018-05-12 16:03:00 +00:00
|
|
|
|
// wireless info variables
|
|
|
|
|
int skfd, has_bitrate = 0;
|
|
|
|
|
struct wireless_info *winfo;
|
|
|
|
|
struct iwreq wrq;
|
2007-08-07 12:51:08 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/* get delta */
|
|
|
|
|
delta = current_update_time - last_update_time;
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (delta <= 0.0001) { return 0; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
/* open file /proc/net/dev. If not something went wrong, clear all
|
|
|
|
|
* network statistics */
|
|
|
|
|
if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
|
|
|
|
|
clear_net_stats();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
/* ignore first two header lines in file /proc/net/dev. If somethings
|
|
|
|
|
* goes wrong, e.g. end of file reached, quit.
|
|
|
|
|
* (Why isn't clear_net_stats called for this case ??? */
|
2018-05-13 13:58:03 +00:00
|
|
|
|
char *one = fgets(buf, 255, net_dev_fp);
|
|
|
|
|
char *two = fgets(buf, 255, net_dev_fp);
|
|
|
|
|
if (!one || /* garbage */
|
|
|
|
|
!two) { /* garbage (field names) */
|
2018-05-12 16:03:00 +00:00
|
|
|
|
fclose(net_dev_fp);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* read each interface */
|
|
|
|
|
for (i2 = 0; i2 < MAX_NET_INTERFACES; i2++) {
|
|
|
|
|
struct net_stat *ns;
|
|
|
|
|
char *s, *p;
|
|
|
|
|
char temp_addr[18];
|
|
|
|
|
long long r, t, last_recv, last_trans;
|
|
|
|
|
|
|
|
|
|
/* quit only after all non-header lines from /proc/net/dev parsed */
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (fgets(buf, 255, net_dev_fp) == nullptr) { break; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
p = buf;
|
|
|
|
|
/* change char * p to first non-space character, which is the beginning
|
|
|
|
|
* of the interface name */
|
2018-08-02 15:15:16 +00:00
|
|
|
|
while (*p != '\0' && isspace((unsigned char)*p)) { p++; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
s = p;
|
|
|
|
|
|
|
|
|
|
/* increment p until the end of the interface name has been reached */
|
2018-05-13 13:58:03 +00:00
|
|
|
|
while (*p != '\0' && *p != ':') { p++; }
|
|
|
|
|
if (*p == '\0') { continue; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/* replace ':' with '\0' in output of /proc/net/dev */
|
|
|
|
|
*p = '\0';
|
|
|
|
|
p++;
|
|
|
|
|
|
|
|
|
|
/* get pointer to interface statistics with the interface name in s */
|
2018-05-12 23:26:31 +00:00
|
|
|
|
ns = get_net_stat(s, nullptr, NULL);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
ns->up = 1;
|
|
|
|
|
memset(&(ns->addr.sa_data), 0, 14);
|
|
|
|
|
|
|
|
|
|
memset(ns->addrs, 0,
|
|
|
|
|
17 * MAX_NET_INTERFACES +
|
|
|
|
|
1); /* Up to 17 chars per ip, max MAX_NET_INTERFACES interfaces.
|
|
|
|
|
Nasty memory usage... */
|
|
|
|
|
|
|
|
|
|
/* bytes packets errs drop fifo frame compressed multicast|bytes ... */
|
|
|
|
|
sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
|
|
|
|
|
&r, &t);
|
|
|
|
|
|
|
|
|
|
/* if the interface is parsed the first time, then set recv and trans
|
|
|
|
|
* to currently received, meaning the change in network traffic is 0 */
|
|
|
|
|
if (ns->last_read_recv == -1) {
|
|
|
|
|
ns->recv = r;
|
|
|
|
|
first = 1;
|
|
|
|
|
ns->last_read_recv = r;
|
|
|
|
|
}
|
|
|
|
|
if (ns->last_read_trans == -1) {
|
|
|
|
|
ns->trans = t;
|
|
|
|
|
first = 1;
|
|
|
|
|
ns->last_read_trans = t;
|
|
|
|
|
}
|
|
|
|
|
/* move current traffic statistic to last thereby obsoleting the
|
|
|
|
|
* current statistic */
|
|
|
|
|
last_recv = ns->recv;
|
|
|
|
|
last_trans = ns->trans;
|
|
|
|
|
|
|
|
|
|
/* If recv or trans is less than last time, an overflow happened.
|
|
|
|
|
* In that case set the last traffic to the current one, don't set
|
|
|
|
|
* it to 0, else a spike in the download and upload speed will occur! */
|
|
|
|
|
if (r < ns->last_read_recv) {
|
|
|
|
|
last_recv = r;
|
|
|
|
|
} else {
|
|
|
|
|
ns->recv += (r - ns->last_read_recv);
|
|
|
|
|
}
|
|
|
|
|
ns->last_read_recv = r;
|
|
|
|
|
|
|
|
|
|
if (t < ns->last_read_trans) {
|
|
|
|
|
last_trans = t;
|
|
|
|
|
} else {
|
|
|
|
|
ns->trans += (t - ns->last_read_trans);
|
|
|
|
|
}
|
|
|
|
|
ns->last_read_trans = t;
|
|
|
|
|
|
|
|
|
|
/*** ip addr patch ***/
|
|
|
|
|
i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
|
|
|
|
|
|
|
|
|
|
conf.ifc_buf = (char *)malloc(sizeof(struct ifreq) * MAX_NET_INTERFACES);
|
|
|
|
|
conf.ifc_len = sizeof(struct ifreq) * MAX_NET_INTERFACES;
|
|
|
|
|
memset(conf.ifc_buf, 0, conf.ifc_len);
|
|
|
|
|
|
|
|
|
|
ioctl((long)i, SIOCGIFCONF, &conf);
|
|
|
|
|
|
|
|
|
|
for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
|
|
|
|
|
struct net_stat *ns2;
|
|
|
|
|
|
|
|
|
|
if (!(((struct ifreq *)conf.ifc_buf) + k)) break;
|
|
|
|
|
|
|
|
|
|
ns2 = get_net_stat(((struct ifreq *)conf.ifc_buf)[k].ifr_ifrn.ifrn_name,
|
2018-05-12 23:26:31 +00:00
|
|
|
|
nullptr, NULL);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
ns2->addr = ((struct ifreq *)conf.ifc_buf)[k].ifr_ifru.ifru_addr;
|
|
|
|
|
sprintf(temp_addr, "%u.%u.%u.%u, ", ns2->addr.sa_data[2] & 255,
|
|
|
|
|
ns2->addr.sa_data[3] & 255, ns2->addr.sa_data[4] & 255,
|
|
|
|
|
ns2->addr.sa_data[5] & 255);
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (nullptr == strstr(ns2->addrs, temp_addr))
|
2018-05-12 16:03:00 +00:00
|
|
|
|
strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
close((long)i);
|
|
|
|
|
|
|
|
|
|
free(conf.ifc_buf);
|
|
|
|
|
/*** end ip addr patch ***/
|
|
|
|
|
|
|
|
|
|
if (!first) {
|
|
|
|
|
/* calculate instantenous speeds */
|
|
|
|
|
ns->net_rec[0] = (ns->recv - last_recv) / delta;
|
|
|
|
|
ns->net_trans[0] = (ns->trans - last_trans) / delta;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
curtmp1 = 0;
|
|
|
|
|
curtmp2 = 0;
|
|
|
|
|
/* get an average over the last speed samples */
|
|
|
|
|
int samples = net_avg_samples.get(*state);
|
|
|
|
|
/* is OpenMP actually useful here? How large is samples? > 1000 ? */
|
2009-05-05 23:36:12 +00:00
|
|
|
|
#ifdef HAVE_OPENMP
|
2018-05-12 16:03:00 +00:00
|
|
|
|
#pragma omp parallel for reduction(+ : curtmp1, curtmp2) schedule(dynamic, 10)
|
2009-05-05 23:36:12 +00:00
|
|
|
|
#endif /* HAVE_OPENMP */
|
2018-05-12 16:03:00 +00:00
|
|
|
|
for (i = 0; i < samples; i++) {
|
|
|
|
|
curtmp1 = curtmp1 + ns->net_rec[i];
|
|
|
|
|
curtmp2 = curtmp2 + ns->net_trans[i];
|
|
|
|
|
}
|
|
|
|
|
ns->recv_speed = curtmp1 / (double)samples;
|
|
|
|
|
ns->trans_speed = curtmp2 / (double)samples;
|
|
|
|
|
if (samples > 1) {
|
2009-05-05 23:36:12 +00:00
|
|
|
|
#ifdef HAVE_OPENMP
|
2018-05-12 16:03:00 +00:00
|
|
|
|
#pragma omp parallel for schedule(dynamic, 10)
|
2009-05-05 23:36:12 +00:00
|
|
|
|
#endif /* HAVE_OPENMP */
|
2018-05-12 16:03:00 +00:00
|
|
|
|
for (i = samples; i > 1; i--) {
|
|
|
|
|
ns->net_rec[i - 1] = ns->net_rec[i - 2];
|
|
|
|
|
ns->net_trans[i - 1] = ns->net_trans[i - 2];
|
|
|
|
|
}
|
|
|
|
|
}
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2010-02-20 16:59:51 +00:00
|
|
|
|
#ifdef BUILD_WLAN
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/* update wireless info */
|
|
|
|
|
winfo = (struct wireless_info *)malloc(sizeof(struct wireless_info));
|
|
|
|
|
memset(winfo, 0, sizeof(struct wireless_info));
|
|
|
|
|
|
|
|
|
|
skfd = iw_sockets_open();
|
|
|
|
|
if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
|
|
|
|
|
// set present winfo variables
|
|
|
|
|
if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
|
|
|
|
|
winfo->has_range = 1;
|
|
|
|
|
}
|
|
|
|
|
if (iw_get_stats(skfd, s, &(winfo->stats), &winfo->range,
|
|
|
|
|
winfo->has_range) >= 0) {
|
|
|
|
|
winfo->has_stats = 1;
|
|
|
|
|
}
|
|
|
|
|
if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
|
|
|
|
|
winfo->has_ap_addr = 1;
|
|
|
|
|
memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get bitrate
|
|
|
|
|
if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
|
|
|
|
|
memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
|
|
|
|
|
iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
|
|
|
|
|
has_bitrate = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get link quality
|
|
|
|
|
if (winfo->has_range && winfo->has_stats &&
|
|
|
|
|
((winfo->stats.qual.level != 0) ||
|
|
|
|
|
(winfo->stats.qual.updated & IW_QUAL_DBM))) {
|
|
|
|
|
if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
|
|
|
|
|
ns->link_qual = winfo->stats.qual.qual;
|
|
|
|
|
ns->link_qual_max = winfo->range.max_qual.qual;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get ap mac
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (winfo->has_ap_addr) { iw_sawap_ntop(&winfo->ap_addr, ns->ap); }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
// get essid
|
|
|
|
|
if (winfo->b.has_essid) {
|
|
|
|
|
if (winfo->b.essid_on) {
|
|
|
|
|
snprintf(ns->essid, 32, "%s", winfo->b.essid);
|
|
|
|
|
} else {
|
2018-08-02 15:15:16 +00:00
|
|
|
|
snprintf(ns->essid, 32, "%s", "off/any");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// get channel and freq
|
|
|
|
|
if (winfo->b.has_freq) {
|
|
|
|
|
if (winfo->has_range == 1) {
|
|
|
|
|
ns->channel = iw_freq_to_channel(winfo->b.freq, &(winfo->range));
|
|
|
|
|
iw_print_freq_value(ns->freq, 16, winfo->b.freq);
|
|
|
|
|
} else {
|
|
|
|
|
ns->channel = 0;
|
|
|
|
|
ns->freq[0] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
|
|
|
|
|
}
|
|
|
|
|
iw_sockets_close(skfd);
|
|
|
|
|
free(winfo);
|
2007-08-07 12:51:08 +00:00
|
|
|
|
#endif
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
2011-02-09 17:49:52 +00:00
|
|
|
|
|
|
|
|
|
#ifdef BUILD_IPV6
|
2018-05-12 16:03:00 +00:00
|
|
|
|
FILE *file;
|
|
|
|
|
char v6addr[33];
|
|
|
|
|
char devname[21];
|
|
|
|
|
unsigned int netmask, scope;
|
|
|
|
|
struct net_stat *ns;
|
|
|
|
|
struct v6addr *lastv6;
|
|
|
|
|
// remove the old v6 addresses otherwise they are listed multiple times
|
|
|
|
|
for (unsigned int i = 0; i < MAX_NET_INTERFACES; i++) {
|
|
|
|
|
ns = &netstats[i];
|
2018-05-12 23:26:31 +00:00
|
|
|
|
while (ns->v6addrs != nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
lastv6 = ns->v6addrs;
|
|
|
|
|
ns->v6addrs = ns->v6addrs->next;
|
|
|
|
|
free(lastv6);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if ((file = fopen(PROCDIR "/net/if_inet6", "r")) != nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
while (fscanf(file, "%32s %*02x %02x %02x %*02x %20s\n", v6addr, &netmask,
|
|
|
|
|
&scope, devname) != EOF) {
|
2018-05-12 23:26:31 +00:00
|
|
|
|
ns = get_net_stat(devname, nullptr, NULL);
|
|
|
|
|
if (ns->v6addrs == nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
lastv6 = (struct v6addr *)malloc(sizeof(struct v6addr));
|
|
|
|
|
ns->v6addrs = lastv6;
|
|
|
|
|
} else {
|
|
|
|
|
lastv6 = ns->v6addrs;
|
|
|
|
|
while (lastv6->next) lastv6 = lastv6->next;
|
|
|
|
|
lastv6->next = (struct v6addr *)malloc(sizeof(struct v6addr));
|
|
|
|
|
lastv6 = lastv6->next;
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < 16; i++)
|
|
|
|
|
sscanf(v6addr + 2 * i, "%2hhx", &(lastv6->addr.s6_addr[i]));
|
|
|
|
|
lastv6->netmask = netmask;
|
|
|
|
|
switch (scope) {
|
|
|
|
|
case 0: // global
|
|
|
|
|
lastv6->scope = 'G';
|
|
|
|
|
break;
|
|
|
|
|
case 16: // host-local
|
|
|
|
|
lastv6->scope = 'H';
|
|
|
|
|
break;
|
|
|
|
|
case 32: // link-local
|
|
|
|
|
lastv6->scope = 'L';
|
|
|
|
|
break;
|
|
|
|
|
case 64: // site-local
|
|
|
|
|
lastv6->scope = 'S';
|
|
|
|
|
break;
|
|
|
|
|
case 128: // compat
|
|
|
|
|
lastv6->scope = 'C';
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
lastv6->scope = '?';
|
|
|
|
|
}
|
2018-05-12 23:26:31 +00:00
|
|
|
|
lastv6->next = nullptr;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
fclose(file);
|
|
|
|
|
}
|
2011-02-09 17:49:52 +00:00
|
|
|
|
#endif /* BUILD_IPV6 */
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
first = 0;
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
fclose(net_dev_fp);
|
|
|
|
|
return 0;
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int result;
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int update_total_processes(void) {
|
|
|
|
|
DIR *dir;
|
|
|
|
|
struct dirent *entry;
|
|
|
|
|
int ignore1;
|
|
|
|
|
char ignore2;
|
|
|
|
|
|
|
|
|
|
info.procs = 0;
|
2018-05-13 13:58:03 +00:00
|
|
|
|
dir = opendir("/proc");
|
|
|
|
|
if (dir) {
|
|
|
|
|
while ((entry = readdir(dir))) {
|
|
|
|
|
if (sscanf(entry->d_name, "%d%c", &ignore1, &ignore2) == 1) {
|
|
|
|
|
info.procs++;
|
|
|
|
|
}
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
2018-05-13 13:58:03 +00:00
|
|
|
|
closedir(dir);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
return 0;
|
2009-11-07 17:14:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int update_threads(void) {
|
2006-12-23 06:01:16 +00:00
|
|
|
|
#ifdef HAVE_SYSINFO
|
2018-05-12 16:03:00 +00:00
|
|
|
|
if (!prefer_proc) {
|
|
|
|
|
struct sysinfo s_info;
|
2008-02-20 20:30:45 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
sysinfo(&s_info);
|
|
|
|
|
info.threads = s_info.procs;
|
|
|
|
|
} else
|
2006-12-23 06:01:16 +00:00
|
|
|
|
#endif
|
2018-05-12 16:03:00 +00:00
|
|
|
|
{
|
|
|
|
|
static int rep = 0;
|
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
|
|
if (!(fp = open_file("/proc/loadavg", &rep))) {
|
|
|
|
|
info.threads = 0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (fscanf(fp, "%*f %*f %*f %*d/%hu", &info.threads) <= 0) info.threads = 0;
|
|
|
|
|
fclose(fp);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2005-08-27 14:55:10 +00:00
|
|
|
|
#define CPU_SAMPLE_COUNT 15
|
2005-08-27 09:29:24 +00:00
|
|
|
|
struct cpu_info {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
unsigned long long cpu_user;
|
|
|
|
|
unsigned long long cpu_system;
|
|
|
|
|
unsigned long long cpu_nice;
|
|
|
|
|
unsigned long long cpu_idle;
|
|
|
|
|
unsigned long long cpu_iowait;
|
|
|
|
|
unsigned long long cpu_irq;
|
|
|
|
|
unsigned long long cpu_softirq;
|
|
|
|
|
unsigned long long cpu_steal;
|
|
|
|
|
unsigned long long cpu_total;
|
|
|
|
|
unsigned long long cpu_active_total;
|
|
|
|
|
unsigned long long cpu_last_total;
|
|
|
|
|
unsigned long long cpu_last_active_total;
|
|
|
|
|
double cpu_val[CPU_SAMPLE_COUNT];
|
2005-08-27 09:29:24 +00:00
|
|
|
|
};
|
|
|
|
|
static short cpu_setup = 0;
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2008-02-20 20:30:45 +00:00
|
|
|
|
/* Determine if this kernel gives us "extended" statistics information in
|
|
|
|
|
* /proc/stat.
|
|
|
|
|
* Kernels around 2.5 and earlier only reported user, system, nice, and
|
|
|
|
|
* idle values in proc stat.
|
|
|
|
|
* Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
|
|
|
|
|
* and steal */
|
2018-05-12 16:03:00 +00:00
|
|
|
|
void determine_longstat(char *buf) {
|
|
|
|
|
unsigned long long iowait = 0;
|
|
|
|
|
|
|
|
|
|
KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
|
|
|
|
|
/* scanf will either return -1 or 1 because there is only 1 assignment */
|
|
|
|
|
if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
|
|
|
|
|
KFLAG_SETON(KFLAG_IS_LONGSTAT);
|
|
|
|
|
}
|
2005-11-27 06:56:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-11-23 17:50:40 +00:00
|
|
|
|
void determine_longstat_file(void) {
|
|
|
|
|
#define MAX_PROCSTAT_LINELEN 255
|
2018-05-12 16:03:00 +00:00
|
|
|
|
FILE *stat_fp;
|
|
|
|
|
static int rep = 0;
|
|
|
|
|
char buf[MAX_PROCSTAT_LINELEN + 1];
|
|
|
|
|
|
2018-12-20 15:22:08 +00:00
|
|
|
|
if (!(stat_fp = open_file("/proc/stat", &rep))) return;
|
|
|
|
|
while (!feof(stat_fp) &&
|
|
|
|
|
fgets(buf, MAX_PROCSTAT_LINELEN, stat_fp) != nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
if (strncmp(buf, "cpu", 3) == 0) {
|
|
|
|
|
determine_longstat(buf);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fclose(stat_fp);
|
2010-11-23 17:50:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
void get_cpu_count(void) {
|
|
|
|
|
FILE *stat_fp;
|
|
|
|
|
static int rep = 0;
|
|
|
|
|
char buf[256];
|
|
|
|
|
char *str1, *str2, *token, *subtoken;
|
|
|
|
|
char *saveptr1, *saveptr2;
|
|
|
|
|
int subtoken1 = -1;
|
|
|
|
|
int subtoken2 = -1;
|
|
|
|
|
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (info.cpu_usage) { return; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
if (!(stat_fp = open_file("/sys/devices/system/cpu/present", &rep))) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info.cpu_count = 0;
|
|
|
|
|
|
|
|
|
|
while (!feof(stat_fp)) {
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (fgets(buf, 255, stat_fp) == nullptr) { break; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
// Do some parsing here to handle skipped cpu numbers. For example,
|
|
|
|
|
// for an AMD FX(tm)-6350 Six-Core Processor /sys/.../present reports
|
|
|
|
|
// "0,3-7". I assume that chip is really an 8-core die with two cores
|
|
|
|
|
// disabled... Presumably you could also get "0,3-4,6", and other
|
|
|
|
|
// combos too...
|
2018-05-12 23:26:31 +00:00
|
|
|
|
for (str1 = buf;; str1 = nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
token = strtok_r(str1, ",", &saveptr1);
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (token == nullptr) break;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
++info.cpu_count;
|
|
|
|
|
|
|
|
|
|
subtoken1 = -1;
|
|
|
|
|
subtoken2 = -1;
|
2018-05-12 23:26:31 +00:00
|
|
|
|
for (str2 = token;; str2 = nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
subtoken = strtok_r(str2, "-", &saveptr2);
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (subtoken == nullptr) break;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
if (subtoken1 < 0)
|
|
|
|
|
subtoken1 = atoi(subtoken);
|
|
|
|
|
else
|
|
|
|
|
subtoken2 = atoi(subtoken);
|
|
|
|
|
}
|
|
|
|
|
if (subtoken2 > 0) info.cpu_count += subtoken2 - subtoken1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
info.cpu_usage = (float *)malloc((info.cpu_count + 1) * sizeof(float));
|
|
|
|
|
|
|
|
|
|
fclose(stat_fp);
|
2005-08-27 09:29:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2005-11-27 06:56:35 +00:00
|
|
|
|
#define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
|
|
|
|
|
#define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
|
2005-08-27 09:29:24 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int update_stat(void) {
|
|
|
|
|
FILE *stat_fp;
|
|
|
|
|
static int rep = 0;
|
2018-12-20 15:22:08 +00:00
|
|
|
|
struct cpu_info *cpu = nullptr;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
char buf[256];
|
|
|
|
|
int i;
|
|
|
|
|
unsigned int idx;
|
|
|
|
|
double curtmp;
|
2018-05-12 23:26:31 +00:00
|
|
|
|
const char *stat_template = nullptr;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
unsigned int malloc_cpu_size = 0;
|
|
|
|
|
extern void *global_cpu;
|
|
|
|
|
|
|
|
|
|
static pthread_mutex_t last_stat_update_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
static double last_stat_update = 0.0;
|
|
|
|
|
float cur_total = 0.0;
|
|
|
|
|
|
|
|
|
|
/* since we use wrappers for this function, the update machinery
|
|
|
|
|
* can't eliminate double invocations of this function. Check for
|
|
|
|
|
* them here, otherwise cpu_usage counters are freaking out. */
|
|
|
|
|
pthread_mutex_lock(&last_stat_update_mutex);
|
|
|
|
|
if (last_stat_update == current_update_time) {
|
|
|
|
|
pthread_mutex_unlock(&last_stat_update_mutex);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
last_stat_update = current_update_time;
|
|
|
|
|
pthread_mutex_unlock(&last_stat_update_mutex);
|
|
|
|
|
|
|
|
|
|
/* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
|
|
|
|
|
if (!cpu_setup || !info.cpu_usage) {
|
|
|
|
|
get_cpu_count();
|
|
|
|
|
cpu_setup = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!stat_template) {
|
|
|
|
|
stat_template =
|
|
|
|
|
KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-20 15:22:08 +00:00
|
|
|
|
if (global_cpu) {
|
|
|
|
|
cpu = reinterpret_cast<struct cpu_info *>(global_cpu);
|
|
|
|
|
} else {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
|
|
|
|
|
cpu = (struct cpu_info *)malloc(malloc_cpu_size);
|
|
|
|
|
memset(cpu, 0, malloc_cpu_size);
|
|
|
|
|
global_cpu = cpu;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(stat_fp = open_file("/proc/stat", &rep))) {
|
|
|
|
|
info.run_threads = 0;
|
|
|
|
|
if (info.cpu_usage) {
|
|
|
|
|
memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idx = 0;
|
|
|
|
|
while (!feof(stat_fp)) {
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (fgets(buf, 255, stat_fp) == nullptr) { break; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
if (strncmp(buf, "procs_running ", 14) == 0) {
|
|
|
|
|
sscanf(buf, "%*s %hu", &info.run_threads);
|
|
|
|
|
} else if (strncmp(buf, "cpu", 3) == 0) {
|
|
|
|
|
double delta;
|
2018-08-02 15:15:16 +00:00
|
|
|
|
if (isdigit((unsigned char)buf[3])) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
idx++; // just increment here since the CPU index can skip numbers
|
|
|
|
|
} else {
|
|
|
|
|
idx = 0;
|
|
|
|
|
}
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (idx > info.cpu_count) { continue; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
sscanf(buf, stat_template, &(cpu[idx].cpu_user), &(cpu[idx].cpu_nice),
|
|
|
|
|
&(cpu[idx].cpu_system), &(cpu[idx].cpu_idle),
|
|
|
|
|
&(cpu[idx].cpu_iowait), &(cpu[idx].cpu_irq),
|
|
|
|
|
&(cpu[idx].cpu_softirq), &(cpu[idx].cpu_steal));
|
|
|
|
|
|
|
|
|
|
cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
|
|
|
|
|
cpu[idx].cpu_system + cpu[idx].cpu_idle +
|
|
|
|
|
cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
|
|
|
|
|
cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
|
|
|
|
|
|
|
|
|
|
cpu[idx].cpu_active_total =
|
|
|
|
|
cpu[idx].cpu_total - (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
|
|
|
|
|
|
|
|
|
|
delta = current_update_time - last_update_time;
|
|
|
|
|
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (delta <= 0.001) { break; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
cur_total = (float)(cpu[idx].cpu_total - cpu[idx].cpu_last_total);
|
|
|
|
|
if (cur_total == 0.0) {
|
|
|
|
|
cpu[idx].cpu_val[0] = 1.0;
|
|
|
|
|
} else {
|
|
|
|
|
cpu[idx].cpu_val[0] =
|
|
|
|
|
(cpu[idx].cpu_active_total - cpu[idx].cpu_last_active_total) /
|
|
|
|
|
cur_total;
|
|
|
|
|
}
|
|
|
|
|
curtmp = 0;
|
|
|
|
|
|
Add toluapp subtree (#712)
* First commit!
* Import to git
* Droping down CMake requirement
* Corrected installation of libraries
* Adding travis build
* Updated cmake macros
* Fixed find package
* Updated travis hook
* Updated travis hook
* Patch to support Lua 5.3.
* Fix Lua header include directives in tolua++.h
Use angle bracket rather than double quotes when including Lua headers in the tolua++ header. This fixes a problem on systems that default to a Lua version newer than 5.1 and install the tolua++ header to the same directory as newer Lua headers.
As there are (usually) no Lua headers in the same directory when building tolua++ itself, the preprocessor will look to the include directories passed to the compiler, one of which would be the Lua 5.1 include directory, and tolua++ will build with those (correct) headers. Then the tolua++ header is usually installed in the default include directory, alongside the newer Lua headers, which you wouldn't expect to cause any trouble.
But it does cause trouble when trying to build other programs that include the tolua++ header, because now the preprocessor does find Lua headers in the same directory as the tolua++ header, which are the newer (incorrect) headers. Now the program will either fail to compile, because it doesn't support the newer headers, or fail to link with the tolua++ shared object because they were compiled against different Lua headers.
Using angle brackets instead of double quotes in the include directives will fix the problem, because then the preprocessor will look to the include directories passed to the compiler first.
See http://www.cegui.org.uk/forum/viewtopic.php?f=10&t=7195
* Remove email notifications.
* Update travis build.
* Build shared and static libs.
* Patch toluapp to support Lua 5.3.
With this change, support for Lua 5.1 is dropped.
This resolve #116.
* Add some comments to clarify the toluapp handling.
* Add minor sonar fix.
2018-12-20 20:18:51 +00:00
|
|
|
|
int samples = std::min(cpu_avg_samples.get(*state), CPU_SAMPLE_COUNT);
|
2018-05-13 13:58:03 +00:00
|
|
|
|
for (i = 0; i < samples; i++) { curtmp = curtmp + cpu[idx].cpu_val[i]; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
info.cpu_usage[idx] = curtmp / samples;
|
2005-11-27 06:56:35 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
cpu[idx].cpu_last_total = cpu[idx].cpu_total;
|
|
|
|
|
cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
|
2018-12-20 15:22:08 +00:00
|
|
|
|
for (i = samples - 1; i > 1; i--) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fclose(stat_fp);
|
|
|
|
|
return 0;
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int update_running_processes(void) {
|
|
|
|
|
update_stat();
|
|
|
|
|
return 0;
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int update_cpu_usage(void) {
|
2018-08-11 22:18:51 +00:00
|
|
|
|
struct timespec tc = {0L, 100L * 1000000L};
|
|
|
|
|
update_stat();
|
|
|
|
|
if (-1 == (nanosleep(&tc, NULL))) {
|
|
|
|
|
NORM_ERR("update_cpu_usage(): nanosleep() failed");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2018-05-12 16:03:00 +00:00
|
|
|
|
update_stat();
|
|
|
|
|
return 0;
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
void free_cpu(struct text_object *) { /* no-op */
|
|
|
|
|
}
|
2018-08-07 15:54:01 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
// fscanf() that reads floats with points even if you are using a locale where
|
|
|
|
|
// floats are with commas
|
2011-02-10 00:08:34 +00:00
|
|
|
|
int fscanf_no_i18n(FILE *stream, const char *format, ...) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int returncode;
|
|
|
|
|
va_list ap;
|
2011-02-10 00:08:34 +00:00
|
|
|
|
|
|
|
|
|
#ifdef BUILD_I18N
|
2018-05-12 23:26:31 +00:00
|
|
|
|
const char *oldlocale = setlocale(LC_NUMERIC, nullptr);
|
2011-02-10 00:08:34 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
setlocale(LC_NUMERIC, "C");
|
2011-02-10 00:08:34 +00:00
|
|
|
|
#endif
|
2018-05-12 16:03:00 +00:00
|
|
|
|
va_start(ap, format);
|
|
|
|
|
returncode = vfscanf(stream, format, ap);
|
|
|
|
|
va_end(ap);
|
2011-02-10 00:08:34 +00:00
|
|
|
|
#ifdef BUILD_I18N
|
2018-05-12 16:03:00 +00:00
|
|
|
|
setlocale(LC_NUMERIC, oldlocale);
|
2011-02-10 00:08:34 +00:00
|
|
|
|
#endif
|
2018-05-12 16:03:00 +00:00
|
|
|
|
return returncode;
|
2011-02-10 00:08:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int update_load_average(void) {
|
2005-07-20 00:30:40 +00:00
|
|
|
|
#ifdef HAVE_GETLOADAVG
|
2018-05-12 16:03:00 +00:00
|
|
|
|
if (!prefer_proc) {
|
|
|
|
|
double v[3];
|
|
|
|
|
|
|
|
|
|
getloadavg(v, 3);
|
|
|
|
|
info.loadavg[0] = (float)v[0];
|
|
|
|
|
info.loadavg[1] = (float)v[1];
|
|
|
|
|
info.loadavg[2] = (float)v[2];
|
|
|
|
|
} else
|
2005-07-20 00:30:40 +00:00
|
|
|
|
#endif
|
2018-05-12 16:03:00 +00:00
|
|
|
|
{
|
|
|
|
|
static int rep = 0;
|
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
|
|
if (!(fp = open_file("/proc/loadavg", &rep))) {
|
|
|
|
|
info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (fscanf_no_i18n(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
|
|
|
|
|
&info.loadavg[2]) < 0)
|
|
|
|
|
info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
|
|
|
|
|
fclose(fp);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2005-08-27 07:15:44 +00:00
|
|
|
|
/***********************************************************/
|
|
|
|
|
/***********************************************************/
|
|
|
|
|
/***********************************************************/
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
static int no_dots(const struct dirent *d) {
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (d->d_name[0] == '.') { return 0; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
return 1;
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
static int get_first_file_in_a_directory(const char *dir, char *s, int *rep) {
|
|
|
|
|
struct dirent **namelist;
|
|
|
|
|
int i, n;
|
|
|
|
|
|
|
|
|
|
n = scandir(dir, &namelist, no_dots, alphasort);
|
|
|
|
|
if (n < 0) {
|
|
|
|
|
if (!rep || !*rep) {
|
|
|
|
|
NORM_ERR("scandir for %s: %s", dir, strerror(errno));
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (rep) { *rep = 1; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (n == 0) { return 0; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
strncpy(s, namelist[0]->d_name, 255);
|
|
|
|
|
s[255] = '\0';
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2009-05-05 23:36:12 +00:00
|
|
|
|
#ifdef HAVE_OPENMP
|
2018-05-12 16:03:00 +00:00
|
|
|
|
#pragma omp parallel for schedule(dynamic, 10)
|
2009-05-05 23:36:12 +00:00
|
|
|
|
#endif /* HAVE_OPENMP */
|
2018-05-13 13:58:03 +00:00
|
|
|
|
for (i = 0; i < n; i++) { free(namelist[i]); }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
free(namelist);
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
static int open_sysfs_sensor(const char *dir, const char *dev, const char *type,
|
|
|
|
|
int n, int *divisor, char *devtype) {
|
|
|
|
|
char path[256];
|
|
|
|
|
char buf[256];
|
|
|
|
|
int fd;
|
|
|
|
|
int divfd;
|
|
|
|
|
|
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
|
|
2018-05-12 23:26:31 +00:00
|
|
|
|
/* if device is nullptr or *, get first */
|
|
|
|
|
if (dev == nullptr || strcmp(dev, "*") == 0) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
static int rep = 0;
|
|
|
|
|
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (!get_first_file_in_a_directory(dir, buf, &rep)) { return -1; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
dev = buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(dir, "/sys/class/hwmon/") == 0) {
|
|
|
|
|
if (*buf) {
|
|
|
|
|
/* buf holds result from get_first_file_in_a_directory() above,
|
|
|
|
|
* e.g. "hwmon0" -- append "/device" */
|
2018-12-20 15:22:08 +00:00
|
|
|
|
strncat(buf, "/device", 255 - strnlen(buf, 255));
|
2018-05-12 16:03:00 +00:00
|
|
|
|
} else {
|
|
|
|
|
/* dev holds device number N as a string,
|
|
|
|
|
* e.g. "0", -- convert to "hwmon0/device" */
|
|
|
|
|
sprintf(buf, "hwmon%s/device", dev);
|
|
|
|
|
dev = buf;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* change vol to in, tempf to temp */
|
|
|
|
|
if (strcmp(type, "vol") == 0) {
|
|
|
|
|
type = "in";
|
|
|
|
|
} else if (strcmp(type, "tempf") == 0) {
|
|
|
|
|
type = "temp";
|
2018-08-04 19:01:28 +00:00
|
|
|
|
} else if (strcmp(type, "temp2") == 0) {
|
|
|
|
|
type = "temp";
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* construct path */
|
|
|
|
|
snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
|
|
|
|
|
|
|
|
|
|
/* first, attempt to open file in /device */
|
|
|
|
|
fd = open(path, O_RDONLY);
|
|
|
|
|
if (fd < 0) {
|
|
|
|
|
/* if it fails, strip the /device from dev and attempt again */
|
2018-12-20 15:22:08 +00:00
|
|
|
|
buf[std::max(0UL, strnlen(buf, 255) - 7)] = 0;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
|
|
|
|
|
fd = open(path, O_RDONLY);
|
|
|
|
|
if (fd < 0) {
|
|
|
|
|
NORM_ERR(
|
|
|
|
|
"can't open '%s': %s\nplease check your device or remove this "
|
|
|
|
|
"var from " PACKAGE_NAME,
|
|
|
|
|
path, strerror(errno));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
strncpy(devtype, path, 255);
|
|
|
|
|
|
|
|
|
|
if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0 ||
|
|
|
|
|
strcmp(type, "tempf") == 0) {
|
|
|
|
|
*divisor = 1;
|
|
|
|
|
} else {
|
|
|
|
|
*divisor = 0;
|
|
|
|
|
}
|
|
|
|
|
/* fan does not use *_div as a read divisor */
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (strcmp("fan", type) == 0) { return fd; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
/* test if *_div file exist, open it and use it as divisor */
|
|
|
|
|
if (strcmp(type, "tempf") == 0) {
|
|
|
|
|
snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
|
|
|
|
|
} else {
|
|
|
|
|
snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
divfd = open(path, O_RDONLY);
|
|
|
|
|
if (divfd > 0) {
|
|
|
|
|
/* read integer */
|
|
|
|
|
char divbuf[64];
|
|
|
|
|
int divn;
|
|
|
|
|
|
|
|
|
|
divn = read(divfd, divbuf, 63);
|
|
|
|
|
/* should read until n == 0 but I doubt that kernel will give these
|
|
|
|
|
* in multiple pieces. :) */
|
|
|
|
|
if (divn < 0) {
|
|
|
|
|
NORM_ERR("open_sysfs_sensor(): can't read from sysfs");
|
|
|
|
|
} else {
|
|
|
|
|
divbuf[divn] = '\0';
|
|
|
|
|
*divisor = atoi(divbuf);
|
|
|
|
|
}
|
|
|
|
|
close(divfd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fd;
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
static double get_sysfs_info(int *fd, int divisor, char *devtype, char *type) {
|
|
|
|
|
int val = 0;
|
|
|
|
|
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (*fd <= 0) { return 0; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
lseek(*fd, 0, SEEK_SET);
|
|
|
|
|
|
|
|
|
|
/* read integer */
|
|
|
|
|
{
|
|
|
|
|
char buf[64];
|
|
|
|
|
int n;
|
|
|
|
|
n = read(*fd, buf, 63);
|
|
|
|
|
/* should read until n == 0 but I doubt that kernel will give these
|
|
|
|
|
* in multiple pieces. :) */
|
|
|
|
|
if (n < 0) {
|
|
|
|
|
NORM_ERR("get_sysfs_info(): read from %s failed\n", devtype);
|
|
|
|
|
} else {
|
|
|
|
|
buf[n] = '\0';
|
|
|
|
|
val = atoi(buf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
close(*fd);
|
|
|
|
|
/* open file */
|
|
|
|
|
*fd = open(devtype, O_RDONLY);
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (*fd < 0) { NORM_ERR("can't open '%s': %s", devtype, strerror(errno)); }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
/* My dirty hack for computing CPU value
|
|
|
|
|
* Filedil, from forums.gentoo.org */
|
2018-05-12 23:26:31 +00:00
|
|
|
|
/* if (strstr(devtype, "temp1_input") != nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
return -15.096 + 1.4893 * (val / 1000.0);
|
|
|
|
|
} */
|
|
|
|
|
|
|
|
|
|
/* divide voltage and temperature by 1000 */
|
|
|
|
|
/* or if any other divisor is given, use that */
|
2018-08-04 19:01:28 +00:00
|
|
|
|
if (0 == (strcmp(type, "temp2"))) {
|
|
|
|
|
temp2 = "temp2";
|
|
|
|
|
} else {
|
|
|
|
|
temp2 = "empty";
|
|
|
|
|
}
|
2018-05-12 16:03:00 +00:00
|
|
|
|
if (strcmp(type, "tempf") == 0) {
|
|
|
|
|
if (divisor > 1) {
|
|
|
|
|
return ((val / divisor + 40) * 9.0 / 5) - 40;
|
|
|
|
|
} else if (divisor) {
|
|
|
|
|
return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
|
|
|
|
|
} else {
|
|
|
|
|
return ((val + 40) * 9.0 / 5) - 40;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (divisor > 1) {
|
|
|
|
|
return val / divisor;
|
|
|
|
|
} else if (divisor) {
|
|
|
|
|
return val / 1000.0;
|
|
|
|
|
} else {
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
}
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
#define HWMON_RESET() \
|
|
|
|
|
{ \
|
|
|
|
|
buf1[0] = 0; \
|
|
|
|
|
factor = 1.0; \
|
|
|
|
|
offset = 0.0; \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void parse_sysfs_sensor(struct text_object *obj, const char *arg,
|
|
|
|
|
const char *path, const char *type) {
|
|
|
|
|
char buf1[64], buf2[64];
|
|
|
|
|
float factor, offset;
|
|
|
|
|
int n, found = 0;
|
|
|
|
|
struct sysfs *sf;
|
2018-12-20 15:22:08 +00:00
|
|
|
|
memset(buf1, 0, 64);
|
|
|
|
|
memset(buf2, 0, 64);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
if (sscanf(arg, "%63s %d %f %f", buf2, &n, &factor, &offset) == 4)
|
|
|
|
|
found = 1;
|
|
|
|
|
else
|
|
|
|
|
HWMON_RESET();
|
|
|
|
|
if (!found &&
|
|
|
|
|
sscanf(arg, "%63s %63s %d %f %f", buf1, buf2, &n, &factor, &offset) == 5)
|
|
|
|
|
found = 1;
|
|
|
|
|
else if (!found)
|
|
|
|
|
HWMON_RESET();
|
|
|
|
|
if (!found && sscanf(arg, "%63s %63s %d", buf1, buf2, &n) == 3)
|
|
|
|
|
found = 1;
|
|
|
|
|
else if (!found)
|
|
|
|
|
HWMON_RESET();
|
|
|
|
|
if (!found && sscanf(arg, "%63s %d", buf2, &n) == 2)
|
|
|
|
|
found = 1;
|
|
|
|
|
else if (!found)
|
|
|
|
|
HWMON_RESET();
|
|
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
|
obj_be_plain_text(obj, "fail");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
DBGP("parsed %s args: '%s' '%s' %d %f %f\n", type, buf1, buf2, n, factor,
|
|
|
|
|
offset);
|
|
|
|
|
sf = (struct sysfs *)malloc(sizeof(struct sysfs));
|
|
|
|
|
memset(sf, 0, sizeof(struct sysfs));
|
|
|
|
|
sf->fd = open_sysfs_sensor(path, (*buf1) ? buf1 : 0, buf2, n, &sf->arg,
|
|
|
|
|
sf->devtype);
|
|
|
|
|
strncpy(sf->type, buf2, 63);
|
|
|
|
|
sf->factor = factor;
|
|
|
|
|
sf->offset = offset;
|
|
|
|
|
obj->data.opaque = sf;
|
2009-10-04 16:05:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
#define PARSER_GENERATOR(name, path) \
|
|
|
|
|
void parse_##name##_sensor(struct text_object *obj, const char *arg) { \
|
|
|
|
|
parse_sysfs_sensor(obj, arg, path, #name); \
|
|
|
|
|
}
|
2009-10-04 16:05:47 +00:00
|
|
|
|
|
|
|
|
|
PARSER_GENERATOR(i2c, "/sys/bus/i2c/devices/")
|
|
|
|
|
PARSER_GENERATOR(hwmon, "/sys/class/hwmon/")
|
|
|
|
|
PARSER_GENERATOR(platform, "/sys/bus/platform/devices/")
|
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
void print_sysfs_sensor(struct text_object *obj, char *p,
|
|
|
|
|
unsigned int p_max_size) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
double r;
|
|
|
|
|
struct sysfs *sf = (struct sysfs *)obj->data.opaque;
|
2009-10-04 16:22:08 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
if (!sf || sf->fd < 0) return;
|
2009-10-04 16:05:47 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
r = get_sysfs_info(&sf->fd, sf->arg, sf->devtype, sf->type);
|
2009-10-04 16:05:47 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
r = r * sf->factor + sf->offset;
|
2009-10-04 16:05:47 +00:00
|
|
|
|
|
2018-08-04 19:01:28 +00:00
|
|
|
|
if (0 == (strcmp(temp2, "temp2"))) {
|
|
|
|
|
temp_print(p, p_max_size, r, TEMP_CELSIUS, 0);
|
|
|
|
|
} else if (!strncmp(sf->type, "temp", 4)) {
|
|
|
|
|
temp_print(p, p_max_size, r, TEMP_CELSIUS, 1);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
} else if (r >= 100.0 || r == 0) {
|
|
|
|
|
snprintf(p, p_max_size, "%d", (int)r);
|
|
|
|
|
} else {
|
|
|
|
|
snprintf(p, p_max_size, "%.1f", r);
|
|
|
|
|
}
|
2009-10-04 16:05:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
void free_sysfs_sensor(struct text_object *obj) {
|
|
|
|
|
struct sysfs *sf = (struct sysfs *)obj->data.opaque;
|
2009-10-04 16:22:08 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
if (!sf) return;
|
2009-10-04 16:22:08 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
if (sf->fd >= 0) close(sf->fd);
|
|
|
|
|
free_and_zero(obj->data.opaque);
|
2009-10-04 16:22:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-05-22 02:49:22 +00:00
|
|
|
|
#define CPUFREQ_PREFIX "/sys/devices/system/cpu"
|
|
|
|
|
#define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
|
2005-08-26 02:16:35 +00:00
|
|
|
|
|
2005-11-13 03:33:26 +00:00
|
|
|
|
/* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
|
2008-03-29 03:45:36 +00:00
|
|
|
|
char get_freq(char *p_client_buffer, size_t client_buffer_size,
|
2018-05-12 16:03:00 +00:00
|
|
|
|
const char *p_format, int divisor, unsigned int cpu) {
|
|
|
|
|
FILE *f;
|
|
|
|
|
static int rep = 0;
|
|
|
|
|
char frequency[32];
|
|
|
|
|
char s[256];
|
|
|
|
|
double freq = 0;
|
|
|
|
|
|
|
|
|
|
if (!p_client_buffer || client_buffer_size <= 0 || !p_format ||
|
|
|
|
|
divisor <= 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!prefer_proc) {
|
|
|
|
|
char current_freq_file[128];
|
|
|
|
|
|
|
|
|
|
snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
|
|
|
|
|
CPUFREQ_POSTFIX);
|
|
|
|
|
f = fopen(current_freq_file, "r");
|
|
|
|
|
if (f) {
|
|
|
|
|
/* if there's a cpufreq /sys node, read the current frequency from
|
|
|
|
|
* this node and divide by 1000 to get Mhz. */
|
|
|
|
|
if (fgets(s, sizeof(s), f)) {
|
|
|
|
|
s[strlen(s) - 1] = '\0';
|
2018-05-12 23:26:31 +00:00
|
|
|
|
freq = strtod(s, nullptr);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
fclose(f);
|
|
|
|
|
snprintf(p_client_buffer, client_buffer_size, p_format,
|
|
|
|
|
(freq / 1000) / divisor);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// open the CPU information file
|
|
|
|
|
f = open_file("/proc/cpuinfo", &rep);
|
|
|
|
|
if (!f) {
|
|
|
|
|
perror(PACKAGE_NAME ": Failed to access '/proc/cpuinfo' at get_freq()");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// read the file
|
2018-05-12 23:26:31 +00:00
|
|
|
|
while (fgets(s, sizeof(s), f) != nullptr) {
|
2005-08-26 02:16:35 +00:00
|
|
|
|
#if defined(__i386) || defined(__x86_64)
|
2018-05-12 16:03:00 +00:00
|
|
|
|
// and search for the cpu mhz
|
|
|
|
|
if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
|
2006-03-06 00:33:32 +00:00
|
|
|
|
#else
|
|
|
|
|
#if defined(__alpha)
|
2018-05-12 16:03:00 +00:00
|
|
|
|
// different on alpha
|
|
|
|
|
if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
|
2005-08-26 02:16:35 +00:00
|
|
|
|
#else
|
2018-05-12 16:03:00 +00:00
|
|
|
|
// this is different on ppc for some reason
|
|
|
|
|
if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
|
|
|
|
|
#endif // defined(__alpha)
|
|
|
|
|
#endif // defined(__i386) || defined(__x86_64)
|
2006-03-06 00:33:32 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
// copy just the number
|
2018-05-13 22:59:39 +00:00
|
|
|
|
strncpy(frequency, strchr(s, ':') + 2, 32);
|
2006-03-06 00:33:32 +00:00
|
|
|
|
#if defined(__alpha)
|
2018-05-12 16:03:00 +00:00
|
|
|
|
// strip " est.\n"
|
|
|
|
|
frequency[strlen(frequency) - 6] = '\0';
|
|
|
|
|
// kernel reports in Hz
|
2018-05-12 23:26:31 +00:00
|
|
|
|
freq = strtod(frequency, nullptr) / 1000000;
|
2006-03-06 00:33:32 +00:00
|
|
|
|
#else
|
2018-05-12 16:03:00 +00:00
|
|
|
|
// strip \n
|
|
|
|
|
frequency[strlen(frequency) - 1] = '\0';
|
2018-05-12 23:26:31 +00:00
|
|
|
|
freq = strtod(frequency, nullptr);
|
2006-03-06 00:33:32 +00:00
|
|
|
|
#endif
|
2018-05-12 16:03:00 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (strncmp(s, "processor", 9) == 0) {
|
|
|
|
|
cpu--;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
|
snprintf(p_client_buffer, client_buffer_size, p_format,
|
|
|
|
|
(float)freq / divisor);
|
|
|
|
|
return 1;
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-05-24 00:23:47 +00:00
|
|
|
|
#define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
|
|
|
|
|
|
2008-02-20 20:30:45 +00:00
|
|
|
|
/* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
|
|
|
|
|
* like this:
|
2006-05-24 00:23:47 +00:00
|
|
|
|
# frequency voltage
|
|
|
|
|
1800000 1340
|
|
|
|
|
1600000 1292
|
|
|
|
|
1400000 1100
|
|
|
|
|
1200000 988
|
|
|
|
|
1000000 1116
|
|
|
|
|
800000 1004
|
|
|
|
|
600000 988
|
2008-02-20 20:30:45 +00:00
|
|
|
|
* Peter Tarjan (ptarjan@citromail.hu) */
|
2006-05-24 00:23:47 +00:00
|
|
|
|
|
2008-02-20 20:30:45 +00:00
|
|
|
|
/* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
|
2009-11-12 23:24:54 +00:00
|
|
|
|
static char get_voltage(char *p_client_buffer, size_t client_buffer_size,
|
2018-05-12 16:03:00 +00:00
|
|
|
|
const char *p_format, int divisor, unsigned int cpu) {
|
|
|
|
|
FILE *f;
|
|
|
|
|
char s[256];
|
|
|
|
|
int freq = 0;
|
|
|
|
|
int voltage = 0;
|
|
|
|
|
char current_freq_file[128];
|
|
|
|
|
int freq_comp = 0;
|
|
|
|
|
|
|
|
|
|
/* build the voltage file name */
|
|
|
|
|
cpu--;
|
|
|
|
|
snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
|
|
|
|
|
CPUFREQ_POSTFIX);
|
|
|
|
|
|
|
|
|
|
if (!p_client_buffer || client_buffer_size <= 0 || !p_format ||
|
|
|
|
|
divisor <= 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* read the current cpu frequency from the /sys node */
|
|
|
|
|
f = fopen(current_freq_file, "r");
|
|
|
|
|
if (f) {
|
|
|
|
|
if (fgets(s, sizeof(s), f)) {
|
|
|
|
|
s[strlen(s) - 1] = '\0';
|
2018-05-12 23:26:31 +00:00
|
|
|
|
freq = strtod(s, nullptr);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
fclose(f);
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(stderr, PACKAGE_NAME ": Failed to access '%s' at ",
|
|
|
|
|
current_freq_file);
|
|
|
|
|
perror("get_voltage()");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
|
|
|
|
|
CPUFREQ_VOLTAGE);
|
|
|
|
|
|
|
|
|
|
/* use the current cpu frequency to find the corresponding voltage */
|
|
|
|
|
f = fopen(current_freq_file, "r");
|
|
|
|
|
|
|
|
|
|
if (f) {
|
|
|
|
|
while (!feof(f)) {
|
|
|
|
|
char line[256];
|
|
|
|
|
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (fgets(line, 255, f) == nullptr) { break; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
sscanf(line, "%d %d", &freq_comp, &voltage);
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (freq_comp == freq) { break; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
fclose(f);
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(stderr, PACKAGE_NAME ": Failed to access '%s' at ",
|
|
|
|
|
current_freq_file);
|
|
|
|
|
perror("get_voltage()");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
snprintf(p_client_buffer, client_buffer_size, p_format,
|
|
|
|
|
(float)voltage / divisor);
|
|
|
|
|
return 1;
|
2006-05-24 00:23:47 +00:00
|
|
|
|
}
|
2005-07-24 23:08:51 +00:00
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
void print_voltage_mv(struct text_object *obj, char *p,
|
|
|
|
|
unsigned int p_max_size) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
static int ok = 1;
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (ok) { ok = get_voltage(p, p_max_size, "%.0f", 1, obj->data.i); }
|
2009-11-12 23:24:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
void print_voltage_v(struct text_object *obj, char *p,
|
|
|
|
|
unsigned int p_max_size) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
static int ok = 1;
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (ok) { ok = get_voltage(p, p_max_size, "%'.3f", 1000, obj->data.i); }
|
2009-11-12 23:24:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2005-07-20 00:30:40 +00:00
|
|
|
|
#define ACPI_FAN_DIR "/proc/acpi/fan/"
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size) {
|
|
|
|
|
static int rep = 0;
|
|
|
|
|
char buf[256];
|
|
|
|
|
char buf2[256];
|
|
|
|
|
FILE *fp;
|
|
|
|
|
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (!p_client_buffer || client_buffer_size <= 0) { return; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
/* yeah, slow... :/ */
|
|
|
|
|
if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
|
2018-08-02 15:15:16 +00:00
|
|
|
|
snprintf(p_client_buffer, client_buffer_size, "%s", "no fans?");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
|
|
|
|
|
|
|
|
|
|
fp = open_file(buf2, &rep);
|
|
|
|
|
if (!fp) {
|
2018-08-02 15:15:16 +00:00
|
|
|
|
snprintf(p_client_buffer, client_buffer_size, "%s",
|
2018-05-12 16:03:00 +00:00
|
|
|
|
"can't open fan's state file");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
|
if (fscanf(fp, "%*s %99s", buf) <= 0) perror("fscanf()");
|
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
|
|
snprintf(p_client_buffer, client_buffer_size, "%s", buf);
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-01-24 14:34:03 +00:00
|
|
|
|
#define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply"
|
2005-07-20 00:30:40 +00:00
|
|
|
|
#define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
|
2008-06-28 19:17:49 +00:00
|
|
|
|
/* Linux 2.6.25 onwards ac adapter info is in
|
|
|
|
|
/sys/class/power_supply/AC/
|
|
|
|
|
On my system I get the following.
|
|
|
|
|
/sys/class/power_supply/AC/uevent:
|
|
|
|
|
PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
|
|
|
|
|
PHYSDEVBUS=acpi
|
|
|
|
|
PHYSDEVDRIVER=ac
|
|
|
|
|
POWER_SUPPLY_NAME=AC
|
|
|
|
|
POWER_SUPPLY_TYPE=Mains
|
|
|
|
|
POWER_SUPPLY_ONLINE=1
|
2010-01-24 14:34:03 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
Update: it seems the folder name is hardware-dependent. We add an aditional
|
|
|
|
|
adapter argument, specifying the folder name.
|
2010-04-12 10:33:24 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
Update: on some systems it's /sys/class/power_supply/ADP1 instead of
|
|
|
|
|
/sys/class/power_supply/AC
|
2008-06-28 19:17:49 +00:00
|
|
|
|
*/
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size,
|
|
|
|
|
const char *adapter) {
|
|
|
|
|
static int rep = 0;
|
|
|
|
|
|
|
|
|
|
char buf[256];
|
|
|
|
|
char buf2[256];
|
|
|
|
|
struct stat sb;
|
|
|
|
|
FILE *fp;
|
|
|
|
|
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (!p_client_buffer || client_buffer_size <= 0) { return; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
if (adapter)
|
|
|
|
|
snprintf(buf2, sizeof(buf2), "%s/%s/uevent", SYSFS_AC_ADAPTER_DIR, adapter);
|
|
|
|
|
else {
|
|
|
|
|
snprintf(buf2, sizeof(buf2), "%s/AC/uevent", SYSFS_AC_ADAPTER_DIR);
|
|
|
|
|
if (stat(buf2, &sb) == -1)
|
|
|
|
|
snprintf(buf2, sizeof(buf2), "%s/ADP1/uevent", SYSFS_AC_ADAPTER_DIR);
|
|
|
|
|
}
|
|
|
|
|
if (stat(buf2, &sb) == 0)
|
|
|
|
|
fp = open_file(buf2, &rep);
|
|
|
|
|
else
|
|
|
|
|
fp = 0;
|
|
|
|
|
if (fp) {
|
|
|
|
|
/* sysfs processing */
|
|
|
|
|
while (!feof(fp)) {
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (fgets(buf, sizeof(buf), fp) == nullptr) break;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
|
|
|
|
|
int online = 0;
|
|
|
|
|
sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
|
|
|
|
|
snprintf(p_client_buffer, client_buffer_size, "%s-line",
|
|
|
|
|
(online ? "on" : "off"));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fclose(fp);
|
|
|
|
|
} else {
|
|
|
|
|
/* yeah, slow... :/ */
|
|
|
|
|
if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
|
2018-08-02 15:15:16 +00:00
|
|
|
|
snprintf(p_client_buffer, client_buffer_size, "%s", "no ac_adapters?");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
|
|
|
|
|
|
|
|
|
|
fp = open_file(buf2, &rep);
|
|
|
|
|
if (!fp) {
|
2018-08-02 15:15:16 +00:00
|
|
|
|
snprintf(p_client_buffer, client_buffer_size, "%s",
|
2018-05-12 16:03:00 +00:00
|
|
|
|
"No ac adapter found.... where is it?");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
|
if (fscanf(fp, "%*s %99s", buf) <= 0) perror("fscanf()");
|
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
|
|
snprintf(p_client_buffer, client_buffer_size, "%s", buf);
|
|
|
|
|
}
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
/proc/acpi/thermal_zone/THRM/cooling_mode
|
|
|
|
|
cooling mode: active
|
|
|
|
|
/proc/acpi/thermal_zone/THRM/polling_frequency
|
|
|
|
|
<polling disabled>
|
|
|
|
|
/proc/acpi/thermal_zone/THRM/state
|
|
|
|
|
state: ok
|
|
|
|
|
/proc/acpi/thermal_zone/THRM/temperature
|
|
|
|
|
temperature: 45 C
|
|
|
|
|
/proc/acpi/thermal_zone/THRM/trip_points
|
|
|
|
|
critical (S5): 73 C
|
|
|
|
|
passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
|
|
|
|
|
*/
|
|
|
|
|
|
2010-11-10 17:22:22 +00:00
|
|
|
|
#define ACPI_THERMAL_ZONE_DEFAULT "thermal_zone0"
|
|
|
|
|
#define ACPI_THERMAL_FORMAT "/sys/class/thermal/%s/temp"
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int open_acpi_temperature(const char *name) {
|
|
|
|
|
char path[256];
|
|
|
|
|
int fd;
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (name == nullptr || strcmp(name, "*") == 0) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
snprintf(path, 255, ACPI_THERMAL_FORMAT, ACPI_THERMAL_ZONE_DEFAULT);
|
|
|
|
|
} else {
|
|
|
|
|
snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
|
|
|
|
|
}
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
fd = open(path, O_RDONLY);
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (fd < 0) { NORM_ERR("can't open '%s': %s", path, strerror(errno)); }
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
return fd;
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static double last_acpi_temp;
|
|
|
|
|
static double last_acpi_temp_time;
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
// the maximum length of the string inside a ACPI_THERMAL_FORMAT file including
|
|
|
|
|
// the ending 0
|
2010-11-10 17:22:22 +00:00
|
|
|
|
#define MAXTHERMZONELEN 6
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
double get_acpi_temperature(int fd) {
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (fd <= 0) { return 0; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
/* don't update acpi temperature too often */
|
|
|
|
|
if (current_update_time - last_acpi_temp_time < 11.32) {
|
|
|
|
|
return last_acpi_temp;
|
|
|
|
|
}
|
|
|
|
|
last_acpi_temp_time = current_update_time;
|
|
|
|
|
|
|
|
|
|
/* seek to beginning */
|
|
|
|
|
lseek(fd, 0, SEEK_SET);
|
|
|
|
|
|
|
|
|
|
/* read */
|
|
|
|
|
{
|
|
|
|
|
char buf[MAXTHERMZONELEN];
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
n = read(fd, buf, MAXTHERMZONELEN - 1);
|
|
|
|
|
if (n < 0) {
|
|
|
|
|
NORM_ERR("can't read fd %d: %s", fd, strerror(errno));
|
|
|
|
|
} else {
|
|
|
|
|
buf[n] = '\0';
|
|
|
|
|
sscanf(buf, "%lf", &last_acpi_temp);
|
|
|
|
|
last_acpi_temp /= 1000;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return last_acpi_temp;
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2007-08-05 04:47:21 +00:00
|
|
|
|
hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
|
2005-07-20 00:30:40 +00:00
|
|
|
|
present: yes
|
|
|
|
|
design capacity: 4400 mAh
|
|
|
|
|
last full capacity: 4064 mAh
|
|
|
|
|
battery technology: rechargeable
|
|
|
|
|
design voltage: 14800 mV
|
|
|
|
|
design capacity warning: 300 mAh
|
|
|
|
|
design capacity low: 200 mAh
|
|
|
|
|
capacity granularity 1: 32 mAh
|
|
|
|
|
capacity granularity 2: 32 mAh
|
|
|
|
|
model number: 02KT
|
|
|
|
|
serial number: 16922
|
|
|
|
|
battery type: LION
|
|
|
|
|
OEM info: SANYO
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
|
|
|
|
|
present: yes
|
|
|
|
|
capacity state: ok
|
|
|
|
|
charging state: unknown
|
|
|
|
|
present rate: 0 mA
|
|
|
|
|
remaining capacity: 4064 mAh
|
|
|
|
|
present voltage: 16608 mV
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
2008-02-20 20:30:45 +00:00
|
|
|
|
2213<@jupet<EFBFBD>kellari<EFBFBD><EFBFBD>> jupet@lagi-unstable:~$ cat /proc/apm
|
|
|
|
|
2213<@jupet<EFBFBD>kellari<EFBFBD><EFBFBD>> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
|
|
|
|
|
2213<@jupet<EFBFBD>kellari<EFBFBD><EFBFBD>> (-1 ollee ei akkua kiinni, koska akku on p<EFBFBD>yd<EFBFBD>ll<EFBFBD>)
|
|
|
|
|
2214<@jupet<EFBFBD>kellari<EFBFBD><EFBFBD>> jupet@lagi-unstable:~$ cat /proc/apm
|
|
|
|
|
2214<@jupet<EFBFBD>kellari<EFBFBD><EFBFBD>> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2008-02-20 20:30:45 +00:00
|
|
|
|
2238<@jupet<EFBFBD>kellari<EFBFBD><EFBFBD>> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
|
|
|
|
|
2239<@jupet<EFBFBD>kellari<EFBFBD><EFBFBD>> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
2240<@jupet<EFBFBD>kellari<EFBFBD><EFBFBD>> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja
|
|
|
|
|
monitori p<EFBFBD><EFBFBD>ll<EFBFBD> 2241<@jupet<EFBFBD>kellari<EFBFBD><EFBFBD>> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ?
|
|
|
|
|
monitori p<EFBFBD><EFBFBD>ll<EFBFBD> mutta ilman verkkovirtaa
|
2005-07-20 00:30:40 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2008-03-17 22:27:20 +00:00
|
|
|
|
/* Kapil Hari Paranjape <kapil@imsc.res.in>
|
|
|
|
|
Linux 2.6.24 onwards battery info is in
|
|
|
|
|
/sys/class/power_supply/BAT0/
|
|
|
|
|
On my system I get the following.
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/sys/class/power_supply/BAT0/uevent:
|
|
|
|
|
PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
|
|
|
|
|
PHYSDEVBUS=acpi
|
|
|
|
|
PHYSDEVDRIVER=battery
|
|
|
|
|
POWER_SUPPLY_NAME=BAT0
|
|
|
|
|
POWER_SUPPLY_TYPE=Battery
|
|
|
|
|
POWER_SUPPLY_STATUS=Discharging
|
|
|
|
|
POWER_SUPPLY_PRESENT=1
|
|
|
|
|
POWER_SUPPLY_TECHNOLOGY=Li-ion
|
|
|
|
|
POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
|
|
|
|
|
POWER_SUPPLY_VOLTAGE_NOW=10780000
|
|
|
|
|
POWER_SUPPLY_CURRENT_NOW=13970000
|
|
|
|
|
POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
|
|
|
|
|
POWER_SUPPLY_ENERGY_FULL=27370000
|
|
|
|
|
POWER_SUPPLY_ENERGY_NOW=11810000
|
|
|
|
|
POWER_SUPPLY_MODEL_NAME=IBM-92P1060
|
|
|
|
|
POWER_SUPPLY_MANUFACTURER=Panasonic
|
2008-03-17 22:27:20 +00:00
|
|
|
|
On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
|
|
|
|
|
*/
|
|
|
|
|
|
2010-12-04 20:45:34 +00:00
|
|
|
|
/* Tiago Marques Vale <tiagomarquesvale@gmail.com>
|
|
|
|
|
Regarding the comment above, since kernel 2.6.36.1 I have
|
|
|
|
|
POWER_SUPPLY_POWER_NOW instead of POWER_SUPPLY_CURRENT_NOW
|
|
|
|
|
See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=532000
|
|
|
|
|
*/
|
|
|
|
|
|
2008-03-17 22:27:20 +00:00
|
|
|
|
#define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
|
2005-07-20 00:30:40 +00:00
|
|
|
|
#define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
|
|
|
|
|
#define APM_PATH "/proc/apm"
|
2007-08-05 04:47:21 +00:00
|
|
|
|
#define MAX_BATTERY_COUNT 4
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2018-05-12 23:26:31 +00:00
|
|
|
|
static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = {nullptr, NULL, NULL, NULL};
|
|
|
|
|
static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = {nullptr, NULL, NULL, NULL};
|
|
|
|
|
static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = {nullptr, NULL, NULL, NULL};
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2007-08-05 04:47:21 +00:00
|
|
|
|
static int batteries_initialized = 0;
|
|
|
|
|
static char batteries[MAX_BATTERY_COUNT][32];
|
|
|
|
|
|
|
|
|
|
static int acpi_last_full[MAX_BATTERY_COUNT];
|
|
|
|
|
static int acpi_design_capacity[MAX_BATTERY_COUNT];
|
|
|
|
|
|
2008-02-20 20:30:45 +00:00
|
|
|
|
/* e.g. "charging 75%" */
|
|
|
|
|
static char last_battery_str[MAX_BATTERY_COUNT][64];
|
|
|
|
|
/* e.g. "3h 15m" */
|
|
|
|
|
static char last_battery_time_str[MAX_BATTERY_COUNT][64];
|
2007-08-05 04:47:21 +00:00
|
|
|
|
|
|
|
|
|
static double last_battery_time[MAX_BATTERY_COUNT];
|
|
|
|
|
|
|
|
|
|
static int last_battery_perct[MAX_BATTERY_COUNT];
|
|
|
|
|
static double last_battery_perct_time[MAX_BATTERY_COUNT];
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
void init_batteries(void) {
|
|
|
|
|
int idx;
|
2008-02-20 20:30:45 +00:00
|
|
|
|
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (batteries_initialized) { return; }
|
2009-05-05 23:36:12 +00:00
|
|
|
|
#ifdef HAVE_OPENMP
|
2018-05-12 16:03:00 +00:00
|
|
|
|
#pragma omp parallel for schedule(dynamic, 10)
|
2009-05-05 23:36:12 +00:00
|
|
|
|
#endif /* HAVE_OPENMP */
|
2018-05-13 13:58:03 +00:00
|
|
|
|
for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) { batteries[idx][0] = '\0'; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
batteries_initialized = 1;
|
2007-08-05 04:47:21 +00:00
|
|
|
|
}
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int get_battery_idx(const char *bat) {
|
|
|
|
|
int idx;
|
2008-02-20 20:30:45 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) { break; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/* if not found, enter a new entry */
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (!strlen(batteries[idx])) { snprintf(batteries[idx], 31, "%s", bat); }
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
return idx;
|
2007-08-05 04:47:21 +00:00
|
|
|
|
}
|
2007-04-06 06:11:53 +00:00
|
|
|
|
|
2008-04-10 22:45:45 +00:00
|
|
|
|
void set_return_value(char *buffer, unsigned int n, int item, int idx);
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
void get_battery_stuff(char *buffer, unsigned int n, const char *bat,
|
|
|
|
|
int item) {
|
|
|
|
|
static int idx, rep = 0, rep1 = 0, rep2 = 0;
|
|
|
|
|
char acpi_path[128];
|
|
|
|
|
char sysfs_path[128];
|
|
|
|
|
|
|
|
|
|
snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
|
|
|
|
|
snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
|
|
|
|
|
|
|
|
|
|
init_batteries();
|
|
|
|
|
|
|
|
|
|
idx = get_battery_idx(bat);
|
|
|
|
|
|
|
|
|
|
/* don't update battery too often */
|
|
|
|
|
if (current_update_time - last_battery_time[idx] < 29.5) {
|
|
|
|
|
set_return_value(buffer, n, item, idx);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
last_battery_time[idx] = current_update_time;
|
|
|
|
|
|
|
|
|
|
memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
|
|
|
|
|
memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
|
|
|
|
|
|
|
|
|
|
/* first try SYSFS if that fails try ACPI */
|
|
|
|
|
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (sysfs_bat_fp[idx] == nullptr && acpi_bat_fp[idx] == NULL &&
|
|
|
|
|
apm_bat_fp[idx] == nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (sysfs_bat_fp[idx] == nullptr && acpi_bat_fp[idx] == NULL &&
|
|
|
|
|
apm_bat_fp[idx] == nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (sysfs_bat_fp[idx] != nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/* SYSFS */
|
|
|
|
|
int present_rate = -1;
|
|
|
|
|
int remaining_capacity = -1;
|
|
|
|
|
char charging_state[64];
|
|
|
|
|
char present[4];
|
|
|
|
|
|
2018-05-13 22:46:09 +00:00
|
|
|
|
strncpy(charging_state, "unknown", 64);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
while (!feof(sysfs_bat_fp[idx])) {
|
|
|
|
|
char buf[256];
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (fgets(buf, 256, sysfs_bat_fp[idx]) == nullptr) break;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
/* let's just hope units are ok */
|
|
|
|
|
if (strncmp(buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
|
2018-05-13 22:46:09 +00:00
|
|
|
|
strncpy(present, "yes", 4);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
else if (strncmp(buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
|
2018-05-13 22:46:09 +00:00
|
|
|
|
strncpy(present, "no", 4);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
else if (strncmp(buf, "POWER_SUPPLY_STATUS=", 20) == 0)
|
|
|
|
|
sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
|
|
|
|
|
/* present_rate is not the same as the current flowing now but it
|
|
|
|
|
* is the same value which was used in the past. so we continue the
|
|
|
|
|
* tradition! */
|
|
|
|
|
else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
|
|
|
|
|
sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
|
|
|
|
|
else if (strncmp(buf, "POWER_SUPPLY_POWER_NOW=", 23) == 0)
|
|
|
|
|
sscanf(buf, "POWER_SUPPLY_POWER_NOW=%d", &present_rate);
|
|
|
|
|
else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
|
|
|
|
|
sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
|
|
|
|
|
else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
|
|
|
|
|
sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
|
|
|
|
|
else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
|
|
|
|
|
sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
|
|
|
|
|
else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
|
|
|
|
|
sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fclose(sysfs_bat_fp[idx]);
|
2018-05-12 23:26:31 +00:00
|
|
|
|
sysfs_bat_fp[idx] = nullptr;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
/* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
|
|
|
|
|
if (remaining_capacity > acpi_last_full[idx])
|
|
|
|
|
acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
|
|
|
|
|
|
|
|
|
|
/* not present */
|
|
|
|
|
if (strcmp(present, "No") == 0) {
|
|
|
|
|
strncpy(last_battery_str[idx], "not present", 64);
|
|
|
|
|
}
|
|
|
|
|
/* charging */
|
|
|
|
|
else if (strcmp(charging_state, "Charging") == 0) {
|
|
|
|
|
if (acpi_last_full[idx] != 0 && present_rate > 0) {
|
|
|
|
|
/* e.g. charging 75% */
|
|
|
|
|
snprintf(
|
|
|
|
|
last_battery_str[idx], sizeof(last_battery_str[idx]) - 1,
|
|
|
|
|
"charging %i%%",
|
|
|
|
|
(int)(((float)remaining_capacity / acpi_last_full[idx]) * 100));
|
|
|
|
|
/* e.g. 2h 37m */
|
|
|
|
|
format_seconds(
|
|
|
|
|
last_battery_time_str[idx], sizeof(last_battery_time_str[idx]) - 1,
|
|
|
|
|
(long)(((float)(acpi_last_full[idx] - remaining_capacity) /
|
|
|
|
|
present_rate) *
|
|
|
|
|
3600));
|
|
|
|
|
} else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
|
|
|
|
|
snprintf(
|
|
|
|
|
last_battery_str[idx], sizeof(last_battery_str[idx]) - 1,
|
|
|
|
|
"charging %d%%",
|
|
|
|
|
(int)(((float)remaining_capacity / acpi_last_full[idx]) * 100));
|
|
|
|
|
snprintf(last_battery_time_str[idx],
|
2018-08-02 15:15:16 +00:00
|
|
|
|
sizeof(last_battery_time_str[idx]) - 1, "%s", "unknown");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
} else {
|
|
|
|
|
strncpy(last_battery_str[idx], "charging",
|
|
|
|
|
sizeof(last_battery_str[idx]) - 1);
|
|
|
|
|
snprintf(last_battery_time_str[idx],
|
2018-08-02 15:15:16 +00:00
|
|
|
|
sizeof(last_battery_time_str[idx]) - 1, "%s", "unknown");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* discharging */
|
|
|
|
|
else if (strncmp(charging_state, "Discharging", 64) == 0) {
|
|
|
|
|
if (present_rate > 0) {
|
|
|
|
|
/* e.g. discharging 35% */
|
|
|
|
|
snprintf(
|
|
|
|
|
last_battery_str[idx], sizeof(last_battery_str[idx]) - 1,
|
|
|
|
|
"discharging %i%%",
|
|
|
|
|
(int)(((float)remaining_capacity / acpi_last_full[idx]) * 100));
|
|
|
|
|
/* e.g. 1h 12m */
|
|
|
|
|
format_seconds(
|
|
|
|
|
last_battery_time_str[idx], sizeof(last_battery_time_str[idx]) - 1,
|
|
|
|
|
(long)(((float)remaining_capacity / present_rate) * 3600));
|
|
|
|
|
} else if (present_rate == 0) { /* Thanks to Nexox for this one */
|
|
|
|
|
snprintf(last_battery_str[idx], sizeof(last_battery_str[idx]) - 1,
|
|
|
|
|
"full");
|
|
|
|
|
snprintf(last_battery_time_str[idx],
|
2018-08-02 15:15:16 +00:00
|
|
|
|
sizeof(last_battery_time_str[idx]) - 1, "%s", "unknown");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
} else {
|
|
|
|
|
snprintf(
|
|
|
|
|
last_battery_str[idx], sizeof(last_battery_str[idx]) - 1,
|
|
|
|
|
"discharging %d%%",
|
|
|
|
|
(int)(((float)remaining_capacity / acpi_last_full[idx]) * 100));
|
|
|
|
|
snprintf(last_battery_time_str[idx],
|
2018-08-02 15:15:16 +00:00
|
|
|
|
sizeof(last_battery_time_str[idx]) - 1, "%s", "unknown");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* charged */
|
|
|
|
|
/* thanks to Lukas Zapletal <lzap@seznam.cz> */
|
|
|
|
|
else if (strncmp(charging_state, "Charged", 64) == 0 ||
|
|
|
|
|
strncmp(charging_state, "Full", 64) == 0) {
|
|
|
|
|
/* Below happens with the second battery on my X40,
|
|
|
|
|
* when the second one is empty and the first one
|
|
|
|
|
* being charged. */
|
|
|
|
|
if (remaining_capacity == 0)
|
2018-05-13 22:46:09 +00:00
|
|
|
|
strncpy(last_battery_str[idx], "empty", 64);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
else
|
2018-05-13 22:46:09 +00:00
|
|
|
|
strncpy(last_battery_str[idx], "charged", 64);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
/* unknown, probably full / AC */
|
|
|
|
|
else {
|
|
|
|
|
if (acpi_last_full[idx] != 0 && remaining_capacity != acpi_last_full[idx])
|
|
|
|
|
snprintf(
|
|
|
|
|
last_battery_str[idx], 64, "unknown %d%%",
|
|
|
|
|
(int)(((float)remaining_capacity / acpi_last_full[idx]) * 100));
|
|
|
|
|
else
|
|
|
|
|
strncpy(last_battery_str[idx], "not present", 64);
|
|
|
|
|
}
|
2018-05-12 23:26:31 +00:00
|
|
|
|
} else if (acpi_bat_fp[idx] != nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/* ACPI */
|
|
|
|
|
int present_rate = -1;
|
|
|
|
|
int remaining_capacity = -1;
|
|
|
|
|
char charging_state[64];
|
|
|
|
|
char present[5];
|
|
|
|
|
|
|
|
|
|
/* read last full capacity if it's zero */
|
|
|
|
|
if (acpi_last_full[idx] == 0) {
|
|
|
|
|
static int rep3 = 0;
|
|
|
|
|
char path[128];
|
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
|
|
snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
|
|
|
|
|
fp = open_file(path, &rep3);
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (fp != nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
while (!feof(fp)) {
|
|
|
|
|
char b[256];
|
|
|
|
|
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (fgets(b, 256, fp) == nullptr) { break; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
if (sscanf(b, "last full capacity: %d", &acpi_last_full[idx]) != 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fclose(fp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fseek(acpi_bat_fp[idx], 0, SEEK_SET);
|
|
|
|
|
|
2018-05-13 22:46:09 +00:00
|
|
|
|
strncpy(charging_state, "unknown", 8);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
while (!feof(acpi_bat_fp[idx])) {
|
|
|
|
|
char buf[256];
|
|
|
|
|
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (fgets(buf, 256, acpi_bat_fp[idx]) == nullptr) { break; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
/* let's just hope units are ok */
|
|
|
|
|
if (strncmp(buf, "present:", 8) == 0) {
|
|
|
|
|
sscanf(buf, "present: %4s", present);
|
|
|
|
|
} else if (strncmp(buf, "charging state:", 15) == 0) {
|
|
|
|
|
sscanf(buf, "charging state: %63s", charging_state);
|
|
|
|
|
} else if (strncmp(buf, "present rate:", 13) == 0) {
|
|
|
|
|
sscanf(buf, "present rate: %d", &present_rate);
|
|
|
|
|
} else if (strncmp(buf, "remaining capacity:", 19) == 0) {
|
|
|
|
|
sscanf(buf, "remaining capacity: %d", &remaining_capacity);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
|
|
|
|
|
if (remaining_capacity > acpi_last_full[idx]) {
|
|
|
|
|
/* normalize to 100% */
|
|
|
|
|
acpi_last_full[idx] = remaining_capacity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* not present */
|
|
|
|
|
if (strcmp(present, "no") == 0) {
|
|
|
|
|
strncpy(last_battery_str[idx], "not present", 64);
|
|
|
|
|
/* charging */
|
|
|
|
|
} else if (strcmp(charging_state, "charging") == 0) {
|
|
|
|
|
if (acpi_last_full[idx] != 0 && present_rate > 0) {
|
|
|
|
|
/* e.g. charging 75% */
|
|
|
|
|
snprintf(last_battery_str[idx], sizeof(last_battery_str[idx]) - 1,
|
|
|
|
|
"charging %i%%",
|
|
|
|
|
(int)((remaining_capacity * 100) / acpi_last_full[idx]));
|
|
|
|
|
/* e.g. 2h 37m */
|
|
|
|
|
format_seconds(
|
|
|
|
|
last_battery_time_str[idx], sizeof(last_battery_time_str[idx]) - 1,
|
|
|
|
|
(long)(((acpi_last_full[idx] - remaining_capacity) * 3600) /
|
|
|
|
|
present_rate));
|
|
|
|
|
} else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
|
|
|
|
|
snprintf(last_battery_str[idx], sizeof(last_battery_str[idx]) - 1,
|
|
|
|
|
"charging %d%%",
|
|
|
|
|
(int)((remaining_capacity * 100) / acpi_last_full[idx]));
|
|
|
|
|
snprintf(last_battery_time_str[idx],
|
2018-08-02 15:15:16 +00:00
|
|
|
|
sizeof(last_battery_time_str[idx]) - 1, "%s", "unknown");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
} else {
|
|
|
|
|
strncpy(last_battery_str[idx], "charging",
|
|
|
|
|
sizeof(last_battery_str[idx]) - 1);
|
|
|
|
|
snprintf(last_battery_time_str[idx],
|
2018-08-02 15:15:16 +00:00
|
|
|
|
sizeof(last_battery_time_str[idx]) - 1, "%s", "unknown");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
/* discharging */
|
|
|
|
|
} else if (strncmp(charging_state, "discharging", 64) == 0) {
|
|
|
|
|
if (present_rate > 0) {
|
|
|
|
|
/* e.g. discharging 35% */
|
|
|
|
|
snprintf(last_battery_str[idx], sizeof(last_battery_str[idx]) - 1,
|
|
|
|
|
"discharging %i%%",
|
|
|
|
|
(int)((remaining_capacity * 100) / acpi_last_full[idx]));
|
|
|
|
|
/* e.g. 1h 12m */
|
|
|
|
|
format_seconds(last_battery_time_str[idx],
|
|
|
|
|
sizeof(last_battery_time_str[idx]) - 1,
|
|
|
|
|
(long)((remaining_capacity * 3600) / present_rate));
|
|
|
|
|
} else if (present_rate == 0) { /* Thanks to Nexox for this one */
|
|
|
|
|
snprintf(last_battery_str[idx], sizeof(last_battery_str[idx]) - 1,
|
|
|
|
|
"charged");
|
|
|
|
|
snprintf(last_battery_time_str[idx],
|
2018-08-02 15:15:16 +00:00
|
|
|
|
sizeof(last_battery_time_str[idx]) - 1, "%s", "unknown");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
} else {
|
|
|
|
|
snprintf(last_battery_str[idx], sizeof(last_battery_str[idx]) - 1,
|
|
|
|
|
"discharging %d%%",
|
|
|
|
|
(int)((remaining_capacity * 100) / acpi_last_full[idx]));
|
|
|
|
|
snprintf(last_battery_time_str[idx],
|
2018-08-02 15:15:16 +00:00
|
|
|
|
sizeof(last_battery_time_str[idx]) - 1, "%s", "unknown");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
/* charged */
|
|
|
|
|
} else if (strncmp(charging_state, "charged", 64) == 0) {
|
|
|
|
|
/* thanks to Lukas Zapletal <lzap@seznam.cz> */
|
|
|
|
|
/* Below happens with the second battery on my X40,
|
|
|
|
|
* when the second one is empty and the first one being charged. */
|
|
|
|
|
if (remaining_capacity == 0) {
|
2018-05-13 22:59:39 +00:00
|
|
|
|
strncpy(last_battery_str[idx], "empty", 6);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
} else {
|
2018-05-13 22:59:39 +00:00
|
|
|
|
strncpy(last_battery_str[idx], "charged", 8);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
/* unknown, probably full / AC */
|
|
|
|
|
} else {
|
|
|
|
|
if (strncmp(charging_state, "Full", 64) == 0) {
|
|
|
|
|
strncpy(last_battery_str[idx], "charged", 64);
|
|
|
|
|
} else if (acpi_last_full[idx] != 0 &&
|
|
|
|
|
remaining_capacity != acpi_last_full[idx]) {
|
|
|
|
|
snprintf(last_battery_str[idx], 64, "unknown %d%%",
|
|
|
|
|
(int)((remaining_capacity * 100) / acpi_last_full[idx]));
|
|
|
|
|
} else {
|
|
|
|
|
strncpy(last_battery_str[idx], "not present", 64);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fclose(acpi_bat_fp[idx]);
|
2018-05-12 23:26:31 +00:00
|
|
|
|
acpi_bat_fp[idx] = nullptr;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
} else {
|
|
|
|
|
/* APM */
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (apm_bat_fp[idx] == nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (apm_bat_fp[idx] != nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
unsigned int ac, status, flag;
|
|
|
|
|
int life;
|
|
|
|
|
|
|
|
|
|
if (fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%", &ac,
|
|
|
|
|
&status, &flag, &life) <= 0)
|
|
|
|
|
goto read_bat_fp_end;
|
|
|
|
|
|
|
|
|
|
if (life == -1) {
|
|
|
|
|
/* could check now that there is ac */
|
2018-08-02 15:15:16 +00:00
|
|
|
|
snprintf(last_battery_str[idx], 64, "%s", "not present");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
/* could check that status == 3 here? */
|
|
|
|
|
} else if (ac && life != 100) {
|
|
|
|
|
snprintf(last_battery_str[idx], 64, "charging %d%%", life);
|
|
|
|
|
} else {
|
|
|
|
|
snprintf(last_battery_str[idx], 64, "%d%%", life);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
read_bat_fp_end:
|
|
|
|
|
/* it seemed to buffer it so file must be closed (or could use
|
|
|
|
|
* syscalls directly but I don't feel like coding it now) */
|
|
|
|
|
fclose(apm_bat_fp[idx]);
|
2018-05-12 23:26:31 +00:00
|
|
|
|
apm_bat_fp[idx] = nullptr;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
set_return_value(buffer, n, item, idx);
|
2008-04-10 22:45:45 +00:00
|
|
|
|
}
|
2005-07-20 00:30:40 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
void set_return_value(char *buffer, unsigned int n, int item, int idx) {
|
|
|
|
|
switch (item) {
|
|
|
|
|
case BATTERY_STATUS:
|
|
|
|
|
snprintf(buffer, n, "%s", last_battery_str[idx]);
|
|
|
|
|
break;
|
|
|
|
|
case BATTERY_TIME:
|
|
|
|
|
snprintf(buffer, n, "%s", last_battery_time_str[idx]);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2005-07-20 00:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
void get_battery_short_status(char *buffer, unsigned int n, const char *bat) {
|
|
|
|
|
get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
|
|
|
|
|
if (0 == strncmp("charging", buffer, 8)) {
|
|
|
|
|
buffer[0] = 'C';
|
|
|
|
|
memmove(buffer + 1, buffer + 8, n - 8);
|
|
|
|
|
} else if (0 == strncmp("discharging", buffer, 11)) {
|
|
|
|
|
buffer[0] = 'D';
|
|
|
|
|
memmove(buffer + 1, buffer + 11, n - 11);
|
|
|
|
|
} else if (0 == strncmp("charged", buffer, 7)) {
|
|
|
|
|
buffer[0] = 'F';
|
|
|
|
|
memmove(buffer + 1, buffer + 7, n - 7);
|
|
|
|
|
} else if (0 == strncmp("not present", buffer, 11)) {
|
|
|
|
|
buffer[0] = 'N';
|
|
|
|
|
memmove(buffer + 1, buffer + 11, n - 11);
|
|
|
|
|
} else if (0 == strncmp("empty", buffer, 5)) {
|
|
|
|
|
buffer[0] = 'E';
|
|
|
|
|
memmove(buffer + 1, buffer + 5, n - 5);
|
|
|
|
|
} else if (0 == strncmp("unknown", buffer, 7)) {
|
|
|
|
|
buffer[0] = 'U';
|
|
|
|
|
memmove(buffer + 1, buffer + 7, n - 7);
|
|
|
|
|
}
|
|
|
|
|
// Otherwise, don't shorten.
|
2009-02-18 04:49:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int _get_battery_perct(const char *bat) {
|
|
|
|
|
static int rep = 0;
|
|
|
|
|
int idx;
|
|
|
|
|
char acpi_path[128];
|
|
|
|
|
char sysfs_path[128];
|
|
|
|
|
int remaining_capacity = -1;
|
|
|
|
|
|
|
|
|
|
snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
|
|
|
|
|
snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
|
|
|
|
|
|
|
|
|
|
idx = get_battery_idx(bat);
|
|
|
|
|
|
|
|
|
|
/* don't update battery too often */
|
|
|
|
|
if (current_update_time - last_battery_perct_time[idx] < 30) {
|
|
|
|
|
return last_battery_perct[idx];
|
|
|
|
|
}
|
|
|
|
|
last_battery_perct_time[idx] = current_update_time;
|
|
|
|
|
|
|
|
|
|
/* Only check for SYSFS or ACPI */
|
|
|
|
|
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (sysfs_bat_fp[idx] == nullptr && acpi_bat_fp[idx] == NULL &&
|
|
|
|
|
apm_bat_fp[idx] == nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
|
|
|
|
|
rep = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (sysfs_bat_fp[idx] == nullptr && acpi_bat_fp[idx] == NULL &&
|
|
|
|
|
apm_bat_fp[idx] == nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
acpi_bat_fp[idx] = open_file(acpi_path, &rep);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (sysfs_bat_fp[idx] != nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/* SYSFS */
|
|
|
|
|
while (!feof(sysfs_bat_fp[idx])) {
|
|
|
|
|
char buf[256];
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (fgets(buf, 256, sysfs_bat_fp[idx]) == nullptr) break;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
|
|
|
|
|
sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
|
|
|
|
|
} else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0) {
|
|
|
|
|
sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
|
|
|
|
|
} else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
|
|
|
|
|
sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
|
|
|
|
|
} else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0) {
|
|
|
|
|
sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fclose(sysfs_bat_fp[idx]);
|
2018-05-12 23:26:31 +00:00
|
|
|
|
sysfs_bat_fp[idx] = nullptr;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
2018-05-12 23:26:31 +00:00
|
|
|
|
} else if (acpi_bat_fp[idx] != nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/* ACPI */
|
|
|
|
|
/* read last full capacity if it's zero */
|
|
|
|
|
if (acpi_design_capacity[idx] == 0) {
|
|
|
|
|
static int rep2;
|
|
|
|
|
char path[128];
|
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
|
|
snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
|
|
|
|
|
fp = open_file(path, &rep2);
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (fp != nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
while (!feof(fp)) {
|
|
|
|
|
char b[256];
|
|
|
|
|
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (fgets(b, 256, fp) == nullptr) { break; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
if (sscanf(b, "last full capacity: %d", &acpi_design_capacity[idx]) !=
|
|
|
|
|
0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fclose(fp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fseek(acpi_bat_fp[idx], 0, SEEK_SET);
|
|
|
|
|
|
|
|
|
|
while (!feof(acpi_bat_fp[idx])) {
|
|
|
|
|
char buf[256];
|
|
|
|
|
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (fgets(buf, 256, acpi_bat_fp[idx]) == nullptr) { break; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
if (buf[0] == 'r') {
|
|
|
|
|
sscanf(buf, "remaining capacity: %d", &remaining_capacity);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (remaining_capacity < 0) { return 0; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/* compute the battery percentage */
|
|
|
|
|
last_battery_perct[idx] =
|
|
|
|
|
(int)(((float)remaining_capacity / acpi_design_capacity[idx]) * 100);
|
|
|
|
|
if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
|
|
|
|
|
return last_battery_perct[idx];
|
2007-04-06 06:11:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int get_battery_perct(const char *bat) {
|
|
|
|
|
int idx, n = 0, total_capacity = 0, remaining_capacity;
|
|
|
|
|
;
|
2014-03-09 20:32:15 +00:00
|
|
|
|
#define BATTERY_LEN 8
|
2018-05-12 16:03:00 +00:00
|
|
|
|
char battery[BATTERY_LEN];
|
2014-03-09 20:32:15 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
init_batteries();
|
2014-03-09 20:32:15 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/* Check if user asked for the mean percentage of all batteries. */
|
|
|
|
|
if (!strcmp(bat, "all")) {
|
|
|
|
|
for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
|
|
|
|
|
snprintf(battery, BATTERY_LEN - 1, "BAT%d", idx);
|
2014-03-09 20:32:15 +00:00
|
|
|
|
#undef BATTERY_LEN
|
2018-05-12 16:03:00 +00:00
|
|
|
|
remaining_capacity = _get_battery_perct(battery);
|
|
|
|
|
if (remaining_capacity > 0) {
|
|
|
|
|
total_capacity += remaining_capacity;
|
|
|
|
|
n++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (n == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
else
|
|
|
|
|
return total_capacity / n;
|
|
|
|
|
} else {
|
|
|
|
|
return _get_battery_perct(bat);
|
|
|
|
|
}
|
2014-03-09 20:32:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
double get_battery_perct_bar(struct text_object *obj) {
|
|
|
|
|
int idx;
|
2008-02-20 20:30:45 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
get_battery_perct(obj->data.s);
|
|
|
|
|
idx = get_battery_idx(obj->data.s);
|
|
|
|
|
return last_battery_perct[idx];
|
2007-04-06 06:11:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-04-23 21:35:31 +00:00
|
|
|
|
/* On Apple powerbook and ibook:
|
|
|
|
|
$ cat /proc/pmu/battery_0
|
|
|
|
|
flags : 00000013
|
|
|
|
|
charge : 3623
|
|
|
|
|
max_charge : 3720
|
|
|
|
|
current : 388
|
|
|
|
|
voltage : 16787
|
|
|
|
|
time rem. : 900
|
|
|
|
|
$ cat /proc/pmu/info
|
|
|
|
|
PMU driver version : 2
|
|
|
|
|
PMU firmware version : 0c
|
|
|
|
|
AC Power : 1
|
|
|
|
|
Battery count : 1
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* defines as in <linux/pmu.h> */
|
2018-05-12 16:03:00 +00:00
|
|
|
|
#define PMU_BATT_PRESENT 0x00000001
|
|
|
|
|
#define PMU_BATT_CHARGING 0x00000002
|
2006-04-23 21:35:31 +00:00
|
|
|
|
|
2008-02-20 20:30:45 +00:00
|
|
|
|
static FILE *pmu_battery_fp;
|
|
|
|
|
static FILE *pmu_info_fp;
|
2006-04-23 21:35:31 +00:00
|
|
|
|
static char pb_battery_info[3][32];
|
|
|
|
|
static double pb_battery_info_update;
|
2007-08-05 04:47:21 +00:00
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
void powerbook_update_status(unsigned int flags, int ac);
|
|
|
|
|
void powerbook_update_percentage(long timeval, unsigned int flags, int ac,
|
|
|
|
|
int charge, int max_charge);
|
|
|
|
|
void powerbook_update_time(long timeval);
|
|
|
|
|
|
2006-04-23 21:35:31 +00:00
|
|
|
|
#define PMU_PATH "/proc/pmu"
|
2018-12-08 22:45:17 +00:00
|
|
|
|
void get_powerbook_batt_info(struct text_object *obj, char *buffer,
|
|
|
|
|
unsigned int n) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
static int rep = 0;
|
|
|
|
|
const char *batt_path = PMU_PATH "/battery_0";
|
|
|
|
|
const char *info_path = PMU_PATH "/info";
|
2018-05-13 13:58:03 +00:00
|
|
|
|
unsigned int flags = 0;
|
|
|
|
|
int charge = 0;
|
|
|
|
|
int max_charge = 1;
|
|
|
|
|
int ac = -1;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
long timeval = -1;
|
|
|
|
|
|
|
|
|
|
/* don't update battery too often */
|
|
|
|
|
if (current_update_time - pb_battery_info_update < 29.5) {
|
|
|
|
|
snprintf(buffer, n, "%s", pb_battery_info[obj->data.i]);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
pb_battery_info_update = current_update_time;
|
|
|
|
|
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (pmu_battery_fp == nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
pmu_battery_fp = open_file(batt_path, &rep);
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (pmu_battery_fp == nullptr) { return; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
rewind(pmu_battery_fp);
|
|
|
|
|
while (!feof(pmu_battery_fp)) {
|
|
|
|
|
char buf[32];
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
if (fgets(buf, sizeof(buf), pmu_battery_fp) == nullptr) { break; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
if (buf[0] == 'f') {
|
|
|
|
|
sscanf(buf, "flags : %8x", &flags);
|
|
|
|
|
} else if (buf[0] == 'c' && buf[1] == 'h') {
|
|
|
|
|
sscanf(buf, "charge : %d", &charge);
|
|
|
|
|
} else if (buf[0] == 'm') {
|
|
|
|
|
sscanf(buf, "max_charge : %d", &max_charge);
|
|
|
|
|
} else if (buf[0] == 't') {
|
|
|
|
|
sscanf(buf, "time rem. : %ld", &timeval);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-08 22:45:17 +00:00
|
|
|
|
pmu_info_fp = open_file(info_path, &rep);
|
|
|
|
|
if (pmu_info_fp == nullptr) { return; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
rewind(pmu_info_fp);
|
|
|
|
|
while (!feof(pmu_info_fp)) {
|
|
|
|
|
char buf[32];
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
if (fgets(buf, sizeof(buf), pmu_info_fp) == nullptr) { break; }
|
|
|
|
|
if (buf[0] == 'A') { sscanf(buf, "AC Power : %d", &ac); }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
2018-12-08 22:45:17 +00:00
|
|
|
|
|
|
|
|
|
powerbook_update_status(flags, ac);
|
|
|
|
|
powerbook_update_percentage(timeval, flags, ac, charge, max_charge);
|
|
|
|
|
powerbook_update_time(timeval);
|
|
|
|
|
|
|
|
|
|
snprintf(buffer, n, "%s", pb_battery_info[obj->data.i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void powerbook_update_status(unsigned int flags, int ac) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/* update status string */
|
|
|
|
|
if ((ac && !(flags & PMU_BATT_PRESENT))) {
|
|
|
|
|
strncpy(pb_battery_info[PB_BATT_STATUS], "AC",
|
|
|
|
|
sizeof(pb_battery_info[PB_BATT_STATUS]));
|
|
|
|
|
} else if (ac && (flags & PMU_BATT_PRESENT) && !(flags & PMU_BATT_CHARGING)) {
|
|
|
|
|
strncpy(pb_battery_info[PB_BATT_STATUS], "charged",
|
|
|
|
|
sizeof(pb_battery_info[PB_BATT_STATUS]));
|
|
|
|
|
} else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
|
|
|
|
|
strncpy(pb_battery_info[PB_BATT_STATUS], "charging",
|
|
|
|
|
sizeof(pb_battery_info[PB_BATT_STATUS]));
|
|
|
|
|
} else {
|
|
|
|
|
strncpy(pb_battery_info[PB_BATT_STATUS], "discharging",
|
|
|
|
|
sizeof(pb_battery_info[PB_BATT_STATUS]));
|
|
|
|
|
}
|
2018-12-08 22:45:17 +00:00
|
|
|
|
}
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
void powerbook_update_percentage(long timeval, unsigned int flags, int ac,
|
|
|
|
|
int charge, int max_charge) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/* update percentage string */
|
|
|
|
|
if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT) &&
|
|
|
|
|
!(flags & PMU_BATT_CHARGING)) {
|
|
|
|
|
snprintf(pb_battery_info[PB_BATT_PERCENT],
|
2018-08-02 15:15:16 +00:00
|
|
|
|
sizeof(pb_battery_info[PB_BATT_PERCENT]), "%s", "100%%");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
} else if (timeval == 0) {
|
|
|
|
|
snprintf(pb_battery_info[PB_BATT_PERCENT],
|
2018-08-02 15:15:16 +00:00
|
|
|
|
sizeof(pb_battery_info[PB_BATT_PERCENT]), "%s", "unknown");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
} else {
|
|
|
|
|
snprintf(pb_battery_info[PB_BATT_PERCENT],
|
|
|
|
|
sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
|
|
|
|
|
(charge * 100) / max_charge);
|
|
|
|
|
}
|
2018-12-08 22:45:17 +00:00
|
|
|
|
}
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
void powerbook_update_time(long timeval) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/* update time string */
|
|
|
|
|
if (timeval == 0) { /* fully charged or battery not present */
|
|
|
|
|
snprintf(pb_battery_info[PB_BATT_TIME],
|
2018-08-02 15:15:16 +00:00
|
|
|
|
sizeof(pb_battery_info[PB_BATT_TIME]), "%s", "unknown");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
} else if (timeval < 60 * 60) { /* don't show secs */
|
|
|
|
|
format_seconds_short(pb_battery_info[PB_BATT_TIME],
|
|
|
|
|
sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
|
|
|
|
|
} else {
|
|
|
|
|
format_seconds(pb_battery_info[PB_BATT_TIME],
|
|
|
|
|
sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
|
|
|
|
|
}
|
2006-04-23 21:35:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-11-12 22:50:17 +00:00
|
|
|
|
#define ENTROPY_AVAIL_PATH "/proc/sys/kernel/random/entropy_avail"
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int get_entropy_avail(unsigned int *val) {
|
|
|
|
|
static int rep = 0;
|
|
|
|
|
FILE *fp;
|
2006-11-30 20:46:34 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
if (!(fp = open_file(ENTROPY_AVAIL_PATH, &rep))) return 1;
|
2006-11-30 20:46:34 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
if (fscanf(fp, "%u", val) != 1) return 1;
|
2006-12-23 06:01:16 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
fclose(fp);
|
|
|
|
|
return 0;
|
2009-11-12 22:50:17 +00:00
|
|
|
|
}
|
2006-12-23 06:01:16 +00:00
|
|
|
|
|
2009-11-12 22:50:17 +00:00
|
|
|
|
#define ENTROPY_POOLSIZE_PATH "/proc/sys/kernel/random/poolsize"
|
2006-12-23 06:01:16 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int get_entropy_poolsize(unsigned int *val) {
|
|
|
|
|
static int rep = 0;
|
|
|
|
|
FILE *fp;
|
2009-11-12 22:50:17 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
if (!(fp = open_file(ENTROPY_POOLSIZE_PATH, &rep))) return 1;
|
2009-11-12 22:50:17 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
if (fscanf(fp, "%u", val) != 1) return 1;
|
2009-11-12 22:50:17 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
fclose(fp);
|
|
|
|
|
return 0;
|
2006-12-23 06:01:16 +00:00
|
|
|
|
}
|
2008-03-24 20:08:16 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
void print_disk_protect_queue(struct text_object *obj, char *p,
|
2018-08-07 23:22:23 +00:00
|
|
|
|
unsigned int p_max_size) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
FILE *fp;
|
|
|
|
|
char path[128];
|
|
|
|
|
int state;
|
|
|
|
|
|
|
|
|
|
snprintf(path, 127, "/sys/block/%s/device/unload_heads", obj->data.s);
|
|
|
|
|
if (access(path, F_OK)) {
|
|
|
|
|
snprintf(path, 127, "/sys/block/%s/queue/protect", obj->data.s);
|
|
|
|
|
}
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if ((fp = fopen(path, "r")) == nullptr) {
|
2018-08-02 15:15:16 +00:00
|
|
|
|
snprintf(p, p_max_size, "%s", "n/a ");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (fscanf(fp, "%d\n", &state) != 1) {
|
|
|
|
|
fclose(fp);
|
2018-08-02 15:15:16 +00:00
|
|
|
|
snprintf(p, p_max_size, "%s", "failed");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
fclose(fp);
|
2018-08-02 15:15:16 +00:00
|
|
|
|
snprintf(p, p_max_size, "%s", (state > 0) ? "frozen" : "free ");
|
2008-03-24 20:08:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-10-02 12:43:44 +00:00
|
|
|
|
std::unordered_map<std::string, bool> dev_list;
|
2010-11-10 23:31:06 +00:00
|
|
|
|
|
2010-01-31 19:57:02 +00:00
|
|
|
|
/* Same as sf #2942117 but memoized using a linked list */
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int is_disk(char *dev) {
|
|
|
|
|
std::string orig(dev);
|
|
|
|
|
std::string syspath("/sys/block/");
|
|
|
|
|
char *slash;
|
2010-01-31 19:57:02 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
auto i = dev_list.find(orig);
|
|
|
|
|
if (i != dev_list.end()) return i->second;
|
2010-01-31 19:57:02 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
while ((slash = strchr(dev, '/'))) *slash = '!';
|
|
|
|
|
syspath += dev;
|
2010-01-31 19:57:02 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
return dev_list[orig] = !(access(syspath.c_str(), F_OK));
|
2010-01-31 19:57:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
int update_diskio(void) {
|
|
|
|
|
FILE *fp;
|
|
|
|
|
static int rep = 0;
|
|
|
|
|
char buf[512], devbuf[64];
|
|
|
|
|
unsigned int major, minor;
|
|
|
|
|
int col_count = 0;
|
|
|
|
|
struct diskio_stat *cur;
|
|
|
|
|
unsigned int reads, writes;
|
|
|
|
|
unsigned int total_reads = 0, total_writes = 0;
|
|
|
|
|
|
|
|
|
|
stats.current = 0;
|
|
|
|
|
stats.current_read = 0;
|
|
|
|
|
stats.current_write = 0;
|
|
|
|
|
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (!(fp = open_file("/proc/diskstats", &rep))) { return 0; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
/* read reads and writes from all disks (minor = 0), including cd-roms
|
|
|
|
|
* and floppies, and sum them up */
|
|
|
|
|
while (fgets(buf, 512, fp)) {
|
|
|
|
|
col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
|
|
|
|
|
&minor, devbuf, &reads, &writes);
|
|
|
|
|
/* ignore subdevices (they have only 3 matching entries in their line)
|
|
|
|
|
* and virtual devices (LVM, network block devices, RAM disks, Loopback)
|
|
|
|
|
*
|
|
|
|
|
* XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
|
|
|
|
|
if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR &&
|
|
|
|
|
major != RAMDISK_MAJOR && major != LOOP_MAJOR && major != DM_MAJOR) {
|
|
|
|
|
/* check needed for kernel >= 2.6.31, see sf #2942117 */
|
|
|
|
|
if (is_disk(devbuf)) {
|
|
|
|
|
total_reads += reads;
|
|
|
|
|
total_writes += writes;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
col_count = sscanf(buf, "%u %u %s %*u %u %*u %u", &major, &minor, devbuf,
|
|
|
|
|
&reads, &writes);
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (col_count != 5) { continue; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
cur = stats.next;
|
|
|
|
|
while (cur && strcmp(devbuf, cur->dev)) cur = cur->next;
|
|
|
|
|
|
|
|
|
|
if (cur) update_diskio_values(cur, reads, writes);
|
|
|
|
|
}
|
|
|
|
|
update_diskio_values(&stats, total_reads, total_writes);
|
|
|
|
|
fclose(fp);
|
|
|
|
|
return 0;
|
2009-05-14 22:52:18 +00:00
|
|
|
|
}
|
2010-04-15 14:05:42 +00:00
|
|
|
|
|
2018-12-08 22:45:17 +00:00
|
|
|
|
void print_distribution(struct text_object *obj, char *p,
|
|
|
|
|
unsigned int p_max_size) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
(void)obj;
|
|
|
|
|
int i, bytes_read;
|
|
|
|
|
char *buf;
|
|
|
|
|
struct stat sb;
|
|
|
|
|
|
|
|
|
|
if (stat("/etc/arch-release", &sb) == 0) {
|
2018-08-02 15:15:16 +00:00
|
|
|
|
snprintf(p, p_max_size, "%s", "Arch Linux");
|
2018-05-12 16:03:00 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
snprintf(p, p_max_size, "Unknown");
|
|
|
|
|
buf = readfile("/proc/version", &bytes_read, 1);
|
|
|
|
|
if (buf) {
|
|
|
|
|
/* I am assuming the distribution name is the first string in /proc/version
|
|
|
|
|
that:
|
|
|
|
|
- is preceded by a '('
|
|
|
|
|
- starts with a capital
|
|
|
|
|
- is followed by a space and a number
|
|
|
|
|
but i am not sure if this is always true... */
|
|
|
|
|
for (i = 1; i < bytes_read; i++) {
|
|
|
|
|
if (buf[i - 1] == '(' && buf[i] >= 'A' && buf[i] <= 'Z') break;
|
|
|
|
|
}
|
|
|
|
|
if (i < bytes_read) {
|
|
|
|
|
snprintf(p, p_max_size, "%s", &buf[i]);
|
|
|
|
|
for (i = 1; p[i]; i++) {
|
|
|
|
|
if (p[i - 1] == ' ' && p[i] >= '0' && p[i] <= '9') {
|
|
|
|
|
p[i - 1] = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
free(buf);
|
|
|
|
|
}
|
2010-04-15 14:05:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-05-30 11:55:50 +00:00
|
|
|
|
/******************************************
|
|
|
|
|
* Calculate cpu total *
|
|
|
|
|
******************************************/
|
|
|
|
|
#define TMPL_SHORTPROC "%*s %llu %llu %llu %llu"
|
|
|
|
|
#define TMPL_LONGPROC "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
static unsigned long long calc_cpu_total(void) {
|
|
|
|
|
static unsigned long long previous_total = 0;
|
|
|
|
|
unsigned long long total = 0;
|
|
|
|
|
unsigned long long t = 0;
|
|
|
|
|
int rc;
|
|
|
|
|
int ps;
|
|
|
|
|
char line[BUFFER_LEN] = {0};
|
|
|
|
|
unsigned long long cpu = 0;
|
|
|
|
|
unsigned long long niceval = 0;
|
|
|
|
|
unsigned long long systemval = 0;
|
|
|
|
|
unsigned long long idle = 0;
|
|
|
|
|
unsigned long long iowait = 0;
|
|
|
|
|
unsigned long long irq = 0;
|
|
|
|
|
unsigned long long softirq = 0;
|
|
|
|
|
unsigned long long steal = 0;
|
|
|
|
|
const char *template_ =
|
|
|
|
|
KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGPROC : TMPL_SHORTPROC;
|
|
|
|
|
|
|
|
|
|
ps = open("/proc/stat", O_RDONLY);
|
|
|
|
|
rc = read(ps, line, BUFFER_LEN - 1);
|
|
|
|
|
close(ps);
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (rc < 0) { return 0; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
sscanf(line, template_, &cpu, &niceval, &systemval, &idle, &iowait, &irq,
|
|
|
|
|
&softirq, &steal);
|
|
|
|
|
total = cpu + niceval + systemval + idle + iowait + irq + softirq + steal;
|
|
|
|
|
|
|
|
|
|
t = total - previous_total;
|
|
|
|
|
previous_total = total;
|
|
|
|
|
|
|
|
|
|
return t;
|
2010-05-30 11:55:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************
|
|
|
|
|
* Calculate each processes cpu *
|
|
|
|
|
******************************************/
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
inline static void calc_cpu_each(unsigned long long total) {
|
|
|
|
|
float mul = 100.0;
|
|
|
|
|
if (top_cpu_separate.get(*state)) mul *= info.cpu_count;
|
2010-05-30 11:55:50 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
for (struct process *p = first_process; p; p = p->next)
|
|
|
|
|
p->amount = mul * (p->user_time + p->kernel_time) / (float)total;
|
2010-05-30 11:55:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef BUILD_IOSTATS
|
2018-05-12 16:03:00 +00:00
|
|
|
|
static void calc_io_each(void) {
|
|
|
|
|
struct process *p;
|
|
|
|
|
unsigned long long sum = 0;
|
|
|
|
|
|
|
|
|
|
for (p = first_process; p; p = p->next) sum += p->read_bytes + p->write_bytes;
|
|
|
|
|
|
|
|
|
|
if (sum == 0) sum = 1; /* to avoid having NANs if no I/O occured */
|
|
|
|
|
for (p = first_process; p; p = p->next)
|
|
|
|
|
p->io_perc = 100.0 * (p->read_bytes + p->write_bytes) / (float)sum;
|
2010-05-30 11:55:50 +00:00
|
|
|
|
}
|
|
|
|
|
#endif /* BUILD_IOSTATS */
|
|
|
|
|
|
|
|
|
|
/******************************************
|
|
|
|
|
* Extract information from /proc *
|
|
|
|
|
******************************************/
|
|
|
|
|
|
|
|
|
|
#define PROCFS_TEMPLATE "/proc/%d/stat"
|
|
|
|
|
#define PROCFS_CMDLINE_TEMPLATE "/proc/%d/cmdline"
|
|
|
|
|
|
|
|
|
|
/* These are the guts that extract information out of /proc.
|
|
|
|
|
* Anyone hoping to port wmtop should look here first. */
|
2018-05-12 16:03:00 +00:00
|
|
|
|
static void process_parse_stat(struct process *process) {
|
|
|
|
|
char line[BUFFER_LEN] = {0}, filename[BUFFER_LEN], procname[BUFFER_LEN];
|
|
|
|
|
char cmdline[BUFFER_LEN] = {0}, cmdline_filename[BUFFER_LEN],
|
|
|
|
|
cmdline_procname[BUFFER_LEN];
|
|
|
|
|
char basename[BUFFER_LEN] = {0};
|
|
|
|
|
char tmpstr[BUFFER_LEN] = {0};
|
|
|
|
|
char state[4];
|
|
|
|
|
int ps, cmdline_ps;
|
|
|
|
|
unsigned long user_time = 0;
|
|
|
|
|
unsigned long kernel_time = 0;
|
|
|
|
|
int rc;
|
|
|
|
|
int endl;
|
|
|
|
|
int nice_val;
|
|
|
|
|
char *lparen, *rparen;
|
|
|
|
|
struct stat process_stat;
|
|
|
|
|
|
|
|
|
|
snprintf(filename, sizeof(filename), PROCFS_TEMPLATE, process->pid);
|
|
|
|
|
snprintf(cmdline_filename, sizeof(cmdline_filename), PROCFS_CMDLINE_TEMPLATE,
|
|
|
|
|
process->pid);
|
|
|
|
|
|
|
|
|
|
ps = open(filename, O_RDONLY);
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (ps == -1) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/* The process must have finished in the last few jiffies! */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (fstat(ps, &process_stat) != 0) {
|
|
|
|
|
close(ps);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-05-12 16:03:00 +00:00
|
|
|
|
process->uid = process_stat.st_uid;
|
|
|
|
|
|
|
|
|
|
/* Mark process as up-to-date. */
|
|
|
|
|
process->time_stamp = g_time;
|
|
|
|
|
|
|
|
|
|
rc = read(ps, line, BUFFER_LEN - 1);
|
|
|
|
|
close(ps);
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (rc < 0) { return; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
/* Read /proc/<pid>/cmdline */
|
|
|
|
|
cmdline_ps = open(cmdline_filename, O_RDONLY);
|
|
|
|
|
if (cmdline_ps < 0) {
|
|
|
|
|
/* The process must have finished in the last few jiffies! */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
endl = read(cmdline_ps, cmdline, BUFFER_LEN - 1);
|
|
|
|
|
close(cmdline_ps);
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (endl < 0) { return; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
/* Some processes have null-separated arguments (see proc(5)); let's fix it */
|
|
|
|
|
int i = endl;
|
|
|
|
|
while (i && cmdline[i - 1] == 0) {
|
|
|
|
|
/* Skip past any trailing null characters */
|
|
|
|
|
--i;
|
|
|
|
|
}
|
|
|
|
|
while (i--) {
|
|
|
|
|
/* Replace null character between arguments with a space */
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (cmdline[i] == 0) { cmdline[i] = ' '; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cmdline[endl] = 0;
|
|
|
|
|
|
|
|
|
|
/* We want to transform for example "/usr/bin/python program.py" to "python
|
|
|
|
|
* program.py"
|
|
|
|
|
* 1. search for first space
|
|
|
|
|
* 2. search for last / before first space
|
|
|
|
|
* 3. copy string from its position
|
|
|
|
|
*/
|
|
|
|
|
char *space_ptr = strchr(cmdline, ' ');
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (space_ptr == nullptr) {
|
2018-05-13 22:59:39 +00:00
|
|
|
|
strncpy(tmpstr, cmdline, BUFFER_LEN);
|
2018-05-12 16:03:00 +00:00
|
|
|
|
} else {
|
|
|
|
|
long int space_pos = space_ptr - cmdline;
|
|
|
|
|
strncpy(tmpstr, cmdline, space_pos);
|
|
|
|
|
tmpstr[space_pos] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *slash_ptr = strrchr(tmpstr, '/');
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (slash_ptr == nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
strncpy(cmdline_procname, cmdline, BUFFER_LEN);
|
|
|
|
|
} else {
|
|
|
|
|
long int slash_pos = slash_ptr - tmpstr;
|
|
|
|
|
strncpy(cmdline_procname, cmdline + slash_pos + 1, BUFFER_LEN - slash_pos);
|
|
|
|
|
cmdline_procname[BUFFER_LEN - slash_pos] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Extract cpu times from data in /proc filesystem */
|
|
|
|
|
lparen = strchr(line, '(');
|
|
|
|
|
rparen = strrchr(line, ')');
|
|
|
|
|
if (!lparen || !rparen || rparen < lparen) return; // this should not happen
|
|
|
|
|
|
|
|
|
|
rc = MIN((unsigned)(rparen - lparen - 1), sizeof(procname) - 1);
|
|
|
|
|
strncpy(procname, lparen + 1, rc);
|
|
|
|
|
procname[rc] = '\0';
|
|
|
|
|
strncpy(basename, procname, strlen(procname) + 1);
|
|
|
|
|
|
|
|
|
|
if (strlen(procname) < strlen(cmdline_procname))
|
|
|
|
|
strncpy(procname, cmdline_procname, strlen(cmdline_procname) + 1);
|
|
|
|
|
|
|
|
|
|
rc = sscanf(rparen + 1,
|
|
|
|
|
"%3s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %lu "
|
|
|
|
|
"%lu %*s %*s %*s %d %*s %*s %*s %llu %llu",
|
|
|
|
|
state, &process->user_time, &process->kernel_time, &nice_val,
|
|
|
|
|
&process->vsize, &process->rss);
|
|
|
|
|
if (rc < 6) {
|
|
|
|
|
NORM_ERR("scaning data for %s failed, got only %d fields", procname, rc);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (state[0] == 'R') ++info.run_procs;
|
|
|
|
|
|
|
|
|
|
free_and_zero(process->name);
|
|
|
|
|
free_and_zero(process->basename);
|
|
|
|
|
process->name = strndup(procname, text_buffer_size.get(*::state));
|
|
|
|
|
process->basename = strndup(basename, text_buffer_size.get(*::state));
|
|
|
|
|
process->rss *= getpagesize();
|
|
|
|
|
|
|
|
|
|
process->total_cpu_time = process->user_time + process->kernel_time;
|
|
|
|
|
if (process->previous_user_time == ULONG_MAX) {
|
|
|
|
|
process->previous_user_time = process->user_time;
|
|
|
|
|
}
|
|
|
|
|
if (process->previous_kernel_time == ULONG_MAX) {
|
|
|
|
|
process->previous_kernel_time = process->kernel_time;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* strangely, the values aren't monotonous */
|
|
|
|
|
if (process->previous_user_time > process->user_time)
|
|
|
|
|
process->previous_user_time = process->user_time;
|
|
|
|
|
|
|
|
|
|
if (process->previous_kernel_time > process->kernel_time)
|
|
|
|
|
process->previous_kernel_time = process->kernel_time;
|
|
|
|
|
|
|
|
|
|
/* store the difference of the user_time */
|
|
|
|
|
user_time = process->user_time - process->previous_user_time;
|
|
|
|
|
kernel_time = process->kernel_time - process->previous_kernel_time;
|
|
|
|
|
|
|
|
|
|
/* backup the process->user_time for next time around */
|
|
|
|
|
process->previous_user_time = process->user_time;
|
|
|
|
|
process->previous_kernel_time = process->kernel_time;
|
|
|
|
|
|
|
|
|
|
/* store only the difference of the user_time here... */
|
|
|
|
|
process->user_time = user_time;
|
|
|
|
|
process->kernel_time = kernel_time;
|
2010-05-30 11:55:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef BUILD_IOSTATS
|
|
|
|
|
#define PROCFS_TEMPLATE_IO "/proc/%d/io"
|
2018-05-12 16:03:00 +00:00
|
|
|
|
static void process_parse_io(struct process *process) {
|
|
|
|
|
static const char *read_bytes_str = "read_bytes:";
|
|
|
|
|
static const char *write_bytes_str = "write_bytes:";
|
|
|
|
|
|
|
|
|
|
char line[BUFFER_LEN] = {0}, filename[BUFFER_LEN];
|
|
|
|
|
int ps;
|
|
|
|
|
int rc;
|
|
|
|
|
char *pos, *endpos;
|
|
|
|
|
unsigned long long read_bytes, write_bytes;
|
|
|
|
|
|
|
|
|
|
snprintf(filename, sizeof(filename), PROCFS_TEMPLATE_IO, process->pid);
|
|
|
|
|
|
|
|
|
|
ps = open(filename, O_RDONLY);
|
|
|
|
|
if (ps < 0) {
|
|
|
|
|
/* The process must have finished in the last few jiffies!
|
|
|
|
|
* Or, the kernel doesn't support I/O accounting.
|
|
|
|
|
*/
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rc = read(ps, line, BUFFER_LEN - 1);
|
|
|
|
|
close(ps);
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (rc < 0) { return; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
pos = strstr(line, read_bytes_str);
|
2018-05-12 23:26:31 +00:00
|
|
|
|
if (pos == nullptr) {
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/* these should not happen (unless the format of the file changes) */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
pos += strlen(read_bytes_str);
|
|
|
|
|
process->read_bytes = strtoull(pos, &endpos, 10);
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (endpos == pos) { return; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
pos = strstr(line, write_bytes_str);
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (pos == nullptr) { return; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
pos += strlen(write_bytes_str);
|
|
|
|
|
process->write_bytes = strtoull(pos, &endpos, 10);
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (endpos == pos) { return; }
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
|
|
if (process->previous_read_bytes == ULLONG_MAX) {
|
|
|
|
|
process->previous_read_bytes = process->read_bytes;
|
|
|
|
|
}
|
|
|
|
|
if (process->previous_write_bytes == ULLONG_MAX) {
|
|
|
|
|
process->previous_write_bytes = process->write_bytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* store the difference of the byte counts */
|
|
|
|
|
read_bytes = process->read_bytes - process->previous_read_bytes;
|
|
|
|
|
write_bytes = process->write_bytes - process->previous_write_bytes;
|
|
|
|
|
|
|
|
|
|
/* backup the counts for next time around */
|
|
|
|
|
process->previous_read_bytes = process->read_bytes;
|
|
|
|
|
process->previous_write_bytes = process->write_bytes;
|
|
|
|
|
|
|
|
|
|
/* store only the difference here... */
|
|
|
|
|
process->read_bytes = read_bytes;
|
|
|
|
|
process->write_bytes = write_bytes;
|
2010-05-30 11:55:50 +00:00
|
|
|
|
}
|
|
|
|
|
#endif /* BUILD_IOSTATS */
|
|
|
|
|
|
|
|
|
|
/******************************************
|
|
|
|
|
* Get process structure for process pid *
|
|
|
|
|
******************************************/
|
|
|
|
|
|
|
|
|
|
/* This function seems to hog all of the CPU time.
|
|
|
|
|
* I can't figure out why - it doesn't do much. */
|
2018-05-12 16:03:00 +00:00
|
|
|
|
static void calculate_stats(struct process *process) {
|
|
|
|
|
/* compute each process cpu usage by reading /proc/<proc#>/stat */
|
|
|
|
|
process_parse_stat(process);
|
2010-05-30 11:55:50 +00:00
|
|
|
|
|
|
|
|
|
#ifdef BUILD_IOSTATS
|
2018-05-12 16:03:00 +00:00
|
|
|
|
process_parse_io(process);
|
2010-05-30 11:55:50 +00:00
|
|
|
|
#endif /* BUILD_IOSTATS */
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/*
|
|
|
|
|
* Check name against the exclusion list
|
|
|
|
|
*/
|
|
|
|
|
/* if (process->counted && exclusion_expression &&
|
|
|
|
|
* !regexec(exclusion_expression, process->name, 0, 0, 0))
|
|
|
|
|
* process->counted = 0; */
|
2010-05-30 11:55:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************
|
|
|
|
|
* Update process table *
|
|
|
|
|
******************************************/
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
static void update_process_table(void) {
|
|
|
|
|
DIR *dir;
|
|
|
|
|
struct dirent *entry;
|
2010-05-30 11:55:50 +00:00
|
|
|
|
|
2018-05-13 13:58:03 +00:00
|
|
|
|
if (!(dir = opendir("/proc"))) { return; }
|
2010-05-30 11:55:50 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
info.run_procs = 0;
|
2010-05-30 11:55:50 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
/* Get list of processes from /proc directory */
|
|
|
|
|
while ((entry = readdir(dir))) {
|
|
|
|
|
pid_t pid;
|
2010-05-30 11:55:50 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
if (sscanf(entry->d_name, "%d", &pid) > 0) {
|
|
|
|
|
/* compute each process cpu usage */
|
|
|
|
|
calculate_stats(get_process(pid));
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-05-30 11:55:50 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
closedir(dir);
|
2010-05-30 11:55:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
void get_top_info(void) {
|
|
|
|
|
unsigned long long total = 0;
|
2010-05-30 11:55:50 +00:00
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
|
total = calc_cpu_total(); /* calculate the total of the processor */
|
|
|
|
|
update_process_table(); /* update the table with process list */
|
|
|
|
|
calc_cpu_each(total); /* and then the percentage for each task */
|
2010-05-30 11:55:50 +00:00
|
|
|
|
#ifdef BUILD_IOSTATS
|
2018-05-12 16:03:00 +00:00
|
|
|
|
calc_io_each(); /* percentage of I/O for each task */
|
|
|
|
|
#endif /* BUILD_IOSTATS */
|
2010-05-30 11:55:50 +00:00
|
|
|
|
}
|