Home | History | Annotate | Download | only in restorecond
      1 /*
      2  * restorecond
      3  *
      4  * Copyright (C) 2006-2009 Red Hat
      5  * see file 'COPYING' for use and warranty information
      6  *
      7  * This program is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU General Public License as
      9  * published by the Free Software Foundation; either version 2 of
     10  * the License, or (at your option) any later version.
     11  *
     12  * This program is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15  * GNU General Public License for more details.
     16 .*
     17  * You should have received a copy of the GNU General Public License
     18  * along with this program; if not, write to the Free Software
     19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
     20  * 02111-1307  USA
     21  *
     22  * Authors:
     23  *   Dan Walsh <dwalsh (at) redhat.com>
     24  *
     25 */
     26 
     27 #define _GNU_SOURCE
     28 #include <sys/inotify.h>
     29 #include <errno.h>
     30 #include <stdio.h>
     31 #include <stdlib.h>
     32 #include <signal.h>
     33 #include <string.h>
     34 #include <unistd.h>
     35 #include <ctype.h>
     36 #include <sys/types.h>
     37 #include <sys/stat.h>
     38 #include <syslog.h>
     39 #include <limits.h>
     40 #include <fcntl.h>
     41 
     42 #include "restorecond.h"
     43 #include "stringslist.h"
     44 #include <glib.h>
     45 #ifdef HAVE_DBUS
     46 #include <dbus/dbus.h>
     47 #include <dbus/dbus-glib.h>
     48 #include <dbus/dbus-glib-lowlevel.h>
     49 
     50 static DBusHandlerResult signal_filter (DBusConnection *connection, DBusMessage *message, void *user_data);
     51 
     52 static const char *PATH="/org/selinux/Restorecond";
     53 //static const char *BUSNAME="org.selinux.Restorecond";
     54 static const char *INTERFACE="org.selinux.RestorecondIface";
     55 static const char *RULE="type='signal',interface='org.selinux.RestorecondIface'";
     56 
     57 static int local_lock_fd = -1;
     58 
     59 static DBusHandlerResult
     60 signal_filter (DBusConnection *connection  __attribute__ ((__unused__)), DBusMessage *message, void *user_data)
     61 {
     62   /* User data is the event loop we are running in */
     63   GMainLoop *loop = user_data;
     64 
     65   /* A signal from the bus saying we are about to be disconnected */
     66   if (dbus_message_is_signal
     67         (message, INTERFACE, "Stop")) {
     68 
     69       /* Tell the main loop to quit */
     70       g_main_loop_quit (loop);
     71       /* We have handled this message, don't pass it on */
     72       return DBUS_HANDLER_RESULT_HANDLED;
     73   }
     74   /* A Ping signal on the com.burtonini.dbus.Signal interface */
     75   else if (dbus_message_is_signal (message, INTERFACE, "Start")) {
     76     DBusError error;
     77     dbus_error_init (&error);
     78     g_print("Start received\n");
     79     return DBUS_HANDLER_RESULT_HANDLED;
     80   }
     81   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
     82 }
     83 
     84 static int dbus_server(GMainLoop *loop) {
     85     DBusConnection *bus;
     86     DBusError error;
     87     dbus_error_init (&error);
     88     bus = dbus_bus_get (DBUS_BUS_SESSION, &error);
     89     if (bus) {
     90 	dbus_connection_setup_with_g_main (bus, NULL);
     91 
     92 	/* listening to messages from all objects as no path is specified */
     93 	dbus_bus_add_match (bus, RULE, &error); // see signals from the given interfacey
     94 	dbus_connection_add_filter (bus, signal_filter, loop, NULL);
     95 	return 0;
     96     }
     97     return -1;
     98 }
     99 
    100 #endif
    101 #include <selinux/selinux.h>
    102 #include <sys/file.h>
    103 
    104 /* size of the event structure, not counting name */
    105 #define EVENT_SIZE  (sizeof (struct inotify_event))
    106 /* reasonable guess as to size of 1024 events */
    107 #define BUF_LEN        (1024 * (EVENT_SIZE + 16))
    108 
    109 static gboolean
    110 io_channel_callback
    111  (GIOChannel *source,
    112   GIOCondition condition,
    113   gpointer data __attribute__((__unused__)))
    114 {
    115 
    116   char buffer[BUF_LEN+1];
    117   gsize bytes_read;
    118   unsigned int i = 0;
    119 
    120   if (condition & G_IO_IN) {
    121     /* Data is available. */
    122     g_io_channel_read_chars
    123       (source, buffer,
    124        sizeof (buffer),
    125        &bytes_read, NULL);
    126 
    127     if (! bytes_read) {
    128 	    /* Sesssion/Terminal Ended */
    129 	    exit(0);
    130     }
    131 
    132     while (i < bytes_read) {
    133 	    struct inotify_event *event;
    134 	    event = (struct inotify_event *)&buffer[i];
    135 	    if (debug_mode)
    136 		    printf("wd=%d mask=%u cookie=%u len=%u\n",
    137 			   event->wd, event->mask,
    138 			   event->cookie, event->len);
    139 	    if (event->len)
    140 		    watch_list_find(event->wd, event->name);
    141 
    142 	    i += EVENT_SIZE + event->len;
    143     }
    144   }
    145 
    146   /* An error happened while reading
    147      the file. */
    148 
    149   if (condition & G_IO_NVAL)
    150     return FALSE;
    151 
    152   /* We have reached the end of the
    153      file. */
    154 
    155   if (condition & G_IO_HUP) {
    156     g_io_channel_shutdown (source, 0, NULL);
    157     exit(0);
    158     return FALSE;
    159   }
    160 
    161   /* Returning TRUE will make sure
    162      the callback remains associated
    163      to the channel. */
    164 
    165   return TRUE;
    166 }
    167 
    168 int start() {
    169 #ifdef HAVE_DBUS
    170 	DBusConnection *bus;
    171 	DBusError error;
    172 	DBusMessage *message;
    173 
    174 	/* Get a connection to the session bus */
    175 	dbus_error_init (&error);
    176 	bus = dbus_bus_get (DBUS_BUS_SESSION, &error);
    177 	if (!bus) {
    178 		if (debug_mode)
    179 			g_warning ("Failed to connect to the D-BUS daemon: %s", error.message);
    180 		dbus_error_free (&error);
    181 		return 1;
    182 	}
    183 
    184 
    185 	/* Create a new signal "Start" on the interface,
    186 	 * from the object  */
    187 	message = dbus_message_new_signal (PATH,
    188 					   INTERFACE, "Start");
    189 	/* Send the signal */
    190 	dbus_connection_send (bus, message, NULL);
    191 	/* Free the signal now we have finished with it */
    192 	dbus_message_unref (message);
    193 #endif /* HAVE_DBUS */
    194 	return 0;
    195 }
    196 
    197 static int local_server(void) {
    198 	// ! dbus, run as local service
    199 	char *ptr=NULL;
    200 	if (asprintf(&ptr, "%s/.restorecond", homedir) < 0) {
    201 		if (debug_mode)
    202 			perror("asprintf");
    203 		return -1;
    204 	}
    205 	local_lock_fd = open(ptr, O_CREAT | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, S_IRUSR | S_IWUSR);
    206 	if (debug_mode)
    207 		g_warning ("Lock file: %s", ptr);
    208 
    209 	free(ptr);
    210 	if (local_lock_fd < 0) {
    211 		if (debug_mode)
    212 			perror("open");
    213 		return -1;
    214 	}
    215 	if (flock(local_lock_fd, LOCK_EX | LOCK_NB) < 0) {
    216 		close(local_lock_fd);
    217 		if (debug_mode)
    218 			perror("flock");
    219 		return -1;
    220 	}
    221 	/* watch for stdin/terminal going away */
    222 	GIOChannel *in = g_io_channel_unix_new(0);
    223 	g_io_add_watch_full( in,
    224 			     G_PRIORITY_HIGH,
    225 			     G_IO_IN|G_IO_ERR|G_IO_HUP,
    226 			     io_channel_callback, NULL, NULL);
    227 
    228 	return 0;
    229 }
    230 
    231 static void end_local_server(void) {
    232 	if (local_lock_fd >= 0)
    233 		close(local_lock_fd);
    234 	local_lock_fd = -1;
    235 }
    236 
    237 int server(int master_fd, const char *watch_file) {
    238     GMainLoop *loop;
    239 
    240     loop = g_main_loop_new (NULL, FALSE);
    241 
    242 #ifdef HAVE_DBUS
    243     if (dbus_server(loop) != 0)
    244 #endif /* HAVE_DBUS */
    245 	    if (local_server())
    246 		    goto end;
    247 
    248     read_config(master_fd, watch_file);
    249 
    250     if (watch_list_isempty()) goto end;
    251 
    252     set_matchpathcon_flags(MATCHPATHCON_NOTRANS);
    253 
    254     GIOChannel *c = g_io_channel_unix_new(master_fd);
    255 
    256     g_io_add_watch_full( c,
    257 			 G_PRIORITY_HIGH,
    258 			 G_IO_IN|G_IO_ERR|G_IO_HUP,
    259 			 io_channel_callback, NULL, NULL);
    260 
    261     g_main_loop_run (loop);
    262 
    263 end:
    264     end_local_server();
    265     g_main_loop_unref (loop);
    266     return 0;
    267 }
    268 
    269