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