]> granicus.if.org Git - sysstat/blob - common.c
603b62d253494c536427bdc8805c1c6d4b4356a0
[sysstat] / common.c
1 /*
2  * sar, sadc, sadf, mpstat and iostat common routines.
3  * (C) 1999-2016 by Sebastien GODARD (sysstat <at> orange.fr)
4  *
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.                                              *
10  *                                                                         *
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 *
14  * for more details.                                                       *
15  *                                                                         *
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  ***************************************************************************
20  */
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <inttypes.h>
27 #include <time.h>
28 #include <errno.h>
29 #include <unistd.h>     /* For STDOUT_FILENO, among others */
30 #include <sys/ioctl.h>
31 #include <sys/types.h>
32 #include <dirent.h>
33 #include <ctype.h>
34 #include <libgen.h>
35
36 #include "version.h"
37 #include "common.h"
38 #include "ioconf.h"
39 #include "rd_stats.h"
40
41 #ifdef USE_NLS
42 #include <locale.h>
43 #include <libintl.h>
44 #define _(string) gettext(string)
45 #else
46 #define _(string) (string)
47 #endif
48
49 /* Number of ticks per second */
50 unsigned int hz;
51 /* Number of bit shifts to convert pages to kB */
52 unsigned int kb_shift;
53
54 /* Colors strings */
55 char sc_percent_high[MAX_SGR_LEN] = C_BOLD_RED;
56 char sc_percent_low[MAX_SGR_LEN] = C_BOLD_BLUE;
57 char sc_zero_int_stat[MAX_SGR_LEN] = C_LIGHT_YELLOW;
58 char sc_int_stat[MAX_SGR_LEN] = C_BOLD_YELLOW;
59 char sc_item_name[MAX_SGR_LEN] = C_LIGHT_GREEN;
60 char sc_sa_restart[MAX_SGR_LEN] = C_LIGHT_RED;
61 char sc_sa_comment[MAX_SGR_LEN] = C_LIGHT_CYAN;
62 char sc_normal[MAX_SGR_LEN] = C_NORMAL;
63
64 /* Type of persistent device names used in sar and iostat */
65 char persistent_name_type[MAX_FILE_LEN];
66
67 /*
68  ***************************************************************************
69  * Print sysstat version number and exit.
70  ***************************************************************************
71  */
72 void print_version(void)
73 {
74         printf(_("sysstat version %s\n"), VERSION);
75         printf("(C) Sebastien Godard (sysstat <at> orange.fr)\n");
76         exit(0);
77 }
78
79 /*
80  ***************************************************************************
81  * Get local date and time.
82  *
83  * IN:
84  * @d_off       Day offset (number of days to go back in the past).
85  *
86  * OUT:
87  * @rectime     Current local date and time.
88  *
89  * RETURNS:
90  * Value of time in seconds since the Epoch.
91  ***************************************************************************
92  */
93 time_t get_localtime(struct tm *rectime, int d_off)
94 {
95         time_t timer;
96         struct tm *ltm;
97
98         time(&timer);
99         timer -= SEC_PER_DAY * d_off;
100         ltm = localtime(&timer);
101
102         if (ltm) {
103                 *rectime = *ltm;
104         }
105         return timer;
106 }
107
108 /*
109  ***************************************************************************
110  * Get date and time expressed in UTC.
111  *
112  * IN:
113  * @d_off       Day offset (number of days to go back in the past).
114  *
115  * OUT:
116  * @rectime     Current date and time expressed in UTC.
117  *
118  * RETURNS:
119  * Value of time in seconds since the Epoch.
120  ***************************************************************************
121  */
122 time_t get_gmtime(struct tm *rectime, int d_off)
123 {
124         time_t timer;
125         struct tm *ltm;
126
127         time(&timer);
128         timer -= SEC_PER_DAY * d_off;
129         ltm = gmtime(&timer);
130
131         if (ltm) {
132                 *rectime = *ltm;
133         }
134         return timer;
135 }
136
137 /*
138  ***************************************************************************
139  * Get date and time and take into account <ENV_TIME_DEFTM> variable.
140  *
141  * IN:
142  * @d_off       Day offset (number of days to go back in the past).
143  *
144  * OUT:
145  * @rectime     Current date and time.
146  *
147  * RETURNS:
148  * Value of time in seconds since the Epoch.
149  ***************************************************************************
150  */
151 time_t get_time(struct tm *rectime, int d_off)
152 {
153         static int utc = 0;
154         char *e;
155
156         if (!utc) {
157                 /* Read environment variable value once */
158                 if ((e = getenv(ENV_TIME_DEFTM)) != NULL) {
159                         utc = !strcmp(e, K_UTC);
160                 }
161                 utc++;
162         }
163
164         if (utc == 2)
165                 return get_gmtime(rectime, d_off);
166         else
167                 return get_localtime(rectime, d_off);
168 }
169
170 /*
171  ***************************************************************************
172  * Count number of comma-separated values in arguments list. For example,
173  * the number will be 3 for the list "foobar -p 1 -p 2,3,4 2 5".
174  *
175  * IN:
176  * @arg_c       Number of arguments in the list.
177  * @arg_v       Arguments list.
178  *
179  * RETURNS:
180  * Number of comma-separated values in the list.
181  ***************************************************************************
182  */
183 int count_csvalues(int arg_c, char **arg_v)
184 {
185         int opt = 1;
186         int nr = 0;
187         char *t;
188
189         while (opt < arg_c) {
190                 if (strchr(arg_v[opt], ',')) {
191                         for (t = arg_v[opt]; t; t = strchr(t + 1, ',')) {
192                                 nr++;
193                         }
194                 }
195                 opt++;
196         }
197
198         return nr;
199 }
200
201 /*
202  ***************************************************************************
203  * Look for partitions of a given block device in /sys filesystem.
204  *
205  * IN:
206  * @dev_name    Name of the block device.
207  *
208  * RETURNS:
209  * Number of partitions for the given block device.
210  ***************************************************************************
211  */
212 int get_dev_part_nr(char *dev_name)
213 {
214         DIR *dir;
215         struct dirent *drd;
216         char dfile[MAX_PF_NAME], line[MAX_PF_NAME];
217         int part = 0;
218
219         snprintf(dfile, MAX_PF_NAME, "%s/%s", SYSFS_BLOCK, dev_name);
220         dfile[MAX_PF_NAME - 1] = '\0';
221
222         /* Open current device directory in /sys/block */
223         if ((dir = opendir(dfile)) == NULL)
224                 return 0;
225
226         /* Get current file entry */
227         while ((drd = readdir(dir)) != NULL) {
228                 if (!strcmp(drd->d_name, ".") || !strcmp(drd->d_name, ".."))
229                         continue;
230                 snprintf(line, MAX_PF_NAME, "%s/%s/%s", dfile, drd->d_name, S_STAT);
231                 line[MAX_PF_NAME - 1] = '\0';
232
233                 /* Try to guess if current entry is a directory containing a stat file */
234                 if (!access(line, R_OK)) {
235                         /* Yep... */
236                         part++;
237                 }
238         }
239
240         /* Close directory */
241         closedir(dir);
242
243         return part;
244 }
245
246 /*
247  ***************************************************************************
248  * Look for block devices present in /sys/ filesystem:
249  * Check first that sysfs is mounted (done by trying to open /sys/block
250  * directory), then find number of devices registered.
251  *
252  * IN:
253  * @display_partitions  Set to TRUE if partitions must also be counted.
254  *
255  * RETURNS:
256  * Total number of block devices (and partitions if @display_partitions was
257  * set).
258  ***************************************************************************
259  */
260 int get_sysfs_dev_nr(int display_partitions)
261 {
262         DIR *dir;
263         struct dirent *drd;
264         char line[MAX_PF_NAME];
265         int dev = 0;
266
267         /* Open /sys/block directory */
268         if ((dir = opendir(SYSFS_BLOCK)) == NULL)
269                 /* sysfs not mounted, or perhaps this is an old kernel */
270                 return 0;
271
272         /* Get current file entry in /sys/block directory */
273         while ((drd = readdir(dir)) != NULL) {
274                 if (!strcmp(drd->d_name, ".") || !strcmp(drd->d_name, ".."))
275                         continue;
276                 snprintf(line, MAX_PF_NAME, "%s/%s/%s", SYSFS_BLOCK, drd->d_name, S_STAT);
277                 line[MAX_PF_NAME - 1] = '\0';
278
279                 /* Try to guess if current entry is a directory containing a stat file */
280                 if (!access(line, R_OK)) {
281                         /* Yep... */
282                         dev++;
283
284                         if (display_partitions) {
285                                 /* We also want the number of partitions for this device */
286                                 dev += get_dev_part_nr(drd->d_name);
287                         }
288                 }
289         }
290
291         /* Close /sys/block directory */
292         closedir(dir);
293
294         return dev;
295 }
296
297 /*
298  ***************************************************************************
299  * Read /proc/devices file and get device-mapper major number.
300  * If device-mapper entry is not found in file, assume it's not active.
301  *
302  * RETURNS:
303  * Device-mapper major number.
304  ***************************************************************************
305  */
306 unsigned int get_devmap_major(void)
307 {
308         FILE *fp;
309         char line[128];
310         /*
311          * Linux uses 12 bits for the major number,
312          * so this shouldn't match any real device.
313          */
314         unsigned int dm_major = ~0U;
315
316         if ((fp = fopen(DEVICES, "r")) == NULL)
317                 return dm_major;
318
319         while (fgets(line, sizeof(line), fp) != NULL) {
320
321                 if (strstr(line, "device-mapper")) {
322                         /* Read device-mapper major number */
323                         sscanf(line, "%u", &dm_major);
324                 }
325         }
326
327         fclose(fp);
328
329         return dm_major;
330 }
331
332 /*
333  ***************************************************************************
334  * Returns whether S_TIME_FORMAT is set to ISO.
335  *
336  * RETURNS:
337  * TRUE if S_TIME_FORMAT is set to ISO, or FALSE otherwise.
338  ***************************************************************************
339  */
340 int is_iso_time_fmt(void)
341 {
342         static int is_iso = -1;
343         char *e;
344
345         if (is_iso < 0) {
346                 is_iso = (((e = getenv(ENV_TIME_FMT)) != NULL) && !strcmp(e, K_ISO));
347         }
348         return is_iso;
349 }
350
351 /*
352  ***************************************************************************
353  * Print tabulations
354  *
355  * IN:
356  * @nr_tab      Number of tabs to print.
357  ***************************************************************************
358  */
359 void prtab(int nr_tab)
360 {
361         int i;
362
363         for (i = 0; i < nr_tab; i++) {
364                 printf("\t");
365         }
366 }
367
368 /*
369  ***************************************************************************
370  * printf() function modified for XML-like output. Don't print a CR at the
371  * end of the line.
372  *
373  * IN:
374  * @nr_tab      Number of tabs to print.
375  * @fmtf        printf() format.
376  ***************************************************************************
377  */
378 void xprintf0(int nr_tab, const char *fmtf, ...)
379 {
380         static char buf[1024];
381         va_list args;
382
383         va_start(args, fmtf);
384         vsnprintf(buf, sizeof(buf), fmtf, args);
385         va_end(args);
386
387         prtab(nr_tab);
388         printf("%s", buf);
389 }
390
391 /*
392  ***************************************************************************
393  * printf() function modified for XML-like output. Print a CR at the end of
394  * the line.
395  *
396  * IN:
397  * @nr_tab      Number of tabs to print.
398  * @fmtf        printf() format.
399  ***************************************************************************
400  */
401 void xprintf(int nr_tab, const char *fmtf, ...)
402 {
403         static char buf[1024];
404         va_list args;
405
406         va_start(args, fmtf);
407         vsnprintf(buf, sizeof(buf), fmtf, args);
408         va_end(args);
409
410         prtab(nr_tab);
411         printf("%s\n", buf);
412 }
413
414 /*
415  ***************************************************************************
416  * Print banner.
417  *
418  * IN:
419  * @rectime     Date to display (don't use time fields).
420  * @sysname     System name to display.
421  * @release     System release number to display.
422  * @nodename    Hostname to display.
423  * @machine     Machine architecture to display.
424  * @cpu_nr      Number of CPU.
425  * @format      Set to FALSE for (default) plain output, and to TRUE for
426  *              JSON format output.
427  *
428  * RETURNS:
429  * TRUE if S_TIME_FORMAT is set to ISO, or FALSE otherwise.
430  ***************************************************************************
431  */
432 int print_gal_header(struct tm *rectime, char *sysname, char *release,
433                      char *nodename, char *machine, int cpu_nr, int format)
434 {
435         char cur_date[TIMESTAMP_LEN];
436         int rc = 0;
437
438         if (rectime == NULL) {
439                 strcpy(cur_date, "?/?/?");
440         }
441         else if (is_iso_time_fmt()) {
442                 strftime(cur_date, sizeof(cur_date), "%Y-%m-%d", rectime);
443                 rc = 1;
444         }
445         else {
446                 strftime(cur_date, sizeof(cur_date), "%x", rectime);
447         }
448
449         if (format == PLAIN_OUTPUT) {
450                 /* Plain output */
451                 printf("%s %s (%s) \t%s \t_%s_\t(%d CPU)\n", sysname, release, nodename,
452                        cur_date, machine, cpu_nr);
453         }
454         else {
455                 /* JSON output */
456                 xprintf(0, "{\"sysstat\": {");
457                 xprintf(1, "\"hosts\": [");
458                 xprintf(2, "{");
459                 xprintf(3, "\"nodename\": \"%s\",", nodename);
460                 xprintf(3, "\"sysname\": \"%s\",", sysname);
461                 xprintf(3, "\"release\": \"%s\",", release);
462                 xprintf(3, "\"machine\": \"%s\",", machine);
463                 xprintf(3, "\"number-of-cpus\": %d,", cpu_nr);
464                 xprintf(3, "\"date\": \"%s\",", cur_date);
465                 xprintf(3, "\"statistics\": [");
466         }
467
468         return rc;
469 }
470
471 #ifdef USE_NLS
472 /*
473  ***************************************************************************
474  * Init National Language Support.
475  ***************************************************************************
476  */
477 void init_nls(void)
478 {
479         setlocale(LC_MESSAGES, "");
480         setlocale(LC_CTYPE, "");
481         setlocale(LC_TIME, "");
482         setlocale(LC_NUMERIC, "");
483
484         bindtextdomain(PACKAGE, LOCALEDIR);
485         textdomain(PACKAGE);
486 }
487 #endif
488
489 /*
490  ***************************************************************************
491  * Get number of rows for current window.
492  *
493  * RETURNS:
494  * Number of rows.
495  ***************************************************************************
496  */
497 int get_win_height(void)
498 {
499         struct winsize win;
500         /*
501          * This default value will be used whenever STDOUT
502          * is redirected to a pipe or a file
503          */
504         int rows = 3600 * 24;
505
506         if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1) {
507                 if (win.ws_row > 2) {
508                         rows = win.ws_row - 2;
509                 }
510         }
511         return rows;
512 }
513
514 /*
515  ***************************************************************************
516  * Canonicalize and remove /dev from path name.
517  *
518  * IN:
519  * @name        Device name (may begin with "/dev/" or can be a symlink).
520  *
521  * RETURNS:
522  * Device basename.
523  ***************************************************************************
524  */
525 char *device_name(char *name)
526 {
527         static char out[MAX_FILE_LEN];
528         char *resolved_name;
529         int i = 0;
530
531         /* realpath() creates new string, so we need to free it later */
532         resolved_name = realpath(name, NULL);
533
534         /* If path doesn't exist, just return input */
535         if (!resolved_name) {
536                 return name;
537         }
538
539         if (!strncmp(resolved_name, "/dev/", 5)) {
540                 i = 5;
541         }
542         strncpy(out, resolved_name + i, MAX_FILE_LEN);
543         out[MAX_FILE_LEN - 1] = '\0';
544
545         free(resolved_name);
546
547         return out;
548 }
549
550 /*
551  ***************************************************************************
552  * Test whether given name is a device or a partition, using sysfs.
553  * This is more straightforward that using ioc_iswhole() function from
554  * ioconf.c which should be used only with kernels that don't have sysfs.
555  *
556  * IN:
557  * @name                Device or partition name.
558  * @allow_virtual       TRUE if virtual devices are also accepted.
559  *                      The device is assumed to be virtual if no
560  *                      /sys/block/<device>/device link exists.
561  *
562  * RETURNS:
563  * TRUE if @name is not a partition.
564  ***************************************************************************
565  */
566 int is_device(char *name, int allow_virtual)
567 {
568         char syspath[PATH_MAX];
569         char *slash;
570
571         /* Some devices may have a slash in their name (eg. cciss/c0d0...) */
572         while ((slash = strchr(name, '/'))) {
573                 *slash = '!';
574         }
575         snprintf(syspath, sizeof(syspath), "%s/%s%s", SYSFS_BLOCK, name,
576                  allow_virtual ? "" : "/device");
577
578         return !(access(syspath, F_OK));
579 }
580
581 /*
582  ***************************************************************************
583  * Get page shift in kB.
584  ***************************************************************************
585  */
586 void get_kb_shift(void)
587 {
588         int shift = 0;
589         long size;
590
591         /* One can also use getpagesize() to get the size of a page */
592         if ((size = sysconf(_SC_PAGESIZE)) == -1) {
593                 perror("sysconf");
594         }
595
596         size >>= 10;    /* Assume that a page has a minimum size of 1 kB */
597
598         while (size > 1) {
599                 shift++;
600                 size >>= 1;
601         }
602
603         kb_shift = (unsigned int) shift;
604 }
605
606 /*
607  ***************************************************************************
608  * Get number of clock ticks per second.
609  ***************************************************************************
610  */
611 void get_HZ(void)
612 {
613         long ticks;
614
615         if ((ticks = sysconf(_SC_CLK_TCK)) == -1) {
616                 perror("sysconf");
617         }
618
619         hz = (unsigned int) ticks;
620 }
621
622 /*
623  ***************************************************************************
624  * Workaround for CPU counters read from /proc/stat: Dyn-tick kernels
625  * have a race issue that can make those counters go backward.
626  ***************************************************************************
627  */
628 double ll_sp_value(unsigned long long value1, unsigned long long value2,
629                    unsigned long long itv)
630 {
631         if (value2 < value1)
632                 return (double) 0;
633         else
634                 return SP_VALUE(value1, value2, itv);
635 }
636
637 /*
638  ***************************************************************************
639  * Compute time interval.
640  *
641  * IN:
642  * @prev_uptime Previous uptime value in jiffies.
643  * @curr_uptime Current uptime value in jiffies.
644  *
645  * RETURNS:
646  * Interval of time in jiffies.
647  ***************************************************************************
648  */
649 unsigned long long get_interval(unsigned long long prev_uptime,
650                                 unsigned long long curr_uptime)
651 {
652         unsigned long long itv;
653
654         /* prev_time=0 when displaying stats since system startup */
655         itv = curr_uptime - prev_uptime;
656
657         if (!itv) {     /* Paranoia checking */
658                 itv = 1;
659         }
660
661         return itv;
662 }
663
664 /*
665  ***************************************************************************
666  * Since ticks may vary slightly from CPU to CPU, we'll want
667  * to recalculate itv based on this CPU's tick count, rather
668  * than that reported by the "cpu" line. Otherwise we
669  * occasionally end up with slightly skewed figures, with
670  * the skew being greater as the time interval grows shorter.
671  *
672  * IN:
673  * @scc Current sample statistics for current CPU.
674  * @scp Previous sample statistics for current CPU.
675  *
676  * RETURNS:
677  * Interval of time based on current CPU.
678  ***************************************************************************
679  */
680 unsigned long long get_per_cpu_interval(struct stats_cpu *scc,
681                                         struct stats_cpu *scp)
682 {
683         unsigned long long ishift = 0LL;
684
685         if ((scc->cpu_user - scc->cpu_guest) < (scp->cpu_user - scp->cpu_guest)) {
686                 /*
687                  * Sometimes the nr of jiffies spent in guest mode given by the guest
688                  * counter in /proc/stat is slightly higher than that included in
689                  * the user counter. Update the interval value accordingly.
690                  */
691                 ishift += (scp->cpu_user - scp->cpu_guest) -
692                           (scc->cpu_user - scc->cpu_guest);
693         }
694         if ((scc->cpu_nice - scc->cpu_guest_nice) < (scp->cpu_nice - scp->cpu_guest_nice)) {
695                 /*
696                  * Idem for nr of jiffies spent in guest_nice mode.
697                  */
698                 ishift += (scp->cpu_nice - scp->cpu_guest_nice) -
699                           (scc->cpu_nice - scc->cpu_guest_nice);
700         }
701
702         /*
703          * Don't take cpu_guest and cpu_guest_nice into account
704          * because cpu_user and cpu_nice already include them.
705          */
706         return ((scc->cpu_user    + scc->cpu_nice   +
707                  scc->cpu_sys     + scc->cpu_iowait +
708                  scc->cpu_idle    + scc->cpu_steal  +
709                  scc->cpu_hardirq + scc->cpu_softirq) -
710                 (scp->cpu_user    + scp->cpu_nice   +
711                  scp->cpu_sys     + scp->cpu_iowait +
712                  scp->cpu_idle    + scp->cpu_steal  +
713                  scp->cpu_hardirq + scp->cpu_softirq) +
714                  ishift);
715 }
716
717 /*
718  ***************************************************************************
719  * Unhandled situation: Panic and exit. Should never happen.
720  *
721  * IN:
722  * @function    Function name where situation occured.
723  * @error_code  Error code.
724  ***************************************************************************
725  */
726 void sysstat_panic(const char *function, int error_code)
727 {
728         fprintf(stderr, "sysstat: %s[%d]: Internal error...\n",
729                 function, error_code);
730         exit(1);
731 }
732
733 /*
734  ***************************************************************************
735  * Count number of bits set in an array.
736  *
737  * IN:
738  * @ptr         Pointer to array.
739  * @size        Size of array in bytes.
740  *
741  * RETURNS:
742  * Number of bits set in the array.
743  ***************************************************************************
744 */
745 int count_bits(void *ptr, int size)
746 {
747         int nr = 0, i, k;
748         char *p;
749
750         p = ptr;
751         for (i = 0; i < size; i++, p++) {
752                 k = 0x80;
753                 while (k) {
754                         if (*p & k)
755                                 nr++;
756                         k >>= 1;
757                 }
758         }
759
760         return nr;
761 }
762
763 /*
764  ***************************************************************************
765  * Compute "extended" device statistics (service time, etc.).
766  *
767  * IN:
768  * @sdc         Structure with current device statistics.
769  * @sdp         Structure with previous device statistics.
770  * @itv         Interval of time in jiffies.
771  *
772  * OUT:
773  * @xds         Structure with extended statistics.
774  ***************************************************************************
775 */
776 void compute_ext_disk_stats(struct stats_disk *sdc, struct stats_disk *sdp,
777                             unsigned long long itv, struct ext_disk_stats *xds)
778 {
779         double tput
780                 = ((double) (sdc->nr_ios - sdp->nr_ios)) * HZ / itv;
781
782         xds->util  = S_VALUE(sdp->tot_ticks, sdc->tot_ticks, itv);
783         xds->svctm = tput ? xds->util / tput : 0.0;
784         /*
785          * Kernel gives ticks already in milliseconds for all platforms
786          * => no need for further scaling.
787          */
788         xds->await = (sdc->nr_ios - sdp->nr_ios) ?
789                 ((sdc->rd_ticks - sdp->rd_ticks) + (sdc->wr_ticks - sdp->wr_ticks)) /
790                 ((double) (sdc->nr_ios - sdp->nr_ios)) : 0.0;
791         xds->arqsz = (sdc->nr_ios - sdp->nr_ios) ?
792                 ((sdc->rd_sect - sdp->rd_sect) + (sdc->wr_sect - sdp->wr_sect)) /
793                 ((double) (sdc->nr_ios - sdp->nr_ios)) : 0.0;
794 }
795
796 /*
797  ***************************************************************************
798  * Convert in-place input string to lowercase.
799  *
800  * IN:
801  * @str         String to be converted.
802  *
803  * OUT:
804  * @str         String in lowercase.
805  *
806  * RETURNS:
807  * String in lowercase.
808  ***************************************************************************
809 */
810 char *strtolower(char *str)
811 {
812         char *cp = str;
813
814         while (*cp) {
815                 *cp = tolower(*cp);
816                 cp++;
817         }
818
819         return(str);
820 }
821
822 /*
823  ***************************************************************************
824  * Get persistent type name directory from type.
825  *
826  * IN:
827  * @type        Persistent type name (UUID, LABEL, etc.)
828  *
829  * RETURNS:
830  * Path to the persistent type name directory, or NULL if access is denied.
831  ***************************************************************************
832 */
833 char *get_persistent_type_dir(char *type)
834 {
835         static char dir[32];
836
837         snprintf(dir, 32, "%s-%s", DEV_DISK_BY, type);
838
839         if (access(dir, R_OK)) {
840                 return (NULL);
841         }
842
843         return (dir);
844 }
845
846 /*
847  ***************************************************************************
848  * Get persistent name absolute path.
849  *
850  * IN:
851  * @name        Persistent name.
852  *
853  * RETURNS:
854  * Path to the persistent name, or NULL if file doesn't exist.
855  ***************************************************************************
856 */
857 char *get_persistent_name_path(char *name)
858 {
859         static char path[PATH_MAX];
860
861         snprintf(path, PATH_MAX, "%s/%s",
862                  get_persistent_type_dir(persistent_name_type), name);
863
864         if (access(path, F_OK)) {
865                 return (NULL);
866         }
867
868         return (path);
869 }
870
871 /*
872  ***************************************************************************
873  * Get files from persistent type name directory.
874  *
875  * RETURNS:
876  * List of files in the persistent type name directory in alphabetical order.
877  ***************************************************************************
878 */
879 char **get_persistent_names(void)
880 {
881         int n, i, k = 0;
882         char *dir;
883         char **files = NULL;
884         struct dirent **namelist;
885
886         /* Get directory name for selected persistent type */
887         dir = get_persistent_type_dir(persistent_name_type);
888         if (!dir)
889                 return (NULL);
890
891         n = scandir(dir, &namelist, NULL, alphasort);
892         if (n < 0)
893                 return (NULL);
894
895         /* If directory is empty, it contains 2 entries: "." and ".." */
896         if (n <= 2)
897                 /* Free list and return NULL */
898                 goto free_list;
899
900         /* Ignore the "." and "..", but keep place for one last NULL. */
901         files = (char **) calloc(n - 1, sizeof(char *));
902         if (!files)
903                 goto free_list;
904
905         /*
906          * i is for traversing namelist, k is for files.
907          * i != k because we are ignoring "." and ".." entries.
908          */
909         for (i = 0; i < n; i++) {
910                 /* Ignore "." and "..". */
911                 if (!strcmp(".", namelist[i]->d_name) ||
912                     !strcmp("..", namelist[i]->d_name))
913                         continue;
914
915                 files[k] = (char *) calloc(strlen(namelist[i]->d_name) + 1, sizeof(char));
916                 if (!files[k])
917                         continue;
918
919                 strcpy(files[k++], namelist[i]->d_name);
920         }
921         files[k] = NULL;
922
923 free_list:
924
925         for (i = 0; i < n; i++) {
926                 free(namelist[i]);
927         }
928         free(namelist);
929
930         return (files);
931 }
932
933 /*
934  ***************************************************************************
935  * Get persistent name from pretty name.
936  *
937  * IN:
938  * @pretty      Pretty name (e.g. sda, sda1, ..).
939  *
940  * RETURNS:
941  * Persistent name.
942  ***************************************************************************
943 */
944 char *get_persistent_name_from_pretty(char *pretty)
945 {
946         int i = -1;
947         ssize_t r;
948         char *link, *name;
949         char **persist_names;
950         char target[PATH_MAX];
951         static char persist_name[FILENAME_MAX];
952
953         persist_name[0] = '\0';
954
955         /* Get list of files from persistent type name directory */
956         persist_names = get_persistent_names();
957         if (!persist_names)
958                 return (NULL);
959
960         while (persist_names[++i]) {
961                 /* Get absolute path for current persistent name */
962                 link = get_persistent_name_path(persist_names[i]);
963                 if (!link)
964                         continue;
965
966                 /* Persistent name is usually a symlink: Read it... */
967                 r = readlink(link, target, PATH_MAX);
968                 if ((r <= 0) || (r >= PATH_MAX))
969                         continue;
970
971                 target[r] = '\0';
972
973                 /* ... and get device pretty name it points at */
974                 name = basename(target);
975                 if (!name || (name[0] == '\0'))
976                         continue;
977
978                 if (!strncmp(name, pretty, FILENAME_MAX)) {
979                         /* We have found pretty name for current persistent one */
980                         strncpy(persist_name, persist_names[i], FILENAME_MAX);
981                         persist_name[FILENAME_MAX - 1] = '\0';
982                         break;
983                 }
984         }
985
986         i = -1;
987         while (persist_names[++i]) {
988                 free (persist_names[i]);
989         }
990         free (persist_names);
991
992         if (strlen(persist_name) <= 0)
993                 return (NULL);
994
995         return persist_name;
996 }
997
998 /*
999  ***************************************************************************
1000  * Get pretty name (sda, sda1...) from persistent name.
1001  *
1002  * IN:
1003  * @persistent  Persistent name.
1004  *
1005  * RETURNS:
1006  * Pretty name.
1007  ***************************************************************************
1008 */
1009 char *get_pretty_name_from_persistent(char *persistent)
1010 {
1011         ssize_t r;
1012         char *link, *pretty, target[PATH_MAX];
1013
1014         /* Get absolute path for persistent name */
1015         link = get_persistent_name_path(persistent);
1016         if (!link)
1017                 return (NULL);
1018
1019         /* Persistent name is usually a symlink: Read it... */
1020         r = readlink(link, target, PATH_MAX);
1021         if ((r <= 0) || (r >= PATH_MAX))
1022                 return (NULL);
1023
1024         target[r] = '\0';
1025
1026         /* ... and get device pretty name it points at */
1027         pretty = basename(target);
1028         if (!pretty || (pretty[0] == '\0'))
1029                 return (NULL);
1030
1031         return pretty;
1032 }
1033
1034 /*
1035  ***************************************************************************
1036  * Init color strings.
1037  ***************************************************************************
1038  */
1039 void init_colors(void)
1040 {
1041         char *e, *p;
1042         int len;
1043
1044         /* Read S_COLORS environment variable */
1045         if (((e = getenv(ENV_COLORS)) == NULL) ||
1046             !strcmp(e, C_NEVER) ||
1047             (strcmp(e, C_ALWAYS) && !isatty(STDOUT_FILENO))) {
1048                 /*
1049                  * Environment variable not set, or set to "never",
1050                  * or set to "auto" and stdout is not a terminal:
1051                  * Unset color strings.
1052                  */
1053                 strcpy(sc_percent_high, "");
1054                 strcpy(sc_percent_low, "");
1055                 strcpy(sc_zero_int_stat, "");
1056                 strcpy(sc_int_stat, "");
1057                 strcpy(sc_item_name, "");
1058                 strcpy(sc_sa_comment, "");
1059                 strcpy(sc_sa_restart, "");
1060                 strcpy(sc_normal, "");
1061
1062                 return;
1063         }
1064
1065         /* Read S_COLORS_SGR environment variable */
1066         if ((e = getenv(ENV_COLORS_SGR)) == NULL)
1067                 /* Environment variable not set */
1068                 return;
1069
1070         for (p = strtok(e, ":"); p; p =strtok(NULL, ":")) {
1071
1072                 len = strlen(p);
1073                 if ((len > 7) || (len < 3) || (*(p + 1) != '=') ||
1074                     (strspn(p + 2, ";0123456789") != (len - 2)))
1075                         /* Ignore malformed codes */
1076                         continue;
1077
1078                 switch (*p) {
1079                         case 'H':
1080                                 snprintf(sc_percent_high, MAX_SGR_LEN, "\e[%sm", p + 2);
1081                                 break;
1082                         case 'M':
1083                                 snprintf(sc_percent_low, MAX_SGR_LEN, "\e[%sm", p + 2);
1084                                 break;
1085                         case 'Z':
1086                                 snprintf(sc_zero_int_stat, MAX_SGR_LEN, "\e[%sm", p + 2);
1087                                 break;
1088                         case 'N':
1089                                 snprintf(sc_int_stat, MAX_SGR_LEN, "\e[%sm", p + 2);
1090                                 break;
1091                         case 'I':
1092                                 snprintf(sc_item_name, MAX_SGR_LEN, "\e[%sm", p + 2);
1093                                 break;
1094                         case 'C':
1095                                 snprintf(sc_sa_comment, MAX_SGR_LEN, "\e[%sm", p + 2);
1096                                 break;
1097                         case 'R':
1098                                 snprintf(sc_sa_restart, MAX_SGR_LEN, "\e[%sm", p + 2);
1099                                 break;
1100                 }
1101         }
1102 }
1103
1104 /*
1105  ***************************************************************************
1106  * Print 64 bit unsigned values using colors.
1107  *
1108  * IN:
1109  * @num         Number of values to print.
1110  * @width       Output width.
1111  ***************************************************************************
1112 */
1113 void cprintf_u64(int num, int width, ...)
1114 {
1115         int i;
1116         uint64_t val;
1117         va_list args;
1118
1119         va_start(args, width);
1120
1121         for (i = 0; i < num; i++) {
1122                 val = va_arg(args, unsigned long long);
1123                 if (!val) {
1124                         printf("%s", sc_zero_int_stat);
1125                 }
1126                 else {
1127                         printf("%s", sc_int_stat);
1128                 }
1129                 printf(" %*"PRIu64, width, val);
1130                 printf("%s", sc_normal);
1131         }
1132
1133         va_end(args);
1134 }
1135
1136 /*
1137  ***************************************************************************
1138  * Print hex values using colors.
1139  *
1140  * IN:
1141  * @num         Number of values to print.
1142  * @width       Output width.
1143  ***************************************************************************
1144 */
1145 void cprintf_x(int num, int width, ...)
1146 {
1147         int i;
1148         unsigned int val;
1149         va_list args;
1150
1151         va_start(args, width);
1152
1153         for (i = 0; i < num; i++) {
1154                 val = va_arg(args, unsigned int);
1155                 printf("%s", sc_int_stat);
1156                 printf(" %*x", width, val);
1157                 printf("%s", sc_normal);
1158         }
1159
1160         va_end(args);
1161 }
1162
1163 /*
1164  ***************************************************************************
1165  * Print "double" statistics values using colors.
1166  *
1167  * IN:
1168  * @num         Number of values to print.
1169  * @width       Output width.
1170  * @wd          Number of decimal places.
1171  ***************************************************************************
1172 */
1173 void cprintf_f(int num, int wi, int wd, ...)
1174 {
1175         int i;
1176         double val;
1177         va_list args;
1178
1179         va_start(args, wd);
1180
1181         for (i = 0; i < num; i++) {
1182                 val = va_arg(args, double);
1183                 if (((val < 0.005) && (val > -0.005)) ||
1184                     ((wd == 0) && (val < 0.5))) {
1185                         printf("%s", sc_zero_int_stat);
1186                 }
1187                 else {
1188                         printf("%s", sc_int_stat);
1189                 }
1190                 printf(" %*.*f", wi, wd, val);
1191                 printf("%s", sc_normal);
1192         }
1193
1194         va_end(args);
1195 }
1196
1197 /*
1198  ***************************************************************************
1199  * Print "percent" statistics values using colors.
1200  *
1201  * IN:
1202  * @num         Number of values to print.
1203  * @width       Output width.
1204  * @wd          Number of decimal places.
1205  ***************************************************************************
1206 */
1207 void cprintf_pc(int num, int wi, int wd, ...)
1208 {
1209         int i;
1210         double val;
1211         va_list args;
1212
1213         va_start(args, wd);
1214
1215         for (i = 0; i < num; i++) {
1216                 val = va_arg(args, double);
1217                 if (val >= PERCENT_LIMIT_HIGH) {
1218                         printf("%s", sc_percent_high);
1219                 }
1220                 else if (val >= PERCENT_LIMIT_LOW) {
1221                         printf("%s", sc_percent_low);
1222                 }
1223                 else if (val < 0.005) {
1224                         printf("%s", sc_zero_int_stat);
1225                 }
1226                 else {
1227                         printf("%s", sc_int_stat);
1228                 }
1229                 printf(" %*.*f", wi, wd, val);
1230                 printf("%s", sc_normal);
1231         }
1232
1233         va_end(args);
1234 }
1235
1236 /*
1237  ***************************************************************************
1238  * Print item name using selected color.
1239  * Only one name can be displayed. Name can be an integer or a string.
1240  *
1241  * IN:
1242  * @type        0 if name is an int, 1 if name is a string
1243  * @format      Output format.
1244  * @item_string Item name (given as a string of characters).
1245  * @item_int    Item name (given as an integer value).
1246  ***************************************************************************
1247 */
1248 void cprintf_in(int type, char *format, char *item_string, int item_int)
1249 {
1250         printf("%s", sc_item_name);
1251         if (type) {
1252                 printf(format, item_string);
1253         }
1254         else {
1255                 printf(format, item_int);
1256         }
1257         printf("%s", sc_normal);
1258 }
1259
1260 /*
1261  ***************************************************************************
1262  * Print a string using selected color.
1263  *
1264  * IN:
1265  * @type        Type of string to display.
1266  * @format      Output format.
1267  * @string      String to display.
1268  ***************************************************************************
1269 */
1270 void cprintf_s(int type, char *format, char *string)
1271 {
1272         if (type == IS_STR) {
1273                 printf("%s", sc_int_stat);
1274         }
1275         else if (type == IS_ZERO) {
1276                 printf("%s", sc_zero_int_stat);
1277         }
1278         else if (type == IS_RESTART) {
1279                 printf("%s", sc_sa_restart);
1280         }
1281         else {
1282                 /* IS_COMMENT */
1283                 printf("%s", sc_sa_comment);
1284         }
1285         printf(format, string);
1286         printf("%s", sc_normal);
1287 }