xref: /netbsd-src/usr.sbin/altq/altqstat/quip_client.c (revision d710132b4b8ce7f7cccaaf660cb16aa16b4077a0)
1 /*	$NetBSD: quip_client.c,v 1.7 2003/05/17 14:54:53 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 				/* sizeof(version) == 1024 */
213 				else if (sscanf(buf, "%1023s %d",
214 						version, &code) != 2) {
215 					/* can't get result code */
216 					fpurge(fp);
217 					return (-1);
218 				}
219 			}
220 			else {
221 				/* entity header expected */
222 				char *field, *cp;
223 
224 				if (buf[0] == '\n') {
225 					/* end of header */
226 					end_of_header = 1;
227 					buflen = BODY_MAXSIZE;
228 					if (resid == 0)
229 						/* no message body */
230 						return (code);
231 				}
232 
233 				cp = buf;
234 				field = strsep(&cp, ":");
235 				if (strcmp(field, "Content-Length") == 0) {
236 					if (sscanf(cp, "%d", &resid) != 1) {
237 						fpurge(fp);
238 						return (-1);
239 					}
240 					if (blen != NULL)
241 						*blen = resid;
242 				}
243 			}
244 		}
245 		else {
246 			/* process message body */
247 			if (body != NULL) {
248 				len = strlcpy(body, buf, buflen);
249 				if (len >= buflen) {
250 					/* body too long */
251 					fpurge(fp);
252 					return (-1);
253 				}
254 				body += len;
255 				buflen -= len;
256 			}
257 			else
258 				len = strlen(buf);
259 			resid -= len;
260 			if (resid <= 0)
261 				return (code);
262 		}
263 	}
264 	return (-1);
265 }
266 
267 void
268 quip_rawmode(void)
269 {
270 	char line[MAXLINESIZE];
271 	int result_code;
272 
273 	printf(">>>Entering the raw interactive mode to the server:\n\n");
274 	if (server == NULL) {
275 		printf("No server available!\n");
276 		return;
277 	}
278 
279 	while (1) {
280 		printf("%% "); fflush(stdout);
281 		/* read a line from stdin */
282 		if (fgets(line, sizeof(line), stdin) == NULL)
283 			break;
284 
285 		if (line[0] == '\n') {
286 			/* if a blank line, echo locally */
287 			fputs(line, stdout);
288 			continue;
289 		}
290 		if (line[0] == 'q') {
291 			printf("Exit\n");
292 			break;
293 		}
294 
295 		/* send the input line to the server */
296 		quip_sendrequest(server, line);
297 
298 		/* get a response message from the server */
299 		result_code = quip_recvresponse(server, NULL, NULL, NULL);
300 	}
301 }
302 
303 char *
304 quip_selectinterface(char *ifname)
305 {
306 	char buf[BODY_MAXSIZE], *cp;
307 	int result_code, len;
308 	u_int if_index;
309 	static char interface[64];
310 
311 	if (server == NULL)
312 		return (ifname);
313 
314 	/* get an inferface list from the server */
315 	quip_sendrequest(server, "GET list?*\n");
316 
317 	result_code = quip_recvresponse(server, NULL, buf, &len);
318 	if (result_code != 200)
319 		errx(1, "can't get interface list");
320 
321 	cp = buf;
322 	while (1) {
323 		if (sscanf(cp, "%x %63s", &if_index, interface) != 2)
324 			break;
325 		if (ifname == NULL) {
326 			/* if name isn't specified, return the 1st entry */
327 			return (interface);
328 		}
329 		if (strcmp(ifname, interface) == 0)
330 			/* found the matching entry */
331 			return (interface);
332 		if ((cp = strchr(cp+1, '\n')) == NULL)
333 			break;
334 	}
335 	errx(1, "can't get interface");
336 	return (NULL);
337 }
338 
339 char *
340 quip_selectqdisc(char *ifname, char *qdisc_name)
341 {
342 	char buf[BODY_MAXSIZE], req[REQ_MAXSIZE];
343 	int result_code, len;
344 	static char qdisc[64];
345 
346 	if (server == NULL) {
347 		if (ifname == NULL || qdisc_name == NULL)
348 			errx(1, "when disabling server communication,\n"
349 			    "specify both interface (-i) and qdisc (-q)!");
350 		return (qdisc_name);
351 	}
352 
353 	/* get qdisc info from the server */
354 	snprintf(req, sizeof(req), "GET qdisc?%s\n", ifname);
355 	quip_sendrequest(server, req);
356 
357 	result_code = quip_recvresponse(server, NULL, buf, &len);
358 	if (result_code != 200)
359 		errx(1, "can't get qdisc info");
360 
361 	if (sscanf(buf, "%63s", qdisc) != 1)
362 		errx(1, "can't get qdisc name");
363 
364 	if (qdisc_name != NULL && strcmp(qdisc, qdisc_name) != 0)
365 		errx(1, "qdisc %s on %s doesn't match specified qdisc %s",
366 		    qdisc, ifname, qdisc_name);
367 
368 	return (qdisc);
369 }
370 
371 void
372 quip_chandle2name(const char *ifname, u_long handle, char *name, size_t size)
373 {
374 	char buf[BODY_MAXSIZE], req[REQ_MAXSIZE], *cp;
375 	int result_code, len;
376 
377 	name[0] = '\0';
378 	if (server == NULL)
379 		return;
380 
381 	/* get class name from the server */
382 	snprintf(req, sizeof(req), "GET handle-to-name?%s:%#lx\n", ifname, handle);
383 	quip_sendrequest(server, req);
384 
385 	result_code = quip_recvresponse(server, NULL, buf, &len);
386 	if (result_code != 200)
387 		errx(1, "can't get class name");
388 
389 	if ((cp = strchr(buf, '\n')) != NULL)
390 		*cp = '\0';
391 	if ((cp = strrchr(buf, '/')) != NULL)
392 		strlcpy(name, cp+1, size);
393 }
394 
395 void
396 quip_printqdisc(const char *ifname)
397 {
398 	char buf[BODY_MAXSIZE], req[REQ_MAXSIZE], *cp;
399 	int result_code, len;
400 
401 	if (server == NULL) {
402 		printf("No server available!\n");
403 		return;
404 	}
405 
406 	/* get qdisc info from the server */
407 	snprintf(req, sizeof(req), "GET qdisc?%s\n", ifname);
408 	quip_sendrequest(server, req);
409 
410 	result_code = quip_recvresponse(server, NULL, buf, &len);
411 	if (result_code != 200)
412 		errx(1, "can't get qdisc info");
413 
414 	/* replace newline by space */
415 	cp = buf;
416 	while ((cp = strchr(cp, '\n')) != NULL)
417 		*cp = ' ';
418 
419 	printf("  qdisc:%s\n", buf);
420 }
421 
422 void
423 quip_printfilter(const char *ifname, const u_long handle)
424 {
425 	char buf[BODY_MAXSIZE], req[REQ_MAXSIZE], *cp;
426 	int result_code, len;
427 
428 	/* get qdisc info from the server */
429 	snprintf(req, sizeof(req), "GET filter?%s::%#lx\n", ifname, handle);
430 	quip_sendrequest(server, req);
431 
432 	result_code = quip_recvresponse(server, NULL, buf, &len);
433 	if (result_code != 200)
434 		errx(1, "can't get filter info");
435 
436 	if ((cp = strchr(buf, '\n')) != NULL)
437 		*cp = '\0';
438 	printf("%s", buf);
439 }
440 
441 static char *
442 extract_ifname(const char *name)
443 {
444 	char *cp;
445 	int len;
446 	static char ifname[64];
447 
448 	if ((cp = strchr(name, ':')) != NULL)
449 		len = cp - name;
450 	else
451 		len = strlen(name);
452 	len = MIN(len, 63);
453 	strncpy(ifname, name, len);
454 	ifname[len] = '\0';
455 	return (ifname);
456 }
457 
458 void
459 quip_printconfig(void)
460 {
461 	char buf[BODY_MAXSIZE], name[256], *cp, *p, *flname;
462 	int result_code, len;
463 	enum nametype type;
464 	u_long handle;
465 
466 	/* get a total list from the server */
467 	quip_sendrequest(server, "GET list\n");
468 
469 	result_code = quip_recvresponse(server, NULL, buf, &len);
470 	if (result_code != 200)
471 		errx(1, "can't get total list");
472 
473 	printf("------------ current configuration ------------");
474 
475 	cp = buf;
476 	while (1) {
477 		if (sscanf(cp, "%lx %255s", &handle, name) != 2)
478 			break;
479 
480 		if ((p = strchr(name, ':')) == NULL)
481 			type = INTERFACE;
482 		else if (strchr(p+1, ':') == NULL)
483 			type = CLASS;
484 		else
485 			type = FILTER;
486 
487 		switch (type) {
488 		case INTERFACE:
489 			printf("\ninterface: %s  (index:%lu)\n",
490 			       name, handle);
491 			quip_printqdisc(name);
492 			break;
493 		case CLASS:
494 			printf("class: %s  (handle:%#lx)\n",
495 			       name, handle);
496 			break;
497 		case FILTER:
498 			flname = strrchr(name, ':') + 1;
499 			printf("  filter: name:%s [", flname);
500 			quip_printfilter(extract_ifname(name), handle);
501 			printf("]  (handle:%#lx)\n", handle);
502 			break;
503 		case CONDITIONER:
504 			break;
505 		}
506 
507 		if ((cp = strchr(cp+1, '\n')) == NULL)
508 			break;
509 	}
510 	printf("-----------------------------------------------\n\n");
511 }
512 
513