diff options
Diffstat (limited to 'src/client')
-rw-r--r-- | src/client/Makefile.am | 49 | ||||
-rw-r--r-- | src/client/Makefile.in | 1276 | ||||
-rw-r--r-- | src/client/README.conf | 8 | ||||
-rw-r--r-- | src/client/client.h | 148 | ||||
-rw-r--r-- | src/client/commands.c | 855 | ||||
-rw-r--r-- | src/client/completion/_lldpcli | 48 | ||||
-rwxr-xr-x | src/client/completion/lldpcli | 26 | ||||
-rw-r--r-- | src/client/conf-dot3.c | 35 | ||||
-rw-r--r-- | src/client/conf-inv.c | 161 | ||||
-rw-r--r-- | src/client/conf-lldp.c | 762 | ||||
-rw-r--r-- | src/client/conf-med.c | 499 | ||||
-rw-r--r-- | src/client/conf-power.c | 389 | ||||
-rw-r--r-- | src/client/conf-system.c | 602 | ||||
-rw-r--r-- | src/client/conf.c | 44 | ||||
-rw-r--r-- | src/client/display.c | 1039 | ||||
-rw-r--r-- | src/client/json_writer.c | 374 | ||||
-rw-r--r-- | src/client/kv_writer.c | 133 | ||||
-rw-r--r-- | src/client/lldpcli.8.in | 1151 | ||||
-rw-r--r-- | src/client/lldpcli.c | 611 | ||||
-rw-r--r-- | src/client/lldpctl.8 | 1 | ||||
-rw-r--r-- | src/client/misc.c | 73 | ||||
-rw-r--r-- | src/client/show.c | 364 | ||||
-rw-r--r-- | src/client/text_writer.c | 183 | ||||
-rw-r--r-- | src/client/tokenizer.c | 116 | ||||
-rw-r--r-- | src/client/utf8.c | 90 | ||||
-rw-r--r-- | src/client/writer.h | 57 | ||||
-rw-r--r-- | src/client/xml_writer.c | 158 |
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, ¤t->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, ¤t->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(¤t->subentries)) { + TAILQ_FOREACH (candidate, ¤t->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(¤t->children); el != NULL; el = el_next) { + el_next = TAILQ_NEXT(el, next); + json_element_free(el); + TAILQ_REMOVE(¤t->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, ¤t->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; +} |