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