venus: Remove dedebug
[openafs.git] / src / libadmin / cfg / cfginternal.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/stds.h>
13
14 #include <roken.h>
15
16 #ifdef AFS_NT40_ENV
17 #include <windows.h>
18 #include <WINNT/afsreg.h>
19 #endif /* AFS_NT40_ENV */
20
21 #ifdef HAVE_MATH_H
22 # include <math.h>
23 #endif
24
25 #include <pthread.h>
26
27 #include <afs/afs_Admin.h>
28 #include <afs/afs_AdminErrors.h>
29 #include <afs/afs_bosAdmin.h>
30 #include <afs/afs_clientAdmin.h>
31
32 #include <afs/dirpath.h>
33
34 #include "cfginternal.h"
35 #include "../adminutil/afs_AdminInternal.h"
36
37
38 /*
39  * cfgutil_HostHandleValidate() -- validate a host configuration handle
40  *
41  * RETURN CODES: 1 success, 0 failure
42  */
43 int
44 cfgutil_HostHandleValidate(const cfg_host_p cfg_host, afs_status_p st)
45 {
46     int rc = 1;
47     afs_status_t tst = 0;
48
49     if (cfg_host == NULL) {
50         tst = ADMCFGHOSTHANDLENULL;
51
52     } else if (cfg_host->begin_magic != BEGIN_MAGIC
53                || cfg_host->end_magic != END_MAGIC) {
54         tst = ADMCFGHOSTHANDLEBADMAGIC;
55
56     } else if (cfg_host->is_valid == 0) {
57         tst = ADMCFGHOSTHANDLEINVALID;
58
59     } else if (cfg_host->hostName == NULL) {
60         tst = ADMCFGHOSTHANDLEHOSTNAMENULL;
61
62     } else if (cfg_host->cellHandle == NULL) {
63         tst = ADMCFGHOSTHANDLECELLHANDLENULL;
64
65     } else if (cfg_host->cellName == NULL) {
66         tst = ADMCFGHOSTHANDLECELLNAMENULL;
67     }
68
69     if (tst != 0) {
70         /* indicate failure */
71         rc = 0;
72     }
73     if (st != NULL) {
74         *st = tst;
75     }
76     return rc;
77 }
78
79
80 /*
81  * cfgutil_HostHandleBosInit() -- initialize bosserver handle in host
82  *     configuration handle.
83  * 
84  * RETURN CODES: 1 success, 0 failure
85  */
86 int
87 cfgutil_HostHandleBosInit(cfg_host_p cfg_host, afs_status_p st)
88 {
89     int rc = 1;
90     afs_status_t tst = 0;
91
92     if (pthread_mutex_lock(&cfg_host->mutex)) {
93         tst = ADMMUTEXLOCK;
94     } else {
95         if (cfg_host->bosHandle == NULL) {
96             /* initialize bosserver handle for host */
97             void *bosHandle;
98             afs_status_t tst2;
99
100             if (bos_ServerOpen
101                 (cfg_host->cellHandle, cfg_host->hostName, &bosHandle,
102                  &tst2)) {
103                 cfg_host->bosHandle = bosHandle;
104             } else {
105                 tst = tst2;
106             }
107         }
108         if (pthread_mutex_unlock(&cfg_host->mutex)) {
109             /* can only return one status; mutex failure is critical */
110             tst = ADMMUTEXUNLOCK;
111         }
112     }
113
114     if (tst != 0) {
115         /* indicate failure */
116         rc = 0;
117     }
118     if (st != NULL) {
119         *st = tst;
120     }
121     return rc;
122 }
123
124
125 /*
126  * cfgutil_HostHandleCellNameCompatible() -- determine if specified cell name
127  *     is compatible with the cell name in the given cfg handle.
128  * 
129  * RETURN CODES: 1 compatible, 0 not compatible
130  */
131 int
132 cfgutil_HostHandleCellNameCompatible(const cfg_host_p cfg_host,
133                                      const char *cellName)
134 {
135     int rc;
136
137     if (*cfg_host->cellName == '\0') {
138         /* null cell handle; any cell name compatible by definition */
139         rc = 1;
140     } else {
141         /* standard cell handle; compare cell names */
142         rc = (strcasecmp(cfg_host->cellName, cellName) == 0 ? 1 : 0);
143     }
144     return rc;
145 }
146
147
148 /*
149  * cfgutil_HostNameGetFull() -- return fully qualified version of specified
150  *     host name.
151  *
152  *     Note: fullHostName is presumed to be a buffer of size MAXHOSTCHARS.
153  *
154  * RETURN CODES: 1 success, 0 failure
155  */
156 int
157 cfgutil_HostNameGetFull(const char *hostName, char *fullHostName,
158                         afs_status_p st)
159 {
160     int rc = 1;
161     afs_status_t tst = 0;
162
163 #ifdef AFS_NT40_ENV
164     /* Note: gethostbyname() allocs hostent on a per-thread basis */
165     struct hostent *hentryp;
166
167     if ((hentryp = gethostbyname(hostName)) == NULL) {
168         tst = ADMCFGCANTRESOLVEHOSTNAME;
169     } else {
170         size_t hostNameLen = strlen(hostName);
171         char *fqName = hentryp->h_name;
172
173         /* verify that canonical name is an expansion of name specified */
174         if (strncasecmp(fqName, hostName, hostNameLen) != 0
175             || fqName[hostNameLen] != '.') {
176             /* canonical name not a direct expansion; consider aliases */
177             int i;
178
179             for (i = 0; hentryp->h_aliases[i] != NULL; i++) {
180                 char *aliasName = hentryp->h_aliases[i];
181                 if (strncasecmp(aliasName, hostName, hostNameLen) == 0
182                     && aliasName[hostNameLen] == '.') {
183                     /* found a direct exapansion of specified name */
184                     fqName = aliasName;
185                     break;
186                 }
187             }
188         }
189
190         if (strlen(fqName) > (MAXHOSTCHARS - 1)) {
191             tst = ADMCFGRESOLVEDHOSTNAMETOOLONG;
192         } else {
193             strcpy(fullHostName, fqName);
194
195             /* lower-case name for consistency */
196             _strlwr(fullHostName);
197         }
198     }
199 #else
200     /* function not yet implemented for Unix */
201     tst = ADMCFGNOTSUPPORTED;
202 #endif /* AFS_NT40_ENV */
203
204     if (tst != 0) {
205         /* indicate failure */
206         rc = 0;
207     }
208     if (st != NULL) {
209         *st = tst;
210     }
211     return rc;
212 }
213
214
215 /*
216  * cfgutil_HostNameIsAlias() -- determine if specified host names
217  *     are aliases (functionally equivalent).
218  *
219  * RETURN CODES: 1 success, 0 failure
220  */
221 int
222 cfgutil_HostNameIsAlias(const char *hostName1, const char *hostName2,
223                         short *isAlias, afs_status_p st)
224 {
225     int rc = 1;
226     afs_status_t tst2, tst = 0;
227     int addrCount1, addrCount2;
228     afs_int32 *addrList1 = NULL, *addrList2 = NULL;
229
230     /* get all addrs for first host */
231
232     if (!cfgutil_HostAddressFetchAll
233         (hostName1, &addrCount1, &addrList1, &tst2)) {
234         tst = tst2;
235     }
236
237     /* get all addrs for second host */
238
239     if (tst == 0) {
240         if (!cfgutil_HostAddressFetchAll
241             (hostName2, &addrCount2, &addrList2, &tst2)) {
242             tst = tst2;
243                 if (addrList1) {
244                         free(addrList1);
245                         addrList1 = NULL;
246                 }
247         }
248     }
249
250     /* compare lists looking for a match */
251
252     if (tst == 0) {
253         int i, j;
254
255         *isAlias = 0;
256
257         for (i = 0; i < addrCount1; i++) {
258             for (j = 0; j < addrCount2; j++) {
259                 if (addrList1[i] == addrList2[j]) {
260                     *isAlias = 1;
261                     break;
262                 }
263             }
264         }
265                 if (addrList1) {
266                         free(addrList1);
267                         addrList1 = NULL;
268                 }
269                 if (addrList2) {
270                         free(addrList2);
271                         addrList2 = NULL;
272                 }
273     }
274
275     if (tst != 0) {
276         /* indicate failure */
277         rc = 0;
278     }
279     if (st != NULL) {
280         *st = tst;
281     }
282     return rc;
283 }
284
285
286 /*
287  * cfgutil_HostNameIsLocal() -- determine if the specified host name is
288  *     equivalent to (is an alias for) the standard host name for the
289  *     machine on which this process is running.
290  *
291  * RETURN CODES: 1 success, 0 failure
292  */
293 int
294 cfgutil_HostNameIsLocal(const char *hostName, short *isLocal, afs_status_p st)
295 {
296     int rc = 1;
297     afs_status_t tst2, tst = 0;
298     char localName[MAXHOSTCHARS];
299
300     if (gethostname(localName, MAXHOSTCHARS)) {
301         /* failed to lookup local name */
302         tst = ADMCANTGETLOCALNAME;
303     } else if (!cfgutil_HostNameIsAlias(hostName, localName, isLocal, &tst2)) {
304         tst = tst2;
305     }
306
307     if (tst != 0) {
308         /* indicate failure */
309         rc = 0;
310     }
311     if (st != NULL) {
312         *st = tst;
313     }
314     return rc;
315 }
316
317
318 /*
319  * cfgutil_HostNameGetCellServDbAlias() -- Get alias for given host name
320  *     as listed in the server CellServDB on the specified host.  If no
321  *     alias is found then hostNameAlias is set to the empty string.
322  *
323  *     Note: hostNameAlias is presumed to be a buffer of size MAXHOSTCHARS.
324  *
325  * RETURN CODES: 1 success, 0 failure
326  */
327 int
328 cfgutil_HostNameGetCellServDbAlias(const char *fsDbHost, const char *hostName,
329                                    char *hostNameAlias, afs_status_p st)
330 {
331     int rc = 1;
332     afs_status_t tst2, tst = 0;
333     void *cellHandle;
334     void *bosHandle;
335
336     if (!afsclient_NullCellOpen(&cellHandle, &tst2)) {
337         tst = tst2;
338     } else {
339         if (!bos_ServerOpen(cellHandle, fsDbHost, &bosHandle, &tst2)) {
340             tst = tst2;
341         } else {
342             void *dbIter;
343
344             if (!bos_HostGetBegin(bosHandle, &dbIter, &tst2)) {
345                 tst = tst2;
346             } else {
347                 short dbhostDone = 0;
348                 short dbhostFound = 0;
349
350                 while (!dbhostDone) {
351                     short isAlias;
352
353                     if (!bos_HostGetNext(dbIter, hostNameAlias, &tst2)) {
354                         /* no more entries (or failure) */
355                         if (tst2 != ADMITERATORDONE) {
356                             tst = tst2;
357                         }
358                         dbhostDone = 1;
359
360                     } else
361                         if (!cfgutil_HostNameIsAlias
362                             (hostName, hostNameAlias, &isAlias, &tst2)) {
363                         tst = tst2;
364                         dbhostDone = 1;
365
366                     } else if (isAlias) {
367                         dbhostFound = 1;
368                         dbhostDone = 1;
369                     }
370                 }
371
372                 if (!dbhostFound) {
373                     *hostNameAlias = '\0';
374                 }
375
376                 if (!bos_HostGetDone(dbIter, &tst2)) {
377                     tst = tst2;
378                 }
379             }
380
381             if (!bos_ServerClose(bosHandle, &tst2)) {
382                 tst = tst2;
383             }
384         }
385
386         if (!afsclient_CellClose(cellHandle, &tst2)) {
387             tst = tst2;
388         }
389     }
390
391     if (tst != 0) {
392         /* indicate failure */
393         rc = 0;
394     }
395     if (st != NULL) {
396         *st = tst;
397     }
398     return rc;
399 }
400
401
402 /*
403  * cfgutil_HostNameGetAddressString() -- Get IP address for specified host in
404  *     in the canonical string form.
405  *
406  *     Returns pointer to a per-thread buffer; do not deallocate.
407  */
408 int
409 cfgutil_HostNameGetAddressString(const char *hostName, const char **hostAddr,
410                                  afs_status_p st)
411 {
412     int rc = 1;
413     afs_status_t tst2, tst = 0;
414     int addrCount;
415     afs_int32 *addrList = NULL;
416
417     /* get address list for host */
418
419     if (!cfgutil_HostAddressFetchAll(hostName, &addrCount, &addrList, &tst2)) {
420         tst = tst2;
421     }
422
423     /* convert first (canonical) address to string */
424
425     if (tst == 0) {
426         struct in_addr ina;
427         char *inaString;
428
429         ina.s_addr = htonl(addrList[0]);
430         if ((inaString = inet_ntoa(ina)) == NULL) {
431             /* should never happen */
432             tst = ADMCFGCANTRESOLVEHOSTNAME;
433         } else {
434             *hostAddr = inaString;
435         }
436                 if (addrList) {
437                         free(addrList);
438                         addrList = NULL;
439                 }
440     }
441
442     if (tst != 0) {
443         /* indicate failure */
444         rc = 0;
445     }
446     if (st != NULL) {
447         *st = tst;
448     }
449     return rc;
450 }
451
452
453 /*
454  * cfgutil_HostAddressFetchAll() -- get allocated list of all known
455  *     addresses for specified host.
456  *
457  *     Note: upon success, *addrList is an address array in host byte order.
458  *
459  * RETURN CODES: 1 success, 0 failure
460  */
461 int
462 cfgutil_HostAddressFetchAll(const char *hostName, int *addrCount,
463                             afs_int32 ** addrList, afs_status_p st)
464 {
465     int rc = 1;
466     afs_status_t tst = 0;
467     int aCount = 0;
468     afs_int32 *aList = NULL;
469
470 #ifdef AFS_NT40_ENV
471     /* Note: gethostbyname() allocs hostent on a per-thread basis */
472     struct hostent *hentryp;
473
474     if ((hentryp = gethostbyname(hostName)) == NULL) {
475         tst = ADMCFGCANTRESOLVEHOSTNAME;
476     } else {
477         int i;
478
479         /* assuming IPv4 addrs returned */
480         for (i = 0; hentryp->h_addr_list[i] != NULL; i++);
481         aCount = i;
482
483         if ((aList = malloc(aCount * sizeof(afs_int32))) == NULL) {
484             tst = ADMNOMEM;
485         } else {
486             for (i = 0; i < aCount; i++) {
487                 afs_int32 hostAddr;
488                 memcpy((void *)&hostAddr, (void *)hentryp->h_addr_list[i],
489                        sizeof(afs_int32));
490                 aList[i] = ntohl(hostAddr);
491             }
492         }
493     }
494 #else
495     /* function not yet implemented for Unix */
496     tst = ADMCFGNOTSUPPORTED;
497 #endif /* AFS_NT40_ENV */
498
499     if (tst == 0) {
500         /* return results */
501         *addrCount = aCount;
502         *addrList = aList;
503     } else {
504         /* indicate failure */
505         rc = 0;
506     }
507     if (st != NULL) {
508         *st = tst;
509     }
510     return rc;
511 }
512
513
514 /*
515  * cfgutil_HostAddressIsValid() -- determine if address is a valid address
516  *     for the named host.
517  *
518  *     Note: hostAddr must be specified in host byte order
519  *
520  * RETURN CODES: 1 success, 0 failure
521  */
522 int
523 cfgutil_HostAddressIsValid(const char *hostName, int hostAddr, short *isValid,
524                            afs_status_p st)
525 {
526     int rc = 1;
527     afs_status_t tst2, tst = 0;
528     int addrCount;
529     afs_int32 *addrList = NULL;
530
531     *isValid = 0;
532
533     /* get all addrs for host */
534
535     if (!cfgutil_HostAddressFetchAll(hostName, &addrCount, &addrList, &tst2)) {
536         tst = tst2;
537     }
538
539     /* search list looking for match */
540
541     if (tst == 0) {
542         int i;
543
544         for (i = 0; i < addrCount; i++) {
545             if (addrList[i] == hostAddr) {
546                 *isValid = 1;
547                 break;
548             }
549         }
550                 if (addrList) {
551                         free(addrList);
552                         addrList = NULL;
553                 }
554     }
555
556     if (tst != 0) {
557         /* indicate failure */
558         rc = 0;
559     }
560     if (st != NULL) {
561         *st = tst;
562     }
563     return rc;
564 }
565
566
567 /*
568  * cfgutil_CleanDirectory() -- remove all files from specified directory;
569  *     function is NOT recursive.
570  *
571  * RETURN CODES: 1 success, 0 failure
572  */
573 int
574 cfgutil_CleanDirectory(const char *dirName, afs_status_p st)
575 {
576     int rc = 1;
577     afs_status_t tst = 0;
578
579     DIR *dirp;
580     struct dirent *entryp;
581     char dirfile[MAXPATHLEN];
582
583     if ((dirp = opendir(dirName)) == NULL) {
584         /* cannot open directory */
585         if (errno == EACCES) {
586             tst = ADMNOPRIV;
587         }
588         /* otherwise assume directory does not exist, which is OK */
589     } else {
590         while ((entryp = readdir(dirp)) != NULL) {
591             /* remove file (except "." and "..") */
592             if (strcmp(entryp->d_name, ".") && strcmp(entryp->d_name, "..")) {
593                 sprintf(dirfile, "%s/%s", dirName, entryp->d_name);
594                 if (unlink(dirfile)) {
595                     /* remove failed */
596                     if (errno == EACCES) {
597                         tst = ADMNOPRIV;
598                         break;
599                     }
600                     /* otherwise assume file removed by someone else or
601                      * that file is a directory, which is OK.
602                      */
603                 }
604             }
605         }
606
607         (void)closedir(dirp);
608     }
609
610     if (tst != 0) {
611         /* indicate failure */
612         rc = 0;
613     }
614     if (st != NULL) {
615         *st = tst;
616     }
617     return rc;
618 }
619
620
621 /*
622  * cfgutil_HostSetNoAuthFlag() -- set AFS server authentication flag on host
623  *
624  * RETURN CODES: 1 success, 0 failure (st indicates why)
625  */
626 int
627 cfgutil_HostSetNoAuthFlag(const cfg_host_p cfg_host, short noAuth,
628                           afs_status_p st)
629 {
630     int rc = 1;
631     afs_status_t tst = 0;
632
633     /* remote configuration not yet supported in this function */
634
635     if (!cfg_host->is_local) {
636         tst = ADMCFGNOTSUPPORTED;
637     }
638
639     /* set mode; not using bosserver because may not have necessary creds */
640
641     if (tst == 0) {
642         if (noAuth) {
643             /* set no-authentication flag */
644             int fd = open(AFSDIR_SERVER_NOAUTH_FILEPATH,
645                           O_CREAT | O_TRUNC | O_RDWR, 0666);
646
647             if (fd >= 0) {
648                 close(fd);
649             } else {
650                 if (errno == EACCES) {
651                     tst = ADMNOPRIV;
652                 } else {
653                     tst = ADMCFGHOSTSETNOAUTHFAILED;
654                 }
655             }
656         } else {
657             /* clear no-authentication flag */
658             if (unlink(AFSDIR_SERVER_NOAUTH_FILEPATH)) {
659                 if (errno != ENOENT) {
660                     if (errno == EACCES) {
661                         tst = ADMNOPRIV;
662                     } else {
663                         tst = ADMCFGHOSTSETNOAUTHFAILED;
664                     }
665                 }
666             }
667         }
668     }
669
670     if (tst != 0) {
671         /* indicate failure */
672         rc = 0;
673     }
674     if (st != NULL) {
675         *st = tst;
676     }
677     return rc;
678 }
679
680
681 /*
682  * cfgutil_Sleep() -- put thread to sleep for specified number of seconds.
683  */
684 void
685 cfgutil_Sleep(unsigned sec)
686 {
687 #ifdef AFS_NT40_ENV
688     Sleep(sec * 1000);
689 #else
690     time_t timeStart = time(NULL);
691     struct timeval sleeptime;
692
693     sleeptime.tv_sec = sec;
694     sleeptime.tv_usec = 0;
695
696     while (1) {
697         if (select(0, 0, 0, 0, &sleeptime) == 0) {
698             /* timeout */
699             break;
700         } else {
701             /* returned for reason other than timeout */
702             double cumSec = difftime(time(NULL), timeStart);
703             double remSec = (double)sec - cumSec;
704
705             if (remSec <= 0.0) {
706                 break;
707             }
708             sleeptime.tv_sec = ceil(remSec);
709         }
710     }
711 #endif
712 }
713
714
715
716 #ifdef AFS_NT40_ENV
717 /* Service control functions */
718
719 /* define generic service error codes */
720 #define CFGUTIL_SVC_NOPRIV      1       /* insufficient privilege */
721 #define CFGUTIL_SVC_BAD         2       /* service not properly configured */
722 #define CFGUTIL_SVC_NOTREADY    3       /* service not ready to accept command */
723 #define CFGUTIL_SVC_TIMEOUT     4       /* timed out waiting for stop/start */
724 #define CFGUTIL_SVC_STATUSUNK   5       /* service status cannot be determined */
725
726
727 /*
728  * ServiceCodeXlate() -- translate generic code to service-specific code
729  */
730 static afs_status_t
731 ServiceCodeXlate(LPCTSTR svcName, int code)
732 {
733     afs_status_t tst = ADMCFGNOTSUPPORTED;
734
735     if (!strcmp(svcName, AFSREG_CLT_SVC_NAME)) {
736         /* AFS client (CM) service code required */
737         switch (code) {
738         case CFGUTIL_SVC_NOPRIV:
739             tst = ADMNOPRIV;
740             break;
741         case CFGUTIL_SVC_BAD:
742             tst = ADMCFGCACHEMGRSERVICEBAD;
743             break;
744         case CFGUTIL_SVC_NOTREADY:
745             tst = ADMCFGCACHEMGRSERVICENOTREADY;
746             break;
747         case CFGUTIL_SVC_TIMEOUT:
748             tst = ADMCFGCACHEMGRSERVICETIMEOUT;
749             break;
750         default:
751             tst = ADMCFGCACHEMGRSERVICESTATUSUNK;
752             break;
753         }
754
755     } else if (!strcmp(svcName, AFSREG_SVR_SVC_NAME)) {
756         /* AFS BOS control service code required */
757         switch (code) {
758         case CFGUTIL_SVC_NOPRIV:
759             tst = ADMNOPRIV;
760             break;
761         case CFGUTIL_SVC_BAD:
762             tst = ADMCFGBOSSERVERCTLSERVICEBAD;
763             break;
764         case CFGUTIL_SVC_NOTREADY:
765             tst = ADMCFGBOSSERVERCTLSERVICENOTREADY;
766             break;
767         case CFGUTIL_SVC_TIMEOUT:
768             tst = ADMCFGBOSSERVERCTLSERVICETIMEOUT;
769             break;
770         default:
771             tst = ADMCFGBOSSERVERCTLSERVICESTATUSUNK;
772             break;
773         }
774     }
775     return tst;
776 }
777
778
779 /*
780  * cfgutil_WindowsServiceStart() -- Start a Windows service on local host.
781  *
782  *     The value of timeout specifies the maximum time, in seconds, to wait
783  *     for the service to be in the SERVICE_RUNNING state.
784  *
785  *     If service was already started/starting then *wasRunning is true (1).
786  *
787  * RETURN CODES: 1 success, 0 failure (st indicates why)
788  */
789 int
790 cfgutil_WindowsServiceStart(LPCTSTR svcName, DWORD svcArgc, LPCTSTR * svcArgv,
791                             unsigned timeout, short *wasRunning,
792                             afs_status_p st)
793 {
794     int rc = 1;
795     afs_status_t tst = 0;
796     SC_HANDLE scmHandle, svcHandle;
797     DWORD svcAccess = (SERVICE_START | SERVICE_QUERY_STATUS);
798
799     *wasRunning = 0;
800
801     if ((scmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT)) == NULL
802         || (svcHandle = OpenService(scmHandle, svcName, svcAccess)) == NULL) {
803         /* can't connect to SCM or can't open service */
804         DWORD status = GetLastError();
805
806         if (status == ERROR_ACCESS_DENIED) {
807             tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_NOPRIV);
808         } else if (status == ERROR_SERVICE_DOES_NOT_EXIST) {
809             tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_BAD);
810         } else {
811             tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_STATUSUNK);
812         }
813
814         if (scmHandle != NULL) {
815             CloseServiceHandle(scmHandle);
816         }
817
818     } else {
819         /* service configured; attempt to start */
820         if (!StartService(svcHandle, svcArgc, svcArgv)) {
821             /* service start failed */
822             DWORD status = GetLastError();
823
824             if (status == ERROR_SERVICE_ALREADY_RUNNING) {
825                 *wasRunning = 1;
826             } else {
827                 if (status == ERROR_ACCESS_DENIED) {
828                     tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_NOPRIV);
829                 } else if (status == ERROR_SERVICE_DATABASE_LOCKED
830                            || status == ERROR_SERVICE_DISABLED
831                            || status == ERROR_SERVICE_REQUEST_TIMEOUT) {
832                     tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_NOTREADY);
833                 } else {
834                     tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_BAD);
835                 }
836             }
837         }
838
839         /* wait for service to be in SERVICE_RUNNING state */
840         if (tst == 0 && timeout > 0) {
841             SERVICE_STATUS svcStatus;
842             time_t timeStart = time(NULL);
843
844             while (1) {
845                 if (!QueryServiceStatus(svcHandle, &svcStatus)) {
846                     tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_STATUSUNK);
847                     break;
848                 } else if (svcStatus.dwCurrentState == SERVICE_RUNNING) {
849                     break;
850                 } else if (difftime(time(NULL), timeStart) > timeout) {
851                     tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_TIMEOUT);
852                     break;
853                 } else {
854                     /* sleep a bit and check state again */
855                     cfgutil_Sleep(5);
856                 }
857             }
858
859             if (tst == 0) {
860                 /* wait just a bit more because we're paranoid */
861                 cfgutil_Sleep(1);
862             }
863         }
864
865         CloseServiceHandle(svcHandle);
866         CloseServiceHandle(scmHandle);
867     }
868
869     if (tst != 0) {
870         /* indicate failure */
871         rc = 0;
872     }
873     if (st != NULL) {
874         *st = tst;
875     }
876     return rc;
877 }
878
879
880 /*
881  * cfgutil_WindowsServiceStop() -- Stop a Windows service on local host.
882  *
883  *     The value of timeout specifies the maximum time, in seconds, to wait
884  *     for the service to be in the SERVICE_STOPPED state.
885  *
886  *     If service was already stopped/stopping then *wasStopped is true (1).
887  *
888  * RETURN CODES: 1 success, 0 failure (st indicates why)
889  */
890 int
891 cfgutil_WindowsServiceStop(LPCTSTR svcName, unsigned timeout,
892                            short *wasStopped, afs_status_p st)
893 {
894     int rc = 1;
895     afs_status_t tst = 0;
896     SC_HANDLE scmHandle, svcHandle;
897     DWORD svcAccess = (SERVICE_STOP | SERVICE_QUERY_STATUS);
898
899     *wasStopped = 0;
900
901     if ((scmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT)) == NULL
902         || (svcHandle = OpenService(scmHandle, svcName, svcAccess)) == NULL) {
903         /* can't connect to SCM or can't open service */
904         DWORD status = GetLastError();
905
906         if (status == ERROR_ACCESS_DENIED) {
907             tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_NOPRIV);
908         } else if (status == ERROR_SERVICE_DOES_NOT_EXIST) {
909             tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_BAD);
910         } else {
911             tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_STATUSUNK);
912         }
913
914         if (scmHandle != NULL) {
915             CloseServiceHandle(scmHandle);
916         }
917
918     } else {
919         /* service configured; attempt to stop */
920         SERVICE_STATUS svcStatus;
921
922         if (!ControlService(svcHandle, SERVICE_CONTROL_STOP, &svcStatus)) {
923             /* service stop failed */
924             DWORD status = GetLastError();
925
926             if (status == ERROR_SERVICE_NOT_ACTIVE) {
927                 *wasStopped = 1;
928             } else {
929                 if (status == ERROR_ACCESS_DENIED) {
930                     tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_NOPRIV);
931                 } else if (status == ERROR_INVALID_SERVICE_CONTROL
932                            || status == ERROR_SERVICE_CANNOT_ACCEPT_CTRL
933                            || status == ERROR_SERVICE_REQUEST_TIMEOUT) {
934                     tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_NOTREADY);
935                 } else {
936                     tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_BAD);
937                 }
938             }
939         }
940
941         /* wait for service to be in SERVICE_STOPPED state */
942         if (tst == 0 && timeout > 0) {
943             time_t timeStart = time(NULL);
944
945             while (1) {
946                 if (!QueryServiceStatus(svcHandle, &svcStatus)) {
947                     tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_STATUSUNK);
948                     break;
949                 } else if (svcStatus.dwCurrentState == SERVICE_STOPPED) {
950                     break;
951                 } else if (difftime(time(NULL), timeStart) > timeout) {
952                     tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_TIMEOUT);
953                     break;
954                 } else {
955                     /* sleep a bit and check state again */
956                     cfgutil_Sleep(5);
957                 }
958             }
959
960             if (tst == 0) {
961                 /* wait just a bit more because we're paranoid */
962                 cfgutil_Sleep(1);
963             }
964         }
965
966         CloseServiceHandle(svcHandle);
967         CloseServiceHandle(scmHandle);
968     }
969
970     if (tst != 0) {
971         /* indicate failure */
972         rc = 0;
973     }
974     if (st != NULL) {
975         *st = tst;
976     }
977     return rc;
978 }
979
980
981 /*
982  * cfgutil_WindowsServiceQuery() -- Query Windows service on local host.
983  * 
984  * RETURN CODES: 1 success, 0 failure (st indicates why)
985  */
986 int
987 cfgutil_WindowsServiceQuery(LPCTSTR svcName, DWORD * svcState,
988                             afs_status_p st)
989 {
990     int rc = 1;
991     afs_status_t tst = 0;
992     SC_HANDLE scmHandle, svcHandle;
993     DWORD svcAccess = SERVICE_QUERY_STATUS;
994
995     if ((scmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT)) == NULL
996         || (svcHandle = OpenService(scmHandle, svcName, svcAccess)) == NULL) {
997         /* can't connect to SCM or can't open service */
998         DWORD status = GetLastError();
999
1000         if (status == ERROR_ACCESS_DENIED) {
1001             tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_NOPRIV);
1002         } else if (status == ERROR_SERVICE_DOES_NOT_EXIST) {
1003             tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_BAD);
1004         } else {
1005             tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_STATUSUNK);
1006         }
1007
1008         if (scmHandle != NULL) {
1009             CloseServiceHandle(scmHandle);
1010         }
1011
1012     } else {
1013         /* service configured; determine service state */
1014         SERVICE_STATUS svcStatus;
1015
1016         if (!QueryServiceStatus(svcHandle, &svcStatus)) {
1017             tst = ServiceCodeXlate(svcName, CFGUTIL_SVC_STATUSUNK);
1018         } else {
1019             *svcState = svcStatus.dwCurrentState;
1020         }
1021
1022         CloseServiceHandle(svcHandle);
1023         CloseServiceHandle(scmHandle);
1024     }
1025
1026     if (tst != 0) {
1027         /* indicate failure */
1028         rc = 0;
1029     }
1030     if (st != NULL) {
1031         *st = tst;
1032     }
1033     return rc;
1034 }
1035
1036 #endif /* AFS_NT40_ENV */