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@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
50static DBusHandlerResult signal_filter (DBusConnection *connection, DBusMessage *message, void *user_data);
51
52static const char *PATH="/org/selinux/Restorecond";
53//static const char *BUSNAME="org.selinux.Restorecond";
54static const char *INTERFACE="org.selinux.RestorecondIface";
55static const char *RULE="type='signal',interface='org.selinux.RestorecondIface'";
56
57static int local_lock_fd = -1;
58
59static DBusHandlerResult
60signal_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
84static 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
109static gboolean
110io_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
168int 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
197static 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
231static void end_local_server(void) {
232	if (local_lock_fd >= 0)
233		close(local_lock_fd);
234	local_lock_fd = -1;
235}
236
237int 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
263end:
264    end_local_server();
265    g_main_loop_unref (loop);
266    return 0;
267}
268
269