about 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.c425
1 files changed, 380 insertions, 45 deletions
diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c
index ca8c9526..54ccdb83 100644
--- a/src/nm-core-utils.c
+++ b/src/nm-core-utils.c
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2004 - 2018 Red Hat, Inc.
+ * Copyright 2004 - 2014 Red Hat, Inc.
  * Copyright 2005 - 2008 Novell, Inc.
  */
 
@@ -25,7 +25,6 @@
 
 #include <errno.h>
 #include <fcntl.h>
-#include <fnmatch.h>
 #include <string.h>
 #include <unistd.h>
 #include <stdlib.h>
@@ -38,7 +37,6 @@
 #include <net/ethernet.h>
 
 #include "nm-utils/nm-random-utils.h"
-#include "nm-utils/nm-io-utils.h"
 #include "nm-utils.h"
 #include "nm-core-internal.h"
 #include "nm-setting-connection.h"
@@ -47,6 +45,15 @@
 #include "nm-setting-wireless.h"
 #include "nm-setting-wireless-security.h"
 
+/*
+ * Some toolchains (E.G. uClibc 0.9.33 and earlier) don't export
+ * CLOCK_BOOTTIME even though the kernel supports it, so provide a
+ * local definition
+ */
+#ifndef CLOCK_BOOTTIME
+#define CLOCK_BOOTTIME 7
+#endif
+
 G_STATIC_ASSERT (sizeof (NMUtilsTestFlags) <= sizeof (int));
 static int _nm_utils_testing = 0;
 
@@ -525,7 +532,7 @@ _kc_waited_to_string (char *buf, gint64 wait_start_us)
 }
 
 static void
-_kc_cb_watch_child (GPid pid, int status, gpointer user_data)
+_kc_cb_watch_child (GPid pid, gint status, gpointer user_data)
 {
 	KillChildAsyncData *data = user_data;
 	char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE], buf_wait[KC_WAITED_TO_STRING];
@@ -1448,7 +1455,7 @@ match_config_eval (const char *str, const char *tag, guint cur_nm_version)
 {
 	gs_free char *s_ver = NULL;
 	gs_strfreev char **s_ver_tokens = NULL;
-	int v_maj = -1, v_min = -1, v_mic = -1;
+	gint v_maj = -1, v_min = -1, v_mic = -1;
 	guint c_maj = -1, c_min = -1, c_mic = -1;
 	guint n_tokens;
 
@@ -1580,9 +1587,9 @@ nm_match_spec_split (const char *value)
 	/* Copied from glibs g_key_file_parse_value_as_string() function
 	 * and adjusted. */
 
-	string_value = g_new (char, strlen (value) + 1);
+	string_value = g_new (gchar, strlen (value) + 1);
 
-	p = (char *) value;
+	p = (gchar *) value;
 
 	/* skip over leading whitespace */
 	while (g_ascii_isspace (*p))
@@ -1732,33 +1739,6 @@ nm_match_spec_join (GSList *specs)
 	return g_string_free (str, FALSE);
 }
 
-gboolean
-nm_wildcard_match_check (const char *str,
-                         const char *const *patterns,
-                         guint num_patterns)
-{
-	guint i, neg = 0;
-
-	for (i = 0; i < num_patterns; i++) {
-		if (patterns[i][0] == '!') {
-			neg++;
-			if (!fnmatch (patterns[i] + 1, str, 0))
-				return FALSE;
-		}
-	}
-
-	if (neg == num_patterns)
-		return TRUE;
-
-	for (i = 0; i < num_patterns; i++) {
-		if (   patterns[i][0] != '!'
-		    && !fnmatch (patterns[i], str, 0))
-			return TRUE;
-	}
-
-	return FALSE;
-}
-
 /*****************************************************************************/
 
 char *
@@ -2056,7 +2036,7 @@ typedef struct
 	NMSettingDiffResult diff_result;
 } LogConnectionSettingItem;
 
