History

Renamed folders and separated GSF.Media and GSF.Net into separate projects.

StephenCWills committed 14 years ago

Commit ac1ef661 parent 45bce55628 files · +733 −461Browse files

Showing the changes to Source/Libraries/GSF.ServiceProcess/ServiceHelper.cs (1 of 628 files). Show all

Unified · Split

Source/Libraries/GSF.ServiceProcess/ServiceHelper.cs+3899−0
@@ -0,0 +1,3899 @@
1+//*******************************************************************************************************
2+// ServiceHelper.cs - Gbtc
3+//
4+// Tennessee Valley Authority, 2009
5+// No copyright is claimed pursuant to 17 USC § 105. All Other Rights Reserved.
6+//
7+// This software is made freely available under the TVA Open Source Agreement (see below).
8+// Code in this file licensed to GSF under one or more contributor license agreements listed below.
9+//
10+// Code Modification History:
11+// -----------------------------------------------------------------------------------------------------
12+// 08/29/2006 - Pinal C. Patel
13+// Original version of source code generated.
14+// 11/30/2007 - Pinal C. Patel
15+// Modified the "design time" check in EndInit() method to use LicenseManager. UsageMode property
16+// instead of DesignMode property as the former is more accurate than the latter.
17+// 12/14/2007 - Pinal C. Patel
18+// Made monitoring of service health optional via the MonitorServiceHealth property.
19+// 09/30/2008 - J. Ritchie Carroll
20+// Converted to C#.
21+// 03/06/2009 - Pinal C. Patel
22+// Edited code comments.
23+// 06/19/2009 - Pinal C. Patel
24+// Modified Initialize() method to just load settings from the config file.
25+// 06/30/2009 - Pinal C. Patel
26+// Changed ServiceComponents to be a collection of object instead of ISupportLifecycle so it can
27+// host a wide range of object implementing various interfaces used by the ServiceHelper commands.
28+// 07/15/2009 - Pinal C. Patel
29+// Added AllowedRemoteUsers and ImpersonateRemoteUser properties as part of security.
30+// 07/16/2009 - Pinal C. Patel
31+// Added SupportTelnetSessions property so that telnet support can be optionally turned-on.
32+// 07/17/2009 - Pinal C. Patel
33+// Modified MonitorServiceHealth to default to false as it is not always needed.
34+// 07/30/2009 - Pinal C. Patel
35+// Modified to set the principal of the thread where client request are handled to that of the the
36+// process or remote client (if transmitted by the remote client upon connection).
37+// 08/07/2009 - Pinal C. Patel
38+// Subscribed to ErrorLogger.LoggingException event.
39+// 08/10/2009 - Josh L. Patterson
40+// Edited Comments
41+// 09/08/2009 - Pinal C. Patel
42+// Modified GetProcess(), GetConnectedClient() and GetClientRequestHandler() to use lambda
43+// functions instead of manual iteration of collection items.
44+// Renamed GetProcess(), GetConnectedClient() and GetClientRequestHandler() to FindProcess(),
45+// FindConnectedClient() and FindClientRequestHandler() respectively.
46+// 09/14/2009 - Stephen C. Wills
47+// Added new header and license agreement.
48+// 10/23/2009 - Pinal C. Patel
49+// Modified UpdateStatus() method to allow the type of update to be specified.
50+// 12/18/2009 - Pinal C. Patel
51+// Added message flooding control to UpdateStatus().
52+// 06/16/2010 - Pinal C. Patel
53+// Made changes necessary to implement role-based security.
54+// Added thread synchronization to list-based member variable.
55+// 10/14/2010 - Pinal C. Patel
56+// Updated security implementation to include the entire client request text including command
57+// and arguments instead of just the command.
58+// 12/16/2010 - Pinal C. Patel
59+// Added SupportSystemCommands property that can be used to enable or disable system-level access
60+// via the build-in commands.
61+// 01/04/2011 - J. Ritchie Carroll
62+// Changed default performance sampling interval on services to every 5 seconds instead of every
63+// second to reduce overhead and increase performance. Added a "-lifetime" option to the Health
64+// command to show lifetime performance statistics. Made sampling interval a user config option.
65+// 01/17/2011 - J. Ritchie Carroll
66+// Added default "version" and "time" service commands.
67+// 02/11/2011 - Pinal C. Patel
68+// Updated VerifySecurity() to work correctly with TCP integrated windows authentication.
69+// 03/09/2011 - Pinal C. Patel
70+// Moved UpdateType enumeration to GSF namespace in GSF.Core.dll for broader usage.
71+// 04/14/2011 - Pinal C. Patel
72+// Updated to use new serialization methods in GSF.Serialization class.
73+// 09/22/2011 - J. Ritchie Carroll
74+// Added Mono implementation exception regions.
75+// 02/17/2012 - Stephen C. Wills
76+// Added ReloadCryptoCache service command.
77+// 04/12/2012 - Pinal C. Patel
78+// Added new UnscheduleProcess() and RemoveScheduledProcess methods.
79+// Changed AddProcess(), AddScheduledProcess() and ScheduleProcess() method to return a boolean.
80+//
81+//*******************************************************************************************************
82+
83+#region [ TVA Open Source Agreement ]
84+/*
85+
86+ THIS OPEN SOURCE AGREEMENT ("AGREEMENT") DEFINES THE RIGHTS OF USE,REPRODUCTION, DISTRIBUTION,
87+ MODIFICATION AND REDISTRIBUTION OF CERTAIN COMPUTER SOFTWARE ORIGINALLY RELEASED BY THE
88+ TENNESSEE VALLEY AUTHORITY, A CORPORATE AGENCY AND INSTRUMENTALITY OF THE UNITED STATES GOVERNMENT
89+ ("GOVERNMENT AGENCY"). GOVERNMENT AGENCY IS AN INTENDED THIRD-PARTY BENEFICIARY OF ALL SUBSEQUENT
90+ DISTRIBUTIONS OR REDISTRIBUTIONS OF THE SUBJECT SOFTWARE. ANYONE WHO USES, REPRODUCES, DISTRIBUTES,
91+ MODIFIES OR REDISTRIBUTES THE SUBJECT SOFTWARE, AS DEFINED HEREIN, OR ANY PART THEREOF, IS, BY THAT
92+ ACTION, ACCEPTING IN FULL THE RESPONSIBILITIES AND OBLIGATIONS CONTAINED IN THIS AGREEMENT.
93+
94+ Original Software Designation: openPDC
95+ Original Software Title: The GSF Open Source Phasor Data Concentrator
96+ User Registration Requested. Please Visit https://naspi.tva.com/Registration/
97+ Point of Contact for Original Software: J. Ritchie Carroll <mailto:jrcarrol@tva.gov>
98+
99+ 1. DEFINITIONS
100+
101+ A. "Contributor" means Government Agency, as the developer of the Original Software, and any entity
102+ that makes a Modification.
103+
104+ B. "Covered Patents" mean patent claims licensable by a Contributor that are necessarily infringed by
105+ the use or sale of its Modification alone or when combined with the Subject Software.
106+
107+ C. "Display" means the showing of a copy of the Subject Software, either directly or by means of an
108+ image, or any other device.
109+
110+ D. "Distribution" means conveyance or transfer of the Subject Software, regardless of means, to
111+ another.
112+
113+ E. "Larger Work" means computer software that combines Subject Software, or portions thereof, with
114+ software separate from the Subject Software that is not governed by the terms of this Agreement.
115+
116+ F. "Modification" means any alteration of, including addition to or deletion from, the substance or
117+ structure of either the Original Software or Subject Software, and includes derivative works, as that
118+ term is defined in the Copyright Statute, 17 USC § 101. However, the act of including Subject Software
119+ as part of a Larger Work does not in and of itself constitute a Modification.
120+
121+ G. "Original Software" means the computer software first released under this Agreement by Government
122+ Agency entitled openPDC, including source code, object code and accompanying documentation, if any.
123+
124+ H. "Recipient" means anyone who acquires the Subject Software under this Agreement, including all
125+ Contributors.
126+
127+ I. "Redistribution" means Distribution of the Subject Software after a Modification has been made.
128+
129+ J. "Reproduction" means the making of a counterpart, image or copy of the Subject Software.
130+
131+ K. "Sale" means the exchange of the Subject Software for money or equivalent value.
132+
133+ L. "Subject Software" means the Original Software, Modifications, or any respective parts thereof.
134+
135+ M. "Use" means the application or employment of the Subject Software for any purpose.
136+
137+ 2. GRANT OF RIGHTS
138+
139+ A. Under Non-Patent Rights: Subject to the terms and conditions of this Agreement, each Contributor,
140+ with respect to its own contribution to the Subject Software, hereby grants to each Recipient a
141+ non-exclusive, world-wide, royalty-free license to engage in the following activities pertaining to
142+ the Subject Software:
143+
144+ 1. Use
145+
146+ 2. Distribution
147+
148+ 3. Reproduction
149+
150+ 4. Modification
151+
152+ 5. Redistribution
153+
154+ 6. Display
155+
156+ B. Under Patent Rights: Subject to the terms and conditions of this Agreement, each Contributor, with
157+ respect to its own contribution to the Subject Software, hereby grants to each Recipient under Covered
158+ Patents a non-exclusive, world-wide, royalty-free license to engage in the following activities
159+ pertaining to the Subject Software:
160+
161+ 1. Use
162+
163+ 2. Distribution
164+
165+ 3. Reproduction
166+
167+ 4. Sale
168+
169+ 5. Offer for Sale
170+
171+ C. The rights granted under Paragraph B. also apply to the combination of a Contributor's Modification
172+ and the Subject Software if, at the time the Modification is added by the Contributor, the addition of
173+ such Modification causes the combination to be covered by the Covered Patents. It does not apply to
174+ any other combinations that include a Modification.
175+
176+ D. The rights granted in Paragraphs A. and B. allow the Recipient to sublicense those same rights.
177+ Such sublicense must be under the same terms and conditions of this Agreement.
178+
179+ 3. OBLIGATIONS OF RECIPIENT
180+
181+ A. Distribution or Redistribution of the Subject Software must be made under this Agreement except for
182+ additions covered under paragraph 3H.
183+
184+ 1. Whenever a Recipient distributes or redistributes the Subject Software, a copy of this Agreement
185+ must be included with each copy of the Subject Software; and
186+
187+ 2. If Recipient distributes or redistributes the Subject Software in any form other than source code,
188+ Recipient must also make the source code freely available, and must provide with each copy of the
189+ Subject Software information on how to obtain the source code in a reasonable manner on or through a
190+ medium customarily used for software exchange.
191+
192+ B. Each Recipient must ensure that the following copyright notice appears prominently in the Subject
193+ Software:
194+
195+ No copyright is claimed pursuant to 17 USC § 105. All Other Rights Reserved.
196+
197+ C. Each Contributor must characterize its alteration of the Subject Software as a Modification and
198+ must identify itself as the originator of its Modification in a manner that reasonably allows
199+ subsequent Recipients to identify the originator of the Modification. In fulfillment of these
200+ requirements, Contributor must include a file (e.g., a change log file) that describes the alterations
201+ made and the date of the alterations, identifies Contributor as originator of the alterations, and
202+ consents to characterization of the alterations as a Modification, for example, by including a
203+ statement that the Modification is derived, directly or indirectly, from Original Software provided by
204+ Government Agency. Once consent is granted, it may not thereafter be revoked.
205+
206+ D. A Contributor may add its own copyright notice to the Subject Software. Once a copyright notice has
207+ been added to the Subject Software, a Recipient may not remove it without the express permission of
208+ the Contributor who added the notice.
209+
210+ E. A Recipient may not make any representation in the Subject Software or in any promotional,
211+ advertising or other material that may be construed as an endorsement by Government Agency or by any
212+ prior Recipient of any product or service provided by Recipient, or that may seek to obtain commercial
213+ advantage by the fact of Government Agency's or a prior Recipient's participation in this Agreement.
214+
215+ F. In an effort to track usage and maintain accurate records of the Subject Software, each Recipient,
216+ upon receipt of the Subject Software, is requested to register with Government Agency by visiting the
217+ following website: https://naspi.tva.com/Registration/. Recipient's name and personal information
218+ shall be used for statistical purposes only. Once a Recipient makes a Modification available, it is
219+ requested that the Recipient inform Government Agency at the web site provided above how to access the
220+ Modification.
221+
222+ G. Each Contributor represents that that its Modification does not violate any existing agreements,
223+ regulations, statutes or rules, and further that Contributor has sufficient rights to grant the rights
224+ conveyed by this Agreement.
225+
226+ H. A Recipient may choose to offer, and to charge a fee for, warranty, support, indemnity and/or
227+ liability obligations to one or more other Recipients of the Subject Software. A Recipient may do so,
228+ however, only on its own behalf and not on behalf of Government Agency or any other Recipient. Such a
229+ Recipient must make it absolutely clear that any such warranty, support, indemnity and/or liability
230+ obligation is offered by that Recipient alone. Further, such Recipient agrees to indemnify Government
231+ Agency and every other Recipient for any liability incurred by them as a result of warranty, support,
232+ indemnity and/or liability offered by such Recipient.
233+
234+ I. A Recipient may create a Larger Work by combining Subject Software with separate software not
235+ governed by the terms of this agreement and distribute the Larger Work as a single product. In such
236+ case, the Recipient must make sure Subject Software, or portions thereof, included in the Larger Work
237+ is subject to this Agreement.
238+
239+ J. Notwithstanding any provisions contained herein, Recipient is hereby put on notice that export of
240+ any goods or technical data from the United States may require some form of export license from the
241+ U.S. Government. Failure to obtain necessary export licenses may result in criminal liability under
242+ U.S. laws. Government Agency neither represents that a license shall not be required nor that, if
243+ required, it shall be issued. Nothing granted herein provides any such export license.
244+
245+ 4. DISCLAIMER OF WARRANTIES AND LIABILITIES; WAIVER AND INDEMNIFICATION
246+
247+ A. No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, EITHER
248+ EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT THE SUBJECT
249+ SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
250+ PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL BE ERROR
251+ FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. THIS
252+ AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR
253+ RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS
254+ RESULTING FROM USE OF THE SUBJECT SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND
255+ LIABILITIES REGARDING THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT
256+ "AS IS."
257+
258+ B. Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST GOVERNMENT AGENCY, ITS
259+ AGENTS, EMPLOYEES, CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF RECIPIENT'S USE
260+ OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES OR LOSSES ARISING FROM
261+ SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING FROM, RECIPIENT'S USE OF THE
262+ SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS GOVERNMENT AGENCY, ITS AGENTS,
263+ EMPLOYEES, CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, TO THE EXTENT PERMITTED BY
264+ LAW. THE FOREGOING RELEASE AND INDEMNIFICATION SHALL APPLY EVEN IF THE LIABILITIES, DEMANDS, DAMAGES,
265+ EXPENSES OR LOSSES ARE CAUSED, OCCASIONED, OR CONTRIBUTED TO BY THE NEGLIGENCE, SOLE OR CONCURRENT, OF
266+ GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE
267+ IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT.
268+
269+ 5. GENERAL TERMS
270+
271+ A. Termination: This Agreement and the rights granted hereunder will terminate automatically if a
272+ Recipient fails to comply with these terms and conditions, and fails to cure such noncompliance within
273+ thirty (30) days of becoming aware of such noncompliance. Upon termination, a Recipient agrees to
274+ immediately cease use and distribution of the Subject Software. All sublicenses to the Subject
275+ Software properly granted by the breaching Recipient shall survive any such termination of this
276+ Agreement.
277+
278+ B. Severability: If any provision of this Agreement is invalid or unenforceable under applicable law,
279+ it shall not affect the validity or enforceability of the remainder of the terms of this Agreement.
280+
281+ C. Applicable Law: This Agreement shall be subject to United States federal law only for all purposes,
282+ including, but not limited to, determining the validity of this Agreement, the meaning of its
283+ provisions and the rights, obligations and remedies of the parties.
284+
285+ D. Entire Understanding: This Agreement constitutes the entire understanding and agreement of the
286+ parties relating to release of the Subject Software and may not be superseded, modified or amended
287+ except by further written agreement duly executed by the parties.
288+
289+ E. Binding Authority: By accepting and using the Subject Software under this Agreement, a Recipient
290+ affirms its authority to bind the Recipient to all terms and conditions of this Agreement and that
291+ Recipient hereby agrees to all terms and conditions herein.
292+
293+ F. Point of Contact: Any Recipient contact with Government Agency is to be directed to the designated
294+ representative as follows: J. Ritchie Carroll <mailto:jrcarrol@tva.gov>.
295+
296+*/
297+#endregion
298+
299+#region [ Contributor License Agreements ]
300+
301+//******************************************************************************************************
302+//
303+// Copyright © 2011, Grid Protection Alliance. All Rights Reserved.
304+//
305+// The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
306+// not use this file except in compliance with the License. You may obtain a copy of the License at:
307+//
308+// http://www.opensource.org/licenses/eclipse-1.0.php
309+//
310+// Unless agreed to in writing, the subject software distributed under the License is distributed on an
311+// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
312+// License for the specific language governing permissions and limitations.
313+//
314+//******************************************************************************************************
315+
316+#endregion
317+
318+using GSF.Collections;
319+using GSF.Communication;
320+using GSF.Configuration;
321+using GSF.Diagnostics;
322+using GSF.ErrorManagement;
323+using GSF.IO;
324+using GSF.Reflection;
325+using GSF.Scheduling;
326+using GSF.Security;
327+using GSF.Security.Cryptography;
328+using System;
329+using System.Collections.Generic;
330+using System.ComponentModel;
331+using System.Configuration;
332+using System.Diagnostics;
333+using System.Drawing;
334+using System.Security;
335+using System.Security.Principal;
336+using System.ServiceProcess;
337+using System.Text;
338+using System.Threading;
339+
340+namespace GSF.ServiceProcess
341+{
342+ #region [ Enumerations ]
343+
344+ /// <summary>
345+ /// Indicates the state of a Windows Service.
346+ /// </summary>
347+ public enum ServiceState
348+ {
349+ /// <summary>
350+ /// Service has started.
351+ /// </summary>
352+ Started,
353+ /// <summary>
354+ /// Service has stopped.
355+ /// </summary>
356+ Stopped,
357+ /// <summary>
358+ /// Service has paused.
359+ /// </summary>
360+ Paused,
361+ /// <summary>
362+ /// Service has resumed.
363+ /// </summary>
364+ Resumed,
365+ /// <summary>
366+ /// Service has shutdown.
367+ /// </summary>
368+ Shutdown
369+ }
370+
371+ #endregion
372+
373+ /// <summary>
374+ /// Component that provides added functionality to a Windows Service.
375+ /// </summary>
376+ /// <seealso cref="ServiceProcess"/>
377+ /// <seealso cref="ServiceResponse"/>
378+ /// <seealso cref="ClientInfo"/>
379+ /// <seealso cref="ClientRequest"/>
380+ /// <seealso cref="ClientRequestHandler"/>
381+ [ToolboxBitmap(typeof(ServiceHelper))]
382+ public class ServiceHelper : Component, ISupportLifecycle, ISupportInitialize, IProvideStatus, IPersistSettings
383+ {
384+ #region [ Members ]
385+
386+ // Nested Types
387+ private class StatusUpdate
388+ {
389+ public StatusUpdate(Guid client, UpdateType type, string message)
390+ {
391+ this.Client = client;
392+ this.Type = type;
393+ this.Message = message;
394+ }
395+
396+ public Guid Client;
397+ public UpdateType Type;
398+ public string Message;
399+ }
400+
401+ // Constants
402+
403+ /// <summary>
404+ /// Specifies the default value for the <see cref="LogStatusUpdates"/> property.
405+ /// </summary>
406+ public const bool DefaultLogStatusUpdates = true;
407+
408+ /// <summary>
409+ /// Specifies the default value for the <see cref="MaxStatusUpdatesLength"/> property.
410+ /// </summary>
411+ public const int DefaultMaxStatusUpdatesLength = 8192;
412+
413+ /// <summary>
414+ /// Specifies the default value for the <see cref="MaxStatusUpdatesFrequency"/> property.
415+ /// </summary>
416+ public const int DefaultMaxStatusUpdatesFrequency = 30;
417+
418+ /// <summary>
419+ /// Specifies the default value for the <see cref="MonitorServiceHealth"/> property.
420+ /// </summary>
421+ public const bool DefaultMonitorServiceHealth = false;
422+
423+ /// <summary>
424+ /// Specifies the default value for the <see cref="HealthMonitorInterval"/> property.
425+ /// </summary>
426+ public const double DefaultHealthMonitorInterval = 5.0D;
427+
428+ /// <summary>
429+ /// Specifies the default value for the <see cref="RequestHistoryLimit"/> property.
430+ /// </summary>
431+ public const int DefaultRequestHistoryLimit = 50;
432+
433+ /// <summary>
434+ /// Specifies the default value for the <see cref="SupportTelnetSessions"/> property.
435+ /// </summary>
436+ public const bool DefaultSupportTelnetSessions = false;
437+
438+ /// <summary>
439+ /// Specifies the default value for the <see cref="SupportSystemCommands"/> property.
440+ /// </summary>
441+ public const bool DefaultSupportSystemCommands = false;
442+
443+ /// <summary>
444+ /// Specifies the default value for the <see cref="SecureRemoteInteractions"/> property.
445+ /// </summary>
446+ public const bool DefaultSecureRemoteInteractions = false;
447+
448+ /// <summary>
449+ /// Specifies the default value for the <see cref="PersistSettings"/> property.
450+ /// </summary>
451+ public const bool DefaultPersistSettings = false;
452+
453+ /// <summary>
454+ /// Specifies the default value for the <see cref="SettingsCategory"/> property.
455+ /// </summary>
456+ public const string DefaultSettingsCategory = "ServiceHelper";
457+
458+ // Events
459+
460+ /// <summary>
461+ /// Occurs when the <see cref="ParentService"/> is starting.
462+ /// </summary>
463+ [Category("Service"),
464+ Description("Occurs when the ParentService is starting.")]
465+ public event EventHandler<EventArgs<string[]>> ServiceStarting;
466+
467+ /// <summary>
468+ /// Occurs when the <see cref="ParentService"/> has started.
469+ /// </summary>
470+ [Category("Service"),
471+ Description("Occurs when the ParentService has started.")]
472+ public event EventHandler ServiceStarted;
473+
474+ /// <summary>
475+ /// Occurs when the <see cref="ParentService"/> is stopping.
476+ /// </summary>
477+ [Category("Service"),
478+ Description("Occurs when the ParentService is stopping.")]
479+ public event EventHandler ServiceStopping;
480+
481+ /// <summary>
482+ /// Occurs when the <see cref="ParentService"/> has stopped.
483+ /// </summary>
484+ [Category("Service"),
485+ Description("Occurs when the ParentService has stopped.")]
486+ public event EventHandler ServiceStopped;
487+
488+ /// <summary>
489+ /// Occurs when the <see cref="ParentService"/> is pausing.
490+ /// </summary>
491+ [Category("Service"),
492+ Description("Occurs when the ParentService is pausing.")]
493+ public event EventHandler ServicePausing;
494+
495+ /// <summary>
496+ /// Occurs when the <see cref="ParentService"/> has paused.
497+ /// </summary>
498+ [Category("Service"),
499+ Description("Occurs when the ParentService has paused.")]
500+ public event EventHandler ServicePaused;
501+
502+ /// <summary>
503+ /// Occurs when the <see cref="ParentService"/> is resuming.
504+ /// </summary>
505+ [Category("Service"),
506+ Description("Occurs when the ParentService is resuming.")]
507+ public event EventHandler ServiceResuming;
508+
509+ /// <summary>
510+ /// Occurs when the <see cref="ParentService"/> has resumed.
511+ /// </summary>
512+ [Category("Service"),
513+ Description("Occurs when the ParentService has resumed.")]
514+ public event EventHandler ServiceResumed;
515+
516+ /// <summary>
517+ /// Occurs when the system is being shutdown.
518+ /// </summary>
519+ [Category("System"),
520+ Description("Occurs when the system is being shutdown.")]
521+ public event EventHandler SystemShutdown;
522+
523+ /// <summary>
524+ /// Occurs when a request is received from one of the <see cref="RemoteClients"/>.
525+ /// </summary>
526+ /// <remarks>
527+ /// <see cref="EventArgs{T1,T2}.Argument1"/> is the ID of the remote client that sent the request.<br/>
528+ /// <see cref="EventArgs{T1,T2}.Argument2"/> is the <see cref="ClientRequest"/> sent by the remote client.
529+ /// </remarks>
530+ [Category("Client"),
531+ Description("Occurs when a request is received from one of the RemoteClients.")]
532+ public event EventHandler<EventArgs<Guid, ClientRequest>> ReceivedClientRequest;
533+
534+ /// <summary>
535+ /// Occurs when the state of a defiend <see cref="ServiceProcess"/> changes.
536+ /// </summary>
537+ /// <remarks>
538+ /// <see cref="EventArgs{T1,T2}.Argument1"/> is the <see cref="ServiceProcess.Name"/>.<br/>
539+ /// <see cref="EventArgs{T1,T2}.Argument2"/> is the <see cref="ServiceProcess.CurrentState"/>
540+ /// </remarks>
541+ [Category("Process"),
542+ Description("Occurs when the state of a defiend ServiceProcess changes.")]
543+ public event EventHandler<EventArgs<string, ServiceProcessState>> ProcessStateChanged;
544+
545+ // Fields
546+ private bool m_logStatusUpdates;
547+ private int m_maxStatusUpdatesLength;
548+ private int m_maxStatusUpdatesFrequency;
549+ private bool m_monitorServiceHealth;
550+ private double m_healthMonitorInterval;
551+ private int m_requestHistoryLimit;
552+ private bool m_supportTelnetSessions;
553+ private bool m_supportSystemCommands;
554+ private bool m_secureRemoteInteractions;
555+ private bool m_persistSettings;
556+ private string m_settingsCategory;
557+ private string m_telnetSessionPassword;
558+ private ServiceBase m_parentService;
559+ private ServerBase m_remotingServer;
560+ private LogFile m_statusLog;
561+ private ScheduleManager m_processScheduler;
562+ private ErrorLogger m_errorLogger;
563+ private PerformanceMonitor m_performanceMonitor;
564+ private List<ServiceProcess> m_processes;
565+ private List<object> m_serviceComponents;
566+ private List<ClientInfo> m_remoteClients;
567+ private List<ClientRequestInfo> m_clientRequestHistory;
568+ private List<ClientRequestHandler> m_clientRequestHandlers;
569+ private Dictionary<ISupportLifecycle, bool> m_componentEnabledStates;
570+ private ProcessQueue<StatusUpdate> m_statusUpdateQueue;
571+ private bool m_enabled;
572+ private bool m_disposed;
573+ private bool m_initialized;
574+ private bool m_suppressUpdates;
575+ private Guid m_remoteCommandClientID;
576+ private Process m_remoteCommandProcess;
577+ private Ticks m_lastStatusUpdateTime;
578+ private int m_statusUpdatesDisplayed;
579+
580+ #endregion
581+
582+ #region [ Constructors ]
583+
584+ /// <summary>
585+ /// Initializes a new instance of the <see cref="ServiceHelper"/> class.
586+ /// </summary>
587+ public ServiceHelper()
588+ : base()
589+ {
590+ m_telnetSessionPassword = "s3cur3";
591+ m_logStatusUpdates = DefaultLogStatusUpdates;
592+ m_maxStatusUpdatesLength = DefaultMaxStatusUpdatesLength;
593+ m_maxStatusUpdatesFrequency = DefaultMaxStatusUpdatesFrequency;
594+ m_monitorServiceHealth = DefaultMonitorServiceHealth;
595+ m_healthMonitorInterval = DefaultHealthMonitorInterval;
596+ m_requestHistoryLimit = DefaultRequestHistoryLimit;
597+ m_supportTelnetSessions = DefaultSupportTelnetSessions;
598+ m_supportSystemCommands = DefaultSupportSystemCommands;
599+ m_secureRemoteInteractions = DefaultSecureRemoteInteractions;
600+ m_persistSettings = DefaultPersistSettings;
601+ m_settingsCategory = DefaultSettingsCategory;
602+ m_processes = new List<ServiceProcess>();
603+ m_remoteClients = new List<ClientInfo>();
604+ m_clientRequestHistory = new List<ClientRequestInfo>();
605+ m_serviceComponents = new List<object>();
606+ m_clientRequestHandlers = new List<ClientRequestHandler>();
607+ m_componentEnabledStates = new Dictionary<ISupportLifecycle, bool>();
608+
609+ // Components
610+ m_statusUpdateQueue = ProcessQueue<StatusUpdate>.CreateSynchronousQueue(ProcessStatusUpdates);
611+ m_statusUpdateQueue.Name = "StatusUpdateQueue";
612+ m_statusUpdateQueue.ProcessException += StatusUpdateQueue_ProcessException;
613+
614+ m_statusLog = new LogFile();
615+ m_statusLog.FileName = "StatusLog.txt";
616+ m_statusLog.SettingsCategory = "StatusLog";
617+ m_statusLog.LogException += StatusLog_LogException;
618+
619+ m_processScheduler = new ScheduleManager();
620+ m_processScheduler.SettingsCategory = "ProcessScheduler";
621+ m_processScheduler.ScheduleDue += Scheduler_ScheduleDue;
622+
623+ m_errorLogger = new ErrorLogger();
624+ m_errorLogger.ExitOnUnhandledException = false;
625+ m_errorLogger.SettingsCategory = "ErrorLogger";
626+ m_errorLogger.ErrorLog.SettingsCategory = "ErrorLog";
627+ m_errorLogger.LoggingException += ErrorLogger_LoggingException;
628+ }
629+
630+ /// <summary>
631+ /// Initializes a new instance of the <see cref="ServiceHelper"/> class.
632+ /// </summary>
633+ /// <param name="container"><see cref="IContainer"/> object that contains the <see cref="ServiceHelper"/>.</param>
634+ public ServiceHelper(IContainer container)
635+ : this()
636+ {
637+ if (container != null)
638+ container.Add(this);
639+ }
640+
641+ #endregion
642+
643+ #region [ Properties ]
644+
645+ /// <summary>
646+ /// Gets or sets a boolean value that indicates whether messages sent using <see cref="UpdateStatus(UpdateType,string,object[])"/>
647+ /// or <see cref="UpdateStatus(Guid,UpdateType,string,object[])"/> are to be logged to the <see cref="StatusLog"/>.
648+ /// </summary>
649+ [Category("Updates"),
650+ DefaultValue(DefaultLogStatusUpdates),
651+ Description("Indicates whether messages sent using UpdateStatus() method overloads are to be logged to the StatusLog.")]
652+ public bool LogStatusUpdates
653+ {
654+ get
655+ {
656+ return m_logStatusUpdates;
657+ }
658+ set
659+ {
660+ m_logStatusUpdates = value;
661+ }
662+ }
663+
664+ /// <summary>
665+ /// Gets or sets the maximum numbers of characters allowed in update status messages without getting suppressed from being displayed.
666+ /// </summary>
667+ /// <exception cref="ArgumentException">The value being assigned is negative or zero.</exception>
668+ [Category("Updates"),
669+ DefaultValue(DefaultMaxStatusUpdatesLength),
670+ Description("Maximum numbers of characters allowed in update status messages without getting suppressed from being displayed.")]
671+ public int MaxStatusUpdatesLength
672+ {
673+ get
674+ {
675+ return m_maxStatusUpdatesLength;
676+ }
677+ set
678+ {
679+ if (value <= 0)
680+ throw new ArgumentException("Value must be positive");
681+
682+ m_maxStatusUpdatesLength = value;
683+ }
684+ }
685+
686+ /// <summary>
687+ /// Gets or sets the maximum number of status update messages that can be issued in a second without getting suppressed from being displayed.
688+ /// </summary>
689+ /// <exception cref="ArgumentException">The value being assigned is negative or zero.</exception>
690+ [Category("Updates"),
691+ DefaultValue(DefaultMaxStatusUpdatesFrequency),
692+ Description("Maximum number of status update messages that can be issued in a second without getting suppressed from being displayed.")]
693+ public int MaxStatusUpdatesFrequency
694+ {
695+ get
696+ {
697+ return m_maxStatusUpdatesFrequency;
698+ }
699+ set
700+ {
701+ if (value <= 0)
702+ throw new ArgumentException("Value must be positive");
703+
704+ m_maxStatusUpdatesFrequency = value;
705+ }
706+ }
707+
708+ /// <summary>
709+ /// Gets or sets a boolean value that indicates whether the health of the <see cref="ParentService"/> is to be monitored.
710+ /// </summary>
711+ [Category("Settings"),
712+ DefaultValue(DefaultMonitorServiceHealth),
713+ Description("Indicates whether the health of the ParentService is to be monitored.")]
714+ public bool MonitorServiceHealth
715+ {
716+ get
717+ {
718+ return m_monitorServiceHealth;
719+ }
720+ set
721+ {
722+ m_monitorServiceHealth = value;
723+ }
724+ }
725+
726+ /// <summary>
727+ /// Gets or sets the interval, in seconds, over which to sample the performance monitor for health statistics.
728+ /// </summary>
729+ [Category("Settings"),
730+ DefaultValue(DefaultHealthMonitorInterval),
731+ Description("Indicates the interval, in seconds, over which to sample the performance monitor for health statistics.")]
732+ public double HealthMonitorInterval
733+ {
734+ get
735+ {
736+ return m_healthMonitorInterval;
737+ }
738+ set
739+ {
740+ if (value <= 0.0D)
741+ m_healthMonitorInterval = DefaultHealthMonitorInterval;
742+ else
743+ m_healthMonitorInterval = value;
744+
745+ if (m_performanceMonitor != null)
746+ m_performanceMonitor.SamplingInterval = m_healthMonitorInterval * 1000.0D;
747+ }
748+ }
749+
750+ /// <summary>
751+ /// Gets or sets the maximum number of <see cref="ClientRequest"/> entries to be maintained in the <see cref="ClientRequestHistory"/>.
752+ /// </summary>
753+ /// <exception cref="ArgumentOutOfRangeException">The value being assigned is zero or negative.</exception>
754+ [Category("Settings"),
755+ DefaultValue(DefaultRequestHistoryLimit),
756+ Description("Maximum number of ClientRequest entries to be maintained in the ClientRequestHistory.")]
757+ public int RequestHistoryLimit
758+ {
759+ get
760+ {
761+ return m_requestHistoryLimit;
762+ }
763+ set
764+ {
765+ if (value < 1)
766+ throw new ArgumentOutOfRangeException("RequestHistoryLimit", "Value must be greater that 0");
767+
768+ m_requestHistoryLimit = value;
769+ }
770+ }
771+
772+ /// <summary>
773+ /// Gets or sets a boolean value that indicates whether the <see cref="ServiceHelper"/> will have support for remote telnet-like sessions.
774+ /// </summary>
775+ [Category("Security"),
776+ DefaultValue(DefaultSupportTelnetSessions),
777+ Description("Indicates whether the ServiceHelper will have support for remote telnet-like sessions.")]
778+ public bool SupportTelnetSessions
779+ {
780+ get
781+ {
782+ return m_supportTelnetSessions;
783+ }
784+ set
785+ {
786+ m_supportTelnetSessions = value;
787+ }
788+ }
789+
790+ /// <summary>
791+ /// Gets or sets a boolean value that indicates whether <see cref="ServiceHelper"/> commands will have support for system-level access (-system switch).
792+ /// </summary>
793+ [Category("Security"),
794+ DefaultValue(DefaultSupportSystemCommands),
795+ Description("Indicates whether the ServiceHelper commands will have support for system-level access (-system switch).")]
796+ public bool SupportSystemCommands
797+ {
798+ get
799+ {
800+ return m_supportSystemCommands;
801+ }
802+ set
803+ {
804+ m_supportSystemCommands = value;
805+ }
806+ }
807+
808+ /// <summary>
809+ /// Gets or sets a boolean value that indicates whether <see cref="ServiceHelper"/> will secure remote interactions from <see cref="ClientHelper"/>.
810+ /// </summary>
811+ [Category("Security"),
812+ DefaultValue(DefaultSecureRemoteInteractions),
813+ Description("Indicates whether ServiceHelper will secure remote interactions from ClientHelper.")]
814+ public bool SecureRemoteInteractions
815+ {
816+ get
817+ {
818+ return m_secureRemoteInteractions;
819+ }
820+ set
821+ {
822+ m_secureRemoteInteractions = value;
823+ }
824+ }
825+
826+ /// <summary>
827+ /// Gets or sets a boolean value that indicates whether the settings of <see cref="ServiceHelper"/> are to be saved to the config file.
828+ /// </summary>
829+ [Category("Persistance"),
830+ DefaultValue(DefaultPersistSettings),
831+ Description("Indicates whether the settings of ServiceHelper are to be saved to the config file.")]
832+ public bool PersistSettings
833+ {
834+ get
835+ {
836+ return m_persistSettings;
837+ }
838+ set
839+ {
840+ m_persistSettings = value;
841+ }
842+ }
843+
844+ /// <summary>
845+ /// Gets or sets the category under which the settings of <see cref="ServiceHelper"/> are to be saved to the config file
846+ /// if the <see cref="PersistSettings"/> property is set to true.
847+ /// </summary>
848+ /// <exception cref="ArgumentNullException">The value being assigned is a null or empty string.</exception>
849+ [Category("Persistance"),
850+ DefaultValue(DefaultSettingsCategory),
851+ Description("Category under which the settings of ServiceHelper are to be saved to the config file if the PersistSettings property is set to true.")]
852+ public string SettingsCategory
853+ {
854+ get
855+ {
856+ return m_settingsCategory;
857+ }
858+ set
859+ {
860+ if (string.IsNullOrEmpty(value))
861+ throw new ArgumentNullException("value");
862+
863+ m_settingsCategory = value;
864+ }
865+ }
866+
867+ /// <summary>
868+ /// Gets or sets the <see cref="ServiceBase"/> to which the <see cref="ServiceHelper"/> will provided added functionality.
869+ /// </summary>
870+ [Category("Components"),
871+ Description("ServiceBase to which the ServiceHelper will provided added functionality.")]
872+ public ServiceBase ParentService
873+ {
874+ get
875+ {
876+ return m_parentService;
877+ }
878+ set
879+ {
880+ m_parentService = value;
881+ }
882+ }
883+
884+ /// <summary>
885+ /// Gets or sets the <see cref="ServerBase"/> component used for communicating with <see cref="RemoteClients"/>.
886+ /// </summary>
887+ [Category("Components"),
888+ Description("ServerBase component used for communicating with RemoteClients.")]
889+ public ServerBase RemotingServer
890+ {
891+ get
892+ {
893+ return m_remotingServer;
894+ }
895+ set
896+ {
897+ if (m_remotingServer != null)
898+ {
899+ // Detach events from any existing instance
900+ m_remotingServer.ClientDisconnected -= RemotingServer_ClientDisconnected;
901+ m_remotingServer.ReceiveClientDataComplete -= RemotingServer_ReceiveClientDataComplete;
902+ }
903+
904+ m_remotingServer = value;
905+
906+ if (m_remotingServer != null)
907+ {
908+ // Attach events to new instance
909+ m_remotingServer.ClientDisconnected += RemotingServer_ClientDisconnected;
910+ m_remotingServer.ReceiveClientDataComplete += RemotingServer_ReceiveClientDataComplete;
911+ }
912+ }
913+ }
914+
915+ /// <summary>
916+ /// Gets the <see cref="ScheduleManager"/> component used for scheduling defined <see cref="ServiceProcess"/>.
917+ /// </summary>
918+ [Category("Components"),
919+ Description("ScheduleManager component used for scheduling defined ServiceProcess."),
920+ DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
921+ public ScheduleManager ProcessScheduler
922+ {
923+ get
924+ {
925+ return m_processScheduler;
926+ }
927+ }
928+
929+ /// <summary>
930+ /// Gets the <see cref="LogFile"/> component used for logging status messages to a text file.
931+ /// </summary>
932+ [Category("Components"),
933+ Description("LogFile component used for logging status messages to a text file."),
934+ DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
935+ public LogFile StatusLog
936+ {
937+ get
938+ {
939+ return m_statusLog;
940+ }
941+ }
942+
943+ /// <summary>
944+ /// Gets the <see cref="ErrorLogger"/> component used for logging errors encountered in the <see cref="ParentService"/>.
945+ /// </summary>
946+ [Category("Components"),
947+ Description("ErrorLogger component used for logging errors encountered in the ParentService."),
948+ DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
949+ public ErrorLogger ErrorLogger
950+ {
951+ get
952+ {
953+ return m_errorLogger;
954+ }
955+ }
956+
957+ /// <summary>
958+ /// Gets or sets a boolean value that indicates whether the <see cref="ServiceHelper"/> is currently enabled.
959+ /// </summary>
960+ /// <remarks>
961+ /// <see cref="Enabled"/> property is not be set by user-code directly.
962+ /// </remarks>
963+ [Browsable(false),
964+ EditorBrowsable(EditorBrowsableState.Never),
965+ DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
966+ public bool Enabled
967+ {
968+ get
969+ {
970+ return m_enabled;
971+ }
972+ set
973+ {
974+ if (value)
975+ {
976+ // Re-enable all service components.
977+ bool state;
978+ ISupportLifecycle typedComponent;
979+ lock (m_serviceComponents)
980+ {
981+ foreach (object component in m_serviceComponents)
982+ {
983+ typedComponent = component as ISupportLifecycle;
984+ if (typedComponent != null)
985+ {
986+ // Restore previous state.
987+ if (m_componentEnabledStates.TryGetValue(typedComponent, out state))
988+ typedComponent.Enabled = state;
989+ }
990+ }
991+ }
992+ }
993+ else
994+ {
995+ // Disable all service components.
996+ m_componentEnabledStates.Clear();
997+ ISupportLifecycle typedComponent;
998+ lock (m_serviceComponents)
999+ {
1000+ foreach (object component in m_serviceComponents)
1001+ {
1002+ typedComponent = component as ISupportLifecycle;
1003+ if (typedComponent != null)
1004+ {
1005+ // Save current state.
1006+ m_componentEnabledStates.Add(typedComponent, typedComponent.Enabled);
1007+ typedComponent.Enabled = false;
1008+ }
1009+ }
1010+ }
1011+ }
1012+
1013+ m_enabled = value;
1014+ }
1015+ }
1016+
1017+ /// <summary>
1018+ /// Gets a list of <see cref="ServiceProcess"/> defined in the <see cref="ServiceHelper"/>.
1019+ /// </summary>
1020+ [Browsable(false),
1021+ EditorBrowsable(EditorBrowsableState.Advanced)]
1022+ public IList<ServiceProcess> Processes
1023+ {
1024+ get
1025+ {
1026+ return m_processes.AsReadOnly();
1027+ }
1028+ }
1029+
1030+ /// <summary>
1031+ /// Gets a list of <see cref="ClientInfo"/> for remote clients connected to the <see cref="RemotingServer"/>.
1032+ /// </summary>
1033+ [Browsable(false),
1034+ EditorBrowsable(EditorBrowsableState.Advanced)]
1035+ public IList<ClientInfo> RemoteClients
1036+ {
1037+ get
1038+ {
1039+ return m_remoteClients.AsReadOnly();
1040+ }
1041+ }
1042+
1043+ /// <summary>
1044+ /// Gets a list of <see cref="ClientRequestInfo"/> for requests made by <see cref="RemoteClients"/>.
1045+ /// </summary>
1046+ [Browsable(false),
1047+ EditorBrowsable(EditorBrowsableState.Advanced)]
1048+ public IList<ClientRequestInfo> ClientRequestHistory
1049+ {
1050+ get
1051+ {
1052+ return m_clientRequestHistory.AsReadOnly();
1053+ }
1054+ }
1055+
1056+ /// <summary>
1057+ /// Gets a list of <see cref="ClientRequestHandler"/> registered for handling requests from <see cref="RemoteClients"/>.
1058+ /// </summary>
1059+ [Browsable(false),
1060+ EditorBrowsable(EditorBrowsableState.Advanced)]
1061+ public IList<ClientRequestHandler> ClientRequestHandlers
1062+ {
1063+ get
1064+ {
1065+ return m_clientRequestHandlers;
1066+ }
1067+ }
1068+
1069+ /// <summary>
1070+ /// Gets a list of components that implement the <see cref="ISupportLifecycle"/> interface used by the <see cref="ParentService"/>.
1071+ /// </summary>
1072+ [Browsable(false),
1073+ EditorBrowsable(EditorBrowsableState.Advanced)]
1074+ public IList<object> ServiceComponents
1075+ {
1076+ get
1077+ {
1078+ return m_serviceComponents;
1079+ }
1080+ }
1081+
1082+ /// <summary>
1083+ /// Gets the <see cref="PerformanceMonitor"/> object used for monitoring the health of the <see cref="ParentService"/>.
1084+ /// </summary>
1085+ [Browsable(false),
1086+ EditorBrowsable(EditorBrowsableState.Advanced)]
1087+ public PerformanceMonitor PerformanceMonitor
1088+ {
1089+ get
1090+ {
1091+ return m_performanceMonitor;
1092+ }
1093+ }
1094+
1095+ /// <summary>
1096+ /// Gets the unique identifier of the <see cref="ServiceHelper"/>.
1097+ /// </summary>
1098+ [Browsable(false)]
1099+ public string Name
1100+ {
1101+ get
1102+ {
1103+ if (m_parentService == null)
1104+ return m_settingsCategory;
1105+ else
1106+ return m_parentService.ServiceName;
1107+ }
1108+ }
1109+
1110+ /// <summary>
1111+ /// Gets the descriptive status of the <see cref="ServiceHelper"/>.
1112+ /// </summary>
1113+ [Browsable(false)]
1114+ public string Status
1115+ {
1116+ get
1117+ {
1118+ StringBuilder status = new StringBuilder();
1119+
1120+ // Show the uptime for the windows service.
1121+ if (m_remotingServer != null)
1122+ {
1123+ status.AppendFormat("System Uptime: {0}", m_remotingServer.RunTime.ToString());
1124+ status.AppendLine();
1125+ status.AppendLine();
1126+ }
1127+
1128+ // Show the status of registered components.
1129+ status.AppendFormat("Status of components used by {0}:", Name);
1130+ status.AppendLine();
1131+ status.AppendLine();
1132+ IProvideStatus typedComponent;
1133+ lock (m_serviceComponents)
1134+ {
1135+ foreach (object component in m_serviceComponents)
1136+ {
1137+ typedComponent = component as IProvideStatus;
1138+ if (typedComponent != null)
1139+ {
1140+ // This component provides status information.
1141+ status.AppendFormat("Status of {0}:", typedComponent.Name);
1142+ status.AppendLine();
1143+ status.Append(typedComponent.Status);
1144+ status.AppendLine();
1145+ }
1146+ }
1147+ }
1148+
1149+ return status.ToString();
1150+ }
1151+ }
1152+
1153+ #endregion
1154+
1155+ #region [ Methods ]
1156+
1157+ /// <summary>
1158+ /// Initializes the <see cref="ServiceHelper"/>.
1159+ /// </summary>
1160+ /// <remarks>
1161+ /// <see cref="Initialize()"/> is to be called by user-code directly only if the <see cref="ServiceHelper"/>
1162+ /// object is not consumed through the designer surface of the IDE.
1163+ /// </remarks>
1164+ public void Initialize()
1165+ {
1166+ if (!m_initialized)
1167+ {
1168+ LoadSettings(); // Load settings from the config file.
1169+ m_initialized = true; // Initialize only once.
1170+ }
1171+ }
1172+
1173+ /// <summary>
1174+ /// Performs necessary operations before the <see cref="ServiceHelper"/> properties are initialized.
1175+ /// </summary>
1176+ /// <remarks>
1177+ /// <see cref="BeginInit()"/> should never be called by user-code directly. This method exists solely for use
1178+ /// by the designer if the <see cref="ServiceHelper"/> is consumed through the designer surface of the IDE.
1179+ /// </remarks>
1180+ [EditorBrowsable(EditorBrowsableState.Never)]
1181+ public void BeginInit()
1182+ {
1183+ if (!DesignMode)
1184+ {
1185+ try
1186+ {
1187+ // Nothing needs to be done before component is initialized.
1188+ }
1189+ catch (Exception)
1190+ {
1191+ // Prevent the IDE from crashing when component is in design mode.
1192+ }
1193+ }
1194+ }
1195+
1196+ /// <summary>
1197+ /// Performs necessary operations after the <see cref="ServiceHelper"/> properties are initialized.
1198+ /// </summary>
1199+ /// <remarks>
1200+ /// <see cref="EndInit()"/> should never be called by user-code directly. This method exists solely for use
1201+ /// by the designer if the <see cref="ServiceHelper"/> is consumed through the designer surface of the IDE.
1202+ /// </remarks>
1203+ [EditorBrowsable(EditorBrowsableState.Never)]
1204+ public void EndInit()
1205+ {
1206+ if (!DesignMode)
1207+ {
1208+ try
1209+ {
1210+ Initialize();
1211+ }
1212+ catch (Exception)
1213+ {
1214+ // Prevent the IDE from crashing when component is in design mode.
1215+ }
1216+ }
1217+ }
1218+
1219+ /// <summary>
1220+ /// Saves settings of the <see cref="ServiceHelper"/> to the config file if the <see cref="PersistSettings"/> property is set to true.
1221+ /// </summary>
1222+ /// <exception cref="ConfigurationErrorsException"><see cref="SettingsCategory"/> has a value of null or empty string.</exception>
1223+ public void SaveSettings()
1224+ {
1225+ if (m_persistSettings)
1226+ {
1227+ // Ensure that settings category is specified.
1228+ if (string.IsNullOrEmpty(m_settingsCategory))
1229+ throw new ConfigurationErrorsException("SettingsCategory property has not been set");
1230+
1231+ // Save settings under the specified category.
1232+ ConfigurationFile config = ConfigurationFile.Current;
1233+ CategorizedSettingsElementCollection settings = config.Settings[m_settingsCategory];
1234+ settings["LogStatusUpdates", true].Update(m_logStatusUpdates);
1235+ settings["MaxStatusUpdatesLength", true].Update(m_maxStatusUpdatesLength);
1236+ settings["MaxStatusUpdatesFrequency", true].Update(m_maxStatusUpdatesFrequency);
1237+ settings["MonitorServiceHealth", true].Update(m_monitorServiceHealth);
1238+ settings["HealthMonitorInterval", true].Update(m_healthMonitorInterval);
1239+ settings["RequestHistoryLimit", true].Update(m_requestHistoryLimit);
1240+ settings["SupportTelnetSessions", true].Update(m_supportTelnetSessions);
1241+ settings["SupportSystemCommands", true].Update(m_supportSystemCommands);
1242+ settings["SecureRemoteInteractions", true].Update(m_secureRemoteInteractions);
1243+ config.Save();
1244+ }
1245+ }
1246+
1247+ /// <summary>
1248+ /// Saves settings of the <see cref="ServiceHelper"/> to the config file if the <see cref="PersistSettings"/> property is set to true.
1249+ /// </summary>
1250+ /// <param name="includeServiceComponents">A boolean value that indicates whether the settings of <see cref="ServiceComponents"/> are to be saved.</param>
1251+ /// <exception cref="ConfigurationErrorsException"><see cref="SettingsCategory"/> has a value of null or empty string.</exception>
1252+ public void SaveSettings(bool includeServiceComponents)
1253+ {
1254+ SaveSettings();
1255+ if (includeServiceComponents)
1256+ {
1257+ IPersistSettings typedComponent;
1258+ lock (m_serviceComponents)
1259+ {
1260+ foreach (object component in m_serviceComponents)
1261+ {
1262+ typedComponent = component as IPersistSettings;
1263+ if (typedComponent != null)
1264+ typedComponent.SaveSettings();
1265+ }
1266+ }
1267+ }
1268+ }
1269+
1270+ /// <summary>
1271+ /// Loads saved settings of the <see cref="ServiceHelper"/> from the config file if the <see cref="PersistSettings"/> property is set to true.
1272+ /// </summary>
1273+ /// <exception cref="ConfigurationErrorsException"><see cref="SettingsCategory"/> has a value of null or empty string.</exception>
1274+ public void LoadSettings()
1275+ {
1276+ if (m_persistSettings)
1277+ {
1278+ // Ensure that settings category is specified.
1279+ if (string.IsNullOrEmpty(m_settingsCategory))
1280+ throw new ConfigurationErrorsException("SettingsCategory property has not been set");
1281+
1282+ // Load settings from the specified category.
1283+ ConfigurationFile config = ConfigurationFile.Current;
1284+ CategorizedSettingsElementCollection settings = config.Settings[m_settingsCategory];
1285+
1286+ settings.Add("LogStatusUpdates", m_logStatusUpdates, "True if status update messages are to be logged to a text file; otherwise False.");
1287+ settings.Add("MaxStatusUpdatesLength", m_maxStatusUpdatesLength, "Maximum numbers of characters allowed in update status messages without getting suppressed from being displayed.");
1288+ settings.Add("MaxStatusUpdatesFrequency", m_maxStatusUpdatesFrequency, "Maximum number of status update messages that can be issued in a second without getting suppressed from being displayed.");
1289+ settings.Add("MonitorServiceHealth", m_monitorServiceHealth, "True if the service health is to be monitored; otherwise False.");
1290+ settings.Add("HealthMonitorInterval", m_healthMonitorInterval, "The interval, in seconds, over which to sample the performance montitor for health statistics.");
1291+ settings.Add("RequestHistoryLimit", m_requestHistoryLimit, "Number of client request entries to be kept in the history.");
1292+ settings.Add("SupportTelnetSessions", m_supportTelnetSessions, "True to enable the support for remote telnet-like sessions; otherwise False.");
1293+ settings.Add("SupportSystemCommands", m_supportSystemCommands, "True to enable system-level access (-system switch) via the build-in commands; otherwise False.");
1294+ settings.Add("SecureRemoteInteractions", m_secureRemoteInteractions, "True to enable security of remote client interactions; otherwise False.");
1295+
1296+ if (settings["TelnetSessionPassword"] != null)
1297+ m_telnetSessionPassword = settings["TelnetSessionPassword"].ValueAs(m_telnetSessionPassword);
1298+
1299+ LogStatusUpdates = settings["LogStatusUpdates"].ValueAs(m_logStatusUpdates);
1300+ MaxStatusUpdatesLength = settings["MaxStatusUpdatesLength"].ValueAs(m_maxStatusUpdatesLength);
1301+ MaxStatusUpdatesFrequency = settings["MaxStatusUpdatesFrequency"].ValueAs(m_maxStatusUpdatesFrequency);
1302+ MonitorServiceHealth = settings["MonitorServiceHealth"].ValueAs(m_monitorServiceHealth);
1303+ HealthMonitorInterval = settings["HealthMonitorInterval"].ValueAs(m_healthMonitorInterval);
1304+ RequestHistoryLimit = settings["RequestHistoryLimit"].ValueAs(m_requestHistoryLimit);
1305+ SupportTelnetSessions = settings["SupportTelnetSessions"].ValueAs(m_supportTelnetSessions);
1306+ SupportSystemCommands = settings["SupportSystemCommands"].ValueAs(m_supportSystemCommands);
1307+ SecureRemoteInteractions = settings["SecureRemoteInteractions"].ValueAs(m_secureRemoteInteractions);
1308+ }
1309+ }
1310+
1311+ /// <summary>
1312+ /// Loads saved settings of the <see cref="ServiceHelper"/> from the config file if the <see cref="PersistSettings"/> property is set to true.
1313+ /// </summary>
1314+ /// <param name="includeServiceComponents">A boolean value that indicates whether the settings of <see cref="ServiceComponents"/> are to be loaded.</param>
1315+ public void LoadSettings(bool includeServiceComponents)
1316+ {
1317+ LoadSettings();
1318+ if (includeServiceComponents)
1319+ {
1320+ IPersistSettings typedComponent;
1321+ lock (m_serviceComponents)
1322+ {
1323+ foreach (object component in m_serviceComponents)
1324+ {
1325+ typedComponent = component as IPersistSettings;
1326+ if (typedComponent != null)
1327+ typedComponent.LoadSettings();
1328+ }
1329+ }
1330+ }
1331+ }
1332+
1333+ /// <summary>
1334+ /// To be called from the <see cref="ServiceBase.OnStart(string[])"/> method of <see cref="ParentService"/>.
1335+ /// </summary>
1336+ /// <param name="args">Array of type <see cref="String"/>.</param>
1337+ [EditorBrowsable(EditorBrowsableState.Advanced)]
1338+ public void OnStart(string[] args)
1339+ {
1340+ // Ensure required components are present.
1341+ if (m_parentService == null)
1342+ throw new InvalidOperationException("ParentService property of ServiceHelper component is not set");
1343+
1344+ if (m_remotingServer == null)
1345+ throw new InvalidOperationException("RemotingServer property of ServiceHelper component is not set");
1346+
1347+ OnServiceStarting(args);
1348+
1349+ lock (m_clientRequestHandlers)
1350+ {
1351+ m_clientRequestHandlers.Add(new ClientRequestHandler("Clients", "Displays list of clients connected to the service", ShowClients));
1352+ m_clientRequestHandlers.Add(new ClientRequestHandler("Settings", "Displays queryable service settings from config file", ShowSettings));
1353+ m_clientRequestHandlers.Add(new ClientRequestHandler("Processes", "Displays list of service or system processes", ShowProcesses));
1354+ m_clientRequestHandlers.Add(new ClientRequestHandler("Schedules", "Displays list of process schedules defined in the service", ShowSchedules));
1355+ m_clientRequestHandlers.Add(new ClientRequestHandler("History", "Displays list of requests received from the clients", ShowRequestHistory));
1356+ m_clientRequestHandlers.Add(new ClientRequestHandler("Help", "Displays list of commands supported by the service", ShowRequestHelp));
1357+ m_clientRequestHandlers.Add(new ClientRequestHandler("Status", "Displays the current service status", ShowServiceStatus));
1358+ m_clientRequestHandlers.Add(new ClientRequestHandler("Start", "Start a service or system process", StartProcess));
1359+ m_clientRequestHandlers.Add(new ClientRequestHandler("Abort", "Aborts a service or system process", AbortProcess));
1360+ m_clientRequestHandlers.Add(new ClientRequestHandler("ReloadCryptoCache", "Reloads local cryptography cache", ReloadCryptoCache));
1361+ m_clientRequestHandlers.Add(new ClientRequestHandler("UpdateSettings", "Updates service setting in the config file", UpdateSettings));
1362+ m_clientRequestHandlers.Add(new ClientRequestHandler("ReloadSettings", "Reloads services settings from the config file", ReloadSettings));
1363+ m_clientRequestHandlers.Add(new ClientRequestHandler("Reschedule", "Reschedules a process defined in the service", RescheduleProcess));
1364+ m_clientRequestHandlers.Add(new ClientRequestHandler("Unschedule", "Unschedules a process defined in the service", UnscheduleProcess));
1365+ m_clientRequestHandlers.Add(new ClientRequestHandler("SaveSchedules", "Saves process schedules to the config file", SaveSchedules));
1366+ m_clientRequestHandlers.Add(new ClientRequestHandler("LoadSchedules", "Loads process schedules from the config file", LoadSchedules));
1367+ m_clientRequestHandlers.Add(new ClientRequestHandler("Version", "Displays current service version", ShowVersion));
1368+ m_clientRequestHandlers.Add(new ClientRequestHandler("Time", "Displays current system time", ShowTime));
1369+
1370+ // Enable telnet support if requested.
1371+ if (m_supportTelnetSessions)
1372+ {
1373+ m_clientRequestHandlers.Add(new ClientRequestHandler("Telnet", "Allows for a telnet session to the service server", RemoteTelnetSession, false));
1374+ }
1375+
1376+ // Enable health monitoring if requested.
1377+ if (m_monitorServiceHealth)
1378+ {
1379+ m_performanceMonitor = new PerformanceMonitor(m_healthMonitorInterval * 1000.0D, true);
1380+ m_clientRequestHandlers.Add(new ClientRequestHandler("Health", "Displays a report of resource utilization for the service", ShowHealthReport));
1381+ m_clientRequestHandlers.Add(new ClientRequestHandler("ResetHealthMonitor", "Resets the system resource utilization monitor", ResetHealthMonitor));
1382+ }
1383+ }
1384+
1385+ // Add internal components as service components by default.
1386+ lock (m_serviceComponents)
1387+ {
1388+ m_serviceComponents.Add(m_processScheduler);
1389+ m_serviceComponents.Add(m_statusLog);
1390+ m_serviceComponents.Add(m_errorLogger);
1391+ m_serviceComponents.Add(m_errorLogger.ErrorLog);
1392+ m_serviceComponents.Add(m_remotingServer);
1393+ m_serviceComponents.Add(m_statusUpdateQueue);
1394+ }
1395+
1396+ // Open log file if file logging is enabled.
1397+ if (m_logStatusUpdates)
1398+ m_statusLog.Open();
1399+
1400+ // Start all of the core components.
1401+ m_statusUpdateQueue.Start();
1402+ m_processScheduler.Start();
1403+ m_remotingServer.Start();
1404+
1405+ m_enabled = true;
1406+ OnServiceStarted();
1407+ }
1408+
1409+ /// <summary>
1410+ /// To be called from the <see cref="ServiceBase.OnStop()"/> method of <see cref="ParentService"/>.
1411+ /// </summary>
1412+ [EditorBrowsable(EditorBrowsableState.Advanced)]
1413+ public void OnStop()
1414+ {
1415+ OnServiceStopping();
1416+
1417+ // Abort any processes that may be currently executing.
1418+ foreach (ServiceProcess process in m_processes)
1419+ {
1420+ if (process != null)
1421+ process.Abort();
1422+ }
1423+
1424+ m_enabled = false; // Mark as disabled.
1425+ m_suppressUpdates = true; // Suppress status updates.
1426+
1427+ OnServiceStopped();
1428+ }
1429+
1430+ /// <summary>
1431+ /// To be called from the <see cref="ServiceBase.OnPause()"/> method of <see cref="ParentService"/>.
1432+ /// </summary>
1433+ [EditorBrowsable(EditorBrowsableState.Advanced)]
1434+ public void OnPause()
1435+ {
1436+ OnServicePausing();
1437+
1438+ // Disable all service components when service is pausing
1439+ Enabled = false;
1440+
1441+ OnServicePaused();
1442+ }
1443+
1444+ /// <summary>
1445+ /// To be called from the <see cref="ServiceBase.OnContinue()"/> method of <see cref="ParentService"/>.
1446+ /// </summary>
1447+ [EditorBrowsable(EditorBrowsableState.Advanced)]
1448+ public void OnResume()
1449+ {
1450+ OnServiceResuming();
1451+
1452+ // Re-enable all service components when service is resuming
1453+ Enabled = true;
1454+
1455+ OnServiceResumed();
1456+ }
1457+
1458+ /// <summary>
1459+ /// To be called from the <see cref="ServiceBase.OnShutdown()"/> method of <see cref="ParentService"/>.
1460+ /// </summary>
1461+ [EditorBrowsable(EditorBrowsableState.Advanced)]
1462+ public void OnShutdown()
1463+ {
1464+ OnSystemShutdown();
1465+ OnStop();
1466+ }
1467+
1468+ /// <summary>
1469+ /// Sends the specified <paramref name="response"/> to all <see cref="RemoteClients"/>.
1470+ /// </summary>
1471+ /// <param name="response">The <see cref="ServiceResponse"/> to be sent to all <see cref="RemoteClients"/>.</param>
1472+ public void SendResponse(ServiceResponse response)
1473+ {
1474+ SendResponse(Guid.Empty, response);
1475+ }
1476+
1477+ /// <summary>
1478+ /// Sends the specified <paramref name="response"/> to the specified <paramref name="client"/> only.
1479+ /// </summary>
1480+ /// <param name="client">ID of the client to whom the <paramref name="response"/> is to be sent.</param>
1481+ /// <param name="response">The <see cref="ServiceResponse"/> to be sent to the <paramref name="client"/>.</param>
1482+ public void SendResponse(Guid client, ServiceResponse response)
1483+ {
1484+ try
1485+ {
1486+ if (client != Guid.Empty)
1487+ {
1488+ // Send message directly to specified client.
1489+ m_remotingServer.SendToAsync(client, response);
1490+ }
1491+ else
1492+ {
1493+ // Send message to all of the connected clients.
1494+ if (m_remoteCommandClientID == Guid.Empty)
1495+ {
1496+ lock (m_remoteClients)
1497+ {
1498+ foreach (ClientInfo clientInfo in m_remoteClients)
1499+ {
1500+ m_remotingServer.SendToAsync(clientInfo.ClientID, response);
1501+ }
1502+ }
1503+ }
1504+ }
1505+ }
1506+ catch (Exception ex)
1507+ {
1508+ // Log the exception.
1509+ m_errorLogger.Log(ex);
1510+ }
1511+ }
1512+
1513+ /// <summary>
1514+ /// Sends an actionable response to client along with an optional formatted message and attachment.
1515+ /// </summary>
1516+ /// <param name="requestInfo"><see cref="ClientRequestInfo"/> instance containing the client request.</param>
1517+ /// <param name="success">Flag that determines if this response to client request was a success.</param>
1518+ /// <param name="attachment">Attachment to send with response.</param>
1519+ /// <param name="status">Formatted status message to send with response.</param>
1520+ /// <param name="args">Arguments of the formatted status message.</param>
1521+ /// <remarks>
1522+ /// This method is used to send an actionable client response that can be used for responding to an event after a command has been issued.
1523+ /// </remarks>
1524+ public void SendActionableResponse(ClientRequestInfo requestInfo, bool success, object attachment = null, string status = null, params object[] args)
1525+ {
1526+ try
1527+ {
1528+ string responseType = requestInfo.Request.Command + (success ? ":Success" : ":Failure");
1529+ string message = "";
1530+
1531+ if (!string.IsNullOrWhiteSpace(status))
1532+ {
1533+ if (args.Length == 0)
1534+ message = status + "\r\n\r\n";
1535+ else
1536+ message = string.Format(status, args) + "\r\n\r\n";
1537+ }
1538+
1539+ ServiceResponse response = new ServiceResponse(responseType, message);
1540+
1541+ // Add any specified attachment to the service response
1542+ if (attachment != null)
1543+ response.Attachments.Add(attachment);
1544+
1545+ // Add original command arguments as an attachment
1546+ response.Attachments.Add(requestInfo.Request.Arguments);
1547+
1548+ // Send response to service
1549+ SendResponse(requestInfo.Sender.ClientID, response);
1550+ }
1551+ catch (Exception ex)
1552+ {
1553+ ErrorLogger.Log(ex);
1554+ UpdateStatus(UpdateType.Alarm, "Failed to send actionable client response with attachment due to an exception: " + ex.Message + "\r\n\r\n");
1555+ }
1556+ }
1557+
1558+ /// <summary>
1559+ /// Provides a status update to all <see cref="RemoteClients"/>.
1560+ /// </summary>
1561+ /// <param name="message">Text message to be transmitted to all <see cref="RemoteClients"/>.</param>
1562+ /// <param name="type">One of the <see cref="UpdateType"/> values.</param>
1563+ /// <param name="args">Arguments to be used for formatting the <paramref name="message"/>.</param>
1564+ public void UpdateStatus(UpdateType type, string message, params object[] args)
1565+ {
1566+ UpdateStatus(Guid.Empty, type, message, args);
1567+ }
1568+
1569+ /// <summary>
1570+ /// Provides a status update to the specified <paramref name="client"/>.
1571+ /// </summary>
1572+ /// <param name="client">ID of the client to whom the <paramref name="message"/> is to be sent.</param>
1573+ /// <param name="type">One of the <see cref="UpdateType"/> values.</param>
1574+ /// <param name="message">Text message to be transmitted to the <paramref name="client"/>.</param>
1575+ /// <param name="args">Arguments to be used for formatting the <paramref name="message"/>.</param>
1576+ public void UpdateStatus(Guid client, UpdateType type, string message, params object[] args)
1577+ {
1578+ if (!m_suppressUpdates)
1579+ {
1580+ // Queue the status update for processing.
1581+ m_statusUpdateQueue.Add(new StatusUpdate(client, type, string.Format(message, args)));
1582+ }
1583+ }
1584+
1585+ /// <summary>
1586+ /// Provides a line terminated status update to all <see cref="RemoteClients"/>.
1587+ /// </summary>
1588+ /// <param name="message">Text message to be transmitted to all <see cref="RemoteClients"/>.</param>
1589+ /// <param name="type">One of the <see cref="UpdateType"/> values.</param>
1590+ /// <param name="args">Arguments to be used for formatting the <paramref name="message"/>.</param>
1591+ public void UpdateStatusAppendLine(UpdateType type, string message, params object[] args)
1592+ {
1593+ UpdateStatusAppendLine(Guid.Empty, type, message, args);
1594+ }
1595+
1596+ /// <summary>
1597+ /// Provides a line terminated status update to the specified <paramref name="client"/>.
1598+ /// </summary>
1599+ /// <param name="client">ID of the client to whom the <paramref name="message"/> is to be sent.</param>
1600+ /// <param name="type">One of the <see cref="UpdateType"/> values.</param>
1601+ /// <param name="message">Text message to be transmitted to the <paramref name="client"/>.</param>
1602+ /// <param name="args">Arguments to be used for formatting the <paramref name="message"/>.</param>
1603+ public void UpdateStatusAppendLine(Guid client, UpdateType type, string message, params object[] args)
1604+ {
1605+ UpdateStatus(type, message + "\r\n", args);
1606+ }
1607+
1608+ /// <summary>
1609+ /// Adds a new <see cref="ServiceProcess"/> to the <see cref="ServiceHelper"/>.
1610+ /// </summary>
1611+ /// <param name="processExecutionMethod">The <see cref="Delegate"/> to be invoked the <see cref="ServiceProcess"/> is started.</param>
1612+ /// <param name="processName">Name of the <see cref="ServiceProcess"/> being added.</param>
1613+ /// <returns>true if the <see cref="ServiceProcess"/> does not already exist and is added, otherwise false.</returns>
1614+ public bool AddProcess(Action<string, object[]> processExecutionMethod, string processName)
1615+ {
1616+ return AddProcess(processExecutionMethod, processName, null);
1617+ }
1618+
1619+ /// <summary>
1620+ /// Adds a new <see cref="ServiceProcess"/> to the <see cref="ServiceHelper"/>.
1621+ /// </summary>
1622+ /// <param name="processExecutionMethod">The <see cref="Delegate"/> to be invoked the <see cref="ServiceProcess"/> is started.</param>
1623+ /// <param name="processName">Name of the <see cref="ServiceProcess"/> being added.</param>
1624+ /// <param name="processArguments">Arguments to be passed in to the <paramref name="processExecutionMethod"/> during execution.</param>
1625+ /// <returns>true if the <see cref="ServiceProcess"/> does not already exist and is added, otherwise false.</returns>
1626+ public bool AddProcess(Action<string, object[]> processExecutionMethod, string processName, object[] processArguments)
1627+ {
1628+ processName = processName.Trim();
1629+ if (FindProcess(processName) == null)
1630+ {
1631+ ServiceProcess process = new ServiceProcess(processExecutionMethod, processName, processArguments);
1632+ process.StateChanged += Process_StateChanged;
1633+
1634+ lock (m_processes)
1635+ {
1636+ m_processes.Add(process);
1637+ }
1638+
1639+ return true;
1640+ }
1641+
1642+ return false;
1643+ }
1644+
1645+ /// <summary>
1646+ /// Removes an existing <see cref="ServiceProcess"/> from the <see cref="ServiceHelper"/>.
1647+ /// </summary>
1648+ /// <param name="processName">Name of the <see cref="ServiceProcess"/> to be removed.</param>
1649+ /// <returns>true if the <see cref="ServiceProcess"/> exists and is removed, otherwise false.</returns>
1650+ public bool RemoveProcess(string processName)
1651+ {
1652+ ServiceProcess process = FindProcess(processName.Trim());
1653+ if (process != null)
1654+ {
1655+ process.StateChanged -= Process_StateChanged;
1656+ lock (m_processes)
1657+ {
1658+ m_processes.Remove(process);
1659+ }
1660+
1661+ return true;
1662+ }
1663+
1664+ return false;
1665+ }
1666+
1667+ /// <summary>
1668+ /// Adds a new <see cref="ServiceProcess"/> to the <see cref="ServiceHelper"/> that executes on a schedule.
1669+ /// </summary>
1670+ /// <param name="processExecutionMethod">The <see cref="Delegate"/> to be invoked the <see cref="ServiceProcess"/> is started.</param>
1671+ /// <param name="processName">Name of the <see cref="ServiceProcess"/> being added.</param>
1672+ /// <param name="processSchedule"><see cref="Schedule"/> for the execution of the <see cref="ServiceProcess"/>.</param>
1673+ /// <returns>true if the <see cref="ServiceProcess"/> does not exist already and is added and scheduled, otherwise false.</returns>
1674+ public bool AddScheduledProcess(Action<string, object[]> processExecutionMethod, string processName, string processSchedule)
1675+ {
1676+ return AddScheduledProcess(processExecutionMethod, processName, null, processSchedule);
1677+ }
1678+
1679+ /// <summary>
1680+ /// Adds a new <see cref="ServiceProcess"/> to the <see cref="ServiceHelper"/> that executes on a schedule.
1681+ /// </summary>
1682+ /// <param name="processExecutionMethod">The <see cref="Delegate"/> to be invoked the <see cref="ServiceProcess"/> is started.</param>
1683+ /// <param name="processName">Name of the <see cref="ServiceProcess"/> being added.</param>
1684+ /// <param name="processArguments">Arguments to be passed in to the <paramref name="processExecutionMethod"/> during execution.</param>
1685+ /// <param name="processSchedule"><see cref="Schedule"/> for the execution of the <see cref="ServiceProcess"/>.</param>
1686+ /// <returns>true if the <see cref="ServiceProcess"/> does not exist already and is added and scheduled, otherwise false.</returns>
1687+ public bool AddScheduledProcess(Action<string, object[]> processExecutionMethod, string processName, object[] processArguments, string processSchedule)
1688+ {
1689+ if (AddProcess(processExecutionMethod, processName, processArguments))
1690+ return ScheduleProcess(processName, processSchedule);
1691+
1692+ return false;
1693+ }
1694+
1695+ /// <summary>
1696+ /// Removes an existing <see cref="ServiceProcess"/> from the <see cref="ServiceHelper"/> that is scheduled for automatic execution.
1697+ /// </summary>
1698+ /// <param name="processName">Name of the scheduled <see cref="ServiceProcess"/> to be removed.</param>
1699+ /// <returns>true is the scheduled <see cref="ServiceProcess"/> is removed, otherwise false.</returns>
1700+ public bool RemoveScheduledProcess(string processName)
1701+ {
1702+ if (UnscheduleProcess(processName))
1703+ return RemoveProcess(processName);
1704+
1705+ return false;
1706+ }
1707+
1708+ /// <summary>
1709+ /// Schedules an existing <see cref="ServiceProcess"/> for automatic execution.
1710+ /// </summary>
1711+ /// <param name="processName">Name of the <see cref="ServiceProcess"/> to be scheduled.</param>
1712+ /// <param name="scheduleRule">Rule that defines the execution pattern of the <see cref="ServiceProcess"/>.</param>
1713+ /// <returns>true if the <see cref="ServiceProcess"/> exists and is scheduled, otherwise false.</returns>
1714+ public bool ScheduleProcess(string processName, string scheduleRule)
1715+ {
1716+ return ScheduleProcess(processName, scheduleRule, false);
1717+ }
1718+
1719+ /// <summary>
1720+ /// Schedules an existing <see cref="ServiceProcess"/> for automatic execution.
1721+ /// </summary>
1722+ /// <param name="processName">Name of the <see cref="ServiceProcess"/> to be scheduled.</param>
1723+ /// <param name="scheduleRule">Rule that defines the execution pattern of the <see cref="ServiceProcess"/>.</param>
1724+ /// <param name="updateExistingSchedule">true if the <see cref="ServiceProcess"/> is to be re-scheduled; otherwise false.</param>
1725+ /// <returns>true if the <see cref="ServiceProcess"/> exists and is scheduled or re-scheduled, otherwise false.</returns>
1726+ public bool ScheduleProcess(string processName, string scheduleRule, bool updateExistingSchedule)
1727+ {
1728+ processName = processName.Trim();
1729+ if (FindProcess(processName) != null)
1730+ {
1731+ // The specified process exists, so we'll schedule it, or update its schedule if it is acheduled already.
1732+ Schedule existingSchedule = m_processScheduler.FindSchedule(processName);
1733+
1734+ if (existingSchedule != null)
1735+ {
1736+ // Update the process schedule if it is already exists.
1737+ if (updateExistingSchedule)
1738+ {
1739+ existingSchedule.Rule = scheduleRule;
1740+ return true;
1741+ }
1742+ }
1743+ else
1744+ {
1745+ // Schedule the process if it is not scheduled already.
1746+ m_processScheduler.Schedules.Add(new Schedule(processName, scheduleRule));
1747+ return true;
1748+ }
1749+ }
1750+
1751+ return false;
1752+ }
1753+
1754+ /// <summary>
1755+ /// Unschedules an existing <see cref="ServiceProcess"/> scheduled for automatic execution.
1756+ /// </summary>
1757+ /// <param name="processName">Name of the <see cref="ServiceProcess"/> to be unscheduled.</param>
1758+ /// <returns>true if the scheduled <see cref="ServiceProcess"/> is unscheduled, otherwise false.</returns>
1759+ public bool UnscheduleProcess(string processName)
1760+ {
1761+ return m_processScheduler.RemoveSchedule(processName.Trim());
1762+ }
1763+
1764+ /// <summary>
1765+ /// Returns the <see cref="ServiceProcess"/> for the specified <paramref name="processName"/>.
1766+ /// </summary>
1767+ /// <param name="processName">Name of the <see cref="ServiceProcess"/> to be retrieved.</param>
1768+ /// <returns><see cref="ServiceProcess"/> object if found; otherwise null.</returns>
1769+ public ServiceProcess FindProcess(string processName)
1770+ {
1771+ lock (m_processes)
1772+ {
1773+ return m_processes.Find(process => string.Compare(process.Name, processName, true) == 0);
1774+ }
1775+ }
1776+
1777+ /// <summary>
1778+ /// Returns the <see cref="ClientInfo"/> object for the specified <paramref name="clientID"/>.
1779+ /// </summary>
1780+ /// <param name="clientID">ID of the client whose <see cref="ClientInfo"/> object is to be retrieved.</param>
1781+ /// <returns><see cref="ClientInfo"/> object if found; otherwise null.</returns>
1782+ public ClientInfo FindConnectedClient(Guid clientID)
1783+ {
1784+ lock (m_remoteClients)
1785+ {
1786+ return m_remoteClients.Find(clientInfo => clientInfo.ClientID == clientID);
1787+ }
1788+ }
1789+
1790+ /// <summary>
1791+ /// Returns the <see cref="ClientRequestHandler"/> object for the specified <paramref name="handlerCommand"/>.
1792+ /// </summary>
1793+ /// <param name="handlerCommand">Request type whose <see cref="ClientRequestHandler"/> object is to be retrieved.</param>
1794+ /// <returns><see cref="ClientRequestHandler"/> object if found; otherwise null.</returns>
1795+ public ClientRequestHandler FindClientRequestHandler(string handlerCommand)
1796+ {
1797+ lock (m_clientRequestHandlers)
1798+ {
1799+ return m_clientRequestHandlers.Find(handler => string.Compare(handler.Command, handlerCommand, true) == 0);
1800+ }
1801+ }
1802+
1803+ /// <summary>
1804+ /// Raises the <see cref="ServiceStarting"/> event.
1805+ /// </summary>
1806+ /// <param name="args">Arguments to be sent to <see cref="ServiceStarting"/> event.</param>
1807+ protected virtual void OnServiceStarting(string[] args)
1808+ {
1809+ // Notify service event consumers of pending service start
1810+ if (ServiceStarting != null)
1811+ ServiceStarting(this, new EventArgs<string[]>(args));
1812+ }
1813+
1814+ /// <summary>
1815+ /// Raises the <see cref="ServiceStarted"/> event.
1816+ /// </summary>
1817+ protected virtual void OnServiceStarted()
1818+ {
1819+ // Notify all remote clients that might possibly be connected at of service start (not likely)
1820+ SendServiceStateChangedResponse(ServiceState.Started);
1821+
1822+ // Notify service event consumers that service has started
1823+ if (ServiceStarted != null)
1824+ ServiceStarted(this, EventArgs.Empty);
1825+ }
1826+
1827+ /// <summary>
1828+ /// Raises the <see cref="ServiceStopping"/> event.
1829+ /// </summary>
1830+ protected virtual void OnServiceStopping()
1831+ {
1832+ // Notify service event consumers of pending service stop
1833+ if (ServiceStopping != null)
1834+ ServiceStopping(this, EventArgs.Empty);
1835+
1836+ // Notify all remote clients of service stop
1837+ SendServiceStateChangedResponse(ServiceState.Stopped);
1838+ }
1839+
1840+ /// <summary>
1841+ /// Raises the <see cref="ServiceStopped"/> event.
1842+ /// </summary>
1843+ protected virtual void OnServiceStopped()
1844+ {
1845+ // Notify service event consumers that service has stopped
1846+ if (ServiceStopped != null)
1847+ ServiceStopped(this, EventArgs.Empty);
1848+ }
1849+
1850+ /// <summary>
1851+ /// Raises the <see cref="ServicePausing"/> event.
1852+ /// </summary>
1853+ protected virtual void OnServicePausing()
1854+ {
1855+ // Notify service event consumers of pending service stop
1856+ if (ServicePausing != null)
1857+ ServicePausing(this, EventArgs.Empty);
1858+
1859+ // Notify all remote clients of service pause
1860+ SendServiceStateChangedResponse(ServiceState.Paused);
1861+ }
1862+
1863+ /// <summary>
1864+ /// Raises the <see cref="ServicePaused"/> event.
1865+ /// </summary>
1866+ protected virtual void OnServicePaused()
1867+ {
1868+ // Notify service event consumers that service has been paused
1869+ if (ServicePaused != null)
1870+ ServicePaused(this, EventArgs.Empty);
1871+ }
1872+
1873+ /// <summary>
1874+ /// Raises the <see cref="ServiceResuming"/> event.
1875+ /// </summary>
1876+ protected virtual void OnServiceResuming()
1877+ {
1878+ // Notify service event consumers of pending service resume
1879+ if (ServiceResuming != null)
1880+ ServiceResuming(this, EventArgs.Empty);
1881+ }
1882+
1883+ /// <summary>
1884+ /// Raises the <see cref="ServiceResumed"/> event.
1885+ /// </summary>
1886+ protected virtual void OnServiceResumed()
1887+ {
1888+ // Notify all remote clients of service resume
1889+ SendServiceStateChangedResponse(ServiceState.Resumed);
1890+
1891+ // Notify service event consumers that service has been resumed
1892+ if (ServiceResumed != null)
1893+ ServiceResumed(this, EventArgs.Empty);
1894+ }
1895+
1896+ /// <summary>
1897+ /// Raises the <see cref="SystemShutdown"/> event.
1898+ /// </summary>
1899+ protected virtual void OnSystemShutdown()
1900+ {
1901+ // Notify service event consumers of pending service shutdown
1902+ SendServiceStateChangedResponse(ServiceState.Shutdown);
1903+
1904+ // Notify service event consumers that service has shutdown
1905+ if (SystemShutdown != null)
1906+ SystemShutdown(this, EventArgs.Empty);
1907+ }
1908+
1909+ /// <summary>
1910+ /// Raises the <see cref="ReceivedClientRequest"/> event.
1911+ /// </summary>
1912+ /// <param name="request">The <see cref="ClientRequest"/> that was received.</param>
1913+ /// <param name="requestSender">The <see cref="ClientInfo"/> object of the <paramref name="request"/> sender.</param>
1914+ protected virtual void OnReceivedClientRequest(ClientRequest request, ClientInfo requestSender)
1915+ {
1916+ if (ReceivedClientRequest != null)
1917+ ReceivedClientRequest(this, new EventArgs<Guid, ClientRequest>(requestSender.ClientID, request));
1918+ }
1919+
1920+ /// <summary>
1921+ /// Raises the <see cref="ProcessStateChanged"/> event.
1922+ /// </summary>
1923+ /// <param name="processName">Name of the <see cref="ServiceProcess"/> whose state changed.</param>
1924+ /// <param name="processState">New <see cref="ServiceProcessState"/> of the <see cref="ServiceProcess"/>.</param>
1925+ protected virtual void OnProcessStateChanged(string processName, ServiceProcessState processState)
1926+ {
1927+ // Notify all service event consumer of change in process state
1928+ if (ProcessStateChanged != null)
1929+ ProcessStateChanged(this, new EventArgs<string, ServiceProcessState>(processName, processState));
1930+
1931+ // Notify all remote clients of change in process state
1932+ SendProcessStateChangedResponse(processName, processState);
1933+ }
1934+
1935+ /// <summary>
1936+ /// Releases the unmanaged resources used by the <see cref="ServiceHelper"/> object and optionally releases the managed resources.
1937+ /// </summary>
1938+ /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
1939+ protected override void Dispose(bool disposing)
1940+ {
1941+ if (!m_disposed)
1942+ {
1943+ try
1944+ {
1945+ // This will be done regardless of whether the object is finalized or disposed.
1946+ if (disposing)
1947+ {
1948+ // This will be done only when the object is disposed by calling Dispose().
1949+ SaveSettings();
1950+
1951+ if (m_statusLog != null)
1952+ {
1953+ m_statusLog.LogException -= StatusLog_LogException;
1954+ m_statusLog.Dispose();
1955+ }
1956+
1957+ if (m_processScheduler != null)
1958+ {
1959+ m_processScheduler.ScheduleDue -= Scheduler_ScheduleDue;
1960+ m_processScheduler.Dispose();
1961+ }
1962+
1963+ if (m_errorLogger != null)
1964+ {
1965+ m_errorLogger.LoggingException -= ErrorLogger_LoggingException;
1966+ m_errorLogger.Dispose();
1967+ }
1968+
1969+ if (m_performanceMonitor != null)
1970+ {
1971+ m_performanceMonitor.Dispose();
1972+ }
1973+
1974+ if (m_remoteCommandProcess != null)
1975+ {
1976+ m_remoteCommandProcess.ErrorDataReceived -= RemoteCommandProcess_ErrorDataReceived;
1977+ m_remoteCommandProcess.OutputDataReceived -= RemoteCommandProcess_OutputDataReceived;
1978+
1979+ if (!m_remoteCommandProcess.HasExited)
1980+ m_remoteCommandProcess.Kill();
1981+
1982+ m_remoteCommandProcess.Dispose();
1983+ }
1984+
1985+ if (m_statusUpdateQueue != null)
1986+ {
1987+ m_statusUpdateQueue.ProcessException -= StatusUpdateQueue_ProcessException;
1988+ m_statusUpdateQueue.Flush();
1989+ m_statusUpdateQueue.Dispose();
1990+ }
1991+
1992+ // Service processes are created and owned by remoting server, so we dispose them
1993+ if (m_processes != null)
1994+ {
1995+ lock (m_processes)
1996+ {
1997+ foreach (ServiceProcess process in m_processes)
1998+ {
1999+ process.StateChanged -= Process_StateChanged;
2000+ process.Dispose();
2001+ }
2002+
2003+ m_processes.Clear();
2004+ }
2005+ }
2006+
2007+ // Detach any remoting server events, we don't own this component so we don't dispose it
2008+ RemotingServer = null;
2009+ }
2010+ }
2011+ finally
2012+ {
2013+ m_disposed = true; // Prevent duplicate dispose.
2014+ base.Dispose(disposing); // Call base class Dispose().
2015+ }
2016+ }
2017+ }
2018+
2019+ private bool VerifySecurity(ClientInfo client)
2020+ {
2021+ // Set current thread principal to remote client's user principal.
2022+ if (!(Thread.CurrentPrincipal is WindowsPrincipal))
2023+ Thread.CurrentPrincipal = client.ClientUser;
2024+
2025+ // Retrieve previously initialized security provider of the remote client's user.
2026+ if (SecurityProviderCache.CurrentProvider == null)
2027+ SecurityProviderCache.CurrentProvider = SecurityProviderUtility.CreateProvider(string.Empty);
2028+
2029+ // Initialize security provider for the remote client's user from specified credentials.
2030+ if (!Thread.CurrentPrincipal.Identity.IsAuthenticated &&
2031+ !string.IsNullOrEmpty(client.ClientUserCredentials))
2032+ {
2033+ string[] credentialParts = client.ClientUserCredentials.Split(':');
2034+ if (credentialParts.Length == 2)
2035+ {
2036+ ISecurityProvider provider = SecurityProviderUtility.CreateProvider(credentialParts[0]);
2037+ provider.Initialize();
2038+ if (provider.Authenticate(credentialParts[1]))
2039+ SecurityProviderCache.CurrentProvider = provider;
2040+ }
2041+ }
2042+
2043+ // Save the initialized security provider of remote client's user for subsequent uses.
2044+ if (client.ClientUser != Thread.CurrentPrincipal)
2045+ client.SetClientUser(Thread.CurrentPrincipal);
2046+
2047+ return client.ClientUser.Identity.IsAuthenticated;
2048+ }
2049+
2050+ private void ProcessStatusUpdates(StatusUpdate[] items)
2051+ {
2052+ bool displayUpdate = false;
2053+ int suppressedUpdates = 0;
2054+ foreach (StatusUpdate item in items)
2055+ {
2056+ if ((DateTime.Now.Ticks - m_lastStatusUpdateTime).ToSeconds() > 1)
2057+ {
2058+ displayUpdate = true;
2059+ m_statusUpdatesDisplayed = 1;
2060+ m_lastStatusUpdateTime = DateTime.Now.Ticks;
2061+ }
2062+ else
2063+ {
2064+ displayUpdate = m_statusUpdatesDisplayed <= m_maxStatusUpdatesFrequency;
2065+ m_statusUpdatesDisplayed++;
2066+ }
2067+
2068+ // Send the status update to specified client(s).
2069+ if (displayUpdate)
2070+ {
2071+ if (item.Message.Length <= m_maxStatusUpdatesLength)
2072+ {
2073+ SendUpdateClientStatusResponse(item.Client, item.Type, item.Message);
2074+ }
2075+ else
2076+ {
2077+ SendUpdateClientStatusResponse(item.Client, item.Type, item.Message.TruncateRight(m_maxStatusUpdatesLength));
2078+ SendUpdateClientStatusResponse(item.Client, UpdateType.Warning, string.Format("\r\nSuppressed {0:N0} status update character(s) from being displayed to avoid flooding.\r\n\r\n", item.Message.Length - m_maxStatusUpdatesLength));
2079+ }
2080+ }
2081+ else
2082+ {
2083+ suppressedUpdates++;
2084+ }
2085+
2086+ // Log the status update to the log file if logging is enabled.
2087+ if (m_logStatusUpdates)
2088+ m_statusLog.WriteTimestampedLine(item.Message);
2089+ }
2090+
2091+ if (suppressedUpdates > 0)
2092+ SendUpdateClientStatusResponse(Guid.Empty, UpdateType.Warning, string.Format("\r\nSuppressed {0:N0} status update(s) from being displayed to avoid flooding.\r\n\r\n", suppressedUpdates));
2093+ }
2094+
2095+ private void SendAuthenticationSuccessResponse(Guid clientID)
2096+ {
2097+ SendResponse(clientID, new ServiceResponse("AuthenticationSuccess"));
2098+ }
2099+
2100+ private void SendAuthenticationFailureResponse(Guid clientID)
2101+ {
2102+ SendResponse(clientID, new ServiceResponse("AuthenticationFailure"));
2103+ }
2104+
2105+ private void SendUpdateClientStatusResponse(Guid clientID, UpdateType type, string response)
2106+ {
2107+ ServiceResponse serviceResponse = new ServiceResponse();
2108+ serviceResponse.Type = "UPDATECLIENTSTATUS-" + type.ToString().ToUpper();
2109+ serviceResponse.Message = response;
2110+ SendResponse(clientID, serviceResponse);
2111+ }
2112+
2113+ private void SendServiceStateChangedResponse(ServiceState currentState)
2114+ {
2115+ ServiceResponse serviceResponse = new ServiceResponse("SERVICESTATECHANGED");
2116+ serviceResponse.Attachments.Add(new ObjectState<ServiceState>(Name, currentState));
2117+ SendResponse(serviceResponse);
2118+ }
2119+
2120+ private void SendProcessStateChangedResponse(string processName, ServiceProcessState currentState)
2121+ {
2122+ ServiceResponse serviceResponse = new ServiceResponse("PROCESSSTATECHANGED");
2123+ serviceResponse.Attachments.Add(new ObjectState<ServiceProcessState>(processName, currentState));
2124+ SendResponse(serviceResponse);
2125+ }
2126+
2127+ private void Process_StateChanged(object sender, EventArgs e)
2128+ {
2129+ ServiceProcess process = sender as ServiceProcess;
2130+
2131+ if ((object)process == null)
2132+ return; // Avoiding Null Derefenrence Exception
2133+
2134+ OnProcessStateChanged(process.Name, process.CurrentState);
2135+ }
2136+
2137+ private void Scheduler_ScheduleDue(object sender, EventArgs<Schedule> e)
2138+ {
2139+ ServiceProcess scheduledProcess = FindProcess(e.Argument.Name);
2140+
2141+ // Start the process execution if it exists.
2142+ if ((object)scheduledProcess != null)
2143+ scheduledProcess.Start();
2144+ }
2145+
2146+ private void StatusUpdateQueue_ProcessException(object sender, EventArgs<Exception> e)
2147+ {
2148+ m_errorLogger.Log(e.Argument);
2149+ }
2150+
2151+ private void StatusLog_LogException(object sender, EventArgs<System.Exception> e)
2152+ {
2153+ // We'll let the connected clients know that we encountered an exception while logging the status update.
2154+ m_logStatusUpdates = false;
2155+ UpdateStatus(UpdateType.Alarm, "Error occurred while logging status update - {0}\r\n\r\n", e.Argument.Message);
2156+ m_logStatusUpdates = true;
2157+ }
2158+
2159+ private void ErrorLogger_LoggingException(object sender, EventArgs<Exception> e)
2160+ {
2161+ UpdateStatus(UpdateType.Alarm, "Error occurred while logging an error - {0}\r\n\r\n", e.Argument.Message);
2162+ }
2163+
2164+ private void RemotingServer_ClientDisconnected(object sender, EventArgs<Guid> e)
2165+ {
2166+ ClientInfo disconnectedClient = FindConnectedClient(e.Argument);
2167+
2168+ if (disconnectedClient != null)
2169+ {
2170+ if (e.Argument == m_remoteCommandClientID)
2171+ {
2172+ try
2173+ {
2174+ RemoteTelnetSession(new ClientRequestInfo(disconnectedClient, ClientRequest.Parse("Telnet -disconnect")));
2175+ }
2176+ catch (Exception ex)
2177+ {
2178+ // We'll encounter an exception because we'll try to update the status of the client that had the
2179+ // remote command session open and this will fail since the client is disconnected.
2180+ m_errorLogger.Log(ex);
2181+ }
2182+ }
2183+
2184+ lock (m_remoteClients)
2185+ {
2186+ m_remoteClients.Remove(disconnectedClient);
2187+ }
2188+ UpdateStatus(UpdateType.Information, "Remote client disconnected - {0} from {1}.\r\n\r\n", disconnectedClient.ClientUser.Identity.Name, disconnectedClient.MachineName);
2189+ }
2190+ }
2191+
2192+ private void RemotingServer_ReceiveClientDataComplete(object sender, EventArgs<Guid, byte[], int> e)
2193+ {
2194+ ClientInfo requestSender = FindConnectedClient(e.Argument1);
2195+ if (requestSender == null)
2196+ {
2197+ // First message from a remote client should be its info.
2198+ ClientInfo client = null;
2199+ Serialization.TryDeserialize<ClientInfo>(e.Argument2.BlockCopy(0, e.Argument3), SerializationFormat.Binary, out client);
2200+ try
2201+ {
2202+ if (client != null)
2203+ {
2204+ client.ClientID = e.Argument1;
2205+ client.ConnectedAt = DateTime.Now;
2206+
2207+ // Engage security for the remote client connection if configured.
2208+ if (!m_secureRemoteInteractions || (m_secureRemoteInteractions && VerifySecurity(client)))
2209+ {
2210+ lock (m_remoteClients)
2211+ {
2212+ m_remoteClients.Add(client);
2213+ }
2214+ SendAuthenticationSuccessResponse(client.ClientID);
2215+ UpdateStatus(UpdateType.Information, "Remote client connected - {0} from {1}.\r\n\r\n", client.ClientUser.Identity.Name, client.MachineName);
2216+ }
2217+ else
2218+ {
2219+ throw new SecurityException(string.Format("Authentication failed for user '{0}'", Thread.CurrentPrincipal.Identity.Name));
2220+ }
2221+ }
2222+ else
2223+ {
2224+ // Required client information is missing.
2225+ throw new SecurityException("Remote client failed to transmit the required information");
2226+ }
2227+ }
2228+ catch (Exception ex)
2229+ {
2230+ try
2231+ {
2232+ SendResponse(e.Argument1, new ServiceResponse("AuthenticationFailure"));
2233+ m_remotingServer.DisconnectOne(e.Argument1);
2234+
2235+ UpdateStatus(UpdateType.Warning, "Remote client connection rejected - {0} from {1}.\r\n\r\n", client.ClientUser.Identity.Name, client.MachineName);
2236+ m_errorLogger.Log(ex);
2237+ }
2238+ catch
2239+ {
2240+ }
2241+ }
2242+ }
2243+ else
2244+ {
2245+ // All subsequest messages from a remote client would be requests.
2246+ ClientRequest request = null;
2247+ Serialization.TryDeserialize<ClientRequest>(e.Argument2.BlockCopy(0, e.Argument3), SerializationFormat.Binary, out request);
2248+ if (request != null)
2249+ {
2250+ try
2251+ {
2252+ ClientRequestInfo requestInfo = new ClientRequestInfo(requestSender, request);
2253+
2254+ if (m_remoteCommandClientID == Guid.Empty)
2255+ {
2256+ // Process incoming requests when remote command session is not in progress.
2257+ lock (m_clientRequestHistory)
2258+ {
2259+ m_clientRequestHistory.Add(requestInfo);
2260+ if (m_clientRequestHistory.Count > m_requestHistoryLimit)
2261+ m_clientRequestHistory.RemoveRange(0, (m_clientRequestHistory.Count - m_requestHistoryLimit));
2262+ }
2263+
2264+ // Check if remote client has permission to invoke the requested command.
2265+ if (m_secureRemoteInteractions && VerifySecurity(requestInfo.Sender) &&
2266+ SecurityProviderUtility.IsResourceSecurable(requestInfo.Request.ToString()) &&
2267+ !SecurityProviderUtility.IsResourceAccessible(requestInfo.Request.ToString()))
2268+ throw new SecurityException(string.Format("Access to '{0}' is denied", requestInfo.Request.Command));
2269+
2270+ // Notify the consumer about the incoming request from client.
2271+ OnReceivedClientRequest(request, requestSender);
2272+
2273+ ClientRequestHandler requestHandler = FindClientRequestHandler(request.Command);
2274+ if (requestHandler != null)
2275+ {
2276+ // Request handler exists.
2277+ requestHandler.HandlerMethod(requestInfo);
2278+ }
2279+ else
2280+ {
2281+ // No request handler exists.
2282+ throw new InvalidOperationException("Request is not supported");
2283+ }
2284+ }
2285+ else if (requestSender.ClientID == m_remoteCommandClientID)
2286+ {
2287+ // Redirect requests to remote command session if requests are from its originator.
2288+ RemoteTelnetSession(requestInfo);
2289+ }
2290+ else
2291+ {
2292+ // Reject all request from other clients since remote command session is in progress.
2293+ throw new InvalidOperationException("Remote telnet session is in progress");
2294+ }
2295+ }
2296+ catch (Exception ex)
2297+ {
2298+ m_errorLogger.Log(ex);
2299+ UpdateStatus(requestSender.ClientID, UpdateType.Alarm, "Failed to process request \"{0}\" - {1}.\r\n\r\n", request.Command, ex.Message);
2300+ }
2301+ }
2302+ else
2303+ {
2304+ UpdateStatus(requestSender.ClientID, UpdateType.Alarm, "Failed to process request - Request could not be deserialized.\r\n\r\n");
2305+ }
2306+ }
2307+ }
2308+
2309+ private void RemoteCommandProcess_ErrorDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
2310+ {
2311+ UpdateStatus(m_remoteCommandClientID, UpdateType.Alarm, e.Data + "\r\n");
2312+ }
2313+
2314+ private void RemoteCommandProcess_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
2315+ {
2316+ UpdateStatus(m_remoteCommandClientID, UpdateType.Information, e.Data + "\r\n");
2317+ }
2318+
2319+ #region [ Client Request Handlers ]
2320+
2321+ private void ShowClients(ClientRequestInfo requestInfo)
2322+ {
2323+ if (requestInfo.Request.Arguments.ContainsHelpRequest)
2324+ {
2325+ StringBuilder helpMessage = new StringBuilder();
2326+
2327+ helpMessage.Append("Displays a list of clients currently connected to the service.");
2328+ helpMessage.AppendLine();
2329+ helpMessage.AppendLine();
2330+ helpMessage.Append(" Usage:");
2331+ helpMessage.AppendLine();
2332+ helpMessage.Append(" Clients -options");
2333+ helpMessage.AppendLine();
2334+ helpMessage.AppendLine();
2335+ helpMessage.Append(" Options:");
2336+ helpMessage.AppendLine();
2337+ helpMessage.Append(" -?".PadRight(20));
2338+ helpMessage.Append("Displays this help message");
2339+ helpMessage.AppendLine();
2340+ helpMessage.AppendLine();
2341+
2342+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
2343+ }
2344+ else
2345+ {
2346+ if (m_remoteClients.Count > 0)
2347+ {
2348+ // Display info about all of the clients connected to the service.
2349+ StringBuilder responseMessage = new StringBuilder();
2350+
2351+ responseMessage.AppendFormat("Clients connected to {0}:", Name);
2352+ responseMessage.AppendLine();
2353+ responseMessage.AppendLine();
2354+ responseMessage.Append("Client".PadRight(25));
2355+ responseMessage.Append(' ');
2356+ responseMessage.Append("Machine".PadRight(15));
2357+ responseMessage.Append(' ');
2358+ responseMessage.Append("User".PadRight(15));
2359+ responseMessage.Append(' ');
2360+ responseMessage.Append("Connected".PadRight(20));
2361+ responseMessage.AppendLine();
2362+ responseMessage.Append(new string('-', 25));
2363+ responseMessage.Append(' ');
2364+ responseMessage.Append(new string('-', 15));
2365+ responseMessage.Append(' ');
2366+ responseMessage.Append(new string('-', 15));
2367+ responseMessage.Append(' ');
2368+ responseMessage.Append(new string('-', 20));
2369+
2370+ lock (m_remoteClients)
2371+ {
2372+ foreach (ClientInfo clientInfo in m_remoteClients)
2373+ {
2374+ responseMessage.AppendLine();
2375+
2376+ if (!string.IsNullOrEmpty(clientInfo.ClientName))
2377+ responseMessage.Append(clientInfo.ClientName.PadRight(25));
2378+ else
2379+ responseMessage.Append("[Not Available]".PadRight(25));
2380+
2381+ responseMessage.Append(' ');
2382+ if (!string.IsNullOrEmpty(clientInfo.MachineName))
2383+ responseMessage.Append(clientInfo.MachineName.PadRight(15));
2384+ else
2385+ responseMessage.Append("[Not Available]".PadRight(15));
2386+
2387+ responseMessage.Append(' ');
2388+ if (!string.IsNullOrEmpty(clientInfo.ClientUser.Identity.Name))
2389+ responseMessage.Append(clientInfo.ClientUser.Identity.Name.PadRight(15));
2390+ else
2391+ responseMessage.Append("[Not Available]".PadRight(15));
2392+
2393+ responseMessage.Append(' ');
2394+ responseMessage.Append(clientInfo.ConnectedAt.ToString("MM/dd/yy hh:mm:ss tt").PadRight(20));
2395+ }
2396+ }
2397+ responseMessage.AppendLine();
2398+ responseMessage.AppendLine();
2399+
2400+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, responseMessage.ToString());
2401+ }
2402+ else
2403+ {
2404+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "No clients are connected to {0}\r\n\r\n", Name);
2405+ }
2406+ }
2407+ }
2408+
2409+ private void ShowSettings(ClientRequestInfo requestInfo)
2410+ {
2411+ if (requestInfo.Request.Arguments.ContainsHelpRequest)
2412+ {
2413+ StringBuilder helpMessage = new StringBuilder();
2414+
2415+ helpMessage.Append("Displays a list of service settings from the config file.");
2416+ helpMessage.AppendLine();
2417+ helpMessage.AppendLine();
2418+ helpMessage.Append(" Usage:");
2419+ helpMessage.AppendLine();
2420+ helpMessage.Append(" Settings -options");
2421+ helpMessage.AppendLine();
2422+ helpMessage.AppendLine();
2423+ helpMessage.Append(" Options:");
2424+ helpMessage.AppendLine();
2425+ helpMessage.Append(" -?".PadRight(20));
2426+ helpMessage.Append("Displays this help message");
2427+ helpMessage.AppendLine();
2428+ helpMessage.AppendLine();
2429+
2430+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
2431+ }
2432+ else
2433+ {
2434+ StringBuilder responseMessage = new StringBuilder();
2435+ responseMessage.AppendFormat("Settings for {0}:", Name);
2436+ responseMessage.AppendLine();
2437+ responseMessage.AppendLine();
2438+ responseMessage.Append("Category".PadRight(20));
2439+ responseMessage.Append(' ');
2440+ responseMessage.Append("Name".PadRight(25));
2441+ responseMessage.Append(' ');
2442+ responseMessage.Append("Value".PadRight(30));
2443+ responseMessage.AppendLine();
2444+ responseMessage.Append(new string('-', 20));
2445+ responseMessage.Append(' ');
2446+ responseMessage.Append(new string('-', 25));
2447+ responseMessage.Append(' ');
2448+ responseMessage.Append(new string('-', 30));
2449+
2450+ IPersistSettings typedComponent;
2451+ lock (m_serviceComponents)
2452+ {
2453+ foreach (object component in m_serviceComponents)
2454+ {
2455+ typedComponent = component as IPersistSettings;
2456+ if (typedComponent != null)
2457+ {
2458+ foreach (CategorizedSettingsElement setting in ConfigurationFile.Current.Settings[typedComponent.SettingsCategory])
2459+ {
2460+ // Skip encrypted settings for security purpose.
2461+ if (setting.Encrypted)
2462+ continue;
2463+
2464+ responseMessage.AppendLine();
2465+ responseMessage.Append(typedComponent.SettingsCategory.PadRight(20));
2466+ responseMessage.Append(' ');
2467+ responseMessage.Append(setting.Name.PadRight(25));
2468+ responseMessage.Append(' ');
2469+
2470+ if (!string.IsNullOrEmpty(setting.Value))
2471+ responseMessage.Append(setting.Value.PadRight(30));
2472+ else
2473+ responseMessage.Append("[Not Set]".PadRight(30));
2474+ }
2475+ }
2476+ }
2477+ }
2478+ responseMessage.AppendLine();
2479+ responseMessage.AppendLine();
2480+
2481+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, responseMessage.ToString());
2482+ }
2483+ }
2484+
2485+ private void ShowProcesses(ClientRequestInfo requestInfo)
2486+ {
2487+ if (requestInfo.Request.Arguments.ContainsHelpRequest)
2488+ {
2489+ bool showAdvancedHelp = requestInfo.Request.Arguments.Exists("advanced");
2490+
2491+ StringBuilder helpMessage = new StringBuilder();
2492+
2493+ helpMessage.Append("Displays a list of defined service processes or running system processes.");
2494+ helpMessage.AppendLine();
2495+ helpMessage.AppendLine();
2496+ helpMessage.Append(" Usage:");
2497+ helpMessage.AppendLine();
2498+ helpMessage.Append(" Processes -options");
2499+ helpMessage.AppendLine();
2500+ helpMessage.AppendLine();
2501+ helpMessage.Append(" Options:");
2502+ helpMessage.AppendLine();
2503+ helpMessage.Append(" -?".PadRight(20));
2504+ helpMessage.Append("Displays this help message");
2505+ if (m_supportSystemCommands && showAdvancedHelp)
2506+ {
2507+ helpMessage.AppendLine();
2508+ helpMessage.Append(" -system".PadRight(20));
2509+ helpMessage.Append("Displays system processes instead of service processes");
2510+ }
2511+ helpMessage.AppendLine();
2512+ helpMessage.AppendLine();
2513+
2514+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
2515+ }
2516+ else
2517+ {
2518+ bool listSystemProcesses = requestInfo.Request.Arguments.Exists("system");
2519+ if (listSystemProcesses && m_supportSystemCommands)
2520+ {
2521+ // Enumerate "system" processes when -system parameter is specified
2522+ StringBuilder responseMessage = new StringBuilder();
2523+
2524+ responseMessage.AppendFormat("Processes running on {0}:", Environment.MachineName);
2525+ responseMessage.AppendLine();
2526+ responseMessage.AppendLine();
2527+ responseMessage.Append("ID".PadRight(5));
2528+ responseMessage.Append(' ');
2529+ responseMessage.Append("Name".PadRight(25));
2530+ responseMessage.Append(' ');
2531+ responseMessage.Append("Priority".PadRight(15));
2532+ responseMessage.Append(' ');
2533+ responseMessage.Append("Responding".PadRight(10));
2534+ responseMessage.Append(' ');
2535+ responseMessage.Append("Start Time".PadRight(20));
2536+ responseMessage.AppendLine();
2537+ responseMessage.Append(new string('-', 5));
2538+ responseMessage.Append(' ');
2539+ responseMessage.Append(new string('-', 25));
2540+ responseMessage.Append(' ');
2541+ responseMessage.Append(new string('-', 15));
2542+ responseMessage.Append(' ');
2543+ responseMessage.Append(new string('-', 10));
2544+ responseMessage.Append(' ');
2545+ responseMessage.Append(new string('-', 20));
2546+
2547+ foreach (Process process in Process.GetProcesses())
2548+ {
2549+ try
2550+ {
2551+ responseMessage.Append(process.StartInfo.UserName);
2552+ responseMessage.AppendLine();
2553+ responseMessage.Append(process.Id.ToString().PadRight(5));
2554+ responseMessage.Append(' ');
2555+ responseMessage.Append(process.ProcessName.PadRight(25));
2556+ responseMessage.Append(' ');
2557+#if MONO
2558+ responseMessage.Append("Undetermined".PadRight(15));
2559+ responseMessage.Append(' ');
2560+ responseMessage.Append("N/A".PadRight(10));
2561+ responseMessage.Append(' ');
2562+#else
2563+ responseMessage.Append(process.PriorityClass.ToString().PadRight(15));
2564+ responseMessage.Append(' ');
2565+ responseMessage.Append((process.Responding ? "Yes" : "No").PadRight(10));
2566+ responseMessage.Append(' ');
2567+#endif
2568+ responseMessage.Append(process.StartTime.ToString("MM/dd/yy hh:mm:ss tt").PadRight(20));
2569+ }
2570+ catch
2571+ {
2572+ }
2573+ }
2574+ responseMessage.AppendLine();
2575+ responseMessage.AppendLine();
2576+
2577+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, responseMessage.ToString());
2578+ }
2579+ else
2580+ {
2581+ if (m_processes.Count > 0)
2582+ {
2583+ // Display info about all the processes defined in the service.
2584+ StringBuilder responseMessage = new StringBuilder();
2585+
2586+ responseMessage.AppendFormat("Processes defined in {0}:", Name);
2587+ responseMessage.AppendLine();
2588+ responseMessage.AppendLine();
2589+ responseMessage.Append("Name".PadRight(20));
2590+ responseMessage.Append(' ');
2591+ responseMessage.Append("State".PadRight(15));
2592+ responseMessage.Append(' ');
2593+ responseMessage.Append("Last Exec. Start".PadRight(20));
2594+ responseMessage.Append(' ');
2595+ responseMessage.Append("Last Exec. Stop".PadRight(20));
2596+ responseMessage.AppendLine();
2597+ responseMessage.Append(new string('-', 20));
2598+ responseMessage.Append(' ');
2599+ responseMessage.Append(new string('-', 15));
2600+ responseMessage.Append(' ');
2601+ responseMessage.Append(new string('-', 20));
2602+ responseMessage.Append(' ');
2603+ responseMessage.Append(new string('-', 20));
2604+
2605+ lock (m_processes)
2606+ {
2607+ foreach (ServiceProcess process in m_processes)
2608+ {
2609+ responseMessage.AppendLine();
2610+ responseMessage.Append(process.Name.PadRight(20));
2611+ responseMessage.Append(' ');
2612+ responseMessage.Append(process.CurrentState.ToString().PadRight(15));
2613+ responseMessage.Append(' ');
2614+
2615+ if (process.ExecutionStartTime != DateTime.MinValue)
2616+ responseMessage.Append(process.ExecutionStartTime.ToString("MM/dd/yy hh:mm:ss tt").PadRight(20));
2617+ else
2618+ responseMessage.Append("[Not Executed]".PadRight(20));
2619+
2620+ responseMessage.Append(' ');
2621+
2622+ if (process.ExecutionStopTime != DateTime.MinValue)
2623+ {
2624+ responseMessage.Append(process.ExecutionStopTime.ToString("MM/dd/yy hh:mm:ss tt").PadRight(20));
2625+ }
2626+ else
2627+ {
2628+ if (process.ExecutionStartTime != DateTime.MinValue)
2629+ responseMessage.Append("[Executing]".PadRight(20));
2630+ else
2631+ responseMessage.Append("[Not Executed]".PadRight(20));
2632+ }
2633+ }
2634+ }
2635+ responseMessage.AppendLine();
2636+ responseMessage.AppendLine();
2637+
2638+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, responseMessage.ToString());
2639+ }
2640+ else
2641+ {
2642+ // No processes defined in the service to be displayed.
2643+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "No processes are defined in {0}.\r\n\r\n", Name);
2644+ }
2645+ }
2646+ }
2647+ }
2648+
2649+ private void ShowSchedules(ClientRequestInfo requestInfo)
2650+ {
2651+ if (requestInfo.Request.Arguments.ContainsHelpRequest)
2652+ {
2653+ StringBuilder helpMessage = new StringBuilder();
2654+
2655+ helpMessage.Append("Displays a list of schedules for processes defined in the service.");
2656+ helpMessage.AppendLine();
2657+ helpMessage.AppendLine();
2658+ helpMessage.Append(" Usage:");
2659+ helpMessage.AppendLine();
2660+ helpMessage.Append(" Schedules -options");
2661+ helpMessage.AppendLine();
2662+ helpMessage.AppendLine();
2663+ helpMessage.Append(" Options:");
2664+ helpMessage.AppendLine();
2665+ helpMessage.Append(" -?".PadRight(20));
2666+ helpMessage.Append("Displays this help message");
2667+ helpMessage.AppendLine();
2668+ helpMessage.AppendLine();
2669+
2670+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
2671+ }
2672+ else
2673+ {
2674+ if (m_processScheduler.Schedules.Count > 0)
2675+ {
2676+ // Display info about all the process schedules defined in the service.
2677+ StringBuilder responseMessage = new StringBuilder();
2678+
2679+ responseMessage.AppendFormat("Process schedules defined in {0}:", Name);
2680+ responseMessage.AppendLine();
2681+ responseMessage.AppendLine();
2682+ responseMessage.Append("Name".PadRight(20));
2683+ responseMessage.Append(' ');
2684+ responseMessage.Append("Rule".PadRight(20));
2685+ responseMessage.Append(' ');
2686+ responseMessage.Append("Last Due".PadRight(30));
2687+ responseMessage.AppendLine();
2688+ responseMessage.Append(new string('-', 20));
2689+ responseMessage.Append(' ');
2690+ responseMessage.Append(new string('-', 20));
2691+ responseMessage.Append(' ');
2692+ responseMessage.Append(new string('-', 30));
2693+
2694+ foreach (Schedule schedule in m_processScheduler.Schedules)
2695+ {
2696+ responseMessage.AppendLine();
2697+ responseMessage.Append(schedule.Name.PadRight(20));
2698+ responseMessage.Append(' ');
2699+ responseMessage.Append(schedule.Rule.PadRight(20));
2700+ responseMessage.Append(' ');
2701+
2702+ if (schedule.LastDueAt != DateTime.MinValue)
2703+ responseMessage.Append(schedule.LastDueAt.ToString().PadRight(30));
2704+ else
2705+ responseMessage.Append("[Never]".PadRight(30));
2706+ }
2707+ responseMessage.AppendLine();
2708+ responseMessage.AppendLine();
2709+
2710+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, responseMessage.ToString());
2711+ }
2712+ else
2713+ {
2714+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "No process schedules are defined in {0}.\r\n\r\n", Name);
2715+ }
2716+ }
2717+ }
2718+
2719+ private void ShowRequestHistory(ClientRequestInfo requestInfo)
2720+ {
2721+ if (requestInfo.Request.Arguments.ContainsHelpRequest)
2722+ {
2723+ StringBuilder helpMessage = new StringBuilder();
2724+
2725+ helpMessage.Append("Displays a list of recent requests received from the clients.");
2726+ helpMessage.AppendLine();
2727+ helpMessage.AppendLine();
2728+ helpMessage.Append(" Usage:");
2729+ helpMessage.AppendLine();
2730+ helpMessage.Append(" History -options");
2731+ helpMessage.AppendLine();
2732+ helpMessage.AppendLine();
2733+ helpMessage.Append(" Options:");
2734+ helpMessage.AppendLine();
2735+ helpMessage.Append(" -?".PadRight(20));
2736+ helpMessage.Append("Displays this help message");
2737+ helpMessage.AppendLine();
2738+ helpMessage.AppendLine();
2739+
2740+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
2741+ }
2742+ else
2743+ {
2744+ StringBuilder responseMessage = new StringBuilder();
2745+
2746+ responseMessage.AppendFormat("History of requests received by {0}:", Name);
2747+ responseMessage.AppendLine();
2748+ responseMessage.AppendLine();
2749+ responseMessage.Append("Command".PadRight(20));
2750+ responseMessage.Append(' ');
2751+ responseMessage.Append("Received".PadRight(25));
2752+ responseMessage.Append(' ');
2753+ responseMessage.Append("Sender".PadRight(30));
2754+ responseMessage.AppendLine();
2755+ responseMessage.Append(new string('-', 20));
2756+ responseMessage.Append(' ');
2757+ responseMessage.Append(new string('-', 25));
2758+ responseMessage.Append(' ');
2759+ responseMessage.Append(new string('-', 30));
2760+
2761+ lock (m_clientRequestHistory)
2762+ {
2763+ foreach (ClientRequestInfo historicRequest in m_clientRequestHistory)
2764+ {
2765+ responseMessage.AppendLine();
2766+ responseMessage.Append(historicRequest.Request.Command.PadRight(20));
2767+ responseMessage.Append(' ');
2768+ responseMessage.Append(historicRequest.ReceivedAt.ToString().PadRight(25));
2769+ responseMessage.Append(' ');
2770+ responseMessage.Append(string.Format("{0} from {1}", historicRequest.Sender.ClientUser.Identity.Name, historicRequest.Sender.MachineName).PadRight(30));
2771+ }
2772+ }
2773+ responseMessage.AppendLine();
2774+ responseMessage.AppendLine();
2775+
2776+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, responseMessage.ToString());
2777+ }
2778+ }
2779+
2780+ private void ShowRequestHelp(ClientRequestInfo requestInfo)
2781+ {
2782+ if (requestInfo.Request.Arguments.ContainsHelpRequest)
2783+ {
2784+ StringBuilder helpMessage = new StringBuilder();
2785+
2786+ helpMessage.Append("Displays a list of commands supported by the service.");
2787+ helpMessage.AppendLine();
2788+ helpMessage.AppendLine();
2789+ helpMessage.Append(" Usage:");
2790+ helpMessage.AppendLine();
2791+ helpMessage.Append(" Help -options");
2792+ helpMessage.AppendLine();
2793+ helpMessage.AppendLine();
2794+ helpMessage.Append(" Options:");
2795+ helpMessage.AppendLine();
2796+ helpMessage.Append(" -?".PadRight(20));
2797+ helpMessage.Append("Displays this help message");
2798+ helpMessage.AppendLine();
2799+ helpMessage.AppendLine();
2800+
2801+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
2802+ }
2803+ else
2804+ {
2805+ bool showAdvancedHelp = requestInfo.Request.Arguments.Exists("advanced");
2806+
2807+ StringBuilder responseMessage = new StringBuilder();
2808+
2809+ responseMessage.AppendFormat("Commands supported by {0}:", Name);
2810+ responseMessage.AppendLine();
2811+ responseMessage.AppendLine();
2812+ responseMessage.Append("Command".PadRight(20));
2813+ responseMessage.Append(' ');
2814+ responseMessage.Append("Description".PadRight(55));
2815+ responseMessage.AppendLine();
2816+ responseMessage.Append(new string('-', 20));
2817+ responseMessage.Append(' ');
2818+ responseMessage.Append(new string('-', 55));
2819+
2820+ lock (m_clientRequestHandlers)
2821+ {
2822+ foreach (ClientRequestHandler handler in m_clientRequestHandlers)
2823+ {
2824+ if (handler.IsAdvertised || showAdvancedHelp)
2825+ {
2826+ responseMessage.AppendLine();
2827+ responseMessage.Append(handler.Command.PadRight(20));
2828+ responseMessage.Append(' ');
2829+ responseMessage.Append(handler.CommandDescription.PadRight(55));
2830+ }
2831+ }
2832+ }
2833+ responseMessage.AppendLine();
2834+ responseMessage.AppendLine();
2835+
2836+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, responseMessage.ToString());
2837+ }
2838+ }
2839+
2840+ private void ShowHealthReport(ClientRequestInfo requestInfo)
2841+ {
2842+ if (requestInfo.Request.Arguments.ContainsHelpRequest)
2843+ {
2844+ StringBuilder helpMessage = new StringBuilder();
2845+
2846+ helpMessage.Append("Displays a resource utilization report for the service.");
2847+ helpMessage.AppendLine();
2848+ helpMessage.AppendLine();
2849+ helpMessage.Append(" Usage:");
2850+ helpMessage.AppendLine();
2851+ helpMessage.Append(" Health -options");
2852+ helpMessage.AppendLine();
2853+ helpMessage.AppendLine();
2854+ helpMessage.Append(" Options:");
2855+ helpMessage.AppendLine();
2856+ helpMessage.Append(" -?".PadRight(20));
2857+ helpMessage.Append("Displays this help message");
2858+ helpMessage.AppendLine();
2859+ helpMessage.Append(" -lifetime".PadRight(20));
2860+ helpMessage.Append("Shows utilization over entire service lifetime");
2861+ helpMessage.AppendLine();
2862+ helpMessage.Append(" -actionable".PadRight(20));
2863+ helpMessage.Append("Returns results via an actionable event");
2864+ helpMessage.AppendLine();
2865+ helpMessage.AppendLine();
2866+
2867+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
2868+ }
2869+ else
2870+ {
2871+ string message;
2872+ bool success;
2873+
2874+ if (m_performanceMonitor != null)
2875+ {
2876+ try
2877+ {
2878+ if (requestInfo.Request.Arguments.Exists("lifetime"))
2879+ message = m_performanceMonitor.LifetimeStatus;
2880+ else
2881+ message = m_performanceMonitor.Status;
2882+
2883+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "\r\n" + message + "\r\n");
2884+ success = true;
2885+ }
2886+ catch (Exception ex)
2887+ {
2888+ m_errorLogger.Log(ex);
2889+ message = "Failed to query system health monitor status: " + ex.Message;
2890+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, message + "\r\n\r\n");
2891+ success = false;
2892+ }
2893+ }
2894+ else
2895+ {
2896+ message = "System health monitor is unavailable.";
2897+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Warning, message + "\r\n\r\n");
2898+ success = false;
2899+ }
2900+
2901+ // Also allow consumers to directly consume message via event in response to a health request
2902+ if (requestInfo.Request.Arguments.Exists("actionable"))
2903+ SendActionableResponse(requestInfo, success, null, message);
2904+ }
2905+ }
2906+
2907+ private void ResetHealthMonitor(ClientRequestInfo requestInfo)
2908+ {
2909+ if (requestInfo.Request.Arguments.ContainsHelpRequest)
2910+ {
2911+ StringBuilder helpMessage = new StringBuilder();
2912+
2913+ helpMessage.Append("Resets the system resource utilization monitor.");
2914+ helpMessage.AppendLine();
2915+ helpMessage.AppendLine();
2916+ helpMessage.Append(" Usage:");
2917+ helpMessage.AppendLine();
2918+ helpMessage.Append(" ResetHealthMonitor -options");
2919+ helpMessage.AppendLine();
2920+ helpMessage.AppendLine();
2921+ helpMessage.Append(" Options:");
2922+ helpMessage.AppendLine();
2923+ helpMessage.Append(" -?".PadRight(20));
2924+ helpMessage.Append("Displays this help message");
2925+ helpMessage.AppendLine();
2926+ helpMessage.AppendLine();
2927+
2928+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
2929+ }
2930+ else
2931+ {
2932+ try
2933+ {
2934+ // Dispose existing performance monitor
2935+ if (m_performanceMonitor != null)
2936+ m_performanceMonitor.Dispose();
2937+
2938+ // Recreate the performance monitor
2939+ m_performanceMonitor = new PerformanceMonitor(m_healthMonitorInterval * 1000.0D, true);
2940+
2941+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "System health monitor successfully reset.\r\n\r\n");
2942+ }
2943+ catch (Exception ex)
2944+ {
2945+ m_errorLogger.Log(ex);
2946+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, "Failed to reset system health monitor: {0}\r\n\r\n", ex.Message);
2947+ }
2948+ }
2949+ }
2950+
2951+ private void ShowServiceStatus(ClientRequestInfo requestInfo)
2952+ {
2953+ if (requestInfo.Request.Arguments.ContainsHelpRequest)
2954+ {
2955+ StringBuilder helpMessage = new StringBuilder();
2956+
2957+ helpMessage.Append("Displays status of this service and its components.");
2958+ helpMessage.AppendLine();
2959+ helpMessage.AppendLine();
2960+ helpMessage.Append(" Usage:");
2961+ helpMessage.AppendLine();
2962+ helpMessage.Append(" Status -options");
2963+ helpMessage.AppendLine();
2964+ helpMessage.AppendLine();
2965+ helpMessage.Append(" Options:");
2966+ helpMessage.AppendLine();
2967+ helpMessage.Append(" -?".PadRight(20));
2968+ helpMessage.Append("Displays this help message");
2969+ helpMessage.AppendLine();
2970+ helpMessage.Append(" -actionable".PadRight(20));
2971+ helpMessage.Append("Returns results via an actionable event");
2972+ helpMessage.AppendLine();
2973+ helpMessage.AppendLine();
2974+
2975+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
2976+ }
2977+ else
2978+ {
2979+ string message = Status;
2980+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, message);
2981+
2982+ // Also allow consumers to directly consume message via event in response to a status request
2983+ if (requestInfo.Request.Arguments.Exists("actionable"))
2984+ SendActionableResponse(requestInfo, true, null, message);
2985+ }
2986+ }
2987+
2988+ private void ReloadCryptoCache(ClientRequestInfo requestInfo)
2989+ {
2990+ if (requestInfo.Request.Arguments.ContainsHelpRequest)
2991+ {
2992+ StringBuilder helpMessage = new StringBuilder();
2993+
2994+ helpMessage.Append("Reloads the local system cryptography cache with data from the common cryptography cache.");
2995+ helpMessage.AppendLine();
2996+ helpMessage.AppendLine();
2997+ helpMessage.Append(" Usage:");
2998+ helpMessage.AppendLine();
2999+ helpMessage.Append(" ReloadCryptoCache -options");
3000+ helpMessage.AppendLine();
3001+ helpMessage.AppendLine();
3002+ helpMessage.Append(" Options:");
3003+ helpMessage.AppendLine();
3004+ helpMessage.Append(" -?".PadRight(20));
3005+ helpMessage.Append("Displays this help message");
3006+ helpMessage.AppendLine();
3007+ helpMessage.AppendLine();
3008+
3009+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
3010+ }
3011+ else
3012+ {
3013+ Cipher.ReloadCache();
3014+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Crypto cache successfully reloaded.\r\n\r\n");
3015+ }
3016+ }
3017+
3018+ private void ReloadSettings(ClientRequestInfo requestInfo)
3019+ {
3020+ if (requestInfo.Request.Arguments.ContainsHelpRequest || requestInfo.Request.Arguments.OrderedArgCount < 1)
3021+ {
3022+ StringBuilder helpMessage = new StringBuilder();
3023+
3024+ helpMessage.Append("Reloads settings of the component whose settings are saved under the specified category in the config file.");
3025+ helpMessage.AppendLine();
3026+ helpMessage.AppendLine();
3027+ helpMessage.Append(" Usage:");
3028+ helpMessage.AppendLine();
3029+ helpMessage.Append(" ReloadSettings \"Category Name\" -options");
3030+ helpMessage.AppendLine();
3031+ helpMessage.AppendLine();
3032+ helpMessage.Append(" Options:");
3033+ helpMessage.AppendLine();
3034+ helpMessage.Append(" -?".PadRight(20));
3035+ helpMessage.Append("Displays this help message");
3036+ helpMessage.AppendLine();
3037+ helpMessage.AppendLine();
3038+ helpMessage.Append("IMPORTANT: Only settings under the categories listed by the \"Settings\" command can be reloaded.");
3039+ helpMessage.AppendLine();
3040+ helpMessage.AppendLine();
3041+
3042+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
3043+ }
3044+ else
3045+ {
3046+ string categoryName = requestInfo.Request.Arguments["orderedarg1"];
3047+
3048+ IPersistSettings typedComponent;
3049+ lock (m_serviceComponents)
3050+ {
3051+ foreach (object component in m_serviceComponents)
3052+ {
3053+ typedComponent = component as IPersistSettings;
3054+ if ((object) typedComponent != null &&
3055+ string.Compare(categoryName, typedComponent.SettingsCategory, true) == 0)
3056+ {
3057+ typedComponent.LoadSettings();
3058+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Successfully loaded settings from category \"{0}\".\r\n\r\n", categoryName);
3059+ return;
3060+ }
3061+ }
3062+ }
3063+
3064+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, "Failed to load settings from category \"{0}\". No corresponding component exists.\r\n\r\n", categoryName);
3065+ }
3066+ }
3067+
3068+ private void UpdateSettings(ClientRequestInfo requestInfo)
3069+ {
3070+ if (requestInfo.Request.Arguments.ContainsHelpRequest || requestInfo.Request.Arguments.OrderedArgCount < 3)
3071+ {
3072+ // We'll display help about the request since we either don't have the required arguments or the user
3073+ // has explicitly requested for the help to be displayed for this request type.
3074+ StringBuilder helpMessage = new StringBuilder();
3075+
3076+ helpMessage.Append("Updates the specified setting under the specified category in the config file.");
3077+ helpMessage.AppendLine();
3078+ helpMessage.AppendLine();
3079+ helpMessage.Append(" Usage:");
3080+ helpMessage.AppendLine();
3081+ helpMessage.Append(" UpdateSettings \"Category Name\" \"Setting Name\" \"Setting Value\" -options");
3082+ helpMessage.AppendLine();
3083+ helpMessage.AppendLine();
3084+ helpMessage.Append(" Options:");
3085+ helpMessage.AppendLine();
3086+ helpMessage.Append(" -?".PadRight(20));
3087+ helpMessage.Append("Displays this help message");
3088+ helpMessage.AppendLine();
3089+ helpMessage.Append(" -add".PadRight(20));
3090+ helpMessage.Append("Adds specified setting to the specified category");
3091+ helpMessage.AppendLine();
3092+ helpMessage.Append(" -delete".PadRight(20));
3093+ helpMessage.Append("Deletes specified setting from the specified category");
3094+ helpMessage.AppendLine();
3095+ helpMessage.Append(" -reload".PadRight(20));
3096+ helpMessage.Append("Causes corresponding component to reload settings");
3097+ helpMessage.AppendLine();
3098+ helpMessage.Append(" -list".PadRight(20));
3099+ helpMessage.Append("Displays list all of the queryable settings");
3100+ helpMessage.AppendLine();
3101+ helpMessage.AppendLine();
3102+ helpMessage.Append("IMPORTANT: Only settings under the categories listed by the \"Settings\" command can be updated.");
3103+ helpMessage.AppendLine();
3104+ helpMessage.AppendLine();
3105+
3106+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
3107+ }
3108+ else
3109+ {
3110+ string categoryName = requestInfo.Request.Arguments["orderedarg1"];
3111+ string settingName = requestInfo.Request.Arguments["orderedarg2"];
3112+ string settingValue = requestInfo.Request.Arguments["orderedarg3"];
3113+ bool addSetting = requestInfo.Request.Arguments.Exists("add");
3114+ bool deleteSetting = requestInfo.Request.Arguments.Exists("delete");
3115+ bool reloadSettings = requestInfo.Request.Arguments.Exists("reload");
3116+ bool listSettings = requestInfo.Request.Arguments.Exists("list");
3117+
3118+ IPersistSettings typedComponent;
3119+ lock (m_serviceComponents)
3120+ {
3121+ foreach (object component in m_serviceComponents)
3122+ {
3123+ typedComponent = component as IPersistSettings;
3124+ if (typedComponent != null &&
3125+ string.Compare(categoryName, typedComponent.SettingsCategory, true) == 0)
3126+ {
3127+ ConfigurationFile config = ConfigurationFile.Current;
3128+ CategorizedSettingsElementCollection settings = config.Settings[categoryName];
3129+ CategorizedSettingsElement setting = settings[settingName];
3130+ if (addSetting)
3131+ {
3132+ // Add new setting.
3133+ if (setting == null)
3134+ {
3135+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Attempting to add setting \"{0}\" under category \"{1}\"...\r\n\r\n", settingName, categoryName);
3136+ settings.Add(settingName, settingValue);
3137+ config.Save();
3138+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Successfully added setting \"{0}\" under category \"{1}\".\r\n\r\n", settingName, categoryName);
3139+ }
3140+ else
3141+ {
3142+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, "Failed to add setting \"{0}\" under category \"{1}\". Setting already exists.\r\n\r\n", settingName, categoryName);
3143+ return;
3144+ }
3145+ }
3146+ else if (deleteSetting)
3147+ {
3148+ // Delete existing setting.
3149+ if (setting != null)
3150+ {
3151+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Attempting to delete setting \"{0}\" under category \"{1}\"...\r\n\r\n", settingName, categoryName);
3152+ settings.Remove(setting);
3153+ config.Save();
3154+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Successfully deleted setting \"{0}\" under category \"{1}\".\r\n\r\n", settingName, categoryName);
3155+ }
3156+ else
3157+ {
3158+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, "Failed to delete setting \"{0}\" under category \"{1}\". Setting does not exist.\r\n\r\n", settingName, categoryName);
3159+ return;
3160+ }
3161+ }
3162+ else
3163+ {
3164+ // Update existing setting.
3165+ if (setting != null)
3166+ {
3167+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Attempting to update setting \"{0}\" under category \"{1}\"...\r\n\r\n", settingName, categoryName);
3168+ setting.Value = settingValue;
3169+ config.Save();
3170+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Successfully updated setting \"{0}\" under category \"{1}\".\r\n\r\n", settingName, categoryName);
3171+ }
3172+ else
3173+ {
3174+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, "Failed to update value of setting \"{0}\" under category \"{1}\" . Setting does not exist.\r\n\r\n", settingName, categoryName);
3175+ return;
3176+ }
3177+ }
3178+
3179+ if (reloadSettings)
3180+ {
3181+ // The user has requested to reload settings for all the components.
3182+ requestInfo.Request = ClientRequest.Parse(string.Format("ReloadSettings {0}", categoryName));
3183+ ReloadSettings(requestInfo);
3184+ }
3185+
3186+ if (listSettings)
3187+ {
3188+ // The user has requested to list all of the queryable settings.
3189+ requestInfo.Request = ClientRequest.Parse("Settings");
3190+ ShowSettings(requestInfo);
3191+ }
3192+
3193+ return;
3194+ }
3195+ }
3196+ }
3197+
3198+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, "Failed to update settings under category \"{0}\". No corresponding component exists.\r\n\r\n", categoryName);
3199+ }
3200+ }
3201+
3202+ private void StartProcess(ClientRequestInfo requestInfo)
3203+ {
3204+ if (requestInfo.Request.Arguments.ContainsHelpRequest || requestInfo.Request.Arguments.OrderedArgCount < 1)
3205+ {
3206+ bool showAdvancedHelp = requestInfo.Request.Arguments.Exists("advanced");
3207+
3208+ StringBuilder helpMessage = new StringBuilder();
3209+
3210+ helpMessage.Append("Starts execution of the specified service or system process.");
3211+ helpMessage.AppendLine();
3212+ helpMessage.AppendLine();
3213+ helpMessage.Append(" Usage:");
3214+ helpMessage.AppendLine();
3215+ helpMessage.Append(" Start \"Process Name\" -options");
3216+ helpMessage.AppendLine();
3217+ helpMessage.AppendLine();
3218+ helpMessage.Append(" Options:");
3219+ helpMessage.AppendLine();
3220+ helpMessage.Append(" -?".PadRight(20));
3221+ helpMessage.Append("Displays this help message");
3222+ helpMessage.AppendLine();
3223+ helpMessage.Append(" -args".PadRight(20));
3224+ helpMessage.Append("Arguments to be passed in to the process");
3225+ helpMessage.AppendLine();
3226+ helpMessage.Append(" -restart".PadRight(20));
3227+ helpMessage.Append("Aborts the process if executing and start it again");
3228+ helpMessage.AppendLine();
3229+ helpMessage.Append(" -list".PadRight(20));
3230+ helpMessage.Append("Displays list of all service or system processes");
3231+ if (m_supportSystemCommands && showAdvancedHelp)
3232+ {
3233+ helpMessage.AppendLine();
3234+ helpMessage.Append(" -system".PadRight(20));
3235+ helpMessage.Append("Treats the specified process as a system process");
3236+ }
3237+ helpMessage.AppendLine();
3238+ helpMessage.AppendLine();
3239+
3240+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
3241+ }
3242+ else
3243+ {
3244+ string processName = requestInfo.Request.Arguments["orderedarg1"];
3245+ string processArgs = requestInfo.Request.Arguments["args"];
3246+ bool systemProcess = requestInfo.Request.Arguments.Exists("system");
3247+ bool restartProcess = requestInfo.Request.Arguments.Exists("restart");
3248+ bool listProcesses = requestInfo.Request.Arguments.Exists("list");
3249+
3250+ if (restartProcess)
3251+ {
3252+ requestInfo.Request = ClientRequest.Parse(string.Format("Abort \"{0}\" {1}", processName, systemProcess ? "-system" : ""));
3253+ AbortProcess(requestInfo);
3254+ }
3255+
3256+ if (systemProcess && m_supportSystemCommands)
3257+ {
3258+ // Start system process.
3259+ try
3260+ {
3261+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Attempting to start system process \"{0}\"...\r\n\r\n", processName);
3262+ Process startedProcess = Process.Start(processName, processArgs);
3263+
3264+ if (startedProcess != null)
3265+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Successfully started system process \"{0}\".\r\n\r\n", processName);
3266+ else
3267+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, "Failed to start system process \"{0}\".\r\n\r\n", processName);
3268+ }
3269+ catch (Exception ex)
3270+ {
3271+ m_errorLogger.Log(ex);
3272+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, "Failed to start system process \"{0}\". {1}.\r\n\r\n", processName, ex.Message);
3273+ }
3274+ }
3275+ else
3276+ {
3277+ // Start service process.
3278+ ServiceProcess processToStart = FindProcess(processName);
3279+ if (processToStart != null)
3280+ {
3281+ if (processToStart.CurrentState != ServiceProcessState.Processing)
3282+ {
3283+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Attempting to start service process \"{0}\"...\r\n\r\n", processName);
3284+ if (string.IsNullOrEmpty(processArgs))
3285+ {
3286+ processToStart.Start();
3287+ }
3288+ else
3289+ {
3290+ // Prepare the arguments.
3291+ string[] splitArgs = processArgs.Split(',');
3292+ Array.ForEach<string>(splitArgs, (string arg) => arg.Trim());
3293+
3294+ // Start the service process.
3295+ processToStart.Start(splitArgs);
3296+ }
3297+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Successfully started service process \"{0}\".\r\n\r\n", processName);
3298+ }
3299+ else
3300+ {
3301+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, "Failed to start process \"{0}\". Process is already executing.\r\n\r\n", processName);
3302+ }
3303+ }
3304+ else
3305+ {
3306+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, "Failed to start service process \"{0}\". Process is not defined.\r\n\r\n", processName);
3307+ }
3308+ }
3309+
3310+ if (listProcesses)
3311+ {
3312+ requestInfo.Request = ClientRequest.Parse(string.Format("Processes {0}", systemProcess ? "-system" : ""));
3313+ ShowProcesses(requestInfo);
3314+ }
3315+ }
3316+ }
3317+
3318+ private void AbortProcess(ClientRequestInfo requestInfo)
3319+ {
3320+ if (requestInfo.Request.Arguments.ContainsHelpRequest || requestInfo.Request.Arguments.OrderedArgCount < 1)
3321+ {
3322+ bool showAdvancedHelp = requestInfo.Request.Arguments.Exists("advanced");
3323+
3324+ StringBuilder helpMessage = new StringBuilder();
3325+
3326+ helpMessage.Append("Aborts the specified service or system process if executing.");
3327+ helpMessage.AppendLine();
3328+ helpMessage.AppendLine();
3329+ helpMessage.Append(" Usage:");
3330+ helpMessage.AppendLine();
3331+ helpMessage.Append(" Abort \"Process Name\" -options");
3332+ helpMessage.AppendLine();
3333+ helpMessage.AppendLine();
3334+ helpMessage.Append(" Options:");
3335+ helpMessage.AppendLine();
3336+ helpMessage.Append(" -?".PadRight(20));
3337+ helpMessage.Append("Displays this help message");
3338+ helpMessage.AppendLine();
3339+ helpMessage.Append(" -list".PadRight(20));
3340+ helpMessage.Append("Displays list of all service or system processes");
3341+ if (m_supportSystemCommands && showAdvancedHelp)
3342+ {
3343+ helpMessage.AppendLine();
3344+ helpMessage.Append(" -system".PadRight(20));
3345+ helpMessage.Append("Treats the specified process as a system process");
3346+ helpMessage.AppendLine();
3347+ helpMessage.AppendLine();
3348+ helpMessage.Append("NOTE: Specify process name of \"Me\" to kill current service process. ");
3349+ }
3350+ helpMessage.AppendLine();
3351+ helpMessage.AppendLine();
3352+
3353+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
3354+ }
3355+ else
3356+ {
3357+ string processName = requestInfo.Request.Arguments["orderedarg1"];
3358+ bool systemProcess = requestInfo.Request.Arguments.Exists("system");
3359+ bool listProcesses = requestInfo.Request.Arguments.Exists("list");
3360+
3361+ if (systemProcess && m_supportSystemCommands)
3362+ {
3363+ // Abort system process.
3364+ Process processToAbort = null;
3365+ if (string.Compare(processName, "Me", true) == 0)
3366+ {
3367+ processName = Process.GetCurrentProcess().ProcessName;
3368+ }
3369+
3370+ foreach (Process process in Process.GetProcessesByName(processName))
3371+ {
3372+ // Lookup for the system process by name.
3373+ processToAbort = process;
3374+ break;
3375+ }
3376+
3377+ if (processToAbort == null)
3378+ {
3379+ int processID;
3380+
3381+ if (int.TryParse(processName, out processID) && processID > 0)
3382+ {
3383+ processToAbort = Process.GetProcessById(processID);
3384+ processName = processToAbort.ProcessName;
3385+ }
3386+ }
3387+
3388+ if (processToAbort != null)
3389+ {
3390+ try
3391+ {
3392+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Attempting to abort system process \"{0}\"...\r\n\r\n", processName);
3393+ processToAbort.Kill();
3394+ if (processToAbort.WaitForExit(10000))
3395+ {
3396+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Successfully aborted system process \"{0}\".\r\n\r\n", processName);
3397+ }
3398+ else
3399+ {
3400+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, "Failed to abort system process \"{0}\". Process not responding.\r\n\r\n", processName);
3401+ }
3402+ }
3403+ catch (Exception ex)
3404+ {
3405+ m_errorLogger.Log(ex);
3406+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, "Failed to abort system process \"{0}\". {1}.\r\n\r\n", processName, ex.Message);
3407+ }
3408+ }
3409+ else
3410+ {
3411+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, "Failed to abort system process \"{0}\". Process is not running.\r\n\r\n", processName);
3412+ }
3413+ }
3414+ else
3415+ {
3416+ // Abort service process.
3417+ ServiceProcess processToAbort = FindProcess(processName);
3418+ if (processToAbort != null)
3419+ {
3420+ if (processToAbort.CurrentState == ServiceProcessState.Processing)
3421+ {
3422+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Attempting to abort service process \"{0}\"...\r\n\r\n", processName);
3423+ processToAbort.Abort();
3424+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Successfully aborted service process \"{0}\".\r\n\r\n", processName);
3425+ }
3426+ else
3427+ {
3428+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, "Failed to abort service process \"{0}\". Process is not executing.\r\n\r\n", processName);
3429+ }
3430+ }
3431+ else
3432+ {
3433+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, "Failed to abort service process \"{0}\". Process is not defined.\r\n\r\n", processName);
3434+ }
3435+ }
3436+
3437+ if (listProcesses)
3438+ {
3439+ requestInfo.Request = ClientRequest.Parse(string.Format("Processes {0}", systemProcess ? "-system" : ""));
3440+ ShowProcesses(requestInfo);
3441+ }
3442+ }
3443+ }
3444+
3445+ private void RescheduleProcess(ClientRequestInfo requestInfo)
3446+ {
3447+ if (requestInfo.Request.Arguments.ContainsHelpRequest || requestInfo.Request.Arguments.OrderedArgCount < 2)
3448+ {
3449+ StringBuilder helpMessage = new StringBuilder();
3450+
3451+ helpMessage.Append("Schedules or re-schedules an existing process defined in the service.");
3452+ helpMessage.AppendLine();
3453+ helpMessage.AppendLine();
3454+ helpMessage.Append(" Usage:");
3455+ helpMessage.AppendLine();
3456+ helpMessage.Append(" Reschedule \"Process Name\" \"Schedule Rule\" -options");
3457+ helpMessage.AppendLine();
3458+ helpMessage.AppendLine();
3459+ helpMessage.Append(" Options:");
3460+ helpMessage.AppendLine();
3461+ helpMessage.Append(" -?".PadRight(20));
3462+ helpMessage.Append("Displays this help message");
3463+ helpMessage.AppendLine();
3464+ helpMessage.Append(" -save".PadRight(20));
3465+ helpMessage.Append("Saves all process schedules to the config file");
3466+ helpMessage.AppendLine();
3467+ helpMessage.Append(" -list".PadRight(20));
3468+ helpMessage.Append("Displays list of all process schedules");
3469+ helpMessage.AppendLine();
3470+ helpMessage.AppendLine();
3471+ helpMessage.Append("NOTE: The schedule rule uses UNIX crontab syntax which consists of 5 parts (For example, \"* * * * *\"). ");
3472+ helpMessage.Append("Following is a brief description of each of the 5 parts that make up the rule:");
3473+ helpMessage.AppendLine();
3474+ helpMessage.AppendLine();
3475+ helpMessage.Append(" Part 1 - Minute part; value range 0 to 59. ");
3476+ helpMessage.AppendLine();
3477+ helpMessage.Append(" Part 2 - Hour part; value range 0 to 23. ");
3478+ helpMessage.AppendLine();
3479+ helpMessage.Append(" Part 3 - Day of month part; value range 1 to 31. ");
3480+ helpMessage.AppendLine();
3481+ helpMessage.Append(" Part 4 - Month part; value range 1 to 12. ");
3482+ helpMessage.AppendLine();
3483+ helpMessage.Append(" Part 5 - Day of week part; value range 0 to 6 (0 = Sunday). ");
3484+ helpMessage.AppendLine();
3485+ helpMessage.AppendLine();
3486+ helpMessage.Append("Following is a description of valid syntax for all parts of the rule:");
3487+ helpMessage.AppendLine();
3488+ helpMessage.AppendLine();
3489+ helpMessage.Append(" * - Any value in the range for the date-time part.");
3490+ helpMessage.AppendLine();
3491+ helpMessage.Append(" */n - Every nth value for the data-time part.");
3492+ helpMessage.AppendLine();
3493+ helpMessage.Append(" n1-n2 - Range of values (inclusive) for the date-time part.");
3494+ helpMessage.AppendLine();
3495+ helpMessage.Append(" n1,n2 - 1 or more specific values for the date-time part.");
3496+ helpMessage.AppendLine();
3497+ helpMessage.AppendLine();
3498+ helpMessage.Append("Examples:");
3499+ helpMessage.AppendLine();
3500+ helpMessage.AppendLine();
3501+ helpMessage.Append(" \"* * * * *\" - Process executes every minute.");
3502+ helpMessage.AppendLine();
3503+ helpMessage.Append(" \"*/5 * * * *\" - Process executes every 5 minutes.");
3504+ helpMessage.AppendLine();
3505+ helpMessage.Append(" \"5 * * * *\" - Process executes 5 past every hour.");
3506+ helpMessage.AppendLine();
3507+ helpMessage.Append(" \"0 0 * * *\" - Process executes every day at midnight.");
3508+ helpMessage.AppendLine();
3509+ helpMessage.Append(" \"0 0 1 * *\" - Process executes 1st of every month at midnight.");
3510+ helpMessage.AppendLine();
3511+ helpMessage.Append(" \"0 0 * * 0\" - Process executes every Sunday at midnight.");
3512+ helpMessage.AppendLine();
3513+ helpMessage.Append(" \"0 0 31 12 *\" - Process executes on December 31 at midnight.");
3514+ helpMessage.AppendLine();
3515+ helpMessage.Append(" \"5,10 0-2 * * *\" - Process executes 5 and 10 past hours 12am to 2am.");
3516+ helpMessage.AppendLine();
3517+ helpMessage.AppendLine();
3518+
3519+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
3520+ }
3521+ else
3522+ {
3523+ string processName = requestInfo.Request.Arguments["orderedarg1"];
3524+ string scheduleRule = requestInfo.Request.Arguments["orderedarg2"];
3525+ bool saveSchedules = requestInfo.Request.Arguments.Exists("save");
3526+ bool listSchedules = requestInfo.Request.Arguments.Exists("list");
3527+
3528+ try
3529+ {
3530+ // Schedule the process if not scheduled or update its schedule if scheduled.
3531+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Attempting to schedule process \"{0}\" with rule \"{1}\"...\r\n\r\n", processName, scheduleRule);
3532+ ScheduleProcess(processName, scheduleRule, true);
3533+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Successfully scheduled process \"{0}\" with rule \"{1}\".\r\n\r\n", processName, scheduleRule);
3534+
3535+ if (saveSchedules)
3536+ {
3537+ requestInfo.Request = ClientRequest.Parse("SaveSchedules");
3538+ SaveSchedules(requestInfo);
3539+ }
3540+ }
3541+ catch (Exception ex)
3542+ {
3543+ m_errorLogger.Log(ex);
3544+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, "Failed to schedule process \"{0}\". {1}\r\n\r\n", processName, ex.Message);
3545+ }
3546+
3547+ if (listSchedules)
3548+ {
3549+ requestInfo.Request = ClientRequest.Parse("Schedules");
3550+ ShowSchedules(requestInfo);
3551+ }
3552+ }
3553+ }
3554+
3555+ private void UnscheduleProcess(ClientRequestInfo requestInfo)
3556+ {
3557+ if (requestInfo.Request.Arguments.ContainsHelpRequest || requestInfo.Request.Arguments.OrderedArgCount < 1)
3558+ {
3559+ StringBuilder helpMessage = new StringBuilder();
3560+
3561+ helpMessage.Append("Unschedules a scheduled process defined in the service.");
3562+ helpMessage.AppendLine();
3563+ helpMessage.AppendLine();
3564+ helpMessage.Append(" Usage:");
3565+ helpMessage.AppendLine();
3566+ helpMessage.Append(" Unschedule \"Process Name\" -options");
3567+ helpMessage.AppendLine();
3568+ helpMessage.AppendLine();
3569+ helpMessage.Append(" Options:");
3570+ helpMessage.AppendLine();
3571+ helpMessage.Append(" -?".PadRight(20));
3572+ helpMessage.Append("Displays this help message");
3573+ helpMessage.AppendLine();
3574+ helpMessage.Append(" -save".PadRight(20));
3575+ helpMessage.Append("Saves all process schedules to the config file");
3576+ helpMessage.AppendLine();
3577+ helpMessage.Append(" -list".PadRight(20));
3578+ helpMessage.Append("Displays list of all process schedules");
3579+ helpMessage.AppendLine();
3580+ helpMessage.AppendLine();
3581+
3582+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
3583+ }
3584+ else
3585+ {
3586+ string processName = requestInfo.Request.Arguments["orderedarg1"];
3587+ bool saveSchedules = requestInfo.Request.Arguments.Exists("save");
3588+ bool listSchedules = requestInfo.Request.Arguments.Exists("list");
3589+
3590+ Schedule scheduleToRemove = m_processScheduler.FindSchedule(processName);
3591+
3592+ if (scheduleToRemove != null)
3593+ {
3594+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Attempting to unschedule process \"{0}\"...\r\n\r\n", processName);
3595+ m_processScheduler.Schedules.Remove(scheduleToRemove);
3596+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Successfully unscheduled process \"{0}\".\r\n\r\n", processName);
3597+
3598+ if (saveSchedules)
3599+ {
3600+ requestInfo.Request = ClientRequest.Parse("SaveSchedules");
3601+ SaveSchedules(requestInfo);
3602+ }
3603+ }
3604+ else
3605+ {
3606+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Alarm, "Failed to unschedule process \"{0}\". Process is not scheduled.\r\n\r\n", processName);
3607+ }
3608+
3609+ if (listSchedules)
3610+ {
3611+ requestInfo.Request = ClientRequest.Parse("Schedules");
3612+ ShowSchedules(requestInfo);
3613+ }
3614+ }
3615+ }
3616+
3617+ private void SaveSchedules(ClientRequestInfo requestInfo)
3618+ {
3619+ if (requestInfo.Request.Arguments.ContainsHelpRequest)
3620+ {
3621+ StringBuilder helpMessage = new StringBuilder();
3622+
3623+ helpMessage.Append("Saves all process schedules to the config file.");
3624+ helpMessage.AppendLine();
3625+ helpMessage.AppendLine();
3626+ helpMessage.Append(" Usage:");
3627+ helpMessage.AppendLine();
3628+ helpMessage.Append(" SaveSchedules -options");
3629+ helpMessage.AppendLine();
3630+ helpMessage.AppendLine();
3631+ helpMessage.Append(" Options:");
3632+ helpMessage.AppendLine();
3633+ helpMessage.Append(" -?".PadRight(20));
3634+ helpMessage.Append("Displays this help message");
3635+ helpMessage.AppendLine();
3636+ helpMessage.Append(" -list".PadRight(20));
3637+ helpMessage.Append("Displays list of all process schedules");
3638+ helpMessage.AppendLine();
3639+ helpMessage.AppendLine();
3640+
3641+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
3642+ }
3643+ else
3644+ {
3645+ bool listSchedules = requestInfo.Request.Arguments.Exists("list");
3646+
3647+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Attempting to save process schedules to the config file...\r\n\r\n");
3648+ m_processScheduler.SaveSettings();
3649+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Successfully saved process schedules to the config file.\r\n\r\n");
3650+
3651+ if (listSchedules)
3652+ {
3653+ requestInfo.Request = ClientRequest.Parse("Schedules");
3654+ ShowSchedules(requestInfo);
3655+ }
3656+ }
3657+ }
3658+
3659+ private void LoadSchedules(ClientRequestInfo requestInfo)
3660+ {
3661+ if (requestInfo.Request.Arguments.ContainsHelpRequest)
3662+ {
3663+ StringBuilder helpMessage = new StringBuilder();
3664+
3665+ helpMessage.Append("Loads all process schedules from the config file.");
3666+ helpMessage.AppendLine();
3667+ helpMessage.AppendLine();
3668+ helpMessage.Append(" Usage:");
3669+ helpMessage.AppendLine();
3670+ helpMessage.Append(" LoadSchedules -options");
3671+ helpMessage.AppendLine();
3672+ helpMessage.AppendLine();
3673+ helpMessage.Append(" Options:");
3674+ helpMessage.AppendLine();
3675+ helpMessage.Append(" -?".PadRight(20));
3676+ helpMessage.Append("Displays this help message");
3677+ helpMessage.AppendLine();
3678+ helpMessage.Append(" -list".PadRight(20));
3679+ helpMessage.Append("Displays list of all process schedules");
3680+ helpMessage.AppendLine();
3681+ helpMessage.AppendLine();
3682+
3683+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
3684+ }
3685+ else
3686+ {
3687+ bool listSchedules = requestInfo.Request.Arguments.Exists("list");
3688+
3689+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Attempting to load process schedules from the config file...\r\n\r\n");
3690+ m_processScheduler.LoadSettings();
3691+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, "Successfully loaded process schedules from the config file.\r\n\r\n");
3692+
3693+ if (listSchedules)
3694+ {
3695+ requestInfo.Request = ClientRequest.Parse("Schedules");
3696+ ShowSchedules(requestInfo);
3697+ }
3698+ }
3699+ }
3700+
3701+ private void ShowVersion(ClientRequestInfo requestInfo)
3702+ {
3703+ if (requestInfo.Request.Arguments.ContainsHelpRequest)
3704+ {
3705+ StringBuilder helpMessage = new StringBuilder();
3706+
3707+ helpMessage.Append("Shows the current service version.");
3708+ helpMessage.AppendLine();
3709+ helpMessage.AppendLine();
3710+ helpMessage.Append(" Usage:");
3711+ helpMessage.AppendLine();
3712+ helpMessage.Append(" Version -options");
3713+ helpMessage.AppendLine();
3714+ helpMessage.AppendLine();
3715+ helpMessage.Append(" Options:");
3716+ helpMessage.AppendLine();
3717+ helpMessage.Append(" -?".PadRight(20));
3718+ helpMessage.Append("Displays this help message");
3719+ helpMessage.AppendLine();
3720+ helpMessage.Append(" -actionable".PadRight(20));
3721+ helpMessage.Append("Returns results via an actionable event");
3722+ helpMessage.AppendLine();
3723+ helpMessage.AppendLine();
3724+
3725+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
3726+ }
3727+ else
3728+ {
3729+ StringBuilder versionInfo = new StringBuilder();
3730+ AssemblyInfo serviceAssembly = AssemblyInfo.EntryAssembly;
3731+ string serviceName;
3732+
3733+ if (m_parentService != null && !string.IsNullOrWhiteSpace(m_parentService.ServiceName))
3734+ serviceName = m_parentService.ServiceName;
3735+ else
3736+ serviceName = AppDomain.CurrentDomain.FriendlyName;
3737+
3738+ versionInfo.AppendFormat("{0} Service Version:\r\n\r\n", serviceName);
3739+ versionInfo.AppendFormat(" App Domain: {0}, running on .NET {1}\r\n", AppDomain.CurrentDomain.FriendlyName, Environment.Version.ToString());
3740+ versionInfo.AppendFormat(" Code Base: {0}\r\n", serviceAssembly.CodeBase);
3741+ versionInfo.AppendFormat(" Build Date: {0}\r\n", serviceAssembly.BuildDate.ToString());
3742+ versionInfo.AppendFormat(" Version: {0}", serviceAssembly.Version.ToString());
3743+
3744+ string message = versionInfo.ToString();
3745+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, message + "\r\n\r\n");
3746+
3747+ // Also allow consumers to directly consume message via event in response to a version request
3748+ if (requestInfo.Request.Arguments.Exists("actionable"))
3749+ SendActionableResponse(requestInfo, true, null, message);
3750+ }
3751+ }
3752+
3753+ private void ShowTime(ClientRequestInfo requestInfo)
3754+ {
3755+ if (requestInfo.Request.Arguments.ContainsHelpRequest)
3756+ {
3757+ StringBuilder helpMessage = new StringBuilder();
3758+
3759+ helpMessage.Append("Shows the current system time.");
3760+ helpMessage.AppendLine();
3761+ helpMessage.AppendLine();
3762+ helpMessage.Append(" Usage:");
3763+ helpMessage.AppendLine();
3764+ helpMessage.Append(" Time -options");
3765+ helpMessage.AppendLine();
3766+ helpMessage.AppendLine();
3767+ helpMessage.Append(" Options:");
3768+ helpMessage.AppendLine();
3769+ helpMessage.Append(" -?".PadRight(20));
3770+ helpMessage.Append("Displays this help message");
3771+ helpMessage.AppendLine();
3772+ helpMessage.Append(" -actionable".PadRight(20));
3773+ helpMessage.Append("Returns results via an actionable event");
3774+ helpMessage.AppendLine();
3775+ helpMessage.AppendLine();
3776+
3777+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
3778+ }
3779+ else
3780+ {
3781+ string message;
3782+ // 1 2 3 4 5 6 7 8
3783+ // 12345678901234567890123456789012345678901234567890123456789012345678901234567890
3784+ // Current system time: yyyy-MM-dd HH:mm:ss.fff, yyyy-MM-dd HH:mm:ss.fff UTC
3785+ // Total system runtime: xx days yy hours zz minutes ii seconds
3786+ if (m_remotingServer != null)
3787+ message = string.Format(" Current system time: {0}, {1} UTC\r\nTotal system runtime: {2}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff"), m_remotingServer.RunTime.ToString());
3788+ else
3789+ message = string.Format("Current system time: {0}, {1} UTC", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff"));
3790+
3791+ UpdateStatus(requestInfo.Sender.ClientID, UpdateType.Information, message + "\r\n\r\n");
3792+
3793+ // Also allow consumers to directly consume message via event in response to a time request
3794+ if (requestInfo.Request.Arguments.Exists("actionable"))
3795+ SendActionableResponse(requestInfo, true, null, message);
3796+ }
3797+ }
3798+
3799+ private void RemoteTelnetSession(ClientRequestInfo requestinfo)
3800+ {
3801+ if (m_remoteCommandProcess == null && requestinfo.Request.Arguments.ContainsHelpRequest)
3802+ {
3803+ StringBuilder helpMessage = new StringBuilder();
3804+
3805+ helpMessage.Append("Allows for a telnet session to the service server.");
3806+ helpMessage.AppendLine();
3807+ helpMessage.AppendLine();
3808+ helpMessage.Append(" Usage:");
3809+ helpMessage.AppendLine();
3810+ helpMessage.Append(" Telnet -options");
3811+ helpMessage.AppendLine();
3812+ helpMessage.AppendLine();
3813+ helpMessage.Append(" Options:");
3814+ helpMessage.AppendLine();
3815+ helpMessage.Append(" -?".PadRight(20));
3816+ helpMessage.Append("Displays this help message");
3817+ helpMessage.AppendLine();
3818+ helpMessage.Append(" -connect".PadRight(20));
3819+ helpMessage.Append("Establishes a telnet session (requires password)");
3820+ helpMessage.AppendLine();
3821+ helpMessage.Append(" -disconnect".PadRight(20));
3822+ helpMessage.Append("Terminates established telnet session");
3823+ helpMessage.AppendLine();
3824+ helpMessage.AppendLine();
3825+
3826+ UpdateStatus(requestinfo.Sender.ClientID, UpdateType.Information, helpMessage.ToString());
3827+ }
3828+ else
3829+ {
3830+ bool connectSession = requestinfo.Request.Arguments.Exists("connect");
3831+ bool disconnectSession = requestinfo.Request.Arguments.Exists("disconnect");
3832+
3833+ if (m_remoteCommandProcess == null && connectSession && !string.IsNullOrEmpty(requestinfo.Request.Arguments["connect"]))
3834+ {
3835+ // User wants to establish a remote command session.
3836+ string password = requestinfo.Request.Arguments["connect"];
3837+
3838+ if (password == m_telnetSessionPassword)
3839+ {
3840+ // Establish remote command session
3841+ m_remoteCommandProcess = new Process();
3842+ m_remoteCommandProcess.ErrorDataReceived += RemoteCommandProcess_ErrorDataReceived;
3843+ m_remoteCommandProcess.OutputDataReceived += RemoteCommandProcess_OutputDataReceived;
3844+ m_remoteCommandProcess.StartInfo.FileName = "cmd.exe";
3845+ m_remoteCommandProcess.StartInfo.UseShellExecute = false;
3846+ m_remoteCommandProcess.StartInfo.RedirectStandardInput = true;
3847+ m_remoteCommandProcess.StartInfo.RedirectStandardOutput = true;
3848+ m_remoteCommandProcess.StartInfo.RedirectStandardError = true;
3849+ m_remoteCommandProcess.Start();
3850+ m_remoteCommandProcess.BeginOutputReadLine();
3851+ m_remoteCommandProcess.BeginErrorReadLine();
3852+
3853+ UpdateStatus(UpdateType.Information, "Remote command session established - status updates are suspended.\r\n\r\n");
3854+
3855+ m_remoteCommandClientID = requestinfo.Sender.ClientID;
3856+ SendResponse(requestinfo.Sender.ClientID, new ServiceResponse("TelnetSession", "Established"));
3857+ }
3858+ else
3859+ {
3860+ UpdateStatus(requestinfo.Sender.ClientID, UpdateType.Alarm, "Failed to establish remote command session - Password is invalid.\r\n\r\n");
3861+ }
3862+ }
3863+ else if (string.Compare(requestinfo.Request.Command, "Telnet", true) == 0 && m_remoteCommandProcess != null && disconnectSession)
3864+ {
3865+ // User wants to terminate an established remote command session.
3866+ m_remoteCommandProcess.ErrorDataReceived -= RemoteCommandProcess_ErrorDataReceived;
3867+ m_remoteCommandProcess.OutputDataReceived -= RemoteCommandProcess_OutputDataReceived;
3868+
3869+ if (!m_remoteCommandProcess.HasExited)
3870+ m_remoteCommandProcess.Kill();
3871+
3872+ m_remoteCommandProcess.Dispose();
3873+ m_remoteCommandProcess = null;
3874+
3875+ m_remoteCommandClientID = Guid.Empty;
3876+ SendResponse(requestinfo.Sender.ClientID, new ServiceResponse("TelnetSession", "Terminated"));
3877+
3878+ UpdateStatus(UpdateType.Information, "Remote command session terminated - status updates are resumed.\r\n\r\n");
3879+ }
3880+ else if (m_remoteCommandProcess != null)
3881+ {
3882+ // User has entered commands that must be redirected to the established command session.
3883+ string input = requestinfo.Request.Command + " " + requestinfo.Request.Arguments.ToString();
3884+ m_remoteCommandProcess.StandardInput.WriteLine(input);
3885+ }
3886+ else
3887+ {
3888+ // User has provided insufficient information.
3889+ requestinfo.Request = ClientRequest.Parse("Telnet /?");
3890+ RemoteTelnetSession(requestinfo);
3891+ }
3892+ }
3893+ }
3894+
3895+ #endregion
3896+
3897+ #endregion
3898+ }
3899+}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t