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