-static int
+static gint
 _log_connection_sort_hashes_fcn (gconstpointer a, gconstpointer b)
 {
 	const LogConnectionSettingData *v1 = a;
@@ -2099,7 +2079,7 @@ _log_connection_sort_hashes (NMConnection *connection, NMConnection *diff_base,
 	return sorted_hashes;
 }
 
-static int
+static gint
 _log_connection_sort_names_fcn (gconstpointer a, gconstpointer b)
 {
 	const LogConnectionSettingItem *v1 = a;
@@ -2561,6 +2541,262 @@ nm_utils_machine_id_read (void)
 
 /*****************************************************************************/
 
+_nm_printf (3, 4)
+static int
+_get_contents_error (GError **error, int errsv, const char *format, ...)
+{
+	if (errsv < 0)
+		errsv = -errsv;
+	else if (!errsv)
+		errsv = errno;
+
+	if (error) {
+		char *msg;
+		va_list args;
+
+		va_start (args, format);
+		msg = g_strdup_vprintf (format, args);
+		va_end (args);
+		g_set_error (error,
+		             G_FILE_ERROR,
+		             g_file_error_from_errno (errsv),
+		             "%s: %s",
+		             msg, g_strerror (errsv));
+		g_free (msg);
+	}
+	return -errsv;
+}
+
+/**
+ * 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.
+ * @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
+ *   file is larger, reading will fail. Set to zero to use
+ *   a very large default.
+ *
+ *   WARNING: @max_length is here to avoid a crash for huge/unlimited files.
+ *   For example, stat(/sys/class/net/enp0s25/ifindex) gives a filesize of
+ *   4K, although the actual real is small. @max_length is the memory
+ *   allocated in the process of reading the file, thus it must be at least
+ *   the size reported by fstat.
+ *   If you set it to 1K, read will fail because fstat() claims the
+ *   file is larger.
+ *
+ * @contents: the output buffer with the file read. It is always
+ *   NUL terminated. The buffer is at most @max_length long, including
+ *  the NUL byte. That is, it reads only files up to a length of
+ *  @max_length - 1 bytes.
+ * @length: optional output argument of the read file size.
+ *
+ * A reimplementation of g_file_get_contents() with a few differences:
+ *   - accepts an open fd, instead of a path name. This allows you to
+ *     use openat().
+ *   - limits the maxium filesize to max_length.
+ *
+ * Returns: a negative error code on failure.
+ */
+int
+nm_utils_fd_get_contents (int fd,
+                          gboolean close_fd,
+                          gsize max_length,
+                          char **contents,
+                          gsize *length,
+                          GError **error)
+{
+	nm_auto_close int fd_keeper = close_fd ? fd : -1;
+	struct stat stat_buf;
+	gs_free char *str = NULL;
+
+	g_return_val_if_fail (fd >= 0, -EINVAL);
+	g_return_val_if_fail (contents, -EINVAL);
+	g_return_val_if_fail (!error || !*error, -EINVAL);
+
+	if (fstat (fd, &stat_buf) < 0)
+		return _get_contents_error (error, 0, "failure during fstat");
+
+	if (!max_length) {
+		/* default to a very large size, but not extreme */
+		max_length = 2 * 1024 * 1024;
+	}
+
+	if (   stat_buf.st_size > 0
+	    && S_ISREG (stat_buf.st_mode)) {
+		const gsize n_stat = stat_buf.st_size;
+		ssize_t n_read;
+
+		if (n_stat > max_length - 1)
+			return _get_contents_error (error, EMSGSIZE, "file too large (%zu+1 bytes with maximum %zu bytes)", n_stat, max_length);
+
+		str = g_try_malloc (n_stat + 1);
+		if (!str)
+			return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu+1 bytes", n_stat);
+
+		n_read = nm_utils_fd_read_loop (fd, str, n_stat, TRUE);
+		if (n_read < 0)
+			return _get_contents_error (error, n_read, "error reading %zu bytes from file descriptor", n_stat);
+		str[n_read] = '\0';
+
+		if (n_read < n_stat) {
+			char *tmp;
+
+			tmp = g_try_realloc (str, n_read + 1);
+			if (!tmp)
+				return _get_contents_error (error, ENOMEM, "failure to reallocate buffer with %zu bytes", n_read + 1);
+			str = tmp;
+		}
+		NM_SET_OUT (length, n_read);
+	} else {
+		nm_auto_fclose FILE *f = NULL;
+		char buf[4096];
+		gsize n_have, n_alloc;
+		int fd2;
+
+		if (fd_keeper >= 0)
+			fd2 = nm_steal_fd (&fd_keeper);
+		else {
+			fd2 = fcntl (fd, F_DUPFD_CLOEXEC, 0);
+			if (fd2 < 0)
+				return _get_contents_error (error, 0, "error during dup");
+		}
+
+		if (!(f = fdopen (fd2, "r"))) {
+			nm_close (fd2);
+			return _get_contents_error (error, 0, "failure during fdopen");
+		}
+
+		n_have = 0;
+		n_alloc = 0;
+
+		while (!feof (f)) {
+			int errsv;
+			gsize n_read;
+
+			n_read = fread (buf, 1, sizeof (buf), f);
+			errsv = errno;
+			if (ferror (f))
+				return _get_contents_error (error, errsv, "error during fread");
+
+			if (   n_have > G_MAXSIZE - 1 - n_read
+			    || n_have + n_read + 1 > max_length) {
+				return _get_contents_error (error, EMSGSIZE, "file stream too large (%zu+1 bytes with maximum %zu bytes)",
+				                            (n_have > G_MAXSIZE - 1 - n_read) ? G_MAXSIZE : n_have + n_read,
+				                            max_length);
+			}
+
+			if (n_have + n_read + 1 >= n_alloc) {
+				char *tmp;
+
+				if (str) {
+					if (n_alloc >= max_length / 2)
+						n_alloc = max_length;
+					else
+						n_alloc *= 2;
+				} else
+					n_alloc = NM_MIN (n_read + 1, sizeof (buf));
+
+				tmp = g_try_realloc (str, n_alloc);
+				if (!tmp)
+					return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu bytes", n_alloc);
+				str = tmp;
+			}
+
+			memcpy (str + n_have, buf, n_read);
+			n_have += n_read;
+		}
+
+		if (n_alloc == 0)
+			str = g_new0 (gchar, 1);
+		else {
+			str[n_have] = '\0';
+			if (n_have + 1 < n_alloc) {
+				char *tmp;
+
+				tmp = g_try_realloc (str, n_have + 1);
+				if (!tmp)
+					return _get_contents_error (error, ENOMEM, "failure to truncate buffer to %zu bytes", n_have + 1);
+				str = tmp;
+			}
+		}
+
+		NM_SET_OUT (length, n_have);
+	}
+
+	*contents = g_steal_pointer (&str);
+	return 0;
+}
+
+/**
+ * nm_utils_file_get_contents:
+ * @dirfd: optional file descriptor to use openat(). If negative, use plain open().
+ * @filename: the filename to open. Possibly relative to @dirfd.
+ * @max_length: allocate at most @max_length bytes.
+ *   WARNING: see nm_utils_fd_get_contents() hint about @max_length.
+ * @contents: the output buffer with the file read. It is always
+ *   NUL terminated. The buffer is at most @max_length long, including
+ *  the NUL byte. That is, it reads only files up to a length of
+ *  @max_length - 1 bytes.
+ * @length: optional output argument of the read file size.
+ *
+ * A reimplementation of g_file_get_contents() with a few differences:
+ *   - accepts an @dirfd to open @filename relative to that path via openat().
+ *   - limits the maxium filesize to max_length.
+ *   - uses O_CLOEXEC on internal file descriptor
+ *
+ * Returns: a negative error code on failure.
+ */
+int
+nm_utils_file_get_contents (int dirfd,
+                            const char *filename,
+                            gsize max_length,
+                            char **contents,
+                            gsize *length,
+                            GError **error)
+{
+	int fd;
+	int errsv;
+
+	g_return_val_if_fail (filename && filename[0], -EINVAL);
+
+	if (dirfd >= 0) {
+		fd = openat (dirfd, filename, O_RDONLY | O_CLOEXEC);
+		if (fd < 0) {
+			errsv = errno;
+
+			g_set_error (error,
+			             G_FILE_ERROR,
+			             g_file_error_from_errno (errsv),
+			             "Failed to open file \"%s\" with openat: %s",
+			             filename,
+			             g_strerror (errsv));
+			return -errsv;
+		}
+	} else {
+		fd = open (filename, O_RDONLY | O_CLOEXEC);
+		if (fd < 0) {
+			errsv = errno;
+
+			g_set_error (error,
+			             G_FILE_ERROR,
+			             g_file_error_from_errno (errsv),
+			             "Failed to open file \"%s\": %s",
+			             filename,
+			             g_strerror (errsv));
+			return -errsv;
+		}
+	}
+	return nm_utils_fd_get_contents (fd,
+	                                 TRUE,
+	                                 max_length,
+	                                 contents,
+	                                 length,
+	                                 error);
+}
+
+/*****************************************************************************/
+
 static gboolean
 _secret_key_read (guint8 **out_secret_key,
                   gsize *out_key_len)
@@ -2687,7 +2923,6 @@ nm_utils_get_boot_id (void)
 		gs_free char *contents = NULL;
 
 		nm_utils_file_get_contents (-1, "/proc/sys/kernel/random/boot_id", 0,
-		                            NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
 		                            &contents, NULL, NULL);
 		if (contents) {
 			g_strstrip (contents);
@@ -2782,6 +3017,7 @@ nm_utils_get_ipv6_interface_identifier (NMLinkType link_type,
 		out_iid->id_u8[0] |= 0x02;
 		return TRUE;
 	case NM_LINK_TYPE_GRE:
+	case NM_LINK_TYPE_GRETAP:
 		/* Hardware address is the network-endian IPv4 address */
 		g_return_val_if_fail (hwaddr_len == 4, FALSE);
 		addr = * (guint32 *) hwaddr;
@@ -2791,11 +3027,6 @@ nm_utils_get_ipv6_interface_identifier (NMLinkType link_type,
 		out_iid->id_u8[3] = 0xFE;
 		memcpy (out_iid->id_u8 + 4, &addr, 4);
 		return TRUE;
-	case NM_LINK_TYPE_6LOWPAN:
-		/* The hardware address is already 64-bit. This is the case for
-		* IEEE 802.15.4 networks. */
-		memcpy (out_iid->id_u8, hwaddr, sizeof (out_iid->id_u8));
-		return TRUE;
 	default:
 		if (hwaddr_len == ETH_ALEN) {
 			/* Translate 48-bit MAC address to a 64-bit Modified EUI-64.  See
@@ -3430,8 +3661,8 @@ nm_utils_g_value_set_strv (GValue *value, GPtrArray *strings)
 /*****************************************************************************/
 
 static gboolean
-debug_key_matches (const char *key,
-                   const char *token,
+debug_key_matches (const gchar *key,
+                   const gchar *token,
                    guint        length)
 {
 	/* may not call GLib functions: see note in g_parse_debug_string() */
@@ -3733,12 +3964,116 @@ nm_utils_get_reverse_dns_domains_ip6 (const struct in6_addr *ip, guint8 plen, GP
 #undef N_SHIFT
 }
 
+/**
+ * Copied from GLib's g_file_set_contents() et al., but allows
+ * specifying a mode for the new file.
+ */
+gboolean
+nm_utils_file_set_contents (const gchar *filename,
+                            const gchar *contents,
+                            gssize length,
+                            mode_t mode,
+                            GError **error)
+{
+	gs_free char *tmp_name = NULL;
+	struct stat statbuf;
+	int errsv;
+	gssize s;
+	int fd;
+
+	g_return_val_if_fail (filename, FALSE);
+	g_return_val_if_fail (contents || !length, FALSE);
+	g_return_val_if_fail (!error || !*error, FALSE);
+	g_return_val_if_fail (length >= -1, FALSE);
+
+	if (length == -1)
+		length = strlen (contents);
+
+	tmp_name = g_strdup_printf ("%s.XXXXXX", filename);
+	fd = g_mkstemp_full (tmp_name, O_RDWR, mode);
+	if (fd < 0) {
+		errsv = errno;
+		g_set_error (error,
+		             G_FILE_ERROR,
+		             g_file_error_from_errno (errsv),
+		             "failed to create file %s: %s",
+		             tmp_name,
+		             g_strerror (errsv));
+		return FALSE;
+	}
+
+	while (length > 0) {
+		s = write (fd, contents, length);
+		if (s < 0) {
+			errsv = errno;
+			if (errsv == EINTR)
+				continue;
+
+			nm_close (fd);
+			unlink (tmp_name);
+
+			g_set_error (error,
+			             G_FILE_ERROR,
+			             g_file_error_from_errno (errsv),
+			             "failed to write to file %s: %s",
+			             tmp_name,
+			             g_strerror (errsv));
+			return FALSE;
+		}
+
+		g_assert (s <= length);
+
+		contents += s;
+		length -= s;
+	}
+
+	/* If the final destination exists and is > 0 bytes, we want to sync the
+	 * newly written file to ensure the data is on disk when we rename over
+	 * the destination. Otherwise if we get a system crash we can lose both
+	 * the new and the old file on some filesystems. (I.E. those that don't
+	 * guarantee the data is written to the disk before the metadata.)
+	 */
+	if (   lstat (filename, &statbuf) == 0
+	    && statbuf.st_size > 0
+	    && fsync (fd) != 0) {
+		errsv = errno;
+
+		nm_close (fd);
+		unlink (tmp_name);
+
+		g_set_error (error,
+		             G_FILE_ERROR,
+		             g_file_error_from_errno (errsv),
+		             "failed to fsync %s: %s",
+		             tmp_name,
+		             g_strerror (errsv));
+		return FALSE;
+	}
+
+	nm_close (fd);
+
+	if (rename (tmp_name, filename)) {
+		errsv = errno;
+		unlink (tmp_name);
+		g_set_error (error,
+		             G_FILE_ERROR,
+		             g_file_error_from_errno (errsv),
+		             "failed to rename %s to %s: %s",
+		             tmp_name,
+		             filename,
+		             g_strerror (errsv));
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 struct plugin_info {
 	char *path;
 	struct stat st;
 };
 
-static int
+static gint
 read_device_factory_paths_sort_fcn (gconstpointer a, gconstpointer b)
 {
 	const struct plugin_info *da = a;