remmina_utils.c 11.7 KB
Newer Older
1 2
/*
 * Remmina - The GTK+ Remote Desktop Client
3
 * Copyright (C) 2016-2018 Antenore Gatta, Giovanni Panozzo
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of portions of this program with the
 *  OpenSSL library under certain conditions as described in each
 *  individual source file, and distribute linked combinations
 *  including the two.
 *  You must obey the GNU General Public License in all respects
 *  for all of the code used other than OpenSSL. *  If you modify
 *  file(s) with this exception, you may extend this exception to your
 *  version of the file(s), but you are not obligated to do so. *  If you
 *  do not wish to do so, delete this exception statement from your
 *  version. *  If you delete this exception statement from all source
 *  files in the program, then also delete it here.
 *
 */

Antenore Gatta's avatar
Antenore Gatta committed
35
/**
Antenore Gatta's avatar
Antenore Gatta committed
36 37 38
 * General utility functions, non-GTK related.
 */

39 40 41 42 43
#include <stdlib.h>
#include <unistd.h>
#include <sys/utsname.h>

#include <glib.h>
44 45 46 47 48 49 50 51 52
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include "remmina/remmina_trace_calls.h"

/** Returns @c TRUE if @a ptr is @c NULL or @c *ptr is @c FALSE. */
#define EMPTY(ptr) \
	(!(ptr) || !*(ptr))

53 54
struct utsname u;

55 56 57 58 59 60 61 62
/* Copyright (C) 1998 VMware, Inc. All rights reserved.
 * Some of the code in this file is taken from the VMware open client.
 */
typedef struct lsb_distro_info {
	gchar *name;
	gchar *scanstring;
} LSBDistroInfo;

Giovanni Panozzo's avatar
Giovanni Panozzo committed
63
/*
64
static LSBDistroInfo lsbFields[] = {
Antenore Gatta's avatar
Antenore Gatta committed
65 66 67 68 69
	{ "DISTRIB_ID=",	  "DISTRIB_ID=%s"		},
	{ "DISTRIB_RELEASE=",	  "DISTRIB_RELEASE=%s"		},
	{ "DISTRIB_CODENAME=",	  "DISTRIB_CODENAME=%s"		},
	{ "DISTRIB_DESCRIPTION=", "DISTRIB_DESCRIPTION=%s"	},
	{ NULL,			  NULL				},
70
};
Giovanni Panozzo's avatar
Giovanni Panozzo committed
71
*/
72 73 74 75 76 77 78

typedef struct distro_info {
	gchar *name;
	gchar *filename;
} DistroInfo;

static DistroInfo distroArray[] = {
Antenore Gatta's avatar
Antenore Gatta committed
79 80 81 82 83 84
	{ "RedHat",		"/etc/redhat-release"		},
	{ "RedHat",		"/etc/redhat_version"		},
	{ "Sun",		"/etc/sun-release"		},
	{ "SuSE",		"/etc/SuSE-release"		},
	{ "SuSE",		"/etc/novell-release"		},
	{ "SuSE",		"/etc/sles-release"		},
85
	{ "SuSE",		"/etc/os-release"		},
Antenore Gatta's avatar
Antenore Gatta committed
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
	{ "Debian",		"/etc/debian_version"		},
	{ "Debian",		"/etc/debian_release"		},
	{ "Ubuntu",		"/etc/lsb-release"		},
	{ "Mandrake",		"/etc/mandrake-release"		},
	{ "Mandriva",		"/etc/mandriva-release"		},
	{ "Mandrake",		"/etc/mandrakelinux-release"	},
	{ "TurboLinux",		"/etc/turbolinux-release"	},
	{ "Fedora Core",	"/etc/fedora-release"		},
	{ "Gentoo",		"/etc/gentoo-release"		},
	{ "Novell",		"/etc/nld-release"		},
	{ "Annvix",		"/etc/annvix-release"		},
	{ "Arch",		"/etc/arch-release"		},
	{ "Arklinux",		"/etc/arklinux-release"		},
	{ "Aurox",		"/etc/aurox-release"		},
	{ "BlackCat",		"/etc/blackcat-release"		},
	{ "Cobalt",		"/etc/cobalt-release"		},
	{ "Conectiva",		"/etc/conectiva-release"	},
	{ "Immunix",		"/etc/immunix-release"		},
	{ "Knoppix",		"/etc/knoppix_version"		},
	{ "Linux-From-Scratch", "/etc/lfs-release"		},
	{ "Linux-PPC",		"/etc/linuxppc-release"		},
	{ "MkLinux",		"/etc/mklinux-release"		},
	{ "PLD",		"/etc/pld-release"		},
	{ "Slackware",		"/etc/slackware-version"	},
	{ "Slackware",		"/etc/slackware-release"	},
	{ "SMEServer",		"/etc/e-smith-release"		},
	{ "Solaris",		"/etc/release"			},
Philipp's avatar
Philipp committed
113
	{ "Solus",		"/etc/solus-release"		},
Antenore Gatta's avatar
Antenore Gatta committed
114 115 116 117 118 119
	{ "Tiny Sofa",		"/etc/tinysofa-release"		},
	{ "UltraPenguin",	"/etc/ultrapenguin-release"	},
	{ "UnitedLinux",	"/etc/UnitedLinux-release"	},
	{ "VALinux",		"/etc/va-release"		},
	{ "Yellow Dog",		"/etc/yellowdog-release"	},
	{ NULL,			NULL				},
120 121
};

