2 * sar, sadc, sadf, mpstat and iostat common routines.
3 * (C) 1999-2022 by Sebastien GODARD (sysstat <at> orange.fr)
5 ***************************************************************************
6 * This program is free software; you can redistribute it and/or modify it *
7 * under the terms of the GNU General Public License as published by the *
8 * Free Software Foundation; either version 2 of the License, or (at your *
9 * option) any later version. *
11 * This program is distributed in the hope that it will be useful, but *
12 * WITHOUT ANY WARRANTY; without the implied warranty of MERCHANTABILITY *
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
16 * You should have received a copy of the GNU General Public License along *
17 * with this program; if not, write to the Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA *
19 ***************************************************************************
29 #include <unistd.h> /* For STDOUT_FILENO, among others */
30 #include <sys/ioctl.h>
31 #include <sys/types.h>
44 #define _(string) gettext(string)
46 #define _(string) (string)
49 /* Number of decimal places */
50 extern int dplaces_nr;
52 /* Units (sectors, Bytes, kilobytes, etc.) */
53 char units[] = {'s', 'B', 'k', 'M', 'G', 'T', 'P', '?'};
55 /* Number of ticks per second */
57 /* Number of bit shifts to convert pages to kB */
58 unsigned int kb_shift;
61 char sc_percent_high[MAX_SGR_LEN] = C_BOLD_RED;
62 char sc_percent_low[MAX_SGR_LEN] = C_BOLD_MAGENTA;
63 char sc_zero_int_stat[MAX_SGR_LEN] = C_LIGHT_BLUE;
64 char sc_int_stat[MAX_SGR_LEN] = C_BOLD_BLUE;
65 char sc_item_name[MAX_SGR_LEN] = C_LIGHT_GREEN;
66 char sc_sa_restart[MAX_SGR_LEN] = C_LIGHT_RED;
67 char sc_sa_comment[MAX_SGR_LEN] = C_LIGHT_YELLOW;
68 char sc_normal[MAX_SGR_LEN] = C_NORMAL;
71 * Type of persistent device names in lowercase letters
72 * (e.g. "uuid", "label", "path"...) Used in sar and iostat.
74 char persistent_name_type[MAX_FILE_LEN];
77 ***************************************************************************
78 * Print sysstat version number and exit.
79 ***************************************************************************
81 void print_version(void)
83 printf(_("sysstat version %s\n"), VERSION);
84 printf("(C) Sebastien Godard (sysstat <at> orange.fr)\n");
89 ***************************************************************************
90 * Get date and time, expressed in UTC or in local time.
93 * @d_off Day offset (number of days to go back in the past).
94 * @utc TRUE if date and time shall be expressed in UTC.
97 * @rectime Current local date and time.
100 * Value of time in seconds since the Epoch (always in UTC)
101 ***************************************************************************
103 time_t get_xtime(struct tm *rectime, int d_off, int utc)
107 timer = __time(NULL);
108 timer -= SEC_PER_DAY * d_off;
111 /* Get date and time in UTC */
112 gmtime_r(&timer, rectime);
115 /* Get date and time in local time */
116 localtime_r(&timer, rectime);
123 ***************************************************************************
124 * Get date and time and take into account <ENV_TIME_DEFTM> variable.
127 * @d_off Day offset (number of days to go back in the past).
130 * @rectime Current date and time.
133 * Value of time in seconds since the Epoch.
134 ***************************************************************************
136 time_t get_time(struct tm *rectime, int d_off)
142 /* Read environment variable value once */
143 if ((e = __getenv(ENV_TIME_DEFTM)) != NULL) {
144 utc = !strcmp(e, K_UTC);
149 return get_xtime(rectime, d_off, utc == 2);
154 ***************************************************************************
155 * Init National Language Support.
156 ***************************************************************************
160 setlocale(LC_MESSAGES, "");
161 setlocale(LC_CTYPE, "");
162 setlocale(LC_TIME, "");
163 setlocale(LC_NUMERIC, "");
165 bindtextdomain(PACKAGE, LOCALEDIR);
171 ***************************************************************************
172 * Test whether given name is a device or a partition, using sysfs.
175 * @sysdev sysfs location.
176 * @name Device or partition name.
177 * @allow_virtual TRUE if virtual devices are also accepted.
178 * The device is assumed to be virtual if no
179 * /sys/block/<device>/device link exists.
182 * TRUE if @name is a device, and FALSE if it's a partition.
183 ***************************************************************************
185 int is_device(char *sysdev, char *name, int allow_virtual)
187 char syspath[PATH_MAX];
190 /* Some devices may have a slash in their name (eg. cciss/c0d0...) */
191 while ((slash = strchr(name, '/'))) {
194 snprintf(syspath, sizeof(syspath), "%s/%s/%s%s", sysdev, __BLOCK, name,
195 allow_virtual ? "" : "/device");
197 return !(access(syspath, F_OK));
201 ***************************************************************************
202 * Get page shift in kB.
203 ***************************************************************************
205 void get_kb_shift(void)
210 /* One can also use getpagesize() to get the size of a page */
211 if ((size = sysconf(_SC_PAGESIZE)) == -1) {
215 size >>= 10; /* Assume that a page has a minimum size of 1 kB */
222 kb_shift = (unsigned int) shift;
226 ***************************************************************************
227 * Get number of clock ticks per second.
228 ***************************************************************************
234 if ((ticks = sysconf(_SC_CLK_TCK)) == -1) {
238 hz = (unsigned long) ticks;
242 ***************************************************************************
243 * Unhandled situation: Panic and exit. Should never happen.
246 * @function Function name where situation occured.
247 * @error_code Error code.
248 ***************************************************************************
250 void sysstat_panic(const char *function, int error_code)
252 fprintf(stderr, "sysstat: %s[%d]: Internal error...\n",
253 function, error_code);
258 ***************************************************************************
259 * Extract WWWN identifiers from filename, as read from /dev/disk/by-id.
261 * Sample valid names read from /dev/disk/by-id:
262 * wwn-0x5000cca369f193ac
263 * wwn-0x5000cca369f193ac-part12
264 * wwn-0x600605b00a2bdf00242b28c10dcb1999
265 * wwn-0x600605b00a2bdf00242b28c10dcb1999-part2
267 * WWN ids like these ones are ignored:
268 * wwn-0x5438850077615869953x
269 * wwn-0x5438850077615869953x-part1
272 * @name Filename read from /dev/disk/by-id.
275 * @wwn WWN identifier (8 or 16 hex characters).
276 * @part-nr Partition number if applicable.
279 * 0 on success, -1 otherwise.
280 ***************************************************************************
282 int extract_wwnid(char *name, unsigned long long *wwn, unsigned int *part_nr)
288 *wwn = *(wwn + 1) = 0ULL;
292 if (((wwnlen = strlen(name)) < 22) || (strncmp(name, "wwn-0x", 6)))
295 /* Is there a partition number? */
296 if ((s = strstr(name, "-part")) != NULL) {
297 /* Yes: Get partition number */
298 if (sscanf(s + 5, "%u", part_nr) == 0)
300 wwnlen = s - name - 6;
303 wwnlen -= 6; /* Don't count "wwn-0x" */
306 /* Check WWN length */
307 if ((wwnlen != 16) && (wwnlen != 32))
310 /* Extract first 16 hex chars of WWN */
311 strncpy(id, name + 6, 16);
313 if (sscanf(id, "%llx", wwn) == 0)
316 if (strlen(name) < 38)
317 /* This is a short (16 hex chars) WWN id */
320 /* Extract second part of WWN */
321 if (sscanf(name + 22, "%llx", wwn + 1) == 0)
328 ***************************************************************************
329 * Get WWWN identifiers from a pretty filename using links present in
330 * /dev/disk/by-id directory.
333 * @pretty Pretty name (e.g. sda, sdb3...).
336 * @wwn WWN identifier (8 or 16 hex characters).
337 * @part-nr Partition number if applicable.
340 * 0 on success, -1 otherwise.
341 ***************************************************************************
343 int get_wwnid_from_pretty(char *pretty, unsigned long long *wwn, unsigned int *part_nr)
348 char link[PATH_MAX], target[PATH_MAX], wwn_name[FILENAME_MAX];
352 /* Open /dev/disk/by-id directory */
353 if ((dir = opendir(DEV_DISK_BY_ID)) == NULL)
357 while ((drd = readdir(dir)) != NULL) {
359 if (strncmp(drd->d_name, "wwn-0x", 6))
362 /* Get absolute path for current persistent name */
363 snprintf(link, PATH_MAX, "%s/%s", DEV_DISK_BY_ID, drd->d_name);
365 /* Persistent name is usually a symlink: Read it... */
366 r = readlink(link, target, PATH_MAX);
367 if ((r <= 0) || (r >= PATH_MAX))
372 /* ... and get device pretty name it points at */
373 name = basename(target);
374 if (!name || (name[0] == '\0'))
377 if (!strncmp(name, pretty, FILENAME_MAX)) {
378 /* We have found pretty name for current persistent one */
379 strncpy(wwn_name, drd->d_name, MINIMUM(sizeof(wwn_name), sizeof(drd->d_name)));
380 wwn_name[sizeof(wwn_name) - 1] = '\0';
382 /* Try to extract WWN */
383 if (!extract_wwnid(wwn_name, wwn, part_nr)) {
384 /* WWN successfully extracted */
391 /* Close directory */
398 ***************************************************************************
399 * Check if a directory exists.
402 * @dirname Name of the directory.
405 * TRUE if @dirname is actually an existing directory.
406 ***************************************************************************
408 int check_dir(char *dirname)
412 if (!stat(dirname, &sb) && S_ISDIR(sb.st_mode))
419 * **************************************************************************
420 * Check if the multiplication of the 3 values may be greater than UINT_MAX.
424 * @val2 Second value.
426 ***************************************************************************
428 void check_overflow(unsigned int val1, unsigned int val2,
431 if ((unsigned long long) val1 * (unsigned long long) val2 *
432 (unsigned long long) val3 > UINT_MAX) {
434 fprintf(stderr, "%s: Overflow detected (%llu). Aborting...\n",
435 __FUNCTION__, (unsigned long long) val1 * (unsigned long long) val2 *
436 (unsigned long long) val3);
444 ***************************************************************************
445 * Read /proc/devices file and get device-mapper major number.
446 * If device-mapper entry is not found in file, assume it's not active.
449 * Device-mapper major number.
450 ***************************************************************************
452 unsigned int get_devmap_major(void)
457 * Linux uses 12 bits for the major number,
458 * so this shouldn't match any real device.
460 unsigned int dm_major = ~0U;
462 if ((fp = fopen(DEVICES, "r")) == NULL)
465 while (fgets(line, sizeof(line), fp) != NULL) {
467 if (strstr(line, "device-mapper")) {
468 /* Read device-mapper major number */
469 sscanf(line, "%u", &dm_major);
479 ***************************************************************************
480 * Returns whether S_TIME_FORMAT is set to ISO.
483 * TRUE if S_TIME_FORMAT is set to ISO, or FALSE otherwise.
484 ***************************************************************************
486 int is_iso_time_fmt(void)
488 static int is_iso = -1;
492 is_iso = (((e = __getenv(ENV_TIME_FMT)) != NULL) && !strcmp(e, K_ISO));
498 ***************************************************************************
502 * @nr_tab Number of tabs to print.
503 ***************************************************************************
505 void prtab(int nr_tab)
509 for (i = 0; i < nr_tab; i++) {
515 ***************************************************************************
516 * printf() function modified for XML-like output. Don't print a CR at the
520 * @nr_tab Number of tabs to print.
521 * @fmtf printf() format.
522 ***************************************************************************
524 void xprintf0(int nr_tab, const char *fmtf, ...)
526 static char buf[1024];
529 va_start(args, fmtf);
530 vsnprintf(buf, sizeof(buf), fmtf, args);
538 ***************************************************************************
539 * printf() function modified for XML-like output. Print a CR at the end of
543 * @nr_tab Number of tabs to print.
544 * @fmtf printf() format.
545 ***************************************************************************
547 void xprintf(int nr_tab, const char *fmtf, ...)
549 static char buf[1024];
552 va_start(args, fmtf);
553 vsnprintf(buf, sizeof(buf), fmtf, args);
561 ***************************************************************************
562 * Get report date as a string of characters.
565 * @rectime Date to display (don't use time fields).
566 * @cur_date String where date will be saved.
567 * @sz Max size of cur_date string.
570 * @cur_date Date (string format).
573 * TRUE if S_TIME_FORMAT is set to ISO, or FALSE otherwise.
574 ***************************************************************************
576 int set_report_date(struct tm *rectime, char cur_date[], int sz)
578 if (rectime == NULL) {
579 strncpy(cur_date, "?/?/?", sz);
580 cur_date[sz - 1] = '\0';
582 else if (is_iso_time_fmt()) {
583 strftime(cur_date, sz, "%Y-%m-%d", rectime);
587 strftime(cur_date, sz, "%x", rectime);
594 ***************************************************************************
598 * @rectime Date to display (don't use time fields).
599 * @sysname System name to display.
600 * @release System release number to display.
601 * @nodename Hostname to display.
602 * @machine Machine architecture to display.
603 * @cpu_nr Number of CPU.
604 * @format Set to FALSE for (default) plain output, and to TRUE for
605 * JSON format output.
608 * TRUE if S_TIME_FORMAT is set to ISO, or FALSE otherwise.
609 ***************************************************************************
611 int print_gal_header(struct tm *rectime, char *sysname, char *release,
612 char *nodename, char *machine, int cpu_nr, int format)
614 char cur_date[TIMESTAMP_LEN];
617 rc = set_report_date(rectime, cur_date, sizeof(cur_date));
619 if (format == PLAIN_OUTPUT) {
621 printf("%s %s (%s) \t%s \t_%s_\t(%d CPU)\n", sysname, release, nodename,
622 cur_date, machine, cpu_nr);
626 xprintf(0, "{\"sysstat\": {");
627 xprintf(1, "\"hosts\": [");
629 xprintf(3, "\"nodename\": \"%s\",", nodename);
630 xprintf(3, "\"sysname\": \"%s\",", sysname);
631 xprintf(3, "\"release\": \"%s\",", release);
632 xprintf(3, "\"machine\": \"%s\",", machine);
633 xprintf(3, "\"number-of-cpus\": %d,", cpu_nr);
634 xprintf(3, "\"date\": \"%s\",", cur_date);
635 xprintf(3, "\"statistics\": [");
642 ***************************************************************************
643 * Get number of rows for current window.
644 * If stdout is not a terminal then use the value given by environment
645 * variable S_REPEAT_HEADER if existent.
649 ***************************************************************************
651 int get_win_height(void)
656 * This default value will be used whenever STDOUT
657 * is redirected to a pipe or a file and S_REPEAT_HEADER variable is not set
659 int rows = 3600 * 24;
661 /* Get number of lines of current terminal */
662 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1) {
663 if (win.ws_row > 2) {
664 rows = win.ws_row - 2;
667 /* STDOUT is not a terminal. Look for S_REPEAT_HEADER variable's value instead */
668 else if ((e = __getenv(ENV_REPEAT_HEADER)) != NULL) {
669 if (strspn(e, DIGITS) == strlen(e)) {
680 ***************************************************************************
681 * Canonicalize and remove /dev from path name. If the device has a slash
682 * character in its name, replace it with a bang character ('!'), e.g.:
683 * cciss/c0d0 -> cciss!c0d0
684 * cciss/c0d0p1 -> cciss!c0d0p1
687 * @name Device name (may begin with "/dev/" or can be a symlink).
691 ***************************************************************************
693 char *device_name(char *name)
695 static char out[MAX_FILE_LEN];
696 char *resolved_name = NULL, *slash;
699 /* realpath() creates new string, so we need to free it later */
700 resolved_name = __realpath(name, NULL);
702 /* If path doesn't exist, just return input */
703 if (!resolved_name) {
708 fprintf(stderr, "Real pathname: %s (%s)\n", resolved_name, name);
711 if (!strncmp(resolved_name, "/dev/", 5)) {
714 strncpy(out, resolved_name + i, sizeof(out));
715 out[sizeof(out) - 1] = '\0';
717 /* Some devices may have a slash in their name (eg. cciss/c0d0...) */
718 while ((slash = strchr(out, '/'))) {
728 ***************************************************************************
729 * Workaround for CPU counters read from /proc/stat: Dyn-tick kernels
730 * have a race issue that can make those counters go backward.
731 ***************************************************************************
733 double ll_sp_value(unsigned long long value1, unsigned long long value2,
734 unsigned long long itv)
739 return SP_VALUE(value1, value2, itv);
743 ***************************************************************************
744 * Compute time interval.
747 * @prev_uptime Previous uptime value (in jiffies or 1/100th of a second).
748 * @curr_uptime Current uptime value (in jiffies or 1/100th of a second).
751 * Interval of time in jiffies or 1/100th of a second.
752 ***************************************************************************
754 unsigned long long get_interval(unsigned long long prev_uptime,
755 unsigned long long curr_uptime)
757 unsigned long long itv;
759 /* prev_time=0 when displaying stats since system startup */
760 itv = curr_uptime - prev_uptime;
762 if (!itv) { /* Paranoia checking */
770 ***************************************************************************
771 * Count number of bits set in an array.
774 * @ptr Pointer to array.
775 * @size Size of array in bytes.
778 * Number of bits set in the array.
779 ***************************************************************************
781 int count_bits(void *ptr, int size)
787 for (i = 0; i < size; i++, p++) {
800 ***************************************************************************
801 * Convert in-place input string to lowercase.
804 * @str String to be converted.
807 * @str String in lowercase.
810 * String in lowercase.
811 ***************************************************************************
813 char *strtolower(char *str)
826 ***************************************************************************
827 * Get persistent type name directory from type.
830 * @type Persistent type name (UUID, LABEL, etc.)
833 * Path to the persistent type name directory, or NULL if access is denied
834 * or strings have been truncated.
835 ***************************************************************************
837 char *get_persistent_type_dir(char *type)
839 static char dir[PATH_MAX];
842 n = snprintf(dir, sizeof(dir), "%s-%s", DEV_DISK_BY, type);
844 if ((n >= sizeof(dir)) || access(dir, R_OK)) {
852 ***************************************************************************
853 * Get persistent name absolute path.
856 * @name Persistent name.
859 * Path to the persistent name, or NULL if file doesn't exist or strings
860 * have been truncated.
861 ***************************************************************************
863 char *get_persistent_name_path(char *name)
865 static char path[PATH_MAX];
868 n = snprintf(path, sizeof(path), "%s/%s",
869 get_persistent_type_dir(persistent_name_type), name);
871 if ((n >= sizeof(path)) || access(path, F_OK)) {
879 ***************************************************************************
880 * Get files from persistent type name directory.
883 * List of files in the persistent type name directory in alphabetical order.
884 ***************************************************************************
886 char **get_persistent_names(void)
891 struct dirent **namelist;
893 /* Get directory name for selected persistent type */
894 dir = get_persistent_type_dir(persistent_name_type);
898 n = scandir(dir, &namelist, NULL, alphasort);
902 /* If directory is empty, it contains 2 entries: "." and ".." */
904 /* Free list and return NULL */
907 /* Ignore the "." and "..", but keep place for one last NULL. */
908 files = (char **) calloc(n - 1, sizeof(char *));
913 * i is for traversing namelist, k is for files.
914 * i != k because we are ignoring "." and ".." entries.
916 for (i = 0; i < n; i++) {
917 /* Ignore "." and ".." */
918 if (!strcmp(".", namelist[i]->d_name) ||
919 !strcmp("..", namelist[i]->d_name))
922 files[k] = (char *) calloc(strlen(namelist[i]->d_name) + 1, sizeof(char));
926 strcpy(files[k++], namelist[i]->d_name);
932 for (i = 0; i < n; i++) {
941 ***************************************************************************
942 * Get persistent name from pretty name.
945 * @pretty Pretty name (e.g. sda, sda1, ..).
949 ***************************************************************************
951 char *get_persistent_name_from_pretty(char *pretty)
956 char **persist_names;
957 char target[PATH_MAX];
958 static char persist_name[FILENAME_MAX];
960 persist_name[0] = '\0';
962 /* Get list of files from persistent type name directory */
963 persist_names = get_persistent_names();
967 while (persist_names[++i]) {
969 /* Get absolute path for current persistent name */
970 link = get_persistent_name_path(persist_names[i]);
974 /* Persistent name is usually a symlink: Read it... */
975 r = readlink(link, target, PATH_MAX);
976 if ((r <= 0) || (r >= PATH_MAX))
981 /* ... and get device pretty name it points at */
982 name = basename(target);
983 if (!name || (name[0] == '\0'))
986 if (!strncmp(name, pretty, FILENAME_MAX)) {
987 /* We have found pretty name for current persistent one */
988 strncpy(persist_name, persist_names[i], sizeof(persist_name));
989 persist_name[sizeof(persist_name) - 1] = '\0';
995 while (persist_names[++i]) {
996 free (persist_names[i]);
998 free (persist_names);
1000 if (strlen(persist_name) <= 0)
1003 return persist_name;
1007 ***************************************************************************
1008 * Get pretty name (sda, sda1...) from persistent name.
1011 * @persistent Persistent name.
1015 ***************************************************************************
1017 char *get_pretty_name_from_persistent(char *persistent)
1020 char *link, *pretty, target[PATH_MAX];
1022 /* Get absolute path for persistent name */
1023 link = get_persistent_name_path(persistent);
1027 /* Persistent name is usually a symlink: Read it... */
1028 r = readlink(link, target, PATH_MAX);
1029 if ((r <= 0) || (r >= PATH_MAX))
1034 /* ... and get device pretty name it points at */
1035 pretty = basename(target);
1036 if (!pretty || (pretty[0] == '\0'))
1043 * **************************************************************************
1044 * Try to get device real name from sysfs tree.
1047 * @major Major number of the device.
1048 * @minor Minor number of the device.
1051 * The name of the device, which may be the real name (as it appears in /dev)
1053 ***************************************************************************
1055 char *get_devname_from_sysfs(unsigned int major, unsigned int minor)
1057 static char link[256], target[PATH_MAX];
1061 snprintf(link, sizeof(link), "%s/%u:%u", SYSFS_DEV_BLOCK, major, minor);
1063 /* Get full path to device knowing its major and minor numbers */
1064 r = readlink(link, target, PATH_MAX);
1065 if (r <= 0 || r >= PATH_MAX)
1070 /* Get device name */
1071 devname = basename(target);
1072 if (!devname || strnlen(devname, FILENAME_MAX) == 0) {
1080 * **************************************************************************
1081 * Get device real name if possible.
1084 * @major Major number of the device.
1085 * @minor Minor number of the device.
1088 * The name of the device, which may be the real name (as it appears in /dev)
1089 * or a string with the following format devM-n.
1090 ***************************************************************************
1092 char *get_devname(unsigned int major, unsigned int minor)
1094 static char buf[32];
1097 name = get_devname_from_sysfs(major, minor);
1101 name = ioc_name(major, minor);
1102 if ((name != NULL) && strcmp(name, K_NODEV))
1105 snprintf(buf, sizeof(buf), "dev%u-%u", major, minor);
1110 * **************************************************************************
1111 * Get device name (whether pretty-printed, persistent or not).
1114 * @major Major number of the device.
1115 * @minor Minor number of the device.
1116 * @wwn WWN identifier of the device (0 if unknown).
1117 * @part_nr Partition number (0 if unknown).
1118 * @disp_devmap_name Display device mapper name.
1119 * @disp_persist_name Display persistent name of the device.
1120 * @use_stable_id Display stable-across-reboots name.
1121 * @dflt_name Device name to use by default (if existent).
1124 * The name of the device.
1125 ***************************************************************************
1127 char *get_device_name(unsigned int major, unsigned int minor, unsigned long long wwn[],
1128 unsigned int part_nr, unsigned int disp_devmap_name,
1129 unsigned int disp_persist_name, unsigned int use_stable_id,
1132 static unsigned int dm_major = 0;
1133 char *dev_name = NULL, *persist_dev_name = NULL, *bang;
1134 static char sid[64], dname[MAX_NAME_LEN];
1135 char xsid[32] = "", pn[16] = "";
1137 if (disp_persist_name) {
1138 persist_dev_name = get_persistent_name_from_pretty(get_devname(major, minor));
1141 if (persist_dev_name) {
1142 dev_name = persist_dev_name;
1145 if (use_stable_id && (wwn[0] != 0)) {
1147 sprintf(xsid, "%016llx", wwn[1]);
1150 sprintf(pn, "-%d", part_nr);
1152 snprintf(sid, sizeof(sid), "%#016llx%s%s", wwn[0], xsid, pn);
1155 else if (disp_devmap_name) {
1157 dm_major = get_devmap_major();
1159 if (major == dm_major) {
1160 dev_name = transform_devmapname(major, minor);
1166 dev_name = dflt_name;
1169 dev_name = get_devname(major, minor);
1174 strncpy(dname, dev_name, sizeof(dname));
1175 dname[sizeof(dname) - 1] = '\0';
1177 while ((bang = strchr(dname, '!'))) {
1179 * Some devices may have had a slash replaced with
1180 * a bang character (eg. cciss!c0d0...)
1181 * Restore their original names.
1190 ***************************************************************************
1191 * Init color strings.
1192 ***************************************************************************
1194 void init_colors(void)
1199 /* Read S_COLORS environment variable */
1200 if ((e = __getenv(ENV_COLORS)) == NULL
1201 ? !isatty(STDOUT_FILENO)
1202 : (!strcmp(e, C_NEVER) ||
1203 (strcmp(e, C_ALWAYS) && !isatty(STDOUT_FILENO)))) {
1205 * Environment variable not set and stdout is not a terminal,
1206 * or set to "never",
1207 * or set to "auto" and stdout is not a terminal:
1208 * Unset color strings.
1210 strcpy(sc_percent_high, "");
1211 strcpy(sc_percent_low, "");
1212 strcpy(sc_zero_int_stat, "");
1213 strcpy(sc_int_stat, "");
1214 strcpy(sc_item_name, "");
1215 strcpy(sc_sa_comment, "");
1216 strcpy(sc_sa_restart, "");
1217 strcpy(sc_normal, "");
1222 /* Read S_COLORS_SGR environment variable */
1223 if ((e = __getenv(ENV_COLORS_SGR)) == NULL)
1224 /* Environment variable not set */
1227 for (p = strtok(e, ":"); p; p =strtok(NULL, ":")) {
1230 if ((len > 7) || (len < 3) || (*(p + 1) != '=') ||
1231 (strspn(p + 2, ";0123456789") != (len - 2)))
1232 /* Ignore malformed codes */
1237 snprintf(sc_percent_high, MAX_SGR_LEN, "\e[%sm", p + 2);
1240 snprintf(sc_percent_low, MAX_SGR_LEN, "\e[%sm", p + 2);
1243 snprintf(sc_zero_int_stat, MAX_SGR_LEN, "\e[%sm", p + 2);
1246 snprintf(sc_int_stat, MAX_SGR_LEN, "\e[%sm", p + 2);
1249 snprintf(sc_item_name, MAX_SGR_LEN, "\e[%sm", p + 2);
1252 snprintf(sc_sa_comment, MAX_SGR_LEN, "\e[%sm", p + 2);
1255 snprintf(sc_sa_restart, MAX_SGR_LEN, "\e[%sm", p + 2);
1262 ***************************************************************************
1263 * Print a value in human readable format. Such a value is a decimal number
1264 * followed by a unit (B, k, M, etc.)
1267 * @unit Default value unit.
1268 * @dval Value to print.
1270 ***************************************************************************
1272 void cprintf_unit(int unit, int wi, double dval)
1279 /* Value is a number of sectors. Convert it to kB */
1283 while (dval >= 1024) {
1287 printf(" %*.*f", wi - 1, dplaces_nr ? 1 : 0, dval);
1288 printf("%s", sc_normal);
1291 if (unit >= NR_UNITS) {
1292 unit = NR_UNITS - 1;
1294 printf("%c", units[unit]);
1298 ***************************************************************************
1299 * Print 64 bit unsigned values using colors, possibly followed by a unit.
1302 * @unit Default values unit. -1 if no unit should be displayed.
1303 * @num Number of values to print.
1305 ***************************************************************************
1307 void cprintf_u64(int unit, int num, int wi, ...)
1315 for (i = 0; i < num; i++) {
1316 val = va_arg(args, unsigned long long);
1318 printf("%s", sc_zero_int_stat);
1321 printf("%s", sc_int_stat);
1324 printf(" %*"PRIu64, wi, val);
1325 printf("%s", sc_normal);
1328 cprintf_unit(unit, wi, (double) val);
1336 ***************************************************************************
1337 * Print hex values using colors.
1340 * @num Number of values to print.
1342 ***************************************************************************
1344 void cprintf_x(int num, int wi, ...)
1352 for (i = 0; i < num; i++) {
1353 val = va_arg(args, unsigned int);
1354 printf("%s", sc_int_stat);
1355 printf(" %*x", wi, val);
1356 printf("%s", sc_normal);
1363 ***************************************************************************
1364 * Print "double" statistics values using colors, possibly followed by a
1368 * @unit Default values unit. -1 if no unit should be displayed.
1369 * @num Number of values to print.
1371 * @wd Number of decimal places.
1372 ***************************************************************************
1374 void cprintf_f(int unit, int num, int wi, int wd, ...)
1377 double val, lim = 0.005;;
1381 * If there are decimal places then get the value
1382 * entered on the command line (if existing).
1384 if ((wd > 0) && (dplaces_nr >= 0)) {
1388 /* Update limit value according to number of decimal places */
1395 for (i = 0; i < num; i++) {
1396 val = va_arg(args, double);
1397 if (((wd > 0) && (val < lim) && (val > (lim * -1))) ||
1398 ((wd == 0) && (val <= 0.5) && (val >= -0.5))) { /* "Round half to even" law */
1399 printf("%s", sc_zero_int_stat);
1402 printf("%s", sc_int_stat);
1406 printf(" %*.*f", wi, wd, val);
1407 printf("%s", sc_normal);
1410 cprintf_unit(unit, wi, val);
1418 ***************************************************************************
1419 * Print "percent" statistics values using colors.
1422 * @human Set to > 0 if a percent sign (%) shall be displayed after
1424 * @num Number of values to print.
1426 * @wd Number of decimal places.
1427 ***************************************************************************
1429 void cprintf_pc(int human, int num, int wi, int wd, ...)
1432 double val, lim = 0.005;
1436 * If there are decimal places then get the value
1437 * entered on the command line (if existing).
1439 if ((wd > 0) && (dplaces_nr >= 0)) {
1444 * If a percent sign is to be displayed, then there will be
1445 * zero (or one) decimal place.
1452 /* Keep one place for the percent sign */
1459 /* Update limit value according to number of decimal places */
1466 for (i = 0; i < num; i++) {
1467 val = va_arg(args, double);
1468 if (val >= PERCENT_LIMIT_HIGH) {
1469 printf("%s", sc_percent_high);
1471 else if (val >= PERCENT_LIMIT_LOW) {
1472 printf("%s", sc_percent_low);
1474 else if (((wd > 0) && (val < lim)) ||
1475 ((wd == 0) && (val <= 0.5))) { /* "Round half to even" law */
1476 printf("%s", sc_zero_int_stat);
1479 printf("%s", sc_int_stat);
1481 printf(" %*.*f", wi, wd, val);
1482 printf("%s", sc_normal);
1483 if (human > 0) printf("%%");
1490 ***************************************************************************
1491 * Print item name using selected color.
1492 * Only one name can be displayed. Name can be an integer or a string.
1495 * @type 0 if name is an int, 1 if name is a string
1496 * @format Output format.
1497 * @item_string Item name (given as a string of characters).
1498 * @item_int Item name (given as an integer value).
1499 ***************************************************************************
1501 void cprintf_in(int type, char *format, char *item_string, int item_int)
1503 printf("%s", sc_item_name);
1505 printf(format, item_string);
1508 printf(format, item_int);
1510 printf("%s", sc_normal);
1514 ***************************************************************************
1515 * Print a string using selected color.
1518 * @type Type of string to display.
1519 * @format Output format.
1520 * @string String to display.
1521 ***************************************************************************
1523 void cprintf_s(int type, char *format, char *string)
1525 if (type == IS_STR) {
1526 printf("%s", sc_int_stat);
1528 else if (type == IS_ZERO) {
1529 printf("%s", sc_zero_int_stat);
1531 /* IS_RESTART and IS_DEBUG are the same value */
1532 else if (type == IS_RESTART) {
1533 printf("%s", sc_sa_restart);
1537 printf("%s", sc_sa_comment);
1539 printf(format, string);
1540 printf("%s", sc_normal);
1544 ***************************************************************************
1545 * Parse a string containing a numerical value (e.g. CPU or IRQ number).
1546 * The string should contain only one value, not a range of values.
1549 * @s String to parse.
1550 * @max_val Upper limit that value should not reach.
1553 * @val Value, or -1 if the string @s was empty.
1556 * 0 if the value has been properly read, 1 otherwise.
1557 ***************************************************************************
1559 int parse_valstr(char *s, int max_val, int *val)
1565 if (strspn(s, DIGITS) != strlen(s))
1569 if ((*val < 0) || (*val >= max_val))
1576 ***************************************************************************
1577 * Parse string containing a single value or a range of values
1578 * (e.g. "0,2-5,10-").
1581 * @t String to parse.
1582 * @max_val Upper limit that value should not reach.
1585 * @val_low Low value in range
1586 * @val High value in range. @val_low and @val are the same if it's
1590 * 0 on success, 1 otherwise.
1591 ***************************************************************************
1593 int parse_range_values(char *t, int max_val, int *val_low, int *val)
1595 char *s, *valstr, range[16];
1597 /* Parse value or range of values */
1598 strncpy(range, t, 16);
1602 if ((s = strchr(range, '-')) != NULL) {
1603 /* Possible range of values */
1605 if (parse_valstr(range, max_val, val_low) || (*val_low < 0))
1609 if (parse_valstr(valstr, max_val, val))
1611 if (s && *val < 0) {
1612 /* Range of values with no upper limit (e.g. "3-") */
1615 if ((!s && (*val < 0)) || (s && (*val < *val_low)))
1617 * Individual value: string cannot be empty.
1618 * Range of values: n-m: m can be empty (e.g. "3-") but
1619 * cannot be lower than n.
1629 ***************************************************************************
1630 * Parse string containing a set of coma-separated values or ranges of
1631 * values (e.g. "0,2-5,10-"). The ALL keyword is allowed and indicate that
1632 * all possible values are selected.
1635 * @strargv Current argument in list to parse.
1636 * @bitmap Bitmap whose contents will indicate which values have been
1638 * @max_val Upper limit that value should not reach.
1639 * @__K_VALUE0 Keyword corresponding to the first bit in bitmap (e.g "all",
1643 * @bitmap Bitmap updated with selected values.
1646 * 0 on success, 1 otherwise.
1647 ***************************************************************************
1649 int parse_values(char *strargv, unsigned char bitmap[], int max_val, const char *__K_VALUE0)
1651 int i, val_low, val;
1654 if (!strcmp(strargv, K_ALL)) {
1655 /* Set bit for every possible values (CPU, IRQ, etc.) */
1656 memset(bitmap, ~0, BITMAP_SIZE(max_val));
1660 for (t = strtok(strargv, ","); t; t = strtok(NULL, ",")) {
1661 if (!strcmp(t, __K_VALUE0)) {
1663 * Set bit 0 in bitmap. This may correspond
1664 * to CPU "all" or IRQ "SUM" for example.
1669 /* Parse value or range of values */
1670 if (parse_range_values(t, max_val, &val_low, &val))
1673 for (i = val_low; i <= val; i++) {
1674 bitmap[(i + 1) >> 3] |= 1 << ((i + 1) & 0x07);
1682 #endif /* SOURCE_SADC undefined */