Files under the xenserver directory are licensed on a file-by-file
basis. Some files are under an uncertain license that may not be
DFSG-compliant or GPL-compatible. Refer to each file for details.
+
+The files under ovsdb/simplejson are covered by the following license:
+
+ Copyright (c) 2006 Bob Ippolito
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use, copy,
+ modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
AM_LDFLAGS = -export-dynamic
endif
+BUILT_SOURCES =
CLEANFILES =
DISTCLEANFILES =
EXTRA_DIST = INSTALL.bridge \
INSTALL.userspace \
INSTALL.OpenFlow \
INSTALL.SSL \
- INSTALL.XenServer
+ INSTALL.XenServer \
+ README-gcov
bin_PROGRAMS =
sbin_PROGRAMS =
bin_SCRIPTS =
dist_pkgdata_SCRIPTS =
dist_sbin_SCRIPTS =
man_MANS =
+noinst_DATA =
noinst_HEADERS =
noinst_LIBRARIES =
noinst_PROGRAMS =
noinst_SCRIPTS =
+SUFFIXES =
EXTRA_DIST += soexpand.pl
ro_c = echo '/* -*- mode: c; buffer-read-only: t -*- */'
-SUFFIXES = .in
+SUFFIXES += .in
.in:
$(PERL) $(srcdir)/soexpand.pl -I$(srcdir) < $< | \
sed \
-e 's,[@]localstatedir[@],$(localstatedir),g' \
-e 's,[@]pkgdatadir[@],$(pkgdatadir),g' \
-e 's,[@]sysconfdir[@],$(sysconfdir),g' \
+ -e 's,[@]abs_top_srcdir[@],$(abs_top_srcdir),g' \
> $@.tmp
@if head -n 1 $@.tmp | grep -q '#!'; then \
echo chmod +x $@.tmp; \
include third-party/automake.mk
include debian/automake.mk
include vswitchd/automake.mk
+include ovsdb/automake.mk
include xenserver/automake.mk
include extras/ezio/automake.mk
/* Get rid of deleted bridges and add new bridges. */
svec_sort(&old_br);
@@ -793,7 +780,7 @@ bridge_create(const char *name)
- br = xcalloc(1, sizeof *br);
+ br = xzalloc(sizeof *br);
error = dpif_create(name, &br->dpif);
- if (error == EEXIST) {
AC_C_BIGENDIAN
AC_SYS_LARGEFILE
+AC_SEARCH_LIBS([pow], [m])
+
OVS_CHECK_COVERAGE
OVS_CHECK_NDEBUG
OVS_CHECK_NETLINK
bin_PROGRAMS += extras/ezio/ezio-term
extras_ezio_ezio_term_SOURCES = \
- extras/ezio/byteq.c \
- extras/ezio/byteq.h \
extras/ezio/ezio-term.c \
extras/ezio/ezio.c \
extras/ezio/ezio.h \
#include <stdlib.h>
#include <term.h>
#include <unistd.h>
+#include "byteq.h"
#include "command-line.h"
-#include "extras/ezio/byteq.h"
#include "extras/ezio/tty.h"
#include "extras/ezio/vt.h"
#include "daemon.h"
{
if (!*msgp) {
/* Allocate and initialize message. */
- *msgp = xcalloc(1, sizeof **msgp);
+ *msgp = xzalloc(sizeof **msgp);
(*msgp)->index = n_messages;
/* Add to list of messages. */
--- /dev/null
+/*
+ * Copyright (c) 2009 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.
+ */
+
+/*
+ * Based on rijndael.txt by Philip J. Erdelsky, downloaded from
+ * http://www.efgh.com/software/rijndael.htm on September 24, 2009. The
+ * license information there is: "Public domain; no restrictions on use."
+ * The Apache license above applies only to Nicira's modifications to the
+ * original code.
+ */
+
+#include <config.h>
+
+#include "aes128.h"
+
+#include <assert.h>
+
+#include "util.h"
+
+static const uint32_t Te0[256] = {
+ 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+ 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+ 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+ 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+ 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+ 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+ 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+ 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+ 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+ 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+ 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+ 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+ 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+ 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+ 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+ 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+ 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+ 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+ 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+ 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+ 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+ 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+ 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+ 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+ 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+ 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+ 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+ 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+ 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+ 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+ 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+ 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+ 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+ 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+ 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+ 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+ 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+ 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+ 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+ 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+ 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+ 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+ 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+ 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+ 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+ 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+ 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+ 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+ 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+ 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+ 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+ 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+ 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+ 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+ 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+ 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+ 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+ 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+ 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+ 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+ 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+ 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+ 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+ 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+
+static const uint32_t Te1[256] = {
+ 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
+ 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
+ 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
+ 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
+ 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+ 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
+ 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
+ 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
+ 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
+ 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+ 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
+ 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
+ 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
+ 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
+ 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+ 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
+ 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
+ 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
+ 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
+ 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+ 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
+ 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
+ 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
+ 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
+ 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+ 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
+ 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
+ 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
+ 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
+ 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+ 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,
+ 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,
+ 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,
+ 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,
+ 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+ 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,
+ 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,
+ 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,
+ 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,
+ 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+ 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,
+ 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,
+ 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,
+ 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,
+ 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+ 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,
+ 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,
+ 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,
+ 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,
+ 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+ 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,
+ 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,
+ 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,
+ 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,
+ 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+ 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,
+ 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,
+ 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,
+ 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,
+ 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+ 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,
+ 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,
+ 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,
+ 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,
+};
+
+static const uint32_t Te2[256] = {
+ 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
+ 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
+ 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
+ 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
+ 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+ 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
+ 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
+ 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
+ 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
+ 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+ 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
+ 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
+ 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
+ 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
+ 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+ 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
+ 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
+ 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
+ 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
+ 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+ 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
+ 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
+ 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
+ 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
+ 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+ 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
+ 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
+ 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
+ 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
+ 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+ 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
+ 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
+ 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
+ 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
+ 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+ 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
+ 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
+ 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
+ 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
+ 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+ 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
+ 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
+ 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
+ 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
+ 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+ 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
+ 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
+ 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
+ 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
+ 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+ 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
+ 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
+ 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
+ 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
+ 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+ 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
+ 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
+ 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
+ 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
+ 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+ 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
+ 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
+ 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
+ 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
+};
+
+static const uint32_t Te3[256] = {
+ 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,
+ 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,
+ 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,
+ 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,
+ 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+ 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,
+ 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,
+ 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,
+ 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,
+ 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+ 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,
+ 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,
+ 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,
+ 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,
+ 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+ 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,
+ 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,
+ 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,
+ 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,
+ 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+ 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,
+ 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,
+ 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,
+ 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,
+ 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+ 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,
+ 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,
+ 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,
+ 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,
+ 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+ 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,
+ 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,
+ 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,
+ 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,
+ 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+ 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,
+ 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,
+ 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,
+ 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,
+ 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+ 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,
+ 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,
+ 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,
+ 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,
+ 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+ 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,
+ 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,
+ 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,
+ 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,
+ 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+ 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,
+ 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,
+ 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,
+ 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,
+ 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+ 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,
+ 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,
+ 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,
+ 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,
+ 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+ 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,
+ 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,
+ 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,
+ 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,
+};
+
+static const uint32_t Te4[256] = {
+ 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,
+ 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,
+ 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,
+ 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,
+ 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,
+ 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,
+ 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,
+ 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,
+ 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,
+ 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,
+ 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,
+ 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,
+ 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,
+ 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,
+ 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,
+ 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,
+ 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,
+ 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,
+ 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,
+ 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,
+ 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,
+ 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,
+ 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,
+ 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,
+ 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,
+ 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,
+ 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,
+ 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,
+ 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,
+ 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,
+ 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,
+ 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,
+ 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,
+ 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,
+ 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,
+ 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,
+ 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,
+ 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,
+ 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,
+ 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,
+ 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,
+ 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,
+ 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,
+ 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,
+ 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,
+ 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,
+ 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,
+ 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,
+ 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,
+ 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,
+ 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,
+ 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,
+ 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,
+ 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,
+ 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,
+ 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,
+ 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,
+ 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,
+ 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,
+ 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,
+ 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,
+ 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,
+ 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,
+ 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,
+};
+
+static const uint32_t Td0[256] = {
+ 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+ 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+ 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+ 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+ 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+ 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+ 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+ 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+ 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+ 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+ 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+ 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+ 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+ 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+ 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+ 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+ 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+ 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+ 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+ 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+ 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+ 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+ 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+ 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+ 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+ 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+ 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+ 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+ 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+ 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+ 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+ 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+ 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+ 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+ 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+ 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+ 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+ 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+ 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+ 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+ 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+ 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+ 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+ 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+ 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+ 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+ 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+ 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+ 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+ 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+ 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+ 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+ 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+ 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+ 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+ 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+ 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+ 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+ 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+ 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+ 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+ 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+ 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+ 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+
+static const uint32_t Td1[256] = {
+ 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,
+ 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,
+ 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,
+ 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,
+ 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+ 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,
+ 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,
+ 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,
+ 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,
+ 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+ 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,
+ 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,
+ 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,
+ 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,
+ 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+ 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,
+ 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,
+ 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,
+ 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,
+ 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+ 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,
+ 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,
+ 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,
+ 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,
+ 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+ 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,
+ 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,
+ 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,
+ 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,
+ 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+ 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,
+ 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,
+ 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,
+ 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,
+ 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+ 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,
+ 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,
+ 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,
+ 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,
+ 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+ 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,
+ 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,
+ 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,
+ 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,
+ 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+ 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,
+ 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,
+ 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,
+ 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,
+ 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+ 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,
+ 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,
+ 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,
+ 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,
+ 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+ 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,
+ 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,
+ 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,
+ 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,
+ 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+ 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,
+ 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,
+ 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,
+ 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,
+};
+
+static const uint32_t Td2[256] = {
+ 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,
+ 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,
+ 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,
+ 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,
+ 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+ 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,
+ 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,
+ 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,
+ 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,
+ 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+ 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,
+ 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,
+ 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,
+ 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,
+ 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+ 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,
+ 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,
+ 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,
+ 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,
+ 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+ 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,
+ 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,
+ 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,
+ 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,
+ 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+ 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,
+ 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,
+ 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,
+ 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,
+ 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+ 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,
+ 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,
+ 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,
+ 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,
+ 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+ 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,
+ 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,
+ 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,
+ 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,
+ 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+ 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,
+ 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,
+ 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,
+ 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,
+ 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+ 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,
+ 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,
+ 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,
+ 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,
+ 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+ 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,
+ 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,
+ 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,
+ 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,
+ 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+ 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,
+ 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,
+ 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,
+ 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,
+ 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+ 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,
+ 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,
+ 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,
+ 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,
+};
+
+static const uint32_t Td3[256] = {
+ 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,
+ 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,
+ 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,
+ 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,
+ 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+ 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,
+ 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,
+ 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,
+ 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,
+ 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+ 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,
+ 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,
+ 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,
+ 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,
+ 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+ 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,
+ 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,
+ 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,
+ 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,
+ 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+ 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,
+ 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,
+ 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,
+ 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,
+ 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+ 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,
+ 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,
+ 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,
+ 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,
+ 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+ 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,
+ 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,
+ 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,
+ 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,
+ 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+ 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,
+ 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,
+ 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,
+ 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,
+ 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+ 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,
+ 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,
+ 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,
+ 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,
+ 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+ 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,
+ 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,
+ 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,
+ 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,
+ 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+ 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,
+ 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,
+ 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,
+ 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,
+ 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+ 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,
+ 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,
+ 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,
+ 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,
+ 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+ 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,
+ 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,
+ 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,
+ 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,
+};
+
+static const uint32_t Td4[256] = {
+ 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U,
+ 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U,
+ 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU,
+ 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU,
+ 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U,
+ 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U,
+ 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U,
+ 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU,
+ 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U,
+ 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU,
+ 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU,
+ 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU,
+ 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U,
+ 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U,
+ 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U,
+ 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U,
+ 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U,
+ 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U,
+ 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU,
+ 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U,
+ 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U,
+ 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU,
+ 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U,
+ 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U,
+ 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U,
+ 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU,
+ 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U,
+ 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U,
+ 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU,
+ 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U,
+ 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U,
+ 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU,
+ 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U,
+ 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU,
+ 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU,
+ 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U,
+ 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U,
+ 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U,
+ 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U,
+ 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU,
+ 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U,
+ 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U,
+ 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU,
+ 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU,
+ 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU,
+ 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U,
+ 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU,
+ 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U,
+ 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U,
+ 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U,
+ 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U,
+ 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU,
+ 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U,
+ 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU,
+ 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU,
+ 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU,
+ 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU,
+ 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U,
+ 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU,
+ 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U,
+ 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU,
+ 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U,
+ 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U,
+ 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU,
+};
+
+static const uint32_t rcon[] = {
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000,
+ 0x1B000000, 0x36000000,
+};
+
+static uint32_t
+get_u32(const uint8_t *p)
+{
+ uint32_t p0 = p[0];
+ uint32_t p1 = p[1];
+ uint32_t p2 = p[2];
+ uint32_t p3 = p[3];
+ return (p0 << 24) | (p1 << 16) | (p2 << 8) | p3;
+}
+
+static void
+put_u32(uint8_t *p, uint32_t x)
+{
+ p[0] = x >> 24;
+ p[1] = x >> 16;
+ p[2] = x >> 8;
+ p[3] = x;
+}
+
+/* Expands 128-bit 'key' into the encryption key 'schedule'. */
+void
+aes128_schedule(struct aes128 *aes, const uint8_t key[16])
+{
+ uint32_t *rk = aes->rk;
+ int i;
+
+ rk[0] = get_u32(key);
+ rk[1] = get_u32(key + 4);
+ rk[2] = get_u32(key + 8);
+ rk[3] = get_u32(key + 12);
+ for (i = 0; i < 10; i++, rk += 4) {
+ uint32_t temp = rk[3];
+ rk[4] = (rk[0]
+ ^ (Te4[(temp >> 16) & 0xff] & 0xff000000)
+ ^ (Te4[(temp >> 8) & 0xff] & 0x00ff0000)
+ ^ (Te4[(temp ) & 0xff] & 0x0000ff00)
+ ^ (Te4[(temp >> 24) ] & 0x000000ff)
+ ^ rcon[i]);
+ rk[5] = rk[1] ^ rk[4];
+ rk[6] = rk[2] ^ rk[5];
+ rk[7] = rk[3] ^ rk[6];
+ }
+ assert(rk == &aes->rk[40]);
+}
+
+void
+aes128_encrypt(const struct aes128 *aes, const void *input_, void *output_)
+{
+ const uint8_t *input = input_;
+ uint8_t *output = output_;
+ const uint32_t *rk = aes->rk;
+ uint32_t s0, s1, s2, s3;
+ uint32_t t0, t1, t2, t3;
+ int r;
+
+ /* Map byte array block to cipher state and add initial round key. */
+ s0 = get_u32(input ) ^ rk[0];
+ s1 = get_u32(input + 4) ^ rk[1];
+ s2 = get_u32(input + 8) ^ rk[2];
+ s3 = get_u32(input + 12) ^ rk[3];
+
+ /* 10 full rounds. */
+ r = 10 / 2;
+ for (;;) {
+ t0 = (Te0[(s0 >> 24) ]
+ ^ Te1[(s1 >> 16) & 0xff]
+ ^ Te2[(s2 >> 8) & 0xff]
+ ^ Te3[(s3 ) & 0xff]
+ ^ rk[4]);
+ t1 = (Te0[(s1 >> 24) ]
+ ^ Te1[(s2 >> 16) & 0xff]
+ ^ Te2[(s3 >> 8) & 0xff]
+ ^ Te3[(s0 ) & 0xff]
+ ^ rk[5]);
+ t2 = (Te0[(s2 >> 24) ]
+ ^ Te1[(s3 >> 16) & 0xff]
+ ^ Te2[(s0 >> 8) & 0xff]
+ ^ Te3[(s1 ) & 0xff]
+ ^ rk[6]);
+ t3 = (Te0[(s3 >> 24) ]
+ ^ Te1[(s0 >> 16) & 0xff]
+ ^ Te2[(s1 >> 8) & 0xff]
+ ^ Te3[(s2 ) & 0xff]
+ ^ rk[7]);
+
+ rk += 8;
+ if (--r == 0) {
+ break;
+ }
+
+ s0 = (Te0[(t0 >> 24) ]
+ ^ Te1[(t1 >> 16) & 0xff]
+ ^ Te2[(t2 >> 8) & 0xff]
+ ^ Te3[(t3 ) & 0xff]
+ ^ rk[0]);
+ s1 = (Te0[(t1 >> 24) ]
+ ^ Te1[(t2 >> 16) & 0xff]
+ ^ Te2[(t3 >> 8) & 0xff]
+ ^ Te3[(t0 ) & 0xff]
+ ^ rk[1]);
+ s2 = (Te0[(t2 >> 24) ]
+ ^ Te1[(t3 >> 16) & 0xff]
+ ^ Te2[(t0 >> 8) & 0xff]
+ ^ Te3[(t1 ) & 0xff]
+ ^ rk[2]);
+ s3 = (Te0[(t3 >> 24) ]
+ ^ Te1[(t0 >> 16) & 0xff]
+ ^ Te2[(t1 >> 8) & 0xff]
+ ^ Te3[(t2 ) & 0xff]
+ ^ rk[3]);
+ }
+
+ /* Apply last round and map cipher state to byte array block. */
+ s0 = ((Te4[(t0 >> 24) ] & 0xff000000)
+ ^ (Te4[(t1 >> 16) & 0xff] & 0x00ff0000)
+ ^ (Te4[(t2 >> 8) & 0xff] & 0x0000ff00)
+ ^ (Te4[(t3 ) & 0xff] & 0x000000ff)
+ ^ rk[0]);
+ put_u32(output , s0);
+ s1 = ((Te4[(t1 >> 24) ] & 0xff000000)
+ ^ (Te4[(t2 >> 16) & 0xff] & 0x00ff0000)
+ ^ (Te4[(t3 >> 8) & 0xff] & 0x0000ff00)
+ ^ (Te4[(t0 ) & 0xff] & 0x000000ff)
+ ^ rk[1]);
+ put_u32(output + 4, s1);
+ s2 = ((Te4[(t2 >> 24) ] & 0xff000000)
+ ^ (Te4[(t3 >> 16) & 0xff] & 0x00ff0000)
+ ^ (Te4[(t0 >> 8) & 0xff] & 0x0000ff00)
+ ^ (Te4[(t1 ) & 0xff] & 0x000000ff)
+ ^ rk[2]);
+ put_u32(output + 8, s2);
+ s3 = ((Te4[(t3 >> 24) ] & 0xff000000)
+ ^ (Te4[(t0 >> 16) & 0xff] & 0x00ff0000)
+ ^ (Te4[(t1 >> 8) & 0xff] & 0x0000ff00)
+ ^ (Te4[(t2 ) & 0xff] & 0x000000ff)
+ ^ rk[3]);
+ put_u32(output + 12, s3);
+}
--- /dev/null
+/*
+ * Copyright (c) 2009 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.
+ */
+
+/*
+ * Based on rijndael.txt by Philip J. Erdelsky, downloaded from
+ * http://www.efgh.com/software/rijndael.htm on September 24, 2009. The
+ * license information there is: "Public domain; no restrictions on use."
+ * The Apache license above applies only to Nicira's modifications to the
+ * original code.
+ */
+
+#ifndef AES128_H
+#define AES128_H
+
+#include <stdint.h>
+
+struct aes128 {
+ uint32_t rk[128/8 + 28];
+};
+
+void aes128_schedule(struct aes128 *, const uint8_t key[16]);
+void aes128_encrypt(const struct aes128 *, const void *, void *);
+
+#endif /* aes128.h */
noinst_LIBRARIES += lib/libopenvswitch.a
lib_libopenvswitch_a_SOURCES = \
+ lib/aes128.c \
+ lib/aes128.h \
lib/backtrace.c \
lib/backtrace.h \
lib/bitmap.c \
lib/bitmap.h \
+ lib/byteq.c \
+ lib/byteq.h \
lib/cfg.c \
lib/cfg.h \
lib/classifier.c \
lib/hash.h \
lib/hmap.c \
lib/hmap.h \
+ lib/json.c \
+ lib/json.h \
+ lib/jsonrpc.c \
+ lib/jsonrpc.h \
lib/leak-checker.c \
lib/leak-checker.h \
lib/learning-switch.c \
lib/learning-switch.h \
lib/list.c \
lib/list.h \
+ lib/lockfile.c \
+ lib/lockfile.h \
lib/mac-learning.c \
lib/mac-learning.h \
lib/netdev-linux.c \
lib/ofp-print.h \
lib/ofpbuf.c \
lib/ofpbuf.h \
+ lib/ovsdb-data.c \
+ lib/ovsdb-data.h \
+ lib/ovsdb-error.c \
+ lib/ovsdb-error.h \
+ lib/ovsdb-idl-provider.h \
+ lib/ovsdb-idl.c \
+ lib/ovsdb-idl.h \
+ lib/ovsdb-parser.c \
+ lib/ovsdb-parser.h \
+ lib/ovsdb-types.c \
+ lib/ovsdb-types.h \
lib/packets.c \
lib/packets.h \
lib/pcap.c \
lib/random.h \
lib/rconn.c \
lib/rconn.h \
+ lib/reconnect.c \
+ lib/reconnect.h \
lib/rtnetlink.c \
lib/rtnetlink.h \
lib/sat-math.h \
lib/signals.h \
lib/socket-util.c \
lib/socket-util.h \
+ lib/sort.c \
+ lib/sort.h \
lib/stp.c \
lib/stp.h \
+ lib/stream-fd.c \
+ lib/stream-fd.h \
+ lib/stream-provider.h \
+ lib/stream-tcp.c \
+ lib/stream-unix.c \
+ lib/stream.c \
+ lib/stream.h \
lib/svec.c \
lib/svec.h \
lib/tag.c \
lib/timeval.c \
lib/timeval.h \
lib/type-props.h \
+ lib/uuid.c \
+ lib/uuid.h \
lib/unixctl.c \
lib/unixctl.h \
+ lib/unicode.c \
+ lib/unicode.h \
lib/util.c \
lib/util.h \
lib/valgrind.h \
EXTRA_DIST += \
lib/common.man \
+ lib/common-syn.man \
lib/daemon.man \
+ lib/daemon-syn.man \
lib/dpif.man \
lib/leak-checker.man \
lib/vlog-unixctl.man \
+ lib/vlog-syn.man \
lib/vlog.man
# All the source files that have coverage counters.
COVERAGE_FILES = \
- lib/cfg.c \
lib/dpif.c \
lib/flow.c \
+ lib/lockfile.c \
lib/hmap.c \
lib/mac-learning.c \
lib/netdev.c \
lib/process.c \
lib/rconn.c \
lib/rtnetlink.c \
+ lib/stream.c \
lib/timeval.c \
lib/unixctl.c \
lib/util.c \
*/
#include <config.h>
-#include "extras/ezio/byteq.h"
+#include "byteq.h"
#include <assert.h>
#include <errno.h>
#include <string.h>
/* The queue size must be a power of 2. */
BUILD_ASSERT_DECL(!(BYTEQ_SIZE & (BYTEQ_SIZE - 1)));
-static uint8_t *head(struct byteq *);
-static int headroom(const struct byteq *);
-static void advance_head(struct byteq *, unsigned int n);
-static int tailroom(const struct byteq *);
-static const uint8_t *tail(const struct byteq *);
-static void advance_tail(struct byteq *, unsigned int n);
-
/* Initializes 'q' as empty. */
void
byteq_init(struct byteq *q)
byteq_put(struct byteq *q, uint8_t c)
{
assert(!byteq_is_full(q));
- *head(q) = c;
+ *byteq_head(q) = c;
q->head++;
}
const uint8_t *p = p_;
assert(byteq_avail(q) >= n);
while (n > 0) {
- size_t chunk = MIN(n, headroom(q));
- memcpy(head(q), p, chunk);
- advance_head(q, chunk);
+ size_t chunk = MIN(n, byteq_headroom(q));
+ memcpy(byteq_head(q), p, chunk);
+ byteq_advance_head(q, chunk);
p += chunk;
n -= chunk;
}
{
uint8_t c;
assert(!byteq_is_empty(q));
- c = *tail(q);
+ c = *byteq_tail(q);
q->tail++;
return c;
}
byteq_write(struct byteq *q, int fd)
{
while (!byteq_is_empty(q)) {
- ssize_t n = write(fd, tail(q), tailroom(q));
+ ssize_t n = write(fd, byteq_tail(q), byteq_tailroom(q));
if (n > 0) {
- advance_tail(q, n);
+ byteq_advance_tail(q, n);
} else {
assert(n < 0);
return errno;
byteq_read(struct byteq *q, int fd)
{
while (!byteq_is_full(q)) {
- ssize_t n = read(fd, head(q), headroom(q));
+ ssize_t n = read(fd, byteq_head(q), byteq_headroom(q));
if (n > 0) {
- advance_head(q, n);
+ byteq_advance_head(q, n);
} else {
return !n ? EOF : errno;
}
}
return 0;
}
-\f
+
/* Returns the number of contiguous bytes of in-use space starting at the tail
* of 'q'. */
-static int
-tailroom(const struct byteq *q)
+int
+byteq_tailroom(const struct byteq *q)
{
int used = byteq_used(q);
int tail_to_end = BYTEQ_SIZE - (q->tail & (BYTEQ_SIZE - 1));
/* Returns the first in-use byte of 'q', the point at which data is removed
* from 'q'. */
-static const uint8_t *
-tail(const struct byteq *q)
+const uint8_t *
+byteq_tail(const struct byteq *q)
{
return &q->buffer[q->tail & (BYTEQ_SIZE - 1)];
}
/* Removes 'n' bytes from the tail of 'q', which must have at least 'n' bytes
* of tailroom. */
-static void
-advance_tail(struct byteq *q, unsigned int n)
+void
+byteq_advance_tail(struct byteq *q, unsigned int n)
{
- assert(tailroom(q) >= n);
+ assert(byteq_tailroom(q) >= n);
q->tail += n;
}
/* Returns the byte after the last in-use byte of 'q', the point at which new
* data will be added to 'q'. */
-static uint8_t *
-head(struct byteq *q)
+uint8_t *
+byteq_head(struct byteq *q)
{
return &q->buffer[q->head & (BYTEQ_SIZE - 1)];
}
/* Returns the number of contiguous bytes of free space starting at the head
* of 'q'. */
-static int
-headroom(const struct byteq *q)
+int
+byteq_headroom(const struct byteq *q)
{
int avail = byteq_avail(q);
int head_to_end = BYTEQ_SIZE - (q->head & (BYTEQ_SIZE - 1));
/* Adds to 'q' the 'n' bytes after the last currently in-use byte of 'q'. 'q'
* must have at least 'n' bytes of headroom. */
-static void
-advance_head(struct byteq *q, unsigned int n)
+void
+byteq_advance_head(struct byteq *q, unsigned int n)
{
- assert(headroom(q) >= n);
+ assert(byteq_headroom(q) >= n);
q->head += n;
}
int byteq_write(struct byteq *, int fd);
int byteq_read(struct byteq *, int fd);
+uint8_t *byteq_head(struct byteq *);
+int byteq_headroom(const struct byteq *);
+void byteq_advance_head(struct byteq *, unsigned int n);
+int byteq_tailroom(const struct byteq *);
+const uint8_t *byteq_tail(const struct byteq *);
+void byteq_advance_tail(struct byteq *, unsigned int n);
+
#endif /* byteq.h */
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
#include "coverage.h"
#include "dynamic-string.h"
+#include "lockfile.h"
#include "ofpbuf.h"
#include "packets.h"
#include "svec.h"
static char *tmp_name;
/* Lock information. */
-static char *lock_name;
-static int lock_fd = -1;
+static struct lockfile *lockfile;
/* Flag to indicate whether local modifications have been made. */
static bool dirty;
int
cfg_set_file(const char *file_name)
{
- const char *slash;
+ char *lock_name;
int fd;
if (cfg_name) {
- assert(lock_fd < 0);
+ assert(!lockfile);
free(cfg_name);
- free(lock_name);
free(tmp_name);
- cfg_name = lock_name = tmp_name = NULL;
+ cfg_name = tmp_name = NULL;
}
/* Make sure that we can open this file for reading. */
* rename(tmp_name, cfg_name) will work. */
tmp_name = xasprintf("%s.~tmp~", file_name);
- /* Put the lock file in the same directory as cfg_name, but prefixed by
- * a dot so as not to garner administrator interest. */
- slash = strrchr(file_name, '/');
- if (slash) {
- lock_name = xasprintf("%.*s/.%s.~lock~",
- (int) (slash - file_name), file_name, slash + 1);
- } else {
- lock_name = xasprintf(".%s.~lock~", file_name);
- }
-
+ lock_name = lockfile_name(file_name);
VLOG_INFO("using \"%s\" as configuration file, \"%s\" as lock file",
file_name, lock_name);
+ free(lock_name);
+
return 0;
}
void
cfg_unlock(void)
{
- if (lock_fd != -1) {
- COVERAGE_INC(cfg_unlock);
- close(lock_fd);
- lock_fd = -1;
+ if (lockfile) {
+ lockfile_unlock(lockfile);
+ lockfile = NULL;
}
}
-static int
-open_lockfile(const char *name)
-{
- for (;;) {
- /* Try to open an existing lock file. */
- int fd = open(name, O_RDWR);
- if (fd >= 0) {
- return fd;
- } else if (errno != ENOENT) {
- VLOG_WARN("%s: failed to open lock file: %s",
- name, strerror(errno));
- return -errno;
- }
-
- /* Try to create a new lock file. */
- VLOG_INFO("%s: lock file does not exist, creating", name);
- fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
- if (fd >= 0) {
- return fd;
- } else if (errno != EEXIST) {
- VLOG_WARN("%s: failed to create lock file: %s",
- name, strerror(errno));
- return -errno;
- }
-
- /* Someone else created the lock file. Try again. */
- }
-}
-
-static int
-try_lock(int fd, bool block)
-{
- struct flock l;
- memset(&l, 0, sizeof l);
- l.l_type = F_WRLCK;
- l.l_whence = SEEK_SET;
- l.l_start = 0;
- l.l_len = 0;
- return fcntl(fd, block ? F_SETLKW : F_SETLK, &l) == -1 ? errno : 0;
-}
-
/* Locks the configuration file against modification by other processes and
* re-reads it from disk.
*
int
cfg_lock(uint8_t *cookie, int timeout)
{
- long long int start;
- long long int elapsed = 0;
- int fd;
- uint8_t curr_cookie[CFG_COOKIE_LEN];
-
- assert(lock_fd < 0);
- COVERAGE_INC(cfg_lock);
-
- time_refresh();
- start = time_msec();
- for (;;) {
- int error;
-
- /* Open lock file. */
- fd = open_lockfile(lock_name);
- if (fd < 0) {
- return -fd;
- }
-
- /* Try to lock it. This will block (if 'timeout' > 0). */
- error = try_lock(fd, timeout > 0);
- time_refresh();
- elapsed = time_msec() - start;
- if (!error) {
- /* Success! */
- break;
- }
-
- /* Lock failed. Close the lock file and reopen it on the next
- * iteration, just in case someone deletes it underneath us (even
- * though that should not happen). */
- close(fd);
- if (error != EINTR) {
- /* Hard error, give up. */
- COVERAGE_INC(cfg_lock_error);
- VLOG_WARN("%s: failed to lock file "
- "(after %lld ms, with %d-ms timeout): %s",
- lock_name, elapsed, timeout, strerror(error));
- return error;
- }
+ int error;
- /* Probably, the periodic timer set up by time_init() woke up us. Just
- * check whether it's time to give up. */
- if (timeout != INT_MAX && elapsed >= timeout) {
- COVERAGE_INC(cfg_lock_timeout);
- VLOG_WARN("%s: giving up on lock file after %lld ms",
- lock_name, elapsed);
- return ETIMEDOUT;
- }
- COVERAGE_INC(cfg_lock_retry);
- }
- if (elapsed) {
- VLOG_WARN("%s: waited %lld ms for lock file", lock_name, elapsed);
+ assert(!lockfile);
+ error = lockfile_lock(cfg_name, timeout, &lockfile);
+ if (error) {
+ return error;
}
- lock_fd = fd;
cfg_read();
if (cookie) {
+ uint8_t curr_cookie[CFG_COOKIE_LEN];
cfg_get_cookie(curr_cookie);
if (memcmp(curr_cookie, cookie, sizeof *curr_cookie)) {
/*
- * Copyright (c) 2008 Nicira Networks.
+ * Copyright (c) 2008, 2009 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "command-line.h"
#include <getopt.h>
#include <limits.h>
+#include <stdlib.h>
#include "util.h"
#include "vlog.h"
return xstrdup(short_options);
}
+/* 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]);
+}
/*
- * Copyright (c) 2008 Nicira Networks.
+ * Copyright (c) 2008, 2009 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/* Utilities for command-line parsing. */
+#include "compiler.h"
+
struct option;
+
+struct command {
+ const char *name;
+ int min_args;
+ int max_args;
+ void (*handler)(int argc, char *argv[]);
+};
+
char *long_options_to_short_options(const struct option *options);
+void run_command(int argc, char *argv[], const struct command[]);
#endif /* command-line.h */
--- /dev/null
+.IP "Common options:"
+[\fB-h\fR | \fB--help\fR]
+[\fB-V\fR | \fB--version\fR]
+
#define ALWAYS_INLINE __attribute__((always_inline))
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
+#define WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))
#endif /* compiler.h */
static unsigned int epoch;
static void
-coverage_unixctl_log(struct unixctl_conn *conn, const char *args UNUSED)
+coverage_unixctl_log(struct unixctl_conn *conn, const char *args UNUSED,
+ void *aux UNUSED)
{
coverage_log(VLL_WARN, false);
unixctl_command_reply(conn, 200, NULL);
void
coverage_init(void)
{
- unixctl_command_register("coverage/log", coverage_unixctl_log);
+ unixctl_command_register("coverage/log", coverage_unixctl_log, NULL);
}
/* Sorts coverage counters in descending order by count, within equal counts
--- /dev/null
+.IP "Daemon options:"
+[\fB--pidfile\fR[\fB=\fIpidfile\fR]]
+[\fB--overwrite-pidfile\fR]
+[\fB--detach\fR]
+[\fB--no-chdir\fR]
#include <unistd.h>
#include "fatal-signal.h"
#include "dirs.h"
+#include "lockfile.h"
#include "timeval.h"
#include "util.h"
chdir_ = false;
}
+/* Will we chdir to "/" as part of daemonizing? */
+bool
+is_chdir_enabled(void)
+{
+ return chdir_;
+}
+
/* Normally, die_if_already_running() will terminate the program with a message
* if a locked pidfile already exists. If this function is called,
* die_if_already_running() will merely log a warning. */
detach = true;
}
+/* Will daemonize() really detach? */
+bool
+get_detach(void)
+{
+ return detach;
+}
+
/* If a pidfile has been configured and that pidfile already exists and is
* locked by a running process, returns the pid of the running process.
* Otherwise, returns 0. */
chdir("/");
}
time_postfork();
+ lockfile_postfork();
break;
case -1:
/*
- * Copyright (c) 2008 Nicira Networks.
+ * Copyright (c) 2008, 2009 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
void set_pidfile(const char *name);
const char *get_pidfile(void);
void set_no_chdir(void);
+bool is_chdir_enabled(void);
void set_detach(void);
+bool get_detach(void);
void daemonize(void);
void die_if_already_running(void);
void ignore_existing_pidfile(void);
return error;
}
- cli = xcalloc(1, sizeof *cli);
+ cli = xzalloc(sizeof *cli);
cli->modify_request = modify_request;
cli->validate_offer = validate_offer;
cli->aux = aux;
}
/* Create datapath. */
- dp_netdevs[dp_idx] = dp = xcalloc(1, sizeof *dp);
+ dp_netdevs[dp_idx] = dp = xzalloc(sizeof *dp);
list_push_back(&dp_netdev_list, &dp->node);
dp->dp_idx = dp_idx;
dp->open_cnt = 0;
struct dp_netdev_flow *flow;
int error;
- flow = xcalloc(1, sizeof *flow);
+ flow = xzalloc(sizeof *flow);
flow->key = odp_flow->key;
flow->key.reserved = 0;
*ds_put_uninit(ds, 1) = c;
}
+/* Appends unicode code point 'uc' to 'ds' in UTF-8 encoding. */
+void
+ds_put_utf8(struct ds *ds, int uc)
+{
+ if (uc <= 0x7f) {
+ ds_put_char(ds, uc);
+ } else if (uc <= 0x7ff) {
+ ds_put_char(ds, 0xc0 | (uc >> 6));
+ ds_put_char(ds, 0x80 | (uc & 0x3f));
+ } else if (uc <= 0xffff) {
+ ds_put_char(ds, 0xe0 | (uc >> 12));
+ ds_put_char(ds, 0x80 | ((uc >> 6) & 0x3f));
+ ds_put_char(ds, 0x80 | (uc & 0x3f));
+ } else if (uc <= 0x10ffff) {
+ ds_put_char(ds, 0xf0 | (uc >> 18));
+ ds_put_char(ds, 0x80 | ((uc >> 12) & 0x3f));
+ ds_put_char(ds, 0x80 | ((uc >> 6) & 0x3f));
+ ds_put_char(ds, 0x80 | (uc & 0x3f));
+ } else {
+ /* Invalid code point. Insert the Unicode general substitute
+ * REPLACEMENT CHARACTER. */
+ ds_put_utf8(ds, 0xfffd);
+ }
+}
+
void
ds_put_char_multiple(struct ds *ds, char c, size_t n)
{
memcpy(ds_put_uninit(ds, s_len), s, s_len);
}
+void
+ds_put_and_free_cstr(struct ds *ds, char *s)
+{
+ ds_put_cstr(ds, s);
+ free(s);
+}
+
void
ds_put_format(struct ds *ds, const char *format, ...)
{
void ds_reserve(struct ds *, size_t min_length);
char *ds_put_uninit(struct ds *, size_t n);
void ds_put_char(struct ds *, char);
+void ds_put_utf8(struct ds *, int uc);
void ds_put_char_multiple(struct ds *, char, size_t n);
void ds_put_buffer(struct ds *, const char *, size_t n);
void ds_put_cstr(struct ds *, const char *);
+void ds_put_and_free_cstr(struct ds *, char *);
void ds_put_format(struct ds *, const char *, ...) PRINTF_FORMAT(2, 3);
void ds_put_format_valist(struct ds *, const char *, va_list)
PRINTF_FORMAT(2, 0);
return c;
}
+/* Returns the hash of the pair of aligned 32-bit words at 'p', starting from
+ * 'basis'. */
+uint32_t
+hash_2words(const uint32_t *p, uint32_t basis)
+{
+ uint32_t a, b, c;
+
+ a = b = c = 0xdeadbeef + (2 << 2) + basis;
+ b += p[1];
+ a += p[0];
+ HASH_FINAL(a, b, c);
+
+ return c;
+}
+
/* Returns the hash of the 'n' bytes at 'p', starting from 'basis'. */
uint32_t
hash_bytes(const void *p_, size_t n, uint32_t basis)
#ifndef HASH_H
#define HASH_H 1
+#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
+#include "util.h"
/* This is the public domain lookup3 hash by Bob Jenkins from
* http://burtleburtle.net/bob/c/lookup3.c, modified for style. */
} while (0)
uint32_t hash_words(const uint32_t *, size_t n_word, uint32_t basis);
+uint32_t hash_2words(const uint32_t *, uint32_t basis);
uint32_t hash_bytes(const void *, size_t n_bytes, uint32_t basis);
static inline uint32_t hash_string(const char *s, uint32_t basis)
x ^= x >> 17;
x -= x << 9;
x ^= x << 4;
+ x += basis;
x -= x << 3;
x ^= x << 10;
x ^= x >> 15;
- return x + basis;
+ return x;
+}
+
+/* An attempt at a useful 1-bit hash function. Has not been analyzed for
+ * quality. */
+static inline uint32_t hash_boolean(bool x, uint32_t basis)
+{
+ enum { P0 = 0xc2b73583 }; /* This is hash_int(1, 0). */
+ enum { P1 = 0xe90f1258 }; /* This is hash_int(2, 0). */
+ return (x ? P0 : P1) ^ HASH_ROT(basis, 1);
+}
+
+static inline uint32_t hash_double(double x, uint32_t basis)
+{
+ BUILD_ASSERT_DECL(sizeof x == 8);
+ return hash_2words((const uint32_t *) &x, basis);
+}
+
+static inline uint32_t hash_pointer(const void *p, uint32_t basis)
+{
+ /* Often pointers are hashed simply by casting to integer type, but that
+ * has pitfalls since the lower bits of a pointer are often all 0 for
+ * alignment reasons. It's hard to guess where the entropy really is, so
+ * we give up here and just use a high-quality hash function.
+ *
+ * The double cast suppresses a warning on 64-bit systems about casting to
+ * an integer to different size. That's OK in this case, since most of the
+ * entropy in the pointer is almost certainly in the lower 32 bits. */
+ return hash_int((uint32_t) (uintptr_t) p, basis);
}
#endif /* hash.h */
static inline void hmap_replace(struct hmap *, const struct hmap_node *old,
struct hmap_node *new);
-/* Search. */
+/* Search.
+ *
+ * HMAP_FOR_EACH_WITH_HASH iterates NODE over all of the nodes in HMAP that
+ * have hash value equal to HASH. HMAP_FOR_EACH_IN_BUCKET iterates NODE over
+ * all of the nodes in HMAP that would fall in the same bucket as HASH. STRUCT
+ * and MEMBER must be the name of the struct that contains the 'struct
+ * hmap_node' and the name of the 'struct hmap_node' member, respectively.
+ *
+ * These macros may be used interchangeably to search for a particular value in
+ * an hmap, see, e.g. shash_find() for an example. Usually, using
+ * HMAP_FOR_EACH_WITH_HASH provides an optimization, because comparing a hash
+ * value is usually cheaper than comparing an entire hash map key. But for
+ * simple hash map keys, it makes sense to use HMAP_FOR_EACH_IN_BUCKET because
+ * it avoids doing two comparisons when a single simple comparison suffices.
+ *
+ * The loop should not change NODE to point to a different node or insert or
+ * delete nodes in HMAP (unless it "break"s out of the loop to terminate
+ * iteration).
+ *
+ * HASH is only evaluated once.
+ */
#define HMAP_FOR_EACH_WITH_HASH(NODE, STRUCT, MEMBER, HASH, HMAP) \
for ((NODE) = CONTAINER_OF(hmap_first_with_hash(HMAP, HASH), \
STRUCT, MEMBER); \
&(NODE)->MEMBER != NULL; \
(NODE) = CONTAINER_OF(hmap_next_with_hash(&(NODE)->MEMBER), \
STRUCT, MEMBER))
+#define HMAP_FOR_EACH_IN_BUCKET(NODE, STRUCT, MEMBER, HASH, HMAP) \
+ for ((NODE) = CONTAINER_OF(hmap_first_in_bucket(HMAP, HASH), \
+ STRUCT, MEMBER); \
+ &(NODE)->MEMBER != NULL; \
+ (NODE) = CONTAINER_OF(hmap_next_in_bucket(&(NODE)->MEMBER), \
+ STRUCT, MEMBER))
static inline struct hmap_node *hmap_first_with_hash(const struct hmap *,
size_t hash);
static inline struct hmap_node *hmap_next_with_hash(const struct hmap_node *);
+static inline struct hmap_node *hmap_first_in_bucket(const struct hmap *,
+ size_t hash);
+static inline struct hmap_node *hmap_next_in_bucket(const struct hmap_node *);
/* Iteration.
*
}
*bucket = new;
new->hash = old->hash;
+ new->next = old->next;
}
static inline struct hmap_node *
return hmap_next_with_hash__(hmap->buckets[hash & hmap->mask], hash);
}
+/* Returns the first node in 'hmap' in the bucket in which the given 'hash'
+ * would land, or a null pointer if that bucket is empty. */
+static inline struct hmap_node *
+hmap_first_in_bucket(const struct hmap *hmap, size_t hash)
+{
+ return hmap->buckets[hash & hmap->mask];
+}
+
+/* Returns the next node in the same bucket as 'node', or a null pointer if
+ * there are no more nodes in that bucket.
+ *
+ * If the hash map has been reallocated since 'node' was visited, some nodes
+ * may be skipped; if new nodes with the same hash value have been added, they
+ * will be skipped. (Removing 'node' from the hash map does not prevent
+ * calling this function, since node->next is preserved, although freeing
+ * 'node' of course does.) */
+static inline struct hmap_node *
+hmap_next_in_bucket(const struct hmap_node *node)
+{
+ return node->next;
+}
+
/* Returns the next node in the same hash map as 'node' with the same hash
* value, or a null pointer if no more nodes have that hash value.
*
--- /dev/null
+/*
+ * Copyright (c) 2009 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 "json.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <string.h>
+
+#include "dynamic-string.h"
+#include "hash.h"
+#include "shash.h"
+#include "unicode.h"
+#include "util.h"
+
+/* The type of a JSON token. */
+enum json_token_type {
+ T_EOF = 0,
+ T_BEGIN_ARRAY = '[',
+ T_END_ARRAY = ']',
+ T_BEGIN_OBJECT = '{',
+ T_END_OBJECT = '}',
+ T_NAME_SEPARATOR = ':',
+ T_VALUE_SEPARATOR = ',',
+ T_FALSE = UCHAR_MAX + 1,
+ T_NULL,
+ T_TRUE,
+ T_INTEGER,
+ T_REAL,
+ T_STRING
+};
+
+/* A JSON token.
+ *
+ * RFC 4627 doesn't define a lexical structure for JSON but I believe this to
+ * be compliant with the standard.
+ */
+struct json_token {
+ enum json_token_type type;
+ union {
+ double real;
+ long long int integer;
+ const char *string;
+ } u;
+};
+
+enum json_lex_state {
+ JSON_LEX_START, /* Not inside a token. */
+ JSON_LEX_NUMBER, /* Reading a number. */
+ JSON_LEX_KEYWORD, /* Reading a keyword. */
+ JSON_LEX_STRING, /* Reading a quoted string. */
+ JSON_LEX_ESCAPE /* In a quoted string just after a "\". */
+};
+
+enum json_parse_state {
+ JSON_PARSE_START, /* Beginning of input. */
+ JSON_PARSE_END, /* End of input. */
+
+ /* Objects. */
+ JSON_PARSE_OBJECT_INIT, /* Expecting '}' or an object name. */
+ JSON_PARSE_OBJECT_NAME, /* Expecting an object name. */
+ JSON_PARSE_OBJECT_COLON, /* Expecting ':'. */
+ JSON_PARSE_OBJECT_VALUE, /* Expecting an object value. */
+ JSON_PARSE_OBJECT_NEXT, /* Expecting ',' or '}'. */
+
+ /* Arrays. */
+ JSON_PARSE_ARRAY_INIT, /* Expecting ']' or a value. */
+ JSON_PARSE_ARRAY_VALUE, /* Expecting a value. */
+ JSON_PARSE_ARRAY_NEXT /* Expecting ',' or ']'. */
+};
+
+struct json_parser_node {
+ struct json *json;
+};
+
+/* A JSON parser. */
+struct json_parser {
+ int flags;
+
+ /* Lexical analysis. */
+ enum json_lex_state lex_state;
+ struct ds buffer; /* Buffer for accumulating token text. */
+
+ /* Parsing. */
+ enum json_parse_state parse_state;
+#define JSON_MAX_HEIGHT 1000
+ struct json_parser_node *stack;
+ size_t height, allocated_height;
+ char *member_name;
+
+ /* Parse status. */
+ bool done;
+ char *error; /* Error message, if any, null if none yet. */
+};
+
+static struct json *json_create(enum json_type type);
+static void json_parser_input(struct json_parser *, struct json_token *);
+
+static void json_error(struct json_parser *p, const char *format, ...)
+ PRINTF_FORMAT(2, 3);
+\f
+const char *
+json_type_to_string(enum json_type type)
+{
+ switch (type) {
+ case JSON_NULL:
+ return "null";
+
+ case JSON_FALSE:
+ return "false";
+
+ case JSON_TRUE:
+ return "true";
+
+ case JSON_OBJECT:
+ return "object";
+
+ case JSON_ARRAY:
+ return "array";
+
+ case JSON_INTEGER:
+ case JSON_REAL:
+ return "number";
+
+ case JSON_STRING:
+ return "string";
+
+ case JSON_N_TYPES:
+ default:
+ return "<invalid>";
+ }
+}
+\f
+/* Functions for manipulating struct json. */
+
+struct json *
+json_null_create(void)
+{
+ return json_create(JSON_NULL);
+}
+
+struct json *
+json_boolean_create(bool b)
+{
+ return json_create(b ? JSON_TRUE : JSON_FALSE);
+}
+
+struct json *
+json_string_create_nocopy(char *s)
+{
+ struct json *json = json_create(JSON_STRING);
+ json->u.string = s;
+ return json;
+}
+
+struct json *
+json_string_create(const char *s)
+{
+ return json_string_create_nocopy(xstrdup(s));
+}
+
+struct json *
+json_array_create_empty(void)
+{
+ struct json *json = json_create(JSON_ARRAY);
+ json->u.array.elems = NULL;
+ json->u.array.n = 0;
+ json->u.array.n_allocated = 0;
+ return json;
+}
+
+void
+json_array_add(struct json *array_, struct json *element)
+{
+ struct json_array *array = json_array(array_);
+ if (array->n >= array->n_allocated) {
+ array->elems = x2nrealloc(array->elems, &array->n_allocated,
+ sizeof *array->elems);
+ }
+ array->elems[array->n++] = element;
+}
+
+void
+json_array_trim(struct json *array_)
+{
+ struct json_array *array = json_array(array_);
+ if (array->n < array->n_allocated){
+ array->n_allocated = array->n;
+ array->elems = xrealloc(array->elems, array->n * sizeof *array->elems);
+ }
+}
+
+struct json *
+json_array_create(struct json **elements, size_t n)
+{
+ struct json *json = json_create(JSON_ARRAY);
+ json->u.array.elems = elements;
+ json->u.array.n = n;
+ json->u.array.n_allocated = n;
+ return json;
+}
+
+struct json *
+json_array_create_2(struct json *elem0, struct json *elem1)
+{
+ struct json **elems = xmalloc(2 * sizeof *elems);
+ elems[0] = elem0;
+ elems[1] = elem1;
+ return json_array_create(elems, 2);
+}
+
+struct json *
+json_array_create_3(struct json *elem0, struct json *elem1, struct json *elem2)
+{
+ struct json **elems = xmalloc(3 * sizeof *elems);
+ elems[0] = elem0;
+ elems[1] = elem1;
+ elems[2] = elem2;
+ return json_array_create(elems, 3);
+}
+
+struct json *
+json_object_create(void)
+{
+ struct json *json = json_create(JSON_OBJECT);
+ json->u.object = xmalloc(sizeof *json->u.object);
+ shash_init(json->u.object);
+ return json;
+}
+
+struct json *
+json_integer_create(long long int integer)
+{
+ struct json *json = json_create(JSON_INTEGER);
+ json->u.integer = integer;
+ return json;
+}
+
+struct json *
+json_real_create(double real)
+{
+ struct json *json = json_create(JSON_REAL);
+ json->u.real = real;
+ return json;
+}
+
+void
+json_object_put(struct json *json, const char *name, struct json *value)
+{
+ shash_add(json->u.object, name, value);
+}
+
+void
+json_object_put_string(struct json *json, const char *name, const char *value)
+{
+ json_object_put(json, name, json_string_create(value));
+}
+
+const char *
+json_string(const struct json *json)
+{
+ assert(json->type == JSON_STRING);
+ return json->u.string;
+}
+
+struct json_array *
+json_array(const struct json *json)
+{
+ assert(json->type == JSON_ARRAY);
+ return (struct json_array *) &json->u.array;
+}
+
+struct shash *
+json_object(const struct json *json)
+{
+ assert(json->type == JSON_OBJECT);
+ return (struct shash *) json->u.object;
+}
+
+bool
+json_boolean(const struct json *json)
+{
+ assert(json->type == JSON_TRUE || json->type == JSON_FALSE);
+ return json->type == JSON_TRUE;
+}
+
+double
+json_real(const struct json *json)
+{
+ assert(json->type == JSON_REAL || json->type == JSON_INTEGER);
+ return json->type == JSON_REAL ? json->u.real : json->u.integer;
+}
+
+int64_t
+json_integer(const struct json *json)
+{
+ assert(json->type == JSON_INTEGER);
+ return json->u.integer;
+}
+\f
+static void json_destroy_object(struct shash *object);
+static void json_destroy_array(struct json_array *array);
+
+/* Frees 'json' and everything it points to, recursively. */
+void
+json_destroy(struct json *json)
+{
+ if (json) {
+ switch (json->type) {
+ case JSON_OBJECT:
+ json_destroy_object(json->u.object);
+ break;
+
+ case JSON_ARRAY:
+ json_destroy_array(&json->u.array);
+ break;
+
+ case JSON_STRING:
+ free(json->u.string);
+ break;
+
+ case JSON_NULL:
+ case JSON_FALSE:
+ case JSON_TRUE:
+ case JSON_INTEGER:
+ case JSON_REAL:
+ break;
+
+ case JSON_N_TYPES:
+ NOT_REACHED();
+ }
+ free(json);
+ }
+}
+
+static void
+json_destroy_object(struct shash *object)
+{
+ struct shash_node *node, *next;
+
+ SHASH_FOR_EACH_SAFE (node, next, object) {
+ struct json *value = node->data;
+
+ json_destroy(value);
+ shash_delete(object, node);
+ }
+ shash_destroy(object);
+ free(object);
+}
+
+static void
+json_destroy_array(struct json_array *array)
+{
+ size_t i;
+
+ for (i = 0; i < array->n; i++) {
+ json_destroy(array->elems[i]);
+ }
+ free(array->elems);
+}
+\f
+static struct json *json_clone_object(const struct shash *object);
+static struct json *json_clone_array(const struct json_array *array);
+
+/* Returns a deep copy of 'json'. */
+struct json *
+json_clone(const struct json *json)
+{
+ switch (json->type) {
+ case JSON_OBJECT:
+ return json_clone_object(json->u.object);
+
+ case JSON_ARRAY:
+ return json_clone_array(&json->u.array);
+
+ case JSON_STRING:
+ return json_string_create(json->u.string);
+
+ case JSON_NULL:
+ case JSON_FALSE:
+ case JSON_TRUE:
+ return json_create(json->type);
+
+ case JSON_INTEGER:
+ return json_integer_create(json->u.integer);
+
+ case JSON_REAL:
+ return json_real_create(json->u.real);
+
+ case JSON_N_TYPES:
+ default:
+ NOT_REACHED();
+ }
+}
+
+static struct json *
+json_clone_object(const struct shash *object)
+{
+ struct shash_node *node;
+ struct json *json;
+
+ json = json_object_create();
+ SHASH_FOR_EACH (node, object) {
+ struct json *value = node->data;
+ json_object_put(json, node->name, json_clone(value));
+ }
+ return json;
+}
+
+static struct json *
+json_clone_array(const struct json_array *array)
+{
+ struct json **elems;
+ size_t i;
+
+ elems = xmalloc(array->n * sizeof *elems);
+ for (i = 0; i < array->n; i++) {
+ elems[i] = json_clone(array->elems[i]);
+ }
+ return json_array_create(elems, array->n);
+}
+\f
+static size_t
+json_hash_object(const struct shash *object, size_t basis)
+{
+ const struct shash_node **nodes;
+ size_t n, i;
+
+ nodes = shash_sort(object);
+ n = shash_count(object);
+ for (i = 0; i < n; i++) {
+ const struct shash_node *node = nodes[i];
+ basis = hash_string(node->name, basis);
+ basis = json_hash(node->data, basis);
+ }
+ return basis;
+}
+
+static size_t
+json_hash_array(const struct json_array *array, size_t basis)
+{
+ size_t i;
+
+ basis = hash_int(array->n, basis);
+ for (i = 0; i < array->n; i++) {
+ basis = json_hash(array->elems[i], basis);
+ }
+ return basis;
+}
+
+size_t
+json_hash(const struct json *json, size_t basis)
+{
+ switch (json->type) {
+ case JSON_OBJECT:
+ return json_hash_object(json->u.object, basis);
+
+ case JSON_ARRAY:
+ return json_hash_array(&json->u.array, basis);
+
+ case JSON_STRING:
+ return hash_string(json->u.string, basis);
+
+ case JSON_NULL:
+ case JSON_FALSE:
+ case JSON_TRUE:
+ return hash_int(json->type << 8, basis);
+
+ case JSON_INTEGER:
+ return hash_int(json->u.integer, basis);
+
+ case JSON_REAL:
+ return hash_double(json->u.real, basis);
+
+ case JSON_N_TYPES:
+ default:
+ NOT_REACHED();
+ }
+}
+
+static bool
+json_equal_object(const struct shash *a, const struct shash *b)
+{
+ struct shash_node *a_node;
+
+ if (shash_count(a) != shash_count(b)) {
+ return false;
+ }
+
+ SHASH_FOR_EACH (a_node, a) {
+ struct shash_node *b_node = shash_find(b, a_node->name);
+ if (!b_node || !json_equal(a_node->data, b_node->data)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool
+json_equal_array(const struct json_array *a, const struct json_array *b)
+{
+ size_t i;
+
+ if (a->n != b->n) {
+ return false;
+ }
+
+ for (i = 0; i < a->n; i++) {
+ if (!json_equal(a->elems[i], b->elems[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+json_equal(const struct json *a, const struct json *b)
+{
+ if (a->type != b->type) {
+ return false;
+ }
+
+ switch (a->type) {
+ case JSON_OBJECT:
+ return json_equal_object(a->u.object, b->u.object);
+
+ case JSON_ARRAY:
+ return json_equal_array(&a->u.array, &b->u.array);
+
+ case JSON_STRING:
+ return !strcmp(a->u.string, b->u.string);
+
+ case JSON_NULL:
+ case JSON_FALSE:
+ case JSON_TRUE:
+ return true;
+
+ case JSON_INTEGER:
+ return a->u.integer == b->u.integer;
+
+ case JSON_REAL:
+ return a->u.real == b->u.real;
+
+ case JSON_N_TYPES:
+ default:
+ NOT_REACHED();
+ }
+}
+\f
+/* Lexical analysis. */
+
+static void
+json_lex_keyword(struct json_parser *p)
+{
+ struct json_token token;
+ const char *s;
+
+ s = ds_cstr(&p->buffer);
+ if (!strcmp(s, "false")) {
+ token.type = T_FALSE;
+ } else if (!strcmp(s, "true")) {
+ token.type = T_TRUE;
+ } else if (!strcmp(s, "null")) {
+ token.type = T_NULL;
+ } else {
+ json_error(p, "invalid keyword '%s'", s);
+ return;
+ }
+ json_parser_input(p, &token);
+}
+
+static void
+json_lex_number(struct json_parser *p)
+{
+ const char *cp = ds_cstr(&p->buffer);
+ unsigned long long int significand = 0;
+ int sig_digits = 0;
+ bool imprecise = false;
+ bool negative = false;
+ int pow10 = 0;
+
+ /* Leading minus sign. */
+ if (*cp == '-') {
+ negative = true;
+ cp++;
+ }
+
+ /* At least one integer digit, but 0 may not be used as a leading digit for
+ * a longer number. */
+ significand = 0;
+ sig_digits = 0;
+ if (*cp == '0') {
+ cp++;
+ if (isdigit(*cp)) {
+ json_error(p, "leading zeros not allowed");
+ return;
+ }
+ } else if (isdigit(*cp)) {
+ do {
+ if (significand <= ULLONG_MAX / 10) {
+ significand = significand * 10 + (*cp - '0');
+ sig_digits++;
+ } else {
+ pow10++;
+ if (*cp != '0') {
+ imprecise = true;
+ }
+ }
+ cp++;
+ } while (isdigit(*cp));
+ } else {
+ json_error(p, "'-' must be followed by digit");
+ return;
+ }
+
+ /* Optional fraction. */
+ if (*cp == '.') {
+ cp++;
+ if (!isdigit(*cp)) {
+ json_error(p, "decimal point must be followed by digit");
+ return;
+ }
+ do {
+ if (significand <= ULLONG_MAX / 10) {
+ significand = significand * 10 + (*cp - '0');
+ sig_digits++;
+ pow10--;
+ } else if (*cp != '0') {
+ imprecise = true;
+ }
+ cp++;
+ } while (isdigit(*cp));
+ }
+
+ /* Optional exponent. */
+ if (*cp == 'e' || *cp == 'E') {
+ bool negative_exponent = false;
+ int exponent;
+
+ cp++;
+ if (*cp == '+') {
+ cp++;
+ } else if (*cp == '-') {
+ negative_exponent = true;
+ cp++;
+ }
+
+ if (!isdigit(*cp)) {
+ json_error(p, "exponent must contain at least one digit");
+ return;
+ }
+
+ exponent = 0;
+ do {
+ if (exponent >= INT_MAX / 10) {
+ json_error(p, "exponent outside valid range");
+ return;
+ }
+ exponent = exponent * 10 + (*cp - '0');
+ cp++;
+ } while (isdigit(*cp));
+
+ if (negative_exponent) {
+ pow10 -= exponent;
+ } else {
+ pow10 += exponent;
+ }
+ }
+
+ if (*cp != '\0') {
+ json_error(p, "syntax error in number");
+ return;
+ }
+
+ /* Figure out number.
+ *
+ * We suppress negative zeros as a matter of policy. */
+ if (!significand) {
+ struct json_token token;
+ token.type = T_INTEGER;
+ token.u.integer = 0;
+ json_parser_input(p, &token);
+ return;
+ }
+
+ if (!imprecise) {
+ while (pow10 > 0 && significand < ULLONG_MAX / 10) {
+ significand *= 10;
+ sig_digits++;
+ pow10--;
+ }
+ while (pow10 < 0 && significand % 10 == 0) {
+ significand /= 10;
+ sig_digits--;
+ pow10++;
+ }
+ if (pow10 == 0
+ && significand <= (negative
+ ? (unsigned long long int) LLONG_MAX + 1
+ : LLONG_MAX)) {
+ struct json_token token;
+ token.type = T_INTEGER;
+ token.u.integer = negative ? -significand : significand;
+ json_parser_input(p, &token);
+ return;
+ }
+ }
+
+ if (pow10 + sig_digits <= DBL_MAX_10_EXP) {
+ struct json_token token;
+ token.type = T_REAL;
+ token.u.real = significand * pow(10.0, pow10);
+ if (token.u.real <= DBL_MAX) {
+ if (negative && token.u.real) {
+ token.u.real = -token.u.real;
+ }
+ json_parser_input(p, &token);
+ return;
+ }
+ }
+ json_error(p, "number outside valid range");
+}
+
+static bool
+json_lex_4hex(struct json_parser *p, const char *cp, int *valuep)
+{
+ int value, i;
+
+ value = 0;
+ for (i = 0; i < 4; i++) {
+ unsigned char c = *cp++;
+ if (!isxdigit(c)) {
+ json_error(p, "malformed \\u escape");
+ return false;
+ }
+ value = (value << 4) | hexit_value(c);
+ }
+ if (!value) {
+ json_error(p, "null bytes not supported in quoted strings");
+ return false;
+ }
+ *valuep = value;
+ return true;
+}
+
+static const char *
+json_lex_unicode(struct json_parser *p, const char *cp, struct ds *s)
+{
+ int c0, c1;
+
+ if (!json_lex_4hex(p, cp, &c0)) {
+ return NULL;
+ }
+ cp += 4;
+ if (!uc_is_leading_surrogate(c0)) {
+ ds_put_utf8(s, c0);
+ return cp;
+ }
+
+ if (*cp++ != '\\' || *cp++ != 'u') {
+ json_error(p, "malformed escaped surrogate pair");
+ return NULL;
+ }
+
+ if (!json_lex_4hex(p, cp, &c1)) {
+ return NULL;
+ }
+ cp += 4;
+ if (!uc_is_trailing_surrogate(c1)) {
+ json_error(p, "second half of escaped surrogate pair is not "
+ "trailing surrogate");
+ return NULL;
+ }
+
+ ds_put_utf8(s, utf16_decode_surrogate_pair(c0, c1));
+ return cp;
+}
+
+static void
+json_lex_string(struct json_parser *p)
+{
+ struct json_token token;
+ const char *cp;
+ struct ds s;
+
+ cp = ds_cstr(&p->buffer);
+ if (!strchr(cp, '\\')) {
+ token.type = T_STRING;
+ token.u.string = cp;
+ json_parser_input(p, &token);
+ return;
+ }
+
+ ds_init(&s);
+ ds_reserve(&s, strlen(cp));
+ while (*cp != '\0') {
+ if (*cp != '\\') {
+ ds_put_char(&s, *cp++);
+ continue;
+ }
+
+ cp++;
+ switch (*cp++) {
+ case '"': case '\\': case '/':
+ ds_put_char(&s, cp[-1]);
+ break;
+
+ case 'b':
+ ds_put_char(&s, '\b');
+ break;
+
+ case 'f':
+ ds_put_char(&s, '\f');
+ break;
+
+ case 'n':
+ ds_put_char(&s, '\n');
+ break;
+
+ case 'r':
+ ds_put_char(&s, '\r');
+ break;
+
+ case 't':
+ ds_put_char(&s, '\t');
+ break;
+
+ case 'u':
+ cp = json_lex_unicode(p, cp, &s);
+ if (!cp) {
+ goto exit;
+ }
+ break;
+
+ default:
+ json_error(p, "bad escape \\%c", cp[-1]);
+ goto exit;
+ }
+ }
+
+ token.type = T_STRING;
+ token.u.string = ds_cstr(&s);
+ json_parser_input(p, &token);
+
+exit:
+ ds_destroy(&s);
+ return;
+}
+
+static bool
+json_lex_input(struct json_parser *p, int c)
+{
+ struct json_token token;
+
+ switch (p->lex_state) {
+ case JSON_LEX_START:
+ switch (c) {
+ case ' ': case '\t': case '\n': case '\r':
+ /* Nothing to do. */
+ return true;
+
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f': case 'g': case 'h': case 'i': case 'j':
+ case 'k': case 'l': case 'm': case 'n': case 'o':
+ case 'p': case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x': case 'y':
+ case 'z':
+ p->lex_state = JSON_LEX_KEYWORD;
+ break;
+
+ case '[': case '{': case ']': case '}': case ':': case ',':
+ token.type = c;
+ json_parser_input(p, &token);
+ return true;
+
+ case '-':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ p->lex_state = JSON_LEX_NUMBER;
+ break;
+
+ case '"':
+ p->lex_state = JSON_LEX_STRING;
+ return true;
+
+ default:
+ if (isprint(c)) {
+ json_error(p, "invalid character '%c'", c);
+ } else {
+ json_error(p, "invalid character U+%04x", c);
+ }
+ return true;
+ }
+ break;
+
+ case JSON_LEX_KEYWORD:
+ if (!isalpha((unsigned char) c)) {
+ json_lex_keyword(p);
+ return false;
+ }
+ break;
+
+ case JSON_LEX_NUMBER:
+ if (!strchr(".0123456789eE-+", c)) {
+ json_lex_number(p);
+ return false;
+ }
+ break;
+
+ case JSON_LEX_STRING:
+ if (c == '\\') {
+ p->lex_state = JSON_LEX_ESCAPE;
+ } else if (c == '"') {
+ json_lex_string(p);
+ return true;
+ } else if (c < 0x20) {
+ json_error(p, "U+%04X must be escaped in quoted string", c);
+ return true;
+ }
+ break;
+
+ case JSON_LEX_ESCAPE:
+ p->lex_state = JSON_LEX_STRING;
+ break;
+
+ default:
+ abort();
+ }
+ ds_put_char(&p->buffer, c);
+ return true;
+}
+\f
+/* Parsing. */
+
+/* Parses 'string' as a JSON object or array and returns a newly allocated
+ * 'struct json'. The caller must free the returned structure with
+ * json_destroy() when it is no longer needed.
+ *
+ * 'string' must be encoded in UTF-8.
+ *
+ * If 'string' is valid JSON, then the returned 'struct json' will be either an
+ * object (JSON_OBJECT) or an array (JSON_ARRAY).
+ *
+ * If 'string' is not valid JSON, then the returned 'struct json' will be a
+ * string (JSON_STRING) that describes the particular error encountered during
+ * parsing. (This is an acceptable means of error reporting because at its top
+ * level JSON must be either an object or an array; a bare string is not
+ * valid.) */
+struct json *
+json_from_string(const char *string)
+{
+ struct json_parser *p = json_parser_create(JSPF_TRAILER);
+ json_parser_feed(p, string, strlen(string));
+ return json_parser_finish(p);
+}
+
+/* Reads the file named 'file_name', parses its contents as a JSON object or
+ * array, and returns a newly allocated 'struct json'. The caller must free
+ * the returned structure with json_destroy() when it is no longer needed.
+ *
+ * The file must be encoded in UTF-8.
+ *
+ * See json_from_string() for return value semantics.
+ */
+struct json *
+json_from_file(const char *file_name)
+{
+ struct json_parser *p;
+ struct json *json;
+ FILE *stream;
+
+ /* Open file. */
+ stream = fopen(file_name, "r");
+ if (!stream) {
+ return json_string_create_nocopy(
+ xasprintf("error opening \"%s\": %s", file_name, strerror(errno)));
+ }
+
+ /* Read and parse file. */
+ p = json_parser_create(JSPF_TRAILER);
+ for (;;) {
+ char buffer[BUFSIZ];
+ size_t n;
+
+ n = fread(buffer, 1, sizeof buffer, stream);
+ if (!n || json_parser_feed(p, buffer, n) != n) {
+ break;
+ }
+ }
+ json = json_parser_finish(p);
+
+ /* Close file and check for I/O errors. */
+ if (ferror(stream)) {
+ json_destroy(json);
+ json = json_string_create_nocopy(
+ xasprintf("error reading \"%s\": %s", file_name, strerror(errno)));
+ }
+ fclose(stream);
+
+ return json;
+}
+
+struct json_parser *
+json_parser_create(int flags)
+{
+ struct json_parser *p = xzalloc(sizeof *p);
+ p->flags = flags;
+ return p;
+}
+
+size_t
+json_parser_feed(struct json_parser *p, const char *input, size_t n)
+{
+ size_t i;
+ for (i = 0; !p->done && i < n; ) {
+ if (json_lex_input(p, input[i])) {
+ i++;
+ }
+ }
+ return i;
+}
+
+bool
+json_parser_is_done(const struct json_parser *p)
+{
+ return p->done;
+}
+
+struct json *
+json_parser_finish(struct json_parser *p)
+{
+ struct json *json;
+
+ switch (p->lex_state) {
+ case JSON_LEX_START:
+ break;
+
+ case JSON_LEX_STRING:
+ case JSON_LEX_ESCAPE:
+ json_error(p, "unexpected end of input in quoted string");
+ break;
+
+ case JSON_LEX_NUMBER:
+ case JSON_LEX_KEYWORD:
+ json_lex_input(p, ' ');
+ break;
+ }
+
+ if (p->parse_state == JSON_PARSE_START) {
+ json_error(p, "empty input stream");
+ } else if (p->parse_state != JSON_PARSE_END) {
+ json_error(p, "unexpected end of input");
+ }
+
+ if (!p->error) {
+ assert(p->height == 1);
+ assert(p->stack[0].json != NULL);
+ json = p->stack[--p->height].json;
+ } else {
+ json = json_string_create_nocopy(p->error);
+ p->error = NULL;
+ }
+
+ json_parser_abort(p);
+
+ return json;
+}
+
+void
+json_parser_abort(struct json_parser *p)
+{
+ if (p) {
+ ds_destroy(&p->buffer);
+ if (p->height) {
+ json_destroy(p->stack[0].json);
+ }
+ free(p->stack);
+ free(p->member_name);
+ free(p->error);
+ free(p);
+ }
+}
+
+static struct json_parser_node *
+json_parser_top(struct json_parser *p)
+{
+ return &p->stack[p->height - 1];
+}
+
+static void
+json_parser_put_value(struct json_parser *p, struct json *value)
+{
+ struct json_parser_node *node = json_parser_top(p);
+ if (node->json->type == JSON_OBJECT) {
+ json_object_put(node->json, p->member_name, value);
+ free(p->member_name);
+ p->member_name = NULL;
+ } else if (node->json->type == JSON_ARRAY) {
+ json_array_add(node->json, value);
+ } else {
+ NOT_REACHED();
+ }
+}
+
+static struct json_parser_node *
+json_parser_push(struct json_parser *p,
+ struct json *new_json, enum json_parse_state new_state)
+{
+ if (p->height < JSON_MAX_HEIGHT) {
+ struct json_parser_node *node;
+
+ if (p->height >= p->allocated_height) {
+ p->stack = x2nrealloc(p->stack, &p->allocated_height,
+ sizeof *p->stack);
+ }
+
+ if (p->height > 0) {
+ json_parser_put_value(p, new_json);
+ }
+
+ node = &p->stack[p->height++];
+ node->json = new_json;
+ p->parse_state = new_state;
+ return node;
+ } else {
+ json_error(p, "input exceeds maximum nesting depth %d",
+ JSON_MAX_HEIGHT);
+ return NULL;
+ }
+}
+
+static void
+json_parser_push_object(struct json_parser *p)
+{
+ json_parser_push(p, json_object_create(), JSON_PARSE_OBJECT_INIT);
+}
+
+static void
+json_parser_push_array(struct json_parser *p)
+{
+ json_parser_push(p, json_array_create_empty(), JSON_PARSE_ARRAY_INIT);
+}
+
+static void
+json_parse_value(struct json_parser *p, struct json_token *token,
+ enum json_parse_state next_state)
+{
+ struct json *value;
+
+ switch (token->type) {
+ case T_FALSE:
+ value = json_boolean_create(false);
+ break;
+
+ case T_NULL:
+ value = json_null_create();
+ break;
+
+ case T_TRUE:
+ value = json_boolean_create(true);
+ break;
+
+ case '{':
+ json_parser_push_object(p);
+ return;
+
+ case '[':
+ json_parser_push_array(p);
+ return;
+
+ case T_INTEGER:
+ value = json_integer_create(token->u.integer);
+ break;
+
+ case T_REAL:
+ value = json_real_create(token->u.real);
+ break;
+
+ case T_STRING:
+ value = json_string_create(token->u.string);
+ break;
+
+ case T_EOF:
+ case '}':
+ case ']':
+ case ':':
+ case ',':
+ default:
+ json_error(p, "syntax error expecting value");
+ return;
+ }
+
+ json_parser_put_value(p, value);
+ p->parse_state = next_state;
+}
+
+static void
+json_parser_pop(struct json_parser *p)
+{
+ struct json_parser_node *node;
+
+ /* Conserve memory. */
+ node = json_parser_top(p);
+ if (node->json->type == JSON_ARRAY) {
+ json_array_trim(node->json);
+ }
+
+ /* Pop off the top-of-stack. */
+ if (p->height == 1) {
+ p->parse_state = JSON_PARSE_END;
+ if (!(p->flags & JSPF_TRAILER)) {
+ p->done = true;
+ }
+ } else {
+ p->height--;
+ node = json_parser_top(p);
+ if (node->json->type == JSON_ARRAY) {
+ p->parse_state = JSON_PARSE_ARRAY_NEXT;
+ } else if (node->json->type == JSON_OBJECT) {
+ p->parse_state = JSON_PARSE_OBJECT_NEXT;
+ } else {
+ NOT_REACHED();
+ }
+ }
+}
+
+static void
+json_parser_input(struct json_parser *p, struct json_token *token)
+{
+ switch (p->parse_state) {
+ case JSON_PARSE_START:
+ if (token->type == '{') {
+ json_parser_push_object(p);
+ } else if (token->type == '[') {
+ json_parser_push_array(p);
+ } else {
+ json_error(p, "syntax error at beginning of input");
+ }
+ break;
+
+ case JSON_PARSE_END:
+ json_error(p, "trailing garbage at end of input");
+ break;
+
+ case JSON_PARSE_OBJECT_INIT:
+ if (token->type == '}') {
+ json_parser_pop(p);
+ break;
+ }
+ /* Fall through. */
+ case JSON_PARSE_OBJECT_NAME:
+ if (token->type == T_STRING) {
+ p->member_name = xstrdup(token->u.string);
+ p->parse_state = JSON_PARSE_OBJECT_COLON;
+ } else {
+ json_error(p, "syntax error parsing object expecting string");
+ }
+ break;
+
+ case JSON_PARSE_OBJECT_COLON:
+ if (token->type == ':') {
+ p->parse_state = JSON_PARSE_OBJECT_VALUE;
+ } else {
+ json_error(p, "syntax error parsing object expecting ':'");
+ }
+ break;
+
+ case JSON_PARSE_OBJECT_VALUE:
+ json_parse_value(p, token, JSON_PARSE_OBJECT_NEXT);
+ break;
+
+ case JSON_PARSE_OBJECT_NEXT:
+ if (token->type == ',') {
+ p->parse_state = JSON_PARSE_OBJECT_NAME;
+ } else if (token->type == '}') {
+ json_parser_pop(p);
+ } else {
+ json_error(p, "syntax error expecting '}' or ','");
+ }
+ break;
+
+ case JSON_PARSE_ARRAY_INIT:
+ if (token->type == ']') {
+ json_parser_pop(p);
+ break;
+ }
+ /* Fall through. */
+ case JSON_PARSE_ARRAY_VALUE:
+ json_parse_value(p, token, JSON_PARSE_ARRAY_NEXT);
+ break;
+
+ case JSON_PARSE_ARRAY_NEXT:
+ if (token->type == ',') {
+ p->parse_state = JSON_PARSE_ARRAY_VALUE;
+ } else if (token->type == ']') {
+ json_parser_pop(p);
+ } else {
+ json_error(p, "syntax error expecting ']' or ','");
+ }
+ break;
+
+ default:
+ abort();
+ }
+
+ p->lex_state = JSON_LEX_START;
+ ds_clear(&p->buffer);
+}
+
+static struct json *
+json_create(enum json_type type)
+{
+ struct json *json = xmalloc(sizeof *json);
+ json->type = type;
+ return json;
+}
+
+static void
+json_error(struct json_parser *p, const char *format, ...)
+{
+ if (!p->error) {
+ va_list args;
+
+ va_start(args, format);
+ p->error = xvasprintf(format, args);
+ va_end(args);
+
+ p->done = true;
+ }
+}
+\f
+#define SPACES_PER_LEVEL 2
+
+struct json_serializer {
+ struct ds ds;
+ int depth;
+ int flags;
+};
+
+static void json_to_ds(const struct json *, struct json_serializer *);
+static void json_object_to_ds(const struct shash *object,
+ struct json_serializer *);
+static void json_array_to_ds(const struct json_array *,
+ struct json_serializer *);
+static void json_string_to_ds(const char *string, struct ds *);
+
+/* Converts 'json' to a string in JSON format, encoded in UTF-8, and returns
+ * that string. The caller is responsible for freeing the returned string,
+ * with free(), when it is no longer needed.
+ *
+ * If 'flags' contains JSSF_PRETTY, the output is pretty-printed with each
+ * nesting level introducing an additional indentation. Otherwise, the
+ * returned string does not contain any new-line characters.
+ *
+ * If 'flags' contains JSSF_SORT, members of objects in the output are sorted
+ * in bytewise lexicographic order for reproducibility. Otherwise, members of
+ * objects are output in an indeterminate order.
+ *
+ * The returned string is valid JSON only if 'json' represents an array or an
+ * object, since a bare literal does not satisfy the JSON grammar. */
+char *
+json_to_string(const struct json *json, int flags)
+{
+ struct json_serializer s;
+ ds_init(&s.ds);
+ s.depth = 0;
+ s.flags = flags;
+ json_to_ds(json, &s);
+ return ds_steal_cstr(&s.ds);
+}
+
+static void
+json_to_ds(const struct json *json, struct json_serializer *s)
+{
+ struct ds *ds = &s->ds;
+
+ switch (json->type) {
+ case JSON_NULL:
+ ds_put_cstr(ds, "null");
+ break;
+
+ case JSON_FALSE:
+ ds_put_cstr(ds, "false");
+ break;
+
+ case JSON_TRUE:
+ ds_put_cstr(ds, "true");
+ break;
+
+ case JSON_OBJECT:
+ json_object_to_ds(json->u.object, s);
+ break;
+
+ case JSON_ARRAY:
+ json_array_to_ds(&json->u.array, s);
+ break;
+
+ case JSON_INTEGER:
+ ds_put_format(ds, "%lld", json->u.integer);
+ break;
+
+ case JSON_REAL:
+ ds_put_format(ds, "%.*g", DBL_DIG, json->u.real);
+ break;
+
+ case JSON_STRING:
+ json_string_to_ds(json->u.string, ds);
+ break;
+
+ case JSON_N_TYPES:
+ default:
+ NOT_REACHED();
+ }
+}
+
+static void
+indent_line(struct json_serializer *s)
+{
+ if (s->flags & JSSF_PRETTY) {
+ ds_put_char(&s->ds, '\n');
+ ds_put_char_multiple(&s->ds, ' ', SPACES_PER_LEVEL * s->depth);
+ }
+}
+
+static void
+json_object_member_to_ds(size_t i, const struct shash_node *node,
+ struct json_serializer *s)
+{
+ struct ds *ds = &s->ds;
+
+ if (i) {
+ ds_put_char(ds, ',');
+ indent_line(s);
+ }
+
+ json_string_to_ds(node->name, ds);
+ ds_put_char(ds, ':');
+ if (s->flags & JSSF_PRETTY) {
+ ds_put_char(ds, ' ');
+ }
+ json_to_ds(node->data, s);
+}
+
+static void
+json_object_to_ds(const struct shash *object, struct json_serializer *s)
+{
+ struct ds *ds = &s->ds;
+
+ ds_put_char(ds, '{');
+
+ s->depth++;
+ indent_line(s);
+
+ if (s->flags & JSSF_SORT) {
+ const struct shash_node **nodes;
+ size_t n, i;
+
+ nodes = shash_sort(object);
+ n = shash_count(object);
+ for (i = 0; i < n; i++) {
+ json_object_member_to_ds(i, nodes[i], s);
+ }
+ free(nodes);
+ } else {
+ struct shash_node *node;
+ size_t i;
+
+ i = 0;
+ SHASH_FOR_EACH (node, object) {
+ json_object_member_to_ds(i++, node, s);
+ }
+ }
+
+ ds_put_char(ds, '}');
+ s->depth--;
+}
+
+static void
+json_array_to_ds(const struct json_array *array, struct json_serializer *s)
+{
+ struct ds *ds = &s->ds;
+ size_t i;
+
+ ds_put_char(ds, '[');
+ s->depth++;
+
+ if (array->n > 0) {
+ indent_line(s);
+
+ for (i = 0; i < array->n; i++) {
+ if (i) {
+ ds_put_char(ds, ',');
+ indent_line(s);
+ }
+ json_to_ds(array->elems[i], s);
+ }
+ }
+
+ s->depth--;
+ ds_put_char(ds, ']');
+}
+
+static void
+json_string_to_ds(const char *string, struct ds *ds)
+{
+ uint8_t c;
+
+ ds_put_char(ds, '"');
+ while ((c = *string++) != '\0') {
+ switch (c) {
+ case '"':
+ ds_put_cstr(ds, "\\\"");
+ break;
+
+ case '\\':
+ ds_put_cstr(ds, "\\\\");
+ break;
+
+ case '\b':
+ ds_put_cstr(ds, "\\b");
+ break;
+
+ case '\f':
+ ds_put_cstr(ds, "\\f");
+ break;
+
+ case '\n':
+ ds_put_cstr(ds, "\\n");
+ break;
+
+ case '\r':
+ ds_put_cstr(ds, "\\r");
+ break;
+
+ case '\t':
+ ds_put_cstr(ds, "\\t");
+ break;
+
+ default:
+ if (c >= 32) {
+ ds_put_char(ds, c);
+ } else {
+ ds_put_format(ds, "\\u%04x", c);
+ }
+ break;
+ }
+ }
+ ds_put_char(ds, '"');
+}
--- /dev/null
+/*
+ * Copyright (c) 2009 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.
+ */
+
+#ifndef JSON_H
+#define JSON_H 1
+
+/* This is an implementation of JavaScript Object Notation (JSON) as specified
+ * by RFC 4627. It is intended to fully comply with RFC 4627, with the
+ * following known exceptions and clarifications:
+ *
+ * - Null bytes (\u0000) are not allowed in strings.
+ *
+ * - Only UTF-8 encoding is supported (RFC 4627 allows for other Unicode
+ * encodings).
+ *
+ * - Names within an object must be unique (RFC 4627 says that they
+ * "should" be unique).
+ */
+
+#include "shash.h"
+
+/* Type of a JSON value. */
+enum json_type {
+ JSON_NULL, /* null */
+ JSON_FALSE, /* false */
+ JSON_TRUE, /* true */
+ JSON_OBJECT, /* {"a": b, "c": d, ...} */
+ JSON_ARRAY, /* [1, 2, 3, ...] */
+ JSON_INTEGER, /* 123. */
+ JSON_REAL, /* 123.456. */
+ JSON_STRING, /* "..." */
+ JSON_N_TYPES
+};
+
+const char *json_type_to_string(enum json_type);
+
+/* A JSON array. */
+struct json_array {
+ size_t n, n_allocated;
+ struct json **elems;
+};
+
+/* A JSON value. */
+struct json {
+ enum json_type type;
+ union {
+ struct shash *object; /* Contains "struct json *"s. */
+ struct json_array array;
+ long long int integer;
+ double real;
+ char *string;
+ } u;
+};
+
+struct json *json_null_create(void);
+struct json *json_boolean_create(bool);
+struct json *json_string_create(const char *);
+struct json *json_string_create_nocopy(char *);
+struct json *json_integer_create(long long int);
+struct json *json_real_create(double);
+
+struct json *json_array_create_empty(void);
+void json_array_add(struct json *, struct json *element);
+void json_array_trim(struct json *);
+struct json *json_array_create(struct json **, size_t n);
+struct json *json_array_create_2(struct json *, struct json *);
+struct json *json_array_create_3(struct json *, struct json *, struct json *);
+
+struct json *json_object_create(void);
+void json_object_put(struct json *, const char *name, struct json *value);
+void json_object_put_string(struct json *,
+ const char *name, const char *value);
+
+const char *json_string(const struct json *);
+struct json_array *json_array(const struct json *);
+struct shash *json_object(const struct json *);
+bool json_boolean(const struct json *);
+double json_real(const struct json *);
+int64_t json_integer(const struct json *);
+
+struct json *json_clone(const struct json *);
+void json_destroy(struct json *);
+
+size_t json_hash(const struct json *, size_t basis);
+bool json_equal(const struct json *, const struct json *);
+\f
+/* Parsing JSON. */
+enum {
+ JSPF_TRAILER = 1 << 0 /* Check for garbage following input. */
+};
+
+struct json_parser *json_parser_create(int flags);
+size_t json_parser_feed(struct json_parser *, const char *, size_t);
+bool json_parser_is_done(const struct json_parser *);
+struct json *json_parser_finish(struct json_parser *);
+void json_parser_abort(struct json_parser *);
+
+struct json *json_from_string(const char *string);
+struct json *json_from_file(const char *file_name);
+\f
+/* Serializing JSON. */
+
+enum {
+ JSSF_PRETTY = 1 << 0, /* Multiple lines with indentation, if true. */
+ JSSF_SORT = 1 << 1 /* Object members in sorted order, if true. */
+};
+char *json_to_string(const struct json *, int flags);
+
+#endif /* json.h */
--- /dev/null
+/*
+ * Copyright (c) 2009 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 "byteq.h"
+#include "dynamic-string.h"
+#include "json.h"
+#include "list.h"
+#include "ofpbuf.h"
+#include "poll-loop.h"
+#include "queue.h"
+#include "reconnect.h"
+#include "stream.h"
+#include "timeval.h"
+
+#define THIS_MODULE VLM_jsonrpc
+#include "vlog.h"
+\f
+struct jsonrpc {
+ struct stream *stream;
+ char *name;
+ int status;
+
+ /* Input. */
+ struct byteq input;
+ struct json_parser *parser;
+ struct jsonrpc_msg *received;
+
+ /* Output. */
+ struct ovs_queue output;
+ size_t backlog;
+};
+
+/* Rate limit for error messages. */
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
+
+static void jsonrpc_received(struct jsonrpc *);
+static void jsonrpc_cleanup(struct jsonrpc *);
+
+struct jsonrpc *
+jsonrpc_open(struct stream *stream)
+{
+ struct jsonrpc *rpc;
+
+ assert(stream != NULL);
+
+ rpc = xzalloc(sizeof *rpc);
+ rpc->name = xstrdup(stream_get_name(stream));
+ rpc->stream = stream;
+ byteq_init(&rpc->input);
+ queue_init(&rpc->output);
+
+ return rpc;
+}
+
+void
+jsonrpc_close(struct jsonrpc *rpc)
+{
+ if (rpc) {
+ jsonrpc_cleanup(rpc);
+ free(rpc->name);
+ free(rpc);
+ }
+}
+
+void
+jsonrpc_run(struct jsonrpc *rpc)
+{
+ if (rpc->status) {
+ return;
+ }
+
+ while (!queue_is_empty(&rpc->output)) {
+ struct ofpbuf *buf = rpc->output.head;
+ int retval;
+
+ retval = stream_send(rpc->stream, buf->data, buf->size);
+ if (retval >= 0) {
+ rpc->backlog -= retval;
+ ofpbuf_pull(buf, retval);
+ if (!buf->size) {
+ ofpbuf_delete(queue_pop_head(&rpc->output));
+ }
+ } else {
+ if (retval != -EAGAIN) {
+ VLOG_WARN_RL(&rl, "%s: send error: %s",
+ rpc->name, strerror(-retval));
+ jsonrpc_error(rpc, -retval);
+ }
+ break;
+ }
+ }
+}
+
+void
+jsonrpc_wait(struct jsonrpc *rpc)
+{
+ if (!rpc->status && !queue_is_empty(&rpc->output)) {
+ stream_send_wait(rpc->stream);
+ }
+}
+
+int
+jsonrpc_get_status(const struct jsonrpc *rpc)
+{
+ return rpc->status;
+}
+
+size_t
+jsonrpc_get_backlog(const struct jsonrpc *rpc)
+{
+ return rpc->status ? 0 : rpc->backlog;
+}
+
+const char *
+jsonrpc_get_name(const struct jsonrpc *rpc)
+{
+ return rpc->name;
+}
+
+static void
+jsonrpc_log_msg(const struct jsonrpc *rpc, const char *title,
+ const struct jsonrpc_msg *msg)
+{
+ if (VLOG_IS_DBG_ENABLED()) {
+ struct ds s = DS_EMPTY_INITIALIZER;
+ if (msg->method) {
+ ds_put_format(&s, ", method=\"%s\"", msg->method);
+ }
+ if (msg->params) {
+ ds_put_cstr(&s, ", params=");
+ ds_put_and_free_cstr(&s, json_to_string(msg->params, 0));
+ }
+ if (msg->result) {
+ ds_put_cstr(&s, ", result=");
+ ds_put_and_free_cstr(&s, json_to_string(msg->result, 0));
+ }
+ if (msg->error) {
+ ds_put_cstr(&s, ", error=");
+ ds_put_and_free_cstr(&s, json_to_string(msg->error, 0));
+ }
+ if (msg->id) {
+ ds_put_cstr(&s, ", id=");
+ ds_put_and_free_cstr(&s, json_to_string(msg->id, 0));
+ }
+ VLOG_DBG("%s: %s %s%s", rpc->name, title,
+ jsonrpc_msg_type_to_string(msg->type), ds_cstr(&s));
+ ds_destroy(&s);
+ }
+}
+
+int
+jsonrpc_send(struct jsonrpc *rpc, struct jsonrpc_msg *msg)
+{
+ struct ofpbuf *buf;
+ struct json *json;
+ size_t length;
+ char *s;
+
+ if (rpc->status) {
+ jsonrpc_msg_destroy(msg);
+ return rpc->status;
+ }
+
+ jsonrpc_log_msg(rpc, "send", msg);
+
+ json = jsonrpc_msg_to_json(msg);
+ s = json_to_string(json, 0);
+ length = strlen(s);
+ json_destroy(json);
+
+ buf = xmalloc(sizeof *buf);
+ ofpbuf_use(buf, s, length);
+ buf->size = length;
+ queue_push_tail(&rpc->output, buf);
+ rpc->backlog += length;
+
+ if (rpc->output.n == 1) {
+ jsonrpc_run(rpc);
+ }
+ return rpc->status;
+}
+
+int
+jsonrpc_recv(struct jsonrpc *rpc, struct jsonrpc_msg **msgp)
+{
+ *msgp = NULL;
+ if (rpc->status) {
+ return rpc->status;
+ }
+
+ while (!rpc->received) {
+ if (byteq_is_empty(&rpc->input)) {
+ size_t chunk;
+ int retval;
+
+ chunk = byteq_headroom(&rpc->input);
+ retval = stream_recv(rpc->stream, byteq_head(&rpc->input), chunk);
+ if (retval < 0) {
+ if (retval == -EAGAIN) {
+ return EAGAIN;
+ } else {
+ VLOG_WARN_RL(&rl, "%s: receive error: %s",
+ rpc->name, strerror(-retval));
+ jsonrpc_error(rpc, -retval);
+ return rpc->status;
+ }
+ } else if (retval == 0) {
+ VLOG_INFO_RL(&rl, "%s: connection closed", rpc->name);
+ jsonrpc_error(rpc, EOF);
+ return EOF;
+ }
+ byteq_advance_head(&rpc->input, retval);
+ } else {
+ size_t n, used;
+
+ if (!rpc->parser) {
+ rpc->parser = json_parser_create(0);
+ }
+ n = byteq_tailroom(&rpc->input);
+ used = json_parser_feed(rpc->parser,
+ (char *) byteq_tail(&rpc->input), n);
+ byteq_advance_tail(&rpc->input, used);
+ if (json_parser_is_done(rpc->parser)) {
+ jsonrpc_received(rpc);
+ if (rpc->status) {
+ return rpc->status;
+ }
+ }
+ }
+ }
+
+ *msgp = rpc->received;
+ rpc->received = NULL;
+ return 0;
+}
+
+void
+jsonrpc_recv_wait(struct jsonrpc *rpc)
+{
+ if (rpc->status || rpc->received || !byteq_is_empty(&rpc->input)) {
+ poll_immediate_wake();
+ } else {
+ stream_recv_wait(rpc->stream);
+ }
+}
+
+int
+jsonrpc_send_block(struct jsonrpc *rpc, struct jsonrpc_msg *msg)
+{
+ int error;
+
+ error = jsonrpc_send(rpc, msg);
+ if (error) {
+ return error;
+ }
+
+ while (!queue_is_empty(&rpc->output) && !rpc->status) {
+ jsonrpc_run(rpc);
+ jsonrpc_wait(rpc);
+ poll_block();
+ }
+ return rpc->status;
+}
+
+int
+jsonrpc_recv_block(struct jsonrpc *rpc, struct jsonrpc_msg **msgp)
+{
+ for (;;) {
+ int error = jsonrpc_recv(rpc, msgp);
+ if (error != EAGAIN) {
+ return error;
+ }
+
+ jsonrpc_run(rpc);
+ jsonrpc_wait(rpc);
+ jsonrpc_recv_wait(rpc);
+ poll_block();
+ }
+}
+
+int
+jsonrpc_transact_block(struct jsonrpc *rpc, struct jsonrpc_msg *request,
+ struct jsonrpc_msg **replyp)
+{
+ struct jsonrpc_msg *reply = NULL;
+ struct json *id;
+ int error;
+
+ id = json_clone(request->id);
+ error = jsonrpc_send_block(rpc, request);
+ if (!error) {
+ for (;;) {
+ error = jsonrpc_recv_block(rpc, &reply);
+ if (error
+ || (reply->type == JSONRPC_REPLY
+ && json_equal(id, reply->id))) {
+ break;
+ }
+ jsonrpc_msg_destroy(reply);
+ }
+ }
+ *replyp = error ? NULL : reply;
+ json_destroy(id);
+ return error;
+}
+
+static void
+jsonrpc_received(struct jsonrpc *rpc)
+{
+ struct jsonrpc_msg *msg;
+ struct json *json;
+ char *error;
+
+ json = json_parser_finish(rpc->parser);
+ rpc->parser = NULL;
+ if (json->type == JSON_STRING) {
+ VLOG_WARN_RL(&rl, "%s: error parsing stream: %s",
+ rpc->name, json_string(json));
+ jsonrpc_error(rpc, EPROTO);
+ json_destroy(json);
+ return;
+ }
+
+ error = jsonrpc_msg_from_json(json, &msg);
+ if (error) {
+ VLOG_WARN_RL(&rl, "%s: received bad JSON-RPC message: %s",
+ rpc->name, error);
+ free(error);
+ jsonrpc_error(rpc, EPROTO);
+ return;
+ }
+
+ jsonrpc_log_msg(rpc, "received", msg);
+ rpc->received = msg;
+}
+
+void
+jsonrpc_error(struct jsonrpc *rpc, int error)
+{
+ assert(error);
+ if (!rpc->status) {
+ rpc->status = error;
+ jsonrpc_cleanup(rpc);
+ }
+}
+
+static void
+jsonrpc_cleanup(struct jsonrpc *rpc)
+{
+ stream_close(rpc->stream);
+ rpc->stream = NULL;
+
+ json_parser_abort(rpc->parser);
+ rpc->parser = NULL;
+
+ jsonrpc_msg_destroy(rpc->received);
+ rpc->received = NULL;
+
+ queue_clear(&rpc->output);
+ rpc->backlog = 0;
+}
+\f
+static struct jsonrpc_msg *
+jsonrpc_create(enum jsonrpc_msg_type type, const char *method,
+ struct json *params, struct json *result, struct json *error,
+ struct json *id)
+{
+ struct jsonrpc_msg *msg = xmalloc(sizeof *msg);
+ msg->type = type;
+ msg->method = method ? xstrdup(method) : NULL;
+ msg->params = params;
+ msg->result = result;
+ msg->error = error;
+ msg->id = id;
+ return msg;
+}
+
+static struct json *
+jsonrpc_create_id(void)
+{
+ static unsigned int id;
+ return json_integer_create(id++);
+}
+
+struct jsonrpc_msg *
+jsonrpc_create_request(const char *method, struct json *params,
+ struct json **idp)
+{
+ struct json *id = jsonrpc_create_id();
+ if (idp) {
+ *idp = json_clone(id);
+ }
+ return jsonrpc_create(JSONRPC_REQUEST, method, params, NULL, NULL, id);
+}
+
+struct jsonrpc_msg *
+jsonrpc_create_notify(const char *method, struct json *params)
+{
+ return jsonrpc_create(JSONRPC_NOTIFY, method, params, NULL, NULL, NULL);
+}
+
+struct jsonrpc_msg *
+jsonrpc_create_reply(struct json *result, const struct json *id)
+{
+ return jsonrpc_create(JSONRPC_REPLY, NULL, NULL, result, NULL,
+ json_clone(id));
+}
+
+struct jsonrpc_msg *
+jsonrpc_create_error(struct json *error, const struct json *id)
+{
+ return jsonrpc_create(JSONRPC_REPLY, NULL, NULL, NULL, error,
+ json_clone(id));
+}
+
+const char *
+jsonrpc_msg_type_to_string(enum jsonrpc_msg_type type)
+{
+ switch (type) {
+ case JSONRPC_REQUEST:
+ return "request";
+
+ case JSONRPC_NOTIFY:
+ return "notification";
+
+ case JSONRPC_REPLY:
+ return "reply";
+
+ case JSONRPC_ERROR:
+ return "error";
+ }
+ return "(null)";
+}
+
+char *
+jsonrpc_msg_is_valid(const struct jsonrpc_msg *m)
+{
+ const char *type_name;
+ unsigned int pattern;
+
+ if (m->params && m->params->type != JSON_ARRAY) {
+ return xstrdup("\"params\" must be JSON array");
+ }
+
+ switch (m->type) {
+ case JSONRPC_REQUEST:
+ pattern = 0x11001;
+ break;
+
+ case JSONRPC_NOTIFY:
+ pattern = 0x11000;
+ break;
+
+ case JSONRPC_REPLY:
+ pattern = 0x00101;
+ break;
+
+ case JSONRPC_ERROR:
+ pattern = 0x00011;
+ break;
+
+ default:
+ return xasprintf("invalid JSON-RPC message type %d", m->type);
+ }
+
+ type_name = jsonrpc_msg_type_to_string(m->type);
+ if ((m->method != NULL) != ((pattern & 0x10000) != 0)) {
+ return xasprintf("%s must%s have \"method\"",
+ type_name, (pattern & 0x10000) ? "" : " not");
+
+ }
+ if ((m->params != NULL) != ((pattern & 0x1000) != 0)) {
+ return xasprintf("%s must%s have \"params\"",
+ type_name, (pattern & 0x1000) ? "" : " not");
+
+ }
+ if ((m->result != NULL) != ((pattern & 0x100) != 0)) {
+ return xasprintf("%s must%s have \"result\"",
+ type_name, (pattern & 0x100) ? "" : " not");
+
+ }
+ if ((m->error != NULL) != ((pattern & 0x10) != 0)) {
+ return xasprintf("%s must%s have \"error\"",
+ type_name, (pattern & 0x10) ? "" : " not");
+
+ }
+ if ((m->id != NULL) != ((pattern & 0x1) != 0)) {
+ return xasprintf("%s must%s have \"id\"",
+ type_name, (pattern & 0x1) ? "" : " not");
+
+ }
+ return NULL;
+}
+
+void
+jsonrpc_msg_destroy(struct jsonrpc_msg *m)
+{
+ if (m) {
+ free(m->method);
+ json_destroy(m->params);
+ json_destroy(m->result);
+ json_destroy(m->error);
+ json_destroy(m->id);
+ free(m);
+ }
+}
+
+static struct json *
+null_from_json_null(struct json *json)
+{
+ if (json && json->type == JSON_NULL) {
+ json_destroy(json);
+ return NULL;
+ }
+ return json;
+}
+
+char *
+jsonrpc_msg_from_json(struct json *json, struct jsonrpc_msg **msgp)
+{
+ struct json *method = NULL;
+ struct jsonrpc_msg *msg = NULL;
+ struct shash *object;
+ char *error;
+
+ if (json->type != JSON_OBJECT) {
+ error = xstrdup("message is not a JSON object");
+ goto exit;
+ }
+ object = json_object(json);
+
+ method = shash_find_and_delete(object, "method");
+ if (method && method->type != JSON_STRING) {
+ error = xstrdup("method is not a JSON string");
+ goto exit;
+ }
+
+ msg = xzalloc(sizeof *msg);
+ msg->method = method ? xstrdup(method->u.string) : NULL;
+ msg->params = null_from_json_null(shash_find_and_delete(object, "params"));
+ msg->result = null_from_json_null(shash_find_and_delete(object, "result"));
+ msg->error = null_from_json_null(shash_find_and_delete(object, "error"));
+ msg->id = null_from_json_null(shash_find_and_delete(object, "id"));
+ msg->type = (msg->result ? JSONRPC_REPLY
+ : msg->error ? JSONRPC_ERROR
+ : msg->id ? JSONRPC_REQUEST
+ : JSONRPC_NOTIFY);
+ if (!shash_is_empty(object)) {
+ error = xasprintf("message has unexpected member \"%s\"",
+ shash_first(object)->name);
+ goto exit;
+ }
+ error = jsonrpc_msg_is_valid(msg);
+ if (error) {
+ goto exit;
+ }
+
+exit:
+ json_destroy(method);
+ json_destroy(json);
+ if (error) {
+ jsonrpc_msg_destroy(msg);
+ msg = NULL;
+ }
+ *msgp = msg;
+ return error;
+}
+
+struct json *
+jsonrpc_msg_to_json(struct jsonrpc_msg *m)
+{
+ struct json *json = json_object_create();
+
+ if (m->method) {
+ json_object_put(json, "method", json_string_create_nocopy(m->method));
+ }
+
+ if (m->params) {
+ json_object_put(json, "params", m->params);
+ }
+
+ if (m->result) {
+ json_object_put(json, "result", m->result);
+ } else if (m->type == JSONRPC_ERROR) {
+ json_object_put(json, "result", json_null_create());
+ }
+
+ if (m->error) {
+ json_object_put(json, "error", m->error);
+ } else if (m->type == JSONRPC_REPLY) {
+ json_object_put(json, "error", json_null_create());
+ }
+
+ if (m->id) {
+ json_object_put(json, "id", m->id);
+ } else if (m->type == JSONRPC_NOTIFY) {
+ json_object_put(json, "id", json_null_create());
+ }
+
+ free(m);
+
+ return json;
+}
+\f
+/* A JSON-RPC session with reconnection. */
+
+struct jsonrpc_session {
+ struct reconnect *reconnect;
+ struct jsonrpc *rpc;
+ struct stream *stream;
+ unsigned int seqno;
+};
+
+struct jsonrpc_session *
+jsonrpc_session_open(const char *name)
+{
+ struct jsonrpc_session *s;
+
+ s = xmalloc(sizeof *s);
+ s->reconnect = reconnect_create(time_msec());
+ reconnect_set_name(s->reconnect, name);
+ reconnect_enable(s->reconnect, time_msec());
+ s->rpc = NULL;
+ s->stream = NULL;
+ s->seqno = 0;
+
+ return s;
+}
+
+void
+jsonrpc_session_close(struct jsonrpc_session *s)
+{
+ if (s) {
+ jsonrpc_close(s->rpc);
+ reconnect_destroy(s->reconnect);
+ free(s);
+ }
+}
+
+static void
+jsonrpc_session_disconnect(struct jsonrpc_session *s)
+{
+ reconnect_disconnected(s->reconnect, time_msec(), 0);
+ if (s->rpc) {
+ jsonrpc_error(s->rpc, EOF);
+ jsonrpc_close(s->rpc);
+ s->rpc = NULL;
+ s->seqno++;
+ } else if (s->stream) {
+ stream_close(s->stream);
+ s->stream = NULL;
+ s->seqno++;
+ }
+}
+
+static void
+jsonrpc_session_connect(struct jsonrpc_session *s)
+{
+ int error;
+
+ jsonrpc_session_disconnect(s);
+ error = stream_open(reconnect_get_name(s->reconnect), &s->stream);
+ if (error) {
+ reconnect_connect_failed(s->reconnect, time_msec(), error);
+ } else {
+ reconnect_connecting(s->reconnect, time_msec());
+ }
+ s->seqno++;
+}
+
+void
+jsonrpc_session_run(struct jsonrpc_session *s)
+{
+ if (s->rpc) {
+ int error;
+
+ jsonrpc_run(s->rpc);
+ error = jsonrpc_get_status(s->rpc);
+ if (error) {
+ jsonrpc_session_disconnect(s);
+ }
+ } else if (s->stream) {
+ int error = stream_connect(s->stream);
+ if (!error) {
+ reconnect_connected(s->reconnect, time_msec());
+ s->rpc = jsonrpc_open(s->stream);
+ s->stream = NULL;
+ } else if (error != EAGAIN) {
+ reconnect_connect_failed(s->reconnect, time_msec(), error);
+ stream_close(s->stream);
+ s->stream = NULL;
+ }
+ }
+
+ switch (reconnect_run(s->reconnect, time_msec())) {
+ case RECONNECT_CONNECT:
+ jsonrpc_session_connect(s);
+ break;
+
+ case RECONNECT_DISCONNECT:
+ jsonrpc_session_disconnect(s);
+ break;
+
+ case RECONNECT_PROBE:
+ if (s->rpc) {
+ struct json *params;
+ struct jsonrpc_msg *request;
+
+ params = json_array_create_empty();
+ request = jsonrpc_create_request("echo", params, NULL);
+ json_destroy(request->id);
+ request->id = json_string_create("echo");
+ jsonrpc_send(s->rpc, request);
+ }
+ break;
+ }
+}
+
+void
+jsonrpc_session_wait(struct jsonrpc_session *s)
+{
+ if (s->rpc) {
+ jsonrpc_wait(s->rpc);
+ } else if (s->stream) {
+ stream_connect_wait(s->stream);
+ }
+ reconnect_wait(s->reconnect, time_msec());
+}
+
+size_t
+jsonrpc_session_get_backlog(const struct jsonrpc_session *s)
+{
+ return s->rpc ? jsonrpc_get_backlog(s->rpc) : 0;
+}
+
+const char *
+jsonrpc_session_get_name(const struct jsonrpc_session *s)
+{
+ return reconnect_get_name(s->reconnect);
+}
+
+int
+jsonrpc_session_send(struct jsonrpc_session *s, struct jsonrpc_msg *msg)
+{
+ return s->rpc ? jsonrpc_send(s->rpc, msg) : ENOTCONN;
+}
+
+struct jsonrpc_msg *
+jsonrpc_session_recv(struct jsonrpc_session *s)
+{
+ struct jsonrpc_msg *msg = NULL;
+ if (s->rpc) {
+ jsonrpc_recv(s->rpc, &msg);
+ if (msg) {
+ reconnect_received(s->reconnect, time_msec());
+ }
+ }
+ return msg;
+}
+
+void
+jsonrpc_session_recv_wait(struct jsonrpc_session *s)
+{
+ if (s->rpc) {
+ jsonrpc_recv_wait(s->rpc);
+ }
+}
+
+bool
+jsonrpc_session_is_connected(const struct jsonrpc_session *s)
+{
+ return s->rpc != NULL;
+}
+
+unsigned int
+jsonrpc_session_get_seqno(const struct jsonrpc_session *s)
+{
+ return s->seqno;
+}
+
+void
+jsonrpc_session_force_reconnect(struct jsonrpc_session *s)
+{
+ reconnect_force_reconnect(s->reconnect, time_msec());
+}
--- /dev/null
+/*
+ * Copyright (c) 2009 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.
+ */
+
+#ifndef JSONRPC_H
+#define JSONRPC_H 1
+
+/* This is an implementation of the JSON-RPC 1.0 specification defined at
+ * http://json-rpc.org/wiki/specification. */
+
+#include <stdbool.h>
+#include <stddef.h>
+
+struct json;
+struct jsonrpc_msg;
+struct stream;
+\f
+/* API for a JSON-RPC stream. */
+
+struct jsonrpc *jsonrpc_open(struct stream *);
+void jsonrpc_close(struct jsonrpc *);
+
+void jsonrpc_run(struct jsonrpc *);
+void jsonrpc_wait(struct jsonrpc *);
+
+void jsonrpc_error(struct jsonrpc *, int error);
+int jsonrpc_get_status(const struct jsonrpc *);
+size_t jsonrpc_get_backlog(const struct jsonrpc *);
+const char *jsonrpc_get_name(const struct jsonrpc *);
+
+int jsonrpc_send(struct jsonrpc *, struct jsonrpc_msg *);
+int jsonrpc_recv(struct jsonrpc *, struct jsonrpc_msg **);
+void jsonrpc_recv_wait(struct jsonrpc *);
+
+int jsonrpc_send_block(struct jsonrpc *, struct jsonrpc_msg *);
+int jsonrpc_recv_block(struct jsonrpc *, struct jsonrpc_msg **);
+int jsonrpc_transact_block(struct jsonrpc *, struct jsonrpc_msg *,
+ struct jsonrpc_msg **);
+
+/* Messages. */
+enum jsonrpc_msg_type {
+ JSONRPC_REQUEST, /* Request. */
+ JSONRPC_NOTIFY, /* Notification. */
+ JSONRPC_REPLY, /* Successful reply. */
+ JSONRPC_ERROR /* Error reply. */
+};
+
+struct jsonrpc_msg {
+ enum jsonrpc_msg_type type;
+ char *method; /* Request or notification only. */
+ struct json *params; /* Request or notification only. */
+ struct json *result; /* Successful reply only. */
+ struct json *error; /* Error reply only. */
+ struct json *id; /* Request or reply only. */
+};
+
+struct jsonrpc_msg *jsonrpc_create_request(const char *method,
+ struct json *params,
+ struct json **idp);
+struct jsonrpc_msg *jsonrpc_create_notify(const char *method,
+ struct json *params);
+struct jsonrpc_msg *jsonrpc_create_reply(struct json *result,
+ const struct json *id);
+struct jsonrpc_msg *jsonrpc_create_error(struct json *error,
+ const struct json *id);
+
+const char *jsonrpc_msg_type_to_string(enum jsonrpc_msg_type);
+char *jsonrpc_msg_is_valid(const struct jsonrpc_msg *);
+void jsonrpc_msg_destroy(struct jsonrpc_msg *);
+
+char *jsonrpc_msg_from_json(struct json *, struct jsonrpc_msg **);
+struct json *jsonrpc_msg_to_json(struct jsonrpc_msg *);
+\f
+/* A JSON-RPC session with reconnection. */
+
+struct jsonrpc_session *jsonrpc_session_open(const char *name);
+void jsonrpc_session_close(struct jsonrpc_session *);
+
+void jsonrpc_session_run(struct jsonrpc_session *);
+void jsonrpc_session_wait(struct jsonrpc_session *);
+
+size_t jsonrpc_session_get_backlog(const struct jsonrpc_session *);
+const char *jsonrpc_session_get_name(const struct jsonrpc_session *);
+
+int jsonrpc_session_send(struct jsonrpc_session *, struct jsonrpc_msg *);
+struct jsonrpc_msg *jsonrpc_session_recv(struct jsonrpc_session *);
+void jsonrpc_session_recv_wait(struct jsonrpc_session *);
+
+bool jsonrpc_session_is_connected(const struct jsonrpc_session *);
+unsigned int jsonrpc_session_get_seqno(const struct jsonrpc_session *);
+void jsonrpc_session_force_reconnect(struct jsonrpc_session *);
+
+#endif /* jsonrpc.h */
struct lswitch *sw;
size_t i;
- sw = xcalloc(1, sizeof *sw);
+ sw = xzalloc(sizeof *sw);
sw->max_idle = max_idle;
sw->datapath_id = 0;
sw->last_features_request = time_now() - 1;
--- /dev/null
+ /* Copyright (c) 2008, 2009 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 "lockfile.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "coverage.h"
+#include "hash.h"
+#include "hmap.h"
+#include "timeval.h"
+#include "util.h"
+
+#define THIS_MODULE VLM_lockfile
+#include "vlog.h"
+
+struct lockfile {
+ struct hmap_node hmap_node;
+ char *name;
+ dev_t device;
+ ino_t inode;
+ int fd;
+};
+
+/* Lock table.
+ *
+ * We have to do this stupid dance because POSIX says that closing *any* file
+ * descriptor for a file on which a process holds a lock drops *all* locks on
+ * that file. That means that we can't afford to open a lockfile more than
+ * once. */
+static struct hmap lock_table = HMAP_INITIALIZER(&lock_table);
+
+static void lockfile_unhash(struct lockfile *);
+static int lockfile_try_lock(const char *name, bool block,
+ struct lockfile **lockfilep);
+
+/* Returns the name of the lockfile that would be created for locking a file
+ * named 'file_name'. The caller is responsible for freeing the returned
+ * name, with free(), when it is no longer needed. */
+char *
+lockfile_name(const char *file_name)
+{
+ const char *slash = strrchr(file_name, '/');
+ return (slash
+ ? xasprintf("%.*s/.%s.~lock~",
+ (int) (slash - file_name), file_name, slash + 1)
+ : xasprintf(".%s.~lock~", file_name));
+}
+
+/* Locks the configuration file against modification by other processes and
+ * re-reads it from disk.
+ *
+ * The 'timeout' specifies the maximum number of milliseconds to wait for the
+ * config file to become free. Use 0 to avoid waiting or INT_MAX to wait
+ * forever.
+ *
+ * Returns 0 on success, otherwise a positive errno value. On success,
+ * '*lockfilep' is set to point to a new "struct lockfile *" that may be
+ * unlocked with lockfile_unlock(). On failure, '*lockfilep' is set to
+ * NULL. */
+int
+lockfile_lock(const char *file, int timeout, struct lockfile **lockfilep)
+{
+ /* Only exclusive ("write") locks are supported. This is not a problem
+ * because the Open vSwitch code that currently uses lock files does so in
+ * stylized ways such that any number of readers may access a file while it
+ * is being written. */
+ long long int start, elapsed;
+ char *lock_name;
+ int error;
+
+ COVERAGE_INC(lockfile_lock);
+
+ lock_name = lockfile_name(file);
+ time_refresh();
+ start = time_msec();
+
+ do {
+ error = lockfile_try_lock(lock_name, timeout > 0, lockfilep);
+ time_refresh();
+ elapsed = time_msec() - start;
+ } while (error == EINTR && (timeout == INT_MAX || elapsed < timeout));
+
+ if (!error) {
+ if (elapsed) {
+ VLOG_WARN("%s: waited %lld ms for lock file",
+ lock_name, elapsed);
+ }
+ } else if (error == EINTR) {
+ COVERAGE_INC(lockfile_timeout);
+ VLOG_WARN("%s: giving up on lock file after %lld ms",
+ lock_name, elapsed);
+ error = ETIMEDOUT;
+ } else {
+ COVERAGE_INC(lockfile_error);
+ if (error == EACCES) {
+ error = EAGAIN;
+ }
+ VLOG_WARN("%s: failed to lock file "
+ "(after %lld ms, with %d-ms timeout): %s",
+ lock_name, elapsed, timeout, strerror(error));
+ }
+
+ free(lock_name);
+ return error;
+}
+
+/* Unlocks 'lockfile', which must have been created by a call to
+ * lockfile_lock(), and frees 'lockfile'. */
+void
+lockfile_unlock(struct lockfile *lockfile)
+{
+ if (lockfile) {
+ COVERAGE_INC(lockfile_unlock);
+ lockfile_unhash(lockfile);
+ free(lockfile->name);
+ free(lockfile);
+ }
+}
+
+/* Marks all the currently locked lockfiles as no longer locked. It makes
+ * sense to call this function after fork(), because a child created by fork()
+ * does not hold its parents' locks. */
+void
+lockfile_postfork(void)
+{
+ struct lockfile *lockfile;
+
+ HMAP_FOR_EACH (lockfile, struct lockfile, hmap_node, &lock_table) {
+ if (lockfile->fd >= 0) {
+ VLOG_WARN("%s: child does not inherit lock", lockfile->name);
+ lockfile_unhash(lockfile);
+ }
+ }
+}
+\f
+static uint32_t
+lockfile_hash(dev_t device, ino_t inode)
+{
+ return hash_bytes(&device, sizeof device,
+ hash_bytes(&inode, sizeof inode, 0));
+}
+
+static struct lockfile *
+lockfile_find(dev_t device, ino_t inode)
+{
+ struct lockfile *lockfile;
+
+ HMAP_FOR_EACH_WITH_HASH (lockfile, struct lockfile, hmap_node,
+ lockfile_hash(device, inode), &lock_table) {
+ if (lockfile->device == device && lockfile->inode == inode) {
+ return lockfile;
+ }
+ }
+ return NULL;
+}
+
+static void
+lockfile_unhash(struct lockfile *lockfile)
+{
+ if (lockfile->fd >= 0) {
+ close(lockfile->fd);
+ lockfile->fd = -1;
+ hmap_remove(&lock_table, &lockfile->hmap_node);
+ }
+}
+
+static struct lockfile *
+lockfile_register(const char *name, dev_t device, ino_t inode, int fd)
+{
+ struct lockfile *lockfile;
+
+ lockfile = lockfile_find(device, inode);
+ if (lockfile) {
+ VLOG_ERR("%s: lock file disappeared and reappeared!", name);
+ lockfile_unhash(lockfile);
+ }
+
+ lockfile = xmalloc(sizeof *lockfile);
+ lockfile->name = xstrdup(name);
+ lockfile->device = device;
+ lockfile->inode = inode;
+ lockfile->fd = fd;
+ hmap_insert(&lock_table, &lockfile->hmap_node,
+ lockfile_hash(device, inode));
+ return lockfile;
+}
+
+static int
+lockfile_try_lock(const char *name, bool block, struct lockfile **lockfilep)
+{
+ struct flock l;
+ struct stat s;
+ int error;
+ int fd;
+
+ *lockfilep = NULL;
+
+ /* Open the lock file, first creating it if necessary. */
+ for (;;) {
+ /* Check whether we've already got a lock on that file. */
+ if (!stat(name, &s)) {
+ if (lockfile_find(s.st_dev, s.st_ino)) {
+ return EDEADLK;
+ }
+ } else if (errno != ENOENT) {
+ VLOG_WARN("%s: failed to stat lock file: %s",
+ name, strerror(errno));
+ return errno;
+ }
+
+ /* Try to open an existing lock file. */
+ fd = open(name, O_RDWR);
+ if (fd >= 0) {
+ break;
+ } else if (errno != ENOENT) {
+ VLOG_WARN("%s: failed to open lock file: %s",
+ name, strerror(errno));
+ return errno;
+ }
+
+ /* Try to create a new lock file. */
+ VLOG_INFO("%s: lock file does not exist, creating", name);
+ fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
+ if (fd >= 0) {
+ break;
+ } else if (errno != EEXIST) {
+ VLOG_WARN("%s: failed to create lock file: %s",
+ name, strerror(errno));
+ return errno;
+ }
+
+ /* Someone else created the lock file. Try again. */
+ }
+
+ /* Get the inode and device number for the lock table. */
+ if (fstat(fd, &s)) {
+ VLOG_ERR("%s: failed to fstat lock file: %s", name, strerror(errno));
+ close(fd);
+ return errno;
+ }
+
+ /* Try to lock the file. */
+ memset(&l, 0, sizeof l);
+ l.l_type = F_WRLCK;
+ l.l_whence = SEEK_SET;
+ l.l_start = 0;
+ l.l_len = 0;
+
+ time_disable_restart();
+ error = fcntl(fd, block ? F_SETLKW : F_SETLK, &l) == -1 ? errno : 0;
+ time_enable_restart();
+
+ if (!error) {
+ *lockfilep = lockfile_register(name, s.st_dev, s.st_ino, fd);
+ } else {
+ close(fd);
+ }
+ return error;
+}
+
--- /dev/null
+/* Copyright (c) 2008, 2009 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.
+ */
+
+#ifndef LOCKFILE_H
+#define LOCKFILE_H 1
+
+struct lockfile;
+
+char *lockfile_name(const char *file);
+int lockfile_lock(const char *file, int timeout, struct lockfile **);
+void lockfile_unlock(struct lockfile *);
+void lockfile_postfork(void);
+
+#endif /* lib/lockfile.h */
int error;
/* Allocate network device. */
- netdev = xcalloc(1, sizeof *netdev);
+ netdev = xzalloc(sizeof *netdev);
netdev_init(&netdev->netdev, name, &netdev_linux_class);
netdev->netdev_fd = -1;
netdev->tap_fd = -1;
--- /dev/null
+/* Copyright (c) 2009 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-data.h"
+
+#include <assert.h>
+
+#include "hash.h"
+#include "ovsdb-error.h"
+#include "json.h"
+#include "shash.h"
+#include "sort.h"
+
+static struct json *
+wrap_json(const char *name, struct json *wrapped)
+{
+ return json_array_create_2(json_string_create(name), wrapped);
+}
+
+void
+ovsdb_atom_init_default(union ovsdb_atom *atom, enum ovsdb_atomic_type type)
+{
+ switch (type) {
+ case OVSDB_TYPE_VOID:
+ NOT_REACHED();
+
+ case OVSDB_TYPE_INTEGER:
+ atom->integer = 0;
+ break;
+
+ case OVSDB_TYPE_REAL:
+ atom->real = 0.0;
+ break;
+
+ case OVSDB_TYPE_BOOLEAN:
+ atom->boolean = false;
+ break;
+
+ case OVSDB_TYPE_STRING:
+ atom->string = xmemdup("", 1);
+ break;
+
+ case OVSDB_TYPE_UUID:
+ uuid_zero(&atom->uuid);
+ break;
+
+ case OVSDB_N_TYPES:
+ default:
+ NOT_REACHED();
+ }
+}
+
+void
+ovsdb_atom_clone(union ovsdb_atom *new, const union ovsdb_atom *old,
+ enum ovsdb_atomic_type type)
+{
+ switch (type) {
+ case OVSDB_TYPE_VOID:
+ NOT_REACHED();
+
+ case OVSDB_TYPE_INTEGER:
+ new->integer = old->integer;
+ break;
+
+ case OVSDB_TYPE_REAL:
+ new->real = old->real;
+ break;
+
+ case OVSDB_TYPE_BOOLEAN:
+ new->boolean = old->boolean;
+ break;
+
+ case OVSDB_TYPE_STRING:
+ new->string = xstrdup(old->string);
+ break;
+
+ case OVSDB_TYPE_UUID:
+ new->uuid = old->uuid;
+ break;
+
+ case OVSDB_N_TYPES:
+ default:
+ NOT_REACHED();
+ }
+}
+
+void
+ovsdb_atom_swap(union ovsdb_atom *a, union ovsdb_atom *b)
+{
+ union ovsdb_atom tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+uint32_t
+ovsdb_atom_hash(const union ovsdb_atom *atom, enum ovsdb_atomic_type type,
+ uint32_t basis)
+{
+ switch (type) {
+ case OVSDB_TYPE_VOID:
+ NOT_REACHED();
+
+ case OVSDB_TYPE_INTEGER:
+ return hash_int(atom->integer, basis);
+
+ case OVSDB_TYPE_REAL:
+ return hash_double(atom->real, basis);
+
+ case OVSDB_TYPE_BOOLEAN:
+ return hash_boolean(atom->boolean, basis);
+
+ case OVSDB_TYPE_STRING:
+ return hash_string(atom->string, basis);
+
+ case OVSDB_TYPE_UUID:
+ return hash_int(uuid_hash(&atom->uuid), basis);
+
+ case OVSDB_N_TYPES:
+ default:
+ NOT_REACHED();
+ }
+}
+
+int
+ovsdb_atom_compare_3way(const union ovsdb_atom *a,
+ const union ovsdb_atom *b,
+ enum ovsdb_atomic_type type)
+{
+ switch (type) {
+ case OVSDB_TYPE_VOID:
+ NOT_REACHED();
+
+ case OVSDB_TYPE_INTEGER:
+ return a->integer < b->integer ? -1 : a->integer > b->integer;
+
+ case OVSDB_TYPE_REAL:
+ return a->real < b->real ? -1 : a->real > b->real;
+
+ case OVSDB_TYPE_BOOLEAN:
+ return a->boolean - b->boolean;
+
+ case OVSDB_TYPE_STRING:
+ return strcmp(a->string, b->string);
+
+ case OVSDB_TYPE_UUID:
+ return uuid_compare_3way(&a->uuid, &b->uuid);
+
+ case OVSDB_N_TYPES:
+ default:
+ NOT_REACHED();
+ }
+}
+
+static struct ovsdb_error *
+unwrap_json(const struct json *json, const char *name,
+ enum json_type value_type, const struct json **value)
+{
+ if (json->type != JSON_ARRAY
+ || json->u.array.n != 2
+ || json->u.array.elems[0]->type != JSON_STRING
+ || (name && strcmp(json->u.array.elems[0]->u.string, name))
+ || json->u.array.elems[1]->type != value_type)
+ {
+ return ovsdb_syntax_error(json, NULL, "expected [\"%s\", <%s>]", name,
+ json_type_to_string(value_type));
+ }
+ *value = json->u.array.elems[1];
+ return NULL;
+}
+
+static struct ovsdb_error *
+parse_json_pair(const struct json *json,
+ const struct json **elem0, const struct json **elem1)
+{
+ if (json->type != JSON_ARRAY || json->u.array.n != 2) {
+ return ovsdb_syntax_error(json, NULL, "expected 2-element array");
+ }
+ *elem0 = json->u.array.elems[0];
+ *elem1 = json->u.array.elems[1];
+ return NULL;
+}
+
+static struct ovsdb_error *
+ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json,
+ const struct ovsdb_symbol_table *symtab)
+ WARN_UNUSED_RESULT;
+
+static struct ovsdb_error *
+ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json,
+ const struct ovsdb_symbol_table *symtab)
+{
+ struct ovsdb_error *error0;
+ const struct json *value;
+
+ error0 = unwrap_json(json, "uuid", JSON_STRING, &value);
+ if (!error0) {
+ const char *uuid_string = json_string(value);
+ if (!uuid_from_string(uuid, uuid_string)) {
+ return ovsdb_syntax_error(json, NULL, "\"%s\" is not a valid UUID",
+ uuid_string);
+ }
+ } else if (symtab) {
+ struct ovsdb_error *error1;
+
+ error1 = unwrap_json(json, "named-uuid", JSON_STRING, &value);
+ if (!error1) {
+ const char *name = json_string(value);
+ const struct uuid *named_uuid;
+
+ ovsdb_error_destroy(error0);
+
+ named_uuid = ovsdb_symbol_table_get(symtab, name);
+ if (named_uuid) {
+ *uuid = *named_uuid;
+ return NULL;
+ } else {
+ return ovsdb_syntax_error(json, NULL,
+ "unknown named-uuid \"%s\"", name);
+ }
+ }
+ ovsdb_error_destroy(error1);
+ }
+
+ return error0;
+}
+
+struct ovsdb_error *
+ovsdb_atom_from_json(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
+ const struct json *json,
+ const struct ovsdb_symbol_table *symtab)
+{
+ switch (type) {
+ case OVSDB_TYPE_VOID:
+ NOT_REACHED();
+
+ case OVSDB_TYPE_INTEGER:
+ if (json->type == JSON_INTEGER) {
+ atom->integer = json->u.integer;
+ return NULL;
+ }
+ break;
+
+ case OVSDB_TYPE_REAL:
+ if (json->type == JSON_INTEGER) {
+ atom->real = json->u.integer;
+ return NULL;
+ } else if (json->type == JSON_REAL) {
+ atom->real = json->u.real;
+ return NULL;
+ }
+ break;
+
+ case OVSDB_TYPE_BOOLEAN:
+ if (json->type == JSON_TRUE) {
+ atom->boolean = true;
+ return NULL;
+ } else if (json->type == JSON_FALSE) {
+ atom->boolean = false;
+ return NULL;
+ }
+ break;
+
+ case OVSDB_TYPE_STRING:
+ if (json->type == JSON_STRING) {
+ atom->string = xstrdup(json->u.string);
+ return NULL;
+ }
+ break;
+
+ case OVSDB_TYPE_UUID:
+ return ovsdb_atom_parse_uuid(&atom->uuid, json, symtab);
+
+ case OVSDB_N_TYPES:
+ default:
+ NOT_REACHED();
+ }
+
+ return ovsdb_syntax_error(json, NULL, "expected %s",
+ ovsdb_atomic_type_to_string(type));
+}
+
+struct json *
+ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type)
+{
+ switch (type) {
+ case OVSDB_TYPE_VOID:
+ NOT_REACHED();
+
+ case OVSDB_TYPE_INTEGER:
+ return json_integer_create(atom->integer);
+
+ case OVSDB_TYPE_REAL:
+ return json_real_create(atom->real);
+
+ case OVSDB_TYPE_BOOLEAN:
+ return json_boolean_create(atom->boolean);
+
+ case OVSDB_TYPE_STRING:
+ return json_string_create(atom->string);
+
+ case OVSDB_TYPE_UUID:
+ return wrap_json("uuid", json_string_create_nocopy(
+ xasprintf(UUID_FMT, UUID_ARGS(&atom->uuid))));
+
+ case OVSDB_N_TYPES:
+ default:
+ NOT_REACHED();
+ }
+}
+\f
+static union ovsdb_atom *
+alloc_default_atoms(enum ovsdb_atomic_type type, size_t n)
+{
+ if (type != OVSDB_TYPE_VOID && n) {
+ union ovsdb_atom *atoms;
+ unsigned int i;
+
+ atoms = xmalloc(n * sizeof *atoms);
+ for (i = 0; i < n; i++) {
+ ovsdb_atom_init_default(&atoms[i], type);
+ }
+ return atoms;
+ } else {
+ /* Avoid wasting memory in the n == 0 case, because xmalloc(0) is
+ * treated as xmalloc(1). */
+ return NULL;
+ }
+}
+
+void
+ovsdb_datum_init_default(struct ovsdb_datum *datum,
+ const struct ovsdb_type *type)
+{
+ datum->n = type->n_min;
+ datum->keys = alloc_default_atoms(type->key_type, datum->n);
+ datum->values = alloc_default_atoms(type->value_type, datum->n);
+}
+
+static union ovsdb_atom *
+clone_atoms(const union ovsdb_atom *old, enum ovsdb_atomic_type type, size_t n)
+{
+ if (type != OVSDB_TYPE_VOID && n) {
+ union ovsdb_atom *new;
+ unsigned int i;
+
+ new = xmalloc(n * sizeof *new);
+ for (i = 0; i < n; i++) {
+ ovsdb_atom_clone(&new[i], &old[i], type);
+ }
+ return new;
+ } else {
+ /* Avoid wasting memory in the n == 0 case, because xmalloc(0) is
+ * treated as xmalloc(1). */
+ return NULL;
+ }
+}
+
+void
+ovsdb_datum_clone(struct ovsdb_datum *new, const struct ovsdb_datum *old,
+ const struct ovsdb_type *type)
+{
+ unsigned int n = old->n;
+ new->n = n;
+ new->keys = clone_atoms(old->keys, type->key_type, n);
+ new->values = clone_atoms(old->values, type->value_type, n);
+}
+
+static void
+free_data(enum ovsdb_atomic_type type,
+ union ovsdb_atom *atoms, size_t n_atoms)
+{
+ if (ovsdb_atom_needs_destruction(type)) {
+ unsigned int i;
+ for (i = 0; i < n_atoms; i++) {
+ ovsdb_atom_destroy(&atoms[i], type);
+ }
+ }
+ free(atoms);
+}
+
+void
+ovsdb_datum_destroy(struct ovsdb_datum *datum, const struct ovsdb_type *type)
+{
+ free_data(type->key_type, datum->keys, datum->n);
+ free_data(type->value_type, datum->values, datum->n);
+}
+
+void
+ovsdb_datum_swap(struct ovsdb_datum *a, struct ovsdb_datum *b)
+{
+ struct ovsdb_datum tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+struct ovsdb_datum_sort_cbdata {
+ const struct ovsdb_type *type;
+ struct ovsdb_datum *datum;
+};
+
+static int
+ovsdb_datum_sort_compare_cb(size_t a, size_t b, void *cbdata_)
+{
+ struct ovsdb_datum_sort_cbdata *cbdata = cbdata_;
+
+ return ovsdb_atom_compare_3way(&cbdata->datum->keys[a],
+ &cbdata->datum->keys[b],
+ cbdata->type->key_type);
+}
+
+static void
+ovsdb_datum_sort_swap_cb(size_t a, size_t b, void *cbdata_)
+{
+ struct ovsdb_datum_sort_cbdata *cbdata = cbdata_;
+
+ ovsdb_atom_swap(&cbdata->datum->keys[a], &cbdata->datum->keys[b]);
+ if (cbdata->type->value_type != OVSDB_TYPE_VOID) {
+ ovsdb_atom_swap(&cbdata->datum->values[a], &cbdata->datum->values[b]);
+ }
+}
+
+static struct ovsdb_error *
+ovsdb_datum_sort(struct ovsdb_datum *datum, const struct ovsdb_type *type)
+{
+ if (datum->n < 2) {
+ return NULL;
+ } else {
+ struct ovsdb_datum_sort_cbdata cbdata;
+ size_t i;
+
+ cbdata.type = type;
+ cbdata.datum = datum;
+ sort(datum->n, ovsdb_datum_sort_compare_cb, ovsdb_datum_sort_swap_cb,
+ &cbdata);
+
+ for (i = 0; i < datum->n - 1; i++) {
+ if (ovsdb_atom_equals(&datum->keys[i], &datum->keys[i + 1],
+ type->key_type)) {
+ if (ovsdb_type_is_map(type)) {
+ return ovsdb_error(NULL, "map contains duplicate key");
+ } else {
+ return ovsdb_error(NULL, "set contains duplicate");
+ }
+ }
+ }
+
+ return NULL;
+ }
+}
+
+struct ovsdb_error *
+ovsdb_datum_from_json(struct ovsdb_datum *datum,
+ const struct ovsdb_type *type,
+ const struct json *json,
+ const struct ovsdb_symbol_table *symtab)
+{
+ struct ovsdb_error *error;
+
+ if (ovsdb_type_is_scalar(type)) {
+ datum->n = 1;
+ datum->keys = xmalloc(sizeof *datum->keys);
+ datum->values = NULL;
+
+ error = ovsdb_atom_from_json(&datum->keys[0], type->key_type,
+ json, symtab);
+ if (error) {
+ free(datum->keys);
+ }
+ return error;
+ } else {
+ bool is_map = ovsdb_type_is_map(type);
+ const char *class = is_map ? "map" : "set";
+ const struct json *inner;
+ unsigned int i;
+ size_t n;
+
+ assert(is_map || ovsdb_type_is_set(type));
+
+ error = unwrap_json(json, class, JSON_ARRAY, &inner);
+ if (error) {
+ return error;
+ }
+
+ n = inner->u.array.n;
+ if (n < type->n_min || n > type->n_max) {
+ return ovsdb_syntax_error(json, NULL, "%s must have %u to "
+ "%u members but %zu are present",
+ class, type->n_min, type->n_max, n);
+ }
+
+ datum->n = 0;
+ datum->keys = xmalloc(n * sizeof *datum->keys);
+ datum->values = is_map ? xmalloc(n * sizeof *datum->values) : NULL;
+ for (i = 0; i < n; i++) {
+ const struct json *element = inner->u.array.elems[i];
+ const struct json *key = NULL;
+ const struct json *value = NULL;
+
+ if (!is_map) {
+ key = element;
+ } else {
+ error = parse_json_pair(element, &key, &value);
+ if (error) {
+ goto error;
+ }
+ }
+
+ error = ovsdb_atom_from_json(&datum->keys[i], type->key_type,
+ key, symtab);
+ if (error) {
+ goto error;
+ }
+
+ if (is_map) {
+ error = ovsdb_atom_from_json(&datum->values[i],
+ type->value_type, value, symtab);
+ if (error) {
+ ovsdb_atom_destroy(&datum->keys[i], type->key_type);
+ goto error;
+ }
+ }
+
+ datum->n++;
+ }
+
+ error = ovsdb_datum_sort(datum, type);
+ if (error) {
+ goto error;
+ }
+
+ return NULL;
+
+ error:
+ ovsdb_datum_destroy(datum, type);
+ return error;
+ }
+}
+
+struct json *
+ovsdb_datum_to_json(const struct ovsdb_datum *datum,
+ const struct ovsdb_type *type)
+{
+ /* These tests somewhat tolerate a 'datum' that does not exactly match
+ * 'type', in particular a datum with 'n' not in the allowed range. */
+ if (datum->n == 1 && ovsdb_type_is_scalar(type)) {
+ return ovsdb_atom_to_json(&datum->keys[0], type->key_type);
+ } else if (type->value_type == OVSDB_TYPE_VOID) {
+ struct json **elems;
+ size_t i;
+
+ elems = xmalloc(datum->n * sizeof *elems);
+ for (i = 0; i < datum->n; i++) {
+ elems[i] = ovsdb_atom_to_json(&datum->keys[i], type->key_type);
+ }
+
+ return wrap_json("set", json_array_create(elems, datum->n));
+ } else {
+ struct json **elems;
+ size_t i;
+
+ elems = xmalloc(datum->n * sizeof *elems);
+ for (i = 0; i < datum->n; i++) {
+ elems[i] = json_array_create_2(
+ ovsdb_atom_to_json(&datum->keys[i], type->key_type),
+ ovsdb_atom_to_json(&datum->values[i], type->value_type));
+ }
+
+ return wrap_json("map", json_array_create(elems, datum->n));
+ }
+}
+
+static uint32_t
+hash_atoms(enum ovsdb_atomic_type type, const union ovsdb_atom *atoms,
+ unsigned int n, uint32_t basis)
+{
+ if (type != OVSDB_TYPE_VOID) {
+ unsigned int i;
+
+ for (i = 0; i < n; i++) {
+ basis = ovsdb_atom_hash(&atoms[i], type, basis);
+ }
+ }
+ return basis;
+}
+
+uint32_t
+ovsdb_datum_hash(const struct ovsdb_datum *datum,
+ const struct ovsdb_type *type, uint32_t basis)
+{
+ basis = hash_atoms(type->key_type, datum->keys, datum->n, basis);
+ basis ^= (type->key_type << 24) | (type->value_type << 16) | datum->n;
+ basis = hash_atoms(type->value_type, datum->values, datum->n, basis);
+ return basis;
+}
+
+static int
+atom_arrays_compare_3way(const union ovsdb_atom *a,
+ const union ovsdb_atom *b,
+ enum ovsdb_atomic_type type,
+ size_t n)
+{
+ unsigned int i;
+
+ for (i = 0; i < n; i++) {
+ int cmp = ovsdb_atom_compare_3way(&a[i], &b[i], type);
+ if (cmp) {
+ return cmp;
+ }
+ }
+
+ return 0;
+}
+
+bool
+ovsdb_datum_equals(const struct ovsdb_datum *a,
+ const struct ovsdb_datum *b,
+ const struct ovsdb_type *type)
+{
+ return !ovsdb_datum_compare_3way(a, b, type);
+}
+
+int
+ovsdb_datum_compare_3way(const struct ovsdb_datum *a,
+ const struct ovsdb_datum *b,
+ const struct ovsdb_type *type)
+{
+ int cmp;
+
+ if (a->n != b->n) {
+ return a->n < b->n ? -1 : 1;
+ }
+
+ cmp = atom_arrays_compare_3way(a->keys, b->keys, type->key_type, a->n);
+ if (cmp) {
+ return cmp;
+ }
+
+ return (type->value_type == OVSDB_TYPE_VOID ? 0
+ : atom_arrays_compare_3way(a->values, b->values, type->value_type,
+ a->n));
+}
+
+static bool
+ovsdb_datum_contains(const struct ovsdb_datum *a, int i,
+ const struct ovsdb_datum *b,
+ const struct ovsdb_type *type)
+{
+ int low = 0;
+ int high = b->n;
+ while (low < high) {
+ int j = (low + high) / 2;
+ int cmp = ovsdb_atom_compare_3way(&a->keys[i], &b->keys[j], type->key_type);
+ if (cmp < 0) {
+ high = j;
+ } else if (cmp > 0) {
+ low = j + 1;
+ } else {
+ return (type->value_type == OVSDB_TYPE_VOID
+ || ovsdb_atom_equals(&a->values[i], &b->values[j],
+ type->value_type));
+ }
+ }
+ return false;
+}
+
+/* Returns true if every element in 'a' is also in 'b', false otherwise. */
+bool
+ovsdb_datum_includes_all(const struct ovsdb_datum *a,
+ const struct ovsdb_datum *b,
+ const struct ovsdb_type *type)
+{
+ size_t i;
+
+ for (i = 0; i < a->n; i++) {
+ if (!ovsdb_datum_contains(a, i, b, type)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/* Returns true if no element in 'a' is also in 'b', false otherwise. */
+bool
+ovsdb_datum_excludes_all(const struct ovsdb_datum *a,
+ const struct ovsdb_datum *b,
+ const struct ovsdb_type *type)
+{
+ size_t i;
+
+ for (i = 0; i < a->n; i++) {
+ if (ovsdb_datum_contains(a, i, b, type)) {
+ return false;
+ }
+ }
+ return true;
+}
+\f
+struct ovsdb_symbol_table {
+ struct shash sh;
+};
+
+struct ovsdb_symbol_table *
+ovsdb_symbol_table_create(void)
+{
+ struct ovsdb_symbol_table *symtab = xmalloc(sizeof *symtab);
+ shash_init(&symtab->sh);
+ return symtab;
+}
+
+void
+ovsdb_symbol_table_destroy(struct ovsdb_symbol_table *symtab)
+{
+ if (symtab) {
+ struct shash_node *node, *next;
+
+ SHASH_FOR_EACH_SAFE (node, next, &symtab->sh) {
+ free(node->data);
+ shash_delete(&symtab->sh, node);
+ }
+ shash_destroy(&symtab->sh);
+ free(symtab);
+ }
+}
+
+const struct uuid *
+ovsdb_symbol_table_get(const struct ovsdb_symbol_table *symtab,
+ const char *name)
+{
+ return shash_find_data(&symtab->sh, name);
+}
+
+void
+ovsdb_symbol_table_put(struct ovsdb_symbol_table *symtab, const char *name,
+ const struct uuid *uuid)
+{
+ struct uuid *entry = shash_find_data(&symtab->sh, name);
+ if (!entry) {
+ shash_add(&symtab->sh, name, xmemdup(uuid, sizeof *uuid));
+ } else {
+ *entry = *uuid;
+ }
+}
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef OVSDB_DATA_H
+#define OVSDB_DATA_H 1
+
+#include <stdlib.h>
+#include "compiler.h"
+#include "ovsdb-types.h"
+
+struct ovsdb_symbol_table;
+
+/* One value of an atomic type (given by enum ovs_atomic_type). */
+union ovsdb_atom {
+ int64_t integer;
+ double real;
+ bool boolean;
+ char *string;
+ struct uuid uuid;
+};
+
+void ovsdb_atom_init_default(union ovsdb_atom *, enum ovsdb_atomic_type);
+void ovsdb_atom_clone(union ovsdb_atom *, const union ovsdb_atom *,
+ enum ovsdb_atomic_type);
+void ovsdb_atom_swap(union ovsdb_atom *, union ovsdb_atom *);
+
+static inline bool
+ovsdb_atom_needs_destruction(enum ovsdb_atomic_type type)
+{
+ return type == OVSDB_TYPE_STRING;
+}
+
+static inline void
+ovsdb_atom_destroy(union ovsdb_atom *atom, enum ovsdb_atomic_type type)
+{
+ if (type == OVSDB_TYPE_STRING) {
+ free(atom->string);
+ }
+}
+
+uint32_t ovsdb_atom_hash(const union ovsdb_atom *, enum ovsdb_atomic_type,
+ uint32_t basis);
+
+int ovsdb_atom_compare_3way(const union ovsdb_atom *,
+ const union ovsdb_atom *,
+ enum ovsdb_atomic_type);
+
+static inline bool ovsdb_atom_equals(const union ovsdb_atom *a,
+ const union ovsdb_atom *b,
+ enum ovsdb_atomic_type type)
+{
+ return !ovsdb_atom_compare_3way(a, b, type);
+}
+
+struct ovsdb_error *ovsdb_atom_from_json(union ovsdb_atom *,
+ enum ovsdb_atomic_type,
+ const struct json *,
+ const struct ovsdb_symbol_table *)
+ WARN_UNUSED_RESULT;
+struct json *ovsdb_atom_to_json(const union ovsdb_atom *,
+ enum ovsdb_atomic_type);
+\f
+/* One value of an OVSDB type (given by struct ovsdb_type). */
+struct ovsdb_datum {
+ unsigned int n; /* Number of 'keys' and 'values'. */
+ union ovsdb_atom *keys; /* Each of the ovsdb_type's 'key_type'. */
+ union ovsdb_atom *values; /* Each of the ovsdb_type's 'value_type'. */
+};
+
+void ovsdb_datum_init_default(struct ovsdb_datum *, const struct ovsdb_type *);
+void ovsdb_datum_clone(struct ovsdb_datum *, const struct ovsdb_datum *,
+ const struct ovsdb_type *);
+void ovsdb_datum_destroy(struct ovsdb_datum *, const struct ovsdb_type *);
+void ovsdb_datum_swap(struct ovsdb_datum *, struct ovsdb_datum *);
+
+struct ovsdb_error *ovsdb_datum_from_json(struct ovsdb_datum *,
+ const struct ovsdb_type *,
+ const struct json *,
+ const struct ovsdb_symbol_table *)
+ WARN_UNUSED_RESULT;
+struct json *ovsdb_datum_to_json(const struct ovsdb_datum *,
+ const struct ovsdb_type *);
+
+uint32_t ovsdb_datum_hash(const struct ovsdb_datum *,
+ const struct ovsdb_type *, uint32_t basis);
+int ovsdb_datum_compare_3way(const struct ovsdb_datum *,
+ const struct ovsdb_datum *,
+ const struct ovsdb_type *);
+bool ovsdb_datum_equals(const struct ovsdb_datum *,
+ const struct ovsdb_datum *,
+ const struct ovsdb_type *);
+bool ovsdb_datum_includes_all(const struct ovsdb_datum *,
+ const struct ovsdb_datum *,
+ const struct ovsdb_type *);
+bool ovsdb_datum_excludes_all(const struct ovsdb_datum *,
+ const struct ovsdb_datum *,
+ const struct ovsdb_type *);
+
+static inline bool
+ovsdb_datum_conforms_to_type(const struct ovsdb_datum *datum,
+ const struct ovsdb_type *type)
+{
+ return datum->n >= type->n_min && datum->n <= type->n_max;
+}
+\f
+/* A table mapping from names to data items. Currently the data items are
+ * always UUIDs; perhaps this will be expanded in the future. */
+
+struct ovsdb_symbol_table *ovsdb_symbol_table_create(void);
+void ovsdb_symbol_table_destroy(struct ovsdb_symbol_table *);
+const struct uuid *ovsdb_symbol_table_get(const struct ovsdb_symbol_table *,
+ const char *name);
+void ovsdb_symbol_table_put(struct ovsdb_symbol_table *, const char *name,
+ const struct uuid *);
+
+#endif /* ovsdb-data.h */
--- /dev/null
+/* Copyright (c) 2009 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-error.h"
+
+#include <inttypes.h>
+
+#include "backtrace.h"
+#include "dynamic-string.h"
+#include "json.h"
+#include "util.h"
+
+struct ovsdb_error {
+ const char *tag; /* String for "error" member. */
+ char *details; /* String for "details" member. */
+ char *syntax; /* String for "syntax" member. */
+ int errno_; /* Unix errno value, 0 if none. */
+};
+
+static struct ovsdb_error *
+ovsdb_error_valist(const char *tag, const char *details, va_list args)
+{
+ struct ovsdb_error *error = xmalloc(sizeof *error);
+ error->tag = tag ? tag : "ovsdb error";
+ error->details = details ? xvasprintf(details, args) : NULL;
+ error->syntax = NULL;
+ error->errno_ = 0;
+ return error;
+}
+
+struct ovsdb_error *
+ovsdb_error(const char *tag, const char *details, ...)
+{
+ struct ovsdb_error *error;
+ va_list args;
+
+ va_start(args, details);
+ error = ovsdb_error_valist(tag, details, args);
+ va_end(args);
+
+ return error;
+}
+
+struct ovsdb_error *
+ovsdb_io_error(int errno_, const char *details, ...)
+{
+ struct ovsdb_error *error;
+ va_list args;
+
+ va_start(args, details);
+ error = ovsdb_error_valist("I/O error", details, args);
+ va_end(args);
+
+ error->errno_ = errno_;
+
+ return error;
+}
+
+struct ovsdb_error *
+ovsdb_syntax_error(const struct json *json, const char *tag,
+ const char *details, ...)
+{
+ struct ovsdb_error *error;
+ va_list args;
+
+ va_start(args, details);
+ error = ovsdb_error_valist(tag ? tag : "syntax error", details, args);
+ va_end(args);
+
+ if (json) {
+ /* XXX this is much too much information in some cases */
+ error->syntax = json_to_string(json, 0);
+ }
+
+ return error;
+}
+
+struct ovsdb_error *
+ovsdb_wrap_error(struct ovsdb_error *error, const char *details, ...)
+{
+ va_list args;
+ char *msg;
+
+ va_start(args, details);
+ msg = xvasprintf(details, args);
+ va_end(args);
+
+ if (error->details) {
+ char *new = xasprintf("%s: %s", msg, error->details);
+ free(error->details);
+ error->details = new;
+ free(msg);
+ } else {
+ error->details = msg;
+ }
+
+ return error;
+}
+
+struct ovsdb_error *
+ovsdb_internal_error(const char *file, int line, const char *details, ...)
+{
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ struct backtrace backtrace;
+ struct ovsdb_error *error;
+ va_list args;
+
+ ds_put_format(&ds, "%s:%d:", file, line);
+
+ if (details) {
+ ds_put_char(&ds, ' ');
+ va_start(args, details);
+ ds_put_format_valist(&ds, details, args);
+ va_end(args);
+ }
+
+ backtrace_capture(&backtrace);
+ if (backtrace.n_frames) {
+ int i;
+
+ ds_put_cstr(&ds, " (backtrace:");
+ for (i = 0; i < backtrace.n_frames; i++) {
+ ds_put_format(&ds, " 0x%08"PRIxPTR, backtrace.frames[i]);
+ }
+ ds_put_char(&ds, ')');
+ }
+
+ ds_put_format(&ds, " (%s %s%s)", program_name, VERSION, BUILDNR);
+
+ error = ovsdb_error("internal error", "%s", ds_cstr(&ds));
+
+ ds_destroy(&ds);
+
+ return error;
+}
+
+void
+ovsdb_error_destroy(struct ovsdb_error *error)
+{
+ if (error) {
+ free(error->details);
+ free(error->syntax);
+ free(error);
+ }
+}
+
+struct ovsdb_error *
+ovsdb_error_clone(const struct ovsdb_error *old)
+{
+ if (old) {
+ struct ovsdb_error *new = xmalloc(sizeof *new);
+ new->tag = old->tag;
+ new->details = old->details ? xstrdup(old->details) : NULL;
+ new->syntax = old->syntax ? xstrdup(old->syntax) : NULL;
+ new->errno_ = old->errno_;
+ return new;
+ } else {
+ return NULL;
+ }
+}
+
+static const char *
+ovsdb_errno_string(int error)
+{
+ return error == EOF ? "unexpected end of file" : strerror(error);
+}
+
+struct json *
+ovsdb_error_to_json(const struct ovsdb_error *error)
+{
+ struct json *json = json_object_create();
+ json_object_put_string(json, "error", error->tag);
+ if (error->details) {
+ json_object_put_string(json, "details", error->details);
+ }
+ if (error->syntax) {
+ json_object_put_string(json, "syntax", error->syntax);
+ }
+ if (error->errno_) {
+ json_object_put_string(json, "io-error",
+ ovsdb_errno_string(error->errno_));
+ }
+ return json;
+}
+
+char *
+ovsdb_error_to_string(const struct ovsdb_error *error)
+{
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ if (error->syntax) {
+ ds_put_format(&ds, "syntax \"%s\": ", error->syntax);
+ }
+ ds_put_cstr(&ds, error->tag);
+ if (error->details) {
+ ds_put_format(&ds, ": %s", error->details);
+ }
+ if (error->errno_) {
+ ds_put_format(&ds, " (%s)", ovsdb_errno_string(error->errno_));
+ }
+ return ds_steal_cstr(&ds);
+}
+
+const char *
+ovsdb_error_get_tag(const struct ovsdb_error *error)
+{
+ return error->tag;
+}
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef OVSDB_ERROR_H
+#define OVSDB_ERROR_H 1
+
+#include "compiler.h"
+
+struct json;
+
+struct ovsdb_error *ovsdb_error(const char *tag, const char *details, ...)
+ PRINTF_FORMAT(2, 3)
+ WARN_UNUSED_RESULT;
+struct ovsdb_error *ovsdb_io_error(int error, const char *details, ...)
+ PRINTF_FORMAT(2, 3)
+ WARN_UNUSED_RESULT;
+struct ovsdb_error *ovsdb_syntax_error(const struct json *, const char *tag,
+ const char *details, ...)
+ PRINTF_FORMAT(3, 4)
+ WARN_UNUSED_RESULT;
+
+struct ovsdb_error *ovsdb_wrap_error(struct ovsdb_error *error,
+ const char *details, ...)
+ PRINTF_FORMAT(2, 3);
+
+struct ovsdb_error *ovsdb_internal_error(const char *file, int line,
+ const char *details, ...)
+ PRINTF_FORMAT(3, 4)
+ WARN_UNUSED_RESULT;
+#define OVSDB_BUG(MSG) ovsdb_internal_error(__FILE__, __LINE__, "%s", MSG)
+
+void ovsdb_error_destroy(struct ovsdb_error *);
+struct ovsdb_error *ovsdb_error_clone(const struct ovsdb_error *)
+ WARN_UNUSED_RESULT;
+
+char *ovsdb_error_to_string(const struct ovsdb_error *);
+struct json *ovsdb_error_to_json(const struct ovsdb_error *);
+
+const char *ovsdb_error_get_tag(const struct ovsdb_error *);
+
+#endif /* ovsdb-error.h */
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef OVSDB_IDL_PROVIDER_H
+#define OVSDB_IDL_PROVIDER_H 1
+
+#include "hmap.h"
+#include "list.h"
+#include "ovsdb-idl.h"
+#include "ovsdb-types.h"
+#include "shash.h"
+#include "uuid.h"
+
+struct ovsdb_idl_row {
+ struct hmap_node hmap_node; /* In struct ovsdb_idl_table's 'rows'. */
+ struct uuid uuid; /* Row "_uuid" field. */
+ struct list src_arcs; /* Forward arcs (ovsdb_idl_arc.src_node). */
+ struct list dst_arcs; /* Backward arcs (ovsdb_idl_arc.dst_node). */
+ struct ovsdb_idl_table *table; /* Containing table. */
+ struct ovsdb_datum *fields; /* Row data, or null if orphaned. */
+};
+
+struct ovsdb_idl_column {
+ char *name;
+ struct ovsdb_type type;
+};
+
+struct ovsdb_idl_table_class {
+ char *name;
+ const struct ovsdb_idl_column *columns;
+ size_t n_columns;
+ size_t allocation_size;
+ void (*parse)(struct ovsdb_idl_row *);
+ void (*unparse)(struct ovsdb_idl_row *);
+};
+
+struct ovsdb_idl_table {
+ const struct ovsdb_idl_table_class *class;
+ struct shash columns; /* Contains "const struct ovsdb_idl_column *"s. */
+ struct hmap rows; /* Contains "struct ovsdb_idl_row"s. */
+ struct ovsdb_idl *idl; /* Containing idl. */
+};
+
+struct ovsdb_idl_class {
+ const struct ovsdb_idl_table_class *tables;
+ size_t n_tables;
+};
+
+struct ovsdb_idl_row *ovsdb_idl_get_row_arc(
+ struct ovsdb_idl_row *src,
+ struct ovsdb_idl_table_class *dst_table,
+ const struct uuid *dst_uuid);
+
+struct ovsdb_idl_row *ovsdb_idl_first_row(
+ const struct ovsdb_idl *, const struct ovsdb_idl_table_class *);
+
+struct ovsdb_idl_row *ovsdb_idl_next_row(const struct ovsdb_idl_row *);
+
+#endif /* ovsdb-idl-provider.h */
--- /dev/null
+/* Copyright (c) 2009 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-idl.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "json.h"
+#include "jsonrpc.h"
+#include "ovsdb-data.h"
+#include "ovsdb-error.h"
+#include "ovsdb-idl-provider.h"
+#include "shash.h"
+#include "util.h"
+
+#define THIS_MODULE VLM_ovsdb_idl
+#include "vlog.h"
+
+/* An arc from one idl_row to another. When row A contains a UUID that
+ * references row B, this is represented by an arc from A (the source) to B
+ * (the destination).
+ *
+ * Arcs from a row to itself are omitted, that is, src and dst are always
+ * different.
+ *
+ * Arcs are never duplicated, that is, even if there are multiple references
+ * from A to B, there is only a single arc from A to B.
+ *
+ * Arcs are directed: an arc from A to B is the converse of an an arc from B to
+ * A. Both an arc and its converse may both be present, if each row refers
+ * to the other circularly.
+ *
+ * The source and destination row may be in the same table or in different
+ * tables.
+ */
+struct ovsdb_idl_arc {
+ struct list src_node; /* In src->src_arcs list. */
+ struct list dst_node; /* In dst->dst_arcs list. */
+ struct ovsdb_idl_row *src; /* Source row. */
+ struct ovsdb_idl_row *dst; /* Destination row. */
+};
+
+struct ovsdb_idl {
+ struct jsonrpc_session *session;
+ struct shash tables;
+ struct json *monitor_request_id;
+ unsigned int last_monitor_request_seqno;
+ unsigned int change_seqno;
+};
+
+static struct vlog_rate_limit syntax_rl = VLOG_RATE_LIMIT_INIT(1, 5);
+static struct vlog_rate_limit semantic_rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+static void ovsdb_idl_clear(struct ovsdb_idl *);
+static void ovsdb_idl_send_monitor_request(struct ovsdb_idl *);
+static void ovsdb_idl_parse_update(struct ovsdb_idl *, const struct json *);
+static struct ovsdb_error *ovsdb_idl_parse_update__(struct ovsdb_idl *,
+ const struct json *);
+static void ovsdb_idl_process_update(struct ovsdb_idl_table *,
+ const struct uuid *,
+ const struct json *old,
+ const struct json *new);
+static void ovsdb_idl_insert_row(struct ovsdb_idl_row *, const struct json *);
+static void ovsdb_idl_delete_row(struct ovsdb_idl_row *);
+static void ovsdb_idl_modify_row(struct ovsdb_idl_row *, const struct json *);
+
+static bool ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *);
+static struct ovsdb_idl_row *ovsdb_idl_row_create(struct ovsdb_idl_table *,
+ const struct uuid *);
+static void ovsdb_idl_row_destroy(struct ovsdb_idl_row *);
+
+static void ovsdb_idl_row_clear_fields(struct ovsdb_idl_row *);
+
+struct ovsdb_idl *
+ovsdb_idl_create(const char *remote, const struct ovsdb_idl_class *class)
+{
+ struct ovsdb_idl *idl;
+ size_t i;
+
+ idl = xzalloc(sizeof *idl);
+ idl->session = jsonrpc_session_open(remote);
+ shash_init(&idl->tables);
+ for (i = 0; i < class->n_tables; i++) {
+ const struct ovsdb_idl_table_class *tc = &class->tables[i];
+ struct ovsdb_idl_table *table;
+ size_t j;
+
+ table = xmalloc(sizeof *table);
+ assert(!shash_find(&idl->tables, tc->name));
+ shash_add(&idl->tables, tc->name, table);
+ table->class = tc;
+ shash_init(&table->columns);
+ for (j = 0; j < tc->n_columns; j++) {
+ const struct ovsdb_idl_column *column = &tc->columns[j];
+
+ assert(!shash_find(&table->columns, column->name));
+ shash_add(&table->columns, column->name, column);
+ }
+ hmap_init(&table->rows);
+ table->idl = idl;
+ }
+ idl->last_monitor_request_seqno = UINT_MAX;
+
+ return idl;
+}
+
+void
+ovsdb_idl_destroy(struct ovsdb_idl *idl)
+{
+ if (idl) {
+ struct shash_node *node;
+
+ ovsdb_idl_clear(idl);
+ jsonrpc_session_close(idl->session);
+
+ SHASH_FOR_EACH (node, &idl->tables) {
+ struct ovsdb_idl_table *table = node->data;
+
+ shash_destroy(&table->columns);
+ hmap_destroy(&table->rows);
+ }
+ shash_destroy(&idl->tables);
+ json_destroy(idl->monitor_request_id);
+ free(idl);
+ }
+}
+
+static void
+ovsdb_idl_clear(struct ovsdb_idl *idl)
+{
+ struct shash_node *node;
+ bool changed = false;
+
+ SHASH_FOR_EACH (node, &idl->tables) {
+ struct ovsdb_idl_table *table = node->data;
+ struct ovsdb_idl_row *row, *next_row;
+
+ if (hmap_is_empty(&table->rows)) {
+ continue;
+ }
+
+ changed = true;
+ HMAP_FOR_EACH_SAFE (row, next_row, struct ovsdb_idl_row, hmap_node,
+ &table->rows) {
+ struct ovsdb_idl_arc *arc, *next_arc;
+
+ if (!ovsdb_idl_row_is_orphan(row)) {
+ (row->table->class->unparse)(row);
+ ovsdb_idl_row_clear_fields(row);
+ }
+ hmap_remove(&table->rows, &row->hmap_node);
+ LIST_FOR_EACH_SAFE (arc, next_arc, struct ovsdb_idl_arc, src_node,
+ &row->src_arcs) {
+ free(arc);
+ }
+ /* No need to do anything with dst_arcs: some node has those arcs
+ * as forward arcs and will destroy them itself. */
+
+ free(row);
+ }
+ }
+
+ if (changed) {
+ idl->change_seqno++;
+ }
+}
+
+void
+ovsdb_idl_run(struct ovsdb_idl *idl)
+{
+ int i;
+
+ jsonrpc_session_run(idl->session);
+ for (i = 0; jsonrpc_session_is_connected(idl->session) && i < 50; i++) {
+ struct jsonrpc_msg *msg, *reply;
+ unsigned int seqno;
+
+ seqno = jsonrpc_session_get_seqno(idl->session);
+ if (idl->last_monitor_request_seqno != seqno) {
+ idl->last_monitor_request_seqno = seqno;
+ ovsdb_idl_send_monitor_request(idl);
+ break;
+ }
+
+ msg = jsonrpc_session_recv(idl->session);
+ if (!msg) {
+ break;
+ }
+
+ reply = NULL;
+ if (msg->type == JSONRPC_REQUEST && !strcmp(msg->method, "echo")) {
+ reply = jsonrpc_create_reply(json_clone(msg->params), msg->id);
+ } else if (msg->type == JSONRPC_NOTIFY
+ && !strcmp(msg->method, "update")
+ && msg->params->type == JSON_ARRAY
+ && msg->params->u.array.n == 2
+ && msg->params->u.array.elems[0]->type == JSON_NULL) {
+ ovsdb_idl_parse_update(idl, msg->params->u.array.elems[1]);
+ } else if (msg->type == JSONRPC_REPLY
+ && idl->monitor_request_id
+ && json_equal(idl->monitor_request_id, msg->id)) {
+ json_destroy(idl->monitor_request_id);
+ idl->monitor_request_id = NULL;
+ ovsdb_idl_clear(idl);
+ ovsdb_idl_parse_update(idl, msg->result);
+ } else if (msg->type == JSONRPC_REPLY
+ && msg->id && msg->id->type == JSON_STRING
+ && !strcmp(msg->id->u.string, "echo")) {
+ /* It's a reply to our echo request. Ignore it. */
+ } else {
+ VLOG_WARN("%s: received unexpected %s message",
+ jsonrpc_session_get_name(idl->session),
+ jsonrpc_msg_type_to_string(msg->type));
+ jsonrpc_session_force_reconnect(idl->session);
+ }
+ if (reply) {
+ jsonrpc_session_send(idl->session, reply);
+ }
+ jsonrpc_msg_destroy(msg);
+ }
+}
+
+void
+ovsdb_idl_wait(struct ovsdb_idl *idl)
+{
+ jsonrpc_session_wait(idl->session);
+ jsonrpc_session_recv_wait(idl->session);
+}
+
+unsigned int
+ovsdb_idl_get_seqno(const struct ovsdb_idl *idl)
+{
+ return idl->change_seqno;
+}
+
+void
+ovsdb_idl_force_reconnect(struct ovsdb_idl *idl)
+{
+ jsonrpc_session_force_reconnect(idl->session);
+}
+\f
+static void
+ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl)
+{
+ struct json *monitor_requests;
+ const struct shash_node *node;
+ struct jsonrpc_msg *msg;
+
+ monitor_requests = json_object_create();
+ SHASH_FOR_EACH (node, &idl->tables) {
+ const struct ovsdb_idl_table *table = node->data;
+ const struct ovsdb_idl_table_class *tc = table->class;
+ struct json *monitor_request, *columns;
+ size_t i;
+
+ monitor_request = json_object_create();
+ columns = json_array_create_empty();
+ for (i = 0; i < tc->n_columns; i++) {
+ const struct ovsdb_idl_column *column = &tc->columns[i];
+ json_array_add(columns, json_string_create(column->name));
+ }
+ json_object_put(monitor_request, "columns", columns);
+ json_object_put(monitor_requests, tc->name, monitor_request);
+ }
+
+ json_destroy(idl->monitor_request_id);
+ msg = jsonrpc_create_request(
+ "monitor", json_array_create_2(json_null_create(), monitor_requests),
+ &idl->monitor_request_id);
+ jsonrpc_session_send(idl->session, msg);
+}
+
+static void
+ovsdb_idl_parse_update(struct ovsdb_idl *idl, const struct json *table_updates)
+{
+ struct ovsdb_error *error;
+
+ idl->change_seqno++;
+
+ error = ovsdb_idl_parse_update__(idl, table_updates);
+ if (error) {
+ if (!VLOG_DROP_WARN(&syntax_rl)) {
+ char *s = ovsdb_error_to_string(error);
+ VLOG_WARN_RL(&syntax_rl, "%s", s);
+ free(s);
+ }
+ ovsdb_error_destroy(error);
+ }
+}
+
+static struct ovsdb_error *
+ovsdb_idl_parse_update__(struct ovsdb_idl *idl,
+ const struct json *table_updates)
+{
+ const struct shash_node *tables_node;
+
+ if (table_updates->type != JSON_OBJECT) {
+ return ovsdb_syntax_error(table_updates, NULL,
+ "<table-updates> is not an object");
+ }
+ SHASH_FOR_EACH (tables_node, json_object(table_updates)) {
+ const struct json *table_update = tables_node->data;
+ const struct shash_node *table_node;
+ struct ovsdb_idl_table *table;
+
+ table = shash_find_data(&idl->tables, tables_node->name);
+ if (!table) {
+ return ovsdb_syntax_error(
+ table_updates, NULL,
+ "<table-updates> includes unknown table \"%s\"",
+ tables_node->name);
+ }
+
+ if (table_update->type != JSON_OBJECT) {
+ return ovsdb_syntax_error(table_update, NULL,
+ "<table-update> for table \"%s\" is "
+ "not an object", table->class->name);
+ }
+ SHASH_FOR_EACH (table_node, json_object(table_update)) {
+ const struct json *row_update = table_node->data;
+ const struct json *old_json, *new_json;
+ struct uuid uuid;
+
+ if (!uuid_from_string(&uuid, table_node->name)) {
+ return ovsdb_syntax_error(table_update, NULL,
+ "<table-update> for table \"%s\" "
+ "contains bad UUID "
+ "\"%s\" as member name",
+ table->class->name,
+ table_node->name);
+ }
+ if (row_update->type != JSON_OBJECT) {
+ return ovsdb_syntax_error(row_update, NULL,
+ "<table-update> for table \"%s\" "
+ "contains <row-update> for %s that "
+ "is not an object",
+ table->class->name,
+ table_node->name);
+ }
+
+ old_json = shash_find_data(json_object(row_update), "old");
+ new_json = shash_find_data(json_object(row_update), "new");
+ if (old_json && old_json->type != JSON_OBJECT) {
+ return ovsdb_syntax_error(old_json, NULL,
+ "\"old\" <row> is not object");
+ } else if (new_json && new_json->type != JSON_OBJECT) {
+ return ovsdb_syntax_error(new_json, NULL,
+ "\"new\" <row> is not object");
+ } else if ((old_json != NULL) + (new_json != NULL)
+ != shash_count(json_object(row_update))) {
+ return ovsdb_syntax_error(row_update, NULL,
+ "<row-update> contains unexpected "
+ "member");
+ } else if (!old_json && !new_json) {
+ return ovsdb_syntax_error(row_update, NULL,
+ "<row-update> missing \"old\" "
+ "and \"new\" members");
+ }
+
+ ovsdb_idl_process_update(table, &uuid, old_json, new_json);
+ }
+ }
+
+ return NULL;
+}
+
+static struct ovsdb_idl_row *
+ovsdb_idl_get_row(struct ovsdb_idl_table *table, const struct uuid *uuid)
+{
+ struct ovsdb_idl_row *row;
+
+ HMAP_FOR_EACH_WITH_HASH (row, struct ovsdb_idl_row, hmap_node,
+ uuid_hash(uuid), &table->rows) {
+ if (uuid_equals(&row->uuid, uuid)) {
+ return row;
+ }
+ }
+ return NULL;
+}
+
+static void
+ovsdb_idl_process_update(struct ovsdb_idl_table *table,
+ const struct uuid *uuid, const struct json *old,
+ const struct json *new)
+{
+ struct ovsdb_idl_row *row;
+
+ row = ovsdb_idl_get_row(table, uuid);
+ if (!new) {
+ /* Delete row. */
+ if (row && !ovsdb_idl_row_is_orphan(row)) {
+ /* XXX perhaps we should check the 'old' values? */
+ ovsdb_idl_delete_row(row);
+ } else {
+ VLOG_WARN_RL(&semantic_rl, "cannot delete missing row "UUID_FMT" "
+ "from table %s",
+ UUID_ARGS(uuid), table->class->name);
+ }
+ } else if (!old) {
+ /* Insert row. */
+ if (!row) {
+ ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), new);
+ } else if (ovsdb_idl_row_is_orphan(row)) {
+ ovsdb_idl_insert_row(row, new);
+ } else {
+ VLOG_WARN_RL(&semantic_rl, "cannot add existing row "UUID_FMT" to "
+ "table %s", UUID_ARGS(uuid), table->class->name);
+ ovsdb_idl_modify_row(row, new);
+ }
+ } else {
+ /* Modify row. */
+ if (row) {
+ /* XXX perhaps we should check the 'old' values? */
+ if (!ovsdb_idl_row_is_orphan(row)) {
+ ovsdb_idl_modify_row(row, new);
+ } else {
+ VLOG_WARN_RL(&semantic_rl, "cannot modify missing but "
+ "referenced row "UUID_FMT" in table %s",
+ UUID_ARGS(uuid), table->class->name);
+ ovsdb_idl_insert_row(row, new);
+ }
+ } else {
+ VLOG_WARN_RL(&semantic_rl, "cannot modify missing row "UUID_FMT" "
+ "in table %s", UUID_ARGS(uuid), table->class->name);
+ ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), new);
+ }
+ }
+}
+
+static void
+ovsdb_idl_row_update(struct ovsdb_idl_row *row, const struct json *row_json)
+{
+ struct ovsdb_idl_table *table = row->table;
+ struct shash_node *node;
+
+ SHASH_FOR_EACH (node, json_object(row_json)) {
+ const char *column_name = node->name;
+ const struct ovsdb_idl_column *column;
+ struct ovsdb_datum datum;
+ struct ovsdb_error *error;
+
+ column = shash_find_data(&table->columns, column_name);
+ if (!column) {
+ VLOG_WARN_RL(&syntax_rl, "unknown column %s updating row "UUID_FMT,
+ column_name, UUID_ARGS(&row->uuid));
+ continue;
+ }
+
+ error = ovsdb_datum_from_json(&datum, &column->type, node->data, NULL);
+ if (!error) {
+ ovsdb_datum_swap(&row->fields[column - table->class->columns],
+ &datum);
+ ovsdb_datum_destroy(&datum, &column->type);
+ } else {
+ char *s = ovsdb_error_to_string(error);
+ VLOG_WARN_RL(&syntax_rl, "error parsing column %s in row "UUID_FMT
+ " in table %s: %s", column_name,
+ UUID_ARGS(&row->uuid), table->class->name, s);
+ free(s);
+ ovsdb_error_destroy(error);
+ }
+ }
+}
+
+static bool
+ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *row)
+{
+ return !row->fields;
+}
+
+static void
+ovsdb_idl_row_clear_fields(struct ovsdb_idl_row *row)
+{
+ if (!ovsdb_idl_row_is_orphan(row)) {
+ const struct ovsdb_idl_table_class *class = row->table->class;
+ size_t i;
+
+ for (i = 0; i < class->n_columns; i++) {
+ ovsdb_datum_destroy(&row->fields[i], &class->columns[i].type);
+ }
+ row->fields = NULL;
+ }
+}
+
+static void
+ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row *row, bool destroy_dsts)
+{
+ struct ovsdb_idl_arc *arc, *next;
+
+ /* Delete all forward arcs. If 'destroy_dsts', destroy any orphaned rows
+ * that this causes to be unreferenced. */
+ LIST_FOR_EACH_SAFE (arc, next, struct ovsdb_idl_arc, src_node,
+ &row->src_arcs) {
+ list_remove(&arc->dst_node);
+ if (destroy_dsts
+ && ovsdb_idl_row_is_orphan(arc->dst)
+ && list_is_empty(&arc->dst->dst_arcs)) {
+ ovsdb_idl_row_destroy(arc->dst);
+ }
+ free(arc);
+ }
+ list_init(&row->src_arcs);
+}
+
+/* Force nodes that reference 'row' to reparse. */
+static void
+ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row *row, bool destroy_dsts)
+{
+ struct ovsdb_idl_arc *arc, *next;
+
+ /* This is trickier than it looks. ovsdb_idl_row_clear_arcs() will destroy
+ * 'arc', so we need to use the "safe" variant of list traversal. However,
+ * calling ref->table->class->parse will add an arc equivalent to 'arc' to
+ * row->arcs. That could be a problem for traversal, but it adds it at the
+ * beginning of the list to prevent us from stumbling upon it again.
+ *
+ * (If duplicate arcs were possible then we would need to make sure that
+ * 'next' didn't also point into 'arc''s destination, but we forbid
+ * duplicate arcs.) */
+ LIST_FOR_EACH_SAFE (arc, next, struct ovsdb_idl_arc, dst_node,
+ &row->dst_arcs) {
+ struct ovsdb_idl_row *ref = arc->src;
+
+ (ref->table->class->unparse)(ref);
+ ovsdb_idl_row_clear_arcs(ref, destroy_dsts);
+ (ref->table->class->parse)(ref);
+ }
+}
+
+static struct ovsdb_idl_row *
+ovsdb_idl_row_create(struct ovsdb_idl_table *table, const struct uuid *uuid)
+{
+ struct ovsdb_idl_row *row = xmalloc(table->class->allocation_size);
+ hmap_insert(&table->rows, &row->hmap_node, uuid_hash(uuid));
+ row->uuid = *uuid;
+ list_init(&row->src_arcs);
+ list_init(&row->dst_arcs);
+ row->table = table;
+ row->fields = NULL;
+ return row;
+}
+
+static void
+ovsdb_idl_row_destroy(struct ovsdb_idl_row *row)
+{
+ if (row) {
+ ovsdb_idl_row_clear_fields(row);
+ hmap_remove(&row->table->rows, &row->hmap_node);
+ free(row);
+ }
+}
+
+static void
+ovsdb_idl_insert_row(struct ovsdb_idl_row *row, const struct json *row_json)
+{
+ const struct ovsdb_idl_table_class *class = row->table->class;
+ size_t i;
+
+ assert(!row->fields);
+ row->fields = xmalloc(class->n_columns * sizeof *row->fields);
+ for (i = 0; i < class->n_columns; i++) {
+ ovsdb_datum_init_default(&row->fields[i], &class->columns[i].type);
+ }
+ ovsdb_idl_row_update(row, row_json);
+ (class->parse)(row);
+
+ ovsdb_idl_row_reparse_backrefs(row, false);
+}
+
+static void
+ovsdb_idl_delete_row(struct ovsdb_idl_row *row)
+{
+ (row->table->class->unparse)(row);
+ ovsdb_idl_row_clear_arcs(row, true);
+ ovsdb_idl_row_clear_fields(row);
+ if (list_is_empty(&row->dst_arcs)) {
+ ovsdb_idl_row_destroy(row);
+ } else {
+ ovsdb_idl_row_reparse_backrefs(row, true);
+ }
+}
+
+static void
+ovsdb_idl_modify_row(struct ovsdb_idl_row *row, const struct json *row_json)
+{
+ (row->table->class->unparse)(row);
+ ovsdb_idl_row_clear_arcs(row, true);
+ ovsdb_idl_row_update(row, row_json);
+ (row->table->class->parse)(row);
+}
+
+static bool
+may_add_arc(const struct ovsdb_idl_row *src, const struct ovsdb_idl_row *dst)
+{
+ const struct ovsdb_idl_arc *arc;
+
+ /* No self-arcs. */
+ if (src == dst) {
+ return false;
+ }
+
+ /* No duplicate arcs.
+ *
+ * We only need to test whether the first arc in dst->dst_arcs originates
+ * at 'src', since we add all of the arcs from a given source in a clump
+ * (in a single call to a row's ->parse function) and new arcs are always
+ * added at the front of the dst_arcs list. */
+ if (list_is_empty(&dst->dst_arcs)) {
+ return true;
+ }
+ arc = CONTAINER_OF(dst->dst_arcs.next, struct ovsdb_idl_arc, dst_node);
+ return arc->src != src;
+}
+
+struct ovsdb_idl_row *
+ovsdb_idl_get_row_arc(struct ovsdb_idl_row *src,
+ struct ovsdb_idl_table_class *dst_table_class,
+ const struct uuid *dst_uuid)
+{
+ struct ovsdb_idl *idl = src->table->idl;
+ struct ovsdb_idl_arc *arc;
+ struct ovsdb_idl_row *dst;
+
+ dst = ovsdb_idl_get_row(src->table, dst_uuid);
+ if (!dst) {
+ struct ovsdb_idl_table *dst_table;
+ dst_table = shash_find_data(&idl->tables, dst_table_class->name);
+ dst = ovsdb_idl_row_create(dst_table, dst_uuid);
+ }
+
+ /* Add a new arc, if it wouldn't be a self-arc or a duplicate arc. */
+ if (may_add_arc(src, dst)) {
+ /* The arc *must* be added at the front of the dst_arcs list. See
+ * ovsdb_idl_row_reparse_backrefs() for details. */
+ arc = xmalloc(sizeof *arc);
+ list_push_front(&src->src_arcs, &arc->src_node);
+ list_push_front(&dst->dst_arcs, &arc->dst_node);
+ arc->src = src;
+ arc->dst = dst;
+ }
+
+ return !ovsdb_idl_row_is_orphan(dst) ? dst : NULL;
+}
+
+static struct ovsdb_idl_row *
+next_real_row(struct ovsdb_idl_table *table, struct hmap_node *node)
+{
+ for (; node; node = hmap_next(&table->rows, node)) {
+ struct ovsdb_idl_row *row;
+
+ row = CONTAINER_OF(node, struct ovsdb_idl_row, hmap_node);
+ if (!ovsdb_idl_row_is_orphan(row)) {
+ return row;
+ }
+ }
+ return NULL;
+}
+
+struct ovsdb_idl_row *
+ovsdb_idl_first_row(const struct ovsdb_idl *idl,
+ const struct ovsdb_idl_table_class *table_class)
+{
+ struct ovsdb_idl_table *table;
+
+ table = shash_find_data(&idl->tables, table_class->name);
+ return next_real_row(table, hmap_first(&table->rows));
+}
+
+struct ovsdb_idl_row *
+ovsdb_idl_next_row(const struct ovsdb_idl_row *row)
+{
+ struct ovsdb_idl_table *table = row->table;
+
+ return next_real_row(table, hmap_next(&table->rows, &row->hmap_node));
+}
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef OVSDB_IDL_H
+#define OVSDB_IDL_H 1
+
+struct ovsdb_idl_class;
+
+struct ovsdb_idl *ovsdb_idl_create(const char *remote,
+ const struct ovsdb_idl_class *);
+void ovsdb_idl_destroy(struct ovsdb_idl *);
+
+void ovsdb_idl_run(struct ovsdb_idl *);
+void ovsdb_idl_wait(struct ovsdb_idl *);
+
+unsigned int ovsdb_idl_get_seqno(const struct ovsdb_idl *);
+void ovsdb_idl_force_reconnect(struct ovsdb_idl *);
+
+#endif /* ovsdb-idl.h */
--- /dev/null
+/* Copyright (c) 2009 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-parser.h"
+
+#include <ctype.h>
+#include <stdarg.h>
+
+#include "ovsdb-error.h"
+
+void
+ovsdb_parser_init(struct ovsdb_parser *parser, const struct json *json,
+ const char *name, ...)
+{
+ va_list args;
+
+ va_start(args, name);
+ parser->name = xvasprintf(name, args);
+ va_end(args);
+
+ svec_init(&parser->used);
+ parser->error = NULL;
+
+ parser->json = (json && json->type == JSON_OBJECT ? json : NULL);
+ if (!parser->json) {
+ ovsdb_parser_raise_error(parser, "Object expected.");
+ }
+}
+
+bool
+ovsdb_parser_is_id(const char *string)
+{
+ unsigned char c;
+
+ c = *string;
+ if (!isalpha(c) && c != '_') {
+ return false;
+ }
+
+ for (;;) {
+ c = *++string;
+ if (c == '\0') {
+ return true;
+ } else if (!isalpha(c) && !isdigit(c) && c != '_') {
+ return false;
+ }
+ }
+}
+
+const struct json *
+ovsdb_parser_member(struct ovsdb_parser *parser, const char *name,
+ enum ovsdb_parser_types types)
+{
+ struct json *value;
+
+ if (!parser->json) {
+ return NULL;
+ }
+
+ value = shash_find_data(json_object(parser->json), name);
+ if (!value) {
+ if (!(types & OP_OPTIONAL)) {
+ ovsdb_parser_raise_error(parser,
+ "Required '%s' member is missing.", name);
+ }
+ return NULL;
+ }
+
+ if ((value->type >= 0 && value->type < JSON_N_TYPES
+ && types & (1u << value->type))
+ || (types & OP_ID && value->type == JSON_STRING
+ && ovsdb_parser_is_id(value->u.string)))
+ {
+ svec_add(&parser->used, name);
+ return value;
+ } else {
+ ovsdb_parser_raise_error(parser, "Type mismatch for member '%s'.",
+ name);
+ return NULL;
+ }
+}
+
+void
+ovsdb_parser_raise_error(struct ovsdb_parser *parser, const char *format, ...)
+{
+ if (!parser->error) {
+ struct ovsdb_error *error;
+ va_list args;
+ char *message;
+
+ va_start(args, format);
+ message = xvasprintf(format, args);
+ va_end(args);
+
+ error = ovsdb_syntax_error(parser->json, NULL, "Parsing %s failed: %s",
+ parser->name, message);
+ free(message);
+
+ parser->error = error;
+ }
+}
+
+struct ovsdb_error *
+ovsdb_parser_get_error(const struct ovsdb_parser *parser)
+{
+ return parser->error ? ovsdb_error_clone(parser->error) : NULL;
+}
+
+bool
+ovsdb_parser_has_error(const struct ovsdb_parser *parser)
+{
+ return parser->error != NULL;
+}
+
+struct ovsdb_error *
+ovsdb_parser_finish(struct ovsdb_parser *parser)
+{
+ if (!parser->error) {
+ const struct shash *object = json_object(parser->json);
+ size_t n_unused;
+
+ /* XXX this method of detecting unused members can be made cheaper */
+ svec_sort_unique(&parser->used);
+ n_unused = shash_count(object) - parser->used.n;
+ if (n_unused) {
+ struct shash_node *node;
+
+ SHASH_FOR_EACH (node, object) {
+ if (!svec_contains(&parser->used, node->name)) {
+ if (n_unused > 1) {
+ ovsdb_parser_raise_error(
+ parser,
+ "Member '%s' and %zu other member%s "
+ "are present but not allowed here.",
+ node->name, n_unused - 1, n_unused > 2 ? "s" : "");
+ } else {
+ ovsdb_parser_raise_error(
+ parser,
+ "Member '%s' is present but not allowed here.",
+ node->name);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ free(parser->name);
+ svec_destroy(&parser->used);
+
+ return parser->error;
+}
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef OVSDB_PARSER_H
+#define OVSDB_PARSER_H 1
+
+#include <stdbool.h>
+#include "compiler.h"
+#include "json.h"
+#include "svec.h"
+#include "util.h"
+
+struct ovsdb_parser {
+ char *name; /* Used only in error messages. */
+ struct svec used; /* Already-parsed names from 'object'. */
+ const struct json *json; /* JSON object being parsed. */
+ struct ovsdb_error *error; /* Error signaled, if any. */
+};
+
+/* Check that the JSON types make the bitwise tricks below work OK. */
+BUILD_ASSERT_DECL(JSON_NULL >= 0 && JSON_NULL < 10);
+BUILD_ASSERT_DECL(JSON_FALSE >= 0 && JSON_FALSE < 10);
+BUILD_ASSERT_DECL(JSON_TRUE >= 0 && JSON_TRUE < 10);
+BUILD_ASSERT_DECL(JSON_OBJECT >= 0 && JSON_OBJECT < 10);
+BUILD_ASSERT_DECL(JSON_ARRAY >= 0 && JSON_ARRAY < 10);
+BUILD_ASSERT_DECL(JSON_INTEGER >= 0 && JSON_INTEGER < 10);
+BUILD_ASSERT_DECL(JSON_REAL >= 0 && JSON_REAL < 10);
+BUILD_ASSERT_DECL(JSON_STRING >= 0 && JSON_STRING < 10);
+BUILD_ASSERT_DECL(JSON_N_TYPES == 8);
+
+enum ovsdb_parser_types {
+ OP_NULL = 1 << JSON_NULL, /* null */
+ OP_FALSE = 1 << JSON_FALSE, /* false */
+ OP_TRUE = 1 << JSON_TRUE, /* true */
+ OP_OBJECT = 1 << JSON_OBJECT, /* {"a": b, "c": d, ...} */
+ OP_ARRAY = 1 << JSON_ARRAY, /* [1, 2, 3, ...] */
+ OP_INTEGER = 1 << JSON_INTEGER, /* 123. */
+ OP_NONINTEGER = 1 << JSON_REAL, /* 123.456. */
+ OP_STRING = 1 << JSON_STRING, /* "..." */
+
+ OP_BOOLEAN = OP_FALSE | OP_TRUE,
+ OP_NUMBER = OP_INTEGER | OP_NONINTEGER,
+
+ OP_ID = 1 << JSON_N_TYPES, /* "[_a-zA-Z][_a-zA-Z0-9]*" */
+ OP_OPTIONAL = 1 << (JSON_N_TYPES + 1) /* no value at all */
+};
+
+void ovsdb_parser_init(struct ovsdb_parser *, const struct json *,
+ const char *name, ...)
+ PRINTF_FORMAT(3, 4);
+const struct json *ovsdb_parser_member(struct ovsdb_parser *, const char *name,
+ enum ovsdb_parser_types);
+
+void ovsdb_parser_raise_error(struct ovsdb_parser *parser,
+ const char *format, ...)
+ PRINTF_FORMAT(2, 3);
+bool ovsdb_parser_has_error(const struct ovsdb_parser *);
+struct ovsdb_error *ovsdb_parser_get_error(const struct ovsdb_parser *);
+struct ovsdb_error *ovsdb_parser_finish(struct ovsdb_parser *)
+ WARN_UNUSED_RESULT;
+
+bool ovsdb_parser_is_id(const char *string);
+
+#endif /* ovsdb-parser.h */
--- /dev/null
+/* Copyright (c) 2009 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-types.h"
+
+#include <limits.h>
+
+#include "dynamic-string.h"
+#include "json.h"
+#include "ovsdb-error.h"
+#include "ovsdb-parser.h"
+
+const struct ovsdb_type ovsdb_type_integer =
+ OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_INTEGER);
+const struct ovsdb_type ovsdb_type_real =
+ OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_REAL);
+const struct ovsdb_type ovsdb_type_boolean =
+ OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_BOOLEAN);
+const struct ovsdb_type ovsdb_type_string =
+ OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_STRING);
+const struct ovsdb_type ovsdb_type_uuid =
+ OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_UUID);
+
+const char *
+ovsdb_atomic_type_to_string(enum ovsdb_atomic_type type)
+{
+ switch (type) {
+ case OVSDB_TYPE_VOID:
+ return "void";
+
+ case OVSDB_TYPE_INTEGER:
+ return "integer";
+
+ case OVSDB_TYPE_REAL:
+ return "real";
+
+ case OVSDB_TYPE_BOOLEAN:
+ return "boolean";
+
+ case OVSDB_TYPE_STRING:
+ return "string";
+
+ case OVSDB_TYPE_UUID:
+ return "uuid";
+
+ case OVSDB_N_TYPES:
+ default:
+ return "<invalid>";
+ }
+}
+
+struct json *
+ovsdb_atomic_type_to_json(enum ovsdb_atomic_type type)
+{
+ return json_string_create(ovsdb_atomic_type_to_string(type));
+}
+
+bool
+ovsdb_type_is_valid(const struct ovsdb_type *type)
+{
+ return (type->key_type != OVSDB_TYPE_VOID
+ && ovsdb_atomic_type_is_valid(type->key_type)
+ && ovsdb_atomic_type_is_valid(type->value_type)
+ && type->n_min <= type->n_max
+ && (type->value_type == OVSDB_TYPE_VOID
+ || ovsdb_atomic_type_is_valid_key(type->key_type)));
+}
+
+bool
+ovsdb_atomic_type_from_string(const char *string, enum ovsdb_atomic_type *type)
+{
+ if (!strcmp(string, "integer")) {
+ *type = OVSDB_TYPE_INTEGER;
+ } else if (!strcmp(string, "real")) {
+ *type = OVSDB_TYPE_REAL;
+ } else if (!strcmp(string, "boolean")) {
+ *type = OVSDB_TYPE_BOOLEAN;
+ } else if (!strcmp(string, "string")) {
+ *type = OVSDB_TYPE_STRING;
+ } else if (!strcmp(string, "uuid")) {
+ *type = OVSDB_TYPE_UUID;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+struct ovsdb_error *
+ovsdb_atomic_type_from_json(enum ovsdb_atomic_type *type,
+ const struct json *json)
+{
+ if (json->type == JSON_STRING) {
+ if (ovsdb_atomic_type_from_string(json_string(json), type)) {
+ return NULL;
+ } else {
+ *type = OVSDB_TYPE_VOID;
+ return ovsdb_syntax_error(json, NULL,
+ "\"%s\" is not an atomic-type",
+ json_string(json));
+ }
+ } else {
+ *type = OVSDB_TYPE_VOID;
+ return ovsdb_syntax_error(json, NULL, "atomic-type expected");
+ }
+}
+
+static struct ovsdb_error *
+n_from_json(const struct json *json, unsigned int *n)
+{
+ if (!json) {
+ return NULL;
+ } else if (json->type == JSON_INTEGER
+ && json->u.integer >= 0 && json->u.integer < UINT_MAX) {
+ *n = json->u.integer;
+ return NULL;
+ } else {
+ return ovsdb_syntax_error(json, NULL, "bad min or max value");
+ }
+}
+
+char *
+ovsdb_type_to_english(const struct ovsdb_type *type)
+{
+ const char *key = ovsdb_atomic_type_to_string(type->key_type);
+ const char *value = ovsdb_atomic_type_to_string(type->value_type);
+ if (ovsdb_type_is_scalar(type)) {
+ return xstrdup(key);
+ } else {
+ struct ds s = DS_EMPTY_INITIALIZER;
+ ds_put_cstr(&s, ovsdb_type_is_set(type) ? "set" : "map");
+ if (type->n_max == UINT_MAX) {
+ if (type->n_min) {
+ ds_put_format(&s, " of %u or more", type->n_min);
+ } else {
+ ds_put_cstr(&s, " of");
+ }
+ } else if (type->n_min) {
+ ds_put_format(&s, " of %u to %u", type->n_min, type->n_max);
+ } else {
+ ds_put_format(&s, " of up to %u", type->n_max);
+ }
+ if (ovsdb_type_is_set(type)) {
+ ds_put_format(&s, " %ss", key);
+ } else {
+ ds_put_format(&s, " (%s, %s) pairs", key, value);
+ }
+ return ds_cstr(&s);
+ }
+}
+
+struct ovsdb_error *
+ovsdb_type_from_json(struct ovsdb_type *type, const struct json *json)
+{
+ type->value_type = OVSDB_TYPE_VOID;
+ type->n_min = 1;
+ type->n_max = 1;
+
+ if (json->type == JSON_STRING) {
+ return ovsdb_atomic_type_from_json(&type->key_type, json);
+ } else if (json->type == JSON_OBJECT) {
+ const struct json *key, *value, *min, *max;
+ struct ovsdb_error *error;
+ struct ovsdb_parser parser;
+
+ ovsdb_parser_init(&parser, json, "ovsdb type");
+ key = ovsdb_parser_member(&parser, "key", OP_STRING);
+ value = ovsdb_parser_member(&parser, "value", OP_STRING | OP_OPTIONAL);
+ min = ovsdb_parser_member(&parser, "min", OP_INTEGER | OP_OPTIONAL);
+ max = ovsdb_parser_member(&parser, "max",
+ OP_INTEGER | OP_STRING | OP_OPTIONAL);
+ error = ovsdb_parser_finish(&parser);
+ if (error) {
+ return error;
+ }
+
+ error = ovsdb_atomic_type_from_json(&type->key_type, key);
+ if (error) {
+ return error;
+ }
+
+ if (value) {
+ error = ovsdb_atomic_type_from_json(&type->value_type, value);
+ if (error) {
+ return error;
+ }
+ }
+
+ error = n_from_json(min, &type->n_min);
+ if (error) {
+ return error;
+ }
+
+ if (max && max->type == JSON_STRING
+ && !strcmp(max->u.string, "unlimited")) {
+ type->n_max = UINT_MAX;
+ } else {
+ error = n_from_json(max, &type->n_max);
+ if (error) {
+ return error;
+ }
+ }
+
+ if (!ovsdb_type_is_valid(type)) {
+ return ovsdb_syntax_error(json, NULL,
+ "ovsdb type fails constraint checks");
+ }
+
+ return NULL;
+ } else {
+ return ovsdb_syntax_error(json, NULL, "ovsdb type expected");
+ }
+}
+
+struct json *
+ovsdb_type_to_json(const struct ovsdb_type *type)
+{
+ if (ovsdb_type_is_scalar(type)) {
+ return ovsdb_atomic_type_to_json(type->key_type);
+ } else {
+ struct json *json = json_object_create();
+ json_object_put(json, "key",
+ ovsdb_atomic_type_to_json(type->key_type));
+ if (type->value_type != OVSDB_TYPE_VOID) {
+ json_object_put(json, "value",
+ ovsdb_atomic_type_to_json(type->value_type));
+ }
+ if (type->n_min != 1) {
+ json_object_put(json, "min", json_integer_create(type->n_min));
+ }
+ if (type->n_max == UINT_MAX) {
+ json_object_put_string(json, "max", "unlimited");
+ } else if (type->n_max != 1) {
+ json_object_put(json, "max", json_integer_create(type->n_max));
+ }
+ return json;
+ }
+}
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef OVSDB_TYPES_H
+#define OVSDB_TYPES_H 1
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "compiler.h"
+#include "uuid.h"
+
+struct json;
+
+/* An atomic type: one that OVSDB regards as a single unit of data. */
+enum ovsdb_atomic_type {
+ OVSDB_TYPE_VOID, /* No value. */
+ OVSDB_TYPE_INTEGER, /* Signed 64-bit integer. */
+ OVSDB_TYPE_REAL, /* IEEE 754 double-precision floating point. */
+ OVSDB_TYPE_BOOLEAN, /* True or false. */
+ OVSDB_TYPE_STRING, /* UTF-8 string. */
+ OVSDB_TYPE_UUID, /* RFC 4122 UUID referencing a table row. */
+ OVSDB_N_TYPES
+};
+
+static inline bool ovsdb_atomic_type_is_valid(enum ovsdb_atomic_type);
+static inline bool ovsdb_atomic_type_is_valid_key(enum ovsdb_atomic_type);
+bool ovsdb_atomic_type_from_string(const char *, enum ovsdb_atomic_type *);
+struct ovsdb_error *ovsdb_atomic_type_from_json(enum ovsdb_atomic_type *,
+ const struct json *);
+const char *ovsdb_atomic_type_to_string(enum ovsdb_atomic_type);
+struct json *ovsdb_atomic_type_to_json(enum ovsdb_atomic_type);
+\f
+/* An OVSDB type. One of:
+ *
+ * - An atomic type.
+ *
+ * - A set of atomic types.
+ *
+ * - A map from one atomic type to another.
+ */
+struct ovsdb_type {
+ enum ovsdb_atomic_type key_type;
+ enum ovsdb_atomic_type value_type;
+ unsigned int n_min;
+ unsigned int n_max; /* UINT_MAX stands in for "unlimited". */
+};
+
+#define OVSDB_TYPE_SCALAR_INITIALIZER(KEY_TYPE) \
+ { KEY_TYPE, OVSDB_TYPE_VOID, 1, 1 }
+
+extern const struct ovsdb_type ovsdb_type_integer;
+extern const struct ovsdb_type ovsdb_type_real;
+extern const struct ovsdb_type ovsdb_type_boolean;
+extern const struct ovsdb_type ovsdb_type_string;
+extern const struct ovsdb_type ovsdb_type_uuid;
+
+bool ovsdb_type_is_valid(const struct ovsdb_type *);
+
+static inline bool ovsdb_type_is_scalar(const struct ovsdb_type *);
+static inline bool ovsdb_type_is_optional(const struct ovsdb_type *);
+static inline bool ovsdb_type_is_composite(const struct ovsdb_type *);
+static inline bool ovsdb_type_is_set(const struct ovsdb_type *);
+static inline bool ovsdb_type_is_map(const struct ovsdb_type *);
+
+char *ovsdb_type_to_english(const struct ovsdb_type *);
+
+struct ovsdb_error *ovsdb_type_from_json(struct ovsdb_type *,
+ const struct json *)
+ WARN_UNUSED_RESULT;
+struct json *ovsdb_type_to_json(const struct ovsdb_type *);
+\f
+/* Inline function implementations. */
+
+static inline bool
+ovsdb_atomic_type_is_valid(enum ovsdb_atomic_type atomic_type)
+{
+ return atomic_type >= 0 && atomic_type < OVSDB_N_TYPES;
+}
+
+static inline bool
+ovsdb_atomic_type_is_valid_key(enum ovsdb_atomic_type atomic_type)
+{
+ /* XXX should we disallow reals or booleans as keys? */
+ return ovsdb_atomic_type_is_valid(atomic_type);
+}
+
+static inline bool ovsdb_type_is_scalar(const struct ovsdb_type *type)
+{
+ return (type->value_type == OVSDB_TYPE_VOID
+ && type->n_min == 1 && type->n_max == 1);
+}
+
+static inline bool ovsdb_type_is_optional(const struct ovsdb_type *type)
+{
+ return type->n_min == 0;
+}
+
+static inline bool ovsdb_type_is_composite(const struct ovsdb_type *type)
+{
+ return type->n_max > 1;
+}
+
+static inline bool ovsdb_type_is_set(const struct ovsdb_type *type)
+{
+ return (type->value_type == OVSDB_TYPE_VOID
+ && (type->n_min != 1 || type->n_max != 1));
+}
+
+static inline bool ovsdb_type_is_map(const struct ovsdb_type *type)
+{
+ return type->value_type != OVSDB_TYPE_VOID;
+}
+
+#endif /* ovsdb-types.h */
static struct poll_waiter *
new_waiter(int fd, short int events)
{
- struct poll_waiter *waiter = xcalloc(1, sizeof *waiter);
+ struct poll_waiter *waiter = xzalloc(sizeof *waiter);
assert(fd >= 0);
waiter->fd = fd;
waiter->events = events;
assert(sigchld_is_blocked());
- p = xcalloc(1, sizeof *p);
+ p = xzalloc(sizeof *p);
p->pid = pid;
slash = strrchr(name, '/');
p->name = xstrdup(slash ? slash + 1 : name);
#ifndef QUEUE_H
#define QUEUE_H 1
+#include <stdbool.h>
+
/* Packet queue. */
struct ovs_queue {
int n; /* Number of queued packets. */
void queue_push_tail(struct ovs_queue *, struct ofpbuf *);
struct ofpbuf *queue_pop_head(struct ovs_queue *);
+static inline bool queue_is_empty(const struct ovs_queue *q)
+{
+ return q->n == 0;
+}
+
#endif /* queue.h */
struct rconn *
rconn_create(int probe_interval, int max_backoff)
{
- struct rconn *rc = xcalloc(1, sizeof *rc);
+ struct rconn *rc = xzalloc(sizeof *rc);
rc->state = S_VOID;
rc->state_entered = time_now();
--- /dev/null
+/*
+ * Copyright (c) 2008, 2009 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 <assert.h>
+#include <stdlib.h>
+
+#include "poll-loop.h"
+
+#define THIS_MODULE VLM_reconnect
+#include "vlog.h"
+
+#define STATES \
+ STATE(VOID, 1 << 0) \
+ STATE(BACKOFF, 1 << 1) \
+ STATE(CONNECTING, 1 << 2) \
+ STATE(ACTIVE, 1 << 3) \
+ STATE(IDLE, 1 << 4) \
+ STATE(RECONNECT, 1 << 5)
+enum state {
+#define STATE(NAME, VALUE) S_##NAME = VALUE,
+ STATES
+#undef STATE
+};
+
+static bool
+is_connected_state(enum state state)
+{
+ return (state & (S_ACTIVE | S_IDLE)) != 0;
+}
+
+struct reconnect {
+ /* Configuration. */
+ char *name;
+ int min_backoff;
+ int max_backoff;
+ int probe_interval;
+
+ /* State. */
+ enum state state;
+ long long int state_entered;
+ int backoff;
+ long long int last_received;
+ long long int last_connected;
+
+ /* These values are simply for statistics reporting, not otherwise used
+ * directly by anything internal. */
+ long long int creation_time;
+ unsigned int n_attempted_connections, n_successful_connections;
+ unsigned int total_connected_duration;
+ unsigned int seqno;
+};
+
+static void reconnect_transition__(struct reconnect *, long long int now,
+ enum state state);
+static long long int reconnect_deadline__(const struct reconnect *);
+
+static const char *
+reconnect_state_name__(enum state state)
+{
+ switch (state) {
+#define STATE(NAME, VALUE) case S_##NAME: return #NAME;
+ STATES
+#undef STATE
+ }
+ return "***ERROR***";
+}
+
+/* Creates and returns a new reconnect FSM with default settings. The FSM is
+ * initially disabled. The caller will likely want to call reconnect_enable()
+ * and reconnect_set_name() on the returned object. */
+struct reconnect *
+reconnect_create(long long int now)
+{
+ struct reconnect *fsm = xzalloc(sizeof *fsm);
+
+ fsm->name = xstrdup("void");
+ fsm->min_backoff = 1000;
+ fsm->max_backoff = 8000;
+ fsm->probe_interval = 5000;
+
+ fsm->state = S_VOID;
+ fsm->state_entered = now;
+ fsm->backoff = 0;
+ fsm->last_received = now;
+ fsm->last_connected = now;
+ fsm->creation_time = now;
+
+ return fsm;
+}
+
+/* Frees 'fsm'. */
+void
+reconnect_destroy(struct reconnect *fsm)
+{
+ if (fsm) {
+ free(fsm->name);
+ free(fsm);
+ }
+}
+
+/* Returns 'fsm''s name. */
+const char *
+reconnect_get_name(const struct reconnect *fsm)
+{
+ return fsm->name;
+}
+
+/* Sets 'fsm''s name to 'name'. If 'name' is null, then "void" is used
+ * instead.
+ *
+ * The name set for 'fsm' is used in log messages. */
+void
+reconnect_set_name(struct reconnect *fsm, const char *name)
+{
+ free(fsm->name);
+ fsm->name = xstrdup(name ? name : "void");
+}
+
+/* Return the minimum number of milliseconds to back off between consecutive
+ * connection attempts. The default is 1000 ms. */
+int
+reconnect_get_min_backoff(const struct reconnect *fsm)
+{
+ return fsm->min_backoff;
+}
+
+/* Return the maximum number of milliseconds to back off between consecutive
+ * connection attempts. The default is 8000 ms. */
+int
+reconnect_get_max_backoff(const struct reconnect *fsm)
+{
+ return fsm->max_backoff;
+}
+
+/* Returns the "probe interval" for 'fsm' in milliseconds. If this is zero, it
+ * disables the connection keepalive feature. If it is nonzero, then if the
+ * interval passes while 'fsm' is connected and without reconnect_received()
+ * being called for 'fsm', reconnect_run() returns RECONNECT_PROBE. If the
+ * interval passes again without reconnect_received() being called,
+ * reconnect_run() returns RECONNECT_DISCONNECT for 'fsm'. */
+int
+reconnect_get_probe_interval(const struct reconnect *fsm)
+{
+ return fsm->probe_interval;
+}
+
+/* Configures the backoff parameters for 'fsm'. 'min_backoff' is the minimum
+ * number of milliseconds, and 'max_backoff' is the maximum, between connection
+ * attempts.
+ *
+ * 'min_backoff' must be at least 1000, and 'max_backoff' must be greater than
+ * or equal to 'min_backoff'. */
+void
+reconnect_set_backoff(struct reconnect *fsm, int min_backoff, int max_backoff)
+{
+ fsm->min_backoff = MAX(min_backoff, 1000);
+ fsm->max_backoff = max_backoff ? MAX(max_backoff, 1000) : 8000;
+ if (fsm->min_backoff > fsm->max_backoff) {
+ fsm->max_backoff = fsm->min_backoff;
+ }
+
+ if (fsm->state == S_BACKOFF && fsm->backoff > max_backoff) {
+ fsm->backoff = max_backoff;
+ }
+}
+
+/* Sets the "probe interval" for 'fsm' to 'probe_interval', in milliseconds.
+ * If this is zero, it disables the connection keepalive feature. If it is
+ * nonzero, then if the interval passes while 'fsm' is connected and without
+ * reconnect_received() being called for 'fsm', reconnect_run() returns
+ * RECONNECT_PROBE. If the interval passes again without reconnect_received()
+ * being called, reconnect_run() returns RECONNECT_DISCONNECT for 'fsm'.
+ *
+ * If 'probe_interval' is nonzero, then it will be forced to a value of at
+ * least 1000 ms. */
+void
+reconnect_set_probe_interval(struct reconnect *fsm, int probe_interval)
+{
+ fsm->probe_interval = probe_interval ? MAX(1000, probe_interval) : 0;
+}
+
+/* Returns true if 'fsm' has been enabled with reconnect_enable(). Calling
+ * another function that indicates a change in connection state, such as
+ * reconnect_disconnected() or reconnect_force_reconnect(), will also enable
+ * a reconnect FSM. */
+bool
+reconnect_is_enabled(const struct reconnect *fsm)
+{
+ return fsm->state != S_VOID;
+}
+
+/* If 'fsm' is disabled (the default for newly created FSMs), enables it, so
+ * that the next call to reconnect_run() for 'fsm' will return
+ * RECONNECT_CONNECT.
+ *
+ * If 'fsm' is not disabled, this function has no effect. */
+void
+reconnect_enable(struct reconnect *fsm, long long int now)
+{
+ if (fsm->state == S_VOID) {
+ reconnect_transition__(fsm, now, S_BACKOFF);
+ fsm->backoff = 0;
+ }
+}
+
+/* Disables 'fsm'. Until 'fsm' is enabled again, reconnect_run() will always
+ * return 0. */
+void
+reconnect_disable(struct reconnect *fsm, long long int now)
+{
+ if (fsm->state != S_VOID) {
+ reconnect_transition__(fsm, now, S_VOID);
+ }
+}
+
+/* If 'fsm' is enabled and currently connected (or attempting to connect),
+ * forces reconnect_run() for 'fsm' to return RECONNECT_DISCONNECT the next
+ * time it is called, which should cause the client to drop the connection (or
+ * attempt), back off, and then reconnect. */
+void
+reconnect_force_reconnect(struct reconnect *fsm, long long int now)
+{
+ if (fsm->state & (S_CONNECTING | S_ACTIVE | S_IDLE)) {
+ reconnect_transition__(fsm, now, S_RECONNECT);
+ }
+}
+
+/* Tell 'fsm' that the connection dropped or that a connection attempt failed.
+ * 'error' specifies the reason: a positive value represents an errno value,
+ * EOF indicates that the connection was closed by the peer (e.g. read()
+ * returned 0), and 0 indicates no specific error.
+ *
+ * The FSM will back off, then reconnect. */
+void
+reconnect_disconnected(struct reconnect *fsm, long long int now, int error)
+{
+ if (fsm->state != S_BACKOFF) {
+ /* Report what happened. */
+ if (fsm->state & (S_ACTIVE | S_IDLE)) {
+ if (error > 0) {
+ VLOG_WARN("%s: connection dropped (%s)",
+ fsm->name, strerror(error));
+ } else if (error == EOF) {
+ VLOG_INFO("%s: connection closed by peer", fsm->name);
+ } else {
+ VLOG_INFO("%s: connection dropped", fsm->name);
+ }
+ } else {
+ if (error > 0) {
+ VLOG_WARN("%s: connection attempt failed (%s)",
+ fsm->name, strerror(error));
+ } else {
+ VLOG_INFO("%s: connection attempt timed out", fsm->name);
+ }
+ }
+
+ /* Back off. */
+ if (fsm->state & (S_ACTIVE | S_IDLE)
+ && fsm->last_received - fsm->last_connected >= fsm->backoff) {
+ fsm->backoff = fsm->min_backoff;
+ } else {
+ if (fsm->backoff < fsm->min_backoff) {
+ fsm->backoff = fsm->min_backoff;
+ } else if (fsm->backoff >= fsm->max_backoff / 2) {
+ fsm->backoff = fsm->max_backoff;
+ } else {
+ fsm->backoff *= 2;
+ }
+ VLOG_INFO("%s: waiting %.3g seconds before reconnect\n",
+ fsm->name, fsm->backoff / 1000.0);
+ }
+ reconnect_transition__(fsm, now, S_BACKOFF);
+ }
+}
+
+/* Tell 'fsm' that a connection attempt is in progress.
+ *
+ * The FSM will start a timer, after which the connection attempt will be
+ * aborted (by returning RECONNECT_DISCONNECT from reconect_run()). */
+void
+reconnect_connecting(struct reconnect *fsm, long long int now)
+{
+ if (fsm->state != S_CONNECTING) {
+ VLOG_INFO("%s: connecting...", fsm->name);
+ reconnect_transition__(fsm, now, S_CONNECTING);
+ }
+}
+
+/* Tell 'fsm' that the connection was successful.
+ *
+ * The FSM will start the probe interval timer, which is reset by
+ * reconnect_received(). If the timer expires, a probe will be sent (by
+ * returning RECONNECT_PROBE from reconnect_run()). If the timer expires
+ * again without being reset, the connection will be aborted (by returning
+ * RECONNECT_DISCONNECT from reconnect_run()). */
+void
+reconnect_connected(struct reconnect *fsm, long long int now)
+{
+ if (!is_connected_state(fsm->state)) {
+ reconnect_connecting(fsm, now);
+
+ VLOG_INFO("%s: connected", fsm->name);
+ reconnect_transition__(fsm, now, S_ACTIVE);
+ fsm->last_connected = now;
+ }
+}
+
+/* Tell 'fsm' that the connection attempt failed.
+ *
+ * The FSM will back off and attempt to reconnect. */
+void
+reconnect_connect_failed(struct reconnect *fsm, long long int now, int error)
+{
+ reconnect_connecting(fsm, now);
+ reconnect_disconnected(fsm, now, error);
+}
+
+/* Tell 'fsm' that some data was received. This resets the probe interval
+ * timer, so that the connection is known not to be idle. */
+void
+reconnect_received(struct reconnect *fsm, long long int now)
+{
+ if (fsm->state != S_ACTIVE) {
+ reconnect_transition__(fsm, now, S_ACTIVE);
+ }
+ fsm->last_received = now;
+}
+
+static void
+reconnect_transition__(struct reconnect *fsm, long long int now,
+ enum state state)
+{
+ if (fsm->state == S_CONNECTING) {
+ fsm->n_attempted_connections++;
+ if (state == S_ACTIVE) {
+ fsm->n_successful_connections++;
+ }
+ }
+ if (is_connected_state(fsm->state) != is_connected_state(state)) {
+ if (is_connected_state(fsm->state)) {
+ fsm->total_connected_duration += now - fsm->last_connected;
+ }
+ fsm->seqno++;
+ }
+
+ VLOG_DBG("%s: entering %s", fsm->name, reconnect_state_name__(state));
+ fsm->state = state;
+ fsm->state_entered = now;
+}
+
+static long long int
+reconnect_deadline__(const struct reconnect *fsm)
+{
+ assert(fsm->state_entered != LLONG_MIN);
+ switch (fsm->state) {
+ case S_VOID:
+ return LLONG_MAX;
+
+ case S_BACKOFF:
+ return fsm->state_entered + fsm->backoff;
+
+ case S_CONNECTING:
+ return fsm->state_entered + MAX(1000, fsm->backoff);
+
+ case S_ACTIVE:
+ if (fsm->probe_interval) {
+ long long int base = MAX(fsm->last_received, fsm->state_entered);
+ return base + fsm->probe_interval;
+ }
+ return LLONG_MAX;
+
+ case S_IDLE:
+ return fsm->state_entered + fsm->probe_interval;
+
+ case S_RECONNECT:
+ return fsm->state_entered;
+ }
+
+ NOT_REACHED();
+}
+
+/* Assesses whether any action should be taken on 'fsm'. The return value is
+ * one of:
+ *
+ * - 0: The client need not take any action.
+ *
+ * - RECONNECT_CONNECT: The client should start a connection attempt and
+ * indicate this by calling reconnect_connecting(). If the connection
+ * attempt has definitely succeeded, it should call
+ * reconnect_connected(). If the connection attempt has definitely
+ * failed, it should call reconnect_connect_failed().
+ *
+ * The FSM is smart enough to back off correctly after successful
+ * connections that quickly abort, so it is OK to call
+ * reconnect_connected() after a low-level successful connection
+ * (e.g. connect()) even if the connection might soon abort due to a
+ * failure at a high-level (e.g. SSL negotiation failure).
+ *
+ * - RECONNECT_DISCONNECT: The client should abort the current connection
+ * or connection attempt and call reconnect_disconnected() or
+ * reconnect_connect_failed() to indicate it.
+ *
+ * - RECONNECT_PROBE: The client should send some kind of request to the
+ * peer that will elicit a response, to ensure that the connection is
+ * indeed in working order. (This will only be returned if the "probe
+ * interval" is nonzero--see reconnect_set_probe_interval()).
+ */
+enum reconnect_action
+reconnect_run(struct reconnect *fsm, long long int now)
+{
+ if (now >= reconnect_deadline__(fsm)) {
+ switch (fsm->state) {
+ case S_VOID:
+ return 0;
+
+ case S_BACKOFF:
+ return RECONNECT_CONNECT;
+
+ case S_CONNECTING:
+ return RECONNECT_DISCONNECT;
+
+ case S_ACTIVE:
+ VLOG_DBG("%s: idle %lld ms, sending inactivity probe", fsm->name,
+ now - MAX(fsm->last_received, fsm->state_entered));
+ reconnect_transition__(fsm, now, S_IDLE);
+ return RECONNECT_PROBE;
+
+ case S_IDLE:
+ VLOG_ERR("%s: no response to inactivity probe after %.3g "
+ "seconds, disconnecting",
+ fsm->name, (now - fsm->state_entered) / 1000.0);
+ return RECONNECT_DISCONNECT;
+
+ case S_RECONNECT:
+ return RECONNECT_DISCONNECT;
+ }
+
+ NOT_REACHED();
+ } else {
+ return fsm->state == S_CONNECTING ? RECONNECT_CONNECT : 0;
+ }
+}
+
+/* Causes the next call to poll_block() to wake up when reconnect_run() should
+ * be called on 'fsm'. */
+void
+reconnect_wait(struct reconnect *fsm, long long int now)
+{
+ int timeout = reconnect_timeout(fsm, now);
+ if (timeout >= 0) {
+ poll_timer_wait(timeout);
+ }
+}
+
+/* Returns the number of milliseconds after which reconnect_run() should be
+ * called on 'fsm' if nothing else notable happens in the meantime, or a
+ * negative number if this is currently unnecessary. */
+int
+reconnect_timeout(struct reconnect *fsm, long long int now)
+{
+ long long int deadline = reconnect_deadline__(fsm);
+ if (deadline != LLONG_MAX) {
+ long long int remaining = deadline - now;
+ return MAX(0, MIN(INT_MAX, remaining));
+ }
+ return -1;
+}
+
+/* Returns true if 'fsm' is currently believed to be connected, that is, if
+ * reconnect_connected() was called more recently than any call to
+ * reconnect_connect_failed() or reconnect_disconnected() or
+ * reconnect_disable(), and false otherwise. */
+bool
+reconnect_is_connected(const struct reconnect *fsm)
+{
+ return is_connected_state(fsm->state);
+}
+
+/* Returns the number of milliseconds for which 'fsm' has been continuously
+ * connected to its peer. (If 'fsm' is not currently connected, this is 0.) */
+unsigned int
+reconnect_get_connection_duration(const struct reconnect *fsm,
+ long long int now)
+{
+ return reconnect_is_connected(fsm) ? now - fsm->last_connected : 0;
+}
+
+/* Copies various statistics for 'fsm' into '*stats'. */
+void
+reconnect_get_stats(const struct reconnect *fsm, long long int now,
+ struct reconnect_stats *stats)
+{
+ stats->creation_time = fsm->creation_time;
+ stats->last_received = fsm->last_received;
+ stats->last_connected = fsm->last_connected;
+ stats->backoff = fsm->backoff;
+ stats->seqno = fsm->seqno;
+ stats->is_connected = reconnect_is_connected(fsm);
+ stats->current_connection_duration
+ = reconnect_get_connection_duration(fsm, now);
+ stats->total_connected_duration = (stats->current_connection_duration
+ + fsm->total_connected_duration);
+ stats->n_attempted_connections = fsm->n_attempted_connections;
+ stats->n_successful_connections = fsm->n_successful_connections;
+ stats->state = reconnect_state_name__(fsm->state);
+ stats->state_elapsed = now - fsm->state_entered;
+}
--- /dev/null
+/*
+ * Copyright (c) 2009 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.
+ */
+
+#ifndef RECONNECT_H
+#define RECONNECT_H 1
+
+/* This library implements a finite-state machine for connecting and
+ * reconnecting to a network resource with exponential backoff. It also
+ * provides optional support for detecting a connection on which the peer is no
+ * longer responding.
+ *
+ * The library does not implement anything networking related, only an FSM for
+ * networking code to use.
+ *
+ * Many "reconnect" functions take a "now" argument. This makes testing easier
+ * since there is no hidden state. When not testing, just pass the return
+ * value of time_msec() from timeval.h. (Perhaps this design should be
+ * revisited later.) */
+
+#include <stdbool.h>
+
+struct reconnect *reconnect_create(long long int now);
+void reconnect_destroy(struct reconnect *);
+
+const char *reconnect_get_name(const struct reconnect *);
+void reconnect_set_name(struct reconnect *, const char *name);
+
+int reconnect_get_min_backoff(const struct reconnect *);
+int reconnect_get_max_backoff(const struct reconnect *);
+int reconnect_get_probe_interval(const struct reconnect *);
+
+void reconnect_set_backoff(struct reconnect *,
+ int min_backoff, int max_backoff);
+void reconnect_set_probe_interval(struct reconnect *, int probe_interval);
+
+bool reconnect_is_enabled(const struct reconnect *);
+void reconnect_enable(struct reconnect *, long long int now);
+void reconnect_disable(struct reconnect *, long long int now);
+
+void reconnect_force_reconnect(struct reconnect *, long long int now);
+
+bool reconnect_is_connected(const struct reconnect *);
+unsigned int reconnect_get_connection_duration(const struct reconnect *,
+ long long int now);
+
+void reconnect_disconnected(struct reconnect *, long long int now, int error);
+void reconnect_connecting(struct reconnect *, long long int now);
+void reconnect_connected(struct reconnect *, long long int now);
+void reconnect_connect_failed(struct reconnect *, long long int now,
+ int error);
+void reconnect_received(struct reconnect *, long long int now);
+
+enum reconnect_action {
+ RECONNECT_CONNECT = 1,
+ RECONNECT_DISCONNECT,
+ RECONNECT_PROBE,
+};
+enum reconnect_action reconnect_run(struct reconnect *, long long int now);
+void reconnect_wait(struct reconnect *, long long int now);
+int reconnect_timeout(struct reconnect *, long long int now);
+
+struct reconnect_stats {
+ /* All times and durations in this structure are in milliseconds. */
+ long long int creation_time; /* Time reconnect_create() called. */
+ long long int last_received; /* Last call to reconnect_received(). */
+ long long int last_connected; /* Last call to reconnect_connected(). */
+ int backoff; /* Current backoff duration. */
+
+ unsigned int seqno; /* # of connections + # of disconnections. */
+
+ bool is_connected; /* Currently connected? */
+ unsigned int current_connection_duration; /* Time of current connection. */
+ unsigned int total_connected_duration; /* Sum of all connections. */
+ unsigned int n_attempted_connections;
+ unsigned int n_successful_connections;
+
+ /* These should only be provided to a human user for debugging purposes.
+ * The client should not attempt to interpret them. */
+ const char *state; /* FSM state. */
+ unsigned int state_elapsed; /* Time since FSM state entered. */
+};
+
+void reconnect_get_stats(const struct reconnect *, long long int now,
+ struct reconnect_stats *);
+
+#endif /* reconnect.h */
#include <config.h>
#include "sha1.h"
+#include <ctype.h>
#include <string.h>
+#include "util.h"
/* a bit faster & bigger, if defined */
#define UNROLL_LOOPS
sha1_update(&ctx, data, n);
sha1_final(&ctx, digest);
}
+
+void
+sha1_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE],
+ char hex[SHA1_HEX_DIGEST_LEN + 1])
+{
+ int i;
+
+ for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
+ *hex++ = "0123456789abcdef"[digest[i] >> 4];
+ *hex++ = "0123456789abcdef"[digest[i] & 15];
+ }
+ *hex = '\0';
+}
+
+bool
+sha1_from_hex(uint8_t digest[SHA1_DIGEST_SIZE], const char *hex)
+{
+ int i;
+
+ for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
+ if (!isxdigit(hex[0]) || !isxdigit(hex[1])) {
+ return false;
+ }
+ digest[i] = (hexit_value(hex[0]) << 4) | hexit_value(hex[1]);
+ hex += 2;
+ }
+ return true;
+}
+
#ifndef SHA1_H
#define SHA1_H
+#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
-/* Size of the SHA1 digest. */
-#define SHA1_DIGEST_SIZE 20
+#define SHA1_DIGEST_SIZE 20 /* Size of the SHA1 digest. */
+#define SHA1_HEX_DIGEST_LEN 40 /* Length of SHA1 digest as hex in ASCII. */
/* SHA1 context structure. */
struct sha1_ctx {
void sha1_final(struct sha1_ctx *, uint8_t digest[SHA1_DIGEST_SIZE]);
void sha1_bytes(const void *, size_t, uint8_t digest[SHA1_DIGEST_SIZE]);
+#define SHA1_FMT \
+ "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" \
+ "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
+#define SHA1_ARGS(DIGEST) \
+ ((DIGEST)[0]), ((DIGEST)[1]), ((DIGEST)[2]), ((DIGEST)[3]), \
+ ((DIGEST)[4]), ((DIGEST)[5]), ((DIGEST)[6]), ((DIGEST)[7]), \
+ ((DIGEST)[8]), ((DIGEST)[9]), ((DIGEST)[10]), ((DIGEST)[11]), \
+ ((DIGEST)[12]), ((DIGEST)[13]), ((DIGEST)[14]), ((DIGEST)[15]), \
+ ((DIGEST)[16]), ((DIGEST)[17]), ((DIGEST)[18]), ((DIGEST)[19])
+
+void sha1_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE],
+ char hex[SHA1_HEX_DIGEST_LEN + 1]);
+bool sha1_from_hex(uint8_t digest[SHA1_DIGEST_SIZE], const char *hex);
+
#endif /* sha1.h */
return hmap_is_empty(&shash->map);
}
+size_t
+shash_count(const struct shash *shash)
+{
+ return hmap_count(&shash->map);
+}
+
/* It is the caller's responsibility to avoid duplicate names, if that is
* desirable. */
struct shash_node *
-shash_add(struct shash *sh, const char *name, void *data)
+shash_add(struct shash *sh, const char *name, const void *data)
{
struct shash_node *node = xmalloc(sizeof *node);
node->name = xstrdup(name);
- node->data = data;
+ node->data = (void *) data;
hmap_insert(&sh->map, &node->node, hash_name(name));
return node;
}
return node ? node->data : NULL;
}
+void *
+shash_find_and_delete(struct shash *sh, const char *name)
+{
+ struct shash_node *node = shash_find(sh, name);
+ if (node) {
+ void *data = node->data;
+ shash_delete(sh, node);
+ return data;
+ } else {
+ return NULL;
+ }
+}
+
struct shash_node *
shash_first(const struct shash *shash)
{
return node ? CONTAINER_OF(node, struct shash_node, node) : NULL;
}
+static int
+compare_nodes_by_name(const void *a_, const void *b_)
+{
+ const struct shash_node *const *a = a_;
+ const struct shash_node *const *b = b_;
+ return strcmp((*a)->name, (*b)->name);
+}
+
+const struct shash_node **
+shash_sort(const struct shash *sh)
+{
+ if (shash_is_empty(sh)) {
+ return NULL;
+ } else {
+ const struct shash_node **nodes;
+ struct shash_node *node;
+ size_t i, n;
+
+ n = shash_count(sh);
+ nodes = xmalloc(n * sizeof *nodes);
+ i = 0;
+ SHASH_FOR_EACH (node, sh) {
+ nodes[i++] = node;
+ }
+ assert(i == n);
+
+ qsort(nodes, n, sizeof *nodes, compare_nodes_by_name);
+
+ return nodes;
+ }
+}
void shash_destroy(struct shash *);
void shash_clear(struct shash *);
bool shash_is_empty(const struct shash *);
-struct shash_node *shash_add(struct shash *, const char *, void *);
+size_t shash_count(const struct shash *);
+struct shash_node *shash_add(struct shash *, const char *, const void *);
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 *);
#endif /* shash.h */
--- /dev/null
+/* Copyright (c) 2009 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 "sort.h"
+
+#include "random.h"
+
+static size_t
+partition(size_t p, size_t r,
+ int (*compare)(size_t a, size_t b, void *aux),
+ void (*swap)(size_t a, size_t b, void *aux),
+ void *aux)
+{
+ size_t x = r - 1;
+ size_t i, j;
+
+ i = p;
+ for (j = p; j < x; j++) {
+ if (compare(j, x, aux) <= 0) {
+ swap(i++, j, aux);
+ }
+ }
+ swap(i, x, aux);
+ return i;
+}
+
+static void
+quicksort(size_t p, size_t r,
+ int (*compare)(size_t a, size_t b, void *aux),
+ void (*swap)(size_t a, size_t b, void *aux),
+ void *aux)
+{
+ size_t i, q;
+
+ if (r - p < 2) {
+ return;
+ }
+
+ i = random_range(r - p) + p;
+ if (r - 1 != i) {
+ swap(r - 1, i, aux);
+ }
+
+ q = partition(p, r, compare, swap, aux);
+ quicksort(p, q, compare, swap, aux);
+ quicksort(q, r, compare, swap, aux);
+}
+
+void
+sort(size_t count,
+ int (*compare)(size_t a, size_t b, void *aux),
+ void (*swap)(size_t a, size_t b, void *aux),
+ void *aux)
+{
+ quicksort(0, count, compare, swap, aux);
+}
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef SORT_H
+#define SORT_H 1
+
+#include <stddef.h>
+
+void sort(size_t count,
+ int (*compare)(size_t a, size_t b, void *aux),
+ void (*swap)(size_t a, size_t b, void *aux),
+ void *aux);
+
+#endif /* sort.h */
struct stp *stp;
struct stp_port *p;
- stp = xcalloc(1, sizeof *stp);
+ stp = xzalloc(sizeof *stp);
stp->name = xstrdup(name);
stp->bridge_id = bridge_id;
if (!(stp->bridge_id >> 48)) {
--- /dev/null
+/*
+ * Copyright (c) 2008, 2009 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-fd.h"
+#include <assert.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "fatal-signal.h"
+#include "leak-checker.h"
+#include "poll-loop.h"
+#include "socket-util.h"
+#include "util.h"
+#include "stream-provider.h"
+#include "stream.h"
+
+#include "vlog.h"
+#define THIS_MODULE VLM_stream_fd
+
+/* Active file descriptor stream. */
+
+struct stream_fd
+{
+ struct stream stream;
+ int fd;
+ char *unlink_path;
+};
+
+static struct stream_class stream_fd_class;
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25);
+
+static void maybe_unlink_and_free(char *path);
+
+/* Creates a new stream named 'name' that will send and receive data on 'fd'
+ * and stores a pointer to the stream in '*streamp'. Initial connection status
+ * 'connect_status' is interpreted as described for stream_init().
+ *
+ * When '*streamp' 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_fd_stream(const char *name, int fd, int connect_status,
+ char *unlink_path, struct stream **streamp)
+{
+ struct stream_fd *s;
+
+ s = xmalloc(sizeof *s);
+ stream_init(&s->stream, &stream_fd_class, connect_status, name);
+ s->fd = fd;
+ s->unlink_path = unlink_path;
+ *streamp = &s->stream;
+ return 0;
+}
+
+static struct stream_fd *
+stream_fd_cast(struct stream *stream)
+{
+ stream_assert_class(stream, &stream_fd_class);
+ return CONTAINER_OF(stream, struct stream_fd, stream);
+}
+
+static void
+fd_close(struct stream *stream)
+{
+ struct stream_fd *s = stream_fd_cast(stream);
+ close(s->fd);
+ maybe_unlink_and_free(s->unlink_path);
+ free(s);
+}
+
+static int
+fd_connect(struct stream *stream)
+{
+ struct stream_fd *s = stream_fd_cast(stream);
+ return check_connection_completion(s->fd);
+}
+
+static ssize_t
+fd_recv(struct stream *stream, void *buffer, size_t n)
+{
+ struct stream_fd *s = stream_fd_cast(stream);
+ ssize_t retval = read(s->fd, buffer, n);
+ return retval >= 0 ? retval : -errno;
+}
+
+static ssize_t
+fd_send(struct stream *stream, const void *buffer, size_t n)
+{
+ struct stream_fd *s = stream_fd_cast(stream);
+ ssize_t retval = write(s->fd, buffer, n);
+ return (retval > 0 ? retval
+ : retval == 0 ? -EAGAIN
+ : -errno);
+}
+
+static void
+fd_wait(struct stream *stream, enum stream_wait_type wait)
+{
+ struct stream_fd *s = stream_fd_cast(stream);
+ switch (wait) {
+ case STREAM_CONNECT:
+ case STREAM_SEND:
+ poll_fd_wait(s->fd, POLLOUT);
+ break;
+
+ case STREAM_RECV:
+ poll_fd_wait(s->fd, POLLIN);
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+}
+
+static struct stream_class stream_fd_class = {
+ "fd", /* name */
+ NULL, /* open */
+ fd_close, /* close */
+ fd_connect, /* connect */
+ fd_recv, /* recv */
+ fd_send, /* send */
+ fd_wait, /* wait */
+};
+\f
+/* Passive file descriptor stream. */
+
+struct fd_pstream
+{
+ struct pstream pstream;
+ int fd;
+ int (*accept_cb)(int fd, const struct sockaddr *, size_t sa_len,
+ struct stream **);
+ char *unlink_path;
+};
+
+static struct pstream_class fd_pstream_class;
+
+static struct fd_pstream *
+fd_pstream_cast(struct pstream *pstream)
+{
+ pstream_assert_class(pstream, &fd_pstream_class);
+ return CONTAINER_OF(pstream, struct fd_pstream, pstream);
+}
+
+/* Creates a new pstream named 'name' that will accept new socket connections
+ * on 'fd' and stores a pointer to the stream in '*pstreamp'.
+ *
+ * When a connection has been accepted, 'accept_cb' will be called with the new
+ * socket fd 'fd' and the remote address of the connection 'sa' and 'sa_len'.
+ * accept_cb must return 0 if the connection is successful, in which case it
+ * must initialize '*streamp' to the new stream, or a positive errno value on
+ * error. In either case accept_cb takes ownership of the 'fd' passed in.
+ *
+ * When '*pstreamp' is closed, then 'unlink_path' (if nonnull) will be passed
+ * to fatal_signal_unlink_file_now() and freed with free().
+ *
+ * Returns 0 if successful, otherwise a positive errno value. (The current
+ * implementation never fails.) */
+int
+new_fd_pstream(const char *name, int fd,
+ int (*accept_cb)(int fd, const struct sockaddr *sa,
+ size_t sa_len, struct stream **streamp),
+ char *unlink_path, struct pstream **pstreamp)
+{
+ struct fd_pstream *ps = xmalloc(sizeof *ps);
+ pstream_init(&ps->pstream, &fd_pstream_class, name);
+ ps->fd = fd;
+ ps->accept_cb = accept_cb;
+ ps->unlink_path = unlink_path;
+ *pstreamp = &ps->pstream;
+ return 0;
+}
+
+static void
+pfd_close(struct pstream *pstream)
+{
+ struct fd_pstream *ps = fd_pstream_cast(pstream);
+ close(ps->fd);
+ maybe_unlink_and_free(ps->unlink_path);
+ free(ps);
+}
+
+static int
+pfd_accept(struct pstream *pstream, struct stream **new_streamp)
+{
+ struct fd_pstream *ps = fd_pstream_cast(pstream);
+ struct sockaddr_storage ss;
+ socklen_t ss_len = sizeof ss;
+ int new_fd;
+ int retval;
+
+ new_fd = accept(ps->fd, (struct sockaddr *) &ss, &ss_len);
+ if (new_fd < 0) {
+ int retval = errno;
+ if (retval != EAGAIN) {
+ VLOG_DBG_RL(&rl, "accept: %s", strerror(retval));
+ }
+ return retval;
+ }
+
+ retval = set_nonblocking(new_fd);
+ if (retval) {
+ close(new_fd);
+ return retval;
+ }
+
+ return ps->accept_cb(new_fd, (const struct sockaddr *) &ss, ss_len,
+ new_streamp);
+}
+
+static void
+pfd_wait(struct pstream *pstream)
+{
+ struct fd_pstream *ps = fd_pstream_cast(pstream);
+ poll_fd_wait(ps->fd, POLLIN);
+}
+
+static struct pstream_class fd_pstream_class = {
+ "pstream",
+ NULL,
+ pfd_close,
+ pfd_accept,
+ pfd_wait
+};
+\f
+/* Helper functions. */
+static void
+maybe_unlink_and_free(char *path)
+{
+ if (path) {
+ fatal_signal_unlink_file_now(path);
+ free(path);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2008, 2009 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.
+ */
+
+#ifndef STREAM_FD_H
+#define STREAM_FD_H 1
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+struct stream;
+struct pstream;
+struct sockaddr;
+
+int new_fd_stream(const char *name, int fd, int connect_status,
+ char *unlink_path, struct stream **streamp);
+int new_fd_pstream(const char *name, int fd,
+ int (*accept_cb)(int fd, const struct sockaddr *,
+ size_t sa_len, struct stream **),
+ char *unlink_path,
+ struct pstream **pstreamp);
+
+#endif /* stream-fd.h */
--- /dev/null
+/*
+ * Copyright (c) 2009 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.
+ */
+
+#ifndef STREAM_PROVIDER_H
+#define STREAM_PROVIDER_H 1
+
+#include <assert.h>
+#include <sys/types.h>
+#include "stream.h"
+
+/* Active stream connection. */
+
+/* Active stream connection.
+ *
+ * This structure should be treated as opaque by implementation. */
+struct stream {
+ struct stream_class *class;
+ int state;
+ int error;
+ uint32_t remote_ip;
+ uint16_t remote_port;
+ uint32_t local_ip;
+ uint16_t local_port;
+ char *name;
+};
+
+void stream_init(struct stream *, struct stream_class *, int connect_status,
+ const char *name);
+void stream_set_remote_ip(struct stream *, uint32_t remote_ip);
+void stream_set_remote_port(struct stream *, uint16_t remote_port);
+void stream_set_local_ip(struct stream *, uint32_t local_ip);
+void stream_set_local_port(struct stream *, uint16_t local_port);
+static inline void stream_assert_class(const struct stream *stream,
+ const struct stream_class *class)
+{
+ assert(stream->class == class);
+}
+
+struct stream_class {
+ /* Prefix for connection names, e.g. "tcp", "ssl", "unix". */
+ const char *name;
+
+ /* Attempts to connect to a peer. 'name' is the full connection name
+ * provided by the user, e.g. "tcp:1.2.3.4". This name is useful for error
+ * messages but must not be modified.
+ *
+ * 'suffix' is a copy of 'name' following the colon and may be modified.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. If
+ * successful, stores a pointer to the new connection in '*streamp'.
+ *
+ * The open function must not block waiting for a connection to complete.
+ * If the connection cannot be completed immediately, it should return
+ * EAGAIN (not EINPROGRESS, as returned by the connect system call) and
+ * continue the connection in the background. */
+ int (*open)(const char *name, char *suffix, struct stream **streamp);
+
+ /* Closes 'stream' and frees associated memory. */
+ void (*close)(struct stream *stream);
+
+ /* Tries to complete the connection on '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.
+ *
+ * The connect function must not block waiting for the connection to
+ * complete; instead, it should return EAGAIN immediately. */
+ int (*connect)(struct stream *stream);
+
+ /* 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.
+ *
+ * The recv function will not be passed a zero 'n'.
+ *
+ * The recv function must not block waiting for data to arrive. If no data
+ * have been received, it should return -EAGAIN immediately. */
+ ssize_t (*recv)(struct stream *stream, void *buffer, size_t 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').
+ *
+ * - On error, a negative errno value.
+ *
+ * - Never returns 0.
+ *
+ * The send function will not be passed a zero 'n'.
+ *
+ * The send function must not block. If no bytes can be immediately
+ * accepted for transmission, it should return -EAGAIN immediately. */
+ ssize_t (*send)(struct stream *stream, const void *buffer, size_t n);
+
+ /* Arranges for the poll loop to wake up when 'stream' is ready to take an
+ * action of the given 'type'. */
+ void (*wait)(struct stream *stream, enum stream_wait_type type);
+};
+\f
+/* Passive listener for incoming stream connections.
+ *
+ * This structure should be treated as opaque by stream implementations. */
+struct pstream {
+ struct pstream_class *class;
+ char *name;
+};
+
+void pstream_init(struct pstream *, struct pstream_class *, const char *name);
+static inline void pstream_assert_class(const struct pstream *pstream,
+ const struct pstream_class *class)
+{
+ assert(pstream->class == class);
+}
+
+struct pstream_class {
+ /* Prefix for connection names, e.g. "ptcp", "pssl", "punix". */
+ const char *name;
+
+ /* Attempts to start listening for stream connections. 'name' is the full
+ * connection name provided by the user, e.g. "ptcp:1234". This name is
+ * useful for error messages but must not be modified.
+ *
+ * 'suffix' is a copy of 'name' following the colon and may be modified.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. If
+ * successful, stores a pointer to the new connection in '*pstreamp'.
+ *
+ * The listen function must not block. If the connection cannot be
+ * completed immediately, it should return EAGAIN (not EINPROGRESS, as
+ * returned by the connect system call) and continue the connection in the
+ * background. */
+ int (*listen)(const char *name, char *suffix, struct pstream **pstreamp);
+
+ /* Closes 'pstream' and frees associated memory. */
+ void (*close)(struct pstream *pstream);
+
+ /* Tries to accept a new connection on 'pstream'. If successful, stores
+ * the new connection in '*new_streamp' and returns 0. Otherwise, returns
+ * a positive errno value.
+ *
+ * The accept function must not block waiting for a connection. If no
+ * connection is ready to be accepted, it should return EAGAIN. */
+ int (*accept)(struct pstream *pstream, struct stream **new_streamp);
+
+ /* Arranges for the poll loop to wake up when a connection is ready to be
+ * accepted on 'pstream'. */
+ void (*wait)(struct pstream *pstream);
+};
+
+/* Active and passive stream classes. */
+extern struct stream_class tcp_stream_class;
+extern struct pstream_class ptcp_pstream_class;
+extern struct stream_class unix_stream_class;
+extern struct pstream_class punix_pstream_class;
+
+#endif /* stream-provider.h */
--- /dev/null
+/*
+ * Copyright (c) 2008, 2009 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, /* 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 UNUSED, char *suffix, struct pstream **pstreamp)
+{
+ int fd;
+
+ fd = inet_open_passive(SOCK_STREAM, suffix, 0);
+ if (fd < 0) {
+ return -fd;
+ } else {
+ return new_fd_pstream("ptcp", 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
+};
+
--- /dev/null
+/*
+ * Copyright (c) 2008, 2009 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, /* 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 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;
+ }
+
+ error = set_nonblocking(fd);
+ if (error) {
+ close(fd);
+ return error;
+ }
+
+ 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
+};
+
--- /dev/null
+/*
+ * Copyright (c) 2008, 2009 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,
+};
+
+static struct pstream_class *pstream_classes[] = {
+ &ptcp_pstream_class,
+ &punix_pstream_class,
+};
+
+/* 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->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)
+{
+ /* 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");
+ 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");
+ printf(" punix:FILE "
+ "listen on Unix domain socket FILE\n");
+ }
+}
+
+/* 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_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));
+}
+
+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);
+}
+
+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);
+}
--- /dev/null
+/*
+ * Copyright (c) 2009 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.
+ */
+
+#ifndef STREAM_H
+#define STREAM_H 1
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "flow.h"
+
+struct pstream;
+struct stream;
+
+void stream_usage(const char *name, bool active, bool passive);
+
+/* Bidirectional byte streams. */
+int stream_open(const char *name, struct stream **);
+int stream_open_block(const char *name, struct stream **);
+void stream_close(struct stream *);
+const char *stream_get_name(const struct stream *);
+uint32_t stream_get_remote_ip(const struct stream *);
+uint16_t stream_get_remote_port(const struct stream *);
+uint32_t stream_get_local_ip(const struct stream *);
+uint16_t stream_get_local_port(const struct stream *);
+int stream_connect(struct stream *);
+int stream_recv(struct stream *, void *buffer, size_t n);
+int stream_send(struct stream *, const void *buffer, size_t n);
+
+enum stream_wait_type {
+ STREAM_CONNECT,
+ STREAM_RECV,
+ STREAM_SEND
+};
+void stream_wait(struct stream *, enum stream_wait_type);
+void stream_connect_wait(struct stream *);
+void stream_recv_wait(struct stream *);
+void stream_send_wait(struct stream *);
+
+/* Passive streams: listeners for incoming stream connections. */
+int pstream_open(const char *name, struct pstream **);
+const char *pstream_get_name(const struct pstream *);
+void pstream_close(struct pstream *);
+int pstream_accept(struct pstream *, struct stream **);
+int pstream_accept_block(struct pstream *, struct stream **);
+void pstream_wait(struct pstream *);
+
+#endif /* stream.h */
svec->n = 0;
}
+bool
+svec_is_empty(const struct svec *svec)
+{
+ return svec->n == 0;
+}
+
void
svec_add(struct svec *svec, const char *name)
{
void svec_clone(struct svec *, const struct svec *);
void svec_destroy(struct svec *);
void svec_clear(struct svec *);
+bool svec_is_empty(const struct svec *);
void svec_add(struct svec *, const char *);
void svec_add_nocopy(struct svec *, char *);
void svec_del(struct svec *, const char *);
static time_t deadline = TIME_MIN;
static void set_up_timer(void);
+static void set_up_signal(int flags);
static void sigalrm_handler(int);
static void refresh_if_ticked(void);
static time_t time_add(time_t, time_t);
static void unblock_sigalrm(const sigset_t *);
static void log_poll_interval(long long int last_wakeup,
const struct rusage *last_rusage);
-static long long int timeval_to_msec(const struct timeval *);
/* Initializes the timetracking module. */
void
time_init(void)
{
- struct sigaction sa;
if (inited) {
return;
}
gettimeofday(&now, NULL);
tick = false;
- /* Set up signal handler. */
+ set_up_signal(SA_RESTART);
+ set_up_timer();
+}
+
+static void
+set_up_signal(int flags)
+{
+ struct sigaction sa;
+
memset(&sa, 0, sizeof sa);
sa.sa_handler = sigalrm_handler;
sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
+ sa.sa_flags = flags;
if (sigaction(SIGALRM, &sa, NULL)) {
ovs_fatal(errno, "sigaction(SIGALRM) failed");
}
+}
- /* Set up periodic signal. */
- set_up_timer();
+/* Remove SA_RESTART from the flags for SIGALRM, so that any system call that
+ * is interrupted by the periodic timer interrupt will return EINTR instead of
+ * continuing after the signal handler returns.
+ *
+ * time_disable_restart() and time_enable_restart() may be usefully wrapped
+ * around function calls that might otherwise block forever unless interrupted
+ * by a signal, e.g.:
+ *
+ * time_disable_restart();
+ * fcntl(fd, F_SETLKW, &lock);
+ * time_enable_restart();
+ */
+void
+time_disable_restart(void)
+{
+ set_up_signal(0);
+}
+
+/* Add SA_RESTART to the flags for SIGALRM, so that any system call that
+ * is interrupted by the periodic timer interrupt will continue after the
+ * signal handler returns instead of returning EINTR. */
+void
+time_enable_restart(void)
+{
+ set_up_signal(SA_RESTART);
}
static void
}
}
-static long long int
+long long int
timeval_to_msec(const struct timeval *tv)
{
return (long long int) tv->tv_sec * 1000 + tv->tv_usec / 1000;
#define TIME_UPDATE_INTERVAL 100
void time_init(void);
+void time_disable_restart(void);
+void time_enable_restart(void);
void time_postfork(void);
void time_refresh(void);
time_t time_now(void);
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 *);
+
#endif /* timeval.h */
--- /dev/null
+/*
+ * Copyright (c) 2009 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 "unicode.h"
+
+/* Returns the unicode code point corresponding to leading surrogate 'leading'
+ * and trailing surrogate 'trailing'. The return value will not make any
+ * sense if 'leading' or 'trailing' are not in the correct ranges for leading
+ * or trailing surrogates. */
+int
+utf16_decode_surrogate_pair(int leading, int trailing)
+{
+ /*
+ * Leading surrogate: 110110wwwwxxxxxx
+ * Trailing surrogate: 110111xxxxxxxxxx
+ * Code point: 000uuuuuxxxxxxxxxxxxxxxx
+ */
+ int w = (leading >> 6) & 0xf;
+ int u = w + 1;
+ int x0 = leading & 0x3f;
+ int x1 = trailing & 0x3ff;
+ return (u << 16) | (x0 << 10) | x1;
+}
--- /dev/null
+/*
+ * Copyright (c) 2009 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.
+ */
+
+#ifndef UNICODE_H
+#define UNICODE_H 1
+
+#include <stdbool.h>
+
+/* Returns true if 'c' is a Unicode code point, otherwise false. */
+static inline bool
+uc_is_code_point(int c)
+{
+ return c >= 0 && c <= 0x10ffff;
+}
+
+/* Returns true if 'c' is a Unicode code point for a leading surrogate. */
+static inline bool
+uc_is_leading_surrogate(int c)
+{
+ return c >= 0xd800 && c <= 0xdbff;
+}
+
+/* Returns true if 'c' is a Unicode code point for a trailing surrogate. */
+static inline bool
+uc_is_trailing_surrogate(int c)
+{
+ return c >= 0xdc00 && c <= 0xdfff;
+}
+
+/* Returns true if 'c' is a Unicode code point for a leading or trailing
+ * surrogate. */
+static inline bool
+uc_is_surrogate(int c)
+{
+ return c >= 0xd800 && c <= 0xdfff;
+}
+
+int utf16_decode_surrogate_pair(int leading, int trailing);
+
+#endif /* unicode.h */
#include "vlog.h"
\f
struct unixctl_command {
- void (*cb)(struct unixctl_conn *, const char *args);
+ unixctl_cb_func *cb;
+ void *aux;
};
struct unixctl_conn {
static struct shash commands = SHASH_INITIALIZER(&commands);
static void
-unixctl_help(struct unixctl_conn *conn, const char *args UNUSED)
+unixctl_help(struct unixctl_conn *conn, const char *args UNUSED,
+ void *aux UNUSED)
{
struct ds ds = DS_EMPTY_INITIALIZER;
struct shash_node *node;
}
void
-unixctl_command_register(const char *name,
- void (*cb)(struct unixctl_conn *, const char *args))
+unixctl_command_register(const char *name, unixctl_cb_func *cb, void *aux)
{
struct unixctl_command *command;
|| shash_find_data(&commands, name) == cb);
command = xmalloc(sizeof *command);
command->cb = cb;
+ command->aux = aux;
shash_add(&commands, name, command);
}
struct unixctl_server *server;
int error;
- unixctl_command_register("help", unixctl_help);
+ unixctl_command_register("help", unixctl_help, NULL);
server = xmalloc(sizeof *server);
list_init(&server->conns);
command = shash_find_data(&commands, name);
if (command) {
- command->cb(conn, args);
+ command->cb(conn, args, command->aux);
} else {
char *msg = xasprintf("\"%s\" is not a valid command", name);
unixctl_command_reply(conn, 400, msg);
/* Command registration. */
struct unixctl_conn;
+typedef void unixctl_cb_func(struct unixctl_conn *,
+ const char *args, void *aux);
void unixctl_command_register(const char *name,
- void (*cb)(struct unixctl_conn *,
- const char *args));
+ unixctl_cb_func *cb, void *aux);
void unixctl_command_reply(struct unixctl_conn *, int code,
const char *body);
return p;
}
+void *
+xzalloc(size_t size)
+{
+ return xcalloc(1, size);
+}
+
void *
xmalloc(size_t size)
{
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
- if (err_no != 0)
- fprintf(stderr, " (%s)", strerror(err_no));
+ if (err_no != 0) {
+ fprintf(stderr, " (%s)",
+ err_no == EOF ? "end of file" : strerror(err_no));
+ }
putc('\n', stderr);
errno = save_errno;
{
return str_to_llong(s, base, (long long *) ull);
}
+
+/* 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);
+ }
+}
void out_of_memory(void) NO_RETURN;
void *xmalloc(size_t) MALLOC_LIKE;
void *xcalloc(size_t, size_t) MALLOC_LIKE;
+void *xzalloc(size_t) MALLOC_LIKE;
void *xrealloc(void *, size_t);
void *xmemdup(const void *, size_t) MALLOC_LIKE;
char *xmemdup0(const char *, size_t) MALLOC_LIKE;
bool str_to_ulong(const char *, int base, unsigned long *);
bool str_to_ullong(const char *, int base, unsigned long long *);
+bool str_to_double(const char *, double *);
+
+int hexit_value(int c);
+
+char *dir_name(const char *file_name);
+
#ifdef __cplusplus
}
#endif
--- /dev/null
+/* Copyright (c) 2008, 2009 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 "uuid.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "aes128.h"
+#include "sha1.h"
+#include "socket-util.h"
+#include "util.h"
+
+static struct aes128 key;
+static uint64_t counter[2];
+BUILD_ASSERT_DECL(sizeof counter == 16);
+
+static void do_init(void);
+static void read_urandom(void *buffer, size_t n);
+
+/*
+ * Initialize the UUID module. Aborts the program with an error message if
+ * initialization fails (which should never happen on a properly configured
+ * machine.)
+ *
+ * Currently initialization is only needed by uuid_generate(). uuid_generate()
+ * will automatically call uuid_init() itself, so it's only necessary to call
+ * this function explicitly if you want to abort the program earlier than the
+ * first UUID generation in case of failure.
+ */
+void
+uuid_init(void)
+{
+ static bool inited;
+ if (!inited) {
+ do_init();
+ inited = true;
+ }
+}
+
+/* Generates a new random UUID in 'uuid'.
+ *
+ * We go to some trouble to ensure as best we can that the generated UUID has
+ * these properties:
+ *
+ * - Uniqueness. The random number generator is seeded using both the
+ * system clock and the system random number generator, plus a few
+ * other identifiers, which is about as good as we can get in any kind
+ * of simple way.
+ *
+ * - Unpredictability. In some situations it could be bad for an
+ * adversary to be able to guess the next UUID to be generated with some
+ * probability of success. This property may or may not be important
+ * for our purposes, but it is better if we can get it.
+ *
+ * To ensure both of these, we start by taking our seed data and passing it
+ * through SHA-1. We use the result as an AES-128 key. We also generate a
+ * random 16-byte value[*] which we then use as the counter for CTR mode. To
+ * generate a UUID in a manner compliant with the above goals, we merely
+ * increment the counter and encrypt it.
+ *
+ * [*] It is not actually important that the initial value of the counter be
+ * random. AES-128 in counter mode is secure either way.
+ */
+void
+uuid_generate(struct uuid *uuid)
+{
+ uuid_init();
+
+ /* Increment the counter. */
+ if (++counter[1] == 0) {
+ counter[0]++;
+ }
+
+ /* AES output is exactly 16 bytes, so we encrypt directly into 'uuid'. */
+ aes128_encrypt(&key, counter, uuid);
+
+ /* Set bits to indicate a random UUID. See RFC 4122 section 4.4. */
+ uuid->parts[2] &= ~0xc0000000;
+ uuid->parts[2] |= 0x80000000;
+ uuid->parts[1] &= ~0x0000f000;
+ uuid->parts[1] |= 0x00004000;
+}
+
+/* Sets 'uuid' to all-zero-bits. */
+void
+uuid_zero(struct uuid *uuid)
+{
+ uuid->parts[0] = uuid->parts[1] = uuid->parts[2] = uuid->parts[3] = 0;
+}
+
+/* Compares 'a' and 'b'. Returns a negative value if 'a < b', zero if 'a ==
+ * b', or positive if 'a > b'. The ordering is lexicographical order of the
+ * conventional way of writing out UUIDs as strings. */
+int
+uuid_compare_3way(const struct uuid *a, const struct uuid *b)
+{
+ if (a->parts[0] != b->parts[0]) {
+ return a->parts[0] > b->parts[0] ? 1 : -1;
+ } else if (a->parts[1] != b->parts[1]) {
+ return a->parts[1] > b->parts[1] ? 1 : -1;
+ } else if (a->parts[2] != b->parts[2]) {
+ return a->parts[2] > b->parts[2] ? 1 : -1;
+ } else if (a->parts[3] != b->parts[3]) {
+ return a->parts[3] > b->parts[3] ? 1 : -1;
+ } else {
+ return 0;
+ }
+}
+
+/* Attempts to convert string 's' into a UUID in 'uuid'. Returns true if
+ * successful, which will be the case only if 's' has the exact format
+ * specified by RFC 4122. Returns false on failure. On failure, 'uuid' will
+ * be set to all-zero-bits. */
+bool
+uuid_from_string(struct uuid *uuid, const char *s)
+{
+ static const char template[] = "00000000-1111-1111-2222-222233333333";
+ const char *t;
+
+ uuid_zero(uuid);
+ for (t = template; ; t++, s++) {
+ if (*t >= '0' && *t <= '3') {
+ uint32_t *part = &uuid->parts[*t - '0'];
+ if (!isxdigit(*s)) {
+ goto error;
+ }
+ *part = (*part << 4) + hexit_value(*s);
+ } else if (*t != *s) {
+ goto error;
+ } else if (*t == 0) {
+ return true;
+ }
+ }
+
+error:
+ uuid_zero(uuid);
+ return false;
+}
+\f
+static void
+read_urandom(void *buffer, size_t n)
+{
+ static const char urandom[] = "/dev/urandom";
+ size_t bytes_read;
+ int error;
+ int fd;
+
+ fd = open(urandom, O_RDONLY);
+ if (fd < 0) {
+ ovs_fatal(errno, "%s: open failed", urandom);
+ }
+ error = read_fully(fd, buffer, n, &bytes_read);
+ if (error == EOF) {
+ ovs_fatal(0, "%s: unexpected end of file", urandom);
+ } else if (error) {
+ ovs_fatal(error, "%s: read error", urandom);
+ }
+ close(fd);
+}
+
+static void
+do_init(void)
+{
+ uint8_t sha1[SHA1_DIGEST_SIZE];
+ struct sha1_ctx sha1_ctx;
+ uint8_t random_seed[16];
+ struct timeval now;
+ pid_t pid, ppid;
+ uid_t uid;
+ gid_t gid;
+
+ /* Get seed data. */
+ read_urandom(random_seed, sizeof random_seed);
+ if (gettimeofday(&now, NULL)) {
+ ovs_fatal(errno, "gettimeofday failed");
+ }
+ pid = getpid();
+ ppid = getppid();
+ uid = getuid();
+ gid = getgid();
+
+ /* Convert seed into key. */
+ sha1_init(&sha1_ctx);
+ sha1_update(&sha1_ctx, random_seed, sizeof random_seed);
+ sha1_update(&sha1_ctx, &pid, sizeof pid);
+ sha1_update(&sha1_ctx, &ppid, sizeof ppid);
+ sha1_update(&sha1_ctx, &uid, sizeof uid);
+ sha1_update(&sha1_ctx, &gid, sizeof gid);
+ sha1_final(&sha1_ctx, sha1);
+
+ /* Generate key. */
+ BUILD_ASSERT(sizeof sha1 >= 16);
+ aes128_schedule(&key, sha1);
+
+ /* Generate initial counter. */
+ read_urandom(counter, sizeof counter);
+}
--- /dev/null
+/* Copyright (c) 2008, 2009 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.
+ */
+
+#ifndef UUID_H
+#define UUID_H 1
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include "util.h"
+
+#define UUID_BIT 128 /* Number of bits in a UUID. */
+#define UUID_OCTET (UUID_BIT / 8) /* Number of bytes in a UUID. */
+
+/* A Universally Unique IDentifier (UUID) compliant with RFC 4122.
+ *
+ * Each of the parts is stored in host byte order, but the parts themselves are
+ * ordered from left to right. That is, (parts[0] >> 24) is the first 8 bits
+ * of the UUID when output in the standard form, and (parts[3] & 0xff) is the
+ * final 8 bits. */
+struct uuid {
+ uint32_t parts[4];
+};
+BUILD_ASSERT_DECL(sizeof(struct uuid) == UUID_OCTET);
+
+/* Formats a UUID as a string, in the conventional format.
+ *
+ * Example:
+ * struct uuid uuid = ...;
+ * printf("This UUID is "UUID_FMT"\n", UUID_ARGS(&uuid));
+ *
+ */
+#define UUID_LEN 36
+#define UUID_FMT "%08x-%04x-%04x-%04x-%04x%08x"
+#define UUID_ARGS(UUID) \
+ ((unsigned int) ((UUID)->parts[0])), \
+ ((unsigned int) ((UUID)->parts[1] >> 16)), \
+ ((unsigned int) ((UUID)->parts[1] & 0xffff)), \
+ ((unsigned int) ((UUID)->parts[2] >> 16)), \
+ ((unsigned int) ((UUID)->parts[2] & 0xffff)), \
+ ((unsigned int) ((UUID)->parts[3]))
+
+/* Returns a hash value for 'uuid'. This hash value is the same regardless of
+ * whether we are running on a 32-bit or 64-bit or big-endian or little-endian
+ * architecture. */
+static inline size_t
+uuid_hash(const struct uuid *uuid)
+{
+ return uuid->parts[0];
+}
+
+/* Returns true if 'a == b', false otherwise. */
+static inline bool
+uuid_equals(const struct uuid *a, const struct uuid *b)
+{
+ return (a->parts[0] == b->parts[0]
+ && a->parts[1] == b->parts[1]
+ && a->parts[2] == b->parts[2]
+ && a->parts[3] == b->parts[3]);
+}
+
+void uuid_init(void);
+void uuid_generate(struct uuid *);
+void uuid_zero(struct uuid *);
+int uuid_compare_3way(const struct uuid *, const struct uuid *);
+bool uuid_from_string(struct uuid *, const char *);
+
+#endif /* vswitchd/cfgdb.h */
VLOG_MODULE(fault)
VLOG_MODULE(flow)
VLOG_MODULE(in_band)
+VLOG_MODULE(jsonrpc)
VLOG_MODULE(leak_checker)
VLOG_MODULE(learning_switch)
+VLOG_MODULE(lockfile)
VLOG_MODULE(mac_learning)
VLOG_MODULE(mgmt)
VLOG_MODULE(netdev)
VLOG_MODULE(ovs_discover)
VLOG_MODULE(ofproto)
VLOG_MODULE(openflowd)
+VLOG_MODULE(ovsdb_client)
+VLOG_MODULE(ovsdb_file)
+VLOG_MODULE(ovsdb_idl)
+VLOG_MODULE(ovsdb_log)
+VLOG_MODULE(ovsdb_jsonrpc_server)
+VLOG_MODULE(ovsdb_server)
+VLOG_MODULE(ovsdb_tool)
VLOG_MODULE(pktbuf)
VLOG_MODULE(pcap)
VLOG_MODULE(poll_loop)
VLOG_MODULE(proc_net_compat)
VLOG_MODULE(process)
VLOG_MODULE(rconn)
+VLOG_MODULE(reconnect)
VLOG_MODULE(rtnetlink)
VLOG_MODULE(stp)
+VLOG_MODULE(stream_fd)
+VLOG_MODULE(stream_tcp)
+VLOG_MODULE(stream_unix)
+VLOG_MODULE(stream)
VLOG_MODULE(stats)
VLOG_MODULE(status)
VLOG_MODULE(svec)
--- /dev/null
+.IP "Logging options:"
+[\fB-v\fR[\fImodule\fR[\fB:\fIfacility\fR[\fB:\fIlevel\fR]]]]\&...
+.br
+[\fB--verbose[=\fImodule\fR[\fB:\fIfacility\fR[\fB:\fIlevel\fR]]]]\&...
+.br
+[\fB--log-file\fR[\fB=\fIfile\fR]]
}
static void
-vlog_unixctl_set(struct unixctl_conn *conn, const char *args)
+vlog_unixctl_set(struct unixctl_conn *conn, const char *args, void *aux 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 UNUSED)
+vlog_unixctl_list(struct unixctl_conn *conn,
+ const char *args UNUSED, void *aux UNUSED)
{
char *msg = vlog_get_levels();
unixctl_command_reply(conn, 200, msg);
}
static void
-vlog_unixctl_reopen(struct unixctl_conn *conn, const char *args UNUSED)
+vlog_unixctl_reopen(struct unixctl_conn *conn,
+ const char *args UNUSED, void *aux UNUSED)
{
if (log_file_name) {
int error = vlog_reopen_log_file();
VLOG_ERR("current time is negative: %s (%ld)", s, (long int) now);
}
- unixctl_command_register("vlog/set", vlog_unixctl_set);
- unixctl_command_register("vlog/list", vlog_unixctl_list);
- unixctl_command_register("vlog/reopen", vlog_unixctl_reopen);
+ unixctl_command_register("vlog/set", vlog_unixctl_set, NULL);
+ unixctl_command_register("vlog/list", vlog_unixctl_list, NULL);
+ unixctl_command_register("vlog/reopen", vlog_unixctl_reopen, NULL);
}
/* Closes the logging subsystem. */
char local_name[IF_NAMESIZE];
int error;
- d = xcalloc(1, sizeof *d);
+ d = xzalloc(sizeof *d);
/* Controller regular expression. */
error = discovery_set_accept_controller_re(d, re);
return errno;
}
- e = xcalloc(1, sizeof *e);
+ e = xzalloc(sizeof *e);
e->command_acl = xstrdup(command_acl);
e->command_dir = (command_dir
? xstrdup(command_dir)
return error;
}
- in_band = xcalloc(1, sizeof *in_band);
+ in_band = xzalloc(sizeof *in_band);
in_band->ofproto = ofproto;
in_band->controller = controller;
in_band->ss_cat = switch_status_register(ss, "in-band",
dpif_recv_purge(dpif);
/* Initialize settings. */
- p = xcalloc(1, sizeof *p);
+ p = xzalloc(sizeof *p);
p->fallback_dpid = pick_fallback_dpid();
p->datapath_id = p->fallback_dpid;
p->manufacturer = xstrdup("Nicira Networks, Inc.");
const union ofp_action *actions, size_t n_actions,
uint16_t idle_timeout, uint16_t hard_timeout)
{
- struct rule *rule = xcalloc(1, sizeof *rule);
+ struct rule *rule = xzalloc(sizeof *rule);
rule->idle_timeout = idle_timeout;
rule->hard_timeout = hard_timeout;
rule->used = rule->created = time_msec();
byte_count = rule->byte_count;
n_odp_flows = rule->cr.wc.wildcards ? list_size(&rule->list) : 1;
- odp_flows = xcalloc(1, n_odp_flows * sizeof *odp_flows);
+ odp_flows = xzalloc(n_odp_flows * sizeof *odp_flows);
if (rule->cr.wc.wildcards) {
size_t i = 0;
LIST_FOR_EACH (subrule, struct rule, list, &rule->list) {
{
struct pinsched *ps;
- ps = xcalloc(1, sizeof *ps);
+ ps = xzalloc(sizeof *ps);
port_array_init(&ps->queues);
ps->n_queued = 0;
ps->last_tx_port = PORT_ARRAY_SIZE;
struct pktbuf *
pktbuf_create(void)
{
- return xcalloc(1, sizeof *pktbuf_create());
+ return xzalloc(sizeof *pktbuf_create());
}
void
struct switch_status *
switch_status_create(const struct ofproto *ofproto)
{
- struct switch_status *ss = xcalloc(1, sizeof *ss);
+ struct switch_status *ss = xzalloc(sizeof *ss);
ss->booted = time_now();
list_init(&ss->categories);
ss->config_cat = switch_status_register(ss, "config", config_status_cb,
--- /dev/null
+ ===================================================
+ Open vSwitch Configuration Database Specification
+ ===================================================
+
+Basic Notation
+--------------
+
+The descriptions below use the following shorthand notations for JSON
+values. Additional notation is presented later.
+
+<string>
+
+ A JSON string.
+
+<id>
+
+ A JSON string matching [a-zA-Z_][a-zA-Z0-9_]*.
+
+ <id>s that begin with _ are reserved to the implementation and may
+ not be used by the user.
+
+<boolean>
+
+ A JSON true or false value.
+
+<number>
+
+ A JSON number.
+
+<integer>
+
+ A JSON number with an integer value, within a certain range
+ (currently -2**63...+2**63-1).
+
+<value>
+
+ Any JSON value.
+
+Schema Format
+-------------
+
+An Open vSwitch configuration database consists of a set of tables,
+each of which has a number of columns and zero or more rows. A schema
+is represented by <database-schema>, as described below.
+
+<database-schema>
+
+ A JSON object with the following members:
+
+ "name": <id> required
+ "comment": <string> optional
+ "tables": {<id>: <table-schema>, ...} required
+
+ The "name" identifies the database as a whole. The "comment"
+ optionally provides more information about the database. The
+ value of "tables" is a JSON object whose names are table names and
+ whose values are <table-schema>s.
+
+<table-schema>
+
+ A JSON object with the following members:
+
+ "comment": <string> optional
+ "columns": {<id>: <column-schema>, ...} required
+
+ The "comment" optionally provides information about this table for
+ a human reader. The value of "columns" is a JSON object whose
+ names are column names and whose values are <column-schema>s.
+
+ Every table has the following columns whose definitions are not
+ included in the schema:
+
+ "_uuid": This column, which contains exactly one UUID value,
+ is initialized to a random value by the database engine when
+ it creates a row. It is read-only, and its value never
+ changes during the lifetime of a row.
+
+ "_version": Like "_uuid", this column contains exactly one
+ UUID value, initialized to a random value by the database
+ engine when it creates a row, and it is read-only. However,
+ its value changes to a new random value whenever any other
+ field in the row changes. Furthermore, its value is
+ ephemeral: when the database is closed and reopened, or when
+ the database process is stopped and then started again, each
+ "_version" also changes to a new random value.
+
+<column-schema>
+
+ A JSON object with the following members:
+
+ "comment": <string> optional
+ "type": <type> required
+ "ephemeral": <boolean> optional
+
+ The "comment" optionally provides information about this column
+ for a human reader. The "type" specifies the type of data stored
+ in this column. If "ephemeral" is specified as true, then this
+ column's values are not guaranteed to be durable; they may be lost
+ when the database restarts.
+
+<type>
+
+ The type of a database column. Either an <atomic-type> or a JSON
+ object that describes the type of a database column, with the
+ following members:
+
+ "key": <atomic-type> required
+ "value": <atomic-type> optional
+ "min": <integer> optional
+ "max": <integer> or "unlimited" optional
+
+ If "min" or "max" is not specified, each defaults to 1. If "max"
+ is specified as "unlimited", then there is no specified maximum
+ number of elements, although the implementation will enforce some
+ limit. After considering defaults, "min" must be at least 0,
+ "max" must be at least 1, and "max" must be greater than or equal
+ to "min".
+
+ If "min" and "max" are both 1 and "value" is not specified, the
+ type is the scalar type specified by "key".
+
+ If "min" is not 1 or "max" is not 1, or both, and "value" is not
+ specified, the type is a set of scalar type "key".
+
+ If "value" is specified, the type is a map from type "key" to type
+ "value".
+
+<atomic-type>
+
+ One of the strings "integer", "real", "boolean", "string", or
+ "uuid", representing the specified scalar type.
+
+Wire Protocol
+-------------
+
+The database wire protocol is implemented in JSON-RPC 1.0. It
+consists of the following JSON-RPC methods:
+
+get_schema
+..........
+
+Request object members:
+
+ "method": "get_schema" required
+ "params": [] required
+ "id": any JSON value except null required
+
+Response object members:
+
+ "result": <database-schema>
+ "error": null
+ "id": same "id" as request
+
+This operation retrieves a <database-schema> that describes the
+hosted database.
+
+transact
+........
+
+Request object members:
+
+ "method": "transact" required
+ "params": [<operation>*] required
+ "id": any JSON value except null required
+
+Response object members:
+
+ "result": [<object>*]
+ "error": null
+ "id": same "id" as request
+
+The "params" array for this method consists of zero or more JSON
+objects, each of which represents a single database operation. The
+"Operations" section below describes the valid operations.
+
+The value of "id" must be unique among all in-flight transactions
+within the current JSON-RPC session. Otherwise, the server may return
+a JSON-RPC error.
+
+The database server executes each of the specified operations in the
+specified order, except that if an operation fails, then the remaining
+operations are not executed.
+
+The set of operations is executed as a single atomic, consistent,
+isolated transaction. The transaction is committed only if every
+operation succeeds. Durability of the commit is not guaranteed unless
+the "commit" operation, with "durable" set to true, is included in the
+operation set (see below).
+
+Regardless of whether errors occur, the response is always a JSON-RPC
+response with null "error" and a "result" member that is an array with
+the same number of elements as "params". Each element of the "result"
+array corresponds to the same element of the "params" array. The
+"result" array elements may be interpreted as follows:
+
+ - A JSON object that does not contain an "error" member indicates
+ that the operation completed successfully. The specific members
+ of the object are specified below in the descriptions of
+ individual operations. Some operations do not produce any
+ results, in which case the object will have no members.
+
+ - A JSON object that contains an "error" member indicates that the
+ operation completed with an error. The value of the "error"
+ member is a short string, specified in this document, that
+ broadly indicates the class of the error. Besides the ones
+ listed for a specific operation, any operation may result in one
+ the following "error"s:
+
+ "error": "resources exhausted"
+
+ The operation or the transaction requires more resources
+ (memory, disk, CPU, etc.) than are currently available to
+ the database server.
+
+ "error": "syntax error"
+
+ The operation is not specified correctly: a required request
+ object member is missing, an unknown or unsupported request
+ object member is present, the operation attempts to act on a
+ table that does not exist, the operation modifies a
+ read-only table column, etc.
+
+ Database implementations may use "error" strings not specified
+ in this document to indicate errors that do not fit into any of
+ the specified categories.
+
+ Optionally, the object may include a "details" member, whose
+ value is a string that describes the error in more detail for
+ the benefit of a human user or administrator. The object may
+ also have other members that describe the error in more detail.
+ This document does not specify the names or values of these
+ members.
+
+ - A JSON null value indicates that the operation was not attempted
+ because a prior operation failed.
+
+In general, "result" contains some number of successful results,
+possibly followed by an error, in turn followed by enough JSON null
+values to match the number of elements in "params". There is one
+exception: if all of the operations succeed, but the results cannot be
+committed (e.g. due to I/O errors), then "result" will have one more
+element than "params", with the additional element describing the
+error.
+
+If "params" contains one or more "wait" operations, then the
+transaction may take an arbitrary amount of time to complete. The
+database implementation must be capable of accepting, executing, and
+replying to other transactions and other JSON-RPC requests while a
+transaction or transactions containing "wait" operations are
+outstanding on the same or different JSON-RPC sessions.
+
+The section "Notation for the Wire Protocol" below describes
+additional notation for use with the wire protocol. After that, the
+"Operations" section describes each operation.
+
+cancel
+......
+
+Request object members:
+
+ "method": "cancel" required
+ "params": [the "id" for an outstanding request] required
+ "id": null required
+
+Response object members:
+
+ <no response>
+
+This JSON-RPC notification instructs the database server to
+immediately complete or cancel the "transact" request whose "id" is
+the same as the notification's "params" value.
+
+If the "transact" request can be completed immediately, then the
+server sends a response in the form described for "transact", above.
+Otherwise, the server sends a JSON-RPC error response of the following
+form:
+
+ "result": null
+ "error": "canceled"
+ "id": the request "id" member
+
+The "cancel" notification itself has no reply.
+
+monitor
+.......
+
+Request object members:
+
+ "method": "monitor" required
+ "params": [<value>, <monitor-requests>] required
+ "id": any JSON value except null required
+
+<monitor-requests> is an object that maps from a table name to a
+<monitor-request>.
+
+Each <monitor-request> is an object with the following members:
+
+ "columns": [<column>*] optional
+ "select": <monitor-select> optional
+
+<monitor-select> is an object with the following members:
+
+ "initial": <boolean> optional
+ "insert": <boolean> optional
+ "delete": <boolean> optional
+ "modify": <boolean> optional
+
+Response object members:
+
+ "result": <table-updates>
+ "error": null
+ "id": same "id" as request
+
+This JSON-RPC request enables a client to replicate tables or subsets
+of tables. Each <monitor-request> specifies a table to be replicated.
+The JSON-RPC response to the "monitor" includes the initial contents
+of each table. Afterward, when changes to those tables are committed,
+the changes are automatically sent to the client using the "update"
+monitor notification. This monitoring persists until the JSON-RPC
+session terminates or until the client sends a "monitor_cancel"
+JSON-RPC request.
+
+Each <monitor-request> describes how to monitor a table:
+
+ The circumstances in which an "update" notification is sent for a
+ row within the table are determined by <monitor-select>:
+
+ If "initial" is omitted or true, every row in the table is
+ sent as part of the reply to the "monitor" request.
+
+ If "insert" is omitted or true, "update" notifications are
+ sent for rows newly inserted into the table.
+
+ If "delete" is omitted or true, "update" notifications are
+ sent for rows deleted from the table.
+
+ If "modify" is omitted or true, "update" notifications are
+ sent whenever when a row in the table is modified.
+
+ The "columns" member specifies the columns whose values are
+ monitored. If "columns" is omitted, all columns in the table,
+ except for "_uuid", are monitored.
+
+The "result" in the JSON-RPC response to the "monitor" request is a
+<table-updates> object (see below) that contains the contents of the
+tables for which "initial" rows are selected. If no tables' initial
+contents are requested, then "result" is an empty object.
+
+update
+......
+
+Notification object members:
+
+ "method": "update"
+ "params": [<value>, <table-updates>]
+ "id": null
+
+The <value> in "params" is the same as the value passed as the <value>
+in "params" for the "monitor" request.
+
+<table-updates> is an object that maps from a table name to a
+<table-update>.
+
+A <table-update> is an object that maps from the row's UUID (as a
+36-byte string) to a <row-update> object.
+
+A <row-update> is an object with the following members:
+
+ "old": <row> present for "delete" and "modify" updates
+ "new": <row> present for "initial", "insert", and "modify" updates
+
+This JSON-RPC notification is sent from the server to the client to
+tell it about changes to a monitored table (or the initial state of a
+modified table). Each table in which one or more rows has changed (or
+whose initial view is being presented) is represented in "updates".
+Each row that has changed (or whose initial view is being presented)
+is represented in its <table-update> as a member with its name taken
+from the row's _uuid member. The corresponding value is a
+<row-update>:
+
+ The "old" member is present for "delete" and "modify" updates.
+ For "delete" updates, each monitored column is included. For
+ "modify" updates, the prior value of each monitored column whose
+ value has changed is included (monitored columns that have not
+ changed are represented in "new").
+
+ The "new" member is present for "initial", "insert", and "modify"
+ updates. For "initial" and "insert" updates, each monitored
+ column is included. For "modify" updates, the new value of each
+ monitored column is included.
+
+monitor_cancel
+..............
+
+Request object members:
+
+ "method": "monitor_cancel" required
+ "params": [<value>] required
+ "id": any JSON value except null required
+
+Response object members:
+
+ "result": {}
+ "error": null
+ "id": the request "id" member
+
+Cancels the ongoing table monitor request, identified by the <value>
+in "params" matching the <value> in "params" for an ongoing "monitor"
+request. No more "update" messages will be sent for this table
+monitor.
+
+echo
+....
+
+Request object members:
+
+ "method": "echo" required
+ "params": JSON array with any contents required
+ "id": <value> required
+
+Response object members:
+
+ "result": same as "params"
+ "error": null
+ "id": the request "id" member
+
+Both the JSON-RPC client and the server must implement this request.
+
+This JSON-RPC request and response can be used to implement connection
+keepalives, by allowing the server to check that the client is still
+there or vice versa.
+
+
+Notation for the Wire Protocol
+------------------------------
+
+<table>
+
+ An <id> that names a table.
+
+<column>
+
+ An <id> that names a table column.
+
+<row>
+
+ A JSON object that describes a table row or a subset of a table
+ row. Each member is the name of a table column paired with the
+ <value> of that column.
+
+<value>
+
+ A JSON value that represents the value of a column in a table row,
+ one of <atom>, a <set>, or a <map>.
+
+<atom>
+
+ A JSON value that represents a scalar value for a column, one of
+ <string>, <number>, <boolean>, <uuid>, <named-uuid>.
+
+<set>
+
+ A 2-element JSON array that represents a database set value. The
+ first element of the array must be the string "set" and the second
+ element must be an array of zero or more <atom>s giving the values
+ in the set. All of the <atom>s must have the same type.
+
+<map>
+
+ A 2-element JSON array that represents a database map value. The
+ first element of the array must be the string "map" and the second
+ element must be an array of zero or more <pair>s giving the values
+ in the map. All of the <pair>s must have the same key and value
+ types.
+
+ (JSON objects are not used to represent <map> because JSON only
+ allows string names in an object.)
+
+<pair>
+
+ A 2-element JSON array that represents a pair within a database
+ map. The first element is an <atom> that represents the key, the
+ second element is an <atom> that represents the value.
+
+<uuid>
+
+ A 2-element JSON array that represents a UUID. The first element
+ of the array must be the string "uuid" and the second element must
+ be a 36-character string giving the UUID in the format described
+ by RFC 4122. For example, the following <uuid> represents the
+ UUID 550e8400-e29b-41d4-a716-446655440000:
+
+ ["uuid", "550e8400-e29b-41d4-a716-446655440000"]
+
+<named-uuid>
+
+ A 2-element JSON array that represents the UUID of a row inserted
+ in a previous "insert" operation within the same transaction. The
+ first element of the array must be the string "named-uuid" and the
+ second element must be the string specified on this "insert"
+ operation's "uuid-name" or on a preceding "insert" within the same
+ transaction. For example, if this or a previous "insert"
+ operation specified a "uuid-name" of "myrow", the following
+ <named-uuid> represents the UUID created by that operation:
+
+ ["named-uuid", "myrow"]
+
+ A <named-uuid> may be used anywhere a <uuid> is valid.
+
+<condition>
+
+ A 3-element JSON array of the form [<column>, <function>,
+ <value>] that represents a test on a column value.
+
+ Except as otherwise specified below, <value> must have the same
+ type as <column>.
+
+ The meaning depends on the type of <column>:
+
+ integer
+ real
+
+ <function> must be "<", "<=", "==", "!=", ">=", ">",
+ "includes", or "excludes".
+
+ The test is true if the column's value satisfies the
+ relation <function> <value>, e.g. if the column has value
+ 1 and <value> is 2, the test is true if <function> is "<",
+ "<=" or "!=", but not otherwise.
+
+ "includes" is equivalent to "=="; "excludes" is equivalent
+ to "!=".
+
+ boolean
+ string
+ uuid
+
+ <function> must be "!=", "==", "includes", or "excludes".
+
+ If <function> is "==" or "includes", the test is true if
+ the column's value equals <value>. If <function> is "!="
+ or "excludes", the test is inverted.
+
+ set
+ map
+
+ <function> must be "!=", "==", "includes", or "excludes".
+
+ If <function> is "==", the test is true if the column's
+ value contains exactly the same values (for sets) or pairs
+ (for maps). If <function> is "!=", the test is inverted.
+
+ If <function> is "includes", the test is true if the
+ column's value contains all of the values (for sets) or
+ pairs (for maps) in <value>. The column's value may also
+ contain other values or pairs.
+
+ If <function> is "excludes", the test is true if the
+ column's value does not contain any of the values (for
+ sets) or pairs (for maps) in <value>. The column's value
+ may contain other values or pairs not in <value>.
+
+ If <function> is "includes" or "excludes", then the
+ required type of <value> is slightly relaxed, in that it
+ may have fewer than the minimum number of elements
+ specified by the column's type. If <function> is
+ "excludes", then the required type is additionally relaxed
+ in that <value> may have more than the maximum number of
+ elements specified by the column's type.
+
+<function>
+
+ One of "<", "<=", "==", "!=", ">=", ">", "includes", "excludes".
+
+Operations
+----------
+
+Each of the available operations is described below.
+
+insert
+......
+
+Request object members:
+
+ "op": "insert" required
+ "table": <table> required
+ "row": <row> required
+ "uuid-name": <string> optional
+
+Result object members:
+
+ "uuid": <uuid>
+
+Semantics:
+
+ Inserts "row" into "table". If "row" does not specify values
+ for all the columns in "table", those columns receive default
+ values.
+
+ The new row receives a new, randomly generated UUID, which is
+ returned as the "uuid" member of the result. If "uuid-name" is
+ supplied, then the UUID is made available under that name to this
+ operation and later operations within the same transaction.
+
+select
+......
+
+Request object members:
+
+ "op": "select" required
+ "table": <table> required
+ "where": [<condition>*] required
+ "columns": [<column>*] optional
+
+Result object members:
+
+ "rows": [<row>*]
+
+Semantics:
+
+ Searches "table" for rows that match all the conditions specified
+ in "where". If "where" is an empty array, every row in "table" is
+ selected.
+
+ The "rows" member of the result is an array of objects. Each
+ object corresponds to a matching row, with each column
+ specified in "columns" as a member, the column's name as the
+ member name and its value as the member value. If "columns"
+ is not specified, all the table's columns are included. If
+ two rows of the result have the same values for all included
+ columns, only one copy of that row is included in "rows".
+ Specifying "_uuid" within "columns" will avoid dropping
+ duplicates, since every row has a unique UUID.
+
+ The ordering of rows within "rows" is unspecified.
+
+update
+......
+
+Request object members:
+
+ "op": "update" required
+ "table": <table> required
+ "where": [<condition>*] required
+ "row": <row> required
+
+Result object members:
+
+ "count": <integer>
+
+Semantics:
+
+ Updates rows in a table.
+
+ Searches "table" for rows that match all the conditions
+ specified in "where". For each matching row, changes the
+ value of each column specified in "row" to the value for that
+ column specified in "row".
+
+ The "_uuid" and "_version" columns of a table may not be directly
+ updated with this operation. Columns designated read-only in the
+ schema also may not be updated.
+
+ The "count" member of the result specifies the number of rows
+ that matched.
+
+delete
+......
+
+Request object members:
+
+ "op": "delete" required
+ "table": <table> required
+ "where": [<condition>*] required
+
+Result object members:
+
+ "count": <integer>
+
+Semantics:
+
+ Deletes all the rows from "table" that match all the conditions
+ specified in "where".
+
+ The "count" member of the result specifies the number of deleted
+ rows.
+
+wait
+....
+
+Request object members:
+
+ "op": "wait" required
+ "timeout": <integer> optional
+ "table": <table> required
+ "where": [<condition>*] required
+ "columns": [<column>*] required
+ "until": "==" or "!=" required
+ "rows": [<row>*] required
+
+Result object members:
+
+ none
+
+Semantics:
+
+ Waits until a condition becomes true.
+
+ If "until" is "==", checks whether the query on "table" specified
+ by "where" and "columns", which is evaluated in the same way as
+ specified for "select", returns the result set specified by
+ "rows". If it does, then the operation completes successfully.
+ Otherwise, the entire transaction rolls back. It is automatically
+ restarted later, after a change in the database makes it possible
+ for the operation to succeed. The client will not receive a
+ response until the operation permanently succeeds or fails.
+
+ If "until" is "!=", the sense of the test is negated. That is, as
+ long as the query on "table" specified by "where" and "columns"
+ returns "rows", the transaction will be rolled back and restarted
+ later.
+
+ If "timeout" is specified, then the transaction aborts after the
+ specified number of milliseconds. The transaction is guaranteed
+ to be attempted at least once before it aborts. A "timeout" of 0
+ will abort the transaction on the first mismatch.
+
+Errors:
+
+ "error": "not supported"
+
+ One or more of the columns in this table do not support
+ triggers. This error will not occur if "timeout" is 0.
+
+ "error": "timed out"
+
+ The "timeout" was reached before the transaction was able to
+ complete.
+
+commit
+......
+
+Request object members:
+
+ "op": "commit" required
+ "durable": <boolean> required
+
+Result object members:
+
+ none
+
+Semantics:
+
+ If "durable" is specified as true, then the transaction, if it
+ commits, will be stored durably (to disk) before the reply is sent
+ to the client.
+
+Errors:
+
+ "error": "not supported"
+
+ When "durable" is true, this database implementation does not
+ support durable commits.
+
+abort
+.....
+
+Request object members:
+
+ "op": "abort" required
+
+Result object members:
+
+ (never succeeds)
+
+Semantics:
+
+ Aborts the transaction with an error. This may be useful for
+ testing.
+
+Errors:
+
+ "error": "aborted"
+
+ This operation always fails with this error.
--- /dev/null
+# libovsdb
+noinst_LIBRARIES += ovsdb/libovsdb.a
+ovsdb_libovsdb_a_SOURCES = \
+ ovsdb/column.c \
+ ovsdb/column.h \
+ ovsdb/condition.c \
+ ovsdb/condition.h \
+ ovsdb/execution.c \
+ ovsdb/file.c \
+ ovsdb/file.h \
+ ovsdb/jsonrpc-server.c \
+ ovsdb/jsonrpc-server.h \
+ ovsdb/log.c \
+ ovsdb/log.h \
+ ovsdb/ovsdb-server.c \
+ ovsdb/ovsdb.c \
+ ovsdb/ovsdb.h \
+ ovsdb/query.c \
+ ovsdb/query.h \
+ ovsdb/row.c \
+ ovsdb/row.h \
+ ovsdb/table.c \
+ ovsdb/table.h \
+ ovsdb/trigger.c \
+ ovsdb/trigger.h \
+ ovsdb/transaction.c \
+ ovsdb/transaction.h
+
+# ovsdb-tool
+bin_PROGRAMS += ovsdb/ovsdb-tool
+ovsdb_ovsdb_tool_SOURCES = ovsdb/ovsdb-tool.c
+ovsdb_ovsdb_tool_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a
+# ovsdb-tool.1
+man_MANS += ovsdb/ovsdb-tool.1
+DISTCLEANFILES += ovsdb/ovsdb-tool.1
+EXTRA_DIST += ovsdb/ovsdb-tool.1.in
+
+# ovsdb-client
+bin_PROGRAMS += ovsdb/ovsdb-client
+ovsdb_ovsdb_client_SOURCES = ovsdb/ovsdb-client.c
+ovsdb_ovsdb_client_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a
+# ovsdb-client.1
+man_MANS += ovsdb/ovsdb-client.1
+DISTCLEANFILES += ovsdb/ovsdb-client.1
+EXTRA_DIST += ovsdb/ovsdb-client.1.in
+
+# ovsdb-server
+sbin_PROGRAMS += ovsdb/ovsdb-server
+ovsdb_ovsdb_server_SOURCES = ovsdb/ovsdb-server.c
+ovsdb_ovsdb_server_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a $(FAULT_LIBS)
+# ovsdb-server.1
+man_MANS += ovsdb/ovsdb-server.1
+DISTCLEANFILES += ovsdb/ovsdb-server.1
+EXTRA_DIST += ovsdb/ovsdb-server.1.in
+
+# ovsdb-idlc
+EXTRA_DIST += \
+ ovsdb/simplejson/__init__.py \
+ ovsdb/simplejson/_speedups.c \
+ ovsdb/simplejson/decoder.py \
+ ovsdb/simplejson/encoder.py \
+ ovsdb/simplejson/scanner.py \
+ ovsdb/simplejson/tests/__init__.py \
+ ovsdb/simplejson/tests/test_check_circular.py \
+ ovsdb/simplejson/tests/test_decode.py \
+ ovsdb/simplejson/tests/test_default.py \
+ ovsdb/simplejson/tests/test_dump.py \
+ ovsdb/simplejson/tests/test_encode_basestring_ascii.py \
+ ovsdb/simplejson/tests/test_fail.py \
+ ovsdb/simplejson/tests/test_float.py \
+ ovsdb/simplejson/tests/test_indent.py \
+ ovsdb/simplejson/tests/test_pass1.py \
+ ovsdb/simplejson/tests/test_pass2.py \
+ ovsdb/simplejson/tests/test_pass3.py \
+ ovsdb/simplejson/tests/test_recursion.py \
+ ovsdb/simplejson/tests/test_scanstring.py \
+ ovsdb/simplejson/tests/test_separators.py \
+ ovsdb/simplejson/tests/test_unicode.py \
+ ovsdb/simplejson/tool.py
+noinst_SCRIPTS += ovsdb/ovsdb-idlc
+EXTRA_DIST += \
+ ovsdb/ovsdb-idlc.in \
+ ovsdb/ovsdb-idlc.1
+DISTCLEANFILES += ovsdb/ovsdb-idlc
+SUFFIXES += .ovsidl
+.ovsidl.c:
+ $(PYTHON) $(srcdir)/ovsdb/ovsdb-idlc.in c-idl-source $< > $@.tmp
+ mv $@.tmp $@
+.ovsidl.h:
+ $(PYTHON) $(srcdir)/ovsdb/ovsdb-idlc.in c-idl-header $< > $@.tmp
+ mv $@.tmp $@
+.ovsidl.ovsschema:
+ $(PYTHON) $(srcdir)/ovsdb/ovsdb-idlc.in ovsdb-schema $< > $@.tmp
+ mv $@.tmp $@
--- /dev/null
+/* Copyright (c) 2009 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/column.h"
+
+#include <stdlib.h>
+
+#include "column.h"
+#include "json.h"
+#include "ovsdb-error.h"
+#include "ovsdb-parser.h"
+#include "table.h"
+#include "util.h"
+
+struct ovsdb_column *
+ovsdb_column_create(const char *name, const char *comment,
+ bool mutable, bool persistent,
+ const struct ovsdb_type *type)
+{
+ struct ovsdb_column *column;
+
+ column = xzalloc(sizeof *column);
+ column->name = xstrdup(name);
+ column->comment = comment ? xstrdup(comment) : NULL;
+ column->mutable = mutable;
+ column->persistent = persistent;
+ column->type = *type;
+
+ return column;
+}
+
+void
+ovsdb_column_destroy(struct ovsdb_column *column)
+{
+ free(column->name);
+ free(column->comment);
+ free(column);
+}
+
+struct ovsdb_error *
+ovsdb_column_from_json(const struct json *json, const char *name,
+ struct ovsdb_column **columnp)
+{
+ const struct json *comment, *mutable, *ephemeral, *type_json;
+ struct ovsdb_error *error;
+ struct ovsdb_type type;
+ struct ovsdb_parser parser;
+ bool persistent;
+
+ *columnp = NULL;
+
+ ovsdb_parser_init(&parser, json, "schema for column %s", name);
+ comment = ovsdb_parser_member(&parser, "comment", OP_STRING | OP_OPTIONAL);
+ mutable = ovsdb_parser_member(&parser, "mutable",
+ OP_TRUE | OP_FALSE | OP_OPTIONAL);
+ ephemeral = ovsdb_parser_member(&parser, "ephemeral",
+ OP_TRUE | OP_FALSE | OP_OPTIONAL);
+ type_json = ovsdb_parser_member(&parser, "type", OP_STRING | OP_OBJECT);
+ error = ovsdb_parser_finish(&parser);
+ if (error) {
+ return error;
+ }
+
+ error = ovsdb_type_from_json(&type, type_json);
+ if (error) {
+ return error;
+ }
+
+ persistent = ephemeral ? !json_boolean(ephemeral) : true;
+ *columnp = ovsdb_column_create(name,
+ comment ? json_string(comment) : NULL,
+ mutable ? json_boolean(mutable) : true,
+ persistent, &type);
+ return NULL;
+}
+
+struct json *
+ovsdb_column_to_json(const struct ovsdb_column *column)
+{
+ struct json *json = json_object_create();
+ if (column->comment) {
+ json_object_put_string(json, "comment", column->comment);
+ }
+ if (!column->mutable) {
+ json_object_put(json, "mutable", json_boolean_create(false));
+ }
+ if (!column->persistent) {
+ json_object_put(json, "ephemeral", json_boolean_create(true));
+ }
+ json_object_put(json, "type", ovsdb_type_to_json(&column->type));
+ return json;
+}
+\f
+void
+ovsdb_column_set_init(struct ovsdb_column_set *set)
+{
+ set->columns = NULL;
+ set->n_columns = set->allocated_columns = 0;
+}
+
+void
+ovsdb_column_set_destroy(struct ovsdb_column_set *set)
+{
+ free(set->columns);
+}
+
+void
+ovsdb_column_set_clone(struct ovsdb_column_set *new,
+ const struct ovsdb_column_set *old)
+{
+ new->columns = xmemdup(old->columns,
+ old->n_columns * sizeof *old->columns);
+ new->n_columns = new->allocated_columns = old->n_columns;
+}
+
+struct ovsdb_error *
+ovsdb_column_set_from_json(const struct json *json,
+ const struct ovsdb_table *table,
+ struct ovsdb_column_set *set)
+{
+ ovsdb_column_set_init(set);
+ if (!json) {
+ struct shash_node *node;
+
+ SHASH_FOR_EACH (node, &table->schema->columns) {
+ const struct ovsdb_column *column = node->data;
+ ovsdb_column_set_add(set, column);
+ }
+
+ return NULL;
+ } else {
+ size_t i;
+
+ if (json->type != JSON_ARRAY) {
+ goto error;
+ }
+
+ /* XXX this is O(n**2) */
+ for (i = 0; i < json->u.array.n; i++) {
+ struct ovsdb_column *column;
+
+ if (json->u.array.elems[i]->type != JSON_STRING) {
+ goto error;
+ }
+
+ column = shash_find_data(&table->schema->columns,
+ json->u.array.elems[i]->u.string);
+ if (ovsdb_column_set_contains(set, column->index)) {
+ goto error;
+ }
+ ovsdb_column_set_add(set, column);
+ }
+
+ return NULL;
+ }
+
+error:
+ ovsdb_column_set_destroy(set);
+ return ovsdb_syntax_error(json, NULL,
+ "array of distinct column names expected");
+}
+
+struct json *
+ovsdb_column_set_to_json(const struct ovsdb_column_set *set)
+{
+ struct json *json;
+ size_t i;
+
+ json = json_array_create_empty();
+ for (i = 0; i < set->n_columns; i++) {
+ json_array_add(json, json_string_create(set->columns[i]->name));
+ }
+ return json;
+}
+
+void
+ovsdb_column_set_add(struct ovsdb_column_set *set,
+ const struct ovsdb_column *column)
+{
+ if (set->n_columns >= set->allocated_columns) {
+ set->columns = x2nrealloc(set->columns, &set->allocated_columns,
+ sizeof *set->columns);
+ }
+ set->columns[set->n_columns++] = column;
+}
+
+void
+ovsdb_column_set_add_all(struct ovsdb_column_set *set,
+ const struct ovsdb_table *table)
+{
+ struct shash_node *node;
+
+ SHASH_FOR_EACH (node, &table->schema->columns) {
+ const struct ovsdb_column *column = node->data;
+ ovsdb_column_set_add(set, column);
+ }
+}
+
+bool
+ovsdb_column_set_contains(const struct ovsdb_column_set *set,
+ unsigned int column_index)
+{
+ size_t i;
+
+ for (i = 0; i < set->n_columns; i++) {
+ if (set->columns[i]->index == column_index) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/* This comparison is sensitive to ordering of columns within a set, but that's
+ * good: the only existing caller wants to make sure that hash values are
+ * comparable, which is only true if column ordering is the same. */
+bool
+ovsdb_column_set_equals(const struct ovsdb_column_set *a,
+ const struct ovsdb_column_set *b)
+{
+ size_t i;
+
+ if (a->n_columns != b->n_columns) {
+ return false;
+ }
+ for (i = 0; i < a->n_columns; i++) {
+ if (a->columns[i] != b->columns[i]) {
+ return false;
+ }
+ }
+ return true;
+}
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef OVSDB_COLUMN_H
+#define OVSDB_COLUMN_H 1
+
+#include <stdbool.h>
+#include "compiler.h"
+#include "ovsdb-types.h"
+
+struct ovsdb_table;
+
+/* A column or a column schema (currently there is no distinction). */
+struct ovsdb_column {
+ unsigned int index;
+ char *name;
+
+ char *comment;
+ bool mutable;
+ bool persistent;
+ struct ovsdb_type type;
+};
+
+/* A few columns appear in every table with standardized column indexes.
+ * These macros define those columns' indexes.
+ *
+ * Don't change these values, because ovsdb_query() depends on OVSDB_COL_UUID
+ * having value 0. */
+enum {
+ OVSDB_COL_UUID = 0, /* UUID for the row. */
+ OVSDB_COL_VERSION = 1, /* Version number for the row. */
+ OVSDB_N_STD_COLUMNS
+};
+
+struct ovsdb_column *ovsdb_column_create(
+ const char *name, const char *comment, bool mutable, bool persistent,
+ const struct ovsdb_type *);
+void ovsdb_column_destroy(struct ovsdb_column *);
+
+struct ovsdb_error *ovsdb_column_from_json(const struct json *,
+ const char *name,
+ struct ovsdb_column **)
+ WARN_UNUSED_RESULT;
+struct json *ovsdb_column_to_json(const struct ovsdb_column *);
+\f
+/* An unordered set of distinct columns. */
+
+struct ovsdb_column_set {
+ const struct ovsdb_column **columns;
+ size_t n_columns, allocated_columns;
+};
+
+#define OVSDB_COLUMN_SET_INITIALIZER { NULL, 0, 0 }
+
+void ovsdb_column_set_init(struct ovsdb_column_set *);
+void ovsdb_column_set_destroy(struct ovsdb_column_set *);
+void ovsdb_column_set_clone(struct ovsdb_column_set *,
+ const struct ovsdb_column_set *);
+struct ovsdb_error *ovsdb_column_set_from_json(const struct json *,
+ const struct ovsdb_table *,
+ struct ovsdb_column_set *);
+struct json *ovsdb_column_set_to_json(const struct ovsdb_column_set *);
+
+void ovsdb_column_set_add(struct ovsdb_column_set *,
+ const struct ovsdb_column *);
+void ovsdb_column_set_add_all(struct ovsdb_column_set *,
+ const struct ovsdb_table *);
+bool ovsdb_column_set_contains(const struct ovsdb_column_set *,
+ unsigned int column_index);
+bool ovsdb_column_set_equals(const struct ovsdb_column_set *,
+ const struct ovsdb_column_set *);
+
+#endif /* column.h */
--- /dev/null
+/* Copyright (c) 2009 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 "condition.h"
+
+#include <limits.h>
+
+#include "column.h"
+#include "json.h"
+#include "ovsdb-error.h"
+#include "row.h"
+#include "table.h"
+
+struct ovsdb_error *
+ovsdb_function_from_string(const char *name, enum ovsdb_function *function)
+{
+#define OVSDB_FUNCTION(ENUM, NAME) \
+ if (!strcmp(name, NAME)) { \
+ *function = ENUM; \
+ return NULL; \
+ }
+ OVSDB_FUNCTIONS;
+#undef OVSDB_FUNCTION
+
+ return ovsdb_syntax_error(NULL, "unknown function",
+ "No function named %s.", name);
+}
+
+const char *
+ovsdb_function_to_string(enum ovsdb_function function)
+{
+ switch (function) {
+#define OVSDB_FUNCTION(ENUM, NAME) case ENUM: return NAME;
+ OVSDB_FUNCTIONS;
+#undef OVSDB_FUNCTION
+ }
+
+ return NULL;
+}
+
+
+static WARN_UNUSED_RESULT struct ovsdb_error *
+ovsdb_clause_from_json(const struct ovsdb_table_schema *ts,
+ const struct json *json,
+ const struct ovsdb_symbol_table *symtab,
+ struct ovsdb_clause *clause)
+{
+ const struct json_array *array;
+ struct ovsdb_error *error;
+ const char *function_name;
+ const char *column_name;
+ struct ovsdb_type type;
+
+ if (json->type != JSON_ARRAY
+ || json->u.array.n != 3
+ || json->u.array.elems[0]->type != JSON_STRING
+ || json->u.array.elems[1]->type != JSON_STRING) {
+ return ovsdb_syntax_error(json, NULL, "Parse error in condition.");
+ }
+ array = json_array(json);
+
+ column_name = json_string(array->elems[0]);
+ clause->column = ovsdb_table_schema_get_column(ts, column_name);
+ if (!clause->column) {
+ return ovsdb_syntax_error(json, "unknown column",
+ "No column %s in table %s.",
+ column_name, ts->name);
+ }
+ type = clause->column->type;
+
+ function_name = json_string(array->elems[1]);
+ error = ovsdb_function_from_string(function_name, &clause->function);
+ if (error) {
+ return error;
+ }
+
+ /* Type-check and relax restrictions on 'type' if appropriate. */
+ switch (clause->function) {
+ case OVSDB_F_LT:
+ case OVSDB_F_LE:
+ case OVSDB_F_GT:
+ case OVSDB_F_GE:
+ /* XXX should we also allow these operators for types with n_min == 0,
+ * n_max == 1? (They would always be "false" if the value was
+ * missing.) */
+ if (!ovsdb_type_is_scalar(&type)
+ || (type.key_type != OVSDB_TYPE_INTEGER
+ && type.key_type != OVSDB_TYPE_REAL)) {
+ char *s = ovsdb_type_to_english(&type);
+ error = ovsdb_syntax_error(
+ json, NULL, "Type mismatch: \"%s\" operator may not be "
+ "applied to column %s of type %s.",
+ ovsdb_function_to_string(clause->function),
+ clause->column->name, s);
+ free(s);
+ return error;
+ }
+ break;
+
+ case OVSDB_F_EQ:
+ case OVSDB_F_NE:
+ break;
+
+ case OVSDB_F_EXCLUDES:
+ if (!ovsdb_type_is_scalar(&type)) {
+ type.n_min = 0;
+ type.n_max = UINT_MAX;
+ }
+ break;
+
+ case OVSDB_F_INCLUDES:
+ if (!ovsdb_type_is_scalar(&type)) {
+ type.n_min = 0;
+ }
+ break;
+ }
+ return ovsdb_datum_from_json(&clause->arg, &type, array->elems[2], symtab);
+}
+
+static void
+ovsdb_clause_free(struct ovsdb_clause *clause)
+{
+ ovsdb_datum_destroy(&clause->arg, &clause->column->type);
+}
+
+static int
+compare_clauses_3way(const void *a_, const void *b_)
+{
+ const struct ovsdb_clause *a = a_;
+ const struct ovsdb_clause *b = b_;
+
+ if (a->function != b->function) {
+ /* Bring functions to the front based on the fraction of table rows
+ * that they are (heuristically) expected to leave in the query
+ * results. Note that "enum ovsdb_function" is intentionally ordered
+ * to make this trivial. */
+ return a->function < b->function ? -1 : 1;
+ } else if (a->column->index != b->column->index) {
+ if (a->column->index < OVSDB_N_STD_COLUMNS
+ || b->column->index < OVSDB_N_STD_COLUMNS) {
+ /* Bring the standard columns and in particular the UUID column
+ * (since OVSDB_COL_UUID has value 0) to the front. We have an
+ * index on the UUID column, so that makes our queries cheaper. */
+ return a->column->index < b->column->index ? -1 : 1;
+ } else {
+ /* Order clauses predictably to make testing easier. */
+ return strcmp(a->column->name, b->column->name);
+ }
+ } else {
+ return 0;
+ }
+}
+
+struct ovsdb_error *
+ovsdb_condition_from_json(const struct ovsdb_table_schema *ts,
+ const struct json *json,
+ const struct ovsdb_symbol_table *symtab,
+ struct ovsdb_condition *cnd)
+{
+ const struct json_array *array = json_array(json);
+ size_t i;
+
+ cnd->clauses = xmalloc(array->n * sizeof *cnd->clauses);
+ cnd->n_clauses = 0;
+ for (i = 0; i < array->n; i++) {
+ struct ovsdb_error *error;
+ error = ovsdb_clause_from_json(ts, array->elems[i], symtab,
+ &cnd->clauses[i]);
+ if (error) {
+ ovsdb_condition_destroy(cnd);
+ cnd->clauses = NULL;
+ cnd->n_clauses = 0;
+ return error;
+ }
+ cnd->n_clauses++;
+ }
+
+ /* A real database would have a query optimizer here. */
+ qsort(cnd->clauses, cnd->n_clauses, sizeof *cnd->clauses,
+ compare_clauses_3way);
+
+ return NULL;
+}
+
+static struct json *
+ovsdb_clause_to_json(const struct ovsdb_clause *clause)
+{
+ return json_array_create_3(
+ json_string_create(clause->column->name),
+ json_string_create(ovsdb_function_to_string(clause->function)),
+ ovsdb_datum_to_json(&clause->arg, &clause->column->type));
+}
+
+struct json *
+ovsdb_condition_to_json(const struct ovsdb_condition *cnd)
+{
+ struct json **clauses;
+ size_t i;
+
+ clauses = xmalloc(cnd->n_clauses * sizeof *clauses);
+ for (i = 0; i < cnd->n_clauses; i++) {
+ clauses[i] = ovsdb_clause_to_json(&cnd->clauses[i]);
+ }
+ return json_array_create(clauses, cnd->n_clauses);
+}
+
+bool
+ovsdb_condition_evaluate(const struct ovsdb_row *row,
+ const struct ovsdb_condition *cnd)
+{
+ size_t i;
+
+ for (i = 0; i < cnd->n_clauses; i++) {
+ const struct ovsdb_clause *c = &cnd->clauses[i];
+ const struct ovsdb_datum *field = &row->fields[c->column->index];
+ const struct ovsdb_datum *arg = &cnd->clauses[i].arg;
+ const struct ovsdb_type *type = &c->column->type;
+
+ if (ovsdb_type_is_scalar(type)) {
+ int cmp = ovsdb_atom_compare_3way(&field->keys[0], &arg->keys[0],
+ type->key_type);
+ switch (c->function) {
+ case OVSDB_F_LT:
+ return cmp < 0;
+ case OVSDB_F_LE:
+ return cmp <= 0;
+ case OVSDB_F_EQ:
+ case OVSDB_F_INCLUDES:
+ return cmp == 0;
+ case OVSDB_F_NE:
+ case OVSDB_F_EXCLUDES:
+ return cmp != 0;
+ case OVSDB_F_GE:
+ return cmp >= 0;
+ case OVSDB_F_GT:
+ return cmp > 0;
+ }
+ } else {
+ switch (c->function) {
+ case OVSDB_F_EQ:
+ return ovsdb_datum_equals(field, arg, type);
+ case OVSDB_F_NE:
+ return !ovsdb_datum_equals(field, arg, type);
+ case OVSDB_F_INCLUDES:
+ return ovsdb_datum_includes_all(arg, field, type);
+ case OVSDB_F_EXCLUDES:
+ return ovsdb_datum_excludes_all(arg, field, type);
+ case OVSDB_F_LT:
+ case OVSDB_F_LE:
+ case OVSDB_F_GE:
+ case OVSDB_F_GT:
+ NOT_REACHED();
+ }
+ }
+ NOT_REACHED();
+ }
+
+ return true;
+}
+
+void
+ovsdb_condition_destroy(struct ovsdb_condition *cnd)
+{
+ size_t i;
+
+ for (i = 0; i < cnd->n_clauses; i++) {
+ ovsdb_clause_free(&cnd->clauses[i]);
+ }
+ free(cnd->clauses);
+}
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef OVSDB_CONDITION_H
+#define OVSDB_CONDITION_H 1
+
+#include <stddef.h>
+#include "compiler.h"
+#include "ovsdb-data.h"
+
+struct json;
+struct ovsdb_table_schema;
+struct ovsdb_row;
+
+/* These list is ordered in ascending order of the fraction of tables row that
+ * they are (heuristically) expected to leave in query results. */
+#define OVSDB_FUNCTIONS \
+ OVSDB_FUNCTION(OVSDB_F_EQ, "==") \
+ OVSDB_FUNCTION(OVSDB_F_INCLUDES, "includes") \
+ OVSDB_FUNCTION(OVSDB_F_LE, "<=") \
+ OVSDB_FUNCTION(OVSDB_F_LT, "<") \
+ OVSDB_FUNCTION(OVSDB_F_GE, ">=") \
+ OVSDB_FUNCTION(OVSDB_F_GT, ">") \
+ OVSDB_FUNCTION(OVSDB_F_EXCLUDES, "excludes") \
+ OVSDB_FUNCTION(OVSDB_F_NE, "!=")
+
+enum ovsdb_function {
+#define OVSDB_FUNCTION(ENUM, NAME) ENUM,
+ OVSDB_FUNCTIONS
+#undef OVSDB_FUNCTION
+};
+
+struct ovsdb_error *ovsdb_function_from_string(const char *,
+ enum ovsdb_function *)
+ WARN_UNUSED_RESULT;
+const char *ovsdb_function_to_string(enum ovsdb_function);
+
+struct ovsdb_clause {
+ enum ovsdb_function function;
+ const struct ovsdb_column *column;
+ struct ovsdb_datum arg;
+};
+
+struct ovsdb_condition {
+ struct ovsdb_clause *clauses;
+ size_t n_clauses;
+};
+
+#define OVSDB_CONDITION_INITIALIZER { NULL, 0 }
+
+struct ovsdb_error *ovsdb_condition_from_json(
+ const struct ovsdb_table_schema *,
+ const struct json *, const struct ovsdb_symbol_table *,
+ struct ovsdb_condition *) WARN_UNUSED_RESULT;
+struct json *ovsdb_condition_to_json(const struct ovsdb_condition *);
+void ovsdb_condition_destroy(struct ovsdb_condition *);
+bool ovsdb_condition_evaluate(const struct ovsdb_row *,
+ const struct ovsdb_condition *);
+
+#endif /* ovsdb/condition.h */
--- /dev/null
+/* Copyright (c) 2009 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 "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_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 *
+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 },
+ { "delete", ovsdb_execute_delete },
+ { "wait", ovsdb_execute_wait },
+ { "commit", ovsdb_execute_commit },
+ { "abort", ovsdb_execute_abort },
+ };
+
+ 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) {
+ struct ovsdb_error *error;
+
+ error = ovsdb_syntax_error(params, NULL, "array expected");
+ 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;
+ error = NULL;
+ for (i = 0; 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;
+ ovsdb_error_destroy(error);
+ json_destroy(results);
+ return NULL;
+ }
+
+ /* 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());
+ }
+
+ 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,
+ struct json *result 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 *
+ovsdb_execute_abort(struct ovsdb_execution *x UNUSED,
+ struct ovsdb_parser *parser UNUSED,
+ struct json *result 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,
+ const 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);
+
+ uuid_generate(&row_uuid);
+ if (uuid_name) {
+ ovsdb_symbol_table_put(x->symtab, json_string(uuid_name), &row_uuid);
+ }
+
+ if (!error) {
+ error = parse_row(parser, "row", table, x->symtab, &row, NULL);
+ }
+ 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));
+ row = NULL;
+ }
+ 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 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 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;
+}
--- /dev/null
+/* Copyright (c) 2009 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 "file.h"
+
+#include <assert.h>
+#include <fcntl.h>
+
+#include "column.h"
+#include "log.h"
+#include "json.h"
+#include "ovsdb.h"
+#include "ovsdb-error.h"
+#include "row.h"
+#include "table.h"
+#include "transaction.h"
+#include "uuid.h"
+#include "util.h"
+
+#define THIS_MODULE VLM_ovsdb_file
+#include "vlog.h"
+
+static struct ovsdb_error *ovsdb_file_txn_from_json(struct ovsdb *,
+ const struct json *,
+ struct ovsdb_txn **);
+static void ovsdb_file_replica_create(struct ovsdb *, struct ovsdb_log *);
+
+struct ovsdb_error *
+ovsdb_file_open(const char *file_name, bool read_only, struct ovsdb **dbp)
+{
+ struct ovsdb_schema *schema;
+ struct ovsdb_error *error;
+ struct ovsdb_log *log;
+ struct json *json;
+ struct ovsdb *db;
+
+ error = ovsdb_log_open(file_name, read_only ? O_RDONLY : O_RDWR, &log);
+ if (error) {
+ return error;
+ }
+
+ error = ovsdb_log_read(log, &json);
+ if (error) {
+ return error;
+ } else if (!json) {
+ return ovsdb_io_error(EOF, "%s: database file contains no schema",
+ file_name);
+ }
+
+ error = ovsdb_schema_from_json(json, &schema);
+ if (error) {
+ json_destroy(json);
+ return ovsdb_wrap_error(error,
+ "failed to parse \"%s\" as ovsdb schema",
+ file_name);
+ }
+ json_destroy(json);
+
+ db = ovsdb_create(schema);
+ while ((error = ovsdb_log_read(log, &json)) == NULL && json) {
+ struct ovsdb_txn *txn;
+
+ error = ovsdb_file_txn_from_json(db, json, &txn);
+ json_destroy(json);
+ if (error) {
+ break;
+ }
+
+ ovsdb_txn_commit(txn, false);
+ }
+ if (error) {
+ char *msg = ovsdb_error_to_string(error);
+ VLOG_WARN("%s", msg);
+ free(msg);
+
+ ovsdb_error_destroy(error);
+ }
+
+ if (!read_only) {
+ ovsdb_file_replica_create(db, log);
+ } else {
+ ovsdb_log_close(log);
+ }
+
+ *dbp = db;
+ return NULL;
+}
+
+static struct ovsdb_error *
+ovsdb_file_txn_row_from_json(struct ovsdb_txn *txn, struct ovsdb_table *table,
+ const struct uuid *row_uuid, struct json *json)
+{
+ const struct ovsdb_row *row = ovsdb_table_get_row(table, row_uuid);
+ if (json->type == JSON_NULL) {
+ if (!row) {
+ return ovsdb_syntax_error(NULL, NULL, "transaction deletes "
+ "row "UUID_FMT" that does not exist",
+ UUID_ARGS(row_uuid));
+ }
+ ovsdb_txn_row_delete(txn, row);
+ return NULL;
+ } else if (row) {
+ return ovsdb_row_from_json(ovsdb_txn_row_modify(txn, row),
+ json, NULL, NULL);
+ } else {
+ struct ovsdb_error *error;
+ struct ovsdb_row *new;
+
+ new = ovsdb_row_create(table);
+ *ovsdb_row_get_uuid_rw(new) = *row_uuid;
+ error = ovsdb_row_from_json(new, json, NULL, NULL);
+ if (error) {
+ ovsdb_row_destroy(new);
+ }
+
+ ovsdb_txn_row_insert(txn, new);
+
+ return error;
+ }
+}
+
+static struct ovsdb_error *
+ovsdb_file_txn_table_from_json(struct ovsdb_txn *txn,
+ struct ovsdb_table *table, struct json *json)
+{
+ struct shash_node *node;
+
+ if (json->type != JSON_OBJECT) {
+ return ovsdb_syntax_error(json, NULL, "object expected");
+ }
+
+ SHASH_FOR_EACH (node, json->u.object) {
+ const char *uuid_string = node->name;
+ struct json *txn_row_json = node->data;
+ struct ovsdb_error *error;
+ struct uuid row_uuid;
+
+ if (!uuid_from_string(&row_uuid, uuid_string)) {
+ return ovsdb_syntax_error(json, NULL, "\"%s\" is not a valid UUID",
+ uuid_string);
+ }
+
+ error = ovsdb_file_txn_row_from_json(txn, table, &row_uuid,
+ txn_row_json);
+ if (error) {
+ return error;
+ }
+ }
+
+ return NULL;
+}
+
+static struct ovsdb_error *
+ovsdb_file_txn_from_json(struct ovsdb *db, const struct json *json,
+ struct ovsdb_txn **txnp)
+{
+ struct ovsdb_error *error;
+ struct shash_node *node;
+ struct ovsdb_txn *txn;
+
+ *txnp = NULL;
+ if (json->type != JSON_OBJECT) {
+ return ovsdb_syntax_error(json, NULL, "object expected");
+ }
+
+ txn = ovsdb_txn_create(db);
+ SHASH_FOR_EACH (node, json->u.object) {
+ const char *table_name = node->name;
+ struct json *txn_table_json = node->data;
+ struct ovsdb_table *table;
+
+ table = shash_find_data(&db->tables, table_name);
+ if (!table) {
+ error = ovsdb_syntax_error(json, "unknown table",
+ "No table named %s.", table_name);
+ goto error;
+ }
+
+ error = ovsdb_file_txn_table_from_json(txn, table, txn_table_json);
+ if (error) {
+ goto error;
+ }
+ }
+ *txnp = txn;
+ return NULL;
+
+error:
+ ovsdb_txn_abort(txn);
+ return error;
+}
+\f
+/* Replica implementation. */
+
+struct ovsdb_file_replica {
+ struct ovsdb_replica replica;
+ struct ovsdb_log *log;
+};
+
+static const struct ovsdb_replica_class ovsdb_file_replica_class;
+
+static void
+ovsdb_file_replica_create(struct ovsdb *db, struct ovsdb_log *log)
+{
+ struct ovsdb_file_replica *r = xmalloc(sizeof *r);
+ ovsdb_replica_init(&r->replica, &ovsdb_file_replica_class);
+ r->log = log;
+ ovsdb_add_replica(db, &r->replica);
+
+}
+
+static struct ovsdb_file_replica *
+ovsdb_file_replica_cast(struct ovsdb_replica *replica)
+{
+ assert(replica->class == &ovsdb_file_replica_class);
+ return CONTAINER_OF(replica, struct ovsdb_file_replica, replica);
+}
+
+struct ovsdb_file_replica_aux {
+ struct json *json; /* JSON for the whole transaction. */
+ struct json *table_json; /* JSON for 'table''s transaction. */
+ struct ovsdb_table *table; /* Table described in 'table_json'. */
+};
+
+static bool
+ovsdb_file_replica_change_cb(const struct ovsdb_row *old,
+ const struct ovsdb_row *new,
+ void *aux_)
+{
+ struct ovsdb_file_replica_aux *aux = aux_;
+ struct json *row;
+
+ if (!new) {
+ row = json_null_create();
+ } else {
+ struct shash_node *node;
+
+ row = NULL;
+ SHASH_FOR_EACH (node, &new->table->schema->columns) {
+ const struct ovsdb_column *column = node->data;
+ const struct ovsdb_type *type = &column->type;
+ unsigned int idx = column->index;
+
+ if (idx != OVSDB_COL_UUID && column->persistent
+ && (!old || !ovsdb_datum_equals(&old->fields[idx],
+ &new->fields[idx], type)))
+ {
+ if (!row) {
+ row = json_object_create();
+ }
+ json_object_put(row, column->name,
+ ovsdb_datum_to_json(&new->fields[idx], type));
+ }
+ }
+ }
+
+ if (row) {
+ struct ovsdb_table *table = new ? new->table : old->table;
+ char uuid[UUID_LEN + 1];
+
+ if (table != aux->table) {
+ /* Create JSON object for transaction overall. */
+ if (!aux->json) {
+ aux->json = json_object_create();
+ }
+
+ /* Create JSON object for transaction on this table. */
+ aux->table_json = json_object_create();
+ aux->table = table;
+ json_object_put(aux->json, table->schema->name, aux->table_json);
+ }
+
+ /* Add row to transaction for this table. */
+ snprintf(uuid, sizeof uuid,
+ UUID_FMT, UUID_ARGS(ovsdb_row_get_uuid(new ? new : old)));
+ json_object_put(aux->table_json, uuid, row);
+ }
+
+ return true;
+}
+
+static struct ovsdb_error *
+ovsdb_file_replica_commit(struct ovsdb_replica *r_,
+ const struct ovsdb_txn *txn, bool durable)
+{
+ struct ovsdb_file_replica *r = ovsdb_file_replica_cast(r_);
+ struct ovsdb_file_replica_aux aux;
+ struct ovsdb_error *error;
+
+ aux.json = NULL;
+ aux.table_json = NULL;
+ aux.table = NULL;
+ ovsdb_txn_for_each_change(txn, ovsdb_file_replica_change_cb, &aux);
+
+ if (!aux.json) {
+ /* Nothing to commit. */
+ return NULL;
+ }
+
+ error = ovsdb_log_write(r->log, aux.json);
+ json_destroy(aux.json);
+ if (error) {
+ return ovsdb_wrap_error(error, "writing transaction failed");
+ }
+
+ if (durable) {
+ error = ovsdb_log_commit(r->log);
+ if (error) {
+ return ovsdb_wrap_error(error, "committing transaction failed");
+ }
+ }
+
+ return NULL;
+}
+
+static void
+ovsdb_file_replica_destroy(struct ovsdb_replica *r_)
+{
+ struct ovsdb_file_replica *r = ovsdb_file_replica_cast(r_);
+
+ ovsdb_log_close(r->log);
+ free(r);
+}
+
+static const struct ovsdb_replica_class ovsdb_file_replica_class = {
+ ovsdb_file_replica_commit,
+ ovsdb_file_replica_destroy
+};
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef OVSDB_FILE_H
+#define OVSDB_FILE_H 1
+
+#include <stdbool.h>
+#include "compiler.h"
+
+struct ovsdb;
+
+struct ovsdb_error *ovsdb_file_open(const char *file_name, bool read_only,
+ struct ovsdb **)
+ WARN_UNUSED_RESULT;
+
+#endif /* ovsdb/file.h */
--- /dev/null
+/* Copyright (c) 2009 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 <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_session;
+
+/* Sessions. */
+static void ovsdb_jsonrpc_session_create_active(struct ovsdb_jsonrpc_server *,
+ const char *name);
+static void ovsdb_jsonrpc_session_create_passive(struct ovsdb_jsonrpc_server *,
+ struct stream *);
+static void ovsdb_jsonrpc_session_run_all(struct ovsdb_jsonrpc_server *);
+static void ovsdb_jsonrpc_session_wait_all(struct ovsdb_jsonrpc_server *);
+
+/* 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;
+
+ struct list sessions; /* List of "struct ovsdb_jsonrpc_session"s. */
+ unsigned int n_sessions, max_sessions;
+ unsigned int max_triggers;
+
+ struct pstream **listeners;
+ size_t n_listeners, allocated_listeners;
+};
+
+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;
+ server->max_triggers = 64;
+ list_init(&server->sessions);
+ return server;
+}
+
+int
+ovsdb_jsonrpc_server_listen(struct ovsdb_jsonrpc_server *svr, const char *name)
+{
+ struct pstream *pstream;
+ int error;
+
+ error = pstream_open(name, &pstream);
+ if (error) {
+ return error;
+ }
+
+ if (svr->n_listeners >= svr->allocated_listeners) {
+ svr->listeners = x2nrealloc(svr->listeners, &svr->allocated_listeners,
+ sizeof *svr->listeners);
+ }
+ svr->listeners[svr->n_listeners++] = pstream;
+ return 0;
+}
+
+void
+ovsdb_jsonrpc_server_connect(struct ovsdb_jsonrpc_server *svr,
+ const char *name)
+{
+ ovsdb_jsonrpc_session_create_active(svr, name);
+}
+
+void
+ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *svr)
+{
+ size_t i;
+
+ /* Accept new connections. */
+ for (i = 0; i < svr->n_listeners && svr->n_sessions < svr->max_sessions;) {
+ struct pstream *listener = svr->listeners[i];
+ struct stream *stream;
+ int error;
+
+ error = pstream_accept(listener, &stream);
+ if (!error) {
+ ovsdb_jsonrpc_session_create_passive(svr, stream);
+ } else if (error == EAGAIN) {
+ i++;
+ } else if (error) {
+ VLOG_WARN("%s: accept failed: %s",
+ pstream_get_name(listener), strerror(error));
+ pstream_close(listener);
+ svr->listeners[i] = svr->listeners[--svr->n_listeners];
+ }
+ }
+
+ /* Handle each session. */
+ ovsdb_jsonrpc_session_run_all(svr);
+}
+
+void
+ovsdb_jsonrpc_server_wait(struct ovsdb_jsonrpc_server *svr)
+{
+ if (svr->n_sessions < svr->max_sessions) {
+ size_t i;
+
+ for (i = 0; i < svr->n_listeners; i++) {
+ pstream_wait(svr->listeners[i]);
+ }
+ }
+
+ ovsdb_jsonrpc_session_wait_all(svr);
+}
+\f
+/* JSON-RPC database server session. */
+
+struct ovsdb_jsonrpc_session {
+ struct ovsdb_jsonrpc_server *server;
+ struct list node; /* Element in server'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. */
+
+ /* Connecting and reconnecting. */
+ struct reconnect *reconnect; /* For back-off. */
+ bool active; /* Active or passive connection? */
+ struct jsonrpc *rpc;
+ struct stream *stream; /* Only if active == false and rpc == NULL. */
+};
+
+static void ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *);
+static void ovsdb_jsonrpc_session_disconnect(struct ovsdb_jsonrpc_session *s);
+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_server *svr,
+ const char *name, bool active)
+{
+ struct ovsdb_jsonrpc_session *s;
+
+ s = xzalloc(sizeof *s);
+ s->server = svr;
+ list_push_back(&svr->sessions, &s->node);
+ hmap_init(&s->triggers);
+ hmap_init(&s->monitors);
+ list_init(&s->completions);
+ s->reconnect = reconnect_create(time_msec());
+ reconnect_set_name(s->reconnect, name);
+ reconnect_enable(s->reconnect, time_msec());
+ s->active = active;
+
+ svr->n_sessions++;
+
+ return s;
+}
+
+static void
+ovsdb_jsonrpc_session_create_active(struct ovsdb_jsonrpc_server *svr,
+ const char *name)
+{
+ ovsdb_jsonrpc_session_create(svr, name, true);
+}
+
+static void
+ovsdb_jsonrpc_session_create_passive(struct ovsdb_jsonrpc_server *svr,
+ struct stream *stream)
+{
+ struct ovsdb_jsonrpc_session *s;
+
+ s = ovsdb_jsonrpc_session_create(svr, stream_get_name(stream), false);
+ reconnect_connected(s->reconnect, time_msec());
+ s->rpc = jsonrpc_open(stream);
+}
+
+static void
+ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *s)
+{
+ ovsdb_jsonrpc_session_disconnect(s);
+ list_remove(&s->node);
+ s->server->n_sessions--;
+}
+
+static void
+ovsdb_jsonrpc_session_disconnect(struct ovsdb_jsonrpc_session *s)
+{
+ reconnect_disconnected(s->reconnect, time_msec(), 0);
+ if (s->rpc) {
+ jsonrpc_error(s->rpc, EOF);
+ ovsdb_jsonrpc_trigger_complete_all(s);
+ ovsdb_jsonrpc_monitor_remove_all(s);
+ jsonrpc_close(s->rpc);
+ s->rpc = NULL;
+ } else if (s->stream) {
+ stream_close(s->stream);
+ s->stream = NULL;
+ }
+}
+
+static void
+ovsdb_jsonrpc_session_connect(struct ovsdb_jsonrpc_session *s)
+{
+ ovsdb_jsonrpc_session_disconnect(s);
+ if (s->active) {
+ int error = stream_open(reconnect_get_name(s->reconnect), &s->stream);
+ if (error) {
+ reconnect_connect_failed(s->reconnect, time_msec(), error);
+ } else {
+ reconnect_connecting(s->reconnect, time_msec());
+ }
+ }
+}
+
+static int
+ovsdb_jsonrpc_session_run(struct ovsdb_jsonrpc_session *s)
+{
+ if (s->rpc) {
+ struct jsonrpc_msg *msg;
+ int error;
+
+ jsonrpc_run(s->rpc);
+
+ ovsdb_jsonrpc_trigger_complete_done(s);
+
+ if (!jsonrpc_get_backlog(s->rpc) && !jsonrpc_recv(s->rpc, &msg)) {
+ reconnect_received(s->reconnect, time_msec());
+ 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 if (msg->type == JSONRPC_REPLY
+ && msg->id && msg->id->type == JSON_STRING
+ && !strcmp(msg->id->u.string, "echo")) {
+ /* It's a reply to our echo request. Ignore it. */
+ } else {
+ VLOG_WARN("%s: received unexpected %s message",
+ jsonrpc_get_name(s->rpc),
+ jsonrpc_msg_type_to_string(msg->type));
+ jsonrpc_error(s->rpc, EPROTO);
+ jsonrpc_msg_destroy(msg);
+ }
+ }
+
+ error = jsonrpc_get_status(s->rpc);
+ if (error) {
+ if (s->active) {
+ ovsdb_jsonrpc_session_disconnect(s);
+ } else {
+ return error;
+ }
+ }
+ } else if (s->stream) {
+ int error = stream_connect(s->stream);
+ if (!error) {
+ reconnect_connected(s->reconnect, time_msec());
+ s->rpc = jsonrpc_open(s->stream);
+ s->stream = NULL;
+ } else if (error != EAGAIN) {
+ reconnect_connect_failed(s->reconnect, time_msec(), error);
+ stream_close(s->stream);
+ s->stream = NULL;
+ }
+ }
+
+ switch (reconnect_run(s->reconnect, time_msec())) {
+ case RECONNECT_CONNECT:
+ ovsdb_jsonrpc_session_connect(s);
+ break;
+
+ case RECONNECT_DISCONNECT:
+ ovsdb_jsonrpc_session_disconnect(s);
+ break;
+
+ case RECONNECT_PROBE:
+ if (s->rpc) {
+ struct json *params;
+ struct jsonrpc_msg *request;
+
+ params = json_array_create_empty();
+ request = jsonrpc_create_request("echo", params, NULL);
+ json_destroy(request->id);
+ request->id = json_string_create("echo");
+ jsonrpc_send(s->rpc, request);
+ }
+ break;
+ }
+ return s->active || s->rpc ? 0 : ETIMEDOUT;
+}
+
+static void
+ovsdb_jsonrpc_session_run_all(struct ovsdb_jsonrpc_server *svr)
+{
+ struct ovsdb_jsonrpc_session *s, *next;
+
+ LIST_FOR_EACH_SAFE (s, next, struct ovsdb_jsonrpc_session, node,
+ &svr->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)
+{
+ if (s->rpc) {
+ jsonrpc_wait(s->rpc);
+ if (!jsonrpc_get_backlog(s->rpc)) {
+ jsonrpc_recv_wait(s->rpc);
+ }
+ } else if (s->stream) {
+ stream_connect_wait(s->stream);
+ }
+ reconnect_wait(s->reconnect, time_msec());
+}
+
+static void
+ovsdb_jsonrpc_session_wait_all(struct ovsdb_jsonrpc_server *svr)
+{
+ struct ovsdb_jsonrpc_session *s;
+
+ LIST_FOR_EACH (s, struct ovsdb_jsonrpc_session, node, &svr->sessions) {
+ ovsdb_jsonrpc_session_wait(s);
+ }
+}
+
+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;
+ 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 = execute_transaction(s, request);
+ } else if (!strcmp(request->method, "monitor")) {
+ 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 = jsonrpc_create_reply(
+ ovsdb_schema_to_json(s->server->db->schema), 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_send(s->rpc, 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) {
+ jsonrpc_send(s->rpc, jsonrpc_create_error(
+ json_string_create("duplicate request ID"), id));
+ json_destroy(id);
+ json_destroy(params);
+ return;
+ }
+
+ /* Insert into trigger table. */
+ t = xmalloc(sizeof *t);
+ ovsdb_trigger_init(s->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 (s->rpc && !jsonrpc_get_status(s->rpc)) {
+ 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_send(s->rpc, 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 != 2) {
+ error = ovsdb_syntax_error(params, NULL, "invalid parameters");
+ goto error;
+ }
+ monitor_id = params->u.array.elems[0];
+ monitor_requests = params->u.array.elems[1];
+ 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->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->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->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->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->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 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_send(aux.monitor->session->rpc, 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
+};
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef OVSDB_JSONRPC_SERVER_H
+#define OVSDB_JSONRPC_SERVER_H 1
+
+struct ovsdb;
+
+struct ovsdb_jsonrpc_server *ovsdb_jsonrpc_server_create(struct ovsdb *);
+
+int ovsdb_jsonrpc_server_listen(struct ovsdb_jsonrpc_server *,
+ const char *name);
+void ovsdb_jsonrpc_server_connect(struct ovsdb_jsonrpc_server *,
+ const char *name);
+
+void ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *);
+void ovsdb_jsonrpc_server_wait(struct ovsdb_jsonrpc_server *);
+
+#endif /* ovsdb/jsonrpc-server.h */
--- /dev/null
+/* Copyright (c) 2009 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 "log.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "json.h"
+#include "lockfile.h"
+#include "ovsdb.h"
+#include "ovsdb-error.h"
+#include "sha1.h"
+#include "transaction.h"
+#include "util.h"
+
+#define THIS_MODULE VLM_ovsdb_log
+#include "vlog.h"
+
+enum ovsdb_log_mode {
+ OVSDB_LOG_READ,
+ OVSDB_LOG_WRITE
+};
+
+struct ovsdb_log {
+ off_t offset;
+ char *name;
+ struct lockfile *lockfile;
+ FILE *stream;
+ struct ovsdb_error *read_error;
+ struct ovsdb_error *write_error;
+ enum ovsdb_log_mode mode;
+};
+
+struct ovsdb_error *
+ovsdb_log_open(const char *name, int flags, struct ovsdb_log **filep)
+{
+ struct lockfile *lockfile;
+ struct ovsdb_error *error;
+ struct ovsdb_log *file;
+ struct stat s;
+ FILE *stream;
+ int accmode;
+ int fd;
+
+ *filep = NULL;
+
+ accmode = flags & O_ACCMODE;
+ if (accmode == O_RDWR || accmode == O_WRONLY) {
+ int retval = lockfile_lock(name, 0, &lockfile);
+ if (retval) {
+ error = ovsdb_io_error(retval, "%s: failed to lock lockfile",
+ name);
+ goto error;
+ }
+ } else {
+ lockfile = NULL;
+ }
+
+ fd = open(name, flags, 0666);
+ if (fd < 0) {
+ const char *op = flags & O_CREAT && flags & O_EXCL ? "create" : "open";
+ error = ovsdb_io_error(errno, "%s: %s failed", op, name);
+ goto error_unlock;
+ }
+
+ if (!fstat(fd, &s) && s.st_size == 0) {
+ /* It's (probably) a new file so fsync() its parent directory to ensure
+ * that its directory entry is committed to disk. */
+ char *dir = dir_name(name);
+ int dirfd = open(dir, O_RDONLY);
+ if (dirfd >= 0) {
+ if (fsync(dirfd) && errno != EINVAL) {
+ VLOG_ERR("%s: fsync failed (%s)", dir, strerror(errno));
+ }
+ close(dirfd);
+ } else {
+ VLOG_ERR("%s: open failed (%s)", dir, strerror(errno));
+ }
+ free(dir);
+ }
+
+ stream = fdopen(fd, (accmode == O_RDONLY ? "rb"
+ : accmode == O_WRONLY ? "wb"
+ : "w+b"));
+ if (!stream) {
+ error = ovsdb_io_error(errno, "%s: fdopen failed", name);
+ goto error_close;
+ }
+
+ file = xmalloc(sizeof *file);
+ file->name = xstrdup(name);
+ file->lockfile = lockfile;
+ file->stream = stream;
+ file->offset = 0;
+ file->read_error = NULL;
+ file->write_error = NULL;
+ file->mode = OVSDB_LOG_READ;
+ *filep = file;
+ return NULL;
+
+error_close:
+ close(fd);
+error_unlock:
+ lockfile_unlock(lockfile);
+error:
+ return error;
+}
+
+void
+ovsdb_log_close(struct ovsdb_log *file)
+{
+ if (file) {
+ free(file->name);
+ fclose(file->stream);
+ lockfile_unlock(file->lockfile);
+ ovsdb_error_destroy(file->read_error);
+ ovsdb_error_destroy(file->write_error);
+ free(file);
+ }
+}
+
+static const char magic[] = "OVSDB JSON ";
+
+static bool
+parse_header(char *header, unsigned long int *length,
+ uint8_t sha1[SHA1_DIGEST_SIZE])
+{
+ char *p;
+
+ /* 'header' must consist of a magic string... */
+ if (strncmp(header, magic, strlen(magic))) {
+ return false;
+ }
+
+ /* ...followed by a length in bytes... */
+ *length = strtoul(header + strlen(magic), &p, 10);
+ if (!*length || *length == ULONG_MAX || *p != ' ') {
+ return false;
+ }
+ p++;
+
+ /* ...followed by a SHA-1 hash... */
+ if (!sha1_from_hex(sha1, p)) {
+ return false;
+ }
+ p += SHA1_HEX_DIGEST_LEN;
+
+ /* ...and ended by a new-line. */
+ if (*p != '\n') {
+ return false;
+ }
+
+ return true;
+}
+
+struct ovsdb_log_read_cbdata {
+ char input[4096];
+ struct ovsdb_log *file;
+ int error;
+ unsigned long length;
+};
+
+static struct ovsdb_error *
+parse_body(struct ovsdb_log *file, off_t offset, unsigned long int length,
+ uint8_t sha1[SHA1_DIGEST_SIZE], struct json **jsonp)
+{
+ unsigned long int bytes_left;
+ struct json_parser *parser;
+ struct sha1_ctx ctx;
+
+ sha1_init(&ctx);
+ parser = json_parser_create(JSPF_TRAILER);
+
+ bytes_left = length;
+ while (length > 0) {
+ char input[BUFSIZ];
+ int chunk;
+
+ chunk = MIN(length, sizeof input);
+ if (fread(input, 1, chunk, file->stream) != chunk) {
+ json_parser_abort(parser);
+ return ovsdb_io_error(ferror(file->stream) ? errno : EOF,
+ "%s: error reading %lu bytes "
+ "starting at offset %lld", file->name,
+ length, (long long int) offset);
+ }
+ sha1_update(&ctx, input, chunk);
+ json_parser_feed(parser, input, chunk);
+ length -= chunk;
+ }
+
+ sha1_final(&ctx, sha1);
+ *jsonp = json_parser_finish(parser);
+ return NULL;
+}
+
+struct ovsdb_error *
+ovsdb_log_read(struct ovsdb_log *file, struct json **jsonp)
+{
+ uint8_t expected_sha1[SHA1_DIGEST_SIZE];
+ uint8_t actual_sha1[SHA1_DIGEST_SIZE];
+ struct ovsdb_error *error;
+ off_t data_offset;
+ unsigned long data_length;
+ struct json *json;
+ char header[128];
+
+ *jsonp = json = NULL;
+
+ if (file->read_error) {
+ return ovsdb_error_clone(file->read_error);
+ } else if (file->mode == OVSDB_LOG_WRITE) {
+ return OVSDB_BUG("reading file in write mode");
+ }
+
+ if (!fgets(header, sizeof header, file->stream)) {
+ if (feof(file->stream)) {
+ error = NULL;
+ } else {
+ error = ovsdb_io_error(errno, "%s: read failed", file->name);
+ }
+ goto error;
+ }
+
+ if (!parse_header(header, &data_length, expected_sha1)) {
+ error = ovsdb_syntax_error(NULL, NULL, "%s: parse error at offset "
+ "%lld in header line \"%.*s\"",
+ file->name, (long long int) file->offset,
+ (int) strcspn(header, "\n"), header);
+ goto error;
+ }
+
+ data_offset = file->offset + strlen(header);
+ error = parse_body(file, data_offset, data_length, actual_sha1, &json);
+ if (error) {
+ goto error;
+ }
+
+ if (memcmp(expected_sha1, actual_sha1, SHA1_DIGEST_SIZE)) {
+ error = ovsdb_syntax_error(NULL, NULL, "%s: %lu bytes starting at "
+ "offset %lld have SHA-1 hash "SHA1_FMT" "
+ "but should have hash "SHA1_FMT,
+ file->name, data_length,
+ (long long int) data_offset,
+ SHA1_ARGS(actual_sha1),
+ SHA1_ARGS(expected_sha1));
+ goto error;
+ }
+
+ if (json->type == JSON_STRING) {
+ error = ovsdb_syntax_error(NULL, NULL, "%s: %lu bytes starting at "
+ "offset %lld are not valid JSON (%s)",
+ file->name, data_length,
+ (long long int) data_offset,
+ json->u.string);
+ goto error;
+ }
+
+ file->offset = data_offset + data_length;
+ *jsonp = json;
+ return 0;
+
+error:
+ file->read_error = ovsdb_error_clone(error);
+ json_destroy(json);
+ return error;
+}
+
+struct ovsdb_error *
+ovsdb_log_write(struct ovsdb_log *file, struct json *json)
+{
+ uint8_t sha1[SHA1_DIGEST_SIZE];
+ struct ovsdb_error *error;
+ char *json_string;
+ char header[128];
+ size_t length;
+
+ json_string = NULL;
+
+ if (file->write_error) {
+ return ovsdb_error_clone(file->write_error);
+ } else if (file->mode == OVSDB_LOG_READ) {
+ file->mode = OVSDB_LOG_WRITE;
+ if (fseeko(file->stream, file->offset, SEEK_SET)) {
+ error = ovsdb_io_error(errno, "%s: cannot seek to offset %lld",
+ file->name, (long long int) file->offset);
+ goto error;
+ }
+ if (ftruncate(fileno(file->stream), file->offset)) {
+ error = ovsdb_io_error(errno, "%s: cannot truncate to length %lld",
+ file->name, (long long int) file->offset);
+ goto error;
+ }
+ }
+
+ if (json->type != JSON_OBJECT && json->type != JSON_ARRAY) {
+ error = OVSDB_BUG("bad JSON type");
+ goto error;
+ }
+
+ /* Compose content. Add a new-line (replacing the null terminator) to make
+ * the file easier to read, even though it has no semantic value. */
+ json_string = json_to_string(json, 0);
+ length = strlen(json_string) + 1;
+ json_string[length - 1] = '\n';
+
+ /* Compose header. */
+ sha1_bytes(json_string, length, sha1);
+ snprintf(header, sizeof header, "%s%zu "SHA1_FMT"\n",
+ magic, length, SHA1_ARGS(sha1));
+
+ /* Write. */
+ if (fwrite(header, strlen(header), 1, file->stream) != 1
+ || fwrite(json_string, length, 1, file->stream) != 1
+ || fflush(file->stream))
+ {
+ error = ovsdb_io_error(errno, "%s: write failed", file->name);
+
+ /* Remove any partially written data, ignoring errors since there is
+ * nothing further we can do. */
+ ftruncate(fileno(file->stream), file->offset);
+
+ goto error;
+ }
+
+ file->offset += strlen(header) + length;
+ free(json_string);
+ return 0;
+
+error:
+ file->write_error = ovsdb_error_clone(error);
+ free(json_string);
+ return error;
+}
+
+struct ovsdb_error *
+ovsdb_log_commit(struct ovsdb_log *file)
+{
+ if (fsync(fileno(file->stream))) {
+ return ovsdb_io_error(errno, "%s: fsync failed", file->name);
+ }
+ return 0;
+}
+
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef OVSDB_LOG_H
+#define OVSDB_LOG_H 1
+
+#include <sys/types.h>
+#include "compiler.h"
+
+struct json;
+struct ovsdb_log;
+
+struct ovsdb_error *ovsdb_log_open(const char *name, int flags,
+ struct ovsdb_log **) WARN_UNUSED_RESULT;
+void ovsdb_log_close(struct ovsdb_log *);
+
+struct ovsdb_error *ovsdb_log_read(struct ovsdb_log *, struct json **)
+ WARN_UNUSED_RESULT;
+struct ovsdb_error *ovsdb_log_write(struct ovsdb_log *, struct json *)
+ WARN_UNUSED_RESULT;
+struct ovsdb_error *ovsdb_log_commit(struct ovsdb_log *)
+ WARN_UNUSED_RESULT;
+
+#endif /* ovsdb/log.h */
--- /dev/null
+.\" -*- nroff -*-
+.de IQ
+. br
+. ns
+. IP "\\$1"
+..
+.\" -*- nroff -*-
+.TH ovsdb\-client 1 "November 2009" "Open vSwitch" "Open vSwitch Manual"
+.ds PN ovsdb\-client
+.
+.SH NAME
+ovsdb\-client \- command-line interface to \fBovsdb-server\fR(1)
+.
+.SH SYNOPSIS
+\fBovsdb\-client \fR[\fIoptions\fR] \fBget-schema\fI server\fR
+.br
+\fBovsdb\-client \fR[\fIoptions\fR] \fBlist-tables\fI server\fR
+.br
+\fBovsdb\-client \fR[\fIoptions\fR] \fBlist-columns\fI server \fR[\fItable\fR]
+.br
+\fBovsdb\-client \fR[\fIoptions\fR] \fBtransact\fI server transaction\fR
+.br
+\fBovsdb\-client \fR[\fIoptions\fR] \fBmonitor\fI server table\fR
+[\fIcolumn\fR[\fB,\fIcolumn\fR]...]
+[\fIselect\fR[\fB,\fIselect\fR]...]
+.br
+\fBovsdb\-client help\fR
+.IP "Output formatting options:"
+[\fB--format=\fIformat\fR]
+[\fB--wide\fR]
+[\fB--no-heading\fR]
+.so lib/vlog-syn.man
+.so lib/common-syn.man
+.
+.SH DESCRIPTION
+The \fBovsdb\-client\fR program is a command-line client for
+interacting with a running \fBovsdb\-server\fR process. For each
+command, the \fIserver\fR to connect to must be specified in one of
+the following forms:
+.IP "\fBtcp:\fIip\fB:\fIport\fR"
+Connect to the given TCP \fIport\fR on \fIip\fR.
+.IP "\fBunix:\fIfile\fR"
+Connect to the Unix domain server socket named \fIfile\fR.
+.IP "\fBptcp:\fIport\fR[\fB:\fIip\fR]"
+Listen on the given TCP \fIport\fR for a connection. By default,
+\fB\*(PN\fR listens for connections to any local IP address, but
+\fIip\fR may be specified to listen only for connections to the given
+\fIip\fR.
+.IP "\fBpunix:\fIfile\fR"
+Listen on the Unix domain server socket named \fIfile\fR for a
+connection.
+.
+.SS "Commands"
+The following commands are implemented:
+.IP "\fBget-schema\fI server\fR"
+Connects to \fIserver\fR, retrieves the database schema, and prints it
+in JSON format.
+.
+.IP "\fBlist-tables\fI server\fR"
+Connects to \fIserver\fR, retrieves the database schema, and prints
+a table listing the names and comments (if any) on each table within
+the database.
+.
+.IP "\fBlist-columns\fI server \fR[\fItable\fR]"
+Connects to \fIserver\fR, retrieves the database schema, and prints
+a table listing the names, type, and comment (if any) on each column. If
+\fItable\fR is specified, only columns in that table are listed;
+otherwise, the tables include columns in all tables.
+.
+.IP "\fBtransact\fI server transaction\fR"
+Connects to \fIserver\fR, sends it the specified \fItransaction\fR,
+which must be a JSON array containing one or more valid OVSDB
+operations, and prints the received reply on stdout.
+.
+.IP "\fBmonitor\fI server table\fR [\fIcolumn\fR[\fB,\fIcolumn\fR]...] [\fIselect\fR[\fB,\fIselect\fR]...]"
+Connects to \fIserver\fR and monitors the contents of \fItable\fR. By
+default, the initial contents of \fItable\fR are printed, followed by
+each change as it occurs. If at least one \fIcolumn\fR is specified,
+only those columns are monitored. If at least one \fIselect\fR is
+specified, they are interpreted as follows:
+.RS
+.IP "\fBinitial\fR"
+Print the initial contents of the specified columns.
+.IP "\fBinsert\fR"
+Print newly inserted rows.
+.IP "\fBdelete\fR"
+Print deleted rows.
+.IP "\fBmodify\fR"
+Print old and new values of modified rows.
+.RE
+.SH OPTIONS
+.SS "Output Formatting Options"
+Much of the output from \fBovsdb\-client\fR is in the form of tables.
+The following options controlling output formatting:
+.
+.IP "\fB-f \fIformat\fR"
+.IQ "\fB--format=\fIformat\fR"
+Sets the basic type of output formatting. The following types of
+\fIformat\fR are available:
+.RS
+.IP "\fBtable\fR (default)"
+Text-based tables with aligned columns.
+.IP "\fBhtml\fR"
+HTML tables.
+.IP "\fBcvs\fR"
+Comma-separated values as defined in RFC 4180.
+.RE
+.
+.IP "\fB--wide\fR"
+In \fBtable\fR output (the default), when standard output is a
+terminal device, by default lines are truncated at a width of 79
+characters. Specifying this option prevents line truncation.
+.
+.IP "\fB--no-heading\fR"
+This option suppresses the heading row that otherwise appears in the
+first row of table output.
+.
+.SS "Logging Options"
+.so lib/vlog.man
+.SS "Other Options"
+.so lib/common.man
+.SH "SEE ALSO"
+.
+\fBovsdb\-server\fR(1),
+\fBovsdb\-client\fR(1),
+and the OVSDB specification.
--- /dev/null
+/*
+ * Copyright (c) 2009 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 <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 "dynamic-string.h"
+#include "json.h"
+#include "jsonrpc.h"
+#include "ovsdb.h"
+#include "ovsdb-error.h"
+#include "stream.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;
+
+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[] = {
+ {"wide", no_argument, &output_width, INT_MAX},
+ {"format", required_argument, 0, 'f'},
+ {"no-headings", no_argument, &output_headings, 0},
+ {"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);
+
+ 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;
+
+ 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 get-schema SERVER\n"
+ " retrieve schema from SERVER\n"
+ "\n list-tables SERVER\n"
+ " list SERVER's tables\n"
+ "\n list-columns SERVER [TABLE]\n"
+ " list columns in TABLE (or all tables) 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 TABLE [COLUMN,...] [SELECT,...]\n"
+ " monitor contents of (COLUMNs in) TABLE on SERVER\n"
+ " Valid SELECTs are: initial, insert, delete, modify\n",
+ program_name, program_name);
+ stream_usage("SERVER", 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");
+ 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, JSSF_SORT);
+ 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)
+{
+ struct jsonrpc_msg *request, *reply;
+ struct ovsdb_schema *schema;
+ int error;
+
+ request = jsonrpc_create_request("get_schema", json_array_create_empty(),
+ 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)
+{
+ struct ovsdb_schema *schema;
+ struct jsonrpc *rpc;
+
+ rpc = open_jsonrpc(server);
+ schema = fetch_schema_from_rpc(rpc);
+ 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("&", stdout);
+ break;
+ case '<':
+ fputs("<", stdout);
+ break;
+ case '>':
+ fputs(">", 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[])
+{
+ struct ovsdb_schema *schema = fetch_schema(argv[1]);
+ print_and_free_json(ovsdb_schema_to_json(schema));
+ ovsdb_schema_destroy(schema);
+}
+
+static void
+do_list_tables(int argc UNUSED, char *argv[])
+{
+ struct ovsdb_schema *schema;
+ struct shash_node *node;
+ struct table t;
+
+ schema = fetch_schema(argv[1]);
+ 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_list_columns(int argc UNUSED, char *argv[])
+{
+ const char *table_name = argv[2];
+ struct ovsdb_schema *schema;
+ struct shash_node *table_node;
+ struct table t;
+
+ schema = fetch_schema(argv[1]);
+ 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) {
+ 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_transact(int argc 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, JSSF_SORT));
+ }
+ 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[])
+{
+ 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(argv[1]);
+
+ schema = fetch_schema_from_rpc(rpc);
+ table = shash_find_data(&schema->tables, argv[2]);
+ if (!table) {
+ ovs_fatal(0, "%s: no table named \"%s\"", argv[1], argv[2]);
+ }
+
+ if (argc >= 4 && *argv[3] != '\0') {
+ char *save_ptr = NULL;
+ char *token;
+
+ for (token = strtok_r(argv[3], ",", &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\" does not have a "
+ "column named \"%s\"", argv[1], argv[2], 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 >= 5 && *argv[4] != '\0') {
+ char *save_ptr = NULL;
+ char *token;
+
+ select = json_object_create();
+ for (token = strtok_r(argv[4], ",", &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, argv[2], monitor_request);
+
+ monitor = json_array_create_2(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) {
+ ovs_fatal(error, "%s: receive failed", argv[1]);
+ }
+
+ 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);
+ } 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);
+ }
+ }
+ }
+}
+
+static void
+do_help(int argc UNUSED, char *argv[] UNUSED)
+{
+ usage();
+}
+
+static const struct command all_commands[] = {
+ { "get-schema", 1, 1, do_get_schema },
+ { "list-tables", 1, 1, do_list_tables },
+ { "list-columns", 1, 2, do_list_columns },
+ { "transact", 2, 2, do_transact },
+ { "monitor", 2, 4, do_monitor },
+ { "help", 0, INT_MAX, do_help },
+ { NULL, 0, 0, NULL },
+};
--- /dev/null
+.\" -*- nroff -*-
+.TH ovsdb\-idlc 1 "November 2009" "Open vSwitch" "Open vSwitch Manual"
+.ds PN ovsdb\-idlc
+.
+.SH NAME
+ovsdb\-idlc \- Open vSwitch IDL (Interface Definition Language) compiler
+.
+.SH SYNOPSIS
+\fBovsdb\-idlc \fBvalidate\fI schema\fR
+.br
+\fBovsdb\-idlc \fBovsdb\-schema\fI schema\fR
+.br
+\fBovsdb\-idlc \fBc\-idl\-header\fI schema\fR
+.br
+\fBovsdb\-idlc \fBc\-idl\-source\fI schema\fR
+.br
+\fBovsdb\-idlc --help\fR
+.br
+\fBovsdb\-idlc --version\fR
+.
+.SH DESCRIPTION
+The \fBovsdb\-idlc\fR program is a command-line tool for translating
+Open vSwitch database interface definition language (IDL) schemas into
+other formats. It is used while building Open vSwitch, not at
+installation or configuration time. Thus, it is not normally
+installed as part of Open vSwitch.
+.
+.PP
+The \fIschema\fR files used as \fBovsdb\-idlc\fR input have the same
+format as the OVSDB schemas, specified in the OVSDB specification,
+with a few additions:
+.
+.IP "\fB//\fR comments"
+Lines that begin with \fB//\fR (two forward slashes) are ignored and
+thus can be used for comments.
+.
+.IP "\fB""\fBidlPrefix\fR"" member of <database-schema>"
+This member, which is required, specifies a string that is prefixed to
+top-level names in C bindings. It should probably end in an
+underscore.
+.
+.IP "\fB""\fBidlHeader\fR"" member of <database-schema>"
+This member, which is required, specifies the name of the IDL header.
+It will be output on an \fB#include\fR line in the source file
+generated by the C bindings. It should include the bracketing
+\fB""\fR or \fB<>\fR.
+.
+.IP "\fB""\fBkeyRefTable\fR"" member of <type>"
+A <type> whose \fBkey\fR is \fB"uuid"\fR may have an additional member
+named \fB"keyRefTable"\fR, whose value is a table name. This
+expresses the constraint that keys of the given <type> are UUIDs that
+reference rows in the named table. This allows the IDL to supply a
+structure pointer in place of a raw UUID in its marshalled version of
+the given type.
+.
+.IP "\fB""valueRefTable""\fR member of <type>"
+Analogous to \fB"keyRefTable"\fR in meaning and effect, except that it
+applies to the \fB"value"\fR member of the <type>.
+.SS "Commands"
+.
+.IP "\fBvalidate\fI schema\fR"
+Reads \fIschema\fR and checks its format, without producing any output.
+.
+.IP "\fBovsdb\-schema\fI schema\fR"
+Reads \fIschema\fR and prints it on standard output with the parts
+that are not part of the OVSDB schema specification stripped out.
+.
+.IP "\fBc\-idl\-header\fI schema\fR"
+Reads \fIschema\fR and prints on standard output a C header file that
+defines a structure for each table defined by the schema.
+.
+.IP "\fBc\-idl\-source\fI schema\fR"
+Reads \fIschema\fR and prints on standard output a C source file that
+implements C bindings for the database defined by the schema.
+.
+.SS "Options"
+.so lib/common.man
+.
+.SH "BUGS"
+\fBovsdb\-idlc\fR is more lenient about the format of OVSDB schemas
+than other OVSDB tools, so the \fBovsdb\-schema\fR command may output
+schemas that other programs refuse to read.
+.
+.SH "SEE ALSO"
+The OVSDB specification.
--- /dev/null
+#! @PYTHON@
+
+import getopt
+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 name, tableJson in tablesJson.iteritems():
+ tables[name] = TableSchema.fromJson(tableJson, "%s table" % name)
+ idlPrefix = mustGetMember(json, 'idlPrefix', [unicode], 'database')
+ idlHeader = mustGetMember(json, 'idlHeader', [unicode], 'database')
+ return DbSchema(name, comment, tables, idlPrefix, idlHeader)
+
+ def toJson(self):
+ d = {"name": self.name,
+ "tables": {}}
+ for name, table in self.tables.iteritems():
+ d["tables"][name] = table.toJson()
+ if self.comment != None:
+ d["comment"] = self.comment
+ return d
+
+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)
+
+ def toJson(self):
+ d = {"columns": {}}
+ for name, column in self.columns.iteritems():
+ d["columns"][name] = column.toJson()
+ if self.comment != None:
+ d["comment"] = self.comment
+ return d
+
+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', [True,False], description)
+ persistent = ephemeral != True
+ return ColumnSchema(comment, type, persistent)
+
+ def toJson(self):
+ d = {"type": self.type.toJson()}
+ if self.persistent == False:
+ d["ephemeral"] = True
+ if self.comment != None:
+ d["comment"] = self.comment
+ return d
+
+class Type:
+ def __init__(self, key, keyRefTable=None, value=None, valueRefTable=None,
+ min=1, max=1):
+ self.key = key
+ self.keyRefTable = keyRefTable
+ self.value = value
+ self.valueRefTable = valueRefTable
+ self.min = min
+ self.max = max
+
+ @staticmethod
+ def fromJson(json, description):
+ if type(json) == unicode:
+ return Type(json)
+ else:
+ key = mustGetMember(json, 'key', [unicode], description)
+ keyRefTable = getMember(json, 'keyRefTable', [unicode], description)
+ value = getMember(json, 'value', [unicode], description)
+ valueRefTable = getMember(json, 'valueRefTable', [unicode], description)
+ min = getMember(json, 'min', [int], description, 1)
+ max = getMember(json, 'max', [int, unicode], description, 1)
+ return Type(key, keyRefTable, value, valueRefTable, min, max)
+
+ def toJson(self):
+ if self.value == None and self.min == 1 and self.max == 1:
+ return self.key
+ else:
+ d = {"key": self.key}
+ if self.value != None:
+ d["value"] = self.value
+ if self.min != 1:
+ d["min"] = self.min
+ if self.max != 1:
+ d["max"] = self.max
+ return d
+
+def parseSchema(filename):
+ file = open(filename, "r")
+ s = ""
+ for line in file:
+ if not line.startswith('//'):
+ s += line
+ return DbSchema.fromJson(json.loads(s))
+
+def cBaseType(prefix, type, refTable=None):
+ if type == 'uuid' and refTable:
+ return "struct %s%s *" % (prefix, refTable.lower())
+ else:
+ return {'integer': 'int64_t ',
+ 'real': 'double ',
+ 'uuid': 'struct uuid ',
+ 'boolean': 'bool ',
+ 'string': 'char *'}[type]
+
+def printCIDLHeader(schema):
+ 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 schema.tables.iteritems():
+ print
+ if table.comment != None:
+ print "/* %s table (%s). */" % (tableName, table.comment)
+ else:
+ print "/* %s table. */" % (tableName)
+ structName = "%s%s" % (prefix, tableName.lower())
+ print "struct %s {" % structName
+ print "\tstruct ovsdb_idl_row header_;"
+ for columnName, column in table.columns.iteritems():
+ print "\n\t/* %s column. */" % columnName
+ type = column.type
+ if type.min == 1 and type.max == 1:
+ singleton = True
+ pointer = ''
+ else:
+ singleton = False
+ pointer = '*'
+ if type.value:
+ print "\tkey_%s%s%s;" % (cBaseType(prefix, type.key, type.keyRefTable), pointer, columnName)
+ print "\tvalue_%s%s%s;" % (cBaseType(prefix, type.value, type.valueRefTable), pointer, columnName)
+ else:
+ print "\t%s%s%s;" % (cBaseType(prefix, type.key, type.keyRefTable), pointer, columnName)
+ if not singleton:
+ print "\tsize_t n_%s;" % columnName
+ 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))''' % {'s': structName, 'S': structName.upper()}
+ print "\nextern struct ovsdb_idl_class %sidl_class;" % 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(schema):
+ prefix = schema.idlPrefix
+ print '''\
+/* Generated automatically -- do not modify! -*- buffer-read-only: t -*- */
+
+#include <config.h>
+#include %s
+#include <limits.h>
+#include "ovsdb-data.h"''' % schema.idlHeader
+
+ # Table indexes.
+ printEnum(["%sTABLE_%s" % (prefix.upper(), tableName.upper()) for tableName in schema.tables] + ["%sN_TABLES" % prefix.upper()])
+ print "\nstatic struct ovsdb_idl_table_class %stable_classes[%sN_TABLES];" % (prefix, prefix.upper())
+
+ for tableName, table in 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)
+
+ # Column indexes.
+ printEnum(["%s_COL_%s" % (structName.upper(), columnName.upper())
+ for columnName in table.columns]
+ + ["%s_N_COLUMNS" % structName.upper()])
+
+ # Parse function.
+ print '''
+static void
+%s_parse(struct ovsdb_idl_row *row_)
+{
+ struct %s *row = (struct %s *) row_;
+ const struct ovsdb_datum *datum;
+ size_t i UNUSED;
+
+ memset(row_ + 1, 0, sizeof *row - sizeof *row_);''' % (structName, structName, structName)
+
+
+ for columnName, column in table.columns.iteritems():
+ type = column.type
+ refKey = type.key == "uuid" and type.keyRefTable
+ refValue = type.value == "uuid" and type.valueRefTable
+ print
+ print " datum = &row_->fields[%s_COL_%s];" % (structName.upper(), columnName.upper())
+ 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:
+ print " if (datum->n >= 1) {"
+ if not refKey:
+ print " %s = datum->keys[0].%s;" % (keyVar, type.key)
+ else:
+ print " %s = (struct %s%s *) ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[0].uuid);" % (keyVar, prefix, type.keyRefTable.lower(), prefix, prefix.upper(), type.keyRefTable.upper())
+
+ if valueVar:
+ if refValue:
+ print " %s = datum->values[0].%s;" % (valueVar, type.value)
+ else:
+ print " %s = (struct %s%s *) ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[0].uuid);" % (valueVar, prefix, type.valueRefTable.lower(), prefix, prefix.upper(), type.valueRefTable.upper())
+ print " }"
+ else:
+ if type.max != 'unlimited':
+ nMax = "MIN(%d, datum->n)" % type.max
+ else:
+ nMax = "datum->n"
+ print " for (i = 0; i < %s; i++) {" % nMax
+ refs = []
+ if refKey:
+ print " struct %s%s *keyRow = (struct %s%s *) ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[i].uuid);" % (prefix, type.keyRefTable.lower(), prefix, type.keyRefTable.lower(), prefix, prefix.upper(), type.keyRefTable.upper())
+ keySrc = "keyRow"
+ refs.append('keyRow')
+ else:
+ keySrc = "datum->keys[i].%s" % type.key
+ if refValue:
+ print " struct %s%s *valueRow = (struct %s%s *) ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[i].uuid);" % (prefix, type.valueRefTable.lower(), prefix, type.valueRefTable.lower(), prefix, prefix.upper(), type.valueRefTable.upper())
+ valueSrc = "valueRow"
+ refs.append('valueRow')
+ elif valueVar:
+ valueSrc = "datum->values[i].%s" % type.value
+ 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[row->n_%s] = %s;" % (indent, valueVar, columnName, valueSrc)
+ print "%srow->n_%s++;" % (indent, columnName)
+ if refs:
+ print " }"
+ print " }"
+ print "}"
+
+ # Unparse function.
+ nArrays = 0
+ for columnName, column in table.columns.iteritems():
+ type = column.type
+ if type.min != 1 or type.max != 1:
+ if not nArrays:
+ print '''
+static void
+%s_unparse(struct ovsdb_idl_row *row_)
+{
+ struct %s *row = (struct %s *) row_;
+''' % (structName, structName, structName)
+ 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
+ nArrays += 1
+ if not nArrays:
+ print '''
+static void
+%s_unparse(struct ovsdb_idl_row *row UNUSED)
+{''' % (structName)
+ print "}"
+
+ # First, next functions.
+ print '''
+const struct %(s)s *%(s)s_first(const struct ovsdb_idl *idl)
+{
+ return (const struct %(s)s *) 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 (const struct %(s)s *) ovsdb_idl_next_row(&row->header_);
+}''' % {'s': structName, 'p': prefix, 'P': prefix.upper(), 'T': tableName.upper()}
+
+ # Table columns.
+ print "\nstatic struct ovsdb_idl_column %s_columns[%s_N_COLUMNS] = {" % (
+ structName, structName.upper())
+ for columnName, column in table.columns.iteritems():
+ type = column.type
+
+ if type.value:
+ valueTypeName = type.value.upper()
+ else:
+ valueTypeName = "VOID"
+ if type.max == "unlimited":
+ max = "UINT_MAX"
+ else:
+ max = type.max
+ print " {\"%s\", {OVSDB_TYPE_%s, OVSDB_TYPE_%s, %d, %s}}," % (
+ columnName, type.key.upper(), valueTypeName,
+ type.min, max)
+ print "};"
+
+ # Table classes.
+ print "\f"
+ print "static struct ovsdb_idl_table_class %stable_classes[%sN_TABLES] = {" % (prefix, prefix.upper())
+ for tableName, table in 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 " %s_parse," % structName
+ print " %s_unparse}," % structName
+ print "};"
+
+ # IDL class.
+ print "\nstruct ovsdb_idl_class %sidl_class = {" % prefix
+ print " %stable_classes, ARRAY_SIZE(%stable_classes)" % (prefix, prefix)
+ 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 printOVSDBSchema(schema):
+ json.dump(schema.toJson(), sys.stdout, sort_keys=True, indent=2)
+
+def usage():
+ print """\
+%(argv0)s: ovsdb schema compiler
+usage: %(argv0)s [OPTIONS] ACTION SCHEMA
+where SCHEMA is the ovsdb schema to read (in JSON format).
+
+One of the following actions must specified:
+ validate validate schema without taking any other action
+ c-idl-header print C header file for IDL
+ c-idl-source print C source file for IDL implementation
+ ovsdb-schema print ovsdb parseable schema
+
+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:], 'hV',
+ ['help',
+ 'version'])
+ except getopt.GetoptError, geo:
+ sys.stderr.write("%s: %s\n" % (argv0, geo.msg))
+ sys.exit(1)
+
+ optKeys = [key for key, value in options]
+ if '-h' in optKeys or '--help' in optKeys:
+ usage()
+ elif '-V' in optKeys or '--version' in optKeys:
+ print "ovsdb-idlc (Open vSwitch) @VERSION@"
+ sys.exit(0)
+
+ if len(args) != 2:
+ sys.stderr.write("%s: exactly two non-option arguments are "
+ "required (use --help for help)\n" % argv0)
+ sys.exit(1)
+
+ action, inputFile = args
+ schema = parseSchema(inputFile)
+ if action == 'validate':
+ pass
+ elif action == 'ovsdb-schema':
+ printOVSDBSchema(schema)
+ elif action == 'c-idl-header':
+ printCIDLHeader(schema)
+ elif action == 'c-idl-source':
+ printCIDLSource(schema)
+ else:
+ sys.stderr.write(
+ "%s: unknown action '%s' (use --help for help)\n" %
+ (argv0, action))
+ sys.exit(1)
+ except Error, e:
+ sys.stderr.write("%s: %s\n" % (argv0, e.msg))
+ sys.exit(1)
+
+# Local variables:
+# mode: python
+# End:
--- /dev/null
+.\" -*- nroff -*-
+.TH ovsdb\-server 1 "November 2009" "Open vSwitch" "Open vSwitch Manual"
+.ds PN ovsdb\-server
+.
+.SH NAME
+ovsdb\-server \- Open vSwitch database server
+.
+.SH SYNOPSIS
+\fBovsdb\-server\fR
+\fIdatabase\fR
+[\fB--connect \fIremote\fR]\&...
+[\fB--listen \fIlocal\fR]\&...
+.so lib/daemon-syn.man
+.so lib/vlog-syn.man
+.so lib/common-syn.man
+.
+.SH DESCRIPTION
+The \fBovsdb\-server\fR program provides RPC interfaces to an Open
+vSwitch database (OVSDB). It can listen for JSON-RPC connections from
+TCP/IP or Unix domain socket clients (with \fB\-\-listen\fR), connect to
+remote JSON-RPC TCP/IP or Unix domain socket clients (with
+\fB\-\-connect\fR).
+.PP
+The name of the OVSDB file must be specified on the command line as
+\fIdatabase\fR, which must already have been created and initialized
+using, for example, \fBovsdb\-tool create\fR.
+.
+.SH OPTIONS
+.
+.IP "\fB\-\-listen=\fIlocal\fR"
+Makes \fBovsdb\-server\fR listen for JSON-RPC connections on
+\fIlocal\fR, which must take one of the following forms:
+.
+.RS
+.IP "\fBptcp:\fIport\fR[\fB:\fIip\fR]"
+Listens for JSON-RPC connections on the given TCP \fIport\fR. By
+default, \fB\*(PN\fR listens for connections to any local IP address,
+but \fIip\fR may be specified to listen only for connections to the
+given \fIip\fR.
+.IP "\fBpunix:\fIfile\fR"
+Listens for JSON-RPC connections on the Unix domain server socket
+named \fIfile\fR.
+.RE
+.
+.IP "\fB\-\-connect=\fIremote\fR"
+Makes \fBovsdb\-server\fR initiate a JSON-RPC connection to
+\fIremote\fR, which must take one of the forms listed below. The
+server will reconnect to \fIremote\fR as necessary.
+.
+.RS
+.IP "\fBtcp:\fIip\fB:\fIport\fR"
+Connects to the given TCP \fIport\fR on \fIip\fR.
+.IP "\fBunix:\fIfile\fR"
+Connects to the Unix domain server socket named \fIfile\fR.
+.RE
+.
+.SS "Daemon Options"
+.so lib/daemon.man
+.SS "Logging Options"
+.so lib/vlog.man
+.SS "Other Options"
+.so lib/common.man
+.SH "RUNTIME MANAGEMENT COMMANDS"
+\fBovs\-appctl\fR(8) can send commands to a running
+\fBovsdb\-server\fR process. The currently supported commands are
+described below.
+.so lib/vlog-unixctl.man
+.SH "SEE ALSO"
+.
+.BR ovsdb\-tool (1).
--- /dev/null
+/* Copyright (c) 2009 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 "command-line.h"
+#include "daemon.h"
+#include "fault.h"
+#include "file.h"
+#include "json.h"
+#include "jsonrpc.h"
+#include "jsonrpc-server.h"
+#include "leak-checker.h"
+#include "list.h"
+#include "ovsdb-error.h"
+#include "poll-loop.h"
+#include "process.h"
+#include "stream.h"
+#include "svec.h"
+#include "timeval.h"
+#include "trigger.h"
+#include "util.h"
+#include "unixctl.h"
+
+#include "vlog.h"
+#define THIS_MODULE VLM_ovsdb_server
+
+static const struct jsonrpc_server_cbs ovsdb_jsonrpc_cbs;
+
+static unixctl_cb_func ovsdb_server_exit;
+
+static void parse_options(int argc, char *argv[], char **file_namep,
+ struct svec *active, struct svec *passive,
+ char **unixctl_pathp);
+static void usage(void) NO_RETURN;
+
+int
+main(int argc, char *argv[])
+{
+ char *unixctl_path = NULL;
+ struct unixctl_server *unixctl;
+ struct ovsdb_jsonrpc_server *jsonrpc;
+ struct svec active, passive;
+ struct ovsdb_error *error;
+ struct ovsdb *db;
+ const char *name;
+ char *file_name;
+ bool do_chdir;
+ bool exiting;
+ int retval;
+ size_t i;
+
+ set_program_name(argv[0]);
+ register_fault_handlers();
+ time_init();
+ vlog_init();
+ signal(SIGPIPE, SIG_IGN);
+ process_init();
+
+ parse_options(argc, argv, &file_name, &active, &passive, &unixctl_path);
+
+ if (get_detach() && is_chdir_enabled()) {
+ /* We need to skip chdir("/") in daemonize() and do it later, because
+ * we need to open the database and possible set up up Unix domain
+ * sockets in the current working directory after we daemonize. We
+ * can't open the database before we daemonize because file locks
+ * aren't inherited by child processes. */
+ do_chdir = true;
+ set_no_chdir();
+ } else {
+ do_chdir = false;
+ }
+ die_if_already_running();
+ daemonize();
+
+ 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);
+ SVEC_FOR_EACH (i, name, &active) {
+ ovsdb_jsonrpc_server_connect(jsonrpc, name);
+ }
+ SVEC_FOR_EACH (i, name, &passive) {
+ retval = ovsdb_jsonrpc_server_listen(jsonrpc, name);
+ if (retval) {
+ ovs_fatal(retval, "failed to listen on %s", name);
+ }
+ }
+ svec_destroy(&active);
+ svec_destroy(&passive);
+
+ retval = unixctl_server_create(unixctl_path, &unixctl);
+ if (retval) {
+ ovs_fatal(retval, "could not listen for control connections");
+ }
+
+ unixctl_command_register("exit", ovsdb_server_exit, &exiting);
+
+ if (do_chdir) {
+ chdir("/");
+ }
+
+ exiting = false;
+ while (!exiting) {
+ 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();
+ }
+
+ return 0;
+}
+
+static void
+ovsdb_server_exit(struct unixctl_conn *conn, const char *args 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 svec *active, struct svec *passive,
+ char **unixctl_pathp)
+{
+ enum {
+ OPT_DUMMY = UCHAR_MAX + 1,
+ OPT_CONNECT,
+ OPT_LISTEN,
+ OPT_UNIXCTL,
+ VLOG_OPTION_ENUMS,
+ LEAK_CHECKER_OPTION_ENUMS
+ };
+ static struct option long_options[] = {
+ {"connect", required_argument, 0, OPT_CONNECT},
+ {"listen", required_argument, 0, OPT_LISTEN},
+ {"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,
+ {0, 0, 0, 0},
+ };
+ char *short_options = long_options_to_short_options(long_options);
+
+ svec_init(active);
+ svec_init(passive);
+ for (;;) {
+ int c;
+
+ c = getopt_long(argc, argv, short_options, long_options, NULL);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case OPT_CONNECT:
+ svec_add(active, optarg);
+ break;
+
+ case OPT_LISTEN:
+ svec_add(passive, optarg);
+ 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
+
+ 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");
+ }
+
+ *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"
+ " --connect=REMOTE make active connection to REMOTE\n"
+ " --listen=LOCAL passively listen on LOCAL\n");
+ stream_usage("JSON-RPC", 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);
+}
--- /dev/null
+.\" -*- nroff -*-
+.de IQ
+. br
+. ns
+. IP "\\$1"
+..
+.\" -*- nroff -*-
+.TH ovsdb\-tool 1 "November 2009" "Open vSwitch" "Open vSwitch Manual"
+.ds PN ovsdb\-tool
+.
+.SH NAME
+ovsdb\-tool \- Open vSwitch database management utility
+.
+.SH SYNOPSIS
+\fBovsdb\-tool \fR[\fIoptions\fR] \fBcreate\fI db schema\fR
+.br
+\fBovsdb\-tool \fR[\fIoptions\fR] \fBquery\fI db transaction\fR
+.br
+\fBovsdb\-tool \fR[\fIoptions\fR] \fBtransact\fI db transaction\fR
+.br
+\fBovsdb\-tool help\fR
+.so lib/vlog-syn.man
+.so lib/common-syn.man
+.
+.SH DESCRIPTION
+The \fBovsdb\-tool\fR program is a command-line tool for managing Open
+vSwitch database (OVSDB) files. It does not interact directly with
+running Open vSwitch database servers (instead, use
+\fBovsdb\-client\fR).
+.
+.SS "Basic Commands"
+.IP "\fBcreate\fI db schema\fR"
+Reads an OVSDB schema from the file named \fIschema\fR and creates a
+new OVSDB database file named \fIdb\fR using that schema. The new
+database is initially empty. This command will not overwrite an
+existing \fIdb\fR.
+.IP
+\fIschema\fR must contain an OVSDB schema in JSON format. Refer to
+the OVSDB specification for details.
+.
+.IP "\fBquery\fI db transaction\fR"
+Opens \fIdb\fR, executes \fItransaction\fR on it, and prints the
+results. The \fItransaction\fR must be a JSON array in the format of
+the \fBparams\fR array for the JSON-RPC \fBtransact\fR method, as
+described in the OVSDB specification.
+.IP
+The \fIdb\fR is opened for read-only access, so this command may
+safely run concurrently with other database activity, including
+\fBovsdb-server\fR and other database writers. The \fItransaction\fR
+may specify database modifications, but these will have no effect on
+\fIdb\fR.
+.
+.IQ "\fBtransact\fI db transaction\fR"
+Opens \fIdb\fR, executes \fItransaction\fR on it, prints the results,
+and commits any changes to \fIdb\fR. The \fItransaction\fR must be a
+JSON array in the format of the \fBparams\fR array for the JSON-RPC
+\fBtransact\fR method, as described in the OVSDB specification.
+.IP
+The \fIdb\fR is opened and locked for read/write access, so this
+command will fail if the database is opened for writing by any other
+process, including \fBovsdb-server\fR(1). Use \fBovsdb\-client\fR(1),
+instead, to write to a database that is served by
+\fBovsdb-server\fR(1).
+.
+.SH OPTIONS
+.SS "Logging Options"
+.so lib/vlog.man
+.SS "Other Options"
+.so lib/common.man
+.SH "SEE ALSO"
+.
+\fBovsdb\-server\fR(1),
+\fBovsdb\-client\fR(1),
+and the OVSDB specification.
--- /dev/null
+/*
+ * Copyright (c) 2009 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
+
+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[] = {
+ {"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 '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",
+ program_name, program_name);
+ 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 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_create(int argc 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);
+
+ /* 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_query(int argc UNUSED, char *argv[])
+{
+ transact(true, argv[1], argv[2]);
+}
+
+static void
+do_transact(int argc UNUSED, char *argv[])
+{
+ transact(false, argv[1], argv[2]);
+}
+
+static void
+do_help(int argc UNUSED, char *argv[] UNUSED)
+{
+ usage();
+}
+
+static const struct command all_commands[] = {
+ { "create", 2, 2, do_create },
+ { "query", 2, 2, do_query },
+ { "transact", 2, 2, do_transact },
+ { "help", 0, INT_MAX, do_help },
+ { NULL, 0, 0, NULL },
+};
--- /dev/null
+/* Copyright (c) 2009 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 "json.h"
+#include "ovsdb-error.h"
+#include "ovsdb-parser.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);
+ if (error) {
+ json_destroy(json);
+ return ovsdb_wrap_error(error,
+ "failed to parse \"%s\" as ovsdb schema",
+ file_name);
+ }
+
+ *schemap = schema;
+ 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);
+ }
+ *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
+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));
+ }
+
+ 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 UNUSED, struct ovsdb_replica *r)
+{
+ list_remove(&r->node);
+ (r->class->destroy)(r);
+}
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef OVSDB_OVSDB_H
+#define OVSDB_OVSDB_H 1
+
+#include "compiler.h"
+#include "hmap.h"
+#include "list.h"
+#include "shash.h"
+
+struct json;
+struct ovsdb_log;
+struct ovsdb_txn;
+struct uuid;
+
+/* Database schema. */
+struct ovsdb_schema {
+ char *name;
+ char *comment;
+ struct shash tables; /* Contains "struct ovsdb_table_schema *"s. */
+};
+
+struct ovsdb_schema *ovsdb_schema_create(const char *name,
+ const char *comment);
+void ovsdb_schema_destroy(struct ovsdb_schema *);
+
+struct ovsdb_error *ovsdb_schema_from_file(const char *file_name,
+ struct ovsdb_schema **)
+ WARN_UNUSED_RESULT;
+struct ovsdb_error *ovsdb_schema_from_json(struct json *,
+ struct ovsdb_schema **)
+ WARN_UNUSED_RESULT;
+struct json *ovsdb_schema_to_json(const struct ovsdb_schema *);
+\f
+/* Database. */
+struct ovsdb {
+ struct ovsdb_schema *schema;
+ struct list replicas; /* Contains "struct ovsdb_replica"s. */
+ struct shash tables; /* Contains "struct ovsdb_table *"s. */
+
+ /* Triggers. */
+ struct list triggers; /* Contains "struct ovsdb_trigger"s. */
+ bool run_triggers;
+};
+
+struct ovsdb *ovsdb_create(struct ovsdb_schema *);
+void ovsdb_destroy(struct ovsdb *);
+
+struct ovsdb_error *ovsdb_from_json(const struct json *, struct ovsdb **)
+ WARN_UNUSED_RESULT;
+struct json *ovsdb_to_json(const struct ovsdb *);
+
+struct ovsdb_table *ovsdb_get_table(const struct ovsdb *, const char *);
+
+struct json *ovsdb_execute(struct ovsdb *, const struct json *params,
+ long long int elapsed_msec,
+ long long int *timeout_msec);
+\f
+/* Database replication. */
+
+struct ovsdb_replica {
+ struct list node; /* Element in "struct ovsdb" replicas list. */
+ const struct ovsdb_replica_class *class;
+};
+
+struct ovsdb_replica_class {
+ struct ovsdb_error *(*commit)(struct ovsdb_replica *,
+ const struct ovsdb_txn *, bool durable);
+ void (*destroy)(struct ovsdb_replica *);
+};
+
+void ovsdb_replica_init(struct ovsdb_replica *,
+ const struct ovsdb_replica_class *);
+
+void ovsdb_add_replica(struct ovsdb *, struct ovsdb_replica *);
+void ovsdb_remove_replica(struct ovsdb *, struct ovsdb_replica *);
+
+#endif /* ovsdb/ovsdb.h */
--- /dev/null
+/* Copyright (c) 2009 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 "query.h"
+
+#include "column.h"
+#include "condition.h"
+#include "row.h"
+#include "table.h"
+
+void
+ovsdb_query(struct ovsdb_table *table, const struct ovsdb_condition *cnd,
+ bool (*output_row)(const struct ovsdb_row *, void *aux), void *aux)
+{
+ if (cnd->n_clauses > 0
+ && cnd->clauses[0].column->index == OVSDB_COL_UUID
+ && cnd->clauses[0].function == OVSDB_F_EQ) {
+ /* Optimize the case where the query has a clause of the form "uuid ==
+ * <some-uuid>", since we have an index on UUID. */
+ const struct ovsdb_row *row;
+
+ row = ovsdb_table_get_row(table, &cnd->clauses[0].arg.keys[0].uuid);
+ if (row && row->table == table && ovsdb_condition_evaluate(row, cnd)) {
+ output_row(row, aux);
+ }
+ } else {
+ /* Linear scan. */
+ const struct ovsdb_row *row, *next;
+
+ HMAP_FOR_EACH_SAFE (row, next, struct ovsdb_row, hmap_node,
+ &table->rows) {
+ if (ovsdb_condition_evaluate(row, cnd) && !output_row(row, aux)) {
+ break;
+ }
+ }
+ }
+}
+
+static bool
+query_row_set_cb(const struct ovsdb_row *row, void *results_)
+{
+ struct ovsdb_row_set *results = results_;
+ ovsdb_row_set_add_row(results, row);
+ return true;
+}
+
+void
+ovsdb_query_row_set(struct ovsdb_table *table,
+ const struct ovsdb_condition *condition,
+ struct ovsdb_row_set *results)
+{
+ ovsdb_query(table, condition, query_row_set_cb, results);
+}
+
+static bool
+query_distinct_cb(const struct ovsdb_row *row, void *hash_)
+{
+ struct ovsdb_row_hash *hash = hash_;
+ ovsdb_row_hash_insert(hash, row);
+ return true;
+}
+
+void
+ovsdb_query_distinct(struct ovsdb_table *table,
+ const struct ovsdb_condition *condition,
+ const struct ovsdb_column_set *columns,
+ struct ovsdb_row_set *results)
+{
+ if (!columns || ovsdb_column_set_contains(columns, OVSDB_COL_UUID)) {
+ /* All the result rows are guaranteed to be distinct anyway. */
+ return ovsdb_query_row_set(table, condition, results);
+ } else {
+ /* Use hash table to drop duplicates. */
+ struct ovsdb_row_hash_node *node;
+ struct ovsdb_row_hash hash;
+
+ ovsdb_row_hash_init(&hash, columns);
+ ovsdb_query(table, condition, query_distinct_cb, &hash);
+ HMAP_FOR_EACH (node, struct ovsdb_row_hash_node, hmap_node,
+ &hash.rows) {
+ ovsdb_row_set_add_row(results, node->row);
+ }
+ ovsdb_row_hash_destroy(&hash, false);
+ }
+}
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef OVSDB_QUERY_H
+#define OVSDB_QUERY_H 1
+
+#include <stdbool.h>
+
+struct ovsdb_column_set;
+struct ovsdb_condition;
+struct ovsdb_row;
+struct ovsdb_row_set;
+struct ovsdb_table;
+struct ovsdb_txn;
+
+void ovsdb_query(struct ovsdb_table *, const struct ovsdb_condition *,
+ bool (*output_row)(const struct ovsdb_row *, void *aux),
+ void *aux);
+void ovsdb_query_row_set(struct ovsdb_table *, const struct ovsdb_condition *,
+ struct ovsdb_row_set *);
+void ovsdb_query_distinct(struct ovsdb_table *, const struct ovsdb_condition *,
+ const struct ovsdb_column_set *,
+ struct ovsdb_row_set *);
+
+#endif /* ovsdb/query.h */
--- /dev/null
+/* Copyright (c) 2009 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 "row.h"
+
+#include <assert.h>
+#include <stddef.h>
+
+#include "json.h"
+#include "ovsdb-error.h"
+#include "shash.h"
+#include "sort.h"
+#include "table.h"
+
+static struct ovsdb_row *
+allocate_row(const struct ovsdb_table *table)
+{
+ size_t n_fields = shash_count(&table->schema->columns);
+ size_t row_size = (offsetof(struct ovsdb_row, fields)
+ + sizeof(struct ovsdb_datum) * n_fields);
+ struct ovsdb_row *row = xmalloc(row_size);
+ row->table = (struct ovsdb_table *) table;
+ row->txn_row = NULL;
+ return row;
+}
+
+struct ovsdb_row *
+ovsdb_row_create(const struct ovsdb_table *table)
+{
+ struct shash_node *node;
+ struct ovsdb_row *row;
+
+ row = allocate_row(table);
+ SHASH_FOR_EACH (node, &table->schema->columns) {
+ const struct ovsdb_column *column = node->data;
+ ovsdb_datum_init_default(&row->fields[column->index], &column->type);
+ }
+ return row;
+}
+
+struct ovsdb_row *
+ovsdb_row_clone(const struct ovsdb_row *old)
+{
+ const struct ovsdb_table *table = old->table;
+ const struct shash_node *node;
+ struct ovsdb_row *new;
+
+ new = allocate_row(table);
+ SHASH_FOR_EACH (node, &table->schema->columns) {
+ const struct ovsdb_column *column = node->data;
+ ovsdb_datum_clone(&new->fields[column->index],
+ &old->fields[column->index],
+ &column->type);
+ }
+ return new;
+}
+
+/* The caller is responsible for ensuring that 'row' has been removed from its
+ * table and that it is not participating in a transaction. */
+void
+ovsdb_row_destroy(struct ovsdb_row *row)
+{
+ if (row) {
+ const struct ovsdb_table *table = row->table;
+ const struct shash_node *node;
+
+ SHASH_FOR_EACH (node, &table->schema->columns) {
+ const struct ovsdb_column *column = node->data;
+ ovsdb_datum_destroy(&row->fields[column->index], &column->type);
+ }
+ free(row);
+ }
+}
+
+uint32_t
+ovsdb_row_hash_columns(const struct ovsdb_row *row,
+ const struct ovsdb_column_set *columns,
+ uint32_t basis)
+{
+ size_t i;
+
+ for (i = 0; i < columns->n_columns; i++) {
+ const struct ovsdb_column *column = columns->columns[i];
+ basis = ovsdb_datum_hash(&row->fields[column->index], &column->type,
+ basis);
+ }
+
+ return basis;
+}
+
+int
+ovsdb_row_compare_columns_3way(const struct ovsdb_row *a,
+ const struct ovsdb_row *b,
+ const struct ovsdb_column_set *columns)
+{
+ size_t i;
+
+ for (i = 0; i < columns->n_columns; i++) {
+ const struct ovsdb_column *column = columns->columns[i];
+ int cmp = ovsdb_datum_compare_3way(&a->fields[column->index],
+ &b->fields[column->index],
+ &column->type);
+ if (cmp) {
+ return cmp;
+ }
+ }
+
+ return 0;
+}
+
+bool
+ovsdb_row_equal_columns(const struct ovsdb_row *a,
+ const struct ovsdb_row *b,
+ const struct ovsdb_column_set *columns)
+{
+ size_t i;
+
+ for (i = 0; i < columns->n_columns; i++) {
+ const struct ovsdb_column *column = columns->columns[i];
+ if (!ovsdb_datum_equals(&a->fields[column->index],
+ &b->fields[column->index],
+ &column->type)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+ovsdb_row_update_columns(struct ovsdb_row *dst,
+ const struct ovsdb_row *src,
+ const struct ovsdb_column_set *columns)
+{
+ size_t i;
+
+ for (i = 0; i < columns->n_columns; i++) {
+ const struct ovsdb_column *column = columns->columns[i];
+ ovsdb_datum_destroy(&dst->fields[column->index], &column->type);
+ ovsdb_datum_clone(&dst->fields[column->index],
+ &src->fields[column->index],
+ &column->type);
+ }
+}
+
+struct ovsdb_error *
+ovsdb_row_from_json(struct ovsdb_row *row, const struct json *json,
+ const struct ovsdb_symbol_table *symtab,
+ struct ovsdb_column_set *included)
+{
+ struct ovsdb_table_schema *schema = row->table->schema;
+ struct ovsdb_error *error;
+ struct shash_node *node;
+
+ if (json->type != JSON_OBJECT) {
+ return ovsdb_syntax_error(json, NULL, "row must be JSON object");
+ }
+
+ SHASH_FOR_EACH (node, json_object(json)) {
+ const char *column_name = node->name;
+ const struct ovsdb_column *column;
+ struct ovsdb_datum datum;
+
+ column = ovsdb_table_schema_get_column(schema, column_name);
+ if (!column) {
+ return ovsdb_syntax_error(json, "unknown column",
+ "No column %s in table %s.",
+ column_name, schema->name);
+ }
+
+ error = ovsdb_datum_from_json(&datum, &column->type, node->data,
+ symtab);
+ if (error) {
+ return error;
+ }
+ ovsdb_datum_swap(&row->fields[column->index], &datum);
+ ovsdb_datum_destroy(&datum, &column->type);
+ if (included) {
+ ovsdb_column_set_add(included, column);
+ }
+ }
+
+ return NULL;
+}
+
+static void
+put_json_column(struct json *object, const struct ovsdb_row *row,
+ const struct ovsdb_column *column)
+{
+ json_object_put(object, column->name,
+ ovsdb_datum_to_json(&row->fields[column->index],
+ &column->type));
+}
+
+struct json *
+ovsdb_row_to_json(const struct ovsdb_row *row,
+ const struct ovsdb_column_set *columns)
+{
+ struct json *json;
+ size_t i;
+
+ json = json_object_create();
+ for (i = 0; i < columns->n_columns; i++) {
+ put_json_column(json, row, columns->columns[i]);
+ }
+ return json;
+}
+\f
+void
+ovsdb_row_set_init(struct ovsdb_row_set *set)
+{
+ set->rows = NULL;
+ set->n_rows = set->allocated_rows = 0;
+}
+
+void
+ovsdb_row_set_destroy(struct ovsdb_row_set *set)
+{
+ free(set->rows);
+}
+
+void
+ovsdb_row_set_add_row(struct ovsdb_row_set *set, const struct ovsdb_row *row)
+{
+ if (set->n_rows >= set->allocated_rows) {
+ set->rows = x2nrealloc(set->rows, &set->allocated_rows,
+ sizeof *set->rows);
+ }
+ set->rows[set->n_rows++] = row;
+}
+
+struct json *
+ovsdb_row_set_to_json(const struct ovsdb_row_set *rows,
+ const struct ovsdb_column_set *columns)
+{
+ struct json **json_rows;
+ size_t i;
+
+ json_rows = xmalloc(rows->n_rows * sizeof *json_rows);
+ for (i = 0; i < rows->n_rows; i++) {
+ json_rows[i] = ovsdb_row_to_json(rows->rows[i], columns);
+ }
+ return json_array_create(json_rows, rows->n_rows);
+}
+
+struct ovsdb_row_set_sort_cbdata {
+ struct ovsdb_row_set *set;
+ const struct ovsdb_column_set *columns;
+};
+
+static int
+ovsdb_row_set_sort_compare_cb(size_t a, size_t b, void *cbdata_)
+{
+ struct ovsdb_row_set_sort_cbdata *cbdata = cbdata_;
+ return ovsdb_row_compare_columns_3way(cbdata->set->rows[a],
+ cbdata->set->rows[b],
+ cbdata->columns);
+}
+
+static void
+ovsdb_row_set_sort_swap_cb(size_t a, size_t b, void *cbdata_)
+{
+ struct ovsdb_row_set_sort_cbdata *cbdata = cbdata_;
+ const struct ovsdb_row *tmp = cbdata->set->rows[a];
+ cbdata->set->rows[a] = cbdata->set->rows[b];
+ cbdata->set->rows[b] = tmp;
+}
+
+void
+ovsdb_row_set_sort(struct ovsdb_row_set *set,
+ const struct ovsdb_column_set *columns)
+{
+ if (columns && columns->n_columns && set->n_rows > 1) {
+ struct ovsdb_row_set_sort_cbdata cbdata;
+ cbdata.set = set;
+ cbdata.columns = columns;
+ sort(set->n_rows,
+ ovsdb_row_set_sort_compare_cb,
+ ovsdb_row_set_sort_swap_cb,
+ &cbdata);
+ }
+}
+\f
+void
+ovsdb_row_hash_init(struct ovsdb_row_hash *rh,
+ const struct ovsdb_column_set *columns)
+{
+ hmap_init(&rh->rows);
+ ovsdb_column_set_clone(&rh->columns, columns);
+}
+
+void
+ovsdb_row_hash_destroy(struct ovsdb_row_hash *rh, bool destroy_rows)
+{
+ struct ovsdb_row_hash_node *node, *next;
+
+ HMAP_FOR_EACH_SAFE (node, next, struct ovsdb_row_hash_node, hmap_node,
+ &rh->rows) {
+ hmap_remove(&rh->rows, &node->hmap_node);
+ if (destroy_rows) {
+ ovsdb_row_destroy((struct ovsdb_row *) node->row);
+ }
+ free(node);
+ }
+ hmap_destroy(&rh->rows);
+ ovsdb_column_set_destroy(&rh->columns);
+}
+
+size_t
+ovsdb_row_hash_count(const struct ovsdb_row_hash *rh)
+{
+ return hmap_count(&rh->rows);
+}
+
+bool
+ovsdb_row_hash_contains(const struct ovsdb_row_hash *rh,
+ const struct ovsdb_row *row)
+{
+ size_t hash = ovsdb_row_hash_columns(row, &rh->columns, 0);
+ return ovsdb_row_hash_contains__(rh, row, hash);
+}
+
+/* Returns true if every row in 'b' has an equal row in 'a'. */
+bool
+ovsdb_row_hash_contains_all(const struct ovsdb_row_hash *a,
+ const struct ovsdb_row_hash *b)
+{
+ struct ovsdb_row_hash_node *node;
+
+ assert(ovsdb_column_set_equals(&a->columns, &b->columns));
+ HMAP_FOR_EACH (node, struct ovsdb_row_hash_node, hmap_node, &b->rows) {
+ if (!ovsdb_row_hash_contains__(a, node->row, node->hmap_node.hash)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+ovsdb_row_hash_insert(struct ovsdb_row_hash *rh, const struct ovsdb_row *row)
+{
+ size_t hash = ovsdb_row_hash_columns(row, &rh->columns, 0);
+ return ovsdb_row_hash_insert__(rh, row, hash);
+}
+
+bool
+ovsdb_row_hash_contains__(const struct ovsdb_row_hash *rh,
+ const struct ovsdb_row *row, size_t hash)
+{
+ struct ovsdb_row_hash_node *node;
+ HMAP_FOR_EACH_WITH_HASH (node, struct ovsdb_row_hash_node, hmap_node,
+ hash, &rh->rows) {
+ if (ovsdb_row_equal_columns(row, node->row, &rh->columns)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+ovsdb_row_hash_insert__(struct ovsdb_row_hash *rh, const struct ovsdb_row *row,
+ size_t hash)
+{
+ if (!ovsdb_row_hash_contains__(rh, row, hash)) {
+ struct ovsdb_row_hash_node *node = xmalloc(sizeof *node);
+ node->row = row;
+ hmap_insert(&rh->rows, &node->hmap_node, hash);
+ return true;
+ } else {
+ return false;
+ }
+}
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef OVSDB_ROW_H
+#define OVSDB_ROW_H 1
+
+#include <stddef.h>
+#include <stdint.h>
+#include "column.h"
+#include "hmap.h"
+#include "ovsdb-data.h"
+
+struct ovsdb_column_set;
+
+/* A row in a database table. */
+struct ovsdb_row {
+ struct ovsdb_table *table; /* Table to which this belongs. */
+ struct hmap_node hmap_node; /* Element in ovsdb_table's 'rows' hmap. */
+ struct ovsdb_txn_row *txn_row; /* Transaction that row is in, if any. */
+ struct ovsdb_datum fields[];
+};
+
+struct ovsdb_row *ovsdb_row_create(const struct ovsdb_table *);
+struct ovsdb_row *ovsdb_row_clone(const struct ovsdb_row *);
+void ovsdb_row_destroy(struct ovsdb_row *);
+
+uint32_t ovsdb_row_hash_columns(const struct ovsdb_row *,
+ const struct ovsdb_column_set *,
+ uint32_t basis);
+bool ovsdb_row_equal_columns(const struct ovsdb_row *,
+ const struct ovsdb_row *,
+ const struct ovsdb_column_set *);
+int ovsdb_row_compare_columns_3way(const struct ovsdb_row *,
+ const struct ovsdb_row *,
+ const struct ovsdb_column_set *);
+void ovsdb_row_update_columns(struct ovsdb_row *, const struct ovsdb_row *,
+ const struct ovsdb_column_set *);
+
+struct ovsdb_error *ovsdb_row_from_json(struct ovsdb_row *,
+ const struct json *,
+ const struct ovsdb_symbol_table *,
+ struct ovsdb_column_set *included)
+ WARN_UNUSED_RESULT;
+struct json *ovsdb_row_to_json(const struct ovsdb_row *,
+ const struct ovsdb_column_set *include);
+
+static inline const struct uuid *
+ovsdb_row_get_uuid(const struct ovsdb_row *row)
+{
+ return &row->fields[OVSDB_COL_UUID].keys[0].uuid;
+}
+
+static inline struct uuid *
+ovsdb_row_get_uuid_rw(struct ovsdb_row *row)
+{
+ return &row->fields[OVSDB_COL_UUID].keys[0].uuid;
+}
+
+static inline const struct uuid *
+ovsdb_row_get_version(const struct ovsdb_row *row)
+{
+ return &row->fields[OVSDB_COL_VERSION].keys[0].uuid;
+}
+
+static inline struct uuid *
+ovsdb_row_get_version_rw(struct ovsdb_row *row)
+{
+ return &row->fields[OVSDB_COL_VERSION].keys[0].uuid;
+}
+
+static inline uint32_t
+ovsdb_row_hash(const struct ovsdb_row *row)
+{
+ return uuid_hash(ovsdb_row_get_uuid(row));
+}
+\f
+/* An unordered collection of rows. */
+struct ovsdb_row_set {
+ const struct ovsdb_row **rows;
+ size_t n_rows, allocated_rows;
+};
+
+#define OVSDB_ROW_SET_INITIALIZER { NULL, 0, 0 }
+
+void ovsdb_row_set_init(struct ovsdb_row_set *);
+void ovsdb_row_set_destroy(struct ovsdb_row_set *);
+void ovsdb_row_set_add_row(struct ovsdb_row_set *, const struct ovsdb_row *);
+
+struct json *ovsdb_row_set_to_json(const struct ovsdb_row_set *,
+ const struct ovsdb_column_set *);
+
+void ovsdb_row_set_sort(struct ovsdb_row_set *,
+ const struct ovsdb_column_set *);
+\f
+/* A hash table of rows. A specified set of columns is used for hashing and
+ * comparing rows.
+ *
+ * The row hash doesn't necessarily own its rows. They may be owned by, for
+ * example, an ovsdb_table. */
+struct ovsdb_row_hash {
+ struct hmap rows;
+ struct ovsdb_column_set columns;
+};
+
+#define OVSDB_ROW_HASH_INITIALIZER(RH) \
+ { HMAP_INITIALIZER(&(RH).rows), OVSDB_COLUMN_SET_INITIALIZER }
+
+struct ovsdb_row_hash_node {
+ struct hmap_node hmap_node;
+ const struct ovsdb_row *row;
+};
+
+void ovsdb_row_hash_init(struct ovsdb_row_hash *,
+ const struct ovsdb_column_set *);
+void ovsdb_row_hash_destroy(struct ovsdb_row_hash *, bool destroy_rows);
+size_t ovsdb_row_hash_count(const struct ovsdb_row_hash *);
+bool ovsdb_row_hash_contains(const struct ovsdb_row_hash *,
+ const struct ovsdb_row *);
+bool ovsdb_row_hash_contains_all(const struct ovsdb_row_hash *,
+ const struct ovsdb_row_hash *);
+bool ovsdb_row_hash_insert(struct ovsdb_row_hash *, const struct ovsdb_row *);
+bool ovsdb_row_hash_contains__(const struct ovsdb_row_hash *,
+ const struct ovsdb_row *, size_t hash);
+bool ovsdb_row_hash_insert__(struct ovsdb_row_hash *,
+ const struct ovsdb_row *, size_t hash);
+
+#endif /* ovsdb/row.h */
--- /dev/null
+r"""JSON (JavaScript Object Notation) <http://json.org> is a subset of
+JavaScript syntax (ECMA-262 3rd edition) used as a lightweight data
+interchange format.
+
+:mod:`simplejson` exposes an API familiar to users of the standard library
+:mod:`marshal` and :mod:`pickle` modules. It is the externally maintained
+version of the :mod:`json` library contained in Python 2.6, but maintains
+compatibility with Python 2.4 and Python 2.5 and (currently) has
+significant performance advantages, even without using the optional C
+extension for speedups.
+
+Encoding basic Python object hierarchies::
+
+ >>> import simplejson as json
+ >>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
+ '["foo", {"bar": ["baz", null, 1.0, 2]}]'
+ >>> print json.dumps("\"foo\bar")
+ "\"foo\bar"
+ >>> print json.dumps(u'\u1234')
+ "\u1234"
+ >>> print json.dumps('\\')
+ "\\"
+ >>> print json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True)
+ {"a": 0, "b": 0, "c": 0}
+ >>> from StringIO import StringIO
+ >>> io = StringIO()
+ >>> json.dump(['streaming API'], io)
+ >>> io.getvalue()
+ '["streaming API"]'
+
+Compact encoding::
+
+ >>> import simplejson as json
+ >>> json.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
+ '[1,2,3,{"4":5,"6":7}]'
+
+Pretty printing::
+
+ >>> import simplejson as json
+ >>> s = json.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4)
+ >>> print '\n'.join([l.rstrip() for l in s.splitlines()])
+ {
+ "4": 5,
+ "6": 7
+ }
+
+Decoding JSON::
+
+ >>> import simplejson as json
+ >>> obj = [u'foo', {u'bar': [u'baz', None, 1.0, 2]}]
+ >>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]') == obj
+ True
+ >>> json.loads('"\\"foo\\bar"') == u'"foo\x08ar'
+ True
+ >>> from StringIO import StringIO
+ >>> io = StringIO('["streaming API"]')
+ >>> json.load(io)[0] == 'streaming API'
+ True
+
+Specializing JSON object decoding::
+
+ >>> import simplejson as json
+ >>> def as_complex(dct):
+ ... if '__complex__' in dct:
+ ... return complex(dct['real'], dct['imag'])
+ ... return dct
+ ...
+ >>> json.loads('{"__complex__": true, "real": 1, "imag": 2}',
+ ... object_hook=as_complex)
+ (1+2j)
+ >>> import decimal
+ >>> json.loads('1.1', parse_float=decimal.Decimal) == decimal.Decimal('1.1')
+ True
+
+Specializing JSON object encoding::
+
+ >>> import simplejson as json
+ >>> def encode_complex(obj):
+ ... if isinstance(obj, complex):
+ ... return [obj.real, obj.imag]
+ ... raise TypeError(repr(o) + " is not JSON serializable")
+ ...
+ >>> json.dumps(2 + 1j, default=encode_complex)
+ '[2.0, 1.0]'
+ >>> json.JSONEncoder(default=encode_complex).encode(2 + 1j)
+ '[2.0, 1.0]'
+ >>> ''.join(json.JSONEncoder(default=encode_complex).iterencode(2 + 1j))
+ '[2.0, 1.0]'
+
+
+Using simplejson.tool from the shell to validate and pretty-print::
+
+ $ echo '{"json":"obj"}' | python -m simplejson.tool
+ {
+ "json": "obj"
+ }
+ $ echo '{ 1.2:3.4}' | python -m simplejson.tool
+ Expecting property name: line 1 column 2 (char 2)
+"""
+__version__ = '2.0.9'
+__all__ = [
+ 'dump', 'dumps', 'load', 'loads',
+ 'JSONDecoder', 'JSONEncoder',
+]
+
+__author__ = 'Bob Ippolito <bob@redivi.com>'
+
+from decoder import JSONDecoder
+from encoder import JSONEncoder
+
+_default_encoder = JSONEncoder(
+ skipkeys=False,
+ ensure_ascii=True,
+ check_circular=True,
+ allow_nan=True,
+ indent=None,
+ separators=None,
+ encoding='utf-8',
+ default=None,
+)
+
+def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
+ allow_nan=True, cls=None, indent=None, separators=None,
+ encoding='utf-8', default=None, **kw):
+ """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
+ ``.write()``-supporting file-like object).
+
+ If ``skipkeys`` is true then ``dict`` keys that are not basic types
+ (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
+ will be skipped instead of raising a ``TypeError``.
+
+ If ``ensure_ascii`` is false, then the some chunks written to ``fp``
+ may be ``unicode`` instances, subject to normal Python ``str`` to
+ ``unicode`` coercion rules. Unless ``fp.write()`` explicitly
+ understands ``unicode`` (as in ``codecs.getwriter()``) this is likely
+ to cause an error.
+
+ If ``check_circular`` is false, then the circular reference check
+ for container types will be skipped and a circular reference will
+ result in an ``OverflowError`` (or worse).
+
+ If ``allow_nan`` is false, then it will be a ``ValueError`` to
+ serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``)
+ in strict compliance of the JSON specification, instead of using the
+ JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
+
+ If ``indent`` is a non-negative integer, then JSON array elements and object
+ members will be pretty-printed with that indent level. An indent level
+ of 0 will only insert newlines. ``None`` is the most compact representation.
+
+ If ``separators`` is an ``(item_separator, dict_separator)`` tuple
+ then it will be used instead of the default ``(', ', ': ')`` separators.
+ ``(',', ':')`` is the most compact JSON representation.
+
+ ``encoding`` is the character encoding for str instances, default is UTF-8.
+
+ ``default(obj)`` is a function that should return a serializable version
+ of obj or raise TypeError. The default simply raises TypeError.
+
+ To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
+ ``.default()`` method to serialize additional types), specify it with
+ the ``cls`` kwarg.
+
+ """
+ # cached encoder
+ if (not skipkeys and ensure_ascii and
+ check_circular and allow_nan and
+ cls is None and indent is None and separators is None and
+ encoding == 'utf-8' and default is None and not kw):
+ iterable = _default_encoder.iterencode(obj)
+ else:
+ if cls is None:
+ cls = JSONEncoder
+ iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
+ check_circular=check_circular, allow_nan=allow_nan, indent=indent,
+ separators=separators, encoding=encoding,
+ default=default, **kw).iterencode(obj)
+ # could accelerate with writelines in some versions of Python, at
+ # a debuggability cost
+ for chunk in iterable:
+ fp.write(chunk)
+
+
+def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
+ allow_nan=True, cls=None, indent=None, separators=None,
+ encoding='utf-8', default=None, **kw):
+ """Serialize ``obj`` to a JSON formatted ``str``.
+
+ If ``skipkeys`` is false then ``dict`` keys that are not basic types
+ (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
+ will be skipped instead of raising a ``TypeError``.
+
+ If ``ensure_ascii`` is false, then the return value will be a
+ ``unicode`` instance subject to normal Python ``str`` to ``unicode``
+ coercion rules instead of being escaped to an ASCII ``str``.
+
+ If ``check_circular`` is false, then the circular reference check
+ for container types will be skipped and a circular reference will
+ result in an ``OverflowError`` (or worse).
+
+ If ``allow_nan`` is false, then it will be a ``ValueError`` to
+ serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in
+ strict compliance of the JSON specification, instead of using the
+ JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
+
+ If ``indent`` is a non-negative integer, then JSON array elements and
+ object members will be pretty-printed with that indent level. An indent
+ level of 0 will only insert newlines. ``None`` is the most compact
+ representation.
+
+ If ``separators`` is an ``(item_separator, dict_separator)`` tuple
+ then it will be used instead of the default ``(', ', ': ')`` separators.
+ ``(',', ':')`` is the most compact JSON representation.
+
+ ``encoding`` is the character encoding for str instances, default is UTF-8.
+
+ ``default(obj)`` is a function that should return a serializable version
+ of obj or raise TypeError. The default simply raises TypeError.
+
+ To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
+ ``.default()`` method to serialize additional types), specify it with
+ the ``cls`` kwarg.
+
+ """
+ # cached encoder
+ if (not skipkeys and ensure_ascii and
+ check_circular and allow_nan and
+ cls is None and indent is None and separators is None and
+ encoding == 'utf-8' and default is None and not kw):
+ return _default_encoder.encode(obj)
+ if cls is None:
+ cls = JSONEncoder
+ return cls(
+ skipkeys=skipkeys, ensure_ascii=ensure_ascii,
+ check_circular=check_circular, allow_nan=allow_nan, indent=indent,
+ separators=separators, encoding=encoding, default=default,
+ **kw).encode(obj)
+
+
+_default_decoder = JSONDecoder(encoding=None, object_hook=None)
+
+
+def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None,
+ parse_int=None, parse_constant=None, **kw):
+ """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
+ a JSON document) to a Python object.
+
+ If the contents of ``fp`` is encoded with an ASCII based encoding other
+ than utf-8 (e.g. latin-1), then an appropriate ``encoding`` name must
+ be specified. Encodings that are not ASCII based (such as UCS-2) are
+ not allowed, and should be wrapped with
+ ``codecs.getreader(fp)(encoding)``, or simply decoded to a ``unicode``
+ object and passed to ``loads()``
+
+ ``object_hook`` is an optional function that will be called with the
+ result of any object literal decode (a ``dict``). The return value of
+ ``object_hook`` will be used instead of the ``dict``. This feature
+ can be used to implement custom decoders (e.g. JSON-RPC class hinting).
+
+ To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
+ kwarg.
+
+ """
+ return loads(fp.read(),
+ encoding=encoding, cls=cls, object_hook=object_hook,
+ parse_float=parse_float, parse_int=parse_int,
+ parse_constant=parse_constant, **kw)
+
+
+def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None,
+ parse_int=None, parse_constant=None, **kw):
+ """Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON
+ document) to a Python object.
+
+ If ``s`` is a ``str`` instance and is encoded with an ASCII based encoding
+ other than utf-8 (e.g. latin-1) then an appropriate ``encoding`` name
+ must be specified. Encodings that are not ASCII based (such as UCS-2)
+ are not allowed and should be decoded to ``unicode`` first.
+
+ ``object_hook`` is an optional function that will be called with the
+ result of any object literal decode (a ``dict``). The return value of
+ ``object_hook`` will be used instead of the ``dict``. This feature
+ can be used to implement custom decoders (e.g. JSON-RPC class hinting).
+
+ ``parse_float``, if specified, will be called with the string
+ of every JSON float to be decoded. By default this is equivalent to
+ float(num_str). This can be used to use another datatype or parser
+ for JSON floats (e.g. decimal.Decimal).
+
+ ``parse_int``, if specified, will be called with the string
+ of every JSON int to be decoded. By default this is equivalent to
+ int(num_str). This can be used to use another datatype or parser
+ for JSON integers (e.g. float).
+
+ ``parse_constant``, if specified, will be called with one of the
+ following strings: -Infinity, Infinity, NaN, null, true, false.
+ This can be used to raise an exception if invalid JSON numbers
+ are encountered.
+
+ To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
+ kwarg.
+
+ """
+ if (cls is None and encoding is None and object_hook is None and
+ parse_int is None and parse_float is None and
+ parse_constant is None and not kw):
+ return _default_decoder.decode(s)
+ if cls is None:
+ cls = JSONDecoder
+ if object_hook is not None:
+ kw['object_hook'] = object_hook
+ if parse_float is not None:
+ kw['parse_float'] = parse_float
+ if parse_int is not None:
+ kw['parse_int'] = parse_int
+ if parse_constant is not None:
+ kw['parse_constant'] = parse_constant
+ return cls(encoding=encoding, **kw).decode(s)
--- /dev/null
+#include "Python.h"
+#include "structmember.h"
+#if PY_VERSION_HEX < 0x02060000 && !defined(Py_TYPE)
+#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
+#endif
+#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
+typedef int Py_ssize_t;
+#define PY_SSIZE_T_MAX INT_MAX
+#define PY_SSIZE_T_MIN INT_MIN
+#define PyInt_FromSsize_t PyInt_FromLong
+#define PyInt_AsSsize_t PyInt_AsLong
+#endif
+#ifndef Py_IS_FINITE
+#define Py_IS_FINITE(X) (!Py_IS_INFINITY(X) && !Py_IS_NAN(X))
+#endif
+
+#ifdef __GNUC__
+#define UNUSED __attribute__((__unused__))
+#else
+#define UNUSED
+#endif
+
+#define DEFAULT_ENCODING "utf-8"
+
+#define PyScanner_Check(op) PyObject_TypeCheck(op, &PyScannerType)
+#define PyScanner_CheckExact(op) (Py_TYPE(op) == &PyScannerType)
+#define PyEncoder_Check(op) PyObject_TypeCheck(op, &PyEncoderType)
+#define PyEncoder_CheckExact(op) (Py_TYPE(op) == &PyEncoderType)
+
+static PyTypeObject PyScannerType;
+static PyTypeObject PyEncoderType;
+
+typedef struct _PyScannerObject {
+ PyObject_HEAD
+ PyObject *encoding;
+ PyObject *strict;
+ PyObject *object_hook;
+ PyObject *parse_float;
+ PyObject *parse_int;
+ PyObject *parse_constant;
+} PyScannerObject;
+
+static PyMemberDef scanner_members[] = {
+ {"encoding", T_OBJECT, offsetof(PyScannerObject, encoding), READONLY, "encoding"},
+ {"strict", T_OBJECT, offsetof(PyScannerObject, strict), READONLY, "strict"},
+ {"object_hook", T_OBJECT, offsetof(PyScannerObject, object_hook), READONLY, "object_hook"},
+ {"parse_float", T_OBJECT, offsetof(PyScannerObject, parse_float), READONLY, "parse_float"},
+ {"parse_int", T_OBJECT, offsetof(PyScannerObject, parse_int), READONLY, "parse_int"},
+ {"parse_constant", T_OBJECT, offsetof(PyScannerObject, parse_constant), READONLY, "parse_constant"},
+ {NULL}
+};
+
+typedef struct _PyEncoderObject {
+ PyObject_HEAD
+ PyObject *markers;
+ PyObject *defaultfn;
+ PyObject *encoder;
+ PyObject *indent;
+ PyObject *key_separator;
+ PyObject *item_separator;
+ PyObject *sort_keys;
+ PyObject *skipkeys;
+ int fast_encode;
+ int allow_nan;
+} PyEncoderObject;
+
+static PyMemberDef encoder_members[] = {
+ {"markers", T_OBJECT, offsetof(PyEncoderObject, markers), READONLY, "markers"},
+ {"default", T_OBJECT, offsetof(PyEncoderObject, defaultfn), READONLY, "default"},
+ {"encoder", T_OBJECT, offsetof(PyEncoderObject, encoder), READONLY, "encoder"},
+ {"indent", T_OBJECT, offsetof(PyEncoderObject, indent), READONLY, "indent"},
+ {"key_separator", T_OBJECT, offsetof(PyEncoderObject, key_separator), READONLY, "key_separator"},
+ {"item_separator", T_OBJECT, offsetof(PyEncoderObject, item_separator), READONLY, "item_separator"},
+ {"sort_keys", T_OBJECT, offsetof(PyEncoderObject, sort_keys), READONLY, "sort_keys"},
+ {"skipkeys", T_OBJECT, offsetof(PyEncoderObject, skipkeys), READONLY, "skipkeys"},
+ {NULL}
+};
+
+static Py_ssize_t
+ascii_escape_char(Py_UNICODE c, char *output, Py_ssize_t chars);
+static PyObject *
+ascii_escape_unicode(PyObject *pystr);
+static PyObject *
+ascii_escape_str(PyObject *pystr);
+static PyObject *
+py_encode_basestring_ascii(PyObject* self UNUSED, PyObject *pystr);
+void init_speedups(void);
+static PyObject *
+scan_once_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr);
+static PyObject *
+scan_once_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr);
+static PyObject *
+_build_rval_index_tuple(PyObject *rval, Py_ssize_t idx);
+static PyObject *
+scanner_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
+static int
+scanner_init(PyObject *self, PyObject *args, PyObject *kwds);
+static void
+scanner_dealloc(PyObject *self);
+static int
+scanner_clear(PyObject *self);
+static PyObject *
+encoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
+static int
+encoder_init(PyObject *self, PyObject *args, PyObject *kwds);
+static void
+encoder_dealloc(PyObject *self);
+static int
+encoder_clear(PyObject *self);
+static int
+encoder_listencode_list(PyEncoderObject *s, PyObject *rval, PyObject *seq, Py_ssize_t indent_level);
+static int
+encoder_listencode_obj(PyEncoderObject *s, PyObject *rval, PyObject *obj, Py_ssize_t indent_level);
+static int
+encoder_listencode_dict(PyEncoderObject *s, PyObject *rval, PyObject *dct, Py_ssize_t indent_level);
+static PyObject *
+_encoded_const(PyObject *const);
+static void
+raise_errmsg(char *msg, PyObject *s, Py_ssize_t end);
+static PyObject *
+encoder_encode_string(PyEncoderObject *s, PyObject *obj);
+static int
+_convertPyInt_AsSsize_t(PyObject *o, Py_ssize_t *size_ptr);
+static PyObject *
+_convertPyInt_FromSsize_t(Py_ssize_t *size_ptr);
+static PyObject *
+encoder_encode_float(PyEncoderObject *s, PyObject *obj);
+
+#define S_CHAR(c) (c >= ' ' && c <= '~' && c != '\\' && c != '"')
+#define IS_WHITESPACE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\n') || ((c) == '\r'))
+
+#define MIN_EXPANSION 6
+#ifdef Py_UNICODE_WIDE
+#define MAX_EXPANSION (2 * MIN_EXPANSION)
+#else
+#define MAX_EXPANSION MIN_EXPANSION
+#endif
+
+static int
+_convertPyInt_AsSsize_t(PyObject *o, Py_ssize_t *size_ptr)
+{
+ /* PyObject to Py_ssize_t converter */
+ *size_ptr = PyInt_AsSsize_t(o);
+ if (*size_ptr == -1 && PyErr_Occurred());
+ return 1;
+ return 0;
+}
+
+static PyObject *
+_convertPyInt_FromSsize_t(Py_ssize_t *size_ptr)
+{
+ /* Py_ssize_t to PyObject converter */
+ return PyInt_FromSsize_t(*size_ptr);
+}
+
+static Py_ssize_t
+ascii_escape_char(Py_UNICODE c, char *output, Py_ssize_t chars)
+{
+ /* Escape unicode code point c to ASCII escape sequences
+ in char *output. output must have at least 12 bytes unused to
+ accommodate an escaped surrogate pair "\uXXXX\uXXXX" */
+ output[chars++] = '\\';
+ switch (c) {
+ case '\\': output[chars++] = (char)c; break;
+ case '"': output[chars++] = (char)c; break;
+ case '\b': output[chars++] = 'b'; break;
+ case '\f': output[chars++] = 'f'; break;
+ case '\n': output[chars++] = 'n'; break;
+ case '\r': output[chars++] = 'r'; break;
+ case '\t': output[chars++] = 't'; break;
+ default:
+#ifdef Py_UNICODE_WIDE
+ if (c >= 0x10000) {
+ /* UTF-16 surrogate pair */
+ Py_UNICODE v = c - 0x10000;
+ c = 0xd800 | ((v >> 10) & 0x3ff);
+ output[chars++] = 'u';
+ output[chars++] = "0123456789abcdef"[(c >> 12) & 0xf];
+ output[chars++] = "0123456789abcdef"[(c >> 8) & 0xf];
+ output[chars++] = "0123456789abcdef"[(c >> 4) & 0xf];
+ output[chars++] = "0123456789abcdef"[(c ) & 0xf];
+ c = 0xdc00 | (v & 0x3ff);
+ output[chars++] = '\\';
+ }
+#endif
+ output[chars++] = 'u';
+ output[chars++] = "0123456789abcdef"[(c >> 12) & 0xf];
+ output[chars++] = "0123456789abcdef"[(c >> 8) & 0xf];
+ output[chars++] = "0123456789abcdef"[(c >> 4) & 0xf];
+ output[chars++] = "0123456789abcdef"[(c ) & 0xf];
+ }
+ return chars;
+}
+
+static PyObject *
+ascii_escape_unicode(PyObject *pystr)
+{
+ /* Take a PyUnicode pystr and return a new ASCII-only escaped PyString */
+ Py_ssize_t i;
+ Py_ssize_t input_chars;
+ Py_ssize_t output_size;
+ Py_ssize_t max_output_size;
+ Py_ssize_t chars;
+ PyObject *rval;
+ char *output;
+ Py_UNICODE *input_unicode;
+
+ input_chars = PyUnicode_GET_SIZE(pystr);
+ input_unicode = PyUnicode_AS_UNICODE(pystr);
+
+ /* One char input can be up to 6 chars output, estimate 4 of these */
+ output_size = 2 + (MIN_EXPANSION * 4) + input_chars;
+ max_output_size = 2 + (input_chars * MAX_EXPANSION);
+ rval = PyString_FromStringAndSize(NULL, output_size);
+ if (rval == NULL) {
+ return NULL;
+ }
+ output = PyString_AS_STRING(rval);
+ chars = 0;
+ output[chars++] = '"';
+ for (i = 0; i < input_chars; i++) {
+ Py_UNICODE c = input_unicode[i];
+ if (S_CHAR(c)) {
+ output[chars++] = (char)c;
+ }
+ else {
+ chars = ascii_escape_char(c, output, chars);
+ }
+ if (output_size - chars < (1 + MAX_EXPANSION)) {
+ /* There's more than four, so let's resize by a lot */
+ Py_ssize_t new_output_size = output_size * 2;
+ /* This is an upper bound */
+ if (new_output_size > max_output_size) {
+ new_output_size = max_output_size;
+ }
+ /* Make sure that the output size changed before resizing */
+ if (new_output_size != output_size) {
+ output_size = new_output_size;
+ if (_PyString_Resize(&rval, output_size) == -1) {
+ return NULL;
+ }
+ output = PyString_AS_STRING(rval);
+ }
+ }
+ }
+ output[chars++] = '"';
+ if (_PyString_Resize(&rval, chars) == -1) {
+ return NULL;
+ }
+ return rval;
+}
+
+static PyObject *
+ascii_escape_str(PyObject *pystr)
+{
+ /* Take a PyString pystr and return a new ASCII-only escaped PyString */
+ Py_ssize_t i;
+ Py_ssize_t input_chars;
+ Py_ssize_t output_size;
+ Py_ssize_t chars;
+ PyObject *rval;
+ char *output;
+ char *input_str;
+
+ input_chars = PyString_GET_SIZE(pystr);
+ input_str = PyString_AS_STRING(pystr);
+
+ /* Fast path for a string that's already ASCII */
+ for (i = 0; i < input_chars; i++) {
+ Py_UNICODE c = (Py_UNICODE)(unsigned char)input_str[i];
+ if (!S_CHAR(c)) {
+ /* If we have to escape something, scan the string for unicode */
+ Py_ssize_t j;
+ for (j = i; j < input_chars; j++) {
+ c = (Py_UNICODE)(unsigned char)input_str[j];
+ if (c > 0x7f) {
+ /* We hit a non-ASCII character, bail to unicode mode */
+ PyObject *uni;
+ uni = PyUnicode_DecodeUTF8(input_str, input_chars, "strict");
+ if (uni == NULL) {
+ return NULL;
+ }
+ rval = ascii_escape_unicode(uni);
+ Py_DECREF(uni);
+ return rval;
+ }
+ }
+ break;
+ }
+ }
+
+ if (i == input_chars) {
+ /* Input is already ASCII */
+ output_size = 2 + input_chars;
+ }
+ else {
+ /* One char input can be up to 6 chars output, estimate 4 of these */
+ output_size = 2 + (MIN_EXPANSION * 4) + input_chars;
+ }
+ rval = PyString_FromStringAndSize(NULL, output_size);
+ if (rval == NULL) {
+ return NULL;
+ }
+ output = PyString_AS_STRING(rval);
+ output[0] = '"';
+
+ /* We know that everything up to i is ASCII already */
+ chars = i + 1;
+ memcpy(&output[1], input_str, i);
+
+ for (; i < input_chars; i++) {
+ Py_UNICODE c = (Py_UNICODE)(unsigned char)input_str[i];
+ if (S_CHAR(c)) {
+ output[chars++] = (char)c;
+ }
+ else {
+ chars = ascii_escape_char(c, output, chars);
+ }
+ /* An ASCII char can't possibly expand to a surrogate! */
+ if (output_size - chars < (1 + MIN_EXPANSION)) {
+ /* There's more than four, so let's resize by a lot */
+ output_size *= 2;
+ if (output_size > 2 + (input_chars * MIN_EXPANSION)) {
+ output_size = 2 + (input_chars * MIN_EXPANSION);
+ }
+ if (_PyString_Resize(&rval, output_size) == -1) {
+ return NULL;
+ }
+ output = PyString_AS_STRING(rval);
+ }
+ }
+ output[chars++] = '"';
+ if (_PyString_Resize(&rval, chars) == -1) {
+ return NULL;
+ }
+ return rval;
+}
+
+static void
+raise_errmsg(char *msg, PyObject *s, Py_ssize_t end)
+{
+ /* Use the Python function simplejson.decoder.errmsg to raise a nice
+ looking ValueError exception */
+ static PyObject *errmsg_fn = NULL;
+ PyObject *pymsg;
+ if (errmsg_fn == NULL) {
+ PyObject *decoder = PyImport_ImportModule("simplejson.decoder");
+ if (decoder == NULL)
+ return;
+ errmsg_fn = PyObject_GetAttrString(decoder, "errmsg");
+ Py_DECREF(decoder);
+ if (errmsg_fn == NULL)
+ return;
+ }
+ pymsg = PyObject_CallFunction(errmsg_fn, "(zOO&)", msg, s, _convertPyInt_FromSsize_t, &end);
+ if (pymsg) {
+ PyErr_SetObject(PyExc_ValueError, pymsg);
+ Py_DECREF(pymsg);
+ }
+}
+
+static PyObject *
+join_list_unicode(PyObject *lst)
+{
+ /* return u''.join(lst) */
+ static PyObject *joinfn = NULL;
+ if (joinfn == NULL) {
+ PyObject *ustr = PyUnicode_FromUnicode(NULL, 0);
+ if (ustr == NULL)
+ return NULL;
+
+ joinfn = PyObject_GetAttrString(ustr, "join");
+ Py_DECREF(ustr);
+ if (joinfn == NULL)
+ return NULL;
+ }
+ return PyObject_CallFunctionObjArgs(joinfn, lst, NULL);
+}
+
+static PyObject *
+join_list_string(PyObject *lst)
+{
+ /* return ''.join(lst) */
+ static PyObject *joinfn = NULL;
+ if (joinfn == NULL) {
+ PyObject *ustr = PyString_FromStringAndSize(NULL, 0);
+ if (ustr == NULL)
+ return NULL;
+
+ joinfn = PyObject_GetAttrString(ustr, "join");
+ Py_DECREF(ustr);
+ if (joinfn == NULL)
+ return NULL;
+ }
+ return PyObject_CallFunctionObjArgs(joinfn, lst, NULL);
+}
+
+static PyObject *
+_build_rval_index_tuple(PyObject *rval, Py_ssize_t idx) {
+ /* return (rval, idx) tuple, stealing reference to rval */
+ PyObject *tpl;
+ PyObject *pyidx;
+ /*
+ steal a reference to rval, returns (rval, idx)
+ */
+ if (rval == NULL) {
+ return NULL;
+ }
+ pyidx = PyInt_FromSsize_t(idx);
+ if (pyidx == NULL) {
+ Py_DECREF(rval);
+ return NULL;
+ }
+ tpl = PyTuple_New(2);
+ if (tpl == NULL) {
+ Py_DECREF(pyidx);
+ Py_DECREF(rval);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(tpl, 0, rval);
+ PyTuple_SET_ITEM(tpl, 1, pyidx);
+ return tpl;
+}
+
+static PyObject *
+scanstring_str(PyObject *pystr, Py_ssize_t end, char *encoding, int strict, Py_ssize_t *next_end_ptr)
+{
+ /* Read the JSON string from PyString pystr.
+ end is the index of the first character after the quote.
+ encoding is the encoding of pystr (must be an ASCII superset)
+ if strict is zero then literal control characters are allowed
+ *next_end_ptr is a return-by-reference index of the character
+ after the end quote
+
+ Return value is a new PyString (if ASCII-only) or PyUnicode
+ */
+ PyObject *rval;
+ Py_ssize_t len = PyString_GET_SIZE(pystr);
+ Py_ssize_t begin = end - 1;
+ Py_ssize_t next = begin;
+ int has_unicode = 0;
+ char *buf = PyString_AS_STRING(pystr);
+ PyObject *chunks = PyList_New(0);
+ if (chunks == NULL) {
+ goto bail;
+ }
+ if (end < 0 || len <= end) {
+ PyErr_SetString(PyExc_ValueError, "end is out of bounds");
+ goto bail;
+ }
+ while (1) {
+ /* Find the end of the string or the next escape */
+ Py_UNICODE c = 0;
+ PyObject *chunk = NULL;
+ for (next = end; next < len; next++) {
+ c = (unsigned char)buf[next];
+ if (c == '"' || c == '\\') {
+ break;
+ }
+ else if (strict && c <= 0x1f) {
+ raise_errmsg("Invalid control character at", pystr, next);
+ goto bail;
+ }
+ else if (c > 0x7f) {
+ has_unicode = 1;
+ }
+ }
+ if (!(c == '"' || c == '\\')) {
+ raise_errmsg("Unterminated string starting at", pystr, begin);
+ goto bail;
+ }
+ /* Pick up this chunk if it's not zero length */
+ if (next != end) {
+ PyObject *strchunk = PyString_FromStringAndSize(&buf[end], next - end);
+ if (strchunk == NULL) {
+ goto bail;
+ }
+ if (has_unicode) {
+ chunk = PyUnicode_FromEncodedObject(strchunk, encoding, NULL);
+ Py_DECREF(strchunk);
+ if (chunk == NULL) {
+ goto bail;
+ }
+ }
+ else {
+ chunk = strchunk;
+ }
+ if (PyList_Append(chunks, chunk)) {
+ Py_DECREF(chunk);
+ goto bail;
+ }
+ Py_DECREF(chunk);
+ }
+ next++;
+ if (c == '"') {
+ end = next;
+ break;
+ }
+ if (next == len) {
+ raise_errmsg("Unterminated string starting at", pystr, begin);
+ goto bail;
+ }
+ c = buf[next];
+ if (c != 'u') {
+ /* Non-unicode backslash escapes */
+ end = next + 1;
+ switch (c) {
+ case '"': break;
+ case '\\': break;
+ case '/': break;
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ default: c = 0;
+ }
+ if (c == 0) {
+ raise_errmsg("Invalid \\escape", pystr, end - 2);
+ goto bail;
+ }
+ }
+ else {
+ c = 0;
+ next++;
+ end = next + 4;
+ if (end >= len) {
+ raise_errmsg("Invalid \\uXXXX escape", pystr, next - 1);
+ goto bail;
+ }
+ /* Decode 4 hex digits */
+ for (; next < end; next++) {
+ Py_UNICODE digit = buf[next];
+ c <<= 4;
+ switch (digit) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ c |= (digit - '0'); break;
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f':
+ c |= (digit - 'a' + 10); break;
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F':
+ c |= (digit - 'A' + 10); break;
+ default:
+ raise_errmsg("Invalid \\uXXXX escape", pystr, end - 5);
+ goto bail;
+ }
+ }
+#ifdef Py_UNICODE_WIDE
+ /* Surrogate pair */
+ if ((c & 0xfc00) == 0xd800) {
+ Py_UNICODE c2 = 0;
+ if (end + 6 >= len) {
+ raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+ goto bail;
+ }
+ if (buf[next++] != '\\' || buf[next++] != 'u') {
+ raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+ goto bail;
+ }
+ end += 6;
+ /* Decode 4 hex digits */
+ for (; next < end; next++) {
+ c2 <<= 4;
+ Py_UNICODE digit = buf[next];
+ switch (digit) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ c2 |= (digit - '0'); break;
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f':
+ c2 |= (digit - 'a' + 10); break;
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F':
+ c2 |= (digit - 'A' + 10); break;
+ default:
+ raise_errmsg("Invalid \\uXXXX escape", pystr, end - 5);
+ goto bail;
+ }
+ }
+ if ((c2 & 0xfc00) != 0xdc00) {
+ raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+ goto bail;
+ }
+ c = 0x10000 + (((c - 0xd800) << 10) | (c2 - 0xdc00));
+ }
+ else if ((c & 0xfc00) == 0xdc00) {
+ raise_errmsg("Unpaired low surrogate", pystr, end - 5);
+ goto bail;
+ }
+#endif
+ }
+ if (c > 0x7f) {
+ has_unicode = 1;
+ }
+ if (has_unicode) {
+ chunk = PyUnicode_FromUnicode(&c, 1);
+ if (chunk == NULL) {
+ goto bail;
+ }
+ }
+ else {
+ char c_char = Py_CHARMASK(c);
+ chunk = PyString_FromStringAndSize(&c_char, 1);
+ if (chunk == NULL) {
+ goto bail;
+ }
+ }
+ if (PyList_Append(chunks, chunk)) {
+ Py_DECREF(chunk);
+ goto bail;
+ }
+ Py_DECREF(chunk);
+ }
+
+ rval = join_list_string(chunks);
+ if (rval == NULL) {
+ goto bail;
+ }
+ Py_CLEAR(chunks);
+ *next_end_ptr = end;
+ return rval;
+bail:
+ *next_end_ptr = -1;
+ Py_XDECREF(chunks);
+ return NULL;
+}
+
+
+static PyObject *
+scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next_end_ptr)
+{
+ /* Read the JSON string from PyUnicode pystr.
+ end is the index of the first character after the quote.
+ if strict is zero then literal control characters are allowed
+ *next_end_ptr is a return-by-reference index of the character
+ after the end quote
+
+ Return value is a new PyUnicode
+ */
+ PyObject *rval;
+ Py_ssize_t len = PyUnicode_GET_SIZE(pystr);
+ Py_ssize_t begin = end - 1;
+ Py_ssize_t next = begin;
+ const Py_UNICODE *buf = PyUnicode_AS_UNICODE(pystr);
+ PyObject *chunks = PyList_New(0);
+ if (chunks == NULL) {
+ goto bail;
+ }
+ if (end < 0 || len <= end) {
+ PyErr_SetString(PyExc_ValueError, "end is out of bounds");
+ goto bail;
+ }
+ while (1) {
+ /* Find the end of the string or the next escape */
+ Py_UNICODE c = 0;
+ PyObject *chunk = NULL;
+ for (next = end; next < len; next++) {
+ c = buf[next];
+ if (c == '"' || c == '\\') {
+ break;
+ }
+ else if (strict && c <= 0x1f) {
+ raise_errmsg("Invalid control character at", pystr, next);
+ goto bail;
+ }
+ }
+ if (!(c == '"' || c == '\\')) {
+ raise_errmsg("Unterminated string starting at", pystr, begin);
+ goto bail;
+ }
+ /* Pick up this chunk if it's not zero length */
+ if (next != end) {
+ chunk = PyUnicode_FromUnicode(&buf[end], next - end);
+ if (chunk == NULL) {
+ goto bail;
+ }
+ if (PyList_Append(chunks, chunk)) {
+ Py_DECREF(chunk);
+ goto bail;
+ }
+ Py_DECREF(chunk);
+ }
+ next++;
+ if (c == '"') {
+ end = next;
+ break;
+ }
+ if (next == len) {
+ raise_errmsg("Unterminated string starting at", pystr, begin);
+ goto bail;
+ }
+ c = buf[next];
+ if (c != 'u') {
+ /* Non-unicode backslash escapes */
+ end = next + 1;
+ switch (c) {
+ case '"': break;
+ case '\\': break;
+ case '/': break;
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ default: c = 0;
+ }
+ if (c == 0) {
+ raise_errmsg("Invalid \\escape", pystr, end - 2);
+ goto bail;
+ }
+ }
+ else {
+ c = 0;
+ next++;
+ end = next + 4;
+ if (end >= len) {
+ raise_errmsg("Invalid \\uXXXX escape", pystr, next - 1);
+ goto bail;
+ }
+ /* Decode 4 hex digits */
+ for (; next < end; next++) {
+ Py_UNICODE digit = buf[next];
+ c <<= 4;
+ switch (digit) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ c |= (digit - '0'); break;
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f':
+ c |= (digit - 'a' + 10); break;
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F':
+ c |= (digit - 'A' + 10); break;
+ default:
+ raise_errmsg("Invalid \\uXXXX escape", pystr, end - 5);
+ goto bail;
+ }
+ }
+#ifdef Py_UNICODE_WIDE
+ /* Surrogate pair */
+ if ((c & 0xfc00) == 0xd800) {
+ Py_UNICODE c2 = 0;
+ if (end + 6 >= len) {
+ raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+ goto bail;
+ }
+ if (buf[next++] != '\\' || buf[next++] != 'u') {
+ raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+ goto bail;
+ }
+ end += 6;
+ /* Decode 4 hex digits */
+ for (; next < end; next++) {
+ c2 <<= 4;
+ Py_UNICODE digit = buf[next];
+ switch (digit) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ c2 |= (digit - '0'); break;
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f':
+ c2 |= (digit - 'a' + 10); break;
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F':
+ c2 |= (digit - 'A' + 10); break;
+ default:
+ raise_errmsg("Invalid \\uXXXX escape", pystr, end - 5);
+ goto bail;
+ }
+ }
+ if ((c2 & 0xfc00) != 0xdc00) {
+ raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+ goto bail;
+ }
+ c = 0x10000 + (((c - 0xd800) << 10) | (c2 - 0xdc00));
+ }
+ else if ((c & 0xfc00) == 0xdc00) {
+ raise_errmsg("Unpaired low surrogate", pystr, end - 5);
+ goto bail;
+ }
+#endif
+ }
+ chunk = PyUnicode_FromUnicode(&c, 1);
+ if (chunk == NULL) {
+ goto bail;
+ }
+ if (PyList_Append(chunks, chunk)) {
+ Py_DECREF(chunk);
+ goto bail;
+ }
+ Py_DECREF(chunk);
+ }
+
+ rval = join_list_unicode(chunks);
+ if (rval == NULL) {
+ goto bail;
+ }
+ Py_DECREF(chunks);
+ *next_end_ptr = end;
+ return rval;
+bail:
+ *next_end_ptr = -1;
+ Py_XDECREF(chunks);
+ return NULL;
+}
+
+PyDoc_STRVAR(pydoc_scanstring,
+ "scanstring(basestring, end, encoding, strict=True) -> (str, end)\n"
+ "\n"
+ "Scan the string s for a JSON string. End is the index of the\n"
+ "character in s after the quote that started the JSON string.\n"
+ "Unescapes all valid JSON string escape sequences and raises ValueError\n"
+ "on attempt to decode an invalid string. If strict is False then literal\n"
+ "control characters are allowed in the string.\n"
+ "\n"
+ "Returns a tuple of the decoded string and the index of the character in s\n"
+ "after the end quote."
+);
+
+static PyObject *
+py_scanstring(PyObject* self UNUSED, PyObject *args)
+{
+ PyObject *pystr;
+ PyObject *rval;
+ Py_ssize_t end;
+ Py_ssize_t next_end = -1;
+ char *encoding = NULL;
+ int strict = 1;
+ if (!PyArg_ParseTuple(args, "OO&|zi:scanstring", &pystr, _convertPyInt_AsSsize_t, &end, &encoding, &strict)) {
+ return NULL;
+ }
+ if (encoding == NULL) {
+ encoding = DEFAULT_ENCODING;
+ }
+ if (PyString_Check(pystr)) {
+ rval = scanstring_str(pystr, end, encoding, strict, &next_end);
+ }
+ else if (PyUnicode_Check(pystr)) {
+ rval = scanstring_unicode(pystr, end, strict, &next_end);
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "first argument must be a string, not %.80s",
+ Py_TYPE(pystr)->tp_name);
+ return NULL;
+ }
+ return _build_rval_index_tuple(rval, next_end);
+}
+
+PyDoc_STRVAR(pydoc_encode_basestring_ascii,
+ "encode_basestring_ascii(basestring) -> str\n"
+ "\n"
+ "Return an ASCII-only JSON representation of a Python string"
+);
+
+static PyObject *
+py_encode_basestring_ascii(PyObject* self UNUSED, PyObject *pystr)
+{
+ /* Return an ASCII-only JSON representation of a Python string */
+ /* METH_O */
+ if (PyString_Check(pystr)) {
+ return ascii_escape_str(pystr);
+ }
+ else if (PyUnicode_Check(pystr)) {
+ return ascii_escape_unicode(pystr);
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "first argument must be a string, not %.80s",
+ Py_TYPE(pystr)->tp_name);
+ return NULL;
+ }
+}
+
+static void
+scanner_dealloc(PyObject *self)
+{
+ /* Deallocate scanner object */
+ scanner_clear(self);
+ Py_TYPE(self)->tp_free(self);
+}
+
+static int
+scanner_traverse(PyObject *self, visitproc visit, void *arg)
+{
+ PyScannerObject *s;
+ assert(PyScanner_Check(self));
+ s = (PyScannerObject *)self;
+ Py_VISIT(s->encoding);
+ Py_VISIT(s->strict);
+ Py_VISIT(s->object_hook);
+ Py_VISIT(s->parse_float);
+ Py_VISIT(s->parse_int);
+ Py_VISIT(s->parse_constant);
+ return 0;
+}
+
+static int
+scanner_clear(PyObject *self)
+{
+ PyScannerObject *s;
+ assert(PyScanner_Check(self));
+ s = (PyScannerObject *)self;
+ Py_CLEAR(s->encoding);
+ Py_CLEAR(s->strict);
+ Py_CLEAR(s->object_hook);
+ Py_CLEAR(s->parse_float);
+ Py_CLEAR(s->parse_int);
+ Py_CLEAR(s->parse_constant);
+ return 0;
+}
+
+static PyObject *
+_parse_object_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) {
+ /* Read a JSON object from PyString pystr.
+ idx is the index of the first character after the opening curly brace.
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the closing curly brace.
+
+ Returns a new PyObject (usually a dict, but object_hook can change that)
+ */
+ char *str = PyString_AS_STRING(pystr);
+ Py_ssize_t end_idx = PyString_GET_SIZE(pystr) - 1;
+ PyObject *rval = PyDict_New();
+ PyObject *key = NULL;
+ PyObject *val = NULL;
+ char *encoding = PyString_AS_STRING(s->encoding);
+ int strict = PyObject_IsTrue(s->strict);
+ Py_ssize_t next_idx;
+ if (rval == NULL)
+ return NULL;
+
+ /* skip whitespace after { */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* only loop if the object is non-empty */
+ if (idx <= end_idx && str[idx] != '}') {
+ while (idx <= end_idx) {
+ /* read key */
+ if (str[idx] != '"') {
+ raise_errmsg("Expecting property name", pystr, idx);
+ goto bail;
+ }
+ key = scanstring_str(pystr, idx + 1, encoding, strict, &next_idx);
+ if (key == NULL)
+ goto bail;
+ idx = next_idx;
+
+ /* skip whitespace between key and : delimiter, read :, skip whitespace */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+ if (idx > end_idx || str[idx] != ':') {
+ raise_errmsg("Expecting : delimiter", pystr, idx);
+ goto bail;
+ }
+ idx++;
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* read any JSON data type */
+ val = scan_once_str(s, pystr, idx, &next_idx);
+ if (val == NULL)
+ goto bail;
+
+ if (PyDict_SetItem(rval, key, val) == -1)
+ goto bail;
+
+ Py_CLEAR(key);
+ Py_CLEAR(val);
+ idx = next_idx;
+
+ /* skip whitespace before } or , */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* bail if the object is closed or we didn't get the , delimiter */
+ if (idx > end_idx) break;
+ if (str[idx] == '}') {
+ break;
+ }
+ else if (str[idx] != ',') {
+ raise_errmsg("Expecting , delimiter", pystr, idx);
+ goto bail;
+ }
+ idx++;
+
+ /* skip whitespace after , delimiter */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+ }
+ }
+ /* verify that idx < end_idx, str[idx] should be '}' */
+ if (idx > end_idx || str[idx] != '}') {
+ raise_errmsg("Expecting object", pystr, end_idx);
+ goto bail;
+ }
+ /* if object_hook is not None: rval = object_hook(rval) */
+ if (s->object_hook != Py_None) {
+ val = PyObject_CallFunctionObjArgs(s->object_hook, rval, NULL);
+ if (val == NULL)
+ goto bail;
+ Py_DECREF(rval);
+ rval = val;
+ val = NULL;
+ }
+ *next_idx_ptr = idx + 1;
+ return rval;
+bail:
+ Py_XDECREF(key);
+ Py_XDECREF(val);
+ Py_DECREF(rval);
+ return NULL;
+}
+
+static PyObject *
+_parse_object_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) {
+ /* Read a JSON object from PyUnicode pystr.
+ idx is the index of the first character after the opening curly brace.
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the closing curly brace.
+
+ Returns a new PyObject (usually a dict, but object_hook can change that)
+ */
+ Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr);
+ Py_ssize_t end_idx = PyUnicode_GET_SIZE(pystr) - 1;
+ PyObject *val = NULL;
+ PyObject *rval = PyDict_New();
+ PyObject *key = NULL;
+ int strict = PyObject_IsTrue(s->strict);
+ Py_ssize_t next_idx;
+ if (rval == NULL)
+ return NULL;
+
+ /* skip whitespace after { */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* only loop if the object is non-empty */
+ if (idx <= end_idx && str[idx] != '}') {
+ while (idx <= end_idx) {
+ /* read key */
+ if (str[idx] != '"') {
+ raise_errmsg("Expecting property name", pystr, idx);
+ goto bail;
+ }
+ key = scanstring_unicode(pystr, idx + 1, strict, &next_idx);
+ if (key == NULL)
+ goto bail;
+ idx = next_idx;
+
+ /* skip whitespace between key and : delimiter, read :, skip whitespace */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+ if (idx > end_idx || str[idx] != ':') {
+ raise_errmsg("Expecting : delimiter", pystr, idx);
+ goto bail;
+ }
+ idx++;
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* read any JSON term */
+ val = scan_once_unicode(s, pystr, idx, &next_idx);
+ if (val == NULL)
+ goto bail;
+
+ if (PyDict_SetItem(rval, key, val) == -1)
+ goto bail;
+
+ Py_CLEAR(key);
+ Py_CLEAR(val);
+ idx = next_idx;
+
+ /* skip whitespace before } or , */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* bail if the object is closed or we didn't get the , delimiter */
+ if (idx > end_idx) break;
+ if (str[idx] == '}') {
+ break;
+ }
+ else if (str[idx] != ',') {
+ raise_errmsg("Expecting , delimiter", pystr, idx);
+ goto bail;
+ }
+ idx++;
+
+ /* skip whitespace after , delimiter */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+ }
+ }
+
+ /* verify that idx < end_idx, str[idx] should be '}' */
+ if (idx > end_idx || str[idx] != '}') {
+ raise_errmsg("Expecting object", pystr, end_idx);
+ goto bail;
+ }
+
+ /* if object_hook is not None: rval = object_hook(rval) */
+ if (s->object_hook != Py_None) {
+ val = PyObject_CallFunctionObjArgs(s->object_hook, rval, NULL);
+ if (val == NULL)
+ goto bail;
+ Py_DECREF(rval);
+ rval = val;
+ val = NULL;
+ }
+ *next_idx_ptr = idx + 1;
+ return rval;
+bail:
+ Py_XDECREF(key);
+ Py_XDECREF(val);
+ Py_DECREF(rval);
+ return NULL;
+}
+
+static PyObject *
+_parse_array_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) {
+ /* Read a JSON array from PyString pystr.
+ idx is the index of the first character after the opening brace.
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the closing brace.
+
+ Returns a new PyList
+ */
+ char *str = PyString_AS_STRING(pystr);
+ Py_ssize_t end_idx = PyString_GET_SIZE(pystr) - 1;
+ PyObject *val = NULL;
+ PyObject *rval = PyList_New(0);
+ Py_ssize_t next_idx;
+ if (rval == NULL)
+ return NULL;
+
+ /* skip whitespace after [ */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* only loop if the array is non-empty */
+ if (idx <= end_idx && str[idx] != ']') {
+ while (idx <= end_idx) {
+
+ /* read any JSON term and de-tuplefy the (rval, idx) */
+ val = scan_once_str(s, pystr, idx, &next_idx);
+ if (val == NULL)
+ goto bail;
+
+ if (PyList_Append(rval, val) == -1)
+ goto bail;
+
+ Py_CLEAR(val);
+ idx = next_idx;
+
+ /* skip whitespace between term and , */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* bail if the array is closed or we didn't get the , delimiter */
+ if (idx > end_idx) break;
+ if (str[idx] == ']') {
+ break;
+ }
+ else if (str[idx] != ',') {
+ raise_errmsg("Expecting , delimiter", pystr, idx);
+ goto bail;
+ }
+ idx++;
+
+ /* skip whitespace after , */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+ }
+ }
+
+ /* verify that idx < end_idx, str[idx] should be ']' */
+ if (idx > end_idx || str[idx] != ']') {
+ raise_errmsg("Expecting object", pystr, end_idx);
+ goto bail;
+ }
+ *next_idx_ptr = idx + 1;
+ return rval;
+bail:
+ Py_XDECREF(val);
+ Py_DECREF(rval);
+ return NULL;
+}
+
+static PyObject *
+_parse_array_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) {
+ /* Read a JSON array from PyString pystr.
+ idx is the index of the first character after the opening brace.
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the closing brace.
+
+ Returns a new PyList
+ */
+ Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr);
+ Py_ssize_t end_idx = PyUnicode_GET_SIZE(pystr) - 1;
+ PyObject *val = NULL;
+ PyObject *rval = PyList_New(0);
+ Py_ssize_t next_idx;
+ if (rval == NULL)
+ return NULL;
+
+ /* skip whitespace after [ */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* only loop if the array is non-empty */
+ if (idx <= end_idx && str[idx] != ']') {
+ while (idx <= end_idx) {
+
+ /* read any JSON term */
+ val = scan_once_unicode(s, pystr, idx, &next_idx);
+ if (val == NULL)
+ goto bail;
+
+ if (PyList_Append(rval, val) == -1)
+ goto bail;
+
+ Py_CLEAR(val);
+ idx = next_idx;
+
+ /* skip whitespace between term and , */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+ /* bail if the array is closed or we didn't get the , delimiter */
+ if (idx > end_idx) break;
+ if (str[idx] == ']') {
+ break;
+ }
+ else if (str[idx] != ',') {
+ raise_errmsg("Expecting , delimiter", pystr, idx);
+ goto bail;
+ }
+ idx++;
+
+ /* skip whitespace after , */
+ while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+ }
+ }
+
+ /* verify that idx < end_idx, str[idx] should be ']' */
+ if (idx > end_idx || str[idx] != ']') {
+ raise_errmsg("Expecting object", pystr, end_idx);
+ goto bail;
+ }
+ *next_idx_ptr = idx + 1;
+ return rval;
+bail:
+ Py_XDECREF(val);
+ Py_DECREF(rval);
+ return NULL;
+}
+
+static PyObject *
+_parse_constant(PyScannerObject *s, char *constant, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) {
+ /* Read a JSON constant from PyString pystr.
+ constant is the constant string that was found
+ ("NaN", "Infinity", "-Infinity").
+ idx is the index of the first character of the constant
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the constant.
+
+ Returns the result of parse_constant
+ */
+ PyObject *cstr;
+ PyObject *rval;
+ /* constant is "NaN", "Infinity", or "-Infinity" */
+ cstr = PyString_InternFromString(constant);
+ if (cstr == NULL)
+ return NULL;
+
+ /* rval = parse_constant(constant) */
+ rval = PyObject_CallFunctionObjArgs(s->parse_constant, cstr, NULL);
+ idx += PyString_GET_SIZE(cstr);
+ Py_DECREF(cstr);
+ *next_idx_ptr = idx;
+ return rval;
+}
+
+static PyObject *
+_match_number_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t start, Py_ssize_t *next_idx_ptr) {
+ /* Read a JSON number from PyString pystr.
+ idx is the index of the first character of the number
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the number.
+
+ Returns a new PyObject representation of that number:
+ PyInt, PyLong, or PyFloat.
+ May return other types if parse_int or parse_float are set
+ */
+ char *str = PyString_AS_STRING(pystr);
+ Py_ssize_t end_idx = PyString_GET_SIZE(pystr) - 1;
+ Py_ssize_t idx = start;
+ int is_float = 0;
+ PyObject *rval;
+ PyObject *numstr;
+
+ /* read a sign if it's there, make sure it's not the end of the string */
+ if (str[idx] == '-') {
+ idx++;
+ if (idx > end_idx) {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+ }
+
+ /* read as many integer digits as we find as long as it doesn't start with 0 */
+ if (str[idx] >= '1' && str[idx] <= '9') {
+ idx++;
+ while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+ }
+ /* if it starts with 0 we only expect one integer digit */
+ else if (str[idx] == '0') {
+ idx++;
+ }
+ /* no integer digits, error */
+ else {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+
+ /* if the next char is '.' followed by a digit then read all float digits */
+ if (idx < end_idx && str[idx] == '.' && str[idx + 1] >= '0' && str[idx + 1] <= '9') {
+ is_float = 1;
+ idx += 2;
+ while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+ }
+
+ /* if the next char is 'e' or 'E' then maybe read the exponent (or backtrack) */
+ if (idx < end_idx && (str[idx] == 'e' || str[idx] == 'E')) {
+
+ /* save the index of the 'e' or 'E' just in case we need to backtrack */
+ Py_ssize_t e_start = idx;
+ idx++;
+
+ /* read an exponent sign if present */
+ if (idx < end_idx && (str[idx] == '-' || str[idx] == '+')) idx++;
+
+ /* read all digits */
+ while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+
+ /* if we got a digit, then parse as float. if not, backtrack */
+ if (str[idx - 1] >= '0' && str[idx - 1] <= '9') {
+ is_float = 1;
+ }
+ else {
+ idx = e_start;
+ }
+ }
+
+ /* copy the section we determined to be a number */
+ numstr = PyString_FromStringAndSize(&str[start], idx - start);
+ if (numstr == NULL)
+ return NULL;
+ if (is_float) {
+ /* parse as a float using a fast path if available, otherwise call user defined method */
+ if (s->parse_float != (PyObject *)&PyFloat_Type) {
+ rval = PyObject_CallFunctionObjArgs(s->parse_float, numstr, NULL);
+ }
+ else {
+ rval = PyFloat_FromDouble(PyOS_ascii_atof(PyString_AS_STRING(numstr)));
+ }
+ }
+ else {
+ /* parse as an int using a fast path if available, otherwise call user defined method */
+ if (s->parse_int != (PyObject *)&PyInt_Type) {
+ rval = PyObject_CallFunctionObjArgs(s->parse_int, numstr, NULL);
+ }
+ else {
+ rval = PyInt_FromString(PyString_AS_STRING(numstr), NULL, 10);
+ }
+ }
+ Py_DECREF(numstr);
+ *next_idx_ptr = idx;
+ return rval;
+}
+
+static PyObject *
+_match_number_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t start, Py_ssize_t *next_idx_ptr) {
+ /* Read a JSON number from PyUnicode pystr.
+ idx is the index of the first character of the number
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the number.
+
+ Returns a new PyObject representation of that number:
+ PyInt, PyLong, or PyFloat.
+ May return other types if parse_int or parse_float are set
+ */
+ Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr);
+ Py_ssize_t end_idx = PyUnicode_GET_SIZE(pystr) - 1;
+ Py_ssize_t idx = start;
+ int is_float = 0;
+ PyObject *rval;
+ PyObject *numstr;
+
+ /* read a sign if it's there, make sure it's not the end of the string */
+ if (str[idx] == '-') {
+ idx++;
+ if (idx > end_idx) {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+ }
+
+ /* read as many integer digits as we find as long as it doesn't start with 0 */
+ if (str[idx] >= '1' && str[idx] <= '9') {
+ idx++;
+ while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+ }
+ /* if it starts with 0 we only expect one integer digit */
+ else if (str[idx] == '0') {
+ idx++;
+ }
+ /* no integer digits, error */
+ else {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+
+ /* if the next char is '.' followed by a digit then read all float digits */
+ if (idx < end_idx && str[idx] == '.' && str[idx + 1] >= '0' && str[idx + 1] <= '9') {
+ is_float = 1;
+ idx += 2;
+ while (idx < end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+ }
+
+ /* if the next char is 'e' or 'E' then maybe read the exponent (or backtrack) */
+ if (idx < end_idx && (str[idx] == 'e' || str[idx] == 'E')) {
+ Py_ssize_t e_start = idx;
+ idx++;
+
+ /* read an exponent sign if present */
+ if (idx < end_idx && (str[idx] == '-' || str[idx] == '+')) idx++;
+
+ /* read all digits */
+ while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+
+ /* if we got a digit, then parse as float. if not, backtrack */
+ if (str[idx - 1] >= '0' && str[idx - 1] <= '9') {
+ is_float = 1;
+ }
+ else {
+ idx = e_start;
+ }
+ }
+
+ /* copy the section we determined to be a number */
+ numstr = PyUnicode_FromUnicode(&str[start], idx - start);
+ if (numstr == NULL)
+ return NULL;
+ if (is_float) {
+ /* parse as a float using a fast path if available, otherwise call user defined method */
+ if (s->parse_float != (PyObject *)&PyFloat_Type) {
+ rval = PyObject_CallFunctionObjArgs(s->parse_float, numstr, NULL);
+ }
+ else {
+ rval = PyFloat_FromString(numstr, NULL);
+ }
+ }
+ else {
+ /* no fast path for unicode -> int, just call */
+ rval = PyObject_CallFunctionObjArgs(s->parse_int, numstr, NULL);
+ }
+ Py_DECREF(numstr);
+ *next_idx_ptr = idx;
+ return rval;
+}
+
+static PyObject *
+scan_once_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr)
+{
+ /* Read one JSON term (of any kind) from PyString pystr.
+ idx is the index of the first character of the term
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the number.
+
+ Returns a new PyObject representation of the term.
+ */
+ char *str = PyString_AS_STRING(pystr);
+ Py_ssize_t length = PyString_GET_SIZE(pystr);
+ if (idx >= length) {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+ switch (str[idx]) {
+ case '"':
+ /* string */
+ return scanstring_str(pystr, idx + 1,
+ PyString_AS_STRING(s->encoding),
+ PyObject_IsTrue(s->strict),
+ next_idx_ptr);
+ case '{':
+ /* object */
+ return _parse_object_str(s, pystr, idx + 1, next_idx_ptr);
+ case '[':
+ /* array */
+ return _parse_array_str(s, pystr, idx + 1, next_idx_ptr);
+ case 'n':
+ /* null */
+ if ((idx + 3 < length) && str[idx + 1] == 'u' && str[idx + 2] == 'l' && str[idx + 3] == 'l') {
+ Py_INCREF(Py_None);
+ *next_idx_ptr = idx + 4;
+ return Py_None;
+ }
+ break;
+ case 't':
+ /* true */
+ if ((idx + 3 < length) && str[idx + 1] == 'r' && str[idx + 2] == 'u' && str[idx + 3] == 'e') {
+ Py_INCREF(Py_True);
+ *next_idx_ptr = idx + 4;
+ return Py_True;
+ }
+ break;
+ case 'f':
+ /* false */
+ if ((idx + 4 < length) && str[idx + 1] == 'a' && str[idx + 2] == 'l' && str[idx + 3] == 's' && str[idx + 4] == 'e') {
+ Py_INCREF(Py_False);
+ *next_idx_ptr = idx + 5;
+ return Py_False;
+ }
+ break;
+ case 'N':
+ /* NaN */
+ if ((idx + 2 < length) && str[idx + 1] == 'a' && str[idx + 2] == 'N') {
+ return _parse_constant(s, "NaN", idx, next_idx_ptr);
+ }
+ break;
+ case 'I':
+ /* Infinity */
+ if ((idx + 7 < length) && str[idx + 1] == 'n' && str[idx + 2] == 'f' && str[idx + 3] == 'i' && str[idx + 4] == 'n' && str[idx + 5] == 'i' && str[idx + 6] == 't' && str[idx + 7] == 'y') {
+ return _parse_constant(s, "Infinity", idx, next_idx_ptr);
+ }
+ break;
+ case '-':
+ /* -Infinity */
+ if ((idx + 8 < length) && str[idx + 1] == 'I' && str[idx + 2] == 'n' && str[idx + 3] == 'f' && str[idx + 4] == 'i' && str[idx + 5] == 'n' && str[idx + 6] == 'i' && str[idx + 7] == 't' && str[idx + 8] == 'y') {
+ return _parse_constant(s, "-Infinity", idx, next_idx_ptr);
+ }
+ break;
+ }
+ /* Didn't find a string, object, array, or named constant. Look for a number. */
+ return _match_number_str(s, pystr, idx, next_idx_ptr);
+}
+
+static PyObject *
+scan_once_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr)
+{
+ /* Read one JSON term (of any kind) from PyUnicode pystr.
+ idx is the index of the first character of the term
+ *next_idx_ptr is a return-by-reference index to the first character after
+ the number.
+
+ Returns a new PyObject representation of the term.
+ */
+ Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr);
+ Py_ssize_t length = PyUnicode_GET_SIZE(pystr);
+ if (idx >= length) {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+ switch (str[idx]) {
+ case '"':
+ /* string */
+ return scanstring_unicode(pystr, idx + 1,
+ PyObject_IsTrue(s->strict),
+ next_idx_ptr);
+ case '{':
+ /* object */
+ return _parse_object_unicode(s, pystr, idx + 1, next_idx_ptr);
+ case '[':
+ /* array */
+ return _parse_array_unicode(s, pystr, idx + 1, next_idx_ptr);
+ case 'n':
+ /* null */
+ if ((idx + 3 < length) && str[idx + 1] == 'u' && str[idx + 2] == 'l' && str[idx + 3] == 'l') {
+ Py_INCREF(Py_None);
+ *next_idx_ptr = idx + 4;
+ return Py_None;
+ }
+ break;
+ case 't':
+ /* true */
+ if ((idx + 3 < length) && str[idx + 1] == 'r' && str[idx + 2] == 'u' && str[idx + 3] == 'e') {
+ Py_INCREF(Py_True);
+ *next_idx_ptr = idx + 4;
+ return Py_True;
+ }
+ break;
+ case 'f':
+ /* false */
+ if ((idx + 4 < length) && str[idx + 1] == 'a' && str[idx + 2] == 'l' && str[idx + 3] == 's' && str[idx + 4] == 'e') {
+ Py_INCREF(Py_False);
+ *next_idx_ptr = idx + 5;
+ return Py_False;
+ }
+ break;
+ case 'N':
+ /* NaN */
+ if ((idx + 2 < length) && str[idx + 1] == 'a' && str[idx + 2] == 'N') {
+ return _parse_constant(s, "NaN", idx, next_idx_ptr);
+ }
+ break;
+ case 'I':
+ /* Infinity */
+ if ((idx + 7 < length) && str[idx + 1] == 'n' && str[idx + 2] == 'f' && str[idx + 3] == 'i' && str[idx + 4] == 'n' && str[idx + 5] == 'i' && str[idx + 6] == 't' && str[idx + 7] == 'y') {
+ return _parse_constant(s, "Infinity", idx, next_idx_ptr);
+ }
+ break;
+ case '-':
+ /* -Infinity */
+ if ((idx + 8 < length) && str[idx + 1] == 'I' && str[idx + 2] == 'n' && str[idx + 3] == 'f' && str[idx + 4] == 'i' && str[idx + 5] == 'n' && str[idx + 6] == 'i' && str[idx + 7] == 't' && str[idx + 8] == 'y') {
+ return _parse_constant(s, "-Infinity", idx, next_idx_ptr);
+ }
+ break;
+ }
+ /* Didn't find a string, object, array, or named constant. Look for a number. */
+ return _match_number_unicode(s, pystr, idx, next_idx_ptr);
+}
+
+static PyObject *
+scanner_call(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ /* Python callable interface to scan_once_{str,unicode} */
+ PyObject *pystr;
+ PyObject *rval;
+ Py_ssize_t idx;
+ Py_ssize_t next_idx = -1;
+ static char *kwlist[] = {"string", "idx", NULL};
+ PyScannerObject *s;
+ assert(PyScanner_Check(self));
+ s = (PyScannerObject *)self;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO&:scan_once", kwlist, &pystr, _convertPyInt_AsSsize_t, &idx))
+ return NULL;
+
+ if (PyString_Check(pystr)) {
+ rval = scan_once_str(s, pystr, idx, &next_idx);
+ }
+ else if (PyUnicode_Check(pystr)) {
+ rval = scan_once_unicode(s, pystr, idx, &next_idx);
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "first argument must be a string, not %.80s",
+ Py_TYPE(pystr)->tp_name);
+ return NULL;
+ }
+ return _build_rval_index_tuple(rval, next_idx);
+}
+
+static PyObject *
+scanner_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyScannerObject *s;
+ s = (PyScannerObject *)type->tp_alloc(type, 0);
+ if (s != NULL) {
+ s->encoding = NULL;
+ s->strict = NULL;
+ s->object_hook = NULL;
+ s->parse_float = NULL;
+ s->parse_int = NULL;
+ s->parse_constant = NULL;
+ }
+ return (PyObject *)s;
+}
+
+static int
+scanner_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ /* Initialize Scanner object */
+ PyObject *ctx;
+ static char *kwlist[] = {"context", NULL};
+ PyScannerObject *s;
+
+ assert(PyScanner_Check(self));
+ s = (PyScannerObject *)self;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:make_scanner", kwlist, &ctx))
+ return -1;
+
+ /* PyString_AS_STRING is used on encoding */
+ s->encoding = PyObject_GetAttrString(ctx, "encoding");
+ if (s->encoding == Py_None) {
+ Py_DECREF(Py_None);
+ s->encoding = PyString_InternFromString(DEFAULT_ENCODING);
+ }
+ else if (PyUnicode_Check(s->encoding)) {
+ PyObject *tmp = PyUnicode_AsEncodedString(s->encoding, NULL, NULL);
+ Py_DECREF(s->encoding);
+ s->encoding = tmp;
+ }
+ if (s->encoding == NULL || !PyString_Check(s->encoding))
+ goto bail;
+
+ /* All of these will fail "gracefully" so we don't need to verify them */
+ s->strict = PyObject_GetAttrString(ctx, "strict");
+ if (s->strict == NULL)
+ goto bail;
+ s->object_hook = PyObject_GetAttrString(ctx, "object_hook");
+ if (s->object_hook == NULL)
+ goto bail;
+ s->parse_float = PyObject_GetAttrString(ctx, "parse_float");
+ if (s->parse_float == NULL)
+ goto bail;
+ s->parse_int = PyObject_GetAttrString(ctx, "parse_int");
+ if (s->parse_int == NULL)
+ goto bail;
+ s->parse_constant = PyObject_GetAttrString(ctx, "parse_constant");
+ if (s->parse_constant == NULL)
+ goto bail;
+
+ return 0;
+
+bail:
+ Py_CLEAR(s->encoding);
+ Py_CLEAR(s->strict);
+ Py_CLEAR(s->object_hook);
+ Py_CLEAR(s->parse_float);
+ Py_CLEAR(s->parse_int);
+ Py_CLEAR(s->parse_constant);
+ return -1;
+}
+
+PyDoc_STRVAR(scanner_doc, "JSON scanner object");
+
+static
+PyTypeObject PyScannerType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* tp_internal */
+ "simplejson._speedups.Scanner", /* tp_name */
+ sizeof(PyScannerObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ scanner_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ scanner_call, /* tp_call */
+ 0, /* tp_str */
+ 0,/* PyObject_GenericGetAttr, */ /* tp_getattro */
+ 0,/* PyObject_GenericSetAttr, */ /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
+ scanner_doc, /* tp_doc */
+ scanner_traverse, /* tp_traverse */
+ scanner_clear, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ scanner_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ scanner_init, /* tp_init */
+ 0,/* PyType_GenericAlloc, */ /* tp_alloc */
+ scanner_new, /* tp_new */
+ 0,/* PyObject_GC_Del, */ /* tp_free */
+};
+
+static PyObject *
+encoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyEncoderObject *s;
+ s = (PyEncoderObject *)type->tp_alloc(type, 0);
+ if (s != NULL) {
+ s->markers = NULL;
+ s->defaultfn = NULL;
+ s->encoder = NULL;
+ s->indent = NULL;
+ s->key_separator = NULL;
+ s->item_separator = NULL;
+ s->sort_keys = NULL;
+ s->skipkeys = NULL;
+ }
+ return (PyObject *)s;
+}
+
+static int
+encoder_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ /* initialize Encoder object */
+ static char *kwlist[] = {"markers", "default", "encoder", "indent", "key_separator", "item_separator", "sort_keys", "skipkeys", "allow_nan", NULL};
+
+ PyEncoderObject *s;
+ PyObject *allow_nan;
+
+ assert(PyEncoder_Check(self));
+ s = (PyEncoderObject *)self;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOOOOOOOO:make_encoder", kwlist,
+ &s->markers, &s->defaultfn, &s->encoder, &s->indent, &s->key_separator, &s->item_separator, &s->sort_keys, &s->skipkeys, &allow_nan))
+ return -1;
+
+ Py_INCREF(s->markers);
+ Py_INCREF(s->defaultfn);
+ Py_INCREF(s->encoder);
+ Py_INCREF(s->indent);
+ Py_INCREF(s->key_separator);
+ Py_INCREF(s->item_separator);
+ Py_INCREF(s->sort_keys);
+ Py_INCREF(s->skipkeys);
+ s->fast_encode = (PyCFunction_Check(s->encoder) && PyCFunction_GetFunction(s->encoder) == (PyCFunction)py_encode_basestring_ascii);
+ s->allow_nan = PyObject_IsTrue(allow_nan);
+ return 0;
+}
+
+static PyObject *
+encoder_call(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ /* Python callable interface to encode_listencode_obj */
+ static char *kwlist[] = {"obj", "_current_indent_level", NULL};
+ PyObject *obj;
+ PyObject *rval;
+ Py_ssize_t indent_level;
+ PyEncoderObject *s;
+ assert(PyEncoder_Check(self));
+ s = (PyEncoderObject *)self;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO&:_iterencode", kwlist,
+ &obj, _convertPyInt_AsSsize_t, &indent_level))
+ return NULL;
+ rval = PyList_New(0);
+ if (rval == NULL)
+ return NULL;
+ if (encoder_listencode_obj(s, rval, obj, indent_level)) {
+ Py_DECREF(rval);
+ return NULL;
+ }
+ return rval;
+}
+
+static PyObject *
+_encoded_const(PyObject *obj)
+{
+ /* Return the JSON string representation of None, True, False */
+ if (obj == Py_None) {
+ static PyObject *s_null = NULL;
+ if (s_null == NULL) {
+ s_null = PyString_InternFromString("null");
+ }
+ Py_INCREF(s_null);
+ return s_null;
+ }
+ else if (obj == Py_True) {
+ static PyObject *s_true = NULL;
+ if (s_true == NULL) {
+ s_true = PyString_InternFromString("true");
+ }
+ Py_INCREF(s_true);
+ return s_true;
+ }
+ else if (obj == Py_False) {
+ static PyObject *s_false = NULL;
+ if (s_false == NULL) {
+ s_false = PyString_InternFromString("false");
+ }
+ Py_INCREF(s_false);
+ return s_false;
+ }
+ else {
+ PyErr_SetString(PyExc_ValueError, "not a const");
+ return NULL;
+ }
+}
+
+static PyObject *
+encoder_encode_float(PyEncoderObject *s, PyObject *obj)
+{
+ /* Return the JSON representation of a PyFloat */
+ double i = PyFloat_AS_DOUBLE(obj);
+ if (!Py_IS_FINITE(i)) {
+ if (!s->allow_nan) {
+ PyErr_SetString(PyExc_ValueError, "Out of range float values are not JSON compliant");
+ return NULL;
+ }
+ if (i > 0) {
+ return PyString_FromString("Infinity");
+ }
+ else if (i < 0) {
+ return PyString_FromString("-Infinity");
+ }
+ else {
+ return PyString_FromString("NaN");
+ }
+ }
+ /* Use a better float format here? */
+ return PyObject_Repr(obj);
+}
+
+static PyObject *
+encoder_encode_string(PyEncoderObject *s, PyObject *obj)
+{
+ /* Return the JSON representation of a string */
+ if (s->fast_encode)
+ return py_encode_basestring_ascii(NULL, obj);
+ else
+ return PyObject_CallFunctionObjArgs(s->encoder, obj, NULL);
+}
+
+static int
+_steal_list_append(PyObject *lst, PyObject *stolen)
+{
+ /* Append stolen and then decrement its reference count */
+ int rval = PyList_Append(lst, stolen);
+ Py_DECREF(stolen);
+ return rval;
+}
+
+static int
+encoder_listencode_obj(PyEncoderObject *s, PyObject *rval, PyObject *obj, Py_ssize_t indent_level)
+{
+ /* Encode Python object obj to a JSON term, rval is a PyList */
+ PyObject *newobj;
+ int rv;
+
+ if (obj == Py_None || obj == Py_True || obj == Py_False) {
+ PyObject *cstr = _encoded_const(obj);
+ if (cstr == NULL)
+ return -1;
+ return _steal_list_append(rval, cstr);
+ }
+ else if (PyString_Check(obj) || PyUnicode_Check(obj))
+ {
+ PyObject *encoded = encoder_encode_string(s, obj);
+ if (encoded == NULL)
+ return -1;
+ return _steal_list_append(rval, encoded);
+ }
+ else if (PyInt_Check(obj) || PyLong_Check(obj)) {
+ PyObject *encoded = PyObject_Str(obj);
+ if (encoded == NULL)
+ return -1;
+ return _steal_list_append(rval, encoded);
+ }
+ else if (PyFloat_Check(obj)) {
+ PyObject *encoded = encoder_encode_float(s, obj);
+ if (encoded == NULL)
+ return -1;
+ return _steal_list_append(rval, encoded);
+ }
+ else if (PyList_Check(obj) || PyTuple_Check(obj)) {
+ return encoder_listencode_list(s, rval, obj, indent_level);
+ }
+ else if (PyDict_Check(obj)) {
+ return encoder_listencode_dict(s, rval, obj, indent_level);
+ }
+ else {
+ PyObject *ident = NULL;
+ if (s->markers != Py_None) {
+ int has_key;
+ ident = PyLong_FromVoidPtr(obj);
+ if (ident == NULL)
+ return -1;
+ has_key = PyDict_Contains(s->markers, ident);
+ if (has_key) {
+ if (has_key != -1)
+ PyErr_SetString(PyExc_ValueError, "Circular reference detected");
+ Py_DECREF(ident);
+ return -1;
+ }
+ if (PyDict_SetItem(s->markers, ident, obj)) {
+ Py_DECREF(ident);
+ return -1;
+ }
+ }
+ newobj = PyObject_CallFunctionObjArgs(s->defaultfn, obj, NULL);
+ if (newobj == NULL) {
+ Py_XDECREF(ident);
+ return -1;
+ }
+ rv = encoder_listencode_obj(s, rval, newobj, indent_level);
+ Py_DECREF(newobj);
+ if (rv) {
+ Py_XDECREF(ident);
+ return -1;
+ }
+ if (ident != NULL) {
+ if (PyDict_DelItem(s->markers, ident)) {
+ Py_XDECREF(ident);
+ return -1;
+ }
+ Py_XDECREF(ident);
+ }
+ return rv;
+ }
+}
+
+static int
+encoder_listencode_dict(PyEncoderObject *s, PyObject *rval, PyObject *dct, Py_ssize_t indent_level)
+{
+ /* Encode Python dict dct a JSON term, rval is a PyList */
+ static PyObject *open_dict = NULL;
+ static PyObject *close_dict = NULL;
+ static PyObject *empty_dict = NULL;
+ PyObject *kstr = NULL;
+ PyObject *ident = NULL;
+ PyObject *key, *value;
+ Py_ssize_t pos;
+ int skipkeys;
+ Py_ssize_t idx;
+
+ if (open_dict == NULL || close_dict == NULL || empty_dict == NULL) {
+ open_dict = PyString_InternFromString("{");
+ close_dict = PyString_InternFromString("}");
+ empty_dict = PyString_InternFromString("{}");
+ if (open_dict == NULL || close_dict == NULL || empty_dict == NULL)
+ return -1;
+ }
+ if (PyDict_Size(dct) == 0)
+ return PyList_Append(rval, empty_dict);
+
+ if (s->markers != Py_None) {
+ int has_key;
+ ident = PyLong_FromVoidPtr(dct);
+ if (ident == NULL)
+ goto bail;
+ has_key = PyDict_Contains(s->markers, ident);
+ if (has_key) {
+ if (has_key != -1)
+ PyErr_SetString(PyExc_ValueError, "Circular reference detected");
+ goto bail;
+ }
+ if (PyDict_SetItem(s->markers, ident, dct)) {
+ goto bail;
+ }
+ }
+
+ if (PyList_Append(rval, open_dict))
+ goto bail;
+
+ if (s->indent != Py_None) {
+ /* TODO: DOES NOT RUN */
+ indent_level += 1;
+ /*
+ newline_indent = '\n' + (' ' * (_indent * _current_indent_level))
+ separator = _item_separator + newline_indent
+ buf += newline_indent
+ */
+ }
+
+ /* TODO: C speedup not implemented for sort_keys */
+
+ pos = 0;
+ skipkeys = PyObject_IsTrue(s->skipkeys);
+ idx = 0;
+ while (PyDict_Next(dct, &pos, &key, &value)) {
+ PyObject *encoded;
+
+ if (PyString_Check(key) || PyUnicode_Check(key)) {
+ Py_INCREF(key);
+ kstr = key;
+ }
+ else if (PyFloat_Check(key)) {
+ kstr = encoder_encode_float(s, key);
+ if (kstr == NULL)
+ goto bail;
+ }
+ else if (PyInt_Check(key) || PyLong_Check(key)) {
+ kstr = PyObject_Str(key);
+ if (kstr == NULL)
+ goto bail;
+ }
+ else if (key == Py_True || key == Py_False || key == Py_None) {
+ kstr = _encoded_const(key);
+ if (kstr == NULL)
+ goto bail;
+ }
+ else if (skipkeys) {
+ continue;
+ }
+ else {
+ /* TODO: include repr of key */
+ PyErr_SetString(PyExc_ValueError, "keys must be a string");
+ goto bail;
+ }
+
+ if (idx) {
+ if (PyList_Append(rval, s->item_separator))
+ goto bail;
+ }
+
+ encoded = encoder_encode_string(s, kstr);
+ Py_CLEAR(kstr);
+ if (encoded == NULL)
+ goto bail;
+ if (PyList_Append(rval, encoded)) {
+ Py_DECREF(encoded);
+ goto bail;
+ }
+ Py_DECREF(encoded);
+ if (PyList_Append(rval, s->key_separator))
+ goto bail;
+ if (encoder_listencode_obj(s, rval, value, indent_level))
+ goto bail;
+ idx += 1;
+ }
+ if (ident != NULL) {
+ if (PyDict_DelItem(s->markers, ident))
+ goto bail;
+ Py_CLEAR(ident);
+ }
+ if (s->indent != Py_None) {
+ /* TODO: DOES NOT RUN */
+ indent_level -= 1;
+ /*
+ yield '\n' + (' ' * (_indent * _current_indent_level))
+ */
+ }
+ if (PyList_Append(rval, close_dict))
+ goto bail;
+ return 0;
+
+bail:
+ Py_XDECREF(kstr);
+ Py_XDECREF(ident);
+ return -1;
+}
+
+
+static int
+encoder_listencode_list(PyEncoderObject *s, PyObject *rval, PyObject *seq, Py_ssize_t indent_level)
+{
+ /* Encode Python list seq to a JSON term, rval is a PyList */
+ static PyObject *open_array = NULL;
+ static PyObject *close_array = NULL;
+ static PyObject *empty_array = NULL;
+ PyObject *ident = NULL;
+ PyObject *s_fast = NULL;
+ Py_ssize_t num_items;
+ PyObject **seq_items;
+ Py_ssize_t i;
+
+ if (open_array == NULL || close_array == NULL || empty_array == NULL) {
+ open_array = PyString_InternFromString("[");
+ close_array = PyString_InternFromString("]");
+ empty_array = PyString_InternFromString("[]");
+ if (open_array == NULL || close_array == NULL || empty_array == NULL)
+ return -1;
+ }
+ ident = NULL;
+ s_fast = PySequence_Fast(seq, "_iterencode_list needs a sequence");
+ if (s_fast == NULL)
+ return -1;
+ num_items = PySequence_Fast_GET_SIZE(s_fast);
+ if (num_items == 0) {
+ Py_DECREF(s_fast);
+ return PyList_Append(rval, empty_array);
+ }
+
+ if (s->markers != Py_None) {
+ int has_key;
+ ident = PyLong_FromVoidPtr(seq);
+ if (ident == NULL)
+ goto bail;
+ has_key = PyDict_Contains(s->markers, ident);
+ if (has_key) {
+ if (has_key != -1)
+ PyErr_SetString(PyExc_ValueError, "Circular reference detected");
+ goto bail;
+ }
+ if (PyDict_SetItem(s->markers, ident, seq)) {
+ goto bail;
+ }
+ }
+
+ seq_items = PySequence_Fast_ITEMS(s_fast);
+ if (PyList_Append(rval, open_array))
+ goto bail;
+ if (s->indent != Py_None) {
+ /* TODO: DOES NOT RUN */
+ indent_level += 1;
+ /*
+ newline_indent = '\n' + (' ' * (_indent * _current_indent_level))
+ separator = _item_separator + newline_indent
+ buf += newline_indent
+ */
+ }
+ for (i = 0; i < num_items; i++) {
+ PyObject *obj = seq_items[i];
+ if (i) {
+ if (PyList_Append(rval, s->item_separator))
+ goto bail;
+ }
+ if (encoder_listencode_obj(s, rval, obj, indent_level))
+ goto bail;
+ }
+ if (ident != NULL) {
+ if (PyDict_DelItem(s->markers, ident))
+ goto bail;
+ Py_CLEAR(ident);
+ }
+ if (s->indent != Py_None) {
+ /* TODO: DOES NOT RUN */
+ indent_level -= 1;
+ /*
+ yield '\n' + (' ' * (_indent * _current_indent_level))
+ */
+ }
+ if (PyList_Append(rval, close_array))
+ goto bail;
+ Py_DECREF(s_fast);
+ return 0;
+
+bail:
+ Py_XDECREF(ident);
+ Py_DECREF(s_fast);
+ return -1;
+}
+
+static void
+encoder_dealloc(PyObject *self)
+{
+ /* Deallocate Encoder */
+ encoder_clear(self);
+ Py_TYPE(self)->tp_free(self);
+}
+
+static int
+encoder_traverse(PyObject *self, visitproc visit, void *arg)
+{
+ PyEncoderObject *s;
+ assert(PyEncoder_Check(self));
+ s = (PyEncoderObject *)self;
+ Py_VISIT(s->markers);
+ Py_VISIT(s->defaultfn);
+ Py_VISIT(s->encoder);
+ Py_VISIT(s->indent);
+ Py_VISIT(s->key_separator);
+ Py_VISIT(s->item_separator);
+ Py_VISIT(s->sort_keys);
+ Py_VISIT(s->skipkeys);
+ return 0;
+}
+
+static int
+encoder_clear(PyObject *self)
+{
+ /* Deallocate Encoder */
+ PyEncoderObject *s;
+ assert(PyEncoder_Check(self));
+ s = (PyEncoderObject *)self;
+ Py_CLEAR(s->markers);
+ Py_CLEAR(s->defaultfn);
+ Py_CLEAR(s->encoder);
+ Py_CLEAR(s->indent);
+ Py_CLEAR(s->key_separator);
+ Py_CLEAR(s->item_separator);
+ Py_CLEAR(s->sort_keys);
+ Py_CLEAR(s->skipkeys);
+ return 0;
+}
+
+PyDoc_STRVAR(encoder_doc, "_iterencode(obj, _current_indent_level) -> iterable");
+
+static
+PyTypeObject PyEncoderType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* tp_internal */
+ "simplejson._speedups.Encoder", /* tp_name */
+ sizeof(PyEncoderObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ encoder_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ encoder_call, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
+ encoder_doc, /* tp_doc */
+ encoder_traverse, /* tp_traverse */
+ encoder_clear, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ encoder_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ encoder_init, /* tp_init */
+ 0, /* tp_alloc */
+ encoder_new, /* tp_new */
+ 0, /* tp_free */
+};
+
+static PyMethodDef speedups_methods[] = {
+ {"encode_basestring_ascii",
+ (PyCFunction)py_encode_basestring_ascii,
+ METH_O,
+ pydoc_encode_basestring_ascii},
+ {"scanstring",
+ (PyCFunction)py_scanstring,
+ METH_VARARGS,
+ pydoc_scanstring},
+ {NULL, NULL, 0, NULL}
+};
+
+PyDoc_STRVAR(module_doc,
+"simplejson speedups\n");
+
+void
+init_speedups(void)
+{
+ PyObject *m;
+ PyScannerType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyScannerType) < 0)
+ return;
+ PyEncoderType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyEncoderType) < 0)
+ return;
+ m = Py_InitModule3("_speedups", speedups_methods, module_doc);
+ Py_INCREF((PyObject*)&PyScannerType);
+ PyModule_AddObject(m, "make_scanner", (PyObject*)&PyScannerType);
+ Py_INCREF((PyObject*)&PyEncoderType);
+ PyModule_AddObject(m, "make_encoder", (PyObject*)&PyEncoderType);
+}
--- /dev/null
+"""Implementation of JSONDecoder
+"""
+import re
+import sys
+import struct
+
+from simplejson.scanner import make_scanner
+try:
+ from simplejson._speedups import scanstring as c_scanstring
+except ImportError:
+ c_scanstring = None
+
+__all__ = ['JSONDecoder']
+
+FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
+
+def _floatconstants():
+ _BYTES = '7FF80000000000007FF0000000000000'.decode('hex')
+ if sys.byteorder != 'big':
+ _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1]
+ nan, inf = struct.unpack('dd', _BYTES)
+ return nan, inf, -inf
+
+NaN, PosInf, NegInf = _floatconstants()
+
+
+def linecol(doc, pos):
+ lineno = doc.count('\n', 0, pos) + 1
+ if lineno == 1:
+ colno = pos
+ else:
+ colno = pos - doc.rindex('\n', 0, pos)
+ return lineno, colno
+
+
+def errmsg(msg, doc, pos, end=None):
+ # Note that this function is called from _speedups
+ lineno, colno = linecol(doc, pos)
+ if end is None:
+ #fmt = '{0}: line {1} column {2} (char {3})'
+ #return fmt.format(msg, lineno, colno, pos)
+ fmt = '%s: line %d column %d (char %d)'
+ return fmt % (msg, lineno, colno, pos)
+ endlineno, endcolno = linecol(doc, end)
+ #fmt = '{0}: line {1} column {2} - line {3} column {4} (char {5} - {6})'
+ #return fmt.format(msg, lineno, colno, endlineno, endcolno, pos, end)
+ fmt = '%s: line %d column %d - line %d column %d (char %d - %d)'
+ return fmt % (msg, lineno, colno, endlineno, endcolno, pos, end)
+
+
+_CONSTANTS = {
+ '-Infinity': NegInf,
+ 'Infinity': PosInf,
+ 'NaN': NaN,
+}
+
+STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS)
+BACKSLASH = {
+ '"': u'"', '\\': u'\\', '/': u'/',
+ 'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t',
+}
+
+DEFAULT_ENCODING = "utf-8"
+
+def py_scanstring(s, end, encoding=None, strict=True, _b=BACKSLASH, _m=STRINGCHUNK.match):
+ """Scan the string s for a JSON string. End is the index of the
+ character in s after the quote that started the JSON string.
+ Unescapes all valid JSON string escape sequences and raises ValueError
+ on attempt to decode an invalid string. If strict is False then literal
+ control characters are allowed in the string.
+
+ Returns a tuple of the decoded string and the index of the character in s
+ after the end quote."""
+ if encoding is None:
+ encoding = DEFAULT_ENCODING
+ chunks = []
+ _append = chunks.append
+ begin = end - 1
+ while 1:
+ chunk = _m(s, end)
+ if chunk is None:
+ raise ValueError(
+ errmsg("Unterminated string starting at", s, begin))
+ end = chunk.end()
+ content, terminator = chunk.groups()
+ # Content is contains zero or more unescaped string characters
+ if content:
+ if not isinstance(content, unicode):
+ content = unicode(content, encoding)
+ _append(content)
+ # Terminator is the end of string, a literal control character,
+ # or a backslash denoting that an escape sequence follows
+ if terminator == '"':
+ break
+ elif terminator != '\\':
+ if strict:
+ msg = "Invalid control character %r at" % (terminator,)
+ #msg = "Invalid control character {0!r} at".format(terminator)
+ raise ValueError(errmsg(msg, s, end))
+ else:
+ _append(terminator)
+ continue
+ try:
+ esc = s[end]
+ except IndexError:
+ raise ValueError(
+ errmsg("Unterminated string starting at", s, begin))
+ # If not a unicode escape sequence, must be in the lookup table
+ if esc != 'u':
+ try:
+ char = _b[esc]
+ except KeyError:
+ msg = "Invalid \\escape: " + repr(esc)
+ raise ValueError(errmsg(msg, s, end))
+ end += 1
+ else:
+ # Unicode escape sequence
+ esc = s[end + 1:end + 5]
+ next_end = end + 5
+ if len(esc) != 4:
+ msg = "Invalid \\uXXXX escape"
+ raise ValueError(errmsg(msg, s, end))
+ uni = int(esc, 16)
+ # Check for surrogate pair on UCS-4 systems
+ if 0xd800 <= uni <= 0xdbff and sys.maxunicode > 65535:
+ msg = "Invalid \\uXXXX\\uXXXX surrogate pair"
+ if not s[end + 5:end + 7] == '\\u':
+ raise ValueError(errmsg(msg, s, end))
+ esc2 = s[end + 7:end + 11]
+ if len(esc2) != 4:
+ raise ValueError(errmsg(msg, s, end))
+ uni2 = int(esc2, 16)
+ uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
+ next_end += 6
+ char = unichr(uni)
+ end = next_end
+ # Append the unescaped character
+ _append(char)
+ return u''.join(chunks), end
+
+
+# Use speedup if available
+scanstring = c_scanstring or py_scanstring
+
+WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS)
+WHITESPACE_STR = ' \t\n\r'
+
+def JSONObject((s, end), encoding, strict, scan_once, object_hook, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
+ pairs = {}
+ # Use a slice to prevent IndexError from being raised, the following
+ # check will raise a more specific ValueError if the string is empty
+ nextchar = s[end:end + 1]
+ # Normally we expect nextchar == '"'
+ if nextchar != '"':
+ if nextchar in _ws:
+ end = _w(s, end).end()
+ nextchar = s[end:end + 1]
+ # Trivial empty object
+ if nextchar == '}':
+ return pairs, end + 1
+ elif nextchar != '"':
+ raise ValueError(errmsg("Expecting property name", s, end))
+ end += 1
+ while True:
+ key, end = scanstring(s, end, encoding, strict)
+
+ # To skip some function call overhead we optimize the fast paths where
+ # the JSON key separator is ": " or just ":".
+ if s[end:end + 1] != ':':
+ end = _w(s, end).end()
+ if s[end:end + 1] != ':':
+ raise ValueError(errmsg("Expecting : delimiter", s, end))
+
+ end += 1
+
+ try:
+ if s[end] in _ws:
+ end += 1
+ if s[end] in _ws:
+ end = _w(s, end + 1).end()
+ except IndexError:
+ pass
+
+ try:
+ value, end = scan_once(s, end)
+ except StopIteration:
+ raise ValueError(errmsg("Expecting object", s, end))
+ pairs[key] = value
+
+ try:
+ nextchar = s[end]
+ if nextchar in _ws:
+ end = _w(s, end + 1).end()
+ nextchar = s[end]
+ except IndexError:
+ nextchar = ''
+ end += 1
+
+ if nextchar == '}':
+ break
+ elif nextchar != ',':
+ raise ValueError(errmsg("Expecting , delimiter", s, end - 1))
+
+ try:
+ nextchar = s[end]
+ if nextchar in _ws:
+ end += 1
+ nextchar = s[end]
+ if nextchar in _ws:
+ end = _w(s, end + 1).end()
+ nextchar = s[end]
+ except IndexError:
+ nextchar = ''
+
+ end += 1
+ if nextchar != '"':
+ raise ValueError(errmsg("Expecting property name", s, end - 1))
+
+ if object_hook is not None:
+ pairs = object_hook(pairs)
+ return pairs, end
+
+def JSONArray((s, end), scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
+ values = []
+ nextchar = s[end:end + 1]
+ if nextchar in _ws:
+ end = _w(s, end + 1).end()
+ nextchar = s[end:end + 1]
+ # Look-ahead for trivial empty array
+ if nextchar == ']':
+ return values, end + 1
+ _append = values.append
+ while True:
+ try:
+ value, end = scan_once(s, end)
+ except StopIteration:
+ raise ValueError(errmsg("Expecting object", s, end))
+ _append(value)
+ nextchar = s[end:end + 1]
+ if nextchar in _ws:
+ end = _w(s, end + 1).end()
+ nextchar = s[end:end + 1]
+ end += 1
+ if nextchar == ']':
+ break
+ elif nextchar != ',':
+ raise ValueError(errmsg("Expecting , delimiter", s, end))
+
+ try:
+ if s[end] in _ws:
+ end += 1
+ if s[end] in _ws:
+ end = _w(s, end + 1).end()
+ except IndexError:
+ pass
+
+ return values, end
+
+class JSONDecoder(object):
+ """Simple JSON <http://json.org> decoder
+
+ Performs the following translations in decoding by default:
+
+ +---------------+-------------------+
+ | JSON | Python |
+ +===============+===================+
+ | object | dict |
+ +---------------+-------------------+
+ | array | list |
+ +---------------+-------------------+
+ | string | unicode |
+ +---------------+-------------------+
+ | number (int) | int, long |
+ +---------------+-------------------+
+ | number (real) | float |
+ +---------------+-------------------+
+ | true | True |
+ +---------------+-------------------+
+ | false | False |
+ +---------------+-------------------+
+ | null | None |
+ +---------------+-------------------+
+
+ It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as
+ their corresponding ``float`` values, which is outside the JSON spec.
+
+ """
+
+ def __init__(self, encoding=None, object_hook=None, parse_float=None,
+ parse_int=None, parse_constant=None, strict=True):
+ """``encoding`` determines the encoding used to interpret any ``str``
+ objects decoded by this instance (utf-8 by default). It has no
+ effect when decoding ``unicode`` objects.
+
+ Note that currently only encodings that are a superset of ASCII work,
+ strings of other encodings should be passed in as ``unicode``.
+
+ ``object_hook``, if specified, will be called with the result
+ of every JSON object decoded and its return value will be used in
+ place of the given ``dict``. This can be used to provide custom
+ deserializations (e.g. to support JSON-RPC class hinting).
+
+ ``parse_float``, if specified, will be called with the string
+ of every JSON float to be decoded. By default this is equivalent to
+ float(num_str). This can be used to use another datatype or parser
+ for JSON floats (e.g. decimal.Decimal).
+
+ ``parse_int``, if specified, will be called with the string
+ of every JSON int to be decoded. By default this is equivalent to
+ int(num_str). This can be used to use another datatype or parser
+ for JSON integers (e.g. float).
+
+ ``parse_constant``, if specified, will be called with one of the
+ following strings: -Infinity, Infinity, NaN.
+ This can be used to raise an exception if invalid JSON numbers
+ are encountered.
+
+ """
+ self.encoding = encoding
+ self.object_hook = object_hook
+ self.parse_float = parse_float or float
+ self.parse_int = parse_int or int
+ self.parse_constant = parse_constant or _CONSTANTS.__getitem__
+ self.strict = strict
+ self.parse_object = JSONObject
+ self.parse_array = JSONArray
+ self.parse_string = scanstring
+ self.scan_once = make_scanner(self)
+
+ def decode(self, s, _w=WHITESPACE.match):
+ """Return the Python representation of ``s`` (a ``str`` or ``unicode``
+ instance containing a JSON document)
+
+ """
+ obj, end = self.raw_decode(s, idx=_w(s, 0).end())
+ end = _w(s, end).end()
+ if end != len(s):
+ raise ValueError(errmsg("Extra data", s, end, len(s)))
+ return obj
+
+ def raw_decode(self, s, idx=0):
+ """Decode a JSON document from ``s`` (a ``str`` or ``unicode`` beginning
+ with a JSON document) and return a 2-tuple of the Python
+ representation and the index in ``s`` where the document ended.
+
+ This can be used to decode a JSON document from a string that may
+ have extraneous data at the end.
+
+ """
+ try:
+ obj, end = self.scan_once(s, idx)
+ except StopIteration:
+ raise ValueError("No JSON object could be decoded")
+ return obj, end
--- /dev/null
+"""Implementation of JSONEncoder
+"""
+import re
+
+try:
+ from simplejson._speedups import encode_basestring_ascii as c_encode_basestring_ascii
+except ImportError:
+ c_encode_basestring_ascii = None
+try:
+ from simplejson._speedups import make_encoder as c_make_encoder
+except ImportError:
+ c_make_encoder = None
+
+ESCAPE = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t]')
+ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
+HAS_UTF8 = re.compile(r'[\x80-\xff]')
+ESCAPE_DCT = {
+ '\\': '\\\\',
+ '"': '\\"',
+ '\b': '\\b',
+ '\f': '\\f',
+ '\n': '\\n',
+ '\r': '\\r',
+ '\t': '\\t',
+}
+for i in range(0x20):
+ #ESCAPE_DCT.setdefault(chr(i), '\\u{0:04x}'.format(i))
+ ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
+
+# Assume this produces an infinity on all machines (probably not guaranteed)
+INFINITY = float('1e66666')
+FLOAT_REPR = repr
+
+def encode_basestring(s):
+ """Return a JSON representation of a Python string
+
+ """
+ def replace(match):
+ return ESCAPE_DCT[match.group(0)]
+ return '"' + ESCAPE.sub(replace, s) + '"'
+
+
+def py_encode_basestring_ascii(s):
+ """Return an ASCII-only JSON representation of a Python string
+
+ """
+ if isinstance(s, str) and HAS_UTF8.search(s) is not None:
+ s = s.decode('utf-8')
+ def replace(match):
+ s = match.group(0)
+ try:
+ return ESCAPE_DCT[s]
+ except KeyError:
+ n = ord(s)
+ if n < 0x10000:
+ #return '\\u{0:04x}'.format(n)
+ return '\\u%04x' % (n,)
+ else:
+ # surrogate pair
+ n -= 0x10000
+ s1 = 0xd800 | ((n >> 10) & 0x3ff)
+ s2 = 0xdc00 | (n & 0x3ff)
+ #return '\\u{0:04x}\\u{1:04x}'.format(s1, s2)
+ return '\\u%04x\\u%04x' % (s1, s2)
+ return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"'
+
+
+encode_basestring_ascii = c_encode_basestring_ascii or py_encode_basestring_ascii
+
+class JSONEncoder(object):
+ """Extensible JSON <http://json.org> encoder for Python data structures.
+
+ Supports the following objects and types by default:
+
+ +-------------------+---------------+
+ | Python | JSON |
+ +===================+===============+
+ | dict | object |
+ +-------------------+---------------+
+ | list, tuple | array |
+ +-------------------+---------------+
+ | str, unicode | string |
+ +-------------------+---------------+
+ | int, long, float | number |
+ +-------------------+---------------+
+ | True | true |
+ +-------------------+---------------+
+ | False | false |
+ +-------------------+---------------+
+ | None | null |
+ +-------------------+---------------+
+
+ To extend this to recognize other objects, subclass and implement a
+ ``.default()`` method with another method that returns a serializable
+ object for ``o`` if possible, otherwise it should call the superclass
+ implementation (to raise ``TypeError``).
+
+ """
+ item_separator = ', '
+ key_separator = ': '
+ def __init__(self, skipkeys=False, ensure_ascii=True,
+ check_circular=True, allow_nan=True, sort_keys=False,
+ indent=None, separators=None, encoding='utf-8', default=None):
+ """Constructor for JSONEncoder, with sensible defaults.
+
+ If skipkeys is false, then it is a TypeError to attempt
+ encoding of keys that are not str, int, long, float or None. If
+ skipkeys is True, such items are simply skipped.
+
+ If ensure_ascii is true, the output is guaranteed to be str
+ objects with all incoming unicode characters escaped. If
+ ensure_ascii is false, the output will be unicode object.
+
+ If check_circular is true, then lists, dicts, and custom encoded
+ objects will be checked for circular references during encoding to
+ prevent an infinite recursion (which would cause an OverflowError).
+ Otherwise, no such check takes place.
+
+ If allow_nan is true, then NaN, Infinity, and -Infinity will be
+ encoded as such. This behavior is not JSON specification compliant,
+ but is consistent with most JavaScript based encoders and decoders.
+ Otherwise, it will be a ValueError to encode such floats.
+
+ If sort_keys is true, then the output of dictionaries will be
+ sorted by key; this is useful for regression tests to ensure
+ that JSON serializations can be compared on a day-to-day basis.
+
+ If indent is a non-negative integer, then JSON array
+ elements and object members will be pretty-printed with that
+ indent level. An indent level of 0 will only insert newlines.
+ None is the most compact representation.
+
+ If specified, separators should be a (item_separator, key_separator)
+ tuple. The default is (', ', ': '). To get the most compact JSON
+ representation you should specify (',', ':') to eliminate whitespace.
+
+ If specified, default is a function that gets called for objects
+ that can't otherwise be serialized. It should return a JSON encodable
+ version of the object or raise a ``TypeError``.
+
+ If encoding is not None, then all input strings will be
+ transformed into unicode using that encoding prior to JSON-encoding.
+ The default is UTF-8.
+
+ """
+
+ self.skipkeys = skipkeys
+ self.ensure_ascii = ensure_ascii
+ self.check_circular = check_circular
+ self.allow_nan = allow_nan
+ self.sort_keys = sort_keys
+ self.indent = indent
+ if separators is not None:
+ self.item_separator, self.key_separator = separators
+ if default is not None:
+ self.default = default
+ self.encoding = encoding
+
+ def default(self, o):
+ """Implement this method in a subclass such that it returns
+ a serializable object for ``o``, or calls the base implementation
+ (to raise a ``TypeError``).
+
+ For example, to support arbitrary iterators, you could
+ implement default like this::
+
+ def default(self, o):
+ try:
+ iterable = iter(o)
+ except TypeError:
+ pass
+ else:
+ return list(iterable)
+ return JSONEncoder.default(self, o)
+
+ """
+ raise TypeError(repr(o) + " is not JSON serializable")
+
+ def encode(self, o):
+ """Return a JSON string representation of a Python data structure.
+
+ >>> JSONEncoder().encode({"foo": ["bar", "baz"]})
+ '{"foo": ["bar", "baz"]}'
+
+ """
+ # This is for extremely simple cases and benchmarks.
+ if isinstance(o, basestring):
+ if isinstance(o, str):
+ _encoding = self.encoding
+ if (_encoding is not None
+ and not (_encoding == 'utf-8')):
+ o = o.decode(_encoding)
+ if self.ensure_ascii:
+ return encode_basestring_ascii(o)
+ else:
+ return encode_basestring(o)
+ # This doesn't pass the iterator directly to ''.join() because the
+ # exceptions aren't as detailed. The list call should be roughly
+ # equivalent to the PySequence_Fast that ''.join() would do.
+ chunks = self.iterencode(o, _one_shot=True)
+ if not isinstance(chunks, (list, tuple)):
+ chunks = list(chunks)
+ return ''.join(chunks)
+
+ def iterencode(self, o, _one_shot=False):
+ """Encode the given object and yield each string
+ representation as available.
+
+ For example::
+
+ for chunk in JSONEncoder().iterencode(bigobject):
+ mysocket.write(chunk)
+
+ """
+ if self.check_circular:
+ markers = {}
+ else:
+ markers = None
+ if self.ensure_ascii:
+ _encoder = encode_basestring_ascii
+ else:
+ _encoder = encode_basestring
+ if self.encoding != 'utf-8':
+ def _encoder(o, _orig_encoder=_encoder, _encoding=self.encoding):
+ if isinstance(o, str):
+ o = o.decode(_encoding)
+ return _orig_encoder(o)
+
+ def floatstr(o, allow_nan=self.allow_nan, _repr=FLOAT_REPR, _inf=INFINITY, _neginf=-INFINITY):
+ # Check for specials. Note that this type of test is processor- and/or
+ # platform-specific, so do tests which don't depend on the internals.
+
+ if o != o:
+ text = 'NaN'
+ elif o == _inf:
+ text = 'Infinity'
+ elif o == _neginf:
+ text = '-Infinity'
+ else:
+ return _repr(o)
+
+ if not allow_nan:
+ raise ValueError(
+ "Out of range float values are not JSON compliant: " +
+ repr(o))
+
+ return text
+
+
+ if _one_shot and c_make_encoder is not None and not self.indent and not self.sort_keys:
+ _iterencode = c_make_encoder(
+ markers, self.default, _encoder, self.indent,
+ self.key_separator, self.item_separator, self.sort_keys,
+ self.skipkeys, self.allow_nan)
+ else:
+ _iterencode = _make_iterencode(
+ markers, self.default, _encoder, self.indent, floatstr,
+ self.key_separator, self.item_separator, self.sort_keys,
+ self.skipkeys, _one_shot)
+ return _iterencode(o, 0)
+
+def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot,
+ ## HACK: hand-optimized bytecode; turn globals into locals
+ False=False,
+ True=True,
+ ValueError=ValueError,
+ basestring=basestring,
+ dict=dict,
+ float=float,
+ id=id,
+ int=int,
+ isinstance=isinstance,
+ list=list,
+ long=long,
+ str=str,
+ tuple=tuple,
+ ):
+
+ def _iterencode_list(lst, _current_indent_level):
+ if not lst:
+ yield '[]'
+ return
+ if markers is not None:
+ markerid = id(lst)
+ if markerid in markers:
+ raise ValueError("Circular reference detected")
+ markers[markerid] = lst
+ buf = '['
+ if _indent is not None:
+ _current_indent_level += 1
+ newline_indent = '\n' + (' ' * (_indent * _current_indent_level))
+ separator = _item_separator + newline_indent
+ buf += newline_indent
+ else:
+ newline_indent = None
+ separator = _item_separator
+ first = True
+ for value in lst:
+ if first:
+ first = False
+ else:
+ buf = separator
+ if isinstance(value, basestring):
+ yield buf + _encoder(value)
+ elif value is None:
+ yield buf + 'null'
+ elif value is True:
+ yield buf + 'true'
+ elif value is False:
+ yield buf + 'false'
+ elif isinstance(value, (int, long)):
+ yield buf + str(value)
+ elif isinstance(value, float):
+ yield buf + _floatstr(value)
+ else:
+ yield buf
+ if isinstance(value, (list, tuple)):
+ chunks = _iterencode_list(value, _current_indent_level)
+ elif isinstance(value, dict):
+ chunks = _iterencode_dict(value, _current_indent_level)
+ else:
+ chunks = _iterencode(value, _current_indent_level)
+ for chunk in chunks:
+ yield chunk
+ if newline_indent is not None:
+ _current_indent_level -= 1
+ yield '\n' + (' ' * (_indent * _current_indent_level))
+ yield ']'
+ if markers is not None:
+ del markers[markerid]
+
+ def _iterencode_dict(dct, _current_indent_level):
+ if not dct:
+ yield '{}'
+ return
+ if markers is not None:
+ markerid = id(dct)
+ if markerid in markers:
+ raise ValueError("Circular reference detected")
+ markers[markerid] = dct
+ yield '{'
+ if _indent is not None:
+ _current_indent_level += 1
+ newline_indent = '\n' + (' ' * (_indent * _current_indent_level))
+ item_separator = _item_separator + newline_indent
+ yield newline_indent
+ else:
+ newline_indent = None
+ item_separator = _item_separator
+ first = True
+ if _sort_keys:
+ items = dct.items()
+ items.sort(key=lambda kv: kv[0])
+ else:
+ items = dct.iteritems()
+ for key, value in items:
+ if isinstance(key, basestring):
+ pass
+ # JavaScript is weakly typed for these, so it makes sense to
+ # also allow them. Many encoders seem to do something like this.
+ elif isinstance(key, float):
+ key = _floatstr(key)
+ elif key is True:
+ key = 'true'
+ elif key is False:
+ key = 'false'
+ elif key is None:
+ key = 'null'
+ elif isinstance(key, (int, long)):
+ key = str(key)
+ elif _skipkeys:
+ continue
+ else:
+ raise TypeError("key " + repr(key) + " is not a string")
+ if first:
+ first = False
+ else:
+ yield item_separator
+ yield _encoder(key)
+ yield _key_separator
+ if isinstance(value, basestring):
+ yield _encoder(value)
+ elif value is None:
+ yield 'null'
+ elif value is True:
+ yield 'true'
+ elif value is False:
+ yield 'false'
+ elif isinstance(value, (int, long)):
+ yield str(value)
+ elif isinstance(value, float):
+ yield _floatstr(value)
+ else:
+ if isinstance(value, (list, tuple)):
+ chunks = _iterencode_list(value, _current_indent_level)
+ elif isinstance(value, dict):
+ chunks = _iterencode_dict(value, _current_indent_level)
+ else:
+ chunks = _iterencode(value, _current_indent_level)
+ for chunk in chunks:
+ yield chunk
+ if newline_indent is not None:
+ _current_indent_level -= 1
+ yield '\n' + (' ' * (_indent * _current_indent_level))
+ yield '}'
+ if markers is not None:
+ del markers[markerid]
+
+ def _iterencode(o, _current_indent_level):
+ if isinstance(o, basestring):
+ yield _encoder(o)
+ elif o is None:
+ yield 'null'
+ elif o is True:
+ yield 'true'
+ elif o is False:
+ yield 'false'
+ elif isinstance(o, (int, long)):
+ yield str(o)
+ elif isinstance(o, float):
+ yield _floatstr(o)
+ elif isinstance(o, (list, tuple)):
+ for chunk in _iterencode_list(o, _current_indent_level):
+ yield chunk
+ elif isinstance(o, dict):
+ for chunk in _iterencode_dict(o, _current_indent_level):
+ yield chunk
+ else:
+ if markers is not None:
+ markerid = id(o)
+ if markerid in markers:
+ raise ValueError("Circular reference detected")
+ markers[markerid] = o
+ o = _default(o)
+ for chunk in _iterencode(o, _current_indent_level):
+ yield chunk
+ if markers is not None:
+ del markers[markerid]
+
+ return _iterencode
--- /dev/null
+"""JSON token scanner
+"""
+import re
+try:
+ from simplejson._speedups import make_scanner as c_make_scanner
+except ImportError:
+ c_make_scanner = None
+
+__all__ = ['make_scanner']
+
+NUMBER_RE = re.compile(
+ r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?',
+ (re.VERBOSE | re.MULTILINE | re.DOTALL))
+
+def py_make_scanner(context):
+ parse_object = context.parse_object
+ parse_array = context.parse_array
+ parse_string = context.parse_string
+ match_number = NUMBER_RE.match
+ encoding = context.encoding
+ strict = context.strict
+ parse_float = context.parse_float
+ parse_int = context.parse_int
+ parse_constant = context.parse_constant
+ object_hook = context.object_hook
+
+ def _scan_once(string, idx):
+ try:
+ nextchar = string[idx]
+ except IndexError:
+ raise StopIteration
+
+ if nextchar == '"':
+ return parse_string(string, idx + 1, encoding, strict)
+ elif nextchar == '{':
+ return parse_object((string, idx + 1), encoding, strict, _scan_once, object_hook)
+ elif nextchar == '[':
+ return parse_array((string, idx + 1), _scan_once)
+ elif nextchar == 'n' and string[idx:idx + 4] == 'null':
+ return None, idx + 4
+ elif nextchar == 't' and string[idx:idx + 4] == 'true':
+ return True, idx + 4
+ elif nextchar == 'f' and string[idx:idx + 5] == 'false':
+ return False, idx + 5
+
+ m = match_number(string, idx)
+ if m is not None:
+ integer, frac, exp = m.groups()
+ if frac or exp:
+ res = parse_float(integer + (frac or '') + (exp or ''))
+ else:
+ res = parse_int(integer)
+ return res, m.end()
+ elif nextchar == 'N' and string[idx:idx + 3] == 'NaN':
+ return parse_constant('NaN'), idx + 3
+ elif nextchar == 'I' and string[idx:idx + 8] == 'Infinity':
+ return parse_constant('Infinity'), idx + 8
+ elif nextchar == '-' and string[idx:idx + 9] == '-Infinity':
+ return parse_constant('-Infinity'), idx + 9
+ else:
+ raise StopIteration
+
+ return _scan_once
+
+make_scanner = c_make_scanner or py_make_scanner
--- /dev/null
+import unittest
+import doctest
+
+def additional_tests():
+ import simplejson
+ import simplejson.encoder
+ import simplejson.decoder
+ suite = unittest.TestSuite()
+ for mod in (simplejson, simplejson.encoder, simplejson.decoder):
+ suite.addTest(doctest.DocTestSuite(mod))
+ suite.addTest(doctest.DocFileSuite('../../index.rst'))
+ return suite
+
+def main():
+ suite = additional_tests()
+ runner = unittest.TextTestRunner()
+ runner.run(suite)
+
+if __name__ == '__main__':
+ import os
+ import sys
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
+ main()
--- /dev/null
+from unittest import TestCase
+import simplejson as json
+
+def default_iterable(obj):
+ return list(obj)
+
+class TestCheckCircular(TestCase):
+ def test_circular_dict(self):
+ dct = {}
+ dct['a'] = dct
+ self.assertRaises(ValueError, json.dumps, dct)
+
+ def test_circular_list(self):
+ lst = []
+ lst.append(lst)
+ self.assertRaises(ValueError, json.dumps, lst)
+
+ def test_circular_composite(self):
+ dct2 = {}
+ dct2['a'] = []
+ dct2['a'].append(dct2)
+ self.assertRaises(ValueError, json.dumps, dct2)
+
+ def test_circular_default(self):
+ json.dumps([set()], default=default_iterable)
+ self.assertRaises(TypeError, json.dumps, [set()])
+
+ def test_circular_off_default(self):
+ json.dumps([set()], default=default_iterable, check_circular=False)
+ self.assertRaises(TypeError, json.dumps, [set()], check_circular=False)
--- /dev/null
+import decimal
+from unittest import TestCase
+
+import simplejson as json
+
+class TestDecode(TestCase):
+ def test_decimal(self):
+ rval = json.loads('1.1', parse_float=decimal.Decimal)
+ self.assert_(isinstance(rval, decimal.Decimal))
+ self.assertEquals(rval, decimal.Decimal('1.1'))
+
+ def test_float(self):
+ rval = json.loads('1', parse_int=float)
+ self.assert_(isinstance(rval, float))
+ self.assertEquals(rval, 1.0)
+
+ def test_decoder_optimizations(self):
+ # Several optimizations were made that skip over calls to
+ # the whitespace regex, so this test is designed to try and
+ # exercise the uncommon cases. The array cases are already covered.
+ rval = json.loads('{ "key" : "value" , "k":"v" }')
+ self.assertEquals(rval, {"key":"value", "k":"v"})
--- /dev/null
+from unittest import TestCase
+
+import simplejson as json
+
+class TestDefault(TestCase):
+ def test_default(self):
+ self.assertEquals(
+ json.dumps(type, default=repr),
+ json.dumps(repr(type)))
--- /dev/null
+from unittest import TestCase
+from cStringIO import StringIO
+
+import simplejson as json
+
+class TestDump(TestCase):
+ def test_dump(self):
+ sio = StringIO()
+ json.dump({}, sio)
+ self.assertEquals(sio.getvalue(), '{}')
+
+ def test_dumps(self):
+ self.assertEquals(json.dumps({}), '{}')
+
+ def test_encode_truefalse(self):
+ self.assertEquals(json.dumps(
+ {True: False, False: True}, sort_keys=True),
+ '{"false": true, "true": false}')
+ self.assertEquals(json.dumps(
+ {2: 3.0, 4.0: 5L, False: 1, 6L: True, "7": 0}, sort_keys=True),
+ '{"false": 1, "2": 3.0, "4.0": 5, "6": true, "7": 0}')
--- /dev/null
+from unittest import TestCase
+
+import simplejson.encoder
+
+CASES = [
+ (u'/\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\x08\x0c\n\r\t`1~!@#$%^&*()_+-=[]{}|;:\',./<>?', '"/\\\\\\"\\ucafe\\ubabe\\uab98\\ufcde\\ubcda\\uef4a\\b\\f\\n\\r\\t`1~!@#$%^&*()_+-=[]{}|;:\',./<>?"'),
+ (u'\u0123\u4567\u89ab\ucdef\uabcd\uef4a', '"\\u0123\\u4567\\u89ab\\ucdef\\uabcd\\uef4a"'),
+ (u'controls', '"controls"'),
+ (u'\x08\x0c\n\r\t', '"\\b\\f\\n\\r\\t"'),
+ (u'{"object with 1 member":["array with 1 element"]}', '"{\\"object with 1 member\\":[\\"array with 1 element\\"]}"'),
+ (u' s p a c e d ', '" s p a c e d "'),
+ (u'\U0001d120', '"\\ud834\\udd20"'),
+ (u'\u03b1\u03a9', '"\\u03b1\\u03a9"'),
+ ('\xce\xb1\xce\xa9', '"\\u03b1\\u03a9"'),
+ (u'\u03b1\u03a9', '"\\u03b1\\u03a9"'),
+ ('\xce\xb1\xce\xa9', '"\\u03b1\\u03a9"'),
+ (u'\u03b1\u03a9', '"\\u03b1\\u03a9"'),
+ (u'\u03b1\u03a9', '"\\u03b1\\u03a9"'),
+ (u"`1~!@#$%^&*()_+-={':[,]}|;.</>?", '"`1~!@#$%^&*()_+-={\':[,]}|;.</>?"'),
+ (u'\x08\x0c\n\r\t', '"\\b\\f\\n\\r\\t"'),
+ (u'\u0123\u4567\u89ab\ucdef\uabcd\uef4a', '"\\u0123\\u4567\\u89ab\\ucdef\\uabcd\\uef4a"'),
+]
+
+class TestEncodeBaseStringAscii(TestCase):
+ def test_py_encode_basestring_ascii(self):
+ self._test_encode_basestring_ascii(simplejson.encoder.py_encode_basestring_ascii)
+
+ def test_c_encode_basestring_ascii(self):
+ if not simplejson.encoder.c_encode_basestring_ascii:
+ return
+ self._test_encode_basestring_ascii(simplejson.encoder.c_encode_basestring_ascii)
+
+ def _test_encode_basestring_ascii(self, encode_basestring_ascii):
+ fname = encode_basestring_ascii.__name__
+ for input_string, expect in CASES:
+ result = encode_basestring_ascii(input_string)
+ self.assertEquals(result, expect,
+ '%r != %r for %s(%r)' % (result, expect, fname, input_string))
--- /dev/null
+from unittest import TestCase
+
+import simplejson as json
+
+# Fri Dec 30 18:57:26 2005
+JSONDOCS = [
+ # http://json.org/JSON_checker/test/fail1.json
+ '"A JSON payload should be an object or array, not a string."',
+ # http://json.org/JSON_checker/test/fail2.json
+ '["Unclosed array"',
+ # http://json.org/JSON_checker/test/fail3.json
+ '{unquoted_key: "keys must be quoted}',
+ # http://json.org/JSON_checker/test/fail4.json
+ '["extra comma",]',
+ # http://json.org/JSON_checker/test/fail5.json
+ '["double extra comma",,]',
+ # http://json.org/JSON_checker/test/fail6.json
+ '[ , "<-- missing value"]',
+ # http://json.org/JSON_checker/test/fail7.json
+ '["Comma after the close"],',
+ # http://json.org/JSON_checker/test/fail8.json
+ '["Extra close"]]',
+ # http://json.org/JSON_checker/test/fail9.json
+ '{"Extra comma": true,}',
+ # http://json.org/JSON_checker/test/fail10.json
+ '{"Extra value after close": true} "misplaced quoted value"',
+ # http://json.org/JSON_checker/test/fail11.json
+ '{"Illegal expression": 1 + 2}',
+ # http://json.org/JSON_checker/test/fail12.json
+ '{"Illegal invocation": alert()}',
+ # http://json.org/JSON_checker/test/fail13.json
+ '{"Numbers cannot have leading zeroes": 013}',
+ # http://json.org/JSON_checker/test/fail14.json
+ '{"Numbers cannot be hex": 0x14}',
+ # http://json.org/JSON_checker/test/fail15.json
+ '["Illegal backslash escape: \\x15"]',
+ # http://json.org/JSON_checker/test/fail16.json
+ '["Illegal backslash escape: \\\'"]',
+ # http://json.org/JSON_checker/test/fail17.json
+ '["Illegal backslash escape: \\017"]',
+ # http://json.org/JSON_checker/test/fail18.json
+ '[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]',
+ # http://json.org/JSON_checker/test/fail19.json
+ '{"Missing colon" null}',
+ # http://json.org/JSON_checker/test/fail20.json
+ '{"Double colon":: null}',
+ # http://json.org/JSON_checker/test/fail21.json
+ '{"Comma instead of colon", null}',
+ # http://json.org/JSON_checker/test/fail22.json
+ '["Colon instead of comma": false]',
+ # http://json.org/JSON_checker/test/fail23.json
+ '["Bad value", truth]',
+ # http://json.org/JSON_checker/test/fail24.json
+ "['single quote']",
+ # http://code.google.com/p/simplejson/issues/detail?id=3
+ u'["A\u001FZ control characters in string"]',
+]
+
+SKIPS = {
+ 1: "why not have a string payload?",
+ 18: "spec doesn't specify any nesting limitations",
+}
+
+class TestFail(TestCase):
+ def test_failures(self):
+ for idx, doc in enumerate(JSONDOCS):
+ idx = idx + 1
+ if idx in SKIPS:
+ json.loads(doc)
+ continue
+ try:
+ json.loads(doc)
+ except ValueError:
+ pass
+ else:
+ self.fail("Expected failure for fail%d.json: %r" % (idx, doc))
--- /dev/null
+import math
+from unittest import TestCase
+
+import simplejson as json
+
+class TestFloat(TestCase):
+ def test_floats(self):
+ for num in [1617161771.7650001, math.pi, math.pi**100, math.pi**-100, 3.1]:
+ self.assertEquals(float(json.dumps(num)), num)
+ self.assertEquals(json.loads(json.dumps(num)), num)
+
+ def test_ints(self):
+ for num in [1, 1L, 1<<32, 1<<64]:
+ self.assertEquals(json.dumps(num), str(num))
+ self.assertEquals(int(json.dumps(num)), num)
--- /dev/null
+from unittest import TestCase
+
+import simplejson as json
+import textwrap
+
+class TestIndent(TestCase):
+ def test_indent(self):
+ h = [['blorpie'], ['whoops'], [], 'd-shtaeou', 'd-nthiouh', 'i-vhbjkhnth',
+ {'nifty': 87}, {'field': 'yes', 'morefield': False} ]
+
+ expect = textwrap.dedent("""\
+ [
+ [
+ "blorpie"
+ ],
+ [
+ "whoops"
+ ],
+ [],
+ "d-shtaeou",
+ "d-nthiouh",
+ "i-vhbjkhnth",
+ {
+ "nifty": 87
+ },
+ {
+ "field": "yes",
+ "morefield": false
+ }
+ ]""")
+
+
+ d1 = json.dumps(h)
+ d2 = json.dumps(h, indent=2, sort_keys=True, separators=(',', ': '))
+
+ h1 = json.loads(d1)
+ h2 = json.loads(d2)
+
+ self.assertEquals(h1, h)
+ self.assertEquals(h2, h)
+ self.assertEquals(d2, expect)
--- /dev/null
+from unittest import TestCase
+
+import simplejson as json
+
+# from http://json.org/JSON_checker/test/pass1.json
+JSON = r'''
+[
+ "JSON Test Pattern pass1",
+ {"object with 1 member":["array with 1 element"]},
+ {},
+ [],
+ -42,
+ true,
+ false,
+ null,
+ {
+ "integer": 1234567890,
+ "real": -9876.543210,
+ "e": 0.123456789e-12,
+ "E": 1.234567890E+34,
+ "": 23456789012E666,
+ "zero": 0,
+ "one": 1,
+ "space": " ",
+ "quote": "\"",
+ "backslash": "\\",
+ "controls": "\b\f\n\r\t",
+ "slash": "/ & \/",
+ "alpha": "abcdefghijklmnopqrstuvwyz",
+ "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
+ "digit": "0123456789",
+ "special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
+ "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
+ "true": true,
+ "false": false,
+ "null": null,
+ "array":[ ],
+ "object":{ },
+ "address": "50 St. James Street",
+ "url": "http://www.JSON.org/",
+ "comment": "// /* <!-- --",
+ "# -- --> */": " ",
+ " s p a c e d " :[1,2 , 3
+
+,
+
+4 , 5 , 6 ,7 ],
+ "compact": [1,2,3,4,5,6,7],
+ "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
+ "quotes": "" \u0022 %22 0x22 034 "",
+ "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
+: "A key can be any string"
+ },
+ 0.5 ,98.6
+,
+99.44
+,
+
+1066
+
+
+,"rosebud"]
+'''
+
+class TestPass1(TestCase):
+ def test_parse(self):
+ # test in/out equivalence and parsing
+ res = json.loads(JSON)
+ out = json.dumps(res)
+ self.assertEquals(res, json.loads(out))
+ try:
+ json.dumps(res, allow_nan=False)
+ except ValueError:
+ pass
+ else:
+ self.fail("23456789012E666 should be out of range")
--- /dev/null
+from unittest import TestCase
+import simplejson as json
+
+# from http://json.org/JSON_checker/test/pass2.json
+JSON = r'''
+[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]]
+'''
+
+class TestPass2(TestCase):
+ def test_parse(self):
+ # test in/out equivalence and parsing
+ res = json.loads(JSON)
+ out = json.dumps(res)
+ self.assertEquals(res, json.loads(out))
--- /dev/null
+from unittest import TestCase
+
+import simplejson as json
+
+# from http://json.org/JSON_checker/test/pass3.json
+JSON = r'''
+{
+ "JSON Test Pattern pass3": {
+ "The outermost value": "must be an object or array.",
+ "In this test": "It is an object."
+ }
+}
+'''
+
+class TestPass3(TestCase):
+ def test_parse(self):
+ # test in/out equivalence and parsing
+ res = json.loads(JSON)
+ out = json.dumps(res)
+ self.assertEquals(res, json.loads(out))
--- /dev/null
+from unittest import TestCase
+
+import simplejson as json
+
+class JSONTestObject:
+ pass
+
+
+class RecursiveJSONEncoder(json.JSONEncoder):
+ recurse = False
+ def default(self, o):
+ if o is JSONTestObject:
+ if self.recurse:
+ return [JSONTestObject]
+ else:
+ return 'JSONTestObject'
+ return json.JSONEncoder.default(o)
+
+
+class TestRecursion(TestCase):
+ def test_listrecursion(self):
+ x = []
+ x.append(x)
+ try:
+ json.dumps(x)
+ except ValueError:
+ pass
+ else:
+ self.fail("didn't raise ValueError on list recursion")
+ x = []
+ y = [x]
+ x.append(y)
+ try:
+ json.dumps(x)
+ except ValueError:
+ pass
+ else:
+ self.fail("didn't raise ValueError on alternating list recursion")
+ y = []
+ x = [y, y]
+ # ensure that the marker is cleared
+ json.dumps(x)
+
+ def test_dictrecursion(self):
+ x = {}
+ x["test"] = x
+ try:
+ json.dumps(x)
+ except ValueError:
+ pass
+ else:
+ self.fail("didn't raise ValueError on dict recursion")
+ x = {}
+ y = {"a": x, "b": x}
+ # ensure that the marker is cleared
+ json.dumps(x)
+
+ def test_defaultrecursion(self):
+ enc = RecursiveJSONEncoder()
+ self.assertEquals(enc.encode(JSONTestObject), '"JSONTestObject"')
+ enc.recurse = True
+ try:
+ enc.encode(JSONTestObject)
+ except ValueError:
+ pass
+ else:
+ self.fail("didn't raise ValueError on default recursion")
--- /dev/null
+import sys
+import decimal
+from unittest import TestCase
+
+import simplejson as json
+import simplejson.decoder
+
+class TestScanString(TestCase):
+ def test_py_scanstring(self):
+ self._test_scanstring(simplejson.decoder.py_scanstring)
+
+ def test_c_scanstring(self):
+ if not simplejson.decoder.c_scanstring:
+ return
+ self._test_scanstring(simplejson.decoder.c_scanstring)
+
+ def _test_scanstring(self, scanstring):
+ self.assertEquals(
+ scanstring('"z\\ud834\\udd20x"', 1, None, True),
+ (u'z\U0001d120x', 16))
+
+ if sys.maxunicode == 65535:
+ self.assertEquals(
+ scanstring(u'"z\U0001d120x"', 1, None, True),
+ (u'z\U0001d120x', 6))
+ else:
+ self.assertEquals(
+ scanstring(u'"z\U0001d120x"', 1, None, True),
+ (u'z\U0001d120x', 5))
+
+ self.assertEquals(
+ scanstring('"\\u007b"', 1, None, True),
+ (u'{', 8))
+
+ self.assertEquals(
+ scanstring('"A JSON payload should be an object or array, not a string."', 1, None, True),
+ (u'A JSON payload should be an object or array, not a string.', 60))
+
+ self.assertEquals(
+ scanstring('["Unclosed array"', 2, None, True),
+ (u'Unclosed array', 17))
+
+ self.assertEquals(
+ scanstring('["extra comma",]', 2, None, True),
+ (u'extra comma', 14))
+
+ self.assertEquals(
+ scanstring('["double extra comma",,]', 2, None, True),
+ (u'double extra comma', 21))
+
+ self.assertEquals(
+ scanstring('["Comma after the close"],', 2, None, True),
+ (u'Comma after the close', 24))
+
+ self.assertEquals(
+ scanstring('["Extra close"]]', 2, None, True),
+ (u'Extra close', 14))
+
+ self.assertEquals(
+ scanstring('{"Extra comma": true,}', 2, None, True),
+ (u'Extra comma', 14))
+
+ self.assertEquals(
+ scanstring('{"Extra value after close": true} "misplaced quoted value"', 2, None, True),
+ (u'Extra value after close', 26))
+
+ self.assertEquals(
+ scanstring('{"Illegal expression": 1 + 2}', 2, None, True),
+ (u'Illegal expression', 21))
+
+ self.assertEquals(
+ scanstring('{"Illegal invocation": alert()}', 2, None, True),
+ (u'Illegal invocation', 21))
+
+ self.assertEquals(
+ scanstring('{"Numbers cannot have leading zeroes": 013}', 2, None, True),
+ (u'Numbers cannot have leading zeroes', 37))
+
+ self.assertEquals(
+ scanstring('{"Numbers cannot be hex": 0x14}', 2, None, True),
+ (u'Numbers cannot be hex', 24))
+
+ self.assertEquals(
+ scanstring('[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]', 21, None, True),
+ (u'Too deep', 30))
+
+ self.assertEquals(
+ scanstring('{"Missing colon" null}', 2, None, True),
+ (u'Missing colon', 16))
+
+ self.assertEquals(
+ scanstring('{"Double colon":: null}', 2, None, True),
+ (u'Double colon', 15))
+
+ self.assertEquals(
+ scanstring('{"Comma instead of colon", null}', 2, None, True),
+ (u'Comma instead of colon', 25))
+
+ self.assertEquals(
+ scanstring('["Colon instead of comma": false]', 2, None, True),
+ (u'Colon instead of comma', 25))
+
+ self.assertEquals(
+ scanstring('["Bad value", truth]', 2, None, True),
+ (u'Bad value', 12))
+
+ def test_issue3623(self):
+ self.assertRaises(ValueError, json.decoder.scanstring, "xxx", 1,
+ "xxx")
+ self.assertRaises(UnicodeDecodeError,
+ json.encoder.encode_basestring_ascii, "xx\xff")
--- /dev/null
+import textwrap
+from unittest import TestCase
+
+import simplejson as json
+
+
+class TestSeparators(TestCase):
+ def test_separators(self):
+ h = [['blorpie'], ['whoops'], [], 'd-shtaeou', 'd-nthiouh', 'i-vhbjkhnth',
+ {'nifty': 87}, {'field': 'yes', 'morefield': False} ]
+
+ expect = textwrap.dedent("""\
+ [
+ [
+ "blorpie"
+ ] ,
+ [
+ "whoops"
+ ] ,
+ [] ,
+ "d-shtaeou" ,
+ "d-nthiouh" ,
+ "i-vhbjkhnth" ,
+ {
+ "nifty" : 87
+ } ,
+ {
+ "field" : "yes" ,
+ "morefield" : false
+ }
+ ]""")
+
+
+ d1 = json.dumps(h)
+ d2 = json.dumps(h, indent=2, sort_keys=True, separators=(' ,', ' : '))
+
+ h1 = json.loads(d1)
+ h2 = json.loads(d2)
+
+ self.assertEquals(h1, h)
+ self.assertEquals(h2, h)
+ self.assertEquals(d2, expect)
--- /dev/null
+from unittest import TestCase
+
+import simplejson as json
+
+class TestUnicode(TestCase):
+ def test_encoding1(self):
+ encoder = json.JSONEncoder(encoding='utf-8')
+ u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}'
+ s = u.encode('utf-8')
+ ju = encoder.encode(u)
+ js = encoder.encode(s)
+ self.assertEquals(ju, js)
+
+ def test_encoding2(self):
+ u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}'
+ s = u.encode('utf-8')
+ ju = json.dumps(u, encoding='utf-8')
+ js = json.dumps(s, encoding='utf-8')
+ self.assertEquals(ju, js)
+
+ def test_encoding3(self):
+ u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}'
+ j = json.dumps(u)
+ self.assertEquals(j, '"\\u03b1\\u03a9"')
+
+ def test_encoding4(self):
+ u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}'
+ j = json.dumps([u])
+ self.assertEquals(j, '["\\u03b1\\u03a9"]')
+
+ def test_encoding5(self):
+ u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}'
+ j = json.dumps(u, ensure_ascii=False)
+ self.assertEquals(j, u'"%s"' % (u,))
+
+ def test_encoding6(self):
+ u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}'
+ j = json.dumps([u], ensure_ascii=False)
+ self.assertEquals(j, u'["%s"]' % (u,))
+
+ def test_big_unicode_encode(self):
+ u = u'\U0001d120'
+ self.assertEquals(json.dumps(u), '"\\ud834\\udd20"')
+ self.assertEquals(json.dumps(u, ensure_ascii=False), u'"\U0001d120"')
+
+ def test_big_unicode_decode(self):
+ u = u'z\U0001d120x'
+ self.assertEquals(json.loads('"' + u + '"'), u)
+ self.assertEquals(json.loads('"z\\ud834\\udd20x"'), u)
+
+ def test_unicode_decode(self):
+ for i in range(0, 0xd7ff):
+ u = unichr(i)
+ s = '"\\u%04x"' % (i,)
+ self.assertEquals(json.loads(s), u)
+
+ def test_default_encoding(self):
+ self.assertEquals(json.loads(u'{"a": "\xe9"}'.encode('utf-8')),
+ {'a': u'\xe9'})
+
+ def test_unicode_preservation(self):
+ self.assertEquals(type(json.loads(u'""')), unicode)
+ self.assertEquals(type(json.loads(u'"a"')), unicode)
+ self.assertEquals(type(json.loads(u'["a"]')[0]), unicode)
\ No newline at end of file
--- /dev/null
+r"""Command-line tool to validate and pretty-print JSON
+
+Usage::
+
+ $ echo '{"json":"obj"}' | python -m simplejson.tool
+ {
+ "json": "obj"
+ }
+ $ echo '{ 1.2:3.4}' | python -m simplejson.tool
+ Expecting property name: line 1 column 2 (char 2)
+
+"""
+import sys
+import simplejson
+
+def main():
+ if len(sys.argv) == 1:
+ infile = sys.stdin
+ outfile = sys.stdout
+ elif len(sys.argv) == 2:
+ infile = open(sys.argv[1], 'rb')
+ outfile = sys.stdout
+ elif len(sys.argv) == 3:
+ infile = open(sys.argv[1], 'rb')
+ outfile = open(sys.argv[2], 'wb')
+ else:
+ raise SystemExit(sys.argv[0] + " [infile [outfile]]")
+ try:
+ obj = simplejson.load(infile)
+ except ValueError, e:
+ raise SystemExit(e)
+ simplejson.dump(obj, outfile, sort_keys=True, indent=4)
+ outfile.write('\n')
+
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+/* Copyright (c) 2009 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 "table.h"
+
+#include <assert.h>
+
+#include "json.h"
+#include "column.h"
+#include "ovsdb-error.h"
+#include "ovsdb-parser.h"
+#include "ovsdb-types.h"
+#include "row.h"
+
+static void
+add_column(struct ovsdb_table_schema *ts, struct ovsdb_column *column)
+{
+ assert(!shash_find(&ts->columns, column->name));
+ column->index = shash_count(&ts->columns);
+ shash_add(&ts->columns, column->name, column);
+}
+
+struct ovsdb_table_schema *
+ovsdb_table_schema_create(const char *name, const char *comment, bool mutable)
+{
+ struct ovsdb_column *uuid, *version;
+ struct ovsdb_table_schema *ts;
+
+ ts = xzalloc(sizeof *ts);
+ ts->name = xstrdup(name);
+ ts->comment = comment ? xstrdup(comment) : NULL;
+ ts->mutable = mutable;
+ shash_init(&ts->columns);
+
+ uuid = ovsdb_column_create(
+ "_uuid", "Unique identifier for this row.",
+ false, true, &ovsdb_type_uuid);
+ add_column(ts, uuid);
+ assert(uuid->index == OVSDB_COL_UUID);
+
+ version = ovsdb_column_create(
+ "_version", "Unique identifier for this version of this row.",
+ false, false, &ovsdb_type_uuid);
+ add_column(ts, version);
+ assert(version->index == OVSDB_COL_VERSION);
+
+ return ts;
+}
+
+void
+ovsdb_table_schema_destroy(struct ovsdb_table_schema *ts)
+{
+ struct shash_node *node;
+
+ SHASH_FOR_EACH (node, &ts->columns) {
+ ovsdb_column_destroy(node->data);
+ }
+ shash_destroy(&ts->columns);
+ free(ts->comment);
+ free(ts->name);
+ free(ts);
+}
+
+struct ovsdb_error *
+ovsdb_table_schema_from_json(const struct json *json, const char *name,
+ struct ovsdb_table_schema **tsp)
+{
+ struct ovsdb_table_schema *ts;
+ const struct json *comment, *columns, *mutable;
+ struct shash_node *node;
+ struct ovsdb_parser parser;
+ struct ovsdb_error *error;
+
+ *tsp = NULL;
+
+ ovsdb_parser_init(&parser, json, "table schema for table %s", name);
+ comment = ovsdb_parser_member(&parser, "comment", OP_STRING | OP_OPTIONAL);
+ columns = ovsdb_parser_member(&parser, "columns", OP_OBJECT);
+ mutable = ovsdb_parser_member(&parser, "mutable",
+ OP_TRUE | OP_FALSE | OP_OPTIONAL);
+ error = ovsdb_parser_finish(&parser);
+ if (error) {
+ return error;
+ }
+
+ if (shash_is_empty(json_object(columns))) {
+ return ovsdb_syntax_error(json, NULL,
+ "table must have at least one column");
+ }
+
+ ts = ovsdb_table_schema_create(name,
+ comment ? json_string(comment) : NULL,
+ mutable ? json_boolean(mutable) : true);
+ SHASH_FOR_EACH (node, json_object(columns)) {
+ struct ovsdb_column *column;
+
+ 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_column_from_json(node->data, node->name, &column);
+ }
+ if (error) {
+ ovsdb_table_schema_destroy(ts);
+ return error;
+ }
+
+ add_column(ts, column);
+ }
+ *tsp = ts;
+ return 0;
+}
+
+struct json *
+ovsdb_table_schema_to_json(const struct ovsdb_table_schema *ts)
+{
+ struct json *json, *columns;
+ struct shash_node *node;
+
+ json = json_object_create();
+ if (ts->comment) {
+ json_object_put_string(json, "comment", ts->comment);
+ }
+ if (!ts->mutable) {
+ json_object_put(json, "mutable", json_boolean_create(false));
+ }
+
+ columns = json_object_create();
+
+ SHASH_FOR_EACH (node, &ts->columns) {
+ struct ovsdb_column *column = node->data;
+ if (node->name[0] != '_') {
+ json_object_put(columns, column->name,
+ ovsdb_column_to_json(column));
+ }
+ }
+ json_object_put(json, "columns", columns);
+
+ return json;
+}
+
+const struct ovsdb_column *
+ovsdb_table_schema_get_column(const struct ovsdb_table_schema *ts,
+ const char *name)
+{
+ return shash_find_data(&ts->columns, name);
+}
+\f
+struct ovsdb_table *
+ovsdb_table_create(struct ovsdb_table_schema *ts)
+{
+ struct ovsdb_table *table;
+
+ table = xmalloc(sizeof *table);
+ table->schema = ts;
+ hmap_init(&table->rows);
+
+ return table;
+}
+
+void
+ovsdb_table_destroy(struct ovsdb_table *table)
+{
+ if (table) {
+ struct ovsdb_row *row, *next;
+
+ HMAP_FOR_EACH_SAFE (row, next, struct ovsdb_row, hmap_node,
+ &table->rows) {
+ ovsdb_row_destroy(row);
+ }
+ hmap_destroy(&table->rows);
+
+ ovsdb_table_schema_destroy(table->schema);
+ free(table);
+ }
+}
+
+static const struct ovsdb_row *
+ovsdb_table_get_row__(const struct ovsdb_table *table, const struct uuid *uuid,
+ size_t hash)
+{
+ struct ovsdb_row *row;
+
+ HMAP_FOR_EACH_WITH_HASH (row, struct ovsdb_row, hmap_node, hash,
+ &table->rows) {
+ if (uuid_equals(ovsdb_row_get_uuid(row), uuid)) {
+ return row;
+ }
+ }
+
+ return NULL;
+}
+
+const struct ovsdb_row *
+ovsdb_table_get_row(const struct ovsdb_table *table, const struct uuid *uuid)
+{
+ return ovsdb_table_get_row__(table, uuid, uuid_hash(uuid));
+}
+
+/* This is probably not the function you want. Use ovsdb_txn_row_modify()
+ * instead. */
+bool
+ovsdb_table_put_row(struct ovsdb_table *table, struct ovsdb_row *row)
+{
+ const struct uuid *uuid = ovsdb_row_get_uuid(row);
+ size_t hash = uuid_hash(uuid);
+
+ if (!ovsdb_table_get_row__(table, uuid, hash)) {
+ hmap_insert(&table->rows, &row->hmap_node, hash);
+ return true;
+ } else {
+ return false;
+ }
+}
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef OVSDB_TABLE_H
+#define OVSDB_TABLE_H 1
+
+#include <stdbool.h>
+#include "compiler.h"
+#include "hmap.h"
+#include "shash.h"
+
+struct json;
+struct uuid;
+
+/* Schema for a database table. */
+struct ovsdb_table_schema {
+ char *name;
+ char *comment;
+ bool mutable;
+ struct shash columns; /* Contains "struct ovsdb_column *"s. */
+};
+
+struct ovsdb_table_schema *ovsdb_table_schema_create(const char *name,
+ const char *comment,
+ bool mutable);
+void ovsdb_table_schema_destroy(struct ovsdb_table_schema *);
+
+struct ovsdb_error *ovsdb_table_schema_from_json(const struct json *,
+ const char *name,
+ struct ovsdb_table_schema **)
+ WARN_UNUSED_RESULT;
+struct json *ovsdb_table_schema_to_json(const struct ovsdb_table_schema *);
+
+const struct ovsdb_column *ovsdb_table_schema_get_column(
+ const struct ovsdb_table_schema *, const char *name);
+\f
+/* Database table. */
+
+struct ovsdb_table {
+ struct ovsdb_table_schema *schema;
+ struct hmap rows; /* Contains "struct ovsdb_row"s. */
+};
+
+struct ovsdb_table *ovsdb_table_create(struct ovsdb_table_schema *);
+void ovsdb_table_destroy(struct ovsdb_table *);
+
+const struct ovsdb_row *ovsdb_table_get_row(const struct ovsdb_table *,
+ const struct uuid *);
+bool ovsdb_table_put_row(struct ovsdb_table *, struct ovsdb_row *);
+
+#endif /* ovsdb/table.h */
--- /dev/null
+/* Copyright (c) 2009 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 "transaction.h"
+
+#include <assert.h>
+
+#include "hash.h"
+#include "hmap.h"
+#include "json.h"
+#include "ovsdb-error.h"
+#include "ovsdb.h"
+#include "row.h"
+#include "table.h"
+#include "uuid.h"
+
+struct ovsdb_txn {
+ struct ovsdb *db;
+ struct hmap txn_tables; /* Contains "struct ovsdb_txn_table"s. */
+};
+
+/* A table modified by a transaction. */
+struct ovsdb_txn_table {
+ struct hmap_node hmap_node; /* Element in ovsdb_txn's txn_tables hmap. */
+ struct ovsdb_table *table;
+ struct hmap txn_rows; /* Contains "struct ovsdb_txn_row"s. */
+};
+
+/* A row modified by the transaction:
+ *
+ * - A row added by a transaction will have null 'old' and non-null 'new'.
+ *
+ * - A row deleted by a transaction will have non-null 'old' and null
+ * 'new'.
+ *
+ * - A row modified by a transaction will have non-null 'old' and 'new'.
+ *
+ * - 'old' and 'new' both null is invalid. It would indicate that a row
+ * was added then deleted within a single transaction, but we instead
+ * handle that case by deleting the txn_row entirely.
+ */
+struct ovsdb_txn_row {
+ struct hmap_node hmap_node; /* In ovsdb_txn_table's txn_rows hmap. */
+ struct ovsdb_row *old; /* The old row. */
+ struct ovsdb_row *new; /* The new row. */
+};
+
+struct ovsdb_txn *
+ovsdb_txn_create(struct ovsdb *db)
+{
+ struct ovsdb_txn *txn = xmalloc(sizeof *txn);
+ txn->db = db;
+ hmap_init(&txn->txn_tables);
+ return txn;
+}
+
+static void
+ovsdb_txn_destroy(struct ovsdb_txn *txn, void (*cb)(struct ovsdb_txn_row *))
+{
+ struct ovsdb_txn_table *txn_table, *next_txn_table;
+
+ HMAP_FOR_EACH_SAFE (txn_table, next_txn_table,
+ struct ovsdb_txn_table, hmap_node, &txn->txn_tables)
+ {
+ struct ovsdb_txn_row *txn_row, *next_txn_row;
+
+ HMAP_FOR_EACH_SAFE (txn_row, next_txn_row,
+ struct ovsdb_txn_row, hmap_node,
+ &txn_table->txn_rows)
+ {
+ if (txn_row->new) {
+ txn_row->new->txn_row = NULL;
+ }
+ cb(txn_row);
+ free(txn_row);
+ }
+
+ hmap_destroy(&txn_table->txn_rows);
+ free(txn_table);
+ }
+ hmap_destroy(&txn->txn_tables);
+ free(txn);
+}
+
+static void
+ovsdb_txn_row_abort(struct ovsdb_txn_row *txn_row)
+{
+ struct ovsdb_row *old = txn_row->old;
+ struct ovsdb_row *new = txn_row->new;
+
+ if (!old) {
+ hmap_remove(&new->table->rows, &new->hmap_node);
+ } else if (!new) {
+ hmap_insert(&old->table->rows, &old->hmap_node, ovsdb_row_hash(old));
+ } else {
+ hmap_replace(&new->table->rows, &new->hmap_node, &old->hmap_node);
+ }
+ ovsdb_row_destroy(new);
+}
+
+void
+ovsdb_txn_abort(struct ovsdb_txn *txn)
+{
+ ovsdb_txn_destroy(txn, ovsdb_txn_row_abort);
+}
+
+static void
+ovsdb_txn_row_commit(struct ovsdb_txn_row *txn_row)
+{
+ ovsdb_row_destroy(txn_row->old);
+}
+
+struct ovsdb_error *
+ovsdb_txn_commit(struct ovsdb_txn *txn, bool durable)
+{
+ struct ovsdb_replica *replica;
+ struct ovsdb_error *error;
+
+ LIST_FOR_EACH (replica, struct ovsdb_replica, node, &txn->db->replicas) {
+ error = (replica->class->commit)(replica, txn, durable);
+ if (error) {
+ /* We don't support two-phase commit so only the first replica is
+ * allowed to report an error. */
+ assert(&replica->node == txn->db->replicas.next);
+
+ ovsdb_txn_abort(txn);
+ return error;
+ }
+ }
+
+ txn->db->run_triggers = true;
+ ovsdb_txn_destroy(txn, ovsdb_txn_row_commit);
+ return NULL;
+}
+
+void
+ovsdb_txn_for_each_change(const struct ovsdb_txn *txn,
+ ovsdb_txn_row_cb_func *cb, void *aux)
+{
+ struct ovsdb_txn_table *t;
+ struct ovsdb_txn_row *r;
+
+ HMAP_FOR_EACH (t, struct ovsdb_txn_table, hmap_node, &txn->txn_tables) {
+ HMAP_FOR_EACH (r, struct ovsdb_txn_row, hmap_node, &t->txn_rows) {
+ if (!cb(r->old, r->new, aux)) {
+ break;
+ }
+ }
+ }
+}
+
+static struct ovsdb_txn_table *
+ovsdb_txn_get_txn_table__(struct ovsdb_txn *txn,
+ const struct ovsdb_table *table,
+ uint32_t hash)
+{
+ struct ovsdb_txn_table *txn_table;
+
+ HMAP_FOR_EACH_IN_BUCKET (txn_table, struct ovsdb_txn_table, hmap_node,
+ hash, &txn->txn_tables) {
+ if (txn_table->table == table) {
+ return txn_table;
+ }
+ }
+
+ return NULL;
+}
+
+static struct ovsdb_txn_table *
+ovsdb_txn_get_txn_table(struct ovsdb_txn *txn, const struct ovsdb_table *table)
+{
+ return ovsdb_txn_get_txn_table__(txn, table, hash_pointer(table, 0));
+}
+
+static struct ovsdb_txn_table *
+ovsdb_txn_create_txn_table(struct ovsdb_txn *txn,
+ struct ovsdb_table *table)
+{
+ uint32_t hash = hash_pointer(table, 0);
+ struct ovsdb_txn_table *txn_table;
+
+ txn_table = ovsdb_txn_get_txn_table__(txn, table, hash);
+ if (!txn_table) {
+ txn_table = xmalloc(sizeof *txn_table);
+ txn_table->table = table;
+ hmap_init(&txn_table->txn_rows);
+ hmap_insert(&txn->txn_tables, &txn_table->hmap_node, hash);
+ }
+ return txn_table;
+}
+
+static struct ovsdb_txn_row *
+ovsdb_txn_row_create(struct ovsdb_txn_table *txn_table,
+ const struct ovsdb_row *old, struct ovsdb_row *new)
+{
+ uint32_t hash = ovsdb_row_hash(old ? old : new);
+ struct ovsdb_txn_row *txn_row;
+
+ txn_row = xmalloc(sizeof *txn_row);
+ txn_row->old = (struct ovsdb_row *) old;
+ txn_row->new = new;
+ hmap_insert(&txn_table->txn_rows, &txn_row->hmap_node, hash);
+
+ return txn_row;
+}
+
+struct ovsdb_row *
+ovsdb_txn_row_modify(struct ovsdb_txn *txn, const struct ovsdb_row *ro_row_)
+{
+ struct ovsdb_row *ro_row = (struct ovsdb_row *) ro_row_;
+
+ if (ro_row->txn_row) {
+ assert(ro_row == ro_row->txn_row->new);
+ return ro_row;
+ } else {
+ struct ovsdb_table *table = ro_row->table;
+ struct ovsdb_txn_table *txn_table;
+ struct ovsdb_row *rw_row;
+
+ txn_table = ovsdb_txn_create_txn_table(txn, table);
+ rw_row = ovsdb_row_clone(ro_row);
+ uuid_generate(ovsdb_row_get_version_rw(rw_row));
+ rw_row->txn_row = ovsdb_txn_row_create(txn_table, ro_row, rw_row);
+ hmap_replace(&table->rows, &ro_row->hmap_node, &rw_row->hmap_node);
+
+ return rw_row;
+ }
+}
+
+void
+ovsdb_txn_row_insert(struct ovsdb_txn *txn, struct ovsdb_row *row)
+{
+ uint32_t hash = ovsdb_row_hash(row);
+ struct ovsdb_table *table = row->table;
+ struct ovsdb_txn_table *txn_table;
+
+ uuid_generate(ovsdb_row_get_version_rw(row));
+
+ txn_table = ovsdb_txn_create_txn_table(txn, table);
+ row->txn_row = ovsdb_txn_row_create(txn_table, NULL, row);
+ hmap_insert(&table->rows, &row->hmap_node, hash);
+}
+
+/* 'row' must be assumed destroyed upon return; the caller must not reference
+ * it again. */
+void
+ovsdb_txn_row_delete(struct ovsdb_txn *txn, const struct ovsdb_row *row_)
+{
+ struct ovsdb_row *row = (struct ovsdb_row *) row_;
+ struct ovsdb_table *table = row->table;
+ struct ovsdb_txn_row *txn_row = row->txn_row;
+ struct ovsdb_txn_table *txn_table;
+
+ hmap_remove(&table->rows, &row->hmap_node);
+
+ if (!txn_row) {
+ txn_table = ovsdb_txn_create_txn_table(txn, table);
+ row->txn_row = ovsdb_txn_row_create(txn_table, row, NULL);
+ } else {
+ assert(txn_row->new == row);
+ if (txn_row->old) {
+ txn_row->new = NULL;
+ } else {
+ txn_table = ovsdb_txn_get_txn_table(txn, table);
+ hmap_remove(&txn_table->txn_rows, &txn_row->hmap_node);
+ }
+ ovsdb_row_destroy(row);
+ }
+}
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef OVSDB_TRANSACTION_H
+#define OVSDB_TRANSACTION_H 1
+
+#include <stdbool.h>
+#include "compiler.h"
+
+struct json;
+struct ovsdb;
+struct ovsdb_table;
+struct uuid;
+
+struct ovsdb_txn *ovsdb_txn_create(struct ovsdb *);
+void ovsdb_txn_abort(struct ovsdb_txn *);
+struct ovsdb_error *ovsdb_txn_commit(struct ovsdb_txn *, bool durable);
+
+struct ovsdb_row *ovsdb_txn_row_modify(struct ovsdb_txn *,
+ const struct ovsdb_row *);
+void ovsdb_txn_row_insert(struct ovsdb_txn *, struct ovsdb_row *);
+void ovsdb_txn_row_delete(struct ovsdb_txn *, const struct ovsdb_row *);
+
+typedef bool ovsdb_txn_row_cb_func(const struct ovsdb_row *old,
+ const struct ovsdb_row *new,
+ void *aux);
+void ovsdb_txn_for_each_change(const struct ovsdb_txn *,
+ ovsdb_txn_row_cb_func *, void *aux);
+
+#endif /* ovsdb/transaction.h */
--- /dev/null
+/* Copyright (c) 2009 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 "trigger.h"
+
+#include <assert.h>
+#include <limits.h>
+
+#include "json.h"
+#include "jsonrpc.h"
+#include "ovsdb.h"
+#include "poll-loop.h"
+
+static bool ovsdb_trigger_try(struct ovsdb *db, struct ovsdb_trigger *,
+ long long int now);
+static void ovsdb_trigger_complete(struct ovsdb_trigger *);
+
+void
+ovsdb_trigger_init(struct ovsdb *db, struct ovsdb_trigger *trigger,
+ struct json *request, struct list *completion,
+ long long int now)
+{
+ list_push_back(&db->triggers, &trigger->node);
+ trigger->completion = completion;
+ trigger->request = request;
+ trigger->result = NULL;
+ trigger->created = now;
+ trigger->timeout_msec = LLONG_MAX;
+ ovsdb_trigger_try(db, trigger, now);
+}
+
+void
+ovsdb_trigger_destroy(struct ovsdb_trigger *trigger)
+{
+ list_remove(&trigger->node);
+ json_destroy(trigger->request);
+ json_destroy(trigger->result);
+}
+
+bool
+ovsdb_trigger_is_complete(const struct ovsdb_trigger *trigger)
+{
+ return trigger->result != NULL;
+}
+
+struct json *
+ovsdb_trigger_steal_result(struct ovsdb_trigger *trigger)
+{
+ struct json *result = trigger->result;
+ trigger->result = NULL;
+ return result;
+}
+
+void
+ovsdb_trigger_run(struct ovsdb *db, long long int now)
+{
+ struct ovsdb_trigger *t, *next;
+ bool run_triggers;
+
+ run_triggers = db->run_triggers;
+ db->run_triggers = false;
+ LIST_FOR_EACH_SAFE (t, next, struct ovsdb_trigger, node, &db->triggers) {
+ if (run_triggers || now - t->created >= t->timeout_msec) {
+ ovsdb_trigger_try(db, t, now);
+ }
+ }
+}
+
+void
+ovsdb_trigger_wait(struct ovsdb *db, long long int now)
+{
+ if (db->run_triggers) {
+ poll_immediate_wake();
+ } else {
+ long long int deadline = LLONG_MAX;
+ struct ovsdb_trigger *t;
+
+ LIST_FOR_EACH (t, struct ovsdb_trigger, node, &db->triggers) {
+ if (t->created < LLONG_MAX - t->timeout_msec) {
+ long long int t_deadline = t->created + t->timeout_msec;
+ if (deadline > t_deadline) {
+ deadline = t_deadline;
+ if (now >= deadline) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (deadline < LLONG_MAX) {
+ poll_timer_wait(MIN(deadline - now, INT_MAX));
+ }
+ }
+}
+
+static bool
+ovsdb_trigger_try(struct ovsdb *db, struct ovsdb_trigger *t, long long int now)
+{
+ t->result = ovsdb_execute(db, t->request, now - t->created,
+ &t->timeout_msec);
+ if (t->result) {
+ ovsdb_trigger_complete(t);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static void
+ovsdb_trigger_complete(struct ovsdb_trigger *t)
+{
+ assert(t->result != NULL);
+ list_remove(&t->node);
+ list_push_back(t->completion, &t->node);
+}
--- /dev/null
+/* Copyright (c) 2009 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.
+ */
+
+#ifndef OVSDB_TRIGGER_H
+#define OVSDB_TRIGGER_H 1
+
+#include "list.h"
+
+struct ovsdb;
+
+struct ovsdb_trigger {
+ struct list node; /* !result: in struct ovsdb "triggers" list;
+ * result: in completion list. */
+ struct list *completion; /* Completion list. */
+ struct json *request; /* Database request. */
+ struct json *result; /* Result (null if none yet). */
+ long long int created; /* Time created. */
+ long long int timeout_msec; /* Max wait duration. */
+};
+
+void ovsdb_trigger_init(struct ovsdb *, struct ovsdb_trigger *,
+ struct json *request, struct list *completion,
+ long long int now);
+void ovsdb_trigger_destroy(struct ovsdb_trigger *);
+
+bool ovsdb_trigger_is_complete(const struct ovsdb_trigger *);
+struct json *ovsdb_trigger_steal_result(struct ovsdb_trigger *);
+
+void ovsdb_trigger_run(struct ovsdb *, long long int now);
+void ovsdb_trigger_wait(struct ovsdb *, long long int now);
+
+#endif /* ovsdb/trigger.h */
/test-flows
/test-hash
/test-hmap
+/test-json
/test-list
/test-stp
/test-type-props
--- /dev/null
+AT_BANNER([AES-128 unit tests])
+
+m4_define([AES128_CHECK],
+ [AT_SETUP([$1])
+ AT_KEYWORDS([aes128])
+ OVS_CHECK_LCOV([test-aes128 $2 $3], [0], [$4
+], [])
+ AT_CLEANUP])
+
+AES128_CHECK(
+ [wikipedia test vector 1],
+ [00010203050607080a0b0c0d0f101112],
+ [506812a45f08c889b97f5980038b8359],
+ [d8f532538289ef7d06b506a4fd5be9c9])
+
+AES128_CHECK(
+ [wikipedia test vector 2],
+ [95A8EE8E89979B9EFDCBC6EB9797528D],
+ [4ec137a426dabf8aa0beb8bc0c2b89d6],
+ [d9b65d1232ba0199cdbd487b2a1fd646])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 0],
+ [10a58869d74be5a374cf867cfb473859],
+ [00000000000000000000000000000000],
+ [6d251e6944b051e04eaa6fb4dbf78465])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 1],
+ [caea65cdbb75e9169ecd22ebe6e54675],
+ [00000000000000000000000000000000],
+ [6e29201190152df4ee058139def610bb])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 2],
+ [a2e2fa9baf7d20822ca9f0542f764a41],
+ [00000000000000000000000000000000],
+ [c3b44b95d9d2f25670eee9a0de099fa3])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 3],
+ [b6364ac4e1de1e285eaf144a2415f7a0],
+ [00000000000000000000000000000000],
+ [5d9b05578fc944b3cf1ccf0e746cd581])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 4],
+ [64cf9c7abc50b888af65f49d521944b2],
+ [00000000000000000000000000000000],
+ [f7efc89d5dba578104016ce5ad659c05])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 5],
+ [47d6742eefcc0465dc96355e851b64d9],
+ [00000000000000000000000000000000],
+ [0306194f666d183624aa230a8b264ae7])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 6],
+ [3eb39790678c56bee34bbcdeccf6cdb5],
+ [00000000000000000000000000000000],
+ [858075d536d79ccee571f7d7204b1f67])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 7],
+ [64110a924f0743d500ccadae72c13427],
+ [00000000000000000000000000000000],
+ [35870c6a57e9e92314bcb8087cde72ce])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 8],
+ [18d8126516f8a12ab1a36d9f04d68e51],
+ [00000000000000000000000000000000],
+ [6c68e9be5ec41e22c825b7c7affb4363])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 9],
+ [f530357968578480b398a3c251cd1093],
+ [00000000000000000000000000000000],
+ [f5df39990fc688f1b07224cc03e86cea])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 10],
+ [da84367f325d42d601b4326964802e8e],
+ [00000000000000000000000000000000],
+ [bba071bcb470f8f6586e5d3add18bc66])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 11],
+ [e37b1c6aa2846f6fdb413f238b089f23],
+ [00000000000000000000000000000000],
+ [43c9f7e62f5d288bb27aa40ef8fe1ea8])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 12],
+ [6c002b682483e0cabcc731c253be5674],
+ [00000000000000000000000000000000],
+ [3580d19cff44f1014a7c966a69059de5])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 13],
+ [143ae8ed6555aba96110ab58893a8ae1],
+ [00000000000000000000000000000000],
+ [806da864dd29d48deafbe764f8202aef])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 14],
+ [b69418a85332240dc82492353956ae0c],
+ [00000000000000000000000000000000],
+ [a303d940ded8f0baff6f75414cac5243])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 15],
+ [71b5c08a1993e1362e4d0ce9b22b78d5],
+ [00000000000000000000000000000000],
+ [c2dabd117f8a3ecabfbb11d12194d9d0])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 16],
+ [e234cdca2606b81f29408d5f6da21206],
+ [00000000000000000000000000000000],
+ [fff60a4740086b3b9c56195b98d91a7b])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 17],
+ [13237c49074a3da078dc1d828bb78c6f],
+ [00000000000000000000000000000000],
+ [8146a08e2357f0caa30ca8c94d1a0544])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 18],
+ [3071a2a48fe6cbd04f1a129098e308f8],
+ [00000000000000000000000000000000],
+ [4b98e06d356deb07ebb824e5713f7be3])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 19],
+ [90f42ec0f68385f2ffc5dfc03a654dce],
+ [00000000000000000000000000000000],
+ [7a20a53d460fc9ce0423a7a0764c6cf2])
+
+AES128_CHECK(
+ [NIST KAT ECBKeySbox128e vector 20],
+ [febd9a24d8b65c1c787d50a4ed3619a9],
+ [00000000000000000000000000000000],
+ [f4a70d8af877f9b02b4c40df57d45b17])
tests/testsuite.at \
tests/lcov-pre.at \
tests/library.at \
+ tests/dir_name.at \
+ tests/aes128.at \
+ tests/uuid.at \
+ tests/json.at \
+ tests/jsonrpc.at \
+ tests/timeval.at \
+ tests/lockfile.at \
+ tests/reconnect.at \
+ tests/ovsdb.at \
+ tests/ovsdb-log.at \
+ tests/ovsdb-types.at \
+ tests/ovsdb-data.at \
+ tests/ovsdb-column.at \
+ tests/ovsdb-table.at \
+ tests/ovsdb-row.at \
+ tests/ovsdb-condition.at \
+ tests/ovsdb-query.at \
+ tests/ovsdb-transaction.at \
+ tests/ovsdb-execution.at \
+ tests/ovsdb-trigger.at \
+ tests/ovsdb-file.at \
+ tests/ovsdb-server.at \
+ tests/ovsdb-monitor.at \
+ tests/ovsdb-idl.at \
tests/stp.at \
tests/ovs-vsctl.at \
tests/lcov-post.at
DISTCLEANFILES += tests/atconfig tests/atlocal $(TESTSUITE)
check-local: tests/atconfig tests/atlocal $(TESTSUITE)
- $(SHELL) '$(TESTSUITE)' -C tests AUTOTEST_PATH='utilities:vswitchd:tests' $(TESTSUITEFLAGS)
+ $(SHELL) '$(TESTSUITE)' -C tests AUTOTEST_PATH='utilities:vswitchd:ovsdb:tests' $(TESTSUITEFLAGS)
clean-local:
test ! -f '$(TESTSUITE)' || $(SHELL) '$(TESTSUITE)' -C tests --clean
echo 'm4_define([AT_PACKAGE_BUGREPORT], [@PACKAGE_BUGREPORT@])'; \
} >'$(srcdir)/package.m4'
+noinst_PROGRAMS += tests/test-aes128
+tests_test_aes128_SOURCES = tests/test-aes128.c
+tests_test_aes128_LDADD = lib/libopenvswitch.a
+
noinst_PROGRAMS += tests/test-classifier
tests_test_classifier_SOURCES = tests/test-classifier.c
tests_test_classifier_LDADD = lib/libopenvswitch.a
tests_test_csum_SOURCES = tests/test-csum.c
tests_test_csum_LDADD = lib/libopenvswitch.a
+noinst_PROGRAMS += tests/test-dir_name
+tests_test_dir_name_SOURCES = tests/test-dir_name.c
+tests_test_dir_name_LDADD = lib/libopenvswitch.a
+
noinst_PROGRAMS += tests/test-flows
tests_test_flows_SOURCES = tests/test-flows.c
tests_test_flows_LDADD = lib/libopenvswitch.a
tests_test_hmap_SOURCES = tests/test-hmap.c
tests_test_hmap_LDADD = lib/libopenvswitch.a
+noinst_PROGRAMS += tests/test-json
+tests_test_json_SOURCES = tests/test-json.c
+tests_test_json_LDADD = lib/libopenvswitch.a
+
+noinst_PROGRAMS += tests/test-jsonrpc
+tests_test_jsonrpc_SOURCES = tests/test-jsonrpc.c
+tests_test_jsonrpc_LDADD = lib/libopenvswitch.a
+
noinst_PROGRAMS += tests/test-list
tests_test_list_SOURCES = tests/test-list.c
tests_test_list_LDADD = lib/libopenvswitch.a
+noinst_PROGRAMS += tests/test-lockfile
+tests_test_lockfile_SOURCES = tests/test-lockfile.c
+tests_test_lockfile_LDADD = lib/libopenvswitch.a
+
+noinst_PROGRAMS += tests/test-ovsdb
+tests_test_ovsdb_SOURCES = tests/test-ovsdb.c tests/idltest.c tests/idltest.h
+tests_test_ovsdb_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a
+EXTRA_DIST += tests/uuidfilt.pl tests/idltest.ovsidl
+BUILT_SOURCES += tests/idltest.c tests/idltest.h
+noinst_DATA += tests/idltest.ovsschema
+DISTCLEANFILES += tests/idltest.ovsschema
+
+noinst_PROGRAMS += tests/test-reconnect
+tests_test_reconnect_SOURCES = tests/test-reconnect.c
+tests_test_reconnect_LDADD = lib/libopenvswitch.a
+
noinst_PROGRAMS += tests/test-sha1
tests_test_sha1_SOURCES = tests/test-sha1.c
tests_test_sha1_LDADD = lib/libopenvswitch.a
+noinst_PROGRAMS += tests/test-timeval
+tests_test_timeval_SOURCES = tests/test-timeval.c
+tests_test_timeval_LDADD = lib/libopenvswitch.a
+
noinst_PROGRAMS += tests/test-type-props
tests_test_type_props_SOURCES = tests/test-type-props.c
tests_test_stp_SOURCES = tests/test-stp.c
tests_test_stp_LDADD = lib/libopenvswitch.a
+noinst_PROGRAMS += tests/test-uuid
+tests_test_uuid_SOURCES = tests/test-uuid.c
+tests_test_uuid_LDADD = lib/libopenvswitch.a
+
noinst_PROGRAMS += tests/test-vconn
tests_test_vconn_SOURCES = tests/test-vconn.c
tests_test_vconn_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
--- /dev/null
+AT_BANNER([test dir_name function])
+
+m4_define([CHECK_DIR_NAME],
+ [AT_SETUP([dir_name("$1") returns "$2"])
+ AT_KEYWORDS([dir_name])
+ OVS_CHECK_LCOV([test-dir_name "AS_ESCAPE($1)"], [0], [$2
+])
+ AT_CLEANUP])
+
+# These are the test cases given in POSIX for dirname().
+CHECK_DIR_NAME([/usr/lib], [/usr])
+CHECK_DIR_NAME([/usr/], [/])
+CHECK_DIR_NAME([usr], [.])
+CHECK_DIR_NAME([/], [/])
+CHECK_DIR_NAME([.], [.])
+CHECK_DIR_NAME([..], [.])
+CHECK_DIR_NAME([//], [//]) # / is also allowed
+CHECK_DIR_NAME([//foo], [//]) # / is also allowed
+CHECK_DIR_NAME([], [.])
+
+# Additional test cases.
+CHECK_DIR_NAME([dir/file], [dir])
+CHECK_DIR_NAME([dir/file/], [dir])
+CHECK_DIR_NAME([dir/file//], [dir])
+CHECK_DIR_NAME([///foo], [/])
--- /dev/null
+//
+// This is an ovsdb-idl schema. The OVSDB IDL compiler, ovsdb-idlc,
+// can translate it into an OVSDB schema (which simply entails
+// deleting some members from the schema) or C headers or source for
+// use with the IDL at runtime.
+//
+
+{"name": "idltest",
+ "idlPrefix": "idltest_",
+ "idlHeader": "\"tests/idltest.h\"",
+ "tables": {
+ "simple": {
+ "columns": {
+ "i": {"type": "integer"},
+ "r": {"type": "real"},
+ "b": {"type": "boolean"},
+ "s": {"type": "string"},
+ "u": {"type": "uuid"},
+ "ia": {"type": {"key": "integer", "min": 0, "max": "unlimited"}},
+ "ra": {"type": {"key": "real", "min": 0, "max": "unlimited"}},
+ "ba": {"type": {"key": "boolean", "min": 0, "max": "unlimited"}},
+ "sa": {"type": {"key": "string", "min": 0, "max": "unlimited"}},
+ "ua": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}}},
+ "selfLink": {
+ "columns": {
+ "i": {"type": "integer"},
+ "k": {"type": {"key": "uuid", "keyRefTable": "selfLink"}},
+ "ka": {"type": {"key": "uuid", "keyRefTable": "selfLink",
+ "min": 0, "max": "unlimited"}}}}}}
--- /dev/null
+m4_define([JSON_CHECK_POSITIVE],
+ [AT_SETUP([$1])
+ AT_KEYWORDS([json positive])
+ AT_CHECK([printf %s "AS_ESCAPE([$2])" > input])
+ OVS_CHECK_LCOV([test-json $4 input], [0], [$3
+], [])
+ AT_CLEANUP])
+
+m4_define([JSON_CHECK_NEGATIVE],
+ [AT_SETUP([$1])
+ AT_KEYWORDS([json negative])
+ AT_CHECK([printf %s "AS_ESCAPE([$2])" > input])
+ OVS_CHECK_LCOV([test-json $4 input], [1], [$3
+])
+ AT_CLEANUP])
+
+AT_BANNER([JSON -- arrays])
+
+JSON_CHECK_POSITIVE([empty array], [[ [ ] ]], [[[]]])
+JSON_CHECK_POSITIVE([single-element array], [[ [ 1 ] ]], [[[1]]])
+JSON_CHECK_POSITIVE([2-element array], [[ [ 1, 2 ] ]], [[[1,2]]])
+JSON_CHECK_POSITIVE([many-element array],
+ [[ [ 1, 2, 3, 4, 5 ] ]],
+ [[[1,2,3,4,5]]])
+JSON_CHECK_NEGATIVE([missing comma], [[ [ 1, 2, 3 4, 5 ] ]],
+ [error: syntax error expecting '@:>@' or ','])
+JSON_CHECK_NEGATIVE([trailing comma not allowed],
+ [[[1,2,]]], [error: syntax error expecting value])
+JSON_CHECK_NEGATIVE([doubled comma not allowed],
+ [[[1,,2]]], [error: syntax error expecting value])
+
+AT_BANNER([JSON -- strings])
+
+JSON_CHECK_POSITIVE([empty string], [[[ "" ]]], [[[""]]])
+JSON_CHECK_POSITIVE([1-character strings],
+ [[[ "a", "b", "c" ]]],
+ [[["a","b","c"]]])
+JSON_CHECK_POSITIVE([escape sequences],
+ [[[ " \" \\ \/ \b \f \n \r \t" ]]],
+ [[[" \" \\ / \b \f \n \r \t"]]])
+JSON_CHECK_POSITIVE([Unicode escape sequences],
+ [[[ " \u0022 \u005c \u002F \u0008 \u000c \u000A \u000d \u0009" ]]],
+ [[[" \" \\ / \b \f \n \r \t"]]])
+JSON_CHECK_POSITIVE([surrogate pairs],
+ [[["\ud834\udd1e"]]],
+ [[["𝄞"]]])
+JSON_CHECK_NEGATIVE([a string by itself is not valid JSON], ["xxx"],
+ [error: syntax error at beginning of input])
+JSON_CHECK_NEGATIVE([end of line in quoted string],
+ [[["xxx
+"]]],
+ [error: U+000A must be escaped in quoted string])
+JSON_CHECK_NEGATIVE([formfeed in quoted string],
+ [[["xxx\f"]]],
+ [error: U+000C must be escaped in quoted string])
+JSON_CHECK_NEGATIVE([bad escape in quoted string],
+ [[["\x12"]]],
+ [error: bad escape \x])
+JSON_CHECK_NEGATIVE([\u must be followed by 4 hex digits],
+ [[["\u1x"]]],
+ [error: malformed \u escape])
+JSON_CHECK_NEGATIVE([isolated leading surrogate not allowed],
+ [[["\ud834xxx"]]],
+ [error: malformed escaped surrogate pair])
+JSON_CHECK_NEGATIVE([surrogatess must paired properly],
+ [[["\ud834\u1234"]]],
+ [error: second half of escaped surrogate pair is not trailing surrogate])
+JSON_CHECK_NEGATIVE([null bytes not allowed],
+ [[["\u0000"]]],
+ [error: null bytes not supported in quoted strings])
+
+AT_SETUP([end of input in quoted string])
+AT_KEYWORDS([json negative])
+AT_CHECK([printf '\"xxx' | test-json -], [1],
+ [error: unexpected end of input in quoted string
+])
+AT_CLEANUP
+
+AT_BANNER([JSON -- objects])
+
+JSON_CHECK_POSITIVE([empty object], [[{ }]], [[{}]])
+JSON_CHECK_POSITIVE([simple object],
+ [[{"b": 2, "a": 1, "c": 3}]],
+ [[{"a":1,"b":2,"c":3}]])
+JSON_CHECK_NEGATIVE([bad value], [[{"a": }, "b": 2]],
+ [error: syntax error expecting value])
+JSON_CHECK_NEGATIVE([missing colon], [[{"b": 2, "a" 1, "c": 3}]],
+ [error: syntax error parsing object expecting ':'])
+JSON_CHECK_NEGATIVE([missing comma], [[{"b": 2 "a" 1, "c": 3}]],
+ [error: syntax error expecting '}' or ','])
+JSON_CHECK_NEGATIVE([trailing comma not allowed],
+ [[{"b": 2, "a": 1, "c": 3, }]],
+ [[error: syntax error parsing object expecting string]])
+JSON_CHECK_NEGATIVE([doubled comma not allowed],
+ [[{"b": 2, "a": 1,, "c": 3}]],
+ [[error: syntax error parsing object expecting string]])
+JSON_CHECK_NEGATIVE([names must be strings],
+ [[{1: 2}]],
+ [[error: syntax error parsing object expecting string]])
+
+AT_BANNER([JSON -- literal names])
+
+JSON_CHECK_POSITIVE([null], [[[ null ]]], [[[null]]])
+JSON_CHECK_POSITIVE([false], [[[ false ]]], [[[false]]])
+JSON_CHECK_POSITIVE([true], [[[ true ]]], [[[true]]])
+JSON_CHECK_NEGATIVE([a literal by itself is not valid JSON], [null],
+ [error: syntax error at beginning of input])
+JSON_CHECK_NEGATIVE([nullify is invalid], [[[ nullify ]]],
+ [error: invalid keyword 'nullify'])
+JSON_CHECK_NEGATIVE([nubs is invalid], [[[ nubs ]]],
+ [error: invalid keyword 'nubs'])
+JSON_CHECK_NEGATIVE([xxx is invalid], [[[ xxx ]]],
+ [error: invalid keyword 'xxx'])
+
+AT_BANNER([JSON -- numbers])
+
+JSON_CHECK_POSITIVE(
+ [integers expressed as reals],
+ [[[1.0000000000,
+ 2.00000000000000000000000000000000000,
+ 2e5,
+ 2.1234e4,
+ 2.1230e3,
+ 0e-10000,
+ 0e10000]]],
+ [[[1,2,200000,21234,2123,0,0]]])
+JSON_CHECK_POSITIVE(
+ [large integers],
+ [[[9223372036854775807, -9223372036854775808]]],
+ [[[9223372036854775807,-9223372036854775808]]])
+JSON_CHECK_POSITIVE(
+ [large integers expressed as reals],
+ [[[9223372036854775807.0, -9223372036854775808.0,
+ 92233720.36854775807e11, -9.223372036854775808e18]]],
+ [[[9223372036854775807,-9223372036854775808,9223372036854775807,-9223372036854775808]]])
+# It seems likely that the following test will fail on some system that
+# rounds slightly differently in arithmetic or in printf, but I'd like
+# to keep it this way until we run into such a system.
+JSON_CHECK_POSITIVE(
+ [large integers that overflow to reals],
+ [[[9223372036854775807000, -92233720368547758080000]]],
+ [[[9.22337203685478e+21,-9.22337203685478e+22]]])
+
+JSON_CHECK_POSITIVE(
+ [negative zero],
+ [[[-0, -0.0, 1e-9999, -1e-9999]]],
+ [[[0,0,0,0]]])
+
+JSON_CHECK_POSITIVE(
+ [reals],
+ [[[0.0, 1.0, 2.0, 3.0, 3.5, 81.250]]],
+ [[[0,1,2,3,3.5,81.25]]])
+JSON_CHECK_POSITIVE(
+ [scientific notation],
+ [[[1e3, 1E3, 2.5E2, 1e+3, 125e-3, 3.125e-2, 3125e-05, 1.525878906e-5]]],
+ [[[1000,1000,250,1000,0.125,0.03125,0.03125,1.525878906e-05]]])
+JSON_CHECK_POSITIVE(
+ [negative reals],
+ [[[-0, -1.0, -2.0, -3.0, -3.5, -8.1250]]],
+ [[[0,-1,-2,-3,-3.5,-8.125]]])
+JSON_CHECK_POSITIVE(
+ [negative scientific notation],
+ [[[-1e3, -1E3, -2.5E2, -1e+3, -125e-3, -3.125e-2, -3125e-05, -1.525878906e-5]]],
+ [[[-1000,-1000,-250,-1000,-0.125,-0.03125,-0.03125,-1.525878906e-05]]])
+JSON_CHECK_POSITIVE(
+ [1e-9999 underflows to 0],
+ [[[1e-9999]]],
+ [[[0]]])
+JSON_CHECK_NEGATIVE([a number by itself is not valid JSON], [1],
+ [error: syntax error at beginning of input])
+JSON_CHECK_NEGATIVE(
+ [leading zeros not allowed],
+ [[[0123]]],
+ [error: leading zeros not allowed])
+JSON_CHECK_NEGATIVE(
+ [1e9999 is too big],
+ [[[1e9999]]],
+ [error: number outside valid range])
+JSON_CHECK_NEGATIVE(
+ [exponent bigger than INT_MAX],
+ [[[1e9999999999999999999]]],
+ [error: exponent outside valid range])
+JSON_CHECK_NEGATIVE(
+ [decimal point must be followed by digit],
+ [[[1.]]],
+ [error: decimal point must be followed by digit])
+JSON_CHECK_NEGATIVE(
+ [exponent must contain at least one digit (1)],
+ [[[1e]]],
+ [error: exponent must contain at least one digit])
+JSON_CHECK_NEGATIVE(
+ [exponent must contain at least one digit (2)],
+ [[[1e+]]],
+ [error: exponent must contain at least one digit])
+JSON_CHECK_NEGATIVE(
+ [exponent must contain at least one digit (3)],
+ [[[1e-]]],
+ [error: exponent must contain at least one digit])
+
+AT_BANNER([JSON -- RFC 4627 examples])
+
+JSON_CHECK_POSITIVE([RFC 4267 object example],
+[[{
+ "Image": {
+ "Width": 800,
+ "Height": 600,
+ "Title": "View from 15th Floor",
+ "Thumbnail": {
+ "Url": "http://www.example.com/image/481989943",
+ "Height": 125,
+ "Width": "100"
+ },
+ "IDs": [116, 943, 234, 38793]
+ }
+}]],
+[[{"Image":{"Height":600,"IDs":[116,943,234,38793],"Thumbnail":{"Height":125,"Url":"http://www.example.com/image/481989943","Width":"100"},"Title":"View from 15th Floor","Width":800}}]])
+
+JSON_CHECK_POSITIVE([RFC 4267 array example],
+[[[
+ {
+ "precision": "zip",
+ "Latitude": 37.7668,
+ "Longitude": -122.3959,
+ "Address": "",
+ "City": "SAN FRANCISCO",
+ "State": "CA",
+ "Zip": "94107",
+ "Country": "US"
+ },
+ {
+ "precision": "zip",
+ "Latitude": 37.371991,
+ "Longitude": -122.026020,
+ "Address": "",
+ "City": "SUNNYVALE",
+ "State": "CA",
+ "Zip": "94085",
+ "Country": "US"
+ }
+]]],
+[[[{"Address":"","City":"SAN FRANCISCO","Country":"US","Latitude":37.7668,"Longitude":-122.3959,"State":"CA","Zip":"94107","precision":"zip"},{"Address":"","City":"SUNNYVALE","Country":"US","Latitude":37.371991,"Longitude":-122.02602,"State":"CA","Zip":"94085","precision":"zip"}]]])
+
+AT_BANNER([JSON -- pathological cases])
+
+JSON_CHECK_NEGATIVE([trailing garbage], [[[1]null]],
+ [error: trailing garbage at end of input])
+JSON_CHECK_NEGATIVE([formfeeds are not valid white space],
+ [[[\f]]], [error: invalid character U+000c])
+JSON_CHECK_NEGATIVE([';' is not a valid token],
+ [;], [error: invalid character ';'])
+JSON_CHECK_NEGATIVE([arrays nesting too deep],
+ [m4_for([i], [0], [1002], [1], [@<:@])dnl
+ m4_for([i], [0], [1002], [1], [@:>@])],
+ [error: input exceeds maximum nesting depth 1000])
+JSON_CHECK_NEGATIVE([objects nesting too deep],
+ [m4_for([i], [0], [1002], [1], [{"x":])dnl
+ m4_for([i], [0], [1002], [1], [}])],
+ [error: input exceeds maximum nesting depth 1000])
+
+AT_SETUP([input may not be empty])
+AT_KEYWORDS([json negative])
+AT_CHECK([test-json /dev/null], [1], [error: empty input stream
+])
+AT_CLEANUP
+
+AT_BANNER([JSON -- multiple inputs])
+
+JSON_CHECK_POSITIVE([multiple adjacent objects], [[{}{}{}]], [[{}
+{}
+{}]],
+ [--multiple])
+
+JSON_CHECK_POSITIVE([multiple space-separated objects], [[{} {} {}]], [[{}
+{}
+{}]],
+ [--multiple])
+
+JSON_CHECK_POSITIVE([multiple objects on separate lines], [[{}
+{}
+{}]], [[{}
+{}
+{}]],
+ [--multiple])
+
+JSON_CHECK_POSITIVE([multiple objects and arrays], [[{}[]{}[]]], [[{}
+[]
+{}
+[]]],
+ [--multiple])
+
+JSON_CHECK_NEGATIVE([garbage between multiple objects], [[{}x{}]], [[{}
+error: invalid keyword 'x'
+{}]], [--multiple])
+
+JSON_CHECK_NEGATIVE([garbage after multiple objects], [[{}{}x]], [[{}
+{}
+error: invalid keyword 'x']], [--multiple])
--- /dev/null
+AT_BANNER([JSON-RPC])
+
+AT_SETUP([JSON-RPC request and successful reply])
+AT_CHECK([test-jsonrpc --detach --pidfile=$PWD/pid listen punix:socket])
+AT_CHECK([test -s pid])
+AT_CHECK([kill -0 `cat pid`])
+AT_CHECK(
+ [[test-jsonrpc request unix:socket echo '[{"a": "b", "x": null}]']], [0],
+ [[{"error":null,"id":0,"result":[{"a":"b","x":null}]}
+]], [ignore], [test ! -e pid || kill `cat pid`])
+AT_CHECK([kill `cat pid`])
+AT_CLEANUP
+
+AT_SETUP([JSON-RPC request and error reply])
+AT_CHECK([test-jsonrpc --detach --pidfile=$PWD/pid listen punix:socket])
+AT_CHECK([test -s pid])
+AT_CHECK([kill -0 `cat pid`])
+AT_CHECK(
+ [[test-jsonrpc request unix:socket bad-request '[]']], [0],
+ [[{"error":{"error":"unknown method"},"id":0,"result":null}
+]], [ignore], [test ! -e pid || kill `cat pid`])
+AT_CHECK([kill `cat pid`])
+AT_CLEANUP
+
+AT_SETUP([JSON-RPC notification])
+AT_CHECK([test-jsonrpc --detach --pidfile=$PWD/pid listen punix:socket])
+AT_CHECK([test -s pid])
+# When a daemon dies it deletes its pidfile, so make a copy.
+AT_CHECK([cp pid pid2])
+AT_CHECK([kill -0 `cat pid2`])
+OVS_CHECK_LCOV([[test-jsonrpc notify unix:socket shutdown '[]']], [0], [],
+ [ignore], [kill `cat pid2`])
+AT_CHECK(
+ [pid=`cat pid2`
+ # First try a quick sleep, so that the test completes very quickly
+ # in the normal case. POSIX doesn't require fractional times to
+ # work, so this might not work.
+ sleep 0.1; if kill -0 $pid; then :; else echo success; exit 0; fi
+ # Then wait up to 2 seconds.
+ sleep 1; if kill -0 $pid; then :; else echo success; exit 0; fi
+ sleep 1; if kill -0 $pid; then :; else echo success; exit 0; fi
+ echo failure; exit 1], [0], [success
+], [ignore])
+AT_CHECK([test ! -e pid])
+AT_CLEANUP
AT_CLEANUP
AT_SETUP([test flow classifier])
+AT_KEYWORDS([slow])
OVS_CHECK_LCOV([test-classifier], [0], [ignore])
AT_CLEANUP
--- /dev/null
+AT_BANNER([lockfile unit tests])
+
+m4_define([CHECK_LOCKFILE],
+ [AT_SETUP([m4_translit([$1], [_], [ ])])
+ AT_KEYWORDS([lockfile])
+ OVS_CHECK_LCOV([test-lockfile $1], [0], [$1: success (m4_if(
+ [$2], [1], [$2 child], [$2 children]))
+])
+ AT_CLEANUP])
+
+CHECK_LOCKFILE([lock_and_unlock], [0])
+CHECK_LOCKFILE([lock_and_unlock_twice], [0])
+CHECK_LOCKFILE([lock_blocks_same_process], [0])
+CHECK_LOCKFILE([lock_blocks_same_process_twice], [0])
+CHECK_LOCKFILE([lock_blocks_other_process], [1])
+CHECK_LOCKFILE([lock_twice_blocks_other_process], [1])
+CHECK_LOCKFILE([lock_and_unlock_allows_other_process], [1])
+CHECK_LOCKFILE([lock_timeout_gets_the_lock], [1])
+CHECK_LOCKFILE([lock_timeout_runs_out], [1])
+CHECK_LOCKFILE([lock_multiple], [0])
--- /dev/null
+AT_BANNER([OVSDB -- columns])
+
+OVSDB_CHECK_POSITIVE([ordinary column],
+ [[parse-column mycol '{"type": "integer"}']],
+ [[{"type":"integer"}]])
+
+OVSDB_CHECK_POSITIVE([immutable column],
+ [[parse-column mycol '{"type": "real", "mutable": false}']],
+ [[{"mutable":false,"type":"real"}]])
+
+OVSDB_CHECK_POSITIVE([ephemeral column],
+ [[parse-column mycol '{"type": "uuid", "ephemeral": true}']],
+ [[{"ephemeral":true,"type":"uuid"}]])
+
+OVSDB_CHECK_POSITIVE([column with comment],
+ [[parse-column mycol '{"type": "boolean",
+ "comment": "extra information about this column"}']],
+ [[{"comment":"extra information about this column","type":"boolean"}]])
--- /dev/null
+AT_BANNER([OVSDB -- conditions])
+
+OVSDB_CHECK_POSITIVE([null condition],
+ [[parse-conditions \
+ '{"columns": {"name": {"type": "string"}}}' \
+ '[]']],
+ [[[]]])
+
+OVSDB_CHECK_POSITIVE([conditions on scalars],
+ [[parse-conditions \
+ '{"columns":
+ {"i": {"type": "integer"},
+ "r": {"type": "real"},
+ "b": {"type": "boolean"},
+ "s": {"type": "string"},
+ "u": {"type": "uuid"}}}' \
+ '[["i", "==", 0]]' \
+ '[["i", "!=", 1]]' \
+ '[["i", "<", 2]]' \
+ '[["i", "<=", 3]]' \
+ '[["i", ">", 4]]' \
+ '[["i", ">=", 5]]' \
+ '[["i", "includes", 6]]' \
+ '[["i", "excludes", 7]]' \
+ '[["r", "==", 0.5]]' \
+ '[["r", "!=", 1.5]]' \
+ '[["r", "<", 2.5]]' \
+ '[["r", "<=", 3.5]]' \
+ '[["r", ">", 4.5]]' \
+ '[["r", ">=", 5.5]]' \
+ '[["r", "includes", 6.5]]' \
+ '[["r", "excludes", 7.5]]' \
+ '[["b", "==", true]]' \
+ '[["b", "!=", false]]' \
+ '[["b", "includes", false]]' \
+ '[["b", "excludes", true]]' \
+ '[["s", "==", "a"]]' \
+ '[["s", "!=", "b"]]' \
+ '[["s", "includes", "c"]]' \
+ '[["s", "excludes", "d"]]' \
+ '[["u", "==", ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]]' \
+ '[["u", "!=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \
+ '[["u", "includes", ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]' \
+ '[["u", "excludes", ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]]]']],
+ [[[["i","==",0]]
+[["i","!=",1]]
+[["i","<",2]]
+[["i","<=",3]]
+[["i",">",4]]
+[["i",">=",5]]
+[["i","includes",6]]
+[["i","excludes",7]]
+[["r","==",0.5]]
+[["r","!=",1.5]]
+[["r","<",2.5]]
+[["r","<=",3.5]]
+[["r",">",4.5]]
+[["r",">=",5.5]]
+[["r","includes",6.5]]
+[["r","excludes",7.5]]
+[["b","==",true]]
+[["b","!=",false]]
+[["b","includes",false]]
+[["b","excludes",true]]
+[["s","==","a"]]
+[["s","!=","b"]]
+[["s","includes","c"]]
+[["s","excludes","d"]]
+[["u","==",["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]]
+[["u","!=",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]]
+[["u","includes",["uuid","ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]
+[["u","excludes",["uuid","62315898-64e0-40b9-b26f-ff74225303e6"]]]]],
+ [condition])
+
+AT_SETUP([disallowed conditions on scalars])
+AT_KEYWORDS([ovsdb negative condition])
+OVS_CHECK_LCOV([[test-ovsdb parse-conditions \
+ '{"columns":
+ {"i": {"type": "integer"},
+ "r": {"type": "real"},
+ "b": {"type": "boolean"},
+ "s": {"type": "string"},
+ "u": {"type": "uuid"}}}' \
+ '[["b", ">", true]]' \
+ '[["b", ">=", false]]' \
+ '[["b", "<", false]]' \
+ '[["b", "<=", false]]' \
+ '[["s", ">", "a"]]' \
+ '[["s", ">=", "b"]]' \
+ '[["s", "<", "c"]]' \
+ '[["s", "<=", "d"]]' \
+ '[["u", ">", ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]]' \
+ '[["u", ">=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \
+ '[["u", "<", ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]' \
+ '[["u", "<=", ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]]]']],
+ [1], [],
+ [[test-ovsdb: syntax "["b",">",true]": syntax error: Type mismatch: ">" operator may not be applied to column b of type boolean.
+test-ovsdb: syntax "["b",">=",false]": syntax error: Type mismatch: ">=" operator may not be applied to column b of type boolean.
+test-ovsdb: syntax "["b","<",false]": syntax error: Type mismatch: "<" operator may not be applied to column b of type boolean.
+test-ovsdb: syntax "["b","<=",false]": syntax error: Type mismatch: "<=" operator may not be applied to column b of type boolean.
+test-ovsdb: syntax "["s",">","a"]": syntax error: Type mismatch: ">" operator may not be applied to column s of type string.
+test-ovsdb: syntax "["s",">=","b"]": syntax error: Type mismatch: ">=" operator may not be applied to column s of type string.
+test-ovsdb: syntax "["s","<","c"]": syntax error: Type mismatch: "<" operator may not be applied to column s of type string.
+test-ovsdb: syntax "["s","<=","d"]": syntax error: Type mismatch: "<=" operator may not be applied to column s of type string.
+test-ovsdb: syntax "["u",">",["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]": syntax error: Type mismatch: ">" operator may not be applied to column u of type uuid.
+test-ovsdb: syntax "["u",">=",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]": syntax error: Type mismatch: ">=" operator may not be applied to column u of type uuid.
+test-ovsdb: syntax "["u","<",["uuid","ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]": syntax error: Type mismatch: "<" operator may not be applied to column u of type uuid.
+test-ovsdb: syntax "["u","<=",["uuid","62315898-64e0-40b9-b26f-ff74225303e6"]]": syntax error: Type mismatch: "<=" operator may not be applied to column u of type uuid.
+]])
+AT_CLEANUP
+
+OVSDB_CHECK_POSITIVE([conditions on sets],
+ [[parse-conditions \
+ '{"columns":
+ {"i": {"type": {"key": "integer", "min": 0, "max": "unlimited"}},
+ "r": {"type": {"key": "real", "min": 0, "max": "unlimited"}},
+ "b": {"type": {"key": "boolean", "min": 0, "max": "unlimited"}},
+ "s": {"type": {"key": "string", "min": 0, "max": "unlimited"}},
+ "u": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}}}' \
+ '[["i", "==", ["set", []]]]' \
+ '[["i", "!=", ["set", [1]]]]' \
+ '[["i", "includes", ["set", [1, 2]]]]' \
+ '[["i", "excludes", ["set", [1, 2, 3]]]]' \
+ '[["r", "==", ["set", []]]]' \
+ '[["r", "!=", ["set", [1.5]]]]' \
+ '[["r", "includes", ["set", [1.5, 2.5]]]]' \
+ '[["r", "excludes", ["set", [1.5, 2.5, 3.5]]]]' \
+ '[["b", "==", ["set", [true]]]]' \
+ '[["b", "!=", ["set", [false]]]]' \
+ '[["b", "includes", ["set", [false]]]]' \
+ '[["b", "excludes", ["set", [true, false]]]]' \
+ '[["s", "==", ["set", ["a"]]]]' \
+ '[["s", "!=", ["set", ["a", "b"]]]]' \
+ '[["s", "includes", ["set", ["c"]]]]' \
+ '[["s", "excludes", ["set", ["c", "d"]]]]' \
+ '[["u", "==",
+ ["set", [["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]]' \
+ '[["u", "==",
+ ["set", [["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"],
+ ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]]]' \
+ '[["u", "includes",
+ ["set", [["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"],
+ ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"],
+ ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]]]' \
+ '[["u", "excludes",
+ ["set", [["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"],
+ ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"],
+ ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"],
+ ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]]]]]' \
+]],
+ [[[["i","==",["set",[]]]]
+[["i","!=",["set",[1]]]]
+[["i","includes",["set",[1,2]]]]
+[["i","excludes",["set",[1,2,3]]]]
+[["r","==",["set",[]]]]
+[["r","!=",["set",[1.5]]]]
+[["r","includes",["set",[1.5,2.5]]]]
+[["r","excludes",["set",[1.5,2.5,3.5]]]]
+[["b","==",["set",[true]]]]
+[["b","!=",["set",[false]]]]
+[["b","includes",["set",[false]]]]
+[["b","excludes",["set",[false,true]]]]
+[["s","==",["set",["a"]]]]
+[["s","!=",["set",["a","b"]]]]
+[["s","includes",["set",["c"]]]]
+[["s","excludes",["set",["c","d"]]]]
+[["u","==",["set",[["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]]
+[["u","==",["set",[["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"],["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]]
+[["u","includes",["set",[["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"],["uuid","ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"],["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]]
+[["u","excludes",["set",[["uuid","62315898-64e0-40b9-b26f-ff74225303e6"],["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"],["uuid","ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"],["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]]]],
+ [condition])
+
+OVSDB_CHECK_POSITIVE([condition sorting],
+ [[parse-conditions \
+ '{"columns": {"i": {"type": "integer"}}}' \
+ '[["i", "excludes", 7],
+ ["i", "!=", 8],
+ ["i", "==", 1],
+ ["i", "includes", 2],
+ ["i", "<=", 3],
+ ["i", "<", 4],
+ ["i", ">", 6],
+ ["i", ">=", 5],
+ ["_uuid", "==", ["uuid", "d50e85c6-8ae7-4b16-b69e-4395928bd9be"]]]']],
+ [[[["_uuid","==",["uuid","d50e85c6-8ae7-4b16-b69e-4395928bd9be"]],["i","==",1],["i","includes",2],["i","<=",3],["i","<",4],["i",">=",5],["i",">",6],["i","excludes",7],["i","!=",8]]]])
+
+OVSDB_CHECK_POSITIVE([evaluating null condition],
+ [[evaluate-conditions \
+ '{"columns": {"i": {"type": "integer"}}}' \
+ '[[]]' \
+ '[{"i": 0},
+ {"i": 1},
+ {"i": 2}']]],
+ [condition 0: TTT])
+
+OVSDB_CHECK_POSITIVE([evaluating conditions on integers],
+ [[evaluate-conditions \
+ '{"columns": {"i": {"type": "integer"}}}' \
+ '[[["i", "<", 1]],
+ [["i", "<=", 1]],
+ [["i", "==", 1]],
+ [["i", "!=", 1]],
+ [["i", ">=", 1]],
+ [["i", ">", 1]],
+ [["i", "includes", 1]],
+ [["i", "excludes", 1]]]' \
+ '[{"i": 0},
+ {"i": 1},
+ {"i": 2}']]],
+ [condition 0: T--
+condition 1: TT-
+condition 2: -T-
+condition 3: T-T
+condition 4: -TT
+condition 5: --T
+condition 6: -T-
+condition 7: T-T], [condition])
+
+OVSDB_CHECK_POSITIVE([evaluating conditions on reals],
+ [[evaluate-conditions \
+ '{"columns": {"r": {"type": "real"}}}' \
+ '[[["r", "<", 5.0]],
+ [["r", "<=", 5.0]],
+ [["r", "==", 5.0]],
+ [["r", "!=", 5.0]],
+ [["r", ">=", 5.0]],
+ [["r", ">", 5.0]],
+ [["r", "includes", 5.0]],
+ [["r", "excludes", 5.0]]]' \
+ '[{"r": 0},
+ {"r": 5.0},
+ {"r": 5.1}']]],
+ [condition 0: T--
+condition 1: TT-
+condition 2: -T-
+condition 3: T-T
+condition 4: -TT
+condition 5: --T
+condition 6: -T-
+condition 7: T-T], [condition])
+
+OVSDB_CHECK_POSITIVE([evaluating conditions on booleans],
+ [[evaluate-conditions \
+ '{"columns": {"b": {"type": "boolean"}}}' \
+ '[[["b", "==", true]],
+ [["b", "!=", true]],
+ [["b", "includes", true]],
+ [["b", "excludes", true]],
+ [["b", "==", false]],
+ [["b", "!=", false]],
+ [["b", "includes", false]],
+ [["b", "excludes", false]]]' \
+ '[{"b": true},
+ {"b": false}']]],
+ [condition 0: T-
+condition 1: -T
+condition 2: T-
+condition 3: -T
+condition 4: -T
+condition 5: T-
+condition 6: -T
+condition 7: T-], [condition])
+
+OVSDB_CHECK_POSITIVE([evaluating conditions on strings],
+ [[evaluate-conditions \
+ '{"columns": {"s": {"type": "string"}}}' \
+ '[[["s", "==", ""]],
+ [["s", "!=", ""]],
+ [["s", "includes", ""]],
+ [["s", "excludes", ""]],
+ [["s", "==", "foo"]],
+ [["s", "!=", "foo"]],
+ [["s", "includes", "foo"]],
+ [["s", "excludes", "foo"]]]' \
+ '[{"s": ""},
+ {"s": "foo"},
+ {"s": "xxx"}']]],
+ [condition 0: T--
+condition 1: -TT
+condition 2: T--
+condition 3: -TT
+condition 4: -T-
+condition 5: T-T
+condition 6: -T-
+condition 7: T-T], [condition])
+
+OVSDB_CHECK_POSITIVE([evaluating conditions on UUIDs],
+ [[evaluate-conditions \
+ '{"columns": {"u": {"type": "uuid"}}}' \
+ '[[["u", "==", ["uuid", "8a1dbdb8-416f-4ce9-affa-3332691714b6"]]],
+ [["u", "!=", ["uuid", "8a1dbdb8-416f-4ce9-affa-3332691714b6"]]],
+ [["u", "includes", ["uuid", "8a1dbdb8-416f-4ce9-affa-3332691714b6"]]],
+ [["u", "excludes", ["uuid", "8a1dbdb8-416f-4ce9-affa-3332691714b6"]]],
+ [["u", "==", ["uuid", "06151f9d-62d6-4f59-8504-e9765107faa9"]]],
+ [["u", "!=", ["uuid", "06151f9d-62d6-4f59-8504-e9765107faa9"]]],
+ [["u", "includes", ["uuid", "06151f9d-62d6-4f59-8504-e9765107faa9"]]],
+ [["u", "excludes", ["uuid", "06151f9d-62d6-4f59-8504-e9765107faa9"]]]]' \
+ '[{"u": ["uuid", "8a1dbdb8-416f-4ce9-affa-3332691714b6"]},
+ {"u": ["uuid", "06151f9d-62d6-4f59-8504-e9765107faa9"]},
+ {"u": ["uuid", "00000000-0000-0000-0000-000000000000"]}']]],
+ [condition 0: T--
+condition 1: -TT
+condition 2: T--
+condition 3: -TT
+condition 4: -T-
+condition 5: T-T
+condition 6: -T-
+condition 7: T-T], [condition])
+
+OVSDB_CHECK_POSITIVE([evaluating conditions on sets],
+ [[evaluate-conditions \
+ '{"columns": {"i": {"type": {"key": "integer", "min": 0, "max": "unlimited"}}}}' \
+ '[[["i", "==", ["set", []]]],
+ [["i", "==", ["set", [0]]]],
+ [["i", "==", ["set", [1]]]],
+ [["i", "==", ["set", [0, 1]]]],
+ [["i", "==", ["set", [2]]]],
+ [["i", "==", ["set", [2, 0]]]],
+ [["i", "==", ["set", [2, 1]]]],
+ [["i", "==", ["set", [2, 1, 0]]]],
+ [["i", "!=", ["set", []]]],
+ [["i", "!=", ["set", [0]]]],
+ [["i", "!=", ["set", [1]]]],
+ [["i", "!=", ["set", [0, 1]]]],
+ [["i", "!=", ["set", [2]]]],
+ [["i", "!=", ["set", [2, 0]]]],
+ [["i", "!=", ["set", [2, 1]]]],
+ [["i", "!=", ["set", [2, 1, 0]]]],
+ [["i", "includes", ["set", []]]],
+ [["i", "includes", ["set", [0]]]],
+ [["i", "includes", ["set", [1]]]],
+ [["i", "includes", ["set", [0, 1]]]],
+ [["i", "includes", ["set", [2]]]],
+ [["i", "includes", ["set", [2, 0]]]],
+ [["i", "includes", ["set", [2, 1]]]],
+ [["i", "includes", ["set", [2, 1, 0]]]],
+ [["i", "excludes", ["set", []]]],
+ [["i", "excludes", ["set", [0]]]],
+ [["i", "excludes", ["set", [1]]]],
+ [["i", "excludes", ["set", [0, 1]]]],
+ [["i", "excludes", ["set", [2]]]],
+ [["i", "excludes", ["set", [2, 0]]]],
+ [["i", "excludes", ["set", [2, 1]]]],
+ [["i", "excludes", ["set", [2, 1, 0]]]]]' \
+ '[{"i": ["set", []]},
+ {"i": ["set", [0]]},
+ {"i": ["set", [1]]},
+ {"i": ["set", [0, 1]]},
+ {"i": ["set", [2]]},
+ {"i": ["set", [2, 0]]},
+ {"i": ["set", [2, 1]]},
+ {"i": ["set", [2, 1, 0]]}]']],
+ [dnl
+condition 0: T---- ---
+condition 1: -T--- ---
+condition 2: --T-- ---
+condition 3: ---T- ---
+condition 4: ----T ---
+condition 5: ----- T--
+condition 6: ----- -T-
+condition 7: ----- --T
+condition 8: -TTTT TTT
+condition 9: T-TTT TTT
+condition 10: TT-TT TTT
+condition 11: TTT-T TTT
+condition 12: TTTT- TTT
+condition 13: TTTTT -TT
+condition 14: TTTTT T-T
+condition 15: TTTTT TT-
+condition 16: TTTTT TTT
+condition 17: -T-T- T-T
+condition 18: --TT- -TT
+condition 19: ---T- --T
+condition 20: ----T TTT
+condition 21: ----- T-T
+condition 22: ----- -TT
+condition 23: ----- --T
+condition 24: TTTTT TTT
+condition 25: T-T-T -T-
+condition 26: TT--T T--
+condition 27: T---T ---
+condition 28: TTTT- ---
+condition 29: T-T-- ---
+condition 30: TT--- ---
+condition 31: T---- ---], [condition])
+
+# This is the same as the "set" test except that it adds values,
+# all of which always match.
+OVSDB_CHECK_POSITIVE([evaluating conditions on maps (1)],
+ [[evaluate-conditions \
+ '{"columns": {"i": {"type": {"key": "integer",
+ "value": "boolean",
+ "min": 0,
+ "max": "unlimited"}}}}' \
+ '[[["i", "==", ["map", []]]],
+ [["i", "==", ["map", [[0, true]]]]],
+ [["i", "==", ["map", [[1, false]]]]],
+ [["i", "==", ["map", [[0, true], [1, false]]]]],
+ [["i", "==", ["map", [[2, true]]]]],
+ [["i", "==", ["map", [[2, true], [0, true]]]]],
+ [["i", "==", ["map", [[2, true], [1, false]]]]],
+ [["i", "==", ["map", [[2, true], [1, false], [0, true]]]]],
+ [["i", "!=", ["map", []]]],
+ [["i", "!=", ["map", [[0, true]]]]],
+ [["i", "!=", ["map", [[1, false]]]]],
+ [["i", "!=", ["map", [[0, true], [1, false]]]]],
+ [["i", "!=", ["map", [[2, true]]]]],
+ [["i", "!=", ["map", [[2, true], [0, true]]]]],
+ [["i", "!=", ["map", [[2, true], [1, false]]]]],
+ [["i", "!=", ["map", [[2, true], [1, false], [0, true]]]]],
+ [["i", "includes", ["map", []]]],
+ [["i", "includes", ["map", [[0, true]]]]],
+ [["i", "includes", ["map", [[1, false]]]]],
+ [["i", "includes", ["map", [[0, true], [1, false]]]]],
+ [["i", "includes", ["map", [[2, true]]]]],
+ [["i", "includes", ["map", [[2, true], [0, true]]]]],
+ [["i", "includes", ["map", [[2, true], [1, false]]]]],
+ [["i", "includes", ["map", [[2, true], [1, false], [0, true]]]]],
+ [["i", "excludes", ["map", []]]],
+ [["i", "excludes", ["map", [[0, true]]]]],
+ [["i", "excludes", ["map", [[1, false]]]]],
+ [["i", "excludes", ["map", [[0, true], [1, false]]]]],
+ [["i", "excludes", ["map", [[2, true]]]]],
+ [["i", "excludes", ["map", [[2, true], [0, true]]]]],
+ [["i", "excludes", ["map", [[2, true], [1, false]]]]],
+ [["i", "excludes", ["map", [[2, true], [1, false], [0, true]]]]]]' \
+ '[{"i": ["map", []]},
+ {"i": ["map", [[0, true]]]},
+ {"i": ["map", [[1, false]]]},
+ {"i": ["map", [[0, true], [1, false]]]},
+ {"i": ["map", [[2, true]]]},
+ {"i": ["map", [[2, true], [0, true]]]},
+ {"i": ["map", [[2, true], [1, false]]]},
+ {"i": ["map", [[2, true], [1, false], [0, true]]]}]']],
+ [dnl
+condition 0: T---- ---
+condition 1: -T--- ---
+condition 2: --T-- ---
+condition 3: ---T- ---
+condition 4: ----T ---
+condition 5: ----- T--
+condition 6: ----- -T-
+condition 7: ----- --T
+condition 8: -TTTT TTT
+condition 9: T-TTT TTT
+condition 10: TT-TT TTT
+condition 11: TTT-T TTT
+condition 12: TTTT- TTT
+condition 13: TTTTT -TT
+condition 14: TTTTT T-T
+condition 15: TTTTT TT-
+condition 16: TTTTT TTT
+condition 17: -T-T- T-T
+condition 18: --TT- -TT
+condition 19: ---T- --T
+condition 20: ----T TTT
+condition 21: ----- T-T
+condition 22: ----- -TT
+condition 23: ----- --T
+condition 24: TTTTT TTT
+condition 25: T-T-T -T-
+condition 26: TT--T T--
+condition 27: T---T ---
+condition 28: TTTT- ---
+condition 29: T-T-- ---
+condition 30: TT--- ---
+condition 31: T---- ---], [condition])
+
+# This is the same as the "set" test except that it adds values,
+# and those values don't always match.
+OVSDB_CHECK_POSITIVE([evaluating conditions on maps (2)],
+ [[evaluate-conditions \
+ '{"columns": {"i": {"type": {"key": "integer",
+ "value": "boolean",
+ "min": 0,
+ "max": "unlimited"}}}}' \
+ '[[["i", "==", ["map", []]]],
+ [["i", "==", ["map", [[0, true]]]]],
+ [["i", "==", ["map", [[1, false]]]]],
+ [["i", "==", ["map", [[0, true], [1, false]]]]],
+ [["i", "==", ["map", [[2, true]]]]],
+ [["i", "==", ["map", [[2, true], [0, true]]]]],
+ [["i", "==", ["map", [[2, true], [1, false]]]]],
+ [["i", "==", ["map", [[2, true], [1, false], [0, true]]]]],
+ [["i", "!=", ["map", []]]],
+ [["i", "!=", ["map", [[0, true]]]]],
+ [["i", "!=", ["map", [[1, false]]]]],
+ [["i", "!=", ["map", [[0, true], [1, false]]]]],
+ [["i", "!=", ["map", [[2, true]]]]],
+ [["i", "!=", ["map", [[2, true], [0, true]]]]],
+ [["i", "!=", ["map", [[2, true], [1, false]]]]],
+ [["i", "!=", ["map", [[2, true], [1, false], [0, true]]]]],
+ [["i", "includes", ["map", []]]],
+ [["i", "includes", ["map", [[0, true]]]]],
+ [["i", "includes", ["map", [[1, false]]]]],
+ [["i", "includes", ["map", [[0, true], [1, false]]]]],
+ [["i", "includes", ["map", [[2, true]]]]],
+ [["i", "includes", ["map", [[2, true], [0, true]]]]],
+ [["i", "includes", ["map", [[2, true], [1, false]]]]],
+ [["i", "includes", ["map", [[2, true], [1, false], [0, true]]]]],
+ [["i", "excludes", ["map", []]]],
+ [["i", "excludes", ["map", [[0, true]]]]],
+ [["i", "excludes", ["map", [[1, false]]]]],
+ [["i", "excludes", ["map", [[0, true], [1, false]]]]],
+ [["i", "excludes", ["map", [[2, true]]]]],
+ [["i", "excludes", ["map", [[2, true], [0, true]]]]],
+ [["i", "excludes", ["map", [[2, true], [1, false]]]]],
+ [["i", "excludes", ["map", [[2, true], [1, false], [0, true]]]]]]' \
+ '[{"i": ["map", []]},
+ {"i": ["map", [[0, true]]]},
+ {"i": ["map", [[0, false]]]},
+ {"i": ["map", [[1, false]]]},
+ {"i": ["map", [[1, true]]]},
+
+ {"i": ["map", [[0, true], [1, false]]]},
+ {"i": ["map", [[0, true], [1, true]]]},
+ {"i": ["map", [[2, true]]]},
+ {"i": ["map", [[2, false]]]},
+ {"i": ["map", [[2, true], [0, true]]]},
+
+ {"i": ["map", [[2, false], [0, true]]]},
+ {"i": ["map", [[2, true], [1, false]]]},
+ {"i": ["map", [[2, true], [1, true]]]},
+ {"i": ["map", [[2, true], [1, false], [0, true]]]},
+ {"i": ["map", [[2, true], [1, false], [0, false]]]}]']],
+ [dnl
+condition 0: T---- ----- -----
+condition 1: -T--- ----- -----
+condition 2: ---T- ----- -----
+condition 3: ----- T---- -----
+condition 4: ----- --T-- -----
+condition 5: ----- ----T -----
+condition 6: ----- ----- -T---
+condition 7: ----- ----- ---T-
+condition 8: -TTTT TTTTT TTTTT
+condition 9: T-TTT TTTTT TTTTT
+condition 10: TTT-T TTTTT TTTTT
+condition 11: TTTTT -TTTT TTTTT
+condition 12: TTTTT TT-TT TTTTT
+condition 13: TTTTT TTTT- TTTTT
+condition 14: TTTTT TTTTT T-TTT
+condition 15: TTTTT TTTTT TTT-T
+condition 16: TTTTT TTTTT TTTTT
+condition 17: -T--- TT--T T--T-
+condition 18: ---T- T---- -T-TT
+condition 19: ----- T---- ---T-
+condition 20: ----- --T-T -TTTT
+condition 21: ----- ----T ---T-
+condition 22: ----- ----- -T-TT
+condition 23: ----- ----- ---T-
+condition 24: TTTTT TTTTT TTTTT
+condition 25: T-TTT --TT- -TT-T
+condition 26: TTT-T -TTTT T-T--
+condition 27: T-T-T --TT- --T--
+condition 28: TTTTT TT-T- T----
+condition 29: T-TTT ---T- -----
+condition 30: TTT-T -T-T- T----
+condition 31: T-T-T ---T- -----], [condition])
--- /dev/null
+AT_BANNER([OVSDB -- atoms])
+
+OVSDB_CHECK_POSITIVE([integer atom],
+ [[parse-atoms '["integer"]' \
+ '[0]' \
+ '[-1]' \
+ '[1e3]' \
+ '[9223372036854775807]' \
+ '[-9223372036854775808]' ]],
+ [0
+-1
+1000
+9223372036854775807
+-9223372036854775808])
+
+OVSDB_CHECK_POSITIVE([real atom],
+ [[parse-atoms '["real"]' \
+ '[0]' \
+ '[0.0]' \
+ '[-0.0]' \
+ '[-1.25]' \
+ '[1e3]' \
+ '[1e37]' \
+ '[0.00390625]' ]],
+ [0
+0
+0
+-1.25
+1000
+1e+37
+0.00390625])
+
+OVSDB_CHECK_POSITIVE([boolean atom],
+ [[parse-atoms '["boolean"]' '[true]' '[false]' ]],
+ [true
+false])
+
+OVSDB_CHECK_POSITIVE([string atom],
+ [[parse-atoms '["string"]' '[""]' '["true"]' '["\"\\\/\b\f\n\r\t"]']],
+ [""
+"true"
+"\"\\/\b\f\n\r\t"])
+
+OVSDB_CHECK_POSITIVE([uuid atom],
+ [[parse-atoms '["uuid"]' '["uuid", "550e8400-e29b-41d4-a716-446655440000"]']],
+ [[["uuid","550e8400-e29b-41d4-a716-446655440000"]]])
+
+OVSDB_CHECK_POSITIVE([integer atom sorting],
+ [[sort-atoms '["integer"]' '[55,0,-1,2,1]']],
+ [[[-1,0,1,2,55]]])
+
+OVSDB_CHECK_POSITIVE([real atom sorting],
+ [[sort-atoms '["real"]' '[1.25,1.23,0.0,-0.0,-1e99]']],
+ [[[-1e+99,0,0,1.23,1.25]]])
+
+OVSDB_CHECK_POSITIVE([boolean atom sorting],
+ [[sort-atoms '["boolean"]' '[true,false,true,false,false]']],
+ [[[false,false,false,true,true]]])
+
+OVSDB_CHECK_POSITIVE([string atom sorting],
+ [[sort-atoms '["string"]' '["abd","abc","\b","xxx"]']],
+ [[["\b","abc","abd","xxx"]]])
+
+OVSDB_CHECK_POSITIVE([uuid atom sorting],
+ [[sort-atoms '["uuid"]' '[
+ ["uuid", "00000000-0000-0000-0000-000000000001"],
+ ["uuid", "00000000-1000-0000-0000-000000000000"],
+ ["uuid", "00000000-0000-1000-0000-000000000000"],
+ ["uuid", "00010000-0000-0000-0000-000000000000"],
+ ["uuid", "00000000-0000-0000-0000-000000000100"],
+ ["uuid", "00000000-0000-0000-0000-000100000000"],
+ ["uuid", "00000000-0000-0010-0000-000000000000"],
+ ["uuid", "00000100-0000-0000-0000-000000000000"],
+ ["uuid", "00000000-0000-0001-0000-000000000000"],
+ ["uuid", "00000000-0000-0000-0000-000001000000"],
+ ["uuid", "01000000-0000-0000-0000-000000000000"],
+ ["uuid", "00000000-0000-0000-0000-000000001000"],
+ ["uuid", "00000000-0000-0000-0000-000010000000"],
+ ["uuid", "00000000-0000-0000-0000-010000000000"],
+ ["uuid", "00000000-0000-0100-0000-000000000000"],
+ ["uuid", "10000000-0000-0000-0000-000000000000"],
+ ["uuid", "00000000-0000-0000-0000-000000000010"],
+ ["uuid", "00000000-0100-0000-0000-000000000000"],
+ ["uuid", "00000000-0000-0000-0100-000000000000"],
+ ["uuid", "00000000-0000-0000-0001-000000000000"],
+ ["uuid", "00000010-0000-0000-0000-000000000000"],
+ ["uuid", "00000000-0000-0000-0010-000000000000"],
+ ["uuid", "00000000-0000-0000-0000-000000010000"],
+ ["uuid", "00000000-0000-0000-1000-000000000000"],
+ ["uuid", "00000000-0000-0000-0000-100000000000"],
+ ["uuid", "00000000-0000-0000-0000-001000000000"],
+ ["uuid", "00000000-0000-0000-0000-000000100000"],
+ ["uuid", "00000000-0000-0000-0000-000000000000"],
+ ["uuid", "00000000-0010-0000-0000-000000000000"],
+ ["uuid", "00100000-0000-0000-0000-000000000000"],
+ ["uuid", "00000000-0001-0000-0000-000000000000"],
+ ["uuid", "00000001-0000-0000-0000-000000000000"],
+ ["uuid", "00001000-0000-0000-0000-000000000000"]]']],
+ [[[["uuid","00000000-0000-0000-0000-000000000000"],["uuid","00000000-0000-0000-0000-000000000001"],["uuid","00000000-0000-0000-0000-000000000010"],["uuid","00000000-0000-0000-0000-000000000100"],["uuid","00000000-0000-0000-0000-000000001000"],["uuid","00000000-0000-0000-0000-000000010000"],["uuid","00000000-0000-0000-0000-000000100000"],["uuid","00000000-0000-0000-0000-000001000000"],["uuid","00000000-0000-0000-0000-000010000000"],["uuid","00000000-0000-0000-0000-000100000000"],["uuid","00000000-0000-0000-0000-001000000000"],["uuid","00000000-0000-0000-0000-010000000000"],["uuid","00000000-0000-0000-0000-100000000000"],["uuid","00000000-0000-0000-0001-000000000000"],["uuid","00000000-0000-0000-0010-000000000000"],["uuid","00000000-0000-0000-0100-000000000000"],["uuid","00000000-0000-0000-1000-000000000000"],["uuid","00000000-0000-0001-0000-000000000000"],["uuid","00000000-0000-0010-0000-000000000000"],["uuid","00000000-0000-0100-0000-000000000000"],["uuid","00000000-0000-1000-0000-000000000000"],["uuid","00000000-0001-0000-0000-000000000000"],["uuid","00000000-0010-0000-0000-000000000000"],["uuid","00000000-0100-0000-0000-000000000000"],["uuid","00000000-1000-0000-0000-000000000000"],["uuid","00000001-0000-0000-0000-000000000000"],["uuid","00000010-0000-0000-0000-000000000000"],["uuid","00000100-0000-0000-0000-000000000000"],["uuid","00001000-0000-0000-0000-000000000000"],["uuid","00010000-0000-0000-0000-000000000000"],["uuid","00100000-0000-0000-0000-000000000000"],["uuid","01000000-0000-0000-0000-000000000000"],["uuid","10000000-0000-0000-0000-000000000000"]]]])
+
+OVSDB_CHECK_NEGATIVE([real not acceptable integer atom],
+ [[parse-atoms '["integer"]' '[0.5]' ]],
+ [expected integer])
+
+OVSDB_CHECK_NEGATIVE([string "true" not acceptable boolean atom],
+ [[parse-atoms '["boolean"]' '["true"]' ]],
+ [expected boolean])
+
+OVSDB_CHECK_NEGATIVE([integer not acceptable string atom],
+ [[parse-atoms '["string"]' '[1]']],
+ [expected string])
+
+OVSDB_CHECK_NEGATIVE([uuid atom must be expressed as array],
+ [[parse-atoms '["uuid"]' '["550e8400-e29b-41d4-a716-446655440000"]']],
+ [[expected ["uuid", <string>]]])
+
+AT_BANNER([OSVDB -- simple data])
+
+OVSDB_CHECK_POSITIVE([integer datum],
+ [[parse-data '["integer"]' '[0]' '[1]' '[-1]']],
+ [0
+1
+-1])
+
+OVSDB_CHECK_POSITIVE([real datum],
+ [[parse-data '["real"]' '[0]' '[1.0]' '[-1.25]']],
+ [0
+1
+-1.25])
+
+OVSDB_CHECK_POSITIVE([boolean datum],
+ [[parse-data '["boolean"]' '[true]' '[false]' ]],
+ [true
+false])
+
+OVSDB_CHECK_POSITIVE([string datum],
+ [[parse-data '["string"]' '[""]' '["true"]' '["\"\\\/\b\f\n\r\t"]']],
+ [""
+"true"
+"\"\\/\b\f\n\r\t"])
+
+AT_BANNER([OVSDB -- set data])
+
+OVSDB_CHECK_POSITIVE([optional boolean],
+ [[parse-data '{"key": "boolean", "min": 0}' \
+ '["set", [true]]' \
+ '["set", [false]]' \
+ '["set", []]']],
+ [[["set",[true]]
+["set",[false]]
+["set",[]]]],
+ [set])
+
+OVSDB_CHECK_POSITIVE([set of 0 or more integers],
+ [[parse-data '{"key": "integer", "min": 0, "max": "unlimited"}' \
+ '["set", [0]]' \
+ '["set", [0, 1]]' \
+ '["set", [0, 1, 2]]' \
+ '["set", [0, 1, 2, 3, 4, 5]]' \
+ '["set", [0, 1, 2, 3, 4, 5, 6, 7, 8]]' \
+ '["set", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]']],
+ [[["set",[0]]
+["set",[0,1]]
+["set",[0,1,2]]
+["set",[0,1,2,3,4,5]]
+["set",[0,1,2,3,4,5,6,7,8]]
+["set",[0,1,2,3,4,5,6,7,8,9,10]]]])
+
+OVSDB_CHECK_POSITIVE([set of 1 to 3 uuids],
+ [[parse-data '{"key": "uuid", "min": 1, "max": 3}' \
+ '["set", [["uuid", "550e8400-e29b-41d4-a716-446655440000"]]]' \
+ '["set", [["uuid", "c5051240-30ff-43ed-b4b9-93cf3f050813"],
+ ["uuid", "90558331-09af-4d2f-a572-509cad2e9088"],
+ ["uuid", "550e8400-e29b-41d4-a716-446655440000"]]]']],
+ [[["set",[["uuid","550e8400-e29b-41d4-a716-446655440000"]]]
+["set",[["uuid","550e8400-e29b-41d4-a716-446655440000"],["uuid","90558331-09af-4d2f-a572-509cad2e9088"],["uuid","c5051240-30ff-43ed-b4b9-93cf3f050813"]]]]])
+
+OVSDB_CHECK_POSITIVE([set of 0 to 3 strings],
+ [[parse-data '{"key": "string", "min": 0, "max": 3}' \
+ '["set", []]' \
+ '["set", ["a relatively long string"]]' \
+ '["set", ["short string", "a relatively long string"]]' \
+ '["set", ["zzz", "short string", "a relatively long string"]]']],
+ [[["set",[]]
+["set",["a relatively long string"]]
+["set",["a relatively long string","short string"]]
+["set",["a relatively long string","short string","zzz"]]]])
+
+OVSDB_CHECK_NEGATIVE([duplicate boolean not allowed in set],
+ [[parse-data '{"key": "boolean", "max": 5}' '["set", [true, true]]']],
+ [ovsdb error: set contains duplicate])
+
+OVSDB_CHECK_NEGATIVE([duplicate integer not allowed in set],
+ [[parse-data '{"key": "integer", "max": 5}' '["set", [1, 2, 3, 1]]']],
+ [ovsdb error: set contains duplicate])
+
+OVSDB_CHECK_NEGATIVE([duplicate real not allowed in set],
+ [[parse-data '{"key": "real", "max": 5}' '["set", [0.0, -0.0]]']],
+ [ovsdb error: set contains duplicate])
+
+OVSDB_CHECK_NEGATIVE([duplicate string not allowed in set],
+ [[parse-data '{"key": "string", "max": 5}' '["set", ["asdf", "ASDF", "asdf"]]']],
+ [ovsdb error: set contains duplicate])
+
+OVSDB_CHECK_NEGATIVE([duplicate uuid not allowed in set],
+ [[parse-data '{"key": "uuid", "max": 5}' \
+ '["set", [["uuid", "7ef21525-0088-4a28-a418-5518413e43ea"],
+ ["uuid", "355ad037-f1da-40aa-b47c-ff9c7e8c6a38"],
+ ["uuid", "7ef21525-0088-4a28-a418-5518413e43ea"]]]']],
+ [ovsdb error: set contains duplicate])
+
+AT_BANNER([OVSDB -- map data])
+
+OVSDB_CHECK_POSITIVE([map of 1 integer to boolean],
+ [[parse-data '{"key": "integer", "value": "boolean"}' \
+ '["map", [[1, true]]]']],
+ [[["map",[[1,true]]]]])
+
+OVSDB_CHECK_POSITIVE([map of at least 1 integer to boolean],
+ [[parse-data '{"key": "integer", "value": "boolean", "max": "unlimited"}' \
+ '["map", [[1, true]]]' \
+ '["map", [[0, true], [1, false], [2, true], [3, true], [4, true]]]' \
+ '["map", [[3, false], [0, true], [4, false]]]']],
+ [[["map",[[1,true]]]
+["map",[[0,true],[1,false],[2,true],[3,true],[4,true]]]
+["map",[[0,true],[3,false],[4,false]]]]])
+
+OVSDB_CHECK_POSITIVE([map of 1 boolean to integer],
+ [[parse-data '{"key": "boolean", "value": "integer"}' \
+ '["map", [[true, 1]]]']],
+ [[["map",[[true,1]]]]])
+
+OVSDB_CHECK_POSITIVE([map of 5 uuid to real],
+ [[parse-data '{"key": "uuid", "value": "real", "min": 5, "max": 5}' \
+ '["map", [[["uuid", "cad8542b-6ee1-486b-971b-7dcbf6e14979"], 1.0],
+ [["uuid", "6b94b968-2702-4f64-9457-314a34d69b8c"], 2.0],
+ [["uuid", "d2c4a168-24de-47eb-a8a3-c1abfc814979"], 3.0],
+ [["uuid", "25bfa475-d072-4f60-8be1-00f48643e9cb"], 4.0],
+ [["uuid", "1c92b8ca-d5e4-4628-a85d-1dc2d099a99a"], 5.0]]]']],
+ [[["map",[[["uuid","1c92b8ca-d5e4-4628-a85d-1dc2d099a99a"],5],[["uuid","25bfa475-d072-4f60-8be1-00f48643e9cb"],4],[["uuid","6b94b968-2702-4f64-9457-314a34d69b8c"],2],[["uuid","cad8542b-6ee1-486b-971b-7dcbf6e14979"],1],[["uuid","d2c4a168-24de-47eb-a8a3-c1abfc814979"],3]]]]])
+
+OVSDB_CHECK_POSITIVE([map of 10 string to string],
+ [[parse-data '{"key": "string", "value": "string", "min": 10, "max": 10}' \
+ '["map", [["2 gills", "1 chopin"],
+ ["2 chopins", "1 pint"],
+ ["2 pints", "1 quart"],
+ ["2 quarts", "1 pottle"],
+ ["2 pottles", "1 gallon"],
+ ["2 gallons", "1 peck"],
+ ["2 pecks", "1 demibushel"],
+ ["2 demibushel", "1 firkin"],
+ ["2 firkins", "1 kilderkin"],
+ ["2 kilderkins", "1 barrel"]]]']],
+ [[["map",[["2 chopins","1 pint"],["2 demibushel","1 firkin"],["2 firkins","1 kilderkin"],["2 gallons","1 peck"],["2 gills","1 chopin"],["2 kilderkins","1 barrel"],["2 pecks","1 demibushel"],["2 pints","1 quart"],["2 pottles","1 gallon"],["2 quarts","1 pottle"]]]]])
+
+OVSDB_CHECK_NEGATIVE([duplicate integer key not allowed in map],
+ [[parse-data '{"key": "integer", "value": "boolean", "max": 5}' \
+ '["map", [[1, true], [2, false], [1, false]]]']],
+ [ovsdb error: map contains duplicate key])
--- /dev/null
+AT_BANNER([OVSDB -- execution])
+
+m4_define([ORDINAL_SCHEMA],
+ [[{"name": "mydb",
+ "tables": {
+ "ordinals": {
+ "columns": {
+ "number": {"type": "integer"},
+ "name": {"type": "string"}}}}}]])
+
+# OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS])
+#
+# Runs "test-ovsdb execute" with the given SCHEMA and each of the
+# TRANSACTIONS (which should be a quoted list of quoted strings).
+#
+# Checks that the overall output is OUTPUT, but UUIDs in the output
+# are replaced by markers of the form <N> where N is a number. The
+# first unique UUID is replaced by <0>, the next by <1>, and so on.
+# If a given UUID appears more than once it is always replaced by the
+# same marker.
+#
+# TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
+m4_define([OVSDB_CHECK_EXECUTION],
+ [AT_SETUP([$1])
+ AT_KEYWORDS([ovsdb execute execution positive $5])
+ OVS_CHECK_LCOV([test-ovsdb execute '$2' m4_foreach([txn], [$3], [ 'txn'])],
+ [0], [stdout], [])
+ AT_CHECK([perl $srcdir/uuidfilt.pl stdout], [0], [$4])
+ AT_CLEANUP])
+
+m4_define([EXECUTION_EXAMPLES], [
+OVSDB_CHECK_EXECUTION([insert row, query table],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}}]]],
+ [[[{"op": "select",
+ "table": "ordinals",
+ "where": []}]]]],
+ [[[{"uuid":["uuid","<0>"]}]
+[{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<1>"],"name":"zero","number":0}]}]
+]])
+
+OVSDB_CHECK_EXECUTION([insert rows, query by value],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}}]]],
+ [[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"}}]]],
+ [[[{"op": "select",
+ "table": "ordinals",
+ "where": [["name", "==", "zero"]]}]]],
+ [[[{"op": "select",
+ "table": "ordinals",
+ "where": [["name", "==", "one"]]}]]]],
+ [[[{"uuid":["uuid","<0>"]}]
+[{"uuid":["uuid","<1>"]}]
+[{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<2>"],"name":"zero","number":0}]}]
+[{"rows":[{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}]
+]])
+
+OVSDB_CHECK_EXECUTION([insert rows, query by named-uuid],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"},
+ "uuid-name": "first"},
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"},
+ "uuid-name": "second"},
+ {"op": "select",
+ "table": "ordinals",
+ "where": [["_uuid", "==", ["named-uuid", "first"]]]},
+ {"op": "select",
+ "table": "ordinals",
+ "where": [["_uuid", "==", ["named-uuid", "second"]]]}]]]],
+ [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<2>"],"name":"zero","number":0}]},{"rows":[{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}]
+]])
+
+OVSDB_CHECK_EXECUTION([insert rows, update rows by value],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"},
+ "uuid-name": "first"}]]],
+ [[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"},
+ "uuid-name": "first"}]]],
+ [[[{"op": "update",
+ "table": "ordinals",
+ "where": [["name", "==", "zero"]],
+ "row": {"name": "nought"}}]]],
+ [[[{"op": "select",
+ "table": "ordinals",
+ "where": [],
+ "sort": ["number"]}]]]],
+ [[[{"uuid":["uuid","<0>"]}]
+[{"uuid":["uuid","<1>"]}]
+[{"count":1}]
+[{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<2>"],"name":"nought","number":0},{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}]
+]])
+
+OVSDB_CHECK_EXECUTION([insert rows, delete by named-uuid],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"},
+ "uuid-name": "first"},
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"},
+ "uuid-name": "second"},
+ {"op": "delete",
+ "table": "ordinals",
+ "where": [["_uuid", "==", ["named-uuid", "first"]]]},
+ {"op": "select",
+ "table": "ordinals",
+ "where": [],
+ "columns": ["name","number"]}]]]],
+ [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"count":1},{"rows":[{"name":"one","number":1}]}]
+]])
+
+OVSDB_CHECK_EXECUTION([insert rows, delete rows by value],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"},
+ "uuid-name": "first"}]]],
+ [[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"},
+ "uuid-name": "first"}]]],
+ [[[{"op": "delete",
+ "table": "ordinals",
+ "where": [["name", "==", "zero"]]}]]],
+ [[[{"op": "select",
+ "table": "ordinals",
+ "where": []}]]]],
+ [[[{"uuid":["uuid","<0>"]}]
+[{"uuid":["uuid","<1>"]}]
+[{"count":1}]
+[{"rows":[{"_uuid":["uuid","<1>"],"_version":["uuid","<2>"],"name":"one","number":1}]}]
+]])
+
+OVSDB_CHECK_EXECUTION([insert rows, delete by (non-matching) value],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"},
+ "uuid-name": "first"}]]],
+ [[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"},
+ "uuid-name": "first"}]]],
+ [[[{"op": "delete",
+ "table": "ordinals",
+ "where": [["name", "==", "nought"]]}]]],
+ [[[{"op": "select",
+ "table": "ordinals",
+ "where": [],
+ "sort": ["number"]}]]]],
+ [[[{"uuid":["uuid","<0>"]}]
+[{"uuid":["uuid","<1>"]}]
+[{"count":0}]
+[{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<2>"],"name":"zero","number":0},{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}]
+]])
+
+OVSDB_CHECK_EXECUTION([insert rows, delete all],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"},
+ "uuid-name": "first"},
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"},
+ "uuid-name": "second"},
+ {"op": "delete",
+ "table": "ordinals",
+ "where": []},
+ {"op": "select",
+ "table": "ordinals",
+ "where": [],
+ "columns": ["name","number"]}]]]],
+ [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"count":2},{"rows":[]}]
+]])
+
+OVSDB_CHECK_EXECUTION([insert row, query table, commit],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}},
+ {"op": "select",
+ "table": "ordinals",
+ "where": []},
+ {"op": "commit",
+ "durable": false}]]]],
+ [[[{"uuid":["uuid","<0>"]},{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<1>"],"name":"zero","number":0}]},{}]
+]])
+
+OVSDB_CHECK_EXECUTION([insert row, query table, commit durably],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}},
+ {"op": "select",
+ "table": "ordinals",
+ "where": []},
+ {"op": "commit",
+ "durable": true}]]]],
+ [[[{"uuid":["uuid","<0>"]},{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<1>"],"name":"zero","number":0}]},{}]
+]])
+
+OVSDB_CHECK_EXECUTION([equality wait with correct rows],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}},
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"}},
+ {"op": "wait",
+ "timeout": 0,
+ "table": "ordinals",
+ "where": [],
+ "columns": ["name", "number"],
+ "until": "==",
+ "rows": [{"name": "zero", "number": 0},
+ {"name": "one", "number": 1}]}]]]],
+ [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{}]
+]])
+
+OVSDB_CHECK_EXECUTION([equality wait with extra row],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}},
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"}},
+ {"op": "wait",
+ "timeout": 0,
+ "table": "ordinals",
+ "where": [],
+ "columns": ["name", "number"],
+ "until": "==",
+ "rows": [{"name": "zero", "number": 0},
+ {"name": "one", "number": 1},
+ {"name": "two", "number": 2}]}]]]],
+ [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"details":"\"wait\" timed out","error":"timed out"}]
+]])
+
+OVSDB_CHECK_EXECUTION([equality wait with missing row],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}},
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"}},
+ {"op": "wait",
+ "timeout": 0,
+ "table": "ordinals",
+ "where": [],
+ "columns": ["name", "number"],
+ "until": "==",
+ "rows": [{"name": "one", "number": 1}]}]]]],
+ [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"details":"\"wait\" timed out","error":"timed out"}]
+]])
+
+OVSDB_CHECK_EXECUTION([inequality wait with correct rows],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}},
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"}},
+ {"op": "wait",
+ "timeout": 0,
+ "table": "ordinals",
+ "where": [],
+ "columns": ["name", "number"],
+ "until": "!=",
+ "rows": [{"name": "zero", "number": 0},
+ {"name": "one", "number": 1}]}]]]],
+ [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"details":"\"wait\" timed out","error":"timed out"}]
+]])
+
+OVSDB_CHECK_EXECUTION([inequality wait with extra row],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}},
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"}},
+ {"op": "wait",
+ "timeout": 0,
+ "table": "ordinals",
+ "where": [],
+ "columns": ["name", "number"],
+ "until": "!=",
+ "rows": [{"name": "zero", "number": 0},
+ {"name": "one", "number": 1},
+ {"name": "two", "number": 2}]}]]]],
+ [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{}]
+]])
+
+OVSDB_CHECK_EXECUTION([inequality wait with missing row],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}},
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"}},
+ {"op": "wait",
+ "timeout": 0,
+ "table": "ordinals",
+ "where": [],
+ "columns": ["name", "number"],
+ "until": "!=",
+ "rows": [{"name": "one", "number": 1}]}]]]],
+ [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{}]
+]])
+])
+
+EXECUTION_EXAMPLES
--- /dev/null
+AT_BANNER([OVSDB -- file storage])
+
+# OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS])
+#
+# Creates a database with the given SCHEMA and runs each of the
+# TRANSACTIONS (which should be a quoted list of quoted strings)
+# against it with ovsdb-tool one at a time.
+#
+# Checks that the overall output is OUTPUT, but UUIDs in the output
+# are replaced by markers of the form <N> where N is a number. The
+# first unique UUID is replaced by <0>, the next by <1>, and so on.
+# If a given UUID appears more than once it is always replaced by the
+# same marker.
+#
+# TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
+m4_define([OVSDB_CHECK_EXECUTION],
+ [AT_SETUP([$1])
+ AT_KEYWORDS([ovsdb file positive $5])
+ AT_DATA([schema], [$2
+])
+ touch .db.~lock~
+ OVS_CHECK_LCOV([ovsdb-tool create db schema], [0], [stdout], [ignore])
+ m4_foreach([txn], [$3],
+ [OVS_CHECK_LCOV([ovsdb-tool transact db 'txn'], [0], [stdout], [ignore])
+cat stdout >> output
+])
+ AT_CHECK([perl $srcdir/uuidfilt.pl output], [0], [$4])
+ AT_CLEANUP])
+
+EXECUTION_EXAMPLES
--- /dev/null
+AT_BANNER([OVSDB -- interface description language (IDL)])
+
+# OVSDB_CHECK_IDL(TITLE, [PRE-IDL-TXN], TRANSACTIONS, OUTPUT, [KEYWORDS])
+#
+# Creates a database with a schema derived from idltest.ovsidl, runs
+# each PRE-IDL-TXN (if any), starts an ovsdb-server on that database,
+# and runs "test-ovsdb idl" passing each of the TRANSACTIONS along.
+#
+# Checks that the overall output is OUTPUT. Before comparison, the
+# output is sorted (using "sort") and UUIDs in the output are replaced
+# by markers of the form <N> where N is a number. The first unique
+# UUID is replaced by <0>, the next by <1>, and so on. If a given
+# UUID appears more than once it is always replaced by the same
+# marker.
+#
+# TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
+m4_define([OVSDB_CHECK_IDL],
+ [AT_SETUP([$1])
+ AT_KEYWORDS([ovsdb server idl positive $5])
+ OVS_CHECK_LCOV([ovsdb-tool create db $abs_builddir/idltest.ovsschema],
+ [0], [stdout], [ignore])
+ AT_CHECK([ovsdb-server --detach --pidfile=$PWD/server-pid --listen=punix:socket --unixctl=$PWD/unixctl db])
+ m4_if([$2], [], [],
+ [OVS_CHECK_LCOV([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore], [kill `cat server-pid`])])
+ AT_CHECK([test-ovsdb -vjsonrpc -t10 idl unix:socket $3],
+ [0], [stdout], [ignore], [kill `cat server-pid`])
+ AT_CHECK([sort stdout | perl $srcdir/uuidfilt.pl], [0], [$4], [],
+ [kill `cat server-pid`])
+ kill `cat server-pid`
+ AT_CLEANUP])
+
+OVSDB_CHECK_IDL([simple idl, initially empty, no ops],
+ [],
+ [],
+ [000: empty
+001: done
+])
+
+OVSDB_CHECK_IDL([simple idl, initially empty, various ops],
+ [],
+ [['[{"op": "insert",
+ "table": "simple",
+ "row": {"i": 1,
+ "r": 2.0,
+ "b": true,
+ "s": "mystring",
+ "u": ["uuid", "84f5c8f5-ac76-4dbc-a24f-8860eb407fc1"],
+ "ia": ["set", [1, 2, 3]],
+ "ra": ["set", [-0.5]],
+ "ba": ["set", [true, false]],
+ "sa": ["set", ["abc", "def"]],
+ "ua": ["set", [["uuid", "69443985-7806-45e2-b35f-574a04e720f9"],
+ ["uuid", "aad11ef0-816a-4b01-93e6-03b8b4256b98"]]]}},
+ {"op": "insert",
+ "table": "simple",
+ "row": {}}]' \
+ '[{"op": "update",
+ "table": "simple",
+ "where": [],
+ "row": {"b": true}}]' \
+ '[{"op": "update",
+ "table": "simple",
+ "where": [],
+ "row": {"r": 123.5}}]' \
+ '[{"op": "insert",
+ "table": "simple",
+ "row": {"i": -1,
+ "r": 125,
+ "b": false,
+ "s": "",
+ "ia": ["set", [1]],
+ "ra": ["set", [1.5]],
+ "ba": ["set", [false]],
+ "sa": ["set", []],
+ "ua": ["set", []]}}]' \
+ '[{"op": "update",
+ "table": "simple",
+ "where": [["i", "<", 1]],
+ "row": {"s": "newstring"}}]' \
+ '[{"op": "delete",
+ "table": "simple",
+ "where": [["i", "==", 0]]}]' \
+ 'reconnect']],
+ [[000: empty
+001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}]}
+002: i=0 r=0 b=false s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+002: i=1 r=2 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+003: {"error":null,"result":[{"count":2}]}
+004: i=0 r=0 b=true s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+004: i=1 r=2 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+005: {"error":null,"result":[{"count":2}]}
+006: i=0 r=123.5 b=true s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+006: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+007: {"error":null,"result":[{"uuid":["uuid","<6>"]}]}
+008: i=-1 r=125 b=false s= u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6>
+008: i=0 r=123.5 b=true s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+008: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+009: {"error":null,"result":[{"count":2}]}
+010: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6>
+010: i=0 r=123.5 b=true s=newstring u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+010: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+011: {"error":null,"result":[{"count":1}]}
+012: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6>
+012: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+013: reconnect
+014: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6>
+014: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+015: done
+]])
+
+OVSDB_CHECK_IDL([simple idl, initially populated],
+ [['[{"op": "insert",
+ "table": "simple",
+ "row": {"i": 1,
+ "r": 2.0,
+ "b": true,
+ "s": "mystring",
+ "u": ["uuid", "84f5c8f5-ac76-4dbc-a24f-8860eb407fc1"],
+ "ia": ["set", [1, 2, 3]],
+ "ra": ["set", [-0.5]],
+ "ba": ["set", [true, false]],
+ "sa": ["set", ["abc", "def"]],
+ "ua": ["set", [["uuid", "69443985-7806-45e2-b35f-574a04e720f9"],
+ ["uuid", "aad11ef0-816a-4b01-93e6-03b8b4256b98"]]]}},
+ {"op": "insert",
+ "table": "simple",
+ "row": {}}]']],
+ [['[{"op": "update",
+ "table": "simple",
+ "where": [],
+ "row": {"b": true}}]']],
+ [[000: i=0 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+000: i=1 r=2 b=true s=mystring u=<2> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<3> <4>] uuid=<5>
+001: {"error":null,"result":[{"count":2}]}
+002: i=0 r=0 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+002: i=1 r=2 b=true s=mystring u=<2> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<3> <4>] uuid=<5>
+003: done
+]])
+
+OVSDB_CHECK_IDL([self-linking idl, consistent ops],
+ [],
+ [['[{"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 0, "k": ["named-uuid", "self"]},
+ "uuid-name": "self"}]' \
+ '[{"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 1},
+ "uuid-name": "row1"},
+ {"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 2, "k": ["named-uuid", "row1"]},
+ "uuid-name": "row2"},
+ {"op": "update",
+ "table": "selfLink",
+ "where": [["i", "==", 1]],
+ "row": {"k": ["named-uuid", "row2"]}}]' \
+ '[{"op": "update",
+ "table": "selfLink",
+ "where": [["i", "==", 1]],
+ "row": {"k": ["uuid", "#1#"]}}]' \
+ '[{"op": "update",
+ "table": "selfLink",
+ "where": [],
+ "row": {"k": ["uuid", "#0#"]}}]']],
+ [[000: empty
+001: {"error":null,"result":[{"uuid":["uuid","<0>"]}]}
+002: i=0 k=0 ka=[] uuid=<0>
+003: {"error":null,"result":[{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]},{"count":1}]}
+004: i=0 k=0 ka=[] uuid=<0>
+004: i=1 k=2 ka=[] uuid=<1>
+004: i=2 k=1 ka=[] uuid=<2>
+005: {"error":null,"result":[{"count":1}]}
+006: i=0 k=0 ka=[] uuid=<0>
+006: i=1 k=1 ka=[] uuid=<1>
+006: i=2 k=1 ka=[] uuid=<2>
+007: {"error":null,"result":[{"count":3}]}
+008: i=0 k=0 ka=[] uuid=<0>
+008: i=1 k=0 ka=[] uuid=<1>
+008: i=2 k=0 ka=[] uuid=<2>
+009: done
+]])
+
+OVSDB_CHECK_IDL([self-linking idl, inconsistent ops],
+ [],
+ [['[{"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 0, "k": ["uuid", "cf197cc5-c8c9-42f5-82d5-c71a9f2cb96b"]}}]' \
+ '[{"op": "update",
+ "table": "selfLink",
+ "where": [],
+ "row": {"k": ["uuid", "#0#"]}}]' \
+ '[{"op": "update",
+ "table": "selfLink",
+ "where": [],
+ "row": {"k": ["uuid", "c2fca39a-e69a-42a4-9c56-5eca85839ce9"]}}]' \
+ '[{"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 1, "k": ["uuid", "52d752a3-b062-4668-9446-d2e0d4a14703"]}}]' \
+ '[{"op": "update",
+ "table": "selfLink",
+ "where": [],
+ "row": {"k": ["uuid", "#1#"]}}]' \
+]],
+ [[000: empty
+001: {"error":null,"result":[{"uuid":["uuid","<0>"]}]}
+002: i=0 k= ka=[] uuid=<0>
+003: {"error":null,"result":[{"count":1}]}
+004: i=0 k=0 ka=[] uuid=<0>
+005: {"error":null,"result":[{"count":1}]}
+006: i=0 k= ka=[] uuid=<0>
+007: {"error":null,"result":[{"uuid":["uuid","<1>"]}]}
+008: i=0 k= ka=[] uuid=<0>
+008: i=1 k= ka=[] uuid=<1>
+009: {"error":null,"result":[{"count":2}]}
+010: i=0 k=1 ka=[] uuid=<0>
+010: i=1 k=1 ka=[] uuid=<1>
+011: done
+]])
+
+OVSDB_CHECK_IDL([self-linking idl, sets],
+ [],
+ [['[{"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 0, "ka": ["set", [["named-uuid", "i0"]]]},
+ "uuid-name": "i0"},
+ {"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 1, "ka": ["set", [["named-uuid", "i1"]]]},
+ "uuid-name": "i1"},
+ {"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 2, "ka": ["set", [["named-uuid", "i2"]]]},
+ "uuid-name": "i2"},
+ {"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 3, "ka": ["set", [["named-uuid", "i3"]]]},
+ "uuid-name": "i3"}]' \
+ '[{"op": "update",
+ "table": "selfLink",
+ "where": [],
+ "row": {"ka": ["set", [["uuid", "#0#"], ["uuid", "#1#"], ["uuid", "#2#"], ["uuid", "#3#"]]]}}]' \
+ '[{"op": "update",
+ "table": "selfLink",
+ "where": [],
+ "row": {"ka": ["set", [["uuid", "#0#"], ["uuid", "88702e78-845b-4a6e-ad08-cf68922ae84a"], ["uuid", "#2#"], ["uuid", "1ac2b12e-b767-4805-a55d-43976e40c465"]]]}}]']],
+ [[000: empty
+001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]},{"uuid":["uuid","<3>"]}]}
+002: i=0 k= ka=[0] uuid=<0>
+002: i=1 k= ka=[1] uuid=<1>
+002: i=2 k= ka=[2] uuid=<2>
+002: i=3 k= ka=[3] uuid=<3>
+003: {"error":null,"result":[{"count":4}]}
+004: i=0 k= ka=[0 1 2 3] uuid=<0>
+004: i=1 k= ka=[0 1 2 3] uuid=<1>
+004: i=2 k= ka=[0 1 2 3] uuid=<2>
+004: i=3 k= ka=[0 1 2 3] uuid=<3>
+005: {"error":null,"result":[{"count":4}]}
+006: i=0 k= ka=[0 2] uuid=<0>
+006: i=1 k= ka=[0 2] uuid=<1>
+006: i=2 k= ka=[0 2] uuid=<2>
+006: i=3 k= ka=[0 2] uuid=<3>
+007: done
+]])
+
+# XXX self-linking idl, maps
--- /dev/null
+AT_BANNER([OVSDB -- logging])
+
+AT_SETUP([create empty, reread])
+AT_KEYWORDS([ovsdb log])
+AT_CAPTURE_FILE([log])
+OVS_CHECK_LCOV(
+ [test-ovsdb log-io file 'O_CREAT|O_RDWR'], [0],
+ [file: open successful
+], [ignore])
+OVS_CHECK_LCOV(
+ [test-ovsdb log-io file 'O_RDONLY' read], [0],
+ [file: open successful
+file: read: end of log
+], [ignore])
+AT_CHECK([test -f .file.~lock~])
+AT_CLEANUP
+
+AT_SETUP([write one, reread])
+AT_KEYWORDS([ovsdb log])
+AT_CAPTURE_FILE([file])
+OVS_CHECK_LCOV(
+ [[test-ovsdb log-io file 'O_CREAT|O_RDWR' 'write:[0]']], [0],
+ [[file: open successful
+file: write:[0] successful
+]], [ignore])
+OVS_CHECK_LCOV(
+ [test-ovsdb log-io file 'O_RDONLY' read read], [0],
+ [[file: open successful
+file: read: [0]
+file: read: end of log
+]], [ignore])
+AT_CHECK([test -f .file.~lock~])
+AT_CLEANUP
+
+AT_SETUP([check that O_EXCL works])
+AT_KEYWORDS([ovsdb log])
+AT_CAPTURE_FILE([file])
+OVS_CHECK_LCOV(
+ [[test-ovsdb log-io file 'O_CREAT|O_RDWR' 'write:[1]']], [0],
+ [[file: open successful
+file: write:[1] successful
+]], [ignore])
+OVS_CHECK_LCOV(
+ [test-ovsdb log-io file 'O_RDONLY' read], [0],
+ [[file: open successful
+file: read: [1]
+]], [ignore])
+OVS_CHECK_LCOV(
+ [test-ovsdb log-io file 'O_CREAT|O_RDWR|O_EXCL' read], [1],
+ [], [test-ovsdb: I/O error: create: file failed (File exists)
+])
+AT_CHECK([test -f .file.~lock~])
+AT_CLEANUP
+
+AT_SETUP([write one, reread])
+AT_KEYWORDS([ovsdb log])
+AT_CAPTURE_FILE([file])
+OVS_CHECK_LCOV(
+ [[test-ovsdb log-io file 'O_CREAT|O_RDWR' 'write:[0]' 'write:[1]' 'write:[2]']], [0],
+ [[file: open successful
+file: write:[0] successful
+file: write:[1] successful
+file: write:[2] successful
+]], [ignore])
+OVS_CHECK_LCOV(
+ [test-ovsdb log-io file 'O_RDONLY' read read read read], [0],
+ [[file: open successful
+file: read: [0]
+file: read: [1]
+file: read: [2]
+file: read: end of log
+]], [ignore])
+AT_CHECK([test -f .file.~lock~])
+AT_CLEANUP
+
+AT_SETUP([write one, reread, append])
+AT_KEYWORDS([ovsdb log])
+AT_CAPTURE_FILE([file])
+OVS_CHECK_LCOV(
+ [[test-ovsdb log-io file 'O_CREAT|O_RDWR' 'write:[0]' 'write:[1]' 'write:[2]']], [0],
+ [[file: open successful
+file: write:[0] successful
+file: write:[1] successful
+file: write:[2] successful
+]], [ignore])
+OVS_CHECK_LCOV(
+ [[test-ovsdb log-io file 'O_RDWR' read read read 'write:["append"]']], [0],
+ [[file: open successful
+file: read: [0]
+file: read: [1]
+file: read: [2]
+file: write:["append"] successful
+]], [ignore])
+OVS_CHECK_LCOV(
+ [test-ovsdb log-io file 'O_RDONLY' read read read read read], [0],
+ [[file: open successful
+file: read: [0]
+file: read: [1]
+file: read: [2]
+file: read: ["append"]
+file: read: end of log
+]], [ignore])
+AT_CHECK([test -f .file.~lock~])
+AT_CLEANUP
+
+AT_SETUP([write, reread one, overwrite])
+AT_KEYWORDS([ovsdb log])
+AT_CAPTURE_FILE([file])
+OVS_CHECK_LCOV(
+ [[test-ovsdb log-io file 'O_CREAT|O_RDWR' 'write:[0]' 'write:[1]' 'write:[2]']], [0],
+ [[file: open successful
+file: write:[0] successful
+file: write:[1] successful
+file: write:[2] successful
+]], [ignore])
+OVS_CHECK_LCOV(
+ [[test-ovsdb log-io file 'O_RDWR' read 'write:["more data"]']], [0],
+ [[file: open successful
+file: read: [0]
+file: write:["more data"] successful
+]], [ignore])
+OVS_CHECK_LCOV(
+ [test-ovsdb log-io file 'O_RDONLY' read read read], [0],
+ [[file: open successful
+file: read: [0]
+file: read: ["more data"]
+file: read: end of log
+]], [ignore])
+AT_CHECK([test -f .file.~lock~])
+AT_CLEANUP
+
+AT_SETUP([write, add corrupted data, read])
+AT_KEYWORDS([ovsdb log])
+AT_CAPTURE_FILE([file])
+OVS_CHECK_LCOV(
+ [[test-ovsdb log-io file 'O_CREAT|O_RDWR' 'write:[0]' 'write:[1]' 'write:[2]']], [0],
+ [[file: open successful
+file: write:[0] successful
+file: write:[1] successful
+file: write:[2] successful
+]], [ignore])
+AT_CHECK([echo 'xxx' >> file])
+OVS_CHECK_LCOV(
+ [test-ovsdb log-io file 'O_RDONLY' read read read read], [0],
+ [[file: open successful
+file: read: [0]
+file: read: [1]
+file: read: [2]
+file: read failed: syntax error: file: parse error at offset 174 in header line "xxx"
+]], [ignore])
+AT_CHECK([test -f .file.~lock~])
+AT_CLEANUP
+
+AT_SETUP([write, add corrupted data, read, overwrite])
+AT_KEYWORDS([ovsdb log])
+AT_CAPTURE_FILE([file])
+OVS_CHECK_LCOV(
+ [[test-ovsdb log-io file 'O_CREAT|O_RDWR' 'write:[0]' 'write:[1]' 'write:[2]']], [0],
+ [[file: open successful
+file: write:[0] successful
+file: write:[1] successful
+file: write:[2] successful
+]], [ignore])
+AT_CHECK([echo 'xxx' >> file])
+OVS_CHECK_LCOV(
+ [[test-ovsdb log-io file 'O_RDWR' read read read read 'write:[3]']], [0],
+ [[file: open successful
+file: read: [0]
+file: read: [1]
+file: read: [2]
+file: read failed: syntax error: file: parse error at offset 174 in header line "xxx"
+file: write:[3] successful
+]], [ignore])
+OVS_CHECK_LCOV(
+ [test-ovsdb log-io file 'O_RDONLY' read read read read read], [0],
+ [[file: open successful
+file: read: [0]
+file: read: [1]
+file: read: [2]
+file: read: [3]
+file: read: end of log
+]], [ignore])
+AT_CHECK([test -f .file.~lock~])
+AT_CLEANUP
+
+AT_SETUP([write, corrupt some data, read, overwrite])
+AT_KEYWORDS([ovsdb log])
+AT_CAPTURE_FILE([file])
+OVS_CHECK_LCOV(
+ [[test-ovsdb log-io file 'O_CREAT|O_RDWR' 'write:[0]' 'write:[1]' 'write:[2]']], [0],
+ [[file: open successful
+file: write:[0] successful
+file: write:[1] successful
+file: write:[2] successful
+]], [ignore])
+AT_CHECK([[sed 's/\[2]/[3]/' < file > file.tmp]])
+AT_CHECK([mv file.tmp file])
+AT_CHECK([[grep -c '\[3]' file]], [0], [1
+])
+OVS_CHECK_LCOV(
+ [[test-ovsdb log-io file 'O_RDWR' read read read 'write:["longer data"]']], [0],
+ [[file: open successful
+file: read: [0]
+file: read: [1]
+file: read failed: syntax error: file: 4 bytes starting at offset 170 have SHA-1 hash 5c031e5c0d3a9338cc127ebe40bb2748b6a67e78 but should have hash 98f55556e7ffd432381b56a19bd485b3e6446442
+file: write:["longer data"] successful
+]], [ignore])
+OVS_CHECK_LCOV(
+ [test-ovsdb log-io file 'O_RDONLY' read read read read], [0],
+ [[file: open successful
+file: read: [0]
+file: read: [1]
+file: read: ["longer data"]
+file: read: end of log
+]], [ignore])
+AT_CHECK([test -f .file.~lock~])
+AT_CLEANUP
+
+AT_SETUP([write, truncate file, read, overwrite])
+AT_KEYWORDS([ovsdb log])
+AT_CAPTURE_FILE([file])
+OVS_CHECK_LCOV(
+ [[test-ovsdb log-io file 'O_CREAT|O_RDWR' 'write:[0]' 'write:[1]' 'write:[2]']], [0],
+ [[file: open successful
+file: write:[0] successful
+file: write:[1] successful
+file: write:[2] successful
+]], [ignore])
+AT_CHECK([[sed 's/\[2]/2/' < file > file.tmp]])
+AT_CHECK([mv file.tmp file])
+AT_CHECK([[grep -c '^2$' file]], [0], [1
+])
+OVS_CHECK_LCOV(
+ [[test-ovsdb log-io file 'O_RDWR' read read read 'write:["longer data"]']], [0],
+ [[file: open successful
+file: read: [0]
+file: read: [1]
+file: read failed: I/O error: file: error reading 4 bytes starting at offset 170 (unexpected end of file)
+file: write:["longer data"] successful
+]], [ignore])
+OVS_CHECK_LCOV(
+ [test-ovsdb log-io file 'O_RDONLY' read read read read], [0],
+ [[file: open successful
+file: read: [0]
+file: read: [1]
+file: read: ["longer data"]
+file: read: end of log
+]], [ignore])
+AT_CHECK([test -f .file.~lock~])
+AT_CLEANUP
+
+AT_SETUP([write bad JSON, read, overwrite])
+AT_KEYWORDS([ovsdb log])
+AT_CAPTURE_FILE([file])
+OVS_CHECK_LCOV(
+ [[test-ovsdb log-io file 'O_CREAT|O_RDWR' 'write:[0]' 'write:[1]' 'write:[2]']], [0],
+ [[file: open successful
+file: write:[0] successful
+file: write:[1] successful
+file: write:[2] successful
+]], [ignore])
+AT_CHECK([[printf '%s\n%s\n' 'OVSDB JSON 5 d910b02871075d3156ec8675dfc95b7d5d640aa6' 'null' >> file]])
+OVS_CHECK_LCOV(
+ [[test-ovsdb log-io file 'O_RDWR' read read read read 'write:["replacement data"]']], [0],
+ [[file: open successful
+file: read: [0]
+file: read: [1]
+file: read: [2]
+file: read failed: syntax error: file: 5 bytes starting at offset 228 are not valid JSON (syntax error at beginning of input)
+file: write:["replacement data"] successful
+]], [ignore])
+OVS_CHECK_LCOV(
+ [test-ovsdb log-io file 'O_RDONLY' read read read read read], [0],
+ [[file: open successful
+file: read: [0]
+file: read: [1]
+file: read: [2]
+file: read: ["replacement data"]
+file: read: end of log
+]], [ignore])
+AT_CHECK([test -f .file.~lock~])
+AT_CLEANUP
--- /dev/null
+AT_BANNER([OVSDB -- ovsdb-server monitors])
+
+# OVSDB_CHECK_MONITOR(TITLE, SCHEMA, [PRE-MONITOR-TXN], MONITOR-ARGS,
+# TRANSACTIONS, OUTPUT, [KEYWORDS])
+#
+# Creates a database with the given SCHEMA, starts an ovsdb-server on
+# that database, and runs each of the TRANSACTIONS (which should be a
+# quoted list of quoted strings) against it with ovsdb-client one at a
+# time.
+#
+# Checks that the overall output is OUTPUT, but UUIDs in the output
+# are replaced by markers of the form <N> where N is a number. The
+# first unique UUID is replaced by <0>, the next by <1>, and so on.
+# If a given UUID appears more than once it is always replaced by the
+# same marker.
+#
+# TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
+m4_define([OVSDB_CHECK_MONITOR],
+ [AT_SETUP([$1])
+ AT_KEYWORDS([ovsdb server monitor positive $7])
+ AT_DATA([schema], [$2
+])
+ OVS_CHECK_LCOV([ovsdb-tool create db schema], [0], [stdout], [ignore])
+ m4_foreach([txn], [$3],
+ [OVS_CHECK_LCOV([ovsdb-tool transact db 'txn'], [0], [ignore], [ignore])])
+ AT_CHECK([ovsdb-server --detach --pidfile=$PWD/server-pid --listen=punix:socket --unixctl=$PWD/unixctl db])
+ AT_CHECK([ovsdb-client monitor --format=csv unix:socket $4 > output & echo $! > monitor-pid],
+ [0], [ignore], [ignore], [kill `cat server-pid`])
+ m4_foreach([txn], [$5],
+ [OVS_CHECK_LCOV([ovsdb-client transact unix:socket 'txn'], [0],
+ [ignore], [ignore], [kill `cat server-pid monitor-pid`])])
+ AT_CHECK([ovs-appctl -t $PWD/unixctl -e exit], [0], [ignore], [ignore])
+ wait
+ AT_CHECK([perl $srcdir/uuidfilt.pl output], [0], [$6])
+ AT_CLEANUP])
+
+OVSDB_CHECK_MONITOR([monitor insert into empty table],
+ [ORDINAL_SCHEMA],
+ [],
+ [ordinals],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}}]]]],
+ [[row,action,name,number,_version
+<0>,insert,"""zero""",0,"[""uuid"",""<1>""]"
+]])
+
+OVSDB_CHECK_MONITOR([monitor insert into populated table],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 10, "name": "ten"}}]]]],
+ [ordinals],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}}]]]],
+ [[row,action,name,number,_version
+<0>,initial,"""ten""",10,"[""uuid"",""<1>""]"
+row,action,name,number,_version
+<2>,insert,"""zero""",0,"[""uuid"",""<3>""]"
+]])
+
+OVSDB_CHECK_MONITOR([monitor delete],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 10, "name": "ten"}}]]]],
+ [ordinals],
+ [[[[{"op": "delete",
+ "table": "ordinals",
+ "where": [["number", "==", 10]]}]]]],
+ [[row,action,name,number,_version
+<0>,initial,"""ten""",10,"[""uuid"",""<1>""]"
+row,action,name,number,_version
+<0>,delete,"""ten""",10,"[""uuid"",""<1>""]"
+]])
+
+OVSDB_CHECK_MONITOR([monitor row update],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 10, "name": "ten"}}]]]],
+ [ordinals],
+ [[[[{"op": "update",
+ "table": "ordinals",
+ "where": [["number", "==", 10]],
+ "row": {"name": "five plus five"}}]]]],
+ [[row,action,name,number,_version
+<0>,initial,"""ten""",10,"[""uuid"",""<1>""]"
+row,action,name,number,_version
+<0>,old,"""ten""",,"[""uuid"",""<1>""]"
+,new,"""five plus five""",10,"[""uuid"",""<2>""]"
+]])
+
+OVSDB_CHECK_MONITOR([monitor no-op row updates],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 10, "name": "ten"}}]]]],
+ [ordinals],
+ [[[[{"op": "update",
+ "table": "ordinals",
+ "where": [["number", "==", 10]],
+ "row": {"number": 10, "name": "ten"}}]]],
+ [[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 9, "name": "nine"}}]]]],
+ [[row,action,name,number,_version
+<0>,initial,"""ten""",10,"[""uuid"",""<1>""]"
+row,action,name,number,_version
+<2>,insert,"""nine""",9,"[""uuid"",""<3>""]"
+]])
+
+OVSDB_CHECK_MONITOR([monitor insert-and-update transaction],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 10, "name": "ten"}}]]]],
+ [ordinals],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 9, "name": "nine"},
+ "uuid-name": "nine"},
+ {"op": "update",
+ "table": "ordinals",
+ "where": [["_uuid", "==", ["named-uuid", "nine"]]],
+ "row": {"name": "three squared"}}]]]],
+ [[row,action,name,number,_version
+<0>,initial,"""ten""",10,"[""uuid"",""<1>""]"
+row,action,name,number,_version
+<2>,insert,"""three squared""",9,"[""uuid"",""<3>""]"
+]])
+
+
+OVSDB_CHECK_MONITOR([monitor insert-update-and-delete transaction],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 10, "name": "ten"}}]]]],
+ [ordinals],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 9, "name": "nine"},
+ "uuid-name": "nine"},
+ {"op": "update",
+ "table": "ordinals",
+ "where": [["_uuid", "==", ["named-uuid", "nine"]]],
+ "row": {"name": "three squared"}},
+ {"op": "delete",
+ "table": "ordinals",
+ "where": [["_uuid", "==", ["named-uuid", "nine"]]]},
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 7, "name": "seven"}}]]]],
+ [[row,action,name,number,_version
+<0>,initial,"""ten""",10,"[""uuid"",""<1>""]"
+row,action,name,number,_version
+<2>,insert,"""seven""",7,"[""uuid"",""<3>""]"
+]])
+
--- /dev/null
+AT_BANNER([OVSDB -- queries])
+
+OVSDB_CHECK_POSITIVE([queries on scalars],
+ [[query \
+ '{"columns":
+ {"i": {"type": "integer"},
+ "r": {"type": "real"},
+ "b": {"type": "boolean"},
+ "s": {"type": "string"},
+ "u": {"type": "uuid"}}}' \
+ '[{"i": 0,
+ "r": 0.5,
+ "b": true,
+ "s": "a",
+ "u": ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]},
+ {"i": 1,
+ "r": 1.5,
+ "b": false,
+ "s": "b",
+ "u": ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]},
+ {"i": 2,
+ "r": 2.5,
+ "b": true,
+ "s": "c",
+ "u": ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]},
+ {"i": 3,
+ "r": 3.5,
+ "b": false,
+ "s": "d",
+ "u": ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]},
+ {"i": 4,
+ "r": 4.5,
+ "b": true,
+ "s": "e",
+ "u": ["uuid", "4a5127e2-0256-4a72-a7dc-6246213967c7"]}]' \
+ '[[],
+ [["i", "==", 0]],
+ [["i", "!=", 1]],
+ [["i", "<", 2]],
+ [["i", "<=", 3]],
+ [["i", ">", 2]],
+ [["i", ">=", 4]],
+ [["i", "includes", 3]],
+ [["i", "excludes", 2]],
+ [["r", "==", 0.5]],
+ [["r", "!=", 1.5]],
+ [["r", "<", 2.5]],
+ [["r", "<=", 3.5]],
+ [["r", ">", 4.5]],
+ [["r", ">=", 5.5]],
+ [["r", "includes", 1]],
+ [["r", "excludes", 3]],
+ [["b", "==", true]],
+ [["b", "!=", true]],
+ [["b", "includes", false]],
+ [["b", "excludes", true]],
+ [["s", "==", "a"]],
+ [["s", "!=", "b"]],
+ [["s", "includes", "c"]],
+ [["s", "excludes", "d"]],
+ [["u", "==", ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]],
+ [["u", "!=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]],
+ [["u", "includes",["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]]']],
+ [dnl
+query 0: 11111
+query 1: 1----
+query 2: 1-111
+query 3: 11---
+query 4: 1111-
+query 5: ---11
+query 6: ----1
+query 7: ---1-
+query 8: 11-11
+query 9: 1----
+query 10: 1-111
+query 11: 11---
+query 12: 1111-
+query 13: -----
+query 14: -----
+query 15: -----
+query 16: 11111
+query 17: 1-1-1
+query 18: -1-1-
+query 19: -1-1-
+query 20: -1-1-
+query 21: 1----
+query 22: 1-111
+query 23: --1--
+query 24: 111-1
+query 25: 1----
+query 26: 1-111
+query 27: --1--],
+ [query])
+
+OVSDB_CHECK_POSITIVE([queries on sets],
+ [[query \
+ '{"columns": {"i": {"type": {"key": "integer", "min": 0, "max": "unlimited"}}}}' \
+ '[{"i": ["set", []]},
+ {"i": ["set", [0]]},
+ {"i": ["set", [1]]},
+ {"i": ["set", [0, 1]]},
+ {"i": ["set", [2]]},
+ {"i": ["set", [2, 0]]},
+ {"i": ["set", [2, 1]]},
+ {"i": ["set", [2, 1, 0]]}]' \
+ '[[],
+ [["i", "==", ["set", []]]],
+ [["i", "==", ["set", [0]]]],
+ [["i", "==", ["set", [1]]]],
+ [["i", "==", ["set", [0, 1]]]],
+ [["i", "==", ["set", [2]]]],
+ [["i", "==", ["set", [2, 0]]]],
+ [["i", "==", ["set", [2, 1]]]],
+ [["i", "==", ["set", [2, 1, 0]]]],
+ [["i", "!=", ["set", []]]],
+ [["i", "!=", ["set", [0]]]],
+ [["i", "!=", ["set", [1]]]],
+ [["i", "!=", ["set", [0, 1]]]],
+ [["i", "!=", ["set", [2]]]],
+ [["i", "!=", ["set", [2, 0]]]],
+ [["i", "!=", ["set", [2, 1]]]],
+ [["i", "!=", ["set", [2, 1, 0]]]],
+ [["i", "includes", ["set", []]]],
+ [["i", "includes", ["set", [0]]]],
+ [["i", "includes", ["set", [1]]]],
+ [["i", "includes", ["set", [0, 1]]]],
+ [["i", "includes", ["set", [2]]]],
+ [["i", "includes", ["set", [2, 0]]]],
+ [["i", "includes", ["set", [2, 1]]]],
+ [["i", "includes", ["set", [2, 1, 0]]]],
+ [["i", "excludes", ["set", []]]],
+ [["i", "excludes", ["set", [0]]]],
+ [["i", "excludes", ["set", [1]]]],
+ [["i", "excludes", ["set", [0, 1]]]],
+ [["i", "excludes", ["set", [2]]]],
+ [["i", "excludes", ["set", [2, 0]]]],
+ [["i", "excludes", ["set", [2, 1]]]],
+ [["i", "excludes", ["set", [2, 1, 0]]]]]']],
+ [dnl
+query 0: 11111 111
+query 1: 1---- ---
+query 2: -1--- ---
+query 3: --1-- ---
+query 4: ---1- ---
+query 5: ----1 ---
+query 6: ----- 1--
+query 7: ----- -1-
+query 8: ----- --1
+query 9: -1111 111
+query 10: 1-111 111
+query 11: 11-11 111
+query 12: 111-1 111
+query 13: 1111- 111
+query 14: 11111 -11
+query 15: 11111 1-1
+query 16: 11111 11-
+query 17: 11111 111
+query 18: -1-1- 1-1
+query 19: --11- -11
+query 20: ---1- --1
+query 21: ----1 111
+query 22: ----- 1-1
+query 23: ----- -11
+query 24: ----- --1
+query 25: 11111 111
+query 26: 1-1-1 -1-
+query 27: 11--1 1--
+query 28: 1---1 ---
+query 29: 1111- ---
+query 30: 1-1-- ---
+query 31: 11--- ---
+query 32: 1---- ---], [query])
+
+# This is the same as the "set" test except that it adds values,
+# all of which always match.
+OVSDB_CHECK_POSITIVE([queries on maps (1)],
+ [[query \
+ '{"columns": {"i": {"type": {"key": "integer",
+ "value": "boolean",
+ "min": 0,
+ "max": "unlimited"}}}}' \
+ '[{"i": ["map", []]},
+ {"i": ["map", [[0, true]]]},
+ {"i": ["map", [[1, false]]]},
+ {"i": ["map", [[0, true], [1, false]]]},
+ {"i": ["map", [[2, true]]]},
+ {"i": ["map", [[2, true], [0, true]]]},
+ {"i": ["map", [[2, true], [1, false]]]},
+ {"i": ["map", [[2, true], [1, false], [0, true]]]}]' \
+ '[[],
+ [["i", "==", ["map", []]]],
+ [["i", "==", ["map", [[0, true]]]]],
+ [["i", "==", ["map", [[1, false]]]]],
+ [["i", "==", ["map", [[0, true], [1, false]]]]],
+ [["i", "==", ["map", [[2, true]]]]],
+ [["i", "==", ["map", [[2, true], [0, true]]]]],
+ [["i", "==", ["map", [[2, true], [1, false]]]]],
+ [["i", "==", ["map", [[2, true], [1, false], [0, true]]]]],
+ [["i", "!=", ["map", []]]],
+ [["i", "!=", ["map", [[0, true]]]]],
+ [["i", "!=", ["map", [[1, false]]]]],
+ [["i", "!=", ["map", [[0, true], [1, false]]]]],
+ [["i", "!=", ["map", [[2, true]]]]],
+ [["i", "!=", ["map", [[2, true], [0, true]]]]],
+ [["i", "!=", ["map", [[2, true], [1, false]]]]],
+ [["i", "!=", ["map", [[2, true], [1, false], [0, true]]]]],
+ [["i", "includes", ["map", []]]],
+ [["i", "includes", ["map", [[0, true]]]]],
+ [["i", "includes", ["map", [[1, false]]]]],
+ [["i", "includes", ["map", [[0, true], [1, false]]]]],
+ [["i", "includes", ["map", [[2, true]]]]],
+ [["i", "includes", ["map", [[2, true], [0, true]]]]],
+ [["i", "includes", ["map", [[2, true], [1, false]]]]],
+ [["i", "includes", ["map", [[2, true], [1, false], [0, true]]]]],
+ [["i", "excludes", ["map", []]]],
+ [["i", "excludes", ["map", [[0, true]]]]],
+ [["i", "excludes", ["map", [[1, false]]]]],
+ [["i", "excludes", ["map", [[0, true], [1, false]]]]],
+ [["i", "excludes", ["map", [[2, true]]]]],
+ [["i", "excludes", ["map", [[2, true], [0, true]]]]],
+ [["i", "excludes", ["map", [[2, true], [1, false]]]]],
+ [["i", "excludes", ["map", [[2, true], [1, false], [0, true]]]]]]']],
+ [dnl
+query 0: 11111 111
+query 1: 1---- ---
+query 2: -1--- ---
+query 3: --1-- ---
+query 4: ---1- ---
+query 5: ----1 ---
+query 6: ----- 1--
+query 7: ----- -1-
+query 8: ----- --1
+query 9: -1111 111
+query 10: 1-111 111
+query 11: 11-11 111
+query 12: 111-1 111
+query 13: 1111- 111
+query 14: 11111 -11
+query 15: 11111 1-1
+query 16: 11111 11-
+query 17: 11111 111
+query 18: -1-1- 1-1
+query 19: --11- -11
+query 20: ---1- --1
+query 21: ----1 111
+query 22: ----- 1-1
+query 23: ----- -11
+query 24: ----- --1
+query 25: 11111 111
+query 26: 1-1-1 -1-
+query 27: 11--1 1--
+query 28: 1---1 ---
+query 29: 1111- ---
+query 30: 1-1-- ---
+query 31: 11--- ---
+query 32: 1---- ---], [query])
+
+# This is the same as the "set" test except that it adds values,
+# and those values don't always match.
+OVSDB_CHECK_POSITIVE([queries on maps (2)],
+ [[query \
+ '{"columns": {"i": {"type": {"key": "integer",
+ "value": "boolean",
+ "min": 0,
+ "max": "unlimited"}}}}' \
+ '[{"i": ["map", []]},
+ {"i": ["map", [[0, true]]]},
+ {"i": ["map", [[0, false]]]},
+ {"i": ["map", [[1, false]]]},
+ {"i": ["map", [[1, true]]]},
+
+ {"i": ["map", [[0, true], [1, false]]]},
+ {"i": ["map", [[0, true], [1, true]]]},
+ {"i": ["map", [[2, true]]]},
+ {"i": ["map", [[2, false]]]},
+ {"i": ["map", [[2, true], [0, true]]]},
+
+ {"i": ["map", [[2, false], [0, true]]]},
+ {"i": ["map", [[2, true], [1, false]]]},
+ {"i": ["map", [[2, true], [1, true]]]},
+ {"i": ["map", [[2, true], [1, false], [0, true]]]},
+ {"i": ["map", [[2, true], [1, false], [0, false]]]}]' \
+ '[[],
+ [["i", "==", ["map", []]]],
+ [["i", "==", ["map", [[0, true]]]]],
+ [["i", "==", ["map", [[1, false]]]]],
+ [["i", "==", ["map", [[0, true], [1, false]]]]],
+ [["i", "==", ["map", [[2, true]]]]],
+ [["i", "==", ["map", [[2, true], [0, true]]]]],
+ [["i", "==", ["map", [[2, true], [1, false]]]]],
+ [["i", "==", ["map", [[2, true], [1, false], [0, true]]]]],
+ [["i", "!=", ["map", []]]],
+ [["i", "!=", ["map", [[0, true]]]]],
+ [["i", "!=", ["map", [[1, false]]]]],
+ [["i", "!=", ["map", [[0, true], [1, false]]]]],
+ [["i", "!=", ["map", [[2, true]]]]],
+ [["i", "!=", ["map", [[2, true], [0, true]]]]],
+ [["i", "!=", ["map", [[2, true], [1, false]]]]],
+ [["i", "!=", ["map", [[2, true], [1, false], [0, true]]]]],
+ [["i", "includes", ["map", []]]],
+ [["i", "includes", ["map", [[0, true]]]]],
+ [["i", "includes", ["map", [[1, false]]]]],
+ [["i", "includes", ["map", [[0, true], [1, false]]]]],
+ [["i", "includes", ["map", [[2, true]]]]],
+ [["i", "includes", ["map", [[2, true], [0, true]]]]],
+ [["i", "includes", ["map", [[2, true], [1, false]]]]],
+ [["i", "includes", ["map", [[2, true], [1, false], [0, true]]]]],
+ [["i", "excludes", ["map", []]]],
+ [["i", "excludes", ["map", [[0, true]]]]],
+ [["i", "excludes", ["map", [[1, false]]]]],
+ [["i", "excludes", ["map", [[0, true], [1, false]]]]],
+ [["i", "excludes", ["map", [[2, true]]]]],
+ [["i", "excludes", ["map", [[2, true], [0, true]]]]],
+ [["i", "excludes", ["map", [[2, true], [1, false]]]]],
+ [["i", "excludes", ["map", [[2, true], [1, false], [0, true]]]]]]']],
+ [dnl
+query 0: 11111 11111 11111
+query 1: 1---- ----- -----
+query 2: -1--- ----- -----
+query 3: ---1- ----- -----
+query 4: ----- 1---- -----
+query 5: ----- --1-- -----
+query 6: ----- ----1 -----
+query 7: ----- ----- -1---
+query 8: ----- ----- ---1-
+query 9: -1111 11111 11111
+query 10: 1-111 11111 11111
+query 11: 111-1 11111 11111
+query 12: 11111 -1111 11111
+query 13: 11111 11-11 11111
+query 14: 11111 1111- 11111
+query 15: 11111 11111 1-111
+query 16: 11111 11111 111-1
+query 17: 11111 11111 11111
+query 18: -1--- 11--1 1--1-
+query 19: ---1- 1---- -1-11
+query 20: ----- 1---- ---1-
+query 21: ----- --1-1 -1111
+query 22: ----- ----1 ---1-
+query 23: ----- ----- -1-11
+query 24: ----- ----- ---1-
+query 25: 11111 11111 11111
+query 26: 1-111 --11- -11-1
+query 27: 111-1 -1111 1-1--
+query 28: 1-1-1 --11- --1--
+query 29: 11111 11-1- 1----
+query 30: 1-111 ---1- -----
+query 31: 111-1 -1-1- 1----
+query 32: 1-1-1 ---1- -----], [query])
+
+OVSDB_CHECK_POSITIVE([UUID-distinct queries on scalars],
+ [[query-distinct \
+ '{"columns":
+ {"i": {"type": "integer"},
+ "r": {"type": "real"},
+ "b": {"type": "boolean"},
+ "s": {"type": "string"},
+ "u": {"type": "uuid"}}}' \
+ '[{"i": 0,
+ "r": 0.5,
+ "b": true,
+ "s": "a",
+ "u": ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]},
+ {"i": 1,
+ "r": 1.5,
+ "b": false,
+ "s": "b",
+ "u": ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]},
+ {"i": 2,
+ "r": 2.5,
+ "b": true,
+ "s": "c",
+ "u": ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]},
+ {"i": 3,
+ "r": 3.5,
+ "b": false,
+ "s": "d",
+ "u": ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]},
+ {"i": 4,
+ "r": 4.5,
+ "b": true,
+ "s": "e",
+ "u": ["uuid", "4a5127e2-0256-4a72-a7dc-6246213967c7"]}]' \
+ '[[],
+ [["i", "==", 0]],
+ [["i", "!=", 1]],
+ [["i", "<", 2]],
+ [["i", "<=", 3]],
+ [["i", ">", 2]],
+ [["i", ">=", 4]],
+ [["i", "includes", 3]],
+ [["i", "excludes", 2]],
+ [["r", "==", 0.5]],
+ [["r", "!=", 1.5]],
+ [["r", "<", 2.5]],
+ [["r", "<=", 3.5]],
+ [["r", ">", 4.5]],
+ [["r", ">=", 5.5]],
+ [["r", "includes", 1]],
+ [["r", "excludes", 3]],
+ [["b", "==", true]],
+ [["b", "!=", true]],
+ [["b", "includes", false]],
+ [["b", "excludes", true]],
+ [["s", "==", "a"]],
+ [["s", "!=", "b"]],
+ [["s", "includes", "c"]],
+ [["s", "excludes", "d"]],
+ [["u", "==", ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]],
+ [["u", "!=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]],
+ [["u", "includes",["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]]' \
+ '["_uuid"]']],
+ [dnl
+query 0: abcde
+query 1: a----
+query 2: a-cde
+query 3: ab---
+query 4: abcd-
+query 5: ---de
+query 6: ----e
+query 7: ---d-
+query 8: ab-de
+query 9: a----
+query 10: a-cde
+query 11: ab---
+query 12: abcd-
+query 13: -----
+query 14: -----
+query 15: -----
+query 16: abcde
+query 17: a-c-e
+query 18: -b-d-
+query 19: -b-d-
+query 20: -b-d-
+query 21: a----
+query 22: a-cde
+query 23: --c--
+query 24: abc-e
+query 25: a----
+query 26: a-cde
+query 27: --c--],
+ [query])
+
+OVSDB_CHECK_POSITIVE([Boolean-distinct queries on scalars],
+ [[query-distinct \
+ '{"columns":
+ {"i": {"type": "integer"},
+ "r": {"type": "real"},
+ "b": {"type": "boolean"},
+ "s": {"type": "string"},
+ "u": {"type": "uuid"}}}' \
+ '[{"i": 0,
+ "r": 0.5,
+ "b": true,
+ "s": "a",
+ "u": ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]},
+ {"i": 1,
+ "r": 1.5,
+ "b": false,
+ "s": "b",
+ "u": ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]},
+ {"i": 2,
+ "r": 2.5,
+ "b": true,
+ "s": "c",
+ "u": ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]},
+ {"i": 3,
+ "r": 3.5,
+ "b": false,
+ "s": "d",
+ "u": ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]},
+ {"i": 4,
+ "r": 4.5,
+ "b": true,
+ "s": "e",
+ "u": ["uuid", "4a5127e2-0256-4a72-a7dc-6246213967c7"]}]' \
+ '[[],
+ [["i", "==", 0]],
+ [["i", "!=", 1]],
+ [["i", "<", 2]],
+ [["i", "<=", 3]],
+ [["i", ">", 2]],
+ [["i", ">=", 4]],
+ [["i", "includes", 3]],
+ [["i", "excludes", 2]],
+ [["r", "==", 0.5]],
+ [["r", "!=", 1.5]],
+ [["r", "<", 2.5]],
+ [["r", "<=", 3.5]],
+ [["r", ">", 4.5]],
+ [["r", ">=", 5.5]],
+ [["r", "includes", 1]],
+ [["r", "excludes", 3]],
+ [["b", "==", true]],
+ [["b", "!=", true]],
+ [["b", "includes", false]],
+ [["b", "excludes", true]],
+ [["s", "==", "a"]],
+ [["s", "!=", "b"]],
+ [["s", "includes", "c"]],
+ [["s", "excludes", "d"]],
+ [["u", "==", ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]],
+ [["u", "!=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]],
+ [["u", "includes",["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]]' \
+ '["b"]']],
+ [dnl
+query 0: ababa
+query 1: a-a-a
+query 2: ababa
+query 3: ababa
+query 4: ababa
+query 5: ababa
+query 6: a-a-a
+query 7: -b-b-
+query 8: ababa
+query 9: a-a-a
+query 10: ababa
+query 11: ababa
+query 12: ababa
+query 13: -----
+query 14: -----
+query 15: -----
+query 16: ababa
+query 17: a-a-a
+query 18: -b-b-
+query 19: -b-b-
+query 20: -b-b-
+query 21: a-a-a
+query 22: ababa
+query 23: a-a-a
+query 24: ababa
+query 25: a-a-a
+query 26: ababa
+query 27: a-a-a],
+ [query])
--- /dev/null
+AT_BANNER([OVSDB -- rows])
+
+# Autoconf 2.63 has a bug that causes the double-quotes below to be
+# lost, so that the following tests fail, so we mark them as XFAIL for
+# Autoconf < 2.64.
+
+m4_define([RESERVED_COLUMNS], [["_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"]]])
+
+OVSDB_CHECK_POSITIVE([row with one string column],
+ [[parse-rows \
+ '{"columns": {"name": {"type": "string"}}}' \
+ '{"name": "value"}' \
+ '{"name": ""}' \
+ '{"name": "longer string with spaces"}' \
+ '{}']],
+ [{RESERVED_COLUMNS,"name":"value"}
+name
+{RESERVED_COLUMNS,"name":""}
+name
+{RESERVED_COLUMNS,"name":"longer string with spaces"}
+name
+{RESERVED_COLUMNS,"name":""}
+<none>], [], [2.64])
+
+OVSDB_CHECK_POSITIVE([row with one integer column],
+ [[parse-rows \
+ '{"columns": {"count": {"type": "integer"}}}' \
+ '{"count": 1}' \
+ '{"count": -1}' \
+ '{"count": 2e10}' \
+ '{}']],
+ [{RESERVED_COLUMNS,"count":1}
+count
+{RESERVED_COLUMNS,"count":-1}
+count
+{RESERVED_COLUMNS,"count":20000000000}
+count
+{RESERVED_COLUMNS,"count":0}
+<none>], [], [2.64])
+
+OVSDB_CHECK_POSITIVE([row with one real column],
+ [[parse-rows \
+ '{"columns": {"cost": {"type": "real"}}}' \
+ '{"cost": 1.0}' \
+ '{"cost": -2.0}' \
+ '{"cost": 123000}' \
+ '{}']],
+ [{RESERVED_COLUMNS,"cost":1}
+cost
+{RESERVED_COLUMNS,"cost":-2}
+cost
+{RESERVED_COLUMNS,"cost":123000}
+cost
+{RESERVED_COLUMNS,"cost":0}
+<none>], [], [2.64])
+
+OVSDB_CHECK_POSITIVE([row with one boolean column],
+ [[parse-rows \
+ '{"columns": {"feasible": {"type": "boolean"}}}' \
+ '{"feasible": true}' \
+ '{"feasible": false}' \
+ '{}']],
+ [{RESERVED_COLUMNS,"feasible":true}
+feasible
+{RESERVED_COLUMNS,"feasible":false}
+feasible
+{RESERVED_COLUMNS,"feasible":false}
+<none>], [], [2.64])
+
+OVSDB_CHECK_POSITIVE([row with one uuid column],
+ [[parse-rows \
+ '{"columns": {"ref": {"type": "uuid"}}}' \
+ '{"ref": ["uuid", "f707423d-bf5b-48b5-b6c0-797c900ba4b6"]}' \
+ '{"ref": ["uuid", "33583cc5-d2f4-43de-b1ca-8aac14071b51"]}' \
+ '{}']],
+ [{RESERVED_COLUMNS,"ref":[["uuid","f707423d-bf5b-48b5-b6c0-797c900ba4b6"]]}
+ref
+{RESERVED_COLUMNS,"ref":[["uuid","33583cc5-d2f4-43de-b1ca-8aac14071b51"]]}
+ref
+{RESERVED_COLUMNS,"ref":[["uuid","00000000-0000-0000-0000-000000000000"]]}
+<none>], [], [2.64])
+
+OVSDB_CHECK_POSITIVE([row with set of 1 to 2 elements],
+ [[parse-rows \
+ '{"columns": {"myset": {"type": {"key": "integer", "min": 1, "max": 2}}}}' \
+ '{}']],
+ [{RESERVED_COLUMNS,["myset":["set",[0]]]}
+<none>])
+
+OVSDB_CHECK_POSITIVE([row with map of 1 to 2 elements],
+ [[parse-rows \
+ '{"columns": {"mymap": {"type": {"key": "integer", "value": "uuid", "min": 1, "max": 2}}}}' \
+ '{}']],
+ [{RESERVED_COLUMNS,["mymap":["map",[[0,["uuid","00000000-0000-0000-0000-000000000000"]]]]]}
+<none>], [], [2.64])
+
+OVSDB_CHECK_POSITIVE([row with several columns],
+ [[parse-rows \
+ '{"columns":
+ {"vswitch": {"type": "uuid"},
+ "name": {"type": "string"},
+ "datapath_id": {"type": {"key": "string", "min": 0}},
+ "hwaddr": {"type": "string"},
+ "mirrors": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}},
+ "netflows": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}},
+ "controller": {"type": {"key": "uuid", "min": 0}},
+ "listeners": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}},
+ "snoops": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}}}' \
+ '{"vswitch": ["uuid", "1a5c7280-0d4c-4e34-9ec7-c772339f7774"],
+ "name": "br0",
+ "datapath_id": ["set", ["000ae4256bb0"]],
+ "hwaddr": "00:0a:e4:25:6b:b0"}' \
+ '{}']],
+ [{RESERVED_COLUMNS,["controller":["set",[]],"datapath_id":["set",["000ae4256bb0"]],"hwaddr":"00:0a:e4:25:6b:b0","listeners":["set",[]],"mirrors":["set",[]],"name":"br0","netflows":["set",[]],"snoops":["set",[]],"vswitch":["uuid","1a5c7280-0d4c-4e34-9ec7-c772339f7774"]]}
+datapath_id, hwaddr, name, vswitch
+{RESERVED_COLUMNS,["controller":["set",[]],"datapath_id":["set",[]],"hwaddr":"","listeners":["set",[]],"mirrors":["set",[]],"name":"","netflows":["set",[]],"snoops":["set",[]],"vswitch":["uuid","00000000-0000-0000-0000-000000000000"]]}
+<none>], [], [2.64])
+
+OVSDB_CHECK_POSITIVE([row hashing (scalars)],
+ [[compare-rows \
+ '{"columns":
+ {"i": {"type": "integer"},
+ "r": {"type": "real"},
+ "b": {"type": "boolean"},
+ "s": {"type": "string"},
+ "u": {"type": "uuid"}}}' \
+ '["null", {}]' \
+ '["i1", {"i": 1}]' \
+ '["i2", {"i": 2}]' \
+ '["i4", {"i": 4}]' \
+ '["i8", {"i": 8}]' \
+ '["i16", {"i": 16}]' \
+ '["i32", {"i": 32}]' \
+ '["i64", {"i": 64}]' \
+ '["i128", {"i": 128}]' \
+ '["i256", {"i": 256}]' \
+ '["null2", {"r": -0}]' \
+ '["r123", {"r": 123}]' \
+ '["r0.0625", {"r": 0.0625}]' \
+ '["r0.125", {"r": 0.125}]' \
+ '["r0.25", {"r": 0.25}]' \
+ '["r0.5", {"r": 0.5}]' \
+ '["r1", {"r": 1}]' \
+ '["r2", {"r": 2}]' \
+ '["r4", {"r": 4}]' \
+ '["r8", {"r": 8}]' \
+ '["r16", {"r": 16}]' \
+ '["r32", {"r": 32}]' \
+ '["null3", {"b": false}]' \
+ '["b1", {"b": true}]' \
+ '["null4", {"s": ""}]' \
+ '["s0", {"s": "a"}]' \
+ '["s1", {"s": "b"}]' \
+ '["s2", {"s": "c"}]' \
+ '["s3", {"s": "d"}]' \
+ '["s4", {"s": "e"}]' \
+ '["s5", {"s": "f"}]' \
+ '["s6", {"s": "g"}]' \
+ '["s7", {"s": "h"}]' \
+ '["s8", {"s": "i"}]' \
+ '["s9", {"s": "j"}]' \
+ '["null5", {"u": ["uuid","00000000-0000-0000-0000-000000000000"]}]' \
+ '["u1", {"u": ["uuid","10000000-0000-0000-0000-000000000000"]}]' \
+ '["u2", {"u": ["uuid","01000000-0000-0000-0000-000000000000"]}]' \
+ '["u3", {"u": ["uuid","00100000-0000-0000-0000-000000000000"]}]' \
+ '["u4", {"u": ["uuid","00010000-0000-0000-0000-000000000000"]}]' \
+ '["u5", {"u": ["uuid","00001000-0000-0000-0000-000000000000"]}]' \
+ '["u6", {"u": ["uuid","00000100-0000-0000-0000-000000000000"]}]' \
+ '["u7", {"u": ["uuid","00000010-0000-0000-0000-000000000000"]}]' \
+ '["u8", {"u": ["uuid","00000001-0000-0000-0000-000000000000"]}]' \
+ '["null6", {"u": ["uuid","00000000-c6db-4d22-970f-b41fabd20c4b"]}]']],
+ [[null == null2
+null == null3
+null == null4
+null == null5
+hash(null) == hash(null6)
+null2 == null3
+null2 == null4
+null2 == null5
+hash(null2) == hash(null6)
+null3 == null4
+null3 == null5
+hash(null3) == hash(null6)
+null4 == null5
+hash(null4) == hash(null6)
+hash(null5) == hash(null6)]])
+
+OVSDB_CHECK_POSITIVE([row hashing (sets)],
+ [[compare-rows \
+ '{"columns":
+ {"i": {"type": {"key": "integer", "min": 0, "max": "unlimited"}},
+ "r": {"type": {"key": "real", "min": 0, "max": "unlimited"}},
+ "b": {"type": {"key": "boolean", "min": 0, "max": "unlimited"}},
+ "s": {"type": {"key": "string", "min": 0, "max": "unlimited"}},
+ "u": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}}}' \
+ '["null0", {"i": ["set", []]}]' \
+ '["i0", {"i": ["set", [0]]}]' \
+ '["i01", {"i": ["set", [0, 1]]}]' \
+ '["i012", {"i": ["set", [0, 1, 2]]}]' \
+ '["i021", {"i": ["set", [0, 2, 1]]}]' \
+ '["i201", {"i": ["set", [2, 0, 1]]}]' \
+ '["i102", {"i": ["set", [1, 0, 2]]}]' \
+ '["i120", {"i": ["set", [1, 2, 0]]}]' \
+ '["i210", {"i": ["set", [2, 1, 0]]}]' \
+ '["r0", {"r": ["set", [0]]}]' \
+ '["r01", {"r": ["set", [0, 1]]}]' \
+ '["r012", {"r": ["set", [0, 1, 2]]}]' \
+ '["r201", {"r": ["set", [2, 0, 1]]}]' \
+ '["null1", {"b": ["set", []]}]' \
+ '["b0", {"b": ["set", [false]]}]' \
+ '["b1", {"b": ["set", [true]]}]' \
+ '["b01", {"b": ["set", [false, true]]}]' \
+ '["b10", {"b": ["set", [true, false]]}]' \
+ '["null2", {"s": ["set", []]}]' \
+ '["sa", {"s": ["set", ["a"]]}]' \
+ '["sb", {"s": ["set", ["b"]]}]' \
+ '["sab", {"s": ["set", ["a", "b"]]}]' \
+ '["sba", {"s": ["set", ["b", "a"]]}]']],
+ [[null0 == null1
+null0 == null2
+i012 == i021
+i012 == i201
+i012 == i102
+i012 == i120
+i012 == i210
+i021 == i201
+i021 == i102
+i021 == i120
+i021 == i210
+i201 == i102
+i201 == i120
+i201 == i210
+i102 == i120
+i102 == i210
+i120 == i210
+r012 == r201
+null1 == null2
+b01 == b10
+sab == sba]])
+
+OVSDB_CHECK_POSITIVE([row hashing (maps)],
+ [[compare-rows \
+ '{"columns":
+ {"ii": {"type": {"key": "integer", "value": "integer",
+ "min": 0, "max": "unlimited"}},
+ "rr": {"type": {"key": "real", "value": "real",
+ "min": 0, "max": "unlimited"}},
+ "bb": {"type": {"key": "boolean", "value": "boolean",
+ "min": 0, "max": "unlimited"}},
+ "ss": {"type": {"key": "string", "value": "string",
+ "min": 0, "max": "unlimited"}}}}' \
+ '["null", {}]' \
+ '["ii0", {"ii": ["map", [[0, 0]]]}]' \
+ '["ii1", {"ii": ["map", [[0, 1]]]}]' \
+ '["ii00", {"ii": ["map", [[0, 0], [1, 0]]]}]' \
+ '["ii01", {"ii": ["map", [[0, 0], [1, 1]]]}]' \
+ '["ii10", {"ii": ["map", [[0, 1], [1, 0]]]}]' \
+ '["ii11", {"ii": ["map", [[0, 1], [1, 1]]]}]' \
+ '["rr0", {"rr": ["map", [[0, 0]]]}]' \
+ '["rr0", {"rr": ["map", [[0, 1]]]}]' \
+ '["rr00", {"rr": ["map", [[0, 0], [1, 0]]]}]' \
+ '["rr01", {"rr": ["map", [[0, 0], [1, 1]]]}]' \
+ '["rr10", {"rr": ["map", [[0, 1], [1, 0]]]}]' \
+ '["rr11", {"rr": ["map", [[0, 1], [1, 1]]]}]' \
+ '["bb0", {"bb": ["map", [[false, false]]]}]' \
+ '["bb1", {"bb": ["map", [[false, true]]]}]' \
+ '["bb00", {"bb": ["map", [[false, false], [true, false]]]}]' \
+ '["bb01", {"bb": ["map", [[false, false], [true, true]]]}]' \
+ '["bb10", {"bb": ["map", [[false, true], [true, false]]]}]' \
+ '["bb11", {"bb": ["map", [[false, true], [true, true]]]}]' \
+ '["ss0", {"ss": ["map", [["a", "a"]]]}]' \
+ '["ss1", {"ss": ["map", [["a", "b"]]]}]' \
+ '["ss00", {"ss": ["map", [["a", "a"], ["b", "a"]]]}]' \
+ '["ss01", {"ss": ["map", [["a", "a"], ["b", "b"]]]}]' \
+ '["ss10", {"ss": ["map", [["a", "b"], ["b", "a"]]]}]' \
+ '["ss11", {"ss": ["map", [["a", "b"], ["b", "b"]]]}]'; echo
+]], [[]])
--- /dev/null
+AT_BANNER([OVSDB -- ovsdb-server transactions])
+
+# OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS])
+#
+# Creates a database with the given SCHEMA, starts an ovsdb-server on
+# that database, and runs each of the TRANSACTIONS (which should be a
+# quoted list of quoted strings) against it with ovsdb-client one at a
+# time.
+#
+# Checks that the overall output is OUTPUT, but UUIDs in the output
+# are replaced by markers of the form <N> where N is a number. The
+# first unique UUID is replaced by <0>, the next by <1>, and so on.
+# If a given UUID appears more than once it is always replaced by the
+# same marker.
+#
+# TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
+m4_define([OVSDB_CHECK_EXECUTION],
+ [AT_SETUP([$1])
+ AT_KEYWORDS([ovsdb server positive $5])
+ AT_DATA([schema], [$2
+])
+ OVS_CHECK_LCOV([ovsdb-tool create db schema], [0], [stdout], [ignore])
+ AT_CHECK([ovsdb-server --detach --pidfile=$PWD/pid --listen=punix:socket --unixctl=$PWD/unixctl db])
+ m4_foreach([txn], [$3],
+ [OVS_CHECK_LCOV([ovsdb-client transact unix:socket 'txn'], [0], [stdout], [ignore],
+ [test ! -e pid || kill `cat pid`])
+cat stdout >> output
+])
+ AT_CHECK([perl $srcdir/uuidfilt.pl output], [0], [$4], [],
+ [test ! -e pid || kill `cat pid`])
+ test ! -e pid || kill `cat pid`
+ AT_CLEANUP])
+
+EXECUTION_EXAMPLES
--- /dev/null
+AT_BANNER([OVSDB -- tables])
+
+OVSDB_CHECK_POSITIVE([table with one column],
+ [[parse-table mytable '{"columns": {"name": {"type": "string"}}}']],
+ [[{"columns":{"name":{"type":"string"}}}]])
+
+OVSDB_CHECK_POSITIVE([immutable table with one column],
+ [[parse-table mytable \
+ '{"columns": {"name": {"type": "string"}},
+ "mutable": false}']],
+ [[{"columns":{"name":{"type":"string"}},"mutable":false}]])
+
+OVSDB_CHECK_POSITIVE([table with comment],
+ [[parse-table mytable \
+ '{"columns": {"name": {"type": "string"}},
+ "comment": "description of table"}']],
+ [[{"columns":{"name":{"type":"string"}},"comment":"description of table"}]])
+
+OVSDB_CHECK_NEGATIVE([column names may not begin with _],
+ [[parse-table mytable \
+ '{"columns": {"_column": {"type": "integer"}}}']],
+ [[names beginning with "_" are reserved]],
+ [table])
+
+OVSDB_CHECK_NEGATIVE([table must have at least one column (1)],
+ [[parse-table mytable '{}']],
+ [[Parsing table schema for table mytable failed: Required 'columns' member is missing.]])
+
+OVSDB_CHECK_NEGATIVE([table must have at least one column (2)],
+ [[parse-table mytable '{"columns": {}}']],
+ [[table must have at least one column]])
--- /dev/null
+AT_BANNER([OVSDB -- transactions])
+
+OVSDB_CHECK_POSITIVE([empty table, empty transaction],
+ [[transact \
+ '["print"]' \
+ '["commit"]' \
+ '["print"]' \
+ '["abort"]' \
+ '["print"]']],
+ [dnl
+print:
+commit:
+print:
+abort:
+print:])
+
+OVSDB_CHECK_POSITIVE([nonempty table, empty transaction],
+ [[transact \
+ '["insert", "1", "2", "3"]' \
+ '["insert", "2", "2", "3"]' \
+ '["print"]' \
+ '["commit"]' \
+ '["print"]' \
+ '["abort"]' \
+ '["print"]']],
+ [dnl
+insert 1 2 3:
+insert 2 2 3:
+print:
+1: i=2, j=3
+2: i=2, j=3
+commit:
+print:
+1: i=2, j=3
+2: i=2, j=3
+abort:
+print:
+1: i=2, j=3
+2: i=2, j=3])
+
+OVSDB_CHECK_POSITIVE([insert, commit],
+ [[transact \
+ '["insert", "1", "2", "3"]' \
+ '["insert", "2", "2", "3"]' \
+ '["commit"]' \
+ '["print"]' \
+ '["insert", "3", "1", "2"]' \
+ '["print"]' \
+ '["commit"]' \
+ '["print"]']],
+ [dnl
+insert 1 2 3:
+insert 2 2 3:
+commit:
+print:
+1: i=2, j=3
+2: i=2, j=3
+insert 3 1 2:
+print:
+1: i=2, j=3
+2: i=2, j=3
+3: i=1, j=2
+commit:
+print:
+1: i=2, j=3
+2: i=2, j=3
+3: i=1, j=2],
+ [transaction])
+
+OVSDB_CHECK_POSITIVE([insert, abort],
+ [[transact \
+ '["insert", "1", "2", "3"]' \
+ '["insert", "2", "2", "3"]' \
+ '["commit"]' \
+ '["print"]' \
+ '["insert", "3", "1", "2"]' \
+ '["print"]' \
+ '["abort"]' \
+ '["print"]']],
+ [dnl
+insert 1 2 3:
+insert 2 2 3:
+commit:
+print:
+1: i=2, j=3
+2: i=2, j=3
+insert 3 1 2:
+print:
+1: i=2, j=3
+2: i=2, j=3
+3: i=1, j=2
+abort:
+print:
+1: i=2, j=3
+2: i=2, j=3],
+ [transaction])
+
+OVSDB_CHECK_POSITIVE([modify, commit],
+ [[transact \
+ '["insert", "1", "2", "3"]' \
+ '["insert", "2", "2", "3"]' \
+ '["commit"]' \
+ '["print"]' \
+ '["modify", "2", "5", "-1"]' \
+ '["modify", "1", "-1", "4"]' \
+ '["print"]' \
+ '["commit"]' \
+ '["print"]']],
+ [dnl
+insert 1 2 3:
+insert 2 2 3:
+commit:
+print:
+1: i=2, j=3
+2: i=2, j=3
+modify 2 5 -1:
+modify 1 -1 4:
+print:
+1: i=2, j=4
+2: i=5, j=3
+commit:
+print:
+1: i=2, j=4
+2: i=5, j=3],
+ [transaction])
+
+OVSDB_CHECK_POSITIVE([modify, abort],
+ [[transact \
+ '["insert", "1", "2", "3"]' \
+ '["insert", "2", "2", "3"]' \
+ '["commit"]' \
+ '["print"]' \
+ '["modify", "2", "5", "-1"]' \
+ '["modify", "1", "-1", "4"]' \
+ '["print"]' \
+ '["abort"]' \
+ '["print"]']],
+ [dnl
+insert 1 2 3:
+insert 2 2 3:
+commit:
+print:
+1: i=2, j=3
+2: i=2, j=3
+modify 2 5 -1:
+modify 1 -1 4:
+print:
+1: i=2, j=4
+2: i=5, j=3
+abort:
+print:
+1: i=2, j=3
+2: i=2, j=3],
+ [transaction])
+
+OVSDB_CHECK_POSITIVE([delete, commit],
+ [[transact \
+ '["insert", "1", "2", "3"]' \
+ '["insert", "2", "2", "3"]' \
+ '["commit"]' \
+ '["print"]' \
+ '["delete", "1"]' \
+ '["print"]' \
+ '["commit"]' \
+ '["print"]']],
+ [dnl
+insert 1 2 3:
+insert 2 2 3:
+commit:
+print:
+1: i=2, j=3
+2: i=2, j=3
+delete 1:
+print:
+2: i=2, j=3
+commit:
+print:
+2: i=2, j=3],
+ [transaction])
+
+OVSDB_CHECK_POSITIVE([delete, abort],
+ [[transact \
+ '["insert", "1", "2", "3"]' \
+ '["insert", "2", "2", "3"]' \
+ '["commit"]' \
+ '["print"]' \
+ '["delete", "1"]' \
+ '["print"]' \
+ '["abort"]' \
+ '["print"]']],
+ [dnl
+insert 1 2 3:
+insert 2 2 3:
+commit:
+print:
+1: i=2, j=3
+2: i=2, j=3
+delete 1:
+print:
+2: i=2, j=3
+abort:
+print:
+1: i=2, j=3
+2: i=2, j=3],
+ [transaction])
+
+OVSDB_CHECK_POSITIVE([modify, delete, commit],
+ [[transact \
+ '["insert", "1", "2", "3"]' \
+ '["insert", "2", "2", "3"]' \
+ '["commit"]' \
+ '["print"]' \
+ '["modify", "1", "5", "6"]' \
+ '["delete", "1"]' \
+ '["print"]' \
+ '["commit"]' \
+ '["print"]']],
+ [dnl
+insert 1 2 3:
+insert 2 2 3:
+commit:
+print:
+1: i=2, j=3
+2: i=2, j=3
+modify 1 5 6:
+delete 1:
+print:
+2: i=2, j=3
+commit:
+print:
+2: i=2, j=3],
+ [transaction])
+
+OVSDB_CHECK_POSITIVE([modify, delete, abort],
+ [[transact \
+ '["insert", "1", "2", "3"]' \
+ '["insert", "2", "2", "3"]' \
+ '["commit"]' \
+ '["print"]' \
+ '["modify", "1", "5", "6"]' \
+ '["delete", "1"]' \
+ '["print"]' \
+ '["abort"]' \
+ '["print"]']],
+ [dnl
+insert 1 2 3:
+insert 2 2 3:
+commit:
+print:
+1: i=2, j=3
+2: i=2, j=3
+modify 1 5 6:
+delete 1:
+print:
+2: i=2, j=3
+abort:
+print:
+1: i=2, j=3
+2: i=2, j=3],
+ [transaction])
+
+OVSDB_CHECK_POSITIVE([insert, delete, commit],
+ [[transact \
+ '["insert", "1", "2", "3"]' \
+ '["insert", "2", "2", "3"]' \
+ '["commit"]' \
+ '["print"]' \
+ '["insert", "3", "5", "6"]' \
+ '["delete", "1"]' \
+ '["delete", "3"]' \
+ '["print"]' \
+ '["commit"]' \
+ '["print"]']],
+ [dnl
+insert 1 2 3:
+insert 2 2 3:
+commit:
+print:
+1: i=2, j=3
+2: i=2, j=3
+insert 3 5 6:
+delete 1:
+delete 3:
+print:
+2: i=2, j=3
+commit:
+print:
+2: i=2, j=3],
+ [transaction])
+
+OVSDB_CHECK_POSITIVE([insert, delete, abort],
+ [[transact \
+ '["insert", "1", "2", "3"]' \
+ '["insert", "2", "2", "3"]' \
+ '["commit"]' \
+ '["print"]' \
+ '["insert", "3", "5", "6"]' \
+ '["delete", "1"]' \
+ '["delete", "3"]' \
+ '["print"]' \
+ '["abort"]' \
+ '["print"]']],
+ [dnl
+insert 1 2 3:
+insert 2 2 3:
+commit:
+print:
+1: i=2, j=3
+2: i=2, j=3
+insert 3 5 6:
+delete 1:
+delete 3:
+print:
+2: i=2, j=3
+abort:
+print:
+1: i=2, j=3
+2: i=2, j=3],
+ [transaction])
+
+
+OVSDB_CHECK_POSITIVE([insert, modify, delete, commit],
+ [[transact \
+ '["insert", "1", "2", "3"]' \
+ '["insert", "2", "2", "3"]' \
+ '["commit"]' \
+ '["print"]' \
+ '["insert", "3", "5", "6"]' \
+ '["delete", "1"]' \
+ '["modify", "3", "7", "8"]' \
+ '["delete", "3"]' \
+ '["print"]' \
+ '["commit"]' \
+ '["print"]']],
+ [dnl
+insert 1 2 3:
+insert 2 2 3:
+commit:
+print:
+1: i=2, j=3
+2: i=2, j=3
+insert 3 5 6:
+delete 1:
+modify 3 7 8:
+delete 3:
+print:
+2: i=2, j=3
+commit:
+print:
+2: i=2, j=3],
+ [transaction])
+
+OVSDB_CHECK_POSITIVE([insert, modify, delete, abort],
+ [[transact \
+ '["insert", "1", "2", "3"]' \
+ '["insert", "2", "2", "3"]' \
+ '["commit"]' \
+ '["print"]' \
+ '["insert", "3", "5", "6"]' \
+ '["delete", "1"]' \
+ '["modify", "3", "7", "8"]' \
+ '["delete", "3"]' \
+ '["print"]' \
+ '["abort"]' \
+ '["print"]']],
+ [dnl
+insert 1 2 3:
+insert 2 2 3:
+commit:
+print:
+1: i=2, j=3
+2: i=2, j=3
+insert 3 5 6:
+delete 1:
+modify 3 7 8:
+delete 3:
+print:
+2: i=2, j=3
+abort:
+print:
+1: i=2, j=3
+2: i=2, j=3],
+ [transaction])
+
--- /dev/null
+AT_BANNER([OVSDB -- triggers])
+
+# This is like OVSDB_CHECK_POSITIVE, except that UUIDs in the output
+# are replaced by markers of the form <N> where N is a number. The
+# first unique UUID is replaced by <0>, the next by <1>, and so on.
+# If a given UUID appears more than once it is always replaced by the
+# same marker.
+m4_define([OVSDB_CHECK_TRIGGER],
+ [AT_SETUP([$1])
+ AT_KEYWORDS([ovsdb execute execution trigger positive $4])
+ OVS_CHECK_LCOV([test-ovsdb trigger $2], [0], [stdout], [])
+ AT_CHECK([perl $srcdir/uuidfilt.pl stdout], [0], [$3])
+ AT_CLEANUP])
+
+OVSDB_CHECK_TRIGGER([trigger fires immediately],
+ ['ORDINAL_SCHEMA' [\
+ '[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}},
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"}},
+ {"op": "wait",
+ "timeout": 10,
+ "table": "ordinals",
+ "where": [],
+ "columns": ["name", "number"],
+ "until": "==",
+ "rows": [{"name": "zero", "number": 0},
+ {"name": "one", "number": 1}]},
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 2, "name": "two"}}]']],
+ [[t=0: trigger 0 (immediate): [{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{},{"uuid":["uuid","<2>"]}]
+]])
+
+OVSDB_CHECK_TRIGGER([trigger times out],
+ ['ORDINAL_SCHEMA' [\
+ '[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}},
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"}},
+ {"op": "wait",
+ "timeout": 10,
+ "table": "ordinals",
+ "where": [],
+ "columns": ["name", "number"],
+ "until": "==",
+ "rows": [{"name": "zero", "number": 0},
+ {"name": "one", "number": 1},
+ {"name": "two", "number": 2}]}]' \
+ '["advance", 10]']],
+ [[t=0: new trigger 0
+t=10: trigger 0 (delayed): [{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"details":"\"wait\" timed out after 10 ms","error":"timed out"}]
+]])
+
+OVSDB_CHECK_TRIGGER([trigger fires after delay],
+ ['ORDINAL_SCHEMA' [\
+ '[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}},
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"}}]' \
+ '["advance", 5]' \
+ '[{"op": "wait",
+ "timeout": 10,
+ "table": "ordinals",
+ "where": [],
+ "columns": ["name", "number"],
+ "until": "==",
+ "rows": [{"name": "zero", "number": 0},
+ {"name": "one", "number": 1},
+ {"name": "two", "number": 2}]}]' \
+ '["advance", 5]' \
+ '[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 2, "name": "two"}}]']],
+ [[t=0: trigger 0 (immediate): [{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}]
+t=5: new trigger 1
+t=10: trigger 2 (immediate): [{"uuid":["uuid","<2>"]}]
+t=10: trigger 1 (delayed): [{}]
+]])
+
+OVSDB_CHECK_TRIGGER([delayed trigger modifies database],
+ ['ORDINAL_SCHEMA' [\
+ '[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}},
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"}}]' \
+ '["advance", 5]' \
+ '[{"op": "wait",
+ "timeout": 10,
+ "table": "ordinals",
+ "where": [],
+ "columns": ["name", "number"],
+ "until": "==",
+ "rows": [{"name": "zero", "number": 0},
+ {"name": "one", "number": 1},
+ {"name": "two", "number": 2}]},
+ {"op": "delete",
+ "table": "ordinals",
+ "where": [["number", "<", 2]]}]' \
+ '["advance", 5]' \
+ '[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 2, "name": "two"}}]' \
+ '["advance", 5]' \
+ '[{"op": "select",
+ "table": "ordinals",
+ "where": []}]']],
+ [[t=0: trigger 0 (immediate): [{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}]
+t=5: new trigger 1
+t=10: trigger 2 (immediate): [{"uuid":["uuid","<2>"]}]
+t=10: trigger 1 (delayed): [{},{"count":2}]
+t=15: trigger 3 (immediate): [{"rows":[{"_uuid":["uuid","<2>"],"_version":["uuid","<3>"],"name":"two","number":2}]}]
+]])
+
+OVSDB_CHECK_TRIGGER([one delayed trigger wakes up another],
+ ['ORDINAL_SCHEMA' [\
+ '[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}},
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"}}]' \
+ '["advance", 5]' \
+ '[{"op": "wait",
+ "timeout": 10,
+ "table": "ordinals",
+ "where": [],
+ "columns": ["name", "number"],
+ "until": "==",
+ "rows": [{"name": "two", "number": 2}]},
+ {"op": "delete",
+ "table": "ordinals",
+ "where": [["number", "==", 2]]},
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 3, "name": "three"}}]' \
+ '[{"op": "wait",
+ "timeout": 10,
+ "table": "ordinals",
+ "where": [],
+ "columns": ["name", "number"],
+ "until": "==",
+ "rows": [{"name": "zero", "number": 0},
+ {"name": "one", "number": 1},
+ {"name": "two", "number": 2}]},
+ {"op": "delete",
+ "table": "ordinals",
+ "where": [["number", "<", 2]]}]' \
+ '["advance", 5]' \
+ '[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 2, "name": "two"}}]' \
+ '["advance", 5]' \
+ '[{"op": "select",
+ "table": "ordinals",
+ "where": []}]']],
+ [[t=0: trigger 0 (immediate): [{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}]
+t=5: new trigger 1
+t=5: new trigger 2
+t=10: trigger 3 (immediate): [{"uuid":["uuid","<2>"]}]
+t=10: trigger 2 (delayed): [{},{"count":2}]
+t=15: trigger 1 (delayed): [{},{"count":1},{"uuid":["uuid","<3>"]}]
+t=15: trigger 4 (immediate): [{"rows":[{"_uuid":["uuid","<3>"],"_version":["uuid","<4>"],"name":"three","number":3}]}]
+]])
+
--- /dev/null
+AT_BANNER([OVSDB -- atomic types])
+
+OVSDB_CHECK_POSITIVE([integer],
+ [[parse-atomic-type '["integer"]' ]], ["integer"])
+OVSDB_CHECK_POSITIVE([real],
+ [[parse-atomic-type '["real"]' ]], ["real"])
+OVSDB_CHECK_POSITIVE([boolean],
+ [[parse-atomic-type '["boolean"]' ]], ["boolean"])
+OVSDB_CHECK_POSITIVE([string],
+ [[parse-atomic-type '["string"]' ]], ["string"])
+OVSDB_CHECK_POSITIVE([uuid],
+ [[parse-atomic-type '["uuid"]' ]], ["uuid"])
+OVSDB_CHECK_NEGATIVE([void is not a valid atomic-type],
+ [[parse-atomic-type '["void"]' ]], ["void" is not an atomic-type])
+
+AT_BANNER([OVSDB -- simple types])
+
+OVSDB_CHECK_POSITIVE([simple integer],
+ [[parse-type '["integer"]' ]], ["integer"])
+OVSDB_CHECK_POSITIVE([simple real],
+ [[parse-type '["real"]' ]], ["real"])
+OVSDB_CHECK_POSITIVE([simple boolean],
+ [[parse-type '["boolean"]' ]], ["boolean"])
+OVSDB_CHECK_POSITIVE([simple string],
+ [[parse-type '["string"]' ]], ["string"])
+OVSDB_CHECK_POSITIVE([simple uuid],
+ [[parse-type '["uuid"]' ]], ["uuid"])
+OVSDB_CHECK_POSITIVE([integer in object],
+ [[parse-type '{"key": "integer"}' ]], ["integer"])
+OVSDB_CHECK_POSITIVE([real in object with explicit min and max],
+ [[parse-type '{"key": "real", "min": 1, "max": 1}' ]], ["real"])
+
+OVSDB_CHECK_NEGATIVE([key type is required],
+ [[parse-type '{}' ]], [Required 'key' member is missing.])
+OVSDB_CHECK_NEGATIVE([void is not a valid type],
+ [[parse-type '["void"]' ]], ["void" is not an atomic-type])
+
+AT_BANNER([OVSDB -- set types])
+
+OVSDB_CHECK_POSITIVE([optional boolean],
+ [[parse-type '{"key": "boolean", "min": 0}' ]],
+ [[{"key":"boolean","min":0}]],
+ [set])
+OVSDB_CHECK_POSITIVE([set of 1 to 3 uuids],
+ [[parse-type '{"key": "uuid", "min": 1, "max": 3}' ]],
+ [[{"key":"uuid","max":3}]])
+OVSDB_CHECK_POSITIVE([set of 0 to 3 strings],
+ [[parse-type '{"key": "string", "min": 0, "max": 3}' ]],
+ [[{"key":"string","max":3,"min":0}]])
+OVSDB_CHECK_POSITIVE([set of 0 or more integers],
+ [[parse-type '{"key": "integer", "min": 0, "max": "unlimited"}']],
+ [[{"key":"integer","max":"unlimited","min":0}]])
+OVSDB_CHECK_POSITIVE([set of 10 or more reals],
+ [[parse-type '{"key": "real", "min": 10, "max": "unlimited"}']],
+ [[{"key":"real","max":"unlimited","min":10}]])
+
+OVSDB_CHECK_NEGATIVE([set max cannot be less than min],
+ [[parse-type '{"key": "real", "min": 5, "max": 3}' ]],
+ [ovsdb type fails constraint checks])
+OVSDB_CHECK_NEGATIVE([set max cannot be negative],
+ [[parse-type '{"key": "real", "max": -1}' ]],
+ [bad min or max value])
+OVSDB_CHECK_NEGATIVE([set min cannot be negative],
+ [[parse-type '{"key": "real", "min": -1}' ]],
+ [bad min or max value])
+
+AT_BANNER([OVSDB -- map types])
+
+OVSDB_CHECK_POSITIVE([map of 1 integer to boolean],
+ [[parse-type '{"key": "integer", "value": "boolean"}' ]],
+ [[{"key":"integer","value":"boolean"}]])
+OVSDB_CHECK_POSITIVE([map of 1 boolean to integer, explicit min and max],
+ [[parse-type '{"key": "boolean", "value": "integer", "min": 1, "max": 1}' ]],
+ [[{"key":"boolean","value":"integer"}]])
+OVSDB_CHECK_POSITIVE([map of 2 to 5 uuid to real],
+ [[parse-type '{"key": "uuid", "value": "real", "min": 2, "max": 5}' ]],
+ [[{"key":"uuid","max":5,"min":2,"value":"real"}]])
+OVSDB_CHECK_POSITIVE([map of 0 to 10 string to uuid],
+ [[parse-type '{"key": "string", "value": "uuid", "min": 0, "max": 10}' ]],
+ [[{"key":"string","max":10,"min":0,"value":"uuid"}]])
+OVSDB_CHECK_POSITIVE([map of 10 to 20 real to string],
+ [[parse-type '{"key": "real", "value": "string", "min": 10, "max": 20}' ]],
+ [[{"key":"real","max":20,"min":10,"value":"string"}]])
+OVSDB_CHECK_POSITIVE([map of 20 or more string to real],
+ [[parse-type '{"key": "string", "value": "real", "min": 20, "max": "unlimited"}' ]],
+ [[{"key":"string","max":"unlimited","min":20,"value":"real"}]])
+
+OVSDB_CHECK_NEGATIVE([map key type is required],
+ [[parse-type '{"value": "integer"}' ]],
+ [Required 'key' member is missing.])
--- /dev/null
+# OVSDB_CHECK_POSITIVE(TITLE, TEST-OVSDB-ARGS, OUTPUT, [KEYWORDS], [PREREQ])
+#
+# Runs "test-ovsdb TEST-OVSDB-ARGS" and checks that it exits with
+# status 0 and prints OUTPUT on stdout.
+#
+# TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. If
+# PREREQ is specified then the test is skipped if the Autoconf version
+# is less than PREREQ.
+m4_define([OVSDB_CHECK_POSITIVE],
+ [AT_SETUP([$1])
+ m4_if([$5], [], [],
+ [AT_XFAIL_IF([m4_version_prereq([$5], [false], [true])])])
+ AT_KEYWORDS([ovsdb positive $4])
+ OVS_CHECK_LCOV([test-ovsdb $2], [0], [$3
+], [])
+ AT_CLEANUP])
+
+# OVSDB_CHECK_NEGATIVE(TITLE, TEST-OVSDB-ARGS, OUTPUT, [KEYWORDS], [PREREQ])
+#
+# Runs "test-ovsdb TEST-OVSDB-ARGS" and checks that it exits with
+# status 1 and that its output on stdout contains substring OUTPUT.
+# TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
+m4_define([OVSDB_CHECK_NEGATIVE],
+ [AT_SETUP([$1])
+ AT_KEYWORDS([ovsdb negative $4])
+ OVS_CHECK_LCOV([test-ovsdb $2], [1], [], [stderr])
+ m4_assert(m4_len([$3]))
+ AT_CHECK(
+ [if grep -F -e "AS_ESCAPE([$3])" stderr
+ then
+ :
+ else
+ exit 99
+ fi],
+ [0], [ignore], [ignore])
+ AT_CLEANUP])
+
+m4_include([tests/ovsdb-log.at])
+m4_include([tests/ovsdb-types.at])
+m4_include([tests/ovsdb-data.at])
+m4_include([tests/ovsdb-column.at])
+m4_include([tests/ovsdb-table.at])
+m4_include([tests/ovsdb-row.at])
+m4_include([tests/ovsdb-condition.at])
+m4_include([tests/ovsdb-query.at])
+m4_include([tests/ovsdb-transaction.at])
+m4_include([tests/ovsdb-execution.at])
+m4_include([tests/ovsdb-trigger.at])
+m4_include([tests/ovsdb-file.at])
+m4_include([tests/ovsdb-server.at])
+m4_include([tests/ovsdb-monitor.at])
+m4_include([tests/ovsdb-idl.at])
--- /dev/null
+AT_BANNER([reconnect library])
+
+######################################################################
+AT_SETUP([nothing happens if not enabled])
+AT_KEYWORDS([reconnect])
+AT_DATA([input], [run
+timeout
+])
+OVS_CHECK_LCOV([test-reconnect < input], [0],
+ [### t=1000 ###
+run
+timeout
+ no timeout
+])
+AT_CLEANUP
+
+######################################################################
+AT_SETUP([quick connect, idle disconnect])
+AT_KEYWORDS([reconnect])
+AT_DATA([input], [enable
+
+# Connection succeeds.
+run
+connected
+
+# Send inactivity probe.
+timeout
+run
+
+# Idle timeout kills connection.
+timeout
+run
+disconnected
+])
+OVS_CHECK_LCOV([test-reconnect < input], [0],
+ [### t=1000 ###
+enable
+ in BACKOFF for 0 ms (0 ms backoff)
+
+# Connection succeeds.
+run
+ should connect
+connected
+ in ACTIVE for 0 ms (0 ms backoff)
+ 1 successful connections out of 1 attempts, seqno 1
+ connected (0 ms), total 0 ms connected
+
+# Send inactivity probe.
+timeout
+ advance 5000 ms
+
+### t=6000 ###
+ in ACTIVE for 5000 ms (0 ms backoff)
+ connected (5000 ms), total 5000 ms connected
+run
+ should send probe
+ in IDLE for 0 ms (0 ms backoff)
+
+# Idle timeout kills connection.
+timeout
+ advance 5000 ms
+
+### t=11000 ###
+ in IDLE for 5000 ms (0 ms backoff)
+ connected (10000 ms), total 10000 ms connected
+run
+ should disconnect
+disconnected
+ in BACKOFF for 0 ms (1000 ms backoff)
+ 1 successful connections out of 1 attempts, seqno 2
+ not connected (0 ms), total 10000 ms connected
+])
+AT_CLEANUP
+
+######################################################################
+AT_SETUP([slow connect, idle disconnect])
+AT_KEYWORDS([reconnect])
+AT_DATA([input], [enable
+
+# Start connecting.
+run
+connecting
+
+# Connect after 500 ms.
+advance 500
+run
+connected
+
+# Send inactivity probe.
+timeout
+run
+
+# Idle timeout kills connection.
+timeout
+run
+disconnected
+])
+OVS_CHECK_LCOV([test-reconnect < input], [0],
+ [### t=1000 ###
+enable
+ in BACKOFF for 0 ms (0 ms backoff)
+
+# Start connecting.
+run
+ should connect
+connecting
+ in CONNECTING for 0 ms (0 ms backoff)
+
+# Connect after 500 ms.
+advance 500
+
+### t=1500 ###
+ in CONNECTING for 500 ms (0 ms backoff)
+run
+ should connect
+connected
+ in ACTIVE for 0 ms (0 ms backoff)
+ created 1000, last received 1000, last connected 1500
+ 1 successful connections out of 1 attempts, seqno 1
+ connected (0 ms), total 0 ms connected
+
+# Send inactivity probe.
+timeout
+ advance 5000 ms
+
+### t=6500 ###
+ in ACTIVE for 5000 ms (0 ms backoff)
+ connected (5000 ms), total 5000 ms connected
+run
+ should send probe
+ in IDLE for 0 ms (0 ms backoff)
+
+# Idle timeout kills connection.
+timeout
+ advance 5000 ms
+
+### t=11500 ###
+ in IDLE for 5000 ms (0 ms backoff)
+ connected (10000 ms), total 10000 ms connected
+run
+ should disconnect
+disconnected
+ in BACKOFF for 0 ms (1000 ms backoff)
+ 1 successful connections out of 1 attempts, seqno 2
+ not connected (0 ms), total 10000 ms connected
+])
+AT_CLEANUP
+
+######################################################################
+AT_SETUP([connect backs off])
+AT_KEYWORDS([reconnect])
+AT_DATA([input], [enable
+
+# First connection attempt fails after 1000 ms.
+run
+connecting
+run
+timeout
+run
+connect-failed
+
+# Back off for 1000 ms.
+timeout
+run
+
+# Second connection attempt fails after 1000 ms.
+connecting
+timeout
+run
+connect-failed
+
+# Back off for 2000 ms.
+timeout
+run
+
+# Third connection attempt fails after 2000 ms.
+connecting
+timeout
+run
+connect-failed
+
+# Back off for 4000 ms.
+timeout
+run
+
+# Third connection attempt fails after 4000 ms.
+connecting
+timeout
+run
+connect-failed
+
+# Back off for 8000 ms.
+timeout
+run
+
+# Third connection attempt fails after 8000 ms.
+connecting
+timeout
+run
+connect-failed
+
+# Back off for 8000 ms.
+timeout
+run
+
+# Fourth connection attempt fails after 8000 ms.
+connecting
+timeout
+run
+connect-failed
+])
+OVS_CHECK_LCOV([test-reconnect < input], [0],
+ [### t=1000 ###
+enable
+ in BACKOFF for 0 ms (0 ms backoff)
+
+# First connection attempt fails after 1000 ms.
+run
+ should connect
+connecting
+ in CONNECTING for 0 ms (0 ms backoff)
+run
+ should connect
+timeout
+ advance 1000 ms
+
+### t=2000 ###
+ in CONNECTING for 1000 ms (0 ms backoff)
+run
+ should disconnect
+connect-failed
+ in BACKOFF for 0 ms (1000 ms backoff)
+ 0 successful connections out of 1 attempts, seqno 0
+
+# Back off for 1000 ms.
+timeout
+ advance 1000 ms
+
+### t=3000 ###
+ in BACKOFF for 1000 ms (1000 ms backoff)
+run
+ should connect
+
+# Second connection attempt fails after 1000 ms.
+connecting
+ in CONNECTING for 0 ms (1000 ms backoff)
+timeout
+ advance 1000 ms
+
+### t=4000 ###
+ in CONNECTING for 1000 ms (1000 ms backoff)
+run
+ should disconnect
+connect-failed
+ in BACKOFF for 0 ms (2000 ms backoff)
+ 0 successful connections out of 2 attempts, seqno 0
+
+# Back off for 2000 ms.
+timeout
+ advance 2000 ms
+
+### t=6000 ###
+ in BACKOFF for 2000 ms (2000 ms backoff)
+run
+ should connect
+
+# Third connection attempt fails after 2000 ms.
+connecting
+ in CONNECTING for 0 ms (2000 ms backoff)
+timeout
+ advance 2000 ms
+
+### t=8000 ###
+ in CONNECTING for 2000 ms (2000 ms backoff)
+run
+ should disconnect
+connect-failed
+ in BACKOFF for 0 ms (4000 ms backoff)
+ 0 successful connections out of 3 attempts, seqno 0
+
+# Back off for 4000 ms.
+timeout
+ advance 4000 ms
+
+### t=12000 ###
+ in BACKOFF for 4000 ms (4000 ms backoff)
+run
+ should connect
+
+# Third connection attempt fails after 4000 ms.
+connecting
+ in CONNECTING for 0 ms (4000 ms backoff)
+timeout
+ advance 4000 ms
+
+### t=16000 ###
+ in CONNECTING for 4000 ms (4000 ms backoff)
+run
+ should disconnect
+connect-failed
+ in BACKOFF for 0 ms (8000 ms backoff)
+ 0 successful connections out of 4 attempts, seqno 0
+
+# Back off for 8000 ms.
+timeout
+ advance 8000 ms
+
+### t=24000 ###
+ in BACKOFF for 8000 ms (8000 ms backoff)
+run
+ should connect
+
+# Third connection attempt fails after 8000 ms.
+connecting
+ in CONNECTING for 0 ms (8000 ms backoff)
+timeout
+ advance 8000 ms
+
+### t=32000 ###
+ in CONNECTING for 8000 ms (8000 ms backoff)
+run
+ should disconnect
+connect-failed
+ in BACKOFF for 0 ms (8000 ms backoff)
+ 0 successful connections out of 5 attempts, seqno 0
+
+# Back off for 8000 ms.
+timeout
+ advance 8000 ms
+
+### t=40000 ###
+ in BACKOFF for 8000 ms (8000 ms backoff)
+run
+ should connect
+
+# Fourth connection attempt fails after 8000 ms.
+connecting
+ in CONNECTING for 0 ms (8000 ms backoff)
+timeout
+ advance 8000 ms
+
+### t=48000 ###
+ in CONNECTING for 8000 ms (8000 ms backoff)
+run
+ should disconnect
+connect-failed
+ in BACKOFF for 0 ms (8000 ms backoff)
+ 0 successful connections out of 6 attempts, seqno 0
+])
+AT_CLEANUP
+
+######################################################################
+AT_SETUP([connections with no data preserve backoff])
+AT_KEYWORDS([reconnect])
+AT_DATA([input], [enable
+
+# First connect, then idle timeout kills connection.
+run
+connected
+timeout
+run
+timeout
+run
+disconnected
+
+# Back off for 1000 ms.
+timeout
+run
+
+# Second connect, then idle timeout kills connection.
+run
+connected
+timeout
+run
+timeout
+run
+disconnected
+
+# Back off for 2000 ms.
+timeout
+run
+
+# Third connect, then idle timeout kills connection.
+run
+connected
+timeout
+run
+timeout
+run
+disconnected
+
+# Back off for 4000 ms.
+timeout
+], [### t=1000 ###
+enable
+ in BACKOFF for 0 ms (0 ms backoff)
+
+# First connect, then idle timeout kills connection.
+run
+ should connect
+connected
+ in ACTIVE for 0 ms (0 ms backoff)
+ 1 successful connections out of 1 attempts, seqno 1
+ connected (0 ms), total 0 ms connected
+timeout
+ advance 5000 ms
+
+### t=6000 ###
+ in ACTIVE for 5000 ms (0 ms backoff)
+ connected (5000 ms), total 5000 ms connected
+run
+ should send probe
+ in IDLE for 0 ms (0 ms backoff)
+timeout
+ advance 5000 ms
+
+### t=11000 ###
+ in IDLE for 5000 ms (0 ms backoff)
+ connected (10000 ms), total 10000 ms connected
+run
+ should disconnect
+disconnected
+ in BACKOFF for 0 ms (1000 ms backoff)
+ 1 successful connections out of 1 attempts, seqno 2
+ not connected (0 ms), total 10000 ms connected
+
+# Back off for 1000 ms.
+timeout
+ advance 1000 ms
+
+### t=12000 ###
+ in BACKOFF for 1000 ms (1000 ms backoff)
+run
+ should connect
+
+# Second connect, then idle timeout kills connection.
+run
+ should connect
+connected
+ in ACTIVE for 0 ms (1000 ms backoff)
+ created 1000, last received 1000, last connected 12000
+ 2 successful connections out of 2 attempts, seqno 3
+ connected (0 ms), total 10000 ms connected
+timeout
+ advance 5000 ms
+
+### t=17000 ###
+ in ACTIVE for 5000 ms (1000 ms backoff)
+ connected (5000 ms), total 15000 ms connected
+run
+ should send probe
+ in IDLE for 0 ms (1000 ms backoff)
+timeout
+ advance 5000 ms
+
+### t=22000 ###
+ in IDLE for 5000 ms (1000 ms backoff)
+ connected (10000 ms), total 20000 ms connected
+run
+ should disconnect
+disconnected
+ in BACKOFF for 0 ms (2000 ms backoff)
+ 2 successful connections out of 2 attempts, seqno 4
+ not connected (0 ms), total 20000 ms connected
+
+# Back off for 2000 ms.
+timeout
+ advance 2000 ms
+
+### t=24000 ###
+ in BACKOFF for 2000 ms (2000 ms backoff)
+run
+ should connect
+
+# Third connect, then idle timeout kills connection.
+run
+ should connect
+connected
+ in ACTIVE for 0 ms (2000 ms backoff)
+ created 1000, last received 1000, last connected 24000
+ 3 successful connections out of 3 attempts, seqno 5
+ connected (0 ms), total 20000 ms connected
+timeout
+ advance 5000 ms
+
+### t=29000 ###
+ in ACTIVE for 5000 ms (2000 ms backoff)
+ connected (5000 ms), total 25000 ms connected
+run
+ should send probe
+ in IDLE for 0 ms (2000 ms backoff)
+timeout
+ advance 5000 ms
+
+### t=34000 ###
+ in IDLE for 5000 ms (2000 ms backoff)
+ connected (10000 ms), total 30000 ms connected
+run
+ should disconnect
+disconnected
+ in BACKOFF for 0 ms (4000 ms backoff)
+ 3 successful connections out of 3 attempts, seqno 6
+ not connected (0 ms), total 30000 ms connected
+
+# Back off for 4000 ms.
+timeout
+ advance 4000 ms
+
+### t=38000 ###
+ in BACKOFF for 4000 ms (4000 ms backoff)
+
+])
+AT_CLEANUP
+
+######################################################################
+AT_SETUP([brief connection preserves backoff])
+AT_KEYWORDS([reconnect])
+AT_DATA([input], [enable
+
+# First connection attempt fails after 1000 ms.
+run
+connecting
+run
+timeout
+run
+connect-failed
+
+# Back off for 1000 ms.
+timeout
+run
+
+# Second connection attempt fails after 1000 ms.
+connecting
+timeout
+run
+connect-failed
+
+# Back off for 2000 ms.
+timeout
+run
+
+# Third connection attempt succeeds after 500 ms.
+connecting
+advance 500
+run
+connected
+
+# Connection drops after another 250 ms.
+advance 250
+disconnected
+run
+
+# Back off for 4000 ms.
+timeout
+run
+])
+OVS_CHECK_LCOV([test-reconnect < input], [0],
+ [### t=1000 ###
+enable
+ in BACKOFF for 0 ms (0 ms backoff)
+
+# First connection attempt fails after 1000 ms.
+run
+ should connect
+connecting
+ in CONNECTING for 0 ms (0 ms backoff)
+run
+ should connect
+timeout
+ advance 1000 ms
+
+### t=2000 ###
+ in CONNECTING for 1000 ms (0 ms backoff)
+run
+ should disconnect
+connect-failed
+ in BACKOFF for 0 ms (1000 ms backoff)
+ 0 successful connections out of 1 attempts, seqno 0
+
+# Back off for 1000 ms.
+timeout
+ advance 1000 ms
+
+### t=3000 ###
+ in BACKOFF for 1000 ms (1000 ms backoff)
+run
+ should connect
+
+# Second connection attempt fails after 1000 ms.
+connecting
+ in CONNECTING for 0 ms (1000 ms backoff)
+timeout
+ advance 1000 ms
+
+### t=4000 ###
+ in CONNECTING for 1000 ms (1000 ms backoff)
+run
+ should disconnect
+connect-failed
+ in BACKOFF for 0 ms (2000 ms backoff)
+ 0 successful connections out of 2 attempts, seqno 0
+
+# Back off for 2000 ms.
+timeout
+ advance 2000 ms
+
+### t=6000 ###
+ in BACKOFF for 2000 ms (2000 ms backoff)
+run
+ should connect
+
+# Third connection attempt succeeds after 500 ms.
+connecting
+ in CONNECTING for 0 ms (2000 ms backoff)
+advance 500
+
+### t=6500 ###
+ in CONNECTING for 500 ms (2000 ms backoff)
+run
+ should connect
+connected
+ in ACTIVE for 0 ms (2000 ms backoff)
+ created 1000, last received 1000, last connected 6500
+ 1 successful connections out of 3 attempts, seqno 1
+ connected (0 ms), total 0 ms connected
+
+# Connection drops after another 250 ms.
+advance 250
+
+### t=6750 ###
+ in ACTIVE for 250 ms (2000 ms backoff)
+ connected (250 ms), total 250 ms connected
+disconnected
+ in BACKOFF for 0 ms (4000 ms backoff)
+ 1 successful connections out of 3 attempts, seqno 2
+ not connected (0 ms), total 250 ms connected
+run
+
+# Back off for 4000 ms.
+timeout
+ advance 4000 ms
+
+### t=10750 ###
+ in BACKOFF for 4000 ms (4000 ms backoff)
+run
+ should connect
+])
+AT_CLEANUP
+
+######################################################################
+AT_SETUP([brief connection with data preserves backoff])
+AT_KEYWORDS([reconnect])
+AT_DATA([input], [enable
+
+# First connection attempt fails after 1000 ms.
+run
+connecting
+run
+timeout
+run
+connect-failed
+
+# Back off for 1000 ms.
+timeout
+run
+
+# Second connection attempt fails after 1000 ms.
+connecting
+timeout
+run
+connect-failed
+
+# Back off for 2000 ms.
+timeout
+run
+
+# Third connection attempt succeeds after 500 ms.
+connecting
+advance 500
+run
+connected
+
+# Connection receives 3 chunks of data spaced 250 ms apart.
+advance 250
+run
+received
+advance 250
+run
+received
+advance 250
+run
+received
+
+# Connection drops.
+disconnected
+run
+
+# Back off for 4000 ms.
+timeout
+run
+])
+OVS_CHECK_LCOV([test-reconnect < input], [0],
+ [### t=1000 ###
+enable
+ in BACKOFF for 0 ms (0 ms backoff)
+
+# First connection attempt fails after 1000 ms.
+run
+ should connect
+connecting
+ in CONNECTING for 0 ms (0 ms backoff)
+run
+ should connect
+timeout
+ advance 1000 ms
+
+### t=2000 ###
+ in CONNECTING for 1000 ms (0 ms backoff)
+run
+ should disconnect
+connect-failed
+ in BACKOFF for 0 ms (1000 ms backoff)
+ 0 successful connections out of 1 attempts, seqno 0
+
+# Back off for 1000 ms.
+timeout
+ advance 1000 ms
+
+### t=3000 ###
+ in BACKOFF for 1000 ms (1000 ms backoff)
+run
+ should connect
+
+# Second connection attempt fails after 1000 ms.
+connecting
+ in CONNECTING for 0 ms (1000 ms backoff)
+timeout
+ advance 1000 ms
+
+### t=4000 ###
+ in CONNECTING for 1000 ms (1000 ms backoff)
+run
+ should disconnect
+connect-failed
+ in BACKOFF for 0 ms (2000 ms backoff)
+ 0 successful connections out of 2 attempts, seqno 0
+
+# Back off for 2000 ms.
+timeout
+ advance 2000 ms
+
+### t=6000 ###
+ in BACKOFF for 2000 ms (2000 ms backoff)
+run
+ should connect
+
+# Third connection attempt succeeds after 500 ms.
+connecting
+ in CONNECTING for 0 ms (2000 ms backoff)
+advance 500
+
+### t=6500 ###
+ in CONNECTING for 500 ms (2000 ms backoff)
+run
+ should connect
+connected
+ in ACTIVE for 0 ms (2000 ms backoff)
+ created 1000, last received 1000, last connected 6500
+ 1 successful connections out of 3 attempts, seqno 1
+ connected (0 ms), total 0 ms connected
+
+# Connection receives 3 chunks of data spaced 250 ms apart.
+advance 250
+
+### t=6750 ###
+ in ACTIVE for 250 ms (2000 ms backoff)
+ connected (250 ms), total 250 ms connected
+run
+received
+ created 1000, last received 6750, last connected 6500
+advance 250
+
+### t=7000 ###
+ in ACTIVE for 500 ms (2000 ms backoff)
+ connected (500 ms), total 500 ms connected
+run
+received
+ created 1000, last received 7000, last connected 6500
+advance 250
+
+### t=7250 ###
+ in ACTIVE for 750 ms (2000 ms backoff)
+ connected (750 ms), total 750 ms connected
+run
+received
+ created 1000, last received 7250, last connected 6500
+
+# Connection drops.
+disconnected
+ in BACKOFF for 0 ms (4000 ms backoff)
+ 1 successful connections out of 3 attempts, seqno 2
+ not connected (0 ms), total 750 ms connected
+run
+
+# Back off for 4000 ms.
+timeout
+ advance 4000 ms
+
+### t=11250 ###
+ in BACKOFF for 4000 ms (4000 ms backoff)
+run
+ should connect
+])
+AT_CLEANUP
+
+######################################################################
+AT_SETUP([long connection resets backoff])
+AT_KEYWORDS([reconnect])
+AT_DATA([input], [enable
+
+# First connection attempt fails after 1000 ms.
+run
+connecting
+run
+timeout
+run
+connect-failed
+
+# Back off for 1000 ms.
+timeout
+run
+
+# Second connection attempt fails after 1000 ms.
+connecting
+timeout
+run
+connect-failed
+
+# Back off for 2000 ms.
+timeout
+run
+
+# Third connection attempt succeeds after 500 ms.
+connecting
+advance 500
+run
+connected
+
+# Connection receives 3 chunks of data spaced 2000 ms apart.
+advance 2000
+run
+received
+advance 2000
+run
+received
+advance 2000
+run
+received
+
+# Connection drops.
+disconnected
+run
+
+# Back off for 1000 ms.
+timeout
+run
+])
+OVS_CHECK_LCOV([test-reconnect < input], [0],
+ [### t=1000 ###
+enable
+ in BACKOFF for 0 ms (0 ms backoff)
+
+# First connection attempt fails after 1000 ms.
+run
+ should connect
+connecting
+ in CONNECTING for 0 ms (0 ms backoff)
+run
+ should connect
+timeout
+ advance 1000 ms
+
+### t=2000 ###
+ in CONNECTING for 1000 ms (0 ms backoff)
+run
+ should disconnect
+connect-failed
+ in BACKOFF for 0 ms (1000 ms backoff)
+ 0 successful connections out of 1 attempts, seqno 0
+
+# Back off for 1000 ms.
+timeout
+ advance 1000 ms
+
+### t=3000 ###
+ in BACKOFF for 1000 ms (1000 ms backoff)
+run
+ should connect
+
+# Second connection attempt fails after 1000 ms.
+connecting
+ in CONNECTING for 0 ms (1000 ms backoff)
+timeout
+ advance 1000 ms
+
+### t=4000 ###
+ in CONNECTING for 1000 ms (1000 ms backoff)
+run
+ should disconnect
+connect-failed
+ in BACKOFF for 0 ms (2000 ms backoff)
+ 0 successful connections out of 2 attempts, seqno 0
+
+# Back off for 2000 ms.
+timeout
+ advance 2000 ms
+
+### t=6000 ###
+ in BACKOFF for 2000 ms (2000 ms backoff)
+run
+ should connect
+
+# Third connection attempt succeeds after 500 ms.
+connecting
+ in CONNECTING for 0 ms (2000 ms backoff)
+advance 500
+
+### t=6500 ###
+ in CONNECTING for 500 ms (2000 ms backoff)
+run
+ should connect
+connected
+ in ACTIVE for 0 ms (2000 ms backoff)
+ created 1000, last received 1000, last connected 6500
+ 1 successful connections out of 3 attempts, seqno 1
+ connected (0 ms), total 0 ms connected
+
+# Connection receives 3 chunks of data spaced 2000 ms apart.
+advance 2000
+
+### t=8500 ###
+ in ACTIVE for 2000 ms (2000 ms backoff)
+ connected (2000 ms), total 2000 ms connected
+run
+received
+ created 1000, last received 8500, last connected 6500
+advance 2000
+
+### t=10500 ###
+ in ACTIVE for 4000 ms (2000 ms backoff)
+ connected (4000 ms), total 4000 ms connected
+run
+received
+ created 1000, last received 10500, last connected 6500
+advance 2000
+
+### t=12500 ###
+ in ACTIVE for 6000 ms (2000 ms backoff)
+ connected (6000 ms), total 6000 ms connected
+run
+received
+ created 1000, last received 12500, last connected 6500
+
+# Connection drops.
+disconnected
+ in BACKOFF for 0 ms (1000 ms backoff)
+ 1 successful connections out of 3 attempts, seqno 2
+ not connected (0 ms), total 6000 ms connected
+run
+
+# Back off for 1000 ms.
+timeout
+ advance 1000 ms
+
+### t=13500 ###
+ in BACKOFF for 1000 ms (1000 ms backoff)
+run
+ should connect
+])
+AT_CLEANUP
+
+######################################################################
+AT_SETUP([connection attempt fails quickly])
+AT_KEYWORDS([reconnect])
+AT_DATA([input], [enable
+
+# Connection fails quickly.
+run
+connect-failed ECONNREFUSED
+
+# Back off for 1000 ms.
+run
+timeout
+
+# Connection fails quickly again.
+run
+connect-failed ECONNREFUSED
+
+# Back off for 2000 ms.
+run
+timeout
+])
+OVS_CHECK_LCOV([test-reconnect < input], [0],
+ [### t=1000 ###
+enable
+ in BACKOFF for 0 ms (0 ms backoff)
+
+# Connection fails quickly.
+run
+ should connect
+connect-failed ECONNREFUSED
+ in BACKOFF for 0 ms (1000 ms backoff)
+ 0 successful connections out of 1 attempts, seqno 0
+
+# Back off for 1000 ms.
+run
+timeout
+ advance 1000 ms
+
+### t=2000 ###
+ in BACKOFF for 1000 ms (1000 ms backoff)
+
+# Connection fails quickly again.
+run
+ should connect
+connect-failed ECONNREFUSED
+ in BACKOFF for 0 ms (2000 ms backoff)
+ 0 successful connections out of 2 attempts, seqno 0
+
+# Back off for 2000 ms.
+run
+timeout
+ advance 2000 ms
+
+### t=4000 ###
+ in BACKOFF for 2000 ms (2000 ms backoff)
+])
+AT_CLEANUP
+
--- /dev/null
+/*
+ * Copyright (c) 2009 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 <ctype.h>
+#include "aes128.h"
+#include "util.h"
+
+static void
+hex_to_uint8(const char *input, uint8_t *output, size_t n)
+{
+ size_t i;
+
+ if (strlen(input) != n * 2) {
+ goto error;
+ }
+ for (i = 0; i < n; i++) {
+ unsigned char hi = input[i * 2];
+ unsigned char lo = input[i * 2 + 1];
+
+ if (!isxdigit(hi) || !isxdigit(lo)) {
+ goto error;
+ }
+ output[i] = (hexit_value(hi) << 4) + hexit_value(lo);
+ }
+ return;
+
+error:
+ ovs_fatal(0, "\"%s\" is not exactly %zu hex digits", input, n * 2);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct aes128 aes;
+ uint8_t plaintext[16];
+ uint8_t ciphertext[16];
+ uint8_t key[16];
+ size_t i;
+
+ if (argc != 3) {
+ ovs_fatal(0, "usage: %s KEY PLAINTEXT, where KEY and PLAINTEXT each "
+ "consist of 32 hex digits", argv[0]);
+ }
+
+ hex_to_uint8(argv[1], key, 16);
+ hex_to_uint8(argv[2], plaintext, 16);
+
+ aes128_schedule(&aes, key);
+ aes128_encrypt(&aes, plaintext, ciphertext);
+ for (i = 0; i < 16; i++) {
+ printf("%02x", ciphertext[i]);
+ }
+ putchar('\n');
+
+ return 0;
+}
}
}
- rule = xcalloc(1, sizeof *rule);
+ rule = xzalloc(sizeof *rule);
cls_rule_from_flow(&rule->cls_rule, &flow, wildcards,
!wildcards ? UINT_MAX : priority);
return rule;
--- /dev/null
+/*
+ * Copyright (c) 2009 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 "util.h"
+#include <stdlib.h>
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ char *dir = dir_name(argv[i]);
+ puts(dir);
+ free(dir);
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2009 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 "json.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+
+#include "util.h"
+
+/* --pretty: If set, the JSON output is pretty-printed, instead of printed as
+ * compactly as possible. */
+static int pretty = 0;
+
+/* --multiple: If set, the input is a sequence of JSON objects or arrays,
+ * instead of exactly one object or array. */
+static int multiple = 0;
+
+static bool
+print_and_free_json(struct json *json)
+{
+ bool ok;
+ if (json->type == JSON_STRING) {
+ printf("error: %s\n", json->u.string);
+ ok = false;
+ } else {
+ char *s = json_to_string(json, JSSF_SORT | (pretty ? JSSF_PRETTY : 0));
+ puts(s);
+ free(s);
+ ok = true;
+ }
+ json_destroy(json);
+ return ok;
+}
+
+static bool
+refill(FILE *file, void *buffer, size_t buffer_size, size_t *n, size_t *used)
+{
+ *used = 0;
+ if (feof(file)) {
+ *n = 0;
+ return false;
+ } else {
+ *n = fread(buffer, 1, buffer_size, file);
+ if (ferror(file)) {
+ ovs_fatal(errno, "Error reading input file");
+ }
+ return *n > 0;
+ }
+}
+
+static bool
+parse_multiple(const char *input_file)
+{
+ struct json_parser *parser;
+ char buffer[BUFSIZ];
+ size_t n, used;
+ FILE *file;
+ bool ok;
+
+ file = fopen(input_file, "r");
+ if (!file) {
+ ovs_fatal(errno, "Cannot open \"%s\"", input_file);
+ }
+
+ parser = NULL;
+ n = used = 0;
+ ok = true;
+ while (used < n || refill(file, buffer, sizeof buffer, &n, &used)) {
+ if (!parser && isspace((unsigned char) buffer[used])) {
+ /* Skip white space. */
+ used++;
+ } else {
+ if (!parser) {
+ parser = json_parser_create(0);
+ }
+
+ used += json_parser_feed(parser, &buffer[used], n - used);
+ if (used < n) {
+ if (!print_and_free_json(json_parser_finish(parser))) {
+ ok = false;
+ }
+ parser = NULL;
+ }
+ }
+ }
+ if (parser) {
+ if (!print_and_free_json(json_parser_finish(parser))) {
+ ok = false;
+ }
+ }
+ return ok;
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *input_file;
+ bool ok;
+
+ set_program_name(argv[0]);
+
+ for (;;) {
+ static const struct option options[] = {
+ {"pretty", no_argument, &pretty, 1},
+ {"multiple", no_argument, &multiple, 1},
+ };
+ int option_index = 0;
+ int c = getopt_long (argc, argv, "", options, &option_index);
+
+ if (c == -1) {
+ break;
+ }
+ switch (c) {
+ case 0:
+ break;
+
+ case '?':
+ exit(1);
+
+ default:
+ abort();
+ }
+ }
+
+ if (argc - optind != 1) {
+ ovs_fatal(0, "usage: %s [--pretty] [--multiple] INPUT.json",
+ program_name);
+ }
+
+ input_file = argv[optind];
+ if (!strcmp(input_file, "-")) {
+ input_file = "/dev/stdin";
+ }
+
+ if (multiple) {
+ ok = parse_multiple(input_file);
+ } else {
+ ok = print_and_free_json(json_from_file(input_file));
+ }
+
+ return !ok;
+}
--- /dev/null
+/*
+ * Copyright (c) 2009 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.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[] = {
+ {"verbose", optional_argument, 0, 'v'},
+ {"help", no_argument, 0, 'h'},
+ DAEMON_LONG_OPTIONS,
+ {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
+
+ 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);
+ 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_listen(int argc 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();
+ }
+}
+
+
+static void
+do_request(int argc 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_notify(int argc 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 UNUSED, char *argv[] 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 },
+};
--- /dev/null
+/*
+ * Copyright (c) 2009 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 "lockfile.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "process.h"
+#include "timeval.h"
+#include "util.h"
+
+#undef NDEBUG
+#include <assert.h>
+
+struct test {
+ const char *name;
+ void (*function)(void);
+};
+
+static const struct test tests[];
+
+static void
+run_lock_and_unlock(void)
+{
+ struct lockfile *lockfile;
+
+ assert(lockfile_lock("file", 0, &lockfile) == 0);
+ lockfile_unlock(lockfile);
+}
+
+static void
+run_lock_and_unlock_twice(void)
+{
+ struct lockfile *lockfile;
+
+ assert(lockfile_lock("file", 0, &lockfile) == 0);
+ lockfile_unlock(lockfile);
+
+ assert(lockfile_lock("file", 0, &lockfile) == 0);
+ lockfile_unlock(lockfile);
+}
+
+static void
+run_lock_blocks_same_process(void)
+{
+ struct lockfile *lockfile;
+
+ assert(lockfile_lock("file", 0, &lockfile) == 0);
+ assert(lockfile_lock("file", 0, &lockfile) == EDEADLK);
+ lockfile_unlock(lockfile);
+}
+
+static void
+run_lock_blocks_same_process_twice(void)
+{
+ struct lockfile *lockfile;
+
+ assert(lockfile_lock("file", 0, &lockfile) == 0);
+ assert(lockfile_lock("file", 0, &lockfile) == EDEADLK);
+ assert(lockfile_lock("file", 0, &lockfile) == EDEADLK);
+ lockfile_unlock(lockfile);
+}
+
+static enum { PARENT, CHILD }
+do_fork(void)
+{
+ switch (fork()) {
+ case 0:
+ time_postfork();
+ lockfile_postfork();
+ return CHILD;
+
+ default:
+ return PARENT;
+
+ case -1:
+ /* Error. */
+ ovs_fatal(errno, "fork failed");
+ }
+}
+
+static void
+run_lock_blocks_other_process(void)
+{
+ struct lockfile *lockfile;
+
+ assert(lockfile_lock("file", 0, &lockfile) == 0);
+ if (do_fork() == CHILD) {
+ assert(lockfile_lock("file", 0, &lockfile) == EAGAIN);
+ exit(11);
+ }
+}
+
+static void
+run_lock_twice_blocks_other_process(void)
+{
+ struct lockfile *lockfile, *dummy;
+
+ assert(lockfile_lock("file", 0, &lockfile) == 0);
+ assert(lockfile_lock("file", 0, &dummy) == EDEADLK);
+ if (do_fork() == CHILD) {
+ assert(lockfile_lock("file", 0, &dummy) == EAGAIN);
+ exit(11);
+ }
+}
+
+static void
+run_lock_and_unlock_allows_other_process(void)
+{
+ struct lockfile *lockfile;
+
+ assert(lockfile_lock("file", 0, &lockfile) == 0);
+ lockfile_unlock(lockfile);
+
+ if (do_fork() == CHILD) {
+ assert(lockfile_lock("file", 0, &lockfile) == 0);
+ exit(11);
+ }
+}
+
+static void
+run_lock_timeout_gets_the_lock(void)
+{
+ struct lockfile *lockfile;
+
+ assert(lockfile_lock("file", 0, &lockfile) == 0);
+
+ if (do_fork() == CHILD) {
+ assert(lockfile_lock("file", TIME_UPDATE_INTERVAL * 3,
+ &lockfile) == 0);
+ exit(11);
+ } else {
+ long long int now = time_msec();
+ while (time_msec() < now + TIME_UPDATE_INTERVAL) {
+ pause();
+ }
+ lockfile_unlock(lockfile);
+ }
+}
+
+static void
+run_lock_timeout_runs_out(void)
+{
+ struct lockfile *lockfile;
+
+ assert(lockfile_lock("file", 0, &lockfile) == 0);
+
+ if (do_fork() == CHILD) {
+ assert(lockfile_lock("file", TIME_UPDATE_INTERVAL,
+ &lockfile) == ETIMEDOUT);
+ exit(11);
+ } else {
+ long long int now = time_msec();
+ while (time_msec() < now + TIME_UPDATE_INTERVAL * 3) {
+ pause();
+ }
+ lockfile_unlock(lockfile);
+ }
+}
+
+static void
+run_lock_multiple(void)
+{
+ struct lockfile *a, *b, *c, *dummy;
+
+ assert(lockfile_lock("a", 0, &a) == 0);
+ assert(lockfile_lock("b", 0, &b) == 0);
+ assert(lockfile_lock("c", 0, &c) == 0);
+
+ lockfile_unlock(a);
+ assert(lockfile_lock("a", 0, &a) == 0);
+ assert(lockfile_lock("a", 0, &dummy) == EDEADLK);
+ lockfile_unlock(a);
+
+ lockfile_unlock(b);
+ assert(lockfile_lock("a", 0, &a) == 0);
+
+ lockfile_unlock(c);
+ lockfile_unlock(a);
+}
+
+static void
+run_help(void)
+{
+ size_t i;
+
+ printf("usage: %s TESTNAME\n"
+ "where TESTNAME is one of the following:\n",
+ program_name);
+ for (i = 0; tests[i].name; i++) {
+ fprintf(stderr, "\t%s\n", tests[i].name);
+ }
+}
+
+static const struct test tests[] = {
+#define TEST(NAME) { #NAME, run_##NAME }
+ TEST(lock_and_unlock),
+ TEST(lock_and_unlock_twice),
+ TEST(lock_blocks_same_process),
+ TEST(lock_blocks_same_process_twice),
+ TEST(lock_blocks_other_process),
+ TEST(lock_twice_blocks_other_process),
+ TEST(lock_and_unlock_allows_other_process),
+ TEST(lock_timeout_gets_the_lock),
+ TEST(lock_timeout_runs_out),
+ TEST(lock_multiple),
+ TEST(help),
+ { 0, 0 }
+#undef TEST
+};
+
+int
+main(int argc, char *argv[])
+{
+ size_t i;
+
+ set_program_name(argv[0]);
+ time_init();
+
+ if (argc != 2) {
+ ovs_fatal(0, "exactly one argument required; use \"%s help\" for help",
+ program_name);
+ return 1;
+ }
+
+ for (i = 0; tests[i].name; i++) {
+ if (!strcmp(argv[1], tests[i].name)) {
+ int n_children;
+ int status;
+
+ (tests[i].function)();
+
+ n_children = 0;
+ while (wait(&status) > 0) {
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 11) {
+ n_children++;
+ } else {
+ ovs_fatal(0, "child exited in unexpected way: %s",
+ process_status_msg(status));
+ }
+ }
+ if (errno != ECHILD) {
+ ovs_fatal(errno, "wait");
+ }
+
+ printf("%s: success (%d child%s)\n",
+ tests[i].name, n_children, n_children != 1 ? "ren" : "");
+ exit(0);
+ }
+ }
+ ovs_fatal(0, "unknown test \"%s\"; use \"%s help\" for help",
+ argv[1], program_name);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2009 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 "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/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-type JSON\n"
+ " parse JSON as OVSDB type, and re-serialize\n"
+ " parse-atoms TYPE ATOM...\n"
+ " parse ATOMs as atoms of given TYPE, and re-serialize\n"
+ " sort-atoms TYPE ATOM...\n"
+ " print ATOMs in sorted order, and re-serialize\n"
+ " parse-data TYPE DATUM...\n"
+ " parse 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"
+ " 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"
+ " 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
+check_ovsdb_error(struct ovsdb_error *error)
+{
+ if (error) {
+ ovs_fatal(0, "%s", ovsdb_error_to_string(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);
+ } else {
+ printf("%s: %s successful\n", name, command);
+ }
+ }
+
+ ovsdb_log_close(log);
+}
+
+static void
+do_parse_atomic_type(int argc 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[])
+{
+ 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));
+}
+
+static void
+do_parse_atoms(int argc, char *argv[])
+{
+ enum ovsdb_atomic_type type;
+ struct json *json;
+ int i;
+
+ json = unbox_json(parse_json(argv[1]));
+ check_ovsdb_error(ovsdb_atomic_type_from_json(&type, json));
+ json_destroy(json);
+
+ for (i = 2; i < argc; i++) {
+ union ovsdb_atom atom;
+
+ json = unbox_json(parse_json(argv[i]));
+ check_ovsdb_error(ovsdb_atom_from_json(&atom, type, json, NULL));
+ json_destroy(json);
+
+ print_and_free_json(ovsdb_atom_to_json(&atom, type));
+
+ ovsdb_atom_destroy(&atom, type);
+ }
+}
+
+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);
+ }
+}
+
+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_sort_atoms(int argc UNUSED, char *argv[])
+{
+ enum ovsdb_atomic_type type;
+ 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_atomic_type_from_json(&type, 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], type,
+ json->u.array.elems[i], NULL));
+ }
+ json_destroy(json);
+
+ /* Sort atoms. */
+ compare_atoms_atomic_type = 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], type);
+ ovsdb_atom_destroy(&atoms[i], type);
+ }
+ print_and_free_json(json_array_create(json_atoms, n_atoms));
+}
+
+static void
+do_parse_column(int argc 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_parse_table(int argc 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]);
+ }
+ }
+ }
+ 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_evaluate_conditions(int argc 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]);
+ }
+ for (i = 0; i < n_rows; i++) {
+ ovsdb_row_destroy(rows[i]);
+ }
+ 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(int argc 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_query_distinct(int argc 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]);
+ 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[])
+{
+ 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);
+ 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);
+ json_destroy(result);
+ ovsdb_trigger_destroy(&t->trigger);
+ free(t);
+}
+
+static void
+do_trigger(int argc 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_help(int argc UNUSED, char *argv[] 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_commit(int argc UNUSED, char *argv[] UNUSED)
+{
+ ovsdb_txn_commit(do_transact_txn, false);
+ do_transact_txn = NULL;
+}
+
+static void
+do_transact_abort(int argc UNUSED, char *argv[] 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_insert(int argc UNUSED, char *argv[] 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_delete(int argc UNUSED, char *argv[] UNUSED)
+{
+ const struct ovsdb_row *row = do_transact_find_row(argv[1]);
+ ovsdb_txn_row_delete(do_transact_txn, row);
+}
+
+static void
+do_transact_modify(int argc UNUSED, char *argv[] 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 UNUSED, char *argv[] 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_selflink(const void *a_, const void *b_)
+{
+ const struct idltest_selflink *const *ap = a_;
+ const struct idltest_selflink *const *bp = b_;
+ const struct idltest_selflink *a = *ap;
+ const struct idltest_selflink *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_selflink *sl;
+ 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_SELFLINK_FOR_EACH (sl, idl) {
+ struct idltest_selflink **links;
+ size_t i;
+
+ printf("%03d: i=%"PRId64" k=", step, sl->i);
+ if (sl->k) {
+ printf("%"PRId64, sl->k->i);
+ }
+ printf(" ka=[");
+ links = xmemdup(sl->ka, sl->n_ka * sizeof *sl->ka);
+ qsort(links, sl->n_ka, sizeof *links, compare_selflink);
+ for (i = 0; i < sl->n_ka; i++) {
+ printf("%s%"PRId64, i ? " " : "", links[i]->i);
+ }
+ free(links);
+ printf("] uuid="UUID_FMT"\n", UUID_ARGS(&sl->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);
+ ovsdb_symbol_table_put(symtab, name, &uuid);
+ 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 uuid *uuid;
+
+ uuid = ovsdb_symbol_table_get(symtab, json->u.string);
+ if (uuid) {
+ free(json->u.string);
+ json->u.string = xasprintf(UUID_FMT, UUID_ARGS(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 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;
+
+ 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;
+ }
+
+ symtab = ovsdb_symbol_table_create();
+ for (i = 2; i < argc; i++) {
+ struct jsonrpc_msg *request, *reply;
+ int error;
+
+ seqno = print_updated_idl(idl, rpc, step++, seqno);
+
+ if (!strcmp(argv[i], "reconnect")) {
+ printf("%03d: reconnect\n", step++);
+ ovsdb_idl_force_reconnect(idl);
+ } else {
+ struct json *json = parse_json(argv[i]);
+ 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-type", 1, 1, do_parse_type },
+ { "parse-atoms", 2, INT_MAX, do_parse_atoms },
+ { "parse-data", 2, INT_MAX, do_parse_data },
+ { "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 },
+ { "query", 3, 3, do_query },
+ { "query-distinct", 4, 4, do_query_distinct },
+ { "transact", 1, INT_MAX, do_transact },
+ { "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 },
+};
--- /dev/null
+/*
+ * Copyright (c) 2009 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;
+ 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;
+ 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;
+ }
+
+ return 0;
+}
+
+static void
+do_enable(int argc UNUSED, char *argv[] UNUSED)
+{
+ reconnect_enable(reconnect, now);
+}
+
+static void
+do_disable(int argc UNUSED, char *argv[] UNUSED)
+{
+ reconnect_disable(reconnect, now);
+}
+
+static void
+do_force_reconnect(int argc UNUSED, char *argv[] 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_disconnected(int argc UNUSED, char *argv[])
+{
+ reconnect_disconnected(reconnect, now, error_from_string(argv[1]));
+}
+
+static void
+do_connecting(int argc UNUSED, char *argv[] UNUSED)
+{
+ reconnect_connecting(reconnect, now);
+}
+
+static void
+do_connect_failed(int argc UNUSED, char *argv[])
+{
+ reconnect_connect_failed(reconnect, now, error_from_string(argv[1]));
+}
+
+static void
+do_connected(int argc UNUSED, char *argv[] UNUSED)
+{
+ reconnect_connected(reconnect, now);
+}
+
+static void
+do_received(int argc UNUSED, char *argv[] 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_advance(int argc UNUSED, char *argv[])
+{
+ now += atoi(argv[1]);
+}
+
+static void
+do_timeout(int argc UNUSED, char *argv[] 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
+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 },
+ { NULL, 0, 0, NULL },
+};
+
--- /dev/null
+/*
+ * Copyright (c) 2009 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 "timeval.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "daemon.h"
+#include "util.h"
+
+#undef NDEBUG
+#include <assert.h>
+
+static long long int
+gettimeofday_in_msec(void)
+{
+ struct timeval tv;
+
+ assert(!gettimeofday(&tv, NULL));
+ return timeval_to_msec(&tv);
+}
+
+static void
+do_test(void)
+{
+ /* Wait until we are awakened by a signal (typically EINTR due to the
+ * setitimer()). Then ensure that, if time has really advanced by
+ * TIME_UPDATE_INTERVAL, then time_msec() reports that it advanced.
+ */
+ long long int start_time_msec;
+ long long int start_gtod;
+
+ start_time_msec = time_msec();
+ start_gtod = gettimeofday_in_msec();
+ for (;;) {
+ /* Wait up to 1 second. Using select() to do the timeout avoids
+ * interfering with the interval timer. */
+ struct timeval timeout;
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ assert(select(0, NULL, NULL, NULL, &timeout) == -1 && errno == EINTR);
+
+ if (gettimeofday_in_msec() - start_gtod >= TIME_UPDATE_INTERVAL) {
+ assert(time_msec() - start_time_msec >= TIME_UPDATE_INTERVAL);
+ break;
+ }
+ }
+}
+
+static void
+usage(void)
+{
+ ovs_fatal(0, "usage: %s TEST, where TEST is \"plain\" or \"daemon\"",
+ program_name);
+}
+
+int
+main(int argc, char *argv[])
+{
+ set_program_name(argv[0]);
+ time_init();
+
+ if (argc != 2) {
+ usage();
+ } else if (!strcmp(argv[1], "plain")) {
+ do_test();
+ } else if (!strcmp(argv[1], "daemon")) {
+ /* Test that time still advances even in a daemon. This is an
+ * interesting test because fork() cancels the interval timer. */
+ char cwd[1024];
+ FILE *success;
+
+ assert(getcwd(cwd, sizeof cwd) == cwd);
+
+ unlink("test-timeval.success");
+
+ /* Daemonize, with a pidfile in the current directory. */
+ set_detach();
+ set_pidfile(xasprintf("%s/test-timeval.pid", cwd));
+ set_no_chdir();
+ daemonize();
+
+ /* Run the test. */
+ do_test();
+
+ /* Report success by writing out a file, since the ultimate invoker of
+ * test-timeval can't wait on the daemonized process. */
+ success = fopen("test-timeval.success", "w");
+ if (!success) {
+ ovs_fatal(errno, "test-timeval.success: create failed");
+ }
+ fprintf(success, "success\n");
+ fclose(success);
+ } else {
+ usage();
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2009 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 "uuid.h"
+#include <stdio.h>
+
+int
+main(int argc, char *argv[])
+{
+ struct uuid uuid;
+
+ if (argc == 1) {
+ uuid_generate(&uuid);
+ } else if (argc == 2) {
+ if (!uuid_from_string(&uuid, argv[1])) {
+ ovs_fatal(0, "\"%s\" is not a valid UUID", argv[1]);
+ }
+ } else {
+ ovs_fatal(0, "usage: %s [UUID]", argv[0]);
+ }
+
+ printf(UUID_FMT"\n", UUID_ARGS(&uuid));
+
+ return 0;
+}
AT_TESTED([ovs-vswitchd])
AT_TESTED([ovs-vsctl])
+AT_TESTED([perl])
m4_include([tests/lcov-pre.at])
m4_include([tests/library.at])
+m4_include([tests/dir_name.at])
+m4_include([tests/aes128.at])
+m4_include([tests/uuid.at])
+m4_include([tests/json.at])
+m4_include([tests/jsonrpc.at])
+m4_include([tests/timeval.at])
+m4_include([tests/lockfile.at])
+m4_include([tests/reconnect.at])
+m4_include([tests/ovsdb.at])
m4_include([tests/stp.at])
m4_include([tests/ovs-vsctl.at])
m4_include([tests/lcov-post.at])
--- /dev/null
+AT_BANNER([timeval unit tests])
+
+AT_SETUP([check that time advances])
+AT_KEYWORDS([timeval])
+OVS_CHECK_LCOV([test-timeval plain], [0])
+AT_CLEANUP
+
+AT_SETUP([check that time advances after daemonize()])
+AT_KEYWORDS([timeval])
+OVS_CHECK_LCOV([test-timeval daemon], [0])
+AT_CHECK(
+ [# First try a quick sleep, so that the test completes very quickly
+ # in the normal case. POSIX doesn't require fractional times to
+ # work, so this might not work.
+ sleep 0.1; if test -e test-timeval.success; then echo success; exit 0; fi
+ # Then wait up to 2 seconds.
+ sleep 1; if test -e test-timeval.success; then echo success; exit 0; fi
+ sleep 1; if test -e test-timeval.success; then echo success; exit 0; fi
+ echo failure; exit 1],
+ [0], [success
+], [])
+AT_CLEANUP
--- /dev/null
+AT_BANNER([UUID unit tests])
+
+m4_define([UUID_REGEX],
+ [[[0-9a-f]\{8\}-[0-9a-f]\{4\}-4[0-9a-f]\{3\}-[89ab][0-9a-f]\{3\}-[0-9a-f]\{12\}$]])
+
+m4_define([CHECK_UUID],
+ [if expr "$uuid" : 'UUID_REGEX' > /dev/null
+ then
+ :
+ else
+ echo "$uuid: not a random UUID"
+ exit 1
+ fi])
+
+# This test is a strict subset of the larger test down below, but it
+# allows us to get test coverage data via OVS_CHECK_LCOV.
+AT_SETUP([UUID generation])
+AT_KEYWORDS([UUID])
+OVS_CHECK_LCOV([test-uuid > uuid])
+AT_CHECK([
+ uuid=`cat uuid`
+ CHECK_UUID])
+AT_CLEANUP
+
+# This test is a strict subset of the larger test down below, but it
+# allows us to get test coverage data via OVS_CHECK_LCOV.
+AT_SETUP([UUID parsing and serialization])
+AT_KEYWORDS([UUID])
+OVS_CHECK_LCOV([test-uuid f47ac10b-58cc-4372-a567-0e02b2c3d479], [0],
+ [f47ac10b-58cc-4372-a567-0e02b2c3d479
+])
+AT_CLEANUP
+
+AT_SETUP([UUID generation, parsing, serialization])
+AT_KEYWORDS([UUID])
+AT_CHECK([
+ uuids=
+ for i in m4_for([count], [1], [100], [1], [count ]); do
+ # Generate random UUID and check that it is in the expected format.
+ uuid=`test-uuid`
+ CHECK_UUID
+
+ # Verify that $uuid does not duplicate any UUID generated so far.
+ case $uuids in
+ *$uuid*)
+ echo "$uuid: generated duplicate UUID"
+ exit 1
+ esac
+ uuids="$uuids $uuid"
+
+ # Verify that test-uuid parses and re-serializes this UUID correctly.
+ serialized=`test-uuid $uuid`
+ if test "$uuid" != "$serialized"; then
+ echo "$uuid: test-uuid serialized this as $serialized"
+ exit 1
+ fi
+ done], [0])
+AT_CLEANUP
--- /dev/null
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+
+our %uuids;
+our $n_uuids = 0;
+sub lookup_uuid {
+ my ($uuid) = @_;
+ if (!exists($uuids{$uuid})) {
+ $uuids{$uuid} = $n_uuids++;
+ }
+ return "<$uuids{$uuid}>";
+}
+
+my $u = '[0-9a-fA-F]';
+my $uuid_re = "${u}{8}-${u}{4}-${u}{4}-${u}{4}-${u}{12}";
+while (<>) {
+ s/($uuid_re)/lookup_uuid($1)/eg;
+ print $_;
+}
#include "vlog.h"
#define THIS_MODULE VLM_dpctl
-struct command {
- const char *name;
- int min_args;
- int max_args;
- void (*handler)(int argc, char *argv[]);
-};
-
-static struct command all_commands[];
+static const struct command all_commands[];
static void usage(void) NO_RETURN;
static void parse_options(int argc, char *argv[]);
-int main(int argc, char *argv[])
+int
+main(int argc, char *argv[])
{
- struct command *p;
-
set_program_name(argv[0]);
time_init();
vlog_init();
parse_options(argc, argv);
signal(SIGPIPE, SIG_IGN);
-
- argc -= optind;
- argv += optind;
- if (argc < 1)
- ovs_fatal(0, "missing command name; use --help for help");
-
- for (p = all_commands; p->name != NULL; p++) {
- if (!strcmp(p->name, argv[0])) {
- int n_arg = argc - 1;
- if (n_arg < p->min_args)
- ovs_fatal(0, "'%s' command requires at least %d arguments",
- p->name, p->min_args);
- else if (n_arg > p->max_args)
- ovs_fatal(0, "'%s' command takes at most %d arguments",
- p->name, p->max_args);
- else {
- p->handler(argc, argv);
- if (ferror(stdout)) {
- ovs_fatal(0, "write to stdout failed");
- }
- if (ferror(stderr)) {
- ovs_fatal(0, "write to stderr failed");
- }
- exit(0);
- }
- }
- }
- ovs_fatal(0, "unknown command '%s'; use --help for help", argv[0]);
-
+ run_command(argc - optind, argv + optind, all_commands);
return 0;
}
usage();
}
-static struct command all_commands[] = {
+static const struct command all_commands[] = {
{ "add-dp", 1, INT_MAX, do_add_dp },
{ "del-dp", 1, 1, do_del_dp },
{ "add-if", 2, INT_MAX, do_add_if },
#define MOD_PORT_CMD_FLOOD "flood"
#define MOD_PORT_CMD_NOFLOOD "noflood"
+/* Use strict matching for flow mod commands? */
+static bool strict;
-/* Settings that may be configured by the user. */
-struct settings {
- bool strict; /* Use strict matching for flow mod commands */
-};
-
-struct command {
- const char *name;
- int min_args;
- int max_args;
- void (*handler)(const struct settings *, int argc, char *argv[]);
-};
-
-static struct command all_commands[];
+static const struct command all_commands[];
static void usage(void) NO_RETURN;
-static void parse_options(int argc, char *argv[], struct settings *);
+static void parse_options(int argc, char *argv[]);
-int main(int argc, char *argv[])
+int
+main(int argc, char *argv[])
{
- struct settings s;
- struct command *p;
-
set_program_name(argv[0]);
time_init();
vlog_init();
- parse_options(argc, argv, &s);
+ parse_options(argc, argv);
signal(SIGPIPE, SIG_IGN);
-
- argc -= optind;
- argv += optind;
- if (argc < 1)
- ovs_fatal(0, "missing command name; use --help for help");
-
- for (p = all_commands; p->name != NULL; p++) {
- if (!strcmp(p->name, argv[0])) {
- int n_arg = argc - 1;
- if (n_arg < p->min_args)
- ovs_fatal(0, "'%s' command requires at least %d arguments",
- p->name, p->min_args);
- else if (n_arg > p->max_args)
- ovs_fatal(0, "'%s' command takes at most %d arguments",
- p->name, p->max_args);
- else {
- p->handler(&s, argc, argv);
- if (ferror(stdout)) {
- ovs_fatal(0, "write to stdout failed");
- }
- if (ferror(stderr)) {
- ovs_fatal(0, "write to stderr failed");
- }
- exit(0);
- }
- }
- }
- ovs_fatal(0, "unknown command '%s'; use --help for help", argv[0]);
-
+ run_command(argc - optind, argv + optind, all_commands);
return 0;
}
static void
-parse_options(int argc, char *argv[], struct settings *s)
+parse_options(int argc, char *argv[])
{
enum {
OPT_STRICT = UCHAR_MAX + 1
};
char *short_options = long_options_to_short_options(long_options);
- /* Set defaults that we can figure out before parsing options. */
- s->strict = false;
-
for (;;) {
unsigned long int timeout;
int c;
break;
case OPT_STRICT:
- s->strict = true;
+ strict = true;
break;
VCONN_SSL_OPTION_HANDLERS
}
static void
-do_show(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+do_show(int argc UNUSED, char *argv[])
{
dump_trivial_transaction(argv[1], OFPT_FEATURES_REQUEST);
dump_trivial_transaction(argv[1], OFPT_GET_CONFIG_REQUEST);
}
static void
-do_status(const struct settings *s UNUSED, int argc, char *argv[])
+do_status(int argc, char *argv[])
{
struct nicira_header *request, *reply;
struct vconn *vconn;
}
static void
-do_dump_desc(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+do_dump_desc(int argc UNUSED, char *argv[])
{
dump_trivial_stats_transaction(argv[1], OFPST_DESC);
}
static void
-do_dump_tables(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+do_dump_tables(int argc UNUSED, char *argv[])
{
dump_trivial_stats_transaction(argv[1], OFPST_TABLE);
}
}
static void
-do_dump_flows(const struct settings *s UNUSED, int argc, char *argv[])
+do_dump_flows(int argc, char *argv[])
{
struct ofp_flow_stats_request *req;
uint16_t out_port;
}
static void
-do_dump_aggregate(const struct settings *s UNUSED, int argc, char *argv[])
+do_dump_aggregate(int argc, char *argv[])
{
struct ofp_aggregate_stats_request *req;
struct ofpbuf *request;
}
static void
-do_add_flow(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+do_add_flow(int argc UNUSED, char *argv[])
{
struct vconn *vconn;
struct ofpbuf *buffer;
}
static void
-do_add_flows(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+do_add_flows(int argc UNUSED, char *argv[])
{
struct vconn *vconn;
FILE *file;
}
static void
-do_mod_flows(const struct settings *s, int argc UNUSED, char *argv[])
+do_mod_flows(int argc UNUSED, char *argv[])
{
uint16_t priority, idle_timeout, hard_timeout;
struct vconn *vconn;
NULL, NULL, &priority, &idle_timeout, &hard_timeout);
ofm = buffer->data;
ofm->match = match;
- if (s->strict) {
+ if (strict) {
ofm->command = htons(OFPFC_MODIFY_STRICT);
} else {
ofm->command = htons(OFPFC_MODIFY);
vconn_close(vconn);
}
-static void do_del_flows(const struct settings *s, int argc, char *argv[])
+static void do_del_flows(int argc, char *argv[])
{
struct vconn *vconn;
uint16_t priority;
ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, NULL,
&out_port, &priority, NULL, NULL);
- if (s->strict) {
+ if (strict) {
ofm->command = htons(OFPFC_DELETE_STRICT);
} else {
ofm->command = htons(OFPFC_DELETE);
}
static void
-do_monitor(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+do_monitor(int argc UNUSED, char *argv[])
{
struct vconn *vconn;
}
static void
-do_dump_ports(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+do_dump_ports(int argc UNUSED, char *argv[])
{
dump_trivial_stats_transaction(argv[1], OFPST_PORT);
}
static void
-do_probe(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+do_probe(int argc UNUSED, char *argv[])
{
struct ofpbuf *request;
struct vconn *vconn;
}
static void
-do_mod_port(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+do_mod_port(int argc UNUSED, char *argv[])
{
struct ofpbuf *request, *reply;
struct ofp_switch_features *osf;
}
static void
-do_ping(const struct settings *s UNUSED, int argc, char *argv[])
+do_ping(int argc, char *argv[])
{
size_t max_payload = 65535 - sizeof(struct ofp_header);
unsigned int payload;
}
static void
-do_benchmark(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+do_benchmark(int argc UNUSED, char *argv[])
{
size_t max_payload = 65535 - sizeof(struct ofp_header);
struct timeval start, end;
}
static void
-do_execute(const struct settings *s UNUSED, int argc, char *argv[])
+do_execute(int argc, char *argv[])
{
struct vconn *vconn;
struct ofpbuf *request;
}
static void
-do_help(const struct settings *s UNUSED, int argc UNUSED, char *argv[] UNUSED)
+do_help(int argc UNUSED, char *argv[] UNUSED)
{
usage();
}
-static struct command all_commands[] = {
+static const struct command all_commands[] = {
{ "show", 1, 1, do_show },
{ "status", 1, 2, do_status },
{ "monitor", 1, 3, do_monitor },
vswitchd/proc-net-compat.h \
vswitchd/ovs-vswitchd.c \
vswitchd/ovs-vswitchd.h \
+ vswitchd/vswitch-idl.c \
+ vswitchd/vswitch-idl.h \
vswitchd/xenserver.c \
vswitchd/xenserver.h
vswitchd_ovs_vswitchd_LDADD = \
vswitchd/ovs-vswitchd.conf.5.in \
vswitchd/ovs-vswitchd.8.in \
vswitchd/ovs-brcompatd.8.in
+
+EXTRA_DIST += vswitchd/vswitch-idl.ovsidl
+BUILT_SOURCES += vswitchd/vswitch-idl.c vswitchd/vswitch-idl.h
+DISTCLEANFILES += vswitchd/vswitch-idl.c vswitchd/vswitch-idl.h
+noinst_DATA += vswitchd/vswitch-idl.ovsschema
+DISTCLEANFILES += vswitchd/vswitch-idl.ovsschema
#include "unixctl.h"
#include "vconn.h"
#include "vconn-ssl.h"
+#include "vswitchd/vswitch-idl.h"
#include "xenserver.h"
#include "xtoxll.h"
static struct bridge *bridge_create(const char *name);
static void bridge_destroy(struct bridge *);
static struct bridge *bridge_lookup(const char *name);
-static void bridge_unixctl_dump_flows(struct unixctl_conn *, const char *);
+static unixctl_cb_func bridge_unixctl_dump_flows;
static int bridge_run_one(struct bridge *);
static void bridge_reconfigure_one(struct bridge *);
static void bridge_reconfigure_controller(struct bridge *);
static struct iface *bridge_get_local_iface(struct bridge *);
static uint64_t dpid_from_hash(const void *, size_t nbytes);
-static void bridge_unixctl_fdb_show(struct unixctl_conn *, const char *args);
+static unixctl_cb_func bridge_unixctl_fdb_show;
static void bond_init(void);
static void bond_run(struct bridge *);
struct svec dpif_names;
size_t i;
- unixctl_command_register("fdb/show", bridge_unixctl_fdb_show);
+ unixctl_command_register("fdb/show", bridge_unixctl_fdb_show, NULL);
svec_init(&dpif_names);
dp_enumerate(&dpif_names);
}
svec_destroy(&dpif_names);
- unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows);
+ unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows,
+ NULL);
bond_init();
bridge_reconfigure();
\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)
{
struct ds ds = DS_EMPTY_INITIALIZER;
const struct bridge *br;
int error;
assert(!bridge_lookup(name));
- br = xcalloc(1, sizeof *br);
+ br = xzalloc(sizeof *br);
error = dpif_create_and_open(name, &br->dpif);
if (error) {
/* 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)
{
struct bridge *br;
struct ds results;
/* Bonding unixctl user interface functions. */
static void
-bond_unixctl_list(struct unixctl_conn *conn, const char *args UNUSED)
+bond_unixctl_list(struct unixctl_conn *conn,
+ const char *args UNUSED, void *aux UNUSED)
{
struct ds ds = DS_EMPTY_INITIALIZER;
const struct bridge *br;
}
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)
{
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)
{
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)
{
char *args = (char *) args_;
char *save_ptr = NULL;
}
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)
{
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)
{
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)
{
uint8_t mac[ETH_ADDR_LEN];
uint8_t hash;
static void
bond_init(void)
{
- unixctl_command_register("bond/list", bond_unixctl_list);
- unixctl_command_register("bond/show", bond_unixctl_show);
- unixctl_command_register("bond/migrate", bond_unixctl_migrate);
+ unixctl_command_register("bond/list", bond_unixctl_list, NULL);
+ unixctl_command_register("bond/show", bond_unixctl_show, NULL);
+ unixctl_command_register("bond/migrate", bond_unixctl_migrate, NULL);
unixctl_command_register("bond/set-active-slave",
- bond_unixctl_set_active_slave);
- unixctl_command_register("bond/enable-slave", bond_unixctl_enable_slave);
- unixctl_command_register("bond/disable-slave", bond_unixctl_disable_slave);
- unixctl_command_register("bond/hash", bond_unixctl_hash);
+ bond_unixctl_set_active_slave, NULL);
+ unixctl_command_register("bond/enable-slave", bond_unixctl_enable_slave,
+ NULL);
+ unixctl_command_register("bond/disable-slave", bond_unixctl_disable_slave,
+ NULL);
+ unixctl_command_register("bond/hash", bond_unixctl_hash, NULL);
}
\f
/* Port functions. */
{
struct port *port;
- port = xcalloc(1, sizeof *port);
+ port = xzalloc(sizeof *port);
port->bridge = br;
port->port_idx = br->n_ports;
port->vlan = -1;
{
struct iface *iface;
- iface = xcalloc(1, sizeof *iface);
+ iface = xzalloc(sizeof *iface);
iface->port = port;
iface->port_ifidx = port->n_ifaces;
iface->name = xstrdup(name);
VLOG_INFO("created port mirror %s on bridge %s", name, br->name);
bridge_flush(br);
- br->mirrors[i] = m = xcalloc(1, sizeof *m);
+ br->mirrors[i] = m = xzalloc(sizeof *m);
m->bridge = br;
m->idx = i;
m->name = xstrdup(name);
static void parse_options(int argc, char *argv[]);
static void usage(void) NO_RETURN;
-static void reload(struct unixctl_conn *, const char *args);
+static unixctl_cb_func reload;
static bool need_reconfigure;
static struct unixctl_conn **conns;
if (retval) {
ovs_fatal(retval, "could not listen for control connections");
}
- unixctl_command_register("vswitchd/reload", reload);
+ unixctl_command_register("vswitchd/reload", reload, NULL);
retval = cfg_read();
if (retval) {
}
static void
-reload(struct unixctl_conn *conn, const char *args UNUSED)
+reload(struct unixctl_conn *conn, const char *args UNUSED, void *aux UNUSED)
{
need_reconfigure = true;
conns = xrealloc(conns, sizeof *conns * (n_conns + 1));
}
if (!vlan) {
/* Create a new compat_vlan for (trunk_dev,vid). */
- vlan = xcalloc(1, sizeof *vlan);
+ vlan = xzalloc(sizeof *vlan);
vlan->trunk_dev = xstrdup(trunk_dev);
vlan->vid = vid;
vlan->vlan_dev = xasprintf("%s.%d", trunk_dev, vid);
--- /dev/null
+//
+// This is an ovsdb-idl schema. The OVSDB IDL compiler, ovsdb-idlc,
+// can translate it into an OVSDB schema (which simply entails
+// deleting some members from the schema) or C headers or source for
+// use with the IDL at runtime.
+//
+
+{"name": "ovs_vswitchd_db",
+ "comment": "Configuration for one Open vSwitch daemon.",
+ "idlPrefix": "ovsrec_",
+ "idlHeader": "\"vswitchd/vswitch-idl.h\"",
+ "tables": {
+ "Open_vSwitch": {
+ "comment": "Configuration for an Open vSwitch daemon.",
+ "columns": {
+ "bridges": {
+ "comment": "Set of bridges managed by the daemon.",
+ "type": {"key": "uuid", "keyRefTable": "Bridge",
+ "min": 0, "max": "unlimited"}},
+ "management_id": {
+ "comment": "Exactly 12 hex digits that identify the daemon.",
+ "type": "string"},
+ "controller": {
+ "comment": "Default Controller used by bridges.",
+ "type": {"key": "uuid", "keyRefTable": "Controller", "min": 0, "max": 1}},
+ "ssl": {
+ "comment": "SSL used globally by the daemon.",
+ "type": {"key": "uuid", "keyRefTable": "SSL", "min": 0, "max": 1}}}},
+ "Bridge": {
+ "comment": "Configuration for a bridge within an Open_vSwitch.",
+ "columns": {
+ "name": {
+ "comment": "Bridge identifier. Should be alphanumeric and no more than about 8 bytes long. Must be unique among the names of ports, interfaces, and bridges on a host.",
+ "type": "string"},
+ "datapath_id": {
+ "comment": "OpenFlow datapath ID. Exactly 12 hex digits.",
+ "type": {"key": "string", "min": 0, "max": 1}},
+ "hwaddr": {
+ "comment": "Ethernet address to use for bridge. Exactly 12 hex digits in the form XX:XX:XX:XX:XX:XX.",
+ "type": {"key": "string", "min": 0, "max": 1}},
+ "ports": {
+ "comment": "Ports included in the bridge.",
+ "type": {"key": "uuid", "keyRefTable": "Port", "min": 0, "max": "unlimited"}},
+ "mirrors": {
+ "comment": "Port mirroring configuration.",
+ "type": {"key": "uuid", "keyRefTable": "Mirror", "min": 0, "max": "unlimited"}},
+ "netflow": {
+ "comment": "NetFlow configuration.",
+ "type": {"key": "uuid", "keyRefTable": "NetFlow", "min": 0, "max": "unlimited"}},
+ "controller": {
+ "comment": "OpenFlow controller. If unset, defaults to that specified by the parent Open_vSwitch.",
+ "type": {"key": "uuid", "keyRefTable": "Controller", "min": 0, "max": 1}}}},
+ "Port": {
+ "comment": "A port within a Bridge. May contain a single Interface or multiple (bonded) Interfaces.",
+ "columns": {
+ "name": {
+ "comment": "Port name. Should be alphanumeric and no more than about 8 bytes long. May be the same as the interface name, for non-bonded ports. Must otherwise be unique among the names of ports, interfaces, and bridges on a host.",
+ "type": "string"},
+ "interfaces": {
+ "comment": "The Port's Interfaces. If there is more than one, this is a bonded Port.",
+ "type": {"key": "uuid", "keyRefTable": "Interface", "min": 1, "max": "unlimited"}},
+ "trunks": {
+ "comment": "The 802.1Q VLAN(s) that this port trunks. Should be empty if this port trunks all VLAN(s) or if this is not a trunk port.",
+ "type": {"key": "integer", "min": 0, "max": 4096}},
+ "tag": {
+ "comment": "This port's implicitly tagged VLAN. Should be empty if this is a trunk port.",
+ "type": {"key": "integer", "min": 0, "max": 1}},
+ "updelay": {
+ "comment": "For a bonded port, the number of milliseconds for which carrier must stay up on an interface before the interface is considered to be up. Ignored for non-bonded ports.",
+ "type": "integer"},
+ "downdelay": {
+ "comment": "For a bonded port, the number of milliseconds for which carrier must stay down on an interface before the interface is considered to be down. Ignored for non-bonded ports.",
+ "type": "integer"}}},
+ "Interface": {
+ "comment": "An interface within a Port.",
+ "columns": {
+ "name": {
+ "comment": "Interface name. Should be alphanumeric and no more than about 8 bytes long. May be the same as the port name, for non-bonded ports. Must otherwise be unique among the names of ports, interfaces, and bridges on a host.",
+ "type": "string"},
+ "internal": {
+ "comment": "An \"internal\" port is one that is implemented in software as a logical device.",
+ "type": "boolean"},
+ "ingress_policing_rate": {
+ "comment": "Maximum rate for data received on this interface, in kbps. Set to 0 to disable policing.",
+ "type": "integer"},
+ "ingress_policing_burst": {
+ "comment": "Maximum burst size for data received on this interface, in kb. The default burst size if set to 0 is 10 kb.",
+ "type": "integer"}}},
+ "Mirror": {
+ "comment": "A port mirror within a Bridge.",
+ "columns": {
+ "name": {
+ "comment": "Arbitrary identifier for the Mirror.",
+ "type": "string"},
+ "select_src_port": {
+ "comment": "Ports on which arriving packets are selected for mirroring.",
+ "type": {"key": "uuid", "keyRefTable": "Port", "min": 0, "max": "unlimited"}},
+ "select_dst_port": {
+ "comment": "Ports on which departing packets are selected for mirroring.",
+ "type": {"key": "uuid", "keyRefTable": "Port", "min": 0, "max": "unlimited"}},
+ "select_vlan": {
+ "comment": "VLANs on which packets are selected for mirroring.",
+ "type": {"key": "integer", "min": 0, "max": 4096}},
+ "output_port": {
+ "comment": "Output port for selected packets. Mutually exclusive with output_vlan.",
+ "type": {"key": "uuid", "keyRefTable": "Port", "min": 0, "max": 1}},
+ "output_vlan": {
+ "comment": "Output VLAN for selected packets. Mutually exclusive with output_port.",
+ "type": {"key": "integer", "min": 0, "max": 1}}}},
+ "NetFlow": {
+ "comment": "A NetFlow target.",
+ "columns": {
+ "target": {
+ "comment": "NetFlow target in the form \"IP:PORT\".",
+ "type": "string"},
+ "engine_type": {
+ "comment": "Engine type to use in NetFlow messages. Defaults to datapath index if not specified.",
+ "type": "integer", "min":0, "max":1},
+ "engine_id": {
+ "comment": "Engine ID to use in NetFlow messages. Defaults to datapath index if not specified.",
+ "type": "integer", "min":0, "max":1},
+ "add_id_to_interface": {
+ "comment": "Place least-significant 7 bits of engine ID into most significant bits of ingress and egress interface fields of NetFlow records?",
+ "type": "boolean"}}},
+ "Controller": {
+ "comment": "An OpenFlow controller.",
+ "columns": {
+ "target": {
+ "comment": "Connection method for controller, e.g. \"ssl:...\", \"tcp:...\". The special string \"discover\" enables controller discovery.",
+ "type": "string"},
+ "max_backoff": {
+ "comment": "Maximum number of milliseconds to wait between connection attempts. Default is implementation-specific.",
+ "type": {"key": "integer", "min": 0, "max": 1}},
+ "inactivity_probe": {
+ "comment": "Maximum number of milliseconds of idle time on connection to controller before sending an inactivity probe message. Default is implementation-specific.",
+ "type": {"key": "integer", "min": 0, "max": 1}},
+ "fail_mode": {
+ "comment": "Either \"standalone\" or \"secure\", or empty to use the implementation's default.",
+ "type": {"key": "string", "min": 0, "max": 1}},
+ "discover_accept_regex": {
+ "comment": "If \"target\" is \"discover\", a POSIX extended regular expression against which the discovered controller location is validated. If not specified, the default is implementation-specific.",
+ "type": {"key": "string", "min": 0, "max": 1}},
+ "discover_update_resolv_conf": {
+ "comment": "If \"target\" is \"discover\", whether to update /etc/resolv.conf when the controller is discovered. If not specified, the default is implementation-specific.",
+ "type": {"key": "boolean", "min": 0, "max": 1}},
+ "connection_mode": {
+ "comment": "Either \"in-band\" or \"out-of-band\". If not specified, the default is implementation-specific.",
+ "type": {"key": "string", "min": 0, "max": 1}},
+ "local_ip": {
+ "comment": "If \"target\" is not \"discover\", the IP address to configure on the local port.",
+ "type": {"key": "string", "min": 0, "max": 1}},
+ "local_netmask": {
+ "comment": "If \"target\" is not \"discover\", the IP netmask to configure on the local port.",
+ "type": {"key": "string", "min": 0, "max": 1}},
+ "local_gateway": {
+ "comment": "If \"target\" is not \"discover\", the IP gateway to configure on the local port.",
+ "type": {"key": "string", "min": 0, "max": 1}},
+ "controller_rate_limit": {
+ "comment": "The maximum rate at which packets will be forwarded to the OpenFlow controller, in packets per second. If not specified, the default is implementation-specific.",
+ "type": {"key": "integer", "min": 0, "max": 1}},
+ "controller_burst_limit": {
+ "comment": "The maximum number of unused packet credits that the bridge will allow to accumulate, in packets. If not specified, the default is implementation-specific.",
+ "type": {"key": "integer", "min": 0, "max": 1}}}},
+ "SSL": {
+ "comment": "SSL configuration for an Open_vSwitch.",
+ "columns": {
+ "private_key": {
+ "comment": "Name of a PEM file containing the private key used as the switch's identity for SSL connections to the controller.",
+ "type": "string"},
+ "certificate": {
+ "comment": "Name of a PEM file containing a certificate, signed by the certificate authority (CA) used by the controller and manager, that certifies the switch's private key, identifying a trustworthy switch.",
+ "type": "string"},
+ "ca_cert": {
+ "comment": "Name of a PEM file containing the CA certificate used to verify that the switch is connected to a trustworthy controller.",
+ "type": "string"}}}}}