+ * The legacy style, an application wishing to receive events from ZooKeeper must
+ * first implement a function with this signature and pass a pointer to the function
+ * to \ref zookeeper_init. Next, the application sets a watch by calling one of
+ * the getter API that accept the watch integer flag (for example, \ref zoo_aexists,
+ * \ref zoo_get, etc).
+ *
+ * The watcher object style uses an instance of a "watcher object" which in
+ * the C world is represented by a pair: a pointer to a function implementing this
+ * signature and a pointer to watcher context -- handback user-specific data.
+ * When a watch is triggered this function will be called along with
+ * the watcher context. An application wishing to use this style must use
+ * the getter API functions with the "w" prefix in their names (for example, \ref
+ * zoo_awexists, \ref zoo_wget, etc).
+ *
+ * \param zh zookeeper handle
+ * \param type event type. This is one of the *_EVENT constants.
+ * \param state connection state. The state value will be one of the *_STATE constants.
+ * \param path znode path for which the watcher is triggered. NULL if the event
+ * type is ZOO_SESSION_EVENT
+ * \param watcherCtx watcher context.
+ */
+typedef void (*watcher_fn)(zhandle_t *zh, int type,
+ int state, const char *path,void *watcherCtx);
+
+/**
+ * \brief create a handle to used communicate with zookeeper.
+ *
+ * This method creates a new handle and a zookeeper session that corresponds
+ * to that handle. Session establishment is asynchronous, meaning that the
+ * session should not be considered established until (and unless) an
+ * event of state ZOO_CONNECTED_STATE is received.
+ * \param host comma separated host:port pairs, each corresponding to a zk
+ * server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"
+ * \param fn the global watcher callback function. When notifications are
+ * triggered this function will be invoked.
+ * \param clientid the id of a previously established session that this
+ * client will be reconnecting to. Pass 0 if not reconnecting to a previous
+ * session. Clients can access the session id of an established, valid,
+ * connection by calling \ref zoo_client_id. If the session corresponding to
+ * the specified clientid has expired, or if the clientid is invalid for
+ * any reason, the returned zhandle_t will be invalid -- the zhandle_t
+ * state will indicate the reason for failure (typically
+ * ZOO_EXPIRED_SESSION_STATE).
+ * \param context the handback object that will be associated with this instance
+ * of zhandle_t. Application can access it (for example, in the watcher
+ * callback) using \ref zoo_get_context. The object is not used by zookeeper
+ * internally and can be null.
+ * \param flags reserved for future use. Should be set to zero.
+ * \return a pointer to the opaque zhandle structure. If it fails to create
+ * a new zhandle the function returns NULL and the errno variable
+ * indicates the reason.
+ */
+ZOOAPI zhandle_t *zookeeper_init(const char *host, watcher_fn fn,
+ int recv_timeout, const clientid_t *clientid, void *context, int flags);
+
+/**
+ * \brief close the zookeeper handle and free up any resources.
+ *
+ * After this call, the client session will no longer be valid. The function
+ * will flush any outstanding send requests before return. As a result it may
+ * block.
+ *
+ * This method should only be called only once on a zookeeper handle. Calling
+ * twice will cause undefined (and probably undesirable behavior). Calling any other
+ * zookeeper method after calling close is undefined behaviour and should be avoided.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \return a result code. Regardless of the error code returned, the zhandle
+ * will be destroyed and all resources freed.
+ *
+ * ZOK - success
+ * ZBADARGUMENTS - invalid input parameters
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ * ZOPERATIONTIMEOUT - failed to flush the buffers within the specified timeout.
+ * ZCONNECTIONLOSS - a network error occurred while attempting to send request to server
+ * ZSYSTEMERROR -- a system (OS) error occurred; it's worth checking errno to get details
+ */
+ZOOAPI int zookeeper_close(zhandle_t *zh);
+
+/**
+ * \brief return the client session id, only valid if the connections
+ * is currently connected (ie. last watcher state is ZOO_CONNECTED_STATE)
+ */
+ZOOAPI const clientid_t *zoo_client_id(zhandle_t *zh);
+
+/**
+ * \brief return the timeout for this session, only valid if the connections
+ * is currently connected (ie. last watcher state is ZOO_CONNECTED_STATE). This
+ * value may change after a server re-connect.
+ */
+ZOOAPI int zoo_recv_timeout(zhandle_t *zh);
+
+/**
+ * \brief return the context for this handle.
+ */
+ZOOAPI const void *zoo_get_context(zhandle_t *zh);
+
+/**
+ * \brief set the context for this handle.
+ */
+ZOOAPI void zoo_set_context(zhandle_t *zh, void *context);
+
+/**
+ * \brief set a watcher function
+ * \return previous watcher function
+ */
+ZOOAPI watcher_fn zoo_set_watcher(zhandle_t *zh,watcher_fn newFn);
+
+/**
+ * \brief returns the socket address for the current connection
+ * \return socket address of the connected host or NULL on failure, only valid if the
+ * connection is current connected
+ */
+ZOOAPI struct sockaddr* zookeeper_get_connected_host(zhandle_t *zh,
+ struct sockaddr *addr, socklen_t *addr_len);
+
+#ifndef THREADED
+/**
+ * \brief Returns the events that zookeeper is interested in.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param fd is the file descriptor of interest
+ * \param interest is an or of the ZOOKEEPER_WRITE and ZOOKEEPER_READ flags to
+ * indicate the I/O of interest on fd.
+ * \param tv a timeout value to be used with select/poll system call
+ * \return a result code.
+ * ZOK - success
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZCONNECTIONLOSS - a network error occurred while attempting to establish
+ * a connection to the server
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ * ZOPERATIONTIMEOUT - hasn't received anything from the server for 2/3 of the
+ * timeout value specified in zookeeper_init()
+ * ZSYSTEMERROR -- a system (OS) error occurred; it's worth checking errno to get details
+ */
+#ifdef WIN32
+ZOOAPI int zookeeper_interest(zhandle_t *zh, SOCKET *fd, int *interest,
+ struct timeval *tv);
+#else
+ZOOAPI int zookeeper_interest(zhandle_t *zh, int *fd, int *interest,
+ struct timeval *tv);
+#endif
+
+/**
+ * \brief Notifies zookeeper that an event of interest has happened.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param events will be an OR of the ZOOKEEPER_WRITE and ZOOKEEPER_READ flags.
+ * \return a result code.
+ * ZOK - success
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZCONNECTIONLOSS - a network error occurred while attempting to send request to server
+ * ZSESSIONEXPIRED - connection attempt failed -- the session's expired
+ * ZAUTHFAILED - authentication request failed, e.i. invalid credentials
+ * ZRUNTIMEINCONSISTENCY - a server response came out of order
+ * ZSYSTEMERROR -- a system (OS) error occurred; it's worth checking errno to get details
+ * ZNOTHING -- not an error; simply indicates that there no more data from the server
+ * to be processed (when called with ZOOKEEPER_READ flag).
+ */
+ZOOAPI int zookeeper_process(zhandle_t *zh, int events);
+#endif
+
+/**
+ * \brief signature of a completion function for a call that returns void.
+ *
+ * This method will be invoked at the end of a asynchronous call and also as
+ * a result of connection loss or timeout.
+ * \param rc the error code of the call. Connection loss/timeout triggers
+ * the completion with one of the following error codes:
+ * ZCONNECTIONLOSS -- lost connection to the server
+ * ZOPERATIONTIMEOUT -- connection timed out
+ * Data related events trigger the completion with error codes listed the
+ * Exceptions section of the documentation of the function that initiated the
+ * call. (Zero indicates call was successful.)
+ * \param data the pointer that was passed by the caller when the function
+ * that this completion corresponds to was invoked. The programmer
+ * is responsible for any memory freeing associated with the data
+ * pointer.
+ */
+typedef void (*void_completion_t)(int rc, const void *data);
+
+/**
+ * \brief signature of a completion function that returns a Stat structure.
+ *
+ * This method will be invoked at the end of a asynchronous call and also as
+ * a result of connection loss or timeout.
+ * \param rc the error code of the call. Connection loss/timeout triggers
+ * the completion with one of the following error codes:
+ * ZCONNECTIONLOSS -- lost connection to the server
+ * ZOPERATIONTIMEOUT -- connection timed out
+ * Data related events trigger the completion with error codes listed the
+ * Exceptions section of the documentation of the function that initiated the
+ * call. (Zero indicates call was successful.)
+ * \param stat a pointer to the stat information for the node involved in
+ * this function. If a non zero error code is returned, the content of
+ * stat is undefined. The programmer is NOT responsible for freeing stat.
+ * \param data the pointer that was passed by the caller when the function
+ * that this completion corresponds to was invoked. The programmer
+ * is responsible for any memory freeing associated with the data
+ * pointer.
+ */
+typedef void (*stat_completion_t)(int rc, const struct Stat *stat,
+ const void *data);
+
+/**
+ * \brief signature of a completion function that returns data.
+ *
+ * This method will be invoked at the end of a asynchronous call and also as
+ * a result of connection loss or timeout.
+ * \param rc the error code of the call. Connection loss/timeout triggers
+ * the completion with one of the following error codes:
+ * ZCONNECTIONLOSS -- lost connection to the server
+ * ZOPERATIONTIMEOUT -- connection timed out
+ * Data related events trigger the completion with error codes listed the
+ * Exceptions section of the documentation of the function that initiated the
+ * call. (Zero indicates call was successful.)
+ * \param value the value of the information returned by the asynchronous call.
+ * If a non zero error code is returned, the content of value is undefined.
+ * The programmer is NOT responsible for freeing value.
+ * \param value_len the number of bytes in value.
+ * \param stat a pointer to the stat information for the node involved in
+ * this function. If a non zero error code is returned, the content of
+ * stat is undefined. The programmer is NOT responsible for freeing stat.
+ * \param data the pointer that was passed by the caller when the function
+ * that this completion corresponds to was invoked. The programmer
+ * is responsible for any memory freeing associated with the data
+ * pointer.
+ */
+typedef void (*data_completion_t)(int rc, const char *value, int value_len,
+ const struct Stat *stat, const void *data);
+
+/**
+ * \brief signature of a completion function that returns a list of strings.
+ *
+ * This method will be invoked at the end of a asynchronous call and also as
+ * a result of connection loss or timeout.
+ * \param rc the error code of the call. Connection loss/timeout triggers
+ * the completion with one of the following error codes:
+ * ZCONNECTIONLOSS -- lost connection to the server
+ * ZOPERATIONTIMEOUT -- connection timed out
+ * Data related events trigger the completion with error codes listed the
+ * Exceptions section of the documentation of the function that initiated the
+ * call. (Zero indicates call was successful.)
+ * \param strings a pointer to the structure containng the list of strings of the
+ * names of the children of a node. If a non zero error code is returned,
+ * the content of strings is undefined. The programmer is NOT responsible
+ * for freeing strings.
+ * \param data the pointer that was passed by the caller when the function
+ * that this completion corresponds to was invoked. The programmer
+ * is responsible for any memory freeing associated with the data
+ * pointer.
+ */
+typedef void (*strings_completion_t)(int rc,
+ const struct String_vector *strings, const void *data);
+
+/**
+ * \brief signature of a completion function that returns a list of strings and stat.
+ * .
+ *
+ * This method will be invoked at the end of a asynchronous call and also as
+ * a result of connection loss or timeout.
+ * \param rc the error code of the call. Connection loss/timeout triggers
+ * the completion with one of the following error codes:
+ * ZCONNECTIONLOSS -- lost connection to the server
+ * ZOPERATIONTIMEOUT -- connection timed out
+ * Data related events trigger the completion with error codes listed the
+ * Exceptions section of the documentation of the function that initiated the
+ * call. (Zero indicates call was successful.)
+ * \param strings a pointer to the structure containng the list of strings of the
+ * names of the children of a node. If a non zero error code is returned,
+ * the content of strings is undefined. The programmer is NOT responsible
+ * for freeing strings.
+ * \param stat a pointer to the stat information for the node involved in
+ * this function. If a non zero error code is returned, the content of
+ * stat is undefined. The programmer is NOT responsible for freeing stat.
+ * \param data the pointer that was passed by the caller when the function
+ * that this completion corresponds to was invoked. The programmer
+ * is responsible for any memory freeing associated with the data
+ * pointer.
+ */
+typedef void (*strings_stat_completion_t)(int rc,
+ const struct String_vector *strings, const struct Stat *stat,
+ const void *data);
+
+/**
+ * \brief signature of a completion function that returns a list of strings.
+ *
+ * This method will be invoked at the end of a asynchronous call and also as
+ * a result of connection loss or timeout.
+ * \param rc the error code of the call. Connection loss/timeout triggers
+ * the completion with one of the following error codes:
+ * ZCONNECTIONLOSS -- lost connection to the server
+ * ZOPERATIONTIMEOUT -- connection timed out
+ * Data related events trigger the completion with error codes listed the
+ * Exceptions section of the documentation of the function that initiated the
+ * call. (Zero indicates call was successful.)
+ * \param value the value of the string returned.
+ * \param data the pointer that was passed by the caller when the function
+ * that this completion corresponds to was invoked. The programmer
+ * is responsible for any memory freeing associated with the data
+ * pointer.
+ */
+typedef void
+ (*string_completion_t)(int rc, const char *value, const void *data);
+
+/**
+ * \brief signature of a completion function that returns an ACL.
+ *
+ * This method will be invoked at the end of a asynchronous call and also as
+ * a result of connection loss or timeout.
+ * \param rc the error code of the call. Connection loss/timeout triggers
+ * the completion with one of the following error codes:
+ * ZCONNECTIONLOSS -- lost connection to the server
+ * ZOPERATIONTIMEOUT -- connection timed out
+ * Data related events trigger the completion with error codes listed the
+ * Exceptions section of the documentation of the function that initiated the
+ * call. (Zero indicates call was successful.)
+ * \param acl a pointer to the structure containng the ACL of a node. If a non
+ * zero error code is returned, the content of strings is undefined. The
+ * programmer is NOT responsible for freeing acl.
+ * \param stat a pointer to the stat information for the node involved in
+ * this function. If a non zero error code is returned, the content of
+ * stat is undefined. The programmer is NOT responsible for freeing stat.
+ * \param data the pointer that was passed by the caller when the function
+ * that this completion corresponds to was invoked. The programmer
+ * is responsible for any memory freeing associated with the data
+ * pointer.
+ */
+typedef void (*acl_completion_t)(int rc, struct ACL_vector *acl,
+ struct Stat *stat, const void *data);
+
+/**
+ * \brief get the state of the zookeeper connection.
+ *
+ * The return value will be one of the \ref State Consts.
+ */
+ZOOAPI int zoo_state(zhandle_t *zh);
+
+/**
+ * \brief create a node.
+ *
+ * This method will create a node in ZooKeeper. A node can only be created if
+ * it does not already exists. The Create Flags affect the creation of nodes.
+ * If ZOO_EPHEMERAL flag is set, the node will automatically get removed if the
+ * client session goes away. If the ZOO_SEQUENCE flag is set, a unique
+ * monotonically increasing sequence number is appended to the path name. The
+ * sequence number is always fixed length of 10 digits, 0 padded.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path The name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param value The data to be stored in the node.
+ * \param valuelen The number of bytes in data.
+ * \param acl The initial ACL of the node. The ACL must not be null or empty.
+ * \param flags this parameter can be set to 0 for normal create or an OR
+ * of the Create Flags
+ * \param completion the routine to invoke when the request completes. The completion
+ * will be triggered with one of the following codes passed in as the rc argument:
+ * ZOK operation completed successfully
+ * ZNONODE the parent node does not exist.
+ * ZNODEEXISTS the node already exists
+ * ZNOAUTH the client does not have permission.
+ * ZNOCHILDRENFOREPHEMERALS cannot create children of ephemeral nodes.
+ * \param data The data that will be passed to the completion routine when the
+ * function completes.
+ * \return ZOK on success or one of the following errcodes on failure:
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_acreate(zhandle_t *zh, const char *path, const char *value,
+ int valuelen, const struct ACL_vector *acl, int flags,
+ string_completion_t completion, const void *data);
+
+/**
+ * \brief delete a node in zookeeper.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param version the expected version of the node. The function will fail if the
+ * actual version of the node does not match the expected version.
+ * If -1 is used the version check will not take place.
+ * \param completion the routine to invoke when the request completes. The completion
+ * will be triggered with one of the following codes passed in as the rc argument:
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * ZBADVERSION expected version does not match actual version.
+ * ZNOTEMPTY children are present; node cannot be deleted.
+ * \param data the data that will be passed to the completion routine when
+ * the function completes.
+ * \return ZOK on success or one of the following errcodes on failure:
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_adelete(zhandle_t *zh, const char *path, int version,
+ void_completion_t completion, const void *data);
+
+/**
+ * \brief checks the existence of a node in zookeeper.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param watch if nonzero, a watch will be set at the server to notify the
+ * client if the node changes. The watch will be set even if the node does not
+ * exist. This allows clients to watch for nodes to appear.
+ * \param completion the routine to invoke when the request completes. The completion
+ * will be triggered with one of the following codes passed in as the rc argument:
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * \param data the data that will be passed to the completion routine when the
+ * function completes.
+ * \return ZOK on success or one of the following errcodes on failure:
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_aexists(zhandle_t *zh, const char *path, int watch,
+ stat_completion_t completion, const void *data);
+
+/**
+ * \brief checks the existence of a node in zookeeper.
+ *
+ * This function is similar to \ref zoo_axists except it allows one specify
+ * a watcher object - a function pointer and associated context. The function
+ * will be called once the watch has fired. The associated context data will be
+ * passed to the function as the watcher context parameter.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param watcher if non-null a watch will set on the specified znode on the server.
+ * The watch will be set even if the node does not exist. This allows clients
+ * to watch for nodes to appear.
+ * \param watcherCtx user specific data, will be passed to the watcher callback.
+ * Unlike the global context set by \ref zookeeper_init, this watcher context
+ * is associated with the given instance of the watcher only.
+ * \param completion the routine to invoke when the request completes. The completion
+ * will be triggered with one of the following codes passed in as the rc argument:
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * \param data the data that will be passed to the completion routine when the
+ * function completes.
+ * \return ZOK on success or one of the following errcodes on failure:
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_awexists(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ stat_completion_t completion, const void *data);
+
+/**
+ * \brief gets the data associated with a node.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param watch if nonzero, a watch will be set at the server to notify
+ * the client if the node changes.
+ * \param completion the routine to invoke when the request completes. The completion
+ * will be triggered with one of the following codes passed in as the rc argument:
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * \param data the data that will be passed to the completion routine when
+ * the function completes.
+ * \return ZOK on success or one of the following errcodes on failure:
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either in ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_aget(zhandle_t *zh, const char *path, int watch,
+ data_completion_t completion, const void *data);
+
+/**
+ * \brief gets the data associated with a node.
+ *
+ * This function is similar to \ref zoo_aget except it allows one specify
+ * a watcher object rather than a boolean watch flag.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param watcher if non-null, a watch will be set at the server to notify
+ * the client if the node changes.
+ * \param watcherCtx user specific data, will be passed to the watcher callback.
+ * Unlike the global context set by \ref zookeeper_init, this watcher context
+ * is associated with the given instance of the watcher only.
+ * \param completion the routine to invoke when the request completes. The completion
+ * will be triggered with one of the following codes passed in as the rc argument:
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * \param data the data that will be passed to the completion routine when
+ * the function completes.
+ * \return ZOK on success or one of the following errcodes on failure:
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either in ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_awget(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ data_completion_t completion, const void *data);
+
+/**
+ * \brief sets the data associated with a node.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param buffer the buffer holding data to be written to the node.
+ * \param buflen the number of bytes from buffer to write.
+ * \param version the expected version of the node. The function will fail if
+ * the actual version of the node does not match the expected version. If -1 is
+ * used the version check will not take place. * completion: If null,
+ * the function will execute synchronously. Otherwise, the function will return
+ * immediately and invoke the completion routine when the request completes.
+ * \param completion the routine to invoke when the request completes. The completion
+ * will be triggered with one of the following codes passed in as the rc argument:
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * ZBADVERSION expected version does not match actual version.
+ * \param data the data that will be passed to the completion routine when
+ * the function completes.
+ * \return ZOK on success or one of the following errcodes on failure:
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_aset(zhandle_t *zh, const char *path, const char *buffer, int buflen,
+ int version, stat_completion_t completion, const void *data);
+
+/**
+ * \brief lists the children of a node.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param watch if nonzero, a watch will be set at the server to notify
+ * the client if the node changes.
+ * \param completion the routine to invoke when the request completes. The completion
+ * will be triggered with one of the following codes passed in as the rc argument:
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * \param data the data that will be passed to the completion routine when
+ * the function completes.
+ * \return ZOK on success or one of the following errcodes on failure:
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_aget_children(zhandle_t *zh, const char *path, int watch,
+ strings_completion_t completion, const void *data);
+
+/**
+ * \brief lists the children of a node.
+ *
+ * This function is similar to \ref zoo_aget_children except it allows one specify
+ * a watcher object rather than a boolean watch flag.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param watcher if non-null, a watch will be set at the server to notify
+ * the client if the node changes.
+ * \param watcherCtx user specific data, will be passed to the watcher callback.
+ * Unlike the global context set by \ref zookeeper_init, this watcher context
+ * is associated with the given instance of the watcher only.
+ * \param completion the routine to invoke when the request completes. The completion
+ * will be triggered with one of the following codes passed in as the rc argument:
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * \param data the data that will be passed to the completion routine when
+ * the function completes.
+ * \return ZOK on success or one of the following errcodes on failure:
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_awget_children(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ strings_completion_t completion, const void *data);
+
+/**
+ * \brief lists the children of a node, and get the parent stat.
+ *
+ * This function is new in version 3.3.0
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param watch if nonzero, a watch will be set at the server to notify
+ * the client if the node changes.
+ * \param completion the routine to invoke when the request completes. The completion
+ * will be triggered with one of the following codes passed in as the rc argument:
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * \param data the data that will be passed to the completion routine when
+ * the function completes.
+ * \return ZOK on success or one of the following errcodes on failure:
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_aget_children2(zhandle_t *zh, const char *path, int watch,
+ strings_stat_completion_t completion, const void *data);
+
+/**
+ * \brief lists the children of a node, and get the parent stat.
+ *
+ * This function is similar to \ref zoo_aget_children2 except it allows one specify
+ * a watcher object rather than a boolean watch flag.
+ *
+ * This function is new in version 3.3.0
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param watcher if non-null, a watch will be set at the server to notify
+ * the client if the node changes.
+ * \param watcherCtx user specific data, will be passed to the watcher callback.
+ * Unlike the global context set by \ref zookeeper_init, this watcher context
+ * is associated with the given instance of the watcher only.
+ * \param completion the routine to invoke when the request completes. The completion
+ * will be triggered with one of the following codes passed in as the rc argument:
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * \param data the data that will be passed to the completion routine when
+ * the function completes.
+ * \return ZOK on success or one of the following errcodes on failure:
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_awget_children2(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ strings_stat_completion_t completion, const void *data);
+
+/**
+ * \brief Flush leader channel.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param completion the routine to invoke when the request completes. The completion
+ * will be triggered with one of the following codes passed in as the rc argument:
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * \param data the data that will be passed to the completion routine when
+ * the function completes.
+ * \return ZOK on success or one of the following errcodes on failure:
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+
+ZOOAPI int zoo_async(zhandle_t *zh, const char *path,
+ string_completion_t completion, const void *data);
+
+
+/**
+ * \brief gets the acl associated with a node.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param completion the routine to invoke when the request completes. The completion
+ * will be triggered with one of the following codes passed in as the rc argument:
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * \param data the data that will be passed to the completion routine when
+ * the function completes.
+ * \return ZOK on success or one of the following errcodes on failure:
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_aget_acl(zhandle_t *zh, const char *path, acl_completion_t completion,
+ const void *data);
+
+/**
+ * \brief sets the acl associated with a node.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param buffer the buffer holding the acls to be written to the node.
+ * \param buflen the number of bytes from buffer to write.
+ * \param completion the routine to invoke when the request completes. The completion
+ * will be triggered with one of the following codes passed in as the rc argument:
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * ZINVALIDACL invalid ACL specified
+ * ZBADVERSION expected version does not match actual version.
+ * \param data the data that will be passed to the completion routine when
+ * the function completes.
+ * \return ZOK on success or one of the following errcodes on failure:
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_aset_acl(zhandle_t *zh, const char *path, int version,
+ struct ACL_vector *acl, void_completion_t, const void *data);
+
+/**
+ * \brief atomically commits multiple zookeeper operations.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param count the number of operations
+ * \param ops an array of operations to commit
+ * \param results an array to hold the results of the operations
+ * \param completion the routine to invoke when the request completes. The completion
+ * will be triggered with any of the error codes that can that can be returned by the
+ * ops supported by a multi op (see \ref zoo_acreate, \ref zoo_adelete, \ref zoo_aset).
+ * \param data the data that will be passed to the completion routine when
+ * the function completes.
+ * \return the return code for the function call. This can be any of the
+ * values that can be returned by the ops supported by a multi op (see
+ * \ref zoo_acreate, \ref zoo_adelete, \ref zoo_aset).
+ */
+ZOOAPI int zoo_amulti(zhandle_t *zh, int count, const zoo_op_t *ops,
+ zoo_op_result_t *results, void_completion_t, const void *data);
+
+/**
+ * \brief return an error string.
+ *
+ * \param return code
+ * \return string corresponding to the return code
+ */
+ZOOAPI const char* zerror(int c);
+
+/**
+ * \brief specify application credentials.
+ *
+ * The application calls this function to specify its credentials for purposes
+ * of authentication. The server will use the security provider specified by
+ * the scheme parameter to authenticate the client connection. If the
+ * authentication request has failed:
+ * - the server connection is dropped
+ * - the watcher is called with the ZOO_AUTH_FAILED_STATE value as the state
+ * parameter.
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param scheme the id of authentication scheme. Natively supported:
+ * "digest" password-based authentication
+ * \param cert application credentials. The actual value depends on the scheme.
+ * \param certLen the length of the data parameter
+ * \param completion the routine to invoke when the request completes. One of
+ * the following result codes may be passed into the completion callback:
+ * ZOK operation completed successfully
+ * ZAUTHFAILED authentication failed
+ * \param data the data that will be passed to the completion routine when the
+ * function completes.
+ * \return ZOK on success or one of the following errcodes on failure:
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ * ZSYSTEMERROR - a system error occurred
+ */
+ZOOAPI int zoo_add_auth(zhandle_t *zh,const char* scheme,const char* cert,
+ int certLen, void_completion_t completion, const void *data);
+
+/**
+ * \brief checks if the current zookeeper connection state can't be recovered.
+ *
+ * The application must close the zhandle and try to reconnect.
+ *
+ * \param zh the zookeeper handle (see \ref zookeeper_init)
+ * \return ZINVALIDSTATE if connection is unrecoverable
+ */
+ZOOAPI int is_unrecoverable(zhandle_t *zh);
+
+/**
+ * \brief sets the debugging level for the library
+ */
+ZOOAPI void zoo_set_debug_level(ZooLogLevel logLevel);
+
+/**
+ * \brief sets the stream to be used by the library for logging
+ *
+ * The zookeeper library uses stderr as its default log stream. Application
+ * must make sure the stream is writable. Passing in NULL resets the stream
+ * to its default value (stderr).
+ */
+ZOOAPI void zoo_set_log_stream(FILE* logStream);
+
+/**
+ * \brief enable/disable quorum endpoint order randomization
+ *
+ * Note: typically this method should NOT be used outside of testing.
+ *
+ * If passed a non-zero value, will make the client connect to quorum peers
+ * in the order as specified in the zookeeper_init() call.
+ * A zero value causes zookeeper_init() to permute the peer endpoints
+ * which is good for more even client connection distribution among the
+ * quorum peers.
+ */
+ZOOAPI void zoo_deterministic_conn_order(int yesOrNo);
+
+/**
+ * \brief create a node synchronously.
+ *
+ * This method will create a node in ZooKeeper. A node can only be created if
+ * it does not already exists. The Create Flags affect the creation of nodes.
+ * If ZOO_EPHEMERAL flag is set, the node will automatically get removed if the
+ * client session goes away. If the ZOO_SEQUENCE flag is set, a unique
+ * monotonically increasing sequence number is appended to the path name.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path The name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param value The data to be stored in the node.
+ * \param valuelen The number of bytes in data. To set the data to be NULL use
+ * value as NULL and valuelen as -1.
+ * \param acl The initial ACL of the node. The ACL must not be null or empty.
+ * \param flags this parameter can be set to 0 for normal create or an OR
+ * of the Create Flags
+ * \param path_buffer Buffer which will be filled with the path of the
+ * new node (this might be different than the supplied path
+ * because of the ZOO_SEQUENCE flag). The path string will always be
+ * null-terminated. This parameter may be NULL if path_buffer_len = 0.
+ * \param path_buffer_len Size of path buffer; if the path of the new
+ * node (including space for the null terminator) exceeds the buffer size,
+ * the path string will be truncated to fit. The actual path of the
+ * new node in the server will not be affected by the truncation.
+ * The path string will always be null-terminated.
+ * \return one of the following codes are returned:
+ * ZOK operation completed successfully
+ * ZNONODE the parent node does not exist.
+ * ZNODEEXISTS the node already exists
+ * ZNOAUTH the client does not have permission.
+ * ZNOCHILDRENFOREPHEMERALS cannot create children of ephemeral nodes.
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_create(zhandle_t *zh, const char *path, const char *value,
+ int valuelen, const struct ACL_vector *acl, int flags,
+ char *path_buffer, int path_buffer_len);
+
+/**
+ * \brief delete a node in zookeeper synchronously.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param version the expected version of the node. The function will fail if the
+ * actual version of the node does not match the expected version.
+ * If -1 is used the version check will not take place.
+ * \return one of the following values is returned.
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * ZBADVERSION expected version does not match actual version.
+ * ZNOTEMPTY children are present; node cannot be deleted.
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_delete(zhandle_t *zh, const char *path, int version);
+
+
+/**
+ * \brief checks the existence of a node in zookeeper synchronously.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param watch if nonzero, a watch will be set at the server to notify the
+ * client if the node changes. The watch will be set even if the node does not
+ * exist. This allows clients to watch for nodes to appear.
+ * \param the return stat value of the node.
+ * \return return code of the function call.
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_exists(zhandle_t *zh, const char *path, int watch, struct Stat *stat);
+
+/**
+ * \brief checks the existence of a node in zookeeper synchronously.
+ *
+ * This function is similar to \ref zoo_exists except it allows one specify
+ * a watcher object rather than a boolean watch flag.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param watcher if non-null a watch will set on the specified znode on the server.
+ * The watch will be set even if the node does not exist. This allows clients
+ * to watch for nodes to appear.
+ * \param watcherCtx user specific data, will be passed to the watcher callback.
+ * Unlike the global context set by \ref zookeeper_init, this watcher context
+ * is associated with the given instance of the watcher only.
+ * \param the return stat value of the node.
+ * \return return code of the function call.
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_wexists(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx, struct Stat *stat);
+
+/**
+ * \brief gets the data associated with a node synchronously.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param watch if nonzero, a watch will be set at the server to notify
+ * the client if the node changes.
+ * \param buffer the buffer holding the node data returned by the server
+ * \param buffer_len is the size of the buffer pointed to by the buffer parameter.
+ * It'll be set to the actual data length upon return. If the data is NULL, length is -1.
+ * \param stat if not NULL, will hold the value of stat for the path on return.
+ * \return return value of the function call.
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either in ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_get(zhandle_t *zh, const char *path, int watch, char *buffer,
+ int* buffer_len, struct Stat *stat);
+
+/**
+ * \brief gets the data associated with a node synchronously.
+ *
+ * This function is similar to \ref zoo_get except it allows one specify
+ * a watcher object rather than a boolean watch flag.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param watcher if non-null, a watch will be set at the server to notify
+ * the client if the node changes.
+ * \param watcherCtx user specific data, will be passed to the watcher callback.
+ * Unlike the global context set by \ref zookeeper_init, this watcher context
+ * is associated with the given instance of the watcher only.
+ * \param buffer the buffer holding the node data returned by the server
+ * \param buffer_len is the size of the buffer pointed to by the buffer parameter.
+ * It'll be set to the actual data length upon return. If the data is NULL, length is -1.
+ * \param stat if not NULL, will hold the value of stat for the path on return.
+ * \return return value of the function call.
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either in ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_wget(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ char *buffer, int* buffer_len, struct Stat *stat);
+
+/**
+ * \brief sets the data associated with a node. See zoo_set2 function if
+ * you require access to the stat information associated with the znode.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param buffer the buffer holding data to be written to the node.
+ * \param buflen the number of bytes from buffer to write. To set NULL as data
+ * use buffer as NULL and buflen as -1.
+ * \param version the expected version of the node. The function will fail if
+ * the actual version of the node does not match the expected version. If -1 is
+ * used the version check will not take place.
+ * \return the return code for the function call.
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * ZBADVERSION expected version does not match actual version.
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_set(zhandle_t *zh, const char *path, const char *buffer,
+ int buflen, int version);
+
+/**
+ * \brief sets the data associated with a node. This function is the same
+ * as zoo_set except that it also provides access to stat information
+ * associated with the znode.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param buffer the buffer holding data to be written to the node.
+ * \param buflen the number of bytes from buffer to write. To set NULL as data
+ * use buffer as NULL and buflen as -1.
+ * \param version the expected version of the node. The function will fail if
+ * the actual version of the node does not match the expected version. If -1 is
+ * used the version check will not take place.
+ * \param stat if not NULL, will hold the value of stat for the path on return.
+ * \return the return code for the function call.
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * ZBADVERSION expected version does not match actual version.
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_set2(zhandle_t *zh, const char *path, const char *buffer,
+ int buflen, int version, struct Stat *stat);
+
+/**
+ * \brief lists the children of a node synchronously.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param watch if nonzero, a watch will be set at the server to notify
+ * the client if the node changes.
+ * \param strings return value of children paths.
+ * \return the return code of the function.
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_get_children(zhandle_t *zh, const char *path, int watch,
+ struct String_vector *strings);
+
+/**
+ * \brief lists the children of a node synchronously.
+ *
+ * This function is similar to \ref zoo_get_children except it allows one specify
+ * a watcher object rather than a boolean watch flag.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param watcher if non-null, a watch will be set at the server to notify
+ * the client if the node changes.
+ * \param watcherCtx user specific data, will be passed to the watcher callback.
+ * Unlike the global context set by \ref zookeeper_init, this watcher context
+ * is associated with the given instance of the watcher only.
+ * \param strings return value of children paths.
+ * \return the return code of the function.
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_wget_children(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ struct String_vector *strings);
+
+/**
+ * \brief lists the children of a node and get its stat synchronously.
+ *
+ * This function is new in version 3.3.0
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param watch if nonzero, a watch will be set at the server to notify
+ * the client if the node changes.
+ * \param strings return value of children paths.
+ * \param stat return value of node stat.
+ * \return the return code of the function.
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_get_children2(zhandle_t *zh, const char *path, int watch,
+ struct String_vector *strings, struct Stat *stat);
+
+/**
+ * \brief lists the children of a node and get its stat synchronously.
+ *
+ * This function is similar to \ref zoo_get_children except it allows one specify
+ * a watcher object rather than a boolean watch flag.
+ *
+ * This function is new in version 3.3.0
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param watcher if non-null, a watch will be set at the server to notify
+ * the client if the node changes.
+ * \param watcherCtx user specific data, will be passed to the watcher callback.
+ * Unlike the global context set by \ref zookeeper_init, this watcher context
+ * is associated with the given instance of the watcher only.
+ * \param strings return value of children paths.
+ * \param stat return value of node stat.
+ * \return the return code of the function.
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_wget_children2(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ struct String_vector *strings, struct Stat *stat);
+
+/**
+ * \brief gets the acl associated with a node synchronously.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param acl the return value of acls on the path.
+ * \param stat returns the stat of the path specified.
+ * \return the return code for the function call.
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_get_acl(zhandle_t *zh, const char *path, struct ACL_vector *acl,
+ struct Stat *stat);
+
+/**
+ * \brief sets the acl associated with a node synchronously.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param path the name of the node. Expressed as a file name with slashes
+ * separating ancestors of the node.
+ * \param version the expected version of the path.
+ * \param acl the acl to be set on the path.
+ * \return the return code for the function call.
+ * ZOK operation completed successfully
+ * ZNONODE the node does not exist.
+ * ZNOAUTH the client does not have permission.
+ * ZINVALIDACL invalid ACL specified
+ * ZBADVERSION expected version does not match actual version.
+ * ZBADARGUMENTS - invalid input parameters
+ * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
+ * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
+ */
+ZOOAPI int zoo_set_acl(zhandle_t *zh, const char *path, int version,
+ const struct ACL_vector *acl);
+
+/**
+ * \brief atomically commits multiple zookeeper operations synchronously.
+ *
+ * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
+ * \param count the number of operations
+ * \param ops an array of operations to commit
+ * \param results an array to hold the results of the operations
+ * \return the return code for the function call. This can be any of the
+ * values that can be returned by the ops supported by a multi op (see
+ * \ref zoo_acreate, \ref zoo_adelete, \ref zoo_aset).
+ */
+ZOOAPI int zoo_multi(zhandle_t *zh, int count, const zoo_op_t *ops, zoo_op_result_t *results);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*ZOOKEEPER_H_*/
diff --git a/src/c/include/zookeeper_log.h b/src/c/include/zookeeper_log.h
new file mode 100644
index 0000000..e5917cb
--- /dev/null
+++ b/src/c/include/zookeeper_log.h
@@ -0,0 +1,51 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 ZK_LOG_H_
+#define ZK_LOG_H_
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern ZOOAPI ZooLogLevel logLevel;
+#define LOGSTREAM getLogStream()
+
+#define LOG_ERROR(x) if(logLevel>=ZOO_LOG_LEVEL_ERROR) \
+ log_message(ZOO_LOG_LEVEL_ERROR,__LINE__,__func__,format_log_message x)
+#define LOG_WARN(x) if(logLevel>=ZOO_LOG_LEVEL_WARN) \
+ log_message(ZOO_LOG_LEVEL_WARN,__LINE__,__func__,format_log_message x)
+#define LOG_INFO(x) if(logLevel>=ZOO_LOG_LEVEL_INFO) \
+ log_message(ZOO_LOG_LEVEL_INFO,__LINE__,__func__,format_log_message x)
+#define LOG_DEBUG(x) if(logLevel==ZOO_LOG_LEVEL_DEBUG) \
+ log_message(ZOO_LOG_LEVEL_DEBUG,__LINE__,__func__,format_log_message x)
+
+ZOOAPI void log_message(ZooLogLevel curLevel, int line,const char* funcName,
+ const char* message);
+
+ZOOAPI const char* format_log_message(const char* format,...);
+
+FILE* getLogStream();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*ZK_LOG_H_*/
diff --git a/src/c/include/zookeeper_version.h b/src/c/include/zookeeper_version.h
new file mode 100644
index 0000000..518f7e6
--- /dev/null
+++ b/src/c/include/zookeeper_version.h
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 ZOOKEEPER_VERSION_H_
+#define ZOOKEEPER_VERSION_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZOO_MAJOR_VERSION 3
+#define ZOO_MINOR_VERSION 4
+#define ZOO_PATCH_VERSION 12
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZOOKEEPER_VERSION_H_ */
diff --git a/src/c/src/cli.c b/src/c/src/cli.c
new file mode 100644
index 0000000..ef32a10
--- /dev/null
+++ b/src/c/src/cli.c
@@ -0,0 +1,697 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/**
+ * cli.c is a example/sample C client shell for ZooKeeper. It contains
+ * basic shell functionality which exercises some of the features of
+ * the ZooKeeper C client API. It is not a full fledged client and is
+ * not meant for production usage - see the Java client shell for a
+ * fully featured shell.
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#ifndef WIN32
+#include
+#include
+#include
+#else
+#include "winport.h"
+//#include <-- can't include, conflicting definitions of close()
+int read(int _FileHandle, void * _DstBuf, unsigned int _MaxCharCount);
+int write(int _Filehandle, const void * _Buf, unsigned int _MaxCharCount);
+#define ctime_r(tctime, buffer) ctime_s (buffer, 40, tctime)
+#endif
+
+#include
+#include
+#include
+
+#ifdef YCA
+#include
+#endif
+
+#define _LL_CAST_ (long long)
+
+static zhandle_t *zh;
+static clientid_t myid;
+static const char *clientIdFile = 0;
+struct timeval startTime;
+static char cmd[1024];
+static int batchMode=0;
+
+static int to_send=0;
+static int sent=0;
+static int recvd=0;
+
+static int shutdownThisThing=0;
+
+static __attribute__ ((unused)) void
+printProfileInfo(struct timeval start, struct timeval end, int thres,
+ const char* msg)
+{
+ int delay=(end.tv_sec*1000+end.tv_usec/1000)-
+ (start.tv_sec*1000+start.tv_usec/1000);
+ if(delay>thres)
+ fprintf(stderr,"%s: execution time=%dms\n",msg,delay);
+}
+
+static const char* state2String(int state){
+ if (state == 0)
+ return "CLOSED_STATE";
+ if (state == ZOO_CONNECTING_STATE)
+ return "CONNECTING_STATE";
+ if (state == ZOO_ASSOCIATING_STATE)
+ return "ASSOCIATING_STATE";
+ if (state == ZOO_CONNECTED_STATE)
+ return "CONNECTED_STATE";
+ if (state == ZOO_EXPIRED_SESSION_STATE)
+ return "EXPIRED_SESSION_STATE";
+ if (state == ZOO_AUTH_FAILED_STATE)
+ return "AUTH_FAILED_STATE";
+
+ return "INVALID_STATE";
+}
+
+static const char* type2String(int state){
+ if (state == ZOO_CREATED_EVENT)
+ return "CREATED_EVENT";
+ if (state == ZOO_DELETED_EVENT)
+ return "DELETED_EVENT";
+ if (state == ZOO_CHANGED_EVENT)
+ return "CHANGED_EVENT";
+ if (state == ZOO_CHILD_EVENT)
+ return "CHILD_EVENT";
+ if (state == ZOO_SESSION_EVENT)
+ return "SESSION_EVENT";
+ if (state == ZOO_NOTWATCHING_EVENT)
+ return "NOTWATCHING_EVENT";
+
+ return "UNKNOWN_EVENT_TYPE";
+}
+
+void watcher(zhandle_t *zzh, int type, int state, const char *path,
+ void* context)
+{
+ /* Be careful using zh here rather than zzh - as this may be mt code
+ * the client lib may call the watcher before zookeeper_init returns */
+
+ fprintf(stderr, "Watcher %s state = %s", type2String(type), state2String(state));
+ if (path && strlen(path) > 0) {
+ fprintf(stderr, " for path %s", path);
+ }
+ fprintf(stderr, "\n");
+
+ if (type == ZOO_SESSION_EVENT) {
+ if (state == ZOO_CONNECTED_STATE) {
+ const clientid_t *id = zoo_client_id(zzh);
+ if (myid.client_id == 0 || myid.client_id != id->client_id) {
+ myid = *id;
+ fprintf(stderr, "Got a new session id: 0x%llx\n",
+ _LL_CAST_ myid.client_id);
+ if (clientIdFile) {
+ FILE *fh = fopen(clientIdFile, "w");
+ if (!fh) {
+ perror(clientIdFile);
+ } else {
+ int rc = fwrite(&myid, sizeof(myid), 1, fh);
+ if (rc != sizeof(myid)) {
+ perror("writing client id");
+ }
+ fclose(fh);
+ }
+ }
+ }
+ } else if (state == ZOO_AUTH_FAILED_STATE) {
+ fprintf(stderr, "Authentication failure. Shutting down...\n");
+ zookeeper_close(zzh);
+ shutdownThisThing=1;
+ zh=0;
+ } else if (state == ZOO_EXPIRED_SESSION_STATE) {
+ fprintf(stderr, "Session expired. Shutting down...\n");
+ zookeeper_close(zzh);
+ shutdownThisThing=1;
+ zh=0;
+ }
+ }
+}
+
+void dumpStat(const struct Stat *stat) {
+ char tctimes[40];
+ char tmtimes[40];
+ time_t tctime;
+ time_t tmtime;
+
+ if (!stat) {
+ fprintf(stderr,"null\n");
+ return;
+ }
+ tctime = stat->ctime/1000;
+ tmtime = stat->mtime/1000;
+
+ ctime_r(&tmtime, tmtimes);
+ ctime_r(&tctime, tctimes);
+
+ fprintf(stderr, "\tctime = %s\tczxid=%llx\n"
+ "\tmtime=%s\tmzxid=%llx\n"
+ "\tversion=%x\taversion=%x\n"
+ "\tephemeralOwner = %llx\n",
+ tctimes, _LL_CAST_ stat->czxid, tmtimes,
+ _LL_CAST_ stat->mzxid,
+ (unsigned int)stat->version, (unsigned int)stat->aversion,
+ _LL_CAST_ stat->ephemeralOwner);
+}
+
+void my_string_completion(int rc, const char *name, const void *data) {
+ fprintf(stderr, "[%s]: rc = %d\n", (char*)(data==0?"null":data), rc);
+ if (!rc) {
+ fprintf(stderr, "\tname = %s\n", name);
+ }
+ if(batchMode)
+ shutdownThisThing=1;
+}
+
+void my_string_completion_free_data(int rc, const char *name, const void *data) {
+ my_string_completion(rc, name, data);
+ free((void*)data);
+}
+
+void my_data_completion(int rc, const char *value, int value_len,
+ const struct Stat *stat, const void *data) {
+ struct timeval tv;
+ int sec;
+ int usec;
+ gettimeofday(&tv, 0);
+ sec = tv.tv_sec - startTime.tv_sec;
+ usec = tv.tv_usec - startTime.tv_usec;
+ fprintf(stderr, "time = %d msec\n", sec*1000 + usec/1000);
+ fprintf(stderr, "%s: rc = %d\n", (char*)data, rc);
+ if (value) {
+ fprintf(stderr, " value_len = %d\n", value_len);
+ assert(write(2, value, value_len) == value_len);
+ }
+ fprintf(stderr, "\nStat:\n");
+ dumpStat(stat);
+ free((void*)data);
+ if(batchMode)
+ shutdownThisThing=1;
+}
+
+void my_silent_data_completion(int rc, const char *value, int value_len,
+ const struct Stat *stat, const void *data) {
+ recvd++;
+ fprintf(stderr, "Data completion %s rc = %d\n",(char*)data,rc);
+ free((void*)data);
+ if (recvd==to_send) {
+ fprintf(stderr,"Recvd %d responses for %d requests sent\n",recvd,to_send);
+ if(batchMode)
+ shutdownThisThing=1;
+ }
+}
+
+void my_strings_completion(int rc, const struct String_vector *strings,
+ const void *data) {
+ struct timeval tv;
+ int sec;
+ int usec;
+ int i;
+
+ gettimeofday(&tv, 0);
+ sec = tv.tv_sec - startTime.tv_sec;
+ usec = tv.tv_usec - startTime.tv_usec;
+ fprintf(stderr, "time = %d msec\n", sec*1000 + usec/1000);
+ fprintf(stderr, "%s: rc = %d\n", (char*)data, rc);
+ if (strings)
+ for (i=0; i < strings->count; i++) {
+ fprintf(stderr, "\t%s\n", strings->data[i]);
+ }
+ free((void*)data);
+ gettimeofday(&tv, 0);
+ sec = tv.tv_sec - startTime.tv_sec;
+ usec = tv.tv_usec - startTime.tv_usec;
+ fprintf(stderr, "time = %d msec\n", sec*1000 + usec/1000);
+ if(batchMode)
+ shutdownThisThing=1;
+}
+
+void my_strings_stat_completion(int rc, const struct String_vector *strings,
+ const struct Stat *stat, const void *data) {
+ my_strings_completion(rc, strings, data);
+ dumpStat(stat);
+ if(batchMode)
+ shutdownThisThing=1;
+}
+
+void my_void_completion(int rc, const void *data) {
+ fprintf(stderr, "%s: rc = %d\n", (char*)data, rc);
+ free((void*)data);
+ if(batchMode)
+ shutdownThisThing=1;
+}
+
+void my_stat_completion(int rc, const struct Stat *stat, const void *data) {
+ fprintf(stderr, "%s: rc = %d Stat:\n", (char*)data, rc);
+ dumpStat(stat);
+ free((void*)data);
+ if(batchMode)
+ shutdownThisThing=1;
+}
+
+void my_silent_stat_completion(int rc, const struct Stat *stat,
+ const void *data) {
+ // fprintf(stderr, "State completion: [%s] rc = %d\n", (char*)data, rc);
+ sent++;
+ free((void*)data);
+}
+
+static void sendRequest(const char* data) {
+ zoo_aset(zh, "/od", data, strlen(data), -1, my_silent_stat_completion,
+ strdup("/od"));
+ zoo_aget(zh, "/od", 1, my_silent_data_completion, strdup("/od"));
+}
+
+void od_completion(int rc, const struct Stat *stat, const void *data) {
+ int i;
+ fprintf(stderr, "od command response: rc = %d Stat:\n", rc);
+ dumpStat(stat);
+ // send a whole bunch of requests
+ recvd=0;
+ sent=0;
+ to_send=200;
+ for (i=0; i\n");
+ fprintf(stderr, " delete \n");
+ fprintf(stderr, " set \n");
+ fprintf(stderr, " get \n");
+ fprintf(stderr, " ls \n");
+ fprintf(stderr, " ls2 \n");
+ fprintf(stderr, " sync \n");
+ fprintf(stderr, " exists \n");
+ fprintf(stderr, " wexists \n");
+ fprintf(stderr, " myid\n");
+ fprintf(stderr, " verbose\n");
+ fprintf(stderr, " addauth \n");
+ fprintf(stderr, " quit\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " prefix the command with the character 'a' to run the command asynchronously.\n");
+ fprintf(stderr, " run the 'verbose' command to toggle verbose logging.\n");
+ fprintf(stderr, " i.e. 'aget /foo' to get /foo asynchronously\n");
+ } else if (startsWith(line, "verbose")) {
+ if (verbose) {
+ verbose = 0;
+ zoo_set_debug_level(ZOO_LOG_LEVEL_WARN);
+ fprintf(stderr, "logging level set to WARN\n");
+ } else {
+ verbose = 1;
+ zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG);
+ fprintf(stderr, "logging level set to DEBUG\n");
+ }
+ } else if (startsWith(line, "get ")) {
+ line += 4;
+ if (line[0] != '/') {
+ fprintf(stderr, "Path must start with /, found: %s\n", line);
+ return;
+ }
+
+ rc = zoo_aget(zh, line, 1, my_data_completion, strdup(line));
+ if (rc) {
+ fprintf(stderr, "Error %d for %s\n", rc, line);
+ }
+ } else if (startsWith(line, "set ")) {
+ char *ptr;
+ line += 4;
+ if (line[0] != '/') {
+ fprintf(stderr, "Path must start with /, found: %s\n", line);
+ return;
+ }
+ ptr = strchr(line, ' ');
+ if (!ptr) {
+ fprintf(stderr, "No data found after path\n");
+ return;
+ }
+ *ptr = '\0';
+ ptr++;
+ if (async) {
+ rc = zoo_aset(zh, line, ptr, strlen(ptr), -1, my_stat_completion,
+ strdup(line));
+ } else {
+ struct Stat stat;
+ rc = zoo_set2(zh, line, ptr, strlen(ptr), -1, &stat);
+ }
+ if (rc) {
+ fprintf(stderr, "Error %d for %s\n", rc, line);
+ }
+ } else if (startsWith(line, "ls ")) {
+ line += 3;
+ if (line[0] != '/') {
+ fprintf(stderr, "Path must start with /, found: %s\n", line);
+ return;
+ }
+ gettimeofday(&startTime, 0);
+ rc= zoo_aget_children(zh, line, 1, my_strings_completion, strdup(line));
+ if (rc) {
+ fprintf(stderr, "Error %d for %s\n", rc, line);
+ }
+ } else if (startsWith(line, "ls2 ")) {
+ line += 4;
+ if (line[0] != '/') {
+ fprintf(stderr, "Path must start with /, found: %s\n", line);
+ return;
+ }
+ gettimeofday(&startTime, 0);
+ rc= zoo_aget_children2(zh, line, 1, my_strings_stat_completion, strdup(line));
+ if (rc) {
+ fprintf(stderr, "Error %d for %s\n", rc, line);
+ }
+ } else if (startsWith(line, "create ")) {
+ int flags = 0;
+ line += 7;
+ if (line[0] == '+') {
+ line++;
+ if (line[0] == 'e') {
+ flags |= ZOO_EPHEMERAL;
+ line++;
+ }
+ if (line[0] == 's') {
+ flags |= ZOO_SEQUENCE;
+ line++;
+ }
+ line++;
+ }
+ if (line[0] != '/') {
+ fprintf(stderr, "Path must start with /, found: %s\n", line);
+ return;
+ }
+ fprintf(stderr, "Creating [%s] node\n", line);
+// {
+// struct ACL _CREATE_ONLY_ACL_ACL[] = {{ZOO_PERM_CREATE, ZOO_ANYONE_ID_UNSAFE}};
+// struct ACL_vector CREATE_ONLY_ACL = {1,_CREATE_ONLY_ACL_ACL};
+// rc = zoo_acreate(zh, line, "new", 3, &CREATE_ONLY_ACL, flags,
+// my_string_completion, strdup(line));
+// }
+ rc = zoo_acreate(zh, line, "new", 3, &ZOO_OPEN_ACL_UNSAFE, flags,
+ my_string_completion_free_data, strdup(line));
+ if (rc) {
+ fprintf(stderr, "Error %d for %s\n", rc, line);
+ }
+ } else if (startsWith(line, "delete ")) {
+ line += 7;
+ if (line[0] != '/') {
+ fprintf(stderr, "Path must start with /, found: %s\n", line);
+ return;
+ }
+ if (async) {
+ rc = zoo_adelete(zh, line, -1, my_void_completion, strdup(line));
+ } else {
+ rc = zoo_delete(zh, line, -1);
+ }
+ if (rc) {
+ fprintf(stderr, "Error %d for %s\n", rc, line);
+ }
+ } else if (startsWith(line, "sync ")) {
+ line += 5;
+ if (line[0] != '/') {
+ fprintf(stderr, "Path must start with /, found: %s\n", line);
+ return;
+ }
+ rc = zoo_async(zh, line, my_string_completion_free_data, strdup(line));
+ if (rc) {
+ fprintf(stderr, "Error %d for %s\n", rc, line);
+ }
+ } else if (startsWith(line, "wexists ")) {
+#ifdef THREADED
+ struct Stat stat;
+#endif
+ line += 8;
+ if (line[0] != '/') {
+ fprintf(stderr, "Path must start with /, found: %s\n", line);
+ return;
+ }
+#ifndef THREADED
+ rc = zoo_awexists(zh, line, watcher, (void*) 0, my_stat_completion, strdup(line));
+#else
+ rc = zoo_wexists(zh, line, watcher, (void*) 0, &stat);
+#endif
+ if (rc) {
+ fprintf(stderr, "Error %d for %s\n", rc, line);
+ }
+ } else if (startsWith(line, "exists ")) {
+#ifdef THREADED
+ struct Stat stat;
+#endif
+ line += 7;
+ if (line[0] != '/') {
+ fprintf(stderr, "Path must start with /, found: %s\n", line);
+ return;
+ }
+#ifndef THREADED
+ rc = zoo_aexists(zh, line, 1, my_stat_completion, strdup(line));
+#else
+ rc = zoo_exists(zh, line, 1, &stat);
+#endif
+ if (rc) {
+ fprintf(stderr, "Error %d for %s\n", rc, line);
+ }
+ } else if (strcmp(line, "myid") == 0) {
+ printf("session Id = %llx\n", _LL_CAST_ zoo_client_id(zh)->client_id);
+ } else if (strcmp(line, "reinit") == 0) {
+ zookeeper_close(zh);
+ // we can't send myid to the server here -- zookeeper_close() removes
+ // the session on the server. We must start anew.
+ zh = zookeeper_init(hostPort, watcher, 30000, 0, 0, 0);
+ } else if (startsWith(line, "quit")) {
+ fprintf(stderr, "Quitting...\n");
+ shutdownThisThing=1;
+ } else if (startsWith(line, "od")) {
+ const char val[]="fire off";
+ fprintf(stderr, "Overdosing...\n");
+ rc = zoo_aset(zh, "/od", val, sizeof(val)-1, -1, od_completion, 0);
+ if (rc)
+ fprintf(stderr, "od command failed: %d\n", rc);
+ } else if (startsWith(line, "addauth ")) {
+ char *ptr;
+ line += 8;
+ ptr = strchr(line, ' ');
+ if (ptr) {
+ *ptr = '\0';
+ ptr++;
+ }
+ zoo_add_auth(zh, line, ptr, ptr ? strlen(ptr) : 0, NULL, NULL);
+ }
+}
+
+int main(int argc, char **argv) {
+#ifndef THREADED
+ fd_set rfds, wfds, efds;
+ int processed=0;
+#endif
+ char buffer[4096];
+ char p[2048];
+#ifdef YCA
+ char *cert=0;
+ char appId[64];
+#endif
+ int bufoff = 0;
+ FILE *fh;
+
+ if (argc < 2) {
+ fprintf(stderr,
+ "USAGE %s zookeeper_host_list [clientid_file|cmd:(ls|ls2|create|od|...)]\n",
+ argv[0]);
+ fprintf(stderr,
+ "Version: ZooKeeper cli (c client) version %d.%d.%d\n",
+ ZOO_MAJOR_VERSION,
+ ZOO_MINOR_VERSION,
+ ZOO_PATCH_VERSION);
+ return 2;
+ }
+ if (argc > 2) {
+ if(strncmp("cmd:",argv[2],4)==0){
+ size_t cmdlen = strlen(argv[2]);
+ if (cmdlen > sizeof(cmd)) {
+ fprintf(stderr,
+ "Command length %zu exceeds max length of %zu\n",
+ cmdlen,
+ sizeof(cmd));
+ return 2;
+ }
+ strncpy(cmd, argv[2]+4, sizeof(cmd));
+ batchMode=1;
+ fprintf(stderr,"Batch mode: %s\n",cmd);
+ }else{
+ clientIdFile = argv[2];
+ fh = fopen(clientIdFile, "r");
+ if (fh) {
+ if (fread(&myid, sizeof(myid), 1, fh) != sizeof(myid)) {
+ memset(&myid, 0, sizeof(myid));
+ }
+ fclose(fh);
+ }
+ }
+ }
+#ifdef YCA
+ strcpy(appId,"yahoo.example.yca_test");
+ cert = yca_get_cert_once(appId);
+ if(cert!=0) {
+ fprintf(stderr,"Certificate for appid [%s] is [%s]\n",appId,cert);
+ strncpy(p,cert,sizeof(p)-1);
+ free(cert);
+ } else {
+ fprintf(stderr,"Certificate for appid [%s] not found\n",appId);
+ strcpy(p,"dummy");
+ }
+#else
+ strcpy(p, "dummy");
+#endif
+ verbose = 0;
+ zoo_set_debug_level(ZOO_LOG_LEVEL_WARN);
+ zoo_deterministic_conn_order(1); // enable deterministic order
+ hostPort = argv[1];
+ zh = zookeeper_init(hostPort, watcher, 30000, &myid, 0, 0);
+ if (!zh) {
+ return errno;
+ }
+
+#ifdef YCA
+ if(zoo_add_auth(zh,"yca",p,strlen(p),0,0)!=ZOK)
+ return 2;
+#endif
+
+#ifdef THREADED
+ while(!shutdownThisThing) {
+ int rc;
+ int len = sizeof(buffer) - bufoff -1;
+ if (len <= 0) {
+ fprintf(stderr, "Can't handle lines that long!\n");
+ exit(2);
+ }
+ rc = read(0, buffer+bufoff, len);
+ if (rc <= 0) {
+ fprintf(stderr, "bye\n");
+ shutdownThisThing=1;
+ break;
+ }
+ bufoff += rc;
+ buffer[bufoff] = '\0';
+ while (strchr(buffer, '\n')) {
+ char *ptr = strchr(buffer, '\n');
+ *ptr = '\0';
+ processline(buffer);
+ ptr++;
+ memmove(buffer, ptr, strlen(ptr)+1);
+ bufoff = 0;
+ }
+ }
+#else
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&efds);
+ while (!shutdownThisThing) {
+ int fd;
+ int interest;
+ int events;
+ struct timeval tv;
+ int rc;
+ zookeeper_interest(zh, &fd, &interest, &tv);
+ if (fd != -1) {
+ if (interest&ZOOKEEPER_READ) {
+ FD_SET(fd, &rfds);
+ } else {
+ FD_CLR(fd, &rfds);
+ }
+ if (interest&ZOOKEEPER_WRITE) {
+ FD_SET(fd, &wfds);
+ } else {
+ FD_CLR(fd, &wfds);
+ }
+ } else {
+ fd = 0;
+ }
+ FD_SET(0, &rfds);
+ rc = select(fd+1, &rfds, &wfds, &efds, &tv);
+ events = 0;
+ if (rc > 0) {
+ if (FD_ISSET(fd, &rfds)) {
+ events |= ZOOKEEPER_READ;
+ }
+ if (FD_ISSET(fd, &wfds)) {
+ events |= ZOOKEEPER_WRITE;
+ }
+ }
+ if(batchMode && processed==0){
+ //batch mode
+ processline(cmd);
+ processed=1;
+ }
+ if (FD_ISSET(0, &rfds)) {
+ int rc;
+ int len = sizeof(buffer) - bufoff -1;
+ if (len <= 0) {
+ fprintf(stderr, "Can't handle lines that long!\n");
+ exit(2);
+ }
+ rc = read(0, buffer+bufoff, len);
+ if (rc <= 0) {
+ fprintf(stderr, "bye\n");
+ break;
+ }
+ bufoff += rc;
+ buffer[bufoff] = '\0';
+ while (strchr(buffer, '\n')) {
+ char *ptr = strchr(buffer, '\n');
+ *ptr = '\0';
+ processline(buffer);
+ ptr++;
+ memmove(buffer, ptr, strlen(ptr)+1);
+ bufoff = 0;
+ }
+ }
+ zookeeper_process(zh, events);
+ }
+#endif
+ if (to_send!=0)
+ fprintf(stderr,"Recvd %d responses for %d requests sent\n",recvd,sent);
+ zookeeper_close(zh);
+ return 0;
+}
diff --git a/src/c/src/hashtable/LICENSE.txt b/src/c/src/hashtable/LICENSE.txt
new file mode 100644
index 0000000..674a624
--- /dev/null
+++ b/src/c/src/hashtable/LICENSE.txt
@@ -0,0 +1,30 @@
+Copyright (c) 2002, 2004, Christopher Clark
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the original author; nor the names of any contributors
+may be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/c/src/hashtable/hashtable.c b/src/c/src/hashtable/hashtable.c
new file mode 100644
index 0000000..763357e
--- /dev/null
+++ b/src/c/src/hashtable/hashtable.c
@@ -0,0 +1,274 @@
+/* Copyright (C) 2004 Christopher Clark */
+
+#include "hashtable.h"
+#include "hashtable_private.h"
+#include
+#include
+#include
+#include
+
+/*
+Credit for primes table: Aaron Krowne
+ http://br.endernet.org/~akrowne/
+ http://planetmath.org/encyclopedia/GoodHashTablePrimes.html
+*/
+static const unsigned int primes[] = {
+53, 97, 193, 389,
+769, 1543, 3079, 6151,
+12289, 24593, 49157, 98317,
+196613, 393241, 786433, 1572869,
+3145739, 6291469, 12582917, 25165843,
+50331653, 100663319, 201326611, 402653189,
+805306457, 1610612741
+};
+const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]);
+const float max_load_factor = 0.65;
+
+/*****************************************************************************/
+struct hashtable *
+create_hashtable(unsigned int minsize,
+ unsigned int (*hashf) (void*),
+ int (*eqf) (void*,void*))
+{
+ struct hashtable *h;
+ unsigned int pindex, size = primes[0];
+ /* Check requested hashtable isn't too large */
+ if (minsize > (1u << 30)) return NULL;
+ /* Enforce size as prime */
+ for (pindex=0; pindex < prime_table_length; pindex++) {
+ if (primes[pindex] > minsize) { size = primes[pindex]; break; }
+ }
+ h = (struct hashtable *)malloc(sizeof(struct hashtable));
+ if (NULL == h) return NULL; /*oom*/
+ h->table = (struct entry **)malloc(sizeof(struct entry*) * size);
+ if (NULL == h->table) { free(h); return NULL; } /*oom*/
+ memset(h->table, 0, size * sizeof(struct entry *));
+ h->tablelength = size;
+ h->primeindex = pindex;
+ h->entrycount = 0;
+ h->hashfn = hashf;
+ h->eqfn = eqf;
+ h->loadlimit = (unsigned int) ceil(size * max_load_factor);
+ return h;
+}
+
+/*****************************************************************************/
+unsigned int
+hash(struct hashtable *h, void *k)
+{
+ /* Aim to protect against poor hash functions by adding logic here
+ * - logic taken from java 1.4 hashtable source */
+ unsigned int i = h->hashfn(k);
+ i += ~(i << 9);
+ i ^= ((i >> 14) | (i << 18)); /* >>> */
+ i += (i << 4);
+ i ^= ((i >> 10) | (i << 22)); /* >>> */
+ return i;
+}
+
+/*****************************************************************************/
+static int
+hashtable_expand(struct hashtable *h)
+{
+ /* Double the size of the table to accomodate more entries */
+ struct entry **newtable;
+ struct entry *e;
+ struct entry **pE;
+ unsigned int newsize, i, index;
+ /* Check we're not hitting max capacity */
+ if (h->primeindex == (prime_table_length - 1)) return 0;
+ newsize = primes[++(h->primeindex)];
+
+ newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize);
+ if (NULL != newtable)
+ {
+ memset(newtable, 0, newsize * sizeof(struct entry *));
+ /* This algorithm is not 'stable'. ie. it reverses the list
+ * when it transfers entries between the tables */
+ for (i = 0; i < h->tablelength; i++) {
+ while (NULL != (e = h->table[i])) {
+ h->table[i] = e->next;
+ index = indexFor(newsize,e->h);
+ e->next = newtable[index];
+ newtable[index] = e;
+ }
+ }
+ free(h->table);
+ h->table = newtable;
+ }
+ /* Plan B: realloc instead */
+ else
+ {
+ newtable = (struct entry **)
+ realloc(h->table, newsize * sizeof(struct entry *));
+ if (NULL == newtable) { (h->primeindex)--; return 0; }
+ h->table = newtable;
+ memset(newtable[h->tablelength], 0, newsize - h->tablelength);
+ for (i = 0; i < h->tablelength; i++) {
+ for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) {
+ index = indexFor(newsize,e->h);
+ if (index == i)
+ {
+ pE = &(e->next);
+ }
+ else
+ {
+ *pE = e->next;
+ e->next = newtable[index];
+ newtable[index] = e;
+ }
+ }
+ }
+ }
+ h->tablelength = newsize;
+ h->loadlimit = (unsigned int) ceil(newsize * max_load_factor);
+ return -1;
+}
+
+/*****************************************************************************/
+unsigned int
+hashtable_count(struct hashtable *h)
+{
+ return h->entrycount;
+}
+
+/*****************************************************************************/
+int
+hashtable_insert(struct hashtable *h, void *k, void *v)
+{
+ /* This method allows duplicate keys - but they shouldn't be used */
+ unsigned int index;
+ struct entry *e;
+ if (++(h->entrycount) > h->loadlimit)
+ {
+ /* Ignore the return value. If expand fails, we should
+ * still try cramming just this value into the existing table
+ * -- we may not have memory for a larger table, but one more
+ * element may be ok. Next time we insert, we'll try expanding again.*/
+ hashtable_expand(h);
+ }
+ e = (struct entry *)malloc(sizeof(struct entry));
+ if (NULL == e) { --(h->entrycount); return 0; } /*oom*/
+ e->h = hash(h,k);
+ index = indexFor(h->tablelength,e->h);
+ e->k = k;
+ e->v = v;
+ e->next = h->table[index];
+ h->table[index] = e;
+ return -1;
+}
+
+/*****************************************************************************/
+void * /* returns value associated with key */
+hashtable_search(struct hashtable *h, void *k)
+{
+ struct entry *e;
+ unsigned int hashvalue, index;
+ hashvalue = hash(h,k);
+ index = indexFor(h->tablelength,hashvalue);
+ e = h->table[index];
+ while (NULL != e)
+ {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v;
+ e = e->next;
+ }
+ return NULL;
+}
+
+/*****************************************************************************/
+void * /* returns value associated with key */
+hashtable_remove(struct hashtable *h, void *k)
+{
+ /* TODO: consider compacting the table when the load factor drops enough,
+ * or provide a 'compact' method. */
+
+ struct entry *e;
+ struct entry **pE;
+ void *v;
+ unsigned int hashvalue, index;
+
+ hashvalue = hash(h,k);
+ index = indexFor(h->tablelength,hash(h,k));
+ pE = &(h->table[index]);
+ e = *pE;
+ while (NULL != e)
+ {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hashvalue == e->h) && (h->eqfn(k, e->k)))
+ {
+ *pE = e->next;
+ h->entrycount--;
+ v = e->v;
+ freekey(e->k);
+ free(e);
+ return v;
+ }
+ pE = &(e->next);
+ e = e->next;
+ }
+ return NULL;
+}
+
+/*****************************************************************************/
+/* destroy */
+void
+hashtable_destroy(struct hashtable *h, int free_values)
+{
+ unsigned int i;
+ struct entry *e, *f;
+ struct entry **table = h->table;
+ if (free_values)
+ {
+ for (i = 0; i < h->tablelength; i++)
+ {
+ e = table[i];
+ while (NULL != e)
+ { f = e; e = e->next; freekey(f->k); free(f->v); free(f); }
+ }
+ }
+ else
+ {
+ for (i = 0; i < h->tablelength; i++)
+ {
+ e = table[i];
+ while (NULL != e)
+ { f = e; e = e->next; freekey(f->k); free(f); }
+ }
+ }
+ free(h->table);
+ free(h);
+}
+
+/*
+ * Copyright (c) 2002, Christopher Clark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
diff --git a/src/c/src/hashtable/hashtable.h b/src/c/src/hashtable/hashtable.h
new file mode 100644
index 0000000..cbead18
--- /dev/null
+++ b/src/c/src/hashtable/hashtable.h
@@ -0,0 +1,209 @@
+/* Copyright (C) 2002 Christopher Clark */
+
+#ifndef __HASHTABLE_CWC22_H__
+#define __HASHTABLE_CWC22_H__
+#ifdef WIN32
+#include "winconfig.h"
+#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct hashtable;
+
+/* Example of use:
+ *
+ * struct hashtable *h;
+ * struct some_key *k;
+ * struct some_value *v;
+ *
+ * static unsigned int hash_from_key_fn( void *k );
+ * static int keys_equal_fn ( void *key1, void *key2 );
+ *
+ * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn);
+ * k = (struct some_key *) malloc(sizeof(struct some_key));
+ * v = (struct some_value *) malloc(sizeof(struct some_value));
+ *
+ * (initialise k and v to suitable values)
+ *
+ * if (! hashtable_insert(h,k,v) )
+ * { exit(-1); }
+ *
+ * if (NULL == (found = hashtable_search(h,k) ))
+ * { printf("not found!"); }
+ *
+ * if (NULL == (found = hashtable_remove(h,k) ))
+ * { printf("Not found\n"); }
+ *
+ */
+
+/* Macros may be used to define type-safe(r) hashtable access functions, with
+ * methods specialized to take known key and value types as parameters.
+ *
+ * Example:
+ *
+ * Insert this at the start of your file:
+ *
+ * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value);
+ * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value);
+ * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value);
+ *
+ * This defines the functions 'insert_some', 'search_some' and 'remove_some'.
+ * These operate just like hashtable_insert etc., with the same parameters,
+ * but their function signatures have 'struct some_key *' rather than
+ * 'void *', and hence can generate compile time errors if your program is
+ * supplying incorrect data as a key (and similarly for value).
+ *
+ * Note that the hash and key equality functions passed to create_hashtable
+ * still take 'void *' parameters instead of 'some key *'. This shouldn't be
+ * a difficult issue as they're only defined and passed once, and the other
+ * functions will ensure that only valid keys are supplied to them.
+ *
+ * The cost for this checking is increased code size and runtime overhead
+ * - if performance is important, it may be worth switching back to the
+ * unsafe methods once your program has been debugged with the safe methods.
+ * This just requires switching to some simple alternative defines - eg:
+ * #define insert_some hashtable_insert
+ *
+ */
+
+/*****************************************************************************
+ * create_hashtable
+
+ * @name create_hashtable
+ * @param minsize minimum initial size of hashtable
+ * @param hashfunction function for hashing keys
+ * @param key_eq_fn function for determining key equality
+ * @return newly created hashtable or NULL on failure
+ */
+
+struct hashtable *
+create_hashtable(unsigned int minsize,
+ unsigned int (*hashfunction) (void*),
+ int (*key_eq_fn) (void*,void*));
+
+/*****************************************************************************
+ * hashtable_insert
+
+ * @name hashtable_insert
+ * @param h the hashtable to insert into
+ * @param k the key - hashtable claims ownership and will free on removal
+ * @param v the value - does not claim ownership
+ * @return non-zero for successful insertion
+ *
+ * This function will cause the table to expand if the insertion would take
+ * the ratio of entries to table size over the maximum load factor.
+ *
+ * This function does not check for repeated insertions with a duplicate key.
+ * The value returned when using a duplicate key is undefined -- when
+ * the hashtable changes size, the order of retrieval of duplicate key
+ * entries is reversed.
+ * If in doubt, remove before insert.
+ */
+
+int
+hashtable_insert(struct hashtable *h, void *k, void *v);
+
+#define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \
+int fnname (struct hashtable *h, keytype *k, valuetype *v) \
+{ \
+ return hashtable_insert(h,k,v); \
+}
+
+/*****************************************************************************
+ * hashtable_search
+
+ * @name hashtable_search
+ * @param h the hashtable to search
+ * @param k the key to search for - does not claim ownership
+ * @return the value associated with the key, or NULL if none found
+ */
+
+void *
+hashtable_search(struct hashtable *h, void *k);
+
+#define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \
+valuetype * fnname (struct hashtable *h, keytype *k) \
+{ \
+ return (valuetype *) (hashtable_search(h,k)); \
+}
+
+/*****************************************************************************
+ * hashtable_remove
+
+ * @name hashtable_remove
+ * @param h the hashtable to remove the item from
+ * @param k the key to search for - does not claim ownership
+ * @return the value associated with the key, or NULL if none found
+ */
+
+void * /* returns value */
+hashtable_remove(struct hashtable *h, void *k);
+
+#define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \
+valuetype * fnname (struct hashtable *h, keytype *k) \
+{ \
+ return (valuetype *) (hashtable_remove(h,k)); \
+}
+
+
+/*****************************************************************************
+ * hashtable_count
+
+ * @name hashtable_count
+ * @param h the hashtable
+ * @return the number of items stored in the hashtable
+ */
+unsigned int
+hashtable_count(struct hashtable *h);
+
+
+/*****************************************************************************
+ * hashtable_destroy
+
+ * @name hashtable_destroy
+ * @param h the hashtable
+ * @param free_values whether to call 'free' on the remaining values
+ */
+
+void
+hashtable_destroy(struct hashtable *h, int free_values);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __HASHTABLE_CWC22_H__ */
+
+/*
+ * Copyright (c) 2002, Christopher Clark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
diff --git a/src/c/src/hashtable/hashtable_itr.c b/src/c/src/hashtable/hashtable_itr.c
new file mode 100644
index 0000000..defac69
--- /dev/null
+++ b/src/c/src/hashtable/hashtable_itr.c
@@ -0,0 +1,176 @@
+/* Copyright (C) 2002, 2004 Christopher Clark */
+
+#include "hashtable.h"
+#include "hashtable_private.h"
+#include "hashtable_itr.h"
+#include /* defines NULL */
+
+/*****************************************************************************/
+/* hashtable_iterator - iterator constructor */
+
+struct hashtable_itr *
+hashtable_iterator(struct hashtable *h)
+{
+ unsigned int i, tablelength;
+ struct hashtable_itr *itr = (struct hashtable_itr *)
+ malloc(sizeof(struct hashtable_itr));
+ if (NULL == itr) return NULL;
+ itr->h = h;
+ itr->e = NULL;
+ itr->parent = NULL;
+ tablelength = h->tablelength;
+ itr->index = tablelength;
+ if (0 == h->entrycount) return itr;
+
+ for (i = 0; i < tablelength; i++)
+ {
+ if (NULL != h->table[i])
+ {
+ itr->e = h->table[i];
+ itr->index = i;
+ break;
+ }
+ }
+ return itr;
+}
+
+/*****************************************************************************/
+/* advance - advance the iterator to the next element
+ * returns zero if advanced to end of table */
+
+int
+hashtable_iterator_advance(struct hashtable_itr *itr)
+{
+ unsigned int j,tablelength;
+ struct entry **table;
+ struct entry *next;
+ if (NULL == itr->e) return 0; /* stupidity check */
+
+ next = itr->e->next;
+ if (NULL != next)
+ {
+ itr->parent = itr->e;
+ itr->e = next;
+ return -1;
+ }
+ tablelength = itr->h->tablelength;
+ itr->parent = NULL;
+ if (tablelength <= (j = ++(itr->index)))
+ {
+ itr->e = NULL;
+ return 0;
+ }
+ table = itr->h->table;
+ while (NULL == (next = table[j]))
+ {
+ if (++j >= tablelength)
+ {
+ itr->index = tablelength;
+ itr->e = NULL;
+ return 0;
+ }
+ }
+ itr->index = j;
+ itr->e = next;
+ return -1;
+}
+
+/*****************************************************************************/
+/* remove - remove the entry at the current iterator position
+ * and advance the iterator, if there is a successive
+ * element.
+ * If you want the value, read it before you remove:
+ * beware memory leaks if you don't.
+ * Returns zero if end of iteration. */
+
+int
+hashtable_iterator_remove(struct hashtable_itr *itr)
+{
+ struct entry *remember_e, *remember_parent;
+ int ret;
+
+ /* Do the removal */
+ if (NULL == (itr->parent))
+ {
+ /* element is head of a chain */
+ itr->h->table[itr->index] = itr->e->next;
+ } else {
+ /* element is mid-chain */
+ itr->parent->next = itr->e->next;
+ }
+ /* itr->e is now outside the hashtable */
+ remember_e = itr->e;
+ itr->h->entrycount--;
+ freekey(remember_e->k);
+
+ /* Advance the iterator, correcting the parent */
+ remember_parent = itr->parent;
+ ret = hashtable_iterator_advance(itr);
+ if (itr->parent == remember_e) { itr->parent = remember_parent; }
+ free(remember_e);
+ return ret;
+}
+
+/*****************************************************************************/
+int /* returns zero if not found */
+hashtable_iterator_search(struct hashtable_itr *itr,
+ struct hashtable *h, void *k)
+{
+ struct entry *e, *parent;
+ unsigned int hashvalue, index;
+
+ hashvalue = hash(h,k);
+ index = indexFor(h->tablelength,hashvalue);
+
+ e = h->table[index];
+ parent = NULL;
+ while (NULL != e)
+ {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hashvalue == e->h) && (h->eqfn(k, e->k)))
+ {
+ itr->index = index;
+ itr->e = e;
+ itr->parent = parent;
+ itr->h = h;
+ return -1;
+ }
+ parent = e;
+ e = e->next;
+ }
+ return 0;
+}
+
+
+/*
+ * Copyright (c) 2002, 2004, Christopher Clark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
diff --git a/src/c/src/hashtable/hashtable_itr.h b/src/c/src/hashtable/hashtable_itr.h
new file mode 100644
index 0000000..30379c7
--- /dev/null
+++ b/src/c/src/hashtable/hashtable_itr.h
@@ -0,0 +1,119 @@
+/* Copyright (C) 2002, 2004 Christopher Clark */
+
+#ifndef __HASHTABLE_ITR_CWC22__
+#define __HASHTABLE_ITR_CWC22__
+#include "hashtable.h"
+#include "hashtable_private.h" /* needed to enable inlining */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*****************************************************************************/
+/* This struct is only concrete here to allow the inlining of two of the
+ * accessor functions. */
+struct hashtable_itr
+{
+ struct hashtable *h;
+ struct entry *e;
+ struct entry *parent;
+ unsigned int index;
+};
+
+
+/*****************************************************************************/
+/* hashtable_iterator
+ */
+
+struct hashtable_itr *
+hashtable_iterator(struct hashtable *h);
+
+/*****************************************************************************/
+/* hashtable_iterator_key
+ * - return the value of the (key,value) pair at the current position */
+
+static inline void *
+hashtable_iterator_key(struct hashtable_itr *i)
+{
+ return i->e->k;
+}
+
+/*****************************************************************************/
+/* value - return the value of the (key,value) pair at the current position */
+
+static inline void *
+hashtable_iterator_value(struct hashtable_itr *i)
+{
+ return i->e->v;
+}
+
+/*****************************************************************************/
+/* advance - advance the iterator to the next element
+ * returns zero if advanced to end of table */
+
+int
+hashtable_iterator_advance(struct hashtable_itr *itr);
+
+/*****************************************************************************/
+/* remove - remove current element and advance the iterator to the next element
+ * NB: if you need the value to free it, read it before
+ * removing. ie: beware memory leaks!
+ * returns zero if advanced to end of table */
+
+int
+hashtable_iterator_remove(struct hashtable_itr *itr);
+
+/*****************************************************************************/
+/* search - overwrite the supplied iterator, to point to the entry
+ * matching the supplied key.
+ h points to the hashtable to be searched.
+ * returns zero if not found. */
+int
+hashtable_iterator_search(struct hashtable_itr *itr,
+ struct hashtable *h, void *k);
+
+#define DEFINE_HASHTABLE_ITERATOR_SEARCH(fnname, keytype) \
+int fnname (struct hashtable_itr *i, struct hashtable *h, keytype *k) \
+{ \
+ return (hashtable_iterator_search(i,h,k)); \
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __HASHTABLE_ITR_CWC22__*/
+
+/*
+ * Copyright (c) 2002, 2004, Christopher Clark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
diff --git a/src/c/src/hashtable/hashtable_private.h b/src/c/src/hashtable/hashtable_private.h
new file mode 100644
index 0000000..3e95f60
--- /dev/null
+++ b/src/c/src/hashtable/hashtable_private.h
@@ -0,0 +1,85 @@
+/* Copyright (C) 2002, 2004 Christopher Clark */
+
+#ifndef __HASHTABLE_PRIVATE_CWC22_H__
+#define __HASHTABLE_PRIVATE_CWC22_H__
+
+#include "hashtable.h"
+
+/*****************************************************************************/
+struct entry
+{
+ void *k, *v;
+ unsigned int h;
+ struct entry *next;
+};
+
+struct hashtable {
+ unsigned int tablelength;
+ struct entry **table;
+ unsigned int entrycount;
+ unsigned int loadlimit;
+ unsigned int primeindex;
+ unsigned int (*hashfn) (void *k);
+ int (*eqfn) (void *k1, void *k2);
+};
+
+/*****************************************************************************/
+unsigned int
+hash(struct hashtable *h, void *k);
+
+/*****************************************************************************/
+/* indexFor */
+static inline unsigned int
+indexFor(unsigned int tablelength, unsigned int hashvalue) {
+ return (hashvalue % tablelength);
+};
+
+/* Only works if tablelength == 2^N */
+/*static inline unsigned int
+indexFor(unsigned int tablelength, unsigned int hashvalue)
+{
+ return (hashvalue & (tablelength - 1u));
+}
+*/
+
+/*****************************************************************************/
+#define freekey(X) free(X)
+/*define freekey(X) ; */
+
+
+/*****************************************************************************/
+
+#endif /* __HASHTABLE_PRIVATE_CWC22_H__*/
+
+/*
+ * Copyright (c) 2002, Christopher Clark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
diff --git a/src/c/src/load_gen.c b/src/c/src/load_gen.c
new file mode 100644
index 0000000..546d5d1
--- /dev/null
+++ b/src/c/src/load_gen.c
@@ -0,0 +1,282 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+#include "zookeeper_log.h"
+#include
+#ifdef THREADED
+#include
+#endif
+#include
+#include
+
+static zhandle_t *zh;
+
+static int shutdownThisThing=0;
+
+// *****************************************************************************
+//
+static pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;
+
+static pthread_cond_t counterCond=PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t counterLock=PTHREAD_MUTEX_INITIALIZER;
+static int counter;
+
+
+
+void ensureConnected(){
+ pthread_mutex_lock(&lock);
+ while (zoo_state(zh)!=ZOO_CONNECTED_STATE) {
+ pthread_cond_wait(&cond,&lock);
+ }
+ pthread_mutex_unlock(&lock);
+}
+
+void incCounter(int delta){
+ pthread_mutex_lock(&counterLock);
+ counter+=delta;
+ pthread_cond_broadcast(&counterCond);
+ pthread_mutex_unlock(&counterLock);
+
+}
+void setCounter(int cnt){
+ pthread_mutex_lock(&counterLock);
+ counter=cnt;
+ pthread_cond_broadcast(&counterCond);
+ pthread_mutex_unlock(&counterLock);
+
+}
+void waitCounter(){
+ pthread_mutex_lock(&counterLock);
+ while (counter>0) {
+ pthread_cond_wait(&counterCond,&counterLock);
+ }
+ pthread_mutex_unlock(&counterLock);
+}
+
+void listener(zhandle_t *zzh, int type, int state, const char *path,void* ctx) {
+ if(type == ZOO_SESSION_EVENT){
+ if(state == ZOO_CONNECTED_STATE){
+ pthread_mutex_lock(&lock);
+ pthread_cond_broadcast(&cond);
+ pthread_mutex_unlock(&lock);
+ }
+ setCounter(0);
+ }
+}
+
+void create_completion(int rc, const char *name, const void *data) {
+ incCounter(-1);
+ if(rc!=ZOK){
+ LOG_ERROR(("Failed to create a node rc=%d",rc));
+ }
+}
+
+int doCreateNodes(const char* root, int count){
+ char nodeName[1024];
+ int i;
+ for(i=0; idata) {
+ int32_t i;
+ for(i=0;icount; i++) {
+ free(v->data[i]);
+ }
+ free(v->data);
+ v->data = 0;
+ }
+ return 0;
+}
+
+static int deletedCounter;
+
+int recursiveDelete(const char* root){
+ struct String_vector children;
+ int i;
+ int rc=zoo_get_children(zh,root,0,&children);
+ if(rc!=ZNONODE){
+ if(rc!=ZOK){
+ LOG_ERROR(("Failed to get children of %s, rc=%d",root,rc));
+ return rc;
+ }
+ for(i=0;i
+#include
+#include
+#include
+#include
+#include
+
+#ifndef WIN32
+#include
+#include
+#include
+#include
+#endif
+
+int zoo_lock_auth(zhandle_t *zh)
+{
+ return pthread_mutex_lock(&zh->auth_h.lock);
+}
+int zoo_unlock_auth(zhandle_t *zh)
+{
+ return pthread_mutex_unlock(&zh->auth_h.lock);
+}
+int lock_buffer_list(buffer_head_t *l)
+{
+ return pthread_mutex_lock(&l->lock);
+}
+int unlock_buffer_list(buffer_head_t *l)
+{
+ return pthread_mutex_unlock(&l->lock);
+}
+int lock_completion_list(completion_head_t *l)
+{
+ return pthread_mutex_lock(&l->lock);
+}
+int unlock_completion_list(completion_head_t *l)
+{
+ pthread_cond_broadcast(&l->cond);
+ return pthread_mutex_unlock(&l->lock);
+}
+struct sync_completion *alloc_sync_completion(void)
+{
+ struct sync_completion *sc = (struct sync_completion*)calloc(1, sizeof(struct sync_completion));
+ if (sc) {
+ pthread_cond_init(&sc->cond, 0);
+ pthread_mutex_init(&sc->lock, 0);
+ }
+ return sc;
+}
+int wait_sync_completion(struct sync_completion *sc)
+{
+ pthread_mutex_lock(&sc->lock);
+ while (!sc->complete) {
+ pthread_cond_wait(&sc->cond, &sc->lock);
+ }
+ pthread_mutex_unlock(&sc->lock);
+ return 0;
+}
+
+void free_sync_completion(struct sync_completion *sc)
+{
+ if (sc) {
+ pthread_mutex_destroy(&sc->lock);
+ pthread_cond_destroy(&sc->cond);
+ free(sc);
+ }
+}
+
+void notify_sync_completion(struct sync_completion *sc)
+{
+ pthread_mutex_lock(&sc->lock);
+ sc->complete = 1;
+ pthread_cond_broadcast(&sc->cond);
+ pthread_mutex_unlock(&sc->lock);
+}
+
+int process_async(int outstanding_sync)
+{
+ return 0;
+}
+
+#ifdef WIN32
+unsigned __stdcall do_io( void * );
+unsigned __stdcall do_completion( void * );
+
+int handle_error(SOCKET sock, char* message)
+{
+ LOG_ERROR(("%s. %d",message, WSAGetLastError()));
+ closesocket (sock);
+ return -1;
+}
+
+//--create socket pair for interupting selects.
+int create_socket_pair(SOCKET fds[2])
+{
+ struct sockaddr_in inaddr;
+ struct sockaddr addr;
+ int yes=1;
+ int len=0;
+
+ SOCKET lst=socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);
+ if (lst == INVALID_SOCKET ){
+ LOG_ERROR(("Error creating socket. %d",WSAGetLastError()));
+ return -1;
+ }
+ memset(&inaddr, 0, sizeof(inaddr));
+ memset(&addr, 0, sizeof(addr));
+ inaddr.sin_family = AF_INET;
+ inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ inaddr.sin_port = 0; //--system assigns the port
+
+ if ( setsockopt(lst,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(yes)) == SOCKET_ERROR ) {
+ return handle_error(lst,"Error trying to set socket option.");
+ }
+ if (bind(lst,(struct sockaddr *)&inaddr,sizeof(inaddr)) == SOCKET_ERROR){
+ return handle_error(lst,"Error trying to bind socket.");
+ }
+ if (listen(lst,1) == SOCKET_ERROR){
+ return handle_error(lst,"Error trying to listen on socket.");
+ }
+ len=sizeof(inaddr);
+ getsockname(lst, &addr,&len);
+ fds[0]=socket(AF_INET, SOCK_STREAM,0);
+ if (connect(fds[0],&addr,len) == SOCKET_ERROR){
+ return handle_error(lst, "Error while connecting to socket.");
+ }
+ if ((fds[1]=accept(lst,0,0)) == INVALID_SOCKET){
+ closesocket(fds[0]);
+ return handle_error(lst, "Error while accepting socket connection.");
+ }
+ closesocket(lst);
+ return 0;
+}
+#else
+void *do_io(void *);
+void *do_completion(void *);
+#endif
+
+
+int wakeup_io_thread(zhandle_t *zh);
+
+#ifdef WIN32
+static int set_nonblock(SOCKET fd){
+ ULONG nonblocking_flag = 1;
+ if (ioctlsocket(fd, FIONBIO, &nonblocking_flag) == 0)
+ return 1;
+ else
+ return -1;
+}
+#else
+static int set_nonblock(int fd){
+ long l = fcntl(fd, F_GETFL);
+ if(l & O_NONBLOCK) return 0;
+ return fcntl(fd, F_SETFL, l | O_NONBLOCK);
+}
+#endif
+
+void wait_for_others(zhandle_t* zh)
+{
+ struct adaptor_threads* adaptor=zh->adaptor_priv;
+ pthread_mutex_lock(&adaptor->lock);
+ while(adaptor->threadsToWait>0)
+ pthread_cond_wait(&adaptor->cond,&adaptor->lock);
+ pthread_mutex_unlock(&adaptor->lock);
+}
+
+void notify_thread_ready(zhandle_t* zh)
+{
+ struct adaptor_threads* adaptor=zh->adaptor_priv;
+ pthread_mutex_lock(&adaptor->lock);
+ adaptor->threadsToWait--;
+ pthread_cond_broadcast(&adaptor->cond);
+ while(adaptor->threadsToWait>0)
+ pthread_cond_wait(&adaptor->cond,&adaptor->lock);
+ pthread_mutex_unlock(&adaptor->lock);
+}
+
+
+void start_threads(zhandle_t* zh)
+{
+ int rc = 0;
+ struct adaptor_threads* adaptor=zh->adaptor_priv;
+ pthread_cond_init(&adaptor->cond,0);
+ pthread_mutex_init(&adaptor->lock,0);
+ adaptor->threadsToWait=2; // wait for 2 threads before opening the barrier
+
+ // use api_prolog() to make sure zhandle doesn't get destroyed
+ // while initialization is in progress
+ api_prolog(zh);
+ LOG_DEBUG(("starting threads..."));
+ rc=pthread_create(&adaptor->io, 0, do_io, zh);
+ assert("pthread_create() failed for the IO thread"&&!rc);
+ rc=pthread_create(&adaptor->completion, 0, do_completion, zh);
+ assert("pthread_create() failed for the completion thread"&&!rc);
+ wait_for_others(zh);
+ api_epilog(zh, 0);
+}
+
+int adaptor_init(zhandle_t *zh)
+{
+ pthread_mutexattr_t recursive_mx_attr;
+ struct adaptor_threads *adaptor_threads = calloc(1, sizeof(*adaptor_threads));
+ if (!adaptor_threads) {
+ LOG_ERROR(("Out of memory"));
+ return -1;
+ }
+
+ /* We use a pipe for interrupting select() in unix/sol and socketpair in windows. */
+#ifdef WIN32
+ if (create_socket_pair(adaptor_threads->self_pipe) == -1){
+ LOG_ERROR(("Can't make a socket."));
+#else
+ if(pipe(adaptor_threads->self_pipe)==-1) {
+ LOG_ERROR(("Can't make a pipe %d",errno));
+#endif
+ free(adaptor_threads);
+ return -1;
+ }
+ set_nonblock(adaptor_threads->self_pipe[1]);
+ set_nonblock(adaptor_threads->self_pipe[0]);
+
+ pthread_mutex_init(&zh->auth_h.lock,0);
+
+ zh->adaptor_priv = adaptor_threads;
+ pthread_mutex_init(&zh->to_process.lock,0);
+ pthread_mutex_init(&adaptor_threads->zh_lock,0);
+ // to_send must be recursive mutex
+ pthread_mutexattr_init(&recursive_mx_attr);
+ pthread_mutexattr_settype(&recursive_mx_attr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&zh->to_send.lock,&recursive_mx_attr);
+ pthread_mutexattr_destroy(&recursive_mx_attr);
+
+ pthread_mutex_init(&zh->sent_requests.lock,0);
+ pthread_cond_init(&zh->sent_requests.cond,0);
+ pthread_mutex_init(&zh->completions_to_process.lock,0);
+ pthread_cond_init(&zh->completions_to_process.cond,0);
+ start_threads(zh);
+ return 0;
+}
+
+void adaptor_finish(zhandle_t *zh)
+{
+ struct adaptor_threads *adaptor_threads;
+ // make sure zh doesn't get destroyed until after we're done here
+ api_prolog(zh);
+ adaptor_threads = zh->adaptor_priv;
+ if(adaptor_threads==0) {
+ api_epilog(zh,0);
+ return;
+ }
+
+ if(!pthread_equal(adaptor_threads->io,pthread_self())){
+ wakeup_io_thread(zh);
+ pthread_join(adaptor_threads->io, 0);
+ }else
+ pthread_detach(adaptor_threads->io);
+
+ if(!pthread_equal(adaptor_threads->completion,pthread_self())){
+ pthread_mutex_lock(&zh->completions_to_process.lock);
+ pthread_cond_broadcast(&zh->completions_to_process.cond);
+ pthread_mutex_unlock(&zh->completions_to_process.lock);
+ pthread_join(adaptor_threads->completion, 0);
+ }else
+ pthread_detach(adaptor_threads->completion);
+
+ api_epilog(zh,0);
+}
+
+void adaptor_destroy(zhandle_t *zh)
+{
+ struct adaptor_threads *adaptor = zh->adaptor_priv;
+ if(adaptor==0) return;
+
+ pthread_cond_destroy(&adaptor->cond);
+ pthread_mutex_destroy(&adaptor->lock);
+ pthread_mutex_destroy(&zh->to_process.lock);
+ pthread_mutex_destroy(&zh->to_send.lock);
+ pthread_mutex_destroy(&zh->sent_requests.lock);
+ pthread_cond_destroy(&zh->sent_requests.cond);
+ pthread_mutex_destroy(&zh->completions_to_process.lock);
+ pthread_cond_destroy(&zh->completions_to_process.cond);
+ pthread_mutex_destroy(&adaptor->zh_lock);
+
+ pthread_mutex_destroy(&zh->auth_h.lock);
+
+ close(adaptor->self_pipe[0]);
+ close(adaptor->self_pipe[1]);
+ free(adaptor);
+ zh->adaptor_priv=0;
+}
+
+int wakeup_io_thread(zhandle_t *zh)
+{
+ struct adaptor_threads *adaptor_threads = zh->adaptor_priv;
+ char c=0;
+#ifndef WIN32
+ return write(adaptor_threads->self_pipe[1],&c,1)==1? ZOK: ZSYSTEMERROR;
+#else
+ return send(adaptor_threads->self_pipe[1], &c, 1, 0)==1? ZOK: ZSYSTEMERROR;
+#endif
+}
+
+int adaptor_send_queue(zhandle_t *zh, int timeout)
+{
+ if(!zh->close_requested)
+ return wakeup_io_thread(zh);
+ // don't rely on the IO thread to send the messages if the app has
+ // requested to close
+ return flush_send_queue(zh, timeout);
+}
+
+/* These two are declared here because we will run the event loop
+ * and not the client */
+#ifdef WIN32
+int zookeeper_interest(zhandle_t *zh, SOCKET *fd, int *interest,
+ struct timeval *tv);
+#else
+int zookeeper_interest(zhandle_t *zh, int *fd, int *interest,
+ struct timeval *tv);
+#endif
+int zookeeper_process(zhandle_t *zh, int events);
+
+#ifdef WIN32
+unsigned __stdcall do_io( void * v)
+#else
+void *do_io(void *v)
+#endif
+{
+ zhandle_t *zh = (zhandle_t*)v;
+#ifndef WIN32
+ struct pollfd fds[2];
+ struct adaptor_threads *adaptor_threads = zh->adaptor_priv;
+
+ api_prolog(zh);
+ notify_thread_ready(zh);
+ LOG_DEBUG(("started IO thread"));
+ fds[0].fd=adaptor_threads->self_pipe[0];
+ fds[0].events=POLLIN;
+ while(!zh->close_requested) {
+ struct timeval tv;
+ int fd;
+ int interest;
+ int timeout;
+ int maxfd=1;
+
+ zookeeper_interest(zh, &fd, &interest, &tv);
+ if (fd != -1) {
+ fds[1].fd=fd;
+ fds[1].events=(interest&ZOOKEEPER_READ)?POLLIN:0;
+ fds[1].events|=(interest&ZOOKEEPER_WRITE)?POLLOUT:0;
+ maxfd=2;
+ }
+ timeout=tv.tv_sec * 1000 + (tv.tv_usec/1000);
+
+ poll(fds,maxfd,timeout);
+ if (fd != -1) {
+ interest=(fds[1].revents&POLLIN)?ZOOKEEPER_READ:0;
+ interest|=((fds[1].revents&POLLOUT)||(fds[1].revents&POLLHUP))?ZOOKEEPER_WRITE:0;
+ }
+ if(fds[0].revents&POLLIN){
+ // flush the pipe
+ char b[128];
+ while(read(adaptor_threads->self_pipe[0],b,sizeof(b))==sizeof(b)){}
+ }
+#else
+ fd_set rfds, wfds, efds;
+ struct adaptor_threads *adaptor_threads = zh->adaptor_priv;
+ api_prolog(zh);
+ notify_thread_ready(zh);
+ LOG_DEBUG(("started IO thread"));
+ FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds);
+ while(!zh->close_requested) {
+ struct timeval tv;
+ SOCKET fd;
+ SOCKET maxfd=adaptor_threads->self_pipe[0];
+ int interest;
+ int rc;
+
+ zookeeper_interest(zh, &fd, &interest, &tv);
+ if (fd != -1) {
+ if (interest&ZOOKEEPER_READ) {
+ FD_SET(fd, &rfds);
+ } else {
+ FD_CLR(fd, &rfds);
+ }
+ if (interest&ZOOKEEPER_WRITE) {
+ FD_SET(fd, &wfds);
+ } else {
+ FD_CLR(fd, &wfds);
+ }
+ }
+ FD_SET( adaptor_threads->self_pipe[0] ,&rfds );
+ rc = select((int)maxfd, &rfds, &wfds, &efds, &tv);
+ if (fd != -1)
+ {
+ interest = (FD_ISSET(fd, &rfds))? ZOOKEEPER_READ:0;
+ interest|= (FD_ISSET(fd, &wfds))? ZOOKEEPER_WRITE:0;
+ }
+
+ if (FD_ISSET(adaptor_threads->self_pipe[0], &rfds)){
+ // flush the pipe/socket
+ char b[128];
+ while(recv(adaptor_threads->self_pipe[0],b,sizeof(b), 0)==sizeof(b)){}
+ }
+#endif
+ // dispatch zookeeper events
+ zookeeper_process(zh, interest);
+ // check the current state of the zhandle and terminate
+ // if it is_unrecoverable()
+ if(is_unrecoverable(zh))
+ break;
+ }
+ api_epilog(zh, 0);
+ LOG_DEBUG(("IO thread terminated"));
+ return 0;
+}
+
+#ifdef WIN32
+unsigned __stdcall do_completion( void * v)
+#else
+void *do_completion(void *v)
+#endif
+{
+ zhandle_t *zh = v;
+ api_prolog(zh);
+ notify_thread_ready(zh);
+ LOG_DEBUG(("started completion thread"));
+ while(!zh->close_requested) {
+ pthread_mutex_lock(&zh->completions_to_process.lock);
+ while(!zh->completions_to_process.head && !zh->close_requested) {
+ pthread_cond_wait(&zh->completions_to_process.cond, &zh->completions_to_process.lock);
+ }
+ pthread_mutex_unlock(&zh->completions_to_process.lock);
+ process_completions(zh);
+ }
+ api_epilog(zh, 0);
+ LOG_DEBUG(("completion thread terminated"));
+ return 0;
+}
+
+int32_t inc_ref_counter(zhandle_t* zh,int i)
+{
+ int incr=(i<0?-1:(i>0?1:0));
+ // fetch_and_add implements atomic post-increment
+ int v=fetch_and_add(&zh->ref_counter,incr);
+ // inc_ref_counter wants pre-increment
+ v+=incr; // simulate pre-increment
+ return v;
+}
+
+int32_t fetch_and_add(volatile int32_t* operand, int incr)
+{
+#ifndef WIN32
+ return __sync_fetch_and_add(operand, incr);
+#else
+ return InterlockedExchangeAdd(operand, incr);
+#endif
+}
+
+// make sure the static xid is initialized before any threads started
+__attribute__((constructor)) int32_t get_xid()
+{
+ static int32_t xid = -1;
+ if (xid == -1) {
+ xid = time(0);
+ }
+ return fetch_and_add(&xid,1);
+}
+
+int enter_critical(zhandle_t* zh)
+{
+ struct adaptor_threads *adaptor = zh->adaptor_priv;
+ if (adaptor) {
+ return pthread_mutex_lock(&adaptor->zh_lock);
+ } else {
+ return 0;
+ }
+}
+
+int leave_critical(zhandle_t* zh)
+{
+ struct adaptor_threads *adaptor = zh->adaptor_priv;
+ if (adaptor) {
+ return pthread_mutex_unlock(&adaptor->zh_lock);
+ } else {
+ return 0;
+ }
+}
diff --git a/src/c/src/recordio.c b/src/c/src/recordio.c
new file mode 100644
index 0000000..0fcf48e
--- /dev/null
+++ b/src/c/src/recordio.c
@@ -0,0 +1,366 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+#include
+#include
+#include
+#include
+#ifndef WIN32
+#include
+#else
+#include /* for _htonl and _ntohl */
+#endif
+
+void deallocate_String(char **s)
+{
+ if (*s)
+ free(*s);
+ *s = 0;
+}
+
+void deallocate_Buffer(struct buffer *b)
+{
+ if (b->buff)
+ free(b->buff);
+ b->buff = 0;
+}
+
+struct buff_struct {
+ int32_t len;
+ int32_t off;
+ char *buffer;
+};
+
+static int resize_buffer(struct buff_struct *s, int newlen)
+{
+ char *buffer= NULL;
+ while (s->len < newlen) {
+ s->len *= 2;
+ }
+ buffer = (char*)realloc(s->buffer, s->len);
+ if (!buffer) {
+ s->buffer = 0;
+ return -ENOMEM;
+ }
+ s->buffer = buffer;
+ return 0;
+}
+
+int oa_start_record(struct oarchive *oa, const char *tag)
+{
+ return 0;
+}
+int oa_end_record(struct oarchive *oa, const char *tag)
+{
+ return 0;
+}
+int oa_serialize_int(struct oarchive *oa, const char *tag, const int32_t *d)
+{
+ struct buff_struct *priv = oa->priv;
+ int32_t i = htonl(*d);
+ if ((priv->len - priv->off) < sizeof(i)) {
+ int rc = resize_buffer(priv, priv->len + sizeof(i));
+ if (rc < 0) return rc;
+ }
+ memcpy(priv->buffer+priv->off, &i, sizeof(i));
+ priv->off+=sizeof(i);
+ return 0;
+}
+int64_t zoo_htonll(int64_t v)
+{
+ int i = 0;
+ char *s = (char *)&v;
+ if (htonl(1) == 1) {
+ return v;
+ }
+ for (i = 0; i < 4; i++) {
+ int tmp = s[i];
+ s[i] = s[8-i-1];
+ s[8-i-1] = tmp;
+ }
+
+ return v;
+}
+
+int oa_serialize_long(struct oarchive *oa, const char *tag, const int64_t *d)
+{
+ const int64_t i = zoo_htonll(*d);
+ struct buff_struct *priv = oa->priv;
+ if ((priv->len - priv->off) < sizeof(i)) {
+ int rc = resize_buffer(priv, priv->len + sizeof(i));
+ if (rc < 0) return rc;
+ }
+ memcpy(priv->buffer+priv->off, &i, sizeof(i));
+ priv->off+=sizeof(i);
+ return 0;
+}
+int oa_start_vector(struct oarchive *oa, const char *tag, const int32_t *count)
+{
+ return oa_serialize_int(oa, tag, count);
+}
+int oa_end_vector(struct oarchive *oa, const char *tag)
+{
+ return 0;
+}
+int oa_serialize_bool(struct oarchive *oa, const char *name, const int32_t *i)
+{
+ //return oa_serialize_int(oa, name, i);
+ struct buff_struct *priv = oa->priv;
+ if ((priv->len - priv->off) < 1) {
+ int rc = resize_buffer(priv, priv->len + 1);
+ if (rc < 0)
+ return rc;
+ }
+ priv->buffer[priv->off] = (*i == 0 ? '\0' : '\1');
+ priv->off++;
+ return 0;
+}
+static const int32_t negone = -1;
+int oa_serialize_buffer(struct oarchive *oa, const char *name,
+ const struct buffer *b)
+{
+ struct buff_struct *priv = oa->priv;
+ int rc;
+ if (!b) {
+ return oa_serialize_int(oa, "len", &negone);
+ }
+ rc = oa_serialize_int(oa, "len", &b->len);
+ if (rc < 0)
+ return rc;
+ // this means a buffer of NUll
+ // with size of -1. This is
+ // waht we use in java serialization for NULL
+ if (b->len == -1) {
+ return rc;
+ }
+ if ((priv->len - priv->off) < b->len) {
+ rc = resize_buffer(priv, priv->len + b->len);
+ if (rc < 0)
+ return rc;
+ }
+ memcpy(priv->buffer+priv->off, b->buff, b->len);
+ priv->off += b->len;
+ return 0;
+}
+int oa_serialize_string(struct oarchive *oa, const char *name, char **s)
+{
+ struct buff_struct *priv = oa->priv;
+ int32_t len;
+ int rc;
+ if (!*s) {
+ oa_serialize_int(oa, "len", &negone);
+ return 0;
+ }
+ len = strlen(*s);
+ rc = oa_serialize_int(oa, "len", &len);
+ if (rc < 0)
+ return rc;
+ if ((priv->len - priv->off) < len) {
+ rc = resize_buffer(priv, priv->len + len);
+ if (rc < 0)
+ return rc;
+ }
+ memcpy(priv->buffer+priv->off, *s, len);
+ priv->off += len;
+ return 0;
+}
+int ia_start_record(struct iarchive *ia, const char *tag)
+{
+ return 0;
+}
+int ia_end_record(struct iarchive *ia, const char *tag)
+{
+ return 0;
+}
+int ia_deserialize_int(struct iarchive *ia, const char *tag, int32_t *count)
+{
+ struct buff_struct *priv = ia->priv;
+ if ((priv->len - priv->off) < sizeof(*count)) {
+ return -E2BIG;
+ }
+ memcpy(count, priv->buffer+priv->off, sizeof(*count));
+ priv->off+=sizeof(*count);
+ *count = ntohl(*count);
+ return 0;
+}
+
+int ia_deserialize_long(struct iarchive *ia, const char *tag, int64_t *count)
+{
+ struct buff_struct *priv = ia->priv;
+ int64_t v = 0;
+ if ((priv->len - priv->off) < sizeof(*count)) {
+ return -E2BIG;
+ }
+ memcpy(count, priv->buffer+priv->off, sizeof(*count));
+ priv->off+=sizeof(*count);
+ v = zoo_htonll(*count); // htonll and ntohll do the same
+ *count = v;
+ return 0;
+}
+int ia_start_vector(struct iarchive *ia, const char *tag, int32_t *count)
+{
+ return ia_deserialize_int(ia, tag, count);
+}
+int ia_end_vector(struct iarchive *ia, const char *tag)
+{
+ return 0;
+}
+int ia_deserialize_bool(struct iarchive *ia, const char *name, int32_t *v)
+{
+ struct buff_struct *priv = ia->priv;
+ //fprintf(stderr, "Deserializing bool %d\n", priv->off);
+ //return ia_deserialize_int(ia, name, v);
+ if ((priv->len - priv->off) < 1) {
+ return -E2BIG;
+ }
+ *v = priv->buffer[priv->off];
+ priv->off+=1;
+ //fprintf(stderr, "Deserializing bool end %d\n", priv->off);
+ return 0;
+}
+int ia_deserialize_buffer(struct iarchive *ia, const char *name,
+ struct buffer *b)
+{
+ struct buff_struct *priv = ia->priv;
+ int rc = ia_deserialize_int(ia, "len", &b->len);
+ if (rc < 0)
+ return rc;
+ if ((priv->len - priv->off) < b->len) {
+ return -E2BIG;
+ }
+ // set the buffer to null
+ if (b->len == -1) {
+ b->buff = NULL;
+ return rc;
+ }
+ b->buff = malloc(b->len);
+ if (!b->buff) {
+ return -ENOMEM;
+ }
+ memcpy(b->buff, priv->buffer+priv->off, b->len);
+ priv->off += b->len;
+ return 0;
+}
+int ia_deserialize_string(struct iarchive *ia, const char *name, char **s)
+{
+ struct buff_struct *priv = ia->priv;
+ int32_t len;
+ int rc = ia_deserialize_int(ia, "len", &len);
+ if (rc < 0)
+ return rc;
+ if ((priv->len - priv->off) < len) {
+ return -E2BIG;
+ }
+ if (len < 0) {
+ return -EINVAL;
+ }
+ *s = malloc(len+1);
+ if (!*s) {
+ return -ENOMEM;
+ }
+ memcpy(*s, priv->buffer+priv->off, len);
+ (*s)[len] = '\0';
+ priv->off += len;
+ return 0;
+}
+
+static struct iarchive ia_default = { STRUCT_INITIALIZER (start_record ,ia_start_record),
+ STRUCT_INITIALIZER (end_record ,ia_end_record), STRUCT_INITIALIZER (start_vector , ia_start_vector),
+ STRUCT_INITIALIZER (end_vector ,ia_end_vector), STRUCT_INITIALIZER (deserialize_Bool , ia_deserialize_bool),
+ STRUCT_INITIALIZER (deserialize_Int ,ia_deserialize_int),
+ STRUCT_INITIALIZER (deserialize_Long , ia_deserialize_long) ,
+ STRUCT_INITIALIZER (deserialize_Buffer, ia_deserialize_buffer),
+ STRUCT_INITIALIZER (deserialize_String, ia_deserialize_string) };
+
+static struct oarchive oa_default = { STRUCT_INITIALIZER (start_record , oa_start_record),
+ STRUCT_INITIALIZER (end_record , oa_end_record), STRUCT_INITIALIZER (start_vector , oa_start_vector),
+ STRUCT_INITIALIZER (end_vector , oa_end_vector), STRUCT_INITIALIZER (serialize_Bool , oa_serialize_bool),
+ STRUCT_INITIALIZER (serialize_Int , oa_serialize_int),
+ STRUCT_INITIALIZER (serialize_Long , oa_serialize_long) ,
+ STRUCT_INITIALIZER (serialize_Buffer , oa_serialize_buffer),
+ STRUCT_INITIALIZER (serialize_String , oa_serialize_string) };
+
+struct iarchive *create_buffer_iarchive(char *buffer, int len)
+{
+ struct iarchive *ia;
+ struct buff_struct *buff;
+ ia = malloc(sizeof(*ia));
+ if (!ia) return 0;
+ buff = malloc(sizeof(struct buff_struct));
+ if (!buff) {
+ free(ia);
+ return 0;
+ }
+ *ia = ia_default;
+ buff->off = 0;
+ buff->buffer = buffer;
+ buff->len = len;
+ ia->priv = buff;
+ return ia;
+}
+
+struct oarchive *create_buffer_oarchive()
+{
+ struct oarchive *oa;
+ struct buff_struct *buff;
+ oa = malloc(sizeof(*oa));
+ if (!oa) return 0;
+ buff = malloc(sizeof(struct buff_struct));
+ if (!buff) {
+ free(oa);
+ return 0;
+ }
+ *oa = oa_default;
+ buff->off = 0;
+ buff->buffer = malloc(128);
+ buff->len = 128;
+ oa->priv = buff;
+ return oa;
+}
+
+void close_buffer_iarchive(struct iarchive **ia)
+{
+ free((*ia)->priv);
+ free(*ia);
+ *ia = 0;
+}
+
+void close_buffer_oarchive(struct oarchive **oa, int free_buffer)
+{
+ if (free_buffer) {
+ struct buff_struct *buff = (struct buff_struct *)(*oa)->priv;
+ if (buff->buffer) {
+ free(buff->buffer);
+ }
+ }
+ free((*oa)->priv);
+ free(*oa);
+ *oa = 0;
+}
+
+char *get_buffer(struct oarchive *oa)
+{
+ struct buff_struct *buff = oa->priv;
+ return buff->buffer;
+}
+int get_buffer_len(struct oarchive *oa)
+{
+ struct buff_struct *buff = oa->priv;
+ return buff->off;
+}
diff --git a/src/c/src/st_adaptor.c b/src/c/src/st_adaptor.c
new file mode 100644
index 0000000..23573b3
--- /dev/null
+++ b/src/c/src/st_adaptor.c
@@ -0,0 +1,113 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 DLL_EXPORT
+# define USE_STATIC_LIB
+#endif
+
+#include "zk_adaptor.h"
+#include
+#include
+
+int zoo_lock_auth(zhandle_t *zh)
+{
+ return 0;
+}
+int zoo_unlock_auth(zhandle_t *zh)
+{
+ return 0;
+}
+int lock_buffer_list(buffer_head_t *l)
+{
+ return 0;
+}
+int unlock_buffer_list(buffer_head_t *l)
+{
+ return 0;
+}
+int lock_completion_list(completion_head_t *l)
+{
+ return 0;
+}
+int unlock_completion_list(completion_head_t *l)
+{
+ return 0;
+}
+struct sync_completion *alloc_sync_completion(void)
+{
+ return (struct sync_completion*)calloc(1, sizeof(struct sync_completion));
+}
+int wait_sync_completion(struct sync_completion *sc)
+{
+ return 0;
+}
+
+void free_sync_completion(struct sync_completion *sc)
+{
+ free(sc);
+}
+
+void notify_sync_completion(struct sync_completion *sc)
+{
+}
+
+int process_async(int outstanding_sync)
+{
+ return outstanding_sync == 0;
+}
+
+int adaptor_init(zhandle_t *zh)
+{
+ return 0;
+}
+
+void adaptor_finish(zhandle_t *zh){}
+
+void adaptor_destroy(zhandle_t *zh){}
+
+int flush_send_queue(zhandle_t *, int);
+
+int adaptor_send_queue(zhandle_t *zh, int timeout)
+{
+ return flush_send_queue(zh, timeout);
+}
+
+int32_t inc_ref_counter(zhandle_t* zh,int i)
+{
+ zh->ref_counter+=(i<0?-1:(i>0?1:0));
+ return zh->ref_counter;
+}
+
+int32_t get_xid()
+{
+ static int32_t xid = -1;
+ if (xid == -1) {
+ xid = time(0);
+ }
+ return xid++;
+}
+
+int enter_critical(zhandle_t* zh)
+{
+ return 0;
+}
+
+int leave_critical(zhandle_t* zh)
+{
+ return 0;
+}
diff --git a/src/c/src/winport.c b/src/c/src/winport.c
new file mode 100644
index 0000000..3592ea1
--- /dev/null
+++ b/src/c/src/winport.c
@@ -0,0 +1,302 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifdef WIN32
+#include "winport.h"
+#include
+#include /* for int64_t */
+#include /* must always be included before ws2tcpip.h */
+#include /* for SOCKET */
+
+int pthread_mutex_lock(pthread_mutex_t* _mutex ){
+ int rc = WaitForSingleObject( *_mutex, // handle to mutex
+ INFINITE); // no time-out interval
+ return ((rc == WAIT_OBJECT_0) ? 0: rc);
+}
+
+int pthread_mutex_unlock( pthread_mutex_t* _mutex ){
+ int rc = ReleaseMutex(*_mutex);
+ return ((rc != 0)? 0: GetLastError());
+}
+
+int pthread_mutex_init(pthread_mutex_t* _mutex, void* ignoredAttr){
+ //use CreateMutex as we are using the HANDLES in pthread_cond
+ *_mutex = CreateMutex(
+ NULL, // default security attributes
+ FALSE, // initially not owned
+ NULL); // unnamed mutex
+ return ((*_mutex == NULL) ? GetLastError() : 0);
+}
+
+int pthread_mutex_destroy(pthread_mutex_t* _mutex)
+{
+ int rc = CloseHandle(*_mutex);
+ return ((rc != 0)? 0: GetLastError());
+}
+
+int pthread_create(pthread_t *thread, const pthread_attr_t *attr, unsigned (__stdcall* start_routine)(void* a), void *arg)
+{
+ int _intThreadId;
+ (*thread).thread_handle = (HANDLE)_beginthreadex( NULL, 0, start_routine , arg, 0, (unsigned int*)&_intThreadId );
+ (*thread).thread_id = _intThreadId;
+ return (((*thread).thread_handle == 0 ) ? errno : 0 );
+}
+
+
+int pthread_equal(pthread_t t1, pthread_t t2){
+//Is there a better way to do this? GetThreadId(handle) is only supported Windows 2003 n above.
+ return ((t1.thread_id == t2.thread_id) ? 1:0);
+}
+
+pthread_t pthread_self(){
+ pthread_t thread_self;
+ thread_self.thread_handle = GetCurrentThread();
+ thread_self.thread_id = GetCurrentThreadId();
+ return thread_self;
+}
+
+int pthread_join(pthread_t _thread, void** ignore)
+{
+ int rc = WaitForSingleObject( _thread.thread_handle, INFINITE );
+ return ((rc == WAIT_OBJECT_0) ? 0: rc);
+}
+
+int pthread_detach(pthread_t _thread)
+{
+ int rc = CloseHandle(_thread.thread_handle) ;
+ return (rc != 0) ? 0: GetLastError();
+}
+
+void pthread_mutexattr_init(pthread_mutexattr_t* ignore){}
+void pthread_mutexattr_settype(pthread_mutexattr_t* ingore_attr, int ignore){}
+void pthread_mutexattr_destroy(pthread_mutexattr_t* ignore_attr){}
+
+int
+pthread_cond_init (pthread_cond_t *cv,
+ const pthread_condattr_t * ignore)
+{
+ cv->waiters_count_ = 0;
+ cv->was_broadcast_ = 0;
+ cv->sema_ = CreateSemaphore (NULL, // no security
+ 0, // initially 0
+ 0x7fffffff, // max count
+ NULL); // unnamed
+ if (cv->sema_ == NULL )
+ return GetLastError();
+ InitializeCriticalSection (&cv->waiters_count_lock_);
+ cv->waiters_done_ = CreateEvent (NULL, // no security
+ FALSE, // auto-reset
+ FALSE, // non-signaled initially
+ NULL); // unnamed
+ return (cv->waiters_done_ == NULL) ? GetLastError() : 0;
+
+}
+
+
+int pthread_cond_destroy(pthread_cond_t *cond)
+{
+ CloseHandle( cond->sema_);
+ DeleteCriticalSection(&cond->waiters_count_lock_);
+ return (CloseHandle( cond->waiters_done_ ) == 0)? GetLastError(): 0 ;
+}
+
+
+int
+pthread_cond_signal (pthread_cond_t *cv)
+{
+ int have_waiters;
+ EnterCriticalSection (& (cv->waiters_count_lock_));
+ have_waiters = cv->waiters_count_ > 0;
+ LeaveCriticalSection (&cv->waiters_count_lock_);
+
+ // If there aren't any waiters, then this is a no-op.
+ if (have_waiters){
+ return (ReleaseSemaphore (cv->sema_, 1, 0) == 0 ) ? GetLastError() : 0 ;
+ }else
+ return 0;
+}
+
+
+int
+pthread_cond_broadcast (pthread_cond_t *cv)
+{
+ // This is needed to ensure that and are
+ // consistent relative to each other.
+ int have_waiters = 0;
+ EnterCriticalSection (&cv->waiters_count_lock_);
+
+ if (cv->waiters_count_ > 0) {
+ // We are broadcasting, even if there is just one waiter...
+ // Record that we are broadcasting, which helps optimize
+ // for the non-broadcast case.
+ cv->was_broadcast_ = 1;
+ have_waiters = 1;
+ }
+
+ if (have_waiters) {
+ // Wake up all the waiters atomically.
+ ReleaseSemaphore (cv->sema_, cv->waiters_count_, 0);
+
+ LeaveCriticalSection (&cv->waiters_count_lock_);
+
+ // Wait for all the awakened threads to acquire the counting
+ // semaphore.
+ WaitForSingleObject (cv->waiters_done_, INFINITE);
+ // This assignment is okay, even without the held
+ // because no other waiter threads can wake up to access it.
+ cv->was_broadcast_ = 0;
+ }
+ else
+ LeaveCriticalSection (&cv->waiters_count_lock_);
+}
+
+
+int
+pthread_cond_wait (pthread_cond_t *cv,
+ pthread_mutex_t *external_mutex)
+{
+ int last_waiter;
+ // Avoid race conditions.
+ EnterCriticalSection (&cv->waiters_count_lock_);
+ cv->waiters_count_++;
+ LeaveCriticalSection (&cv->waiters_count_lock_);
+
+ // This call atomically releases the mutex and waits on the
+ // semaphore until or
+ // are called by another thread.
+ SignalObjectAndWait (*external_mutex, cv->sema_, INFINITE, FALSE);
+
+ // Reacquire lock to avoid race conditions.
+ EnterCriticalSection (&cv->waiters_count_lock_);
+
+ // We're no longer waiting...
+ cv->waiters_count_--;
+
+ // Check to see if we're the last waiter after .
+ last_waiter = cv->was_broadcast_ && cv->waiters_count_ == 0;
+
+ LeaveCriticalSection (&cv->waiters_count_lock_);
+
+ // If we're the last waiter thread during this particular broadcast
+ // then let all the other threads proceed.
+ if (last_waiter)
+ // This call atomically signals the event and waits until
+ // it can acquire the . This is required to ensure fairness.
+ SignalObjectAndWait (cv->waiters_done_, *external_mutex, INFINITE, FALSE);
+ else
+ // Always regain the external mutex since that's the guarantee we
+ // give to our callers.
+ WaitForSingleObject (*external_mutex, INFINITE);
+}
+
+int pthread_key_create(pthread_key_t *key, void (*destructor)(void *) )
+{
+ int result = 0;
+ pthread_key_t* newkey;
+
+ if ((newkey = (pthread_key_t*) calloc (1, sizeof (pthread_key_t))) == NULL)
+ {
+ result = ENOMEM;
+ }
+ else if ((newkey->key = TlsAlloc ()) == TLS_OUT_OF_INDEXES)
+ {
+ result = EAGAIN;
+ free (newkey);
+ newkey = NULL;
+ }
+ else if (destructor != NULL)
+ {
+ //--we have to store the function pointer for destructor, so that we can call it
+ //--to free up the user allocated storage--
+ newkey->destructor = destructor;
+ }
+ key = newkey;
+ return (result);
+}
+
+int pthread_key_delete(pthread_key_t key)
+{
+ int rc = 0;
+ LPVOID lpvData = TlsGetValue(key.key);
+ rc = TlsFree (key.key);
+ rc = (rc != 0 ) ? 0 : GetLastError();
+ if (key.destructor != NULL && lpvData != 0){
+ key.destructor(lpvData); //we take control of calling destructor, instead of calling it on thread exit.
+ }
+ free (&key);
+ return (rc);
+}
+
+void *pthread_getspecific(pthread_key_t key)
+{
+ LPVOID lpvData = TlsGetValue(key.key);
+ if ((lpvData == 0) && (GetLastError() != ERROR_SUCCESS))
+ return NULL;
+ else
+ return lpvData;
+}
+
+int pthread_setspecific(pthread_key_t key, const void *value)
+{
+ int rc = TlsSetValue (key.key, value);
+ return ((rc != 0 ) ? 0 : GetLastError());
+}
+
+int gettimeofday(struct timeval *tp, void *tzp) {
+ int64_t now = 0;
+ if (tzp != 0) { errno = EINVAL; return -1; }
+ GetSystemTimeAsFileTime( (LPFILETIME)&now );
+ tp->tv_sec = (long)(now / 10000000 - 11644473600LL);
+ tp->tv_usec = (now / 10) % 1000000;
+ return 0;
+}
+
+int close(SOCKET fd) {
+ return closesocket(fd);
+}
+
+int Win32WSAStartup()
+{
+ WORD wVersionRq;
+ WSADATA wsaData;
+ int err;
+
+ wVersionRq = MAKEWORD(2,0);
+ err = WSAStartup(wVersionRq, &wsaData);
+ if (err != 0)
+ return 1;
+
+ // confirm the version information
+ if ((LOBYTE(wsaData.wVersion) != 2) ||
+ (HIBYTE(wsaData.wVersion) != 0))
+ {
+ Win32WSACleanup();
+ return 1;
+ }
+ return 0;
+}
+
+void Win32WSACleanup()
+{
+ WSACleanup();
+}
+
+#endif //WIN32
+
+
+
diff --git a/src/c/src/winport.h b/src/c/src/winport.h
new file mode 100644
index 0000000..da6028c
--- /dev/null
+++ b/src/c/src/winport.h
@@ -0,0 +1,138 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/**
+ * This header file is to port pthread lib , sockets and other utility methods on windows.
+ * Specifically the threads function, mutexes, keys, and socket initialization.
+ */
+
+#ifndef WINPORT_H_
+#define WINPORT_H_
+
+#ifdef WIN32
+#include "winconfig.h"
+
+#define _WINSOCK_DEPRECATED_NO_WARNINGS
+#include /* must always be included before ws2tcpip.h */
+#include /* for struct sock_addr used in zookeeper.h */
+
+/* POSIX names are deprecated, use ISO conformant names instead. */
+#define strdup _strdup
+#define getcwd _getcwd
+#define getpid _getpid
+
+/* Windows "secure" versions of POSIX reentrant functions */
+#define strtok_r strtok_s
+#define localtime_r(a,b) localtime_s(b,a)
+
+/* After this version of MSVC, snprintf became a defined function,
+ and so cannot be redefined, nor can #ifndef be used to guard it. */
+#if ((defined(_MSC_VER) && _MSC_VER < 1900) || !defined(_MSC_VER))
+#define snprintf _snprintf
+#endif
+
+
+#include
+#include
+#include /* for int64_t */
+#include
+#include
+
+
+typedef int ssize_t;
+typedef HANDLE pthread_mutex_t;
+
+struct pthread_t_
+{
+ HANDLE thread_handle;
+ DWORD thread_id;
+};
+
+typedef struct pthread_t_ pthread_t;
+typedef int pthread_mutexattr_t;
+typedef int pthread_condattr_t;
+typedef int pthread_attr_t;
+#define PTHREAD_MUTEX_RECURSIVE 0
+
+int pthread_mutex_lock(pthread_mutex_t* _mutex );
+int pthread_mutex_unlock( pthread_mutex_t* _mutex );
+int pthread_mutex_init(pthread_mutex_t* _mutex, void* ignoredAttr);
+int pthread_mutex_destroy(pthread_mutex_t* _mutex);
+int pthread_create(pthread_t *thread, const pthread_attr_t *attr, unsigned (__stdcall* start_routine)(void* a), void *arg);
+int pthread_equal(pthread_t t1, pthread_t t2);
+pthread_t pthread_self();
+int pthread_join(pthread_t _thread, void** ignore);
+int pthread_detach(pthread_t _thread);
+
+void pthread_mutexattr_init(pthread_mutexattr_t* ignore);
+void pthread_mutexattr_settype(pthread_mutexattr_t* ingore_attr, int ignore);
+void pthread_mutexattr_destroy(pthread_mutexattr_t* ignore_attr);
+
+
+// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
+
+typedef struct
+{
+ int waiters_count_;
+ // Number of waiting threads.
+
+ CRITICAL_SECTION waiters_count_lock_;
+ // Serialize access to .
+
+ HANDLE sema_;
+ // Semaphore used to queue up threads waiting for the condition to
+ // become signaled.
+
+ HANDLE waiters_done_;
+ // An auto-reset event used by the broadcast/signal thread to wait
+ // for all the waiting thread(s) to wake up and be released from the
+ // semaphore.
+
+ size_t was_broadcast_;
+ // Keeps track of whether we were broadcasting or signaling. This
+ // allows us to optimize the code if we're just signaling.
+}pthread_cond_t;
+
+int pthread_cond_init (pthread_cond_t *cv,const pthread_condattr_t * ignore);
+int pthread_cond_destroy(pthread_cond_t *cond);
+int pthread_cond_signal (pthread_cond_t *cv);
+int pthread_cond_broadcast (pthread_cond_t *cv);
+int pthread_cond_wait (pthread_cond_t *cv, pthread_mutex_t *external_mutex);
+
+
+struct pthread_key_t_
+{
+ DWORD key;
+ void (*destructor) (void *);
+};
+
+typedef struct pthread_key_t_ pthread_key_t;
+int pthread_key_create(pthread_key_t *key, void (*destructor)(void *) );
+int pthread_key_delete(pthread_key_t key);
+void *pthread_getspecific(pthread_key_t key);
+int pthread_setspecific(pthread_key_t key, const void *value);
+
+int gettimeofday(struct timeval *tp, void *tzp);
+int close(SOCKET fd);
+int Win32WSAStartup();
+void Win32WSACleanup();
+#endif //WIN32
+
+
+
+#endif //WINPORT_H_
diff --git a/src/c/src/zk_adaptor.h b/src/c/src/zk_adaptor.h
new file mode 100644
index 0000000..b990779
--- /dev/null
+++ b/src/c/src/zk_adaptor.h
@@ -0,0 +1,276 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 ZK_ADAPTOR_H_
+#define ZK_ADAPTOR_H_
+#include
+#ifdef THREADED
+#ifndef WIN32
+#include
+#else
+#include "winport.h"
+#endif
+#endif
+#include "zookeeper.h"
+#include "zk_hashtable.h"
+
+/* predefined xid's values recognized as special by the server */
+#define WATCHER_EVENT_XID -1
+#define PING_XID -2
+#define AUTH_XID -4
+#define SET_WATCHES_XID -8
+
+/* zookeeper state constants */
+#define EXPIRED_SESSION_STATE_DEF -112
+#define AUTH_FAILED_STATE_DEF -113
+#define CONNECTING_STATE_DEF 1
+#define ASSOCIATING_STATE_DEF 2
+#define CONNECTED_STATE_DEF 3
+#define NOTCONNECTED_STATE_DEF 999
+
+/* zookeeper event type constants */
+#define CREATED_EVENT_DEF 1
+#define DELETED_EVENT_DEF 2
+#define CHANGED_EVENT_DEF 3
+#define CHILD_EVENT_DEF 4
+#define SESSION_EVENT_DEF -1
+#define NOTWATCHING_EVENT_DEF -2
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _buffer_list;
+struct _completion_list;
+
+typedef struct _buffer_head {
+ struct _buffer_list *volatile head;
+ struct _buffer_list *last;
+#ifdef THREADED
+ pthread_mutex_t lock;
+#endif
+} buffer_head_t;
+
+typedef struct _completion_head {
+ struct _completion_list *volatile head;
+ struct _completion_list *last;
+#ifdef THREADED
+ pthread_cond_t cond;
+ pthread_mutex_t lock;
+#endif
+} completion_head_t;
+
+int lock_buffer_list(buffer_head_t *l);
+int unlock_buffer_list(buffer_head_t *l);
+int lock_completion_list(completion_head_t *l);
+int unlock_completion_list(completion_head_t *l);
+
+struct sync_completion {
+ int rc;
+ union {
+ struct {
+ char *str;
+ int str_len;
+ } str;
+ struct Stat stat;
+ struct {
+ char *buffer;
+ int buff_len;
+ struct Stat stat;
+ } data;
+ struct {
+ struct ACL_vector acl;
+ struct Stat stat;
+ } acl;
+ struct String_vector strs2;
+ struct {
+ struct String_vector strs2;
+ struct Stat stat2;
+ } strs_stat;
+ } u;
+ int complete;
+#ifdef THREADED
+ pthread_cond_t cond;
+ pthread_mutex_t lock;
+#endif
+};
+
+typedef struct _auth_info {
+ int state; /* 0=>inactive, >0 => active */
+ char* scheme;
+ struct buffer auth;
+ void_completion_t completion;
+ const char* data;
+ struct _auth_info *next;
+} auth_info;
+
+/**
+ * This structure represents a packet being read or written.
+ */
+typedef struct _buffer_list {
+ char *buffer;
+ int len; /* This represents the length of sizeof(header) + length of buffer */
+ int curr_offset; /* This is the offset into the header followed by offset into the buffer */
+ struct _buffer_list *next;
+} buffer_list_t;
+
+/* the size of connect request */
+#define HANDSHAKE_REQ_SIZE 44
+/* connect request */
+struct connect_req {
+ int32_t protocolVersion;
+ int64_t lastZxidSeen;
+ int32_t timeOut;
+ int64_t sessionId;
+ int32_t passwd_len;
+ char passwd[16];
+};
+
+/* the connect response */
+struct prime_struct {
+ int32_t len;
+ int32_t protocolVersion;
+ int32_t timeOut;
+ int64_t sessionId;
+ int32_t passwd_len;
+ char passwd[16];
+};
+
+#ifdef THREADED
+/* this is used by mt_adaptor internally for thread management */
+struct adaptor_threads {
+ pthread_t io;
+ pthread_t completion;
+ int threadsToWait; // barrier
+ pthread_cond_t cond; // barrier's conditional
+ pthread_mutex_t lock; // ... and a lock
+ pthread_mutex_t zh_lock; // critical section lock
+#ifdef WIN32
+ SOCKET self_pipe[2];
+#else
+ int self_pipe[2];
+#endif
+};
+#endif
+
+/** the auth list for adding auth */
+typedef struct _auth_list_head {
+ auth_info *auth;
+#ifdef THREADED
+ pthread_mutex_t lock;
+#endif
+} auth_list_head_t;
+
+/**
+ * This structure represents the connection to zookeeper.
+ */
+
+struct _zhandle {
+#ifdef WIN32
+ SOCKET fd; /* the descriptor used to talk to zookeeper */
+#else
+ int fd; /* the descriptor used to talk to zookeeper */
+#endif
+ char *hostname; /* the hostname of zookeeper */
+ struct sockaddr_storage *addrs; /* the addresses that correspond to the hostname */
+ int addrs_count; /* The number of addresses in the addrs array */
+ watcher_fn watcher; /* the registered watcher */
+ struct timeval last_recv; /* The time that the last message was received */
+ struct timeval last_send; /* The time that the last message was sent */
+ struct timeval last_ping; /* The time that the last PING was sent */
+ struct timeval next_deadline; /* The time of the next deadline */
+ int recv_timeout; /* The maximum amount of time that can go by without
+ receiving anything from the zookeeper server */
+ buffer_list_t *input_buffer; /* the current buffer being read in */
+ buffer_head_t to_process; /* The buffers that have been read and are ready to be processed. */
+ buffer_head_t to_send; /* The packets queued to send */
+ completion_head_t sent_requests; /* The outstanding requests */
+ completion_head_t completions_to_process; /* completions that are ready to run */
+ int connect_index; /* The index of the address to connect to */
+ clientid_t client_id;
+ long long last_zxid;
+ int outstanding_sync; /* Number of outstanding synchronous requests */
+ struct _buffer_list primer_buffer; /* The buffer used for the handshake at the start of a connection */
+ struct prime_struct primer_storage; /* the connect response */
+ char primer_storage_buffer[40]; /* the true size of primer_storage */
+ volatile int state;
+ void *context;
+ auth_list_head_t auth_h; /* authentication data list */
+ /* zookeeper_close is not reentrant because it de-allocates the zhandler.
+ * This guard variable is used to defer the destruction of zhandle till
+ * right before top-level API call returns to the caller */
+ int32_t ref_counter;
+ volatile int close_requested;
+ void *adaptor_priv;
+ /* Used for debugging only: non-zero value indicates the time when the zookeeper_process
+ * call returned while there was at least one unprocessed server response
+ * available in the socket recv buffer */
+ struct timeval socket_readable;
+
+ zk_hashtable* active_node_watchers;
+ zk_hashtable* active_exist_watchers;
+ zk_hashtable* active_child_watchers;
+ /** used for chroot path at the client side **/
+ char *chroot;
+};
+
+
+int adaptor_init(zhandle_t *zh);
+void adaptor_finish(zhandle_t *zh);
+void adaptor_destroy(zhandle_t *zh);
+struct sync_completion *alloc_sync_completion(void);
+int wait_sync_completion(struct sync_completion *sc);
+void free_sync_completion(struct sync_completion *sc);
+void notify_sync_completion(struct sync_completion *sc);
+int adaptor_send_queue(zhandle_t *zh, int timeout);
+int process_async(int outstanding_sync);
+void process_completions(zhandle_t *zh);
+int flush_send_queue(zhandle_t*zh, int timeout);
+char* sub_string(zhandle_t *zh, const char* server_path);
+void free_duplicate_path(const char* free_path, const char* path);
+int zoo_lock_auth(zhandle_t *zh);
+int zoo_unlock_auth(zhandle_t *zh);
+
+// critical section guards
+int enter_critical(zhandle_t* zh);
+int leave_critical(zhandle_t* zh);
+// zhandle object reference counting
+void api_prolog(zhandle_t* zh);
+int api_epilog(zhandle_t *zh, int rc);
+int32_t get_xid();
+// returns the new value of the ref counter
+int32_t inc_ref_counter(zhandle_t* zh,int i);
+
+#ifdef THREADED
+// atomic post-increment
+int32_t fetch_and_add(volatile int32_t* operand, int incr);
+// in mt mode process session event asynchronously by the completion thread
+#define PROCESS_SESSION_EVENT(zh,newstate) queue_session_event(zh,newstate)
+#else
+// in single-threaded mode process session event immediately
+//#define PROCESS_SESSION_EVENT(zh,newstate) deliverWatchers(zh,ZOO_SESSION_EVENT,newstate,0)
+#define PROCESS_SESSION_EVENT(zh,newstate) queue_session_event(zh,newstate)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*ZK_ADAPTOR_H_*/
+
+
diff --git a/src/c/src/zk_hashtable.c b/src/c/src/zk_hashtable.c
new file mode 100644
index 0000000..0efc5aa
--- /dev/null
+++ b/src/c/src/zk_hashtable.c
@@ -0,0 +1,337 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 "zk_hashtable.h"
+#include "zk_adaptor.h"
+#include "hashtable/hashtable.h"
+#include "hashtable/hashtable_itr.h"
+#include
+#include
+#include
+
+typedef struct _watcher_object {
+ watcher_fn watcher;
+ void* context;
+ struct _watcher_object* next;
+} watcher_object_t;
+
+
+struct _zk_hashtable {
+ struct hashtable* ht;
+};
+
+struct watcher_object_list {
+ watcher_object_t* head;
+};
+
+/* the following functions are for testing only */
+typedef struct hashtable hashtable_impl;
+
+hashtable_impl* getImpl(zk_hashtable* ht){
+ return ht->ht;
+}
+
+watcher_object_t* getFirstWatcher(zk_hashtable* ht,const char* path)
+{
+ watcher_object_list_t* wl=hashtable_search(ht->ht,(void*)path);
+ if(wl!=0)
+ return wl->head;
+ return 0;
+}
+/* end of testing functions */
+
+watcher_object_t* clone_watcher_object(watcher_object_t* wo)
+{
+ watcher_object_t* res=calloc(1,sizeof(watcher_object_t));
+ assert(res);
+ res->watcher=wo->watcher;
+ res->context=wo->context;
+ return res;
+}
+
+static unsigned int string_hash_djb2(void *str)
+{
+ unsigned int hash = 5381;
+ int c;
+ const char* cstr = (const char*)str;
+ while ((c = *cstr++))
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+
+ return hash;
+}
+
+static int string_equal(void *key1,void *key2)
+{
+ return strcmp((const char*)key1,(const char*)key2)==0;
+}
+
+static watcher_object_t* create_watcher_object(watcher_fn watcher,void* ctx)
+{
+ watcher_object_t* wo=calloc(1,sizeof(watcher_object_t));
+ assert(wo);
+ wo->watcher=watcher;
+ wo->context=ctx;
+ return wo;
+}
+
+static watcher_object_list_t* create_watcher_object_list(watcher_object_t* head)
+{
+ watcher_object_list_t* wl=calloc(1,sizeof(watcher_object_list_t));
+ assert(wl);
+ wl->head=head;
+ return wl;
+}
+
+static void destroy_watcher_object_list(watcher_object_list_t* list)
+{
+ watcher_object_t* e = NULL;
+
+ if(list==0)
+ return;
+ e=list->head;
+ while(e!=0){
+ watcher_object_t* this=e;
+ e=e->next;
+ free(this);
+ }
+ free(list);
+}
+
+zk_hashtable* create_zk_hashtable()
+{
+ struct _zk_hashtable *ht=calloc(1,sizeof(struct _zk_hashtable));
+ assert(ht);
+ ht->ht=create_hashtable(32,string_hash_djb2,string_equal);
+ return ht;
+}
+
+static void do_clean_hashtable(zk_hashtable* ht)
+{
+ struct hashtable_itr *it;
+ int hasMore;
+ if(hashtable_count(ht->ht)==0)
+ return;
+ it=hashtable_iterator(ht->ht);
+ do {
+ watcher_object_list_t* w=hashtable_iterator_value(it);
+ destroy_watcher_object_list(w);
+ hasMore=hashtable_iterator_remove(it);
+ } while(hasMore);
+ free(it);
+}
+
+void destroy_zk_hashtable(zk_hashtable* ht)
+{
+ if(ht!=0){
+ do_clean_hashtable(ht);
+ hashtable_destroy(ht->ht,0);
+ free(ht);
+ }
+}
+
+// searches for a watcher object instance in a watcher object list;
+// two watcher objects are equal if their watcher function and context pointers
+// are equal
+static watcher_object_t* search_watcher(watcher_object_list_t** wl,watcher_object_t* wo)
+{
+ watcher_object_t* wobj=(*wl)->head;
+ while(wobj!=0){
+ if(wobj->watcher==wo->watcher && wobj->context==wo->context)
+ return wobj;
+ wobj=wobj->next;
+ }
+ return 0;
+}
+
+static int add_to_list(watcher_object_list_t **wl, watcher_object_t *wo,
+ int clone)
+{
+ if (search_watcher(wl, wo)==0) {
+ watcher_object_t* cloned=wo;
+ if (clone) {
+ cloned = clone_watcher_object(wo);
+ assert(cloned);
+ }
+ cloned->next = (*wl)->head;
+ (*wl)->head = cloned;
+ return 1;
+ } else if (!clone) {
+ // If it's here and we aren't supposed to clone, we must destroy
+ free(wo);
+ }
+ return 0;
+}
+
+static int do_insert_watcher_object(zk_hashtable *ht, const char *path, watcher_object_t* wo)
+{
+ int res=1;
+ watcher_object_list_t* wl;
+
+ wl=hashtable_search(ht->ht,(void*)path);
+ if(wl==0){
+ int res;
+ /* inserting a new path element */
+ res=hashtable_insert(ht->ht,strdup(path),create_watcher_object_list(wo));
+ assert(res);
+ }else{
+ /*
+ * Path already exists; check if the watcher already exists.
+ * Don't clone the watcher since it's allocated on the heap --- avoids
+ * a memory leak and saves a clone operation (calloc + copy).
+ */
+ res = add_to_list(&wl, wo, 0);
+ }
+ return res;
+}
+
+
+char **collect_keys(zk_hashtable *ht, int *count)
+{
+ char **list;
+ struct hashtable_itr *it;
+ int i;
+
+ *count = hashtable_count(ht->ht);
+ list = calloc(*count, sizeof(char*));
+ it=hashtable_iterator(ht->ht);
+ for(i = 0; i < *count; i++) {
+ list[i] = strdup(hashtable_iterator_key(it));
+ hashtable_iterator_advance(it);
+ }
+ free(it);
+ return list;
+}
+
+static int insert_watcher_object(zk_hashtable *ht, const char *path,
+ watcher_object_t* wo)
+{
+ int res;
+ res=do_insert_watcher_object(ht,path,wo);
+ return res;
+}
+
+static void copy_watchers(watcher_object_list_t *from, watcher_object_list_t *to, int clone)
+{
+ watcher_object_t* wo=from->head;
+ while(wo){
+ watcher_object_t *next = wo->next;
+ add_to_list(&to, wo, clone);
+ wo=next;
+ }
+}
+
+static void copy_table(zk_hashtable *from, watcher_object_list_t *to) {
+ struct hashtable_itr *it;
+ int hasMore;
+ if(hashtable_count(from->ht)==0)
+ return;
+ it=hashtable_iterator(from->ht);
+ do {
+ watcher_object_list_t *w = hashtable_iterator_value(it);
+ copy_watchers(w, to, 1);
+ hasMore=hashtable_iterator_advance(it);
+ } while(hasMore);
+ free(it);
+}
+
+static void collect_session_watchers(zhandle_t *zh,
+ watcher_object_list_t **list)
+{
+ copy_table(zh->active_node_watchers, *list);
+ copy_table(zh->active_exist_watchers, *list);
+ copy_table(zh->active_child_watchers, *list);
+}
+
+static void add_for_event(zk_hashtable *ht, char *path, watcher_object_list_t **list)
+{
+ watcher_object_list_t* wl;
+ wl = (watcher_object_list_t*)hashtable_remove(ht->ht, path);
+ if (wl) {
+ copy_watchers(wl, *list, 0);
+ // Since we move, not clone the watch_objects, we just need to free the
+ // head pointer
+ free(wl);
+ }
+}
+
+static void do_foreach_watcher(watcher_object_t* wo,zhandle_t* zh,
+ const char* path,int type,int state)
+{
+ // session event's don't have paths
+ const char *client_path =
+ (type != ZOO_SESSION_EVENT ? sub_string(zh, path) : path);
+ while(wo!=0){
+ wo->watcher(zh,type,state,client_path,wo->context);
+ wo=wo->next;
+ }
+ free_duplicate_path(client_path, path);
+}
+
+watcher_object_list_t *collectWatchers(zhandle_t *zh,int type, char *path)
+{
+ struct watcher_object_list *list = create_watcher_object_list(0);
+
+ if(type==ZOO_SESSION_EVENT){
+ watcher_object_t defWatcher;
+ defWatcher.watcher=zh->watcher;
+ defWatcher.context=zh->context;
+ add_to_list(&list, &defWatcher, 1);
+ collect_session_watchers(zh, &list);
+ return list;
+ }
+ switch(type){
+ case CREATED_EVENT_DEF:
+ case CHANGED_EVENT_DEF:
+ // look up the watchers for the path and move them to a delivery list
+ add_for_event(zh->active_node_watchers,path,&list);
+ add_for_event(zh->active_exist_watchers,path,&list);
+ break;
+ case CHILD_EVENT_DEF:
+ // look up the watchers for the path and move them to a delivery list
+ add_for_event(zh->active_child_watchers,path,&list);
+ break;
+ case DELETED_EVENT_DEF:
+ // look up the watchers for the path and move them to a delivery list
+ add_for_event(zh->active_node_watchers,path,&list);
+ add_for_event(zh->active_exist_watchers,path,&list);
+ add_for_event(zh->active_child_watchers,path,&list);
+ break;
+ }
+ return list;
+}
+
+void deliverWatchers(zhandle_t *zh, int type,int state, char *path, watcher_object_list_t **list)
+{
+ if (!list || !(*list)) return;
+ do_foreach_watcher((*list)->head, zh, path, type, state);
+ destroy_watcher_object_list(*list);
+ *list = 0;
+}
+
+void activateWatcher(zhandle_t *zh, watcher_registration_t* reg, int rc)
+{
+ if(reg){
+ /* in multithreaded lib, this code is executed
+ * by the IO thread */
+ zk_hashtable *ht = reg->checker(zh, rc);
+ if(ht){
+ insert_watcher_object(ht,reg->path,
+ create_watcher_object(reg->watcher, reg->context));
+ }
+ }
+}
diff --git a/src/c/src/zk_hashtable.h b/src/c/src/zk_hashtable.h
new file mode 100644
index 0000000..31109c1
--- /dev/null
+++ b/src/c/src/zk_hashtable.h
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 ZK_HASHTABLE_H_
+#define ZK_HASHTABLE_H_
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ typedef struct watcher_object_list watcher_object_list_t;
+typedef struct _zk_hashtable zk_hashtable;
+
+/**
+ * The function must return a non-zero value if the watcher object can be activated
+ * as a result of the server response. Normally, a watch can only be activated
+ * if the server returns a success code (ZOK). However in the case when zoo_exists()
+ * returns a ZNONODE code the watcher should be activated nevertheless.
+ */
+typedef zk_hashtable *(*result_checker_fn)(zhandle_t *, int rc);
+
+/**
+ * A watcher object gets temporarily stored with the completion entry until
+ * the server response comes back at which moment the watcher object is moved
+ * to the active watchers map.
+ */
+typedef struct _watcher_registration {
+ watcher_fn watcher;
+ void* context;
+ result_checker_fn checker;
+ const char* path;
+} watcher_registration_t;
+
+zk_hashtable* create_zk_hashtable();
+void destroy_zk_hashtable(zk_hashtable* ht);
+
+char **collect_keys(zk_hashtable *ht, int *count);
+
+/**
+ * check if the completion has a watcher object associated
+ * with it. If it does, move the watcher object to the map of
+ * active watchers (only if the checker allows to do so)
+ */
+ void activateWatcher(zhandle_t *zh, watcher_registration_t* reg, int rc);
+ watcher_object_list_t *collectWatchers(zhandle_t *zh,int type, char *path);
+ void deliverWatchers(zhandle_t *zh, int type, int state, char *path, struct watcher_object_list **list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*ZK_HASHTABLE_H_*/
diff --git a/src/c/src/zk_log.c b/src/c/src/zk_log.c
new file mode 100644
index 0000000..6b4fdfa
--- /dev/null
+++ b/src/c/src/zk_log.c
@@ -0,0 +1,180 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#if !defined(DLL_EXPORT) && !defined(USE_STATIC_LIB)
+# define USE_STATIC_LIB
+#endif
+
+#include "zookeeper_log.h"
+#ifndef WIN32
+#include
+#else
+typedef DWORD pid_t;
+#include /* for getpid */
+#endif
+
+#include
+#include
+
+#define TIME_NOW_BUF_SIZE 1024
+#define FORMAT_LOG_BUF_SIZE 4096
+
+#ifdef THREADED
+#ifndef WIN32
+#include
+#else
+#include "winport.h"
+#endif
+
+static pthread_key_t time_now_buffer;
+static pthread_key_t format_log_msg_buffer;
+
+void freeBuffer(void* p){
+ if(p) free(p);
+}
+
+__attribute__((constructor)) void prepareTSDKeys() {
+ pthread_key_create (&time_now_buffer, freeBuffer);
+ pthread_key_create (&format_log_msg_buffer, freeBuffer);
+}
+
+char* getTSData(pthread_key_t key,int size){
+ char* p=pthread_getspecific(key);
+ if(p==0){
+ int res;
+ p=calloc(1,size);
+ res=pthread_setspecific(key,p);
+ if(res!=0){
+ fprintf(stderr,"Failed to set TSD key: %d",res);
+ }
+ }
+ return p;
+}
+
+char* get_time_buffer(){
+ return getTSData(time_now_buffer,TIME_NOW_BUF_SIZE);
+}
+
+char* get_format_log_buffer(){
+ return getTSData(format_log_msg_buffer,FORMAT_LOG_BUF_SIZE);
+}
+#else
+char* get_time_buffer(){
+ static char buf[TIME_NOW_BUF_SIZE];
+ return buf;
+}
+
+char* get_format_log_buffer(){
+ static char buf[FORMAT_LOG_BUF_SIZE];
+ return buf;
+}
+
+#endif
+
+ZooLogLevel logLevel=ZOO_LOG_LEVEL_INFO;
+
+static FILE* logStream=0;
+FILE* getLogStream(){
+ if(logStream==0)
+ logStream=stderr;
+ return logStream;
+}
+
+void zoo_set_log_stream(FILE* stream){
+ logStream=stream;
+}
+
+static const char* time_now(char* now_str){
+ struct timeval tv;
+ struct tm lt;
+ time_t now = 0;
+ size_t len = 0;
+
+ gettimeofday(&tv,0);
+
+ now = tv.tv_sec;
+ localtime_r(&now, <);
+
+ // clone the format used by log4j ISO8601DateFormat
+ // specifically: "yyyy-MM-dd HH:mm:ss,SSS"
+
+ len = strftime(now_str, TIME_NOW_BUF_SIZE,
+ "%Y-%m-%d %H:%M:%S",
+ <);
+
+ len += snprintf(now_str + len,
+ TIME_NOW_BUF_SIZE - len,
+ ",%03d",
+ (int)(tv.tv_usec/1000));
+
+ return now_str;
+}
+
+void log_message(ZooLogLevel curLevel,int line,const char* funcName,
+ const char* message)
+{
+ static const char* dbgLevelStr[]={"ZOO_INVALID","ZOO_ERROR","ZOO_WARN",
+ "ZOO_INFO","ZOO_DEBUG"};
+ static pid_t pid=0;
+#ifdef WIN32
+ char timebuf [TIME_NOW_BUF_SIZE];
+#endif
+ if(pid==0)pid=getpid();
+#ifndef THREADED
+ // pid_t is long on Solaris
+ fprintf(LOGSTREAM, "%s:%ld:%s@%s@%d: %s\n", time_now(get_time_buffer()),(long)pid,
+ dbgLevelStr[curLevel],funcName,line,message);
+#else
+#ifdef WIN32
+ fprintf(LOGSTREAM, "%s:%d(0x%lx):%s@%s@%d: %s\n", time_now(timebuf),pid,
+ (unsigned long int)(pthread_self().thread_id),
+ dbgLevelStr[curLevel],funcName,line,message);
+#else
+ fprintf(LOGSTREAM, "%s:%ld(0x%lx):%s@%s@%d: %s\n", time_now(get_time_buffer()),(long)pid,
+ (unsigned long int)pthread_self(),
+ dbgLevelStr[curLevel],funcName,line,message);
+#endif
+#endif
+ fflush(LOGSTREAM);
+}
+
+const char* format_log_message(const char* format,...)
+{
+ va_list va;
+ char* buf=get_format_log_buffer();
+ if(!buf)
+ return "format_log_message: Unable to allocate memory buffer";
+
+ va_start(va,format);
+ vsnprintf(buf, FORMAT_LOG_BUF_SIZE-1,format,va);
+ va_end(va);
+ return buf;
+}
+
+void zoo_set_debug_level(ZooLogLevel level)
+{
+ if(level==0){
+ // disable logging (unit tests do this)
+ logLevel=(ZooLogLevel)0;
+ return;
+ }
+ if(levelZOO_LOG_LEVEL_DEBUG)level=ZOO_LOG_LEVEL_DEBUG;
+ logLevel=level;
+}
+
diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c
new file mode 100644
index 0000000..c087864
--- /dev/null
+++ b/src/c/src/zookeeper.c
@@ -0,0 +1,3767 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#if !defined(DLL_EXPORT) && !defined(USE_STATIC_LIB)
+# define USE_STATIC_LIB
+#endif
+
+#if defined(__CYGWIN__)
+#define USE_IPV6
+#endif
+
+#include "config.h"
+#include
+#include
+#include
+#include "zk_adaptor.h"
+#include "zookeeper_log.h"
+#include "zk_hashtable.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef HAVE_SYS_TIME_H
+#include
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include
+#endif
+
+#ifdef HAVE_POLL
+#include
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include
+#include
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include
+#endif
+
+#ifdef HAVE_NETDB_H
+#include
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include // needed for _POSIX_MONOTONIC_CLOCK
+#endif
+
+#ifdef HAVE_SYS_UTSNAME_H
+#include
+#endif
+
+#ifdef HAVE_GETPWUID_R
+#include
+#endif
+
+#ifdef WIN32
+#define random rand /* replace POSIX random with Windows rand */
+#include /* for getpid */
+#include /* for getcwd */
+#define EAI_ADDRFAMILY WSAEINVAL /* is this still needed? */
+#define EHOSTDOWN EPIPE
+#define ESTALE ENODEV
+#endif
+
+#define IF_DEBUG(x) if(logLevel==ZOO_LOG_LEVEL_DEBUG) {x;}
+
+const int ZOOKEEPER_WRITE = 1 << 0;
+const int ZOOKEEPER_READ = 1 << 1;
+
+const int ZOO_EPHEMERAL = 1 << 0;
+const int ZOO_SEQUENCE = 1 << 1;
+
+const int ZOO_EXPIRED_SESSION_STATE = EXPIRED_SESSION_STATE_DEF;
+const int ZOO_AUTH_FAILED_STATE = AUTH_FAILED_STATE_DEF;
+const int ZOO_CONNECTING_STATE = CONNECTING_STATE_DEF;
+const int ZOO_ASSOCIATING_STATE = ASSOCIATING_STATE_DEF;
+const int ZOO_CONNECTED_STATE = CONNECTED_STATE_DEF;
+static __attribute__ ((unused)) const char* state2String(int state){
+ switch(state){
+ case 0:
+ return "ZOO_CLOSED_STATE";
+ case CONNECTING_STATE_DEF:
+ return "ZOO_CONNECTING_STATE";
+ case ASSOCIATING_STATE_DEF:
+ return "ZOO_ASSOCIATING_STATE";
+ case CONNECTED_STATE_DEF:
+ return "ZOO_CONNECTED_STATE";
+ case EXPIRED_SESSION_STATE_DEF:
+ return "ZOO_EXPIRED_SESSION_STATE";
+ case AUTH_FAILED_STATE_DEF:
+ return "ZOO_AUTH_FAILED_STATE";
+ }
+ return "INVALID_STATE";
+}
+
+const int ZOO_CREATED_EVENT = CREATED_EVENT_DEF;
+const int ZOO_DELETED_EVENT = DELETED_EVENT_DEF;
+const int ZOO_CHANGED_EVENT = CHANGED_EVENT_DEF;
+const int ZOO_CHILD_EVENT = CHILD_EVENT_DEF;
+const int ZOO_SESSION_EVENT = SESSION_EVENT_DEF;
+const int ZOO_NOTWATCHING_EVENT = NOTWATCHING_EVENT_DEF;
+static __attribute__ ((unused)) const char* watcherEvent2String(int ev){
+ switch(ev){
+ case 0:
+ return "ZOO_ERROR_EVENT";
+ case CREATED_EVENT_DEF:
+ return "ZOO_CREATED_EVENT";
+ case DELETED_EVENT_DEF:
+ return "ZOO_DELETED_EVENT";
+ case CHANGED_EVENT_DEF:
+ return "ZOO_CHANGED_EVENT";
+ case CHILD_EVENT_DEF:
+ return "ZOO_CHILD_EVENT";
+ case SESSION_EVENT_DEF:
+ return "ZOO_SESSION_EVENT";
+ case NOTWATCHING_EVENT_DEF:
+ return "ZOO_NOTWATCHING_EVENT";
+ }
+ return "INVALID_EVENT";
+}
+
+const int ZOO_PERM_READ = 1 << 0;
+const int ZOO_PERM_WRITE = 1 << 1;
+const int ZOO_PERM_CREATE = 1 << 2;
+const int ZOO_PERM_DELETE = 1 << 3;
+const int ZOO_PERM_ADMIN = 1 << 4;
+const int ZOO_PERM_ALL = 0x1f;
+struct Id ZOO_ANYONE_ID_UNSAFE = {"world", "anyone"};
+struct Id ZOO_AUTH_IDS = {"auth", ""};
+static struct ACL _OPEN_ACL_UNSAFE_ACL[] = {{0x1f, {"world", "anyone"}}};
+static struct ACL _READ_ACL_UNSAFE_ACL[] = {{0x01, {"world", "anyone"}}};
+static struct ACL _CREATOR_ALL_ACL_ACL[] = {{0x1f, {"auth", ""}}};
+struct ACL_vector ZOO_OPEN_ACL_UNSAFE = { 1, _OPEN_ACL_UNSAFE_ACL};
+struct ACL_vector ZOO_READ_ACL_UNSAFE = { 1, _READ_ACL_UNSAFE_ACL};
+struct ACL_vector ZOO_CREATOR_ALL_ACL = { 1, _CREATOR_ALL_ACL_ACL};
+
+#define COMPLETION_WATCH -1
+#define COMPLETION_VOID 0
+#define COMPLETION_STAT 1
+#define COMPLETION_DATA 2
+#define COMPLETION_STRINGLIST 3
+#define COMPLETION_STRINGLIST_STAT 4
+#define COMPLETION_ACLLIST 5
+#define COMPLETION_STRING 6
+#define COMPLETION_MULTI 7
+
+typedef struct _auth_completion_list {
+ void_completion_t completion;
+ const char *auth_data;
+ struct _auth_completion_list *next;
+} auth_completion_list_t;
+
+typedef struct completion {
+ int type; /* one of COMPLETION_* values above */
+ union {
+ void_completion_t void_result;
+ stat_completion_t stat_result;
+ data_completion_t data_result;
+ strings_completion_t strings_result;
+ strings_stat_completion_t strings_stat_result;
+ acl_completion_t acl_result;
+ string_completion_t string_result;
+ struct watcher_object_list *watcher_result;
+ };
+ completion_head_t clist; /* For multi-op */
+} completion_t;
+
+typedef struct _completion_list {
+ int xid;
+ completion_t c;
+ const void *data;
+ buffer_list_t *buffer;
+ struct _completion_list *next;
+ watcher_registration_t* watcher;
+} completion_list_t;
+
+const char*err2string(int err);
+static int queue_session_event(zhandle_t *zh, int state);
+static const char* format_endpoint_info(const struct sockaddr_storage* ep);
+static const char* format_current_endpoint_info(zhandle_t* zh);
+
+/* deserialize forward declarations */
+static void deserialize_response(int type, int xid, int failed, int rc, completion_list_t *cptr, struct iarchive *ia);
+static int deserialize_multi(int xid, completion_list_t *cptr, struct iarchive *ia);
+
+/* completion routine forward declarations */
+static int add_completion(zhandle_t *zh, int xid, int completion_type,
+ const void *dc, const void *data, int add_to_front,
+ watcher_registration_t* wo, completion_head_t *clist);
+static completion_list_t* create_completion_entry(int xid, int completion_type,
+ const void *dc, const void *data, watcher_registration_t* wo,
+ completion_head_t *clist);
+static void destroy_completion_entry(completion_list_t* c);
+static void queue_completion_nolock(completion_head_t *list, completion_list_t *c,
+ int add_to_front);
+static void queue_completion(completion_head_t *list, completion_list_t *c,
+ int add_to_front);
+static int handle_socket_error_msg(zhandle_t *zh, int line, int rc,
+ const char* format,...);
+static void cleanup_bufs(zhandle_t *zh,int callCompletion,int rc);
+
+static int disable_conn_permute=0; // permute enabled by default
+
+static __attribute__((unused)) void print_completion_queue(zhandle_t *zh);
+
+static void *SYNCHRONOUS_MARKER = (void*)&SYNCHRONOUS_MARKER;
+static int isValidPath(const char* path, const int flags);
+
+#ifdef _WINDOWS
+static int zookeeper_send(SOCKET s, const char* buf, int len)
+#else
+static ssize_t zookeeper_send(int s, const void* buf, size_t len)
+#endif
+{
+#ifdef __linux__
+ return send(s, buf, len, MSG_NOSIGNAL);
+#else
+ return send(s, buf, len, 0);
+#endif
+}
+
+const void *zoo_get_context(zhandle_t *zh)
+{
+ return zh->context;
+}
+
+void zoo_set_context(zhandle_t *zh, void *context)
+{
+ if (zh != NULL) {
+ zh->context = context;
+ }
+}
+
+int zoo_recv_timeout(zhandle_t *zh)
+{
+ return zh->recv_timeout;
+}
+
+/** these functions are thread unsafe, so make sure that
+ zoo_lock_auth is called before you access them **/
+static auth_info* get_last_auth(auth_list_head_t *auth_list) {
+ auth_info *element;
+ element = auth_list->auth;
+ if (element == NULL) {
+ return NULL;
+ }
+ while (element->next != NULL) {
+ element = element->next;
+ }
+ return element;
+}
+
+static void free_auth_completion(auth_completion_list_t *a_list) {
+ auth_completion_list_t *tmp, *ftmp;
+ if (a_list == NULL) {
+ return;
+ }
+ tmp = a_list->next;
+ while (tmp != NULL) {
+ ftmp = tmp;
+ tmp = tmp->next;
+ ftmp->completion = NULL;
+ ftmp->auth_data = NULL;
+ free(ftmp);
+ }
+ a_list->completion = NULL;
+ a_list->auth_data = NULL;
+ a_list->next = NULL;
+ return;
+}
+
+static void add_auth_completion(auth_completion_list_t* a_list, void_completion_t* completion,
+ const char *data) {
+ auth_completion_list_t *element;
+ auth_completion_list_t *n_element;
+ element = a_list;
+ if (a_list->completion == NULL) {
+ //this is the first element
+ a_list->completion = *completion;
+ a_list->next = NULL;
+ a_list->auth_data = data;
+ return;
+ }
+ while (element->next != NULL) {
+ element = element->next;
+ }
+ n_element = (auth_completion_list_t*) malloc(sizeof(auth_completion_list_t));
+ n_element->next = NULL;
+ n_element->completion = *completion;
+ n_element->auth_data = data;
+ element->next = n_element;
+ return;
+}
+
+static void get_auth_completions(auth_list_head_t *auth_list, auth_completion_list_t *a_list) {
+ auth_info *element;
+ element = auth_list->auth;
+ if (element == NULL) {
+ return;
+ }
+ while (element) {
+ if (element->completion) {
+ add_auth_completion(a_list, &element->completion, element->data);
+ }
+ element->completion = NULL;
+ element = element->next;
+ }
+ return;
+}
+
+static void add_last_auth(auth_list_head_t *auth_list, auth_info *add_el) {
+ auth_info *element;
+ element = auth_list->auth;
+ if (element == NULL) {
+ //first element in the list
+ auth_list->auth = add_el;
+ return;
+ }
+ while (element->next != NULL) {
+ element = element->next;
+ }
+ element->next = add_el;
+ return;
+}
+
+static void init_auth_info(auth_list_head_t *auth_list)
+{
+ auth_list->auth = NULL;
+}
+
+static void mark_active_auth(zhandle_t *zh) {
+ auth_list_head_t auth_h = zh->auth_h;
+ auth_info *element;
+ if (auth_h.auth == NULL) {
+ return;
+ }
+ element = auth_h.auth;
+ while (element != NULL) {
+ element->state = 1;
+ element = element->next;
+ }
+}
+
+static void free_auth_info(auth_list_head_t *auth_list)
+{
+ auth_info *auth = auth_list->auth;
+ while (auth != NULL) {
+ auth_info* old_auth = NULL;
+ if(auth->scheme!=NULL)
+ free(auth->scheme);
+ deallocate_Buffer(&auth->auth);
+ old_auth = auth;
+ auth = auth->next;
+ free(old_auth);
+ }
+ init_auth_info(auth_list);
+}
+
+int is_unrecoverable(zhandle_t *zh)
+{
+ return (zh->state<0)? ZINVALIDSTATE: ZOK;
+}
+
+zk_hashtable *exists_result_checker(zhandle_t *zh, int rc)
+{
+ if (rc == ZOK) {
+ return zh->active_node_watchers;
+ } else if (rc == ZNONODE) {
+ return zh->active_exist_watchers;
+ }
+ return 0;
+}
+
+zk_hashtable *data_result_checker(zhandle_t *zh, int rc)
+{
+ return rc==ZOK ? zh->active_node_watchers : 0;
+}
+
+zk_hashtable *child_result_checker(zhandle_t *zh, int rc)
+{
+ return rc==ZOK ? zh->active_child_watchers : 0;
+}
+
+/**
+ * Frees and closes everything associated with a handle,
+ * including the handle itself.
+ */
+static void destroy(zhandle_t *zh)
+{
+ if (zh == NULL) {
+ return;
+ }
+ /* call any outstanding completions with a special error code */
+ cleanup_bufs(zh,1,ZCLOSING);
+ if (zh->hostname != 0) {
+ free(zh->hostname);
+ zh->hostname = NULL;
+ }
+ if (zh->fd != -1) {
+ close(zh->fd);
+ zh->fd = -1;
+ zh->state = 0;
+ }
+ if (zh->addrs != 0) {
+ free(zh->addrs);
+ zh->addrs = NULL;
+ }
+
+ if (zh->chroot != 0) {
+ free(zh->chroot);
+ zh->chroot = NULL;
+ }
+
+ free_auth_info(&zh->auth_h);
+ destroy_zk_hashtable(zh->active_node_watchers);
+ destroy_zk_hashtable(zh->active_exist_watchers);
+ destroy_zk_hashtable(zh->active_child_watchers);
+}
+
+static void setup_random()
+{
+#ifndef WIN32 // TODO: better seed
+ int seed;
+ int fd = open("/dev/urandom", O_RDONLY);
+ if (fd == -1) {
+ seed = getpid();
+ } else {
+ int seed_len = 0;
+
+ /* Enter a loop to fill in seed with random data from /dev/urandom.
+ * This is done in a loop so that we can safely handle short reads
+ * which can happen due to signal interruptions.
+ */
+ while (seed_len < sizeof(seed)) {
+ /* Assert we either read something or we were interrupted due to a
+ * signal (errno == EINTR) in which case we need to retry.
+ */
+ int rc = read(fd, &seed + seed_len, sizeof(seed) - seed_len);
+ assert(rc > 0 || errno == EINTR);
+ if (rc > 0) {
+ seed_len += rc;
+ }
+ }
+ close(fd);
+ }
+ srandom(seed);
+#endif
+}
+
+#ifndef __CYGWIN__
+/**
+ * get the errno from the return code
+ * of get addrinfo. Errno is not set
+ * with the call to getaddrinfo, so thats
+ * why we have to do this.
+ */
+static int getaddrinfo_errno(int rc) {
+ switch(rc) {
+ case EAI_NONAME:
+// ZOOKEEPER-1323 EAI_NODATA and EAI_ADDRFAMILY are deprecated in FreeBSD.
+#if defined EAI_NODATA && EAI_NODATA != EAI_NONAME
+ case EAI_NODATA:
+#endif
+ return ENOENT;
+ case EAI_MEMORY:
+ return ENOMEM;
+ default:
+ return EINVAL;
+ }
+}
+#endif
+
+/**
+ * fill in the addrs array of the zookeeper servers in the zhandle. after filling
+ * them in, we will permute them for load balancing.
+ */
+int getaddrs(zhandle_t *zh)
+{
+ char *hosts = strdup(zh->hostname);
+ char *host;
+ char *strtok_last;
+ struct sockaddr_storage *addr;
+ int i;
+ int rc;
+ int alen = 0; /* the allocated length of the addrs array */
+
+ zh->addrs_count = 0;
+ if (zh->addrs) {
+ free(zh->addrs);
+ zh->addrs = 0;
+ }
+ if (!hosts) {
+ LOG_ERROR(("out of memory"));
+ errno=ENOMEM;
+ return ZSYSTEMERROR;
+ }
+ zh->addrs = 0;
+ host=strtok_r(hosts, ",", &strtok_last);
+ while(host) {
+ char *port_spec = strrchr(host, ':');
+ char *end_port_spec;
+ int port;
+ if (!port_spec) {
+ LOG_ERROR(("no port in %s", host));
+ errno=EINVAL;
+ rc=ZBADARGUMENTS;
+ goto fail;
+ }
+ *port_spec = '\0';
+ port_spec++;
+ port = strtol(port_spec, &end_port_spec, 0);
+ if (!*port_spec || *end_port_spec || port == 0) {
+ LOG_ERROR(("invalid port in %s", host));
+ errno=EINVAL;
+ rc=ZBADARGUMENTS;
+ goto fail;
+ }
+#if defined(__CYGWIN__)
+ // sadly CYGWIN doesn't have getaddrinfo
+ // but happily gethostbyname is threadsafe in windows
+ {
+ struct hostent *he;
+ char **ptr;
+ struct sockaddr_in *addr4;
+
+ he = gethostbyname(host);
+ if (!he) {
+ LOG_ERROR(("could not resolve %s", host));
+ errno=ENOENT;
+ rc=ZBADARGUMENTS;
+ goto fail;
+ }
+
+ /* Setup the address array */
+ for(ptr = he->h_addr_list;*ptr != 0; ptr++) {
+ if (zh->addrs_count == alen) {
+ alen += 16;
+ zh->addrs = realloc(zh->addrs, sizeof(*zh->addrs)*alen);
+ if (zh->addrs == 0) {
+ LOG_ERROR(("out of memory"));
+ errno=ENOMEM;
+ rc=ZSYSTEMERROR;
+ goto fail;
+ }
+ }
+ addr = &zh->addrs[zh->addrs_count];
+ addr4 = (struct sockaddr_in*)addr;
+ addr->ss_family = he->h_addrtype;
+ if (addr->ss_family == AF_INET) {
+ addr4->sin_port = htons(port);
+ memset(&addr4->sin_zero, 0, sizeof(addr4->sin_zero));
+ memcpy(&addr4->sin_addr, *ptr, he->h_length);
+ zh->addrs_count++;
+ }
+#if defined(AF_INET6)
+ else if (addr->ss_family == AF_INET6) {
+ struct sockaddr_in6 *addr6;
+
+ addr6 = (struct sockaddr_in6*)addr;
+ addr6->sin6_port = htons(port);
+ addr6->sin6_scope_id = 0;
+ addr6->sin6_flowinfo = 0;
+ memcpy(&addr6->sin6_addr, *ptr, he->h_length);
+ zh->addrs_count++;
+ }
+#endif
+ else {
+ LOG_WARN(("skipping unknown address family %x for %s",
+ addr->ss_family, zh->hostname));
+ }
+ }
+ host = strtok_r(0, ",", &strtok_last);
+ }
+#else
+ {
+ struct addrinfo hints, *res, *res0;
+
+ memset(&hints, 0, sizeof(hints));
+#ifdef AI_ADDRCONFIG
+ hints.ai_flags = AI_ADDRCONFIG;
+#else
+ hints.ai_flags = 0;
+#endif
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ while(isspace(*host) && host != strtok_last)
+ host++;
+
+ if ((rc = getaddrinfo(host, port_spec, &hints, &res0)) != 0) {
+ //bug in getaddrinfo implementation when it returns
+ //EAI_BADFLAGS or EAI_ADDRFAMILY with AF_UNSPEC and
+ // ai_flags as AI_ADDRCONFIG
+#ifdef AI_ADDRCONFIG
+ if ((hints.ai_flags == AI_ADDRCONFIG) &&
+// ZOOKEEPER-1323 EAI_NODATA and EAI_ADDRFAMILY are deprecated in FreeBSD.
+#ifdef EAI_ADDRFAMILY
+ ((rc ==EAI_BADFLAGS) || (rc == EAI_ADDRFAMILY))) {
+#else
+ (rc == EAI_BADFLAGS)) {
+#endif
+ //reset ai_flags to null
+ hints.ai_flags = 0;
+ //retry getaddrinfo
+ rc = getaddrinfo(host, port_spec, &hints, &res0);
+ }
+#endif
+ if (rc != 0) {
+ errno = getaddrinfo_errno(rc);
+#ifdef WIN32
+ LOG_ERROR(("Win32 message: %s\n", gai_strerror(rc)));
+#else
+ LOG_ERROR(("getaddrinfo: %s\n", strerror(errno)));
+#endif
+ rc=ZSYSTEMERROR;
+ goto fail;
+ }
+ }
+
+ for (res = res0; res; res = res->ai_next) {
+ // Expand address list if needed
+ if (zh->addrs_count == alen) {
+ void *tmpaddr;
+ alen += 16;
+ tmpaddr = realloc(zh->addrs, sizeof(*zh->addrs)*alen);
+ if (tmpaddr == 0) {
+ LOG_ERROR(("out of memory"));
+ errno=ENOMEM;
+ rc=ZSYSTEMERROR;
+ goto fail;
+ }
+ zh->addrs=tmpaddr;
+ }
+
+ // Copy addrinfo into address list
+ addr = &zh->addrs[zh->addrs_count];
+ switch (res->ai_family) {
+ case AF_INET:
+#if defined(AF_INET6)
+ case AF_INET6:
+#endif
+ memcpy(addr, res->ai_addr, res->ai_addrlen);
+ ++zh->addrs_count;
+ break;
+ default:
+ LOG_WARN(("skipping unknown address family %x for %s",
+ res->ai_family, zh->hostname));
+ break;
+ }
+ }
+
+ freeaddrinfo(res0);
+
+ host = strtok_r(0, ",", &strtok_last);
+ }
+#endif
+ }
+ free(hosts);
+
+ if(!disable_conn_permute){
+ setup_random();
+ /* Permute */
+ for (i = zh->addrs_count - 1; i > 0; --i) {
+ long int j = random()%(i+1);
+ if (i != j) {
+ struct sockaddr_storage t = zh->addrs[i];
+ zh->addrs[i] = zh->addrs[j];
+ zh->addrs[j] = t;
+ }
+ }
+ }
+ return ZOK;
+fail:
+ if (zh->addrs) {
+ free(zh->addrs);
+ zh->addrs=0;
+ }
+ if (hosts) {
+ free(hosts);
+ }
+ return rc;
+}
+
+const clientid_t *zoo_client_id(zhandle_t *zh)
+{
+ return &zh->client_id;
+}
+
+static void null_watcher_fn(zhandle_t* p1, int p2, int p3,const char* p4,void*p5){}
+
+watcher_fn zoo_set_watcher(zhandle_t *zh,watcher_fn newFn)
+{
+ watcher_fn oldWatcher=zh->watcher;
+ if (newFn) {
+ zh->watcher = newFn;
+ } else {
+ zh->watcher = null_watcher_fn;
+ }
+ return oldWatcher;
+}
+
+struct sockaddr* zookeeper_get_connected_host(zhandle_t *zh,
+ struct sockaddr *addr, socklen_t *addr_len)
+{
+ if (zh->state!=ZOO_CONNECTED_STATE) {
+ return NULL;
+ }
+ if (getpeername(zh->fd, addr, addr_len)==-1) {
+ return NULL;
+ }
+ return addr;
+}
+
+static void log_env() {
+ char buf[2048];
+#ifdef HAVE_SYS_UTSNAME_H
+ struct utsname utsname;
+#endif
+
+#if defined(HAVE_GETUID) && defined(HAVE_GETPWUID_R)
+ struct passwd pw;
+ struct passwd *pwp = NULL;
+ uid_t uid = 0;
+#endif
+
+ LOG_INFO(("Client environment:zookeeper.version=%s", PACKAGE_STRING));
+
+#ifdef HAVE_GETHOSTNAME
+ gethostname(buf, sizeof(buf));
+ LOG_INFO(("Client environment:host.name=%s", buf));
+#else
+ LOG_INFO(("Client environment:host.name="));
+#endif
+
+#ifdef HAVE_SYS_UTSNAME_H
+ uname(&utsname);
+ LOG_INFO(("Client environment:os.name=%s", utsname.sysname));
+ LOG_INFO(("Client environment:os.arch=%s", utsname.release));
+ LOG_INFO(("Client environment:os.version=%s", utsname.version));
+#else
+ LOG_INFO(("Client environment:os.name="));
+ LOG_INFO(("Client environment:os.arch="));
+ LOG_INFO(("Client environment:os.version="));
+#endif
+
+#ifdef HAVE_GETLOGIN
+ LOG_INFO(("Client environment:user.name=%s", getlogin()));
+#else
+ LOG_INFO(("Client environment:user.name="));
+#endif
+
+#if defined(HAVE_GETUID) && defined(HAVE_GETPWUID_R)
+ uid = getuid();
+ if (!getpwuid_r(uid, &pw, buf, sizeof(buf), &pwp)) {
+ LOG_INFO(("Client environment:user.home=%s", pw.pw_dir));
+ } else {
+ LOG_INFO(("Client environment:user.home="));
+ }
+#else
+ LOG_INFO(("Client environment:user.home="));
+#endif
+
+#ifdef HAVE_GETCWD
+ if (!getcwd(buf, sizeof(buf))) {
+ LOG_INFO(("Client environment:user.dir="));
+ } else {
+ LOG_INFO(("Client environment:user.dir=%s", buf));
+ }
+#else
+ LOG_INFO(("Client environment:user.dir="));
+#endif
+}
+
+/**
+ * Create a zookeeper handle associated with the given host and port.
+ */
+zhandle_t *zookeeper_init(const char *host, watcher_fn watcher,
+ int recv_timeout, const clientid_t *clientid, void *context, int flags)
+{
+ int errnosave = 0;
+ zhandle_t *zh = NULL;
+ char *index_chroot = NULL;
+
+ log_env();
+#ifdef WIN32
+ if (Win32WSAStartup()){
+ LOG_ERROR(("Error initializing ws2_32.dll"));
+ return 0;
+ }
+#endif
+ LOG_INFO(("Initiating client connection, host=%s sessionTimeout=%d watcher=%p"
+ " sessionId=%#llx sessionPasswd=%s context=%p flags=%d",
+ host,
+ recv_timeout,
+ watcher,
+ (clientid == 0 ? 0 : clientid->client_id),
+ ((clientid == 0) || (clientid->passwd[0] == 0) ?
+ "" : ""),
+ context,
+ flags));
+
+ zh = calloc(1, sizeof(*zh));
+ if (!zh) {
+ return 0;
+ }
+ zh->fd = -1;
+ zh->state = NOTCONNECTED_STATE_DEF;
+ zh->context = context;
+ zh->recv_timeout = recv_timeout;
+ init_auth_info(&zh->auth_h);
+ if (watcher) {
+ zh->watcher = watcher;
+ } else {
+ zh->watcher = null_watcher_fn;
+ }
+ if (host == 0 || *host == 0) { // what we shouldn't dup
+ errno=EINVAL;
+ goto abort;
+ }
+ //parse the host to get the chroot if
+ //available
+ index_chroot = strchr(host, '/');
+ if (index_chroot) {
+ zh->chroot = strdup(index_chroot);
+ if (zh->chroot == NULL) {
+ goto abort;
+ }
+ // if chroot is just / set it to null
+ if (strlen(zh->chroot) == 1) {
+ free(zh->chroot);
+ zh->chroot = NULL;
+ }
+ // cannot use strndup so allocate and strcpy
+ zh->hostname = (char *) malloc(index_chroot - host + 1);
+ zh->hostname = strncpy(zh->hostname, host, (index_chroot - host));
+ //strncpy does not null terminate
+ *(zh->hostname + (index_chroot - host)) = '\0';
+
+ } else {
+ zh->chroot = NULL;
+ zh->hostname = strdup(host);
+ }
+ if (zh->chroot && !isValidPath(zh->chroot, 0)) {
+ errno = EINVAL;
+ goto abort;
+ }
+ if (zh->hostname == 0) {
+ goto abort;
+ }
+ if(getaddrs(zh)!=0) {
+ goto abort;
+ }
+ zh->connect_index = 0;
+ if (clientid) {
+ memcpy(&zh->client_id, clientid, sizeof(zh->client_id));
+ } else {
+ memset(&zh->client_id, 0, sizeof(zh->client_id));
+ }
+ zh->primer_buffer.buffer = zh->primer_storage_buffer;
+ zh->primer_buffer.curr_offset = 0;
+ zh->primer_buffer.len = sizeof(zh->primer_storage_buffer);
+ zh->primer_buffer.next = 0;
+ zh->last_zxid = 0;
+ zh->next_deadline.tv_sec=zh->next_deadline.tv_usec=0;
+ zh->socket_readable.tv_sec=zh->socket_readable.tv_usec=0;
+ zh->active_node_watchers=create_zk_hashtable();
+ zh->active_exist_watchers=create_zk_hashtable();
+ zh->active_child_watchers=create_zk_hashtable();
+
+ if (adaptor_init(zh) == -1) {
+ goto abort;
+ }
+
+ return zh;
+abort:
+ errnosave=errno;
+ destroy(zh);
+ free(zh);
+ errno=errnosave;
+ return 0;
+}
+
+/**
+ * deallocated the free_path only its beeen allocated
+ * and not equal to path
+ */
+void free_duplicate_path(const char *free_path, const char* path) {
+ if (free_path != path) {
+ free((void*)free_path);
+ }
+}
+
+/**
+ prepend the chroot path if available else return the path
+*/
+static char* prepend_string(zhandle_t *zh, const char* client_path) {
+ char *ret_str;
+ if (zh == NULL || zh->chroot == NULL)
+ return (char *) client_path;
+ // handle the chroot itself, client_path = "/"
+ if (strlen(client_path) == 1) {
+ return strdup(zh->chroot);
+ }
+ ret_str = (char *) malloc(strlen(zh->chroot) + strlen(client_path) + 1);
+ strcpy(ret_str, zh->chroot);
+ return strcat(ret_str, client_path);
+}
+
+/**
+ strip off the chroot string from the server path
+ if there is one else return the exact path
+ */
+char* sub_string(zhandle_t *zh, const char* server_path) {
+ char *ret_str;
+ if (zh->chroot == NULL)
+ return (char *) server_path;
+ //ZOOKEEPER-1027
+ if (strncmp(server_path, zh->chroot, strlen(zh->chroot)) != 0) {
+ LOG_ERROR(("server path %s does not include chroot path %s",
+ server_path, zh->chroot));
+ return (char *) server_path;
+ }
+ if (strlen(server_path) == strlen(zh->chroot)) {
+ //return "/"
+ ret_str = strdup("/");
+ return ret_str;
+ }
+ ret_str = strdup(server_path + strlen(zh->chroot));
+ return ret_str;
+}
+
+static buffer_list_t *allocate_buffer(char *buff, int len)
+{
+ buffer_list_t *buffer = calloc(1, sizeof(*buffer));
+ if (buffer == 0)
+ return 0;
+
+ buffer->len = len==0?sizeof(*buffer):len;
+ buffer->curr_offset = 0;
+ buffer->buffer = buff;
+ buffer->next = 0;
+ return buffer;
+}
+
+static void free_buffer(buffer_list_t *b)
+{
+ if (!b) {
+ return;
+ }
+ if (b->buffer) {
+ free(b->buffer);
+ }
+ free(b);
+}
+
+static buffer_list_t *dequeue_buffer(buffer_head_t *list)
+{
+ buffer_list_t *b;
+ lock_buffer_list(list);
+ b = list->head;
+ if (b) {
+ list->head = b->next;
+ if (!list->head) {
+ assert(b == list->last);
+ list->last = 0;
+ }
+ }
+ unlock_buffer_list(list);
+ return b;
+}
+
+static int remove_buffer(buffer_head_t *list)
+{
+ buffer_list_t *b = dequeue_buffer(list);
+ if (!b) {
+ return 0;
+ }
+ free_buffer(b);
+ return 1;
+}
+
+static void queue_buffer(buffer_head_t *list, buffer_list_t *b, int add_to_front)
+{
+ b->next = 0;
+ lock_buffer_list(list);
+ if (list->head) {
+ assert(list->last);
+ // The list is not empty
+ if (add_to_front) {
+ b->next = list->head;
+ list->head = b;
+ } else {
+ list->last->next = b;
+ list->last = b;
+ }
+ }else{
+ // The list is empty
+ assert(!list->head);
+ list->head = b;
+ list->last = b;
+ }
+ unlock_buffer_list(list);
+}
+
+static int queue_buffer_bytes(buffer_head_t *list, char *buff, int len)
+{
+ buffer_list_t *b = allocate_buffer(buff,len);
+ if (!b)
+ return ZSYSTEMERROR;
+ queue_buffer(list, b, 0);
+ return ZOK;
+}
+
+static int queue_front_buffer_bytes(buffer_head_t *list, char *buff, int len)
+{
+ buffer_list_t *b = allocate_buffer(buff,len);
+ if (!b)
+ return ZSYSTEMERROR;
+ queue_buffer(list, b, 1);
+ return ZOK;
+}
+
+static __attribute__ ((unused)) int get_queue_len(buffer_head_t *list)
+{
+ int i;
+ buffer_list_t *ptr;
+ lock_buffer_list(list);
+ ptr = list->head;
+ for (i=0; ptr!=0; ptr=ptr->next, i++)
+ ;
+ unlock_buffer_list(list);
+ return i;
+}
+/* returns:
+ * -1 if send failed,
+ * 0 if send would block while sending the buffer (or a send was incomplete),
+ * 1 if success
+ */
+#ifdef WIN32
+static int send_buffer(SOCKET fd, buffer_list_t *buff)
+#else
+static int send_buffer(int fd, buffer_list_t *buff)
+#endif
+{
+ int len = buff->len;
+ int off = buff->curr_offset;
+ int rc = -1;
+
+ if (off < 4) {
+ /* we need to send the length at the beginning */
+ int nlen = htonl(len);
+ char *b = (char*)&nlen;
+ rc = zookeeper_send(fd, b + off, sizeof(nlen) - off);
+ if (rc == -1) {
+#ifndef _WINDOWS
+ if (errno != EAGAIN) {
+#else
+ if (WSAGetLastError() != WSAEWOULDBLOCK) {
+#endif
+ return -1;
+ } else {
+ return 0;
+ }
+ } else {
+ buff->curr_offset += rc;
+ }
+ off = buff->curr_offset;
+ }
+ if (off >= 4) {
+ /* want off to now represent the offset into the buffer */
+ off -= sizeof(buff->len);
+ rc = zookeeper_send(fd, buff->buffer + off, len - off);
+ if (rc == -1) {
+#ifndef _WINDOWS
+ if (errno != EAGAIN) {
+#else
+ if (WSAGetLastError() != WSAEWOULDBLOCK) {
+#endif
+ return -1;
+ }
+ } else {
+ buff->curr_offset += rc;
+ }
+ }
+ return buff->curr_offset == len + sizeof(buff->len);
+}
+
+/* returns:
+ * -1 if recv call failed,
+ * 0 if recv would block,
+ * 1 if success
+ */
+#ifdef WIN32
+static int recv_buffer(SOCKET fd, buffer_list_t *buff)
+#else
+static int recv_buffer(int fd, buffer_list_t *buff)
+#endif
+{
+ int off = buff->curr_offset;
+ int rc = 0;
+ //fprintf(LOGSTREAM, "rc = %d, off = %d, line %d\n", rc, off, __LINE__);
+
+ /* if buffer is less than 4, we are reading in the length */
+ if (off < 4) {
+ char *buffer = (char*)&(buff->len);
+ rc = recv(fd, buffer+off, sizeof(int)-off, 0);
+ //fprintf(LOGSTREAM, "rc = %d, off = %d, line %d\n", rc, off, __LINE__);
+ switch(rc) {
+ case 0:
+ errno = EHOSTDOWN;
+ case -1:
+#ifndef _WINDOWS
+ if (errno == EAGAIN) {
+#else
+ if (WSAGetLastError() == WSAEWOULDBLOCK) {
+#endif
+ return 0;
+ }
+ return -1;
+ default:
+ buff->curr_offset += rc;
+ }
+ off = buff->curr_offset;
+ if (buff->curr_offset == sizeof(buff->len)) {
+ buff->len = ntohl(buff->len);
+ buff->buffer = calloc(1, buff->len);
+ }
+ }
+ if (buff->buffer) {
+ /* want off to now represent the offset into the buffer */
+ off -= sizeof(buff->len);
+
+ rc = recv(fd, buff->buffer+off, buff->len-off, 0);
+ switch(rc) {
+ case 0:
+ errno = EHOSTDOWN;
+ case -1:
+#ifndef _WINDOWS
+ if (errno == EAGAIN) {
+#else
+ if (WSAGetLastError() == WSAEWOULDBLOCK) {
+#endif
+ break;
+ }
+ return -1;
+ default:
+ buff->curr_offset += rc;
+ }
+ }
+ return buff->curr_offset == buff->len + sizeof(buff->len);
+}
+
+void free_buffers(buffer_head_t *list)
+{
+ while (remove_buffer(list))
+ ;
+}
+
+void free_completions(zhandle_t *zh,int callCompletion,int reason)
+{
+ completion_head_t tmp_list;
+ struct oarchive *oa;
+ struct ReplyHeader h;
+ void_completion_t auth_completion = NULL;
+ auth_completion_list_t a_list, *a_tmp;
+
+ if (lock_completion_list(&zh->sent_requests) == 0) {
+ tmp_list = zh->sent_requests;
+ zh->sent_requests.head = 0;
+ zh->sent_requests.last = 0;
+ unlock_completion_list(&zh->sent_requests);
+
+ while (tmp_list.head) {
+ completion_list_t *cptr = tmp_list.head;
+
+ tmp_list.head = cptr->next;
+ if (cptr->c.data_result == SYNCHRONOUS_MARKER) {
+ struct sync_completion
+ *sc = (struct sync_completion*)cptr->data;
+ sc->rc = reason;
+ notify_sync_completion(sc);
+ zh->outstanding_sync--;
+ destroy_completion_entry(cptr);
+ } else if (callCompletion) {
+ // Fake the response
+ buffer_list_t *bptr;
+ h.xid = cptr->xid;
+ h.zxid = -1;
+ h.err = reason;
+ oa = create_buffer_oarchive();
+ serialize_ReplyHeader(oa, "header", &h);
+ bptr = calloc(sizeof(*bptr), 1);
+ assert(bptr);
+ bptr->len = get_buffer_len(oa);
+ bptr->buffer = get_buffer(oa);
+ close_buffer_oarchive(&oa, 0);
+ cptr->buffer = bptr;
+ queue_completion(&zh->completions_to_process, cptr, 0);
+ }
+ }
+ }
+ if (zoo_lock_auth(zh) == 0) {
+ a_list.completion = NULL;
+ a_list.next = NULL;
+
+ get_auth_completions(&zh->auth_h, &a_list);
+ zoo_unlock_auth(zh);
+
+ a_tmp = &a_list;
+ // chain call user's completion function
+ while (a_tmp->completion != NULL) {
+ auth_completion = a_tmp->completion;
+ auth_completion(reason, a_tmp->auth_data);
+ a_tmp = a_tmp->next;
+ if (a_tmp == NULL)
+ break;
+ }
+ }
+ free_auth_completion(&a_list);
+}
+
+static void cleanup_bufs(zhandle_t *zh,int callCompletion,int rc)
+{
+ enter_critical(zh);
+ free_buffers(&zh->to_send);
+ free_buffers(&zh->to_process);
+ free_completions(zh,callCompletion,rc);
+ leave_critical(zh);
+ if (zh->input_buffer && zh->input_buffer != &zh->primer_buffer) {
+ free_buffer(zh->input_buffer);
+ zh->input_buffer = 0;
+ }
+}
+
+static void handle_error(zhandle_t *zh,int rc)
+{
+ close(zh->fd);
+ if (is_unrecoverable(zh)) {
+ LOG_DEBUG(("Calling a watcher for a ZOO_SESSION_EVENT and the state=%s",
+ state2String(zh->state)));
+ PROCESS_SESSION_EVENT(zh, zh->state);
+ } else if (zh->state == ZOO_CONNECTED_STATE) {
+ LOG_DEBUG(("Calling a watcher for a ZOO_SESSION_EVENT and the state=CONNECTING_STATE"));
+ PROCESS_SESSION_EVENT(zh, ZOO_CONNECTING_STATE);
+ }
+ cleanup_bufs(zh,1,rc);
+ zh->fd = -1;
+ zh->connect_index++;
+ if (!is_unrecoverable(zh)) {
+ zh->state = 0;
+ }
+ if (process_async(zh->outstanding_sync)) {
+ process_completions(zh);
+ }
+}
+
+static int handle_socket_error_msg(zhandle_t *zh, int line, int rc,
+ const char* format, ...)
+{
+ if(logLevel>=ZOO_LOG_LEVEL_ERROR){
+ va_list va;
+ char buf[1024];
+ va_start(va,format);
+ vsnprintf(buf, sizeof(buf)-1,format,va);
+ log_message(ZOO_LOG_LEVEL_ERROR,line,__func__,
+ format_log_message("Socket [%s] zk retcode=%d, errno=%d(%s): %s",
+ format_current_endpoint_info(zh),rc,errno,strerror(errno),buf));
+ va_end(va);
+ }
+ handle_error(zh,rc);
+ return rc;
+}
+
+static void auth_completion_func(int rc, zhandle_t* zh)
+{
+ void_completion_t auth_completion = NULL;
+ auth_completion_list_t a_list;
+ auth_completion_list_t *a_tmp;
+
+ if(zh==NULL)
+ return;
+
+ zoo_lock_auth(zh);
+
+ if(rc!=0){
+ zh->state=ZOO_AUTH_FAILED_STATE;
+ }else{
+ //change state for all auths
+ mark_active_auth(zh);
+ }
+ a_list.completion = NULL;
+ a_list.next = NULL;
+ get_auth_completions(&zh->auth_h, &a_list);
+ zoo_unlock_auth(zh);
+ if (rc) {
+ LOG_ERROR(("Authentication scheme %s failed. Connection closed.",
+ zh->auth_h.auth->scheme));
+ }
+ else {
+ LOG_INFO(("Authentication scheme %s succeeded", zh->auth_h.auth->scheme));
+ }
+ a_tmp = &a_list;
+ // chain call user's completion function
+ while (a_tmp->completion != NULL) {
+ auth_completion = a_tmp->completion;
+ auth_completion(rc, a_tmp->auth_data);
+ a_tmp = a_tmp->next;
+ if (a_tmp == NULL)
+ break;
+ }
+ free_auth_completion(&a_list);
+}
+
+static int send_info_packet(zhandle_t *zh, auth_info* auth) {
+ struct oarchive *oa;
+ struct RequestHeader h = { STRUCT_INITIALIZER(xid , AUTH_XID), STRUCT_INITIALIZER(type , ZOO_SETAUTH_OP)};
+ struct AuthPacket req;
+ int rc;
+ oa = create_buffer_oarchive();
+ rc = serialize_RequestHeader(oa, "header", &h);
+ req.type=0; // ignored by the server
+ req.scheme = auth->scheme;
+ req.auth = auth->auth;
+ rc = rc < 0 ? rc : serialize_AuthPacket(oa, "req", &req);
+ /* add this buffer to the head of the send queue */
+ rc = rc < 0 ? rc : queue_front_buffer_bytes(&zh->to_send, get_buffer(oa),
+ get_buffer_len(oa));
+ /* We queued the buffer, so don't free it */
+ close_buffer_oarchive(&oa, 0);
+
+ return rc;
+}
+
+/** send all auths, not just the last one **/
+static int send_auth_info(zhandle_t *zh) {
+ int rc = 0;
+ auth_info *auth = NULL;
+
+ zoo_lock_auth(zh);
+ auth = zh->auth_h.auth;
+ if (auth == NULL) {
+ zoo_unlock_auth(zh);
+ return ZOK;
+ }
+ while (auth != NULL) {
+ rc = send_info_packet(zh, auth);
+ auth = auth->next;
+ }
+ zoo_unlock_auth(zh);
+ LOG_DEBUG(("Sending all auth info request to %s", format_current_endpoint_info(zh)));
+ return (rc <0) ? ZMARSHALLINGERROR:ZOK;
+}
+
+static int send_last_auth_info(zhandle_t *zh)
+{
+ int rc = 0;
+ auth_info *auth = NULL;
+
+ zoo_lock_auth(zh);
+ auth = get_last_auth(&zh->auth_h);
+ if(auth==NULL) {
+ zoo_unlock_auth(zh);
+ return ZOK; // there is nothing to send
+ }
+ rc = send_info_packet(zh, auth);
+ zoo_unlock_auth(zh);
+ LOG_DEBUG(("Sending auth info request to %s",format_current_endpoint_info(zh)));
+ return (rc < 0)?ZMARSHALLINGERROR:ZOK;
+}
+
+static void free_key_list(char **list, int count)
+{
+ int i;
+
+ for(i = 0; i < count; i++) {
+ free(list[i]);
+ }
+ free(list);
+}
+
+static int send_set_watches(zhandle_t *zh)
+{
+ struct oarchive *oa;
+ struct RequestHeader h = { STRUCT_INITIALIZER(xid , SET_WATCHES_XID), STRUCT_INITIALIZER(type , ZOO_SETWATCHES_OP)};
+ struct SetWatches req;
+ int rc;
+
+ req.relativeZxid = zh->last_zxid;
+ req.dataWatches.data = collect_keys(zh->active_node_watchers, (int*)&req.dataWatches.count);
+ req.existWatches.data = collect_keys(zh->active_exist_watchers, (int*)&req.existWatches.count);
+ req.childWatches.data = collect_keys(zh->active_child_watchers, (int*)&req.childWatches.count);
+
+ // return if there are no pending watches
+ if (!req.dataWatches.count && !req.existWatches.count &&
+ !req.childWatches.count) {
+ free_key_list(req.dataWatches.data, req.dataWatches.count);
+ free_key_list(req.existWatches.data, req.existWatches.count);
+ free_key_list(req.childWatches.data, req.childWatches.count);
+ return ZOK;
+ }
+
+
+ oa = create_buffer_oarchive();
+ rc = serialize_RequestHeader(oa, "header", &h);
+ rc = rc < 0 ? rc : serialize_SetWatches(oa, "req", &req);
+ /* add this buffer to the head of the send queue */
+ rc = rc < 0 ? rc : queue_front_buffer_bytes(&zh->to_send, get_buffer(oa),
+ get_buffer_len(oa));
+ /* We queued the buffer, so don't free it */
+ close_buffer_oarchive(&oa, 0);
+ free_key_list(req.dataWatches.data, req.dataWatches.count);
+ free_key_list(req.existWatches.data, req.existWatches.count);
+ free_key_list(req.childWatches.data, req.childWatches.count);
+ LOG_DEBUG(("Sending set watches request to %s",format_current_endpoint_info(zh)));
+ return (rc < 0)?ZMARSHALLINGERROR:ZOK;
+}
+
+static int serialize_prime_connect(struct connect_req *req, char* buffer){
+ //this should be the order of serialization
+ int offset = 0;
+ req->protocolVersion = htonl(req->protocolVersion);
+ memcpy(buffer + offset, &req->protocolVersion, sizeof(req->protocolVersion));
+ offset = offset + sizeof(req->protocolVersion);
+
+ req->lastZxidSeen = zoo_htonll(req->lastZxidSeen);
+ memcpy(buffer + offset, &req->lastZxidSeen, sizeof(req->lastZxidSeen));
+ offset = offset + sizeof(req->lastZxidSeen);
+
+ req->timeOut = htonl(req->timeOut);
+ memcpy(buffer + offset, &req->timeOut, sizeof(req->timeOut));
+ offset = offset + sizeof(req->timeOut);
+
+ req->sessionId = zoo_htonll(req->sessionId);
+ memcpy(buffer + offset, &req->sessionId, sizeof(req->sessionId));
+ offset = offset + sizeof(req->sessionId);
+
+ req->passwd_len = htonl(req->passwd_len);
+ memcpy(buffer + offset, &req->passwd_len, sizeof(req->passwd_len));
+ offset = offset + sizeof(req->passwd_len);
+
+ memcpy(buffer + offset, req->passwd, sizeof(req->passwd));
+
+ return 0;
+}
+
+ static int deserialize_prime_response(struct prime_struct *req, char* buffer){
+ //this should be the order of deserialization
+ int offset = 0;
+ memcpy(&req->len, buffer + offset, sizeof(req->len));
+ offset = offset + sizeof(req->len);
+
+ req->len = ntohl(req->len);
+ memcpy(&req->protocolVersion, buffer + offset, sizeof(req->protocolVersion));
+ offset = offset + sizeof(req->protocolVersion);
+
+ req->protocolVersion = ntohl(req->protocolVersion);
+ memcpy(&req->timeOut, buffer + offset, sizeof(req->timeOut));
+ offset = offset + sizeof(req->timeOut);
+
+ req->timeOut = ntohl(req->timeOut);
+ memcpy(&req->sessionId, buffer + offset, sizeof(req->sessionId));
+ offset = offset + sizeof(req->sessionId);
+
+ req->sessionId = zoo_htonll(req->sessionId);
+ memcpy(&req->passwd_len, buffer + offset, sizeof(req->passwd_len));
+ offset = offset + sizeof(req->passwd_len);
+
+ req->passwd_len = ntohl(req->passwd_len);
+ memcpy(req->passwd, buffer + offset, sizeof(req->passwd));
+ return 0;
+ }
+
+static int prime_connection(zhandle_t *zh)
+{
+ int rc;
+ /*this is the size of buffer to serialize req into*/
+ char buffer_req[HANDSHAKE_REQ_SIZE];
+ int len = sizeof(buffer_req);
+ int hlen = 0;
+ struct connect_req req;
+ req.protocolVersion = 0;
+ req.sessionId = zh->client_id.client_id;
+ req.passwd_len = sizeof(req.passwd);
+ memcpy(req.passwd, zh->client_id.passwd, sizeof(zh->client_id.passwd));
+ req.timeOut = zh->recv_timeout;
+ req.lastZxidSeen = zh->last_zxid;
+ hlen = htonl(len);
+ /* We are running fast and loose here, but this string should fit in the initial buffer! */
+ rc=zookeeper_send(zh->fd, &hlen, sizeof(len));
+ serialize_prime_connect(&req, buffer_req);
+ rc=rc<0 ? rc : zookeeper_send(zh->fd, buffer_req, len);
+ if (rc<0) {
+ return handle_socket_error_msg(zh, __LINE__, ZCONNECTIONLOSS,
+ "failed to send a handshake packet: %s", strerror(errno));
+ }
+ zh->state = ZOO_ASSOCIATING_STATE;
+
+ zh->input_buffer = &zh->primer_buffer;
+ /* This seems a bit weird to to set the offset to 4, but we already have a
+ * length, so we skip reading the length (and allocating the buffer) by
+ * saying that we are already at offset 4 */
+ zh->input_buffer->curr_offset = 4;
+
+ return ZOK;
+}
+
+static inline int calculate_interval(const struct timeval *start,
+ const struct timeval *end)
+{
+ int interval;
+ struct timeval i = *end;
+ i.tv_sec -= start->tv_sec;
+ i.tv_usec -= start->tv_usec;
+ interval = i.tv_sec * 1000 + (i.tv_usec/1000);
+ return interval;
+}
+
+static struct timeval get_timeval(int interval)
+{
+ struct timeval tv;
+ if (interval < 0) {
+ interval = 0;
+ }
+ tv.tv_sec = interval/1000;
+ tv.tv_usec = (interval%1000)*1000;
+ return tv;
+}
+
+ static int add_void_completion(zhandle_t *zh, int xid, void_completion_t dc,
+ const void *data);
+ static int add_string_completion(zhandle_t *zh, int xid,
+ string_completion_t dc, const void *data);
+
+ int send_ping(zhandle_t* zh)
+ {
+ int rc;
+ struct oarchive *oa = create_buffer_oarchive();
+ struct RequestHeader h = { STRUCT_INITIALIZER(xid ,PING_XID), STRUCT_INITIALIZER (type , ZOO_PING_OP) };
+
+ rc = serialize_RequestHeader(oa, "header", &h);
+ enter_critical(zh);
+ gettimeofday(&zh->last_ping, 0);
+ rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
+ get_buffer_len(oa));
+ leave_critical(zh);
+ close_buffer_oarchive(&oa, 0);
+ return rc<0 ? rc : adaptor_send_queue(zh, 0);
+}
+
+#ifdef WIN32
+int zookeeper_interest(zhandle_t *zh, SOCKET *fd, int *interest,
+ struct timeval *tv)
+{
+
+ ULONG nonblocking_flag = 1;
+#else
+int zookeeper_interest(zhandle_t *zh, int *fd, int *interest,
+ struct timeval *tv)
+{
+#endif
+ struct timeval now;
+ if(zh==0 || fd==0 ||interest==0 || tv==0)
+ return ZBADARGUMENTS;
+ if (is_unrecoverable(zh))
+ return ZINVALIDSTATE;
+ gettimeofday(&now, 0);
+ if(zh->next_deadline.tv_sec!=0 || zh->next_deadline.tv_usec!=0){
+ int time_left = calculate_interval(&zh->next_deadline, &now);
+ if (time_left > 10)
+ LOG_WARN(("Exceeded deadline by %dms", time_left));
+ }
+ api_prolog(zh);
+ *fd = zh->fd;
+ *interest = 0;
+ tv->tv_sec = 0;
+ tv->tv_usec = 0;
+ if (*fd == -1) {
+ if (zh->connect_index == zh->addrs_count) {
+ /* Wait a bit before trying again so that we don't spin */
+ zh->connect_index = 0;
+ }else {
+ int rc;
+#ifdef WIN32
+ char enable_tcp_nodelay = 1;
+#else
+ int enable_tcp_nodelay = 1;
+#endif
+ int ssoresult;
+
+ zh->fd = socket(zh->addrs[zh->connect_index].ss_family, SOCK_STREAM, 0);
+ if (zh->fd < 0) {
+ return api_epilog(zh,handle_socket_error_msg(zh,__LINE__,
+ ZSYSTEMERROR, "socket() call failed"));
+ }
+ ssoresult = setsockopt(zh->fd, IPPROTO_TCP, TCP_NODELAY, &enable_tcp_nodelay, sizeof(enable_tcp_nodelay));
+ if (ssoresult != 0) {
+ LOG_WARN(("Unable to set TCP_NODELAY, operation latency may be effected"));
+ }
+#ifdef WIN32
+ ioctlsocket(zh->fd, FIONBIO, &nonblocking_flag);
+#else
+ fcntl(zh->fd, F_SETFL, O_NONBLOCK|fcntl(zh->fd, F_GETFL, 0));
+#endif
+#if defined(AF_INET6)
+ if (zh->addrs[zh->connect_index].ss_family == AF_INET6) {
+ rc = connect(zh->fd, (struct sockaddr*) &zh->addrs[zh->connect_index], sizeof(struct sockaddr_in6));
+ } else {
+#else
+ LOG_DEBUG(("[zk] connect()\n"));
+ {
+#endif
+ rc = connect(zh->fd, (struct sockaddr*) &zh->addrs[zh->connect_index], sizeof(struct sockaddr_in));
+#ifdef WIN32
+ errno = GetLastError();
+
+#ifndef EWOULDBLOCK
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#endif
+
+#ifndef EINPROGRESS
+#define EINPROGRESS WSAEINPROGRESS
+#endif
+
+#if _MSC_VER >= 1600
+ switch (errno) {
+ case WSAEWOULDBLOCK:
+ errno = EWOULDBLOCK;
+ break;
+ case WSAEINPROGRESS:
+ errno = EINPROGRESS;
+ break;
+ }
+#endif
+#endif
+ }
+ if (rc == -1) {
+ /* we are handling the non-blocking connect according to
+ * the description in section 16.3 "Non-blocking connect"
+ * in UNIX Network Programming vol 1, 3rd edition */
+ if (errno == EWOULDBLOCK || errno == EINPROGRESS)
+ zh->state = ZOO_CONNECTING_STATE;
+ else
+ return api_epilog(zh,handle_socket_error_msg(zh,__LINE__,
+ ZCONNECTIONLOSS,"connect() call failed"));
+ } else {
+ if((rc=prime_connection(zh))!=0)
+ return api_epilog(zh,rc);
+
+ LOG_INFO(("Initiated connection to server [%s]",
+ format_endpoint_info(&zh->addrs[zh->connect_index])));
+ }
+ }
+ *fd = zh->fd;
+ *tv = get_timeval(zh->recv_timeout/3);
+ zh->last_recv = now;
+ zh->last_send = now;
+ zh->last_ping = now;
+ }
+ if (zh->fd != -1) {
+ int idle_recv = calculate_interval(&zh->last_recv, &now);
+ int idle_send = calculate_interval(&zh->last_send, &now);
+ int recv_to = zh->recv_timeout*2/3 - idle_recv;
+ int send_to = zh->recv_timeout/3;
+ // have we exceeded the receive timeout threshold?
+ if (recv_to <= 0) {
+ // We gotta cut our losses and connect to someone else
+#ifdef WIN32
+ errno = WSAETIMEDOUT;
+#else
+ errno = ETIMEDOUT;
+#endif
+ *interest=0;
+ *tv = get_timeval(0);
+ return api_epilog(zh,handle_socket_error_msg(zh,
+ __LINE__,ZOPERATIONTIMEOUT,
+ "connection to %s timed out (exceeded timeout by %dms)",
+ format_endpoint_info(&zh->addrs[zh->connect_index]),
+ -recv_to));
+
+ }
+ // We only allow 1/3 of our timeout time to expire before sending
+ // a PING
+ if (zh->state==ZOO_CONNECTED_STATE) {
+ send_to = zh->recv_timeout/3 - idle_send;
+ if (send_to <= 0) {
+ if (zh->sent_requests.head==0) {
+// LOG_DEBUG(("Sending PING to %s (exceeded idle by %dms)",
+// format_current_endpoint_info(zh),-send_to));
+ int rc=send_ping(zh);
+ if (rc < 0){
+ LOG_ERROR(("failed to send PING request (zk retcode=%d)",rc));
+ return api_epilog(zh,rc);
+ }
+ }
+ send_to = zh->recv_timeout/3;
+ }
+ }
+ // choose the lesser value as the timeout
+ *tv = get_timeval(recv_to < send_to? recv_to:send_to);
+ zh->next_deadline.tv_sec = now.tv_sec + tv->tv_sec;
+ zh->next_deadline.tv_usec = now.tv_usec + tv->tv_usec;
+ if (zh->next_deadline.tv_usec > 1000000) {
+ zh->next_deadline.tv_sec += zh->next_deadline.tv_usec / 1000000;
+ zh->next_deadline.tv_usec = zh->next_deadline.tv_usec % 1000000;
+ }
+ *interest = ZOOKEEPER_READ;
+ /* we are interested in a write if we are connected and have something
+ * to send, or we are waiting for a connect to finish. */
+ if ((zh->to_send.head && (zh->state == ZOO_CONNECTED_STATE))
+ || zh->state == ZOO_CONNECTING_STATE) {
+ *interest |= ZOOKEEPER_WRITE;
+ }
+ }
+ return api_epilog(zh,ZOK);
+}
+
+static int check_events(zhandle_t *zh, int events)
+{
+ if (zh->fd == -1)
+ return ZINVALIDSTATE;
+ if ((events&ZOOKEEPER_WRITE)&&(zh->state == ZOO_CONNECTING_STATE)) {
+ int rc, error;
+ socklen_t len = sizeof(error);
+ rc = getsockopt(zh->fd, SOL_SOCKET, SO_ERROR, &error, &len);
+ /* the description in section 16.4 "Non-blocking connect"
+ * in UNIX Network Programming vol 1, 3rd edition, points out
+ * that sometimes the error is in errno and sometimes in error */
+ if (rc < 0 || error) {
+ if (rc == 0)
+ errno = error;
+ return handle_socket_error_msg(zh, __LINE__,ZCONNECTIONLOSS,
+ "server refused to accept the client");
+ }
+ if((rc=prime_connection(zh))!=0)
+ return rc;
+ LOG_INFO(("initiated connection to server [%s]",
+ format_endpoint_info(&zh->addrs[zh->connect_index])));
+ return ZOK;
+ }
+ if (zh->to_send.head && (events&ZOOKEEPER_WRITE)) {
+ /* make the flush call non-blocking by specifying a 0 timeout */
+ int rc=flush_send_queue(zh,0);
+ if (rc < 0)
+ return handle_socket_error_msg(zh,__LINE__,ZCONNECTIONLOSS,
+ "failed while flushing send queue");
+ }
+ if (events&ZOOKEEPER_READ) {
+ int rc;
+ if (zh->input_buffer == 0) {
+ zh->input_buffer = allocate_buffer(0,0);
+ }
+
+ rc = recv_buffer(zh->fd, zh->input_buffer);
+ if (rc < 0) {
+ return handle_socket_error_msg(zh, __LINE__,ZCONNECTIONLOSS,
+ "failed while receiving a server response");
+ }
+ if (rc > 0) {
+ gettimeofday(&zh->last_recv, 0);
+ if (zh->input_buffer != &zh->primer_buffer) {
+ queue_buffer(&zh->to_process, zh->input_buffer, 0);
+ } else {
+ int64_t oldid,newid;
+ //deserialize
+ deserialize_prime_response(&zh->primer_storage, zh->primer_buffer.buffer);
+ /* We are processing the primer_buffer, so we need to finish
+ * the connection handshake */
+ oldid = zh->client_id.client_id;
+ newid = zh->primer_storage.sessionId;
+ if (oldid != 0 && oldid != newid) {
+ zh->state = ZOO_EXPIRED_SESSION_STATE;
+ errno = ESTALE;
+ return handle_socket_error_msg(zh,__LINE__,ZSESSIONEXPIRED,
+ "sessionId=%#llx has expired.",oldid);
+ } else {
+ zh->recv_timeout = zh->primer_storage.timeOut;
+ zh->client_id.client_id = newid;
+
+ memcpy(zh->client_id.passwd, &zh->primer_storage.passwd,
+ sizeof(zh->client_id.passwd));
+ zh->state = ZOO_CONNECTED_STATE;
+ LOG_INFO(("session establishment complete on server [%s], sessionId=%#llx, negotiated timeout=%d",
+ format_endpoint_info(&zh->addrs[zh->connect_index]),
+ newid, zh->recv_timeout));
+ /* we want the auth to be sent for, but since both call push to front
+ we need to call send_watch_set first */
+ send_set_watches(zh);
+ /* send the authentication packet now */
+ send_auth_info(zh);
+ LOG_DEBUG(("Calling a watcher for a ZOO_SESSION_EVENT and the state=ZOO_CONNECTED_STATE"));
+ zh->input_buffer = 0; // just in case the watcher calls zookeeper_process() again
+ PROCESS_SESSION_EVENT(zh, ZOO_CONNECTED_STATE);
+ }
+ }
+ zh->input_buffer = 0;
+ } else {
+ // zookeeper_process was called but there was nothing to read
+ // from the socket
+ return ZNOTHING;
+ }
+ }
+ return ZOK;
+}
+
+void api_prolog(zhandle_t* zh)
+{
+ inc_ref_counter(zh,1);
+}
+
+int api_epilog(zhandle_t *zh,int rc)
+{
+ if(inc_ref_counter(zh,-1)==0 && zh->close_requested!=0)
+ zookeeper_close(zh);
+ return rc;
+}
+
+static __attribute__((unused)) void print_completion_queue(zhandle_t *zh)
+{
+ completion_list_t* cptr;
+
+ if(logLevelsent_requests.head==0) {
+ fprintf(LOGSTREAM,"empty\n");
+ return;
+ }
+
+ cptr=zh->sent_requests.head;
+ while(cptr){
+ fprintf(LOGSTREAM,"%d,",cptr->xid);
+ cptr=cptr->next;
+ }
+ fprintf(LOGSTREAM,"end\n");
+}
+
+//#ifdef THREADED
+// IO thread queues session events to be processed by the completion thread
+static int queue_session_event(zhandle_t *zh, int state)
+{
+ int rc;
+ struct WatcherEvent evt = { ZOO_SESSION_EVENT, state, "" };
+ struct ReplyHeader hdr = { WATCHER_EVENT_XID, 0, 0 };
+ struct oarchive *oa;
+ completion_list_t *cptr;
+
+ if ((oa=create_buffer_oarchive())==NULL) {
+ LOG_ERROR(("out of memory"));
+ goto error;
+ }
+ rc = serialize_ReplyHeader(oa, "hdr", &hdr);
+ rc = rc<0?rc: serialize_WatcherEvent(oa, "event", &evt);
+ if(rc<0){
+ close_buffer_oarchive(&oa, 1);
+ goto error;
+ }
+ cptr = create_completion_entry(WATCHER_EVENT_XID,-1,0,0,0,0);
+ cptr->buffer = allocate_buffer(get_buffer(oa), get_buffer_len(oa));
+ cptr->buffer->curr_offset = get_buffer_len(oa);
+ if (!cptr->buffer) {
+ free(cptr);
+ close_buffer_oarchive(&oa, 1);
+ goto error;
+ }
+ /* We queued the buffer, so don't free it */
+ close_buffer_oarchive(&oa, 0);
+ cptr->c.watcher_result = collectWatchers(zh, ZOO_SESSION_EVENT, "");
+ queue_completion(&zh->completions_to_process, cptr, 0);
+ if (process_async(zh->outstanding_sync)) {
+ process_completions(zh);
+ }
+ return ZOK;
+error:
+ errno=ENOMEM;
+ return ZSYSTEMERROR;
+}
+//#endif
+
+completion_list_t *dequeue_completion(completion_head_t *list)
+{
+ completion_list_t *cptr;
+ lock_completion_list(list);
+ cptr = list->head;
+ if (cptr) {
+ list->head = cptr->next;
+ if (!list->head) {
+ assert(list->last == cptr);
+ list->last = 0;
+ }
+ }
+ unlock_completion_list(list);
+ return cptr;
+}
+
+static void process_sync_completion(
+ completion_list_t *cptr,
+ struct sync_completion *sc,
+ struct iarchive *ia,
+ zhandle_t *zh)
+{
+ LOG_DEBUG(("Processing sync_completion with type=%d xid=%#x rc=%d",
+ cptr->c.type, cptr->xid, sc->rc));
+
+ switch(cptr->c.type) {
+ case COMPLETION_DATA:
+ if (sc->rc==0) {
+ struct GetDataResponse res;
+ int len;
+ deserialize_GetDataResponse(ia, "reply", &res);
+ if (res.data.len <= sc->u.data.buff_len) {
+ len = res.data.len;
+ } else {
+ len = sc->u.data.buff_len;
+ }
+ sc->u.data.buff_len = len;
+ // check if len is negative
+ // just of NULL which is -1 int
+ if (len == -1) {
+ sc->u.data.buffer = NULL;
+ } else {
+ memcpy(sc->u.data.buffer, res.data.buff, len);
+ }
+ sc->u.data.stat = res.stat;
+ deallocate_GetDataResponse(&res);
+ }
+ break;
+ case COMPLETION_STAT:
+ if (sc->rc==0) {
+ struct SetDataResponse res;
+ deserialize_SetDataResponse(ia, "reply", &res);
+ sc->u.stat = res.stat;
+ deallocate_SetDataResponse(&res);
+ }
+ break;
+ case COMPLETION_STRINGLIST:
+ if (sc->rc==0) {
+ struct GetChildrenResponse res;
+ deserialize_GetChildrenResponse(ia, "reply", &res);
+ sc->u.strs2 = res.children;
+ /* We don't deallocate since we are passing it back */
+ // deallocate_GetChildrenResponse(&res);
+ }
+ break;
+ case COMPLETION_STRINGLIST_STAT:
+ if (sc->rc==0) {
+ struct GetChildren2Response res;
+ deserialize_GetChildren2Response(ia, "reply", &res);
+ sc->u.strs_stat.strs2 = res.children;
+ sc->u.strs_stat.stat2 = res.stat;
+ /* We don't deallocate since we are passing it back */
+ // deallocate_GetChildren2Response(&res);
+ }
+ break;
+ case COMPLETION_STRING:
+ if (sc->rc==0) {
+ struct CreateResponse res;
+ int len;
+ const char * client_path;
+ deserialize_CreateResponse(ia, "reply", &res);
+ //ZOOKEEPER-1027
+ client_path = sub_string(zh, res.path);
+ len = strlen(client_path) + 1;if (len > sc->u.str.str_len) {
+ len = sc->u.str.str_len;
+ }
+ if (len > 0) {
+ memcpy(sc->u.str.str, client_path, len - 1);
+ sc->u.str.str[len - 1] = '\0';
+ }
+ free_duplicate_path(client_path, res.path);
+ deallocate_CreateResponse(&res);
+ }
+ break;
+ case COMPLETION_ACLLIST:
+ if (sc->rc==0) {
+ struct GetACLResponse res;
+ deserialize_GetACLResponse(ia, "reply", &res);
+ sc->u.acl.acl = res.acl;
+ sc->u.acl.stat = res.stat;
+ /* We don't deallocate since we are passing it back */
+ //deallocate_GetACLResponse(&res);
+ }
+ break;
+ case COMPLETION_VOID:
+ break;
+ case COMPLETION_MULTI:
+ sc->rc = deserialize_multi(cptr->xid, cptr, ia);
+ break;
+ default:
+ LOG_DEBUG(("Unsupported completion type=%d", cptr->c.type));
+ break;
+ }
+}
+
+static int deserialize_multi(int xid, completion_list_t *cptr, struct iarchive *ia)
+{
+ int rc = 0;
+ completion_head_t *clist = &cptr->c.clist;
+ struct MultiHeader mhdr = { STRUCT_INITIALIZER(type , 0), STRUCT_INITIALIZER(done , 0), STRUCT_INITIALIZER(err , 0) };
+ assert(clist);
+ deserialize_MultiHeader(ia, "multiheader", &mhdr);
+ while (!mhdr.done) {
+ completion_list_t *entry = dequeue_completion(clist);
+ assert(entry);
+
+ if (mhdr.type == -1) {
+ struct ErrorResponse er;
+ deserialize_ErrorResponse(ia, "error", &er);
+ mhdr.err = er.err ;
+ if (rc == 0 && er.err != 0 && er.err != ZRUNTIMEINCONSISTENCY) {
+ rc = er.err;
+ }
+ }
+
+ deserialize_response(entry->c.type, xid, mhdr.type == -1, mhdr.err, entry, ia);
+ deserialize_MultiHeader(ia, "multiheader", &mhdr);
+ //While deserializing the response we must destroy completion entry for each operation in
+ //the zoo_multi transaction. Otherwise this results in memory leak when client invokes zoo_multi
+ //operation.
+ destroy_completion_entry(entry);
+ }
+
+ return rc;
+}
+
+static void deserialize_response(int type, int xid, int failed, int rc, completion_list_t *cptr, struct iarchive *ia)
+{
+ switch (type) {
+ case COMPLETION_DATA:
+ LOG_DEBUG(("Calling COMPLETION_DATA for xid=%#x failed=%d rc=%d",
+ cptr->xid, failed, rc));
+ if (failed) {
+ cptr->c.data_result(rc, 0, 0, 0, cptr->data);
+ } else {
+ struct GetDataResponse res;
+ deserialize_GetDataResponse(ia, "reply", &res);
+ cptr->c.data_result(rc, res.data.buff, res.data.len,
+ &res.stat, cptr->data);
+ deallocate_GetDataResponse(&res);
+ }
+ break;
+ case COMPLETION_STAT:
+ LOG_DEBUG(("Calling COMPLETION_STAT for xid=%#x failed=%d rc=%d",
+ cptr->xid, failed, rc));
+ if (failed) {
+ cptr->c.stat_result(rc, 0, cptr->data);
+ } else {
+ struct SetDataResponse res;
+ deserialize_SetDataResponse(ia, "reply", &res);
+ cptr->c.stat_result(rc, &res.stat, cptr->data);
+ deallocate_SetDataResponse(&res);
+ }
+ break;
+ case COMPLETION_STRINGLIST:
+ LOG_DEBUG(("Calling COMPLETION_STRINGLIST for xid=%#x failed=%d rc=%d",
+ cptr->xid, failed, rc));
+ if (failed) {
+ cptr->c.strings_result(rc, 0, cptr->data);
+ } else {
+ struct GetChildrenResponse res;
+ deserialize_GetChildrenResponse(ia, "reply", &res);
+ cptr->c.strings_result(rc, &res.children, cptr->data);
+ deallocate_GetChildrenResponse(&res);
+ }
+ break;
+ case COMPLETION_STRINGLIST_STAT:
+ LOG_DEBUG(("Calling COMPLETION_STRINGLIST_STAT for xid=%#x failed=%d rc=%d",
+ cptr->xid, failed, rc));
+ if (failed) {
+ cptr->c.strings_stat_result(rc, 0, 0, cptr->data);
+ } else {
+ struct GetChildren2Response res;
+ deserialize_GetChildren2Response(ia, "reply", &res);
+ cptr->c.strings_stat_result(rc, &res.children, &res.stat, cptr->data);
+ deallocate_GetChildren2Response(&res);
+ }
+ break;
+ case COMPLETION_STRING:
+ LOG_DEBUG(("Calling COMPLETION_STRING for xid=%#x failed=%d, rc=%d",
+ cptr->xid, failed, rc));
+ if (failed) {
+ cptr->c.string_result(rc, 0, cptr->data);
+ } else {
+ struct CreateResponse res;
+ memset(&res, 0, sizeof(res));
+ deserialize_CreateResponse(ia, "reply", &res);
+ cptr->c.string_result(rc, res.path, cptr->data);
+ deallocate_CreateResponse(&res);
+ }
+ break;
+ case COMPLETION_ACLLIST:
+ LOG_DEBUG(("Calling COMPLETION_ACLLIST for xid=%#x failed=%d rc=%d",
+ cptr->xid, failed, rc));
+ if (failed) {
+ cptr->c.acl_result(rc, 0, 0, cptr->data);
+ } else {
+ struct GetACLResponse res;
+ deserialize_GetACLResponse(ia, "reply", &res);
+ cptr->c.acl_result(rc, &res.acl, &res.stat, cptr->data);
+ deallocate_GetACLResponse(&res);
+ }
+ break;
+ case COMPLETION_VOID:
+ LOG_DEBUG(("Calling COMPLETION_VOID for xid=%#x failed=%d rc=%d",
+ cptr->xid, failed, rc));
+ assert(cptr->c.void_result);
+ cptr->c.void_result(rc, cptr->data);
+ break;
+ case COMPLETION_MULTI:
+ LOG_DEBUG(("Calling COMPLETION_MULTI for xid=%#x failed=%d rc=%d",
+ cptr->xid, failed, rc));
+ rc = deserialize_multi(xid, cptr, ia);
+ assert(cptr->c.void_result);
+ cptr->c.void_result(rc, cptr->data);
+ break;
+ default:
+ LOG_DEBUG(("Unsupported completion type=%d", cptr->c.type));
+ }
+}
+
+
+/* handles async completion (both single- and multithreaded) */
+void process_completions(zhandle_t *zh)
+{
+ completion_list_t *cptr;
+ while ((cptr = dequeue_completion(&zh->completions_to_process)) != 0) {
+ struct ReplyHeader hdr;
+ buffer_list_t *bptr = cptr->buffer;
+ struct iarchive *ia = create_buffer_iarchive(bptr->buffer,
+ bptr->len);
+ deserialize_ReplyHeader(ia, "hdr", &hdr);
+
+ if (hdr.xid == WATCHER_EVENT_XID) {
+ int type, state;
+ struct WatcherEvent evt;
+ deserialize_WatcherEvent(ia, "event", &evt);
+ /* We are doing a notification, so there is no pending request */
+ type = evt.type;
+ state = evt.state;
+ /* This is a notification so there aren't any pending requests */
+ LOG_DEBUG(("Calling a watcher for node [%s], type = %d event=%s",
+ (evt.path==NULL?"NULL":evt.path), cptr->c.type,
+ watcherEvent2String(type)));
+ deliverWatchers(zh,type,state,evt.path, &cptr->c.watcher_result);
+ deallocate_WatcherEvent(&evt);
+ } else {
+ deserialize_response(cptr->c.type, hdr.xid, hdr.err != 0, hdr.err, cptr, ia);
+ }
+ destroy_completion_entry(cptr);
+ close_buffer_iarchive(&ia);
+ }
+}
+
+static void isSocketReadable(zhandle_t* zh)
+{
+#ifndef WIN32
+ struct pollfd fds;
+ fds.fd = zh->fd;
+ fds.events = POLLIN;
+ if (poll(&fds,1,0)<=0) {
+ // socket not readable -- no more responses to process
+ zh->socket_readable.tv_sec=zh->socket_readable.tv_usec=0;
+ }
+#else
+ fd_set rfds;
+ struct timeval waittime = {0, 0};
+ FD_ZERO(&rfds);
+ FD_SET( zh->fd , &rfds);
+ if (select(0, &rfds, NULL, NULL, &waittime) <= 0){
+ // socket not readable -- no more responses to process
+ zh->socket_readable.tv_sec=zh->socket_readable.tv_usec=0;
+ }
+#endif
+ else{
+ gettimeofday(&zh->socket_readable,0);
+ }
+}
+
+static void checkResponseLatency(zhandle_t* zh)
+{
+ int delay;
+ struct timeval now;
+
+ if(zh->socket_readable.tv_sec==0)
+ return;
+
+ gettimeofday(&now,0);
+ delay=calculate_interval(&zh->socket_readable, &now);
+ if(delay>20)
+ LOG_DEBUG(("The following server response has spent at least %dms sitting in the client socket recv buffer",delay));
+
+ zh->socket_readable.tv_sec=zh->socket_readable.tv_usec=0;
+}
+
+int zookeeper_process(zhandle_t *zh, int events)
+{
+ buffer_list_t *bptr;
+ int rc;
+
+ if (zh==NULL)
+ return ZBADARGUMENTS;
+ if (is_unrecoverable(zh))
+ return ZINVALIDSTATE;
+ api_prolog(zh);
+ IF_DEBUG(checkResponseLatency(zh));
+ rc = check_events(zh, events);
+ if (rc!=ZOK)
+ return api_epilog(zh, rc);
+
+ IF_DEBUG(isSocketReadable(zh));
+
+ while (rc >= 0 && (bptr=dequeue_buffer(&zh->to_process))) {
+ struct ReplyHeader hdr;
+ struct iarchive *ia = create_buffer_iarchive(
+ bptr->buffer, bptr->curr_offset);
+ deserialize_ReplyHeader(ia, "hdr", &hdr);
+ if (hdr.zxid > 0) {
+ zh->last_zxid = hdr.zxid;
+ } else {
+ // fprintf(stderr, "Got %#x for %#x\n", hdr.zxid, hdr.xid);
+ }
+
+ if (hdr.xid == PING_XID) {
+ // Ping replies can arrive out-of-order
+ int elapsed = 0;
+ struct timeval now;
+ gettimeofday(&now, 0);
+ elapsed = calculate_interval(&zh->last_ping, &now);
+ LOG_DEBUG(("Got ping response in %d ms", elapsed));
+ free_buffer(bptr);
+ } else if (hdr.xid == WATCHER_EVENT_XID) {
+ struct WatcherEvent evt;
+ int type = 0;
+ char *path = NULL;
+ completion_list_t *c = NULL;
+
+ LOG_DEBUG(("Processing WATCHER_EVENT"));
+
+ deserialize_WatcherEvent(ia, "event", &evt);
+ type = evt.type;
+ path = evt.path;
+ /* We are doing a notification, so there is no pending request */
+ c = create_completion_entry(WATCHER_EVENT_XID,-1,0,0,0,0);
+ c->buffer = bptr;
+ c->c.watcher_result = collectWatchers(zh, type, path);
+
+ // We cannot free until now, otherwise path will become invalid
+ deallocate_WatcherEvent(&evt);
+ queue_completion(&zh->completions_to_process, c, 0);
+ } else if (hdr.xid == SET_WATCHES_XID) {
+ LOG_DEBUG(("Processing SET_WATCHES"));
+ free_buffer(bptr);
+ } else if (hdr.xid == AUTH_XID){
+ LOG_DEBUG(("Processing AUTH_XID"));
+
+ /* special handling for the AUTH response as it may come back
+ * out-of-band */
+ auth_completion_func(hdr.err,zh);
+ free_buffer(bptr);
+ /* authentication completion may change the connection state to
+ * unrecoverable */
+ if(is_unrecoverable(zh)){
+ handle_error(zh, ZAUTHFAILED);
+ close_buffer_iarchive(&ia);
+ return api_epilog(zh, ZAUTHFAILED);
+ }
+ } else {
+ int rc = hdr.err;
+ /* Find the request corresponding to the response */
+ completion_list_t *cptr = dequeue_completion(&zh->sent_requests);
+
+ /* [ZOOKEEPER-804] Don't assert if zookeeper_close has been called. */
+ if (zh->close_requested == 1 && cptr == NULL) {
+ LOG_DEBUG(("Completion queue has been cleared by zookeeper_close()"));
+ close_buffer_iarchive(&ia);
+ free_buffer(bptr);
+ return api_epilog(zh,ZINVALIDSTATE);
+ }
+ assert(cptr);
+ /* The requests are going to come back in order */
+ if (cptr->xid != hdr.xid) {
+ LOG_DEBUG(("Processing unexpected or out-of-order response!"));
+
+ // received unexpected (or out-of-order) response
+ close_buffer_iarchive(&ia);
+ free_buffer(bptr);
+ // put the completion back on the queue (so it gets properly
+ // signaled and deallocated) and disconnect from the server
+ queue_completion(&zh->sent_requests,cptr,1);
+ return api_epilog(zh,
+ handle_socket_error_msg(zh, __LINE__,ZRUNTIMEINCONSISTENCY,
+ "unexpected server response: expected %#x, but received %#x",
+ hdr.xid,cptr->xid));
+ }
+
+ activateWatcher(zh, cptr->watcher, rc);
+
+ if (cptr->c.void_result != SYNCHRONOUS_MARKER) {
+ LOG_DEBUG(("Queueing asynchronous response"));
+ cptr->buffer = bptr;
+ queue_completion(&zh->completions_to_process, cptr, 0);
+ } else {
+ struct sync_completion
+ *sc = (struct sync_completion*)cptr->data;
+ sc->rc = rc;
+
+ process_sync_completion(cptr, sc, ia, zh);
+
+ notify_sync_completion(sc);
+ free_buffer(bptr);
+ zh->outstanding_sync--;
+ destroy_completion_entry(cptr);
+ }
+ }
+
+ close_buffer_iarchive(&ia);
+
+ }
+ if (process_async(zh->outstanding_sync)) {
+ process_completions(zh);
+ }
+ return api_epilog(zh,ZOK);}
+
+int zoo_state(zhandle_t *zh)
+{
+ if(zh!=0)
+ return zh->state;
+ return 0;
+}
+
+static watcher_registration_t* create_watcher_registration(const char* path,
+ result_checker_fn checker,watcher_fn watcher,void* ctx){
+ watcher_registration_t* wo;
+ if(watcher==0)
+ return 0;
+ wo=calloc(1,sizeof(watcher_registration_t));
+ wo->path=strdup(path);
+ wo->watcher=watcher;
+ wo->context=ctx;
+ wo->checker=checker;
+ return wo;
+}
+
+static void destroy_watcher_registration(watcher_registration_t* wo){
+ if(wo!=0){
+ free((void*)wo->path);
+ free(wo);
+ }
+}
+
+static completion_list_t* create_completion_entry(int xid, int completion_type,
+ const void *dc, const void *data,watcher_registration_t* wo, completion_head_t *clist)
+{
+ completion_list_t *c = calloc(1,sizeof(completion_list_t));
+ if (!c) {
+ LOG_ERROR(("out of memory"));
+ return 0;
+ }
+ c->c.type = completion_type;
+ c->data = data;
+ switch(c->c.type) {
+ case COMPLETION_VOID:
+ c->c.void_result = (void_completion_t)dc;
+ break;
+ case COMPLETION_STRING:
+ c->c.string_result = (string_completion_t)dc;
+ break;
+ case COMPLETION_DATA:
+ c->c.data_result = (data_completion_t)dc;
+ break;
+ case COMPLETION_STAT:
+ c->c.stat_result = (stat_completion_t)dc;
+ break;
+ case COMPLETION_STRINGLIST:
+ c->c.strings_result = (strings_completion_t)dc;
+ break;
+ case COMPLETION_STRINGLIST_STAT:
+ c->c.strings_stat_result = (strings_stat_completion_t)dc;
+ break;
+ case COMPLETION_ACLLIST:
+ c->c.acl_result = (acl_completion_t)dc;
+ break;
+ case COMPLETION_MULTI:
+ assert(clist);
+ c->c.void_result = (void_completion_t)dc;
+ c->c.clist = *clist;
+ break;
+ }
+ c->xid = xid;
+ c->watcher = wo;
+
+ return c;
+}
+
+static void destroy_completion_entry(completion_list_t* c){
+ if(c!=0){
+ destroy_watcher_registration(c->watcher);
+ if(c->buffer!=0)
+ free_buffer(c->buffer);
+ free(c);
+ }
+}
+
+static void queue_completion_nolock(completion_head_t *list,
+ completion_list_t *c,
+ int add_to_front)
+{
+ c->next = 0;
+ /* appending a new entry to the back of the list */
+ if (list->last) {
+ assert(list->head);
+ // List is not empty
+ if (!add_to_front) {
+ list->last->next = c;
+ list->last = c;
+ } else {
+ c->next = list->head;
+ list->head = c;
+ }
+ } else {
+ // List is empty
+ assert(!list->head);
+ list->head = c;
+ list->last = c;
+ }
+}
+
+static void queue_completion(completion_head_t *list, completion_list_t *c,
+ int add_to_front)
+{
+
+ lock_completion_list(list);
+ queue_completion_nolock(list, c, add_to_front);
+ unlock_completion_list(list);
+}
+
+static int add_completion(zhandle_t *zh, int xid, int completion_type,
+ const void *dc, const void *data, int add_to_front,
+ watcher_registration_t* wo, completion_head_t *clist)
+{
+ completion_list_t *c =create_completion_entry(xid, completion_type, dc,
+ data, wo, clist);
+ int rc = 0;
+ if (!c)
+ return ZSYSTEMERROR;
+ lock_completion_list(&zh->sent_requests);
+ if (zh->close_requested != 1) {
+ queue_completion_nolock(&zh->sent_requests, c, add_to_front);
+ if (dc == SYNCHRONOUS_MARKER) {
+ zh->outstanding_sync++;
+ }
+ rc = ZOK;
+ } else {
+ free(c);
+ rc = ZINVALIDSTATE;
+ }
+ unlock_completion_list(&zh->sent_requests);
+ return rc;
+}
+
+static int add_data_completion(zhandle_t *zh, int xid, data_completion_t dc,
+ const void *data,watcher_registration_t* wo)
+{
+ return add_completion(zh, xid, COMPLETION_DATA, dc, data, 0, wo, 0);
+}
+
+static int add_stat_completion(zhandle_t *zh, int xid, stat_completion_t dc,
+ const void *data,watcher_registration_t* wo)
+{
+ return add_completion(zh, xid, COMPLETION_STAT, dc, data, 0, wo, 0);
+}
+
+static int add_strings_completion(zhandle_t *zh, int xid,
+ strings_completion_t dc, const void *data,watcher_registration_t* wo)
+{
+ return add_completion(zh, xid, COMPLETION_STRINGLIST, dc, data, 0, wo, 0);
+}
+
+static int add_strings_stat_completion(zhandle_t *zh, int xid,
+ strings_stat_completion_t dc, const void *data,watcher_registration_t* wo)
+{
+ return add_completion(zh, xid, COMPLETION_STRINGLIST_STAT, dc, data, 0, wo, 0);
+}
+
+static int add_acl_completion(zhandle_t *zh, int xid, acl_completion_t dc,
+ const void *data)
+{
+ return add_completion(zh, xid, COMPLETION_ACLLIST, dc, data, 0, 0, 0);
+}
+
+static int add_void_completion(zhandle_t *zh, int xid, void_completion_t dc,
+ const void *data)
+{
+ return add_completion(zh, xid, COMPLETION_VOID, dc, data, 0, 0, 0);
+}
+
+static int add_string_completion(zhandle_t *zh, int xid,
+ string_completion_t dc, const void *data)
+{
+ return add_completion(zh, xid, COMPLETION_STRING, dc, data, 0, 0, 0);
+}
+
+static int add_multi_completion(zhandle_t *zh, int xid, void_completion_t dc,
+ const void *data, completion_head_t *clist)
+{
+ return add_completion(zh, xid, COMPLETION_MULTI, dc, data, 0,0, clist);
+}
+
+int zookeeper_close(zhandle_t *zh)
+{
+ int rc=ZOK;
+ if (zh==0)
+ return ZBADARGUMENTS;
+
+ zh->close_requested=1;
+ if (inc_ref_counter(zh,1)>1) {
+ /* We have incremented the ref counter to prevent the
+ * completions from calling zookeeper_close before we have
+ * completed the adaptor_finish call below. */
+
+ /* Signal any syncronous completions before joining the threads */
+ enter_critical(zh);
+ free_completions(zh,1,ZCLOSING);
+ leave_critical(zh);
+
+ adaptor_finish(zh);
+ /* Now we can allow the handle to be cleaned up, if the completion
+ * threads finished during the adaptor_finish call. */
+ api_epilog(zh, 0);
+ return ZOK;
+ }
+ /* No need to decrement the counter since we're just going to
+ * destroy the handle later. */
+ if(zh->state==ZOO_CONNECTED_STATE){
+ struct oarchive *oa;
+ struct RequestHeader h = { STRUCT_INITIALIZER (xid , get_xid()), STRUCT_INITIALIZER (type , ZOO_CLOSE_OP)};
+ LOG_INFO(("Closing zookeeper sessionId=%#llx to [%s]\n",
+ zh->client_id.client_id,format_current_endpoint_info(zh)));
+ oa = create_buffer_oarchive();
+ rc = serialize_RequestHeader(oa, "header", &h);
+ rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
+ get_buffer_len(oa));
+ /* We queued the buffer, so don't free it */
+ close_buffer_oarchive(&oa, 0);
+ if (rc < 0) {
+ rc = ZMARSHALLINGERROR;
+ goto finish;
+ }
+
+ /* make sure the close request is sent; we set timeout to an arbitrary
+ * (but reasonable) number of milliseconds since we want the call to block*/
+ rc=adaptor_send_queue(zh, 3000);
+ }else{
+ LOG_INFO(("Freeing zookeeper resources for sessionId=%#llx\n",
+ zh->client_id.client_id));
+ rc = ZOK;
+ }
+
+finish:
+ destroy(zh);
+ adaptor_destroy(zh);
+ free(zh);
+#ifdef WIN32
+ Win32WSACleanup();
+#endif
+ return rc;
+}
+
+static int isValidPath(const char* path, const int flags) {
+ int len = 0;
+ char lastc = '/';
+ char c;
+ int i = 0;
+
+ if (path == 0)
+ return 0;
+ len = strlen(path);
+ if (len == 0)
+ return 0;
+ if (path[0] != '/')
+ return 0;
+ if (len == 1) // done checking - it's the root
+ return 1;
+ if (path[len - 1] == '/' && !(flags & ZOO_SEQUENCE))
+ return 0;
+
+ i = 1;
+ for (; i < len; lastc = path[i], i++) {
+ c = path[i];
+
+ if (c == 0) {
+ return 0;
+ } else if (c == '/' && lastc == '/') {
+ return 0;
+ } else if (c == '.' && lastc == '.') {
+ if (path[i-2] == '/' && (((i + 1 == len) && !(flags & ZOO_SEQUENCE))
+ || path[i+1] == '/')) {
+ return 0;
+ }
+ } else if (c == '.') {
+ if ((path[i-1] == '/') && (((i + 1 == len) && !(flags & ZOO_SEQUENCE))
+ || path[i+1] == '/')) {
+ return 0;
+ }
+ } else if (c > 0x00 && c < 0x1f) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*---------------------------------------------------------------------------*
+ * REQUEST INIT HELPERS
+ *---------------------------------------------------------------------------*/
+/* Common Request init helper functions to reduce code duplication */
+static int Request_path_init(zhandle_t *zh, int flags,
+ char **path_out, const char *path)
+{
+ assert(path_out);
+
+ *path_out = prepend_string(zh, path);
+ if (zh == NULL || !isValidPath(*path_out, flags)) {
+ free_duplicate_path(*path_out, path);
+ return ZBADARGUMENTS;
+ }
+ if (is_unrecoverable(zh)) {
+ free_duplicate_path(*path_out, path);
+ return ZINVALIDSTATE;
+ }
+
+ return ZOK;
+}
+
+static int Request_path_watch_init(zhandle_t *zh, int flags,
+ char **path_out, const char *path,
+ int32_t *watch_out, uint32_t watch)
+{
+ int rc = Request_path_init(zh, flags, path_out, path);
+ if (rc != ZOK) {
+ return rc;
+ }
+ *watch_out = watch;
+ return ZOK;
+}
+
+/*---------------------------------------------------------------------------*
+ * ASYNC API
+ *---------------------------------------------------------------------------*/
+int zoo_aget(zhandle_t *zh, const char *path, int watch, data_completion_t dc,
+ const void *data)
+{
+ return zoo_awget(zh,path,watch?zh->watcher:0,zh->context,dc,data);
+}
+
+int zoo_awget(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ data_completion_t dc, const void *data)
+{
+ struct oarchive *oa;
+ char *server_path = prepend_string(zh, path);
+ struct RequestHeader h = { STRUCT_INITIALIZER (xid , get_xid()), STRUCT_INITIALIZER (type ,ZOO_GETDATA_OP)};
+ struct GetDataRequest req = { (char*)server_path, watcher!=0 };
+ int rc;
+
+ if (zh==0 || !isValidPath(server_path, 0)) {
+ free_duplicate_path(server_path, path);
+ return ZBADARGUMENTS;
+ }
+ if (is_unrecoverable(zh)) {
+ free_duplicate_path(server_path, path);
+ return ZINVALIDSTATE;
+ }
+ oa=create_buffer_oarchive();
+ rc = serialize_RequestHeader(oa, "header", &h);
+ rc = rc < 0 ? rc : serialize_GetDataRequest(oa, "req", &req);
+ enter_critical(zh);
+ rc = rc < 0 ? rc : add_data_completion(zh, h.xid, dc, data,
+ create_watcher_registration(server_path,data_result_checker,watcher,watcherCtx));
+ rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
+ get_buffer_len(oa));
+ leave_critical(zh);
+ free_duplicate_path(server_path, path);
+ /* We queued the buffer, so don't free it */
+ close_buffer_oarchive(&oa, 0);
+
+ LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path,
+ format_current_endpoint_info(zh)));
+ /* make a best (non-blocking) effort to send the requests asap */
+ adaptor_send_queue(zh, 0);
+ return (rc < 0)?ZMARSHALLINGERROR:ZOK;
+}
+
+static int SetDataRequest_init(zhandle_t *zh, struct SetDataRequest *req,
+ const char *path, const char *buffer, int buflen, int version)
+{
+ int rc;
+ assert(req);
+ rc = Request_path_init(zh, 0, &req->path, path);
+ if (rc != ZOK) {
+ return rc;
+ }
+ req->data.buff = (char*)buffer;
+ req->data.len = buflen;
+ req->version = version;
+
+ return ZOK;
+}
+
+int zoo_aset(zhandle_t *zh, const char *path, const char *buffer, int buflen,
+ int version, stat_completion_t dc, const void *data)
+{
+ struct oarchive *oa;
+ struct RequestHeader h = { STRUCT_INITIALIZER(xid , get_xid()), STRUCT_INITIALIZER (type , ZOO_SETDATA_OP)};
+ struct SetDataRequest req;
+ int rc = SetDataRequest_init(zh, &req, path, buffer, buflen, version);
+ if (rc != ZOK) {
+ return rc;
+ }
+ oa = create_buffer_oarchive();
+ rc = serialize_RequestHeader(oa, "header", &h);
+ rc = rc < 0 ? rc : serialize_SetDataRequest(oa, "req", &req);
+ enter_critical(zh);
+ rc = rc < 0 ? rc : add_stat_completion(zh, h.xid, dc, data,0);
+ rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
+ get_buffer_len(oa));
+ leave_critical(zh);
+ free_duplicate_path(req.path, path);
+ /* We queued the buffer, so don't free it */
+ close_buffer_oarchive(&oa, 0);
+
+ LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path,
+ format_current_endpoint_info(zh)));
+ /* make a best (non-blocking) effort to send the requests asap */
+ adaptor_send_queue(zh, 0);
+ return (rc < 0)?ZMARSHALLINGERROR:ZOK;
+}
+
+static int CreateRequest_init(zhandle_t *zh, struct CreateRequest *req,
+ const char *path, const char *value,
+ int valuelen, const struct ACL_vector *acl_entries, int flags)
+{
+ int rc;
+ assert(req);
+ rc = Request_path_init(zh, flags, &req->path, path);
+ assert(req);
+ if (rc != ZOK) {
+ return rc;
+ }
+ req->flags = flags;
+ req->data.buff = (char*)value;
+ req->data.len = valuelen;
+ if (acl_entries == 0) {
+ req->acl.count = 0;
+ req->acl.data = 0;
+ } else {
+ req->acl = *acl_entries;
+ }
+
+ return ZOK;
+}
+
+int zoo_acreate(zhandle_t *zh, const char *path, const char *value,
+ int valuelen, const struct ACL_vector *acl_entries, int flags,
+ string_completion_t completion, const void *data)
+{
+ struct oarchive *oa;
+ struct RequestHeader h = { STRUCT_INITIALIZER (xid , get_xid()), STRUCT_INITIALIZER (type ,ZOO_CREATE_OP) };
+ struct CreateRequest req;
+
+ int rc = CreateRequest_init(zh, &req,
+ path, value, valuelen, acl_entries, flags);
+ if (rc != ZOK) {
+ return rc;
+ }
+ oa = create_buffer_oarchive();
+ rc = serialize_RequestHeader(oa, "header", &h);
+ rc = rc < 0 ? rc : serialize_CreateRequest(oa, "req", &req);
+ enter_critical(zh);
+ rc = rc < 0 ? rc : add_string_completion(zh, h.xid, completion, data);
+ rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
+ get_buffer_len(oa));
+ leave_critical(zh);
+ free_duplicate_path(req.path, path);
+ /* We queued the buffer, so don't free it */
+ close_buffer_oarchive(&oa, 0);
+
+ LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path,
+ format_current_endpoint_info(zh)));
+ /* make a best (non-blocking) effort to send the requests asap */
+ adaptor_send_queue(zh, 0);
+ return (rc < 0)?ZMARSHALLINGERROR:ZOK;
+}
+
+int DeleteRequest_init(zhandle_t *zh, struct DeleteRequest *req,
+ const char *path, int version)
+{
+ int rc = Request_path_init(zh, 0, &req->path, path);
+ if (rc != ZOK) {
+ return rc;
+ }
+ req->version = version;
+ return ZOK;
+}
+
+int zoo_adelete(zhandle_t *zh, const char *path, int version,
+ void_completion_t completion, const void *data)
+{
+ struct oarchive *oa;
+ struct RequestHeader h = { STRUCT_INITIALIZER (xid , get_xid()), STRUCT_INITIALIZER (type , ZOO_DELETE_OP)};
+ struct DeleteRequest req;
+ int rc = DeleteRequest_init(zh, &req, path, version);
+ if (rc != ZOK) {
+ return rc;
+ }
+ oa = create_buffer_oarchive();
+ rc = serialize_RequestHeader(oa, "header", &h);
+ rc = rc < 0 ? rc : serialize_DeleteRequest(oa, "req", &req);
+ enter_critical(zh);
+ rc = rc < 0 ? rc : add_void_completion(zh, h.xid, completion, data);
+ rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
+ get_buffer_len(oa));
+ leave_critical(zh);
+ free_duplicate_path(req.path, path);
+ /* We queued the buffer, so don't free it */
+ close_buffer_oarchive(&oa, 0);
+
+ LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path,
+ format_current_endpoint_info(zh)));
+ /* make a best (non-blocking) effort to send the requests asap */
+ adaptor_send_queue(zh, 0);
+ return (rc < 0)?ZMARSHALLINGERROR:ZOK;
+}
+
+int zoo_aexists(zhandle_t *zh, const char *path, int watch,
+ stat_completion_t sc, const void *data)
+{
+ return zoo_awexists(zh,path,watch?zh->watcher:0,zh->context,sc,data);
+}
+
+int zoo_awexists(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ stat_completion_t completion, const void *data)
+{
+ struct oarchive *oa;
+ struct RequestHeader h = { STRUCT_INITIALIZER (xid ,get_xid()), STRUCT_INITIALIZER (type , ZOO_EXISTS_OP) };
+ struct ExistsRequest req;
+ int rc = Request_path_watch_init(zh, 0, &req.path, path,
+ &req.watch, watcher != NULL);
+ if (rc != ZOK) {
+ return rc;
+ }
+ oa = create_buffer_oarchive();
+ rc = serialize_RequestHeader(oa, "header", &h);
+ rc = rc < 0 ? rc : serialize_ExistsRequest(oa, "req", &req);
+ enter_critical(zh);
+ rc = rc < 0 ? rc : add_stat_completion(zh, h.xid, completion, data,
+ create_watcher_registration(req.path,exists_result_checker,
+ watcher,watcherCtx));
+ rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
+ get_buffer_len(oa));
+ leave_critical(zh);
+ free_duplicate_path(req.path, path);
+ /* We queued the buffer, so don't free it */
+ close_buffer_oarchive(&oa, 0);
+
+ LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path,
+ format_current_endpoint_info(zh)));
+ /* make a best (non-blocking) effort to send the requests asap */
+ adaptor_send_queue(zh, 0);
+ return (rc < 0)?ZMARSHALLINGERROR:ZOK;
+}
+
+static int zoo_awget_children_(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ strings_completion_t sc,
+ const void *data)
+{
+ struct oarchive *oa;
+ struct RequestHeader h = { STRUCT_INITIALIZER (xid , get_xid()), STRUCT_INITIALIZER (type , ZOO_GETCHILDREN_OP)};
+ struct GetChildrenRequest req ;
+ int rc = Request_path_watch_init(zh, 0, &req.path, path,
+ &req.watch, watcher != NULL);
+ if (rc != ZOK) {
+ return rc;
+ }
+ oa = create_buffer_oarchive();
+ rc = serialize_RequestHeader(oa, "header", &h);
+ rc = rc < 0 ? rc : serialize_GetChildrenRequest(oa, "req", &req);
+ enter_critical(zh);
+ rc = rc < 0 ? rc : add_strings_completion(zh, h.xid, sc, data,
+ create_watcher_registration(req.path,child_result_checker,watcher,watcherCtx));
+ rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
+ get_buffer_len(oa));
+ leave_critical(zh);
+ free_duplicate_path(req.path, path);
+ /* We queued the buffer, so don't free it */
+ close_buffer_oarchive(&oa, 0);
+
+ LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path,
+ format_current_endpoint_info(zh)));
+ /* make a best (non-blocking) effort to send the requests asap */
+ adaptor_send_queue(zh, 0);
+ return (rc < 0)?ZMARSHALLINGERROR:ZOK;
+}
+
+int zoo_aget_children(zhandle_t *zh, const char *path, int watch,
+ strings_completion_t dc, const void *data)
+{
+ return zoo_awget_children_(zh,path,watch?zh->watcher:0,zh->context,dc,data);
+}
+
+int zoo_awget_children(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ strings_completion_t dc,
+ const void *data)
+{
+ return zoo_awget_children_(zh,path,watcher,watcherCtx,dc,data);
+}
+
+static int zoo_awget_children2_(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ strings_stat_completion_t ssc,
+ const void *data)
+{
+ /* invariant: (sc == NULL) != (sc == NULL) */
+ struct oarchive *oa;
+ struct RequestHeader h = { STRUCT_INITIALIZER( xid, get_xid()), STRUCT_INITIALIZER (type ,ZOO_GETCHILDREN2_OP)};
+ struct GetChildren2Request req ;
+ int rc = Request_path_watch_init(zh, 0, &req.path, path,
+ &req.watch, watcher != NULL);
+ if (rc != ZOK) {
+ return rc;
+ }
+ oa = create_buffer_oarchive();
+ rc = serialize_RequestHeader(oa, "header", &h);
+ rc = rc < 0 ? rc : serialize_GetChildren2Request(oa, "req", &req);
+ enter_critical(zh);
+ rc = rc < 0 ? rc : add_strings_stat_completion(zh, h.xid, ssc, data,
+ create_watcher_registration(req.path,child_result_checker,watcher,watcherCtx));
+ rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
+ get_buffer_len(oa));
+ leave_critical(zh);
+ free_duplicate_path(req.path, path);
+ /* We queued the buffer, so don't free it */
+ close_buffer_oarchive(&oa, 0);
+
+ LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path,
+ format_current_endpoint_info(zh)));
+ /* make a best (non-blocking) effort to send the requests asap */
+ adaptor_send_queue(zh, 0);
+ return (rc < 0)?ZMARSHALLINGERROR:ZOK;
+}
+
+int zoo_aget_children2(zhandle_t *zh, const char *path, int watch,
+ strings_stat_completion_t dc, const void *data)
+{
+ return zoo_awget_children2_(zh,path,watch?zh->watcher:0,zh->context,dc,data);
+}
+
+int zoo_awget_children2(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ strings_stat_completion_t dc,
+ const void *data)
+{
+ return zoo_awget_children2_(zh,path,watcher,watcherCtx,dc,data);
+}
+
+int zoo_async(zhandle_t *zh, const char *path,
+ string_completion_t completion, const void *data)
+{
+ struct oarchive *oa;
+ struct RequestHeader h = { STRUCT_INITIALIZER (xid , get_xid()), STRUCT_INITIALIZER (type , ZOO_SYNC_OP)};
+ struct SyncRequest req;
+ int rc = Request_path_init(zh, 0, &req.path, path);
+ if (rc != ZOK) {
+ return rc;
+ }
+ oa = create_buffer_oarchive();
+ rc = serialize_RequestHeader(oa, "header", &h);
+ rc = rc < 0 ? rc : serialize_SyncRequest(oa, "req", &req);
+ enter_critical(zh);
+ rc = rc < 0 ? rc : add_string_completion(zh, h.xid, completion, data);
+ rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
+ get_buffer_len(oa));
+ leave_critical(zh);
+ free_duplicate_path(req.path, path);
+ /* We queued the buffer, so don't free it */
+ close_buffer_oarchive(&oa, 0);
+
+ LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path,
+ format_current_endpoint_info(zh)));
+ /* make a best (non-blocking) effort to send the requests asap */
+ adaptor_send_queue(zh, 0);
+ return (rc < 0)?ZMARSHALLINGERROR:ZOK;
+}
+
+
+int zoo_aget_acl(zhandle_t *zh, const char *path, acl_completion_t completion,
+ const void *data)
+{
+ struct oarchive *oa;
+ struct RequestHeader h = { STRUCT_INITIALIZER (xid , get_xid()), STRUCT_INITIALIZER(type ,ZOO_GETACL_OP)};
+ struct GetACLRequest req;
+ int rc = Request_path_init(zh, 0, &req.path, path) ;
+ if (rc != ZOK) {
+ return rc;
+ }
+ oa = create_buffer_oarchive();
+ rc = serialize_RequestHeader(oa, "header", &h);
+ rc = rc < 0 ? rc : serialize_GetACLRequest(oa, "req", &req);
+ enter_critical(zh);
+ rc = rc < 0 ? rc : add_acl_completion(zh, h.xid, completion, data);
+ rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
+ get_buffer_len(oa));
+ leave_critical(zh);
+ free_duplicate_path(req.path, path);
+ /* We queued the buffer, so don't free it */
+ close_buffer_oarchive(&oa, 0);
+
+ LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path,
+ format_current_endpoint_info(zh)));
+ /* make a best (non-blocking) effort to send the requests asap */
+ adaptor_send_queue(zh, 0);
+ return (rc < 0)?ZMARSHALLINGERROR:ZOK;
+}
+
+int zoo_aset_acl(zhandle_t *zh, const char *path, int version,
+ struct ACL_vector *acl, void_completion_t completion, const void *data)
+{
+ struct oarchive *oa;
+ struct RequestHeader h = { STRUCT_INITIALIZER(xid ,get_xid()), STRUCT_INITIALIZER (type , ZOO_SETACL_OP)};
+ struct SetACLRequest req;
+ int rc = Request_path_init(zh, 0, &req.path, path);
+ if (rc != ZOK) {
+ return rc;
+ }
+ oa = create_buffer_oarchive();
+ req.acl = *acl;
+ req.version = version;
+ rc = serialize_RequestHeader(oa, "header", &h);
+ rc = rc < 0 ? rc : serialize_SetACLRequest(oa, "req", &req);
+ enter_critical(zh);
+ rc = rc < 0 ? rc : add_void_completion(zh, h.xid, completion, data);
+ rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
+ get_buffer_len(oa));
+ leave_critical(zh);
+ free_duplicate_path(req.path, path);
+ /* We queued the buffer, so don't free it */
+ close_buffer_oarchive(&oa, 0);
+
+ LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path,
+ format_current_endpoint_info(zh)));
+ /* make a best (non-blocking) effort to send the requests asap */
+ adaptor_send_queue(zh, 0);
+ return (rc < 0)?ZMARSHALLINGERROR:ZOK;
+}
+
+/* Completions for multi-op results */
+static void op_result_string_completion(int err, const char *value, const void *data)
+{
+ struct zoo_op_result *result = (struct zoo_op_result *)data;
+ assert(result);
+ result->err = err;
+
+ if (result->value && value) {
+ int len = strlen(value) + 1;
+ if (len > result->valuelen) {
+ len = result->valuelen;
+ }
+ if (len > 0) {
+ memcpy(result->value, value, len - 1);
+ result->value[len - 1] = '\0';
+ }
+ } else {
+ result->value = NULL;
+ }
+}
+
+static void op_result_void_completion(int err, const void *data)
+{
+ struct zoo_op_result *result = (struct zoo_op_result *)data;
+ assert(result);
+ result->err = err;
+}
+
+static void op_result_stat_completion(int err, const struct Stat *stat, const void *data)
+{
+ struct zoo_op_result *result = (struct zoo_op_result *)data;
+ assert(result);
+ result->err = err;
+
+ if (result->stat && err == 0 && stat) {
+ *result->stat = *stat;
+ } else {
+ result->stat = NULL ;
+ }
+}
+
+static int CheckVersionRequest_init(zhandle_t *zh, struct CheckVersionRequest *req,
+ const char *path, int version)
+{
+ int rc ;
+ assert(req);
+ rc = Request_path_init(zh, 0, &req->path, path);
+ if (rc != ZOK) {
+ return rc;
+ }
+ req->version = version;
+
+ return ZOK;
+}
+
+int zoo_amulti(zhandle_t *zh, int count, const zoo_op_t *ops,
+ zoo_op_result_t *results, void_completion_t completion, const void *data)
+{
+ struct RequestHeader h = { STRUCT_INITIALIZER(xid, get_xid()), STRUCT_INITIALIZER(type, ZOO_MULTI_OP) };
+ struct MultiHeader mh = { STRUCT_INITIALIZER(type, -1), STRUCT_INITIALIZER(done, 1), STRUCT_INITIALIZER(err, -1) };
+ struct oarchive *oa = create_buffer_oarchive();
+ completion_head_t clist = { 0 };
+
+ int rc = serialize_RequestHeader(oa, "header", &h);
+
+ int index = 0;
+ for (index=0; index < count; index++) {
+ const zoo_op_t *op = ops+index;
+ zoo_op_result_t *result = results+index;
+ completion_list_t *entry = NULL;
+
+ struct MultiHeader mh = { STRUCT_INITIALIZER(type, op->type), STRUCT_INITIALIZER(done, 0), STRUCT_INITIALIZER(err, -1) };
+ rc = rc < 0 ? rc : serialize_MultiHeader(oa, "multiheader", &mh);
+
+ switch(op->type) {
+ case ZOO_CREATE_OP: {
+ struct CreateRequest req;
+
+ rc = rc < 0 ? rc : CreateRequest_init(zh, &req,
+ op->create_op.path, op->create_op.data,
+ op->create_op.datalen, op->create_op.acl,
+ op->create_op.flags);
+ rc = rc < 0 ? rc : serialize_CreateRequest(oa, "req", &req);
+ result->value = op->create_op.buf;
+ result->valuelen = op->create_op.buflen;
+
+ enter_critical(zh);
+ entry = create_completion_entry(h.xid, COMPLETION_STRING, op_result_string_completion, result, 0, 0);
+ leave_critical(zh);
+ free_duplicate_path(req.path, op->create_op.path);
+ break;
+ }
+
+ case ZOO_DELETE_OP: {
+ struct DeleteRequest req;
+ rc = rc < 0 ? rc : DeleteRequest_init(zh, &req, op->delete_op.path, op->delete_op.version);
+ rc = rc < 0 ? rc : serialize_DeleteRequest(oa, "req", &req);
+
+ enter_critical(zh);
+ entry = create_completion_entry(h.xid, COMPLETION_VOID, op_result_void_completion, result, 0, 0);
+ leave_critical(zh);
+ free_duplicate_path(req.path, op->delete_op.path);
+ break;
+ }
+
+ case ZOO_SETDATA_OP: {
+ struct SetDataRequest req;
+ rc = rc < 0 ? rc : SetDataRequest_init(zh, &req,
+ op->set_op.path, op->set_op.data,
+ op->set_op.datalen, op->set_op.version);
+ rc = rc < 0 ? rc : serialize_SetDataRequest(oa, "req", &req);
+ result->stat = op->set_op.stat;
+
+ enter_critical(zh);
+ entry = create_completion_entry(h.xid, COMPLETION_STAT, op_result_stat_completion, result, 0, 0);
+ leave_critical(zh);
+ free_duplicate_path(req.path, op->set_op.path);
+ break;
+ }
+
+ case ZOO_CHECK_OP: {
+ struct CheckVersionRequest req;
+ rc = rc < 0 ? rc : CheckVersionRequest_init(zh, &req,
+ op->check_op.path, op->check_op.version);
+ rc = rc < 0 ? rc : serialize_CheckVersionRequest(oa, "req", &req);
+
+ enter_critical(zh);
+ entry = create_completion_entry(h.xid, COMPLETION_VOID, op_result_void_completion, result, 0, 0);
+ leave_critical(zh);
+ free_duplicate_path(req.path, op->check_op.path);
+ break;
+ }
+
+ default:
+ LOG_ERROR(("Unimplemented sub-op type=%d in multi-op", op->type));
+ return ZUNIMPLEMENTED;
+ }
+
+ queue_completion(&clist, entry, 0);
+ }
+
+ rc = rc < 0 ? rc : serialize_MultiHeader(oa, "multiheader", &mh);
+
+ /* BEGIN: CRTICIAL SECTION */
+ enter_critical(zh);
+ rc = rc < 0 ? rc : add_multi_completion(zh, h.xid, completion, data, &clist);
+ rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
+ get_buffer_len(oa));
+ leave_critical(zh);
+
+ /* We queued the buffer, so don't free it */
+ close_buffer_oarchive(&oa, 0);
+
+ LOG_DEBUG(("Sending multi request xid=%#x with %d subrequests to %s",
+ h.xid, index, format_current_endpoint_info(zh)));
+ /* make a best (non-blocking) effort to send the requests asap */
+ adaptor_send_queue(zh, 0);
+
+ return (rc < 0) ? ZMARSHALLINGERROR : ZOK;
+}
+
+void zoo_create_op_init(zoo_op_t *op, const char *path, const char *value,
+ int valuelen, const struct ACL_vector *acl, int flags,
+ char *path_buffer, int path_buffer_len)
+{
+ assert(op);
+ op->type = ZOO_CREATE_OP;
+ op->create_op.path = path;
+ op->create_op.data = value;
+ op->create_op.datalen = valuelen;
+ op->create_op.acl = acl;
+ op->create_op.flags = flags;
+ op->create_op.buf = path_buffer;
+ op->create_op.buflen = path_buffer_len;
+}
+
+void zoo_delete_op_init(zoo_op_t *op, const char *path, int version)
+{
+ assert(op);
+ op->type = ZOO_DELETE_OP;
+ op->delete_op.path = path;
+ op->delete_op.version = version;
+}
+
+void zoo_set_op_init(zoo_op_t *op, const char *path, const char *buffer,
+ int buflen, int version, struct Stat *stat)
+{
+ assert(op);
+ op->type = ZOO_SETDATA_OP;
+ op->set_op.path = path;
+ op->set_op.data = buffer;
+ op->set_op.datalen = buflen;
+ op->set_op.version = version;
+ op->set_op.stat = stat;
+}
+
+void zoo_check_op_init(zoo_op_t *op, const char *path, int version)
+{
+ assert(op);
+ op->type = ZOO_CHECK_OP;
+ op->check_op.path = path;
+ op->check_op.version = version;
+}
+
+int zoo_multi(zhandle_t *zh, int count, const zoo_op_t *ops, zoo_op_result_t *results)
+{
+ int rc;
+
+ struct sync_completion *sc = alloc_sync_completion();
+ if (!sc) {
+ return ZSYSTEMERROR;
+ }
+
+ rc = zoo_amulti(zh, count, ops, results, SYNCHRONOUS_MARKER, sc);
+ if (rc == ZOK) {
+ wait_sync_completion(sc);
+ rc = sc->rc;
+ }
+ free_sync_completion(sc);
+
+ return rc;
+}
+
+/* specify timeout of 0 to make the function non-blocking */
+/* timeout is in milliseconds */
+int flush_send_queue(zhandle_t*zh, int timeout)
+{
+ int rc= ZOK;
+ struct timeval started;
+#ifdef WIN32
+ fd_set pollSet;
+ struct timeval wait;
+#endif
+ gettimeofday(&started,0);
+ // we can't use dequeue_buffer() here because if (non-blocking) send_buffer()
+ // returns EWOULDBLOCK we'd have to put the buffer back on the queue.
+ // we use a recursive lock instead and only dequeue the buffer if a send was
+ // successful
+ lock_buffer_list(&zh->to_send);
+ while (zh->to_send.head != 0&& zh->state == ZOO_CONNECTED_STATE) {
+ if(timeout!=0){
+ int elapsed;
+ struct timeval now;
+ gettimeofday(&now,0);
+ elapsed=calculate_interval(&started,&now);
+ if (elapsed>timeout) {
+ rc = ZOPERATIONTIMEOUT;
+ break;
+ }
+
+#ifdef WIN32
+ wait = get_timeval(timeout-elapsed);
+ FD_ZERO(&pollSet);
+ FD_SET(zh->fd, &pollSet);
+ // Poll the socket
+ rc = select((int)(zh->fd)+1, NULL, &pollSet, NULL, &wait);
+#else
+ struct pollfd fds;
+ fds.fd = zh->fd;
+ fds.events = POLLOUT;
+ fds.revents = 0;
+ rc = poll(&fds, 1, timeout-elapsed);
+#endif
+ if (rc<=0) {
+ /* timed out or an error or POLLERR */
+ rc = rc==0 ? ZOPERATIONTIMEOUT : ZSYSTEMERROR;
+ break;
+ }
+ }
+
+ rc = send_buffer(zh->fd, zh->to_send.head);
+ if(rc==0 && timeout==0){
+ /* send_buffer would block while sending this buffer */
+ rc = ZOK;
+ break;
+ }
+ if (rc < 0) {
+ rc = ZCONNECTIONLOSS;
+ break;
+ }
+ // if the buffer has been sent successfully, remove it from the queue
+ if (rc > 0)
+ remove_buffer(&zh->to_send);
+ gettimeofday(&zh->last_send, 0);
+ rc = ZOK;
+ }
+ unlock_buffer_list(&zh->to_send);
+ return rc;
+}
+
+const char* zerror(int c)
+{
+ switch (c){
+ case ZOK:
+ return "ok";
+ case ZSYSTEMERROR:
+ return "system error";
+ case ZRUNTIMEINCONSISTENCY:
+ return "run time inconsistency";
+ case ZDATAINCONSISTENCY:
+ return "data inconsistency";
+ case ZCONNECTIONLOSS:
+ return "connection loss";
+ case ZMARSHALLINGERROR:
+ return "marshalling error";
+ case ZUNIMPLEMENTED:
+ return "unimplemented";
+ case ZOPERATIONTIMEOUT:
+ return "operation timeout";
+ case ZBADARGUMENTS:
+ return "bad arguments";
+ case ZINVALIDSTATE:
+ return "invalid zhandle state";
+ case ZAPIERROR:
+ return "api error";
+ case ZNONODE:
+ return "no node";
+ case ZNOAUTH:
+ return "not authenticated";
+ case ZBADVERSION:
+ return "bad version";
+ case ZNOCHILDRENFOREPHEMERALS:
+ return "no children for ephemerals";
+ case ZNODEEXISTS:
+ return "node exists";
+ case ZNOTEMPTY:
+ return "not empty";
+ case ZSESSIONEXPIRED:
+ return "session expired";
+ case ZINVALIDCALLBACK:
+ return "invalid callback";
+ case ZINVALIDACL:
+ return "invalid acl";
+ case ZAUTHFAILED:
+ return "authentication failed";
+ case ZCLOSING:
+ return "zookeeper is closing";
+ case ZNOTHING:
+ return "(not error) no server responses to process";
+ case ZSESSIONMOVED:
+ return "session moved to another server, so operation is ignored";
+ }
+ if (c > 0) {
+ return strerror(c);
+ }
+ return "unknown error";
+}
+
+int zoo_add_auth(zhandle_t *zh,const char* scheme,const char* cert,
+ int certLen,void_completion_t completion, const void *data)
+{
+ struct buffer auth;
+ auth_info *authinfo;
+ if(scheme==NULL || zh==NULL)
+ return ZBADARGUMENTS;
+
+ if (is_unrecoverable(zh))
+ return ZINVALIDSTATE;
+
+ // [ZOOKEEPER-800] zoo_add_auth should return ZINVALIDSTATE if
+ // the connection is closed.
+ if (zoo_state(zh) == 0) {
+ return ZINVALIDSTATE;
+ }
+
+ if(cert!=NULL && certLen!=0){
+ auth.buff=calloc(1,certLen);
+ if(auth.buff==0) {
+ return ZSYSTEMERROR;
+ }
+ memcpy(auth.buff,cert,certLen);
+ auth.len=certLen;
+ } else {
+ auth.buff = 0;
+ auth.len = 0;
+ }
+
+ zoo_lock_auth(zh);
+ authinfo = (auth_info*) malloc(sizeof(auth_info));
+ authinfo->scheme=strdup(scheme);
+ authinfo->auth=auth;
+ authinfo->completion=completion;
+ authinfo->data=data;
+ authinfo->next = NULL;
+ add_last_auth(&zh->auth_h, authinfo);
+ zoo_unlock_auth(zh);
+
+ if(zh->state == ZOO_CONNECTED_STATE || zh->state == ZOO_ASSOCIATING_STATE)
+ return send_last_auth_info(zh);
+
+ return ZOK;
+}
+
+static const char* format_endpoint_info(const struct sockaddr_storage* ep)
+{
+ static char buf[128];
+ char addrstr[128];
+ void *inaddr;
+#ifdef WIN32
+ char * addrstring;
+#endif
+ int port;
+ if(ep==0)
+ return "null";
+
+#if defined(AF_INET6)
+ if(ep->ss_family==AF_INET6){
+ inaddr=&((struct sockaddr_in6*)ep)->sin6_addr;
+ port=((struct sockaddr_in6*)ep)->sin6_port;
+ } else {
+#endif
+ inaddr=&((struct sockaddr_in*)ep)->sin_addr;
+ port=((struct sockaddr_in*)ep)->sin_port;
+#if defined(AF_INET6)
+ }
+#endif
+#ifdef WIN32
+ addrstring = inet_ntoa (*(struct in_addr*)inaddr);
+ sprintf(buf,"%s:%d",addrstring,ntohs(port));
+#else
+ inet_ntop(ep->ss_family,inaddr,addrstr,sizeof(addrstr)-1);
+ sprintf(buf,"%s:%d",addrstr,ntohs(port));
+#endif
+ return buf;
+}
+
+static const char* format_current_endpoint_info(zhandle_t* zh)
+{
+ return format_endpoint_info(&zh->addrs[zh->connect_index]);
+}
+
+void zoo_deterministic_conn_order(int yesOrNo)
+{
+ disable_conn_permute=yesOrNo;
+}
+
+/*---------------------------------------------------------------------------*
+ * SYNC API
+ *---------------------------------------------------------------------------*/
+int zoo_create(zhandle_t *zh, const char *path, const char *value,
+ int valuelen, const struct ACL_vector *acl, int flags,
+ char *path_buffer, int path_buffer_len)
+{
+ struct sync_completion *sc = alloc_sync_completion();
+ int rc;
+ if (!sc) {
+ return ZSYSTEMERROR;
+ }
+ sc->u.str.str = path_buffer;
+ sc->u.str.str_len = path_buffer_len;
+ rc=zoo_acreate(zh, path, value, valuelen, acl, flags, SYNCHRONOUS_MARKER, sc);
+ if(rc==ZOK){
+ wait_sync_completion(sc);
+ rc = sc->rc;
+ }
+ free_sync_completion(sc);
+ return rc;
+}
+
+int zoo_delete(zhandle_t *zh, const char *path, int version)
+{
+ struct sync_completion *sc = alloc_sync_completion();
+ int rc;
+ if (!sc) {
+ return ZSYSTEMERROR;
+ }
+ rc=zoo_adelete(zh, path, version, SYNCHRONOUS_MARKER, sc);
+ if(rc==ZOK){
+ wait_sync_completion(sc);
+ rc = sc->rc;
+ }
+ free_sync_completion(sc);
+ return rc;
+}
+
+int zoo_exists(zhandle_t *zh, const char *path, int watch, struct Stat *stat)
+{
+ return zoo_wexists(zh,path,watch?zh->watcher:0,zh->context,stat);
+}
+
+int zoo_wexists(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx, struct Stat *stat)
+{
+ struct sync_completion *sc = alloc_sync_completion();
+ int rc;
+ if (!sc) {
+ return ZSYSTEMERROR;
+ }
+ rc=zoo_awexists(zh,path,watcher,watcherCtx,SYNCHRONOUS_MARKER, sc);
+ if(rc==ZOK){
+ wait_sync_completion(sc);
+ rc = sc->rc;
+ if (rc == 0&& stat) {
+ *stat = sc->u.stat;
+ }
+ }
+ free_sync_completion(sc);
+ return rc;
+}
+
+int zoo_get(zhandle_t *zh, const char *path, int watch, char *buffer,
+ int* buffer_len, struct Stat *stat)
+{
+ return zoo_wget(zh,path,watch?zh->watcher:0,zh->context,
+ buffer,buffer_len,stat);
+}
+
+int zoo_wget(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ char *buffer, int* buffer_len, struct Stat *stat)
+{
+ struct sync_completion *sc;
+ int rc=0;
+
+ if(buffer_len==NULL)
+ return ZBADARGUMENTS;
+ if((sc=alloc_sync_completion())==NULL)
+ return ZSYSTEMERROR;
+
+ sc->u.data.buffer = buffer;
+ sc->u.data.buff_len = *buffer_len;
+ rc=zoo_awget(zh, path, watcher, watcherCtx, SYNCHRONOUS_MARKER, sc);
+ if(rc==ZOK){
+ wait_sync_completion(sc);
+ rc = sc->rc;
+ if (rc == 0) {
+ if(stat)
+ *stat = sc->u.data.stat;
+ *buffer_len = sc->u.data.buff_len;
+ }
+ }
+ free_sync_completion(sc);
+ return rc;
+}
+
+int zoo_set(zhandle_t *zh, const char *path, const char *buffer, int buflen,
+ int version)
+{
+ return zoo_set2(zh, path, buffer, buflen, version, 0);
+}
+
+int zoo_set2(zhandle_t *zh, const char *path, const char *buffer, int buflen,
+ int version, struct Stat *stat)
+{
+ struct sync_completion *sc = alloc_sync_completion();
+ int rc;
+ if (!sc) {
+ return ZSYSTEMERROR;
+ }
+ rc=zoo_aset(zh, path, buffer, buflen, version, SYNCHRONOUS_MARKER, sc);
+ if(rc==ZOK){
+ wait_sync_completion(sc);
+ rc = sc->rc;
+ if (rc == 0 && stat) {
+ *stat = sc->u.stat;
+ }
+ }
+ free_sync_completion(sc);
+ return rc;
+}
+
+static int zoo_wget_children_(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ struct String_vector *strings)
+{
+ struct sync_completion *sc = alloc_sync_completion();
+ int rc;
+ if (!sc) {
+ return ZSYSTEMERROR;
+ }
+ rc= zoo_awget_children (zh, path, watcher, watcherCtx, SYNCHRONOUS_MARKER, sc);
+ if(rc==ZOK){
+ wait_sync_completion(sc);
+ rc = sc->rc;
+ if (rc == 0) {
+ if (strings) {
+ *strings = sc->u.strs2;
+ } else {
+ deallocate_String_vector(&sc->u.strs2);
+ }
+ }
+ }
+ free_sync_completion(sc);
+ return rc;
+}
+
+static int zoo_wget_children2_(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ struct String_vector *strings, struct Stat *stat)
+{
+ struct sync_completion *sc = alloc_sync_completion();
+ int rc;
+ if (!sc) {
+ return ZSYSTEMERROR;
+ }
+ rc= zoo_awget_children2(zh, path, watcher, watcherCtx, SYNCHRONOUS_MARKER, sc);
+
+ if(rc==ZOK){
+ wait_sync_completion(sc);
+ rc = sc->rc;
+ if (rc == 0) {
+ *stat = sc->u.strs_stat.stat2;
+ if (strings) {
+ *strings = sc->u.strs_stat.strs2;
+ } else {
+ deallocate_String_vector(&sc->u.strs_stat.strs2);
+ }
+ }
+ }
+ free_sync_completion(sc);
+ return rc;
+}
+
+int zoo_get_children(zhandle_t *zh, const char *path, int watch,
+ struct String_vector *strings)
+{
+ return zoo_wget_children_(zh,path,watch?zh->watcher:0,zh->context,strings);
+}
+
+int zoo_wget_children(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ struct String_vector *strings)
+{
+ return zoo_wget_children_(zh,path,watcher,watcherCtx,strings);
+}
+
+int zoo_get_children2(zhandle_t *zh, const char *path, int watch,
+ struct String_vector *strings, struct Stat *stat)
+{
+ return zoo_wget_children2_(zh,path,watch?zh->watcher:0,zh->context,strings,stat);
+}
+
+int zoo_wget_children2(zhandle_t *zh, const char *path,
+ watcher_fn watcher, void* watcherCtx,
+ struct String_vector *strings, struct Stat *stat)
+{
+ return zoo_wget_children2_(zh,path,watcher,watcherCtx,strings,stat);
+}
+
+int zoo_get_acl(zhandle_t *zh, const char *path, struct ACL_vector *acl,
+ struct Stat *stat)
+{
+ struct sync_completion *sc = alloc_sync_completion();
+ int rc;
+ if (!sc) {
+ return ZSYSTEMERROR;
+ }
+ rc=zoo_aget_acl(zh, path, SYNCHRONOUS_MARKER, sc);
+ if(rc==ZOK){
+ wait_sync_completion(sc);
+ rc = sc->rc;
+ if (rc == 0&& stat) {
+ *stat = sc->u.acl.stat;
+ }
+ if (rc == 0) {
+ if (acl) {
+ *acl = sc->u.acl.acl;
+ } else {
+ deallocate_ACL_vector(&sc->u.acl.acl);
+ }
+ }
+ }
+ free_sync_completion(sc);
+ return rc;
+}
+
+int zoo_set_acl(zhandle_t *zh, const char *path, int version,
+ const struct ACL_vector *acl)
+{
+ struct sync_completion *sc = alloc_sync_completion();
+ int rc;
+ if (!sc) {
+ return ZSYSTEMERROR;
+ }
+ rc=zoo_aset_acl(zh, path, version, (struct ACL_vector*)acl,
+ SYNCHRONOUS_MARKER, sc);
+ if(rc==ZOK){
+ wait_sync_completion(sc);
+ rc = sc->rc;
+ }
+ free_sync_completion(sc);
+ return rc;
+}
diff --git a/src/c/tests/CollectionUtil.h b/src/c/tests/CollectionUtil.h
new file mode 100644
index 0000000..dd34811
--- /dev/null
+++ b/src/c/tests/CollectionUtil.h
@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 _COLLECTION_UTIL_H_
+#define _COLLECTION_UTIL_H_
+
+/**
+ * \file
+ * CollectionBuilder and DictionaryBuilder classes and collection utility functions
+ */
+
+namespace Util
+{
+
+// *********************************************************
+/** A shortcut to use for building collections.
+ * This class is a wrapper around standard STL collection containers such as vector.
+ * It allows one to conveniently build collections at the variable initialization time:
+ * \code
+ * #include "CollectionUtil.h"
+ * #include "Vector.h" // for ostream << operator overload for STL vector
+ * using Util;
+ *
+ * int main()
+ * {
+ * typedef vector MyVector;
+ * MyVector myVector=CollectionBuilder()("str1")("str2")("str3");
+ * cout<
+class CollectionBuilder
+{
+public:
+ /// Type of the collection container.
+ typedef CONT CollectionType;
+ /// Container's value type.
+ typedef typename CollectionType::value_type value_type;
+ /// Container's constant iterator type.
+ typedef typename CollectionType::const_iterator const_iterator;
+ /// Container's size type.
+ typedef typename CollectionType::size_type size_type;
+
+ /** Operator function call overload to allow call chaining.
+ * \param value the value to be inserted into the container
+ */
+ CollectionBuilder& operator()(const value_type& value){
+ return push_back(value);
+ }
+ /** Same as regular STL push_back() but allows call chaining.
+ * \param value the value to be inserted into the container
+ */
+ CollectionBuilder& push_back(const value_type& value){
+ collection_.push_back(value);
+ return *this;
+ }
+ /// \name Standard STL container interface
+ /// @{
+ const_iterator begin() const{return collection_.begin();}
+ const_iterator end() const{return collection_.end();}
+ size_type size() const{return collection_.size();}
+ void clear() {collection_.clear();}
+ ///@}
+ /// Explicit typecast operator.
+ operator const CollectionType&() const {return collection_;}
+private:
+ /// \cond PRIVATE
+ CollectionType collection_;
+ /// \endcond
+};
+
+
+// *********************************************************
+/** A shortcut to use for building dictionaries.
+ * This class is a wrapper around standard STL associative containers such as map.
+ * It allows one to conveniently build dictionaries at the variable initialization time:
+ * \code
+ * #include "CollectionUtil.h"
+ * #include "Map.h" // for ostream << operator overload for STL map
+ * using Util;
+ *
+ * int main()
+ * {
+ * typedef map MyMap;
+ * MyMap myMap=DictionaryBuilder()("str1",1)("str2",2)("str3",3);
+ * cout<
+class DictionaryBuilder
+{
+public:
+ /// The type of the associative container
+ typedef CONT DictionaryType;
+ /// Container's element type (usually a pair)
+ typedef typename DictionaryType::value_type value_type;
+ /// Container's key type
+ typedef typename DictionaryType::key_type key_type;
+ /// Container's value type
+ typedef typename DictionaryType::mapped_type mapped_type;
+ /// Container's constant iterator type
+ typedef typename DictionaryType::const_iterator const_iterator;
+ /// Container's writable iterator type
+ typedef typename DictionaryType::iterator iterator;
+ /// Container's size type
+ typedef typename DictionaryType::size_type size_type;
+
+ /** Operator function call overload to allow call chaining.
+ * \param key the value key to be inserted
+ * \param value the value to be inserted into the container
+ * \return a non-const reference to self
+ */
+ DictionaryBuilder& operator()(const key_type& key,const mapped_type& value){
+ dict_.insert(value_type(key,value));
+ return *this;
+ }
+ /** Lookup value by key.
+ * \param key the key associated with the value.
+ * \return a non-const iterator pointing to the element whose key matched the \a key parameter
+ */
+ iterator find(const key_type& key){
+ return dict_.find(key);
+ }
+ /** Lookup value by key.
+ * \param key the key associated with the value.
+ * \return a const iterator pointing to the element whose key matched the \a key parameter
+ */
+ const_iterator find(const key_type& key) const{
+ return dict_.find(key);
+ }
+
+ /// \name Standard STL container interface
+ /// @{
+ const_iterator begin() const{return dict_.begin();}
+ const_iterator end() const{return dict_.end();}
+ size_type size() const{return dict_.size();}
+ void clear() {dict_.clear();}
+ ///@}
+ /// Explicit typecast operator.
+ operator const DictionaryType&() const {return dict_;}
+private:
+ DictionaryType dict_;
+};
+
+
+// ***********************************************************
+/** Deletes all dynamically allocated elements of a collection.
+ * C::value_type is expected to be a pointer to a dynamically allocated object, or it won't compile.
+ * The function will iterate over all container elements and call delete for each of them.
+ * \param c a collection (vector,set) whose elements are being deleted.
+ */
+template
+void clearCollection(C& c){
+ for(typename C::const_iterator it=c.begin();it!=c.end();++it)
+ delete *it;
+ c.clear();
+}
+
+/** Deletes all dynamically allocated values of the assotiative container.
+ * The function expects the M::value_type to be a pair<..., ptr_to_type>, or it won't compile.
+ * It first deletes the objects pointed to by ptr_to_type
+ * and then clears (calls m.clear()) the container.
+ * \param m an associative container (map,hash_map) whose elements are being deleted.
+ */
+template
+void clearMap(M& m){
+ for(typename M::const_iterator it=m.begin();it!=m.end();++it)
+ delete it->second;
+ m.clear();
+}
+
+} // namespace Util
+
+
+#endif // _COLLECTION_UTIL_H_
diff --git a/src/c/tests/CppAssertHelper.h b/src/c/tests/CppAssertHelper.h
new file mode 100644
index 0000000..3926f51
--- /dev/null
+++ b/src/c/tests/CppAssertHelper.h
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 CPPASSERTHELPER_H_
+#define CPPASSERTHELPER_H_
+
+#include
+
+// make it possible to specify location of the ASSERT call
+#define CPPUNIT_ASSERT_EQUAL_LOC(expected,actual,file,line) \
+ ( CPPUNIT_NS::assertEquals( (expected), \
+ (actual), \
+ CPPUNIT_NS::SourceLine(file,line), \
+ "" ) )
+
+#define CPPUNIT_ASSERT_EQUAL_MESSAGE_LOC(message,expected,actual,file,line) \
+ ( CPPUNIT_NS::assertEquals( (expected), \
+ (actual), \
+ CPPUNIT_NS::SourceLine(file,line), \
+ (message) ) )
+
+#endif /*CPPASSERTHELPER_H_*/
diff --git a/src/c/tests/LibCMocks.cc b/src/c/tests/LibCMocks.cc
new file mode 100644
index 0000000..5db4553
--- /dev/null
+++ b/src/c/tests/LibCMocks.cc
@@ -0,0 +1,333 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+#include
+#include
+#include
+
+#include "Util.h"
+#include "LibCMocks.h"
+
+#undef USING_DUMA
+
+using namespace std;
+
+// *****************************************************************************
+// gethostbyname
+
+struct hostent* gethostbyname(const char *name) {
+ if(!Mock_gethostbyname::mock_)
+ return LIBC_SYMBOLS.gethostbyname(name);
+ return Mock_gethostbyname::mock_->call(name);
+}
+
+Mock_gethostbyname* Mock_gethostbyname::mock_=0;
+
+Mock_gethostbyname::~Mock_gethostbyname(){
+ mock_=0;
+ for(unsigned int i=0;icall(p1,p2);
+}
+#endif
+
+void* Mock_calloc::call(size_t p1, size_t p2){
+#ifndef USING_DUMA
+ if(counter++ ==callsBeforeFailure){
+ counter=0;
+ errno=errnoOnFailure;
+ return 0;
+ }
+ return CALL_REAL(calloc,(p1,p2));
+#else
+ return 0;
+#endif
+}
+
+Mock_calloc* Mock_calloc::mock_=0;
+
+// *****************************************************************************
+// realloc
+
+#ifndef USING_DUMA
+DECLARE_WRAPPER(void*,realloc,(void* p, size_t s)){
+ if(!Mock_realloc::mock_)
+ return LIBC_SYMBOLS.realloc(p,s);
+ return Mock_realloc::mock_->call(p,s);
+}
+#endif
+
+Mock_realloc* Mock_realloc::mock_=0;
+
+void* Mock_realloc::call(void* p, size_t s){
+ if(counter++ ==callsBeforeFailure){
+ counter=0;
+ errno=errnoOnFailure;
+ return 0;
+ }
+ return LIBC_SYMBOLS.realloc(p,s);
+}
+
+// *****************************************************************************
+// random
+RANDOM_RET_TYPE random(){
+ if(!Mock_random::mock_)
+ return LIBC_SYMBOLS.random();
+ return Mock_random::mock_->call();
+}
+
+void srandom(unsigned long seed){
+ if (!Mock_random::mock_)
+ LIBC_SYMBOLS.srandom(seed);
+ else
+ Mock_random::mock_->setSeed(seed);
+}
+
+Mock_random* Mock_random::mock_=0;
+
+int Mock_random::call(){
+ assert("Must specify one or more random integers"&&(randomReturns.size()!=0));
+ return randomReturns[currentIdx++ % randomReturns.size()];
+}
+
+// *****************************************************************************
+// free
+#ifndef USING_DUMA
+DECLARE_WRAPPER(void,free,(void* p)){
+ if(Mock_free_noop::mock_ && !Mock_free_noop::mock_->nested)
+ Mock_free_noop::mock_->call(p);
+ else
+ CALL_REAL(free,(p));
+}
+#endif
+
+void Mock_free_noop::call(void* p){
+ // on cygwin libc++ is linked statically
+ // push_back() may call free(), hence the nesting guards
+ synchronized(mx);
+ nested++;
+ callCounter++;
+ requested.push_back(p);
+ nested--;
+}
+void Mock_free_noop::freeRequested(){
+#ifndef USING_DUMA
+ synchronized(mx);
+ for(unsigned i=0; icallSocket(domain,type,protocol);
+}
+
+int close(int fd){
+ if (!Mock_socket::mock_)
+ return LIBC_SYMBOLS.close(fd);
+ return Mock_socket::mock_->callClose(fd);
+}
+
+int getsockopt(int s,int level,int optname,void *optval,socklen_t *optlen){
+ if (!Mock_socket::mock_)
+ return LIBC_SYMBOLS.getsockopt(s,level,optname,optval,optlen);
+ return Mock_socket::mock_->callGet(s,level,optname,optval,optlen);
+}
+
+int setsockopt(int s,int level,int optname,const void *optval,socklen_t optlen){
+ if (!Mock_socket::mock_)
+ return LIBC_SYMBOLS.setsockopt(s,level,optname,optval,optlen);
+ return Mock_socket::mock_->callSet(s,level,optname,optval,optlen);
+}
+int connect(int s,const struct sockaddr *addr,socklen_t len){
+ if (!Mock_socket::mock_)
+ return LIBC_SYMBOLS.connect(s,addr,len);
+ return Mock_socket::mock_->callConnect(s,addr,len);
+}
+ssize_t send(int s,const void *buf,size_t len,int flags){
+ if (!Mock_socket::mock_)
+ return LIBC_SYMBOLS.send(s,buf,len,flags);
+ return Mock_socket::mock_->callSend(s,buf,len,flags);
+}
+
+ssize_t recv(int s,void *buf,size_t len,int flags){
+ if (!Mock_socket::mock_)
+ return LIBC_SYMBOLS.recv(s,buf,len,flags);
+ return Mock_socket::mock_->callRecv(s,buf,len,flags);
+}
+
+Mock_socket* Mock_socket::mock_=0;
+
+// *****************************************************************************
+// fcntl
+extern "C" int fcntl(int fd,int cmd,...){
+ va_list va;
+ va_start(va,cmd);
+ void* arg = va_arg(va, void *);
+ va_end (va);
+ if (!Mock_fcntl::mock_)
+ return LIBC_SYMBOLS.fcntl(fd,cmd,arg);
+ return Mock_fcntl::mock_->call(fd,cmd,arg);
+}
+
+Mock_fcntl* Mock_fcntl::mock_=0;
+
+// *****************************************************************************
+// select
+int select(int nfds,fd_set *rfds,fd_set *wfds,fd_set *efds,struct timeval *timeout){
+ if (!Mock_select::mock_)
+ return LIBC_SYMBOLS.select(nfds,rfds,wfds,efds,timeout);
+ return Mock_select::mock_->call(nfds,rfds,wfds,efds,timeout);
+}
+
+Mock_select* Mock_select::mock_=0;
+
+// *****************************************************************************
+// poll
+Mock_poll* Mock_poll::mock_=0;
+int poll(struct pollfd *fds, POLL_NFDS_TYPE nfds, int timeout){
+ if (!Mock_poll::mock_)
+ return LIBC_SYMBOLS.poll(fds,nfds,timeout);
+ return Mock_poll::mock_->call(fds,nfds,timeout);
+
+}
+
+/*
+ * Recent gcc with -O2 and glibc FORTIFY feature may cause our poll
+ * mock to be ignored.
+ */
+#if __USE_FORTIFY_LEVEL > 0
+int __poll_chk (struct pollfd *__fds, nfds_t __nfds, int __timeout,
+ __SIZE_TYPE__ __fdslen) {
+ return poll(__fds, __nfds, __timeout);
+}
+#endif
+
+// *****************************************************************************
+// gettimeofday
+int gettimeofday(struct timeval *tp, GETTIMEOFDAY_ARG2_TYPE tzp){
+ if (!Mock_gettimeofday::mock_)
+ return LIBC_SYMBOLS.gettimeofday(tp,tzp);
+ return Mock_gettimeofday::mock_->call(tp,tzp);
+}
+
+Mock_gettimeofday* Mock_gettimeofday::mock_=0;
+
diff --git a/src/c/tests/LibCMocks.h b/src/c/tests/LibCMocks.h
new file mode 100644
index 0000000..5b07cda
--- /dev/null
+++ b/src/c/tests/LibCMocks.h
@@ -0,0 +1,408 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 LIBCMOCKS_H_
+#define LIBCMOCKS_H_
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include "MocksBase.h"
+#include "LibCSymTable.h"
+#include "ThreadingUtil.h"
+
+// *****************************************************************************
+// gethostbyname
+
+class Mock_gethostbyname: public Mock
+{
+public:
+ struct HostEntry: public hostent {
+ HostEntry(const char* hostName,short addrtype);
+ ~HostEntry();
+ HostEntry& addAlias(const char* alias);
+ HostEntry& addAddress(const char* addr4);
+ };
+
+ Mock_gethostbyname():current(0){mock_=this;}
+ virtual ~Mock_gethostbyname();
+ HostEntry& addHostEntry(const char* hostName,short addrtype=AF_INET);
+ virtual hostent* call(const char* name);
+
+ typedef std::vector HostEntryCollection;
+ HostEntryCollection gethostbynameReturns;
+ int current;
+ static Mock_gethostbyname* mock_;
+};
+
+class MockFailed_gethostbyname: public Mock_gethostbyname
+{
+public:
+ MockFailed_gethostbyname():h_errnoReturn(HOST_NOT_FOUND) {}
+
+ int h_errnoReturn;
+ virtual hostent* call(const char* name) {
+ h_errno=h_errnoReturn;
+ return 0;
+ }
+};
+
+// *****************************************************************************
+// calloc
+
+class Mock_calloc: public Mock
+{
+public:
+ Mock_calloc():errnoOnFailure(ENOMEM),callsBeforeFailure(-1),counter(0) {
+ mock_=this;
+ }
+ virtual ~Mock_calloc() {mock_=0;}
+
+ int errnoOnFailure;
+ int callsBeforeFailure;
+ int counter;
+ virtual void* call(size_t p1, size_t p2);
+
+ static Mock_calloc* mock_;
+};
+
+// *****************************************************************************
+// realloc
+
+class Mock_realloc: public Mock
+{
+public:
+ Mock_realloc():errnoOnFailure(ENOMEM),callsBeforeFailure(-1),counter(0) {
+ mock_=this;
+ }
+ virtual ~Mock_realloc() {mock_=0;}
+
+ int errnoOnFailure;
+ int callsBeforeFailure;
+ int counter;
+ virtual void* call(void* p, size_t s);
+
+ static Mock_realloc* mock_;
+};
+
+// *****************************************************************************
+// random
+
+class Mock_random: public Mock
+{
+public:
+ Mock_random():currentIdx(0) {mock_=this;}
+ virtual ~Mock_random() {mock_=0;}
+
+ int currentIdx;
+ std::vector randomReturns;
+ virtual int call();
+ void setSeed(unsigned long){currentIdx=0;}
+
+ static Mock_random* mock_;
+};
+
+// *****************************************************************************
+// no-op free; keeps track of all deallocation requests
+class Mock_free_noop: public Mock
+{
+ Mutex mx;
+ std::vector requested;
+public:
+ Mock_free_noop():nested(0),callCounter(0){mock_=this;}
+ virtual ~Mock_free_noop(){
+ mock_=0;
+ freeRequested();
+ }
+
+ int nested;
+ int callCounter;
+ virtual void call(void* p);
+ void freeRequested();
+ void disable(){mock_=0;}
+ // returns number of times the pointer was freed
+ int getFreeCount(void*);
+ bool isFreed(void*);
+
+ static Mock_free_noop* mock_;
+};
+
+// *****************************************************************************
+// socket and related system calls
+
+class Mock_socket: public Mock
+{
+public:
+ static const int FD=63;
+ Mock_socket():socketReturns(FD),closeReturns(0),getsocketoptReturns(0),
+ optvalSO_ERROR(0),
+ setsockoptReturns(0),connectReturns(0),connectErrno(0),
+ sendErrno(0),recvErrno(0)
+ {
+ mock_=this;
+ }
+ virtual ~Mock_socket(){mock_=0;}
+
+ int socketReturns;
+ virtual int callSocket(int domain, int type, int protocol){
+ return socketReturns;
+ }
+ int closeReturns;
+ virtual int callClose(int fd){
+ return closeReturns;
+ }
+ int getsocketoptReturns;
+ int optvalSO_ERROR;
+ virtual int callGet(int s,int level,int optname,void *optval,socklen_t *len){
+ if(level==SOL_SOCKET && optname==SO_ERROR){
+ setSO_ERROR(optval,*len);
+ }
+ return getsocketoptReturns;
+ }
+ virtual void setSO_ERROR(void *optval,socklen_t len){
+ memcpy(optval,&optvalSO_ERROR,len);
+ }
+
+ int setsockoptReturns;
+ virtual int callSet(int s,int level,int optname,const void *optval,socklen_t len){
+ return setsockoptReturns;
+ }
+ int connectReturns;
+ int connectErrno;
+ virtual int callConnect(int s,const struct sockaddr *addr,socklen_t len){
+ errno=connectErrno;
+ return connectReturns;
+ }
+
+ virtual void notifyBufferSent(const std::string& buffer){}
+
+ int sendErrno;
+ std::string sendBuffer;
+ virtual ssize_t callSend(int s,const void *buf,size_t len,int flags){
+ if(sendErrno!=0){
+ errno=sendErrno;
+ return -1;
+ }
+ // first call to send() is always the length of the buffer to follow
+ bool sendingLength=sendBuffer.size()==0;
+ // overwrite the length bytes
+ sendBuffer.assign((const char*)buf,len);
+ if(!sendingLength){
+ notifyBufferSent(sendBuffer);
+ sendBuffer.erase();
+ }
+ return len;
+ }
+
+ int recvErrno;
+ std::string recvReturnBuffer;
+ virtual ssize_t callRecv(int s,void *buf,size_t len,int flags){
+ if(recvErrno!=0){
+ errno=recvErrno;
+ return -1;
+ }
+ int k=std::min(len,recvReturnBuffer.length());
+ if(k==0)
+ return 0;
+ memcpy(buf,recvReturnBuffer.data(),k);
+ recvReturnBuffer.erase(0,k);
+ return k;
+ }
+ virtual bool hasMoreRecv() const{
+ return recvReturnBuffer.size()!=0;
+ }
+ static Mock_socket* mock_;
+};
+
+// *****************************************************************************
+// fcntl
+class Mock_fcntl: public Mock
+{
+public:
+ Mock_fcntl():callReturns(0),trapFD(-1){mock_=this;}
+ ~Mock_fcntl(){mock_=0;}
+
+ int callReturns;
+ int trapFD;
+ virtual int call(int fd, int cmd, void* arg){
+ if(trapFD==-1)
+ return LIBC_SYMBOLS.fcntl(fd,cmd,arg);
+ return callReturns;
+ }
+
+ static Mock_fcntl* mock_;
+};
+
+// *****************************************************************************
+// select
+class Mock_select: public Mock
+{
+public:
+ Mock_select(Mock_socket* s,int fd):sock(s),
+ callReturns(0),myFD(fd),timeout(50)
+ {
+ mock_=this;
+ }
+ ~Mock_select(){mock_=0;}
+
+ Mock_socket* sock;
+ int callReturns;
+ int myFD;
+ int timeout; //in millis
+ virtual int call(int nfds,fd_set *rfds,fd_set *wfds,fd_set *efds,struct timeval *tv){
+ bool isWritableRequested=(wfds && FD_ISSET(myFD,wfds));
+ if(rfds) FD_CLR(myFD,rfds);
+ if(wfds) FD_CLR(myFD,wfds);
+ // this timeout is only to prevent a tight loop
+ timeval myTimeout={0,0};
+ if(!isWritableRequested && !isFDReadable()){
+ myTimeout.tv_sec=timeout/1000;
+ myTimeout.tv_usec=(timeout%1000)*1000;
+ }
+ LIBC_SYMBOLS.select(nfds,rfds,wfds,efds,&myTimeout);
+ // myFD is always writable
+ if(isWritableRequested) FD_SET(myFD,wfds);
+ // myFD is only readable if the socket has anything to read
+ if(isFDReadable() && rfds) FD_SET(myFD,rfds);
+ return callReturns;
+ }
+
+ virtual bool isFDReadable() const {
+ return sock->hasMoreRecv();
+ }
+
+ static Mock_select* mock_;
+};
+
+// *****************************************************************************
+// poll
+// the last element of the pollfd array is expected to be test FD
+class Mock_poll: public Mock
+{
+public:
+ Mock_poll(Mock_socket* s,int fd):sock(s),
+ callReturns(1),myFD(fd),timeout(50)
+ {
+ mock_=this;
+ }
+ ~Mock_poll(){mock_=0;}
+
+ Mock_socket* sock;
+ int callReturns;
+ int myFD;
+ int timeout; //in millis
+ virtual int call(struct pollfd *fds, POLL_NFDS_TYPE nfds, int to) {
+ pollfd* myPoll=0;
+ if(fds[nfds-1].fd==myFD)
+ myPoll=&fds[nfds-1];
+ bool isWritableRequested=false;
+ if(myPoll!=0){
+ isWritableRequested=myPoll->events&POLLOUT;
+ nfds--;
+ }
+ LIBC_SYMBOLS.poll(fds,nfds,(!isWritableRequested&&!isFDReadable())?timeout:0);
+ if(myPoll!=0){
+ // myFD is always writable if requested
+ myPoll->revents=isWritableRequested?POLLOUT:0;
+ // myFD is only readable if the socket has anything to read
+ myPoll->revents|=isFDReadable()?POLLIN:0;
+ }
+ return callReturns;
+ }
+
+ virtual bool isFDReadable() const {
+ return sock->hasMoreRecv();
+ }
+
+ static Mock_poll* mock_;
+};
+
+// *****************************************************************************
+// gettimeofday
+class Mock_gettimeofday: public Mock
+{
+public:
+ Mock_gettimeofday(){
+ LIBC_SYMBOLS.gettimeofday(&tv,0);
+ mock_=this;
+ }
+ Mock_gettimeofday(const Mock_gettimeofday& other):tv(other.tv){}
+ Mock_gettimeofday(int32_t sec,int32_t usec){
+ tv.tv_sec=sec;
+ tv.tv_usec=usec;
+ }
+ ~Mock_gettimeofday(){mock_=0;}
+
+ timeval tv;
+ virtual int call(struct timeval *tp, GETTIMEOFDAY_ARG2_TYPE tzp){
+ *tp=tv;
+ return 0;
+ }
+ operator timeval() const{
+ return tv;
+ }
+ // advance secs
+ virtual void tick(int howmuch=1){tv.tv_sec+=howmuch;}
+ // advance milliseconds
+ // can move the clock forward as well as backward by providing a negative
+ // number
+ virtual void millitick(int howmuch=1){
+ int ms=tv.tv_usec/1000+howmuch;
+ tv.tv_sec+=ms/1000;
+ // going backward?
+ if(ms<0){
+ ms=1000-(-ms%1000); //wrap millis around
+ }
+ tv.tv_usec=(ms%1000)*1000;
+ }
+ virtual void tick(const timeval& howmuch){
+ // add milliseconds (discarding microsecond portion)
+ long ms=tv.tv_usec/1000+howmuch.tv_usec/1000;
+ tv.tv_sec+=howmuch.tv_sec+ms/1000;
+ tv.tv_usec=(ms%1000)*1000;
+ }
+ static Mock_gettimeofday* mock_;
+};
+
+// discard microseconds!
+inline bool operator==(const timeval& lhs, const timeval& rhs){
+ return rhs.tv_sec==lhs.tv_sec && rhs.tv_usec/1000==lhs.tv_usec/1000;
+}
+
+// simplistic implementation: no normalization, assume lhs >= rhs,
+// discarding microseconds
+inline timeval operator-(const timeval& lhs, const timeval& rhs){
+ timeval res;
+ res.tv_sec=lhs.tv_sec-rhs.tv_sec;
+ res.tv_usec=(lhs.tv_usec/1000-rhs.tv_usec/1000)*1000;
+ if(res.tv_usec<0){
+ res.tv_sec--;
+ res.tv_usec=1000000+res.tv_usec%1000000; // wrap the millis around
+ }
+ return res;
+}
+
+inline int32_t toMilliseconds(const timeval& tv){
+ return tv.tv_sec*1000+tv.tv_usec/1000;
+}
+
+#endif /*LIBCMOCKS_H_*/
diff --git a/src/c/tests/LibCSymTable.cc b/src/c/tests/LibCSymTable.cc
new file mode 100644
index 0000000..5378579
--- /dev/null
+++ b/src/c/tests/LibCSymTable.cc
@@ -0,0 +1,83 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 "LibCSymTable.h"
+
+#define LOAD_SYM(sym) \
+ sym=(sym##_sig)dlsym(handle,#sym); \
+ assert("Unable to load "#sym" from libc"&&sym)
+
+
+LibCSymTable& LibCSymTable::instance(){
+ static LibCSymTable tbl;
+ return tbl;
+}
+
+//******************************************************************************
+// preload original libc symbols
+LibCSymTable::LibCSymTable()
+{
+ void* handle=getHandle();
+ LOAD_SYM(gethostbyname);
+ LOAD_SYM(calloc);
+ LOAD_SYM(realloc);
+ LOAD_SYM(free);
+ LOAD_SYM(random);
+ LOAD_SYM(srandom);
+ LOAD_SYM(printf);
+ LOAD_SYM(socket);
+ LOAD_SYM(close);
+ LOAD_SYM(getsockopt);
+ LOAD_SYM(setsockopt);
+ LOAD_SYM(fcntl);
+ LOAD_SYM(connect);
+ LOAD_SYM(send);
+ LOAD_SYM(recv);
+ LOAD_SYM(select);
+ LOAD_SYM(poll);
+ LOAD_SYM(gettimeofday);
+#ifdef THREADED
+ LOAD_SYM(pthread_create);
+ LOAD_SYM(pthread_detach);
+ LOAD_SYM(pthread_cond_broadcast);
+ LOAD_SYM(pthread_cond_destroy);
+ LOAD_SYM(pthread_cond_init);
+ LOAD_SYM(pthread_cond_signal);
+ LOAD_SYM(pthread_cond_timedwait);
+ LOAD_SYM(pthread_cond_wait);
+ LOAD_SYM(pthread_join);
+ LOAD_SYM(pthread_mutex_destroy);
+ LOAD_SYM(pthread_mutex_init);
+ LOAD_SYM(pthread_mutex_lock);
+ LOAD_SYM(pthread_mutex_trylock);
+ LOAD_SYM(pthread_mutex_unlock);
+#endif
+}
+
+void* LibCSymTable::getHandle(){
+ static void* handle=0;
+ if(!handle){
+#ifdef __CYGWIN__
+ handle=dlopen("cygwin1.dll",RTLD_LAZY);
+ assert("Unable to dlopen global sym table"&&handle);
+#else
+ handle=RTLD_NEXT;
+#endif
+ }
+ return handle;
+}
diff --git a/src/c/tests/LibCSymTable.h b/src/c/tests/LibCSymTable.h
new file mode 100644
index 0000000..2f7e0c2
--- /dev/null
+++ b/src/c/tests/LibCSymTable.h
@@ -0,0 +1,107 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 LIBCSYMTABLE_H_
+#define LIBCSYMTABLE_H_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef THREADED
+#include
+#endif
+
+#include "config.h"
+
+// TODO: move all these macros to config.h (generated by autoconf)
+#ifdef __CYGWIN__
+#if (CYGWIN_VERSION_DLL_MAJOR < 1007)
+#define RANDOM_RET_TYPE int
+#else
+#define RANDOM_RET_TYPE long int
+#endif
+#define GETTIMEOFDAY_ARG2_TYPE void*
+#else
+#define RANDOM_RET_TYPE long int
+#define GETTIMEOFDAY_ARG2_TYPE struct timezone*
+#endif
+
+#define DECLARE_SYM(ret,sym,sig) \
+ typedef ret (*sym##_sig)sig; \
+ static sym##_sig preload_##sym () { \
+ static sym##_sig ptr=0;\
+ if(!ptr){ void* h=getHandle(); ptr=(sym##_sig)dlsym(h,#sym); } \
+ assert("Unable to load "#sym" from libc"&&ptr); \
+ return ptr; \
+ } \
+ sym##_sig sym
+
+#define LIBC_SYMBOLS LibCSymTable::instance()
+
+//******************************************************************************
+// preload original libc symbols
+struct LibCSymTable
+{
+ DECLARE_SYM(hostent*,gethostbyname,(const char*));
+ DECLARE_SYM(void*,calloc,(size_t, size_t));
+ DECLARE_SYM(void*,realloc,(void*, size_t));
+ DECLARE_SYM(void,free,(void*));
+ DECLARE_SYM(RANDOM_RET_TYPE,random,(void));
+ DECLARE_SYM(void,srandom,(unsigned long));
+ DECLARE_SYM(int,printf,(const char*, ...));
+ DECLARE_SYM(int,socket,(int,int,int));
+ DECLARE_SYM(int,close,(int));
+ DECLARE_SYM(int,getsockopt,(int,int,int,void*,socklen_t*));
+ DECLARE_SYM(int,setsockopt,(int,int,int,const void*,socklen_t));
+ DECLARE_SYM(int,fcntl,(int,int,...));
+ DECLARE_SYM(int,connect,(int,const struct sockaddr*,socklen_t));
+ DECLARE_SYM(ssize_t,send,(int,const void*,size_t,int));
+ DECLARE_SYM(ssize_t,recv,(int,const void*,size_t,int));
+ DECLARE_SYM(int,select,(int,fd_set*,fd_set*,fd_set*,struct timeval*));
+ DECLARE_SYM(int,poll,(struct pollfd*,POLL_NFDS_TYPE,int));
+ DECLARE_SYM(int,gettimeofday,(struct timeval*,GETTIMEOFDAY_ARG2_TYPE));
+#ifdef THREADED
+ DECLARE_SYM(int,pthread_create,(pthread_t *, const pthread_attr_t *,
+ void *(*)(void *), void *));
+ DECLARE_SYM(int,pthread_detach,(pthread_t));
+ DECLARE_SYM(int,pthread_cond_broadcast,(pthread_cond_t *));
+ DECLARE_SYM(int,pthread_cond_destroy,(pthread_cond_t *));
+ DECLARE_SYM(int,pthread_cond_init,(pthread_cond_t *, const pthread_condattr_t *));
+ DECLARE_SYM(int,pthread_cond_signal,(pthread_cond_t *));
+ DECLARE_SYM(int,pthread_cond_timedwait,(pthread_cond_t *,
+ pthread_mutex_t *, const struct timespec *));
+ DECLARE_SYM(int,pthread_cond_wait,(pthread_cond_t *, pthread_mutex_t *));
+ DECLARE_SYM(int,pthread_join,(pthread_t, void **));
+ DECLARE_SYM(int,pthread_mutex_destroy,(pthread_mutex_t *));
+ DECLARE_SYM(int,pthread_mutex_init,(pthread_mutex_t *, const pthread_mutexattr_t *));
+ DECLARE_SYM(int,pthread_mutex_lock,(pthread_mutex_t *));
+ DECLARE_SYM(int,pthread_mutex_trylock,(pthread_mutex_t *));
+ DECLARE_SYM(int,pthread_mutex_unlock,(pthread_mutex_t *));
+#endif
+ LibCSymTable();
+
+ static void* getHandle();
+ static LibCSymTable& instance();
+};
+
+#endif /*LIBCSYMTABLE_H_*/
diff --git a/src/c/tests/MocksBase.cc b/src/c/tests/MocksBase.cc
new file mode 100644
index 0000000..60b2c75
--- /dev/null
+++ b/src/c/tests/MocksBase.cc
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+#include
+
+#include "MocksBase.h"
+#include "LibCSymTable.h"
+
+// *****************************************************************************
+// Mock base
+void* Mock::operator new(std::size_t s){
+ void* p=malloc(s);
+ if(!p)
+ throw std::bad_alloc();
+ return p;
+}
+
+void Mock::operator delete(void* p){
+ LIBC_SYMBOLS.free(p);
+}
diff --git a/src/c/tests/MocksBase.h b/src/c/tests/MocksBase.h
new file mode 100644
index 0000000..5b54251
--- /dev/null
+++ b/src/c/tests/MocksBase.h
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 MOCKSBASE_H_
+#define MOCKSBASE_H_
+
+#include
+
+// *****************************************************************************
+// Mock base
+
+class Mock
+{
+public:
+ virtual ~Mock(){}
+
+ static void* operator new(std::size_t s);
+ static void operator delete(void* p);
+};
+
+#endif /*MOCKSBASE_H_*/
diff --git a/src/c/tests/PthreadMocks.cc b/src/c/tests/PthreadMocks.cc
new file mode 100644
index 0000000..490cebf
--- /dev/null
+++ b/src/c/tests/PthreadMocks.cc
@@ -0,0 +1,106 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 "PthreadMocks.h"
+
+MockPthreadsBase* MockPthreadsBase::mock_=0;
+
+#undef USING_DUMA
+
+#ifndef USING_DUMA
+int pthread_cond_broadcast (pthread_cond_t *c){
+ if(!MockPthreadsBase::mock_)
+ return LIBC_SYMBOLS.pthread_cond_broadcast(c);
+ return MockPthreadsBase::mock_->pthread_cond_broadcast(c);
+}
+int pthread_cond_destroy (pthread_cond_t *c){
+ if(!MockPthreadsBase::mock_)
+ return LIBC_SYMBOLS.pthread_cond_destroy(c);
+ return MockPthreadsBase::mock_->pthread_cond_destroy(c);
+}
+int pthread_cond_init (pthread_cond_t *c, const pthread_condattr_t *a){
+ if(!MockPthreadsBase::mock_)
+ return LIBC_SYMBOLS.pthread_cond_init(c,a);
+ return MockPthreadsBase::mock_->pthread_cond_init(c,a);
+}
+int pthread_cond_signal (pthread_cond_t *c){
+ if(!MockPthreadsBase::mock_)
+ return LIBC_SYMBOLS.pthread_cond_signal(c);
+ return MockPthreadsBase::mock_->pthread_cond_signal(c);
+}
+int pthread_cond_timedwait (pthread_cond_t *c,
+ pthread_mutex_t *m, const struct timespec *t){
+ if(!MockPthreadsBase::mock_)
+ return LIBC_SYMBOLS.pthread_cond_timedwait(c,m,t);
+ return MockPthreadsBase::mock_->pthread_cond_timedwait(c,m,t);
+}
+int pthread_cond_wait (pthread_cond_t *c, pthread_mutex_t *m){
+ if(!MockPthreadsBase::mock_)
+ return LIBC_SYMBOLS.pthread_cond_wait(c,m);
+ return MockPthreadsBase::mock_->pthread_cond_wait(c,m);
+}
+int pthread_create (pthread_t *t, const pthread_attr_t *a,
+ void *(*f)(void *), void *d){
+ if(!MockPthreadsBase::mock_)
+ return LIBC_SYMBOLS.pthread_create(t,a,f,d);
+ return MockPthreadsBase::mock_->pthread_create(t,a,f,d);
+}
+int pthread_detach(pthread_t t){
+ if(!MockPthreadsBase::mock_)
+ return LIBC_SYMBOLS.pthread_detach(t);
+ return MockPthreadsBase::mock_->pthread_detach(t);
+}
+int pthread_join (pthread_t t, void **r){
+ if(!MockPthreadsBase::mock_)
+ return LIBC_SYMBOLS.pthread_join(t,r);
+ return MockPthreadsBase::mock_->pthread_join(t,r);
+}
+int pthread_mutex_destroy (pthread_mutex_t *m){
+ if(!MockPthreadsBase::mock_)
+ return LIBC_SYMBOLS.pthread_mutex_destroy(m);
+ return MockPthreadsBase::mock_->pthread_mutex_destroy(m);
+}
+int pthread_mutex_init (pthread_mutex_t *m, const pthread_mutexattr_t *a){
+ if(!MockPthreadsBase::mock_)
+ return LIBC_SYMBOLS.pthread_mutex_init(m,a);
+ return MockPthreadsBase::mock_->pthread_mutex_init(m,a);
+}
+
+DECLARE_WRAPPER(int,pthread_mutex_lock,(pthread_mutex_t *m)){
+ if(!MockPthreadsBase::mock_)
+ return CALL_REAL(pthread_mutex_lock,(m));
+ return MockPthreadsBase::mock_->pthread_mutex_lock(m);
+}
+
+int pthread_mutex_trylock (pthread_mutex_t *m){
+ if(!MockPthreadsBase::mock_)
+ return LIBC_SYMBOLS.pthread_mutex_trylock(m);
+ return MockPthreadsBase::mock_->pthread_mutex_trylock(m);
+}
+
+DECLARE_WRAPPER(int,pthread_mutex_unlock,(pthread_mutex_t *m)){
+ if(!MockPthreadsBase::mock_)
+ return CALL_REAL(pthread_mutex_unlock,(m));
+ return MockPthreadsBase::mock_->pthread_mutex_unlock(m);
+}
+#endif
+
+CheckedPthread::ThreadMap CheckedPthread::tmap_;
+CheckedPthread::MutexMap CheckedPthread::mmap_;
+CheckedPthread::CVMap CheckedPthread::cvmap_;
+Mutex CheckedPthread::mx;
diff --git a/src/c/tests/PthreadMocks.h b/src/c/tests/PthreadMocks.h
new file mode 100644
index 0000000..8db8815
--- /dev/null
+++ b/src/c/tests/PthreadMocks.h
@@ -0,0 +1,449 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 PTHREADMOCKS_H_
+#define PTHREADMOCKS_H_
+
+#include
+#include
+#include
+
+#include "src/zk_adaptor.h"
+
+#include "Util.h"
+#include "MocksBase.h"
+#include "LibCSymTable.h"
+#include "ThreadingUtil.h"
+
+// an ABC for pthreads
+class MockPthreadsBase: public Mock
+{
+public:
+ MockPthreadsBase(){mock_=this;}
+ virtual ~MockPthreadsBase(){mock_=0;}
+
+ virtual int pthread_create(pthread_t * t, const pthread_attr_t *a,
+ void *(*f)(void *), void *d) =0;
+ virtual int pthread_join(pthread_t t, void ** r) =0;
+ virtual int pthread_detach(pthread_t t) =0;
+ virtual int pthread_cond_broadcast(pthread_cond_t *c) =0;
+ virtual int pthread_cond_destroy(pthread_cond_t *c) =0;
+ virtual int pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *a) =0;
+ virtual int pthread_cond_signal(pthread_cond_t *c) =0;
+ virtual int pthread_cond_timedwait(pthread_cond_t *c,
+ pthread_mutex_t *m, const struct timespec *t) =0;
+ virtual int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m) =0;
+ virtual int pthread_mutex_destroy(pthread_mutex_t *m) =0;
+ virtual int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a) =0;
+ virtual int pthread_mutex_lock(pthread_mutex_t *m) =0;
+ virtual int pthread_mutex_trylock(pthread_mutex_t *m) =0;
+ virtual int pthread_mutex_unlock(pthread_mutex_t *m) =0;
+
+ static MockPthreadsBase* mock_;
+};
+
+// all pthread functions simply return an error code
+// and increment their invocation counter. No actual threads are spawned.
+class MockPthreadsNull: public MockPthreadsBase
+{
+public:
+ MockPthreadsNull():
+ pthread_createReturns(0),pthread_createCounter(0),
+ pthread_joinReturns(0),pthread_joinCounter(0),pthread_joinResultReturn(0),
+ pthread_detachReturns(0),pthread_detachCounter(0),
+ pthread_cond_broadcastReturns(0),pthread_cond_broadcastCounter(0),
+ pthread_cond_destroyReturns(0),pthread_cond_destroyCounter(0),
+ pthread_cond_initReturns(0),pthread_cond_initCounter(0),
+ pthread_cond_signalReturns(0),pthread_cond_signalCounter(0),
+ pthread_cond_timedwaitReturns(0),pthread_cond_timedwaitCounter(0),
+ pthread_cond_waitReturns(0),pthread_cond_waitCounter(0),
+ pthread_mutex_destroyReturns(0),pthread_mutex_destroyCounter(0),
+ pthread_mutex_initReturns(0),pthread_mutex_initCounter(0),
+ pthread_mutex_lockReturns(0),pthread_mutex_lockCounter(0),
+ pthread_mutex_trylockReturns(0),pthread_mutex_trylockCounter(0),
+ pthread_mutex_unlockReturns(0),pthread_mutex_unlockCounter(0)
+ {
+ memset(threads,0,sizeof(threads));
+ }
+
+ short threads[512];
+
+ int pthread_createReturns;
+ int pthread_createCounter;
+ virtual int pthread_create(pthread_t * t, const pthread_attr_t *a,
+ void *(*f)(void *), void *d){
+ char* p=(char*)&threads[pthread_createCounter++];
+ p[0]='i'; // mark as created
+ *t=(pthread_t)p;
+ return pthread_createReturns;
+ }
+ int pthread_joinReturns;
+ int pthread_joinCounter;
+ void* pthread_joinResultReturn;
+ virtual int pthread_join(pthread_t t, void ** r){
+ pthread_joinCounter++;
+ if(r!=0)
+ *r=pthread_joinResultReturn;
+ char* p=(char*)t;
+ p[0]='x';p[1]+=1;
+ return pthread_joinReturns;
+ }
+ int pthread_detachReturns;
+ int pthread_detachCounter;
+ virtual int pthread_detach(pthread_t t){
+ pthread_detachCounter++;
+ char* p=(char*)t;
+ p[0]='x';p[1]+=1;
+ return pthread_detachReturns;
+ }
+
+ template
+ static bool isInitialized(const T& t){
+ return ((char*)t)[0]=='i';
+ }
+ template
+ static bool isDestroyed(const T& t){
+ return ((char*)t)[0]=='x';
+ }
+ template
+ static int getDestroyCounter(const T& t){
+ return ((char*)t)[1];
+ }
+ template
+ static int getInvalidAccessCounter(const T& t){
+ return ((char*)t)[2];
+ }
+ int pthread_cond_broadcastReturns;
+ int pthread_cond_broadcastCounter;
+ virtual int pthread_cond_broadcast(pthread_cond_t *c){
+ pthread_cond_broadcastCounter++;
+ if(isDestroyed(c))((char*)c)[2]++;
+ return pthread_cond_broadcastReturns;
+ }
+ int pthread_cond_destroyReturns;
+ int pthread_cond_destroyCounter;
+ virtual int pthread_cond_destroy(pthread_cond_t *c){
+ pthread_cond_destroyCounter++;
+ char* p=(char*)c;
+ p[0]='x';p[1]+=1;
+ return pthread_cond_destroyReturns;
+ }
+ int pthread_cond_initReturns;
+ int pthread_cond_initCounter;
+ virtual int pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *a){
+ pthread_cond_initCounter++;
+ char* p=(char*)c;
+ p[0]='i'; // mark as created
+ p[1]=0; // destruction counter
+ p[2]=0; // access after destruction counter
+ return pthread_cond_initReturns;
+ }
+ int pthread_cond_signalReturns;
+ int pthread_cond_signalCounter;
+ virtual int pthread_cond_signal(pthread_cond_t *c){
+ pthread_cond_signalCounter++;
+ if(isDestroyed(c))((char*)c)[2]++;
+ return pthread_cond_signalReturns;
+ }
+ int pthread_cond_timedwaitReturns;
+ int pthread_cond_timedwaitCounter;
+ virtual int pthread_cond_timedwait(pthread_cond_t *c,
+ pthread_mutex_t *m, const struct timespec *t){
+ pthread_cond_timedwaitCounter++;
+ if(isDestroyed(c))((char*)c)[2]++;
+ return pthread_cond_timedwaitReturns;
+ }
+ int pthread_cond_waitReturns;
+ int pthread_cond_waitCounter;
+ virtual int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m){
+ pthread_cond_waitCounter++;
+ if(isDestroyed(c))((char*)c)[2]++;
+ return pthread_cond_waitReturns;
+ }
+ int pthread_mutex_destroyReturns;
+ int pthread_mutex_destroyCounter;
+ virtual int pthread_mutex_destroy(pthread_mutex_t *m){
+ pthread_mutex_destroyCounter++;
+ char* p=(char*)m;
+ p[0]='x';p[1]+=1;
+ return pthread_mutex_destroyReturns;
+ }
+ int pthread_mutex_initReturns;
+ int pthread_mutex_initCounter;
+ virtual int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a){
+ pthread_mutex_initCounter++;
+ char* p=(char*)m;
+ p[0]='i'; // mark as created
+ p[1]=0; // destruction counter
+ p[2]=0; // access after destruction counter
+ return pthread_mutex_initReturns;
+ }
+ int pthread_mutex_lockReturns;
+ int pthread_mutex_lockCounter;
+ virtual int pthread_mutex_lock(pthread_mutex_t *m){
+ pthread_mutex_lockCounter++;
+ if(isDestroyed(m))((char*)m)[2]++;
+ return pthread_mutex_lockReturns;
+ }
+ int pthread_mutex_trylockReturns;
+ int pthread_mutex_trylockCounter;
+ virtual int pthread_mutex_trylock(pthread_mutex_t *m){
+ pthread_mutex_trylockCounter++;
+ if(isDestroyed(m))((char*)m)[2]++;
+ return pthread_mutex_trylockReturns;
+ }
+ int pthread_mutex_unlockReturns;
+ int pthread_mutex_unlockCounter;
+ virtual int pthread_mutex_unlock(pthread_mutex_t *m){
+ pthread_mutex_unlockCounter++;
+ if(isDestroyed(m))((char*)m)[2]++;
+ return pthread_mutex_unlockReturns;
+ }
+};
+
+// simulates the way zookeeper threads make use of api_prolog/epilog and
+//
+class MockPthreadZKNull: public MockPthreadsNull
+{
+ typedef std::map Map;
+ Map map_;
+public:
+ virtual int pthread_create(pthread_t * t, const pthread_attr_t *a,
+ void *(*f)(void *), void *d){
+ int ret=MockPthreadsNull::pthread_create(t,a,f,d);
+ zhandle_t* zh=(zhandle_t*)d;
+ adaptor_threads* ad=(adaptor_threads*)zh->adaptor_priv;
+ api_prolog(zh);
+ ad->threadsToWait--;
+ putValue(map_,*t,zh);
+ return ret;
+ }
+ virtual int pthread_join(pthread_t t, void ** r){
+ zhandle_t* zh=0;
+ if(getValue(map_,t,zh))
+ api_epilog(zh,0);
+ return MockPthreadsNull::pthread_join(t,r);
+ }
+};
+
+struct ThreadInfo{
+ typedef enum {RUNNING,TERMINATED} ThreadState;
+
+ ThreadInfo():
+ destructionCounter_(0),invalidAccessCounter_(0),state_(RUNNING)
+ {
+ }
+
+ ThreadInfo& incDestroyed() {
+ destructionCounter_++;
+ return *this;
+ }
+ ThreadInfo& incInvalidAccess(){
+ invalidAccessCounter_++;
+ return *this;
+ }
+ ThreadInfo& setTerminated(){
+ state_=TERMINATED;
+ return *this;
+ }
+ int destructionCounter_;
+ int invalidAccessCounter_;
+ ThreadState state_;
+};
+
+class CheckedPthread: public MockPthreadsBase
+{
+ // first => destruction counter
+ // second => invalid access counter
+ //typedef std::pair Entry;
+ typedef ThreadInfo Entry;
+ typedef std::map ThreadMap;
+ static ThreadMap tmap_;
+ static ThreadMap& getMap(const TypeOp::BareT&){return tmap_;}
+ typedef std::map MutexMap;
+ static MutexMap mmap_;
+ static MutexMap& getMap(const TypeOp::BareT&){return mmap_;}
+ typedef std::map CVMap;
+ static CVMap cvmap_;
+ static CVMap& getMap(const TypeOp::BareT&){return cvmap_;}
+
+ static Mutex mx;
+
+ template