Merge pull request #637 from LogosBible/enetdown
[mono.git] / mcs / class / System.ServiceProcess / System.ServiceProcess / ServiceController.cs
1 //
2 // System.ServiceProcess.ServiceController 
3 //
4 // Authors:
5 //      Marek Safar (marek.safar@seznam.cz)
6 //      Gert Driesen (drieseng@users.sourceforge.net)
7 //
8 // (C) 2005, Marek Safar
9 //
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30 // TODO: check if there's more information to cache (eg. status)
31 // Start / Stop / ...
32
33 using System;
34 using System.ComponentModel;
35 using System.Globalization;
36 using System.Runtime.InteropServices;
37 using System.ServiceProcess.Design;
38 using System.Threading;
39
40 namespace System.ServiceProcess
41 {
42         [Designer("System.ServiceProcess.Design.ServiceControllerDesigner, " + Consts.AssemblySystem_Design)]
43         [MonoTODO ("No unix implementation")]
44         [ServiceProcessDescription ("Provides the ability to connect to, query, and manipulate running or stopped Windows services.")]
45         public class ServiceController : Component
46         {
47                 private string _name;
48                 private string _serviceName = string.Empty;
49                 private string _machineName;
50                 private string _displayName = string.Empty;
51                 private readonly ServiceControllerImpl _impl;
52                 private ServiceController [] _dependentServices;
53                 private ServiceController [] _servicesDependedOn;
54
55                 public ServiceController ()
56                 {
57                         _machineName = ".";
58                         _name = string.Empty;
59                         _impl = CreateServiceControllerImpl (this);
60                 }
61
62                 public ServiceController (string name) : this (name, ".")
63                 {
64                 }
65
66                 public ServiceController (string name, string machineName)
67                 {
68                         if (name == null || name.Length == 0)
69                                 throw new ArgumentException (string.Format (CultureInfo.CurrentCulture,
70                                         "Invalid value {0} for parameter name.", name));
71
72                         ValidateMachineName (machineName);
73
74                         _machineName = machineName;
75                         _name = name;
76                         _impl = CreateServiceControllerImpl (this);
77                 }
78
79                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
80                 [ServiceProcessDescription ("Whether this service recognizes the Pause and Continue commands.")]
81                 public bool CanPauseAndContinue {
82                         get {
83                                 ValidateServiceName (ServiceName);
84                                 return _impl.CanPauseAndContinue;
85                         }
86                 }
87
88                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
89                 [ServiceProcessDescription ("Whether this service can respond to a system shutdown.")]
90                 public bool CanShutdown {
91                         get
92                         {
93                                 ValidateServiceName (ServiceName);
94                                 return _impl.CanShutdown;
95                         }
96                 }
97
98                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
99                 [ServiceProcessDescription ("Whether this service can be stopped.")]
100                 public bool CanStop {
101                         get
102                         {
103                                 ValidateServiceName (ServiceName);
104                                 return _impl.CanStop;
105                         }
106                 }
107
108                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
109                 [ServiceProcessDescription ("The services that depend on this service in order to run.")]
110                 public ServiceController [] DependentServices {
111                         get
112                         {
113                                 ValidateServiceName (ServiceName);
114                                 if (_dependentServices == null)
115                                         _dependentServices = _impl.DependentServices;
116                                 return _dependentServices;
117                         }
118                 }
119
120                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
121                 [ReadOnly (true)]
122                 [ServiceProcessDescription ("The descriptive name of the service.")]
123                 public string DisplayName {
124                         get {
125                                 if (_displayName.Length == 0 && (_serviceName.Length > 0 || _name.Length > 0))
126                                         _displayName = _impl.DisplayName;
127                                 return _displayName;
128                         }
129                         set {
130                                 if (value == null)
131                                         throw new ArgumentNullException ("value");
132
133                                 if (_displayName == value)
134                                         return;
135
136                                 _displayName = value;
137
138                                 // if display name is modified, then we also need to force a
139                                 // new lookup of the corresponding service name
140                                 _serviceName = string.Empty;
141
142                                 // you'd expect the DependentServices and ServiceDependedOn cache
143                                 // to be cleared too, but the MS implementation doesn't do this
144                                 //
145                                 // categorized as by design:
146                                 // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=201762
147
148                                 // release any handles and clear cache
149                                 Close ();
150                         }
151                 }
152
153                 [Browsable (false)]
154                 [DefaultValue (".")]
155                 [RecommendedAsConfigurable (true)]
156                 [ServiceProcessDescription ("The name of the machine on which this service resides.")]
157                 public string MachineName {
158                         get {
159                                 return _machineName;
160                         }
161                         set {
162                                 ValidateMachineName (value);
163
164                                 if (_machineName == value)
165                                         return;
166
167                                 _machineName = value;
168
169                                 // you'd expect the DependentServices and ServiceDependedOn cache
170                                 // to be cleared too, but the MS implementation doesn't do this
171                                 //
172                                 // categorized as by design:
173                                 // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=201762
174
175                                 // release any handles and clear cache
176                                 Close ();
177                         }
178                 }
179
180                 [DefaultValue ("")]
181                 [ReadOnly (true)]
182                 [RecommendedAsConfigurable (true)]
183                 [ServiceProcessDescription ("The short name of the service.")]
184                 [TypeConverter (typeof (ServiceNameConverter))]
185                 public string ServiceName {
186                         get {
187                                 if (_serviceName.Length == 0 && (_displayName.Length > 0 || _name.Length > 0))
188                                         _serviceName = _impl.ServiceName;
189                                 return _serviceName;
190                         }
191                         set {
192                                 if (value == null)
193                                         throw new ArgumentNullException ("value");
194
195                                 if (_serviceName == value)
196                                         return;
197
198                                 ValidateServiceName (value);
199
200                                 _serviceName = value;
201
202                                 // if service name is modified, then we also need to force a
203                                 // new lookup of the corresponding display name
204                                 _displayName = string.Empty;
205
206                                 // you'd expect the DependentServices and ServiceDependedOn cache
207                                 // to be cleared too, but the MS implementation doesn't do this
208                                 //
209                                 // categorized as by design:
210                                 // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=201762
211
212                                 // release any handles and clear cache
213                                 Close ();
214                         }
215                 }
216
217                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
218                 [ServiceProcessDescription ("Services that must be started in order for this one to start.")]
219                 public ServiceController [] ServicesDependedOn {
220                         get
221                         {
222                                 ValidateServiceName (ServiceName);
223                                 if (_servicesDependedOn == null)
224                                         _servicesDependedOn = _impl.ServicesDependedOn;
225                                 return _servicesDependedOn;
226                         }
227                 }
228
229                 [MonoTODO]
230                 [Browsable (false)]
231                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
232                 public SafeHandle ServiceHandle 
233                 {
234                         get {
235                                 throw new NotImplementedException ();
236                         }
237                 }
238
239                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
240                 [ServiceProcessDescription ("The type of this service.")]
241                 public ServiceType ServiceType {
242                         get
243                         {
244                                 ValidateServiceName (ServiceName);
245                                 return _impl.ServiceType;
246                         }
247                 }
248
249                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
250                 [ServiceProcessDescription ("The status of the service, e.g., Running or Stopped.")]
251                 public ServiceControllerStatus Status {
252                         get
253                         {
254                                 ValidateServiceName (ServiceName);
255                                 return _impl.Status;
256                         }
257                 }
258
259                 public void Close () 
260                 {
261                         _impl.Close ();
262                 }
263
264                 public void Continue ()
265                 {
266                         ValidateServiceName (ServiceName);
267                         _impl.Continue ();
268                 }
269
270                 protected override void Dispose (bool disposing)
271                 {
272                         _impl.Dispose (disposing);
273                         base.Dispose (disposing);
274                 }
275
276                 public void ExecuteCommand (int command)
277                 {
278                         ValidateServiceName (ServiceName);
279                         _impl.ExecuteCommand (command);
280                 }
281
282                 public static ServiceController[] GetDevices ()
283                 {
284                         return GetDevices (".");
285                 }
286
287                 public static ServiceController[] GetDevices (string machineName)
288                 {
289                         ValidateMachineName (machineName);
290
291                         using (ServiceController sc = new ServiceController ("dummy", machineName)) {
292                                 ServiceControllerImpl impl = CreateServiceControllerImpl (sc);
293                                 return impl.GetDevices ();
294                         }
295                 }
296
297                 public static ServiceController[] GetServices ()
298                 {
299                         return GetServices (".");
300                 }
301
302                 public static ServiceController[] GetServices (string machineName)
303                 {
304                         ValidateMachineName (machineName);
305
306                         using (ServiceController sc = new ServiceController ("dummy", machineName)) {
307                                 ServiceControllerImpl impl = CreateServiceControllerImpl (sc);
308                                 return impl.GetServices ();
309                         }
310                 }
311
312                 public void Pause ()
313                 {
314                         ValidateServiceName (ServiceName);
315                         _impl.Pause ();
316                 }
317
318                 public void Refresh ()
319                 {
320                         // MSDN: this method also sets the  ServicesDependedOn and 
321                         // DependentServices properties to a null reference
322                         //
323                         // I assume they wanted to say that the cache for these properties
324                         // is cleared. Verified by unit tests.
325                         _dependentServices = null;
326                         _servicesDependedOn = null;
327                         _impl.Refresh ();
328                 }
329
330                 public void Start () 
331                 {
332                         Start (new string [0]);
333                 }
334
335                 public void Start (string [] args)
336                 {
337                         ValidateServiceName (ServiceName);
338                         _impl.Start (args);
339                 }
340
341                 public void Stop ()
342                 {
343                         ValidateServiceName (ServiceName);
344                         _impl.Stop ();
345                 }
346
347                 public void WaitForStatus (ServiceControllerStatus desiredStatus)
348                 {
349                         WaitForStatus (desiredStatus, TimeSpan.MaxValue);
350                 }
351
352                 public void WaitForStatus (ServiceControllerStatus desiredStatus, TimeSpan timeout)
353                 {
354                         ValidateServiceName (ServiceName);
355
356                         DateTime start = DateTime.Now;
357                         while (Status != desiredStatus) {
358                                 if (timeout  < (DateTime.Now - start))
359                                         throw new TimeoutException ("Time out has expired and the"
360                                                 + " operation has not been completed.");
361                                 Thread.Sleep (100);
362                                 // force refresh of status
363                                 Refresh ();
364                         }
365                 }
366
367                 internal string Name {
368                         get {
369                                 return _name;
370                         }
371                         set {
372                                 _name = value;
373                         }
374                 }
375
376                 internal string InternalDisplayName {
377                         get {
378                                 return _displayName;
379                         }
380                         set {
381                                 _displayName = value;
382                         }
383                 }
384
385                 internal string InternalServiceName {
386                         get {
387                                 return _serviceName;
388                         }
389                         set {
390                                 _serviceName = value;
391                         }
392                 }
393
394                 private static void ValidateServiceName (string serviceName)
395                 {
396                         if (serviceName.Length == 0 || serviceName.Length > 80)
397                                 throw new ArgumentException (string.Format (CultureInfo.CurrentCulture,
398                                         "Service name {0} contains invalid characters, is empty"
399                                         + " or is too long (max length = 80).", serviceName));
400                 }
401
402                 private static void ValidateMachineName (string machineName)
403                 {
404                         if (machineName == null || machineName.Length == 0)
405                                 throw new ArgumentException (string.Format (CultureInfo.CurrentCulture,
406                                         "MachineName value {0} is invalid.", machineName));
407                 }
408
409                 private static ServiceControllerImpl CreateServiceControllerImpl (ServiceController serviceController)
410                 {
411                         int p = (int) Environment.OSVersion.Platform;
412
413                         if (p == 4 || p == 128 || p == 6){
414                                 return new UnixServiceController (serviceController);
415                         } else {
416                                 return new Win32ServiceController (serviceController);
417                         }
418                 }
419         }
420 }