1 /* -*- mode: c; c-file-style: "openbsd" -*- */
2 /*
3  * Copyright (c) 2013 Vincent Bernat <bernat@luffy.cx>
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <unistd.h>
19 #include <string.h>
20 
21 #include "client.h"
22 #include "../log.h"
23 
24 static int
cmd_medpower(struct lldpctl_conn_t * conn,struct writer * w,struct cmd_env * env,void * arg)25 cmd_medpower(struct lldpctl_conn_t *conn, struct writer *w,
26     struct cmd_env *env, void *arg)
27 {
28 	log_debug("lldpctl", "set MED power");
29 	lldpctl_atom_t *port;
30 	const char *name;
31 	while ((port = cmd_iterate_on_ports(conn, env, &name))) {
32 		lldpctl_atom_t *med_power;
33 		const char *what = NULL;
34 
35 		med_power = lldpctl_atom_get(port, lldpctl_k_port_med_power);
36 		if (med_power == NULL) {
37 			log_warnx("lldpctl", "unable to set LLDP-MED power: support seems unavailable");
38 			continue; /* Need to finish the loop */
39 		}
40 
41 		if ((what = "device type", lldpctl_atom_set_str(med_power,
42 			    lldpctl_k_med_power_type,
43 			    cmdenv_get(env, "device-type"))) == NULL ||
44 		    (what = "power source", lldpctl_atom_set_str(med_power,
45 			lldpctl_k_med_power_source,
46 			cmdenv_get(env, "source"))) == NULL ||
47 		    (what = "power priority", lldpctl_atom_set_str(med_power,
48 			lldpctl_k_med_power_priority,
49 			cmdenv_get(env, "priority"))) == NULL ||
50 		    (what = "power value", lldpctl_atom_set_str(med_power,
51 			lldpctl_k_med_power_val,
52 			cmdenv_get(env, "value"))) == NULL)
53 			log_warnx("lldpctl",
54 			    "unable to set LLDP MED power value for %s on %s. %s.",
55 			    what, name, lldpctl_last_strerror(conn));
56 		else {
57 			if (lldpctl_atom_set(port, lldpctl_k_port_med_power,
58 				med_power) == NULL) {
59 				log_warnx("lldpctl", "unable to set LLDP MED power on %s. %s.",
60 				    name, lldpctl_last_strerror(conn));
61 			} else
62 				log_info("lldpctl", "LLDP-MED power has been set for port %s",
63 				    name);
64 		}
65 
66 		lldpctl_atom_dec_ref(med_power);
67 	}
68 	return 1;
69 }
70 
71 static int
cmd_store_powerpairs_env_value_and_pop2(struct lldpctl_conn_t * conn,struct writer * w,struct cmd_env * env,void * value)72 cmd_store_powerpairs_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w,
73     struct cmd_env *env, void *value)
74 {
75 	return cmd_store_something_env_value_and_pop2("powerpairs", env, value);
76 }
77 static int
cmd_store_class_env_value_and_pop2(struct lldpctl_conn_t * conn,struct writer * w,struct cmd_env * env,void * value)78 cmd_store_class_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w,
79     struct cmd_env *env, void *value)
80 {
81 	return cmd_store_something_env_value_and_pop2("class", env, value);
82 }
83 static int
cmd_store_prio_env_value_and_pop2(struct lldpctl_conn_t * conn,struct writer * w,struct cmd_env * env,void * value)84 cmd_store_prio_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w,
85     struct cmd_env *env, void *value)
86 {
87 	return cmd_store_something_env_value_and_pop2("priority", env, value);
88 }
89 
90 static int
cmd_dot3power(struct lldpctl_conn_t * conn,struct writer * w,struct cmd_env * env,void * arg)91 cmd_dot3power(struct lldpctl_conn_t *conn, struct writer *w,
92     struct cmd_env *env, void *arg)
93 {
94 	log_debug("lldpctl", "set dot3 power");
95 	lldpctl_atom_t *port;
96 	const char *name;
97 	while ((port = cmd_iterate_on_ports(conn, env, &name))) {
98 		lldpctl_atom_t *dot3_power;
99 		const char *what = NULL;
100 		int ok = 1;
101 
102 		dot3_power = lldpctl_atom_get(port, lldpctl_k_port_dot3_power);
103 		if (dot3_power == NULL) {
104 			log_warnx("lldpctl", "unable to set Dot3 power: support seems unavailable");
105 			continue; /* Need to finish the loop */
106 		}
107 
108 		if ((what = "device type", lldpctl_atom_set_str(dot3_power,
109 			    lldpctl_k_dot3_power_devicetype,
110 			    cmdenv_get(env, "device-type"))) == NULL ||
111 		    /* Flags */
112 		    (what = "supported flag", lldpctl_atom_set_int(dot3_power,
113 			lldpctl_k_dot3_power_supported,
114 			cmdenv_get(env, "supported")?1:0)) == NULL ||
115 		    (what = "enabled flag", lldpctl_atom_set_int(dot3_power,
116 			lldpctl_k_dot3_power_enabled,
117 			cmdenv_get(env, "enabled")?1:0)) == NULL ||
118 		    (what = "paircontrol flag", lldpctl_atom_set_int(dot3_power,
119 			lldpctl_k_dot3_power_paircontrol,
120 			cmdenv_get(env, "paircontrol")?1:0)) == NULL ||
121 		    /* Powerpairs */
122 		    (what = "power pairs", lldpctl_atom_set_str(dot3_power,
123 			lldpctl_k_dot3_power_pairs,
124 			cmdenv_get(env, "powerpairs")?cmdenv_get(env, "powerpairs"):"unknown")) == NULL ||
125 		    /* Class */
126 		    (what = "power class", cmdenv_get(env, "class")?
127 			lldpctl_atom_set_str(dot3_power,
128 			    lldpctl_k_dot3_power_class,
129 			    cmdenv_get(env, "class")):
130 			lldpctl_atom_set_int(dot3_power,
131 			    lldpctl_k_dot3_power_class, 0)) == NULL ||
132 		    (what = "802.3at type", lldpctl_atom_set_int(dot3_power,
133 			lldpctl_k_dot3_power_type, 0)) == NULL) {
134 			log_warnx("lldpctl",
135 			    "unable to set LLDP Dot3 power value for %s on %s. %s.",
136 			    what, name, lldpctl_last_strerror(conn));
137 			ok = 0;
138 		} else if (cmdenv_get(env, "typeat")) {
139 			int typeat = cmdenv_get(env, "typeat")[0] - '0';
140 			const char *source = cmdenv_get(env, "source");
141 			if ((what = "802.3at type", lldpctl_atom_set_int(dot3_power,
142 				    lldpctl_k_dot3_power_type,
143 				    typeat)) == NULL ||
144 			    (what = "source", lldpctl_atom_set_int(dot3_power,
145 				lldpctl_k_dot3_power_source,
146 				(!strcmp(source, "primary"))?LLDP_DOT3_POWER_SOURCE_PRIMARY:
147 				(!strcmp(source, "backup"))? LLDP_DOT3_POWER_SOURCE_BACKUP:
148 				(!strcmp(source, "pse"))?    LLDP_DOT3_POWER_SOURCE_PSE:
149 				(!strcmp(source, "local"))?  LLDP_DOT3_POWER_SOURCE_LOCAL:
150 				(!strcmp(source, "both"))?   LLDP_DOT3_POWER_SOURCE_BOTH:
151 				LLDP_DOT3_POWER_SOURCE_UNKNOWN)) == NULL ||
152 			    (what = "priority", lldpctl_atom_set_str(dot3_power,
153 				lldpctl_k_dot3_power_priority,
154 				cmdenv_get(env, "priority"))) == NULL ||
155 			    (what = "requested power", lldpctl_atom_set_str(dot3_power,
156 				lldpctl_k_dot3_power_requested,
157 				cmdenv_get(env, "requested"))) == NULL ||
158 			    (what = "allocated power", lldpctl_atom_set_str(dot3_power,
159 				lldpctl_k_dot3_power_allocated,
160 				cmdenv_get(env, "allocated"))) == NULL) {
161 				log_warnx("lldpctl", "unable to set LLDP Dot3 power value for %s on %s. %s.",
162 				    what, name, lldpctl_last_strerror(conn));
163 				ok = 0;
164 			}
165 		}
166 		if (ok) {
167 			if (lldpctl_atom_set(port, lldpctl_k_port_dot3_power,
168 				dot3_power) == NULL) {
169 				log_warnx("lldpctl", "unable to set LLDP Dot3 power on %s. %s.",
170 				    name, lldpctl_last_strerror(conn));
171 			} else
172 				log_info("lldpctl", "LLDP Dot3 power has been set for port %s",
173 				    name);
174 		}
175 
176 		lldpctl_atom_dec_ref(dot3_power);
177 	}
178 	return 1;
179 }
180 
181 static int
cmd_check_type_but_no(struct cmd_env * env,void * arg)182 cmd_check_type_but_no(struct cmd_env *env, void *arg)
183 {
184 	const char *what = arg;
185 	if (!cmdenv_get(env, "device-type")) return 0;
186 	if (cmdenv_get(env, what)) return 0;
187 	return 1;
188 }
189 static int
cmd_check_typeat_but_no(struct cmd_env * env,void * arg)190 cmd_check_typeat_but_no(struct cmd_env *env, void *arg)
191 {
192 	const char *what = arg;
193 	if (!cmdenv_get(env, "typeat")) return 0;
194 	if (cmdenv_get(env, what)) return 0;
195 	return 1;
196 }
197 static int
cmd_check_type(struct cmd_env * env,const char * type)198 cmd_check_type(struct cmd_env *env, const char *type)
199 {
200 	const char *etype = cmdenv_get(env, "device-type");
201 	if (!etype) return 0;
202 	return (!strcmp(type, etype));
203 }
204 static int
cmd_check_pse(struct cmd_env * env,void * arg)205 cmd_check_pse(struct cmd_env *env, void *arg)
206 {
207 	return cmd_check_type(env, "pse");
208 }
209 static int
cmd_check_pd(struct cmd_env * env,void * arg)210 cmd_check_pd(struct cmd_env *env, void *arg)
211 {
212 	return cmd_check_type(env, "pd");
213 }
214 
215 static void
register_commands_pow_source(struct cmd_node * source)216 register_commands_pow_source(struct cmd_node *source)
217 {
218 	commands_new(source,
219 	    "unknown", "Unknown power source",
220 	    NULL, cmd_store_env_value_and_pop2, "source");
221 	commands_new(source,
222 	    "primary", "Primary power source",
223 	    cmd_check_pse, cmd_store_env_value_and_pop2, "source");
224 	commands_new(source,
225 	    "backup", "Backup power source",
226 	    cmd_check_pse, cmd_store_env_value_and_pop2, "source");
227 	commands_new(source,
228 	    "pse", "Power source is PSE",
229 	    cmd_check_pd, cmd_store_env_value_and_pop2, "source");
230 	commands_new(source,
231 	    "local", "Local power source",
232 	    cmd_check_pd, cmd_store_env_value_and_pop2, "source");
233 	commands_new(source,
234 	    "both", "Both PSE and local source available",
235 	    cmd_check_pd, cmd_store_env_value_and_pop2, "source");
236 }
237 
238 static void
register_commands_pow_priority(struct cmd_node * priority,int key)239 register_commands_pow_priority(struct cmd_node *priority, int key)
240 {
241 	for (lldpctl_map_t *prio_map =
242 		 lldpctl_key_get_map(key);
243 	     prio_map->string;
244 	     prio_map++) {
245 		char *tag = strdup(totag(prio_map->string));
246 		SUPPRESS_LEAK(tag);
247 		commands_new(
248 			priority,
249 			tag,
250 			prio_map->string,
251 			NULL, cmd_store_prio_env_value_and_pop2, prio_map->string);
252 	}
253 }
254 
255 /**
256  * Register `configure med power` commands.
257  */
258 void
register_commands_medpow(struct cmd_node * configure_med)259 register_commands_medpow(struct cmd_node *configure_med)
260 {
261 	struct cmd_node *configure_medpower = commands_new(
262 		configure_med,
263 		"power", "MED power configuration",
264 		NULL, NULL, NULL);
265 
266 	commands_new(
267 		configure_medpower,
268 		NEWLINE, "Apply new MED power configuration",
269 		cmd_check_env, cmd_medpower, "device-type,source,priority,value");
270 
271 	/* Type: PSE or PD */
272 	commands_new(
273 		configure_medpower,
274 		"pd", "MED power consumer",
275 		cmd_check_no_env, cmd_store_env_value_and_pop, "device-type");
276 	commands_new(
277 		configure_medpower,
278 		"pse", "MED power provider",
279 		cmd_check_no_env, cmd_store_env_value_and_pop, "device-type");
280 
281 	/* Source */
282 	struct cmd_node *source = commands_new(
283 		configure_medpower,
284 		"source", "MED power source",
285 		cmd_check_type_but_no, NULL, "source");
286 	register_commands_pow_source(source);
287 
288 	/* Priority */
289 	struct cmd_node *priority = commands_new(
290 		configure_medpower,
291 		"priority", "MED power priority",
292 		cmd_check_type_but_no, NULL, "priority");
293 	register_commands_pow_priority(priority, lldpctl_k_med_power_priority);
294 
295 	/* Value */
296 	commands_new(
297 		commands_new(configure_medpower,
298 		    "value", "MED power value",
299 		    cmd_check_type_but_no, NULL, "value"),
300 		NULL, "MED power value in milliwatts",
301 		NULL, cmd_store_env_value_and_pop2, "value");
302 }
303 
304 static int
cmd_check_env_power(struct cmd_env * env,void * nothing)305 cmd_check_env_power(struct cmd_env *env, void *nothing)
306 {
307 	/* We need type. If we type is PSE, we need powerpairs. If we have
308 	 * typeat, we also request source, priority, requested and allocated. */
309 	if (!cmdenv_get(env, "device-type")) return 0;
310 	if (!strcmp(cmdenv_get(env, "device-type"), "pse") &&
311 	    !cmdenv_get(env, "powerpairs")) return 0;
312 	if (cmdenv_get(env, "typeat")) {
313 		return (!!cmdenv_get(env, "source") &&
314 		    !!cmdenv_get(env, "priority") &&
315 		    !!cmdenv_get(env, "requested") &&
316 		    !!cmdenv_get(env, "allocated"));
317 	}
318 	return 1;
319 }
320 
321 /**
322  * Register `configure med dot3` commands.
323  */
324 void
register_commands_dot3pow(struct cmd_node * configure_dot3)325 register_commands_dot3pow(struct cmd_node *configure_dot3)
326 {
327 	struct cmd_node *configure_dot3power = commands_new(
328 		configure_dot3,
329 		"power", "Dot3 power configuration",
330 		NULL, NULL, NULL);
331 
332 	commands_new(
333 		configure_dot3power,
334 		NEWLINE, "Apply new Dot3 power configuration",
335 		cmd_check_env_power, cmd_dot3power, NULL);
336 
337 	/* Type: PSE or PD */
338 	commands_new(
339 		configure_dot3power,
340 		"pd", "Dot3 power consumer",
341 		cmd_check_no_env, cmd_store_env_value_and_pop, "device-type");
342 	commands_new(
343 		configure_dot3power,
344 		"pse", "Dot3 power provider",
345 		cmd_check_no_env, cmd_store_env_value_and_pop, "device-type");
346 
347 	/* Flags */
348 	commands_new(
349 		configure_dot3power,
350 		"supported", "MDI power support present",
351 		cmd_check_type_but_no, cmd_store_env_and_pop, "supported");
352 	commands_new(
353 		configure_dot3power,
354 		"enabled", "MDI power support enabled",
355 		cmd_check_type_but_no, cmd_store_env_and_pop, "enabled");
356 	commands_new(
357 		configure_dot3power,
358 		"paircontrol", "MDI power pair can be selected",
359 		cmd_check_type_but_no, cmd_store_env_and_pop, "paircontrol");
360 
361 	/* Power pairs */
362 	struct cmd_node *powerpairs = commands_new(
363 		configure_dot3power,
364 		"powerpairs", "Which pairs are currently used for power",
365 		cmd_check_pse, NULL, "powerpairs");
366 	for (lldpctl_map_t *pp_map =
367 		 lldpctl_key_get_map(lldpctl_k_dot3_power_pairs);
368 	     pp_map->string;
369 	     pp_map++) {
370 		commands_new(
371 			powerpairs,
372 			pp_map->string,
373 			pp_map->string,
374 			NULL, cmd_store_powerpairs_env_value_and_pop2, pp_map->string);
375 	}
376 
377 	/* Class */
378 	struct cmd_node *class = commands_new(
379 		configure_dot3power,
380 		"class", "Power class",
381 		cmd_check_type_but_no, NULL, "class");
382 	for (lldpctl_map_t *class_map =
383 		 lldpctl_key_get_map(lldpctl_k_dot3_power_class);
384 	     class_map->string;
385 	     class_map++) {
386 		const char *tag = strdup(totag(class_map->string));
387 		SUPPRESS_LEAK(tag);
388 		commands_new(
389 			class,
390 			tag,
391 			class_map->string,
392 			NULL, cmd_store_class_env_value_and_pop2, class_map->string);
393 	}
394 
395 	/* 802.3at type */
396 	struct cmd_node *typeat = commands_new(
397 		configure_dot3power,
398 		"type", "802.3at device type",
399 		cmd_check_type_but_no, NULL, "typeat");
400 	commands_new(typeat,
401 	    "1", "802.3at type 1",
402 	    NULL, cmd_store_env_value_and_pop2, "typeat");
403 	commands_new(typeat,
404 	    "2", "802.3at type 2",
405 	    NULL, cmd_store_env_value_and_pop2, "typeat");
406 
407 	/* Source */
408 	struct cmd_node *source = commands_new(
409 		configure_dot3power,
410 		"source", "802.3at dot3 power source (mandatory)",
411 		cmd_check_typeat_but_no, NULL, "source");
412 	register_commands_pow_source(source);
413 
414 	/* Priority */
415 	struct cmd_node *priority = commands_new(
416 		configure_dot3power,
417 		"priority", "802.3at dot3 power priority (mandatory)",
418 		cmd_check_typeat_but_no, NULL, "priority");
419 	register_commands_pow_priority(priority, lldpctl_k_dot3_power_priority);
420 
421 	/* Values */
422 	commands_new(
423 		commands_new(configure_dot3power,
424 		    "requested", "802.3at dot3 power value requested (mandatory)",
425 		    cmd_check_typeat_but_no, NULL, "requested"),
426 		NULL, "802.3at power value requested in milliwatts",
427 		NULL, cmd_store_env_value_and_pop2, "requested");
428 	commands_new(
429 		commands_new(configure_dot3power,
430 		    "allocated", "802.3at dot3 power value allocated (mandatory)",
431 		    cmd_check_typeat_but_no, NULL, "allocated"),
432 		NULL, "802.3at power value allocated in milliwatts",
433 		NULL, cmd_store_env_value_and_pop2, "allocated");
434 }
435