summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2020-07-03 10:41:46 +0200
committerThomas Haller <thaller@redhat.com>2020-07-03 10:41:46 +0200
commit66e2d8c38a26e3b797fe2cbea961ea942042e13c (patch)
treef4f22a703d1d34db6468094f4ef63d8906df5604
parent8209095ee1c462c69d5203cdef11c61bfb0aa546 (diff)
parent9702f79db6ffce2a3319d0a710fe9498380d17e0 (diff)
downloadNetworkManager-66e2d8c38a26e3b797fe2cbea961ea942042e13c.tar.gz
cloud-setup: merge branch 'th/cloud-setup-various'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/553
-rw-r--r--clients/cloud-setup/nm-cloud-setup-utils.c32
-rw-r--r--clients/cloud-setup/nm-http-client.c67
-rw-r--r--clients/cloud-setup/nmcs-provider-ec2.c87
-rw-r--r--clients/cloud-setup/nmcs-provider-gcp.c77
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.c64
-rw-r--r--shared/nm-glib-aux/nm-str-buf.h17
-rw-r--r--shared/nm-glib-aux/tests/test-shared-general.c104
7 files changed, 286 insertions, 162 deletions
diff --git a/clients/cloud-setup/nm-cloud-setup-utils.c b/clients/cloud-setup/nm-cloud-setup-utils.c
index a32003cb35..39f3b100a1 100644
--- a/clients/cloud-setup/nm-cloud-setup-utils.c
+++ b/clients/cloud-setup/nm-cloud-setup-utils.c
@@ -6,6 +6,7 @@
#include "nm-glib-aux/nm-time-utils.h"
#include "nm-glib-aux/nm-logging-base.h"
+#include "nm-glib-aux/nm-str-buf.h"
/*****************************************************************************/
@@ -257,7 +258,6 @@ _poll_task_data_free (gpointer data)
static void
_poll_return (PollTaskData *poll_task_data,
- gboolean success,
GError *error_take)
{
nm_clear_g_source_inst (&poll_task_data->source_next_poll);
@@ -270,7 +270,7 @@ _poll_return (PollTaskData *poll_task_data,
if (error_take)
g_task_return_error (poll_task_data->task, g_steal_pointer (&error_take));
else
- g_task_return_boolean (poll_task_data->task, success);
+ g_task_return_boolean (poll_task_data->task, TRUE);
g_object_unref (poll_task_data->task);
}
@@ -301,7 +301,7 @@ _poll_done_cb (GObject *source,
if ( error
|| is_finished) {
- _poll_return (poll_task_data, TRUE, g_steal_pointer (&error));
+ _poll_return (poll_task_data, g_steal_pointer (&error));
return;
}
@@ -345,8 +345,8 @@ _poll_timeout_cb (gpointer user_data)
{
PollTaskData *poll_task_data = user_data;
- _poll_return (poll_task_data, FALSE, nm_utils_error_new (NM_UTILS_ERROR_UNKNOWN,
- "timeout expired"));
+ _poll_return (poll_task_data, nm_utils_error_new (NM_UTILS_ERROR_UNKNOWN,
+ "timeout expired"));
return G_SOURCE_CONTINUE;
}
@@ -356,17 +356,16 @@ _poll_cancelled_cb (GObject *object, gpointer user_data)
PollTaskData *poll_task_data = user_data;
GError *error = NULL;
- _LOGD (">> poll cancelled");
nm_clear_g_signal_handler (g_task_get_cancellable (poll_task_data->task),
&poll_task_data->cancellable_id);
nm_utils_error_set_cancelled (&error, FALSE, NULL);
- _poll_return (poll_task_data, FALSE, error);
+ _poll_return (poll_task_data, error);
}
/**
* nmcs_utils_poll:
* @poll_timeout_ms: if >= 0, then this is the overall timeout for how long we poll.
- * When this timeout expires, the request completes with failure (but no error set).
+ * When this timeout expires, the request completes with failure (and error set).
* @ratelimit_timeout_ms: if > 0, we ratelimit the starts from one prope_start_fcn
* call to the next.
* @sleep_timeout_ms: if > 0, then we wait after a probe finished this timeout
@@ -459,7 +458,8 @@ nmcs_utils_poll (int poll_timeout_ms,
* %FALSE will be returned.
* If the probe returned a failure, this returns %FALSE and the error
* provided by @probe_finish_fcn.
- * If the request times out, this returns %FALSE without error set.
+ * If the request times out, this returns %FALSE with error set.
+ * Error is always set if (and only if) the function returns %FALSE.
*/
gboolean
nmcs_utils_poll_finish (GAsyncResult *result,
@@ -565,15 +565,13 @@ nmcs_utils_uri_build_concat_v (const char *base,
const char **components,
gsize n_components)
{
- GString *uri;
+ NMStrBuf strbuf = NM_STR_BUF_INIT (NM_UTILS_GET_NEXT_REALLOC_SIZE_104, FALSE);
nm_assert (base);
nm_assert (base[0]);
nm_assert (!NM_STR_HAS_SUFFIX (base, "/"));
- uri = g_string_sized_new (100);
-
- g_string_append (uri, base);
+ nm_str_buf_append (&strbuf, base);
if ( n_components > 0
&& components[0]
@@ -583,18 +581,18 @@ nmcs_utils_uri_build_concat_v (const char *base,
*
* We only do that for the first component. */
} else
- g_string_append_c (uri, '/');
+ nm_str_buf_append_c (&strbuf, '/');
while (n_components > 0) {
if (!components[0]) {
- /* we allow NULL, to indicate nothing to append*/
+ /* we allow NULL, to indicate nothing to append */
} else
- g_string_append (uri, components[0]);
+ nm_str_buf_append (&strbuf, components[0]);
components++;
n_components--;
}
- return g_string_free (uri, FALSE);
+ return nm_str_buf_finalize (&strbuf, NULL);
}
/*****************************************************************************/
diff --git a/clients/cloud-setup/nm-http-client.c b/clients/cloud-setup/nm-http-client.c
index 946ed8ce93..294259f261 100644
--- a/clients/cloud-setup/nm-http-client.c
+++ b/clients/cloud-setup/nm-http-client.c
@@ -7,6 +7,7 @@
#include <curl/curl.h>
#include "nm-cloud-setup-utils.h"
+#include "nm-glib-aux/nm-str-buf.h"
#define NM_CURL_DEBUG 0
@@ -118,7 +119,7 @@ typedef struct {
CURLcode ehandle_result;
CURL *ehandle;
char *url;
- GString *recv_data;
+ NMStrBuf recv_data;
struct curl_slist *headers;
gssize max_data;
gulong cancellable_id;
@@ -144,8 +145,7 @@ _ehandle_free (EHandleData *edata)
g_object_unref (edata->task);
- if (edata->recv_data)
- g_string_free (edata->recv_data, TRUE);
+ nm_str_buf_destroy (&edata->recv_data);
if (edata->headers)
curl_slist_free_all (edata->headers);
g_free (edata->url);
@@ -191,12 +191,15 @@ _ehandle_complete (EHandleData *edata,
_LOG2E (edata, "failed to get response code from curl easy handle");
_LOG2D (edata, "success getting %"G_GSIZE_FORMAT" bytes (response code %ld)",
- edata->recv_data->len,
+ edata->recv_data.len,
response_code);
_LOG2T (edata, "received %"G_GSIZE_FORMAT" bytes: [[%s]]",
- edata->recv_data->len,
- nm_utils_buf_utf8safe_escape (edata->recv_data->str, edata->recv_data->len, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, &str_tmp_1));
+ edata->recv_data.len,
+ nm_utils_buf_utf8safe_escape (nm_str_buf_get_str (&edata->recv_data),
+ edata->recv_data.len,
+ NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL,
+ &str_tmp_1));
_ehandle_free_ehandle (edata);
@@ -205,7 +208,7 @@ _ehandle_complete (EHandleData *edata,
.response_code = response_code,
/* This ensures that response_data is always NUL terminated. This is an important guarantee
* that NMHttpClient makes. */
- .response_data = g_string_free_to_bytes (g_steal_pointer (&edata->recv_data)),
+ .response_data = nm_str_buf_finalize_to_gbytes (&edata->recv_data),
};
g_task_return_pointer (edata->task, get_result, _get_result_free);
@@ -225,14 +228,14 @@ _get_writefunction_cb (char *ptr, size_t size, size_t nmemb, void *user_data)
nmemb *= size;
if (edata->max_data >= 0) {
- nm_assert (edata->recv_data->len <= edata->max_data);
- nconsume = (((gsize) edata->max_data) - edata->recv_data->len);
+ nm_assert (edata->recv_data.len <= edata->max_data);
+ nconsume = (((gsize) edata->max_data) - edata->recv_data.len);
if (nconsume > nmemb)
nconsume = nmemb;
} else
nconsume = nmemb;
- g_string_append_len (edata->recv_data, ptr, nconsume);
+ nm_str_buf_append_len (&edata->recv_data, ptr, nconsume);
return nconsume;
}
@@ -283,7 +286,7 @@ nm_http_client_get (NMHttpClient *self,
edata = g_slice_new (EHandleData);
*edata = (EHandleData) {
.task = nm_g_task_new (self, cancellable, nm_http_client_get, callback, user_data),
- .recv_data = g_string_sized_new (NM_MIN (max_data, 245)),
+ .recv_data = NM_STR_BUF_INIT (0, FALSE),
.max_data = max_data,
.url = g_strdup (url),
.headers = NULL,
@@ -351,6 +354,21 @@ nm_http_client_get (NMHttpClient *self,
}
}
+/**
+ * nm_http_client_get_finish:
+ * @self: the #NMHttpClient instance
+ * @result: the #GAsyncResult which to complete.
+ * @out_response_code: (allow-none) (out): the HTTP response code or -1 on other error.
+ * @out_response_data: (allow-none) (transfer full): the HTTP response data, if any.
+ * The GBytes buffer is guaranteed to have a trailing NUL character *after* the
+ * returned buffer size. That means, you can always trust that the buffer is NUL terminated
+ * and that there is one additional hidden byte after the data.
+ * Also, the returned buffer is allocated just for you. While GBytes is immutable, you are
+ * allowed to modify the buffer as it's not used by anybody else.
+ * @error: the error
+ *
+ * Returns: %TRUE on success or %FALSE with an error code.
+ */
gboolean
nm_http_client_get_finish (NMHttpClient *self,
GAsyncResult *result,
@@ -364,6 +382,9 @@ nm_http_client_get_finish (NMHttpClient *self,
g_return_val_if_fail (nm_g_task_is_valid (result, self, nm_http_client_get), FALSE);
get_result = g_task_propagate_pointer (G_TASK (result), error);
+
+ nm_assert ((!!get_result) == (!error));
+
if (!get_result) {
NM_SET_OUT (out_response_code, -1);
NM_SET_OUT (out_response_data, NULL);
@@ -376,7 +397,6 @@ nm_http_client_get_finish (NMHttpClient *self,
NM_SET_OUT (out_response_data, g_steal_pointer (&get_result->response_data));
_get_result_free (get_result);
-
return TRUE;
}
@@ -447,11 +467,14 @@ _poll_get_probe_finish_fcn (GObject *source,
&response_data,
&local_error);
- if (!success) {
+ nm_assert ((!!success) == (!local_error));
+
+ if (local_error) {
if (nm_utils_error_is_cancelled (local_error)) {
g_propagate_error (error, g_steal_pointer (&local_error));
return TRUE;
}
+ /* any other error. Continue polling. */
return FALSE;
}
@@ -468,8 +491,10 @@ _poll_get_probe_finish_fcn (GObject *source,
return TRUE;
}
- if (!success)
+ if (!success) {
+ /* Not yet ready. Continue polling. */
return FALSE;
+ }
poll_get_data->response_code = response_code;
poll_get_data->response_data = g_steal_pointer (&response_data);
@@ -487,10 +512,12 @@ _poll_get_done_cb (GObject *source,
success = nmcs_utils_poll_finish (result, NULL, &error);
+ nm_assert ((!!success) == (!error));
+
if (error)
g_task_return_error (poll_get_data->task, g_steal_pointer (&error));
else
- g_task_return_boolean (poll_get_data->task, success);
+ g_task_return_boolean (poll_get_data->task, TRUE);
g_object_unref (poll_get_data->task);
}
@@ -569,10 +596,11 @@ nm_http_client_poll_get_finish (NMHttpClient *self,
task = G_TASK (result);
success = g_task_propagate_boolean (task, &local_error);
- if ( local_error
- || !success) {
- if (local_error)
- g_propagate_error (error, g_steal_pointer (&local_error));
+
+ nm_assert ((!!success) == (!local_error));
+
+ if (local_error) {
+ g_propagate_error (error, g_steal_pointer (&local_error));
NM_SET_OUT (out_response_code, -1);
NM_SET_OUT (out_response_data, NULL);
return FALSE;
@@ -582,7 +610,6 @@ nm_http_client_poll_get_finish (NMHttpClient *self,
NM_SET_OUT (out_response_code, poll_get_data->response_code);
NM_SET_OUT (out_response_data, g_steal_pointer (&poll_get_data->response_data));
-
return TRUE;
}
diff --git a/clients/cloud-setup/nmcs-provider-ec2.c b/clients/cloud-setup/nmcs-provider-ec2.c
index c8db31f97f..1578a33bed 100644
--- a/clients/cloud-setup/nmcs-provider-ec2.c
+++ b/clients/cloud-setup/nmcs-provider-ec2.c
@@ -90,13 +90,12 @@ _detect_get_meta_data_done_cb (GObject *source,
gs_unref_object GTask *task = user_data;
gs_free_error GError *get_error = NULL;
gs_free_error GError *error = NULL;
- gboolean success;
- success = nm_http_client_poll_get_finish (NM_HTTP_CLIENT (source),
- result,
- NULL,
- NULL,
- &get_error);
+ nm_http_client_poll_get_finish (NM_HTTP_CLIENT (source),
+ result,
+ NULL,
+ NULL,
+ &get_error);
if (nm_utils_error_is_cancelled (get_error)) {
g_task_return_error (task, g_steal_pointer (&get_error));
@@ -112,14 +111,6 @@ _detect_get_meta_data_done_cb (GObject *source,
return;
}
- if (!success) {
- nm_utils_error_set (&error,
- NM_UTILS_ERROR_UNKNOWN,
- "failure to detect EC2 metadata");
- g_task_return_error (task, g_steal_pointer (&error));
- return;
- }
-
g_task_return_boolean (task, TRUE);
}
@@ -191,31 +182,28 @@ _get_config_fetch_done_cb (NMHttpClient *http_client,
gboolean is_local_ipv4)
{
GetConfigIfaceData *iface_data;
- NMCSProviderGetConfigTaskData *get_config_data;
const char *hwaddr = NULL;
gs_unref_bytes GBytes *response_data = NULL;
gs_free_error GError *error = NULL;
- gboolean success;
- NMCSProviderGetConfigIfaceData *config_iface_data;
nm_utils_user_data_unpack (user_data, &iface_data, &hwaddr);
- success = nm_http_client_poll_get_finish (http_client,
- result,
- NULL,
- &response_data,
- &error);
+ nm_http_client_poll_get_finish (http_client,
+ result,
+ NULL,
+ &response_data,
+ &error);
+
if (nm_utils_error_is_cancelled (error))
return;
- get_config_data = iface_data->get_config_data;
-
- config_iface_data = g_hash_table_lookup (get_config_data->result_dict, hwaddr);
-
- if (success) {
+ if (!error) {
+ NMCSProviderGetConfigIfaceData *config_iface_data;
in_addr_t tmp_addr;
int tmp_prefix;
+ config_iface_data = g_hash_table_lookup (iface_data->get_config_data->result_dict, hwaddr);
+
if (is_local_ipv4) {
gs_free const char **s_addrs = NULL;
gsize i, len;
@@ -436,7 +424,9 @@ _get_config_metadata_ready_check (long response_code,
GetConfigMetadataData *metadata_data = check_user_data;
gs_unref_hashtable GHashTable *response_parsed = NULL;
const guint8 *r_data;
+ const char *cur_line;
gsize r_len;
+ gsize cur_line_len;
GHashTableIter h_iter;
gboolean has_all;
const char *c_hwaddr;
@@ -449,48 +439,33 @@ _get_config_metadata_ready_check (long response_code,
}
r_data = g_bytes_get_data (response_data, &r_len);
+ /* NMHttpClient guarantees that there is a trailing NUL after the data. */
+ nm_assert (r_data[r_len] == 0);
- while (r_len > 0) {
- const guint8 *p_eol;
- const char *p_start;
- gsize p_start_l;
- gsize p_start_l_2;
- char *hwaddr;
+ while (nm_utils_parse_next_line ((const char **) &r_data, &r_len, &cur_line, &cur_line_len)) {
GetConfigMetadataMac *mac_data;
+ char *hwaddr;
- p_start = (const char *) r_data;
-
- p_eol = memchr (r_data, '\n', r_len);
- if (p_eol) {
- p_start_l = (p_eol - r_data);
- r_len -= p_start_l + 1;
- r_data = &p_eol[1];
- } else {
- p_start_l = r_len;
- r_data = &r_data[r_len];
- r_len = 0;
- }
-
- if (p_start_l == 0)
+ if (cur_line_len == 0)
continue;
- p_start_l_2 = p_start_l;
- if (p_start[p_start_l_2 - 1] == '/') {
- /* trim the trailing "/". */
- p_start_l_2--;
- }
+ /* Truncate the string. It's safe to do, because we own @response_data an it has an
+ * extra NUL character after the buffer. */
+ ((char *) cur_line)[cur_line_len] = '\0';
- hwaddr = nmcs_utils_hwaddr_normalize (p_start, p_start_l_2);
+ hwaddr = nmcs_utils_hwaddr_normalize (cur_line,
+ cur_line[cur_line_len - 1u] == '/'
+ ? (gssize) (cur_line_len - 1u)
+ : -1);
if (!hwaddr)
continue;
if (!response_parsed)
response_parsed = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
- mac_data = g_malloc (sizeof (GetConfigMetadataData) + 1 + p_start_l);
+ mac_data = g_malloc (sizeof (GetConfigMetadataMac) + 1u + cur_line_len);
mac_data->iface_idx = iface_idx_counter++;
- memcpy (mac_data->path, p_start, p_start_l);
- mac_data->path[p_start_l] = '\0';
+ memcpy (mac_data->path, cur_line, cur_line_len + 1u);
g_hash_table_insert (response_parsed, hwaddr, mac_data);
}
diff --git a/clients/cloud-setup/nmcs-provider-gcp.c b/clients/cloud-setup/nmcs-provider-gcp.c
index ba4016dd15..7a8f3ef893 100644
--- a/clients/cloud-setup/nmcs-provider-gcp.c
+++ b/clients/cloud-setup/nmcs-provider-gcp.c
@@ -46,13 +46,12 @@ _detect_get_meta_data_done_cb (GObject *source,
gs_unref_object GTask *task = user_data;
gs_free_error GError *get_error = NULL;
gs_free_error GError *error = NULL;
- gboolean success;
- success = nm_http_client_poll_get_finish (NM_HTTP_CLIENT (source),
- result,
- NULL,
- NULL,
- &get_error);
+ nm_http_client_poll_get_finish (NM_HTTP_CLIENT (source),
+ result,
+ NULL,
+ NULL,
+ &get_error);
if (nm_utils_error_is_cancelled (get_error)) {
g_task_return_error (task, g_steal_pointer (&get_error));
@@ -68,14 +67,6 @@ _detect_get_meta_data_done_cb (GObject *source,
return;
}
- if (!success) {
- nm_utils_error_set (&error,
- NM_UTILS_ERROR_UNKNOWN,
- "failure to detect GCP metadata");
- g_task_return_error (task, g_steal_pointer (&error));
- return;
- }
-
g_task_return_boolean (task, TRUE);
}
@@ -246,29 +237,29 @@ _get_config_ips_list_cb (GObject *source,
if (error)
goto fips_error;
-
- uri_arr = g_ptr_array_new_with_free_func (g_free);
response_str = g_bytes_get_data (response, &response_len);
+ /* NMHttpClient guarantees that there is a trailing NUL after the data. */
+ nm_assert (response_str[response_len] == 0);
+ uri_arr = g_ptr_array_new_with_free_func (g_free);
while (nm_utils_parse_next_line (&response_str,
&response_len,
&line,
&line_len)) {
- nm_auto_free_gstring GString *gstr = NULL;
gint64 fip_index;
- gstr = g_string_new_len (line, line_len);
- fip_index = _nm_utils_ascii_str_to_int64 (gstr->str, 10, 0, G_MAXINT64, -1);
+ /* Truncate the string. It's safe to do, because we own @response_data an it has an
+ * extra NUL character after the buffer. */
+ ((char *) line)[line_len] = '\0';
- if (fip_index < 0) {
+ fip_index = _nm_utils_ascii_str_to_int64 (line, 10, 0, G_MAXINT64, -1);
+ if (fip_index < 0)
continue;
- }
- g_string_printf (gstr,
- "%"G_GSSIZE_FORMAT"/forwarded-ips/%"G_GINT64_FORMAT,
- iface_data->iface_idx,
- fip_index);
- g_ptr_array_add (uri_arr, g_string_free (g_steal_pointer (&gstr), FALSE));
+ g_ptr_array_add (uri_arr,
+ g_strdup_printf ("%"G_GSSIZE_FORMAT"/forwarded-ips/%"G_GINT64_FORMAT,
+ iface_data->iface_idx,
+ fip_index));
}
iface_data->n_fips_pending = uri_arr->len;
@@ -321,7 +312,7 @@ _get_config_iface_cb (GObject *source,
gs_free_error GError *error = NULL;
gs_free const char *hwaddr = NULL;
gs_free const char *uri = NULL;
- gs_free char *str = NULL;
+ char sbuf[100];
GCPData *gcp_data;
gcp_data = iface_data->gcp_data;
@@ -350,11 +341,10 @@ _get_config_iface_cb (GObject *source,
iface_data->iface_idx,
hwaddr);
- str = g_strdup_printf ("%"G_GSSIZE_FORMAT"/forwarded-ips/",
- iface_data->iface_idx);
+ nm_sprintf_buf (sbuf, "%"G_GSSIZE_FORMAT"/forwarded-ips/", iface_data->iface_idx);
nm_http_client_poll_get (NM_HTTP_CLIENT (source),
- (uri = _gcp_uri_interfaces (str)),
+ (uri = _gcp_uri_interfaces (sbuf)),
HTTP_TIMEOUT_MS,
HTTP_REQ_MAX_DATA,
HTTP_POLL_TIMEOUT_MS,
@@ -379,13 +369,10 @@ _get_net_ifaces_list_cb (GObject *source,
gpointer user_data)
{
gs_unref_ptrarray GPtrArray *ifaces_arr = NULL;
- nm_auto_free_gstring GString *gstr = NULL;
gs_unref_bytes GBytes *response = NULL;
gs_free_error GError *error = NULL;
GCPData *gcp_data = user_data;
const char *response_str;
- const char *token_start;
- const char *token_end;
gsize response_len;
const char *line;
gsize line_len;
@@ -403,8 +390,10 @@ _get_net_ifaces_list_cb (GObject *source,
}
response_str = g_bytes_get_data (response, &response_len);
+ /* NMHttpClient guarantees that there is a trailing NUL after the data. */
+ nm_assert (response_str[response_len] == 0);
+
ifaces_arr = g_ptr_array_new ();
- gstr = g_string_new (NULL);
while (nm_utils_parse_next_line (&response_str,
&response_len,
@@ -413,16 +402,16 @@ _get_net_ifaces_list_cb (GObject *source,
GCPIfaceData *iface_data;
gssize iface_idx;
- token_start = line;
- token_end = memchr (token_start, '/', line_len);
-
- if (!token_end)
+ if (line_len == 0)
continue;
- g_string_truncate (gstr, 0);
- g_string_append_len (gstr, token_start, token_end - token_start);
- iface_idx = _nm_utils_ascii_str_to_int64 (gstr->str, 10, 0, G_MAXSSIZE, -1);
+ /* Truncate the string. It's safe to do, because we own @response_data an it has an
+ * extra NUL character after the buffer. */
+ ((char *) line)[line_len] = '\0';
+ if (line[line_len - 1] == '/')
+ ((char *) line)[--line_len] = '\0';
+ iface_idx = _nm_utils_ascii_str_to_int64 (line, 10, 0, G_MAXSSIZE, -1);
if (iface_idx < 0)
continue;
@@ -442,14 +431,15 @@ _get_net_ifaces_list_cb (GObject *source,
for (i = 0; i < ifaces_arr->len; ++i) {
GCPIfaceData *data = ifaces_arr->pdata[i];
gs_free const char *uri = NULL;
+ char sbuf[100];
_LOGD ("GCP interface[%"G_GSSIZE_FORMAT"]: retrieving configuration",
data->iface_idx);
- g_string_printf (gstr, "%"G_GSSIZE_FORMAT"/mac", data->iface_idx);
+ nm_sprintf_buf (sbuf, "%"G_GSSIZE_FORMAT"/mac", data->iface_idx);
nm_http_client_poll_get (NM_HTTP_CLIENT (source),
- (uri = _gcp_uri_interfaces (gstr->str)),
+ (uri = _gcp_uri_interfaces (sbuf)),
HTTP_TIMEOUT_MS,
HTTP_REQ_MAX_DATA,
HTTP_POLL_TIMEOUT_MS,
@@ -484,7 +474,6 @@ get_config (NMCSProvider *provider,
.n_ifaces_pending = 0,
.error = NULL,
.success = FALSE,
-
};
nm_http_client_poll_get (nmcs_provider_get_http_client (provider),
diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c
index 1569662f8c..9fc4e510ac 100644
--- a/shared/nm-glib-aux/nm-shared-utils.c
+++ b/shared/nm-glib-aux/nm-shared-utils.c
@@ -1080,39 +1080,53 @@ nm_utils_parse_next_line (const char **inout_ptr,
const char **out_line,
gsize *out_line_len)
{
+ gboolean eol_is_carriage_return;
const char *line_start;
- const char *line_end;
+ gsize line_len;
- g_return_val_if_fail (inout_ptr, FALSE);
- g_return_val_if_fail (inout_len, FALSE);
- g_return_val_if_fail (out_line, FALSE);
+ nm_assert (inout_ptr);
+ nm_assert (inout_len);
+ nm_assert (*inout_len == 0 || *inout_ptr);
+ nm_assert (out_line);
+ nm_assert (out_line_len);
- if (*inout_len <= 0)
- goto error;
+ if (G_UNLIKELY (*inout_len == 0))
+ return FALSE;
line_start = *inout_ptr;
- line_end = memchr (line_start, '\n', *inout_len);
- if (!line_end)
- line_end = memchr (line_start, '\0', *inout_len);
- if (!line_end) {
- line_end = line_start + *inout_len;
- NM_SET_OUT (inout_len, 0);
- } else
- NM_SET_OUT (inout_len, *inout_len - (line_end - line_start) - 1);
- NM_SET_OUT (out_line, line_start);
- NM_SET_OUT (out_line_len, (gsize) (line_end - line_start));
+ eol_is_carriage_return = FALSE;
+ for (line_len = 0; ; line_len++) {
+ if (line_len >= *inout_len) {
+ /* if we consumed the entire line, we place the pointer at
+ * one character after the end. */
+ *inout_ptr = &line_start[line_len];
+ *inout_len = 0;
+ goto done;
+ }
+ switch (line_start[line_len]) {
+ case '\r':
+ eol_is_carriage_return = TRUE;
+ /* fall-through*/
+ case '\0':
+ case '\n':
+ *inout_ptr = &line_start[line_len + 1];
+ *inout_len = *inout_len - line_len - 1u;
+ if ( eol_is_carriage_return
+ && *inout_len > 0
+ && (*inout_ptr)[0] == '\n') {
+ /* also consume "\r\n" as one. */
+ (*inout_len)--;
+ (*inout_ptr)++;
+ }
+ goto done;
+ }
+ }
- if (*inout_len > 0)
- NM_SET_OUT (inout_ptr, line_end + 1);
- else
- NM_SET_OUT (inout_ptr, NULL);
+done:
+ *out_line = line_start;
+ *out_line_len = line_len;
return TRUE;
-
-error:
- NM_SET_OUT (out_line, NULL);
- NM_SET_OUT (out_line_len, 0);
- return FALSE;
}
/*****************************************************************************/
diff --git a/shared/nm-glib-aux/nm-str-buf.h b/shared/nm-glib-aux/nm-str-buf.h
index 61246969de..f4a3542c4f 100644
--- a/shared/nm-glib-aux/nm-str-buf.h
+++ b/shared/nm-glib-aux/nm-str-buf.h
@@ -429,6 +429,23 @@ nm_str_buf_finalize (NMStrBuf *strbuf,
return g_steal_pointer (&strbuf->_priv_str);
}
+static inline GBytes *
+nm_str_buf_finalize_to_gbytes (NMStrBuf *strbuf)
+{
+ char *s;
+ gsize l;
+
+ /* this always returns a non-NULL, newly allocated GBytes instance.
+ * The data buffer always has an additional NUL character after
+ * the data, and the data is allocated with malloc.
+ *
+ * That means, the caller who takes ownership of the GBytes can
+ * safely modify the content of the buffer (including the additional
+ * NUL sentinel). */
+ s = nm_str_buf_finalize (strbuf, &l);
+ return g_bytes_new_take (s ?: g_new0 (char, 1), l);
+}
+
/**
* nm_str_buf_destroy:
* @strbuf: an initialized #NMStrBuf
diff --git a/shared/nm-glib-aux/tests/test-shared-general.c b/shared/nm-glib-aux/tests/test-shared-general.c
index a435e28195..f389770a7a 100644
--- a/shared/nm-glib-aux/tests/test-shared-general.c
+++ b/shared/nm-glib-aux/tests/test-shared-general.c
@@ -773,6 +773,109 @@ test_nm_str_buf (void)
/*****************************************************************************/
+static void
+test_nm_utils_parse_next_line (void)
+{
+ const char *data;
+ const char *data0;
+ gsize data_len;
+ const char *line_start;
+ gsize line_len;
+ int i_run;
+ gsize j, k;
+
+ data = NULL;
+ data_len = 0;
+ g_assert (!nm_utils_parse_next_line (&data, &data_len, &line_start, &line_len));
+
+ for (i_run = 0; i_run < 1000; i_run++) {
+ gs_unref_ptrarray GPtrArray *strv = g_ptr_array_new_with_free_func (g_free);
+ gs_unref_ptrarray GPtrArray *strv2 = g_ptr_array_new_with_free_func (g_free);
+ gsize strv_len = nmtst_get_rand_word_length (NULL);
+ nm_auto_str_buf NMStrBuf strbuf = NM_STR_BUF_INIT (0, nmtst_get_rand_bool ());
+
+ /* create a list of random words. */
+ for (j = 0; j < strv_len; j++) {
+ gsize w_len = nmtst_get_rand_word_length (NULL);
+ NMStrBuf w_buf = NM_STR_BUF_INIT (nmtst_get_rand_uint32 () % (w_len + 1), nmtst_get_rand_bool ());
+
+ for (k = 0; k < w_len; k++)
+ nm_str_buf_append_c (&w_buf, '0' + (k % 10));
+ nm_str_buf_maybe_expand (&w_buf, 1, TRUE);
+ g_ptr_array_add (strv, nm_str_buf_finalize (&w_buf, NULL));
+ }
+
+ /* join the list of random words with (random) line delimiters
+ * ("\0", "\n", "\r" or EOF). */
+ for (j = 0; j < strv_len; j++) {
+ nm_str_buf_append (&strbuf, strv->pdata[j]);
+again:
+ switch (nmtst_get_rand_uint32 () % 5) {
+ case 0:
+ nm_str_buf_append_c (&strbuf, '\0');
+ break;
+ case 1:
+ if ( strbuf.len > 0
+ && (nm_str_buf_get_str_unsafe (&strbuf))[strbuf.len - 1] == '\r') {
+ /* the previous line was empty and terminated by "\r". We
+ * must not join with "\n". Retry. */
+ goto again;
+ }
+ nm_str_buf_append_c (&strbuf, '\n');
+ break;
+ case 2:
+ nm_str_buf_append_c (&strbuf, '\r');
+ break;
+ case 3:
+ nm_str_buf_append (&strbuf, "\r\n");
+ break;
+ case 4:
+ /* the last word randomly is delimited or not, but not if the last
+ * word is "". */
+ if (j + 1 < strv_len) {
+ /* it's not the last word. Retry. */
+ goto again;
+ }
+ g_assert (j == strv_len - 1);
+ if (((const char *) strv->pdata[j])[0] == '\0') {
+ /* if the last word was "", we need a delimiter (to parse it back).
+ * Retry. */
+ goto again;
+ }
+ /* The final delimiter gets omitted. It's EOF. */
+ break;
+ }
+ }
+
+ data0 = nm_str_buf_get_str_unsafe (&strbuf);
+ if ( !data0
+ && nmtst_get_rand_bool ()) {
+ nm_str_buf_maybe_expand (&strbuf, 1, TRUE);
+ data0 = nm_str_buf_get_str_unsafe (&strbuf);
+ g_assert (data0);
+ }
+ data_len = strbuf.len;
+ g_assert ((data_len > 0 && data0) || data_len == 0);
+ data = data0;
+ while (nm_utils_parse_next_line (&data, &data_len, &line_start, &line_len)) {
+ g_assert (line_start);
+ g_assert (line_start >= data0);
+ g_assert (line_start < &data0[strbuf.len]);
+ g_assert (!memchr (line_start, '\0', line_len));
+ g_ptr_array_add (strv2, g_strndup (line_start, line_len));
+ }
+ g_assert (data_len == 0);
+ if (data0)
+ g_assert (data == &data0[strbuf.len]);
+ else
+ g_assert (!data);
+
+ g_assert (_nm_utils_strv_cmp_n ((const char *const*) strv->pdata, strv->len, (const char *const*) strv2->pdata, strv2->len) == 0);
+ }
+}
+
+/*****************************************************************************/
+
NMTST_DEFINE ();
int main (int argc, char **argv)
@@ -794,6 +897,7 @@ int main (int argc, char **argv)
g_test_add_func ("/general/test_string_table_lookup", test_string_table_lookup);
g_test_add_func ("/general/test_nm_utils_get_next_realloc_size", test_nm_utils_get_next_realloc_size);
g_test_add_func ("/general/test_nm_str_buf", test_nm_str_buf);
+ g_test_add_func ("/general/test_nm_utils_parse_next_line", test_nm_utils_parse_next_line);
return g_test_run ();
}