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