Reverse previous changes
[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 #if NET_2_0
37 using System.Runtime.InteropServices;
38 #endif
39 using System.ServiceProcess.Design;
40 using System.Threading;
41
42 namespace System.ServiceProcess
43 {
44         [Designer("System.ServiceProcess.Design.ServiceControllerDesigner, " + Consts.AssemblySystem_Design)]
45         [MonoTODO ("No unix implementation")]
46 #if NET_2_0
47         [ServiceProcessDescription ("Provides the ability to connect to, query, and manipulate running or stopped Windows services.")]
48 #endif
49         public class ServiceController : Component
50         {
51                 private string _name;
52                 private string _serviceName = string.Empty;
53                 private string _machineName;
54                 private string _displayName = string.Empty;
55                 private readonly ServiceControllerImpl _impl;
56                 private ServiceController [] _dependentServices;
57                 private ServiceController [] _servicesDependedOn;
58
59                 public ServiceController ()
60                 {
61                         _machineName = ".";
62                         _name = string.Empty;
63                         _impl = CreateServiceControllerImpl (this);
64                 }
65
66                 public ServiceController (string name) : this (name, ".")
67                 {
68                 }
69
70                 public ServiceController (string name, string machineName)
71                 {
72                         if (name == null || name.Length == 0)
73                                 throw new ArgumentException (string.Format (CultureInfo.CurrentCulture,
74                                         "Invalid value {0} for parameter name.", name));
75
76                         ValidateMachineName (machineName);
77
78                         _machineName = machineName;
79                         _name = name;
80                         _impl = CreateServiceControllerImpl (this);
81                 }
82
83                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
84                 [ServiceProcessDescription ("Whether this service recognizes the Pause and Continue commands.")]
85                 public bool CanPauseAndContinue {
86                         get {
87 #if NET_2_0
88                                 ValidateServiceName (ServiceName);
89 #endif
90                                 return _impl.CanPauseAndContinue;
91                         }
92                 }
93
94                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
95                 [ServiceProcessDescription ("Whether this service can respond to a system shutdown.")]
96                 public bool CanShutdown {
97                         get
98                         {
99 #if NET_2_0
100                                 ValidateServiceName (ServiceName);
101 #endif
102                                 return _impl.CanShutdown;
103                         }
104                 }
105
106                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
107                 [ServiceProcessDescription ("Whether this service can be stopped.")]
108                 public bool CanStop {
109                         get
110                         {
111 #if NET_2_0
112                                 ValidateServiceName (ServiceName);
113 #endif
114                                 return _impl.CanStop;
115                         }
116                 }
117
118                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
119                 [ServiceProcessDescription ("The services that depend on this service in order to run.")]
120                 public ServiceController [] DependentServices {
121                         get
122                         {
123 #if NET_2_0
124                                 ValidateServiceName (ServiceName);
125 #endif
126                                 if (_dependentServices == null)
127                                         _dependentServices = _impl.DependentServices;
128                                 return _dependentServices;
129                         }
130                 }
131
132                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
133                 [ReadOnly (true)]
134                 [ServiceProcessDescription ("The descriptive name of the service.")]
135                 public string DisplayName {
136                         get {
137                                 if (_displayName.Length == 0 && (_serviceName.Length > 0 || _name.Length > 0))
138                                         _displayName = _impl.DisplayName;
139                                 return _displayName;
140                         }
141                         set {
142                                 if (value == null)
143                                         throw new ArgumentNullException ("value");
144
145                                 if (_displayName == value)
146                                         return;
147
148                                 _displayName = value;
149
150                                 // if display name is modified, then we also need to force a
151                                 // new lookup of the corresponding service name
152                                 _serviceName = string.Empty;
153
154                                 // you'd expect the DependentServices and ServiceDependedOn cache
155                                 // to be cleared too, but the MS implementation doesn't do this
156                                 //
157                                 // categorized as by design:
158                                 // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=201762
159
160                                 // release any handles and clear cache
161                                 Close ();
162                         }
163                 }
164
165                 [Browsable (false)]
166                 [DefaultValue (".")]
167                 [RecommendedAsConfigurable (true)]
168                 [ServiceProcessDescription ("The name of the machine on which this service resides.")]
169                 public string MachineName {
170                         get {
171                                 return _machineName;
172                         }
173                         set {
174                                 ValidateMachineName (value);
175
176                                 if (_machineName == value)
177                                         return;
178
179                                 _machineName = value;
180
181                                 // you'd expect the DependentServices and ServiceDependedOn cache
182                                 // to be cleared too, but the MS implementation doesn't do this
183                                 //
184                                 // categorized as by design:
185                                 // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=201762
186
187                                 // release any handles and clear cache
188                                 Close ();
189                         }
190                 }
191
192                 [DefaultValue ("")]
193                 [ReadOnly (true)]
194                 [RecommendedAsConfigurable (true)]
195                 [ServiceProcessDescription ("The short name of the service.")]
196                 [TypeConverter (typeof (ServiceNameConverter))]
197                 public string ServiceName {
198                         get {
199                                 if (_serviceName.Length == 0 && (_displayName.Length > 0 || _name.Length > 0))
200                                         _serviceName = _impl.ServiceName;
201                                 return _serviceName;
202                         }
203                         set {
204                                 if (value == null)
205                                         throw new ArgumentNullException ("value");
206
207                                 if (_serviceName == value)
208                                         return;
209
210 #if NET_2_0
211                                 ValidateServiceName (value);
212 #endif
213
214                                 _serviceName = value;
215
216                                 // if service name is modified, then we also need to force a
217                                 // new lookup of the corresponding display name
218                                 _displayName = string.Empty;
219
220                                 // you'd expect the DependentServices and ServiceDependedOn cache
221                                 // to be cleared too, but the MS implementation doesn't do this
222                                 //
223                                 // categorized as by design:
224                                 // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=201762
225
226                                 // release any handles and clear cache
227                                 Close ();
228                         }
229                 }
230
231                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
232                 [ServiceProcessDescription ("Services that must be started in order for this one to start.")]
233                 public ServiceController [] ServicesDependedOn {
234                         get
235                         {
236 #if NET_2_0
237                                 ValidateServiceName (ServiceName);
238 #endif
239                                 if (_servicesDependedOn == null)
240                                         _servicesDependedOn = _impl.ServicesDependedOn;
241                                 return _servicesDependedOn;
242                         }
243                 }
244
245 #if NET_2_0
246                 [MonoTODO]
247                 [Browsable (false)]
248                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
249                 public SafeHandle ServiceHandle 
250                 {
251                         get {
252                                 throw new NotImplementedException ();
253                         }
254                 }
255 #endif
256
257                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
258                 [ServiceProcessDescription ("The type of this service.")]
259                 public ServiceType ServiceType {
260                         get
261                         {
262 #if NET_2_0
263                                 ValidateServiceName (ServiceName);
264 #endif
265                                 return _impl.ServiceType;
266                         }
267                 }
268
269                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
270                 [ServiceProcessDescription ("The status of the service, e.g., Running or Stopped.")]
271                 public ServiceControllerStatus Status {
272                         get
273                         {
274 #if NET_2_0
275                                 ValidateServiceName (ServiceName);
276 #endif
277                                 return _impl.Status;
278                         }
279                 }
280
281                 public void Close () 
282                 {
283                         _impl.Close ();
284                 }
285
286                 public void Continue ()
287                 {
288 #if NET_2_0
289                         ValidateServiceName (ServiceName);
290 #endif
291                         _impl.Continue ();
292                 }
293
294                 protected override void Dispose (bool disposing)
295                 {
296                         _impl.Dispose (disposing);
297                         base.Dispose (disposing);
298                 }
299
300                 public void ExecuteCommand (int command)
301                 {
302 #if NET_2_0
303                         ValidateServiceName (ServiceName);
304 #endif
305                         _impl.ExecuteCommand (command);
306                 }
307
308                 public static ServiceController[] GetDevices ()
309                 {
310                         return GetDevices (".");
311                 }
312
313                 public static ServiceController[] GetDevices (string machineName)
314                 {
315                         ValidateMachineName (machineName);
316
317                         using (ServiceController sc = new ServiceController ("dummy", machineName)) {
318                                 ServiceControllerImpl impl = CreateServiceControllerImpl (sc);
319                                 return impl.GetDevices ();
320                         }
321                 }
322
323                 public static ServiceController[] GetServices ()
324                 {
325                         return GetServices (".");
326                 }
327
328                 public static ServiceController[] GetServices (string machineName)
329                 {
330                         ValidateMachineName (machineName);
331
332                         using (ServiceController sc = new ServiceController ("dummy", machineName)) {
333                                 ServiceControllerImpl impl = CreateServiceControllerImpl (sc);
334                                 return impl.GetServices ();
335                         }
336                 }
337
338                 public void Pause ()
339                 {
340 #if NET_2_0
341                         ValidateServiceName (ServiceName);
342 #endif
343                         _impl.Pause ();
344                 }
345
346                 public void Refresh ()
347                 {
348                         // MSDN: this method also sets the  ServicesDependedOn and 
349                         // DependentServices properties to a null reference
350                         //
351                         // I assume they wanted to say that the cache for these properties
352                         // is cleared. Verified by unit tests.
353                         _dependentServices = null;
354                         _servicesDependedOn = null;
355                         _impl.Refresh ();
356                 }
357
358                 public void Start () 
359                 {
360                         Start (new string [0]);
361                 }
362
363                 public void Start (string [] args)
364                 {
365 #if NET_2_0
366                         ValidateServiceName (ServiceName);
367 #endif
368                         _impl.Start (args);
369                 }
370
371                 public void Stop ()
372                 {
373 #if NET_2_0
374                         ValidateServiceName (ServiceName);
375 #endif
376                         _impl.Stop ();
377                 }
378
379                 public void WaitForStatus (ServiceControllerStatus desiredStatus)
380                 {
381                         WaitForStatus (desiredStatus, TimeSpan.MaxValue);
382                 }
383
384                 public void WaitForStatus (ServiceControllerStatus desiredStatus, TimeSpan timeout)
385                 {
386 #if NET_2_0
387                         ValidateServiceName (ServiceName);
388 #endif
389
390                         DateTime start = DateTime.Now;
391                         while (Status != desiredStatus) {
392                                 if (timeout  < (DateTime.Now - start))
393                                         throw new TimeoutException ("Time out has expired and the"
394                                                 + " operation has not been completed.");
395                                 Thread.Sleep (100);
396                                 // force refresh of status
397                                 Refresh ();
398                         }
399                 }
400
401                 internal string Name {
402                         get {
403                                 return _name;
404                         }
405                         set {
406                                 _name = value;
407                         }
408                 }
409
410                 internal string InternalDisplayName {
411                         get {
412                                 return _displayName;
413                         }
414                         set {
415                                 _displayName = value;
416                         }
417                 }
418
419                 internal string InternalServiceName {
420                         get {
421                                 return _serviceName;
422                         }
423                         set {
424                                 _serviceName = value;
425                         }
426                 }
427
428 #if NET_2_0
429                 private static void ValidateServiceName (string serviceName)
430                 {
431                         if (serviceName.Length == 0 || serviceName.Length > 80)
432                                 throw new ArgumentException (string.Format (CultureInfo.CurrentCulture,
433                                         "Service name {0} contains invalid characters, is empty"
434                                         + " or is too long (max length = 80).", serviceName));
435                 }
436 #endif
437
438                 private static void ValidateMachineName (string machineName)
439                 {
440                         if (machineName == null || machineName.Length == 0)
441                                 throw new ArgumentException (string.Format (CultureInfo.CurrentCulture,
442                                         "MachineName value {0} is invalid.", machineName));
443                 }
444
445                 private static ServiceControllerImpl CreateServiceControllerImpl (ServiceController serviceController)
446                 {
447                         int p = (int) Environment.OSVersion.Platform;
448
449                         if (p == 4 || p == 128 || p == 6){
450                                 return new UnixServiceController (serviceController);
451                         } else {
452                                 return new Win32ServiceController (serviceController);
453                         }
454                 }
455         }
456 }