summary refs log tree commit diff
path: root/src/nm-core-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm-core-utils.c')
-rw-r--r--src/nm-core-utils.c278
1 files changed, 218 insertions, 60 deletions
diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c
index b97be0b8..f6b33a14 100644
--- a/src/nm-core-utils.c
+++ b/src/nm-core-utils.c
@@ -452,6 +452,83 @@ nm_utils_modprobe (GError **error, gboolean suppress_error_logging, const char *
 	return exit_status;
 }
 
+/**
+ * nm_utils_get_start_time_for_pid:
+ * @pid: the process identifier
+ * @out_state: return the state character, like R, S, Z. See `man 5 proc`.
+ * @out_ppid: parent process id
+ *
+ * Originally copied from polkit source (src/polkit/polkitunixprocess.c)
+ * and adjusted.
+ *
+ * Returns: the timestamp when the process started (by parsing /proc/$PID/stat).
+ * If an error occurs (e.g. the process does not exist), 0 is returned.
+ *
+ * The returned start time counts since boot, in the unit HZ (with HZ usually being (1/100) seconds)
+ **/
+guint64
+nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid)
+{
+	guint64 start_time;
+	char filename[256];
+	gs_free gchar *contents = NULL;
+	size_t length;
+	gs_strfreev gchar **tokens = NULL;
+	guint num_tokens;
+	gchar *p;
+	char state = ' ';
+	gint64 ppid = 0;
+
+	start_time = 0;
+	contents = NULL;
+
+	g_return_val_if_fail (pid > 0, 0);
+
+	nm_sprintf_buf (filename, "/proc/%"G_GUINT64_FORMAT"/stat", (guint64) pid);
+
+	if (!g_file_get_contents (filename, &contents, &length, NULL))
+		goto fail;
+
+	/* start time is the token at index 19 after the '(process name)' entry - since only this
+	 * field can contain the ')' character, search backwards for this to avoid malicious
+	 * processes trying to fool us
+	 */
+	p = strrchr (contents, ')');
+	if (p == NULL)
+		goto fail;
+	p += 2; /* skip ') ' */
+	if (p - contents >= (int) length)
+		goto fail;
+
+	state = p[0];
+
+	tokens = g_strsplit (p, " ", 0);
+
+	num_tokens = g_strv_length (tokens);
+
+	if (num_tokens < 20)
+		goto fail;
+
+	if (out_ppid) {
+		ppid = _nm_utils_ascii_str_to_int64 (tokens[1], 10, 1, G_MAXINT, 0);
+		if (ppid == 0)
+			goto fail;
+	}
+
+	start_time = _nm_utils_ascii_str_to_int64 (tokens[19], 10, 1, G_MAXINT64, 0);
+	if (start_time == 0)
+		goto fail;
+
+	NM_SET_OUT (out_state, state);
+	NM_SET_OUT (out_ppid, ppid);
+	return start_time;
+
+fail:
+	NM_SET_OUT (out_state, ' ');
+	NM_SET_OUT (out_ppid, 0);
+	return 0;
+}
+
 /*****************************************************************************/
 
 typedef struct {
@@ -1169,8 +1246,8 @@ typedef struct {
 static gboolean
 match_device_s390_subchannels_parse (const char *s390_subchannels, guint32 *out_a, guint32 *out_b, guint32 *out_c)
 {
-	char buf[30 + 1];
-	const int BUFSIZE = G_N_ELEMENTS (buf) - 1;
+	const int BUFSIZE = 30;
+	char buf[BUFSIZE + 1];
 	guint i = 0;
 	char *pa = NULL, *pb = NULL, *pc = NULL;
 	gint64 a, b, c;
@@ -1800,6 +1877,110 @@ nm_utils_new_infiniband_name (char *name, const char *parent_name, int p_key)
 
 /*****************************************************************************/
 
+gboolean
+nm_utils_resolve_conf_parse (int addr_family,
+                             const char *rc_contents,
+                             GArray *nameservers,
+                             GPtrArray *dns_options)
+{
+	guint i;
+	gboolean changed = FALSE;
+	gs_free const char **lines = NULL;
+	gsize l;
+
+	g_return_val_if_fail (rc_contents, FALSE);
+	g_return_val_if_fail (nameservers, FALSE);
+	g_return_val_if_fail (   (   addr_family == AF_INET
+	                          && g_array_get_element_size (nameservers) == sizeof (in_addr_t))
+	                      || (   addr_family == AF_INET6
+	                          && g_array_get_element_size (nameservers) == sizeof (struct in6_addr)), FALSE);
+
+	lines = nm_utils_strsplit_set (rc_contents, "\r\n");
+	if (!lines)
+		return FALSE;
+
+/* like glibc's MATCH() macro in resolv/res_init.c. */
+#define RC_MATCH(line, option, out_arg) \
+    ({ \
+        const char *const _line = (line); \
+        gboolean _match = FALSE; \
+        \
+        if (   (strncmp (_line, option, NM_STRLEN (option)) == 0) \
+            && (NM_IN_SET (_line[NM_STRLEN (option)], ' ', '\t'))) { \
+            _match = TRUE;\
+            (out_arg) = &_line[NM_STRLEN (option) + 1]; \
+        } \
+        _match; \
+    })
+
+	for (l = 0; lines[l]; l++) {
+		const char *const line = lines[l];
+		const char *s = NULL;
+
+		if (RC_MATCH (line, "nameserver", s)) {
+			gs_free char *s_cpy = NULL;
+			NMIPAddr ns;
+
+			s = nm_strstrip_avoid_copy (s, &s_cpy);
+			if (inet_pton (addr_family, s, &ns) != 1)
+				continue;
+
+			if (addr_family == AF_INET) {
+				if (!ns.addr4)
+					continue;
+				for (i = 0; i < nameservers->len; i++) {
+					if (g_array_index (nameservers, guint32, i) == ns.addr4)
+						break;
+				}
+			} else {
+				if (IN6_IS_ADDR_UNSPECIFIED (&ns.addr6))
+					continue;
+				for (i = 0; i < nameservers->len; i++) {
+					struct in6_addr *t = &g_array_index (nameservers, struct in6_addr, i);
+
+					if (IN6_ARE_ADDR_EQUAL (t, &ns.addr6))
+						break;
+				}
+			}
+
+			if (i == nameservers->len) {
+				g_array_append_val (nameservers, ns);
+				changed = TRUE;
+			}
+			continue;
+		}
+
+		if (RC_MATCH (line, "options", s)) {
+			if (!dns_options)
+				continue;
+
+			s = nm_str_skip_leading_spaces (s);
+			if (s[0]) {
+				gs_free const char **tokens = NULL;
+				gsize i_tokens;
+
+				tokens = nm_utils_strsplit_set (s, " \t");
+				for (i_tokens = 0; tokens && tokens[i_tokens]; i_tokens++) {
+					gs_free char *t = g_strstrip (g_strdup (tokens[i_tokens]));
+
+					if (   _nm_utils_dns_option_validate (t, NULL, NULL,
+					                                      addr_family == AF_INET6,
+					                                      _nm_utils_dns_option_descs)
+					    && _nm_utils_dns_option_find_idx (dns_options, t) < 0) {
+						g_ptr_array_add (dns_options, g_steal_pointer (&t));
+						changed = TRUE;
+					}
+				}
+			}
+			continue;
+		}
+	}
+
+	return changed;
+}
+
+/*****************************************************************************/
+
 /**
  * nm_utils_cmp_connection_by_autoconnect_priority:
  * @a:
@@ -2157,13 +2338,7 @@ _log_connection_sort_names (LogConnectionSettingData *setting_data, GArray *sort
 }
 
 void
-nm_utils_log_connection_diff (NMConnection *connection,
-                              NMConnection *diff_base,
-                              guint32 level,
-                              guint64 domain,
-                              const char *name,
-                              const char *prefix,
-                              const char *dbus_path)
+nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, guint32 level, guint64 domain, const char *name, const char *prefix)
 {
 	GHashTable *connection_diff = NULL;
 	GArray *sorted_hashes;
@@ -2239,6 +2414,7 @@ nm_utils_log_connection_diff (NMConnection *connection,
 
 			if (print_header) {
 				GError *err_verify = NULL;
+				const char *path = nm_connection_get_path (connection);
 				const char *t1, *t2;
 
 				t1 = nm_connection_get_connection_type (connection);
@@ -2248,12 +2424,12 @@ nm_utils_log_connection_diff (NMConnection *connection,
 					        prefix, name,
 					        connection, G_OBJECT_TYPE_NAME (connection), NM_PRINT_FMT_QUOTE_STRING (t1),
 					        diff_base, G_OBJECT_TYPE_NAME (diff_base), NM_PRINT_FMT_QUOTE_STRING (t2),
-					        NM_PRINT_FMT_QUOTED (dbus_path, " [", dbus_path, "]", ""));
+					        NM_PRINT_FMT_QUOTED (path, " [", path, "]", ""));
 				} else {
 					nm_log (level, domain, NULL, NULL, "%sconnection '%s' (%p/%s/%s%s%s):%s%s%s",
 					        prefix, name,
 					        connection, G_OBJECT_TYPE_NAME (connection), NM_PRINT_FMT_QUOTE_STRING (t1),
-					        NM_PRINT_FMT_QUOTED (dbus_path, " [", dbus_path, "]", ""));
+					        NM_PRINT_FMT_QUOTED (path, " [", path, "]", ""));
 				}
 				print_header = FALSE;
 
@@ -2571,7 +2747,7 @@ _get_contents_error (GError **error, int errsv, const char *format, ...)
 /**
  * nm_utils_fd_get_contents:
  * @fd: open file descriptor to read. The fd will not be closed,
- *   but don't rely on its state afterwards.
+ *   but don't rely on it's state afterwards.
  * @close_fd: if %TRUE, @fd will be closed by the function.
  *  Passing %TRUE here might safe a syscall for dup().
  * @max_length: allocate at most @max_length bytes. If the
@@ -3573,8 +3749,7 @@ nm_utils_setpgid (gpointer unused G_GNUC_UNUSED)
 /**
  * nm_utils_g_value_set_strv:
  * @value: a #GValue, initialized to store a #G_TYPE_STRV
- * @strings: a #GPtrArray of strings. %NULL values are not
- *   allowed.
+ * @strings: a #GPtrArray of strings
  *
  * Converts @strings to a #GStrv and stores it in @value.
  */
@@ -3582,13 +3757,11 @@ void
 nm_utils_g_value_set_strv (GValue *value, GPtrArray *strings)
 {
 	char **strv;
-	guint i;
+	int i;
 
 	strv = g_new (char *, strings->len + 1);
-	for (i = 0; i < strings->len; i++) {
-		nm_assert (strings->pdata[i]);
+	for (i = 0; i < strings->len; i++)
 		strv[i] = g_strdup (strings->pdata[i]);
-	}
 	strv[i] = NULL;
 
 	g_value_take_boxed (value, strv);
@@ -3718,11 +3891,12 @@ nm_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp,
 	return t;
 }
 
-guint32
+gboolean
 nm_utils_lifetime_get (guint32 timestamp,
                        guint32 lifetime,
                        guint32 preferred,
                        gint32 now,
+                       guint32 *out_lifetime,
                        guint32 *out_preferred)
 {
 	guint32 t_lifetime, t_preferred;
@@ -3730,39 +3904,38 @@ nm_utils_lifetime_get (guint32 timestamp,
 	nm_assert (now >= 0);
 
 	if (timestamp == 0 && lifetime == 0) {
-		/* We treat lifetime==0 && timestamp==0 addresses as permanent addresses to allow easy
+		/* We treat lifetime==0 && timestamp == 0 addresses as permanent addresses to allow easy
 		 * creation of such addresses (without requiring to set the lifetime fields to
 		 * NM_PLATFORM_LIFETIME_PERMANENT). The real lifetime==0 addresses (E.g. DHCP6 telling us
 		 * to drop an address will have timestamp set.
 		 */
-		NM_SET_OUT (out_preferred, NM_PLATFORM_LIFETIME_PERMANENT);
-		g_return_val_if_fail (preferred == 0, NM_PLATFORM_LIFETIME_PERMANENT);
-		return NM_PLATFORM_LIFETIME_PERMANENT;
-	}
+		*out_lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
+		*out_preferred = NM_PLATFORM_LIFETIME_PERMANENT;
+		g_return_val_if_fail (preferred == 0, TRUE);
+	} else {
+		if (now <= 0)
+			now = nm_utils_get_monotonic_timestamp_s ();
+		t_lifetime = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now);
+		if (!t_lifetime) {
+			*out_lifetime = 0;
+			*out_preferred = 0;
+			return FALSE;
+		}
+		t_preferred = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, preferred, now);
 
-	if (now <= 0)
-		now = nm_utils_get_monotonic_timestamp_s ();
+		*out_lifetime = t_lifetime;
+		*out_preferred = MIN (t_preferred, t_lifetime);
 
-	t_lifetime = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now);
-	if (!t_lifetime) {
-		NM_SET_OUT (out_preferred, 0);
-		return 0;
+		/* Assert that non-permanent addresses have a (positive) @timestamp. nm_utils_lifetime_rebase_relative_time_on_now()
+		 * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always
+		 * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew.
+		 */
+		g_return_val_if_fail (   timestamp != 0
+		                      || (   lifetime  == NM_PLATFORM_LIFETIME_PERMANENT
+		                          && preferred == NM_PLATFORM_LIFETIME_PERMANENT), TRUE);
+		g_return_val_if_fail (t_preferred <= t_lifetime, TRUE);
 	}
-
-	t_preferred = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, preferred, now);
-
-	NM_SET_OUT (out_preferred, MIN (t_preferred, t_lifetime));
-
-	/* Assert that non-permanent addresses have a (positive) @timestamp. nm_utils_lifetime_rebase_relative_time_on_now()
-	 * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always
-	 * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew.
-	 */
-	g_return_val_if_fail (   timestamp != 0
-	                      || (   lifetime  == NM_PLATFORM_LIFETIME_PERMANENT
-	                          && preferred == NM_PLATFORM_LIFETIME_PERMANENT), t_lifetime);
-	g_return_val_if_fail (t_preferred <= t_lifetime, t_lifetime);
-
-	return t_lifetime;
+	return TRUE;
 }
 
 const char *
@@ -4156,21 +4329,6 @@ nm_utils_format_con_diff_for_audit (GHashTable *diff)
 	return g_string_free (str, FALSE);
 }
 
-const char *
-nm_utils_parse_dns_domain (const char *domain, gboolean *is_routing)
-{
-	g_return_val_if_fail (domain, NULL);
-	g_return_val_if_fail (domain[0], NULL);
-
-	if (domain[0] == '~') {
-		domain++;
-		NM_SET_OUT (is_routing, TRUE);
-	} else
-		NM_SET_OUT (is_routing, FALSE);
-
-	return domain;
-}
-
 /*****************************************************************************/
 
 NM_UTILS_ENUM2STR_DEFINE (nm_icmpv6_router_pref_to_string, NMIcmpv6RouterPref,