summaryrefslogtreecommitdiff
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 000000000..0fa3644f3
--- /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;
+}
+