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