c597c79d2e44ca59d0b871699802c33fec64ae01
[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 offline, mark them non-busy and start over */
138         if (errorCode == CM_ERROR_ALLOFFLINE) {
139             osi_Log0(afsd_logp, "cm_Analyze passed CM_ERROR_ALLOFFLINE.");
140             thrd_Sleep(5000);
141             /* cm_ForceUpdateVolume marks all servers as non_busy */
142             cm_ForceUpdateVolume(fidp, userp, reqp);
143             retry = 1;
144         }
145
146         /* if all servers are busy, mark them non-busy and start over */
147         if (errorCode == CM_ERROR_ALLBUSY) {
148                 cm_GetServerList(fidp, userp, reqp, &serversp);
149                 for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
150                         if (tsrp->status == busy)
151                                 tsrp->status = not_busy;
152                 }
153                 thrd_Sleep(5000);
154                 retry = 1;
155         }
156
157         /* special codes:  VBUSY and VRESTARTING */
158         if (errorCode == VBUSY || errorCode == VRESTARTING) {
159                 cm_GetServerList(fidp, userp, reqp, &serversp);
160                 for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
161                         if (tsrp->server == serverp
162                             && tsrp->status == not_busy) {
163                                 tsrp->status = busy;
164                                 break;
165                         }
166                 }
167                 retry = 1;
168         }
169
170         /* special codes:  missing volumes */
171         if (errorCode == VNOVOL || errorCode == VMOVED || errorCode == VOFFLINE
172             || errorCode == VSALVAGE || errorCode == VNOSERVICE) {
173                 long oldSum, newSum;
174                 int same;
175
176                 /* Log server being offline for this volume */
177                 osi_Log4(afsd_logp, "cm_Analyze found server %d.%d.%d.%d marked offline for a volume",
178                          ((serverp->addr.sin_addr.s_addr & 0xff)),
179                          ((serverp->addr.sin_addr.s_addr & 0xff00)>> 8),
180                          ((serverp->addr.sin_addr.s_addr & 0xff0000)>> 16),
181                          ((serverp->addr.sin_addr.s_addr & 0xff000000)>> 24));
182                 /* Create Event Log message */ 
183                 {
184                     HANDLE h;
185                     char *ptbuf[1];
186                     char s[100];
187                     h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
188                     sprintf(s, "cm_Analyze: Server %d.%d.%d.%d reported volume %d as missing.",
189                             ((serverp->addr.sin_addr.s_addr & 0xff)),
190                             ((serverp->addr.sin_addr.s_addr & 0xff00)>> 8),
191                             ((serverp->addr.sin_addr.s_addr & 0xff0000)>> 16),
192                             ((serverp->addr.sin_addr.s_addr & 0xff000000)>> 24),
193                             fidp->volume);
194                     ptbuf[0] = s;
195                     ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1009, NULL,
196                                 1, 0, ptbuf, NULL);
197                     DeregisterEventSource(h);
198                 }
199
200                 /* Mark server offline for this volume */
201                 cm_GetServerList(fidp, userp, reqp, &serversp);
202
203                 for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
204                         if (tsrp->server == serverp)
205                                 tsrp->status = offline;
206                 }
207                 retry = 1;
208         }
209
210         /* RX codes */
211         if (errorCode == RX_CALL_TIMEOUT) {
212                 /* server took longer than hardDeadTime 
213                  * don't mark server as down but don't retry
214                  * this is to prevent the SMB session from timing out
215                  * In addition, we log an event to the event log 
216                  */
217 #ifndef DJGPP
218                 HANDLE h;
219                 char *ptbuf[1];
220                 char s[100];
221                 h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
222                 sprintf(s, "cm_Analyze: HardDeadTime exceeded.");
223                 ptbuf[0] = s;
224                 ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1009, NULL,
225                         1, 0, ptbuf, NULL);
226                 DeregisterEventSource(h);
227 #endif /* !DJGPP */
228           
229                 retry = 0;
230                 osi_Log0(afsd_logp, "cm_Analyze: hardDeadTime exceeded");
231         }
232         else if (errorCode >= -64 && errorCode < 0) {
233                 /* mark server as down */
234                 lock_ObtainMutex(&serverp->mx);
235                 serverp->flags |= CM_SERVERFLAG_DOWN;
236                 lock_ReleaseMutex(&serverp->mx);
237                 retry = 1;
238         }
239
240         if (errorCode == RXKADEXPIRED && !dead_session) {
241                 lock_ObtainMutex(&userp->mx);
242                 ucellp = cm_GetUCell(userp, serverp->cellp);
243                 if (ucellp->ticketp) {
244                         free(ucellp->ticketp);
245                         ucellp->ticketp = NULL;
246                 }
247                 ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
248                 ucellp->gen++;
249                 lock_ReleaseMutex(&userp->mx);
250                 retry = 1;
251         }
252
253         if (retry && dead_session)
254                 retry = 0;
255  
256 out:
257         /* drop this on the way out */
258         if (connp)
259                 cm_PutConn(connp);
260
261         /* retry until we fail to find a connection */
262         return retry;
263 }
264
265 long cm_ConnByMServers(cm_serverRef_t *serversp, cm_user_t *usersp,
266         cm_req_t *reqp, cm_conn_t **connpp)
267 {
268         long code;
269         cm_serverRef_t *tsrp;
270         cm_server_t *tsp;
271         long firstError = 0;
272         int someBusy = 0, someOffline = 0;
273         long timeUsed, timeLeft, hardTimeLeft;
274 #ifdef DJGPP
275         struct timeval now;
276 #endif /* DJGPP */        
277
278         *connpp = NULL;
279
280 #ifndef DJGPP
281         timeUsed = (GetCurrentTime() - reqp->startTime) / 1000;
282 #else
283         gettimeofday(&now, NULL);
284         timeUsed = sub_time(now, reqp->startTime) / 1000;
285 #endif
286         
287         /* leave 5 seconds margin of safety */
288         timeLeft = RDRtimeout - timeUsed - 5;
289         hardTimeLeft = timeLeft;
290
291         /* Time enough to do an RPC? */
292         if (timeLeft < 1) {
293                 return CM_ERROR_TIMEDOUT;
294         }
295
296         lock_ObtainWrite(&cm_serverLock);
297
298         for(tsrp = serversp; tsrp; tsrp=tsrp->next) {
299                 tsp = tsrp->server;
300                 tsp->refCount++;
301                 lock_ReleaseWrite(&cm_serverLock);
302                 if (!(tsp->flags & CM_SERVERFLAG_DOWN)) {
303                         if (tsrp->status == busy)
304                                 someBusy = 1;
305                         else if (tsrp->status == offline)
306                                 someOffline = 1;
307                         else {
308                                 code = cm_ConnByServer(tsp, usersp, connpp);
309                                 if (code == 0) {
310                                         cm_PutServer(tsp);
311                                         /* Set RPC timeout */
312                                         if (timeLeft > CM_CONN_CONNDEADTIME)
313                                                 timeLeft = CM_CONN_CONNDEADTIME;
314
315                                         if (hardTimeLeft > CM_CONN_HARDDEADTIME) 
316                                                 hardTimeLeft = CM_CONN_HARDDEADTIME;
317
318                                         lock_ObtainMutex(&(*connpp)->mx);
319                                         rx_SetConnDeadTime((*connpp)->callp,
320                                                            timeLeft);
321                                         rx_SetConnHardDeadTime((*connpp)->callp, 
322                                                                (u_short) hardTimeLeft);
323                                         lock_ReleaseMutex(&(*connpp)->mx);
324
325                                         return 0;
326                                 }
327                                 if (firstError == 0) firstError = code;
328                         }
329                 }
330                 lock_ObtainWrite(&cm_serverLock);
331                 osi_assert(tsp->refCount-- > 0);
332         }
333
334         lock_ReleaseWrite(&cm_serverLock);
335         if (firstError == 0) {
336                 if (someBusy) firstError = CM_ERROR_ALLBUSY;
337                 else if (someOffline) firstError = CM_ERROR_ALLOFFLINE;
338                 else if (serversp) firstError = CM_ERROR_TIMEDOUT;
339                 /* Only return CM_ERROR_NOSUCHVOLUME if there are no
340                    servers for this volume */
341                 else firstError = CM_ERROR_NOSUCHVOLUME;
342         }
343         osi_Log1(afsd_logp, "cm_ConnByMServers returning %x", firstError);
344         return firstError;
345 }
346
347 /* called with a held server to GC all bad connections hanging off of the server */
348 void cm_GCConnections(cm_server_t *serverp)
349 {
350         cm_conn_t *tcp;
351         cm_conn_t **lcpp;
352         cm_user_t *userp;
353
354         lock_ObtainWrite(&cm_connLock);
355         lcpp = &serverp->connsp;
356         for(tcp = *lcpp; tcp; tcp = *lcpp) {
357                 userp = tcp->userp;
358                 if (userp && tcp->refCount == 0 && (userp->vcRefs == 0)) {
359                         /* do the deletion of this guy */
360                         cm_ReleaseUser(userp);
361                         *lcpp = tcp->nextp;
362                         rx_DestroyConnection(tcp->callp);
363                         lock_FinalizeMutex(&tcp->mx);
364                         free(tcp);
365                 }
366                 else {
367                         /* just advance to the next */
368                         lcpp = &tcp->nextp;
369                 }
370         }
371         lock_ReleaseWrite(&cm_connLock);
372 }
373
374 static void cm_NewRXConnection(cm_conn_t *tcp, cm_ucell_t *ucellp,
375         cm_server_t *serverp)
376 {
377         unsigned short port;
378         int serviceID;
379         int secIndex;
380         struct rx_securityClass *secObjp;
381         afs_int32 level;
382
383         if (serverp->type == CM_SERVER_VLDB) {
384                 port = htons(7003);
385                 serviceID = 52;
386         }
387         else {
388                 osi_assert(serverp->type == CM_SERVER_FILE);
389                 port = htons(7000);
390                 serviceID = 1;
391         }
392         if (ucellp->flags & CM_UCELLFLAG_RXKAD) {
393                 secIndex = 2;
394                 if (cryptall) {
395                         level = rxkad_crypt;
396                         tcp->cryptlevel = rxkad_crypt;
397                 } else {
398                         level = rxkad_clear;
399                 }
400                 secObjp = rxkad_NewClientSecurityObject(level,
401                         &ucellp->sessionKey, ucellp->kvno,
402                         ucellp->ticketLen, ucellp->ticketp);
403         }
404         else {
405                 /* normal auth */
406                 secIndex = 0;
407                 secObjp = rxnull_NewClientSecurityObject();
408         }
409         osi_assert(secObjp != NULL);
410         tcp->callp = rx_NewConnection(serverp->addr.sin_addr.s_addr,
411                 port,
412                 serviceID,
413                 secObjp,
414                 secIndex);
415         rx_SetConnDeadTime(tcp->callp, CM_CONN_CONNDEADTIME);
416         rx_SetConnHardDeadTime(tcp->callp, CM_CONN_HARDDEADTIME);
417         tcp->ucgen = ucellp->gen;
418 }
419
420 long cm_ConnByServer(cm_server_t *serverp, cm_user_t *userp, cm_conn_t **connpp)
421 {
422         cm_conn_t *tcp;
423         cm_ucell_t *ucellp;
424
425         lock_ObtainMutex(&userp->mx);
426         lock_ObtainWrite(&cm_connLock);
427         for(tcp = serverp->connsp; tcp; tcp=tcp->nextp) {
428                 if (tcp->userp == userp) break;
429         }
430         /* find ucell structure */
431         ucellp = cm_GetUCell(userp, serverp->cellp);
432         if (!tcp) {
433                 tcp = malloc(sizeof(*tcp));
434                 memset(tcp, 0, sizeof(*tcp));
435                 tcp->nextp = serverp->connsp;
436                 serverp->connsp = tcp;
437                 tcp->userp = userp;
438                 cm_HoldUser(userp);
439                 lock_InitializeMutex(&tcp->mx, "cm_conn_t mutex");
440                 tcp->serverp = serverp;
441                 tcp->cryptlevel = rxkad_clear;
442                 cm_NewRXConnection(tcp, ucellp, serverp);
443                 tcp->refCount = 1;
444         }
445         else {
446                 if ((tcp->ucgen < ucellp->gen) || (tcp->cryptlevel != cryptall))
447                 {
448                         rx_DestroyConnection(tcp->callp);
449                         cm_NewRXConnection(tcp, ucellp, serverp);
450                 }
451                 tcp->refCount++;
452         }
453         lock_ReleaseWrite(&cm_connLock);
454         lock_ReleaseMutex(&userp->mx);
455
456         /* return this pointer to our caller */
457         osi_Log1(afsd_logp, "cm_ConnByServer returning conn 0x%x", (long) tcp);
458         *connpp = tcp;
459
460         return 0;
461 }
462
463 long cm_Conn(struct cm_fid *fidp, struct cm_user *userp, cm_req_t *reqp,
464         cm_conn_t **connpp)
465 {
466         long code;
467
468         cm_serverRef_t *serversp;
469
470         code = cm_GetServerList(fidp, userp, reqp, &serversp);
471         if (code) {
472                 *connpp = NULL;
473                 return code;
474         }
475
476         code = cm_ConnByMServers(serversp, userp, reqp, connpp);
477         return code;
478 }