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