08918369b3b7b24da167af43c1387429afdc517a
[openafs.git] / src / WINNT / afsd / cm_daemon.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 <afs/param.h>
11 #include <afs/stds.h>
12
13 #ifndef DJGPP
14 #include <windows.h>
15 #include <winsock2.h>
16 #include <iphlpapi.h>
17 #else
18 #include <netdb.h>
19 #endif /* !DJGPP */
20 #include <stdlib.h>
21 #include <malloc.h>
22 #include <string.h>
23
24 #include <rx/rx.h>
25 #include <rx/rx_prototypes.h>
26 #include <WINNT/afsreg.h>
27
28 #include "afsd.h"
29 #include "afsicf.h"
30
31 /* in seconds */
32 long cm_daemonCheckDownInterval  = 180;
33 long cm_daemonCheckUpInterval    = 600;
34 long cm_daemonCheckVolInterval   = 3600;
35 long cm_daemonCheckCBInterval    = 60;
36 long cm_daemonCheckLockInterval  = 60;
37 long cm_daemonTokenCheckInterval = 180;
38
39 osi_rwlock_t cm_daemonLock;
40
41 long cm_bkgQueueCount;          /* # of queued requests */
42
43 int cm_bkgWaitingForCount;      /* true if someone's waiting for cm_bkgQueueCount to drop */
44
45 cm_bkgRequest_t *cm_bkgListp;           /* first elt in the list of requests */
46 cm_bkgRequest_t *cm_bkgListEndp;        /* last elt in the list of requests */
47
48 static int daemon_ShutdownFlag = 0;
49
50 #ifndef DJGPP
51 void cm_IpAddrDaemon(long parm)
52 {
53     extern void smb_CheckVCs(void);
54
55     rx_StartClientThread();
56
57     while (daemon_ShutdownFlag == 0) {
58         DWORD Result = NotifyAddrChange(NULL,NULL);
59         if (Result == NO_ERROR && daemon_ShutdownFlag == 0) {
60             osi_Log0(afsd_logp, "cm_IpAddrDaemon CheckDownServers");
61             Sleep(2500);
62             cm_CheckServers(CM_FLAG_CHECKDOWNSERVERS, NULL);
63             smb_CheckVCs();
64         }       
65     }
66 }
67 #endif
68
69 void cm_BkgDaemon(long parm)
70 {
71     cm_bkgRequest_t *rp;
72
73     rx_StartClientThread();
74
75     lock_ObtainWrite(&cm_daemonLock);
76     while (daemon_ShutdownFlag == 0) {
77         if (!cm_bkgListEndp) {
78             osi_SleepW((LONG_PTR)&cm_bkgListp, &cm_daemonLock);
79             lock_ObtainWrite(&cm_daemonLock);
80             continue;
81         }
82                 
83         /* we found a request */
84         rp = cm_bkgListEndp;
85         cm_bkgListEndp = (cm_bkgRequest_t *) osi_QPrev(&rp->q);
86         osi_QRemove((osi_queue_t **) &cm_bkgListp, &rp->q);
87         osi_assert(cm_bkgQueueCount-- > 0);
88         lock_ReleaseWrite(&cm_daemonLock);
89
90         (*rp->procp)(rp->scp, rp->p1, rp->p2, rp->p3, rp->p4, rp->userp);
91                 
92         cm_ReleaseUser(rp->userp);
93         cm_ReleaseSCache(rp->scp);
94         free(rp);
95
96         lock_ObtainWrite(&cm_daemonLock);
97     }
98     lock_ReleaseWrite(&cm_daemonLock);
99 }
100
101 void cm_QueueBKGRequest(cm_scache_t *scp, cm_bkgProc_t *procp, long p1, long p2, long p3, long p4,
102         cm_user_t *userp)
103 {
104     cm_bkgRequest_t *rp;
105         
106     rp = malloc(sizeof(*rp));
107     memset(rp, 0, sizeof(*rp));
108         
109     cm_HoldSCache(scp);
110     rp->scp = scp;
111     cm_HoldUser(userp);
112     rp->userp = userp;
113     rp->procp = procp;
114     rp->p1 = p1;
115     rp->p2 = p2;
116     rp->p3 = p3;
117     rp->p4 = p4;
118
119     lock_ObtainWrite(&cm_daemonLock);
120     cm_bkgQueueCount++;
121     osi_QAdd((osi_queue_t **) &cm_bkgListp, &rp->q);
122     if (!cm_bkgListEndp) 
123         cm_bkgListEndp = rp;
124     lock_ReleaseWrite(&cm_daemonLock);
125
126     osi_Wakeup((LONG_PTR) &cm_bkgListp);
127 }
128
129 static int
130 IsWindowsFirewallPresent(void)
131 {
132     SC_HANDLE scm;
133     SC_HANDLE svc;
134     BOOLEAN flag;
135     BOOLEAN result = FALSE;
136     LPQUERY_SERVICE_CONFIG pConfig = NULL;
137     DWORD BufSize;
138     LONG status;
139
140     /* Open services manager */
141     scm = OpenSCManager(NULL, NULL, GENERIC_READ);
142     if (!scm) return FALSE;
143
144     /* Open Windows Firewall service */
145     svc = OpenService(scm, "SharedAccess", SERVICE_QUERY_CONFIG);
146     if (!svc)
147         goto close_scm;
148
149     /* Query Windows Firewall service config, first just to get buffer size */
150     /* Expected to fail, so don't test return value */
151     (void) QueryServiceConfig(svc, NULL, 0, &BufSize);
152     status = GetLastError();
153     if (status != ERROR_INSUFFICIENT_BUFFER)
154         goto close_svc;
155
156     /* Allocate buffer */
157     pConfig = (LPQUERY_SERVICE_CONFIG)GlobalAlloc(GMEM_FIXED,BufSize);
158     if (!pConfig)
159         goto close_svc;
160
161     /* Query Windows Firewall service config, this time for real */
162     flag = QueryServiceConfig(svc, pConfig, BufSize, &BufSize);
163     if (!flag)
164         goto free_pConfig;
165
166     /* Is it autostart? */
167     if (pConfig->dwStartType < SERVICE_DEMAND_START)
168         result = TRUE;
169
170   free_pConfig:
171     GlobalFree(pConfig);
172   close_svc:
173     CloseServiceHandle(svc);
174   close_scm:
175     CloseServiceHandle(scm);
176
177     return result;
178 }
179
180 void
181 cm_DaemonCheckInit(void)
182 {
183     HKEY parmKey;
184     DWORD dummyLen;
185     DWORD dummy;
186     DWORD code;
187
188     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
189                          0, KEY_QUERY_VALUE, &parmKey);
190     if (code)
191         return;
192
193     dummyLen = sizeof(DWORD);
194     code = RegQueryValueEx(parmKey, "DownServerCheckInterval", NULL, NULL,
195                             (BYTE *) &dummy, &dummyLen);
196     if (code == ERROR_SUCCESS)
197         cm_daemonCheckDownInterval = dummy;
198     
199     dummyLen = sizeof(DWORD);
200     code = RegQueryValueEx(parmKey, "UpServerCheckInterval", NULL, NULL,
201                             (BYTE *) &dummy, &dummyLen);
202     if (code == ERROR_SUCCESS)
203         cm_daemonCheckUpInterval = dummy;
204     
205     dummyLen = sizeof(DWORD);
206     code = RegQueryValueEx(parmKey, "VolumeCheckInterval", NULL, NULL,
207                             (BYTE *) &dummy, &dummyLen);
208     if (code == ERROR_SUCCESS)
209         cm_daemonCheckVolInterval = dummy;
210     
211     dummyLen = sizeof(DWORD);
212     code = RegQueryValueEx(parmKey, "CallbackCheckInterval", NULL, NULL,
213                             (BYTE *) &dummy, &dummyLen);
214     if (code == ERROR_SUCCESS)
215         cm_daemonCheckCBInterval = dummy;
216     
217     dummyLen = sizeof(DWORD);
218     code = RegQueryValueEx(parmKey, "LockCheckInterval", NULL, NULL,
219                             (BYTE *) &dummy, &dummyLen);
220     if (code == ERROR_SUCCESS)
221         cm_daemonCheckLockInterval = dummy;
222     
223     dummyLen = sizeof(DWORD);
224     code = RegQueryValueEx(parmKey, "TokenCheckInterval", NULL, NULL,
225                             (BYTE *) &dummy, &dummyLen);
226     if (code == ERROR_SUCCESS)
227         cm_daemonTokenCheckInterval = dummy;
228     
229     RegCloseKey(parmKey);
230 }
231
232 /* periodic check daemon */
233 void cm_Daemon(long parm)
234 {
235     time_t now;
236     time_t lastLockCheck;
237     time_t lastVolCheck;
238     time_t lastCBExpirationCheck;
239     time_t lastDownServerCheck;
240     time_t lastUpServerCheck;
241     time_t lastTokenCacheCheck;
242     char thostName[200];
243     unsigned long code;
244     struct hostent *thp;
245     HMODULE hHookDll;
246     int configureFirewall = IsWindowsFirewallPresent();
247
248     /* ping all file servers, up or down, with unauthenticated connection,
249      * to find out whether we have all our callbacks from the server still.
250      * Also, ping down VLDBs.
251      */
252     /*
253      * Seed the random number generator with our own address, so that
254      * clients starting at the same time don't all do vol checks at the
255      * same time.
256      */
257     gethostname(thostName, sizeof(thostName));
258     thp = gethostbyname(thostName);
259     if (thp == NULL)    /* In djgpp, gethostname returns the netbios
260                            name of the machine.  gethostbyname will fail
261                            looking this up if it differs from DNS name. */
262         code = 0;
263     else
264         memcpy(&code, thp->h_addr_list[0], 4);
265     
266     srand(ntohl(code));
267
268     cm_DaemonCheckInit();
269
270     now = osi_Time();
271     lastVolCheck = now - cm_daemonCheckVolInterval/2 + (rand() % cm_daemonCheckVolInterval);
272     lastCBExpirationCheck = now - cm_daemonCheckCBInterval/2 + (rand() % cm_daemonCheckCBInterval);
273     lastLockCheck = now - cm_daemonCheckLockInterval/2 + (rand() % cm_daemonCheckLockInterval);
274     lastDownServerCheck = now - cm_daemonCheckDownInterval/2 + (rand() % cm_daemonCheckDownInterval);
275     lastUpServerCheck = now - cm_daemonCheckUpInterval/2 + (rand() % cm_daemonCheckUpInterval);
276     lastTokenCacheCheck = now - cm_daemonTokenCheckInterval/2 + (rand() % cm_daemonTokenCheckInterval);
277
278     while (daemon_ShutdownFlag == 0) {
279         thrd_Sleep(30 * 1000);          /* sleep 30 seconds */
280         if (daemon_ShutdownFlag == 1)
281             return;
282
283         if (configureFirewall) {
284             /* Open Microsoft Firewall to allow in port 7001 */
285             switch (icf_CheckAndAddAFSPorts(AFS_PORTSET_CLIENT)) {
286             case 0:
287                 afsi_log("Windows Firewall Configuration succeeded");
288                 configureFirewall = 0;
289                 break;
290             case 1:
291                 afsi_log("Invalid Windows Firewall Port Set");
292                 break;
293             case 2:
294                 afsi_log("Unable to open Windows Firewall Profile");
295                 break;
296             case 3:
297                 afsi_log("Unable to create/modify Windows Firewall Port entries");
298                 break;
299             default:
300                 afsi_log("Unknown Windows Firewall Configuration error");
301             }
302         }
303
304         /* find out what time it is */
305         now = osi_Time();
306
307         /* check down servers */
308         if (now > lastDownServerCheck + cm_daemonCheckDownInterval) {
309             lastDownServerCheck = now;
310             osi_Log0(afsd_logp, "cm_Daemon CheckDownServers");
311             cm_CheckServers(CM_FLAG_CHECKDOWNSERVERS, NULL);
312             now = osi_Time();
313         }
314
315         /* check up servers */
316         if (now > lastUpServerCheck + cm_daemonCheckUpInterval) {
317             lastUpServerCheck = now;
318             osi_Log0(afsd_logp, "cm_Daemon CheckUpServers");
319             cm_CheckServers(CM_FLAG_CHECKUPSERVERS, NULL);
320             now = osi_Time();
321         }
322
323         if (now > lastVolCheck + cm_daemonCheckVolInterval) {
324             lastVolCheck = now;
325             cm_CheckVolumes();
326             now = osi_Time();
327         }
328
329         if (now > lastCBExpirationCheck + cm_daemonCheckCBInterval) {
330             lastCBExpirationCheck = now;
331             cm_CheckCBExpiration();
332             now = osi_Time();
333         }
334
335         if (now > lastLockCheck + cm_daemonCheckLockInterval) {
336             lastLockCheck = now;
337             cm_CheckLocks();
338             now = osi_Time();
339         }
340
341         if (now > lastTokenCacheCheck + cm_daemonTokenCheckInterval) {
342             lastTokenCacheCheck = now;
343             cm_CheckTokenCache(now);
344             now = osi_Time();
345         }
346
347         /* allow an exit to be called prior to stopping the service */
348         hHookDll = LoadLibrary(AFSD_HOOK_DLL);
349         if (hHookDll)
350         {
351             BOOL hookRc = TRUE;
352             AfsdDaemonHook daemonHook = ( AfsdDaemonHook ) GetProcAddress(hHookDll, AFSD_DAEMON_HOOK);
353             if (daemonHook)
354             {
355                 hookRc = daemonHook();
356             }
357             FreeLibrary(hHookDll);
358             hHookDll = NULL;
359
360             if (hookRc == FALSE)
361             {
362                 SetEvent(WaitToTerminate);
363             }
364         }
365     }
366 }       
367
368 void cm_DaemonShutdown(void)
369 {
370     daemon_ShutdownFlag = 1;
371 }
372
373 void cm_InitDaemon(int nDaemons)
374 {
375     static osi_once_t once;
376     long pid;
377     thread_t phandle;
378     int i;
379         
380     if (osi_Once(&once)) {
381         lock_InitializeRWLock(&cm_daemonLock, "cm_daemonLock");
382         osi_EndOnce(&once);
383
384 #ifndef DJGPP
385         /* creating IP Address Change monitor daemon */
386         phandle = thrd_Create((SecurityAttrib) 0, 0,
387                                (ThreadFunc) cm_IpAddrDaemon, 0, 0, &pid, "cm_IpAddrDaemon");
388         osi_assert(phandle != NULL);
389         thrd_CloseHandle(phandle);
390 #endif /* DJGPP */
391
392         /* creating pinging daemon */
393         phandle = thrd_Create((SecurityAttrib) 0, 0,
394                                (ThreadFunc) cm_Daemon, 0, 0, &pid, "cm_Daemon");
395         osi_assert(phandle != NULL);
396         thrd_CloseHandle(phandle);
397
398         for(i=0; i < nDaemons; i++) {
399             phandle = thrd_Create((SecurityAttrib) 0, 0,
400                                    (ThreadFunc) cm_BkgDaemon, 0, 0, &pid,
401                                    "cm_BkgDaemon");
402             osi_assert(phandle != NULL);
403             thrd_CloseHandle(phandle);
404         }
405     }
406 }