]> git.proxmox.com Git - mirror_ovs.git/blob - extras/ezio/ovs-switchui.c
Merge "master" into "next".
[mirror_ovs.git] / extras / ezio / ovs-switchui.c
1 /* Copyright (c) 2008, 2009, 2010 Nicira Networks, Inc.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <config.h>
17 #include <arpa/inet.h>
18 #include <assert.h>
19 #include <ctype.h>
20 #include <curses.h>
21 #include <errno.h>
22 #include <getopt.h>
23 #include <inttypes.h>
24 #include <math.h>
25 #include <pcre.h>
26 #include <signal.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/stat.h>
30 #include <term.h>
31 #include <unistd.h>
32 #include "command-line.h"
33 #include "daemon.h"
34 #include "dynamic-string.h"
35 #include "ezio.h"
36 #include "fatal-signal.h"
37 #include "netdev.h"
38 #include "ofpbuf.h"
39 #include "openflow/nicira-ext.h"
40 #include "openflow/openflow.h"
41 #include "packets.h"
42 #include "poll-loop.h"
43 #include "process.h"
44 #include "random.h"
45 #include "rconn.h"
46 #include "socket-util.h"
47 #include "svec.h"
48 #include "timeval.h"
49 #include "util.h"
50 #include "vconn.h"
51 #include "xtoxll.h"
52
53 #define THIS_MODULE VLM_switchui
54 #include "vlog.h"
55
56 static void parse_options(int argc, char *argv[]);
57 static void usage(void);
58
59 static void initialize_terminal(void);
60 static void restore_terminal(void *aux);
61
62 enum priority {
63 P_STATUS = 5,
64 P_PROGRESS = 10,
65 P_WARNING = 15,
66 P_ERROR = 20,
67 P_FATAL = 25
68 };
69
70 struct message;
71 static void emit(struct message **, enum priority, const char *, ...)
72 PRINTF_FORMAT(3, 4);
73 static void emit_function(struct message **, enum priority,
74 void (*function)(void *aux), void *aux);
75 static int shown(struct message **);
76 static void clear_messages(void);
77 static bool empty_message(const struct message *);
78 static struct message *best_message(void);
79 static struct message *next_message(struct message *);
80 static struct message *prev_message(struct message *);
81 static void put_message(const struct message *);
82 static void message_shown(struct message *);
83 static void age_messages(void);
84
85 struct pair {
86 char *name;
87 char *value;
88 };
89
90 struct dict {
91 struct pair *pairs;
92 size_t n, max;
93 };
94
95 static void dict_init(struct dict *);
96 static void dict_add(struct dict *, const char *name, const char *value);
97 static void dict_add_nocopy(struct dict *, char *name, char *value);
98 static void dict_delete(struct dict *, const char *name);
99 static void dict_parse(struct dict *, const char *data, size_t nbytes);
100 static void dict_free(struct dict *);
101 static bool dict_lookup(const struct dict *,
102 const char *name, const char **value);
103 static int dict_get_int(const struct dict *, const char *name, int def);
104 static bool dict_get_bool(const struct dict *, const char *name, bool def);
105 static const char *dict_get_string(const struct dict *,
106 const char *name, const char *def);
107 static uint32_t dict_get_ip(const struct dict *, const char *name);
108
109 static void addf(const char *format, ...) PRINTF_FORMAT(1, 2);
110
111 static void fetch_status(struct rconn *, struct dict *, long long int timeout);
112 static bool parse_reply(void *, struct dict *, uint32_t xid);
113 static void compose_messages(const struct dict *, struct rconn *rconn);
114
115 static void show_flows(struct rconn *);
116 static void show_dpid_ip(struct rconn *, const struct dict *);
117 static void show_ofproto_state(const struct dict *);
118 static void show_fail_open_state(const struct dict *);
119 static void show_discovery_state(const struct dict *);
120 static void show_remote_state(const struct dict *);
121 static void show_data_rates(struct rconn *, const struct dict *);
122
123 static void init_reboot_notifier(void);
124 static bool show_reboot_state(void);
125
126 static void show_string(const char *string);
127 static void block_until(long long timeout);
128 static void menu(const struct dict *);
129 static void drain_keyboard_buffer(void);
130
131 static const char *progress(void);
132
133 int
134 main(int argc, char *argv[])
135 {
136 struct rconn *rconn;
137 struct message *msg;
138 int countdown = 5;
139 bool user_selected;
140 bool debug_mode;
141
142 /* Tracking keystroke repeat counts. */
143 int last_key = 0;
144 long long int last_key_time = 0;
145 int repeat_count = 0;
146
147 proctitle_init(argc, argv);
148 set_program_name(argv[0]);
149 time_init();
150 vlog_init();
151 parse_options(argc, argv);
152 signal(SIGPIPE, SIG_IGN);
153 vlog_set_levels(VLM_ANY_MODULE, VLF_CONSOLE, VLL_EMER);
154 init_reboot_notifier();
155
156 argc -= optind;
157 argv += optind;
158 if (argc != 1) {
159 ovs_fatal(0, "exactly one non-option argument required; "
160 "use --help for help");
161 }
162
163 rconn = rconn_new(argv[0], 5, 5);
164
165 die_if_already_running();
166 daemonize();
167
168 initialize_terminal();
169 fatal_signal_add_hook(restore_terminal, NULL, NULL, true);
170
171 msg = NULL;
172 countdown = 0;
173 user_selected = false;
174 debug_mode = false;
175 for (;;) {
176 struct dict dict;
177 long long timeout = time_msec() + 1000;
178
179 clear_messages();
180
181 dict_init(&dict);
182 fetch_status(rconn, &dict, timeout);
183 dict_add(&dict, "debug", debug_mode ? "true" : "false");
184 compose_messages(&dict, rconn);
185
186 if (countdown) {
187 if (!empty_message(msg)) {
188 countdown--;
189 } else {
190 msg = user_selected ? next_message(msg) : best_message();
191 countdown = 5;
192 }
193 } else {
194 msg = best_message();
195 countdown = 5;
196 user_selected = false;
197 }
198 if (!user_selected) {
199 message_shown(msg);
200 }
201
202 do {
203 for (;;) {
204 int c = getch();
205 if (c == ERR) {
206 break;
207 }
208
209 if (c != last_key || time_msec() > last_key_time + 250) {
210 repeat_count = 0;
211 }
212 last_key = c;
213 last_key_time = time_msec();
214 repeat_count++;
215
216 if (c == KEY_DOWN || c == KEY_UP) {
217 msg = (c == KEY_DOWN ? next_message(msg)
218 : prev_message(msg));
219 countdown = 5;
220 user_selected = true;
221 } else if (c == '\r' || c == '\n') {
222 countdown = 60;
223 user_selected = true;
224 if (repeat_count >= 20) {
225 debug_mode = !debug_mode;
226 show_string(debug_mode
227 ? "Debug Mode\nEnabled"
228 : "Debug Mode\nDisabled");
229 }
230 } else if (c == '\b' || c == '\x7f' ||
231 c == '\x1b' || c == KEY_BACKSPACE || c == KEY_DC) {
232 menu(&dict);
233 drain_keyboard_buffer();
234 break;
235 }
236 }
237
238 erase();
239 curs_set(0);
240 move(0, 0);
241 put_message(msg);
242 refresh();
243
244 poll_fd_wait(STDIN_FILENO, POLLIN);
245 poll_timer_wait(timeout - time_msec());
246 poll_block();
247 } while (time_msec() < timeout);
248 age_messages();
249 dict_free(&dict);
250 }
251
252 return 0;
253 }
254
255 static void
256 compose_messages(const struct dict *dict, struct rconn *rconn)
257 {
258 if (!show_reboot_state()) {
259 show_flows(rconn);
260 show_dpid_ip(rconn, dict);
261 show_ofproto_state(dict);
262 show_fail_open_state(dict);
263 show_discovery_state(dict);
264 show_remote_state(dict);
265 show_data_rates(rconn, dict);
266 }
267 }
268
269 struct put_flows_data {
270 struct rconn *rconn;
271 uint32_t xid;
272 uint32_t flow_count;
273 bool got_reply;
274 };
275
276 static void
277 parse_flow_reply(void *data, struct put_flows_data *pfd)
278 {
279 struct ofp_header *oh;
280 struct ofp_stats_reply *rpy;
281 struct ofp_aggregate_stats_reply *asr;
282 const size_t min_size = sizeof *rpy + sizeof *asr;
283
284 oh = data;
285 if (ntohs(oh->length) < min_size) {
286 VLOG_WARN("reply is too short (%"PRIu16")", ntohs(oh->length));
287 return;
288 }
289 if (oh->xid != pfd->xid) {
290 VLOG_WARN("xid 0x%08"PRIx32" != expected 0x%08"PRIx32,
291 oh->xid, pfd->xid);
292 return;
293 }
294 if (oh->type != OFPT_STATS_REPLY) {
295 VLOG_WARN("reply is wrong type %"PRIu8, oh->type);
296 return;
297 }
298
299 rpy = data;
300 if (rpy->type != htons(OFPST_AGGREGATE)) {
301 VLOG_WARN("reply has wrong stat type ID %08"PRIx16, rpy->type);
302 return;
303 }
304
305 asr = (struct ofp_aggregate_stats_reply *) rpy->body;
306 pfd->flow_count = ntohl(asr->flow_count);
307 pfd->got_reply = true;
308 }
309
310 static bool
311 have_icons(void)
312 {
313 const char *dico = tigetstr("dico");
314 return dico && dico != (const char *) -1;
315 }
316
317 static void
318 set_icon(int num, int r0, int r1, int r2, int r3, int r4, int r5, int r6,
319 int r7)
320 {
321 if (have_icons()) {
322 putp(tparm(tigetstr("dico"), num, r0, r1, r2, r3, r4, r5, r6, r7));
323 }
324 }
325
326 static void
327 set_repeated_icon(int num, int row)
328 {
329 set_icon(num, row, row, row, row, row, row, row, row);
330 }
331
332 #if 0
333 static void
334 set_brick_icon(int num, int n_solid)
335 {
336 const static int rows[6] = {_____, X____, XX___, XXX__, XXXX_, XXXXX};
337 set_repeated_icon(num, rows[n_solid < 0 ? 0
338 : n_solid > 5 ? 5
339 : n_solid]);
340 }
341 #endif
342
343 static int
344 icon_char(int num, int alternate)
345 {
346 return have_icons() ? 0x80 | num | A_ALTCHARSET : alternate;
347 }
348
349 static void
350 put_icon(int num, char alternate)
351 {
352 addch(icon_char(num, alternate));
353 }
354
355 #if 0
356 static void
357 bar_graph(int n_chars, int n_pixels)
358 {
359 int i;
360
361 if (n_pixels < 0) {
362 n_pixels = 0;
363 } else if (n_pixels > n_chars * 5) {
364 n_pixels = n_chars * 5;
365 }
366
367 if (n_pixels > 5) {
368 set_brick_icon(0, 5);
369 for (i = 0; i < n_pixels / 5; i++) {
370 put_icon(0, "#");
371 }
372 }
373 if (n_pixels % 5) {
374 set_brick_icon(1, n_pixels % 5);
375 put_icon(1, "#");
376 }
377 }
378 #endif
379
380 static void
381 put_flows(void *pfd_)
382 {
383 struct put_flows_data *pfd = pfd_;
384 static struct rconn_packet_counter *counter;
385 char host[64];
386
387 if (!counter) {
388 counter = rconn_packet_counter_create();
389 }
390
391 if (!pfd->xid) {
392 struct ofp_stats_request *rq;
393 struct ofp_aggregate_stats_request *asr;
394 struct ofpbuf *b;
395
396 pfd->xid = random_uint32();
397 rq = make_openflow_xid(sizeof *rq, OFPT_STATS_REQUEST,
398 pfd->xid, &b);
399 rq->type = htons(OFPST_AGGREGATE);
400 rq->flags = htons(0);
401 asr = ofpbuf_put_uninit(b, sizeof *asr);
402 memset(asr, 0, sizeof *asr);
403 asr->match.wildcards = htonl(OFPFW_ALL);
404 asr->table_id = 0xff;
405 asr->out_port = htons(OFPP_NONE);
406 update_openflow_length(b);
407 rconn_send_with_limit(pfd->rconn, b, counter, 10);
408 }
409
410 if (!pfd->got_reply) {
411 int i;
412
413 rconn_run(pfd->rconn);
414 for (i = 0; i < 50; i++) {
415 struct ofpbuf *b;
416
417 b = rconn_recv(pfd->rconn);
418 if (!b) {
419 break;
420 }
421
422 parse_flow_reply(b->data, pfd);
423 ofpbuf_delete(b);
424 if (pfd->got_reply) {
425 break;
426 }
427 }
428 }
429
430 gethostname(host, sizeof host);
431 host[sizeof host - 1] = '\0';
432 if (strlen(host) + 6 <= 16) {
433 addf("Host: %s\n", host);
434 } else {
435 addf("%s\n", host);
436 }
437 if (pfd->got_reply) {
438 addf("Flows: %"PRIu32, pfd->flow_count);
439 }
440
441 if (!pfd->got_reply) {
442 rconn_run_wait(pfd->rconn);
443 rconn_recv_wait(pfd->rconn);
444 }
445 }
446
447 static void
448 show_flows(struct rconn *rconn)
449 {
450 static struct message *m;
451 static struct put_flows_data pfd;
452
453 memset(&pfd, 0, sizeof pfd);
454 pfd.rconn = rconn;
455 emit_function(&m, P_STATUS, put_flows, &pfd);
456
457 }
458
459 struct put_dpid_ip_data {
460 struct rconn *rconn;
461 uint32_t xid;
462 uint64_t dpid;
463 char ip[16];
464 bool got_reply;
465 };
466
467 static void
468 parse_dp_reply(void *data, struct put_dpid_ip_data *pdid)
469 {
470 struct ofp_switch_features *osf;
471 struct ofp_header *oh;
472
473 oh = data;
474 if (ntohs(oh->length) < sizeof *osf) {
475 VLOG_WARN("reply is too short (%"PRIu16")", ntohs(oh->length));
476 return;
477 }
478 if (oh->xid != pdid->xid) {
479 VLOG_WARN("xid 0x%08"PRIx32" != expected 0x%08"PRIx32,
480 oh->xid, pdid->xid);
481 return;
482 }
483 if (oh->type != OFPT_FEATURES_REPLY) {
484 VLOG_WARN("reply is wrong type %"PRIu8, oh->type);
485 return;
486 }
487
488 osf = data;
489 pdid->dpid = ntohll(osf->datapath_id);
490 pdid->got_reply = true;
491 }
492
493 static void
494 put_dpid_id(void *pdid_)
495 {
496 struct put_dpid_ip_data *pdid = pdid_;
497 static struct rconn_packet_counter *counter;
498
499 if (!counter) {
500 counter = rconn_packet_counter_create();
501 }
502
503 if (!pdid->xid) {
504 struct ofp_header *oh;
505 struct ofpbuf *b;
506
507 pdid->xid = random_uint32();
508 oh = make_openflow_xid(sizeof *oh, OFPT_FEATURES_REQUEST,
509 pdid->xid, &b);
510 rconn_send_with_limit(pdid->rconn, b, counter, 10);
511 }
512
513 if (!pdid->got_reply) {
514 int i;
515
516 rconn_run(pdid->rconn);
517 for (i = 0; i < 50; i++) {
518 struct ofpbuf *b;
519
520 b = rconn_recv(pdid->rconn);
521 if (!b) {
522 break;
523 }
524
525 parse_dp_reply(b->data, pdid);
526 ofpbuf_delete(b);
527 if (pdid->got_reply) {
528 break;
529 }
530 }
531 }
532
533 addf("DP: ");
534 if (pdid->got_reply) {
535 addf("%012"PRIx64, pdid->dpid);
536 }
537 addf("\nIP: %s", pdid->ip);
538
539 if (!pdid->got_reply) {
540 rconn_run_wait(pdid->rconn);
541 rconn_recv_wait(pdid->rconn);
542 }
543 }
544
545 static void
546 show_dpid_ip(struct rconn *rconn, const struct dict *dict)
547 {
548 static struct message *m;
549 static struct put_dpid_ip_data pdid;
550 const char *is_connected, *local_ip;
551
552 dict_lookup(dict, "local.is-connected", &is_connected);
553 dict_lookup(dict, "remote.local-ip", &local_ip);
554 if (!is_connected && !local_ip) {
555 /* If we're not connected to the datapath and don't have a local IP,
556 * then we won't have anything useful to show anyhow. */
557 return;
558 }
559
560 memset(&pdid, 0, sizeof pdid);
561 pdid.rconn = rconn;
562 ovs_strlcpy(pdid.ip, local_ip ? local_ip : "", sizeof pdid.ip);
563 emit_function(&m, P_STATUS, put_dpid_id, &pdid);
564 }
565
566 static size_t
567 dict_find(const struct dict *dict, const char *name)
568 {
569 size_t i;
570
571 for (i = 0; i < dict->n; i++) {
572 const struct pair *p = &dict->pairs[i];
573 if (!strcmp(p->name, name)) {
574 return i;
575 }
576 }
577
578 return SIZE_MAX;
579 }
580
581 static bool
582 dict_lookup(const struct dict *dict, const char *name, const char **value)
583 {
584 size_t idx = dict_find(dict, name);
585 if (idx != SIZE_MAX) {
586 *value = dict->pairs[idx].value;
587 return true;
588 } else {
589 *value = NULL;
590 return false;
591 }
592 }
593
594 static const char *
595 dict_get(const struct dict *dict, const char *name)
596 {
597 const char *value;
598 return dict_lookup(dict, name, &value) ? value : NULL;
599 }
600
601 static int
602 dict_get_int(const struct dict *dict, const char *name, int def)
603 {
604 const char *value;
605 return dict_lookup(dict, name, &value) ? atoi(value) : def;
606 }
607
608 static bool
609 dict_get_bool(const struct dict *dict, const char *name, bool def)
610 {
611 const char *value;
612 if (dict_lookup(dict, name, &value)) {
613 if (!strcmp(value, "true")) {
614 return true;
615 }
616 if (!strcmp(value, "false")) {
617 return false;
618 }
619 }
620 return def;
621 }
622
623 static const char *
624 dict_get_string(const struct dict *dict, const char *name, const char *def)
625 {
626 const char *value;
627 return dict_lookup(dict, name, &value) ? value : def;
628 }
629
630 static uint32_t
631 dict_get_ip(const struct dict *dict, const char *name)
632 {
633 struct in_addr in;
634 return (inet_aton(dict_get_string(dict, name, ""), &in) ? in.s_addr
635 : htonl(0));
636 }
637
638 static void
639 addf(const char *format, ...)
640 {
641 char buf[128];
642 va_list args;
643
644 va_start(args, format);
645 vsnprintf(buf, sizeof buf, format, args);
646 va_end(args);
647
648 addstr(buf);
649 }
650
651 static void
652 show_ofproto_state(const struct dict *dict)
653 {
654 static struct message *msg;
655 const char *is_connected;
656
657 if (!dict_lookup(dict, "remote.is-connected", &is_connected)) {
658 /* Secchan not running or not responding. */
659 emit(&msg, P_ERROR, "Switch disabled");
660 }
661 }
662
663 static const char *
664 discovery_state_label(const char *name)
665 {
666 static struct dict *states;
667 if (!states) {
668 states = xmalloc(sizeof *states);
669 dict_init(states);
670 dict_add(states, "INIT", "Init");
671 dict_add(states, "INIT_REBOOT", "Init");
672 dict_add(states, "REBOOTING", "Init");
673 dict_add(states, "SELECTING", "Searching");
674 dict_add(states, "REQUESTING", "Requesting");
675 dict_add(states, "BOUND", "Got");
676 dict_add(states, "RENEWING", "Renewing");
677 dict_add(states, "REBINDING", "Rebinding");
678 dict_add(states, "RELEASED", "Released");
679 }
680 return dict_get_string(states, name, "Error");
681 }
682
683 static void
684 show_discovery_state(const struct dict *dict)
685 {
686 static struct message *m_bound, *m_other;
687 struct message **m;
688 const char *state, *ip;
689 enum priority priority;
690 int state_elapsed;
691
692 state = dict_get_string(dict, "discovery.state", NULL);
693 if (!state) {
694 return;
695 }
696 ip = dict_get_string(dict, "discovery.ip", NULL);
697 state_elapsed = dict_get_int(dict, "discovery.state-elapsed", 0);
698
699 if (!strcmp(state, "BOUND")) {
700 m = &m_bound;
701 priority = P_STATUS;
702 } else {
703 m = &m_other;
704 priority = P_PROGRESS;
705 }
706 emit(m, priority, "Discovery %s\n%s",
707 progress(), discovery_state_label(state));
708 if (ip) {
709 emit(m, priority, " %s", ip);
710 }
711 }
712
713 static void
714 human_time(int seconds, char *buf, size_t size)
715 {
716 const char *sign = "";
717 if (seconds < 0) {
718 sign = "-";
719 seconds = seconds == INT_MIN ? INT_MAX : -seconds;
720 }
721
722 if (seconds <= 60) {
723 snprintf(buf, size, "%s%d s", sign, seconds);
724 } else if (seconds <= 60 * 60) {
725 snprintf(buf, size, "%s%d min", sign, seconds / 60);
726 } else if (seconds <= 60 * 60 * 24 * 2) {
727 snprintf(buf, size, "%s%d h", sign, seconds / 60 / 60);
728 } else {
729 snprintf(buf, size, "%s%d days", sign, seconds / 60 / 60 / 24);
730 }
731 }
732
733 static void
734 show_fail_open_state(const struct dict *dict)
735 {
736 static struct message *m;
737 int cur_duration, trigger_duration;
738
739 if (!dict_get_bool(dict, "fail-open.triggered", false)) {
740 return;
741 }
742 trigger_duration = dict_get_int(dict, "fail-open.trigger-duration", 0);
743 cur_duration = dict_get_int(dict, "fail-open.current-duration", 0);
744 if (shown(&m) < 5) {
745 emit(&m, P_WARNING, "Failed open %s\nafter %d secs",
746 progress(), trigger_duration);
747 } else {
748 char buf[16];
749 human_time(cur_duration - trigger_duration, buf, sizeof buf);
750 emit(&m, P_WARNING, "In fail open for\n%s now %s", buf, progress());
751 }
752 }
753
754 static const char *
755 progress(void)
756 {
757 return "..." + (3 - (unsigned int) time_now() % 4);
758 }
759
760 static void
761 show_remote_state(const struct dict *dict)
762 {
763 bool debug_mode = dict_get_bool(dict, "debug", false);
764 const char *state, *is_connected;
765
766 state = dict_get_string(dict, "remote.state", NULL);
767 if (!state) {
768 return;
769 }
770 is_connected = dict_get_string(dict, "remote.is-connected", "false");
771 if (!strcmp(is_connected, "true")) {
772 if (debug_mode) {
773 static struct message *m_connected;
774 char buf[16];
775 human_time(dict_get_int(dict, "remote.last-connection", 0),
776 buf, sizeof buf);
777 emit(&m_connected, P_STATUS,
778 "Connected for\nlast %s %s", buf, progress());
779 }
780
781 if (!strcmp(state, "IDLE")) {
782 static struct message *m_idle;
783 emit(&m_idle, P_PROGRESS, "Sent idle probe");
784 }
785
786 if (debug_mode) {
787 const char *name = dict_get_string(dict, "remote.name", NULL);
788 if (name) {
789 static struct message *m_name;
790 emit(&m_name, P_STATUS, "Connected to\n%s", name);
791 }
792 }
793 } else {
794 int elapsed, backoff;
795 const char *name, *error;
796
797 elapsed = dict_get_int(dict, "remote.state-elapsed", 0);
798 backoff = dict_get_int(dict, "remote.backoff", 0);
799 name = dict_get_string(dict, "remote.name", "unknown");
800 state = dict_get_string(dict, "remote.state", "VOID");
801 error = dict_get_string(dict, "remote.last-connect-error", NULL);
802 if (!strcmp(state, "VOID")) {
803 static struct message *m;
804 emit(&m, P_PROGRESS, "Controller not\nfound");
805 } else if (!strcmp(state, "BACKOFF")) {
806 static struct message *m[3];
807 char buf[16];
808
809 if (error) {
810 emit(&m[0], P_PROGRESS, "Connect failed:\n%s", error);
811 }
812 emit(&m[2], P_STATUS, "Last connected\n%s ago", buf);
813 emit(&m[1], P_PROGRESS,
814 "Disconnected\nReconnect in %d", backoff - elapsed);
815 human_time(dict_get_int(dict, "remote.last-connection", 0),
816 buf, sizeof buf);
817 } else if (!strcmp(state, "CONNECTING")) {
818 static struct message *m;
819 emit(&m, P_PROGRESS, "Connecting %s\n%s", progress(), name);
820 }
821 }
822 }
823
824 static void
825 fetch_status(struct rconn *rconn, struct dict *dict, long long timeout)
826 {
827 static struct rconn_packet_counter *counter;
828 static uint32_t xid;
829 struct nicira_header *rq;
830 struct ofpbuf *b;
831 int retval;
832
833 if (!counter) {
834 counter = rconn_packet_counter_create();
835 }
836 if (!xid) {
837 xid = random_uint32();
838 }
839
840 rq = make_openflow_xid(sizeof *rq, OFPT_VENDOR, ++xid, &b);
841 rq->vendor = htonl(NX_VENDOR_ID);
842 rq->subtype = htonl(NXT_STATUS_REQUEST);
843 retval = rconn_send_with_limit(rconn, b, counter, 10);
844 if (retval) {
845 /* continue into the loop so that we pause for a while */
846 }
847
848 while (time_msec() < timeout) {
849 int i;
850
851 rconn_run(rconn);
852
853 for (i = 0; i < 50; i++) {
854 struct ofpbuf *b;
855 bool got_reply;
856
857 b = rconn_recv(rconn);
858 if (!b) {
859 break;
860 }
861
862 got_reply = parse_reply(b->data, dict, xid);
863 ofpbuf_delete(b);
864 if (got_reply) {
865 return;
866 }
867 }
868
869 rconn_run_wait(rconn);
870 rconn_recv_wait(rconn);
871 poll_timer_wait(timeout - time_msec());
872 poll_block();
873 }
874 }
875
876 static bool
877 parse_reply(void *data, struct dict *dict, uint32_t xid)
878 {
879 struct ofp_header *oh;
880 struct nicira_header *rpy;
881
882 oh = data;
883 if (ntohs(oh->length) < sizeof *rpy) {
884 VLOG_WARN("reply is too short (%"PRIu16")", ntohs(oh->length));
885 return false;
886 }
887 if (oh->xid != xid) {
888 VLOG_WARN("xid 0x%08"PRIx32" != expected 0x%08"PRIx32, oh->xid, xid);
889 return false;
890 }
891 if (oh->type != OFPT_VENDOR) {
892 VLOG_WARN("reply is wrong type %"PRIu8, oh->type);
893 return false;
894 }
895
896 rpy = data;
897 if (rpy->vendor != htonl(NX_VENDOR_ID)) {
898 VLOG_WARN("reply has wrong vendor ID %08"PRIx32, rpy->vendor);
899 return false;
900 }
901 if (rpy->subtype != htonl(NXT_STATUS_REPLY)) {
902 VLOG_WARN("reply has wrong subtype %08"PRIx32, rpy->subtype);
903 return false;
904 }
905
906 dict_parse(dict, (const char *) (rpy + 1),
907 ntohs(oh->length) - sizeof *rpy);
908 return true;
909 }
910
911 static void
912 dict_parse(struct dict *dict, const char *data, size_t nbytes)
913 {
914 char *save_ptr = NULL;
915 char *copy, *name;
916
917 copy = xmemdup0(data, nbytes);
918 for (name = strtok_r(copy, "=", &save_ptr); name;
919 name = strtok_r(NULL, "=", &save_ptr))
920 {
921 char *value = strtok_r(NULL, "\n", &save_ptr);
922 if (!value) {
923 break;
924 }
925 dict_add(dict, name, value);
926 }
927 free(copy);
928 }
929
930 static void
931 dict_init(struct dict *dict)
932 {
933 dict->n = 0;
934 dict->max = 16;
935 dict->pairs = xmalloc(sizeof *dict->pairs * dict->max);
936 }
937
938 static void
939 dict_add(struct dict *dict, const char *name, const char *value)
940 {
941 dict_add_nocopy(dict, xstrdup(name), xstrdup(value));
942 }
943
944 static void
945 dict_add_nocopy(struct dict *dict, char *name, char *value)
946 {
947 struct pair *p;
948
949 if (dict->n >= dict->max) {
950 dict->max *= 2;
951 dict->pairs = xrealloc(dict->pairs, sizeof *dict->pairs * dict->max);
952 }
953 p = &dict->pairs[dict->n++];
954 p->name = name;
955 p->value = value;
956 }
957
958 static void
959 dict_delete(struct dict *dict, const char *name)
960 {
961 size_t idx;
962 while ((idx = dict_find(dict, name)) != SIZE_MAX) {
963 struct pair *pair = &dict->pairs[idx];
964 free(pair->name);
965 free(pair->value);
966 dict->pairs[idx] = dict->pairs[--dict->n];
967 }
968 }
969
970 static void
971 dict_free(struct dict *dict)
972 {
973 if (dict) {
974 size_t i;
975
976 for (i = 0; i < dict->n; i++) {
977 free(dict->pairs[i].name);
978 free(dict->pairs[i].value);
979 }
980 free(dict->pairs);
981 }
982 }
983
984 static void
985 initialize_terminal(void)
986 {
987 initscr();
988 cbreak();
989 noecho();
990 nonl();
991 intrflush(stdscr, FALSE);
992 keypad(stdscr, TRUE);
993 nodelay(stdscr, TRUE);
994 typeahead(-1);
995 scrollok(stdscr, TRUE);
996 }
997
998 static void
999 restore_terminal(void *aux OVS_UNUSED)
1000 {
1001 endwin();
1002 }
1003 \f
1004 struct byte_count {
1005 long long int when;
1006 uint64_t tx_bytes;
1007 };
1008
1009 struct show_rates_data {
1010 struct rconn *rconn;
1011 uint32_t xid;
1012 struct byte_count prev, now;
1013 bool got_reply;
1014 };
1015
1016 static void
1017 parse_port_reply(void *data, struct show_rates_data *rates)
1018 {
1019 struct ofp_header *oh;
1020 struct ofp_stats_reply *rpy;
1021 struct ofp_port_stats *ops;
1022 size_t n_ports;
1023 size_t i;
1024
1025 oh = data;
1026 if (ntohs(oh->length) < sizeof *rpy) {
1027 VLOG_WARN("reply is too short (%"PRIu16")", ntohs(oh->length));
1028 return;
1029 }
1030 if (oh->xid != rates->xid) {
1031 VLOG_WARN("xid 0x%08"PRIx32" != expected 0x%08"PRIx32,
1032 oh->xid, rates->xid);
1033 return;
1034 }
1035 if (oh->type != OFPT_STATS_REPLY) {
1036 VLOG_WARN("reply is wrong type %"PRIu8, oh->type);
1037 return;
1038 }
1039
1040 rpy = data;
1041 if (rpy->type != htons(OFPST_PORT)) {
1042 VLOG_WARN("reply has wrong stat type ID %08"PRIx16, rpy->type);
1043 return;
1044 }
1045
1046 n_ports = ((ntohs(oh->length) - offsetof(struct ofp_stats_reply, body))
1047 / sizeof *ops);
1048 ops = (struct ofp_port_stats *) rpy->body;
1049 rates->prev = rates->now;
1050 rates->now.when = time_msec();
1051 rates->now.tx_bytes = UINT64_MAX;
1052 for (i = 0; i < n_ports; i++, ops++) {
1053 if (ops->tx_bytes != htonll(UINT64_MAX)) {
1054 if (rates->now.tx_bytes == UINT64_MAX) {
1055 rates->now.tx_bytes = 0;
1056 }
1057 rates->now.tx_bytes += ntohll(ops->tx_bytes);
1058 }
1059 }
1060 rates->got_reply = true;
1061 }
1062
1063 static void
1064 dump_graph(const bool graph[80])
1065 {
1066 signed char icons[32];
1067 int n_icons = 3;
1068 int i;
1069
1070 memset(icons, -1, sizeof icons);
1071 for (i = 0; i < 16; i++) {
1072 uint8_t row;
1073 int j;
1074
1075 row = 0;
1076 for (j = 0; j < 5; j++) {
1077 row = (row << 1) | graph[i * 5 + j];
1078 }
1079 if (!row) {
1080 addch(' ');
1081 continue;
1082 }
1083
1084 if (icons[row] < 0) {
1085 if (n_icons >= 8) {
1086 addch('X');
1087 continue;
1088 }
1089 set_repeated_icon(n_icons, row);
1090 icons[row] = n_icons++;
1091 }
1092 put_icon(icons[row], row == 0x1f ? '#' : ' ');
1093 }
1094 }
1095
1096 static void
1097 do_show_data_rates(void *rates_)
1098 {
1099 struct show_rates_data *rates = rates_;
1100 static struct rconn_packet_counter *counter;
1101 bool graph[80];
1102
1103 if (!counter) {
1104 counter = rconn_packet_counter_create();
1105 }
1106 if (!rates->xid) {
1107 struct ofp_stats_request *rq;
1108 struct ofpbuf *b;
1109
1110 rates->xid = random_uint32();
1111 rq = make_openflow_xid(sizeof *rq, OFPT_STATS_REQUEST,
1112 rates->xid, &b);
1113 rq->type = htons(OFPST_PORT);
1114 rq->flags = htons(0);
1115 rconn_send_with_limit(rates->rconn, b, counter, 10);
1116 }
1117
1118 if (!rates->got_reply) {
1119 int i;
1120
1121 rconn_run(rates->rconn);
1122 for (i = 0; i < 50; i++) {
1123 struct ofpbuf *b;
1124
1125 b = rconn_recv(rates->rconn);
1126 if (!b) {
1127 break;
1128 }
1129
1130 parse_port_reply(b->data, rates);
1131 ofpbuf_delete(b);
1132 if (rates->got_reply) {
1133 break;
1134 }
1135 }
1136 }
1137
1138 set_icon(0,
1139 e_____,
1140 e_____,
1141 e_____,
1142 e__X__,
1143 e__X__,
1144 e__X_X,
1145 e__XX_,
1146 e__X_X);
1147 set_icon(1,
1148 e_____,
1149 e_____,
1150 e_____,
1151 eX___X,
1152 eXX_XX,
1153 eX_X_X,
1154 eX___X,
1155 eX___X);
1156 set_icon(2,
1157 e_____,
1158 e_____,
1159 e_____,
1160 e_XXX_,
1161 eX____,
1162 eX_XXX,
1163 eX___X,
1164 e_XXX_);
1165
1166 memset(graph, 0, sizeof graph);
1167 graph[24] = 1;
1168 graph[48] = 1;
1169 graph[72] = 1;
1170
1171 addstr("TX: ");
1172 put_icon(0, 'k');
1173 addstr(" ");
1174 put_icon(1, 'M');
1175 addstr(" ");
1176 put_icon(2, 'G');
1177 addch('\n');
1178
1179 if (rates->now.tx_bytes != UINT64_MAX
1180 && rates->prev.tx_bytes != UINT64_MAX
1181 && rates->now.when - rates->prev.when > 500
1182 && time_msec() - rates->now.when < 2000)
1183 {
1184 uint64_t bits = (rates->now.tx_bytes - rates->prev.tx_bytes) * 8;
1185 uint64_t msecs = rates->now.when - rates->prev.when;
1186 double bps = (double) bits * 1000.0 / msecs;
1187 int pixels = bps > 0 ? log(bps) / log(10.0) * 8 + .5 : 0;
1188 if (pixels < 0) {
1189 pixels = 0;
1190 } else if (pixels > 80) {
1191 pixels = 80;
1192 }
1193 memset(graph, 1, pixels);
1194 }
1195
1196 dump_graph(graph);
1197
1198 if (!rates->got_reply) {
1199 rconn_run_wait(rates->rconn);
1200 rconn_recv_wait(rates->rconn);
1201 }
1202 }
1203
1204 static void
1205 show_data_rates(struct rconn *rconn, const struct dict *dict)
1206 {
1207 static struct message *m;
1208 static struct show_rates_data rates;
1209 const char *is_connected, *local_ip;
1210 static bool inited = false;
1211
1212 dict_lookup(dict, "local.is-connected", &is_connected);
1213 dict_lookup(dict, "remote.local-ip", &local_ip);
1214 if (!is_connected && !local_ip) {
1215 /* If we're not connected to the datapath and don't have a local IP,
1216 * then we won't have anything useful to show anyhow. */
1217 return;
1218 }
1219
1220 rates.rconn = rconn;
1221 rates.xid = 0;
1222 rates.got_reply = false;
1223 if (!inited) {
1224 rates.now.tx_bytes = UINT64_MAX;
1225 rates.prev.tx_bytes = UINT64_MAX;
1226 inited = true;
1227 }
1228 emit_function(&m, P_STATUS, do_show_data_rates, &rates);
1229 }
1230 \f
1231 struct message {
1232 /* Content. */
1233 void (*function)(void *aux);
1234 void *aux;
1235 char string[128];
1236
1237 size_t index;
1238 enum priority priority;
1239 int age;
1240 int shown;
1241 };
1242
1243 static struct message **messages;
1244 static size_t n_messages, allocated_messages;
1245
1246 static struct message *
1247 allocate_message(struct message **msgp)
1248 {
1249 if (!*msgp) {
1250 /* Allocate and initialize message. */
1251 *msgp = xzalloc(sizeof **msgp);
1252 (*msgp)->index = n_messages;
1253
1254 /* Add to list of messages. */
1255 if (n_messages >= allocated_messages) {
1256 allocated_messages = 2 * allocated_messages + 1;
1257 messages = xrealloc(messages,
1258 sizeof *messages * allocated_messages);
1259 }
1260 messages[n_messages++] = *msgp;
1261 }
1262 return *msgp;
1263 }
1264
1265 static void
1266 emit(struct message **msgp, enum priority priority, const char *format, ...)
1267 {
1268 struct message *msg = allocate_message(msgp);
1269 va_list args;
1270 size_t length;
1271
1272 msg->priority = priority;
1273
1274 va_start(args, format);
1275 length = strlen(msg->string);
1276 vsnprintf(msg->string + length, sizeof msg->string - length, format, args);
1277 va_end(args);
1278 }
1279
1280 static void
1281 emit_function(struct message **msgp, enum priority priority,
1282 void (*function)(void *aux), void *aux)
1283 {
1284 struct message *msg = allocate_message(msgp);
1285 msg->priority = priority;
1286 msg->function = function;
1287 msg->aux = aux;
1288 }
1289
1290 static int
1291 shown(struct message **msgp)
1292 {
1293 struct message *msg = allocate_message(msgp);
1294 return msg->shown;
1295 }
1296
1297 static void
1298 clear_messages(void)
1299 {
1300 size_t i;
1301
1302 for (i = 0; i < n_messages; i++) {
1303 struct message *msg = messages[i];
1304 msg->string[0] = '\0';
1305 msg->function = NULL;
1306 }
1307 }
1308
1309 static struct message *
1310 best_message(void)
1311 {
1312 struct message *best_msg;
1313 int best_score;
1314 size_t i;
1315
1316 best_score = INT_MIN;
1317 best_msg = NULL;
1318 for (i = 0; i < n_messages; i++) {
1319 struct message *msg = messages[i];
1320 int score;
1321
1322 if (empty_message(msg)) {
1323 continue;
1324 }
1325
1326 score = msg->priority;
1327 if (!msg->shown) {
1328 score += msg->age;
1329 } else {
1330 score -= msg->shown;
1331 }
1332 if (score > best_score) {
1333 best_score = score;
1334 best_msg = msg;
1335 }
1336 }
1337 return best_msg;
1338 }
1339
1340 static void
1341 message_shown(struct message *msg)
1342 {
1343 if (msg && msg->shown++ > 3600) {
1344 msg->shown = 0;
1345 }
1346 }
1347
1348 static bool
1349 empty_message(const struct message *msg)
1350 {
1351 return !msg || (!msg->string[0] && !msg->function);
1352 }
1353
1354 static struct message *get_message(size_t index)
1355 {
1356 assert(index <= n_messages || index == SIZE_MAX);
1357 return (index < n_messages ? messages[index]
1358 : index == SIZE_MAX ? messages[n_messages - 1]
1359 : messages[0]);
1360 }
1361
1362 static struct message *
1363 next_message(struct message *msg)
1364 {
1365 struct message *p;
1366
1367 for (p = get_message(msg->index + 1); p != msg;
1368 p = get_message(p->index + 1)) {
1369 if (!empty_message(p)) {
1370 break;
1371 }
1372 }
1373 return p;
1374 }
1375
1376 static struct message *
1377 prev_message(struct message *msg)
1378 {
1379 struct message *p;
1380
1381 for (p = get_message(msg->index - 1); p != msg;
1382 p = get_message(p->index - 1)) {
1383 if (!empty_message(p)) {
1384 break;
1385 }
1386 }
1387 return p;
1388 }
1389
1390 static void
1391 put_message(const struct message *m)
1392 {
1393 if (m->string[0]) {
1394 addstr(m->string);
1395 } else if (m->function) {
1396 m->function(m->aux);
1397 }
1398 }
1399
1400 static void
1401 age_messages(void)
1402 {
1403 size_t i;
1404 int load;
1405
1406 load = 0;
1407 for (i = 0; i < n_messages; i++) {
1408 struct message *msg = messages[i];
1409 if (!empty_message(msg)) {
1410 load++;
1411 }
1412 }
1413
1414 for (i = 0; i < n_messages; i++) {
1415 struct message *msg = messages[i];
1416 if (empty_message(msg)) {
1417 msg->age = msg->shown = 0;
1418 } else {
1419 if (msg->age && msg->age % 60 == 0) {
1420 msg->shown -= MAX(0, 5 - (load + 6) / 12);
1421 if (msg->shown < 0) {
1422 msg->shown = 0;
1423 }
1424 }
1425 if (msg->age++ > 3600) {
1426 msg->age = 0;
1427 }
1428 }
1429 }
1430 }
1431 \f
1432 /* Set by SIGUSR1 handler. */
1433 static volatile sig_atomic_t sigusr1_triggered;
1434
1435 /* The time after which we stop indicating that the switch is rebooting.
1436 * (This is just in case the reboot fails.) */
1437 static time_t reboot_deadline = TIME_MIN;
1438
1439 static void sigusr1_handler(int);
1440
1441 static void
1442 init_reboot_notifier(void)
1443 {
1444 signal(SIGUSR1, sigusr1_handler);
1445 }
1446
1447 static void
1448 sigusr1_handler(int signr OVS_UNUSED)
1449 {
1450 sigusr1_triggered = true;
1451 }
1452
1453 static bool
1454 show_reboot_state(void)
1455 {
1456 if (sigusr1_triggered) {
1457 reboot_deadline = time_now() + 30;
1458 sigusr1_triggered = false;
1459 }
1460 if (time_now() < reboot_deadline) {
1461 static struct message *msg;
1462 emit(&msg, P_FATAL, "Rebooting");
1463 return true;
1464 }
1465 return false;
1466 }
1467 \f
1468 struct menu_item {
1469 char *text;
1470 void (*f)(const struct dict *);
1471 int id;
1472 bool enabled;
1473 int toggle;
1474 };
1475
1476 struct menu {
1477 struct menu_item **items;
1478 size_t n_items, allocated_items;
1479 };
1480
1481 static void menu_init(struct menu *);
1482 static void menu_free(struct menu *);
1483 static struct menu_item *menu_add_item(struct menu *, const char *text, ...)
1484 PRINTF_FORMAT(2, 3);
1485 static int menu_show(const struct menu *, int start, bool select);
1486
1487 static void cmd_shell(const struct dict *);
1488 static void cmd_show_version(const struct dict *);
1489 static void cmd_configure(const struct dict *);
1490 static void cmd_set_up_pki(const struct dict *);
1491 static void cmd_browse_status(const struct dict *);
1492 static void cmd_show_motto(const struct dict *);
1493
1494 static void
1495 menu_init(struct menu *menu)
1496 {
1497 memset(menu, 0, sizeof *menu);
1498 }
1499
1500 static void
1501 menu_free(struct menu *menu)
1502 {
1503 size_t i;
1504
1505 for (i = 0; i < menu->n_items; i++) {
1506 struct menu_item *item = menu->items[i];
1507 free(item->text);
1508 free(item);
1509 }
1510 free(menu->items);
1511 }
1512
1513 static struct menu_item *
1514 menu_add_item(struct menu *menu, const char *text, ...)
1515 {
1516 struct menu_item *item;
1517 va_list args;
1518
1519 if (menu->n_items >= menu->allocated_items) {
1520 menu->allocated_items = 2 * menu->allocated_items + 1;
1521 menu->items = xrealloc(menu->items,
1522 sizeof *menu->items * menu->allocated_items);
1523 }
1524 item = menu->items[menu->n_items++] = xmalloc(sizeof *item);
1525 va_start(args, text);
1526 item->text = xvasprintf(text, args);
1527 va_end(args);
1528 item->f = NULL;
1529 item->id = -1;
1530 item->enabled = true;
1531 item->toggle = -1;
1532 return item;
1533 }
1534
1535 static void
1536 menu(const struct dict *dict)
1537 {
1538 bool debug_mode = dict_get_bool(dict, "debug", false);
1539 struct menu menu;
1540 int choice;
1541
1542 menu_init(&menu);
1543 menu_add_item(&menu, "Exit");
1544 menu_add_item(&menu, "Show Version")->f = cmd_show_version;
1545 menu_add_item(&menu, "Configure")->f = cmd_configure;
1546 menu_add_item(&menu, "Set up PKI")->f = cmd_set_up_pki;
1547 if (debug_mode) {
1548 menu_add_item(&menu, "Browse Status")->f = cmd_browse_status;
1549 menu_add_item(&menu, "Shell")->f = cmd_shell;
1550 menu_add_item(&menu, "Show Motto")->f = cmd_show_motto;
1551 }
1552
1553 choice = menu_show(&menu, 0, true);
1554 if (choice >= 0) {
1555 void (*f)(const struct dict *) = menu.items[choice]->f;
1556 if (f) {
1557 (f)(dict);
1558 }
1559 }
1560
1561 menu_free(&menu);
1562 }
1563
1564 static int
1565 menu_show(const struct menu *menu, int start, bool select)
1566 {
1567 long long int adjust = LLONG_MAX;
1568 int min = 0, max = MAX(menu->n_items - 2, 0);
1569 int pos, selection;
1570 set_icon(0,
1571 eXX___,
1572 eXXX__,
1573 eXXXX_,
1574 eXXXXX,
1575 eXXXX_,
1576 eXXX__,
1577 eXX___,
1578 e_____);
1579 set_icon(1,
1580 eXXXXX,
1581 eX___X,
1582 eX___X,
1583 eX___X,
1584 eX___X,
1585 eX___X,
1586 eXXXXX,
1587 e_____);
1588 set_icon(2,
1589 eXXXXX,
1590 eX___X,
1591 eXX_XX,
1592 eX_X_X,
1593 eXX_XX,
1594 eX___X,
1595 eXXXXX,
1596 e_____);
1597 if (menu->n_items) {
1598 pos = MIN(menu->n_items - 1, MAX(0, start));
1599 selection = pos;
1600 } else {
1601 pos = 0;
1602 selection = -1;
1603 }
1604 for (;;) {
1605 int key;
1606
1607 while ((key = getch()) != ERR) {
1608 switch (key) {
1609 case KEY_UP:
1610 if (select && selection > 0) {
1611 selection--;
1612 if (selection >= pos) {
1613 break;
1614 }
1615 }
1616 if (pos >= min) {
1617 pos--;
1618 }
1619 break;
1620
1621 case KEY_DOWN:
1622 if (select && selection < menu->n_items - 1) {
1623 selection++;
1624 if (selection <= pos + 1) {
1625 break;
1626 }
1627 }
1628 if (pos <= max) {
1629 pos++;
1630 }
1631 break;
1632
1633 case '\r': case '\n':
1634 if (select && selection >= 0 && selection < menu->n_items) {
1635 struct menu_item *item = menu->items[selection];
1636 if (!item->enabled) {
1637 show_string("Item disabled");
1638 break;
1639 } else if (item->toggle >= 0) {
1640 item->toggle = !item->toggle;
1641 break;
1642 }
1643 }
1644 return selection;
1645
1646 case '\b': case '\x7f': case '\x1b':
1647 case KEY_BACKSPACE: case KEY_DC:
1648 return -1;
1649 }
1650 adjust = time_msec() + 1000;
1651 }
1652 if (time_msec() >= adjust && menu->n_items > 1) {
1653 if (pos < min) {
1654 pos = min;
1655 } else if (pos > max) {
1656 pos = max;
1657 }
1658 }
1659
1660 erase();
1661 curs_set(0);
1662 move(0, 0);
1663 if (!menu->n_items) {
1664 addstr("[Empty]");
1665 } else {
1666 int idx;
1667 for (idx = pos; idx < pos + 2; idx++) {
1668 size_t width = 40;
1669
1670 if (select) {
1671 width--;
1672 if (selection == idx) {
1673 put_icon(0, '>');
1674 } else {
1675 addch(' ');
1676 }
1677 }
1678
1679 if (idx < 0) {
1680 addstr("[Top]");
1681 } else if (idx >= menu->n_items) {
1682 addstr("[Bottom]");
1683 } else {
1684 const struct menu_item *item = menu->items[idx];
1685 size_t length = strlen(item->text);
1686 if (!item->enabled) {
1687 width -= 2;
1688 addch('(');
1689 }
1690 if (item->toggle >= 0) {
1691 if (have_icons()) {
1692 addch(icon_char(item->toggle ? 2 : 1, 0));
1693 width--;
1694 } else {
1695 addstr(item->toggle ? "[X]" : "[ ]");
1696 width -= 3;
1697 }
1698 }
1699 addnstr(item->text, MIN(width, length));
1700 if (!item->enabled) {
1701 addch(')');
1702 }
1703 }
1704 if (idx == pos) {
1705 addch('\n');
1706 }
1707 }
1708 }
1709 refresh();
1710
1711 if (pos < min || pos > max) {
1712 poll_timer_wait(adjust - time_msec());
1713 }
1714 poll_fd_wait(STDIN_FILENO, POLLIN);
1715 poll_block();
1716 }
1717 }
1718
1719 static int
1720 menu_show2(const struct menu *menu, int start, bool select)
1721 {
1722 int pos;
1723 if (menu->n_items) {
1724 pos = MIN(menu->n_items - 1, MAX(0, start));
1725 } else {
1726 pos = -1;
1727 }
1728 set_icon(0,
1729 e__X__,
1730 e_XXX_,
1731 eXXXXX,
1732 e__X__,
1733 e__X__,
1734 e__X__,
1735 e__X__,
1736 e__X__);
1737 set_icon(1,
1738 e__X__,
1739 e__X__,
1740 e__X__,
1741 e__X__,
1742 e__X__,
1743 eXXXXX,
1744 e_XXX_,
1745 e__X__);
1746 for (;;) {
1747 int key;
1748
1749 while ((key = getch()) != ERR) {
1750 switch (key) {
1751 case KEY_UP:
1752 if (pos > 0) {
1753 pos--;
1754 }
1755 break;
1756
1757 case KEY_DOWN:
1758 if (menu->n_items > 0 && pos < menu->n_items - 1) {
1759 pos++;
1760 }
1761 break;
1762
1763 case '\r': case '\n':
1764 if (select && !menu->items[pos]->enabled) {
1765 show_string("Item disabled");
1766 break;
1767 }
1768 return pos;
1769
1770 case '\b': case '\x7f': case '\x1b':
1771 case KEY_BACKSPACE: case KEY_DC:
1772 return -1;
1773 }
1774 }
1775
1776 erase();
1777 curs_set(0);
1778 move(0, 0);
1779 if (pos == -1) {
1780 addstr("[Empty]");
1781 } else {
1782 const struct menu_item *item = menu->items[pos];
1783 const char *line1 = item->text;
1784 size_t len1 = strcspn(line1, "\n");
1785 const char *line2 = line1[len1] ? &line1[len1 + 1] : "";
1786 size_t len2 = strcspn(line2, "\n");
1787 size_t width = 39 - 2 * !item->enabled;
1788
1789 /* First line. */
1790 addch(pos > 0 ? icon_char(0, '^') : ' ');
1791 if (!item->enabled && len1) {
1792 addch('(');
1793 }
1794 addnstr(line1, MIN(len1, width));
1795 if (!item->enabled && len1) {
1796 addch(')');
1797 }
1798 addch('\n');
1799
1800 /* Second line. */
1801 addch(pos < menu->n_items - 1 ? icon_char(1, 'V') : ' ');
1802 if (!item->enabled && len2) {
1803 addch('(');
1804 }
1805 addnstr(line2, MIN(len2, width));
1806 if (!item->enabled && len2) {
1807 addch(')');
1808 }
1809 }
1810 refresh();
1811
1812 poll_fd_wait(STDIN_FILENO, POLLIN);
1813 poll_block();
1814 }
1815 }
1816
1817 static bool
1818 yesno(const char *title, bool def)
1819 {
1820 bool answer = def;
1821
1822 set_icon(0,
1823 eXX___,
1824 eXXX__,
1825 eXXXX_,
1826 eXXXXX,
1827 eXXXX_,
1828 eXXX__,
1829 eXX___,
1830 e_____);
1831
1832 for (;;) {
1833 int key;
1834
1835 while ((key = getch()) != ERR) {
1836 switch (key) {
1837 case KEY_UP:
1838 case KEY_DOWN:
1839 case KEY_LEFT:
1840 case KEY_RIGHT:
1841 answer = !answer;
1842 break;
1843
1844 case 'y': case 'Y':
1845 answer = true;
1846 break;
1847
1848 case 'n': case 'N':
1849 answer = false;
1850 break;
1851
1852 case '\r': case '\n':
1853 return answer;
1854 }
1855 }
1856
1857 erase();
1858 curs_set(0);
1859 move(0, 0);
1860 addstr(title);
1861
1862 move(0, 12);
1863 addch(answer ? icon_char(0, '>') : ' ');
1864 addstr("Yes");
1865
1866 move(1, 12);
1867 addch(!answer ? icon_char(0, '>') : ' ');
1868 addstr("No");
1869
1870 refresh();
1871
1872 poll_fd_wait(STDIN_FILENO, POLLIN);
1873 poll_block();
1874 }
1875 }
1876
1877 static void
1878 cmd_show_version(const struct dict *dict OVS_UNUSED)
1879 {
1880 show_string(VERSION BUILDNR);
1881 }
1882
1883 static void
1884 cmd_browse_status(const struct dict *dict)
1885 {
1886 struct menu menu;
1887 size_t i;
1888
1889 menu_init(&menu);
1890 for (i = 0; i < dict->n; i++) {
1891 const struct pair *p = &dict->pairs[i];
1892 menu_add_item(&menu, "%s = %s", p->name, p->value);
1893 }
1894 menu_show(&menu, 0, false);
1895 menu_free(&menu);
1896 }
1897
1898 static void
1899 cmd_shell(const struct dict *dict OVS_UNUSED)
1900 {
1901 const char *home;
1902
1903 erase();
1904 refresh();
1905 endwin();
1906
1907 printf("Type ^D to exit\n");
1908 fflush(stdout);
1909
1910 putenv("PS1=#");
1911 putenv("PS2=>");
1912 putenv("PS3=?");
1913 putenv("PS4=+");
1914 home = getenv("HOME");
1915 if (home) {
1916 chdir(home);
1917 }
1918 system("/bin/sh");
1919 initialize_terminal();
1920 }
1921
1922 static void
1923 cmd_show_motto(const struct dict *dict OVS_UNUSED)
1924 {
1925 show_string("\"Just Add Ice\"");
1926 }
1927
1928 static void
1929 show_string(const char *string)
1930 {
1931 VLOG_INFO("%s", string);
1932 erase();
1933 curs_set(0);
1934 move(0, 0);
1935 addstr(string);
1936 refresh();
1937 block_until(time_msec() + 5000);
1938 }
1939
1940 static void
1941 block_until(long long timeout)
1942 {
1943 while (timeout > time_msec()) {
1944 poll_timer_wait(timeout - time_msec());
1945 poll_block();
1946 }
1947 drain_keyboard_buffer();
1948 }
1949
1950 static void
1951 drain_keyboard_buffer(void)
1952 {
1953 while (getch() != ERR) {
1954 continue;
1955 }
1956 }
1957 \f
1958 static int
1959 read_vars(const char *cmd, struct dict *dict)
1960 {
1961 struct ds ds;
1962 FILE *stream;
1963 int status;
1964
1965 stream = popen(cmd, "r");
1966 if (!stream) {
1967 VLOG_ERR("popen(\"%s\") failed: %s", cmd, strerror(errno));
1968 return errno;
1969 }
1970
1971 dict_init(dict);
1972 ds_init(&ds);
1973 while (!ds_get_line(&ds, stream)) {
1974 const char *s = ds_cstr(&ds);
1975 const char *equals = strchr(s, '=');
1976 if (equals) {
1977 dict_add_nocopy(dict,
1978 xmemdup0(s, equals - s), xstrdup(equals + 1));
1979 }
1980 }
1981 status = pclose(stream);
1982 if (status) {
1983 char *msg = process_status_msg(status);
1984 VLOG_ERR("pclose(\"%s\") reported subprocess failure: %s",
1985 cmd, msg);
1986 free(msg);
1987 dict_free(dict);
1988 return ECHILD;
1989 }
1990 return 0;
1991 }
1992
1993 static bool
1994 run_and_report_failure(char **argv, const char *title)
1995 {
1996 int null_fds[3] = {0, 1, 2};
1997 int status;
1998 int retval;
1999 char *s;
2000
2001 s = process_escape_args(argv);
2002 VLOG_INFO("starting subprocess: %s", s);
2003 free(s);
2004
2005 retval = process_run(argv, NULL, 0, null_fds, 3, &status);
2006 if (retval) {
2007 char *s = xasprintf("%s:\n%s", title, strerror(retval));
2008 show_string(s);
2009 free(s);
2010 return false;
2011 } else if (status) {
2012 char *msg = process_status_msg(status);
2013 char *s = xasprintf("%s:\n%s", title, msg);
2014 show_string(s);
2015 free(msg);
2016 free(s);
2017 return false;
2018 } else {
2019 VLOG_INFO("subprocess exited with status 0");
2020 return true;
2021 }
2022 }
2023
2024 static int
2025 do_load_config(const char *file_name, struct dict *dict)
2026 {
2027 struct dict auto_vars;
2028 int retval;
2029 char *cmd;
2030 size_t i;
2031
2032 /* Get the list of the variables that the shell sets automatically. */
2033 retval = read_vars("set -a && env", &auto_vars);
2034 if (retval) {
2035 return retval;
2036 }
2037
2038 /* Get the variables from 'file_name'. */
2039 cmd = xasprintf("set -a && . '%s' && env", file_name);
2040 retval = read_vars(cmd, dict);
2041 free(cmd);
2042 if (retval) {
2043 dict_free(&auto_vars);
2044 return retval;
2045 }
2046
2047 /* Subtract. */
2048 for (i = 0; i < auto_vars.n; i++) {
2049 dict_delete(dict, auto_vars.pairs[i].name);
2050 }
2051 dict_free(&auto_vars);
2052 return 0;
2053 }
2054
2055 static bool
2056 load_config(struct dict *dict)
2057 {
2058 static const char default_file[] = "/etc/default/openflow-switch";
2059 int retval = do_load_config(default_file, dict);
2060 if (!retval) {
2061 return true;
2062 } else {
2063 char *s = xasprintf("Cfg load failed:\n%s", strerror(retval));
2064 show_string(s);
2065 free(s);
2066 return false;
2067 }
2068 }
2069
2070 static bool
2071 save_config(const struct svec *settings)
2072 {
2073 struct svec argv;
2074 size_t i;
2075 bool ok;
2076
2077 VLOG_INFO("Saving configuration:");
2078 for (i = 0; i < settings->n; i++) {
2079 VLOG_INFO("%s", settings->names[i]);
2080 }
2081
2082 svec_init(&argv);
2083 svec_add(&argv, "/usr/share/openvswitch-switchui/reconfigure");
2084 svec_append(&argv, settings);
2085 svec_terminate(&argv);
2086 ok = run_and_report_failure(argv.names, "Save failed");
2087 if (ok) {
2088 long long int timeout = time_msec() + 5000;
2089
2090 erase();
2091 curs_set(0);
2092 move(0, 0);
2093 addstr("Saved.\nRestarting...");
2094 refresh();
2095
2096 svec_clear(&argv);
2097 svec_add(&argv, "/bin/sh");
2098 svec_add(&argv, "-c");
2099 svec_add(&argv,
2100 "/etc/init.d/openflow-switch restart >/dev/null 2>&1");
2101 svec_terminate(&argv);
2102
2103 ok = run_and_report_failure(argv.names, "Restart failed");
2104 if (ok) {
2105 block_until(timeout);
2106 }
2107 }
2108 svec_destroy(&argv);
2109
2110 if (ok) {
2111 VLOG_INFO("Save completed successfully");
2112 } else {
2113 VLOG_WARN("Save failed");
2114 }
2115 return ok;
2116 }
2117
2118 static int
2119 match(pcre *re, const char *string, int length)
2120 {
2121 int ovec[999];
2122 int retval;
2123
2124 retval = pcre_exec(re, NULL, string, length, 0, PCRE_PARTIAL,
2125 ovec, ARRAY_SIZE(ovec));
2126 if (retval >= 0) {
2127 if (ovec[0] >= 0 && ovec[1] >= length) {
2128 /* 're' matched all of 'string'. */
2129 return 0;
2130 } else {
2131 /* 're' matched the initial part of 'string' but not all of it. */
2132 return PCRE_ERROR_NOMATCH;
2133 }
2134 } else {
2135 return retval;
2136 }
2137 }
2138
2139 static void
2140 figure_choices(pcre *re, struct ds *s, int pos, struct ds *choices)
2141 {
2142 struct ds tmp;
2143 int retval;
2144 char c;
2145
2146 ds_clear(choices);
2147
2148 /* See whether the current string is a complete match. */
2149 if (!match(re, s->string, pos)) {
2150 ds_put_char(choices, '\n');
2151 }
2152
2153 /* Then try all the other possibilities. */
2154 ds_init(&tmp);
2155 ds_put_buffer(&tmp, s->string, pos);
2156 for (c = 0x20; c < 0x7f; c++) {
2157 ds_put_char(&tmp, c);
2158 retval = match(re, tmp.string, pos + 1);
2159 if (retval == PCRE_ERROR_PARTIAL || !retval) {
2160 ds_put_char(choices, c);
2161 }
2162 tmp.length--;
2163 }
2164 ds_destroy(&tmp);
2165
2166 if (!choices->length) {
2167 ds_put_char(choices, '\n');
2168 }
2169 }
2170
2171 static void
2172 figure_completion(pcre *re, struct ds *s)
2173 {
2174 for (;;) {
2175 int found = -1;
2176 int c;
2177
2178 /* See whether the current string is a complete match. */
2179 if (!match(re, s->string, s->length)) {
2180 return;
2181 }
2182 for (c = 0x20; c < 0x7f; c++) {
2183 int retval;
2184
2185 ds_put_char(s, c);
2186 retval = match(re, s->string, s->length);
2187 s->length--;
2188
2189 if (retval == PCRE_ERROR_PARTIAL || !retval) {
2190 if (found != -1) {
2191 return;
2192 }
2193 found = c;
2194 }
2195 }
2196 if (found == -1) {
2197 return;
2198 }
2199 ds_put_char(s, found);
2200 }
2201 }
2202
2203 #define OCTET_RE "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"
2204 #define IP_RE "("OCTET_RE"\\."OCTET_RE"\\."OCTET_RE"\\."OCTET_RE")"
2205 #define PORT_RE \
2206 "([0-9]|" \
2207 "[1-9][0-9]|" \
2208 "[1-9][0-9][0-9]|" \
2209 "[1-9][0-9][0-9][0-9]|" \
2210 "[1-5][0-9][0-9][0-9][0-9]|" \
2211 "6[1-4][0-9][0-9][0-9]|" \
2212 "65[1-4][0-9][0-9]|" \
2213 "655[1-2][0-9]|" \
2214 "6553[1-5])"
2215 #define XOCTET_RE "[0-9A-F][0-9A-F]"
2216 #define MAC_RE \
2217 XOCTET_RE":"XOCTET_RE":"XOCTET_RE":"\
2218 XOCTET_RE":"XOCTET_RE":"XOCTET_RE
2219 #define NUM100_TO_99999_RE \
2220 "([1-9][0-9][0-9]|" \
2221 "[1-9][0-9][0-9][0-9]|" \
2222 "[1-9][0-9][0-9][0-9][0-9])"
2223 #define NUM5_TO_99999_RE \
2224 "([5-9]|" \
2225 "[1-9][0-9]|" \
2226 "[1-9][0-9][0-9]|" \
2227 "[1-9][0-9][0-9][0-9]|" \
2228 "[1-9][0-9][0-9][0-9][0-9])"
2229 #define NUM1_TO_99999_RE \
2230 "([1-9]|" \
2231 "[1-9][0-9]|" \
2232 "[1-9][0-9][0-9]|" \
2233 "[1-9][0-9][0-9][0-9]|" \
2234 "[1-9][0-9][0-9][0-9][0-9])"
2235
2236 static char *
2237 prompt(const char *prompt, const char *initial, const char *pattern)
2238 {
2239 struct ds ds;
2240 int pos, chidx;
2241 struct ds choices;
2242 const char *error;
2243 int erroffset;
2244 pcre *re;
2245 int retval;
2246 int okpartial;
2247 char *p;
2248
2249 set_icon(0,
2250 e____X,
2251 e____X,
2252 e__X_X,
2253 e_X__X,
2254 eXXXXX,
2255 e_X___,
2256 e__X__,
2257 e_____);
2258
2259 re = pcre_compile(pattern, PCRE_ANCHORED, &error, &erroffset, NULL);
2260 if (!re) {
2261 VLOG_ERR("PCRE error for pattern \"%s\" at offset %d: %s",
2262 pattern, erroffset, error);
2263 return xstrdup(initial);
2264 }
2265
2266 retval = pcre_fullinfo(re, NULL, PCRE_INFO_OKPARTIAL, &okpartial);
2267 assert(!retval);
2268 assert(okpartial);
2269
2270 pos = 0;
2271 ds_init(&ds);
2272 ds_put_cstr(&ds, initial);
2273 ds_init(&choices);
2274 figure_choices(re, &ds, pos, &choices);
2275 p = memchr(choices.string, initial[0], choices.length);
2276 chidx = p ? p - choices.string : 0;
2277 for (;;) {
2278 int c, key;
2279
2280 while ((key = getch()) != ERR) {
2281 switch (key) {
2282 case KEY_UP:
2283 if (choices.length > 1) {
2284 if (++chidx >= choices.length) {
2285 chidx = 0;
2286 }
2287 ds.string[pos] = choices.string[chidx];
2288 ds_truncate(&ds, pos + 1);
2289 figure_completion(re, &ds);
2290 }
2291 break;
2292
2293 case KEY_DOWN:
2294 if (choices.length > 1) {
2295 if (--chidx < 0) {
2296 chidx = choices.length - 1;
2297 }
2298 ds.string[pos] = choices.string[chidx];
2299 ds_truncate(&ds, pos + 1);
2300 figure_completion(re, &ds);
2301 }
2302 break;
2303
2304 case '\r': case '\n':
2305 if (choices.string[chidx] == '\n') {
2306 ds_truncate(&ds, pos);
2307 return ds_cstr(&ds);
2308 } else {
2309 if (pos >= ds.length) {
2310 pos++;
2311 ds_put_char(&ds, choices.string[chidx]);
2312 figure_choices(re, &ds, pos, &choices);
2313 chidx = 0;
2314 figure_completion(re, &ds);
2315 } else {
2316 pos = ds.length;
2317 figure_choices(re, &ds, pos, &choices);
2318 chidx = 0;
2319 figure_completion(re, &ds);
2320 }
2321 }
2322 break;
2323
2324 case '\f':
2325 ds_truncate(&ds, pos + 1);
2326 figure_choices(re, &ds, pos, &choices);
2327 chidx = 0;
2328 break;
2329
2330 case '\b': case '\x7f': case '\x1b':
2331 case KEY_BACKSPACE: case KEY_DC:
2332 if (pos) {
2333 pos--;
2334 } else {
2335 return xstrdup(initial);
2336 }
2337 figure_choices(re, &ds, pos, &choices);
2338 chidx = 0;
2339 if (pos < ds.length) {
2340 p = memchr(choices.string, ds.string[pos],
2341 choices.length);
2342 if (p) {
2343 chidx = p - choices.string;
2344 }
2345 }
2346 break;
2347
2348 default:
2349 if (key >= 0x20 && key < 0x7f) {
2350 /* Check whether 'key' is valid and toggle case if
2351 * necessary. */
2352 if (!memchr(choices.string, key, choices.length)) {
2353 if (memchr(choices.string, toupper(key),
2354 choices.length)) {
2355 key = toupper(key);
2356 } else if (memchr(choices.string, tolower(key),
2357 choices.length)) {
2358 key = tolower(key);
2359 } else {
2360 break;
2361 }
2362 }
2363
2364 /* Insert 'key' and advance the position. */
2365 if (pos >= ds.length) {
2366 ds_put_char(&ds, key);
2367 } else {
2368 ds.string[pos] = key;
2369 }
2370 pos++;
2371
2372 if (choices.string[chidx] != key) {
2373 ds_truncate(&ds, pos);
2374 }
2375 figure_choices(re, &ds, pos, &choices);
2376 chidx = 0;
2377 if (pos < ds.length) {
2378 p = memchr(choices.string, ds.string[pos],
2379 choices.length);
2380 if (p) {
2381 chidx = p - choices.string;
2382 }
2383 }
2384 figure_completion(re, &ds);
2385 }
2386 }
2387 }
2388
2389 erase();
2390 curs_set(1);
2391 move(0, 0);
2392 addnstr(prompt, MIN(40, strlen(prompt)));
2393
2394 c = choices.string[chidx];
2395 move(1, 0);
2396 addstr(ds_cstr(&ds));
2397 move(1, pos);
2398 if (c == '\n') {
2399 put_icon(0, '$');
2400 } else {
2401 addch(c);
2402 }
2403 move(1, pos);
2404 refresh();
2405
2406 poll_fd_wait(STDIN_FILENO, POLLIN);
2407 poll_block();
2408 }
2409 }
2410
2411 static void
2412 prompt_ip(const char *title, uint32_t *ip)
2413 {
2414 char *in = xasprintf(IP_FMT, IP_ARGS(ip));
2415 char *out = prompt(title, in, "^"IP_RE"$");
2416 *ip = inet_addr(out);
2417 free(in);
2418 free(out);
2419 }
2420
2421 static void
2422 abbreviate_netdevs(const struct svec *netdevs, struct ds *abbrev)
2423 {
2424 size_t i;
2425
2426 ds_init(abbrev);
2427 for (i = 0; i < netdevs->n; ) {
2428 size_t i_len = strlen(netdevs->names[i]);
2429 size_t j;
2430
2431 for (j = i + 1; j < netdevs->n; j++) {
2432 size_t j_len = strlen(netdevs->names[j]);
2433 if (!i_len || !j_len || i_len != j_len
2434 || memcmp(netdevs->names[i], netdevs->names[j], i_len - 1)) {
2435 break;
2436 }
2437 }
2438
2439 if (abbrev->length) {
2440 ds_put_char(abbrev, ' ');
2441 }
2442 if (j - i == 1) {
2443 ds_put_cstr(abbrev, netdevs->names[i]);
2444 } else {
2445 size_t k;
2446
2447 ds_put_buffer(abbrev, netdevs->names[i], i_len - 1);
2448 ds_put_char(abbrev, '[');
2449 for (k = i; k < j; k++) {
2450 ds_put_char(abbrev, netdevs->names[k][i_len - 1]);
2451 }
2452 ds_put_char(abbrev, ']');
2453 }
2454 i = j;
2455 }
2456 }
2457
2458 static void
2459 choose_netdevs(struct svec *choices)
2460 {
2461 struct svec netdevs = SVEC_EMPTY_INITIALIZER;
2462 struct menu menu;
2463 size_t i;
2464
2465 netdev_enumerate(&netdevs);
2466 svec_sort(&netdevs);
2467
2468 menu_init(&menu);
2469 menu_add_item(&menu, "Exit");
2470 for (i = 0; i < netdevs.n; i++) {
2471 const char *name = netdevs.names[i];
2472 struct menu_item *item;
2473 struct netdev *netdev;
2474 int retval;
2475
2476 if (!strncmp(name, "wmaster", strlen("wmaster"))
2477 || !strncmp(name, "of", strlen("of"))
2478 || !strcmp(name, "lo")) {
2479 continue;
2480 }
2481
2482 retval = netdev_open_default(name, &netdev);
2483 if (!retval) {
2484 bool exclude = netdev_get_in4(netdev, NULL, NULL) == 0;
2485 netdev_close(netdev);
2486 if (exclude) {
2487 continue;
2488 }
2489 }
2490
2491 item = menu_add_item(&menu, "%s", name);
2492 item->toggle = svec_contains(choices, name);
2493 }
2494 if (menu.n_items > 1) {
2495 menu_show(&menu, 0, true);
2496 } else {
2497 show_string("No available\nbridge ports");
2498 }
2499
2500 svec_clear(choices);
2501 for (i = 0; i < menu.n_items; i++) {
2502 struct menu_item *item = menu.items[i];
2503 if (item->toggle > 0) {
2504 svec_add(choices, item->text);
2505 }
2506 }
2507
2508 menu_free(&menu);
2509 }
2510
2511 static bool
2512 is_datapath_id_in_dmi(void)
2513 {
2514 FILE *dmidecode;
2515 char line[256];
2516 bool is_in_dmi;
2517
2518 dmidecode = popen("dmidecode -s system-uuid", "r");
2519 if (!dmidecode) {
2520 return false;
2521 }
2522 is_in_dmi = fgets(line, sizeof line, dmidecode) && strstr(line, "-002320");
2523 fclose(dmidecode);
2524 return is_in_dmi;
2525 }
2526
2527 struct switch_config {
2528 struct svec netdevs;
2529 enum { DISCOVERY, IN_BAND } mode;
2530 uint32_t switch_ip;
2531 uint32_t switch_mask;
2532 uint32_t switch_gw;
2533 enum { FAIL_DROP, FAIL_SWITCH } disconnected;
2534 bool stp;
2535 int rate_limit;
2536 int inactivity_probe;
2537 int max_backoff;
2538 char *controller_vconn;
2539 char *datapath_id;
2540 };
2541
2542 static const char *
2543 disconnected_string(int value)
2544 {
2545 #define FAIL_SWITCH_STRING "Switch packets"
2546 #define FAIL_DROP_STRING "Drop packets"
2547 return value == FAIL_SWITCH ? FAIL_SWITCH_STRING : FAIL_DROP_STRING;
2548 }
2549
2550 static void
2551 cmd_configure(const struct dict *dict OVS_UNUSED)
2552 {
2553 bool debug_mode = dict_get_bool(dict, "debug", false);
2554 struct dict config_dict;
2555 struct switch_config config;
2556 int start;
2557
2558 if (!load_config(&config_dict)) {
2559 return;
2560 }
2561 svec_init(&config.netdevs);
2562 svec_parse_words(&config.netdevs,
2563 dict_get_string(&config_dict, "NETDEVS", ""));
2564 config.mode = (!strcmp(dict_get_string(&config_dict, "MODE", "discovery"),
2565 "in-band") ? IN_BAND : DISCOVERY);
2566 config.switch_ip = dict_get_ip(&config_dict, "SWITCH_IP");
2567 config.switch_mask = dict_get_ip(&config_dict, "SWITCH_NETMASK");
2568 config.switch_gw = dict_get_ip(&config_dict, "SWITCH_GATEWAY");
2569 config.controller_vconn = xstrdup(dict_get_string(&config_dict,
2570 "CONTROLLER", ""));
2571 config.disconnected = (!strcmp(dict_get_string(&config_dict,
2572 "DISCONNECTED_MODE", ""),
2573 "switch")
2574 ? FAIL_SWITCH : FAIL_DROP);
2575 config.stp = !strcmp(dict_get_string(&config_dict, "stp", ""), "yes");
2576 config.rate_limit = dict_get_int(&config_dict, "RATE_LIMIT", -1);
2577 config.inactivity_probe = dict_get_int(&config_dict, "INACTIVITY_PROBE",
2578 -1);
2579 config.max_backoff = dict_get_int(&config_dict, "MAX_BACKOFF", -1);
2580 if (is_datapath_id_in_dmi()) {
2581 config.datapath_id = xstrdup("DMI");
2582 } else {
2583 const char *dpid = dict_get(&config_dict, "DATAPATH_ID");
2584 if (dpid) {
2585 struct ds ds = DS_EMPTY_INITIALIZER;
2586 const char *cp;
2587 for (cp = dpid; *cp != '\0'; cp++) {
2588 if (*cp != ':') {
2589 ds_put_char(&ds, toupper((unsigned char) *cp));
2590 }
2591 }
2592 config.datapath_id = ds_cstr(&ds);
2593 } else {
2594 config.datapath_id = xstrdup("Random");
2595 }
2596 }
2597 dict_free(&config_dict);
2598
2599 start = 0;
2600 while (start != -1) {
2601 enum {
2602 MENU_EXIT,
2603 MENU_NETDEVS,
2604 MENU_MODE,
2605 MENU_IP,
2606 MENU_NETMASK,
2607 MENU_GATEWAY,
2608 MENU_CONTROLLER,
2609 MENU_DISCONNECTED_MODE,
2610 MENU_DATAPATH_ID,
2611 MENU_STP,
2612 MENU_RATE_LIMIT,
2613 MENU_INACTIVITY_PROBE,
2614 MENU_MAX_BACKOFF,
2615 };
2616
2617 struct ds ports;
2618 struct menu_item *item;
2619 struct menu menu;
2620 char *in, *out;
2621 uint32_t ip;
2622
2623 menu_init(&menu);
2624
2625 /* Exit. */
2626 item = menu_add_item(&menu, "Exit");
2627 item->id = MENU_EXIT;
2628
2629 /* Bridge Ports. */
2630 abbreviate_netdevs(&config.netdevs, &ports);
2631 item = menu_add_item(&menu, "Bridge Ports:\n%s", ds_cstr(&ports));
2632 item->id = MENU_NETDEVS;
2633 ds_destroy(&ports);
2634
2635 /* Mode. */
2636 item = menu_add_item(&menu, "Mode:\n%s",
2637 (config.mode == DISCOVERY
2638 ? "Discovery" : "In-Band"));
2639 item->id = MENU_MODE;
2640
2641 /* IP address. */
2642 if (config.switch_ip == htonl(0)) {
2643 item = menu_add_item(&menu, "Switch IP Addr:\nDHCP");
2644 } else {
2645 item = menu_add_item(&menu, "Switch IP Addr:\n"IP_FMT,
2646 IP_ARGS(&config.switch_ip));
2647 }
2648 item->id = MENU_IP;
2649 item->enabled = config.mode == IN_BAND;
2650
2651 /* Netmask. */
2652 item = menu_add_item(&menu, "Switch Netmask:\n"IP_FMT,
2653 IP_ARGS(&config.switch_mask));
2654 item->id = MENU_NETMASK;
2655 item->enabled = config.mode == IN_BAND && config.switch_ip != htonl(0);
2656
2657 /* Gateway. */
2658 item = menu_add_item(&menu, "Switch Gateway:\n"IP_FMT,
2659 IP_ARGS(&config.switch_gw));
2660 item->id = MENU_GATEWAY;
2661 item->enabled = config.mode == IN_BAND && config.switch_ip != htonl(0);
2662
2663 /* Controller. */
2664 item = menu_add_item(&menu, "Controller:\n%s",
2665 config.controller_vconn);
2666 item->id = MENU_CONTROLLER;
2667 item->enabled = config.mode == IN_BAND;
2668
2669 /* Disconnected mode. */
2670 item = menu_add_item(&menu, "If disconnected:\n%s\n",
2671 disconnected_string(config.disconnected));
2672 item->id = MENU_DISCONNECTED_MODE;
2673
2674 /* Datapath ID. */
2675 item = menu_add_item(&menu, "Datapath ID:\n%s", config.datapath_id);
2676 item->id = MENU_DATAPATH_ID;
2677 item->enabled = strcmp(config.datapath_id, "DMI");
2678
2679 /* Spanning tree protocol. */
2680 if (debug_mode) {
2681 item = menu_add_item(&menu, "802.1D-1998 STP:\n%s",
2682 config.stp ? "Enabled" : "Disabled");
2683 item->id = MENU_STP;
2684 }
2685
2686 /* Rate-limiting. */
2687 if (debug_mode) {
2688 if (config.rate_limit < 0) {
2689 item = menu_add_item(&menu, "Ctlr rate limit:\nDisabled");
2690 } else {
2691 item = menu_add_item(&menu, "Ctlr rate limit:\n%d/s",
2692 config.rate_limit);
2693 }
2694 item->id = MENU_RATE_LIMIT;
2695 }
2696
2697 /* Inactivity probe. */
2698 if (debug_mode) {
2699 if (config.inactivity_probe < 0) {
2700 item = menu_add_item(&menu, "Activity probe:\nDefault");
2701 } else {
2702 item = menu_add_item(&menu, "Activity probe:\n%d s",
2703 config.inactivity_probe);
2704 }
2705 item->id = MENU_INACTIVITY_PROBE;
2706 }
2707
2708 /* Max backoff. */
2709 if (debug_mode) {
2710 if (config.max_backoff < 0) {
2711 item = menu_add_item(&menu, "Max backoff:\nDefault");
2712 } else {
2713 item = menu_add_item(&menu, "Max backoff:\n%d s",
2714 config.max_backoff);
2715 }
2716 item->id = MENU_MAX_BACKOFF;
2717 }
2718
2719 start = menu_show2(&menu, start, true);
2720 menu_free(&menu);
2721
2722 in = out = NULL;
2723 switch (start) {
2724 case MENU_EXIT:
2725 start = -1;
2726 break;
2727
2728 case MENU_NETDEVS:
2729 choose_netdevs(&config.netdevs);
2730 break;
2731
2732 case MENU_MODE:
2733 out = prompt("Mode:",
2734 config.mode == DISCOVERY ? "Discovery" : "In-Band",
2735 "^(Discovery|In-Band)$");
2736 config.mode = !strcmp(out, "Discovery") ? DISCOVERY : IN_BAND;
2737 free(out);
2738 break;
2739
2740 case MENU_IP:
2741 in = (config.switch_ip == htonl(0) ? xstrdup("DHCP")
2742 : xasprintf(IP_FMT, IP_ARGS(&config.switch_ip)));
2743 out = prompt("Switch IP:", in, "^(DHCP|"IP_RE")$");
2744 ip = strcmp(out, "DHCP") ? inet_addr(out) : htonl(0);
2745 free(in);
2746 free(out);
2747 if (ip != config.switch_ip) {
2748 config.switch_ip = ip;
2749 if (ip != htonl(0)) {
2750 uint32_t mask = guess_netmask(ip);
2751 if (mask) {
2752 config.switch_mask = mask;
2753 config.switch_gw = (ip & mask) | htonl(1);
2754 }
2755 }
2756 }
2757 break;
2758
2759 case MENU_NETMASK:
2760 prompt_ip("Switch Netmask:", &config.switch_mask);
2761 break;
2762
2763 case MENU_GATEWAY:
2764 prompt_ip("Switch Gateway:", &config.switch_gw);
2765 break;
2766
2767 case MENU_CONTROLLER:
2768 out = prompt("Controller:", config.controller_vconn,
2769 "^(tcp|ssl):"IP_RE"(:"PORT_RE")?$");
2770 free(config.controller_vconn);
2771 config.controller_vconn = out;
2772 break;
2773
2774 case MENU_DISCONNECTED_MODE:
2775 out = prompt("If disconnected",
2776 disconnected_string(config.disconnected),
2777 "^("FAIL_DROP_STRING"|"FAIL_SWITCH_STRING")$");
2778 config.disconnected = (!strcmp(out, FAIL_DROP_STRING)
2779 ? FAIL_DROP : FAIL_SWITCH);
2780 free(out);
2781 break;
2782
2783 case MENU_DATAPATH_ID:
2784 out = prompt("Datapath ID:", config.datapath_id,
2785 "^Random|"MAC_RE"$");
2786 free(config.datapath_id);
2787 config.datapath_id = out;
2788 break;
2789
2790 case MENU_STP:
2791 out = prompt("802.1D-1998 STP:",
2792 config.stp ? "Enabled" : "Disabled",
2793 "^(Enabled|Disabled)$");
2794 config.stp = !strcmp(out, "Enabled");
2795 free(out);
2796 break;
2797
2798 case MENU_RATE_LIMIT:
2799 in = (config.rate_limit < 0
2800 ? xstrdup("Disabled")
2801 : xasprintf("%d/s", config.rate_limit));
2802 out = prompt("Ctlr rate limit:", in,
2803 "^(Disabled|("NUM100_TO_99999_RE")/s)$");
2804 free(in);
2805 config.rate_limit
2806 = isdigit((unsigned char)out[0]) ? atoi(out) : -1;
2807 free(out);
2808 break;
2809
2810 case MENU_INACTIVITY_PROBE:
2811 in = (config.inactivity_probe < 0
2812 ? xstrdup("Default")
2813 : xasprintf("%d s", config.inactivity_probe));
2814 out = prompt("Activity probe:", in,
2815 "^(Default|("NUM5_TO_99999_RE") s)$");
2816 free(in);
2817 config.inactivity_probe
2818 = isdigit((unsigned char)out[0]) ? atoi(out) : -1;
2819 free(out);
2820 break;
2821
2822 case MENU_MAX_BACKOFF:
2823 in = (config.max_backoff < 0
2824 ? xstrdup("Default")
2825 : xasprintf("%d s", config.max_backoff));
2826 out = prompt("Max backoff:", in,
2827 "^(Default|("NUM1_TO_99999_RE") s)$");
2828 free(in);
2829 config.max_backoff
2830 = isdigit((unsigned char)out[0]) ? atoi(out) : -1;
2831 free(out);
2832 break;
2833 }
2834 }
2835
2836 if (yesno("Save\nChanges?", false)) {
2837 struct svec set;
2838 char *netdevs;
2839
2840 svec_init(&set);
2841 netdevs = svec_join(&config.netdevs, " ", "");
2842 svec_add_nocopy(&set, xasprintf("NETDEVS=%s", netdevs));
2843 free(netdevs);
2844 svec_add(&set,
2845 config.mode == IN_BAND ? "MODE=in-band" : "MODE=discovery");
2846 if (config.mode == IN_BAND) {
2847 if (config.switch_ip == htonl(0)) {
2848 svec_add(&set, "SWITCH_IP=dhcp");
2849 } else {
2850 svec_add_nocopy(&set, xasprintf("SWITCH_IP="IP_FMT,
2851 IP_ARGS(&config.switch_ip)));
2852 svec_add_nocopy(&set,
2853 xasprintf("SWITCH_NETMASK="IP_FMT,
2854 IP_ARGS(&config.switch_mask)));
2855 svec_add_nocopy(&set, xasprintf("SWITCH_GATEWAY="IP_FMT,
2856 IP_ARGS(&config.switch_gw)));
2857 svec_add_nocopy(&set, xasprintf("CONTROLLER=%s",
2858 config.controller_vconn));
2859 }
2860 }
2861 svec_add(&set, (config.disconnected == FAIL_DROP
2862 ? "DISCONNECTED_MODE=drop"
2863 : "DISCONNECTED_MODE=switch"));
2864 svec_add_nocopy(&set, xasprintf("STP=%s", config.stp ? "yes" : "no"));
2865 if (config.rate_limit < 0) {
2866 svec_add(&set, "RATE_LIMIT=");
2867 } else {
2868 svec_add_nocopy(&set,
2869 xasprintf("RATE_LIMIT=%d", config.rate_limit));
2870 }
2871 if (config.inactivity_probe < 0) {
2872 svec_add(&set, "INACTIVITY_PROBE=");
2873 } else {
2874 svec_add_nocopy(&set, xasprintf("INACTIVITY_PROBE=%d",
2875 config.inactivity_probe));
2876 }
2877 if (config.max_backoff < 0) {
2878 svec_add(&set, "MAX_BACKOFF=");
2879 } else {
2880 svec_add_nocopy(&set, xasprintf("MAX_BACKOFF=%d",
2881 config.max_backoff));
2882 }
2883 save_config(&set);
2884 svec_destroy(&set);
2885 }
2886
2887 svec_destroy(&config.netdevs);
2888 free(config.controller_vconn);
2889 free(config.datapath_id);
2890 }
2891
2892 static void
2893 cmd_set_up_pki(const struct dict *dict OVS_UNUSED)
2894 {
2895 static const char def_privkey_file[]
2896 = "/etc/openflow-switch/of0-privkey.pem";
2897 static const char def_cert_file[] = "/etc/openflow-switch/of0-cert.pem";
2898 static const char def_cacert_file[] = "/etc/openflow-switch/cacert.pem";
2899 struct dict config_dict;
2900 const char *privkey_file, *cert_file, *cacert_file;
2901 bool bootstrap;
2902 struct stat s;
2903 struct svec set;
2904 bool has_keys;
2905
2906 if (!load_config(&config_dict)) {
2907 return;
2908 }
2909 privkey_file = dict_get_string(&config_dict, "PRIVKEY", def_privkey_file);
2910 cert_file = dict_get_string(&config_dict, "CERT", def_cert_file);
2911 cacert_file = dict_get_string(&config_dict, "CACERT", def_cacert_file);
2912 bootstrap = !strcmp(dict_get_string(&config_dict, "CACERT_MODE", "secure"),
2913 "bootstrap");
2914
2915 has_keys = !stat(privkey_file, &s) && !stat(cert_file, &s);
2916 if (!has_keys
2917 ? yesno("Generate\nkeys?", true)
2918 : yesno("Generate\nnew keys?", false)) {
2919 struct svec argv;
2920 bool ok;
2921
2922 privkey_file = def_privkey_file;
2923 cert_file = def_cert_file;
2924
2925 svec_init(&argv);
2926 svec_parse_words(&argv, "sh -c 'cd /etc/openflow-switch "
2927 "&& ovs-pki --force req of0"
2928 "&& ovs-pki --force self-sign of0'");
2929 svec_terminate(&argv);
2930 ok = run_and_report_failure(argv.names, "Key gen failed");
2931 svec_destroy(&argv);
2932 if (!ok) {
2933 return;
2934 }
2935 has_keys = true;
2936 }
2937 if (!has_keys) {
2938 return;
2939 }
2940
2941 if (stat(cacert_file, &s) && errno == ENOENT) {
2942 bootstrap = yesno("Bootstrap\nCA cert?", bootstrap);
2943 } else if (yesno("Replace\nCA cert?", false)) {
2944 unlink(cacert_file);
2945 bootstrap = true;
2946 }
2947
2948 svec_init(&set);
2949 svec_add_nocopy(&set, xasprintf("PRIVKEY=%s", privkey_file));
2950 svec_add_nocopy(&set, xasprintf("CERT=%s", cert_file));
2951 svec_add_nocopy(&set, xasprintf("CACERT=%s", cacert_file));
2952 svec_add_nocopy(&set, xasprintf("CACERT_MODE=%s",
2953 bootstrap ? "bootstrap" : "secure"));
2954 save_config(&set);
2955 svec_destroy(&set);
2956 }
2957 \f
2958 static void
2959 parse_options(int argc, char *argv[])
2960 {
2961 enum {
2962 OPT_DUMMY = UCHAR_MAX + 1,
2963 VLOG_OPTION_ENUMS
2964 };
2965 static struct option long_options[] = {
2966 {"verbose", optional_argument, 0, 'v'},
2967 {"help", no_argument, 0, 'h'},
2968 {"version", no_argument, 0, 'V'},
2969 DAEMON_LONG_OPTIONS,
2970 VLOG_LONG_OPTIONS,
2971 {0, 0, 0, 0},
2972 };
2973 char *short_options = long_options_to_short_options(long_options);
2974
2975 for (;;) {
2976 int c;
2977
2978 c = getopt_long(argc, argv, short_options, long_options, NULL);
2979 if (c == -1) {
2980 break;
2981 }
2982
2983 switch (c) {
2984 case 'h':
2985 usage();
2986
2987 case 'V':
2988 OVS_PRINT_VERSION(OFP_VERSION, OFP_VERSION);
2989 exit(EXIT_SUCCESS);
2990
2991 VLOG_OPTION_HANDLERS
2992 DAEMON_OPTION_HANDLERS
2993
2994 case '?':
2995 exit(EXIT_FAILURE);
2996
2997 default:
2998 abort();
2999 }
3000 }
3001 free(short_options);
3002 }
3003
3004 static void
3005 usage(void)
3006 {
3007 printf("%s: OpenFlow switch monitoring user interface\n"
3008 "usage: %s [OPTIONS] SWITCH\n"
3009 "where SWITCH is an active OpenFlow connection method.\n",
3010 program_name, program_name);
3011 vconn_usage(true, false, false);
3012 printf("\nOptions:\n"
3013 " -v, --verbose=MODULE:FACILITY:LEVEL configure logging levels\n"
3014 " -v, --verbose set maximum verbosity level\n"
3015 " -h, --help display this help message\n"
3016 " -V, --version display version information\n");
3017 exit(EXIT_SUCCESS);
3018 }