xref: /f-stack/tools/ngctl/dot.c (revision 22ce4aff)
13b2bd0f6Slogwang 
23b2bd0f6Slogwang /*
33b2bd0f6Slogwang  * dot.c
43b2bd0f6Slogwang  *
5*22ce4affSfengbojiang  * Copyright (c) 2019 Lutz Donnerhacke
63b2bd0f6Slogwang  * Copyright (c) 2004 Brian Fundakowski Feldman
73b2bd0f6Slogwang  * Copyright (c) 1996-1999 Whistle Communications, Inc.
83b2bd0f6Slogwang  * All rights reserved.
93b2bd0f6Slogwang  *
103b2bd0f6Slogwang  * Subject to the following obligations and disclaimer of warranty, use and
113b2bd0f6Slogwang  * redistribution of this software, in source or object code forms, with or
123b2bd0f6Slogwang  * without modifications are expressly permitted by Whistle Communications;
133b2bd0f6Slogwang  * provided, however, that:
143b2bd0f6Slogwang  * 1. Any and all reproductions of the source or object code must include the
153b2bd0f6Slogwang  *    copyright notice above and the following disclaimer of warranties; and
163b2bd0f6Slogwang  * 2. No rights are granted, in any manner or form, to use Whistle
173b2bd0f6Slogwang  *    Communications, Inc. trademarks, including the mark "WHISTLE
183b2bd0f6Slogwang  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
193b2bd0f6Slogwang  *    such appears in the above copyright notice or in the software.
203b2bd0f6Slogwang  *
213b2bd0f6Slogwang  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
223b2bd0f6Slogwang  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
233b2bd0f6Slogwang  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
243b2bd0f6Slogwang  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
253b2bd0f6Slogwang  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
263b2bd0f6Slogwang  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
273b2bd0f6Slogwang  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
283b2bd0f6Slogwang  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
293b2bd0f6Slogwang  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
303b2bd0f6Slogwang  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
313b2bd0f6Slogwang  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
323b2bd0f6Slogwang  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
333b2bd0f6Slogwang  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
343b2bd0f6Slogwang  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
353b2bd0f6Slogwang  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
363b2bd0f6Slogwang  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
373b2bd0f6Slogwang  * OF SUCH DAMAGE.
383b2bd0f6Slogwang  *
393b2bd0f6Slogwang  * $FreeBSD$
403b2bd0f6Slogwang  */
413b2bd0f6Slogwang 
423b2bd0f6Slogwang #include <err.h>
433b2bd0f6Slogwang #include <inttypes.h>
443b2bd0f6Slogwang #include <netgraph.h>
453b2bd0f6Slogwang #include <stdio.h>
463b2bd0f6Slogwang #include <stdlib.h>
473b2bd0f6Slogwang #include <unistd.h>
483b2bd0f6Slogwang 
493b2bd0f6Slogwang #include "ngctl.h"
503b2bd0f6Slogwang 
513b2bd0f6Slogwang #define UNNAMED		"\\<unnamed\\>"
523b2bd0f6Slogwang 
533b2bd0f6Slogwang static int DotCmd(int ac, char **av);
543b2bd0f6Slogwang 
553b2bd0f6Slogwang const struct ngcmd dot_cmd = {
563b2bd0f6Slogwang 	DotCmd,
57*22ce4affSfengbojiang 	"dot [-c] [outputfile]",
583b2bd0f6Slogwang 	"Produce a GraphViz (.dot) of the entire netgraph.",
59*22ce4affSfengbojiang 	"If no outputfile is specified, stdout will be assumed."
60*22ce4affSfengbojiang 	" The optional -c argument generates a graph without separate"
61*22ce4affSfengbojiang 	" structures for edge names. Such a graph is more compact.",
623b2bd0f6Slogwang 	{ "graphviz", "confdot" }
633b2bd0f6Slogwang };
643b2bd0f6Slogwang 
653b2bd0f6Slogwang static int
DotCmd(int ac,char ** av)663b2bd0f6Slogwang DotCmd(int ac, char **av)
673b2bd0f6Slogwang {
683b2bd0f6Slogwang 	struct ng_mesg *nlresp;
693b2bd0f6Slogwang 	struct namelist *nlist;
703b2bd0f6Slogwang 	FILE *f = stdout;
713b2bd0f6Slogwang 	int ch;
72*22ce4affSfengbojiang 	int compact = 0;
733b2bd0f6Slogwang 	u_int i;
743b2bd0f6Slogwang 
753b2bd0f6Slogwang 	/* Get options */
763b2bd0f6Slogwang 	optind = 1;
77*22ce4affSfengbojiang 	while ((ch = getopt(ac, av, "c")) != -1) {
783b2bd0f6Slogwang 		switch (ch) {
79*22ce4affSfengbojiang 		case 'c':
80*22ce4affSfengbojiang 			compact = 1;
81*22ce4affSfengbojiang 			break;
823b2bd0f6Slogwang 		case '?':
833b2bd0f6Slogwang 		default:
843b2bd0f6Slogwang 			return (CMDRTN_USAGE);
853b2bd0f6Slogwang 			break;
863b2bd0f6Slogwang 		}
873b2bd0f6Slogwang 	}
883b2bd0f6Slogwang 	ac -= optind;
893b2bd0f6Slogwang 	av += optind;
903b2bd0f6Slogwang 
913b2bd0f6Slogwang 	/* Get arguments */
923b2bd0f6Slogwang 	switch (ac) {
933b2bd0f6Slogwang 	case 1:
943b2bd0f6Slogwang 		f = fopen(av[0], "w");
953b2bd0f6Slogwang 		if (f == NULL) {
963b2bd0f6Slogwang 			warn("Could not open %s for writing", av[0]);
973b2bd0f6Slogwang 			return (CMDRTN_ERROR);
983b2bd0f6Slogwang 		}
993b2bd0f6Slogwang 	case 0:
1003b2bd0f6Slogwang 		break;
1013b2bd0f6Slogwang 	default:
1023b2bd0f6Slogwang 		if (f != stdout)
1033b2bd0f6Slogwang 			(void)fclose(f);
1043b2bd0f6Slogwang 		return (CMDRTN_USAGE);
1053b2bd0f6Slogwang 	}
1063b2bd0f6Slogwang 
1073b2bd0f6Slogwang 	/* Get list of nodes */
1083b2bd0f6Slogwang 	if (NgSendMsg(csock, ".", NGM_GENERIC_COOKIE, NGM_LISTNODES, NULL,
1093b2bd0f6Slogwang 	    0) < 0) {
1103b2bd0f6Slogwang 		warn("send listnodes msg");
1113b2bd0f6Slogwang 		goto error;
1123b2bd0f6Slogwang 	}
1133b2bd0f6Slogwang 	if (NgAllocRecvMsg(csock, &nlresp, NULL) < 0) {
1143b2bd0f6Slogwang 		warn("recv listnodes msg");
1153b2bd0f6Slogwang 		goto error;
1163b2bd0f6Slogwang 	}
1173b2bd0f6Slogwang 
1183b2bd0f6Slogwang 	nlist = (struct namelist *)nlresp->data;
119*22ce4affSfengbojiang 	if (compact) {
120*22ce4affSfengbojiang 		fprintf(f, "digraph netgraph {\n");
121*22ce4affSfengbojiang 		fprintf(f, "\tedge [ dir = \"none\", fontsize = 10 ];\n");
122*22ce4affSfengbojiang 	} else {
1233b2bd0f6Slogwang 		fprintf(f, "graph netgraph {\n");
1243b2bd0f6Slogwang 		/* TODO: implement rank = same or subgraphs at some point */
1253b2bd0f6Slogwang 		fprintf(f, "\tedge [ weight = 1.0 ];\n");
126*22ce4affSfengbojiang 	}
1273b2bd0f6Slogwang 	fprintf(f, "\tnode [ shape = record, fontsize = 12 ] {\n");
1283b2bd0f6Slogwang 	for (i = 0; i < nlist->numnames; i++)
1293b2bd0f6Slogwang 		fprintf(f, "\t\t\"%jx\" [ label = \"{%s:|{%s|[%jx]:}}\" ];\n",
1303b2bd0f6Slogwang 		    (uintmax_t)nlist->nodeinfo[i].id,
1313b2bd0f6Slogwang 		    nlist->nodeinfo[i].name[0] != '\0' ?
1323b2bd0f6Slogwang 		    nlist->nodeinfo[i].name : UNNAMED,
1333b2bd0f6Slogwang 		    nlist->nodeinfo[i].type, (uintmax_t)nlist->nodeinfo[i].id);
1343b2bd0f6Slogwang 	fprintf(f, "\t};\n");
1353b2bd0f6Slogwang 
1363b2bd0f6Slogwang 	fprintf(f, "\tsubgraph cluster_disconnected {\n");
1373b2bd0f6Slogwang 	fprintf(f, "\t\tbgcolor = pink;\n");
1383b2bd0f6Slogwang 	for (i = 0; i < nlist->numnames; i++)
1393b2bd0f6Slogwang 		if (nlist->nodeinfo[i].hooks == 0)
1403b2bd0f6Slogwang 			fprintf(f, "\t\t\"%jx\";\n",
1413b2bd0f6Slogwang 			    (uintmax_t)nlist->nodeinfo[i].id);
1423b2bd0f6Slogwang 	fprintf(f, "\t};\n");
1433b2bd0f6Slogwang 
1443b2bd0f6Slogwang 	for (i = 0; i < nlist->numnames; i++) {
1453b2bd0f6Slogwang 		struct ng_mesg *hlresp;
1463b2bd0f6Slogwang 		struct hooklist *hlist;
1473b2bd0f6Slogwang 		struct nodeinfo *ninfo;
1483b2bd0f6Slogwang 		char path[NG_PATHSIZ];
1493b2bd0f6Slogwang 		u_int j;
1503b2bd0f6Slogwang 
1513b2bd0f6Slogwang 		(void)snprintf(path, sizeof(path), "[%jx]:",
1523b2bd0f6Slogwang 		    (uintmax_t)nlist->nodeinfo[i].id);
1533b2bd0f6Slogwang 
1543b2bd0f6Slogwang 		/* Get node info and hook list */
1553b2bd0f6Slogwang 		if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
1563b2bd0f6Slogwang 		    NULL, 0) < 0) {
1573b2bd0f6Slogwang 			free(nlresp);
1583b2bd0f6Slogwang 			warn("send listhooks msg");
1593b2bd0f6Slogwang 			goto error;
1603b2bd0f6Slogwang 		}
1613b2bd0f6Slogwang 		if (NgAllocRecvMsg(csock, &hlresp, NULL) < 0) {
1623b2bd0f6Slogwang 			free(nlresp);
1633b2bd0f6Slogwang 			warn("recv listhooks msg");
1643b2bd0f6Slogwang 			goto error;
1653b2bd0f6Slogwang 		}
1663b2bd0f6Slogwang 
1673b2bd0f6Slogwang 		hlist = (struct hooklist *)hlresp->data;
1683b2bd0f6Slogwang 		ninfo = &hlist->nodeinfo;
1693b2bd0f6Slogwang 		if (ninfo->hooks == 0) {
1703b2bd0f6Slogwang 			free(hlresp);
1713b2bd0f6Slogwang 			continue;
1723b2bd0f6Slogwang 		}
1733b2bd0f6Slogwang 
174*22ce4affSfengbojiang 		if (!compact) {
1753b2bd0f6Slogwang 			fprintf(f, "\tnode [ shape = octagon, fontsize = 10 ] {\n");
1763b2bd0f6Slogwang 			for (j = 0; j < ninfo->hooks; j++)
1773b2bd0f6Slogwang 				fprintf(f, "\t\t\"%jx.%s\" [ label = \"%s\" ];\n",
1783b2bd0f6Slogwang 				    (uintmax_t)nlist->nodeinfo[i].id,
1793b2bd0f6Slogwang 				    hlist->link[j].ourhook, hlist->link[j].ourhook);
1803b2bd0f6Slogwang 			fprintf(f, "\t};\n");
1813b2bd0f6Slogwang 
1823b2bd0f6Slogwang 			fprintf(f, "\t{\n\t\tedge [ weight = 2.0, style = bold ];\n");
1833b2bd0f6Slogwang 			for (j = 0; j < ninfo->hooks; j++)
1843b2bd0f6Slogwang 				fprintf(f, "\t\t\"%jx\" -- \"%jx.%s\";\n",
1853b2bd0f6Slogwang 				    (uintmax_t)nlist->nodeinfo[i].id,
1863b2bd0f6Slogwang 				    (uintmax_t)nlist->nodeinfo[i].id,
1873b2bd0f6Slogwang 				    hlist->link[j].ourhook);
1883b2bd0f6Slogwang 			fprintf(f, "\t};\n");
189*22ce4affSfengbojiang 		}
1903b2bd0f6Slogwang 
1913b2bd0f6Slogwang 		for (j = 0; j < ninfo->hooks; j++) {
1923b2bd0f6Slogwang 			/* Only print the edges going in one direction. */
1933b2bd0f6Slogwang 			if (hlist->link[j].nodeinfo.id > nlist->nodeinfo[i].id)
1943b2bd0f6Slogwang 				continue;
195*22ce4affSfengbojiang 			if (compact) {
196*22ce4affSfengbojiang 				fprintf(f, "\t\"%jx\" -> \"%jx\" [ headlabel = \"%s\", taillabel = \"%s\" ] ;\n",
197*22ce4affSfengbojiang 				    (uintmax_t)hlist->link[j].nodeinfo.id,
198*22ce4affSfengbojiang 				    (uintmax_t)nlist->nodeinfo[i].id,
199*22ce4affSfengbojiang 				    hlist->link[j].ourhook,
200*22ce4affSfengbojiang 				    hlist->link[j].peerhook);
201*22ce4affSfengbojiang 			} else {
2023b2bd0f6Slogwang 				fprintf(f, "\t\"%jx.%s\" -- \"%jx.%s\";\n",
2033b2bd0f6Slogwang 				    (uintmax_t)nlist->nodeinfo[i].id,
2043b2bd0f6Slogwang 				    hlist->link[j].ourhook,
2053b2bd0f6Slogwang 				    (uintmax_t)hlist->link[j].nodeinfo.id,
2063b2bd0f6Slogwang 				    hlist->link[j].peerhook);
2073b2bd0f6Slogwang 			}
208*22ce4affSfengbojiang 		}
2093b2bd0f6Slogwang 		free(hlresp);
2103b2bd0f6Slogwang 	}
2113b2bd0f6Slogwang 
2123b2bd0f6Slogwang 	fprintf(f, "}\n");
2133b2bd0f6Slogwang 
2143b2bd0f6Slogwang 	free(nlresp);
2153b2bd0f6Slogwang 	if (f != stdout)
2163b2bd0f6Slogwang 		(void)fclose(f);
2173b2bd0f6Slogwang 	return (CMDRTN_OK);
2183b2bd0f6Slogwang error:
2193b2bd0f6Slogwang 	if (f != stdout)
2203b2bd0f6Slogwang 		(void)fclose(f);
2213b2bd0f6Slogwang 	return (CMDRTN_ERROR);
2223b2bd0f6Slogwang }
223