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