Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Erve

macrumors newbie
Original poster
Jun 19, 2014
3
0
I'm trying to get a similar output to that of Activity Monitor's total network In/Out KB/s via the command line.

However, I can't use sudo, which rules out lsof and netstat's -w option results in "streaming" output: I need a command which returns/prints the total In/Out KB/s for en0 (wifi), then "ends" (i.e. doesn't "stream").

Is this possible? I've no objection to installing a third-party command via homebrew if there's a suitable tool out there.

Thanks!

EDIT: I know iStat Menus (and Activity Monitor) offer this, but I need to be able to call this script from another application and parse the output, so those apps don't help in this case.
 
This source code is based on the netstat utility source on Apple's Open Source website. Pass in an interface name on the command-line. Defaults to 'en0'. Computes the input and output bytes per second over a 2 second interval.

Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/if_mib.h>
#include <net/route.h>

#include <netinet/in.h>
#include <netinet/in_var.h>
#include <unistd.h>

static u_int64_t opackets = 0;
static u_int64_t ipackets = 0;
static u_int64_t obytes = 0;
static u_int64_t ibytes = 0;

int 
get_stats(char *interface)
{
	int		ret = 0;
	char		name      [32];
	int		mib        [6];
	char           *buf = NULL, *lim, *next;
	size_t		len;
	struct if_msghdr *ifm;
	unsigned int	ifindex = 0;

	if (interface != 0)
		ifindex = if_nametoindex(interface);

	mib[0] = CTL_NET;
	mib[1] = PF_ROUTE;
	mib[2] = 0;
	mib[3] = 0;
	mib[4] = NET_RT_IFLIST2;
	mib[5] = 0;

	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
		return ret;
	if ((buf = malloc(len)) == NULL) {
		printf("malloc failed\n");
		exit(1);
	}
	if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
		if (buf)
			free(buf);
		return ret;
	}
	lim = buf + len;
	for (next = buf; next < lim;) {
		ifm = (struct if_msghdr *)next;
		next += ifm->ifm_msglen;

		if (ifm->ifm_type == RTM_IFINFO2) {
			struct if_msghdr2 *if2m = (struct if_msghdr2 *)ifm;
			struct sockaddr_dl *sdl = (struct sockaddr_dl *)(if2m + 1);

			strncpy(name, sdl->sdl_data, sdl->sdl_nlen);
			name[sdl->sdl_nlen] = 0;
			if (interface != 0 && if2m->ifm_index != ifindex)
				continue;

			/*
			 * Get the interface stats.  These may get overriden
			 * below on a per-interface basis.
			 */
			opackets = if2m->ifm_data.ifi_opackets;
			ipackets = if2m->ifm_data.ifi_ipackets;
			obytes = if2m->ifm_data.ifi_obytes;
			ibytes = if2m->ifm_data.ifi_ibytes;
			if (ret == 0) {
				printf("%-5.5s %8.8s %10.10s ",
				       "Name", "Ipkts", "IBytes");
				printf("%8.8s %10.10s\n", "Opkts", "Obytes");
				ret = 1;
			}
			printf("%-5.5s %-8.8llu ", name, ipackets);
			printf("%10.10llu ", ibytes);
			printf("%-8.8llu ", opackets);
			printf("%10.10llu\n", obytes);
		}
	}

        free(buf);

	return ret;
}

int 
main(int argc, char *argv[])
{
	char           *ifname;
	int		r;
	int		sleeptime = 2;

	if (argc > 1) {
		ifname = argv[1];
	} else {
		ifname = "en0";
	}

	r = get_stats(ifname);
	if (r) {
		u_int64_t	ib = ibytes;
		u_int64_t	ob = obytes;

		sleep(sleeptime);
		get_stats(ifname);
		printf("%llu %llu\n", (ibytes - ib) / sleeptime, (obytes - ob) / sleeptime);
	} else {
		printf("No interface %s\n", ifname);
	}
}
 
Last edited:
Thanks mfram!

We're getting out of my comfort zone here.... How to call this from the command line?
 
It's C code. It needs to be compiled. I assumed you were familiar with that and could modify it to your needs. I guess not. I'll clean it up, make command-line parameters and post source and an executable.
 
Updated code with actual command-line parameters. You can specify interface name, and time interval. See the comments.

