cvsimport
[beagle.git] / beagled / IndexHelper / IndexHelper.cs
blobf46f39bd9fcced44a37d4947e2b2b9b50b4e8ee8
2 // IndexHelper.cs
3 //
4 // Copyright (C) 2005 Novell, Inc.
5 //
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in all
16 // copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 // SOFTWARE.
28 using System;
29 using System.Collections;
30 using System.IO;
31 using SNS = System.Net.Sockets;
32 using System.Runtime.InteropServices;
33 using System.Threading;
35 using GLib;
37 using Beagle.Daemon;
38 using Beagle.Util;
39 using Log = Beagle.Util.Log;
40 using Thread = System.Threading.Thread;
42 namespace Beagle.IndexHelper {
44 class IndexHelperTool {
45 private static MainLoop main_loop;
47 private static DateTime last_activity;
48 private static Server server;
50 // Current state with filtering.
51 public static Uri CurrentUri;
52 public static Filter CurrentFilter;
54 [DllImport ("libc")]
55 extern static private int unsetenv (string name);
57 public static void Main (string [] args)
59 try {
60 DoMain (args);
61 } catch (Exception ex) {
62 Logger.Log.Error (ex, "Unhandled exception thrown. Exiting immediately.");
63 Environment.Exit (1);
67 [DllImport("libgobject-2.0.so.0")]
68 static extern void g_type_init ();
70 private static void DoMain (string [] args)
72 SystemInformation.SetProcessName ("beagled-helper");
74 bool run_by_hand = (Environment.GetEnvironmentVariable ("BEAGLE_RUN_HELPER_BY_HAND") != null);
75 bool log_in_fg = (Environment.GetEnvironmentVariable ("BEAGLE_LOG_IN_THE_FOREGROUND_PLEASE") != null);
77 // FIXME: We always turn on full debugging output! We are still
78 // debugging this code, after all...
79 //bool debug = (Environment.GetEnvironmentVariable ("BEAGLE_DEBUG_FLAG_IS_SET") != null);
81 last_activity = DateTime.Now;
83 Log.Initialize (PathFinder.LogDir,
84 "IndexHelper",
85 //debug ? LogLevel.Debug : LogLevel.Warn,
86 LogLevel.Debug,
87 run_by_hand || log_in_fg);
89 // Intentionally unset DISPLAY so that we can't connect
90 // to the X server and aren't influenced by it if it
91 // goes away. It's important to do this before
92 // Application.InitCheck(), since that's what makes the
93 // connection.
94 //unsetenv ("DISPLAY");
96 SystemInformation.XssInit (false);
98 // Initialize GObject type system
99 g_type_init ();
101 Server.Init ();
103 SetupSignalHandlers ();
105 Shutdown.ShutdownEvent += OnShutdown;
107 main_loop = new MainLoop ();
108 Shutdown.RegisterMainLoop (main_loop);
110 // Start the server
111 Logger.Log.Debug ("Starting messaging server");
112 bool server_has_been_started = false;
113 try {
114 server = new Server ("socket-helper");
115 server.Start ();
116 server_has_been_started = true;
117 } catch (InvalidOperationException ex) {
118 Logger.Log.Error (ex, "Couldn't start server. Exiting immediately.");
121 if (server_has_been_started) {
122 // Set the IO priority to idle so we don't slow down the system
123 if (Environment.GetEnvironmentVariable ("BEAGLE_EXERCISE_THE_DOG") == null) {
124 IoPriority.ReduceIoPriority ();
126 int prio = Mono.Unix.Native.Syscall.nice (15);
128 if (prio < 0)
129 Log.Warn ("Unable to renice helper to +15");
130 else if (prio == 15)
131 Log.Debug ("Reniced helper to +15");
132 else
133 Log.Debug ("Helper was already niced to {0}, not renicing to +15", prio);
134 } else
135 Log.Info ("BEAGLE_EXERCISE_THE_DOG is set");
137 // Start the monitor thread, which keeps an eye on memory usage and idle time.
138 ExceptionHandlingThread.Start (new ThreadStart (MemoryAndIdleMonitorWorker));
140 // Start a thread that watches the daemon and begins a shutdown
141 // if it terminates.
142 ExceptionHandlingThread.Start (new ThreadStart (DaemonMonitorWorker));
144 // Start the main loop
145 main_loop.Run ();
147 ExceptionHandlingThread.JoinAllThreads ();
149 // If we placed our sockets in a temp directory, try to clean it up
150 // Note: this may fail because the daemon is still running
151 if (PathFinder.GetRemoteStorageDir (false) != PathFinder.StorageDir) {
152 try {
153 Directory.Delete (PathFinder.GetRemoteStorageDir (false));
154 } catch (IOException) { }
157 Log.Info ("Index helper process shut down cleanly.");
161 public static void ReportActivity ()
163 last_activity = DateTime.Now;
166 private static void MemoryAndIdleMonitorWorker ()
168 int vmrss_original = SystemInformation.VmRss;
170 const double max_idle_time = 30; // minutes
172 const double threshold = 5.0;
173 const int max_request_count = 0;
174 int last_vmrss = 0;
176 while (! Shutdown.ShutdownRequested) {
178 double idle_time;
179 idle_time = (DateTime.Now - last_activity).TotalMinutes;
180 if (idle_time > max_idle_time && RemoteIndexerExecutor.Count > 0) {
181 Logger.Log.Debug ("No activity for {0:0.0} minutes, shutting down", idle_time);
182 Shutdown.BeginShutdown ();
183 return;
186 // Check resident memory usage
187 int vmrss = SystemInformation.VmRss;
188 double size = vmrss / (double) vmrss_original;
189 if (vmrss != last_vmrss)
190 Logger.Log.Debug ("Helper Size: VmRSS={0:0.0} MB, size={1:0.00}, {2:0.0}%",
191 vmrss/1024.0, size, 100.0 * (size - 1) / (threshold - 1));
192 last_vmrss = vmrss;
193 if (size > threshold
194 || (max_request_count > 0 && RemoteIndexerExecutor.Count > max_request_count)) {
195 if (RemoteIndexerExecutor.Count > 0) {
196 Logger.Log.Debug ("Process too big, shutting down!");
197 Shutdown.BeginShutdown ();
198 return;
199 } else {
200 // Paranoia: don't shut down if we haven't done anything yet
201 Logger.Log.Debug ("Deferring shutdown until we've actually done something.");
202 Thread.Sleep (1000);
204 } else {
205 Thread.Sleep (3000);
210 private static void DaemonMonitorWorker ()
212 string storage_dir = PathFinder.GetRemoteStorageDir (false);
214 if (storage_dir == null) {
215 Logger.Log.Debug ("The daemon doesn't appear to have started");
216 Logger.Log.Debug ("Shutting down helper.");
217 Shutdown.BeginShutdown ();
218 return;
221 // FIXME: We shouldn't need to know the name of the daemon's socket.
222 string socket_name;
223 socket_name = Path.Combine (storage_dir, "socket");
225 try {
226 SNS.Socket socket;
227 socket = new SNS.Socket (SNS.AddressFamily.Unix, SNS.SocketType.Stream, 0);
228 socket.Connect (new Mono.Unix.UnixEndPoint (socket_name));
230 ArrayList socket_list = new ArrayList ();
232 while (! Shutdown.ShutdownRequested) {
233 socket_list.Add (socket);
234 SNS.Socket.Select (socket_list, null, null, 1000000); // 1000000 microseconds = 1 second
235 if (socket_list.Count != 0) {
236 Logger.Log.Debug ("The daemon appears to have gone away.");
237 Logger.Log.Debug ("Shutting down helper.");
238 Shutdown.BeginShutdown ();
241 } catch (SNS.SocketException) {
242 Logger.Log.Debug ("Caught a SocketException while trying to monitor the daemon");
243 Logger.Log.Debug ("Shutting down");
244 Shutdown.BeginShutdown ();
248 /////////////////////////////////////////////////////////////////////////////
250 private static void SetupSignalHandlers ()
252 // Force OurSignalHandler to be JITed
253 OurSignalHandler (-1);
255 // Set up our signal handler
256 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGINT, OurSignalHandler);
257 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGTERM, OurSignalHandler);
258 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGUSR1, OurSignalHandler);
259 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGUSR2, OurSignalHandler);
261 // Ignore SIGPIPE
262 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGPIPE, Mono.Unix.Native.Stdlib.SIG_IGN);
265 // Our handler triggers an orderly shutdown when it receives a signal.
266 // However, this can be annoying if the process gets wedged during
267 // shutdown. To deal with that case, we make a note of the time when
268 // the first signal comes in, and we allow signals to unconditionally
269 // kill the process after 5 seconds have passed.
270 private static DateTime signal_time = DateTime.MinValue;
271 private static void OurSignalHandler (int signal)
273 // This allows us to call OurSignalHandler w/o doing anything.
274 // We want to call it once to ensure that it is pre-JITed.
275 if (signal < 0)
276 return;
278 // SIGUSR1 and SIGUSR2 are informational signals. For other
279 // signals that we handle, set the shutdown flag to true so
280 // that other threads can stop initializing
281 if ((Mono.Unix.Native.Signum) signal != Mono.Unix.Native.Signum.SIGUSR1 &&
282 (Mono.Unix.Native.Signum) signal != Mono.Unix.Native.Signum.SIGUSR2)
283 Shutdown.ShutdownRequested = true;
285 // Do all signal handling work in the main loop and not in the signal handler.
286 GLib.Idle.Add (new GLib.IdleHandler (delegate () { HandleSignal (signal); return false; }));
289 private static void HandleSignal (int signal)
291 Log.Warn ("Handling signal {0} ({1})", signal, (Mono.Unix.Native.Signum) signal);
293 // If we get SIGUSR1, turn the debugging level up.
294 if ((Mono.Unix.Native.Signum) signal == Mono.Unix.Native.Signum.SIGUSR1) {
295 LogLevel old_level = Log.Level;
296 Log.Level = LogLevel.Debug;
297 Log.Debug ("Moving from log level {0} to Debug", old_level);
300 string span = StringFu.TimeSpanToString (DateTime.Now - last_activity);
302 if (CurrentUri == null)
303 Log.Warn ("Filtering status ({0} ago): no document is currently being filtered.", span);
304 else if (CurrentFilter == null)
305 Log.Warn ("Filtering status ({0} ago): determining filter for {1}", span, CurrentUri);
306 else
307 Log.Warn ("Filtering status ({0} ago): filtering {1} with {2}", span, CurrentUri, CurrentFilter);
309 // Don't shut down on information signals (SIGUSR1 and SIGUSR2)
310 if ((Mono.Unix.Native.Signum) signal == Mono.Unix.Native.Signum.SIGUSR1 ||
311 (Mono.Unix.Native.Signum) signal == Mono.Unix.Native.Signum.SIGUSR2)
312 return;
314 Logger.Log.Debug ("Initiating shutdown in response to signal.");
315 Shutdown.BeginShutdown ();
318 private static void OnShutdown ()
320 if (server != null)
321 server.Stop ();