xref: /netbsd-src/usr.sbin/altq/altqstat/quip_client.c (revision 9fbd88883c38d0c0fbfcbe66d76fe6b0fab3f9de)
1 /*	$NetBSD: quip_client.c,v 1.5 2001/12/27 13:22:01 wiz Exp $	*/
2 /*	$KAME: quip_client.c,v 1.5 2001/08/20 06:41:33 kjc 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 <signal.h>
38 #include <errno.h>
39 #include <err.h>
40 
41 #include "quip_client.h"
42 #include "altqstat.h"
43 
44 /*
45  * quip (queue information protocol) is a http-like protocol
46  * in order to retrieve information from the server.
47  * a unix domain TCP socket "/var/run/altq_quip" is used for
48  * client-server style communication.
49  *
50  * there are 2 quip message types: request and response.
51  * request format: (only single-line request message is used at this moment)
52  *	request-line
53  *
54  *      request-line = <method> <operation>[?<query>] <quip-version>
55  *	<method> = GET (only GET is defined at this moment)
56  *	<operation> = list | handle-to-name | qdisc | filter
57  *	query format is operation dependent but most query takes
58  *	<interface> or <class> or <filter>.
59  *	<interface> = <if_name>
60  *	<class>     = <if_name>:<class_path>/<class_name>
61  *	<filter>    = <if_name>:<class_path>/<class_name>:<filter_name>
62  *	"list" operation accepts "*" as a wildcard.
63  *
64  * response format:
65  *	status-line
66  * 	response-headers (0 or more)
67  *	<blank line>
68  *	body
69  *
70  *	status-line = <quip-version> <status-code> <reason phrase>
71  *	response-header = Content-Length:<value>
72  *
73  *	"Content-Length" specifies the length of the message body.
74  *
75  * example:
76  *	to retrieve a list of classes (handle and name) on interface "fxp0":
77  *	a request message looks like,
78  *		GET list?fxp0:* QUIP/1.0<cr>
79  *	a response message looks like,
80  *		QUIP/1.0 200 OK<cr>
81  *		Content-Length:86<cr>
82  *		<cr>
83  *		0000000000	fxp0:/root<cr>
84  *		0xc0d1be00	fxp0:/root/parent<cr>
85  *		0xc0d1ba00	fxp0:/root/parent/child<cr>
86  *
87  *	other examples:
88  *	list all interfaces, classes, and filters:
89  *		GET list QUIP/1.0<cr>
90  *	list all interfaces:
91  *		GET list?* QUIP/1.0<cr>
92  *	list all classes:
93  *		GET list?*:* QUIP/1.0<cr>
94  *	list all filters:
95  *		GET list?*:*:* QUIP/1.0<cr>
96  *	convert class handle to class name:
97  *		GET handle-to-name?fxp0:0xc0d1be00 QUIP/1.0<cr>
98  *	convert filter handle to filter name:
99  *		GET handle-to-name?fxp0::0x1000000a QUIP/1.0<cr>
100  */
101 
102 #define	MAXLINESIZE	1024
103 
104 enum nametype { INTERFACE, CLASS, FILTER, CONDITIONER };
105 
106 static FILE *server = NULL;
107 int quip_echo = 0;
108 
109 static char *extract_ifname(const char *);
110 
111 int
112 quip_openserver(void)
113 {
114 	struct sockaddr_un addr;
115 	int fd;
116 
117 	if ((fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
118 		err(1, "can't open socket");
119 
120 	bzero(&addr, sizeof(addr));
121 	addr.sun_family = AF_LOCAL;
122 	strlcpy(addr.sun_path, QUIP_PATH,sizeof(addr.sun_path));
123 
124 	if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
125 		fprintf(stderr, "can't talk to altqd!\n"
126 			"probably, altqd is not running\n");
127 		return (-1);
128 	}
129 
130 	if ((server = fdopen(fd, "r+")) == NULL) {
131 		warn("fdopen: can't open stream to the quip server");
132 		return (-1);
133 	}
134 	return (0);
135 }
136 
137 int
138 quip_closeserver(void)
139 {
140 	if (server != NULL)
141 		return fclose(server);
142 	return (0);
143 }
144 
145 void
146 quip_sendrequest(FILE *fp, const char *request)
147 {
148 	char buf[QUIPMSG_MAXSIZE], *cp;
149 	int n;
150 
151 	if ((cp = strstr(request, "QUIP")) == NULL) {
152 		cp = strchr(request, '\n');
153 		n = cp - request;
154 		if (cp == NULL || n > REQ_MAXSIZE - 10)
155 			return;
156 		strncpy(buf, request, n);
157 		snprintf(buf + n, REQ_MAXSIZE - n, " QUIP/1.0");
158 		strlcat(buf, cp, REQ_MAXSIZE);
159 	}
160 	else
161 		strlcpy(buf, request, REQ_MAXSIZE);
162 
163 	if (fputs(buf, fp) != 0)
164 		err(1, "fputs");
165 	if (fflush(fp) != 0)
166 		err(1, "fflush");
167 	if (quip_echo) {
168 		fputs("<< ", stdout);
169 		fputs(buf, stdout);
170 	}
171 }
172 
173 /*
174  * recv_response receives a response message from the server
175  * and returns status_code.
176  */
177 int
178 quip_recvresponse(FILE *fp, char *header, char *body, int *blen)
179 {
180 	char buf[MAXLINESIZE], version[MAXLINESIZE];
181 	int code, resid, len, buflen;
182 	int end_of_header = 0;
183 
184 	if (blen != NULL)
185 		*blen = 0;
186 	code = 0;
187 	resid = 0;
188 	buflen = RES_MAXSIZE;
189 	while (fgets(buf, sizeof(buf), fp) != 0) {
190 		if (quip_echo) {
191 			fputs(">  ", stdout);
192 			fputs(buf, stdout);
193 		}
194 
195 		if (!end_of_header) {
196 			/* process message header */
197 			if (header != NULL) {
198 				len = strlcpy(header, buf, buflen);
199 				if (len >= buflen) {
200 					/* header too long */
201 					fpurge(fp);
202 					return (-1);
203 				}
204 				header += len;
205 				buflen -= len;
206 			}
207 
208 			if (code == 0) {
209 				/* status line expected */
210 				if (buf[0] == '\n') {
211 					/* ignore blank lines */
212 				}
213 				else if (sscanf(buf, "%s %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 %s", &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, "%s", 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 %s", &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