/* * restorecond * * Copyright (C) 2006-2009 Red Hat * see file 'COPYING' for use and warranty information * * 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., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA * * Authors: * Dan Walsh * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "restorecond.h" #include "stringslist.h" #include #ifdef HAVE_DBUS #include #include #include static DBusHandlerResult signal_filter (DBusConnection *connection, DBusMessage *message, void *user_data); static const char *PATH="/org/selinux/Restorecond"; //static const char *BUSNAME="org.selinux.Restorecond"; static const char *INTERFACE="org.selinux.RestorecondIface"; static const char *RULE="type='signal',interface='org.selinux.RestorecondIface'"; static int local_lock_fd = -1; static DBusHandlerResult signal_filter (DBusConnection *connection __attribute__ ((__unused__)), DBusMessage *message, void *user_data) { /* User data is the event loop we are running in */ GMainLoop *loop = user_data; /* A signal from the bus saying we are about to be disconnected */ if (dbus_message_is_signal (message, INTERFACE, "Stop")) { /* Tell the main loop to quit */ g_main_loop_quit (loop); /* We have handled this message, don't pass it on */ return DBUS_HANDLER_RESULT_HANDLED; } /* A Ping signal on the com.burtonini.dbus.Signal interface */ else if (dbus_message_is_signal (message, INTERFACE, "Start")) { DBusError error; dbus_error_init (&error); g_print("Start received\n"); return DBUS_HANDLER_RESULT_HANDLED; } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static int dbus_server(GMainLoop *loop) { DBusConnection *bus; DBusError error; dbus_error_init (&error); bus = dbus_bus_get (DBUS_BUS_SESSION, &error); if (bus) { dbus_connection_setup_with_g_main (bus, NULL); /* listening to messages from all objects as no path is specified */ dbus_bus_add_match (bus, RULE, &error); // see signals from the given interfacey dbus_connection_add_filter (bus, signal_filter, loop, NULL); return 0; } return -1; } #endif #include #include /* size of the event structure, not counting name */ #define EVENT_SIZE (sizeof (struct inotify_event)) /* reasonable guess as to size of 1024 events */ #define BUF_LEN (1024 * (EVENT_SIZE + 16)) static gboolean io_channel_callback (GIOChannel *source, GIOCondition condition, gpointer data __attribute__((__unused__))) { char buffer[BUF_LEN+1]; gsize bytes_read; unsigned int i = 0; if (condition & G_IO_IN) { /* Data is available. */ g_io_channel_read_chars (source, buffer, sizeof (buffer), &bytes_read, NULL); if (! bytes_read) { /* Session/Terminal Ended */ exit(0); } while (i < bytes_read) { struct inotify_event *event; event = (struct inotify_event *)&buffer[i]; if (debug_mode) printf("wd=%d mask=%u cookie=%u len=%u\n", event->wd, event->mask, event->cookie, event->len); if (event->len) watch_list_find(event->wd, event->name); i += EVENT_SIZE + event->len; } } /* An error happened while reading the file. */ if (condition & G_IO_NVAL) return FALSE; /* We have reached the end of the file. */ if (condition & G_IO_HUP) { g_io_channel_shutdown (source, 0, NULL); exit(0); return FALSE; } /* Returning TRUE will make sure the callback remains associated to the channel. */ return TRUE; } int start() { #ifdef HAVE_DBUS DBusConnection *bus; DBusError error; DBusMessage *message; /* Get a connection to the session bus */ dbus_error_init (&error); bus = dbus_bus_get (DBUS_BUS_SESSION, &error); if (!bus) { if (debug_mode) g_warning ("Failed to connect to the D-BUS daemon: %s", error.message); dbus_error_free (&error); return 1; } /* Create a new signal "Start" on the interface, * from the object */ message = dbus_message_new_signal (PATH, INTERFACE, "Start"); /* Send the signal */ dbus_connection_send (bus, message, NULL); /* Free the signal now we have finished with it */ dbus_message_unref (message); #endif /* HAVE_DBUS */ return 0; } static int local_server(void) { // ! dbus, run as local service char *ptr=NULL; if (asprintf(&ptr, "%s/.restorecond", homedir) < 0) { if (debug_mode) perror("asprintf"); return -1; } local_lock_fd = open(ptr, O_CREAT | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, S_IRUSR | S_IWUSR); if (debug_mode) g_warning ("Lock file: %s", ptr); free(ptr); if (local_lock_fd < 0) { if (debug_mode) perror("open"); return -1; } if (flock(local_lock_fd, LOCK_EX | LOCK_NB) < 0) { close(local_lock_fd); if (debug_mode) perror("flock"); return -1; } /* watch for stdin/terminal going away */ GIOChannel *in = g_io_channel_unix_new(0); g_io_add_watch_full( in, G_PRIORITY_HIGH, G_IO_IN|G_IO_ERR|G_IO_HUP, io_channel_callback, NULL, NULL); return 0; } static void end_local_server(void) { if (local_lock_fd >= 0) close(local_lock_fd); local_lock_fd = -1; } int server(int master_fd, const char *watch_file) { GMainLoop *loop; loop = g_main_loop_new (NULL, FALSE); #ifdef HAVE_DBUS if (dbus_server(loop) != 0) #endif /* HAVE_DBUS */ if (local_server()) goto end; read_config(master_fd, watch_file); if (watch_list_isempty()) goto end; set_matchpathcon_flags(MATCHPATHCON_NOTRANS); GIOChannel *c = g_io_channel_unix_new(master_fd); g_io_add_watch_full( c, G_PRIORITY_HIGH, G_IO_IN|G_IO_ERR|G_IO_HUP, io_channel_callback, NULL, NULL); g_main_loop_run (loop); end: end_local_server(); g_main_loop_unref (loop); return 0; }