Merge "master" into "next".
authorBen Pfaff <blp@nicira.com>
Thu, 11 Feb 2010 19:11:23 +0000 (11:11 -0800)
committerBen Pfaff <blp@nicira.com>
Thu, 11 Feb 2010 19:11:23 +0000 (11:11 -0800)
The main change here is the need to update all of the uses of UNUSED in
the next branch to OVS_UNUSED as it is now spelled on "master".

52 files changed:
1  2 
datapath/datapath.c
extras/ezio/ovs-switchui.c
extras/ezio/tty.c
lib/command-line.c
lib/compiler.h
lib/coverage.c
lib/dhcp-client.c
lib/dhcp.c
lib/dpif-linux.c
lib/dpif-netdev.c
lib/fatal-signal.c
lib/learning-switch.c
lib/netdev-linux.c
lib/netdev.c
lib/netdev.h
lib/packets.h
lib/process.c
lib/rconn.c
lib/shash.h
lib/socket-util.c
lib/stream-ssl.c
lib/stream-tcp.c
lib/stream-unix.c
lib/stream.c
lib/timeval.h
lib/unixctl.c
lib/util.c
lib/util.h
lib/vconn-stream.c
lib/vconn.c
lib/vlog.c
ofproto/discovery.c
ofproto/ofproto-sflow.c
ofproto/ofproto.c
ovsdb/execution.c
ovsdb/jsonrpc-server.c
ovsdb/ovsdb-client.c
ovsdb/ovsdb-idlc.in
ovsdb/ovsdb-server.c
ovsdb/ovsdb-tool.c
ovsdb/ovsdb.c
tests/test-dhcp-client.c
tests/test-jsonrpc.c
tests/test-ovsdb.c
tests/test-reconnect.c
tests/test-vconn.c
utilities/ovs-discover.c
utilities/ovs-dpctl.c
utilities/ovs-ofctl.c
utilities/ovs-vsctl.c
vswitchd/bridge.c
xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py

diff --combined datapath/datapath.c
@@@ -81,7 -81,7 +81,7 @@@ struct datapath *get_dp(int dp_idx
  }
  EXPORT_SYMBOL_GPL(get_dp);
  
- struct datapath *get_dp_locked(int dp_idx)
+ static struct datapath *get_dp_locked(int dp_idx)
  {
        struct datapath *dp;
  
@@@ -176,7 -176,7 +176,7 @@@ static void release_dp(struct kobject *
        kfree(dp);
  }
  
- struct kobj_type dp_ktype = {
+ static struct kobj_type dp_ktype = {
        .release = release_dp
  };
  
@@@ -325,7 -325,7 +325,7 @@@ static void release_nbp(struct kobject 
        kfree(p);
  }
  
- struct kobj_type brport_ktype = {
+ static struct kobj_type brport_ktype = {
  #ifdef CONFIG_SYSFS
        .sysfs_ops = &brport_sysfs_ops,
  #endif
@@@ -420,10 -420,9 +420,10 @@@ got_port_no
        if (err)
                goto out_put;
  
 +      set_dp_devs_mtu(dp, dev);
        dp_sysfs_add_if(dp->ports[port_no]);
  
-       err = __put_user(port_no, &port.port);
+       err = __put_user(port_no, &portp->port);
  
  out_put:
        dev_put(dev);
@@@ -577,10 -576,9 +577,10 @@@ static int dp_frame_hook(struct net_bri
  #endif
  
  #if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID)
 -/* This code is copied verbatim from net/dev/core.c in Xen's
 - * linux-2.6.18-92.1.10.el5.xs5.0.0.394.644.  We can't call those functions
 - * directly because they aren't exported. */
 +/* This code is based on a skb_checksum_setup from net/dev/core.c from a
 + * combination of Lenny's 2.6.26 Xen kernel and Xen's
 + * linux-2.6.18-92.1.10.el5.xs5.0.0.394.644.  We can't call this function
 + * directly because it isn't exported in all versions. */
  static int skb_pull_up_to(struct sk_buff *skb, void *ptr)
  {
        if (ptr < (void *)skb->tail)
  
  int vswitch_skb_checksum_setup(struct sk_buff *skb)
  {
 -      if (skb->proto_csum_blank) {
 -              if (skb->protocol != htons(ETH_P_IP))
 -                      goto out;
 -              if (!skb_pull_up_to(skb, skb->nh.iph + 1))
 -                      goto out;
 -              skb->h.raw = (unsigned char *)skb->nh.iph + 4*skb->nh.iph->ihl;
 -              switch (skb->nh.iph->protocol) {
 -              case IPPROTO_TCP:
 -                      skb->csum = offsetof(struct tcphdr, check);
 -                      break;
 -              case IPPROTO_UDP:
 -                      skb->csum = offsetof(struct udphdr, check);
 -                      break;
 -              default:
 -                      if (net_ratelimit())
 -                              printk(KERN_ERR "Attempting to checksum a non-"
 -                                     "TCP/UDP packet, dropping a protocol"
 -                                     " %d packet", skb->nh.iph->protocol);
 -                      goto out;
 -              }
 -              if (!skb_pull_up_to(skb, skb->h.raw + skb->csum + 2))
 -                      goto out;
 -              skb->ip_summed = CHECKSUM_HW;
 -              skb->proto_csum_blank = 0;
 +      struct iphdr *iph;
 +      unsigned char *th;
 +      int err = -EPROTO;
 +      __u16 csum_start, csum_offset;
 +
 +      if (!skb->proto_csum_blank)
 +              return 0;
 +
 +      if (skb->protocol != htons(ETH_P_IP))
 +              goto out;
 +
 +      if (!skb_pull_up_to(skb, skb_network_header(skb) + 1))
 +              goto out;
 +
 +      iph = ip_hdr(skb);
 +      th = skb_network_header(skb) + 4 * iph->ihl;
 +
 +      csum_start = th - skb->head;
 +      switch (iph->protocol) {
 +      case IPPROTO_TCP:
 +              csum_offset = offsetof(struct tcphdr, check);
 +              break;
 +      case IPPROTO_UDP:
 +              csum_offset = offsetof(struct udphdr, check);
 +              break;
 +      default:
 +              if (net_ratelimit())
 +                      printk(KERN_ERR "Attempting to checksum a non-"
 +                             "TCP/UDP packet, dropping a protocol"
 +                             " %d packet", iph->protocol);
 +              goto out;
        }
 -      return 0;
 +
 +      if (!skb_pull_up_to(skb, th + csum_offset + 2))
 +              goto out;
 +
 +      skb->ip_summed = CHECKSUM_PARTIAL;
 +      skb->proto_csum_blank = 0;
 +
 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
 +      skb->csum_start = csum_start;
 +      skb->csum_offset = csum_offset;
 +#else
 +      skb_set_transport_header(skb, csum_start - skb_headroom(skb));
 +      skb->csum = csum_offset;
 +#endif
 +
 +      err = 0;
 +
  out:
 -      return -EPROTO;
 +      return err;
  }
  #endif /* CONFIG_XEN && HAVE_PROTO_DATA_VALID */
  
 + /* Types of checksums that we can receive (these all refer to L4 checksums):
 + * 1. CHECKSUM_NONE: Device that did not compute checksum, contains full
 + *    (though not verified) checksum in packet but not in skb->csum.  Packets
 + *    from the bridge local port will also have this type.
 + * 2. CHECKSUM_COMPLETE (CHECKSUM_HW): Good device that computes checksums,
 + *    also the GRE module.  This is the same as CHECKSUM_NONE, except it has
 + *    a valid skb->csum.  Importantly, both contain a full checksum (not
 + *    verified) in the packet itself.  The only difference is that if the
 + *    packet gets to L4 processing on this machine (not in DomU) we won't
 + *    have to recompute the checksum to verify.  Most hardware devices do not
 + *    produce packets with this type, even if they support receive checksum
 + *    offloading (they produce type #5).
 + * 3. CHECKSUM_PARTIAL (CHECKSUM_HW): Packet without full checksum and needs to
 + *    be computed if it is sent off box.  Unfortunately on earlier kernels,
 + *    this case is impossible to distinguish from #2, despite having opposite
 + *    meanings.  Xen adds an extra field on earlier kernels (see #4) in order
 + *    to distinguish the different states.  The only real user of this type
 + *    with bridging is Xen (on later kernels).
 + * 4. CHECKSUM_UNNECESSARY (with proto_csum_blank true): This packet was
 + *    generated locally by a Xen DomU and has a partial checksum.  If it is
 + *    handled on this machine (Dom0 or DomU), then the checksum will not be
 + *    computed.  If it goes off box, the checksum in the packet needs to be
 + *    completed.  Calling skb_checksum_setup converts this to CHECKSUM_HW
 + *    (CHECKSUM_PARTIAL) so that the checksum can be completed.  In later
 + *    kernels, this combination is replaced with CHECKSUM_PARTIAL.
 + * 5. CHECKSUM_UNNECESSARY (with proto_csum_blank false): Packet with a correct
 + *    full checksum or using a protocol without a checksum.  skb->csum is
 + *    undefined.  This is common from devices with receive checksum
 + *    offloading.  This is somewhat similar to CHECKSUM_NONE, except that
 + *    nobody will try to verify the checksum with CHECKSUM_UNNECESSARY.
 + *
 + * Note that on earlier kernels, CHECKSUM_COMPLETE and CHECKSUM_PARTIAL are
 + * both defined as CHECKSUM_HW.  Normally the meaning of CHECKSUM_HW is clear
 + * based on whether it is on the transmit or receive path.  After the datapath
 + * it will be intepreted as CHECKSUM_PARTIAL.  If the packet already has a
 + * checksum, we will panic.  Since we can receive packets with checksums, we
 + * assume that all CHECKSUM_HW packets have checksums and map them to
 + * CHECKSUM_NONE, which has a similar meaning (the it is only different if the
 + * packet is processed by the local IP stack, in which case it will need to
 + * be reverified).  If we receive a packet with CHECKSUM_HW that really means
 + * CHECKSUM_PARTIAL, it will be sent with the wrong checksum.  However, there
 + * shouldn't be any devices that do this with bridging.
 + *
 + * The bridge has similar behavior and this function closely resembles
 + * skb_forward_csum().  It is slightly different because we are only concerned
 + * with bridging and not other types of forwarding and can get away with
 + * slightly more optimal behavior.*/
 +void
 +forward_ip_summed(struct sk_buff *skb)
 +{
 +#ifdef CHECKSUM_HW
 +      if (skb->ip_summed == CHECKSUM_HW)
 +              skb->ip_summed = CHECKSUM_NONE;
 +#endif
 +}
 +
  /* Append each packet in 'skb' list to 'queue'.  There will be only one packet
   * unless we broke up a GSO packet. */
  static int
@@@ -801,8 -720,6 +801,8 @@@ dp_output_control(struct datapath *dp, 
        if (skb_queue_len(queue) >= DP_MAX_QUEUE_LEN)
                goto err_kfree_skb;
  
 +      forward_ip_summed(skb);
 +
        /* Break apart GSO packets into their component pieces.  Otherwise
         * userspace may try to stuff a 64kB packet into a 1500-byte MTU. */
        if (skb_is_gso(skb)) {
@@@ -1318,29 -1235,6 +1318,29 @@@ int dp_min_mtu(const struct datapath *d
        return mtu ? mtu : ETH_DATA_LEN;
  }
  
 +/* Sets the MTU of all datapath devices to the minimum of the ports. 'dev'
 + * is the device whose MTU may have changed.  Must be called with RTNL lock
 + * and dp_mutex. */
 +void set_dp_devs_mtu(const struct datapath *dp, struct net_device *dev)
 +{
 +      struct net_bridge_port *p;
 +      int mtu;
 +
 +      ASSERT_RTNL();
 +
 +      if (is_dp_dev(dev))
 +              return;
 +
 +      mtu = dp_min_mtu(dp);
 +
 +      list_for_each_entry_rcu (p, &dp->port_list, node) {
 +              struct net_device *br_dev = p->dev;
 +
 +              if (is_dp_dev(br_dev))
 +                      dev_set_mtu(br_dev, mtu);
 +      }
 +}
 +
  static int
  put_port(const struct net_bridge_port *p, struct odp_port __user *uop)
  {
@@@ -144,7 -144,6 +144,7 @@@ main(int argc, char *argv[]
      long long int last_key_time = 0;
      int repeat_count = 0;
  
 +    proctitle_init(argc, argv);
      set_program_name(argv[0]);
      time_init();
      vlog_init();
      daemonize();
  
      initialize_terminal();
 -    fatal_signal_add_hook(restore_terminal, NULL, true);
 +    fatal_signal_add_hook(restore_terminal, NULL, NULL, true);
  
      msg = NULL;
      countdown = 0;
@@@ -996,7 -995,7 +996,7 @@@ initialize_terminal(void
  }
  
  static void
- restore_terminal(void *aux UNUSED)
+ restore_terminal(void *aux OVS_UNUSED)
  {
      endwin();
  }
@@@ -1248,7 -1247,7 +1248,7 @@@ allocate_message(struct message **msgp
  {
      if (!*msgp) {
          /* Allocate and initialize message. */
 -        *msgp = xcalloc(1, sizeof **msgp);
 +        *msgp = xzalloc(sizeof **msgp);
          (*msgp)->index = n_messages;
  
          /* Add to list of messages. */
@@@ -1445,7 -1444,7 +1445,7 @@@ init_reboot_notifier(void
  }
  
  static void
- sigusr1_handler(int signr UNUSED)
+ sigusr1_handler(int signr OVS_UNUSED)
  {
      sigusr1_triggered = true;
  }
@@@ -1875,7 -1874,7 +1875,7 @@@ yesno(const char *title, bool def
  }
  
  static void
- cmd_show_version(const struct dict *dict UNUSED)
+ cmd_show_version(const struct dict *dict OVS_UNUSED)
  {
      show_string(VERSION BUILDNR);
  }
@@@ -1896,7 -1895,7 +1896,7 @@@ cmd_browse_status(const struct dict *di
  }
  
  static void
- cmd_shell(const struct dict *dict UNUSED)
+ cmd_shell(const struct dict *dict OVS_UNUSED)
  {
      const char *home;
  
  }
  
  static void
- cmd_show_motto(const struct dict *dict UNUSED)
+ cmd_show_motto(const struct dict *dict OVS_UNUSED)
  {
      show_string("\"Just Add Ice\"");
  }
@@@ -2080,7 -2079,7 +2080,7 @@@ save_config(const struct svec *settings
      }
  
      svec_init(&argv);
 -    svec_add(&argv, "/usr/share/openvswitch/commands/reconfigure");
 +    svec_add(&argv, "/usr/share/openvswitch-switchui/reconfigure");
      svec_append(&argv, settings);
      svec_terminate(&argv);
      ok = run_and_report_failure(argv.names, "Save failed");
@@@ -2458,7 -2457,7 +2458,7 @@@ abbreviate_netdevs(const struct svec *n
  static void
  choose_netdevs(struct svec *choices)
  {
 -    struct svec netdevs;
 +    struct svec netdevs = SVEC_EMPTY_INITIALIZER;
      struct menu menu;
      size_t i;
  
              continue;
          }
  
 -        retval = netdev_open(name, NETDEV_ETH_TYPE_NONE, &netdev);
 +        retval = netdev_open_default(name, &netdev);
          if (!retval) {
              bool exclude = netdev_get_in4(netdev, NULL, NULL) == 0;
              netdev_close(netdev);
@@@ -2548,7 -2547,7 +2548,7 @@@ disconnected_string(int value
  }
  
  static void
- cmd_configure(const struct dict *dict UNUSED)
+ cmd_configure(const struct dict *dict OVS_UNUSED)
  {
      bool debug_mode = dict_get_bool(dict, "debug", false);
      struct dict config_dict;
  }
  
  static void
- cmd_set_up_pki(const struct dict *dict UNUSED)
+ cmd_set_up_pki(const struct dict *dict OVS_UNUSED)
  {
      static const char def_privkey_file[]
          = "/etc/openflow-switch/of0-privkey.pem";
diff --combined extras/ezio/tty.c
@@@ -263,7 -263,7 +263,7 @@@ tty_set_raw_mode(int fd, speed_t speed
              return errno;
          }
          s->tios = tios;
 -        fatal_signal_add_hook(restore_termios, s, true);
 +        fatal_signal_add_hook(restore_termios, NULL, s, true);
  
          tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
                            | INLCR | IGNCR | ICRNL | IXON);
@@@ -374,7 -374,9 +374,9 @@@ tty_fork_child(int master_fd, char *arg
  }
  
  int
- tty_set_window_size(int fd UNUSED, int rows UNUSED, int columns UNUSED)
+ tty_set_window_size(int fd OVS_UNUSED,
+                     int rows OVS_UNUSED,
+                     int columns OVS_UNUSED)
  {
  #ifdef TIOCGWINSZ
      struct winsize win;
diff --combined lib/command-line.c
@@@ -1,5 -1,5 +1,5 @@@
  /*
 - * Copyright (c) 2008 Nicira Networks.
 + * Copyright (c) 2008, 2009, 2010 Nicira Networks.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
@@@ -18,8 -18,8 +18,8 @@@
  #include "command-line.h"
  #include <getopt.h>
  #include <limits.h>
 +#include <stdlib.h>
  #include "util.h"
 -#include "vlog.h"
  
  /* Given the GNU-style long options in 'options', returns a string that may be
   * passed to getopt() with the corresponding short options.  The caller is
@@@ -47,150 -47,3 +47,150 @@@ long_options_to_short_options(const str
      return xstrdup(short_options);
  }
  
- proctitle_init(int argc UNUSED, char **argv UNUSED)
 +/* Runs the command designated by argv[0] within the command table specified by
 + * 'commands', which must be terminated by a command whose 'name' member is a
 + * null pointer.
 + *
 + * Command-line options should be stripped off, so that a typical invocation
 + * looks like "run_command(argc - optind, argv + optind, my_commands);". */
 +void
 +run_command(int argc, char *argv[], const struct command commands[])
 +{
 +    const struct command *p;
 +
 +    if (argc < 1) {
 +        ovs_fatal(0, "missing command name; use --help for help");
 +    }
 +
 +    for (p = commands; p->name != NULL; p++) {
 +        if (!strcmp(p->name, argv[0])) {
 +            int n_arg = argc - 1;
 +            if (n_arg < p->min_args) {
 +                ovs_fatal(0, "'%s' command requires at least %d arguments",
 +                          p->name, p->min_args);
 +            } else if (n_arg > p->max_args) {
 +                ovs_fatal(0, "'%s' command takes at most %d arguments",
 +                          p->name, p->max_args);
 +            } else {
 +                p->handler(argc, argv);
 +                if (ferror(stdout)) {
 +                    ovs_fatal(0, "write to stdout failed");
 +                }
 +                if (ferror(stderr)) {
 +                    ovs_fatal(0, "write to stderr failed");
 +                }
 +                return;
 +            }
 +        }
 +    }
 +
 +    ovs_fatal(0, "unknown command '%s'; use --help for help", argv[0]);
 +}
 +\f
 +/* Process title. */
 +
 +#ifdef __linux__
 +static char *argv_start;       /* Start of command-line arguments in memory. */
 +static size_t argv_size;       /* Number of bytes of command-line arguments. */
 +static char *saved_proctitle;  /* Saved command-line arguments. */
 +
 +/* Prepares the process so that proctitle_set() can later succeed.
 + *
 + * This modifies the argv[] array so that it no longer points into the memory
 + * that it originally does.  Later, proctitle_set() might overwrite that
 + * memory.  That means that this function should be called before anything else
 + * that accesses the process's argv[] array.  Ideally, it should be called
 + * before anything else, period, at the very beginning of program
 + * execution.  */
 +void
 +proctitle_init(int argc, char **argv)
 +{
 +    int i;
 +
 +    if (!argc || !argv[0]) {
 +        /* This situation should never occur, but... */
 +        return;
 +    }
 +
 +    /* Specialized version of first loop iteration below. */
 +    argv_start = argv[0];
 +    argv_size = strlen(argv[0]) + 1;
 +    argv[0] = xstrdup(argv[0]);
 +
 +    for (i = 1; i < argc; i++) {
 +        size_t size = strlen(argv[i]) + 1;
 +
 +        /* Add (argv[i], strlen(argv[i])+1) to (argv_start, argv_size). */
 +        if (argv[i] + size == argv_start) {
 +            /* Arguments grow downward in memory. */
 +            argv_start -= size;
 +            argv_size += size;
 +        } else if (argv[i] == argv_start + argv_size) {
 +            /* Arguments grow upward in memory. */
 +            argv_size += size;
 +        } else {
 +            /* Arguments not contiguous.  (Is this really Linux?) */
 +        }
 +
 +        /* Copy out the old argument so we can reuse the space. */
 +        argv[i] = xstrdup(argv[i]);
 +    }
 +}
 +
 +/* Changes the name of the process, as shown by "ps", to 'format', which is
 + * formatted as if by printf(). */
 +void
 +proctitle_set(const char *format, ...)
 +{
 +    va_list args;
 +    int n;
 +
 +    if (!argv_start || argv_size < 8) {
 +        return;
 +    }
 +
 +    if (!saved_proctitle) {
 +        saved_proctitle = xmemdup(argv_start, argv_size);
 +    }
 +
 +    va_start(args, format);
 +    n = vsnprintf(argv_start, argv_size, format, args);
 +    if (n >= argv_size) {
 +        /* The name is too long, so add an ellipsis at the end. */
 +        strcpy(&argv_start[argv_size - 4], "...");
 +    } else {
 +        /* Fill the extra space with null bytes, so that trailing bytes don't
 +         * show up in the command line. */
 +        memset(&argv_start[n], '\0', argv_size - n);
 +    }
 +    va_end(args);
 +}
 +
 +/* Restores the process's original command line, as seen by "ps". */
 +void
 +proctitle_restore(void)
 +{
 +    if (saved_proctitle) {
 +        memcpy(argv_start, saved_proctitle, argv_size);
 +        free(saved_proctitle);
 +        saved_proctitle = NULL;
 +    }
 +}
 +#else  /* !__linux__ */
 +/* Stubs that don't do anything on non-Linux systems. */
 +
 +void
- proctitle_set(const char *format UNUSED, ...)
++proctitle_init(int argc OVS_UNUSED, char **argv OVS_UNUSED)
 +{
 +}
 +
 +void
++proctitle_set(const char *format OVS_UNUSED, ...)
 +{
 +}
 +
 +void
 +proctitle_restore(void)
 +{
 +}
 +#endif  /* !__linux__ */
diff --combined lib/compiler.h
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * Copyright (c) 2008, 2009 Nicira Networks.
+  * Copyright (c) 2008, 2009, 2010 Nicira Networks.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
@@@ -18,7 -18,7 +18,7 @@@
  #define COMPILER_H 1
  
  #define NO_RETURN __attribute__((__noreturn__))
- #define UNUSED __attribute__((__unused__))
+ #define OVS_UNUSED __attribute__((__unused__))
  #define PACKED __attribute__((__packed__))
  #define PRINTF_FORMAT(FMT, ARG1) __attribute__((__format__(printf, FMT, ARG1)))
  #define STRFTIME_FORMAT(FMT) __attribute__((__format__(__strftime__, FMT, 0)))
@@@ -26,6 -26,5 +26,6 @@@
  #define ALWAYS_INLINE __attribute__((always_inline))
  #define likely(x) __builtin_expect((x),1)
  #define unlikely(x) __builtin_expect((x),0)
 +#define WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))
  
  #endif /* compiler.h */
diff --combined lib/coverage.c
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * Copyright (c) 2009 Nicira Networks.
+  * Copyright (c) 2009, 2010 Nicira Networks.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
@@@ -30,8 -30,7 +30,8 @@@
  static unsigned int epoch;
  
  static void
- coverage_unixctl_log(struct unixctl_conn *conn, const char *args UNUSED,
-                      void *aux UNUSED)
 -coverage_unixctl_log(struct unixctl_conn *conn, const char *args OVS_UNUSED)
++coverage_unixctl_log(struct unixctl_conn *conn, const char *args OVS_UNUSED,
++                     void *aux OVS_UNUSED)
  {
      coverage_log(VLL_WARN, false);
      unixctl_command_reply(conn, 200, NULL);
@@@ -40,7 -39,7 +40,7 @@@
  void
  coverage_init(void)
  {
 -    unixctl_command_register("coverage/log", coverage_unixctl_log);
 +    unixctl_command_register("coverage/log", coverage_unixctl_log, NULL);
  }
  
  /* Sorts coverage counters in descending order by count, within equal counts
@@@ -109,7 -108,7 +109,7 @@@ coverage_hit(uint32_t hash
      unsigned int word_mask = 1u << (bit_index % BITS_PER_WORD);
  
      if (hit[word_index] & word_mask) {
--        return true;
++ return true;
      } else {
          hit[word_index] |= word_mask;
          return false;
diff --combined lib/dhcp-client.c
@@@ -151,19 -151,12 +151,19 @@@ dhclient_create(const char *netdev_name
                  void *aux, struct dhclient **cli_)
  {
      struct dhclient *cli;
 +    struct netdev_options netdev_options;
      struct netdev *netdev;
      int error;
  
      *cli_ = NULL;
  
 -    error = netdev_open(netdev_name, ETH_TYPE_IP, &netdev);
 +    memset(&netdev_options, 0, sizeof netdev_options);
 +    netdev_options.name = netdev_name;
 +    netdev_options.ethertype = ETH_TYPE_IP;
 +    netdev_options.may_create = true;
 +    netdev_options.may_open = true;
 +
 +    error = netdev_open(&netdev_options, &netdev);
      /* XXX install socket filter to catch only DHCP packets. */
      if (error) {
          VLOG_ERR("could not open %s network device: %s",
          return error;
      }
  
 -    cli = xcalloc(1, sizeof *cli);
 +    cli = xzalloc(sizeof *cli);
      cli->modify_request = modify_request;
      cli->validate_offer = validate_offer;
      cli->aux = aux;
@@@ -775,7 -768,7 +775,7 @@@ dhclient_run_REBINDING(struct dhclient 
  }
  
  static void
- dhclient_run_RELEASED(struct dhclient *cli UNUSED)
+ dhclient_run_RELEASED(struct dhclient *cli OVS_UNUSED)
  {
      /* Nothing to do. */
  }
diff --combined lib/dhcp.c
@@@ -497,7 -497,7 +497,7 @@@ dhcp_option_equals(const struct dhcp_op
  {
      return ((a->data != NULL) == (b->data != NULL)
              && a->n == b->n
 -            && !memcmp(a->data, b->data, a->n));
 +            && (!a->data || !memcmp(a->data, b->data, a->n)));
  }
  
  /* Replaces 'ds' by a string representation of 'msg'.  If 'multiline' is
@@@ -674,7 -674,7 +674,7 @@@ dhcp_parse(struct dhcp_msg *msg, const 
      msg->giaddr = dhcp->giaddr;
      memcpy(msg->chaddr, dhcp->chaddr, ETH_ADDR_LEN);
  
-     cookie = ofpbuf_try_pull(&b, sizeof cookie);
+     cookie = ofpbuf_try_pull(&b, sizeof *cookie);
      if (cookie) {
          if (ntohl(*cookie) == DHCP_OPTS_COOKIE) {
              uint8_t overload;
diff --combined lib/dpif-linux.c
@@@ -95,10 -95,10 +95,10 @@@ dpif_linux_enumerate(struct svec *all_d
          int retval;
  
          sprintf(devname, "dp%d", i);
 -        retval = dpif_open(devname, &dpif);
 +        retval = dpif_open(devname, "system", &dpif);
          if (!retval) {
              svec_add(all_dps, devname);
 -            dpif_close(dpif);
 +            dpif_uninit(dpif, true);
          } else if (retval != ENODEV && !error) {
              error = retval;
          }
  }
  
  static int
- dpif_linux_open(const char *name, const char *type UNUSED, bool create,
 -dpif_linux_open(const char *name OVS_UNUSED, char *suffix, bool create,
++dpif_linux_open(const char *name, const char *type OVS_UNUSED, bool create,
                  struct dpif **dpifp)
  {
      int minor;
              && isdigit((unsigned char)name[2]) ? atoi(name + 2) : -1;
      if (create) {
          if (minor >= 0) {
 -            return create_minor(suffix, minor, dpifp);
 +            return create_minor(name, minor, dpifp);
          } else {
              /* Scan for unused minor number. */
              for (minor = 0; minor < ODP_MAX; minor++) {
 -                int error = create_minor(suffix, minor, dpifp);
 +                int error = create_minor(name, minor, dpifp);
                  if (error != EBUSY) {
                      return error;
                  }
          int error;
  
          if (minor < 0) {
 -            error = lookup_minor(suffix, &minor);
 +            error = lookup_minor(name, &minor);
              if (error) {
                  return error;
              }
                  VLOG_WARN("%s: probe returned unexpected error: %s",
                            dpif_name(*dpifp), strerror(error));
              }
 -            dpif_close(*dpifp);
 +            dpif_uninit(*dpifp, true);
              return error;
          }
  
@@@ -188,7 -188,7 +188,7 @@@ dpif_linux_get_all_names(const struct d
  }
  
  static int
 -dpif_linux_delete(struct dpif *dpif_)
 +dpif_linux_destroy(struct dpif *dpif_)
  {
      return do_ioctl(dpif_, ODP_DP_DESTROY, NULL);
  }
@@@ -460,14 -460,15 +460,14 @@@ dpif_linux_recv_wait(struct dpif *dpif_
  }
  
  const struct dpif_class dpif_linux_class = {
 -    "",                         /* This is the default class. */
 -    "linux",
 +    "system",
      NULL,
      NULL,
      dpif_linux_enumerate,
      dpif_linux_open,
      dpif_linux_close,
      dpif_linux_get_all_names,
 -    dpif_linux_delete,
 +    dpif_linux_destroy,
      dpif_linux_get_stats,
      dpif_linux_get_drop_frags,
      dpif_linux_set_drop_frags,
@@@ -685,11 -686,11 +685,11 @@@ static in
  finish_open(struct dpif *dpif_, const char *local_ifname)
  {
      struct dpif_linux *dpif = dpif_linux_cast(dpif_);
 -    dpif->local_ifname = strdup(local_ifname);
 +    dpif->local_ifname = xstrdup(local_ifname);
      dpif->local_ifindex = if_nametoindex(local_ifname);
      if (!dpif->local_ifindex) {
          int error = errno;
 -        dpif_close(dpif_);
 +        dpif_uninit(dpif_, true);
          VLOG_WARN("could not get ifindex of %s device: %s",
                    local_ifname, strerror(errno));
          return error;
@@@ -706,7 -707,7 +706,7 @@@ create_minor(const char *name, int mino
          if (!error) {
              error = finish_open(*dpifp, name);
          } else {
 -            dpif_close(*dpifp);
 +            dpif_uninit(*dpifp, true);
          }
      }
      return error;
diff --combined lib/dpif-netdev.c
@@@ -64,7 -64,7 +64,7 @@@ struct dp_netdev 
      struct list node;
      int dp_idx;
      int open_cnt;
 -    bool deleted;
 +    bool destroyed;
  
      bool drop_frags;            /* Drop all IP fragments, if true. */
      struct ovs_queue queues[N_QUEUES]; /* Messages queued for dpif_recv(). */
@@@ -196,7 -196,7 +196,7 @@@ create_dpif_netdev(struct dp_netdev *dp
  
      dp->open_cnt++;
  
 -    dpname = xasprintf("netdev:dp%d", dp->dp_idx);
 +    dpname = xasprintf("dp%d", dp->dp_idx);
      dpif = xmalloc(sizeof *dpif);
      dpif_init(&dpif->dpif, &dpif_netdev_class, dpname, dp->dp_idx, dp->dp_idx);
      dpif->dp = dp;
@@@ -219,7 -219,7 +219,7 @@@ create_dp_netdev(const char *name, int 
      }
  
      /* Create datapath. */
 -    dp_netdevs[dp_idx] = dp = xcalloc(1, sizeof *dp);
 +    dp_netdevs[dp_idx] = dp = xzalloc(sizeof *dp);
      list_push_back(&dp_netdev_list, &dp->node);
      dp->dp_idx = dp_idx;
      dp->open_cnt = 0;
      error = do_add_port(dp, name, ODP_PORT_INTERNAL, ODPP_LOCAL);
      if (error) {
          dp_netdev_free(dp);
 -        return error;
 +        return ENODEV;
      }
  
      *dpifp = create_dpif_netdev(dp);
  }
  
  static int
- dpif_netdev_open(const char *name, const char *type UNUSED, bool create,
 -dpif_netdev_open(const char *name OVS_UNUSED, char *suffix, bool create,
++dpif_netdev_open(const char *name, const char *type OVS_UNUSED, bool create,
                   struct dpif **dpifp)
  {
      if (create) {
 -        if (find_dp_netdev(suffix)) {
 +        if (find_dp_netdev(name)) {
              return EEXIST;
          } else {
 -            int dp_idx = name_to_dp_idx(suffix);
 +            int dp_idx = name_to_dp_idx(name);
              if (dp_idx >= 0) {
 -                return create_dp_netdev(suffix, dp_idx, dpifp);
 +                return create_dp_netdev(name, dp_idx, dpifp);
              } else {
                  /* Scan for unused dp_idx number. */
                  for (dp_idx = 0; dp_idx < N_DP_NETDEVS; dp_idx++) {
 -                    int error = create_dp_netdev(suffix, dp_idx, dpifp);
 +                    int error = create_dp_netdev(name, dp_idx, dpifp);
                      if (error != EBUSY) {
                          return error;
                      }
              }
          }
      } else {
 -        struct dp_netdev *dp = find_dp_netdev(suffix);
 +        struct dp_netdev *dp = find_dp_netdev(name);
          if (dp) {
              *dpifp = create_dpif_netdev(dp);
              return 0;
@@@ -307,17 -307,17 +307,17 @@@ dpif_netdev_close(struct dpif *dpif
  {
      struct dp_netdev *dp = get_dp_netdev(dpif);
      assert(dp->open_cnt > 0);
 -    if (--dp->open_cnt == 0 && dp->deleted) {
 +    if (--dp->open_cnt == 0 && dp->destroyed) {
          dp_netdev_free(dp);
      }
      free(dpif);
  }
  
  static int
 -dpif_netdev_delete(struct dpif *dpif)
 +dpif_netdev_destroy(struct dpif *dpif)
  {
      struct dp_netdev *dp = get_dp_netdev(dpif);
 -    dp->deleted = true;
 +    dp->destroyed = true;
      return 0;
  }
  
@@@ -363,7 -363,6 +363,7 @@@ do_add_port(struct dp_netdev *dp, cons
  {
      bool internal = (flags & ODP_PORT_INTERNAL) != 0;
      struct dp_netdev_port *port;
 +    struct netdev_options netdev_options;
      struct netdev *netdev;
      int mtu;
      int error;
      /* XXX reject devices already in some dp_netdev. */
  
      /* Open and validate network device. */
 -    if (!internal) {
 -        error = netdev_open(devname, NETDEV_ETH_TYPE_ANY, &netdev);
 +    memset(&netdev_options, 0, sizeof netdev_options);
 +    netdev_options.name = devname;
 +    netdev_options.ethertype = NETDEV_ETH_TYPE_ANY;
 +    netdev_options.may_create = true;
 +    if (internal) {
 +        netdev_options.type = "tap";
      } else {
 -        error = netdev_create(devname, "tap", NULL);
 -        if (!error) {
 -            error = netdev_open(devname, NETDEV_ETH_TYPE_ANY, &netdev);
 -            if (error) {
 -                netdev_destroy(devname);
 -            }
 -        }
 +        netdev_options.may_open = true;
      }
 +
 +    error = netdev_open(&netdev_options, &netdev);
      if (error) {
          return error;
      }
@@@ -488,7 -487,9 +488,7 @@@ do_del_port(struct dp_netdev *dp, uint1
  
      name = xstrdup(netdev_get_name(port->netdev));
      netdev_close(port->netdev);
 -    if (port->internal) {
 -        netdev_destroy(name);
 -    }
 +
      free(name);
      free(port);
  
@@@ -582,7 -583,7 +582,7 @@@ dpif_netdev_port_list(const struct dpi
  }
  
  static int
- dpif_netdev_port_poll(const struct dpif *dpif_, char **devnamep UNUSED)
+ dpif_netdev_port_poll(const struct dpif *dpif_, char **devnamep OVS_UNUSED)
  {
      struct dpif_netdev *dpif = dpif_netdev_cast(dpif_);
      if (dpif->dp_serial != dpif->dp->serial) {
@@@ -803,7 -804,7 +803,7 @@@ add_flow(struct dpif *dpif, struct odp_
      struct dp_netdev_flow *flow;
      int error;
  
 -    flow = xcalloc(1, sizeof *flow);
 +    flow = xzalloc(sizeof *flow);
      flow->key = odp_flow->key;
      flow->key.reserved = 0;
  
@@@ -1303,6 -1304,7 +1303,6 @@@ dp_netdev_execute_actions(struct dp_net
  }
  
  const struct dpif_class dpif_netdev_class = {
 -    "netdev",
      "netdev",
      dp_netdev_run,
      dp_netdev_wait,
      dpif_netdev_open,
      dpif_netdev_close,
      NULL,                       /* get_all_names */
 -    dpif_netdev_delete,
 +    dpif_netdev_destroy,
      dpif_netdev_get_stats,
      dpif_netdev_get_drop_frags,
      dpif_netdev_set_drop_frags,
diff --combined lib/fatal-signal.c
  #include <signal.h>
  #include <stdbool.h>
  #include <stdio.h>
 +#include <stdint.h>
  #include <stdlib.h>
  #include <string.h>
  #include <unistd.h>
 +#include "poll-loop.h"
  #include "shash.h"
 +#include "socket-util.h"
  #include "util.h"
  
  #define THIS_MODULE VLM_fatal_signal
@@@ -40,8 -37,7 +40,8 @@@ static sigset_t fatal_signal_set
  
  /* Hooks to call upon catching a signal */
  struct hook {
 -    void (*func)(void *aux);
 +    void (*hook_cb)(void *aux);
 +    void (*cancel_cb)(void *aux);
      void *aux;
      bool run_at_exit;
  };
  static struct hook hooks[MAX_HOOKS];
  static size_t n_hooks;
  
 -/* Number of nesting signal blockers. */
 -static int block_level = 0;
 +static int signal_fds[2];
 +static volatile sig_atomic_t stored_sig_nr = SIG_ATOMIC_MAX;
  
 -/* Signal mask saved by outermost signal blocker. */
 -static sigset_t saved_signal_mask;
 -
 -/* Disabled by fatal_signal_fork()? */
 -static bool disabled;
 -
 -static void call_sigprocmask(int how, sigset_t* new_set, sigset_t* old_set);
 +static void fatal_signal_init(void);
  static void atexit_handler(void);
  static void call_hooks(int sig_nr);
  
 -/* Registers 'hook' to be called when a process termination signal is raised.
 - * If 'run_at_exit' is true, 'hook' is also called during normal process
 - * termination, e.g. when exit() is called or when main() returns.
 - *
 - * 'func' will be invoked from an asynchronous signal handler, so it must be
 - * written appropriately.  For example, it must not call most C library
 - * functions, including malloc() or free(). */
 -void
 -fatal_signal_add_hook(void (*func)(void *aux), void *aux, bool run_at_exit)
 -{
 -    fatal_signal_block();
 -    assert(n_hooks < MAX_HOOKS);
 -    hooks[n_hooks].func = func;
 -    hooks[n_hooks].aux = aux;
 -    hooks[n_hooks].run_at_exit = run_at_exit;
 -    n_hooks++;
 -    fatal_signal_unblock();
 -}
 -
 -/* Blocks program termination signals until fatal_signal_unblock() is called.
 - * May be called multiple times with nesting; if so, fatal_signal_unblock()
 - * must be called the same number of times to unblock signals.
 - *
 - * This is needed while adjusting a data structure that will be accessed by a
 - * fatal signal hook, so that the hook is not invoked while the data structure
 - * is in an inconsistent state. */
 -void
 -fatal_signal_block(void)
 +static void
 +fatal_signal_init(void)
  {
      static bool inited = false;
 +
      if (!inited) {
          size_t i;
  
          inited = true;
 +
 +        if (pipe(signal_fds)) {
 +            ovs_fatal(errno, "could not create pipe");
 +        }
 +        set_nonblocking(signal_fds[0]);
 +        set_nonblocking(signal_fds[1]);
 +
          sigemptyset(&fatal_signal_set);
          for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) {
              int sig_nr = fatal_signals[i];
          }
          atexit(atexit_handler);
      }
 -
 -    if (++block_level == 1) {
 -        call_sigprocmask(SIG_BLOCK, &fatal_signal_set, &saved_signal_mask);
 -    }
  }
  
 -/* Unblocks program termination signals blocked by fatal_signal_block() is
 - * called.  If multiple calls to fatal_signal_block() are nested,
 - * fatal_signal_unblock() must be called the same number of times to unblock
 - * signals. */
 +/* Registers 'hook_cb' to be called when a process termination signal is
 + * raised.  If 'run_at_exit' is true, 'hook_cb' is also called during normal
 + * process termination, e.g. when exit() is called or when main() returns.
 + *
 + * 'hook_cb' is not called immediately from the signal handler but rather the
 + * next time the poll loop iterates, so it is freed from the usual restrictions
 + * on signal handler functions.
 + *
 + * If the current process forks, fatal_signal_fork() may be called to clear the
 + * parent process's fatal signal hooks, so that 'hook_cb' is only called when
 + * the child terminates, not when the parent does.  When fatal_signal_fork() is
 + * called, it calls the 'cancel_cb' function if it is nonnull, passing 'aux',
 + * to notify that the hook has been canceled.  This allows the hook to free
 + * memory, etc. */
  void
 -fatal_signal_unblock(void)
 +fatal_signal_add_hook(void (*hook_cb)(void *aux), void (*cancel_cb)(void *aux),
 +                      void *aux, bool run_at_exit)
  {
 -    assert(block_level > 0);
 -    if (--block_level == 0) {
 -        call_sigprocmask(SIG_SETMASK, &saved_signal_mask, NULL);
 -    }
 +    fatal_signal_init();
 +
 +    assert(n_hooks < MAX_HOOKS);
 +    hooks[n_hooks].hook_cb = hook_cb;
 +    hooks[n_hooks].cancel_cb = cancel_cb;
 +    hooks[n_hooks].aux = aux;
 +    hooks[n_hooks].run_at_exit = run_at_exit;
 +    n_hooks++;
  }
  
  /* Handles fatal signal number 'sig_nr'.
  void
  fatal_signal_handler(int sig_nr)
  {
 -    call_hooks(sig_nr);
 +    ignore(write(signal_fds[1], "", 1));
 +    stored_sig_nr = sig_nr;
 +}
 +
 +void
 +fatal_signal_run(void)
 +{
 +    int sig_nr = stored_sig_nr;
 +
 +    if (sig_nr != SIG_ATOMIC_MAX) {
 +        call_hooks(sig_nr);
  
 -    /* Re-raise the signal with the default handling so that the program
 -     * termination status reflects that we were killed by this signal */
 -    signal(sig_nr, SIG_DFL);
 -    raise(sig_nr);
 +        /* Re-raise the signal with the default handling so that the program
 +         * termination status reflects that we were killed by this signal */
 +        signal(sig_nr, SIG_DFL);
 +        raise(sig_nr);
 +    }
 +}
 +
 +void
 +fatal_signal_wait(void)
 +{
 +    poll_fd_wait(signal_fds[0], POLLIN);
  }
  
  static void
  atexit_handler(void)
  {
 -    if (!disabled) {
 -        call_hooks(0);
 -    }
 +    call_hooks(0);
  }
  
  static void
@@@ -173,21 -167,15 +173,21 @@@ call_hooks(int sig_nr
          for (i = 0; i < n_hooks; i++) {
              struct hook *h = &hooks[i];
              if (sig_nr || h->run_at_exit) {
 -                h->func(h->aux);
 +                h->hook_cb(h->aux);
              }
          }
      }
  }
  \f
 +/* Files to delete on exit.  (The 'data' member of each node is unused.) */
  static struct shash files = SHASH_INITIALIZER(&files);
  
 +/* Has a hook function been registered with fatal_signal_add_hook() (and not
 + * cleared by fatal_signal_fork())? */
 +static bool added_hook;
 +
  static void unlink_files(void *aux);
 +static void cancel_files(void *aux);
  static void do_unlink_files(void);
  
  /* Registers 'file' to be unlinked when the program terminates via exit() or a
  void
  fatal_signal_add_file_to_unlink(const char *file)
  {
 -    static bool added_hook = false;
      if (!added_hook) {
          added_hook = true;
 -        fatal_signal_add_hook(unlink_files, NULL, true);
 +        fatal_signal_add_hook(unlink_files, cancel_files, NULL, true);
      }
  
 -    fatal_signal_block();
      if (!shash_find(&files, file)) {
          shash_add(&files, file, NULL);
      }
 -    fatal_signal_unblock();
  }
  
  /* Unregisters 'file' from being unlinked when the program terminates via
@@@ -212,10 -203,12 +212,10 @@@ fatal_signal_remove_file_to_unlink(cons
  {
      struct shash_node *node;
  
 -    fatal_signal_block();
      node = shash_find(&files, file);
      if (node) {
          shash_delete(&files, node);
      }
 -    fatal_signal_unblock();
  }
  
  /* Like fatal_signal_remove_file_to_unlink(), but also unlinks 'file'.
@@@ -234,18 -227,17 +234,18 @@@ fatal_signal_unlink_file_now(const cha
  }
  
  static void
- unlink_files(void *aux UNUSED)
+ unlink_files(void *aux OVS_UNUSED)
  {
      do_unlink_files(); 
  }
  
 -/* This is a fatal_signal_add_hook() callback (via unlink_files()).  It will be
 - * invoked from an asynchronous signal handler, so it cannot call most C
 - * library functions (unlink() is an explicit exception, see
 - * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html).
 - * That includes free(), so it doesn't try to free the 'files' data
 - * structure. */
 +static void
- cancel_files(void *aux UNUSED)
++cancel_files(void *aux OVS_UNUSED)
 +{
 +    shash_clear(&files);
 +    added_hook = false;
 +}
 +
  static void
  do_unlink_files(void)
  {
      }
  }
  \f
 -/* Disables the fatal signal hook mechanism.  Following a fork, one of the
 - * resulting processes can call this function to allow it to terminate without
 - * triggering fatal signal processing or removing files.  Fatal signal
 - * processing is still enabled in the other process. */
 +/* Clears all of the fatal signal hooks without executing them.  If any of the
 + * hooks passed a 'cancel_cb' function to fatal_signal_add_hook(), then those
 + * functions will be called, allowing them to free resources, etc.
 + *
 + * Following a fork, one of the resulting processes can call this function to
 + * allow it to terminate without calling the hooks registered before calling
 + * this function.  New hooks registered after calling this function will take
 + * effect normally. */
  void
  fatal_signal_fork(void)
  {
      size_t i;
  
 -    disabled = true;
 -
 -    for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) {
 -        int sig_nr = fatal_signals[i];
 -        if (signal(sig_nr, SIG_DFL) == SIG_IGN) {
 -            signal(sig_nr, SIG_IGN);
 +    for (i = 0; i < n_hooks; i++) {
 +        struct hook *h = &hooks[i];
 +        if (h->cancel_cb) {
 +            h->cancel_cb(h->aux);
          }
      }
 -}
 -\f
 -static void
 -call_sigprocmask(int how, sigset_t* new_set, sigset_t* old_set)
 -{
 -    int error = sigprocmask(how, new_set, old_set);
 -    if (error) {
 -        fprintf(stderr, "sigprocmask: %s\n", strerror(errno));
 +    n_hooks = 0;
 +
 +    /* Raise any signals that we have already received with the default
 +     * handler. */
 +    if (stored_sig_nr != SIG_ATOMIC_MAX) {
 +        raise(stored_sig_nr);
      }
  }
diff --combined lib/learning-switch.c
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * Copyright (c) 2008, 2009 Nicira Networks.
+  * Copyright (c) 2008, 2009, 2010 Nicira Networks.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
@@@ -113,7 -113,7 +113,7 @@@ lswitch_create(struct rconn *rconn, boo
      struct lswitch *sw;
      size_t i;
  
 -    sw = xcalloc(1, sizeof *sw);
 +    sw = xzalloc(sizeof *sw);
      sw->max_idle = max_idle;
      sw->datapath_id = 0;
      sw->last_features_request = time_now() - 1;
@@@ -517,7 -517,8 +517,8 @@@ process_port_status(struct lswitch *sw
  }
  
  static void
- process_phy_port(struct lswitch *sw, struct rconn *rconn UNUSED, void *opp_)
+ process_phy_port(struct lswitch *sw, struct rconn *rconn OVS_UNUSED,
+                  void *opp_)
  {
      const struct ofp_phy_port *opp = opp_;
      uint16_t port_no = ntohs(opp->port_no);
diff --combined lib/netdev-linux.c
@@@ -21,7 -21,6 +21,7 @@@
  #include <arpa/inet.h>
  #include <inttypes.h>
  #include <linux/if_tun.h>
 +#include <linux/ip.h>
  #include <linux/types.h>
  #include <linux/ethtool.h>
  #include <linux/rtnetlink.h>
@@@ -33,7 -32,6 +33,7 @@@
  #include <netpacket/packet.h>
  #include <net/ethernet.h>
  #include <net/if.h>
 +#include <linux/if_tunnel.h>
  #include <net/if_arp.h>
  #include <net/if_packet.h>
  #include <net/route.h>
@@@ -50,7 -48,6 +50,7 @@@
  #include "netlink.h"
  #include "ofpbuf.h"
  #include "openflow/openflow.h"
 +#include "openvswitch/gre.h"
  #include "packets.h"
  #include "poll-loop.h"
  #include "rtnetlink.h"
  #include "shash.h"
  #include "svec.h"
  
 +#ifndef GRE_IOCTL_ONLY
 +#include <linux/if_link.h>
 +#endif
 +
  #define THIS_MODULE VLM_netdev_linux
  #include "vlog.h"
  \f
  #define ADVERTISED_Asym_Pause           (1 << 14)
  #endif
  
 -/* Provider-specific netdev object.  Netdev objects are devices that are
 - * created by the netdev library through a netdev_create() call. */
 -struct netdev_obj_linux {
 -    struct netdev_obj netdev_obj;
 -
 -    int tap_fd;                 /* File descriptor for TAP device. */
 -};
 -
 -struct netdev_linux {
 -    struct netdev netdev;
 -
 -    /* File descriptors.  For ordinary network devices, the two fds below are
 -     * the same; for tap devices, they differ. */
 -    int netdev_fd;              /* Network device. */
 -    int tap_fd;                 /* TAP character device, if any, otherwise the
 -                                 * network device. */
 -
 -    struct netdev_linux_cache *cache;
 -};
 +static struct rtnetlink_notifier netdev_linux_cache_notifier;
 +static int cache_notifier_refcount;
  
  enum {
      VALID_IFINDEX = 1 << 0,
      VALID_IS_INTERNAL = 1 << 6
  };
  
 -/* Cached network device information. */
 -struct netdev_linux_cache {
 +struct tap_state {
 +    int fd;
 +};
 +
 +struct netdev_dev_linux {
 +    struct netdev_dev netdev_dev;
 +
      struct shash_node *shash_node;
 -    unsigned int valid;
 -    int ref_cnt;
 +    unsigned int cache_valid;
  
      int ifindex;
      uint8_t etheraddr[ETH_ADDR_LEN];
      int mtu;
      int carrier;
      bool is_internal;
 +
 +    union {
 +        struct tap_state tap;
 +    } state;
  };
  
 -static struct shash cache_map = SHASH_INITIALIZER(&cache_map);
 -static struct rtnetlink_notifier netdev_linux_cache_notifier;
 +struct netdev_linux {
 +    struct netdev netdev;
 +    int fd;
 +};
  
  /* An AF_INET socket (used for ioctl operations). */
  static int af_inet_sock = -1;
  
 +struct gre_config {
 +    uint32_t local_ip;
 +    uint32_t remote_ip;
 +    uint32_t in_key;
 +    uint32_t out_key;
 +    bool have_in_key;
 +    bool have_out_key;
 +    bool in_csum;
 +    bool out_csum;
 +};
 +
 +static struct {
 +    union {
 +        struct nl_sock *nl_sock;
 +        int ioctl_fd;
 +    };
 +    bool use_ioctl;
 +} gre_descriptors;
 +
  struct netdev_linux_notifier {
      struct netdev_notifier notifier;
      struct list node;
@@@ -150,11 -131,10 +150,11 @@@ static struct rtnetlink_notifier netdev
   * additional log messages. */
  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
  
 -static int netdev_linux_do_ethtool(struct netdev *, struct ethtool_cmd *,
 +static int destroy_gre(const char *name);
 +static int netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *,
                                     int cmd, const char *cmd_name);
 -static int netdev_linux_do_ioctl(const struct netdev *, struct ifreq *,
 -                                 int cmd, const char *cmd_name);
 +static int netdev_linux_do_ioctl(const char *name, struct ifreq *, int cmd,
 +                                 const char *cmd_name);
  static int netdev_linux_get_ipv4(const struct netdev *, struct in_addr *,
                                   int cmd, const char *cmd_name);
  static int get_flags(const struct netdev *, int *flagsp);
@@@ -170,21 -150,17 +170,21 @@@ static int set_etheraddr(const char *ne
  static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats);
  static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats);
  
 -static struct netdev_obj_linux *
 -netdev_obj_linux_cast(const struct netdev_obj *netdev_obj)
 +static struct netdev_dev_linux *
 +netdev_dev_linux_cast(const struct netdev_dev *netdev_dev)
  {
 -    netdev_obj_assert_class(netdev_obj, &netdev_linux_class);
 -    return CONTAINER_OF(netdev_obj, struct netdev_obj_linux, netdev_obj);
 +    const char *type = netdev_dev_get_type(netdev_dev);
 +    assert(!strcmp(type, "system") || !strcmp(type, "tap")
 +            || !strcmp(type, "gre"));
 +    return CONTAINER_OF(netdev_dev, struct netdev_dev_linux, netdev_dev);
  }
  
  static struct netdev_linux *
  netdev_linux_cast(const struct netdev *netdev)
  {
 -    netdev_assert_class(netdev, &netdev_linux_class);
 +    const char *type = netdev_get_type(netdev);
 +    assert(!strcmp(type, "system") || !strcmp(type, "tap")
 +            || !strcmp(type, "gre"));
      return CONTAINER_OF(netdev, struct netdev_linux, netdev);
  }
  
@@@ -216,411 -192,50 +216,411 @@@ netdev_linux_wait(void
  
  static void
  netdev_linux_cache_cb(const struct rtnetlink_change *change,
-                       void *aux UNUSED)
+                       void *aux OVS_UNUSED)
  {
 -    struct netdev_linux_cache *cache;
 +    struct netdev_dev_linux *dev;
      if (change) {
 -        cache = shash_find_data(&cache_map, change->ifname);
 -        if (cache) {
 -            cache->valid = 0;
 +        struct netdev_dev *base_dev = netdev_dev_from_name(change->ifname);
 +        if (base_dev) {
 +            dev = netdev_dev_linux_cast(base_dev);
 +            dev->cache_valid = 0;
          }
      } else {
 +        struct shash device_shash;
          struct shash_node *node;
 -        SHASH_FOR_EACH (node, &cache_map) {
 -            cache = node->data;
 -            cache->valid = 0;
 +
 +        shash_init(&device_shash);
 +        netdev_dev_get_devices(&netdev_linux_class, &device_shash);
 +        SHASH_FOR_EACH (node, &device_shash) {
 +            dev = node->data;
 +            dev->cache_valid = 0;
          }
 +        shash_destroy(&device_shash);
      }
  }
  
 -/* Creates the netdev object of 'type' with 'name'. */
 +/* The arguments are marked as unused to prevent warnings on platforms where
 + * the Netlink interface isn't supported. */
  static int
- setup_gre_netlink(const char *name UNUSED, struct gre_config *config UNUSED,
-                   bool create UNUSED)
 -netdev_linux_create(const char *name, const char *type, 
 -                    const struct shash *args, bool created)
++setup_gre_netlink(const char *name OVS_UNUSED,
++                  struct gre_config *config OVS_UNUSED, bool create OVS_UNUSED)
  {
 -    struct netdev_obj_linux *netdev_obj;
 -    static const char tap_dev[] = "/dev/net/tun";
 +#ifdef GRE_IOCTL_ONLY
 +    return EOPNOTSUPP;
 +#else
 +    int error;
 +    struct ofpbuf request, *reply;
 +    unsigned int nl_flags;
 +    struct ifinfomsg ifinfomsg;
 +    struct nlattr *linkinfo_hdr;
 +    struct nlattr *info_data_hdr;
 +    uint16_t iflags = 0;
 +    uint16_t oflags = 0;
 +    uint8_t pmtudisc = 0;
 +
 +    VLOG_DBG("%s: attempting to create gre device using netlink", name);
 +
 +    if (!gre_descriptors.nl_sock) {
 +        error = nl_sock_create(NETLINK_ROUTE, 0, 0, 0,
 +                               &gre_descriptors.nl_sock);
 +        if (error) {
 +            VLOG_WARN("couldn't create netlink socket: %s", strerror(error));
 +            goto error;
 +        }
 +    }
 +
 +    ofpbuf_init(&request, 0);
 +
 +    nl_flags = NLM_F_REQUEST;
 +    if (create) {
 +        nl_flags |= NLM_F_CREATE|NLM_F_EXCL;
 +    }
 +
 +    /* We over-reserve space, because we do some pointer arithmetic
 +     * and don't want the buffer address shifting under us. */
 +    nl_msg_put_nlmsghdr(&request, gre_descriptors.nl_sock, 2048, RTM_NEWLINK,
 +                        nl_flags);
 +
 +    memset(&ifinfomsg, 0, sizeof ifinfomsg);
 +    ifinfomsg.ifi_family = AF_UNSPEC;
 +    nl_msg_put(&request, &ifinfomsg, sizeof ifinfomsg);
 +
 +    linkinfo_hdr = ofpbuf_tail(&request);
 +    nl_msg_put_unspec(&request, IFLA_LINKINFO, NULL, 0);
 +
 +    nl_msg_put_unspec(&request, IFLA_INFO_KIND, "gretap", 6);
 +
 +    info_data_hdr = ofpbuf_tail(&request);
 +    nl_msg_put_unspec(&request, IFLA_INFO_DATA, NULL, 0);
 +
 +    /* Set flags */
 +    if (config->have_in_key) {
 +        iflags |= GRE_KEY;
 +    }
 +    if (config->have_out_key) {
 +        oflags |= GRE_KEY;
 +    }
 +
 +    if (config->in_csum) {
 +        iflags |= GRE_CSUM;
 +    }
 +    if (config->out_csum) {
 +        oflags |= GRE_CSUM;
 +    }
 +
 +    /* Add options */
 +    nl_msg_put_u32(&request, IFLA_GRE_IKEY, config->in_key);
 +    nl_msg_put_u32(&request, IFLA_GRE_OKEY, config->out_key);
 +    nl_msg_put_u16(&request, IFLA_GRE_IFLAGS, iflags);
 +    nl_msg_put_u16(&request, IFLA_GRE_OFLAGS, oflags);
 +    nl_msg_put_u32(&request, IFLA_GRE_LOCAL, config->local_ip);
 +    nl_msg_put_u32(&request, IFLA_GRE_REMOTE, config->remote_ip);
 +    nl_msg_put_u8(&request, IFLA_GRE_PMTUDISC, pmtudisc);
 +    nl_msg_put_u8(&request, IFLA_GRE_TTL, 0);
 +    nl_msg_put_u8(&request, IFLA_GRE_TOS, 0);
 +
 +    info_data_hdr->nla_len = (char *)ofpbuf_tail(&request)
 +                                - (char *)info_data_hdr;
 +    linkinfo_hdr->nla_len = (char *)ofpbuf_tail(&request)
 +                                - (char *)linkinfo_hdr;
 +
 +    nl_msg_put_string(&request, IFLA_IFNAME, name);
 +
 +    error = nl_sock_transact(gre_descriptors.nl_sock, &request, &reply);
 +    ofpbuf_uninit(&request);
 +    if (error) {
 +        VLOG_WARN("couldn't transact netlink socket: %s", strerror(error));
 +        goto error;
 +    }
 +    ofpbuf_delete(reply);
 +
 +error:
 +    return error;
 +#endif
 +}
 +
 +static int
 +setup_gre_ioctl(const char *name, struct gre_config *config, bool create)
 +{
 +    struct ip_tunnel_parm p;
      struct ifreq ifr;
- check_gre_device_netlink(const char *name UNUSED)
 +
 +    VLOG_DBG("%s: attempting to create gre device using ioctl", name);
 +
 +    memset(&p, 0, sizeof p);
 +
 +    strncpy(p.name, name, IFNAMSIZ);
 +
 +    p.iph.version = 4;
 +    p.iph.ihl = 5;
 +    p.iph.protocol = IPPROTO_GRE;
 +    p.iph.saddr = config->local_ip;
 +    p.iph.daddr = config->remote_ip;
 +
 +    if (config->have_in_key) {
 +        p.i_flags |= GRE_KEY;
 +        p.i_key = config->in_key;
 +    }
 +    if (config->have_out_key) {
 +        p.o_flags |= GRE_KEY;
 +        p.o_key = config->out_key;
 +    }
 +
 +    if (config->in_csum) {
 +        p.i_flags |= GRE_CSUM;
 +    }
 +    if (config->out_csum) {
 +        p.o_flags |= GRE_CSUM;
 +    }
 +
 +    strncpy(ifr.ifr_name, create ? GRE_IOCTL_DEVICE : name, IFNAMSIZ);
 +    ifr.ifr_ifru.ifru_data = (void *)&p;
 +
 +    if (!gre_descriptors.ioctl_fd) {
 +        gre_descriptors.ioctl_fd = socket(AF_INET, SOCK_DGRAM, 0);
 +        if (gre_descriptors.ioctl_fd < 0) {
 +            VLOG_WARN("couldn't create gre ioctl socket: %s", strerror(errno));
 +            gre_descriptors.ioctl_fd = 0;
 +            return errno;
 +        }
 +    }
 +
 +    if (ioctl(gre_descriptors.ioctl_fd, create ? SIOCADDGRETAP : SIOCCHGGRETAP,
 +              &ifr) < 0) {
 +        VLOG_WARN("couldn't do gre ioctl: %s", strerror(errno));
 +        return errno;
 +    }
 +
 +    return 0;
 +}
 +
 +/* The arguments are marked as unused to prevent warnings on platforms where
 + * the Netlink interface isn't supported. */
 +static bool
- netdev_linux_create_system(const char *name, const char *type UNUSED,
++check_gre_device_netlink(const char *name OVS_UNUSED)
 +{
 +#ifdef GRE_IOCTL_ONLY
 +    return false;
 +#else
 +    static const struct nl_policy getlink_policy[] = {
 +        [IFLA_LINKINFO] = { .type = NL_A_NESTED, .optional = false },
 +    };
 +
 +    static const struct nl_policy linkinfo_policy[] = {
 +        [IFLA_INFO_KIND] = { .type = NL_A_STRING, .optional = false },
 +    };
 +
 +    int error;
 +    bool ret = false;
 +    struct ofpbuf request, *reply;
 +    struct ifinfomsg ifinfomsg;
 +    struct nlattr *getlink_attrs[ARRAY_SIZE(getlink_policy)];
 +    struct nlattr *linkinfo_attrs[ARRAY_SIZE(linkinfo_policy)];
 +    struct ofpbuf linkinfo;
 +    const char *device_kind;
 +
 +    ofpbuf_init(&request, 0);
 +
 +    nl_msg_put_nlmsghdr(&request, gre_descriptors.nl_sock,
 +                        NLMSG_LENGTH(sizeof ifinfomsg), RTM_GETLINK,
 +                        NLM_F_REQUEST);
 +
 +    memset(&ifinfomsg, 0, sizeof ifinfomsg);
 +    ifinfomsg.ifi_family = AF_UNSPEC;
 +    ifinfomsg.ifi_index =  do_get_ifindex(name);
 +    nl_msg_put(&request, &ifinfomsg, sizeof ifinfomsg);
 +
 +    error = nl_sock_transact(gre_descriptors.nl_sock, &request, &reply);
 +    ofpbuf_uninit(&request);
 +    if (error) {
 +        VLOG_WARN("couldn't transact netlink socket: %s", strerror(error));
 +        return false;
 +    }
 +
 +    if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
 +                         getlink_policy, getlink_attrs,
 +                         ARRAY_SIZE(getlink_policy))) {
 +        VLOG_WARN("received bad rtnl message (getlink policy)");
 +        goto error;
 +    }
 +
 +    linkinfo.data = (void *)nl_attr_get(getlink_attrs[IFLA_LINKINFO]);
 +    linkinfo.size = nl_attr_get_size(getlink_attrs[IFLA_LINKINFO]);
 +    if (!nl_policy_parse(&linkinfo, 0, linkinfo_policy,
 +                        linkinfo_attrs, ARRAY_SIZE(linkinfo_policy))) {
 +        VLOG_WARN("received bad rtnl message (linkinfo policy)");
 +        goto error;
 +    }
 +
 +    device_kind = nl_attr_get_string(linkinfo_attrs[IFLA_INFO_KIND]);
 +    ret = !strcmp(device_kind, "gretap");
 +
 +error:
 +    ofpbuf_delete(reply);
 +    return ret;
 +#endif
 +}
 +
 +static bool
 +check_gre_device_ioctl(const char *name)
 +{
 +    struct ethtool_drvinfo drvinfo;
 +    int error;
 +
 +    memset(&drvinfo, 0, sizeof drvinfo);
 +    error = netdev_linux_do_ethtool(name, (struct ethtool_cmd *)&drvinfo,
 +                                    ETHTOOL_GDRVINFO, "ETHTOOL_GDRVINFO");
 +
 +    return !error && !strcmp(drvinfo.driver, "ip_gre")
 +           && !strcmp(drvinfo.bus_info, "gretap");
 +}
 +
 +static int
 +setup_gre(const char *name, const struct shash *args, bool create)
 +{
 +    int error;
 +    struct in_addr in_addr;
 +    struct shash_node *node;
 +    struct gre_config config;
 +
 +    memset(&config, 0, sizeof config);
 +    config.in_csum = true;
 +    config.out_csum = true;
 +
 +    SHASH_FOR_EACH (node, args) {
 +        if (!strcmp(node->name, "remote_ip")) {
 +            if (lookup_ip(node->data, &in_addr)) {
 +                VLOG_WARN("bad 'remote_ip' for gre device %s ", name);
 +            } else {
 +                config.remote_ip = in_addr.s_addr;
 +            }
 +        } else if (!strcmp(node->name, "local_ip")) {
 +            if (lookup_ip(node->data, &in_addr)) {
 +                VLOG_WARN("bad 'local_ip' for gre device %s ", name);
 +            } else {
 +                config.local_ip = in_addr.s_addr;
 +            }
 +        } else if (!strcmp(node->name, "key")) {
 +            config.have_in_key = true;
 +            config.have_out_key = true;
 +            config.in_key = htonl(atoi(node->data));
 +            config.out_key = htonl(atoi(node->data));
 +        } else if (!strcmp(node->name, "in_key")) {
 +            config.have_in_key = true;
 +            config.in_key = htonl(atoi(node->data));
 +        } else if (!strcmp(node->name, "out_key")) {
 +            config.have_out_key = true;
 +            config.out_key = htonl(atoi(node->data));
 +        } else if (!strcmp(node->name, "csum")) {
 +            if (!strcmp(node->data, "false")) {
 +                config.in_csum = false;
 +                config.out_csum = false;
 +            }
 +        } else {
 +            VLOG_WARN("unknown gre argument '%s'", node->name);
 +        }
 +    }
 +
 +    if (!config.remote_ip) {
 +        VLOG_WARN("gre type requires valid 'remote_ip' argument");
 +        error = EINVAL;
 +        goto error;
 +    }
 +
 +    if (!gre_descriptors.use_ioctl) {
 +        error = setup_gre_netlink(name, &config, create);
 +        if (error == EOPNOTSUPP) {
 +            gre_descriptors.use_ioctl = true;
 +        }
 +    }
 +    if (gre_descriptors.use_ioctl) {
 +        error = setup_gre_ioctl(name, &config, create);
 +    }
 +
 +    if (create && error == EEXIST) {
 +        bool gre_device;
 +
 +        if (gre_descriptors.use_ioctl) {
 +            gre_device = check_gre_device_ioctl(name);
 +        } else {
 +            gre_device = check_gre_device_netlink(name);
 +        }
 +
 +        if (!gre_device) {
 +            goto error;
 +        }
 +
 +        VLOG_WARN("replacing existing gre device %s", name);
 +        error = destroy_gre(name);
 +        if (error) {
 +            goto error;
 +        }
 +
 +        if (gre_descriptors.use_ioctl) {
 +            error = setup_gre_ioctl(name, &config, create);
 +        } else {
 +            error = setup_gre_netlink(name, &config, create);
 +        }
 +    }
 +
 +error:
 +    return error;
 +}
 +
 +/* Creates the netdev device of 'type' with 'name'. */
 +static int
++netdev_linux_create_system(const char *name, const char *type OVS_UNUSED,
 +                    const struct shash *args, struct netdev_dev **netdev_devp)
 +{
 +    struct netdev_dev_linux *netdev_dev;
      int error;
  
      if (!shash_is_empty(args)) {
 -        VLOG_WARN("arguments for %s devices should be empty", type);
 +        VLOG_WARN("%s: arguments for system devices should be empty", name);
      }
  
 -    /* Create the name binding in the netdev library for this object. */
 -    netdev_obj = xcalloc(1, sizeof *netdev_obj);
 -    netdev_obj_init(&netdev_obj->netdev_obj, name, &netdev_linux_class,
 -                    created);
 -    netdev_obj->tap_fd = -1;
 +    if (!cache_notifier_refcount) {
 +        error = rtnetlink_notifier_register(&netdev_linux_cache_notifier,
 +                                            netdev_linux_cache_cb, NULL);
 +        if (error) {
 +            return error;
 +        }
 +    }
 +    cache_notifier_refcount++;
  
 -    if (strcmp(type, "tap")) {
 -        return 0;
 +    netdev_dev = xzalloc(sizeof *netdev_dev);
 +    netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_linux_class);
 +
 +    *netdev_devp = &netdev_dev->netdev_dev;
 +    return 0;
 +}
 +
 +/* For most types of netdevs we open the device for each call of
 + * netdev_open().  However, this is not the case with tap devices,
 + * since it is only possible to open the device once.  In this
 + * situation we share a single file descriptor, and consequently
 + * buffers, across all readers.  Therefore once data is read it will
 + * be unavailable to other reads for tap devices. */
 +static int
- netdev_linux_create_tap(const char *name, const char *type UNUSED,
++netdev_linux_create_tap(const char *name, const char *type OVS_UNUSED,
 +                    const struct shash *args, struct netdev_dev **netdev_devp)
 +{
 +    struct netdev_dev_linux *netdev_dev;
 +    struct tap_state *state;
 +    static const char tap_dev[] = "/dev/net/tun";
 +    struct ifreq ifr;
 +    int error;
 +
 +    if (!shash_is_empty(args)) {
 +        VLOG_WARN("%s: arguments for TAP devices should be empty", name);
      }
  
 +    netdev_dev = xzalloc(sizeof *netdev_dev);
 +    state = &netdev_dev->state.tap;
 +
      /* Open tap device. */
 -    netdev_obj->tap_fd = open(tap_dev, O_RDWR);
 -    if (netdev_obj->tap_fd < 0) {
 +    state->fd = open(tap_dev, O_RDWR);
 +    if (state->fd < 0) {
          error = errno;
          VLOG_WARN("opening \"%s\" failed: %s", tap_dev, strerror(error));
          goto error;
      /* Create tap device. */
      ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
      strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
 -    if (ioctl(netdev_obj->tap_fd, TUNSETIFF, &ifr) == -1) {
 +    if (ioctl(state->fd, TUNSETIFF, &ifr) == -1) {
          VLOG_WARN("%s: creating tap device failed: %s", name,
                    strerror(errno));
          error = errno;
      }
  
      /* Make non-blocking. */
 -    error = set_nonblocking(netdev_obj->tap_fd);
 +    error = set_nonblocking(state->fd);
      if (error) {
          goto error;
      }
  
 +    netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_tap_class);
 +    *netdev_devp = &netdev_dev->netdev_dev;
      return 0;
  
  error:
 -    netdev_destroy(name);
 +    free(netdev_dev);
      return error;
  }
  
 -/* Destroys the netdev object 'netdev_obj_'. */
 -static void
 -netdev_linux_destroy(struct netdev_obj *netdev_obj_)
 +static int
 +if_up(const char *name)
  {
 -    struct netdev_obj_linux *netdev_obj = netdev_obj_linux_cast(netdev_obj_);
 +    struct ifreq ifr;
 +
 +    strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
 +    ifr.ifr_flags = IFF_UP;
  
 -    if (netdev_obj->tap_fd >= 0) {
 -        close(netdev_obj->tap_fd);
 +    if (ioctl(af_inet_sock, SIOCSIFFLAGS, &ifr) == -1) {
 +        VLOG_DBG_RL(&rl, "%s: failed to bring device up: %s",
 +                    name, strerror(errno));
 +        return errno;
      }
 -    free(netdev_obj);
  
 -    return;
 +    return 0;
  }
  
  static int
- netdev_linux_create_gre(const char *name, const char *type UNUSED,
 -netdev_linux_open(const char *name, int ethertype, struct netdev **netdevp)
++netdev_linux_create_gre(const char *name, const char *type OVS_UNUSED,
 +                    const struct shash *args, struct netdev_dev **netdev_devp)
  {
 -    struct netdev_linux *netdev;
 -    enum netdev_flags flags;
 +    struct netdev_dev_linux *netdev_dev;
      int error;
  
 -    /* Allocate network device. */
 -    netdev = xcalloc(1, sizeof *netdev);
 -    netdev_init(&netdev->netdev, name, &netdev_linux_class);
 -    netdev->netdev_fd = -1;
 -    netdev->tap_fd = -1;
 -    netdev->cache = shash_find_data(&cache_map, name);
 -    if (!netdev->cache) {
 -        if (shash_is_empty(&cache_map)) {
 -            int error = rtnetlink_notifier_register(
 -                &netdev_linux_cache_notifier, netdev_linux_cache_cb, NULL);
 -            if (error) {
 -                netdev_close(&netdev->netdev);
 -                return error;
 -            }
 -        }
 -        netdev->cache = xmalloc(sizeof *netdev->cache);
 -        netdev->cache->shash_node = shash_add(&cache_map, name,
 -                                              netdev->cache);
 -        netdev->cache->valid = 0;
 -        netdev->cache->ref_cnt = 0;
 +    netdev_dev = xzalloc(sizeof *netdev_dev);
 +
 +    error = setup_gre(name, args, true);
 +    if (error) {
 +        goto error;
      }
 -    netdev->cache->ref_cnt++;
  
 -    if (!strcmp(netdev_get_type(&netdev->netdev), "tap")) {
 -        static const char tap_dev[] = "/dev/net/tun";
 -        struct ifreq ifr;
 +    error = if_up(name);
 +    if (error) {
 +        goto error;
 +    }
  
 -        /* Open tap device. */
 -        netdev->tap_fd = open(tap_dev, O_RDWR);
 -        if (netdev->tap_fd < 0) {
 -            error = errno;
 -            VLOG_WARN("opening \"%s\" failed: %s", tap_dev, strerror(error));
 -            goto error;
 -        }
 +    netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_gre_class);
 +    *netdev_devp = &netdev_dev->netdev_dev;
 +    return 0;
  
 -        /* Create tap device. */
 -        ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
 -        strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
 -        if (ioctl(netdev->tap_fd, TUNSETIFF, &ifr) == -1) {
 -            VLOG_WARN("%s: creating tap device failed: %s", name,
 -                      strerror(errno));
 -            error = errno;
 -            goto error;
 -        }
 +error:
 +    free(netdev_dev);
 +    return error;
 +}
  
 -        /* Make non-blocking. */
 -        error = set_nonblocking(netdev->tap_fd);
 -        if (error) {
 -            goto error;
 +static int
 +netdev_linux_reconfigure_gre(struct netdev_dev *netdev_dev_,
 +                             const struct shash *args)
 +{
 +    const char *name = netdev_dev_get_name(netdev_dev_);
 +
 +    return setup_gre(name, args, false);
 +}
 +
 +/* The arguments are marked as unused to prevent warnings on platforms where
 + * the Netlink interface isn't supported. */
 +static int
- destroy_gre_netlink(const char *name UNUSED)
++destroy_gre_netlink(const char *name OVS_UNUSED)
 +{
 +#ifdef GRE_IOCTL_ONLY
 +    return EOPNOTSUPP;
 +#else
 +    int error;
 +    struct ofpbuf request, *reply;
 +    struct ifinfomsg ifinfomsg;
 +    int ifindex;
 +
 +    ofpbuf_init(&request, 0);
 +    
 +    nl_msg_put_nlmsghdr(&request, gre_descriptors.nl_sock, 0, RTM_DELLINK,
 +                        NLM_F_REQUEST);
 +
 +    memset(&ifinfomsg, 0, sizeof ifinfomsg);
 +    ifinfomsg.ifi_family = AF_UNSPEC;
 +    nl_msg_put(&request, &ifinfomsg, sizeof ifinfomsg);
 +
 +    ifindex = do_get_ifindex(name);
 +    nl_msg_put_u32(&request, IFLA_LINK, ifindex);
 +
 +    nl_msg_put_string(&request, IFLA_IFNAME, name);
 +
 +    error = nl_sock_transact(gre_descriptors.nl_sock, &request, &reply);
 +    ofpbuf_uninit(&request);
 +    if (error) {
 +        VLOG_WARN("couldn't transact netlink socket: %s", strerror(error));
 +        goto error;
 +    }
 +    ofpbuf_delete(reply);
 +
 +error:
 +    return 0;
 +#endif
 +}
 +
 +static int
 +destroy_gre_ioctl(const char *name)
 +{
 +    struct ip_tunnel_parm p;
 +    struct ifreq ifr;
 +
 +    memset(&p, 0, sizeof p);
 +    strncpy(p.name, name, IFNAMSIZ);
 +
 +    strncpy(ifr.ifr_name, name, IFNAMSIZ);
 +    ifr.ifr_ifru.ifru_data = (void *)&p;
 +
 +    if (ioctl(gre_descriptors.ioctl_fd, SIOCDELGRETAP, &ifr) < 0) {
 +        VLOG_WARN("couldn't do gre ioctl: %s\n", strerror(errno));
 +        return errno;
 +    }
 +
 +    return 0;
 +}
 +
 +static void
 +destroy_tap(struct netdev_dev_linux *netdev_dev)
 +{
 +    struct tap_state *state = &netdev_dev->state.tap;
 +
 +    if (state->fd >= 0) {
 +        close(state->fd);
 +    }
 +}
 +
 +static int
 +destroy_gre(const char *name)
 +{
 +    if (gre_descriptors.use_ioctl) {
 +        return destroy_gre_ioctl(name);
 +    } else {
 +        return destroy_gre_netlink(name);
 +    }
 +}
 +
 +/* Destroys the netdev device 'netdev_dev_'. */
 +static void
 +netdev_linux_destroy(struct netdev_dev *netdev_dev_)
 +{
 +    struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_dev_);
 +    const char *type = netdev_dev_get_type(netdev_dev_);
 +
 +    if (!strcmp(type, "system")) {
 +        cache_notifier_refcount--;
 +
 +        if (!cache_notifier_refcount) {
 +            rtnetlink_notifier_unregister(&netdev_linux_cache_notifier);
          }
 +    } else if (!strcmp(type, "tap")) {
 +        destroy_tap(netdev_dev);
 +    } else if (!strcmp(type, "gre")) {
 +        destroy_gre(netdev_dev_get_name(&netdev_dev->netdev_dev));
      }
  
 +    free(netdev_dev_);
 +}
 +
 +static int
 +netdev_linux_open(struct netdev_dev *netdev_dev_, int ethertype,
 +                  struct netdev **netdevp)
 +{
 +    struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_dev_);
 +    struct netdev_linux *netdev;
 +    enum netdev_flags flags;
 +    int error;
 +
 +    /* Allocate network device. */
 +    netdev = xzalloc(sizeof *netdev);
 +    netdev->fd = -1;
 +    netdev_init(&netdev->netdev, netdev_dev_);
 +
      error = netdev_get_flags(&netdev->netdev, &flags);
      if (error == ENODEV) {
          goto error;
      }
  
 -    if (netdev->tap_fd >= 0 || ethertype != NETDEV_ETH_TYPE_NONE) {
 +    if (!strcmp(netdev_dev_get_type(netdev_dev_), "tap")) {
 +        netdev->fd = netdev_dev->state.tap.fd;
 +    } else if (ethertype != NETDEV_ETH_TYPE_NONE) {
          struct sockaddr_ll sll;
          int protocol;
          int ifindex;
          protocol = (ethertype == NETDEV_ETH_TYPE_ANY ? ETH_P_ALL
                      : ethertype == NETDEV_ETH_TYPE_802_2 ? ETH_P_802_2
                      : ethertype);
 -        netdev->netdev_fd = socket(PF_PACKET, SOCK_RAW, htons(protocol));
 -        if (netdev->netdev_fd < 0) {
 +        netdev->fd = socket(PF_PACKET, SOCK_RAW, htons(protocol));
 +        if (netdev->fd < 0) {
              error = errno;
              goto error;
          }
 -        if (netdev->tap_fd < 0) {
 -            netdev->tap_fd = netdev->netdev_fd;
 -        }
  
          /* Set non-blocking mode. */
 -        error = set_nonblocking(netdev->netdev_fd);
 +        error = set_nonblocking(netdev->fd);
          if (error) {
              goto error;
          }
          memset(&sll, 0, sizeof sll);
          sll.sll_family = AF_PACKET;
          sll.sll_ifindex = ifindex;
 -        if (bind(netdev->netdev_fd,
 +        if (bind(netdev->fd,
                   (struct sockaddr *) &sll, sizeof sll) < 0) {
              error = errno;
 -            VLOG_ERR("bind to %s failed: %s", name, strerror(error));
 +            VLOG_ERR("bind to %s failed: %s", netdev_dev_get_name(netdev_dev_),
 +                     strerror(error));
              goto error;
          }
  
           * packets of the requested type on all system interfaces.  We do not
           * want to receive that data, but there is no way to avoid it.  So we
           * must now drain out the receive queue. */
 -        error = drain_rcvbuf(netdev->netdev_fd);
 +        error = drain_rcvbuf(netdev->fd);
          if (error) {
              goto error;
          }
      return 0;
  
  error:
 -    netdev_close(&netdev->netdev);
 +    netdev_uninit(&netdev->netdev, true);
      return error;
  }
  
@@@ -891,8 -407,19 +891,8 @@@ netdev_linux_close(struct netdev *netde
  {
      struct netdev_linux *netdev = netdev_linux_cast(netdev_);
  
 -    if (netdev->cache && !--netdev->cache->ref_cnt) {
 -        shash_delete(&cache_map, netdev->cache->shash_node);
 -        free(netdev->cache);
 -
 -        if (shash_is_empty(&cache_map)) {
 -            rtnetlink_notifier_unregister(&netdev_linux_cache_notifier);
 -        }
 -    }
 -    if (netdev->netdev_fd >= 0) {
 -        close(netdev->netdev_fd);
 -    }
 -    if (netdev->tap_fd >= 0 && netdev->netdev_fd != netdev->tap_fd) {
 -        close(netdev->tap_fd);
 +    if (netdev->fd > 0 && strcmp(netdev_get_type(netdev_), "tap")) {
 +        close(netdev->fd);
      }
      free(netdev);
  }
@@@ -924,13 -451,13 +924,13 @@@ netdev_linux_recv(struct netdev *netdev
  {
      struct netdev_linux *netdev = netdev_linux_cast(netdev_);
  
 -    if (netdev->tap_fd < 0) {
 +    if (netdev->fd < 0) {
          /* Device was opened with NETDEV_ETH_TYPE_NONE. */
          return -EAGAIN;
      }
  
      for (;;) {
 -        ssize_t retval = read(netdev->tap_fd, data, size);
 +        ssize_t retval = read(netdev->fd, data, size);
          if (retval >= 0) {
              return retval;
          } else if (errno != EINTR) {
@@@ -949,8 -476,8 +949,8 @@@ static voi
  netdev_linux_recv_wait(struct netdev *netdev_)
  {
      struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 -    if (netdev->tap_fd >= 0) {
 -        poll_fd_wait(netdev->tap_fd, POLLIN);
 +    if (netdev->fd >= 0) {
 +        poll_fd_wait(netdev->fd, POLLIN);
      }
  }
  
@@@ -959,19 -486,19 +959,19 @@@ static in
  netdev_linux_drain(struct netdev *netdev_)
  {
      struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 -    if (netdev->tap_fd < 0 && netdev->netdev_fd < 0) {
 +    if (netdev->fd < 0) {
          return 0;
 -    } else if (netdev->tap_fd != netdev->netdev_fd) {
 +    } else if (!strcmp(netdev_get_type(netdev_), "tap")) {
          struct ifreq ifr;
 -        int error = netdev_linux_do_ioctl(netdev_, &ifr,
 +        int error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr,
                                            SIOCGIFTXQLEN, "SIOCGIFTXQLEN");
          if (error) {
              return error;
          }
 -        drain_fd(netdev->tap_fd, ifr.ifr_qlen);
 +        drain_fd(netdev->fd, ifr.ifr_qlen);
          return 0;
      } else {
 -        return drain_rcvbuf(netdev->netdev_fd);
 +        return drain_rcvbuf(netdev->fd);
      }
  }
  
@@@ -991,12 -518,12 +991,12 @@@ netdev_linux_send(struct netdev *netdev
  
      /* XXX should support sending even if 'ethertype' was NETDEV_ETH_TYPE_NONE.
       */
 -    if (netdev->tap_fd < 0) {
 +    if (netdev->fd < 0) {
          return EPIPE;
      }
  
      for (;;) {
 -        ssize_t retval = write(netdev->tap_fd, data, size);
 +        ssize_t retval = write(netdev->fd, data, size);
          if (retval < 0) {
              /* The Linux AF_PACKET implementation never blocks waiting for room
               * for packets, instead returning ENOBUFS.  Translate this into
@@@ -1031,10 -558,10 +1031,10 @@@ static voi
  netdev_linux_send_wait(struct netdev *netdev_)
  {
      struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 -    if (netdev->tap_fd < 0 && netdev->netdev_fd < 0) {
 +    if (netdev->fd < 0) {
          /* Nothing to do. */
 -    } else if (netdev->tap_fd == netdev->netdev_fd) {
 -        poll_fd_wait(netdev->tap_fd, POLLOUT);
 +    } else if (strcmp(netdev_get_type(netdev_), "tap")) {
 +        poll_fd_wait(netdev->fd, POLLOUT);
      } else {
          /* TAP device always accepts packets.*/
          poll_immediate_wake();
@@@ -1047,16 -574,15 +1047,16 @@@ static in
  netdev_linux_set_etheraddr(struct netdev *netdev_,
                             const uint8_t mac[ETH_ADDR_LEN])
  {
 -    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +    struct netdev_dev_linux *netdev_dev =
 +                                netdev_dev_linux_cast(netdev_get_dev(netdev_));
      int error;
  
 -    if (!(netdev->cache->valid & VALID_ETHERADDR)
 -        || !eth_addr_equals(netdev->cache->etheraddr, mac)) {
 +    if (!(netdev_dev->cache_valid & VALID_ETHERADDR)
 +        || !eth_addr_equals(netdev_dev->etheraddr, mac)) {
          error = set_etheraddr(netdev_get_name(netdev_), ARPHRD_ETHER, mac);
          if (!error) {
 -            netdev->cache->valid |= VALID_ETHERADDR;
 -            memcpy(netdev->cache->etheraddr, mac, ETH_ADDR_LEN);
 +            netdev_dev->cache_valid |= VALID_ETHERADDR;
 +            memcpy(netdev_dev->etheraddr, mac, ETH_ADDR_LEN);
          }
      } else {
          error = 0;
@@@ -1070,17 -596,16 +1070,17 @@@ static in
  netdev_linux_get_etheraddr(const struct netdev *netdev_,
                             uint8_t mac[ETH_ADDR_LEN])
  {
 -    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 -    if (!(netdev->cache->valid & VALID_ETHERADDR)) {
 +    struct netdev_dev_linux *netdev_dev =
 +                                netdev_dev_linux_cast(netdev_get_dev(netdev_));
 +    if (!(netdev_dev->cache_valid & VALID_ETHERADDR)) {
          int error = get_etheraddr(netdev_get_name(netdev_),
 -                                  netdev->cache->etheraddr);
 +                                  netdev_dev->etheraddr);
          if (error) {
              return error;
          }
 -        netdev->cache->valid |= VALID_ETHERADDR;
 +        netdev_dev->cache_valid |= VALID_ETHERADDR;
      }
 -    memcpy(mac, netdev->cache->etheraddr, ETH_ADDR_LEN);
 +    memcpy(mac, netdev_dev->etheraddr, ETH_ADDR_LEN);
      return 0;
  }
  
  static int
  netdev_linux_get_mtu(const struct netdev *netdev_, int *mtup)
  {
 -    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 -    if (!(netdev->cache->valid & VALID_MTU)) {
 +    struct netdev_dev_linux *netdev_dev =
 +                                netdev_dev_linux_cast(netdev_get_dev(netdev_));
 +    if (!(netdev_dev->cache_valid & VALID_MTU)) {
          struct ifreq ifr;
          int error;
  
 -        error = netdev_linux_do_ioctl(netdev_, &ifr, SIOCGIFMTU, "SIOCGIFMTU");
 +        error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr,
 +                                      SIOCGIFMTU, "SIOCGIFMTU");
          if (error) {
              return error;
          }
 -        netdev->cache->mtu = ifr.ifr_mtu;
 -        netdev->cache->valid |= VALID_MTU;
 +        netdev_dev->mtu = ifr.ifr_mtu;
 +        netdev_dev->cache_valid |= VALID_MTU;
      }
 -    *mtup = netdev->cache->mtu;
 +    *mtup = netdev_dev->mtu;
      return 0;
  }
  
@@@ -1122,18 -645,16 +1122,18 @@@ netdev_linux_get_ifindex(const struct n
  static int
  netdev_linux_get_carrier(const struct netdev *netdev_, bool *carrier)
  {
 -    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +    struct netdev_dev_linux *netdev_dev =
 +                                netdev_dev_linux_cast(netdev_get_dev(netdev_));
      int error = 0;
      char *fn = NULL;
      int fd = -1;
  
 -    if (!(netdev->cache->valid & VALID_CARRIER)) {
 +    if (!(netdev_dev->cache_valid & VALID_CARRIER)) {
          char line[8];
          int retval;
  
 -        fn = xasprintf("/sys/class/net/%s/carrier", netdev_get_name(netdev_));
 +        fn = xasprintf("/sys/class/net/%s/carrier",
 +                       netdev_get_name(netdev_));
          fd = open(fn, O_RDONLY);
          if (fd < 0) {
              error = errno;
                           fn, line[0]);
              goto exit;
          }
 -        netdev->cache->carrier = line[0] != '0';
 -        netdev->cache->valid |= VALID_CARRIER;
 +        netdev_dev->carrier = line[0] != '0';
 +        netdev_dev->cache_valid |= VALID_CARRIER;
      }
 -    *carrier = netdev->cache->carrier;
 +    *carrier = netdev_dev->carrier;
      error = 0;
  
  exit:
@@@ -1210,11 -731,9 +1210,11 @@@ check_for_working_netlink_stats(void
   * XXX All of the members of struct netdev_stats are 64 bits wide, but on
   * 32-bit architectures the Linux network stats are only 32 bits. */
  static int
 -netdev_linux_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
 +netdev_linux_get_stats(const struct netdev *netdev_,
 +                       struct netdev_stats *stats)
  {
 -    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +    struct netdev_dev_linux *netdev_dev =
 +                                netdev_dev_linux_cast(netdev_get_dev(netdev_));
      static int use_netlink_stats = -1;
      int error;
      struct netdev_stats raw_stats;
  
      COVERAGE_INC(netdev_get_stats);
  
 -    if (!(netdev->cache->valid & VALID_IS_INTERNAL)) {
 -        netdev->cache->is_internal = (netdev->tap_fd != -1);
 -
 -        if (!netdev->cache->is_internal) {
 +    if (!(netdev_dev->cache_valid & VALID_IS_INTERNAL)) {
 +        netdev_dev->is_internal = !strcmp(netdev_get_type(netdev_), "tap");
 +        if (!netdev_dev->is_internal) {
              struct ethtool_drvinfo drvinfo;
  
              memset(&drvinfo, 0, sizeof drvinfo);
 -            error = netdev_linux_do_ethtool(&netdev->netdev,
 +            error = netdev_linux_do_ethtool(netdev_get_name(netdev_),
                                              (struct ethtool_cmd *)&drvinfo,
                                              ETHTOOL_GDRVINFO,
                                              "ETHTOOL_GDRVINFO");
  
              if (!error) {
 -                netdev->cache->is_internal = !strcmp(drvinfo.driver,
 -                                                     "openvswitch");
 +                netdev_dev->is_internal = !strcmp(drvinfo.driver,
 +                                                        "openvswitch");
              }
          }
  
 -        netdev->cache->valid |= VALID_IS_INTERNAL;
 +        netdev_dev->cache_valid |= VALID_IS_INTERNAL;
      }
  
 -    if (netdev->cache->is_internal) {
 +    if (netdev_dev->is_internal) {
          collect_stats = &raw_stats;
      }
  
      if (use_netlink_stats) {
          int ifindex;
  
 -        error = get_ifindex(&netdev->netdev, &ifindex);
 +        error = get_ifindex(netdev_, &ifindex);
          if (!error) {
              error = get_stats_via_netlink(ifindex, collect_stats);
          }
      } else {
 -        error = get_stats_via_proc(netdev->netdev.name, collect_stats);
 +        error = get_stats_via_proc(netdev_get_name(netdev_), collect_stats);
      }
  
      /* If this port is an internal port then the transmit and receive stats
       * will appear to be swapped relative to the other ports since we are the
       * one sending the data, not a remote computer.  For consistency, we swap
       * them back here. */
 -    if (netdev->cache->is_internal) {
 +    if (!error && netdev_dev->is_internal) {
          stats->rx_packets = raw_stats.tx_packets;
          stats->tx_packets = raw_stats.rx_packets;
          stats->rx_bytes = raw_stats.tx_bytes;
@@@ -1304,7 -824,7 +1304,7 @@@ netdev_linux_get_features(struct netde
      int error;
  
      memset(&ecmd, 0, sizeof ecmd);
 -    error = netdev_linux_do_ethtool(netdev, &ecmd,
 +    error = netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd,
                                      ETHTOOL_GSET, "ETHTOOL_GSET");
      if (error) {
          return error;
@@@ -1425,7 -945,7 +1425,7 @@@ netdev_linux_set_advertisements(struct 
      int error;
  
      memset(&ecmd, 0, sizeof ecmd);
 -    error = netdev_linux_do_ethtool(netdev, &ecmd,
 +    error = netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd,
                                      ETHTOOL_GSET, "ETHTOOL_GSET");
      if (error) {
          return error;
      if (advertise & OFPPF_PAUSE_ASYM) {
          ecmd.advertising |= ADVERTISED_Asym_Pause;
      }
 -    return netdev_linux_do_ethtool(netdev, &ecmd,
 +    return netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd,
                                     ETHTOOL_SSET, "ETHTOOL_SSET");
  }
  
@@@ -1546,8 -1066,8 +1546,8 @@@ netdev_linux_set_policing(struct netde
      COVERAGE_INC(netdev_set_policing);
      if (kbits_rate) {
          if (!kbits_burst) {
 -            /* Default to 10 kilobits if not specified. */
 -            kbits_burst = 10;
 +            /* Default to 1000 kilobits if not specified. */
 +            kbits_burst = 1000;
          }
  
          /* xxx This should be more careful about only adding if it
@@@ -1584,28 -1104,26 +1584,28 @@@ static in
  netdev_linux_get_in4(const struct netdev *netdev_,
                       struct in_addr *address, struct in_addr *netmask)
  {
 -    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 -    if (!(netdev->cache->valid & VALID_IN4)) {
 +    struct netdev_dev_linux *netdev_dev =
 +                                netdev_dev_linux_cast(netdev_get_dev(netdev_));
 +
 +    if (!(netdev_dev->cache_valid & VALID_IN4)) {
          int error;
  
 -        error = netdev_linux_get_ipv4(netdev_, &netdev->cache->address,
 +        error = netdev_linux_get_ipv4(netdev_, &netdev_dev->address,
                                        SIOCGIFADDR, "SIOCGIFADDR");
          if (error) {
              return error;
          }
  
 -        error = netdev_linux_get_ipv4(netdev_, &netdev->cache->netmask,
 +        error = netdev_linux_get_ipv4(netdev_, &netdev_dev->netmask,
                                        SIOCGIFNETMASK, "SIOCGIFNETMASK");
          if (error) {
              return error;
          }
  
 -        netdev->cache->valid |= VALID_IN4;
 +        netdev_dev->cache_valid |= VALID_IN4;
      }
 -    *address = netdev->cache->address;
 -    *netmask = netdev->cache->netmask;
 +    *address = netdev_dev->address;
 +    *netmask = netdev_dev->netmask;
      return address->s_addr == INADDR_ANY ? EADDRNOTAVAIL : 0;
  }
  
@@@ -1613,15 -1131,14 +1613,15 @@@ static in
  netdev_linux_set_in4(struct netdev *netdev_, struct in_addr address,
                       struct in_addr netmask)
  {
 -    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +    struct netdev_dev_linux *netdev_dev =
 +                                netdev_dev_linux_cast(netdev_get_dev(netdev_));
      int error;
  
      error = do_set_addr(netdev_, SIOCSIFADDR, "SIOCSIFADDR", address);
      if (!error) {
 -        netdev->cache->valid |= VALID_IN4;
 -        netdev->cache->address = address;
 -        netdev->cache->netmask = netmask;
 +        netdev_dev->cache_valid |= VALID_IN4;
 +        netdev_dev->address = address;
 +        netdev_dev->netmask = netmask;
          if (address.s_addr != INADDR_ANY) {
              error = do_set_addr(netdev_, SIOCSIFNETMASK,
                                  "SIOCSIFNETMASK", netmask);
@@@ -1651,13 -1168,12 +1651,13 @@@ parse_if_inet6_line(const char *line
  static int
  netdev_linux_get_in6(const struct netdev *netdev_, struct in6_addr *in6)
  {
 -    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 -    if (!(netdev->cache->valid & VALID_IN6)) {
 +    struct netdev_dev_linux *netdev_dev =
 +                                netdev_dev_linux_cast(netdev_get_dev(netdev_));
 +    if (!(netdev_dev->cache_valid & VALID_IN6)) {
          FILE *file;
          char line[128];
  
 -        netdev->cache->in6 = in6addr_any;
 +        netdev_dev->in6 = in6addr_any;
  
          file = fopen("/proc/net/if_inet6", "r");
          if (file != NULL) {
                  if (parse_if_inet6_line(line, &in6, ifname)
                      && !strcmp(name, ifname))
                  {
 -                    netdev->cache->in6 = in6;
 +                    netdev_dev->in6 = in6;
                      break;
                  }
              }
              fclose(file);
          }
 -        netdev->cache->valid |= VALID_IN6;
 +        netdev_dev->cache_valid |= VALID_IN6;
      }
 -    *in6 = netdev->cache->in6;
 +    *in6 = netdev_dev->in6;
      return 0;
  }
  
@@@ -1698,16 -1214,14 +1698,16 @@@ do_set_addr(struct netdev *netdev
              int ioctl_nr, const char *ioctl_name, struct in_addr addr)
  {
      struct ifreq ifr;
 -    strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name);
 +    strncpy(ifr.ifr_name, netdev_get_name(netdev), sizeof ifr.ifr_name);
      make_in4_sockaddr(&ifr.ifr_addr, addr);
 -    return netdev_linux_do_ioctl(netdev, &ifr, ioctl_nr, ioctl_name);
 +
 +    return netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, ioctl_nr,
 +                                 ioctl_name);
  }
  
  /* Adds 'router' as a default IP gateway. */
  static int
- netdev_linux_add_router(struct netdev *netdev UNUSED, struct in_addr router)
+ netdev_linux_add_router(struct netdev *netdev OVS_UNUSED, struct in_addr router)
  {
      struct in_addr any = { INADDR_ANY };
      struct rtentry rt;
@@@ -1796,24 -1310,24 +1796,24 @@@ netdev_linux_arp_lookup(const struct ne
                          uint32_t ip, uint8_t mac[ETH_ADDR_LEN])
  {
      struct arpreq r;
 -    struct sockaddr_in *pa;
 +    struct sockaddr_in sin;
      int retval;
  
      memset(&r, 0, sizeof r);
 -    pa = (struct sockaddr_in *) &r.arp_pa;
 -    pa->sin_family = AF_INET;
 -    pa->sin_addr.s_addr = ip;
 -    pa->sin_port = 0;
 +    sin.sin_family = AF_INET;
 +    sin.sin_addr.s_addr = ip;
 +    sin.sin_port = 0;
 +    memcpy(&r.arp_pa, &sin, sizeof sin);
      r.arp_ha.sa_family = ARPHRD_ETHER;
      r.arp_flags = 0;
 -    strncpy(r.arp_dev, netdev->name, sizeof r.arp_dev);
 +    strncpy(r.arp_dev, netdev_get_name(netdev), sizeof r.arp_dev);
      COVERAGE_INC(netdev_arp_lookup);
      retval = ioctl(af_inet_sock, SIOCGARP, &r) < 0 ? errno : 0;
      if (!retval) {
          memcpy(mac, r.arp_ha.sa_data, ETH_ADDR_LEN);
      } else if (retval != ENXIO) {
          VLOG_WARN_RL(&rl, "%s: could not look up ARP entry for "IP_FMT": %s",
 -                     netdev->name, IP_ARGS(&ip), strerror(retval));
 +                     netdev_get_name(netdev), IP_ARGS(&ip), strerror(retval));
      }
      return retval;
  }
@@@ -1874,7 -1388,7 +1874,7 @@@ poll_notify(struct list *list
  
  static void
  netdev_linux_poll_cb(const struct rtnetlink_change *change,
-                      void *aux UNUSED)
+                      void *aux OVS_UNUSED)
  {
      if (change) {
          struct list *list = shash_find_data(&netdev_linux_notifiers,
@@@ -1946,13 -1460,13 +1946,13 @@@ netdev_linux_poll_remove(struct netdev_
  }
  
  const struct netdev_class netdev_linux_class = {
 -    "system",                   /* type */
 +    "system",
  
      netdev_linux_init,
      netdev_linux_run,
      netdev_linux_wait,
  
 -    netdev_linux_create,
 +    netdev_linux_create_system,
      netdev_linux_destroy,
      NULL,                       /* reconfigure */
  
  };
  
  const struct netdev_class netdev_tap_class = {
 -    "tap",                      /* type */
 +    "tap",
  
      netdev_linux_init,
 -    NULL,                       /* run */
 -    NULL,                       /* wait */
 +    netdev_linux_run,
 +    netdev_linux_wait,
  
 -    netdev_linux_create,
 +    netdev_linux_create_tap,
      netdev_linux_destroy,
      NULL,                       /* reconfigure */
  
      netdev_linux_open,
      netdev_linux_close,
  
 -    netdev_linux_enumerate,
 +    NULL,                       /* enumerate */
 +
 +    netdev_linux_recv,
 +    netdev_linux_recv_wait,
 +    netdev_linux_drain,
 +
 +    netdev_linux_send,
 +    netdev_linux_send_wait,
 +
 +    netdev_linux_set_etheraddr,
 +    netdev_linux_get_etheraddr,
 +    netdev_linux_get_mtu,
 +    netdev_linux_get_ifindex,
 +    netdev_linux_get_carrier,
 +    netdev_linux_get_stats,
 +
 +    netdev_linux_get_features,
 +    netdev_linux_set_advertisements,
 +    netdev_linux_get_vlan_vid,
 +    netdev_linux_set_policing,
 +
 +    netdev_linux_get_in4,
 +    netdev_linux_set_in4,
 +    netdev_linux_get_in6,
 +    netdev_linux_add_router,
 +    netdev_linux_get_next_hop,
 +    netdev_linux_arp_lookup,
 +
 +    netdev_linux_update_flags,
 +
 +    netdev_linux_poll_add,
 +    netdev_linux_poll_remove,
 +};
 +
 +const struct netdev_class netdev_gre_class = {
 +    "gre",
 +
 +    netdev_linux_init,
 +    netdev_linux_run,
 +    netdev_linux_wait,
 +
 +    netdev_linux_create_gre,
 +    netdev_linux_destroy,
 +    netdev_linux_reconfigure_gre,
 +
 +    netdev_linux_open,
 +    netdev_linux_close,
 +
 +    NULL,                       /* enumerate */
  
      netdev_linux_recv,
      netdev_linux_recv_wait,
@@@ -2236,8 -1702,7 +2236,8 @@@ get_flags(const struct netdev *netdev, 
      struct ifreq ifr;
      int error;
  
 -    error = netdev_linux_do_ioctl(netdev, &ifr, SIOCGIFFLAGS, "SIOCGIFFLAGS");
 +    error = netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, SIOCGIFFLAGS,
 +                                  "SIOCGIFFLAGS");
      *flags = ifr.ifr_flags;
      return error;
  }
@@@ -2248,8 -1713,7 +2248,8 @@@ set_flags(struct netdev *netdev, int fl
      struct ifreq ifr;
  
      ifr.ifr_flags = flags;
 -    return netdev_linux_do_ioctl(netdev, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
 +    return netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, SIOCSIFFLAGS,
 +                                 "SIOCSIFFLAGS");
  }
  
  static int
@@@ -2270,18 -1734,17 +2270,18 @@@ do_get_ifindex(const char *netdev_name
  static int
  get_ifindex(const struct netdev *netdev_, int *ifindexp)
  {
 -    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 +    struct netdev_dev_linux *netdev_dev =
 +                                netdev_dev_linux_cast(netdev_get_dev(netdev_));
      *ifindexp = 0;
 -    if (!(netdev->cache->valid & VALID_IFINDEX)) {
 +    if (!(netdev_dev->cache_valid & VALID_IFINDEX)) {
          int ifindex = do_get_ifindex(netdev_get_name(netdev_));
          if (ifindex < 0) {
              return -ifindex;
          }
 -        netdev->cache->valid |= VALID_IFINDEX;
 -        netdev->cache->ifindex = ifindex;
 +        netdev_dev->cache_valid |= VALID_IFINDEX;
 +        netdev_dev->ifindex = ifindex;
      }
 -    *ifindexp = netdev->cache->ifindex;
 +    *ifindexp = netdev_dev->ifindex;
      return 0;
  }
  
@@@ -2328,13 -1791,13 +2328,13 @@@ set_etheraddr(const char *netdev_name, 
  }
  
  static int
 -netdev_linux_do_ethtool(struct netdev *netdev, struct ethtool_cmd *ecmd,
 +netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *ecmd,
                          int cmd, const char *cmd_name)
  {
      struct ifreq ifr;
  
      memset(&ifr, 0, sizeof ifr);
 -    strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name);
 +    strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
      ifr.ifr_data = (caddr_t) ecmd;
  
      ecmd->cmd = cmd;
      } else {
          if (errno != EOPNOTSUPP) {
              VLOG_WARN_RL(&rl, "ethtool command %s on network device %s "
 -                         "failed: %s", cmd_name, netdev->name,
 -                         strerror(errno));
 +                         "failed: %s", cmd_name, name, strerror(errno));
          } else {
              /* The device doesn't support this operation.  That's pretty
               * common, so there's no point in logging anything. */
  }
  
  static int
 -netdev_linux_do_ioctl(const struct netdev *netdev, struct ifreq *ifr,
 -                      int cmd, const char *cmd_name)
 +netdev_linux_do_ioctl(const char *name, struct ifreq *ifr, int cmd,
 +                      const char *cmd_name)
  {
 -    strncpy(ifr->ifr_name, netdev_get_name(netdev), sizeof ifr->ifr_name);
 +    strncpy(ifr->ifr_name, name, sizeof ifr->ifr_name);
      if (ioctl(af_inet_sock, cmd, ifr) == -1) {
 -        VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s",
 -                    netdev_get_name(netdev), cmd_name, strerror(errno));
 +        VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s", name, cmd_name,
 +                     strerror(errno));
          return errno;
      }
      return 0;
@@@ -2374,7 -1838,7 +2374,7 @@@ netdev_linux_get_ipv4(const struct netd
      int error;
  
      ifr.ifr_addr.sa_family = AF_INET;
 -    error = netdev_linux_do_ioctl(netdev, &ifr, cmd, cmd_name);
 +    error = netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, cmd, cmd_name);
      if (!error) {
          const struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr;
          *ip = sin->sin_addr;
diff --combined lib/netdev.c
@@@ -28,7 -28,6 +28,7 @@@
  #include "coverage.h"
  #include "dynamic-string.h"
  #include "fatal-signal.h"
 +#include "hash.h"
  #include "list.h"
  #include "netdev-provider.h"
  #include "ofpbuf.h"
  #define THIS_MODULE VLM_netdev
  #include "vlog.h"
  
 -static const struct netdev_class *netdev_classes[] = {
 +static const struct netdev_class *base_netdev_classes[] = {
      &netdev_linux_class,
      &netdev_tap_class,
 +    &netdev_gre_class,
  };
 -static int n_netdev_classes = ARRAY_SIZE(netdev_classes);
 +
 +static struct shash netdev_classes = SHASH_INITIALIZER(&netdev_classes);
  
  /* All created network devices. */
 -static struct shash netdev_obj_shash = SHASH_INITIALIZER(&netdev_obj_shash);
 +static struct shash netdev_dev_shash = SHASH_INITIALIZER(&netdev_dev_shash);
  
  /* All open network devices. */
  static struct list netdev_list = LIST_INITIALIZER(&netdev_list);
   * additional log messages. */
  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
  
- static void close_all_netdevs(void *aux UNUSED);
 -static void restore_all_flags(void *aux);
++static void close_all_netdevs(void *aux OVS_UNUSED);
  static int restore_flags(struct netdev *netdev);
 +void update_device_args(struct netdev_dev *, const struct shash *args);
  
 -/* Attempts to initialize the netdev module.  Returns 0 if successful,
 - * otherwise a positive errno value.
 - *
 - * Calling this function is optional.  If not called explicitly, it will
 - * automatically be called upon the first attempt to open or create a 
 - * network device. */
 -int
 +static void
  netdev_initialize(void)
  {
      static int status = -1;
 +
      if (status < 0) {
 -        int i, j;
 +        int i;
  
 -        fatal_signal_add_hook(restore_all_flags, NULL, true);
 +        fatal_signal_add_hook(close_all_netdevs, NULL, NULL, true);
  
          status = 0;
 -        for (i = j = 0; i < n_netdev_classes; i++) {
 -            const struct netdev_class *class = netdev_classes[i];
 -            if (class->init) {
 -                int retval = class->init();
 -                if (!retval) {
 -                    netdev_classes[j++] = class;
 -                } else {
 -                    VLOG_ERR("failed to initialize %s network device "
 -                             "class: %s", class->type, strerror(retval));
 -                    if (!status) {
 -                        status = retval;
 -                    }
 -                }
 -            } else {
 -                netdev_classes[j++] = class;
 -            }
 +        for (i = 0; i < ARRAY_SIZE(base_netdev_classes); i++) {
 +            netdev_register_provider(base_netdev_classes[i]);
          }
 -        n_netdev_classes = j;
      }
 -    return status;
  }
  
  /* Performs periodic work needed by all the various kinds of netdevs.
  void
  netdev_run(void)
  {
 -    int i;
 -    for (i = 0; i < n_netdev_classes; i++) {
 -        const struct netdev_class *class = netdev_classes[i];
 -        if (class->run) {
 -            class->run();
 +    struct shash_node *node;
 +    SHASH_FOR_EACH(node, &netdev_classes) {
 +        const struct netdev_class *netdev_class = node->data;
 +        if (netdev_class->run) {
 +            netdev_class->run();
          }
      }
  }
  void
  netdev_wait(void)
  {
 -    int i;
 -    for (i = 0; i < n_netdev_classes; i++) {
 -        const struct netdev_class *class = netdev_classes[i];
 -        if (class->wait) {
 -            class->wait();
 +    struct shash_node *node;
 +    SHASH_FOR_EACH(node, &netdev_classes) {
 +        const struct netdev_class *netdev_class = node->data;
 +        if (netdev_class->wait) {
 +            netdev_class->wait();
          }
      }
  }
  
 -/* Attempts to create a network device object of 'type' with 'name'.  'type' 
 - * corresponds to the 'type' field used in the netdev_class * structure.  
 - * Arguments for creation are provided in 'args', which may be empty or NULL 
 - * if none are needed. */
 +/* Initializes and registers a new netdev provider.  After successful
 + * registration, new netdevs of that type can be opened using netdev_open(). */
  int
 -netdev_create(const char *name, const char *type, const struct shash *args)
 +netdev_register_provider(const struct netdev_class *new_class)
  {
 -    struct shash empty_args = SHASH_INITIALIZER(&empty_args);
 -    int i;
 +    struct netdev_class *new_provider;
  
 -    netdev_initialize();
 +    if (shash_find(&netdev_classes, new_class->type)) {
 +        VLOG_WARN("attempted to register duplicate netdev provider: %s",
 +                   new_class->type);
 +        return EEXIST;
 +    }
  
 -    if (!args) {
 -        args = &empty_args;
 +    if (new_class->init) {
 +        int error = new_class->init();
 +        if (error) {
 +            VLOG_ERR("failed to initialize %s network device class: %s",
 +                     new_class->type, strerror(error));
 +            return error;
 +        }
      }
  
 -    if (shash_find(&netdev_obj_shash, name)) {
 -        VLOG_WARN("attempted to create a netdev object with bound name: %s",
 -                name);
 -        return EEXIST;
 +    new_provider = xmalloc(sizeof *new_provider);
 +    memcpy(new_provider, new_class, sizeof *new_provider);
 +
 +    shash_add(&netdev_classes, new_class->type, new_provider);
 +
 +    return 0;
 +}
 +
 +/* Unregisters a netdev provider.  'type' must have been previously
 + * registered and not currently be in use by any netdevs.  After unregistration
 + * new netdevs of that type cannot be opened using netdev_open(). */
 +int
 +netdev_unregister_provider(const char *type)
 +{
 +    struct shash_node *del_node, *netdev_dev_node;
 +
 +    del_node = shash_find(&netdev_classes, type);
 +    if (!del_node) {
 +        VLOG_WARN("attempted to unregister a netdev provider that is not "
 +                  "registered: %s", type);
 +        return EAFNOSUPPORT;
      }
  
 -    for (i = 0; i < n_netdev_classes; i++) {
 -        const struct netdev_class *class = netdev_classes[i];
 -        if (!strcmp(type, class->type)) {
 -            return class->create(name, type, args, true);
 +    SHASH_FOR_EACH(netdev_dev_node, &netdev_dev_shash) {
 +        struct netdev_dev *netdev_dev = netdev_dev_node->data;
 +        if (!strcmp(netdev_dev->netdev_class->type, type)) {
 +            VLOG_WARN("attempted to unregister in use netdev provider: %s",
 +                      type);
 +            return EBUSY;
          }
      }
  
 -    VLOG_WARN("could not create netdev object of unknown type: %s", type);
 +    shash_delete(&netdev_classes, del_node);
 +    free(del_node->data);
  
 -    return EINVAL;
 +    return 0;
  }
  
 -/* Destroys netdev object 'name'.  Netdev objects maintain a reference count
 - * which is incremented on netdev_open() and decremented on netdev_close().  
 - * If 'name' has a non-zero reference count, it will not destroy the object 
 - * and return EBUSY. */
 -int
 -netdev_destroy(const char *name)
 +/* Clears 'types' and enumerates the types of all currently registered netdev
 + * providers into it.  The caller must first initialize the svec. */
 +void
 +netdev_enumerate_types(struct svec *types)
  {
      struct shash_node *node;
 -    struct netdev_obj *netdev_obj;
  
 -    node = shash_find(&netdev_obj_shash, name);
 -    if (!node) {
 -        return ENODEV;
 +    netdev_initialize();
 +    svec_clear(types);
 +
 +    SHASH_FOR_EACH(node, &netdev_classes) {
 +        const struct netdev_class *netdev_class = node->data;
 +        svec_add(types, netdev_class->type);
      }
 +}
 +
 +/* Compares 'args' to those used to those used by 'dev'.  Returns true
 + * if the arguments are the same, false otherwise.  Does not update the
 + * values stored in 'dev'. */
 +static bool
 +compare_device_args(const struct netdev_dev *dev, const struct shash *args)
 +{
 +    const struct shash_node **new_args;
 +    bool result = true;
 +    int i;
  
 -    netdev_obj = node->data;
 -    if (netdev_obj->ref_cnt != 0) {
 -        VLOG_WARN("attempt to destroy open netdev object (%d): %s", 
 -                netdev_obj->ref_cnt, name);
 -        return EBUSY;
 +    if (shash_count(args) != dev->n_args) {
 +        return false;
      }
  
 -    shash_delete(&netdev_obj_shash, node);
 -    netdev_obj->netdev_class->destroy(netdev_obj);
 +    new_args = shash_sort(args);
 +    for (i = 0; i < dev->n_args; i++) {
 +        if (strcmp(dev->args[i].key, new_args[i]->name) || 
 +            strcmp(dev->args[i].value, new_args[i]->data)) {
 +            result = false;
 +            goto finish;
 +        }
 +    }
  
 -    return 0;
 +finish:
 +    free(new_args);
 +    return result;
  }
  
 -/* Reconfigures the device object 'name' with 'args'.  'args' may be empty 
 - * or NULL if none are needed. */
 -int
 -netdev_reconfigure(const char *name, const struct shash *args)
 +static int
 +compare_args(const void *a_, const void *b_)
  {
 -    struct shash empty_args = SHASH_INITIALIZER(&empty_args);
 -    struct netdev_obj *netdev_obj;
 +    const struct arg *a = a_;
 +    const struct arg *b = b_;
 +    return strcmp(a->key, b->key);
 +}
  
 -    if (!args) {
 -        args = &empty_args;
 +void
 +update_device_args(struct netdev_dev *dev, const struct shash *args)
 +{
 +    struct shash_node *node;
 +    int i;
 +
 +    if (dev->n_args) {
 +        for (i = 0; i < dev->n_args; i++) {
 +            free(dev->args[i].key);
 +            free(dev->args[i].value);
 +        }
 +
 +        free(dev->args);
 +        dev->n_args = 0;
 +    }
 +
 +    if (!args || shash_is_empty(args)) {
 +        return;
      }
  
 -    netdev_obj = shash_find_data(&netdev_obj_shash, name);
 -    if (!netdev_obj) {
 +    dev->n_args = shash_count(args);
 +    dev->args = xmalloc(dev->n_args * sizeof *dev->args);
 +
 +    i = 0;
 +    SHASH_FOR_EACH(node, args) {
 +        dev->args[i].key = xstrdup(node->name);
 +        dev->args[i].value = xstrdup(node->data);
 +        i++;
 +    }
 +
 +    qsort(dev->args, dev->n_args, sizeof *dev->args, compare_args);
 +}
 +
 +static int
 +create_device(struct netdev_options *options, struct netdev_dev **netdev_devp)
 +{
 +    struct netdev_class *netdev_class;
 +
 +    if (!options->may_create) {
 +        VLOG_WARN("attempted to create a device that may not be created: %s",
 +                  options->name);
          return ENODEV;
      }
  
 -    if (netdev_obj->netdev_class->reconfigure) {
 -        return netdev_obj->netdev_class->reconfigure(netdev_obj, args);
 +    if (!options->type || strlen(options->type) == 0) {
 +        /* Default to system. */
 +        options->type = "system";
      }
  
 -    return 0;
 +    netdev_class = shash_find_data(&netdev_classes, options->type);
 +    if (!netdev_class) {
 +        VLOG_WARN("could not create netdev %s of unknown type %s",
 +                  options->name, options->type);
 +        return EAFNOSUPPORT;
 +    }
 +
 +    return netdev_class->create(options->name, options->type, options->args,
 +                                netdev_devp);
  }
  
  /* Opens the network device named 'name' (e.g. "eth0") and returns zero if
   * successful, otherwise a positive errno value.  On success, sets '*netdevp'
   * to the new network device, otherwise to null.
   *
 + * If this is the first time the device has been opened, then create is called
 + * before opening.  The device is  created using the given type and arguments.
 + *
   * 'ethertype' may be a 16-bit Ethernet protocol value in host byte order to
   * capture frames of that type received on the device.  It may also be one of
   * the 'enum netdev_pseudo_ethertype' values to receive frames in one of those
 - * categories. */
 + * categories.
 + *
 + * If the 'may_create' flag is set then this is allowed to be the first time
 + * the device is opened (i.e. the refcount will be 1 after this call).  It
 + * may be set to false if the device should have already been created.
 + *
 + * If the 'may_open' flag is set then the call will succeed even if another
 + * caller has already opened it.  It may be to false if the device should not
 + * currently be open. */
 +
  int
 -netdev_open(const char *name, int ethertype, struct netdev **netdevp)
 +netdev_open(struct netdev_options *options, struct netdev **netdevp)
  {
 -    struct netdev_obj *netdev_obj;
 -    struct netdev *netdev = NULL;
 +    struct shash empty_args = SHASH_INITIALIZER(&empty_args);
 +    struct netdev_dev *netdev_dev;
      int error;
 -    int i;
  
 +    *netdevp = NULL;
      netdev_initialize();
  
 -    netdev_obj = shash_find_data(&netdev_obj_shash, name);
 -    if (netdev_obj) {
 -        netdev_obj->ref_cnt++;
 -        error = netdev_obj->netdev_class->open(name, ethertype, &netdev);
 +    if (!options->args) {
 +        options->args = &empty_args;
 +    }
 +
 +    netdev_dev = shash_find_data(&netdev_dev_shash, options->name);
 +
 +    if (!netdev_dev) {
 +        error = create_device(options, &netdev_dev);
 +        if (error) {
 +            return error;
 +        }
 +        update_device_args(netdev_dev, options->args);
 +
 +    } else if (options->may_open) {
 +        if (!shash_is_empty(options->args) &&
 +            !compare_device_args(netdev_dev, options->args)) {
 +
 +            VLOG_WARN("%s: attempted to open already created netdev with "
 +                      "different arguments", options->name);
 +            return EINVAL;
 +        }
      } else {
 -        /* Default to "system". */
 -        error = EAFNOSUPPORT;
 -        for (i = 0; i < n_netdev_classes; i++) {
 -            const struct netdev_class *class = netdev_classes[i];
 -            if (!strcmp(class->type, "system")) {
 -                struct shash empty_args = SHASH_INITIALIZER(&empty_args);
 -
 -                /* Dynamically create the netdev object, but indicate
 -                 * that it should be destroyed when the the last user
 -                 * closes its handle. */
 -                error = class->create(name, "system", &empty_args, false);
 -                if (!error) {
 -                    netdev_obj = shash_find_data(&netdev_obj_shash, name);
 -                    netdev_obj->ref_cnt++;
 -                    error = class->open(name, ethertype, &netdev);
 -                }
 -                break;
 -            }
 +        VLOG_WARN("%s: attempted to create a netdev device with bound name",
 +                  options->name);
 +        return EEXIST;
 +    }
 +
 +    error = netdev_dev->netdev_class->open(netdev_dev, options->ethertype, 
 +                netdevp);
 +
 +    if (!error) {
 +        netdev_dev->ref_cnt++;
 +    } else {
 +        if (!netdev_dev->ref_cnt) {
 +            netdev_dev_uninit(netdev_dev, true);
          }
      }
  
 -    *netdevp = error ? NULL : netdev;
      return error;
  }
  
 +int
 +netdev_open_default(const char *name, struct netdev **netdevp)
 +{
 +    struct netdev_options options;
 +
 +    memset(&options, 0, sizeof options);
 +
 +    options.name = name;
 +    options.ethertype = NETDEV_ETH_TYPE_NONE;
 +    options.may_create = true;
 +    options.may_open = true;
 +
 +    return netdev_open(&options, netdevp);
 +}
 +
 +/* Reconfigures the device 'netdev' with 'args'.  'args' may be empty
 + * or NULL if none are needed. */
 +int
 +netdev_reconfigure(struct netdev *netdev, const struct shash *args)
 +{
 +    struct shash empty_args = SHASH_INITIALIZER(&empty_args);
 +    struct netdev_dev *netdev_dev = netdev_get_dev(netdev);
 +
 +    if (!args) {
 +        args = &empty_args;
 +    }
 +
 +    if (netdev_dev->netdev_class->reconfigure) {
 +        if (!compare_device_args(netdev_dev, args)) {
 +            update_device_args(netdev_dev, args);
 +            return netdev_dev->netdev_class->reconfigure(netdev_dev, args);
 +        }
 +    } else if (!shash_is_empty(args)) {
 +        VLOG_WARN("%s: arguments provided to device that does not have a "
 +                  "reconfigure function", netdev_get_name(netdev));
 +    }
 +
 +    return 0;
 +}
 +
  /* Closes and destroys 'netdev'. */
  void
  netdev_close(struct netdev *netdev)
  {
      if (netdev) {
 -        struct netdev_obj *netdev_obj;
 -        char *name = netdev->name;
 -        int error;
 -
 -        netdev_obj = shash_find_data(&netdev_obj_shash, name);
 -        assert(netdev_obj);
 -        if (netdev_obj->ref_cnt > 0) {
 -            netdev_obj->ref_cnt--;
 -        } else {
 -            VLOG_WARN("netdev %s closed too many times", name);
 -        }
 +        struct netdev_dev *netdev_dev = netdev_get_dev(netdev);
  
 -        /* If the reference count for the netdev object is zero, and it
 -         * was dynamically created by netdev_open(), destroy it. */
 -        if (!netdev_obj->ref_cnt && !netdev_obj->created) {
 -            netdev_destroy(name);
 -        }
 +        assert(netdev_dev->ref_cnt);
 +        netdev_dev->ref_cnt--;
 +        netdev_uninit(netdev, true);
  
 -        /* Restore flags that we changed, if any. */
 -        fatal_signal_block();
 -        error = restore_flags(netdev);
 -        list_remove(&netdev->node);
 -        fatal_signal_unblock();
 -        if (error) {
 -            VLOG_WARN("failed to restore network device flags on %s: %s",
 -                      name, strerror(error));
 +        /* If the reference count for the netdev device is zero, destroy it. */
 +        if (!netdev_dev->ref_cnt) {
 +            netdev_dev_uninit(netdev_dev, true);
          }
 -
 -        /* Free. */
 -        netdev->netdev_class->close(netdev);
 -        free(name);
      }
  }
  
@@@ -421,7 -310,7 +421,7 @@@ netdev_exists(const char *name
      struct netdev *netdev;
      int error;
  
 -    error = netdev_open(name, NETDEV_ETH_TYPE_NONE, &netdev);
 +    error = netdev_open_default(name, &netdev);
      if (!error) {
          netdev_close(netdev);
          return true;
      }
  }
  
 -/* Initializes 'svec' with a list of the names of all known network devices. */
 +/*  Clears 'svec' and enumerates the names of all known network devices. */
  int
  netdev_enumerate(struct svec *svec)
  {
 -    int error;
 -    int i;
 -
 -    svec_init(svec);
 +    struct shash_node *node;
 +    int error = 0;
  
      netdev_initialize();
 +    svec_clear(svec);
  
 -    error = 0;
 -    for (i = 0; i < n_netdev_classes; i++) {
 -        const struct netdev_class *class = netdev_classes[i];
 -        if (class->enumerate) {
 -            int retval = class->enumerate(svec);
 +    SHASH_FOR_EACH(node, &netdev_classes) {
 +        const struct netdev_class *netdev_class = node->data;
 +        if (netdev_class->enumerate) {
 +            int retval = netdev_class->enumerate(svec);
              if (retval) {
                  VLOG_WARN("failed to enumerate %s network devices: %s",
 -                          class->type, strerror(retval));
 +                          netdev_class->type, strerror(retval));
                  if (!error) {
                      error = retval;
                  }
              }
          }
      }
 +
      return error;
  }
  
@@@ -481,8 -371,8 +481,8 @@@ netdev_recv(struct netdev *netdev, stru
      assert(buffer->size == 0);
      assert(ofpbuf_tailroom(buffer) >= ETH_TOTAL_MIN);
  
 -    retval = netdev->netdev_class->recv(netdev,
 -                                        buffer->data, ofpbuf_tailroom(buffer));
 +    retval = netdev_get_dev(netdev)->netdev_class->recv(netdev, buffer->data,
 +             ofpbuf_tailroom(buffer));
      if (retval >= 0) {
          COVERAGE_INC(netdev_received);
          buffer->size += retval;
  void
  netdev_recv_wait(struct netdev *netdev)
  {
 -    netdev->netdev_class->recv_wait(netdev);
 +    netdev_get_dev(netdev)->netdev_class->recv_wait(netdev);
  }
  
  /* Discards all packets waiting to be received from 'netdev'. */
  int
  netdev_drain(struct netdev *netdev)
  {
 -    return netdev->netdev_class->drain(netdev);
 +    return netdev_get_dev(netdev)->netdev_class->drain(netdev);
  }
  
  /* Sends 'buffer' on 'netdev'.  Returns 0 if successful, otherwise a positive
  int
  netdev_send(struct netdev *netdev, const struct ofpbuf *buffer)
  {
 -    int error = netdev->netdev_class->send(netdev, buffer->data, buffer->size);
 +    int error = netdev_get_dev(netdev)->netdev_class->send(netdev, 
 +            buffer->data, buffer->size);
      if (!error) {
          COVERAGE_INC(netdev_sent);
      }
  void
  netdev_send_wait(struct netdev *netdev)
  {
 -    return netdev->netdev_class->send_wait(netdev);
 +    return netdev_get_dev(netdev)->netdev_class->send_wait(netdev);
  }
  
  /* Attempts to set 'netdev''s MAC address to 'mac'.  Returns 0 if successful,
  int
  netdev_set_etheraddr(struct netdev *netdev, const uint8_t mac[ETH_ADDR_LEN])
  {
 -    return netdev->netdev_class->set_etheraddr(netdev, mac);
 +    return netdev_get_dev(netdev)->netdev_class->set_etheraddr(netdev, mac);
  }
  
  /* Retrieves 'netdev''s MAC address.  If successful, returns 0 and copies the
  int
  netdev_get_etheraddr(const struct netdev *netdev, uint8_t mac[ETH_ADDR_LEN])
  {
 -    return netdev->netdev_class->get_etheraddr(netdev, mac);
 +    return netdev_get_dev(netdev)->netdev_class->get_etheraddr(netdev, mac);
  }
  
  /* Returns the name of the network device that 'netdev' represents,
  const char *
  netdev_get_name(const struct netdev *netdev)
  {
 -    return netdev->name;
 +    return netdev_get_dev(netdev)->name;
  }
  
  /* Retrieves the MTU of 'netdev'.  The MTU is the maximum size of transmitted
  int
  netdev_get_mtu(const struct netdev *netdev, int *mtup)
  {
 -    int error = netdev->netdev_class->get_mtu(netdev, mtup);
 +    int error = netdev_get_dev(netdev)->netdev_class->get_mtu(netdev, mtup);
      if (error) {
          VLOG_WARN_RL(&rl, "failed to retrieve MTU for network device %s: %s",
                       netdev_get_name(netdev), strerror(error));
  int
  netdev_get_ifindex(const struct netdev *netdev)
  {
 -    return netdev->netdev_class->get_ifindex(netdev);
 +    return netdev_get_dev(netdev)->netdev_class->get_ifindex(netdev);
  }
  
  /* Stores the features supported by 'netdev' into each of '*current',
@@@ -628,8 -517,8 +628,8 @@@ netdev_get_features(struct netdev *netd
          peer = &dummy[3];
      }
  
 -    error = netdev->netdev_class->get_features(netdev, current, advertised,
 -                                               supported, peer);
 +    error = netdev_get_dev(netdev)->netdev_class->get_features(netdev, current,
 +            advertised, supported, peer);
      if (error) {
          *current = *advertised = *supported = *peer = 0;
      }
@@@ -670,9 -559,8 +670,9 @@@ netdev_features_is_full_duplex(uint32_
  int
  netdev_set_advertisements(struct netdev *netdev, uint32_t advertise)
  {
 -    return (netdev->netdev_class->set_advertisements
 -            ? netdev->netdev_class->set_advertisements(netdev, advertise)
 +    return (netdev_get_dev(netdev)->netdev_class->set_advertisements
 +            ? netdev_get_dev(netdev)->netdev_class->set_advertisements(
 +                    netdev, advertise)
              : EOPNOTSUPP);
  }
  
@@@ -696,9 -584,8 +696,9 @@@ netdev_get_in4(const struct netdev *net
      struct in_addr netmask;
      int error;
  
 -    error = (netdev->netdev_class->get_in4
 -             ? netdev->netdev_class->get_in4(netdev, &address, &netmask)
 +    error = (netdev_get_dev(netdev)->netdev_class->get_in4
 +             ? netdev_get_dev(netdev)->netdev_class->get_in4(netdev, 
 +                    &address, &netmask)
               : EOPNOTSUPP);
      if (address_) {
          address_->s_addr = error ? 0 : address.s_addr;
  int
  netdev_set_in4(struct netdev *netdev, struct in_addr addr, struct in_addr mask)
  {
 -    return (netdev->netdev_class->set_in4
 -            ? netdev->netdev_class->set_in4(netdev, addr, mask)
 +    return (netdev_get_dev(netdev)->netdev_class->set_in4
 +            ? netdev_get_dev(netdev)->netdev_class->set_in4(netdev, addr, mask)
              : EOPNOTSUPP);
  }
  
@@@ -726,8 -613,8 +726,8 @@@ in
  netdev_add_router(struct netdev *netdev, struct in_addr router)
  {
      COVERAGE_INC(netdev_add_router);
 -    return (netdev->netdev_class->add_router
 -            ? netdev->netdev_class->add_router(netdev, router)
 +    return (netdev_get_dev(netdev)->netdev_class->add_router
 +            ? netdev_get_dev(netdev)->netdev_class->add_router(netdev, router)
              : EOPNOTSUPP);
  }
  
@@@ -743,9 -630,9 +743,9 @@@ netdev_get_next_hop(const struct netde
                      const struct in_addr *host, struct in_addr *next_hop,
                      char **netdev_name)
  {
 -    int error = (netdev->netdev_class->get_next_hop
 -                 ? netdev->netdev_class->get_next_hop(host, next_hop,
 -                                                      netdev_name)
 +    int error = (netdev_get_dev(netdev)->netdev_class->get_next_hop
 +                 ? netdev_get_dev(netdev)->netdev_class->get_next_hop(
 +                        host, next_hop, netdev_name)
                   : EOPNOTSUPP);
      if (error) {
          next_hop->s_addr = 0;
@@@ -771,9 -658,8 +771,9 @@@ netdev_get_in6(const struct netdev *net
      struct in6_addr dummy;
      int error;
  
 -    error = (netdev->netdev_class->get_in6
 -             ? netdev->netdev_class->get_in6(netdev, in6 ? in6 : &dummy)
 +    error = (netdev_get_dev(netdev)->netdev_class->get_in6
 +             ? netdev_get_dev(netdev)->netdev_class->get_in6(netdev, 
 +                    in6 ? in6 : &dummy)
               : EOPNOTSUPP);
      if (error && in6) {
          memset(in6, 0, sizeof *in6);
@@@ -793,8 -679,8 +793,8 @@@ do_update_flags(struct netdev *netdev, 
      enum netdev_flags old_flags;
      int error;
  
 -    error = netdev->netdev_class->update_flags(netdev, off & ~on,
 -                                               on, &old_flags);
 +    error = netdev_get_dev(netdev)->netdev_class->update_flags(netdev, 
 +                off & ~on, on, &old_flags);
      if (error) {
          VLOG_WARN_RL(&rl, "failed to %s flags for network device %s: %s",
                       off || on ? "set" : "get", netdev_get_name(netdev),
@@@ -867,9 -753,8 +867,9 @@@ in
  netdev_arp_lookup(const struct netdev *netdev,
                    uint32_t ip, uint8_t mac[ETH_ADDR_LEN])
  {
 -    int error = (netdev->netdev_class->arp_lookup
 -                 ? netdev->netdev_class->arp_lookup(netdev, ip, mac)
 +    int error = (netdev_get_dev(netdev)->netdev_class->arp_lookup
 +                 ? netdev_get_dev(netdev)->netdev_class->arp_lookup(netdev, 
 +                        ip, mac)
                   : EOPNOTSUPP);
      if (error) {
          memset(mac, 0, ETH_ADDR_LEN);
  int
  netdev_get_carrier(const struct netdev *netdev, bool *carrier)
  {
 -    int error = (netdev->netdev_class->get_carrier
 -                 ? netdev->netdev_class->get_carrier(netdev, carrier)
 +    int error = (netdev_get_dev(netdev)->netdev_class->get_carrier
 +                 ? netdev_get_dev(netdev)->netdev_class->get_carrier(netdev, 
 +                        carrier)
                   : EOPNOTSUPP);
      if (error) {
          *carrier = false;
@@@ -899,8 -783,8 +899,8 @@@ netdev_get_stats(const struct netdev *n
      int error;
  
      COVERAGE_INC(netdev_get_stats);
 -    error = (netdev->netdev_class->get_stats
 -             ? netdev->netdev_class->get_stats(netdev, stats)
 +    error = (netdev_get_dev(netdev)->netdev_class->get_stats
 +             ? netdev_get_dev(netdev)->netdev_class->get_stats(netdev, stats)
               : EOPNOTSUPP);
      if (error) {
          memset(stats, 0xff, sizeof *stats);
@@@ -915,9 -799,9 +915,9 @@@ in
  netdev_set_policing(struct netdev *netdev, uint32_t kbits_rate,
                      uint32_t kbits_burst)
  {
 -    return (netdev->netdev_class->set_policing
 -            ? netdev->netdev_class->set_policing(netdev,
 -                                                 kbits_rate, kbits_burst)
 +    return (netdev_get_dev(netdev)->netdev_class->set_policing
 +            ? netdev_get_dev(netdev)->netdev_class->set_policing(netdev, 
 +                    kbits_rate, kbits_burst)
              : EOPNOTSUPP);
  }
  
  int
  netdev_get_vlan_vid(const struct netdev *netdev, int *vlan_vid)
  {
 -    int error = (netdev->netdev_class->get_vlan_vid
 -                 ? netdev->netdev_class->get_vlan_vid(netdev, vlan_vid)
 +    int error = (netdev_get_dev(netdev)->netdev_class->get_vlan_vid
 +                 ? netdev_get_dev(netdev)->netdev_class->get_vlan_vid(netdev, 
 +                        vlan_vid)
                   : ENOENT);
      if (error) {
          *vlan_vid = 0;
@@@ -945,7 -828,7 +945,7 @@@ struct netdev 
  netdev_find_dev_by_in4(const struct in_addr *in4)
  {
      struct netdev *netdev;
 -    struct svec dev_list;
 +    struct svec dev_list = SVEC_EMPTY_INITIALIZER;
      size_t i;
  
      netdev_enumerate(&dev_list);
          const char *name = dev_list.names[i];
          struct in_addr dev_in4;
  
 -        if (!netdev_open(name, NETDEV_ETH_TYPE_NONE, &netdev)
 +        if (!netdev_open_default(name, &netdev)
              && !netdev_get_in4(netdev, &dev_in4, NULL)
              && dev_in4.s_addr == in4->s_addr) {
              goto exit;
@@@ -967,140 -850,46 +967,140 @@@ exit
      return netdev;
  }
  \f
 -/* Initializes 'netdev_obj' as a netdev object named 'name' of the 
 +/* Initializes 'netdev_dev' as a netdev device named 'name' of the
   * specified 'netdev_class'.
   *
 - * This function adds 'netdev_obj' to a netdev-owned shash, so it is
 - * very important that 'netdev_obj' only be freed after calling
 - * netdev_destroy().  */
 + * This function adds 'netdev_dev' to a netdev-owned shash, so it is
 + * very important that 'netdev_dev' only be freed after calling
 + * the refcount drops to zero.  */
  void
 -netdev_obj_init(struct netdev_obj *netdev_obj, const char *name,
 -                const struct netdev_class *netdev_class, bool created)
 +netdev_dev_init(struct netdev_dev *netdev_dev, const char *name,
 +                const struct netdev_class *netdev_class)
  {
 -    assert(!shash_find(&netdev_obj_shash, name));
 +    assert(!shash_find(&netdev_dev_shash, name));
  
 -    netdev_obj->netdev_class = netdev_class;
 -    netdev_obj->ref_cnt = 0;
 -    netdev_obj->created = created;
 -    shash_add(&netdev_obj_shash, name, netdev_obj);
 +    memset(netdev_dev, 0, sizeof *netdev_dev);
 +    netdev_dev->netdev_class = netdev_class;
 +    netdev_dev->name = xstrdup(name);
 +    netdev_dev->node = shash_add(&netdev_dev_shash, name, netdev_dev);
  }
  
 -/* Initializes 'netdev' as a netdev named 'name' of the specified
 - * 'netdev_class'.
 +/* Undoes the results of initialization.
 + *
 + * Normally this function does not need to be called as netdev_close has
 + * the same effect when the refcount drops to zero.
 + * However, it may be called by providers due to an error on creation
 + * that occurs after initialization.  It this case netdev_close() would
 + * never be called. */
 +void
 +netdev_dev_uninit(struct netdev_dev *netdev_dev, bool destroy)
 +{
 +    char *name = netdev_dev->name;
 +
 +    assert(!netdev_dev->ref_cnt);
 +
 +    shash_delete(&netdev_dev_shash, netdev_dev->node);
 +    update_device_args(netdev_dev, NULL);
 +
 +    if (destroy) {
 +        netdev_dev->netdev_class->destroy(netdev_dev);
 +    }
 +    free(name);
 +}
 +
 +/* Returns the class type of 'netdev_dev'.
 + *
 + * The caller must not free the returned value. */
 +const char *
 +netdev_dev_get_type(const struct netdev_dev *netdev_dev)
 +{
 +    return netdev_dev->netdev_class->type;
 +}
 +
 +/* Returns the name of 'netdev_dev'.
 + *
 + * The caller must not free the returned value. */
 +const char *
 +netdev_dev_get_name(const struct netdev_dev *netdev_dev)
 +{
 +    return netdev_dev->name;
 +}
 +
 +/* Returns the netdev_dev with 'name' or NULL if there is none.
 + *
 + * The caller must not free the returned value. */
 +struct netdev_dev *
 +netdev_dev_from_name(const char *name)
 +{
 +    return shash_find_data(&netdev_dev_shash, name);
 +}
 +
 +/* Fills 'device_list' with devices that match 'netdev_class'.
 + *
 + * The caller is responsible for initializing and destroying 'device_list'
 + * but the contained netdev_devs must not be freed. */
 +void
 +netdev_dev_get_devices(const struct netdev_class *netdev_class,
 +                       struct shash *device_list)
 +{
 +    struct shash_node *node;
 +    SHASH_FOR_EACH (node, &netdev_dev_shash) {
 +        struct netdev_dev *dev = node->data;
 +
 +        if (dev->netdev_class == netdev_class) {
 +            shash_add(device_list, node->name, node->data);
 +        }
 +    }
 +}
 +
 +/* Initializes 'netdev' as a instance of the netdev_dev.
   *
   * This function adds 'netdev' to a netdev-owned linked list, so it is very
   * important that 'netdev' only be freed after calling netdev_close(). */
  void
 -netdev_init(struct netdev *netdev, const char *name,
 -            const struct netdev_class *netdev_class)
 +netdev_init(struct netdev *netdev, struct netdev_dev *netdev_dev)
  {
 -    netdev->netdev_class = netdev_class;
 -    netdev->name = xstrdup(name);
 -    netdev->save_flags = 0;
 -    netdev->changed_flags = 0;
 +    memset(netdev, 0, sizeof *netdev);
 +    netdev->netdev_dev = netdev_dev;
      list_push_back(&netdev_list, &netdev->node);
  }
  
 +/* Undoes the results of initialization.
 + *
 + * Normally this function only needs to be called from netdev_close().
 + * However, it may be called by providers due to an error on opening
 + * that occurs after initialization.  It this case netdev_close() would
 + * never be called. */
 +void
 +netdev_uninit(struct netdev *netdev, bool close)
 +{
 +    /* Restore flags that we changed, if any. */
 +    int error = restore_flags(netdev);
 +    list_remove(&netdev->node);
 +    if (error) {
 +        VLOG_WARN("failed to restore network device flags on %s: %s",
 +                  netdev_get_name(netdev), strerror(error));
 +    }
 +
 +    if (close) {
 +        netdev_get_dev(netdev)->netdev_class->close(netdev);
 +    }
 +}
 +
 +
  /* Returns the class type of 'netdev'.  
   *
   * The caller must not free the returned value. */
 -const char *netdev_get_type(const struct netdev *netdev)
 +const char *
 +netdev_get_type(const struct netdev *netdev)
  {
 -    return netdev->netdev_class->type;
 +    return netdev_get_dev(netdev)->netdev_class->type;
 +}
 +
 +struct netdev_dev *
 +netdev_get_dev(const struct netdev *netdev)
 +{
 +    return netdev->netdev_dev;
  }
  
  /* Initializes 'notifier' as a netdev notifier for 'netdev', for which
@@@ -1140,8 -929,7 +1140,8 @@@ netdev_monitor_destroy(struct netdev_mo
  
          SHASH_FOR_EACH (node, &monitor->polled_netdevs) {
              struct netdev_notifier *notifier = node->data;
 -            notifier->netdev->netdev_class->poll_remove(notifier);
 +            netdev_get_dev(notifier->netdev)->netdev_class->poll_remove(
 +                    notifier);
          }
  
          shash_destroy(&monitor->polled_netdevs);
@@@ -1171,11 -959,11 +1171,11 @@@ netdev_monitor_add(struct netdev_monito
      const char *netdev_name = netdev_get_name(netdev);
      int error = 0;
      if (!shash_find(&monitor->polled_netdevs, netdev_name)
 -        && netdev->netdev_class->poll_add)
 +            && netdev_get_dev(netdev)->netdev_class->poll_add)
      {
          struct netdev_notifier *notifier;
 -        error = netdev->netdev_class->poll_add(netdev, netdev_monitor_cb,
 -                                               monitor, &notifier);
 +        error = netdev_get_dev(netdev)->netdev_class->poll_add(netdev,
 +                    netdev_monitor_cb, monitor, &notifier);
          if (!error) {
              assert(notifier->netdev == netdev);
              shash_add(&monitor->polled_netdevs, netdev_name, notifier);
@@@ -1197,7 -985,7 +1197,7 @@@ netdev_monitor_remove(struct netdev_mon
      if (node) {
          /* Cancel future notifications. */
          struct netdev_notifier *notifier = node->data;
 -        netdev->netdev_class->poll_remove(notifier);
 +        netdev_get_dev(netdev)->netdev_class->poll_remove(notifier);
          shash_delete(&monitor->polled_netdevs, node);
  
          /* Drop any pending notification. */
@@@ -1255,20 -1043,21 +1255,20 @@@ restore_flags(struct netdev *netdev
      if (netdev->changed_flags) {
          enum netdev_flags restore = netdev->save_flags & netdev->changed_flags;
          enum netdev_flags old_flags;
 -        return netdev->netdev_class->update_flags(netdev,
 -                                                  netdev->changed_flags
 -                                                  & ~restore,
 -                                                  restore, &old_flags);
 +        return netdev_get_dev(netdev)->netdev_class->update_flags(netdev,
 +                                           netdev->changed_flags & ~restore,
 +                                           restore, &old_flags);
      }
      return 0;
  }
  
 -/* Retores all the flags on all network devices that we modified.  Called from
 - * a signal handler, so it does not attempt to report error conditions. */
 +/* Close all netdevs on shutdown so they can do any needed cleanup such as
 + * destroying devices, restoring flags, etc. */
  static void
- close_all_netdevs(void *aux UNUSED)
 -restore_all_flags(void *aux OVS_UNUSED)
++close_all_netdevs(void *aux OVS_UNUSED)
  {
 -    struct netdev *netdev;
 -    LIST_FOR_EACH (netdev, struct netdev, node, &netdev_list) {
 -        restore_flags(netdev);
 +    struct netdev *netdev, *next;
 +    LIST_FOR_EACH_SAFE(netdev, next, struct netdev, node, &netdev_list) {
 +        netdev_close(netdev);
      }
  }
diff --combined lib/netdev.h
@@@ -1,5 -1,5 +1,5 @@@
  /*
 - * Copyright (c) 2008, 2009 Nicira Networks.
 + * Copyright (c) 2008, 2009, 2010 Nicira Networks.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
  #include <stddef.h>
  #include <stdint.h>
  
+ #ifdef  __cplusplus
+ extern "C" {
+ #endif
  /* Generic interface to network devices.
   *
   * Currently, there is a single implementation of this interface that supports
@@@ -76,28 -80,18 +80,28 @@@ struct netdev_stats 
      uint64_t tx_window_errors;
  };
  
 +struct netdev_options {
 +    const char *name;
 +    const char *type;
 +    const struct shash *args;
 +    int ethertype;
 +    bool may_create;
 +    bool may_open;
 +};
 +
  struct netdev;
 +struct netdev_class;
  
 -int netdev_initialize(void);
  void netdev_run(void);
  void netdev_wait(void);
  
 -int netdev_create(const char *name, const char *type, 
 -                  const struct shash *args);
 -int netdev_destroy(const char *name);
 -int netdev_reconfigure(const char *name, const struct shash *args);
 +int netdev_register_provider(const struct netdev_class *);
 +int netdev_unregister_provider(const char *type);
 +void netdev_enumerate_types(struct svec *types);
  
 -int netdev_open(const char *name, int ethertype, struct netdev **);
 +int netdev_open(struct netdev_options *, struct netdev **);
 +int netdev_open_default(const char *name, struct netdev **);
 +int netdev_reconfigure(struct netdev *, const struct shash *args);
  void netdev_close(struct netdev *);
  
  bool netdev_exists(const char *name);
  int netdev_enumerate(struct svec *);
  
  const char *netdev_get_name(const struct netdev *);
 +const char *netdev_get_type(const struct netdev *);
  int netdev_get_mtu(const struct netdev *, int *mtup);
  int netdev_get_ifindex(const struct netdev *);
  
@@@ -155,4 -148,8 +159,8 @@@ void netdev_monitor_remove(struct netde
  int netdev_monitor_poll(struct netdev_monitor *, char **devnamep);
  void netdev_monitor_poll_wait(const struct netdev_monitor *);
  
+ #ifdef  __cplusplus
+ }
+ #endif
  #endif /* netdev.h */
diff --combined lib/packets.h
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * Copyright (c) 2008, 2009 Nicira Networks.
+  * Copyright (c) 2008, 2009, 2010 Nicira Networks.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
  
  struct ofpbuf;
  
 +bool dpid_from_string(const char *s, uint64_t *dpidp);
 +
  #define ETH_ADDR_LEN           6
  
- static const uint8_t eth_addr_broadcast[ETH_ADDR_LEN] UNUSED
+ static const uint8_t eth_addr_broadcast[ETH_ADDR_LEN] OVS_UNUSED
      = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  
  static inline bool eth_addr_is_broadcast(const uint8_t ea[6])
      return (ea[0] & ea[1] & ea[2] & ea[3] & ea[4] & ea[5]) == 0xff;
  }
  
 -/* Returns true if 'ea' is an Ethernet address used for virtual interfaces
 - * under XenServer.  Generally the actual Ethernet address is FE:FF:FF:FF:FF:FF
 - * but it can be FE:FE:FE:FE:FE:FE in some cases. */
 -static inline bool eth_addr_is_vif(const uint8_t ea[6])
 -{
 -    return ea[0] == 0xfe && (ea[1] & ea[2] & ea[3] & ea[4] & ea[5]) >= 0xfe;
 -}
 -
  static inline bool eth_addr_is_multicast(const uint8_t ea[6])
  {
      return ea[0] & 1;
  }
  static inline bool eth_addr_is_local(const uint8_t ea[6]) 
  {
 -    return ea[0] & 2;
 +    /* Local if it is either a locally administered address or a Nicira random
 +     * address. */
 +    return !!(ea[0] & 2)
 +       || (ea[0] == 0x00 && ea[1] == 0x23 && ea[2] == 0x20 && !!(ea[3] & 0x80));
  }
  static inline bool eth_addr_is_zero(const uint8_t ea[6]) 
  {
@@@ -86,18 -89,6 +86,18 @@@ static inline void eth_addr_random(uint
      random_bytes(ea, ETH_ADDR_LEN);
      eth_addr_mark_random(ea);
  }
 +static inline void eth_addr_nicira_random(uint8_t ea[ETH_ADDR_LEN])
 +{
 +    eth_addr_random(ea);
 +
 +    /* Set the OUI to the Nicira one. */
 +    ea[0] = 0x00;
 +    ea[1] = 0x23;
 +    ea[2] = 0x20;
 +
 +    /* Set the top bit to indicate random Nicira address. */
 +    ea[3] |= 0x80;
 +}
  /* Returns true if 'ea' is a reserved multicast address, that a bridge must
   * never forward, false otherwise. */
  static inline bool eth_addr_is_reserved(const uint8_t ea[ETH_ADDR_LEN])
              && (ea[5] & 0xf0) == 0x00);
  }
  
 +bool eth_addr_from_string(const char *, uint8_t ea[ETH_ADDR_LEN]);
 +
  void compose_benign_packet(struct ofpbuf *, const char *tag,
                             uint16_t snap_type,
                             const uint8_t eth_src[ETH_ADDR_LEN]);
diff --combined lib/process.c
@@@ -55,7 -55,7 +55,7 @@@ static struct list all_processes = LIST
  static bool sigchld_is_blocked(void);
  static void block_sigchld(sigset_t *);
  static void unblock_sigchld(const sigset_t *);
- static void sigchld_handler(int signr UNUSED);
+ static void sigchld_handler(int signr OVS_UNUSED);
  static bool is_member(int x, const int *array, size_t);
  
  /* Initializes the process subsystem (if it is not already initialized).  Calls
@@@ -161,7 -161,7 +161,7 @@@ process_register(const char *name, pid_
  
      assert(sigchld_is_blocked());
  
 -    p = xcalloc(1, sizeof *p);
 +    p = xzalloc(sizeof *p);
      p->pid = pid;
      slash = strrchr(name, '/');
      p->name = xstrdup(slash ? slash + 1 : name);
@@@ -201,14 -201,17 +201,14 @@@ process_start(char **argv
      }
  
      block_sigchld(&oldsigs);
 -    fatal_signal_block();
      pid = fork();
      if (pid < 0) {
 -        fatal_signal_unblock();
          unblock_sigchld(&oldsigs);
          VLOG_WARN("fork failed: %s", strerror(errno));
          return errno;
      } else if (pid) {
          /* Running in parent process. */
          *pp = process_register(argv[0], pid);
 -        fatal_signal_unblock();
          unblock_sigchld(&oldsigs);
          return 0;
      } else {
          int fd;
  
          fatal_signal_fork();
 -        fatal_signal_unblock();
          unblock_sigchld(&oldsigs);
          for (fd = 0; fd < fd_max; fd++) {
              if (is_member(fd, null_fds, n_null_fds)) {
@@@ -285,7 -289,7 +285,7 @@@ process_exited(struct process *p
          return true;
      } else {
          char buf[_POSIX_PIPE_BUF];
 -        read(fds[0], buf, sizeof buf);
 +        ignore(read(fds[0], buf, sizeof buf));
          return false;
      }
  }
@@@ -415,13 -419,15 +415,13 @@@ stream_open(struct stream *s
  static void
  stream_read(struct stream *s)
  {
 -    int error = 0;
 -
      if (s->fds[0] < 0) {
          return;
      }
  
 -    error = 0;
      for (;;) {
          char buffer[512];
 +        int error;
          size_t n;
  
          error = read_fully(s->fds[0], buffer, sizeof buffer, &n);
@@@ -515,10 -521,12 +515,10 @@@ process_run_capture(char **argv, char *
      }
  
      block_sigchld(&oldsigs);
 -    fatal_signal_block();
      pid = fork();
      if (pid < 0) {
          int error = errno;
  
 -        fatal_signal_unblock();
          unblock_sigchld(&oldsigs);
          VLOG_WARN("fork failed: %s", strerror(error));
  
          struct process *p;
  
          p = process_register(argv[0], pid);
 -        fatal_signal_unblock();
          unblock_sigchld(&oldsigs);
  
          close(s_stdout.fds[1]);
          int i;
  
          fatal_signal_fork();
 -        fatal_signal_unblock();
          unblock_sigchld(&oldsigs);
  
          dup2(get_null_fd(), 0);
  }
  \f
  static void
- sigchld_handler(int signr UNUSED)
+ sigchld_handler(int signr OVS_UNUSED)
  {
      struct process *p;
  
              }
          }
      }
 -    write(fds[1], "", 1);
 +    ignore(write(fds[1], "", 1));
  }
  
  static bool
diff --combined lib/rconn.c
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * Copyright (c) 2008, 2009 Nicira Networks.
+  * Copyright (c) 2008, 2009, 2010 Nicira Networks.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
@@@ -176,7 -176,7 +176,7 @@@ rconn_new_from_vconn(const char *name, 
  struct rconn *
  rconn_create(int probe_interval, int max_backoff)
  {
 -    struct rconn *rc = xcalloc(1, sizeof *rc);
 +    struct rconn *rc = xzalloc(sizeof *rc);
  
      rc->state = S_VOID;
      rc->state_entered = time_now();
@@@ -313,13 -313,13 +313,13 @@@ rconn_destroy(struct rconn *rc
  }
  
  static unsigned int
- timeout_VOID(const struct rconn *rc UNUSED)
+ timeout_VOID(const struct rconn *rc OVS_UNUSED)
  {
      return UINT_MAX;
  }
  
  static void
- run_VOID(struct rconn *rc UNUSED)
+ run_VOID(struct rconn *rc OVS_UNUSED)
  {
      /* Nothing to do. */
  }
@@@ -459,15 -459,6 +459,15 @@@ voi
  rconn_run(struct rconn *rc)
  {
      int old_state;
 +    size_t i;
 +
 +    if (rc->vconn) {
 +        vconn_run(rc->vconn);
 +    }
 +    for (i = 0; i < rc->n_monitors; i++) {
 +        vconn_run(rc->monitors[i]);
 +    }
 +
      do {
          old_state = rc->state;
          switch (rc->state) {
  void
  rconn_run_wait(struct rconn *rc)
  {
 -    unsigned int timeo = timeout(rc);
 +    unsigned int timeo;
 +    size_t i;
 +
 +    if (rc->vconn) {
 +        vconn_run_wait(rc->vconn);
 +    }
 +    for (i = 0; i < rc->n_monitors; i++) {
 +        vconn_run_wait(rc->monitors[i]);
 +    }
 +
 +    timeo = timeout(rc);
      if (timeo != UINT_MAX) {
          unsigned int expires = sat_add(rc->state_entered, timeo);
          unsigned int remaining = sat_sub(expires, time_now());
@@@ -566,7 -547,7 +566,7 @@@ rconn_send(struct rconn *rc, struct ofp
      if (rconn_is_connected(rc)) {
          COVERAGE_INC(rconn_queued);
          copy_to_monitor(rc, b);
-         b->private = counter;
+         b->private_p = counter;
          if (counter) {
              rconn_packet_counter_inc(counter);
          }
@@@ -864,7 -845,7 +864,7 @@@ try_send(struct rconn *rc
  {
      int retval = 0;
      struct ofpbuf *next = rc->txq.head->next;
-     struct rconn_packet_counter *counter = rc->txq.head->private;
+     struct rconn_packet_counter *counter = rc->txq.head->private_p;
      retval = vconn_send(rc->vconn, rc->txq.head);
      if (retval) {
          if (retval != EAGAIN) {
@@@ -933,7 -914,7 +933,7 @@@ flush_queue(struct rconn *rc
      }
      while (rc->txq.n > 0) {
          struct ofpbuf *b = queue_pop_head(&rc->txq);
-         struct rconn_packet_counter *counter = b->private;
+         struct rconn_packet_counter *counter = b->private_p;
          if (counter) {
              rconn_packet_counter_dec(counter);
          }
diff --combined lib/shash.h
@@@ -1,5 -1,5 +1,5 @@@
  /*
 - * Copyright (c) 2009 Nicira Networks.
 + * Copyright (c) 2009, 2010 Nicira Networks.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
  
  #include "hmap.h"
  
+ #ifdef  __cplusplus
+ extern "C" {
+ #endif
  struct shash_node {
      struct hmap_node node;
      char *name;
@@@ -40,19 -44,16 +44,23 @@@ struct shash 
  
  void shash_init(struct shash *);
  void shash_destroy(struct shash *);
 +void shash_swap(struct shash *, struct shash *);
 +void shash_moved(struct shash *);
  void shash_clear(struct shash *);
  bool shash_is_empty(const struct shash *);
 -struct shash_node *shash_add(struct shash *, const char *, void *);
 +size_t shash_count(const struct shash *);
 +struct shash_node *shash_add(struct shash *, const char *, const void *);
 +bool shash_add_once(struct shash *, const char *, const void *);
  void shash_delete(struct shash *, struct shash_node *);
  struct shash_node *shash_find(const struct shash *, const char *);
  void *shash_find_data(const struct shash *, const char *);
 +void *shash_find_and_delete(struct shash *, const char *);
  struct shash_node *shash_first(const struct shash *);
 +const struct shash_node **shash_sort(const struct shash *);
 +bool shash_equal_keys(const struct shash *, const struct shash *);
  
+ #ifdef  __cplusplus
+ }
+ #endif
  #endif /* shash.h */
diff --combined lib/socket-util.c
@@@ -204,7 -204,7 +204,7 @@@ make_sockaddr_un(const char *name, stru
   *
   * Returns the socket's fd if successful, otherwise a negative errno value. */
  int
- make_unix_socket(int style, bool nonblock, bool passcred UNUSED,
+ make_unix_socket(int style, bool nonblock, bool passcred OVS_UNUSED,
                   const char *bind_path, const char *connect_path)
  {
      int error;
          make_sockaddr_un(connect_path, &un, &un_len);
          if (connect(fd, (struct sockaddr*) &un, un_len)
              && errno != EINPROGRESS) {
 +            printf("connect failed with %s\n", strerror(errno));
              goto error;
          }
      }
      return fd;
  
  error:
 +    error = errno == EAGAIN ? EPROTO : errno;
      if (bind_path) {
          fatal_signal_remove_file_to_unlink(bind_path);
      }
 -    error = errno;
      close(fd);
      return -error;
  }
@@@ -385,35 -384,25 +385,35 @@@ exit
  
  /* Opens a non-blocking IPv4 socket of the specified 'style', binds to
   * 'target', and listens for incoming connections.  'target' should be a string
 - * in the format "[<port>][:<ip>]".  <port> may be omitted if 'default_port' is
 - * nonzero, in which case it defaults to 'default_port'.  If <ip> is omitted it
 - * defaults to the wildcard IP address.
 + * in the format "[<port>][:<ip>]":
 + *
 + *      - If 'default_port' is -1, then <port> is required.  Otherwise, if
 + *        <port> is omitted, then 'default_port' is used instead.
 + *
 + *      - If <port> (or 'default_port', if used) is 0, then no port is bound
 + *        and the TCP/IP stack will select a port.
 + *
 + *      - If <ip> is omitted then the IP address is wildcarded.
   *
   * 'style' should be SOCK_STREAM (for TCP) or SOCK_DGRAM (for UDP).
   *
   * For TCP, the socket will have SO_REUSEADDR turned on.
   *
   * On success, returns a non-negative file descriptor.  On failure, returns a
 - * negative errno value. */
 + * negative errno value.
 + *
 + * If 'sinp' is non-null, then on success the bound address is stored into
 + * '*sinp'. */
  int
 -inet_open_passive(int style, const char *target_, uint16_t default_port)
 +inet_open_passive(int style, const char *target_, int default_port,
 +                  struct sockaddr_in *sinp)
  {
      char *target = xstrdup(target_);
      char *string_ptr = target;
      struct sockaddr_in sin;
      const char *host_name;
      const char *port_string;
 -    int fd, error;
 +    int fd, error, port;
      unsigned int yes  = 1;
  
      /* Address defaults. */
  
      /* Parse optional port number. */
      port_string = strsep(&string_ptr, ":");
 -    if (port_string && atoi(port_string)) {
 -        sin.sin_port = htons(atoi(port_string));
 -    } else if (!default_port) {
 +    if (port_string && str_to_int(port_string, 10, &port)) {
 +        sin.sin_port = htons(port);
 +    } else if (default_port < 0) {
          VLOG_ERR("%s: port number must be specified", target_);
          error = EAFNOSUPPORT;
          goto exit;
          VLOG_ERR("%s: listen: %s", target_, strerror(error));
          goto exit_close;
      }
 +
 +    if (sinp) {
 +        socklen_t sin_len = sizeof sin;
 +        if (getsockname(fd, (struct sockaddr *) &sin, &sin_len) < 0){
 +            error = errno;
 +            VLOG_ERR("%s: getsockname: %s", target_, strerror(error));
 +            goto exit_close;
 +        }
 +        if (sin.sin_family != AF_INET || sin_len != sizeof sin) {
 +            VLOG_ERR("%s: getsockname: invalid socket name", target_);
 +            goto exit_close;
 +        }
 +        *sinp = sin;
 +    }
 +
      error = 0;
      goto exit;
  
diff --combined lib/stream-ssl.c
index 941f779,0000000..51ce306
mode 100644,000000..100644
--- /dev/null
@@@ -1,1089 -1,0 +1,1089 @@@
- static DH *tmp_dh_callback(SSL *ssl, int is_export UNUSED, int keylength);
 +/*
 + * Copyright (c) 2008, 2009, 2010 Nicira Networks.
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at:
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <config.h>
 +#include "stream-ssl.h"
 +#include "dhparams.h"
 +#include <assert.h>
 +#include <ctype.h>
 +#include <errno.h>
 +#include <inttypes.h>
 +#include <string.h>
 +#include <netinet/tcp.h>
 +#include <openssl/err.h>
 +#include <openssl/ssl.h>
 +#include <openssl/x509v3.h>
 +#include <poll.h>
 +#include <sys/fcntl.h>
 +#include <sys/stat.h>
 +#include <unistd.h>
 +#include "dynamic-string.h"
 +#include "leak-checker.h"
 +#include "ofpbuf.h"
 +#include "openflow/openflow.h"
 +#include "packets.h"
 +#include "poll-loop.h"
 +#include "socket-util.h"
 +#include "socket-util.h"
 +#include "util.h"
 +#include "stream-provider.h"
 +#include "stream.h"
 +
 +#include "vlog.h"
 +#define THIS_MODULE VLM_stream_ssl
 +
 +/* Active SSL. */
 +
 +enum ssl_state {
 +    STATE_TCP_CONNECTING,
 +    STATE_SSL_CONNECTING
 +};
 +
 +enum session_type {
 +    CLIENT,
 +    SERVER
 +};
 +
 +struct ssl_stream
 +{
 +    struct stream stream;
 +    enum ssl_state state;
 +    int connect_error;
 +    enum session_type type;
 +    int fd;
 +    SSL *ssl;
 +    struct ofpbuf *txbuf;
 +
 +    /* rx_want and tx_want record the result of the last call to SSL_read()
 +     * and SSL_write(), respectively:
 +     *
 +     *    - If the call reported that data needed to be read from the file
 +     *      descriptor, the corresponding member is set to SSL_READING.
 +     *
 +     *    - If the call reported that data needed to be written to the file
 +     *      descriptor, the corresponding member is set to SSL_WRITING.
 +     *
 +     *    - Otherwise, the member is set to SSL_NOTHING, indicating that the
 +     *      call completed successfully (or with an error) and that there is no
 +     *      need to block.
 +     *
 +     * These are needed because there is no way to ask OpenSSL what a data read
 +     * or write would require without giving it a buffer to receive into or
 +     * data to send, respectively.  (Note that the SSL_want() status is
 +     * overwritten by each SSL_read() or SSL_write() call, so we can't rely on
 +     * its value.)
 +     *
 +     * A single call to SSL_read() or SSL_write() can perform both reading
 +     * and writing and thus invalidate not one of these values but actually
 +     * both.  Consider this situation, for example:
 +     *
 +     *    - SSL_write() blocks on a read, so tx_want gets SSL_READING.
 +     *
 +     *    - SSL_read() laters succeeds reading from 'fd' and clears out the
 +     *      whole receive buffer, so rx_want gets SSL_READING.
 +     *
 +     *    - Client calls stream_wait(STREAM_RECV) and stream_wait(STREAM_SEND)
 +     *      and blocks.
 +     *
 +     *    - Now we're stuck blocking until the peer sends us data, even though
 +     *      SSL_write() could now succeed, which could easily be a deadlock
 +     *      condition.
 +     *
 +     * On the other hand, we can't reset both tx_want and rx_want on every call
 +     * to SSL_read() or SSL_write(), because that would produce livelock,
 +     * e.g. in this situation:
 +     *
 +     *    - SSL_write() blocks, so tx_want gets SSL_READING or SSL_WRITING.
 +     *
 +     *    - SSL_read() blocks, so rx_want gets SSL_READING or SSL_WRITING,
 +     *      but tx_want gets reset to SSL_NOTHING.
 +     *
 +     *    - Client calls stream_wait(STREAM_RECV) and stream_wait(STREAM_SEND)
 +     *      and blocks.
 +     *
 +     *    - Client wakes up immediately since SSL_NOTHING in tx_want indicates
 +     *      that no blocking is necessary.
 +     *
 +     * The solution we adopt here is to set tx_want to SSL_NOTHING after
 +     * calling SSL_read() only if the SSL state of the connection changed,
 +     * which indicates that an SSL-level renegotiation made some progress, and
 +     * similarly for rx_want and SSL_write().  This prevents both the
 +     * deadlock and livelock situations above.
 +     */
 +    int rx_want, tx_want;
 +};
 +
 +/* SSL context created by ssl_init(). */
 +static SSL_CTX *ctx;
 +
 +/* Required configuration. */
 +static bool has_private_key, has_certificate, has_ca_cert;
 +
 +/* Ordinarily, we require a CA certificate for the peer to be locally
 + * available.  'has_ca_cert' is true when this is the case, and neither of the
 + * following variables matter.
 + *
 + * We can, however, bootstrap the CA certificate from the peer at the beginning
 + * of our first connection then use that certificate on all subsequent
 + * connections, saving it to a file for use in future runs also.  In this case,
 + * 'has_ca_cert' is false, 'bootstrap_ca_cert' is true, and 'ca_cert_file'
 + * names the file to be saved. */
 +static bool bootstrap_ca_cert;
 +static char *ca_cert_file;
 +
 +/* Who knows what can trigger various SSL errors, so let's throttle them down
 + * quite a bit. */
 +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25);
 +
 +static int ssl_init(void);
 +static int do_ssl_init(void);
 +static bool ssl_wants_io(int ssl_error);
 +static void ssl_close(struct stream *);
 +static void ssl_clear_txbuf(struct ssl_stream *);
 +static int interpret_ssl_error(const char *function, int ret, int error,
 +                               int *want);
- pssl_open(const char *name UNUSED, char *suffix, struct pstream **pstreamp)
++static DH *tmp_dh_callback(SSL *ssl, int is_export OVS_UNUSED, int keylength);
 +static void log_ca_cert(const char *file_name, X509 *cert);
 +
 +static short int
 +want_to_poll_events(int want)
 +{
 +    switch (want) {
 +    case SSL_NOTHING:
 +        NOT_REACHED();
 +
 +    case SSL_READING:
 +        return POLLIN;
 +
 +    case SSL_WRITING:
 +        return POLLOUT;
 +
 +    default:
 +        NOT_REACHED();
 +    }
 +}
 +
 +static int
 +new_ssl_stream(const char *name, int fd, enum session_type type,
 +              enum ssl_state state, const struct sockaddr_in *remote,
 +              struct stream **streamp)
 +{
 +    struct sockaddr_in local;
 +    socklen_t local_len = sizeof local;
 +    struct ssl_stream *sslv;
 +    SSL *ssl = NULL;
 +    int on = 1;
 +    int retval;
 +
 +    /* Check for all the needful configuration. */
 +    retval = 0;
 +    if (!has_private_key) {
 +        VLOG_ERR("Private key must be configured to use SSL");
 +        retval = ENOPROTOOPT;
 +    }
 +    if (!has_certificate) {
 +        VLOG_ERR("Certificate must be configured to use SSL");
 +        retval = ENOPROTOOPT;
 +    }
 +    if (!has_ca_cert && !bootstrap_ca_cert) {
 +        VLOG_ERR("CA certificate must be configured to use SSL");
 +        retval = ENOPROTOOPT;
 +    }
 +    if (!SSL_CTX_check_private_key(ctx)) {
 +        VLOG_ERR("Private key does not match certificate public key: %s",
 +                 ERR_error_string(ERR_get_error(), NULL));
 +        retval = ENOPROTOOPT;
 +    }
 +    if (retval) {
 +        goto error;
 +    }
 +
 +    /* Get the local IP and port information */
 +    retval = getsockname(fd, (struct sockaddr *) &local, &local_len);
 +    if (retval) {
 +        memset(&local, 0, sizeof local);
 +    }
 +
 +    /* Disable Nagle. */
 +    retval = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof on);
 +    if (retval) {
 +        VLOG_ERR("%s: setsockopt(TCP_NODELAY): %s", name, strerror(errno));
 +        retval = errno;
 +        goto error;
 +    }
 +
 +    /* Create and configure OpenSSL stream. */
 +    ssl = SSL_new(ctx);
 +    if (ssl == NULL) {
 +        VLOG_ERR("SSL_new: %s", ERR_error_string(ERR_get_error(), NULL));
 +        retval = ENOPROTOOPT;
 +        goto error;
 +    }
 +    if (SSL_set_fd(ssl, fd) == 0) {
 +        VLOG_ERR("SSL_set_fd: %s", ERR_error_string(ERR_get_error(), NULL));
 +        retval = ENOPROTOOPT;
 +        goto error;
 +    }
 +    if (bootstrap_ca_cert && type == CLIENT) {
 +        SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
 +    }
 +
 +    /* Create and return the ssl_stream. */
 +    sslv = xmalloc(sizeof *sslv);
 +    stream_init(&sslv->stream, &ssl_stream_class, EAGAIN, name);
 +    stream_set_remote_ip(&sslv->stream, remote->sin_addr.s_addr);
 +    stream_set_remote_port(&sslv->stream, remote->sin_port);
 +    stream_set_local_ip(&sslv->stream, local.sin_addr.s_addr);
 +    stream_set_local_port(&sslv->stream, local.sin_port);
 +    sslv->state = state;
 +    sslv->type = type;
 +    sslv->fd = fd;
 +    sslv->ssl = ssl;
 +    sslv->txbuf = NULL;
 +    sslv->rx_want = sslv->tx_want = SSL_NOTHING;
 +    *streamp = &sslv->stream;
 +    return 0;
 +
 +error:
 +    if (ssl) {
 +        SSL_free(ssl);
 +    }
 +    close(fd);
 +    return retval;
 +}
 +
 +static struct ssl_stream *
 +ssl_stream_cast(struct stream *stream)
 +{
 +    stream_assert_class(stream, &ssl_stream_class);
 +    return CONTAINER_OF(stream, struct ssl_stream, stream);
 +}
 +
 +static int
 +ssl_open(const char *name, char *suffix, struct stream **streamp)
 +{
 +    struct sockaddr_in sin;
 +    int error, fd;
 +
 +    error = ssl_init();
 +    if (error) {
 +        return error;
 +    }
 +
 +    error = inet_open_active(SOCK_STREAM, suffix, OFP_SSL_PORT, &sin, &fd);
 +    if (fd >= 0) {
 +        int state = error ? STATE_TCP_CONNECTING : STATE_SSL_CONNECTING;
 +        return new_ssl_stream(name, fd, CLIENT, state, &sin, streamp);
 +    } else {
 +        VLOG_ERR("%s: connect: %s", name, strerror(error));
 +        return error;
 +    }
 +}
 +
 +static int
 +do_ca_cert_bootstrap(struct stream *stream)
 +{
 +    struct ssl_stream *sslv = ssl_stream_cast(stream);
 +    STACK_OF(X509) *chain;
 +    X509 *ca_cert;
 +    FILE *file;
 +    int error;
 +    int fd;
 +
 +    chain = SSL_get_peer_cert_chain(sslv->ssl);
 +    if (!chain || !sk_X509_num(chain)) {
 +        VLOG_ERR("could not bootstrap CA cert: no certificate presented by "
 +                 "peer");
 +        return EPROTO;
 +    }
 +    ca_cert = sk_X509_value(chain, sk_X509_num(chain) - 1);
 +
 +    /* Check that 'ca_cert' is self-signed.  Otherwise it is not a CA
 +     * certificate and we should not attempt to use it as one. */
 +    error = X509_check_issued(ca_cert, ca_cert);
 +    if (error) {
 +        VLOG_ERR("could not bootstrap CA cert: obtained certificate is "
 +                 "not self-signed (%s)",
 +                 X509_verify_cert_error_string(error));
 +        if (sk_X509_num(chain) < 2) {
 +            VLOG_ERR("only one certificate was received, so probably the peer "
 +                     "is not configured to send its CA certificate");
 +        }
 +        return EPROTO;
 +    }
 +
 +    fd = open(ca_cert_file, O_CREAT | O_EXCL | O_WRONLY, 0444);
 +    if (fd < 0) {
 +        VLOG_ERR("could not bootstrap CA cert: creating %s failed: %s",
 +                 ca_cert_file, strerror(errno));
 +        return errno;
 +    }
 +
 +    file = fdopen(fd, "w");
 +    if (!file) {
 +        int error = errno;
 +        VLOG_ERR("could not bootstrap CA cert: fdopen failed: %s",
 +                 strerror(error));
 +        unlink(ca_cert_file);
 +        return error;
 +    }
 +
 +    if (!PEM_write_X509(file, ca_cert)) {
 +        VLOG_ERR("could not bootstrap CA cert: PEM_write_X509 to %s failed: "
 +                 "%s", ca_cert_file, ERR_error_string(ERR_get_error(), NULL));
 +        fclose(file);
 +        unlink(ca_cert_file);
 +        return EIO;
 +    }
 +
 +    if (fclose(file)) {
 +        int error = errno;
 +        VLOG_ERR("could not bootstrap CA cert: writing %s failed: %s",
 +                 ca_cert_file, strerror(error));
 +        unlink(ca_cert_file);
 +        return error;
 +    }
 +
 +    VLOG_INFO("successfully bootstrapped CA cert to %s", ca_cert_file);
 +    log_ca_cert(ca_cert_file, ca_cert);
 +    bootstrap_ca_cert = false;
 +    has_ca_cert = true;
 +
 +    /* SSL_CTX_add_client_CA makes a copy of ca_cert's relevant data. */
 +    SSL_CTX_add_client_CA(ctx, ca_cert);
 +
 +    /* SSL_CTX_use_certificate() takes ownership of the certificate passed in.
 +     * 'ca_cert' is owned by sslv->ssl, so we need to duplicate it. */
 +    ca_cert = X509_dup(ca_cert);
 +    if (!ca_cert) {
 +        out_of_memory();
 +    }
 +    if (SSL_CTX_load_verify_locations(ctx, ca_cert_file, NULL) != 1) {
 +        VLOG_ERR("SSL_CTX_load_verify_locations: %s",
 +                 ERR_error_string(ERR_get_error(), NULL));
 +        return EPROTO;
 +    }
 +    VLOG_INFO("killing successful connection to retry using CA cert");
 +    return EPROTO;
 +}
 +
 +static int
 +ssl_connect(struct stream *stream)
 +{
 +    struct ssl_stream *sslv = ssl_stream_cast(stream);
 +    int retval;
 +
 +    switch (sslv->state) {
 +    case STATE_TCP_CONNECTING:
 +        retval = check_connection_completion(sslv->fd);
 +        if (retval) {
 +            return retval;
 +        }
 +        sslv->state = STATE_SSL_CONNECTING;
 +        /* Fall through. */
 +
 +    case STATE_SSL_CONNECTING:
 +        retval = (sslv->type == CLIENT
 +                   ? SSL_connect(sslv->ssl) : SSL_accept(sslv->ssl));
 +        if (retval != 1) {
 +            int error = SSL_get_error(sslv->ssl, retval);
 +            if (retval < 0 && ssl_wants_io(error)) {
 +                return EAGAIN;
 +            } else {
 +                int unused;
 +                interpret_ssl_error((sslv->type == CLIENT ? "SSL_connect"
 +                                     : "SSL_accept"), retval, error, &unused);
 +                shutdown(sslv->fd, SHUT_RDWR);
 +                return EPROTO;
 +            }
 +        } else if (bootstrap_ca_cert) {
 +            return do_ca_cert_bootstrap(stream);
 +        } else if ((SSL_get_verify_mode(sslv->ssl)
 +                    & (SSL_VERIFY_NONE | SSL_VERIFY_PEER))
 +                   != SSL_VERIFY_PEER) {
 +            /* Two or more SSL connections completed at the same time while we
 +             * were in bootstrap mode.  Only one of these can finish the
 +             * bootstrap successfully.  The other one(s) must be rejected
 +             * because they were not verified against the bootstrapped CA
 +             * certificate.  (Alternatively we could verify them against the CA
 +             * certificate, but that's more trouble than it's worth.  These
 +             * connections will succeed the next time they retry, assuming that
 +             * they have a certificate against the correct CA.) */
 +            VLOG_ERR("rejecting SSL connection during bootstrap race window");
 +            return EPROTO;
 +        } else {
 +            return 0;
 +        }
 +    }
 +
 +    NOT_REACHED();
 +}
 +
 +static void
 +ssl_close(struct stream *stream)
 +{
 +    struct ssl_stream *sslv = ssl_stream_cast(stream);
 +    ssl_clear_txbuf(sslv);
 +
 +    /* Attempt clean shutdown of the SSL connection.  This will work most of
 +     * the time, as long as the kernel send buffer has some free space and the
 +     * SSL connection isn't renegotiating, etc.  That has to be good enough,
 +     * since we don't have any way to continue the close operation in the
 +     * background. */
 +    SSL_shutdown(sslv->ssl);
 +
 +    SSL_free(sslv->ssl);
 +    close(sslv->fd);
 +    free(sslv);
 +}
 +
 +static int
 +interpret_ssl_error(const char *function, int ret, int error,
 +                    int *want)
 +{
 +    *want = SSL_NOTHING;
 +
 +    switch (error) {
 +    case SSL_ERROR_NONE:
 +        VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_NONE", function);
 +        break;
 +
 +    case SSL_ERROR_ZERO_RETURN:
 +        VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_ZERO_RETURN", function);
 +        break;
 +
 +    case SSL_ERROR_WANT_READ:
 +        *want = SSL_READING;
 +        return EAGAIN;
 +
 +    case SSL_ERROR_WANT_WRITE:
 +        *want = SSL_WRITING;
 +        return EAGAIN;
 +
 +    case SSL_ERROR_WANT_CONNECT:
 +        VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_WANT_CONNECT", function);
 +        break;
 +
 +    case SSL_ERROR_WANT_ACCEPT:
 +        VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_WANT_ACCEPT", function);
 +        break;
 +
 +    case SSL_ERROR_WANT_X509_LOOKUP:
 +        VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_WANT_X509_LOOKUP",
 +                    function);
 +        break;
 +
 +    case SSL_ERROR_SYSCALL: {
 +        int queued_error = ERR_get_error();
 +        if (queued_error == 0) {
 +            if (ret < 0) {
 +                int status = errno;
 +                VLOG_WARN_RL(&rl, "%s: system error (%s)",
 +                             function, strerror(status));
 +                return status;
 +            } else {
 +                VLOG_WARN_RL(&rl, "%s: unexpected SSL connection close",
 +                             function);
 +                return EPROTO;
 +            }
 +        } else {
 +            VLOG_WARN_RL(&rl, "%s: %s",
 +                         function, ERR_error_string(queued_error, NULL));
 +            break;
 +        }
 +    }
 +
 +    case SSL_ERROR_SSL: {
 +        int queued_error = ERR_get_error();
 +        if (queued_error != 0) {
 +            VLOG_WARN_RL(&rl, "%s: %s",
 +                         function, ERR_error_string(queued_error, NULL));
 +        } else {
 +            VLOG_ERR_RL(&rl, "%s: SSL_ERROR_SSL without queued error",
 +                        function);
 +        }
 +        break;
 +    }
 +
 +    default:
 +        VLOG_ERR_RL(&rl, "%s: bad SSL error code %d", function, error);
 +        break;
 +    }
 +    return EIO;
 +}
 +
 +static ssize_t
 +ssl_recv(struct stream *stream, void *buffer, size_t n)
 +{
 +    struct ssl_stream *sslv = ssl_stream_cast(stream);
 +    int old_state;
 +    ssize_t ret;
 +
 +    /* Behavior of zero-byte SSL_read is poorly defined. */
 +    assert(n > 0);
 +
 +    old_state = SSL_get_state(sslv->ssl);
 +    ret = SSL_read(sslv->ssl, buffer, n);
 +    if (old_state != SSL_get_state(sslv->ssl)) {
 +        sslv->tx_want = SSL_NOTHING;
 +    }
 +    sslv->rx_want = SSL_NOTHING;
 +
 +    if (ret > 0) {
 +        return ret;
 +    } else {
 +        int error = SSL_get_error(sslv->ssl, ret);
 +        if (error == SSL_ERROR_ZERO_RETURN) {
 +            return 0;
 +        } else {
 +            return -interpret_ssl_error("SSL_read", ret, error,
 +                                        &sslv->rx_want);
 +        }
 +    }
 +}
 +
 +static void
 +ssl_clear_txbuf(struct ssl_stream *sslv)
 +{
 +    ofpbuf_delete(sslv->txbuf);
 +    sslv->txbuf = NULL;
 +}
 +
 +static int
 +ssl_do_tx(struct stream *stream)
 +{
 +    struct ssl_stream *sslv = ssl_stream_cast(stream);
 +
 +    for (;;) {
 +        int old_state = SSL_get_state(sslv->ssl);
 +        int ret = SSL_write(sslv->ssl, sslv->txbuf->data, sslv->txbuf->size);
 +        if (old_state != SSL_get_state(sslv->ssl)) {
 +            sslv->rx_want = SSL_NOTHING;
 +        }
 +        sslv->tx_want = SSL_NOTHING;
 +        if (ret > 0) {
 +            ofpbuf_pull(sslv->txbuf, ret);
 +            if (sslv->txbuf->size == 0) {
 +                return 0;
 +            }
 +        } else {
 +            int ssl_error = SSL_get_error(sslv->ssl, ret);
 +            if (ssl_error == SSL_ERROR_ZERO_RETURN) {
 +                VLOG_WARN_RL(&rl, "SSL_write: connection closed");
 +                return EPIPE;
 +            } else {
 +                return interpret_ssl_error("SSL_write", ret, ssl_error,
 +                                           &sslv->tx_want);
 +            }
 +        }
 +    }
 +}
 +
 +static ssize_t
 +ssl_send(struct stream *stream, const void *buffer, size_t n)
 +{
 +    struct ssl_stream *sslv = ssl_stream_cast(stream);
 +
 +    if (sslv->txbuf) {
 +        return -EAGAIN;
 +    } else {
 +        int error;
 +
 +        sslv->txbuf = ofpbuf_clone_data(buffer, n);
 +        error = ssl_do_tx(stream);
 +        switch (error) {
 +        case 0:
 +            ssl_clear_txbuf(sslv);
 +            return n;
 +        case EAGAIN:
 +            leak_checker_claim(buffer);
 +            return n;
 +        default:
 +            sslv->txbuf = NULL;
 +            return -error;
 +        }
 +    }
 +}
 +
 +static void
 +ssl_run(struct stream *stream)
 +{
 +    struct ssl_stream *sslv = ssl_stream_cast(stream);
 +
 +    if (sslv->txbuf && ssl_do_tx(stream) != EAGAIN) {
 +        ssl_clear_txbuf(sslv);
 +    }
 +}
 +
 +static void
 +ssl_run_wait(struct stream *stream)
 +{
 +    struct ssl_stream *sslv = ssl_stream_cast(stream);
 +
 +    if (sslv->tx_want != SSL_NOTHING) {
 +        poll_fd_wait(sslv->fd, want_to_poll_events(sslv->tx_want));
 +    }
 +}
 +
 +static void
 +ssl_wait(struct stream *stream, enum stream_wait_type wait)
 +{
 +    struct ssl_stream *sslv = ssl_stream_cast(stream);
 +
 +    switch (wait) {
 +    case STREAM_CONNECT:
 +        if (stream_connect(stream) != EAGAIN) {
 +            poll_immediate_wake();
 +        } else {
 +            switch (sslv->state) {
 +            case STATE_TCP_CONNECTING:
 +                poll_fd_wait(sslv->fd, POLLOUT);
 +                break;
 +
 +            case STATE_SSL_CONNECTING:
 +                /* ssl_connect() called SSL_accept() or SSL_connect(), which
 +                 * set up the status that we test here. */
 +                poll_fd_wait(sslv->fd,
 +                             want_to_poll_events(SSL_want(sslv->ssl)));
 +                break;
 +
 +            default:
 +                NOT_REACHED();
 +            }
 +        }
 +        break;
 +
 +    case STREAM_RECV:
 +        if (sslv->rx_want != SSL_NOTHING) {
 +            poll_fd_wait(sslv->fd, want_to_poll_events(sslv->rx_want));
 +        } else {
 +            poll_immediate_wake();
 +        }
 +        break;
 +
 +    case STREAM_SEND:
 +        if (!sslv->txbuf) {
 +            /* We have room in our tx queue. */
 +            poll_immediate_wake();
 +        } else {
 +            /* stream_run_wait() will do the right thing; don't bother with
 +             * redundancy. */
 +        }
 +        break;
 +
 +    default:
 +        NOT_REACHED();
 +    }
 +}
 +
 +struct stream_class ssl_stream_class = {
 +    "ssl",                      /* name */
 +    ssl_open,                   /* open */
 +    ssl_close,                  /* close */
 +    ssl_connect,                /* connect */
 +    ssl_recv,                   /* recv */
 +    ssl_send,                   /* send */
 +    ssl_run,                    /* run */
 +    ssl_run_wait,               /* run_wait */
 +    ssl_wait,                   /* wait */
 +};
 +\f
 +/* Passive SSL. */
 +
 +struct pssl_pstream
 +{
 +    struct pstream pstream;
 +    int fd;
 +};
 +
 +struct pstream_class pssl_pstream_class;
 +
 +static struct pssl_pstream *
 +pssl_pstream_cast(struct pstream *pstream)
 +{
 +    pstream_assert_class(pstream, &pssl_pstream_class);
 +    return CONTAINER_OF(pstream, struct pssl_pstream, pstream);
 +}
 +
 +static int
- tmp_dh_callback(SSL *ssl UNUSED, int is_export UNUSED, int keylength)
++pssl_open(const char *name OVS_UNUSED, char *suffix, struct pstream **pstreamp)
 +{
 +    struct pssl_pstream *pssl;
 +    struct sockaddr_in sin;
 +    char bound_name[128];
 +    int retval;
 +    int fd;
 +
 +    retval = ssl_init();
 +    if (retval) {
 +        return retval;
 +    }
 +
 +    fd = inet_open_passive(SOCK_STREAM, suffix, OFP_SSL_PORT, &sin);
 +    if (fd < 0) {
 +        return -fd;
 +    }
 +    sprintf(bound_name, "pssl:%"PRIu16":"IP_FMT,
 +            ntohs(sin.sin_port), IP_ARGS(&sin.sin_addr.s_addr));
 +
 +    pssl = xmalloc(sizeof *pssl);
 +    pstream_init(&pssl->pstream, &pssl_pstream_class, bound_name);
 +    pssl->fd = fd;
 +    *pstreamp = &pssl->pstream;
 +    return 0;
 +}
 +
 +static void
 +pssl_close(struct pstream *pstream)
 +{
 +    struct pssl_pstream *pssl = pssl_pstream_cast(pstream);
 +    close(pssl->fd);
 +    free(pssl);
 +}
 +
 +static int
 +pssl_accept(struct pstream *pstream, struct stream **new_streamp)
 +{
 +    struct pssl_pstream *pssl = pssl_pstream_cast(pstream);
 +    struct sockaddr_in sin;
 +    socklen_t sin_len = sizeof sin;
 +    char name[128];
 +    int new_fd;
 +    int error;
 +
 +    new_fd = accept(pssl->fd, &sin, &sin_len);
 +    if (new_fd < 0) {
 +        int error = errno;
 +        if (error != EAGAIN) {
 +            VLOG_DBG_RL(&rl, "accept: %s", strerror(error));
 +        }
 +        return error;
 +    }
 +
 +    error = set_nonblocking(new_fd);
 +    if (error) {
 +        close(new_fd);
 +        return error;
 +    }
 +
 +    sprintf(name, "ssl:"IP_FMT, IP_ARGS(&sin.sin_addr));
 +    if (sin.sin_port != htons(OFP_SSL_PORT)) {
 +        sprintf(strchr(name, '\0'), ":%"PRIu16, ntohs(sin.sin_port));
 +    }
 +    return new_ssl_stream(name, new_fd, SERVER, STATE_SSL_CONNECTING, &sin,
 +                         new_streamp);
 +}
 +
 +static void
 +pssl_wait(struct pstream *pstream)
 +{
 +    struct pssl_pstream *pssl = pssl_pstream_cast(pstream);
 +    poll_fd_wait(pssl->fd, POLLIN);
 +}
 +
 +struct pstream_class pssl_pstream_class = {
 +    "pssl",
 +    pssl_open,
 +    pssl_close,
 +    pssl_accept,
 +    pssl_wait,
 +};
 +\f
 +/*
 + * Returns true if OpenSSL error is WANT_READ or WANT_WRITE, indicating that
 + * OpenSSL is requesting that we call it back when the socket is ready for read
 + * or writing, respectively.
 + */
 +static bool
 +ssl_wants_io(int ssl_error)
 +{
 +    return (ssl_error == SSL_ERROR_WANT_WRITE
 +            || ssl_error == SSL_ERROR_WANT_READ);
 +}
 +
 +static int
 +ssl_init(void)
 +{
 +    static int init_status = -1;
 +    if (init_status < 0) {
 +        init_status = do_ssl_init();
 +        assert(init_status >= 0);
 +    }
 +    return init_status;
 +}
 +
 +static int
 +do_ssl_init(void)
 +{
 +    SSL_METHOD *method;
 +
 +    SSL_library_init();
 +    SSL_load_error_strings();
 +
 +    method = TLSv1_method();
 +    if (method == NULL) {
 +        VLOG_ERR("TLSv1_method: %s", ERR_error_string(ERR_get_error(), NULL));
 +        return ENOPROTOOPT;
 +    }
 +
 +    ctx = SSL_CTX_new(method);
 +    if (ctx == NULL) {
 +        VLOG_ERR("SSL_CTX_new: %s", ERR_error_string(ERR_get_error(), NULL));
 +        return ENOPROTOOPT;
 +    }
 +    SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
 +    SSL_CTX_set_tmp_dh_callback(ctx, tmp_dh_callback);
 +    SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
 +    SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
 +    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
 +                       NULL);
 +
 +    return 0;
 +}
 +
 +static DH *
++tmp_dh_callback(SSL *ssl OVS_UNUSED, int is_export OVS_UNUSED, int keylength)
 +{
 +    struct dh {
 +        int keylength;
 +        DH *dh;
 +        DH *(*constructor)(void);
 +    };
 +
 +    static struct dh dh_table[] = {
 +        {1024, NULL, get_dh1024},
 +        {2048, NULL, get_dh2048},
 +        {4096, NULL, get_dh4096},
 +    };
 +
 +    struct dh *dh;
 +
 +    for (dh = dh_table; dh < &dh_table[ARRAY_SIZE(dh_table)]; dh++) {
 +        if (dh->keylength == keylength) {
 +            if (!dh->dh) {
 +                dh->dh = dh->constructor();
 +                if (!dh->dh) {
 +                    ovs_fatal(ENOMEM, "out of memory constructing "
 +                              "Diffie-Hellman parameters");
 +                }
 +            }
 +            return dh->dh;
 +        }
 +    }
 +    VLOG_ERR_RL(&rl, "no Diffie-Hellman parameters for key length %d",
 +                keylength);
 +    return NULL;
 +}
 +
 +/* Returns true if SSL is at least partially configured. */
 +bool
 +stream_ssl_is_configured(void) 
 +{
 +    return has_private_key || has_certificate || has_ca_cert;
 +}
 +
 +void
 +stream_ssl_set_private_key_file(const char *file_name)
 +{
 +    if (ssl_init()) {
 +        return;
 +    }
 +    if (SSL_CTX_use_PrivateKey_file(ctx, file_name, SSL_FILETYPE_PEM) != 1) {
 +        VLOG_ERR("SSL_use_PrivateKey_file: %s",
 +                 ERR_error_string(ERR_get_error(), NULL));
 +        return;
 +    }
 +    has_private_key = true;
 +}
 +
 +void
 +stream_ssl_set_certificate_file(const char *file_name)
 +{
 +    if (ssl_init()) {
 +        return;
 +    }
 +    if (SSL_CTX_use_certificate_chain_file(ctx, file_name) != 1) {
 +        VLOG_ERR("SSL_use_certificate_file: %s",
 +                 ERR_error_string(ERR_get_error(), NULL));
 +        return;
 +    }
 +    has_certificate = true;
 +}
 +
 +/* Reads the X509 certificate or certificates in file 'file_name'.  On success,
 + * stores the address of the first element in an array of pointers to
 + * certificates in '*certs' and the number of certificates in the array in
 + * '*n_certs', and returns 0.  On failure, stores a null pointer in '*certs', 0
 + * in '*n_certs', and returns a positive errno value.
 + *
 + * The caller is responsible for freeing '*certs'. */
 +static int
 +read_cert_file(const char *file_name, X509 ***certs, size_t *n_certs)
 +{
 +    FILE *file;
 +    size_t allocated_certs = 0;
 +
 +    *certs = NULL;
 +    *n_certs = 0;
 +
 +    file = fopen(file_name, "r");
 +    if (!file) {
 +        VLOG_ERR("failed to open %s for reading: %s",
 +                 file_name, strerror(errno));
 +        return errno;
 +    }
 +
 +    for (;;) {
 +        X509 *certificate;
 +        int c;
 +
 +        /* Read certificate from file. */
 +        certificate = PEM_read_X509(file, NULL, NULL, NULL);
 +        if (!certificate) {
 +            size_t i;
 +
 +            VLOG_ERR("PEM_read_X509 failed reading %s: %s",
 +                     file_name, ERR_error_string(ERR_get_error(), NULL));
 +            for (i = 0; i < *n_certs; i++) {
 +                X509_free((*certs)[i]);
 +            }
 +            free(*certs);
 +            *certs = NULL;
 +            *n_certs = 0;
 +            return EIO;
 +        }
 +
 +        /* Add certificate to array. */
 +        if (*n_certs >= allocated_certs) {
 +            *certs = x2nrealloc(*certs, &allocated_certs, sizeof **certs);
 +        }
 +        (*certs)[(*n_certs)++] = certificate;
 +
 +        /* Are there additional certificates in the file? */
 +        do {
 +            c = getc(file);
 +        } while (isspace(c));
 +        if (c == EOF) {
 +            break;
 +        }
 +        ungetc(c, file);
 +    }
 +    fclose(file);
 +    return 0;
 +}
 +
 +
 +/* Sets 'file_name' as the name of a file containing one or more X509
 + * certificates to send to the peer.  Typical use in OpenFlow is to send the CA
 + * certificate to the peer, which enables a switch to pick up the controller's
 + * CA certificate on its first connection. */
 +void
 +stream_ssl_set_peer_ca_cert_file(const char *file_name)
 +{
 +    X509 **certs;
 +    size_t n_certs;
 +    size_t i;
 +
 +    if (ssl_init()) {
 +        return;
 +    }
 +
 +    if (!read_cert_file(file_name, &certs, &n_certs)) {
 +        for (i = 0; i < n_certs; i++) {
 +            if (SSL_CTX_add_extra_chain_cert(ctx, certs[i]) != 1) {
 +                VLOG_ERR("SSL_CTX_add_extra_chain_cert: %s",
 +                         ERR_error_string(ERR_get_error(), NULL));
 +            }
 +        }
 +        free(certs);
 +    }
 +}
 +
 +/* Logs fingerprint of CA certificate 'cert' obtained from 'file_name'. */
 +static void
 +log_ca_cert(const char *file_name, X509 *cert)
 +{
 +    unsigned char digest[EVP_MAX_MD_SIZE];
 +    unsigned int n_bytes;
 +    struct ds fp;
 +    char *subject;
 +
 +    ds_init(&fp);
 +    if (!X509_digest(cert, EVP_sha1(), digest, &n_bytes)) {
 +        ds_put_cstr(&fp, "<out of memory>");
 +    } else {
 +        unsigned int i;
 +        for (i = 0; i < n_bytes; i++) {
 +            if (i) {
 +                ds_put_char(&fp, ':');
 +            }
 +            ds_put_format(&fp, "%02hhx", digest[i]);
 +        }
 +    }
 +    subject = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
 +    VLOG_INFO("Trusting CA cert from %s (%s) (fingerprint %s)", file_name,
 +              subject ? subject : "<out of memory>", ds_cstr(&fp));
 +    free(subject);
 +    ds_destroy(&fp);
 +}
 +
 +/* Sets 'file_name' as the name of the file from which to read the CA
 + * certificate used to verify the peer within SSL connections.  If 'bootstrap'
 + * is false, the file must exist.  If 'bootstrap' is false, then the file is
 + * read if it is exists; if it does not, then it will be created from the CA
 + * certificate received from the peer on the first SSL connection. */
 +void
 +stream_ssl_set_ca_cert_file(const char *file_name, bool bootstrap)
 +{
 +    X509 **certs;
 +    size_t n_certs;
 +    struct stat s;
 +
 +    if (ssl_init()) {
 +        return;
 +    }
 +
 +    if (bootstrap && stat(file_name, &s) && errno == ENOENT) {
 +        bootstrap_ca_cert = true;
 +        ca_cert_file = xstrdup(file_name);
 +    } else if (!read_cert_file(file_name, &certs, &n_certs)) {
 +        size_t i;
 +
 +        /* Set up list of CAs that the server will accept from the client. */
 +        for (i = 0; i < n_certs; i++) {
 +            /* SSL_CTX_add_client_CA makes a copy of the relevant data. */
 +            if (SSL_CTX_add_client_CA(ctx, certs[i]) != 1) {
 +                VLOG_ERR("failed to add client certificate %d from %s: %s",
 +                         i, file_name,
 +                         ERR_error_string(ERR_get_error(), NULL));
 +            } else {
 +                log_ca_cert(file_name, certs[i]);
 +            }
 +            X509_free(certs[i]);
 +        }
 +        free(certs);
 +
 +        /* Set up CAs for OpenSSL to trust in verifying the peer's
 +         * certificate. */
 +        if (SSL_CTX_load_verify_locations(ctx, file_name, NULL) != 1) {
 +            VLOG_ERR("SSL_CTX_load_verify_locations: %s",
 +                     ERR_error_string(ERR_get_error(), NULL));
 +            return;
 +        }
 +
 +        has_ca_cert = true;
 +    }
 +}
diff --combined lib/stream-tcp.c
index e690e9c,0000000..a9bcaeb
mode 100644,000000..100644
--- /dev/null
@@@ -1,143 -1,0 +1,143 @@@
- ptcp_open(const char *name UNUSED, char *suffix, struct pstream **pstreamp)
 +/*
 + * Copyright (c) 2008, 2009, 2010 Nicira Networks.
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at:
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <config.h>
 +#include "stream.h"
 +#include <errno.h>
 +#include <inttypes.h>
 +#include <sys/types.h>
 +#include <netinet/in.h>
 +#include <netinet/tcp.h>
 +#include <stdlib.h>
 +#include <string.h>
 +#include <unistd.h>
 +#include "packets.h"
 +#include "socket-util.h"
 +#include "util.h"
 +#include "stream-provider.h"
 +#include "stream-fd.h"
 +
 +#include "vlog.h"
 +#define THIS_MODULE VLM_stream_tcp
 +
 +/* Active TCP. */
 +
 +static int
 +new_tcp_stream(const char *name, int fd, int connect_status,
 +              const struct sockaddr_in *remote, struct stream **streamp)
 +{
 +    struct sockaddr_in local;
 +    socklen_t local_len = sizeof local;
 +    int on = 1;
 +    int retval;
 +
 +    /* Get the local IP and port information */
 +    retval = getsockname(fd, (struct sockaddr *)&local, &local_len);
 +    if (retval) {
 +        memset(&local, 0, sizeof local);
 +    }
 +
 +    retval = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof on);
 +    if (retval) {
 +        VLOG_ERR("%s: setsockopt(TCP_NODELAY): %s", name, strerror(errno));
 +        close(fd);
 +        return errno;
 +    }
 +
 +    retval = new_fd_stream(name, fd, connect_status, NULL, streamp);
 +    if (!retval) {
 +        struct stream *stream = *streamp;
 +        stream_set_remote_ip(stream, remote->sin_addr.s_addr);
 +        stream_set_remote_port(stream, remote->sin_port);
 +        stream_set_local_ip(stream, local.sin_addr.s_addr);
 +        stream_set_local_port(stream, local.sin_port);
 +    }
 +    return retval;
 +}
 +
 +static int
 +tcp_open(const char *name, char *suffix, struct stream **streamp)
 +{
 +    struct sockaddr_in sin;
 +    int fd, error;
 +
 +    error = inet_open_active(SOCK_STREAM, suffix, 0, &sin, &fd);
 +    if (fd >= 0) {
 +        return new_tcp_stream(name, fd, error, &sin, streamp);
 +    } else {
 +        VLOG_ERR("%s: connect: %s", name, strerror(error));
 +        return error;
 +    }
 +}
 +
 +struct stream_class tcp_stream_class = {
 +    "tcp",                      /* name */
 +    tcp_open,                   /* open */
 +    NULL,                       /* close */
 +    NULL,                       /* connect */
 +    NULL,                       /* recv */
 +    NULL,                       /* send */
 +    NULL,                       /* run */
 +    NULL,                       /* run_wait */
 +    NULL,                       /* wait */
 +};
 +\f
 +/* Passive TCP. */
 +
 +static int ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len,
 +                       struct stream **streamp);
 +
 +static int
++ptcp_open(const char *name OVS_UNUSED, char *suffix, struct pstream **pstreamp)
 +{
 +    struct sockaddr_in sin;
 +    char bound_name[128];
 +    int fd;
 +
 +    fd = inet_open_passive(SOCK_STREAM, suffix, -1, &sin);
 +    if (fd < 0) {
 +        return -fd;
 +    }
 +
 +    sprintf(bound_name, "ptcp:%"PRIu16":"IP_FMT,
 +            ntohs(sin.sin_port), IP_ARGS(&sin.sin_addr.s_addr));
 +    return new_fd_pstream(bound_name, fd, ptcp_accept, NULL, pstreamp);
 +}
 +
 +static int
 +ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len,
 +            struct stream **streamp)
 +{
 +    const struct sockaddr_in *sin = (const struct sockaddr_in *) sa;
 +    char name[128];
 +
 +    if (sa_len == sizeof(struct sockaddr_in) && sin->sin_family == AF_INET) {
 +        sprintf(name, "tcp:"IP_FMT, IP_ARGS(&sin->sin_addr));
 +        sprintf(strchr(name, '\0'), ":%"PRIu16, ntohs(sin->sin_port));
 +    } else {
 +        strcpy(name, "tcp");
 +    }
 +    return new_tcp_stream(name, fd, 0, sin, streamp);
 +}
 +
 +struct pstream_class ptcp_pstream_class = {
 +    "ptcp",
 +    ptcp_open,
 +    NULL,
 +    NULL,
 +    NULL
 +};
 +
diff --combined lib/stream-unix.c
index 6ce7790,0000000..33f566b
mode 100644,000000..100644
--- /dev/null
@@@ -1,127 -1,0 +1,128 @@@
-  * Copyright (c) 2008, 2009 Nicira Networks.
 +/*
- punix_open(const char *name UNUSED, char *suffix, struct pstream **pstreamp)
++ * Copyright (c) 2008, 2009, 2010 Nicira Networks.
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at:
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <config.h>
 +#include "stream.h"
 +#include <assert.h>
 +#include <errno.h>
 +#include <inttypes.h>
 +#include <netdb.h>
 +#include <poll.h>
 +#include <sys/types.h>
 +#include <sys/un.h>
 +#include <stdlib.h>
 +#include <string.h>
 +#include <unistd.h>
 +#include "packets.h"
 +#include "poll-loop.h"
 +#include "socket-util.h"
 +#include "util.h"
 +#include "stream-provider.h"
 +#include "stream-fd.h"
 +
 +#include "vlog.h"
 +#define THIS_MODULE VLM_stream_unix
 +
 +/* Active UNIX socket. */
 +
 +/* Number of unix sockets created so far, to ensure binding path uniqueness. */
 +static int n_unix_sockets;
 +
 +static int
 +unix_open(const char *name, char *suffix, struct stream **streamp)
 +{
 +    const char *connect_path = suffix;
 +    char *bind_path;
 +    int fd;
 +
 +    bind_path = xasprintf("/tmp/stream-unix.%ld.%d",
 +                          (long int) getpid(), n_unix_sockets++);
 +    fd = make_unix_socket(SOCK_STREAM, true, false, bind_path, connect_path);
 +    if (fd < 0) {
 +        VLOG_ERR("%s: connection to %s failed: %s",
 +                 bind_path, connect_path, strerror(-fd));
 +        free(bind_path);
 +        return -fd;
 +    }
 +
 +    return new_fd_stream(name, fd, check_connection_completion(fd),
 +                         bind_path, streamp);
 +}
 +
 +struct stream_class unix_stream_class = {
 +    "unix",                     /* name */
 +    unix_open,                  /* open */
 +    NULL,                       /* close */
 +    NULL,                       /* connect */
 +    NULL,                       /* recv */
 +    NULL,                       /* send */
 +    NULL,                       /* run */
 +    NULL,                       /* run_wait */
 +    NULL,                       /* wait */
 +};
 +\f
 +/* Passive UNIX socket. */
 +
 +static int punix_accept(int fd, const struct sockaddr *sa, size_t sa_len,
 +                        struct stream **streamp);
 +
 +static int
++punix_open(const char *name OVS_UNUSED, char *suffix,
++           struct pstream **pstreamp)
 +{
 +    int fd, error;
 +
 +    fd = make_unix_socket(SOCK_STREAM, true, true, suffix, NULL);
 +    if (fd < 0) {
 +        VLOG_ERR("%s: binding failed: %s", suffix, strerror(errno));
 +        return errno;
 +    }
 +
 +    if (listen(fd, 10) < 0) {
 +        error = errno;
 +        VLOG_ERR("%s: listen: %s", name, strerror(error));
 +        close(fd);
 +        return error;
 +    }
 +
 +    return new_fd_pstream("punix", fd, punix_accept,
 +                          xstrdup(suffix), pstreamp);
 +}
 +
 +static int
 +punix_accept(int fd, const struct sockaddr *sa, size_t sa_len,
 +             struct stream **streamp)
 +{
 +    const struct sockaddr_un *sun = (const struct sockaddr_un *) sa;
 +    int name_len = get_unix_name_len(sa_len);
 +    char name[128];
 +
 +    if (name_len > 0) {
 +        snprintf(name, sizeof name, "unix:%.*s", name_len, sun->sun_path);
 +    } else {
 +        strcpy(name, "unix");
 +    }
 +    return new_fd_stream(name, fd, 0, NULL, streamp);
 +}
 +
 +struct pstream_class punix_pstream_class = {
 +    "punix",
 +    punix_open,
 +    NULL,
 +    NULL,
 +    NULL
 +};
 +
diff --combined lib/stream.c
index dcd8da5,0000000..db6ec61
mode 100644,000000..100644
--- /dev/null
@@@ -1,563 -1,0 +1,563 @@@
-              bool bootstrap UNUSED)
 +/*
 + * Copyright (c) 2008, 2009, 2010 Nicira Networks.
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at:
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <config.h>
 +#include "stream-provider.h"
 +#include <assert.h>
 +#include <errno.h>
 +#include <inttypes.h>
 +#include <netinet/in.h>
 +#include <poll.h>
 +#include <stdlib.h>
 +#include <string.h>
 +#include "coverage.h"
 +#include "dynamic-string.h"
 +#include "flow.h"
 +#include "ofp-print.h"
 +#include "ofpbuf.h"
 +#include "openflow/nicira-ext.h"
 +#include "openflow/openflow.h"
 +#include "packets.h"
 +#include "poll-loop.h"
 +#include "random.h"
 +#include "util.h"
 +
 +#define THIS_MODULE VLM_stream
 +#include "vlog.h"
 +
 +/* State of an active stream.*/
 +enum stream_state {
 +    SCS_CONNECTING,             /* Underlying stream is not connected. */
 +    SCS_CONNECTED,              /* Connection established. */
 +    SCS_DISCONNECTED            /* Connection failed or connection closed. */
 +};
 +
 +static struct stream_class *stream_classes[] = {
 +    &tcp_stream_class,
 +    &unix_stream_class,
 +#ifdef HAVE_OPENSSL
 +    &ssl_stream_class,
 +#endif
 +};
 +
 +static struct pstream_class *pstream_classes[] = {
 +    &ptcp_pstream_class,
 +    &punix_pstream_class,
 +#ifdef HAVE_OPENSSL
 +    &pssl_pstream_class,
 +#endif
 +};
 +
 +/* Check the validity of the stream class structures. */
 +static void
 +check_stream_classes(void)
 +{
 +#ifndef NDEBUG
 +    size_t i;
 +
 +    for (i = 0; i < ARRAY_SIZE(stream_classes); i++) {
 +        struct stream_class *class = stream_classes[i];
 +        assert(class->name != NULL);
 +        assert(class->open != NULL);
 +        if (class->close || class->recv || class->send || class->run
 +            || class->run_wait || class->wait) {
 +            assert(class->close != NULL);
 +            assert(class->recv != NULL);
 +            assert(class->send != NULL);
 +            assert(class->wait != NULL);
 +        } else {
 +            /* This class delegates to another one. */
 +        }
 +    }
 +
 +    for (i = 0; i < ARRAY_SIZE(pstream_classes); i++) {
 +        struct pstream_class *class = pstream_classes[i];
 +        assert(class->name != NULL);
 +        assert(class->listen != NULL);
 +        if (class->close || class->accept || class->wait) {
 +            assert(class->close != NULL);
 +            assert(class->accept != NULL);
 +            assert(class->wait != NULL);
 +        } else {
 +            /* This class delegates to another one. */
 +        }
 +    }
 +#endif
 +}
 +
 +/* Prints information on active (if 'active') and passive (if 'passive')
 + * connection methods supported by the stream. */
 +void
 +stream_usage(const char *name, bool active, bool passive,
++             bool bootstrap OVS_UNUSED)
 +{
 +    /* Really this should be implemented via callbacks into the stream
 +     * providers, but that seems too heavy-weight to bother with at the
 +     * moment. */
 +
 +    printf("\n");
 +    if (active) {
 +        printf("Active %s connection methods:\n", name);
 +        printf("  tcp:IP:PORT             "
 +               "PORT at remote IP\n");
 +#ifdef HAVE_OPENSSL
 +        printf("  ssl:IP:PORT             "
 +               "SSL PORT at remote IP\n");
 +#endif
 +        printf("  unix:FILE               "
 +               "Unix domain socket named FILE\n");
 +    }
 +
 +    if (passive) {
 +        printf("Passive %s connection methods:\n", name);
 +        printf("  ptcp:PORT[:IP]          "
 +               "listen to TCP PORT on IP\n");
 +#ifdef HAVE_OPENSSL
 +        printf("  pssl:PORT[:IP]          "
 +               "listen for SSL on PORT on IP\n");
 +#endif
 +        printf("  punix:FILE              "
 +               "listen on Unix domain socket FILE\n");
 +    }
 +
 +#ifdef HAVE_OPENSSL
 +    printf("PKI configuration (required to use SSL):\n"
 +           "  -p, --private-key=FILE  file with private key\n"
 +           "  -c, --certificate=FILE  file with certificate for private key\n"
 +           "  -C, --ca-cert=FILE      file with peer CA certificate\n");
 +    if (bootstrap) {
 +        printf("  --bootstrap-ca-cert=FILE  file with peer CA certificate "
 +               "to read or create\n");
 +    }
 +#endif
 +}
 +
 +/* Attempts to connect a stream to a remote peer.  'name' is a connection name
 + * in the form "TYPE:ARGS", where TYPE is an active stream class's name and
 + * ARGS are stream class-specific.
 + *
 + * Returns 0 if successful, otherwise a positive errno value.  If successful,
 + * stores a pointer to the new connection in '*streamp', otherwise a null
 + * pointer.  */
 +int
 +stream_open(const char *name, struct stream **streamp)
 +{
 +    size_t prefix_len;
 +    size_t i;
 +
 +    COVERAGE_INC(stream_open);
 +    check_stream_classes();
 +
 +    *streamp = NULL;
 +    prefix_len = strcspn(name, ":");
 +    if (prefix_len == strlen(name)) {
 +        return EAFNOSUPPORT;
 +    }
 +    for (i = 0; i < ARRAY_SIZE(stream_classes); i++) {
 +        struct stream_class *class = stream_classes[i];
 +        if (strlen(class->name) == prefix_len
 +            && !memcmp(class->name, name, prefix_len)) {
 +            struct stream *stream;
 +            char *suffix_copy = xstrdup(name + prefix_len + 1);
 +            int retval = class->open(name, suffix_copy, &stream);
 +            free(suffix_copy);
 +            if (!retval) {
 +                assert(stream->state != SCS_CONNECTING
 +                       || stream->class->connect);
 +                *streamp = stream;
 +            }
 +            return retval;
 +        }
 +    }
 +    return EAFNOSUPPORT;
 +}
 +
 +int
 +stream_open_block(const char *name, struct stream **streamp)
 +{
 +    struct stream *stream;
 +    int error;
 +
 +    error = stream_open(name, &stream);
 +    while (error == EAGAIN) {
 +        stream_run(stream);
 +        stream_run_wait(stream);
 +        stream_connect_wait(stream);
 +        poll_block();
 +        error = stream_connect(stream);
 +        assert(error != EINPROGRESS);
 +    }
 +    if (error) {
 +        stream_close(stream);
 +        *streamp = NULL;
 +    } else {
 +        *streamp = stream;
 +    }
 +    return error;
 +}
 +
 +/* Closes 'stream'. */
 +void
 +stream_close(struct stream *stream)
 +{
 +    if (stream != NULL) {
 +        char *name = stream->name;
 +        (stream->class->close)(stream);
 +        free(name);
 +    }
 +}
 +
 +/* Returns the name of 'stream', that is, the string passed to
 + * stream_open(). */
 +const char *
 +stream_get_name(const struct stream *stream)
 +{
 +    return stream ? stream->name : "(null)";
 +}
 +
 +/* Returns the IP address of the peer, or 0 if the peer is not connected over
 + * an IP-based protocol or if its IP address is not yet known. */
 +uint32_t
 +stream_get_remote_ip(const struct stream *stream)
 +{
 +    return stream->remote_ip;
 +}
 +
 +/* Returns the transport port of the peer, or 0 if the connection does not
 + * contain a port or if the port is not yet known. */
 +uint16_t
 +stream_get_remote_port(const struct stream *stream)
 +{
 +    return stream->remote_port;
 +}
 +
 +/* Returns the IP address used to connect to the peer, or 0 if the connection
 + * is not an IP-based protocol or if its IP address is not yet known. */
 +uint32_t
 +stream_get_local_ip(const struct stream *stream)
 +{
 +    return stream->local_ip;
 +}
 +
 +/* Returns the transport port used to connect to the peer, or 0 if the
 + * connection does not contain a port or if the port is not yet known. */
 +uint16_t
 +stream_get_local_port(const struct stream *stream)
 +{
 +    return stream->local_port;
 +}
 +
 +static void
 +scs_connecting(struct stream *stream)
 +{
 +    int retval = (stream->class->connect)(stream);
 +    assert(retval != EINPROGRESS);
 +    if (!retval) {
 +        stream->state = SCS_CONNECTED;
 +    } else if (retval != EAGAIN) {
 +        stream->state = SCS_DISCONNECTED;
 +        stream->error = retval;
 +    }
 +}
 +
 +/* Tries to complete the connection on 'stream', which must be an active
 + * stream.  If 'stream''s connection is complete, returns 0 if the connection
 + * was successful or a positive errno value if it failed.  If the
 + * connection is still in progress, returns EAGAIN. */
 +int
 +stream_connect(struct stream *stream)
 +{
 +    enum stream_state last_state;
 +
 +    do {
 +        last_state = stream->state;
 +        switch (stream->state) {
 +        case SCS_CONNECTING:
 +            scs_connecting(stream);
 +            break;
 +
 +        case SCS_CONNECTED:
 +            return 0;
 +
 +        case SCS_DISCONNECTED:
 +            return stream->error;
 +
 +        default:
 +            NOT_REACHED();
 +        }
 +    } while (stream->state != last_state);
 +
 +    return EAGAIN;
 +}
 +
 +/* Tries to receive up to 'n' bytes from 'stream' into 'buffer', and returns:
 + *
 + *     - If successful, the number of bytes received (between 1 and 'n').
 + *
 + *     - On error, a negative errno value.
 + *
 + *     - 0, if the connection has been closed in the normal fashion, or if 'n'
 + *       is zero.
 + *
 + * The recv function will not block waiting for a packet to arrive.  If no
 + * data have been received, it returns -EAGAIN immediately. */
 +int
 +stream_recv(struct stream *stream, void *buffer, size_t n)
 +{
 +    int retval = stream_connect(stream);
 +    return (retval ? -retval
 +            : n == 0 ? 0
 +            : (stream->class->recv)(stream, buffer, n));
 +}
 +
 +/* Tries to send up to 'n' bytes of 'buffer' on 'stream', and returns:
 + *
 + *     - If successful, the number of bytes sent (between 1 and 'n').  0 is
 + *       only a valid return value if 'n' is 0.
 + *
 + *     - On error, a negative errno value.
 + *
 + * The send function will not block.  If no bytes can be immediately accepted
 + * for transmission, it returns -EAGAIN immediately. */
 +int
 +stream_send(struct stream *stream, const void *buffer, size_t n)
 +{
 +    int retval = stream_connect(stream);
 +    return (retval ? -retval
 +            : n == 0 ? 0
 +            : (stream->class->send)(stream, buffer, n));
 +}
 +
 +/* Allows 'stream' to perform maintenance activities, such as flushing
 + * output buffers. */
 +void
 +stream_run(struct stream *stream)
 +{
 +    if (stream->class->run) {
 +        (stream->class->run)(stream);
 +    }
 +}
 +
 +/* Arranges for the poll loop to wake up when 'stream' needs to perform
 + * maintenance activities. */
 +void
 +stream_run_wait(struct stream *stream)
 +{
 +    if (stream->class->run_wait) {
 +        (stream->class->run_wait)(stream);
 +    }
 +}
 +
 +/* Arranges for the poll loop to wake up when 'stream' is ready to take an
 + * action of the given 'type'. */
 +void
 +stream_wait(struct stream *stream, enum stream_wait_type wait)
 +{
 +    assert(wait == STREAM_CONNECT || wait == STREAM_RECV
 +           || wait == STREAM_SEND);
 +
 +    switch (stream->state) {
 +    case SCS_CONNECTING:
 +        wait = STREAM_CONNECT;
 +        break;
 +
 +    case SCS_DISCONNECTED:
 +        poll_immediate_wake();
 +        return;
 +    }
 +    (stream->class->wait)(stream, wait);
 +}
 +
 +void
 +stream_connect_wait(struct stream *stream)
 +{
 +    stream_wait(stream, STREAM_CONNECT);
 +}
 +
 +void
 +stream_recv_wait(struct stream *stream)
 +{
 +    stream_wait(stream, STREAM_RECV);
 +}
 +
 +void
 +stream_send_wait(struct stream *stream)
 +{
 +    stream_wait(stream, STREAM_SEND);
 +}
 +
 +/* Attempts to start listening for remote stream connections.  'name' is a
 + * connection name in the form "TYPE:ARGS", where TYPE is an passive stream
 + * class's name and ARGS are stream class-specific.
 + *
 + * Returns 0 if successful, otherwise a positive errno value.  If successful,
 + * stores a pointer to the new connection in '*pstreamp', otherwise a null
 + * pointer.  */
 +int
 +pstream_open(const char *name, struct pstream **pstreamp)
 +{
 +    size_t prefix_len;
 +    size_t i;
 +
 +    check_stream_classes();
 +
 +    *pstreamp = NULL;
 +    prefix_len = strcspn(name, ":");
 +    if (prefix_len == strlen(name)) {
 +        return EAFNOSUPPORT;
 +    }
 +    for (i = 0; i < ARRAY_SIZE(pstream_classes); i++) {
 +        struct pstream_class *class = pstream_classes[i];
 +        if (strlen(class->name) == prefix_len
 +            && !memcmp(class->name, name, prefix_len)) {
 +            char *suffix_copy = xstrdup(name + prefix_len + 1);
 +            int retval = class->listen(name, suffix_copy, pstreamp);
 +            free(suffix_copy);
 +            if (retval) {
 +                *pstreamp = NULL;
 +            }
 +            return retval;
 +        }
 +    }
 +    return EAFNOSUPPORT;
 +}
 +
 +/* Returns the name that was used to open 'pstream'.  The caller must not
 + * modify or free the name. */
 +const char *
 +pstream_get_name(const struct pstream *pstream)
 +{
 +    return pstream->name;
 +}
 +
 +/* Closes 'pstream'. */
 +void
 +pstream_close(struct pstream *pstream)
 +{
 +    if (pstream != NULL) {
 +        char *name = pstream->name;
 +        (pstream->class->close)(pstream);
 +        free(name);
 +    }
 +}
 +
 +/* Tries to accept a new connection on 'pstream'.  If successful, stores the
 + * new connection in '*new_stream' and returns 0.  Otherwise, returns a
 + * positive errno value.
 + *
 + * pstream_accept() will not block waiting for a connection.  If no connection
 + * is ready to be accepted, it returns EAGAIN immediately. */
 +int
 +pstream_accept(struct pstream *pstream, struct stream **new_stream)
 +{
 +    int retval = (pstream->class->accept)(pstream, new_stream);
 +    if (retval) {
 +        *new_stream = NULL;
 +    } else {
 +        assert((*new_stream)->state != SCS_CONNECTING
 +               || (*new_stream)->class->connect);
 +    }
 +    return retval;
 +}
 +
 +/* Tries to accept a new connection on 'pstream'.  If successful, stores the
 + * new connection in '*new_stream' and returns 0.  Otherwise, returns a
 + * positive errno value.
 + *
 + * pstream_accept_block() blocks until a connection is ready or until an error
 + * occurs.  It will not return EAGAIN. */
 +int
 +pstream_accept_block(struct pstream *pstream, struct stream **new_stream)
 +{
 +    int error;
 +
 +    while ((error = pstream_accept(pstream, new_stream)) == EAGAIN) {
 +        pstream_wait(pstream);
 +        poll_block();
 +    }
 +    if (error) {
 +        *new_stream = NULL;
 +    }
 +    return error;
 +}
 +
 +void
 +pstream_wait(struct pstream *pstream)
 +{
 +    (pstream->class->wait)(pstream);
 +}
 +\f
 +/* Initializes 'stream' as a new stream named 'name', implemented via 'class'.
 + * The initial connection status, supplied as 'connect_status', is interpreted
 + * as follows:
 + *
 + *      - 0: 'stream' is connected.  Its 'send' and 'recv' functions may be
 + *        called in the normal fashion.
 + *
 + *      - EAGAIN: 'stream' is trying to complete a connection.  Its 'connect'
 + *        function should be called to complete the connection.
 + *
 + *      - Other positive errno values indicate that the connection failed with
 + *        the specified error.
 + *
 + * After calling this function, stream_close() must be used to destroy
 + * 'stream', otherwise resources will be leaked.
 + *
 + * The caller retains ownership of 'name'. */
 +void
 +stream_init(struct stream *stream, struct stream_class *class,
 +            int connect_status, const char *name)
 +{
 +    stream->class = class;
 +    stream->state = (connect_status == EAGAIN ? SCS_CONNECTING
 +                    : !connect_status ? SCS_CONNECTED
 +                    : SCS_DISCONNECTED);
 +    stream->error = connect_status;
 +    stream->name = xstrdup(name);
 +    assert(stream->state != SCS_CONNECTING || class->connect);
 +}
 +
 +void
 +stream_set_remote_ip(struct stream *stream, uint32_t ip)
 +{
 +    stream->remote_ip = ip;
 +}
 +
 +void
 +stream_set_remote_port(struct stream *stream, uint16_t port)
 +{
 +    stream->remote_port = port;
 +}
 +
 +void
 +stream_set_local_ip(struct stream *stream, uint32_t ip)
 +{
 +    stream->local_ip = ip;
 +}
 +
 +void
 +stream_set_local_port(struct stream *stream, uint16_t port)
 +{
 +    stream->local_port = port;
 +}
 +
 +void
 +pstream_init(struct pstream *pstream, struct pstream_class *class,
 +            const char *name)
 +{
 +    pstream->class = class;
 +    pstream->name = xstrdup(name);
 +}
diff --combined lib/timeval.h
@@@ -1,5 -1,5 +1,5 @@@
  /*
-- * Copyright (c) 2008, 2009 Nicira Networks.
++ * Copyright (c) 2008, 2009, 2010 Nicira Networks.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
  #include "type-props.h"
  #include "util.h"
  
+ #ifdef  __cplusplus
+ extern "C" {
+ #endif
  struct pollfd;
  struct timeval;
  
@@@ -41,8 -45,6 +45,8 @@@ BUILD_ASSERT_DECL(TYPE_IS_SIGNED(time_t
  #define TIME_UPDATE_INTERVAL 100
  
  void time_init(void);
 +void time_disable_restart(void);
 +void time_enable_restart(void);
  void time_postfork(void);
  void time_refresh(void);
  time_t time_now(void);
@@@ -51,6 -53,8 +55,10 @@@ void time_timeval(struct timeval *)
  void time_alarm(unsigned int secs);
  int time_poll(struct pollfd *, int n_pollfds, int timeout);
  
 +long long int timeval_to_msec(const struct timeval *);
 +
+ #ifdef  __cplusplus
+ }
+ #endif
  #endif /* timeval.h */
diff --combined lib/unixctl.c
@@@ -44,8 -44,7 +44,8 @@@
  #include "vlog.h"
  \f
  struct unixctl_command {
 -    void (*cb)(struct unixctl_conn *, const char *args);
 +    unixctl_cb_func *cb;
 +    void *aux;
  };
  
  struct unixctl_conn {
@@@ -77,8 -76,7 +77,8 @@@ static struct vlog_rate_limit rl = VLOG
  static struct shash commands = SHASH_INITIALIZER(&commands);
  
  static void
- unixctl_help(struct unixctl_conn *conn, const char *args UNUSED,
-              void *aux UNUSED)
 -unixctl_help(struct unixctl_conn *conn, const char *args OVS_UNUSED)
++unixctl_help(struct unixctl_conn *conn, const char *args OVS_UNUSED,
++             void *aux OVS_UNUSED)
  {
      struct ds ds = DS_EMPTY_INITIALIZER;
      struct shash_node *node;
@@@ -92,7 -90,8 +92,7 @@@
  }
  
  void
 -unixctl_command_register(const char *name,
 -                         void (*cb)(struct unixctl_conn *, const char *args))
 +unixctl_command_register(const char *name, unixctl_cb_func *cb, void *aux)
  {
      struct unixctl_command *command;
  
             || shash_find_data(&commands, name) == cb);
      command = xmalloc(sizeof *command);
      command->cb = cb;
 +    command->aux = aux;
      shash_add(&commands, name, command);
  }
  
@@@ -180,7 -178,7 +180,7 @@@ unixctl_server_create(const char *path
      struct unixctl_server *server;
      int error;
  
 -    unixctl_command_register("help", unixctl_help);
 +    unixctl_command_register("help", unixctl_help, NULL);
  
      server = xmalloc(sizeof *server);
      list_init(&server->conns);
                                    NULL);
      if (server->fd < 0) {
          error = -server->fd;
 -        fprintf(stderr, "Could not initialize control socket %s (%s)\n",
 -                server->path, strerror(error));
 +        ovs_error(error, "could not initialize control socket %s",
 +                  server->path);
          goto error;
      }
  
      if (chmod(server->path, S_IRUSR | S_IWUSR) < 0) {
          error = errno;
 -        fprintf(stderr, "Failed to chmod control socket %s (%s)\n",
 -                server->path, strerror(error));
 +        ovs_error(error, "failed to chmod control socket %s", server->path);
          goto error;
      }
  
      if (listen(server->fd, 10) < 0) {
          error = errno;
 -        fprintf(stderr, "Failed to listen on control socket %s (%s)\n",
 -                server->path, strerror(error));
 +        ovs_error(error, "Failed to listen on control socket %s",
 +                  server->path);
          goto error;
      }
  
@@@ -283,7 -282,7 +283,7 @@@ process_command(struct unixctl_conn *co
  
      command = shash_find_data(&commands, name);
      if (command) {
 -        command->cb(conn, args);
 +        command->cb(conn, args, command->aux);
      } else {
          char *msg = xasprintf("\"%s\" is not a valid command", name);
          unixctl_command_reply(conn, 400, msg);
diff --combined lib/util.c
@@@ -1,5 -1,5 +1,5 @@@
  /*
-- * Copyright (c) 2008, 2009 Nicira Networks.
++ * Copyright (c) 2008, 2009, 2010 Nicira Networks.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
@@@ -42,12 -42,6 +42,12 @@@ xcalloc(size_t count, size_t size
      return p;
  }
  
 +void *
 +xzalloc(size_t size)
 +{
 +    return xcalloc(1, size);
 +}
 +
  void *
  xmalloc(size_t size) 
  {
@@@ -168,10 -162,8 +168,10 @@@ ovs_error(int err_no, const char *forma
      va_start(args, format);
      vfprintf(stderr, format, args);
      va_end(args);
 -    if (err_no != 0)
 -        fprintf(stderr, " (%s)", strerror(err_no));
 +    if (err_no != 0) {
 +        fprintf(stderr, " (%s)",
 +                err_no == EOF ? "end of file" : strerror(err_no));
 +    }
      putc('\n', stderr);
  
      errno = save_errno;
@@@ -302,90 -294,3 +302,90 @@@ str_to_ullong(const char *s, int base, 
  {
      return str_to_llong(s, base, (long long *) ull);
  }
- void ignore(bool x UNUSED) { }
 +
 +/* Converts floating-point string 's' into a double.  If successful, stores
 + * the double in '*d' and returns true; on failure, stores 0 in '*d' and
 + * returns false.
 + *
 + * Underflow (e.g. "1e-9999") is not considered an error, but overflow
 + * (e.g. "1e9999)" is. */
 +bool
 +str_to_double(const char *s, double *d)
 +{
 +    int save_errno = errno;
 +    char *tail;
 +    errno = 0;
 +    *d = strtod(s, &tail);
 +    if (errno == EINVAL || (errno == ERANGE && *d != 0)
 +        || tail == s || *tail != '\0') {
 +        errno = save_errno;
 +        *d = 0;
 +        return false;
 +    } else {
 +        errno = save_errno;
 +        return true;
 +    }
 +}
 +
 +/* Returns the value of 'c' as a hexadecimal digit. */
 +int
 +hexit_value(int c)
 +{
 +    switch (c) {
 +    case '0': case '1': case '2': case '3': case '4':
 +    case '5': case '6': case '7': case '8': case '9':
 +        return c - '0';
 +
 +    case 'a': case 'A':
 +        return 0xa;
 +
 +    case 'b': case 'B':
 +        return 0xb;
 +
 +    case 'c': case 'C':
 +        return 0xc;
 +
 +    case 'd': case 'D':
 +        return 0xd;
 +
 +    case 'e': case 'E':
 +        return 0xe;
 +
 +    case 'f': case 'F':
 +        return 0xf;
 +    }
 +
 +    NOT_REACHED();
 +}
 +
 +/* Returns the directory name portion of 'file_name' as a malloc()'d string,
 + * similar to the POSIX dirname() function but thread-safe. */
 +char *
 +dir_name(const char *file_name)
 +{
 +    size_t len = strlen(file_name);
 +    while (len > 0 && file_name[len - 1] == '/') {
 +        len--;
 +    }
 +    while (len > 0 && file_name[len - 1] != '/') {
 +        len--;
 +    }
 +    while (len > 0 && file_name[len - 1] == '/') {
 +        len--;
 +    }
 +    if (!len) {
 +        return xstrdup((file_name[0] == '/'
 +                        && file_name[1] == '/'
 +                        && file_name[2] != '/') ? "//"
 +                       : file_name[0] == '/' ? "/"
 +                       : ".");
 +    } else {
 +        return xmemdup0(file_name, len);
 +    }
 +}
 +
 +/* Pass a value to this function if it is marked with
 + * __attribute__((warn_unused_result)) and you genuinely want to ignore 
 + * its return value.  (Note that every scalar type can be implicitly 
 + * converted to bool.) */
++void ignore(bool x OVS_UNUSED) { }
diff --combined lib/util.h
@@@ -1,5 -1,5 +1,5 @@@
  /*
-- * Copyright (c) 2008, 2009 Nicira Networks.
++ * Copyright (c) 2008, 2009, 2010 Nicira Networks.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
@@@ -98,7 -98,6 +98,7 @@@ void ovs_print_version(char *date, cha
  void out_of_memory(void) NO_RETURN;
  void *xmalloc(size_t) MALLOC_LIKE;
  void *xcalloc(size_t, size_t) MALLOC_LIKE;
 +void *xzalloc(size_t) MALLOC_LIKE;
  void *xrealloc(void *, size_t);
  void *xmemdup(const void *, size_t) MALLOC_LIKE;
  char *xmemdup0(const char *, size_t) MALLOC_LIKE;
@@@ -121,14 -120,6 +121,14 @@@ bool str_to_uint(const char *, int base
  bool str_to_ulong(const char *, int base, unsigned long *);
  bool str_to_ullong(const char *, int base, unsigned long long *);
  
- void ignore(bool x UNUSED);
 +bool str_to_double(const char *, double *);
 +
 +int hexit_value(int c);
 +
 +char *dir_name(const char *file_name);
 +
++void ignore(bool x OVS_UNUSED);
 +
  #ifdef  __cplusplus
  }
  #endif
diff --combined lib/vconn-stream.c
@@@ -15,6 -15,7 +15,6 @@@
   */
  
  #include <config.h>
 -#include "vconn-stream.h"
  #include <assert.h>
  #include <errno.h>
  #include <poll.h>
@@@ -28,7 -29,6 +28,7 @@@
  #include "openflow/openflow.h"
  #include "poll-loop.h"
  #include "socket-util.h"
 +#include "stream.h"
  #include "util.h"
  #include "vconn-provider.h"
  #include "vconn.h"
  
  /* Active stream socket vconn. */
  
 -struct stream_vconn
 +struct vconn_stream
  {
      struct vconn vconn;
 -    int fd;
 +    struct stream *stream;
      struct ofpbuf *rxbuf;
      struct ofpbuf *txbuf;
 -    struct poll_waiter *tx_waiter;
 -    char *unlink_path;
  };
  
  static struct vconn_class stream_vconn_class;
  
  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25);
  
 -static void stream_clear_txbuf(struct stream_vconn *);
 -static void maybe_unlink_and_free(char *path);
 +static void vconn_stream_clear_txbuf(struct vconn_stream *);
 +static int count_fields(const char *);
  
 -/* Creates a new vconn named 'name' that will send and receive data on 'fd' and
 - * stores a pointer to the vconn in '*vconnp'.  Initial connection status
 - * 'connect_status' is interpreted as described for vconn_init().
 - *
 - * When '*vconnp' is closed, then 'unlink_path' (if nonnull) will be passed to
 - * fatal_signal_unlink_file_now() and then freed with free().
 - *
 - * Returns 0 if successful, otherwise a positive errno value.  (The current
 - * implementation never fails.) */
 -int
 -new_stream_vconn(const char *name, int fd, int connect_status,
 -                 char *unlink_path, struct vconn **vconnp)
 +static struct vconn *
 +vconn_stream_new(struct stream *stream, int connect_status)
  {
 -    struct stream_vconn *s;
 +    struct vconn_stream *s;
  
      s = xmalloc(sizeof *s);
 -    vconn_init(&s->vconn, &stream_vconn_class, connect_status, name);
 -    s->fd = fd;
 +    vconn_init(&s->vconn, &stream_vconn_class, connect_status,
 +               stream_get_name(stream));
 +    s->stream = stream;
      s->txbuf = NULL;
 -    s->tx_waiter = NULL;
      s->rxbuf = NULL;
 -    s->unlink_path = unlink_path;
 -    *vconnp = &s->vconn;
 +    return &s->vconn;
 +}
 +
 +/* Creates a new vconn that will send and receive data on a stream named 'name'
 + * and stores a pointer to the vconn in '*vconnp'.
 + *
 + * Returns 0 if successful, otherwise a positive errno value. */
 +static int
- vconn_stream_open(const char *name_, char *suffix UNUSED,
++vconn_stream_open(const char *name_, char *suffix OVS_UNUSED,
 +                  struct vconn **vconnp)
 +{
 +    struct stream *stream;
 +    char *name;
 +    int error;
 +
 +    if (!strncmp(name_, "tcp:", 4) && count_fields(name_) < 3) {
 +        name = xasprintf("%s:%d", name_, OFP_TCP_PORT);
 +    } else if (!strncmp(name_, "ssl:", 4) && count_fields(name_) < 3) {
 +        name = xasprintf("%s:%d", name_, OFP_SSL_PORT);
 +    } else {
 +        name = xstrdup(name_);
 +    }
 +    error = stream_open(name, &stream);
 +    free(name);
 +
 +    if (error && error != EAGAIN) {
 +        return error;
 +    }
 +
 +    *vconnp = vconn_stream_new(stream, error);
      return 0;
  }
  
 -static struct stream_vconn *
 -stream_vconn_cast(struct vconn *vconn)
 +static struct vconn_stream *
 +vconn_stream_cast(struct vconn *vconn)
  {
 -    vconn_assert_class(vconn, &stream_vconn_class);
 -    return CONTAINER_OF(vconn, struct stream_vconn, vconn);
 +    return CONTAINER_OF(vconn, struct vconn_stream, vconn);
  }
  
  static void
 -stream_close(struct vconn *vconn)
 +vconn_stream_close(struct vconn *vconn)
  {
 -    struct stream_vconn *s = stream_vconn_cast(vconn);
 -    poll_cancel(s->tx_waiter);
 -    stream_clear_txbuf(s);
 +    struct vconn_stream *s = vconn_stream_cast(vconn);
 +    stream_close(s->stream);
 +    vconn_stream_clear_txbuf(s);
      ofpbuf_delete(s->rxbuf);
 -    close(s->fd);
 -    maybe_unlink_and_free(s->unlink_path);
      free(s);
  }
  
  static int
 -stream_connect(struct vconn *vconn)
 +vconn_stream_connect(struct vconn *vconn)
  {
 -    struct stream_vconn *s = stream_vconn_cast(vconn);
 -    return check_connection_completion(s->fd);
 +    struct vconn_stream *s = vconn_stream_cast(vconn);
 +    return stream_connect(s->stream);
  }
  
  static int
 -stream_recv(struct vconn *vconn, struct ofpbuf **bufferp)
 +vconn_stream_recv(struct vconn *vconn, struct ofpbuf **bufferp)
  {
 -    struct stream_vconn *s = stream_vconn_cast(vconn);
 +    struct vconn_stream *s = vconn_stream_cast(vconn);
      struct ofpbuf *rx;
      size_t want_bytes;
      ssize_t retval;
@@@ -153,7 -140,7 +153,7 @@@ again
      }
      ofpbuf_prealloc_tailroom(rx, want_bytes);
  
 -    retval = read(s->fd, ofpbuf_tail(rx), want_bytes);
 +    retval = stream_recv(s->stream, ofpbuf_tail(rx), want_bytes);
      if (retval > 0) {
          rx->size += retval;
          if (retval == want_bytes) {
              return EOF;
          }
      } else {
 -        return errno;
 +        return -retval;
      }
  }
  
  static void
 -stream_clear_txbuf(struct stream_vconn *s)
 +vconn_stream_clear_txbuf(struct vconn_stream *s)
  {
      ofpbuf_delete(s->txbuf);
      s->txbuf = NULL;
 -    s->tx_waiter = NULL;
 -}
 -
 -static void
 -stream_do_tx(int fd OVS_UNUSED, short int revents OVS_UNUSED, void *vconn_)
 -{
 -    struct vconn *vconn = vconn_;
 -    struct stream_vconn *s = stream_vconn_cast(vconn);
 -    ssize_t n = write(s->fd, s->txbuf->data, s->txbuf->size);
 -    if (n < 0) {
 -        if (errno != EAGAIN) {
 -            VLOG_ERR_RL(&rl, "send: %s", strerror(errno));
 -            stream_clear_txbuf(s);
 -            return;
 -        }
 -    } else if (n > 0) {
 -        ofpbuf_pull(s->txbuf, n);
 -        if (!s->txbuf->size) {
 -            stream_clear_txbuf(s);
 -            return;
 -        }
 -    }
 -    s->tx_waiter = poll_fd_callback(s->fd, POLLOUT, stream_do_tx, vconn);
  }
  
  static int
 -stream_send(struct vconn *vconn, struct ofpbuf *buffer)
 +vconn_stream_send(struct vconn *vconn, struct ofpbuf *buffer)
  {
 -    struct stream_vconn *s = stream_vconn_cast(vconn);
 +    struct vconn_stream *s = vconn_stream_cast(vconn);
      ssize_t retval;
  
      if (s->txbuf) {
          return EAGAIN;
      }
  
 -    retval = write(s->fd, buffer->data, buffer->size);
 +    retval = stream_send(s->stream, buffer->data, buffer->size);
      if (retval == buffer->size) {
          ofpbuf_delete(buffer);
          return 0;
 -    } else if (retval >= 0 || errno == EAGAIN) {
 +    } else if (retval >= 0 || retval == -EAGAIN) {
          leak_checker_claim(buffer);
          s->txbuf = buffer;
          if (retval > 0) {
              ofpbuf_pull(buffer, retval);
          }
 -        s->tx_waiter = poll_fd_callback(s->fd, POLLOUT, stream_do_tx, vconn);
          return 0;
      } else {
 -        return errno;
 +        return -retval;
 +    }
 +}
 +
 +static void
 +vconn_stream_run(struct vconn *vconn)
 +{
 +    struct vconn_stream *s = vconn_stream_cast(vconn);
 +    ssize_t retval;
 +
 +    if (!s->txbuf) {
 +        return;
 +    }
 +
 +    retval = stream_send(s->stream, s->txbuf->data, s->txbuf->size);
 +    if (retval < 0) {
 +        if (retval != -EAGAIN) {
 +            VLOG_ERR_RL(&rl, "send: %s", strerror(-retval));
 +            vconn_stream_clear_txbuf(s);
 +            return;
 +        }
 +    } else if (retval > 0) {
 +        ofpbuf_pull(s->txbuf, retval);
 +        if (!s->txbuf->size) {
 +            vconn_stream_clear_txbuf(s);
 +            return;
 +        }
 +    }
 +}
 +
 +static void
 +vconn_stream_run_wait(struct vconn *vconn)
 +{
 +    struct vconn_stream *s = vconn_stream_cast(vconn);
 +
 +    if (s->txbuf) {
 +        stream_send_wait(s->stream);
      }
  }
  
  static void
 -stream_wait(struct vconn *vconn, enum vconn_wait_type wait)
 +vconn_stream_wait(struct vconn *vconn, enum vconn_wait_type wait)
  {
 -    struct stream_vconn *s = stream_vconn_cast(vconn);
 +    struct vconn_stream *s = vconn_stream_cast(vconn);
      switch (wait) {
      case WAIT_CONNECT:
 -        poll_fd_wait(s->fd, POLLOUT);
 +        stream_connect_wait(s->stream);
          break;
  
      case WAIT_SEND:
          if (!s->txbuf) {
 -            poll_fd_wait(s->fd, POLLOUT);
 +            stream_send_wait(s->stream);
          } else {
 -            /* Nothing to do: need to drain txbuf first. */
 +            /* Nothing to do: need to drain txbuf first.
 +             * vconn_stream_run_wait() will arrange to wake up when there room
 +             * to send data, so there's no point in calling poll_fd_wait()
 +             * redundantly here. */
          }
          break;
  
      case WAIT_RECV:
 -        poll_fd_wait(s->fd, POLLIN);
 +        stream_recv_wait(s->stream);
          break;
  
      default:
          NOT_REACHED();
      }
  }
 -
 -static struct vconn_class stream_vconn_class = {
 -    "stream",                   /* name */
 -    NULL,                       /* open */
 -    stream_close,               /* close */
 -    stream_connect,             /* connect */
 -    stream_recv,                /* recv */
 -    stream_send,                /* send */
 -    stream_wait,                /* wait */
 -};
  \f
  /* Passive stream socket vconn. */
  
 -struct pstream_pvconn
 +struct pvconn_pstream
  {
      struct pvconn pvconn;
 -    int fd;
 -    int (*accept_cb)(int fd, const struct sockaddr *, size_t sa_len,
 -                     struct vconn **);
 -    char *unlink_path;
 +    struct pstream *pstream;
  };
  
  static struct pvconn_class pstream_pvconn_class;
  
 -static struct pstream_pvconn *
 -pstream_pvconn_cast(struct pvconn *pvconn)
 +static struct pvconn_pstream *
 +pvconn_pstream_cast(struct pvconn *pvconn)
  {
 -    pvconn_assert_class(pvconn, &pstream_pvconn_class);
 -    return CONTAINER_OF(pvconn, struct pstream_pvconn, pvconn);
 +    return CONTAINER_OF(pvconn, struct pvconn_pstream, pvconn);
  }
  
 -/* Creates a new pvconn named 'name' that will accept new socket connections on
 - * 'fd' and stores a pointer to the vconn in '*pvconnp'.
 - *
 - * When a connection has been accepted, 'accept_cb' will be called with the new
 - * socket fd 'fd' and the remote address of the connection 'sa' and 'sa_len'.
 - * accept_cb must return 0 if the connection is successful, in which case it
 - * must initialize '*vconnp' to the new vconn, or a positive errno value on
 - * error.  In either case accept_cb takes ownership of the 'fd' passed in.
 - *
 - * When '*pvconnp' is closed, then 'unlink_path' (if nonnull) will be passed to
 - * fatal_signal_unlink_file_now() and freed with free().
 +/* Creates a new pvconn named 'name' that will accept new connections using
 + * pstream_accept() and stores a pointer to the pvconn in '*pvconnp'.
   *
   * Returns 0 if successful, otherwise a positive errno value.  (The current
   * implementation never fails.) */
 -int
 -new_pstream_pvconn(const char *name, int fd,
 -                  int (*accept_cb)(int fd, const struct sockaddr *sa,
 -                                   size_t sa_len, struct vconn **vconnp),
 -                  char *unlink_path, struct pvconn **pvconnp)
 +static int
- pvconn_pstream_listen(const char *name_, char *suffix UNUSED,
++pvconn_pstream_listen(const char *name_, char *suffix OVS_UNUSED,
 +                      struct pvconn **pvconnp)
  {
 -    struct pstream_pvconn *ps = xmalloc(sizeof *ps);
 -    pvconn_init(&ps->pvconn, &pstream_pvconn_class, name);
 -    ps->fd = fd;
 -    ps->accept_cb = accept_cb;
 -    ps->unlink_path = unlink_path;
 +    struct pvconn_pstream *ps;
 +    struct pstream *pstream;
 +    char *name;
 +    int error;
 +
 +    if (!strncmp(name_, "ptcp:", 5) && count_fields(name_) < 2) {
 +        name = xasprintf("%s:%d", name_, OFP_TCP_PORT);
 +    } else if (!strncmp(name_, "pssl:", 5) && count_fields(name_) < 2) {
 +        name = xasprintf("%s:%d", name_, OFP_SSL_PORT);
 +    } else {
 +        name = xstrdup(name_);
 +    }
 +    error = pstream_open(name, &pstream);
 +    free(name);
 +    if (error) {
 +        return error;
 +    }
 +
 +    ps = xmalloc(sizeof *ps);
 +    pvconn_init(&ps->pvconn, &pstream_pvconn_class, name_);
 +    ps->pstream = pstream;
      *pvconnp = &ps->pvconn;
      return 0;
  }
  
  static void
 -pstream_close(struct pvconn *pvconn)
 +pvconn_pstream_close(struct pvconn *pvconn)
  {
 -    struct pstream_pvconn *ps = pstream_pvconn_cast(pvconn);
 -    close(ps->fd);
 -    maybe_unlink_and_free(ps->unlink_path);
 +    struct pvconn_pstream *ps = pvconn_pstream_cast(pvconn);
 +    pstream_close(ps->pstream);
      free(ps);
  }
  
  static int
 -pstream_accept(struct pvconn *pvconn, struct vconn **new_vconnp)
 +pvconn_pstream_accept(struct pvconn *pvconn, struct vconn **new_vconnp)
  {
 -    struct pstream_pvconn *ps = pstream_pvconn_cast(pvconn);
 -    struct sockaddr_storage ss;
 -    socklen_t ss_len = sizeof ss;
 -    int new_fd;
 -    int retval;
 -
 -    new_fd = accept(ps->fd, (struct sockaddr *) &ss, &ss_len);
 -    if (new_fd < 0) {
 -        int retval = errno;
 -        if (retval != EAGAIN) {
 -            VLOG_DBG_RL(&rl, "accept: %s", strerror(retval));
 +    struct pvconn_pstream *ps = pvconn_pstream_cast(pvconn);
 +    struct stream *stream;
 +    int error;
 +
 +    error = pstream_accept(ps->pstream, &stream);
 +    if (error) {
 +        if (error != EAGAIN) {
 +            VLOG_DBG_RL(&rl, "%s: accept: %s",
 +                        pstream_get_name(ps->pstream), strerror(error));
          }
 -        return retval;
 -    }
 -
 -    retval = set_nonblocking(new_fd);
 -    if (retval) {
 -        close(new_fd);
 -        return retval;
 +        return error;
      }
  
 -    return ps->accept_cb(new_fd, (const struct sockaddr *) &ss, ss_len,
 -                         new_vconnp);
 +    *new_vconnp = vconn_stream_new(stream, 0);
 +    return 0;
  }
  
  static void
 -pstream_wait(struct pvconn *pvconn)
 +pvconn_pstream_wait(struct pvconn *pvconn)
  {
 -    struct pstream_pvconn *ps = pstream_pvconn_cast(pvconn);
 -    poll_fd_wait(ps->fd, POLLIN);
 +    struct pvconn_pstream *ps = pvconn_pstream_cast(pvconn);
 +    pstream_wait(ps->pstream);
  }
 -
 -static struct pvconn_class pstream_pvconn_class = {
 -    "pstream",
 -    NULL,
 -    pstream_close,
 -    pstream_accept,
 -    pstream_wait
 -};
  \f
 -/* Helper functions. */
 -static void
 -maybe_unlink_and_free(char *path)
 +static int
 +count_fields(const char *s_)
  {
 -    if (path) {
 -        fatal_signal_unlink_file_now(path);
 -        free(path);
 +    char *s, *field, *save_ptr;
 +    int n = 0;
 +
 +    save_ptr = NULL;
 +    s = xstrdup(s_);
 +    for (field = strtok_r(s, ":", &save_ptr); field != NULL;
 +         field = strtok_r(NULL, ":", &save_ptr)) {
 +        n++;
      }
 +    free(s);
 +
 +    return n;
  }
 +\f
 +/* Stream-based vconns and pvconns. */
 +
 +#define DEFINE_VCONN_STREAM_CLASS(NAME)             \
 +        struct vconn_class NAME##_vconn_class = {   \
 +            #NAME,                                  \
 +            vconn_stream_open,                      \
 +            vconn_stream_close,                     \
 +            vconn_stream_connect,                   \
 +            vconn_stream_recv,                      \
 +            vconn_stream_send,                      \
 +            vconn_stream_run,                       \
 +            vconn_stream_run_wait,                  \
 +            vconn_stream_wait,                      \
 +        };
 +
 +#define DEFINE_PVCONN_STREAM_CLASS(NAME)            \
 +        struct pvconn_class NAME##_pvconn_class = { \
 +            #NAME,                                  \
 +            pvconn_pstream_listen,                  \
 +            pvconn_pstream_close,                   \
 +            pvconn_pstream_accept,                  \
 +            pvconn_pstream_wait                     \
 +        };
 +
 +static DEFINE_VCONN_STREAM_CLASS(stream);
 +static DEFINE_PVCONN_STREAM_CLASS(pstream);
 +
 +DEFINE_VCONN_STREAM_CLASS(tcp);
 +DEFINE_PVCONN_STREAM_CLASS(ptcp);
 +
 +DEFINE_VCONN_STREAM_CLASS(unix);
 +DEFINE_PVCONN_STREAM_CLASS(punix);
 +
 +#ifdef HAVE_OPENSSL
 +DEFINE_VCONN_STREAM_CLASS(ssl);
 +DEFINE_PVCONN_STREAM_CLASS(pssl);
 +#endif
diff --combined lib/vconn.c
@@@ -90,8 -90,7 +90,8 @@@ check_vconn_classes(void
          struct vconn_class *class = vconn_classes[i];
          assert(class->name != NULL);
          assert(class->open != NULL);
 -        if (class->close || class->recv || class->send || class->wait) {
 +        if (class->close || class->recv || class->send
 +            || class->run || class->run_wait || class->wait) {
              assert(class->close != NULL);
              assert(class->recv != NULL);
              assert(class->send != NULL);
   * connection methods supported by the vconn.  If 'bootstrap' is true, also
   * advertises options to bootstrap the CA certificate. */
  void
- vconn_usage(bool active, bool passive, bool bootstrap UNUSED)
+ vconn_usage(bool active, bool passive, bool bootstrap OVS_UNUSED)
  {
      /* Really this should be implemented via callbacks into the vconn
       * providers, but that seems too heavy-weight to bother with at the
@@@ -209,26 -208,6 +209,26 @@@ vconn_open(const char *name, int min_ve
      return EAFNOSUPPORT;
  }
  
 +/* Allows 'vconn' to perform maintenance activities, such as flushing output
 + * buffers. */
 +void
 +vconn_run(struct vconn *vconn)
 +{
 +    if (vconn->class->run) {
 +        (vconn->class->run)(vconn);
 +    }
 +}
 +
 +/* Arranges for the poll loop to wake up when 'vconn' needs to perform
 + * maintenance activities. */
 +void
 +vconn_run_wait(struct vconn *vconn)
 +{
 +    if (vconn->class->run_wait) {
 +        (vconn->class->run_wait)(vconn);
 +    }
 +}
 +
  int
  vconn_open_block(const char *name, int min_version, struct vconn **vconnp)
  {
  
      error = vconn_open(name, min_version, &vconn);
      while (error == EAGAIN) {
 +        vconn_run(vconn);
 +        vconn_run_wait(vconn);
          vconn_connect_wait(vconn);
          poll_block();
          error = vconn_connect(vconn);
@@@ -570,8 -547,6 +570,8 @@@ vconn_send_block(struct vconn *vconn, s
  {
      int retval;
      while ((retval = vconn_send(vconn, msg)) == EAGAIN) {
 +        vconn_run(vconn);
 +        vconn_run_wait(vconn);
          vconn_send_wait(vconn);
          poll_block();
      }
@@@ -584,8 -559,6 +584,8 @@@ vconn_recv_block(struct vconn *vconn, s
  {
      int retval;
      while ((retval = vconn_recv(vconn, msgp)) == EAGAIN) {
 +        vconn_run(vconn);
 +        vconn_run_wait(vconn);
          vconn_recv_wait(vconn);
          poll_block();
      }
@@@ -1471,7 -1444,6 +1471,7 @@@ vconn_init(struct vconn *vconn, struct 
      vconn->local_ip = 0;
      vconn->local_port = 0;
      vconn->name = xstrdup(name);
 +    assert(vconn->state != VCS_CONNECTING || class->connect);
  }
  
  void
diff --combined lib/vlog.c
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * Copyright (c) 2008, 2009 Nicira Networks.
+  * Copyright (c) 2008, 2009, 2010 Nicira Networks.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
@@@ -385,7 -385,7 +385,8 @@@ vlog_set_verbosity(const char *arg
  }
  
  static void
- vlog_unixctl_set(struct unixctl_conn *conn, const char *args, void *aux UNUSED)
 -vlog_unixctl_set(struct unixctl_conn *conn, const char *args)
++vlog_unixctl_set(struct unixctl_conn *conn,
++                 const char *args, void *aux OVS_UNUSED)
  {
      char *msg = vlog_set_levels_from_string(args);
      unixctl_command_reply(conn, msg ? 501 : 202, msg);
  }
  
  static void
 -vlog_unixctl_list(struct unixctl_conn *conn, const char *args OVS_UNUSED)
 +vlog_unixctl_list(struct unixctl_conn *conn,
-                   const char *args UNUSED, void *aux UNUSED)
++                  const char *args OVS_UNUSED, void *aux OVS_UNUSED)
  {
      char *msg = vlog_get_levels();
      unixctl_command_reply(conn, 200, msg);
  }
  
  static void
 -vlog_unixctl_reopen(struct unixctl_conn *conn, const char *args OVS_UNUSED)
 +vlog_unixctl_reopen(struct unixctl_conn *conn,
-                     const char *args UNUSED, void *aux UNUSED)
++                    const char *args OVS_UNUSED, void *aux OVS_UNUSED)
  {
      if (log_file_name) {
          int error = vlog_reopen_log_file();
@@@ -437,9 -435,9 +438,9 @@@ vlog_init(void
          VLOG_ERR("current time is negative: %s (%ld)", s, (long int) now);
      }
  
 -    unixctl_command_register("vlog/set", vlog_unixctl_set);
 -    unixctl_command_register("vlog/list", vlog_unixctl_list);
 -    unixctl_command_register("vlog/reopen", vlog_unixctl_reopen);
 +    unixctl_command_register("vlog/set", vlog_unixctl_set, NULL);
 +    unixctl_command_register("vlog/list", vlog_unixctl_list, NULL);
 +    unixctl_command_register("vlog/reopen", vlog_unixctl_reopen, NULL);
  }
  
  /* Closes the logging subsystem. */
diff --combined ofproto/discovery.c
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * Copyright (c) 2008, 2009 Nicira Networks.
+  * Copyright (c) 2008, 2009, 2010 Nicira Networks.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
@@@ -29,7 -29,7 +29,7 @@@
  #include "openflow/openflow.h"
  #include "packets.h"
  #include "status.h"
 -#include "vconn-ssl.h"
 +#include "stream-ssl.h"
  
  #define THIS_MODULE VLM_discovery
  #include "vlog.h"
@@@ -102,7 -102,7 +102,7 @@@ discovery_create(const char *re, bool u
      char local_name[IF_NAMESIZE];
      int error;
  
 -    d = xcalloc(1, sizeof *d);
 +    d = xzalloc(sizeof *d);
  
      /* Controller regular expression. */
      error = discovery_set_accept_controller_re(d, re);
@@@ -169,7 -169,7 +169,7 @@@ discovery_set_accept_controller_re(stru
      int error;
      char *re;
  
 -    re = (!re_ ? xstrdup(vconn_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*")
 +    re = (!re_ ? xstrdup(stream_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*")
            : re_[0] == '^' ? xstrdup(re_) : xasprintf("^%s", re_));
      regex = xmalloc(sizeof *regex);
      error = regcomp(regex, re, REG_NOSUB | REG_EXTENDED);
@@@ -244,7 -244,7 +244,7 @@@ discovery_wait(struct discovery *d
  }
  
  static void
- modify_dhcp_request(struct dhcp_msg *msg, void *aux UNUSED)
+ modify_dhcp_request(struct dhcp_msg *msg, void *aux OVS_UNUSED)
  {
      dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
  }
diff --combined ofproto/ofproto-sflow.c
@@@ -96,14 -96,16 +96,16 @@@ ofproto_sflow_options_destroy(struct of
  
  /* sFlow library callback to allocate memory. */
  static void *
- sflow_agent_alloc_cb(void *magic UNUSED, SFLAgent *agent UNUSED, size_t bytes)
+ sflow_agent_alloc_cb(void *magic OVS_UNUSED, SFLAgent *agent OVS_UNUSED,
+                      size_t bytes)
  {
      return calloc(1, bytes);
  }
  
  /* sFlow library callback to free memory. */
  static int
- sflow_agent_free_cb(void *magic UNUSED, SFLAgent *agent UNUSED, void *obj)
+ sflow_agent_free_cb(void *magic OVS_UNUSED, SFLAgent *agent OVS_UNUSED,
+                     void *obj)
  {
      free(obj);
      return 0;
  
  /* sFlow library callback to report error. */
  static void
- sflow_agent_error_cb(void *magic UNUSED, SFLAgent *agent UNUSED, char *msg)
+ sflow_agent_error_cb(void *magic OVS_UNUSED, SFLAgent *agent OVS_UNUSED,
+                      char *msg)
  {
      VLOG_WARN("sFlow agent error: %s", msg);
  }
  
  /* sFlow library callback to send datagram. */
  static void
- sflow_agent_send_packet_cb(void *os_, SFLAgent *agent UNUSED,
-                            SFLReceiver *receiver UNUSED, u_char *pkt,
+ sflow_agent_send_packet_cb(void *os_, SFLAgent *agent OVS_UNUSED,
+                            SFLReceiver *receiver OVS_UNUSED, u_char *pkt,
                             uint32_t pktLen)
  {
      struct ofproto_sflow *os = os_;
@@@ -212,7 -215,7 +215,7 @@@ sflow_choose_agent_address(const char *
      if (agent_device) {
          struct netdev *netdev;
  
 -        if (!netdev_open(agent_device, NETDEV_ETH_TYPE_NONE, &netdev)) {
 +        if (!netdev_open_default(agent_device, &netdev)) {
              int error = netdev_get_in4(netdev, &in4, NULL);
              netdev_close(netdev);
              if (!error) {
@@@ -319,7 -322,7 +322,7 @@@ ofproto_sflow_add_port(struct ofproto_s
      ofproto_sflow_del_port(os, odp_port);
  
      /* Open network device. */
 -    error = netdev_open(netdev_name, NETDEV_ETH_TYPE_NONE, &netdev);
 +    error = netdev_open_default(netdev_name, &netdev);
      if (error) {
          VLOG_WARN_RL(&rl, "failed to open network device \"%s\": %s",
                       netdev_name, strerror(error));
@@@ -367,6 -370,7 +370,6 @@@ ofproto_sflow_set_options(struct ofprot
      unsigned int odp_port;
      SFLAddress agentIP;
      time_t now;
 -    int error;
  
      if (!options->targets.n || !options->sampling_rate) {
          /* No point in doing any work if there are no targets or nothing to
      if (options_changed
          || collectors_count(os->collectors) < options->targets.n) {
          collectors_destroy(os->collectors);
 -        error = collectors_create(&options->targets,
 -                                  SFL_DEFAULT_COLLECTOR_PORT, &os->collectors);
 +        collectors_create(&options->targets, SFL_DEFAULT_COLLECTOR_PORT,
 +                          &os->collectors);
          if (os->collectors == NULL) {
              VLOG_WARN_RL(&rl, "no collectors could be initialized, "
                           "sFlow disabled");
diff --combined ofproto/ofproto.c
@@@ -27,6 -27,7 +27,6 @@@
  #include "discovery.h"
  #include "dpif.h"
  #include "dynamic-string.h"
 -#include "executer.h"
  #include "fail-open.h"
  #include "in-band.h"
  #include "mac-learning.h"
@@@ -38,6 -39,7 +38,6 @@@
  #include "ofpbuf.h"
  #include "openflow/nicira-ext.h"
  #include "openflow/openflow.h"
 -#include "openflow/openflow-mgmt.h"
  #include "openvswitch/datapath-protocol.h"
  #include "packets.h"
  #include "pinsched.h"
  #include "shash.h"
  #include "status.h"
  #include "stp.h"
 +#include "stream-ssl.h"
  #include "svec.h"
  #include "tag.h"
  #include "timeval.h"
  #include "unixctl.h"
  #include "vconn.h"
 -#include "vconn-ssl.h"
  #include "xtoxll.h"
  
  #define THIS_MODULE VLM_ofproto
@@@ -172,7 -174,7 +172,7 @@@ struct ofconn 
  };
  
  static struct ofconn *ofconn_create(struct ofproto *, struct rconn *);
 -static void ofconn_destroy(struct ofconn *, struct ofproto *);
 +static void ofconn_destroy(struct ofconn *);
  static void ofconn_run(struct ofconn *, struct ofproto *);
  static void ofconn_wait(struct ofconn *);
  static void queue_tx(struct ofpbuf *msg, const struct ofconn *ofconn,
@@@ -182,6 -184,7 +182,6 @@@ struct ofproto 
      /* Settings. */
      uint64_t datapath_id;       /* Datapath ID. */
      uint64_t fallback_dpid;     /* Datapath ID if no better choice found. */
 -    uint64_t mgmt_id;           /* Management channel identifier. */
      char *manufacturer;         /* Manufacturer. */
      char *hardware;             /* Hardware. */
      char *software;             /* Software version. */
      struct discovery *discovery;
      struct fail_open *fail_open;
      struct pinsched *miss_sched, *action_sched;
 -    struct executer *executer;
      struct netflow *netflow;
      struct ofproto_sflow *sflow;
  
@@@ -255,8 -259,7 +255,8 @@@ static int init_ports(struct ofproto *)
  static void reinit_ports(struct ofproto *);
  
  int
 -ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux,
 +ofproto_create(const char *datapath, const char *datapath_type,
 +               const struct ofhooks *ofhooks, void *aux,
                 struct ofproto **ofprotop)
  {
      struct odp_stats stats;
      *ofprotop = NULL;
  
      /* Connect to datapath and start listening for messages. */
 -    error = dpif_open(datapath, &dpif);
 +    error = dpif_open(datapath, datapath_type, &dpif);
      if (error) {
          VLOG_ERR("failed to open datapath %s: %s", datapath, strerror(error));
          return error;
      dpif_recv_purge(dpif);
  
      /* Initialize settings. */
 -    p = xcalloc(1, sizeof *p);
 +    p = xzalloc(sizeof *p);
      p->fallback_dpid = pick_fallback_dpid();
      p->datapath_id = p->fallback_dpid;
      p->manufacturer = xstrdup("Nicira Networks, Inc.");
      p->discovery = NULL;
      p->fail_open = NULL;
      p->miss_sched = p->action_sched = NULL;
 -    p->executer = NULL;
      p->netflow = NULL;
      p->sflow = NULL;
  
      p->ss_cat = switch_status_register(p->switch_status, "remote",
                                         rconn_status_cb, p->controller->rconn);
  
 -    /* Almost done... */
 -    error = init_ports(p);
 -    if (error) {
 -        ofproto_destroy(p);
 -        return error;
 -    }
 -
      /* Pick final datapath ID. */
      p->datapath_id = pick_datapath_id(p);
      VLOG_INFO("using datapath ID %012"PRIx64, p->datapath_id);
@@@ -364,6 -375,12 +364,6 @@@ ofproto_set_datapath_id(struct ofproto 
      }
  }
  
 -void
 -ofproto_set_mgmt_id(struct ofproto *p, uint64_t mgmt_id)
 -{
 -    p->mgmt_id = mgmt_id;
 -}
 -
  void
  ofproto_set_probe_interval(struct ofproto *p, int probe_interval)
  {
@@@ -520,7 -537,7 +520,7 @@@ in
  ofproto_set_netflow(struct ofproto *ofproto,
                      const struct netflow_options *nf_options)
  {
 -    if (nf_options->collectors.n) {
 +    if (nf_options && nf_options->collectors.n) {
          if (!ofproto->netflow) {
              ofproto->netflow = netflow_create();
          }
@@@ -600,7 -617,7 +600,7 @@@ ofproto_set_rate_limit(struct ofproto *
  }
  
  int
- ofproto_set_stp(struct ofproto *ofproto UNUSED, bool enable_stp)
+ ofproto_set_stp(struct ofproto *ofproto OVS_UNUSED, bool enable_stp)
  {
      /* XXX */
      if (enable_stp) {
      }
  }
  
 -int
 -ofproto_set_remote_execution(struct ofproto *ofproto, const char *command_acl,
 -                             const char *command_dir)
 -{
 -    if (command_acl) {
 -        if (!ofproto->executer) {
 -            return executer_create(command_acl, command_dir,
 -                                   &ofproto->executer);
 -        } else {
 -            executer_set_acl(ofproto->executer, command_acl, command_dir);
 -        }
 -    } else {
 -        executer_destroy(ofproto->executer);
 -        ofproto->executer = NULL;
 -    }
 -    return 0;
 -}
 -
  uint64_t
  ofproto_get_datapath_id(const struct ofproto *ofproto)
  {
      return ofproto->datapath_id;
  }
  
 -uint64_t
 -ofproto_get_mgmt_id(const struct ofproto *ofproto)
 -{
 -    return ofproto->mgmt_id;
 -}
 -
  int
  ofproto_get_probe_interval(const struct ofproto *ofproto)
  {
@@@ -684,7 -725,7 +684,7 @@@ ofproto_destroy(struct ofproto *p
  
      LIST_FOR_EACH_SAFE (ofconn, next_ofconn, struct ofconn, node,
                          &p->all_conns) {
 -        ofconn_destroy(ofconn, p);
 +        ofconn_destroy(ofconn);
      }
  
      dpif_close(p->dpif);
      fail_open_destroy(p->fail_open);
      pinsched_destroy(p->miss_sched);
      pinsched_destroy(p->action_sched);
 -    executer_destroy(p->executer);
      netflow_destroy(p->netflow);
      ofproto_sflow_destroy(p->sflow);
  
@@@ -749,10 -791,6 +749,10 @@@ ofproto_run1(struct ofproto *p
      int error;
      int i;
  
 +    if (shash_is_empty(&p->port_by_name)) {
 +        init_ports(p);
 +    }
 +
      for (i = 0; i < 50; i++) {
          struct ofpbuf *buf;
          int error;
      }
      pinsched_run(p->miss_sched, send_packet_in_miss, p);
      pinsched_run(p->action_sched, send_packet_in_action, p);
 -    if (p->executer) {
 -        executer_run(p->executer);
 -    }
  
      LIST_FOR_EACH_SAFE (ofconn, next_ofconn, struct ofconn, node,
                          &p->all_conns) {
@@@ -911,6 -952,9 +911,6 @@@ ofproto_wait(struct ofproto *p
      }
      pinsched_wait(p->miss_sched);
      pinsched_wait(p->action_sched);
 -    if (p->executer) {
 -        executer_wait(p->executer);
 -    }
      if (p->sflow) {
          ofproto_sflow_wait(p->sflow);
      }
@@@ -1090,19 -1134,13 +1090,19 @@@ refresh_port_groups(struct ofproto *p
  static struct ofport *
  make_ofport(const struct odp_port *odp_port)
  {
 +    struct netdev_options netdev_options;
      enum netdev_flags flags;
      struct ofport *ofport;
      struct netdev *netdev;
      bool carrier;
      int error;
  
 -    error = netdev_open(odp_port->devname, NETDEV_ETH_TYPE_NONE, &netdev);
 +    memset(&netdev_options, 0, sizeof netdev_options);
 +    netdev_options.name = odp_port->devname;
 +    netdev_options.ethertype = NETDEV_ETH_TYPE_NONE;
 +    netdev_options.may_open = true;
 +
 +    error = netdev_open(&netdev_options, &netdev);
      if (error) {
          VLOG_WARN_RL(&rl, "ignoring port %s (%"PRIu16") because netdev %s "
                       "cannot be opened (%s)",
@@@ -1337,8 -1375,12 +1337,8 @@@ ofconn_create(struct ofproto *p, struc
  }
  
  static void
 -ofconn_destroy(struct ofconn *ofconn, struct ofproto *p)
 +ofconn_destroy(struct ofconn *ofconn)
  {
 -    if (p->executer) {
 -        executer_rconn_closing(p->executer, ofconn->rconn);
 -    }
 -
      list_remove(&ofconn->node);
      rconn_destroy(ofconn->rconn);
      rconn_packet_counter_destroy(ofconn->packet_in_counter);
@@@ -1371,7 -1413,7 +1371,7 @@@ ofconn_run(struct ofconn *ofconn, struc
      }
  
      if (ofconn != p->controller && !rconn_is_alive(ofconn->rconn)) {
 -        ofconn_destroy(ofconn, p);
 +        ofconn_destroy(ofconn);
      }
  }
  
@@@ -1393,7 -1435,7 +1393,7 @@@ rule_create(struct ofproto *ofproto, st
              const union ofp_action *actions, size_t n_actions,
              uint16_t idle_timeout, uint16_t hard_timeout)
  {
 -    struct rule *rule = xcalloc(1, sizeof *rule);
 +    struct rule *rule = xzalloc(sizeof *rule);
      rule->idle_timeout = idle_timeout;
      rule->hard_timeout = hard_timeout;
      rule->used = rule->created = time_msec();
@@@ -2497,7 -2539,7 +2497,7 @@@ query_stats(struct ofproto *p, struct r
      byte_count = rule->byte_count;
  
      n_odp_flows = rule->cr.wc.wildcards ? list_size(&rule->list) : 1;
 -    odp_flows = xcalloc(1, n_odp_flows * sizeof *odp_flows);
 +    odp_flows = xzalloc(n_odp_flows * sizeof *odp_flows);
      if (rule->cr.wc.wildcards) {
          size_t i = 0;
          LIST_FOR_EACH (subrule, struct rule, list, &rule->list) {
@@@ -2795,13 -2837,11 +2795,13 @@@ add_flow(struct ofproto *p, struct ofco
                         ntohs(ofm->hard_timeout));
      cls_rule_from_match(&rule->cr, &ofm->match, ntohs(ofm->priority));
  
 -    packet = NULL;
      error = 0;
      if (ofm->buffer_id != htonl(UINT32_MAX)) {
          error = pktbuf_retrieve(ofconn->pktbuf, ntohl(ofm->buffer_id),
                                  &packet, &in_port);
 +    } else {
 +        packet = NULL;
 +        in_port = -1;
      }
  
      rule_insert(p, rule, packet, in_port);
@@@ -2957,6 -2997,58 +2957,6 @@@ handle_flow_mod(struct ofproto *p, stru
      }
  }
  
 -static void
 -send_capability_reply(struct ofproto *p, struct ofconn *ofconn, uint32_t xid)
 -{
 -    struct ofmp_capability_reply *ocr;
 -    struct ofpbuf *b;
 -    char capabilities[] = "com.nicira.mgmt.manager=false\n";
 -
 -    ocr = make_openflow_xid(sizeof(*ocr), OFPT_VENDOR, xid, &b);
 -    ocr->header.header.vendor = htonl(NX_VENDOR_ID);
 -    ocr->header.header.subtype = htonl(NXT_MGMT);
 -    ocr->header.type = htons(OFMPT_CAPABILITY_REPLY);
 -
 -    ocr->format = htonl(OFMPCOF_SIMPLE);
 -    ocr->mgmt_id = htonll(p->mgmt_id);
 -
 -    ofpbuf_put(b, capabilities, strlen(capabilities));
 -
 -    queue_tx(b, ofconn, ofconn->reply_counter);
 -}
 -
 -static int
 -handle_ofmp(struct ofproto *p, struct ofconn *ofconn, 
 -            struct ofmp_header *ofmph)
 -{
 -    size_t msg_len = ntohs(ofmph->header.header.length);
 -    if (msg_len < sizeof(*ofmph)) {
 -        VLOG_WARN_RL(&rl, "dropping short managment message: %zu\n", msg_len);
 -        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
 -    }
 -
 -    if (ofmph->type == htons(OFMPT_CAPABILITY_REQUEST)) {
 -        struct ofmp_capability_request *ofmpcr;
 -
 -        if (msg_len < sizeof(struct ofmp_capability_request)) {
 -            VLOG_WARN_RL(&rl, "dropping short capability request: %zu\n",
 -                    msg_len);
 -            return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
 -        }
 -
 -        ofmpcr = (struct ofmp_capability_request *)ofmph;
 -        if (ofmpcr->format != htonl(OFMPCAF_SIMPLE)) {
 -            /* xxx Find a better type than bad subtype */
 -            return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
 -        }
 -
 -        send_capability_reply(p, ofconn, ofmph->header.header.xid);
 -        return 0;
 -    } else {
 -        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
 -    }
 -}
 -
  static int
  handle_vendor(struct ofproto *p, struct ofconn *ofconn, void *msg)
  {
      case NXT_STATUS_REQUEST:
          return switch_status_handle_request(p->switch_status, ofconn->rconn,
                                              msg);
 -
 -    case NXT_ACT_SET_CONFIG:
 -        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE); /* XXX */
 -
 -    case NXT_ACT_GET_CONFIG:
 -        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE); /* XXX */
 -
 -    case NXT_COMMAND_REQUEST:
 -        if (p->executer) {
 -            return executer_handle_request(p->executer, ofconn->rconn, msg);
 -        }
 -        break;
 -
 -    case NXT_MGMT:
 -        return handle_ofmp(p, ofconn, msg);
      }
  
      return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
@@@ -3460,7 -3567,10 +3460,7 @@@ static uint64_
  pick_fallback_dpid(void)
  {
      uint8_t ea[ETH_ADDR_LEN];
 -    eth_addr_random(ea);
 -    ea[0] = 0x00;               /* Set Nicira OUI. */
 -    ea[1] = 0x23;
 -    ea[2] = 0x20;
 +    eth_addr_nicira_random(ea);
      return eth_addr_to_uint64(ea);
  }
  \f
diff --combined ovsdb/execution.c
index 3e3d356,0000000..b984b1b
mode 100644,000000..100644
--- /dev/null
@@@ -1,706 -1,0 +1,706 @@@
-                      struct json *result UNUSED)
 +/* Copyright (c) 2009, 2010 Nicira Networks
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at:
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <config.h>
 +
 +#include <assert.h>
 +#include <limits.h>
 +
 +#include "column.h"
 +#include "condition.h"
 +#include "file.h"
 +#include "json.h"
 +#include "mutation.h"
 +#include "ovsdb-data.h"
 +#include "ovsdb-error.h"
 +#include "ovsdb-parser.h"
 +#include "ovsdb.h"
 +#include "query.h"
 +#include "row.h"
 +#include "table.h"
 +#include "timeval.h"
 +#include "transaction.h"
 +
 +struct ovsdb_execution {
 +    struct ovsdb *db;
 +    struct ovsdb_txn *txn;
 +    struct ovsdb_symbol_table *symtab;
 +    bool durable;
 +
 +    /* Triggers. */
 +    long long int elapsed_msec;
 +    long long int timeout_msec;
 +};
 +
 +typedef struct ovsdb_error *ovsdb_operation_executor(struct ovsdb_execution *,
 +                                                     struct ovsdb_parser *,
 +                                                     struct json *result);
 +
 +static ovsdb_operation_executor ovsdb_execute_insert;
 +static ovsdb_operation_executor ovsdb_execute_select;
 +static ovsdb_operation_executor ovsdb_execute_update;
 +static ovsdb_operation_executor ovsdb_execute_mutate;
 +static ovsdb_operation_executor ovsdb_execute_delete;
 +static ovsdb_operation_executor ovsdb_execute_wait;
 +static ovsdb_operation_executor ovsdb_execute_commit;
 +static ovsdb_operation_executor ovsdb_execute_abort;
 +static ovsdb_operation_executor ovsdb_execute_comment;
 +
 +static ovsdb_operation_executor *
 +lookup_executor(const char *name)
 +{
 +    struct ovsdb_operation {
 +        const char *name;
 +        ovsdb_operation_executor *executor;
 +    };
 +
 +    static const struct ovsdb_operation operations[] = {
 +        { "insert", ovsdb_execute_insert },
 +        { "select", ovsdb_execute_select },
 +        { "update", ovsdb_execute_update },
 +        { "mutate", ovsdb_execute_mutate },
 +        { "delete", ovsdb_execute_delete },
 +        { "wait", ovsdb_execute_wait },
 +        { "commit", ovsdb_execute_commit },
 +        { "abort", ovsdb_execute_abort },
 +        { "comment", ovsdb_execute_comment },
 +    };
 +
 +    size_t i;
 +
 +    for (i = 0; i < ARRAY_SIZE(operations); i++) {
 +        const struct ovsdb_operation *c = &operations[i];
 +        if (!strcmp(c->name, name)) {
 +            return c->executor;
 +        }
 +    }
 +    return NULL;
 +}
 +
 +struct json *
 +ovsdb_execute(struct ovsdb *db, const struct json *params,
 +              long long int elapsed_msec, long long int *timeout_msec)
 +{
 +    struct ovsdb_execution x;
 +    struct ovsdb_error *error;
 +    struct json *results;
 +    size_t n_operations;
 +    size_t i;
 +
 +    if (params->type != JSON_ARRAY
 +        || !params->u.array.n
 +        || params->u.array.elems[0]->type != JSON_STRING
 +        || strcmp(params->u.array.elems[0]->u.string, db->schema->name)) {
 +        struct ovsdb_error *error;
 +
 +        if (params->type != JSON_ARRAY) {
 +            error = ovsdb_syntax_error(params, NULL, "array expected");
 +        } else {
 +            error = ovsdb_syntax_error(params, NULL, "database name expected "
 +                                       "as first parameter");
 +        }
 +
 +        results = ovsdb_error_to_json(error);
 +        ovsdb_error_destroy(error);
 +        return results;
 +    }
 +
 +    x.db = db;
 +    x.txn = ovsdb_txn_create(db);
 +    x.symtab = ovsdb_symbol_table_create();
 +    x.durable = false;
 +    x.elapsed_msec = elapsed_msec;
 +    x.timeout_msec = LLONG_MAX;
 +    results = NULL;
 +
 +    results = json_array_create_empty();
 +    n_operations = params->u.array.n - 1;
 +    error = NULL;
 +    for (i = 1; i <= n_operations; i++) {
 +        struct json *operation = params->u.array.elems[i];
 +        struct ovsdb_error *parse_error;
 +        struct ovsdb_parser parser;
 +        struct json *result;
 +        const struct json *op;
 +
 +        /* Parse and execute operation. */
 +        ovsdb_parser_init(&parser, operation,
 +                          "ovsdb operation %zu of %zu", i + 1, n_operations);
 +        op = ovsdb_parser_member(&parser, "op", OP_ID);
 +        result = json_object_create();
 +        if (op) {
 +            const char *op_name = json_string(op);
 +            ovsdb_operation_executor *executor = lookup_executor(op_name);
 +            if (executor) {
 +                error = executor(&x, &parser, result);
 +            } else {
 +                ovsdb_parser_raise_error(&parser, "No operation \"%s\"",
 +                                         op_name);
 +            }
 +        } else {
 +            assert(ovsdb_parser_has_error(&parser));
 +        }
 +
 +        /* A parse error overrides any other error.
 +         * An error overrides any other result. */
 +        parse_error = ovsdb_parser_finish(&parser);
 +        if (parse_error) {
 +            ovsdb_error_destroy(error);
 +            error = parse_error;
 +        }
 +        if (error) {
 +            json_destroy(result);
 +            result = ovsdb_error_to_json(error);
 +        }
 +        if (error && !strcmp(ovsdb_error_get_tag(error), "not supported")
 +            && timeout_msec) {
 +            ovsdb_txn_abort(x.txn);
 +            *timeout_msec = x.timeout_msec;
 +
 +            json_destroy(result);
 +            json_destroy(results);
 +            results = NULL;
 +            goto exit;
 +        }
 +
 +        /* Add result to array. */
 +        json_array_add(results, result);
 +        if (error) {
 +            break;
 +        }
 +    }
 +
 +    if (!error) {
 +        error = ovsdb_txn_commit(x.txn, x.durable);
 +        if (error) {
 +            json_array_add(results, ovsdb_error_to_json(error));
 +        }
 +    } else {
 +        ovsdb_txn_abort(x.txn);
 +    }
 +
 +    while (json_array(results)->n < n_operations) {
 +        json_array_add(results, json_null_create());
 +    }
 +
 +exit:
 +    ovsdb_error_destroy(error);
 +    ovsdb_symbol_table_destroy(x.symtab);
 +
 +    return results;
 +}
 +
 +struct ovsdb_error *
 +ovsdb_execute_commit(struct ovsdb_execution *x, struct ovsdb_parser *parser,
- ovsdb_execute_abort(struct ovsdb_execution *x UNUSED,
-                     struct ovsdb_parser *parser UNUSED,
-                     struct json *result UNUSED)
++                     struct json *result OVS_UNUSED)
 +{
 +    const struct json *durable;
 +
 +    durable = ovsdb_parser_member(parser, "durable", OP_BOOLEAN);
 +    if (durable && json_boolean(durable)) {
 +        x->durable = true;
 +    }
 +    return NULL;
 +}
 +
 +static struct ovsdb_error *
-                    struct json *result UNUSED)
++ovsdb_execute_abort(struct ovsdb_execution *x OVS_UNUSED,
++                    struct ovsdb_parser *parser OVS_UNUSED,
++                    struct json *result OVS_UNUSED)
 +{
 +    return ovsdb_error("aborted", "aborted by request");
 +}
 +
 +static struct ovsdb_table *
 +parse_table(struct ovsdb_execution *x,
 +            struct ovsdb_parser *parser, const char *member)
 +{
 +    struct ovsdb_table *table;
 +    const char *table_name;
 +    const struct json *json;
 +
 +    json = ovsdb_parser_member(parser, member, OP_ID);
 +    if (!json) {
 +        return NULL;
 +    }
 +    table_name = json_string(json);
 +
 +    table = shash_find_data(&x->db->tables, table_name);
 +    if (!table) {
 +        ovsdb_parser_raise_error(parser, "No table named %s.", table_name);
 +    }
 +    return table;
 +}
 +
 +static WARN_UNUSED_RESULT struct ovsdb_error *
 +parse_row(struct ovsdb_parser *parser, const char *member,
 +          const struct ovsdb_table *table,
 +          struct ovsdb_symbol_table *symtab,
 +          struct ovsdb_row **rowp, struct ovsdb_column_set *columns)
 +{
 +    struct ovsdb_error *error;
 +    const struct json *json;
 +    struct ovsdb_row *row;
 +
 +    *rowp = NULL;
 +
 +    if (!table) {
 +        return OVSDB_BUG("null table");
 +    }
 +    json = ovsdb_parser_member(parser, member, OP_OBJECT);
 +    if (!json) {
 +        return OVSDB_BUG("null row member");
 +    }
 +
 +    row = ovsdb_row_create(table);
 +    error = ovsdb_row_from_json(row, json, symtab, columns);
 +    if (error) {
 +        ovsdb_row_destroy(row);
 +        return error;
 +    } else {
 +        *rowp = row;
 +        return NULL;
 +    }
 +}
 +
 +struct ovsdb_error *
 +ovsdb_execute_insert(struct ovsdb_execution *x, struct ovsdb_parser *parser,
 +                     struct json *result)
 +{
 +    struct ovsdb_table *table;
 +    struct ovsdb_row *row = NULL;
 +    const struct json *uuid_name;
 +    struct ovsdb_error *error;
 +    struct uuid row_uuid;
 +
 +    table = parse_table(x, parser, "table");
 +    uuid_name = ovsdb_parser_member(parser, "uuid-name", OP_ID | OP_OPTIONAL);
 +    error = ovsdb_parser_get_error(parser);
 +
 +    if (uuid_name) {
 +        struct ovsdb_symbol *symbol;
 +
 +        symbol = ovsdb_symbol_table_insert(x->symtab, json_string(uuid_name));
 +        if (symbol->used) {
 +            return ovsdb_syntax_error(uuid_name, "duplicate uuid-name",
 +                                      "This \"uuid-name\" appeared on an "
 +                                      "earlier \"insert\" operation.");
 +        }
 +        row_uuid = symbol->uuid;
 +        symbol->used = true;
 +    } else {
 +        uuid_generate(&row_uuid);
 +    }
 +
 +    if (!error) {
 +        error = parse_row(parser, "row", table, x->symtab, &row, NULL);
 +    }
 +    if (!error) {
 +        /* Check constraints for columns not included in "row", in case the
 +         * default values do not satisfy the constraints.  We could check only
 +         * the columns that have their default values by supplying an
 +         * ovsdb_column_set to parse_row() above, but I suspect that this is
 +         * cheaper.  */
 +        const struct shash_node *node;
 +
 +        SHASH_FOR_EACH (node, &table->schema->columns) {
 +            const struct ovsdb_column *column = node->data;
 +            const struct ovsdb_datum *datum = &row->fields[column->index];
 +
 +            /* If there are 0 keys or pairs, there's nothing to check.
 +             * If there is 1, it might be a default value.
 +             * If there are more, it can't be a default value, so the value has
 +             * already been checked. */
 +            if (datum->n == 1) {
 +                error = ovsdb_datum_check_constraints(datum, &column->type);
 +                if (error) {
 +                    ovsdb_row_destroy(row);
 +                    break;
 +                }
 +            }
 +        }
 +    }
 +    if (!error) {
 +        *ovsdb_row_get_uuid_rw(row) = row_uuid;
 +        ovsdb_txn_row_insert(x->txn, row);
 +        json_object_put(result, "uuid",
 +                        ovsdb_datum_to_json(&row->fields[OVSDB_COL_UUID],
 +                                            &ovsdb_type_uuid));
 +    }
 +    return error;
 +}
 +
 +struct ovsdb_error *
 +ovsdb_execute_select(struct ovsdb_execution *x, struct ovsdb_parser *parser,
 +                     struct json *result)
 +{
 +    struct ovsdb_table *table;
 +    const struct json *where, *columns_json, *sort_json;
 +    struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER;
 +    struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER;
 +    struct ovsdb_column_set sort = OVSDB_COLUMN_SET_INITIALIZER;
 +    struct ovsdb_error *error;
 +
 +    table = parse_table(x, parser, "table");
 +    where = ovsdb_parser_member(parser, "where", OP_ARRAY);
 +    columns_json = ovsdb_parser_member(parser, "columns",
 +                                       OP_ARRAY | OP_OPTIONAL);
 +    sort_json = ovsdb_parser_member(parser, "sort", OP_ARRAY | OP_OPTIONAL);
 +
 +    error = ovsdb_parser_get_error(parser);
 +    if (!error) {
 +        error = ovsdb_condition_from_json(table->schema, where, x->symtab,
 +                                          &condition);
 +    }
 +    if (!error) {
 +        error = ovsdb_column_set_from_json(columns_json, table, &columns);
 +    }
 +    if (!error) {
 +        error = ovsdb_column_set_from_json(sort_json, table, &sort);
 +    }
 +    if (!error) {
 +        struct ovsdb_row_set rows = OVSDB_ROW_SET_INITIALIZER;
 +
 +        ovsdb_query_distinct(table, &condition, &columns, &rows);
 +        ovsdb_row_set_sort(&rows, &sort);
 +        json_object_put(result, "rows",
 +                        ovsdb_row_set_to_json(&rows, &columns));
 +
 +        ovsdb_row_set_destroy(&rows);
 +    }
 +
 +    ovsdb_column_set_destroy(&columns);
 +    ovsdb_column_set_destroy(&sort);
 +    ovsdb_condition_destroy(&condition);
 +
 +    return error;
 +}
 +
 +struct update_row_cbdata {
 +    size_t n_matches;
 +    struct ovsdb_txn *txn;
 +    const struct ovsdb_row *row;
 +    const struct ovsdb_column_set *columns;
 +};
 +
 +static bool
 +update_row_cb(const struct ovsdb_row *row, void *ur_)
 +{
 +    struct update_row_cbdata *ur = ur_;
 +
 +    ur->n_matches++;
 +    if (!ovsdb_row_equal_columns(row, ur->row, ur->columns)) {
 +        ovsdb_row_update_columns(ovsdb_txn_row_modify(ur->txn, row),
 +                                 ur->row, ur->columns);
 +    }
 +
 +    return true;
 +}
 +
 +struct ovsdb_error *
 +ovsdb_execute_update(struct ovsdb_execution *x, struct ovsdb_parser *parser,
 +                     struct json *result)
 +{
 +    struct ovsdb_table *table;
 +    const struct json *where;
 +    struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER;
 +    struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER;
 +    struct ovsdb_row *row = NULL;
 +    struct update_row_cbdata ur;
 +    struct ovsdb_error *error;
 +
 +    table = parse_table(x, parser, "table");
 +    where = ovsdb_parser_member(parser, "where", OP_ARRAY);
 +    error = ovsdb_parser_get_error(parser);
 +    if (!error) {
 +        error = parse_row(parser, "row", table, x->symtab, &row, &columns);
 +    }
 +    if (!error) {
 +        error = ovsdb_condition_from_json(table->schema, where, x->symtab,
 +                                          &condition);
 +    }
 +    if (!error) {
 +        ur.n_matches = 0;
 +        ur.txn = x->txn;
 +        ur.row = row;
 +        ur.columns = &columns;
 +        ovsdb_query(table, &condition, update_row_cb, &ur);
 +        json_object_put(result, "count", json_integer_create(ur.n_matches));
 +    }
 +
 +    ovsdb_row_destroy(row);
 +    ovsdb_column_set_destroy(&columns);
 +    ovsdb_condition_destroy(&condition);
 +
 +    return error;
 +}
 +
 +struct mutate_row_cbdata {
 +    size_t n_matches;
 +    struct ovsdb_txn *txn;
 +    const struct ovsdb_mutation_set *mutations;
 +};
 +
 +static bool
 +mutate_row_cb(const struct ovsdb_row *row, void *mr_)
 +{
 +    struct mutate_row_cbdata *mr = mr_;
 +
 +    mr->n_matches++;
 +    ovsdb_mutation_set_execute(ovsdb_txn_row_modify(mr->txn, row),
 +                               mr->mutations);
 +
 +    return true;
 +}
 +
 +struct ovsdb_error *
 +ovsdb_execute_mutate(struct ovsdb_execution *x, struct ovsdb_parser *parser,
 +                     struct json *result)
 +{
 +    struct ovsdb_table *table;
 +    const struct json *where;
 +    const struct json *mutations_json;
 +    struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER;
 +    struct ovsdb_mutation_set mutations = OVSDB_MUTATION_SET_INITIALIZER;
 +    struct ovsdb_row *row = NULL;
 +    struct mutate_row_cbdata mr;
 +    struct ovsdb_error *error;
 +
 +    table = parse_table(x, parser, "table");
 +    where = ovsdb_parser_member(parser, "where", OP_ARRAY);
 +    mutations_json = ovsdb_parser_member(parser, "mutations", OP_ARRAY);
 +    error = ovsdb_parser_get_error(parser);
 +    if (!error) {
 +        error = ovsdb_mutation_set_from_json(table->schema, mutations_json,
 +                                             x->symtab, &mutations);
 +    }
 +    if (!error) {
 +        error = ovsdb_condition_from_json(table->schema, where, x->symtab,
 +                                          &condition);
 +    }
 +    if (!error) {
 +        mr.n_matches = 0;
 +        mr.txn = x->txn;
 +        mr.mutations = &mutations;
 +        ovsdb_query(table, &condition, mutate_row_cb, &mr);
 +        json_object_put(result, "count", json_integer_create(mr.n_matches));
 +    }
 +
 +    ovsdb_row_destroy(row);
 +    ovsdb_mutation_set_destroy(&mutations);
 +    ovsdb_condition_destroy(&condition);
 +
 +    return error;
 +}
 +
 +struct delete_row_cbdata {
 +    size_t n_matches;
 +    const struct ovsdb_table *table;
 +    struct ovsdb_txn *txn;
 +};
 +
 +static bool
 +delete_row_cb(const struct ovsdb_row *row, void *dr_)
 +{
 +    struct delete_row_cbdata *dr = dr_;
 +
 +    dr->n_matches++;
 +    ovsdb_txn_row_delete(dr->txn, row);
 +
 +    return true;
 +}
 +
 +struct ovsdb_error *
 +ovsdb_execute_delete(struct ovsdb_execution *x, struct ovsdb_parser *parser,
 +                     struct json *result)
 +{
 +    struct ovsdb_table *table;
 +    const struct json *where;
 +    struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER;
 +    struct ovsdb_error *error;
 +
 +    where = ovsdb_parser_member(parser, "where", OP_ARRAY);
 +    table = parse_table(x, parser, "table");
 +    error = ovsdb_parser_get_error(parser);
 +    if (!error) {
 +        error = ovsdb_condition_from_json(table->schema, where, x->symtab,
 +                                          &condition);
 +    }
 +    if (!error) {
 +        struct delete_row_cbdata dr;
 +
 +        dr.n_matches = 0;
 +        dr.table = table;
 +        dr.txn = x->txn;
 +        ovsdb_query(table, &condition, delete_row_cb, &dr);
 +
 +        json_object_put(result, "count", json_integer_create(dr.n_matches));
 +    }
 +
 +    ovsdb_condition_destroy(&condition);
 +
 +    return error;
 +}
 +
 +struct wait_auxdata {
 +    struct ovsdb_row_hash *actual;
 +    struct ovsdb_row_hash *expected;
 +    bool *equal;
 +};
 +
 +static bool
 +ovsdb_execute_wait_query_cb(const struct ovsdb_row *row, void *aux_)
 +{
 +    struct wait_auxdata *aux = aux_;
 +
 +    if (ovsdb_row_hash_contains(aux->expected, row)) {
 +        ovsdb_row_hash_insert(aux->actual, row);
 +        return true;
 +    } else {
 +        /* The query row isn't in the expected result set, so the actual and
 +         * expected results sets definitely differ and we can short-circuit the
 +         * rest of the query. */
 +        *aux->equal = false;
 +        return false;
 +    }
 +}
 +
 +static struct ovsdb_error *
 +ovsdb_execute_wait(struct ovsdb_execution *x, struct ovsdb_parser *parser,
-                       struct json *result UNUSED)
++                   struct json *result OVS_UNUSED)
 +{
 +    struct ovsdb_table *table;
 +    const struct json *timeout, *where, *columns_json, *until, *rows;
 +    struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER;
 +    struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER;
 +    struct ovsdb_row_hash expected = OVSDB_ROW_HASH_INITIALIZER(expected);
 +    struct ovsdb_row_hash actual = OVSDB_ROW_HASH_INITIALIZER(actual);
 +    struct ovsdb_error *error;
 +    struct wait_auxdata aux;
 +    long long int timeout_msec = 0;
 +    size_t i;
 +
 +    timeout = ovsdb_parser_member(parser, "timeout", OP_NUMBER | OP_OPTIONAL);
 +    where = ovsdb_parser_member(parser, "where", OP_ARRAY);
 +    columns_json = ovsdb_parser_member(parser, "columns",
 +                                       OP_ARRAY | OP_OPTIONAL);
 +    until = ovsdb_parser_member(parser, "until", OP_STRING);
 +    rows = ovsdb_parser_member(parser, "rows", OP_ARRAY);
 +    table = parse_table(x, parser, "table");
 +    error = ovsdb_parser_get_error(parser);
 +    if (!error) {
 +        error = ovsdb_condition_from_json(table->schema, where, x->symtab,
 +                                          &condition);
 +    }
 +    if (!error) {
 +        error = ovsdb_column_set_from_json(columns_json, table, &columns);
 +    }
 +    if (!error) {
 +        if (timeout) {
 +            timeout_msec = MIN(LLONG_MAX, json_real(timeout));
 +            if (timeout_msec < 0) {
 +                error = ovsdb_syntax_error(timeout, NULL,
 +                                           "timeout must be nonnegative");
 +            } else if (timeout_msec < x->timeout_msec) {
 +                x->timeout_msec = timeout_msec;
 +            }
 +        } else {
 +            timeout_msec = LLONG_MAX;
 +        }
 +        if (strcmp(json_string(until), "==")
 +            && strcmp(json_string(until), "!=")) {
 +            error = ovsdb_syntax_error(until, NULL,
 +                                       "\"until\" must be \"==\" or \"!=\"");
 +        }
 +    }
 +    if (!error) {
 +        /* Parse "rows" into 'expected'. */
 +        ovsdb_row_hash_init(&expected, &columns);
 +        for (i = 0; i < rows->u.array.n; i++) {
 +            struct ovsdb_error *error;
 +            struct ovsdb_row *row;
 +
 +            row = ovsdb_row_create(table);
 +            error = ovsdb_row_from_json(row, rows->u.array.elems[i], x->symtab,
 +                                        NULL);
 +            if (error) {
 +                break;
 +            }
 +
 +            if (!ovsdb_row_hash_insert(&expected, row)) {
 +                /* XXX Perhaps we should abort with an error or log a
 +                 * warning. */
 +                ovsdb_row_destroy(row);
 +            }
 +        }
 +    }
 +    if (!error) {
 +        /* Execute query. */
 +        bool equal = true;
 +        ovsdb_row_hash_init(&actual, &columns);
 +        aux.actual = &actual;
 +        aux.expected = &expected;
 +        aux.equal = &equal;
 +        ovsdb_query(table, &condition, ovsdb_execute_wait_query_cb, &aux);
 +        if (equal) {
 +            /* We know that every row in 'actual' is also in 'expected'.  We
 +             * also know that all of the rows in 'actual' are distinct and that
 +             * all of the rows in 'expected' are distinct.  Therefore, if
 +             * 'actual' and 'expected' have the same number of rows, then they
 +             * have the same content. */
 +            size_t n_actual = ovsdb_row_hash_count(&actual);
 +            size_t n_expected = ovsdb_row_hash_count(&expected);
 +            equal = n_actual == n_expected;
 +        }
 +        if (!strcmp(json_string(until), "==") != equal) {
 +            if (timeout && x->elapsed_msec >= timeout_msec) {
 +                if (x->elapsed_msec) {
 +                    error = ovsdb_error("timed out",
 +                                        "\"wait\" timed out after %lld ms",
 +                                        x->elapsed_msec);
 +                } else {
 +                    error = ovsdb_error("timed out", "\"wait\" timed out");
 +                }
 +            } else {
 +                /* ovsdb_execute() will change this, if triggers really are
 +                 * supported. */
 +                error = ovsdb_error("not supported", "triggers not supported");
 +            }
 +        }
 +    }
 +
 +
 +    ovsdb_row_hash_destroy(&expected, true);
 +    ovsdb_row_hash_destroy(&actual, false);
 +    ovsdb_column_set_destroy(&columns);
 +    ovsdb_condition_destroy(&condition);
 +
 +    return error;
 +}
 +
 +static struct ovsdb_error *
 +ovsdb_execute_comment(struct ovsdb_execution *x, struct ovsdb_parser *parser,
++                      struct json *result OVS_UNUSED)
 +{
 +    const struct json *comment;
 +
 +    comment = ovsdb_parser_member(parser, "comment", OP_STRING);
 +    if (!comment) {
 +        return NULL;
 +    }
 +    ovsdb_txn_add_comment(x->txn, json_string(comment));
 +
 +    return NULL;
 +}
diff --combined ovsdb/jsonrpc-server.c
index 26fabe5,0000000..e398464
mode 100644,000000..100644
--- /dev/null
@@@ -1,980 -1,0 +1,981 @@@
-                              const struct ovsdb_txn *txn, bool durable UNUSED)
 +/* Copyright (c) 2009, 2010 Nicira Networks
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at:
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <config.h>
 +
 +#include "jsonrpc-server.h"
 +
 +#include <assert.h>
 +#include <errno.h>
 +
 +#include "column.h"
 +#include "json.h"
 +#include "jsonrpc.h"
 +#include "ovsdb-error.h"
 +#include "ovsdb-parser.h"
 +#include "ovsdb.h"
 +#include "reconnect.h"
 +#include "row.h"
 +#include "stream.h"
 +#include "table.h"
 +#include "timeval.h"
 +#include "transaction.h"
 +#include "trigger.h"
 +
 +#define THIS_MODULE VLM_ovsdb_jsonrpc_server
 +#include "vlog.h"
 +
 +struct ovsdb_jsonrpc_remote;
 +struct ovsdb_jsonrpc_session;
 +
 +/* Message rate-limiting. */
 +struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 +
 +/* Sessions. */
 +static struct ovsdb_jsonrpc_session *ovsdb_jsonrpc_session_create(
 +    struct ovsdb_jsonrpc_remote *, struct jsonrpc_session *);
 +static void ovsdb_jsonrpc_session_run_all(struct ovsdb_jsonrpc_remote *);
 +static void ovsdb_jsonrpc_session_wait_all(struct ovsdb_jsonrpc_remote *);
 +static void ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *);
 +
 +/* Triggers. */
 +static void ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *,
 +                                         struct json *id, struct json *params);
 +static struct ovsdb_jsonrpc_trigger *ovsdb_jsonrpc_trigger_find(
 +    struct ovsdb_jsonrpc_session *, const struct json *id, size_t hash);
 +static void ovsdb_jsonrpc_trigger_complete(struct ovsdb_jsonrpc_trigger *);
 +static void ovsdb_jsonrpc_trigger_complete_all(struct ovsdb_jsonrpc_session *);
 +static void ovsdb_jsonrpc_trigger_complete_done(
 +    struct ovsdb_jsonrpc_session *);
 +
 +/* Monitors. */
 +static struct json *ovsdb_jsonrpc_monitor_create(
 +    struct ovsdb_jsonrpc_session *, struct json *params);
 +static struct jsonrpc_msg *ovsdb_jsonrpc_monitor_cancel(
 +    struct ovsdb_jsonrpc_session *,
 +    struct json_array *params,
 +    const struct json *request_id);
 +static void ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *);
 +\f
 +/* JSON-RPC database server. */
 +
 +struct ovsdb_jsonrpc_server {
 +    struct ovsdb *db;
 +    unsigned int n_sessions, max_sessions;
 +    struct shash remotes;      /* Contains "struct ovsdb_jsonrpc_remote *"s. */
 +};
 +
 +/* A configured remote.  This is either a passive stream listener plus a list
 + * of the currently connected sessions, or a list of exactly one active
 + * session. */
 +struct ovsdb_jsonrpc_remote {
 +    struct ovsdb_jsonrpc_server *server;
 +    struct pstream *listener;   /* Listener, if passive. */
 +    struct list sessions;       /* List of "struct ovsdb_jsonrpc_session"s. */
 +};
 +
 +static void ovsdb_jsonrpc_server_add_remote(struct ovsdb_jsonrpc_server *,
 +                                            const char *name);
 +static void ovsdb_jsonrpc_server_del_remote(struct shash_node *);
 +
 +struct ovsdb_jsonrpc_server *
 +ovsdb_jsonrpc_server_create(struct ovsdb *db)
 +{
 +    struct ovsdb_jsonrpc_server *server = xzalloc(sizeof *server);
 +    server->db = db;
 +    server->max_sessions = 64;
 +    shash_init(&server->remotes);
 +    return server;
 +}
 +
 +void
 +ovsdb_jsonrpc_server_destroy(struct ovsdb_jsonrpc_server *svr)
 +{
 +    struct shash_node *node, *next;
 +
 +    SHASH_FOR_EACH_SAFE (node, next, &svr->remotes) {
 +        ovsdb_jsonrpc_server_del_remote(node);
 +    }
 +    shash_destroy(&svr->remotes);
 +    free(svr);
 +}
 +
 +/* Sets 'svr''s current set of remotes to the names in 'new_remotes'.  The data
 + * values in 'new_remotes' are ignored.
 + *
 + * A remote is an active or passive stream connection method, e.g. "pssl:" or
 + * "tcp:1.2.3.4". */
 +void
 +ovsdb_jsonrpc_server_set_remotes(struct ovsdb_jsonrpc_server *svr,
 +                                 const struct shash *new_remotes)
 +{
 +    struct shash_node *node, *next;
 +
 +    SHASH_FOR_EACH_SAFE (node, next, &svr->remotes) {
 +        if (!shash_find(new_remotes, node->name)) {
 +            ovsdb_jsonrpc_server_del_remote(node);
 +        }
 +    }
 +    SHASH_FOR_EACH (node, new_remotes) {
 +        if (!shash_find(&svr->remotes, node->name)) {
 +            ovsdb_jsonrpc_server_add_remote(svr, node->name);
 +        }
 +    }
 +}
 +
 +static void
 +ovsdb_jsonrpc_server_add_remote(struct ovsdb_jsonrpc_server *svr,
 +                                const char *name)
 +{
 +    struct ovsdb_jsonrpc_remote *remote;
 +    struct pstream *listener;
 +    int error;
 +
 +    error = pstream_open(name, &listener);
 +    if (error && error != EAFNOSUPPORT) {
 +        VLOG_ERR_RL(&rl, "%s: listen failed: %s", name, strerror(error));
 +        return;
 +    }
 +
 +    remote = xmalloc(sizeof *remote);
 +    remote->server = svr;
 +    remote->listener = listener;
 +    list_init(&remote->sessions);
 +    shash_add(&svr->remotes, name, remote);
 +
 +    if (!listener) {
 +        ovsdb_jsonrpc_session_create(remote, jsonrpc_session_open(name));
 +    }
 +}
 +
 +static void
 +ovsdb_jsonrpc_server_del_remote(struct shash_node *node)
 +{
 +    struct ovsdb_jsonrpc_remote *remote = node->data;
 +
 +    ovsdb_jsonrpc_session_close_all(remote);
 +    pstream_close(remote->listener);
 +    shash_delete(&remote->server->remotes, node);
 +    free(remote);
 +}
 +
 +void
 +ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *svr)
 +{
 +    struct shash_node *node;
 +
 +    SHASH_FOR_EACH (node, &svr->remotes) {
 +        struct ovsdb_jsonrpc_remote *remote = node->data;
 +
 +        if (remote->listener && svr->n_sessions < svr->max_sessions) {
 +            struct stream *stream;
 +            int error;
 +
 +            error = pstream_accept(remote->listener, &stream);
 +            if (!error) {
 +                struct jsonrpc_session *js;
 +                js = jsonrpc_session_open_unreliably(jsonrpc_open(stream));
 +                ovsdb_jsonrpc_session_create(remote, js);
 +            } else if (error != EAGAIN) {
 +                VLOG_WARN_RL(&rl, "%s: accept failed: %s",
 +                             pstream_get_name(remote->listener),
 +                             strerror(error));
 +            }
 +        }
 +
 +        ovsdb_jsonrpc_session_run_all(remote);
 +    }
 +}
 +
 +void
 +ovsdb_jsonrpc_server_wait(struct ovsdb_jsonrpc_server *svr)
 +{
 +    struct shash_node *node;
 +
 +    SHASH_FOR_EACH (node, &svr->remotes) {
 +        struct ovsdb_jsonrpc_remote *remote = node->data;
 +
 +        if (remote->listener && svr->n_sessions < svr->max_sessions) {
 +            pstream_wait(remote->listener);
 +        }
 +
 +        ovsdb_jsonrpc_session_wait_all(remote);
 +    }
 +}
 +\f
 +/* JSON-RPC database server session. */
 +
 +struct ovsdb_jsonrpc_session {
 +    struct ovsdb_jsonrpc_remote *remote;
 +    struct list node;           /* Element in remote's sessions list. */
 +
 +    /* Triggers. */
 +    struct hmap triggers;       /* Hmap of "struct ovsdb_jsonrpc_trigger"s. */
 +    struct list completions;    /* Completed triggers. */
 +
 +    /* Monitors. */
 +    struct hmap monitors;       /* Hmap of "struct ovsdb_jsonrpc_monitor"s. */
 +
 +    /* Network connectivity. */
 +    struct jsonrpc_session *js;  /* JSON-RPC session. */
 +    unsigned int js_seqno;       /* Last jsonrpc_session_get_seqno() value. */
 +};
 +
 +static void ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *);
 +static int ovsdb_jsonrpc_session_run(struct ovsdb_jsonrpc_session *);
 +static void ovsdb_jsonrpc_session_wait(struct ovsdb_jsonrpc_session *);
 +static void ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *,
 +                                             struct jsonrpc_msg *);
 +static void ovsdb_jsonrpc_session_got_notify(struct ovsdb_jsonrpc_session *,
 +                                             struct jsonrpc_msg *);
 +
 +static struct ovsdb_jsonrpc_session *
 +ovsdb_jsonrpc_session_create(struct ovsdb_jsonrpc_remote *remote,
 +                             struct jsonrpc_session *js)
 +{
 +    struct ovsdb_jsonrpc_session *s;
 +
 +    s = xzalloc(sizeof *s);
 +    s->remote = remote;
 +    list_push_back(&remote->sessions, &s->node);
 +    hmap_init(&s->triggers);
 +    hmap_init(&s->monitors);
 +    list_init(&s->completions);
 +    s->js = js;
 +    s->js_seqno = jsonrpc_session_get_seqno(js);
 +
 +    remote->server->n_sessions++;
 +
 +    return s;
 +}
 +
 +static void
 +ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *s)
 +{
 +    ovsdb_jsonrpc_monitor_remove_all(s);
 +    jsonrpc_session_close(s->js);
 +    list_remove(&s->node);
 +    s->remote->server->n_sessions--;
 +    free(s);
 +}
 +
 +static int
 +ovsdb_jsonrpc_session_run(struct ovsdb_jsonrpc_session *s)
 +{
 +    jsonrpc_session_run(s->js);
 +    if (s->js_seqno != jsonrpc_session_get_seqno(s->js)) {
 +        s->js_seqno = jsonrpc_session_get_seqno(s->js);
 +        ovsdb_jsonrpc_trigger_complete_all(s);
 +        ovsdb_jsonrpc_monitor_remove_all(s);
 +    }
 +
 +    ovsdb_jsonrpc_trigger_complete_done(s);
 +
 +    if (!jsonrpc_session_get_backlog(s->js)) {
 +        struct jsonrpc_msg *msg = jsonrpc_session_recv(s->js);
 +        if (msg) {
 +            if (msg->type == JSONRPC_REQUEST) {
 +                ovsdb_jsonrpc_session_got_request(s, msg);
 +            } else if (msg->type == JSONRPC_NOTIFY) {
 +                ovsdb_jsonrpc_session_got_notify(s, msg);
 +            } else {
 +                VLOG_WARN("%s: received unexpected %s message",
 +                          jsonrpc_session_get_name(s->js),
 +                          jsonrpc_msg_type_to_string(msg->type));
 +                jsonrpc_session_force_reconnect(s->js);
 +                jsonrpc_msg_destroy(msg);
 +            }
 +        }
 +    }
 +    return jsonrpc_session_is_alive(s->js) ? 0 : ETIMEDOUT;
 +}
 +
 +static void
 +ovsdb_jsonrpc_session_run_all(struct ovsdb_jsonrpc_remote *remote)
 +{
 +    struct ovsdb_jsonrpc_session *s, *next;
 +
 +    LIST_FOR_EACH_SAFE (s, next, struct ovsdb_jsonrpc_session, node,
 +                        &remote->sessions) {
 +        int error = ovsdb_jsonrpc_session_run(s);
 +        if (error) {
 +            ovsdb_jsonrpc_session_close(s);
 +        }
 +    }
 +}
 +
 +static void
 +ovsdb_jsonrpc_session_wait(struct ovsdb_jsonrpc_session *s)
 +{
 +    jsonrpc_session_wait(s->js);
 +    if (!jsonrpc_session_get_backlog(s->js)) {
 +        jsonrpc_session_recv_wait(s->js);
 +    }
 +}
 +
 +static void
 +ovsdb_jsonrpc_session_wait_all(struct ovsdb_jsonrpc_remote *remote)
 +{
 +    struct ovsdb_jsonrpc_session *s;
 +
 +    LIST_FOR_EACH (s, struct ovsdb_jsonrpc_session, node, &remote->sessions) {
 +        ovsdb_jsonrpc_session_wait(s);
 +    }
 +}
 +
 +static void
 +ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *remote)
 +{
 +    struct ovsdb_jsonrpc_session *s, *next;
 +
 +    LIST_FOR_EACH_SAFE (s, next, struct ovsdb_jsonrpc_session, node,
 +                        &remote->sessions) {
 +        ovsdb_jsonrpc_session_close(s);
 +    }
 +}
 +
 +static const char *
 +get_db_name(const struct ovsdb_jsonrpc_session *s)
 +{
 +    return s->remote->server->db->schema->name;
 +}
 +
 +static struct jsonrpc_msg *
 +ovsdb_jsonrpc_check_db_name(const struct ovsdb_jsonrpc_session *s,
 +                            const struct jsonrpc_msg *request)
 +{
 +    struct json_array *params;
 +    const char *want_db_name;
 +    const char *have_db_name;
 +    struct ovsdb_error *error;
 +    struct jsonrpc_msg *reply;
 +
 +    params = json_array(request->params);
 +    if (!params->n || params->elems[0]->type != JSON_STRING) {
 +        error = ovsdb_syntax_error(
 +            request->params, NULL,
 +            "%s request params must begin with <db-name>", request->method);
 +        goto error;
 +    }
 +
 +    want_db_name = params->elems[0]->u.string;
 +    have_db_name = get_db_name(s);
 +    if (strcmp(want_db_name, have_db_name)) {
 +        error = ovsdb_syntax_error(
 +            request->params, "unknown database",
 +            "%s request specifies unknown database %s",
 +            request->method, want_db_name);
 +        goto error;
 +    }
 +
 +    return NULL;
 +
 +error:
 +    reply = jsonrpc_create_reply(ovsdb_error_to_json(error), request->id);
 +    ovsdb_error_destroy(error);
 +    return reply;
 +}
 +
 +static struct jsonrpc_msg *
 +execute_transaction(struct ovsdb_jsonrpc_session *s,
 +                    struct jsonrpc_msg *request)
 +{
 +    ovsdb_jsonrpc_trigger_create(s, request->id, request->params);
 +    request->id = NULL;
 +    request->params = NULL;
 +    jsonrpc_msg_destroy(request);
 +    return NULL;
 +}
 +
 +static void
 +ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s,
 +                                  struct jsonrpc_msg *request)
 +{
 +    struct jsonrpc_msg *reply;
 +
 +    if (!strcmp(request->method, "transact")) {
 +        reply = ovsdb_jsonrpc_check_db_name(s, request);
 +        if (!reply) {
 +            reply = execute_transaction(s, request);
 +        }
 +    } else if (!strcmp(request->method, "monitor")) {
 +        reply = ovsdb_jsonrpc_check_db_name(s, request);
 +        if (!reply) {
 +            reply = jsonrpc_create_reply(
 +                ovsdb_jsonrpc_monitor_create(s, request->params), request->id);
 +        }
 +    } else if (!strcmp(request->method, "monitor_cancel")) {
 +        reply = ovsdb_jsonrpc_monitor_cancel(s, json_array(request->params),
 +                                             request->id);
 +    } else if (!strcmp(request->method, "get_schema")) {
 +        reply = ovsdb_jsonrpc_check_db_name(s, request);
 +        if (!reply) {
 +            reply = jsonrpc_create_reply(
 +                ovsdb_schema_to_json(s->remote->server->db->schema),
 +                request->id);
 +        }
 +    } else if (!strcmp(request->method, "list_dbs")) {
 +        reply = jsonrpc_create_reply(
 +            json_array_create_1(json_string_create(get_db_name(s))),
 +            request->id);
 +    } else if (!strcmp(request->method, "echo")) {
 +        reply = jsonrpc_create_reply(json_clone(request->params), request->id);
 +    } else {
 +        reply = jsonrpc_create_error(json_string_create("unknown method"),
 +                                     request->id);
 +    }
 +
 +    if (reply) {
 +        jsonrpc_msg_destroy(request);
 +        jsonrpc_session_send(s->js, reply);
 +    }
 +}
 +
 +static void
 +execute_cancel(struct ovsdb_jsonrpc_session *s, struct jsonrpc_msg *request)
 +{
 +    if (json_array(request->params)->n == 1) {
 +        struct ovsdb_jsonrpc_trigger *t;
 +        struct json *id;
 +
 +        id = request->params->u.array.elems[0];
 +        t = ovsdb_jsonrpc_trigger_find(s, id, json_hash(id, 0));
 +        if (t) {
 +            ovsdb_jsonrpc_trigger_complete(t);
 +        }
 +    }
 +}
 +
 +static void
 +ovsdb_jsonrpc_session_got_notify(struct ovsdb_jsonrpc_session *s,
 +                                 struct jsonrpc_msg *request)
 +{
 +    if (!strcmp(request->method, "cancel")) {
 +        execute_cancel(s, request);
 +    }
 +    jsonrpc_msg_destroy(request);
 +}
 +\f
 +/* JSON-RPC database server triggers.
 + *
 + * (Every transaction is treated as a trigger even if it doesn't actually have
 + * any "wait" operations.) */
 +
 +struct ovsdb_jsonrpc_trigger {
 +    struct ovsdb_trigger trigger;
 +    struct ovsdb_jsonrpc_session *session;
 +    struct hmap_node hmap_node; /* In session's "triggers" hmap. */
 +    struct json *id;
 +};
 +
 +static void
 +ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *s,
 +                             struct json *id, struct json *params)
 +{
 +    struct ovsdb_jsonrpc_trigger *t;
 +    size_t hash;
 +
 +    /* Check for duplicate ID. */
 +    hash = json_hash(id, 0);
 +    t = ovsdb_jsonrpc_trigger_find(s, id, hash);
 +    if (t) {
 +        struct jsonrpc_msg *msg;
 +
 +        msg = jsonrpc_create_error(json_string_create("duplicate request ID"),
 +                                   id);
 +        jsonrpc_session_send(s->js, msg);
 +        json_destroy(id);
 +        json_destroy(params);
 +        return;
 +    }
 +
 +    /* Insert into trigger table. */
 +    t = xmalloc(sizeof *t);
 +    ovsdb_trigger_init(s->remote->server->db,
 +                       &t->trigger, params, &s->completions,
 +                       time_msec());
 +    t->session = s;
 +    t->id = id;
 +    hmap_insert(&s->triggers, &t->hmap_node, hash);
 +
 +    /* Complete early if possible. */
 +    if (ovsdb_trigger_is_complete(&t->trigger)) {
 +        ovsdb_jsonrpc_trigger_complete(t);
 +    }
 +}
 +
 +static struct ovsdb_jsonrpc_trigger *
 +ovsdb_jsonrpc_trigger_find(struct ovsdb_jsonrpc_session *s,
 +                           const struct json *id, size_t hash)
 +{
 +    struct ovsdb_jsonrpc_trigger *t;
 +
 +    HMAP_FOR_EACH_WITH_HASH (t, struct ovsdb_jsonrpc_trigger, hmap_node, hash,
 +                             &s->triggers) {
 +        if (json_equal(t->id, id)) {
 +            return t;
 +        }
 +    }
 +
 +    return NULL;
 +}
 +
 +static void
 +ovsdb_jsonrpc_trigger_complete(struct ovsdb_jsonrpc_trigger *t)
 +{
 +    struct ovsdb_jsonrpc_session *s = t->session;
 +
 +    if (jsonrpc_session_is_connected(s->js)) {
 +        struct jsonrpc_msg *reply;
 +        struct json *result;
 +
 +        result = ovsdb_trigger_steal_result(&t->trigger);
 +        if (result) {
 +            reply = jsonrpc_create_reply(result, t->id);
 +        } else {
 +            reply = jsonrpc_create_error(json_string_create("canceled"),
 +                                         t->id);
 +        }
 +        jsonrpc_session_send(s->js, reply);
 +    }
 +
 +    json_destroy(t->id);
 +    ovsdb_trigger_destroy(&t->trigger);
 +    hmap_remove(&s->triggers, &t->hmap_node);
 +    free(t);
 +}
 +
 +static void
 +ovsdb_jsonrpc_trigger_complete_all(struct ovsdb_jsonrpc_session *s)
 +{
 +    struct ovsdb_jsonrpc_trigger *t, *next;
 +    HMAP_FOR_EACH_SAFE (t, next, struct ovsdb_jsonrpc_trigger, hmap_node,
 +                        &s->triggers) {
 +        ovsdb_jsonrpc_trigger_complete(t);
 +    }
 +}
 +
 +static void
 +ovsdb_jsonrpc_trigger_complete_done(struct ovsdb_jsonrpc_session *s)
 +{
 +    while (!list_is_empty(&s->completions)) {
 +        struct ovsdb_jsonrpc_trigger *t
 +            = CONTAINER_OF(s->completions.next,
 +                           struct ovsdb_jsonrpc_trigger, trigger.node);
 +        ovsdb_jsonrpc_trigger_complete(t);
 +    }
 +}
 +\f
 +/* JSON-RPC database table monitors. */
 +
 +enum ovsdb_jsonrpc_monitor_selection {
 +    OJMS_INITIAL = 1 << 0,      /* All rows when monitor is created. */
 +    OJMS_INSERT = 1 << 1,       /* New rows. */
 +    OJMS_DELETE = 1 << 2,       /* Deleted rows. */
 +    OJMS_MODIFY = 1 << 3        /* Modified rows. */
 +};
 +
 +struct ovsdb_jsonrpc_monitor_table {
 +    const struct ovsdb_table *table;
 +    enum ovsdb_jsonrpc_monitor_selection select;
 +    struct ovsdb_column_set columns;
 +};
 +
 +struct ovsdb_jsonrpc_monitor {
 +    struct ovsdb_replica replica;
 +    struct ovsdb_jsonrpc_session *session;
 +    struct hmap_node node;      /* In ovsdb_jsonrpc_session's "monitors". */
 +
 +    struct json *monitor_id;
 +    struct shash tables;     /* Holds "struct ovsdb_jsonrpc_monitor_table"s. */
 +};
 +
 +static const struct ovsdb_replica_class ovsdb_jsonrpc_replica_class;
 +
 +struct ovsdb_jsonrpc_monitor *ovsdb_jsonrpc_monitor_find(
 +    struct ovsdb_jsonrpc_session *, const struct json *monitor_id);
 +static void ovsdb_jsonrpc_monitor_destroy(struct ovsdb_replica *);
 +static struct json *ovsdb_jsonrpc_monitor_get_initial(
 +    const struct ovsdb_jsonrpc_monitor *);
 +
 +static bool
 +parse_bool(struct ovsdb_parser *parser, const char *name, bool default_value)
 +{
 +    const struct json *json;
 +
 +    json = ovsdb_parser_member(parser, name, OP_BOOLEAN | OP_OPTIONAL);
 +    return json ? json_boolean(json) : default_value;
 +}
 +
 +struct ovsdb_jsonrpc_monitor *
 +ovsdb_jsonrpc_monitor_find(struct ovsdb_jsonrpc_session *s,
 +                           const struct json *monitor_id)
 +{
 +    struct ovsdb_jsonrpc_monitor *m;
 +
 +    HMAP_FOR_EACH_WITH_HASH (m, struct ovsdb_jsonrpc_monitor, node,
 +                             json_hash(monitor_id, 0), &s->monitors) {
 +        if (json_equal(m->monitor_id, monitor_id)) {
 +            return m;
 +        }
 +    }
 +
 +    return NULL;
 +}
 +
 +static struct json *
 +ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s,
 +                             struct json *params)
 +{
 +    struct ovsdb_jsonrpc_monitor *m = NULL;
 +    struct json *monitor_id, *monitor_requests;
 +    struct ovsdb_error *error = NULL;
 +    struct shash_node *node;
 +    struct json *json;
 +
 +    if (json_array(params)->n != 3) {
 +        error = ovsdb_syntax_error(params, NULL, "invalid parameters");
 +        goto error;
 +    }
 +    monitor_id = params->u.array.elems[1];
 +    monitor_requests = params->u.array.elems[2];
 +    if (monitor_requests->type != JSON_OBJECT) {
 +        error = ovsdb_syntax_error(monitor_requests, NULL,
 +                                   "monitor-requests must be object");
 +        goto error;
 +    }
 +
 +    if (ovsdb_jsonrpc_monitor_find(s, monitor_id)) {
 +        error = ovsdb_syntax_error(monitor_id, NULL, "duplicate monitor ID");
 +        goto error;
 +    }
 +
 +    m = xzalloc(sizeof *m);
 +    ovsdb_replica_init(&m->replica, &ovsdb_jsonrpc_replica_class);
 +    ovsdb_add_replica(s->remote->server->db, &m->replica);
 +    m->session = s;
 +    hmap_insert(&s->monitors, &m->node, json_hash(monitor_id, 0));
 +    m->monitor_id = json_clone(monitor_id);
 +    shash_init(&m->tables);
 +
 +    SHASH_FOR_EACH (node, json_object(monitor_requests)) {
 +        const struct ovsdb_table *table;
 +        struct ovsdb_jsonrpc_monitor_table *mt;
 +        const struct json *columns_json, *select_json;
 +        struct ovsdb_parser parser;
 +
 +        table = ovsdb_get_table(s->remote->server->db, node->name);
 +        if (!table) {
 +            error = ovsdb_syntax_error(NULL, NULL,
 +                                       "no table named %s", node->name);
 +            goto error;
 +        }
 +
 +        mt = xzalloc(sizeof *mt);
 +        mt->table = table;
 +        mt->select = OJMS_INITIAL | OJMS_INSERT | OJMS_DELETE | OJMS_MODIFY;
 +        ovsdb_column_set_init(&mt->columns);
 +        shash_add(&m->tables, table->schema->name, mt);
 +
 +        ovsdb_parser_init(&parser, node->data, "table %s", node->name);
 +        columns_json = ovsdb_parser_member(&parser, "columns",
 +                                           OP_ARRAY | OP_OPTIONAL);
 +        select_json = ovsdb_parser_member(&parser, "select",
 +                                          OP_OBJECT | OP_OPTIONAL);
 +        error = ovsdb_parser_finish(&parser);
 +        if (error) {
 +            goto error;
 +        }
 +
 +        if (columns_json) {
 +            error = ovsdb_column_set_from_json(columns_json, table,
 +                                               &mt->columns);
 +            if (error) {
 +                goto error;
 +            }
 +        } else {
 +            struct shash_node *node;
 +
 +            SHASH_FOR_EACH (node, &table->schema->columns) {
 +                const struct ovsdb_column *column = node->data;
 +                if (column->index != OVSDB_COL_UUID) {
 +                    ovsdb_column_set_add(&mt->columns, column);
 +                }
 +            }
 +        }
 +
 +        if (select_json) {
 +            mt->select = 0;
 +            ovsdb_parser_init(&parser, select_json, "table %s select",
 +                              table->schema->name);
 +            if (parse_bool(&parser, "initial", true)) {
 +                mt->select |= OJMS_INITIAL;
 +            }
 +            if (parse_bool(&parser, "insert", true)) {
 +                mt->select |= OJMS_INSERT;
 +            }
 +            if (parse_bool(&parser, "delete", true)) {
 +                mt->select |= OJMS_DELETE;
 +            }
 +            if (parse_bool(&parser, "modify", true)) {
 +                mt->select |= OJMS_MODIFY;
 +            }
 +            error = ovsdb_parser_finish(&parser);
 +            if (error) {
 +                goto error;
 +            }
 +        }
 +    }
 +
 +    return ovsdb_jsonrpc_monitor_get_initial(m);
 +
 +error:
 +    if (m) {
 +        ovsdb_remove_replica(s->remote->server->db, &m->replica);
 +    }
 +
 +    json = ovsdb_error_to_json(error);
 +    ovsdb_error_destroy(error);
 +    return json;
 +}
 +
 +static struct jsonrpc_msg *
 +ovsdb_jsonrpc_monitor_cancel(struct ovsdb_jsonrpc_session *s,
 +                             struct json_array *params,
 +                             const struct json *request_id)
 +{
 +    if (params->n != 1) {
 +        return jsonrpc_create_error(json_string_create("invalid parameters"),
 +                                    request_id);
 +    } else {
 +        struct ovsdb_jsonrpc_monitor *m;
 +
 +        m = ovsdb_jsonrpc_monitor_find(s, params->elems[0]);
 +        if (!m) {
 +            return jsonrpc_create_error(json_string_create("unknown monitor"),
 +                                        request_id);
 +        } else {
 +            ovsdb_remove_replica(s->remote->server->db, &m->replica);
 +            return jsonrpc_create_reply(json_object_create(), request_id);
 +        }
 +    }
 +}
 +
 +static void
 +ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *s)
 +{
 +    struct ovsdb_jsonrpc_monitor *m, *next;
 +
 +    HMAP_FOR_EACH_SAFE (m, next,
 +                        struct ovsdb_jsonrpc_monitor, node, &s->monitors) {
 +        ovsdb_remove_replica(s->remote->server->db, &m->replica);
 +    }
 +}
 +
 +static struct ovsdb_jsonrpc_monitor *
 +ovsdb_jsonrpc_monitor_cast(struct ovsdb_replica *replica)
 +{
 +    assert(replica->class == &ovsdb_jsonrpc_replica_class);
 +    return CONTAINER_OF(replica, struct ovsdb_jsonrpc_monitor, replica);
 +}
 +
 +struct ovsdb_jsonrpc_monitor_aux {
 +    bool initial;               /* Sending initial contents of table? */
 +    const struct ovsdb_jsonrpc_monitor *monitor;
 +    struct json *json;          /* JSON for the whole transaction. */
 +
 +    /* Current table.  */
 +    struct ovsdb_jsonrpc_monitor_table *mt;
 +    struct json *table_json;    /* JSON for table's transaction. */
 +};
 +
 +static bool
 +ovsdb_jsonrpc_monitor_change_cb(const struct ovsdb_row *old,
 +                                const struct ovsdb_row *new,
 +                                void *aux_)
 +{
 +    struct ovsdb_jsonrpc_monitor_aux *aux = aux_;
 +    const struct ovsdb_jsonrpc_monitor *m = aux->monitor;
 +    struct ovsdb_table *table = new ? new->table : old->table;
 +    enum ovsdb_jsonrpc_monitor_selection type;
 +    struct json *old_json, *new_json;
 +    struct json *row_json;
 +    char uuid[UUID_LEN + 1];
 +    int n_changed;
 +    size_t i;
 +
 +    if (!aux->mt || table != aux->mt->table) {
 +        aux->mt = shash_find_data(&m->tables, table->schema->name);
 +        aux->table_json = NULL;
 +        if (!aux->mt) {
 +            /* We don't care about rows in this table at all.  Tell the caller
 +             * to skip it.  */
 +            return false;
 +        }
 +    }
 +
 +    type = (aux->initial ? OJMS_INITIAL
 +            : !old ? OJMS_INSERT
 +            : !new ? OJMS_DELETE
 +            : OJMS_MODIFY);
 +    if (!(aux->mt->select & type)) {
 +        /* We don't care about this type of change (but do want to be called
 +         * back for changes to other rows in the same table). */
 +        return true;
 +    }
 +
 +    old_json = new_json = NULL;
 +    n_changed = 0;
 +    for (i = 0; i < aux->mt->columns.n_columns; i++) {
 +        const struct ovsdb_column *column = aux->mt->columns.columns[i];
 +        unsigned int idx = column->index;
 +        bool changed = false;
 +
 +        if (type == OJMS_MODIFY) {
 +            changed = !ovsdb_datum_equals(&old->fields[idx],
 +                                          &new->fields[idx], &column->type);
 +            n_changed += changed;
 +        }
 +        if (changed || type == OJMS_DELETE) {
 +            if (!old_json) {
 +                old_json = json_object_create();
 +            }
 +            json_object_put(old_json, column->name,
 +                            ovsdb_datum_to_json(&old->fields[idx],
 +                                                &column->type));
 +        }
 +        if (type & (OJMS_INITIAL | OJMS_INSERT | OJMS_MODIFY)) {
 +            if (!new_json) {
 +                new_json = json_object_create();
 +            }
 +            json_object_put(new_json, column->name,
 +                            ovsdb_datum_to_json(&new->fields[idx],
 +                                                &column->type));
 +        }
 +    }
 +    if ((type == OJMS_MODIFY && !n_changed) || (!old_json && !new_json)) {
 +        /* No reportable changes. */
 +        json_destroy(old_json);
 +        json_destroy(new_json);
 +        return true;
 +    }
 +
 +    /* Create JSON object for transaction overall. */
 +    if (!aux->json) {
 +        aux->json = json_object_create();
 +    }
 +
 +    /* Create JSON object for transaction on this table. */
 +    if (!aux->table_json) {
 +        aux->table_json = json_object_create();
 +        json_object_put(aux->json, aux->mt->table->schema->name,
 +                        aux->table_json);
 +    }
 +
 +    /* Create JSON object for transaction on this row. */
 +    row_json = json_object_create();
 +    if (old_json) {
 +        json_object_put(row_json, "old", old_json);
 +    }
 +    if (new_json) {
 +        json_object_put(row_json, "new", new_json);
 +    }
 +
 +    /* Add JSON row to JSON table. */
 +    snprintf(uuid, sizeof uuid,
 +             UUID_FMT, UUID_ARGS(ovsdb_row_get_uuid(new ? new : old)));
 +    json_object_put(aux->table_json, uuid, row_json);
 +
 +    return true;
 +}
 +
 +static void
 +ovsdb_jsonrpc_monitor_init_aux(struct ovsdb_jsonrpc_monitor_aux *aux,
 +                               const struct ovsdb_jsonrpc_monitor *m,
 +                               bool initial)
 +{
 +    aux->initial = initial;
 +    aux->monitor = m;
 +    aux->json = NULL;
 +    aux->mt = NULL;
 +    aux->table_json = NULL;
 +}
 +
 +static struct ovsdb_error *
 +ovsdb_jsonrpc_monitor_commit(struct ovsdb_replica *replica,
++                             const struct ovsdb_txn *txn,
++                             bool durable OVS_UNUSED)
 +{
 +    struct ovsdb_jsonrpc_monitor *m = ovsdb_jsonrpc_monitor_cast(replica);
 +    struct ovsdb_jsonrpc_monitor_aux aux;
 +
 +    ovsdb_jsonrpc_monitor_init_aux(&aux, m, false);
 +    ovsdb_txn_for_each_change(txn, ovsdb_jsonrpc_monitor_change_cb, &aux);
 +    if (aux.json) {
 +        struct jsonrpc_msg *msg;
 +        struct json *params;
 +
 +        params = json_array_create_2(json_clone(aux.monitor->monitor_id),
 +                                     aux.json);
 +        msg = jsonrpc_create_notify("update", params);
 +        jsonrpc_session_send(aux.monitor->session->js, msg);
 +    }
 +
 +    return NULL;
 +}
 +
 +static struct json *
 +ovsdb_jsonrpc_monitor_get_initial(const struct ovsdb_jsonrpc_monitor *m)
 +{
 +    struct ovsdb_jsonrpc_monitor_aux aux;
 +    struct shash_node *node;
 +
 +    ovsdb_jsonrpc_monitor_init_aux(&aux, m, true);
 +    SHASH_FOR_EACH (node, &m->tables) {
 +        struct ovsdb_jsonrpc_monitor_table *mt = node->data;
 +
 +        if (mt->select & OJMS_INITIAL) {
 +            struct ovsdb_row *row;
 +
 +            HMAP_FOR_EACH (row, struct ovsdb_row, hmap_node,
 +                           &mt->table->rows) {
 +                ovsdb_jsonrpc_monitor_change_cb(NULL, row, &aux);
 +            }
 +        }
 +    }
 +    return aux.json ? aux.json : json_object_create();
 +}
 +
 +static void
 +ovsdb_jsonrpc_monitor_destroy(struct ovsdb_replica *replica)
 +{
 +    struct ovsdb_jsonrpc_monitor *m = ovsdb_jsonrpc_monitor_cast(replica);
 +    struct shash_node *node;
 +
 +    json_destroy(m->monitor_id);
 +    SHASH_FOR_EACH (node, &m->tables) {
 +        struct ovsdb_jsonrpc_monitor_table *mt = node->data;
 +        ovsdb_column_set_destroy(&mt->columns);
 +        free(mt);
 +    }
 +    shash_destroy(&m->tables);
 +    hmap_remove(&m->session->monitors, &m->node);
 +    free(m);
 +}
 +
 +static const struct ovsdb_replica_class ovsdb_jsonrpc_replica_class = {
 +    ovsdb_jsonrpc_monitor_commit,
 +    ovsdb_jsonrpc_monitor_destroy
 +};
diff --combined ovsdb/ovsdb-client.c
index df24e37,0000000..acc4185
mode 100644,000000..100644
--- /dev/null
@@@ -1,926 -1,0 +1,926 @@@
- do_list_dbs(int argc UNUSED, char *argv[])
 +/*
 + * Copyright (c) 2009, 2010 Nicira Networks.
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at:
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <config.h>
 +
 +#include <assert.h>
 +#include <errno.h>
 +#include <getopt.h>
 +#include <limits.h>
 +#include <signal.h>
 +#include <stdlib.h>
 +#include <string.h>
 +#include <unistd.h>
 +
 +#include "command-line.h"
 +#include "column.h"
 +#include "compiler.h"
 +#include "daemon.h"
 +#include "dynamic-string.h"
 +#include "json.h"
 +#include "jsonrpc.h"
 +#include "ovsdb.h"
 +#include "ovsdb-error.h"
 +#include "stream.h"
 +#include "stream-ssl.h"
 +#include "table.h"
 +#include "timeval.h"
 +#include "util.h"
 +
 +#include "vlog.h"
 +#define THIS_MODULE VLM_ovsdb_client
 +
 +/* --format: Output formatting. */
 +static enum {
 +    FMT_TABLE,                  /* Textual table. */
 +    FMT_HTML,                   /* HTML table. */
 +    FMT_CSV                     /* Comma-separated lines. */
 +} output_format;
 +
 +/* --wide: For --format=table, the maximum output width. */
 +static int output_width;
 +
 +/* --no-headings: Whether table output should include headings. */
 +static int output_headings = true;
 +
 +/* --pretty: Flags to pass to json_to_string(). */
 +static int json_flags = JSSF_SORT;
 +
 +static const struct command all_commands[];
 +
 +static void usage(void) NO_RETURN;
 +static void parse_options(int argc, char *argv[]);
 +
 +int
 +main(int argc, char *argv[])
 +{
 +    proctitle_init(argc, argv);
 +    set_program_name(argv[0]);
 +    time_init();
 +    vlog_init();
 +    parse_options(argc, argv);
 +    signal(SIGPIPE, SIG_IGN);
 +    run_command(argc - optind, argv + optind, all_commands);
 +    return 0;
 +}
 +
 +static void
 +parse_options(int argc, char *argv[])
 +{
 +    enum {
 +        OPT_BOOTSTRAP_CA_CERT = UCHAR_MAX + 1
 +    };
 +    static struct option long_options[] = {
 +        {"wide", no_argument, &output_width, INT_MAX},
 +        {"format", required_argument, 0, 'f'},
 +          {"no-headings", no_argument, &output_headings, 0},
 +        {"pretty", no_argument, &json_flags, JSSF_PRETTY | JSSF_SORT},
 +        {"verbose", optional_argument, 0, 'v'},
 +        {"help", no_argument, 0, 'h'},
 +        {"version", no_argument, 0, 'V'},
 +        DAEMON_LONG_OPTIONS,
 +#ifdef HAVE_OPENSSL
 +        {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT},
 +        STREAM_SSL_LONG_OPTIONS
 +#endif
 +        {0, 0, 0, 0},
 +    };
 +    char *short_options = long_options_to_short_options(long_options);
 +
 +    output_width = isatty(fileno(stdout)) ? 79 : INT_MAX;
 +    for (;;) {
 +        int c;
 +
 +        c = getopt_long(argc, argv, short_options, long_options, NULL);
 +        if (c == -1) {
 +            break;
 +        }
 +
 +        switch (c) {
 +        case 'f':
 +            if (!strcmp(optarg, "table")) {
 +                output_format = FMT_TABLE;
 +            } else if (!strcmp(optarg, "html")) {
 +                output_format = FMT_HTML;
 +            } else if (!strcmp(optarg, "csv")) {
 +                output_format = FMT_CSV;
 +            } else {
 +                ovs_fatal(0, "unknown output format \"%s\"", optarg);
 +            }
 +            break;
 +
 +        case 'w':
 +            output_width = INT_MAX;
 +            break;
 +
 +        case 'h':
 +            usage();
 +
 +        case 'V':
 +            OVS_PRINT_VERSION(0, 0);
 +            exit(EXIT_SUCCESS);
 +
 +        case 'v':
 +            vlog_set_verbosity(optarg);
 +            break;
 +
 +        DAEMON_OPTION_HANDLERS
 +
 +#ifdef HAVE_OPENSSL
 +        STREAM_SSL_OPTION_HANDLERS
 +
 +        case OPT_BOOTSTRAP_CA_CERT:
 +            stream_ssl_set_ca_cert_file(optarg, true);
 +            break;
 +#endif
 +
 +        case '?':
 +            exit(EXIT_FAILURE);
 +
 +        case 0:
 +            /* getopt_long() already set the value for us. */
 +            break;
 +
 +        default:
 +            abort();
 +        }
 +    }
 +    free(short_options);
 +}
 +
 +static void
 +usage(void)
 +{
 +    printf("%s: Open vSwitch database JSON-RPC client\n"
 +           "usage: %s [OPTIONS] COMMAND [ARG...]\n"
 +           "\nValid commands are:\n"
 +           "\n  list-dbs SERVER\n"
 +           "    list databases available on SERVER\n"
 +           "\n  get-schema SERVER DATABASE\n"
 +           "    retrieve schema for DATABASE from SERVER\n"
 +           "\n  list-tables SERVER DATABSE\n"
 +           "    list tables for DATABSAE on SERVER\n"
 +           "\n  list-columns SERVER DATABASE [TABLE]\n"
 +           "    list columns in TABLE (or all tables) in DATABASE on SERVER\n"
 +           "\n  transact SERVER TRANSACTION\n"
 +           "    run TRANSACTION (a JSON array of operations) on SERVER\n"
 +           "    and print the results as JSON on stdout\n"
 +           "\n  monitor SERVER DATABASE TABLE [COLUMN,...] [SELECT,...]\n"
 +           "    monitor contents of (COLUMNs in) TABLE in DATABASE on SERVER\n"
 +           "    Valid SELECTs are: initial, insert, delete, modify\n",
 +           program_name, program_name);
 +    stream_usage("SERVER", true, true, true);
 +    printf("\nOutput formatting options:\n"
 +           "  -f, --format=FORMAT         set output formatting to FORMAT\n"
 +           "                              (\"table\", \"html\", or \"csv\"\n"
 +           "  --wide                      don't limit TTY lines to 79 bytes\n"
 +           "  --no-headings               omit table heading row\n"
 +           "  --pretty                    pretty-print JSON in output");
 +    daemon_usage();
 +    vlog_usage();
 +    printf("\nOther options:\n"
 +           "  -h, --help                  display this help message\n"
 +           "  -V, --version               display version information\n");
 +    exit(EXIT_SUCCESS);
 +}
 +\f
 +static struct json *
 +parse_json(const char *s)
 +{
 +    struct json *json = json_from_string(s);
 +    if (json->type == JSON_STRING) {
 +        ovs_fatal(0, "\"%s\": %s", s, json->u.string);
 +    }
 +    return json;
 +}
 +
 +static struct jsonrpc *
 +open_jsonrpc(const char *server)
 +{
 +    struct stream *stream;
 +    int error;
 +
 +    error = stream_open_block(server, &stream);
 +    if (error == EAFNOSUPPORT) {
 +        struct pstream *pstream;
 +
 +        error = pstream_open(server, &pstream);
 +        if (error) {
 +            ovs_fatal(error, "failed to connect or listen to \"%s\"", server);
 +        }
 +
 +        VLOG_INFO("%s: waiting for connection...", server);
 +        error = pstream_accept_block(pstream, &stream);
 +        if (error) {
 +            ovs_fatal(error, "failed to accept connection on \"%s\"", server);
 +        }
 +
 +        pstream_close(pstream);
 +    } else if (error) {
 +        ovs_fatal(error, "failed to connect to \"%s\"", server);
 +    }
 +
 +    return jsonrpc_open(stream);
 +}
 +
 +static void
 +print_json(struct json *json)
 +{
 +    char *string = json_to_string(json, json_flags);
 +    fputs(string, stdout);
 +    free(string);
 +}
 +
 +static void
 +print_and_free_json(struct json *json)
 +{
 +    print_json(json);
 +    json_destroy(json);
 +}
 +
 +static void
 +check_ovsdb_error(struct ovsdb_error *error)
 +{
 +    if (error) {
 +        ovs_fatal(0, "%s", ovsdb_error_to_string(error));
 +    }
 +}
 +
 +static struct ovsdb_schema *
 +fetch_schema_from_rpc(struct jsonrpc *rpc, const char *database)
 +{
 +    struct jsonrpc_msg *request, *reply;
 +    struct ovsdb_schema *schema;
 +    int error;
 +
 +    request = jsonrpc_create_request("get_schema",
 +                                     json_array_create_1(
 +                                         json_string_create(database)),
 +                                     NULL);
 +    error = jsonrpc_transact_block(rpc, request, &reply);
 +    if (error) {
 +        ovs_fatal(error, "transaction failed");
 +    }
 +    check_ovsdb_error(ovsdb_schema_from_json(reply->result, &schema));
 +    jsonrpc_msg_destroy(reply);
 +
 +    return schema;
 +}
 +
 +static struct ovsdb_schema *
 +fetch_schema(const char *server, const char *database)
 +{
 +    struct ovsdb_schema *schema;
 +    struct jsonrpc *rpc;
 +
 +    rpc = open_jsonrpc(server);
 +    schema = fetch_schema_from_rpc(rpc, database);
 +    jsonrpc_close(rpc);
 +
 +    return schema;
 +}
 +\f
 +struct column {
 +    char *heading;
 +    int width;
 +};
 +
 +struct table {
 +    char **cells;
 +    struct column *columns;
 +    size_t n_columns, allocated_columns;
 +    size_t n_rows, allocated_rows;
 +    size_t current_column;
 +};
 +
 +static void
 +table_init(struct table *table)
 +{
 +    memset(table, 0, sizeof *table);
 +}
 +
 +static void
 +table_destroy(struct table *table)
 +{
 +    size_t i;
 +
 +    for (i = 0; i < table->n_columns; i++) {
 +        free(table->columns[i].heading);
 +    }
 +    free(table->columns);
 +
 +    for (i = 0; i < table->n_columns * table->n_rows; i++) {
 +        free(table->cells[i]);
 +    }
 +    free(table->cells);
 +}
 +
 +static void
 +table_add_column(struct table *table, const char *heading, ...)
 +    PRINTF_FORMAT(2, 3);
 +
 +static void
 +table_add_column(struct table *table, const char *heading, ...)
 +{
 +    struct column *column;
 +    va_list args;
 +
 +    assert(!table->n_rows);
 +    if (table->n_columns >= table->allocated_columns) {
 +        table->columns = x2nrealloc(table->columns, &table->allocated_columns,
 +                                    sizeof *table->columns);
 +    }
 +    column = &table->columns[table->n_columns++];
 +
 +    va_start(args, heading);
 +    column->heading = xvasprintf(heading, args);
 +    column->width = strlen(column->heading);
 +    va_end(args);
 +}
 +
 +static char **
 +table_cell__(const struct table *table, size_t row, size_t column)
 +{
 +    return &table->cells[column + row * table->n_columns];
 +}
 +
 +static void
 +table_add_row(struct table *table)
 +{
 +    size_t x, y;
 +
 +    if (table->n_rows >= table->allocated_rows) {
 +        table->cells = x2nrealloc(table->cells, &table->allocated_rows,
 +                                  table->n_columns * sizeof *table->cells);
 +    }
 +
 +    y = table->n_rows++;
 +    table->current_column = 0;
 +    for (x = 0; x < table->n_columns; x++) {
 +        *table_cell__(table, y, x) = NULL;
 +    }
 +}
 +
 +static void
 +table_add_cell_nocopy(struct table *table, char *s)
 +{
 +    size_t x, y;
 +    int length;
 +
 +    assert(table->n_rows > 0);
 +    assert(table->current_column < table->n_columns);
 +
 +    x = table->current_column++;
 +    y = table->n_rows - 1;
 +    *table_cell__(table, y, x) = s;
 +
 +    length = strlen(s);
 +    if (length > table->columns[x].width) {
 +        table->columns[x].width = length;
 +    }
 +}
 +
 +static void
 +table_add_cell(struct table *table, const char *format, ...)
 +{
 +    va_list args;
 +
 +    va_start(args, format);
 +    table_add_cell_nocopy(table, xvasprintf(format, args));
 +    va_end(args);
 +}
 +
 +static void
 +table_print_table_line__(struct ds *line, size_t max_width)
 +{
 +    ds_truncate(line, max_width);
 +    puts(ds_cstr(line));
 +    ds_clear(line);
 +}
 +
 +static void
 +table_print_table__(const struct table *table)
 +{
 +    struct ds line = DS_EMPTY_INITIALIZER;
 +    size_t x, y;
 +
 +    if (output_headings) {
 +        for (x = 0; x < table->n_columns; x++) {
 +            const struct column *column = &table->columns[x];
 +            if (x) {
 +                ds_put_char(&line, ' ');
 +            }
 +            ds_put_format(&line, "%-*s", column->width, column->heading);
 +        }
 +        table_print_table_line__(&line, output_width);
 +
 +        for (x = 0; x < table->n_columns; x++) {
 +            const struct column *column = &table->columns[x];
 +            int i;
 +
 +            if (x) {
 +                ds_put_char(&line, ' ');
 +            }
 +            for (i = 0; i < column->width; i++) {
 +                ds_put_char(&line, '-');
 +            }
 +        }
 +        table_print_table_line__(&line, output_width);
 +    }
 +
 +    for (y = 0; y < table->n_rows; y++) {
 +        for (x = 0; x < table->n_columns; x++) {
 +            const char *cell = *table_cell__(table, y, x);
 +            if (x) {
 +                ds_put_char(&line, ' ');
 +            }
 +            ds_put_format(&line, "%-*s", table->columns[x].width, cell);
 +        }
 +        table_print_table_line__(&line, output_width);
 +    }
 +
 +    ds_destroy(&line);
 +}
 +
 +static void
 +table_print_html_cell__(const char *element, const char *content)
 +{
 +    const char *p;
 +
 +    printf("    <%s>", element);
 +    for (p = content; *p != '\0'; p++) {
 +        switch (*p) {
 +        case '&':
 +            fputs("&amp;", stdout);
 +            break;
 +        case '<':
 +            fputs("&lt;", stdout);
 +            break;
 +        case '>':
 +            fputs("&gt;", stdout);
 +            break;
 +        default:
 +            putchar(*p);
 +            break;
 +        }
 +    }
 +    printf("</%s>\n", element);
 +}
 +
 +static void
 +table_print_html__(const struct table *table)
 +{
 +    size_t x, y;
 +
 +    fputs("<table>\n", stdout);
 +
 +    if (output_headings) {
 +        fputs("  <tr>\n", stdout);
 +        for (x = 0; x < table->n_columns; x++) {
 +            const struct column *column = &table->columns[x];
 +            table_print_html_cell__("th", column->heading);
 +        }
 +        fputs("  </tr>\n", stdout);
 +    }
 +
 +    for (y = 0; y < table->n_rows; y++) {
 +        fputs("  <tr>\n", stdout);
 +        for (x = 0; x < table->n_columns; x++) {
 +            table_print_html_cell__("td", *table_cell__(table, y, x));
 +        }
 +        fputs("  </tr>\n", stdout);
 +    }
 +
 +    fputs("</table>\n", stdout);
 +}
 +
 +static void
 +table_print_csv_cell__(const char *content)
 +{
 +    const char *p;
 +
 +    if (!strpbrk(content, "\n\",")) {
 +        fputs(content, stdout);
 +    } else {
 +        putchar('"');
 +        for (p = content; *p != '\0'; p++) {
 +            switch (*p) {
 +            case '"':
 +                fputs("\"\"", stdout);
 +                break;
 +            default:
 +                putchar(*p);
 +                break;
 +            }
 +        }
 +        putchar('"');
 +    }
 +}
 +
 +static void
 +table_print_csv__(const struct table *table)
 +{
 +    size_t x, y;
 +
 +    if (output_headings) {
 +        for (x = 0; x < table->n_columns; x++) {
 +            const struct column *column = &table->columns[x];
 +            if (x) {
 +                putchar(',');
 +            }
 +            table_print_csv_cell__(column->heading);
 +        }
 +        putchar('\n');
 +    }
 +
 +    for (y = 0; y < table->n_rows; y++) {
 +        for (x = 0; x < table->n_columns; x++) {
 +            if (x) {
 +                putchar(',');
 +            }
 +            table_print_csv_cell__(*table_cell__(table, y, x));
 +        }
 +        putchar('\n');
 +    }
 +}
 +
 +static void
 +table_print(const struct table *table)
 +{
 +    switch (output_format) {
 +    case FMT_TABLE:
 +        table_print_table__(table);
 +        break;
 +
 +    case FMT_HTML:
 +        table_print_html__(table);
 +        break;
 +
 +    case FMT_CSV:
 +        table_print_csv__(table);
 +        break;
 +    }
 +}
 +\f
 +static void
- do_get_schema(int argc UNUSED, char *argv[])
++do_list_dbs(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct jsonrpc_msg *request, *reply;
 +    struct jsonrpc *rpc;
 +    int error;
 +    size_t i;
 +
 +    rpc = open_jsonrpc(argv[1]);
 +    request = jsonrpc_create_request("list_dbs", json_array_create_empty(),
 +                                     NULL);
 +    error = jsonrpc_transact_block(rpc, request, &reply);
 +    if (error) {
 +        ovs_fatal(error, "transaction failed");
 +    }
 +
 +    if (reply->result->type != JSON_ARRAY) {
 +        ovs_fatal(0, "list_dbs response is not array");
 +    }
 +
 +    for (i = 0; i < reply->result->u.array.n; i++) {
 +        const struct json *name = reply->result->u.array.elems[i];
 +
 +        if (name->type != JSON_STRING) {
 +            ovs_fatal(0, "list_dbs response %zu is not string", i);
 +        }
 +        puts(name->u.string);
 +    }
 +    jsonrpc_msg_destroy(reply);
 +}
 +
 +static void
- do_list_tables(int argc UNUSED, char *argv[])
++do_get_schema(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct ovsdb_schema *schema = fetch_schema(argv[1], argv[2]);
 +    print_and_free_json(ovsdb_schema_to_json(schema));
 +    ovsdb_schema_destroy(schema);
 +}
 +
 +static void
- do_list_columns(int argc UNUSED, char *argv[])
++do_list_tables(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct ovsdb_schema *schema;
 +    struct shash_node *node;
 +    struct table t;
 +
 +    schema = fetch_schema(argv[1], argv[2]);
 +    table_init(&t);
 +    table_add_column(&t, "Table");
 +    table_add_column(&t, "Comment");
 +    SHASH_FOR_EACH (node, &schema->tables) {
 +        struct ovsdb_table_schema *ts = node->data;
 +
 +        table_add_row(&t);
 +        table_add_cell(&t, ts->name);
 +        if (ts->comment) {
 +            table_add_cell(&t, ts->comment);
 +        }
 +    }
 +    ovsdb_schema_destroy(schema);
 +    table_print(&t);
 +}
 +
 +static void
- do_transact(int argc UNUSED, char *argv[])
++do_list_columns(int argc OVS_UNUSED, char *argv[])
 +{
 +    const char *table_name = argv[3];
 +    struct ovsdb_schema *schema;
 +    struct shash_node *table_node;
 +    struct table t;
 +
 +    schema = fetch_schema(argv[1], argv[2]);
 +    table_init(&t);
 +    if (!table_name) {
 +        table_add_column(&t, "Table");
 +    }
 +    table_add_column(&t, "Column");
 +    table_add_column(&t, "Type");
 +    table_add_column(&t, "Comment");
 +    SHASH_FOR_EACH (table_node, &schema->tables) {
 +        struct ovsdb_table_schema *ts = table_node->data;
 +
 +        if (!table_name || !strcmp(table_name, ts->name)) {
 +            struct shash_node *column_node;
 +
 +            SHASH_FOR_EACH (column_node, &ts->columns) {
 +                const struct ovsdb_column *column = column_node->data;
 +                struct json *type = ovsdb_type_to_json(&column->type);
 +
 +                table_add_row(&t);
 +                if (!table_name) {
 +                    table_add_cell(&t, ts->name);
 +                }
 +                table_add_cell(&t, column->name);
 +                table_add_cell_nocopy(&t, json_to_string(type, JSSF_SORT));
 +                if (column->comment) {
 +                    table_add_cell(&t, column->comment);
 +                }
 +
 +                json_destroy(type);
 +            }
 +        }
 +    }
 +    ovsdb_schema_destroy(schema);
 +    table_print(&t);
 +}
 +
 +static void
- do_help(int argc UNUSED, char *argv[] UNUSED)
++do_transact(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct jsonrpc_msg *request, *reply;
 +    struct json *transaction;
 +    struct jsonrpc *rpc;
 +    int error;
 +
 +    transaction = parse_json(argv[2]);
 +
 +    rpc = open_jsonrpc(argv[1]);
 +    request = jsonrpc_create_request("transact", transaction, NULL);
 +    error = jsonrpc_transact_block(rpc, request, &reply);
 +    if (error) {
 +        ovs_fatal(error, "transaction failed");
 +    }
 +    if (reply->error) {
 +        ovs_fatal(error, "transaction returned error: %s",
 +                  json_to_string(reply->error, json_flags));
 +    }
 +    print_json(reply->result);
 +    putchar('\n');
 +    jsonrpc_msg_destroy(reply);
 +    jsonrpc_close(rpc);
 +}
 +
 +static void
 +monitor_print_row(struct json *row, const char *type, const char *uuid,
 +                  const struct ovsdb_column_set *columns, struct table *t)
 +{
 +    size_t i;
 +
 +    if (!row) {
 +        ovs_error(0, "missing %s row", type);
 +        return;
 +    } else if (row->type != JSON_OBJECT) {
 +        ovs_error(0, "<row> is not object");
 +        return;
 +    }
 +
 +    table_add_row(t);
 +    table_add_cell(t, uuid);
 +    table_add_cell(t, type);
 +    for (i = 0; i < columns->n_columns; i++) {
 +        const struct ovsdb_column *column = columns->columns[i];
 +        struct json *value = shash_find_data(json_object(row), column->name);
 +        if (value) {
 +            table_add_cell_nocopy(t, json_to_string(value, JSSF_SORT));
 +        } else {
 +            table_add_cell(t, "");
 +        }
 +    }
 +}
 +
 +static void
 +monitor_print(struct json *table_updates,
 +              const struct ovsdb_table_schema *table,
 +              const struct ovsdb_column_set *columns, bool initial)
 +{
 +    struct json *table_update;
 +    struct shash_node *node;
 +    struct table t;
 +    size_t i;
 +
 +    table_init(&t);
 +
 +    if (table_updates->type != JSON_OBJECT) {
 +        ovs_error(0, "<table-updates> is not object");
 +        return;
 +    }
 +    table_update = shash_find_data(json_object(table_updates), table->name);
 +    if (!table_update) {
 +        return;
 +    }
 +    if (table_update->type != JSON_OBJECT) {
 +        ovs_error(0, "<table-update> is not object");
 +        return;
 +    }
 +
 +    table_add_column(&t, "row");
 +    table_add_column(&t, "action");
 +    for (i = 0; i < columns->n_columns; i++) {
 +        table_add_column(&t, "%s", columns->columns[i]->name);
 +    }
 +    SHASH_FOR_EACH (node, json_object(table_update)) {
 +        struct json *row_update = node->data;
 +        struct json *old, *new;
 +
 +        if (row_update->type != JSON_OBJECT) {
 +            ovs_error(0, "<row-update> is not object");
 +            continue;
 +        }
 +        old = shash_find_data(json_object(row_update), "old");
 +        new = shash_find_data(json_object(row_update), "new");
 +        if (initial) {
 +            monitor_print_row(new, "initial", node->name, columns, &t);
 +        } else if (!old) {
 +            monitor_print_row(new, "insert", node->name, columns, &t);
 +        } else if (!new) {
 +            monitor_print_row(old, "delete", node->name, columns, &t);
 +        } else {
 +            monitor_print_row(old, "old", node->name, columns, &t);
 +            monitor_print_row(new, "new", "", columns, &t);
 +        }
 +    }
 +    table_print(&t);
 +    table_destroy(&t);
 +}
 +
 +static void
 +do_monitor(int argc, char *argv[])
 +{
 +    const char *server = argv[1];
 +    const char *database = argv[2];
 +    const char *table_name = argv[3];
 +    struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER;
 +    struct ovsdb_table_schema *table;
 +    struct ovsdb_schema *schema;
 +    struct jsonrpc_msg *request;
 +    struct jsonrpc *rpc;
 +    struct json *select, *monitor, *monitor_request, *monitor_requests,
 +        *request_id;
 +
 +    rpc = open_jsonrpc(server);
 +
 +    schema = fetch_schema_from_rpc(rpc, database);
 +    table = shash_find_data(&schema->tables, table_name);
 +    if (!table) {
 +        ovs_fatal(0, "%s: %s does not have a table named \"%s\"",
 +                  server, database, table_name);
 +    }
 +
 +    if (argc >= 5 && *argv[4] != '\0') {
 +        char *save_ptr = NULL;
 +        char *token;
 +
 +        for (token = strtok_r(argv[4], ",", &save_ptr); token != NULL;
 +             token = strtok_r(NULL, ",", &save_ptr)) {
 +            const struct ovsdb_column *column;
 +            column = ovsdb_table_schema_get_column(table, token);
 +            if (!column) {
 +                ovs_fatal(0, "%s: table \"%s\" in %s does not have a "
 +                          "column named \"%s\"",
 +                          server, table_name, database, token);
 +            }
 +            ovsdb_column_set_add(&columns, column);
 +        }
 +    } else {
 +        struct shash_node *node;
 +
 +        SHASH_FOR_EACH (node, &table->columns) {
 +            const struct ovsdb_column *column = node->data;
 +            if (column->index != OVSDB_COL_UUID) {
 +                ovsdb_column_set_add(&columns, column);
 +            }
 +        }
 +    }
 +
 +    if (argc >= 6 && *argv[5] != '\0') {
 +        char *save_ptr = NULL;
 +        char *token;
 +
 +        select = json_object_create();
 +        for (token = strtok_r(argv[5], ",", &save_ptr); token != NULL;
 +             token = strtok_r(NULL, ",", &save_ptr)) {
 +            json_object_put(select, token, json_boolean_create(true));
 +        }
 +    } else {
 +        select = NULL;
 +    }
 +
 +    monitor_request = json_object_create();
 +    json_object_put(monitor_request,
 +                    "columns", ovsdb_column_set_to_json(&columns));
 +    if (select) {
 +        json_object_put(monitor_request, "select", select);
 +    }
 +
 +    monitor_requests = json_object_create();
 +    json_object_put(monitor_requests, table_name, monitor_request);
 +
 +    monitor = json_array_create_3(json_string_create(database),
 +                                  json_null_create(), monitor_requests);
 +    request = jsonrpc_create_request("monitor", monitor, NULL);
 +    request_id = json_clone(request->id);
 +    jsonrpc_send(rpc, request);
 +    for (;;) {
 +        struct jsonrpc_msg *msg;
 +        int error;
 +
 +        error = jsonrpc_recv_block(rpc, &msg);
 +        if (error) {
 +            ovsdb_schema_destroy(schema);
 +            ovs_fatal(error, "%s: receive failed", server);
 +        }
 +
 +        if (msg->type == JSONRPC_REQUEST && !strcmp(msg->method, "echo")) {
 +            jsonrpc_send(rpc, jsonrpc_create_reply(json_clone(msg->params),
 +                                                   msg->id));
 +        } else if (msg->type == JSONRPC_REPLY
 +                   && json_equal(msg->id, request_id)) {
 +            monitor_print(msg->result, table, &columns, true);
 +            fflush(stdout);
 +            if (get_detach()) {
 +                /* daemonize() closes the standard file descriptors.  We output
 +                 * to stdout, so we need to save and restore STDOUT_FILENO. */
 +                int fd = dup(STDOUT_FILENO);
 +                daemonize();
 +                dup2(fd, STDOUT_FILENO);
 +                close(fd);
 +            }
 +        } else if (msg->type == JSONRPC_NOTIFY
 +                   && !strcmp(msg->method, "update")) {
 +            struct json *params = msg->params;
 +            if (params->type == JSON_ARRAY
 +                && params->u.array.n == 2
 +                && params->u.array.elems[0]->type == JSON_NULL) {
 +                monitor_print(params->u.array.elems[1],
 +                              table, &columns, false);
 +                fflush(stdout);
 +            }
 +        }
 +        jsonrpc_msg_destroy(msg);
 +    }
 +}
 +
 +static void
++do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 +{
 +    usage();
 +}
 +
 +static const struct command all_commands[] = {
 +    { "list-dbs", 1, 1, do_list_dbs },
 +    { "get-schema", 2, 2, do_get_schema },
 +    { "list-tables", 2, 2, do_list_tables },
 +    { "list-columns", 2, 3, do_list_columns },
 +    { "transact", 2, 2, do_transact },
 +    { "monitor", 3, 5, do_monitor },
 +    { "help", 0, INT_MAX, do_help },
 +    { NULL, 0, 0, NULL },
 +};
diff --combined ovsdb/ovsdb-idlc.in
index a0f4a56,0000000..9a90679
mode 100755,000000..100755
--- /dev/null
@@@ -1,866 -1,0 +1,866 @@@
- static void UNUSED
 +#! @PYTHON@
 +
 +import getopt
 +import os
 +import re
 +import sys
 +
 +sys.path.insert(0, "@abs_top_srcdir@/ovsdb")
 +import simplejson as json
 +
 +argv0 = sys.argv[0]
 +
 +class Error(Exception):
 +    def __init__(self, msg):
 +        Exception.__init__(self)
 +        self.msg = msg
 +
 +def getMember(json, name, validTypes, description, default=None):
 +    if name in json:
 +        member = json[name]
 +        if type(member) not in validTypes:
 +            raise Error("%s: type mismatch for '%s' member"
 +                        % (description, name))
 +        return member
 +    return default
 +
 +def mustGetMember(json, name, expectedType, description):
 +    member = getMember(json, name, expectedType, description)
 +    if member == None:
 +        raise Error("%s: missing '%s' member" % (description, name))
 +    return member
 +
 +class DbSchema:
 +    def __init__(self, name, comment, tables, idlPrefix, idlHeader):
 +        self.name = name
 +        self.comment = comment
 +        self.tables = tables
 +        self.idlPrefix = idlPrefix
 +        self.idlHeader = idlHeader
 +
 +    @staticmethod
 +    def fromJson(json):
 +        name = mustGetMember(json, 'name', [unicode], 'database')
 +        comment = getMember(json, 'comment', [unicode], 'database')
 +        tablesJson = mustGetMember(json, 'tables', [dict], 'database')
 +        tables = {}
 +        for tableName, tableJson in tablesJson.iteritems():
 +            tables[tableName] = TableSchema.fromJson(tableJson,
 +                                                     "%s table" % tableName)
 +        idlPrefix = mustGetMember(json, 'idlPrefix', [unicode], 'database')
 +        idlHeader = mustGetMember(json, 'idlHeader', [unicode], 'database')
 +        return DbSchema(name, comment, tables, idlPrefix, idlHeader)
 +
 +class TableSchema:
 +    def __init__(self, comment, columns):
 +        self.comment = comment
 +        self.columns = columns
 +
 +    @staticmethod
 +    def fromJson(json, description):
 +        comment = getMember(json, 'comment', [unicode], description)
 +        columnsJson = mustGetMember(json, 'columns', [dict], description)
 +        columns = {}
 +        for name, json in columnsJson.iteritems():
 +            columns[name] = ColumnSchema.fromJson(
 +                json, "column %s in %s" % (name, description))
 +        return TableSchema(comment, columns)
 +
 +class ColumnSchema:
 +    def __init__(self, comment, type, persistent):
 +        self.comment = comment
 +        self.type = type
 +        self.persistent = persistent
 +
 +    @staticmethod
 +    def fromJson(json, description):
 +        comment = getMember(json, 'comment', [unicode], description)
 +        type = Type.fromJson(mustGetMember(json, 'type', [dict, unicode],
 +                                           description),
 +                             'type of %s' % description)
 +        ephemeral = getMember(json, 'ephemeral', [bool], description)
 +        persistent = ephemeral != True
 +        return ColumnSchema(comment, type, persistent)
 +
 +def escapeCString(src):
 +    dst = ""
 +    for c in src:
 +        if c in "\\\"":
 +            dst += "\\" + c
 +        elif ord(c) < 32:
 +            if c == '\n':
 +                dst += '\\n'
 +            elif c == '\r':
 +                dst += '\\r'
 +            elif c == '\a':
 +                dst += '\\a'
 +            elif c == '\b':
 +                dst += '\\b'
 +            elif c == '\f':
 +                dst += '\\f'
 +            elif c == '\t':
 +                dst += '\\t'
 +            elif c == '\v':
 +                dst += '\\v'
 +            else:
 +                dst += '\\%03o' % ord(c)
 +        else:
 +            dst += c
 +    return dst
 +
 +class BaseType:
 +    def __init__(self, type, refTable=None, minInteger=None, maxInteger=None,
 +                 minReal=None, maxReal=None, reMatch=None, reComment=None,
 +                 minLength=None, maxLength=None):
 +        self.type = type
 +        self.refTable = refTable
 +        self.minInteger = minInteger
 +        self.maxInteger = maxInteger
 +        self.minReal = minReal
 +        self.maxReal = maxReal
 +        self.reMatch = reMatch
 +        self.reComment = reComment
 +        self.minLength = minLength
 +        self.maxLength = maxLength
 +
 +    @staticmethod
 +    def fromJson(json, description):
 +        if type(json) == unicode:
 +            return BaseType(json)
 +        else:
 +            atomicType = mustGetMember(json, 'type', [unicode], description)
 +            refTable = getMember(json, 'refTable', [unicode], description)
 +            minInteger = getMember(json, 'minInteger', [int, long], description)
 +            maxInteger = getMember(json, 'maxInteger', [int, long], description)
 +            minReal = getMember(json, 'minReal', [int, long, float], description)
 +            maxReal = getMember(json, 'maxReal', [int, long, float], description)
 +            reMatch = getMember(json, 'reMatch', [unicode], description)
 +            reComment = getMember(json, 'reComment', [unicode], description)
 +            minLength = getMember(json, 'minLength', [int], description)
 +            maxLength = getMember(json, 'minLength', [int], description)
 +            return BaseType(atomicType, refTable, minInteger, maxInteger, minReal, maxReal, reMatch, reComment, minLength, maxLength)
 +
 +    def toEnglish(self):
 +        if self.type == 'uuid' and self.refTable:
 +            return self.refTable
 +        else:
 +            return self.type
 +
 +    def toCType(self, prefix):
 +        if self.refTable:
 +            return "struct %s%s *" % (prefix, self.refTable.lower())
 +        else:
 +            return {'integer': 'int64_t ',
 +                    'real': 'double ',
 +                    'uuid': 'struct uuid ',
 +                    'boolean': 'bool ',
 +                    'string': 'char *'}[self.type]
 +
 +    def copyCValue(self, dst, src):
 +        args = {'dst': dst, 'src': src}
 +        if self.refTable:
 +            return ("%(dst)s = %(src)s->header_.uuid;") % args
 +        elif self.type == 'string':
 +            return "%(dst)s = xstrdup(%(src)s);" % args
 +        else:
 +            return "%(dst)s = %(src)s;" % args
 +
 +    def initCDefault(self, var, isOptional):
 +        if self.refTable:
 +            return "%s = NULL;" % var
 +        elif self.type == 'string' and not isOptional:
 +            return "%s = \"\";" % var
 +        else:
 +            return {'integer': '%s = 0;',
 +                    'real': '%s = 0.0;',
 +                    'uuid': 'uuid_zero(&%s);',
 +                    'boolean': '%s = false;',
 +                    'string': '%s = NULL;'}[self.type] % var
 +
 +    def cInitBaseType(self, indent, var):
 +        stmts = []
 +        stmts.append('ovsdb_base_type_init(&%s, OVSDB_TYPE_%s);' % (
 +                var, self.type.upper()),)
 +        if self.type == 'integer':
 +            if self.minInteger != None:
 +                stmts.append('%s.u.integer.min = %d;' % (var, self.minInteger))
 +            if self.maxInteger != None:
 +                stmts.append('%s.u.integer.max = %d;' % (var, self.maxInteger))
 +        elif self.type == 'real':
 +            if self.minReal != None:
 +                stmts.append('%s.u.real.min = %d;' % (var, self.minReal))
 +            if self.maxReal != None:
 +                stmts.append('%s.u.real.max = %d;' % (var, self.maxReal))
 +        elif self.type == 'string':
 +            if self.reMatch != None:
 +                if self.reComment != None:
 +                    reComment = '"%s"' % escapeCString(self.reComment)
 +                else:
 +                    reComment = NULL
 +                stmts.append('do_set_regex(&%s, "%s", %s);' % (
 +                        var, escapeCString(self.reMatch), reComment))
 +            if self.minLength != None:
 +                stmts.append('%s.u.string.minLen = %d;' % (var, self.minLength))            
 +            if self.maxLength != None:
 +                stmts.append('%s.u.string.maxLen = %d;' % (var, self.maxLength))
 +        elif self.type == 'uuid':
 +            if self.refTable != None:
 +                stmts.append('%s.u.uuid.refTableName = "%s";' % (var, escapeCString(self.refTable)))
 +        return '\n'.join([indent + stmt for stmt in stmts])
 +
 +class Type:
 +    def __init__(self, key, value=None, min=1, max=1):
 +        self.key = key
 +        self.value = value
 +        self.min = min
 +        self.max = max
 +    
 +    @staticmethod
 +    def fromJson(json, description):
 +        if type(json) == unicode:
 +            return Type(BaseType(json))
 +        else:
 +            keyJson = mustGetMember(json, 'key', [dict, unicode], description)
 +            key = BaseType.fromJson(keyJson, 'key in %s' % description)
 +
 +            valueJson = getMember(json, 'value', [dict, unicode], description)
 +            if valueJson:
 +                value = BaseType.fromJson(valueJson,
 +                                          'value in %s' % description)
 +            else:
 +                value = None
 +
 +            min = getMember(json, 'min', [int], description, 1)
 +            max = getMember(json, 'max', [int, unicode], description, 1)
 +            return Type(key, value, min, max)
 +
 +    def isScalar(self):
 +        return self.min == 1 and self.max == 1 and not self.value
 +
 +    def isOptional(self):
 +        return self.min == 0 and self.max == 1
 +
 +    def isOptionalPointer(self):
 +        return (self.min == 0 and self.max == 1 and not self.value
 +                and (self.key.type == 'string' or self.key.refTable))
 +
 +    def toEnglish(self):
 +        keyName = self.key.toEnglish()
 +        if self.value:
 +            valueName = self.value.toEnglish()
 +
 +        if self.isScalar():
 +            return keyName
 +        elif self.isOptional():
 +            if self.value:
 +                return "optional %s-%s pair" % (keyName, valueName)
 +            else:
 +                return "optional %s" % keyName
 +        else:
 +            if self.max == "unlimited":
 +                if self.min:
 +                    quantity = "%d or more " % self.min
 +                else:
 +                    quantity = ""
 +            elif self.min:
 +                quantity = "%d to %d " % (self.min, self.max)
 +            else:
 +                quantity = "up to %d " % self.max
 +
 +            if self.value:
 +                return "map of %s%s-%s pairs" % (quantity, keyName, valueName)
 +            else:
 +                return "set of %s%s" % (quantity, keyName)
 +                
 +    def cDeclComment(self):
 +        if self.min == 1 and self.max == 1 and self.key.type == "string":
 +            return "\t/* Always nonnull. */"
 +        else:
 +            return ""
 +
 +    def cInitType(self, indent, var):
 +        initKey = self.key.cInitBaseType(indent, "%s.key" % var)
 +        if self.value:
 +            initValue = self.value.cInitBaseType(indent, "%s.value" % var)
 +        else:
 +            initValue = ('%sovsdb_base_type_init(&%s.value, '
 +                         'OVSDB_TYPE_VOID);' % (indent, var))
 +        initMin = "%s%s.n_min = %s;" % (indent, var, self.min)
 +        if self.max == "unlimited":
 +            max = "UINT_MAX"
 +        else:
 +            max = self.max
 +        initMax = "%s%s.n_max = %s;" % (indent, var, max)
 +        return "\n".join((initKey, initValue, initMin, initMax))
 +
 +def parseSchema(filename):
 +    return DbSchema.fromJson(json.load(open(filename, "r")))
 +
 +def annotateSchema(schemaFile, annotationFile):
 +    schemaJson = json.load(open(schemaFile, "r"))
 +    execfile(annotationFile, globals(), {"s": schemaJson})
 +    json.dump(schemaJson, sys.stdout)
 +
 +def constify(cType, const):
 +    if (const
 +        and cType.endswith('*') and not cType.endswith('**')
 +        and (cType.startswith('struct uuid') or cType.startswith('char'))):
 +        return 'const %s' % cType
 +    else:
 +        return cType
 +
 +def cMembers(prefix, columnName, column, const):
 +    type = column.type
 +    if type.min == 1 and type.max == 1:
 +        singleton = True
 +        pointer = ''
 +    else:
 +        singleton = False
 +        if type.isOptionalPointer():
 +            pointer = ''
 +        else:
 +            pointer = '*'
 +
 +    if type.value:
 +        key = {'name': "key_%s" % columnName,
 +               'type': constify(type.key.toCType(prefix) + pointer, const),
 +               'comment': ''}
 +        value = {'name': "value_%s" % columnName,
 +                 'type': constify(type.value.toCType(prefix) + pointer, const),
 +                 'comment': ''}
 +        members = [key, value]
 +    else:
 +        m = {'name': columnName,
 +             'type': constify(type.key.toCType(prefix) + pointer, const),
 +             'comment': type.cDeclComment()}
 +        members = [m]
 +
 +    if not singleton and not type.isOptionalPointer():
 +        members.append({'name': 'n_%s' % columnName,
 +                        'type': 'size_t ',
 +                        'comment': ''})
 +    return members
 +
 +def printCIDLHeader(schemaFile):
 +    schema = parseSchema(schemaFile)
 +    prefix = schema.idlPrefix
 +    print '''\
 +/* Generated automatically -- do not modify!    -*- buffer-read-only: t -*- */
 +
 +#ifndef %(prefix)sIDL_HEADER
 +#define %(prefix)sIDL_HEADER 1
 +
 +#include <stdbool.h>
 +#include <stddef.h>
 +#include <stdint.h>
 +#include "ovsdb-idl-provider.h"
 +#include "uuid.h"''' % {'prefix': prefix.upper()}
 +
 +    for tableName, table in sorted(schema.tables.iteritems()):
 +        structName = "%s%s" % (prefix, tableName.lower())
 +
 +        print "\f"
 +        print "/* %s table. */" % tableName
 +        print "struct %s {" % structName
 +        print "\tstruct ovsdb_idl_row header_;"
 +        for columnName, column in sorted(table.columns.iteritems()):
 +            print "\n\t/* %s column. */" % columnName
 +            for member in cMembers(prefix, columnName, column, False):
 +                print "\t%(type)s%(name)s;%(comment)s" % member
 +        print "};"
 +
 +        # Column indexes.
 +        printEnum(["%s_COL_%s" % (structName.upper(), columnName.upper())
 +                   for columnName in sorted(table.columns)]
 +                  + ["%s_N_COLUMNS" % structName.upper()])
 +
 +        print
 +        for columnName in table.columns:
 +            print "#define %(s)s_col_%(c)s (%(s)s_columns[%(S)s_COL_%(C)s])" % {
 +                's': structName,
 +                'S': structName.upper(),
 +                'c': columnName,
 +                'C': columnName.upper()}
 +
 +        print "\nextern struct ovsdb_idl_column %s_columns[%s_N_COLUMNS];" % (structName, structName.upper())
 +
 +        print '''
 +const struct %(s)s *%(s)s_first(const struct ovsdb_idl *);
 +const struct %(s)s *%(s)s_next(const struct %(s)s *);
 +#define %(S)s_FOR_EACH(ROW, IDL) for ((ROW) = %(s)s_first(IDL); (ROW); (ROW) = %(s)s_next(ROW))
 +
 +void %(s)s_delete(const struct %(s)s *);
 +struct %(s)s *%(s)s_insert(struct ovsdb_idl_txn *);
 +''' % {'s': structName, 'S': structName.upper()}
 +
 +        for columnName, column in sorted(table.columns.iteritems()):
 +            print 'void %(s)s_verify_%(c)s(const struct %(s)s *);' % {'s': structName, 'c': columnName}
 +
 +        print
 +        for columnName, column in sorted(table.columns.iteritems()):
 +
 +            print 'void %(s)s_set_%(c)s(const struct %(s)s *,' % {'s': structName, 'c': columnName},
 +            args = ['%(type)s%(name)s' % member for member
 +                    in cMembers(prefix, columnName, column, True)]
 +            print '%s);' % ', '.join(args)
 +
 +    # Table indexes.
 +    printEnum(["%sTABLE_%s" % (prefix.upper(), tableName.upper()) for tableName in sorted(schema.tables)] + ["%sN_TABLES" % prefix.upper()])
 +    print
 +    for tableName in schema.tables:
 +        print "#define %(p)stable_%(t)s (%(p)stable_classes[%(P)sTABLE_%(T)s])" % {
 +            'p': prefix,
 +            'P': prefix.upper(),
 +            't': tableName.lower(),
 +            'T': tableName.upper()}
 +    print "\nextern struct ovsdb_idl_table_class %stable_classes[%sN_TABLES];" % (prefix, prefix.upper())
 +
 +    print "\nextern struct ovsdb_idl_class %sidl_class;" % prefix
 +    print "\nvoid %sinit(void);" % prefix
 +    print "\n#endif /* %(prefix)sIDL_HEADER */" % {'prefix': prefix.upper()}
 +
 +def printEnum(members):
 +    if len(members) == 0:
 +        return
 +
 +    print "\nenum {";
 +    for member in members[:-1]:
 +        print "    %s," % member
 +    print "    %s" % members[-1]
 +    print "};"
 +
 +def printCIDLSource(schemaFile):
 +    schema = parseSchema(schemaFile)
 +    prefix = schema.idlPrefix
 +    print '''\
 +/* Generated automatically -- do not modify!    -*- buffer-read-only: t -*- */
 +
 +#include <config.h>
 +#include %s
 +#include <assert.h>
 +#include <limits.h>
 +#include "ovsdb-data.h"
 +#include "ovsdb-error.h"
 +
 +static bool inited;
 +
- %(s)s_unparse_%(c)s(struct ovsdb_idl_row *row UNUSED)
++static void OVS_UNUSED
 +do_set_regex(struct ovsdb_base_type *base, const char *reMatch,
 +             const char *reComment)
 +{
 +    struct ovsdb_error *error;
 +
 +    error = ovsdb_base_type_set_regex(base, reMatch, reComment);
 +    if (error) {
 +        char *s = ovsdb_error_to_string(error);
 +        ovs_error(0, "%%s", s);
 +        free(s);
 +        ovsdb_error_destroy(error);
 +    }
 +}''' % schema.idlHeader
 +
 +    # Cast functions.
 +    for tableName, table in sorted(schema.tables.iteritems()):
 +        structName = "%s%s" % (prefix, tableName.lower())
 +        print '''
 +static struct %(s)s *
 +%(s)s_cast(const struct ovsdb_idl_row *row)
 +{
 +    return row ? CONTAINER_OF(row, struct %(s)s, header_) : NULL;
 +}\
 +''' % {'s': structName}
 +
 +
 +    for tableName, table in sorted(schema.tables.iteritems()):
 +        structName = "%s%s" % (prefix, tableName.lower())
 +        print "\f"
 +        if table.comment != None:
 +            print "/* %s table (%s). */" % (tableName, table.comment)
 +        else:
 +            print "/* %s table. */" % (tableName)
 +
 +        # Parse functions.
 +        for columnName, column in sorted(table.columns.iteritems()):
 +            print '''
 +static void
 +%(s)s_parse_%(c)s(struct ovsdb_idl_row *row_, const struct ovsdb_datum *datum)
 +{
 +    struct %(s)s *row = %(s)s_cast(row_);''' % {'s': structName,
 +                                                'c': columnName}
 +
 +            type = column.type
 +            if type.value:
 +                keyVar = "row->key_%s" % columnName
 +                valueVar = "row->value_%s" % columnName
 +            else:
 +                keyVar = "row->%s" % columnName
 +                valueVar = None
 +
 +            if (type.min == 1 and type.max == 1) or type.isOptionalPointer():
 +                print
 +                print "    assert(inited);"
 +                print "    if (datum->n >= 1) {"
 +                if not type.key.refTable:
 +                    print "        %s = datum->keys[0].%s;" % (keyVar, type.key.type)
 +                else:
 +                    print "        %s = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[0].uuid));" % (keyVar, prefix, type.key.refTable.lower(), prefix, prefix.upper(), type.key.refTable.upper())
 +
 +                if valueVar:
 +                    if type.value.refTable:
 +                        print "        %s = datum->values[0].%s;" % (valueVar, type.value.type)
 +                    else:
 +                        print "        %s = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[0].uuid));" % (valueVar, prefix, type.value.refTable.lower(), prefix, prefix.upper(), type.value.refTable.upper())
 +                print "    } else {"
 +                print "        %s" % type.key.initCDefault(keyVar, type.min == 0)
 +                if valueVar:
 +                    print "        %s" % type.value.initCDefault(valueVar, type.min == 0)
 +                print "    }"
 +            else:
 +                if type.max != 'unlimited':
 +                    print "    size_t n = MIN(%d, datum->n);" % type.max
 +                    nMax = "n"
 +                else:
 +                    nMax = "datum->n"
 +                print "    size_t i;"
 +                print
 +                print "    assert(inited);"
 +                print "    %s = NULL;" % keyVar
 +                if valueVar:
 +                    print "    %s = NULL;" % valueVar
 +                print "    row->n_%s = 0;" % columnName
 +                print "    for (i = 0; i < %s; i++) {" % nMax
 +                refs = []
 +                if type.key.refTable:
 +                    print "        struct %s%s *keyRow = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[i].uuid));" % (prefix, type.key.refTable.lower(), prefix, type.key.refTable.lower(), prefix, prefix.upper(), type.key.refTable.upper())
 +                    keySrc = "keyRow"
 +                    refs.append('keyRow')
 +                else:
 +                    keySrc = "datum->keys[i].%s" % type.key.type
 +                if type.value and type.value.refTable:
 +                    print "        struct %s%s *valueRow = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[i].uuid));" % (prefix, type.value.refTable.lower(), prefix, type.value.refTable.lower(), prefix, prefix.upper(), type.value.refTable.upper())
 +                    valueSrc = "valueRow"
 +                    refs.append('valueRow')
 +                elif valueVar:
 +                    valueSrc = "datum->values[i].%s" % type.value.type
 +                if refs:
 +                    print "        if (%s) {" % ' && '.join(refs)
 +                    indent = "            "
 +                else:
 +                    indent = "        "
 +                print "%sif (!row->n_%s) {" % (indent, columnName)
 +                print "%s    %s = xmalloc(%s * sizeof *%s);" % (indent, keyVar, nMax, keyVar)
 +                if valueVar:
 +                    print "%s    %s = xmalloc(%s * sizeof %s);" % (indent, valueVar, nMax, valueVar)
 +                print "%s}" % indent
 +                print "%s%s[row->n_%s] = %s;" % (indent, keyVar, columnName, keySrc)
 +                if valueVar:
 +                    print "%s%s[row->n_%s] = %s;" % (indent, valueVar, columnName, valueSrc)
 +                print "%srow->n_%s++;" % (indent, columnName)
 +                if refs:
 +                    print "        }"
 +                print "    }"
 +            print "}"
 +
 +        # Unparse functions.
 +        for columnName, column in sorted(table.columns.iteritems()):
 +            type = column.type
 +            if (type.min != 1 or type.max != 1) and not type.isOptionalPointer():
 +                print '''
 +static void
 +%(s)s_unparse_%(c)s(struct ovsdb_idl_row *row_)
 +{
 +    struct %(s)s *row = %(s)s_cast(row_);
 +
 +    assert(inited);''' % {'s': structName, 'c': columnName}
 +                if type.value:
 +                    keyVar = "row->key_%s" % columnName
 +                    valueVar = "row->value_%s" % columnName
 +                else:
 +                    keyVar = "row->%s" % columnName
 +                    valueVar = None
 +                print "    free(%s);" % keyVar
 +                if valueVar:
 +                    print "    free(%s);" % valueVar
 +                print '}'
 +            else:
 +                print '''
 +static void
++%(s)s_unparse_%(c)s(struct ovsdb_idl_row *row OVS_UNUSED)
 +{
 +    /* Nothing to do. */
 +}''' % {'s': structName, 'c': columnName}
 + 
 +        # First, next functions.
 +        print '''
 +const struct %(s)s *
 +%(s)s_first(const struct ovsdb_idl *idl)
 +{
 +    return %(s)s_cast(ovsdb_idl_first_row(idl, &%(p)stable_classes[%(P)sTABLE_%(T)s]));
 +}
 +
 +const struct %(s)s *
 +%(s)s_next(const struct %(s)s *row)
 +{
 +    return %(s)s_cast(ovsdb_idl_next_row(&row->header_));
 +}''' % {'s': structName,
 +        'p': prefix,
 +        'P': prefix.upper(),
 +        'T': tableName.upper()}
 +
 +        print '''
 +void
 +%(s)s_delete(const struct %(s)s *row)
 +{
 +    ovsdb_idl_txn_delete(&row->header_);
 +}
 +
 +struct %(s)s *
 +%(s)s_insert(struct ovsdb_idl_txn *txn)
 +{
 +    return %(s)s_cast(ovsdb_idl_txn_insert(txn, &%(p)stable_classes[%(P)sTABLE_%(T)s]));
 +}
 +''' % {'s': structName,
 +       'p': prefix,
 +       'P': prefix.upper(),
 +       'T': tableName.upper()}
 +
 +        # Verify functions.
 +        for columnName, column in sorted(table.columns.iteritems()):
 +            print '''
 +void
 +%(s)s_verify_%(c)s(const struct %(s)s *row)
 +{
 +    assert(inited);
 +    ovsdb_idl_txn_verify(&row->header_, &%(s)s_columns[%(S)s_COL_%(C)s]);
 +}''' % {'s': structName,
 +        'S': structName.upper(),
 +        'c': columnName,
 +        'C': columnName.upper()}
 +
 +        # Set functions.
 +        for columnName, column in sorted(table.columns.iteritems()):
 +            type = column.type
 +            print '\nvoid'
 +            members = cMembers(prefix, columnName, column, True)
 +            keyVar = members[0]['name']
 +            nVar = None
 +            valueVar = None
 +            if type.value:
 +                valueVar = members[1]['name']
 +                if len(members) > 2:
 +                    nVar = members[2]['name']
 +            else:
 +                if len(members) > 1:
 +                    nVar = members[1]['name']
 +            print '%(s)s_set_%(c)s(const struct %(s)s *row, %(args)s)' % \
 +                {'s': structName, 'c': columnName,
 +                 'args': ', '.join(['%(type)s%(name)s' % m for m in members])}
 +            print "{"
 +            print "    struct ovsdb_datum datum;"
 +            if type.min == 1 and type.max == 1:
 +                print
 +                print "    assert(inited);"
 +                print "    datum.n = 1;"
 +                print "    datum.keys = xmalloc(sizeof *datum.keys);"
 +                print "    " + type.key.copyCValue("datum.keys[0].%s" % type.key.type, keyVar)
 +                if type.value:
 +                    print "    datum.values = xmalloc(sizeof *datum.values);"
 +                    print "    "+ type.value.copyCValue("datum.values[0].%s" % type.value.type, valueVar)
 +                else:
 +                    print "    datum.values = NULL;"
 +            elif type.isOptionalPointer():
 +                print
 +                print "    assert(inited);"
 +                print "    if (%s) {" % keyVar
 +                print "        datum.n = 1;"
 +                print "        datum.keys = xmalloc(sizeof *datum.keys);"
 +                print "        " + type.key.copyCValue("datum.keys[0].%s" % type.key.type, keyVar)
 +                print "    } else {"
 +                print "        datum.n = 0;"
 +                print "        datum.keys = NULL;"
 +                print "    }"
 +                print "    datum.values = NULL;"
 +            else:
 +                print "    size_t i;"
 +                print
 +                print "    assert(inited);"
 +                print "    datum.n = %s;" % nVar
 +                print "    datum.keys = xmalloc(%s * sizeof *datum.keys);" % nVar
 +                if type.value:
 +                    print "    datum.values = xmalloc(%s * sizeof *datum.values);" % nVar
 +                else:
 +                    print "    datum.values = NULL;"
 +                print "    for (i = 0; i < %s; i++) {" % nVar
 +                print "        " + type.key.copyCValue("datum.keys[i].%s" % type.key.type, "%s[i]" % keyVar)
 +                if type.value:
 +                    print "        " + type.value.copyCValue("datum.values[i].%s" % type.value.type, "%s[i]" % valueVar)
 +                print "    }"
 +            print "    ovsdb_idl_txn_write(&row->header_, &%(s)s_columns[%(S)s_COL_%(C)s], &datum);" \
 +                % {'s': structName,
 +                   'S': structName.upper(),
 +                   'C': columnName.upper()}
 +            print "}"
 +
 +        # Table columns.
 +        print "\nstruct ovsdb_idl_column %s_columns[%s_N_COLUMNS];" % (
 +            structName, structName.upper())
 +        print """
 +static void\n%s_columns_init(void)
 +{
 +    struct ovsdb_idl_column *c;\
 +""" % structName
 +        for columnName, column in sorted(table.columns.iteritems()):
 +            cs = "%s_col_%s" % (structName, columnName)
 +            d = {'cs': cs, 'c': columnName, 's': structName}
 +            print
 +            print "    /* Initialize %(cs)s. */" % d
 +            print "    c = &%(cs)s;" % d
 +            print "    c->name = \"%(c)s\";" % d
 +            print column.type.cInitType("    ", "c->type")
 +            print "    c->parse = %(s)s_parse_%(c)s;" % d
 +            print "    c->unparse = %(s)s_unparse_%(c)s;" % d
 +        print "}"
 +
 +    # Table classes.
 +    print "\f"
 +    print "struct ovsdb_idl_table_class %stable_classes[%sN_TABLES] = {" % (prefix, prefix.upper())
 +    for tableName, table in sorted(schema.tables.iteritems()):
 +        structName = "%s%s" % (prefix, tableName.lower())
 +        print "    {\"%s\"," % tableName
 +        print "     %s_columns, ARRAY_SIZE(%s_columns)," % (
 +            structName, structName)
 +        print "     sizeof(struct %s)}," % structName
 +    print "};"
 +
 +    # IDL class.
 +    print "\nstruct ovsdb_idl_class %sidl_class = {" % prefix
 +    print "    \"%s\", %stable_classes, ARRAY_SIZE(%stable_classes)" % (
 +        schema.name, prefix, prefix)
 +    print "};"
 +
 +    # global init function
 +    print """
 +void
 +%sinit(void)
 +{
 +    if (inited) {
 +        return;
 +    }
 +    inited = true;
 +""" % prefix
 +    for tableName, table in sorted(schema.tables.iteritems()):
 +        structName = "%s%s" % (prefix, tableName.lower())
 +        print "    %s_columns_init();" % structName
 +    print "}"
 +
 +def ovsdb_escape(string):
 +    def escape(match):
 +        c = match.group(0)
 +        if c == '\0':
 +            raise Error("strings may not contain null bytes")
 +        elif c == '\\':
 +            return '\\\\'
 +        elif c == '\n':
 +            return '\\n'
 +        elif c == '\r':
 +            return '\\r'
 +        elif c == '\t':
 +            return '\\t'
 +        elif c == '\b':
 +            return '\\b'
 +        elif c == '\a':
 +            return '\\a'
 +        else:
 +            return '\\x%02x' % ord(c)
 +    return re.sub(r'["\\\000-\037]', escape, string)
 +
 +def printDoc(schemaFile):
 +    schema = parseSchema(schemaFile)
 +    print schema.name
 +    if schema.comment:
 +        print schema.comment
 +
 +    for tableName, table in sorted(schema.tables.iteritems()):
 +        title = "%s table" % tableName
 +        print
 +        print title
 +        print '-' * len(title)
 +        if table.comment:
 +            print table.comment
 +
 +        for columnName, column in sorted(table.columns.iteritems()):
 +            print
 +            print "%s (%s)" % (columnName, column.type.toEnglish())
 +            if column.comment:
 +                print "\t%s" % column.comment
 +
 +def usage():
 +    print """\
 +%(argv0)s: ovsdb schema compiler
 +usage: %(argv0)s [OPTIONS] COMMAND ARG...
 +
 +The following commands are supported:
 +  annotate SCHEMA ANNOTATIONS print SCHEMA combined with ANNOTATIONS
 +  c-idl-header IDL            print C header file for IDL
 +  c-idl-source IDL            print C source file for IDL implementation
 +  doc IDL                     print schema documentation
 +
 +The following options are also available:
 +  -h, --help                  display this help message
 +  -V, --version               display version information\
 +""" % {'argv0': argv0}
 +    sys.exit(0)
 +
 +if __name__ == "__main__":
 +    try:
 +        try:
 +            options, args = getopt.gnu_getopt(sys.argv[1:], 'C:hV',
 +                                              ['directory',
 +                                               'help',
 +                                               'version'])
 +        except getopt.GetoptError, geo:
 +            sys.stderr.write("%s: %s\n" % (argv0, geo.msg))
 +            sys.exit(1)
 +            
 +        for key, value in options:
 +            if key in ['-h', '--help']:
 +                usage()
 +            elif key in ['-V', '--version']:
 +                print "ovsdb-idlc (Open vSwitch) @VERSION@"
 +            elif key in ['-C', '--directory']:
 +                os.chdir(value)
 +            else:
 +                sys.exit(0)
 +            
 +        optKeys = [key for key, value in options]
 +
 +        if not args:
 +            sys.stderr.write("%s: missing command argument "
 +                             "(use --help for help)\n" % argv0)
 +            sys.exit(1)
 +
 +        commands = {"annotate": (annotateSchema, 2),
 +                    "c-idl-header": (printCIDLHeader, 1),
 +                    "c-idl-source": (printCIDLSource, 1),
 +                    "doc": (printDoc, 1)}
 +
 +        if not args[0] in commands:
 +            sys.stderr.write("%s: unknown command \"%s\" "
 +                             "(use --help for help)\n" % (argv0, args[0]))
 +            sys.exit(1)
 +
 +        func, n_args = commands[args[0]]
 +        if len(args) - 1 != n_args:
 +            sys.stderr.write("%s: \"%s\" requires %d arguments but %d "
 +                             "provided\n"
 +                             % (argv0, args[0], n_args, len(args) - 1))
 +            sys.exit(1)
 +
 +        func(*args[1:])
 +    except Error, e:
 +        sys.stderr.write("%s: %s\n" % (argv0, e.msg))
 +        sys.exit(1)
 +
 +# Local variables:
 +# mode: python
 +# End:
diff --combined ovsdb/ovsdb-server.c
index 03ebba1,0000000..eccbf7d
mode 100644,000000..100644
--- /dev/null
@@@ -1,308 -1,0 +1,308 @@@
- ovsdb_server_exit(struct unixctl_conn *conn, const char *args UNUSED,
 +/* Copyright (c) 2009, 2010 Nicira Networks
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at:
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <config.h>
 +
 +#include "ovsdb.h"
 +
 +#include <errno.h>
 +#include <getopt.h>
 +#include <signal.h>
 +#include <unistd.h>
 +
 +#include "column.h"
 +#include "command-line.h"
 +#include "daemon.h"
 +#include "file.h"
 +#include "json.h"
 +#include "jsonrpc.h"
 +#include "jsonrpc-server.h"
 +#include "leak-checker.h"
 +#include "list.h"
 +#include "ovsdb-data.h"
 +#include "ovsdb-types.h"
 +#include "ovsdb-error.h"
 +#include "poll-loop.h"
 +#include "process.h"
 +#include "row.h"
 +#include "stream-ssl.h"
 +#include "stream.h"
 +#include "svec.h"
 +#include "table.h"
 +#include "timeval.h"
 +#include "trigger.h"
 +#include "util.h"
 +#include "unixctl.h"
 +
 +#include "vlog.h"
 +#define THIS_MODULE VLM_ovsdb_server
 +
 +static unixctl_cb_func ovsdb_server_exit;
 +
 +static void parse_options(int argc, char *argv[], char **file_namep,
 +                          struct shash *remotes, char **unixctl_pathp);
 +static void usage(void) NO_RETURN;
 +
 +static void set_remotes(struct ovsdb_jsonrpc_server *jsonrpc,
 +                        const struct ovsdb *db, struct shash *remotes);
 +
 +int
 +main(int argc, char *argv[])
 +{
 +    char *unixctl_path = NULL;
 +    struct unixctl_server *unixctl;
 +    struct ovsdb_jsonrpc_server *jsonrpc;
 +    struct shash remotes;
 +    struct ovsdb_error *error;
 +    struct ovsdb *db;
 +    char *file_name;
 +    bool exiting;
 +    int retval;
 +
 +    proctitle_init(argc, argv);
 +    set_program_name(argv[0]);
 +    time_init();
 +    vlog_init();
 +    signal(SIGPIPE, SIG_IGN);
 +    process_init();
 +
 +    parse_options(argc, argv, &file_name, &remotes, &unixctl_path);
 +
 +    die_if_already_running();
 +    daemonize_start();
 +
 +    error = ovsdb_file_open(file_name, false, &db);
 +    if (error) {
 +        ovs_fatal(0, "%s", ovsdb_error_to_string(error));
 +    }
 +
 +    jsonrpc = ovsdb_jsonrpc_server_create(db);
 +    set_remotes(jsonrpc, db, &remotes);
 +
 +    retval = unixctl_server_create(unixctl_path, &unixctl);
 +    if (retval) {
 +        exit(EXIT_FAILURE);
 +    }
 +
 +    daemonize_complete();
 +
 +    unixctl_command_register("exit", ovsdb_server_exit, &exiting);
 +
 +    exiting = false;
 +    while (!exiting) {
 +        set_remotes(jsonrpc, db, &remotes);
 +        ovsdb_jsonrpc_server_run(jsonrpc);
 +        unixctl_server_run(unixctl);
 +        ovsdb_trigger_run(db, time_msec());
 +
 +        ovsdb_jsonrpc_server_wait(jsonrpc);
 +        unixctl_server_wait(unixctl);
 +        ovsdb_trigger_wait(db, time_msec());
 +        poll_block();
 +    }
 +    ovsdb_jsonrpc_server_destroy(jsonrpc);
 +    ovsdb_destroy(db);
 +    shash_destroy(&remotes);
 +    unixctl_server_destroy(unixctl);
 +
 +    return 0;
 +}
 +
 +static void
 +query_db_remotes(const char *name_, const struct ovsdb *db,
 +                 struct shash *remotes)
 +{
 +    char *name, *table_name, *column_name;
 +    const struct ovsdb_column *column;
 +    const struct ovsdb_table *table;
 +    const struct ovsdb_row *row;
 +    char *save_ptr = NULL;
 +
 +    name = xstrdup(name_);
 +    strtok_r(name, ":", &save_ptr); /* "db:" */
 +    table_name = strtok_r(NULL, ",", &save_ptr);
 +    column_name = strtok_r(NULL, ",", &save_ptr);
 +    if (!table_name || !column_name) {
 +        ovs_fatal(0, "remote \"%s\": invalid syntax", name_);
 +    }
 +
 +    table = ovsdb_get_table(db, table_name);
 +    if (!table) {
 +        ovs_fatal(0, "remote \"%s\": no table named %s", name_, table_name);
 +    }
 +
 +    column = ovsdb_table_schema_get_column(table->schema, column_name);
 +    if (!column) {
 +        ovs_fatal(0, "remote \"%s\": table \"%s\" has no column \"%s\"",
 +                  name_, table_name, column_name);
 +    }
 +
 +    if (column->type.key.type != OVSDB_TYPE_STRING
 +        || column->type.value.type != OVSDB_TYPE_VOID) {
 +        ovs_fatal(0, "remote \"%s\": type of table \"%s\" column \"%s\" is "
 +                  "not string or set of strings",
 +                  name_, table_name, column_name);
 +    }
 +
 +    HMAP_FOR_EACH (row, struct ovsdb_row, hmap_node, &table->rows) {
 +        const struct ovsdb_datum *datum;
 +        size_t i;
 +
 +        datum = &row->fields[column->index];
 +        for (i = 0; i < datum->n; i++) {
 +            shash_add_once(remotes, datum->keys[i].string, NULL);
 +        }
 +    }
 +
 +    free(name);
 +}
 +
 +static void
 +set_remotes(struct ovsdb_jsonrpc_server *jsonrpc,
 +            const struct ovsdb *db, struct shash *remotes)
 +{
 +    struct shash resolved_remotes;
 +    struct shash_node *node;
 +
 +    shash_init(&resolved_remotes);
 +    SHASH_FOR_EACH (node, remotes) {
 +        const char *name = node->name;
 +
 +        if (!strncmp(name, "db:", 3)) {
 +            query_db_remotes(name, db, &resolved_remotes);
 +        } else {
 +            shash_add_once(&resolved_remotes, name, NULL);
 +        }
 +    }
 +    ovsdb_jsonrpc_server_set_remotes(jsonrpc, &resolved_remotes);
 +    shash_destroy(&resolved_remotes);
 +}
 +
 +
 +static void
++ovsdb_server_exit(struct unixctl_conn *conn, const char *args OVS_UNUSED,
 +                  void *exiting_)
 +{
 +    bool *exiting = exiting_;
 +    *exiting = true;
 +    unixctl_command_reply(conn, 200, NULL);
 +}
 +
 +static void
 +parse_options(int argc, char *argv[], char **file_namep,
 +              struct shash *remotes, char **unixctl_pathp)
 +{
 +    enum {
 +        OPT_DUMMY = UCHAR_MAX + 1,
 +        OPT_REMOTE,
 +        OPT_UNIXCTL,
 +        OPT_BOOTSTRAP_CA_CERT,
 +        VLOG_OPTION_ENUMS,
 +        LEAK_CHECKER_OPTION_ENUMS
 +    };
 +    static struct option long_options[] = {
 +        {"remote",      required_argument, 0, OPT_REMOTE},
 +        {"unixctl",     required_argument, 0, OPT_UNIXCTL},
 +        {"help",        no_argument, 0, 'h'},
 +        {"version",     no_argument, 0, 'V'},
 +        DAEMON_LONG_OPTIONS,
 +        VLOG_LONG_OPTIONS,
 +        LEAK_CHECKER_LONG_OPTIONS,
 +#ifdef HAVE_OPENSSL
 +        {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT},
 +        STREAM_SSL_LONG_OPTIONS
 +#endif
 +        {0, 0, 0, 0},
 +    };
 +    char *short_options = long_options_to_short_options(long_options);
 +
 +    shash_init(remotes);
 +    for (;;) {
 +        int c;
 +
 +        c = getopt_long(argc, argv, short_options, long_options, NULL);
 +        if (c == -1) {
 +            break;
 +        }
 +
 +        switch (c) {
 +        case OPT_REMOTE:
 +            shash_add_once(remotes, optarg, NULL);
 +            break;
 +
 +        case OPT_UNIXCTL:
 +            *unixctl_pathp = optarg;
 +            break;
 +
 +        case 'h':
 +            usage();
 +
 +        case 'V':
 +            OVS_PRINT_VERSION(0, 0);
 +            exit(EXIT_SUCCESS);
 +
 +        VLOG_OPTION_HANDLERS
 +        DAEMON_OPTION_HANDLERS
 +        LEAK_CHECKER_OPTION_HANDLERS
 +
 +#ifdef HAVE_OPENSSL
 +        STREAM_SSL_OPTION_HANDLERS
 +
 +        case OPT_BOOTSTRAP_CA_CERT:
 +            stream_ssl_set_ca_cert_file(optarg, true);
 +            break;
 +#endif
 +
 +
 +        case '?':
 +            exit(EXIT_FAILURE);
 +
 +        default:
 +            abort();
 +        }
 +    }
 +    free(short_options);
 +
 +    argc -= optind;
 +    argv += optind;
 +
 +    if (argc > 1) {
 +        ovs_fatal(0, "database file is only non-option argument; "
 +                "use --help for usage");
 +    } else if (argc < 1) {
 +        ovs_fatal(0, "missing database file argument; use --help for usage");
 +    }
 +
 +    *file_namep = argv[0];
 +}
 +
 +static void
 +usage(void)
 +{
 +    printf("%s: Open vSwitch database server\n"
 +           "usage: %s [OPTIONS] DATABASE\n"
 +           "where DATABASE is a database file in ovsdb format.\n",
 +           program_name, program_name);
 +    printf("\nJSON-RPC options (may be specified any number of times):\n"
 +           "  --remote=REMOTE         connect or listen to REMOTE\n");
 +    stream_usage("JSON-RPC", true, true, true);
 +    daemon_usage();
 +    vlog_usage();
 +    printf("\nOther options:\n"
 +           "  -h, --help              display this help message\n"
 +           "  -V, --version           display version information\n");
 +    leak_checker_usage();
 +    exit(EXIT_SUCCESS);
 +}
diff --combined ovsdb/ovsdb-tool.c
index 1f2a5ef,0000000..34c7676
mode 100644,000000..100644
--- /dev/null
@@@ -1,344 -1,0 +1,344 @@@
- do_create(int argc UNUSED, char *argv[])
 +/*
 + * Copyright (c) 2009, 2010 Nicira Networks.
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at:
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <config.h>
 +#include <errno.h>
 +#include <fcntl.h>
 +#include <getopt.h>
 +#include <signal.h>
 +#include <stdlib.h>
 +#include <string.h>
 +
 +#include "command-line.h"
 +#include "compiler.h"
 +#include "file.h"
 +#include "log.h"
 +#include "json.h"
 +#include "ovsdb.h"
 +#include "ovsdb-error.h"
 +#include "table.h"
 +#include "timeval.h"
 +#include "util.h"
 +
 +#include "vlog.h"
 +#define THIS_MODULE VLM_ovsdb_tool
 +
 +/* -m, --more: Verbosity level for "show-log" command output. */
 +static int show_log_verbosity;
 +
 +static const struct command all_commands[];
 +
 +static void usage(void) NO_RETURN;
 +static void parse_options(int argc, char *argv[]);
 +
 +int
 +main(int argc, char *argv[])
 +{
 +    set_program_name(argv[0]);
 +    time_init();
 +    vlog_init();
 +    parse_options(argc, argv);
 +    signal(SIGPIPE, SIG_IGN);
 +    run_command(argc - optind, argv + optind, all_commands);
 +    return 0;
 +}
 +
 +static void
 +parse_options(int argc, char *argv[])
 +{
 +    static struct option long_options[] = {
 +        {"more", no_argument, 0, 'm'},
 +        {"verbose", optional_argument, 0, 'v'},
 +        {"help", no_argument, 0, 'h'},
 +        {"version", no_argument, 0, 'V'},
 +        {0, 0, 0, 0},
 +    };
 +    char *short_options = long_options_to_short_options(long_options);
 +
 +    for (;;) {
 +        int c;
 +
 +        c = getopt_long(argc, argv, short_options, long_options, NULL);
 +        if (c == -1) {
 +            break;
 +        }
 +
 +        switch (c) {
 +        case 'm':
 +            show_log_verbosity++;
 +            break;
 +
 +        case 'h':
 +            usage();
 +
 +        case 'V':
 +            OVS_PRINT_VERSION(0, 0);
 +            exit(EXIT_SUCCESS);
 +
 +        case 'v':
 +            vlog_set_verbosity(optarg);
 +            break;
 +
 +        case '?':
 +            exit(EXIT_FAILURE);
 +
 +        default:
 +            abort();
 +        }
 +    }
 +    free(short_options);
 +}
 +
 +static void
 +usage(void)
 +{
 +    printf("%s: Open vSwitch database management utility\n"
 +           "usage: %s [OPTIONS] COMMAND [ARG...]\n"
 +           "  create DB SCHEMA   create DB with the given SCHEMA\n"
 +           "  compact DB [DST]   compact DB in-place (or to DST)\n"
 +           "  extract-schema DB  print DB's schema on stdout\n"
 +           "  query DB TRNS      execute read-only transaction on DB\n"
 +           "  transact DB TRNS   execute read/write transaction on DB\n"
 +           "  show-log DB        prints information about DB's log entries\n",
 +           program_name, program_name);
 +    vlog_usage();
 +    printf("\nOther options:\n"
 +           "  -m, --more                  increase show-log verbosity\n"
 +           "  -h, --help                  display this help message\n"
 +           "  -V, --version               display version information\n");
 +    exit(EXIT_SUCCESS);
 +}
 +\f
 +static struct json *
 +parse_json(const char *s)
 +{
 +    struct json *json = json_from_string(s);
 +    if (json->type == JSON_STRING) {
 +        ovs_fatal(0, "\"%s\": %s", s, json->u.string);
 +    }
 +    return json;
 +}
 +
 +static void
 +print_and_free_json(struct json *json)
 +{
 +    char *string = json_to_string(json, JSSF_SORT);
 +    json_destroy(json);
 +    puts(string);
 +    free(string);
 +}
 +
 +static void
 +check_ovsdb_error(struct ovsdb_error *error)
 +{
 +    if (error) {
 +        ovs_fatal(0, "%s", ovsdb_error_to_string(error));
 +    }
 +}
 +\f
 +static void
- do_query(int argc UNUSED, char *argv[])
++do_create(int argc OVS_UNUSED, char *argv[])
 +{
 +    const char *db_file_name = argv[1];
 +    const char *schema_file_name = argv[2];
 +    struct ovsdb_schema *schema;
 +    struct ovsdb_log *log;
 +    struct json *json;
 +
 +    /* Read schema from file and convert to JSON. */
 +    check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema));
 +    json = ovsdb_schema_to_json(schema);
 +    ovsdb_schema_destroy(schema);
 +
 +    /* Create database file. */
 +    check_ovsdb_error(ovsdb_log_open(db_file_name, O_RDWR | O_CREAT | O_EXCL,
 +                                     &log));
 +    check_ovsdb_error(ovsdb_log_write(log, json));
 +    check_ovsdb_error(ovsdb_log_commit(log));
 +    ovsdb_log_close(log);
 +
 +    json_destroy(json);
 +}
 +
 +static void
 +transact(bool read_only, const char *db_file_name, const char *transaction)
 +{
 +    struct json *request, *result;
 +    struct ovsdb *db;
 +
 +    check_ovsdb_error(ovsdb_file_open(db_file_name, read_only, &db));
 +
 +    request = parse_json(transaction);
 +    result = ovsdb_execute(db, request, 0, NULL);
 +    json_destroy(request);
 +
 +    print_and_free_json(result);
 +    ovsdb_destroy(db);
 +}
 +
 +static void
- do_transact(int argc UNUSED, char *argv[])
++do_query(int argc OVS_UNUSED, char *argv[])
 +{
 +    transact(true, argv[1], argv[2]);
 +}
 +
 +static void
- do_show_log(int argc UNUSED, char *argv[])
++do_transact(int argc OVS_UNUSED, char *argv[])
 +{
 +    transact(false, argv[1], argv[2]);
 +}
 +
 +static void
 +print_db_changes(struct shash *tables, struct shash *names)
 +{
 +    struct shash_node *n1;
 +
 +    SHASH_FOR_EACH (n1, tables) {
 +        const char *table = n1->name;
 +        struct json *rows = n1->data;
 +        struct shash_node *n2;
 +
 +        if (n1->name[0] == '_' || rows->type != JSON_OBJECT) {
 +            continue;
 +        }
 +
 +        SHASH_FOR_EACH (n2, json_object(rows)) {
 +            const char *row_uuid = n2->name;
 +            struct json *columns = n2->data;
 +            struct shash_node *n3;
 +            char *old_name, *new_name;
 +            bool free_new_name = false;
 +
 +            old_name = new_name = shash_find_data(names, row_uuid);
 +            if (columns->type == JSON_OBJECT) {
 +                struct json *new_name_json;
 +
 +                new_name_json = shash_find_data(json_object(columns), "name");
 +                if (new_name_json) {
 +                    new_name = json_to_string(new_name_json, JSSF_SORT);
 +                    free_new_name = true;
 +                }
 +            }
 +
 +            printf("\ttable %s", table);
 +
 +            if (!old_name) {
 +                if (new_name) {
 +                    printf(" insert row %s:\n", new_name);
 +                } else {
 +                    printf(" insert row %.8s:\n", row_uuid);
 +                }
 +            } else {
 +                printf(" row %s:\n", old_name);
 +            }
 +
 +            if (columns->type == JSON_OBJECT) {
 +                if (show_log_verbosity > 1) {
 +                    SHASH_FOR_EACH (n3, json_object(columns)) {
 +                        const char *column = n3->name;
 +                        struct json *value = n3->data;
 +                        char *value_string;
 +
 +                        value_string = json_to_string(value, JSSF_SORT);
 +                        printf("\t\t%s=%s\n", column, value_string);
 +                        free(value_string);
 +                    }
 +                }
 +                if (!old_name
 +                    || (new_name != old_name && strcmp(old_name, new_name))) {
 +                    if (old_name) {
 +                        shash_delete(names, shash_find(names, row_uuid));
 +                        free(old_name);
 +                    }
 +                    shash_add(names, row_uuid, (new_name
 +                                                ? xstrdup(new_name)
 +                                                : xmemdup0(row_uuid, 8)));
 +                }
 +            } else if (columns->type == JSON_NULL) {
 +                printf("\t\tdelete row\n");
 +                shash_delete(names, shash_find(names, row_uuid));
 +                free(old_name);
 +            }
 +
 +            if (free_new_name) {
 +                free(new_name);
 +            }
 +        }
 +    }
 +}
 +
 +static void
- do_help(int argc UNUSED, char *argv[] UNUSED)
++do_show_log(int argc OVS_UNUSED, char *argv[])
 +{
 +    const char *db_file_name = argv[1];
 +    struct shash names;
 +    struct ovsdb_log *log;
 +    unsigned int i;
 +
 +    check_ovsdb_error(ovsdb_log_open(db_file_name, O_RDONLY, &log));
 +    shash_init(&names);
 +    for (i = 0; ; i++) {
 +        struct json *json;
 +
 +        check_ovsdb_error(ovsdb_log_read(log, &json));
 +        if (!json) {
 +            break;
 +        }
 +
 +        printf("record %u:", i);
 +        if (json->type == JSON_OBJECT) {
 +            struct json *date, *comment;
 +
 +            date = shash_find_data(json_object(json), "_date");
 +            if (date && date->type == JSON_INTEGER) {
 +                time_t t = json_integer(date);
 +                char s[128];
 +
 +                strftime(s, sizeof s, "%Y-%m-%d %H:%M:%S", localtime(&t));
 +                printf(" %s", s);
 +            }
 +
 +            comment = shash_find_data(json_object(json), "_comment");
 +            if (comment && comment->type == JSON_STRING) {
 +                printf(" \"%s\"", json_string(comment));
 +            }
 +
 +            if (i > 0 && show_log_verbosity > 0) {
 +                putchar('\n');
 +                print_db_changes(json_object(json), &names);
 +            }
 +        }
 +        json_destroy(json);
 +        putchar('\n');
 +    }
 +
 +    /* XXX free 'names'. */
 +}
 +
 +static void
++do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 +{
 +    usage();
 +}
 +
 +static const struct command all_commands[] = {
 +    { "create", 2, 2, do_create },
 +    { "query", 2, 2, do_query },
 +    { "transact", 2, 2, do_transact },
 +    { "show-log", 1, 1, do_show_log },
 +    { "help", 0, INT_MAX, do_help },
 +    { NULL, 0, 0, NULL },
 +};
diff --combined ovsdb/ovsdb.c
index 2dea507,0000000..2b5bdc3
mode 100644,000000..100644
--- /dev/null
@@@ -1,294 -1,0 +1,294 @@@
- ovsdb_remove_replica(struct ovsdb *db UNUSED, struct ovsdb_replica *r)
 +/* Copyright (c) 2009, 2010 Nicira Networks
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at:
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <config.h>
 +
 +#include "ovsdb.h"
 +
 +#include "column.h"
 +#include "json.h"
 +#include "ovsdb-error.h"
 +#include "ovsdb-parser.h"
 +#include "ovsdb-types.h"
 +#include "table.h"
 +#include "transaction.h"
 +
 +struct ovsdb_schema *
 +ovsdb_schema_create(const char *name, const char *comment)
 +{
 +    struct ovsdb_schema *schema;
 +
 +    schema = xzalloc(sizeof *schema);
 +    schema->name = xstrdup(name);
 +    schema->comment = comment ? xstrdup(comment) : NULL;
 +    shash_init(&schema->tables);
 +
 +    return schema;
 +}
 +
 +void
 +ovsdb_schema_destroy(struct ovsdb_schema *schema)
 +{
 +    struct shash_node *node;
 +
 +    SHASH_FOR_EACH (node, &schema->tables) {
 +        ovsdb_table_schema_destroy(node->data);
 +    }
 +    shash_destroy(&schema->tables);
 +    free(schema->comment);
 +    free(schema->name);
 +    free(schema);
 +}
 +
 +struct ovsdb_error *
 +ovsdb_schema_from_file(const char *file_name, struct ovsdb_schema **schemap)
 +{
 +    struct ovsdb_schema *schema;
 +    struct ovsdb_error *error;
 +    struct json *json;
 +
 +    *schemap = NULL;
 +    json = json_from_file(file_name);
 +    if (json->type == JSON_STRING) {
 +        error = ovsdb_error("failed to read schema",
 +                           "\"%s\" could not be read as JSON (%s)",
 +                           file_name, json_string(json));
 +        json_destroy(json);
 +        return error;
 +    }
 +
 +    error = ovsdb_schema_from_json(json, &schema);
 +    json_destroy(json);
 +    if (error) {
 +        return ovsdb_wrap_error(error,
 +                                "failed to parse \"%s\" as ovsdb schema",
 +                                file_name);
 +    }
 +
 +    *schemap = schema;
 +    return NULL;
 +}
 +
 +static struct ovsdb_error * WARN_UNUSED_RESULT
 +ovsdb_schema_check_ref_table(const struct ovsdb_column *column,
 +                             const struct shash *tables,
 +                             const struct ovsdb_base_type *base,
 +                             const char *base_name)
 +{
 +    if (base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName
 +        && !shash_find(tables, base->u.uuid.refTableName)) {
 +        return ovsdb_syntax_error(NULL, NULL,
 +                                  "column %s %s refers to undefined table %s",
 +                                  column->name, base_name,
 +                                  base->u.uuid.refTableName);
 +    } else {
 +        return NULL;
 +    }
 +}
 +
 +struct ovsdb_error *
 +ovsdb_schema_from_json(struct json *json, struct ovsdb_schema **schemap)
 +{
 +    struct ovsdb_schema *schema;
 +    const struct json *name, *comment, *tables;
 +    struct ovsdb_error *error;
 +    struct shash_node *node;
 +    struct ovsdb_parser parser;
 +
 +    *schemap = NULL;
 +
 +    ovsdb_parser_init(&parser, json, "database schema");
 +    name = ovsdb_parser_member(&parser, "name", OP_ID);
 +    comment = ovsdb_parser_member(&parser, "comment", OP_STRING | OP_OPTIONAL);
 +    tables = ovsdb_parser_member(&parser, "tables", OP_OBJECT);
 +    error = ovsdb_parser_finish(&parser);
 +    if (error) {
 +        return error;
 +    }
 +
 +    schema = ovsdb_schema_create(json_string(name),
 +                                 comment ? json_string(comment) : NULL);
 +    SHASH_FOR_EACH (node, json_object(tables)) {
 +        struct ovsdb_table_schema *table;
 +
 +        if (node->name[0] == '_') {
 +            error = ovsdb_syntax_error(json, NULL, "names beginning with "
 +                                       "\"_\" are reserved");
 +        } else if (!ovsdb_parser_is_id(node->name)) {
 +            error = ovsdb_syntax_error(json, NULL, "name must be a valid id");
 +        } else {
 +            error = ovsdb_table_schema_from_json(node->data, node->name,
 +                                                 &table);
 +        }
 +        if (error) {
 +            ovsdb_schema_destroy(schema);
 +            return error;
 +        }
 +
 +        shash_add(&schema->tables, table->name, table);
 +    }
 +
 +    /* Validate that all refTables refer to the names of tables that exist. */
 +    SHASH_FOR_EACH (node, &schema->tables) {
 +        struct ovsdb_table_schema *table = node->data;
 +        struct shash_node *node2;
 +
 +        SHASH_FOR_EACH (node2, &table->columns) {
 +            struct ovsdb_column *column = node2->data;
 +
 +            error = ovsdb_schema_check_ref_table(column, &schema->tables,
 +                                                 &column->type.key, "key");
 +            if (!error) {
 +                error = ovsdb_schema_check_ref_table(column, &schema->tables,
 +                                                     &column->type.value,
 +                                                     "value");
 +            }
 +            if (error) {
 +                ovsdb_schema_destroy(schema);
 +                return error;
 +            }
 +        }
 +    }
 +
 +    *schemap = schema;
 +    return 0;
 +}
 +
 +struct json *
 +ovsdb_schema_to_json(const struct ovsdb_schema *schema)
 +{
 +    struct json *json, *tables;
 +    struct shash_node *node;
 +
 +    json = json_object_create();
 +    json_object_put_string(json, "name", schema->name);
 +    if (schema->comment) {
 +        json_object_put_string(json, "comment", schema->comment);
 +    }
 +
 +    tables = json_object_create();
 +
 +    SHASH_FOR_EACH (node, &schema->tables) {
 +        struct ovsdb_table_schema *table = node->data;
 +        json_object_put(tables, table->name,
 +                        ovsdb_table_schema_to_json(table));
 +    }
 +    json_object_put(json, "tables", tables);
 +
 +    return json;
 +}
 +\f
 +static void
 +ovsdb_set_ref_table(const struct shash *tables,
 +                    struct ovsdb_base_type *base)
 +{
 +    if (base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName) {
 +        struct ovsdb_table *table;
 +
 +        table = shash_find_data(tables, base->u.uuid.refTableName);
 +        base->u.uuid.refTable = table;
 +    }
 +}
 +
 +struct ovsdb *
 +ovsdb_create(struct ovsdb_schema *schema)
 +{
 +    struct shash_node *node;
 +    struct ovsdb *db;
 +
 +    db = xmalloc(sizeof *db);
 +    db->schema = schema;
 +    list_init(&db->replicas);
 +    list_init(&db->triggers);
 +    db->run_triggers = false;
 +
 +    shash_init(&db->tables);
 +    SHASH_FOR_EACH (node, &schema->tables) {
 +        struct ovsdb_table_schema *ts = node->data;
 +        shash_add(&db->tables, node->name, ovsdb_table_create(ts));
 +    }
 +
 +    /* Set all the refTables. */
 +    SHASH_FOR_EACH (node, &schema->tables) {
 +        struct ovsdb_table_schema *table = node->data;
 +        struct shash_node *node2;
 +
 +        SHASH_FOR_EACH (node2, &table->columns) {
 +            struct ovsdb_column *column = node2->data;
 +
 +            ovsdb_set_ref_table(&db->tables, &column->type.key);
 +            ovsdb_set_ref_table(&db->tables, &column->type.value);
 +        }
 +    }
 +
 +    return db;
 +}
 +
 +void
 +ovsdb_destroy(struct ovsdb *db)
 +{
 +    if (db) {
 +        struct shash_node *node;
 +
 +        /* Remove all the replicas. */
 +        while (!list_is_empty(&db->replicas)) {
 +            struct ovsdb_replica *r
 +                = CONTAINER_OF(list_pop_back(&db->replicas),
 +                               struct ovsdb_replica, node);
 +            ovsdb_remove_replica(db, r);
 +        }
 +
 +        /* Delete all the tables.  This also deletes their schemas. */
 +        SHASH_FOR_EACH (node, &db->tables) {
 +            struct ovsdb_table *table = node->data;
 +            ovsdb_table_destroy(table);
 +        }
 +        shash_destroy(&db->tables);
 +
 +        /* The schemas, but not the table that points to them, were deleted in
 +         * the previous step, so we need to clear out the table.  We can't
 +         * destroy the table, because ovsdb_schema_destroy() will do that. */
 +        shash_clear(&db->schema->tables);
 +
 +        ovsdb_schema_destroy(db->schema);
 +        free(db);
 +    }
 +}
 +
 +struct ovsdb_table *
 +ovsdb_get_table(const struct ovsdb *db, const char *name)
 +{
 +    return shash_find_data(&db->tables, name);
 +}
 +\f
 +void
 +ovsdb_replica_init(struct ovsdb_replica *r,
 +                   const struct ovsdb_replica_class *class)
 +{
 +    r->class = class;
 +}
 +
 +void
 +ovsdb_add_replica(struct ovsdb *db, struct ovsdb_replica *r)
 +{
 +    list_push_back(&db->replicas, &r->node);
 +}
 +
 +void
++ovsdb_remove_replica(struct ovsdb *db OVS_UNUSED, struct ovsdb_replica *r)
 +{
 +    list_remove(&r->node);
 +    (r->class->destroy)(r);
 +}
diff --combined tests/test-dhcp-client.c
@@@ -23,6 -23,7 +23,6 @@@
  #include "command-line.h"
  #include "dhcp.h"
  #include "fatal-signal.h"
 -#include "fault.h"
  #include "poll-loop.h"
  #include "util.h"
  #include "vlog.h"
@@@ -50,6 -51,7 +50,6 @@@ main(int argc, char *argv[]
      int error;
  
      set_program_name(argv[0]);
 -    register_fault_handlers();
      vlog_init();
      parse_options(argc, argv);
  
          ovs_fatal(error, "dhclient_create failed");
      }
      dhclient_init(cli, request_ip.s_addr);
 -    fatal_signal_add_hook(release, cli, true);
 +    fatal_signal_add_hook(release, NULL, cli, true);
  
      for (;;) {
 -        fatal_signal_block();
          dhclient_run(cli);
          if (dhclient_changed(cli)) {
              dhclient_configure_netdev(cli);
@@@ -76,6 -79,7 +76,6 @@@
              }
          }
          dhclient_wait(cli);
 -        fatal_signal_unblock();
          poll_block();
      }
  }
@@@ -91,7 -95,7 +91,7 @@@ release(void *cli_
  }
  
  static void
- modify_dhcp_request(struct dhcp_msg *msg, void *aux UNUSED)
+ modify_dhcp_request(struct dhcp_msg *msg, void *aux OVS_UNUSED)
  {
      if (vendor_class) {
          dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, vendor_class);
diff --combined tests/test-jsonrpc.c
index 03d3000,0000000..06b1cf4
mode 100644,000000..100644
--- /dev/null
@@@ -1,341 -1,0 +1,341 @@@
- do_listen(int argc UNUSED, char *argv[])
 +/*
 + * Copyright (c) 2009, 2010 Nicira Networks.
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at:
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <config.h>
 +
 +#include "jsonrpc.h"
 +
 +#include <errno.h>
 +#include <fcntl.h>
 +#include <getopt.h>
 +#include <stdio.h>
 +#include <stdlib.h>
 +
 +#include "command-line.h"
 +#include "daemon.h"
 +#include "json.h"
 +#include "poll-loop.h"
 +#include "stream-ssl.h"
 +#include "stream.h"
 +#include "timeval.h"
 +#include "util.h"
 +#include "vlog.h"
 +
 +static struct command all_commands[];
 +
 +static void usage(void) NO_RETURN;
 +static void parse_options(int argc, char *argv[]);
 +
 +int
 +main(int argc, char *argv[])
 +{
 +    proctitle_init(argc, argv);
 +    set_program_name(argv[0]);
 +    time_init();
 +    vlog_init();
 +    parse_options(argc, argv);
 +    run_command(argc - optind, argv + optind, all_commands);
 +    return 0;
 +}
 +
 +static void
 +parse_options(int argc, char *argv[])
 +{
 +    enum {
 +        OPT_BOOTSTRAP_CA_CERT = UCHAR_MAX + 1
 +    };
 +    static struct option long_options[] = {
 +        {"verbose", optional_argument, 0, 'v'},
 +        {"help", no_argument, 0, 'h'},
 +        DAEMON_LONG_OPTIONS,
 +#ifdef HAVE_OPENSSL
 +        {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT},
 +        STREAM_SSL_LONG_OPTIONS
 +#endif
 +        {0, 0, 0, 0},
 +    };
 +    char *short_options = long_options_to_short_options(long_options);
 +
 +    for (;;) {
 +        int c = getopt_long(argc, argv, short_options, long_options, NULL);
 +        if (c == -1) {
 +            break;
 +        }
 +
 +        switch (c) {
 +        case 'h':
 +            usage();
 +
 +        case 'v':
 +            vlog_set_verbosity(optarg);
 +            break;
 +
 +        DAEMON_OPTION_HANDLERS
 +
 +#ifdef HAVE_OPENSSL
 +        STREAM_SSL_OPTION_HANDLERS
 +
 +        case OPT_BOOTSTRAP_CA_CERT:
 +            stream_ssl_set_ca_cert_file(optarg, true);
 +            break;
 +#endif
 +
 +        case '?':
 +            exit(EXIT_FAILURE);
 +
 +        default:
 +            abort();
 +        }
 +    }
 +    free(short_options);
 +}
 +
 +static void
 +usage(void)
 +{
 +    printf("%s: JSON-RPC test utility\n"
 +           "usage: %s [OPTIONS] COMMAND [ARG...]\n"
 +           "  listen LOCAL             listen for connections on LOCAL\n"
 +           "  request REMOTE METHOD PARAMS   send request, print reply\n"
 +           "  notify REMOTE METHOD PARAMS  send notification and exit\n",
 +           program_name, program_name);
 +    stream_usage("JSON-RPC", true, true, true);
 +    daemon_usage();
 +    vlog_usage();
 +    printf("\nOther options:\n"
 +           "  -h, --help                  display this help message\n");
 +    exit(EXIT_SUCCESS);
 +}
 +\f
 +/* Command helper functions. */
 +
 +static struct json *
 +parse_json(const char *s)
 +{
 +    struct json *json = json_from_string(s);
 +    if (json->type == JSON_STRING) {
 +        ovs_fatal(0, "\"%s\": %s", s, json->u.string);
 +    }
 +    return json;
 +}
 +
 +static void
 +print_and_free_json(struct json *json)
 +{
 +    char *string = json_to_string(json, JSSF_SORT);
 +    json_destroy(json);
 +    puts(string);
 +    free(string);
 +}
 +\f
 +/* Command implementations. */
 +
 +static void
 +handle_rpc(struct jsonrpc *rpc, struct jsonrpc_msg *msg, bool *done)
 +{
 +    struct jsonrpc_msg *reply = NULL;
 +    if (msg->type == JSONRPC_REQUEST) {
 +        if (!strcmp(msg->method, "echo")) {
 +            reply = jsonrpc_create_reply(json_clone(msg->params), msg->id);
 +        } else {
 +            struct json *error = json_object_create();
 +            json_object_put_string(error, "error", "unknown method");
 +            reply = jsonrpc_create_error(error, msg->id);
 +            ovs_error(0, "unknown request %s", msg->method);
 +        }
 +
 +    } else if (msg->type == JSONRPC_NOTIFY) {
 +        if (!strcmp(msg->method, "shutdown")) {
 +            *done = true;
 +        } else {
 +            jsonrpc_error(rpc, ENOTTY);
 +            ovs_error(0, "unknown notification %s", msg->method);
 +        }
 +    } else {
 +        jsonrpc_error(rpc, EPROTO);
 +        ovs_error(0, "unsolicited JSON-RPC reply or error");
 +    }
 +
 +    if (reply) {
 +        jsonrpc_send(rpc, reply);
 +    }
 +}
 +
 +static void
- do_request(int argc UNUSED, char *argv[])
++do_listen(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct pstream *pstream;
 +    struct jsonrpc **rpcs;
 +    size_t n_rpcs, allocated_rpcs;
 +    bool done;
 +    int error;
 +
 +    die_if_already_running();
 +
 +    error = pstream_open(argv[1], &pstream);
 +    if (error) {
 +        ovs_fatal(error, "could not listen on \"%s\"", argv[1]);
 +    }
 +
 +    daemonize();
 +
 +    rpcs = NULL;
 +    n_rpcs = allocated_rpcs = 0;
 +    done = false;
 +    for (;;) {
 +        struct stream *stream;
 +        size_t i;
 +
 +        /* Accept new connections. */
 +        error = pstream_accept(pstream, &stream);
 +        if (!error) {
 +            if (n_rpcs >= allocated_rpcs) {
 +                rpcs = x2nrealloc(rpcs, &allocated_rpcs, sizeof *rpcs);
 +            }
 +            rpcs[n_rpcs++] = jsonrpc_open(stream);
 +        } else if (error != EAGAIN) {
 +            ovs_fatal(error, "pstream_accept failed");
 +        }
 +
 +        /* Service existing connections. */
 +        for (i = 0; i < n_rpcs; ) {
 +            struct jsonrpc *rpc = rpcs[i];
 +            struct jsonrpc_msg *msg;
 +
 +            jsonrpc_run(rpc);
 +            if (!jsonrpc_get_backlog(rpc)) {
 +                error = jsonrpc_recv(rpc, &msg);
 +                if (!error) {
 +                    handle_rpc(rpc, msg, &done);
 +                    jsonrpc_msg_destroy(msg);
 +                }
 +            }
 +
 +            error = jsonrpc_get_status(rpc);
 +            if (error) {
 +                jsonrpc_close(rpc);
 +                ovs_error(error, "connection closed");
 +                memmove(&rpcs[i], &rpcs[i + 1],
 +                        (n_rpcs - i - 1) * sizeof *rpcs);
 +                n_rpcs--;
 +            } else {
 +                i++;
 +            }
 +        }
 +
 +        /* Wait for something to do. */
 +        if (done && !n_rpcs) {
 +            break;
 +        }
 +        pstream_wait(pstream);
 +        for (i = 0; i < n_rpcs; i++) {
 +            struct jsonrpc *rpc = rpcs[i];
 +
 +            jsonrpc_wait(rpc);
 +            if (!jsonrpc_get_backlog(rpc)) {
 +                jsonrpc_recv_wait(rpc);
 +            }
 +        }
 +        poll_block();
 +    }
 +    free(rpcs);
 +    pstream_close(pstream);
 +}
 +
 +static void
- do_notify(int argc UNUSED, char *argv[])
++do_request(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct jsonrpc_msg *msg;
 +    struct jsonrpc *rpc;
 +    struct json *params;
 +    struct stream *stream;
 +    const char *method;
 +    char *string;
 +    int error;
 +
 +    method = argv[2];
 +    params = parse_json(argv[3]);
 +    msg = jsonrpc_create_request(method, params, NULL);
 +    string = jsonrpc_msg_is_valid(msg);
 +    if (string) {
 +        ovs_fatal(0, "not a valid JSON-RPC request: %s", string);
 +    }
 +
 +    error = stream_open_block(argv[1], &stream);
 +    if (error) {
 +        ovs_fatal(error, "could not open \"%s\"", argv[1]);
 +    }
 +    rpc = jsonrpc_open(stream);
 +
 +    error = jsonrpc_send(rpc, msg);
 +    if (error) {
 +        ovs_fatal(error, "could not send request");
 +    }
 +
 +    error = jsonrpc_recv_block(rpc, &msg);
 +    if (error) {
 +        ovs_fatal(error, "error waiting for reply");
 +    }
 +    print_and_free_json(jsonrpc_msg_to_json(msg));
 +
 +    jsonrpc_close(rpc);
 +}
 +
 +static void
- do_help(int argc UNUSED, char *argv[] UNUSED)
++do_notify(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct jsonrpc_msg *msg;
 +    struct jsonrpc *rpc;
 +    struct json *params;
 +    struct stream *stream;
 +    const char *method;
 +    char *string;
 +    int error;
 +
 +    method = argv[2];
 +    params = parse_json(argv[3]);
 +    msg = jsonrpc_create_notify(method, params);
 +    string = jsonrpc_msg_is_valid(msg);
 +    if (string) {
 +        ovs_fatal(0, "not a JSON RPC-valid notification: %s", string);
 +    }
 +
 +    error = stream_open_block(argv[1], &stream);
 +    if (error) {
 +        ovs_fatal(error, "could not open \"%s\"", argv[1]);
 +    }
 +    rpc = jsonrpc_open(stream);
 +
 +    error = jsonrpc_send_block(rpc, msg);
 +    if (error) {
 +        ovs_fatal(error, "could not send request");
 +    }
 +    jsonrpc_close(rpc);
 +}
 +
 +static void
++do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 +{
 +    usage();
 +}
 +
 +static struct command all_commands[] = {
 +    { "listen", 1, 1, do_listen },
 +    { "request", 3, 3, do_request },
 +    { "notify", 3, 3, do_notify },
 +    { "help", 0, INT_MAX, do_help },
 +    { NULL, 0, 0, NULL },
 +};
diff --combined tests/test-ovsdb.c
index 3025ce3,0000000..2e12d49
mode 100644,000000..100644
--- /dev/null
@@@ -1,1865 -1,0 +1,1865 @@@
- do_parse_atomic_type(int argc UNUSED, char *argv[])
 +/*
 + * Copyright (c) 2009, 2010 Nicira Networks.
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at:
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <config.h>
 +
 +#include <assert.h>
 +#include <fcntl.h>
 +#include <getopt.h>
 +#include <inttypes.h>
 +#include <stdio.h>
 +#include <stdlib.h>
 +
 +#include "command-line.h"
 +#include "dynamic-string.h"
 +#include "json.h"
 +#include "jsonrpc.h"
 +#include "ovsdb-data.h"
 +#include "ovsdb-error.h"
 +#include "ovsdb-idl.h"
 +#include "ovsdb-types.h"
 +#include "ovsdb/column.h"
 +#include "ovsdb/condition.h"
 +#include "ovsdb/file.h"
 +#include "ovsdb/log.h"
 +#include "ovsdb/mutation.h"
 +#include "ovsdb/ovsdb.h"
 +#include "ovsdb/query.h"
 +#include "ovsdb/row.h"
 +#include "ovsdb/table.h"
 +#include "ovsdb/transaction.h"
 +#include "ovsdb/trigger.h"
 +#include "poll-loop.h"
 +#include "stream.h"
 +#include "svec.h"
 +#include "tests/idltest.h"
 +#include "timeval.h"
 +#include "util.h"
 +#include "vlog.h"
 +
 +static struct command all_commands[];
 +
 +static void usage(void) NO_RETURN;
 +static void parse_options(int argc, char *argv[]);
 +
 +int
 +main(int argc, char *argv[])
 +{
 +    set_program_name(argv[0]);
 +    time_init();
 +    vlog_init();
 +    parse_options(argc, argv);
 +    run_command(argc - optind, argv + optind, all_commands);
 +    return 0;
 +}
 +
 +static void
 +parse_options(int argc, char *argv[])
 +{
 +    static struct option long_options[] = {
 +        {"timeout", required_argument, 0, 't'},
 +        {"verbose", optional_argument, 0, 'v'},
 +        {"help", no_argument, 0, 'h'},
 +        {0, 0, 0, 0},
 +    };
 +    char *short_options = long_options_to_short_options(long_options);
 +
 +    for (;;) {
 +        unsigned long int timeout;
 +        int c;
 +
 +        c = getopt_long(argc, argv, short_options, long_options, NULL);
 +        if (c == -1) {
 +            break;
 +        }
 +
 +        switch (c) {
 +        case 't':
 +            timeout = strtoul(optarg, NULL, 10);
 +            if (timeout <= 0) {
 +                ovs_fatal(0, "value %s on -t or --timeout is not at least 1",
 +                          optarg);
 +            } else {
 +                time_alarm(timeout);
 +            }
 +            break;
 +
 +        case 'h':
 +            usage();
 +
 +        case 'v':
 +            vlog_set_verbosity(optarg);
 +            break;
 +
 +        case '?':
 +            exit(EXIT_FAILURE);
 +
 +        default:
 +            abort();
 +        }
 +    }
 +    free(short_options);
 +}
 +
 +static void
 +usage(void)
 +{
 +    printf("%s: Open vSwitch database test utility\n"
 +           "usage: %s [OPTIONS] COMMAND [ARG...]\n\n"
 +           "  log-io FILE FLAGS COMMAND...\n"
 +           "    open FILE with FLAGS, run COMMANDs\n"
 +           "  parse-atomic-type TYPE\n"
 +           "    parse TYPE as OVSDB atomic type, and re-serialize\n"
 +           "  parse-base-type TYPE\n"
 +           "    parse TYPE as OVSDB base type, and re-serialize\n"
 +           "  parse-type JSON\n"
 +           "    parse JSON as OVSDB type, and re-serialize\n"
 +           "  parse-atoms TYPE ATOM...\n"
 +           "    parse JSON ATOMs as atoms of TYPE, and re-serialize\n"
 +           "  parse-atom-strings TYPE ATOM...\n"
 +           "    parse string ATOMs as atoms of given TYPE, and re-serialize\n"
 +           "  sort-atoms TYPE ATOM...\n"
 +           "    print JSON ATOMs in sorted order\n"
 +           "  parse-data TYPE DATUM...\n"
 +           "    parse JSON DATUMs as data of given TYPE, and re-serialize\n"
 +           "  parse-data-strings TYPE DATUM...\n"
 +           "    parse string DATUMs as data of given TYPE, and re-serialize\n"
 +           "  parse-column NAME OBJECT\n"
 +           "    parse column NAME with info OBJECT, and re-serialize\n"
 +           "  parse-table NAME OBJECT\n"
 +           "    parse table NAME with info OBJECT\n"
 +           "  parse-row TABLE ROW..., and re-serialize\n"
 +           "    parse each ROW of defined TABLE\n"
 +           "  compare-row TABLE ROW...\n"
 +           "    mutually compare all of the ROWs, print those that are equal\n"
 +           "  parse-conditions TABLE CONDITION...\n"
 +           "    parse each CONDITION on TABLE, and re-serialize\n"
 +           "  evaluate-conditions TABLE [CONDITION,...] [ROW,...]\n"
 +           "    test CONDITIONS on TABLE against each ROW, print results\n"
 +           "  parse-mutations TABLE MUTATION...\n"
 +           "    parse each MUTATION on TABLE, and re-serialize\n"
 +           "  execute-mutations TABLE [MUTATION,...] [ROW,...]\n"
 +           "    execute MUTATIONS on TABLE on each ROW, print results\n"
 +           "  query TABLE [ROW,...] [CONDITION,...]\n"
 +           "    add each ROW to TABLE, then query and print the rows that\n"
 +           "    satisfy each CONDITION.\n"
 +           "  query-distinct TABLE [ROW,...] [CONDITION,...] COLUMNS\n"
 +           "    add each ROW to TABLE, then query and print the rows that\n"
 +           "    satisfy each CONDITION and have distinct COLUMNS.\n"
 +           "  parse-schema JSON\n"
 +           "    parse JSON as an OVSDB schema, and re-serialize\n"
 +           "  transact COMMAND\n"
 +           "    execute each specified transactional COMMAND:\n"
 +           "      commit\n"
 +           "      abort\n"
 +           "      insert UUID I J\n"
 +           "      delete UUID\n"
 +           "      modify UUID I J\n"
 +           "      print\n"
 +           "  execute SCHEMA TRANSACTION...\n"
 +           "    executes each TRANSACTION on an initially empty database\n"
 +           "    the specified SCHEMA\n"
 +           "  trigger SCHEMA TRANSACTION...\n"
 +           "    executes each TRANSACTION on an initially empty database\n"
 +           "    the specified SCHEMA.   A TRANSACTION of the form\n"
 +           "    [\"advance\", NUMBER] advances NUMBER milliseconds in\n"
 +           "    simulated time, for causing triggers to time out.\n"
 +           "  idl SERVER [TRANSACTION...]\n"
 +           "    connect to SERVER and dump the contents of the database\n"
 +           "    as seen initially by the IDL implementation and after\n"
 +           "    executing each TRANSACTION.  (Each TRANSACTION must modify\n"
 +           "    the database or this command will hang.)\n",
 +           program_name, program_name);
 +    vlog_usage();
 +    printf("\nOther options:\n"
 +           "  -t, --timeout=SECS          give up after SECS seconds\n"
 +           "  -h, --help                  display this help message\n");
 +    exit(EXIT_SUCCESS);
 +}
 +\f
 +/* Command helper functions. */
 +
 +static struct json *
 +parse_json(const char *s)
 +{
 +    struct json *json = json_from_string(s);
 +    if (json->type == JSON_STRING) {
 +        ovs_fatal(0, "\"%s\": %s", s, json->u.string);
 +    }
 +    return json;
 +}
 +
 +static struct json *
 +unbox_json(struct json *json)
 +{
 +    if (json->type == JSON_ARRAY && json->u.array.n == 1) {
 +        struct json *inner = json->u.array.elems[0];
 +        json->u.array.elems[0] = NULL;
 +        json_destroy(json);
 +        return inner;
 +    } else {
 +        return json;
 +    }
 +}
 +
 +static void
 +print_and_free_json(struct json *json)
 +{
 +    char *string = json_to_string(json, JSSF_SORT);
 +    json_destroy(json);
 +    puts(string);
 +    free(string);
 +}
 +
 +static void
 +print_and_free_ovsdb_error(struct ovsdb_error *error)
 +{
 +    char *string = ovsdb_error_to_string(error);
 +    ovsdb_error_destroy(error);
 +    puts(string);
 +    free(string);
 +}
 +
 +static void
 +check_ovsdb_error(struct ovsdb_error *error)
 +{
 +    if (error) {
 +        char *s = ovsdb_error_to_string(error);
 +        ovsdb_error_destroy(error);
 +        ovs_fatal(0, "%s", s);
 +    }
 +}
 +
 +static void
 +die_if_error(char *error)
 +{
 +    if (error) {
 +        ovs_fatal(0, "%s", error);
 +    }
 +}
 +\f
 +/* Command implementations. */
 +
 +static void
 +do_log_io(int argc, char *argv[])
 +{
 +    const char *name = argv[1];
 +    char *mode = argv[2];
 +
 +    struct ovsdb_error *error;
 +    struct ovsdb_log *log;
 +    char *save_ptr = NULL;
 +    const char *token;
 +    int flags;
 +    int i;
 +
 +    for (flags = 0, token = strtok_r(mode, " |", &save_ptr); token != NULL;
 +         token = strtok_r(NULL, " |", &save_ptr))
 +    {
 +        if (!strcmp(token, "O_RDONLY")) {
 +            flags |= O_RDONLY;
 +        } else if (!strcmp(token, "O_RDWR")) {
 +            flags |= O_RDWR;
 +        } else if (!strcmp(token, "O_TRUNC")) {
 +            flags |= O_TRUNC;
 +        } else if (!strcmp(token, "O_CREAT")) {
 +            flags |= O_CREAT;
 +        } else if (!strcmp(token, "O_EXCL")) {
 +            flags |= O_EXCL;
 +        } else if (!strcmp(token, "O_TRUNC")) {
 +            flags |= O_TRUNC;
 +        }
 +    }
 +
 +    check_ovsdb_error(ovsdb_log_open(name, flags, &log));
 +    printf("%s: open successful\n", name);
 +
 +    for (i = 3; i < argc; i++) {
 +        const char *command = argv[i];
 +        if (!strcmp(command, "read")) {
 +            struct json *json;
 +
 +            error = ovsdb_log_read(log, &json);
 +            if (!error) {
 +                printf("%s: read: ", name);
 +                if (json) {
 +                    print_and_free_json(json);
 +                } else {
 +                    printf("end of log\n");
 +                }
 +                continue;
 +            }
 +        } else if (!strncmp(command, "write:", 6)) {
 +            struct json *json = parse_json(command + 6);
 +            error = ovsdb_log_write(log, json);
 +            json_destroy(json);
 +        } else if (!strcmp(command, "commit")) {
 +            error = ovsdb_log_commit(log);
 +        } else {
 +            ovs_fatal(0, "unknown log-io command \"%s\"", command);
 +        }
 +        if (error) {
 +            char *s = ovsdb_error_to_string(error);
 +            printf("%s: %s failed: %s\n", name, command, s);
 +            free(s);
 +            ovsdb_error_destroy(error);
 +        } else {
 +            printf("%s: %s successful\n", name, command);
 +        }
 +    }
 +
 +    ovsdb_log_close(log);
 +}
 +
 +static void
- do_parse_base_type(int argc UNUSED, char *argv[])
++do_parse_atomic_type(int argc OVS_UNUSED, char *argv[])
 +{
 +    enum ovsdb_atomic_type type;
 +    struct json *json;
 +
 +    json = unbox_json(parse_json(argv[1]));
 +    check_ovsdb_error(ovsdb_atomic_type_from_json(&type, json));
 +    json_destroy(json);
 +    print_and_free_json(ovsdb_atomic_type_to_json(type));
 +}
 +
 +static void
- do_parse_type(int argc UNUSED, char *argv[])
++do_parse_base_type(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct ovsdb_base_type base;
 +    struct json *json;
 +
 +    json = unbox_json(parse_json(argv[1]));
 +    check_ovsdb_error(ovsdb_base_type_from_json(&base, json));
 +    json_destroy(json);
 +    print_and_free_json(ovsdb_base_type_to_json(&base));
 +    ovsdb_base_type_destroy(&base);
 +}
 +
 +static void
- do_sort_atoms(int argc UNUSED, char *argv[])
++do_parse_type(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct ovsdb_type type;
 +    struct json *json;
 +
 +    json = unbox_json(parse_json(argv[1]));
 +    check_ovsdb_error(ovsdb_type_from_json(&type, json));
 +    json_destroy(json);
 +    print_and_free_json(ovsdb_type_to_json(&type));
 +    ovsdb_type_destroy(&type);
 +}
 +
 +static void
 +do_parse_atoms(int argc, char *argv[])
 +{
 +    struct ovsdb_base_type base;
 +    struct json *json;
 +    int i;
 +
 +    json = unbox_json(parse_json(argv[1]));
 +    check_ovsdb_error(ovsdb_base_type_from_json(&base, json));
 +    json_destroy(json);
 +
 +    for (i = 2; i < argc; i++) {
 +        struct ovsdb_error *error;
 +        union ovsdb_atom atom;
 +
 +        json = unbox_json(parse_json(argv[i]));
 +        error = ovsdb_atom_from_json(&atom, &base, json, NULL);
 +        json_destroy(json);
 +
 +        if (error) {
 +            print_and_free_ovsdb_error(error);
 +        } else {
 +            print_and_free_json(ovsdb_atom_to_json(&atom, base.type));
 +            ovsdb_atom_destroy(&atom, base.type);
 +        }
 +    }
 +    ovsdb_base_type_destroy(&base);
 +}
 +
 +static void
 +do_parse_atom_strings(int argc, char *argv[])
 +{
 +    struct ovsdb_base_type base;
 +    struct json *json;
 +    int i;
 +
 +    json = unbox_json(parse_json(argv[1]));
 +    check_ovsdb_error(ovsdb_base_type_from_json(&base, json));
 +    json_destroy(json);
 +
 +    for (i = 2; i < argc; i++) {
 +        union ovsdb_atom atom;
 +        struct ds out;
 +
 +        die_if_error(ovsdb_atom_from_string(&atom, &base, argv[i]));
 +
 +        ds_init(&out);
 +        ovsdb_atom_to_string(&atom, base.type, &out);
 +        puts(ds_cstr(&out));
 +        ds_destroy(&out);
 +
 +        ovsdb_atom_destroy(&atom, base.type);
 +    }
 +    ovsdb_base_type_destroy(&base);
 +}
 +
 +static void
 +do_parse_data(int argc, char *argv[])
 +{
 +    struct ovsdb_type type;
 +    struct json *json;
 +    int i;
 +
 +    json = unbox_json(parse_json(argv[1]));
 +    check_ovsdb_error(ovsdb_type_from_json(&type, json));
 +    json_destroy(json);
 +
 +    for (i = 2; i < argc; i++) {
 +        struct ovsdb_datum datum;
 +
 +        json = unbox_json(parse_json(argv[i]));
 +        check_ovsdb_error(ovsdb_datum_from_json(&datum, &type, json, NULL));
 +        json_destroy(json);
 +
 +        print_and_free_json(ovsdb_datum_to_json(&datum, &type));
 +
 +        ovsdb_datum_destroy(&datum, &type);
 +    }
 +    ovsdb_type_destroy(&type);
 +}
 +
 +static void
 +do_parse_data_strings(int argc, char *argv[])
 +{
 +    struct ovsdb_type type;
 +    struct json *json;
 +    int i;
 +
 +    json = unbox_json(parse_json(argv[1]));
 +    check_ovsdb_error(ovsdb_type_from_json(&type, json));
 +    json_destroy(json);
 +
 +    for (i = 2; i < argc; i++) {
 +        struct ovsdb_datum datum;
 +        struct ds out;
 +
 +        die_if_error(ovsdb_datum_from_string(&datum, &type, argv[i]));
 +
 +        ds_init(&out);
 +        ovsdb_datum_to_string(&datum, &type, &out);
 +        puts(ds_cstr(&out));
 +        ds_destroy(&out);
 +
 +        ovsdb_datum_destroy(&datum, &type);
 +    }
 +    ovsdb_type_destroy(&type);
 +}
 +
 +static enum ovsdb_atomic_type compare_atoms_atomic_type;
 +
 +static int
 +compare_atoms(const void *a_, const void *b_)
 +{
 +    const union ovsdb_atom *a = a_;
 +    const union ovsdb_atom *b = b_;
 +
 +    return ovsdb_atom_compare_3way(a, b, compare_atoms_atomic_type);
 +}
 +
 +static void
- do_parse_column(int argc UNUSED, char *argv[])
++do_sort_atoms(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct ovsdb_base_type base;
 +    union ovsdb_atom *atoms;
 +    struct json *json, **json_atoms;
 +    size_t n_atoms;
 +    int i;
 +
 +    json = unbox_json(parse_json(argv[1]));
 +    check_ovsdb_error(ovsdb_base_type_from_json(&base, json));
 +    json_destroy(json);
 +
 +    json = unbox_json(parse_json(argv[2]));
 +    if (json->type != JSON_ARRAY) {
 +        ovs_fatal(0, "second argument must be array");
 +    }
 +
 +    /* Convert JSON atoms to internal representation. */
 +    n_atoms = json->u.array.n;
 +    atoms = xmalloc(n_atoms * sizeof *atoms);
 +    for (i = 0; i < n_atoms; i++) {
 +        check_ovsdb_error(ovsdb_atom_from_json(&atoms[i], &base,
 +                                               json->u.array.elems[i], NULL));
 +    }
 +    json_destroy(json);
 +
 +    /* Sort atoms. */
 +    compare_atoms_atomic_type = base.type;
 +    qsort(atoms, n_atoms, sizeof *atoms, compare_atoms);
 +
 +    /* Convert internal representation back to JSON. */
 +    json_atoms = xmalloc(n_atoms * sizeof *json_atoms);
 +    for (i = 0; i < n_atoms; i++) {
 +        json_atoms[i] = ovsdb_atom_to_json(&atoms[i], base.type);
 +        ovsdb_atom_destroy(&atoms[i], base.type);
 +    }
 +    print_and_free_json(json_array_create(json_atoms, n_atoms));
 +    free(atoms);
 +    ovsdb_base_type_destroy(&base);
 +}
 +
 +static void
- do_parse_table(int argc UNUSED, char *argv[])
++do_parse_column(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct ovsdb_column *column;
 +    struct json *json;
 +
 +    json = parse_json(argv[2]);
 +    check_ovsdb_error(ovsdb_column_from_json(json, argv[1], &column));
 +    json_destroy(json);
 +    print_and_free_json(ovsdb_column_to_json(column));
 +    ovsdb_column_destroy(column);
 +}
 +
 +static void
- do_evaluate_conditions(int argc UNUSED, char *argv[])
++do_parse_table(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct ovsdb_table_schema *ts;
 +    struct json *json;
 +
 +    json = parse_json(argv[2]);
 +    check_ovsdb_error(ovsdb_table_schema_from_json(json, argv[1], &ts));
 +    json_destroy(json);
 +    print_and_free_json(ovsdb_table_schema_to_json(ts));
 +    ovsdb_table_schema_destroy(ts);
 +}
 +
 +static void
 +do_parse_rows(int argc, char *argv[])
 +{
 +    struct ovsdb_column_set all_columns;
 +    struct ovsdb_table_schema *ts;
 +    struct ovsdb_table *table;
 +    struct json *json;
 +    int i;
 +
 +    json = unbox_json(parse_json(argv[1]));
 +    check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts));
 +    json_destroy(json);
 +
 +    table = ovsdb_table_create(ts);
 +    ovsdb_column_set_init(&all_columns);
 +    ovsdb_column_set_add_all(&all_columns, table);
 +
 +    for (i = 2; i < argc; i++) {
 +        struct ovsdb_column_set columns;
 +        struct ovsdb_row *row;
 +
 +        ovsdb_column_set_init(&columns);
 +        row = ovsdb_row_create(table);
 +
 +        json = unbox_json(parse_json(argv[i]));
 +        check_ovsdb_error(ovsdb_row_from_json(row, json, NULL, &columns));
 +        json_destroy(json);
 +
 +        print_and_free_json(ovsdb_row_to_json(row, &all_columns));
 +
 +        if (columns.n_columns) {
 +            struct svec names;
 +            size_t j;
 +            char *s;
 +
 +            svec_init(&names);
 +            for (j = 0; j < columns.n_columns; j++) {
 +                svec_add(&names, columns.columns[j]->name);
 +            }
 +            svec_sort(&names);
 +            s = svec_join(&names, ", ", "");
 +            puts(s);
 +            free(s);
 +            svec_destroy(&names);
 +        } else {
 +            printf("<none>\n");
 +        }
 +
 +        ovsdb_column_set_destroy(&columns);
 +        ovsdb_row_destroy(row);
 +    }
 +
 +    ovsdb_column_set_destroy(&all_columns);
 +    ovsdb_table_destroy(table); /* Also destroys 'ts'. */
 +}
 +
 +static void
 +do_compare_rows(int argc, char *argv[])
 +{
 +    struct ovsdb_column_set all_columns;
 +    struct ovsdb_table_schema *ts;
 +    struct ovsdb_table *table;
 +    struct ovsdb_row **rows;
 +    struct json *json;
 +    char **names;
 +    int n_rows;
 +    int i, j;
 +
 +    json = unbox_json(parse_json(argv[1]));
 +    check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts));
 +    json_destroy(json);
 +
 +    table = ovsdb_table_create(ts);
 +    ovsdb_column_set_init(&all_columns);
 +    ovsdb_column_set_add_all(&all_columns, table);
 +
 +    n_rows = argc - 2;
 +    rows = xmalloc(sizeof *rows * n_rows);
 +    names = xmalloc(sizeof *names * n_rows);
 +    for (i = 0; i < n_rows; i++) {
 +        rows[i] = ovsdb_row_create(table);
 +
 +        json = parse_json(argv[i + 2]);
 +        if (json->type != JSON_ARRAY || json->u.array.n != 2
 +            || json->u.array.elems[0]->type != JSON_STRING) {
 +            ovs_fatal(0, "\"%s\" does not have expected form "
 +                      "[\"name\", {data}]", argv[i]);
 +        }
 +        names[i] = xstrdup(json->u.array.elems[0]->u.string);
 +        check_ovsdb_error(ovsdb_row_from_json(rows[i], json->u.array.elems[1],
 +                                              NULL, NULL));
 +        json_destroy(json);
 +    }
 +    for (i = 0; i < n_rows; i++) {
 +        uint32_t i_hash = ovsdb_row_hash_columns(rows[i], &all_columns, 0);
 +        for (j = i + 1; j < n_rows; j++) {
 +            uint32_t j_hash = ovsdb_row_hash_columns(rows[j], &all_columns, 0);
 +            if (ovsdb_row_equal_columns(rows[i], rows[j], &all_columns)) {
 +                printf("%s == %s\n", names[i], names[j]);
 +                if (i_hash != j_hash) {
 +                    printf("but hash(%s) != hash(%s)\n", names[i], names[j]);
 +                    abort();
 +                }
 +            } else if (i_hash == j_hash) {
 +                printf("hash(%s) == hash(%s)\n", names[i], names[j]);
 +            }
 +        }
 +    }
 +    for (i = 0; i < n_rows; i++) {
 +        ovsdb_row_destroy(rows[i]);
 +        free(names[i]);
 +    }
 +    free(rows);
 +    free(names);
 +
 +    ovsdb_column_set_destroy(&all_columns);
 +    ovsdb_table_destroy(table); /* Also destroys 'ts'. */
 +}
 +
 +static void
 +do_parse_conditions(int argc, char *argv[])
 +{
 +    struct ovsdb_table_schema *ts;
 +    struct json *json;
 +    int exit_code = 0;
 +    int i;
 +
 +    json = unbox_json(parse_json(argv[1]));
 +    check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts));
 +    json_destroy(json);
 +
 +    for (i = 2; i < argc; i++) {
 +        struct ovsdb_condition cnd;
 +        struct ovsdb_error *error;
 +
 +        json = parse_json(argv[i]);
 +        error = ovsdb_condition_from_json(ts, json, NULL, &cnd);
 +        if (!error) {
 +            print_and_free_json(ovsdb_condition_to_json(&cnd));
 +        } else {
 +            char *s = ovsdb_error_to_string(error);
 +            ovs_error(0, "%s", s);
 +            free(s);
 +            ovsdb_error_destroy(error);
 +            exit_code = 1;
 +        }
 +        json_destroy(json);
 +
 +        ovsdb_condition_destroy(&cnd);
 +    }
 +    ovsdb_table_schema_destroy(ts);
 +
 +    exit(exit_code);
 +}
 +
 +static void
- do_execute_mutations(int argc UNUSED, char *argv[])
++do_evaluate_conditions(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct ovsdb_table_schema *ts;
 +    struct ovsdb_table *table;
 +    struct ovsdb_condition *conditions;
 +    size_t n_conditions;
 +    struct ovsdb_row **rows;
 +    size_t n_rows;
 +    struct json *json;
 +    size_t i, j;
 +
 +    /* Parse table schema, create table. */
 +    json = unbox_json(parse_json(argv[1]));
 +    check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts));
 +    json_destroy(json);
 +
 +    table = ovsdb_table_create(ts);
 +
 +    /* Parse conditions. */
 +    json = parse_json(argv[2]);
 +    if (json->type != JSON_ARRAY) {
 +        ovs_fatal(0, "CONDITION argument is not JSON array");
 +    }
 +    n_conditions = json->u.array.n;
 +    conditions = xmalloc(n_conditions * sizeof *conditions);
 +    for (i = 0; i < n_conditions; i++) {
 +        check_ovsdb_error(ovsdb_condition_from_json(ts, json->u.array.elems[i],
 +                                                    NULL, &conditions[i]));
 +    }
 +    json_destroy(json);
 +
 +    /* Parse rows. */
 +    json = parse_json(argv[3]);
 +    if (json->type != JSON_ARRAY) {
 +        ovs_fatal(0, "ROW argument is not JSON array");
 +    }
 +    n_rows = json->u.array.n;
 +    rows = xmalloc(n_rows * sizeof *rows);
 +    for (i = 0; i < n_rows; i++) {
 +        rows[i] = ovsdb_row_create(table);
 +        check_ovsdb_error(ovsdb_row_from_json(rows[i], json->u.array.elems[i],
 +                                              NULL, NULL));
 +    }
 +    json_destroy(json);
 +
 +    for (i = 0; i < n_conditions; i++) {
 +        printf("condition %2d:", i);
 +        for (j = 0; j < n_rows; j++) {
 +            bool result = ovsdb_condition_evaluate(rows[j], &conditions[i]);
 +            if (j % 5 == 0) {
 +                putchar(' ');
 +            }
 +            putchar(result ? 'T' : '-');
 +        }
 +        printf("\n");
 +    }
 +
 +    for (i = 0; i < n_conditions; i++) {
 +        ovsdb_condition_destroy(&conditions[i]);
 +    }
 +    free(conditions);
 +    for (i = 0; i < n_rows; i++) {
 +        ovsdb_row_destroy(rows[i]);
 +    }
 +    free(rows);
 +    ovsdb_table_destroy(table); /* Also destroys 'ts'. */
 +}
 +
 +static void
 +do_parse_mutations(int argc, char *argv[])
 +{
 +    struct ovsdb_table_schema *ts;
 +    struct json *json;
 +    int exit_code = 0;
 +    int i;
 +
 +    json = unbox_json(parse_json(argv[1]));
 +    check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts));
 +    json_destroy(json);
 +
 +    for (i = 2; i < argc; i++) {
 +        struct ovsdb_mutation_set set;
 +        struct ovsdb_error *error;
 +
 +        json = parse_json(argv[i]);
 +        error = ovsdb_mutation_set_from_json(ts, json, NULL, &set);
 +        if (!error) {
 +            print_and_free_json(ovsdb_mutation_set_to_json(&set));
 +        } else {
 +            char *s = ovsdb_error_to_string(error);
 +            ovs_error(0, "%s", s);
 +            free(s);
 +            ovsdb_error_destroy(error);
 +            exit_code = 1;
 +        }
 +        json_destroy(json);
 +
 +        ovsdb_mutation_set_destroy(&set);
 +    }
 +    ovsdb_table_schema_destroy(ts);
 +
 +    exit(exit_code);
 +}
 +
 +static void
- do_query(int argc UNUSED, char *argv[])
++do_execute_mutations(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct ovsdb_table_schema *ts;
 +    struct ovsdb_table *table;
 +    struct ovsdb_mutation_set *sets;
 +    size_t n_sets;
 +    struct ovsdb_row **rows;
 +    size_t n_rows;
 +    struct json *json;
 +    size_t i, j;
 +
 +    /* Parse table schema, create table. */
 +    json = unbox_json(parse_json(argv[1]));
 +    check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts));
 +    json_destroy(json);
 +
 +    table = ovsdb_table_create(ts);
 +
 +    /* Parse mutations. */
 +    json = parse_json(argv[2]);
 +    if (json->type != JSON_ARRAY) {
 +        ovs_fatal(0, "MUTATION argument is not JSON array");
 +    }
 +    n_sets = json->u.array.n;
 +    sets = xmalloc(n_sets * sizeof *sets);
 +    for (i = 0; i < n_sets; i++) {
 +        check_ovsdb_error(ovsdb_mutation_set_from_json(ts,
 +                                                       json->u.array.elems[i],
 +                                                       NULL, &sets[i]));
 +    }
 +    json_destroy(json);
 +
 +    /* Parse rows. */
 +    json = parse_json(argv[3]);
 +    if (json->type != JSON_ARRAY) {
 +        ovs_fatal(0, "ROW argument is not JSON array");
 +    }
 +    n_rows = json->u.array.n;
 +    rows = xmalloc(n_rows * sizeof *rows);
 +    for (i = 0; i < n_rows; i++) {
 +        rows[i] = ovsdb_row_create(table);
 +        check_ovsdb_error(ovsdb_row_from_json(rows[i], json->u.array.elems[i],
 +                                              NULL, NULL));
 +    }
 +    json_destroy(json);
 +
 +    for (i = 0; i < n_sets; i++) {
 +        printf("mutation %2d:\n", i);
 +        for (j = 0; j < n_rows; j++) {
 +            struct ovsdb_error *error;
 +            struct ovsdb_row *row;
 +
 +            row = ovsdb_row_clone(rows[j]);
 +            error = ovsdb_mutation_set_execute(row, &sets[i]);
 +
 +            printf("row %zu: ", j);
 +            if (error) {
 +                print_and_free_ovsdb_error(error);
 +            } else {
 +                struct ovsdb_column_set columns;
 +                struct shash_node *node;
 +
 +                ovsdb_column_set_init(&columns);
 +                SHASH_FOR_EACH (node, &ts->columns) {
 +                    struct ovsdb_column *c = node->data;
 +                    if (!ovsdb_datum_equals(&row->fields[c->index],
 +                                            &rows[j]->fields[c->index],
 +                                            &c->type)) {
 +                        ovsdb_column_set_add(&columns, c);
 +                    }
 +                }
 +                if (columns.n_columns) {
 +                    print_and_free_json(ovsdb_row_to_json(row, &columns));
 +                } else {
 +                    printf("no change\n");
 +                }
 +                ovsdb_column_set_destroy(&columns);
 +            }
 +            ovsdb_row_destroy(row);
 +        }
 +        printf("\n");
 +    }
 +
 +    for (i = 0; i < n_sets; i++) {
 +        ovsdb_mutation_set_destroy(&sets[i]);
 +    }
 +    free(sets);
 +    for (i = 0; i < n_rows; i++) {
 +        ovsdb_row_destroy(rows[i]);
 +    }
 +    free(rows);
 +    ovsdb_table_destroy(table); /* Also destroys 'ts'. */
 +}
 +
 +struct do_query_cbdata {
 +    struct uuid *row_uuids;
 +    int *counts;
 +    size_t n_rows;
 +};
 +
 +static bool
 +do_query_cb(const struct ovsdb_row *row, void *cbdata_)
 +{
 +    struct do_query_cbdata *cbdata = cbdata_;
 +    size_t i;
 +
 +    for (i = 0; i < cbdata->n_rows; i++) {
 +        if (uuid_equals(ovsdb_row_get_uuid(row), &cbdata->row_uuids[i])) {
 +            cbdata->counts[i]++;
 +        }
 +    }
 +
 +    return true;
 +}
 +
 +static void
- do_query_distinct(int argc UNUSED, char *argv[])
++do_query(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct do_query_cbdata cbdata;
 +    struct ovsdb_table_schema *ts;
 +    struct ovsdb_table *table;
 +    struct json *json;
 +    int exit_code = 0;
 +    size_t i;
 +
 +    /* Parse table schema, create table. */
 +    json = unbox_json(parse_json(argv[1]));
 +    check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts));
 +    json_destroy(json);
 +
 +    table = ovsdb_table_create(ts);
 +
 +    /* Parse rows, add to table. */
 +    json = parse_json(argv[2]);
 +    if (json->type != JSON_ARRAY) {
 +        ovs_fatal(0, "ROW argument is not JSON array");
 +    }
 +    cbdata.n_rows = json->u.array.n;
 +    cbdata.row_uuids = xmalloc(cbdata.n_rows * sizeof *cbdata.row_uuids);
 +    cbdata.counts = xmalloc(cbdata.n_rows * sizeof *cbdata.counts);
 +    for (i = 0; i < cbdata.n_rows; i++) {
 +        struct ovsdb_row *row = ovsdb_row_create(table);
 +        uuid_generate(ovsdb_row_get_uuid_rw(row));
 +        check_ovsdb_error(ovsdb_row_from_json(row, json->u.array.elems[i],
 +                                              NULL, NULL));
 +        if (ovsdb_table_get_row(table, ovsdb_row_get_uuid(row))) {
 +            ovs_fatal(0, "duplicate UUID "UUID_FMT" in table",
 +                      UUID_ARGS(ovsdb_row_get_uuid(row)));
 +        }
 +        cbdata.row_uuids[i] = *ovsdb_row_get_uuid(row);
 +        ovsdb_table_put_row(table, row);
 +    }
 +    json_destroy(json);
 +
 +    /* Parse conditions and execute queries. */
 +    json = parse_json(argv[3]);
 +    if (json->type != JSON_ARRAY) {
 +        ovs_fatal(0, "CONDITION argument is not JSON array");
 +    }
 +    for (i = 0; i < json->u.array.n; i++) {
 +        struct ovsdb_condition cnd;
 +        size_t j;
 +
 +        check_ovsdb_error(ovsdb_condition_from_json(ts, json->u.array.elems[i],
 +                                                    NULL, &cnd));
 +
 +        memset(cbdata.counts, 0, cbdata.n_rows * sizeof *cbdata.counts);
 +        ovsdb_query(table, &cnd, do_query_cb, &cbdata);
 +
 +        printf("query %2d:", i);
 +        for (j = 0; j < cbdata.n_rows; j++) {
 +            if (j % 5 == 0) {
 +                putchar(' ');
 +            }
 +            if (cbdata.counts[j]) {
 +                printf("%d", cbdata.counts[j]);
 +                if (cbdata.counts[j] > 1) {
 +                    /* Dup! */
 +                    exit_code = 1;
 +                }
 +            } else {
 +                putchar('-');
 +            }
 +        }
 +        putchar('\n');
 +
 +        ovsdb_condition_destroy(&cnd);
 +    }
 +    json_destroy(json);
 +
 +    ovsdb_table_destroy(table); /* Also destroys 'ts'. */
 +
 +    exit(exit_code);
 +}
 +
 +struct do_query_distinct_class {
 +    struct ovsdb_row *example;
 +    int count;
 +};
 +
 +struct do_query_distinct_row {
 +    struct uuid uuid;
 +    struct do_query_distinct_class *class;
 +};
 +
 +static void
- do_parse_schema(int argc UNUSED, char *argv[])
++do_query_distinct(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct ovsdb_column_set columns;
 +    struct ovsdb_table_schema *ts;
 +    struct ovsdb_table *table;
 +    struct do_query_distinct_row *rows;
 +    size_t n_rows;
 +    struct do_query_distinct_class *classes;
 +    size_t n_classes;
 +    struct json *json;
 +    int exit_code = 0;
 +    size_t i, j, k;
 +
 +    /* Parse table schema, create table. */
 +    json = unbox_json(parse_json(argv[1]));
 +    check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts));
 +    json_destroy(json);
 +
 +    table = ovsdb_table_create(ts);
 +
 +    /* Parse column set. */
 +    json = parse_json(argv[4]);
 +    check_ovsdb_error(ovsdb_column_set_from_json(json, table, &columns));
 +    json_destroy(json);
 +
 +    /* Parse rows, add to table. */
 +    json = parse_json(argv[2]);
 +    if (json->type != JSON_ARRAY) {
 +        ovs_fatal(0, "ROW argument is not JSON array");
 +    }
 +    n_rows = json->u.array.n;
 +    rows = xmalloc(n_rows * sizeof *rows);
 +    classes = xmalloc(n_rows * sizeof *classes);
 +    n_classes = 0;
 +    for (i = 0; i < n_rows; i++) {
 +        struct ovsdb_row *row;
 +        size_t j;
 +
 +        /* Parse row. */
 +        row = ovsdb_row_create(table);
 +        uuid_generate(ovsdb_row_get_uuid_rw(row));
 +        check_ovsdb_error(ovsdb_row_from_json(row, json->u.array.elems[i],
 +                                              NULL, NULL));
 +
 +        /* Initialize row and find equivalence class. */
 +        rows[i].uuid = *ovsdb_row_get_uuid(row);
 +        rows[i].class = NULL;
 +        for (j = 0; j < n_classes; j++) {
 +            if (ovsdb_row_equal_columns(row, classes[j].example, &columns)) {
 +                rows[i].class = &classes[j];
 +                break;
 +            }
 +        }
 +        if (!rows[i].class) {
 +            rows[i].class = &classes[n_classes];
 +            classes[n_classes].example = ovsdb_row_clone(row);
 +            n_classes++;
 +        }
 +
 +        /* Add row to table. */
 +        if (ovsdb_table_get_row(table, ovsdb_row_get_uuid(row))) {
 +            ovs_fatal(0, "duplicate UUID "UUID_FMT" in table",
 +                      UUID_ARGS(ovsdb_row_get_uuid(row)));
 +        }
 +        ovsdb_table_put_row(table, row);
 +
 +    }
 +    json_destroy(json);
 +
 +    /* Parse conditions and execute queries. */
 +    json = parse_json(argv[3]);
 +    if (json->type != JSON_ARRAY) {
 +        ovs_fatal(0, "CONDITION argument is not JSON array");
 +    }
 +    for (i = 0; i < json->u.array.n; i++) {
 +        struct ovsdb_row_set results;
 +        struct ovsdb_condition cnd;
 +
 +        check_ovsdb_error(ovsdb_condition_from_json(ts, json->u.array.elems[i],
 +                                                    NULL, &cnd));
 +
 +        for (j = 0; j < n_classes; j++) {
 +            classes[j].count = 0;
 +        }
 +        ovsdb_row_set_init(&results);
 +        ovsdb_query_distinct(table, &cnd, &columns, &results);
 +        for (j = 0; j < results.n_rows; j++) {
 +            for (k = 0; k < n_rows; k++) {
 +                if (uuid_equals(ovsdb_row_get_uuid(results.rows[j]),
 +                                &rows[k].uuid)) {
 +                    rows[k].class->count++;
 +                }
 +            }
 +        }
 +        ovsdb_row_set_destroy(&results);
 +
 +        printf("query %2d:", i);
 +        for (j = 0; j < n_rows; j++) {
 +            int count = rows[j].class->count;
 +
 +            if (j % 5 == 0) {
 +                putchar(' ');
 +            }
 +            if (count > 1) {
 +                /* Dup! */
 +                printf("%d", count);
 +                exit_code = 1;
 +            } else if (count == 1) {
 +                putchar("abcdefghijklmnopqrstuvwxyz"[rows[j].class - classes]);
 +            } else {
 +                putchar('-');
 +            }
 +        }
 +        putchar('\n');
 +
 +        ovsdb_condition_destroy(&cnd);
 +    }
 +    json_destroy(json);
 +
 +    ovsdb_table_destroy(table); /* Also destroys 'ts'. */
 +
 +    exit(exit_code);
 +}
 +
 +static void
- do_execute(int argc UNUSED, char *argv[])
++do_parse_schema(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct ovsdb_schema *schema;
 +    struct json *json;
 +
 +    json = parse_json(argv[1]);
 +    check_ovsdb_error(ovsdb_schema_from_json(json, &schema));
 +    json_destroy(json);
 +    print_and_free_json(ovsdb_schema_to_json(schema));
 +    ovsdb_schema_destroy(schema);
 +}
 +
 +static void
- do_trigger(int argc UNUSED, char *argv[])
++do_execute(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct ovsdb_schema *schema;
 +    struct json *json;
 +    struct ovsdb *db;
 +    int i;
 +
 +    /* Create database. */
 +    json = parse_json(argv[1]);
 +    check_ovsdb_error(ovsdb_schema_from_json(json, &schema));
 +    json_destroy(json);
 +    db = ovsdb_create(schema);
 +
 +    for (i = 2; i < argc; i++) {
 +        struct json *params, *result;
 +        char *s;
 +
 +        params = parse_json(argv[i]);
 +        result = ovsdb_execute(db, params, 0, NULL);
 +        s = json_to_string(result, JSSF_SORT);
 +        printf("%s\n", s);
 +        free(s);
 +        json_destroy(params);
 +        json_destroy(result);
 +    }
 +
 +    ovsdb_destroy(db);
 +}
 +
 +struct test_trigger {
 +    struct ovsdb_trigger trigger;
 +    int number;
 +};
 +
 +static void
 +do_trigger_dump(struct test_trigger *t, long long int now, const char *title)
 +{
 +    struct json *result;
 +    char *s;
 +
 +    result = ovsdb_trigger_steal_result(&t->trigger);
 +    s = json_to_string(result, JSSF_SORT);
 +    printf("t=%lld: trigger %d (%s): %s\n", now, t->number, title, s);
 +    free(s);
 +    json_destroy(result);
 +    ovsdb_trigger_destroy(&t->trigger);
 +    free(t);
 +}
 +
 +static void
- do_help(int argc UNUSED, char *argv[] UNUSED)
++do_trigger(int argc OVS_UNUSED, char *argv[])
 +{
 +    struct ovsdb_schema *schema;
 +    struct list completions;
 +    struct json *json;
 +    struct ovsdb *db;
 +    long long int now;
 +    int number;
 +    int i;
 +
 +    /* Create database. */
 +    json = parse_json(argv[1]);
 +    check_ovsdb_error(ovsdb_schema_from_json(json, &schema));
 +    json_destroy(json);
 +    db = ovsdb_create(schema);
 +
 +    list_init(&completions);
 +    now = 0;
 +    number = 0;
 +    for (i = 2; i < argc; i++) {
 +        struct json *params = parse_json(argv[i]);
 +        if (params->type == JSON_ARRAY
 +            && json_array(params)->n == 2
 +            && json_array(params)->elems[0]->type == JSON_STRING
 +            && !strcmp(json_string(json_array(params)->elems[0]), "advance")
 +            && json_array(params)->elems[1]->type == JSON_INTEGER) {
 +            now += json_integer(json_array(params)->elems[1]);
 +            json_destroy(params);
 +        } else {
 +            struct test_trigger *t = xmalloc(sizeof *t);
 +            ovsdb_trigger_init(db, &t->trigger, params, &completions, now);
 +            t->number = number++;
 +            if (ovsdb_trigger_is_complete(&t->trigger)) {
 +                do_trigger_dump(t, now, "immediate");
 +            } else {
 +                printf("t=%lld: new trigger %d\n", now, t->number);
 +            }
 +        }
 +
 +        ovsdb_trigger_run(db, now);
 +        while (!list_is_empty(&completions)) {
 +            do_trigger_dump(CONTAINER_OF(list_pop_front(&completions),
 +                                         struct test_trigger, trigger.node),
 +                            now, "delayed");
 +        }
 +
 +        ovsdb_trigger_wait(db, now);
 +        poll_immediate_wake();
 +        poll_block();
 +    }
 +
 +    ovsdb_destroy(db);
 +}
 +
 +static void
- do_transact_commit(int argc UNUSED, char *argv[] UNUSED)
++do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 +{
 +    usage();
 +}
 +\f
 +/* "transact" command. */
 +
 +static struct ovsdb *do_transact_db;
 +static struct ovsdb_txn *do_transact_txn;
 +static struct ovsdb_table *do_transact_table;
 +
 +static void
- do_transact_abort(int argc UNUSED, char *argv[] UNUSED)
++do_transact_commit(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 +{
 +    ovsdb_txn_commit(do_transact_txn, false);
 +    do_transact_txn = NULL;
 +}
 +
 +static void
- do_transact_insert(int argc UNUSED, char *argv[] UNUSED)
++do_transact_abort(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 +{
 +    ovsdb_txn_abort(do_transact_txn);
 +    do_transact_txn = NULL;
 +}
 +
 +static void
 +uuid_from_integer(int integer, struct uuid *uuid)
 +{
 +    uuid_zero(uuid);
 +    uuid->parts[3] = integer;
 +}
 +
 +static const struct ovsdb_row *
 +do_transact_find_row(const char *uuid_string)
 +{
 +    const struct ovsdb_row *row;
 +    struct uuid uuid;
 +
 +    uuid_from_integer(atoi(uuid_string), &uuid);
 +    row = ovsdb_table_get_row(do_transact_table, &uuid);
 +    if (!row) {
 +        ovs_fatal(0, "table does not contain row with UUID "UUID_FMT,
 +                  UUID_ARGS(&uuid));
 +    }
 +    return row;
 +}
 +
 +static void
 +do_transact_set_integer(struct ovsdb_row *row, const char *column_name,
 +                        int integer)
 +{
 +    if (integer != -1) {
 +        const struct ovsdb_column *column;
 +
 +        column = ovsdb_table_schema_get_column(do_transact_table->schema,
 +                                               column_name);
 +        row->fields[column->index].keys[0].integer = integer;
 +    }
 +}
 +
 +static int
 +do_transact_get_integer(const struct ovsdb_row *row, const char *column_name)
 +{
 +    const struct ovsdb_column *column;
 +
 +    column = ovsdb_table_schema_get_column(do_transact_table->schema,
 +                                           column_name);
 +    return row->fields[column->index].keys[0].integer;
 +}
 +
 +static void
 +do_transact_set_i_j(struct ovsdb_row *row,
 +                    const char *i_string, const char *j_string)
 +{
 +    do_transact_set_integer(row, "i", atoi(i_string));
 +    do_transact_set_integer(row, "j", atoi(j_string));
 +}
 +
 +static void
- do_transact_delete(int argc UNUSED, char *argv[] UNUSED)
++do_transact_insert(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 +{
 +    struct ovsdb_row *row;
 +    struct uuid *uuid;
 +
 +    row = ovsdb_row_create(do_transact_table);
 +
 +    /* Set UUID. */
 +    uuid = ovsdb_row_get_uuid_rw(row);
 +    uuid_from_integer(atoi(argv[1]), uuid);
 +    if (ovsdb_table_get_row(do_transact_table, uuid)) {
 +        ovs_fatal(0, "table already contains row with UUID "UUID_FMT,
 +                  UUID_ARGS(uuid));
 +    }
 +
 +    do_transact_set_i_j(row, argv[2], argv[3]);
 +
 +    /* Insert row. */
 +    ovsdb_txn_row_insert(do_transact_txn, row);
 +}
 +
 +static void
- do_transact_modify(int argc UNUSED, char *argv[] UNUSED)
++do_transact_delete(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 +{
 +    const struct ovsdb_row *row = do_transact_find_row(argv[1]);
 +    ovsdb_txn_row_delete(do_transact_txn, row);
 +}
 +
 +static void
- do_transact_print(int argc UNUSED, char *argv[] UNUSED)
++do_transact_modify(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 +{
 +    const struct ovsdb_row *row_ro;
 +    struct ovsdb_row *row_rw;
 +
 +    row_ro = do_transact_find_row(argv[1]);
 +    row_rw = ovsdb_txn_row_modify(do_transact_txn, row_ro);
 +    do_transact_set_i_j(row_rw, argv[2], argv[3]);
 +}
 +
 +static int
 +compare_rows_by_uuid(const void *a_, const void *b_)
 +{
 +    struct ovsdb_row *const *ap = a_;
 +    struct ovsdb_row *const *bp = b_;
 +
 +    return uuid_compare_3way(ovsdb_row_get_uuid(*ap), ovsdb_row_get_uuid(*bp));
 +}
 +
 +static void
++do_transact_print(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 +{
 +    const struct ovsdb_row **rows;
 +    const struct ovsdb_row *row;
 +    size_t n_rows;
 +    size_t i;
 +
 +    n_rows = hmap_count(&do_transact_table->rows);
 +    rows = xmalloc(n_rows * sizeof *rows);
 +    i = 0;
 +    HMAP_FOR_EACH (row, struct ovsdb_row, hmap_node,
 +                   &do_transact_table->rows) {
 +        rows[i++] = row;
 +    }
 +    assert(i == n_rows);
 +
 +    qsort(rows, n_rows, sizeof *rows, compare_rows_by_uuid);
 +
 +    for (i = 0; i < n_rows; i++) {
 +        printf("\n%"PRId32": i=%d, j=%d",
 +               ovsdb_row_get_uuid(rows[i])->parts[3],
 +               do_transact_get_integer(rows[i], "i"),
 +               do_transact_get_integer(rows[i], "j"));
 +    }
 +
 +    free(rows);
 +}
 +
 +static void
 +do_transact(int argc, char *argv[])
 +{
 +    static const struct command do_transact_commands[] = {
 +        { "commit", 0, 0, do_transact_commit },
 +        { "abort", 0, 0, do_transact_abort },
 +        { "insert", 2, 3, do_transact_insert },
 +        { "delete", 1, 1, do_transact_delete },
 +        { "modify", 2, 3, do_transact_modify },
 +        { "print", 0, 0, do_transact_print },
 +        { NULL, 0, 0, NULL },
 +    };
 +
 +    struct ovsdb_schema *schema;
 +    struct json *json;
 +    int i;
 +
 +    /* Create table. */
 +    json = parse_json("{\"name\": \"testdb\", "
 +                      " \"tables\": "
 +                      "  {\"mytable\": "
 +                      "    {\"columns\": "
 +                      "      {\"i\": {\"type\": \"integer\"}, "
 +                      "       \"j\": {\"type\": \"integer\"}}}}}");
 +    check_ovsdb_error(ovsdb_schema_from_json(json, &schema));
 +    json_destroy(json);
 +    do_transact_db = ovsdb_create(schema);
 +    do_transact_table = ovsdb_get_table(do_transact_db, "mytable");
 +    assert(do_transact_table != NULL);
 +
 +    for (i = 1; i < argc; i++) {
 +        struct json *command;
 +        size_t n_args;
 +        char **args;
 +        int j;
 +
 +        command = parse_json(argv[i]);
 +        if (command->type != JSON_ARRAY) {
 +            ovs_fatal(0, "transaction %d must be JSON array "
 +                      "with at least 1 element", i);
 +        }
 +
 +        n_args = command->u.array.n;
 +        args = xmalloc((n_args + 1) * sizeof *args);
 +        for (j = 0; j < n_args; j++) {
 +            struct json *s = command->u.array.elems[j];
 +            if (s->type != JSON_STRING) {
 +                ovs_fatal(0, "transaction %d argument %d must be JSON string",
 +                          i, j);
 +            }
 +            args[j] = xstrdup(json_string(s));
 +        }
 +        args[n_args] = NULL;
 +
 +        if (!do_transact_txn) {
 +            do_transact_txn = ovsdb_txn_create(do_transact_db);
 +        }
 +
 +        for (j = 0; j < n_args; j++) {
 +            if (j) {
 +                putchar(' ');
 +            }
 +            fputs(args[j], stdout);
 +        }
 +        fputs(":", stdout);
 +        run_command(n_args, args, do_transact_commands);
 +        putchar('\n');
 +
 +        for (j = 0; j < n_args; j++) {
 +            free(args[j]);
 +        }
 +        free(args);
 +        json_destroy(command);
 +    }
 +    ovsdb_txn_abort(do_transact_txn);
 +    ovsdb_destroy(do_transact_db); /* Also destroys 'schema'. */
 +}
 +
 +static int
 +compare_link1(const void *a_, const void *b_)
 +{
 +    const struct idltest_link1 *const *ap = a_;
 +    const struct idltest_link1 *const *bp = b_;
 +    const struct idltest_link1 *a = *ap;
 +    const struct idltest_link1 *b = *bp;
 +
 +    return a->i < b->i ? -1 : a->i > b->i;
 +}
 +
 +static void
 +print_idl(struct ovsdb_idl *idl, int step)
 +{
 +    const struct idltest_simple *s;
 +    const struct idltest_link1 *l1;
 +    const struct idltest_link2 *l2;
 +    int n = 0;
 +
 +    IDLTEST_SIMPLE_FOR_EACH (s, idl) {
 +        size_t i;
 +
 +        printf("%03d: i=%"PRId64" r=%g b=%s s=%s u="UUID_FMT" ia=[",
 +               step, s->i, s->r, s->b ? "true" : "false",
 +               s->s, UUID_ARGS(&s->u));
 +        for (i = 0; i < s->n_ia; i++) {
 +            printf("%s%"PRId64, i ? " " : "", s->ia[i]);
 +        }
 +        printf("] ra=[");
 +        for (i = 0; i < s->n_ra; i++) {
 +            printf("%s%g", i ? " " : "", s->ra[i]);
 +        }
 +        printf("] ba=[");
 +        for (i = 0; i < s->n_ba; i++) {
 +            printf("%s%s", i ? " " : "", s->ba[i] ? "true" : "false");
 +        }
 +        printf("] sa=[");
 +        for (i = 0; i < s->n_sa; i++) {
 +            printf("%s%s", i ? " " : "", s->sa[i]);
 +        }
 +        printf("] ua=[");
 +        for (i = 0; i < s->n_ua; i++) {
 +            printf("%s"UUID_FMT, i ? " " : "", UUID_ARGS(&s->ua[i]));
 +        }
 +        printf("] uuid="UUID_FMT"\n", UUID_ARGS(&s->header_.uuid));
 +        n++;
 +    }
 +    IDLTEST_LINK1_FOR_EACH (l1, idl) {
 +        struct idltest_link1 **links;
 +        size_t i;
 +
 +        printf("%03d: i=%"PRId64" k=", step, l1->i);
 +        if (l1->k) {
 +            printf("%"PRId64, l1->k->i);
 +        }
 +        printf(" ka=[");
 +        links = xmemdup(l1->ka, l1->n_ka * sizeof *l1->ka);
 +        qsort(links, l1->n_ka, sizeof *links, compare_link1);
 +        for (i = 0; i < l1->n_ka; i++) {
 +            printf("%s%"PRId64, i ? " " : "", links[i]->i);
 +        }
 +        free(links);
 +        printf("] l2=");
 +        if (l1->l2) {
 +            printf("%"PRId64, l1->l2->i);
 +        }
 +        printf(" uuid="UUID_FMT"\n", UUID_ARGS(&l1->header_.uuid));
 +        n++;
 +    }
 +    IDLTEST_LINK2_FOR_EACH (l2, idl) {
 +        printf("%03d: i=%"PRId64" l1=", step, l2->i);
 +        if (l2->l1) {
 +            printf("%"PRId64, l2->l1->i);
 +        }
 +        printf(" uuid="UUID_FMT"\n", UUID_ARGS(&l2->header_.uuid));
 +        n++;
 +    }
 +    if (!n) {
 +        printf("%03d: empty\n", step);
 +    }
 +}
 +
 +static unsigned int
 +print_updated_idl(struct ovsdb_idl *idl, struct jsonrpc *rpc,
 +                  int step, unsigned int seqno)
 +{
 +    for (;;) {
 +        unsigned int new_seqno;
 +
 +        if (rpc) {
 +            jsonrpc_run(rpc);
 +        }
 +        ovsdb_idl_run(idl);
 +        new_seqno = ovsdb_idl_get_seqno(idl);
 +        if (new_seqno != seqno) {
 +            print_idl(idl, step);
 +            return new_seqno;
 +        }
 +
 +        if (rpc) {
 +            jsonrpc_wait(rpc);
 +        }
 +        ovsdb_idl_wait(idl);
 +        poll_block();
 +    }
 +}
 +
 +static void
 +parse_uuids(const struct json *json, struct ovsdb_symbol_table *symtab,
 +            size_t *n)
 +{
 +    struct uuid uuid;
 +
 +    if (json->type == JSON_STRING && uuid_from_string(&uuid, json->u.string)) {
 +        char *name = xasprintf("#%d#", *n);
 +        fprintf(stderr, "%s = "UUID_FMT"\n", name, UUID_ARGS(&uuid));
 +        ovsdb_symbol_table_put(symtab, name, &uuid, false);
 +        free(name);
 +        *n += 1;
 +    } else if (json->type == JSON_ARRAY) {
 +        size_t i;
 +
 +        for (i = 0; i < json->u.array.n; i++) {
 +            parse_uuids(json->u.array.elems[i], symtab, n);
 +        }
 +    } else if (json->type == JSON_OBJECT) {
 +        const struct shash_node *node;
 +
 +        SHASH_FOR_EACH (node, json_object(json)) {
 +            parse_uuids(node->data, symtab, n);
 +        }
 +    }
 +}
 +
 +static void
 +substitute_uuids(struct json *json, const struct ovsdb_symbol_table *symtab)
 +{
 +    if (json->type == JSON_STRING) {
 +        const struct ovsdb_symbol *symbol;
 +
 +        symbol = ovsdb_symbol_table_get(symtab, json->u.string);
 +        if (symbol) {
 +            free(json->u.string);
 +            json->u.string = xasprintf(UUID_FMT, UUID_ARGS(&symbol->uuid));
 +        }
 +    } else if (json->type == JSON_ARRAY) {
 +        size_t i;
 +
 +        for (i = 0; i < json->u.array.n; i++) {
 +            substitute_uuids(json->u.array.elems[i], symtab);
 +        }
 +    } else if (json->type == JSON_OBJECT) {
 +        const struct shash_node *node;
 +
 +        SHASH_FOR_EACH (node, json_object(json)) {
 +            substitute_uuids(node->data, symtab);
 +        }
 +    }
 +}
 +
 +static const struct idltest_simple *
 +idltest_find_simple(struct ovsdb_idl *idl, int i)
 +{
 +    const struct idltest_simple *s;
 +
 +    IDLTEST_SIMPLE_FOR_EACH (s, idl) {
 +        if (s->i == i) {
 +            return s;
 +        }
 +    }
 +    return NULL;
 +}
 +
 +static void
 +idl_set(struct ovsdb_idl *idl, char *commands, int step)
 +{
 +    char *cmd, *save_ptr1 = NULL;
 +    struct ovsdb_idl_txn *txn;
 +    enum ovsdb_idl_txn_status status;
 +    bool increment = false;
 +
 +    txn = ovsdb_idl_txn_create(idl);
 +    for (cmd = strtok_r(commands, ",", &save_ptr1); cmd;
 +         cmd = strtok_r(NULL, ",", &save_ptr1)) {
 +        char *save_ptr2 = NULL;
 +        char *name, *arg1, *arg2, *arg3;
 +
 +        name = strtok_r(cmd, " ", &save_ptr2);
 +        arg1 = strtok_r(NULL, " ", &save_ptr2);
 +        arg2 = strtok_r(NULL, " ", &save_ptr2);
 +        arg3 = strtok_r(NULL, " ", &save_ptr2);
 +
 +        if (!strcmp(name, "set")) {
 +            const struct idltest_simple *s;
 +
 +            if (!arg3) {
 +                ovs_fatal(0, "\"set\" command requires 3 arguments");
 +            }
 +
 +            s = idltest_find_simple(idl, atoi(arg1));
 +            if (!s) {
 +                ovs_fatal(0, "\"set\" command asks for nonexistent "
 +                          "i=%d", atoi(arg1));
 +            }
 +
 +            if (!strcmp(arg2, "b")) {
 +                idltest_simple_set_b(s, atoi(arg3));
 +            } else if (!strcmp(arg2, "s")) {
 +                idltest_simple_set_s(s, arg3);
 +            } else if (!strcmp(arg2, "u")) {
 +                struct uuid uuid;
 +                uuid_from_string(&uuid, arg3);
 +                idltest_simple_set_u(s, uuid);
 +            } else if (!strcmp(arg2, "r")) {
 +                idltest_simple_set_r(s, atof(arg3));
 +            } else {
 +                ovs_fatal(0, "\"set\" command asks for unknown column %s",
 +                          arg2);
 +            }
 +        } else if (!strcmp(name, "insert")) {
 +            struct idltest_simple *s;
 +
 +            if (!arg1 || arg2) {
 +                ovs_fatal(0, "\"set\" command requires 1 argument");
 +            }
 +
 +            s = idltest_simple_insert(txn);
 +            idltest_simple_set_i(s, atoi(arg1));
 +        } else if (!strcmp(name, "delete")) {
 +            const struct idltest_simple *s;
 +
 +            if (!arg1 || arg2) {
 +                ovs_fatal(0, "\"set\" command requires 1 argument");
 +            }
 +
 +            s = idltest_find_simple(idl, atoi(arg1));
 +            if (!s) {
 +                ovs_fatal(0, "\"set\" command asks for nonexistent "
 +                          "i=%d", atoi(arg1));
 +            }
 +            idltest_simple_delete(s);
 +        } else if (!strcmp(name, "increment")) {
 +            if (!arg2 || arg3) {
 +                ovs_fatal(0, "\"set\" command requires 2 arguments");
 +            }
 +            ovsdb_idl_txn_increment(txn, arg1, arg2, NULL);
 +            increment = true;
 +        } else {
 +            ovs_fatal(0, "unknown command %s", name);
 +        }
 +    }
 +
 +    while ((status = ovsdb_idl_txn_commit(txn)) == TXN_INCOMPLETE) {
 +        ovsdb_idl_run(idl);
 +        ovsdb_idl_wait(idl);
 +        ovsdb_idl_txn_wait(txn);
 +        poll_block();
 +    }
 +    printf("%03d: commit, status=%s",
 +           step, ovsdb_idl_txn_status_to_string(status));
 +    if (increment) {
 +        printf(", increment=%"PRId64,
 +               ovsdb_idl_txn_get_increment_new_value(txn));
 +    }
 +    putchar('\n');
 +    ovsdb_idl_txn_destroy(txn);
 +}
 +
 +static void
 +do_idl(int argc, char *argv[])
 +{
 +    struct jsonrpc *rpc;
 +    struct ovsdb_idl *idl;
 +    unsigned int seqno = 0;
 +    struct ovsdb_symbol_table *symtab;
 +    size_t n_uuids = 0;
 +    int step = 0;
 +    int error;
 +    int i;
 +
 +    idltest_init();
 +
 +    idl = ovsdb_idl_create(argv[1], &idltest_idl_class);
 +    if (argc > 2) {
 +        struct stream *stream;
 +
 +        error = stream_open_block(argv[1], &stream);
 +        if (error) {
 +            ovs_fatal(error, "failed to connect to \"%s\"", argv[1]);
 +        }
 +        rpc = jsonrpc_open(stream);
 +    } else {
 +        rpc = NULL;
 +    }
 +
 +    setvbuf(stdout, NULL, _IOLBF, 0);
 +
 +    symtab = ovsdb_symbol_table_create();
 +    for (i = 2; i < argc; i++) {
 +        char *arg = argv[i];
 +        struct jsonrpc_msg *request, *reply;
 +        int error;
 +
 +        if (*arg == '+') {
 +            /* The previous transaction didn't change anything. */
 +            arg++;
 +        } else {
 +            seqno = print_updated_idl(idl, rpc, step++, seqno);
 +        }
 +
 +        if (!strcmp(arg, "reconnect")) {
 +            printf("%03d: reconnect\n", step++);
 +            ovsdb_idl_force_reconnect(idl);
 +        } else if (arg[0] != '[') {
 +            idl_set(idl, arg, step++);
 +        } else {
 +            struct json *json = parse_json(arg);
 +            substitute_uuids(json, symtab);
 +            request = jsonrpc_create_request("transact", json, NULL);
 +            error = jsonrpc_transact_block(rpc, request, &reply);
 +            if (error) {
 +                ovs_fatal(error, "jsonrpc transaction failed");
 +            }
 +            printf("%03d: ", step++);
 +            if (reply->result) {
 +                parse_uuids(reply->result, symtab, &n_uuids);
 +            }
 +            json_destroy(reply->id);
 +            reply->id = NULL;
 +            print_and_free_json(jsonrpc_msg_to_json(reply));
 +        }
 +    }
 +    ovsdb_symbol_table_destroy(symtab);
 +
 +    if (rpc) {
 +        jsonrpc_close(rpc);
 +    }
 +    print_updated_idl(idl, NULL, step++, seqno);
 +    ovsdb_idl_destroy(idl);
 +    printf("%03d: done\n", step);
 +}
 +
 +static struct command all_commands[] = {
 +    { "log-io", 2, INT_MAX, do_log_io },
 +    { "parse-atomic-type", 1, 1, do_parse_atomic_type },
 +    { "parse-base-type", 1, 1, do_parse_base_type },
 +    { "parse-type", 1, 1, do_parse_type },
 +    { "parse-atoms", 2, INT_MAX, do_parse_atoms },
 +    { "parse-atom-strings", 2, INT_MAX, do_parse_atom_strings },
 +    { "parse-data", 2, INT_MAX, do_parse_data },
 +    { "parse-data-strings", 2, INT_MAX, do_parse_data_strings },
 +    { "sort-atoms", 2, 2, do_sort_atoms },
 +    { "parse-column", 2, 2, do_parse_column },
 +    { "parse-table", 2, 2, do_parse_table },
 +    { "parse-rows", 2, INT_MAX, do_parse_rows },
 +    { "compare-rows", 2, INT_MAX, do_compare_rows },
 +    { "parse-conditions", 2, INT_MAX, do_parse_conditions },
 +    { "evaluate-conditions", 3, 3, do_evaluate_conditions },
 +    { "parse-mutations", 2, INT_MAX, do_parse_mutations },
 +    { "execute-mutations", 3, 3, do_execute_mutations },
 +    { "query", 3, 3, do_query },
 +    { "query-distinct", 4, 4, do_query_distinct },
 +    { "transact", 1, INT_MAX, do_transact },
 +    { "parse-schema", 1, 1, do_parse_schema },
 +    { "execute", 2, INT_MAX, do_execute },
 +    { "trigger", 2, INT_MAX, do_trigger },
 +    { "idl", 1, INT_MAX, do_idl },
 +    { "help", 0, INT_MAX, do_help },
 +    { NULL, 0, 0, NULL },
 +};
diff --combined tests/test-reconnect.c
index 8441fad,0000000..93991ff
mode 100644,000000..100644
--- /dev/null
@@@ -1,253 -1,0 +1,253 @@@
-  * Copyright (c) 2009 Nicira Networks.
 +/*
- do_enable(int argc UNUSED, char *argv[] UNUSED)
++ * Copyright (c) 2009, 2010 Nicira Networks.
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at:
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <config.h>
 +
 +#include "reconnect.h"
 +
 +#include <errno.h>
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <string.h>
 +
 +#include "command-line.h"
 +#include "compiler.h"
 +#include "svec.h"
 +#include "util.h"
 +
 +static struct reconnect *reconnect;
 +static int now;
 +
 +static const struct command commands[];
 +
 +static void diff_stats(const struct reconnect_stats *old,
 +                       const struct reconnect_stats *new);
 +
 +int
 +main(void)
 +{
 +    struct reconnect_stats prev;
 +    unsigned int old_max_tries;
 +    int old_time;
 +    char line[128];
 +
 +    now = 1000;
 +    reconnect = reconnect_create(now);
 +    reconnect_set_name(reconnect, "remote");
 +    reconnect_get_stats(reconnect, now, &prev);
 +    printf("### t=%d ###\n", now);
 +    old_time = now;
 +    old_max_tries = reconnect_get_max_tries(reconnect);
 +    while (fgets(line, sizeof line, stdin)) {
 +        struct reconnect_stats cur;
 +        struct svec args;
 +
 +        fputs(line, stdout);
 +        if (line[0] == '#') {
 +            continue;
 +        }
 +
 +        svec_init(&args);
 +        svec_parse_words(&args, line);
 +        svec_terminate(&args);
 +        if (!svec_is_empty(&args)) {
 +            run_command(args.n, args.names, commands);
 +        }
 +        svec_destroy(&args);
 +
 +        if (old_time != now) {
 +            printf("\n### t=%d ###\n", now);
 +            old_time = now;
 +        }
 +
 +        reconnect_get_stats(reconnect, now, &cur);
 +        diff_stats(&prev, &cur);
 +        prev = cur;
 +        if (reconnect_get_max_tries(reconnect) != old_max_tries) {
 +            old_max_tries = reconnect_get_max_tries(reconnect);
 +            printf("  %u tries left\n", old_max_tries);
 +        }
 +    }
 +
 +    return 0;
 +}
 +
 +static void
- do_disable(int argc UNUSED, char *argv[] UNUSED)
++do_enable(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 +{
 +    reconnect_enable(reconnect, now);
 +}
 +
 +static void
- do_force_reconnect(int argc UNUSED, char *argv[] UNUSED)
++do_disable(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 +{
 +    reconnect_disable(reconnect, now);
 +}
 +
 +static void
- do_disconnected(int argc UNUSED, char *argv[])
++do_force_reconnect(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 +{
 +    reconnect_force_reconnect(reconnect, now);
 +}
 +
 +static int
 +error_from_string(const char *s)
 +{
 +    if (!s) {
 +        return 0;
 +    } else if (!strcmp(s, "ECONNREFUSED")) {
 +        return ECONNREFUSED;
 +    } else if (!strcmp(s, "EOF")) {
 +        return EOF;
 +    } else {
 +        ovs_fatal(0, "unknown error '%s'", s);
 +    }
 +}
 +
 +static void
- do_connecting(int argc UNUSED, char *argv[] UNUSED)
++do_disconnected(int argc OVS_UNUSED, char *argv[])
 +{
 +    reconnect_disconnected(reconnect, now, error_from_string(argv[1]));
 +}
 +
 +static void
- do_connect_failed(int argc UNUSED, char *argv[])
++do_connecting(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 +{
 +    reconnect_connecting(reconnect, now);
 +}
 +
 +static void
- do_connected(int argc UNUSED, char *argv[] UNUSED)
++do_connect_failed(int argc OVS_UNUSED, char *argv[])
 +{
 +    reconnect_connect_failed(reconnect, now, error_from_string(argv[1]));
 +}
 +
 +static void
- do_received(int argc UNUSED, char *argv[] UNUSED)
++do_connected(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 +{
 +    reconnect_connected(reconnect, now);
 +}
 +
 +static void
- do_advance(int argc UNUSED, char *argv[])
++do_received(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 +{
 +    reconnect_received(reconnect, now);
 +}
 +
 +static void
 +do_run(int argc, char *argv[])
 +{
 +    enum reconnect_action action;
 +
 +    if (argc > 1) {
 +        now += atoi(argv[1]);
 +    }
 +
 +    action = reconnect_run(reconnect, now);
 +    switch (action) {
 +    default:
 +        if (action != 0) {
 +            NOT_REACHED();
 +        }
 +        break;
 +
 +    case RECONNECT_CONNECT:
 +        printf("  should connect\n");
 +        break;
 +
 +    case RECONNECT_DISCONNECT:
 +        printf("  should disconnect\n");
 +        break;
 +
 +    case RECONNECT_PROBE:
 +        printf("  should send probe\n");
 +        break;
 +    }
 +}
 +
 +static void
- do_timeout(int argc UNUSED, char *argv[] UNUSED)
++do_advance(int argc OVS_UNUSED, char *argv[])
 +{
 +    now += atoi(argv[1]);
 +}
 +
 +static void
- do_set_max_tries(int argc UNUSED, char *argv[])
++do_timeout(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 +{
 +    int timeout = reconnect_timeout(reconnect, now);
 +    if (timeout >= 0) {
 +        printf("  advance %d ms\n", timeout);
 +        now += timeout;
 +    } else {
 +        printf("  no timeout\n");
 +    }
 +}
 +
 +static void
++do_set_max_tries(int argc OVS_UNUSED, char *argv[])
 +{
 +    reconnect_set_max_tries(reconnect, atoi(argv[1]));
 +}
 +
 +static void
 +diff_stats(const struct reconnect_stats *old,
 +           const struct reconnect_stats *new)
 +{
 +    if (old->state != new->state
 +        || old->state_elapsed != new->state_elapsed
 +        || old->backoff != new->backoff) {
 +        printf("  in %s for %u ms (%d ms backoff)\n",
 +               new->state, new->state_elapsed, new->backoff);
 +    }
 +    if (old->creation_time != new->creation_time
 +        || old->last_received != new->last_received
 +        || old->last_connected != new->last_connected) {
 +        printf("  created %lld, last received %lld, last connected %lld\n",
 +               new->creation_time, new->last_received, new->last_connected);
 +    }
 +    if (old->n_successful_connections != new->n_successful_connections
 +        || old->n_attempted_connections != new->n_attempted_connections
 +        || old->seqno != new->seqno) {
 +        printf("  %u successful connections out of %u attempts, seqno %u\n",
 +               new->n_successful_connections, new->n_attempted_connections,
 +               new->seqno);
 +    }
 +    if (old->is_connected != new->is_connected
 +        || old->current_connection_duration != new->current_connection_duration
 +        || old->total_connected_duration != new->total_connected_duration) {
 +        printf("  %sconnected (%u ms), total %u ms connected\n",
 +               new->is_connected ? "" : "not ",
 +               new->current_connection_duration,
 +               new->total_connected_duration);
 +    }
 +}
 +
 +static const struct command commands[] = {
 +    { "enable", 0, 0, do_enable },
 +    { "disable", 0, 0, do_disable },
 +    { "force-reconnect", 0, 0, do_force_reconnect },
 +    { "disconnected", 0, 1, do_disconnected },
 +    { "connecting", 0, 0, do_connecting },
 +    { "connect-failed", 0, 1, do_connect_failed },
 +    { "connected", 0, 0, do_connected },
 +    { "received", 0, 0, do_received },
 +    { "run", 0, 1, do_run },
 +    { "advance", 1, 1, do_advance },
 +    { "timeout", 0, 0, do_timeout },
 +    { "set-max-tries", 1, 1, do_set_max_tries },
 +    { NULL, 0, 0, NULL },
 +};
 +
diff --combined tests/test-vconn.c
  #include <signal.h>
  #include <stdlib.h>
  #include <unistd.h>
 +#include "command-line.h"
  #include "poll-loop.h"
  #include "socket-util.h"
 +#include "stream.h"
 +#include "stream-ssl.h"
  #include "timeval.h"
  #include "util.h"
  #include "vlog.h"
@@@ -37,90 -34,94 +37,90 @@@ struct fake_pvconn 
      const char *type;
      char *pvconn_name;
      char *vconn_name;
 -    int fd;
 +    struct pstream *pstream;
  };
  
 +static void
 +check(int a, int b, const char *as, const char *file, int line)
 +{
 +    if (a != b) {
 +        ovs_fatal(0, "%s:%d: %s is %d but should be %d", file, line, as, a, b);
 +    }
 +}
 +
 +
 +#define CHECK(A, B) check(A, B, #A, __FILE__, __LINE__)
 +
 +static void
 +check_errno(int a, int b, const char *as, const char *file, int line)
 +{
 +    if (a != b) {
 +        ovs_fatal(0, "%s:%d: %s is %d (%s) but should be %d (%s)",
 +                  file, line, as, a, strerror(abs(a)), b, strerror(abs(b)));
 +    }
 +}
 +
 +#define CHECK_ERRNO(A, B) check_errno(A, B, #A, __FILE__, __LINE__)
 +
  static void
  fpv_create(const char *type, struct fake_pvconn *fpv)
  {
 +#ifdef HAVE_OPENSSL
 +    if (!strcmp(type, "ssl")) {
 +        stream_ssl_set_private_key_file("testpki-privkey.pem");
 +        stream_ssl_set_certificate_file("testpki-cert.pem");
 +        stream_ssl_set_ca_cert_file("testpki-cacert.pem", false);
 +    }
 +#endif
 +
      fpv->type = type;
      if (!strcmp(type, "unix")) {
          static int unix_count = 0;
          char *bind_path;
 -        int fd;
  
          bind_path = xasprintf("fake-pvconn.%d", unix_count++);
 -        fd = make_unix_socket(SOCK_STREAM, false, false, bind_path, NULL);
 -        if (fd < 0) {
 -            ovs_fatal(-fd, "%s: could not bind to Unix domain socket",
 -                      bind_path);
 -        }
 -
          fpv->pvconn_name = xasprintf("punix:%s", bind_path);
          fpv->vconn_name = xasprintf("unix:%s", bind_path);
 -        fpv->fd = fd;
 +        CHECK_ERRNO(pstream_open(fpv->pvconn_name, &fpv->pstream), 0);
          free(bind_path);
 -    } else if (!strcmp(type, "tcp")) {
 -        struct sockaddr_in sin;
 -        socklen_t sin_len;
 -        int fd;
 -
 -        /* Create TCP socket. */
 -        fd = socket(PF_INET, SOCK_STREAM, 0);
 -        if (fd < 0) {
 -            ovs_fatal(errno, "failed to create TCP socket");
 -        }
 +    } else if (!strcmp(type, "tcp") || !strcmp(type, "ssl")) {
 +        char *s, *port, *save_ptr = NULL;
 +        char *open_name;
  
 -        /* Bind TCP socket to localhost on any available port. */
 -        sin.sin_family = AF_INET;
 -        sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
 -        sin.sin_port = htons(0);
 -        if (bind(fd, (struct sockaddr *) &sin, sizeof sin) < 0) {
 -            ovs_fatal(errno, "failed to bind TCP socket");
 -        }
 +        open_name = xasprintf("p%s:0:127.0.0.1", type);
 +        CHECK_ERRNO(pstream_open(open_name, &fpv->pstream), 0);
  
 -        /* Retrieve socket's port number. */
 -        sin_len = sizeof sin;
 -        if (getsockname(fd, (struct sockaddr *)&sin, &sin_len) < 0) {
 -            ovs_fatal(errno, "failed to read TCP socket name");
 -        }
 -        if (sin_len != sizeof sin || sin.sin_family != AF_INET) {
 -            ovs_fatal(errno, "bad TCP socket name");
 -        }
 +        /* Extract bound port number from pstream name. */
 +        s = xstrdup(pstream_get_name(fpv->pstream));
 +        strtok_r(s, ":", &save_ptr);
 +        port = strtok_r(NULL, ":", &save_ptr);
  
          /* Save info. */
 -        fpv->pvconn_name = xasprintf("ptcp:%"PRIu16":127.0.0.1",
 -                                    ntohs(sin.sin_port));
 -        fpv->vconn_name = xasprintf("tcp:127.0.0.1:%"PRIu16,
 -                                    ntohs(sin.sin_port));
 -        fpv->fd = fd;
 +        fpv->pvconn_name = xstrdup(pstream_get_name(fpv->pstream));
 +        fpv->vconn_name = xasprintf("%s:127.0.0.1:%s", type, port);
 +
 +        free(open_name);
 +        free(s);
      } else {
          abort();
      }
 -
 -    /* Listen. */
 -    if (listen(fpv->fd, 0) < 0) {
 -        ovs_fatal(errno, "%s: listen failed", fpv->vconn_name);
 -    }
  }
  
 -static int
 +static struct stream *
  fpv_accept(struct fake_pvconn *fpv)
  {
 -    int fd;
 +    struct stream *stream;
  
 -    fd = accept(fpv->fd, NULL, NULL);
 -    if (fd < 0) {
 -        ovs_fatal(errno, "%s: accept failed", fpv->pvconn_name);
 -    }
 -    return fd;
 +    CHECK_ERRNO(pstream_accept_block(fpv->pstream, &stream), 0);
 +
 +    return stream;
  }
  
  static void
  fpv_close(struct fake_pvconn *fpv)
  {
 -    if (fpv->fd >= 0) {
 -        if (close(fpv->fd) < 0) {
 -            ovs_fatal(errno, "failed to close %s fake pvconn", fpv->type);
 -        }
 -        fpv->fd = -1;
 -    }
 +    pstream_close(fpv->pstream);
 +    fpv->pstream = NULL;
  }
  
  static void
@@@ -134,20 -135,15 +134,20 @@@ fpv_destroy(struct fake_pvconn *fpv
  /* Connects to a fake_pvconn with vconn_open(), then closes the listener and
   * verifies that vconn_connect() reports 'expected_error'. */
  static void
- test_refuse_connection(int argc UNUSED, char *argv[])
 -test_refuse_connection(const char *type, int expected_error)
++test_refuse_connection(int argc OVS_UNUSED, char *argv[])
  {
 +    const char *type = argv[1];
 +    int expected_error;
      struct fake_pvconn fpv;
      struct vconn *vconn;
  
 +    expected_error = !strcmp(type, "unix") ? EPIPE : ECONNRESET;
 +
      fpv_create(type, &fpv);
 -    assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
 +    CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP_VERSION, &vconn), 0);
      fpv_close(&fpv);
 -    assert(vconn_connect(vconn) == expected_error);
 +    vconn_run(vconn);
 +    CHECK_ERRNO(vconn_connect(vconn), expected_error);
      vconn_close(vconn);
      fpv_destroy(&fpv);
  }
   * closes it immediately, and verifies that vconn_connect() reports
   * 'expected_error'. */
  static void
- test_accept_then_close(int argc UNUSED, char *argv[])
 -test_accept_then_close(const char *type, int expected_error)
++test_accept_then_close(int argc OVS_UNUSED, char *argv[])
  {
 +    const char *type = argv[1];
 +    int expected_error;
      struct fake_pvconn fpv;
      struct vconn *vconn;
  
 +    expected_error = (!strcmp(type, "unix") ? EPIPE
 +                      : !strcmp(type, "tcp") ? ECONNRESET
 +                      : EPROTO);
 +
      fpv_create(type, &fpv);
 -    assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
 -    close(fpv_accept(&fpv));
 +    CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP_VERSION, &vconn), 0);
 +    vconn_run(vconn);
 +    stream_close(fpv_accept(&fpv));
      fpv_close(&fpv);
 -    assert(vconn_connect(vconn) == expected_error);
 +    CHECK_ERRNO(vconn_connect(vconn), expected_error);
      vconn_close(vconn);
      fpv_destroy(&fpv);
  }
   * reads the hello message from it, then closes the connection and verifies
   * that vconn_connect() reports 'expected_error'. */
  static void
- test_read_hello(int argc UNUSED, char *argv[])
 -test_read_hello(const char *type, int expected_error)
++test_read_hello(int argc OVS_UNUSED, char *argv[])
  {
 +    const char *type = argv[1];
      struct fake_pvconn fpv;
      struct vconn *vconn;
 -    int fd;
 +    struct stream *stream;
  
      fpv_create(type, &fpv);
 -    assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
 -    fd = fpv_accept(&fpv);
 +    CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP_VERSION, &vconn), 0);
 +    vconn_run(vconn);
 +    stream = fpv_accept(&fpv);
      fpv_destroy(&fpv);
 -    assert(!set_nonblocking(fd));
      for (;;) {
         struct ofp_header hello;
         int retval;
  
 -       retval = read(fd, &hello, sizeof hello);
 +       retval = stream_recv(stream, &hello, sizeof hello);
         if (retval == sizeof hello) {
 -           assert(hello.version == OFP_VERSION);
 -           assert(hello.type == OFPT_HELLO);
 -           assert(hello.length == htons(sizeof hello));
 +           CHECK(hello.version, OFP_VERSION);
 +           CHECK(hello.type, OFPT_HELLO);
 +           CHECK(hello.length, htons(sizeof hello));
             break;
         } else {
 -           assert(errno == EAGAIN);
 +           CHECK_ERRNO(retval, -EAGAIN);
         }
  
 -       assert(vconn_connect(vconn) == EAGAIN);
 +       vconn_run(vconn);
 +       CHECK_ERRNO(vconn_connect(vconn), EAGAIN);
 +       vconn_run_wait(vconn);
         vconn_connect_wait(vconn);
 -       poll_fd_wait(fd, POLLIN);
 +       stream_recv_wait(stream);
         poll_block();
      }
 -    close(fd);
 -    assert(vconn_connect(vconn) == expected_error);
 +    stream_close(stream);
 +    CHECK_ERRNO(vconn_connect(vconn), ECONNRESET);
      vconn_close(vconn);
  }
  
@@@ -231,62 -217,44 +231,62 @@@ test_send_hello(const char *type, cons
      struct vconn *vconn;
      bool read_hello, connected;
      struct ofpbuf *msg;
 -    int fd;
 +    struct stream *stream;
 +    size_t n_sent;
  
      fpv_create(type, &fpv);
 -    assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
 -    fd = fpv_accept(&fpv);
 +    CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP_VERSION, &vconn), 0);
 +    vconn_run(vconn);
 +    stream = fpv_accept(&fpv);
      fpv_destroy(&fpv);
  
 -    write(fd, out, out_size);
 -
 -    assert(!set_nonblocking(fd));
 +    n_sent = 0;
 +    while (n_sent < out_size) {
 +        int retval;
 +
 +        retval = stream_send(stream, (char *) out + n_sent, out_size - n_sent);
 +        if (retval > 0) {
 +            n_sent += retval;
 +        } else if (retval == -EAGAIN) {
 +            stream_run(stream);
 +            vconn_run(vconn);
 +            stream_recv_wait(stream);
 +            vconn_connect_wait(vconn);
 +            vconn_run_wait(vconn);
 +            poll_block();
 +        } else {
 +            ovs_fatal(0, "stream_send returned unexpected value %d", retval);
 +        }
 +    }
  
      read_hello = connected = false;
      for (;;) {
         if (!read_hello) {
             struct ofp_header hello;
 -           int retval = read(fd, &hello, sizeof hello);
 +           int retval = stream_recv(stream, &hello, sizeof hello);
             if (retval == sizeof hello) {
 -               assert(hello.version == OFP_VERSION);
 -               assert(hello.type == OFPT_HELLO);
 -               assert(hello.length == htons(sizeof hello));
 +               CHECK(hello.version, OFP_VERSION);
 +               CHECK(hello.type, OFPT_HELLO);
 +               CHECK(hello.length, htons(sizeof hello));
                 read_hello = true;
             } else {
 -               assert(errno == EAGAIN);
 +               CHECK_ERRNO(retval, -EAGAIN);
             }
         }
  
 +       vconn_run(vconn);
         if (!connected) {
             int error = vconn_connect(vconn);
             if (error == expect_connect_error) {
                 if (!error) {
                     connected = true;
                 } else {
 -                   close(fd);
 +                   stream_close(stream);
                     vconn_close(vconn);
                     return;
                 }
             } else {
 -               assert(error == EAGAIN);
 +               CHECK_ERRNO(error, EAGAIN);
             }
         }
  
             break;
         }
  
 +       vconn_run_wait(vconn);
         if (!connected) {
             vconn_connect_wait(vconn);
         }
         if (!read_hello) {
 -           poll_fd_wait(fd, POLLIN);
 +           stream_recv_wait(stream);
         }
         poll_block();
      }
 -    close(fd);
 -    assert(vconn_recv(vconn, &msg) == EOF);
 +    stream_close(stream);
 +    CHECK_ERRNO(vconn_recv(vconn, &msg), EOF);
      vconn_close(vconn);
  }
  
  /* Try connecting and sending a normal hello, which should succeed. */
  static void
- test_send_plain_hello(int argc UNUSED, char *argv[])
 -test_send_plain_hello(const char *type)
++test_send_plain_hello(int argc OVS_UNUSED, char *argv[])
  {
 +    const char *type = argv[1];
      struct ofp_header hello;
  
      hello.version = OFP_VERSION;
   * the specification says that implementations must accept and ignore extra
   * data). */
  static void
- test_send_long_hello(int argc UNUSED, char *argv[])
 -test_send_long_hello(const char *type)
++test_send_long_hello(int argc OVS_UNUSED, char *argv[])
  {
 +    const char *type = argv[1];
      struct ofp_header hello;
      char buffer[sizeof hello * 2];
  
  /* Try connecting and sending an echo request instead of a hello, which should
   * fail with EPROTO. */
  static void
- test_send_echo_hello(int argc UNUSED, char *argv[])
 -test_send_echo_hello(const char *type)
++test_send_echo_hello(int argc OVS_UNUSED, char *argv[])
  {
 +    const char *type = argv[1];
      struct ofp_header echo;
  
      echo.version = OFP_VERSION;
  /* Try connecting and sending a hello packet that has its length field as 0,
   * which should fail with EPROTO. */
  static void
- test_send_short_hello(int argc UNUSED, char *argv[])
 -test_send_short_hello(const char *type)
++test_send_short_hello(int argc OVS_UNUSED, char *argv[])
  {
 +    const char *type = argv[1];
      struct ofp_header hello;
  
      memset(&hello, 0, sizeof hello);
  /* Try connecting and sending a hello packet that has a bad version, which
   * should fail with EPROTO. */
  static void
- test_send_invalid_version_hello(int argc UNUSED, char *argv[])
 -test_send_invalid_version_hello(const char *type)
++test_send_invalid_version_hello(int argc OVS_UNUSED, char *argv[])
  {
 +    const char *type = argv[1];
      struct ofp_header hello;
  
      hello.version = OFP_VERSION - 1;
      test_send_hello(type, &hello, sizeof hello, EPROTO);
  }
  
 +static const struct command commands[] = {
 +    {"refuse-connection", 1, 1, test_refuse_connection},
 +    {"accept-then-close", 1, 1, test_accept_then_close},
 +    {"read-hello", 1, 1, test_read_hello},
 +    {"send-plain-hello", 1, 1, test_send_plain_hello},
 +    {"send-long-hello", 1, 1, test_send_long_hello},
 +    {"send-echo-hello", 1, 1, test_send_echo_hello},
 +    {"send-short-hello", 1, 1, test_send_short_hello},
 +    {"send-invalid-version-hello", 1, 1, test_send_invalid_version_hello},
 +    {NULL, 0, 0, NULL},
 +};
 +
  int
- main(int argc, char *argv[])
+ main(int argc OVS_UNUSED, char *argv[])
  {
      set_program_name(argv[0]);
      time_init();
      vlog_init();
 -    signal(SIGPIPE, SIG_IGN);
      vlog_set_levels(VLM_ANY_MODULE, VLF_ANY_FACILITY, VLL_EMER);
 +    vlog_set_levels(VLM_ANY_MODULE, VLF_CONSOLE, VLL_DBG);
 +    signal(SIGPIPE, SIG_IGN);
  
      time_alarm(10);
  
 -    test_refuse_connection("unix", EPIPE);
 -    test_refuse_connection("tcp", ECONNRESET);
 -
 -    test_accept_then_close("unix", EPIPE);
 -    test_accept_then_close("tcp", ECONNRESET);
 -
 -    test_read_hello("unix", ECONNRESET);
 -    test_read_hello("tcp", ECONNRESET);
 -
 -    test_send_plain_hello("unix");
 -    test_send_plain_hello("tcp");
 -
 -    test_send_long_hello("unix");
 -    test_send_long_hello("tcp");
 -
 -    test_send_echo_hello("unix");
 -    test_send_echo_hello("tcp");
 -
 -    test_send_short_hello("unix");
 -    test_send_short_hello("tcp");
 -
 -    test_send_invalid_version_hello("unix");
 -    test_send_invalid_version_hello("tcp");
 +    run_command(argc - 1, argv + 1, commands);
  
      return 0;
  }
diff --combined utilities/ovs-discover.c
@@@ -60,7 -60,7 +60,7 @@@ static bool exit_without_bind
  static bool exit_after_bind;
  
  static bool iface_init(struct iface *, const char *netdev_name);
- static void release_ifaces(void *aux UNUSED);
+ static void release_ifaces(void *aux OVS_UNUSED);
  
  static void parse_options(int argc, char *argv[]);
  static void usage(void) NO_RETURN;
@@@ -75,7 -75,6 +75,7 @@@ main(int argc, char *argv[]
      int retval;
      int i;
  
 +    proctitle_init(argc, argv);
      set_program_name(argv[0]);
      time_init();
      vlog_init();
          struct iface *iface = &ifaces[i];
          dhclient_init(iface->dhcp, 0);
      }
 -    fatal_signal_add_hook(release_ifaces, NULL, true);
 +    fatal_signal_add_hook(release_ifaces, NULL, NULL, true);
  
      retval = regcomp(&accept_controller_regex, accept_controller_re,
                       REG_NOSUB | REG_EXTENDED);
  
      retval = unixctl_server_create(NULL, &unixctl);
      if (retval) {
 -        ovs_fatal(retval, "Could not listen for unixctl connections");
 +        exit(EXIT_FAILURE);
      }
  
      die_if_already_running();
  
      signal(SIGPIPE, SIG_IGN);
      for (;;) {
 -        fatal_signal_block();
          for (i = 0; i < n_ifaces; i++) {
              struct iface *iface = &ifaces[i];
              dhclient_run(iface->dhcp);
              dhclient_wait(iface->dhcp);
          }
          unixctl_server_wait(unixctl);
 -        fatal_signal_unblock();
          poll_block();
      }
  
@@@ -214,7 -215,7 +214,7 @@@ iface_init(struct iface *iface, const c
           * persists past program termination. */
          struct netdev *netdev;
  
 -        retval = netdev_open(iface->name, NETDEV_ETH_TYPE_NONE, &netdev);
 +        retval = netdev_open_default(iface->name, &netdev);
          if (retval) {
              ovs_error(retval, "Could not open %s device", iface->name);
              return false;
  }
  
  static void
- release_ifaces(void *aux UNUSED)
+ release_ifaces(void *aux OVS_UNUSED)
  {
      int i;
  
  }
  
  static void
- modify_dhcp_request(struct dhcp_msg *msg, void *aux UNUSED)
+ modify_dhcp_request(struct dhcp_msg *msg, void *aux OVS_UNUSED)
  {
      dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
  }
  
  static bool
- validate_dhcp_offer(const struct dhcp_msg *msg, void *aux UNUSED)
+ validate_dhcp_offer(const struct dhcp_msg *msg, void *aux OVS_UNUSED)
  {
      static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
      char *vconn_name;
diff --combined utilities/ovs-dpctl.c
  #include "vlog.h"
  #define THIS_MODULE VLM_dpctl
  
 -struct command {
 -    const char *name;
 -    int min_args;
 -    int max_args;
 -    void (*handler)(int argc, char *argv[]);
 -};
 -
 -static struct command all_commands[];
 +static const struct command all_commands[];
  
  static void usage(void) NO_RETURN;
  static void parse_options(int argc, char *argv[]);
  
 -int main(int argc, char *argv[])
 +int
 +main(int argc, char *argv[])
  {
 -    struct command *p;
 -
      set_program_name(argv[0]);
      time_init();
      vlog_init();
      parse_options(argc, argv);
      signal(SIGPIPE, SIG_IGN);
 -
 -    argc -= optind;
 -    argv += optind;
 -    if (argc < 1)
 -        ovs_fatal(0, "missing command name; use --help for help");
 -
 -    for (p = all_commands; p->name != NULL; p++) {
 -        if (!strcmp(p->name, argv[0])) {
 -            int n_arg = argc - 1;
 -            if (n_arg < p->min_args)
 -                ovs_fatal(0, "'%s' command requires at least %d arguments",
 -                          p->name, p->min_args);
 -            else if (n_arg > p->max_args)
 -                ovs_fatal(0, "'%s' command takes at most %d arguments",
 -                          p->name, p->max_args);
 -            else {
 -                p->handler(argc, argv);
 -                if (ferror(stdout)) {
 -                    ovs_fatal(0, "write to stdout failed");
 -                }
 -                if (ferror(stderr)) {
 -                    ovs_fatal(0, "write to stderr failed");
 -                }
 -                exit(0);
 -            }
 -        }
 -    }
 -    ovs_fatal(0, "unknown command '%s'; use --help for help", argv[0]);
 -
 +    run_command(argc - optind, argv + optind, all_commands);
      return 0;
  }
  
@@@ -168,7 -204,7 +168,7 @@@ static int if_up(const char *netdev_nam
      struct netdev *netdev;
      int retval;
  
 -    retval = netdev_open(netdev_name, NETDEV_ETH_TYPE_NONE, &netdev);
 +    retval = netdev_open_default(netdev_name, &netdev);
      if (!retval) {
          retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
          netdev_close(netdev);
      return retval;
  }
  
 +static int
 +parsed_dpif_open(const char *arg_, bool create, struct dpif **dpifp)
 +{
 +    int result;
 +    char *name, *type;
 +
 +    dp_parse_name(arg_, &name, &type);
 +
 +    if (create) {
 +        result = dpif_create(name, type, dpifp);
 +    } else {
 +        result = dpif_open(name, type, dpifp);
 +    }
 +
 +    free(name);
 +    free(type);
 +    return result;
 +}
 +
  static void
- do_add_dp(int argc UNUSED, char *argv[])
+ do_add_dp(int argc OVS_UNUSED, char *argv[])
  {
      struct dpif *dpif;
 -    run(dpif_create(argv[1], &dpif), "add_dp");
 +    run(parsed_dpif_open(argv[1], true, &dpif), "add_dp");
      dpif_close(dpif);
      if (argc > 2) {
          do_add_if(argc, argv);
  }
  
  static void
- do_del_dp(int argc UNUSED, char *argv[])
+ do_del_dp(int argc OVS_UNUSED, char *argv[])
  {
      struct dpif *dpif;
 -    run(dpif_open(argv[1], &dpif), "opening datapath");
 +    run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
      run(dpif_delete(dpif), "del_dp");
      dpif_close(dpif);
  }
@@@ -231,13 -248,13 +231,13 @@@ query_ports(struct dpif *dpif, struct o
  }
  
  static void
- do_add_if(int argc UNUSED, char *argv[])
+ do_add_if(int argc OVS_UNUSED, char *argv[])
  {
      bool failure = false;
      struct dpif *dpif;
      int i;
  
 -    run(dpif_open(argv[1], &dpif), "opening datapath");
 +    run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
      for (i = 2; i < argc; i++) {
          char *save_ptr = NULL;
          char *devname, *suboptions;
@@@ -309,13 -326,13 +309,13 @@@ get_port_number(struct dpif *dpif, cons
  }
  
  static void
- do_del_if(int argc UNUSED, char *argv[])
+ do_del_if(int argc OVS_UNUSED, char *argv[])
  {
      bool failure = false;
      struct dpif *dpif;
      int i;
  
 -    run(dpif_open(argv[1], &dpif), "opening datapath");
 +    run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
      for (i = 2; i < argc; i++) {
          const char *name = argv[i];
          uint16_t port;
@@@ -387,7 -404,7 +387,7 @@@ do_show(int argc, char *argv[]
              struct dpif *dpif;
              int error;
  
 -            error = dpif_open(name, &dpif);
 +            error = parsed_dpif_open(name, false, &dpif);
              if (!error) {
                  show_dpif(dpif);
              } else {
              int error;
  
              sprintf(name, "dp%u", i);
 -            error = dpif_open(name, &dpif);
 +            error = parsed_dpif_open(name, false, &dpif);
              if (!error) {
                  show_dpif(dpif);
              } else if (error != ENODEV) {
  }
  
  static void
- do_dump_dps(int argc UNUSED, char *argv[] UNUSED)
+ do_dump_dps(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
  {
 -    struct svec all_dps;
 +    struct svec dpif_names, dpif_types;
      unsigned int i;
 -    int error;
 +    int error = 0;
 +
 +    svec_init(&dpif_names);
 +    svec_init(&dpif_types);
 +    dp_enumerate_types(&dpif_types);
 +
 +    for (i = 0; i < dpif_types.n; i++) {
 +        unsigned int j;
 +        int retval;
  
 -    svec_init(&all_dps);
 -    error = dp_enumerate(&all_dps);
 +        retval = dp_enumerate_names(dpif_types.names[i], &dpif_names);
 +        if (retval) {
 +            error = retval;
 +        }
  
 -    for (i = 0; i < all_dps.n; i++) {
 -        struct dpif *dpif;
 -        if (!dpif_open(all_dps.names[i], &dpif)) {
 -            printf("%s\n", dpif_name(dpif));
 -            dpif_close(dpif);
 +        for (j = 0; j < dpif_names.n; j++) {
 +            struct dpif *dpif;
 +            if (!dpif_open(dpif_names.names[j], dpif_types.names[i], &dpif)) {
 +                printf("%s\n", dpif_name(dpif));
 +                dpif_close(dpif);
 +            }
          }
      }
  
 -    svec_destroy(&all_dps);
 +    svec_destroy(&dpif_names);
 +    svec_destroy(&dpif_types);
      if (error) {
          exit(EXIT_FAILURE);
      }
  }
  
  static void
- do_dump_flows(int argc UNUSED, char *argv[])
+ do_dump_flows(int argc OVS_UNUSED, char *argv[])
  {
      struct odp_flow *flows;
      struct dpif *dpif;
      struct ds ds;
      size_t i;
  
 -    run(dpif_open(argv[1], &dpif), "opening datapath");
 +    run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
      run(dpif_flow_list_all(dpif, &flows, &n_flows), "listing all flows");
  
      ds_init(&ds);
  }
  
  static void
- do_del_flows(int argc UNUSED, char *argv[])
+ do_del_flows(int argc OVS_UNUSED, char *argv[])
  {
      struct dpif *dpif;
  
 -    run(dpif_open(argv[1], &dpif), "opening datapath");
 +    run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
      run(dpif_flow_flush(dpif), "deleting all flows");
      dpif_close(dpif);
  }
  
  static void
- do_dump_groups(int argc UNUSED, char *argv[])
+ do_dump_groups(int argc OVS_UNUSED, char *argv[])
  {
      struct odp_stats stats;
      struct dpif *dpif;
      unsigned int i;
  
 -    run(dpif_open(argv[1], &dpif), "opening datapath");
 +    run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
      run(dpif_get_dp_stats(dpif, &stats), "get datapath stats");
      for (i = 0; i < stats.max_groups; i++) {
          uint16_t *ports;
  }
  
  static void
- do_help(int argc UNUSED, char *argv[] UNUSED)
+ do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
  {
      usage();
  }
  
 -static struct command all_commands[] = {
 +static const struct command all_commands[] = {
      { "add-dp", 1, INT_MAX, do_add_dp },
      { "del-dp", 1, 1, do_del_dp },
      { "add-if", 2, INT_MAX, do_add_if },
diff --combined utilities/ovs-ofctl.c
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * Copyright (c) 2008, 2009 Nicira Networks.
+  * Copyright (c) 2008, 2009, 2010 Nicira Networks.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
@@@ -44,9 -44,9 +44,9 @@@
  #include "packets.h"
  #include "random.h"
  #include "socket-util.h"
 +#include "stream-ssl.h"
  #include "timeval.h"
  #include "util.h"
 -#include "vconn-ssl.h"
  #include "vconn.h"
  
  #include "vlog.h"
  #define MOD_PORT_CMD_FLOOD   "flood"
  #define MOD_PORT_CMD_NOFLOOD "noflood"
  
 +/* Use strict matching for flow mod commands? */
 +static bool strict;
  
 -/* Settings that may be configured by the user. */
 -struct settings {
 -    bool strict;        /* Use strict matching for flow mod commands */
 -};
 -
 -struct command {
 -    const char *name;
 -    int min_args;
 -    int max_args;
 -    void (*handler)(const struct settings *, int argc, char *argv[]);
 -};
 -
 -static struct command all_commands[];
 +static const struct command all_commands[];
  
  static void usage(void) NO_RETURN;
 -static void parse_options(int argc, char *argv[], struct settings *);
 +static void parse_options(int argc, char *argv[]);
  
 -int main(int argc, char *argv[])
 +int
 +main(int argc, char *argv[])
  {
 -    struct settings s;
 -    struct command *p;
 -
      set_program_name(argv[0]);
      time_init();
      vlog_init();
 -    parse_options(argc, argv, &s);
 +    parse_options(argc, argv);
      signal(SIGPIPE, SIG_IGN);
 -
 -    argc -= optind;
 -    argv += optind;
 -    if (argc < 1)
 -        ovs_fatal(0, "missing command name; use --help for help");
 -
 -    for (p = all_commands; p->name != NULL; p++) {
 -        if (!strcmp(p->name, argv[0])) {
 -            int n_arg = argc - 1;
 -            if (n_arg < p->min_args)
 -                ovs_fatal(0, "'%s' command requires at least %d arguments",
 -                          p->name, p->min_args);
 -            else if (n_arg > p->max_args)
 -                ovs_fatal(0, "'%s' command takes at most %d arguments",
 -                          p->name, p->max_args);
 -            else {
 -                p->handler(&s, argc, argv);
 -                if (ferror(stdout)) {
 -                    ovs_fatal(0, "write to stdout failed");
 -                }
 -                if (ferror(stderr)) {
 -                    ovs_fatal(0, "write to stderr failed");
 -                }
 -                exit(0);
 -            }
 -        }
 -    }
 -    ovs_fatal(0, "unknown command '%s'; use --help for help", argv[0]);
 -
 +    run_command(argc - optind, argv + optind, all_commands);
      return 0;
  }
  
  static void
 -parse_options(int argc, char *argv[], struct settings *s)
 +parse_options(int argc, char *argv[])
  {
      enum {
          OPT_STRICT = UCHAR_MAX + 1,
          {"help", no_argument, 0, 'h'},
          {"version", no_argument, 0, 'V'},
          VLOG_LONG_OPTIONS,
 -        VCONN_SSL_LONG_OPTIONS
 +        STREAM_SSL_LONG_OPTIONS
          {0, 0, 0, 0},
      };
      char *short_options = long_options_to_short_options(long_options);
  
 -    /* Set defaults that we can figure out before parsing options. */
 -    s->strict = false;
 -
      for (;;) {
          unsigned long int timeout;
          int c;
              exit(EXIT_SUCCESS);
  
          case OPT_STRICT:
 -            s->strict = true;
 +            strict = true;
              break;
  
          VLOG_OPTION_HANDLERS
 -        VCONN_SSL_OPTION_HANDLERS
 +        STREAM_SSL_OPTION_HANDLERS
  
          case '?':
              exit(EXIT_FAILURE);
@@@ -162,6 -205,7 +162,6 @@@ usage(void
             "  mod-flows SWITCH FLOW       modify actions of matching FLOWs\n"
             "  del-flows SWITCH [FLOW]     delete matching FLOWs\n"
             "  monitor SWITCH MISSLEN EXP  print packets received from SWITCH\n"
 -           "  execute SWITCH CMD [ARG...] execute CMD with ARGS on SWITCH\n"
             "\nFor OpenFlow switches and controllers:\n"
             "  probe VCONN                 probe whether VCONN is up\n"
             "  ping VCONN [N]              latency of N-byte echos\n"
@@@ -202,36 -246,25 +202,36 @@@ static void run(int retval, const char 
  \f
  /* Generic commands. */
  
 +static void
 +open_vconn_socket(const char *name, struct vconn **vconnp)
 +{
 +    char *vconn_name = xasprintf("unix:%s", name);
 +    VLOG_INFO("connecting to %s", vconn_name);
 +    run(vconn_open_block(vconn_name, OFP_VERSION, vconnp),
 +        "connecting to %s", vconn_name);
 +    free(vconn_name);
 +}
 +
  static void
  open_vconn(const char *name, struct vconn **vconnp)
  {
      struct dpif *dpif;
      struct stat s;
 +    char *bridge_path, *datapath_name, *datapath_type;
 +
 +    bridge_path = xasprintf("%s/%s.mgmt", ovs_rundir, name);
 +    dp_parse_name(name, &datapath_name, &datapath_type);
  
      if (strstr(name, ":")) {
          run(vconn_open_block(name, OFP_VERSION, vconnp),
              "connecting to %s", name);
      } else if (!stat(name, &s) && S_ISSOCK(s.st_mode)) {
 -        char *vconn_name = xasprintf("unix:%s", name);
 -        VLOG_INFO("connecting to %s", vconn_name);
 -        run(vconn_open_block(vconn_name, OFP_VERSION, vconnp),
 -            "connecting to %s", vconn_name);
 -        free(vconn_name);
 -    } else if (!dpif_open(name, &dpif)) {
 +        open_vconn_socket(name, vconnp);
 +    } else if (!stat(bridge_path, &s) && S_ISSOCK(s.st_mode)) {
 +        open_vconn_socket(bridge_path, vconnp);
 +    } else if (!dpif_open(datapath_name, datapath_type, &dpif)) {
          char dpif_name[IF_NAMESIZE + 1];
          char *socket_name;
 -        char *vconn_name;
  
          run(dpif_port_get_name(dpif, ODPP_LOCAL, dpif_name, sizeof dpif_name),
              "obtaining name of %s", dpif_name);
                        name, socket_name);
          }
  
 -        vconn_name = xasprintf("unix:%s", socket_name);
 -        VLOG_INFO("connecting to %s", vconn_name);
 -        run(vconn_open_block(vconn_name, OFP_VERSION, vconnp),
 -            "connecting to %s", vconn_name);
 +        open_vconn_socket(socket_name, vconnp);
          free(socket_name);
 -        free(vconn_name);
      } else {
          ovs_fatal(0, "%s is not a valid connection method", name);
      }
 +
 +    free(datapath_name);
 +    free(datapath_type);
 +    free(bridge_path);
  }
  
  static void *
@@@ -339,14 -372,14 +339,14 @@@ dump_trivial_stats_transaction(const ch
  }
  
  static void
- do_show(int argc UNUSED, char *argv[])
 -do_show(const struct settings *s OVS_UNUSED, int argc OVS_UNUSED, char *argv[])
++do_show(int argc OVS_UNUSED, char *argv[])
  {
      dump_trivial_transaction(argv[1], OFPT_FEATURES_REQUEST);
      dump_trivial_transaction(argv[1], OFPT_GET_CONFIG_REQUEST);
  }
  
  static void
 -do_status(const struct settings *s OVS_UNUSED, int argc, char *argv[])
 +do_status(int argc, char *argv[])
  {
      struct nicira_header *request, *reply;
      struct vconn *vconn;
  }
  
  static void
- do_dump_desc(int argc UNUSED, char *argv[])
 -do_dump_desc(const struct settings *s OVS_UNUSED,
 -             int argc OVS_UNUSED, char *argv[])
++do_dump_desc(int argc OVS_UNUSED, char *argv[])
  {
      dump_trivial_stats_transaction(argv[1], OFPST_DESC);
  }
  
  static void
- do_dump_tables(int argc UNUSED, char *argv[])
 -do_dump_tables(const struct settings *s OVS_UNUSED,
 -               int argc OVS_UNUSED, char *argv[])
++do_dump_tables(int argc OVS_UNUSED, char *argv[])
  {
      dump_trivial_stats_transaction(argv[1], OFPST_TABLE);
  }
@@@ -780,7 -815,7 +780,7 @@@ str_to_flow(char *string, struct ofp_ma
  }
  
  static void
 -do_dump_flows(const struct settings *s OVS_UNUSED, int argc, char *argv[])
 +do_dump_flows(int argc, char *argv[])
  {
      struct ofp_flow_stats_request *req;
      uint16_t out_port;
  }
  
  static void
 -do_dump_aggregate(const struct settings *s OVS_UNUSED, int argc, char *argv[])
 +do_dump_aggregate(int argc, char *argv[])
  {
      struct ofp_aggregate_stats_request *req;
      struct ofpbuf *request;
  }
  
  static void
- do_add_flow(int argc UNUSED, char *argv[])
 -do_add_flow(const struct settings *s OVS_UNUSED, int argc OVS_UNUSED,
 -            char *argv[])
++do_add_flow(int argc OVS_UNUSED, char *argv[])
  {
      struct vconn *vconn;
      struct ofpbuf *buffer;
  }
  
  static void
- do_add_flows(int argc UNUSED, char *argv[])
 -do_add_flows(const struct settings *s OVS_UNUSED, int argc OVS_UNUSED,
 -             char *argv[])
++do_add_flows(int argc OVS_UNUSED, char *argv[])
  {
      struct vconn *vconn;
      FILE *file;
          /* Parse and send.  str_to_flow() will expand and reallocate the data
           * in 'buffer', so we can't keep pointers to across the str_to_flow()
           * call. */
 -        ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
 +        make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
          str_to_flow(line, &match, buffer,
                      NULL, NULL, &priority, &idle_timeout, &hard_timeout);
          ofm = buffer->data;
  }
  
  static void
- do_mod_flows(int argc UNUSED, char *argv[])
 -do_mod_flows(const struct settings *s, int argc OVS_UNUSED, char *argv[])
++do_mod_flows(int argc OVS_UNUSED, char *argv[])
  {
      uint16_t priority, idle_timeout, hard_timeout;
      struct vconn *vconn;
                  NULL, NULL, &priority, &idle_timeout, &hard_timeout);
      ofm = buffer->data;
      ofm->match = match;
 -    if (s->strict) {
 +    if (strict) {
          ofm->command = htons(OFPFC_MODIFY_STRICT);
      } else {
          ofm->command = htons(OFPFC_MODIFY);
      vconn_close(vconn);
  }
  
 -static void do_del_flows(const struct settings *s, int argc, char *argv[])
 +static void do_del_flows(int argc, char *argv[])
  {
      struct vconn *vconn;
      uint16_t priority;
      ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
      str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, NULL, 
                  &out_port, &priority, NULL, NULL);
 -    if (s->strict) {
 +    if (strict) {
          ofm->command = htons(OFPFC_DELETE_STRICT);
      } else {
          ofm->command = htons(OFPFC_DELETE);
  }
  
  static void
- do_monitor(int argc UNUSED, char *argv[])
 -do_monitor(const struct settings *s OVS_UNUSED,
 -           int argc OVS_UNUSED, char *argv[])
++do_monitor(int argc OVS_UNUSED, char *argv[])
  {
      struct vconn *vconn;
  
  }
  
  static void
- do_dump_ports(int argc UNUSED, char *argv[])
 -do_dump_ports(const struct settings *s OVS_UNUSED,
 -              int argc OVS_UNUSED, char *argv[])
++do_dump_ports(int argc OVS_UNUSED, char *argv[])
  {
      dump_trivial_stats_transaction(argv[1], OFPST_PORT);
  }
  
  static void
- do_probe(int argc UNUSED, char *argv[])
 -do_probe(const struct settings *s OVS_UNUSED,
 -         int argc OVS_UNUSED, char *argv[])
++do_probe(int argc OVS_UNUSED, char *argv[])
  {
      struct ofpbuf *request;
      struct vconn *vconn;
  }
  
  static void
- do_mod_port(int argc UNUSED, char *argv[])
 -do_mod_port(const struct settings *s OVS_UNUSED,
 -            int argc OVS_UNUSED, char *argv[])
++do_mod_port(int argc OVS_UNUSED, char *argv[])
  {
      struct ofpbuf *request, *reply;
      struct ofp_switch_features *osf;
  }
  
  static void
 -do_ping(const struct settings *s OVS_UNUSED, int argc, char *argv[])
 +do_ping(int argc, char *argv[])
  {
      size_t max_payload = 65535 - sizeof(struct ofp_header);
      unsigned int payload;
  }
  
  static void
- do_benchmark(int argc UNUSED, char *argv[])
 -do_benchmark(const struct settings *s OVS_UNUSED,
 -             int argc OVS_UNUSED, char *argv[])
++do_benchmark(int argc OVS_UNUSED, char *argv[])
  {
      size_t max_payload = 65535 - sizeof(struct ofp_header);
      struct timeval start, end;
  }
  
  static void
- do_help(int argc UNUSED, char *argv[] UNUSED)
 -do_execute(const struct settings *s OVS_UNUSED, int argc, char *argv[])
 -{
 -    struct vconn *vconn;
 -    struct ofpbuf *request;
 -    struct nicira_header *nicira;
 -    struct nx_command_reply *ncr;
 -    uint32_t xid;
 -    int i;
 -
 -    nicira = make_openflow(sizeof *nicira, OFPT_VENDOR, &request);
 -    xid = nicira->header.xid;
 -    nicira->vendor = htonl(NX_VENDOR_ID);
 -    nicira->subtype = htonl(NXT_COMMAND_REQUEST);
 -    ofpbuf_put(request, argv[2], strlen(argv[2]));
 -    for (i = 3; i < argc; i++) {
 -        ofpbuf_put_zeros(request, 1);
 -        ofpbuf_put(request, argv[i], strlen(argv[i]));
 -    }
 -    update_openflow_length(request);
 -
 -    open_vconn(argv[1], &vconn);
 -    run(vconn_send_block(vconn, request), "send");
 -
 -    for (;;) {
 -        struct ofpbuf *reply;
 -        uint32_t status;
 -
 -        run(vconn_recv_xid(vconn, xid, &reply), "recv_xid");
 -        if (reply->size < sizeof *ncr) {
 -            ovs_fatal(0, "reply is too short (%zu bytes < %zu bytes)",
 -                      reply->size, sizeof *ncr);
 -        }
 -        ncr = reply->data;
 -        if (ncr->nxh.header.type != OFPT_VENDOR
 -            || ncr->nxh.vendor != htonl(NX_VENDOR_ID)
 -            || ncr->nxh.subtype != htonl(NXT_COMMAND_REPLY)) {
 -            ovs_fatal(0, "reply is invalid");
 -        }
 -
 -        status = ntohl(ncr->status);
 -        if (status & NXT_STATUS_STARTED) {
 -            /* Wait for a second reply. */
 -            continue;
 -        } else if (status & NXT_STATUS_EXITED) {
 -            fprintf(stderr, "process terminated normally with exit code %d",
 -                    status & NXT_STATUS_EXITSTATUS);
 -        } else if (status & NXT_STATUS_SIGNALED) {
 -            fprintf(stderr, "process terminated by signal %d",
 -                    status & NXT_STATUS_TERMSIG);
 -        } else if (status & NXT_STATUS_ERROR) {
 -            fprintf(stderr, "error executing command");
 -        } else {
 -            fprintf(stderr, "process terminated for unknown reason");
 -        }
 -        if (status & NXT_STATUS_COREDUMP) {
 -            fprintf(stderr, " (core dumped)");
 -        }
 -        putc('\n', stderr);
 -
 -        fwrite(ncr + 1, reply->size - sizeof *ncr, 1, stdout);
 -        break;
 -    }
 -}
 -
 -static void
 -do_help(const struct settings *s OVS_UNUSED,
 -        int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
++do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
  {
      usage();
  }
  
 -static struct command all_commands[] = {
 +static const struct command all_commands[] = {
      { "show", 1, 1, do_show },
      { "status", 1, 2, do_status },
      { "monitor", 1, 3, do_monitor },
      { "probe", 1, 1, do_probe },
      { "ping", 1, 2, do_ping },
      { "benchmark", 3, 3, do_benchmark },
 -    { "execute", 2, INT_MAX, do_execute },
      { "help", 0, INT_MAX, do_help },
      { NULL, 0, 0, NULL },
  };
diff --combined utilities/ovs-vsctl.c
index 498a0b0,0000000..84db728
mode 100644,000000..100644
--- /dev/null
@@@ -1,2519 -1,0 +1,2519 @@@
- cmd_init(struct vsctl_context *ctx UNUSED)
 +/*
 + * Copyright (c) 2009, 2010 Nicira Networks.
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at:
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <config.h>
 +
 +#include <assert.h>
 +#include <ctype.h>
 +#include <errno.h>
 +#include <float.h>
 +#include <getopt.h>
 +#include <inttypes.h>
 +#include <signal.h>
 +#include <stdarg.h>
 +#include <stdlib.h>
 +#include <string.h>
 +
 +#include "command-line.h"
 +#include "compiler.h"
 +#include "dirs.h"
 +#include "dynamic-string.h"
 +#include "json.h"
 +#include "ovsdb-data.h"
 +#include "ovsdb-idl.h"
 +#include "poll-loop.h"
 +#include "process.h"
 +#include "svec.h"
 +#include "vswitchd/vswitch-idl.h"
 +#include "timeval.h"
 +#include "util.h"
 +
 +#include "vlog.h"
 +#define THIS_MODULE VLM_vsctl
 +
 +/* vsctl_fatal() also logs the error, so it is preferred in this file. */
 +#define ovs_fatal please_use_vsctl_fatal_instead_of_ovs_fatal
 +
 +struct vsctl_context;
 +
 +typedef void vsctl_handler_func(struct vsctl_context *);
 +
 +struct vsctl_command_syntax {
 +    const char *name;
 +    int min_args;
 +    int max_args;
 +    vsctl_handler_func *run;
 +    vsctl_handler_func *postprocess;
 +    const char *options;
 +};
 +
 +struct vsctl_command {
 +    /* Data that remains constant after initialization. */
 +    const struct vsctl_command_syntax *syntax;
 +    int argc;
 +    char **argv;
 +    struct shash options;
 +
 +    /* Data modified by commands. */
 +    struct ds output;
 +};
 +
 +/* --db: The database server to contact. */
 +static const char *db;
 +
 +/* --oneline: Write each command's output as a single line? */
 +static bool oneline;
 +
 +/* --dry-run: Do not commit any changes. */
 +static bool dry_run;
 +
 +/* --no-wait: Wait for ovs-vswitchd to reload its configuration? */
 +static bool wait_for_reload = true;
 +
 +/* --timeout: Time to wait for a connection to 'db'. */
 +static int timeout = 5;
 +
 +/* All supported commands. */
 +static const struct vsctl_command_syntax all_commands[];
 +
 +/* The IDL we're using and the current transaction, if any.
 + * This is for use by vsctl_exit() only, to allow it to clean up.
 + * Other code should use its context arguments. */
 +static struct ovsdb_idl *the_idl;
 +static struct ovsdb_idl_txn *the_idl_txn;
 +
 +static void vsctl_exit(int status) NO_RETURN;
 +static void vsctl_fatal(const char *, ...) PRINTF_FORMAT(1, 2) NO_RETURN;
 +static char *default_db(void);
 +static void usage(void) NO_RETURN;
 +static void parse_options(int argc, char *argv[]);
 +
 +static struct vsctl_command *parse_commands(int argc, char *argv[],
 +                                            size_t *n_commandsp);
 +static void parse_command(int argc, char *argv[], struct vsctl_command *);
 +static void do_vsctl(const char *args,
 +                     struct vsctl_command *, size_t n_commands,
 +                     struct ovsdb_idl *);
 +
 +int
 +main(int argc, char *argv[])
 +{
 +    struct ovsdb_idl *idl;
 +    unsigned int seqno;
 +    struct vsctl_command *commands;
 +    size_t n_commands;
 +    char *args;
 +    int trials;
 +
 +    set_program_name(argv[0]);
 +    signal(SIGPIPE, SIG_IGN);
 +    time_init();
 +    vlog_init();
 +    vlog_set_levels(VLM_ANY_MODULE, VLF_CONSOLE, VLL_WARN);
 +    vlog_set_levels(VLM_reconnect, VLF_ANY_FACILITY, VLL_WARN);
 +    ovsrec_init();
 +
 +    /* Log our arguments.  This is often valuable for debugging systems. */
 +    args = process_escape_args(argv);
 +    VLOG_INFO("Called as %s", args);
 +
 +    /* Parse command line. */
 +    parse_options(argc, argv);
 +    commands = parse_commands(argc - optind, argv + optind, &n_commands);
 +
 +    if (timeout) {
 +        time_alarm(timeout);
 +    }
 +
 +    /* Now execute the commands. */
 +    idl = the_idl = ovsdb_idl_create(db, &ovsrec_idl_class);
 +    seqno = ovsdb_idl_get_seqno(idl);
 +    trials = 0;
 +    for (;;) {
 +        unsigned int new_seqno;
 +
 +        ovsdb_idl_run(idl);
 +        new_seqno = ovsdb_idl_get_seqno(idl);
 +        if (new_seqno != seqno) {
 +            if (++trials > 5) {
 +                vsctl_fatal("too many database inconsistency failures");
 +            }
 +            do_vsctl(args, commands, n_commands, idl);
 +            seqno = new_seqno;
 +        }
 +
 +        ovsdb_idl_wait(idl);
 +        poll_block();
 +    }
 +}
 +
 +static void
 +parse_options(int argc, char *argv[])
 +{
 +    enum {
 +        OPT_DB = UCHAR_MAX + 1,
 +        OPT_ONELINE,
 +        OPT_NO_SYSLOG,
 +        OPT_NO_WAIT,
 +        OPT_DRY_RUN,
 +        VLOG_OPTION_ENUMS
 +    };
 +    static struct option long_options[] = {
 +        {"db", required_argument, 0, OPT_DB},
 +        {"no-syslog", no_argument, 0, OPT_NO_SYSLOG},
 +        {"no-wait", no_argument, 0, OPT_NO_WAIT},
 +        {"dry-run", no_argument, 0, OPT_DRY_RUN},
 +        {"oneline", no_argument, 0, OPT_ONELINE},
 +        {"timeout", required_argument, 0, 't'},
 +        {"help", no_argument, 0, 'h'},
 +        {"version", no_argument, 0, 'V'},
 +        VLOG_LONG_OPTIONS,
 +        {0, 0, 0, 0},
 +    };
 +
 +
 +    for (;;) {
 +        int c;
 +
 +        c = getopt_long(argc, argv, "+v::hVt:", long_options, NULL);
 +        if (c == -1) {
 +            break;
 +        }
 +
 +        switch (c) {
 +        case OPT_DB:
 +            db = optarg;
 +            break;
 +
 +        case OPT_ONELINE:
 +            oneline = true;
 +            break;
 +
 +        case OPT_NO_SYSLOG:
 +            vlog_set_levels(VLM_vsctl, VLF_SYSLOG, VLL_WARN);
 +            break;
 +
 +        case OPT_NO_WAIT:
 +            wait_for_reload = false;
 +            break;
 +
 +        case OPT_DRY_RUN:
 +            dry_run = true;
 +            break;
 +
 +        case 'h':
 +            usage();
 +
 +        case 'V':
 +            OVS_PRINT_VERSION(0, 0);
 +            exit(EXIT_SUCCESS);
 +
 +        case 't':
 +            timeout = strtoul(optarg, NULL, 10);
 +            if (timeout < 0) {
 +                vsctl_fatal("value %s on -t or --timeout is invalid",
 +                            optarg);
 +            }
 +            break;
 +
 +        VLOG_OPTION_HANDLERS
 +
 +        case '?':
 +            exit(EXIT_FAILURE);
 +
 +        default:
 +            abort();
 +        }
 +    }
 +
 +    if (!db) {
 +        db = default_db();
 +    }
 +}
 +
 +static struct vsctl_command *
 +parse_commands(int argc, char *argv[], size_t *n_commandsp)
 +{
 +    struct vsctl_command *commands;
 +    size_t n_commands, allocated_commands;
 +    int i, start;
 +
 +    commands = NULL;
 +    n_commands = allocated_commands = 0;
 +
 +    for (start = i = 0; i <= argc; i++) {
 +        if (i == argc || !strcmp(argv[i], "--")) {
 +            if (i > start) {
 +                if (n_commands >= allocated_commands) {
 +                    struct vsctl_command *c;
 +
 +                    commands = x2nrealloc(commands, &allocated_commands,
 +                                          sizeof *commands);
 +                    for (c = commands; c < &commands[n_commands]; c++) {
 +                        shash_moved(&c->options);
 +                    }
 +                }
 +                parse_command(i - start, &argv[start],
 +                              &commands[n_commands++]);
 +            }
 +            start = i + 1;
 +        }
 +    }
 +    if (!n_commands) {
 +        vsctl_fatal("missing command name (use --help for help)");
 +    }
 +    *n_commandsp = n_commands;
 +    return commands;
 +}
 +
 +static void
 +parse_command(int argc, char *argv[], struct vsctl_command *command)
 +{
 +    const struct vsctl_command_syntax *p;
 +    int i;
 +
 +    shash_init(&command->options);
 +    for (i = 0; i < argc; i++) {
 +        if (argv[i][0] != '-') {
 +            break;
 +        }
 +        if (!shash_add_once(&command->options, argv[i], NULL)) {
 +            vsctl_fatal("'%s' option specified multiple times", argv[i]);
 +        }
 +    }
 +    if (i == argc) {
 +        vsctl_fatal("missing command name");
 +    }
 +
 +    for (p = all_commands; p->name; p++) {
 +        if (!strcmp(p->name, argv[i])) {
 +            struct shash_node *node;
 +            int n_arg;
 +
 +            SHASH_FOR_EACH (node, &command->options) {
 +                const char *s = strstr(p->options, node->name);
 +                int end = s ? s[strlen(node->name)] : EOF;
 +                if (end != ',' && end != ' ' && end != '\0') {
 +                    vsctl_fatal("'%s' command has no '%s' option",
 +                                argv[i], node->name);
 +                }
 +            }
 +
 +            n_arg = argc - i - 1;
 +            if (n_arg < p->min_args) {
 +                vsctl_fatal("'%s' command requires at least %d arguments",
 +                            p->name, p->min_args);
 +            } else if (n_arg > p->max_args) {
 +                int j;
 +
 +                for (j = i + 1; j < argc; j++) {
 +                    if (argv[j][0] == '-') {
 +                        vsctl_fatal("'%s' command takes at most %d arguments "
 +                                    "(note that options must precede command "
 +                                    "names and follow a \"--\" argument)",
 +                                    p->name, p->max_args);
 +                    }
 +                }
 +
 +                vsctl_fatal("'%s' command takes at most %d arguments",
 +                            p->name, p->max_args);
 +            } else {
 +                command->syntax = p;
 +                command->argc = n_arg + 1;
 +                command->argv = &argv[i];
 +                return;
 +            }
 +        }
 +    }
 +
 +    vsctl_fatal("unknown command '%s'; use --help for help", argv[i]);
 +}
 +
 +static void
 +vsctl_fatal(const char *format, ...)
 +{
 +    char *message;
 +    va_list args;
 +
 +    va_start(args, format);
 +    message = xvasprintf(format, args);
 +    va_end(args);
 +
 +    vlog_set_levels(VLM_vsctl, VLF_CONSOLE, VLL_EMER);
 +    VLOG_ERR("%s", message);
 +    ovs_error(0, "%s", message);
 +    vsctl_exit(EXIT_FAILURE);
 +}
 +
 +/* Frees the current transaction and the underlying IDL and then calls
 + * exit(status).
 + *
 + * Freeing the transaction and the IDL is not strictly necessary, but it makes
 + * for a clean memory leak report from valgrind in the normal case.  That makes
 + * it easier to notice real memory leaks. */
 +static void
 +vsctl_exit(int status)
 +{
 +    if (the_idl_txn) {
 +        ovsdb_idl_txn_abort(the_idl_txn);
 +        ovsdb_idl_txn_destroy(the_idl_txn);
 +    }
 +    ovsdb_idl_destroy(the_idl);
 +    exit(status);
 +}
 +
 +static void
 +usage(void)
 +{
 +    printf("\
 +%s: ovs-vswitchd management utility\n\
 +usage: %s [OPTIONS] COMMAND [ARG...]\n\
 +\n\
 +Bridge commands:\n\
 +  add-br BRIDGE               create a new bridge named BRIDGE\n\
 +  add-br BRIDGE PARENT VLAN   create new fake BRIDGE in PARENT on VLAN\n\
 +  del-br BRIDGE               delete BRIDGE and all of its ports\n\
 +  list-br                     print the names of all the bridges\n\
 +  br-exists BRIDGE            test whether BRIDGE exists\n\
 +  br-to-vlan BRIDGE           print the VLAN which BRIDGE is on\n\
 +  br-to-parent BRIDGE         print the parent of BRIDGE\n\
 +  br-set-external-id BRIDGE KEY VALUE  set KEY on BRIDGE to VALUE\n\
 +  br-set-external-id BRIDGE KEY  unset KEY on BRIDGE\n\
 +  br-get-external-id BRIDGE KEY  print value of KEY on BRIDGE\n\
 +  br-get-external-id BRIDGE  list key-value pairs on BRIDGE\n\
 +\n\
 +Port commands:\n\
 +  list-ports BRIDGE           print the names of all the ports on BRIDGE\n\
 +  add-port BRIDGE PORT        add network device PORT to BRIDGE\n\
 +  add-bond BRIDGE PORT IFACE...  add bonded port PORT in BRIDGE from IFACES\n\
 +  del-port [BRIDGE] PORT      delete PORT (which may be bonded) from BRIDGE\n\
 +  port-to-br PORT             print name of bridge that contains PORT\n\
 +A bond is considered to be a single port.\n\
 +\n\
 +Interface commands (a bond consists of multiple interfaces):\n\
 +  list-ifaces BRIDGE          print the names of all interfaces on BRIDGE\n\
 +  iface-to-br IFACE           print name of bridge that contains IFACE\n\
 +\n\
 +Controller commands:\n\
 +  get-controller [BRIDGE]     print the controller for BRIDGE\n\
 +  del-controller [BRIDGE]     delete the controller for BRIDGE\n\
 +  set-controller [BRIDGE] TARGET  set the controller for BRIDGE to TARGET\n\
 +  get-fail-mode [BRIDGE]      print the fail-mode for BRIDGE\n\
 +  del-fail-mode [BRIDGE]      delete the fail-mode for BRIDGE\n\
 +  set-fail-mode [BRIDGE] MODE set the fail-mode for BRIDGE to MODE\n\
 +\n\
 +SSL commands:\n\
 +  get-ssl                     print the SSL configuration\n\
 +  del-ssl                     delete the SSL configuration\n\
 +  set-ssl PRIV-KEY CERT CA-CERT  set the SSL configuration\n\
 +\n\
 +Database commands:\n\
 +  list TBL [REC]              list RECord (or all records) in TBL\n\
 +  get TBL REC COL[:KEY]       print values of COLumns in RECORD in TBL\n\
 +  set TBL REC COL[:KEY]=VALUE set COLumn values in RECord in TBL\n\
 +  add TBL REC COL [KEY=]VALUE add (KEY=)VALUE to COLumn in RECord in TBL\n\
 +  remove TBL REC COL [KEY=]VALUE  remove (KEY=)VALUE from COLumn\n\
 +  clear TBL REC COL           clear values from COLumn in RECord in TBL\n\
 +  create TBL COL[:KEY]=VALUE  create and initialize new record\n\
 +  destroy TBL REC             delete REC from TBL\n\
 +Potentially unsafe database commands require --force option.\n\
 +\n\
 +Options:\n\
 +  --db=DATABASE               connect to DATABASE\n\
 +                              (default: %s)\n\
 +  --oneline                   print exactly one line of output per command\n",
 +           program_name, program_name, default_db());
 +    vlog_usage();
 +    printf("\n\
 +Other options:\n\
 +  -h, --help                  display this help message\n\
 +  -V, --version               display version information\n");
 +    exit(EXIT_SUCCESS);
 +}
 +
 +static char *
 +default_db(void)
 +{
 +    static char *def;
 +    if (!def) {
 +        def = xasprintf("unix:%s/ovsdb-server", ovs_rundir);
 +    }
 +    return def;
 +}
 +\f
 +struct vsctl_context {
 +    /* Read-only. */
 +    int argc;
 +    char **argv;
 +    struct shash options;
 +
 +    /* Modifiable state. */
 +    struct ds output;
 +    struct ovsdb_idl *idl;
 +    struct ovsdb_idl_txn *txn;
 +    const struct ovsrec_open_vswitch *ovs;
 +};
 +
 +struct vsctl_bridge {
 +    struct ovsrec_bridge *br_cfg;
 +    char *name;
 +    struct ovsrec_controller *ctrl;
 +    struct vsctl_bridge *parent;
 +    int vlan;
 +};
 +
 +struct vsctl_port {
 +    struct ovsrec_port *port_cfg;
 +    struct vsctl_bridge *bridge;
 +};
 +
 +struct vsctl_iface {
 +    struct ovsrec_interface *iface_cfg;
 +    struct vsctl_port *port;
 +};
 +
 +struct vsctl_info {
 +    struct shash bridges;
 +    struct shash ports;
 +    struct shash ifaces;
 +    struct ovsrec_controller *ctrl;
 +};
 +
 +static char *
 +vsctl_context_to_string(const struct vsctl_context *ctx)
 +{
 +    const struct shash_node *node;
 +    struct svec words;
 +    char *s;
 +    int i;
 +
 +    svec_init(&words);
 +    SHASH_FOR_EACH (node, &ctx->options) {
 +        svec_add(&words, node->name);
 +    }
 +    for (i = 0; i < ctx->argc; i++) {
 +        svec_add(&words, ctx->argv[i]);
 +    }
 +    svec_terminate(&words);
 +
 +    s = process_escape_args(words.names);
 +
 +    svec_destroy(&words);
 +
 +    return s;
 +}
 +
 +static struct vsctl_bridge *
 +add_bridge(struct vsctl_info *b,
 +           struct ovsrec_bridge *br_cfg, const char *name,
 +           struct vsctl_bridge *parent, int vlan)
 +{
 +    struct vsctl_bridge *br = xmalloc(sizeof *br);
 +    br->br_cfg = br_cfg;
 +    br->name = xstrdup(name);
 +    br->parent = parent;
 +    br->vlan = vlan;
 +    br->ctrl = parent ? parent->br_cfg->controller : br_cfg->controller;
 +    shash_add(&b->bridges, br->name, br);
 +    return br;
 +}
 +
 +static bool
 +port_is_fake_bridge(const struct ovsrec_port *port_cfg)
 +{
 +    return (port_cfg->fake_bridge
 +            && port_cfg->tag
 +            && *port_cfg->tag >= 1 && *port_cfg->tag <= 4095);
 +}
 +
 +static struct vsctl_bridge *
 +find_vlan_bridge(struct vsctl_info *info,
 +                 struct vsctl_bridge *parent, int vlan)
 +{
 +    struct shash_node *node;
 +
 +    SHASH_FOR_EACH (node, &info->bridges) {
 +        struct vsctl_bridge *br = node->data;
 +        if (br->parent == parent && br->vlan == vlan) {
 +            return br;
 +        }
 +    }
 +
 +    return NULL;
 +}
 +
 +static void
 +free_info(struct vsctl_info *info)
 +{
 +    struct shash_node *node;
 +
 +    SHASH_FOR_EACH (node, &info->bridges) {
 +        struct vsctl_bridge *bridge = node->data;
 +        free(bridge->name);
 +        free(bridge);
 +    }
 +    shash_destroy(&info->bridges);
 +
 +    SHASH_FOR_EACH (node, &info->ports) {
 +        struct vsctl_port *port = node->data;
 +        free(port);
 +    }
 +    shash_destroy(&info->ports);
 +
 +    SHASH_FOR_EACH (node, &info->ifaces) {
 +        struct vsctl_iface *iface = node->data;
 +        free(iface);
 +    }
 +    shash_destroy(&info->ifaces);
 +}
 +
 +static void
 +get_info(const struct ovsrec_open_vswitch *ovs, struct vsctl_info *info)
 +{
 +    struct shash bridges, ports;
 +    size_t i;
 +
 +    shash_init(&info->bridges);
 +    shash_init(&info->ports);
 +    shash_init(&info->ifaces);
 +
 +    info->ctrl = ovs->controller;
 +
 +    shash_init(&bridges);
 +    shash_init(&ports);
 +    for (i = 0; i < ovs->n_bridges; i++) {
 +        struct ovsrec_bridge *br_cfg = ovs->bridges[i];
 +        struct vsctl_bridge *br;
 +        size_t j;
 +
 +        if (!shash_add_once(&bridges, br_cfg->name, NULL)) {
 +            VLOG_WARN("%s: database contains duplicate bridge name",
 +                      br_cfg->name);
 +            continue;
 +        }
 +        br = add_bridge(info, br_cfg, br_cfg->name, NULL, 0);
 +        if (!br) {
 +            continue;
 +        }
 +
 +        for (j = 0; j < br_cfg->n_ports; j++) {
 +            struct ovsrec_port *port_cfg = br_cfg->ports[j];
 +
 +            if (!shash_add_once(&ports, port_cfg->name, NULL)) {
 +                VLOG_WARN("%s: database contains duplicate port name",
 +                          port_cfg->name);
 +                continue;
 +            }
 +
 +            if (port_is_fake_bridge(port_cfg)
 +                && shash_add_once(&bridges, port_cfg->name, NULL)) {
 +                add_bridge(info, NULL, port_cfg->name, br, *port_cfg->tag);
 +            }
 +        }
 +    }
 +    shash_destroy(&bridges);
 +    shash_destroy(&ports);
 +
 +    shash_init(&bridges);
 +    shash_init(&ports);
 +    for (i = 0; i < ovs->n_bridges; i++) {
 +        struct ovsrec_bridge *br_cfg = ovs->bridges[i];
 +        struct vsctl_bridge *br;
 +        size_t j;
 +
 +        if (!shash_add_once(&bridges, br_cfg->name, NULL)) {
 +            continue;
 +        }
 +        br = shash_find_data(&info->bridges, br_cfg->name);
 +        for (j = 0; j < br_cfg->n_ports; j++) {
 +            struct ovsrec_port *port_cfg = br_cfg->ports[j];
 +            struct vsctl_port *port;
 +            size_t k;
 +
 +            if (!shash_add_once(&ports, port_cfg->name, NULL)) {
 +                continue;
 +            }
 +
 +            if (port_is_fake_bridge(port_cfg)
 +                && !shash_add_once(&bridges, port_cfg->name, NULL)) {
 +                continue;
 +            }
 +
 +            port = xmalloc(sizeof *port);
 +            port->port_cfg = port_cfg;
 +            if (port_cfg->tag
 +                && *port_cfg->tag >= 1 && *port_cfg->tag <= 4095) {
 +                port->bridge = find_vlan_bridge(info, br, *port_cfg->tag);
 +                if (!port->bridge) {
 +                    port->bridge = br;
 +                }
 +            } else {
 +                port->bridge = br;
 +            }
 +            shash_add(&info->ports, port_cfg->name, port);
 +
 +            for (k = 0; k < port_cfg->n_interfaces; k++) {
 +                struct ovsrec_interface *iface_cfg = port_cfg->interfaces[k];
 +                struct vsctl_iface *iface;
 +
 +                if (shash_find(&info->ifaces, iface_cfg->name)) {
 +                    VLOG_WARN("%s: database contains duplicate interface name",
 +                              iface_cfg->name);
 +                    continue;
 +                }
 +
 +                iface = xmalloc(sizeof *iface);
 +                iface->iface_cfg = iface_cfg;
 +                iface->port = port;
 +                shash_add(&info->ifaces, iface_cfg->name, iface);
 +            }
 +        }
 +    }
 +    shash_destroy(&bridges);
 +    shash_destroy(&ports);
 +}
 +
 +static void
 +check_conflicts(struct vsctl_info *info, const char *name,
 +                char *msg)
 +{
 +    struct vsctl_iface *iface;
 +    struct vsctl_port *port;
 +
 +    if (shash_find(&info->bridges, name)) {
 +        vsctl_fatal("%s because a bridge named %s already exists",
 +                    msg, name);
 +    }
 +
 +    port = shash_find_data(&info->ports, name);
 +    if (port) {
 +        vsctl_fatal("%s because a port named %s already exists on "
 +                    "bridge %s", msg, name, port->bridge->name);
 +    }
 +
 +    iface = shash_find_data(&info->ifaces, name);
 +    if (iface) {
 +        vsctl_fatal("%s because an interface named %s already exists "
 +                    "on bridge %s", msg, name, iface->port->bridge->name);
 +    }
 +
 +    free(msg);
 +}
 +
 +static struct vsctl_bridge *
 +find_bridge(struct vsctl_info *info, const char *name, bool must_exist)
 +{
 +    struct vsctl_bridge *br = shash_find_data(&info->bridges, name);
 +    if (must_exist && !br) {
 +        vsctl_fatal("no bridge named %s", name);
 +    }
 +    return br;
 +}
 +
 +static struct vsctl_bridge *
 +find_real_bridge(struct vsctl_info *info, const char *name, bool must_exist)
 +{
 +    struct vsctl_bridge *br = find_bridge(info, name, must_exist);
 +    if (br && br->parent) {
 +        vsctl_fatal("%s is a fake bridge", name);
 +    }
 +    return br;
 +}
 +
 +static struct vsctl_port *
 +find_port(struct vsctl_info *info, const char *name, bool must_exist)
 +{
 +    struct vsctl_port *port = shash_find_data(&info->ports, name);
 +    if (port && !strcmp(name, port->bridge->name)) {
 +        port = NULL;
 +    }
 +    if (must_exist && !port) {
 +        vsctl_fatal("no port named %s", name);
 +    }
 +    return port;
 +}
 +
 +static struct vsctl_iface *
 +find_iface(struct vsctl_info *info, const char *name, bool must_exist)
 +{
 +    struct vsctl_iface *iface = shash_find_data(&info->ifaces, name);
 +    if (iface && !strcmp(name, iface->port->bridge->name)) {
 +        iface = NULL;
 +    }
 +    if (must_exist && !iface) {
 +        vsctl_fatal("no interface named %s", name);
 +    }
 +    return iface;
 +}
 +
 +static void
 +bridge_insert_port(struct ovsrec_bridge *br, struct ovsrec_port *port)
 +{
 +    struct ovsrec_port **ports;
 +    size_t i;
 +
 +    ports = xmalloc(sizeof *br->ports * (br->n_ports + 1));
 +    for (i = 0; i < br->n_ports; i++) {
 +        ports[i] = br->ports[i];
 +    }
 +    ports[br->n_ports] = port;
 +    ovsrec_bridge_set_ports(br, ports, br->n_ports + 1);
 +    free(ports);
 +}
 +
 +static void
 +bridge_delete_port(struct ovsrec_bridge *br, struct ovsrec_port *port)
 +{
 +    struct ovsrec_port **ports;
 +    size_t i, n;
 +
 +    ports = xmalloc(sizeof *br->ports * br->n_ports);
 +    for (i = n = 0; i < br->n_ports; i++) {
 +        if (br->ports[i] != port) {
 +            ports[n++] = br->ports[i];
 +        }
 +    }
 +    ovsrec_bridge_set_ports(br, ports, n);
 +    free(ports);
 +}
 +
 +static void
 +ovs_insert_bridge(const struct ovsrec_open_vswitch *ovs,
 +                  struct ovsrec_bridge *bridge)
 +{
 +    struct ovsrec_bridge **bridges;
 +    size_t i;
 +
 +    bridges = xmalloc(sizeof *ovs->bridges * (ovs->n_bridges + 1));
 +    for (i = 0; i < ovs->n_bridges; i++) {
 +        bridges[i] = ovs->bridges[i];
 +    }
 +    bridges[ovs->n_bridges] = bridge;
 +    ovsrec_open_vswitch_set_bridges(ovs, bridges, ovs->n_bridges + 1);
 +    free(bridges);
 +}
 +
 +static void
 +ovs_delete_bridge(const struct ovsrec_open_vswitch *ovs,
 +                  struct ovsrec_bridge *bridge)
 +{
 +    struct ovsrec_bridge **bridges;
 +    size_t i, n;
 +
 +    bridges = xmalloc(sizeof *ovs->bridges * ovs->n_bridges);
 +    for (i = n = 0; i < ovs->n_bridges; i++) {
 +        if (ovs->bridges[i] != bridge) {
 +            bridges[n++] = ovs->bridges[i];
 +        }
 +    }
 +    ovsrec_open_vswitch_set_bridges(ovs, bridges, n);
 +    free(bridges);
 +}
 +
 +static void
++cmd_init(struct vsctl_context *ctx OVS_UNUSED)
 +{
 +}
 +
 +static void
 +cmd_add_br(struct vsctl_context *ctx)
 +{
 +    bool may_exist = shash_find(&ctx->options, "--may-exist") != 0;
 +    const char *br_name, *parent_name;
 +    struct vsctl_info info;
 +    int vlan;
 +
 +    br_name = ctx->argv[1];
 +    if (ctx->argc == 2) {
 +        parent_name = NULL;
 +        vlan = 0;
 +    } else if (ctx->argc == 4) {
 +        parent_name = ctx->argv[2];
 +        vlan = atoi(ctx->argv[3]);
 +        if (vlan < 1 || vlan > 4095) {
 +            vsctl_fatal("%s: vlan must be between 1 and 4095", ctx->argv[0]);
 +        }
 +    } else {
 +        vsctl_fatal("'%s' command takes exactly 1 or 3 arguments",
 +                    ctx->argv[0]);
 +    }
 +
 +    get_info(ctx->ovs, &info);
 +    if (may_exist) {
 +        struct vsctl_bridge *br;
 +
 +        br = find_bridge(&info, br_name, false);
 +        if (br) {
 +            if (!parent_name) {
 +                if (br->parent) {
 +                    vsctl_fatal("\"--may-exist add-br %s\" but %s is "
 +                                "a VLAN bridge for VLAN %d",
 +                                br_name, br_name, br->vlan);
 +                }
 +            } else {
 +                if (!br->parent) {
 +                    vsctl_fatal("\"--may-exist add-br %s %s %d\" but %s "
 +                                "is not a VLAN bridge",
 +                                br_name, parent_name, vlan, br_name);
 +                } else if (strcmp(br->parent->name, parent_name)) {
 +                    vsctl_fatal("\"--may-exist add-br %s %s %d\" but %s "
 +                                "has the wrong parent %s",
 +                                br_name, parent_name, vlan,
 +                                br_name, br->parent->name);
 +                } else if (br->vlan != vlan) {
 +                    vsctl_fatal("\"--may-exist add-br %s %s %d\" but %s "
 +                                "is a VLAN bridge for the wrong VLAN %d",
 +                                br_name, parent_name, vlan, br_name, br->vlan);
 +                }
 +            }
 +            return;
 +        }
 +    }
 +    check_conflicts(&info, br_name,
 +                    xasprintf("cannot create a bridge named %s", br_name));
 +
 +    if (!parent_name) {
 +        struct ovsrec_port *port;
 +        struct ovsrec_interface *iface;
 +        struct ovsrec_bridge *br;
 +
 +        iface = ovsrec_interface_insert(ctx->txn);
 +        ovsrec_interface_set_name(iface, br_name);
 +
 +        port = ovsrec_port_insert(ctx->txn);
 +        ovsrec_port_set_name(port, br_name);
 +        ovsrec_port_set_interfaces(port, &iface, 1);
 +
 +        br = ovsrec_bridge_insert(ctx->txn);
 +        ovsrec_bridge_set_name(br, br_name);
 +        ovsrec_bridge_set_ports(br, &port, 1);
 +
 +        ovs_insert_bridge(ctx->ovs, br);
 +    } else {
 +        struct vsctl_bridge *parent;
 +        struct ovsrec_port *port;
 +        struct ovsrec_interface *iface;
 +        struct ovsrec_bridge *br;
 +        int64_t tag = vlan;
 +
 +        parent = find_bridge(&info, parent_name, false);
 +        if (parent && parent->vlan) {
 +            vsctl_fatal("cannot create bridge with fake bridge as parent");
 +        }
 +        if (!parent) {
 +            vsctl_fatal("parent bridge %s does not exist", parent_name);
 +        }
 +        br = parent->br_cfg;
 +
 +        iface = ovsrec_interface_insert(ctx->txn);
 +        ovsrec_interface_set_name(iface, br_name);
 +        ovsrec_interface_set_type(iface, "internal");
 +
 +        port = ovsrec_port_insert(ctx->txn);
 +        ovsrec_port_set_name(port, br_name);
 +        ovsrec_port_set_interfaces(port, &iface, 1);
 +        ovsrec_port_set_fake_bridge(port, true);
 +        ovsrec_port_set_tag(port, &tag, 1);
 +
 +        bridge_insert_port(br, port);
 +    }
 +
 +    free_info(&info);
 +}
 +
 +static void
 +del_port(struct vsctl_info *info, struct vsctl_port *port)
 +{
 +    struct shash_node *node;
 +
 +    SHASH_FOR_EACH (node, &info->ifaces) {
 +        struct vsctl_iface *iface = node->data;
 +        if (iface->port == port) {
 +            ovsrec_interface_delete(iface->iface_cfg);
 +        }
 +    }
 +    ovsrec_port_delete(port->port_cfg);
 +
 +    bridge_delete_port((port->bridge->parent
 +                        ? port->bridge->parent->br_cfg
 +                        : port->bridge->br_cfg), port->port_cfg);
 +}
 +
 +static void
 +cmd_del_br(struct vsctl_context *ctx)
 +{
 +    bool must_exist = !shash_find(&ctx->options, "--if-exists");
 +    struct vsctl_bridge *bridge;
 +    struct vsctl_info info;
 +
 +    get_info(ctx->ovs, &info);
 +    bridge = find_bridge(&info, ctx->argv[1], must_exist);
 +    if (bridge) {
 +        struct shash_node *node;
 +
 +        SHASH_FOR_EACH (node, &info.ports) {
 +            struct vsctl_port *port = node->data;
 +            if (port->bridge == bridge || port->bridge->parent == bridge
 +                || !strcmp(port->port_cfg->name, bridge->name)) {
 +                del_port(&info, port);
 +            }
 +        }
 +        if (bridge->br_cfg) {
 +            ovsrec_bridge_delete(bridge->br_cfg);
 +            ovs_delete_bridge(ctx->ovs, bridge->br_cfg);
 +        }
 +    }
 +    free_info(&info);
 +}
 +
 +static void
 +output_sorted(struct svec *svec, struct ds *output)
 +{
 +    const char *name;
 +    size_t i;
 +
 +    svec_sort(svec);
 +    SVEC_FOR_EACH (i, name, svec) {
 +        ds_put_format(output, "%s\n", name);
 +    }
 +}
 +
 +static void
 +cmd_list_br(struct vsctl_context *ctx)
 +{
 +    struct shash_node *node;
 +    struct vsctl_info info;
 +    struct svec bridges;
 +
 +    get_info(ctx->ovs, &info);
 +
 +    svec_init(&bridges);
 +    SHASH_FOR_EACH (node, &info.bridges) {
 +        struct vsctl_bridge *br = node->data;
 +        svec_add(&bridges, br->name);
 +    }
 +    output_sorted(&bridges, &ctx->output);
 +    svec_destroy(&bridges);
 +
 +    free_info(&info);
 +}
 +
 +static void
 +cmd_br_exists(struct vsctl_context *ctx)
 +{
 +    struct vsctl_info info;
 +
 +    get_info(ctx->ovs, &info);
 +    if (!find_bridge(&info, ctx->argv[1], false)) {
 +        vsctl_exit(2);
 +    }
 +    free_info(&info);
 +}
 +
 +/* Returns true if 'b_prefix' (of length 'b_prefix_len') concatenated with 'b'
 + * equals 'a', false otherwise. */
 +static bool
 +key_matches(const char *a,
 +            const char *b_prefix, size_t b_prefix_len, const char *b)
 +{
 +    return !strncmp(a, b_prefix, b_prefix_len) && !strcmp(a + b_prefix_len, b);
 +}
 +
 +static void
 +set_external_id(char **old_keys, char **old_values, size_t old_n,
 +                char *key, char *value,
 +                char ***new_keysp, char ***new_valuesp, size_t *new_np)
 +{
 +    char **new_keys;
 +    char **new_values;
 +    size_t new_n;
 +    size_t i;
 +
 +    new_keys = xmalloc(sizeof *new_keys * (old_n + 1));
 +    new_values = xmalloc(sizeof *new_values * (old_n + 1));
 +    new_n = 0;
 +    for (i = 0; i < old_n; i++) {
 +        if (strcmp(key, old_keys[i])) {
 +            new_keys[new_n] = old_keys[i];
 +            new_values[new_n] = old_values[i];
 +            new_n++;
 +        }
 +    }
 +    if (value) {
 +        new_keys[new_n] = key;
 +        new_values[new_n] = value;
 +        new_n++;
 +    }
 +    *new_keysp = new_keys;
 +    *new_valuesp = new_values;
 +    *new_np = new_n;
 +}
 +
 +static void
 +cmd_br_set_external_id(struct vsctl_context *ctx)
 +{
 +    struct vsctl_info info;
 +    struct vsctl_bridge *bridge;
 +    char **keys, **values;
 +    size_t n;
 +
 +    get_info(ctx->ovs, &info);
 +    bridge = find_bridge(&info, ctx->argv[1], true);
 +    if (bridge->br_cfg) {
 +        set_external_id(bridge->br_cfg->key_external_ids,
 +                        bridge->br_cfg->value_external_ids,
 +                        bridge->br_cfg->n_external_ids,
 +                        ctx->argv[2], ctx->argc >= 4 ? ctx->argv[3] : NULL,
 +                        &keys, &values, &n);
 +        ovsrec_bridge_set_external_ids(bridge->br_cfg, keys, values, n);
 +    } else {
 +        char *key = xasprintf("fake-bridge-%s", ctx->argv[2]);
 +        struct vsctl_port *port = shash_find_data(&info.ports, ctx->argv[1]);
 +        set_external_id(port->port_cfg->key_external_ids,
 +                        port->port_cfg->value_external_ids,
 +                        port->port_cfg->n_external_ids,
 +                        key, ctx->argc >= 4 ? ctx->argv[3] : NULL,
 +                        &keys, &values, &n);
 +        ovsrec_port_set_external_ids(port->port_cfg, keys, values, n);
 +        free(key);
 +    }
 +    free(keys);
 +    free(values);
 +
 +    free_info(&info);
 +}
 +
 +static void
 +get_external_id(char **keys, char **values, size_t n,
 +                const char *prefix, const char *key,
 +                struct ds *output)
 +{
 +    size_t prefix_len = strlen(prefix);
 +    struct svec svec;
 +    size_t i;
 +
 +    svec_init(&svec);
 +    for (i = 0; i < n; i++) {
 +        if (!key && !strncmp(keys[i], prefix, prefix_len)) {
 +            svec_add_nocopy(&svec, xasprintf("%s=%s",
 +                                             keys[i] + prefix_len, values[i]));
 +        } else if (key_matches(keys[i], prefix, prefix_len, key)) {
 +            svec_add(&svec, values[i]);
 +            break;
 +        }
 +    }
 +    output_sorted(&svec, output);
 +    svec_destroy(&svec);
 +}
 +
 +static void
 +cmd_br_get_external_id(struct vsctl_context *ctx)
 +{
 +    struct vsctl_info info;
 +    struct vsctl_bridge *bridge;
 +
 +    get_info(ctx->ovs, &info);
 +    bridge = find_bridge(&info, ctx->argv[1], true);
 +    if (bridge->br_cfg) {
 +        get_external_id(bridge->br_cfg->key_external_ids,
 +                        bridge->br_cfg->value_external_ids,
 +                        bridge->br_cfg->n_external_ids,
 +                        "", ctx->argc >= 3 ? ctx->argv[2] : NULL,
 +                        &ctx->output);
 +    } else {
 +        struct vsctl_port *port = shash_find_data(&info.ports, ctx->argv[1]);
 +        get_external_id(port->port_cfg->key_external_ids,
 +                        port->port_cfg->value_external_ids,
 +                        port->port_cfg->n_external_ids,
 +                        "fake-bridge-", ctx->argc >= 3 ? ctx->argv[2] : NULL, &ctx->output);
 +    }
 +    free_info(&info);
 +}
 +
 +
 +static void
 +cmd_list_ports(struct vsctl_context *ctx)
 +{
 +    struct vsctl_bridge *br;
 +    struct shash_node *node;
 +    struct vsctl_info info;
 +    struct svec ports;
 +
 +    get_info(ctx->ovs, &info);
 +    br = find_bridge(&info, ctx->argv[1], true);
 +
 +    svec_init(&ports);
 +    SHASH_FOR_EACH (node, &info.ports) {
 +        struct vsctl_port *port = node->data;
 +
 +        if (strcmp(port->port_cfg->name, br->name) && br == port->bridge) {
 +            svec_add(&ports, port->port_cfg->name);
 +        }
 +    }
 +    output_sorted(&ports, &ctx->output);
 +    svec_destroy(&ports);
 +
 +    free_info(&info);
 +}
 +
 +static void
 +add_port(struct vsctl_context *ctx,
 +         const char *br_name, const char *port_name,
 +         bool may_exist, bool fake_iface,
 +         char *iface_names[], int n_ifaces)
 +{
 +    struct vsctl_info info;
 +    struct vsctl_bridge *bridge;
 +    struct ovsrec_interface **ifaces;
 +    struct ovsrec_port *port;
 +    size_t i;
 +
 +    get_info(ctx->ovs, &info);
 +    if (may_exist) {
 +        struct vsctl_port *port;
 +
 +        port = find_port(&info, port_name, false);
 +        if (port) {
 +            struct svec want_names, have_names;
 +            size_t i;
 +
 +            svec_init(&want_names);
 +            for (i = 0; i < n_ifaces; i++) {
 +                svec_add(&want_names, iface_names[i]);
 +            }
 +            svec_sort(&want_names);
 +
 +            svec_init(&have_names);
 +            for (i = 0; i < port->port_cfg->n_interfaces; i++) {
 +                svec_add(&have_names, port->port_cfg->interfaces[i]->name);
 +            }
 +            svec_sort(&have_names);
 +
 +            if (strcmp(port->bridge->name, br_name)) {
 +                char *command = vsctl_context_to_string(ctx);
 +                vsctl_fatal("\"%s\" but %s is actually attached to bridge %s",
 +                            command, port_name, port->bridge->name);
 +            }
 +
 +            if (!svec_equal(&want_names, &have_names)) {
 +                char *have_names_string = svec_join(&have_names, ", ", "");
 +                char *command = vsctl_context_to_string(ctx);
 +
 +                vsctl_fatal("\"%s\" but %s actually has interface(s) %s",
 +                            command, port_name, have_names_string);
 +            }
 +
 +            svec_destroy(&want_names);
 +            svec_destroy(&have_names);
 +
 +            return;
 +        }
 +    }
 +    check_conflicts(&info, port_name,
 +                    xasprintf("cannot create a port named %s", port_name));
 +    for (i = 0; i < n_ifaces; i++) {
 +        check_conflicts(&info, iface_names[i],
 +                        xasprintf("cannot create an interface named %s",
 +                                  iface_names[i]));
 +    }
 +    bridge = find_bridge(&info, br_name, true);
 +
 +    ifaces = xmalloc(n_ifaces * sizeof *ifaces);
 +    for (i = 0; i < n_ifaces; i++) {
 +        ifaces[i] = ovsrec_interface_insert(ctx->txn);
 +        ovsrec_interface_set_name(ifaces[i], iface_names[i]);
 +    }
 +
 +    port = ovsrec_port_insert(ctx->txn);
 +    ovsrec_port_set_name(port, port_name);
 +    ovsrec_port_set_interfaces(port, ifaces, n_ifaces);
 +    ovsrec_port_set_bond_fake_iface(port, fake_iface);
 +    free(ifaces);
 +
 +    if (bridge->vlan) {
 +        int64_t tag = bridge->vlan;
 +        ovsrec_port_set_tag(port, &tag, 1);
 +    }
 +
 +    bridge_insert_port((bridge->parent ? bridge->parent->br_cfg
 +                        : bridge->br_cfg), port);
 +
 +    free_info(&info);
 +}
 +
 +static void
 +cmd_add_port(struct vsctl_context *ctx)
 +{
 +    bool may_exist = shash_find(&ctx->options, "--may-exist") != 0;
 +
 +    add_port(ctx, ctx->argv[1], ctx->argv[2], may_exist, false,
 +             &ctx->argv[2], 1);
 +}
 +
 +static void
 +cmd_add_bond(struct vsctl_context *ctx)
 +{
 +    bool may_exist = shash_find(&ctx->options, "--may-exist") != 0;
 +    bool fake_iface = shash_find(&ctx->options, "--fake-iface");
 +
 +    add_port(ctx, ctx->argv[1], ctx->argv[2], may_exist, fake_iface,
 +             &ctx->argv[3], ctx->argc - 3);
 +}
 +
 +static void
 +cmd_del_port(struct vsctl_context *ctx)
 +{
 +    bool must_exist = !shash_find(&ctx->options, "--if-exists");
 +    struct vsctl_info info;
 +
 +    get_info(ctx->ovs, &info);
 +    if (ctx->argc == 2) {
 +        struct vsctl_port *port = find_port(&info, ctx->argv[1], must_exist);
 +        if (port) {
 +            del_port(&info, port);
 +        }
 +    } else if (ctx->argc == 3) {
 +        struct vsctl_bridge *bridge = find_bridge(&info, ctx->argv[1], true);
 +        struct vsctl_port *port = find_port(&info, ctx->argv[2], must_exist);
 +
 +        if (port) {
 +            if (port->bridge == bridge) {
 +                del_port(&info, port);
 +            } else if (port->bridge->parent == bridge) {
 +                vsctl_fatal("bridge %s does not have a port %s (although its "
 +                            "parent bridge %s does)",
 +                            ctx->argv[1], ctx->argv[2], bridge->parent->name);
 +            } else {
 +                vsctl_fatal("bridge %s does not have a port %s",
 +                            ctx->argv[1], ctx->argv[2]);
 +            }
 +        }
 +    }
 +    free_info(&info);
 +}
 +
 +static void
 +cmd_port_to_br(struct vsctl_context *ctx)
 +{
 +    struct vsctl_port *port;
 +    struct vsctl_info info;
 +
 +    get_info(ctx->ovs, &info);
 +    port = find_port(&info, ctx->argv[1], true);
 +    ds_put_format(&ctx->output, "%s\n", port->bridge->name);
 +    free_info(&info);
 +}
 +
 +static void
 +cmd_br_to_vlan(struct vsctl_context *ctx)
 +{
 +    struct vsctl_bridge *bridge;
 +    struct vsctl_info info;
 +
 +    get_info(ctx->ovs, &info);
 +    bridge = find_bridge(&info, ctx->argv[1], true);
 +    ds_put_format(&ctx->output, "%d\n", bridge->vlan);
 +    free_info(&info);
 +}
 +
 +static void
 +cmd_br_to_parent(struct vsctl_context *ctx)
 +{
 +    struct vsctl_bridge *bridge;
 +    struct vsctl_info info;
 +
 +    get_info(ctx->ovs, &info);
 +    bridge = find_bridge(&info, ctx->argv[1], true);
 +    if (bridge->parent) {
 +        bridge = bridge->parent;
 +    }
 +    ds_put_format(&ctx->output, "%s\n", bridge->name);
 +    free_info(&info);
 +}
 +
 +static void
 +cmd_list_ifaces(struct vsctl_context *ctx)
 +{
 +    struct vsctl_bridge *br;
 +    struct shash_node *node;
 +    struct vsctl_info info;
 +    struct svec ifaces;
 +
 +    get_info(ctx->ovs, &info);
 +    br = find_bridge(&info, ctx->argv[1], true);
 +
 +    svec_init(&ifaces);
 +    SHASH_FOR_EACH (node, &info.ifaces) {
 +        struct vsctl_iface *iface = node->data;
 +
 +        if (strcmp(iface->iface_cfg->name, br->name)
 +            && br == iface->port->bridge) {
 +            svec_add(&ifaces, iface->iface_cfg->name);
 +        }
 +    }
 +    output_sorted(&ifaces, &ctx->output);
 +    svec_destroy(&ifaces);
 +
 +    free_info(&info);
 +}
 +
 +static void
 +cmd_iface_to_br(struct vsctl_context *ctx)
 +{
 +    struct vsctl_iface *iface;
 +    struct vsctl_info info;
 +
 +    get_info(ctx->ovs, &info);
 +    iface = find_iface(&info, ctx->argv[1], true);
 +    ds_put_format(&ctx->output, "%s\n", iface->port->bridge->name);
 +    free_info(&info);
 +}
 +
 +static void
 +cmd_get_controller(struct vsctl_context *ctx)
 +{
 +    struct vsctl_info info;
 +
 +    get_info(ctx->ovs, &info);
 +
 +    if (ctx->argc == 1) {
 +        /* Return the controller from the "Open_vSwitch" table */
 +        if (info.ctrl) {
 +            ds_put_format(&ctx->output, "%s\n", info.ctrl->target);
 +        }
 +    } else {
 +        /* Return the controller for a particular bridge. */
 +        struct vsctl_bridge *br = find_bridge(&info, ctx->argv[1], true);
 +
 +        /* If no controller is explicitly defined for the requested
 +         * bridge, fallback to the "Open_vSwitch" table's controller. */
 +        if (br->ctrl) {
 +            ds_put_format(&ctx->output, "%s\n", br->ctrl->target);
 +        } else if (info.ctrl) {
 +            ds_put_format(&ctx->output, "%s\n", info.ctrl->target);
 +        }
 +    }
 +
 +    free_info(&info);
 +}
 +
 +static void
 +cmd_del_controller(struct vsctl_context *ctx)
 +{
 +    struct vsctl_info info;
 +
 +    get_info(ctx->ovs, &info);
 +
 +    if (ctx->argc == 1) {
 +        if (info.ctrl) {
 +            ovsrec_controller_delete(info.ctrl);
 +            ovsrec_open_vswitch_set_controller(ctx->ovs, NULL);
 +        }
 +    } else {
 +        struct vsctl_bridge *br = find_real_bridge(&info, ctx->argv[1], true);
 +
 +        if (br->ctrl) {
 +            ovsrec_controller_delete(br->ctrl);
 +            ovsrec_bridge_set_controller(br->br_cfg, NULL);
 +        }
 +    }
 +
 +    free_info(&info);
 +}
 +
 +static void
 +cmd_set_controller(struct vsctl_context *ctx)
 +{
 +    struct vsctl_info info;
 +    struct ovsrec_controller *ctrl;
 +
 +    get_info(ctx->ovs, &info);
 +
 +    if (ctx->argc == 2) {
 +        /* Set the controller in the "Open_vSwitch" table. */
 +        if (info.ctrl) {
 +            ovsrec_controller_delete(info.ctrl);
 +        }
 +        ctrl = ovsrec_controller_insert(ctx->txn);
 +        ovsrec_controller_set_target(ctrl, ctx->argv[1]);
 +        ovsrec_open_vswitch_set_controller(ctx->ovs, ctrl);
 +    } else {
 +        /* Set the controller for a particular bridge. */
 +        struct vsctl_bridge *br = find_real_bridge(&info, ctx->argv[1], true);
 +
 +        if (br->ctrl) {
 +            ovsrec_controller_delete(br->ctrl);
 +        }
 +        ctrl = ovsrec_controller_insert(ctx->txn);
 +        ovsrec_controller_set_target(ctrl, ctx->argv[2]);
 +        ovsrec_bridge_set_controller(br->br_cfg, ctrl);
 +    }
 +
 +    free_info(&info);
 +}
 +
 +static void
 +cmd_get_fail_mode(struct vsctl_context *ctx)
 +{
 +    struct vsctl_info info;
 +    const char *fail_mode = NULL;
 +
 +    get_info(ctx->ovs, &info);
 +
 +    if (ctx->argc == 1) {
 +        /* Return the fail-mode from the "Open_vSwitch" table */
 +        if (info.ctrl && info.ctrl->fail_mode) {
 +            fail_mode = info.ctrl->fail_mode;
 +        }
 +    } else {
 +        /* Return the fail-mode for a particular bridge. */
 +        struct vsctl_bridge *br = find_bridge(&info, ctx->argv[1], true);
 +
 +        /* If no controller or fail-mode is explicitly defined for the 
 +         * requested bridge, fallback to the "Open_vSwitch" table's 
 +         * setting. */
 +        if (br->ctrl && br->ctrl->fail_mode) {
 +            fail_mode = br->ctrl->fail_mode;
 +        } else if (info.ctrl && info.ctrl->fail_mode) {
 +            fail_mode = info.ctrl->fail_mode;
 +        }
 +    }
 +
 +    if (fail_mode && strlen(fail_mode)) {
 +        ds_put_format(&ctx->output, "%s\n", fail_mode);
 +    }
 +
 +    free_info(&info);
 +}
 +
 +static void
 +cmd_del_fail_mode(struct vsctl_context *ctx)
 +{
 +    struct vsctl_info info;
 +
 +    get_info(ctx->ovs, &info);
 +
 +    if (ctx->argc == 1) {
 +        if (info.ctrl && info.ctrl->fail_mode) {
 +            ovsrec_controller_set_fail_mode(info.ctrl, NULL);
 +        }
 +    } else {
 +        struct vsctl_bridge *br = find_real_bridge(&info, ctx->argv[1], true);
 +
 +        if (br->ctrl && br->ctrl->fail_mode) {
 +            ovsrec_controller_set_fail_mode(br->ctrl, NULL);
 +        }
 +    }
 +
 +    free_info(&info);
 +}
 +
 +static void
 +cmd_set_fail_mode(struct vsctl_context *ctx)
 +{
 +    struct vsctl_info info;
 +    const char *fail_mode;
 +
 +    get_info(ctx->ovs, &info);
 +
 +    fail_mode = (ctx->argc == 2) ? ctx->argv[1] : ctx->argv[2];
 +
 +    if (strcmp(fail_mode, "standalone") && strcmp(fail_mode, "secure")) {
 +        vsctl_fatal("fail-mode must be \"standalone\" or \"secure\"");
 +    }
 +
 +    if (ctx->argc == 2) {
 +        /* Set the fail-mode in the "Open_vSwitch" table. */
 +        if (!info.ctrl) {
 +            vsctl_fatal("no controller declared");
 +        }
 +        ovsrec_controller_set_fail_mode(info.ctrl, fail_mode);
 +    } else {
 +        struct vsctl_bridge *br = find_real_bridge(&info, ctx->argv[1], true);
 +
 +        if (!br->ctrl) {
 +            vsctl_fatal("no controller declared for %s", br->name);
 +        }
 +        ovsrec_controller_set_fail_mode(br->ctrl, fail_mode);
 +    }
 +
 +    free_info(&info);
 +}
 +
 +static void
 +cmd_get_ssl(struct vsctl_context *ctx)
 +{
 +    struct ovsrec_ssl *ssl = ctx->ovs->ssl;
 +
 +    if (ssl) {
 +        ds_put_format(&ctx->output, "Private key: %s\n", ssl->private_key);
 +        ds_put_format(&ctx->output, "Certificate: %s\n", ssl->certificate);
 +        ds_put_format(&ctx->output, "CA Certificate: %s\n", ssl->ca_cert);
 +        ds_put_format(&ctx->output, "Bootstrap: %s\n",
 +                ssl->bootstrap_ca_cert ? "true" : "false");
 +    }
 +}
 +
 +static void
 +cmd_del_ssl(struct vsctl_context *ctx)
 +{
 +    struct ovsrec_ssl *ssl = ctx->ovs->ssl;
 +
 +    if (ssl) {
 +        ovsrec_ssl_delete(ssl);
 +        ovsrec_open_vswitch_set_ssl(ctx->ovs, NULL);
 +    }
 +}
 +
 +static void
 +cmd_set_ssl(struct vsctl_context *ctx)
 +{
 +    bool bootstrap = shash_find(&ctx->options, "--bootstrap");
 +    struct ovsrec_ssl *ssl = ctx->ovs->ssl;
 +
 +    if (ssl) {
 +        ovsrec_ssl_delete(ssl);
 +    }
 +    ssl = ovsrec_ssl_insert(ctx->txn);
 +
 +    ovsrec_ssl_set_private_key(ssl, ctx->argv[1]);
 +    ovsrec_ssl_set_certificate(ssl, ctx->argv[2]);
 +    ovsrec_ssl_set_ca_cert(ssl, ctx->argv[3]);
 +
 +    ovsrec_ssl_set_bootstrap_ca_cert(ssl, bootstrap);
 +
 +    ovsrec_open_vswitch_set_ssl(ctx->ovs, ssl);
 +}
 +\f
 +/* Parameter commands. */
 +
 +struct vsctl_row_id {
 +    const struct ovsdb_idl_table_class *table;
 +    const struct ovsdb_idl_column *name_column;
 +    const struct ovsdb_idl_column *uuid_column;
 +};
 +
 +struct vsctl_table_class {
 +    struct ovsdb_idl_table_class *class;
 +    struct vsctl_row_id row_ids[2];
 +};
 +
 +static const struct vsctl_table_class tables[] = {
 +    {&ovsrec_table_bridge,
 +     {{&ovsrec_table_bridge, &ovsrec_bridge_col_name, NULL},
 +      {NULL, NULL, NULL}}},
 +
 +    {&ovsrec_table_controller,
 +     {{&ovsrec_table_bridge,
 +       &ovsrec_bridge_col_name,
 +       &ovsrec_bridge_col_controller},
 +      {&ovsrec_table_open_vswitch,
 +       NULL,
 +       &ovsrec_open_vswitch_col_controller}}},
 +
 +    {&ovsrec_table_interface,
 +     {{&ovsrec_table_interface, &ovsrec_interface_col_name, NULL},
 +      {NULL, NULL, NULL}}},
 +
 +    {&ovsrec_table_mirror,
 +     {{&ovsrec_table_mirror, &ovsrec_mirror_col_name, NULL},
 +      {NULL, NULL, NULL}}},
 +
 +    {&ovsrec_table_netflow,
 +     {{&ovsrec_table_bridge,
 +       &ovsrec_bridge_col_name,
 +       &ovsrec_bridge_col_netflow},
 +      {NULL, NULL, NULL}}},
 +
 +    {&ovsrec_table_open_vswitch,
 +     {{&ovsrec_table_open_vswitch, NULL, NULL},
 +      {NULL, NULL, NULL}}},
 +
 +    {&ovsrec_table_port,
 +     {{&ovsrec_table_port, &ovsrec_port_col_name, NULL},
 +      {NULL, NULL, NULL}}},
 +
 +    {&ovsrec_table_ssl,
 +     {{&ovsrec_table_open_vswitch, NULL, &ovsrec_open_vswitch_col_ssl}}},
 +
 +    {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}
 +};
 +
 +static void
 +die_if_error(char *error)
 +{
 +    if (error) {
 +        vsctl_fatal("%s", error);
 +    }
 +}
 +
 +static int
 +to_lower_and_underscores(unsigned c)
 +{
 +    return c == '-' ? '_' : tolower(c);
 +}
 +
 +static unsigned int
 +score_partial_match(const char *name, const char *s)
 +{
 +    int score;
 +
 +    if (!strcmp(name, s)) {
 +        return UINT_MAX;
 +    }
 +    for (score = 0; ; score++, name++, s++) {
 +        if (to_lower_and_underscores(*name) != to_lower_and_underscores(*s)) {
 +            break;
 +        } else if (*name == '\0') {
 +            return UINT_MAX - 1;
 +        }
 +    }
 +    return *s == '\0' ? score : 0;
 +}
 +
 +static const struct vsctl_table_class *
 +get_table(const char *table_name)
 +{
 +    const struct vsctl_table_class *table;
 +    const struct vsctl_table_class *best_match = NULL;
 +    unsigned int best_score = 0;
 +
 +    for (table = tables; table->class; table++) {
 +        unsigned int score = score_partial_match(table->class->name,
 +                                                 table_name);
 +        if (score > best_score) {
 +            best_match = table;
 +            best_score = score;
 +        } else if (score == best_score) {
 +            best_match = NULL;
 +        }
 +    }
 +    if (best_match) {
 +        return best_match;
 +    } else if (best_score) {
 +        vsctl_fatal("multiple table names match \"%s\"", table_name);
 +    } else {
 +        vsctl_fatal("unknown table \"%s\"", table_name);
 +    }
 +}
 +
 +static const struct ovsdb_idl_row *
 +get_row_by_id(struct vsctl_context *ctx, const struct vsctl_table_class *table,
 +              const struct vsctl_row_id *id, const char *record_id)
 +{
 +    const struct ovsdb_idl_row *referrer, *final;
 +
 +    if (!id->table) {
 +        return NULL;
 +    }
 +
 +    if (!id->name_column) {
 +        if (strcmp(record_id, ".")) {
 +            return NULL;
 +        }
 +        referrer = ovsdb_idl_first_row(ctx->idl, id->table);
 +        if (!referrer || ovsdb_idl_next_row(referrer)) {
 +            return NULL;
 +        }
 +    } else {
 +        const struct ovsdb_idl_row *row;
 +        unsigned int best_score = 0;
 +
 +        /* It might make sense to relax this assertion. */
 +        assert(id->name_column->type.key.type == OVSDB_TYPE_STRING);
 +
 +        referrer = NULL;
 +        for (row = ovsdb_idl_first_row(ctx->idl, id->table);
 +             row != NULL && best_score != UINT_MAX;
 +             row = ovsdb_idl_next_row(row))
 +        {
 +            struct ovsdb_datum name;
 +
 +            ovsdb_idl_txn_read(row, id->name_column, &name);
 +            if (name.n == 1) {
 +                unsigned int score = score_partial_match(name.keys[0].string,
 +                                                         record_id);
 +                if (score > best_score) {
 +                    referrer = row;
 +                    best_score = score;
 +                } else if (score == best_score) {
 +                    referrer = NULL;
 +                }
 +            }
 +            ovsdb_datum_destroy(&name, &id->name_column->type);
 +        }
 +        if (best_score && !referrer) {
 +            vsctl_fatal("multiple rows in %s match \"%s\"",
 +                        table->class->name, record_id);
 +        }
 +    }
 +    if (!referrer) {
 +        return NULL;
 +    }
 +
 +    final = NULL;
 +    if (id->uuid_column) {
 +        struct ovsdb_datum uuid;
 +
 +        assert(id->uuid_column->type.key.type == OVSDB_TYPE_UUID);
 +        assert(id->uuid_column->type.value.type == OVSDB_TYPE_VOID);
 +
 +        ovsdb_idl_txn_read(referrer, id->uuid_column, &uuid);
 +        if (uuid.n == 1) {
 +            final = ovsdb_idl_get_row_for_uuid(ctx->idl, table->class,
 +                                               &uuid.keys[0].uuid);
 +        }
 +        ovsdb_datum_destroy(&uuid, &id->uuid_column->type);
 +    } else {
 +        final = referrer;
 +    }
 +
 +    return final;
 +}
 +
 +static const struct ovsdb_idl_row *
 +get_row(struct vsctl_context *ctx,
 +        const struct vsctl_table_class *table, const char *record_id)
 +{
 +    const struct ovsdb_idl_row *row;
 +    struct uuid uuid;
 +
 +    if (uuid_from_string(&uuid, record_id)) {
 +        row = ovsdb_idl_get_row_for_uuid(ctx->idl, table->class, &uuid);
 +    } else {
 +        int i;
 +
 +        for (i = 0; i < ARRAY_SIZE(table->row_ids); i++) {
 +            row = get_row_by_id(ctx, table, &table->row_ids[i], record_id);
 +            if (row) {
 +                break;
 +            }
 +        }
 +    }
 +    return row;
 +}
 +
 +static const struct ovsdb_idl_row *
 +must_get_row(struct vsctl_context *ctx,
 +             const struct vsctl_table_class *table, const char *record_id)
 +{
 +    const struct ovsdb_idl_row *row = get_row(ctx, table, record_id);
 +    if (!row) {
 +        vsctl_fatal("no row \"%s\" in table %s",
 +                    record_id, table->class->name);
 +    }
 +    return row;
 +}
 +
 +static char *
 +get_column(const struct vsctl_table_class *table, const char *column_name,
 +           const struct ovsdb_idl_column **columnp)
 +{
 +    const struct ovsdb_idl_column *best_match = NULL;
 +    unsigned int best_score = 0;
 +    size_t i;
 +
 +    for (i = 0; i < table->class->n_columns; i++) {
 +        const struct ovsdb_idl_column *column = &table->class->columns[i];
 +        unsigned int score = score_partial_match(column->name, column_name);
 +        if (score > best_score) {
 +            best_match = column;
 +            best_score = score;
 +        } else if (score == best_score) {
 +            best_match = NULL;
 +        }
 +    }
 +
 +    *columnp = best_match;
 +    if (best_match) {
 +        return NULL;
 +    } else if (best_score) {
 +        return xasprintf("%s contains more than one column whose name "
 +                         "matches \"%s\"", table->class->name, column_name);
 +    } else {
 +        return xasprintf("%s does not contain a column whose name matches "
 +                         "\"%s\"", table->class->name, column_name);
 +    }
 +}
 +
 +static char * WARN_UNUSED_RESULT
 +parse_column_key_value(const char *arg, const struct vsctl_table_class *table,
 +                       const struct ovsdb_idl_column **columnp,
 +                       char **keyp, char **valuep)
 +{
 +    const char *p = arg;
 +    char *error;
 +
 +    assert(columnp || keyp);
 +    if (keyp) {
 +        *keyp = NULL;
 +    }
 +    if (valuep) {
 +        *valuep = NULL;
 +    }
 +
 +    /* Parse column name. */
 +    if (columnp) {
 +        char *column_name;
 +
 +        error = ovsdb_token_parse(&p, &column_name);
 +        if (error) {
 +            goto error;
 +        }
 +        if (column_name[0] == '\0') {
 +            free(column_name);
 +            error = xasprintf("%s: missing column name", arg);
 +            goto error;
 +        }
 +        error = get_column(table, column_name, columnp);
 +        free(column_name);
 +        if (error) {
 +            goto error;
 +        }
 +    }
 +
 +    /* Parse key string. */
 +    if (*p == ':' || !columnp) {
 +        if (columnp) {
 +            p++;
 +        } else if (!keyp) {
 +            error = xasprintf("%s: key not accepted here", arg);
 +            goto error;
 +        }
 +        error = ovsdb_token_parse(&p, keyp);
 +        if (error) {
 +            goto error;
 +        }
 +    } else if (keyp) {
 +        *keyp = NULL;
 +    }
 +
 +    /* Parse value string. */
 +    if (*p == '=') {
 +        if (!valuep) {
 +            error = xasprintf("%s: value not accepted here", arg);
 +            goto error;
 +        }
 +        *valuep = xstrdup(p + 1);
 +    } else {
 +        if (valuep) {
 +            *valuep = NULL;
 +        }
 +        if (*p != '\0') {
 +            error = xasprintf("%s: trailing garbage \"%s\" in argument",
 +                              arg, p);
 +            goto error;
 +        }
 +    }
 +    return NULL;
 +
 +error:
 +    if (columnp) {
 +        *columnp = NULL;
 +    }
 +    if (keyp) {
 +        free(*keyp);
 +        *keyp = NULL;
 +    }
 +    if (valuep) {
 +        free(*valuep);
 +        *valuep = NULL;
 +    }
 +    return error;
 +}
 +
 +static void
 +cmd_get(struct vsctl_context *ctx)
 +{
 +    bool if_exists = shash_find(&ctx->options, "--if-exists");
 +    const char *table_name = ctx->argv[1];
 +    const char *record_id = ctx->argv[2];
 +    const struct vsctl_table_class *table;
 +    const struct ovsdb_idl_row *row;
 +    struct ds *out = &ctx->output;
 +    int i;
 +
 +    table = get_table(table_name);
 +    row = must_get_row(ctx, table, record_id);
 +    for (i = 3; i < ctx->argc; i++) {
 +        const struct ovsdb_idl_column *column;
 +        struct ovsdb_datum datum;
 +        char *key_string;
 +
 +        die_if_error(parse_column_key_value(ctx->argv[i], table,
 +                                            &column, &key_string, NULL));
 +
 +        ovsdb_idl_txn_read(row, column, &datum);
 +        if (key_string) {
 +            union ovsdb_atom key;
 +            unsigned int idx;
 +
 +            if (column->type.value.type == OVSDB_TYPE_VOID) {
 +                vsctl_fatal("cannot specify key to get for non-map column %s",
 +                            column->name);
 +            }
 +
 +            die_if_error(ovsdb_atom_from_string(&key,
 +                                                &column->type.key,
 +                                                key_string));
 +
 +            idx = ovsdb_datum_find_key(&datum, &key,
 +                                       column->type.key.type);
 +            if (idx == UINT_MAX) {
 +                if (!if_exists) {
 +                    vsctl_fatal("no key \"%s\" in %s record \"%s\" column %s",
 +                                key_string, table->class->name, record_id,
 +                                column->name);
 +                }
 +            } else {
 +                ovsdb_atom_to_string(&datum.values[idx],
 +                                     column->type.value.type, out);
 +            }
 +            ovsdb_atom_destroy(&key, column->type.key.type);
 +        } else {
 +            ovsdb_datum_to_string(&datum, &column->type, out);
 +        }
 +        ds_put_char(out, '\n');
 +        ovsdb_datum_destroy(&datum, &column->type);
 +
 +        free(key_string);
 +    }
 +}
 +
 +static void
 +list_record(const struct vsctl_table_class *table,
 +            const struct ovsdb_idl_row *row, struct ds *out)
 +{
 +    size_t i;
 +
 +    ds_put_format(out, "%-20s: "UUID_FMT"\n", "_uuid",
 +                  UUID_ARGS(&row->uuid));
 +    for (i = 0; i < table->class->n_columns; i++) {
 +        const struct ovsdb_idl_column *column = &table->class->columns[i];
 +        struct ovsdb_datum datum;
 +
 +        ovsdb_idl_txn_read(row, column, &datum);
 +
 +        ds_put_format(out, "%-20s: ", column->name);
 +        ovsdb_datum_to_string(&datum, &column->type, out);
 +        ds_put_char(out, '\n');
 +
 +        ovsdb_datum_destroy(&datum, &column->type);
 +    }
 +}
 +
 +static void
 +cmd_list(struct vsctl_context *ctx)
 +{
 +    const char *table_name = ctx->argv[1];
 +    const struct vsctl_table_class *table;
 +    struct ds *out = &ctx->output;
 +    int i;
 +
 +    table = get_table(table_name);
 +    if (ctx->argc > 2) {
 +        for (i = 2; i < ctx->argc; i++) {
 +            if (i > 2) {
 +                ds_put_char(out, '\n');
 +            }
 +            list_record(table, must_get_row(ctx, table, ctx->argv[i]), out);
 +        }
 +    } else {
 +        const struct ovsdb_idl_row *row;
 +        bool first;
 +
 +        for (row = ovsdb_idl_first_row(ctx->idl, table->class), first = true;
 +             row != NULL;
 +             row = ovsdb_idl_next_row(row), first = false) {
 +            if (!first) {
 +                ds_put_char(out, '\n');
 +            }
 +            list_record(table, row, out);
 +        }
 +    }
 +}
 +
 +static void
 +set_column(const struct vsctl_table_class *table,
 +           const struct ovsdb_idl_row *row, const char *arg)
 +{
 +    const struct ovsdb_idl_column *column;
 +    char *key_string, *value_string;
 +    char *error;
 +
 +    error = parse_column_key_value(arg, table, &column, &key_string,
 +                                   &value_string);
 +    die_if_error(error);
 +    if (!value_string) {
 +        vsctl_fatal("%s: missing value", arg);
 +    }
 +
 +    if (key_string) {
 +        union ovsdb_atom key, value;
 +        struct ovsdb_datum old, new;
 +
 +        if (column->type.value.type == OVSDB_TYPE_VOID) {
 +            vsctl_fatal("cannot specify key to set for non-map column %s",
 +                        column->name);
 +        }
 +
 +        die_if_error(ovsdb_atom_from_string(&key, &column->type.key,
 +                                            key_string));
 +        die_if_error(ovsdb_atom_from_string(&value, &column->type.value,
 +                                            value_string));
 +
 +        ovsdb_datum_init_empty(&new);
 +        ovsdb_datum_add_unsafe(&new, &key, &value, &column->type);
 +
 +        ovsdb_atom_destroy(&key, column->type.key.type);
 +        ovsdb_atom_destroy(&value, column->type.value.type);
 +
 +        ovsdb_idl_txn_read(row, column, &old);
 +        ovsdb_datum_union(&old, &new, &column->type, true);
 +        ovsdb_idl_txn_write(row, column, &old);
 +
 +        ovsdb_datum_destroy(&new, &column->type);
 +    } else {
 +        struct ovsdb_datum datum;
 +
 +        die_if_error(ovsdb_datum_from_string(&datum, &column->type,
 +                                             value_string));
 +        ovsdb_idl_txn_write(row, column, &datum);
 +    }
 +
 +    free(key_string);
 +    free(value_string);
 +}
 +
 +static void
 +cmd_set(struct vsctl_context *ctx)
 +{
 +    const char *table_name = ctx->argv[1];
 +    const char *record_id = ctx->argv[2];
 +    const struct vsctl_table_class *table;
 +    const struct ovsdb_idl_row *row;
 +    int i;
 +
 +    table = get_table(table_name);
 +    row = must_get_row(ctx, table, record_id);
 +    for (i = 3; i < ctx->argc; i++) {
 +        set_column(table, row, ctx->argv[i]);
 +    }
 +}
 +
 +static void
 +cmd_add(struct vsctl_context *ctx)
 +{
 +    const char *table_name = ctx->argv[1];
 +    const char *record_id = ctx->argv[2];
 +    const char *column_name = ctx->argv[3];
 +    const struct vsctl_table_class *table;
 +    const struct ovsdb_idl_column *column;
 +    const struct ovsdb_idl_row *row;
 +    const struct ovsdb_type *type;
 +    struct ovsdb_datum old;
 +    int i;
 +
 +    table = get_table(table_name);
 +    row = must_get_row(ctx, table, record_id);
 +    die_if_error(get_column(table, column_name, &column));
 +
 +    type = &column->type;
 +    ovsdb_idl_txn_read(row, column, &old);
 +    for (i = 4; i < ctx->argc; i++) {
 +        struct ovsdb_type add_type;
 +        struct ovsdb_datum add;
 +
 +        add_type = *type;
 +        add_type.n_min = 1;
 +        add_type.n_max = UINT_MAX;
 +        die_if_error(ovsdb_datum_from_string(&add, &add_type, ctx->argv[i]));
 +        ovsdb_datum_union(&old, &add, type, false);
 +        ovsdb_datum_destroy(&add, type);
 +    }
 +    if (old.n > type->n_max) {
 +        vsctl_fatal("\"add\" operation would put %u %s in column %s of "
 +                    "table %s but the maximum number is %u",
 +                    old.n,
 +                    type->value.type == OVSDB_TYPE_VOID ? "values" : "pairs",
 +                    column->name, table->class->name, type->n_max);
 +    }
 +    ovsdb_idl_txn_write(row, column, &old);
 +}
 +
 +static void
 +cmd_remove(struct vsctl_context *ctx)
 +{
 +    const char *table_name = ctx->argv[1];
 +    const char *record_id = ctx->argv[2];
 +    const char *column_name = ctx->argv[3];
 +    const struct vsctl_table_class *table;
 +    const struct ovsdb_idl_column *column;
 +    const struct ovsdb_idl_row *row;
 +    const struct ovsdb_type *type;
 +    struct ovsdb_datum old;
 +    int i;
 +
 +    table = get_table(table_name);
 +    row = must_get_row(ctx, table, record_id);
 +    die_if_error(get_column(table, column_name, &column));
 +
 +    type = &column->type;
 +    ovsdb_idl_txn_read(row, column, &old);
 +    for (i = 4; i < ctx->argc; i++) {
 +        struct ovsdb_type rm_type;
 +        struct ovsdb_datum rm;
 +        char *error;
 +
 +        rm_type = *type;
 +        rm_type.n_min = 1;
 +        rm_type.n_max = UINT_MAX;
 +        error = ovsdb_datum_from_string(&rm, &rm_type, ctx->argv[i]);
 +        if (error && ovsdb_type_is_map(&rm_type)) {
 +            free(error);
 +            rm_type.value.type = OVSDB_TYPE_VOID;
 +            die_if_error(ovsdb_datum_from_string(&rm, &rm_type, ctx->argv[i]));
 +        }
 +        ovsdb_datum_subtract(&old, type, &rm, &rm_type);
 +        ovsdb_datum_destroy(&rm, &rm_type);
 +    }
 +    if (old.n < type->n_min) {
 +        vsctl_fatal("\"remove\" operation would put %u %s in column %s of "
 +                    "table %s but the minimun number is %u",
 +                    old.n,
 +                    type->value.type == OVSDB_TYPE_VOID ? "values" : "pairs",
 +                    column->name, table->class->name, type->n_min);
 +    }
 +    ovsdb_idl_txn_write(row, column, &old);
 +}
 +
 +static void
 +cmd_clear(struct vsctl_context *ctx)
 +{
 +    const char *table_name = ctx->argv[1];
 +    const char *record_id = ctx->argv[2];
 +    const struct vsctl_table_class *table;
 +    const struct ovsdb_idl_row *row;
 +    int i;
 +
 +    table = get_table(table_name);
 +    row = must_get_row(ctx, table, record_id);
 +    for (i = 3; i < ctx->argc; i++) {
 +        const struct ovsdb_idl_column *column;
 +        const struct ovsdb_type *type;
 +        struct ovsdb_datum datum;
 +
 +        die_if_error(get_column(table, ctx->argv[i], &column));
 +
 +        type = &column->type;
 +        if (type->n_min > 0) {
 +            vsctl_fatal("\"clear\" operation cannot be applied to column %s "
 +                        "of table %s, which is not allowed to be empty",
 +                        column->name, table->class->name);
 +        }
 +
 +        ovsdb_datum_init_empty(&datum);
 +        ovsdb_idl_txn_write(row, column, &datum);
 +    }
 +}
 +
 +static void
 +cmd_create(struct vsctl_context *ctx)
 +{
 +    const char *table_name = ctx->argv[1];
 +    const struct vsctl_table_class *table;
 +    const struct ovsdb_idl_row *row;
 +    int i;
 +
 +    table = get_table(table_name);
 +    row = ovsdb_idl_txn_insert(ctx->txn, table->class);
 +    for (i = 2; i < ctx->argc; i++) {
 +        set_column(table, row, ctx->argv[i]);
 +    }
 +    ds_put_format(&ctx->output, UUID_FMT, UUID_ARGS(&row->uuid));
 +}
 +
 +/* This function may be used as the 'postprocess' function for commands that
 + * insert new rows into the database.  It expects that the command's 'run'
 + * function prints the UUID reported by ovsdb_idl_txn_insert() as the command's
 + * sole output.  It replaces that output by the row's permanent UUID assigned
 + * by the database server and appends a new-line.
 + *
 + * Currently we use this only for "create", because the higher-level commands
 + * are supposed to be independent of the actual structure of the vswitch
 + * configuration. */
 +static void
 +post_create(struct vsctl_context *ctx)
 +{
 +    const struct uuid *real;
 +    struct uuid dummy;
 +
 +    uuid_from_string(&dummy, ds_cstr(&ctx->output));
 +    real = ovsdb_idl_txn_get_insert_uuid(ctx->txn, &dummy);
 +    if (real) {
 +        ds_clear(&ctx->output);
 +        ds_put_format(&ctx->output, UUID_FMT, UUID_ARGS(real));
 +    }
 +    ds_put_char(&ctx->output, '\n');
 +}
 +
 +static void
 +cmd_destroy(struct vsctl_context *ctx)
 +{
 +    bool must_exist = !shash_find(&ctx->options, "--if-exists");
 +    const char *table_name = ctx->argv[1];
 +    const struct vsctl_table_class *table;
 +    int i;
 +
 +    table = get_table(table_name);
 +    for (i = 2; i < ctx->argc; i++) {
 +        const struct ovsdb_idl_row *row;
 +
 +        row = (must_exist ? must_get_row : get_row)(ctx, table, ctx->argv[i]);
 +        if (row) {
 +            ovsdb_idl_txn_delete(row);
 +        }
 +    }
 +}
 +\f
 +static struct json *
 +where_uuid_equals(const struct uuid *uuid)
 +{
 +    return
 +        json_array_create_1(
 +            json_array_create_3(
 +                json_string_create("_uuid"),
 +                json_string_create("=="),
 +                json_array_create_2(
 +                    json_string_create("uuid"),
 +                    json_string_create_nocopy(
 +                        xasprintf(UUID_FMT, UUID_ARGS(uuid))))));
 +}
 +
 +static void
 +vsctl_context_init(struct vsctl_context *ctx, struct vsctl_command *command,
 +                   struct ovsdb_idl *idl, struct ovsdb_idl_txn *txn,
 +                   const struct ovsrec_open_vswitch *ovs)
 +{
 +    ctx->argc = command->argc;
 +    ctx->argv = command->argv;
 +    ctx->options = command->options;
 +
 +    ds_swap(&ctx->output, &command->output);
 +    ctx->idl = idl;
 +    ctx->txn = txn;
 +    ctx->ovs = ovs;
 +
 +}
 +
 +static void
 +vsctl_context_done(struct vsctl_context *ctx, struct vsctl_command *command)
 +{
 +    ds_swap(&ctx->output, &command->output);
 +}
 +
 +static void
 +do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands,
 +         struct ovsdb_idl *idl)
 +{
 +    struct ovsdb_idl_txn *txn;
 +    const struct ovsrec_open_vswitch *ovs;
 +    enum ovsdb_idl_txn_status status;
 +    struct vsctl_command *c;
 +    int64_t next_cfg = 0;
 +    char *comment;
 +    char *error;
 +
 +    txn = the_idl_txn = ovsdb_idl_txn_create(idl);
 +    if (dry_run) {
 +        ovsdb_idl_txn_set_dry_run(txn);
 +    }
 +
 +    comment = xasprintf("ovs-vsctl: %s", args);
 +    ovsdb_idl_txn_add_comment(txn, comment);
 +    free(comment);
 +
 +    ovs = ovsrec_open_vswitch_first(idl);
 +    if (!ovs) {
 +        /* XXX add verification that table is empty */
 +        ovs = ovsrec_open_vswitch_insert(txn);
 +    }
 +
 +    if (wait_for_reload) {
 +        struct json *where = where_uuid_equals(&ovs->header_.uuid);
 +        ovsdb_idl_txn_increment(txn, "Open_vSwitch", "next_cfg", where);
 +        json_destroy(where);
 +    }
 +
 +    for (c = commands; c < &commands[n_commands]; c++) {
 +        struct vsctl_context ctx;
 +
 +        ds_init(&c->output);
 +        vsctl_context_init(&ctx, c, idl, txn, ovs);
 +        (c->syntax->run)(&ctx);
 +        vsctl_context_done(&ctx, c);
 +    }
 +
 +    while ((status = ovsdb_idl_txn_commit(txn)) == TXN_INCOMPLETE) {
 +        ovsdb_idl_run(idl);
 +        ovsdb_idl_wait(idl);
 +        ovsdb_idl_txn_wait(txn);
 +        poll_block();
 +    }
 +    if (wait_for_reload && status == TXN_SUCCESS) {
 +        next_cfg = ovsdb_idl_txn_get_increment_new_value(txn);
 +    }
 +    for (c = commands; c < &commands[n_commands]; c++) {
 +        if (c->syntax->postprocess) {
 +            struct vsctl_context ctx;
 +
 +            vsctl_context_init(&ctx, c, idl, txn, ovs);
 +            (c->syntax->postprocess)(&ctx);
 +            vsctl_context_done(&ctx, c);
 +        }
 +    }
 +    error = xstrdup(ovsdb_idl_txn_get_error(txn));
 +    ovsdb_idl_txn_destroy(txn);
 +    the_idl_txn = NULL;
 +
 +    switch (status) {
 +    case TXN_INCOMPLETE:
 +        NOT_REACHED();
 +
 +    case TXN_ABORTED:
 +        /* Should not happen--we never call ovsdb_idl_txn_abort(). */
 +        vsctl_fatal("transaction aborted");
 +
 +    case TXN_UNCHANGED:
 +    case TXN_SUCCESS:
 +        break;
 +
 +    case TXN_TRY_AGAIN:
 +        for (c = commands; c < &commands[n_commands]; c++) {
 +            ds_destroy(&c->output);
 +        }
 +        free(error);
 +        return;
 +
 +    case TXN_ERROR:
 +        vsctl_fatal("transaction error: %s", error);
 +
 +    default:
 +        NOT_REACHED();
 +    }
 +    free(error);
 +
 +    for (c = commands; c < &commands[n_commands]; c++) {
 +        struct ds *ds = &c->output;
 +        if (oneline) {
 +            size_t j;
 +
 +            ds_chomp(ds, '\n');
 +            for (j = 0; j < ds->length; j++) {
 +                int c = ds->string[j];
 +                switch (c) {
 +                case '\n':
 +                    fputs("\\n", stdout);
 +                    break;
 +
 +                case '\\':
 +                    fputs("\\\\", stdout);
 +                    break;
 +
 +                default:
 +                    putchar(c);
 +                }
 +            }
 +            putchar('\n');
 +        } else {
 +            fputs(ds_cstr(ds), stdout);
 +        }
 +        ds_destroy(&c->output);
 +        shash_destroy(&c->options);
 +    }
 +    free(commands);
 +
 +    if (wait_for_reload && status != TXN_UNCHANGED) {
 +        for (;;) {
 +            const struct ovsrec_open_vswitch *ovs;
 +
 +            ovsdb_idl_run(idl);
 +            OVSREC_OPEN_VSWITCH_FOR_EACH (ovs, idl) {
 +                if (ovs->cur_cfg >= next_cfg) {
 +                    goto done;
 +                }
 +            }
 +            ovsdb_idl_wait(idl);
 +            poll_block();
 +        }
 +    done: ;
 +    }
 +    ovsdb_idl_destroy(idl);
 +
 +    exit(EXIT_SUCCESS);
 +}
 +
 +static const struct vsctl_command_syntax all_commands[] = {
 +    /* Open vSwitch commands. */
 +    {"init", 0, 0, cmd_init, NULL, ""},
 +
 +    /* Bridge commands. */
 +    {"add-br", 1, 3, cmd_add_br, NULL, "--may-exist"},
 +    {"del-br", 1, 1, cmd_del_br, NULL, "--if-exists"},
 +    {"list-br", 0, 0, cmd_list_br, NULL, ""},
 +    {"br-exists", 1, 1, cmd_br_exists, NULL, ""},
 +    {"br-to-vlan", 1, 1, cmd_br_to_vlan, NULL, ""},
 +    {"br-to-parent", 1, 1, cmd_br_to_parent, NULL, ""},
 +    {"br-set-external-id", 2, 3, cmd_br_set_external_id, NULL, ""},
 +    {"br-get-external-id", 1, 2, cmd_br_get_external_id, NULL, ""},
 +
 +    /* Port commands. */
 +    {"list-ports", 1, 1, cmd_list_ports, NULL, ""},
 +    {"add-port", 2, 2, cmd_add_port, NULL, "--may-exist"},
 +    {"add-bond", 4, INT_MAX, cmd_add_bond, NULL, "--may-exist,--fake-iface"},
 +    {"del-port", 1, 2, cmd_del_port, NULL, "--if-exists"},
 +    {"port-to-br", 1, 1, cmd_port_to_br, NULL, ""},
 +
 +    /* Interface commands. */
 +    {"list-ifaces", 1, 1, cmd_list_ifaces, NULL, ""},
 +    {"iface-to-br", 1, 1, cmd_iface_to_br, NULL, ""},
 +
 +    /* Controller commands. */
 +    {"get-controller", 0, 1, cmd_get_controller, NULL, ""},
 +    {"del-controller", 0, 1, cmd_del_controller, NULL, ""},
 +    {"set-controller", 1, 2, cmd_set_controller, NULL, ""},
 +    {"get-fail-mode", 0, 1, cmd_get_fail_mode, NULL, ""},
 +    {"del-fail-mode", 0, 1, cmd_del_fail_mode, NULL, ""},
 +    {"set-fail-mode", 1, 2, cmd_set_fail_mode, NULL, ""},
 +
 +    /* SSL commands. */
 +    {"get-ssl", 0, 0, cmd_get_ssl, NULL, ""},
 +    {"del-ssl", 0, 0, cmd_del_ssl, NULL, ""},
 +    {"set-ssl", 3, 3, cmd_set_ssl, NULL, "--bootstrap"},
 +
 +    /* Parameter commands. */
 +    {"get", 3, INT_MAX, cmd_get, NULL, "--if-exists"},
 +    {"list", 1, INT_MAX, cmd_list, NULL, ""},
 +    {"set", 3, INT_MAX, cmd_set, NULL, ""},
 +    {"add", 4, INT_MAX, cmd_add, NULL, ""},
 +    {"remove", 4, INT_MAX, cmd_remove, NULL, ""},
 +    {"clear", 3, INT_MAX, cmd_clear, NULL, ""},
 +    {"create", 2, INT_MAX, cmd_create, post_create, ""},
 +    {"destroy", 1, INT_MAX, cmd_destroy, NULL, "--if-exists"},
 +
 +    {NULL, 0, 0, NULL, NULL, NULL},
 +};
 +
diff --combined vswitchd/bridge.c
@@@ -30,6 -30,7 +30,6 @@@
  #include <sys/types.h>
  #include <unistd.h>
  #include "bitmap.h"
 -#include "cfg.h"
  #include "coverage.h"
  #include "dirs.h"
  #include "dpif.h"
  #include "port-array.h"
  #include "proc-net-compat.h"
  #include "process.h"
 +#include "sha1.h"
  #include "shash.h"
  #include "socket-util.h"
 -#include "stp.h"
 +#include "stream-ssl.h"
  #include "svec.h"
  #include "timeval.h"
  #include "util.h"
  #include "unixctl.h"
  #include "vconn.h"
 -#include "vconn-ssl.h"
 +#include "vswitchd/vswitch-idl.h"
  #include "xenserver.h"
  #include "xtoxll.h"
  #include "sflow_api.h"
@@@ -71,6 -71,8 +71,6 @@@ struct dst 
      uint16_t dp_ifidx;
  };
  
 -extern uint64_t mgmt_id;
 -
  struct iface {
      /* These members are always valid. */
      struct port *port;          /* Containing port. */
@@@ -84,9 -86,6 +84,9 @@@
      int dp_ifidx;               /* Index within kernel datapath. */
      struct netdev *netdev;      /* Network device. */
      bool enabled;               /* May be chosen for flows? */
 +
 +    /* This member is only valid *during* bridge_reconfigure(). */
 +    const struct ovsrec_interface *cfg;
  };
  
  #define BOND_MASK 0xff
@@@ -106,8 -105,8 +106,8 @@@ struct mirror 
      char *name;
  
      /* Selection criteria. */
 -    struct svec src_ports;
 -    struct svec dst_ports;
 +    struct shash src_ports;     /* Name is port name; data is always NULL. */
 +    struct shash dst_ports;     /* Name is port name; data is always NULL. */
      int *vlans;
      size_t n_vlans;
  
@@@ -136,15 -135,15 +136,15 @@@ struct port 
      tag_type no_ifaces_tag;     /* Tag for flows when all ifaces disabled. */
      int updelay, downdelay;     /* Delay before iface goes up/down, in ms. */
      bool bond_compat_is_stale;  /* Need to call port_update_bond_compat()? */
 +    bool bond_fake_iface;       /* Fake a bond interface for legacy compat? */
  
      /* Port mirroring info. */
      mirror_mask_t src_mirrors;  /* Mirrors triggered when packet received. */
      mirror_mask_t dst_mirrors;  /* Mirrors triggered when packet sent. */
      bool is_mirror_output_port; /* Does port mirroring send frames here? */
  
 -    /* Spanning tree info. */
 -    enum stp_state stp_state;   /* Always STP_FORWARDING if STP not in use. */
 -    tag_type stp_state_tag;     /* Tag for STP state change. */
 +    /* This member is only valid *during* bridge_reconfigure(). */
 +    const struct ovsrec_port *cfg;
  };
  
  #define DP_MAX_PORTS 255
@@@ -184,8 -183,9 +184,8 @@@ struct bridge 
      /* Port mirroring. */
      struct mirror *mirrors[MAX_MIRRORS];
  
 -    /* Spanning tree. */
 -    struct stp *stp;
 -    long long int stp_last_tick;
 +    /* This member is only valid *during* bridge_reconfigure(). */
 +    const struct ovsrec_bridge *cfg;
  };
  
  /* List of all bridges. */
@@@ -194,19 -194,14 +194,19 @@@ static struct list all_bridges = LIST_I
  /* Maximum number of datapaths. */
  enum { DP_MAX = 256 };
  
 -static struct bridge *bridge_create(const char *name);
 +static struct bridge *bridge_create(const struct ovsrec_bridge *br_cfg);
  static void bridge_destroy(struct bridge *);
  static struct bridge *bridge_lookup(const char *name);
 -static void bridge_unixctl_dump_flows(struct unixctl_conn *, const char *);
 +static unixctl_cb_func bridge_unixctl_dump_flows;
  static int bridge_run_one(struct bridge *);
 -static void bridge_reconfigure_one(struct bridge *);
 -static void bridge_reconfigure_controller(struct bridge *);
 -static void bridge_get_all_ifaces(const struct bridge *, struct svec *ifaces);
 +static const struct ovsrec_controller *bridge_get_controller(
 +                      const struct ovsrec_open_vswitch *ovs_cfg,
 +                      const struct bridge *br);
 +static void bridge_reconfigure_one(const struct ovsrec_open_vswitch *,
 +                                   struct bridge *);
 +static void bridge_reconfigure_controller(const struct ovsrec_open_vswitch *,
 +                                          struct bridge *);
 +static void bridge_get_all_ifaces(const struct bridge *, struct shash *ifaces);
  static void bridge_fetch_dp_ifaces(struct bridge *);
  static void bridge_flush(struct bridge *);
  static void bridge_pick_local_hw_addr(struct bridge *,
@@@ -216,9 -211,10 +216,9 @@@ static uint64_t bridge_pick_datapath_id
                                          const uint8_t bridge_ea[ETH_ADDR_LEN],
                                          struct iface *hw_addr_iface);
  static struct iface *bridge_get_local_iface(struct bridge *);
 -static const char *bridge_get_controller(const struct bridge *br);
  static uint64_t dpid_from_hash(const void *, size_t nbytes);
  
 -static void bridge_unixctl_fdb_show(struct unixctl_conn *, const char *args);
 +static unixctl_cb_func bridge_unixctl_fdb_show;
  
  static void bond_init(void);
  static void bond_run(struct bridge *);
@@@ -227,8 -223,8 +227,8 @@@ static void bond_rebalance_port(struct 
  static void bond_send_learning_packets(struct port *);
  static void bond_enable_slave(struct iface *iface, bool enable);
  
 -static void port_create(struct bridge *, const char *name);
 -static void port_reconfigure(struct port *);
 +static struct port *port_create(struct bridge *, const char *name);
 +static void port_reconfigure(struct port *, const struct ovsrec_port *);
  static void port_destroy(struct port *);
  static struct port *port_lookup(const struct bridge *, const char *name);
  static struct iface *port_lookup_iface(const struct port *, const char *name);
@@@ -238,14 -234,18 +238,14 @@@ static void port_update_bond_compat(str
  static void port_update_vlan_compat(struct port *);
  static void port_update_bonding(struct port *);
  
 -static void mirror_create(struct bridge *, const char *name);
 +static struct mirror *mirror_create(struct bridge *, const char *name);
  static void mirror_destroy(struct mirror *);
  static void mirror_reconfigure(struct bridge *);
 -static void mirror_reconfigure_one(struct mirror *);
 +static void mirror_reconfigure_one(struct mirror *, struct ovsrec_mirror *);
  static bool vlan_is_mirrored(const struct mirror *, int vlan);
  
 -static void brstp_reconfigure(struct bridge *);
 -static void brstp_adjust_timers(struct bridge *);
 -static void brstp_run(struct bridge *);
 -static void brstp_wait(struct bridge *);
 -
 -static void iface_create(struct port *, const char *name);
 +static struct iface *iface_create(struct port *port, 
 +                                  const struct ovsrec_interface *if_cfg);
  static void iface_destroy(struct iface *);
  static struct iface *iface_lookup(const struct bridge *, const char *name);
  static struct iface *iface_from_dp_ifidx(const struct bridge *,
@@@ -285,65 -285,53 +285,65 @@@ bridge_get_ifaces(struct svec *svec
      }
  }
  
 -/* The caller must already have called cfg_read(). */
  void
 -bridge_init(void)
 +bridge_init(const struct ovsrec_open_vswitch *cfg)
  {
 -    struct svec dpif_names;
 +    struct svec bridge_names;
 +    struct svec dpif_names, dpif_types;
      size_t i;
  
 -    unixctl_command_register("fdb/show", bridge_unixctl_fdb_show);
 +    unixctl_command_register("fdb/show", bridge_unixctl_fdb_show, NULL);
 +
 +    svec_init(&bridge_names);
 +    for (i = 0; i < cfg->n_bridges; i++) {
 +        svec_add(&bridge_names, cfg->bridges[i]->name);
 +    }
 +    svec_sort(&bridge_names);
  
      svec_init(&dpif_names);
 -    dp_enumerate(&dpif_names);
 -    for (i = 0; i < dpif_names.n; i++) {
 -        const char *dpif_name = dpif_names.names[i];
 +    svec_init(&dpif_types);
 +    dp_enumerate_types(&dpif_types);
 +    for (i = 0; i < dpif_types.n; i++) {
          struct dpif *dpif;
          int retval;
 +        size_t j;
  
 -        retval = dpif_open(dpif_name, &dpif);
 -        if (!retval) {
 -            struct svec all_names;
 -            size_t j;
 +        dp_enumerate_names(dpif_types.names[i], &dpif_names);
  
 -            svec_init(&all_names);
 -            dpif_get_all_names(dpif, &all_names);
 -            for (j = 0; j < all_names.n; j++) {
 -                if (cfg_has("bridge.%s.port", all_names.names[j])) {
 -                    goto found;
 +        for (j = 0; j < dpif_names.n; j++) {
 +            retval = dpif_open(dpif_names.names[j], dpif_types.names[i], &dpif);
 +            if (!retval) {
 +                struct svec all_names;
 +                size_t k;
 +
 +                svec_init(&all_names);
 +                dpif_get_all_names(dpif, &all_names);
 +                for (k = 0; k < all_names.n; k++) {
 +                    if (svec_contains(&bridge_names, all_names.names[k])) {
 +                        goto found;
 +                    }
                  }
 +                dpif_delete(dpif);
 +            found:
 +                svec_destroy(&all_names);
 +                dpif_close(dpif);
              }
 -            dpif_delete(dpif);
 -        found:
 -            svec_destroy(&all_names);
 -            dpif_close(dpif);
          }
      }
      svec_destroy(&dpif_names);
 +    svec_destroy(&dpif_types);
  
 -    unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows);
 +    unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows,
 +                             NULL);
  
      bond_init();
 -    bridge_reconfigure();
 +    bridge_reconfigure(cfg);
  }
  
  #ifdef HAVE_OPENSSL
  static bool
 -config_string_change(const char *key, char **valuep)
 +config_string_change(const char *value, char **valuep)
  {
 -    const char *value = cfg_get_string(0, "%s", key);
      if (value && (!*valuep || strcmp(value, *valuep))) {
          free(*valuep);
          *valuep = xstrdup(value);
  }
  
  static void
 -bridge_configure_ssl(void)
 +bridge_configure_ssl(const struct ovsrec_ssl *ssl)
  {
      /* XXX SSL should be configurable on a per-bridge basis.
       * XXX should be possible to de-configure SSL. */
      static char *cacert_file;
      struct stat s;
  
 -    if (config_string_change("ssl.private-key", &private_key_file)) {
 -        vconn_ssl_set_private_key_file(private_key_file);
 +    if (!ssl) {
 +        /* XXX We can't un-set SSL settings. */
 +        return;
 +    }
 +
 +    if (config_string_change(ssl->private_key, &private_key_file)) {
 +        stream_ssl_set_private_key_file(private_key_file);
      }
  
 -    if (config_string_change("ssl.certificate", &certificate_file)) {
 -        vconn_ssl_set_certificate_file(certificate_file);
 +    if (config_string_change(ssl->certificate, &certificate_file)) {
 +        stream_ssl_set_certificate_file(certificate_file);
      }
  
      /* We assume that even if the filename hasn't changed, if the CA cert 
       * boot-strapping mode.  This opens a small security hole, because
       * the old certificate will still be trusted until vSwitch is
       * restarted.  We may want to address this in vconn's SSL library. */
 -    if (config_string_change("ssl.ca-cert", &cacert_file)
 +    if (config_string_change(ssl->ca_cert, &cacert_file)
          || (cacert_file && stat(cacert_file, &s) && errno == ENOENT)) {
 -        vconn_ssl_set_ca_cert_file(cacert_file,
 -                                   cfg_get_bool(0, "ssl.bootstrap-ca-cert"));
 +        stream_ssl_set_ca_cert_file(cacert_file, ssl->bootstrap_ca_cert);
      }
  }
  #endif
  /* Attempt to create the network device 'iface_name' through the netdev
   * library. */
  static int
 -set_up_iface(const char *iface_name, bool create) 
 +set_up_iface(const struct ovsrec_interface *iface_cfg, struct iface *iface,
 +             bool create)
  {
 -    const char *type;
 -    const char *arg;
 -    struct svec arg_svec;
 -    struct shash args;
 -    int error;
 +    struct shash_node *node;
 +    struct shash options;
 +    int error = 0;
      size_t i;
  
 -    /* If a type is not explicitly declared, then assume it's an existing
 -     * "system" device. */
 -    type = cfg_get_string(0, "iface.%s.type", iface_name);
 -    if (!type || !strcmp(type, "system")) {
 -        return 0;
 +    shash_init(&options);
 +    for (i = 0; i < iface_cfg->n_options; i++) {
 +        shash_add(&options, iface_cfg->key_options[i],
 +                  xstrdup(iface_cfg->value_options[i]));
      }
  
 -    svec_init(&arg_svec);
 -    cfg_get_subsections(&arg_svec, "iface.%s.args", iface_name);
 +    if (create) {
 +        struct netdev_options netdev_options;
 +
 +        memset(&netdev_options, 0, sizeof netdev_options);
 +        netdev_options.name = iface_cfg->name;
 +        netdev_options.type = iface_cfg->type;
 +        netdev_options.args = &options;
 +        netdev_options.ethertype = NETDEV_ETH_TYPE_NONE;
 +        netdev_options.may_create = true;
 +        if (iface_is_internal(iface->port->bridge, iface_cfg->name)) {
 +            netdev_options.may_open = true;
 +        }
  
 -    shash_init(&args);
 -    SVEC_FOR_EACH (i, arg, &arg_svec) {
 -        const char *value;
 +        error = netdev_open(&netdev_options, &iface->netdev);
  
 -        value = cfg_get_string(0, "iface.%s.args.%s", iface_name, arg);
 -        if (value) {
 -            shash_add(&args, arg, xstrdup(value));
 +        if (iface->netdev) {
 +            netdev_get_carrier(iface->netdev, &iface->enabled);
          }
 -    }
 +    } else if (iface->netdev) {
 +        const char *netdev_type = netdev_get_type(iface->netdev);
 +        const char *iface_type = iface_cfg->type && strlen(iface_cfg->type)
 +                                  ? iface_cfg->type : NULL;
  
 -    if (create) {
 -        error = netdev_create(iface_name, type, &args);
 -    } else {
 -        /* xxx Check to make sure that the type hasn't changed. */
 -        error = netdev_reconfigure(iface_name, &args);
 +        if (!iface_type || !strcmp(netdev_type, iface_type)) {
 +            error = netdev_reconfigure(iface->netdev, &options);
 +        } else {
 +            VLOG_WARN("%s: attempting change device type from %s to %s",
 +                      iface_cfg->name, netdev_type, iface_type);
 +            error = EINVAL;
 +        }
      }
  
 -    svec_destroy(&arg_svec);
 -    shash_destroy(&args);
 +    SHASH_FOR_EACH (node, &options) {
 +        free(node->data);
 +    }
 +    shash_destroy(&options);
  
      return error;
  }
  
  static int
 -create_iface(const char *iface_name)
 -{
 -    return set_up_iface(iface_name, true);
 -}
 -
 -static int
 -reconfigure_iface(const char *iface_name)
 +reconfigure_iface(const struct ovsrec_interface *iface_cfg, struct iface *iface)
  {
 -    return set_up_iface(iface_name, false);
 +    return set_up_iface(iface_cfg, iface, false);
  }
  
 -static void
 -destroy_iface(const char *iface_name)
 -{
 -    netdev_destroy(iface_name);
 -}
 -
 -
 -/* iterate_and_prune_ifaces() callback function that opens the network device
 - * for 'iface', if it is not already open, and retrieves the interface's MAC
 - * address and carrier status. */
  static bool
- check_iface_netdev(struct bridge *br UNUSED, struct iface *iface,
-                    void *aux UNUSED)
 -init_iface_netdev(struct bridge *br OVS_UNUSED, struct iface *iface,
 -                  void *aux OVS_UNUSED)
++check_iface_netdev(struct bridge *br OVS_UNUSED, struct iface *iface,
++                   void *aux OVS_UNUSED)
  {
 -    if (iface->netdev) {
 -        return true;
 -    } else if (!netdev_open(iface->name, NETDEV_ETH_TYPE_NONE,
 -                            &iface->netdev)) {
 -        netdev_get_carrier(iface->netdev, &iface->enabled);
 -        return true;
 -    } else {
 -        /* If the network device can't be opened, then we're not going to try
 -         * to do anything with this interface. */
 -        return false;
 +    if (!iface->netdev) {
 +        int error = set_up_iface(iface->cfg, iface, true);
 +        if (error) {
 +            VLOG_WARN("could not open netdev on %s, dropping: %s", iface->name,
 +                                                               strerror(error));
 +            return false;
 +        }
      }
 +
 +    return true;
  }
  
  static bool
- check_iface_dp_ifidx(struct bridge *br, struct iface *iface, void *aux UNUSED)
+ check_iface_dp_ifidx(struct bridge *br, struct iface *iface,
+                      void *aux OVS_UNUSED)
  {
      if (iface->dp_ifidx >= 0) {
          VLOG_DBG("%s has interface %s on port %d",
  }
  
  static bool
- set_iface_properties(struct bridge *br UNUSED, struct iface *iface,
-                    void *aux UNUSED)
+ set_iface_properties(struct bridge *br OVS_UNUSED, struct iface *iface,
+                      void *aux OVS_UNUSED)
  {
 -    int rate, burst;
 -
      /* Set policing attributes. */
 -    rate = cfg_get_int(0, "port.%s.ingress.policing-rate", iface->name);
 -    burst = cfg_get_int(0, "port.%s.ingress.policing-burst", iface->name);
 -    netdev_set_policing(iface->netdev, rate, burst);
 +    netdev_set_policing(iface->netdev,
 +                        iface->cfg->ingress_policing_rate,
 +                        iface->cfg->ingress_policing_burst);
  
      /* Set MAC address of internal interfaces other than the local
       * interface. */
@@@ -533,67 -525,50 +534,67 @@@ iterate_and_prune_ifaces(struct bridge 
  }
  
  void
 -bridge_reconfigure(void)
 +bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
  {
 -    struct svec old_br, new_br;
 +    struct ovsdb_idl_txn *txn;
 +    struct shash old_br, new_br;
 +    struct shash_node *node;
      struct bridge *br, *next;
      size_t i;
      int sflow_bridge_number;
  
      COVERAGE_INC(bridge_reconfigure);
  
 +    txn = ovsdb_idl_txn_create(ovs_cfg->header_.table->idl);
 +
      /* Collect old and new bridges. */
 -    svec_init(&old_br);
 -    svec_init(&new_br);
 +    shash_init(&old_br);
 +    shash_init(&new_br);
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
 -        svec_add(&old_br, br->name);
 +        shash_add(&old_br, br->name, br);
 +    }
 +    for (i = 0; i < ovs_cfg->n_bridges; i++) {
 +        const struct ovsrec_bridge *br_cfg = ovs_cfg->bridges[i];
 +        if (!shash_add_once(&new_br, br_cfg->name, br_cfg)) {
 +            VLOG_WARN("more than one bridge named %s", br_cfg->name);
 +        }
      }
 -    cfg_get_subsections(&new_br, "bridge");
  
      /* Get rid of deleted bridges and add new bridges. */
 -    svec_sort(&old_br);
 -    svec_sort(&new_br);
 -    assert(svec_is_unique(&old_br));
 -    assert(svec_is_unique(&new_br));
      LIST_FOR_EACH_SAFE (br, next, struct bridge, node, &all_bridges) {
 -        if (!svec_contains(&new_br, br->name)) {
 +        struct ovsrec_bridge *br_cfg = shash_find_data(&new_br, br->name);
 +        if (br_cfg) {
 +            br->cfg = br_cfg;
 +        } else {
              bridge_destroy(br);
          }
      }
 -    for (i = 0; i < new_br.n; i++) {
 -        const char *name = new_br.names[i];
 -        if (!svec_contains(&old_br, name)) {
 -            bridge_create(name);
 +    SHASH_FOR_EACH (node, &new_br) {
 +        const char *br_name = node->name;
 +        const struct ovsrec_bridge *br_cfg = node->data;
 +        br = shash_find_data(&old_br, br_name);
 +        if (br) {
 +            /* If the bridge datapath type has changed, we need to tear it
 +             * down and recreate. */
 +            if (strcmp(br->cfg->datapath_type, br_cfg->datapath_type)) {
 +                bridge_destroy(br);
 +                bridge_create(br_cfg);
 +            }
 +        } else {
 +            bridge_create(br_cfg);
          }
      }
 -    svec_destroy(&old_br);
 -    svec_destroy(&new_br);
 +    shash_destroy(&old_br);
 +    shash_destroy(&new_br);
  
  #ifdef HAVE_OPENSSL
      /* Configure SSL. */
 -    bridge_configure_ssl();
 +    bridge_configure_ssl(ovs_cfg->ssl);
  #endif
  
      /* Reconfigure all bridges. */
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
 -        bridge_reconfigure_one(br);
 +        bridge_reconfigure_one(ovs_cfg, br);
      }
  
      /* Add and delete ports on all datapaths.
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
          struct odp_port *dpif_ports;
          size_t n_dpif_ports;
 -        struct svec want_ifaces;
 +        struct shash want_ifaces;
  
          dpif_port_list(br->dpif, &dpif_ports, &n_dpif_ports);
          bridge_get_all_ifaces(br, &want_ifaces);
          for (i = 0; i < n_dpif_ports; i++) {
              const struct odp_port *p = &dpif_ports[i];
 -            if (!svec_contains(&want_ifaces, p->devname)
 +            if (!shash_find(&want_ifaces, p->devname)
                  && strcmp(p->devname, br->name)) {
                  int retval = dpif_port_del(br->dpif, p->port);
                  if (retval) {
                               p->devname, dpif_name(br->dpif),
                               strerror(retval));
                  }
 -                destroy_iface(p->devname);
              }
          }
 -        svec_destroy(&want_ifaces);
 +        shash_destroy(&want_ifaces);
          free(dpif_ports);
      }
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
          struct odp_port *dpif_ports;
          size_t n_dpif_ports;
 -        struct svec cur_ifaces, want_ifaces, add_ifaces;
 +        struct shash cur_ifaces, want_ifaces;
 +        struct shash_node *node;
  
 +        /* Get the set of interfaces currently in this datapath. */
          dpif_port_list(br->dpif, &dpif_ports, &n_dpif_ports);
 -        svec_init(&cur_ifaces);
 +        shash_init(&cur_ifaces);
          for (i = 0; i < n_dpif_ports; i++) {
 -            svec_add(&cur_ifaces, dpif_ports[i].devname);
 +            const char *name = dpif_ports[i].devname;
 +            if (!shash_find(&cur_ifaces, name)) {
 +                shash_add(&cur_ifaces, name, NULL);
 +            }
          }
          free(dpif_ports);
 -        svec_sort_unique(&cur_ifaces);
 -        bridge_get_all_ifaces(br, &want_ifaces);
 -        svec_diff(&want_ifaces, &cur_ifaces, &add_ifaces, NULL, NULL);
 -
 -        for (i = 0; i < cur_ifaces.n; i++) {
 -            const char *if_name = cur_ifaces.names[i];
 -            reconfigure_iface(if_name);
 -        }
  
 -        for (i = 0; i < add_ifaces.n; i++) {
 -            const char *if_name = add_ifaces.names[i];
 -            bool internal;
 -            int error;
 +        /* Get the set of interfaces we want on this datapath. */
 +        bridge_get_all_ifaces(br, &want_ifaces);
  
 -            /* Attempt to create the network interface in case it
 -             * doesn't exist yet. */
 -            error = create_iface(if_name);
 -            if (error) {
 -                VLOG_WARN("could not create iface %s: %s\n", if_name,
 -                        strerror(error));
 -                continue;
 -            }
 +        SHASH_FOR_EACH (node, &want_ifaces) {
 +            const char *if_name = node->name;
 +            struct iface *iface = node->data;
  
 -            /* Add to datapath. */
 -            internal = iface_is_internal(br, if_name);
 -            error = dpif_port_add(br->dpif, if_name,
 -                                  internal ? ODP_PORT_INTERNAL : 0, NULL);
 -            if (error == EFBIG) {
 -                VLOG_ERR("ran out of valid port numbers on %s",
 -                         dpif_name(br->dpif));
 -                break;
 -            } else if (error) {
 -                VLOG_ERR("failed to add %s interface to %s: %s",
 -                         if_name, dpif_name(br->dpif), strerror(error));
 +            if (shash_find(&cur_ifaces, if_name)) {
 +                /* Already exists, just reconfigure it. */
 +                if (iface) {
 +                    reconfigure_iface(iface->cfg, iface);
 +                }
 +            } else {
 +                /* Need to add to datapath. */
 +                bool internal;
 +                int error;
 +
 +                /* Add to datapath. */
 +                internal = iface_is_internal(br, if_name);
 +                error = dpif_port_add(br->dpif, if_name,
 +                                      internal ? ODP_PORT_INTERNAL : 0, NULL);
 +                if (error == EFBIG) {
 +                    VLOG_ERR("ran out of valid port numbers on %s",
 +                             dpif_name(br->dpif));
 +                    break;
 +                } else if (error) {
 +                    VLOG_ERR("failed to add %s interface to %s: %s",
 +                             if_name, dpif_name(br->dpif), strerror(error));
 +                }
              }
          }
 -        svec_destroy(&cur_ifaces);
 -        svec_destroy(&want_ifaces);
 -        svec_destroy(&add_ifaces);
 +        shash_destroy(&cur_ifaces);
 +        shash_destroy(&want_ifaces);
      }
      sflow_bridge_number = 0;
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
          uint64_t dpid;
          struct iface *local_iface;
          struct iface *hw_addr_iface;
 -        struct netflow_options nf_options;
 +        char *dpid_string;
  
          bridge_fetch_dp_ifaces(br);
 -        iterate_and_prune_ifaces(br, init_iface_netdev, NULL);
  
 +        iterate_and_prune_ifaces(br, check_iface_netdev, NULL);
          iterate_and_prune_ifaces(br, check_iface_dp_ifidx, NULL);
  
          /* Pick local port hardware address, datapath ID. */
          dpid = bridge_pick_datapath_id(br, ea, hw_addr_iface);
          ofproto_set_datapath_id(br->ofproto, dpid);
  
 +        dpid_string = xasprintf("%012"PRIx64, dpid);
 +        ovsrec_bridge_set_datapath_id(br->cfg, dpid_string);
 +        free(dpid_string);
 +
          /* Set NetFlow configuration on this bridge. */
 -        memset(&nf_options, 0, sizeof nf_options);
 -        dpif_get_netflow_ids(br->dpif, &nf_options.engine_type,
 -                             &nf_options.engine_id);
 -        nf_options.active_timeout = -1;
 -
 -        if (cfg_has("netflow.%s.engine-type", br->name)) {
 -            nf_options.engine_type = cfg_get_int(0, "netflow.%s.engine-type", 
 -                    br->name);
 -        }
 -        if (cfg_has("netflow.%s.engine-id", br->name)) {
 -            nf_options.engine_id = cfg_get_int(0, "netflow.%s.engine-id",
 -                                               br->name);
 -        }
 -        if (cfg_has("netflow.%s.active-timeout", br->name)) {
 -            nf_options.active_timeout = cfg_get_int(0,
 -                                                    "netflow.%s.active-timeout",
 -                                                    br->name);
 -        }
 -        if (cfg_has("netflow.%s.add-id-to-iface", br->name)) {
 -            nf_options.add_id_to_iface = cfg_get_bool(0,
 -                                                   "netflow.%s.add-id-to-iface",
 -                                                    br->name);
 -        }
 -        if (nf_options.add_id_to_iface && nf_options.engine_id > 0x7f) {
 -            VLOG_WARN("bridge %s: netflow port mangling may conflict with "
 -                    "another vswitch, choose an engine id less than 128", 
 -                    br->name);
 -        }
 -        if (nf_options.add_id_to_iface && br->n_ports > 508) {
 -            VLOG_WARN("bridge %s: netflow port mangling will conflict with "
 -                    "another port when more than 508 ports are used", 
 -                    br->name);
 -        }
 -        svec_init(&nf_options.collectors);
 -        cfg_get_all_keys(&nf_options.collectors, "netflow.%s.host", br->name);
 -        if (ofproto_set_netflow(br->ofproto, &nf_options)) {
 -            VLOG_ERR("bridge %s: problem setting netflow collectors", 
 -                    br->name);
 -        }
 -        svec_destroy(&nf_options.collectors);
 -
 -        if (cfg_has("sflow.%s.host", br->name)) {
 +        if (br->cfg->netflow) {
 +            struct ovsrec_netflow *nf_cfg = br->cfg->netflow;
 +            struct netflow_options opts;
 +
 +            memset(&opts, 0, sizeof opts);
 +
 +            dpif_get_netflow_ids(br->dpif, &opts.engine_type, &opts.engine_id);
 +            if (nf_cfg->engine_type) {
 +                opts.engine_type = *nf_cfg->engine_type;
 +            }
 +            if (nf_cfg->engine_id) {
 +                opts.engine_id = *nf_cfg->engine_id;
 +            }
 +
 +            opts.active_timeout = nf_cfg->active_timeout;
 +            if (!opts.active_timeout) {
 +                opts.active_timeout = -1;
 +            } else if (opts.active_timeout < 0) {
 +                VLOG_WARN("bridge %s: active timeout interval set to negative "
 +                          "value, using default instead (%d seconds)", br->name,
 +                          NF_ACTIVE_TIMEOUT_DEFAULT);
 +                opts.active_timeout = -1;
 +            }
 +
 +            opts.add_id_to_iface = nf_cfg->add_id_to_interface;
 +            if (opts.add_id_to_iface) {
 +                if (opts.engine_id > 0x7f) {
 +                    VLOG_WARN("bridge %s: netflow port mangling may conflict "
 +                              "with another vswitch, choose an engine id less "
 +                              "than 128", br->name);
 +                }
 +                if (br->n_ports > 508) {
 +                    VLOG_WARN("bridge %s: netflow port mangling will conflict "
 +                              "with another port when more than 508 ports are "
 +                              "used", br->name);
 +                }
 +            }
 +
 +            opts.collectors.n = nf_cfg->n_targets;
 +            opts.collectors.names = nf_cfg->targets;
 +            if (ofproto_set_netflow(br->ofproto, &opts)) {
 +                VLOG_ERR("bridge %s: problem setting netflow collectors", 
 +                         br->name);
 +            }
 +        } else {
 +            ofproto_set_netflow(br->ofproto, NULL);
 +        }
 +
 +        /* Set sFlow configuration on this bridge. */
 +        if (br->cfg->sflow) {
 +            const struct ovsrec_sflow *sflow_cfg = br->cfg->sflow;
 +            const struct ovsrec_controller *ctrl;
              struct ofproto_sflow_options oso;
  
 -            svec_init(&oso.targets);
 -            cfg_get_all_keys(&oso.targets, "sflow.%s.host", br->name);
 +            memset(&oso, 0, sizeof oso);
 +
 +            oso.targets.n = sflow_cfg->n_targets;
 +            oso.targets.names = sflow_cfg->targets;
  
              oso.sampling_rate = SFL_DEFAULT_SAMPLING_RATE;
 -            if (cfg_has("sflow.%s.sampling", br->name)) {
 -                oso.sampling_rate = cfg_get_int(0, "sflow.%s.sampling",
 -                                                br->name);
 +            if (sflow_cfg->sampling) {
 +                oso.sampling_rate = *sflow_cfg->sampling;
              }
  
              oso.polling_interval = SFL_DEFAULT_POLLING_INTERVAL;
 -            if (cfg_has("sflow.%s.polling", br->name)) {
 -                oso.polling_interval = cfg_get_int(0, "sflow.%s.polling",
 -                                                   br->name);
 +            if (sflow_cfg->polling) {
 +                oso.polling_interval = *sflow_cfg->polling;
              }
  
              oso.header_len = SFL_DEFAULT_HEADER_SIZE;
 -            if (cfg_has("sflow.%s.header", br->name)) {
 -                oso.header_len = cfg_get_int(0, "sflow.%s.header", br->name);
 +            if (sflow_cfg->header) {
 +                oso.header_len = *sflow_cfg->header;
              }
  
              oso.sub_id = sflow_bridge_number++;
 -            oso.agent_device = (char *) cfg_get_string(0, "sflow.%s.agent",
 -                                                       br->name);
 -            oso.control_ip = (char *) cfg_get_string(0,
 -                                                     "bridge.%s.controller.ip",
 -                                                     br->name);
 +            oso.agent_device = sflow_cfg->agent;
 +
 +            ctrl = bridge_get_controller(ovs_cfg, br);
 +            oso.control_ip = ctrl ? ctrl->local_ip : NULL;
              ofproto_set_sflow(br->ofproto, &oso);
  
              svec_destroy(&oso.targets);
           * yet; when a controller is configured, resetting the datapath ID will
           * immediately disconnect from the controller, so it's better to set
           * the datapath ID before the controller. */
 -        bridge_reconfigure_controller(br);
 +        bridge_reconfigure_controller(ovs_cfg, br);
      }
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
          for (i = 0; i < br->n_ports; i++) {
          }
      }
      LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
 -        brstp_reconfigure(br);
          iterate_and_prune_ifaces(br, set_iface_properties, NULL);
      }
 +
 +    ovsrec_open_vswitch_set_cur_cfg(ovs_cfg, ovs_cfg->next_cfg);
 +
 +    ovsdb_idl_txn_commit(txn);
 +    ovsdb_idl_txn_destroy(txn); /* XXX */
 +}
 +
 +static const char *
 +bridge_get_other_config(const struct ovsrec_bridge *br_cfg, const char *key)
 +{
 +    size_t i;
 +
 +    for (i = 0; i < br_cfg->n_other_config; i++) {
 +        if (!strcmp(br_cfg->key_other_config[i], key)) {
 +            return br_cfg->value_other_config[i];
 +        }
 +    }
 +    return NULL;
  }
  
  static void
  bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
                            struct iface **hw_addr_iface)
  {
 -    uint64_t requested_ea;
 +    const char *hwaddr;
      size_t i, j;
      int error;
  
      *hw_addr_iface = NULL;
  
      /* Did the user request a particular MAC? */
 -    requested_ea = cfg_get_mac(0, "bridge.%s.mac", br->name);
 -    if (requested_ea) {
 -        eth_addr_from_uint64(requested_ea, ea);
 +    hwaddr = bridge_get_other_config(br->cfg, "hwaddr");
 +    if (hwaddr && eth_addr_from_string(hwaddr, ea)) {
          if (eth_addr_is_multicast(ea)) {
              VLOG_ERR("bridge %s: cannot set MAC address to multicast "
                       "address "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(ea));
          }
      }
  
 -    /* Otherwise choose the minimum MAC address among all of the interfaces.
 -     * (Xen uses FE:FF:FF:FF:FF:FF for virtual interfaces so this will get the
 -     * MAC of the physical interface in such an environment.) */
 +    /* Otherwise choose the minimum non-local MAC address among all of the
 +     * interfaces. */
      memset(ea, 0xff, sizeof ea);
      for (i = 0; i < br->n_ports; i++) {
          struct port *port = br->ports[i];
          uint8_t iface_ea[ETH_ADDR_LEN];
 -        uint64_t iface_ea_u64;
          struct iface *iface;
  
          /* Mirror output ports don't participate. */
          }
  
          /* Choose the MAC address to represent the port. */
 -        iface_ea_u64 = cfg_get_mac(0, "port.%s.mac", port->name);
 -        if (iface_ea_u64) {
 -            /* User specified explicitly. */
 -            eth_addr_from_uint64(iface_ea_u64, iface_ea);
 -
 +        if (port->cfg->mac && eth_addr_from_string(port->cfg->mac, iface_ea)) {
              /* Find the interface with this Ethernet address (if any) so that
               * we can provide the correct devname to the caller. */
              iface = NULL;
              }
  
              /* The local port doesn't count (since we're trying to choose its
 -             * MAC address anyway).  Other internal ports don't count because
 -             * we really want a physical MAC if we can get it, and internal
 -             * ports typically have randomly generated MACs. */
 -            if (iface->dp_ifidx == ODPP_LOCAL
 -                || cfg_get_bool(0, "iface.%s.internal", iface->name)) {
 +             * MAC address anyway). */
 +            if (iface->dp_ifidx == ODPP_LOCAL) {
                  continue;
              }
  
  
          /* Compare against our current choice. */
          if (!eth_addr_is_multicast(iface_ea) &&
 +            !eth_addr_is_local(iface_ea) &&
              !eth_addr_is_reserved(iface_ea) &&
              !eth_addr_is_zero(iface_ea) &&
              memcmp(iface_ea, ea, ETH_ADDR_LEN) < 0)
              *hw_addr_iface = iface;
          }
      }
 -    if (eth_addr_is_multicast(ea) || eth_addr_is_vif(ea)) {
 +    if (eth_addr_is_multicast(ea)) {
          memcpy(ea, br->default_ea, ETH_ADDR_LEN);
          *hw_addr_iface = NULL;
          VLOG_WARN("bridge %s: using default bridge Ethernet "
@@@ -960,11 -915,10 +961,11 @@@ bridge_pick_datapath_id(struct bridge *
       * stable from one run to the next, so that policy set on a datapath
       * "sticks".
       */
 +    const char *datapath_id;
      uint64_t dpid;
  
 -    dpid = cfg_get_dpid(0, "bridge.%s.datapath-id", br->name);
 -    if (dpid) {
 +    datapath_id = bridge_get_other_config(br->cfg, "datapath-id");
 +    if (datapath_id && dpid_from_string(datapath_id, &dpid)) {
          return dpid;
      }
  
@@@ -1063,6 -1017,7 +1064,6 @@@ bridge_wait(void
  
          mac_learning_wait(br->ml);
          bond_wait(br);
 -        brstp_wait(br);
      }
  }
  
@@@ -1098,8 -1053,7 +1099,8 @@@ bridge_get_local_iface(struct bridge *b
  \f
  /* Bridge unixctl user interface functions. */
  static void
 -bridge_unixctl_fdb_show(struct unixctl_conn *conn, const char *args)
 +bridge_unixctl_fdb_show(struct unixctl_conn *conn,
-                         const char *args, void *aux UNUSED)
++                        const char *args, void *aux OVS_UNUSED)
  {
      struct ds ds = DS_EMPTY_INITIALIZER;
      const struct bridge *br;
  }
  \f
  /* Bridge reconfiguration functions. */
 -
  static struct bridge *
 -bridge_create(const char *name)
 +bridge_create(const struct ovsrec_bridge *br_cfg)
  {
      struct bridge *br;
      int error;
  
 -    assert(!bridge_lookup(name));
 -    br = xcalloc(1, sizeof *br);
 +    assert(!bridge_lookup(br_cfg->name));
 +    br = xzalloc(sizeof *br);
  
 -    error = dpif_create_and_open(name, &br->dpif);
 +    error = dpif_create_and_open(br_cfg->name, br_cfg->datapath_type,
 +                                 &br->dpif);
      if (error) {
          free(br);
          return NULL;
      }
      dpif_flow_flush(br->dpif);
  
 -    error = ofproto_create(name, &bridge_ofhooks, br, &br->ofproto);
 +    error = ofproto_create(br_cfg->name, br_cfg->datapath_type, &bridge_ofhooks,
 +                           br, &br->ofproto);
      if (error) {
 -        VLOG_ERR("failed to create switch %s: %s", name, strerror(error));
 +        VLOG_ERR("failed to create switch %s: %s", br_cfg->name,
 +                 strerror(error));
          dpif_delete(br->dpif);
          dpif_close(br->dpif);
          free(br);
          return NULL;
      }
  
 -    br->name = xstrdup(name);
 +    br->name = xstrdup(br_cfg->name);
 +    br->cfg = br_cfg;
      br->ml = mac_learning_create();
      br->sent_config_request = false;
 -    eth_addr_random(br->default_ea);
 +    eth_addr_nicira_random(br->default_ea);
  
      port_array_init(&br->ifaces);
  
@@@ -1226,8 -1177,7 +1227,8 @@@ bridge_get_datapathid(const char *name
  /* Handle requests for a listing of all flows known by the OpenFlow
   * stack, including those normally hidden. */
  static void
 -bridge_unixctl_dump_flows(struct unixctl_conn *conn, const char *args)
 +bridge_unixctl_dump_flows(struct unixctl_conn *conn,
-                           const char *args, void *aux UNUSED)
++                          const char *args, void *aux OVS_UNUSED)
  {
      struct bridge *br;
      struct ds results;
@@@ -1257,6 -1207,7 +1258,6 @@@ bridge_run_one(struct bridge *br
  
      mac_learning_run(br->ml, ofproto_get_revalidate_set(br->ofproto));
      bond_run(br);
 -    brstp_run(br);
  
      error = ofproto_run2(br->ofproto, br->flush);
      br->flush = false;
      return error;
  }
  
 -static const char *
 -bridge_get_controller(const struct bridge *br)
 +static const struct ovsrec_controller *
 +bridge_get_controller(const struct ovsrec_open_vswitch *ovs_cfg,
 +                      const struct bridge *br)
  {
 -    const char *controller;
 +    const struct ovsrec_controller *controller;
  
 -    controller = cfg_get_string(0, "bridge.%s.controller", br->name);
 -    if (!controller) {
 -        controller = cfg_get_string(0, "mgmt.controller");
 +    controller = (br->cfg->controller ? br->cfg->controller
 +                  : ovs_cfg->controller ? ovs_cfg->controller
 +                  : NULL);
 +
 +    if (controller && !strcmp(controller->target, "none")) {
 +        return NULL;
      }
 -    return controller && controller[0] ? controller : NULL;
 +
 +    return controller;
  }
  
  static bool
@@@ -1298,64 -1244,66 +1299,64 @@@ check_duplicate_ifaces(struct bridge *b
  }
  
  static void
 -bridge_reconfigure_one(struct bridge *br)
 +bridge_reconfigure_one(const struct ovsrec_open_vswitch *ovs_cfg,
 +                       struct bridge *br)
  {
 -    struct svec old_ports, new_ports, ifaces;
 +    struct shash old_ports, new_ports;
 +    struct svec ifaces;
      struct svec listeners, old_listeners;
      struct svec snoops, old_snoops;
 +    struct shash_node *node;
      size_t i;
  
      /* Collect old ports. */
 -    svec_init(&old_ports);
 +    shash_init(&old_ports);
      for (i = 0; i < br->n_ports; i++) {
 -        svec_add(&old_ports, br->ports[i]->name);
 +        shash_add(&old_ports, br->ports[i]->name, br->ports[i]);
      }
 -    svec_sort(&old_ports);
 -    assert(svec_is_unique(&old_ports));
  
      /* Collect new ports. */
 -    svec_init(&new_ports);
 -    cfg_get_all_keys(&new_ports, "bridge.%s.port", br->name);
 -    svec_sort(&new_ports);
 -    if (bridge_get_controller(br)) {
 +    shash_init(&new_ports);
 +    for (i = 0; i < br->cfg->n_ports; i++) {
 +        const char *name = br->cfg->ports[i]->name;
 +        if (!shash_add_once(&new_ports, name, br->cfg->ports[i])) {
 +            VLOG_WARN("bridge %s: %s specified twice as bridge port",
 +                      br->name, name);
 +        }
 +    }
 +
 +    /* If we have a controller, then we need a local port.  Complain if the
 +     * user didn't specify one.
 +     *
 +     * XXX perhaps we should synthesize a port ourselves in this case. */
 +    if (bridge_get_controller(ovs_cfg, br)) {
          char local_name[IF_NAMESIZE];
          int error;
  
          error = dpif_port_get_name(br->dpif, ODPP_LOCAL,
                                     local_name, sizeof local_name);
 -        if (!error && !svec_contains(&new_ports, local_name)) {
 -            svec_add(&new_ports, local_name);
 -            svec_sort(&new_ports);
 +        if (!error && !shash_find(&new_ports, local_name)) {
 +            VLOG_WARN("bridge %s: controller specified but no local port "
 +                      "(port named %s) defined",
 +                      br->name, local_name);
          }
      }
 -    if (!svec_is_unique(&new_ports)) {
 -        VLOG_WARN("bridge %s: %s specified twice as bridge port",
 -                  br->name, svec_get_duplicate(&new_ports));
 -        svec_unique(&new_ports);
 -    }
 -
 -    ofproto_set_mgmt_id(br->ofproto, mgmt_id);
  
      /* Get rid of deleted ports and add new ports. */
 -    for (i = 0; i < br->n_ports; ) {
 -        struct port *port = br->ports[i];
 -        if (!svec_contains(&new_ports, port->name)) {
 -            port_destroy(port);
 -        } else {
 -            i++;
 +    SHASH_FOR_EACH (node, &old_ports) {
 +        if (!shash_find(&new_ports, node->name)) {
 +            port_destroy(node->data);
          }
      }
 -    for (i = 0; i < new_ports.n; i++) {
 -        const char *name = new_ports.names[i];
 -        if (!svec_contains(&old_ports, name)) {
 -            port_create(br, name);
 +    SHASH_FOR_EACH (node, &new_ports) {
 +        struct port *port = shash_find_data(&old_ports, node->name);
 +        if (!port) {
 +            port = port_create(br, node->name);
          }
 +        port_reconfigure(port, node->data);
      }
 -    svec_destroy(&old_ports);
 -    svec_destroy(&new_ports);
 -
 -    /* Reconfigure all ports. */
 -    for (i = 0; i < br->n_ports; i++) {
 -        port_reconfigure(br->ports[i]);
 -    }
 +    shash_destroy(&old_ports);
 +    shash_destroy(&new_ports);
  
      /* Check and delete duplicate interfaces. */
      svec_init(&ifaces);
       * versa.  (XXX Should we delete all flows if we are switching from one
       * controller to another?) */
  
 +#if 0
      /* Configure OpenFlow management listeners. */
      svec_init(&listeners);
      cfg_get_all_strings(&listeners, "bridge.%s.openflow.listeners", br->name);
      }
      svec_destroy(&snoops);
      svec_destroy(&old_snoops);
 +#else
 +    /* Default listener. */
 +    svec_init(&listeners);
 +    svec_add_nocopy(&listeners, xasprintf("punix:%s/%s.mgmt",
 +                                          ovs_rundir, br->name));
 +    svec_init(&old_listeners);
 +    ofproto_get_listeners(br->ofproto, &old_listeners);
 +    if (!svec_equal(&listeners, &old_listeners)) {
 +        ofproto_set_listeners(br->ofproto, &listeners);
 +    }
 +    svec_destroy(&listeners);
 +    svec_destroy(&old_listeners);
 +
 +    /* Default snoop. */
 +    svec_init(&snoops);
 +    svec_add_nocopy(&snoops, xasprintf("punix:%s/%s.snoop",
 +                                       ovs_rundir, br->name));
 +    svec_init(&old_snoops);
 +    ofproto_get_snoops(br->ofproto, &old_snoops);
 +    if (!svec_equal(&snoops, &old_snoops)) {
 +        ofproto_set_snoops(br->ofproto, &snoops);
 +    }
 +    svec_destroy(&snoops);
 +    svec_destroy(&old_snoops);
 +#endif
  
      mirror_reconfigure(br);
  }
  
  static void
 -bridge_reconfigure_controller(struct bridge *br)
 +bridge_reconfigure_controller(const struct ovsrec_open_vswitch *ovs_cfg,
 +                              struct bridge *br)
  {
      char *pfx = xasprintf("bridge.%s.controller", br->name);
 -    const char *controller;
 +    const struct ovsrec_controller *c;
  
 -    controller = bridge_get_controller(br);
 -    if ((br->controller != NULL) != (controller != NULL)) {
 +    c = bridge_get_controller(ovs_cfg, br);
 +    if ((br->controller != NULL) != (c != NULL)) {
          ofproto_flush_flows(br->ofproto);
      }
      free(br->controller);
 -    br->controller = controller ? xstrdup(controller) : NULL;
 +    br->controller = c ? xstrdup(c->target) : NULL;
  
 -    if (controller) {
 -        const char *fail_mode;
 +    if (c) {
          int max_backoff, probe;
          int rate_limit, burst_limit;
  
 -        if (!strcmp(controller, "discover")) {
 -            bool update_resolv_conf = true;
 -
 -            if (cfg_has("%s.update-resolv.conf", pfx)) {
 -                update_resolv_conf = cfg_get_bool(0, "%s.update-resolv.conf",
 -                        pfx);
 -            }
 +        if (!strcmp(c->target, "discover")) {
              ofproto_set_discovery(br->ofproto, true,
 -                                  cfg_get_string(0, "%s.accept-regex", pfx),
 -                                  update_resolv_conf);
 +                                  c->discover_accept_regex,
 +                                  c->discover_update_resolv_conf);
          } else {
              struct iface *local_iface;
 +            struct in_addr ip;
              bool in_band;
  
 -            in_band = (!cfg_is_valid(CFG_BOOL | CFG_REQUIRED,
 -                                     "%s.in-band", pfx)
 -                       || cfg_get_bool(0, "%s.in-band", pfx));
 +            in_band = (!c->connection_mode
 +                       || !strcmp(c->connection_mode, "out-of-band"));
              ofproto_set_discovery(br->ofproto, false, NULL, NULL);
              ofproto_set_in_band(br->ofproto, in_band);
  
              local_iface = bridge_get_local_iface(br);
 -            if (local_iface
 -                && cfg_is_valid(CFG_IP | CFG_REQUIRED, "%s.ip", pfx)) {
 +            if (local_iface && c->local_ip && inet_aton(c->local_ip, &ip)) {
                  struct netdev *netdev = local_iface->netdev;
 -                struct in_addr ip, mask, gateway;
 -                ip.s_addr = cfg_get_ip(0, "%s.ip", pfx);
 -                mask.s_addr = cfg_get_ip(0, "%s.netmask", pfx);
 -                gateway.s_addr = cfg_get_ip(0, "%s.gateway", pfx);
 +                struct in_addr mask, gateway;
 +
 +                if (!c->local_netmask || !inet_aton(c->local_netmask, &mask)) {
 +                    mask.s_addr = 0;
 +                }
 +                if (!c->local_gateway
 +                    || !inet_aton(c->local_gateway, &gateway)) {
 +                    gateway.s_addr = 0;
 +                }
  
                  netdev_turn_flags_on(netdev, NETDEV_UP, true);
                  if (!mask.s_addr) {
              }
          }
  
 -        fail_mode = cfg_get_string(0, "%s.fail-mode", pfx);
 -        if (!fail_mode) {
 -            fail_mode = cfg_get_string(0, "mgmt.fail-mode");
 -        }
          ofproto_set_failure(br->ofproto,
 -                            (!fail_mode
 -                             || !strcmp(fail_mode, "standalone")
 -                             || !strcmp(fail_mode, "open")));
 -
 -        probe = cfg_get_int(0, "%s.inactivity-probe", pfx);
 -        if (probe < 5) {
 -            probe = cfg_get_int(0, "mgmt.inactivity-probe");
 -            if (probe < 5) {
 -                probe = 5;
 -            }
 -        }
 +                            (!c->fail_mode
 +                             || !strcmp(c->fail_mode, "standalone")
 +                             || !strcmp(c->fail_mode, "open")));
 +
 +        probe = c->inactivity_probe ? *c->inactivity_probe / 1000 : 5;
          ofproto_set_probe_interval(br->ofproto, probe);
  
 -        max_backoff = cfg_get_int(0, "%s.max-backoff", pfx);
 -        if (!max_backoff) {
 -            max_backoff = cfg_get_int(0, "mgmt.max-backoff");
 -            if (!max_backoff) {
 -                max_backoff = 8;
 -            }
 -        }
 +        max_backoff = c->max_backoff ? *c->max_backoff / 1000 : 8;
          ofproto_set_max_backoff(br->ofproto, max_backoff);
  
 -        rate_limit = cfg_get_int(0, "%s.rate-limit", pfx);
 -        if (!rate_limit) {
 -            rate_limit = cfg_get_int(0, "mgmt.rate-limit");
 -        }
 -        burst_limit = cfg_get_int(0, "%s.burst-limit", pfx);
 -        if (!burst_limit) {
 -            burst_limit = cfg_get_int(0, "mgmt.burst-limit");
 -        }
 +        rate_limit = c->controller_rate_limit ? *c->controller_rate_limit : 0;
 +        burst_limit = c->controller_burst_limit ? *c->controller_burst_limit : 0;
          ofproto_set_rate_limit(br->ofproto, rate_limit, burst_limit);
 -
 -        ofproto_set_stp(br->ofproto, cfg_get_bool(0, "%s.stp", pfx));
 -
 -        if (cfg_has("%s.commands.acl", pfx)) {
 -            struct svec command_acls;
 -            char *command_acl;
 -
 -            svec_init(&command_acls);
 -            cfg_get_all_strings(&command_acls, "%s.commands.acl", pfx);
 -            command_acl = svec_join(&command_acls, ",", "");
 -
 -            ofproto_set_remote_execution(br->ofproto, command_acl,
 -                                         cfg_get_string(0, "%s.commands.dir",
 -                                                        pfx));
 -
 -            svec_destroy(&command_acls);
 -            free(command_acl);
 -        } else {
 -            ofproto_set_remote_execution(br->ofproto, NULL, NULL);
 -        }
      } else {
          union ofp_action action;
          flow_t flow;
          ofproto_set_max_backoff(br->ofproto, 1);
          ofproto_set_probe_interval(br->ofproto, 5);
          ofproto_set_failure(br->ofproto, false);
 -        ofproto_set_stp(br->ofproto, false);
      }
      free(pfx);
  
  }
  
  static void
 -bridge_get_all_ifaces(const struct bridge *br, struct svec *ifaces)
 +bridge_get_all_ifaces(const struct bridge *br, struct shash *ifaces)
  {
      size_t i, j;
  
 -    svec_init(ifaces);
 +    shash_init(ifaces);
      for (i = 0; i < br->n_ports; i++) {
          struct port *port = br->ports[i];
          for (j = 0; j < port->n_ifaces; j++) {
              struct iface *iface = port->ifaces[j];
 -            svec_add(ifaces, iface->name);
 +            shash_add_once(ifaces, iface->name, iface);
          }
 -        if (port->n_ifaces > 1
 -            && cfg_get_bool(0, "bonding.%s.fake-iface", port->name)) {
 -            svec_add(ifaces, port->name);
 +        if (port->n_ifaces > 1 && port->cfg->bond_fake_iface) {
 +            shash_add_once(ifaces, port->name, NULL);
          }
      }
 -    svec_sort_unique(ifaces);
  }
  
  /* For robustness, in case the administrator moves around datapath ports behind
@@@ -1598,13 -1567,6 +1599,13 @@@ bridge_fetch_dp_ifaces(struct bridge *b
                  port_array_set(&br->ifaces, p->port, iface);
                  iface->dp_ifidx = p->port;
              }
 +
 +            if (iface->cfg) {
 +                int64_t ofport = (iface->dp_ifidx >= 0
 +                                  ? odp_port_to_ofp_port(iface->dp_ifidx)
 +                                  : -1);
 +                ovsrec_interface_set_ofport(iface->cfg, &ofport, 1);
 +            }
          }
      }
      free(dpif_ports);
@@@ -1834,6 -1796,18 +1835,6 @@@ set_dst(struct dst *p, const flow_t *fl
          const struct port *in_port, const struct port *out_port,
          tag_type *tags)
  {
 -    /* STP handling.
 -     *
 -     * XXX This uses too many tags: any broadcast flow will get one tag per
 -     * destination port, and thus a broadcast on a switch of any size is likely
 -     * to have all tag bits set.  We should figure out a way to be smarter.
 -     *
 -     * This is OK when STP is disabled, because stp_state_tag is 0 then. */
 -    *tags |= out_port->stp_state_tag;
 -    if (!(out_port->stp_state & (STP_DISABLED | STP_FORWARDING))) {
 -        return false;
 -    }
 -
      p->vlan = (out_port->vlan >= 0 ? OFP_VLAN_NONE
                : in_port->vlan >= 0 ? in_port->vlan
                : ntohs(flow->dl_vlan));
@@@ -1926,6 -1900,7 +1927,6 @@@ compose_dsts(const struct bridge *br, c
      struct dst *dst = dsts;
      size_t i;
  
 -    *tags |= in_port->stp_state_tag;
      if (out_port == FLOOD_PORT) {
          /* XXX use ODP_FLOOD if no vlans or bonding. */
          /* XXX even better, define each VLAN as a datapath port group */
      return dst - dsts;
  }
  
- static void UNUSED
+ static void OVS_UNUSED
  print_dsts(const struct dst *dsts, size_t n)
  {
      for (; n--; dsts++) {
@@@ -2154,6 -2129,13 +2155,6 @@@ process_flow(struct bridge *br, const f
          goto done;
      }
  
 -    /* Drop frames for ports that STP wants entirely killed (both for
 -     * forwarding and for learning).  Later, after we do learning, we'll drop
 -     * the frames that STP wants to do learning but not forwarding on. */
 -    if (in_port->stp_state & (STP_LISTENING | STP_BLOCKING)) {
 -        goto done;
 -    }
 -
      /* Drop frames for reserved multicast addresses. */
      if (eth_addr_is_reserved(flow->dl_dst)) {
          goto done;
          return false;
      }
  
 -    /* Don't send packets out their input ports.  Don't forward frames that STP
 -     * wants us to discard. */
 -    if (in_port == out_port || in_port->stp_state == STP_LEARNING) {
 +    /* Don't send packets out their input ports. */
 +    if (in_port == out_port) {
          out_port = NULL;
      }
  
@@@ -2268,6 -2251,14 +2269,6 @@@ bridge_normal_ofhook_cb(const flow_t *f
  {
      struct bridge *br = br_;
  
 -#if 0
 -    if (flow->dl_type == htons(OFP_DL_TYPE_NOT_ETH_TYPE)
 -        && eth_addr_equals(flow->dl_dst, stp_eth_addr)) {
 -        brstp_receive(br, flow, payload);
 -        return true;
 -    }
 -#endif
 -
      COVERAGE_INC(bridge_process_flow);
      return process_flow(br, flow, packet, actions, tags, nf_output_iface);
  }
@@@ -2708,8 -2699,7 +2709,8 @@@ bond_send_learning_packets(struct port 
  /* Bonding unixctl user interface functions. */
  
  static void
 -bond_unixctl_list(struct unixctl_conn *conn, const char *args OVS_UNUSED)
 +bond_unixctl_list(struct unixctl_conn *conn,
-                   const char *args UNUSED, void *aux UNUSED)
++                  const char *args OVS_UNUSED, void *aux OVS_UNUSED)
  {
      struct ds ds = DS_EMPTY_INITIALIZER;
      const struct bridge *br;
@@@ -2759,8 -2749,7 +2760,8 @@@ bond_find(const char *name
  }
  
  static void
 -bond_unixctl_show(struct unixctl_conn *conn, const char *args)
 +bond_unixctl_show(struct unixctl_conn *conn,
-                   const char *args, void *aux UNUSED)
++                  const char *args, void *aux OVS_UNUSED)
  {
      struct ds ds = DS_EMPTY_INITIALIZER;
      const struct port *port;
  }
  
  static void
 -bond_unixctl_migrate(struct unixctl_conn *conn, const char *args_)
 +bond_unixctl_migrate(struct unixctl_conn *conn, const char *args_,
-                      void *aux UNUSED)
++                     void *aux OVS_UNUSED)
  {
      char *args = (char *) args_;
      char *save_ptr = NULL;
  }
  
  static void
 -bond_unixctl_set_active_slave(struct unixctl_conn *conn, const char *args_)
 +bond_unixctl_set_active_slave(struct unixctl_conn *conn, const char *args_,
-                               void *aux UNUSED)
++                              void *aux OVS_UNUSED)
  {
      char *args = (char *) args_;
      char *save_ptr = NULL;
@@@ -2963,22 -2950,19 +2964,22 @@@ enable_slave(struct unixctl_conn *conn
  }
  
  static void
 -bond_unixctl_enable_slave(struct unixctl_conn *conn, const char *args)
 +bond_unixctl_enable_slave(struct unixctl_conn *conn, const char *args,
-                           void *aux UNUSED)
++                          void *aux OVS_UNUSED)
  {
      enable_slave(conn, args, true);
  }
  
  static void
 -bond_unixctl_disable_slave(struct unixctl_conn *conn, const char *args)
 +bond_unixctl_disable_slave(struct unixctl_conn *conn, const char *args,
-                            void *aux UNUSED)
++                           void *aux OVS_UNUSED)
  {
      enable_slave(conn, args, false);
  }
  
  static void
 -bond_unixctl_hash(struct unixctl_conn *conn, const char *args)
 +bond_unixctl_hash(struct unixctl_conn *conn, const char *args,
-                   void *aux UNUSED)
++                  void *aux OVS_UNUSED)
  {
        uint8_t mac[ETH_ADDR_LEN];
        uint8_t hash;
  static void
  bond_init(void)
  {
 -    unixctl_command_register("bond/list", bond_unixctl_list);
 -    unixctl_command_register("bond/show", bond_unixctl_show);
 -    unixctl_command_register("bond/migrate", bond_unixctl_migrate);
 +    unixctl_command_register("bond/list", bond_unixctl_list, NULL);
 +    unixctl_command_register("bond/show", bond_unixctl_show, NULL);
 +    unixctl_command_register("bond/migrate", bond_unixctl_migrate, NULL);
      unixctl_command_register("bond/set-active-slave",
 -                             bond_unixctl_set_active_slave);
 -    unixctl_command_register("bond/enable-slave", bond_unixctl_enable_slave);
 -    unixctl_command_register("bond/disable-slave", bond_unixctl_disable_slave);
 -    unixctl_command_register("bond/hash", bond_unixctl_hash);
 +                             bond_unixctl_set_active_slave, NULL);
 +    unixctl_command_register("bond/enable-slave", bond_unixctl_enable_slave,
 +                             NULL);
 +    unixctl_command_register("bond/disable-slave", bond_unixctl_disable_slave,
 +                             NULL);
 +    unixctl_command_register("bond/hash", bond_unixctl_hash, NULL);
  }
  \f
  /* Port functions. */
  
 -static void
 +static struct port *
  port_create(struct bridge *br, const char *name)
  {
      struct port *port;
  
 -    port = xcalloc(1, sizeof *port);
 +    port = xzalloc(sizeof *port);
      port->bridge = br;
      port->port_idx = br->n_ports;
      port->vlan = -1;
      port->trunks = NULL;
      port->name = xstrdup(name);
      port->active_iface = -1;
 -    port->stp_state = STP_DISABLED;
 -    port->stp_state_tag = 0;
  
      if (br->n_ports >= br->allocated_ports) {
          br->ports = x2nrealloc(br->ports, &br->allocated_ports,
  
      VLOG_INFO("created port %s on bridge %s", port->name, br->name);
      bridge_flush(br);
 +
 +    return port;
  }
  
  static void
 -port_reconfigure(struct port *port)
 +port_reconfigure(struct port *port, const struct ovsrec_port *cfg)
  {
 -    bool bonded = cfg_has_section("bonding.%s", port->name);
 -    struct svec old_ifaces, new_ifaces;
 +    struct shash old_ifaces, new_ifaces;
 +    struct shash_node *node;
      unsigned long *trunks;
      int vlan;
      size_t i;
  
 +    port->cfg = cfg;
 +
      /* Collect old and new interfaces. */
 -    svec_init(&old_ifaces);
 -    svec_init(&new_ifaces);
 +    shash_init(&old_ifaces);
 +    shash_init(&new_ifaces);
      for (i = 0; i < port->n_ifaces; i++) {
 -        svec_add(&old_ifaces, port->ifaces[i]->name);
 +        shash_add(&old_ifaces, port->ifaces[i]->name, port->ifaces[i]);
      }
 -    svec_sort(&old_ifaces);
 -    if (bonded) {
 -        cfg_get_all_keys(&new_ifaces, "bonding.%s.slave", port->name);
 -        if (!new_ifaces.n) {
 -            VLOG_ERR("port %s: no interfaces specified for bonded port",
 -                     port->name);
 -        } else if (new_ifaces.n == 1) {
 -            VLOG_WARN("port %s: only 1 interface specified for bonded port",
 -                      port->name);
 -        }
 -
 -        port->updelay = cfg_get_int(0, "bonding.%s.updelay", port->name);
 -        if (port->updelay < 0) {
 -            port->updelay = 0;
 +    for (i = 0; i < cfg->n_interfaces; i++) {
 +        const char *name = cfg->interfaces[i]->name;
 +        if (!shash_add_once(&new_ifaces, name, cfg->interfaces[i])) {
 +            VLOG_WARN("port %s: %s specified twice as port interface",
 +                      port->name, name);
          }
 -        port->downdelay = cfg_get_int(0, "bonding.%s.downdelay", port->name);
 -        if (port->downdelay < 0) {
 -            port->downdelay = 0;
 -        }
 -    } else {
 -        svec_init(&new_ifaces);
 -        svec_add(&new_ifaces, port->name);
 +    }
 +    port->updelay = cfg->bond_updelay;
 +    if (port->updelay < 0) {
 +        port->updelay = 0;
 +    }
 +    port->updelay = cfg->bond_downdelay;
 +    if (port->downdelay < 0) {
 +        port->downdelay = 0;
      }
  
      /* Get rid of deleted interfaces and add new interfaces. */
 -    for (i = 0; i < port->n_ifaces; i++) {
 -        struct iface *iface = port->ifaces[i];
 -        if (!svec_contains(&new_ifaces, iface->name)) {
 -            iface_destroy(iface);
 -        } else {
 -            i++;
 +    SHASH_FOR_EACH (node, &old_ifaces) {
 +        if (!shash_find(&new_ifaces, node->name)) {
 +            iface_destroy(node->data);
          }
      }
 -    for (i = 0; i < new_ifaces.n; i++) {
 -        const char *name = new_ifaces.names[i];
 -        if (!svec_contains(&old_ifaces, name)) {
 -            iface_create(port, name);
 +    SHASH_FOR_EACH (node, &new_ifaces) {
 +        const struct ovsrec_interface *if_cfg = node->data;
 +        struct iface *iface;
 +
 +        iface = shash_find_data(&old_ifaces, if_cfg->name);
 +        if (!iface) {
 +            iface_create(port, if_cfg);
 +        } else {
 +            iface->cfg = if_cfg;
          }
      }
  
      /* Get VLAN tag. */
      vlan = -1;
 -    if (cfg_has("vlan.%s.tag", port->name)) {
 -        if (!bonded) {
 -            vlan = cfg_get_vlan(0, "vlan.%s.tag", port->name);
 +    if (cfg->tag) {
 +        if (port->n_ifaces < 2) {
 +            vlan = *cfg->tag;
              if (vlan >= 0 && vlan <= 4095) {
                  VLOG_DBG("port %s: assigning VLAN tag %d", port->name, vlan);
 +            } else {
 +                vlan = -1;
              }
          } else {
              /* It's possible that bonded, VLAN-tagged ports make sense.  Maybe
      /* Get trunked VLANs. */
      trunks = NULL;
      if (vlan < 0) {
 -        size_t n_trunks, n_errors;
 +        size_t n_errors;
          size_t i;
  
          trunks = bitmap_allocate(4096);
 -        n_trunks = cfg_count("vlan.%s.trunks", port->name);
          n_errors = 0;
 -        for (i = 0; i < n_trunks; i++) {
 -            int trunk = cfg_get_vlan(i, "vlan.%s.trunks", port->name);
 +        for (i = 0; i < cfg->n_trunks; i++) {
 +            int trunk = cfg->trunks[i];
              if (trunk >= 0) {
                  bitmap_set1(trunks, trunk);
              } else {
          }
          if (n_errors) {
              VLOG_ERR("port %s: invalid values for %zu trunk VLANs",
 -                     port->name, n_trunks);
 +                     port->name, cfg->n_trunks);
          }
 -        if (n_errors == n_trunks) {
 +        if (n_errors == cfg->n_trunks) {
              if (n_errors) {
                  VLOG_ERR("port %s: no valid trunks, trunking all VLANs",
                           port->name);
              bitmap_set_multiple(trunks, 0, 4096, 1);
          }
      } else {
 -        if (cfg_has("vlan.%s.trunks", port->name)) {
 -            VLOG_ERR("ignoring vlan.%s.trunks in favor of vlan.%s.vlan",
 -                     port->name, port->name);
 +        if (cfg->n_trunks) {
 +            VLOG_ERR("port %s: ignoring trunks in favor of implicit vlan",
 +                     port->name);
          }
      }
      if (trunks == NULL
      bitmap_free(port->trunks);
      port->trunks = trunks;
  
 -    svec_destroy(&old_ifaces);
 -    svec_destroy(&new_ifaces);
 +    shash_destroy(&old_ifaces);
 +    shash_destroy(&new_ifaces);
  }
  
  static void
@@@ -3162,7 -3147,7 +3163,7 @@@ port_destroy(struct port *port
      if (port) {
          struct bridge *br = port->bridge;
          struct port *del;
 -        size_t i;
 +        int i;
  
          proc_net_compat_update_vlan(port->name, NULL, 0);
          proc_net_compat_update_bond(port->name, NULL);
@@@ -3233,7 -3218,6 +3234,7 @@@ port_update_bonding(struct port *port
              free(port->bond_hash);
              port->bond_hash = NULL;
              port->bond_compat_is_stale = true;
 +            port->bond_fake_iface = false;
          }
      } else {
          if (!port->bond_hash) {
              bond_choose_active_iface(port);
          }
          port->bond_compat_is_stale = true;
 +        port->bond_fake_iface = port->cfg->bond_fake_iface;
      }
  }
  
@@@ -3309,10 -3292,10 +3310,10 @@@ port_update_bond_compat(struct port *po
          netdev_get_etheraddr(iface->netdev, slave->mac);
      }
  
 -    if (cfg_get_bool(0, "bonding.%s.fake-iface", port->name)) {
 +    if (port->bond_fake_iface) {
          struct netdev *bond_netdev;
  
 -        if (!netdev_open(port->name, NETDEV_ETH_TYPE_NONE, &bond_netdev)) {
 +        if (!netdev_open_default(port->name, &bond_netdev)) {
              if (bond.up) {
                  netdev_turn_flags_on(bond_netdev, NETDEV_UP, true);
              } else {
@@@ -3365,14 -3348,12 +3366,14 @@@ port_update_vlan_compat(struct port *po
  \f
  /* Interface functions. */
  
 -static void
 -iface_create(struct port *port, const char *name)
 +static struct iface *
 +iface_create(struct port *port, const struct ovsrec_interface *if_cfg)
  {
      struct iface *iface;
 +    char *name = if_cfg->name;
 +    int error;
  
 -    iface = xcalloc(1, sizeof *iface);
 +    iface = xzalloc(sizeof *iface);
      iface->port = port;
      iface->port_ifidx = port->n_ifaces;
      iface->name = xstrdup(name);
      iface->tag = tag_create_random();
      iface->delay_expires = LLONG_MAX;
      iface->netdev = NULL;
 +    iface->cfg = if_cfg;
  
      if (port->n_ifaces >= port->allocated_ifaces) {
          port->ifaces = x2nrealloc(port->ifaces, &port->allocated_ifaces,
          port->bridge->has_bonded_ports = true;
      }
  
 +    /* Attempt to create the network interface in case it
 +     * doesn't exist yet. */
 +    if (!iface_is_internal(port->bridge, iface->name)) {
 +        error = set_up_iface(if_cfg, iface, true);
 +        if (error) {
 +            VLOG_WARN("could not create iface %s: %s", iface->name,
 +                    strerror(error));
 +        }
 +    }
 +
      VLOG_DBG("attached network device %s to port %s", iface->name, port->name);
  
      bridge_flush(port->bridge);
 +
 +    return iface;
  }
  
  static void
@@@ -3425,6 -3393,8 +3426,6 @@@ iface_destroy(struct iface *iface
          del->port_ifidx = iface->port_ifidx;
  
          netdev_close(iface->netdev);
 -        free(iface->name);
 -        free(iface);
  
          if (del_active) {
              ofproto_revalidate(port->bridge->ofproto, port->active_iface_tag);
              bond_send_learning_packets(port);
          }
  
 +        free(iface->name);
 +        free(iface);
 +
          bridge_flush(port->bridge);
      }
  }
@@@ -3473,25 -3440,20 +3474,25 @@@ iface_from_dp_ifidx(const struct bridg
   * reason why this function takes a name instead of a struct iface: the fake
   * interfaces created this way do not have a struct iface. */
  static bool
 -iface_is_internal(const struct bridge *br, const char *iface)
 +iface_is_internal(const struct bridge *br, const char *if_name)
  {
 -    if (!strcmp(iface, br->name)
 -        || cfg_get_bool(0, "iface.%s.internal", iface)) {
 +    /* XXX wastes time */
 +    struct iface *iface;
 +    struct port *port;
 +
 +    if (!strcmp(if_name, br->name)) {
          return true;
      }
  
 -    if (cfg_get_bool(0, "bonding.%s.fake-iface", iface)) {
 -        struct port *port = port_lookup(br, iface);
 -        if (port && port->n_ifaces > 1) {
 -            return true;
 -        }
 +    iface = iface_lookup(br, if_name);
 +    if (iface && !strcmp(iface->cfg->type, "internal")) {
 +        return true;
      }
  
 +    port = port_lookup(br, if_name);
 +    if (port && port->n_ifaces > 1 && port->cfg->bond_fake_iface) {
 +        return true;
 +    }
      return false;
  }
  
  static void
  iface_set_mac(struct iface *iface)
  {
 -    uint64_t mac = cfg_get_mac(0, "iface.%s.mac", iface->name);
 -    if (mac) {
 -        static uint8_t ea[ETH_ADDR_LEN];
 +    uint8_t ea[ETH_ADDR_LEN];
  
 -        eth_addr_from_uint64(mac, ea);
 +    if (iface->cfg->mac && eth_addr_from_string(iface->cfg->mac, ea)) {
          if (eth_addr_is_multicast(ea)) {
              VLOG_ERR("interface %s: cannot set MAC to multicast address",
                       iface->name);
  static void
  mirror_reconfigure(struct bridge *br)
  {
 -    struct svec old_mirrors, new_mirrors;
 -    size_t i, n_rspan_vlans;
 +    struct shash old_mirrors, new_mirrors;
 +    struct shash_node *node;
      unsigned long *rspan_vlans;
 +    int i;
  
 -    /* Collect old and new mirrors. */
 -    svec_init(&old_mirrors);
 -    svec_init(&new_mirrors);
 -    cfg_get_subsections(&new_mirrors, "mirror.%s", br->name);
 +    /* Collect old mirrors. */
 +    shash_init(&old_mirrors);
      for (i = 0; i < MAX_MIRRORS; i++) {
          if (br->mirrors[i]) {
 -            svec_add(&old_mirrors, br->mirrors[i]->name);
 +            shash_add(&old_mirrors, br->mirrors[i]->name, br->mirrors[i]);
          }
      }
  
 -    /* Get rid of deleted mirrors and add new mirrors. */
 -    svec_sort(&old_mirrors);
 -    assert(svec_is_unique(&old_mirrors));
 -    svec_sort(&new_mirrors);
 -    assert(svec_is_unique(&new_mirrors));
 -    for (i = 0; i < MAX_MIRRORS; i++) {
 -        struct mirror *m = br->mirrors[i];
 -        if (m && !svec_contains(&new_mirrors, m->name)) {
 -            mirror_destroy(m);
 +    /* Collect new mirrors. */
 +    shash_init(&new_mirrors);
 +    for (i = 0; i < br->cfg->n_mirrors; i++) {
 +        struct ovsrec_mirror *cfg = br->cfg->mirrors[i];
 +        if (!shash_add_once(&new_mirrors, cfg->name, cfg)) {
 +            VLOG_WARN("bridge %s: %s specified twice as mirror",
 +                      br->name, cfg->name);
          }
      }
 -    for (i = 0; i < new_mirrors.n; i++) {
 -        const char *name = new_mirrors.names[i];
 -        if (!svec_contains(&old_mirrors, name)) {
 -            mirror_create(br, name);
 +
 +    /* Get rid of deleted mirrors and add new mirrors. */
 +    SHASH_FOR_EACH (node, &old_mirrors) {
 +        if (!shash_find(&new_mirrors, node->name)) {
 +            mirror_destroy(node->data);
          }
      }
 -    svec_destroy(&old_mirrors);
 -    svec_destroy(&new_mirrors);
 -
 -    /* Reconfigure all mirrors. */
 -    for (i = 0; i < MAX_MIRRORS; i++) {
 -        if (br->mirrors[i]) {
 -            mirror_reconfigure_one(br->mirrors[i]);
 +    SHASH_FOR_EACH (node, &new_mirrors) {
 +        struct mirror *mirror = shash_find_data(&old_mirrors, node->name);
 +        if (!mirror) {
 +            mirror = mirror_create(br, node->name);
 +            if (!mirror) {
 +                break;
 +            }
          }
 +        mirror_reconfigure_one(mirror, node->data);
      }
 +    shash_destroy(&old_mirrors);
 +    shash_destroy(&new_mirrors);
  
      /* Update port reserved status. */
      for (i = 0; i < br->n_ports; i++) {
          }
      }
  
 -    /* Update learning disabled vlans (for RSPAN). */
 +    /* Update flooded vlans (for RSPAN). */
      rspan_vlans = NULL;
 -    n_rspan_vlans = cfg_count("vlan.%s.disable-learning", br->name);
 -    if (n_rspan_vlans) {
 +    if (br->cfg->n_flood_vlans) {
          rspan_vlans = bitmap_allocate(4096);
  
 -        for (i = 0; i < n_rspan_vlans; i++) {
 -            int vlan = cfg_get_vlan(i, "vlan.%s.disable-learning", br->name);
 -            if (vlan >= 0) {
 +        for (i = 0; i < br->cfg->n_flood_vlans; i++) {
 +            int64_t vlan = br->cfg->flood_vlans[i];
 +            if (vlan >= 0 && vlan < 4096) {
                  bitmap_set1(rspan_vlans, vlan);
 -                VLOG_INFO("bridge %s: disabling learning on vlan %d\n",
 +                VLOG_INFO("bridge %s: disabling learning on vlan %"PRId64,
                            br->name, vlan);
              } else {
 -                VLOG_ERR("bridge %s: invalid value '%s' for learning disabled "
 -                         "VLAN", br->name,
 -                       cfg_get_string(i, "vlan.%s.disable-learning", br->name));
 +                VLOG_ERR("bridge %s: invalid value %"PRId64 "for flood VLAN",
 +                         br->name, vlan);
              }
          }
      }
 -    if (mac_learning_set_disabled_vlans(br->ml, rspan_vlans)) {
 +    if (mac_learning_set_flood_vlans(br->ml, rspan_vlans)) {
          bridge_flush(br);
      }
  }
  
 -static void
 +static struct mirror *
  mirror_create(struct bridge *br, const char *name)
  {
      struct mirror *m;
          if (i >= MAX_MIRRORS) {
              VLOG_WARN("bridge %s: maximum of %d port mirrors reached, "
                        "cannot create %s", br->name, MAX_MIRRORS, name);
 -            return;
 +            return NULL;
          }
          if (!br->mirrors[i]) {
              break;
      VLOG_INFO("created port mirror %s on bridge %s", name, br->name);
      bridge_flush(br);
  
 -    br->mirrors[i] = m = xcalloc(1, sizeof *m);
 +    br->mirrors[i] = m = xzalloc(sizeof *m);
      m->bridge = br;
      m->idx = i;
      m->name = xstrdup(name);
 -    svec_init(&m->src_ports);
 -    svec_init(&m->dst_ports);
 +    shash_init(&m->src_ports);
 +    shash_init(&m->dst_ports);
      m->vlans = NULL;
      m->n_vlans = 0;
      m->out_vlan = -1;
      m->out_port = NULL;
 +
 +    return m;
  }
  
  static void
@@@ -3645,8 -3608,8 +3646,8 @@@ mirror_destroy(struct mirror *m
              br->ports[i]->dst_mirrors &= ~(MIRROR_MASK_C(1) << m->idx);
          }
  
 -        svec_destroy(&m->src_ports);
 -        svec_destroy(&m->dst_ports);
 +        shash_destroy(&m->src_ports);
 +        shash_destroy(&m->dst_ports);
          free(m->vlans);
  
          m->bridge->mirrors[m->idx] = NULL;
  }
  
  static void
 -prune_ports(struct mirror *m, struct svec *ports)
 +mirror_collect_ports(struct mirror *m, struct ovsrec_port **ports, int n_ports,
 +                     struct shash *names)
  {
 -    struct svec tmp;
      size_t i;
  
 -    svec_sort_unique(ports);
 -
 -    svec_init(&tmp);
 -    for (i = 0; i < ports->n; i++) {
 -        const char *name = ports->names[i];
 +    for (i = 0; i < n_ports; i++) {
 +        const char *name = ports[i]->name;
          if (port_lookup(m->bridge, name)) {
 -            svec_add(&tmp, name);
 +            shash_add_once(names, name, NULL);
          } else {
 -            VLOG_WARN("mirror.%s.%s: cannot match on nonexistent port %s",
 -                      m->bridge->name, m->name, name);
 +            VLOG_WARN("bridge %s: mirror %s cannot match on nonexistent "
 +                      "port %s", m->bridge->name, m->name, name);
          }
      }
 -    svec_swap(ports, &tmp);
 -    svec_destroy(&tmp);
  }
  
  static size_t
 -prune_vlans(struct mirror *m, struct svec *vlan_strings, int **vlans)
 +mirror_collect_vlans(struct mirror *m, const struct ovsrec_mirror *cfg,
 +                     int **vlans)
  {
 -    size_t n_vlans, i;
 -
 -    /* This isn't perfect: it won't combine "0" and "00", and the textual sort
 -     * order won't give us numeric sort order.  But that's good enough for what
 -     * we need right now. */
 -    svec_sort_unique(vlan_strings);
 +    size_t n_vlans;
 +    size_t i;
  
 -    *vlans = xmalloc(sizeof *vlans * vlan_strings->n);
 +    *vlans = xmalloc(sizeof **vlans * cfg->n_select_vlan);
      n_vlans = 0;
 -    for (i = 0; i < vlan_strings->n; i++) {
 -        const char *name = vlan_strings->names[i];
 -        int vlan;
 -        if (!str_to_int(name, 10, &vlan) || vlan < 0 || vlan > 4095) {
 -            VLOG_WARN("mirror.%s.%s.select.vlan: ignoring invalid VLAN %s",
 -                      m->bridge->name, m->name, name);
 +    for (i = 0; i < cfg->n_select_vlan; i++) {
 +        int64_t vlan = cfg->select_vlan[i];
 +        if (vlan < 0 || vlan > 4095) {
 +            VLOG_WARN("bridge %s: mirror %s selects invalid VLAN %"PRId64,
 +                      m->bridge->name, m->name, vlan);
          } else {
              (*vlans)[n_vlans++] = vlan;
          }
@@@ -3721,10 -3693,13 +3722,10 @@@ port_trunks_any_mirrored_vlan(const str
  }
  
  static void
 -mirror_reconfigure_one(struct mirror *m)
 +mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg)
  {
 -    char *pfx = xasprintf("mirror.%s.%s", m->bridge->name, m->name);
 -    struct svec src_ports, dst_ports, ports;
 -    struct svec vlan_strings;
 +    struct shash src_ports, dst_ports;
      mirror_mask_t mirror_bit;
 -    const char *out_port_name;
      struct port *out_port;
      int out_vlan;
      size_t n_vlans;
      size_t i;
      bool mirror_all_ports;
      bool any_ports_specified;
 +    bool any_vlans_specified;
  
      /* Get output port. */
 -    out_port_name = cfg_get_key(0, "mirror.%s.%s.output.port",
 -                                m->bridge->name, m->name);
 -    if (out_port_name) {
 -        out_port = port_lookup(m->bridge, out_port_name);
 +    if (cfg->output_port) {
 +        out_port = port_lookup(m->bridge, cfg->output_port->name);
          if (!out_port) {
 -            VLOG_ERR("%s.output.port: bridge %s does not have a port "
 -                      "named %s", pfx, m->bridge->name, out_port_name);
 +            VLOG_ERR("bridge %s: mirror %s outputs to port not on bridge",
 +                     m->bridge->name, m->name);
              mirror_destroy(m);
 -            free(pfx);
              return;
          }
          out_vlan = -1;
  
 -        if (cfg_has("%s.output.vlan", pfx)) {
 -            VLOG_ERR("%s.output.port and %s.output.vlan both specified; "
 -                     "ignoring %s.output.vlan", pfx, pfx, pfx);
 +        if (cfg->output_vlan) {
 +            VLOG_ERR("bridge %s: mirror %s specifies both output port and "
 +                     "output vlan; ignoring output vlan",
 +                     m->bridge->name, m->name);
          }
 -    } else if (cfg_has("%s.output.vlan", pfx)) {
 +    } else if (cfg->output_vlan) {
          out_port = NULL;
 -        out_vlan = cfg_get_vlan(0, "%s.output.vlan", pfx);
 +        out_vlan = *cfg->output_vlan;
      } else {
 -        VLOG_ERR("%s: neither %s.output.port nor %s.output.vlan specified, "
 -                 "but exactly one is required; disabling port mirror %s",
 -                 pfx, pfx, pfx, pfx);
 +        VLOG_ERR("bridge %s: mirror %s does not specify output; ignoring",
 +                 m->bridge->name, m->name);
          mirror_destroy(m);
 -        free(pfx);
          return;
      }
  
      /* Get all the ports, and drop duplicates and ports that don't exist. */
 -    svec_init(&src_ports);
 -    svec_init(&dst_ports);
 -    svec_init(&ports);
 -    cfg_get_all_keys(&src_ports, "%s.select.src-port", pfx);
 -    cfg_get_all_keys(&dst_ports, "%s.select.dst-port", pfx);
 -    cfg_get_all_keys(&ports, "%s.select.port", pfx);
 -    any_ports_specified = src_ports.n || dst_ports.n || ports.n;
 -    svec_append(&src_ports, &ports);
 -    svec_append(&dst_ports, &ports);
 -    svec_destroy(&ports);
 -    prune_ports(m, &src_ports);
 -    prune_ports(m, &dst_ports);
 -    if (any_ports_specified && !src_ports.n && !dst_ports.n) {
 -        VLOG_ERR("%s: none of the specified ports exist; "
 -                 "disabling port mirror %s", pfx, pfx);
 +    shash_init(&src_ports);
 +    shash_init(&dst_ports);
 +    mirror_collect_ports(m, cfg->select_src_port, cfg->n_select_src_port,
 +                         &src_ports);
 +    mirror_collect_ports(m, cfg->select_dst_port, cfg->n_select_dst_port,
 +                         &dst_ports);
 +    any_ports_specified = cfg->n_select_dst_port || cfg->n_select_dst_port;
 +    if (any_ports_specified
 +        && shash_is_empty(&src_ports) && shash_is_empty(&dst_ports)) {
 +        VLOG_ERR("bridge %s: disabling mirror %s since none of the specified "
 +                 "selection ports exists", m->bridge->name, m->name);
          mirror_destroy(m);
          goto exit;
      }
  
      /* Get all the vlans, and drop duplicate and invalid vlans. */
 -    svec_init(&vlan_strings);
 -    cfg_get_all_keys(&vlan_strings, "%s.select.vlan", pfx);
 -    n_vlans = prune_vlans(m, &vlan_strings, &vlans);
 -    svec_destroy(&vlan_strings);
 +    n_vlans = mirror_collect_vlans(m, cfg, &vlans);
 +    any_vlans_specified = cfg->n_select_vlan > 0;
 +    if (any_vlans_specified && !n_vlans) {
 +        VLOG_ERR("bridge %s: disabling mirror %s since none of the specified "
 +                 "VLANs exists", m->bridge->name, m->name);
 +        mirror_destroy(m);
 +        goto exit;
 +    }
  
      /* Update mirror data. */
 -    if (!svec_equal(&m->src_ports, &src_ports)
 -        || !svec_equal(&m->dst_ports, &dst_ports)
 +    if (!shash_equal_keys(&m->src_ports, &src_ports)
 +        || !shash_equal_keys(&m->dst_ports, &dst_ports)
          || m->n_vlans != n_vlans
          || memcmp(m->vlans, vlans, sizeof *vlans * n_vlans)
          || m->out_port != out_port
          || m->out_vlan != out_vlan) {
          bridge_flush(m->bridge);
      }
 -    svec_swap(&m->src_ports, &src_ports);
 -    svec_swap(&m->dst_ports, &dst_ports);
 +    shash_swap(&m->src_ports, &src_ports);
 +    shash_swap(&m->dst_ports, &dst_ports);
      free(m->vlans);
      m->vlans = vlans;
      m->n_vlans = n_vlans;
      m->out_vlan = out_vlan;
  
      /* If no selection criteria have been given, mirror for all ports. */
 -    mirror_all_ports = (!m->src_ports.n) && (!m->dst_ports.n) && (!m->n_vlans);
 +    mirror_all_ports = !any_ports_specified && !any_vlans_specified;
  
      /* Update ports. */
      mirror_bit = MIRROR_MASK_C(1) << m->idx;
          struct port *port = m->bridge->ports[i];
  
          if (mirror_all_ports
 -            || svec_contains(&m->src_ports, port->name)
 +            || shash_find(&m->src_ports, port->name)
              || (m->n_vlans
                  && (!port->vlan
                      ? port_trunks_any_mirrored_vlan(m, port)
              port->src_mirrors &= ~mirror_bit;
          }
  
 -        if (mirror_all_ports || svec_contains(&m->dst_ports, port->name)) {
 +        if (mirror_all_ports || shash_find(&m->dst_ports, port->name)) {
              port->dst_mirrors |= mirror_bit;
          } else {
              port->dst_mirrors &= ~mirror_bit;
  
      /* Clean up. */
  exit:
 -    svec_destroy(&src_ports);
 -    svec_destroy(&dst_ports);
 -    free(pfx);
 -}
 -\f
 -/* Spanning tree protocol. */
 -
 -static void brstp_update_port_state(struct port *);
 -
 -static void
 -brstp_send_bpdu(struct ofpbuf *pkt, int port_no, void *br_)
 -{
 -    struct bridge *br = br_;
 -    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 -    struct iface *iface = iface_from_dp_ifidx(br, port_no);
 -    if (!iface) {
 -        VLOG_WARN_RL(&rl, "%s: cannot send BPDU on unknown port %d",
 -                     br->name, port_no);
 -    } else {
 -        struct eth_header *eth = pkt->l2;
 -
 -        netdev_get_etheraddr(iface->netdev, eth->eth_src);
 -        if (eth_addr_is_zero(eth->eth_src)) {
 -            VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d "
 -                         "with unknown MAC", br->name, port_no);
 -        } else {
 -            union ofp_action action;
 -            flow_t flow;
 -
 -            memset(&action, 0, sizeof action);
 -            action.type = htons(OFPAT_OUTPUT);
 -            action.output.len = htons(sizeof action);
 -            action.output.port = htons(port_no);
 -
 -            flow_extract(pkt, ODPP_NONE, &flow);
 -            ofproto_send_packet(br->ofproto, &flow, &action, 1, pkt);
 -        }
 -    }
 -    ofpbuf_delete(pkt);
 -}
 -
 -static void
 -brstp_reconfigure(struct bridge *br)
 -{
 -    size_t i;
 -
 -    if (!cfg_get_bool(0, "stp.%s.enabled", br->name)) {
 -        if (br->stp) {
 -            stp_destroy(br->stp);
 -            br->stp = NULL;
 -
 -            bridge_flush(br);
 -        }
 -    } else {
 -        uint64_t bridge_address, bridge_id;
 -        int bridge_priority;
 -
 -        bridge_address = cfg_get_mac(0, "stp.%s.address", br->name);
 -        if (!bridge_address) {
 -            if (br->stp) {
 -                bridge_address = (stp_get_bridge_id(br->stp)
 -                                  & ((UINT64_C(1) << 48) - 1));
 -            } else {
 -                uint8_t mac[ETH_ADDR_LEN];
 -                eth_addr_random(mac);
 -                bridge_address = eth_addr_to_uint64(mac);
 -            }
 -        }
 -
 -        if (cfg_is_valid(CFG_INT | CFG_REQUIRED, "stp.%s.priority",
 -                         br->name)) {
 -            bridge_priority = cfg_get_int(0, "stp.%s.priority", br->name);
 -        } else {
 -            bridge_priority = STP_DEFAULT_BRIDGE_PRIORITY;
 -        }
 -
 -        bridge_id = bridge_address | ((uint64_t) bridge_priority << 48);
 -        if (!br->stp) {
 -            br->stp = stp_create(br->name, bridge_id, brstp_send_bpdu, br);
 -            br->stp_last_tick = time_msec();
 -            bridge_flush(br);
 -        } else {
 -            if (bridge_id != stp_get_bridge_id(br->stp)) {
 -                stp_set_bridge_id(br->stp, bridge_id);
 -                bridge_flush(br);
 -            }
 -        }
 -
 -        for (i = 0; i < br->n_ports; i++) {
 -            struct port *p = br->ports[i];
 -            int dp_ifidx;
 -            struct stp_port *sp;
 -            int path_cost, priority;
 -            bool enable;
 -
 -            if (!p->n_ifaces) {
 -                continue;
 -            }
 -            dp_ifidx = p->ifaces[0]->dp_ifidx;
 -            if (dp_ifidx < 0 || dp_ifidx >= STP_MAX_PORTS) {
 -                continue;
 -            }
 -
 -            sp = stp_get_port(br->stp, dp_ifidx);
 -            enable = (!cfg_is_valid(CFG_BOOL | CFG_REQUIRED,
 -                                    "stp.%s.port.%s.enabled",
 -                                    br->name, p->name)
 -                      || cfg_get_bool(0, "stp.%s.port.%s.enabled",
 -                                      br->name, p->name));
 -            if (p->is_mirror_output_port) {
 -                enable = false;
 -            }
 -            if (enable != (stp_port_get_state(sp) != STP_DISABLED)) {
 -                bridge_flush(br); /* Might not be necessary. */
 -                if (enable) {
 -                    stp_port_enable(sp);
 -                } else {
 -                    stp_port_disable(sp);
 -                }
 -            }
 -
 -            path_cost = cfg_get_int(0, "stp.%s.port.%s.path-cost",
 -                                    br->name, p->name);
 -            stp_port_set_path_cost(sp, path_cost ? path_cost : 19 /* XXX */);
 -
 -            priority = (cfg_is_valid(CFG_INT | CFG_REQUIRED,
 -                                     "stp.%s.port.%s.priority",
 -                                     br->name, p->name)
 -                        ? cfg_get_int(0, "stp.%s.port.%s.priority",
 -                                      br->name, p->name)
 -                        : STP_DEFAULT_PORT_PRIORITY);
 -            stp_port_set_priority(sp, priority);
 -        }
 -
 -        brstp_adjust_timers(br);
 -    }
 -    for (i = 0; i < br->n_ports; i++) {
 -        brstp_update_port_state(br->ports[i]);
 -    }
 -}
 -
 -static void
 -brstp_update_port_state(struct port *p)
 -{
 -    struct bridge *br = p->bridge;
 -    enum stp_state state;
 -
 -    /* Figure out new state. */
 -    state = STP_DISABLED;
 -    if (br->stp && p->n_ifaces > 0) {
 -        int dp_ifidx = p->ifaces[0]->dp_ifidx;
 -        if (dp_ifidx >= 0 && dp_ifidx < STP_MAX_PORTS) {
 -            state = stp_port_get_state(stp_get_port(br->stp, dp_ifidx));
 -        }
 -    }
 -
 -    /* Update state. */
 -    if (p->stp_state != state) {
 -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
 -        VLOG_INFO_RL(&rl, "port %s: STP state changed from %s to %s",
 -                     p->name, stp_state_name(p->stp_state),
 -                     stp_state_name(state));
 -        if (p->stp_state == STP_DISABLED) {
 -            bridge_flush(br);
 -        } else {
 -            ofproto_revalidate(p->bridge->ofproto, p->stp_state_tag);
 -        }
 -        p->stp_state = state;
 -        p->stp_state_tag = (p->stp_state == STP_DISABLED ? 0
 -                            : tag_create_random());
 -    }
 -}
 -
 -static void
 -brstp_adjust_timers(struct bridge *br)
 -{
 -    int hello_time = cfg_get_int(0, "stp.%s.hello-time", br->name);
 -    int max_age = cfg_get_int(0, "stp.%s.max-age", br->name);
 -    int forward_delay = cfg_get_int(0, "stp.%s.forward-delay", br->name);
 -
 -    stp_set_hello_time(br->stp, hello_time ? hello_time : 2000);
 -    stp_set_max_age(br->stp, max_age ? max_age : 20000);
 -    stp_set_forward_delay(br->stp, forward_delay ? forward_delay : 15000);
 -}
 -
 -static void
 -brstp_run(struct bridge *br)
 -{
 -    if (br->stp) {
 -        long long int now = time_msec();
 -        long long int elapsed = now - br->stp_last_tick;
 -        struct stp_port *sp;
 -
 -        if (elapsed > 0) {
 -            stp_tick(br->stp, MIN(INT_MAX, elapsed));
 -            br->stp_last_tick = now;
 -        }
 -        while (stp_get_changed_port(br->stp, &sp)) {
 -            struct port *p = port_from_dp_ifidx(br, stp_port_no(sp));
 -            if (p) {
 -                brstp_update_port_state(p);
 -            }
 -        }
 -    }
 -}
 -
 -static void
 -brstp_wait(struct bridge *br)
 -{
 -    if (br->stp) {
 -        poll_timer_wait(1000);
 -    }
 +    shash_destroy(&src_ports);
 +    shash_destroy(&dst_ports);
  }
@@@ -1,5 -1,5 +1,5 @@@
  # Copyright (c) 2008,2009 Citrix Systems, Inc.
 -# Copyright (c) 2009 Nicira Networks.
 +# Copyright (c) 2009,2010 Nicira Networks.
  #
  # This program is free software; you can redistribute it and/or modify
  # it under the terms of the GNU Lesser General Public License as published
@@@ -118,20 -118,25 +118,20 @@@ A VLAN PIF cannot be a datapath PIF
          return [pif]
  
  def datapath_deconfigure_physical(netdev):
 -    # The use of [!0-9] keeps an interface of 'eth0' from matching
 -    # VLANs attached to eth0 (such as 'eth0.123'), which are distinct
 -    # interfaces.
 -    return ['--del-match=bridge.*.port=%s' % netdev,
 -            '--del-match=port.%s.[!0-9]*' % netdev,
 -            '--del-match=bonding.*.slave=%s' % netdev,
 -            '--del-match=iface.%s.[!0-9]*' % netdev]
 +    return ['--', '--if-exists', 'del-port', netdev]
  
  def datapath_configure_bond(pif,slaves):
 +    bridge = pif_bridge_name(pif)
      pifrec = db().get_pif_record(pif)
      interface = pif_netdev_name(pif)
  
 -    argv = ['--del-match=bonding.%s.[!0-9]*' % interface]
 -    argv += ["--add=bonding.%s.slave=%s" % (interface, pif_netdev_name(slave))
 -             for slave in slaves]
 -    argv += ['--add=bonding.%s.fake-iface=true' % interface]
 +    argv = ['--', '--fake-iface', 'add-bond', bridge, interface]
 +    for slave in slaves:
 +        argv += [pif_netdev_name(slave)]
  
 -    if pifrec['MAC'] != "":
 -        argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])]
 +    # XXX need ovs-vsctl support
 +    #if pifrec['MAC'] != "":
 +    #    argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])]
  
      # Bonding options.
      bond_options = {
      overrides = map(lambda (key,val): (key[5:], val), overrides)
      bond_options.update(overrides)
      for (name,val) in bond_options.items():
 -        argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)]
 +        # XXX need ovs-vsctl support for bond options
 +        #argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)]
 +        pass
      return argv
  
  def datapath_deconfigure_bond(netdev):
 -    # The use of [!0-9] keeps an interface of 'eth0' from matching
 -    # VLANs attached to eth0 (such as 'eth0.123'), which are distinct
 -    # interfaces.
 -    return ['--del-match=bonding.%s.[!0-9]*' % netdev,
 -            '--del-match=port.%s.[!0-9]*' % netdev]
 +    return ['--', '--if-exists', 'del-port', netdev]
  
  def datapath_deconfigure_ipdev(interface):
 -    # The use of [!0-9] keeps an interface of 'eth0' from matching
 -    # VLANs attached to eth0 (such as 'eth0.123'), which are distinct
 -    # interfaces.
 -    return ['--del-match=bridge.*.port=%s' % interface,
 -            '--del-match=port.%s.[!0-9]*' % interface,
 -            '--del-match=iface.%s.[!0-9]*' % interface,
 -            '--del-match=vlan.%s.trunks=*' % interface,
 -            '--del-match=vlan.%s.tag=*' % interface]
 +    return ['--', '--if-exists', 'del-port', interface]
  
  def datapath_modify_config(commands):
      #log("modifying configuration:")
      #for c in commands:
      #    log("  %s" % c)
 -
 -    rc = run_command(['/usr/bin/ovs-cfg-mod', '-vANY:console:emer',
 -                 '-F', '/etc/ovs-vswitchd.conf']
 -                + [c for c in commands if c[0] != '#'] + ['-c'])
 -    if not rc:
 +            
 +    rc = run_command(['/usr/bin/ovs-vsctl'] + ['--timeout=20']
 +                     + [c for c in commands if not c.startswith('#')])
 +    if not rc:       
          raise Error("Failed to modify vswitch configuration")
 -    run_command(['/sbin/service', 'vswitch', 'reload'])
      return True
  
  #
  # Toplevel Datapath Configuration.
  #
  
 -def configure_datapath(pif):
 +def configure_datapath(pif, parent=None, vlan=None):
      """Bring up the datapath configuration for PIF.
  
      Should be careful not to glitch existing users of the datapath, e.g. other VLANs etc.
      Should take care of tearing down other PIFs which encompass common physical devices.
  
      Returns a tuple containing
 -    - A list containing the necessary cfgmod command line arguments
 +    - A list containing the necessary vsctl command line arguments
      - A list of additional devices which should be brought up after
        the configuration is applied.
      """
  
 -    cfgmod_argv = []
 +    vsctl_argv = []
      extra_up_ports = []
  
      bridge = pif_bridge_name(pif)
          #ifdown(b)
          # XXX
          netdev_down(b)
 -        cfgmod_argv += ['# remove bridge %s' % b]
 -        cfgmod_argv += ['--del-match=bridge.%s.*' % b]
 +        vsctl_argv += ['# remove bridge %s' % b]
 +        vsctl_argv += ['--', '--if-exists', 'del-br', b]
  
      for n in extra_down_ports:
          dev = pif_netdev_name(n)
 -        cfgmod_argv += ['# deconfigure sibling physical device %s' % dev]
 -        cfgmod_argv += datapath_deconfigure_physical(dev)
 +        vsctl_argv += ['# deconfigure sibling physical device %s' % dev]
 +        vsctl_argv += datapath_deconfigure_physical(dev)
          netdev_down(dev)
  
      for n in extra_down_bonds:
          dev = pif_netdev_name(n)
 -        cfgmod_argv += ['# deconfigure bond device %s' % dev]
 -        cfgmod_argv += datapath_deconfigure_bond(dev)
 +        vsctl_argv += ['# deconfigure bond device %s' % dev]
 +        vsctl_argv += datapath_deconfigure_bond(dev)
          netdev_down(dev)
  
      for p in physical_devices:
          dev = pif_netdev_name(p)
 -        cfgmod_argv += ['# deconfigure physical port %s' % dev]
 -        cfgmod_argv += datapath_deconfigure_physical(dev)
 +        vsctl_argv += ['# deconfigure physical port %s' % dev]
 +        vsctl_argv += datapath_deconfigure_physical(dev)
 +
 +    if parent and datapath:
 +        vsctl_argv += ['--', '--may-exist', 'add-br', bridge, parent, vlan]
 +    else:
 +        vsctl_argv += ['--', '--may-exist', 'add-br', bridge]
 +
      if len(physical_devices) > 1:
 -        cfgmod_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
 -        cfgmod_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
 -        cfgmod_argv += ['--del-entry=bridge.%s.port=%s' % (bridge,pif_netdev_name(pif))]
 -        cfgmod_argv += ['# configure bond %s' % pif_netdev_name(pif)]
 -        cfgmod_argv += datapath_configure_bond(pif, physical_devices)
 -        cfgmod_argv += ['--add=bridge.%s.port=%s' % (bridge,pif_netdev_name(pif)) ]
 +        vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
 +        vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
 +        vsctl_argv += ['# configure bond %s' % pif_netdev_name(pif)]
 +        vsctl_argv += datapath_configure_bond(pif, physical_devices)
          extra_up_ports += [pif_netdev_name(pif)]
      else:
          iface = pif_netdev_name(physical_devices[0])
 -        cfgmod_argv += ['# add physical device %s' % iface]
 -        cfgmod_argv += ['--add=bridge.%s.port=%s' % (bridge,iface) ]
 +        vsctl_argv += ['# add physical device %s' % iface]
 +        vsctl_argv += ['--', '--may-exist', 'add-port', bridge, iface]
  
 -    return cfgmod_argv,extra_up_ports
 +    return vsctl_argv,extra_up_ports
  
  def deconfigure_datapath(pif):
 -    cfgmod_argv = []
 +    vsctl_argv = []
  
      bridge = pif_bridge_name(pif)
  
  
      for p in physical_devices:
          dev = pif_netdev_name(p)
 -        cfgmod_argv += ['# deconfigure physical port %s' % dev]
 -        cfgmod_argv += datapath_deconfigure_physical(dev)
 +        vsctl_argv += ['# deconfigure physical port %s' % dev]
 +        vsctl_argv += datapath_deconfigure_physical(dev)
          netdev_down(dev)
  
      if len(physical_devices) > 1:
 -        cfgmod_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
 -        cfgmod_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
 +        vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
 +        vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
  
 -    cfgmod_argv += ['# deconfigure bridge %s' % bridge]
 -    cfgmod_argv += ['--del-match=bridge.%s.*' % bridge]
 +    vsctl_argv += ['# deconfigure bridge %s' % bridge]
 +    vsctl_argv += ['--', '--if-exists', 'del-br', bridge]
  
 -    return cfgmod_argv
 +    return vsctl_argv
  
  #
  #
@@@ -326,22 -338,21 +326,23 @@@ class DatapathVswitch(Datapath)
          cfg.write("TYPE=Ethernet\n")
  
      def preconfigure(self, parent):
 -        cfgmod_argv = []
 +        vsctl_argv = []
          extra_ports = []
  
          pifrec = db().get_pif_record(self._pif)
+         dprec = db().get_pif_record(self._dp)
  
          ipdev = self._ipdev
          bridge = pif_bridge_name(self._dp)
 -        c,e = configure_datapath(self._dp)
 -        cfgmod_argv += c
 +        if pif_is_vlan(self._pif):
 +            datapath = pif_datapath(self._pif)
 +            c,e = configure_datapath(self._dp, datapath, pifrec['VLAN'])
 +        else:
 +            c,e = configure_datapath(self._dp)
 +        vsctl_argv += c
          extra_ports += e
  
 -        cfgmod_argv += ['# configure xs-network-uuids']
 -        cfgmod_argv += ['--del-match=bridge.%s.xs-network-uuids=*' % bridge]
 -
 +        xs_network_uuids = []
          for nwpif in db().get_pifs_by_device(db().get_pif_record(self._pif)['device']):
              rec = db().get_pif_record(nwpif)
  
              #    log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid']))
              #    continue
              nwrec = db().get_network_record(rec['network'])
 -            cfgmod_argv += ['--add=bridge.%s.xs-network-uuids=%s' % (bridge, nwrec['uuid'])]
 -
 -        cfgmod_argv += ["# deconfigure ipdev %s" % ipdev]
 -        cfgmod_argv += datapath_deconfigure_ipdev(ipdev)
 -        cfgmod_argv += ["# reconfigure ipdev %s" % ipdev]
 -        cfgmod_argv += ['--add=bridge.%s.port=%s' % (bridge, ipdev)]
 -        if bridge == ipdev:
 -            cfgmod_argv += ['--add=bridge.%s.mac=%s' % (bridge, dprec['MAC'])]
 -        else:
 -            cfgmod_argv += ['--add=iface.%s.mac=%s' % (ipdev, dprec['MAC'])]
 -            
 -        if pif_is_vlan(self._pif):
 -            cfgmod_argv += ['--add=vlan.%s.tag=%s' % (ipdev, pifrec['VLAN'])]
 -            cfgmod_argv += ['--add=iface.%s.internal=true' % (ipdev)]
 -            cfgmod_argv += ['--add=iface.%s.fake-bridge=true' % (ipdev)]
 +            xs_network_uuids += [nwrec['uuid']]
 +
 +        vsctl_argv += ['# configure xs-network-uuids']
 +        vsctl_argv += ['--', 'br-set-external-id', bridge,
 +                'xs-network-uuids', ';'.join(xs_network_uuids)]
 +
 +        if ipdev != bridge:
 +            vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
 +            vsctl_argv += datapath_deconfigure_ipdev(ipdev)
 +            vsctl_argv += ["# reconfigure ipdev %s" % ipdev]
 +            vsctl_argv += ['--', 'add-port', bridge, ipdev]
 +
 +        # XXX Needs support in ovs-vsctl
 +        #if bridge == ipdev:
-         #    vsctl_argv += ['--add=bridge.%s.mac=%s' % (bridge, pifrec['MAC'])]
++        #    vsctl_argv += ['--add=bridge.%s.mac=%s' % (bridge, dprec['MAC'])]
 +        #else:
-         #    vsctl_argv += ['--add=iface.%s.mac=%s' % (ipdev, pifrec['MAC'])]
++        #    vsctl_argv += ['--add=iface.%s.mac=%s' % (ipdev, dprec['MAC'])]
  
 -        self._cfgmod_argv = cfgmod_argv
 +        self._vsctl_argv = vsctl_argv
          self._extra_ports = extra_ports
  
      def bring_down_existing(self):
              if len(offload):
                  run_command(['/sbin/ethtool', '-K', dev] + offload)
  
 -        datapath_modify_config(self._cfgmod_argv)
 +        datapath_modify_config(self._vsctl_argv)
  
      def post(self):
          for p in self._extra_ports:
              netdev_up(p)
  
      def bring_down(self):
 -        cfgmod_argv = []
 +        vsctl_argv = []
  
          dp = self._dp
          ipdev = self._ipdev
  
          #nw = db().get_pif_record(self._pif)['network']
          #nwrec = db().get_network_record(nw)
 -        #cfgmod_argv += ['# deconfigure xs-network-uuids']
 -        #cfgmod_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])]
 +        #vsctl_argv += ['# deconfigure xs-network-uuids']
 +        #vsctl_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])]
  
          log("deconfigure ipdev %s on %s" % (ipdev,bridge))
 -        cfgmod_argv += ["# deconfigure ipdev %s" % ipdev]
 -        cfgmod_argv += datapath_deconfigure_ipdev(ipdev)
 +        vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
 +        vsctl_argv += datapath_deconfigure_ipdev(ipdev)
  
          if pif_is_vlan(self._pif):
              # If the VLAN's slave is attached, leave datapath setup.
                  dp = None
  
          if dp:
 -            cfgmod_argv += deconfigure_datapath(dp)
 -            datapath_modify_config(cfgmod_argv)
 +            vsctl_argv += deconfigure_datapath(dp)
 +            datapath_modify_config(vsctl_argv)