xref: /netbsd/usr.sbin/altq/altqstat/quip_client.c (revision bf9ec67e)
1 /*	$NetBSD: quip_client.c,v 1.6 2002/03/05 04:11:52 itojun Exp $	*/
2 /*	$KAME: quip_client.c,v 1.7 2001/12/28 00:50:28 itojun Exp $	*/
3 /*
4  * Copyright (C) 1999-2000
5  *	Sony Computer Science Laboratories, Inc.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/param.h>
30 #include <sys/socket.h>
31 #include <sys/un.h>
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <err.h>
39 
40 #include "quip_client.h"
41 #include "altqstat.h"
42 
43 /*
44  * quip (queue information protocol) is a http-like protocol
45  * in order to retrieve information from the server.
46  * a unix domain TCP socket "/var/run/altq_quip" is used for
47  * client-server style communication.
48  *
49  * there are 2 quip message types: request and response.
50  * request format: (only single-line request message is used at this moment)
51  *	request-line
52  *
53  *      request-line = <method> <operation>[?<query>] <quip-version>
54  *	<method> = GET (only GET is defined at this moment)
55  *	<operation> = list | handle-to-name | qdisc | filter
56  *	query format is operation dependent but most query takes
57  *	<interface> or <class> or <filter>.
58  *	<interface> = <if_name>
59  *	<class>     = <if_name>:<class_path>/<class_name>
60  *	<filter>    = <if_name>:<class_path>/<class_name>:<filter_name>
61  *	"list" operation accepts "*" as a wildcard.
62  *
63  * response format:
64  *	status-line
65  * 	response-headers (0 or more)
66  *	<blank line>
67  *	body
68  *
69  *	status-line = <quip-version> <status-code> <reason phrase>
70  *	response-header = Content-Length:<value>
71  *
72  *	"Content-Length" specifies the length of the message body.
73  *
74  * example:
75  *	to retrieve a list of classes (handle and name) on interface "fxp0":
76  *	a request message looks like,
77  *		GET list?fxp0:* QUIP/1.0<cr>
78  *	a response message looks like,
79  *		QUIP/1.0 200 OK<cr>
80  *		Content-Length:86<cr>
81  *		<cr>
82  *		0000000000	fxp0:/root<cr>
83  *		0xc0d1be00	fxp0:/root/parent<cr>
84  *		0xc0d1ba00	fxp0:/root/parent/child<cr>
85  *
86  *	other examples:
87  *	list all interfaces, classes, and filters:
88  *		GET list QUIP/1.0<cr>
89  *	list all interfaces:
90  *		GET list?* QUIP/1.0<cr>
91  *	list all classes:
92  *		GET list?*:* QUIP/1.0<cr>
93  *	list all filters:
94  *		GET list?*:*:* QUIP/1.0<cr>
95  *	convert class handle to class name:
96  *		GET handle-to-name?fxp0:0xc0d1be00 QUIP/1.0<cr>
97  *	convert filter handle to filter name:
98  *		GET handle-to-name?fxp0::0x1000000a QUIP/1.0<cr>
99  */
100 
101 #define	MAXLINESIZE	1024
102 
103 enum nametype { INTERFACE, CLASS, FILTER, CONDITIONER };
104 
105 static FILE *server = NULL;
106 int quip_echo = 0;
107 
108 static char *extract_ifname(const char *);
109 
110 int
111 quip_openserver(void)
112 {
113 	struct sockaddr_un addr;
114 	int fd;
115 
116 	if ((fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
117 		err(1, "can't open socket");
118 
119 	bzero(&addr, sizeof(addr));
120 	addr.sun_family = AF_LOCAL;
121 	strlcpy(addr.sun_path, QUIP_PATH,sizeof(addr.sun_path));
122 
123 	if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
124 		fprintf(stderr, "can't talk to altqd!\n"
125 			"probably, altqd is not running\n");
126 		return (-1);
127 	}
128 
129 	if ((server = fdopen(fd, "r+")) == NULL) {
130 		warn("fdopen: can't open stream to the quip server");
131 		return (-1);
132 	}
133 	return (0);
134 }
135 
136 int
137 quip_closeserver(void)
138 {
139 	if (server != NULL)
140 		return fclose(server);
141 	return (0);
142 }
143 
144 void
145 quip_sendrequest(FILE *fp, const char *request)
146 {
147 	char buf[QUIPMSG_MAXSIZE], *cp;
148 	int n;
149 
150 	if ((cp = strstr(request, "QUIP")) == NULL) {
151 		cp = strchr(request, '\n');
152 		n = cp - request;
153 		if (cp == NULL || n > REQ_MAXSIZE - 10)
154 			return;
155 		strncpy(buf, request, n);
156 		snprintf(buf + n, REQ_MAXSIZE - n, " QUIP/1.0");
157 		strlcat(buf, cp, REQ_MAXSIZE);
158 	}
159 	else
160 		strlcpy(buf, request, REQ_MAXSIZE);
161 
162 	if (fputs(buf, fp) != 0)
163 		err(1, "fputs");
164 	if (fflush(fp) != 0)
165 		err(1, "fflush");
166 	if (quip_echo) {
167 		fputs("<< ", stdout);
168 		fputs(buf, stdout);
169 	}
170 }
171 
172 /*
173  * recv_response receives a response message from the server
174  * and returns status_code.
175  */
176 int
177 quip_recvresponse(FILE *fp, char *header, char *body, int *blen)
178 {
179 	char buf[MAXLINESIZE], version[MAXLINESIZE];
180 	int code, resid, len, buflen;
181 	int end_of_header = 0;
182 
183 	if (blen != NULL)
184 		*blen = 0;
185 	code = 0;
186 	resid = 0;
187 	buflen = RES_MAXSIZE;
188 	while (fgets(buf, sizeof(buf), fp) != 0) {
189 		if (quip_echo) {
190 			fputs(">  ", stdout);
191 			fputs(buf, stdout);
192 		}
193 
194 		if (!end_of_header) {
195 			/* process message header */
196 			if (header != NULL) {
197 				len = strlcpy(header, buf, buflen);
198 				if (len >= buflen) {
199 					/* header too long */
200 					fpurge(fp);
201 					return (-1);
202 				}
203 				header += len;
204 				buflen -= len;
205 			}
206 
207 			if (code == 0) {
208 				/* status line expected */
209 				if (buf[0] == '\n') {
210 					/* ignore blank lines */
211 				}
212 				else if (sscanf(buf, "%s %d",
213 						version, &code) != 2) {
214 					/* can't get result code */
215 					fpurge(fp);
216 					return (-1);
217 				}
218 			}
219 			else {
220 				/* entity header expected */
221 				char *field, *cp;
222 
223 				if (buf[0] == '\n') {
224 					/* end of header */
225 					end_of_header = 1;
226 					buflen = BODY_MAXSIZE;
227 					if (resid == 0)
228 						/* no message body */
229 						return (code);
230 				}
231 
232 				cp = buf;
233 				field = strsep(&cp, ":");
234 				if (strcmp(field, "Content-Length") == 0) {
235 					if (sscanf(cp, "%d", &resid) != 1) {
236 						fpurge(fp);
237 						return (-1);
238 					}
239 					if (blen != NULL)
240 						*blen = resid;
241 				}
242 			}
243 		}
244 		else {
245 			/* process message body */
246 			if (body != NULL) {
247 				len = strlcpy(body, buf, buflen);
248 				if (len >= buflen) {
249 					/* body too long */
250 					fpurge(fp);
251 					return (-1);
252 				}
253 				body += len;
254 				buflen -= len;
255 			}
256 			else
257 				len = strlen(buf);
258 			resid -= len;
259 			if (resid <= 0)
260 				return (code);
261 		}
262 	}
263 	return (-1);
264 }
265 
266 void
267 quip_rawmode(void)
268 {
269 	char line[MAXLINESIZE];
270 	int result_code;
271 
272 	printf(">>>Entering the raw interactive mode to the server:\n\n");
273 	if (server == NULL) {
274 		printf("No server available!\n");
275 		return;
276 	}
277 
278 	while (1) {
279 		printf("%% "); fflush(stdout);
280 		/* read a line from stdin */
281 		if (fgets(line, sizeof(line), stdin) == NULL)
282 			break;
283 
284 		if (line[0] == '\n') {
285 			/* if a blank line, echo locally */
286 			fputs(line, stdout);
287 			continue;
288 		}
289 		if (line[0] == 'q') {
290 			printf("Exit\n");
291 			break;
292 		}
293 
294 		/* send the input line to the server */
295 		quip_sendrequest(server, line);
296 
297 		/* get a response message from the server */
298 		result_code = quip_recvresponse(server, NULL, NULL, NULL);
299 	}
300 }
301 
302 char *
303 quip_selectinterface(char *ifname)
304 {
305 	char buf[BODY_MAXSIZE], *cp;
306 	int result_code, len;
307 	u_int if_index;
308 	static char interface[64];
309 
310 	if (server == NULL)
311 		return (ifname);
312 
313 	/* get an inferface list from the server */
314 	quip_sendrequest(server, "GET list?*\n");
315 
316 	result_code = quip_recvresponse(server, NULL, buf, &len);
317 	if (result_code != 200)
318 		errx(1, "can't get interface list");
319 
320 	cp = buf;
321 	while (1) {
322 		if (sscanf(cp, "%x %s", &if_index, interface) != 2)
323 			break;
324 		if (ifname == NULL) {
325 			/* if name isn't specified, return the 1st entry */
326 			return (interface);
327 		}
328 		if (strcmp(ifname, interface) == 0)
329 			/* found the matching entry */
330 			return (interface);
331 		if ((cp = strchr(cp+1, '\n')) == NULL)
332 			break;
333 	}
334 	errx(1, "can't get interface");
335 	return (NULL);
336 }
337 
338 char *
339 quip_selectqdisc(char *ifname, char *qdisc_name)
340 {
341 	char buf[BODY_MAXSIZE], req[REQ_MAXSIZE];
342 	int result_code, len;
343 	static char qdisc[64];
344 
345 	if (server == NULL) {
346 		if (ifname == NULL || qdisc_name == NULL)
347 			errx(1, "when disabling server communication,\n"
348 			    "specify both interface (-i) and qdisc (-q)!");
349 		return (qdisc_name);
350 	}
351 
352 	/* get qdisc info from the server */
353 	snprintf(req, sizeof(req), "GET qdisc?%s\n", ifname);
354 	quip_sendrequest(server, req);
355 
356 	result_code = quip_recvresponse(server, NULL, buf, &len);
357 	if (result_code != 200)
358 		errx(1, "can't get qdisc info");
359 
360 	if (sscanf(buf, "%s", qdisc) != 1)
361 		errx(1, "can't get qdisc name");
362 
363 	if (qdisc_name != NULL && strcmp(qdisc, qdisc_name) != 0)
364 		errx(1, "qdisc %s on %s doesn't match specified qdisc %s",
365 		    qdisc, ifname, qdisc_name);
366 
367 	return (qdisc);
368 }
369 
370 void
371 quip_chandle2name(const char *ifname, u_long handle, char *name, size_t size)
372 {
373 	char buf[BODY_MAXSIZE], req[REQ_MAXSIZE], *cp;
374 	int result_code, len;
375 
376 	name[0] = '\0';
377 	if (server == NULL)
378 		return;
379 
380 	/* get class name from the server */
381 	snprintf(req, sizeof(req), "GET handle-to-name?%s:%#lx\n", ifname, handle);
382 	quip_sendrequest(server, req);
383 
384 	result_code = quip_recvresponse(server, NULL, buf, &len);
385 	if (result_code != 200)
386 		errx(1, "can't get class name");
387 
388 	if ((cp = strchr(buf, '\n')) != NULL)
389 		*cp = '\0';
390 	if ((cp = strrchr(buf, '/')) != NULL)
391 		strlcpy(name, cp+1, size);
392 }
393 
394 void
395 quip_printqdisc(const char *ifname)
396 {
397 	char buf[BODY_MAXSIZE], req[REQ_MAXSIZE], *cp;
398 	int result_code, len;
399 
400 	if (server == NULL) {
401 		printf("No server available!\n");
402 		return;
403 	}
404 
405 	/* get qdisc info from the server */
406 	snprintf(req, sizeof(req), "GET qdisc?%s\n", ifname);
407 	quip_sendrequest(server, req);
408 
409 	result_code = quip_recvresponse(server, NULL, buf, &len);
410 	if (result_code != 200)
411 		errx(1, "can't get qdisc info");
412 
413 	/* replace newline by space */
414 	cp = buf;
415 	while ((cp = strchr(cp, '\n')) != NULL)
416 		*cp = ' ';
417 
418 	printf("  qdisc:%s\n", buf);
419 }
420 
421 void
422 quip_printfilter(const char *ifname, const u_long handle)
423 {
424 	char buf[BODY_MAXSIZE], req[REQ_MAXSIZE], *cp;
425 	int result_code, len;
426 
427 	/* get qdisc info from the server */
428 	snprintf(req, sizeof(req), "GET filter?%s::%#lx\n", ifname, handle);
429 	quip_sendrequest(server, req);
430 
431 	result_code = quip_recvresponse(server, NULL, buf, &len);
432 	if (result_code != 200)
433 		errx(1, "can't get filter info");
434 
435 	if ((cp = strchr(buf, '\n')) != NULL)
436 		*cp = '\0';
437 	printf("%s", buf);
438 }
439 
440 static char *
441 extract_ifname(const char *name)
442 {
443 	char *cp;
444 	int len;
445 	static char ifname[64];
446 
447 	if ((cp = strchr(name, ':')) != NULL)
448 		len = cp - name;
449 	else
450 		len = strlen(name);
451 	len = MIN(len, 63);
452 	strncpy(ifname, name, len);
453 	ifname[len] = '\0';
454 	return (ifname);
455 }
456 
457 void
458 quip_printconfig(void)
459 {
460 	char buf[BODY_MAXSIZE], name[256], *cp, *p, *flname;
461 	int result_code, len;
462 	enum nametype type;
463 	u_long handle;
464 
465 	/* get a total list from the server */
466 	quip_sendrequest(server, "GET list\n");
467 
468 	result_code = quip_recvresponse(server, NULL, buf, &len);
469 	if (result_code != 200)
470 		errx(1, "can't get total list");
471 
472 	printf("------------ current configuration ------------");
473 
474 	cp = buf;
475 	while (1) {
476 		if (sscanf(cp, "%lx %s", &handle, name) != 2)
477 			break;
478 
479 		if ((p = strchr(name, ':')) == NULL)
480 			type = INTERFACE;
481 		else if (strchr(p+1, ':') == NULL)
482 			type = CLASS;
483 		else
484 			type = FILTER;
485 
486 		switch (type) {
487 		case INTERFACE:
488 			printf("\ninterface: %s  (index:%lu)\n",
489 			       name, handle);
490 			quip_printqdisc(name);
491 			break;
492 		case CLASS:
493 			printf("class: %s  (handle:%#lx)\n",
494 			       name, handle);
495 			break;
496 		case FILTER:
497 			flname = strrchr(name, ':') + 1;
498 			printf("  filter: name:%s [", flname);
499 			quip_printfilter(extract_ifname(name), handle);
500 			printf("]  (handle:%#lx)\n", handle);
501 			break;
502 		case CONDITIONER:
503 			break;
504 		}
505 
506 		if ((cp = strchr(cp+1, '\n')) == NULL)
507 			break;
508 	}
509 	printf("-----------------------------------------------\n\n");
510 }
511 
512