]> granicus.if.org Git - sysstat/blob - common.c
Merge pull request #346 from Kwstubbs/Kwstubbs/add-codeql-workflow
[sysstat] / common.c
1 /*
2  * sar, sadc, sadf, mpstat and iostat common routines.
3  * (C) 1999-2022 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 <limits.h>
35 #include <libgen.h>
36
37 #include "version.h"
38 #include "common.h"
39 #include "ioconf.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 decimal places */
50 extern int dplaces_nr;
51
52 /* Units (sectors, Bytes, kilobytes, etc.) */
53 char units[] = {'s', 'B', 'k', 'M', 'G', 'T', 'P', '?'};
54
55 /* Number of ticks per second */
56 unsigned long hz;
57 /* Number of bit shifts to convert pages to kB */
58 unsigned int kb_shift;
59
60 /* Colors strings */
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;
69
70 /*
71  * Type of persistent device names in lowercase letters
72  * (e.g. "uuid", "label", "path"...) Used in sar and iostat.
73  */
74 char persistent_name_type[MAX_FILE_LEN];
75
76 /*
77  ***************************************************************************
78  * Print sysstat version number and exit.
79  ***************************************************************************
80  */
81 void print_version(void)
82 {
83         printf(_("sysstat version %s\n"), VERSION);
84         printf("(C) Sebastien Godard (sysstat <at> orange.fr)\n");
85         exit(0);
86 }
87
88 /*
89  ***************************************************************************
90  * Get date and time, expressed in UTC or in local time.
91  *
92  * IN:
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.
95  *
96  * OUT:
97  * @rectime     Current local date and time.
98  *
99  * RETURNS:
100  * Value of time in seconds since the Epoch (always in UTC)
101  ***************************************************************************
102  */
103 time_t get_xtime(struct tm *rectime, int d_off, int utc)
104 {
105         time_t timer;
106
107         timer = __time(NULL);
108         timer -= SEC_PER_DAY * d_off;
109
110         if (utc) {
111                 /* Get date and time in UTC */
112                 gmtime_r(&timer, rectime);
113         }
114         else {
115                 /* Get date and time in local time */
116                 localtime_r(&timer, rectime);
117         }
118
119         return timer;
120 }
121
122 /*
123  ***************************************************************************
124  * Get date and time and take into account <ENV_TIME_DEFTM> variable.
125  *
126  * IN:
127  * @d_off       Day offset (number of days to go back in the past).
128  *
129  * OUT:
130  * @rectime     Current date and time.
131  *
132  * RETURNS:
133  * Value of time in seconds since the Epoch.
134  ***************************************************************************
135  */
136 time_t get_time(struct tm *rectime, int d_off)
137 {
138         static int utc = 0;
139         char *e;
140
141         if (!utc) {
142                 /* Read environment variable value once */
143                 if ((e = __getenv(ENV_TIME_DEFTM)) != NULL) {
144                         utc = !strcmp(e, K_UTC);
145                 }
146                 utc++;
147         }
148
149         return get_xtime(rectime, d_off, utc == 2);
150 }
151
152 #ifdef USE_NLS
153 /*
154  ***************************************************************************
155  * Init National Language Support.
156  ***************************************************************************
157  */
158 void init_nls(void)
159 {
160         setlocale(LC_MESSAGES, "");
161         setlocale(LC_CTYPE, "");
162         setlocale(LC_TIME, "");
163         setlocale(LC_NUMERIC, "");
164
165         bindtextdomain(PACKAGE, LOCALEDIR);
166         textdomain(PACKAGE);
167 }
168 #endif
169
170 /*
171  ***************************************************************************
172  * Test whether given name is a device or a partition, using sysfs.
173  *
174  * IN:
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.
180  *
181  * RETURNS:
182  * TRUE if @name is a device, and FALSE if it's a partition.
183  ***************************************************************************
184  */
185 int is_device(char *sysdev, char *name, int allow_virtual)
186 {
187         char syspath[PATH_MAX];
188         char *slash;
189
190         /* Some devices may have a slash in their name (eg. cciss/c0d0...) */
191         while ((slash = strchr(name, '/'))) {
192                 *slash = '!';
193         }
194         snprintf(syspath, sizeof(syspath), "%s/%s/%s%s", sysdev, __BLOCK, name,
195                  allow_virtual ? "" : "/device");
196
197         return !(access(syspath, F_OK));
198 }
199
200 /*
201  ***************************************************************************
202  * Get page shift in kB.
203  ***************************************************************************
204  */
205 void get_kb_shift(void)
206 {
207         int shift = 0;
208         long size;
209
210         /* One can also use getpagesize() to get the size of a page */
211         if ((size = sysconf(_SC_PAGESIZE)) == -1) {
212                 perror("sysconf");
213         }
214
215         size >>= 10;    /* Assume that a page has a minimum size of 1 kB */
216
217         while (size > 1) {
218                 shift++;
219                 size >>= 1;
220         }
221
222         kb_shift = (unsigned int) shift;
223 }
224
225 /*
226  ***************************************************************************
227  * Get number of clock ticks per second.
228  ***************************************************************************
229  */
230 void get_HZ(void)
231 {
232         long ticks;
233
234         if ((ticks = sysconf(_SC_CLK_TCK)) == -1) {
235                 perror("sysconf");
236         }
237
238         hz = (unsigned long) ticks;
239 }
240
241 /*
242  ***************************************************************************
243  * Unhandled situation: Panic and exit. Should never happen.
244  *
245  * IN:
246  * @function    Function name where situation occured.
247  * @error_code  Error code.
248  ***************************************************************************
249  */
250 void sysstat_panic(const char *function, int error_code)
251 {
252         fprintf(stderr, "sysstat: %s[%d]: Internal error...\n",
253                 function, error_code);
254         exit(1);
255 }
256
257 /*
258  ***************************************************************************
259  * Extract WWWN identifiers from filename, as read from /dev/disk/by-id.
260  *
261  * Sample valid names read from /dev/disk/by-id:
262  * wwn-0x5000cca369f193ac
263  * wwn-0x5000cca369f193ac-part12
264  * wwn-0x600605b00a2bdf00242b28c10dcb1999
265  * wwn-0x600605b00a2bdf00242b28c10dcb1999-part2
266  *
267  * WWN ids like these ones are ignored:
268  * wwn-0x5438850077615869953x
269  * wwn-0x5438850077615869953x-part1
270  *
271  * IN:
272  * @name        Filename read from /dev/disk/by-id.
273  *
274  * OUT:
275  * @wwn         WWN identifier (8 or 16 hex characters).
276  * @part-nr     Partition number if applicable.
277  *
278  * RETURNS:
279  * 0 on success, -1 otherwise.
280  ***************************************************************************
281 */
282 int extract_wwnid(char *name, unsigned long long *wwn, unsigned int *part_nr)
283 {
284         char id[17];
285         char *s;
286         int wwnlen;
287
288         *wwn = *(wwn + 1) = 0ULL;
289         *part_nr = 0;
290
291         /* Check name */
292         if (((wwnlen = strlen(name)) < 22) || (strncmp(name, "wwn-0x", 6)))
293                 return -1;
294
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)
299                         return -1;
300                 wwnlen = s - name - 6;
301         }
302         else {
303                 wwnlen -= 6;    /* Don't count "wwn-0x" */
304         }
305
306         /* Check WWN length */
307         if ((wwnlen != 16) && (wwnlen != 32))
308                 return -1;
309
310         /* Extract first 16 hex chars of WWN */
311         strncpy(id, name + 6, 16);
312         id[16] = '\0';
313         if (sscanf(id, "%llx", wwn) == 0)
314                 return -1;
315
316         if (strlen(name) < 38)
317                 /* This is a short (16 hex chars) WWN id */
318                 return 0;
319
320         /* Extract second part of WWN */
321         if (sscanf(name + 22, "%llx", wwn + 1) == 0)
322                 return -1;
323
324         return 0;
325 }
326
327 /*
328  ***************************************************************************
329  * Get WWWN identifiers from a pretty filename using links present in
330  * /dev/disk/by-id directory.
331  *
332  * IN:
333  * @pretty      Pretty name (e.g. sda, sdb3...).
334  *
335  * OUT:
336  * @wwn         WWN identifier (8 or 16 hex characters).
337  * @part-nr     Partition number if applicable.
338  *
339  * RETURNS:
340  * 0 on success, -1 otherwise.
341  ***************************************************************************
342 */
343 int get_wwnid_from_pretty(char *pretty, unsigned long long *wwn, unsigned int *part_nr)
344 {
345         DIR *dir;
346         struct dirent *drd;
347         ssize_t r;
348         char link[PATH_MAX], target[PATH_MAX], wwn_name[FILENAME_MAX];
349         char *name;
350         int rc = -1;
351
352         /* Open  /dev/disk/by-id directory */
353         if ((dir = opendir(DEV_DISK_BY_ID)) == NULL)
354                 return -1;
355
356         /* Get current id */
357         while ((drd = readdir(dir)) != NULL) {
358
359                 if (strncmp(drd->d_name, "wwn-0x", 6))
360                         continue;
361
362                 /* Get absolute path for current persistent name */
363                 snprintf(link, PATH_MAX, "%s/%s", DEV_DISK_BY_ID, drd->d_name);
364
365                 /* Persistent name is usually a symlink: Read it... */
366                 r = readlink(link, target, PATH_MAX);
367                 if ((r <= 0) || (r >= PATH_MAX))
368                         continue;
369
370                 target[r] = '\0';
371
372                 /* ... and get device pretty name it points at */
373                 name = basename(target);
374                 if (!name || (name[0] == '\0'))
375                         continue;
376
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';
381
382                         /* Try to extract WWN */
383                         if (!extract_wwnid(wwn_name, wwn, part_nr)) {
384                                 /* WWN successfully extracted */
385                                 rc = 0;
386                                 break;
387                         }
388                 }
389         }
390
391         /* Close directory */
392         closedir(dir);
393
394         return rc;
395 }
396
397 /*
398  ***************************************************************************
399  * Check if a directory exists.
400  *
401  * IN:
402  * @dirname     Name of the directory.
403  *
404  * RETURNS:
405  * TRUE if @dirname is actually an existing directory.
406  ***************************************************************************
407  */
408 int check_dir(char *dirname)
409 {
410         struct stat sb;
411
412         if (!stat(dirname, &sb) && S_ISDIR(sb.st_mode))
413                 return 1;
414
415         return 0;
416 }
417
418 /*
419  * **************************************************************************
420  * Check if the multiplication of the 3 values may be greater than UINT_MAX.
421  *
422  * IN:
423  * @val1        First value.
424  * @val2        Second value.
425  * @val3        Third value.
426  ***************************************************************************
427  */
428 void check_overflow(unsigned int val1, unsigned int val2,
429                     unsigned int val3)
430 {
431         if ((unsigned long long) val1 * (unsigned long long) val2 *
432             (unsigned long long) val3 > UINT_MAX) {
433 #ifdef DEBUG
434                 fprintf(stderr, "%s: Overflow detected (%llu). Aborting...\n",
435                         __FUNCTION__, (unsigned long long) val1 * (unsigned long long) val2 *
436                         (unsigned long long) val3);
437 #endif
438         exit(4);
439                 }
440 }
441
442 #ifndef SOURCE_SADC
443 /*
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.
447  *
448  * RETURNS:
449  * Device-mapper major number.
450  ***************************************************************************
451  */
452 unsigned int get_devmap_major(void)
453 {
454         FILE *fp;
455         char line[128];
456         /*
457          * Linux uses 12 bits for the major number,
458          * so this shouldn't match any real device.
459          */
460         unsigned int dm_major = ~0U;
461
462         if ((fp = fopen(DEVICES, "r")) == NULL)
463                 return dm_major;
464
465         while (fgets(line, sizeof(line), fp) != NULL) {
466
467                 if (strstr(line, "device-mapper")) {
468                         /* Read device-mapper major number */
469                         sscanf(line, "%u", &dm_major);
470                 }
471         }
472
473         fclose(fp);
474
475         return dm_major;
476 }
477
478 /*
479  ***************************************************************************
480  * Returns whether S_TIME_FORMAT is set to ISO.
481  *
482  * RETURNS:
483  * TRUE if S_TIME_FORMAT is set to ISO, or FALSE otherwise.
484  ***************************************************************************
485  */
486 int is_iso_time_fmt(void)
487 {
488         static int is_iso = -1;
489         char *e;
490
491         if (is_iso < 0) {
492                 is_iso = (((e = __getenv(ENV_TIME_FMT)) != NULL) && !strcmp(e, K_ISO));
493         }
494         return is_iso;
495 }
496
497 /*
498  ***************************************************************************
499  * Print tabulations
500  *
501  * IN:
502  * @nr_tab      Number of tabs to print.
503  ***************************************************************************
504  */
505 void prtab(int nr_tab)
506 {
507         int i;
508
509         for (i = 0; i < nr_tab; i++) {
510                 printf("\t");
511         }
512 }
513
514 /*
515  ***************************************************************************
516  * printf() function modified for XML-like output. Don't print a CR at the
517  * end of the line.
518  *
519  * IN:
520  * @nr_tab      Number of tabs to print.
521  * @fmtf        printf() format.
522  ***************************************************************************
523  */
524 void xprintf0(int nr_tab, const char *fmtf, ...)
525 {
526         static char buf[1024];
527         va_list args;
528
529         va_start(args, fmtf);
530         vsnprintf(buf, sizeof(buf), fmtf, args);
531         va_end(args);
532
533         prtab(nr_tab);
534         printf("%s", buf);
535 }
536
537 /*
538  ***************************************************************************
539  * printf() function modified for XML-like output. Print a CR at the end of
540  * the line.
541  *
542  * IN:
543  * @nr_tab      Number of tabs to print.
544  * @fmtf        printf() format.
545  ***************************************************************************
546  */
547 void xprintf(int nr_tab, const char *fmtf, ...)
548 {
549         static char buf[1024];
550         va_list args;
551
552         va_start(args, fmtf);
553         vsnprintf(buf, sizeof(buf), fmtf, args);
554         va_end(args);
555
556         prtab(nr_tab);
557         printf("%s\n", buf);
558 }
559
560 /*
561  ***************************************************************************
562  * Get report date as a string of characters.
563  *
564  * IN:
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.
568  *
569  * OUT:
570  * @cur_date    Date (string format).
571  *
572  * RETURNS:
573  * TRUE if S_TIME_FORMAT is set to ISO, or FALSE otherwise.
574  ***************************************************************************
575  */
576 int set_report_date(struct tm *rectime, char cur_date[], int sz)
577 {
578         if (rectime == NULL) {
579                 strncpy(cur_date, "?/?/?", sz);
580                 cur_date[sz - 1] = '\0';
581         }
582         else if (is_iso_time_fmt()) {
583                 strftime(cur_date, sz, "%Y-%m-%d", rectime);
584                 return 1;
585         }
586         else {
587                 strftime(cur_date, sz, "%x", rectime);
588         }
589
590         return 0;
591 }
592
593 /*
594  ***************************************************************************
595  * Print banner.
596  *
597  * IN:
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.
606  *
607  * RETURNS:
608  * TRUE if S_TIME_FORMAT is set to ISO, or FALSE otherwise.
609  ***************************************************************************
610  */
611 int print_gal_header(struct tm *rectime, char *sysname, char *release,
612                      char *nodename, char *machine, int cpu_nr, int format)
613 {
614         char cur_date[TIMESTAMP_LEN];
615         int rc = 0;
616
617         rc = set_report_date(rectime, cur_date, sizeof(cur_date));
618
619         if (format == PLAIN_OUTPUT) {
620                 /* Plain output */
621                 printf("%s %s (%s) \t%s \t_%s_\t(%d CPU)\n", sysname, release, nodename,
622                        cur_date, machine, cpu_nr);
623         }
624         else {
625                 /* JSON output */
626                 xprintf(0, "{\"sysstat\": {");
627                 xprintf(1, "\"hosts\": [");
628                 xprintf(2, "{");
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\": [");
636         }
637
638         return rc;
639 }
640
641 /*
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.
646  *
647  * RETURNS:
648  * Number of rows.
649  ***************************************************************************
650  */
651 int get_win_height(void)
652 {
653         struct winsize win;
654         char *e;
655         /*
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
658          */
659         int rows = 3600 * 24;
660
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;
665                 }
666         }
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)) {
670                         int v = atol(e);
671                         if (v > 0) {
672                                 rows = v;
673                         }
674                 }
675         }
676         return rows;
677 }
678
679 /*
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
685  *
686  * IN:
687  * @name        Device name (may begin with "/dev/" or can be a symlink).
688  *
689  * RETURNS:
690  * Device basename.
691  ***************************************************************************
692  */
693 char *device_name(char *name)
694 {
695         static char out[MAX_FILE_LEN];
696         char *resolved_name = NULL, *slash;
697         int i = 0;
698
699         /* realpath() creates new string, so we need to free it later */
700         resolved_name = __realpath(name, NULL);
701
702         /* If path doesn't exist, just return input */
703         if (!resolved_name) {
704                 return name;
705         }
706
707 #ifdef DEBUG
708         fprintf(stderr, "Real pathname: %s (%s)\n", resolved_name, name);
709 #endif
710
711         if (!strncmp(resolved_name, "/dev/", 5)) {
712                 i = 5;
713         }
714         strncpy(out, resolved_name + i, sizeof(out));
715         out[sizeof(out) - 1] = '\0';
716
717         /* Some devices may have a slash in their name (eg. cciss/c0d0...) */
718         while ((slash = strchr(out, '/'))) {
719                 *slash = '!';
720         }
721
722         free(resolved_name);
723
724         return out;
725 }
726
727 /*
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  ***************************************************************************
732  */
733 double ll_sp_value(unsigned long long value1, unsigned long long value2,
734                    unsigned long long itv)
735 {
736         if (value2 < value1)
737                 return (double) 0;
738         else
739                 return SP_VALUE(value1, value2, itv);
740 }
741
742 /*
743  ***************************************************************************
744  * Compute time interval.
745  *
746  * IN:
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).
749  *
750  * RETURNS:
751  * Interval of time in jiffies or 1/100th of a second.
752  ***************************************************************************
753  */
754 unsigned long long get_interval(unsigned long long prev_uptime,
755                                 unsigned long long curr_uptime)
756 {
757         unsigned long long itv;
758
759         /* prev_time=0 when displaying stats since system startup */
760         itv = curr_uptime - prev_uptime;
761
762         if (!itv) {     /* Paranoia checking */
763                 itv = 1;
764         }
765
766         return itv;
767 }
768
769 /*
770  ***************************************************************************
771  * Count number of bits set in an array.
772  *
773  * IN:
774  * @ptr         Pointer to array.
775  * @size        Size of array in bytes.
776  *
777  * RETURNS:
778  * Number of bits set in the array.
779  ***************************************************************************
780 */
781 int count_bits(void *ptr, int size)
782 {
783         int nr = 0, i, k;
784         char *p;
785
786         p = ptr;
787         for (i = 0; i < size; i++, p++) {
788                 k = 0x80;
789                 while (k) {
790                         if (*p & k)
791                                 nr++;
792                         k >>= 1;
793                 }
794         }
795
796         return nr;
797 }
798
799 /*
800  ***************************************************************************
801  * Convert in-place input string to lowercase.
802  *
803  * IN:
804  * @str         String to be converted.
805  *
806  * OUT:
807  * @str         String in lowercase.
808  *
809  * RETURNS:
810  * String in lowercase.
811  ***************************************************************************
812 */
813 char *strtolower(char *str)
814 {
815         char *cp = str;
816
817         while (*cp) {
818                 *cp = tolower(*cp);
819                 cp++;
820         }
821
822         return(str);
823 }
824
825 /*
826  ***************************************************************************
827  * Get persistent type name directory from type.
828  *
829  * IN:
830  * @type        Persistent type name (UUID, LABEL, etc.)
831  *
832  * RETURNS:
833  * Path to the persistent type name directory, or NULL if access is denied
834  * or strings have been truncated.
835  ***************************************************************************
836 */
837 char *get_persistent_type_dir(char *type)
838 {
839         static char dir[PATH_MAX];
840         int n;
841
842         n = snprintf(dir, sizeof(dir), "%s-%s", DEV_DISK_BY, type);
843
844         if ((n >= sizeof(dir)) || access(dir, R_OK)) {
845                 return (NULL);
846         }
847
848         return (dir);
849 }
850
851 /*
852  ***************************************************************************
853  * Get persistent name absolute path.
854  *
855  * IN:
856  * @name        Persistent name.
857  *
858  * RETURNS:
859  * Path to the persistent name, or NULL if file doesn't exist or strings
860  * have been truncated.
861  ***************************************************************************
862 */
863 char *get_persistent_name_path(char *name)
864 {
865         static char path[PATH_MAX];
866         int n;
867
868         n = snprintf(path, sizeof(path), "%s/%s",
869                      get_persistent_type_dir(persistent_name_type), name);
870
871         if ((n >= sizeof(path)) || access(path, F_OK)) {
872                 return (NULL);
873         }
874
875         return (path);
876 }
877
878 /*
879  ***************************************************************************
880  * Get files from persistent type name directory.
881  *
882  * RETURNS:
883  * List of files in the persistent type name directory in alphabetical order.
884  ***************************************************************************
885 */
886 char **get_persistent_names(void)
887 {
888         int n, i, k = 0;
889         char *dir;
890         char **files = NULL;
891         struct dirent **namelist;
892
893         /* Get directory name for selected persistent type */
894         dir = get_persistent_type_dir(persistent_name_type);
895         if (!dir)
896                 return (NULL);
897
898         n = scandir(dir, &namelist, NULL, alphasort);
899         if (n < 0)
900                 return (NULL);
901
902         /* If directory is empty, it contains 2 entries: "." and ".." */
903         if (n <= 2)
904                 /* Free list and return NULL */
905                 goto free_list;
906
907         /* Ignore the "." and "..", but keep place for one last NULL. */
908         files = (char **) calloc(n - 1, sizeof(char *));
909         if (!files)
910                 goto free_list;
911
912         /*
913          * i is for traversing namelist, k is for files.
914          * i != k because we are ignoring "." and ".." entries.
915          */
916         for (i = 0; i < n; i++) {
917                 /* Ignore "." and ".." */
918                 if (!strcmp(".", namelist[i]->d_name) ||
919                     !strcmp("..", namelist[i]->d_name))
920                         continue;
921
922                 files[k] = (char *) calloc(strlen(namelist[i]->d_name) + 1, sizeof(char));
923                 if (!files[k])
924                         continue;
925
926                 strcpy(files[k++], namelist[i]->d_name);
927         }
928         files[k] = NULL;
929
930 free_list:
931
932         for (i = 0; i < n; i++) {
933                 free(namelist[i]);
934         }
935         free(namelist);
936
937         return (files);
938 }
939
940 /*
941  ***************************************************************************
942  * Get persistent name from pretty name.
943  *
944  * IN:
945  * @pretty      Pretty name (e.g. sda, sda1, ..).
946  *
947  * RETURNS:
948  * Persistent name.
949  ***************************************************************************
950 */
951 char *get_persistent_name_from_pretty(char *pretty)
952 {
953         int i = -1;
954         ssize_t r;
955         char *link, *name;
956         char **persist_names;
957         char target[PATH_MAX];
958         static char persist_name[FILENAME_MAX];
959
960         persist_name[0] = '\0';
961
962         /* Get list of files from persistent type name directory */
963         persist_names = get_persistent_names();
964         if (!persist_names)
965                 return (NULL);
966
967         while (persist_names[++i]) {
968
969                 /* Get absolute path for current persistent name */
970                 link = get_persistent_name_path(persist_names[i]);
971                 if (!link)
972                         continue;
973
974                 /* Persistent name is usually a symlink: Read it... */
975                 r = readlink(link, target, PATH_MAX);
976                 if ((r <= 0) || (r >= PATH_MAX))
977                         continue;
978
979                 target[r] = '\0';
980
981                 /* ... and get device pretty name it points at */
982                 name = basename(target);
983                 if (!name || (name[0] == '\0'))
984                         continue;
985
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';
990                         break;
991                 }
992         }
993
994         i = -1;
995         while (persist_names[++i]) {
996                 free (persist_names[i]);
997         }
998         free (persist_names);
999
1000         if (strlen(persist_name) <= 0)
1001                 return (NULL);
1002
1003         return persist_name;
1004 }
1005
1006 /*
1007  ***************************************************************************
1008  * Get pretty name (sda, sda1...) from persistent name.
1009  *
1010  * IN:
1011  * @persistent  Persistent name.
1012  *
1013  * RETURNS:
1014  * Pretty name.
1015  ***************************************************************************
1016 */
1017 char *get_pretty_name_from_persistent(char *persistent)
1018 {
1019         ssize_t r;
1020         char *link, *pretty, target[PATH_MAX];
1021
1022         /* Get absolute path for persistent name */
1023         link = get_persistent_name_path(persistent);
1024         if (!link)
1025                 return (NULL);
1026
1027         /* Persistent name is usually a symlink: Read it... */
1028         r = readlink(link, target, PATH_MAX);
1029         if ((r <= 0) || (r >= PATH_MAX))
1030                 return (NULL);
1031
1032         target[r] = '\0';
1033
1034         /* ... and get device pretty name it points at */
1035         pretty = basename(target);
1036         if (!pretty || (pretty[0] == '\0'))
1037                 return (NULL);
1038
1039         return pretty;
1040 }
1041
1042 /*
1043  * **************************************************************************
1044  * Try to get device real name from sysfs tree.
1045  *
1046  * IN:
1047  * @major       Major number of the device.
1048  * @minor       Minor number of the device.
1049  *
1050  * RETURNS:
1051  * The name of the device, which may be the real name (as it appears in /dev)
1052  * or NULL.
1053  ***************************************************************************
1054  */
1055 char *get_devname_from_sysfs(unsigned int major, unsigned int minor)
1056 {
1057         static char link[256], target[PATH_MAX];
1058         char *devname;
1059         ssize_t r;
1060
1061         snprintf(link, sizeof(link), "%s/%u:%u", SYSFS_DEV_BLOCK, major, minor);
1062
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)
1066                 return (NULL);
1067
1068         target[r] = '\0';
1069
1070         /* Get device name */
1071         devname = basename(target);
1072         if (!devname || strnlen(devname, FILENAME_MAX) == 0) {
1073                 return (NULL);
1074         }
1075
1076         return (devname);
1077 }
1078
1079 /*
1080  * **************************************************************************
1081  * Get device real name if possible.
1082  *
1083  * IN:
1084  * @major       Major number of the device.
1085  * @minor       Minor number of the device.
1086  *
1087  * RETURNS:
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  ***************************************************************************
1091  */
1092 char *get_devname(unsigned int major, unsigned int minor)
1093 {
1094         static char buf[32];
1095         char *name;
1096
1097         name = get_devname_from_sysfs(major, minor);
1098         if (name != NULL)
1099                 return (name);
1100
1101         name = ioc_name(major, minor);
1102         if ((name != NULL) && strcmp(name, K_NODEV))
1103                 return (name);
1104
1105         snprintf(buf, sizeof(buf), "dev%u-%u", major, minor);
1106         return (buf);
1107 }
1108
1109 /*
1110  * **************************************************************************
1111  * Get device name (whether pretty-printed, persistent or not).
1112  *
1113  * IN:
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).
1122  *
1123  * RETURNS:
1124  * The name of the device.
1125  ***************************************************************************
1126  */
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,
1130                       char *dflt_name)
1131 {
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] = "";
1136
1137         if (disp_persist_name) {
1138                 persist_dev_name = get_persistent_name_from_pretty(get_devname(major, minor));
1139         }
1140
1141         if (persist_dev_name) {
1142                 dev_name = persist_dev_name;
1143         }
1144         else {
1145                 if (use_stable_id && (wwn[0] != 0)) {
1146                         if (wwn[1] != 0) {
1147                                 sprintf(xsid, "%016llx", wwn[1]);
1148                         }
1149                         if (part_nr) {
1150                                 sprintf(pn, "-%d", part_nr);
1151                         }
1152                         snprintf(sid, sizeof(sid), "%#016llx%s%s", wwn[0], xsid, pn);
1153                         dev_name = sid;
1154                 }
1155                 else if (disp_devmap_name) {
1156                         if (!dm_major) {
1157                                 dm_major = get_devmap_major();
1158                         }
1159                         if (major == dm_major) {
1160                                 dev_name = transform_devmapname(major, minor);
1161                         }
1162                 }
1163
1164                 if (!dev_name) {
1165                         if (dflt_name) {
1166                                 dev_name = dflt_name;
1167                         }
1168                         else {
1169                                 dev_name = get_devname(major, minor);
1170                         }
1171                 }
1172         }
1173
1174         strncpy(dname, dev_name, sizeof(dname));
1175         dname[sizeof(dname) - 1] = '\0';
1176
1177         while ((bang = strchr(dname, '!'))) {
1178                 /*
1179                  * Some devices may have had a slash replaced with
1180                  * a bang character (eg. cciss!c0d0...)
1181                  * Restore their original names.
1182                  */
1183                 *bang = '/';
1184         }
1185
1186         return dname;
1187 }
1188
1189 /*
1190  ***************************************************************************
1191  * Init color strings.
1192  ***************************************************************************
1193  */
1194 void init_colors(void)
1195 {
1196         char *e, *p;
1197         int len;
1198
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)))) {
1204                 /*
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.
1209                  */
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, "");
1218
1219                 return;
1220         }
1221
1222         /* Read S_COLORS_SGR environment variable */
1223         if ((e = __getenv(ENV_COLORS_SGR)) == NULL)
1224                 /* Environment variable not set */
1225                 return;
1226
1227         for (p = strtok(e, ":"); p; p =strtok(NULL, ":")) {
1228
1229                 len = strlen(p);
1230                 if ((len > 7) || (len < 3) || (*(p + 1) != '=') ||
1231                     (strspn(p + 2, ";0123456789") != (len - 2)))
1232                         /* Ignore malformed codes */
1233                         continue;
1234
1235                 switch (*p) {
1236                         case 'H':
1237                                 snprintf(sc_percent_high, MAX_SGR_LEN, "\e[%sm", p + 2);
1238                                 break;
1239                         case 'M':
1240                                 snprintf(sc_percent_low, MAX_SGR_LEN, "\e[%sm", p + 2);
1241                                 break;
1242                         case 'Z':
1243                                 snprintf(sc_zero_int_stat, MAX_SGR_LEN, "\e[%sm", p + 2);
1244                                 break;
1245                         case 'N':
1246                                 snprintf(sc_int_stat, MAX_SGR_LEN, "\e[%sm", p + 2);
1247                                 break;
1248                         case 'I':
1249                                 snprintf(sc_item_name, MAX_SGR_LEN, "\e[%sm", p + 2);
1250                                 break;
1251                         case 'C':
1252                                 snprintf(sc_sa_comment, MAX_SGR_LEN, "\e[%sm", p + 2);
1253                                 break;
1254                         case 'R':
1255                                 snprintf(sc_sa_restart, MAX_SGR_LEN, "\e[%sm", p + 2);
1256                                 break;
1257                 }
1258         }
1259 }
1260
1261 /*
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.)
1265  *
1266  * IN:
1267  * @unit        Default value unit.
1268  * @dval        Value to print.
1269  * @wi          Output width.
1270  ***************************************************************************
1271 */
1272 void cprintf_unit(int unit, int wi, double dval)
1273 {
1274         if (wi < 4) {
1275                 /* E.g. 1.3M */
1276                 wi = 4;
1277         }
1278         if (!unit) {
1279                 /* Value is a number of sectors. Convert it to kB */
1280                 dval /= 2;
1281                 unit = 2;
1282         }
1283         while (dval >= 1024) {
1284                 dval /= 1024;
1285                 unit++;
1286         }
1287         printf(" %*.*f", wi - 1, dplaces_nr ? 1 : 0, dval);
1288         printf("%s", sc_normal);
1289
1290         /* Display unit */
1291         if (unit >= NR_UNITS) {
1292                 unit = NR_UNITS - 1;
1293         }
1294         printf("%c", units[unit]);
1295 }
1296
1297 /*
1298  ***************************************************************************
1299  * Print 64 bit unsigned values using colors, possibly followed by a unit.
1300  *
1301  * IN:
1302  * @unit        Default values unit. -1 if no unit should be displayed.
1303  * @num         Number of values to print.
1304  * @wi          Output width.
1305  ***************************************************************************
1306 */
1307 void cprintf_u64(int unit, int num, int wi, ...)
1308 {
1309         int i;
1310         uint64_t val;
1311         va_list args;
1312
1313         va_start(args, wi);
1314
1315         for (i = 0; i < num; i++) {
1316                 val = va_arg(args, unsigned long long);
1317                 if (!val) {
1318                         printf("%s", sc_zero_int_stat);
1319                 }
1320                 else {
1321                         printf("%s", sc_int_stat);
1322                 }
1323                 if (unit < 0) {
1324                         printf(" %*"PRIu64, wi, val);
1325                         printf("%s", sc_normal);
1326                 }
1327                 else {
1328                         cprintf_unit(unit, wi, (double) val);
1329                 }
1330         }
1331
1332         va_end(args);
1333 }
1334
1335 /*
1336  ***************************************************************************
1337  * Print hex values using colors.
1338  *
1339  * IN:
1340  * @num         Number of values to print.
1341  * @wi          Output width.
1342  ***************************************************************************
1343 */
1344 void cprintf_x(int num, int wi, ...)
1345 {
1346         int i;
1347         unsigned int val;
1348         va_list args;
1349
1350         va_start(args, wi);
1351
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);
1357         }
1358
1359         va_end(args);
1360 }
1361
1362 /*
1363  ***************************************************************************
1364  * Print "double" statistics values using colors, possibly followed by a
1365  * unit.
1366  *
1367  * IN:
1368  * @unit        Default values unit. -1 if no unit should be displayed.
1369  * @num         Number of values to print.
1370  * @wi          Output width.
1371  * @wd          Number of decimal places.
1372  ***************************************************************************
1373 */
1374 void cprintf_f(int unit, int num, int wi, int wd, ...)
1375 {
1376         int i;
1377         double val, lim = 0.005;;
1378         va_list args;
1379
1380         /*
1381          * If there are decimal places then get the value
1382          * entered on the command line (if existing).
1383          */
1384         if ((wd > 0) && (dplaces_nr >= 0)) {
1385                 wd = dplaces_nr;
1386         }
1387
1388         /* Update limit value according to number of decimal places */
1389         if (wd == 1) {
1390                 lim = 0.05;
1391         }
1392
1393         va_start(args, wd);
1394
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);
1400                 }
1401                 else {
1402                         printf("%s", sc_int_stat);
1403                 }
1404
1405                 if (unit < 0) {
1406                         printf(" %*.*f", wi, wd, val);
1407                         printf("%s", sc_normal);
1408                 }
1409                 else {
1410                         cprintf_unit(unit, wi, val);
1411                 }
1412         }
1413
1414         va_end(args);
1415 }
1416
1417 /*
1418  ***************************************************************************
1419  * Print "percent" statistics values using colors.
1420  *
1421  * IN:
1422  * @human       Set to > 0 if a percent sign (%) shall be displayed after
1423  *              the value.
1424  * @num         Number of values to print.
1425  * @wi          Output width.
1426  * @wd          Number of decimal places.
1427  ***************************************************************************
1428 */
1429 void cprintf_pc(int human, int num, int wi, int wd, ...)
1430 {
1431         int i;
1432         double val, lim = 0.005;
1433         va_list args;
1434
1435         /*
1436          * If there are decimal places then get the value
1437          * entered on the command line (if existing).
1438          */
1439         if ((wd > 0) && (dplaces_nr >= 0)) {
1440                 wd = dplaces_nr;
1441         }
1442
1443         /*
1444          * If a percent sign is to be displayed, then there will be
1445          * zero (or one) decimal place.
1446          */
1447         if (human > 0) {
1448                 if (wi < 4) {
1449                         /* E.g., 100% */
1450                         wi = 4;
1451                 }
1452                 /* Keep one place for the percent sign */
1453                 wi -= 1;
1454                 if (wd > 1) {
1455                         wd -= 1;
1456                 }
1457         }
1458
1459         /* Update limit value according to number of decimal places */
1460         if (wd == 1) {
1461                 lim = 0.05;
1462         }
1463
1464         va_start(args, wd);
1465
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);
1470                 }
1471                 else if (val >= PERCENT_LIMIT_LOW) {
1472                         printf("%s", sc_percent_low);
1473                 }
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);
1477                 }
1478                 else {
1479                         printf("%s", sc_int_stat);
1480                 }
1481                 printf(" %*.*f", wi, wd, val);
1482                 printf("%s", sc_normal);
1483                 if (human > 0) printf("%%");
1484         }
1485
1486         va_end(args);
1487 }
1488
1489 /*
1490  ***************************************************************************
1491  * Print item name using selected color.
1492  * Only one name can be displayed. Name can be an integer or a string.
1493  *
1494  * IN:
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  ***************************************************************************
1500 */
1501 void cprintf_in(int type, char *format, char *item_string, int item_int)
1502 {
1503         printf("%s", sc_item_name);
1504         if (type) {
1505                 printf(format, item_string);
1506         }
1507         else {
1508                 printf(format, item_int);
1509         }
1510         printf("%s", sc_normal);
1511 }
1512
1513 /*
1514  ***************************************************************************
1515  * Print a string using selected color.
1516  *
1517  * IN:
1518  * @type        Type of string to display.
1519  * @format      Output format.
1520  * @string      String to display.
1521  ***************************************************************************
1522 */
1523 void cprintf_s(int type, char *format, char *string)
1524 {
1525         if (type == IS_STR) {
1526                 printf("%s", sc_int_stat);
1527         }
1528         else if (type == IS_ZERO) {
1529                 printf("%s", sc_zero_int_stat);
1530         }
1531         /* IS_RESTART and IS_DEBUG are the same value */
1532         else if (type == IS_RESTART) {
1533                 printf("%s", sc_sa_restart);
1534         }
1535         else {
1536                 /* IS_COMMENT */
1537                 printf("%s", sc_sa_comment);
1538         }
1539         printf(format, string);
1540         printf("%s", sc_normal);
1541 }
1542
1543 /*
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.
1547  *
1548  * IN:
1549  * @s           String to parse.
1550  * @max_val     Upper limit that value should not reach.
1551  *
1552  * OUT:
1553  * @val         Value, or -1 if the string @s was empty.
1554  *
1555  * RETURNS:
1556  * 0 if the value has been properly read, 1 otherwise.
1557  ***************************************************************************
1558  */
1559 int parse_valstr(char *s, int max_val, int *val)
1560 {
1561         if (!strlen(s)) {
1562                 *val = -1;
1563                 return 0;
1564         }
1565         if (strspn(s, DIGITS) != strlen(s))
1566                 return 1;
1567
1568         *val = atoi(s);
1569         if ((*val < 0) || (*val >= max_val))
1570                 return 1;
1571
1572         return 0;
1573 }
1574
1575 /*
1576  ***************************************************************************
1577  * Parse string containing a single value or a range of values
1578  * (e.g. "0,2-5,10-").
1579  *
1580  * IN:
1581  * @t           String to parse.
1582  * @max_val     Upper limit that value should not reach.
1583  *
1584  * OUT:
1585  * @val_low     Low value in range
1586  * @val         High value in range. @val_low and @val are the same if it's
1587  *              a single value.
1588  *
1589  * RETURNS:
1590  * 0 on success, 1 otherwise.
1591  ***************************************************************************
1592  */
1593 int parse_range_values(char *t, int max_val, int *val_low, int *val)
1594 {
1595         char *s, *valstr, range[16];
1596
1597         /* Parse value or range of values */
1598         strncpy(range, t, 16);
1599         range[15] = '\0';
1600         valstr = t;
1601
1602         if ((s = strchr(range, '-')) != NULL) {
1603                 /* Possible range of values */
1604                 *s = '\0';
1605                 if (parse_valstr(range, max_val, val_low) || (*val_low < 0))
1606                         return 1;
1607                 valstr = s + 1;
1608         }
1609         if (parse_valstr(valstr, max_val, val))
1610                 return 1;
1611         if (s && *val < 0) {
1612                 /* Range of values with no upper limit (e.g. "3-") */
1613                 *val = max_val - 1;
1614         }
1615         if ((!s && (*val < 0)) || (s && (*val < *val_low)))
1616                 /*
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.
1620                  */
1621                 return 1;
1622         if (!s) {
1623                 *val_low = *val;
1624         }
1625         return 0;
1626 }
1627
1628 /*
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.
1633  *
1634  * IN:
1635  * @strargv     Current argument in list to parse.
1636  * @bitmap      Bitmap whose contents will indicate which values have been
1637  *              selected.
1638  * @max_val     Upper limit that value should not reach.
1639  * @__K_VALUE0  Keyword corresponding to the first bit in bitmap (e.g "all",
1640  *              "SUM"...)
1641  *
1642  * OUT:
1643  * @bitmap      Bitmap updated with selected values.
1644  *
1645  * RETURNS:
1646  * 0 on success, 1 otherwise.
1647  ***************************************************************************
1648  */
1649 int parse_values(char *strargv, unsigned char bitmap[], int max_val, const char *__K_VALUE0)
1650 {
1651         int i, val_low, val;
1652         char *t;
1653
1654         if (!strcmp(strargv, K_ALL)) {
1655                 /* Set bit for every possible values (CPU, IRQ, etc.) */
1656                 memset(bitmap, ~0, BITMAP_SIZE(max_val));
1657                 return 0;
1658         }
1659
1660         for (t = strtok(strargv, ","); t; t = strtok(NULL, ",")) {
1661                 if (!strcmp(t, __K_VALUE0)) {
1662                         /*
1663                          * Set bit 0 in bitmap. This may correspond
1664                          * to CPU "all" or IRQ "SUM" for example.
1665                          */
1666                         bitmap[0] |= 1;
1667                 }
1668                 else {
1669                         /* Parse value or range of values */
1670                         if (parse_range_values(t, max_val, &val_low, &val))
1671                                 return 1;
1672
1673                         for (i = val_low; i <= val; i++) {
1674                                 bitmap[(i + 1) >> 3] |= 1 << ((i + 1) & 0x07);
1675                         }
1676                 }
1677         }
1678
1679         return 0;
1680 }
1681
1682 #endif /* SOURCE_SADC undefined */