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