summary refs log tree commit diff
path: root/src/main-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main-utils.c')
-rw-r--r--src/main-utils.c279
1 files changed, 279 insertions, 0 deletions
diff --git a/src/main-utils.c b/src/main-utils.c
new file mode 100644
index 00000000..0fa3644f
--- /dev/null
+++ b/src/main-utils.c
@@ -0,0 +1,279 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * 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.
+ *
+ * Copyright (C) 2004 - 2012 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gmodule.h>
+
+#include "main-utils.h"
+#include "nm-posix-signals.h"
+#include "nm-logging.h"
+
+static sigset_t signal_set;
+static gboolean *quit_early = NULL;
+
+/*
+ * Thread function waiting for signals and processing them.
+ * Wait for signals in signal set. The semantics of sigwait() require that all
+ * threads (including the thread calling sigwait()) have the signal masked, for
+ * reliable operation. Otherwise, a signal that arrives while this thread is
+ * not blocked in sigwait() might be delivered to another thread.
+ */
+static void *
+signal_handling_thread (void *arg)
+{
+	GMainLoop *main_loop = arg;
+	int signo;
+
+	while (1) {
+		sigwait (&signal_set, &signo);
+
+		switch (signo) {
+		case SIGINT:
+		case SIGTERM:
+			nm_log_info (LOGD_CORE, "caught signal %d, shutting down normally.", signo);
+			*quit_early = TRUE; /* for quitting before entering the main loop */
+			g_main_loop_quit (main_loop);
+			break;
+		case SIGHUP:
+			/* Reread config stuff like system config files, VPN service files, etc */
+			nm_log_info (LOGD_CORE, "caught signal %d, not supported yet.", signo);
+			break;
+		case SIGPIPE:
+			/* silently ignore signal */
+			break;
+		default:
+			nm_log_err (LOGD_CORE, "caught unexpected signal %d", signo);
+			break;
+		}
+    }
+    return NULL;
+}
+
+/**
+ * nm_main_utils_setup_signals:
+ * @main_loop: the #GMainLoop to quit when SIGINT or SIGTERM is received
+ * @quit_early: location of a variable that will be set to TRUE when
+ *   SIGINT or SIGTERM is received
+ *
+ * Mask the signals we are interested in and create a signal handling thread.
+ * Because all threads inherit the signal mask from their creator, all threads
+ * in the process will have the signals masked. That's why setup_signals() has
+ * to be called before creating other threads.
+ *
+ * Returns: %TRUE on success
+ */
+gboolean
+nm_main_utils_setup_signals (GMainLoop *main_loop, gboolean *quit_early_ptr)
+{
+	pthread_t signal_thread_id;
+	sigset_t old_sig_mask;
+	int status;
+
+	g_return_val_if_fail (main_loop != NULL, FALSE);
+	g_return_val_if_fail (quit_early_ptr != NULL, FALSE);
+
+	quit_early = quit_early_ptr;
+
+	sigemptyset (&signal_set);
+	sigaddset (&signal_set, SIGHUP);
+	sigaddset (&signal_set, SIGINT);
+	sigaddset (&signal_set, SIGTERM);
+	sigaddset (&signal_set, SIGPIPE);
+
+	/* Block all signals of interest. */
+	status = pthread_sigmask (SIG_BLOCK, &signal_set, &old_sig_mask);
+	if (status != 0) {
+		fprintf (stderr, _("Failed to set signal mask: %d"), status);
+		return FALSE;
+	}
+	/* Save original mask so that we could use it for child processes. */
+	nm_save_original_signal_mask (old_sig_mask);
+
+	/* Create the signal handling thread. */
+	status = pthread_create (&signal_thread_id, NULL, signal_handling_thread, main_loop);
+	if (status != 0) {
+		fprintf (stderr, _("Failed to create signal handling thread: %d"), status);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+gboolean
+nm_main_utils_write_pidfile (const char *pidfile)
+{
+	char pid[16];
+	int fd;
+	gboolean success = FALSE;
+
+	if ((fd = open (pidfile, O_CREAT|O_WRONLY|O_TRUNC, 00644)) < 0) {
+		fprintf (stderr, _("Opening %s failed: %s\n"), pidfile, strerror (errno));
+		return FALSE;
+	}
+
+	g_snprintf (pid, sizeof (pid), "%d", getpid ());
+	if (write (fd, pid, strlen (pid)) < 0)
+		fprintf (stderr, _("Writing to %s failed: %s\n"), pidfile, strerror (errno));
+	else
+		success = TRUE;
+
+	if (close (fd))
+		fprintf (stderr, _("Closing %s failed: %s\n"), pidfile, strerror (errno));
+
+	return success;
+}
+
+/**
+ * nm_main_utils_check_pidfile:
+ * @pidfile: the pid file
+ * @name: the process name
+ *
+ * Checks whether the pidfile already exists and contains PID of a running
+ * process.
+ *
+ * Returns: %TRUE if the specified pidfile already exists and contains the PID
+ *  of a running process named @name, or %FALSE if not
+ */
+gboolean
+nm_main_utils_check_pidfile (const char *pidfile, const char *name)
+{
+	char *contents = NULL;
+	gsize len = 0;
+	glong pid;
+	char *proc_cmdline = NULL;
+	gboolean nm_running = FALSE;
+	const char *process_name;
+
+	/* Setup runtime directory */
+	if (g_mkdir_with_parents (NMRUNDIR, 0755) != 0) {
+		nm_log_err (LOGD_CORE, "Cannot create '%s': %s", NMRUNDIR, strerror (errno));
+		exit (1);
+	}
+
+	if (!g_file_get_contents (pidfile, &contents, &len, NULL))
+		return FALSE;
+
+	if (len <= 0)
+		goto done;
+
+	errno = 0;
+	pid = strtol (contents, NULL, 10);
+	if (pid <= 0 || pid > 65536 || errno)
+		goto done;
+
+	g_free (contents);
+	proc_cmdline = g_strdup_printf ("/proc/%ld/cmdline", pid);
+	if (!g_file_get_contents (proc_cmdline, &contents, &len, NULL))
+		goto done;
+
+	process_name = strrchr (contents, '/');
+	if (process_name)
+		process_name++;
+	else
+		process_name = contents;
+	if (strcmp (process_name, name) == 0) {
+		/* Check that the process exists */
+		if (kill (pid, 0) == 0) {
+			fprintf (stderr, _("%s is already running (pid %ld)\n"), name, pid);
+			nm_running = TRUE;
+		}
+	}
+
+done:
+	g_free (proc_cmdline);
+	g_free (contents);
+	return nm_running;
+}
+
+gboolean
+nm_main_utils_early_setup (const char *progname,
+                           char **argv[],
+                           int *argc,
+                           GOptionEntry *options,
+                           GOptionEntry *more_options,
+                           const char *summary)
+{
+	GOptionContext *opt_ctx = NULL;
+	GError *error = NULL;
+	gboolean success = FALSE;
+	int i;
+
+	/* Make GIO ignore the remote VFS service; otherwise it tries to use the
+	 * session bus to contact the remote service, and NM shouldn't ever be
+	 * talking on the session bus.  See rh #588745
+	 */
+	setenv ("GIO_USE_VFS", "local", 1);
+
+	/*
+	 * Set the umask to 0022, which results in 0666 & ~0022 = 0644.
+	 * Otherwise, if root (or an su'ing user) has a wacky umask, we could
+	 * write out an unreadable resolv.conf.
+	 */
+	umask (022);
+
+	/* Ensure gettext() gets the right environment (bgo #666516) */
+	setlocale (LC_ALL, "");
+	textdomain (GETTEXT_PACKAGE);
+
+	if (getuid () != 0) {
+		fprintf (stderr, _("You must be root to run %s!\n"), progname);
+		exit (1);
+	}
+
+	for (i = 0; options[i].long_name; i++) {
+		if (!strcmp (options[i].long_name, "log-level"))
+			options[i].description = g_strdup_printf (options[i].description, nm_logging_all_levels_to_string ());
+		else if (!strcmp (options[i].long_name, "log-domains"))
+			options[i].description = g_strdup_printf (options[i].description, nm_logging_all_domains_to_string ());
+	}
+
+	/* Parse options */
+	opt_ctx = g_option_context_new (NULL);
+	g_option_context_set_translation_domain (opt_ctx, GETTEXT_PACKAGE);
+	g_option_context_set_ignore_unknown_options (opt_ctx, FALSE);
+	g_option_context_set_help_enabled (opt_ctx, TRUE);
+	g_option_context_add_main_entries (opt_ctx, options, NULL);
+	if (more_options)
+		g_option_context_add_main_entries (opt_ctx, more_options, NULL);
+	g_option_context_set_summary (opt_ctx, summary);
+
+	success = g_option_context_parse (opt_ctx, argc, argv, &error);
+	if (!success) {
+		fprintf (stderr, _("%s.  Please use --help to see a list of valid options.\n"),
+		         error->message);
+		g_clear_error (&error);
+	}
+	g_option_context_free (opt_ctx);
+
+	return success;
+}
+