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