1 /* Copyright (c) 2008, 2009, 2010 Nicira Networks, Inc.
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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <arpa/inet.h>
32 #include "command-line.h"
34 #include "dynamic-string.h"
36 #include "fatal-signal.h"
39 #include "openflow/nicira-ext.h"
40 #include "openflow/openflow.h"
42 #include "poll-loop.h"
46 #include "socket-util.h"
53 #define THIS_MODULE VLM_switchui
56 static void parse_options(int argc
, char *argv
[]);
57 static void usage(void);
59 static void initialize_terminal(void);
60 static void restore_terminal(void *aux
);
71 static void emit(struct message
**, enum priority
, const char *, ...)
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);
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
);
109 static void addf(const char *format
, ...) PRINTF_FORMAT(1, 2);
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
);
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
*);
123 static void init_reboot_notifier(void);
124 static bool show_reboot_state(void);
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);
131 static const char *progress(void);
134 main(int argc
, char *argv
[])
142 /* Tracking keystroke repeat counts. */
144 long long int last_key_time
= 0;
145 int repeat_count
= 0;
147 proctitle_init(argc
, argv
);
148 set_program_name(argv
[0]);
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();
159 ovs_fatal(0, "exactly one non-option argument required; "
160 "use --help for help");
163 rconn
= rconn_new(argv
[0], 5, 5);
165 die_if_already_running();
168 initialize_terminal();
169 fatal_signal_add_hook(restore_terminal
, NULL
, NULL
, true);
173 user_selected
= false;
177 long long timeout
= time_msec() + 1000;
182 fetch_status(rconn
, &dict
, timeout
);
183 dict_add(&dict
, "debug", debug_mode
? "true" : "false");
184 compose_messages(&dict
, rconn
);
187 if (!empty_message(msg
)) {
190 msg
= user_selected
? next_message(msg
) : best_message();
194 msg
= best_message();
196 user_selected
= false;
198 if (!user_selected
) {
209 if (c
!= last_key
|| time_msec() > last_key_time
+ 250) {
213 last_key_time
= time_msec();
216 if (c
== KEY_DOWN
|| c
== KEY_UP
) {
217 msg
= (c
== KEY_DOWN
? next_message(msg
)
218 : prev_message(msg
));
220 user_selected
= true;
221 } else if (c
== '\r' || c
== '\n') {
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");
230 } else if (c
== '\b' || c
== '\x7f' ||
231 c
== '\x1b' || c
== KEY_BACKSPACE
|| c
== KEY_DC
) {
233 drain_keyboard_buffer();
244 poll_fd_wait(STDIN_FILENO
, POLLIN
);
245 poll_timer_wait(timeout
- time_msec());
247 } while (time_msec() < timeout
);
256 compose_messages(const struct dict
*dict
, struct rconn
*rconn
)
258 if (!show_reboot_state()) {
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
);
269 struct put_flows_data
{
277 parse_flow_reply(void *data
, struct put_flows_data
*pfd
)
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
;
285 if (ntohs(oh
->length
) < min_size
) {
286 VLOG_WARN("reply is too short (%"PRIu16
")", ntohs(oh
->length
));
289 if (oh
->xid
!= pfd
->xid
) {
290 VLOG_WARN("xid 0x%08"PRIx32
" != expected 0x%08"PRIx32
,
294 if (oh
->type
!= OFPT_STATS_REPLY
) {
295 VLOG_WARN("reply is wrong type %"PRIu8
, oh
->type
);
300 if (rpy
->type
!= htons(OFPST_AGGREGATE
)) {
301 VLOG_WARN("reply has wrong stat type ID %08"PRIx16
, rpy
->type
);
305 asr
= (struct ofp_aggregate_stats_reply
*) rpy
->body
;
306 pfd
->flow_count
= ntohl(asr
->flow_count
);
307 pfd
->got_reply
= true;
313 const char *dico
= tigetstr("dico");
314 return dico
&& dico
!= (const char *) -1;
318 set_icon(int num
, int r0
, int r1
, int r2
, int r3
, int r4
, int r5
, int r6
,
322 putp(tparm(tigetstr("dico"), num
, r0
, r1
, r2
, r3
, r4
, r5
, r6
, r7
));
327 set_repeated_icon(int num
, int row
)
329 set_icon(num
, row
, row
, row
, row
, row
, row
, row
, row
);
334 set_brick_icon(int num
, int n_solid
)
336 const static int rows
[6] = {_____
, X____
, XX___
, XXX__
, XXXX_
, XXXXX
};
337 set_repeated_icon(num
, rows
[n_solid
< 0 ? 0
344 icon_char(int num
, int alternate
)
346 return have_icons() ? 0x80 | num
| A_ALTCHARSET
: alternate
;
350 put_icon(int num
, char alternate
)
352 addch(icon_char(num
, alternate
));
357 bar_graph(int n_chars
, int n_pixels
)
363 } else if (n_pixels
> n_chars
* 5) {
364 n_pixels
= n_chars
* 5;
368 set_brick_icon(0, 5);
369 for (i
= 0; i
< n_pixels
/ 5; i
++) {
374 set_brick_icon(1, n_pixels
% 5);
381 put_flows(void *pfd_
)
383 struct put_flows_data
*pfd
= pfd_
;
384 static struct rconn_packet_counter
*counter
;
388 counter
= rconn_packet_counter_create();
392 struct ofp_stats_request
*rq
;
393 struct ofp_aggregate_stats_request
*asr
;
396 pfd
->xid
= random_uint32();
397 rq
= make_openflow_xid(sizeof *rq
, OFPT_STATS_REQUEST
,
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);
410 if (!pfd
->got_reply
) {
413 rconn_run(pfd
->rconn
);
414 for (i
= 0; i
< 50; i
++) {
417 b
= rconn_recv(pfd
->rconn
);
422 parse_flow_reply(b
->data
, pfd
);
424 if (pfd
->got_reply
) {
430 gethostname(host
, sizeof host
);
431 host
[sizeof host
- 1] = '\0';
432 if (strlen(host
) + 6 <= 16) {
433 addf("Host: %s\n", host
);
437 if (pfd
->got_reply
) {
438 addf("Flows: %"PRIu32
, pfd
->flow_count
);
441 if (!pfd
->got_reply
) {
442 rconn_run_wait(pfd
->rconn
);
443 rconn_recv_wait(pfd
->rconn
);
448 show_flows(struct rconn
*rconn
)
450 static struct message
*m
;
451 static struct put_flows_data pfd
;
453 memset(&pfd
, 0, sizeof pfd
);
455 emit_function(&m
, P_STATUS
, put_flows
, &pfd
);
459 struct put_dpid_ip_data
{
468 parse_dp_reply(void *data
, struct put_dpid_ip_data
*pdid
)
470 struct ofp_switch_features
*osf
;
471 struct ofp_header
*oh
;
474 if (ntohs(oh
->length
) < sizeof *osf
) {
475 VLOG_WARN("reply is too short (%"PRIu16
")", ntohs(oh
->length
));
478 if (oh
->xid
!= pdid
->xid
) {
479 VLOG_WARN("xid 0x%08"PRIx32
" != expected 0x%08"PRIx32
,
483 if (oh
->type
!= OFPT_FEATURES_REPLY
) {
484 VLOG_WARN("reply is wrong type %"PRIu8
, oh
->type
);
489 pdid
->dpid
= ntohll(osf
->datapath_id
);
490 pdid
->got_reply
= true;
494 put_dpid_id(void *pdid_
)
496 struct put_dpid_ip_data
*pdid
= pdid_
;
497 static struct rconn_packet_counter
*counter
;
500 counter
= rconn_packet_counter_create();
504 struct ofp_header
*oh
;
507 pdid
->xid
= random_uint32();
508 oh
= make_openflow_xid(sizeof *oh
, OFPT_FEATURES_REQUEST
,
510 rconn_send_with_limit(pdid
->rconn
, b
, counter
, 10);
513 if (!pdid
->got_reply
) {
516 rconn_run(pdid
->rconn
);
517 for (i
= 0; i
< 50; i
++) {
520 b
= rconn_recv(pdid
->rconn
);
525 parse_dp_reply(b
->data
, pdid
);
527 if (pdid
->got_reply
) {
534 if (pdid
->got_reply
) {
535 addf("%012"PRIx64
, pdid
->dpid
);
537 addf("\nIP: %s", pdid
->ip
);
539 if (!pdid
->got_reply
) {
540 rconn_run_wait(pdid
->rconn
);
541 rconn_recv_wait(pdid
->rconn
);
546 show_dpid_ip(struct rconn
*rconn
, const struct dict
*dict
)
548 static struct message
*m
;
549 static struct put_dpid_ip_data pdid
;
550 const char *is_connected
, *local_ip
;
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. */
560 memset(&pdid
, 0, sizeof pdid
);
562 ovs_strlcpy(pdid
.ip
, local_ip
? local_ip
: "", sizeof pdid
.ip
);
563 emit_function(&m
, P_STATUS
, put_dpid_id
, &pdid
);
567 dict_find(const struct dict
*dict
, const char *name
)
571 for (i
= 0; i
< dict
->n
; i
++) {
572 const struct pair
*p
= &dict
->pairs
[i
];
573 if (!strcmp(p
->name
, name
)) {
582 dict_lookup(const struct dict
*dict
, const char *name
, const char **value
)
584 size_t idx
= dict_find(dict
, name
);
585 if (idx
!= SIZE_MAX
) {
586 *value
= dict
->pairs
[idx
].value
;
595 dict_get(const struct dict
*dict
, const char *name
)
598 return dict_lookup(dict
, name
, &value
) ? value
: NULL
;
602 dict_get_int(const struct dict
*dict
, const char *name
, int def
)
605 return dict_lookup(dict
, name
, &value
) ? atoi(value
) : def
;
609 dict_get_bool(const struct dict
*dict
, const char *name
, bool def
)
612 if (dict_lookup(dict
, name
, &value
)) {
613 if (!strcmp(value
, "true")) {
616 if (!strcmp(value
, "false")) {
624 dict_get_string(const struct dict
*dict
, const char *name
, const char *def
)
627 return dict_lookup(dict
, name
, &value
) ? value
: def
;
631 dict_get_ip(const struct dict
*dict
, const char *name
)
634 return (inet_aton(dict_get_string(dict
, name
, ""), &in
) ? in
.s_addr
639 addf(const char *format
, ...)
644 va_start(args
, format
);
645 vsnprintf(buf
, sizeof buf
, format
, args
);
652 show_ofproto_state(const struct dict
*dict
)
654 static struct message
*msg
;
655 const char *is_connected
;
657 if (!dict_lookup(dict
, "remote.is-connected", &is_connected
)) {
658 /* Secchan not running or not responding. */
659 emit(&msg
, P_ERROR
, "Switch disabled");
664 discovery_state_label(const char *name
)
666 static struct dict
*states
;
668 states
= xmalloc(sizeof *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");
680 return dict_get_string(states
, name
, "Error");
684 show_discovery_state(const struct dict
*dict
)
686 static struct message
*m_bound
, *m_other
;
688 const char *state
, *ip
;
689 enum priority priority
;
692 state
= dict_get_string(dict
, "discovery.state", NULL
);
696 ip
= dict_get_string(dict
, "discovery.ip", NULL
);
697 state_elapsed
= dict_get_int(dict
, "discovery.state-elapsed", 0);
699 if (!strcmp(state
, "BOUND")) {
704 priority
= P_PROGRESS
;
706 emit(m
, priority
, "Discovery %s\n%s",
707 progress(), discovery_state_label(state
));
709 emit(m
, priority
, " %s", ip
);
714 human_time(int seconds
, char *buf
, size_t size
)
716 const char *sign
= "";
719 seconds
= seconds
== INT_MIN
? INT_MAX
: -seconds
;
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);
729 snprintf(buf
, size
, "%s%d days", sign
, seconds
/ 60 / 60 / 24);
734 show_fail_open_state(const struct dict
*dict
)
736 static struct message
*m
;
737 int cur_duration
, trigger_duration
;
739 if (!dict_get_bool(dict
, "fail-open.triggered", false)) {
742 trigger_duration
= dict_get_int(dict
, "fail-open.trigger-duration", 0);
743 cur_duration
= dict_get_int(dict
, "fail-open.current-duration", 0);
745 emit(&m
, P_WARNING
, "Failed open %s\nafter %d secs",
746 progress(), trigger_duration
);
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());
757 return "..." + (3 - (unsigned int) time_now() % 4);
761 show_remote_state(const struct dict
*dict
)
763 bool debug_mode
= dict_get_bool(dict
, "debug", false);
764 const char *state
, *is_connected
;
766 state
= dict_get_string(dict
, "remote.state", NULL
);
770 is_connected
= dict_get_string(dict
, "remote.is-connected", "false");
771 if (!strcmp(is_connected
, "true")) {
773 static struct message
*m_connected
;
775 human_time(dict_get_int(dict
, "remote.last-connection", 0),
777 emit(&m_connected
, P_STATUS
,
778 "Connected for\nlast %s %s", buf
, progress());
781 if (!strcmp(state
, "IDLE")) {
782 static struct message
*m_idle
;
783 emit(&m_idle
, P_PROGRESS
, "Sent idle probe");
787 const char *name
= dict_get_string(dict
, "remote.name", NULL
);
789 static struct message
*m_name
;
790 emit(&m_name
, P_STATUS
, "Connected to\n%s", name
);
794 int elapsed
, backoff
;
795 const char *name
, *error
;
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];
810 emit(&m
[0], P_PROGRESS
, "Connect failed:\n%s", error
);
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),
817 } else if (!strcmp(state
, "CONNECTING")) {
818 static struct message
*m
;
819 emit(&m
, P_PROGRESS
, "Connecting %s\n%s", progress(), name
);
825 fetch_status(struct rconn
*rconn
, struct dict
*dict
, long long timeout
)
827 static struct rconn_packet_counter
*counter
;
829 struct nicira_header
*rq
;
834 counter
= rconn_packet_counter_create();
837 xid
= random_uint32();
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);
845 /* continue into the loop so that we pause for a while */
848 while (time_msec() < timeout
) {
853 for (i
= 0; i
< 50; i
++) {
857 b
= rconn_recv(rconn
);
862 got_reply
= parse_reply(b
->data
, dict
, xid
);
869 rconn_run_wait(rconn
);
870 rconn_recv_wait(rconn
);
871 poll_timer_wait(timeout
- time_msec());
877 parse_reply(void *data
, struct dict
*dict
, uint32_t xid
)
879 struct ofp_header
*oh
;
880 struct nicira_header
*rpy
;
883 if (ntohs(oh
->length
) < sizeof *rpy
) {
884 VLOG_WARN("reply is too short (%"PRIu16
")", ntohs(oh
->length
));
887 if (oh
->xid
!= xid
) {
888 VLOG_WARN("xid 0x%08"PRIx32
" != expected 0x%08"PRIx32
, oh
->xid
, xid
);
891 if (oh
->type
!= OFPT_VENDOR
) {
892 VLOG_WARN("reply is wrong type %"PRIu8
, oh
->type
);
897 if (rpy
->vendor
!= htonl(NX_VENDOR_ID
)) {
898 VLOG_WARN("reply has wrong vendor ID %08"PRIx32
, rpy
->vendor
);
901 if (rpy
->subtype
!= htonl(NXT_STATUS_REPLY
)) {
902 VLOG_WARN("reply has wrong subtype %08"PRIx32
, rpy
->subtype
);
906 dict_parse(dict
, (const char *) (rpy
+ 1),
907 ntohs(oh
->length
) - sizeof *rpy
);
912 dict_parse(struct dict
*dict
, const char *data
, size_t nbytes
)
914 char *save_ptr
= NULL
;
917 copy
= xmemdup0(data
, nbytes
);
918 for (name
= strtok_r(copy
, "=", &save_ptr
); name
;
919 name
= strtok_r(NULL
, "=", &save_ptr
))
921 char *value
= strtok_r(NULL
, "\n", &save_ptr
);
925 dict_add(dict
, name
, value
);
931 dict_init(struct dict
*dict
)
935 dict
->pairs
= xmalloc(sizeof *dict
->pairs
* dict
->max
);
939 dict_add(struct dict
*dict
, const char *name
, const char *value
)
941 dict_add_nocopy(dict
, xstrdup(name
), xstrdup(value
));
945 dict_add_nocopy(struct dict
*dict
, char *name
, char *value
)
949 if (dict
->n
>= dict
->max
) {
951 dict
->pairs
= xrealloc(dict
->pairs
, sizeof *dict
->pairs
* dict
->max
);
953 p
= &dict
->pairs
[dict
->n
++];
959 dict_delete(struct dict
*dict
, const char *name
)
962 while ((idx
= dict_find(dict
, name
)) != SIZE_MAX
) {
963 struct pair
*pair
= &dict
->pairs
[idx
];
966 dict
->pairs
[idx
] = dict
->pairs
[--dict
->n
];
971 dict_free(struct dict
*dict
)
976 for (i
= 0; i
< dict
->n
; i
++) {
977 free(dict
->pairs
[i
].name
);
978 free(dict
->pairs
[i
].value
);
985 initialize_terminal(void)
991 intrflush(stdscr
, FALSE
);
992 keypad(stdscr
, TRUE
);
993 nodelay(stdscr
, TRUE
);
995 scrollok(stdscr
, TRUE
);
999 restore_terminal(void *aux OVS_UNUSED
)
1009 struct show_rates_data
{
1010 struct rconn
*rconn
;
1012 struct byte_count prev
, now
;
1017 parse_port_reply(void *data
, struct show_rates_data
*rates
)
1019 struct ofp_header
*oh
;
1020 struct ofp_stats_reply
*rpy
;
1021 struct ofp_port_stats
*ops
;
1026 if (ntohs(oh
->length
) < sizeof *rpy
) {
1027 VLOG_WARN("reply is too short (%"PRIu16
")", ntohs(oh
->length
));
1030 if (oh
->xid
!= rates
->xid
) {
1031 VLOG_WARN("xid 0x%08"PRIx32
" != expected 0x%08"PRIx32
,
1032 oh
->xid
, rates
->xid
);
1035 if (oh
->type
!= OFPT_STATS_REPLY
) {
1036 VLOG_WARN("reply is wrong type %"PRIu8
, oh
->type
);
1041 if (rpy
->type
!= htons(OFPST_PORT
)) {
1042 VLOG_WARN("reply has wrong stat type ID %08"PRIx16
, rpy
->type
);
1046 n_ports
= ((ntohs(oh
->length
) - offsetof(struct ofp_stats_reply
, body
))
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;
1057 rates
->now
.tx_bytes
+= ntohll(ops
->tx_bytes
);
1060 rates
->got_reply
= true;
1064 dump_graph(const bool graph
[80])
1066 signed char icons
[32];
1070 memset(icons
, -1, sizeof icons
);
1071 for (i
= 0; i
< 16; i
++) {
1076 for (j
= 0; j
< 5; j
++) {
1077 row
= (row
<< 1) | graph
[i
* 5 + j
];
1084 if (icons
[row
] < 0) {
1089 set_repeated_icon(n_icons
, row
);
1090 icons
[row
] = n_icons
++;
1092 put_icon(icons
[row
], row
== 0x1f ? '#' : ' ');
1097 do_show_data_rates(void *rates_
)
1099 struct show_rates_data
*rates
= rates_
;
1100 static struct rconn_packet_counter
*counter
;
1104 counter
= rconn_packet_counter_create();
1107 struct ofp_stats_request
*rq
;
1110 rates
->xid
= random_uint32();
1111 rq
= make_openflow_xid(sizeof *rq
, OFPT_STATS_REQUEST
,
1113 rq
->type
= htons(OFPST_PORT
);
1114 rq
->flags
= htons(0);
1115 rconn_send_with_limit(rates
->rconn
, b
, counter
, 10);
1118 if (!rates
->got_reply
) {
1121 rconn_run(rates
->rconn
);
1122 for (i
= 0; i
< 50; i
++) {
1125 b
= rconn_recv(rates
->rconn
);
1130 parse_port_reply(b
->data
, rates
);
1132 if (rates
->got_reply
) {
1166 memset(graph
, 0, sizeof graph
);
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)
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;
1190 } else if (pixels
> 80) {
1193 memset(graph
, 1, pixels
);
1198 if (!rates
->got_reply
) {
1199 rconn_run_wait(rates
->rconn
);
1200 rconn_recv_wait(rates
->rconn
);
1205 show_data_rates(struct rconn
*rconn
, const struct dict
*dict
)
1207 static struct message
*m
;
1208 static struct show_rates_data rates
;
1209 const char *is_connected
, *local_ip
;
1210 static bool inited
= false;
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. */
1220 rates
.rconn
= rconn
;
1222 rates
.got_reply
= false;
1224 rates
.now
.tx_bytes
= UINT64_MAX
;
1225 rates
.prev
.tx_bytes
= UINT64_MAX
;
1228 emit_function(&m
, P_STATUS
, do_show_data_rates
, &rates
);
1233 void (*function
)(void *aux
);
1238 enum priority priority
;
1243 static struct message
**messages
;
1244 static size_t n_messages
, allocated_messages
;
1246 static struct message
*
1247 allocate_message(struct message
**msgp
)
1250 /* Allocate and initialize message. */
1251 *msgp
= xzalloc(sizeof **msgp
);
1252 (*msgp
)->index
= n_messages
;
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
);
1260 messages
[n_messages
++] = *msgp
;
1266 emit(struct message
**msgp
, enum priority priority
, const char *format
, ...)
1268 struct message
*msg
= allocate_message(msgp
);
1272 msg
->priority
= priority
;
1274 va_start(args
, format
);
1275 length
= strlen(msg
->string
);
1276 vsnprintf(msg
->string
+ length
, sizeof msg
->string
- length
, format
, args
);
1281 emit_function(struct message
**msgp
, enum priority priority
,
1282 void (*function
)(void *aux
), void *aux
)
1284 struct message
*msg
= allocate_message(msgp
);
1285 msg
->priority
= priority
;
1286 msg
->function
= function
;
1291 shown(struct message
**msgp
)
1293 struct message
*msg
= allocate_message(msgp
);
1298 clear_messages(void)
1302 for (i
= 0; i
< n_messages
; i
++) {
1303 struct message
*msg
= messages
[i
];
1304 msg
->string
[0] = '\0';
1305 msg
->function
= NULL
;
1309 static struct message
*
1312 struct message
*best_msg
;
1316 best_score
= INT_MIN
;
1318 for (i
= 0; i
< n_messages
; i
++) {
1319 struct message
*msg
= messages
[i
];
1322 if (empty_message(msg
)) {
1326 score
= msg
->priority
;
1330 score
-= msg
->shown
;
1332 if (score
> best_score
) {
1341 message_shown(struct message
*msg
)
1343 if (msg
&& msg
->shown
++ > 3600) {
1349 empty_message(const struct message
*msg
)
1351 return !msg
|| (!msg
->string
[0] && !msg
->function
);
1354 static struct message
*get_message(size_t index
)
1356 assert(index
<= n_messages
|| index
== SIZE_MAX
);
1357 return (index
< n_messages
? messages
[index
]
1358 : index
== SIZE_MAX
? messages
[n_messages
- 1]
1362 static struct message
*
1363 next_message(struct message
*msg
)
1367 for (p
= get_message(msg
->index
+ 1); p
!= msg
;
1368 p
= get_message(p
->index
+ 1)) {
1369 if (!empty_message(p
)) {
1376 static struct message
*
1377 prev_message(struct message
*msg
)
1381 for (p
= get_message(msg
->index
- 1); p
!= msg
;
1382 p
= get_message(p
->index
- 1)) {
1383 if (!empty_message(p
)) {
1391 put_message(const struct message
*m
)
1395 } else if (m
->function
) {
1396 m
->function(m
->aux
);
1407 for (i
= 0; i
< n_messages
; i
++) {
1408 struct message
*msg
= messages
[i
];
1409 if (!empty_message(msg
)) {
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;
1419 if (msg
->age
&& msg
->age
% 60 == 0) {
1420 msg
->shown
-= MAX(0, 5 - (load
+ 6) / 12);
1421 if (msg
->shown
< 0) {
1425 if (msg
->age
++ > 3600) {
1432 /* Set by SIGUSR1 handler. */
1433 static volatile sig_atomic_t sigusr1_triggered
;
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
;
1439 static void sigusr1_handler(int);
1442 init_reboot_notifier(void)
1444 signal(SIGUSR1
, sigusr1_handler
);
1448 sigusr1_handler(int signr OVS_UNUSED
)
1450 sigusr1_triggered
= true;
1454 show_reboot_state(void)
1456 if (sigusr1_triggered
) {
1457 reboot_deadline
= time_now() + 30;
1458 sigusr1_triggered
= false;
1460 if (time_now() < reboot_deadline
) {
1461 static struct message
*msg
;
1462 emit(&msg
, P_FATAL
, "Rebooting");
1470 void (*f
)(const struct dict
*);
1477 struct menu_item
**items
;
1478 size_t n_items
, allocated_items
;
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
);
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
*);
1495 menu_init(struct menu
*menu
)
1497 memset(menu
, 0, sizeof *menu
);
1501 menu_free(struct menu
*menu
)
1505 for (i
= 0; i
< menu
->n_items
; i
++) {
1506 struct menu_item
*item
= menu
->items
[i
];
1513 static struct menu_item
*
1514 menu_add_item(struct menu
*menu
, const char *text
, ...)
1516 struct menu_item
*item
;
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
);
1524 item
= menu
->items
[menu
->n_items
++] = xmalloc(sizeof *item
);
1525 va_start(args
, text
);
1526 item
->text
= xvasprintf(text
, args
);
1530 item
->enabled
= true;
1536 menu(const struct dict
*dict
)
1538 bool debug_mode
= dict_get_bool(dict
, "debug", false);
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
;
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
;
1553 choice
= menu_show(&menu
, 0, true);
1555 void (*f
)(const struct dict
*) = menu
.items
[choice
]->f
;
1565 menu_show(const struct menu
*menu
, int start
, bool select
)
1567 long long int adjust
= LLONG_MAX
;
1568 int min
= 0, max
= MAX(menu
->n_items
- 2, 0);
1597 if (menu
->n_items
) {
1598 pos
= MIN(menu
->n_items
- 1, MAX(0, start
));
1607 while ((key
= getch()) != ERR
) {
1610 if (select
&& selection
> 0) {
1612 if (selection
>= pos
) {
1622 if (select
&& selection
< menu
->n_items
- 1) {
1624 if (selection
<= pos
+ 1) {
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");
1639 } else if (item
->toggle
>= 0) {
1640 item
->toggle
= !item
->toggle
;
1646 case '\b': case '\x7f': case '\x1b':
1647 case KEY_BACKSPACE
: case KEY_DC
:
1650 adjust
= time_msec() + 1000;
1652 if (time_msec() >= adjust
&& menu
->n_items
> 1) {
1655 } else if (pos
> max
) {
1663 if (!menu
->n_items
) {
1667 for (idx
= pos
; idx
< pos
+ 2; idx
++) {
1672 if (selection
== idx
) {
1681 } else if (idx
>= menu
->n_items
) {
1684 const struct menu_item
*item
= menu
->items
[idx
];
1685 size_t length
= strlen(item
->text
);
1686 if (!item
->enabled
) {
1690 if (item
->toggle
>= 0) {
1692 addch(icon_char(item
->toggle
? 2 : 1, 0));
1695 addstr(item
->toggle
? "[X]" : "[ ]");
1699 addnstr(item
->text
, MIN(width
, length
));
1700 if (!item
->enabled
) {
1711 if (pos
< min
|| pos
> max
) {
1712 poll_timer_wait(adjust
- time_msec());
1714 poll_fd_wait(STDIN_FILENO
, POLLIN
);
1720 menu_show2(const struct menu
*menu
, int start
, bool select
)
1723 if (menu
->n_items
) {
1724 pos
= MIN(menu
->n_items
- 1, MAX(0, start
));
1749 while ((key
= getch()) != ERR
) {
1758 if (menu
->n_items
> 0 && pos
< menu
->n_items
- 1) {
1763 case '\r': case '\n':
1764 if (select
&& !menu
->items
[pos
]->enabled
) {
1765 show_string("Item disabled");
1770 case '\b': case '\x7f': case '\x1b':
1771 case KEY_BACKSPACE
: case KEY_DC
:
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
;
1790 addch(pos
> 0 ? icon_char(0, '^') : ' ');
1791 if (!item
->enabled
&& len1
) {
1794 addnstr(line1
, MIN(len1
, width
));
1795 if (!item
->enabled
&& len1
) {
1801 addch(pos
< menu
->n_items
- 1 ? icon_char(1, 'V') : ' ');
1802 if (!item
->enabled
&& len2
) {
1805 addnstr(line2
, MIN(len2
, width
));
1806 if (!item
->enabled
&& len2
) {
1812 poll_fd_wait(STDIN_FILENO
, POLLIN
);
1818 yesno(const char *title
, bool def
)
1835 while ((key
= getch()) != ERR
) {
1852 case '\r': case '\n':
1863 addch(answer
? icon_char(0, '>') : ' ');
1867 addch(!answer
? icon_char(0, '>') : ' ');
1872 poll_fd_wait(STDIN_FILENO
, POLLIN
);
1878 cmd_show_version(const struct dict
*dict OVS_UNUSED
)
1880 show_string(VERSION BUILDNR
);
1884 cmd_browse_status(const struct dict
*dict
)
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
);
1894 menu_show(&menu
, 0, false);
1899 cmd_shell(const struct dict
*dict OVS_UNUSED
)
1907 printf("Type ^D to exit\n");
1914 home
= getenv("HOME");
1919 initialize_terminal();
1923 cmd_show_motto(const struct dict
*dict OVS_UNUSED
)
1925 show_string("\"Just Add Ice\"");
1929 show_string(const char *string
)
1931 VLOG_INFO("%s", string
);
1937 block_until(time_msec() + 5000);
1941 block_until(long long timeout
)
1943 while (timeout
> time_msec()) {
1944 poll_timer_wait(timeout
- time_msec());
1947 drain_keyboard_buffer();
1951 drain_keyboard_buffer(void)
1953 while (getch() != ERR
) {
1959 read_vars(const char *cmd
, struct dict
*dict
)
1965 stream
= popen(cmd
, "r");
1967 VLOG_ERR("popen(\"%s\") failed: %s", cmd
, strerror(errno
));
1973 while (!ds_get_line(&ds
, stream
)) {
1974 const char *s
= ds_cstr(&ds
);
1975 const char *equals
= strchr(s
, '=');
1977 dict_add_nocopy(dict
,
1978 xmemdup0(s
, equals
- s
), xstrdup(equals
+ 1));
1981 status
= pclose(stream
);
1983 char *msg
= process_status_msg(status
);
1984 VLOG_ERR("pclose(\"%s\") reported subprocess failure: %s",
1994 run_and_report_failure(char **argv
, const char *title
)
1996 int null_fds
[3] = {0, 1, 2};
2001 s
= process_escape_args(argv
);
2002 VLOG_INFO("starting subprocess: %s", s
);
2005 retval
= process_run(argv
, NULL
, 0, null_fds
, 3, &status
);
2007 char *s
= xasprintf("%s:\n%s", title
, strerror(retval
));
2011 } else if (status
) {
2012 char *msg
= process_status_msg(status
);
2013 char *s
= xasprintf("%s:\n%s", title
, msg
);
2019 VLOG_INFO("subprocess exited with status 0");
2025 do_load_config(const char *file_name
, struct dict
*dict
)
2027 struct dict auto_vars
;
2032 /* Get the list of the variables that the shell sets automatically. */
2033 retval
= read_vars("set -a && env", &auto_vars
);
2038 /* Get the variables from 'file_name'. */
2039 cmd
= xasprintf("set -a && . '%s' && env", file_name
);
2040 retval
= read_vars(cmd
, dict
);
2043 dict_free(&auto_vars
);
2048 for (i
= 0; i
< auto_vars
.n
; i
++) {
2049 dict_delete(dict
, auto_vars
.pairs
[i
].name
);
2051 dict_free(&auto_vars
);
2056 load_config(struct dict
*dict
)
2058 static const char default_file
[] = "/etc/default/openflow-switch";
2059 int retval
= do_load_config(default_file
, dict
);
2063 char *s
= xasprintf("Cfg load failed:\n%s", strerror(retval
));
2071 save_config(const struct svec
*settings
)
2077 VLOG_INFO("Saving configuration:");
2078 for (i
= 0; i
< settings
->n
; i
++) {
2079 VLOG_INFO("%s", settings
->names
[i
]);
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");
2088 long long int timeout
= time_msec() + 5000;
2093 addstr("Saved.\nRestarting...");
2097 svec_add(&argv
, "/bin/sh");
2098 svec_add(&argv
, "-c");
2100 "/etc/init.d/openflow-switch restart >/dev/null 2>&1");
2101 svec_terminate(&argv
);
2103 ok
= run_and_report_failure(argv
.names
, "Restart failed");
2105 block_until(timeout
);
2108 svec_destroy(&argv
);
2111 VLOG_INFO("Save completed successfully");
2113 VLOG_WARN("Save failed");
2119 match(pcre
*re
, const char *string
, int length
)
2124 retval
= pcre_exec(re
, NULL
, string
, length
, 0, PCRE_PARTIAL
,
2125 ovec
, ARRAY_SIZE(ovec
));
2127 if (ovec
[0] >= 0 && ovec
[1] >= length
) {
2128 /* 're' matched all of 'string'. */
2131 /* 're' matched the initial part of 'string' but not all of it. */
2132 return PCRE_ERROR_NOMATCH
;
2140 figure_choices(pcre
*re
, struct ds
*s
, int pos
, struct ds
*choices
)
2148 /* See whether the current string is a complete match. */
2149 if (!match(re
, s
->string
, pos
)) {
2150 ds_put_char(choices
, '\n');
2153 /* Then try all the other possibilities. */
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
);
2166 if (!choices
->length
) {
2167 ds_put_char(choices
, '\n');
2172 figure_completion(pcre
*re
, struct ds
*s
)
2178 /* See whether the current string is a complete match. */
2179 if (!match(re
, s
->string
, s
->length
)) {
2182 for (c
= 0x20; c
< 0x7f; c
++) {
2186 retval
= match(re
, s
->string
, s
->length
);
2189 if (retval
== PCRE_ERROR_PARTIAL
|| !retval
) {
2199 ds_put_char(s
, found
);
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")"
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]|" \
2215 #define XOCTET_RE "[0-9A-F][0-9A-F]"
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 \
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 \
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])"
2237 prompt(const char *prompt
, const char *initial
, const char *pattern
)
2259 re
= pcre_compile(pattern
, PCRE_ANCHORED
, &error
, &erroffset
, NULL
);
2261 VLOG_ERR("PCRE error for pattern \"%s\" at offset %d: %s",
2262 pattern
, erroffset
, error
);
2263 return xstrdup(initial
);
2266 retval
= pcre_fullinfo(re
, NULL
, PCRE_INFO_OKPARTIAL
, &okpartial
);
2272 ds_put_cstr(&ds
, initial
);
2274 figure_choices(re
, &ds
, pos
, &choices
);
2275 p
= memchr(choices
.string
, initial
[0], choices
.length
);
2276 chidx
= p
? p
- choices
.string
: 0;
2280 while ((key
= getch()) != ERR
) {
2283 if (choices
.length
> 1) {
2284 if (++chidx
>= choices
.length
) {
2287 ds
.string
[pos
] = choices
.string
[chidx
];
2288 ds_truncate(&ds
, pos
+ 1);
2289 figure_completion(re
, &ds
);
2294 if (choices
.length
> 1) {
2296 chidx
= choices
.length
- 1;
2298 ds
.string
[pos
] = choices
.string
[chidx
];
2299 ds_truncate(&ds
, pos
+ 1);
2300 figure_completion(re
, &ds
);
2304 case '\r': case '\n':
2305 if (choices
.string
[chidx
] == '\n') {
2306 ds_truncate(&ds
, pos
);
2307 return ds_cstr(&ds
);
2309 if (pos
>= ds
.length
) {
2311 ds_put_char(&ds
, choices
.string
[chidx
]);
2312 figure_choices(re
, &ds
, pos
, &choices
);
2314 figure_completion(re
, &ds
);
2317 figure_choices(re
, &ds
, pos
, &choices
);
2319 figure_completion(re
, &ds
);
2325 ds_truncate(&ds
, pos
+ 1);
2326 figure_choices(re
, &ds
, pos
, &choices
);
2330 case '\b': case '\x7f': case '\x1b':
2331 case KEY_BACKSPACE
: case KEY_DC
:
2335 return xstrdup(initial
);
2337 figure_choices(re
, &ds
, pos
, &choices
);
2339 if (pos
< ds
.length
) {
2340 p
= memchr(choices
.string
, ds
.string
[pos
],
2343 chidx
= p
- choices
.string
;
2349 if (key
>= 0x20 && key
< 0x7f) {
2350 /* Check whether 'key' is valid and toggle case if
2352 if (!memchr(choices
.string
, key
, choices
.length
)) {
2353 if (memchr(choices
.string
, toupper(key
),
2356 } else if (memchr(choices
.string
, tolower(key
),
2364 /* Insert 'key' and advance the position. */
2365 if (pos
>= ds
.length
) {
2366 ds_put_char(&ds
, key
);
2368 ds
.string
[pos
] = key
;
2372 if (choices
.string
[chidx
] != key
) {
2373 ds_truncate(&ds
, pos
);
2375 figure_choices(re
, &ds
, pos
, &choices
);
2377 if (pos
< ds
.length
) {
2378 p
= memchr(choices
.string
, ds
.string
[pos
],
2381 chidx
= p
- choices
.string
;
2384 figure_completion(re
, &ds
);
2392 addnstr(prompt
, MIN(40, strlen(prompt
)));
2394 c
= choices
.string
[chidx
];
2396 addstr(ds_cstr(&ds
));
2406 poll_fd_wait(STDIN_FILENO
, POLLIN
);
2412 prompt_ip(const char *title
, uint32_t *ip
)
2414 char *in
= xasprintf(IP_FMT
, IP_ARGS(ip
));
2415 char *out
= prompt(title
, in
, "^"IP_RE
"$");
2416 *ip
= inet_addr(out
);
2422 abbreviate_netdevs(const struct svec
*netdevs
, struct ds
*abbrev
)
2427 for (i
= 0; i
< netdevs
->n
; ) {
2428 size_t i_len
= strlen(netdevs
->names
[i
]);
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)) {
2439 if (abbrev
->length
) {
2440 ds_put_char(abbrev
, ' ');
2443 ds_put_cstr(abbrev
, netdevs
->names
[i
]);
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]);
2452 ds_put_char(abbrev
, ']');
2459 choose_netdevs(struct svec
*choices
)
2461 struct svec netdevs
= SVEC_EMPTY_INITIALIZER
;
2465 netdev_enumerate(&netdevs
);
2466 svec_sort(&netdevs
);
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
;
2476 if (!strncmp(name
, "wmaster", strlen("wmaster"))
2477 || !strncmp(name
, "of", strlen("of"))
2478 || !strcmp(name
, "lo")) {
2482 retval
= netdev_open_default(name
, &netdev
);
2484 bool exclude
= netdev_get_in4(netdev
, NULL
, NULL
) == 0;
2485 netdev_close(netdev
);
2491 item
= menu_add_item(&menu
, "%s", name
);
2492 item
->toggle
= svec_contains(choices
, name
);
2494 if (menu
.n_items
> 1) {
2495 menu_show(&menu
, 0, true);
2497 show_string("No available\nbridge ports");
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
);
2512 is_datapath_id_in_dmi(void)
2518 dmidecode
= popen("dmidecode -s system-uuid", "r");
2522 is_in_dmi
= fgets(line
, sizeof line
, dmidecode
) && strstr(line
, "-002320");
2527 struct switch_config
{
2528 struct svec netdevs
;
2529 enum { DISCOVERY
, IN_BAND
} mode
;
2531 uint32_t switch_mask
;
2533 enum { FAIL_DROP
, FAIL_SWITCH
} disconnected
;
2536 int inactivity_probe
;
2538 char *controller_vconn
;
2543 disconnected_string(int value
)
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
;
2551 cmd_configure(const struct dict
*dict OVS_UNUSED
)
2553 bool debug_mode
= dict_get_bool(dict
, "debug", false);
2554 struct dict config_dict
;
2555 struct switch_config config
;
2558 if (!load_config(&config_dict
)) {
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
,
2571 config
.disconnected
= (!strcmp(dict_get_string(&config_dict
,
2572 "DISCONNECTED_MODE", ""),
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",
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");
2583 const char *dpid
= dict_get(&config_dict
, "DATAPATH_ID");
2585 struct ds ds
= DS_EMPTY_INITIALIZER
;
2587 for (cp
= dpid
; *cp
!= '\0'; cp
++) {
2589 ds_put_char(&ds
, toupper((unsigned char) *cp
));
2592 config
.datapath_id
= ds_cstr(&ds
);
2594 config
.datapath_id
= xstrdup("Random");
2597 dict_free(&config_dict
);
2600 while (start
!= -1) {
2609 MENU_DISCONNECTED_MODE
,
2613 MENU_INACTIVITY_PROBE
,
2618 struct menu_item
*item
;
2626 item
= menu_add_item(&menu
, "Exit");
2627 item
->id
= MENU_EXIT
;
2630 abbreviate_netdevs(&config
.netdevs
, &ports
);
2631 item
= menu_add_item(&menu
, "Bridge Ports:\n%s", ds_cstr(&ports
));
2632 item
->id
= MENU_NETDEVS
;
2636 item
= menu_add_item(&menu
, "Mode:\n%s",
2637 (config
.mode
== DISCOVERY
2638 ? "Discovery" : "In-Band"));
2639 item
->id
= MENU_MODE
;
2642 if (config
.switch_ip
== htonl(0)) {
2643 item
= menu_add_item(&menu
, "Switch IP Addr:\nDHCP");
2645 item
= menu_add_item(&menu
, "Switch IP Addr:\n"IP_FMT
,
2646 IP_ARGS(&config
.switch_ip
));
2649 item
->enabled
= config
.mode
== IN_BAND
;
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);
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);
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
;
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
;
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");
2679 /* Spanning tree protocol. */
2681 item
= menu_add_item(&menu
, "802.1D-1998 STP:\n%s",
2682 config
.stp
? "Enabled" : "Disabled");
2683 item
->id
= MENU_STP
;
2686 /* Rate-limiting. */
2688 if (config
.rate_limit
< 0) {
2689 item
= menu_add_item(&menu
, "Ctlr rate limit:\nDisabled");
2691 item
= menu_add_item(&menu
, "Ctlr rate limit:\n%d/s",
2694 item
->id
= MENU_RATE_LIMIT
;
2697 /* Inactivity probe. */
2699 if (config
.inactivity_probe
< 0) {
2700 item
= menu_add_item(&menu
, "Activity probe:\nDefault");
2702 item
= menu_add_item(&menu
, "Activity probe:\n%d s",
2703 config
.inactivity_probe
);
2705 item
->id
= MENU_INACTIVITY_PROBE
;
2710 if (config
.max_backoff
< 0) {
2711 item
= menu_add_item(&menu
, "Max backoff:\nDefault");
2713 item
= menu_add_item(&menu
, "Max backoff:\n%d s",
2714 config
.max_backoff
);
2716 item
->id
= MENU_MAX_BACKOFF
;
2719 start
= menu_show2(&menu
, start
, true);
2729 choose_netdevs(&config
.netdevs
);
2733 out
= prompt("Mode:",
2734 config
.mode
== DISCOVERY
? "Discovery" : "In-Band",
2735 "^(Discovery|In-Band)$");
2736 config
.mode
= !strcmp(out
, "Discovery") ? DISCOVERY
: IN_BAND
;
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);
2747 if (ip
!= config
.switch_ip
) {
2748 config
.switch_ip
= ip
;
2749 if (ip
!= htonl(0)) {
2750 uint32_t mask
= guess_netmask(ip
);
2752 config
.switch_mask
= mask
;
2753 config
.switch_gw
= (ip
& mask
) | htonl(1);
2760 prompt_ip("Switch Netmask:", &config
.switch_mask
);
2764 prompt_ip("Switch Gateway:", &config
.switch_gw
);
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
;
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
);
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
;
2791 out
= prompt("802.1D-1998 STP:",
2792 config
.stp
? "Enabled" : "Disabled",
2793 "^(Enabled|Disabled)$");
2794 config
.stp
= !strcmp(out
, "Enabled");
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)$");
2806 = isdigit((unsigned char)out
[0]) ? atoi(out
) : -1;
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)$");
2817 config
.inactivity_probe
2818 = isdigit((unsigned char)out
[0]) ? atoi(out
) : -1;
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)$");
2830 = isdigit((unsigned char)out
[0]) ? atoi(out
) : -1;
2836 if (yesno("Save\nChanges?", false)) {
2841 netdevs
= svec_join(&config
.netdevs
, " ", "");
2842 svec_add_nocopy(&set
, xasprintf("NETDEVS=%s", netdevs
));
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");
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
));
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=");
2868 svec_add_nocopy(&set
,
2869 xasprintf("RATE_LIMIT=%d", config
.rate_limit
));
2871 if (config
.inactivity_probe
< 0) {
2872 svec_add(&set
, "INACTIVITY_PROBE=");
2874 svec_add_nocopy(&set
, xasprintf("INACTIVITY_PROBE=%d",
2875 config
.inactivity_probe
));
2877 if (config
.max_backoff
< 0) {
2878 svec_add(&set
, "MAX_BACKOFF=");
2880 svec_add_nocopy(&set
, xasprintf("MAX_BACKOFF=%d",
2881 config
.max_backoff
));
2887 svec_destroy(&config
.netdevs
);
2888 free(config
.controller_vconn
);
2889 free(config
.datapath_id
);
2893 cmd_set_up_pki(const struct dict
*dict OVS_UNUSED
)
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
;
2906 if (!load_config(&config_dict
)) {
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"),
2915 has_keys
= !stat(privkey_file
, &s
) && !stat(cert_file
, &s
);
2917 ? yesno("Generate\nkeys?", true)
2918 : yesno("Generate\nnew keys?", false)) {
2922 privkey_file
= def_privkey_file
;
2923 cert_file
= def_cert_file
;
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
);
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
);
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"));
2959 parse_options(int argc
, char *argv
[])
2962 OPT_DUMMY
= UCHAR_MAX
+ 1,
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
,
2973 char *short_options
= long_options_to_short_options(long_options
);
2978 c
= getopt_long(argc
, argv
, short_options
, long_options
, NULL
);
2988 OVS_PRINT_VERSION(OFP_VERSION
, OFP_VERSION
);
2991 VLOG_OPTION_HANDLERS
2992 DAEMON_OPTION_HANDLERS
3001 free(short_options
);
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");