122 123
gint remmina_utils_strpos(const gchar *haystack, const gchar *needle)
{
124
	TRACE_CALL(__func__);
125 126
	const gchar *sub;

127
	if (!*needle)
128 129 130
		return -1;

	sub = strstr(haystack, needle);
131
	if (!sub)
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
		return -1;

	return sub - haystack;
}

/* end can be -1 for haystack->len.
 * returns: position of found text or -1.
 * (C) Taken from geany */
gint remmina_utils_string_find(GString *haystack, gint start, gint end, const gchar *needle)
{
	TRACE_CALL(__func__);
	gint pos;

	g_return_val_if_fail(haystack != NULL, -1);
	if (haystack->len == 0)
		return -1;

	g_return_val_if_fail(start >= 0, -1);
	if (start >= (gint)haystack->len)
		return -1;

	g_return_val_if_fail(!EMPTY(needle), -1);

	if (end < 0)
		end = haystack->len;

	pos = remmina_utils_strpos(haystack->str + start, needle);
	if (pos == -1)
		return -1;

	pos += start;
	if (pos >= end)
		return -1;
	return pos;
}

/* Replaces @len characters from offset @a pos.
 * len can be -1 to replace the remainder of @a str.
 * returns: pos + strlen(replace).
 * (C) Taken from geany */
gint remmina_utils_string_replace(GString *str, gint pos, gint len, const gchar *replace)
{
174
	TRACE_CALL(__func__);
175
	g_string_erase(str, pos, len);
176
	if (replace) {
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
		g_string_insert(str, pos, replace);
		pos += strlen(replace);
	}
	return pos;
}

/**
 * Replaces all occurrences of @a needle in @a haystack with @a replace.
 *
 * @param haystack The input string to operate on. This string is modified in place.
 * @param needle The string which should be replaced.
 * @param replace The replacement for @a needle.
 *
 * @return Number of replacements made.
 **/
guint remmina_utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
{
194
	TRACE_CALL(__func__);
195 196 197 198
	guint count = 0;
	gint pos = 0;
	gsize needle_length = strlen(needle);

199
	while (1) {
200 201 202 203 204 205 206 207 208 209 210
		pos = remmina_utils_string_find(haystack, pos, -1, needle);

		if (pos == -1)
			break;

		pos = remmina_utils_string_replace(haystack, pos, needle_length, replace);
		count++;
	}
	return count;
}

211 212 213 214 215 216
/**
 * Strip \n, \t and \" from a given string.
 * This function is particulary useful with g_spawn_command_line_sync that does
 * not strip control characters from the output.
 * @warning the result should be freed.
 * @param a string.
Giovanni Panozzo's avatar
Giovanni Panozzo committed
217
 * @return a newly allocated copy of string cleaned by \t, \n and \"
218 219 220
 */