Code:
/*
 * Print interface statistics in bytes per second
 * 
 * Parameters: 
 * -t #		Time interval to compute, default 2 seconds
 * -i <ifname>	Interface name, defaults to 'en0' 
 * -s		Print raw stats
 * 
 * Output: Two numbers separated by a space. First value is input bytes per
 * second. Second value is output bytes per second
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/if_mib.h>
#include <net/route.h>

#include <netinet/in.h>
#include <netinet/in_var.h>
#include <unistd.h>

static u_int64_t opackets = 0;
static u_int64_t ipackets = 0;
static u_int64_t obytes = 0;
static u_int64_t ibytes = 0;
static int	raw_stats = 0;

int
get_stats(char *interface)
{
	int		ret = 0;
	char		name      [32];
	int		mib        [6];
	char           *buf = NULL, *lim, *next;
	size_t		len;
	struct if_msghdr *ifm;
	unsigned int	ifindex = 0;

	if (interface != 0)
		ifindex = if_nametoindex(interface);

	mib[0] = CTL_NET;
	mib[1] = PF_ROUTE;
	mib[2] = 0;
	mib[3] = 0;
	mib[4] = NET_RT_IFLIST2;
	mib[5] = 0;

	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
		return ret;
	if ((buf = malloc(len)) == NULL) {
		printf("malloc failed\n");
		exit(1);
	}
	if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
		if (buf)
			free(buf);
		return ret;
	}
	lim = buf + len;
	for (next = buf; next < lim;) {
		ifm = (struct if_msghdr *)next;
		next += ifm->ifm_msglen;

		if (ifm->ifm_type == RTM_IFINFO2) {
			struct if_msghdr2 *if2m = (struct if_msghdr2 *)ifm;
			struct sockaddr_dl *sdl = (struct sockaddr_dl *)(if2m + 1);

			strncpy(name, sdl->sdl_data, sdl->sdl_nlen);
			name[sdl->sdl_nlen] = 0;
			if (interface != 0 && if2m->ifm_index != ifindex)
				continue;

			/*
			 * Get the interface stats.  These may get overriden
			 * below on a per-interface basis.
			 */
			opackets = if2m->ifm_data.ifi_opackets;
			ipackets = if2m->ifm_data.ifi_ipackets;
			obytes = if2m->ifm_data.ifi_obytes;
			ibytes = if2m->ifm_data.ifi_ibytes;
			if (ret == 0) {
				ret = 1;
				if (raw_stats) {
					printf("%5s %10s %14s ",
					       "Name", "Ipkts", "IBytes");
					printf("%10s %14s\n", "Opkts", "Obytes");
				}
			}
			if (raw_stats) {
				printf("%-5s %10llu ", name, ipackets);
				printf("%14llu ", ibytes);
				printf("%10llu ", opackets);
				printf("%14llu\n", obytes);
			}
		}
	}

	free(buf);

	return ret;
}

void
usage(void)
{
	printf("arguments:\n\t-t #\t\tTime interval to compute, default 2 seconds\n");
	printf("\t-i <ifname>\tInterface name, defaults to 'en0'\n");
	printf("\t-s\t\tPrint raw stats\n");
	exit(1);
}

int
main(int argc, char *argv[])
{
	char           *ifname = "en0";
	int		r;
	int		sleeptime = 2;

	int		bflag     , ch, fd;

	bflag = 0;
	while ((ch = getopt(argc, argv, "st:i:")) != -1) {
		switch (ch) {
		case 't':
			sleeptime = atoi(optarg);
			if (sleeptime < 1) {
				sleeptime = 1;
			}
			break;
		case 'i':
			ifname = optarg;
			break;
		case 's':
			raw_stats = 1;
			break;
		default:
			usage();
		}
	}
	argc -= optind;
	argv += optind;

	r = get_stats(ifname);
	if (r) {
		u_int64_t	ib = ibytes;
		u_int64_t	ob = obytes;
		u_int64_t	diffo, diffi;

		sleep(sleeptime);
		get_stats(ifname);
		diffo = (obytes - ob) / (u_int64_t) sleeptime;
		diffi = (ibytes - ib) / (u_int64_t) sleeptime;
		printf("%llu %llu\n", diffi, diffo);
	} else {
		printf("No interface %s\n", ifname);
	}
}
 

Attachments

  • netst.zip
    3.7 KB · Views: 214
Unzip the netst.zip. Inside is the source code and an executable called 'netst'.

The executable must be marked executable in the shell. (Only has to be done once.)

Code:
$ chmod u+x netst
$ ./netst
51939 807
$ ./netst -i en0 -t 3 -s
 Name      Ipkts         IBytes      Opkts         Obytes
en0       124326      165387520      52059        6387271
 Name      Ipkts         IBytes      Opkts         Obytes
en0       124397      165490944      52076        6388885
34474 538
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.