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 (15 of 628 files). Show all

Unified · Split

15 files
  1. Source/Libraries/GSF.ServiceProcess/ClientHelper.bmp +0 −0
  2. Source/Libraries/GSF.ServiceProcess/ClientHelper.cs +1013 −0
  3. Source/Libraries/GSF.ServiceProcess/ClientInfo.cs +201 −0
  4. Source/Libraries/GSF.ServiceProcess/ClientRequest.cs +190 −0
  5. Source/Libraries/GSF.ServiceProcess/ClientRequestHandler.cs +100 −0
  6. Source/Libraries/GSF.ServiceProcess/ClientRequestInfo.cs +76 −0
  7. Source/Libraries/GSF.ServiceProcess/GSF.ServiceProcess.csproj +124 −0
  8. Source/Libraries/GSF.ServiceProcess/NamespaceDoc.cs +39 −0
  9. Source/Libraries/GSF.ServiceProcess/Properties/AssemblyInfo.cs +26 −0
  10. Source/Libraries/GSF.ServiceProcess/ServiceHelper.bmp +0 −0
  11. Source/Libraries/GSF.ServiceProcess/ServiceHelper.cs +3899 −0
  12. Source/Libraries/GSF.ServiceProcess/ServiceInstallerEx.cs +702 −0
  13. Source/Libraries/GSF.ServiceProcess/ServiceProcess.cs +400 −0
  14. Source/Libraries/GSF.ServiceProcess/ServiceResponse.cs +139 −0
  15. Source/Libraries/GSF.ServiceProcess/TVA.ServiceProcess.nuspec +35 −0
Source/Libraries/GSF.ServiceProcess/ClientHelper.bmp+0−0

Binary file not shown.

