Use .so and not .dll for library. Stupid me.
[beagle.git] / beagled / IndexHelper / IndexHelper.cs
blobf55ec0e0888693120fab65409bbe976cdda74c69
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 [DllImport ("libc")]
51 extern static private int unsetenv (string name);
53 public static void Main (string [] args)
55 try {
56 DoMain (args);
57 } catch (Exception ex) {
58 Logger.Log.Error ("Unhandled exception thrown. Exiting immediately.");
59 Logger.Log.Error (ex);
60 Environment.Exit (1);
64 [DllImport("libgobject-2.0.so")]
65 static extern void g_type_init ();
67 private static void DoMain (string [] args)
69 SystemInformation.SetProcessName ("beagled-helper");
71 bool run_by_hand = (Environment.GetEnvironmentVariable ("BEAGLE_RUN_HELPER_BY_HAND") != null);
72 bool log_in_fg = (Environment.GetEnvironmentVariable ("BEAGLE_LOG_IN_THE_FOREGROUND_PLEASE") != null);
74 // FIXME: We always turn on full debugging output! We are still
75 // debugging this code, after all...
76 //bool debug = (Environment.GetEnvironmentVariable ("BEAGLE_DEBUG_FLAG_IS_SET") != null);
78 last_activity = DateTime.Now;
80 Log.Initialize (PathFinder.LogDir,
81 "IndexHelper",
82 //debug ? LogLevel.Debug : LogLevel.Warn,
83 LogLevel.Debug,
84 run_by_hand || log_in_fg);
86 // Intentionally unset DISPLAY so that we can't connect
87 // to the X server and aren't influenced by it if it
88 // goes away. It's important to do this before
89 // Application.InitCheck(), since that's what makes the
90 // connection.
91 //unsetenv ("DISPLAY");
93 SystemInformation.XssInit (false);
95 // Initialize GObject type system
96 g_type_init ();
98 SetupSignalHandlers ();
100 Shutdown.ShutdownEvent += OnShutdown;
102 main_loop = new MainLoop ();
103 Shutdown.RegisterMainLoop (main_loop);
105 // Start the server
106 Logger.Log.Debug ("Starting messaging server");
107 bool server_has_been_started = false;
108 try {
109 server = new Server ("socket-helper");
110 server.Start ();
111 server_has_been_started = true;
112 } catch (InvalidOperationException ex) {
113 Logger.Log.Error ("Couldn't start server:");
114 Logger.Log.Error (ex);
117 if (server_has_been_started) {
118 // Set the IO priority to idle so we don't slow down the system
119 if (Environment.GetEnvironmentVariable ("BEAGLE_EXERCISE_THE_DOG") == null) {
120 if (! IoPriority.SetIdle ())
121 IoPriority.SetIoPriority (7);
124 // Start the monitor thread, which keeps an eye on memory usage and idle time.
125 ExceptionHandlingThread.Start (new ThreadStart (MemoryAndIdleMonitorWorker));
127 // Start a thread that watches the daemon and begins a shutdown
128 // if it terminates.
129 ExceptionHandlingThread.Start (new ThreadStart (DaemonMonitorWorker));
131 //Application.Run ();
132 main_loop.Run ();
134 // If we palced our sockets in a temp directory, try to clean it up
135 // Note: this may fail because the daemon is still running
136 if (PathFinder.GetRemoteStorageDir (false) != PathFinder.StorageDir) {
137 try {
138 Directory.Delete (PathFinder.GetRemoteStorageDir (false));
139 } catch (IOException) { }
144 public static void ReportActivity ()
146 last_activity = DateTime.Now;
149 private static void MemoryAndIdleMonitorWorker ()
151 int vmrss_original = SystemInformation.VmRss;
153 const double max_idle_time = 30; // minutes
155 const double threshold = 5.0;
156 const int max_request_count = 0;
157 int last_vmrss = 0;
159 while (! Shutdown.ShutdownRequested) {
161 double idle_time;
162 idle_time = (DateTime.Now - last_activity).TotalMinutes;
163 if (idle_time > max_idle_time && RemoteIndexerExecutor.Count > 0) {
164 Logger.Log.Debug ("No activity for {0:0.0} minutes, shutting down", idle_time);
165 Shutdown.BeginShutdown ();
166 return;
169 // Check resident memory usage
170 int vmrss = SystemInformation.VmRss;
171 double size = vmrss / (double) vmrss_original;
172 if (vmrss != last_vmrss)
173 Logger.Log.Debug ("Helper Size: VmRSS={0:0.0} MB, size={1:0.00}, {2:0.0}%",
174 vmrss/1024.0, size, 100.0 * (size - 1) / (threshold - 1));
175 last_vmrss = vmrss;
176 if (size > threshold
177 || (max_request_count > 0 && RemoteIndexerExecutor.Count > max_request_count)) {
178 if (RemoteIndexerExecutor.Count > 0) {
179 Logger.Log.Debug ("Process too big, shutting down!");
180 Shutdown.BeginShutdown ();
181 return;
182 } else {
183 // Paranoia: don't shut down if we haven't done anything yet
184 Logger.Log.Debug ("Deferring shutdown until we've actually done something.");
185 Thread.Sleep (1000);
187 } else {
188 Thread.Sleep (3000);
193 private static void DaemonMonitorWorker ()
195 string storage_dir = PathFinder.GetRemoteStorageDir (false);
197 if (storage_dir == null) {
198 Logger.Log.Debug ("The daemon doesn't appear to have started");
199 Logger.Log.Debug ("Shutting down helper.");
200 Shutdown.BeginShutdown ();
201 return;
204 // FIXME: We shouldn't need to know the name of the daemon's socket.
205 string socket_name;
206 socket_name = Path.Combine (storage_dir, "socket");
208 try {
209 SNS.Socket socket;
210 socket = new SNS.Socket (SNS.AddressFamily.Unix, SNS.SocketType.Stream, 0);
211 socket.Connect (new Mono.Unix.UnixEndPoint (socket_name));
213 ArrayList socket_list = new ArrayList ();
215 while (! Shutdown.ShutdownRequested) {
216 socket_list.Add (socket);
217 SNS.Socket.Select (socket_list, null, null, 1000000); // 1000000 microseconds = 1 second
218 if (socket_list.Count != 0) {
219 Logger.Log.Debug ("The daemon appears to have gone away.");
220 Logger.Log.Debug ("Shutting down helper.");
221 Shutdown.BeginShutdown ();
224 } catch (SNS.SocketException) {
225 Logger.Log.Debug ("Caught a SocketException while trying to monitor the daemon");
226 Logger.Log.Debug ("Shutting down");
227 Shutdown.BeginShutdown ();
231 /////////////////////////////////////////////////////////////////////////////
233 private static void SetupSignalHandlers ()
235 // Force OurSignalHandler to be JITed
236 OurSignalHandler (-1);
238 // Set up our signal handler
239 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGINT, OurSignalHandler);
240 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGTERM, OurSignalHandler);
241 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGUSR1, OurSignalHandler);
243 // Ignore SIGPIPE
244 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGPIPE, Mono.Unix.Native.Stdlib.SIG_IGN);
247 // Our handler triggers an orderly shutdown when it receives a signal.
248 // However, this can be annoying if the process gets wedged during
249 // shutdown. To deal with that case, we make a note of the time when
250 // the first signal comes in, and we allow signals to unconditionally
251 // kill the process after 5 seconds have passed.
252 private static DateTime signal_time = DateTime.MinValue;
253 private static void OurSignalHandler (int signal)
255 // This allows us to call OurSignalHandler w/o doing anything.
256 // We want to call it once to ensure that it is pre-JITed.
257 if (signal < 0)
258 return;
260 // Set shutdown flag to true so that other threads can stop initializing
261 if ((Mono.Unix.Native.Signum) signal != Mono.Unix.Native.Signum.SIGUSR1)
262 Shutdown.ShutdownRequested = true;
264 // Do all signal handling work in the main loop and not in the signal handler.
265 GLib.Idle.Add (new GLib.IdleHandler (delegate () { HandleSignal (signal); return false; }));
268 private static void HandleSignal (int signal)
270 Logger.Log.Debug ("Handling signal {0} ({1})", signal, (Mono.Unix.Native.Signum) signal);
272 // If we get SIGUSR1, turn the debugging level up.
273 if ((Mono.Unix.Native.Signum) signal == Mono.Unix.Native.Signum.SIGUSR1) {
274 LogLevel old_level = Log.Level;
275 Log.Level = LogLevel.Debug;
276 Log.Debug ("Moving from log level {0} to Debug", old_level);
277 return;
280 Logger.Log.Debug ("Initiating shutdown in response to signal.");
281 Shutdown.BeginShutdown ();
284 private static void OnShutdown ()
286 if (server != null)
287 server.Stop ();