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