summary refs log tree commit diff
path: root/src/nmcli/common.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2022-08-16 18:24:19 +0200
committerMichael Biebl <biebl@debian.org>2022-08-16 18:24:19 +0200
commit0018d1f3cf71d680d7b6bceda55a5717244d8b26 (patch)
treea058f1d106d172d3354179437ef034c9355cdf9c /src/nmcli/common.c
parent6accbd3ec0e42d8633bbde4d47ed7bfe854e7e0b (diff)
New upstream version 1.39.90 upstream/1.39.90
Diffstat (limited to 'src/nmcli/common.c')
-rw-r--r--src/nmcli/common.c163
1 files changed, 156 insertions, 7 deletions
diff --git a/src/nmcli/common.c b/src/nmcli/common.c
index f42f76e4..ff4c15c3 100644
--- a/src/nmcli/common.c
+++ b/src/nmcli/common.c
@@ -16,6 +16,8 @@
 #include <readline/history.h>
 #include <readline/readline.h>
 #endif
+#include <gio/gunixinputstream.h>
+
 #include "libnm-client-aux-extern/nm-libnm-aux.h"
 
 #include "libnmc-base/nm-vpn-helpers.h"
@@ -469,7 +471,8 @@ nmc_find_connection(const GPtrArray *connections,
                 goto found;
         }
 
