summaryrefslogtreecommitdiffstats
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/Makefile.am49
-rw-r--r--src/client/Makefile.in1276
-rw-r--r--src/client/README.conf8
-rw-r--r--src/client/client.h148
-rw-r--r--src/client/commands.c855
-rw-r--r--src/client/completion/_lldpcli48
-rwxr-xr-xsrc/client/completion/lldpcli26
-rw-r--r--src/client/conf-dot3.c35
-rw-r--r--src/client/conf-inv.c161
-rw-r--r--src/client/conf-lldp.c762
-rw-r--r--src/client/conf-med.c499
-rw-r--r--src/client/conf-power.c389
-rw-r--r--src/client/conf-system.c602
-rw-r--r--src/client/conf.c44
-rw-r--r--src/client/display.c1039
-rw-r--r--src/client/json_writer.c374
-rw-r--r--src/client/kv_writer.c133
-rw-r--r--src/client/lldpcli.8.in1151
-rw-r--r--src/client/lldpcli.c611
-rw-r--r--src/client/lldpctl.81
-rw-r--r--src/client/misc.c73
-rw-r--r--src/client/show.c364
-rw-r--r--src/client/text_writer.c183
-rw-r--r--src/client/tokenizer.c116
-rw-r--r--src/client/utf8.c90
-rw-r--r--src/client/writer.h57
-rw-r--r--src/client/xml_writer.c158
27 files changed, 9252 insertions, 0 deletions
diff --git a/src/client/Makefile.am b/src/client/Makefile.am
new file mode 100644
index 0000000..23a3225
--- /dev/null
+++ b/src/client/Makefile.am
@@ -0,0 +1,49 @@
+AM_CFLAGS = -I $(top_srcdir)/include $(LLDP_CFLAGS)
+AM_CPPFLAGS = $(LLDP_CPPFLAGS)
+AM_LDFLAGS = $(LLDP_LDFLAGS)
+
+sbin_PROGRAMS = lldpcli
+man_MANS = lldpcli.8
+dist_man_MANS = lldpctl.8
+
+install-exec-local: lldpcli
+ cd $(DESTDIR)$(sbindir) && rm -f lldpctl
+ cd $(DESTDIR)$(sbindir) && $(LN_S) lldpcli lldpctl
+uninstall-local:
+ cd $(DESTDIR)$(sbindir) && rm -f lldpctl
+
+lldpcli_SOURCES = client.h lldpcli.c display.c \
+ conf.c conf-med.c conf-inv.c conf-dot3.c conf-power.c \
+ conf-lldp.c conf-system.c \
+ commands.c show.c \
+ misc.c tokenizer.c \
+ utf8.c \
+ writer.h text_writer.c kv_writer.c json_writer.c
+lldpcli_LDADD = \
+ $(top_builddir)/src/libcommon-daemon-client.la \
+ $(top_builddir)/src/lib/liblldpctl.la \
+ @READLINE_LIBS@
+lldpcli_CFLAGS = $(AM_CFLAGS)
+lldpcli_LDFLAGS = $(AM_LDFLAGS) $(LLDP_BIN_LDFLAGS)
+
+if USE_XML
+lldpcli_SOURCES += xml_writer.c
+lldpcli_CFLAGS += @libxml2_CFLAGS@
+lldpcli_LDADD += @libxml2_LIBS@
+endif
+
+# Completions
+bashcompletiondir = $(datadir)/bash-completion/completions
+dist_bashcompletion_DATA = completion/lldpcli
+zshcompletiondir = $(datadir)/zsh/site-functions
+dist_zshcompletion_DATA = completion/_lldpcli
+
+# Default configuration
+lldpdconfdir = $(sysconfdir)/lldpd.d
+dist_lldpdconf_DATA = README.conf
+
+TEMPLATES = lldpcli.8
+EXTRA_DIST = lldpcli.8.in
+CLEANFILES = $(TEMPLATES)
+lldpcli.8: lldpcli.8.in
+include $(top_srcdir)/edit.am
diff --git a/src/client/Makefile.in b/src/client/Makefile.in
new file mode 100644
index 0000000..c179404
--- /dev/null
+++ b/src/client/Makefile.in
@@ -0,0 +1,1276 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+sbin_PROGRAMS = lldpcli$(EXEEXT)
+@USE_XML_TRUE@am__append_1 = xml_writer.c
+@USE_XML_TRUE@am__append_2 = @libxml2_CFLAGS@
+@USE_XML_TRUE@am__append_3 = @libxml2_LIBS@
+subdir = src/client
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/alignof.m4 \
+ $(top_srcdir)/m4/args.m4 \
+ $(top_srcdir)/m4/ax_build_date_epoch.m4 \
+ $(top_srcdir)/m4/ax_cflags_gcc_option.m4 \
+ $(top_srcdir)/m4/ax_ld_check_flag.m4 \
+ $(top_srcdir)/m4/ax_lib_readline.m4 \
+ $(top_srcdir)/m4/ax_prog_doxygen.m4 \
+ $(top_srcdir)/m4/config_subdirs.m4 \
+ $(top_srcdir)/m4/ld-version-script.m4 \
+ $(top_srcdir)/m4/libcap.m4 $(top_srcdir)/m4/libevent.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/os.m4 \
+ $(top_srcdir)/m4/progname.m4 $(top_srcdir)/m4/seccomp.m4 \
+ $(top_srcdir)/m4/snmp.m4 $(top_srcdir)/m4/stdint.m4 \
+ $(top_srcdir)/m4/systemtap.m4 $(top_srcdir)/m4/xml2.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(dist_bashcompletion_DATA) \
+ $(dist_lldpdconf_DATA) $(dist_zshcompletion_DATA) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)" \
+ "$(DESTDIR)$(bashcompletiondir)" "$(DESTDIR)$(lldpdconfdir)" \
+ "$(DESTDIR)$(zshcompletiondir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am__lldpcli_SOURCES_DIST = client.h lldpcli.c display.c conf.c \
+ conf-med.c conf-inv.c conf-dot3.c conf-power.c conf-lldp.c \
+ conf-system.c commands.c show.c misc.c tokenizer.c utf8.c \
+ writer.h text_writer.c kv_writer.c json_writer.c xml_writer.c
+@USE_XML_TRUE@am__objects_1 = lldpcli-xml_writer.$(OBJEXT)
+am_lldpcli_OBJECTS = lldpcli-lldpcli.$(OBJEXT) \
+ lldpcli-display.$(OBJEXT) lldpcli-conf.$(OBJEXT) \
+ lldpcli-conf-med.$(OBJEXT) lldpcli-conf-inv.$(OBJEXT) \
+ lldpcli-conf-dot3.$(OBJEXT) lldpcli-conf-power.$(OBJEXT) \
+ lldpcli-conf-lldp.$(OBJEXT) lldpcli-conf-system.$(OBJEXT) \
+ lldpcli-commands.$(OBJEXT) lldpcli-show.$(OBJEXT) \
+ lldpcli-misc.$(OBJEXT) lldpcli-tokenizer.$(OBJEXT) \
+ lldpcli-utf8.$(OBJEXT) lldpcli-text_writer.$(OBJEXT) \
+ lldpcli-kv_writer.$(OBJEXT) lldpcli-json_writer.$(OBJEXT) \
+ $(am__objects_1)
+lldpcli_OBJECTS = $(am_lldpcli_OBJECTS)
+am__DEPENDENCIES_1 =
+lldpcli_DEPENDENCIES = $(top_builddir)/src/libcommon-daemon-client.la \
+ $(top_builddir)/src/lib/liblldpctl.la $(am__DEPENDENCIES_1)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+lldpcli_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(lldpcli_CFLAGS) \
+ $(CFLAGS) $(lldpcli_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/lldpcli-commands.Po \
+ ./$(DEPDIR)/lldpcli-conf-dot3.Po \
+ ./$(DEPDIR)/lldpcli-conf-inv.Po \
+ ./$(DEPDIR)/lldpcli-conf-lldp.Po \
+ ./$(DEPDIR)/lldpcli-conf-med.Po \
+ ./$(DEPDIR)/lldpcli-conf-power.Po \
+ ./$(DEPDIR)/lldpcli-conf-system.Po ./$(DEPDIR)/lldpcli-conf.Po \
+ ./$(DEPDIR)/lldpcli-display.Po \
+ ./$(DEPDIR)/lldpcli-json_writer.Po \
+ ./$(DEPDIR)/lldpcli-kv_writer.Po \
+ ./$(DEPDIR)/lldpcli-lldpcli.Po ./$(DEPDIR)/lldpcli-misc.Po \
+ ./$(DEPDIR)/lldpcli-show.Po ./$(DEPDIR)/lldpcli-text_writer.Po \
+ ./$(DEPDIR)/lldpcli-tokenizer.Po ./$(DEPDIR)/lldpcli-utf8.Po \
+ ./$(DEPDIR)/lldpcli-xml_writer.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(lldpcli_SOURCES)
+DIST_SOURCES = $(am__lldpcli_SOURCES_DIST)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(dist_man_MANS) $(man_MANS)
+DATA = $(dist_bashcompletion_DATA) $(dist_lldpdconf_DATA) \
+ $(dist_zshcompletion_DATA)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__DIST_COMMON = $(dist_man_MANS) $(srcdir)/Makefile.in \
+ $(top_srcdir)/depcomp $(top_srcdir)/edit.am
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPARMORDIR = @APPARMORDIR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONFIGURE_ARGS = @CONFIGURE_ARGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DOXYGEN_PAPER_SIZE = @DOXYGEN_PAPER_SIZE@
+DSYMUTIL = @DSYMUTIL@
+DTRACE = @DTRACE@
+DUMPBIN = @DUMPBIN@
+DX_CONFIG = @DX_CONFIG@
+DX_DOCDIR = @DX_DOCDIR@
+DX_DOT = @DX_DOT@
+DX_DOXYGEN = @DX_DOXYGEN@
+DX_DVIPS = @DX_DVIPS@
+DX_EGREP = @DX_EGREP@
+DX_ENV = @DX_ENV@
+DX_FLAG_chi = @DX_FLAG_chi@
+DX_FLAG_chm = @DX_FLAG_chm@
+DX_FLAG_doc = @DX_FLAG_doc@
+DX_FLAG_dot = @DX_FLAG_dot@
+DX_FLAG_html = @DX_FLAG_html@
+DX_FLAG_man = @DX_FLAG_man@
+DX_FLAG_pdf = @DX_FLAG_pdf@
+DX_FLAG_ps = @DX_FLAG_ps@
+DX_FLAG_rtf = @DX_FLAG_rtf@
+DX_FLAG_xml = @DX_FLAG_xml@
+DX_HHC = @DX_HHC@
+DX_LATEX = @DX_LATEX@
+DX_MAKEINDEX = @DX_MAKEINDEX@
+DX_PDFLATEX = @DX_PDFLATEX@
+DX_PERL = @DX_PERL@
+DX_PROJECT = @DX_PROJECT@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+FUZZ_DECODE_ENGINE = @FUZZ_DECODE_ENGINE@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LAUNCHDDAEMONSDIR = @LAUNCHDDAEMONSDIR@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LLDPD_CTL_SOCKET = @LLDPD_CTL_SOCKET@
+LLDPD_PID_FILE = @LLDPD_PID_FILE@
+LLDP_BIN_LDFLAGS = @LLDP_BIN_LDFLAGS@
+LLDP_CFLAGS = @LLDP_CFLAGS@
+LLDP_CPPFLAGS = @LLDP_CPPFLAGS@
+LLDP_LDFLAGS = @LLDP_LDFLAGS@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NETLINK_MAX_RECEIVE_BUFSIZE = @NETLINK_MAX_RECEIVE_BUFSIZE@
+NETLINK_RECEIVE_BUFSIZE = @NETLINK_RECEIVE_BUFSIZE@
+NETLINK_SEND_BUFSIZE = @NETLINK_SEND_BUFSIZE@
+NETSNMP_CFLAGS = @NETSNMP_CFLAGS@
+NETSNMP_CONFIG = @NETSNMP_CONFIG@
+NETSNMP_LIBS = @NETSNMP_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PRIVSEP_CHROOT = @PRIVSEP_CHROOT@
+PRIVSEP_GROUP = @PRIVSEP_GROUP@
+PRIVSEP_USER = @PRIVSEP_USER@
+RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEMDSYSTEMUNITDIR = @SYSTEMDSYSTEMUNITDIR@
+SYSUSERSDIR = @SYSUSERSDIR@
+VERSION = @VERSION@
+XML2_CONFIG = @XML2_CONFIG@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+apparmordir = @apparmordir@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+check_CFLAGS = @check_CFLAGS@
+check_LIBS = @check_LIBS@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+launchddaemonsdir = @launchddaemonsdir@
+libbsd_CFLAGS = @libbsd_CFLAGS@
+libbsd_LIBS = @libbsd_LIBS@
+libcap_CFLAGS = @libcap_CFLAGS@
+libcap_LIBS = @libcap_LIBS@
+libdir = @libdir@
+libevent_CFLAGS = @libevent_CFLAGS@
+libevent_LDFLAGS = @libevent_LDFLAGS@
+libevent_LIBS = @libevent_LIBS@
+libexecdir = @libexecdir@
+libseccomp_CFLAGS = @libseccomp_CFLAGS@
+libseccomp_LIBS = @libseccomp_LIBS@
+libxml2_CFLAGS = @libxml2_CFLAGS@
+libxml2_LIBS = @libxml2_LIBS@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfigdir = @pkgconfigdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+systemdsystemunitdir = @systemdsystemunitdir@
+sysusersdir = @sysusersdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CFLAGS = -I $(top_srcdir)/include $(LLDP_CFLAGS)
+AM_CPPFLAGS = $(LLDP_CPPFLAGS)
+AM_LDFLAGS = $(LLDP_LDFLAGS)
+man_MANS = lldpcli.8
+dist_man_MANS = lldpctl.8
+lldpcli_SOURCES = client.h lldpcli.c display.c conf.c conf-med.c \
+ conf-inv.c conf-dot3.c conf-power.c conf-lldp.c conf-system.c \
+ commands.c show.c misc.c tokenizer.c utf8.c writer.h \
+ text_writer.c kv_writer.c json_writer.c $(am__append_1)
+lldpcli_LDADD = $(top_builddir)/src/libcommon-daemon-client.la \
+ $(top_builddir)/src/lib/liblldpctl.la @READLINE_LIBS@ \
+ $(am__append_3)
+lldpcli_CFLAGS = $(AM_CFLAGS) $(am__append_2)
+lldpcli_LDFLAGS = $(AM_LDFLAGS) $(LLDP_BIN_LDFLAGS)
+
+# Completions
+bashcompletiondir = $(datadir)/bash-completion/completions
+dist_bashcompletion_DATA = completion/lldpcli
+zshcompletiondir = $(datadir)/zsh/site-functions
+dist_zshcompletion_DATA = completion/_lldpcli
+
+# Default configuration
+lldpdconfdir = $(sysconfdir)/lldpd.d
+dist_lldpdconf_DATA = README.conf
+TEMPLATES = lldpcli.8
+EXTRA_DIST = lldpcli.8.in
+CLEANFILES = $(TEMPLATES)
+edit = $(SED) \
+ -e 's|@bindir[@]|$(bindir)|g' \
+ -e 's|@sbindir[@]|$(sbindir)|g' \
+ -e 's|@sysconfdir[@]|$(sysconfdir)|g' \
+ -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' \
+ -e 's|@libdir[@]|$(libdir)|g' \
+ -e 's|@srcdir[@]|$(srcdir)|g' \
+ -e 's|@top_builddir[@]|$(top_builddir)|g' \
+ -e 's|@includedir[@]|$(includedir)|g' \
+ -e 's|@exec_prefix[@]|$(exec_prefix)|g' \
+ -e 's|@prefix[@]|$(prefix)|g' \
+ -e 's|@VERSION[@]|$(VERSION)|g' \
+ -e 's|@PACKAGE[@]|$(PACKAGE)|g' \
+ -e 's|@PACKAGE_NAME[@]|$(PACKAGE_NAME)|g' \
+ -e 's|@PACKAGE_URL[@]|$(PACKAGE_URL)|g' \
+ -e 's|@PRIVSEP_USER[@]|$(PRIVSEP_USER)|g' \
+ -e 's|@PRIVSEP_GROUP[@]|$(PRIVSEP_GROUP)|g' \
+ -e 's|@PRIVSEP_CHROOT[@]|$(PRIVSEP_CHROOT)|g' \
+ -e 's|@LLDPD_PID_FILE[@]|$(LLDPD_PID_FILE)|g' \
+ -e 's|@LLDPD_CTL_SOCKET[@]|$(LLDPD_CTL_SOCKET)|g' \
+ -e 's|@PRIVSEP_CHROOT[@]|$(PRIVSEP_CHROOT)|g'
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/edit.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/client/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/client/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/edit.am $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+lldpcli$(EXEEXT): $(lldpcli_OBJECTS) $(lldpcli_DEPENDENCIES) $(EXTRA_lldpcli_DEPENDENCIES)
+ @rm -f lldpcli$(EXEEXT)
+ $(AM_V_CCLD)$(lldpcli_LINK) $(lldpcli_OBJECTS) $(lldpcli_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-commands.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-conf-dot3.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-conf-inv.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-conf-lldp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-conf-med.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-conf-power.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-conf-system.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-conf.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-display.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-json_writer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-kv_writer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-lldpcli.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-misc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-show.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-text_writer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-tokenizer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-utf8.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpcli-xml_writer.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+lldpcli-lldpcli.o: lldpcli.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-lldpcli.o -MD -MP -MF $(DEPDIR)/lldpcli-lldpcli.Tpo -c -o lldpcli-lldpcli.o `test -f 'lldpcli.c' || echo '$(srcdir)/'`lldpcli.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-lldpcli.Tpo $(DEPDIR)/lldpcli-lldpcli.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lldpcli.c' object='lldpcli-lldpcli.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-lldpcli.o `test -f 'lldpcli.c' || echo '$(srcdir)/'`lldpcli.c
+
+lldpcli-lldpcli.obj: lldpcli.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-lldpcli.obj -MD -MP -MF $(DEPDIR)/lldpcli-lldpcli.Tpo -c -o lldpcli-lldpcli.obj `if test -f 'lldpcli.c'; then $(CYGPATH_W) 'lldpcli.c'; else $(CYGPATH_W) '$(srcdir)/lldpcli.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-lldpcli.Tpo $(DEPDIR)/lldpcli-lldpcli.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lldpcli.c' object='lldpcli-lldpcli.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-lldpcli.obj `if test -f 'lldpcli.c'; then $(CYGPATH_W) 'lldpcli.c'; else $(CYGPATH_W) '$(srcdir)/lldpcli.c'; fi`
+
+lldpcli-display.o: display.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-display.o -MD -MP -MF $(DEPDIR)/lldpcli-display.Tpo -c -o lldpcli-display.o `test -f 'display.c' || echo '$(srcdir)/'`display.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-display.Tpo $(DEPDIR)/lldpcli-display.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='display.c' object='lldpcli-display.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-display.o `test -f 'display.c' || echo '$(srcdir)/'`display.c
+
+lldpcli-display.obj: display.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-display.obj -MD -MP -MF $(DEPDIR)/lldpcli-display.Tpo -c -o lldpcli-display.obj `if test -f 'display.c'; then $(CYGPATH_W) 'display.c'; else $(CYGPATH_W) '$(srcdir)/display.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-display.Tpo $(DEPDIR)/lldpcli-display.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='display.c' object='lldpcli-display.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-display.obj `if test -f 'display.c'; then $(CYGPATH_W) 'display.c'; else $(CYGPATH_W) '$(srcdir)/display.c'; fi`
+
+lldpcli-conf.o: conf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf.o -MD -MP -MF $(DEPDIR)/lldpcli-conf.Tpo -c -o lldpcli-conf.o `test -f 'conf.c' || echo '$(srcdir)/'`conf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf.Tpo $(DEPDIR)/lldpcli-conf.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf.c' object='lldpcli-conf.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf.o `test -f 'conf.c' || echo '$(srcdir)/'`conf.c
+
+lldpcli-conf.obj: conf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf.obj -MD -MP -MF $(DEPDIR)/lldpcli-conf.Tpo -c -o lldpcli-conf.obj `if test -f 'conf.c'; then $(CYGPATH_W) 'conf.c'; else $(CYGPATH_W) '$(srcdir)/conf.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf.Tpo $(DEPDIR)/lldpcli-conf.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf.c' object='lldpcli-conf.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf.obj `if test -f 'conf.c'; then $(CYGPATH_W) 'conf.c'; else $(CYGPATH_W) '$(srcdir)/conf.c'; fi`
+
+lldpcli-conf-med.o: conf-med.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-med.o -MD -MP -MF $(DEPDIR)/lldpcli-conf-med.Tpo -c -o lldpcli-conf-med.o `test -f 'conf-med.c' || echo '$(srcdir)/'`conf-med.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-med.Tpo $(DEPDIR)/lldpcli-conf-med.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-med.c' object='lldpcli-conf-med.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-med.o `test -f 'conf-med.c' || echo '$(srcdir)/'`conf-med.c
+
+lldpcli-conf-med.obj: conf-med.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-med.obj -MD -MP -MF $(DEPDIR)/lldpcli-conf-med.Tpo -c -o lldpcli-conf-med.obj `if test -f 'conf-med.c'; then $(CYGPATH_W) 'conf-med.c'; else $(CYGPATH_W) '$(srcdir)/conf-med.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-med.Tpo $(DEPDIR)/lldpcli-conf-med.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-med.c' object='lldpcli-conf-med.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-med.obj `if test -f 'conf-med.c'; then $(CYGPATH_W) 'conf-med.c'; else $(CYGPATH_W) '$(srcdir)/conf-med.c'; fi`
+
+lldpcli-conf-inv.o: conf-inv.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-inv.o -MD -MP -MF $(DEPDIR)/lldpcli-conf-inv.Tpo -c -o lldpcli-conf-inv.o `test -f 'conf-inv.c' || echo '$(srcdir)/'`conf-inv.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-inv.Tpo $(DEPDIR)/lldpcli-conf-inv.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-inv.c' object='lldpcli-conf-inv.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-inv.o `test -f 'conf-inv.c' || echo '$(srcdir)/'`conf-inv.c
+
+lldpcli-conf-inv.obj: conf-inv.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-inv.obj -MD -MP -MF $(DEPDIR)/lldpcli-conf-inv.Tpo -c -o lldpcli-conf-inv.obj `if test -f 'conf-inv.c'; then $(CYGPATH_W) 'conf-inv.c'; else $(CYGPATH_W) '$(srcdir)/conf-inv.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-inv.Tpo $(DEPDIR)/lldpcli-conf-inv.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-inv.c' object='lldpcli-conf-inv.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-inv.obj `if test -f 'conf-inv.c'; then $(CYGPATH_W) 'conf-inv.c'; else $(CYGPATH_W) '$(srcdir)/conf-inv.c'; fi`
+
+lldpcli-conf-dot3.o: conf-dot3.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-dot3.o -MD -MP -MF $(DEPDIR)/lldpcli-conf-dot3.Tpo -c -o lldpcli-conf-dot3.o `test -f 'conf-dot3.c' || echo '$(srcdir)/'`conf-dot3.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-dot3.Tpo $(DEPDIR)/lldpcli-conf-dot3.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-dot3.c' object='lldpcli-conf-dot3.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-dot3.o `test -f 'conf-dot3.c' || echo '$(srcdir)/'`conf-dot3.c
+
+lldpcli-conf-dot3.obj: conf-dot3.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-dot3.obj -MD -MP -MF $(DEPDIR)/lldpcli-conf-dot3.Tpo -c -o lldpcli-conf-dot3.obj `if test -f 'conf-dot3.c'; then $(CYGPATH_W) 'conf-dot3.c'; else $(CYGPATH_W) '$(srcdir)/conf-dot3.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-dot3.Tpo $(DEPDIR)/lldpcli-conf-dot3.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-dot3.c' object='lldpcli-conf-dot3.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-dot3.obj `if test -f 'conf-dot3.c'; then $(CYGPATH_W) 'conf-dot3.c'; else $(CYGPATH_W) '$(srcdir)/conf-dot3.c'; fi`
+
+lldpcli-conf-power.o: conf-power.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-power.o -MD -MP -MF $(DEPDIR)/lldpcli-conf-power.Tpo -c -o lldpcli-conf-power.o `test -f 'conf-power.c' || echo '$(srcdir)/'`conf-power.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-power.Tpo $(DEPDIR)/lldpcli-conf-power.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-power.c' object='lldpcli-conf-power.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-power.o `test -f 'conf-power.c' || echo '$(srcdir)/'`conf-power.c
+
+lldpcli-conf-power.obj: conf-power.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-power.obj -MD -MP -MF $(DEPDIR)/lldpcli-conf-power.Tpo -c -o lldpcli-conf-power.obj `if test -f 'conf-power.c'; then $(CYGPATH_W) 'conf-power.c'; else $(CYGPATH_W) '$(srcdir)/conf-power.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-power.Tpo $(DEPDIR)/lldpcli-conf-power.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-power.c' object='lldpcli-conf-power.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-power.obj `if test -f 'conf-power.c'; then $(CYGPATH_W) 'conf-power.c'; else $(CYGPATH_W) '$(srcdir)/conf-power.c'; fi`
+
+lldpcli-conf-lldp.o: conf-lldp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-lldp.o -MD -MP -MF $(DEPDIR)/lldpcli-conf-lldp.Tpo -c -o lldpcli-conf-lldp.o `test -f 'conf-lldp.c' || echo '$(srcdir)/'`conf-lldp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-lldp.Tpo $(DEPDIR)/lldpcli-conf-lldp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-lldp.c' object='lldpcli-conf-lldp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-lldp.o `test -f 'conf-lldp.c' || echo '$(srcdir)/'`conf-lldp.c
+
+lldpcli-conf-lldp.obj: conf-lldp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-lldp.obj -MD -MP -MF $(DEPDIR)/lldpcli-conf-lldp.Tpo -c -o lldpcli-conf-lldp.obj `if test -f 'conf-lldp.c'; then $(CYGPATH_W) 'conf-lldp.c'; else $(CYGPATH_W) '$(srcdir)/conf-lldp.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-lldp.Tpo $(DEPDIR)/lldpcli-conf-lldp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-lldp.c' object='lldpcli-conf-lldp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-lldp.obj `if test -f 'conf-lldp.c'; then $(CYGPATH_W) 'conf-lldp.c'; else $(CYGPATH_W) '$(srcdir)/conf-lldp.c'; fi`
+
+lldpcli-conf-system.o: conf-system.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-system.o -MD -MP -MF $(DEPDIR)/lldpcli-conf-system.Tpo -c -o lldpcli-conf-system.o `test -f 'conf-system.c' || echo '$(srcdir)/'`conf-system.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-system.Tpo $(DEPDIR)/lldpcli-conf-system.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-system.c' object='lldpcli-conf-system.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-system.o `test -f 'conf-system.c' || echo '$(srcdir)/'`conf-system.c
+
+lldpcli-conf-system.obj: conf-system.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-conf-system.obj -MD -MP -MF $(DEPDIR)/lldpcli-conf-system.Tpo -c -o lldpcli-conf-system.obj `if test -f 'conf-system.c'; then $(CYGPATH_W) 'conf-system.c'; else $(CYGPATH_W) '$(srcdir)/conf-system.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-conf-system.Tpo $(DEPDIR)/lldpcli-conf-system.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conf-system.c' object='lldpcli-conf-system.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-conf-system.obj `if test -f 'conf-system.c'; then $(CYGPATH_W) 'conf-system.c'; else $(CYGPATH_W) '$(srcdir)/conf-system.c'; fi`
+
+lldpcli-commands.o: commands.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-commands.o -MD -MP -MF $(DEPDIR)/lldpcli-commands.Tpo -c -o lldpcli-commands.o `test -f 'commands.c' || echo '$(srcdir)/'`commands.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-commands.Tpo $(DEPDIR)/lldpcli-commands.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='commands.c' object='lldpcli-commands.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-commands.o `test -f 'commands.c' || echo '$(srcdir)/'`commands.c
+
+lldpcli-commands.obj: commands.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-commands.obj -MD -MP -MF $(DEPDIR)/lldpcli-commands.Tpo -c -o lldpcli-commands.obj `if test -f 'commands.c'; then $(CYGPATH_W) 'commands.c'; else $(CYGPATH_W) '$(srcdir)/commands.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-commands.Tpo $(DEPDIR)/lldpcli-commands.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='commands.c' object='lldpcli-commands.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-commands.obj `if test -f 'commands.c'; then $(CYGPATH_W) 'commands.c'; else $(CYGPATH_W) '$(srcdir)/commands.c'; fi`
+
+lldpcli-show.o: show.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-show.o -MD -MP -MF $(DEPDIR)/lldpcli-show.Tpo -c -o lldpcli-show.o `test -f 'show.c' || echo '$(srcdir)/'`show.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-show.Tpo $(DEPDIR)/lldpcli-show.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='show.c' object='lldpcli-show.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-show.o `test -f 'show.c' || echo '$(srcdir)/'`show.c
+
+lldpcli-show.obj: show.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-show.obj -MD -MP -MF $(DEPDIR)/lldpcli-show.Tpo -c -o lldpcli-show.obj `if test -f 'show.c'; then $(CYGPATH_W) 'show.c'; else $(CYGPATH_W) '$(srcdir)/show.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-show.Tpo $(DEPDIR)/lldpcli-show.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='show.c' object='lldpcli-show.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-show.obj `if test -f 'show.c'; then $(CYGPATH_W) 'show.c'; else $(CYGPATH_W) '$(srcdir)/show.c'; fi`
+
+lldpcli-misc.o: misc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-misc.o -MD -MP -MF $(DEPDIR)/lldpcli-misc.Tpo -c -o lldpcli-misc.o `test -f 'misc.c' || echo '$(srcdir)/'`misc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-misc.Tpo $(DEPDIR)/lldpcli-misc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='misc.c' object='lldpcli-misc.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-misc.o `test -f 'misc.c' || echo '$(srcdir)/'`misc.c
+
+lldpcli-misc.obj: misc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-misc.obj -MD -MP -MF $(DEPDIR)/lldpcli-misc.Tpo -c -o lldpcli-misc.obj `if test -f 'misc.c'; then $(CYGPATH_W) 'misc.c'; else $(CYGPATH_W) '$(srcdir)/misc.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-misc.Tpo $(DEPDIR)/lldpcli-misc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='misc.c' object='lldpcli-misc.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-misc.obj `if test -f 'misc.c'; then $(CYGPATH_W) 'misc.c'; else $(CYGPATH_W) '$(srcdir)/misc.c'; fi`
+
+lldpcli-tokenizer.o: tokenizer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-tokenizer.o -MD -MP -MF $(DEPDIR)/lldpcli-tokenizer.Tpo -c -o lldpcli-tokenizer.o `test -f 'tokenizer.c' || echo '$(srcdir)/'`tokenizer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-tokenizer.Tpo $(DEPDIR)/lldpcli-tokenizer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tokenizer.c' object='lldpcli-tokenizer.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-tokenizer.o `test -f 'tokenizer.c' || echo '$(srcdir)/'`tokenizer.c
+
+lldpcli-tokenizer.obj: tokenizer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-tokenizer.obj -MD -MP -MF $(DEPDIR)/lldpcli-tokenizer.Tpo -c -o lldpcli-tokenizer.obj `if test -f 'tokenizer.c'; then $(CYGPATH_W) 'tokenizer.c'; else $(CYGPATH_W) '$(srcdir)/tokenizer.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-tokenizer.Tpo $(DEPDIR)/lldpcli-tokenizer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tokenizer.c' object='lldpcli-tokenizer.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-tokenizer.obj `if test -f 'tokenizer.c'; then $(CYGPATH_W) 'tokenizer.c'; else $(CYGPATH_W) '$(srcdir)/tokenizer.c'; fi`
+
+lldpcli-utf8.o: utf8.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-utf8.o -MD -MP -MF $(DEPDIR)/lldpcli-utf8.Tpo -c -o lldpcli-utf8.o `test -f 'utf8.c' || echo '$(srcdir)/'`utf8.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-utf8.Tpo $(DEPDIR)/lldpcli-utf8.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utf8.c' object='lldpcli-utf8.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-utf8.o `test -f 'utf8.c' || echo '$(srcdir)/'`utf8.c
+
+lldpcli-utf8.obj: utf8.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-utf8.obj -MD -MP -MF $(DEPDIR)/lldpcli-utf8.Tpo -c -o lldpcli-utf8.obj `if test -f 'utf8.c'; then $(CYGPATH_W) 'utf8.c'; else $(CYGPATH_W) '$(srcdir)/utf8.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-utf8.Tpo $(DEPDIR)/lldpcli-utf8.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utf8.c' object='lldpcli-utf8.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-utf8.obj `if test -f 'utf8.c'; then $(CYGPATH_W) 'utf8.c'; else $(CYGPATH_W) '$(srcdir)/utf8.c'; fi`
+
+lldpcli-text_writer.o: text_writer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-text_writer.o -MD -MP -MF $(DEPDIR)/lldpcli-text_writer.Tpo -c -o lldpcli-text_writer.o `test -f 'text_writer.c' || echo '$(srcdir)/'`text_writer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-text_writer.Tpo $(DEPDIR)/lldpcli-text_writer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='text_writer.c' object='lldpcli-text_writer.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-text_writer.o `test -f 'text_writer.c' || echo '$(srcdir)/'`text_writer.c
+
+lldpcli-text_writer.obj: text_writer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-text_writer.obj -MD -MP -MF $(DEPDIR)/lldpcli-text_writer.Tpo -c -o lldpcli-text_writer.obj `if test -f 'text_writer.c'; then $(CYGPATH_W) 'text_writer.c'; else $(CYGPATH_W) '$(srcdir)/text_writer.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-text_writer.Tpo $(DEPDIR)/lldpcli-text_writer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='text_writer.c' object='lldpcli-text_writer.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-text_writer.obj `if test -f 'text_writer.c'; then $(CYGPATH_W) 'text_writer.c'; else $(CYGPATH_W) '$(srcdir)/text_writer.c'; fi`
+
+lldpcli-kv_writer.o: kv_writer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-kv_writer.o -MD -MP -MF $(DEPDIR)/lldpcli-kv_writer.Tpo -c -o lldpcli-kv_writer.o `test -f 'kv_writer.c' || echo '$(srcdir)/'`kv_writer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-kv_writer.Tpo $(DEPDIR)/lldpcli-kv_writer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kv_writer.c' object='lldpcli-kv_writer.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-kv_writer.o `test -f 'kv_writer.c' || echo '$(srcdir)/'`kv_writer.c
+
+lldpcli-kv_writer.obj: kv_writer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-kv_writer.obj -MD -MP -MF $(DEPDIR)/lldpcli-kv_writer.Tpo -c -o lldpcli-kv_writer.obj `if test -f 'kv_writer.c'; then $(CYGPATH_W) 'kv_writer.c'; else $(CYGPATH_W) '$(srcdir)/kv_writer.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-kv_writer.Tpo $(DEPDIR)/lldpcli-kv_writer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kv_writer.c' object='lldpcli-kv_writer.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-kv_writer.obj `if test -f 'kv_writer.c'; then $(CYGPATH_W) 'kv_writer.c'; else $(CYGPATH_W) '$(srcdir)/kv_writer.c'; fi`
+
+lldpcli-json_writer.o: json_writer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-json_writer.o -MD -MP -MF $(DEPDIR)/lldpcli-json_writer.Tpo -c -o lldpcli-json_writer.o `test -f 'json_writer.c' || echo '$(srcdir)/'`json_writer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-json_writer.Tpo $(DEPDIR)/lldpcli-json_writer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='json_writer.c' object='lldpcli-json_writer.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-json_writer.o `test -f 'json_writer.c' || echo '$(srcdir)/'`json_writer.c
+
+lldpcli-json_writer.obj: json_writer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-json_writer.obj -MD -MP -MF $(DEPDIR)/lldpcli-json_writer.Tpo -c -o lldpcli-json_writer.obj `if test -f 'json_writer.c'; then $(CYGPATH_W) 'json_writer.c'; else $(CYGPATH_W) '$(srcdir)/json_writer.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-json_writer.Tpo $(DEPDIR)/lldpcli-json_writer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='json_writer.c' object='lldpcli-json_writer.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-json_writer.obj `if test -f 'json_writer.c'; then $(CYGPATH_W) 'json_writer.c'; else $(CYGPATH_W) '$(srcdir)/json_writer.c'; fi`
+
+lldpcli-xml_writer.o: xml_writer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-xml_writer.o -MD -MP -MF $(DEPDIR)/lldpcli-xml_writer.Tpo -c -o lldpcli-xml_writer.o `test -f 'xml_writer.c' || echo '$(srcdir)/'`xml_writer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-xml_writer.Tpo $(DEPDIR)/lldpcli-xml_writer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xml_writer.c' object='lldpcli-xml_writer.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-xml_writer.o `test -f 'xml_writer.c' || echo '$(srcdir)/'`xml_writer.c
+
+lldpcli-xml_writer.obj: xml_writer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -MT lldpcli-xml_writer.obj -MD -MP -MF $(DEPDIR)/lldpcli-xml_writer.Tpo -c -o lldpcli-xml_writer.obj `if test -f 'xml_writer.c'; then $(CYGPATH_W) 'xml_writer.c'; else $(CYGPATH_W) '$(srcdir)/xml_writer.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lldpcli-xml_writer.Tpo $(DEPDIR)/lldpcli-xml_writer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xml_writer.c' object='lldpcli-xml_writer.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lldpcli_CFLAGS) $(CFLAGS) -c -o lldpcli-xml_writer.obj `if test -f 'xml_writer.c'; then $(CYGPATH_W) 'xml_writer.c'; else $(CYGPATH_W) '$(srcdir)/xml_writer.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(dist_man_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ @list1=''; \
+ list2='$(dist_man_MANS) $(man_MANS)'; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list=''; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(dist_man_MANS) $(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.8[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+install-dist_bashcompletionDATA: $(dist_bashcompletion_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(dist_bashcompletion_DATA)'; test -n "$(bashcompletiondir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bashcompletiondir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bashcompletiondir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(bashcompletiondir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(bashcompletiondir)" || exit $$?; \
+ done
+
+uninstall-dist_bashcompletionDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_bashcompletion_DATA)'; test -n "$(bashcompletiondir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(bashcompletiondir)'; $(am__uninstall_files_from_dir)
+install-dist_lldpdconfDATA: $(dist_lldpdconf_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(dist_lldpdconf_DATA)'; test -n "$(lldpdconfdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(lldpdconfdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(lldpdconfdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(lldpdconfdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(lldpdconfdir)" || exit $$?; \
+ done
+
+uninstall-dist_lldpdconfDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_lldpdconf_DATA)'; test -n "$(lldpdconfdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(lldpdconfdir)'; $(am__uninstall_files_from_dir)
+install-dist_zshcompletionDATA: $(dist_zshcompletion_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(dist_zshcompletion_DATA)'; test -n "$(zshcompletiondir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(zshcompletiondir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(zshcompletiondir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(zshcompletiondir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(zshcompletiondir)" || exit $$?; \
+ done
+
+uninstall-dist_zshcompletionDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_zshcompletion_DATA)'; test -n "$(zshcompletiondir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(zshcompletiondir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(MANS) $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(bashcompletiondir)" "$(DESTDIR)$(lldpdconfdir)" "$(DESTDIR)$(zshcompletiondir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/lldpcli-commands.Po
+ -rm -f ./$(DEPDIR)/lldpcli-conf-dot3.Po
+ -rm -f ./$(DEPDIR)/lldpcli-conf-inv.Po
+ -rm -f ./$(DEPDIR)/lldpcli-conf-lldp.Po
+ -rm -f ./$(DEPDIR)/lldpcli-conf-med.Po
+ -rm -f ./$(DEPDIR)/lldpcli-conf-power.Po
+ -rm -f ./$(DEPDIR)/lldpcli-conf-system.Po
+ -rm -f ./$(DEPDIR)/lldpcli-conf.Po
+ -rm -f ./$(DEPDIR)/lldpcli-display.Po
+ -rm -f ./$(DEPDIR)/lldpcli-json_writer.Po
+ -rm -f ./$(DEPDIR)/lldpcli-kv_writer.Po
+ -rm -f ./$(DEPDIR)/lldpcli-lldpcli.Po
+ -rm -f ./$(DEPDIR)/lldpcli-misc.Po
+ -rm -f ./$(DEPDIR)/lldpcli-show.Po
+ -rm -f ./$(DEPDIR)/lldpcli-text_writer.Po
+ -rm -f ./$(DEPDIR)/lldpcli-tokenizer.Po
+ -rm -f ./$(DEPDIR)/lldpcli-utf8.Po
+ -rm -f ./$(DEPDIR)/lldpcli-xml_writer.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-dist_bashcompletionDATA \
+ install-dist_lldpdconfDATA install-dist_zshcompletionDATA \
+ install-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-exec-local install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man8
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/lldpcli-commands.Po
+ -rm -f ./$(DEPDIR)/lldpcli-conf-dot3.Po
+ -rm -f ./$(DEPDIR)/lldpcli-conf-inv.Po
+ -rm -f ./$(DEPDIR)/lldpcli-conf-lldp.Po
+ -rm -f ./$(DEPDIR)/lldpcli-conf-med.Po
+ -rm -f ./$(DEPDIR)/lldpcli-conf-power.Po
+ -rm -f ./$(DEPDIR)/lldpcli-conf-system.Po
+ -rm -f ./$(DEPDIR)/lldpcli-conf.Po
+ -rm -f ./$(DEPDIR)/lldpcli-display.Po
+ -rm -f ./$(DEPDIR)/lldpcli-json_writer.Po
+ -rm -f ./$(DEPDIR)/lldpcli-kv_writer.Po
+ -rm -f ./$(DEPDIR)/lldpcli-lldpcli.Po
+ -rm -f ./$(DEPDIR)/lldpcli-misc.Po
+ -rm -f ./$(DEPDIR)/lldpcli-show.Po
+ -rm -f ./$(DEPDIR)/lldpcli-text_writer.Po
+ -rm -f ./$(DEPDIR)/lldpcli-tokenizer.Po
+ -rm -f ./$(DEPDIR)/lldpcli-utf8.Po
+ -rm -f ./$(DEPDIR)/lldpcli-xml_writer.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-dist_bashcompletionDATA \
+ uninstall-dist_lldpdconfDATA uninstall-dist_zshcompletionDATA \
+ uninstall-local uninstall-man uninstall-sbinPROGRAMS
+
+uninstall-man: uninstall-man8
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS cscopelist-am \
+ ctags ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dist_bashcompletionDATA \
+ install-dist_lldpdconfDATA install-dist_zshcompletionDATA \
+ install-dvi install-dvi-am install-exec install-exec-am \
+ install-exec-local install-html install-html-am install-info \
+ install-info-am install-man install-man8 install-pdf \
+ install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \
+ uninstall-dist_bashcompletionDATA uninstall-dist_lldpdconfDATA \
+ uninstall-dist_zshcompletionDATA uninstall-local uninstall-man \
+ uninstall-man8 uninstall-sbinPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+install-exec-local: lldpcli
+ cd $(DESTDIR)$(sbindir) && rm -f lldpctl
+ cd $(DESTDIR)$(sbindir) && $(LN_S) lldpcli lldpctl
+uninstall-local:
+ cd $(DESTDIR)$(sbindir) && rm -f lldpctl
+lldpcli.8: lldpcli.8.in
+
+$(TEMPLATES): Makefile
+ $(AM_V_GEN)$(MKDIR_P) $(@D) && $(edit) $(srcdir)/$@.in > $@.tmp && mv $@.tmp $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/client/README.conf b/src/client/README.conf
new file mode 100644
index 0000000..4982e9e
--- /dev/null
+++ b/src/client/README.conf
@@ -0,0 +1,8 @@
+# You can put lldpd configuration snippets into this directory.
+# Upon start, lldpd will read each files in this directory and execute content
+# as if they were passed as arguments to lldpcli
+#
+# Files should be suffixed by .conf and have content like:
+# configure system description 'my little server'
+#
+# See lldpcli(8) for more details.
diff --git a/src/client/client.h b/src/client/client.h
new file mode 100644
index 0000000..04fca94
--- /dev/null
+++ b/src/client/client.h
@@ -0,0 +1,148 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _CLIENT_H
+#define _CLIENT_H
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "../lib/lldpctl.h"
+#include "../lldp-const.h"
+#include "../log.h"
+#include "../ctl.h"
+#include "../compat/compat.h"
+#include "writer.h"
+
+#ifdef HAVE_ADDRESS_SANITIZER
+# include <sanitizer/lsan_interface.h>
+# define SUPPRESS_LEAK(x) __lsan_ignore_object(x)
+#else
+# define SUPPRESS_LEAK(x)
+#endif
+
+/* Readline stuff */
+#ifdef HAVE_LIBREADLINE
+# if defined(HAVE_READLINE_READLINE_H)
+# include <readline/readline.h>
+# elif defined(HAVE_READLINE_H)
+# include <readline.h>
+# else
+extern char *readline();
+extern char *rl_line_buffer;
+extern int rl_point;
+extern int rl_insert_text(const char *);
+extern void rl_forced_update_display(void);
+extern int rl_bind_key(int, int (*f)(int, int));
+# endif
+#endif
+#ifdef HAVE_READLINE_HISTORY
+# if defined(HAVE_READLINE_HISTORY_H)
+# include <readline/history.h>
+# elif defined(HAVE_HISTORY_H)
+# include <history.h>
+# else
+extern void add_history();
+# endif
+#endif
+#undef NEWLINE
+
+extern const char *ctlname;
+
+/* commands.c */
+#define NEWLINE "<CR>"
+struct cmd_node;
+struct cmd_env;
+struct cmd_node *commands_root(void);
+struct cmd_node *commands_new(struct cmd_node *, const char *, const char *,
+ int (*validate)(struct cmd_env *, const void *),
+ int (*execute)(struct lldpctl_conn_t *, struct writer *, struct cmd_env *,
+ const void *),
+ const void *);
+struct cmd_node *commands_privileged(struct cmd_node *);
+struct cmd_node *commands_lock(struct cmd_node *);
+struct cmd_node *commands_hidden(struct cmd_node *);
+void commands_free(struct cmd_node *);
+const char *cmdenv_arg(struct cmd_env *);
+const char *cmdenv_get(struct cmd_env *, const char *);
+int cmdenv_put(struct cmd_env *, const char *, const char *);
+int cmdenv_pop(struct cmd_env *, int);
+int commands_execute(struct lldpctl_conn_t *, struct writer *, struct cmd_node *, int,
+ const char **, int);
+char *commands_complete(struct cmd_node *, int, const char **, int, int);
+/* helpers */
+int cmd_check_no_env(struct cmd_env *, const void *);
+int cmd_check_env(struct cmd_env *, const void *);
+int cmd_store_env(struct lldpctl_conn_t *, struct writer *, struct cmd_env *,
+ const void *);
+int cmd_store_env_and_pop(struct lldpctl_conn_t *, struct writer *, struct cmd_env *,
+ const void *);
+int cmd_store_env_value(struct lldpctl_conn_t *, struct writer *, struct cmd_env *,
+ const void *);
+int cmd_store_env_value_and_pop(struct lldpctl_conn_t *, struct writer *,
+ struct cmd_env *, const void *);
+int cmd_store_env_value_and_pop2(struct lldpctl_conn_t *, struct writer *,
+ struct cmd_env *, const void *);
+int cmd_store_env_value_and_pop3(struct lldpctl_conn_t *, struct writer *,
+ struct cmd_env *, const void *);
+int cmd_store_something_env_value_and_pop2(const char *, struct cmd_env *,
+ const void *);
+int cmd_store_something_env_value(const char *, struct cmd_env *, const void *);
+lldpctl_atom_t *cmd_iterate_on_interfaces(struct lldpctl_conn_t *, struct cmd_env *);
+lldpctl_atom_t *cmd_iterate_on_ports(struct lldpctl_conn_t *, struct cmd_env *,
+ const char **);
+void cmd_restrict_ports(struct cmd_node *);
+void cmd_restrict_protocol(struct cmd_node *);
+
+/* misc.c */
+int contains(const char *, const char *);
+const char *totag(const char *);
+
+/* display.c */
+#define DISPLAY_BRIEF 1
+#define DISPLAY_NORMAL 2
+#define DISPLAY_DETAILS 3
+void display_interfaces(lldpctl_conn_t *, struct writer *, struct cmd_env *, int, int);
+void display_interface(lldpctl_conn_t *, struct writer *, int, lldpctl_atom_t *,
+ lldpctl_atom_t *, int, int);
+void display_local_chassis(lldpctl_conn_t *, struct writer *, struct cmd_env *, int);
+void display_configuration(lldpctl_conn_t *, struct writer *);
+void display_interfaces_stats(lldpctl_conn_t *, struct writer *, struct cmd_env *);
+void display_interface_stats(lldpctl_conn_t *, struct writer *, lldpctl_atom_t *);
+void display_local_interfaces(lldpctl_conn_t *, struct writer *, struct cmd_env *, int,
+ int);
+
+/* show.c */
+void register_commands_show(struct cmd_node *);
+void register_commands_watch(struct cmd_node *);
+
+/* conf*.c */
+void register_commands_configure(struct cmd_node *);
+void register_commands_configure_system(struct cmd_node *, struct cmd_node *);
+void register_commands_configure_lldp(struct cmd_node *, struct cmd_node *);
+void register_commands_configure_med(struct cmd_node *, struct cmd_node *);
+void register_commands_configure_inventory(struct cmd_node *, struct cmd_node *);
+void register_commands_configure_dot3(struct cmd_node *);
+void register_commands_medpow(struct cmd_node *);
+void register_commands_dot3pow(struct cmd_node *);
+
+/* tokenizer.c */
+int tokenize_line(const char *, int *, char ***);
+void tokenize_free(int, char **);
+
+#endif
diff --git a/src/client/commands.c b/src/client/commands.c
new file mode 100644
index 0000000..804789e
--- /dev/null
+++ b/src/client/commands.c
@@ -0,0 +1,855 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "client.h"
+#include <string.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <libgen.h>
+
+/**
+ * An element of the environment (a key and a value).
+ */
+struct cmd_env_el {
+ TAILQ_ENTRY(cmd_env_el) next; /**< Next environment element */
+ const char *key; /**< Key for this element */
+ const char *value; /**< Value for this element */
+};
+
+/**
+ * A stack element.
+ */
+struct cmd_env_stack {
+ TAILQ_ENTRY(cmd_env_stack) next; /**< Next element, down the stack */
+ struct cmd_node *el; /**< Stored element */
+};
+
+/**
+ * Structure representing an environment for the current command.
+ *
+ * An environment is a list of values stored for use for the function executing
+ * as well as the current command, the current position in the command and a
+ * stack for cmd_node
+ */
+struct cmd_env {
+ TAILQ_HEAD(, cmd_env_el) elements; /**< List of environment variables */
+ TAILQ_HEAD(, cmd_env_stack) stack; /**< Stack */
+ int argc; /**< Number of argument in the command */
+ int argp; /**< Current argument */
+ const char **argv; /**< Arguments */
+};
+
+/**
+ * Structure representing a command node.
+ *
+ * Such a node contains a token accepted to enter the node (or @c NULL if there
+ * is no token needed), a documentation string to present the user, a function
+ * to validate the user input (or @c NULL if no function is needed) and a
+ * function to execute when entering the node. Because we can enter a node just
+ * by completing, the execution part should have no other effect than modifying
+ * the environment, with the exception of execution on @c NEWLINE (which cannot
+ * happen when completing).
+ */
+struct cmd_node {
+ TAILQ_ENTRY(cmd_node) next; /**< Next sibling */
+
+ const char *token; /**< Token to enter this cnode */
+ const char *doc; /**< Documentation string */
+ int privileged; /**< Privileged command? */
+ int lock; /**< Lock required for execution? */
+ int hidden; /**< Hidden command? */
+
+ /**
+ * Function validating entry in this node. Can be @c NULL.
+ */
+ int (*validate)(struct cmd_env *, const void *);
+ /**
+ * Function to execute when entering this node. May be @c NULL.
+ *
+ * This function can alter the environment
+ */
+ int (*execute)(struct lldpctl_conn_t *, struct writer *, struct cmd_env *,
+ const void *);
+ const void *arg; /**< Magic argument for the previous two functions */
+
+ /* List of possible subentries */
+ TAILQ_HEAD(, cmd_node) subentries; /* List of subnodes */
+};
+
+/**
+ * Create a root node.
+ *
+ * @return the root node.
+ */
+struct cmd_node *
+commands_root(void)
+{
+ struct cmd_node *new = calloc(1, sizeof(struct cmd_node));
+ if (new == NULL) fatalx("lldpctl", "out of memory");
+ TAILQ_INIT(&new->subentries);
+ return new;
+}
+
+/**
+ * Make a node accessible only to privileged users.
+ *
+ * @param node node to change privileges
+ * @return the modified node
+ *
+ * The node is modified. It is returned to ease chaining.
+ */
+struct cmd_node *
+commands_privileged(struct cmd_node *node)
+{
+ if (node) node->privileged = 1;
+ return node;
+}
+
+/**
+ * Make a node accessible only with a lock.
+ *
+ * @param node node to use lock to execute
+ * @return the modified node
+ *
+ * The node is modified. It is returned to ease chaining.
+ */
+struct cmd_node *
+commands_lock(struct cmd_node *node)
+{
+ if (node) node->lock = 1;
+ return node;
+}
+
+/**
+ * Hide a node from help or completion.
+ *
+ * @param node node to hide
+ * @return the modified node
+ *
+ * The node is modified. It is returned to ease chaining.
+ */
+struct cmd_node *
+commands_hidden(struct cmd_node *node)
+{
+ if (node) node->hidden = 1;
+ return node;
+}
+
+/**
+ * Create a new node acessible by any user.
+ *
+ * @param root The node we want to attach this node.
+ * @param token Token to enter this node. Or @c NULL if no token is needed.
+ * @param doc Documentation for this node.
+ * @param validate Function that should return 1 if we can enter the node.
+ * @param execute Function that should return 1 on successful execution of this node.
+ * @param arg Magic argument for precedent functions.
+ * @return the newly created node
+ */
+struct cmd_node *
+commands_new(struct cmd_node *root, const char *token, const char *doc,
+ int (*validate)(struct cmd_env *, const void *),
+ int (*execute)(struct lldpctl_conn_t *, struct writer *, struct cmd_env *,
+ const void *),
+ const void *arg)
+{
+ struct cmd_node *new = calloc(1, sizeof(struct cmd_node));
+ if (new == NULL) fatalx("lldpctl", "out of memory");
+ new->token = token;
+ new->doc = doc;
+ new->validate = validate;
+ new->execute = execute;
+ new->arg = arg;
+ TAILQ_INIT(&new->subentries);
+ TAILQ_INSERT_TAIL(&root->subentries, new, next);
+ return new;
+}
+
+/**
+ * Free a command tree.
+ *
+ * @param root The node we want to free.
+ */
+void
+commands_free(struct cmd_node *root)
+{
+ struct cmd_node *subcmd, *subcmd_next;
+ for (subcmd = TAILQ_FIRST(&root->subentries); subcmd != NULL;
+ subcmd = subcmd_next) {
+ subcmd_next = TAILQ_NEXT(subcmd, next);
+ TAILQ_REMOVE(&root->subentries, subcmd, next);
+ commands_free(subcmd);
+ }
+ free(root);
+}
+
+/**
+ * Return the current argument in the environment. This can be @c NEWLINE or
+ * @c NULL.
+ *
+ * @param env The environment.
+ * @return current argument.
+ */
+const char *
+cmdenv_arg(struct cmd_env *env)
+{
+ if (env->argp < env->argc) return env->argv[env->argp];
+ if (env->argp == env->argc) return NEWLINE;
+ return NULL;
+}
+
+/**
+ * Get a value from the environment.
+ *
+ * @param env The environment.
+ * @param key The key for the requested value.
+ * @return @c NULL if not found or the requested value otherwise. If no value is
+ * associated, return the key.
+ */
+const char *
+cmdenv_get(struct cmd_env *env, const char *key)
+{
+ struct cmd_env_el *el;
+ TAILQ_FOREACH (el, &env->elements, next)
+ if (!strcmp(el->key, key)) return el->value ? el->value : el->key;
+ return NULL;
+}
+
+/**
+ * Put a value in the environment.
+ *
+ * @param env The environment.
+ * @param key The key for the value.
+ * @param value The value.
+ * @return 0 on success, -1 otherwise.
+ */
+int
+cmdenv_put(struct cmd_env *env, const char *key, const char *value)
+{
+ struct cmd_env_el *el = malloc(sizeof(struct cmd_env_el));
+ if (el == NULL) {
+ log_warn("lldpctl",
+ "unable to allocate memory for new environment variable");
+ return -1;
+ }
+ el->key = key;
+ el->value = value;
+ TAILQ_INSERT_TAIL(&env->elements, el, next);
+ return 0;
+}
+
+/**
+ * Pop some node from the execution stack.
+ *
+ * This allows to resume parsing on a previous state. Useful to call after
+ * parsing optional arguments.
+ *
+ * @param env The environment.
+ * @param n How many element we want to pop.
+ * @return 0 on success, -1 otherwise.
+ */
+int
+cmdenv_pop(struct cmd_env *env, int n)
+{
+ while (n-- > 0) {
+ if (TAILQ_EMPTY(&env->stack)) {
+ log_warnx("lldpctl", "environment stack is empty");
+ return -1;
+ }
+ struct cmd_env_stack *first = TAILQ_FIRST(&env->stack);
+ TAILQ_REMOVE(&env->stack, first, next);
+ free(first);
+ }
+ return 0;
+}
+
+/**
+ * Push some node on the execution stack.
+ *
+ * @param env The environment.
+ * @param node The node to push.
+ * @return 0 on success, -1 on error.
+ */
+static int
+cmdenv_push(struct cmd_env *env, struct cmd_node *node)
+{
+ struct cmd_env_stack *el = malloc(sizeof(struct cmd_env_stack));
+ if (el == NULL) {
+ log_warn("lldpctl", "not enough memory to allocate a stack element");
+ return -1;
+ }
+ el->el = node;
+ TAILQ_INSERT_HEAD(&env->stack, el, next);
+ return 0;
+}
+
+/**
+ * Return the top of the stack, without poping it.
+ *
+ * @param env The environment.
+ * @return the top element or @c NULL is the stack is empty.
+ */
+static struct cmd_node *
+cmdenv_top(struct cmd_env *env)
+{
+ if (TAILQ_EMPTY(&env->stack)) return NULL;
+ return TAILQ_FIRST(&env->stack)->el;
+}
+
+/**
+ * Free execution environment.
+ *
+ * @param env The environment.
+ */
+static void
+cmdenv_free(struct cmd_env *env)
+{
+ while (!TAILQ_EMPTY(&env->stack))
+ cmdenv_pop(env, 1);
+
+ struct cmd_env_el *first;
+ while (!TAILQ_EMPTY(&env->elements)) {
+ first = TAILQ_FIRST(&env->elements);
+ TAILQ_REMOVE(&env->elements, first, next);
+ free(first);
+ }
+}
+
+struct candidate_word {
+ TAILQ_ENTRY(candidate_word) next;
+ const char *word;
+ const char *doc;
+ int hidden;
+};
+
+/**
+ * Execute or complete a command from the given node.
+ *
+ * @param conn Connection to lldpd.
+ * @param w Writer for output.
+ * @param root Root node we want to start from.
+ * @param argc Number of arguments.
+ * @param argv Array of arguments.
+ * @param word Completed word. Or NULL when no completion is required.
+ * @param all When completing, display possible completions even if only one choice
+ * is possible.
+ * @param priv Is the current user privileged?
+ * @return 0 on success, -1 otherwise.
+ */
+static int
+_commands_execute(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_node *root,
+ int argc, const char **argv, char **word, int all, int priv)
+{
+ int n, rc = 0, completion = (word != NULL);
+ int help = 0; /* Are we asking for help? */
+ int complete = 0; /* Are we asking for possible completions? */
+ int needlock = 0; /* Do we need a lock? */
+ struct cmd_env env = { .elements = TAILQ_HEAD_INITIALIZER(env.elements),
+ .stack = TAILQ_HEAD_INITIALIZER(env.stack),
+ .argc = argc,
+ .argv = argv,
+ .argp = 0 };
+ static int lockfd = -1;
+ static char *lockname = NULL; /* Name of lockfile */
+ cmdenv_push(&env, root);
+ if (!completion)
+ for (n = 0; n < argc; n++)
+ log_debug("lldpctl", "argument %02d: `%s`", n, argv[n]);
+ if (completion) *word = NULL;
+
+#define CAN_EXECUTE(candidate) \
+ ((!candidate->privileged || priv || complete) && \
+ (!candidate->validate || candidate->validate(&env, candidate->arg) == 1))
+
+ /* When completion is in progress, we use the same algorithm than for
+ * execution until we reach the cursor position. */
+ struct cmd_node *current = NULL;
+ while ((current = cmdenv_top(&env))) {
+ if (!completion) {
+ help = !!cmdenv_get(&env, "help"); /* Are we asking for help? */
+ complete = !!cmdenv_get(&env, "complete"); /* Or completion? */
+ }
+
+ struct cmd_node *candidate, *best = NULL;
+ const char *token = (env.argp < env.argc) ? env.argv[env.argp] :
+ (env.argp == env.argc && !help && !complete) ? NEWLINE :
+ NULL;
+ if (token == NULL || (completion && env.argp == env.argc - 1)) goto end;
+ if (!completion)
+ log_debug("lldpctl", "process argument %02d: `%s`", env.argp,
+ token);
+ TAILQ_FOREACH (candidate, &current->subentries, next) {
+ if (candidate->token &&
+ !strncmp(candidate->token, token, strlen(token)) &&
+ CAN_EXECUTE(candidate)) {
+ if (candidate->token &&
+ !strcmp(candidate->token, token)) {
+ /* Exact match */
+ best = candidate;
+ needlock = needlock || candidate->lock;
+ break;
+ }
+ if (!best)
+ best = candidate;
+ else {
+ if (!completion)
+ log_warnx("lldpctl",
+ "ambiguous token: %s (%s or %s)",
+ token, candidate->token,
+ best->token);
+ rc = -1;
+ goto end;
+ }
+ }
+ }
+ if (!best) {
+ /* Take first that validate */
+ TAILQ_FOREACH (candidate, &current->subentries, next) {
+ if (!candidate->token && CAN_EXECUTE(candidate)) {
+ best = candidate;
+ needlock = needlock || candidate->lock;
+ break;
+ }
+ }
+ }
+ if (!best && env.argp == env.argc) goto end;
+ if (!best) {
+ if (!completion)
+ log_warnx("lldpctl",
+ "unknown command from argument %d: `%s`",
+ env.argp + 1, token);
+ rc = -1;
+ goto end;
+ }
+
+ /* Push and execute */
+ cmdenv_push(&env, best);
+ if (best->execute) {
+ if (needlock) {
+ if (lockfd == -1) {
+ if (lockname == NULL &&
+ asprintf(&lockname, "%s.lock", ctlname) ==
+ -1) {
+ log_warnx("lldpctl",
+ "not enough memory to build lock filename");
+ rc = -1;
+ goto end;
+ }
+ log_debug("lldpctl", "open %s for locking",
+ lockname);
+ if ((lockfd = open(lockname, O_RDWR)) == -1) {
+ log_warn("lldpctl",
+ "cannot open lock %s", lockname);
+ rc = -1;
+ goto end;
+ }
+ }
+ if (lockf(lockfd, F_LOCK, 0) == -1) {
+ log_warn("lldpctl", "cannot get lock on %s",
+ lockname);
+ rc = -1;
+ close(lockfd);
+ lockfd = -1;
+ goto end;
+ }
+ }
+ rc = best->execute(conn, w, &env, best->arg) != 1 ? -1 : rc;
+ if (needlock && lockf(lockfd, F_ULOCK, 0) == -1) {
+ log_warn("lldpctl", "cannot unlock %s", lockname);
+ close(lockfd);
+ lockfd = -1;
+ }
+ if (rc == -1) goto end;
+ }
+ env.argp++;
+ }
+end:
+ if (!completion && !help && !complete) {
+ if (rc == 0 && env.argp != env.argc + 1) {
+ log_warnx("lldpctl", "incomplete command");
+ rc = -1;
+ }
+ } else if (rc == 0 && (env.argp == env.argc - 1 || help || complete)) {
+ /* We need to complete. Let's build the list of candidate words. */
+ struct cmd_node *candidate = NULL;
+ size_t maxl = 10; /* Max length of a word */
+ TAILQ_HEAD(, candidate_word) words; /* List of subnodes */
+ TAILQ_INIT(&words);
+ current = cmdenv_top(&env);
+ if (!TAILQ_EMPTY(&current->subentries)) {
+ TAILQ_FOREACH (candidate, &current->subentries, next) {
+ if ((!candidate->token || help || complete ||
+ !strncmp(env.argv[env.argc - 1],
+ candidate->token,
+ strlen(env.argv[env.argc - 1]))) &&
+ CAN_EXECUTE(candidate)) {
+ struct candidate_word *cword =
+ malloc(sizeof(struct candidate_word));
+ if (!cword) break;
+ cword->word = candidate->token;
+ cword->doc = candidate->doc;
+ cword->hidden = candidate->hidden;
+ if (cword->word && strlen(cword->word) > maxl)
+ maxl = strlen(cword->word);
+ TAILQ_INSERT_TAIL(&words, cword, next);
+ }
+ }
+ }
+ if (!TAILQ_EMPTY(&words)) {
+ /* Search if there is a common prefix, then return it. */
+ char prefix[maxl + 2]; /* Extra space may be added at the end */
+ struct candidate_word *cword, *cword_next;
+ memset(prefix, 0, maxl + 2);
+ for (size_t n = 0; n < maxl; n++) {
+ int c = 1; /* Set to 0 to exit outer loop */
+ TAILQ_FOREACH (cword, &words, next) {
+ c = 0;
+ if (cword->hidden) continue;
+ if (cword->word == NULL) break;
+ if (!strcmp(cword->word, NEWLINE)) break;
+ if (strlen(cword->word) == n) break;
+ if (prefix[n] == '\0')
+ prefix[n] = cword->word[n];
+ else if (prefix[n] != cword->word[n])
+ break;
+ c = 1;
+ }
+ if (c == 0) {
+ prefix[n] = '\0';
+ break;
+ }
+ }
+ /* If the prefix is complete, add a space, otherwise,
+ * just return it as is. */
+ if (!all && !help && !complete && strcmp(prefix, NEWLINE) &&
+ strlen(prefix) > 0 &&
+ strlen(env.argv[env.argc - 1]) < strlen(prefix)) {
+ TAILQ_FOREACH (cword, &words, next) {
+ if (cword->word &&
+ !strcmp(prefix, cword->word)) {
+ prefix[strlen(prefix)] = ' ';
+ break;
+ }
+ }
+ *word = strdup(prefix);
+ } else {
+ /* No common prefix, print possible completions */
+ if (!complete)
+ fprintf(stderr, "\n-- \033[1;34m%s\033[0m\n",
+ current->doc ? current->doc : "Help");
+ TAILQ_FOREACH (cword, &words, next) {
+ if (cword->hidden) continue;
+
+ char fmt[100];
+ if (!complete) {
+ snprintf(fmt, sizeof(fmt),
+ "%s%%%ds%s %%s\n", "\033[1m",
+ (int)maxl, "\033[0m");
+ fprintf(stderr, fmt,
+ cword->word ? cword->word : "WORD",
+ cword->doc ? cword->doc : "...");
+ } else {
+ if (!cword->word ||
+ !strcmp(cword->word, NEWLINE))
+ continue;
+ fprintf(stdout, "%s %s\n",
+ cword->word ? cword->word : "WORD",
+ cword->doc ? cword->doc : "...");
+ }
+ }
+ }
+ for (cword = TAILQ_FIRST(&words); cword != NULL;
+ cword = cword_next) {
+ cword_next = TAILQ_NEXT(cword, next);
+ TAILQ_REMOVE(&words, cword, next);
+ free(cword);
+ }
+ }
+ }
+ cmdenv_free(&env);
+ return rc;
+}
+
+/**
+ * Complete the given command.
+ */
+char *
+commands_complete(struct cmd_node *root, int argc, const char **argv, int all,
+ int privileged)
+{
+ char *word = NULL;
+ if (_commands_execute(NULL, NULL, root, argc, argv, &word, all, privileged) ==
+ 0)
+ return word;
+ return NULL;
+}
+
+/**
+ * Execute the given commands.
+ */
+int
+commands_execute(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_node *root,
+ int argc, const char **argv, int privileged)
+{
+ return _commands_execute(conn, w, root, argc, argv, NULL, 0, privileged);
+}
+
+/**
+ * Check if the environment does not contain the given key.
+ *
+ * @param env The environment.
+ * @param key The key to search for.
+ * @return 1 if the environment does not contain the key. 0 otherwise.
+ */
+int
+cmd_check_no_env(struct cmd_env *env, const void *key)
+{
+ return cmdenv_get(env, (const char *)key) == NULL;
+}
+
+/**
+ * Check if the environment does contain the given key.
+ *
+ * @param env The environment.
+ * @param key The key to search for. Can be a comma separated list.
+ * @return 1 if the environment does contain the key. 0 otherwise.
+ */
+int
+cmd_check_env(struct cmd_env *env, const void *key)
+{
+ struct cmd_env_el *el;
+ const char *list = key;
+ int count = 0;
+ TAILQ_FOREACH (el, &env->elements, next) {
+ if (contains(list, el->key)) count++;
+ }
+ while ((list = strchr(list, ','))) {
+ list++;
+ count--;
+ }
+ return (count == 1);
+}
+
+/**
+ * Store the given key in the environment.
+ *
+ * @param conn The connection.
+ * @param w The writer (not used).
+ * @param env The environment.
+ * @param key The key to store.
+ * @return 1 if the key was stored
+ */
+int
+cmd_store_env(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *key)
+{
+ return cmdenv_put(env, key, NULL) != -1;
+}
+
+/**
+ * Store the given key in the environment and pop one element from the stack.
+ *
+ * @param conn The connection.
+ * @param w The writer (not used).
+ * @param env The environment.
+ * @param key The key to store.
+ * @return 1 if the key was stored
+ */
+int
+cmd_store_env_and_pop(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *key)
+{
+ return (cmd_store_env(conn, w, env, key) != -1 && cmdenv_pop(env, 1) != -1);
+}
+
+/**
+ * Store the given key with a value being the current keyword in the environment
+ * and pop X elements from the stack.
+ *
+ * @param conn The connection.
+ * @param w The writer (not used).
+ * @param env The environment.
+ * @param key The key to store.
+ * @return 1 if the key was stored
+ */
+int
+cmd_store_env_value_and_pop(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *key)
+{
+ return (
+ cmdenv_put(env, key, cmdenv_arg(env)) != -1 && cmdenv_pop(env, 1) != -1);
+}
+int
+cmd_store_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *key)
+{
+ return (
+ cmdenv_put(env, key, cmdenv_arg(env)) != -1 && cmdenv_pop(env, 2) != -1);
+}
+int
+cmd_store_env_value(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *key)
+{
+ return (cmdenv_put(env, key, cmdenv_arg(env)) != -1);
+}
+int
+cmd_store_env_value_and_pop3(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *key)
+{
+ return (
+ cmdenv_put(env, key, cmdenv_arg(env)) != -1 && cmdenv_pop(env, 3) != -1);
+}
+int
+cmd_store_something_env_value_and_pop2(const char *what, struct cmd_env *env,
+ const void *value)
+{
+ return (cmdenv_put(env, what, value) != -1 && cmdenv_pop(env, 2) != -1);
+}
+int
+cmd_store_something_env_value(const char *what, struct cmd_env *env, const void *value)
+{
+ return (cmdenv_put(env, what, value) != -1);
+}
+
+/**
+ * Provide an iterator on all interfaces contained in "ports".
+ *
+ * @warning This function is not reentrant. It uses static variables to keep
+ * track of ports that have already been provided. Moreover, to release all
+ * resources, the iterator should be used until its end.
+ *
+ * @param conn The connection.
+ * @param env The environment.
+ * @return The next interface in the set of ports (or in all ports if no `ports`
+ * variable is present in the environment)
+ */
+lldpctl_atom_t *
+cmd_iterate_on_interfaces(struct lldpctl_conn_t *conn, struct cmd_env *env)
+{
+ static lldpctl_atom_iter_t *iter = NULL;
+ static lldpctl_atom_t *iface_list = NULL;
+ static lldpctl_atom_t *iface = NULL;
+ const char *interfaces = cmdenv_get(env, "ports");
+
+ do {
+ if (iter == NULL) {
+ iface_list = lldpctl_get_interfaces(conn);
+ if (!iface_list) {
+ log_warnx("lldpctl",
+ "not able to get the list of interfaces. %s",
+ lldpctl_last_strerror(conn));
+ return NULL;
+ }
+ iter = lldpctl_atom_iter(iface_list);
+ if (!iter) return NULL;
+ } else {
+ iter = lldpctl_atom_iter_next(iface_list, iter);
+ if (iface) {
+ lldpctl_atom_dec_ref(iface);
+ iface = NULL;
+ }
+ if (!iter) {
+ lldpctl_atom_dec_ref(iface_list);
+ return NULL;
+ }
+ }
+
+ iface = lldpctl_atom_iter_value(iface_list, iter);
+ } while (interfaces &&
+ !contains(interfaces,
+ lldpctl_atom_get_str(iface, lldpctl_k_interface_name)));
+
+ return iface;
+}
+
+/**
+ * Provide an iterator on all ports contained in "ports", as well as the
+ * default port.
+ *
+ * @warning This function is not reentrant. It uses static variables to keep
+ * track of ports that have already been provided. Moreover, to release all
+ * resources, the iterator should be used until its end.
+ *
+ * @param conn The connection.
+ * @param env The environment.
+ * @param name Name of the interface (for logging purpose)
+ * @return The next interface in the set of ports (or in all ports if no `ports`
+ * variable is present in the environment), including the default port
+ * if no `ports` variable is present in the environment.
+ */
+lldpctl_atom_t *
+cmd_iterate_on_ports(struct lldpctl_conn_t *conn, struct cmd_env *env,
+ const char **name)
+{
+ static int put_default = 0;
+ static lldpctl_atom_t *last_port = NULL;
+ const char *interfaces = cmdenv_get(env, "ports");
+
+ if (last_port) {
+ lldpctl_atom_dec_ref(last_port);
+ last_port = NULL;
+ }
+ if (!put_default) {
+ lldpctl_atom_t *iface = cmd_iterate_on_interfaces(conn, env);
+ if (iface) {
+ *name = lldpctl_atom_get_str(iface, lldpctl_k_interface_name);
+ last_port = lldpctl_get_port(iface);
+ return last_port;
+ }
+ if (!interfaces) {
+ put_default = 1;
+ *name = "(default)";
+ last_port = lldpctl_get_default_port(conn);
+ return last_port;
+ }
+ return NULL;
+ } else {
+ put_default = 0;
+ return NULL;
+ }
+}
+
+/**
+ * Restrict the command to some ports.
+ */
+void
+cmd_restrict_ports(struct cmd_node *root)
+{
+ /* Restrict to some ports. */
+ commands_new(commands_new(root, "ports", "Restrict configuration to some ports",
+ cmd_check_no_env, NULL, "ports"),
+ NULL,
+ "Restrict configuration to the specified ports (comma-separated list)",
+ NULL, cmd_store_env_value_and_pop2, "ports");
+}
+
+/**
+ * Restrict the command to specific protocol
+ */
+void
+cmd_restrict_protocol(struct cmd_node *root)
+{
+ /* Restrict to some ports. */
+ commands_new(commands_new(root, "protocol", "Restrict to specific protocol",
+ cmd_check_no_env, NULL, "protocol"),
+ NULL, "Restrict display to the specified protocol", NULL,
+ cmd_store_env_value_and_pop2, "protocol");
+}
diff --git a/src/client/completion/_lldpcli b/src/client/completion/_lldpcli
new file mode 100644
index 0000000..ff942f1
--- /dev/null
+++ b/src/client/completion/_lldpcli
@@ -0,0 +1,48 @@
+#compdef lldpcli
+#
+# zsh completion for lldpcli
+#
+# Copyright (c) 2014 Vincent Bernat <bernat@luffy.cx>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+__lldpcli_command () {
+ local -a completions
+ completions=(${(f)"$(_call_program commands lldpcli complete ${words[1,$(($CURRENT-1))]})"})
+ completions=(${completions:s/ /:/})
+ _describe -t lldpcli-command "lldpcli completion" completions "$@"
+}
+
+_lldpcli () {
+ local curcontext="$curcontext" state line
+
+ _arguments -C \
+ '*-d[print more debugging information]' \
+ '(- *)-v[print version number and exit]' \
+ '-u[use an alternate socket with lldpd]:UNIX socket:_files' \
+ '-f[output format]:format:(plain xml json json0 keyvalue)' \
+ '*-c[read a configuration file]:configuration file:_files' \
+ '(-)*::lldpcli command:__lldpcli_command'
+}
+
+
+_lldpcli "$@"
+
+# Local Variables:
+# mode: Shell-Script
+# sh-indentation: 4
+# indent-tabs-mode: nil
+# sh-basic-offset: 4
+# End:
+# vim: ft=zsh sw=4 ts=4 et
diff --git a/src/client/completion/lldpcli b/src/client/completion/lldpcli
new file mode 100755
index 0000000..48476f7
--- /dev/null
+++ b/src/client/completion/lldpcli
@@ -0,0 +1,26 @@
+_lldpcli()
+{
+ COMPREPLY=()
+ COMP_WORDBREAKS=" "
+ local cur=${COMP_WORDS[COMP_CWORD]}
+ local cmd=(${COMP_WORDS[*]})
+
+ if [ "" != "$cur" ]; then
+ unset cmd[COMP_CWORD]
+ fi
+
+ local choices=$(${cmd[0]} complete ${cmd[@]:1} | \
+ cut -d " " -f 1)
+ COMPREPLY=($(compgen -W '${choices}' -- ${cur} ))
+ return 0
+}
+
+complete -F _lldpcli lldpcli
+
+# Local Variables:
+# mode: Shell-Script
+# sh-indentation: 4
+# indent-tabs-mode: nil
+# sh-basic-offset: 4
+# End:
+# vim: ft=zsh sw=4 ts=4 et
diff --git a/src/client/conf-dot3.c b/src/client/conf-dot3.c
new file mode 100644
index 0000000..e90b60d
--- /dev/null
+++ b/src/client/conf-dot3.c
@@ -0,0 +1,35 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2013 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+
+#include "client.h"
+#include "../log.h"
+
+/**
+ * Commands to configure dot3
+ */
+void
+register_commands_configure_dot3(struct cmd_node *configure)
+{
+ if (lldpctl_key_get_map(lldpctl_k_dot3_power_class)[0].string == NULL) return;
+
+ struct cmd_node *configure_dot3 =
+ commands_new(configure, "dot3", "Dot3 configuration", NULL, NULL, NULL);
+ register_commands_dot3pow(configure_dot3);
+}
diff --git a/src/client/conf-inv.c b/src/client/conf-inv.c
new file mode 100644
index 0000000..b137a18
--- /dev/null
+++ b/src/client/conf-inv.c
@@ -0,0 +1,161 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * SPDX-FileCopyrightText: 2022 Koninklijke Philips N.V.
+ * SPDX-License-Identifier: ISC
+ */
+
+#include <unistd.h>
+#include <string.h>
+
+#include "client.h"
+#include "../log.h"
+
+static int
+cmd_inventory(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ log_debug("lldpctl", "configure inventory information");
+
+ lldpctl_atom_t *chassis = lldpctl_get_local_chassis(conn);
+
+ if (chassis == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+
+ const char *action = arg;
+ if ((!strcmp(action, "hardware-revision") &&
+ (lldpctl_atom_set_str(chassis, lldpctl_k_chassis_med_inventory_hw,
+ cmdenv_get(env, "hardware-revision")) == NULL)) ||
+ (!strcmp(action, "software-revision") &&
+ (lldpctl_atom_set_str(chassis, lldpctl_k_chassis_med_inventory_sw,
+ cmdenv_get(env, "software-revision")) == NULL)) ||
+ (!strcmp(action, "firmware-revision") &&
+ (lldpctl_atom_set_str(chassis, lldpctl_k_chassis_med_inventory_fw,
+ cmdenv_get(env, "firmware-revision")) == NULL)) ||
+ (!strcmp(action, "serial-number") &&
+ (lldpctl_atom_set_str(chassis, lldpctl_k_chassis_med_inventory_sn,
+ cmdenv_get(env, "serial-number")) == NULL)) ||
+ (!strcmp(action, "manufacturer") &&
+ (lldpctl_atom_set_str(chassis, lldpctl_k_chassis_med_inventory_manuf,
+ cmdenv_get(env, "manufacturer")) == NULL)) ||
+ (!strcmp(action, "model") &&
+ (lldpctl_atom_set_str(chassis, lldpctl_k_chassis_med_inventory_model,
+ cmdenv_get(env, "model")) == NULL)) ||
+ (!strcmp(action, "asset") &&
+ (lldpctl_atom_set_str(chassis, lldpctl_k_chassis_med_inventory_asset,
+ cmdenv_get(env, "asset")) == NULL))) {
+ log_warnx("lldpctl", "Unable to setup inventory. %s",
+ lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(chassis);
+ return 0;
+ }
+
+ log_info("lldpctl", "Configuration for inventory is applied");
+ lldpctl_atom_dec_ref(chassis);
+ return 1;
+}
+
+/**
+ * Register `configure inventory *` commands
+ *
+ */
+static void
+register_commands_inv(struct cmd_node *configure_inv, struct cmd_node *unconfigure_inv)
+{
+ commands_new(commands_new(commands_new(configure_inv, "hardware-revision",
+ "Set hardware-revision string", NULL, NULL, NULL),
+ NULL, "Inventory hardware-revision string", NULL,
+ cmd_store_env_value, "hardware-revision"),
+ NEWLINE, "Set hardware-revision string", NULL, cmd_inventory,
+ "hardware-revision");
+
+ commands_new(commands_new(unconfigure_inv, "hardware-revision",
+ "Unset hardware-revision string", NULL, NULL, NULL),
+ NEWLINE, "Unset hardware-revision string", NULL, cmd_inventory,
+ "hardware-revision");
+
+ commands_new(commands_new(commands_new(configure_inv, "software-revision",
+ "Set software-revision string", NULL, NULL, NULL),
+ NULL, "Inventory software-revision string", NULL,
+ cmd_store_env_value, "software-revision"),
+ NEWLINE, "Set software-revision string", NULL, cmd_inventory,
+ "software-revision");
+
+ commands_new(commands_new(unconfigure_inv, "software-revision",
+ "Unset software-revision string", NULL, NULL, NULL),
+ NEWLINE, "Unset software-revision string", NULL, cmd_inventory,
+ "software-revision");
+
+ commands_new(commands_new(commands_new(configure_inv, "firmware-revision",
+ "Set firmware-revision string", NULL, NULL, NULL),
+ NULL, "Inventory firmware-revision string", NULL,
+ cmd_store_env_value, "firmware-revision"),
+ NEWLINE, "Set firmware-revision string", NULL, cmd_inventory,
+ "firmware-revision");
+
+ commands_new(commands_new(unconfigure_inv, "firmware-revision",
+ "Unset firmware-revision string", NULL, NULL, NULL),
+ NEWLINE, "Unset firmware-revision string", NULL, cmd_inventory,
+ "firmware-revision");
+
+ commands_new(commands_new(commands_new(configure_inv, "serial-number",
+ "Set serial-number string", NULL, NULL, NULL),
+ NULL, "Inventory serial-number string", NULL,
+ cmd_store_env_value, "serial-number"),
+ NEWLINE, "Set serial-number string", NULL, cmd_inventory, "serial-number");
+
+ commands_new(commands_new(unconfigure_inv, "serial-number",
+ "Unset serial-number string", NULL, NULL, NULL),
+ NEWLINE, "Unset serial-number string", NULL, cmd_inventory,
+ "serial-number");
+
+ commands_new(commands_new(commands_new(configure_inv, "manufacturer",
+ "Set manufacturer string", NULL, NULL, NULL),
+ NULL, "Inventory manufacturer string", NULL,
+ cmd_store_env_value, "manufacturer"),
+ NEWLINE, "Set manufacturer string", NULL, cmd_inventory, "manufacturer");
+
+ commands_new(commands_new(unconfigure_inv, "manufacturer",
+ "Unset manufacturer string", NULL, NULL, NULL),
+ NEWLINE, "Unset manufacturer string", NULL, cmd_inventory, "manufacturer");
+
+ commands_new(commands_new(commands_new(configure_inv, "model",
+ "Set model string", NULL, NULL, NULL),
+ NULL, "Inventory model string", NULL, cmd_store_env_value,
+ "model"),
+ NEWLINE, "Set model string", NULL, cmd_inventory, "model");
+
+ commands_new(commands_new(unconfigure_inv, "model", "Unset model string", NULL,
+ NULL, NULL),
+ NEWLINE, "Unset model string", NULL, cmd_inventory, "model");
+
+ commands_new(commands_new(commands_new(configure_inv, "asset",
+ "Set asset string", NULL, NULL, NULL),
+ NULL, "Inventory asset string", NULL, cmd_store_env_value,
+ "asset"),
+ NEWLINE, "Set asset string", NULL, cmd_inventory, "asset");
+
+ commands_new(commands_new(unconfigure_inv, "asset", "Unset asset string", NULL,
+ NULL, NULL),
+ NEWLINE, "Unset asset string", NULL, cmd_inventory, "asset");
+}
+
+/**
+ * Register `configure inventory *`
+ *
+ */
+void
+register_commands_configure_inventory(struct cmd_node *configure,
+ struct cmd_node *unconfigure)
+{
+ if (lldpctl_key_get_map(lldpctl_k_med_policy_type)[0].value <= 0) return;
+
+ struct cmd_node *configure_inv = commands_new(configure, "inventory",
+ "Inventory configuration", NULL, NULL, NULL);
+ struct cmd_node *unconfigure_inv = commands_new(unconfigure, "inventory",
+ "Inventory configuration", NULL, NULL, NULL);
+
+ register_commands_inv(configure_inv, unconfigure_inv);
+}
diff --git a/src/client/conf-lldp.c b/src/client/conf-lldp.c
new file mode 100644
index 0000000..0c5b985
--- /dev/null
+++ b/src/client/conf-lldp.c
@@ -0,0 +1,762 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2013 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+
+#include "client.h"
+#include "../log.h"
+
+static int
+cmd_txdelay(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ const char *interval;
+ char interval_ms[8]; /* less than 2.5 hours */
+ lldpctl_key_t key;
+ int arglen;
+
+ log_debug("lldpctl", "set transmit delay");
+
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+ interval = cmdenv_get(env, "tx-interval");
+ key = lldpctl_k_config_tx_interval;
+ /* interval is either <number> for seconds or <number>ms for milliseconds */
+ if (interval) {
+ arglen = strlen(interval);
+ /* room for "ms" in interval, room for interval in interval_ms */
+ if (arglen >= 2 && arglen - 2 < sizeof(interval_ms) &&
+ strcmp("ms", interval + arglen - 2) == 0) {
+ /* remove "ms" suffix */
+ memcpy(interval_ms, interval, arglen - 2);
+ interval_ms[arglen - 2] = '\0';
+ /* substitute key and value */
+ key = lldpctl_k_config_tx_interval_ms;
+ interval = interval_ms;
+ }
+ }
+ if (lldpctl_atom_set_str(config, key, interval) == NULL) {
+ log_warnx("lldpctl", "unable to set transmit delay. %s",
+ lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+ log_info("lldpctl", "transmit delay set to new value");
+ lldpctl_atom_dec_ref(config);
+ return 1;
+}
+
+static int
+cmd_txhold(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ log_debug("lldpctl", "set transmit hold");
+
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+ if (lldpctl_atom_set_str(config, lldpctl_k_config_tx_hold,
+ cmdenv_get(env, "tx-hold")) == NULL) {
+ log_warnx("lldpctl", "unable to set transmit hold. %s",
+ lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+ log_info("lldpctl", "transmit hold set to new value %s",
+ cmdenv_get(env, "tx-hold"));
+ lldpctl_atom_dec_ref(config);
+ return 1;
+}
+
+static int
+cmd_status(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ lldpctl_atom_t *port;
+ const char *name;
+ const char *status = cmdenv_get(env, "status");
+
+ log_debug("lldpctl", "lldp administrative port status set to '%s'", status);
+
+ if (!status || !strlen(status)) {
+ log_warnx("lldpctl", "no status specified");
+ return 0;
+ }
+
+ while ((port = cmd_iterate_on_ports(conn, env, &name))) {
+ if (lldpctl_atom_set_str(port, lldpctl_k_port_status, status) == NULL) {
+ log_warnx("lldpctl",
+ "unable to set LLDP status for %s."
+ " %s",
+ name, lldpctl_last_strerror(conn));
+ }
+ }
+
+ return 1;
+}
+
+static int
+cmd_agent_type(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ const char *str = arg;
+ int value = -1;
+
+ log_debug("lldpctl", "set agent type to '%s'", str);
+
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+
+ for (lldpctl_map_t *b_map =
+ lldpctl_key_get_map(lldpctl_k_config_lldp_agent_type);
+ b_map->string; b_map++) {
+ if (!strcmp(b_map->string, str)) {
+ value = b_map->value;
+ break;
+ }
+ }
+
+ if (value == -1) {
+ log_warnx("lldpctl", "invalid value");
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+
+ if (lldpctl_atom_set_int(config, lldpctl_k_config_lldp_agent_type, value) ==
+ NULL) {
+ log_warnx("lldpctl",
+ "unable to set LLDP agent type."
+ " %s",
+ lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+
+ log_info("lldpctl", "agent type set to new value : %s", str);
+ lldpctl_atom_dec_ref(config);
+
+ return 1;
+}
+
+static int
+cmd_portid_type_local(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *arg)
+{
+ lldpctl_atom_t *port;
+ const char *name;
+ const char *id = cmdenv_get(env, "port-id");
+ const char *descr = cmdenv_get(env, "port-descr");
+
+ log_debug("lldpctl",
+ "lldp PortID TLV Subtype Local port-id '%s' port-descr '%s'", id, descr);
+
+ if (!id || !strlen(id)) {
+ log_warnx("lldpctl", "no id specified");
+ return 0;
+ }
+
+ while ((port = cmd_iterate_on_ports(conn, env, &name))) {
+ if (lldpctl_atom_set_str(port, lldpctl_k_port_id, id) == NULL) {
+ log_warnx("lldpctl",
+ "unable to set LLDP PortID for %s."
+ " %s",
+ name, lldpctl_last_strerror(conn));
+ }
+ if (descr &&
+ lldpctl_atom_set_str(port, lldpctl_k_port_descr, descr) == NULL) {
+ log_warnx("lldpctl",
+ "unable to set LLDP Port Description for %s."
+ " %s",
+ name, lldpctl_last_strerror(conn));
+ }
+ }
+
+ return 1;
+}
+
+static int
+cmd_port_descr(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ lldpctl_atom_t *port;
+ const char *name;
+ const char *descr = cmdenv_get(env, "port-descr");
+
+ log_debug("lldpctl", "lldp port-descr '%s'", descr);
+
+ while ((port = cmd_iterate_on_ports(conn, env, &name))) {
+ if (descr &&
+ lldpctl_atom_set_str(port, lldpctl_k_port_descr, descr) == NULL) {
+ log_warnx("lldpctl",
+ "unable to set LLDP Port Description for %s."
+ " %s",
+ name, lldpctl_last_strerror(conn));
+ }
+ }
+
+ return 1;
+}
+
+static int
+cmd_portid_type(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ const char *value_str = 0;
+ int value = -1;
+
+ log_debug("lldpctl", "lldp PortID TLV Subtype");
+
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+
+ value_str = arg;
+ for (lldpctl_map_t *b_map =
+ lldpctl_key_get_map(lldpctl_k_config_lldp_portid_type);
+ b_map->string; b_map++) {
+ if (!strcmp(b_map->string, value_str)) {
+ value = b_map->value;
+ break;
+ }
+ }
+
+ if (value == -1) {
+ log_warnx("lldpctl", "invalid value");
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+
+ if (lldpctl_atom_set_int(config, lldpctl_k_config_lldp_portid_type, value) ==
+ NULL) {
+ log_warnx("lldpctl",
+ "unable to set LLDP PortID type."
+ " %s",
+ lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+
+ log_info("lldpctl", "LLDP PortID TLV type set to new value : %s", value_str);
+ lldpctl_atom_dec_ref(config);
+
+ return 1;
+}
+
+static int
+cmd_chassis_cap_advertise(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *arg)
+{
+ log_debug("lldpctl", "lldp capabilities-advertisements %s",
+ arg ? "enable" : "disable");
+
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+ if (lldpctl_atom_set_int(config, lldpctl_k_config_chassis_cap_advertise,
+ arg ? 1 : 0) == NULL) {
+ log_warnx("lldpctl",
+ "unable to %s chassis capabilities advertisement: %s",
+ arg ? "enable" : "disable", lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+ log_info("lldpctl", "chassis capabilities advertisement %s",
+ arg ? "enabled" : "disabled");
+ lldpctl_atom_dec_ref(config);
+ return 1;
+}
+
+/* FIXME: see about compressing this with other functions */
+static int
+cmd_chassis_mgmt_advertise(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *arg)
+{
+ log_debug("lldpctl", "lldp management-addresses-advertisements %s",
+ arg ? "enable" : "disable");
+
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+ if (lldpctl_atom_set_int(config, lldpctl_k_config_chassis_mgmt_advertise,
+ arg ? 1 : 0) == NULL) {
+ log_warnx("lldpctl",
+ "unable to %s management addresses advertisement: %s",
+ arg ? "enable" : "disable", lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+ log_info("lldpctl", "management addresses advertisement %s",
+ arg ? "enabled" : "disabled");
+ lldpctl_atom_dec_ref(config);
+ return 1;
+}
+
+static int
+cmd_vlan_tx(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ lldpctl_atom_t *port;
+ const char *name;
+ const char *vlan_id = cmdenv_get(env, "vlan-tx-id");
+ const char *vlan_prio = cmdenv_get(env, "vlan-tx-prio");
+ const char *vlan_dei = cmdenv_get(env, "vlan-tx-dei");
+
+ /* Default values are used to disable VLAN */
+ int vlan_id_int = -1;
+ int vlan_prio_int = -1;
+ int vlan_dei_int = -1;
+ int vlan_tag = -1;
+
+ if (!arg)
+ log_debug("lldpctl",
+ "lldp disable VLAN tagging of transmitted LLDP frames");
+ else
+ log_debug("lldpctl",
+ "lldp enable VLAN tagging of transmitted LLDP frames with VLAN ID: '%s', Priority: '%s', DEI: '%s'",
+ vlan_id, vlan_prio, vlan_dei);
+
+ if (arg) {
+ if (!vlan_id || !strlen(vlan_id)) {
+ log_warnx("lldpctl", "no VLAN id for TX specified");
+ return 0;
+ } else {
+ const char *errstr;
+ vlan_id_int = strtonum(vlan_id, 0, 4094, &errstr);
+ if (errstr != NULL) {
+ log_warnx("lldpctl", "invalid VLAN ID for TX `%s': %s",
+ vlan_id, errstr);
+ return 0;
+ }
+ }
+
+ if (!vlan_prio || !strlen(vlan_prio)) {
+ log_warnx("lldpctl",
+ "no VLAN priority for TX specified, using default (0)");
+ /* Use default priority */
+ vlan_prio_int = 0;
+ } else {
+ const char *errstr;
+ vlan_prio_int = strtonum(vlan_prio, 0, 7, &errstr);
+ if (errstr != NULL) {
+ log_warnx("lldpctl", "invalid VLAN piority `%s': %s",
+ vlan_prio, errstr);
+ return 0;
+ }
+ }
+
+ if (!vlan_dei || !strlen(vlan_dei)) {
+ log_warnx("lldpctl",
+ "no VLAN Drop eligible indicator (DEI) for TX specified, using default (0)");
+ /* Use default priority */
+ vlan_dei_int = 0;
+ } else {
+ const char *errstr;
+ vlan_dei_int = strtonum(vlan_dei, 0, 1, &errstr);
+ if (errstr != NULL) {
+ log_warnx("lldpctl",
+ "invalid VLAN Drop eligible indicator (DEI) `%s': %s",
+ vlan_dei, errstr);
+ return 0;
+ }
+ }
+ /* Priority(3bits) | DEI(1bit) | VID(12bits) */
+ vlan_tag = ((vlan_prio_int & 0x7) << 13) |
+ ((vlan_dei_int & 0x1) << 12) | (vlan_id_int & 0xfff);
+ }
+
+ while ((port = cmd_iterate_on_ports(conn, env, &name))) {
+ if (lldpctl_atom_set_int(port, lldpctl_k_port_vlan_tx, vlan_tag) ==
+ NULL) {
+ log_warnx("lldpctl",
+ "unable to set VLAN TX config on %s."
+ " %s",
+ name, lldpctl_last_strerror(conn));
+ }
+ }
+
+ return 1;
+}
+
+#ifdef ENABLE_CUSTOM
+static int
+cmd_custom_tlv_set(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ lldpctl_atom_t *port;
+ const char *s;
+ const char *name;
+ uint8_t oui[LLDP_TLV_ORG_OUI_LEN];
+ uint8_t oui_info[LLDP_TLV_ORG_OUI_INFO_MAXLEN];
+ int oui_info_len = 0;
+ uint16_t subtype = 0;
+ const char *op = "add";
+
+ if (!arg || !strcmp(arg, "remove")) op = "remove";
+
+ log_debug("lldpctl", "lldp custom-tlv(s) %s", op);
+
+ if (!arg) goto set;
+
+ s = cmdenv_get(env, "oui");
+ if (!s ||
+ (sscanf(s, "%02hhx,%02hhx,%02hhx", &oui[0], &oui[1], &oui[2]) != 3 &&
+ sscanf(s, "%02hhX,%02hhX,%02hhX", &oui[0], &oui[1], &oui[2]) != 3)) {
+ log_warnx("lldpctl", "invalid OUI value '%s'", s);
+ return 0;
+ }
+
+ s = cmdenv_get(env, "subtype");
+ if (!s) {
+ log_warnx("lldpctl", "no subtype specified");
+ return 0;
+ } else {
+ const char *errstr;
+ subtype = strtonum(s, 0, 255, &errstr);
+ if (errstr != NULL) {
+ log_warnx("lldpctl", "invalid subtype value `%s': %s", s,
+ errstr);
+ return 0;
+ }
+ }
+
+ s = cmdenv_get(env, "oui-info");
+ /* This info is optional */
+ if (s) {
+ const char delim[] = ",";
+ char *s_copy = strdup(s);
+ char *token = strtok(s_copy, delim);
+ while (token != NULL) {
+ if (sscanf(token, "%02hhx", &oui_info[oui_info_len]) == 1 ||
+ sscanf(token, "%02hhX", &oui_info[oui_info_len]) == 1)
+ oui_info_len++;
+ if (oui_info_len >= sizeof(oui_info)) break;
+ token = strtok(NULL, delim);
+ }
+ free(s_copy);
+ }
+
+ s = cmdenv_get(env, "replace");
+ /* This info is optional */
+ if (s) op = "replace";
+
+set:
+ while ((port = cmd_iterate_on_ports(conn, env, &name))) {
+ lldpctl_atom_t *custom_tlvs;
+ if (!arg) {
+ lldpctl_atom_set(port, lldpctl_k_custom_tlvs_clear, NULL);
+ } else if (!(custom_tlvs =
+ lldpctl_atom_get(port, lldpctl_k_custom_tlvs))) {
+ log_warnx("lldpctl", "unable to get custom TLVs for port %s",
+ name);
+ } else {
+ lldpctl_atom_t *tlv = lldpctl_atom_create(custom_tlvs);
+ if (!tlv) {
+ log_warnx("lldpctl",
+ "unable to create new custom TLV for port %s",
+ name);
+ } else {
+ /* Configure custom TLV */
+ lldpctl_atom_set_buffer(tlv, lldpctl_k_custom_tlv_oui,
+ oui, sizeof(oui));
+ lldpctl_atom_set_int(tlv,
+ lldpctl_k_custom_tlv_oui_subtype, subtype);
+ lldpctl_atom_set_buffer(tlv,
+ lldpctl_k_custom_tlv_oui_info_string, oui_info,
+ oui_info_len);
+ lldpctl_atom_set_str(tlv, lldpctl_k_custom_tlv_op, op);
+
+ /* Assign it to port */
+ lldpctl_atom_set(port, lldpctl_k_custom_tlv, tlv);
+
+ lldpctl_atom_dec_ref(tlv);
+ }
+ lldpctl_atom_dec_ref(custom_tlvs);
+ }
+ }
+
+ return 1;
+}
+
+static int
+cmd_check_no_add_env(struct cmd_env *env, const void *arg)
+{
+ const char *what = arg;
+ if (cmdenv_get(env, "add")) return 0;
+ if (cmdenv_get(env, what)) return 0;
+ return 1;
+}
+
+static int
+cmd_check_no_replace_env(struct cmd_env *env, const void *arg)
+{
+ const char *what = arg;
+ if (cmdenv_get(env, "replace")) return 0;
+ if (cmdenv_get(env, what)) return 0;
+ return 1;
+}
+
+static void
+register_commands_configure_lldp_custom_tlvs(struct cmd_node *configure_lldp,
+ struct cmd_node *unconfigure_lldp)
+{
+ struct cmd_node *configure_custom_tlvs;
+ struct cmd_node *unconfigure_custom_tlvs;
+ struct cmd_node *configure_custom_tlvs_basic;
+ struct cmd_node *unconfigure_custom_tlvs_basic;
+
+ configure_custom_tlvs = commands_new(configure_lldp, "custom-tlv",
+ "Add custom TLV(s) to be broadcast on ports", NULL, NULL, NULL);
+
+ unconfigure_custom_tlvs = commands_new(unconfigure_lldp, "custom-tlv",
+ "Remove ALL custom TLV(s)", NULL, NULL, NULL);
+
+ commands_new(unconfigure_custom_tlvs, NEWLINE, "Remove ALL custom TLV", NULL,
+ cmd_custom_tlv_set, NULL);
+
+ commands_new(configure_custom_tlvs, "add", "Add custom TLV",
+ cmd_check_no_replace_env, cmd_store_env_and_pop, "add");
+ commands_new(configure_custom_tlvs, "replace", "Replace custom TLV",
+ cmd_check_no_add_env, cmd_store_env_and_pop, "replace");
+
+ /* Basic form: 'configure lldp custom-tlv oui 11,22,33 subtype 44' */
+ configure_custom_tlvs_basic = commands_new(
+ commands_new(commands_new(commands_new(configure_custom_tlvs, "oui",
+ "Organizationally Unique Identifier", NULL,
+ NULL, NULL),
+ NULL, "Organizationally Unique Identifier", NULL,
+ cmd_store_env_value, "oui"),
+ "subtype", "Organizationally Defined Subtype", NULL, NULL, NULL),
+ NULL, "Organizationally Defined Subtype", NULL, cmd_store_env_value,
+ "subtype");
+
+ commands_new(configure_custom_tlvs_basic, NEWLINE,
+ "Add custom TLV(s) to be broadcast on ports", NULL, cmd_custom_tlv_set,
+ "enable");
+
+ /* Basic form: 'unconfigure lldp custom-tlv oui 11,22,33 subtype 44' */
+ unconfigure_custom_tlvs_basic = commands_new(
+ commands_new(commands_new(commands_new(unconfigure_custom_tlvs, "oui",
+ "Organizationally Unique Identifier", NULL,
+ NULL, NULL),
+ NULL, "Organizationally Unique Identifier", NULL,
+ cmd_store_env_value, "oui"),
+ "subtype", "Organizationally Defined Subtype", NULL, NULL, NULL),
+ NULL, "Organizationally Defined Subtype", NULL, cmd_store_env_value,
+ "subtype");
+
+ commands_new(unconfigure_custom_tlvs_basic, NEWLINE,
+ "Remove specific custom TLV", NULL, cmd_custom_tlv_set, "remove");
+
+ /* Extended form: 'configure custom-tlv lldp oui 11,22,33 subtype 44 oui-info
+ * 55,66,77,...' */
+ commands_new(commands_new(commands_new(configure_custom_tlvs_basic, "oui-info",
+ "Organizationally Unique Identifier", NULL, NULL,
+ NULL),
+ NULL, "OUI Info String", NULL, cmd_store_env_value,
+ "oui-info"),
+ NEWLINE, "Add custom TLV(s) to be broadcast on ports", NULL,
+ cmd_custom_tlv_set, "enable");
+}
+#endif /* ENABLE_CUSTOM */
+
+static int
+cmd_store_status_env_value(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *value)
+{
+ return cmd_store_something_env_value("status", env, value);
+}
+
+/**
+ * Register `configure lldp` commands.
+ *
+ * Those are the commands that are related to the LLDP protocol but not
+ * Dot1/Dot3/MED. Commands not related to LLDP should go in system instead.
+ */
+void
+register_commands_configure_lldp(struct cmd_node *configure,
+ struct cmd_node *unconfigure)
+{
+ struct cmd_node *configure_lldp =
+ commands_new(configure, "lldp", "LLDP configuration", NULL, NULL, NULL);
+ struct cmd_node *unconfigure_lldp =
+ commands_new(unconfigure, "lldp", "LLDP configuration", NULL, NULL, NULL);
+
+ commands_new(
+ commands_new(commands_new(configure_lldp, "tx-interval",
+ "Set LLDP transmit delay", cmd_check_no_env, NULL,
+ "ports"),
+ NULL, "LLDP transmit <delay> in seconds or <delay>ms in milliseconds",
+ NULL, cmd_store_env_value, "tx-interval"),
+ NEWLINE, "Set LLDP transmit delay", NULL, cmd_txdelay, NULL);
+
+ commands_new(commands_new(commands_new(configure_lldp, "tx-hold",
+ "Set LLDP transmit hold", cmd_check_no_env, NULL,
+ "ports"),
+ NULL, "LLDP transmit hold in seconds", NULL,
+ cmd_store_env_value, "tx-hold"),
+ NEWLINE, "Set LLDP transmit hold", NULL, cmd_txhold, NULL);
+
+ struct cmd_node *status = commands_new(configure_lldp, "status",
+ "Set administrative status", NULL, NULL, NULL);
+
+ for (lldpctl_map_t *status_map = lldpctl_key_get_map(lldpctl_k_port_status);
+ status_map->string; status_map++) {
+ const char *tag = strdup(totag(status_map->string));
+ SUPPRESS_LEAK(tag);
+ commands_new(commands_new(status, tag, status_map->string, NULL,
+ cmd_store_status_env_value, status_map->string),
+ NEWLINE, "Set port administrative status", NULL, cmd_status, NULL);
+ }
+
+ /* Configure the various agent type we can configure. */
+ struct cmd_node *configure_lldp_agent_type = commands_new(configure_lldp,
+ "agent-type", "LLDP agent type", NULL, NULL, NULL);
+ for (lldpctl_map_t *b_map =
+ lldpctl_key_get_map(lldpctl_k_config_lldp_agent_type);
+ b_map->string; b_map++) {
+ const char *tag = strdup(totag(b_map->string));
+ SUPPRESS_LEAK(tag);
+ commands_new(commands_new(configure_lldp_agent_type, tag, b_map->string,
+ NULL, NULL, NULL),
+ NEWLINE, "Set LLDP agent type", NULL, cmd_agent_type,
+ b_map->string);
+ }
+
+ /* Now handle the various portid subtypes we can configure. */
+ struct cmd_node *configure_lldp_portid_type = commands_new(configure_lldp,
+ "portidsubtype", "LLDP PortID TLV Subtype", NULL, NULL, NULL);
+
+ for (lldpctl_map_t *b_map =
+ lldpctl_key_get_map(lldpctl_k_config_lldp_portid_type);
+ b_map->string; b_map++) {
+ if (!strcmp(b_map->string, "ifname")) {
+ commands_new(commands_new(configure_lldp_portid_type,
+ b_map->string, "Interface Name",
+ cmd_check_no_env, NULL, "ports"),
+ NEWLINE, NULL, NULL, cmd_portid_type, b_map->string);
+ } else if (!strcmp(b_map->string, "local")) {
+ struct cmd_node *port_id =
+ commands_new(commands_new(configure_lldp_portid_type,
+ b_map->string, "Local", NULL, NULL, NULL),
+ NULL, "Port ID", NULL, cmd_store_env_value, "port-id");
+ commands_new(port_id, NEWLINE, "Set local port ID", NULL,
+ cmd_portid_type_local, b_map->string);
+ commands_new(commands_new(commands_new(port_id, "description",
+ "Also set port description", NULL,
+ NULL, NULL),
+ NULL, "Port description", NULL,
+ cmd_store_env_value, "port-descr"),
+ NEWLINE, "Set local port ID and description", NULL,
+ cmd_portid_type_local, NULL);
+ } else if (!strcmp(b_map->string, "macaddress")) {
+ commands_new(commands_new(configure_lldp_portid_type,
+ b_map->string, "MAC Address", cmd_check_no_env,
+ NULL, "ports"),
+ NEWLINE, NULL, NULL, cmd_portid_type, b_map->string);
+ }
+ }
+
+ commands_new(commands_new(commands_new(configure_lldp, "portdescription",
+ "Port Description", NULL, NULL, NULL),
+ NULL, "Port description", NULL, cmd_store_env_value,
+ "port-descr"),
+ NEWLINE, "Set port description", NULL, cmd_port_descr, NULL);
+
+ commands_new(commands_new(configure_lldp, "capabilities-advertisements",
+ "Enable chassis capabilities advertisement", cmd_check_no_env,
+ NULL, "ports"),
+ NEWLINE, "Enable chassis capabilities advertisement", NULL,
+ cmd_chassis_cap_advertise, "enable");
+ commands_new(commands_new(unconfigure_lldp, "capabilities-advertisements",
+ "Don't enable chassis capabilities advertisement",
+ cmd_check_no_env, NULL, "ports"),
+ NEWLINE, "Don't enable chassis capabilities advertisement", NULL,
+ cmd_chassis_cap_advertise, NULL);
+
+ commands_new(commands_new(configure_lldp, "management-addresses-advertisements",
+ "Enable management addresses advertisement", cmd_check_no_env,
+ NULL, "ports"),
+ NEWLINE, "Enable management addresses advertisement", NULL,
+ cmd_chassis_mgmt_advertise, "enable");
+ commands_new(commands_new(unconfigure_lldp,
+ "management-addresses-advertisements",
+ "Don't enable management addresses advertisement",
+ cmd_check_no_env, NULL, "ports"),
+ NEWLINE, "Don't enable management addresses advertisement", NULL,
+ cmd_chassis_mgmt_advertise, NULL);
+
+ struct cmd_node *vlan_tx =
+ commands_new(commands_new(configure_lldp, "vlan-tx",
+ "Send LLDP frames with a VLAN tag", NULL, NULL, NULL),
+ NULL, "VLAN ID (0-4094)", NULL, cmd_store_env_value, "vlan-tx-id");
+
+ struct cmd_node *vlan_tx_prio =
+ commands_new(commands_new(vlan_tx, "priority",
+ "Also set a priority in a VLAN tag (default 0)", NULL,
+ NULL, NULL),
+ NULL, "Priority to be included in a VLAN tag (0-7)", NULL,
+ cmd_store_env_value, "vlan-tx-prio");
+
+ commands_new(vlan_tx, NEWLINE, "Enable VLAN tagging of transmitted LLDP frames",
+ NULL, cmd_vlan_tx, "enable");
+
+ commands_new(vlan_tx_prio, NEWLINE,
+ "Set VLAN ID and priority for transmitted frames", NULL, cmd_vlan_tx,
+ "enable");
+
+ commands_new(
+ commands_new(
+ commands_new(vlan_tx_prio, "dei",
+ "Also set a Drop eligible indicator (DEI) in a VLAN tag (default 0)",
+ NULL, NULL, NULL),
+ NULL,
+ "Drop eligible indicator (DEI) in a VLAN tag (0-don't drop; 1-drop)",
+ NULL, cmd_store_env_value, "vlan-tx-dei"),
+ NEWLINE, "Set VLAN ID, priority and DEI for transmitted frames", NULL,
+ cmd_vlan_tx, "enable");
+
+ commands_new(commands_new(unconfigure_lldp, "vlan-tx",
+ "Send LLDP frames without VLAN tag", NULL, NULL, NULL),
+ NEWLINE, "Disable VLAN tagging of transmitted LLDP frames", NULL,
+ cmd_vlan_tx, NULL);
+
+#ifdef ENABLE_CUSTOM
+ register_commands_configure_lldp_custom_tlvs(configure_lldp, unconfigure_lldp);
+#endif
+}
diff --git a/src/client/conf-med.c b/src/client/conf-med.c
new file mode 100644
index 0000000..8c03f8e
--- /dev/null
+++ b/src/client/conf-med.c
@@ -0,0 +1,499 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+
+#include "client.h"
+#include "../log.h"
+
+static int
+_cmd_medlocation(struct lldpctl_conn_t *conn, struct cmd_env *env, int format)
+{
+ lldpctl_atom_t *port;
+ const char *name;
+ while ((port = cmd_iterate_on_ports(conn, env, &name))) {
+ lldpctl_atom_t *med_location = NULL, *med_locations = NULL;
+ const char *what = NULL;
+ int ok = 0;
+
+ med_locations = lldpctl_atom_get(port, lldpctl_k_port_med_locations);
+ if (med_locations == NULL) {
+ log_warnx("lldpctl",
+ "unable to set LLDP-MED location: support seems unavailable");
+ continue; /* Iterator needs to be run until end */
+ }
+
+ med_location = lldpctl_atom_iter_value(med_locations,
+ lldpctl_atom_iter_next(med_locations,
+ lldpctl_atom_iter(med_locations)));
+
+ switch (format) {
+ case LLDP_MED_LOCFORMAT_COORD:
+ if ((what = "format",
+ lldpctl_atom_set_int(med_location,
+ lldpctl_k_med_location_format, format)) == NULL ||
+ (what = "latitude",
+ lldpctl_atom_set_str(med_location,
+ lldpctl_k_med_location_latitude,
+ cmdenv_get(env, "latitude"))) == NULL ||
+ (what = "longitude",
+ lldpctl_atom_set_str(med_location,
+ lldpctl_k_med_location_longitude,
+ cmdenv_get(env, "longitude"))) == NULL ||
+ (what = "altitude",
+ lldpctl_atom_set_str(med_location,
+ lldpctl_k_med_location_altitude,
+ cmdenv_get(env, "altitude"))) == NULL ||
+ (what = "altitude unit",
+ lldpctl_atom_set_str(med_location,
+ lldpctl_k_med_location_altitude_unit,
+ cmdenv_get(env, "altitude-unit"))) == NULL ||
+ (what = "datum",
+ lldpctl_atom_set_str(med_location,
+ lldpctl_k_med_location_geoid,
+ cmdenv_get(env, "datum"))) == NULL)
+ log_warnx("lldpctl",
+ "unable to set LLDP MED location value for %s on %s. %s.",
+ what, name, lldpctl_last_strerror(conn));
+ else
+ ok = 1;
+ break;
+ case LLDP_MED_LOCFORMAT_CIVIC:
+ if ((what = "format",
+ lldpctl_atom_set_int(med_location,
+ lldpctl_k_med_location_format, format)) == NULL ||
+ (what = "country",
+ lldpctl_atom_set_str(med_location,
+ lldpctl_k_med_location_country,
+ cmdenv_get(env, "country"))) == NULL) {
+ log_warnx("lldpctl",
+ "unable to set LLDP MED location value for %s on %s. %s.",
+ what, name, lldpctl_last_strerror(conn));
+ break;
+ }
+ ok = 1;
+ for (lldpctl_map_t *addr_map =
+ lldpctl_key_get_map(lldpctl_k_med_civicaddress_type);
+ addr_map->string; addr_map++) {
+ lldpctl_atom_t *cael, *caels;
+ const char *value = cmdenv_get(env, addr_map->string);
+ if (!value) continue;
+
+ caels = lldpctl_atom_get(med_location,
+ lldpctl_k_med_location_ca_elements);
+ cael = lldpctl_atom_create(caels);
+
+ if (lldpctl_atom_set_str(cael,
+ lldpctl_k_med_civicaddress_type,
+ addr_map->string) == NULL ||
+ lldpctl_atom_set_str(cael,
+ lldpctl_k_med_civicaddress_value,
+ value) == NULL ||
+ lldpctl_atom_set(med_location,
+ lldpctl_k_med_location_ca_elements,
+ cael) == NULL) {
+ log_warnx("lldpctl",
+ "unable to add a civic address element `%s`. %s",
+ addr_map->string,
+ lldpctl_last_strerror(conn));
+ ok = 0;
+ }
+
+ lldpctl_atom_dec_ref(cael);
+ lldpctl_atom_dec_ref(caels);
+ if (!ok) break;
+ }
+ break;
+ case LLDP_MED_LOCFORMAT_ELIN:
+ if (lldpctl_atom_set_int(med_location,
+ lldpctl_k_med_location_format, format) == NULL ||
+ lldpctl_atom_set_str(med_location,
+ lldpctl_k_med_location_elin,
+ cmdenv_get(env, "elin")) == NULL)
+ log_warnx("lldpctl",
+ "unable to set LLDP MED location on %s. %s", name,
+ lldpctl_last_strerror(conn));
+ else
+ ok = 1;
+ break;
+ }
+ if (ok) {
+ if (lldpctl_atom_set(port, lldpctl_k_port_med_locations,
+ med_location) == NULL) {
+ log_warnx("lldpctl",
+ "unable to set LLDP MED location on %s. %s.", name,
+ lldpctl_last_strerror(conn));
+ } else
+ log_info("lldpctl",
+ "LLDP-MED location has been set for port %s", name);
+ }
+
+ lldpctl_atom_dec_ref(med_location);
+ lldpctl_atom_dec_ref(med_locations);
+ }
+ return 1;
+}
+
+static int
+cmd_medlocation_coordinate(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *arg)
+{
+ log_debug("lldpctl", "set MED location coordinate");
+ return _cmd_medlocation(conn, env, LLDP_MED_LOCFORMAT_COORD);
+}
+
+static int
+cmd_medlocation_address(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *arg)
+{
+ log_debug("lldpctl", "set MED location address");
+ return _cmd_medlocation(conn, env, LLDP_MED_LOCFORMAT_CIVIC);
+}
+
+static int
+cmd_medlocation_elin(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ log_debug("lldpctl", "set MED location ELIN");
+ return _cmd_medlocation(conn, env, LLDP_MED_LOCFORMAT_ELIN);
+}
+
+static int
+cmd_medpolicy(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ log_debug("lldpctl", "set MED policy");
+ lldpctl_atom_t *iface;
+ while ((iface = cmd_iterate_on_interfaces(conn, env))) {
+ const char *name =
+ lldpctl_atom_get_str(iface, lldpctl_k_interface_name);
+ lldpctl_atom_t *port = lldpctl_get_port(iface);
+ lldpctl_atom_t *med_policy = NULL, *med_policies = NULL;
+ const char *what = NULL;
+
+ med_policies = lldpctl_atom_get(port, lldpctl_k_port_med_policies);
+ if (med_policies == NULL) {
+ log_warnx("lldpctl",
+ "unable to set LLDP-MED policies: support seems unavailable");
+ goto end;
+ }
+
+ med_policy = lldpctl_atom_iter_value(med_policies,
+ lldpctl_atom_iter_next(med_policies,
+ lldpctl_atom_iter(med_policies)));
+
+ if ((what = "application",
+ lldpctl_atom_set_str(med_policy, lldpctl_k_med_policy_type,
+ cmdenv_get(env, "application"))) == NULL ||
+ (what = "unknown flag",
+ lldpctl_atom_set_int(med_policy, lldpctl_k_med_policy_unknown,
+ cmdenv_get(env, "unknown") ? 1 : 0)) == NULL ||
+ (what = "tagged flag",
+ lldpctl_atom_set_int(med_policy, lldpctl_k_med_policy_tagged,
+ cmdenv_get(env, "tagged") ? 1 : 0)) == NULL ||
+ (what = "vlan",
+ cmdenv_get(env, "vlan") ?
+ lldpctl_atom_set_str(med_policy, lldpctl_k_med_policy_vid,
+ cmdenv_get(env, "vlan")) :
+ lldpctl_atom_set_int(med_policy, lldpctl_k_med_policy_vid,
+ 0)) == NULL ||
+ (what = "priority",
+ cmdenv_get(env, "priority") ?
+ lldpctl_atom_set_str(med_policy,
+ lldpctl_k_med_policy_priority,
+ cmdenv_get(env, "priority")) :
+ lldpctl_atom_set_int(med_policy,
+ lldpctl_k_med_policy_priority, 0)) == NULL ||
+ (what = "dscp",
+ cmdenv_get(env, "dscp") ?
+ lldpctl_atom_set_str(med_policy, lldpctl_k_med_policy_dscp,
+ cmdenv_get(env, "dscp")) :
+ lldpctl_atom_set_int(med_policy, lldpctl_k_med_policy_dscp,
+ 0)) == NULL)
+ log_warnx("lldpctl",
+ "unable to set LLDP MED policy value for %s on %s. %s.",
+ what, name, lldpctl_last_strerror(conn));
+ else {
+ if (lldpctl_atom_set(port, lldpctl_k_port_med_policies,
+ med_policy) == NULL) {
+ log_warnx("lldpctl",
+ "unable to set LLDP MED policy on %s. %s.", name,
+ lldpctl_last_strerror(conn));
+ } else
+ log_info("lldpctl",
+ "LLDP-MED policy has been set for port %s", name);
+ }
+
+ end:
+ lldpctl_atom_dec_ref(med_policy);
+ lldpctl_atom_dec_ref(med_policies);
+ lldpctl_atom_dec_ref(port);
+ }
+ return 1;
+}
+
+/**
+ * Register `configure med location coordinate` commands.
+ */
+static void
+register_commands_medloc_coord(struct cmd_node *configure_medlocation)
+{
+ /* MED location coordinate (set) */
+ struct cmd_node *configure_medloc_coord = commands_new(configure_medlocation,
+ "coordinate", "MED location coordinate configuration", NULL, NULL, NULL);
+ commands_new(configure_medloc_coord, NEWLINE,
+ "Configure MED location coordinates", cmd_check_env,
+ cmd_medlocation_coordinate,
+ "latitude,longitude,altitude,altitude-unit,datum");
+ commands_new(commands_new(configure_medloc_coord, "latitude",
+ "Specify latitude", cmd_check_no_env, NULL, "latitude"),
+ NULL, "Latitude as xx.yyyyN or xx.yyyyS", NULL,
+ cmd_store_env_value_and_pop2, "latitude");
+ commands_new(commands_new(configure_medloc_coord, "longitude",
+ "Specify longitude", cmd_check_no_env, NULL, "longitude"),
+ NULL, "Longitude as xx.yyyyE or xx.yyyyW", NULL,
+ cmd_store_env_value_and_pop2, "longitude");
+ struct cmd_node *altitude =
+ commands_new(commands_new(configure_medloc_coord, "altitude",
+ "Specify altitude", cmd_check_no_env, NULL, "altitude"),
+ NULL, "Altitude", NULL, cmd_store_env_value, "altitude");
+ commands_new(altitude, "m", "meters", NULL, cmd_store_env_value_and_pop3,
+ "altitude-unit");
+ commands_new(altitude, "f", "floors", NULL, cmd_store_env_value_and_pop3,
+ "altitude-unit");
+
+ struct cmd_node *datum = commands_new(configure_medloc_coord, "datum",
+ "Specify datum", cmd_check_no_env, NULL, "datum");
+ for (lldpctl_map_t *datum_map =
+ lldpctl_key_get_map(lldpctl_k_med_location_geoid);
+ datum_map->string; datum_map++)
+ commands_new(datum, datum_map->string, NULL, NULL,
+ cmd_store_env_value_and_pop2, "datum");
+}
+
+/**
+ * Register `configure med location address` commands.
+ */
+static void
+register_commands_medloc_addr(struct cmd_node *configure_medlocation)
+{
+ /* MED location address (set) */
+ struct cmd_node *configure_medloc_addr = commands_new(configure_medlocation,
+ "address", "MED location address configuration", NULL, NULL, NULL);
+ commands_new(configure_medloc_addr, NEWLINE, "Configure MED location address",
+ cmd_check_env, cmd_medlocation_address, "country");
+
+ /* Country */
+ commands_new(commands_new(configure_medloc_addr, "country",
+ "Specify country (mandatory)", cmd_check_no_env, NULL,
+ "country"),
+ NULL, "Country as a two-letter code", NULL, cmd_store_env_value_and_pop2,
+ "country");
+
+ /* Other fields */
+ for (lldpctl_map_t *addr_map =
+ lldpctl_key_get_map(lldpctl_k_med_civicaddress_type);
+ addr_map->string; addr_map++) {
+ const char *tag = strdup(totag(addr_map->string));
+ SUPPRESS_LEAK(tag);
+ commands_new(commands_new(configure_medloc_addr, tag, addr_map->string,
+ cmd_check_no_env, NULL, addr_map->string),
+ NULL, addr_map->string, NULL, cmd_store_env_value_and_pop2,
+ addr_map->string);
+ }
+}
+
+/**
+ * Register `configure med location elin` commands.
+ */
+static void
+register_commands_medloc_elin(struct cmd_node *configure_medlocation)
+{
+ /* MED location elin (set) */
+ commands_new(commands_new(commands_new(configure_medlocation, "elin",
+ "MED location ELIN configuration", NULL, NULL,
+ NULL),
+ NULL, "ELIN number", NULL, cmd_store_env_value, "elin"),
+ NEWLINE, "Set MED location ELIN number", NULL, cmd_medlocation_elin, NULL);
+}
+
+/**
+ * Register `configure med location` commands.
+ */
+static void
+register_commands_medloc(struct cmd_node *configure_med)
+{
+ struct cmd_node *configure_medlocation = commands_new(configure_med, "location",
+ "MED location configuration", NULL, NULL, NULL);
+
+ register_commands_medloc_coord(configure_medlocation);
+ register_commands_medloc_addr(configure_medlocation);
+ register_commands_medloc_elin(configure_medlocation);
+}
+
+static int
+cmd_check_application_but_no(struct cmd_env *env, const void *arg)
+{
+ const char *what = arg;
+ if (!cmdenv_get(env, "application")) return 0;
+ if (cmdenv_get(env, what)) return 0;
+ return 1;
+}
+static int
+cmd_store_app_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *value)
+{
+ return cmd_store_something_env_value_and_pop2("application", env, value);
+}
+static int
+cmd_store_prio_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *value)
+{
+ return cmd_store_something_env_value_and_pop2("priority", env, value);
+}
+
+/**
+ * Register `configure med policy` commands.
+ */
+static void
+register_commands_medpol(struct cmd_node *configure_med)
+{
+ struct cmd_node *configure_medpolicy = commands_new(configure_med, "policy",
+ "MED policy configuration", NULL, NULL, NULL);
+
+ commands_new(configure_medpolicy, NEWLINE, "Apply new MED policy",
+ cmd_check_env, cmd_medpolicy, "application");
+
+ /* Application */
+ struct cmd_node *configure_application =
+ commands_new(configure_medpolicy, "application", "MED policy application",
+ cmd_check_no_env, NULL, "application");
+
+ for (lldpctl_map_t *pol_map = lldpctl_key_get_map(lldpctl_k_med_policy_type);
+ pol_map->string; pol_map++) {
+ char *tag = strdup(totag(pol_map->string));
+ SUPPRESS_LEAK(tag);
+ commands_new(configure_application, tag, pol_map->string, NULL,
+ cmd_store_app_env_value_and_pop2, pol_map->string);
+ }
+
+ /* Remaining keywords */
+ commands_new(configure_medpolicy, "unknown", "Set unknown flag",
+ cmd_check_application_but_no, cmd_store_env_and_pop, "unknown");
+ commands_new(configure_medpolicy, "tagged", "Set tagged flag",
+ cmd_check_application_but_no, cmd_store_env_and_pop, "tagged");
+ commands_new(commands_new(configure_medpolicy, "vlan", "VLAN advertising",
+ cmd_check_application_but_no, NULL, "vlan"),
+ NULL, "VLAN ID to advertise", NULL, cmd_store_env_value_and_pop2, "vlan");
+ commands_new(commands_new(configure_medpolicy, "dscp", "DiffServ advertising",
+ cmd_check_application_but_no, NULL, "dscp"),
+ NULL, "DSCP value to advertise (between 0 and 63)", NULL,
+ cmd_store_env_value_and_pop2, "dscp");
+ struct cmd_node *priority = commands_new(configure_medpolicy, "priority",
+ "MED policy priority", cmd_check_application_but_no, NULL, "priority");
+ for (lldpctl_map_t *prio_map =
+ lldpctl_key_get_map(lldpctl_k_med_policy_priority);
+ prio_map->string; prio_map++) {
+ char *tag = strdup(totag(prio_map->string));
+ SUPPRESS_LEAK(tag);
+ commands_new(priority, tag, prio_map->string, NULL,
+ cmd_store_prio_env_value_and_pop2, prio_map->string);
+ }
+}
+
+static int
+cmd_faststart(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ log_debug("lldpctl", "configure fast interval support");
+
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+
+ const char *action = arg;
+ if ((!strcmp(action, "enable") &&
+ (lldpctl_atom_set_int(config, lldpctl_k_config_fast_start_enabled, 1) ==
+ NULL)) ||
+ (!strcmp(action, "disable") &&
+ (lldpctl_atom_set_int(config, lldpctl_k_config_fast_start_enabled, 0) ==
+ NULL)) ||
+ (!strcmp(action, "delay") &&
+ (lldpctl_atom_set_str(config, lldpctl_k_config_fast_start_interval,
+ cmdenv_get(env, "tx-interval")) == NULL))) {
+ log_warnx("lldpctl", "unable to setup fast start. %s",
+ lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+ log_info("lldpctl", "configruation for fast start applied");
+ lldpctl_atom_dec_ref(config);
+ return 1;
+}
+
+/**
+ * Register "configure med fast-start *"
+ */
+static void
+register_commands_medfast(struct cmd_node *med, struct cmd_node *nomed)
+{
+ struct cmd_node *configure_fast = commands_new(med, "fast-start",
+ "Fast start configuration", cmd_check_no_env, NULL, "ports");
+ struct cmd_node *unconfigure_fast = commands_new(nomed, "fast-start",
+ "Fast start configuration", cmd_check_no_env, NULL, "ports");
+
+ /* Enable */
+ commands_new(commands_new(configure_fast, "enable", "Enable fast start", NULL,
+ NULL, NULL),
+ NEWLINE, "Enable fast start", NULL, cmd_faststart, "enable");
+
+ /* Set TX delay */
+ commands_new(commands_new(commands_new(configure_fast, "tx-interval",
+ "Set LLDP fast transmit delay", NULL, NULL, NULL),
+ NULL, "LLDP fast transmit delay in seconds", NULL,
+ cmd_store_env_value, "tx-interval"),
+ NEWLINE, "Set LLDP fast transmit delay", NULL, cmd_faststart, "delay");
+
+ /* Disable */
+ commands_new(commands_new(unconfigure_fast, NEWLINE, "Disable fast start", NULL,
+ cmd_faststart, "disable"),
+ NEWLINE, "Disable fast start", NULL, cmd_faststart, "disable");
+}
+
+/**
+ * Register "configure med *"
+ */
+void
+register_commands_configure_med(struct cmd_node *configure,
+ struct cmd_node *unconfigure)
+{
+ if (lldpctl_key_get_map(lldpctl_k_med_policy_type)[0].string == NULL) return;
+
+ struct cmd_node *configure_med =
+ commands_new(configure, "med", "MED configuration", NULL, NULL, NULL);
+ struct cmd_node *unconfigure_med =
+ commands_new(unconfigure, "med", "MED configuration", NULL, NULL, NULL);
+
+ register_commands_medloc(configure_med);
+ register_commands_medpol(configure_med);
+ register_commands_medpow(configure_med);
+ register_commands_medfast(configure_med, unconfigure_med);
+}
diff --git a/src/client/conf-power.c b/src/client/conf-power.c
new file mode 100644
index 0000000..7c33540
--- /dev/null
+++ b/src/client/conf-power.c
@@ -0,0 +1,389 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2013 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+
+#include "client.h"
+#include "../log.h"
+
+static int
+cmd_medpower(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ log_debug("lldpctl", "set MED power");
+ lldpctl_atom_t *port;
+ const char *name;
+ while ((port = cmd_iterate_on_ports(conn, env, &name))) {
+ lldpctl_atom_t *med_power;
+ const char *what = NULL;
+
+ med_power = lldpctl_atom_get(port, lldpctl_k_port_med_power);
+ if (med_power == NULL) {
+ log_warnx("lldpctl",
+ "unable to set LLDP-MED power: support seems unavailable");
+ continue; /* Need to finish the loop */
+ }
+
+ if ((what = "device type",
+ lldpctl_atom_set_str(med_power, lldpctl_k_med_power_type,
+ cmdenv_get(env, "device-type"))) == NULL ||
+ (what = "power source",
+ lldpctl_atom_set_str(med_power, lldpctl_k_med_power_source,
+ cmdenv_get(env, "source"))) == NULL ||
+ (what = "power priority",
+ lldpctl_atom_set_str(med_power, lldpctl_k_med_power_priority,
+ cmdenv_get(env, "priority"))) == NULL ||
+ (what = "power value",
+ lldpctl_atom_set_str(med_power, lldpctl_k_med_power_val,
+ cmdenv_get(env, "value"))) == NULL)
+ log_warnx("lldpctl",
+ "unable to set LLDP MED power value for %s on %s. %s.",
+ what, name, lldpctl_last_strerror(conn));
+ else {
+ if (lldpctl_atom_set(port, lldpctl_k_port_med_power,
+ med_power) == NULL) {
+ log_warnx("lldpctl",
+ "unable to set LLDP MED power on %s. %s.", name,
+ lldpctl_last_strerror(conn));
+ } else
+ log_info("lldpctl",
+ "LLDP-MED power has been set for port %s", name);
+ }
+
+ lldpctl_atom_dec_ref(med_power);
+ }
+ return 1;
+}
+
+static int
+cmd_store_powerpairs_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *value)
+{
+ return cmd_store_something_env_value_and_pop2("powerpairs", env, value);
+}
+static int
+cmd_store_class_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *value)
+{
+ return cmd_store_something_env_value_and_pop2("class", env, value);
+}
+static int
+cmd_store_prio_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *value)
+{
+ return cmd_store_something_env_value_and_pop2("priority", env, value);
+}
+
+static int
+cmd_dot3power(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ log_debug("lldpctl", "set dot3 power");
+ lldpctl_atom_t *port;
+ const char *name;
+ while ((port = cmd_iterate_on_ports(conn, env, &name))) {
+ lldpctl_atom_t *dot3_power;
+ const char *what = NULL;
+ int ok = 1;
+
+ dot3_power = lldpctl_atom_get(port, lldpctl_k_port_dot3_power);
+ if (dot3_power == NULL) {
+ log_warnx("lldpctl",
+ "unable to set Dot3 power: support seems unavailable");
+ continue; /* Need to finish the loop */
+ }
+
+ if ((what = "device type",
+ lldpctl_atom_set_str(dot3_power,
+ lldpctl_k_dot3_power_devicetype,
+ cmdenv_get(env, "device-type"))) == NULL ||
+ /* Flags */
+ (what = "supported flag",
+ lldpctl_atom_set_int(dot3_power, lldpctl_k_dot3_power_supported,
+ cmdenv_get(env, "supported") ? 1 : 0)) == NULL ||
+ (what = "enabled flag",
+ lldpctl_atom_set_int(dot3_power, lldpctl_k_dot3_power_enabled,
+ cmdenv_get(env, "enabled") ? 1 : 0)) == NULL ||
+ (what = "paircontrol flag",
+ lldpctl_atom_set_int(dot3_power,
+ lldpctl_k_dot3_power_paircontrol,
+ cmdenv_get(env, "paircontrol") ? 1 : 0)) == NULL ||
+ /* Powerpairs */
+ (what = "power pairs",
+ lldpctl_atom_set_str(dot3_power, lldpctl_k_dot3_power_pairs,
+ cmdenv_get(env, "powerpairs"))) == NULL ||
+ /* Class */
+ (what = "power class",
+ cmdenv_get(env, "class") ?
+ lldpctl_atom_set_str(dot3_power, lldpctl_k_dot3_power_class,
+ cmdenv_get(env, "class")) :
+ lldpctl_atom_set_int(dot3_power, lldpctl_k_dot3_power_class,
+ 0)) == NULL ||
+ (what = "802.3at type",
+ lldpctl_atom_set_int(dot3_power, lldpctl_k_dot3_power_type,
+ 0)) == NULL) {
+ log_warnx("lldpctl",
+ "unable to set LLDP Dot3 power value for %s on %s. %s.",
+ what, name, lldpctl_last_strerror(conn));
+ ok = 0;
+ } else if (cmdenv_get(env, "typeat")) {
+ int typeat = cmdenv_get(env, "typeat")[0] - '0';
+ const char *source = cmdenv_get(env, "source");
+ if ((what = "802.3at type",
+ lldpctl_atom_set_int(dot3_power,
+ lldpctl_k_dot3_power_type, typeat)) == NULL ||
+ (what = "source",
+ lldpctl_atom_set_int(dot3_power,
+ lldpctl_k_dot3_power_source,
+ (!strcmp(source, "primary")) ?
+ LLDP_DOT3_POWER_SOURCE_PRIMARY :
+ (!strcmp(source, "backup")) ?
+ LLDP_DOT3_POWER_SOURCE_BACKUP :
+ (!strcmp(source, "pse")) ?
+ LLDP_DOT3_POWER_SOURCE_PSE :
+ (!strcmp(source, "local")) ?
+ LLDP_DOT3_POWER_SOURCE_LOCAL :
+ (!strcmp(source, "both")) ?
+ LLDP_DOT3_POWER_SOURCE_BOTH :
+ LLDP_DOT3_POWER_SOURCE_UNKNOWN)) == NULL ||
+ (what = "priority",
+ lldpctl_atom_set_str(dot3_power,
+ lldpctl_k_dot3_power_priority,
+ cmdenv_get(env, "priority"))) == NULL ||
+ (what = "requested power",
+ lldpctl_atom_set_str(dot3_power,
+ lldpctl_k_dot3_power_requested,
+ cmdenv_get(env, "requested"))) == NULL ||
+ (what = "allocated power",
+ lldpctl_atom_set_str(dot3_power,
+ lldpctl_k_dot3_power_allocated,
+ cmdenv_get(env, "allocated"))) == NULL) {
+ log_warnx("lldpctl",
+ "unable to set LLDP Dot3 power value for %s on %s. %s.",
+ what, name, lldpctl_last_strerror(conn));
+ ok = 0;
+ }
+ }
+ if (ok) {
+ if (lldpctl_atom_set(port, lldpctl_k_port_dot3_power,
+ dot3_power) == NULL) {
+ log_warnx("lldpctl",
+ "unable to set LLDP Dot3 power on %s. %s.", name,
+ lldpctl_last_strerror(conn));
+ } else
+ log_info("lldpctl",
+ "LLDP Dot3 power has been set for port %s", name);
+ }
+
+ lldpctl_atom_dec_ref(dot3_power);
+ }
+ return 1;
+}
+
+static int
+cmd_check_type_but_no(struct cmd_env *env, const void *arg)
+{
+ const char *what = arg;
+ if (!cmdenv_get(env, "device-type")) return 0;
+ if (cmdenv_get(env, what)) return 0;
+ return 1;
+}
+static int
+cmd_check_typeat_but_no(struct cmd_env *env, const void *arg)
+{
+ const char *what = arg;
+ if (!cmdenv_get(env, "typeat")) return 0;
+ if (cmdenv_get(env, what)) return 0;
+ return 1;
+}
+static int
+cmd_check_type(struct cmd_env *env, const char *type)
+{
+ const char *etype = cmdenv_get(env, "device-type");
+ if (!etype) return 0;
+ return (!strcmp(type, etype));
+}
+static int
+cmd_check_pse(struct cmd_env *env, const void *arg)
+{
+ return cmd_check_type(env, "pse");
+}
+static int
+cmd_check_pd(struct cmd_env *env, const void *arg)
+{
+ return cmd_check_type(env, "pd");
+}
+
+static void
+register_commands_pow_source(struct cmd_node *source)
+{
+ commands_new(source, "unknown", "Unknown power source", NULL,
+ cmd_store_env_value_and_pop2, "source");
+ commands_new(source, "primary", "Primary power source", cmd_check_pse,
+ cmd_store_env_value_and_pop2, "source");
+ commands_new(source, "backup", "Backup power source", cmd_check_pse,
+ cmd_store_env_value_and_pop2, "source");
+ commands_new(source, "pse", "Power source is PSE", cmd_check_pd,
+ cmd_store_env_value_and_pop2, "source");
+ commands_new(source, "local", "Local power source", cmd_check_pd,
+ cmd_store_env_value_and_pop2, "source");
+ commands_new(source, "both", "Both PSE and local source available",
+ cmd_check_pd, cmd_store_env_value_and_pop2, "source");
+}
+
+static void
+register_commands_pow_priority(struct cmd_node *priority, int key)
+{
+ for (lldpctl_map_t *prio_map = lldpctl_key_get_map(key); prio_map->string;
+ prio_map++) {
+ char *tag = strdup(totag(prio_map->string));
+ SUPPRESS_LEAK(tag);
+ commands_new(priority, tag, prio_map->string, NULL,
+ cmd_store_prio_env_value_and_pop2, prio_map->string);
+ }
+}
+
+/**
+ * Register `configure med power` commands.
+ */
+void
+register_commands_medpow(struct cmd_node *configure_med)
+{
+ struct cmd_node *configure_medpower = commands_new(configure_med, "power",
+ "MED power configuration", NULL, NULL, NULL);
+
+ commands_new(configure_medpower, NEWLINE, "Apply new MED power configuration",
+ cmd_check_env, cmd_medpower, "device-type,source,priority,value");
+
+ /* Type: PSE or PD */
+ commands_new(configure_medpower, "pd", "MED power consumer", cmd_check_no_env,
+ cmd_store_env_value_and_pop, "device-type");
+ commands_new(configure_medpower, "pse", "MED power provider", cmd_check_no_env,
+ cmd_store_env_value_and_pop, "device-type");
+
+ /* Source */
+ struct cmd_node *source = commands_new(configure_medpower, "source",
+ "MED power source", cmd_check_type_but_no, NULL, "source");
+ register_commands_pow_source(source);
+
+ /* Priority */
+ struct cmd_node *priority = commands_new(configure_medpower, "priority",
+ "MED power priority", cmd_check_type_but_no, NULL, "priority");
+ register_commands_pow_priority(priority, lldpctl_k_med_power_priority);
+
+ /* Value */
+ commands_new(commands_new(configure_medpower, "value", "MED power value",
+ cmd_check_type_but_no, NULL, "value"),
+ NULL, "MED power value in milliwatts", NULL, cmd_store_env_value_and_pop2,
+ "value");
+}
+
+static int
+cmd_check_env_power(struct cmd_env *env, const void *nothing)
+{
+ /* We need type and powerpair but if we have typeat, we also request
+ * source, priority, requested and allocated. */
+ if (!cmdenv_get(env, "device-type")) return 0;
+ if (!cmdenv_get(env, "powerpairs")) return 0;
+ if (cmdenv_get(env, "typeat")) {
+ return (!!cmdenv_get(env, "source") && !!cmdenv_get(env, "priority") &&
+ !!cmdenv_get(env, "requested") && !!cmdenv_get(env, "allocated"));
+ }
+ return 1;
+}
+
+/**
+ * Register `configure med dot3` commands.
+ */
+void
+register_commands_dot3pow(struct cmd_node *configure_dot3)
+{
+ struct cmd_node *configure_dot3power = commands_new(configure_dot3, "power",
+ "Dot3 power configuration", NULL, NULL, NULL);
+
+ commands_new(configure_dot3power, NEWLINE, "Apply new Dot3 power configuration",
+ cmd_check_env_power, cmd_dot3power, NULL);
+
+ /* Type: PSE or PD */
+ commands_new(configure_dot3power, "pd", "Dot3 power consumer", cmd_check_no_env,
+ cmd_store_env_value_and_pop, "device-type");
+ commands_new(configure_dot3power, "pse", "Dot3 power provider",
+ cmd_check_no_env, cmd_store_env_value_and_pop, "device-type");
+
+ /* Flags */
+ commands_new(configure_dot3power, "supported", "MDI power support present",
+ cmd_check_type_but_no, cmd_store_env_and_pop, "supported");
+ commands_new(configure_dot3power, "enabled", "MDI power support enabled",
+ cmd_check_type_but_no, cmd_store_env_and_pop, "enabled");
+ commands_new(configure_dot3power, "paircontrol",
+ "MDI power pair can be selected", cmd_check_type_but_no,
+ cmd_store_env_and_pop, "paircontrol");
+
+ /* Power pairs */
+ struct cmd_node *powerpairs = commands_new(configure_dot3power, "powerpairs",
+ "Which pairs are currently used for power (mandatory)",
+ cmd_check_type_but_no, NULL, "powerpairs");
+ for (lldpctl_map_t *pp_map = lldpctl_key_get_map(lldpctl_k_dot3_power_pairs);
+ pp_map->string; pp_map++) {
+ commands_new(powerpairs, pp_map->string, pp_map->string, NULL,
+ cmd_store_powerpairs_env_value_and_pop2, pp_map->string);
+ }
+
+ /* Class */
+ struct cmd_node *class = commands_new(configure_dot3power, "class",
+ "Power class", cmd_check_type_but_no, NULL, "class");
+ for (lldpctl_map_t *class_map = lldpctl_key_get_map(lldpctl_k_dot3_power_class);
+ class_map->string; class_map++) {
+ const char *tag = strdup(totag(class_map->string));
+ SUPPRESS_LEAK(tag);
+ commands_new(class, tag, class_map->string, NULL,
+ cmd_store_class_env_value_and_pop2, class_map->string);
+ }
+
+ /* 802.3at type */
+ struct cmd_node *typeat = commands_new(configure_dot3power, "type",
+ "802.3at device type", cmd_check_type_but_no, NULL, "typeat");
+ commands_new(typeat, "1", "802.3at type 1", NULL, cmd_store_env_value_and_pop2,
+ "typeat");
+ commands_new(typeat, "2", "802.3at type 2", NULL, cmd_store_env_value_and_pop2,
+ "typeat");
+
+ /* Source */
+ struct cmd_node *source = commands_new(configure_dot3power, "source",
+ "802.3at dot3 power source (mandatory)", cmd_check_typeat_but_no, NULL,
+ "source");
+ register_commands_pow_source(source);
+
+ /* Priority */
+ struct cmd_node *priority = commands_new(configure_dot3power, "priority",
+ "802.3at dot3 power priority (mandatory)", cmd_check_typeat_but_no, NULL,
+ "priority");
+ register_commands_pow_priority(priority, lldpctl_k_dot3_power_priority);
+
+ /* Values */
+ commands_new(commands_new(configure_dot3power, "requested",
+ "802.3at dot3 power value requested (mandatory)",
+ cmd_check_typeat_but_no, NULL, "requested"),
+ NULL, "802.3at power value requested in milliwatts", NULL,
+ cmd_store_env_value_and_pop2, "requested");
+ commands_new(commands_new(configure_dot3power, "allocated",
+ "802.3at dot3 power value allocated (mandatory)",
+ cmd_check_typeat_but_no, NULL, "allocated"),
+ NULL, "802.3at power value allocated in milliwatts", NULL,
+ cmd_store_env_value_and_pop2, "allocated");
+}
diff --git a/src/client/conf-system.c b/src/client/conf-system.c
new file mode 100644
index 0000000..0700ec2
--- /dev/null
+++ b/src/client/conf-system.c
@@ -0,0 +1,602 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2013 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/utsname.h>
+
+#include "client.h"
+#include "../log.h"
+
+static int
+cmd_iface_pattern(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ log_debug("lldpctl", "set iface pattern");
+
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+
+ const char *value = cmdenv_get(env, "iface-pattern");
+ if (lldpctl_atom_set_str(config, lldpctl_k_config_iface_pattern, value) ==
+ NULL) {
+ log_warnx("lldpctl", "unable to set iface-pattern. %s",
+ lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+ log_info("lldpctl", "iface-pattern set to new value %s",
+ value ? value : "(none)");
+ lldpctl_atom_dec_ref(config);
+ return 1;
+}
+
+static int
+cmd_perm_iface_pattern(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *arg)
+{
+ log_debug("lldpctl", "set permanent iface pattern");
+
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+
+ const char *value = cmdenv_get(env, "iface-pattern");
+ if (lldpctl_atom_set_str(config, lldpctl_k_config_perm_iface_pattern, value) ==
+ NULL) {
+ log_warnx("lldpctl", "unable to set permanent iface pattern. %s",
+ lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+ log_info("lldpctl", "permanent iface pattern set to new value %s",
+ value ? value : "(none)");
+ lldpctl_atom_dec_ref(config);
+ return 1;
+}
+
+static int
+cmd_iface_promisc(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+ if (lldpctl_atom_set_int(config, lldpctl_k_config_iface_promisc, arg ? 1 : 0) ==
+ NULL) {
+ log_warnx("lldpctl", "unable to %s promiscuous mode: %s",
+ arg ? "enable" : "disable", lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+ log_info("lldpctl", "interface promiscuous mode %s",
+ arg ? "enabled" : "disabled");
+ lldpctl_atom_dec_ref(config);
+ return 1;
+}
+
+static int
+cmd_system_description(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *arg)
+{
+ int platform = 0;
+ const char *what = arg;
+ const char *value;
+ if (!strcmp(what, "system")) {
+ value = cmdenv_get(env, "description");
+ } else {
+ value = cmdenv_get(env, "platform");
+ platform = 1;
+ }
+ log_debug("lldpctl", "set %s description", what);
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+ if (lldpctl_atom_set_str(config,
+ platform ? lldpctl_k_config_platform : lldpctl_k_config_description,
+ value) == NULL) {
+ log_warnx("lldpctl", "unable to set description. %s",
+ lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+ log_info("lldpctl", "description set to new value %s",
+ value ? value : "(none)");
+ lldpctl_atom_dec_ref(config);
+ return 1;
+}
+
+static int
+cmd_system_chassisid(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ const char *value;
+ value = cmdenv_get(env, "description");
+ log_debug("lldpctl", "set chassis ID");
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+ if (lldpctl_atom_set_str(config, lldpctl_k_config_cid_string, value) == NULL) {
+ log_warnx("lldpctl", "unable to set chassis ID. %s",
+ lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+ log_info("lldpctl", "chassis ID set to new value %s", value ? value : "(none)");
+ lldpctl_atom_dec_ref(config);
+ return 1;
+}
+
+static int
+cmd_management(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ log_debug("lldpctl", "set management pattern");
+
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+
+ const char *value = cmdenv_get(env, "management-pattern");
+ if (lldpctl_atom_set_str(config, lldpctl_k_config_mgmt_pattern, value) ==
+ NULL) {
+ log_warnx("lldpctl", "unable to set management pattern. %s",
+ lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+ log_info("lldpctl", "management pattern set to new value %s",
+ value ? value : "(none)");
+ lldpctl_atom_dec_ref(config);
+ return 1;
+}
+
+static int
+cmd_hostname(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ struct utsname un;
+ log_debug("lldpctl", "set system name");
+
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+
+ const char *value = cmdenv_get(env, "hostname");
+ if (value && strlen(value) == 1 && value[0] == '.') {
+ if (uname(&un) < 0) {
+ log_warn("lldpctl", "cannot get node name");
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+ char *c = strchr(un.nodename, '.');
+ if (c) *c = 0;
+ value = un.nodename;
+ }
+ if (lldpctl_atom_set_str(config, lldpctl_k_config_hostname, value) == NULL) {
+ log_warnx("lldpctl", "unable to set system name. %s",
+ lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+ log_info("lldpctl", "system name set to new value %s",
+ value ? value : "(none)");
+ lldpctl_atom_dec_ref(config);
+ return 1;
+}
+
+static int
+cmd_capability(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ log_debug("lldpctl", "set capabilities");
+
+ int ret = 0;
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ lldpctl_atom_t *chassis = NULL;
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ goto cmd_capability_end;
+ }
+
+ if (!strcmp(arg, "configure")) {
+
+ const char *s = cmdenv_get(env, "capabilities");
+ if (s) {
+ chassis = lldpctl_get_local_chassis(conn);
+ if (chassis == NULL) {
+ log_warnx("lldpctl",
+ "unable to get local chassis from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ goto cmd_capability_end;
+ }
+ u_int16_t value = 0;
+ const char delim[] = ",";
+ char *s_copy = strdup(s);
+ char *token = strtok(s_copy, delim);
+ while (token != NULL) {
+ if (!strcmp(token, "other")) {
+ value |= LLDP_CAP_OTHER;
+ } else if (!strcmp(token, "repeater")) {
+ value |= LLDP_CAP_REPEATER;
+ } else if (!strcmp(token, "bridge")) {
+ value |= LLDP_CAP_BRIDGE;
+ } else if (!strcmp(token, "wlan")) {
+ value |= LLDP_CAP_WLAN;
+ } else if (!strcmp(token, "router")) {
+ value |= LLDP_CAP_ROUTER;
+ } else if (!strcmp(token, "telephone")) {
+ value |= LLDP_CAP_TELEPHONE;
+ } else if (!strcmp(token, "docsis")) {
+ value |= LLDP_CAP_DOCSIS;
+ } else if (!strcmp(token, "station")) {
+ value |= LLDP_CAP_STATION;
+ } else {
+ log_warnx("lldpctl", "capability %s not found",
+ token);
+ }
+ token = strtok(NULL, delim);
+ }
+ free(s_copy);
+
+ if (lldpctl_atom_set_int(chassis, lldpctl_k_chassis_cap_enabled,
+ value) == NULL) {
+ log_warnx("lldpctl",
+ "unable to set system capabilities. %s",
+ lldpctl_last_strerror(conn));
+ goto cmd_capability_end;
+ }
+ if (lldpctl_atom_set_int(config,
+ lldpctl_k_config_chassis_cap_override, 1) == NULL) {
+ log_warnx("lldpctl",
+ "unable to set system capabilities override. %s",
+ lldpctl_last_strerror(conn));
+ goto cmd_capability_end;
+ }
+ log_debug("lldpctl", "system capabilities set to new value %d",
+ value);
+ }
+ } else {
+ if (lldpctl_atom_set_int(config, lldpctl_k_config_chassis_cap_override,
+ 0) == NULL) {
+ log_warnx("lldpctl",
+ "unable to set system capabilities to not override. %s",
+ lldpctl_last_strerror(conn));
+ goto cmd_capability_end;
+ }
+ }
+
+ ret = 1;
+cmd_capability_end:
+ lldpctl_atom_dec_ref(chassis);
+ lldpctl_atom_dec_ref(config);
+ return ret;
+}
+
+static int
+cmd_update_descriptions(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *arg)
+{
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+ if (lldpctl_atom_set_int(config, lldpctl_k_config_ifdescr_update,
+ arg ? 1 : 0) == NULL) {
+ log_warnx("lldpctl", "unable to %s interface description update: %s",
+ arg ? "enable" : "disable", lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+ log_info("lldpctl", "interface description update %s",
+ arg ? "enabled" : "disabled");
+ lldpctl_atom_dec_ref(config);
+ return 1;
+}
+
+static int
+cmd_bondslave_srcmac_type(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *arg)
+{
+ const char *value_str = 0;
+ int value = -1;
+
+ log_debug("lldpctl", "bond slave src mac");
+
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+
+ value_str = arg;
+ for (lldpctl_map_t *b_map =
+ lldpctl_key_get_map(lldpctl_k_config_bond_slave_src_mac_type);
+ b_map->string; b_map++) {
+ if (!strcmp(b_map->string, value_str)) {
+ value = b_map->value;
+ break;
+ }
+ }
+
+ if (value == -1) {
+ log_warnx("lldpctl", "invalid value");
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+
+ if (lldpctl_atom_set_int(config, lldpctl_k_config_bond_slave_src_mac_type,
+ value) == NULL) {
+ log_warnx("lldpctl",
+ "unable to set bond slave src mac type."
+ " %s",
+ lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+
+ log_info("lldpctl", "bond slave src mac set to new value: %s", value_str);
+ lldpctl_atom_dec_ref(config);
+
+ return 1;
+}
+
+static int
+cmd_maxneighs(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ log_debug("lldpctl", "set maximum neighbors");
+
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+ if (lldpctl_atom_set_str(config, lldpctl_k_config_max_neighbors,
+ cmdenv_get(env, "max-neighbors")) == NULL) {
+ log_warnx("lldpctl", "unable to set maximum of neighbors. %s",
+ lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+ log_info("lldpctl", "maximum neighbors set to new value %s",
+ cmdenv_get(env, "max-neighbors"));
+ lldpctl_atom_dec_ref(config);
+ return 1;
+}
+
+/**
+ * Register `configure system bond-slave-src-mac-type`
+ */
+static void
+register_commands_srcmac_type(struct cmd_node *configure)
+{
+ struct cmd_node *bond_slave_src_mac_type =
+ commands_new(configure, "bond-slave-src-mac-type",
+ "Set LLDP bond slave source MAC type", NULL, NULL, NULL);
+
+ for (lldpctl_map_t *b_map =
+ lldpctl_key_get_map(lldpctl_k_config_bond_slave_src_mac_type);
+ b_map->string; b_map++) {
+ if (!strcmp(b_map->string, "real")) {
+ commands_new(commands_new(bond_slave_src_mac_type,
+ b_map->string, "Real mac", NULL, NULL, NULL),
+ NEWLINE, NULL, NULL, cmd_bondslave_srcmac_type,
+ b_map->string);
+ } else if (!strcmp(b_map->string, "zero")) {
+ commands_new(commands_new(bond_slave_src_mac_type,
+ b_map->string, "All zero mac", NULL, NULL,
+ NULL),
+ NEWLINE, NULL, NULL, cmd_bondslave_srcmac_type,
+ b_map->string);
+ } else if (!strcmp(b_map->string, "fixed")) {
+ commands_new(commands_new(bond_slave_src_mac_type,
+ b_map->string, "Fixed value (3Com card)", NULL,
+ NULL, NULL),
+ NEWLINE, NULL, NULL, cmd_bondslave_srcmac_type,
+ b_map->string);
+ } else if (!strcmp(b_map->string, "local")) {
+ commands_new(commands_new(bond_slave_src_mac_type,
+ b_map->string,
+ "Real Mac with locally "
+ "administered bit set",
+ NULL, NULL, NULL),
+ NEWLINE, NULL, NULL, cmd_bondslave_srcmac_type,
+ b_map->string);
+ }
+ }
+}
+
+static void
+register_commands_capabilities(struct cmd_node *configure, struct cmd_node *unconfigure)
+{
+ struct cmd_node *configure_capability = commands_new(configure, "capabilities",
+ "Capabilities configuration", cmd_check_no_env, NULL, "ports");
+ struct cmd_node *unconfigure_capability =
+ commands_new(unconfigure, "capabilities", "Capabilities configuration",
+ cmd_check_no_env, NULL, "ports");
+
+ /* Override */
+ commands_new(commands_new(commands_new(configure_capability, "enabled",
+ "Override capabilities", NULL, NULL, NULL),
+ NULL, " Set of capabilities separated by commas", NULL,
+ cmd_store_env_value, "capabilities"),
+ NEWLINE, "Override capabilities", NULL, cmd_capability, "configure");
+
+ /* Do not override */
+ commands_new(commands_new(unconfigure_capability, "enabled",
+ "Do not override capabilities", NULL, NULL, NULL),
+ NEWLINE, "Do not override capabilities", NULL, cmd_capability,
+ "unconfigure");
+}
+
+/**
+ * Register `configure system` commands.
+ *
+ * Those are the commands to configure protocol-independant stuff.
+ */
+void
+register_commands_configure_system(struct cmd_node *configure,
+ struct cmd_node *unconfigure)
+{
+ struct cmd_node *configure_system = commands_new(configure, "system",
+ "System configuration", cmd_check_no_env, NULL, "ports");
+ struct cmd_node *unconfigure_system = commands_new(unconfigure, "system",
+ "System configuration", cmd_check_no_env, NULL, "ports");
+ struct cmd_node *configure_interface = commands_new(configure_system,
+ "interface", "Interface related items", NULL, NULL, NULL);
+ struct cmd_node *unconfigure_interface = commands_new(unconfigure_system,
+ "interface", "Interface related items", NULL, NULL, NULL);
+
+ commands_new(commands_new(commands_new(configure_system, "description",
+ "Override chassis description", NULL, NULL, NULL),
+ NULL, "Chassis description", NULL, cmd_store_env_value,
+ "description"),
+ NEWLINE, "Override chassis description", NULL, cmd_system_description,
+ "system");
+ commands_new(commands_new(unconfigure_system, "description",
+ "Don't override chassis description", NULL, NULL, NULL),
+ NEWLINE, "Don't override chassis description", NULL, cmd_system_description,
+ "system");
+
+ commands_new(commands_new(commands_new(configure_system, "chassisid",
+ "Override chassis ID", NULL, NULL, NULL),
+ NULL, "Chassis ID", NULL, cmd_store_env_value, "description"),
+ NEWLINE, "Override chassis ID", NULL, cmd_system_chassisid, "system");
+ commands_new(commands_new(unconfigure_system, "chassisid",
+ "Don't override chassis ID", NULL, NULL, NULL),
+ NEWLINE, "Don't override chassis ID", NULL, cmd_system_chassisid, "system");
+
+ commands_new(commands_new(commands_new(configure_system, "platform",
+ "Override platform description", NULL, NULL,
+ NULL),
+ NULL, "Platform description (CDP)", NULL, cmd_store_env_value,
+ "platform"),
+ NEWLINE, "Override platform description", NULL, cmd_system_description,
+ "platform");
+ commands_new(commands_new(unconfigure_system, "platform",
+ "Don't override platform description", NULL, NULL, NULL),
+ NEWLINE, "Don't override platform description", NULL,
+ cmd_system_description, "platform");
+
+ commands_new(commands_new(commands_new(configure_system, "hostname",
+ "Override system name", NULL, NULL, NULL),
+ NULL, "System name", NULL, cmd_store_env_value, "hostname"),
+ NEWLINE, "Override system name", NULL, cmd_hostname, NULL);
+ commands_new(commands_new(unconfigure_system, "hostname",
+ "Don't override system name", NULL, NULL, NULL),
+ NEWLINE, "Don't override system name", NULL, cmd_hostname, NULL);
+
+ commands_new(commands_new(commands_new(configure_system, "max-neighbors",
+ "Set maximum number of neighbors per port",
+ cmd_check_no_env, NULL, "ports"),
+ NULL, "Maximum number of neighbors", NULL, cmd_store_env_value,
+ "max-neighbors"),
+ NEWLINE, "Set maximum number of neighbors per port", NULL, cmd_maxneighs,
+ NULL);
+
+ commands_new(
+ commands_new(commands_new(commands_new(commands_new(configure_system, "ip",
+ "IP related options", NULL, NULL,
+ NULL),
+ "management", "IP management related options",
+ NULL, NULL, NULL),
+ "pattern", "Set IP management pattern", NULL, NULL, NULL),
+ NULL, "IP management pattern (comma-separated list of wildcards)", NULL,
+ cmd_store_env_value, "management-pattern"),
+ NEWLINE, "Set IP management pattern", NULL, cmd_management, NULL);
+ commands_new(
+ commands_new(commands_new(commands_new(unconfigure_system, "ip",
+ "IP related options", NULL, NULL, NULL),
+ "management", "IP management related options", NULL, NULL,
+ NULL),
+ "pattern", "Delete any IP management pattern", NULL, NULL, NULL),
+ NEWLINE, "Delete any IP management pattern", NULL, cmd_management, NULL);
+
+ commands_new(commands_new(commands_new(configure_interface, "pattern",
+ "Set active interface pattern", NULL, NULL, NULL),
+ NULL, "Interface pattern (comma-separated list of wildcards)",
+ NULL, cmd_store_env_value, "iface-pattern"),
+ NEWLINE, "Set active interface pattern", NULL, cmd_iface_pattern, NULL);
+ commands_new(commands_new(unconfigure_interface, "pattern",
+ "Delete any interface pattern", NULL, NULL, NULL),
+ NEWLINE, "Clear interface pattern", NULL, cmd_iface_pattern, NULL);
+
+ commands_new(
+ commands_new(commands_new(configure_interface, "permanent",
+ "Set permanent interface pattern", NULL, NULL, NULL),
+ NULL, "Permanent interface pattern (comma-separated list of wildcards)",
+ NULL, cmd_store_env_value, "iface-pattern"),
+ NEWLINE, "Set permanent interface pattern", NULL, cmd_perm_iface_pattern,
+ NULL);
+ commands_new(commands_new(unconfigure_interface, "permanent",
+ "Clear permanent interface pattern", NULL, NULL, NULL),
+ NEWLINE, "Delete any interface pattern", NULL, cmd_perm_iface_pattern,
+ NULL);
+
+ commands_new(commands_new(configure_interface, "description",
+ "Update interface descriptions with neighbor name", NULL, NULL,
+ NULL),
+ NEWLINE, "Update interface descriptions with neighbor name", NULL,
+ cmd_update_descriptions, "enable");
+ commands_new(commands_new(unconfigure_interface, "description",
+ "Don't update interface descriptions with neighbor name", NULL,
+ NULL, NULL),
+ NEWLINE, "Don't update interface descriptions with neighbor name", NULL,
+ cmd_update_descriptions, NULL);
+
+ commands_new(commands_new(configure_interface, "promiscuous",
+ "Enable promiscuous mode on managed interfaces", NULL, NULL,
+ NULL),
+ NEWLINE, "Enable promiscuous mode on managed interfaces", NULL,
+ cmd_iface_promisc, "enable");
+ commands_new(commands_new(unconfigure_interface, "promiscuous",
+ "Don't enable promiscuous mode on managed interfaces", NULL,
+ NULL, NULL),
+ NEWLINE, "Don't enable promiscuous mode on managed interfaces", NULL,
+ cmd_iface_promisc, NULL);
+
+ register_commands_capabilities(configure_system, unconfigure_system);
+ register_commands_srcmac_type(configure_system);
+}
diff --git a/src/client/conf.c b/src/client/conf.c
new file mode 100644
index 0000000..7bbb1f9
--- /dev/null
+++ b/src/client/conf.c
@@ -0,0 +1,44 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2013 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+
+#include "client.h"
+#include "../log.h"
+
+/**
+ * Register `configure` and `no configure` commands.
+ */
+void
+register_commands_configure(struct cmd_node *root)
+{
+ struct cmd_node *configure =
+ commands_new(root, "configure", "Change system settings", NULL, NULL, NULL);
+ struct cmd_node *unconfigure = commands_new(root, "unconfigure",
+ "Unconfigure system settings", NULL, NULL, NULL);
+ commands_privileged(commands_lock(configure));
+ commands_privileged(commands_lock(unconfigure));
+ cmd_restrict_ports(configure);
+ cmd_restrict_ports(unconfigure);
+
+ register_commands_configure_system(configure, unconfigure);
+ register_commands_configure_lldp(configure, unconfigure);
+ register_commands_configure_med(configure, unconfigure);
+ register_commands_configure_inventory(configure, unconfigure);
+ register_commands_configure_dot3(configure);
+}
diff --git a/src/client/display.c b/src/client/display.c
new file mode 100644
index 0000000..6e3ec78
--- /dev/null
+++ b/src/client/display.c
@@ -0,0 +1,1039 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "../log.h"
+#include "client.h"
+
+static void
+display_cap(struct writer *w, lldpctl_atom_t *chassis, u_int8_t bit, const char *symbol)
+{
+ if (lldpctl_atom_get_int(chassis, lldpctl_k_chassis_cap_available) & bit) {
+ tag_start(w, "capability", "Capability");
+ tag_attr(w, "type", "", symbol);
+ tag_attr(w, "enabled", "",
+ (lldpctl_atom_get_int(chassis, lldpctl_k_chassis_cap_enabled) &
+ bit) ?
+ "on" :
+ "off");
+ tag_end(w);
+ }
+}
+
+static void
+display_med_capability(struct writer *w, long int available, int cap,
+ const char *symbol)
+{
+ if (available & cap) {
+ tag_start(w, "capability", "Capability");
+ tag_attr(w, "type", "", symbol);
+ tag_attr(w, "available", "", "yes");
+ tag_end(w);
+ }
+}
+
+static void
+display_med(struct writer *w, lldpctl_atom_t *port, lldpctl_atom_t *chassis)
+{
+ lldpctl_atom_t *medpolicies, *medpolicy;
+ lldpctl_atom_t *medlocations, *medlocation;
+ lldpctl_atom_t *caelements, *caelement;
+ lldpctl_atom_t *medpower;
+ long int cap = lldpctl_atom_get_int(chassis, lldpctl_k_chassis_med_cap);
+ const char *type;
+
+ if (lldpctl_atom_get_int(chassis, lldpctl_k_chassis_med_type) <= 0) return;
+
+ tag_start(w, "lldp-med", "LLDP-MED");
+
+ tag_datatag(w, "device-type", "Device Type",
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_med_type));
+
+ display_med_capability(w, cap, LLDP_MED_CAP_CAP, "Capabilities");
+ display_med_capability(w, cap, LLDP_MED_CAP_POLICY, "Policy");
+ display_med_capability(w, cap, LLDP_MED_CAP_LOCATION, "Location");
+ display_med_capability(w, cap, LLDP_MED_CAP_MDI_PSE, "MDI/PSE");
+ display_med_capability(w, cap, LLDP_MED_CAP_MDI_PD, "MDI/PD");
+ display_med_capability(w, cap, LLDP_MED_CAP_IV, "Inventory");
+
+ /* LLDP MED policies */
+ medpolicies = lldpctl_atom_get(port, lldpctl_k_port_med_policies);
+ lldpctl_atom_foreach(medpolicies, medpolicy)
+ {
+ if (lldpctl_atom_get_int(medpolicy, lldpctl_k_med_policy_type) <= 0)
+ continue;
+
+ tag_start(w, "policy", "LLDP-MED Network Policy for");
+ tag_attr(w, "apptype", "",
+ lldpctl_atom_get_str(medpolicy, lldpctl_k_med_policy_type));
+ tag_attr(w, "defined", "Defined",
+ (lldpctl_atom_get_int(medpolicy, lldpctl_k_med_policy_unknown) >
+ 0) ?
+ "no" :
+ "yes");
+
+ if (lldpctl_atom_get_int(medpolicy, lldpctl_k_med_policy_tagged) > 0) {
+ int vid =
+ lldpctl_atom_get_int(medpolicy, lldpctl_k_med_policy_vid);
+ tag_start(w, "vlan", "VLAN");
+ if (vid == 0) {
+ tag_attr(w, "vid", "", "priority");
+ } else if (vid == 4095) {
+ tag_attr(w, "vid", "", "reserved");
+ } else {
+ tag_attr(w, "vid", "",
+ lldpctl_atom_get_str(medpolicy,
+ lldpctl_k_med_policy_vid));
+ }
+ tag_end(w);
+ }
+
+ tag_datatag(w, "priority", "Priority",
+ lldpctl_atom_get_str(medpolicy, lldpctl_k_med_policy_priority));
+ /* Also give a numeric value */
+ int pcp =
+ lldpctl_atom_get_int(medpolicy, lldpctl_k_med_policy_priority);
+ char spcp[2] = { pcp + '0', '\0' };
+ tag_datatag(w, "pcp", "PCP", spcp);
+ tag_datatag(w, "dscp", "DSCP Value",
+ lldpctl_atom_get_str(medpolicy, lldpctl_k_med_policy_dscp));
+
+ tag_end(w);
+ }
+ lldpctl_atom_dec_ref(medpolicies);
+
+ /* LLDP MED locations */
+ medlocations = lldpctl_atom_get(port, lldpctl_k_port_med_locations);
+ lldpctl_atom_foreach(medlocations, medlocation)
+ {
+ int format =
+ lldpctl_atom_get_int(medlocation, lldpctl_k_med_location_format);
+ if (format <= 0) continue;
+ tag_start(w, "location", "LLDP-MED Location Identification");
+ tag_attr(w, "type", "Type",
+ lldpctl_atom_get_str(medlocation, lldpctl_k_med_location_format));
+
+ switch (format) {
+ case LLDP_MED_LOCFORMAT_COORD:
+ tag_attr(w, "geoid", "Geoid",
+ lldpctl_atom_get_str(medlocation,
+ lldpctl_k_med_location_geoid));
+ tag_datatag(w, "lat", "Latitude",
+ lldpctl_atom_get_str(medlocation,
+ lldpctl_k_med_location_latitude));
+ tag_datatag(w, "lon", "Longitude",
+ lldpctl_atom_get_str(medlocation,
+ lldpctl_k_med_location_longitude));
+ tag_start(w, "altitude", "Altitude");
+ tag_attr(w, "unit", "",
+ lldpctl_atom_get_str(medlocation,
+ lldpctl_k_med_location_altitude_unit));
+ tag_data(w,
+ lldpctl_atom_get_str(medlocation,
+ lldpctl_k_med_location_altitude));
+ tag_end(w);
+ break;
+ case LLDP_MED_LOCFORMAT_CIVIC:
+ tag_datatag(w, "country", "Country",
+ lldpctl_atom_get_str(medlocation,
+ lldpctl_k_med_location_country));
+ caelements = lldpctl_atom_get(medlocation,
+ lldpctl_k_med_location_ca_elements);
+ lldpctl_atom_foreach(caelements, caelement)
+ {
+ type = lldpctl_atom_get_str(caelement,
+ lldpctl_k_med_civicaddress_type);
+ tag_datatag(w, totag(type), type,
+ lldpctl_atom_get_str(caelement,
+ lldpctl_k_med_civicaddress_value));
+ }
+ lldpctl_atom_dec_ref(caelements);
+ break;
+ case LLDP_MED_LOCFORMAT_ELIN:
+ tag_datatag(w, "ecs", "ECS ELIN",
+ lldpctl_atom_get_str(medlocation,
+ lldpctl_k_med_location_elin));
+ break;
+ }
+
+ tag_end(w);
+ }
+ lldpctl_atom_dec_ref(medlocations);
+
+ /* LLDP MED power */
+ medpower = lldpctl_atom_get(port, lldpctl_k_port_med_power);
+ if (lldpctl_atom_get_int(medpower, lldpctl_k_med_power_type) > 0) {
+ tag_start(w, "poe", "Extended Power-over-Ethernet");
+
+ tag_datatag(w, "device-type", "Power Type & Source",
+ lldpctl_atom_get_str(medpower, lldpctl_k_med_power_type));
+ tag_datatag(w, "source", "Power Source",
+ lldpctl_atom_get_str(medpower, lldpctl_k_med_power_source));
+ tag_datatag(w, "priority", "Power priority",
+ lldpctl_atom_get_str(medpower, lldpctl_k_med_power_priority));
+ tag_datatag(w, "power", "Power Value",
+ lldpctl_atom_get_str(medpower, lldpctl_k_med_power_val));
+
+ tag_end(w);
+ }
+ lldpctl_atom_dec_ref(medpower);
+
+ /* LLDP MED inventory */
+ do {
+ const char *hw =
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_med_inventory_hw);
+ const char *sw =
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_med_inventory_sw);
+ const char *fw =
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_med_inventory_fw);
+ const char *sn =
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_med_inventory_sn);
+ const char *manuf = lldpctl_atom_get_str(chassis,
+ lldpctl_k_chassis_med_inventory_manuf);
+ const char *model = lldpctl_atom_get_str(chassis,
+ lldpctl_k_chassis_med_inventory_model);
+ const char *asset = lldpctl_atom_get_str(chassis,
+ lldpctl_k_chassis_med_inventory_asset);
+ if (!(hw || sw || fw || sn || manuf || model || asset)) break;
+
+ tag_start(w, "inventory", "Inventory");
+ tag_datatag(w, "hardware", "Hardware Revision", hw);
+ tag_datatag(w, "software", "Software Revision", sw);
+ tag_datatag(w, "firmware", "Firmware Revision", fw);
+ tag_datatag(w, "serial", "Serial Number", sn);
+ tag_datatag(w, "manufacturer", "Manufacturer", manuf);
+ tag_datatag(w, "model", "Model", model);
+ tag_datatag(w, "asset", "Asset ID", asset);
+ tag_end(w);
+ } while (0);
+
+ tag_end(w);
+}
+
+static void
+display_chassis(struct writer *w, lldpctl_atom_t *chassis, int details)
+{
+ lldpctl_atom_t *mgmts, *mgmt;
+
+ tag_start(w, "chassis", "Chassis");
+ tag_start(w, "id", "ChassisID");
+ tag_attr(w, "type", "",
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_id_subtype));
+ tag_data(w, lldpctl_atom_get_str(chassis, lldpctl_k_chassis_id));
+ tag_end(w);
+ tag_datatag(w, "name", "SysName",
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_name));
+ if (details == DISPLAY_BRIEF) {
+ tag_end(w);
+ return;
+ }
+ tag_datatag(w, "descr", "SysDescr",
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_descr));
+
+ /* Management addresses */
+ mgmts = lldpctl_atom_get(chassis, lldpctl_k_chassis_mgmt);
+ lldpctl_atom_foreach(mgmts, mgmt)
+ {
+ tag_datatag(w, "mgmt-ip", "MgmtIP",
+ lldpctl_atom_get_str(mgmt, lldpctl_k_mgmt_ip));
+ if (lldpctl_atom_get_int(mgmt, lldpctl_k_mgmt_iface_index))
+ tag_datatag(w, "mgmt-iface", "MgmtIface",
+ lldpctl_atom_get_str(mgmt, lldpctl_k_mgmt_iface_index));
+ }
+ lldpctl_atom_dec_ref(mgmts);
+
+ /* Capabilities */
+ display_cap(w, chassis, LLDP_CAP_OTHER, "Other");
+ display_cap(w, chassis, LLDP_CAP_REPEATER, "Repeater");
+ display_cap(w, chassis, LLDP_CAP_BRIDGE, "Bridge");
+ display_cap(w, chassis, LLDP_CAP_ROUTER, "Router");
+ display_cap(w, chassis, LLDP_CAP_WLAN, "Wlan");
+ display_cap(w, chassis, LLDP_CAP_TELEPHONE, "Tel");
+ display_cap(w, chassis, LLDP_CAP_DOCSIS, "Docsis");
+ display_cap(w, chassis, LLDP_CAP_STATION, "Station");
+
+ tag_end(w);
+}
+
+static void
+display_custom_tlvs(struct writer *w, lldpctl_atom_t *neighbor)
+{
+ lldpctl_atom_t *custom_list, *custom;
+ int have_custom_tlvs = 0;
+ size_t i, len, slen;
+ const uint8_t *oui, *oui_info;
+ char buf[1600]; /* should be enough for printing */
+
+ custom_list = lldpctl_atom_get(neighbor, lldpctl_k_custom_tlvs);
+ lldpctl_atom_foreach(custom_list, custom)
+ {
+ /* This tag gets added only once, if there are any custom TLVs */
+ if (!have_custom_tlvs) {
+ tag_start(w, "unknown-tlvs", "Unknown TLVs");
+ have_custom_tlvs++;
+ }
+ len = 0;
+ oui = lldpctl_atom_get_buffer(custom, lldpctl_k_custom_tlv_oui, &len);
+ len = 0;
+ oui_info = lldpctl_atom_get_buffer(custom,
+ lldpctl_k_custom_tlv_oui_info_string, &len);
+ if (!oui) continue;
+ tag_start(w, "unknown-tlv", "TLV");
+
+ /* Add OUI as attribute */
+ snprintf(buf, sizeof(buf), "%02X,%02X,%02X", oui[0], oui[1], oui[2]);
+ tag_attr(w, "oui", "OUI", buf);
+ snprintf(buf, sizeof(buf), "%d",
+ (int)lldpctl_atom_get_int(custom,
+ lldpctl_k_custom_tlv_oui_subtype));
+ tag_attr(w, "subtype", "SubType", buf);
+ snprintf(buf, sizeof(buf), "%d", (int)len);
+ tag_attr(w, "len", "Len", buf);
+ if (len > 0) {
+ for (slen = 0, i = 0; i < len; ++i)
+ slen += snprintf(buf + slen,
+ sizeof(buf) > slen ? sizeof(buf) - slen : 0,
+ "%02X%s", oui_info[i], ((i < len - 1) ? "," : ""));
+ tag_data(w, buf);
+ }
+ tag_end(w);
+ }
+ lldpctl_atom_dec_ref(custom_list);
+
+ if (have_custom_tlvs) tag_end(w);
+}
+
+static void
+display_autoneg(struct writer *w, int advertised, int bithd, int bitfd,
+ const char *desc)
+{
+ if (!((advertised & bithd) || (advertised & bitfd))) return;
+
+ tag_start(w, "advertised", "Adv");
+ tag_attr(w, "type", "", desc);
+ if (bitfd != bithd) {
+ tag_attr(w, "hd", "HD", (advertised & bithd) ? "yes" : "no");
+ tag_attr(w, "fd", "FD", (advertised & bitfd) ? "yes" : "no");
+ }
+ tag_end(w);
+}
+
+static void
+display_port(struct writer *w, lldpctl_atom_t *port, int details)
+{
+ int vlan_tx_tag;
+ char buf[5]; /* should be enough for printing */
+
+ tag_start(w, "port", "Port");
+ tag_start(w, "id", "PortID");
+ tag_attr(w, "type", "", lldpctl_atom_get_str(port, lldpctl_k_port_id_subtype));
+ tag_data(w, lldpctl_atom_get_str(port, lldpctl_k_port_id));
+ tag_end(w);
+
+ tag_datatag(w, "descr", "PortDescr",
+ lldpctl_atom_get_str(port, lldpctl_k_port_descr));
+
+ if ((vlan_tx_tag = lldpctl_atom_get_int(port, lldpctl_k_port_vlan_tx)) != -1) {
+ tag_start(w, "vlanTX", "VlanTX");
+ snprintf(buf, sizeof(buf), "%d", vlan_tx_tag & 0xfff);
+ tag_attr(w, "id", "VID", buf);
+ snprintf(buf, sizeof(buf), "%d", (vlan_tx_tag >> 13) & 0x7);
+ tag_attr(w, "prio", "Prio", buf);
+ snprintf(buf, sizeof(buf), "%d", (vlan_tx_tag >> 12) & 0x1);
+ tag_attr(w, "dei", "DEI", buf);
+ tag_end(w);
+ }
+
+ if (details && lldpctl_atom_get_int(port, lldpctl_k_port_ttl) > 0)
+ tag_datatag(w, "ttl", "TTL",
+ lldpctl_atom_get_str(port, lldpctl_k_port_ttl));
+
+ /* Dot3 */
+ if (details == DISPLAY_DETAILS) {
+ tag_datatag(w, "mfs", "MFS",
+ lldpctl_atom_get_str(port, lldpctl_k_port_dot3_mfs));
+ tag_datatag(w, "aggregation", "Port is aggregated. PortAggregID",
+ lldpctl_atom_get_str(port, lldpctl_k_port_dot3_aggregid));
+
+ long int autoneg_support, autoneg_enabled, autoneg_advertised, mautype;
+ autoneg_support =
+ lldpctl_atom_get_int(port, lldpctl_k_port_dot3_autoneg_support);
+ autoneg_enabled =
+ lldpctl_atom_get_int(port, lldpctl_k_port_dot3_autoneg_enabled);
+ autoneg_advertised =
+ lldpctl_atom_get_int(port, lldpctl_k_port_dot3_autoneg_advertised);
+ mautype = lldpctl_atom_get_int(port, lldpctl_k_port_dot3_mautype);
+ if (autoneg_support > 0 || autoneg_enabled > 0 || mautype > 0) {
+ tag_start(w, "auto-negotiation", "PMD autoneg");
+ tag_attr(w, "supported", "supported",
+ (autoneg_support > 0) ? "yes" : "no");
+ tag_attr(w, "enabled", "enabled",
+ (autoneg_enabled > 0) ? "yes" : "no");
+
+ if (autoneg_enabled > 0) {
+ if (autoneg_advertised < 0) autoneg_advertised = 0;
+ display_autoneg(w, autoneg_advertised,
+ LLDP_DOT3_LINK_AUTONEG_10BASE_T,
+ LLDP_DOT3_LINK_AUTONEG_10BASET_FD, "10Base-T");
+ display_autoneg(w, autoneg_advertised,
+ LLDP_DOT3_LINK_AUTONEG_100BASE_TX,
+ LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD, "100Base-TX");
+ display_autoneg(w, autoneg_advertised,
+ LLDP_DOT3_LINK_AUTONEG_100BASE_T2,
+ LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD, "100Base-T2");
+ display_autoneg(w, autoneg_advertised,
+ LLDP_DOT3_LINK_AUTONEG_100BASE_T4,
+ LLDP_DOT3_LINK_AUTONEG_100BASE_T4, "100Base-T4");
+ display_autoneg(w, autoneg_advertised,
+ LLDP_DOT3_LINK_AUTONEG_1000BASE_X,
+ LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD, "1000Base-X");
+ display_autoneg(w, autoneg_advertised,
+ LLDP_DOT3_LINK_AUTONEG_1000BASE_T,
+ LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD, "1000Base-T");
+ }
+ tag_datatag(w, "current", "MAU oper type",
+ lldpctl_atom_get_str(port, lldpctl_k_port_dot3_mautype));
+ tag_end(w);
+ }
+
+ lldpctl_atom_t *dot3_power =
+ lldpctl_atom_get(port, lldpctl_k_port_dot3_power);
+ int devicetype =
+ lldpctl_atom_get_int(dot3_power, lldpctl_k_dot3_power_devicetype);
+ if (devicetype > 0) {
+ tag_start(w, "power", "MDI Power");
+ tag_attr(w, "supported", "Supported",
+ (lldpctl_atom_get_int(dot3_power,
+ lldpctl_k_dot3_power_supported) > 0) ?
+ "yes" :
+ "no");
+ tag_attr(w, "enabled", "Enabled",
+ (lldpctl_atom_get_int(dot3_power,
+ lldpctl_k_dot3_power_enabled) > 0) ?
+ "yes" :
+ "no");
+ tag_attr(w, "paircontrol", "Pair control",
+ (lldpctl_atom_get_int(dot3_power,
+ lldpctl_k_dot3_power_paircontrol) > 0) ?
+ "yes" :
+ "no");
+ tag_start(w, "device-type", "Device type");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_devicetype));
+ ;
+ tag_end(w);
+ tag_start(w, "pairs", "Power pairs");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_pairs));
+ tag_end(w);
+ tag_start(w, "class", "Class");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_class));
+ tag_end(w);
+
+ /* 802.3at */
+ if (lldpctl_atom_get_int(dot3_power,
+ lldpctl_k_dot3_power_type) >
+ LLDP_DOT3_POWER_8023AT_OFF) {
+ tag_start(w, "power-type", "Power type");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_type));
+ tag_end(w);
+
+ tag_start(w, "source", "Power Source");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_source));
+ tag_end(w);
+
+ tag_start(w, "priority", "Power Priority");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_priority));
+ tag_end(w);
+
+ tag_start(w, "requested", "PD requested power Value");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_requested));
+ tag_end(w);
+
+ tag_start(w, "allocated", "PSE allocated power Value");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_allocated));
+ tag_end(w);
+ }
+
+ /* 802.3bt */
+ if (lldpctl_atom_get_int(dot3_power,
+ lldpctl_k_dot3_power_type_ext) >
+ LLDP_DOT3_POWER_8023BT_OFF) {
+ tag_start(w, "requested-a", "Requested mode A");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_requested_a));
+ tag_end(w);
+ tag_start(w, "requested-b", "Requested mode B");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_requested_b));
+ tag_end(w);
+ tag_start(w, "allocated-a", "Allocated alternative A");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_allocated_a));
+ tag_end(w);
+ tag_start(w, "allocated-b", "Allocated alternative B");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_allocated_b));
+ tag_end(w);
+ tag_start(w, "pse-powering-status",
+ "PSE powering status");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_pse_status));
+ tag_end(w);
+ tag_start(w, "pd-powering-status",
+ "PD powering status");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_pd_status));
+ tag_end(w);
+ tag_start(w, "power-pairs-ext", "Power pairs extra");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_pse_pairs_ext));
+ tag_end(w);
+ tag_start(w, "power-class-ext-a", "Class extra A");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_class_a));
+ tag_end(w);
+ tag_start(w, "power-class-ext-b", "Class extra B");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_class_b));
+ tag_end(w);
+ tag_start(w, "power-class-ext", "Class extra");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_class_ext));
+ tag_end(w);
+ tag_start(w, "power-type-ext", "Power type extra");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_type_ext));
+ tag_end(w);
+ tag_start(w, "pd-load", "PD load");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_pd_load));
+ tag_end(w);
+ tag_start(w, "max-power",
+ "PSE maximum available power");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_pse_max));
+ tag_end(w);
+ }
+
+ tag_end(w);
+ }
+ lldpctl_atom_dec_ref(dot3_power);
+ }
+
+ tag_end(w);
+}
+
+static void
+display_local_ttl(struct writer *w, lldpctl_conn_t *conn, int details)
+{
+ char *ttl;
+ long int tx_hold;
+ long int tx_interval;
+
+ lldpctl_atom_t *configuration;
+ configuration = lldpctl_get_configuration(conn);
+ if (!configuration) {
+ log_warnx("lldpctl", "not able to get configuration. %s",
+ lldpctl_last_strerror(conn));
+ return;
+ }
+
+ tx_hold = lldpctl_atom_get_int(configuration, lldpctl_k_config_tx_hold);
+ tx_interval =
+ lldpctl_atom_get_int(configuration, lldpctl_k_config_tx_interval_ms);
+
+ tx_interval = (tx_interval * tx_hold + 999) / 1000;
+
+ if (asprintf(&ttl, "%lu", tx_interval) == -1) {
+ log_warnx("lldpctl", "not enough memory to build TTL.");
+ goto end;
+ }
+
+ tag_start(w, "ttl", "TTL");
+ tag_attr(w, "ttl", "", ttl);
+ tag_end(w);
+ free(ttl);
+end:
+ lldpctl_atom_dec_ref(configuration);
+}
+
+static void
+display_vlans(struct writer *w, lldpctl_atom_t *port)
+{
+ lldpctl_atom_t *vlans, *vlan;
+ int foundpvid = 0;
+ int pvid, vid;
+
+ pvid = lldpctl_atom_get_int(port, lldpctl_k_port_vlan_pvid);
+
+ vlans = lldpctl_atom_get(port, lldpctl_k_port_vlans);
+ lldpctl_atom_foreach(vlans, vlan)
+ {
+ vid = lldpctl_atom_get_int(vlan, lldpctl_k_vlan_id);
+
+ tag_start(w, "vlan", "VLAN");
+ tag_attr(w, "vlan-id", "",
+ lldpctl_atom_get_str(vlan, lldpctl_k_vlan_id));
+ if (pvid == vid) {
+ tag_attr(w, "pvid", "pvid", "yes");
+ foundpvid = 1;
+ } else {
+ tag_attr(w, "pvid", "pvid", "no");
+ }
+ tag_data(w, lldpctl_atom_get_str(vlan, lldpctl_k_vlan_name));
+ tag_end(w);
+ }
+ lldpctl_atom_dec_ref(vlans);
+
+ if (!foundpvid && pvid > 0) {
+ tag_start(w, "vlan", "VLAN");
+ tag_attr(w, "vlan-id", "",
+ lldpctl_atom_get_str(port, lldpctl_k_port_vlan_pvid));
+ tag_attr(w, "pvid", "pvid", "yes");
+ tag_end(w);
+ }
+}
+
+static void
+display_ppvids(struct writer *w, lldpctl_atom_t *port)
+{
+ lldpctl_atom_t *ppvids, *ppvid;
+ ppvids = lldpctl_atom_get(port, lldpctl_k_port_ppvids);
+ lldpctl_atom_foreach(ppvids, ppvid)
+ {
+ int status = lldpctl_atom_get_int(ppvid, lldpctl_k_ppvid_status);
+ tag_start(w, "ppvid", "PPVID");
+ if (lldpctl_atom_get_int(ppvid, lldpctl_k_ppvid_id) > 0)
+ tag_attr(w, "value", "",
+ lldpctl_atom_get_str(ppvid, lldpctl_k_ppvid_id));
+ tag_attr(w, "supported", "supported",
+ (status & LLDP_PPVID_CAP_SUPPORTED) ? "yes" : "no");
+ tag_attr(w, "enabled", "enabled",
+ (status & LLDP_PPVID_CAP_ENABLED) ? "yes" : "no");
+ tag_end(w);
+ }
+ lldpctl_atom_dec_ref(ppvids);
+}
+
+static void
+display_pids(struct writer *w, lldpctl_atom_t *port)
+{
+ lldpctl_atom_t *pids, *pid;
+ pids = lldpctl_atom_get(port, lldpctl_k_port_pis);
+ lldpctl_atom_foreach(pids, pid)
+ {
+ const char *pi = lldpctl_atom_get_str(pid, lldpctl_k_pi_id);
+ if (pi && strlen(pi) > 0) tag_datatag(w, "pi", "PI", pi);
+ }
+ lldpctl_atom_dec_ref(pids);
+}
+
+static const char *
+display_age(time_t lastchange)
+{
+ static char sage[30];
+ int age = (int)(time(NULL) - lastchange);
+ if (snprintf(sage, sizeof(sage), "%d day%s, %02d:%02d:%02d",
+ age / (60 * 60 * 24), (age / (60 * 60 * 24) > 1) ? "s" : "",
+ (age / (60 * 60)) % 24, (age / 60) % 60, age % 60) >= sizeof(sage))
+ return "too much";
+ else
+ return sage;
+}
+
+void
+display_local_chassis(lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ int details)
+{
+ tag_start(w, "local-chassis", "Local chassis");
+
+ lldpctl_atom_t *chassis = lldpctl_get_local_chassis(conn);
+ display_chassis(w, chassis, details);
+ if (details == DISPLAY_DETAILS) {
+ display_med(w, NULL, chassis);
+ }
+ lldpctl_atom_dec_ref(chassis);
+
+ tag_end(w);
+}
+
+void
+display_interface(lldpctl_conn_t *conn, struct writer *w, int hidden,
+ lldpctl_atom_t *iface, lldpctl_atom_t *port, int details, int protocol)
+{
+ int local = 0;
+
+ if (!hidden && lldpctl_atom_get_int(port, lldpctl_k_port_hidden)) return;
+
+ /* user might have specified protocol to filter on display */
+ if ((protocol != LLDPD_MODE_MAX) &&
+ (protocol != lldpctl_atom_get_int(port, lldpctl_k_port_protocol)))
+ return;
+
+ /* Infer local / remote port from the port index (remote == 0) */
+ local = lldpctl_atom_get_int(port, lldpctl_k_port_index) > 0 ? 1 : 0;
+
+ lldpctl_atom_t *chassis = lldpctl_atom_get(port, lldpctl_k_port_chassis);
+
+ tag_start(w, "interface", "Interface");
+ tag_attr(w, "name", "", lldpctl_atom_get_str(iface, lldpctl_k_interface_name));
+ if (!local) {
+ tag_attr(w, "via", "via",
+ lldpctl_atom_get_str(port, lldpctl_k_port_protocol));
+ if (details > DISPLAY_BRIEF) {
+ tag_attr(w, "rid", "RID",
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_index));
+ tag_attr(w, "age", "Time",
+ display_age(
+ lldpctl_atom_get_int(port, lldpctl_k_port_age)));
+ }
+ } else {
+ tag_datatag(w, "status", "Administrative status",
+ lldpctl_atom_get_str(port, lldpctl_k_port_status));
+ }
+
+ display_chassis(w, chassis, details);
+ display_port(w, port, details);
+ if (details && local && conn) display_local_ttl(w, conn, details);
+ if (details == DISPLAY_DETAILS) {
+ display_vlans(w, port);
+ display_ppvids(w, port);
+ display_pids(w, port);
+ display_med(w, port, chassis);
+ }
+
+ lldpctl_atom_dec_ref(chassis);
+
+ display_custom_tlvs(w, port);
+
+ tag_end(w);
+}
+
+/**
+ * Display information about interfaces.
+ *
+ * @param conn Connection to lldpd.
+ * @param w Writer.
+ * @param env Environment from which we may find the list of ports.
+ * @param hidden Whatever to show hidden ports.
+ * @param details Level of details we need (DISPLAY_*).
+ */
+void
+display_interfaces(lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ int hidden, int details)
+{
+ lldpctl_atom_t *iface;
+ int protocol = LLDPD_MODE_MAX;
+ const char *proto_str;
+
+ /* user might have specified protocol to filter display results */
+ proto_str = cmdenv_get(env, "protocol");
+
+ if (proto_str) {
+ log_debug("display", "filter protocol: %s ", proto_str);
+
+ protocol = 0;
+ for (lldpctl_map_t *protocol_map =
+ lldpctl_key_get_map(lldpctl_k_port_protocol);
+ protocol_map->string; protocol_map++) {
+ if (!strcasecmp(proto_str, protocol_map->string)) {
+ protocol = protocol_map->value;
+ break;
+ }
+ }
+ }
+
+ tag_start(w, "lldp", "LLDP neighbors");
+ while ((iface = cmd_iterate_on_interfaces(conn, env))) {
+ lldpctl_atom_t *port;
+ lldpctl_atom_t *neighbors;
+ lldpctl_atom_t *neighbor;
+ port = lldpctl_get_port(iface);
+ neighbors = lldpctl_atom_get(port, lldpctl_k_port_neighbors);
+ lldpctl_atom_foreach(neighbors, neighbor)
+ {
+ display_interface(conn, w, hidden, iface, neighbor, details,
+ protocol);
+ }
+ lldpctl_atom_dec_ref(neighbors);
+ lldpctl_atom_dec_ref(port);
+ }
+ tag_end(w);
+}
+
+/**
+ * Display information about local interfaces.
+ *
+ * @param conn Connection to lldpd.
+ * @param w Writer.
+ * @param hidden Whatever to show hidden ports.
+ * @param env Environment from which we may find the list of ports.
+ * @param details Level of details we need (DISPLAY_*).
+ */
+void
+display_local_interfaces(lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ int hidden, int details)
+{
+ lldpctl_atom_t *iface;
+ int protocol = LLDPD_MODE_MAX;
+
+ tag_start(w, "lldp", "LLDP interfaces");
+ while ((iface = cmd_iterate_on_interfaces(conn, env))) {
+ lldpctl_atom_t *port;
+ port = lldpctl_get_port(iface);
+ display_interface(conn, w, hidden, iface, port, details, protocol);
+ lldpctl_atom_dec_ref(port);
+ }
+ tag_end(w);
+}
+
+static void
+display_stat(struct writer *w, const char *tag, const char *descr,
+ long unsigned int cnt)
+{
+ char buf[20] = {};
+
+ tag_start(w, tag, descr);
+ snprintf(buf, sizeof(buf), "%lu", cnt);
+ tag_attr(w, tag, "", buf);
+ tag_end(w);
+}
+
+void
+display_interface_stats(lldpctl_conn_t *conn, struct writer *w, lldpctl_atom_t *port)
+{
+ tag_start(w, "interface", "Interface");
+ tag_attr(w, "name", "", lldpctl_atom_get_str(port, lldpctl_k_port_name));
+
+ display_stat(w, "tx", "Transmitted",
+ lldpctl_atom_get_int(port, lldpctl_k_tx_cnt));
+ display_stat(w, "rx", "Received", lldpctl_atom_get_int(port, lldpctl_k_rx_cnt));
+
+ display_stat(w, "rx_discarded_cnt", "Discarded",
+ lldpctl_atom_get_int(port, lldpctl_k_rx_discarded_cnt));
+
+ display_stat(w, "rx_unrecognized_cnt", "Unrecognized",
+ lldpctl_atom_get_int(port, lldpctl_k_rx_unrecognized_cnt));
+
+ display_stat(w, "ageout_cnt", "Ageout",
+ lldpctl_atom_get_int(port, lldpctl_k_ageout_cnt));
+
+ display_stat(w, "insert_cnt", "Inserted",
+ lldpctl_atom_get_int(port, lldpctl_k_insert_cnt));
+
+ display_stat(w, "delete_cnt", "Deleted",
+ lldpctl_atom_get_int(port, lldpctl_k_delete_cnt));
+
+ tag_end(w);
+}
+
+/**
+ * Display interface stats
+ *
+ * @param conn Connection to lldpd.
+ * @param w Writer.
+ * @param env Environment from which we may find the list of ports.
+ */
+void
+display_interfaces_stats(lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env)
+{
+ lldpctl_atom_t *iface;
+ int summary = 0;
+ u_int64_t h_tx_cnt = 0;
+ u_int64_t h_rx_cnt = 0;
+ u_int64_t h_rx_discarded_cnt = 0;
+ u_int64_t h_rx_unrecognized_cnt = 0;
+ u_int64_t h_ageout_cnt = 0;
+ u_int64_t h_insert_cnt = 0;
+ u_int64_t h_delete_cnt = 0;
+
+ if (cmdenv_get(env, "summary")) summary = 1;
+
+ tag_start(w, "lldp", (summary ? "LLDP Global statistics" : "LLDP statistics"));
+ while ((iface = cmd_iterate_on_interfaces(conn, env))) {
+ lldpctl_atom_t *port;
+ port = lldpctl_get_port(iface);
+ if (!summary)
+ display_interface_stats(conn, w, port);
+ else {
+ h_tx_cnt += lldpctl_atom_get_int(port, lldpctl_k_tx_cnt);
+ h_rx_cnt += lldpctl_atom_get_int(port, lldpctl_k_rx_cnt);
+ h_rx_discarded_cnt +=
+ lldpctl_atom_get_int(port, lldpctl_k_rx_discarded_cnt);
+ h_rx_unrecognized_cnt +=
+ lldpctl_atom_get_int(port, lldpctl_k_rx_unrecognized_cnt);
+ h_ageout_cnt +=
+ lldpctl_atom_get_int(port, lldpctl_k_ageout_cnt);
+ h_insert_cnt +=
+ lldpctl_atom_get_int(port, lldpctl_k_insert_cnt);
+ h_delete_cnt +=
+ lldpctl_atom_get_int(port, lldpctl_k_delete_cnt);
+ }
+ lldpctl_atom_dec_ref(port);
+ }
+
+ if (summary) {
+ tag_start(w, "summary", "Summary of stats");
+ display_stat(w, "tx", "Transmitted", h_tx_cnt);
+ display_stat(w, "rx", "Received", h_rx_cnt);
+ display_stat(w, "rx_discarded_cnt", "Discarded", h_rx_discarded_cnt);
+
+ display_stat(w, "rx_unrecognized_cnt", "Unrecognized",
+ h_rx_unrecognized_cnt);
+
+ display_stat(w, "ageout_cnt", "Ageout", h_ageout_cnt);
+
+ display_stat(w, "insert_cnt", "Inserted", h_insert_cnt);
+
+ display_stat(w, "delete_cnt", "Deleted", h_delete_cnt);
+ tag_end(w);
+ }
+ tag_end(w);
+}
+
+static const char *
+N(const char *str)
+{
+ if (str == NULL || strlen(str) == 0) return "(none)";
+ return str;
+}
+
+void
+display_configuration(lldpctl_conn_t *conn, struct writer *w)
+{
+ lldpctl_atom_t *configuration;
+
+ configuration = lldpctl_get_configuration(conn);
+ if (!configuration) {
+ log_warnx("lldpctl", "not able to get configuration. %s",
+ lldpctl_last_strerror(conn));
+ return;
+ }
+
+ tag_start(w, "configuration", "Global configuration");
+ tag_start(w, "config", "Configuration");
+
+ tag_datatag(w, "tx-delay", "Transmit delay",
+ lldpctl_atom_get_str(configuration, lldpctl_k_config_tx_interval));
+ tag_datatag(w, "tx-delay-ms", "Transmit delay in milliseconds",
+ lldpctl_atom_get_str(configuration, lldpctl_k_config_tx_interval_ms));
+ tag_datatag(w, "tx-hold", "Transmit hold",
+ lldpctl_atom_get_str(configuration, lldpctl_k_config_tx_hold));
+ tag_datatag(w, "max-neighbors", "Maximum number of neighbors",
+ lldpctl_atom_get_str(configuration, lldpctl_k_config_max_neighbors));
+ tag_datatag(w, "rx-only", "Receive mode",
+ lldpctl_atom_get_int(configuration, lldpctl_k_config_receiveonly) ? "yes" :
+ "no");
+ tag_datatag(w, "mgmt-pattern", "Pattern for management addresses",
+ N(lldpctl_atom_get_str(configuration, lldpctl_k_config_mgmt_pattern)));
+ tag_datatag(w, "iface-pattern", "Interface pattern",
+ N(lldpctl_atom_get_str(configuration, lldpctl_k_config_iface_pattern)));
+ tag_datatag(w, "perm-iface-pattern", "Permanent interface pattern",
+ N(lldpctl_atom_get_str(configuration,
+ lldpctl_k_config_perm_iface_pattern)));
+ tag_datatag(w, "cid-pattern", "Interface pattern for chassis ID",
+ N(lldpctl_atom_get_str(configuration, lldpctl_k_config_cid_pattern)));
+ tag_datatag(w, "cid-string", "Override chassis ID with",
+ N(lldpctl_atom_get_str(configuration, lldpctl_k_config_cid_string)));
+ tag_datatag(w, "description", "Override description with",
+ N(lldpctl_atom_get_str(configuration, lldpctl_k_config_description)));
+ tag_datatag(w, "platform", "Override platform with",
+ N(lldpctl_atom_get_str(configuration, lldpctl_k_config_platform)));
+ tag_datatag(w, "hostname", "Override system name with",
+ N(lldpctl_atom_get_str(configuration, lldpctl_k_config_hostname)));
+ tag_datatag(w, "capabilities", "Override system capabilities",
+ lldpctl_atom_get_int(configuration, lldpctl_k_config_chassis_cap_override) ?
+ "yes" :
+ "no");
+ tag_datatag(w, "advertise-version", "Advertise version",
+ lldpctl_atom_get_int(configuration, lldpctl_k_config_advertise_version) ?
+ "yes" :
+ "no");
+ tag_datatag(w, "ifdescr-update", "Update interface descriptions",
+ lldpctl_atom_get_int(configuration, lldpctl_k_config_ifdescr_update) ?
+ "yes" :
+ "no");
+ tag_datatag(w, "iface-promisc", "Promiscuous mode on managed interfaces",
+ lldpctl_atom_get_int(configuration, lldpctl_k_config_iface_promisc) ?
+ "yes" :
+ "no");
+ tag_datatag(w, "lldpmed-no-inventory", "Disable LLDP-MED inventory",
+ (lldpctl_atom_get_int(configuration,
+ lldpctl_k_config_lldpmed_noinventory) == 0) ?
+ "no" :
+ "yes");
+ tag_datatag(w, "lldpmed-faststart", "LLDP-MED fast start mechanism",
+ (lldpctl_atom_get_int(configuration, lldpctl_k_config_fast_start_enabled) ==
+ 0) ?
+ "no" :
+ "yes");
+ tag_datatag(w, "lldpmed-faststart-interval", "LLDP-MED fast start interval",
+ N(lldpctl_atom_get_str(configuration,
+ lldpctl_k_config_fast_start_interval)));
+ tag_datatag(w, "bond-slave-src-mac-type",
+ "Source MAC for LLDP frames on bond slaves",
+ lldpctl_atom_get_str(configuration,
+ lldpctl_k_config_bond_slave_src_mac_type));
+ tag_datatag(w, "lldp-portid-type", "Port ID TLV subtype for LLDP frames",
+ lldpctl_atom_get_str(configuration, lldpctl_k_config_lldp_portid_type));
+ tag_datatag(w, "lldp-agent-type", "Agent type",
+ lldpctl_atom_get_str(configuration, lldpctl_k_config_lldp_agent_type));
+
+ tag_end(w);
+ tag_end(w);
+
+ lldpctl_atom_dec_ref(configuration);
+}
diff --git a/src/client/json_writer.c b/src/client/json_writer.c
new file mode 100644
index 0000000..dc6665a
--- /dev/null
+++ b/src/client/json_writer.c
@@ -0,0 +1,374 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2017 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include "writer.h"
+#include "../compat/compat.h"
+#include "../log.h"
+
+enum tag { STRING, BOOL, ARRAY, OBJECT };
+
+struct element {
+ struct element *parent; /* Parent (if any) */
+ TAILQ_ENTRY(element) next; /* Sibling (if any) */
+ char *key; /* Key if parent is an object */
+ enum tag tag; /* Kind of element */
+ union {
+ char *string; /* STRING */
+ int boolean; /* BOOL */
+ TAILQ_HEAD(, element) children; /* ARRAY or OBJECT */
+ };
+};
+
+struct json_writer_private {
+ FILE *fh;
+ int variant;
+ struct element *root;
+ struct element *current; /* should always be an object */
+};
+
+/* Create a new element. If a parent is provided, it will also be attached to
+ * the parent. */
+static struct element *
+json_element_new(struct element *parent, const char *key, enum tag tag)
+{
+ struct element *child = malloc(sizeof(*child));
+ if (child == NULL) fatal(NULL, NULL);
+ child->parent = parent;
+ child->key = key ? strdup(key) : NULL;
+ child->tag = tag;
+ TAILQ_INIT(&child->children);
+ if (parent) TAILQ_INSERT_TAIL(&parent->children, child, next);
+ return child;
+}
+
+/* Free the element content (but not the element itself) */
+static void
+json_element_free(struct element *current)
+{
+ struct element *el, *el_next;
+ switch (current->tag) {
+ case STRING:
+ free(current->string);
+ break;
+ case BOOL:
+ break;
+ case ARRAY:
+ case OBJECT:
+ for (el = TAILQ_FIRST(&current->children); el != NULL; el = el_next) {
+ el_next = TAILQ_NEXT(el, next);
+ json_element_free(el);
+ TAILQ_REMOVE(&current->children, el, next);
+ if (current->tag == OBJECT) free(el->key);
+ free(el);
+ }
+ break;
+ }
+}
+
+static void
+json_free(struct json_writer_private *p)
+{
+ json_element_free(p->root);
+ free(p->root);
+}
+
+static void
+json_string_dump(FILE *fh, const char *s)
+{
+ fprintf(fh, "\"");
+ while (*s != '\0') {
+ unsigned int c = *s;
+ size_t len;
+ switch (c) {
+ case '"':
+ fprintf(fh, "\\\"");
+ s++;
+ break;
+ case '\\':
+ fprintf(fh, "\\\\");
+ s++;
+ break;
+ case '\b':
+ fprintf(fh, "\\b");
+ s++;
+ break;
+ case '\f':
+ fprintf(fh, "\\f");
+ s++;
+ break;
+ case '\n':
+ fprintf(fh, "\\n");
+ s++;
+ break;
+ case '\r':
+ fprintf(fh, "\\r");
+ s++;
+ break;
+ case '\t':
+ fprintf(fh, "\\t");
+ s++;
+ break;
+ default:
+ len = utf8_validate_cz(s);
+ if (len == 0) {
+ /* Not a valid UTF-8 char, use a
+ * replacement character */
+ fprintf(fh, "\\uFFFD");
+ s++;
+ } else if (c < 0x1f) {
+ /* 7-bit ASCII character */
+ fprintf(fh, "\\u%04X", c);
+ s++;
+ } else {
+ /* UTF-8, write as is */
+ while (len--)
+ fprintf(fh, "%c", *s++);
+ }
+ break;
+ }
+ }
+ fprintf(fh, "\"");
+}
+
+/* Dump an element to the specified file handle. */
+static void
+json_element_dump(FILE *fh, struct element *current, int indent)
+{
+ static const char pairs[2][2] = { "{}", "[]" };
+ struct element *el;
+ switch (current->tag) {
+ case STRING:
+ json_string_dump(fh, current->string);
+ break;
+ case BOOL:
+ fprintf(fh, current->boolean ? "true" : "false");
+ break;
+ case ARRAY:
+ case OBJECT:
+ fprintf(fh, "%c\n%*s", pairs[(current->tag == ARRAY)][0], indent + 2,
+ "");
+ TAILQ_FOREACH (el, &current->children, next) {
+ if (current->tag == OBJECT) fprintf(fh, "\"%s\": ", el->key);
+ json_element_dump(fh, el, indent + 2);
+ if (TAILQ_NEXT(el, next)) fprintf(fh, ",\n%*s", indent + 2, "");
+ }
+ fprintf(fh, "\n%*c", indent + 1, pairs[(current->tag == ARRAY)][1]);
+ break;
+ }
+}
+
+static void
+json_dump(struct json_writer_private *p)
+{
+ json_element_dump(p->fh, p->root, 0);
+ fprintf(p->fh, "\n");
+}
+
+static void
+json_start(struct writer *w, const char *tag, const char *descr)
+{
+ struct json_writer_private *p = w->priv;
+ struct element *child;
+ struct element *new;
+
+ /* Look for the tag in the current object. */
+ TAILQ_FOREACH (child, &p->current->children, next) {
+ if (!strcmp(child->key, tag)) break;
+ }
+ if (!child) child = json_element_new(p->current, tag, ARRAY);
+
+ /* Queue the new element. */
+ new = json_element_new(child, NULL, OBJECT);
+ p->current = new;
+}
+
+static void
+json_attr(struct writer *w, const char *tag, const char *descr, const char *value)
+{
+ struct json_writer_private *p = w->priv;
+ struct element *new = json_element_new(p->current, tag, STRING);
+ if (value && (!strcmp(value, "yes") || !strcmp(value, "on"))) {
+ new->tag = BOOL;
+ new->boolean = 1;
+ } else if (value && (!strcmp(value, "no") || !strcmp(value, "off"))) {
+ new->tag = BOOL;
+ new->boolean = 0;
+ } else {
+ new->string = strdup(value ? value : "");
+ }
+}
+
+static void
+json_data(struct writer *w, const char *data)
+{
+ struct json_writer_private *p = w->priv;
+ struct element *new = json_element_new(p->current, "value", STRING);
+ new->string = strdup(data ? data : "");
+}
+
+/* When an array has only one member, just remove the array. When an object has
+ * `value` as the only key, remove the object. Moreover, for an object, move the
+ * `name` key outside (inside a new object). This is a recursive function. We
+ * think the depth will be limited. Also, the provided element can be
+ * destroyed. Don't use it after this function!
+ *
+ * During the cleaning process, we will generate array of 1-size objects that
+ * could be turned into an object. We don't do that since people may rely on
+ * this format. Another problem is the format is changing depending on the
+ * number of interfaces or the number of neighbors.
+ */
+static void
+json_element_cleanup(struct element *el)
+{
+#ifndef ENABLE_JSON0
+ struct element *child, *child_next;
+
+ /* If array with one element, steal the content. Object with only one
+ * value whose key is "value", steal the content. */
+ if ((el->tag == ARRAY || el->tag == OBJECT) &&
+ (child = TAILQ_FIRST(&el->children)) && !TAILQ_NEXT(child, next) &&
+ (el->tag == ARRAY || !strcmp(child->key, "value"))) {
+ free(child->key);
+ child->key = el->key;
+ child->parent = el->parent;
+ TAILQ_INSERT_BEFORE(el, child, next);
+ TAILQ_REMOVE(&el->parent->children, el, next);
+ free(el);
+ json_element_cleanup(child);
+ return;
+ }
+
+ /* Other kind of arrays, recursively clean */
+ if (el->tag == ARRAY) {
+ for (child = TAILQ_FIRST(&el->children); child; child = child_next) {
+ child_next = TAILQ_NEXT(child, next);
+ json_element_cleanup(child);
+ }
+ return;
+ }
+
+ /* Other kind of objects, recursively clean, but if one key is "name",
+ * use it's value as a key for a new object stealing the existing
+ * one. */
+ if (el->tag == OBJECT) {
+ struct element *name_child = NULL;
+ for (child = TAILQ_FIRST(&el->children); child; child = child_next) {
+ child_next = TAILQ_NEXT(child, next);
+ json_element_cleanup(child);
+ }
+ /* Redo a check to find if we have a "name" key now */
+ for (child = TAILQ_FIRST(&el->children); child; child = child_next) {
+ child_next = TAILQ_NEXT(child, next);
+ if (!strcmp(child->key, "name") && child->tag == STRING) {
+ name_child = child;
+ }
+ }
+ if (name_child) {
+ struct element *new_el = json_element_new(NULL, NULL, OBJECT);
+ /* Replace el by new_el in parent object/array */
+ new_el->parent = el->parent;
+ TAILQ_INSERT_BEFORE(el, new_el, next);
+ TAILQ_REMOVE(&el->parent->children, el, next);
+ new_el->key = el->key;
+
+ /* new_el is parent of el */
+ el->parent = new_el;
+ el->key = name_child->string; /* stolen */
+ TAILQ_INSERT_TAIL(&new_el->children, el, next);
+
+ /* Remove "name" child */
+ TAILQ_REMOVE(&el->children, name_child, next);
+ free(name_child->key);
+ free(name_child);
+ }
+ return;
+ }
+#endif
+}
+
+static void
+json_cleanup(struct json_writer_private *p)
+{
+ if (p->variant != 0) json_element_cleanup(p->root);
+}
+
+static void
+json_end(struct writer *w)
+{
+ struct json_writer_private *p = w->priv;
+ while ((p->current = p->current->parent) != NULL && p->current->tag != OBJECT)
+ ;
+ if (p->current == NULL) {
+ fatalx("lldpctl", "unbalanced tags");
+ return;
+ }
+
+ /* Display current object if last one */
+ if (p->current == p->root) {
+ json_cleanup(p);
+ json_dump(p);
+ json_free(p);
+ fprintf(p->fh, "\n");
+ fflush(p->fh);
+ p->root = p->current = json_element_new(NULL, NULL, OBJECT);
+ }
+}
+
+static void
+json_finish(struct writer *w)
+{
+ struct json_writer_private *p = w->priv;
+ if (p->current != p->root) log_warnx("lldpctl", "unbalanced tags");
+ json_free(p);
+ free(p);
+ free(w);
+}
+
+struct writer *
+json_init(FILE *fh, int variant)
+{
+ struct writer *result;
+ struct json_writer_private *priv;
+
+ priv = malloc(sizeof(*priv));
+ if (priv == NULL) fatal(NULL, NULL);
+
+ priv->fh = fh;
+ priv->root = priv->current = json_element_new(NULL, NULL, OBJECT);
+ priv->variant = variant;
+
+ result = malloc(sizeof(*result));
+ if (result == NULL) fatal(NULL, NULL);
+
+ result->priv = priv;
+ result->start = json_start;
+ result->attr = json_attr;
+ result->data = json_data;
+ result->end = json_end;
+ result->finish = json_finish;
+
+ return result;
+}
diff --git a/src/client/kv_writer.c b/src/client/kv_writer.c
new file mode 100644
index 0000000..56c9a43
--- /dev/null
+++ b/src/client/kv_writer.c
@@ -0,0 +1,133 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2010 Andreas Hofmeister <andi@collax.com>
+ * 2010 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "writer.h"
+#include "../log.h"
+
+#define SEP '.'
+
+struct kv_writer_private {
+ FILE *fh;
+ char *prefix;
+};
+
+static void
+kv_start(struct writer *w, const char *tag, const char *descr)
+{
+ struct kv_writer_private *p = w->priv;
+ char *newprefix;
+ int s;
+
+ s = strlen(p->prefix) + 1 + strlen(tag);
+ if ((newprefix = malloc(s + 1)) == NULL) fatal(NULL, NULL);
+ if (strlen(p->prefix) > 0)
+ snprintf(newprefix, s + 1, "%s\1%s", p->prefix, tag);
+ else
+ snprintf(newprefix, s + 1, "%s", tag);
+ free(p->prefix);
+ p->prefix = newprefix;
+}
+
+static void
+kv_data(struct writer *w, const char *data)
+{
+ struct kv_writer_private *p = w->priv;
+ char *key = strdup(p->prefix);
+ char *value = data ? strdup(data) : NULL;
+ char *dot, *nl;
+ if (!key) fatal(NULL, NULL);
+ while ((dot = strchr(key, '\1')) != NULL)
+ *dot = SEP;
+ if (value) {
+ nl = value;
+ while ((nl = strchr(nl, '\n'))) {
+ *nl = ' ';
+ nl++;
+ }
+ }
+ fprintf(p->fh, "%s=%s\n", key, value ? value : "");
+ free(key);
+ free(value);
+}
+
+static void
+kv_end(struct writer *w)
+{
+ struct kv_writer_private *p = w->priv;
+ char *dot;
+
+ if ((dot = strrchr(p->prefix, '\1')) == NULL) {
+ p->prefix[0] = '\0';
+ fflush(p->fh);
+ } else
+ *dot = '\0';
+}
+
+static void
+kv_attr(struct writer *w, const char *tag, const char *descr, const char *value)
+{
+ if (!strcmp(tag, "name") || !strcmp(tag, "type")) {
+ /* Special case for name, replace the last prefix */
+ kv_end(w);
+ kv_start(w, value, NULL);
+ } else {
+ kv_start(w, tag, NULL);
+ kv_data(w, value);
+ kv_end(w);
+ }
+}
+
+static void
+kv_finish(struct writer *w)
+{
+ struct kv_writer_private *p = w->priv;
+
+ free(p->prefix);
+ free(w->priv);
+ w->priv = NULL;
+
+ free(w);
+}
+
+struct writer *
+kv_init(FILE *fh)
+{
+
+ struct writer *result;
+ struct kv_writer_private *priv;
+
+ if ((priv = malloc(sizeof(*priv))) == NULL) fatal(NULL, NULL);
+
+ priv->fh = fh;
+ priv->prefix = strdup("");
+
+ if ((result = malloc(sizeof(struct writer))) == NULL) fatal(NULL, NULL);
+
+ result->priv = priv;
+ result->start = kv_start;
+ result->attr = kv_attr;
+ result->data = kv_data;
+ result->end = kv_end;
+ result->finish = kv_finish;
+
+ return result;
+}
diff --git a/src/client/lldpcli.8.in b/src/client/lldpcli.8.in
new file mode 100644
index 0000000..3f85da3
--- /dev/null
+++ b/src/client/lldpcli.8.in
@@ -0,0 +1,1151 @@
+.\" Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
+.\" Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+.\"
+.\" Permission to use, copy, modify, and/or distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: July 16 2008 $
+.Dt LLDPCLI 8
+.Os
+.Sh NAME
+.Nm lldpcli ,
+.Nm lldpctl
+.Nd control LLDP daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl dv
+.Op Fl u Ar socket
+.Op Fl f Ar format
+.Op Fl c Ar file
+.Op Ar command ...
+.Nm lldpctl
+.Op Fl dv
+.Op Fl u Ar socket
+.Op Fl f Ar format
+.Op Ar interfaces ...
+.Sh DESCRIPTION
+The
+.Nm
+program controls
+.Xr lldpd 8
+daemon.
+.Pp
+When no command is specified,
+.Nm
+will start an interactive shell which can be used to input arbitrary
+commands as if they were specified on the command line. This
+interactive shell should provide completion and history support.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Enable more debugging information. This flag can be repeated.
+.It Fl u Ar socket
+Specify the Unix-domain socket used for communication with
+.Xr lldpd 8 .
+.It Fl v
+Show
+.Nm
+version. When repeated, show more build information.
+.It Fl f Ar format
+Choose the output format. Currently
+.Em plain ,
+.Em xml ,
+.Em json ,
+.Em json0
+and
+.Em keyvalue
+formats are available. The default is
+.Em plain .
+.Em json0
+is more verbose than
+.Em json
+but the structure of the JSON object is not affected by the number of
+interfaces or the number of neighbors. It is therefore easier to
+parse.
+.It Fl c Ar file
+Read the given configuration file. This option may be repeated several
+times. If a directory is provided, each file contained in it will be
+read if ending by
+.Li .conf .
+Order is alphabetical.
+.El
+.Pp
+When invoked as
+.Nm lldpctl ,
+.Nm
+will display detailed information about each neighbors on the
+specified interfaces or on all interfaces if none are specified. This
+command is mostly kept for backward compatibility with older versions.
+.Pp
+The following commands are supported by
+.Nm .
+When there is no ambiguity, the keywords can be abbreviated. For
+example,
+.Cd show neighbors ports eth0 summary
+and
+.Cd sh neigh p eth0 sum
+are the same command.
+.Bd -ragged -offset XX
+.Cd exit
+.Bd -ragged -offset XXXXXX
+Quit
+.Nm .
+.Ed
+
+.Cd help Op ...
+.Bd -ragged -offset XXXXXX
+Display general help or help about a command. Also, you can get help
+using the completion or by pressing the
+.Ic ?
+key. However, completion and inline help may be unavailable if
+.Nm
+was compiled without readline support but
+.Cd help
+command is always available.
+.Ed
+
+.Cd show neighbors
+.Op ports Ar ethX Op ,...
+.Op Cd details | summary
+.Op Cd hidden
+.Bd -ragged -offset XXXXXX
+Display information about each neighbor known by
+.Xr lldpd 8
+daemon. With
+.Cd summary ,
+only the name and the port description of each remote host will be
+displayed. On the other hand, with
+.Cd details ,
+all available information will be displayed, giving a verbose
+view. When using
+.Cd hidden ,
+also display remote ports hidden by the smart filter. When specifying
+one or several ports, the information displayed is limited to the
+given list of ports.
+.Ed
+
+.Cd show interfaces
+.Op ports Ar ethX Op ,...
+.Op Cd details | summary
+.Op Cd hidden
+.Bd -ragged -offset XXXXXX
+Display information about each local interface known by
+.Xr lldpd 8
+daemon. With
+.Cd summary ,
+only the name and the port description of each local interface will be
+displayed. On the other hand, with
+.Cd details ,
+all available information will be displayed, giving a verbose
+view. When using
+.Cd hidden ,
+also display local ports hidden by the smart filter. When specifying
+one or several ports, the information displayed is limited to the
+given list of ports.
+.Ed
+
+.Cd show chassis
+.Op Cd details | summary
+.Bd -ragged -offset XXXXXX
+Display information about local chassis. With
+.Cd summary ,
+most details are skipped. On the other hand, with
+.Cd details ,
+all available information will be displayed, giving a verbose
+view.
+.Ed
+
+.Cd watch
+.Op ports Ar ethX Op ,...
+.Op Cd details | summary
+.Op Cd hidden
+.Op Cd limit Ar X
+.Bd -ragged -offset XXXXXX
+Watch for any neighbor changes and report them as soon as they
+happen. When specifying ports, the changes are only reported when
+happening on the given ports.
+.Cd hidden , summary
+and
+.Cd details
+have the same meaning than previously described. If
+.Cd limit
+is specified,
+.Nm
+will exit after receiving the specified number of events.
+.Ed
+
+.Cd show configuration
+.Bd -ragged -offset XXXXXX
+Display global configuration of
+.Xr lldpd 8
+daemon.
+.Ed
+
+.Cd show statistics
+.Op ports Ar ethX Op ,...
+.Op Cd summary
+.Bd -ragged -offset XXXXXX
+Report LLDP-related statistics, like the number of LLDPDU transmitted,
+received, discarded or unrecognized. When specifying ports, only the
+statistics from the given port are reported. With
+.Cd summary
+the statistics of each port is summed.
+.Ed
+
+.Cd update
+.Bd -ragged -offset XXXXXX
+Make
+.Xr lldpd 8
+update its information and send new LLDP PDU on all interfaces.
+.Ed
+
+.Cd configure
+.Cd system hostname Ar name
+.Bd -ragged -offset XXXXXX
+Override system hostname with the provided value. By default, the
+system name is the FQDN found from the resolved value of
+.Ic uname -n .
+As a special value, use "." (dot) to use the short hostname instead of
+a FQDN.
+.Ed
+
+.Cd unconfigure
+.Cd system hostname
+.Bd -ragged -offset XXXXXX
+Do not override system hostname and restore the use of the node name.
+.Ed
+
+.Cd configure
+.Cd system description Ar description
+.Bd -ragged -offset XXXXXX
+Override chassis description with the provided value instead of using
+kernel name, node name, kernel version, build date and architecture.
+.Ed
+
+.Cd unconfigure
+.Cd system description
+.Bd -ragged -offset XXXXXX
+Do not override chassis description and use a value computed from node
+name, kernel name, kernel version, build date and architecture instead.
+.Ed
+
+.Cd configure
+.Cd system chassisid Ar description
+.Bd -ragged -offset XXXXXX
+Override chassis ID with the provided value instead of using MAC address
+from one interface or host name.
+.Ed
+
+.Cd unconfigure
+.Cd system chassisid
+.Bd -ragged -offset XXXXXX
+Do not override chassis ID and use a value computed from one of the interface
+MAC address (or host name if none is found).
+.Ed
+
+.Cd configure
+.Cd system platform Ar description
+.Bd -ragged -offset XXXXXX
+Override platform description with the provided value instead of using
+kernel name. This value is currently only used for CDP.
+.Ed
+
+.Cd unconfigure
+.Cd system platform
+.Bd -ragged -offset XXXXXX
+Do not override platform description and use the kernel name. This
+option undoes the previous one.
+.Ed
+
+.Cd configure
+.Cd system capabilities enabled Ar capabilities
+.Bd -ragged -offset XXXXXX
+Override system capabilities with the provided value instead of using
+kernel information. Several capabilities can be specified separated by
+commas. Only available capabilities can be enabled. Valid capabilities are:
+.Bl -tag -width "XXX." -compact -offset XX
+.It Sy other
+.It Sy repeater
+.It Sy bridge
+.It Sy wlan
+.It Sy router
+.It Sy telephone
+.It Sy docsis
+.It Sy station
+.El
+Here is an example of use:
+.D1 configure system capabilities enabled bridge,router
+.Pp
+.Ed
+
+.Cd unconfigure
+.Cd system capabilities enabled
+.Bd -ragged -offset XXXXXX
+Do not override capabilities and use the kernel information. This option
+undoes the previous one.
+.Ed
+
+.Cd configure
+.Cd system interface pattern Ar pattern
+.Bd -ragged -offset XXXXXX
+Specify which interface to listen and send LLDPDU to. Without this
+option,
+.Nm lldpd
+will use all available physical interfaces. This option can use
+wildcards. Several interfaces can be specified separated by commas.
+It is also possible to remove an interface by prefixing it with an
+exclamation mark. It is possible to allow an interface by
+prefixing it with two exclamation marks. An allowed interface beats
+a forbidden interfaces which beats a simple matched interface. For
+example, with
+.Em eth*,!eth1,!eth2
+.Nm lldpd
+will only use interfaces starting by
+.Em eth
+with the exception of
+.Em eth1
+and
+.Em eth2 .
+While with
+.Em *,!eth*,!!eth1
+.Nm
+will use all interfaces, except interfaces starting by
+.Em eth
+with the exception of
+.Em eth1 .
+When an exact match is found, it will circumvent some tests. For example, if
+.Em eth0.12
+is specified, it will be accepted even if this is a VLAN interface.
+.Ed
+
+.Cd unconfigure
+.Cd system interface pattern
+.Bd -ragged -offset XXXXXX
+Remove any previously configured interface pattern and use all
+physical interfaces. This option undoes the previous one.
+.Ed
+
+.Cd configure
+.Cd system interface permanent Ar pattern
+.Bd -ragged -offset XXXXXX
+Specify interfaces whose configuration is permanently kept by
+.Nm lldpd .
+By default,
+.Nm lldpd
+disregard any data about interfaces when they are removed from the
+system (statistics, custom configuration). This option allows one to
+specify a pattern similar to the interface pattern. If an interface
+disappear but matches the pattern, its data is kept in memory and
+reused if the interface reappear at some point. For example, on Linux,
+one could use the pattern
+.Em eth*,eno*,enp* ,
+which should match fixed interfaces on most systems.
+.Ed
+
+.Cd unconfigure
+.Cd system interface permanent
+.Bd -ragged -offset XXXXXX
+Remove any previously configured permanent interface pattern. Any
+interface removed from the system will be forgotten. This option
+undoes the previous one.
+.Ed
+
+.Cd configure
+.Cd system interface description
+.Bd -ragged -offset XXXXXX
+Some OS allows the user to set a description for an interface. Setting
+this option will enable
+.Nm lldpd
+to override this description with the name of the peer neighbor if one
+is found or with the number of neighbors found.
+.Ed
+
+.Cd unconfigure
+.Cd system interface description
+.Bd -ragged -offset XXXXXX
+Do not update interface description with the name of the peer
+neighbor. This option undoes the previous one.
+.Ed
+
+.Cd configure
+.Cd system interface promiscuous
+.Bd -ragged -offset XXXXXX
+Enable promiscuous mode on managed interfaces.
+.Pp
+When the interface is not managed any more (or when quitting
+.Nm lldpd ) ,
+the interface is left in promiscuous mode as it is difficult to know
+if someone else also put the interface in promiscuous mode.
+.Pp
+This option is known to be useful when the remote switch is a Cisco
+2960 and the local network card features VLAN hardware
+acceleration. In this case, you may not receive LLDP frames from the
+remote switch. The most plausible explanation for this is the frame is
+tagged with some VLAN (usually VLAN 1) and your network card is
+filtering VLAN. This is not the only available solution to work-around
+this problem. If you are concerned about performance issues, you can
+also tag the VLAN 1 on each interface instead.
+.Pp
+Currently, this option has no effect on anything else than Linux. On
+other OS, either disable VLAN acceleration, tag VLAN 1 or enable
+promiscuous mode manually on the interface.
+.Ed
+
+.Cd unconfigure
+.Cd system interface promiscuous
+.Bd -ragged -offset XXXXXX
+Do not set promiscuous mode on managed interfaces. This option does
+not disable promiscuous mode on interfaces already using this mode.
+.Ed
+
+.Cd configure
+.Cd system ip management pattern Ar pattern
+.Bd -ragged -offset XXXXXX
+Specify the management addresses of this system. As for interfaces
+(described above), this option can use wildcards and inversions.
+Without this option, the first IPv4 and the first IPv6 are used. If an
+exact IP address is provided, it is used as a management address
+without any check. If only negative patterns are provided, only one
+IPv4 and one IPv6 addresses are chosen. Otherwise, many of them can be
+selected. If you want to remove IPv6 addresses, you can use
+.Em !*:* .
+If an interface name is matched, the first IPv4 address and the first
+IPv6 address associated to this interface will be chosen.
+.Ed
+
+.Cd unconfigure
+.Cd system ip management pattern
+.Bd -ragged -offset XXXXXX
+Unset any specific pattern for matching management addresses. This
+option undoes the previous one.
+.Ed
+
+.Cd configure
+.Cd system bond-slave-src-mac-type Ar value
+.Bd -ragged -offset XXXXXX
+Set the type of src mac in lldp frames sent on bond slaves
+
+Valid types are:
+.Bl -tag -width "XXX." -compact -offset XX
+.It Sy real
+Slave real mac
+.It Sy zero
+All zero mac
+.It Sy fixed
+An arbitrary fixed value
+.Li ( 00:60:08:69:97:ef )
+.It Sy local
+Real mac with locally administered bit set. If the real mac already
+has the locally administered bit set, fallback to the fixed value.
+.El
+.Pp
+Default value for
+.Nm bond-slave-src-mac-type
+is
+.Nm local .
+Some switches may complain when using one of the two other possible
+values (either because
+.Li 00:00:00:00:00:00
+is not a valid MAC or because the MAC address is flapping from one
+port to another). Using
+.Sy local
+might lead to a duplicate MAC address on the network (but this is
+quite unlikely).
+.Ed
+
+.Cd configure
+.Cd system max-neighbors Ar neighbors
+.Bd -ragged -offset XXXXXX
+Change the maximum number of neighbors accepted (for each protocol) on
+an interface. This is a global value. The default is 32. This setting
+only applies to future neighbors.
+.Ed
+
+.Cd configure
+.Cd lldp agent-type
+.Cd nearest-bridge | nearest-non-tpmr-bridge | nearest-customer-bridge
+.Bd -ragged -offset XXXXXX
+The destination MAC address used to send LLDPDU allows an agent to
+control the propagation of LLDPDUs. By default, the
+.Li 01:80:c2:00:00:0e
+MAC address is used and limit the propagation of the LLDPDU to the
+nearest bridge
+.Cd ( nearest-bridge ) .
+To instruct
+.Nm lldpd
+to use the
+.Li 01:80:c2:00:00:03
+MAC address instead, use
+.Cd nearest-nontpmr-bridge
+instead.
+To use the
+.Li 01:80:c2:00:00:00
+MAC address instead, use
+.Cd nearest-customer-bridge
+instead.
+.Ed
+
+.Cd configure
+.Cd lldp capabilities-advertisements
+.Pp
+.Cd unconfigure
+.Cd lldp capabilities-advertisements
+.Bd -ragged -offset XXXXXX
+Enable or disable advertisements of the chassis capabilities TLV.
+.Ed
+
+.Cd configure
+.Cd lldp management-addresses-advertisements
+.Pp
+.Cd unconfigure
+.Cd lldp management-addresses-advertisements
+.Bd -ragged -offset XXXXXX
+Enable or disable advertisements of the management address TLV.
+.Ed
+
+.Cd configure
+.Cd lldp portidsubtype
+.Cd ifname | macaddress
+.Pp
+.Cd configure
+.Op ports Ar ethX Op ,...
+.Cd lldp portidsubtype
+.Cd local Ar value
+.Bd -ragged -offset XXXXXX
+Force port ID subtype. By default,
+.Nm lldpd
+will use the MAC address as port identifier and the interface name as
+port description, unless the interface has an alias. In this case, the
+interface name will be used as port identifier and the description
+will be the interface alias. With this command, you can force the port
+identifier to be the interface name (with
+.Cd ifname ) ,
+the MAC address (with
+.Cd macaddress )
+or a local value (with
+.Cd value ) .
+In the latest case, the local value should be provided.
+.Ed
+
+.Cd configure
+.Op ports Ar ethX Op ,...
+.Cd lldp portdescription
+.Cd Ar description
+.Bd -ragged -offset XXXXXX
+Force port description to the provided string.
+.Ed
+
+.Cd configure
+.Cd lldp tx-interval Ar interval
+.Bd -ragged -offset XXXXXX
+Change transmit delay to the specified value in seconds. The transmit
+delay is the delay between two transmissions of LLDP PDU. The default
+value is 30 seconds. Note:
+.Nm lldpd
+also starts another system based refresh timer on each port to detect
+changes such as a hostname. This is the value of the tx-interval
+multiplied by 20.
+.Pp
+You can specify an
+.Cd interval
+value in milliseconds by appending a "ms" suffix to the figure (e.g.
+"configure lldp tx-interval 1500ms" is 1.5s, not 1500s). In this case
+the TTL for received and sent LLDP frames is rounded up to the next
+second. Note: the effective interval can be limited by the operating
+system capabilities and CPU speed.
+.Ed
+
+.Cd configure
+.Cd lldp tx-hold Ar hold
+.Bd -ragged -offset XXXXXX
+Change transmit hold value to the specified value. This value is used
+to compute the TTL of transmitted packets which is the product of this
+value and of the transmit delay. The default value is 4 and therefore
+the default TTL is 120 seconds.
+.Ed
+
+.Cd configure
+.Op ports Ar ethX Op ,...
+.Cd lldp
+.Cd status Ar rx-and-tx | rx-only | tx-only | disabled
+.Bd -ragged -offset XXXXXX
+Configure the administrative status of the given port. By default, all
+ports are configured to be in
+.Ar rx-and-tx
+mode. This means they can receive and transmit LLDP frames (as well as
+other protocols if needed). In
+.Ar rx-only
+mode, they won't emit any frames and in
+.Ar tx-only
+mode, they won't receive any frames. In
+.Ar disabled
+mode, no frame will be sent and any incoming frame will be
+discarded. This setting does not override the operational mode of the
+main daemon. If it is configured in receive-only mode (with the
+.Fl r
+flag), setting any transmit mode won't have any effect.
+.Ed
+
+.Cd configure
+.Op ports Ar ethX Op ,...
+.Cd lldp
+.Cd vlan-tx Ar vlan_id
+.Op Cd prio Ar priority Op Cd dei Ar dei
+.Bd -ragged -offset XXXXXX
+Configure the given port to send LLDP frames over a specified VLAN. With VLAN Identifier (VID) as
+.Ar vlan_id ,
+Priority Code Point (PCP) as
+.Ar priority ,
+and Drop Eligible Indicator (DEI) as
+.Ar dei .
+.Nm lldpd
+accepts LLDP frames on all VLANs.
+.Ed
+
+.Cd configure
+.Op ports Ar ethX Op ,...
+.Cd lldp custom-tlv
+.Op Cd add | replace
+.Cd oui Ar oui
+.Cd subtype Ar subtype
+.Op Cd oui-info Ar content
+.Bd -ragged -offset XXXXXX
+Emit a custom TLV for OUI
+.Ar oui ,
+with subtype
+.Ar subtype
+and optionally with the bytes specified in
+.Ar content .
+Both
+.Ar oui
+and
+.Ar content
+should be a comma-separated list of bytes in hex format.
+.Ar oui
+must be exactly 3-byte long.
+If
+.Ar add
+is specified then the TLV will be added. This is the default action.
+If
+.Ar replace
+is specified then all TLVs with the same
+.Ar oui
+and
+.Ar subtype
+will be replaced.
+
+.Ed
+
+.Cd unconfigure
+.Op ports Ar ethX Op ,...
+.Cd lldp custom-tlv
+.Op Cd oui Ar oui
+.Op Cd subtype Ar subtype
+.Bd -ragged -offset XXXXXX
+When no oui is specified, remove all previously configured custom TLV.
+When OUI
+.Ar oui
+and subtype
+.Ar subtype
+is specified, remove specific instances of custom TLV.
+.Ed
+
+.Cd configure med fast-start
+.Cd enable | tx-interval Ar interval
+.Bd -ragged -offset XXXXXX
+Configure LLDP-MED fast start mechanism. When a new LLDP-MED-enabled
+neighbor is detected, fast start allows
+.Nm lldpd
+to shorten the interval between two LLDPDU.
+.Cd enable
+should enable LLDP-MED fast start while
+.Cd tx-interval
+specifies the interval between two LLDPDU in seconds. The default
+interval is 1 second. Once 4 LLDPDU have been sent, the fast start
+mechanism is disabled until a new neighbor is detected.
+.Ed
+
+.Cd unconfigure med fast-start
+.Bd -ragged -offset XXXXXX
+Disable LLDP-MED fast start mechanism.
+.Ed
+
+.Cd configure
+.Op ports Ar ethX Op ,...
+.Cd med location coordinate
+.Cd latitude Ar latitude
+.Cd longitude Ar longitude
+.Cd altitude Ar altitude Ar unit
+.Cd datum Ar datum
+.Bd -ragged -offset XXXXXX
+Advertise a coordinate based location on the given ports (or on all
+ports if no port is specified). The format of
+.Ar latitude
+is a decimal floating point number followed either by
+.Em N
+or
+.Em S .
+The format of
+.Ar longitude
+is a decimal floating point number followed either by
+.Em E
+or
+.Em W .
+.Ar altitude
+is a decimal floating point number followed either by
+.Em m
+when expressed in meters or
+.Em f
+when expressed in floors. A space is expected between the floating
+point number and the unit.
+.Ar datum
+is one of those values:
+.Bl -bullet -compact -offset XXXXXXXX
+.It
+WGS84
+.It
+NAD83
+.It
+NAD83/MLLW
+.El
+.Pp
+A valid use of this command is:
+.D1 configure ports eth0 med location coordinate latitude 48.85667N longitude 2.2014E altitude 117.47 m datum WGS84
+.Ed
+
+.Cd configure
+.Op ports Ar ethX Op ,...
+.Cd med location address
+.Cd country Ar country
+.Cd Op Ar type value Op ...
+.Bd -ragged -offset XXXXXX
+Advertise a civic address on the given ports (or on all ports if no
+port is specified).
+.Ar country
+is the two-letter code representing the country. The remaining
+arguments should be paired to form the address. The first member of
+each pair indicates the type of the second member which is a free-form
+text. Here is the list of valid types:
+.Bl -bullet -compact -offset XXXXXXXX
+.It
+language
+.It
+country-subdivision
+.It
+county
+.It
+city
+.It
+city-division
+.It
+block
+.It
+street
+.It
+direction
+.It
+trailing-street-suffix
+.It
+street-suffix
+.It
+number
+.It
+number-suffix
+.It
+landmark
+.It
+additional
+.It
+name
+.It
+zip
+.It
+building
+.It
+unit
+.It
+floor
+.It
+room
+.It
+place-type
+.It
+script
+.El
+.Pp
+A valid use of this command is:
+.D1 configure ports eth1 med location address country US street Qo Commercial Road Qc city Qo Roseville Qc
+.Ed
+
+.Cd configure
+.Op ports Ar ethX Op ,...
+.Cd med location elin
+.Ar number
+.Bd -ragged -offset XXXXXX
+Advertise the availability of an ELIN number. This is used for setting
+up emergency call. If the provided number is too small, it will be
+padded with 0. Here is an example of use:
+.D1 configure ports eth2 med location elin 911
+.Ed
+
+.Cd configure
+.Op ports Ar ethX Op ,...
+.Cd med policy
+.Cd application Ar application
+.Op Cd unknown
+.Op Cd tagged
+.Op Cd vlan Ar vlan
+.Op Cd priority Ar priority
+.Op Cd dscp Ar dscp
+.Bd -ragged -offset XXXXXX
+Advertise a specific network policy for the given ports (or for all
+ports if no port was provided). Only the application type is
+mandatory.
+.Ar application
+should be one of the following values:
+.Bl -bullet -compact -offset XXXXXXXX
+.It
+voice
+.It
+voice-signaling
+.It
+guest-voice
+.It
+guest-voice-signaling
+.It
+softphone-voice
+.It
+video-conferencing
+.It
+streaming-video
+.It
+video-signaling
+.El
+.Pp
+The
+.Cd unknown
+flag tells that the network policy for the specified application type
+is required by the device but is currently unknown. This is used by
+Endpoint Devices, not by Network Connectivity Devices. If not
+specified, the network policy for the given application type is
+defined.
+.Pp
+When a VLAN is specified with
+.Ar vlan
+tells which 802.1q VLAN ID has to be advertised for the network
+policy. A valid value is between 1 and 4094.
+.Cd tagged
+tells the VLAN should be tagged for the specified application type.
+.Pp
+.Ar priority
+allows one to specify IEEE 802.1d / IEEE 802.1p Layer 2 Priority, also
+known as Class of Service (CoS), to be used for the specified
+application type. This field is usually ignored if no VLAN is
+specified. The names match 802.1D-2004 standard (table G-2). Some more
+recent standards may use different labels. Only the numeric values
+should be relied upon. The accepted labels are:
+.Bl -tag -width "X." -compact -offset XXXX
+.It Sy 1
+background
+.It Sy 0
+best-effort
+.It Sy 2
+excellent-effort
+.It Sy 3
+critical-applications
+.It Sy 4
+video
+.It Sy 5
+voice
+.It Sy 6
+internetwork-control
+.It Sy 7
+network-control
+.El
+.Pp
+.Ar dscp
+represents the DSCP value to be advertised for the given network
+policy. DiffServ/Differentiated Services Code Point (DSCP) value as
+defined in IETF RFC 2474 for the specified application type. Value: 0
+(default per RFC 2475) through 63. Note: The class selector DSCP
+values are backwards compatible for devices that only support the old
+IP precedence Type of Service (ToS) format. (See the RFCs for what
+these values mean)
+.Pp
+A valid use of this command is:
+.D1 configure med policy application voice vlan 500 priority voice dscp 46
+.Ed
+
+.Cd configure
+.Cd inventory hardware-revision Ar value
+.Bd -ragged -offset XXXXXX
+Override hardware-revision with the provided value. By default, the
+hardware-revision is fetched from /sys/class/dmi
+.Ed
+
+.Cd unconfigure
+.Cd inventory hardware-revision
+.Bd -ragged -offset XXXXXX
+Do not override hardware-revision and restore the use of the /sys/class/dmi value.
+.Ed
+
+.Cd configure
+.Cd inventory software-revision Ar value
+.Bd -ragged -offset XXXXXX
+Override software-revision with the provided value. By default, the
+software-revision is fetched from uname
+.Ed
+
+.Cd unconfigure
+.Cd inventory software-revision
+.Bd -ragged -offset XXXXXX
+Do not override software-revision and restore the use of the uname value.
+.Ed
+
+.Cd configure
+.Cd inventory firmware-revision Ar value
+.Bd -ragged -offset XXXXXX
+Override firmware-revision with the provided value. By default, the
+firmware-revision is fetched from /sys/class/dmi
+.Ed
+
+.Cd unconfigure
+.Cd inventory firmware-revision
+.Bd -ragged -offset XXXXXX
+Do not override firmware-revision and restore the use of the /sys/class/dmi value.
+.Ed
+
+.Cd configure
+.Cd inventory serial-number Ar value
+.Bd -ragged -offset XXXXXX
+Override serial-number with the provided value. By default, the
+serial-number is fetched from /sys/class/dmi
+.Ed
+
+.Cd unconfigure
+.Cd inventory serial-number
+.Bd -ragged -offset XXXXXX
+Do not override serial-number and restore the use of the /sys/class/dmi value.
+.Ed
+
+.Cd configure
+.Cd inventory manufacturer Ar value
+.Bd -ragged -offset XXXXXX
+Override manufacturer with the provided value. By default, the
+manufacturer is fetched from /sys/class/dmi
+.Ed
+
+.Cd unconfigure
+.Cd inventory manufacturer
+.Bd -ragged -offset XXXXXX
+Do not override manufacturer and restore the use of the /sys/class/dmi value.
+.Ed
+
+.Cd configure
+.Cd inventory model Ar value
+.Bd -ragged -offset XXXXXX
+Override model with the provided value. By default, the
+model is fetched from /sys/class/dmi
+.Ed
+
+.Cd unconfigure
+.Cd inventory model
+.Bd -ragged -offset XXXXXX
+Do not override model and restore the use of the /sys/class/dmi value.
+.Ed
+
+.Cd configure
+.Cd inventory asset Ar value
+.Bd -ragged -offset XXXXXX
+Override asset with the provided value. By default, the
+asset is fetched from /sys/class/dmi
+.Ed
+
+.Cd unconfigure
+.Cd inventory asset
+.Bd -ragged -offset XXXXXX
+Do not override asset and restore the use of the /sys/class/dmi value.
+.Ed
+
+.Cd configure
+.Op ports Ar ethX Op ,...
+.Cd med power pse | pd
+.Cd source Ar source
+.Cd priority Ar priority
+.Cd value Ar value
+.Bd -ragged -offset XXXXXX
+Advertise the LLDP-MED POE-MDI TLV for the given ports or for all
+interfaces if no port is provided. One can act as a PD (power
+consumer) or a PSE (power provider). No check is done on the validity
+of the parameters while LLDP-MED requires some restrictions:
+.Bl -bullet
+.It
+PD shall never request more power than physical 802.3af class.
+.It
+PD shall never draw more than the maximum power advertised by PSE.
+.It
+PSE shall not reduce power allocated to PD when this power is in use.
+.It
+PSE may request reduced power using conservation mode
+.It
+Being PSE or PD is a global parameter, not a per-port parameter.
+.Nm
+does not enforce this: a port can be set as PD or PSE. LLDP-MED also
+requires for a PSE to only have one power source (primary or
+backup). Again,
+.Nm
+does not enforce this. Each port can have its own power source. The
+same applies for PD and power priority. LLDP-MED MIB does not allow
+this kind of representation.
+.El
+.Pp
+Valid types are:
+.Bl -tag -width "XXX." -compact -offset XX
+.It Sy pse
+Power Sourcing Entity (power provider)
+.It Sy pd
+Power Device (power consumer)
+.El
+.Pp
+Valid sources are:
+.Bl -tag -width "XXXXXXX" -compact -offset XX
+.It Sy unknown
+Unknown
+.It Sy primary
+For PSE, the power source is the primary power source.
+.It Sy backup
+For PSE, the power source is the backup power source or a power
+conservation mode is asked (the PSE may be running on UPS for
+example).
+.It Sy pse
+For PD, the power source is the PSE.
+.It Sy local
+For PD, the power source is a local source.
+.It Sy both
+For PD, the power source is both the PSE and a local source.
+.El
+.Pp
+Valid priorities are:
+.Bl -tag -width "XXXXXXXXX" -compact -offset XX
+.It Sy unknown
+Unknown priority
+.It Sy critical
+Critical
+.It Sy high
+High
+.It Sy low
+Low
+.El
+.Pp
+.Ar value
+should be the total power in milliwatts required by the PD device or
+available by the PSE device.
+.Pp
+Here is an example of use:
+.D1 configure med power pd source pse priority high value 5000
+.Ed
+
+.Cd configure
+.Op ports Ar ethX Op ,...
+.Cd dot3 power pse | pd
+.Op Cd supported
+.Op Cd enabled
+.Op Cd paircontrol
+.Cd powerpairs Ar powerpairs
+.Op Cd class Ar class
+.Op Cd type Ar type Cd source Ar source Cd priority Ar priority Cd requested Ar requested Cd allocated Ar allocated
+.Bd -ragged -offset XXXXXX
+Advertise Dot3 POE-MDI TLV for the given port or for all ports if none
+was provided. One can act as a PD (power consumer) or a PSE (power
+provider). This configuration is distinct of the configuration of the
+transmission of the LLDP-MED POE-MDI TLV but the user should ensure
+the coherency of those two configurations if they are used together.
+.Pp
+.Ar supported
+means that MDI power is supported on the given port while
+.Ar enabled
+means that MDI power is enabled.
+.Ar paircontrol
+is used to indicate if pair selection can be controlled. Valid values
+for
+.Ar powerpairs
+are:
+.Bl -tag -width "XXXXXX" -compact -offset XX
+.It Sy signal
+The signal pairs only are in use.
+.It Sy spare
+The spare pairs only are in use.
+.El
+.Pp
+When specified,
+.Ar class
+is a number between 0 and 4.
+.Pp
+The remaining parameters are in conformance with 802.3at and are optional.
+.Ar type
+should be either 1 or 2, indicating which if the device conforms to
+802.3at type 1 or 802.3at type 2. Values of
+.Ar source
+and
+.Ar priority
+are the same as for LLDP-MED POE-MDI TLV.
+.Ar requested
+and
+.Ar allocated
+are expressed in milliwats.
+.Pp
+Here are two valid uses of this command:
+.D1 configure ports eth3 dot3 power pse supported enabled paircontrol powerpairs spare class class-3
+.D1 configure dot3 power pd supported enabled powerpairs spare class class-3 type 1 source pse priority low requested 10000 allocated 15000
+.Ed
+
+.Cd pause
+.Bd -ragged -offset XXXXXX
+Pause
+.Nm lldpd
+operations.
+.Nm lldpd
+will not send any more frames or receive ones. This can be undone with
+.Cd resume
+command. This only works interactively as lldpd asks lldpcli to
+unpause after reading the configuration file.
+.Ed
+
+.Cd resume
+.Bd -ragged -offset XXXXXX
+Resume
+.Nm lldpd
+operations.
+.Nm lldpd
+will start to send and receive frames. This command is issued
+internally after processing configuration but can be used at any time
+if a manual
+.Cd pause
+command is issued.
+.Ed
+
+.Ed
+.Sh FILES
+.Bl -tag -width "@LLDPD_CTL_SOCKET@XX" -compact
+.It @LLDPD_CTL_SOCKET@
+Unix-domain socket used for communication with
+.Xr lldpd 8 .
+.El
+.Sh SEE ALSO
+.Xr lldpd 8
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+program was written by
+.An Vincent Bernat Aq bernat@luffy.cx .
diff --git a/src/client/lldpcli.c b/src/client/lldpcli.c
new file mode 100644
index 0000000..8999ea7
--- /dev/null
+++ b/src/client/lldpcli.c
@@ -0,0 +1,611 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <libgen.h>
+#include <dirent.h>
+#include <signal.h>
+#include <sys/queue.h>
+
+#include "client.h"
+
+#ifdef HAVE___PROGNAME
+extern const char *__progname;
+#else
+# define __progname "lldpcli"
+#endif
+
+/* Global for completion */
+static struct cmd_node *root = NULL;
+const char *ctlname = NULL;
+
+static int
+is_lldpctl(const char *name)
+{
+ static int last_result = -1;
+ if (last_result == -1 && name) {
+ char *basec = strdup(name);
+ if (!basec) return 0;
+ char *bname = basename(basec);
+ last_result = (!strcmp(bname, "lldpctl"));
+ free(basec);
+ }
+ return (last_result == -1) ? 0 : last_result;
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "Usage: %s [OPTIONS ...] [COMMAND ...]\n", __progname);
+ fprintf(stderr, "Version: %s\n", PACKAGE_STRING);
+
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "-d Enable more debugging information.\n");
+ fprintf(stderr,
+ "-u socket Specify the Unix-domain socket used for communication with lldpd(8).\n");
+ fprintf(stderr,
+ "-f format Choose output format (plain, keyvalue, json, json0"
+#if defined USE_XML
+ ", xml"
+#endif
+ ").\n");
+ if (!is_lldpctl(NULL))
+ fprintf(stderr, "-c conf Read the provided configuration file.\n");
+
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "See manual page lldpcli(8) for more information\n");
+ exit(1);
+}
+
+static int
+is_privileged()
+{
+ /* Check we can access the control socket with read/write
+ * privileges. The `access()` function uses the real UID and real GID,
+ * therefore we don't have to mangle with our identity. */
+ return (ctlname && access(ctlname, R_OK | W_OK) == 0);
+}
+
+static const char *
+prompt()
+{
+#define CESC "\033"
+ int privileged = is_privileged();
+ if (isatty(STDIN_FILENO)) {
+ if (privileged) return "[lldpcli] # ";
+ return "[lldpcli] $ ";
+ }
+ return "";
+}
+
+static int must_exit = 0;
+/**
+ * Exit the interpreter.
+ */
+static int
+cmd_exit(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ log_info("lldpctl", "quit lldpcli");
+ must_exit = 1;
+ return 1;
+}
+
+/**
+ * Send an "update" request.
+ */
+static int
+cmd_update(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ log_info("lldpctl", "ask for global update");
+
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+ if (lldpctl_atom_set_int(config, lldpctl_k_config_tx_interval, -1) == NULL) {
+ log_warnx("lldpctl",
+ "unable to ask lldpd for immediate retransmission. %s",
+ lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+ log_info("lldpctl", "immediate retransmission requested successfully");
+ lldpctl_atom_dec_ref(config);
+ return 1;
+}
+
+/**
+ * Pause or resume execution of lldpd.
+ *
+ * @param conn The connection to lldpd.
+ * @param pause 1 if we want to pause lldpd, 0 otherwise
+ * @return 1 on success, 0 on error
+ */
+static int
+cmd_pause_resume(lldpctl_conn_t *conn, int pause)
+{
+ lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+ if (config == NULL) {
+ log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+ if (lldpctl_atom_get_int(config, lldpctl_k_config_paused) == pause) {
+ log_debug("lldpctl", "lldpd is already %s",
+ pause ? "paused" : "resumed");
+ lldpctl_atom_dec_ref(config);
+ return 1;
+ }
+ if (lldpctl_atom_set_int(config, lldpctl_k_config_paused, pause) == NULL) {
+ log_warnx("lldpctl", "unable to ask lldpd to %s operations. %s",
+ pause ? "pause" : "resume", lldpctl_last_strerror(conn));
+ lldpctl_atom_dec_ref(config);
+ return 0;
+ }
+ log_info("lldpctl", "lldpd should %s operations", pause ? "pause" : "resume");
+ lldpctl_atom_dec_ref(config);
+ return 1;
+}
+static int
+cmd_pause(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ (void)w;
+ (void)env;
+ return cmd_pause_resume(conn, 1);
+}
+static int
+cmd_resume(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ (void)w;
+ (void)env;
+ return cmd_pause_resume(conn, 0);
+}
+
+#ifdef HAVE_LIBREADLINE
+static int
+_cmd_complete(int all)
+{
+ char **argv = NULL;
+ int argc = 0;
+ int rc = 1;
+ size_t len = strlen(rl_line_buffer);
+ char *line = malloc(len + 2);
+ if (!line) return -1;
+ strlcpy(line, rl_line_buffer, len + 2);
+ line[rl_point] = 2; /* empty character, will force a word */
+ line[rl_point + 1] = 0;
+
+ if (tokenize_line(line, &argc, &argv) != 0) goto end;
+
+ char *compl =
+ commands_complete(root, argc, (const char **)argv, all, is_privileged());
+ if (compl &&strlen(argv[argc - 1]) < strlen(compl )) {
+ if (rl_insert_text(compl +strlen(argv[argc - 1])) < 0) {
+ free(compl );
+ goto end;
+ }
+ free(compl );
+ rc = 0;
+ goto end;
+ }
+ /* No completion or several completion available. */
+ free(compl );
+ fprintf(stderr, "\n");
+ rl_forced_update_display();
+ rc = 0;
+end:
+ free(line);
+ tokenize_free(argc, argv);
+ return rc;
+}
+
+static int
+cmd_complete(int count, int ch)
+{
+ return _cmd_complete(0);
+}
+
+static int
+cmd_help(int count, int ch)
+{
+ return _cmd_complete(1);
+}
+#else
+static char *
+readline(const char *p)
+{
+ static char line[2048];
+ fprintf(stderr, "%s", p);
+ fflush(stderr);
+ if (fgets(line, sizeof(line) - 2, stdin) == NULL) return NULL;
+ return strdup(line);
+}
+#endif
+
+/**
+ * Execute a tokenized command and display its output.
+ *
+ * @param conn The connection to lldpd.
+ * @param fmt Output format.
+ * @param argc Number of arguments.
+ * @param argv Array of arguments.
+ * @return 0 if an error occurred, 1 otherwise
+ */
+static int
+cmd_exec(lldpctl_conn_t *conn, const char *fmt, int argc, const char **argv)
+{
+ /* Init output formatter */
+ struct writer *w;
+
+ if (strcmp(fmt, "plain") == 0)
+ w = txt_init(stdout);
+ else if (strcmp(fmt, "keyvalue") == 0)
+ w = kv_init(stdout);
+ else if (strcmp(fmt, "json") == 0)
+ w = json_init(stdout, 1);
+ else if (strcmp(fmt, "json0") == 0)
+ w = json_init(stdout, 0);
+#ifdef USE_XML
+ else if (strcmp(fmt, "xml") == 0)
+ w = xml_init(stdout);
+#endif
+ else {
+ log_warnx("lldpctl", "unknown output format \"%s\"", fmt);
+ w = txt_init(stdout);
+ }
+
+ /* Execute command */
+ int rc = commands_execute(conn, w, root, argc, argv, is_privileged());
+ if (rc != 0) {
+ log_info("lldpctl", "an error occurred while executing last command");
+ w->finish(w);
+ return 0;
+ }
+ w->finish(w);
+ return 1;
+}
+
+/**
+ * Execute a command line and display its output.
+ *
+ * @param conn The connection to lldpd.
+ * @param fmt Output format.
+ * @param line Line to execute.
+ * @return -1 if an error occurred, 0 if nothing was executed. 1 otherwise.
+ */
+static int
+parse_and_exec(lldpctl_conn_t *conn, const char *fmt, const char *line)
+{
+ int cargc = 0;
+ char **cargv = NULL;
+ int n;
+ log_debug("lldpctl", "tokenize command line");
+ n = tokenize_line(line, &cargc, &cargv);
+ switch (n) {
+ case -1:
+ log_warnx("lldpctl", "internal error while tokenizing");
+ return -1;
+ case 1:
+ log_warnx("lldpctl", "unmatched quotes");
+ return -1;
+ }
+ if (cargc != 0) n = cmd_exec(conn, fmt, cargc, (const char **)cargv);
+ tokenize_free(cargc, cargv);
+ return (cargc == 0) ? 0 : (n == 0) ? -1 : 1;
+}
+
+static struct cmd_node *
+register_commands()
+{
+ root = commands_root();
+ register_commands_show(root);
+ register_commands_watch(root);
+ commands_new(commands_privileged(commands_new(root, "update",
+ "Update information and send LLDPU on all ports", NULL, NULL,
+ NULL)),
+ NEWLINE, "Update information and send LLDPU on all ports", NULL, cmd_update,
+ NULL);
+ register_commands_configure(root);
+ commands_hidden(commands_new(root, "complete",
+ "Get possible completions from a given command", NULL,
+ cmd_store_env_and_pop, "complete"));
+ commands_new(root, "help", "Get help on a possible command", NULL,
+ cmd_store_env_and_pop, "help");
+ commands_new(commands_new(root, "pause", "Pause lldpd operations", NULL, NULL,
+ NULL),
+ NEWLINE, "Pause lldpd operations", NULL, cmd_pause, NULL);
+ commands_new(commands_new(root, "resume", "Resume lldpd operations", NULL, NULL,
+ NULL),
+ NEWLINE, "Resume lldpd operations", NULL, cmd_resume, NULL);
+ commands_new(commands_new(root, "exit", "Exit interpreter", NULL, NULL, NULL),
+ NEWLINE, "Exit interpreter", NULL, cmd_exit, NULL);
+ return root;
+}
+
+struct input {
+ TAILQ_ENTRY(input) next;
+ char *name;
+};
+TAILQ_HEAD(inputs, input);
+static int
+filter(const struct dirent *dir)
+{
+ if (strlen(dir->d_name) < 5) return 0;
+ if (strcmp(dir->d_name + strlen(dir->d_name) - 5, ".conf")) return 0;
+ return 1;
+}
+
+/**
+ * Append a new input file/directory to the list of inputs.
+ *
+ * @param arg Directory or file name to add.
+ * @param inputs List of inputs
+ * @param acceptdir 1 if we accept a directory, 0 otherwise
+ */
+static void
+input_append(const char *arg, struct inputs *inputs, int acceptdir, int warn)
+{
+ struct stat statbuf;
+ if (stat(arg, &statbuf) == -1) {
+ if (warn) {
+ log_warn("lldpctl",
+ "cannot find configuration file/directory %s", arg);
+ } else {
+ log_debug("lldpctl",
+ "cannot find configuration file/directory %s", arg);
+ }
+ return;
+ }
+
+ if (!S_ISDIR(statbuf.st_mode)) {
+ struct input *input = malloc(sizeof(struct input));
+ if (!input) {
+ log_warn("lldpctl", "not enough memory to process %s", arg);
+ return;
+ }
+ log_debug("lldpctl", "input: %s", arg);
+ input->name = strdup(arg);
+ TAILQ_INSERT_TAIL(inputs, input, next);
+ return;
+ }
+ if (!acceptdir) {
+ log_debug("lldpctl", "skip directory %s", arg);
+ return;
+ }
+
+ struct dirent **namelist = NULL;
+ int n = scandir(arg, &namelist, filter, alphasort);
+ if (n < 0) {
+ log_warnx("lldpctl", "unable to read directory %s", arg);
+ return;
+ }
+ for (int i = 0; i < n; i++) {
+ char *fullname;
+ if (asprintf(&fullname, "%s/%s", arg, namelist[i]->d_name) != -1) {
+ input_append(fullname, inputs, 0, 1);
+ free(fullname);
+ }
+ free(namelist[i]);
+ }
+ free(namelist);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ch, debug = 0, use_syslog = 0, rc = EXIT_FAILURE;
+ const char *fmt = "plain";
+ lldpctl_conn_t *conn = NULL;
+ const char *options = is_lldpctl(argv[0]) ? "hdvf:u:" : "hdsvf:c:C:u:";
+ lldpctl_atom_t *configuration;
+
+ int gotinputs = 0, version = 0;
+ struct inputs inputs;
+ TAILQ_INIT(&inputs);
+
+ ctlname = lldpctl_get_default_transport();
+
+ signal(SIGHUP, SIG_IGN);
+
+ /* Get and parse command line options */
+ optind = 1;
+ while ((ch = getopt(argc, argv, options)) != -1) {
+ switch (ch) {
+ case 'd':
+ if (use_syslog)
+ use_syslog = 0;
+ else
+ debug++;
+ break;
+ case 's':
+ if (debug == 0)
+ use_syslog = 1;
+ else
+ debug--;
+ break;
+ case 'h':
+ usage();
+ break;
+ case 'u':
+ ctlname = optarg;
+ break;
+ case 'v':
+ version++;
+ break;
+ case 'f':
+ fmt = optarg;
+ break;
+ case 'C':
+ case 'c':
+ if (!gotinputs) {
+ log_init(use_syslog, debug, __progname);
+ lldpctl_log_level(debug + 1);
+ gotinputs = 1;
+ }
+ input_append(optarg, &inputs, 1, ch == 'c');
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (version) {
+ version_display(stdout, "lldpcli", version > 1);
+ exit(0);
+ }
+
+ if (!gotinputs) {
+ log_init(use_syslog, debug, __progname);
+ lldpctl_log_level(debug + 1);
+ }
+
+ /* Disable SIGPIPE */
+ signal(SIGPIPE, SIG_IGN);
+
+ /* Register commands */
+ root = register_commands();
+
+ /* Make a connection */
+ log_debug("lldpctl", "connect to lldpd");
+ conn = lldpctl_new_name(ctlname, NULL, NULL, NULL);
+ if (conn == NULL) goto end;
+
+ /* Check we have a working connection */
+ if ((configuration = lldpctl_get_configuration(conn)) == NULL) {
+ /* ctl.c already outputs an error */
+ goto end;
+ }
+ lldpctl_atom_dec_ref(configuration);
+
+ /* Process file inputs */
+ while (gotinputs && !TAILQ_EMPTY(&inputs)) {
+ /* coverity[use_after_free]
+ TAILQ_REMOVE does the right thing */
+ struct input *first = TAILQ_FIRST(&inputs);
+ log_debug("lldpctl", "process: %s", first->name);
+ FILE *file = fopen(first->name, "r");
+ if (file) {
+ size_t n;
+ ssize_t len;
+ char *line;
+ while (line = NULL, len = 0,
+ (len = getline(&line, &n, file)) > 0) {
+ if (line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ parse_and_exec(conn, fmt, line);
+ }
+ free(line);
+ }
+ free(line);
+ fclose(file);
+ } else {
+ log_warn("lldpctl", "unable to open %s", first->name);
+ }
+ TAILQ_REMOVE(&inputs, first, next);
+ free(first->name);
+ free(first);
+ }
+
+ /* Process additional arguments. First if we are lldpctl (interfaces) */
+ if (is_lldpctl(NULL)) {
+ char *line = NULL;
+ for (int i = optind; i < argc; i++) {
+ char *prev = line;
+ if (asprintf(&line, "%s%s%s", prev ? prev : "show neigh ports ",
+ argv[i], (i == argc - 1) ? " details" : ",") == -1) {
+ log_warnx("lldpctl",
+ "not enough memory to build list of interfaces");
+ free(prev);
+ goto end;
+ }
+ free(prev);
+ }
+ if (line == NULL && (line = strdup("show neigh details")) == NULL) {
+ log_warnx("lldpctl", "not enough memory to build command line");
+ goto end;
+ }
+ log_debug("lldpctl", "execute %s", line);
+ if (parse_and_exec(conn, fmt, line) != -1) rc = EXIT_SUCCESS;
+ free(line);
+ goto end;
+ }
+
+ /* Then, if we are regular lldpcli (command line) */
+ if (optind < argc) {
+ const char **cargv;
+ int cargc;
+ cargv = &((const char **)argv)[optind];
+ cargc = argc - optind;
+ if (cmd_exec(conn, fmt, cargc, cargv) == 1) rc = EXIT_SUCCESS;
+ goto end;
+ }
+
+ if (gotinputs) {
+ rc = EXIT_SUCCESS;
+ goto end;
+ }
+
+ /* Interactive session */
+#ifdef HAVE_LIBREADLINE
+ rl_bind_key('?', cmd_help);
+ rl_bind_key('\t', cmd_complete);
+#endif
+ char *line = NULL;
+ do {
+ if ((line = readline(prompt()))) {
+ int n = parse_and_exec(conn, fmt, line);
+ if (n != 0) {
+#ifdef HAVE_READLINE_HISTORY
+ add_history(line);
+#endif
+ }
+ free(line);
+ }
+ } while (!must_exit && line != NULL);
+ rc = EXIT_SUCCESS;
+
+end:
+ while (!TAILQ_EMPTY(&inputs)) {
+ /* coverity[use_after_free]
+ TAILQ_REMOVE does the right thing */
+ struct input *first = TAILQ_FIRST(&inputs);
+ TAILQ_REMOVE(&inputs, first, next);
+ free(first->name);
+ free(first);
+ }
+ if (conn) lldpctl_release(conn);
+ if (root) commands_free(root);
+ return rc;
+}
diff --git a/src/client/lldpctl.8 b/src/client/lldpctl.8
new file mode 100644
index 0000000..68a27a1
--- /dev/null
+++ b/src/client/lldpctl.8
@@ -0,0 +1 @@
+.so man8/lldpcli.8
diff --git a/src/client/misc.c b/src/client/misc.c
new file mode 100644
index 0000000..827bd90
--- /dev/null
+++ b/src/client/misc.c
@@ -0,0 +1,73 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "client.h"
+#include <string.h>
+#include <ctype.h>
+
+/**
+ * Check if an element is present in a comma-separated list.
+ *
+ * @param list Comma-separated list of elements.
+ * @param element Element we want to check for.
+ * @return 0 if the element was not found, 1 otherwise.
+ */
+int
+contains(const char *list, const char *element)
+{
+ int len;
+ if (element == NULL || list == NULL) return 0;
+ while (list) {
+ len = strlen(element);
+ if (!strncmp(list, element, len) &&
+ (list[len] == '\0' || list[len] == ','))
+ return 1;
+ list = strchr(list, ',');
+ if (list) list++;
+ }
+ return 0;
+}
+
+/**
+ * Transform a string to a tag. This puts the string into lower space and
+ * replace spaces with '-'. The result is statically allocated.
+ *
+ * @param value String to transform to a tag.
+ * @return The tagged value or the string "none" if @c value is @c NULL
+ */
+const char *
+totag(const char *value)
+{
+ int i;
+ static char *result = NULL;
+ free(result);
+ result = NULL;
+ if (!value) return "none";
+ result = calloc(1, strlen(value) + 1);
+ if (!result) return "none";
+ for (i = 0; i < strlen(value); i++) {
+ switch (value[i]) {
+ case ' ':
+ result[i] = '-';
+ break;
+ default:
+ result[i] = tolower((int)value[i]);
+ break;
+ }
+ }
+ return result;
+}
diff --git a/src/client/show.c b/src/client/show.c
new file mode 100644
index 0000000..f13ae57
--- /dev/null
+++ b/src/client/show.c
@@ -0,0 +1,364 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "client.h"
+#include <string.h>
+#include <limits.h>
+
+/**
+ * Show neighbors.
+ *
+ * The environment will contain the following keys:
+ * - C{ports} list of ports we want to restrict showing.
+ * - C{hidden} if we should show hidden ports.
+ * - C{summary} if we want to show only a summary
+ * - C{detailed} for a detailed overview
+ */
+static int
+cmd_show_neighbors(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ log_debug("lldpctl", "show neighbors data (%s) %s hidden neighbors",
+ cmdenv_get(env, "summary") ? "summary" :
+ cmdenv_get(env, "detailed") ? "detailed" :
+ "normal",
+ cmdenv_get(env, "hidden") ? "with" : "without");
+ if (cmdenv_get(env, "ports"))
+ log_debug("lldpctl", "restrict to the following ports: %s",
+ cmdenv_get(env, "ports"));
+
+ display_interfaces(conn, w, env, !!cmdenv_get(env, "hidden"),
+ cmdenv_get(env, "summary") ? DISPLAY_BRIEF :
+ cmdenv_get(env, "detailed") ? DISPLAY_DETAILS :
+ DISPLAY_NORMAL);
+
+ return 1;
+}
+
+/**
+ * Show interfaces.
+ *
+ * The environment will contain the following keys:
+ * - C{ports} list of ports we want to restrict showing.
+ * - C{hidden} if we should show hidden ports.
+ * - C{summary} if we want to show only a summary
+ * - C{detailed} for a detailed overview
+ */
+static int
+cmd_show_interfaces(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ log_debug("lldpctl", "show interfaces data (%s) %s hidden interfaces",
+ cmdenv_get(env, "summary") ? "summary" :
+ cmdenv_get(env, "detailed") ? "detailed" :
+ "normal",
+ cmdenv_get(env, "hidden") ? "with" : "without");
+ if (cmdenv_get(env, "ports"))
+ log_debug("lldpctl", "restrict to the following ports: %s",
+ cmdenv_get(env, "ports"));
+
+ display_local_interfaces(conn, w, env, !!cmdenv_get(env, "hidden"),
+ cmdenv_get(env, "summary") ? DISPLAY_BRIEF :
+ cmdenv_get(env, "detailed") ? DISPLAY_DETAILS :
+ DISPLAY_NORMAL);
+
+ return 1;
+}
+
+/**
+ * Show chassis.
+ *
+ * The environment will contain the following keys:
+ * - C{summary} if we want to show only a summary
+ * - C{detailed} for a detailed overview
+ */
+static int
+cmd_show_chassis(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ log_debug("lldpctl", "show chassis data (%s)",
+ cmdenv_get(env, "summary") ? "summary" :
+ cmdenv_get(env, "detailed") ? "detailed" :
+ "normal");
+
+ display_local_chassis(conn, w, env,
+ cmdenv_get(env, "summary") ? DISPLAY_BRIEF :
+ cmdenv_get(env, "detailed") ? DISPLAY_DETAILS :
+ DISPLAY_NORMAL);
+
+ return 1;
+}
+
+/**
+ * Show stats.
+ *
+ * The environment will contain the following keys:
+ * - C{ports} list of ports we want to restrict showing.
+ * - C{summary} summary of stats
+ */
+static int
+cmd_show_interface_stats(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *arg)
+{
+ log_debug("lldpctl", "show stats data");
+ if (cmdenv_get(env, "ports"))
+ log_debug("lldpctl", "restrict to the following ports: %s",
+ cmdenv_get(env, "ports"));
+ if (cmdenv_get(env, "summary"))
+ log_debug("lldpctl", "show summary of stats across ports");
+
+ display_interfaces_stats(conn, w, env);
+
+ return 1;
+}
+
+static int
+cmd_check_no_detailed_nor_summary(struct cmd_env *env, const void *arg)
+{
+ if (cmdenv_get(env, "detailed")) return 0;
+ if (cmdenv_get(env, "summary")) return 0;
+ return 1;
+}
+
+/**
+ * Show running configuration.
+ */
+static int
+cmd_show_configuration(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, const void *arg)
+{
+ log_debug("lldpctl", "show running configuration");
+ display_configuration(conn, w);
+ return 1;
+}
+
+struct watcharg {
+ struct cmd_env *env;
+ struct writer *w;
+ size_t nb;
+};
+
+/**
+ * Callback for the next function to display a new neighbor.
+ */
+static void
+watchcb(lldpctl_change_t type, lldpctl_atom_t *interface, lldpctl_atom_t *neighbor,
+ void *data)
+{
+ struct watcharg *wa = data;
+ struct cmd_env *env = wa->env;
+ struct writer *w = wa->w;
+ const char *interfaces = cmdenv_get(env, "ports");
+ const char *proto_str;
+ int protocol = LLDPD_MODE_MAX;
+
+ if (interfaces &&
+ !contains(interfaces,
+ lldpctl_atom_get_str(interface, lldpctl_k_interface_name)))
+ return;
+
+ /* user might have specified protocol to filter display results */
+ proto_str = cmdenv_get(env, "protocol");
+
+ if (proto_str) {
+ log_debug("display", "filter protocol: %s ", proto_str);
+
+ protocol = 0; /* unsupported */
+ for (lldpctl_map_t *protocol_map =
+ lldpctl_key_get_map(lldpctl_k_port_protocol);
+ protocol_map->string; protocol_map++) {
+ if (!strcasecmp(proto_str, protocol_map->string)) {
+ protocol = protocol_map->value;
+ break;
+ }
+ }
+ }
+
+ switch (type) {
+ case lldpctl_c_deleted:
+ tag_start(w, "lldp-deleted", "LLDP neighbor deleted");
+ break;
+ case lldpctl_c_updated:
+ tag_start(w, "lldp-updated", "LLDP neighbor updated");
+ break;
+ case lldpctl_c_added:
+ tag_start(w, "lldp-added", "LLDP neighbor added");
+ break;
+ default:
+ return;
+ }
+ display_interface(NULL, w, 1, interface, neighbor,
+ cmdenv_get(env, "summary") ? DISPLAY_BRIEF :
+ cmdenv_get(env, "detailed") ? DISPLAY_DETAILS :
+ DISPLAY_NORMAL,
+ protocol);
+ tag_end(w);
+ wa->nb++;
+}
+
+/**
+ * Watch for neighbor changes.
+ */
+static int
+cmd_watch_neighbors(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ const void *arg)
+{
+ struct watcharg wa = { .env = env, .w = w, .nb = 0 };
+ const char *limit_str = cmdenv_get(env, "limit");
+ size_t limit = 0;
+
+ if (limit_str) {
+ const char *errstr;
+ limit = strtonum(limit_str, 1, LLONG_MAX, &errstr);
+ if (errstr != NULL) {
+ log_warnx("lldpctl", "specified limit (%s) is %s and ignored",
+ limit_str, errstr);
+ }
+ }
+
+ log_debug("lldpctl", "watch for neighbor changes");
+ if (lldpctl_watch_callback2(conn, watchcb, &wa) < 0) {
+ log_warnx("lldpctl", "unable to watch for neighbors. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+ while (1) {
+ if (lldpctl_watch(conn) < 0) {
+ log_warnx("lldpctl", "unable to watch for neighbors. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+ if (limit > 0 && wa.nb >= limit) return 1;
+ }
+ return 0;
+}
+
+/**
+ * Register common subcommands for `watch` and `show neighbors` and `show chassis'
+ */
+static void
+register_common_commands(struct cmd_node *root, int neighbor)
+{
+ /* With more details */
+ commands_new(root, "details", "With more details",
+ cmd_check_no_detailed_nor_summary, cmd_store_env_and_pop, "detailed");
+
+ /* With less details */
+ commands_new(root, "summary", "With less details",
+ cmd_check_no_detailed_nor_summary, cmd_store_env_and_pop, "summary");
+
+ if (!neighbor) return;
+
+ /* With hidden neighbors */
+ commands_new(root, "hidden", "Include hidden neighbors", cmd_check_no_env,
+ cmd_store_env_and_pop, "hidden");
+
+ /* Some specific port */
+ cmd_restrict_ports(root);
+
+ /* Specific protocol */
+ cmd_restrict_protocol(root);
+}
+
+/**
+ * Register sub command summary
+ */
+static void
+register_summary_command(struct cmd_node *root)
+{
+ commands_new(root, "summary", "With less details",
+ cmd_check_no_detailed_nor_summary, cmd_store_env_and_pop, "summary");
+}
+
+/**
+ * Register subcommands to `show`
+ *
+ * @param root Root node
+ */
+void
+register_commands_show(struct cmd_node *root)
+{
+ struct cmd_node *show = commands_new(root, "show",
+ "Show running system information", NULL, NULL, NULL);
+ struct cmd_node *neighbors =
+ commands_new(show, "neighbors", "Show neighbors data", NULL, NULL, NULL);
+
+ struct cmd_node *interfaces =
+ commands_new(show, "interfaces", "Show interfaces data", NULL, NULL, NULL);
+
+ struct cmd_node *chassis =
+ commands_new(show, "chassis", "Show local chassis data", NULL, NULL, NULL);
+
+ struct cmd_node *stats =
+ commands_new(show, "statistics", "Show statistics", NULL, NULL, NULL);
+
+ /* Neighbors data */
+ commands_new(neighbors, NEWLINE, "Show neighbors data", NULL,
+ cmd_show_neighbors, NULL);
+
+ register_common_commands(neighbors, 1);
+
+ /* Interfaces data */
+ commands_new(interfaces, NEWLINE, "Show interfaces data", NULL,
+ cmd_show_interfaces, NULL);
+
+ cmd_restrict_ports(interfaces);
+ register_common_commands(interfaces, 0);
+
+ /* Chassis data */
+ commands_new(chassis, NEWLINE, "Show local chassis data", NULL,
+ cmd_show_chassis, NULL);
+
+ register_common_commands(chassis, 0);
+
+ /* Stats data */
+ commands_new(stats, NEWLINE, "Show stats data", NULL, cmd_show_interface_stats,
+ NULL);
+
+ cmd_restrict_ports(stats);
+ register_summary_command(stats);
+
+ /* Register "show configuration" and "show running-configuration" */
+ commands_new(commands_new(show, "configuration", "Show running configuration",
+ NULL, NULL, NULL),
+ NEWLINE, "Show running configuration", NULL, cmd_show_configuration, NULL);
+ commands_new(commands_new(show, "running-configuration",
+ "Show running configuration", NULL, NULL, NULL),
+ NEWLINE, "Show running configuration", NULL, cmd_show_configuration, NULL);
+}
+
+/**
+ * Register subcommands to `watch`
+ *
+ * @param root Root node
+ */
+void
+register_commands_watch(struct cmd_node *root)
+{
+ struct cmd_node *watch =
+ commands_new(root, "watch", "Monitor neighbor changes", NULL, NULL, NULL);
+
+ commands_new(watch, NEWLINE, "Monitor neighbors change", NULL,
+ cmd_watch_neighbors, NULL);
+
+ commands_new(commands_new(watch, "limit", "Don't show more than X events",
+ cmd_check_no_env, NULL, "limit"),
+ NULL, "Stop after getting X events", NULL, cmd_store_env_value_and_pop2,
+ "limit");
+
+ register_common_commands(watch, 1);
+}
diff --git a/src/client/text_writer.c b/src/client/text_writer.c
new file mode 100644
index 0000000..95990e7
--- /dev/null
+++ b/src/client/text_writer.c
@@ -0,0 +1,183 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2010 Andreas Hofmeister <andi@collax.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "writer.h"
+#include "../log.h"
+
+static char sep[] =
+ "-------------------------------------------------------------------------------";
+
+struct txt_writer_private {
+ FILE *fh;
+ int level;
+ int attrs;
+ int lastnl;
+};
+
+static void
+txt_fprintf(struct txt_writer_private *p, const char *fmt, ...)
+{
+ va_list ap;
+
+ // Don't add a newline if we already had one.
+ while (p->lastnl && fmt[0] == '\n') {
+ fmt += 1;
+ }
+ if (fmt[0] == '\0') return;
+
+ va_start(ap, fmt);
+ vfprintf(p->fh, fmt, ap);
+ va_end(ap);
+
+ p->lastnl = (strlen(fmt) > 0 && fmt[strlen(fmt) - 1] == '\n');
+}
+
+static void
+txt_start(struct writer *w, const char *tag, const char *descr)
+{
+ struct txt_writer_private *p = w->priv;
+ int i = 0;
+ char buf[128];
+
+ if (p->level == 0) {
+ txt_fprintf(p, "%s\n", sep);
+ } else if (!p->lastnl) {
+ txt_fprintf(p, "\n");
+ }
+
+ for (i = 1; i < p->level; i++) {
+ txt_fprintf(p, " ");
+ }
+
+ snprintf(buf, sizeof(buf), "%s:", descr);
+ txt_fprintf(p, "%-13s", buf);
+
+ if (p->level == 0) txt_fprintf(p, "\n%s", sep);
+
+ p->level++;
+ p->attrs = 0;
+}
+
+static void
+txt_attr(struct writer *w, const char *tag, const char *descr, const char *value)
+{
+ struct txt_writer_private *p = w->priv;
+
+ if (descr == NULL || strlen(descr) == 0) {
+ txt_fprintf(p, "%s%s", (p->attrs > 0 ? ", " : " "),
+ value ? value : "(none)");
+ } else {
+ txt_fprintf(p, "%s%s: %s", (p->attrs > 0 ? ", " : " "), descr,
+ value ? value : "(none)");
+ }
+
+ p->attrs++;
+}
+
+static void
+txt_data(struct writer *w, const char *data)
+{
+ struct txt_writer_private *p = w->priv;
+ char *nl, *begin;
+ char *v = begin = data ? strdup(data) : NULL;
+
+ if (v == NULL) {
+ txt_fprintf(p, " %s", data ? data : "(none)");
+ return;
+ }
+
+ txt_fprintf(p, " ");
+ while ((nl = strchr(v, '\n')) != NULL) {
+ *nl = '\0';
+ txt_fprintf(p, "%s\n", v);
+ v = nl + 1;
+
+ /* Indent */
+ int i;
+ for (i = 1; i < p->level - 1; i++) {
+ txt_fprintf(p, " ");
+ }
+ txt_fprintf(p, "%-14s", " ");
+ }
+ txt_fprintf(p, "%s", v);
+ free(begin);
+}
+
+static void
+txt_end(struct writer *w)
+{
+ struct txt_writer_private *p = w->priv;
+ p->level--;
+
+ if (p->level == 1) {
+ txt_fprintf(p, "\n%s", sep);
+ fflush(p->fh);
+ }
+}
+
+static void
+txt_finish(struct writer *w)
+{
+ struct txt_writer_private *p = w->priv;
+
+ txt_fprintf(p, "\n");
+
+ free(w->priv);
+ w->priv = NULL;
+
+ free(w);
+}
+
+struct writer *
+txt_init(FILE *fh)
+{
+
+ struct writer *result;
+ struct txt_writer_private *priv;
+
+ priv = malloc(sizeof(*priv));
+ if (!priv) {
+ fatalx("lldpctl", "out of memory");
+ return NULL;
+ }
+
+ priv->fh = fh;
+ priv->level = 0;
+ priv->attrs = 0;
+ priv->lastnl = 1;
+
+ result = malloc(sizeof(struct writer));
+ if (!result) {
+ fatalx("lldpctl", "out of memory");
+ free(priv);
+ return NULL;
+ }
+
+ result->priv = priv;
+ result->start = txt_start;
+ result->attr = txt_attr;
+ result->data = txt_data;
+ result->end = txt_end;
+ result->finish = txt_finish;
+
+ return result;
+}
diff --git a/src/client/tokenizer.c b/src/client/tokenizer.c
new file mode 100644
index 0000000..c4cdda6
--- /dev/null
+++ b/src/client/tokenizer.c
@@ -0,0 +1,116 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2013 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "client.h"
+
+#include <string.h>
+/**
+ * Tokenize the given line. We support quoted strings and escaped characters
+ * with backslash.
+ *
+ * @param line Line to tokenize.
+ * @param argv Will get an array of arguments tokenized.
+ * @param argc Will get the number of tokenized arguments.
+ * @return 0 on success, -1 on internal error, 1 on unmatched quotes
+ */
+int
+tokenize_line(const char *line, int *argc, char ***argv)
+{
+ int iargc = 0;
+ char **iargv = NULL;
+ const char ifs[] = " \n\t";
+ const char quotes[] = "'\"";
+ const char escapes[] = "\\";
+ char empty = 2; /* Empty character, will be removed from output
+ * but will mark a word. */
+
+ /* Escape handle. Also escape quoted characters. */
+ int escaped = 0;
+ int ipos = 0;
+ char quote = 0;
+ char input[2 * strlen(line) + 3]; /* 3 = 2 for '\n ' and 1 for \0 */
+ memset(input, 0, 2 * strlen(line) + 3);
+ for (int pos = 0; line[pos]; pos++) {
+ if (line[pos] == '#' && !escaped && !quote) break;
+ if (!escaped && strchr(escapes, line[pos]))
+ escaped = 1;
+ else if (!escaped && strchr(quotes, line[pos]) && !quote) {
+ input[ipos++] = empty;
+ input[ipos++] = '!';
+ quote = line[pos];
+ } else if (!escaped && quote == line[pos])
+ quote = 0;
+ else {
+ input[ipos++] = line[pos];
+ input[ipos++] = (escaped || quote) ? '!' : ' ';
+ escaped = 0;
+ }
+ }
+ if (escaped || quote) return 1;
+ /* Trick to not have to handle \0 in a special way */
+ input[ipos++] = ifs[0];
+ input[ipos++] = ' ';
+
+ /* Tokenize, we don't have to handle quotes anymore */
+ int wbegin = -1; /* Offset of the beginning of the current word */
+
+#define CURRENT (input[2 * pos])
+#define ESCAPED (input[2 * pos + 1] != ' ')
+ for (int pos = 0; CURRENT; pos++) {
+ if (wbegin == -1) {
+ if (!ESCAPED && strchr(ifs, CURRENT))
+ /* IFS while not in a word, continue. */
+ continue;
+ /* Start a word. */
+ wbegin = pos;
+ continue;
+ }
+ if (ESCAPED || !strchr(ifs, CURRENT)) /* Regular character in a word. */
+ continue;
+
+ /* End of word. */
+ char *word = calloc(1, pos - wbegin + 1);
+ if (!word) goto error;
+ int i, j;
+ for (i = wbegin, j = 0; i != pos; i++)
+ if (input[2 * i] != empty) word[j++] = input[2 * i];
+ char **nargv = realloc(iargv, sizeof(char *) * (iargc + 1));
+ if (!nargv) {
+ free(word);
+ goto error;
+ }
+ nargv[iargc++] = word;
+ iargv = nargv;
+ wbegin = -1;
+ }
+
+ *argc = iargc;
+ *argv = iargv;
+ return 0;
+
+error:
+ tokenize_free(iargc, iargv);
+ return -1;
+}
+
+void
+tokenize_free(int argc, char **argv)
+{
+ while (argc)
+ free(argv[--argc]);
+ free(argv);
+}
diff --git a/src/client/utf8.c b/src/client/utf8.c
new file mode 100644
index 0000000..4dbf178
--- /dev/null
+++ b/src/client/utf8.c
@@ -0,0 +1,90 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ Copyright (c) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#include <stddef.h>
+#include "writer.h"
+
+/*
+ * Validate a single UTF-8 character starting at @s.
+ * The string must be null-terminated.
+ *
+ * If it's valid, return its length (1 thru 4).
+ * If it's invalid or clipped, return 0.
+ *
+ * This function implements the syntax given in RFC3629, which is
+ * the same as that given in The Unicode Standard, Version 6.0.
+ *
+ * It has the following properties:
+ *
+ * * All codepoints U+0000..U+10FFFF may be encoded,
+ * except for U+D800..U+DFFF, which are reserved
+ * for UTF-16 surrogate pair encoding.
+ * * UTF-8 byte sequences longer than 4 bytes are not permitted,
+ * as they exceed the range of Unicode.
+ * * The sixty-six Unicode "non-characters" are permitted
+ * (namely, U+FDD0..U+FDEF, U+xxFFFE, and U+xxFFFF).
+ */
+size_t
+utf8_validate_cz(const char *s)
+{
+ unsigned char c = *s++;
+
+ if (c <= 0x7F) { /* 00..7F */
+ return 1;
+ } else if (c <= 0xC1) { /* 80..C1 */
+ /* Disallow overlong 2-byte sequence. */
+ return 0;
+ } else if (c <= 0xDF) { /* C2..DF */
+ /* Make sure subsequent byte is in the range 0x80..0xBF. */
+ if (((unsigned char)*s++ & 0xC0) != 0x80) return 0;
+
+ return 2;
+ } else if (c <= 0xEF) { /* E0..EF */
+ /* Disallow overlong 3-byte sequence. */
+ if (c == 0xE0 && (unsigned char)*s < 0xA0) return 0;
+
+ /* Disallow U+D800..U+DFFF. */
+ if (c == 0xED && (unsigned char)*s > 0x9F) return 0;
+
+ /* Make sure subsequent bytes are in the range 0x80..0xBF. */
+ if (((unsigned char)*s++ & 0xC0) != 0x80) return 0;
+ if (((unsigned char)*s++ & 0xC0) != 0x80) return 0;
+
+ return 3;
+ } else if (c <= 0xF4) { /* F0..F4 */
+ /* Disallow overlong 4-byte sequence. */
+ if (c == 0xF0 && (unsigned char)*s < 0x90) return 0;
+
+ /* Disallow codepoints beyond U+10FFFF. */
+ if (c == 0xF4 && (unsigned char)*s > 0x8F) return 0;
+
+ /* Make sure subsequent bytes are in the range 0x80..0xBF. */
+ if (((unsigned char)*s++ & 0xC0) != 0x80) return 0;
+ if (((unsigned char)*s++ & 0xC0) != 0x80) return 0;
+ if (((unsigned char)*s++ & 0xC0) != 0x80) return 0;
+
+ return 4;
+ } else { /* F5..FF */
+ return 0;
+ }
+}
diff --git a/src/client/writer.h b/src/client/writer.h
new file mode 100644
index 0000000..c66f699
--- /dev/null
+++ b/src/client/writer.h
@@ -0,0 +1,57 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2010 Andreas Hofmeister <andi@collax.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _WRITER_H
+#define _WRITER_H
+
+#include <stdio.h>
+#include <config.h>
+
+struct writer {
+ void *priv;
+ void (*start)(struct writer *, const char *tag, const char *descr);
+ void (*attr)(struct writer *, const char *tag, const char *descr,
+ const char *value);
+ void (*data)(struct writer *, const char *data);
+ void (*end)(struct writer *);
+ void (*finish)(struct writer *);
+};
+
+#define tag_start(w, ...) w->start(w, ##__VA_ARGS__)
+#define tag_attr(w, ...) w->attr(w, ##__VA_ARGS__)
+#define tag_data(w, ...) w->data(w, ##__VA_ARGS__)
+#define tag_end(w, ...) w->end(w, ##__VA_ARGS__)
+#define tag_datatag(w, t, d, v) \
+ do { \
+ if ((v) == NULL) break; \
+ w->start(w, t, d); \
+ w->data(w, v); \
+ w->end(w); \
+ } while (0);
+
+extern struct writer *txt_init(FILE *);
+extern struct writer *kv_init(FILE *);
+extern struct writer *json_init(FILE *, int);
+
+#ifdef USE_XML
+extern struct writer *xml_init(FILE *);
+#endif
+
+/* utf8.c */
+size_t utf8_validate_cz(const char *s);
+
+#endif /* _WRITER_H */
diff --git a/src/client/xml_writer.c b/src/client/xml_writer.c
new file mode 100644
index 0000000..0c5efb5
--- /dev/null
+++ b/src/client/xml_writer.c
@@ -0,0 +1,158 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2010 Andreas Hofmeister <andi@collax.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wdocumentation"
+#endif
+#include <libxml/encoding.h>
+#include <libxml/xmlwriter.h>
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#endif
+
+#include "writer.h"
+#include "../log.h"
+
+struct xml_writer_private {
+ FILE *fh;
+ ssize_t depth;
+ xmlTextWriterPtr xw;
+ xmlDocPtr doc;
+};
+
+static void
+xml_new_writer(struct xml_writer_private *priv)
+{
+ priv->xw = xmlNewTextWriterDoc(&(priv->doc), 0);
+ if (!priv->xw) fatalx("lldpctl", "cannot create xml writer");
+
+ xmlTextWriterSetIndent(priv->xw, 4);
+
+ if (xmlTextWriterStartDocument(priv->xw, NULL, "UTF-8", NULL) < 0)
+ fatalx("lldpctl", "cannot start xml document");
+}
+
+static void
+xml_start(struct writer *w, const char *tag, const char *descr)
+{
+ struct xml_writer_private *p = w->priv;
+
+ if (p->depth == 0) xml_new_writer(p);
+
+ if (xmlTextWriterStartElement(p->xw, BAD_CAST tag) < 0)
+ log_warnx("lldpctl", "cannot start '%s' element", tag);
+
+ if (descr && (strlen(descr) > 0)) {
+ if (xmlTextWriterWriteFormatAttribute(p->xw, BAD_CAST "label", "%s",
+ descr) < 0)
+ log_warnx("lldpctl",
+ "cannot add attribute 'label' to element %s", tag);
+ }
+
+ p->depth++;
+}
+
+static void
+xml_attr(struct writer *w, const char *tag, const char *descr, const char *value)
+{
+ struct xml_writer_private *p = w->priv;
+
+ if (xmlTextWriterWriteFormatAttribute(p->xw, BAD_CAST tag, "%s",
+ value ? value : "") < 0)
+ log_warnx("lldpctl", "cannot add attribute %s with value %s", tag,
+ value ? value : "(none)");
+}
+
+static void
+xml_data(struct writer *w, const char *data)
+{
+ struct xml_writer_private *p = w->priv;
+ if (xmlTextWriterWriteString(p->xw, BAD_CAST(data ? data : "")) < 0)
+ log_warnx("lldpctl", "cannot add '%s' as data to element",
+ data ? data : "(none)");
+}
+
+static void
+xml_end(struct writer *w)
+{
+ struct xml_writer_private *p = w->priv;
+
+ if (xmlTextWriterEndElement(p->xw) < 0)
+ log_warnx("lldpctl", "cannot end element");
+
+ if (--p->depth == 0) {
+ int failed = 0;
+
+ if (xmlTextWriterEndDocument(p->xw) < 0) {
+ log_warnx("lldpctl", "cannot finish document");
+ failed = 1;
+ }
+
+ xmlFreeTextWriter(p->xw);
+ if (!failed) {
+ xmlDocDump(p->fh, p->doc);
+ fflush(p->fh);
+ }
+ xmlFreeDoc(p->doc);
+ }
+}
+
+static void
+xml_finish(struct writer *w)
+{
+ struct xml_writer_private *p = w->priv;
+ if (p->depth != 0) {
+ log_warnx("lldpctl", "unbalanced tags");
+ /* memory leak... */
+ }
+
+ free(p);
+ free(w);
+}
+
+struct writer *
+xml_init(FILE *fh)
+{
+
+ struct writer *result;
+ struct xml_writer_private *priv;
+
+ priv = malloc(sizeof(*priv));
+ if (!priv) {
+ fatalx("lldpctl", "out of memory");
+ return NULL;
+ }
+ priv->fh = fh;
+ priv->depth = 0;
+
+ result = malloc(sizeof(struct writer));
+ if (!result) fatalx("lldpctl", "out of memory");
+
+ result->priv = priv;
+ result->start = xml_start;
+ result->attr = xml_attr;
+ result->data = xml_data;
+ result->end = xml_end;
+ result->finish = xml_finish;
+
+ return result;
+}