1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <sys/wait.h>
4 
5 #include <err.h>
6 #include <fcntl.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 
11 #define NETPERF_CMD	"netperf"
12 #define NETPERF_PATH	"/usr/local/bin/" NETPERF_CMD
13 
14 struct netperf_child {
15 	int		pipes[2];
16 };
17 
18 static void
19 usage(const char *cmd)
20 {
21 	fprintf(stderr, "%s -H host [-l len_s] [-i instances] [-m msgsz] "
22 	    "[-r|-s]\n", cmd);
23 	exit(1);
24 }
25 
26 int
27 main(int argc, char *argv[])
28 {
29 	struct netperf_child *instance;
30 	char len_str[32];
31 	char *args[32];
32 	const char *host, *msgsz;
33 	volatile int ninst, set_minmax = 0;
34 	int len, ninst_done;
35 	int opt, i, null_fd;
36 	volatile int reverse = 0, sfile = 0;
37 	double result, res_max, res_min, jain;
38 
39 	host = NULL;
40 	ninst = 2;
41 	len = 10;
42 	msgsz = NULL;
43 
44 	while ((opt = getopt(argc, argv, "H:i:l:m:rs")) != -1) {
45 		switch (opt) {
46 		case 'H':
47 			host = optarg;
48 			break;
49 
50 		case 'i':
51 			ninst = strtoul(optarg, NULL, 10);
52 			break;
53 
54 		case 'l':
55 			len = strtoul(optarg, NULL, 10);
56 			break;
57 
58 		case 'm':
59 			msgsz = optarg;
60 			break;
61 
62 		case 'r':
63 			reverse = 1;
64 			sfile = 0;
65 			break;
66 
67 		case 's':
68 			reverse = 0;
69 			sfile = 1;
70 			break;
71 
72 		default:
73 			usage(argv[0]);
74 		}
75 	}
76 	if (ninst <= 0 || host == NULL || len <= 0)
77 		usage(argv[0]);
78 
79 	snprintf(len_str, sizeof(len_str), "%d", len);
80 
81 	i = 0;
82 	args[i++] = __DECONST(char *, NETPERF_CMD);
83 	args[i++] = __DECONST(char *, "-P0");
84 	args[i++] = __DECONST(char *, "-H");
85 	args[i++] = __DECONST(char *, host);
86 	args[i++] = __DECONST(char *, "-l");
87 	args[i++] = __DECONST(char *, len_str);
88 	args[i++] = __DECONST(char *, "-t");
89 	if (reverse)
90 		args[i++] = __DECONST(char *, "TCP_MAERTS");
91 	else if (sfile)
92 		args[i++] = __DECONST(char *, "TCP_SENDFILE");
93 	else
94 		args[i++] = __DECONST(char *, "TCP_STREAM");
95 	if (msgsz != NULL) {
96 		args[i++] = __DECONST(char *, "--");
97 		args[i++] = __DECONST(char *, "-m");
98 		args[i++] = __DECONST(char *, msgsz);
99 	}
100 	args[i] = NULL;
101 
102 	instance = calloc(ninst, sizeof(struct netperf_child));
103 	if (instance == NULL)
104 		err(1, "calloc failed");
105 
106 	null_fd = open("/dev/null", O_RDWR);
107 	if (null_fd < 0)
108 		err(1, "open null failed");
109 
110 	for (i = 0; i < ninst; ++i) {
111 		if (pipe(instance[i].pipes) < 0)
112 			err(1, "pipe %dth failed", i);
113 	}
114 
115 	for (i = 0; i < ninst; ++i) {
116 		pid_t pid;
117 
118 		pid = vfork();
119 		if (pid == 0) {
120 			int ret;
121 
122 			dup2(instance[i].pipes[1], STDOUT_FILENO);
123 			dup2(null_fd, STDERR_FILENO);
124 			ret = execv(NETPERF_PATH, args);
125 			if (ret < 0) {
126 				warn("execv %d failed", i);
127 				_exit(1);
128 			}
129 			/* Never reached */
130 			abort();
131 		} else if (pid < 0) {
132 			err(1, "vfork %d failed", i);
133 		}
134 		close(instance[i].pipes[1]);
135 		instance[i].pipes[1] = -1;
136 	}
137 
138 	ninst_done = 0;
139 	while (ninst_done < ninst) {
140 		pid_t pid;
141 
142 		pid = waitpid(-1, NULL, 0);
143 		if (pid < 0)
144 			err(1, "waitpid failed");
145 		++ninst_done;
146 	}
147 
148 	res_max = 0.0;
149 	res_min = 0.0;
150 	jain = 0.0;
151 	result = 0.0;
152 	for (i = 0; i < ninst; ++i) {
153 		char line[128];
154 		FILE *fp;
155 
156 		fp = fdopen(instance[i].pipes[0], "r");
157 		if (fp == NULL)
158 			err(1, "fdopen %dth failed", i);
159 
160 		while (fgets(line, sizeof(line), fp) != NULL) {
161 			int n, arg1, arg2, arg3;
162 			double res, arg4;
163 
164 			n = sscanf(line, "%d%d%d%lf%lf",
165 			    &arg1, &arg2, &arg3, &arg4, &res);
166 			if (n == 5) {
167 				if (!set_minmax) {
168 					res_max = res;
169 					res_min = res;
170 					set_minmax = 1;
171 				} else {
172 					if (res > res_max)
173 						res_max = res;
174 					if (res < res_min)
175 						res_min = res;
176 				}
177 				jain += (res * res);
178 				result += res;
179 				break;
180 			}
181 		}
182 		fclose(fp);
183 	}
184 
185 	jain *= ninst;
186 	jain = (result * result) / jain;
187 
188 	printf("%s %.2f Mbps\n", reverse ? "TCP_MAERTS" : "TCP_STREAM", result);
189 	printf("min/max (jain) %.2f Mbps/%.2f Mbps (%f)\n",
190 	    res_min, res_max, jain);
191 
192 	exit(0);
193 }
194