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