gchar *remmina_utils_string_strip(const gchar *s)
{
Giovanni Panozzo's avatar
Giovanni Panozzo committed
221
	gchar *p = g_malloc(strlen(s) + 1);
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236

	if (p) {
		gchar *p2 = p;
		while (*s != '\0') {
			if (*s != '\t' && *s != '\n' && *s != '\"') {
				*p2++ = *s++;
			} else {
				++s;
			}
		}
		*p2 = '\0';
	}
	return p;
}

237 238
/** OS related functions */

239
/**
240
 * remmina_utils_read_distrofile.
241 242 243 244 245 246 247
 *
 * Look for a distro version file /etc/xxx-release.
 * Once found, read the file in and figure out which distribution.
 *
 * @param filename The file path of a Linux distribution release file.
 * @param distroSize The size of the distribition name.
 * @param distro The full distro name.
248
 * @return Returns a string containing distro information verbatium from /etc/xxx-release (distro). Use g_free to free the string.
249 250
 *
 */
251
static gchar* remmina_utils_read_distrofile(gchar *filename)
252 253 254
{
	TRACE_CALL(__func__);
	gsize file_sz;
255 256 257
	struct stat st;
	gchar *distro_desc = NULL;
	GError *err = NULL;
258

259 260 261
	if (g_stat(filename, &st) == -1) {
		g_debug("%s: could not stat the file %s\n", __func__, filename);
		return NULL;
262 263
	}

Giovanni Panozzo's avatar
Giovanni Panozzo committed
264 265 266 267
	g_debug("%s: File %s is %lu bytes long\n", __func__, filename, st.st_size);
	if (st.st_size > 131072)
		return NULL;

268 269 270 271
	if (!g_file_get_contents(filename, &distro_desc, &file_sz, &err)) {
		g_debug("%s: could not get the file content%s: %s\n", __func__, filename, err->message);
		g_error_free(err);
		return NULL;
272 273 274
	}

	if (file_sz == 0) {
275 276
		g_debug("%s: Cannot work with empty file.\n", __FUNCTION__);
		return NULL;
277 278
	}

279 280
	g_debug("%s: Distro description %s\n", __func__, distro_desc);
	return distro_desc;
281 282
}

283 284 285 286
/**
 * Return the OS name as in "uname -s".
 * @return The OS name or NULL.
 */
287
const gchar* remmina_utils_get_kernel_name()
288 289 290 291 292 293
{
	TRACE_CALL(__func__);
	if (u.sysname)
		return u.sysname;
}

294
const gchar* remmina_utils_get_kernel_release()
295 296 297 298 299 300 301 302 303 304 305 306 307 308
/**
 * Return the OS version as in "uname -r".
 * @return The OS release or NULL.
 */
{
	TRACE_CALL(__func__);
	if (u.release)
		return u.release;
}

/**
 * Return the machine hardware name as in "uname -m".
 * @return The machine hardware name or NULL.
 */
309
const gchar* remmina_utils_get_kernel_arch()
310 311 312 313 314 315
{
	TRACE_CALL(__func__);
	if (u.machine)
		return u.machine;
}

316 317
/**
 * Print the Distributor as specified by the lsb_release command.
Giovanni Panozzo's avatar
Giovanni Panozzo committed
318
 * @return the distributor ID string or NULL. Caller must free it with g_free().
319
 */
Giovanni Panozzo's avatar
Giovanni Panozzo committed
320
gchar* remmina_utils_get_lsb_id()
321 322 323
{
	TRACE_CALL(__func__);
	gchar *lsb_id = NULL;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
324
	if (g_spawn_command_line_sync("/usr/bin/lsb_release -si", &lsb_id, NULL, NULL, NULL)) {
Giovanni Panozzo's avatar
Giovanni Panozzo committed
325
		return lsb_id;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
326
	}
327 328 329 330 331
	return NULL;
}

/**
 * Print the Distribution description as specified by the lsb_release command.
Giovanni Panozzo's avatar
Giovanni Panozzo committed
332
 * @return the Distribution description string or NULL. Caller must free it with g_free().
333
 */
