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 private static void DoMain (string [] args
)
66 SystemInformation
.SetProcessName ("beagled-helper");
68 bool run_by_hand
= (Environment
.GetEnvironmentVariable ("BEAGLE_RUN_HELPER_BY_HAND") != null);
69 bool log_in_fg
= (Environment
.GetEnvironmentVariable ("BEAGLE_LOG_IN_THE_FOREGROUND_PLEASE") != null);
71 // FIXME: We always turn on full debugging output! We are still
72 // debugging this code, after all...
73 //bool debug = (Environment.GetEnvironmentVariable ("BEAGLE_DEBUG_FLAG_IS_SET") != null);
75 last_activity
= DateTime
.Now
;
77 Log
.Initialize (PathFinder
.LogDir
,
79 //debug ? LogLevel.Debug : LogLevel.Warn,
81 run_by_hand
|| log_in_fg
);
83 // Intentionally unset DISPLAY so that we can't connect
84 // to the X server and aren't influenced by it if it
85 // goes away. It's important to do this before
86 // Application.InitCheck(), since that's what makes the
88 //unsetenv ("DISPLAY");
90 SystemInformation
.XssInit (false);
92 SetupSignalHandlers ();
94 Shutdown
.ShutdownEvent
+= OnShutdown
;
96 main_loop
= new MainLoop ();
97 Shutdown
.RegisterMainLoop (main_loop
);
100 Logger
.Log
.Debug ("Starting messaging server");
101 bool server_has_been_started
= false;
103 server
= new Server ("socket-helper");
105 server_has_been_started
= true;
106 } catch (InvalidOperationException ex
) {
107 Logger
.Log
.Error ("Couldn't start server:");
108 Logger
.Log
.Error (ex
);
111 if (server_has_been_started
) {
112 // Set the IO priority to idle so we don't slow down the system
113 if (Environment
.GetEnvironmentVariable ("BEAGLE_EXERCISE_THE_DOG") == null) {
114 if (! IoPriority
.SetIdle ())
115 IoPriority
.SetIoPriority (7);
118 // Start the monitor thread, which keeps an eye on memory usage and idle time.
119 ExceptionHandlingThread
.Start (new ThreadStart (MemoryAndIdleMonitorWorker
));
121 // Start a thread that watches the daemon and begins a shutdown
123 ExceptionHandlingThread
.Start (new ThreadStart (DaemonMonitorWorker
));
125 //Application.Run ();
128 // If we palced our sockets in a temp directory, try to clean it up
129 // Note: this may fail because the daemon is still running
130 if (PathFinder
.GetRemoteStorageDir (false) != PathFinder
.StorageDir
) {
132 Directory
.Delete (PathFinder
.GetRemoteStorageDir (false));
133 } catch (IOException
) { }
138 public static void ReportActivity ()
140 last_activity
= DateTime
.Now
;
143 private static void MemoryAndIdleMonitorWorker ()
145 int vmrss_original
= SystemInformation
.VmRss
;
147 const double max_idle_time
= 30; // minutes
149 const double threshold
= 5.0;
150 const int max_request_count
= 0;
153 while (! Shutdown
.ShutdownRequested
) {
156 idle_time
= (DateTime
.Now
- last_activity
).TotalMinutes
;
157 if (idle_time
> max_idle_time
&& RemoteIndexerExecutor
.Count
> 0) {
158 Logger
.Log
.Debug ("No activity for {0:0.0} minutes, shutting down", idle_time
);
159 Shutdown
.BeginShutdown ();
163 // Check resident memory usage
164 int vmrss
= SystemInformation
.VmRss
;
165 double size
= vmrss
/ (double) vmrss_original
;
166 if (vmrss
!= last_vmrss
)
167 Logger
.Log
.Debug ("Helper Size: VmRSS={0:0.0} MB, size={1:0.00}, {2:0.0}%",
168 vmrss
/1024.0, size
, 100.0 * (size
- 1) / (threshold
- 1));
171 || (max_request_count
> 0 && RemoteIndexerExecutor
.Count
> max_request_count
)) {
172 if (RemoteIndexerExecutor
.Count
> 0) {
173 Logger
.Log
.Debug ("Process too big, shutting down!");
174 Shutdown
.BeginShutdown ();
177 // Paranoia: don't shut down if we haven't done anything yet
178 Logger
.Log
.Debug ("Deferring shutdown until we've actually done something.");
187 private static void DaemonMonitorWorker ()
189 string storage_dir
= PathFinder
.GetRemoteStorageDir (false);
191 if (storage_dir
== null) {
192 Logger
.Log
.Debug ("The daemon doesn't appear to have started");
193 Logger
.Log
.Debug ("Shutting down helper.");
194 Shutdown
.BeginShutdown ();
198 // FIXME: We shouldn't need to know the name of the daemon's socket.
200 socket_name
= Path
.Combine (storage_dir
, "socket");
204 socket
= new SNS
.Socket (SNS
.AddressFamily
.Unix
, SNS
.SocketType
.Stream
, 0);
205 socket
.Connect (new Mono
.Unix
.UnixEndPoint (socket_name
));
207 ArrayList socket_list
= new ArrayList ();
209 while (! Shutdown
.ShutdownRequested
) {
210 socket_list
.Add (socket
);
211 SNS
.Socket
.Select (socket_list
, null, null, 1000000); // 1000000 microseconds = 1 second
212 if (socket_list
.Count
!= 0) {
213 Logger
.Log
.Debug ("The daemon appears to have gone away.");
214 Logger
.Log
.Debug ("Shutting down helper.");
215 Shutdown
.BeginShutdown ();
218 } catch (SNS
.SocketException
) {
219 Logger
.Log
.Debug ("Caught a SocketException while trying to monitor the daemon");
220 Logger
.Log
.Debug ("Shutting down");
221 Shutdown
.BeginShutdown ();
225 /////////////////////////////////////////////////////////////////////////////
227 private static void SetupSignalHandlers ()
229 // Force OurSignalHandler to be JITed
230 OurSignalHandler (-1);
232 // Set up our signal handler
233 Mono
.Unix
.Native
.Stdlib
.signal (Mono
.Unix
.Native
.Signum
.SIGINT
, OurSignalHandler
);
234 Mono
.Unix
.Native
.Stdlib
.signal (Mono
.Unix
.Native
.Signum
.SIGTERM
, OurSignalHandler
);
235 Mono
.Unix
.Native
.Stdlib
.signal (Mono
.Unix
.Native
.Signum
.SIGUSR1
, OurSignalHandler
);
238 Mono
.Unix
.Native
.Stdlib
.signal (Mono
.Unix
.Native
.Signum
.SIGPIPE
, Mono
.Unix
.Native
.Stdlib
.SIG_IGN
);
241 // Our handler triggers an orderly shutdown when it receives a signal.
242 // However, this can be annoying if the process gets wedged during
243 // shutdown. To deal with that case, we make a note of the time when
244 // the first signal comes in, and we allow signals to unconditionally
245 // kill the process after 5 seconds have passed.
246 private static DateTime signal_time
= DateTime
.MinValue
;
247 private static void OurSignalHandler (int signal
)
249 // This allows us to call OurSignalHandler w/o doing anything.
250 // We want to call it once to ensure that it is pre-JITed.
254 // Set shutdown flag to true so that other threads can stop initializing
255 if ((Mono
.Unix
.Native
.Signum
) signal
!= Mono
.Unix
.Native
.Signum
.SIGUSR1
)
256 Shutdown
.ShutdownRequested
= true;
258 // Do all signal handling work in the main loop and not in the signal handler.
259 GLib
.Idle
.Add (new GLib
.IdleHandler (delegate () { HandleSignal (signal); return false; }
));
262 private static void HandleSignal (int signal
)
264 Logger
.Log
.Debug ("Handling signal {0} ({1})", signal
, (Mono
.Unix
.Native
.Signum
) signal
);
266 // If we get SIGUSR1, turn the debugging level up.
267 if ((Mono
.Unix
.Native
.Signum
) signal
== Mono
.Unix
.Native
.Signum
.SIGUSR1
) {
268 LogLevel old_level
= Log
.Level
;
269 Log
.Level
= LogLevel
.Debug
;
270 Log
.Debug ("Moving from log level {0} to Debug", old_level
);
274 Logger
.Log
.Debug ("Initiating shutdown in response to signal.");
275 Shutdown
.BeginShutdown ();
278 private static void OnShutdown ()