xref: /openbsd/usr.bin/top/utils.c (revision ead56677)
1*ead56677Smillert /* $OpenBSD: utils.c,v 1.29 2018/09/22 17:10:28 millert Exp $	 */
27cb5cbe1Sdownsj 
37cb5cbe1Sdownsj /*
47cb5cbe1Sdownsj  *  Top users/processes display for Unix
57cb5cbe1Sdownsj  *  Version 3
67cb5cbe1Sdownsj  *
77cb5cbe1Sdownsj  * Copyright (c) 1984, 1989, William LeFebvre, Rice University
87cb5cbe1Sdownsj  * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
9735f730eSderaadt  *
10735f730eSderaadt  * Redistribution and use in source and binary forms, with or without
11735f730eSderaadt  * modification, are permitted provided that the following conditions
12735f730eSderaadt  * are met:
13735f730eSderaadt  * 1. Redistributions of source code must retain the above copyright
14735f730eSderaadt  *    notice, this list of conditions and the following disclaimer.
15735f730eSderaadt  * 2. Redistributions in binary form must reproduce the above copyright
16735f730eSderaadt  *    notice, this list of conditions and the following disclaimer in the
17735f730eSderaadt  *    documentation and/or other materials provided with the distribution.
18735f730eSderaadt  *
19735f730eSderaadt  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20735f730eSderaadt  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21735f730eSderaadt  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22735f730eSderaadt  * IN NO EVENT SHALL THE AUTHOR OR HIS EMPLOYER BE LIABLE FOR ANY DIRECT, INDIRECT,
23735f730eSderaadt  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24735f730eSderaadt  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25735f730eSderaadt  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26735f730eSderaadt  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27735f730eSderaadt  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28735f730eSderaadt  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
297cb5cbe1Sdownsj  */
307cb5cbe1Sdownsj 
317cb5cbe1Sdownsj /*
327cb5cbe1Sdownsj  *  This file contains various handy utilities used by top.
337cb5cbe1Sdownsj  */
347cb5cbe1Sdownsj 
35b9fc9a72Sderaadt #include <sys/types.h>
364211733aSotto #include <sys/sysctl.h>
371643a62dSmillert #include <err.h>
3833729aa2Sdownsj #include <stdio.h>
3933729aa2Sdownsj #include <string.h>
4033729aa2Sdownsj #include <stdlib.h>
41fd43fba1Sotto #include <stdint.h>
42b9fc9a72Sderaadt #include <limits.h>
4333729aa2Sdownsj 
447cb5cbe1Sdownsj #include "top.h"
454211733aSotto #include "machine.h"
46d33b10b9Sderaadt #include "utils.h"
477cb5cbe1Sdownsj 
480e802f14Spvalchev int
atoiwi(char * str)490e802f14Spvalchev atoiwi(char *str)
507cb5cbe1Sdownsj {
513c5a1c27Sderaadt 	size_t len;
5241163b98Sotto 	const char *errstr;
5341163b98Sotto 	int i;
547cb5cbe1Sdownsj 
557cb5cbe1Sdownsj 	len = strlen(str);
562569d8ccSderaadt 	if (len != 0) {
577cb5cbe1Sdownsj 		if (strncmp(str, "infinity", len) == 0 ||
587cb5cbe1Sdownsj 		    strncmp(str, "all", len) == 0 ||
592569d8ccSderaadt 		    strncmp(str, "maximum", len) == 0) {
607cb5cbe1Sdownsj 			return (Infinity);
6141163b98Sotto 		}
6241163b98Sotto 		i = (int)strtonum(str, 0, INT_MAX, &errstr);
6341163b98Sotto 		if (errstr) {
647cb5cbe1Sdownsj 			return (Invalid);
6541163b98Sotto 		} else
6641163b98Sotto 			return (i);
677cb5cbe1Sdownsj 	}
687cb5cbe1Sdownsj 	return (0);
697cb5cbe1Sdownsj }
707cb5cbe1Sdownsj 
717cb5cbe1Sdownsj /*
723fc21ca4Smillert  * itoa - convert integer (decimal) to ascii string.
737cb5cbe1Sdownsj  */
740e802f14Spvalchev char *
itoa(int val)750e802f14Spvalchev itoa(int val)
767cb5cbe1Sdownsj {
777cb5cbe1Sdownsj 	static char buffer[16];	/* result is built here */
787cb5cbe1Sdownsj 
792569d8ccSderaadt 	/*
802569d8ccSderaadt 	 * 16 is sufficient since the largest number we will ever convert
812569d8ccSderaadt 	 * will be 2^32-1, which is 10 digits.
822569d8ccSderaadt 	 */
833fc21ca4Smillert 	(void)snprintf(buffer, sizeof(buffer), "%d", val);
843fc21ca4Smillert 	return (buffer);
857cb5cbe1Sdownsj }
867cb5cbe1Sdownsj 
877cb5cbe1Sdownsj /*
883fc21ca4Smillert  * format_uid(uid) - like itoa, except for uid_t and the number is right
893fc21ca4Smillert  * justified in a 6 character field to match uname_field in top.c.
907cb5cbe1Sdownsj  */
91cdc5a29bSmillert const char *
format_uid(uid_t uid,int nouser)9286e032d7Smillert format_uid(uid_t uid, int nouser)
937cb5cbe1Sdownsj {
943fc21ca4Smillert 	static char buffer[16];	/* result is built here */
957cb5cbe1Sdownsj 
963fc21ca4Smillert 	/*
973fc21ca4Smillert 	 * 16 is sufficient since the largest uid we will ever convert
983fc21ca4Smillert 	 * will be 2^32-1, which is 10 digits.
993fc21ca4Smillert 	 */
1003fc21ca4Smillert 	(void)snprintf(buffer, sizeof(buffer), "%6u", uid);
1013fc21ca4Smillert 	return (buffer);
1027cb5cbe1Sdownsj }
1037cb5cbe1Sdownsj 
1047cb5cbe1Sdownsj /*
1057cb5cbe1Sdownsj  * string_index(string, array) - find string in array and return index
1067cb5cbe1Sdownsj  */
1070e802f14Spvalchev int
string_index(char * string,char ** array)1080e802f14Spvalchev string_index(char *string, char **array)
1097cb5cbe1Sdownsj {
110c0932ef1Smpech 	int i = 0;
1117cb5cbe1Sdownsj 
1122569d8ccSderaadt 	while (*array != NULL) {
1133954a2d5Stedu 		if (strncmp(string, *array, strlen(string)) == 0)
1147cb5cbe1Sdownsj 			return (i);
1157cb5cbe1Sdownsj 		array++;
1167cb5cbe1Sdownsj 		i++;
1177cb5cbe1Sdownsj 	}
1187cb5cbe1Sdownsj 	return (-1);
1197cb5cbe1Sdownsj }
1207cb5cbe1Sdownsj 
1217cb5cbe1Sdownsj /*
1227cb5cbe1Sdownsj  * argparse(line, cntp) - parse arguments in string "line", separating them
1237cb5cbe1Sdownsj  * out into an argv-like array, and setting *cntp to the number of
1247cb5cbe1Sdownsj  * arguments encountered.  This is a simple parser that doesn't understand
1257cb5cbe1Sdownsj  * squat about quotes.
1267cb5cbe1Sdownsj  */
1270e802f14Spvalchev char **
argparse(char * line,int * cntp)1280e802f14Spvalchev argparse(char *line, int *cntp)
1297cb5cbe1Sdownsj {
1302569d8ccSderaadt 	char **argv, **argarray, *args, *from, *to;
1312569d8ccSderaadt 	int cnt, ch, length, lastch;
1327cb5cbe1Sdownsj 
1332569d8ccSderaadt 	/*
1342569d8ccSderaadt 	 * unfortunately, the only real way to do this is to go thru the
1352569d8ccSderaadt 	 * input string twice.
1362569d8ccSderaadt 	 */
1377cb5cbe1Sdownsj 
1387cb5cbe1Sdownsj 	/* step thru the string counting the white space sections */
1397cb5cbe1Sdownsj 	from = line;
1407cb5cbe1Sdownsj 	lastch = cnt = length = 0;
1412569d8ccSderaadt 	while ((ch = *from++) != '\0') {
1427cb5cbe1Sdownsj 		length++;
1437cb5cbe1Sdownsj 		if (ch == ' ' && lastch != ' ')
1447cb5cbe1Sdownsj 			cnt++;
1457cb5cbe1Sdownsj 		lastch = ch;
1467cb5cbe1Sdownsj 	}
1477cb5cbe1Sdownsj 
1482569d8ccSderaadt 	/*
1492569d8ccSderaadt 	 * add three to the count:  one for the initial "dummy" argument, one
1502569d8ccSderaadt 	 * for the last argument and one for NULL
1512569d8ccSderaadt 	 */
1527cb5cbe1Sdownsj 	cnt += 3;
1537cb5cbe1Sdownsj 
1547cb5cbe1Sdownsj 	/* allocate a char * array to hold the pointers */
1557e4e4b78Sderaadt 	if ((argarray = calloc(cnt, sizeof(char *))) == NULL)
1561643a62dSmillert 		err(1, NULL);
1577cb5cbe1Sdownsj 
1587cb5cbe1Sdownsj 	/* allocate another array to hold the strings themselves */
1591643a62dSmillert 	if ((args = malloc(length + 2)) == NULL)
1601643a62dSmillert 		err(1, NULL);
1617cb5cbe1Sdownsj 
1627cb5cbe1Sdownsj 	/* initialization for main loop */
1637cb5cbe1Sdownsj 	from = line;
1647cb5cbe1Sdownsj 	to = args;
1657cb5cbe1Sdownsj 	argv = argarray;
1667cb5cbe1Sdownsj 	lastch = '\0';
1677cb5cbe1Sdownsj 
1687cb5cbe1Sdownsj 	/* create a dummy argument to keep getopt happy */
1697cb5cbe1Sdownsj 	*argv++ = to;
1707cb5cbe1Sdownsj 	*to++ = '\0';
1717cb5cbe1Sdownsj 	cnt = 2;
1727cb5cbe1Sdownsj 
1737cb5cbe1Sdownsj 	/* now build argv while copying characters */
1747cb5cbe1Sdownsj 	*argv++ = to;
1752569d8ccSderaadt 	while ((ch = *from++) != '\0') {
1762569d8ccSderaadt 		if (ch != ' ') {
1772569d8ccSderaadt 			if (lastch == ' ') {
1787cb5cbe1Sdownsj 				*to++ = '\0';
1797cb5cbe1Sdownsj 				*argv++ = to;
1807cb5cbe1Sdownsj 				cnt++;
1817cb5cbe1Sdownsj 			}
1827cb5cbe1Sdownsj 			*to++ = ch;
1837cb5cbe1Sdownsj 		}
1847cb5cbe1Sdownsj 		lastch = ch;
1857cb5cbe1Sdownsj 	}
1867cb5cbe1Sdownsj 	*to++ = '\0';
1877cb5cbe1Sdownsj 
1887cb5cbe1Sdownsj 	/* set cntp and return the allocated array */
1897cb5cbe1Sdownsj 	*cntp = cnt;
1907cb5cbe1Sdownsj 	return (argarray);
1917cb5cbe1Sdownsj }
1927cb5cbe1Sdownsj 
1937cb5cbe1Sdownsj /*
1947cb5cbe1Sdownsj  * percentages(cnt, out, new, old, diffs) - calculate percentage change
195a11b5c97Sotto  * between array "old" and "new", putting the percentages in "out".
1967cb5cbe1Sdownsj  * "cnt" is size of each array and "diffs" is used for scratch space.
1977cb5cbe1Sdownsj  * The array "old" is updated on each call.
1987cb5cbe1Sdownsj  * The routine assumes modulo arithmetic.  This function is especially
199a11b5c97Sotto  * useful on BSD machines for calculating cpu state percentages.
2007cb5cbe1Sdownsj  */
2010e802f14Spvalchev int
percentages(int cnt,int64_t * out,int64_t * new,int64_t * old,int64_t * diffs)2021643a62dSmillert percentages(int cnt, int64_t *out, int64_t *new, int64_t *old, int64_t *diffs)
2037cb5cbe1Sdownsj {
2041643a62dSmillert 	int64_t change, total_change, *dp, half_total;
205c0932ef1Smpech 	int i;
2067cb5cbe1Sdownsj 
2077cb5cbe1Sdownsj 	/* initialization */
2087cb5cbe1Sdownsj 	total_change = 0;
2097cb5cbe1Sdownsj 	dp = diffs;
2107cb5cbe1Sdownsj 
2117cb5cbe1Sdownsj 	/* calculate changes for each state and the overall change */
2122569d8ccSderaadt 	for (i = 0; i < cnt; i++) {
2132569d8ccSderaadt 		if ((change = *new - *old) < 0) {
2147cb5cbe1Sdownsj 			/* this only happens when the counter wraps */
215fd43fba1Sotto 			change = INT64_MAX - *old + *new;
2167cb5cbe1Sdownsj 		}
2177cb5cbe1Sdownsj 		total_change += (*dp++ = change);
2187cb5cbe1Sdownsj 		*old++ = *new++;
2197cb5cbe1Sdownsj 	}
2207cb5cbe1Sdownsj 
2217cb5cbe1Sdownsj 	/* avoid divide by zero potential */
2227cb5cbe1Sdownsj 	if (total_change == 0)
2237cb5cbe1Sdownsj 		total_change = 1;
2247cb5cbe1Sdownsj 
2257cb5cbe1Sdownsj 	/* calculate percentages based on overall change, rounding up */
2267cb5cbe1Sdownsj 	half_total = total_change / 2l;
2277cb5cbe1Sdownsj 	for (i = 0; i < cnt; i++)
22833729aa2Sdownsj 		*out++ = ((*diffs++ * 1000 + half_total) / total_change);
2297cb5cbe1Sdownsj 
2307cb5cbe1Sdownsj 	/* return the total in case the caller wants to use it */
2317cb5cbe1Sdownsj 	return (total_change);
2327cb5cbe1Sdownsj }
2337cb5cbe1Sdownsj 
2342569d8ccSderaadt /*
2352569d8ccSderaadt  * format_time(seconds) - format number of seconds into a suitable display
2362569d8ccSderaadt  * that will fit within 6 characters.  Note that this routine builds its
2372569d8ccSderaadt  * string in a static area.  If it needs to be called more than once without
2382569d8ccSderaadt  * overwriting previous data, then we will need to adopt a technique similar
2392569d8ccSderaadt  * to the one used for format_k.
2407cb5cbe1Sdownsj  */
2417cb5cbe1Sdownsj 
2422569d8ccSderaadt /*
2432569d8ccSderaadt  * Explanation: We want to keep the output within 6 characters.  For low
2442569d8ccSderaadt  * values we use the format mm:ss.  For values that exceed 999:59, we switch
2452569d8ccSderaadt  * to a format that displays hours and fractions:  hhh.tH.  For values that
2462569d8ccSderaadt  * exceed 999.9, we use hhhh.t and drop the "H" designator.  For values that
2472569d8ccSderaadt  * exceed 9999.9, we use "???".
2487cb5cbe1Sdownsj  */
2497cb5cbe1Sdownsj 
2500e802f14Spvalchev char *
format_time(time_t seconds)2510e802f14Spvalchev format_time(time_t seconds)
2527cb5cbe1Sdownsj {
2537cb5cbe1Sdownsj 	static char result[10];
2547cb5cbe1Sdownsj 
2557cb5cbe1Sdownsj 	/* sanity protection */
2562569d8ccSderaadt 	if (seconds < 0 || seconds > (99999l * 360l)) {
2577c861855Sderaadt 		strlcpy(result, "   ???", sizeof result);
2582569d8ccSderaadt 	} else if (seconds >= (1000l * 60l)) {
2597cb5cbe1Sdownsj 		/* alternate (slow) method displaying hours and tenths */
2607cb5cbe1Sdownsj 		snprintf(result, sizeof(result), "%5.1fH",
2617cb5cbe1Sdownsj 		    (double) seconds / (double) (60l * 60l));
2627cb5cbe1Sdownsj 
2632569d8ccSderaadt 		/*
2642569d8ccSderaadt 		 * It is possible that the snprintf took more than 6
2652569d8ccSderaadt 		 * characters. If so, then the "H" appears as result[6].  If
2662569d8ccSderaadt 		 * not, then there is a \0 in result[6].  Either way, it is
2672569d8ccSderaadt 		 * safe to step on.
2687cb5cbe1Sdownsj 		 */
2697cb5cbe1Sdownsj 		result[6] = '\0';
2702569d8ccSderaadt 	} else {
2717cb5cbe1Sdownsj 		/* standard method produces MMM:SS */
2727cb5cbe1Sdownsj 		/* we avoid printf as must as possible to make this quick */
27342cfecbbSguenther 		snprintf(result, sizeof(result), "%3d:%02d", (int)seconds / 60,
27442cfecbbSguenther 		    (int)seconds % 60);
2757cb5cbe1Sdownsj 	}
2767cb5cbe1Sdownsj 	return (result);
2777cb5cbe1Sdownsj }
2787cb5cbe1Sdownsj 
2797cb5cbe1Sdownsj /*
2807cb5cbe1Sdownsj  * format_k(amt) - format a kilobyte memory value, returning a string
2817cb5cbe1Sdownsj  * suitable for display.  Returns a pointer to a static
2827cb5cbe1Sdownsj  * area that changes each call.  "amt" is converted to a
2837cb5cbe1Sdownsj  * string with a trailing "K".  If "amt" is 10000 or greater,
2847cb5cbe1Sdownsj  * then it is formatted as megabytes (rounded) with a
2857cb5cbe1Sdownsj  * trailing "M".
2867cb5cbe1Sdownsj  */
2877cb5cbe1Sdownsj 
2887cb5cbe1Sdownsj /*
2897cb5cbe1Sdownsj  * Compromise time.  We need to return a string, but we don't want the
2907cb5cbe1Sdownsj  * caller to have to worry about freeing a dynamically allocated string.
2917cb5cbe1Sdownsj  * Unfortunately, we can't just return a pointer to a static area as one
2927979a71eSderaadt  * of the common uses of this function is in a large call to snprintf where
2937cb5cbe1Sdownsj  * it might get invoked several times.  Our compromise is to maintain an
2947cb5cbe1Sdownsj  * array of strings and cycle thru them with each invocation.  We make the
2957cb5cbe1Sdownsj  * array large enough to handle the above mentioned case.  The constant
2967cb5cbe1Sdownsj  * NUM_STRINGS defines the number of strings in this array:  we can tolerate
2977cb5cbe1Sdownsj  * up to NUM_STRINGS calls before we start overwriting old information.
2987cb5cbe1Sdownsj  * Keeping NUM_STRINGS a power of two will allow an intelligent optimizer
2997cb5cbe1Sdownsj  * to convert the modulo operation into something quicker.  What a hack!
3007cb5cbe1Sdownsj  */
3017cb5cbe1Sdownsj 
3027cb5cbe1Sdownsj #define NUM_STRINGS 8
3037cb5cbe1Sdownsj 
3040e802f14Spvalchev char *
format_k(int amt)3050e802f14Spvalchev format_k(int amt)
3067cb5cbe1Sdownsj {
3077cb5cbe1Sdownsj 	static char retarray[NUM_STRINGS][16];
3083c5a1c27Sderaadt 	static int  idx = 0;
3093fc21ca4Smillert 	char *ret, tag = 'K';
3107cb5cbe1Sdownsj 
3113c5a1c27Sderaadt 	ret = retarray[idx];
3123c5a1c27Sderaadt 	idx = (idx + 1) % NUM_STRINGS;
3137cb5cbe1Sdownsj 
3142569d8ccSderaadt 	if (amt >= 10000) {
3157cb5cbe1Sdownsj 		amt = (amt + 512) / 1024;
3167cb5cbe1Sdownsj 		tag = 'M';
3172569d8ccSderaadt 		if (amt >= 10000) {
3187cb5cbe1Sdownsj 			amt = (amt + 512) / 1024;
3197cb5cbe1Sdownsj 			tag = 'G';
3207cb5cbe1Sdownsj 		}
3217cb5cbe1Sdownsj 	}
3223fc21ca4Smillert 	snprintf(ret, sizeof(retarray[0]), "%d%c", amt, tag);
3237cb5cbe1Sdownsj 	return (ret);
3247cb5cbe1Sdownsj }
3254211733aSotto 
3264211733aSotto int
find_pid(pid_t pid)3274211733aSotto find_pid(pid_t pid)
3284211733aSotto {
3295027561dSguenther 	struct kinfo_proc *pbase, *cur;
330df816b2eSpat 	int nproc;
3314211733aSotto 
3324211733aSotto 	if ((pbase = getprocs(KERN_PROC_KTHREAD, 0, &nproc)) == NULL)
3334211733aSotto 		quit(23);
3344211733aSotto 
335df816b2eSpat 	for (cur = pbase; cur < &pbase[nproc]; cur++)
3364211733aSotto 		if (cur->p_pid == pid)
3374211733aSotto 			return 1;
3384211733aSotto 	return 0;
3394211733aSotto }
340