Giovanni Panozzo's avatar
Giovanni Panozzo committed
334
gchar* remmina_utils_get_lsb_description()
335 336 337
{
	TRACE_CALL(__func__);
	gchar *lsb_description = NULL;
338 339 340
	GError *err = NULL;

	if (g_spawn_command_line_sync("/usr/bin/lsb_release -sd", &lsb_description, NULL, NULL, &err)) {
Giovanni Panozzo's avatar
Giovanni Panozzo committed
341
		return lsb_description;
342 343 344 345 346
	}else {
		g_debug("%s: could not execute lsb_release %s\n", __func__, err->message);
		g_error_free(err);
	}
	g_debug("%s: lsb_release %s\n", __func__, lsb_description);
347 348 349 350 351
	return NULL;
}

/**
 * Print the Distribution release name as specified by the lsb_release command.
Giovanni Panozzo's avatar
Giovanni Panozzo committed
352
 * @return the Distribution release name string or NULL. Caller must free it with g_free().
353
 */
Giovanni Panozzo's avatar
Giovanni Panozzo committed
354
gchar* remmina_utils_get_lsb_release()
355 356 357
{
	TRACE_CALL(__func__);
	gchar *lsb_release = NULL;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
358
	if (g_spawn_command_line_sync("/usr/bin/lsb_release -sr", &lsb_release, NULL, NULL, NULL)) {
Giovanni Panozzo's avatar
Giovanni Panozzo committed
359
		return lsb_release;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
360
	}
361 362 363 364 365
	return NULL;
}

/**
 * Print the Distribution codename as specified by the lsb_release command.
Giovanni Panozzo's avatar
Giovanni Panozzo committed
366
 * @return the codename string or NULL. Caller must free it with g_free().
367
 */
Giovanni Panozzo's avatar
Giovanni Panozzo committed
368
gchar* remmina_utils_get_lsb_codename()
369 370 371
{
	TRACE_CALL(__func__);
	gchar *lsb_codename = NULL;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
372
	if (g_spawn_command_line_sync("/usr/bin/lsb_release -sc", &lsb_codename, NULL, NULL, NULL)) {
Giovanni Panozzo's avatar
Giovanni Panozzo committed
373
		return lsb_codename;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
374
	}
375 376 377
	return NULL;
}

378 379 380
/**
 * Print the distribution description if found.
 * Test each known distribution specific information file and print it's content.
Giovanni Panozzo's avatar
Giovanni Panozzo committed
381
 * @return a string or NULL. Caller must free it with g_free().
382
 */
Giovanni Panozzo's avatar
Giovanni Panozzo committed
383
GHashTable* remmina_utils_get_etc_release()
384 385
{
	TRACE_CALL(__func__);
Giovanni Panozzo's avatar
Giovanni Panozzo committed
386
	gchar *etc_release = NULL;
387
	gint i;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
388 389 390
	GHashTable *r;

	r = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
391 392 393

	for (i = 0; distroArray[i].filename != NULL; i++) {
		g_debug("%s: File %s\n", __func__, distroArray[i].filename);
Giovanni Panozzo's avatar
Giovanni Panozzo committed
394 395 396 397 398 399 400
		etc_release = remmina_utils_read_distrofile(distroArray[i].filename);
		if (etc_release) {
			if (etc_release[0] != '\0') {
				g_debug("%s: Distro description %s\n", __func__, etc_release);
				g_hash_table_insert(r, distroArray[i].filename, etc_release);
			} else
				g_free(etc_release);
401 402
		}
	}
Giovanni Panozzo's avatar
Giovanni Panozzo committed
403
	return r;
404 405
}

406 407 408 409 410 411 412
/**
 * A sample function to show how use the other fOS releated functions.
 * @return a semicolon separated OS data like in "uname -srm".
 */
const gchar* remmina_utils_get_os_info()
{
	TRACE_CALL(__func__);
413
	gchar *kernel_string;
414 415 416 417

	if (uname(&u) == -1)
		g_print("uname:");

418 419 420 421 422 423
	kernel_string = g_strdup_printf("%s;%s;%s\n",
		remmina_utils_get_kernel_name(),
		remmina_utils_get_kernel_release(),
		remmina_utils_get_kernel_arch());
	if (!kernel_string || kernel_string[0] == '\0')
		kernel_string = g_strdup_printf("%s;%s;%s\n",
424 425 426
			"UNKNOWN",
			"UNKNOWN",
			"UNKNOWN");
427
	return kernel_string;
428 429
}