This is an automated email from the git hooks/post-receive script. x2go pushed a commit to branch master in repository libx2goclient. commit b3fa7908b5331e00ef0453f8248fcb121b683b82 Author: Mihai Moldovan <ionic@ionic.de> Date: Sat Feb 27 11:21:39 2021 +0100 src/x2goclient-network-ssh.c: document full class. --- src/x2goclient-network-ssh.c | 711 +++++++++++++++++++++++++++++++++++++++++++ src/x2goclient-network.c | 6 + 2 files changed, 717 insertions(+) diff --git a/src/x2goclient-network-ssh.c b/src/x2goclient-network-ssh.c index 024e728..b057124 100644 --- a/src/x2goclient-network-ssh.c +++ b/src/x2goclient-network-ssh.c @@ -52,6 +52,22 @@ #include "x2goclient-openssh-bugs.h" +/** + * SECTION:X2GoClientNetworkOptionsSSH + * @short_description: Class encapsulating OpenSSH options + * @see_also: #X2GoClientNetworkOptions + * @stability: Unstable + * @include: libx2goclient/x2goclient-network-ssh.h + * + * #X2GoClientNetworkOptions is a class used to encapsulate OpenSSH options. + * + * Currently, there are no common options that could be used generically for + * all dependent classes, so it's essentially fully empty (yet providing the + * general skeleton). + * + * An example of a more abstract implementation is + * #X2GoClientNetworkSSHOptions. + */ struct _X2GoClientNetworkOptionsSSH { X2GoClientNetworkOptions parent_instance; @@ -81,6 +97,19 @@ static void x2goclient_network_options_ssh_class_init (X2GoClientNetworkOptionsS static void x2goclient_network_options_ssh_init (X2GoClientNetworkOptionsSSH * const self) { } +/** + * x2goclient_network_options_ssh_to_array: + * @self: (not optional): an #X2GoClientNetworkOptionsSSH. + * + * Converts the internal state to an #GPtrArray of SSH client options that can + * be passed by prefixing "-o" to each entry when calling the SSH client. + * + * Returns: (transfer full) (element-type utf8): a pointer to the converted + * result as a #GPtrArray. %NULL + * on error. + * + * Since: 0.0.5 + */ GPtrArray* x2goclient_network_options_ssh_to_array (X2GoClientNetworkOptionsSSH * const self) { GPtrArray *ret = NULL; @@ -95,8 +124,24 @@ GPtrArray* x2goclient_network_options_ssh_to_array (X2GoClientNetworkOptionsSSH /* + * sockaddr_ho: + * @sho_family: address family entry, typed #sa_family_t. Should always be set + * to #AF_HOST for objects of this type. + * @sho_port: higher level port the socket is supposed to be bound to, typed + * #in_port_t. + * @sho_addr: fixed-size address buffer as a C-style, null-terminated string. + * * Custom sockaddr structure used to represent socket endpoints based on a FQDN * or ssh_config-specific host alias. + * + * Unlike other implementations, we explicitly require @sho_addr to be null- + * byte terminated. That also means that the total length is one character + * less than defined here. + * + * This type should be private to our implementation and not be used in outer + * code. + * + * Since: 0.0.5 */ typedef struct sockaddr_ho_ { sa_family_t sho_family; @@ -104,9 +149,53 @@ typedef struct sockaddr_ho_ { char sho_addr[256]; } sockaddr_ho; +/* + * AF_HOST: + * + * Address family identifier for our custom FQDN/ssh_config-specific host + * alias sockaddr type. + * + * Determined by fair dice roll. Should be high enough above any other defined + * system value. + * + * Setting this correctly is pretty important so that the address family is + * not inadvertently misinterpreted as a different, defined address family. We + * could use something like (AF_MAX + 1) here, but that would assume that + * AF_MAX does not reach UINT16_MAX (beware of wraps) and that the library is + * rebuilt after each network API update (on Linux systems, that mostly means + * kernel updates, but on other operating systems, other components might + * influence this.) + * + * Since: 0.0.5 + */ #define AF_HOST 10417 +/** + * SECTION:X2GoClientNetworkSSH + * @short_description: Class encapsulating an OpenSSH (client) network + * connection + * @see_also: #X2GoClientNetwork + * @stability: Unstable + * @include: libx2goclient/x2goclient-network.h + * + * #X2GoClientNetwork is a class implementing network connections via the + * OpenSSH client. + * + * In addition to the base functions defined in #X2GoClientNetwork, it opens + * up a master connection, from which additional data connections can be + * spawned. The master connection is constantly monitored and, in case of + * disconnects or errors, all dependent connections cleaned up. + * + * An example of a more abstract implementation is #X2GoClientNetwork. +*/ + +/** + * X2GoClientNetworkSSH: + * + * #X2GoClientNetworkSSH is an opaque data structure and can only be accessed + * using the following functions. + */ struct _X2GoClientNetworkSSH { X2GoClientNetwork parent_instance; @@ -126,6 +215,16 @@ struct _X2GoClientNetworkSSH { G_DEFINE_TYPE (X2GoClientNetworkSSH, x2goclient_network_ssh, X2GOCLIENT_TYPE_NETWORK); +/* + * x2goclient_network_ssh_check_timeout_data: + * @self: (not nullable): pointer to the #X2GoClientNetworkSSH instance. + * @gerr: (nullable): pointer to a #GError location, set to %NULL if not + * interested in precise errors. + * + * Data structure used to pass common data to the timeout checking thread. + * + * Since: 0.0.5 + */ struct x2goclient_network_ssh_check_timeout_data { X2GoClientNetworkSSH *self; GError **gerr; @@ -179,12 +278,34 @@ static void x2goclient_network_ssh_class_init (X2GoClientNetworkSSHClass * const object_class->set_property = &x2goclient_network_ssh_set_property; object_class->get_property = &x2goclient_network_ssh_get_property; + /** + * X2GoClientNetworkSSH:openssh-version: + * + * The parsed OpenSSH client version number, put into a custom structure. + * + * It is read-only. + * + * See also x2goclient_network_ssh_fetch_openssh_version(). + * + * Since: 0.0.5 + */ net_ssh_obj_properties[X2GO_NET_SSH_PROP_OPENSSH_VERSION] = g_param_spec_boxed ("openssh-version", _("Parsed OpenSSH version number"), _("The OpenSSH client version number, parsed in a custom " "structure."), X2GOCLIENT_TYPE_OPENSSH_VERSION, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); + /** + * X2GoClientNetworkSSH:openssh-bugs: + * + * Contains the quirk bits for known OpenSSH client bugs. + * + * It is read-only. + * + * See also x2goclient_openssh_bugs_update(). + * + * Since: 0.0.5 + */ net_ssh_obj_properties[X2GO_NET_SSH_PROP_OPENSSH_BUGS] = g_param_spec_boxed ("openssh-bugs", _("Bug quirks structure for OpenSSH Client"), _("A structure containing quirks bits for known OpenSSH Client " "bugs."), @@ -316,6 +437,37 @@ static void x2goclient_network_ssh_get_property (GObject * const object, const g } } +/* + * x2goclient_network_ssh_parse_sockspec_unix_socket: + * @sockspec: (in) (not optional): socket specification as a pointer to a + * #GString. + * + * Takes a socket specification and tries to parse it as UNIX socket path. + * + * Both abstract and non-abstract (i.e., "normal") UNIX sockets are supported. + * + * Note that abstract UNIX sockets start with a null byte and can contain + * other null bytes that are part of the socket address, but not handled in + * a special way. Since abstract UNIX sockets don't have a corresponding file + * system path, no check is done to ensure that the socket actually exists. + * Since abstract UNIX sockets are difficult to pass in shell contexts, this + * functionality was not extensively tested and might be buggy. + * + * For pathname-based, "normal" UNIX sockets, the existence and its type is + * checked for. A non-existent path, or one that doesn't point to a UNIX- + * socket-type file will be treated as an error. + * + * Unnamed UNIX sockets, i.e., stream sockets that have been not passed to + * bind(2) or which have been created via socketpair(2), are currently + * <emphasis role='strong'>not supported</emphasis>. A special syntax will + * have to be created to support those types of UNIX sockets. + * + * Returns: (transfer full): a pointer to the parsed result as a + * #GSocketAddress. %NULL if the function believes + * that the passed @socketspec is not a UNIX socket. + * + * Since: 0.0.5 + */ static GSocketAddress* x2goclient_network_ssh_parse_sockspec_unix_socket (const GString * const sockspec) { GSocketAddress *ret = NULL; @@ -359,6 +511,26 @@ static GSocketAddress* x2goclient_network_ssh_parse_sockspec_unix_socket (const return (ret); } +/* + * x2goclient_network_ssh_parse_sockspec_port: + * @portspec: (in) (not optional): port specification as a pointer to a + * #GString. + * @gerr: (out) (nullable): a return location for a #GError, pass %NULL if not + * interested. + * + * Use this function to parse a port specification into a port value. + * + * A valid port specification <emphasis role='strong'>must</emphasis> start + * with a colon character (<literal>:</literal>). + * + * A parsing error will be mapped to the default value, 0. It is up to the + * application to decide what this default value actually maps to. + * + * Returns: (transfer full): the #guint16 value corresponding to the parsed + * port. On parsing errors, returns 0. + * + * Since: 0.0.5 + */ static guint16 x2goclient_network_ssh_parse_sockspec_port (const GString * const portspec, GError ** const gerr) { guint16 ret = 0; @@ -407,6 +579,23 @@ static guint16 x2goclient_network_ssh_parse_sockspec_port (const GString * const return (ret); } +/* + * x2goclient_network_ssh_sanitize_sockspec: + * @sockspec: (in) (not optional): socket specification as a pointer to a + * #GString. + * + * "Sanitizes" a socket specification. + * + * This currently means stripping whitespace from the socket specification, if + * the input looks like a string. + * + * Returns: (transfer full): a pointer to a #GString containing the sanitized + * result if successful, %NULL on error. On success, + * the returned data will always be a copy of the + * input data. + * + * Since: 0.0.5 + */ static GString* x2goclient_network_ssh_sanitize_sockspec (const GString * const sockspec) { GString *ret = NULL; @@ -429,6 +618,54 @@ static GString* x2goclient_network_ssh_sanitize_sockspec (const GString * const return (ret); } +/* + * x2goclient_network_ssh_parse_sockspec_ip: + * @sockspec: (in) (not optional): socket specification as a pointer to a + * #GString. + * @want_v6: (in): %TRUE is IPv6-based parsing is requested, %FALSE for the + * IPv4-based parsing. + * @gerr: (out) (nullable): a return location for a #GError, pass %NULL if not + * interested. + * + * Tries to parse a socket specification as an IPv4 or IPv6 address. + * + * There is no automatic detection or fallback mechanism. The address family + * (either IPv4 or IPv6) must be explicitly set via the @want_v6 parameter. If + * @want_v6 is set to %TRUE, the given address will be parsed as an IPv6 + * address, otherwise as an IPv4 address. + * + * The socket specification for both address types can optionally include a + * port specifier. Port specifiers must be separated from the address with a + * colon character (<literal>:</literal>) and are parsed via + * x2goclient_network_ssh_parse_sockspec_port() after parsing the address. + * + * IPv4 addresses can be provided in any IPv4 dotted decimal notation. + * + * IPv6 address criteria are more complicated: + * - If a port specification is provided, the address + * <emphasis role='strong'>must</emphasis> be wrapped in square brackets + * (<literal>[]</literal>). + * - An optional scope ID is supported and can be provided by appending a + * percent character (<literal>%</literal>) and the actual scope ID to the + * address. No validation is done one scope IDs. Network interface names + * are often not constraint in allowed characters or length. Note, that, + * while this function might support parsing scope IDs, the underlying + * glib implementation might not and either refuse to create such an + * #GSocketAddress or remove scope IDs. + * - The actual validation should follow the rules established in + * [RFC 2373](https://tools.ietf.org/html/rfc2373) (and, possibly, later + * versions). + * + * Only basic syntax-based "validation" is done. No connection is attempted + * and even private or reserved addresses, according to IANA, are supported + * and accepted. + * + * Returns: (transfer full): a pointer to a #GSocketAddress containing the + * parsed result if successful, %NULL on error, + * potentially with @gerr populated if provided. + * + * Since: 0.0.5 + */ static GSocketAddress* x2goclient_network_ssh_parse_sockspec_ip (const GString * const sockspec, const gboolean want_v6, GError ** const gerr) { GSocketAddress *ret = NULL; @@ -593,6 +830,42 @@ static GSocketAddress* x2goclient_network_ssh_parse_sockspec_ip (const GString * return (ret); } +/* + * x2goclient_network_ssh_parse_sockspec_alias: + * @sockspec: (in) (not optional): socket specification as a pointer to a + * #GString. + * @gerr: (out) (nullable): a return location for a #GError, pass %NULL if not + * interested. + * + * Tries to parse a socket specification as a FQDN or ssh_config-based + * aliases. + * + * The socket specification can optionally include a port specifier. Port + * specifiers must be separated from the address with a colon character + * (<literal>:</literal>) and are parsed via + * x2goclient_network_ssh_parse_sockspec_port() after parsing the address. + * + * No actual validation is currently carried out. Currently, almost any + * characters, with the exception of null characters, are allowed. This is + * mostly a consequence of ssh_config-based aliases, which are not explicitly + * constraint. This behavior is unlikely to change in future versions, but if + * you aim for maximal compatibility, make sure to keep your input aligned + * with [RFC 1123](https://tools.ietf.org/html/rfc1123) (and, possibly, later + * editions). + * + * The socket specification @sockspec experiences whitespace-removal on its + * front and back end before parsing. + * + * The address length is limited by the size of the #sockaddr_ho.sho_addr + * field, including the mandatory terminating null byte. Socket specifications + * with addresses longer than that will be regarded as erroneous. + * + * Returns: (transfer full): a pointer to a #GSocketAddress containing the + * parsed result if successful, %NULL on error, + * potentially with @gerr populated if provided. + * + * Since: 0.0.5 + */ static GSocketAddress* x2goclient_network_ssh_parse_sockspec_alias (const GString * const sockspec, GError ** const gerr) { GSocketAddress *ret = NULL; @@ -676,6 +949,44 @@ static GSocketAddress* x2goclient_network_ssh_parse_sockspec_alias (const GStrin return (ret); } +/* + * x2goclient_network_ssh_parse_sockspec: + * @parent: (in) (not optional): pointer to the #X2GoClientNetwork parent + * instance. + * @sockspec: (in) (not optional): socket specification as a pointer to a + * #GString. + * + * Tries to parse a socket specification. + * + * This function is actually, more or less, just a wrapper that calls other + * functions to parse the input socket specification, in the following order: + * - parse as UNIX socket via + * x2goclient_network_ssh_parse_sockspec_unix_socket() + * - if that failed, parse as IPv6 address via + * x2goclient_network_ssh_parse_sockspec_ip() with its + * <code>want_v6</code> parameter set to %TRUE + * - if that failed, parse as IPv4 address via + * x2goclient_network_ssh_parse_sockspec_ip() with its + * <code>want_v4</code> parameter set to %FALSE + * - if that failed, parse as FQDN or ssh_config-based alias via + * x2goclient_network_ssh_parse_sockspec_alias(). + * + * Please consult the listed function's documentation for valid input + * references and their behavior. + * + * There currently is no way to modify the outlined fallback behavior, e.g., + * to skip specific parsing steps or request a specific one. + * + * The general idea is that socket specifications are unique enough that only + * one parsing function can match them (or, more specifically, all but the + * last one, which will essentially accept any input as long as it is not too + * long). + * + * Returns: (transfer full): a pointer to a #GSocketAddress containing the + * parsed result if successful, %NULL on error. + * + * Since: 0.0.5 + */ static GSocketAddress* x2goclient_network_ssh_parse_sockspec (X2GoClientNetwork * const parent, const GString * const sockspec) { GSocketAddress *ret = NULL; @@ -737,6 +1048,31 @@ static GSocketAddress* x2goclient_network_ssh_parse_sockspec (X2GoClientNetwork return ret; } +/* + * x2goclient_network_ssh_kill_subprocesses: + * @self: (in) (not optional): pointer to the #X2GoClientNetworkSSH instance. + * + * Terminates all subprocesses associated with the master connection. + * + * This also entails terminating the master connection itself. + * + * <note> + * <title> + * Current implementation notice + * </title> + * <para> + * Right now, no additional processes but the master connection itself are + * terminated. This function will need to be extended to match the + * intention and documentation, but, as of right now, no additional + * processes are being created, so there is nothing to clean up. + * </para> + * </note> + * + * Returns: (transfer full): a #gboolean value indicating success (%TRUE) or + * failure (%FALSE). + * + * Since: 0.0.5 + */ static gboolean x2goclient_network_ssh_kill_subprocesses (X2GoClientNetworkSSH * const self) { gboolean ret = FALSE; @@ -791,6 +1127,67 @@ static gboolean x2goclient_network_ssh_kill_subprocesses (X2GoClientNetworkSSH * return (ret); } +/** + * x2goclient_network_ssh_connect: + * @self: (in) (not optional): pointer to the #X2GoClientNetworkSSH instance. + * @gerr: (out) (nullable): a return location for a #GError, pass %NULL if not + * interested. + * + * Wraps the SSH connect function. + * + * This function can be called on #X2GoClientNetworkSSH objects and redirects + * to the actual connection function, x2goclient_network_ssh_parent_connect(). + * + * x2goclient_network_ssh_parent_connect() opens an SSH connection to the + * remote location and registers the corresponding master connection + * internally. + * + * Additionally, if connecting was successful, it starts a new check thread, + * which periodically checks if this master connection is still alive and + * tears it down on connection losses, also updating the + * #X2GoClientNetwork:connected property. + * + * The connection parameters are looked up from the + * #X2GoClientNetwork:socket, #X2GoClientNetworkSSH:options and + * #X2GoClientNetwork:session-path properties. + * + * Currently, the control socket path is hardcoded to + * <filename><varname>session-path</varname>/ssh/control</filename>. + * + * Each connection attempt will also re-fetch the OpenSSH client version and + * re-apply quirks as necessary, (hopefully) making it resilient to system + * upgrades. + * + * This function is idempotent. + * + * Calling this function should be functionally equivalent to calling the + * parent's connect function: + * + * <informalexample> + * <programlisting language='C'> + * // Assume that this variable is pointing to a fully setup + * // #X2GoClientNetworkSSH instance. + * X2GoClientNetworkSSH *net_ssh = magic_ssh_object; + * + * // This part should be equivalent to the later one. + * gboolean conn_ret = x2goclient_network_ssh_connect (net_ssh, NULL); + * if (conn_ret) { + * x2goclient_network_ssh_disconnect (net_ssh, NULL); + * } + * + * // To this one. + * conn_ret = x2goclient_network_connect ((X2GoClientNetwork*) (net_ssh), + * NULL); + * if (conn_ret) { + * x2goclient_network_disconnect ((X2GoClientNetwork*) (net_ssh), NULL); + * } + * </programlisting> + * </informalexample> + * + * Returns: a #gboolean value indicating success (%TRUE) or failure (%FALSE). + * + * Since: 0.0.5 + */ gboolean x2goclient_network_ssh_connect (X2GoClientNetworkSSH * const self, GError ** const gerr) { gboolean ret = FALSE; @@ -812,6 +1209,37 @@ gboolean x2goclient_network_ssh_connect (X2GoClientNetworkSSH * const self, GErr return (ret); } +/* + * x2goclient_network_ssh_parent_connect: + * @parent: (in) (not optional): pointer to the #X2GoClientNetwork parent + * instance. + * @gerr: (out) (nullable): a return location for a #GError, pass %NULL if not + * interested. + * + * Opens an SSH connection to the remote location and registers this master + * connection internally. + * + * For documentation of its internal behavior, refer to the public function + * x2goclient_network_ssh_connect(). + * + * This function is <emphasis role='strong'>not</emphasis> idempotent. + * + * This function should not be called by user code. It's meant to be the + * private connect function of this class, also showcased by the fact that it + * is not idempotent. + * + * User code should call the public functions x2goclient_network_ssh_connect() + * on #X2GoClientNetworkSSH objects, or x2goclient_network_connect() on + * #X2GoClientNetwork objects. The latter function redirects to/automatically + * calls the more specific connection function of the derived class, which, + * for #X2GoClientNetworkSSH objects, should be functionally equivalent to + * calling x2goclient_network_ssh_connect() directly. + * + * Returns: (transfer full): a #gboolean value indicating success (%TRUE) or + * failure (%FALSE). + * + * Since: 0.0.5 + */ static gboolean x2goclient_network_ssh_parent_connect (X2GoClientNetwork * const parent, GError ** const gerr) { gboolean ret = FALSE; @@ -964,6 +1392,35 @@ static gboolean x2goclient_network_ssh_parent_connect (X2GoClientNetwork * const return (ret); } +/** + * x2goclient_network_ssh_disconnect: + * @self: (in) (not optional): pointer to the #X2GoClientNetworkSSH instance. + * @gerr: (out) (nullable): a return location for a #GError, pass %NULL if not + * interested. + * + * Wraps the SSH disconnect function. + * + * This function can be called on #X2GoClientNetworkSSH objects and redirects + * to the actual disconnection function, + * x2goclient_network_ssh_parent_disconnect(). + * + * x2goclient_network_ssh_parent_disconnect() terminates the registered master + * connection to the remote location (if it exists), clears the connected + * state and also terminates all processes that have been spawned as part of + * this connection. + * + * Additionally, if disconnecting was successful, the check thread is + * terminated. + * + * For more information, see also x2goclient_network_ssh_connect(), which + * documents the connection parameters and example usage. + * + * This function is idempotent. + * + * Returns: a #gboolean value indicating success (%TRUE) or failure (%FALSE). + * + * Since: 0.0.5 + */ gboolean x2goclient_network_ssh_disconnect (X2GoClientNetworkSSH * const self, GError ** const gerr) { gboolean ret = FALSE; @@ -985,6 +1442,38 @@ gboolean x2goclient_network_ssh_disconnect (X2GoClientNetworkSSH * const self, G return (ret); } +/* + * x2goclient_network_ssh_parent_disconnect: + * @parent: (in) (not optional): pointer to the #X2GoClientNetwork parent + * instance. + * @gerr: (out) (nullable): a return location for a #GError, pass %NULL if not + * interested. + * + * Terminates the master SSH connection to the remote location and all + * associated processes. + * + * For documentation of its internal behavior, refer to the public + * x2goclient_network_ssh_disconnect() function. + * + * This function is <emphasis role='strong'>not</emphasis> idempotent. + * + * This function should not be called by user code. It's meant to be the + * private disconnect function of this class, also showcased by the fact that + * it is not idempotent. + * + * User code should call the public functions + * x2goclient_network_ssh_disconnect() on #X2GoClientNetworkSSH objects, or + * x2goclient_network_disconnect() on #X2GoClientNetwork objects. The latter + * function redirects to/automatically calls the more specific disconnection + * function of the derived class, which, for #X2GoClientNetworkSSH objects, + * should be functionally equivalent to calling + * x2goclient_network_ssh_disconnect() directly. + * + * Returns: (transfer full): a #gboolean value indicating success (%TRUE) or + * failure (%FALSE). + * + * Since: 0.0.5 + */ static gboolean x2goclient_network_ssh_parent_disconnect (X2GoClientNetwork * const parent, GError ** const gerr) { gboolean ret = FALSE; @@ -1047,6 +1536,20 @@ static gboolean x2goclient_network_ssh_parent_disconnect (X2GoClientNetwork * co return (ret); } +/* + * x2goclient_network_ssh_fetch_openssh_version: + * @self: (in) (not optional): pointer to the #X2GoClientNetworkSSH instance. + * @gerr: (out) (nullable): a return location for a #GError, pass %NULL if not + * interested. + * + * Fetches the OpenSSH client version and updates the read-only + * #X2GoClientNetworkSSH:openssh-version property. + * + * Returns: (transfer full): a #gboolean value indicating success (%TRUE) or + * failure (%FALSE). + * + * Since: 0.0.5 + */ static gboolean x2goclient_network_ssh_fetch_openssh_version (X2GoClientNetworkSSH * const self, GError ** const gerr) { gboolean ret = FALSE; @@ -1075,6 +1578,29 @@ static gboolean x2goclient_network_ssh_fetch_openssh_version (X2GoClientNetworkS return (ret); } +/* + * x2goclient_network_ssh_log_std_str: + * @str: (in) (optional): string to print. + * @str_size: (in): the total size of @str. + * @select_stderr: (in): indicates whether the string was printed to stderr + * (%TRUE) or stdout (%FALSE). + * + * Prints the whole string passed via @str to the debug log level, indicating + * whether the string originally came from stderr or stdout. + * + * @str is allowed to be %NULL iff its size @str_size is zero. Other + * combinations are invalid and will lead to the function terminating without + * processing any data. + * + * This function exists because the #printf formatter specifier + * <code>%.*s</code>, which limits the size of the string to be printed, is + * limited to the size that can be passed via an #int. To work around that, + * it splits up the string into %INT_MAX increments (if needed) and prints + * each additional part prepended with a <literal>(continuation)</literal> + * message. + * + * Since: 0.0.5 + */ static void x2goclient_network_ssh_log_std_str (const gchar * const str, const gsize str_size, const _Bool select_stderr) { /* * For a size bigger than zero, the bytes object must be non-NULL, @@ -1126,6 +1652,27 @@ static void x2goclient_network_ssh_log_std_str (const gchar * const str, const g } } +/* + * x2goclient_network_ssh_gptrarray_to_string: + * @arr: (in) (not optional) (array) + * (element-type (type utf8)): pointer to the #GPtrArray to convert to a + * string. + * @prelude: (in) (nullable): initial string to prepend to the resulting + * output string. + * @ret_str: (out) (not optional) (transfer full): output location of the + * converted string. + * + * Converts a #GPtrArray passed as @arr to a flat string, optionally prefixing + * it with @prelude and returns it via @ret_str. + * + * Elements in @arr must be strings. %NULL elements are explicitly allowed and + * will be mapped to an internal representation: <literal>(NULL)</literal>. + * + * Returns: (transfer full): a #gboolean value indicating success (%TRUE) or + * failure (%FALSE). + * + * Since: 0.0.5 + */ static gboolean x2goclient_network_ssh_gptrarray_to_string (GPtrArray * const arr, const gchar * const prelude, gchar ** const ret_str) { #define SIZE_LOW_WRAP(buffer_size, elem_size) ((G_MAXSIZE - (buffer_size)) < ((elem_size))) gboolean ret = FALSE; @@ -1313,6 +1860,27 @@ static void x2goclient_network_ssh_gptrarray_print_debug (GPtrArray * const arr, } } +/* + * x2goclient_network_ssh_sshcmd_add_host_port: + * @self: (in) (not optional): pointer to the #X2GoClientNetworkSSH instance. + * @ssh_cmd: (in) (not optional): a #GPtrArray to be modified. + * @gerr: (out) (nullable): a return location for a #GError, pass %NULL if not + * interested. + * + * Adds a host and port specification to the OpenSSH client command passed in + * as @ssh_cmd. + * + * These parameters are determined from the #X2GoNetworkSSH:socket property. + * + * Some socket types, like UNIX sockets, do not have semantics of a port. + * Likewise, a port may have not been explicitly provided, indicated by the + * port number 0 internally. In such cases, no port option is added. + * + * Returns: (transfer full): a #gboolean value indicating success (%TRUE) or + * failure (%FALSE). + * + * Since: 0.0.5 + */ static gboolean x2goclient_network_ssh_sshcmd_add_host_port (X2GoClientNetworkSSH * const self, GPtrArray * const ssh_cmd, GError ** const gerr) { gboolean ret = FALSE; @@ -1409,6 +1977,30 @@ static gboolean x2goclient_network_ssh_sshcmd_add_host_port (X2GoClientNetworkSS return (ret); } +/* + * x2goclient_network_ssh_start_sshcmd: + * @self: (in) (not optional): pointer to the #X2GoClientNetworkSSH instance. + * @ssh_cmd: (in) (not optional): a #GPtrArray to be used as argv. + * @gerr: (out) (nullable): a return location for a #GError, pass %NULL if not + * interested. + * @master: (in): a boolean value indicating, whether the process to spawn is + * the master connection (%TRUE) or a control connection + * (%FALSE). + * + * Starts a new SSH process. Use @master to select between a master or control + * connection. + * + * This function blocks until the command finished execution. + * + * After process execution, the accumulated data on stdout and stderr are + * queried (as buffered by glib) and subsequently printed to the debug log + * level. + * + * Returns: (transfer full): a #gboolean value indicating success (%TRUE) or + * failure (%FALSE). + * + * Since: 0.0.5 + */ static gboolean x2goclient_network_ssh_start_sshcmd (X2GoClientNetworkSSH * const self, const GPtrArray * const ssh_cmd, GError ** const gerr, const gboolean master) { gboolean ret = FALSE; @@ -1536,11 +2128,48 @@ static gboolean x2goclient_network_ssh_start_sshcmd (X2GoClientNetworkSSH * cons return (ret); } +/* + * x2goclient_network_ssh_check_timeout_data_free: + * @data: (in) (nullable): pointer to an + * #x2goclient_network_ssh_check_timeout_data + * instance. + * + * Frees elements in the data structure used by timeout check threads and + * afterwards the instance itself. + * + * While a %NULL input is supported, avoid that, since it will essentially + * degrade into a no-op with additional overhead. + * + * Since: 0.0.5 + */ static void x2goclient_network_ssh_check_timeout_data_free (struct x2goclient_network_ssh_check_timeout_data * const data) { /* No need to clear any data within the structure. */ g_free (data); } +/* + * x2goclient_network_ssh_check_timeout_thread_main: + * @user_data: (in) (not optional): pointer to the #X2GoClientNetworkSSH + * instance (actually, something like + * "self"). + * + * Implements the check thread's main function. + * + * This means setting up the main context (as used by glib), executing the + * main loop (as set up and registered in the #X2GoClientNetworkSSH instance + * previously) and eventually tears down the main loop and context data. + * + * Importantly, this function will execute in the actual check thread. + * + * Also, this function does not encapsulate any checking functionality. The + * actual checking is done in a timeout callback, but this one relies on the + * glib main loop started here. + * + * Returns: (transfer full): a #gpointer to... something. Currently always + * %NULL. + * + * Since: 0.0.5 + */ static gpointer x2goclient_network_ssh_check_timeout_thread_main (const gpointer user_data) { gpointer ret = NULL; @@ -1586,6 +2215,21 @@ static gpointer x2goclient_network_ssh_check_timeout_thread_main (const gpointer return (ret); } +/* + * x2goclient_network_ssh_start_check_timeout: + * @self: (in) (not optional): pointer to the #X2GoClientNetworkSSH instance. + * @gerr: (out) (nullable): a return location for a #GError, pass %NULL if not + * interested. + * + * Set ups and starts the check thread and executes the actual check timeout. + * + * This function will execute in the main thread. + * + * Returns: (transfer full): a #gboolean value indicating success (%TRUE) or + * failure (%FALSE). + * + * Since: 0.0.5 + */ static gboolean x2goclient_network_ssh_start_check_timeout (X2GoClientNetworkSSH * const self, GError ** const gerr) { gboolean ret = FALSE; @@ -1610,6 +2254,18 @@ static gboolean x2goclient_network_ssh_start_check_timeout (X2GoClientNetworkSSH return (ret); } +/* + * x2goclient_network_ssh_start_check_timeout_invoke: + * @self: (in) (not optional): pointer to the #X2GoClientNetworkSSH instance. + * @gerr: (out) (nullable): a return location for a #GError, pass %NULL if not + * interested. + * + * Invokes the check timeout in the check thread context. + * + * This function will execute in the main thread. + * + * Since: 0.0.5 + */ static void x2goclient_network_ssh_start_check_timeout_invoke (X2GoClientNetworkSSH * const self, GError ** const gerr) { g_return_if_fail (X2GOCLIENT_IS_NETWORK_SSH (self)); g_return_if_fail (((NULL == gerr) || (NULL == *gerr))); @@ -1623,6 +2279,21 @@ static void x2goclient_network_ssh_start_check_timeout_invoke (X2GoClientNetwork data, (GDestroyNotify) (&x2goclient_network_ssh_check_timeout_data_free)); } +/* + * x2goclient_network_ssh_start_check_timeout_unwrap: + * @user_data: (in) (not optional): pointer to an + * #x2goclient_network_ssh_check_timeout_data + * instance. + * + * Unwraps the provided check thread data and starts the actual check timeout. + * + * This function will execute in the check thread. + * + * Returns: (transfer full): a #gboolean value indicating success (%TRUE) or + * failure (%FALSE). + * + * Since: 0.0.5 + */ static gboolean x2goclient_network_ssh_start_check_timeout_unwrap (const gpointer user_data) { gboolean ret = G_SOURCE_CONTINUE; @@ -1638,6 +2309,21 @@ static gboolean x2goclient_network_ssh_start_check_timeout_unwrap (const gpointe return (ret); } +/* + * x2goclient_network_ssh_start_check_timeout_real: + * @self: (in) (not optional): pointer to the #X2GoClientNetworkSSH instance. + * @gerr: (out) (nullable): a return location for a #GError, pass %NULL if not + * interested. + * + * Unwraps the provided check thread data and starts the actual check timeout. + * + * This function will execute in the check thread. + * + * Returns: (transfer full): a #gboolean value indicating success (%TRUE) or + * failure (%FALSE). + * + * Since: 0.0.5 + */ static gboolean x2goclient_network_ssh_start_check_timeout_real (X2GoClientNetworkSSH * const self, GError ** const gerr) { gboolean ret = FALSE; @@ -1677,6 +2363,31 @@ static gboolean x2goclient_network_ssh_start_check_timeout_real (X2GoClientNetwo return (ret); } +/* + * x2goclient_network_ssh_start_check_timeout: + * @data: (in) (not optional): pointer to the #X2GoClientNetworkSSH instance + * (actually, something like "self"). + * + * Checks if the master connection is still alive. + * + * The actual checking is done through spawning an OpenSSH client process with + * the control path and the <constant>check</constant> command set. + * + * The return value of the OpenSSH client process will be used as an indicator + * for failure or success. + * + * Importantly, if the OpenSSH client process fails, the timeout source is + * deleted, the glib main loop terminated and the active master connection + * flag #X2GoClientNetworkSSH:active_master_conn is set to %FALSE. This means + * that the check thread will terminate. + * + * This function will execute in the check thread. + * + * Returns: (transfer full): a #gboolean value indicating success (%TRUE) or + * failure (%FALSE). + * + * Since: 0.0.5 + */ static gboolean x2goclient_network_ssh_check_timeout (const gpointer data) { gboolean ret = FALSE; diff --git a/src/x2goclient-network.c b/src/x2goclient-network.c index e944d0c..3580429 100644 --- a/src/x2goclient-network.c +++ b/src/x2goclient-network.c @@ -48,6 +48,8 @@ * * An example of a more concrete implementation is * #X2GoClientNetworkOptionsSSH. + * + * Since: 0.0.5 */ /* Not sure if we need this, so comment out for now. */ @@ -79,6 +81,8 @@ static void x2goclient_network_options_init (X2GoClientNetworkOptions * const se * implement, rely upon and obey. * * An example of a more concrete implementation is #X2GoClientNetworkSSH. + * + * Since: 0.0.5 */ /** @@ -86,6 +90,8 @@ static void x2goclient_network_options_init (X2GoClientNetworkOptions * const se * * #X2GoClientNetwork is an opaque data structure and can only be accessed * using the following functions. + * + * Since: 0.0.5 */ typedef struct X2GoClientNetworkPrivate_ { -- Alioth's /home/x2go-admin/maintenancescripts/git/hooks/post-receive-email on /srv/git/code.x2go.org/libx2goclient.git