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