####################### Public C API Reference ####################### .. default-domain:: c .. contents:: Notes on the C API ================== This API is mainly used to implement the C++ and the Python API. It is provided here so that you can find, if needed, quite detailed information on its functions. Depending on which programming language you need to use, it might be useful to start to read first with the :doc:`reference_python` or the :doc:`reference_cpp` parts. .. index:: pair: thread safety; C API Thread-Safety ============= The API functions can be classified into two categories: * Functions and constructors which set up connections and configure the client, like the :c:func:`ln_client()` constructor, or the :c:func:`ln_subscribe()` method. * Functions and constructors which perform communication. The first group of functions should only be used in a single thread. The second group of functions can be used from multiple threads and are thread-safe. .. index:: pair: C API; fork() calls fork() calls ============ The ``fork()`` system call is not supported by the LN library. This means that ``fork()`` should not be called when any LN client instance is active. Otherwise, strange things can happen. Memory Management ================= If functions such as :c:func:`ln_subscribe()` return pointers, these are always, unless noted otherwise, non-owning pointers which do not need to be freed by the caller of that function. When they to refer, for example, to ``ln_client`` instances, their life time ends when that instance is freed or destroyed, so they cannot be used any more. LN Client ========== LN client objects are needed for both :term:`publish/subscribe` communication and for :term:`remote procedure calls ` which are called "services". .. Note: normally, we would use the "c:struct" label here, but it is not supported by our old sphinx versions installed on OSL 1.3! .. c:type:: ln_client An LN client instance which holds information on the connection. Needs to be initialized using :func:`ln_init()`. .. Note: you need to omit the semicolon at the end of the prototype declarations, otherwise sphinx/reST cannot parse that!! .. c:function:: int ln_init(ln_client* clnt, const char* client_name, int argc, char** argv) :param clnt: is a pointer to an :c:struct: :param client_name: a default-value to use when there is no :envvar:`LN_PROGRAM_NAME` environment variable. The exact behavior is described in :ref:`guide/concepts/clients/client-names`. :param argc: number of arguments to the program, as supplied to ``main()``. :param argv: pointer to the argument list of the program, as supplied to ``main()``. :rtype: an integer indicating success or failure connect to ln_manager specified via command line option "--ln-manager HOST:PORT" use :envvar:`LN_MANAGER` env-var if there is no such option. will initialize ln_client at address clnt and return 0 on success. .. c:function:: int ln_init_to_manager(ln_client* clnt, const char* client_name, \ const char* ln_manager) Connect to specified ln_manager, only use the :envvar:`LN_MANAGER` environment variable if ln_manager is empty/NULL. Will initialize ln_client at address clnt and return 0 on success. .. c:function:: char* ln_get_error_message(ln_client clnt_) To display the dynamically generated error message on this client connection. The returned ``char*`` pointer is owned by the corresponding :c:type:`ln_client` object. It must not be freed by user code. It is only valid while that client object still exists, and must not be accessed after the client has been destroyed via :c:func:`ln_deinit()`. Ports and Topics ================ Ports link an initialized :term:`LN client` with a :term:`topic` and a :term:`message definition`. For sending data, the topic needs a :term:`publisher` port, and for receiving data, it needs a :term:`subscriber` port. .. c:type:: ln_inport Input port which results from subscribing to a topic. .. c:type:: ln_outport Output port which results from publishing to a topic .. c:function:: int ln_subscribe(ln_client clnt, const char* topic_name, \ const char* message_definition_name, ln_inport* port, double rate, \ int reliable_transport) Subscribe with a :term:`message definition` to a :term:`topic`. The rate is a maximum rate of messages per seconds. If more messages are sent than this rate, they will be discarded. Error Codes: NE_CLIENT_NOT_INITIALIZED : the LN client object was not initialized .. c:function:: int ln_subscribe_with_buffers(ln_client clnt, const char* topic_name, \ const char* message_definition_name, ln_inport* port, double rate, \ int reliable_transport, unsigned int buffers) :rtype: ln_outport Subscribe to a topic, and also specify the number of used buffers .. c:function:: int ln_unsubscribe(ln_inport* port) Unsubscribe from a topic. .. c:function:: int ln_publish(ln_client clnt, const char* topic_name, \ const char* message_definition_name, ln_outport* port) publish to a topic with a given message type Error Codes: NE_CLIENT_NOT_INITIALIZED : the LN client object was not initialized .. c:function:: int ln_publish_with_buffers(ln_client clnt, const char* topic_name, \ const char* message_definition_name, unsigned int buffers, ln_outport* port) publish to a topic with a given message type, specifying the number of used buffers .. c:function:: int ln_unpublish(ln_outport* port) Sending, Receiving and Handling Message Packets ================================================ Packets are pieces of message data that can be sent and received. Common Types ------------ .. c:type:: void* ln_packet A pointer to a transmitted data structure whose members can be accessed by name .. _reference/c/ln_read: Receiving Packets ----------------- .. c:function:: int ln_read(ln_inport port, void* ln_packet, unsigned int size, \ double* timestamp, int blocking) read from the inport and fill the ln_packet with data. If there is new data, it is copied atomically. Otherwise, the input buffer is not changed. :param port: the input port :param ln_packet: the packet data structure that is filled or whose content is transmitted :param size: the size of the packet :param timestamp: the timestamp of the package :param blocking: if true and no data is there, the method waits for new data. If false and no data is there, the method returns immediately without changing the input buffer. :rtype: Returns 0 if no packet was read, 1 if a packet was read, and a negative value for run-time-errors. :error return codes (with negative sign): :retval 0: if port does not have yet a publisher :retval LNE_INVALID_PORT_OBJECT: If used without a valid port object :retval LNE_NOT_YET_IMPLEMENTED: parameter combination not yet implemented, e.g. if trying a blocking read with a set request rate :retval LNE_PORT_UNBLOCKED: if port is not blocked (wrong configuration) :retval LNE_SHM_READ_ERROR: read from SHM failed (internal error) .. c:function:: int ln_read_timeout(ln_inport port, void* ln_packet, unsigned int packet_size, \ double* ts, double timeout) As for ``ln_read()``, data is either transferred completely, or not at all. :param port: the input port :param ln_packet: the packet data structure that is filled or whose content is transmitted :param size: the size of the packet :param ts: the timestamp of the package :param timeout: an optional time-out with a maximum waiting time in seconds timeout parameter: 0 - non-blocking >0 - blocking after seconds :rtype: return value: 0 - the timeout was hit 1 - packet! Sending Packets --------------- .. c:function:: int ln_write(ln_outport port, void* ln_packet, unsigned int size, double* timestamp) write a packet to an output port, attaching a time stamp to it (The time stamp is useful to discern otherwise identical packets which arrive in a sequence) The write is atomically and does not block. The timestamp is the time in seconds. It is used when rate-limiting messages. The return code is normally zero and is a negative value for any run-time errors, such as not being able to write to shared memory. :param port: port handle :param ln_packet: pointer to packet data :param size: size of packet :param timestamp: timestamp of message :Error codes: :retval LNE_INVALID_PORT_OBJECT: If used without a valid port object :retval LNE_PACKET_TOO_LARGE: packet was too large for configured message size LN Services =========== :term:`LN services ` implement a form of :term:`remote procedure call`. These are split in a :term:`service provider ` and a :term:`service client `. Common Types ------------ .. c:type:: ln_service An LN service client instance, which can be used by a service client to access functions provided by a service provider .. c:type:: ln_service_request Provider API ------------ The service provider implements the remote call, by registering a handler to a specific service call. .. c:function:: int ln_service_init(ln_client clnt, ln_service* svc, const char* service_name, \ const char* service_interface, const char* signature) Initialize a service for a client :param service_name: The name which identifies the service within the current distributed system. :param service_interface: The service :term:`interface` is a string containing the name of the :term:`service message definition `, which defines both call parameters and return parameters. :param signature: A string that is auto-generated by ln_generate, that mirrors the data element of a service request and response, and how to copy them. .. c:function:: int ln_service_deinit(ln_service* svc) Unregister and de-initialize a service, freeing the internal resources associated with it. .. c:type:: int (*ln_service_handler)(ln_client clnt, ln_service_request req, void* user_data) Type of user-provided handler function to handle service calls. The function will be called with the service request data and with a pointer to arbitrary user data, that can hold server-side state between calls. The user data pointer can be a pointer to a static struct or object instance. .. c:function:: int ln_service_provider_set_handler(ln_service svc, ln_service_handler handler, \ void* user_data) Set a handler function to an initialized service. This will cause the handler function to be called if a service request arrives :param svc: service handle :param handler: handler function of type :c:type:`ln_service_handler`, defined and provided by the user. .. c:function:: int ln_service_provider_register(ln_client clnt, ln_service svc) Register the service provider setup with the LN manager to provide a service. This function performs communication with the LN manager. Because network connections are not absolutely reliable, this operation can potentially fail, which will result in an exception. :param clnt: LN client :param svc: service handle .. c:function:: int ln_service_request_respond(ln_service_request req) Transfers the result of a service call back to the caller. Any error while transferring the response will cause a non-zero return value. When ``ln_service_request_respond()`` returns, the provider can be sure that the response has arrived at the caller, and that both processes have reached the corresponding point of their execution (in other words, the ``ln_service_request_respond()`` method provides synchronization between threads). :param req: service request handle .. c:function:: int ln_wait_and_handle_service_group_requests(ln_client clnt, \ const char* group_name, double timeout) Handle service requests for one :term:`service group` with the provided client in the current thread, executing requests of one :term:`service group` serially in the current thread. If the data accessed by the request handlers is not modified concurrently by additional threads, no thread synchronization is necessary. .. note:: Concurrent calls of ``ln_wait_and_handle_service_group_requests()`` for the same service group are serialized internally. This is a safety guard, not a recommended scheduling model. Prefer one dispatcher thread per service group, or use :c:func:`ln_handle_service_group_in_thread_pool()` when service handlers should run concurrently. :param clnt: LN client :param group_name: name of the :term:`service group` for which requests are handled. The parameter NULL/nullptr refers to the default group. :param timeout: The maximum time, in seconds, that the function will wait for requests before returning. This can be used to handle requests to different service groups in quick succession, by handling them sequentially, while not incurring risk that bugs occur because of incorrect locking. .. c:function:: int ln_handle_service_group_in_thread_pool(ln_client clnt, \ const char* group_name, const char* pool_name) Handle service requests for one :term:`service group` in a thread pool, potentially executing them in parallel. Write access to common resources needs to be secured by locks/:term:`mutexes` in the handler implementation. .. important:: This function is mutually exclusive with :c:func:`ln_wait_and_handle_service_group_requests()`, only one of both functions must be called. :param clnt: LN client :param group_name: name of the :term:`service group`. The parameter NULL refers to the default group. :param pool_name: name of the thread pool. If a pool with that name does not exist, it will be automatically created with a pool size of 1. .. c:function:: int ln_set_max_threads(ln_client clnt, const char* pool_name, \ unsigned int n_threads) Sets the maximum number of threads for a thread pool with the given name. Here, the number ``n_threads`` means: 1. Only one service request can be handled at one time, the requests for the corresponding service groups are handled sequentially. 2. Still, only one service request is handled at the same time, but the setup of the connection is done in an own thread. 3. Up to two service requests can be handled at the same time (and so on). .. c:function:: int ln_service_request_set_data(ln_service_request req, void* data, \ const char* signature) This function does two things: 1. It registers a pointer to a data buffer, which is used in the operation of the ``respond()`` function to retrieve the response data for a service call. 2. It copies the request paremeter data from the LN internal buffer to that data buffer. The buffer pointed to by data has to be managed by the user - it is neither automatically allocated, nor freed by LN. :param req: service request handle :param data: pointer to data buffer which serves to receive request data, and will provide reponse data when the call is responded to with ``respond()``. Client API ---------- The client can call the service function, which can be executed on another node. This service call is completely :term:`network-transparent `. .. c:function:: int ln_service_call(ln_service svc, void* data) Call the service from the client message definitions =================== The client can query information about message definitions known to the LNM. it can also register new message definitions at runtime (useful when md is generated dynamically). .. c:function:: int ln_get_message_definition_v21(ln_client clnt, const char* message_definition_name, char** message_definition, unsigned int* message_size, char** hash, char** flat_message_definition) get information about a named message definition. :param clnt: LN client :param message_definition_name: the name of the used message definition to get information for. :param message_definition: output-parameter, might be ``NULL``. needs to be freed by caller. will point to a python-text-representation of the message structure. :param message_size: output-parameter, might be ``NULL``. size in bytes of a packet according to this message definition. :param hash: output-parameter, might be ``NULL``. needs to be freed by caller. will point to a hex-string-representation of a hash which is computed for this message structure. :param flat_message_definition: output-parameter, might be ``NULL``. needs to be freed by caller. will point to an internal text-representation of the message structure. example for md ``uint32_t`` from ``tests/local_publish_subscribe``: * ``message_definition``:: {'defines': {'st': [['uint32_t', 'data', 1], ['float', 'f1', 1], ['char', 'f1_text', 8], ['float', 'f8', 8]]}, 'fields': [['uint32_t', 'data', 1], ['uint64_t', 'bigdata', 1], ['float', 'f1', 1], ['char', 'f1_text', 8], ['float', 'f8', 8], ['double', 'd1', 1], ['double', 'd8', 8], ['char', 'long_text', 1024], ['double', 'd80', 80], ['st', 'st1', 1], ['st', 'st3', 3]], 'resp_fields': []} * ``message_size``:: 1984 * ``hash``:: 275d98fd43f228c6e8447070d1271675306eab30e16bd6e21f5c936e7b303b73 * ``flat_message_definition``:: 7 4 1 data|-2 8 1 bigdata|1 4 1 f1|2 1 8 f1_text|1 4 8 f8|0 8 1 d1|0 8 8 d8|2 1 1024 long_text|0 8 80 d80|7 4 1 st1_data|1 4 1 st1_f1|2 1 8 st1_f1_text|1 4 8 st1_f8|7 4 1 st3_data[0]|1 4 1 st3_f1[0]|2 1 8 st3_f1_text[0]|1 4 8 st3_f8[0]|7 4 1 st3_data[1]|1 4 1 st3_f1[1]|2 1 8 st3_f1_text[1]|1 4 8 st3_f8[1]|7 4 1 st3_data[2]|1 4 1 st3_f1[2]|2 1 8 st3_f1_text[2]|1 4 8 st3_f8[2]| .. c:function:: int ln_get_message_definition_for_topic_v17(ln_client clnt, const char* topic_name, char** message_definition_name, char** message_definition, unsigned int* message_size, char** hash) get message definition that is used by named topic. :param clnt: LN client :param topic_name: name of the :term:`topic`. The topic-name must be in use by some other LN-Client (subscriber or publisher). :param message_definition_name: output-parameter. needs to be freed by caller. will point to the name of the used message definition. :param message_definition: output-parameter. needs to be freed by caller. will point to a python-text-representation of the message structure. :param message_size: output-parameter. size in bytes of a packet according to this message definition. :param hash: output-parameter, might be ``NULL``. needs to be freed by caller. will point to a hex-string-representation of a hash which is computed for this message structure. .. c:function:: int ln_describe_message_definition(ln_client clnt, const char* message_definition_name, char** message_definition) get a human readable description for a named message definition. :param clnt: LN client :param message_definition_name: the name of the used message definition to get information for. :param message_definition: output-parameter. needs to be freed by caller. will point to a human-readable text-representation of the message structure including any used non-primitives/subtypes. example for md ``uint32_t`` from ``tests/local_publish_subscribe``:: name: uint32_t file: /home/schm_fl/.../tests/local_publish_subscribe/mds/uint32_t type: packet fields: uint32_t data uint64_t bigdata float f1 char f1_text[8] float f8[8] double d1 double d8[8] char long_text[1024] double d80[80] st st1 st st3[3] non primitives: st: subtype --- name: subtype file: /home/schm_fl/.../tests/local_publish_subscribe/mds/subtype type: packet fields: uint32_t data float f1 char f1_text[8] float f8[8] .. c:function:: int ln_put_message_definition(ln_client clnt, const char* message_definition_name, const char* message_definition, char** real_message_definition_name) register a new message definition with LNM. dynamically generated message definitions will always have a name-prefix of ``gen/`` to clearly distinguish them from well-known/published message definitions. :param clnt: LN client :param message_definition_name: requested name of the new message definition. :param message_definition: source of to be created message definition. this is the same what you would write into a md-file. (do not put the ``gen/`` prefix here) :param real_message_definition: output-parameter. needs to be freed by caller. will point to the name of the new message-definition with the LNM-decided ``gen/``-prefix. .. _reference/c/API/eror_codes: C API Error Codes and their Meaning =================================== .. index:: pair: Reference (C API); error codes single: LNE_CHECK_ERRNO single: LNE_NO_MEM single: LNE_NO_MANAGER_ADDRESS single: LNE_INVALID_MANAGER_ADDRESS single: LNE_INVALID_HOSTNAME single: LNE_UNKNOWN_HOSTNAME single: LNE_HOST_WRONG_NET single: LNE_GOT_INVALID_RESPONSE single: LNE_REGISTER_DENIED single: LNE_CLIENT_NOT_INITIALIZED single: LNE_REQUEST_FAILED single: LNE_FIELD_NOT_IN_HEADER single: LNE_STRING_LENGTH_SPEC_MISSING single: LNE_PORT_NOT_FOUND single: LNE_INVALID_PORT_OBJECT single: LNE_SHM_IS_TOO_SMALL single: LNE_SHM_IS_TOO_BIG single: LNE_SHM_DOES_NOT_EXIT single: LNE_PACKET_TOO_LARGE single: LNE_SHM_READ_ERROR single: LNE_LOST_CONNECTION single: LNE_REGISTER_FAILED single: LNE_SVC_IS_PROVIDER single: LNE_SVC_IS_CLIENT single: LNE_SVC_RECV_RESP single: LNE_SVC_RECV_RESP_MEM single: LNE_SVC_ALREADY_REGISTERED single: LNE_SVC_NOT_REGISTERED single: LNE_SVC_RECV_REQ single: LNE_SVC_RECV_REQ_MEM single: LNE_SVC_REQ_SET_DATA_WRONG single: LNE_SVC_HANDLER_NO_RESP single: LNE_INVALID_REQ_FIELD_FORMAT single: LNE_USER_DEFINED_ERROR single: LNE_SVC_NO_HANDLER single: LNE_SVC_HANDLER_EXC single: LNE_SVC_REQ_RUNNING single: LNE_SVC_REQ_NOT_RUNNING single: LNE_SVC_REQ_ABORTED single: LNE_NO_PROVIDER_FOUND single: LNE_START_PROVIDER_FAILED single: LNE_ERROR_GETTING_SHM_FD single: LNE_SHM_NAME_TOO_LONG single: LNE_NOT_YET_IMPLEMENTED single: LNE_NO_LOGGER_TOPICS single: LNE_INTERNAL_ERROR single: LNE_TOPIC_ID_OUT_OF_RANGE single: LNE_LOG_FILE_TOO_NEW single: LNE_WRONG_BYTE_ORDER single: LNE_INVALID_SHM_MAGIC single: LNE_SHM_VERSION_TOO_NEW single: LNE_WRONG_SHM_SIZE single: LNE_INVALID_SIGNATURE single: LNE_INVALID_PARAMETER single: LNE_SVC_RESP_LATER single: LNE_SVC_GRP_UNKNOWN single: LNE_THREADP_INIT single: LNE_NOT_ON_THIS_OS single: LNE_PORT_UNBLOCKED single: LNE_UNKNOWN_PARAMETER single: LNE_SVC_HANDLER_MULTI_RESP single: LNE_MANAGER_TOO_OLD single: LNE_INVALID_DSA_SIGNATURE ================================== ================================================================== Error Code Meaning ================================== ================================================================== LNE_CHECK_ERRNO check errno for error description LNE_NO_MEM out of memory LNE_NO_MANAGER_ADDRESS no --ln-manager HOST:PORT command line argument and no :envvar:`LN_MANAGER` environment var LNE_INVALID_MANAGER_ADDRESS specified ln_manager address is not in format HOST_OR_IP:PORT_NUMBER LNE_INVALID_HOSTNAME invalid hostname specified - has to be a hostname or an ip LNE_UNKNOWN_HOSTNAME could not resolve hostname LNE_HOST_WRONG_NET host resolved to wrong network family - should be IPv4 - AF_INET LNE_GOT_INVALID_RESPONSE client library received an invalid formatted response from ln_manager LNE_REGISTER_DENIED ln_manager denied register-request LNE_CLIENT_NOT_INITIALIZED provided client instance is not initalized! LNE_REQUEST_FAILED the request to ln_manager failed / was not successfull - see ln_get_error_message() LNE_FIELD_NOT_IN_HEADER the requested field name was not found in this request object LNE_STRING_LENGTH_SPEC_MISSING string length specification is missing in string format! LNE_PORT_NOT_FOUND the specified port was not found! LNE_INVALID_PORT_OBJECT the specified object is not or a wrong port object! LNE_SHM_IS_TOO_SMALL the requested shared memory region is too small! LNE_SHM_IS_TOO_BIG the requested shared memory region is too big! LNE_SHM_DOES_NOT_EXIT the requested shared memory region does not exist! LNE_PACKET_TOO_LARGE the provided packet is too large to write to this port! LNE_SHM_READ_ERROR error reading from shared memory source! LNE_LOST_CONNECTION lost connection to ln_manager LNE_REGISTER_FAILED could not register client to manager! is a ln_daemon running on this host? LNE_SVC_IS_PROVIDER this is a service provider not a service client! LNE_SVC_IS_CLIENT this is a service client not a service provider! LNE_SVC_RECV_RESP error receiving service response LNE_SVC_RECV_RESP_MEM error receiving service response: can not allocate sufficiently sized buffer LNE_SVC_ALREADY_REGISTERED this service already has an opened socket - is it already registered LNE_SVC_NOT_REGISTERED this service is not yet registered - it has no opened socket! LNE_SVC_RECV_REQ error receiving service request LNE_SVC_RECV_REQ_MEM error receiving service request: can not allocate sufficiently sized buffer LNE_SVC_REQ_SET_DATA_WRONG wrong signature passed to ln_service_request_set_data() for this service! LNE_SVC_HANDLER_NO_RESP programming error in service provider handler: handler returned success(==0) but did not call ln_service_request_respond()! LNE_INVALID_REQ_FIELD_FORMAT init_request failed because of an invalid format character! use one of r O,d,f! LNE_USER_DEFINED_ERROR user-defined error number LNE_SVC_NO_HANDLER can't register service without service-handler! use ln_service_provider_set_handler()! LNE_SVC_HANDLER_EXC exception was thrown in service provider handler! LNE_SVC_REQ_RUNNING there is already a service request/call running for this service! LNE_SVC_REQ_NOT_RUNNING there is not async service request running! LNE_SVC_REQ_ABORTED this async service request was aborted! LNE_NO_PROVIDER_FOUND there is no known provider for this service/topic! LNE_START_PROVIDER_FAILED there was an error starting a provider for this service/topic! LNE_ERROR_GETTING_SHM_FD there was an error trying to get a fd for this shm name LNE_SHM_NAME_TOO_LONG the shared memory name is too long LNE_NOT_YET_IMPLEMENTED the requested feature is not yet implemented LNE_NO_LOGGER_TOPICS there are no topics defined for this logger LNE_INTERNAL_ERROR internal error, should not happen! LNE_TOPIC_ID_OUT_OF_RANGE supplied logging_data topic_id is out of range! LNE_LOG_FILE_TOO_NEW this logfile uses an unknown/too new fileformat! LNE_WRONG_BYTE_ORDER data has wrong byte order. usually this means its different than the host-byte-order LNE_INVALID_SHM_MAGIC invalid shm magic - recompile creator of this shm! LNE_SHM_VERSION_TOO_NEW shm version too new! recompile this client! LNE_WRONG_SHM_SIZE shm reports too small / invalid segment size! LNE_INVALID_SIGNATURE passed signature is not a valid event signature! LNE_INVALID_PARAMETER user-provided parameter is invalid! LNE_SVC_RESP_LATER this service request will be answered later... LNE_SVC_GRP_UNKNOWN this service group name is not known! LNE_THREADP_INIT could not get thread pool! LNE_NOT_ON_THIS_OS feature not avaliable on this os! LNE_PORT_UNBLOCKED another thread unblocked this blocking read LNE_UNKNOWN_PARAMETER unknown parameter specified LNE_SVC_HANDLER_MULTI_RESP programming error in service provider handler: handler tried to call ln_service_request_respond() more than once!! LNE_MANAGER_TOO_OLD the connected ln_manager is too old LNE_INVALID_DSA_SIGNATURE the dss1-dsa signature is not valid for this message & public ================================== ==================================================================