-        if (NM_IN_STRSET(filter_type, NULL, "filename")) {
+        if (NM_IS_REMOTE_CONNECTION(connections->pdata[i])
+            && NM_IN_STRSET(filter_type, NULL, "filename")) {
             v = nm_remote_connection_get_filename(NM_REMOTE_CONNECTION(connections->pdata[i]));
             if (complete && (filter_type || *filter_val))
                 nmc_complete_strings(filter_val, v);
@@ -536,8 +539,6 @@ nmc_find_active_connection(const GPtrArray *active_cons,
         NMActiveConnection *candidate = g_ptr_array_index(active_cons, i);
         const char         *v, *v_num;
 
-        con = nm_active_connection_get_connection(candidate);
-
         /* When filter_type is NULL, compare connection ID (filter_val)
          * against all types. Otherwise, only compare against the specific
          * type. If 'path' or 'apath' filter types are specified, comparison
@@ -559,6 +560,8 @@ nmc_find_active_connection(const GPtrArray *active_cons,
                 goto found;
         }
 
+        con = nm_active_connection_get_connection(candidate);
+
         if (NM_IN_STRSET(filter_type, NULL, "path")) {
             v     = con ? nm_connection_get_path(NM_CONNECTION(con)) : NULL;
             v_num = nm_utils_dbus_path_get_last_component(v);
@@ -569,7 +572,7 @@ nmc_find_active_connection(const GPtrArray *active_cons,
         }
 
         if (NM_IN_STRSET(filter_type, NULL, "filename")) {
-            v = nm_remote_connection_get_filename(con);
+            v = con ? nm_remote_connection_get_filename(con) : NULL;
             if (complete && (filter_type || *filter_val))
                 nmc_complete_strings(filter_val, v);
             if (nm_streq0(filter_val, v))
@@ -926,7 +929,8 @@ read_again:
         }
     } else if (!rl_string) {
         /* Ctrl-D, exit */
-        nmc_exit();
+        if (g_main_loop_is_running(loop))
+            nmc_exit();
     }
 
     /* Return NULL, not empty string */
@@ -1010,7 +1014,7 @@ nmc_readline_echo(const NmcConfig *nmc_config, gboolean echo_on, const char *pro
 #if HAVE_READLINE_HISTORY
     nm_auto_free HISTORY_STATE *saved_history  = NULL;
     HISTORY_STATE               passwd_history = {
-        0,
+                      0,
     };
 #else
     int start, curpos;
@@ -1273,12 +1277,157 @@ got_client(GObject *source_object, GAsyncResult *res, gpointer user_data)
     nm_g_slice_free(call);
 }
 
+typedef struct {
+    GString *str;
+    char     buf[512];
+    CmdCall *call;
+} CmdStdinData;
+
+static void read_offline_connection_next(GInputStream *stream, CmdStdinData *data);
+
+static void
+read_offline_connection_chunk(GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+    GInputStream                   *stream   = G_INPUT_STREAM(source_object);
+    CmdStdinData                   *data     = user_data;
+    CmdCall                        *call     = data->call;
+    gs_unref_object GTask          *task     = NULL;
+    nm_auto_unref_keyfile GKeyFile *keyfile  = NULL;
+    gs_free char                   *base_dir = NULL;
+    GError                         *error    = NULL;
+    gssize                          bytes_read;
+    NMConnection                   *connection;
+    NmCli                          *nmc;
+
+    bytes_read = g_input_stream_read_finish(stream, res, &error);
+    if (bytes_read > 0) {
+        /* We need to read more. */
+        g_string_append_len(data->str, data->buf, bytes_read);
+        read_offline_connection_next(stream, data);
+        return;
+    }
+
+    /* End reached. */
+
+    task = g_steal_pointer(&call->task);
+    nmc  = g_task_get_task_data(task);
+    nmc->should_wait--;
+
+    if (bytes_read == -1) {
+        g_task_return_error(task, error);
+        goto finish;
+    }
+
+    keyfile = g_key_file_new();
+    if (!g_key_file_load_from_data(keyfile,
+                                   data->str->str,
+                                   data->str->len,
+                                   G_KEY_FILE_NONE,
+                                   &error)) {
+        g_task_return_error(task, error);
+        goto finish;
+    }
+
+    base_dir = g_get_current_dir();
+    connection =
+        nm_keyfile_read(keyfile, base_dir, NM_KEYFILE_HANDLER_FLAGS_NONE, NULL, NULL, &error);
+    if (!connection) {
+        g_task_return_error(task, error);
+        goto finish;
+    }
+
+    g_ptr_array_add(nmc->offline_connections, connection);
+    call->cmd->func(call->cmd, nmc, call->argc, (const char *const *) call->argv);
+    g_task_return_boolean(task, TRUE);
+
+finish:
+    g_strfreev(call->argv);
+    nm_g_slice_free(call);
+    g_string_free(data->str, TRUE);
+    nm_g_slice_free(data);
+}
+
+static void
+read_offline_connection_next(GInputStream *stream, CmdStdinData *data)
+{
+    g_input_stream_read_async(stream,
+                              data->buf,
+                              sizeof(data->buf),
+                              G_PRIORITY_DEFAULT,
+                              NULL,
+                              read_offline_connection_chunk,
+                              data);
+}
+
+static void
+read_offline_connection(CmdCall *call)
+{
+    gs_unref_object GInputStream *stream = NULL;
+    CmdStdinData                 *data;
+
+    stream     = g_unix_input_stream_new(STDIN_FILENO, TRUE);
+    data       = g_slice_new(CmdStdinData);
+    data->call = call;
+    data->str  = g_string_new_len(NULL, sizeof(data->buf));
+
+    read_offline_connection_next(stream, data);
+}
+
+static NMConnection *
+dummy_offline_connection(void)
+{
+    NMConnection *connection;
+
+    connection = nm_simple_connection_new();
+    nm_connection_add_setting(connection, nm_setting_connection_new());
+    return connection;
+}
+
 static void
 call_cmd(NmCli *nmc, GTask *task, const NMCCommand *cmd, int argc, const char *const *argv)
 {
     CmdCall *call;
 
-    if (nmc->client || !cmd->needs_client) {
+    if (nmc->offline) {
+        if (!cmd->supports_offline) {
+            g_task_return_new_error(task,
+                                    NMCLI_ERROR,
+                                    NMC_RESULT_ERROR_USER_INPUT,
+                                    _("Error: command doesn't support --offline mode."));
+            g_object_unref(task);
+            return;
+        }
+
+        if (!nmc->offline_connections)
+            nmc->offline_connections = g_ptr_array_new_full(1, g_object_unref);
+
+        if (cmd->needs_offline_conn) {
+            g_return_if_fail(nmc->offline_connections->len == 0);
+
+            if (nmc->complete) {
+                g_ptr_array_add(nmc->offline_connections, dummy_offline_connection());
+                cmd->func(cmd, nmc, argc, argv);
+                g_task_return_boolean(task, TRUE);
+                g_object_unref(task);
+                return;
+            }
+
+            nmc->should_wait++;
+            call  = g_slice_new(CmdCall);
+            *call = (CmdCall){
+                .cmd  = cmd,
+                .argc = argc,
+                .argv = nm_strv_dup(argv, argc, TRUE),
+                .task = task,
+            };
+            read_offline_connection(call);
+            return;
+        } else {
+            cmd->func(cmd, nmc, argc, argv);
+            g_task_return_boolean(task, TRUE);
+            g_object_unref(task);
+        }
+    } else if (nmc->client || !cmd->needs_client) {
         /* Check whether NetworkManager is running */
         if (cmd->needs_nm_running && !nm_client_get_nm_running(nmc->client)) {
             g_task_return_new_error(task,