4 // Copyright (C) 2005 Novell, Inc.
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
29 using System
.Collections
;
31 using SNS
= System
.Net
.Sockets
;
32 using System
.Runtime
.InteropServices
;
33 using System
.Threading
;
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
;
51 extern static private int unsetenv (string name
);
53 public static void Main (string [] args
)
57 } catch (Exception ex
) {
58 Logger
.Log
.Error ("Unhandled exception thrown. Exiting immediately.");
59 Logger
.Log
.Error (ex
);
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
,
82 //debug ? LogLevel.Debug : LogLevel.Warn,
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
91 //unsetenv ("DISPLAY");
93 SystemInformation
.XssInit (false);
95 // Initialize GObject type system
98 SetupSignalHandlers ();
100 Shutdown
.ShutdownEvent
+= OnShutdown
;
102 main_loop
= new MainLoop ();
103 Shutdown
.RegisterMainLoop (main_loop
);
106 Logger
.Log
.Debug ("Starting messaging server");
107 bool server_has_been_started
= false;
109 server
= new Server ("socket-helper");
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
129 ExceptionHandlingThread
.Start (new ThreadStart (DaemonMonitorWorker
));
131 //Application.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
) {
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;
159 while (! Shutdown
.ShutdownRequested
) {
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 ();
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));
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 ();
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.");
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 ();
204 // FIXME: We shouldn't need to know the name of the daemon's socket.
206 socket_name
= Path
.Combine (storage_dir
, "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
);
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.
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
);
280 Logger
.Log
.Debug ("Initiating shutdown in response to signal.");
281 Shutdown
.BeginShutdown ();
284 private static void OnShutdown ()