CLI provides contextual help and completion. It uses libedit.
+ FreeBSD support.
+ OpenBSD support.
+ NetBSD support.
- + Detect interface changes and handle them in less than one
- second. Each port has now its own timer.
- * Features:
+ + Detect interface changes.
+ + CLI for lldpctl.
+ * Other features:
+ Allow to disable LLDP protocol (with `-ll`). In this case, the
first enabled protocol will be used when no neighbor is detected.
+ Allow to filter debug logs using tokens. Add more debug logs.
# Libevent
lldp_CHECK_LIBEVENT
+# editline
+PKG_CHECK_MODULES([EDITLINE], [libedit >= 2.9])
+
#######################
### Options
sbin_PROGRAMS = lldpctl
dist_man_MANS = lldpctl.8
-lldpctl_SOURCES = client.h lldpctl.c display.c writer.h text_writer.c kv_writer.c actions.c
-lldpctl_LDADD = $(top_builddir)/src/libcommon-daemon-client.la $(top_builddir)/src/lib/liblldpctl.la
-lldpctl_CFLAGS = $(AM_CFLAGS)
+lldpctl_SOURCES = client.h lldpctl.c display.c actions.c \
+ commands.c show.c \
+ misc.c \
+ writer.h text_writer.c kv_writer.c
+lldpctl_LDADD = \
+ $(top_builddir)/src/libcommon-daemon-client.la \
+ $(top_builddir)/src/lib/liblldpctl.la \
+ @EDITLINE_LIBS@
+lldpctl_CFLAGS = $(AM_CFLAGS) @EDITLINE_CFLAGS@
if USE_XML
lldpctl_SOURCES += xml_writer.c
#include "client.h"
#include "../log.h"
-/* Helpers to parse a ':'-separated string. */
-static char*
-get_next(lldpctl_atom_t *atom, char *string,
- const char *what, int mandatory)
+static int
+_cmd_medlocation(struct lldpctl_conn_t *conn,
+ struct cmd_env *env, int format)
{
- static char *e2 = NULL;
- static char *e1 = NULL;
- static char *saved_string = NULL;
- static int pos;
- if (e2 != NULL) {
- *e2 = ':';
- e1 = e2 + 1;
- } else if (e1 != NULL) e1 = "";
- if (e1 == NULL || (saved_string != string)) {
- e1 = saved_string = string;
- pos = 1;
- e2 = NULL;
+ 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_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");
+ goto 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);
+ }
+
+ end:
+ lldpctl_atom_dec_ref(med_location);
+ lldpctl_atom_dec_ref(med_locations);
+ lldpctl_atom_dec_ref(port);
}
+ return 1;
+}
+static int
+cmd_medlocation_coordinate(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, void *arg)
+{
+ log_debug("lldpctl", "set MED location coordinate");
+ return _cmd_medlocation(conn, env, LLDP_MED_LOCFORMAT_COORD);
+}
- if (*e1 == '\0') {
- if (mandatory)
- log_warnx(NULL, "unable to find %s in `%s' at pos %d",
- what, string, pos);
- return NULL;
+static int
+cmd_medlocation_address(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, 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, 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, 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 = "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);
}
- e2 = strchr(e1, ':');
- if (e2 != NULL) *e2 = '\0';
- pos++;
- return e1;
+ return 1;
}
static int
-get_next_and_set(lldpctl_atom_t *atom, char *string,
- const char *what, lldpctl_key_t key, int mandatory)
+cmd_medpower(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, void *arg)
{
- char *e1 = get_next(atom, string, what, mandatory);
- if (e1 == NULL) return -1;
- if (lldpctl_atom_set_str(atom, key, e1) == NULL) {
- log_warnx(NULL, "unable to set %s. %s.", what,
- lldpctl_last_strerror(lldpctl_atom_get_connection(atom)));
- return 0;
+ log_debug("lldpctl", "set MED power");
+ 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_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");
+ goto end;
+ }
+
+ 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);
+ }
+
+ end:
+ lldpctl_atom_dec_ref(med_power);
+ lldpctl_atom_dec_ref(port);
}
return 1;
}
-/**
- * Parse dot3 power string.
- *
- * @param value String describing the new value.
- * @param power Atom to use to insert new values.
- * @return 1 on success, 0 otherwise.
- */
static int
-parse_dot3_power(char *value, lldpctl_atom_t *power)
+cmd_dot3power(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, void *arg)
{
- int rc = 0;
-
- if (get_next_and_set(power, value, "device type", lldpctl_k_dot3_power_devicetype, 1) != 1)
- return 0;
- if (get_next_and_set(power, value, "power support", lldpctl_k_dot3_power_supported, 1) != 1)
- return 0;
- if (get_next_and_set(power, value, "power enableness", lldpctl_k_dot3_power_enabled, 1) != 1)
- return 0;
- if (get_next_and_set(power, value, "pair control ability", lldpctl_k_dot3_power_paircontrol, 1) != 1)
- return 0;
- if (get_next_and_set(power, value, "power pairs", lldpctl_k_dot3_power_pairs, 1) != 1)
- return 0;
- if (get_next_and_set(power, value, "class", lldpctl_k_dot3_power_class, 1) != 1)
- return 0;
- rc = get_next_and_set(power, value, "power type", lldpctl_k_dot3_power_type, 0);
- if (rc == 0) return 0;
- if (rc == -1) return 1;
-
- if (get_next_and_set(power, value, "power source", lldpctl_k_dot3_power_source, 1) != 1)
- return 0;
- if (get_next_and_set(power, value, "power priority", lldpctl_k_dot3_power_priority, 1) != 1)
- return 0;
- if (get_next_and_set(power, value, "power requested", lldpctl_k_dot3_power_requested, 1) != 1)
- return 0;
- if (get_next_and_set(power, value, "power allocated", lldpctl_k_dot3_power_allocated, 1) != 1)
- return 0;
+ log_debug("lldpctl", "set dot3 power");
+ 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 *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");
+ goto end;
+ }
+
+ 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);
+ }
+ end:
+ lldpctl_atom_dec_ref(dot3_power);
+ lldpctl_atom_dec_ref(port);
+ }
return 1;
}
+#define cmd_no_medlocation_coordinate cmd_not_implemented
+#define cmd_no_medlocation_address cmd_not_implemented
+#define cmd_no_medlocation_elin cmd_not_implemented
+#define cmd_no_medpolicy cmd_not_implemented
+#define cmd_no_medpower cmd_not_implemented
+#define cmd_no_dot3power cmd_not_implemented
+
/**
- * Parse LLDP-MED power string.
- *
- * @param value String describing the new value.
- * @param power Atom to use to insert new values.
- * @return 1 on success, 0 otherwise.
+ * Restrict the command to some ports.
*/
-static int
-parse_med_power(char *value, lldpctl_atom_t *power)
+static void
+restrict_ports(struct cmd_node *root)
{
- if (get_next_and_set(power, value, "device type", lldpctl_k_med_power_type, 1) != 1)
- return 0;
- if (get_next_and_set(power, value, "power source", lldpctl_k_med_power_source, 1) != 1)
- return 0;
- if (get_next_and_set(power, value, "power priority", lldpctl_k_med_power_priority, 1) != 1)
- return 0;
- if (get_next_and_set(power, value, "power value", lldpctl_k_med_power_val, 1) != 1)
- return 0;
+ /* 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");
+}
- 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");
}
/**
- * Parse LLDP-MED policy string.
- *
- * @param value String describing the new value.
- * @param power Atom to use to insert new values.
- * @return 1 on success, 0 otherwise.
+ * Register `configure med location address` commands.
*/
-static int
-parse_med_policy(char *value, lldpctl_atom_t *policy)
+static void
+register_commands_medloc_addr(struct cmd_node *configure_medlocation)
{
- if (get_next_and_set(policy, value, "application type", lldpctl_k_med_policy_type, 1) != 1)
- return 0;
- if (get_next_and_set(policy, value, "unknown flag", lldpctl_k_med_policy_unknown, 1) != 1)
- return 0;
- if (get_next_and_set(policy, value, "tagged flag", lldpctl_k_med_policy_tagged, 1) != 1)
- return 0;
- if (get_next_and_set(policy, value, "VLAN ID", lldpctl_k_med_policy_vid, 1) != 1)
- return 0;
- if (get_next_and_set(policy, value, "Layer 2 priority", lldpctl_k_med_policy_priority, 1) != 1)
- return 0;
- if (get_next_and_set(policy, value, "DSCP", lldpctl_k_med_policy_dscp, 1) != 1)
- return 0;
+ /* 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");
- return 1;
+ /* 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++)
+ commands_new(
+ commands_new(
+ configure_medloc_addr,
+ strdup(totag(addr_map->string)), /* TODO: memory leak, happens once */
+ 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);
}
/**
- * Parse LLDP-MED location string.
- *
- * @param value String describing the new value.
- * @param power Atom to use to insert new values.
- * @return 1 on success, 0 otherwise.
+ * 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 *unconfigure_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);
+
+ /* MED location (unset) */
+ struct cmd_node *unconfigure_medlocation = commands_new(
+ unconfigure_med,
+ "location", "MED location configuration",
+ NULL, NULL, NULL);
+ commands_new(
+ commands_new(
+ unconfigure_medlocation,
+ "coordinate", "Unconfigure MED location coordinate",
+ NULL, NULL, NULL),
+ NEWLINE, "Unconfigure MED location coordinate",
+ NULL, cmd_no_medlocation_coordinate, NULL);
+ commands_new(
+ commands_new(
+ unconfigure_medlocation,
+ "coordinate", "Unconfigure MED location address",
+ NULL, NULL, NULL),
+ NEWLINE, "Unconfigure MED location address",
+ NULL, cmd_no_medlocation_address, NULL);
+ commands_new(
+ commands_new(
+ unconfigure_medlocation,
+ "coordinate", "Unconfigure MED location ELIN",
+ NULL, NULL, NULL),
+ NEWLINE, "Unconfigure MED location ELIN",
+ NULL, cmd_no_medlocation_elin, NULL);
+}
+
static int
-parse_med_location(char *value, lldpctl_atom_t *location)
+cmd_check_application_but_no(struct cmd_env *env, void *arg)
{
- int format, stop = 0;
- lldpctl_atom_t *cael, *caels;
- char *type;
-
- if (get_next_and_set(location, value, "location format", lldpctl_k_med_location_format, 1) != 1)
- return 0;
- format = lldpctl_atom_get_int(location, lldpctl_k_med_location_format);
- switch (format) {
- case LLDP_MED_LOCFORMAT_COORD:
- if (get_next_and_set(location, value, "latitude", lldpctl_k_med_location_latitude, 1) != 1)
- return 0;
- if (get_next_and_set(location, value, "longitude", lldpctl_k_med_location_longitude, 1) != 1)
- return 0;
- if (get_next_and_set(location, value, "altitude", lldpctl_k_med_location_altitude, 1) != 1)
- return 0;
- if (get_next_and_set(location, value, "altitude unit", lldpctl_k_med_location_altitude_unit, 1) != 1)
- return 0;
- if (get_next_and_set(location, value, "datum", lldpctl_k_med_location_geoid, 1) != 1)
- return 0;
- return 1;
- case LLDP_MED_LOCFORMAT_CIVIC:
- if (get_next_and_set(location, value, "country", lldpctl_k_med_location_country, 1) != 1)
- return 0;
- while ((type = get_next(location, value, "civic address type", 0)) != NULL &&
- !stop) {
- /* Next we have the element addresses */
- caels = lldpctl_atom_get(location, lldpctl_k_med_location_ca_elements);
- cael = lldpctl_atom_create(caels);
-
- if (lldpctl_atom_set_str(cael, lldpctl_k_med_civicaddress_type, type) != NULL) {
- if (get_next_and_set(cael, value, "civic address value",
- lldpctl_k_med_civicaddress_value, 1) == 1) {
- if (lldpctl_atom_set(location, lldpctl_k_med_location_ca_elements,
- cael) == NULL) {
- log_warnx(NULL, "unable to add a civic address element. %s",
- lldpctl_last_strerror(lldpctl_atom_get_connection(location)));
- stop = 1;
- }
- } else stop = 1;
- } else {
- log_warnx(NULL, "unable to set civic address type. %s.",
- lldpctl_last_strerror(lldpctl_atom_get_connection(cael)));
- stop = 1;
- }
+ const char *what = arg;
+ if (!cmdenv_get(env, "application")) return 0;
+ if (cmdenv_get(env, what)) return 0;
+ return 1;
+}
+static int
+cmd_store_something_env_value_and_pop2(const char *what,
+ struct cmd_env *env, void *value)
+{
+ return (cmdenv_put(env, what, value) != -1 &&
+ cmdenv_pop(env, 2) != -1);
+}
+static int
+cmd_store_app_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, void *value)
+{
+ return cmd_store_something_env_value_and_pop2("application", env, value);
+}
+static int
+cmd_store_powerpairs_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, 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, 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, void *value)
+{
+ return cmd_store_something_env_value_and_pop2("priority", env, value);
+}
+static int
+cmd_store_app_env_value(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, void *value)
+{
+ return (cmdenv_put(env, "application", value) != -1);
+}
- lldpctl_atom_dec_ref(cael);
- lldpctl_atom_dec_ref(caels);
- }
- if (stop) return 0;
- return 1;
- case LLDP_MED_LOCFORMAT_ELIN:
- if (get_next_and_set(location, value, "ELIN number", lldpctl_k_med_location_elin, 1) != 1)
- return 0;
- return 1;
- default:
- log_warnx(NULL, "unable to determine the requested location format");
- return 0;
+/**
+ * Register `configure med policy` commands.
+ */
+static void
+register_commands_medpol(struct cmd_node *configure_med, struct cmd_node *unconfigure_med)
+{
+ struct cmd_node *configure_medpolicy = commands_new(
+ configure_med,
+ "policy", "MED policy configuration",
+ NULL, NULL, NULL);
+
+ /* MED policy (un set) */
+ struct cmd_node *unconfigure_application =
+ commands_new(
+ commands_new(
+ unconfigure_med,
+ "policy", "MED policy configuration",
+ NULL, NULL, NULL),
+ "application", "MED policy application",
+ 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)); /* TODO: memory leak, happens once */
+ commands_new(
+ commands_new(
+ unconfigure_application,
+ tag,
+ pol_map->string,
+ NULL, cmd_store_app_env_value, pol_map->string),
+ NEWLINE, "Remove specified MED policy",
+ NULL, cmd_no_medpolicy, NULL);
+ 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(
+ 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)); /* TODO: memory leak, happens once */
+ commands_new(
+ priority,
+ tag, prio_map->string,
+ NULL, cmd_store_prio_env_value_and_pop2, prio_map->string);
}
+}
+static int
+cmd_check_type_but_no(struct cmd_env *env, 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, 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, void *arg)
+{
+ return cmd_check_type(env, "pse");
+}
+static int
+cmd_check_pd(struct cmd_env *env, 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_and_pop, "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)); /* TODO: memory leak, happens once */
+ commands_new(
+ priority,
+ tag,
+ prio_map->string,
+ NULL, cmd_store_prio_env_value_and_pop2, prio_map->string);
+ }
+}
/**
- * Modify the interfaces specified on the command line.
- *
- * @param conn Connection to lldpd.
- * @param argc Number of arguments.
- * @param argv Array of arguments.
- * @param ifindex Index of the first non optional argument
+ * Register `configure med power` commands.
*/
-void
-modify_interfaces(lldpctl_conn_t *conn,
- int argc, char **argv, int ifindex)
+static void
+register_commands_medpow(struct cmd_node *configure_med, struct cmd_node *unconfigure_med)
{
- int i, ch;
- const char *iface_name;
- lldpctl_atom_t *ifaces, *iface;
- lldpctl_atom_t *port;
-
- ifaces = lldpctl_get_interfaces(conn);
- if (!ifaces) {
- log_warnx(NULL, "not able to get the list of interfaces: %s", lldpctl_strerror(lldpctl_last_error(conn)));
- return;
+ commands_new(
+ commands_new(unconfigure_med,
+ "power", "MED power configuration",
+ NULL, NULL, NULL),
+ NEWLINE, "Disable advertising of LLDP-MED POE-MDI TLV",
+ NULL, cmd_no_medpower, NULL);
+
+ 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, 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;
+}
- lldpctl_atom_foreach(ifaces, iface) {
- /* Only process specified interfaces or all interfaces if none
- * is specified. */
- iface_name = lldpctl_atom_get_str(iface,
- lldpctl_k_interface_name);
- if (ifindex < argc) {
- for (i = ifindex; i < argc; i++)
- if (strcmp(argv[i],
- iface_name) == 0)
- break;
- if (i == argc)
- continue;
- }
+/**
+ * Register `configure med dot3` commands.
+ */
+static void
+register_commands_dot3pow(struct cmd_node *configure_dot3, struct cmd_node *unconfigure_dot3)
+{
+ commands_new(
+ commands_new(unconfigure_dot3,
+ "power", "Dot3 power configuration",
+ NULL, NULL, NULL),
+ NEWLINE, "Disable advertising of Dot3 POE-MDI TLV",
+ NULL, cmd_no_dot3power, NULL);
- port = lldpctl_get_port(iface);
-
- optind = 1;
- while ((ch = getopt(argc, argv, LLDPCTL_ARGS)) != -1) {
- lldpctl_atom_t *dot3_power;
- lldpctl_atom_t *med_power;
- lldpctl_atom_t *med_policy, *med_policies;
- lldpctl_atom_t *med_location, *med_locations;
-
- switch (ch) {
- case 'o':
- /* Dot3 power */
- dot3_power = lldpctl_atom_get(port, lldpctl_k_port_dot3_power);
- if (dot3_power == NULL) {
- log_warnx(NULL, "unable to set Dot3 power: support seems unavailable");
- break;
- }
- if (parse_dot3_power(optarg, dot3_power)) {
- if (lldpctl_atom_set(port, lldpctl_k_port_dot3_power,
- dot3_power) == NULL)
- log_warnx(NULL, "unable to set Dot3 power. %s",
- lldpctl_strerror(lldpctl_last_error(conn)));
- else
- log_info(NULL, "Dot3 power has been set for port %s",
- iface_name);
- }
- lldpctl_atom_dec_ref(dot3_power);
- break;
- case 'O':
- /* LLDP-MED power */
- med_power = lldpctl_atom_get(port, lldpctl_k_port_med_power);
- if (med_power == NULL) {
- log_warnx(NULL, "unable to set LLDP-MED power: support seems unavailable");
- break;
- }
- if (parse_med_power(optarg, med_power)) {
- if (lldpctl_atom_set(port, lldpctl_k_port_med_power,
- med_power) == NULL)
- log_warnx(NULL, "unable to set LLDP-MED power. %s",
- lldpctl_strerror(lldpctl_last_error(conn)));
- else
- log_info(NULL, "LLDP-MED power has been set for port %s",
- iface_name);
- }
- lldpctl_atom_dec_ref(med_power);
- break;
- case 'P':
- /* LLDP-MED network policy */
- med_policies = lldpctl_atom_get(port, lldpctl_k_port_med_policies);
- if (med_policies == NULL) {
- log_warnx(NULL, "unable to set LLDP-MED policy: support seems unavailable");
- break;
- }
- /* We select the first policy. Since we will
- * modify the application type, it is not
- * necessary to select the one with the
- * appropriate index. */
- med_policy = lldpctl_atom_iter_value(med_policies,
- lldpctl_atom_iter_next(med_policies,
- lldpctl_atom_iter(med_policies)));
- if (parse_med_policy(optarg, med_policy)) {
- if (lldpctl_atom_set(port, lldpctl_k_port_med_policies,
- med_policy) == NULL)
- log_warnx(NULL, "unable to set LLDP-MED policy. %s",
- lldpctl_strerror(lldpctl_last_error(conn)));
- else
- log_info(NULL, "LLDP-MED policy has been set for port %s",
- iface_name);
- }
- lldpctl_atom_dec_ref(med_policy);
- lldpctl_atom_dec_ref(med_policies);
- break;
- case 'L':
- /* LLDP-MED location */
- med_locations = lldpctl_atom_get(port, lldpctl_k_port_med_locations);
- if (med_locations == NULL) {
- log_warnx(NULL, "unable to set LLDP-MED location: support seems unavailable");
- break;
- }
- /* As for policy, we pick the first and it will
- * be reset when setting the format. No need to
- * pick the one with the appropriate index. */
- med_location = lldpctl_atom_iter_value(med_locations,
- lldpctl_atom_iter_next(med_locations,
- lldpctl_atom_iter(med_locations)));
- if (parse_med_location(optarg, med_location)) {
- if (lldpctl_atom_set(port, lldpctl_k_port_med_locations,
- med_location) == NULL)
- log_warnx(NULL, "unable to set LLDP-MED location. %s",
- lldpctl_strerror(lldpctl_last_error(conn)));
- else
- log_info(NULL, "LLDP-MED location has been set for port %s",
- iface_name);
- }
- lldpctl_atom_dec_ref(med_location);
- lldpctl_atom_dec_ref(med_locations);
- break;
- default:
- /* We shouldn't be here... */
- break;
- }
- }
+ struct cmd_node *configure_dot3power = commands_new(
+ configure_dot3,
+ "power", "Dot3 power configuration",
+ NULL, NULL, NULL);
- lldpctl_atom_dec_ref(port);
+ 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));
+ commands_new(
+ class,
+ tag,
+ class_map->string,
+ NULL, cmd_store_class_env_value_and_pop2, class_map->string);
}
- lldpctl_atom_dec_ref(ifaces);
+ /* 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");
+}
+
+/**
+ * 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",
+ "Unset configuration option",
+ NULL, NULL, NULL);
+ restrict_ports(configure);
+ restrict_ports(unconfigure);
+
+ 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, unconfigure_med);
+ register_commands_medpol(configure_med, unconfigure_med);
+ register_commands_medpow(configure_med, unconfigure_med);
+
+ struct cmd_node *configure_dot3 = commands_new(
+ configure,
+ "dot3", "Dot3 configuration",
+ NULL, NULL, NULL);
+ struct cmd_node *unconfigure_dot3 = commands_new(
+ unconfigure,
+ "dot3", "Dot3 configuration",
+ NULL, NULL, NULL);
+
+ register_commands_dot3pow(configure_dot3, unconfigure_dot3);
}
#include "../lib/lldpctl.h"
#include "../lldp-const.h"
+#include "../log.h"
+#include "../ctl.h"
#include "writer.h"
-#define LLDPCTL_ARGS "hdvaf:L:P:O:o:wCN"
+/* 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*, void *),
+ int(*execute)(struct lldpctl_conn_t*, struct writer*,
+ struct cmd_env*, void *),
+ void *);
+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 argc, const char **argv);
+char *commands_complete(struct cmd_node *, int argc, const char **argv,
+ int cursorc, int cursoro, int all);
+/* helpers */
+int cmd_not_implemented(struct lldpctl_conn_t *, struct writer *,
+ struct cmd_env *, void *);
+int cmd_check_no_env(struct cmd_env *, void *);
+int cmd_check_env(struct cmd_env *, void *);
+int cmd_store_env(struct lldpctl_conn_t *, struct writer *,
+ struct cmd_env *, void *);
+int cmd_store_env_and_pop(struct lldpctl_conn_t *, struct writer *,
+ struct cmd_env *, void *);
+int cmd_store_env_value(struct lldpctl_conn_t *, struct writer *,
+ struct cmd_env *, void *);
+int cmd_store_env_value_and_pop(struct lldpctl_conn_t *, struct writer *,
+ struct cmd_env *, void *);
+int cmd_store_env_value_and_pop2(struct lldpctl_conn_t *, struct writer *,
+ struct cmd_env *, void *);
+int cmd_store_env_value_and_pop3(struct lldpctl_conn_t *, struct writer *,
+ struct cmd_env *, void *);
+lldpctl_atom_t* cmd_iterate_on_interfaces(struct lldpctl_conn_t *,
+ struct cmd_env *);
+
+/* misc.c */
+int contains(const char *, const char *);
+char* totag(const char *);
/* display.c */
-void display_interfaces(lldpctl_conn_t *, struct writer *, int, int, char **);
+#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 *);
+ lldpctl_atom_t *, lldpctl_atom_t *, int);
void display_configuration(lldpctl_conn_t *, struct writer *);
+/* show.c */
+void register_commands_show(struct cmd_node *);
+void register_commands_watch(struct cmd_node *);
+
/* actions.c */
-void modify_interfaces(lldpctl_conn_t *, int, char **, int);
+void register_commands_configure(struct cmd_node *);
#endif
--- /dev/null
+/* -*- 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>
+
+/**
+ * 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 */
+
+ /**
+ * Function validating entry in this node. Can be @c NULL.
+ */
+ int(*validate)(struct cmd_env*, 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*, void *);
+ 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)
+{
+ return commands_new(NULL, NULL, NULL, NULL, NULL, NULL);
+}
+
+/**
+ * Create a new node.
+ *
+ * @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*, void *),
+ int(*execute)(struct lldpctl_conn_t*, struct writer*,
+ struct cmd_env*, void *),
+ void *arg)
+{
+ struct cmd_node *new = calloc(1, sizeof(struct cmd_node));
+ if (new == NULL) {
+ log_warn("lldpctl", "unable to allocate memory for new command node");
+ return NULL;
+ }
+ new->token = token;
+ new->doc = doc;
+ new->validate = validate;
+ new->execute = execute;
+ new->arg = arg;
+ TAILQ_INIT(&new->subentries);
+ if (root != NULL)
+ 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;
+};
+
+/**
+ * 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 cursorc On which argument the cursor is. -1 if no completion is required.
+ * @param cursoro On which offset the cursor is. -1 if no completion is required.
+ * @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.
+ * @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,
+ int cursorc, int cursoro, char **word, int all)
+{
+ int n, rc = 0, completion = (cursorc != -1 && cursoro != -1 && word != NULL);
+ struct cmd_env env = {
+ .elements = TAILQ_HEAD_INITIALIZER(env.elements),
+ .stack = TAILQ_HEAD_INITIALIZER(env.stack),
+ .argc = completion?cursorc:argc,
+ .argv = argv,
+ .argp = 0
+ };
+ 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;
+
+ /* 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))) {
+ struct cmd_node *candidate, *best = NULL;
+ const char *token = (env.argp < env.argc) ? env.argv[env.argp] :
+ (env.argp == env.argc && !completion) ? NEWLINE : NULL;
+ if (token == NULL) 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))) {
+ if (!candidate->validate ||
+ candidate->validate(&env, candidate->arg) == 1) {
+ if (candidate->token &&
+ !strcmp(candidate->token, token)) {
+ /* Exact match */
+ best = candidate;
+ 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 && (!candidate->validate ||
+ candidate->validate(&env, candidate->arg) == 1)) {
+ best = candidate;
+ 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 && best->execute(conn, w, &env, best->arg) != 1) {
+ rc = -1;
+ goto end;
+ }
+ env.argp++;
+ }
+end:
+ if (!completion) {
+ if (rc == 0 && env.argp != env.argc + 1) {
+ log_warnx("lldpctl", "incomplete command");
+ rc = -1;
+ }
+ } else if (rc == 0 && env.argp == env.argc) {
+ /* We need to complete. Let's build the list of candidate words. */
+ struct cmd_node *candidate = NULL;
+ int maxl = 10; /* Max length of a word */
+ TAILQ_HEAD(, candidate_word) words; /* List of subnodes */
+ TAILQ_INIT(&words);
+ current = cmdenv_top(&env);
+ TAILQ_FOREACH(candidate, ¤t->subentries, next) {
+ if ((!candidate->token ||
+ !strncmp(env.argv[cursorc], candidate->token, cursoro)) &&
+ (!candidate->validate ||
+ candidate->validate(&env, candidate->arg) == 1)) {
+ struct candidate_word *cword = malloc(sizeof(struct candidate_word));
+ if (!cword) break;
+ cword->word = candidate->token;
+ cword->doc = candidate->doc;
+ 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. */
+ int c = 0;
+ char prefix[maxl + 2]; /* Extra space may be added at the end */
+ struct candidate_word *cword, *cword_next;
+ memset(prefix, 0, maxl+2);
+ for (int n = 0; n < maxl; n++) {
+ c = 1; /* Set to 0 to exit outer loop */
+ TAILQ_FOREACH(cword, &words, next) {
+ c = 0;
+ 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 && strcmp(prefix, NEWLINE) &&
+ strlen(prefix) > 0 && cursoro < 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 */
+ fprintf(stderr, "\n-- \033[1;34m%s\033[0m\n",
+ current->doc ? current->doc : "Help");
+ TAILQ_FOREACH(cword, &words, next) {
+ char fmt[100];
+ snprintf(fmt, sizeof(fmt),
+ "%s%%%ds%s %%s\n",
+ "\033[1;30m", maxl, "\033[0m");
+ fprintf(stderr, fmt,
+ 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 cursorc, int cursoro, int all)
+{
+ char *word = NULL;
+ if (_commands_execute(NULL, NULL, root, argc, argv,
+ cursorc, cursoro, &word, all) == 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)
+{
+ return _commands_execute(conn, w, root, argc, argv, -1, -1, NULL, 0);
+}
+
+/**
+ * A generic "not implemented" command.
+ */
+int
+cmd_not_implemented(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, void *arg)
+{
+ log_warnx("lldpctl", "not supported yet");
+ return 1;
+}
+
+/**
+ * Check if the environment does not contain the given key.
+ *
+ * @param env The environment.
+ * @param arg The key to search for.
+ */
+int
+cmd_check_no_env(struct cmd_env *env, void *key)
+{
+ return cmdenv_get(env, (const char*)key) == NULL;
+}
+
+/**
+ * Check if the environment does contain the given key.
+ *
+ * @param env The environment.
+ * @param arg The key to search for. Can be a comma separated list.
+ */
+int
+cmd_check_env(struct cmd_env *env, 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 env The environment.
+ * @param key The key to store.
+ */
+int
+cmd_store_env(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, void *key)
+{
+ return cmdenv_put(env, key, NULL) != -1;
+}
+
+/**
+ * Store the given key in the environment and pop one element from the stack.
+ *
+ * @param env The environment.
+ * @param key The key to store.
+ */
+int
+cmd_store_env_and_pop(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, 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 env The environment.
+ * @param key The key to store.
+ */
+int
+cmd_store_env_value_and_pop(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, 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, 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, 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, void *key)
+{
+ return (cmdenv_put(env, key, cmdenv_arg(env)) != -1 &&
+ cmdenv_pop(env, 3) != -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;
+}
}
}
-static char*
-totag(const char *value)
-{
- int i;
- static char *result = NULL;
- free(result); result = NULL;
- if (!value) return "none";
- result = calloc(1, strlen(value));
- 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;
-}
-
static void
display_med(struct writer *w, lldpctl_atom_t *port)
{
}
static void
-display_chassis(struct writer* w, lldpctl_atom_t* neighbor)
+display_chassis(struct writer* w, lldpctl_atom_t* neighbor, int details)
{
lldpctl_atom_t *mgmts, *mgmt;
tag_end(w);
tag_datatag(w, "name", "SysName",
lldpctl_atom_get_str(neighbor, lldpctl_k_chassis_name));
+ if (details == DISPLAY_BRIEF) {
+ tag_end(w);
+ return;
+ }
tag_datatag(w, "descr", "SysDescr",
lldpctl_atom_get_str(neighbor, lldpctl_k_chassis_descr));
}
static void
-display_port(struct writer *w, lldpctl_atom_t *port)
+display_port(struct writer *w, lldpctl_atom_t *port, int details)
{
tag_start(w, "port", "Port");
tag_start(w, "id", "PortID");
lldpctl_atom_get_str(port, lldpctl_k_port_descr));
/* Dot3 */
- 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));
+ 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));
- do {
long int autoneg_support, autoneg_enabled, autoneg_advertised;
autoneg_support = lldpctl_atom_get_int(port,
lldpctl_k_port_dot3_autoneg_support);
lldpctl_atom_get_str(port, lldpctl_k_port_dot3_mautype));
tag_end(w);
}
- } while (0);
- do {
lldpctl_atom_t *dot3_power = lldpctl_atom_get(port,
lldpctl_k_port_dot3_power);
int devicetype = lldpctl_atom_get_int(dot3_power,
tag_end(w);
}
lldpctl_atom_dec_ref(dot3_power);
- } while(0);
+ }
tag_end(w);
}
void
display_interface(lldpctl_conn_t *conn, struct writer *w, int hidden,
- lldpctl_atom_t *iface, lldpctl_atom_t *neighbor)
+ lldpctl_atom_t *iface, lldpctl_atom_t *neighbor, int details)
{
if (!hidden &&
lldpctl_atom_get_int(neighbor, lldpctl_k_port_hidden))
lldpctl_atom_get_str(iface, lldpctl_k_interface_name));
tag_attr(w, "via" , "via",
lldpctl_atom_get_str(neighbor, lldpctl_k_port_protocol));
- tag_attr(w, "rid" , "RID",
- lldpctl_atom_get_str(neighbor, lldpctl_k_chassis_index));
- tag_attr(w, "age" , "Time",
- display_age(lldpctl_atom_get_int(neighbor, lldpctl_k_port_age)));
-
- display_chassis(w, neighbor);
- display_port(w, neighbor);
- display_vlans(w, neighbor);
- display_ppvids(w, neighbor);
- display_pids(w, neighbor);
- display_med(w, neighbor);
+ if (details > DISPLAY_BRIEF) {
+ tag_attr(w, "rid" , "RID",
+ lldpctl_atom_get_str(neighbor, lldpctl_k_chassis_index));
+ tag_attr(w, "age" , "Time",
+ display_age(lldpctl_atom_get_int(neighbor, lldpctl_k_port_age)));
+ }
+
+ display_chassis(w, neighbor, details);
+ display_port(w, neighbor, details);
+ if (details == DISPLAY_DETAILS) {
+ display_vlans(w, neighbor);
+ display_ppvids(w, neighbor);
+ display_pids(w, neighbor);
+ display_med(w, neighbor);
+ }
tag_end(w);
}
+/**
+ * Display information about interfaces.
+ *
+ * @param conn Connection to lldpd.
+ * @param w Writer.
+ * @param hidden Whatever to show hidden ports.
+ * @param interfaces List of interfaces we should restrict to (comma separated).
+ * @param details Level of details we need (DISPLAY_*).
+ */
void
-display_interfaces(lldpctl_conn_t *conn, struct writer *w, int hidden,
- int argc, char *argv[])
+display_interfaces(lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env,
+ int hidden, int details)
{
- int i;
- lldpctl_atom_t *iface_list;
lldpctl_atom_t *iface;
- lldpctl_atom_t *port;
- lldpctl_atom_t *neighbors;
- lldpctl_atom_t *neighbor;
-
- iface_list = lldpctl_get_interfaces(conn);
- if (!iface_list) {
- log_warnx(NULL, "not able to get the list of interfaces. %s",
- lldpctl_last_strerror(conn));
- return;
- }
tag_start(w, "lldp", "LLDP neighbors");
- lldpctl_atom_foreach(iface_list, iface) {
- if (optind < argc) {
- for (i = optind; i < argc; i++)
- if (strcmp(argv[i],
- lldpctl_atom_get_str(iface,
- lldpctl_k_interface_name)) == 0)
- break;
- if (i == argc)
- continue;
- }
+ 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);
+ display_interface(conn, w, hidden, iface, neighbor, details);
}
lldpctl_atom_dec_ref(neighbors);
lldpctl_atom_dec_ref(port);
}
- lldpctl_atom_dec_ref(iface_list);
tag_end(w);
}
-const char *
+static const char *
N(const char *str) {
if (str == NULL || strlen(str) == 0) return "(none)";
return str;
configuration = lldpctl_get_configuration(conn);
if (!configuration) {
- log_warnx(NULL, "not able to get configuration. %s",
+ log_warnx("lldpctl", "not able to get configuration. %s",
lldpctl_last_strerror(conn));
return;
}
struct json_writer_private *p = w->priv;
struct json_element *current = TAILQ_LAST(p, json_writer_private);
if (current == NULL) {
- log_warnx(NULL, "unbalanced tags");
+ log_warnx("lldpctl", "unbalanced tags");
return;
}
TAILQ_REMOVE(p, current, next);
{
struct json_writer_private *p = w->priv;
if (TAILQ_EMPTY(p)) {
- log_warnx(NULL, "nothing to output");
+ log_warnx("lldpctl", "nothing to output");
} else if (TAILQ_NEXT(TAILQ_FIRST(p), next) != NULL) {
- log_warnx(NULL, "unbalanced tags");
+ log_warnx("lldpctl", "unbalanced tags");
/* memory will leak... */
} else {
struct json_element *first = TAILQ_FIRST(p);
if (json_dumpf(export,
stdout,
JSON_INDENT(2) | JSON_PRESERVE_ORDER) == -1)
- log_warnx(NULL, "unable to output JSON");
+ log_warnx("lldpctl", "unable to output JSON");
json_decref(first->el);
json_decref(export);
TAILQ_REMOVE(p, first, next);
.Nd control LLDP daemon
.Sh SYNOPSIS
.Nm
-.Op Fl advwCN
-.Op Fl L Ar location
-.Op Fl P Ar policy
-.Op Fl O Ar poe
-.Op Fl o Ar poe
-.Op Ar interface ...
+.Op Fl dv
+.Op Fl f Ar format
+.Op Ar command ...
.Sh DESCRIPTION
The
.Nm
.Xr lldpd 8
daemon.
.Pp
-When no specific option is given,
+When no command is specified,
.Nm
-displays the list of discovered neighbors along with
-some of their advertised capabilities. If some interfaces are given,
-only those interfaces will be displayed.
+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
Show
.Nm
version.
-.It Fl a
-Display all remote ports, including those hidden by the smart filter.
-.It Fl w
-Monitor neighbor changes. Each changed neighbor will be displayed.
-.It Fl C
-Display global configuration of
-.Em lldpd
-daemon.
-.It Fl N
-Make
-.Em lldpd
-update its information and send new LLDP PDU on all interfaces.
.It Fl f Ar format
Choose the output format. Currently
.Em plain ,
and
.Em keyvalue
formats are available. The default is
-.Em plain.
-.It Fl L Ar location
-Enable the transmission of LLDP-MED location TLV for the given
-interfaces. This option can be repeated several times to enable the
-transmission of the location in several formats. Several formats are
-accepted:
-.Bl -tag -width "XX"
-.It Em Coordinate based location
-The format of
-.Ar location
-is
-.Ar 1:48.85667N:2.2014E:117.47:m:1
-The first digit is always
-.Ar 1 .
-It is followed by the latitude, a letter for the direction (
-.Ar E
+.Em plain .
+.El
+.Pp
+
+The following commands are supported. 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 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 a 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 watch
+.Op ports Ar ethX Op ...
+.Op Cd details | summary
+.Op Cd hidden
+.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.
+.Ed
+
+.Cd show configuration
+.Bd -ragged -offset XXXXXX
+Display global configuration of
+.Xr lldpd 8
+daemon.
+.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
+.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
-.Ar W
-for East or West), the longitude and a letter for the direction (
-.Ar N
+.Em S .
+The format of
+.Ar longitude
+is a decimal floating point number followed either by
+.Em E
or
-.Ar S
-). The next figure is the altitude. It can be expressed in meters (the
-next letter is then
-.Ar m
-) or in floors (the letter should be
-.Ar f
-). The last digit is the datum. It can either be
-.Ar 1
-(WGS84),
-.Ar 2
-(NAD83) or
-.Ar 3
-(NAD83/MLLW).
-.It Em Civic address
-The location can be expressed as an address. The format of the
-location is then
-.Ar 2:FR:6:Commercial Rd:3:Roseville:19:4
-The first digit is always
-.Ar 2 .
-The next two letters are the country code. Then, arguments are paired
-to form the address. The first member of the pair is a digit
-indicating the type of the second member. Here is the list of
-valid types:
-.Bl -tag -width "XXXX." -compact
-.It Sy 0
-Language
-.It Sy 1
-National subdivisions
-.It Sy 2
-County, parish, district
-.It Sy 3
-City, township
-.It Sy 4
-City division, borough, ward
-.It Sy 5
-Neighborhood, block
-.It Sy 6
-Street
-.It Sy 16
-Leading street direction
-.It Sy 17
-Trailing street suffix
-.It Sy 18
-Street suffix
-.It Sy 19
-House number
-.It Sy 20
-House number suffix
-.It Sy 21
-Landmark or vanity address
-.It Sy 22
-Additional location info
-.It Sy 23
-Name
-.It Sy 24
-Postal/ZIP code
-.It Sy 25
-Building
-.It Sy 26
-Unit
-.It Sy 27
-Floor
-.It Sy 28
-Room number
-.It Sy 29
-Place type
-.It Sy 128
-Script
+.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.
+.Ar datum
+is one of those values:
+.Bl -bullet -compact -offset XXXXXXXX
+.It
+WGS84
+.It
+NAD83
+.It
+NAD83/MLLW
.El
-.It ECS ELIN
-This is a numerical string using for setting up emergency call. The
-format of the location is then the following:
-.Ar 3:0000000911
-where the first digit should be
-.Ar 3
-and the second argument is the ELIN number.
+.Pp
+A valid use of this command is:
+.D1 configure ports eth0 med location coordinate latitude 48.85667N longitude 2.2014E altitude 117.47m 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
-When setting a location for a given port, all previous locations are
-erased. To erase all location, just use the empty string. There is
-currently no way to get the location from the command line.
+A valid use of this command is:
+.D1 configure ports eth1 med location address 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 unconfigure
+.Op ports Ar ethX Op ...
+.Cd med location
+.Op Cd coordinate | address | elin
+.Bd -ragged -offset XXXXXX
+Do not advertise the location on the given ports (or on all ports if
+no port was provided). Optionally, only remove the specified location
+(coordinate-based, address or ELIN number). Here is an example of use:
+.D1 unconfigure ports eth0 eth2 med location coordinate
+.Ed
+
+.Cd configure
+.Op ports Ar ethX Op ...
+.Cd med policy
+.Cd application Ar application
+.Op Cd unknown
+.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:
+static const struct value_string port_med_policy_map[] = {
+.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
-.It Fl P Ar policy
-Enable the transmission of LLDP-MED Network Policy TLVs for the given
-interfaces. This option can be repeated several times to specify
-different policies. Format (without spaces!):
+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
-.Em App-Type
-:
-.Ar U
-:
-.Ar T
-:
-.Ar VLAN-ID
-:
-.Ar L2-Prio
-:
-.Ar DSCP
-.Bl -tag -width "XX"
-.It Ar App-Type
-Valid application types (see ANSI/TIA-1057 table 12):
-.Bl -tag -width "X." -compact
-.It Sy 1
-Voice
-.It Sy 2
-Voice Signaling
-.It Sy 3
-Guest Voice
-.It Sy 4
-Guest Voice Signaling
-.It Sy 5
-Softphone Voice
-.It Sy 6
-Video Conferencing
-.It Sy 7
-Streaming Video
-.It Sy 8
-Video Signaling
-.El
-.It Ar U
-Unknown Policy Flag.
-.Bl -tag -width "X." -compact
-.It Sy 0
-Network policy for the specified application type is defined.
-.It Sy 1
-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.
-.El
-.It Ar T
-Tagged Flag.
-.Bl -tag -width "X." -compact
-.It Sy 0
-Untagged VLAN. In this case the VLAN ID and the Layer 2 Priority
-are ignored and only the DSCP value has relevance.
-.It Sy 1
-Tagged VLAN.
-.El
-.It Ar VLAN-ID
-IEEE 802.1q VLAN ID (VID). A value of 1 through 4094 defines a
-VLAN ID. A value of 0 means that only the priority level is
-significant.
-.It Ar L2-Prio
-IEEE 802.1d / IEEE 802.1p Layer 2 Priority, also known as Class of Service
-(CoS), to be used for the specified application type.
-.Bl -tag -width "X." -compact
-.It Sy 1
-Background
-.It Sy 2
-Spare
-.It Sy 0
-Best Effort (default)
-.It Sy 3
-Excellent Effort
-.It Sy 4
-Controlled Load
-.It Sy 5
-Video
-.It Sy 6
-Voice
-.It Sy 7
-Network Control
-.El
-.It Ar DSCP
-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.)
-.It Examples:
-.Bl -tag -width "X." -compact
-.It Sy 1:0:1:500:6:46
-Voice (1): not unknown (0), tagged (1), VLAN-ID 500, l2 prio Voice (6), DSCP 46 (EF, Expedited Forwarding)
-.It Sy 2:0:1:500:3:24
-Voice Signaling (2): not unknown (0), tagged (1), VLAN-ID 500, l2 prio
-Excellent Effort (3), DSCP 24 (CS3, Class Selector 3)
-.El
+When a VLAN is specified with
+.Cd vlan
+tells which 802.1q VLAN ID has to be advertised for the network
+policy. A valid value is between 1 and 4094.
+.Cd 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. It should be one of those values:
+.Bl -bullet -compact -offset XXXXXXXX
+.It
+background
+.It
+spare
+.It
+best-effort
+.It
+excellent-effort
+.It
+controlled-load
+.It
+video
+.It
+voice
+.It
+network-control
.El
.Pp
-.It Fl O Ar poe
-Enable the transmission of LLDP-MED POE-MDI TLV for the given
-interfaces. 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:
+.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 unconfigure
+.Op ports Ar ethX Op ...
+.Cd med policy
+.Cd application Ar application
+.Bd -ragged -offset XXXXXX
+Do not advertise any network policy on the given ports (or on all
+ports if no port was provided). Optionally, only remove the specified
+application type. Here is an example of use:
+.D1 unconfigure ports eth0 eth2 med policy application voice
+.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.
this kind of representation.
.El
.Pp
-This option is distinct of
-.Fl o
-option. You may want to use both options at the same time.
-.Pp
-The format of this option is (without spaces):
-.Pp
-.Em type
-:
-.Ar source
-:
-.Ar priority
-:
-.Ar value
-.Bl -tag -width "XX"
-.It Ar type
Valid types are:
-.Bl -tag -width "XXX." -compact
-.It Sy PSE
+.Bl -tag -width "XXX." -compact -offset XX
+.It Sy pse
Power Sourcing Entity (power provider)
-.It Sy PD
+.It Sy pd
Power Device (power consumer)
.El
-.It Ar source
+.Pp
Valid sources are:
-.Bl -tag -width "X." -compact
+.Bl -tag -width "XXXXXXX" -compact -offset XX
.It Sy unknown
Unknown
.It Sy primary
.It Sy both
For PD, the power source is both the PSE and a local source.
.El
-.It Ar priority
-Four priorities are available:
-.Bl -tag -width "X." -compact
+.Pp
+Valid priorities are:
+.Bl -tag -width "XXXXXXXXX" -compact -offset XX
.It Sy unknown
Unknown priority
.It Sy critical
.It Sy low
Low
.El
-.It Ar value
-For PD, the power value is the total power in milliwatts required
-by a PD device from the PSE device.
-.El
-.It Fl o Ar poe
-Enable the transmission of Dot3 POE-MDI TLV for the given
-interfaces. One can act as a PD (power consumer) or a PSE (power
-provider). This option is distinct of the
-.Fl O
-option. You might want to use both. Contrary to LLDP-MED POE-MDI TLV,
-Dot3 POE-MDI TLV are strictly per-port values.
.Pp
-The format of this option is (without spaces):
+.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 unconfigure
+.Op ports Ar ethX Op ...
+.Cd med power
+.Bd -ragged -offset XXXXXX
+Do not advertise the LLDP-MED POE-MDI TLV for the given portd (or on all
+ports if no port was provided).
+.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
-.Em type
-:
.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
+forr
.Ar powerpairs
-:
-.Ar class
-[ :
-.Ar powertype
-:
-.Ar source
-:
-.Ar priority
-:
-.Ar requested
-:
-.Ar allocated
-]
-.Bl -tag -width "XX"
-.It Ar type
-Valid types are:
-.Bl -tag -width "XXX." -compact
-.It Sy PSE
-Power Sourcing Entity (power provider)
-.It Sy PD
-Power Device (power consumer)
-.El
-.It Ar powerpairs
-Valid values are:
-.Bl -tag -width "X." -compact
+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
-.It Ar class
-Five classes are available:
-.Bl -tag -width "X." -compact
-.It Sy 1
-class 0
-.It Sy 2
-class 1
-.It Sy 3
-class 2
-.It Sy 4
-class 3
-.It Sy 5
-class 4
-.It Sy 0
-no class
-.El
-.El
.Pp
-.Ar supported ,
-.Ar enabled
-and
-.Ar paircontrol
-can be set to to 0 or 1.
-.Ar supported
-means that MDI power is supported on the given port.
-.Ar enabled
-means that MDI power is enabled on the given port.
-.Ar paircontrol
-is used to indicate if the pair selection can be controlled on the
-given port.
+When specified,
+.Ar class
+is a number between 0 and 4.
.Pp
-.Ar powertype ,
-.Ar source ,
-.Ar priority
-(and remaining values) are optional. They are only requested in
-conformance with 802.3at.
+The remaining parameters are in conformance with 802.3at and are optional.
.Ar type
-should be either 1 or 2. For source, use one of the following values:
-Valid sources are:
-.Bl -tag -width "X." -compact
-.It Sy 0
-Unknown
-.It Sy 1
-For PD, the power source is the PSE. For PSE, the power source is the
-primary power source.
-.It Sy 2
-For PD, the power source is a local source. 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 3
-For PD, the power source is both the PSE and a local source. For PSE,
-this value should not be used.
-.El
-For
-.Ar priority ,
-see what is done for LLDP-MED MDI/POE.
+should be either 1 or 2, indicating which if the device conforms to
+802.3at type 1 or 802.3at type 2. Values ofr
+.Ar source
+and
+.Ar priority
+are the same as for LLDP-MED POE-MDI TLV.
.Ar requested
and
.Ar allocated
-are respectively the PD requested power value and the PSE allocated
-power value. This should be expressed in milliwatts.
-.El
+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 3
+.D1 configure dot3 power pd supported enabled powerpairs spare class 3 type 1 source pse priority low requested 10000 allocated 15000
+.Ed
+
+.Cd unconfigure
+.Op ports Ar ethX Op ...
+.Cd dot3 power
+.Bd -ragged -offset XXXXXX
+Do not advertise the Dot3 POE-MDI TLV for the given ports (or on all
+ports if no port was provided).
+.Ed
+
+.Ed
.Sh FILES
.Bl -tag -width "/var/run/lldpd.socketXX" -compact
.It /var/run/lldpd.socket
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
+#include <histedit.h>
-#include "../log.h"
-#include "../ctl.h"
#include "client.h"
-static void usage(void);
-
#ifdef HAVE___PROGNAME
extern const char *__progname;
#else
# define __progname "lldpctl"
#endif
+/* Global for completion */
+static struct cmd_node *root = NULL;
static void
-usage(void)
+usage()
{
- fprintf(stderr, "Usage: %s [OPTIONS ...] [INTERFACES ...]\n", __progname);
+ 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, "-a Display all remote ports, including hidden ones.\n");
- fprintf(stderr, "-w Watch for changes.\n");
- fprintf(stderr, "-C Display global configuration of lldpd.\n");
- fprintf(stderr, "-N Make lldpd transmit LLDP PDU now.\n");
fprintf(stderr, "-f format Choose output format (plain, keyvalue or xml).\n");
- fprintf(stderr, "-L location Enable the transmission of LLDP-MED location TLV for the\n");
- fprintf(stderr, " given interfaces. Can be repeated to enable the transmission\n");
- fprintf(stderr, " of the location in several formats.\n");
- fprintf(stderr, "-P policy Enable the transmission of LLDP-MED Network Policy TLVs\n");
- fprintf(stderr, " for the given interfaces. Can be repeated to specify\n");
- fprintf(stderr, " different policies.\n");
- fprintf(stderr, "-O poe Enable the transmission of LLDP-MED POE-MDI TLV\n");
- fprintf(stderr, " for the given interfaces.\n");
- fprintf(stderr, "-o poe Enable the transmission of Dot3 POE-MDI TLV\n");
- fprintf(stderr, " for the given interfaces.\n");
fprintf(stderr, "\n");
exit(1);
}
-struct cbargs {
- int argc;
- char **argv;
- struct writer *w;
-};
-
-void
-watchcb(lldpctl_conn_t *conn,
- lldpctl_change_t type,
- lldpctl_atom_t *interface,
- lldpctl_atom_t *neighbor,
- void *data)
+static int
+is_privileged()
+{
+ return (!(getuid() != geteuid() || getgid() != getegid()));
+}
+
+static char*
+prompt(EditLine *el)
+{
+ int privileged = is_privileged();
+ if (privileged)
+ return "[lldpctl] # ";
+ return "[lldpctl] $ ";
+}
+
+static int must_exit = 0;
+/**
+ * Exit the interpreter.
+ */
+static int
+cmd_exit(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, void *arg)
{
- int i;
- struct cbargs *args = data;
- optind = 0;
- while (getopt(args->argc, args->argv, LLDPCTL_ARGS) != -1);
- if (optind < args->argc) {
- for (i = optind; i < args->argc; i++)
- if (strcmp(args->argv[i],
- lldpctl_atom_get_str(interface,
- lldpctl_k_interface_name)) == 0)
- break;
- if (i == args->argc)
- return;
+ log_info("lldpctl", "quit lldpctl");
+ 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, 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;
}
- switch (type) {
- case lldpctl_c_deleted:
- tag_start(args->w, "lldp-deleted", "LLDP neighbor deleted");
- break;
- case lldpctl_c_updated:
- tag_start(args->w, "lldp-updated", "LLDP neighbor updated");
- break;
- case lldpctl_c_added:
- tag_start(args->w, "lldp-added", "LLDP neighbor added");
- break;
- default: return;
+ 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;
}
- display_interface(conn, args->w, 1, interface, neighbor);
- tag_end(args->w);
+ log_info("lldpctl", "immediate retransmission requested successfuly");
+ lldpctl_atom_dec_ref(config);
+ return 1;
+}
+
+static unsigned char
+_cmd_complete(EditLine *el, int ch, int all)
+{
+ int rc = CC_ERROR;
+ Tokenizer *eltok;
+ if ((eltok = tok_init(NULL)) == NULL)
+ goto end;
+
+ const LineInfo *li = el_line(el);
+
+ const char **argv;
+ char *compl;
+ int argc, cursorc, cursoro;
+ if (tok_line(eltok, li, &argc, &argv, &cursorc, &cursoro) != 0)
+ goto end;
+ compl = commands_complete(root, argc, argv, cursorc, cursoro, all);
+ if (compl) {
+ el_deletestr(el, cursoro);
+ if (el_insertstr(el, compl) == -1) {
+ free(compl);
+ goto end;
+ }
+ free(compl);
+ rc = CC_REDISPLAY;
+ goto end;
+ }
+ /* No completion or several completion available. We beep. */
+ el_beep(el);
+ rc = CC_REDISPLAY;
+end:
+ if (eltok) tok_end(eltok);
+ return rc;
+}
+
+static unsigned char
+cmd_complete(EditLine *el, int ch)
+{
+ return _cmd_complete(el, ch, 0);
+}
+
+static unsigned char
+cmd_help(EditLine *el, int ch)
+{
+ return _cmd_complete(el, ch, 1);
+}
+
+static struct cmd_node*
+register_commands()
+{
+ root = commands_root();
+ register_commands_show(root);
+ register_commands_watch(root);
+ if (is_privileged()) {
+ commands_new(
+ 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_new(
+ commands_new(root, "exit", "Exit interpreter", NULL, NULL, NULL),
+ NEWLINE, "Exit interpreter", NULL, cmd_exit, NULL);
+ return root;
}
int
main(int argc, char *argv[])
{
- int ch, debug = 1;
+ int ch, debug = 1, rc = EXIT_FAILURE;
char *fmt = "plain";
- int action = 0, hidden = 0, watch = 0, configuration = 0, now = 0;
lldpctl_conn_t *conn;
- struct cbargs args;
+ struct writer *w;
+
+ EditLine *el;
+ History *elhistory;
+ HistEvent elhistev;
+ Tokenizer *eltok;
/* Get and parse command line options */
- while ((ch = getopt(argc, argv, LLDPCTL_ARGS)) != -1) {
+ while ((ch = getopt(argc, argv, "hdvf:")) != -1) {
switch (ch) {
case 'h':
usage();
fprintf(stdout, "%s\n", PACKAGE_VERSION);
exit(0);
break;
- case 'a':
- hidden = 1;
- break;
case 'f':
fmt = optarg;
break;
- case 'L':
- case 'P':
- case 'O':
- case 'o':
- action = 1;
- break;
- case 'w':
- watch = 1;
- break;
- case 'C':
- configuration = 1;
- break;
- case 'N':
- now = 1;
- break;
default:
usage();
}
log_init(debug, __progname);
- if ((action != 0) &&
- (getuid() != geteuid() || getgid() != getegid())) {
- fatalx("mere mortals may not do that, admin privileges are required.");
+ /* Register commands */
+ root = register_commands();
+
+ /* Init editline */
+ log_debug("lldpctl", "init editline");
+ el = el_init("lldpctl", stdin, stdout, stderr);
+ if (el == NULL) {
+ log_warnx("lldpctl", "unable to setup editline");
+ goto end;
+ }
+ el_set(el, EL_PROMPT, prompt);
+ el_set(el, EL_SIGNAL, 0);
+ el_set(el, EL_EDITOR, "emacs");
+ /* If on a TTY, setup completion */
+ if (isatty(STDERR_FILENO)) {
+ el_set(el, EL_ADDFN, "command_complete",
+ "Execute completion", cmd_complete);
+ el_set(el, EL_ADDFN, "command_help",
+ "Show completion", cmd_help);
+ el_set(el, EL_BIND, "^I", "command_complete", NULL);
+ el_set(el, EL_BIND, "?", "command_help", NULL);
}
- conn = lldpctl_new(NULL, NULL, NULL);
- if (conn == NULL) exit(EXIT_FAILURE);
-
- args.argc = argc;
- args.argv = argv;
- if (watch) {
- if (lldpctl_watch_callback(conn, watchcb, &args) < 0) {
- log_warnx(NULL, "unable to watch for neighbors. %s",
- lldpctl_last_strerror(conn));
- exit(EXIT_FAILURE);
- }
+ /* Init history */
+ elhistory = history_init();
+ if (elhistory == NULL) {
+ log_warnx("lldpctl", "unable to enable history");
+ } else {
+ history(elhistory, &elhistev, H_SETSIZE, 800);
+ el_set(el, EL_HIST, history, elhistory);
}
- do {
- if (strcmp(fmt, "plain") == 0) {
- args.w = txt_init(stdout);
- } else if (strcmp(fmt, "keyvalue") == 0) {
- args.w = kv_init(stdout);
+ /* Init tokenizer */
+ eltok = tok_init(NULL);
+ if (eltok == NULL) {
+ log_warnx("lldpctl", "unable to initialize tokenizer");
+ goto end;
+ }
+
+ /* Make a connection */
+ log_debug("lldpctl", "connect to lldpd");
+ conn = lldpctl_new(NULL, NULL, NULL);
+ if (conn == NULL)
+ exit(EXIT_FAILURE);
+
+ while (!must_exit) {
+ const char *line;
+ const char **argv;
+ int count, n, argc;
+
+ /* Read a new line. */
+ line = el_gets(el, &count);
+ if (line == NULL) break;
+
+ /* Tokenize it */
+ log_debug("lldpctl", "tokenize command line");
+ n = tok_str(eltok, line, &argc, &argv);
+ switch (n) {
+ case -1:
+ log_warnx("lldpctl", "internal error while tokenizing");
+ goto end;
+ case 1:
+ case 2:
+ case 3:
+ /* TODO: handle multiline statements */
+ log_warnx("lldpctl", "unmatched quotes");
+ tok_reset(eltok);
+ continue;
}
-#ifdef USE_XML
- else if (strcmp(fmt,"xml") == 0 ) {
- args.w = xml_init(stdout);
+ if (argc == 0) {
+ tok_reset(eltok);
+ continue;
}
+ if (elhistory) history(elhistory, &elhistev, H_ENTER, line);
+
+ /* Init output formatter */
+ if (strcmp(fmt, "plain") == 0) w = txt_init(stdout);
+ else if (strcmp(fmt, "keyvalue") == 0) w = kv_init(stdout);
+#ifdef USE_XML
+ else if (strcmp(fmt, "xml") == 0) w = xml_init(stdout);
#endif
#ifdef USE_JSON
- else if (strcmp(fmt, "json") == 0) {
- args.w = json_init(stdout);
- }
+ else if (strcmp(fmt, "json") == 0) w = json_init(stdout);
#endif
- else {
- args.w = txt_init(stdout);
- }
+ else w = txt_init(stdout);
- if (action) {
- modify_interfaces(conn, argc, argv, optind);
- } else if (watch) {
- if (lldpctl_watch(conn) < 0) {
- log_warnx(NULL, "unable to watch for neighbors. %s",
- lldpctl_last_strerror(conn));
- watch = 0;
- }
- } else if (configuration) {
- display_configuration(conn, args.w);
- } else if (now) {
- lldpctl_atom_t *config = lldpctl_get_configuration(conn);
- if (config == NULL) {
- log_warnx(NULL, "unable to get configuration from lldpd. %s",
- lldpctl_last_strerror(conn));
- } else {
- if (lldpctl_atom_set_int(config,
- lldpctl_k_config_tx_interval, -1) == NULL) {
- log_warnx(NULL, "unable to ask lldpd for immediate retransmission. %s",
- lldpctl_last_strerror(conn));
- } else
- log_info(NULL, "immediate retransmission requested successfuly");
- lldpctl_atom_dec_ref(config);
- }
- } else {
- display_interfaces(conn, args.w,
- hidden, argc, argv);
- }
- args.w->finish(args.w);
- } while (watch);
+ /* Execute command */
+ if (commands_execute(conn, w,
+ root, argc, argv) != 0)
+ log_info("lldpctl", "an error occurred while executing last command");
+ w->finish(w);
+ tok_reset(eltok);
+ }
- lldpctl_release(conn);
- return EXIT_SUCCESS;
+ rc = EXIT_SUCCESS;
+end:
+ if (conn) lldpctl_release(conn);
+ if (eltok) tok_end(eltok);
+ if (elhistory) history_end(elhistory);
+ if (el) el_end(el);
+ if (root) commands_free(root);
+ return rc;
}
--- /dev/null
+# We have those one-time leaks that we don't bother to correct. Those leaks
+# are due to the conversion of the some map strings to tags. This
+# happens only when registering new commands, so once.
+{
+ one-time-memory-leak-with-med-civicaddress-map-conversion
+ Memcheck:Leak
+ ...
+ fun:strdup
+ fun:register_commands_medloc_addr
+ ...
+}
+{
+ one-time-memory-leak-with-med-policy-map-conversion
+ Memcheck:Leak
+ ...
+ fun:strdup
+ fun:register_commands_medpol
+ ...
+}
+{
+ one-time-memory-leak-with-power-map-conversion
+ Memcheck:Leak
+ ...
+ fun:strdup
+ fun:register_commands_pow_priority
+ ...
+}
+{
+ one-time-memory-leak-with-dot3-power-map-conversion
+ Memcheck:Leak
+ ...
+ fun:strdup
+ fun:register_commands_dot3pow
+ ...
+}
--- /dev/null
+/* -*- 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
+ */
+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;
+}
--- /dev/null
+/* -*- 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"
+
+/**
+ * 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, 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;
+}
+
+static int
+cmd_check_no_detailed_nor_summary(struct cmd_env *env, 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, void *arg)
+{
+ log_debug("lldpctl", "show running configuration");
+ display_configuration(conn, w);
+ return 1;
+}
+
+/**
+ * Callback for the next function to display a new neighbor.
+ */
+static void
+watchcb(lldpctl_conn_t *conn,
+ lldpctl_change_t type,
+ lldpctl_atom_t *interface,
+ lldpctl_atom_t *neighbor,
+ void *data)
+{
+ struct cmd_env *env = data;
+ struct writer *w = (struct writer *)cmdenv_get(env, "writer");
+ const char *interfaces = cmdenv_get(env, "ports");
+ if (interfaces && !contains(interfaces, lldpctl_atom_get_str(interface,
+ lldpctl_k_interface_name)))
+ return;
+
+ 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(conn, w, 1, interface, neighbor,
+ cmdenv_get(env, "summary")?DISPLAY_BRIEF:
+ cmdenv_get(env, "detailed")?DISPLAY_DETAILS:
+ DISPLAY_NORMAL);
+ tag_end(w);
+}
+
+/**
+ * Watch for neighbor changes.
+ */
+static int
+cmd_watch_neighbors(struct lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env, void *arg)
+{
+ int watch = 1;
+ log_debug("lldpctl", "watch for neighbor changes");
+ if (lldpctl_watch_callback(conn, watchcb, env) < 0) {
+ log_warnx("lldpctl", "unable to watch for neighbors. %s",
+ lldpctl_last_strerror(conn));
+ return 0;
+ }
+ cmdenv_put(env, "writer", (const char*)w); /* Hackish, but we really
+ * don't care. */
+ while (watch) {
+ if (lldpctl_watch(conn) < 0) {
+ log_warnx("lldpctl", "unable to watch for neighbors. %s",
+ lldpctl_last_strerror(conn));
+ watch = 0;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Register common subcommands for `watch` and `show neighbors`.
+ */
+void
+register_common_commands(struct cmd_node *root)
+{
+ /* With hidden neighbors */
+ commands_new(root,
+ "hidden",
+ "Include hidden neighbors",
+ cmd_check_no_env, cmd_store_env_and_pop, "hidden");
+
+ /* 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");
+
+ /* Some specific port */
+ commands_new(
+ commands_new(root,
+ "ports",
+ "Restrict to neighbors seen on some ports",
+ cmd_check_no_env, NULL, "ports"),
+ NULL,
+ "Restrict to neighbors on those ports (comma-separated list)",
+ NULL, cmd_store_env_value_and_pop2, "ports");
+}
+
+/**
+ * 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);
+
+ /* Neighbors data */
+ commands_new(neighbors,
+ NEWLINE,
+ "Show neighbors data",
+ NULL, cmd_show_neighbors, NULL);
+
+ register_common_commands(neighbors);
+
+ /* 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);
+
+ /* Neighbors data */
+ commands_new(watch,
+ NEWLINE,
+ "Monitor neighbors change",
+ NULL, cmd_watch_neighbors, NULL);
+
+ register_common_commands(watch);
+}
struct xml_writer_private * p = w->priv;
if (xmlTextWriterStartElement(p->xw, BAD_CAST tag) < 0)
- log_warnx(NULL, "cannot start '%s' element", tag);
+ 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(NULL, "cannot add attribute 'label' to element %s", tag);
+ log_warnx("lldpctl", "cannot add attribute 'label' to element %s", tag);
}
}
struct xml_writer_private * p = w->priv;
if (xmlTextWriterWriteFormatAttribute(p->xw, BAD_CAST tag, "%s", value) < 0)
- log_warnx(NULL, "cannot add attribute %s with value %s", tag, value);
+ log_warnx("lldpctl", "cannot add attribute %s with value %s", tag, value);
}
void xml_data(struct writer * w, const char * data) {
struct xml_writer_private * p = w->priv;
if (xmlTextWriterWriteString(p->xw, BAD_CAST data) < 0 )
- log_warnx(NULL, "cannot add '%s' as data to element", data);
+ log_warnx("lldpctl", "cannot add '%s' as data to element", data);
}
void xml_end(struct writer * w) {
struct xml_writer_private * p = w->priv;
if (xmlTextWriterEndElement(p->xw) < 0 )
- log_warnx(NULL, "cannot end element");
+ log_warnx("lldpctl", "cannot end element");
}
#define MY_ENCODING "UTF-8"
int failed = 0;
if (xmlTextWriterEndDocument(p->xw) < 0 ) {
- log_warnx(NULL, "cannot finish document");
+ log_warnx("lldpctl", "cannot finish document");
failed = 1;
}