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