34 Star 0 Fork 4

openKylin/sysstat

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
common.c 40.65 KB
一键复制 编辑 原始数据 按行查看 历史
hehedaddd 提交于 2023-07-17 12:16 . update common.c.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690
/*
* sar, sadc, sadf, mpstat and iostat common routines.
* (C) 1999-2022 by Sebastien GODARD (sysstat <at> orange.fr)
*
***************************************************************************
* 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 2 of the License, or (at your *
* option) any later version. *
* *
* This program is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without the implied warranty of MERCHANTABILITY *
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
* for more details. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA *
***************************************************************************
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <inttypes.h>
#include <time.h>
#include <errno.h>
#include <unistd.h> /* For STDOUT_FILENO, among others */
#include <sys/ioctl.h>
#include <sys/types.h>
#include <dirent.h>
#include <ctype.h>
#include <limits.h>
#include <libgen.h>
#include "version.h"
#include "common.h"
#include "ioconf.h"
#ifdef USE_NLS
#include <locale.h>
#include <libintl.h>
#define _(string) gettext(string)
#else
#define _(string) (string)
#endif
/* Number of decimal places */
extern int dplaces_nr;
/* Units (sectors, Bytes, kilobytes, etc.) */
char units[] = {'s', 'B', 'k', 'M', 'G', 'T', 'P', '?'};
/* Number of ticks per second */
unsigned long hz;
/* Number of bit shifts to convert pages to kB */
unsigned int kb_shift;
/* Colors strings */
char sc_percent_high[MAX_SGR_LEN] = C_BOLD_RED;
char sc_percent_low[MAX_SGR_LEN] = C_BOLD_MAGENTA;
char sc_zero_int_stat[MAX_SGR_LEN] = C_LIGHT_BLUE;
char sc_int_stat[MAX_SGR_LEN] = C_BOLD_BLUE;
char sc_item_name[MAX_SGR_LEN] = C_LIGHT_GREEN;
char sc_sa_restart[MAX_SGR_LEN] = C_LIGHT_RED;
char sc_sa_comment[MAX_SGR_LEN] = C_LIGHT_YELLOW;
char sc_normal[MAX_SGR_LEN] = C_NORMAL;
/*
* Type of persistent device names in lowercase letters
* (e.g. "uuid", "label", "path"...) Used in sar and iostat.
*/
char persistent_name_type[MAX_FILE_LEN];
/*
***************************************************************************
* Print sysstat version number and exit.
***************************************************************************
*/
void print_version(void)
{
printf(_("sysstat version %s\n"), VERSION);
printf("(C) Sebastien Godard (sysstat <at> orange.fr)\n");
exit(0);
}
/*
***************************************************************************
* Get local date and time.
*
* IN:
* @d_off Day offset (number of days to go back in the past).
*
* OUT:
* @rectime Current local date and time.
*
* RETURNS:
* Value of time in seconds since the Epoch.
***************************************************************************
*/
time_t get_localtime(struct tm *rectime, int d_off)
{
time_t timer;
timer = __time(NULL);
timer -= SEC_PER_DAY * d_off;
localtime_r(&timer, rectime);
return timer;
}
/*
***************************************************************************
* Get date and time expressed in UTC.
*
* IN:
* @d_off Day offset (number of days to go back in the past).
*
* OUT:
* @rectime Current date and time expressed in UTC.
*
* RETURNS:
* Value of time in seconds since the Epoch.
***************************************************************************
*/
time_t get_gmtime(struct tm *rectime, int d_off)
{
time_t timer;
timer = __time(NULL);
timer -= SEC_PER_DAY * d_off;
gmtime_r(&timer, rectime);
return timer;
}
/*
***************************************************************************
* Get date and time and take into account <ENV_TIME_DEFTM> variable.
*
* IN:
* @d_off Day offset (number of days to go back in the past).
*
* OUT:
* @rectime Current date and time.
*
* RETURNS:
* Value of time in seconds since the Epoch.
***************************************************************************
*/
time_t get_time(struct tm *rectime, int d_off)
{
static int utc = 0;
char *e;
if (!utc) {
/* Read environment variable value once */
if ((e = __getenv(ENV_TIME_DEFTM)) != NULL) {
utc = !strcmp(e, K_UTC);
}
utc++;
}
if (utc == 2)
return get_gmtime(rectime, d_off);
else
return get_localtime(rectime, d_off);
}
#ifdef USE_NLS
/*
***************************************************************************
* Init National Language Support.
***************************************************************************
*/
void init_nls(void)
{
setlocale(LC_MESSAGES, "");
setlocale(LC_CTYPE, "");
setlocale(LC_TIME, "");
setlocale(LC_NUMERIC, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
}
#endif
/*
***************************************************************************
* Test whether given name is a device or a partition, using sysfs.
*
* IN:
* @sysdev sysfs location.
* @name Device or partition name.
* @allow_virtual TRUE if virtual devices are also accepted.
* The device is assumed to be virtual if no
* /sys/block/<device>/device link exists.
*
* RETURNS:
* TRUE if @name is a device, and FALSE if it's a partition.
***************************************************************************
*/
int is_device(char *sysdev, char *name, int allow_virtual)
{
char syspath[PATH_MAX];
char *slash;
/* Some devices may have a slash in their name (eg. cciss/c0d0...) */
while ((slash = strchr(name, '/'))) {
*slash = '!';
}
snprintf(syspath, sizeof(syspath), "%s/%s/%s%s", sysdev, __BLOCK, name,
allow_virtual ? "" : "/device");
return !(access(syspath, F_OK));
}
/*
***************************************************************************
* Get page shift in kB.
***************************************************************************
*/
void get_kb_shift(void)
{
int shift = 0;
long size;
/* One can also use getpagesize() to get the size of a page */
if ((size = sysconf(_SC_PAGESIZE)) == -1) {
perror("sysconf");
}
size >>= 10; /* Assume that a page has a minimum size of 1 kB */
while (size > 1) {
shift++;
size >>= 1;
}
kb_shift = (unsigned int) shift;
}
/*
***************************************************************************
* Get number of clock ticks per second.
***************************************************************************
*/
void get_HZ(void)
{
long ticks;
if ((ticks = sysconf(_SC_CLK_TCK)) == -1) {
perror("sysconf");
}
hz = (unsigned long) ticks;
}
/*
***************************************************************************
* Unhandled situation: Panic and exit. Should never happen.
*
* IN:
* @function Function name where situation occured.
* @error_code Error code.
***************************************************************************
*/
void sysstat_panic(const char *function, int error_code)
{
fprintf(stderr, "sysstat: %s[%d]: Internal error...\n",
function, error_code);
exit(1);
}
/*
***************************************************************************
* Extract WWWN identifiers from filename, as read from /dev/disk/by-id.
*
* Sample valid names read from /dev/disk/by-id:
* wwn-0x5000cca369f193ac
* wwn-0x5000cca369f193ac-part12
* wwn-0x600605b00a2bdf00242b28c10dcb1999
* wwn-0x600605b00a2bdf00242b28c10dcb1999-part2
*
* WWN ids like these ones are ignored:
* wwn-0x5438850077615869953x
* wwn-0x5438850077615869953x-part1
*
* IN:
* @name Filename read from /dev/disk/by-id.
*
* OUT:
* @wwn WWN identifier (8 or 16 hex characters).
* @part-nr Partition number if applicable.
*
* RETURNS:
* 0 on success, -1 otherwise.
***************************************************************************
*/
int extract_wwnid(char *name, unsigned long long *wwn, unsigned int *part_nr)
{
char id[17];
char *s;
int wwnlen;
*wwn = *(wwn + 1) = 0ULL;
*part_nr = 0;
/* Check name */
if (((wwnlen = strlen(name)) < 22) || (strncmp(name, "wwn-0x", 6)))
return -1;
/* Is there a partition number? */
if ((s = strstr(name, "-part")) != NULL) {
/* Yes: Get partition number */
if (sscanf(s + 5, "%u", part_nr) == 0)
return -1;
wwnlen = s - name - 6;
}
else {
wwnlen -= 6; /* Don't count "wwn-0x" */
}
/* Check WWN length */
if ((wwnlen != 16) && (wwnlen != 32))
return -1;
/* Extract first 16 hex chars of WWN */
strncpy(id, name + 6, 16);
id[16] = '\0';
if (sscanf(id, "%llx", wwn) == 0)
return -1;
if (strlen(name) < 38)
/* This is a short (16 hex chars) WWN id */
return 0;
/* Extract second part of WWN */
if (sscanf(name + 22, "%llx", wwn + 1) == 0)
return -1;
return 0;
}
/*
***************************************************************************
* Get WWWN identifiers from a pretty filename using links present in
* /dev/disk/by-id directory.
*
* IN:
* @pretty Pretty name (e.g. sda, sdb3...).
*
* OUT:
* @wwn WWN identifier (8 or 16 hex characters).
* @part-nr Partition number if applicable.
*
* RETURNS:
* 0 on success, -1 otherwise.
***************************************************************************
*/
int get_wwnid_from_pretty(char *pretty, unsigned long long *wwn, unsigned int *part_nr)
{
DIR *dir;
struct dirent *drd;
ssize_t r;
char link[PATH_MAX], target[PATH_MAX], wwn_name[FILENAME_MAX];
char *name;
int rc = -1;
/* Open /dev/disk/by-id directory */
if ((dir = opendir(DEV_DISK_BY_ID)) == NULL)
return -1;
/* Get current id */
while ((drd = readdir(dir)) != NULL) {
if (strncmp(drd->d_name, "wwn-0x", 6))
continue;
/* Get absolute path for current persistent name */
snprintf(link, PATH_MAX, "%s/%s", DEV_DISK_BY_ID, drd->d_name);
/* Persistent name is usually a symlink: Read it... */
r = readlink(link, target, PATH_MAX);
if ((r <= 0) || (r >= PATH_MAX))
continue;
target[r] = '\0';
/* ... and get device pretty name it points at */
name = basename(target);
if (!name || (name[0] == '\0'))
continue;
if (!strncmp(name, pretty, FILENAME_MAX)) {
/* We have found pretty name for current persistent one */
strncpy(wwn_name, drd->d_name, MINIMUM(sizeof(wwn_name), sizeof(drd->d_name)));
wwn_name[sizeof(wwn_name) - 1] = '\0';
/* Try to extract WWN */
if (!extract_wwnid(wwn_name, wwn, part_nr)) {
/* WWN successfully extracted */
rc = 0;
break;
}
}
}
/* Close directory */
closedir(dir);
return rc;
}
/*
***************************************************************************
* Check if a directory exists.
*
* IN:
* @dirname Name of the directory.
*
* RETURNS:
* TRUE if @dirname is actually an existing directory.
***************************************************************************
*/
int check_dir(char *dirname)
{
struct stat sb;
if (!stat(dirname, &sb) && S_ISDIR(sb.st_mode))
return 1;
return 0;
}
/*
* **************************************************************************
* Check if the multiplication of the 3 values may be greater than UINT_MAX.
*
* IN:
* @val1 First value.
* @val2 Second value.
* @val3 Third value.
***************************************************************************
*/
void check_overflow(unsigned int val1, unsigned int val2,
unsigned int val3)
{
if ((val1 != 0) && (val2 != 0) && (val3 != 0) &&
(((unsigned long long) UINT_MAX / (unsigned long long) val1 <
(unsigned long long) val2) ||
((unsigned long long) UINT_MAX / ((unsigned long long) val1 * (unsigned long long) val2) <
(unsigned long long) val3))) {
#ifdef DEBUG
fprintf(stderr, "%s: Overflow detected (%u,%u,%u). Aborting...\n",
__FUNCTION__, val1, val2, val3);
#endif
exit(4);
}
}
#ifndef SOURCE_SADC
/*
***************************************************************************
* Read /proc/devices file and get device-mapper major number.
* If device-mapper entry is not found in file, assume it's not active.
*
* RETURNS:
* Device-mapper major number.
***************************************************************************
*/
unsigned int get_devmap_major(void)
{
FILE *fp;
char line[128];
/*
* Linux uses 12 bits for the major number,
* so this shouldn't match any real device.
*/
unsigned int dm_major = ~0U;
if ((fp = fopen(DEVICES, "r")) == NULL)
return dm_major;
while (fgets(line, sizeof(line), fp) != NULL) {
if (strstr(line, "device-mapper")) {
/* Read device-mapper major number */
sscanf(line, "%u", &dm_major);
}
}
fclose(fp);
return dm_major;
}
/*
***************************************************************************
* Returns whether S_TIME_FORMAT is set to ISO.
*
* RETURNS:
* TRUE if S_TIME_FORMAT is set to ISO, or FALSE otherwise.
***************************************************************************
*/
int is_iso_time_fmt(void)
{
static int is_iso = -1;
char *e;
if (is_iso < 0) {
is_iso = (((e = __getenv(ENV_TIME_FMT)) != NULL) && !strcmp(e, K_ISO));
}
return is_iso;
}
/*
***************************************************************************
* Print tabulations
*
* IN:
* @nr_tab Number of tabs to print.
***************************************************************************
*/
void prtab(int nr_tab)
{
int i;
for (i = 0; i < nr_tab; i++) {
printf("\t");
}
}
/*
***************************************************************************
* printf() function modified for XML-like output. Don't print a CR at the
* end of the line.
*
* IN:
* @nr_tab Number of tabs to print.
* @fmtf printf() format.
***************************************************************************
*/
void xprintf0(int nr_tab, const char *fmtf, ...)
{
static char buf[1024];
va_list args;
va_start(args, fmtf);
vsnprintf(buf, sizeof(buf), fmtf, args);
va_end(args);
prtab(nr_tab);
printf("%s", buf);
}
/*
***************************************************************************
* printf() function modified for XML-like output. Print a CR at the end of
* the line.
*
* IN:
* @nr_tab Number of tabs to print.
* @fmtf printf() format.
***************************************************************************
*/
void xprintf(int nr_tab, const char *fmtf, ...)
{
static char buf[1024];
va_list args;
va_start(args, fmtf);
vsnprintf(buf, sizeof(buf), fmtf, args);
va_end(args);
prtab(nr_tab);
printf("%s\n", buf);
}
/*
***************************************************************************
* Get report date as a string of characters.
*
* IN:
* @rectime Date to display (don't use time fields).
* @cur_date String where date will be saved.
* @sz Max size of cur_date string.
*
* OUT:
* @cur_date Date (string format).
*
* RETURNS:
* TRUE if S_TIME_FORMAT is set to ISO, or FALSE otherwise.
***************************************************************************
*/
int set_report_date(struct tm *rectime, char cur_date[], int sz)
{
if (rectime == NULL) {
strncpy(cur_date, "?/?/?", sz);
cur_date[sz - 1] = '\0';
}
else if (is_iso_time_fmt()) {
strftime(cur_date, sz, "%Y-%m-%d", rectime);
return 1;
}
else {
strftime(cur_date, sz, "%x", rectime);
}
return 0;
}
/*
***************************************************************************
* Print banner.
*
* IN:
* @rectime Date to display (don't use time fields).
* @sysname System name to display.
* @release System release number to display.
* @nodename Hostname to display.
* @machine Machine architecture to display.
* @cpu_nr Number of CPU.
* @format Set to FALSE for (default) plain output, and to TRUE for
* JSON format output.
*
* RETURNS:
* TRUE if S_TIME_FORMAT is set to ISO, or FALSE otherwise.
***************************************************************************
*/
int print_gal_header(struct tm *rectime, char *sysname, char *release,
char *nodename, char *machine, int cpu_nr, int format)
{
char cur_date[TIMESTAMP_LEN];
int rc = 0;
rc = set_report_date(rectime, cur_date, sizeof(cur_date));
if (format == PLAIN_OUTPUT) {
/* Plain output */
printf("%s %s (%s) \t%s \t_%s_\t(%d CPU)\n", sysname, release, nodename,
cur_date, machine, cpu_nr);
}
else {
/* JSON output */
xprintf(0, "{\"sysstat\": {");
xprintf(1, "\"hosts\": [");
xprintf(2, "{");
xprintf(3, "\"nodename\": \"%s\",", nodename);
xprintf(3, "\"sysname\": \"%s\",", sysname);
xprintf(3, "\"release\": \"%s\",", release);
xprintf(3, "\"machine\": \"%s\",", machine);
xprintf(3, "\"number-of-cpus\": %d,", cpu_nr);
xprintf(3, "\"date\": \"%s\",", cur_date);
xprintf(3, "\"statistics\": [");
}
return rc;
}
/*
***************************************************************************
* Get number of rows for current window.
*
* RETURNS:
* Number of rows.
***************************************************************************
*/
int get_win_height(void)
{
struct winsize win;
/*
* This default value will be used whenever STDOUT
* is redirected to a pipe or a file
*/
int rows = 3600 * 24;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1) {
if (win.ws_row > 2) {
rows = win.ws_row - 2;
}
}
return rows;
}
/*
***************************************************************************
* Canonicalize and remove /dev from path name. If the device has a slash
* character in its name, replace it with a bang character ('!'), e.g.:
* cciss/c0d0 -> cciss!c0d0
* cciss/c0d0p1 -> cciss!c0d0p1
*
* IN:
* @name Device name (may begin with "/dev/" or can be a symlink).
*
* RETURNS:
* Device basename.
***************************************************************************
*/
char *device_name(char *name)
{
static char out[MAX_FILE_LEN];
char *resolved_name = NULL, *slash;
int i = 0;
/* realpath() creates new string, so we need to free it later */
resolved_name = __realpath(name, NULL);
/* If path doesn't exist, just return input */
if (!resolved_name) {
return name;
}
#ifdef DEBUG
fprintf(stderr, "Real pathname: %s (%s)\n", resolved_name, name);
#endif
if (!strncmp(resolved_name, "/dev/", 5)) {
i = 5;
}
strncpy(out, resolved_name + i, sizeof(out));
out[sizeof(out) - 1] = '\0';
/* Some devices may have a slash in their name (eg. cciss/c0d0...) */
while ((slash = strchr(out, '/'))) {
*slash = '!';
}
free(resolved_name);
return out;
}
/*
***************************************************************************
* Workaround for CPU counters read from /proc/stat: Dyn-tick kernels
* have a race issue that can make those counters go backward.
***************************************************************************
*/
double ll_sp_value(unsigned long long value1, unsigned long long value2,
unsigned long long itv)
{
if (value2 < value1)
return (double) 0;
else
return SP_VALUE(value1, value2, itv);
}
/*
***************************************************************************
* Compute time interval.
*
* IN:
* @prev_uptime Previous uptime value (in jiffies or 1/100th of a second).
* @curr_uptime Current uptime value (in jiffies or 1/100th of a second).
*
* RETURNS:
* Interval of time in jiffies or 1/100th of a second.
***************************************************************************
*/
unsigned long long get_interval(unsigned long long prev_uptime,
unsigned long long curr_uptime)
{
unsigned long long itv;
/* prev_time=0 when displaying stats since system startup */
itv = curr_uptime - prev_uptime;
if (!itv) { /* Paranoia checking */
itv = 1;
}
return itv;
}
/*
***************************************************************************
* Count number of bits set in an array.
*
* IN:
* @ptr Pointer to array.
* @size Size of array in bytes.
*
* RETURNS:
* Number of bits set in the array.
***************************************************************************
*/
int count_bits(void *ptr, int size)
{
int nr = 0, i, k;
char *p;
p = ptr;
for (i = 0; i < size; i++, p++) {
k = 0x80;
while (k) {
if (*p & k)
nr++;
k >>= 1;
}
}
return nr;
}
/*
***************************************************************************
* Convert in-place input string to lowercase.
*
* IN:
* @str String to be converted.
*
* OUT:
* @str String in lowercase.
*
* RETURNS:
* String in lowercase.
***************************************************************************
*/
char *strtolower(char *str)
{
char *cp = str;
while (*cp) {
*cp = tolower(*cp);
cp++;
}
return(str);
}
/*
***************************************************************************
* Get persistent type name directory from type.
*
* IN:
* @type Persistent type name (UUID, LABEL, etc.)
*
* RETURNS:
* Path to the persistent type name directory, or NULL if access is denied
* or strings have been truncated.
***************************************************************************
*/
char *get_persistent_type_dir(char *type)
{
static char dir[PATH_MAX];
int n;
n = snprintf(dir, sizeof(dir), "%s-%s", DEV_DISK_BY, type);
if ((n >= sizeof(dir)) || access(dir, R_OK)) {
return (NULL);
}
return (dir);
}
/*
***************************************************************************
* Get persistent name absolute path.
*
* IN:
* @name Persistent name.
*
* RETURNS:
* Path to the persistent name, or NULL if file doesn't exist or strings
* have been truncated.
***************************************************************************
*/
char *get_persistent_name_path(char *name)
{
static char path[PATH_MAX];
int n;
n = snprintf(path, sizeof(path), "%s/%s",
get_persistent_type_dir(persistent_name_type), name);
if ((n >= sizeof(path)) || access(path, F_OK)) {
return (NULL);
}
return (path);
}
/*
***************************************************************************
* Get files from persistent type name directory.
*
* RETURNS:
* List of files in the persistent type name directory in alphabetical order.
***************************************************************************
*/
char **get_persistent_names(void)
{
int n, i, k = 0;
char *dir;
char **files = NULL;
struct dirent **namelist;
/* Get directory name for selected persistent type */
dir = get_persistent_type_dir(persistent_name_type);
if (!dir)
return (NULL);
n = scandir(dir, &namelist, NULL, alphasort);
if (n < 0)
return (NULL);
/* If directory is empty, it contains 2 entries: "." and ".." */
if (n <= 2)
/* Free list and return NULL */
goto free_list;
/* Ignore the "." and "..", but keep place for one last NULL. */
files = (char **) calloc(n - 1, sizeof(char *));
if (!files)
goto free_list;
/*
* i is for traversing namelist, k is for files.
* i != k because we are ignoring "." and ".." entries.
*/
for (i = 0; i < n; i++) {
/* Ignore "." and ".." */
if (!strcmp(".", namelist[i]->d_name) ||
!strcmp("..", namelist[i]->d_name))
continue;
files[k] = (char *) calloc(strlen(namelist[i]->d_name) + 1, sizeof(char));
if (!files[k])
continue;
strcpy(files[k++], namelist[i]->d_name);
}
files[k] = NULL;
free_list:
for (i = 0; i < n; i++) {
free(namelist[i]);
}
free(namelist);
return (files);
}
/*
***************************************************************************
* Get persistent name from pretty name.
*
* IN:
* @pretty Pretty name (e.g. sda, sda1, ..).
*
* RETURNS:
* Persistent name.
***************************************************************************
*/
char *get_persistent_name_from_pretty(char *pretty)
{
int i = -1;
ssize_t r;
char *link, *name;
char **persist_names;
char target[PATH_MAX];
static char persist_name[FILENAME_MAX];
persist_name[0] = '\0';
/* Get list of files from persistent type name directory */
persist_names = get_persistent_names();
if (!persist_names)
return (NULL);
while (persist_names[++i]) {
/* Get absolute path for current persistent name */
link = get_persistent_name_path(persist_names[i]);
if (!link)
continue;
/* Persistent name is usually a symlink: Read it... */
r = readlink(link, target, PATH_MAX);
if ((r <= 0) || (r >= PATH_MAX))
continue;
target[r] = '\0';
/* ... and get device pretty name it points at */
name = basename(target);
if (!name || (name[0] == '\0'))
continue;
if (!strncmp(name, pretty, FILENAME_MAX)) {
/* We have found pretty name for current persistent one */
strncpy(persist_name, persist_names[i], sizeof(persist_name));
persist_name[sizeof(persist_name) - 1] = '\0';
break;
}
}
i = -1;
while (persist_names[++i]) {
free (persist_names[i]);
}
free (persist_names);
if (strlen(persist_name) <= 0)
return (NULL);
return persist_name;
}
/*
***************************************************************************
* Get pretty name (sda, sda1...) from persistent name.
*
* IN:
* @persistent Persistent name.
*
* RETURNS:
* Pretty name.
***************************************************************************
*/
char *get_pretty_name_from_persistent(char *persistent)
{
ssize_t r;
char *link, *pretty, target[PATH_MAX];
/* Get absolute path for persistent name */
link = get_persistent_name_path(persistent);
if (!link)
return (NULL);
/* Persistent name is usually a symlink: Read it... */
r = readlink(link, target, PATH_MAX);
if ((r <= 0) || (r >= PATH_MAX))
return (NULL);
target[r] = '\0';
/* ... and get device pretty name it points at */
pretty = basename(target);
if (!pretty || (pretty[0] == '\0'))
return (NULL);
return pretty;
}
/*
* **************************************************************************
* Try to get device real name from sysfs tree.
*
* IN:
* @major Major number of the device.
* @minor Minor number of the device.
*
* RETURNS:
* The name of the device, which may be the real name (as it appears in /dev)
* or NULL.
***************************************************************************
*/
char *get_devname_from_sysfs(unsigned int major, unsigned int minor)
{
static char link[256], target[PATH_MAX];
char *devname;
ssize_t r;
snprintf(link, sizeof(link), "%s/%u:%u", SYSFS_DEV_BLOCK, major, minor);
/* Get full path to device knowing its major and minor numbers */
r = readlink(link, target, PATH_MAX);
if (r <= 0 || r >= PATH_MAX)
return (NULL);
target[r] = '\0';
/* Get device name */
devname = basename(target);
if (!devname || strnlen(devname, FILENAME_MAX) == 0) {
return (NULL);
}
return (devname);
}
/*
* **************************************************************************
* Get device real name if possible.
*
* IN:
* @major Major number of the device.
* @minor Minor number of the device.
*
* RETURNS:
* The name of the device, which may be the real name (as it appears in /dev)
* or a string with the following format devM-n.
***************************************************************************
*/
char *get_devname(unsigned int major, unsigned int minor)
{
static char buf[32];
char *name;
name = get_devname_from_sysfs(major, minor);
if (name != NULL)
return (name);
name = ioc_name(major, minor);
if ((name != NULL) && strcmp(name, K_NODEV))
return (name);
snprintf(buf, sizeof(buf), "dev%u-%u", major, minor);
return (buf);
}
/*
* **************************************************************************
* Get device name (whether pretty-printed, persistent or not).
*
* IN:
* @major Major number of the device.
* @minor Minor number of the device.
* @wwn WWN identifier of the device (0 if unknown).
* @part_nr Partition number (0 if unknown).
* @disp_devmap_name Display device mapper name.
* @disp_persist_name Display persistent name of the device.
* @use_stable_id Display stable-across-reboots name.
* @dflt_name Device name to use by default (if existent).
*
* RETURNS:
* The name of the device.
***************************************************************************
*/
char *get_device_name(unsigned int major, unsigned int minor, unsigned long long wwn[],
unsigned int part_nr, unsigned int disp_devmap_name,
unsigned int disp_persist_name, unsigned int use_stable_id,
char *dflt_name)
{
static unsigned int dm_major = 0;
char *dev_name = NULL, *persist_dev_name = NULL, *bang;
static char sid[64], dname[MAX_NAME_LEN];
char xsid[32] = "", pn[16] = "";
if (disp_persist_name) {
persist_dev_name = get_persistent_name_from_pretty(get_devname(major, minor));
}
if (persist_dev_name) {
dev_name = persist_dev_name;
}
else {
if (use_stable_id && (wwn[0] != 0)) {
if (wwn[1] != 0) {
sprintf(xsid, "%016llx", wwn[1]);
}
if (part_nr) {
sprintf(pn, "-%d", part_nr);
}
snprintf(sid, sizeof(sid), "%#016llx%s%s", wwn[0], xsid, pn);
dev_name = sid;
}
else if (disp_devmap_name) {
if (!dm_major) {
dm_major = get_devmap_major();
}
if (major == dm_major) {
dev_name = transform_devmapname(major, minor);
}
}
if (!dev_name) {
if (dflt_name) {
dev_name = dflt_name;
}
else {
dev_name = get_devname(major, minor);
}
}
}
strncpy(dname, dev_name, sizeof(dname));
dname[sizeof(dname) - 1] = '\0';
while ((bang = strchr(dname, '!'))) {
/*
* Some devices may have had a slash replaced with
* a bang character (eg. cciss!c0d0...)
* Restore their original names.
*/
*bang = '/';
}
return dname;
}
/*
***************************************************************************
* Init color strings.
***************************************************************************
*/
void init_colors(void)
{
char *e, *p;
int len;
/* Read S_COLORS environment variable */
if ((e = __getenv(ENV_COLORS)) == NULL
? !isatty(STDOUT_FILENO)
: (!strcmp(e, C_NEVER) ||
(strcmp(e, C_ALWAYS) && !isatty(STDOUT_FILENO)))) {
/*
* Environment variable not set and stdout is not a terminal,
* or set to "never",
* or set to "auto" and stdout is not a terminal:
* Unset color strings.
*/
strcpy(sc_percent_high, "");
strcpy(sc_percent_low, "");
strcpy(sc_zero_int_stat, "");
strcpy(sc_int_stat, "");
strcpy(sc_item_name, "");
strcpy(sc_sa_comment, "");
strcpy(sc_sa_restart, "");
strcpy(sc_normal, "");
return;
}
/* Read S_COLORS_SGR environment variable */
if ((e = __getenv(ENV_COLORS_SGR)) == NULL)
/* Environment variable not set */
return;
for (p = strtok(e, ":"); p; p =strtok(NULL, ":")) {
len = strlen(p);
if ((len > 7) || (len < 3) || (*(p + 1) != '=') ||
(strspn(p + 2, ";0123456789") != (len - 2)))
/* Ignore malformed codes */
continue;
switch (*p) {
case 'H':
snprintf(sc_percent_high, MAX_SGR_LEN, "\e[%sm", p + 2);
break;
case 'M':
snprintf(sc_percent_low, MAX_SGR_LEN, "\e[%sm", p + 2);
break;
case 'Z':
snprintf(sc_zero_int_stat, MAX_SGR_LEN, "\e[%sm", p + 2);
break;
case 'N':
snprintf(sc_int_stat, MAX_SGR_LEN, "\e[%sm", p + 2);
break;
case 'I':
snprintf(sc_item_name, MAX_SGR_LEN, "\e[%sm", p + 2);
break;
case 'C':
snprintf(sc_sa_comment, MAX_SGR_LEN, "\e[%sm", p + 2);
break;
case 'R':
snprintf(sc_sa_restart, MAX_SGR_LEN, "\e[%sm", p + 2);
break;
}
}
}
/*
***************************************************************************
* Print a value in human readable format. Such a value is a decimal number
* followed by a unit (B, k, M, etc.)
*
* IN:
* @unit Default value unit.
* @dval Value to print.
* @wi Output width.
***************************************************************************
*/
void cprintf_unit(int unit, int wi, double dval)
{
if (wi < 4) {
/* E.g. 1.3M */
wi = 4;
}
if (!unit) {
/* Value is a number of sectors. Convert it to kB */
dval /= 2;
unit = 2;
}
while (dval >= 1024) {
dval /= 1024;
unit++;
}
printf(" %*.*f", wi - 1, dplaces_nr ? 1 : 0, dval);
printf("%s", sc_normal);
/* Display unit */
if (unit >= NR_UNITS) {
unit = NR_UNITS - 1;
}
printf("%c", units[unit]);
}
/*
***************************************************************************
* Print 64 bit unsigned values using colors, possibly followed by a unit.
*
* IN:
* @unit Default values unit. -1 if no unit should be displayed.
* @num Number of values to print.
* @wi Output width.
***************************************************************************
*/
void cprintf_u64(int unit, int num, int wi, ...)
{
int i;
uint64_t val;
va_list args;
va_start(args, wi);
for (i = 0; i < num; i++) {
val = va_arg(args, unsigned long long);
if (!val) {
printf("%s", sc_zero_int_stat);
}
else {
printf("%s", sc_int_stat);
}
if (unit < 0) {
printf(" %*"PRIu64, wi, val);
printf("%s", sc_normal);
}
else {
cprintf_unit(unit, wi, (double) val);
}
}
va_end(args);
}
/*
***************************************************************************
* Print hex values using colors.
*
* IN:
* @num Number of values to print.
* @wi Output width.
***************************************************************************
*/
void cprintf_x(int num, int wi, ...)
{
int i;
unsigned int val;
va_list args;
va_start(args, wi);
for (i = 0; i < num; i++) {
val = va_arg(args, unsigned int);
printf("%s", sc_int_stat);
printf(" %*x", wi, val);
printf("%s", sc_normal);
}
va_end(args);
}
/*
***************************************************************************
* Print "double" statistics values using colors, possibly followed by a
* unit.
*
* IN:
* @unit Default values unit. -1 if no unit should be displayed.
* @num Number of values to print.
* @wi Output width.
* @wd Number of decimal places.
***************************************************************************
*/
void cprintf_f(int unit, int num, int wi, int wd, ...)
{
int i;
double val, lim = 0.005;;
va_list args;
/*
* If there are decimal places then get the value
* entered on the command line (if existing).
*/
if ((wd > 0) && (dplaces_nr >= 0)) {
wd = dplaces_nr;
}
/* Update limit value according to number of decimal places */
if (wd == 1) {
lim = 0.05;
}
va_start(args, wd);
for (i = 0; i < num; i++) {
val = va_arg(args, double);
if (((wd > 0) && (val < lim) && (val > (lim * -1))) ||
((wd == 0) && (val <= 0.5) && (val >= -0.5))) { /* "Round half to even" law */
printf("%s", sc_zero_int_stat);
}
else {
printf("%s", sc_int_stat);
}
if (unit < 0) {
printf(" %*.*f", wi, wd, val);
printf("%s", sc_normal);
}
else {
cprintf_unit(unit, wi, val);
}
}
va_end(args);
}
/*
***************************************************************************
* Print "percent" statistics values using colors.
*
* IN:
* @human Set to > 0 if a percent sign (%) shall be displayed after
* the value.
* @num Number of values to print.
* @wi Output width.
* @wd Number of decimal places.
***************************************************************************
*/
void cprintf_pc(int human, int num, int wi, int wd, ...)
{
int i;
double val, lim = 0.005;
va_list args;
/*
* If there are decimal places then get the value
* entered on the command line (if existing).
*/
if ((wd > 0) && (dplaces_nr >= 0)) {
wd = dplaces_nr;
}
/*
* If a percent sign is to be displayed, then there will be
* zero (or one) decimal place.
*/
if (human > 0) {
if (wi < 4) {
/* E.g., 100% */
wi = 4;
}
/* Keep one place for the percent sign */
wi -= 1;
if (wd > 1) {
wd -= 1;
}
}
/* Update limit value according to number of decimal places */
if (wd == 1) {
lim = 0.05;
}
va_start(args, wd);
for (i = 0; i < num; i++) {
val = va_arg(args, double);
if (val >= PERCENT_LIMIT_HIGH) {
printf("%s", sc_percent_high);
}
else if (val >= PERCENT_LIMIT_LOW) {
printf("%s", sc_percent_low);
}
else if (((wd > 0) && (val < lim)) ||
((wd == 0) && (val <= 0.5))) { /* "Round half to even" law */
printf("%s", sc_zero_int_stat);
}
else {
printf("%s", sc_int_stat);
}
printf(" %*.*f", wi, wd, val);
printf("%s", sc_normal);
if (human > 0) printf("%%");
}
va_end(args);
}
/*
***************************************************************************
* Print item name using selected color.
* Only one name can be displayed. Name can be an integer or a string.
*
* IN:
* @type 0 if name is an int, 1 if name is a string
* @format Output format.
* @item_string Item name (given as a string of characters).
* @item_int Item name (given as an integer value).
***************************************************************************
*/
void cprintf_in(int type, char *format, char *item_string, int item_int)
{
printf("%s", sc_item_name);
if (type) {
printf(format, item_string);
}
else {
printf(format, item_int);
}
printf("%s", sc_normal);
}
/*
***************************************************************************
* Print a string using selected color.
*
* IN:
* @type Type of string to display.
* @format Output format.
* @string String to display.
***************************************************************************
*/
void cprintf_s(int type, char *format, char *string)
{
if (type == IS_STR) {
printf("%s", sc_int_stat);
}
else if (type == IS_ZERO) {
printf("%s", sc_zero_int_stat);
}
/* IS_RESTART and IS_DEBUG are the same value */
else if (type == IS_RESTART) {
printf("%s", sc_sa_restart);
}
else {
/* IS_COMMENT */
printf("%s", sc_sa_comment);
}
printf(format, string);
printf("%s", sc_normal);
}
/*
***************************************************************************
* Parse a string containing a numerical value (e.g. CPU or IRQ number).
* The string should contain only one value, not a range of values.
*
* IN:
* @s String to parse.
* @max_val Upper limit that value should not reach.
*
* OUT:
* @val Value, or -1 if the string @s was empty.
*
* RETURNS:
* 0 if the value has been properly read, 1 otherwise.
***************************************************************************
*/
int parse_valstr(char *s, int max_val, int *val)
{
if (!strlen(s)) {
*val = -1;
return 0;
}
if (strspn(s, DIGITS) != strlen(s))
return 1;
*val = atoi(s);
if ((*val < 0) || (*val >= max_val))
return 1;
return 0;
}
/*
***************************************************************************
* Parse string containing a single value or a range of values
* (e.g. "0,2-5,10-").
*
* IN:
* @t String to parse.
* @max_val Upper limit that value should not reach.
*
* OUT:
* @val_low Low value in range
* @val High value in range. @val_low and @val are the same if it's
* a single value.
*
* RETURNS:
* 0 on success, 1 otherwise.
***************************************************************************
*/
int parse_range_values(char *t, int max_val, int *val_low, int *val)
{
char *s, *valstr, range[16];
/* Parse value or range of values */
strncpy(range, t, 16);
range[15] = '\0';
valstr = t;
if ((s = strchr(range, '-')) != NULL) {
/* Possible range of values */
*s = '\0';
if (parse_valstr(range, max_val, val_low) || (*val_low < 0))
return 1;
valstr = s + 1;
}
if (parse_valstr(valstr, max_val, val))
return 1;
if (s && *val < 0) {
/* Range of values with no upper limit (e.g. "3-") */
*val = max_val - 1;
}
if ((!s && (*val < 0)) || (s && (*val < *val_low)))
/*
* Individual value: string cannot be empty.
* Range of values: n-m: m can be empty (e.g. "3-") but
* cannot be lower than n.
*/
return 1;
if (!s) {
*val_low = *val;
}
return 0;
}
/*
***************************************************************************
* Parse string containing a set of coma-separated values or ranges of
* values (e.g. "0,2-5,10-"). The ALL keyword is allowed and indicate that
* all possible values are selected.
*
* IN:
* @strargv Current argument in list to parse.
* @bitmap Bitmap whose contents will indicate which values have been
* selected.
* @max_val Upper limit that value should not reach.
* @__K_VALUE0 Keyword corresponding to the first bit in bitmap (e.g "all",
* "SUM"...)
*
* OUT:
* @bitmap Bitmap updated with selected values.
*
* RETURNS:
* 0 on success, 1 otherwise.
***************************************************************************
*/
int parse_values(char *strargv, unsigned char bitmap[], int max_val, const char *__K_VALUE0)
{
int i, val_low, val;
char *t;
if (!strcmp(strargv, K_ALL)) {
/* Set bit for every possible values (CPU, IRQ, etc.) */
memset(bitmap, ~0, BITMAP_SIZE(max_val));
return 0;
}
for (t = strtok(strargv, ","); t; t = strtok(NULL, ",")) {
if (!strcmp(t, __K_VALUE0)) {
/*
* Set bit 0 in bitmap. This may correspond
* to CPU "all" or IRQ "SUM" for example.
*/
bitmap[0] |= 1;
}
else {
/* Parse value or range of values */
if (parse_range_values(t, max_val, &val_low, &val))
return 1;
for (i = val_low; i <= val; i++) {
bitmap[(i + 1) >> 3] |= 1 << ((i + 1) & 0x07);
}
}
}
return 0;
}
#endif /* SOURCE_SADC undefined */
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/openkylin/sysstat.git
git@gitee.com:openkylin/sysstat.git
openkylin
sysstat
sysstat
openkylin/yangtze

搜索帮助