* 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_*/
================================================
FILE: src/c/include/zookeeper_log.h
================================================
/**
* 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_*/
================================================
FILE: src/c/include/zookeeper_version.h
================================================
/**
* 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_ */
================================================
FILE: src/c/src/cli.c
================================================
/**
* 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;
}
================================================
FILE: src/c/src/hashtable/LICENSE.txt
================================================
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.
================================================
FILE: src/c/src/hashtable/hashtable.c
================================================
/* 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.
*/
================================================
FILE: src/c/src/hashtable/hashtable.h
================================================
/* 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.
*/
================================================
FILE: src/c/src/hashtable/hashtable_itr.c
================================================
/* 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.
*/
================================================
FILE: src/c/src/hashtable/hashtable_itr.h
================================================
/* 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.
*/
================================================
FILE: src/c/src/hashtable/hashtable_private.h
================================================
/* 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.
*/
================================================
FILE: src/c/src/load_gen.c
================================================
/**
* 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;
}
}
================================================
FILE: src/c/src/recordio.c
================================================
/**
* 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;
}
================================================
FILE: src/c/src/st_adaptor.c
================================================
/**
* 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;
}
================================================
FILE: src/c/src/winport.c
================================================
/**
* 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
================================================
FILE: src/c/src/winport.h
================================================
/**
* 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_
================================================
FILE: src/c/src/zk_adaptor.h
================================================
/**
* 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_*/
================================================
FILE: src/c/src/zk_hashtable.c
================================================
/**
* 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));
}
}
}
================================================
FILE: src/c/src/zk_hashtable.h
================================================
/**
* 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_*/
================================================
FILE: src/c/src/zk_log.c
================================================
/**
* 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;
}
================================================
FILE: src/c/src/zookeeper.c
================================================
/**
* 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;
}
================================================
FILE: src/c/tests/CollectionUtil.h
================================================
/**
* 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_
================================================
FILE: src/c/tests/CppAssertHelper.h
================================================
/**
* 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_*/
================================================
FILE: src/c/tests/LibCMocks.cc
================================================
/**
* 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;
================================================
FILE: src/c/tests/LibCMocks.h
================================================
/**
* 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_*/
================================================
FILE: src/c/tests/LibCSymTable.cc
================================================
/**
* 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;
}
================================================
FILE: src/c/tests/LibCSymTable.h
================================================
/**
* 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_*/
================================================
FILE: src/c/tests/MocksBase.cc
================================================
/**
* 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);
}
================================================
FILE: src/c/tests/MocksBase.h
================================================
/**
* 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_*/
================================================
FILE: src/c/tests/PthreadMocks.cc
================================================
/**
* 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;
================================================
FILE: src/c/tests/PthreadMocks.h
================================================
/**
* 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