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
258using GSF.Communication;
259using GSF.Configuration;
260using System;
261using System.ComponentModel;
262using System.Configuration;
263using System.Drawing;
264using System.Text;
265using System.Threading;
266
267namespace 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
42using GSF.Identity;
43using GSF.Reflection;
44using System;
45using System.Security.Principal;
46using System.Web.Hosting;
47
48namespace 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
41using GSF.Console;
42using System;
43using System.Collections.Generic;
44
45namespace 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
34using System;
35
36namespace 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
30using System;
31
32namespace 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
28using System.Runtime.CompilerServices;
29
30namespace 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 @@
1using System;
2using System.Reflection;
3using 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
318using GSF.Collections;
319using GSF.Communication;
320using GSF.Configuration;
321using GSF.Diagnostics;
322using GSF.ErrorManagement;
323using GSF.IO;
324using GSF.Reflection;
325using GSF.Scheduling;
326using GSF.Security;
327using GSF.Security.Cryptography;
328using System;
329using System.Collections.Generic;
330using System.ComponentModel;
331using System.Configuration;
332using System.Diagnostics;
333using System.Drawing;
334using System.Security;
335using System.Security.Principal;
336using System.ServiceProcess;
337using System.Text;
338using System.Threading;
339
340namespace 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
270using GSF.Interop;
271using System;
272using System.Collections.Generic;
273using System.Configuration.Install;
274using System.Diagnostics;
275using System.Runtime.InteropServices;
276using System.ServiceProcess;
277using System.Threading;
278
279namespace 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
34using GSF.Units;
35using System;
36using System.Text;
37using System.Threading;
38
39namespace 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
34using System;
35using System.Collections.Generic;
36
37namespace 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