winnt-include-sanity-20030314
[openafs.git] / src / WINNT / afsd / cm_conn.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 #endif /* !DJGPP */
16 #include <string.h>
17 #include <malloc.h>
18 #include <osi.h>
19 #include <rx/rx.h>
20 #ifndef DJGPP
21 #include <rx/rxkad.h>
22 #else
23 #include <rx/rxkad.h>
24 #endif
25
26 #include "afsd.h"
27
28 osi_rwlock_t cm_connLock;
29
30 long RDRtimeout = CM_CONN_DEFAULTRDRTIMEOUT;
31
32 afs_int32 cryptall = 0;
33
34 void cm_PutConn(cm_conn_t *connp)
35 {
36         lock_ObtainWrite(&cm_connLock);
37         osi_assert(connp->refCount-- > 0);
38         lock_ReleaseWrite(&cm_connLock);
39 }
40
41 void cm_InitConn(void)
42 {
43         static osi_once_t once;
44         
45         if (osi_Once(&once)) {
46                 lock_InitializeRWLock(&cm_connLock, "connection global lock");
47                 osi_EndOnce(&once);
48         }
49 }
50
51 void cm_InitReq(cm_req_t *reqp)
52 {
53         memset((char *)reqp, 0, sizeof(cm_req_t));
54 #ifndef DJGPP
55         reqp->startTime = GetCurrentTime();
56 #else
57         gettimeofday(&reqp->startTime, NULL);
58 #endif
59  
60 }
61
62 long cm_GetServerList(struct cm_fid *fidp, struct cm_user *userp,
63         struct cm_req *reqp, cm_serverRef_t **serverspp)
64 {
65         long code;
66         cm_volume_t *volp = NULL;
67         cm_serverRef_t *serversp = NULL;
68         cm_cell_t *cellp = NULL;
69
70         if (!fidp) {
71                 *serverspp = NULL;
72                 return 0;
73         }
74
75         cellp = cm_FindCellByID(fidp->cell);
76         if (!cellp) return CM_ERROR_NOSUCHCELL;
77
78         code = cm_GetVolumeByID(cellp, fidp->volume, userp, reqp, &volp);
79         if (code) return code;
80         
81         if (fidp->volume == volp->rwID)
82                 serversp = volp->rwServersp;
83         else if (fidp->volume == volp->roID)
84                 serversp = volp->roServersp;
85         else if (fidp->volume == volp->bkID)
86                 serversp = volp->bkServersp;
87         else
88                 serversp = NULL;
89
90         cm_PutVolume(volp);
91         *serverspp = serversp;
92         return 0;
93 }
94
95 /*
96  * Analyze the error return from an RPC.  Determine whether or not to retry,
97  * and if we're going to retry, determine whether failover is appropriate,
98  * and whether timed backoff is appropriate.
99  *
100  * If the error code is from cm_Conn() or friends, it will be a CM_ERROR code.
101  * Otherwise it will be an RPC code.  This may be a UNIX code (e.g. EDQUOT), or
102  * it may be an RX code, or it may be a special code (e.g. VNOVOL), or it may
103  * be a security code (e.g. RXKADEXPIRED).
104  *
105  * If the error code is from cm_Conn() or friends, connp will be NULL.
106  *
107  * For VLDB calls, fidp will be NULL.
108  *
109  * volSyncp and/or cbrp may also be NULL.
110  */
111 cm_Analyze(cm_conn_t *connp, cm_user_t *userp, cm_req_t *reqp,
112         struct cm_fid *fidp,
113         AFSVolSync *volSyncp, cm_callbackRequest_t *cbrp, long errorCode)
114 {
115         cm_server_t *serverp;
116         cm_serverRef_t *serversp, *tsrp;
117         cm_ucell_t *ucellp;
118         int retry = 0;
119         int dead_session;
120         
121         osi_Log2(afsd_logp, "cm_Analyze connp 0x%x, code %d",
122                  (long) connp, errorCode);
123
124         /* no locking required, since connp->serverp never changes after
125          * creation */
126         dead_session = (userp->cellInfop == NULL);
127         if (connp)
128                 serverp = connp->serverp;
129
130         /* Update callback pointer */
131         if (cbrp && errorCode == 0) cbrp->serverp = connp->serverp;
132
133         /* If not allowed to retry, don't */
134         if (reqp->flags & CM_REQ_NORETRY)
135                 goto out;
136
137         /* if all servers are busy, mark them non-busy and start over */
138         if (errorCode == CM_ERROR_ALLBUSY) {
139                 cm_GetServerList(fidp, userp, reqp, &serversp);
140                 for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
141                         if (tsrp->status == busy)
142                                 tsrp->status = not_busy;
143                 }
144                 thrd_Sleep(5000);
145                 retry = 1;
146         }
147
148         /* special codes:  VBUSY and VRESTARTING */
149         if (errorCode == VBUSY || errorCode == VRESTARTING) {
150                 cm_GetServerList(fidp, userp, reqp, &serversp);
151                 for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
152                         if (tsrp->server == serverp
153                             && tsrp->status == not_busy) {
154                                 tsrp->status = busy;
155                                 break;
156                         }
157                 }
158                 retry = 1;
159         }
160
161         /* special codes:  missing volumes */
162         if (errorCode == VNOVOL || errorCode == VMOVED || errorCode == VOFFLINE
163             || errorCode == VSALVAGE || errorCode == VNOSERVICE) {
164                 long oldSum, newSum;
165                 int same;
166
167                 /* Back off to allow move to complete */
168                 thrd_Sleep(2000);
169
170                 /* Update the volume location and see if it changed */
171                 cm_GetServerList(fidp, userp, reqp, &serversp);
172                 oldSum = cm_ChecksumServerList(serversp);
173                 cm_ForceUpdateVolume(fidp, userp, reqp);
174                 cm_GetServerList(fidp, userp, reqp, &serversp);
175                 newSum = cm_ChecksumServerList(serversp);
176                 same = (oldSum == newSum);
177
178                 /* mark servers as appropriate */
179                 for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
180                         if (tsrp->server == serverp)
181                                 tsrp->status = offline;
182                         else if (!same)
183                                 tsrp->status = not_busy;
184                 }
185                 retry = 1;
186         }
187
188         /* RX codes */
189         if (errorCode == RX_CALL_TIMEOUT) {
190                 /* server took longer than hardDeadTime 
191                  * don't mark server as down but don't retry
192                  * this is to prevent the SMB session from timing out
193                  * In addition, we log an event to the event log 
194                  */
195 #ifndef DJGPP
196                 HANDLE h;
197                 char *ptbuf[1];
198                 char s[100];
199                 h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
200                 sprintf(s, "cm_Analyze: HardDeadTime exceeded.");
201                 ptbuf[0] = s;
202                 ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1009, NULL,
203                         1, 0, ptbuf, NULL);
204                 DeregisterEventSource(h);
205 #endif /* !DJGPP */
206           
207                 retry = 0;
208                 osi_Log0(afsd_logp, "cm_Analyze: hardDeadTime exceeded");
209         }
210         else if (errorCode >= -64 && errorCode < 0) {
211                 /* mark server as down */
212                 lock_ObtainMutex(&serverp->mx);
213                 serverp->flags |= CM_SERVERFLAG_DOWN;
214                 lock_ReleaseMutex(&serverp->mx);
215                 retry = 1;
216         }
217
218         if (errorCode == RXKADEXPIRED && !dead_session) {
219                 lock_ObtainMutex(&userp->mx);
220                 ucellp = cm_GetUCell(userp, serverp->cellp);
221                 if (ucellp->ticketp) {
222                         free(ucellp->ticketp);
223                         ucellp->ticketp = NULL;
224                 }
225                 ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
226                 ucellp->gen++;
227                 lock_ReleaseMutex(&userp->mx);
228                 retry = 1;
229         }
230
231         if (retry && dead_session)
232                 retry = 0;
233  
234 out:
235         /* drop this on the way out */
236         if (connp)
237                 cm_PutConn(connp);
238
239         /* retry until we fail to find a connection */
240         return retry;
241 }
242
243 long cm_ConnByMServers(cm_serverRef_t *serversp, cm_user_t *usersp,
244         cm_req_t *reqp, cm_conn_t **connpp)
245 {
246         long code;
247         cm_serverRef_t *tsrp;
248         cm_server_t *tsp;
249         long firstError = 0;
250         int someBusy = 0, someOffline = 0;
251         long timeUsed, timeLeft, hardTimeLeft;
252 #ifdef DJGPP
253         struct timeval now;
254 #endif /* DJGPP */        
255
256         *connpp = NULL;
257
258 #ifndef DJGPP
259         timeUsed = (GetCurrentTime() - reqp->startTime) / 1000;
260 #else
261         gettimeofday(&now, NULL);
262         timeUsed = sub_time(now, reqp->startTime) / 1000;
263 #endif
264         
265         /* leave 5 seconds margin of safety */
266         timeLeft = RDRtimeout - timeUsed - 5;
267         hardTimeLeft = timeLeft;
268
269         /* Time enough to do an RPC? */
270         if (timeLeft < 1) {
271                 return CM_ERROR_TIMEDOUT;
272         }
273
274         lock_ObtainWrite(&cm_serverLock);
275
276         for(tsrp = serversp; tsrp; tsrp=tsrp->next) {
277                 tsp = tsrp->server;
278                 tsp->refCount++;
279                 lock_ReleaseWrite(&cm_serverLock);
280                 if (!(tsp->flags & CM_SERVERFLAG_DOWN)) {
281                         if (tsrp->status == busy)
282                                 someBusy = 1;
283                         else if (tsrp->status == offline)
284                                 someOffline = 1;
285                         else {
286                                 code = cm_ConnByServer(tsp, usersp, connpp);
287                                 if (code == 0) {
288                                         cm_PutServer(tsp);
289                                         /* Set RPC timeout */
290                                         if (timeLeft > CM_CONN_CONNDEADTIME)
291                                                 timeLeft = CM_CONN_CONNDEADTIME;
292
293                                         if (hardTimeLeft > CM_CONN_HARDDEADTIME) 
294                                                 hardTimeLeft = CM_CONN_HARDDEADTIME;
295
296                                         lock_ObtainMutex(&(*connpp)->mx);
297                                         rx_SetConnDeadTime((*connpp)->callp,
298                                                            timeLeft);
299                                         rx_SetConnHardDeadTime((*connpp)->callp, 
300                                                                (u_short) hardTimeLeft);
301                                         lock_ReleaseMutex(&(*connpp)->mx);
302
303                                         return 0;
304                                 }
305                                 if (firstError == 0) firstError = code;
306                         }
307                 }
308                 lock_ObtainWrite(&cm_serverLock);
309                 osi_assert(tsp->refCount-- > 0);
310         }
311
312         lock_ReleaseWrite(&cm_serverLock);
313         if (firstError == 0) {
314                 if (someBusy) firstError = CM_ERROR_ALLBUSY;
315                 else if (someOffline) firstError = CM_ERROR_NOSUCHVOLUME;
316                 else firstError = CM_ERROR_TIMEDOUT;
317         }
318         osi_Log1(afsd_logp, "cm_ConnByMServers returning %x", firstError);
319         return firstError;
320 }
321
322 /* called with a held server to GC all bad connections hanging off of the server */
323 void cm_GCConnections(cm_server_t *serverp)
324 {
325         cm_conn_t *tcp;
326         cm_conn_t **lcpp;
327         cm_user_t *userp;
328
329         lock_ObtainWrite(&cm_connLock);
330         lcpp = &serverp->connsp;
331         for(tcp = *lcpp; tcp; tcp = *lcpp) {
332                 userp = tcp->userp;
333                 if (userp && tcp->refCount == 0 && (userp->vcRefs == 0)) {
334                         /* do the deletion of this guy */
335                         cm_ReleaseUser(userp);
336                         *lcpp = tcp->nextp;
337                         rx_DestroyConnection(tcp->callp);
338                         lock_FinalizeMutex(&tcp->mx);
339                         free(tcp);
340                 }
341                 else {
342                         /* just advance to the next */
343                         lcpp = &tcp->nextp;
344                 }
345         }
346         lock_ReleaseWrite(&cm_connLock);
347 }
348
349 static void cm_NewRXConnection(cm_conn_t *tcp, cm_ucell_t *ucellp,
350         cm_server_t *serverp)
351 {
352         unsigned short port;
353         int serviceID;
354         int secIndex;
355         struct rx_securityClass *secObjp;
356         afs_int32 level;
357
358         if (serverp->type == CM_SERVER_VLDB) {
359                 port = htons(7003);
360                 serviceID = 52;
361         }
362         else {
363                 osi_assert(serverp->type == CM_SERVER_FILE);
364                 port = htons(7000);
365                 serviceID = 1;
366         }
367         if (ucellp->flags & CM_UCELLFLAG_RXKAD) {
368                 secIndex = 2;
369                 if (cryptall) {
370                         level = rxkad_crypt;
371                         tcp->cryptlevel = rxkad_crypt;
372                 } else {
373                         level = rxkad_clear;
374                 }
375                 secObjp = rxkad_NewClientSecurityObject(level,
376                         &ucellp->sessionKey, ucellp->kvno,
377                         ucellp->ticketLen, ucellp->ticketp);
378         }
379         else {
380                 /* normal auth */
381                 secIndex = 0;
382                 secObjp = rxnull_NewClientSecurityObject();
383         }
384         osi_assert(secObjp != NULL);
385         tcp->callp = rx_NewConnection(serverp->addr.sin_addr.s_addr,
386                 port,
387                 serviceID,
388                 secObjp,
389                 secIndex);
390         rx_SetConnDeadTime(tcp->callp, CM_CONN_CONNDEADTIME);
391         rx_SetConnHardDeadTime(tcp->callp, CM_CONN_HARDDEADTIME);
392         tcp->ucgen = ucellp->gen;
393 }
394
395 long cm_ConnByServer(cm_server_t *serverp, cm_user_t *userp, cm_conn_t **connpp)
396 {
397         cm_conn_t *tcp;
398         cm_ucell_t *ucellp;
399
400         lock_ObtainMutex(&userp->mx);
401         lock_ObtainWrite(&cm_connLock);
402         for(tcp = serverp->connsp; tcp; tcp=tcp->nextp) {
403                 if (tcp->userp == userp) break;
404         }
405         /* find ucell structure */
406         ucellp = cm_GetUCell(userp, serverp->cellp);
407         if (!tcp) {
408                 tcp = malloc(sizeof(*tcp));
409                 memset(tcp, 0, sizeof(*tcp));
410                 tcp->nextp = serverp->connsp;
411                 serverp->connsp = tcp;
412                 tcp->userp = userp;
413                 cm_HoldUser(userp);
414                 lock_InitializeMutex(&tcp->mx, "cm_conn_t mutex");
415                 tcp->serverp = serverp;
416                 tcp->cryptlevel = rxkad_clear;
417                 cm_NewRXConnection(tcp, ucellp, serverp);
418                 tcp->refCount = 1;
419         }
420         else {
421                 if ((tcp->ucgen < ucellp->gen) || (tcp->cryptlevel != cryptall))
422                 {
423                         rx_DestroyConnection(tcp->callp);
424                         cm_NewRXConnection(tcp, ucellp, serverp);
425                 }
426                 tcp->refCount++;
427         }
428         lock_ReleaseWrite(&cm_connLock);
429         lock_ReleaseMutex(&userp->mx);
430
431         /* return this pointer to our caller */
432         osi_Log1(afsd_logp, "cm_ConnByServer returning conn 0x%x", (long) tcp);
433         *connpp = tcp;
434
435         return 0;
436 }
437
438 long cm_Conn(struct cm_fid *fidp, struct cm_user *userp, cm_req_t *reqp,
439         cm_conn_t **connpp)
440 {
441         long code;
442
443         cm_serverRef_t *serversp;
444
445         code = cm_GetServerList(fidp, userp, reqp, &serversp);
446         if (code) {
447                 *connpp = NULL;
448                 return code;
449         }
450
451         code = cm_ConnByMServers(serversp, userp, reqp, connpp);
452         return code;
453 }