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