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
.Threading
;
41 namespace Beagle
.IndexHelper
{
43 class IndexHelperTool
{
47 static void Main (string [] args
)
49 bool run_by_hand
= (Environment
.GetEnvironmentVariable ("BEAGLE_RUN_HELPER_BY_HAND") != null);
50 bool log_in_fg
= (Environment
.GetEnvironmentVariable ("BEAGLE_LOG_IN_THE_FOREGROUND_PLEASE") != null);
52 Logger
.DefaultLevel
= LogLevel
.Debug
;
54 Logger
.LogToFile (PathFinder
.LogDir
, "IndexHelper", run_by_hand
|| log_in_fg
);
56 Application
.InitCheck ("IndexHelper", ref args
);
58 SetupSignalHandlers ();
60 Shutdown
.ShutdownEvent
+= OnShutdown
;
63 Logger
.Log
.Debug ("Starting messaging server");
64 bool server_has_been_started
= false;
66 server
= new Server ("socket-helper");
68 server_has_been_started
= true;
69 } catch (InvalidOperationException ex
) {
70 Logger
.Log
.Error ("Couldn't start server:");
71 Logger
.Log
.Error (ex
);
74 if (server_has_been_started
) {
75 // Set the IO priority to idle so we don't slow down the system
76 if (Environment
.GetEnvironmentVariable ("BEAGLE_EXERCISE_THE_DOG") == null)
77 IoPriority
.SetIdle ();
79 // Start the monitor thread, which keeps an eye on memory usage.
80 ExceptionHandlingThread
.Start (new ThreadStart (MemoryMonitorWorker
));
82 // Start a thread that watches the daemon and begins a shutdown
84 ExceptionHandlingThread
.Start (new ThreadStart (DaemonMonitorWorker
));
88 // If we palced our sockets in a temp directory, try to clean it up
89 // Note: this may fail because the daemon is still running
90 if (PathFinder
.GetRemoteStorageDir (false) != PathFinder
.StorageDir
) {
92 Directory
.Delete (PathFinder
.GetRemoteStorageDir (false));
93 } catch (IOException
) { }
100 static void MemoryMonitorWorker ()
102 int vmrss_original
= SystemInformation
.VmRss
;
103 const double threshold
= 5.0;
104 const int max_request_count
= 0;
107 while (! Shutdown
.ShutdownRequested
) {
109 // Check resident memory usage
110 int vmrss
= SystemInformation
.VmRss
;
111 double size
= vmrss
/ (double) vmrss_original
;
112 if (vmrss
!= last_vmrss
)
113 Logger
.Log
.Debug ("Helper Size: VmRSS={0:0.0} MB, size={1:0.00}, {2:0.0}%",
114 vmrss
/1024.0, size
, 100.0 * (size
- 1) / (threshold
- 1));
117 || (max_request_count
> 0 && RemoteIndexerExecutor
.Count
> max_request_count
)) {
118 if (RemoteIndexerExecutor
.Count
> 0) {
119 Logger
.Log
.Debug ("Process too big, shutting down!");
120 Shutdown
.BeginShutdown ();
122 // Paranoia: don't shut down if we haven't done anything yet
123 Logger
.Log
.Debug ("Deferring shutdown until we've actually done something.");
132 static void DaemonMonitorWorker ()
134 string storage_dir
= PathFinder
.GetRemoteStorageDir (false);
136 if (storage_dir
== null) {
137 Logger
.Log
.Debug ("The daemon doesn't appear to have started");
138 Logger
.Log
.Debug ("Shutting down helper.");
139 Shutdown
.BeginShutdown ();
143 // FIXME: We shouldn't need to know the name of the daemon's socket.
145 socket_name
= Path
.Combine (storage_dir
, "socket");
149 socket
= new SNS
.Socket (SNS
.AddressFamily
.Unix
, SNS
.SocketType
.Stream
, 0);
150 socket
.Connect (new UnixEndPoint (socket_name
));
152 ArrayList socket_list
= new ArrayList ();
154 while (! Shutdown
.ShutdownRequested
) {
155 socket_list
.Add (socket
);
156 SNS
.Socket
.Select (socket_list
, null, null, 1000000); // 1000000 microseconds = 1 second
157 if (socket_list
.Count
!= 0) {
158 Logger
.Log
.Debug ("The daemon appears to have gone away.");
159 Logger
.Log
.Debug ("Shutting down helper.");
160 Shutdown
.BeginShutdown ();
163 } catch (SNS
.SocketException
) {
164 Logger
.Log
.Debug ("Caught a SocketException while trying to monitor the daemon");
165 Logger
.Log
.Debug ("Shutting down");
166 Shutdown
.BeginShutdown ();
170 /////////////////////////////////////////////////////////////////////////////
172 // The integer values of the Mono.Posix.Signal enumeration don't actually
173 // match the Linux signal numbers of Linux. Oops!
174 // This is fixed in Mono.Unix, but for the moment we want to maintain
175 // compatibility with mono 1.0.x.
176 const int ACTUAL_LINUX_SIGINT
= 2;
177 const int ACTUAL_LINUX_SIGQUIT
= 3;
178 const int ACTUAL_LINUX_SIGTERM
= 15;
180 static void SetupSignalHandlers ()
182 // Force OurSignalHandler to be JITed
183 OurSignalHandler (-1);
185 // Set up our signal handler
186 Mono
.Posix
.Syscall
.sighandler_t sig_handler
;
187 sig_handler
= new Mono
.Posix
.Syscall
.sighandler_t (OurSignalHandler
);
188 Mono
.Posix
.Syscall
.signal (ACTUAL_LINUX_SIGINT
, sig_handler
);
189 Mono
.Posix
.Syscall
.signal (ACTUAL_LINUX_SIGQUIT
, sig_handler
);
190 Mono
.Posix
.Syscall
.signal (ACTUAL_LINUX_SIGTERM
, sig_handler
);
193 // Our handler triggers an orderly shutdown when it receives a signal.
194 // However, this can be annoying if the process gets wedged during
195 // shutdown. To deal with that case, we make a note of the time when
196 // the first signal comes in, and we allow signals to unconditionally
197 // kill the process after 5 seconds have passed.
198 static DateTime signal_time
= DateTime
.MinValue
;
199 static void OurSignalHandler (int signal
)
201 // This allows us to call OurSignalHandler w/o doing anything.
202 // We want to call it once to ensure that it is pre-JITed.
205 Logger
.Log
.Debug ("Handling signal {0}", signal
);
207 bool first_signal
= false;
208 if (signal_time
== DateTime
.MinValue
) {
209 signal_time
= DateTime
.Now
;
213 if (Shutdown
.ShutdownRequested
) {
216 Logger
.Log
.Debug ("Shutdown already in progress.");
218 double t
= (DateTime
.Now
- signal_time
).TotalSeconds
;
219 const double min_t
= 5;
222 Logger
.Log
.Debug ("Signals can force an immediate shutdown in {0:0.00}s", min_t
-t
);
224 Logger
.Log
.Debug ("Forcing immediate shutdown.");
225 Environment
.Exit (0);
230 Logger
.Log
.Debug ("Initiating shutdown in response to signal.");
231 Shutdown
.BeginShutdown ();
235 static void OnShutdown ()