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

@@@ -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);
Simple merge
Simple merge
@@@ -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 --cc lib/compiler.h
Simple merge
diff --cc lib/coverage.c
@@@ -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);
@@@ -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;
Simple merge
diff --cc lib/dhcp.c
Simple merge
@@@ -107,7 -107,7 +107,7 @@@ dpif_linux_enumerate(struct svec *all_d
  }
  
  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;
@@@ -245,7 -245,7 +245,7 @@@ create_dp_netdev(const char *name, int 
  }
  
  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) {
@@@ -239,13 -232,12 +239,13 @@@ 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)
  {
Simple merge
@@@ -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;
@@@ -669,158 -279,64 +669,158 @@@ if_up(const char *name
  }
  
  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;
diff --cc lib/netdev.c
@@@ -59,11 -56,16 +59,11 @@@ static struct list netdev_list = LIST_I
   * 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;
@@@ -1262,13 -1051,13 +1262,13 @@@ restore_flags(struct netdev *netdev
      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 --cc lib/netdev.h
Simple merge
diff --cc lib/packets.h
  
  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])
diff --cc lib/process.c
Simple merge
diff --cc lib/rconn.c
Simple merge
diff --cc lib/shash.h
@@@ -50,9 -50,10 +54,13 @@@ bool shash_add_once(struct shash *, con
  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 */
Simple merge
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;
 +    }
 +}
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
 +};
 +
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 --cc 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 --cc 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.
@@@ -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 --cc lib/unixctl.c
@@@ -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;
diff --cc 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.
@@@ -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 --cc 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.
@@@ -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
@@@ -50,50 -52,32 +50,50 @@@ static struct vconn_class stream_vconn_
  
  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;
  }
  
@@@ -297,31 -292,17 +297,31 @@@ pvconn_pstream_cast(struct pvconn *pvco
   *
   * 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;
  }
diff --cc lib/vconn.c
Simple merge
diff --cc lib/vlog.c
@@@ -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();
Simple merge
Simple merge
Simple merge
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;
 +}
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
 +};
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 },
 +};
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:
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);
 +}
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 --cc 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);
 +}
Simple merge
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 },
 +};
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 },
 +};
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 },
 +};
 +
@@@ -134,10 -135,8 +134,10 @@@ 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;
  
   * 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;
  
   * 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;
@@@ -310,9 -277,8 +310,9 @@@ test_send_hello(const char *type, cons
  
  /* 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();
Simple merge
@@@ -176,30 -212,11 +176,30 @@@ static int if_up(const char *netdev_nam
      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);
  }
@@@ -418,31 -435,20 +418,31 @@@ do_show(int argc, char *argv[]
  }
  
  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);
 +            }
          }
      }
  
@@@ -339,7 -372,7 +339,7 @@@ 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);
@@@ -378,13 -411,15 +378,13 @@@ do_status(int argc, char *argv[]
  }
  
  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);
  }
@@@ -812,7 -847,8 +812,7 @@@ do_dump_aggregate(int argc, char *argv[
  }
  
  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;
  }
  
  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;
@@@ -954,7 -991,8 +954,7 @@@ static void do_del_flows(int argc, cha
  }
  
  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;
@@@ -1129,7 -1170,8 +1129,7 @@@ do_ping(int argc, char *argv[]
  }
  
  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();
  }
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},
 +};
 +
@@@ -446,25 -418,42 +446,25 @@@ set_up_iface(const struct ovsrec_interf
  }
  
  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
@@@ -483,13 -473,15 +484,13 @@@ check_iface_dp_ifidx(struct bridge *br
  }
  
  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. */
@@@ -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;
@@@ -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;
@@@ -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;
@@@ -352,25 -363,23 +353,25 @@@ class DatapathVswitch(Datapath)
              #    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):