Source/Libraries/GSF.ServiceProcess/ClientHelper.cs+1013−0
@@ -0,0 +1,1013 @@
1+//*******************************************************************************************************
2+// ClientHelper.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+//
9+// Code Modification History:
10+// -----------------------------------------------------------------------------------------------------
11+// 08/29/2006 - Pinal C. Patel
12+// Original version of source code generated.
13+// 11/30/2007 - Pinal C. Patel
14+// Modified the "design time" check in EndInit() method to use LicenseManager.UsageMode property
15+// instead of DesignMode property as the former is more accurate than the latter.
16+// 09/30/2008 - J. Ritchie Carroll
17+// Convert to C#.
18+// 07/15/2009 - Pinal C. Patel
19+// Added AuthenticationMethod, AuthenticationUsername and AuthenticationPassword properties to
20+// provision for the authentication process as part of security.
21+// Added ISupportLifecycle, ISupportInitialize and IPersistSettings interface implementations
22+// to support the persistance and retrieval of settings from the config file.
23+// 07/17/2009 - Pinal C. Patel
24+// Added static PretendRequest() method that can be used to create pretend request for manually
25+// invoking request handlers registered with the ServiceHelper.
26+// 07/21/2009 - Pinal C. Patel
27+// Replace AuthenticationUsername and AuthenticationPassword properties with AuthenticationInput
28+// to allow for input text to be specified for any AuthenticationMethod instead of just Ntml.
29+// 07/23/2009 - Pinal C. Patel
30+// ReceivedServiceResponse is now raised only for custom service responses instead of all.
31+// 09/14/2009 - Stephen C. Wills
32+// Added new header and license agreement.
33+// 10/23/2009 - Pinal C. Patel
34+// Modified ReceivedServiceUpdate event to support the change in ServiceHelper.UpdateStatus().
35+// 06/16/2010 - Pinal C. Patel
36+// Made changes necessary to implement role-based security.
37+// 04/14/2011 - Pinal C. Patel
38+// Updated to use new deserialization methods in GSF.Serialization class.
39+//
40+//*******************************************************************************************************
41+
42+#region [ TVA Open Source Agreement ]
43+/*
44+
45+ THIS OPEN SOURCE AGREEMENT ("AGREEMENT") DEFINES THE RIGHTS OF USE,REPRODUCTION, DISTRIBUTION,
46+ MODIFICATION AND REDISTRIBUTION OF CERTAIN COMPUTER SOFTWARE ORIGINALLY RELEASED BY THE
47+ TENNESSEE VALLEY AUTHORITY, A CORPORATE AGENCY AND INSTRUMENTALITY OF THE UNITED STATES GOVERNMENT
48+ ("GOVERNMENT AGENCY"). GOVERNMENT AGENCY IS AN INTENDED THIRD-PARTY BENEFICIARY OF ALL SUBSEQUENT
49+ DISTRIBUTIONS OR REDISTRIBUTIONS OF THE SUBJECT SOFTWARE. ANYONE WHO USES, REPRODUCES, DISTRIBUTES,
50+ MODIFIES OR REDISTRIBUTES THE SUBJECT SOFTWARE, AS DEFINED HEREIN, OR ANY PART THEREOF, IS, BY THAT
51+ ACTION, ACCEPTING IN FULL THE RESPONSIBILITIES AND OBLIGATIONS CONTAINED IN THIS AGREEMENT.
52+
53+ Original Software Designation: openPDC
54+ Original Software Title: The GSF Open Source Phasor Data Concentrator
55+ User Registration Requested. Please Visit https://naspi.tva.com/Registration/
56+ Point of Contact for Original Software: J. Ritchie Carroll <mailto:jrcarrol@tva.gov>
57+
58+ 1. DEFINITIONS
59+
60+ A. "Contributor" means Government Agency, as the developer of the Original Software, and any entity
61+ that makes a Modification.
62+
63+ B. "Covered Patents" mean patent claims licensable by a Contributor that are necessarily infringed by
64+ the use or sale of its Modification alone or when combined with the Subject Software.
65+
66+ C. "Display" means the showing of a copy of the Subject Software, either directly or by means of an
67+ image, or any other device.
68+
69+ D. "Distribution" means conveyance or transfer of the Subject Software, regardless of means, to
70+ another.
71+
72+ E. "Larger Work" means computer software that combines Subject Software, or portions thereof, with
73+ software separate from the Subject Software that is not governed by the terms of this Agreement.
74+
75+ F. "Modification" means any alteration of, including addition to or deletion from, the substance or
76+ structure of either the Original Software or Subject Software, and includes derivative works, as that
77+ term is defined in the Copyright Statute, 17 USC § 101. However, the act of including Subject Software
78+ as part of a Larger Work does not in and of itself constitute a Modification.
79+
80+ G. "Original Software" means the computer software first released under this Agreement by Government
81+ Agency entitled openPDC, including source code, object code and accompanying documentation, if any.
82+
83+ H. "Recipient" means anyone who acquires the Subject Software under this Agreement, including all
84+ Contributors.
85+
86+ I. "Redistribution" means Distribution of the Subject Software after a Modification has been made.
87+
88+ J. "Reproduction" means the making of a counterpart, image or copy of the Subject Software.
89+
90+ K. "Sale" means the exchange of the Subject Software for money or equivalent value.
91+
92+ L. "Subject Software" means the Original Software, Modifications, or any respective parts thereof.
93+
94+ M. "Use" means the application or employment of the Subject Software for any purpose.
95+
96+ 2. GRANT OF RIGHTS
97+
98+ A. Under Non-Patent Rights: Subject to the terms and conditions of this Agreement, each Contributor,
99+ with respect to its own contribution to the Subject Software, hereby grants to each Recipient a
100+ non-exclusive, world-wide, royalty-free license to engage in the following activities pertaining to
101+ the Subject Software:
102+
103+ 1. Use
104+
105+ 2. Distribution
106+
107+ 3. Reproduction
108+
109+ 4. Modification
110+
111+ 5. Redistribution
112+
113+ 6. Display
114+
115+ B. Under Patent Rights: Subject to the terms and conditions of this Agreement, each Contributor, with
116+ respect to its own contribution to the Subject Software, hereby grants to each Recipient under Covered
117+ Patents a non-exclusive, world-wide, royalty-free license to engage in the following activities
118+ pertaining to the Subject Software:
119+
120+ 1. Use
121+
122+ 2. Distribution
123+
124+ 3. Reproduction
125+
126+ 4. Sale
127+
128+ 5. Offer for Sale
129+
130+ C. The rights granted under Paragraph B. also apply to the combination of a Contributor's Modification
131+ and the Subject Software if, at the time the Modification is added by the Contributor, the addition of
132+ such Modification causes the combination to be covered by the Covered Patents. It does not apply to
133+ any other combinations that include a Modification.
134+
135+ D. The rights granted in Paragraphs A. and B. allow the Recipient to sublicense those same rights.
136+ Such sublicense must be under the same terms and conditions of this Agreement.
137+
138+ 3. OBLIGATIONS OF RECIPIENT
139+
140+ A. Distribution or Redistribution of the Subject Software must be made under this Agreement except for
141+ additions covered under paragraph 3H.
142+
143+ 1. Whenever a Recipient distributes or redistributes the Subject Software, a copy of this Agreement
144+ must be included with each copy of the Subject Software; and
145+
146+ 2. If Recipient distributes or redistributes the Subject Software in any form other than source code,
147+ Recipient must also make the source code freely available, and must provide with each copy of the
148+ Subject Software information on how to obtain the source code in a reasonable manner on or through a
149+ medium customarily used for software exchange.
150+
151+ B. Each Recipient must ensure that the following copyright notice appears prominently in the Subject
152+ Software:
153+
154+ No copyright is claimed pursuant to 17 USC § 105. All Other Rights Reserved.
155+
156+ C. Each Contributor must characterize its alteration of the Subject Software as a Modification and
157+ must identify itself as the originator of its Modification in a manner that reasonably allows
158+ subsequent Recipients to identify the originator of the Modification. In fulfillment of these
159+ requirements, Contributor must include a file (e.g., a change log file) that describes the alterations
160+ made and the date of the alterations, identifies Contributor as originator of the alterations, and
161+ consents to characterization of the alterations as a Modification, for example, by including a
162+ statement that the Modification is derived, directly or indirectly, from Original Software provided by
163+ Government Agency. Once consent is granted, it may not thereafter be revoked.
164+
165+ D. A Contributor may add its own copyright notice to the Subject Software. Once a copyright notice has
166+ been added to the Subject Software, a Recipient may not remove it without the express permission of
167+ the Contributor who added the notice.
168+
169+ E. A Recipient may not make any representation in the Subject Software or in any promotional,
170+ advertising or other material that may be construed as an endorsement by Government Agency or by any
171+ prior Recipient of any product or service provided by Recipient, or that may seek to obtain commercial
172+ advantage by the fact of Government Agency's or a prior Recipient's participation in this Agreement.
173+
174+ F. In an effort to track usage and maintain accurate records of the Subject Software, each Recipient,
175+ upon receipt of the Subject Software, is requested to register with Government Agency by visiting the
176+ following website: https://naspi.tva.com/Registration/. Recipient's name and personal information
177+ shall be used for statistical purposes only. Once a Recipient makes a Modification available, it is
178+ requested that the Recipient inform Government Agency at the web site provided above how to access the
179+ Modification.
180+
181+ G. Each Contributor represents that that its Modification does not violate any existing agreements,
182+ regulations, statutes or rules, and further that Contributor has sufficient rights to grant the rights
183+ conveyed by this Agreement.
184+
185+ H. A Recipient may choose to offer, and to charge a fee for, warranty, support, indemnity and/or
186+ liability obligations to one or more other Recipients of the Subject Software. A Recipient may do so,
187+ however, only on its own behalf and not on behalf of Government Agency or any other Recipient. Such a
188+ Recipient must make it absolutely clear that any such warranty, support, indemnity and/or liability
189+ obligation is offered by that Recipient alone. Further, such Recipient agrees to indemnify Government
190+ Agency and every other Recipient for any liability incurred by them as a result of warranty, support,
191+ indemnity and/or liability offered by such Recipient.
192+
193+ I. A Recipient may create a Larger Work by combining Subject Software with separate software not
194+ governed by the terms of this agreement and distribute the Larger Work as a single product. In such
195+ case, the Recipient must make sure Subject Software, or portions thereof, included in the Larger Work
196+ is subject to this Agreement.
197+
198+ J. Notwithstanding any provisions contained herein, Recipient is hereby put on notice that export of
199+ any goods or technical data from the United States may require some form of export license from the
200+ U.S. Government. Failure to obtain necessary export licenses may result in criminal liability under
201+ U.S. laws. Government Agency neither represents that a license shall not be required nor that, if
202+ required, it shall be issued. Nothing granted herein provides any such export license.
203+
204+ 4. DISCLAIMER OF WARRANTIES AND LIABILITIES; WAIVER AND INDEMNIFICATION
205+
206+ A. No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, EITHER
207+ EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT THE SUBJECT
208+ SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
209+ PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL BE ERROR
210+ FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. THIS
211+ AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR
212+ RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS
213+ RESULTING FROM USE OF THE SUBJECT SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND
214+ LIABILITIES REGARDING THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT
215+ "AS IS."
216+
217+ B. Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST GOVERNMENT AGENCY, ITS
218+ AGENTS, EMPLOYEES, CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF RECIPIENT'S USE
219+ OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES OR LOSSES ARISING FROM
220+ SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING FROM, RECIPIENT'S USE OF THE
221+ SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS GOVERNMENT AGENCY, ITS AGENTS,
222+ EMPLOYEES, CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, TO THE EXTENT PERMITTED BY
223+ LAW. THE FOREGOING RELEASE AND INDEMNIFICATION SHALL APPLY EVEN IF THE LIABILITIES, DEMANDS, DAMAGES,
224+ EXPENSES OR LOSSES ARE CAUSED, OCCASIONED, OR CONTRIBUTED TO BY THE NEGLIGENCE, SOLE OR CONCURRENT, OF
225+ GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE
226+ IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT.
227+
228+ 5. GENERAL TERMS
229+
230+ A. Termination: This Agreement and the rights granted hereunder will terminate automatically if a
231+ Recipient fails to comply with these terms and conditions, and fails to cure such noncompliance within
232+ thirty (30) days of becoming aware of such noncompliance. Upon termination, a Recipient agrees to
233+ immediately cease use and distribution of the Subject Software. All sublicenses to the Subject
234+ Software properly granted by the breaching Recipient shall survive any such termination of this
235+ Agreement.
236+
237+ B. Severability: If any provision of this Agreement is invalid or unenforceable under applicable law,
238+ it shall not affect the validity or enforceability of the remainder of the terms of this Agreement.
239+
240+ C. Applicable Law: This Agreement shall be subject to United States federal law only for all purposes,
241+ including, but not limited to, determining the validity of this Agreement, the meaning of its
242+ provisions and the rights, obligations and remedies of the parties.
243+
244+ D. Entire Understanding: This Agreement constitutes the entire understanding and agreement of the
245+ parties relating to release of the Subject Software and may not be superseded, modified or amended
246+ except by further written agreement duly executed by the parties.
247+
248+ E. Binding Authority: By accepting and using the Subject Software under this Agreement, a Recipient
249+ affirms its authority to bind the Recipient to all terms and conditions of this Agreement and that
250+ Recipient hereby agrees to all terms and conditions herein.
251+
252+ F. Point of Contact: Any Recipient contact with Government Agency is to be directed to the designated
253+ representative as follows: J. Ritchie Carroll <mailto:jrcarrol@tva.gov>.
254+
255+*/
256+#endregion
257+
258+using GSF.Communication;
259+using GSF.Configuration;
260+using System;
261+using System.ComponentModel;
262+using System.Configuration;
263+using System.Drawing;
264+using System.Text;
265+using System.Threading;
266+
267+namespace GSF.ServiceProcess
268+{
269+ /// <summary>
270+ /// Component that provides client-side functionality to <see cref="ServiceHelper"/>.
271+ /// </summary>
272+ [ToolboxBitmap(typeof(ClientHelper))]
273+ public class ClientHelper : Component, ISupportLifecycle, ISupportInitialize, IPersistSettings
274+ {
275+ #region [ Members ]
276+
277+ // Constants
278+
279+ /// <summary>
280+ /// Specifies the default value for the <see cref="Username"/> property.
281+ /// </summary>
282+ public const string DefaultUsername = "";
283+
284+ /// <summary>
285+ /// Specifies the default value for the <see cref="Password"/> property.
286+ /// </summary>
287+ public const string DefaultPassword = "";
288+
289+ /// <summary>
290+ /// Specifies the default value for the <see cref="PersistSettings"/> property.
291+ /// </summary>
292+ public const bool DefaultPersistSettings = false;
293+
294+ /// <summary>
295+ /// Specifies the default value for the <see cref="SettingsCategory"/> property.
296+ /// </summary>
297+ public const string DefaultSettingsCategory = "ClientHelper";
298+
299+ // Events
300+
301+ /// <summary>
302+ /// Occurs when a status update is received from the <see cref="ServiceHelper"/>.
303+ /// </summary>
304+ [Category("Client"),
305+ Description("Occurs when a status update is received from the ServiceHelper.")]
306+ public event EventHandler<EventArgs<UpdateType, string>> ReceivedServiceUpdate;
307+
308+ /// <summary>
309+ /// Occurs when a custom <see cref="ServiceResponse"/> is received from the <see cref="ServiceHelper"/>.
310+ /// </summary>
311+ [Category("Service"),
312+ Description("Occurs when a ServiceResponse is received from the ServiceHelper.")]
313+ public event EventHandler<EventArgs<ServiceResponse>> ReceivedServiceResponse;
314+
315+ /// <summary>
316+ /// Occurs when the state of the <see cref="ServiceHelper"/> is changed.
317+ /// </summary>
318+ [Category("Service"),
319+ Description("Occurs when the state of the ServiceHelper is changed.")]
320+ public event EventHandler<EventArgs<ObjectState<ServiceState>>> ServiceStateChanged;
321+
322+ /// <summary>
323+ /// Occurs when the state of a <see cref="ServiceProcess"/> is changed.
324+ /// </summary>
325+ [Category("Service"),
326+ Description("Occurs when the state of a ServiceProcess is changed.")]
327+ public event EventHandler<EventArgs<ObjectState<ServiceProcessState>>> ProcessStateChanged;
328+
329+ /// <summary>
330+ /// Occurs when the <see cref="ServiceHelper"/> successfully authenticates the <see cref="ClientHelper"/>.
331+ /// </summary>
332+ [Category("Security"),
333+ Description("Occurs when the ServiceHelper successfully authenticates the ClientHelper.")]
334+ public event EventHandler AuthenticationSuccess;
335+
336+ /// <summary>
337+ /// Occurs when the <see cref="ServiceHelper"/> fails to authenticate the <see cref="ClientHelper"/>.
338+ /// </summary>
339+ /// <remarks>
340+ /// Set <see cref="CancelEventArgs.Cancel"/> to <b>true</b> to continue with connection attempts even after authentication fails.
341+ /// This can be useful for re-authenticating the <see cref="ClientHelper"/> using different <see cref="Username"/> and <see cref="Password"/>.
342+ /// </remarks>
343+ [Category("Security"),
344+ Description("Occurs when the ServiceHelper fails to authenticate the ClientHelper.")]
345+ public event EventHandler<CancelEventArgs> AuthenticationFailure;
346+
347+ /// <summary>
348+ /// Occurs when a telnet session has been established.
349+ /// </summary>
350+ [Category("Command"),
351+ Description("Occurs when a telnet session has been established.")]
352+ public event EventHandler TelnetSessionEstablished;
353+
354+ /// <summary>
355+ /// Occurs when a telnet session has been terminated.
356+ /// </summary>
357+ [Category("Command"),
358+ Description("Occurs when a telnet session has been terminated.")]
359+ public event EventHandler TelnetSessionTerminated;
360+
361+ // Fields
362+ private ClientBase m_remotingClient;
363+ private string m_username;
364+ private string m_password;
365+ private bool m_persistSettings;
366+ private string m_settingsCategory;
367+ private bool m_attemptReconnection;
368+ private bool m_authenticationComplete;
369+ private bool m_disposed;
370+ private bool m_initialized;
371+
372+ #endregion
373+
374+ #region [ Constructors ]
375+
376+ /// <summary>
377+ /// Initializes a new instance of the <see cref="ClientHelper"/> class.
378+ /// </summary>
379+ public ClientHelper()
380+ : base()
381+ {
382+ m_username = DefaultUsername;
383+ m_password = DefaultPassword;
384+ m_persistSettings = DefaultPersistSettings;
385+ m_settingsCategory = DefaultSettingsCategory;
386+ }
387+
388+ /// <summary>
389+ /// Initializes a new instance of the <see cref="ClientHelper"/> class.
390+ /// </summary>
391+ /// <param name="container"><see cref="IContainer"/> object that contains the <see cref="ClientHelper"/>.</param>
392+ public ClientHelper(IContainer container)
393+ : this()
394+ {
395+ if (container != null)
396+ container.Add(this);
397+ }
398+
399+ #endregion
400+
401+ #region [ Properties ]
402+
403+ /// <summary>
404+ /// Gets or sets the <see cref="ClientBase"/> object used for communicating with the <see cref="ServiceHelper"/>.
405+ /// </summary>
406+ [Category("Components"),
407+ Description("ClientBase object used for communicating with the ServiceHelper.")]
408+ public ClientBase RemotingClient
409+ {
410+ get
411+ {
412+ return m_remotingClient;
413+ }
414+ set
415+ {
416+ if (m_remotingClient != null)
417+ {
418+ // Detach events from any existing instance
419+ m_remotingClient.ConnectionEstablished -= RemotingClient_ConnectionEstablished;
420+ m_remotingClient.ConnectionAttempt -= RemotingClient_ConnectionAttempt;
421+ m_remotingClient.ConnectionTerminated -= RemotingClient_ConnectionTerminated;
422+ m_remotingClient.ReceiveDataComplete -= RemotingClient_ReceiveDataComplete;
423+ }
424+
425+ m_remotingClient = value;
426+
427+ if (m_remotingClient != null)
428+ {
429+ // Attach events to new instance
430+ m_remotingClient.ConnectionEstablished += RemotingClient_ConnectionEstablished;
431+ m_remotingClient.ConnectionAttempt += RemotingClient_ConnectionAttempt;
432+ m_remotingClient.ConnectionTerminated += RemotingClient_ConnectionTerminated;
433+ m_remotingClient.ReceiveDataComplete += RemotingClient_ReceiveDataComplete;
434+ }
435+ }
436+ }
437+
438+ /// <summary>
439+ /// Gets or sets the username of the <see cref="ClientHelper"/>'s user to be used for authenticating with the <see cref="ServiceHelper"/>.
440+ /// </summary>
441+ /// <exception cref="ArgumentNullException">The value being specified is a null string.</exception>
442+ [Category("Security"),
443+ DefaultValue(DefaultUsername),
444+ Description("Username of the ClientHelper's user to be used for authenticating with the ServiceHelper.")]
445+ public string Username
446+ {
447+ get
448+ {
449+ return m_username;
450+ }
451+ set
452+ {
453+ if (value == null)
454+ throw new ArgumentNullException("value");
455+
456+ m_username = value;
457+ }
458+ }
459+
460+ /// <summary>
461+ /// Gets or sets the password of the <see cref="ClientHelper"/>'s user to be used for authenticating with the <see cref="ServiceHelper"/>.
462+ /// </summary>
463+ /// <exception cref="ArgumentNullException">The value being specified is a null string.</exception>
464+ [Category("Security"),
465+ DefaultValue(DefaultPassword),
466+ Description("Password of the ClientHelper's user to be used for authenticating with the ServiceHelper.")]
467+ public string Password
468+ {
469+ get
470+ {
471+ return m_password;
472+ }
473+ set
474+ {
475+ if (value == null)
476+ throw new ArgumentNullException("value");
477+
478+ m_password = value;
479+ }
480+ }
481+
482+ /// <summary>
483+ /// Gets or sets a boolean value that indicates whether the settings of <see cref="ClientHelper"/> are to be saved to the config file.
484+ /// </summary>
485+ [Category("Persistance"),
486+ DefaultValue(DefaultPersistSettings),
487+ Description("Indicates whether the settings of ClientHelper are to be saved to the config file.")]
488+ public bool PersistSettings
489+ {
490+ get
491+ {
492+ return m_persistSettings;
493+ }
494+ set
495+ {
496+ m_persistSettings = value;
497+ }
498+ }
499+
500+ /// <summary>
501+ /// Gets or sets the category under which the settings of <see cref="ClientHelper"/> are to be saved to the config file if the <see cref="PersistSettings"/> property is set to true.
502+ /// </summary>
503+ /// <exception cref="ArgumentNullException">The value being assigned is a null or empty string.</exception>
504+ [Category("Persistance"),
505+ DefaultValue(DefaultSettingsCategory),
506+ Description("Category under which the settings of ClientHelper are to be saved to the config file if the PersistSettings property is set to true.")]
507+ public string SettingsCategory
508+ {
509+ get
510+ {
511+ return m_settingsCategory;
512+ }
513+ set
514+ {
515+ if (string.IsNullOrEmpty(value))
516+ throw new ArgumentNullException("value");
517+
518+ m_settingsCategory = value;
519+ }
520+ }
521+
522+ /// <summary>
523+ /// Gets or sets a boolean value that indicates whether the <see cref="ClientHelper"/> is currently enabled.
524+ /// </summary>
525+ /// <remarks>
526+ /// <see cref="Enabled"/> property is not be set by user-code directly.
527+ /// </remarks>
528+ [Browsable(false),
529+ EditorBrowsable(EditorBrowsableState.Never),
530+ DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
531+ public bool Enabled
532+ {
533+ get
534+ {
535+ if (m_remotingClient == null)
536+ return false;
537+ else
538+ return m_remotingClient.Enabled;
539+ }
540+ set
541+ {
542+ if (m_remotingClient != null)
543+ m_remotingClient.Enabled = value;
544+ }
545+ }
546+
547+
548+ #endregion
549+
550+ #region [ Methods ]
551+
552+ /// <summary>
553+ /// Initializes the <see cref="ClientHelper"/>.
554+ /// </summary>
555+ /// <remarks>
556+ /// <see cref="Initialize()"/> is to be called by user-code directly only if the <see cref="ClientHelper"/> is not consumed through the designer surface of the IDE.
557+ /// </remarks>
558+ public void Initialize()
559+ {
560+ if (!m_initialized)
561+ {
562+ LoadSettings(); // Load settings from the config file.
563+ m_initialized = true; // Initialize only once.
564+ }
565+ }
566+
567+ /// <summary>
568+ /// Performs necessary operations before the <see cref="ClientHelper"/> properties are initialized.
569+ /// </summary>
570+ /// <remarks>
571+ /// <see cref="BeginInit()"/> should never be called by user-code directly. This method exists solely for use
572+ /// by the designer if the <see cref="ClientHelper"/> is consumed through the designer surface of the IDE.
573+ /// </remarks>
574+ [EditorBrowsable(EditorBrowsableState.Never)]
575+ public void BeginInit()
576+ {
577+ if (!DesignMode)
578+ {
579+ try
580+ {
581+ // Nothing needs to be done before component is initialized.
582+ }
583+ catch (Exception)
584+ {
585+ // Prevent the IDE from crashing when component is in design mode.
586+ }
587+ }
588+ }
589+
590+ /// <summary>
591+ /// Performs necessary operations after the <see cref="ClientHelper"/> properties are initialized.
592+ /// </summary>
593+ /// <remarks>
594+ /// <see cref="EndInit()"/> should never be called by user-code directly. This method exists solely for use
595+ /// by the designer if the <see cref="ClientHelper"/> is consumed through the designer surface of the IDE.
596+ /// </remarks>
597+ [EditorBrowsable(EditorBrowsableState.Never)]
598+ public void EndInit()
599+ {
600+ if (!DesignMode)
601+ {
602+ try
603+ {
604+ Initialize();
605+ }
606+ catch (Exception)
607+ {
608+ // Prevent the IDE from crashing when component is in design mode.
609+ }
610+ }
611+ }
612+
613+ /// <summary>
614+ /// Saves settings for the <see cref="ClientHelper"/> to the config file if the <see cref="PersistSettings"/> property is set to true.
615+ /// </summary>
616+ /// <exception cref="ConfigurationErrorsException"><see cref="SettingsCategory"/> has a value of null or empty string.</exception>
617+ public void SaveSettings()
618+ {
619+ if (m_persistSettings)
620+ {
621+ // Ensure that settings category is specified.
622+ if (string.IsNullOrEmpty(m_settingsCategory))
623+ throw new ConfigurationErrorsException("SettingsCategory property has not been set");
624+
625+ // Save settings under the specified category.
626+ ConfigurationFile config = ConfigurationFile.Current;
627+ CategorizedSettingsElementCollection settings = config.Settings[m_settingsCategory];
628+ settings["Username", true].Update(m_username);
629+ settings["Password", true].Update(m_password);
630+ config.Save();
631+ }
632+ }
633+
634+ /// <summary>
635+ /// Loads saved settings for the <see cref="ClientHelper"/> from the config file if the <see cref="PersistSettings"/> property is set to true.
636+ /// </summary>
637+ /// <exception cref="ConfigurationErrorsException"><see cref="SettingsCategory"/> has a value of null or empty string.</exception>
638+ public void LoadSettings()
639+ {
640+ if (m_persistSettings)
641+ {
642+ // Ensure that settings category is specified.
643+ if (string.IsNullOrEmpty(m_settingsCategory))
644+ throw new ConfigurationErrorsException("SettingsCategory property has not been set");
645+
646+ // Load settings from the specified category.
647+ ConfigurationFile config = ConfigurationFile.Current;
648+ CategorizedSettingsElementCollection settings = config.Settings[m_settingsCategory];
649+ settings.Add("Username", m_username, "Username to be used for authentication with the service.");
650+ settings.Add("Password", m_password, "Password to be used for authentication with the service.", true);
651+ Username = settings["Username"].ValueAs(m_username);
652+ Password = settings["Password"].ValueAs(m_password);
653+ }
654+ }
655+
656+ /// <summary>
657+ /// Connects <see cref="RemotingClient"/> to <see cref="ServiceHelper.RemotingServer"/> and wait until authentication is complete.
658+ /// </summary>
659+ public void Connect()
660+ {
661+ if (m_remotingClient == null)
662+ throw new InvalidOperationException("RemotingClient property of ClientHelper component is not set");
663+
664+ m_attemptReconnection = true;
665+ m_authenticationComplete = false;
666+ m_remotingClient.Connect(); // Wait for connection.
667+
668+ while (!m_authenticationComplete && m_remotingClient.Enabled)
669+ {
670+ Thread.Sleep(100);
671+ } // Wait for authentication.
672+ }
673+
674+ /// <summary>
675+ /// Disconnects <see cref="RemotingClient"/> from <see cref="ServiceHelper.RemotingServer"/>.
676+ /// </summary>
677+ public void Disconnect()
678+ {
679+ m_attemptReconnection = false;
680+
681+ if (m_remotingClient != null)
682+ m_remotingClient.Disconnect();
683+ }
684+
685+ /// <summary>
686+ /// Sends a request to the <see cref="ServiceHelper"/> using <see cref="RemotingClient"/>.
687+ /// </summary>
688+ /// <param name="request">Request text to be sent.</param>
689+ public void SendRequest(string request)
690+ {
691+ ClientRequest requestInstance = ClientRequest.Parse(request);
692+
693+ if (requestInstance != null)
694+ SendRequest(requestInstance);
695+ else
696+ UpdateStatus(UpdateType.Warning, string.Format("Request command \"{0}\" is invalid\r\n\r\n", request));
697+ }
698+
699+ /// <summary>
700+ /// Sends a request to the <see cref="ServiceHelper"/> using <see cref="RemotingClient"/>.
701+ /// </summary>
702+ /// <param name="request"><see cref="ClientRequest"/> object to be sent.</param>
703+ public void SendRequest(ClientRequest request)
704+ {
705+ m_remotingClient.SendAsync(request);
706+ }
707+
708+ /// <summary>
709+ /// Raises the <see cref="ReceivedServiceUpdate"/> event.
710+ /// </summary>
711+ /// <param name="type">One of the <see cref="UpdateType"/> values.</param>
712+ /// <param name="update">Update message received.</param>
713+ protected virtual void OnReceivedServiceUpdate(UpdateType type, string update)
714+ {
715+ if (ReceivedServiceUpdate != null)
716+ ReceivedServiceUpdate(this, new EventArgs<UpdateType, string>(type, update));
717+ }
718+
719+ /// <summary>
720+ /// Raises the <see cref="ReceivedServiceResponse"/> event.
721+ /// </summary>
722+ /// <param name="response"><see cref="ServiceResponse"/> received.</param>
723+ protected virtual void OnReceivedServiceResponse(ServiceResponse response)
724+ {
725+ if (ReceivedServiceResponse != null)
726+ ReceivedServiceResponse(this, new EventArgs<ServiceResponse>(response));
727+ }
728+
729+ /// <summary>
730+ /// Raises the <see cref="ServiceStateChanged"/> event.
731+ /// </summary>
732+ /// <param name="state">New <see cref="ServiceState"/>.</param>
733+ protected virtual void OnServiceStateChanged(ObjectState<ServiceState> state)
734+ {
735+ if (ServiceStateChanged != null)
736+ ServiceStateChanged(this, new EventArgs<ObjectState<ServiceState>>(state));
737+ }
738+
739+ /// <summary>
740+ /// Raises the <see cref="ProcessStateChanged"/> event.
741+ /// </summary>
742+ /// <param name="state">New <see cref="ServiceProcessState"/>.</param>
743+ protected virtual void OnProcessStateChanged(ObjectState<ServiceProcessState> state)
744+ {
745+ if (ProcessStateChanged != null)
746+ ProcessStateChanged(this, new EventArgs<ObjectState<ServiceProcessState>>(state));
747+ }
748+
749+ /// <summary>
750+ /// Raises the <see cref="AuthenticationSuccess"/> event.
751+ /// </summary>
752+ protected virtual void OnAuthenticationSuccess()
753+ {
754+ m_authenticationComplete = true;
755+ if (AuthenticationSuccess != null)
756+ AuthenticationSuccess(this, EventArgs.Empty);
757+ }
758+
759+ /// <summary>
760+ /// Raises the <see cref="AuthenticationFailure"/> event.
761+ /// </summary>
762+ protected virtual void OnAuthenticationFailure()
763+ {
764+ CancelEventArgs args = new CancelEventArgs(true);
765+ if (AuthenticationFailure != null)
766+ AuthenticationFailure(this, args);
767+
768+ // Continue connection attempts if requested.
769+ if (args.Cancel)
770+ {
771+ m_attemptReconnection = false;
772+ m_authenticationComplete = true;
773+ }
774+ }
775+
776+ /// <summary>
777+ /// Raises the <see cref="TelnetSessionEstablished"/> event.
778+ /// </summary>
779+ protected virtual void OnTelnetSessionEstablished()
780+ {
781+ if (TelnetSessionEstablished != null)
782+ TelnetSessionEstablished(this, EventArgs.Empty);
783+ }
784+
785+ /// <summary>
786+ /// Raises the <see cref="TelnetSessionTerminated"/> event.
787+ /// </summary>
788+ protected virtual void OnTelnetSessionTerminated()
789+ {
790+ if (TelnetSessionTerminated != null)
791+ TelnetSessionTerminated(this, EventArgs.Empty);
792+ }
793+
794+ /// <summary>
795+ /// Releases the unmanaged resources used by the <see cref="ClientHelper"/> object and optionally releases the managed resources.
796+ /// </summary>
797+ /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
798+ protected override void Dispose(bool disposing)
799+ {
800+ if (!m_disposed)
801+ {
802+ try
803+ {
804+ // This will be done regardless of whether the object is finalized or disposed.
805+ if (disposing)
806+ {
807+ // This will be done only when the object is disposed by calling Dispose().
808+ Disconnect();
809+ SaveSettings();
810+ RemotingClient = null;
811+ }
812+ }
813+ finally
814+ {
815+ m_disposed = true; // Prevent duplicate dispose.
816+ base.Dispose(disposing); // Call base class Dispose().
817+ }
818+ }
819+ }
820+
821+ private void UpdateStatus(UpdateType type, string message, params object[] args)
822+ {
823+ OnReceivedServiceUpdate(type, string.Format(message, args));
824+ }
825+
826+ private void RemotingClient_ConnectionAttempt(object sender, System.EventArgs e)
827+ {
828+ UpdateStatus(UpdateType.Information, "Connecting to {0}...\r\n\r\n", m_remotingClient.ServerUri);
829+ }
830+
831+ private void RemotingClient_ConnectionEstablished(object sender, System.EventArgs e)
832+ {
833+ // Upon establishing connection with the service's communication client, we'll send our information to the
834+ // service so the service can keep track of all the client that are connected to its communication server.
835+ m_remotingClient.SendAsync(new ClientInfo(this));
836+
837+ StringBuilder status = new StringBuilder();
838+ status.AppendFormat("Connected to {0}:", m_remotingClient.ServerUri);
839+ status.AppendLine();
840+ status.AppendLine();
841+ status.Append(m_remotingClient.Status);
842+ status.AppendLine();
843+ UpdateStatus(UpdateType.Information, status.ToString());
844+ }
845+
846+ private void RemotingClient_ConnectionTerminated(object sender, System.EventArgs e)
847+ {
848+ StringBuilder status = new StringBuilder();
849+ status.AppendFormat("Disconnected from {0}:", m_remotingClient.ServerUri);
850+ status.AppendLine();
851+ status.AppendLine();
852+ status.Append(m_remotingClient.Status);
853+ status.AppendLine();
854+ UpdateStatus(UpdateType.Warning, status.ToString());
855+
856+ // Attempt reconnection on a seperate thread.
857+ if (m_attemptReconnection)
858+ new Thread((ThreadStart)delegate()
859+ {
860+ Connect();
861+ }).Start();
862+ }
863+
864+ private void RemotingClient_ReceiveDataComplete(object sender, EventArgs<byte[], int> e)
865+ {
866+ ServiceResponse response = null;
867+ Serialization.TryDeserialize<ServiceResponse>(e.Argument1.BlockCopy(0, e.Argument2), SerializationFormat.Binary, out response);
868+ if (response != null)
869+ {
870+ switch (response.Type)
871+ {
872+ case "UPDATECLIENTSTATUS-INFORMATION":
873+ UpdateStatus(UpdateType.Information, response.Message);
874+ break;
875+ case "UPDATECLIENTSTATUS-WARNING":
876+ UpdateStatus(UpdateType.Warning, response.Message);
877+ break;
878+ case "UPDATECLIENTSTATUS-ALARM":
879+ UpdateStatus(UpdateType.Alarm, response.Message);
880+ break;
881+ case "AUTHENTICATIONSUCCESS":
882+ OnAuthenticationSuccess();
883+ break;
884+ case "AUTHENTICATIONFAILURE":
885+ OnAuthenticationFailure();
886+ break;
887+ case "SERVICESTATECHANGED":
888+ if (response.Attachments.Count > 0)
889+ {
890+ ObjectState<ServiceState> state = response.Attachments[0] as ObjectState<ServiceState>;
891+
892+ if (state != null)
893+ {
894+ // Notify change in service state by raising an event.
895+ OnServiceStateChanged(state);
896+
897+ // Provide a status update for change in state of the service.
898+ UpdateType type = UpdateType.Information;
899+ switch (state.CurrentState)
900+ {
901+ case ServiceState.Stopped:
902+ case ServiceState.Paused:
903+ case ServiceState.Shutdown:
904+ type = UpdateType.Warning;
905+ break;
906+ }
907+ UpdateStatus(type, string.Format("State of service \"{0}\" has changed to \"{1}\".\r\n\r\n", state.ObjectName, state.CurrentState));
908+ }
909+ }
910+ break;
911+ case "PROCESSSTATECHANGED":
912+ if (response.Attachments.Count > 0)
913+ {
914+ ObjectState<ServiceProcessState> state = response.Attachments[0] as ObjectState<ServiceProcessState>;
915+
916+ if (state != null)
917+ {
918+ // Notify change in process state by raising an event.
919+ OnProcessStateChanged(state);
920+
921+ // Provide a status update for change in state of the service process.
922+ UpdateType type = UpdateType.Information;
923+ switch (state.CurrentState)
924+ {
925+ case ServiceProcessState.Aborted:
926+ case ServiceProcessState.Exception:
927+ type = UpdateType.Alarm;
928+ break;
929+
930+ }
931+ UpdateStatus(type, string.Format("State of process \"{0}\" has changed to \"{1}\".\r\n\r\n", state.ObjectName, state.CurrentState));
932+ }
933+ }
934+ break;
935+ case "TELNETSESSION":
936+ switch (response.Message.ToUpper())
937+ {
938+ case "ESTABLISHED":
939+ OnTelnetSessionEstablished();
940+ break;
941+ case "TERMINATED":
942+ OnTelnetSessionTerminated();
943+ break;
944+ }
945+ break;
946+ default:
947+ OnReceivedServiceResponse(response);
948+ break;
949+ }
950+ }
951+ }
952+
953+ #endregion
954+
955+ #region [ Static ]
956+
957+ /// <summary>
958+ /// Returns an <see cref="ClientRequestInfo"/> object for the specified <paramref name="requestCommand"/> that can be used
959+ /// to invoke <see cref="ServiceHelper.ClientRequestHandlers"/> manually as if the request was sent by a <see cref="ClientHelper"/> remotely.
960+ /// </summary>
961+ /// <param name="requestCommand">Command for which an <see cref="ClientRequestInfo"/> object is to be created.</param>
962+ /// <returns>An <see cref="ClientRequestInfo"/> object.</returns>
963+ public static ClientRequestInfo PretendRequest(string requestCommand)
964+ {
965+ ClientRequest request = ClientRequest.Parse(requestCommand);
966+ ClientRequestInfo requestInfo = new ClientRequestInfo(new ClientInfo(), request);
967+
968+ return requestInfo;
969+ }
970+
971+ /// <summary>
972+ /// Attempts to parse an actionable reponse sent from the service.
973+ /// </summary>
974+ /// <param name="serviceResponse"><see cref="ServiceResponse"/> to test for actionable response.</param>
975+ /// <param name="sourceCommand">Command that invoked <paramref name="serviceResponse"/>.</param>
976+ /// <param name="responseSuccess">Boolean success state of <paramref name="serviceResponse"/>.</param>
977+ /// <returns><c>true</c> if actionable response was able to be parsed successfully; otherwise <c>false</c>.</returns>
978+ public static bool TryParseActionableResponse(ServiceResponse serviceResponse, out string sourceCommand, out bool responseSuccess)
979+ {
980+ bool parseSucceeded = false;
981+
982+ sourceCommand = null;
983+ responseSuccess = false;
984+
985+ try
986+ {
987+ string response = serviceResponse.Type;
988+
989+ // Attempt to parse response message
990+ if (!string.IsNullOrWhiteSpace(response))
991+ {
992+ // Reponse types are formatted as "Command:Success" or "Command:Failure"
993+ string[] parts = response.Split(':');
994+
995+ if (parts.Length > 1)
996+ {
997+ sourceCommand = parts[0].Trim().ToTitleCase();
998+ responseSuccess = (string.Compare(parts[1].Trim(), "Success", true) == 0);
999+ parseSucceeded = true;
1000+ }
1001+ }
1002+ }
1003+ catch
1004+ {
1005+ parseSucceeded = false;
1006+ }
1007+
1008+ return parseSucceeded;
1009+ }
1010+
1011+ #endregion
1012+ }
1013+}
Source/Libraries/GSF.ServiceProcess/ClientInfo.cs+201−0
@@ -0,0 +1,201 @@
1+//******************************************************************************************************
2+// ClientInfo.cs - Gbtc
3+//
4+// Copyright � 2010, Grid Protection Alliance. All Rights Reserved.
5+//
6+// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
7+// the NOTICE file distributed with this work for additional information regarding copyright ownership.
8+// The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
9+// not use this file except in compliance with the License. You may obtain a copy of the License at:
10+//
11+// http://www.opensource.org/licenses/eclipse-1.0.php
12+//
13+// Unless agreed to in writing, the subject software distributed under the License is distributed on an
14+// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
15+// License for the specific language governing permissions and limitations.
16+//
17+// Code Modification History:
18+// ----------------------------------------------------------------------------------------------------
19+// 09/12/2006 - Pinal C. Patel
20+// Generated original version of source code.
21+// 09/30/2008 - James R. Carroll
22+// Converted to C#.
23+// 03/09/2009 - Pinal C. Patel
24+// Edited code comments.
25+// 07/10/2009 - Pinal C. Patel
26+// Modified to transmit serialized identity token used for authentication by the ServiceHelper.
27+// 07/21/2009 - Pinal C. Patel
28+// Modified identity token generation to use the new ClientHelper.AuthenticationInput property.
29+// 09/14/2009 - Stephen C. Wills
30+// Added new header and license agreement.
31+// 02/08/2010 - Pinal C. Patel
32+// Corrected the assignment of ClientName property for web applications.
33+// 06/16/2010 - Pinal C. Patel
34+// Made changes necessary to implement role-based security.
35+// 10/8/2012 - Danyelle Gilliam
36+// Modified Header
37+//
38+//******************************************************************************************************
39+
40+
41+
42+using GSF.Identity;
43+using GSF.Reflection;
44+using System;
45+using System.Security.Principal;
46+using System.Web.Hosting;
47+
48+namespace GSF.ServiceProcess
49+{
50+ /// <summary>
51+ /// Represents information about a client using <see cref="ClientHelper"/> for connecting to a Windows Service that uses <see cref="ServiceHelper"/>.
52+ /// </summary>
53+ /// <remarks>
54+ /// <see cref="ClientInfo"/> can be serialized and deserialized using <see cref="System.Runtime.Serialization.Formatters.Binary.BinaryFormatter"/> only.
55+ /// </remarks>
56+ /// <seealso cref="ClientHelper"/>
57+ /// <seealso cref="ServiceHelper"/>
58+ [Serializable()]
59+ public class ClientInfo
60+ {
61+ #region [ Members ]
62+
63+ // Fields
64+ private Guid m_clientID;
65+ private ApplicationType m_clientType;
66+ private string m_clientName;
67+ private IPrincipal m_clientUser;
68+ private string m_clientUserCredentials;
69+ private string m_machineName;
70+ private DateTime m_connectedAt;
71+
72+ #endregion
73+
74+ #region [ Constructors ]
75+
76+ /// <summary>
77+ /// Initializes a new instance of the <see cref="ClientInfo"/> class.
78+ /// </summary>
79+ public ClientInfo()
80+ : this(null)
81+ {
82+ }
83+
84+ /// <summary>
85+ /// Initializes a new instance of the <see cref="ClientInfo"/> class.
86+ /// </summary>
87+ /// <param name="parent">An <see cref="ClientHelper"/> object.</param>
88+ public ClientInfo(ClientHelper parent)
89+ {
90+ // Initialize member variables.
91+ m_clientType = Common.GetApplicationType();
92+ m_machineName = Environment.MachineName;
93+
94+ // Initialize user principal.
95+ if (m_clientType == ApplicationType.Web)
96+ m_clientUser = new GenericPrincipal(new GenericIdentity(UserInfo.RemoteUserID), new string[] { });
97+ else
98+ m_clientUser = new GenericPrincipal(new GenericIdentity(UserInfo.CurrentUserID), new string[] { });
99+
100+ // TODO: Must validate that SSL is enabled before sending unencrypted username/password across the wire.
101+ // Initialize user credentials.
102+ if (parent == null || string.IsNullOrEmpty(parent.Username) || string.IsNullOrEmpty(parent.Password))
103+ m_clientUserCredentials = string.Empty;
104+ else
105+ m_clientUserCredentials = string.Format("{0}:{1}", parent.Username, parent.Password);
106+
107+ // Initialize client application name.
108+ if (m_clientType == ApplicationType.Web)
109+ {
110+ if (HostingEnvironment.ApplicationVirtualPath == "/")
111+ m_clientName = HostingEnvironment.SiteName;
112+ else
113+ m_clientName = HostingEnvironment.ApplicationVirtualPath.Trim('/');
114+ }
115+ else
116+ {
117+ m_clientName = AssemblyInfo.EntryAssembly.Name;
118+ }
119+ }
120+
121+ #endregion
122+
123+ #region [ Properties ]
124+
125+ /// <summary>
126+ /// Gets the identifier of the remote client application.
127+ /// </summary>
128+ public Guid ClientID
129+ {
130+ get { return m_clientID; }
131+ set { m_clientID = value; }
132+ }
133+
134+ /// <summary>
135+ /// Gets the <see cref="ApplicationType"/> of the remote client application.
136+ /// </summary>
137+ public ApplicationType ClientType
138+ {
139+ get { return m_clientType; }
140+ }
141+
142+ /// <summary>
143+ /// Gets the friendly name of the remote client application.
144+ /// </summary>
145+ public string ClientName
146+ {
147+ get { return m_clientName; }
148+ }
149+
150+ /// <summary>
151+ /// Gets the <see cref="IPrincipal"/> of the remote client application's user.
152+ /// </summary>
153+ public IPrincipal ClientUser
154+ {
155+ get { return m_clientUser; }
156+ }
157+
158+ /// <summary>
159+ /// Gets the credentials in 'username:password' format for authenticating the remote client application's user if a valid <see cref="ClientUser"/> is not available.
160+ /// </summary>
161+ public string ClientUserCredentials
162+ {
163+ get { return m_clientUserCredentials; }
164+ }
165+
166+ /// <summary>
167+ /// Gets the name of the machine running the remote client application.
168+ /// </summary>
169+ public string MachineName
170+ {
171+ get { return m_machineName; }
172+ }
173+
174+ /// <summary>
175+ /// Gets or sets the <see cref="DateTime"/> when the remote client application connected to the <see cref="ServiceHelper"/>.
176+ /// </summary>
177+ public DateTime ConnectedAt
178+ {
179+ get { return m_connectedAt; }
180+ set { m_connectedAt = value; }
181+ }
182+
183+ #endregion
184+
185+ #region [ Methods ]
186+
187+ /// <summary>
188+ /// Updates the <see cref="ClientUser"/>.
189+ /// </summary>
190+ /// <param name="user">New <see cref="IPrincipal"/> object to be assigned to <see cref="ClientUser"/>.</param>
191+ internal void SetClientUser(IPrincipal user)
192+ {
193+ if (user == null)
194+ throw new ArgumentNullException("user");
195+
196+ m_clientUser = user;
197+ }
198+
199+ #endregion
200+ }
201+}
Source/Libraries/GSF.ServiceProcess/ClientRequest.cs+190−0
@@ -0,0 +1,190 @@
1+//******************************************************************************************************
2+// ClientRequest.cs - Gbtc
3+//
4+// Copyright © 2010, Grid Protection Alliance. All Rights Reserved.
5+//
6+// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
7+// the NOTICE file distributed with this work for additional information regarding copyright ownership.
8+// The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
9+// not use this file except in compliance with the License. You may obtain a copy of the License at:
10+//
11+// http://www.opensource.org/licenses/eclipse-1.0.php
12+//
13+// Unless agreed to in writing, the subject software distributed under the License is distributed on an
14+// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
15+// License for the specific language governing permissions and limitations.
16+//
17+// Code Modification History:
18+// ----------------------------------------------------------------------------------------------------
19+// 08/29/2006 - Pinal C. Patel
20+// Original version of source code generated.
21+// 04/27/2007 - Pinal C. Patel
22+// Added Attachments property for clients to send serializable objects as part of the request.
23+// 09/30/2008 - J. Ritchie Carroll
24+// Converted to C#.
25+// 03/09/2009 - Pinal C. Patel
26+// Edited code comments.
27+// 09/14/2009 - Stephen C. Wills
28+// Added new header and license agreement.
29+// 10/14/2010 - Pinal C. Patel
30+// Overrode ToString() method to provide a text representation of ClientRequest.
31+// Recoded static Parse() method to make it more robust.
32+// 01/24/201 - Pinal C. Patel
33+// Modified ToString() remove leading and trailing white spaces.
34+// 10/8/2012 - Danyelle Gilliam
35+// Modified Header
36+//
37+//******************************************************************************************************
38+
39+
40+
41+using GSF.Console;
42+using System;
43+using System.Collections.Generic;
44+
45+namespace GSF.ServiceProcess
46+{
47+ /// <summary>
48+ /// Represents a request sent by <see cref="ClientHelper"/> to <see cref="ServiceHelper"/>.
49+ /// </summary>
50+ /// <seealso cref="ClientHelper"/>
51+ /// <seealso cref="ServiceHelper"/>
52+ [Serializable()]
53+ public class ClientRequest
54+ {
55+ #region [ Members ]
56+
57+ // Fields
58+ private string m_command;
59+ private Arguments m_arguments;
60+ private List<object> m_attachments;
61+
62+ #endregion
63+
64+ #region [ Constructors ]
65+
66+ /// <summary>
67+ /// Initializes a new instance of the <see cref="ClientRequest"/> class.
68+ /// </summary>
69+ public ClientRequest()
70+ : this("UNDEFINED")
71+ {
72+ }
73+
74+ /// <summary>
75+ /// Initializes a new instance of the <see cref="ClientRequest"/> class.
76+ /// </summary>
77+ /// <param name="command">Command text for the <see cref="ClientRequest"/>.</param>
78+ public ClientRequest(string command)
79+ : this(command, new Arguments(""))
80+ {
81+ }
82+
83+ /// <summary>
84+ /// Initializes a new instance of the <see cref="ClientRequest"/> class.
85+ /// </summary>
86+ /// <param name="command">Command text for the <see cref="ClientRequest"/>.</param>
87+ /// <param name="arguments"><see cref="Arguments"/> for the <paramref name="command"/>.</param>
88+ public ClientRequest(string command, Arguments arguments)
89+ {
90+ m_command = command.ToUpper();
91+ m_arguments = arguments;
92+ m_attachments = new List<object>();
93+ }
94+
95+ #endregion
96+
97+ #region [ Properties ]
98+
99+ /// <summary>
100+ /// Gets or sets the command text for the <see cref="ClientRequest"/>.
101+ /// </summary>
102+ /// <exception cref="ArgumentNullException">The value being assigned is either a null or empty string.</exception>
103+ public string Command
104+ {
105+ get
106+ {
107+ return m_command;
108+ }
109+ set
110+ {
111+ if (string.IsNullOrEmpty(value))
112+ throw new ArgumentNullException("value");
113+
114+ m_command = value.ToUpper();
115+ }
116+ }
117+
118+ /// <summary>
119+ /// Gets or sets the <see cref="Arguments"/> for the <see cref="Command"/>.
120+ /// </summary>
121+ public Arguments Arguments
122+ {
123+ get
124+ {
125+ return m_arguments;
126+ }
127+ set
128+ {
129+ m_arguments = value;
130+ }
131+ }
132+
133+ /// <summary>
134+ /// Gets a list of attachments for the <see cref="ClientRequest"/>.
135+ /// </summary>
136+ public List<object> Attachments
137+ {
138+ get
139+ {
140+ return m_attachments;
141+ }
142+ }
143+
144+ #endregion
145+
146+ #region [ Methods ]
147+
148+ /// <summary>
149+ /// Returns the <see cref="String"/> that represents the <see cref="ClientRequest"/>.
150+ /// </summary>
151+ /// <returns>A <see cref="String"/> that represents the <see cref="ClientRequest"/>.</returns>
152+ public override string ToString()
153+ {
154+ return string.Format("{0} {1}", m_command, m_arguments).Trim();
155+ }
156+
157+ #endregion
158+
159+ #region [ Static ]
160+
161+ /// <summary>
162+ /// Converts <see cref="string"/> to a <see cref="ClientRequest"/>.
163+ /// </summary>
164+ /// <param name="text">Text to be converted to a <see cref="ClientRequest"/>.</param>
165+ /// <returns><see cref="ClientRequest"/> object if parsing is successful; otherwise null.</returns>
166+ public static ClientRequest Parse(string text)
167+ {
168+ // Input text can't be null.
169+ if (text == null)
170+ return null;
171+
172+ // Input text can't be empty.
173+ text = text.Trim();
174+ if (text == "")
175+ return null;
176+
177+ string[] textSegments = text.Split(' ');
178+ ClientRequest request = new ClientRequest();
179+ request.Command = textSegments[0].ToUpper();
180+ if (textSegments.Length == 1)
181+ request.Arguments = new Arguments("");
182+ else
183+ request.Arguments = new Arguments(text.Remove(0, text.IndexOf(' ') + 1).Trim());
184+
185+ return request;
186+ }
187+
188+ #endregion
189+ }
190+}
Source/Libraries/GSF.ServiceProcess/ClientRequestHandler.cs+100−0
@@ -0,0 +1,100 @@
1+//******************************************************************************************************
2+// ClientRequestHandler.cs - Gbtc
3+//
4+// Copyright � 2010, Grid Protection Alliance. All Rights Reserved.
5+//
6+// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
7+// the NOTICE file distributed with this work for additional information regarding copyright ownership.
8+// The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
9+// not use this file except in compliance with the License. You may obtain a copy of the License at:
10+//
11+// http://www.opensource.org/licenses/eclipse-1.0.php
12+//
13+// Unless agreed to in writing, the subject software distributed under the License is distributed on an
14+// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
15+// License for the specific language governing permissions and limitations.
16+//
17+// Code Modification History:
18+// ----------------------------------------------------------------------------------------------------
19+// 05/02/2007 - Pinal C. Patel
20+// Generated original version of source code.
21+// 09/30/2008 - J. Ritchie Carroll
22+// Converted to C#.
23+// 03/09/2009 - Pinal C. Patel
24+// Edited code comments.
25+// 09/14/2009 - Stephen C. Wills
26+// Added new header and license agreement.
27+// 10/8/2012 - Danyelle Gilliam
28+// Modified Header
29+//
30+//******************************************************************************************************
31+
32+
33+
34+using System;
35+
36+namespace GSF.ServiceProcess
37+{
38+ /// <summary>
39+ /// Represents a handler for <see cref="ClientRequest"/>s sent by <see cref="ClientHelper"/>.
40+ /// </summary>
41+ /// <seealso cref="ClientHelper"/>
42+ /// <seealso cref="ClientRequest"/>
43+ /// <seealso cref="ServiceHelper"/>
44+ public class ClientRequestHandler
45+ {
46+ #region [ Constructors ]
47+
48+ /// <summary>
49+ /// Initializes a new instance of the <see cref="ClientRequestHandler"/> class.
50+ /// </summary>
51+ /// <param name="requestCommand">Command text that the <see cref="ClientRequestHandler"/> will process.</param>
52+ /// <param name="requestDescription">Description of the <see cref="ClientRequestHandler"/>.</param>
53+ /// <param name="handlerMethod"><see cref="Delegate"/> method that will be invoked for processing the <paramref name="requestCommand"/>.</param>
54+ public ClientRequestHandler(string requestCommand, string requestDescription, Action<ClientRequestInfo> handlerMethod)
55+ : this(requestCommand, requestDescription, handlerMethod, true)
56+ {
57+ }
58+
59+ /// <summary>
60+ /// Initializes a new instance of the <see cref="ClientRequestHandler"/> class.
61+ /// </summary>
62+ /// <param name="requestCommand">Command text that the <see cref="ClientRequestHandler"/> will process.</param>
63+ /// <param name="requestDescription">Description of the <see cref="ClientRequestHandler"/>.</param>
64+ /// <param name="handlerMethod"><see cref="Delegate"/> method that will be invoked for processing the <paramref name="requestCommand"/>.</param>
65+ /// <param name="isAdvertised">true if the <see cref="ClientRequestHandler"/> is to be published by the <see cref="ServiceHelper"/>; otherwise false.</param>
66+ public ClientRequestHandler(string requestCommand, string requestDescription, Action<ClientRequestInfo> handlerMethod, bool isAdvertised)
67+ {
68+ Command = requestCommand;
69+ CommandDescription = requestDescription;
70+ HandlerMethod = handlerMethod;
71+ IsAdvertised = isAdvertised;
72+ }
73+
74+ #endregion
75+
76+ #region [ Properties ]
77+
78+ /// <summary>
79+ /// Gets the command text that the <see cref="ClientRequestHandler"/> will process.
80+ /// </summary>
81+ public string Command { get; private set; }
82+
83+ /// <summary>
84+ /// Gets the description of the <see cref="ClientRequestHandler"/>.
85+ /// </summary>
86+ public string CommandDescription { get; private set; }
87+
88+ /// <summary>
89+ /// Gets the <see cref="Delegate"/> method that gets invoked for processing the <see cref="Command"/>.
90+ /// </summary>
91+ public Action<ClientRequestInfo> HandlerMethod { get; private set; }
92+
93+ /// <summary>
94+ /// Gets a boolean value that indicates whether the <see cref="ClientRequestHandler"/> will be published by the <see cref="ServiceHelper"/>.
95+ /// </summary>
96+ public bool IsAdvertised { get; private set; }
97+
98+ #endregion
99+ }
100+}
Source/Libraries/GSF.ServiceProcess/ClientRequestInfo.cs+76−0
@@ -0,0 +1,76 @@
1+//******************************************************************************************************
2+// ClientRequestInfo.cs - Gbtc
3+//
4+// Copyright © 2010, Grid Protection Alliance. All Rights Reserved.
5+//
6+// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
7+// the NOTICE file distributed with this work for additional information regarding copyright ownership.
8+// The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
9+// not use this file except in compliance with the License. You may obtain a copy of the License at:
10+//
11+// http://www.opensource.org/licenses/eclipse-1.0.php
12+//
13+// Unless agreed to in writing, the subject software distributed under the License is distributed on an
14+// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
15+// License for the specific language governing permissions and limitations.
16+//
17+// Code Modification History:
18+// ----------------------------------------------------------------------------------------------------
19+// 05/02/2007 - Pinal C. Patel
20+// Generated original version of source code.
21+// 09/14/2009 - Stephen C. Wills
22+// Added new header and license agreement.
23+// 10/8/2012 - Danyelle Gilliam
24+// Modified Header
25+//
26+//******************************************************************************************************
27+
28+
29+
30+using System;
31+
32+namespace GSF.ServiceProcess
33+{
34+ /// <summary>
35+ /// Represents information about a <see cref="ClientRequest"/> sent by <see cref="ClientHelper"/>.
36+ /// </summary>
37+ /// <seealso cref="ClientInfo"/>
38+ /// <seealso cref="ClientRequest"/>
39+ public class ClientRequestInfo
40+ {
41+ #region [ Constructors ]
42+
43+ /// <summary>
44+ /// Initializes a new instance of the <see cref="ClientRequestInfo"/> class.
45+ /// </summary>
46+ /// <param name="sender"><see cref="ClientInfo"/> object of the <paramref name="request"/> sender.</param>
47+ /// <param name="request"><see cref="ClientRequest"/> object sent by the <paramref name="sender"/>.</param>
48+ public ClientRequestInfo(ClientInfo sender, ClientRequest request)
49+ {
50+ Request = request;
51+ Sender = sender;
52+ ReceivedAt = DateTime.Now;
53+ }
54+
55+ #endregion
56+
57+ #region [ Properties ]
58+
59+ /// <summary>
60+ /// Gets or sets the <see cref="ClientInfo"/> object of the <see cref="Request"/> sender.
61+ /// </summary>
62+ public ClientInfo Sender { get; set; }
63+
64+ /// <summary>
65+ /// Gets or sets the <see cref="ClientRequest"/> object sent by the <see cref="Sender"/>.
66+ /// </summary>
67+ public ClientRequest Request { get; set; }
68+
69+ /// <summary>
70+ /// Gets or sets the <see cref="DateTime"/> when the <see cref="Request"/> was received from the <see cref="Sender"/>.
71+ /// </summary>
72+ public DateTime ReceivedAt { get; set; }
73+
74+ #endregion
75+ }
76+}
Source/Libraries/GSF.ServiceProcess/GSF.ServiceProcess.csproj+124−0
@@ -0,0 +1,124 @@
1+<?xml version="1.0" encoding="utf-8"?>
2+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+ <PropertyGroup>
4+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6+ <ProductVersion>8.0.30703</ProductVersion>
7+ <SchemaVersion>2.0</SchemaVersion>
8+ <ProjectGuid>{2E23E926-CF64-4607-BDE1-1B2FC76F4919}</ProjectGuid>
9+ <OutputType>Library</OutputType>
10+ <AppDesignerFolder>Properties</AppDesignerFolder>
11+ <RootNamespace>GSF.ServiceProcess</RootNamespace>
12+ <AssemblyName>GSF.ServiceProcess</AssemblyName>
13+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
14+ <FileAlignment>512</FileAlignment>
15+ <SccProjectName>SAK</SccProjectName>
16+ <SccLocalPath>SAK</SccLocalPath>
17+ <SccAuxPath>SAK</SccAuxPath>
18+ <SccProvider>SAK</SccProvider>
19+ <TargetFrameworkProfile />
20+ </PropertyGroup>
21+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
22+ <DebugSymbols>true</DebugSymbols>
23+ <DebugType>full</DebugType>
24+ <Optimize>false</Optimize>
25+ <OutputPath>..\..\..\Build\Output\Debug\Libraries\</OutputPath>
26+ <DefineConstants>DEBUG;TRACE</DefineConstants>
27+ <ErrorReport>prompt</ErrorReport>
28+ <WarningLevel>4</WarningLevel>
29+ <DocumentationFile>..\..\..\Build\Output\Debug\Libraries\GSF.ServiceProcess.xml</DocumentationFile>
30+ <UseVSHostingProcess>false</UseVSHostingProcess>
31+ <Prefer32Bit>false</Prefer32Bit>
32+ </PropertyGroup>
33+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
34+ <DebugType>pdbonly</DebugType>
35+ <Optimize>true</Optimize>
36+ <OutputPath>..\..\..\Build\Output\Release\Libraries\</OutputPath>
37+ <DefineConstants>TRACE</DefineConstants>
38+ <ErrorReport>prompt</ErrorReport>
39+ <WarningLevel>4</WarningLevel>
40+ <DocumentationFile>..\..\..\Build\Output\Release\Libraries\GSF.ServiceProcess.xml</DocumentationFile>
41+ <UseVSHostingProcess>false</UseVSHostingProcess>
42+ <Prefer32Bit>false</Prefer32Bit>
43+ </PropertyGroup>
44+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Mono|AnyCPU'">
45+ <OutputPath>..\..\..\Build\Output\Mono\Libraries\</OutputPath>
46+ <DefineConstants>TRACE;MONO</DefineConstants>
47+ <DocumentationFile>..\..\..\Build\Output\Mono\Libraries\GSF.ServiceProcess.xml</DocumentationFile>
48+ <Optimize>true</Optimize>
49+ <DebugType>pdbonly</DebugType>
50+ <PlatformTarget>AnyCPU</PlatformTarget>
51+ <CodeAnalysisLogFile>..\..\..\Build\Output\Release\Libraries\TVA.ServiceProcess.dll.CodeAnalysisLog.xml</CodeAnalysisLogFile>
52+ <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
53+ <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
54+ <UseVSHostingProcess>false</UseVSHostingProcess>
55+ <ErrorReport>prompt</ErrorReport>
56+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
57+ <CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>
58+ <CodeAnalysisIgnoreBuiltInRuleSets>false</CodeAnalysisIgnoreBuiltInRuleSets>
59+ <CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
60+ <CodeAnalysisIgnoreBuiltInRules>false</CodeAnalysisIgnoreBuiltInRules>
61+ <Prefer32Bit>false</Prefer32Bit>
62+ </PropertyGroup>
63+ <ItemGroup>
64+ <Reference Include="System" />
65+ <Reference Include="System.Configuration" />
66+ <Reference Include="System.Configuration.Install" />
67+ <Reference Include="System.Core" />
68+ <Reference Include="System.Drawing" />
69+ <Reference Include="System.ServiceProcess" />
70+ <Reference Include="System.Web" />
71+ <Reference Include="System.Xml.Linq" />
72+ <Reference Include="System.Data.DataSetExtensions" />
73+ <Reference Include="Microsoft.CSharp" />
74+ <Reference Include="System.Data" />
75+ <Reference Include="System.Xml" />
76+ </ItemGroup>
77+ <ItemGroup>
78+ <Compile Include="ClientHelper.cs">
79+ <SubType>Component</SubType>
80+ </Compile>
81+ <Compile Include="ClientInfo.cs" />
82+ <Compile Include="ClientRequest.cs" />
83+ <Compile Include="ClientRequestHandler.cs" />
84+ <Compile Include="ClientRequestInfo.cs" />
85+ <Compile Include="NamespaceDoc.cs" />
86+ <Compile Include="Properties\AssemblyInfo.cs" />
87+ <Compile Include="ServiceHelper.cs">
88+ <SubType>Component</SubType>
89+ </Compile>
90+ <Compile Include="ServiceInstallerEx.cs">
91+ <SubType>Component</SubType>
92+ </Compile>
93+ <Compile Include="ServiceProcess.cs" />
94+ <Compile Include="ServiceResponse.cs" />
95+ </ItemGroup>
96+ <ItemGroup>
97+ <EmbeddedResource Include="ClientHelper.bmp" />
98+ </ItemGroup>
99+ <ItemGroup>
100+ <EmbeddedResource Include="ServiceHelper.bmp" />
101+ </ItemGroup>
102+ <ItemGroup>
103+ <ProjectReference Include="..\GSF.Communication\GSF.Communication.csproj">
104+ <Project>{2B2EB9BE-E6A9-406F-BFE6-FF46DD6BD264}</Project>
105+ <Name>GSF.Communication</Name>
106+ </ProjectReference>
107+ <ProjectReference Include="..\GSF.Core\GSF.Core.csproj">
108+ <Project>{FF3FCBA6-F01A-4EC2-BC3F-6BA832AFCF88}</Project>
109+ <Name>GSF.Core</Name>
110+ </ProjectReference>
111+ <ProjectReference Include="..\GSF.Security\GSF.Security.csproj">
112+ <Project>{4060E729-EFFD-4E50-A0D5-99C0990EF7DA}</Project>
113+ <Name>GSF.Security</Name>
114+ </ProjectReference>
115+ </ItemGroup>
116+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
117+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
118+ Other similar extension points exist, see Microsoft.Common.targets.
119+ <Target Name="BeforeBuild">
120+ </Target>
121+ <Target Name="AfterBuild">
122+ </Target>
123+ -->
124+</Project>
Source/Libraries/GSF.ServiceProcess/NamespaceDoc.cs+39−0
@@ -0,0 +1,39 @@
1+//******************************************************************************************************
2+// NamespaceDoc.cs - Gbtc
3+//
4+// Copyright © 2010, Grid Protection Alliance. All Rights Reserved.
5+//
6+// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
7+// the NOTICE file distributed with this work for additional information regarding copyright ownership.
8+// The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
9+// not use this file except in compliance with the License. You may obtain a copy of the License at:
10+//
11+// http://www.opensource.org/licenses/eclipse-1.0.php
12+//
13+// Unless agreed to in writing, the subject software distributed under the License is distributed on an
14+// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
15+// License for the specific language governing permissions and limitations.
16+//
17+// Code Modification History:
18+// ----------------------------------------------------------------------------------------------------
19+// 09/29/2009 - Pinal C. Patel
20+// Generated original version of source code.
21+// 10/8/2012 - Danyelle Gilliam
22+// Modified Header
23+//
24+//******************************************************************************************************
25+
26+
27+
28+using System.Runtime.CompilerServices;
29+
30+namespace GSF.ServiceProcess
31+{
32+ /// <summary>
33+ /// Contains classes used to simplify and standardize development and operation of Windows services that are remotely manageable.
34+ /// </summary>
35+ [CompilerGenerated()]
36+ class NamespaceDoc
37+ {
38+ }
39+}
Source/Libraries/GSF.ServiceProcess/Properties/AssemblyInfo.cs+26−0
@@ -0,0 +1,26 @@
1+using System;
2+using System.Reflection;
3+using System.Runtime.InteropServices;
4+
5+// Assembly identity attributes.
6+[assembly: AssemblyVersion("4.0.4.15")]
7+
8+// Informational attributes.
9+[assembly: AssemblyCompany("TVA")]
10+[assembly: AssemblyCopyright("No copyright is claimed pursuant to 17 USC § 105. All Other Rights Reserved.")]
11+[assembly: AssemblyProduct("TVA Code Library")]
12+
13+// Assembly manifest attributes.
14+#if DEBUG
15+[assembly: AssemblyConfiguration("Debug Build")]
16+#else
17+[assembly: AssemblyConfiguration("Release Build")]
18+#endif
19+[assembly: AssemblyDefaultAlias("TVA.ServiceProcess")]
20+[assembly: AssemblyDescription("Library of components that facilitate remote interaction with a Windows Service from Console, Windows and Web application.")]
21+[assembly: AssemblyTitle("TVA.ServiceProcess")]
22+
23+// Other configuration attributes.
24+[assembly: CLSCompliant(true)]
25+[assembly: ComVisible(false)]
26+[assembly: Guid("eebbee45-2987-4bbd-9cc1-ff6d4dcf55f7")]
Source/Libraries/GSF.ServiceProcess/ServiceHelper.bmp+0−0

Binary file not shown.

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+}
Source/Libraries/GSF.ServiceProcess/ServiceInstallerEx.cs+702−0
@@ -0,0 +1,702 @@
1+//*******************************************************************************************************
2+// ServiceInstallerEx.cs - Gbtc
3+//
4+// Tennessee Valley Authority, 2011
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+// 02/04/2011 - J. Ritchie Carroll
13+// Initial version of source integrated into GSF Code Library.
14+// 09/21/2011 - J. Ritchie Carroll
15+// Excluded class from Mono deployments due to P/Invoke requirements.
16+//
17+//*******************************************************************************************************
18+
19+#region [ TVA Open Source Agreement ]
20+/*
21+
22+ THIS OPEN SOURCE AGREEMENT ("AGREEMENT") DEFINES THE RIGHTS OF USE,REPRODUCTION, DISTRIBUTION,
23+ MODIFICATION AND REDISTRIBUTION OF CERTAIN COMPUTER SOFTWARE ORIGINALLY RELEASED BY THE
24+ TENNESSEE VALLEY AUTHORITY, A CORPORATE AGENCY AND INSTRUMENTALITY OF THE UNITED STATES GOVERNMENT
25+ ("GOVERNMENT AGENCY"). GOVERNMENT AGENCY IS AN INTENDED THIRD-PARTY BENEFICIARY OF ALL SUBSEQUENT
26+ DISTRIBUTIONS OR REDISTRIBUTIONS OF THE SUBJECT SOFTWARE. ANYONE WHO USES, REPRODUCES, DISTRIBUTES,
27+ MODIFIES OR REDISTRIBUTES THE SUBJECT SOFTWARE, AS DEFINED HEREIN, OR ANY PART THEREOF, IS, BY THAT
28+ ACTION, ACCEPTING IN FULL THE RESPONSIBILITIES AND OBLIGATIONS CONTAINED IN THIS AGREEMENT.
29+
30+ Original Software Designation: openPDC
31+ Original Software Title: The GSF Open Source Phasor Data Concentrator
32+ User Registration Requested. Please Visit https://naspi.tva.com/Registration/
33+ Point of Contact for Original Software: J. Ritchie Carroll <mailto:jrcarrol@tva.gov>
34+
35+ 1. DEFINITIONS
36+
37+ A. "Contributor" means Government Agency, as the developer of the Original Software, and any entity
38+ that makes a Modification.
39+
40+ B. "Covered Patents" mean patent claims licensable by a Contributor that are necessarily infringed by
41+ the use or sale of its Modification alone or when combined with the Subject Software.
42+
43+ C. "Display" means the showing of a copy of the Subject Software, either directly or by means of an
44+ image, or any other device.
45+
46+ D. "Distribution" means conveyance or transfer of the Subject Software, regardless of means, to
47+ another.
48+
49+ E. "Larger Work" means computer software that combines Subject Software, or portions thereof, with
50+ software separate from the Subject Software that is not governed by the terms of this Agreement.
51+
52+ F. "Modification" means any alteration of, including addition to or deletion from, the substance or
53+ structure of either the Original Software or Subject Software, and includes derivative works, as that
54+ term is defined in the Copyright Statute, 17 USC � 101. However, the act of including Subject Software
55+ as part of a Larger Work does not in and of itself constitute a Modification.
56+
57+ G. "Original Software" means the computer software first released under this Agreement by Government
58+ Agency entitled openPDC, including source code, object code and accompanying documentation, if any.
59+
60+ H. "Recipient" means anyone who acquires the Subject Software under this Agreement, including all
61+ Contributors.
62+
63+ I. "Redistribution" means Distribution of the Subject Software after a Modification has been made.
64+
65+ J. "Reproduction" means the making of a counterpart, image or copy of the Subject Software.
66+
67+ K. "Sale" means the exchange of the Subject Software for money or equivalent value.
68+
69+ L. "Subject Software" means the Original Software, Modifications, or any respective parts thereof.
70+
71+ M. "Use" means the application or employment of the Subject Software for any purpose.
72+
73+ 2. GRANT OF RIGHTS
74+
75+ A. Under Non-Patent Rights: Subject to the terms and conditions of this Agreement, each Contributor,
76+ with respect to its own contribution to the Subject Software, hereby grants to each Recipient a
77+ non-exclusive, world-wide, royalty-free license to engage in the following activities pertaining to
78+ the Subject Software:
79+
80+ 1. Use
81+
82+ 2. Distribution
83+
84+ 3. Reproduction
85+
86+ 4. Modification
87+
88+ 5. Redistribution
89+
90+ 6. Display
91+
92+ B. Under Patent Rights: Subject to the terms and conditions of this Agreement, each Contributor, with
93+ respect to its own contribution to the Subject Software, hereby grants to each Recipient under Covered
94+ Patents a non-exclusive, world-wide, royalty-free license to engage in the following activities
95+ pertaining to the Subject Software:
96+
97+ 1. Use
98+
99+ 2. Distribution
100+
101+ 3. Reproduction
102+
103+ 4. Sale
104+
105+ 5. Offer for Sale
106+
107+ C. The rights granted under Paragraph B. also apply to the combination of a Contributor's Modification
108+ and the Subject Software if, at the time the Modification is added by the Contributor, the addition of
109+ such Modification causes the combination to be covered by the Covered Patents. It does not apply to
110+ any other combinations that include a Modification.
111+
112+ D. The rights granted in Paragraphs A. and B. allow the Recipient to sublicense those same rights.
113+ Such sublicense must be under the same terms and conditions of this Agreement.
114+
115+ 3. OBLIGATIONS OF RECIPIENT
116+
117+ A. Distribution or Redistribution of the Subject Software must be made under this Agreement except for
118+ additions covered under paragraph 3H.
119+
120+ 1. Whenever a Recipient distributes or redistributes the Subject Software, a copy of this Agreement
121+ must be included with each copy of the Subject Software; and
122+
123+ 2. If Recipient distributes or redistributes the Subject Software in any form other than source code,
124+ Recipient must also make the source code freely available, and must provide with each copy of the
125+ Subject Software information on how to obtain the source code in a reasonable manner on or through a
126+ medium customarily used for software exchange.
127+
128+ B. Each Recipient must ensure that the following copyright notice appears prominently in the Subject
129+ Software:
130+
131+ No copyright is claimed pursuant to 17 USC � 105. All Other Rights Reserved.
132+
133+ C. Each Contributor must characterize its alteration of the Subject Software as a Modification and
134+ must identify itself as the originator of its Modification in a manner that reasonably allows
135+ subsequent Recipients to identify the originator of the Modification. In fulfillment of these
136+ requirements, Contributor must include a file (e.g., a change log file) that describes the alterations
137+ made and the date of the alterations, identifies Contributor as originator of the alterations, and
138+ consents to characterization of the alterations as a Modification, for example, by including a
139+ statement that the Modification is derived, directly or indirectly, from Original Software provided by
140+ Government Agency. Once consent is granted, it may not thereafter be revoked.
141+
142+ D. A Contributor may add its own copyright notice to the Subject Software. Once a copyright notice has
143+ been added to the Subject Software, a Recipient may not remove it without the express permission of
144+ the Contributor who added the notice.
145+
146+ E. A Recipient may not make any representation in the Subject Software or in any promotional,
147+ advertising or other material that may be construed as an endorsement by Government Agency or by any
148+ prior Recipient of any product or service provided by Recipient, or that may seek to obtain commercial
149+ advantage by the fact of Government Agency's or a prior Recipient's participation in this Agreement.
150+
151+ F. In an effort to track usage and maintain accurate records of the Subject Software, each Recipient,
152+ upon receipt of the Subject Software, is requested to register with Government Agency by visiting the
153+ following website: https://naspi.tva.com/Registration/. Recipient's name and personal information
154+ shall be used for statistical purposes only. Once a Recipient makes a Modification available, it is
155+ requested that the Recipient inform Government Agency at the web site provided above how to access the
156+ Modification.
157+
158+ G. Each Contributor represents that that its Modification does not violate any existing agreements,
159+ regulations, statutes or rules, and further that Contributor has sufficient rights to grant the rights
160+ conveyed by this Agreement.
161+
162+ H. A Recipient may choose to offer, and to charge a fee for, warranty, support, indemnity and/or
163+ liability obligations to one or more other Recipients of the Subject Software. A Recipient may do so,
164+ however, only on its own behalf and not on behalf of Government Agency or any other Recipient. Such a
165+ Recipient must make it absolutely clear that any such warranty, support, indemnity and/or liability
166+ obligation is offered by that Recipient alone. Further, such Recipient agrees to indemnify Government
167+ Agency and every other Recipient for any liability incurred by them as a result of warranty, support,
168+ indemnity and/or liability offered by such Recipient.
169+
170+ I. A Recipient may create a Larger Work by combining Subject Software with separate software not
171+ governed by the terms of this agreement and distribute the Larger Work as a single product. In such
172+ case, the Recipient must make sure Subject Software, or portions thereof, included in the Larger Work
173+ is subject to this Agreement.
174+
175+ J. Notwithstanding any provisions contained herein, Recipient is hereby put on notice that export of
176+ any goods or technical data from the United States may require some form of export license from the
177+ U.S. Government. Failure to obtain necessary export licenses may result in criminal liability under
178+ U.S. laws. Government Agency neither represents that a license shall not be required nor that, if
179+ required, it shall be issued. Nothing granted herein provides any such export license.
180+
181+ 4. DISCLAIMER OF WARRANTIES AND LIABILITIES; WAIVER AND INDEMNIFICATION
182+
183+ A. No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, EITHER
184+ EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT THE SUBJECT
185+ SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
186+ PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL BE ERROR
187+ FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. THIS
188+ AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR
189+ RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS
190+ RESULTING FROM USE OF THE SUBJECT SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND
191+ LIABILITIES REGARDING THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT
192+ "AS IS."
193+
194+ B. Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST GOVERNMENT AGENCY, ITS
195+ AGENTS, EMPLOYEES, CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF RECIPIENT'S USE
196+ OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES OR LOSSES ARISING FROM
197+ SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING FROM, RECIPIENT'S USE OF THE
198+ SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS GOVERNMENT AGENCY, ITS AGENTS,
199+ EMPLOYEES, CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, TO THE EXTENT PERMITTED BY
200+ LAW. THE FOREGOING RELEASE AND INDEMNIFICATION SHALL APPLY EVEN IF THE LIABILITIES, DEMANDS, DAMAGES,
201+ EXPENSES OR LOSSES ARE CAUSED, OCCASIONED, OR CONTRIBUTED TO BY THE NEGLIGENCE, SOLE OR CONCURRENT, OF
202+ GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE
203+ IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT.
204+
205+ 5. GENERAL TERMS
206+
207+ A. Termination: This Agreement and the rights granted hereunder will terminate automatically if a
208+ Recipient fails to comply with these terms and conditions, and fails to cure such noncompliance within
209+ thirty (30) days of becoming aware of such noncompliance. Upon termination, a Recipient agrees to
210+ immediately cease use and distribution of the Subject Software. All sublicenses to the Subject
211+ Software properly granted by the breaching Recipient shall survive any such termination of this
212+ Agreement.
213+
214+ B. Severability: If any provision of this Agreement is invalid or unenforceable under applicable law,
215+ it shall not affect the validity or enforceability of the remainder of the terms of this Agreement.
216+
217+ C. Applicable Law: This Agreement shall be subject to United States federal law only for all purposes,
218+ including, but not limited to, determining the validity of this Agreement, the meaning of its
219+ provisions and the rights, obligations and remedies of the parties.
220+
221+ D. Entire Understanding: This Agreement constitutes the entire understanding and agreement of the
222+ parties relating to release of the Subject Software and may not be superseded, modified or amended
223+ except by further written agreement duly executed by the parties.
224+
225+ E. Binding Authority: By accepting and using the Subject Software under this Agreement, a Recipient
226+ affirms its authority to bind the Recipient to all terms and conditions of this Agreement and that
227+ Recipient hereby agrees to all terms and conditions herein.
228+
229+ F. Point of Contact: Any Recipient contact with Government Agency is to be directed to the designated
230+ representative as follows: J. Ritchie Carroll <mailto:jrcarrol@tva.gov>.
231+
232+*/
233+#endregion
234+
235+#region [ Contributor License Agreements ]
236+
237+//******************************************************************************************************
238+//
239+// Copyright � 2011, Grid Protection Alliance. All Rights Reserved.
240+//
241+// The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
242+// not use this file except in compliance with the License. You may obtain a copy of the License at:
243+//
244+// http://www.opensource.org/licenses/eclipse-1.0.php
245+//
246+// Unless agreed to in writing, the subject software distributed under the License is distributed on an
247+// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
248+// License for the specific language governing permissions and limitations.
249+//
250+//******************************************************************************************************
251+
252+/////////////////////////////////////////////////////////////////////////////
253+//
254+// Original Name : Verifide.ServiceUtils.dll
255+// Description : Extension to System.ServiceProcess.ServiceInstallerEx
256+// to enable configuration of advanced options
257+// Date : 1/14/04
258+//
259+// Copyright (C) 2004 Narendra (Neil) Baliga
260+//
261+// This library is free software; you can redistribute it and/or
262+// modify it as you wish. It is distributed in the hope that it will
263+// be useful,but WITHOUT ANY WARRANTY; without even the implied
264+// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
265+//
266+/////////////////////////////////////////////////////////////////////////////
267+
268+#endregion
269+
270+using GSF.Interop;
271+using System;
272+using System.Collections.Generic;
273+using System.Configuration.Install;
274+using System.Diagnostics;
275+using System.Runtime.InteropServices;
276+using System.ServiceProcess;
277+using System.Threading;
278+
279+namespace GSF.ServiceProcess
280+{
281+#if !MONO
282+ #region [ Enumerations ]
283+
284+ /// <summary>
285+ /// Defines the recover action to be performed upon service failure.
286+ /// </summary>
287+ /// <remarks>
288+ /// Enum values correspond to Win32 equivalents.
289+ /// </remarks>
290+ public enum RecoverAction
291+ {
292+ /// <summary>
293+ /// No action.
294+ /// </summary>
295+ None = 0,
296+ /// <summary>
297+ /// Restart the service.
298+ /// </summary>
299+ Restart = 1,
300+ /// <summary>
301+ /// Reboot the computer.
302+ /// </summary>
303+ Reboot = 2,
304+ /// <summary>
305+ /// Run a command.
306+ /// </summary>
307+ RunCommand = 3
308+ }
309+
310+ #endregion
311+
312+ /// <summary>
313+ /// Defines an extended service installer class that can define service failure actions at install time.
314+ /// </summary>
315+ public class ServiceInstallerEx : ServiceInstaller
316+ {
317+ #region [ Members ]
318+
319+ // Constants
320+ private const int SC_MANAGER_ALL_ACCESS = 0xF003F;
321+ private const int SERVICE_ALL_ACCESS = 0xF01FF;
322+ private const int SERVICE_CONFIG_FAILURE_ACTIONS = 0x2;
323+ private const int SERVICE_CONFIG_FAILURE_ACTIONS_FLAG = 0x4;
324+
325+ // Fields
326+ private List<WindowsApi.SC_ACTION> m_failureActions; // Service failure actions
327+ private int m_failResetPeriod = Timeout.Infinite; // Service fail count reset time
328+ private string m_failRebootMessage = ""; // Service reboot message
329+ private string m_failRunCommand = ""; // Service fail run command
330+ private bool m_executeActionsOnNonCrashErrors = true; // Windows 2008 check box to restart on exit with error
331+ private bool m_startOnInstall = false;
332+ private int m_startTimeout = 15000;
333+ private string m_logMessagePrefix;
334+
335+ #endregion
336+
337+ #region [ Constructors ]
338+
339+ /// <summary>
340+ /// Creates a new <see cref="ServiceInstallerEx"/>.
341+ /// </summary>
342+ public ServiceInstallerEx() : base()
343+ {
344+ // Initialize the failure actions and register for the Committed event
345+ m_failureActions = new List<WindowsApi.SC_ACTION>();
346+
347+ // Register the event handlers for post install operations
348+ base.Committed += new InstallEventHandler(UpdateServiceConfig);
349+ base.Committed += new InstallEventHandler(StartIfNeeded);
350+
351+ // Set the log message prefix
352+ m_logMessagePrefix = base.ServiceName + " - ServiceInstallerEx: ";
353+ }
354+
355+ #endregion
356+
357+ #region [ Properties ]
358+
359+ /// <summary>
360+ /// Sets the time after which to reset the failure count to zero if there are no failures, in seconds.
361+ /// Specify <see cref="Timeout.Infinite"/> to indicate that this value should never be reset.
362+ /// </summary>
363+ public int FailResetPeriod
364+ {
365+ set
366+ {
367+ if (value < 0)
368+ m_failResetPeriod = Timeout.Infinite;
369+ else
370+ m_failResetPeriod = value;
371+ }
372+ }
373+
374+ /// <summary>
375+ /// Sets the service fail reboot message.
376+ /// </summary>
377+ public string FailRebootMessage
378+ {
379+ set
380+ {
381+ m_failRebootMessage = value;
382+ }
383+ }
384+
385+ /// <summary>
386+ /// Sets the service fail run command.
387+ /// </summary>
388+ public string FailRunCommand
389+ {
390+ set
391+ {
392+ m_failRunCommand = value;
393+ }
394+ }
395+
396+ /// <summary>
397+ /// Sets flag that determines when failure actions are to be executed.
398+ /// </summary>
399+ /// <remarks>
400+ /// <para>
401+ /// If this member is <c>true</c> and the service has configured failure actions, the failure actions are queued if the service process
402+ /// terminates without reporting a status of SERVICE_STOPPED or if it enters the SERVICE_STOPPED state but the exit code returned in the
403+ /// SERVICE_STATUS structure is not ERROR_SUCCESS (0).
404+ /// </para>
405+ /// <para>
406+ /// If this member is <c>false</c> and the service has configured failure actions, the failure actions are queued only if the service
407+ /// terminates without reporting a status of SERVICE_STOPPED.
408+ /// </para>
409+ /// <para>
410+ /// This setting is ignored unless the service has configured failure actions.
411+ /// </para>
412+ /// </remarks>
413+ public bool ExecuteActionsOnNonCrashErrors
414+ {
415+ set
416+ {
417+ m_executeActionsOnNonCrashErrors = value;
418+ }
419+
420+ }
421+
422+ /// <summary>
423+ /// Sets the boolean value to configure the service to start after it is installed.
424+ /// </summary>
425+ public bool StartOnInstall
426+ {
427+ set
428+ {
429+ m_startOnInstall = value;
430+ }
431+ }
432+
433+ /// <summary>
434+ /// Sets the service start timeout in milliseconds.
435+ /// </summary>
436+ public int StartTimeout
437+ {
438+ set
439+ {
440+ m_startTimeout = value;
441+ }
442+ }
443+
444+ #endregion
445+
446+ #region [ Methods ]
447+
448+ /// <summary>
449+ /// Defines a new recover action to be performed upon service failure.
450+ /// </summary>
451+ /// <param name="recoverAction"><see cref="RecoverAction"/> to execute upon service failure.</param>
452+ /// <param name="delay">The time to wait before performing the specified <paramref name="recoverAction"/>, in milliseconds.</param>
453+ public void DefineRecoverAction(RecoverAction recoverAction, int delay)
454+ {
455+ m_failureActions.Add(new WindowsApi.SC_ACTION() { Type = (WindowsApi.SC_ACTION_TYPE)(uint)recoverAction, Delay = delay });
456+ }
457+
458+ // The worker method to set all the extension properties for the service
459+ private void UpdateServiceConfig(object sender, InstallEventArgs e)
460+ {
461+ int actionCount = m_failureActions.Count;
462+
463+ // Do we need to do any work that the base installer did not do already?
464+ if (actionCount == 0)
465+ return;
466+
467+ // We've got work to do
468+ IntPtr serviceManagerHandle = IntPtr.Zero;
469+ IntPtr serviceHandle = IntPtr.Zero;
470+ IntPtr serviceLockHandle = IntPtr.Zero;
471+ IntPtr actionsPtr = IntPtr.Zero;
472+ IntPtr failureActionsPtr = IntPtr.Zero;
473+
474+ // Err check var
475+ bool result = false;
476+
477+ // Place all our code in a try block
478+ try
479+ {
480+ // Open the service control manager
481+ serviceManagerHandle = WindowsApi.OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
482+
483+ if (serviceManagerHandle.ToInt32() <= 0)
484+ {
485+ LogInstallMessage(EventLogEntryType.Error, m_logMessagePrefix + "Failed to Open Service Control Manager");
486+ return;
487+ }
488+
489+ // Lock the Service Database
490+ serviceLockHandle = WindowsApi.LockServiceDatabase(serviceManagerHandle);
491+
492+ if (serviceLockHandle.ToInt32() <= 0)
493+ {
494+ LogInstallMessage(EventLogEntryType.Error, m_logMessagePrefix + "Failed to Lock Service Database for Write");
495+ return;
496+ }
497+
498+ // Open the service
499+ serviceHandle = WindowsApi.OpenService(serviceManagerHandle, base.ServiceName, SERVICE_ALL_ACCESS);
500+
501+ if (serviceHandle.ToInt32() <= 0)
502+ {
503+ LogInstallMessage(EventLogEntryType.Information, m_logMessagePrefix + "Failed to Open Service ");
504+ return;
505+ }
506+
507+ // Need to set service failure actions. Note that the API lets us set as many as
508+ // we want, yet the Service Control Manager GUI only lets us see the first 3.
509+ // Bill is aware of this and has promised no fixes. Also note that the API allows
510+ // granularity of seconds whereas GUI only shows days and minutes.
511+
512+ // Calculate size of SC_ACTION structure
513+ int scActionSize = Marshal.SizeOf(typeof(WindowsApi.SC_ACTION));
514+ bool needShutdownPrivilege = false;
515+
516+ // Allocate memory for the array of individual actions
517+ actionsPtr = Marshal.AllocHGlobal(scActionSize * actionCount);
518+
519+ // Set up the restart actions array by copying in each failure action structure
520+ for (int i = 0; i < actionCount; i++)
521+ {
522+ // Handle pointer math as 64-bit, cast will convert back to 32-bit if needed
523+ Marshal.StructureToPtr(m_failureActions[i], (IntPtr)((Int64)actionsPtr + i * scActionSize), false);
524+
525+ if (m_failureActions[i].Type == WindowsApi.SC_ACTION_TYPE.SC_ACTION_REBOOT)
526+ needShutdownPrivilege = true;
527+ }
528+
529+
530+ // If we need shutdown privilege, then grant it to this process
531+ if (needShutdownPrivilege)
532+ {
533+ result = GrantShutdownPrivilege();
534+
535+ if (!result)
536+ return;
537+ }
538+
539+ // Set up the failure actions
540+ WindowsApi.SERVICE_FAILURE_ACTIONS failureActions = new WindowsApi.SERVICE_FAILURE_ACTIONS();
541+ failureActions.cActions = actionCount;
542+ failureActions.dwResetPeriod = m_failResetPeriod;
543+ failureActions.lpCommand = m_failRunCommand;
544+ failureActions.lpRebootMsg = m_failRebootMessage;
545+ failureActions.lpsaActions = actionsPtr;
546+
547+ failureActionsPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WindowsApi.SERVICE_FAILURE_ACTIONS)));
548+ Marshal.StructureToPtr(failureActions, failureActionsPtr, false);
549+
550+ // Make the change
551+ result = WindowsApi.ChangeServiceConfig2(serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, failureActionsPtr);
552+
553+ // Check the return
554+ if (!result)
555+ {
556+ int err = WindowsApi.GetLastError();
557+
558+ if (err == WindowsApi.ERROR_ACCESS_DENIED)
559+ throw new Exception(m_logMessagePrefix + "Access Denied while setting service failure actions");
560+ }
561+
562+ LogInstallMessage(EventLogEntryType.Information, m_logMessagePrefix + "Successfully configured service failure actions");
563+
564+ // Failure actions flag only applies on Vista / 2008 or better
565+ if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Major >= 6)
566+ {
567+ WindowsApi.SERVICE_FAILURE_ACTIONS_FLAG failureActionFlag = new WindowsApi.SERVICE_FAILURE_ACTIONS_FLAG();
568+ failureActionFlag.bFailureAction = m_executeActionsOnNonCrashErrors;
569+ result = WindowsApi.ChangeServiceConfig2(serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, ref failureActionFlag);
570+
571+ // Error setting description?
572+ if (!result)
573+ throw new Exception(m_logMessagePrefix + "Failed to set failure actions flag");
574+ }
575+ }
576+ // Catch all exceptions
577+ catch (Exception ex)
578+ {
579+ LogInstallMessage(EventLogEntryType.Error, ex.Message);
580+ }
581+ finally
582+ {
583+ if (serviceManagerHandle != IntPtr.Zero)
584+ {
585+ // Unlock the service database
586+ if (serviceLockHandle != IntPtr.Zero)
587+ WindowsApi.UnlockServiceDatabase(serviceLockHandle);
588+
589+ // Close the service control manager handle
590+ WindowsApi.CloseServiceHandle(serviceManagerHandle);
591+ }
592+
593+ // Close the service handle
594+ if (serviceHandle != IntPtr.Zero)
595+ WindowsApi.CloseServiceHandle(serviceHandle);
596+
597+ // Free allocated memory
598+ if (failureActionsPtr != IntPtr.Zero)
599+ Marshal.FreeHGlobal(failureActionsPtr);
600+
601+ if (actionsPtr != IntPtr.Zero)
602+ Marshal.FreeHGlobal(actionsPtr);
603+ }
604+ }
605+
606+ // This code mimics the MSDN defined way to adjust privilege for shutdown
607+ // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/shutting_down.asp
608+ private bool GrantShutdownPrivilege()
609+ {
610+ bool grantSuccess = false;
611+ IntPtr processToken = IntPtr.Zero;
612+ IntPtr processHandle = IntPtr.Zero;
613+ WindowsApi.TOKEN_PRIVILEGES tokenPrivileges = new WindowsApi.TOKEN_PRIVILEGES();
614+ long luid = 0;
615+ int returnLen = 0;
616+
617+ try
618+ {
619+ processHandle = WindowsApi.GetCurrentProcess();
620+
621+ bool result = WindowsApi.OpenProcessToken(processHandle, WindowsApi.TOKEN_ADJUST_PRIVILEGES | WindowsApi.TOKEN_QUERY, ref processToken);
622+
623+ if (!result)
624+ return grantSuccess;
625+
626+ WindowsApi.LookupPrivilegeValue(null, WindowsApi.SE_SHUTDOWN_NAME, ref luid);
627+
628+ tokenPrivileges.PrivilegeCount = 1;
629+ tokenPrivileges.Privileges.Luid = luid;
630+ tokenPrivileges.Privileges.Attributes = WindowsApi.SE_PRIVILEGE_ENABLED;
631+
632+ result = WindowsApi.AdjustTokenPrivileges(processToken, false, ref tokenPrivileges, 0, IntPtr.Zero, ref returnLen);
633+
634+ if (WindowsApi.GetLastError() != 0)
635+ throw new Exception("Failed to grant shutdown privilege");
636+
637+ grantSuccess = true;
638+
639+ }
640+ catch (Exception ex)
641+ {
642+ LogInstallMessage(EventLogEntryType.Error, m_logMessagePrefix + ex.Message);
643+ }
644+ finally
645+ {
646+ if (processToken != IntPtr.Zero)
647+ WindowsApi.CloseHandle(processToken);
648+ }
649+
650+ return grantSuccess;
651+ }
652+
653+ // Method to start the service automatically after installation
654+ private void StartIfNeeded(object sender, InstallEventArgs e)
655+ {
656+ // Do we need to do any work?
657+ if (!m_startOnInstall)
658+ return;
659+
660+ try
661+ {
662+ TimeSpan waitTo = new TimeSpan(0, 0, m_startTimeout);
663+
664+ // Get a handle to the service
665+ ServiceController sc = new ServiceController(base.ServiceName);
666+
667+ // Start the service and wait for it to start
668+ sc.Start();
669+ sc.WaitForStatus(ServiceControllerStatus.Running, waitTo);
670+
671+ // Be good and release our handle
672+ sc.Close();
673+
674+ LogInstallMessage(EventLogEntryType.Information, m_logMessagePrefix + " Service Started");
675+
676+ }
677+ // Catch all exceptions
678+ catch (Exception ex)
679+ {
680+ LogInstallMessage(EventLogEntryType.Error, m_logMessagePrefix + ex.Message);
681+ }
682+ }
683+
684+ // Method to log to console and event log
685+ private void LogInstallMessage(EventLogEntryType logLevel, string msg)
686+ {
687+ System.Console.WriteLine(msg);
688+
689+ try
690+ {
691+ EventLog.WriteEntry(base.ServiceName, msg, logLevel);
692+ }
693+ catch (Exception ex)
694+ {
695+ System.Console.WriteLine(ex.ToString());
696+ }
697+ }
698+
699+ #endregion
700+ }
701+#endif
702+}
Source/Libraries/GSF.ServiceProcess/ServiceProcess.cs+400−0
@@ -0,0 +1,400 @@
1+//******************************************************************************************************
2+// ServiceProcess.cs - Gbtc
3+//
4+// Copyright � 2010, Grid Protection Alliance. All Rights Reserved.
5+//
6+// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
7+// the NOTICE file distributed with this work for additional information regarding copyright ownership.
8+// The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
9+// not use this file except in compliance with the License. You may obtain a copy of the License at:
10+//
11+// http://www.opensource.org/licenses/eclipse-1.0.php
12+//
13+// Unless agreed to in writing, the subject software distributed under the License is distributed on an
14+// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
15+// License for the specific language governing permissions and limitations.
16+//
17+// Code Modification History:
18+// ----------------------------------------------------------------------------------------------------
19+// 09/13/2006 - Pinal C. Patel
20+// Generated original version of source code.
21+// 09/30/2008 - J. Ritchie Carroll
22+// Converted to C#.
23+// 03/03/2009 - Pinal C. Patel
24+// Edited code comments.
25+// 09/14/2009 - Stephen C. Wills
26+// Added new header and license agreement.
27+// 10/8/2012 - Danyelle Gilliam
28+// Modified Header
29+//
30+//******************************************************************************************************
31+
32+
33+
34+using GSF.Units;
35+using System;
36+using System.Text;
37+using System.Threading;
38+
39+namespace GSF.ServiceProcess
40+{
41+ #region [ Enumerations ]
42+
43+ /// <summary>
44+ /// Indicates the current state of <see cref="ServiceProcess"/>.
45+ /// </summary>
46+ public enum ServiceProcessState
47+ {
48+ /// <summary>
49+ /// <see cref="ServiceProcess"/> has not been started.
50+ /// </summary>
51+ Unprocessed,
52+ /// <summary>
53+ /// <see cref="ServiceProcess"/> is currently executing.
54+ /// </summary>
55+ Processing,
56+ /// <summary>
57+ /// <see cref="ServiceProcess"/> has completed processing.
58+ /// </summary>
59+ Processed,
60+ /// <summary>
61+ /// <see cref="ServiceProcess"/> was aborted.
62+ /// </summary>
63+ Aborted,
64+ /// <summary>
65+ /// <see cref="ServiceProcess"/> stopped due to exception.
66+ /// </summary>
67+ Exception
68+ }
69+
70+ #endregion
71+
72+ /// <summary>
73+ /// Represents a process that executes asynchronously inside a <see cref="ServiceHelper"/>.
74+ /// </summary>
75+ /// <seealso cref="ServiceHelper"/>
76+ public class ServiceProcess : IDisposable, IProvideStatus
77+ {
78+ #region [ Members ]
79+
80+ //Events
81+
82+ /// <summary>
83+ /// Occurs when the <see cref="CurrentState"/> of the <see cref="ServiceProcess"/> changes.
84+ /// </summary>
85+ public event EventHandler StateChanged;
86+
87+ // Fields
88+#if ThreadTracking
89+ private ManagedThread m_processThread;
90+#else
91+ private Thread m_processThread;
92+#endif
93+ private string m_name;
94+ private object[] m_arguments;
95+ private Action<string, object[]> m_executionMethod;
96+ private ServiceProcessState m_currentState;
97+ private DateTime m_executionStartTime;
98+ private DateTime m_executionStopTime;
99+ private bool m_disposed;
100+
101+ #endregion
102+
103+ #region [ Constructors ]
104+
105+ /// <summary>
106+ /// Initializes a new instance of the <see cref="ServiceProcess"/> class.
107+ /// </summary>
108+ /// <param name="executionMethod"><see cref="Delegate"/> that gets invoked when <see cref="Start()"/> is called.</param>
109+ /// <param name="name">Name of the <see cref="ServiceProcess"/>.</param>
110+ /// <param name="arguments">Arguments to be passed in to the <paramref name="executionMethod"/>.</param>
111+ public ServiceProcess(Action<string, object[]> executionMethod, string name, params object[] arguments)
112+ {
113+ m_name = name;
114+ m_arguments = arguments;
115+ m_executionMethod = executionMethod;
116+ m_currentState = ServiceProcessState.Unprocessed;
117+ }
118+
119+ /// <summary>
120+ /// Releases the unmanaged resources before the <see cref="ServiceProcess" /> object is reclaimed by <see cref="GC"/>.
121+ /// </summary>
122+ ~ServiceProcess()
123+ {
124+ Dispose(false);
125+ }
126+
127+ #endregion
128+
129+ #region [ Properties ]
130+
131+ /// <summary>
132+ /// Gets or sets the name of the <see cref="ServiceProcess"/>.
133+ /// </summary>
134+ /// <exception cref="ArgumentNullException">The value being assigned is a null or empty string.</exception>
135+ public string Name
136+ {
137+ get
138+ {
139+ return m_name;
140+ }
141+ set
142+ {
143+ if (string.IsNullOrEmpty(value))
144+ throw new ArgumentNullException("Name");
145+
146+ m_name = value;
147+ }
148+ }
149+
150+ /// <summary>
151+ /// Gets or sets the arguments to be passed in to the <see cref="ExecutionMethod"/>.
152+ /// </summary>
153+ public object[] Arguments
154+ {
155+ get
156+ {
157+ return m_arguments;
158+ }
159+ set
160+ {
161+ m_arguments = value;
162+ }
163+ }
164+
165+ /// <summary>
166+ /// Gets or sets the <see cref="Delegate"/> that gets invoked when <see cref="Start()"/> is called.
167+ /// </summary>
168+ /// <remarks>
169+ /// Argument1 gets the <see cref="Name"/> of the <see cref="ServiceProcess"/>.<br/>
170+ /// Argument2 gets the <see cref="Arguments"/> of the <see cref="ServiceProcess"/>.
171+ /// </remarks>
172+ public Action<string, object[]> ExecutionMethod
173+ {
174+ get
175+ {
176+ return m_executionMethod;
177+ }
178+ set
179+ {
180+ m_executionMethod = value;
181+ }
182+ }
183+
184+ /// <summary>
185+ /// Gets the current <see cref="ServiceProcessState"/>.
186+ /// </summary>
187+ public ServiceProcessState CurrentState
188+ {
189+ get
190+ {
191+ return m_currentState;
192+ }
193+ private set
194+ {
195+ m_currentState = value;
196+ OnStateChanged(); // Notify of the change in state.
197+ }
198+ }
199+
200+ /// <summary>
201+ /// Gets the <see cref="DateTime"/> when execution of <see cref="ServiceProcess"/> last started.
202+ /// </summary>
203+ public DateTime ExecutionStartTime
204+ {
205+ get
206+ {
207+ return m_executionStartTime;
208+ }
209+ }
210+
211+ /// <summary>
212+ /// Gets the <see cref="DateTime"/> when execution of <see cref="ServiceProcess"/> last completed.
213+ /// </summary>
214+ public DateTime ExecutionStopTime
215+ {
216+ get
217+ {
218+ return m_executionStopTime;
219+ }
220+ }
221+
222+ /// <summary>
223+ /// Gets the <see cref="Time"/> taken by the <see cref="ServiceProcess"/> during the last execution.
224+ /// </summary>
225+ public Time LastExecutionTime
226+ {
227+ get
228+ {
229+ return Ticks.ToSeconds(m_executionStopTime.Ticks - m_executionStartTime.Ticks);
230+ }
231+ }
232+
233+ /// <summary>
234+ /// Gets the descriptive status of the <see cref="ServiceProcess"/>.
235+ /// </summary>
236+ public string Status
237+ {
238+ get
239+ {
240+ StringBuilder status = new StringBuilder();
241+
242+ status.Append(" Process Name: ");
243+ status.Append(m_name);
244+ status.AppendLine();
245+ status.Append(" Current State: ");
246+ status.Append(m_currentState.ToString());
247+ status.AppendLine();
248+ status.Append(" Execution Start Time: ");
249+
250+ if (m_executionStartTime != DateTime.MinValue)
251+ {
252+ status.Append(m_executionStartTime.ToString());
253+ }
254+ else
255+ {
256+ status.Append("N/A");
257+ }
258+
259+ status.AppendLine();
260+ status.Append(" Execution Stop Time: ");
261+
262+ if (m_executionStopTime != DateTime.MinValue)
263+ {
264+ status.Append(m_executionStopTime.ToString());
265+ }
266+ else
267+ {
268+ status.Append("N/A");
269+ }
270+
271+ status.AppendLine();
272+ status.Append(" Last Execution Time: ");
273+ status.Append(LastExecutionTime.ToString());
274+ status.AppendLine();
275+
276+ return status.ToString();
277+ }
278+ }
279+
280+ #endregion
281+
282+ #region [ Methods ]
283+
284+ /// <summary>
285+ /// Starts the execution of <see cref="ServiceProcess"/>.
286+ /// </summary>
287+ public void Start()
288+ {
289+ Start(m_arguments);
290+ }
291+
292+ /// <summary>
293+ /// Starts the execution of <see cref="ServiceProcess"/>.
294+ /// </summary>
295+ /// <param name="arguments">Arguments to be passed in to the <see cref="ExecutionMethod"/>.</param>
296+ public void Start(object[] arguments)
297+ {
298+ // Start the execution on a seperate thread.
299+#if ThreadTracking
300+ m_processThread = new ManagedThread(InvokeExecutionMethod);
301+ m_processThread.Name = "TVA.ServiceProcess.ServiceProcess.InvokeExecutionMethod() [" + m_name + "]";
302+#else
303+ m_processThread = new Thread(InvokeExecutionMethod);
304+#endif
305+ m_processThread.Start(arguments);
306+ }
307+
308+ /// <summary>
309+ /// Stops the execution of <see cref="ServiceProcess"/> if it executing.
310+ /// </summary>
311+ public void Abort()
312+ {
313+ // We'll abort the process only if it is currently executing.
314+ if (m_processThread != null)
315+ {
316+ if (m_processThread.IsAlive)
317+ m_processThread.Abort();
318+ }
319+ m_processThread = null;
320+ }
321+
322+ /// <summary>
323+ /// Releases all the resources used by the <see cref="ServiceProcess"/> object.
324+ /// </summary>
325+ public void Dispose()
326+ {
327+ Dispose(true);
328+ GC.SuppressFinalize(this);
329+ }
330+
331+ /// <summary>
332+ /// Releases the unmanaged resources used by the <see cref="ServiceProcess"/> and optionally releases the managed resources.
333+ /// </summary>
334+ /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
335+ protected virtual void Dispose(bool disposing)
336+ {
337+ if (!m_disposed)
338+ {
339+ try
340+ {
341+ // This will be done regardless of whether the object is finalized or disposed.
342+ Abort();
343+ if (disposing)
344+ {
345+ // This will be done only when the object is disposed by calling Dispose().
346+ m_executionMethod = null;
347+ }
348+ }
349+ finally
350+ {
351+ m_disposed = true; // Prevent duplicate dispose.
352+ }
353+ }
354+ }
355+
356+ /// <summary>
357+ /// Raises the <see cref="StateChanged"/> event.
358+ /// </summary>
359+ protected virtual void OnStateChanged()
360+ {
361+ if (StateChanged != null)
362+ StateChanged(this, EventArgs.Empty);
363+ }
364+
365+ private void InvokeExecutionMethod(object state)
366+ {
367+ if (m_executionMethod != null)
368+ {
369+ CurrentState = ServiceProcessState.Processing;
370+ m_executionStartTime = DateTime.Now;
371+ m_executionStopTime = DateTime.MinValue;
372+
373+ try
374+ {
375+ // We'll keep the invocation of the delegate in Try...Catch to absorb any exceptions that
376+ // were not handled by the consumer.
377+ m_executionMethod(m_name, state as object[]);
378+
379+ CurrentState = ServiceProcessState.Processed;
380+ }
381+ catch (ThreadAbortException)
382+ {
383+ CurrentState = ServiceProcessState.Aborted;
384+ }
385+ catch (Exception)
386+ {
387+ // We'll absorb any exceptions if unhandled by the client.
388+ CurrentState = ServiceProcessState.Exception;
389+ }
390+ finally
391+ {
392+ m_executionStopTime = DateTime.Now;
393+ }
394+ }
395+ m_processThread = null;
396+ }
397+
398+ #endregion
399+ }
400+}
Source/Libraries/GSF.ServiceProcess/ServiceResponse.cs+139−0
@@ -0,0 +1,139 @@
1+//******************************************************************************************************
2+// ServiceResponse.cs - Gbtc
3+//
4+// Copyright © 2010, Grid Protection Alliance. All Rights Reserved.
5+//
6+// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
7+// the NOTICE file distributed with this work for additional information regarding copyright ownership.
8+// The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
9+// not use this file except in compliance with the License. You may obtain a copy of the License at:
10+//
11+// http://www.opensource.org/licenses/eclipse-1.0.php
12+//
13+// Unless agreed to in writing, the subject software distributed under the License is distributed on an
14+// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
15+// License for the specific language governing permissions and limitations.
16+//
17+// Code Modification History:
18+// ----------------------------------------------------------------------------------------------------
19+// 08/29/2006 - Pinal C. Patel
20+// Generated original version of source code.
21+// 09/30/2008 - J. Ritchie Carroll
22+// Converted to C#.
23+// 03/09/2009 - Pinal C. Patel
24+// Edited code comments.
25+// 09/14/2009 - Stephen C. Wills
26+// Added new header and license agreement.
27+// 10/8/2012 - Danyelle Gilliam
28+// Modified Header
29+//
30+//******************************************************************************************************
31+
32+
33+
34+using System;
35+using System.Collections.Generic;
36+
37+namespace GSF.ServiceProcess
38+{
39+ /// <summary>
40+ /// Represents a response sent by the <see cref="ServiceHelper"/> to a <see cref="ClientRequest"/> from the <see cref="ClientHelper"/>.
41+ /// </summary>
42+ /// <seealso cref="ServiceHelper"/>
43+ /// <seealso cref="ClientHelper"/>
44+ /// <seealso cref="ClientRequest"/>
45+ [Serializable()]
46+ public class ServiceResponse
47+ {
48+ #region [ Members ]
49+
50+ // Fields
51+ private string m_type;
52+ private string m_message;
53+ private List<object> m_attachments;
54+
55+ #endregion
56+
57+ #region [ Constructors ]
58+
59+ /// <summary>
60+ /// Initializes a new instance of the <see cref="ServiceResponse"/> class.
61+ /// </summary>
62+ public ServiceResponse()
63+ : this("UNDETERMINED")
64+ {
65+ }
66+
67+ /// <summary>
68+ /// Initializes a new instance of the <see cref="ServiceResponse"/> class.
69+ /// </summary>
70+ /// <param name="type">Type of the <see cref="ServiceResponse"/> in plain text.</param>
71+ public ServiceResponse(string type)
72+ : this(type, "")
73+ {
74+ }
75+
76+ /// <summary>
77+ /// Initializes a new instance of the <see cref="ServiceResponse"/> class.
78+ /// </summary>
79+ /// <param name="type">Type of the <see cref="ServiceResponse"/> in plain-text.</param>
80+ /// <param name="message">Message associated with the <see cref="ServiceResponse"/>.</param>
81+ public ServiceResponse(string type, string message)
82+ {
83+ m_type = type.ToUpper();
84+ m_message = message;
85+ m_attachments = new List<object>();
86+ }
87+
88+ #endregion
89+
90+ #region [ Properties ]
91+
92+ /// <summary>
93+ /// Gets or sets the plain-text type of the <see cref="ServiceResponse"/>.
94+ /// </summary>
95+ /// <exception cref="ArgumentNullException">The value being assigned is either a null or empty string.</exception>
96+ public string Type
97+ {
98+ get
99+ {
100+ return m_type;
101+ }
102+ set
103+ {
104+ if (string.IsNullOrEmpty(value))
105+ throw new ArgumentNullException("value");
106+
107+ m_type = value.ToUpper();
108+ }
109+ }
110+
111+ /// <summary>
112+ /// Gets or sets the palin-text message associated with the <see cref="ServiceResponse"/>.
113+ /// </summary>
114+ public string Message
115+ {
116+ get
117+ {
118+ return m_message;
119+ }
120+ set
121+ {
122+ m_message = value;
123+ }
124+ }
125+
126+ /// <summary>
127+ /// Gets a list of serializable attachments of the <see cref="ServiceResponse"/>.
128+ /// </summary>
129+ public List<object> Attachments
130+ {
131+ get
132+ {
133+ return m_attachments;
134+ }
135+ }
136+
137+ #endregion
138+ }
139+}
Source/Libraries/GSF.ServiceProcess/TVA.ServiceProcess.nuspec+35−0
@@ -0,0 +1,35 @@
1+<?xml version="1.0" encoding="utf-8"?>
2+
3+<!--
4+*******************************************************************************************************
5+** TVA.ServiceProcess.nuspec - Gbtc
6+**
7+** Tennessee Valley Authority, 2009
8+** No copyright is claimed pursuant to 17 USC § 105. All Other Rights Reserved.
9+**
10+** This software is made freely available under the TVA Open Source Agreement (see below).
11+**
12+** Code Modification History:
13+** ===================================================================================================
14+** 05/31/2011 - Pinal C. Patel
15+** Generated original version of source code.
16+**
17+*******************************************************************************************************
18+-->
19+
20+<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
21+ <metadata>
22+ <id>TVACodeLibrary.Libraries.ServiceProcess</id>
23+ <version>$version$</version>
24+ <authors>$author$</authors>
25+ <description>$description$</description>
26+ <projectUrl>http://tvacodelibrary.codeplex.com/</projectUrl>
27+ <licenseUrl>http://tvacodelibrary.codeplex.com/license</licenseUrl>
28+ <iconUrl>http://download.codeplex.com/Project/Download/FileDownload.aspx?ProjectName=tvacodelibrary&#x26;DownloadId=140265&#x26;Build=17672</iconUrl>
29+ <dependencies>
30+ <dependency id="TVACodeLibrary.Libraries.Core" version="" />
31+ <dependency id="TVACodeLibrary.Libraries.Communication" version="" />
32+ <dependency id="TVACodeLibrary.Libraries.Security" version="" />
33+ </dependencies>
34+ </metadata>
35+</package>

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