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 (ex
, "Unhandled exception thrown. Exiting immediately.");
63 [DllImport("libgobject-2.0.so.0")]
64 static extern void g_type_init ();
66 private static void DoMain (string [] args
)
68 SystemInformation
.SetProcessName ("beagled-helper");
70 bool run_by_hand
= (Environment
.GetEnvironmentVariable ("BEAGLE_RUN_HELPER_BY_HAND") != null);
71 bool log_in_fg
= (Environment
.GetEnvironmentVariable ("BEAGLE_LOG_IN_THE_FOREGROUND_PLEASE") != null);
73 // FIXME: We always turn on full debugging output! We are still
74 // debugging this code, after all...
75 //bool debug = (Environment.GetEnvironmentVariable ("BEAGLE_DEBUG_FLAG_IS_SET") != null);
77 last_activity
= DateTime
.Now
;
79 Log
.Initialize (PathFinder
.LogDir
,
81 //debug ? LogLevel.Debug : LogLevel.Warn,
83 run_by_hand
|| log_in_fg
);
85 // Intentionally unset DISPLAY so that we can't connect
86 // to the X server and aren't influenced by it if it
87 // goes away. It's important to do this before
88 // Application.InitCheck(), since that's what makes the
90 //unsetenv ("DISPLAY");
92 SystemInformation
.XssInit (false);
94 // Initialize GObject type system
99 SetupSignalHandlers ();
101 Shutdown
.ShutdownEvent
+= OnShutdown
;
103 main_loop
= new MainLoop ();
104 Shutdown
.RegisterMainLoop (main_loop
);
107 Logger
.Log
.Debug ("Starting messaging server");
108 bool server_has_been_started
= false;
110 server
= new Server ("socket-helper");
112 server_has_been_started
= true;
113 } catch (InvalidOperationException ex
) {
114 Logger
.Log
.Error (ex
, "Couldn't start server. Exiting immediately.");
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 IoPriority
.ReduceIoPriority ();
122 int prio
= Mono
.Unix
.Native
.Syscall
.nice (15);
125 Log
.Warn ("Unable to renice helper to +15");
127 Log
.Debug ("Reniced helper to +15");
129 Log
.Debug ("Helper was already niced to {0}, not renicing to +15", prio
);
131 Log
.Info ("BEAGLE_EXERCISE_THE_DOG is set");
133 // Start the monitor thread, which keeps an eye on memory usage and idle time.
134 ExceptionHandlingThread
.Start (new ThreadStart (MemoryAndIdleMonitorWorker
));
136 // Start a thread that watches the daemon and begins a shutdown
138 ExceptionHandlingThread
.Start (new ThreadStart (DaemonMonitorWorker
));
140 // Start the main loop
143 ExceptionHandlingThread
.JoinAllThreads ();
145 // If we placed our sockets in a temp directory, try to clean it up
146 // Note: this may fail because the daemon is still running
147 if (PathFinder
.GetRemoteStorageDir (false) != PathFinder
.StorageDir
) {
149 Directory
.Delete (PathFinder
.GetRemoteStorageDir (false));
150 } catch (IOException
) { }
153 Log
.Info ("Index helper process shut down cleanly.");
157 public static void ReportActivity ()
159 last_activity
= DateTime
.Now
;
162 private static void MemoryAndIdleMonitorWorker ()
164 int vmrss_original
= SystemInformation
.VmRss
;
166 const double max_idle_time
= 30; // minutes
168 const double threshold
= 5.0;
169 const int max_request_count
= 0;
172 while (! Shutdown
.ShutdownRequested
) {
175 idle_time
= (DateTime
.Now
- last_activity
).TotalMinutes
;
176 if (idle_time
> max_idle_time
&& RemoteIndexerExecutor
.Count
> 0) {
177 Logger
.Log
.Debug ("No activity for {0:0.0} minutes, shutting down", idle_time
);
178 Shutdown
.BeginShutdown ();
182 // Check resident memory usage
183 int vmrss
= SystemInformation
.VmRss
;
184 double size
= vmrss
/ (double) vmrss_original
;
185 if (vmrss
!= last_vmrss
)
186 Logger
.Log
.Debug ("Helper Size: VmRSS={0:0.0} MB, size={1:0.00}, {2:0.0}%",
187 vmrss
/1024.0, size
, 100.0 * (size
- 1) / (threshold
- 1));
190 || (max_request_count
> 0 && RemoteIndexerExecutor
.Count
> max_request_count
)) {
191 if (RemoteIndexerExecutor
.Count
> 0) {
192 Logger
.Log
.Debug ("Process too big, shutting down!");
193 Shutdown
.BeginShutdown ();
196 // Paranoia: don't shut down if we haven't done anything yet
197 Logger
.Log
.Debug ("Deferring shutdown until we've actually done something.");
206 private static void DaemonMonitorWorker ()
208 string storage_dir
= PathFinder
.GetRemoteStorageDir (false);
210 if (storage_dir
== null) {
211 Logger
.Log
.Debug ("The daemon doesn't appear to have started");
212 Logger
.Log
.Debug ("Shutting down helper.");
213 Shutdown
.BeginShutdown ();
217 // FIXME: We shouldn't need to know the name of the daemon's socket.
219 socket_name
= Path
.Combine (storage_dir
, "socket");
223 socket
= new SNS
.Socket (SNS
.AddressFamily
.Unix
, SNS
.SocketType
.Stream
, 0);
224 socket
.Connect (new Mono
.Unix
.UnixEndPoint (socket_name
));
226 ArrayList socket_list
= new ArrayList ();
228 while (! Shutdown
.ShutdownRequested
) {
229 socket_list
.Add (socket
);
230 SNS
.Socket
.Select (socket_list
, null, null, 1000000); // 1000000 microseconds = 1 second
231 if (socket_list
.Count
!= 0) {
232 Logger
.Log
.Debug ("The daemon appears to have gone away.");
233 Logger
.Log
.Debug ("Shutting down helper.");
234 Shutdown
.BeginShutdown ();
237 } catch (SNS
.SocketException
) {
238 Logger
.Log
.Debug ("Caught a SocketException while trying to monitor the daemon");
239 Logger
.Log
.Debug ("Shutting down");
240 Shutdown
.BeginShutdown ();
244 /////////////////////////////////////////////////////////////////////////////
246 private static void SetupSignalHandlers ()
248 // Force OurSignalHandler to be JITed
249 OurSignalHandler (-1);
251 // Set up our signal handler
252 Mono
.Unix
.Native
.Stdlib
.signal (Mono
.Unix
.Native
.Signum
.SIGINT
, OurSignalHandler
);
253 Mono
.Unix
.Native
.Stdlib
.signal (Mono
.Unix
.Native
.Signum
.SIGTERM
, OurSignalHandler
);
254 Mono
.Unix
.Native
.Stdlib
.signal (Mono
.Unix
.Native
.Signum
.SIGUSR1
, OurSignalHandler
);
257 Mono
.Unix
.Native
.Stdlib
.signal (Mono
.Unix
.Native
.Signum
.SIGPIPE
, Mono
.Unix
.Native
.Stdlib
.SIG_IGN
);
260 // Our handler triggers an orderly shutdown when it receives a signal.
261 // However, this can be annoying if the process gets wedged during
262 // shutdown. To deal with that case, we make a note of the time when
263 // the first signal comes in, and we allow signals to unconditionally
264 // kill the process after 5 seconds have passed.
265 private static DateTime signal_time
= DateTime
.MinValue
;
266 private static void OurSignalHandler (int signal
)
268 // This allows us to call OurSignalHandler w/o doing anything.
269 // We want to call it once to ensure that it is pre-JITed.
273 // Set shutdown flag to true so that other threads can stop initializing
274 if ((Mono
.Unix
.Native
.Signum
) signal
!= Mono
.Unix
.Native
.Signum
.SIGUSR1
)
275 Shutdown
.ShutdownRequested
= true;
277 // Do all signal handling work in the main loop and not in the signal handler.
278 GLib
.Idle
.Add (new GLib
.IdleHandler (delegate () { HandleSignal (signal); return false; }
));
281 private static void HandleSignal (int signal
)
283 Logger
.Log
.Debug ("Handling signal {0} ({1})", signal
, (Mono
.Unix
.Native
.Signum
) signal
);
285 // If we get SIGUSR1, turn the debugging level up.
286 if ((Mono
.Unix
.Native
.Signum
) signal
== Mono
.Unix
.Native
.Signum
.SIGUSR1
) {
287 LogLevel old_level
= Log
.Level
;
288 Log
.Level
= LogLevel
.Debug
;
289 Log
.Debug ("Moving from log level {0} to Debug", old_level
);
293 Logger
.Log
.Debug ("Initiating shutdown in response to signal.");
294 Shutdown
.BeginShutdown ();
297 private static void OnShutdown ()