d22f2abe749effcca5ec96ae4b9e9e5997c0a3e7
[openafs.git] / src / afs / afs_user.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 /*
11  * Implements:
12  */
13 #include <afsconfig.h>
14 #include "afs/param.h"
15
16
17 #include "afs/stds.h"
18 #include "afs/sysincludes.h"    /* Standard vendor system headers */
19
20 #if !defined(UKERNEL)
21 #if !defined(AFS_LINUX20_ENV)
22 #include <net/if.h>
23 #endif
24 #include <netinet/in.h>
25
26 #ifdef AFS_SGI62_ENV
27 #include "h/hashing.h"
28 #endif
29 #if !defined(AFS_HPUX110_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN_ENV)
30 #include <netinet/in_var.h>
31 #endif /* ! AFS_HPUX110_ENV */
32 #endif /* !defined(UKERNEL) */
33
34 #include "afsincludes.h"        /* Afs-based standard headers */
35 #include "afs/afs_stats.h"      /* afs statistics */
36
37 #if     defined(AFS_SUN56_ENV)
38 #include <inet/led.h>
39 #include <inet/common.h>
40 #if   defined(AFS_SUN58_ENV)
41 #include <netinet/ip6.h>
42 #endif
43 #include <inet/ip.h>
44 #endif
45
46
47 /* Exported variables */
48 afs_rwlock_t afs_xuser;
49 struct unixuser *afs_users[NUSERS];
50
51
52 #ifndef AFS_PAG_MANAGER
53 /* Forward declarations */
54 void afs_ResetAccessCache(afs_int32 uid, int alock);
55
56 /*
57  * Called with afs_xuser, afs_xserver and afs_xconn locks held, to delete
58  * appropriate conn structures for au
59  */
60 static void
61 RemoveUserConns(struct unixuser *au)
62 {
63     int i;
64     struct server *ts;
65     struct srvAddr *sa;
66     struct afs_conn *tc, **lc;
67
68     AFS_STATCNT(RemoveUserConns);
69     for (i = 0; i < NSERVERS; i++) {
70         for (ts = afs_servers[i]; ts; ts = ts->next) {
71             for (sa = ts->addr; sa; sa = sa->next_sa) {
72                 lc = &sa->conns;
73                 for (tc = *lc; tc; lc = &tc->next, tc = *lc) {
74                     if (tc->user == au && tc->refCount == 0) {
75                         *lc = tc->next;
76                         AFS_GUNLOCK();
77                         rx_DestroyConnection(tc->id);
78                         AFS_GLOCK();
79                         afs_osi_Free(tc, sizeof(struct afs_conn));
80                         break;  /* at most one instance per server */
81                     }           /*Found unreferenced connection for user */
82                 }               /*For each connection on the server */
83             }
84         }                       /*For each server on chain */
85     }                           /*For each chain */
86
87 }                               /*RemoveUserConns */
88 #endif /* !AFS_PAG_MANAGER */
89
90
91 /* Called from afs_Daemon to garbage collect unixusers no longer using system,
92  * and their conns.  The aforce parameter tells the function to flush all
93  * *unauthenticated* conns, no matter what their expiration time; it exists
94  * because after we choose our final rx epoch, we want to stop using calls with
95  * other epochs as soon as possible (old file servers act bizarrely when they
96  * see epoch changes).
97  */
98 void
99 afs_GCUserData(int aforce)
100 {
101     struct unixuser *tu, **lu, *nu;
102     int i;
103     afs_int32 now, delFlag;
104
105     AFS_STATCNT(afs_GCUserData);
106     /* Obtain locks in valid order */
107     ObtainWriteLock(&afs_xuser, 95);
108 #ifndef AFS_PAG_MANAGER
109     ObtainReadLock(&afs_xserver);
110     ObtainWriteLock(&afs_xconn, 96);
111 #endif
112     now = osi_Time();
113     for (i = 0; i < NUSERS; i++) {
114         for (lu = &afs_users[i], tu = *lu; tu; tu = nu) {
115             delFlag = 0;        /* should we delete this dude? */
116             /* Don't garbage collect users in use now (refCount) */
117             if (tu->refCount == 0) {
118                 if (tu->tokens) {
119                     /* Need to walk the token stack, and dispose of
120                      * all expired tokens */
121                     afs_DiscardExpiredTokens(&tu->tokens, now);
122                     if (!afs_HasUsableTokens(tu->tokens, now))
123                         delFlag = 1;
124                 } else {
125                     if (aforce || (tu->tokenTime < now - NOTOKTIMEOUT))
126                         delFlag = 1;
127                 }
128             }
129             nu = tu->next;
130             if (delFlag) {
131                 *lu = tu->next;
132 #ifndef AFS_PAG_MANAGER
133                 RemoveUserConns(tu);
134 #endif
135                 afs_FreeTokens(&tu->tokens);
136
137                 if (tu->exporter)
138                     EXP_RELE(tu->exporter);
139                 afs_osi_Free(tu, sizeof(struct unixuser));
140             } else {
141                 lu = &tu->next;
142             }
143         }
144     }
145 #ifndef AFS_PAG_MANAGER
146     ReleaseWriteLock(&afs_xconn);
147 #endif
148 #ifndef AFS_PAG_MANAGER
149     ReleaseReadLock(&afs_xserver);
150 #endif
151     ReleaseWriteLock(&afs_xuser);
152
153 }                               /*afs_GCUserData */
154
155
156 #ifndef AFS_PAG_MANAGER
157 /*
158  * Check for unixusers who encountered bad tokens, and reset the access
159  * cache for these guys.  Can't do this when token expiration detected,
160  * since too many locks are set then.
161  */
162 void
163 afs_CheckTokenCache(void)
164 {
165     int i;
166     struct unixuser *tu;
167     afs_int32 now;
168
169     AFS_STATCNT(afs_CheckCacheResets);
170     ObtainReadLock(&afs_xvcache);
171     ObtainReadLock(&afs_xuser);
172     now = osi_Time();
173     for (i = 0; i < NUSERS; i++) {
174         for (tu = afs_users[i]; tu; tu = tu->next) {
175             afs_int32 uid;
176
177             /*
178              * If tokens are still good and user has Kerberos tickets,
179              * check expiration
180              */
181             if ((tu->states & UHasTokens) && !(tu->states & UTokensBad)) {
182                 if (!afs_HasUsableTokens(tu->tokens, now)) {
183                     /*
184                      * This token has expired, warn users and reset access
185                      * cache.
186                      */
187                     tu->states |= (UTokensBad | UNeedsReset);
188                 }
189             }
190             if (tu->states & UNeedsReset) {
191                 tu->states &= ~UNeedsReset;
192                 uid = tu->uid;
193                 afs_ResetAccessCache(uid, 0);
194             }
195         }
196     }
197     ReleaseReadLock(&afs_xuser);
198     ReleaseReadLock(&afs_xvcache);
199 }                               /*afs_CheckTokenCache */
200
201
202 void
203 afs_ResetAccessCache(afs_int32 uid, int alock)
204 {
205     int i;
206     struct vcache *tvc;
207     struct axscache *ac;
208
209     AFS_STATCNT(afs_ResetAccessCache);
210     if (alock)
211         ObtainReadLock(&afs_xvcache);
212     for (i = 0; i < VCSIZE; i++) {
213         for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
214             /* really should do this under cache write lock, but that.
215              * is hard to under locking hierarchy */
216             if (tvc->Access && (ac = afs_FindAxs(tvc->Access, uid))) {
217                 afs_RemoveAxs(&tvc->Access, ac);
218             }
219         }
220     }
221     if (alock)
222         ReleaseReadLock(&afs_xvcache);
223
224 }                               /*afs_ResetAccessCache */
225
226
227 /*
228  * Ensure all connections make use of new tokens.  Discard incorrectly-cached
229  * access info.
230  */
231 void
232 afs_ResetUserConns(struct unixuser *auser)
233 {
234     int i;
235     struct srvAddr *sa;
236     struct afs_conn *tc;
237
238     AFS_STATCNT(afs_ResetUserConns);
239     ObtainReadLock(&afs_xsrvAddr);
240     ObtainWriteLock(&afs_xconn, 98);
241
242     for (i = 0; i < NSERVERS; i++) {
243         for (sa = afs_srvAddrs[i]; sa; sa = sa->next_bkt) {
244             for (tc = sa->conns; tc; tc = tc->next) {
245                 if (tc->user == auser) {
246                     tc->forceConnectFS = 1;
247                 }
248             }
249         }
250     }
251
252     ReleaseWriteLock(&afs_xconn);
253     ReleaseReadLock(&afs_xsrvAddr);
254     afs_ResetAccessCache(auser->uid, 1);
255     auser->states &= ~UNeedsReset;
256 }                               /*afs_ResetUserConns */
257 #endif /* !AFS_PAG_MANAGER */
258
259
260 struct unixuser *
261 afs_FindUser(afs_int32 auid, afs_int32 acell, afs_int32 locktype)
262 {
263     struct unixuser *tu;
264     afs_int32 i;
265
266     AFS_STATCNT(afs_FindUser);
267     i = UHash(auid);
268     ObtainWriteLock(&afs_xuser, 99);
269     for (tu = afs_users[i]; tu; tu = tu->next) {
270         if (tu->uid == auid && ((tu->cell == acell) || (acell == -1))) {
271             tu->refCount++;
272             ReleaseWriteLock(&afs_xuser);
273             return tu;
274         }
275     }
276     ReleaseWriteLock(&afs_xuser);
277     return NULL;
278
279 }                               /*afs_FindUser */
280
281
282 /*------------------------------------------------------------------------
283  * EXPORTED afs_ComputePAGStats
284  *
285  * Description:
286  *      Compute a set of stats concerning PAGs used by this machine.
287  *
288  * Arguments:
289  *      None.
290  *
291  * Returns:
292  *      Nothing.
293  *
294  * Environment:
295  *      The results are put in the structure responsible for keeping
296  *      detailed CM stats.  Note: entries corresponding to a single PAG
297  *      will appear on the identical hash chain, so sweeping the chain
298  *      will find all entries related to a single PAG.
299  *
300  * Side Effects:
301  *      As advertised.
302  *------------------------------------------------------------------------*/
303
304 void
305 afs_ComputePAGStats(void)
306 {
307     struct unixuser *currPAGP;  /*Ptr to curr PAG */
308     struct unixuser *cmpPAGP;   /*Ptr to PAG being compared */
309     struct afs_stats_AuthentInfo *authP;        /*Ptr to stats area */
310     int curr_Record;            /*Curr record */
311     int currChain;              /*Curr hash chain */
312     int currChainLen;           /*Length of curr hash chain */
313     int currPAGRecords;         /*# records in curr PAG */
314
315     /*
316      * Lock out everyone else from scribbling on the PAG entries.
317      */
318     ObtainReadLock(&afs_xuser);
319
320     /*
321      * Initialize the tallies, then sweep through each hash chain.  We
322      * can't bzero the structure, since some fields are cumulative over
323      * the CM's lifetime.
324      */
325     curr_Record = 0;
326     authP = &(afs_stats_cmfullperf.authent);
327     authP->curr_PAGs = 0;
328     authP->curr_Records = 0;
329     authP->curr_AuthRecords = 0;
330     authP->curr_UnauthRecords = 0;
331     authP->curr_MaxRecordsInPAG = 0;
332     authP->curr_LongestChain = 0;
333
334     for (currChain = 0; currChain < NUSERS; currChain++) {
335         currChainLen = 0;
336         for (currPAGP = afs_users[currChain]; currPAGP;
337              currPAGP = currPAGP->next) {
338             /*
339              * Bump the number of records on this current chain, along with
340              * the total number of records in existence.
341              */
342             currChainLen++;
343             curr_Record++;
344             /*
345              * We've found a previously-uncounted PAG.  If it's been deleted
346              * but just not garbage-collected yet, we step over it.
347              */
348             if (!(currPAGP->states & UHasTokens))
349                 continue;
350
351             /*
352              * If this PAG record has already been ``counted', namely
353              * associated with a PAG and tallied, clear its bit and move on.
354              */
355             (authP->curr_Records)++;
356             if (currPAGP->states & UPAGCounted) {
357                 currPAGP->states &= ~UPAGCounted;
358                 continue;
359             }
360
361
362
363             /*We've counted this one already */
364             /*
365              * Jot initial info down, then sweep down the rest of the hash
366              * chain, looking for matching PAG entries.  Note: to properly
367              * ``count'' the current record, we first compare it to itself
368              * in the following loop.
369              */
370             (authP->curr_PAGs)++;
371             currPAGRecords = 0;
372
373             for (cmpPAGP = currPAGP; cmpPAGP; cmpPAGP = cmpPAGP->next) {
374                 if (currPAGP->uid == cmpPAGP->uid) {
375                     /*
376                      * The records belong to the same PAG.  First, mark the
377                      * new record as ``counted'' and bump the PAG size.
378                      * Then, record the state of its ticket, if any.
379                      */
380                     cmpPAGP->states |= UPAGCounted;
381                     currPAGRecords++;
382                     if ((cmpPAGP->states & UHasTokens)
383                         && !(cmpPAGP->states & UTokensBad))
384                         (authP->curr_AuthRecords)++;
385                     else
386                         (authP->curr_UnauthRecords)++;
387                 }               /*Records belong to same PAG */
388             }                   /*Compare to rest of PAG records in chain */
389
390             /*
391              * In the above comparisons, the current PAG record has been
392              * marked as counted.  Erase this mark before moving on.
393              */
394             currPAGP->states &= ~UPAGCounted;
395
396             /*
397              * We've compared our current PAG record with all remaining
398              * PAG records in the hash chain.  Update our tallies, and
399              * perhaps even our lifetime high water marks.  After that,
400              * remove our search mark and advance to the next comparison
401              * pair.
402              */
403             if (currPAGRecords > authP->curr_MaxRecordsInPAG) {
404                 authP->curr_MaxRecordsInPAG = currPAGRecords;
405                 if (currPAGRecords > authP->HWM_MaxRecordsInPAG)
406                     authP->HWM_MaxRecordsInPAG = currPAGRecords;
407             }
408         }                       /*Sweep a hash chain */
409
410         /*
411          * If the chain we just finished zipping through is the longest we've
412          * seen yet, remember this fact before advancing to the next chain.
413          */
414         if (currChainLen > authP->curr_LongestChain) {
415             authP->curr_LongestChain = currChainLen;
416             if (currChainLen > authP->HWM_LongestChain)
417                 authP->HWM_LongestChain = currChainLen;
418         }
419
420     }                           /*For each hash chain in afs_user */
421
422     /*
423      * Now that we've counted everything up, we can consider all-time
424      * numbers.
425      */
426     if (authP->curr_PAGs > authP->HWM_PAGs)
427         authP->HWM_PAGs = authP->curr_PAGs;
428     if (authP->curr_Records > authP->HWM_Records)
429         authP->HWM_Records = authP->curr_Records;
430
431     /*
432      * People are free to manipulate the PAG structures now.
433      */
434     ReleaseReadLock(&afs_xuser);
435
436 }                               /*afs_ComputePAGStats */
437
438
439 struct unixuser *
440 afs_GetUser(afs_int32 auid, afs_int32 acell, afs_int32 locktype)
441 {
442     struct unixuser *tu, *pu = 0;
443     afs_int32 i;
444     afs_int32 RmtUser = 0;
445
446     AFS_STATCNT(afs_GetUser);
447     i = UHash(auid);
448     ObtainWriteLock(&afs_xuser, 104);
449     for (tu = afs_users[i]; tu; tu = tu->next) {
450         if (tu->uid == auid) {
451             RmtUser = 0;
452             pu = NULL;
453             if (tu->exporter) {
454                 RmtUser = 1;
455                 pu = tu;
456             }
457             if (tu->cell == -1 && acell != -1) {
458                 /* Here we setup the real cell for the client */
459                 tu->cell = acell;
460                 tu->refCount++;
461                 ReleaseWriteLock(&afs_xuser);
462                 return tu;
463             } else if (tu->cell == acell || acell == -1) {
464                 tu->refCount++;
465                 ReleaseWriteLock(&afs_xuser);
466                 return tu;
467             }
468         }
469     }
470     tu = afs_osi_Alloc(sizeof(struct unixuser));
471     osi_Assert(tu != NULL);
472 #ifndef AFS_NOSTATS
473     afs_stats_cmfullperf.authent.PAGCreations++;
474 #endif /* AFS_NOSTATS */
475     memset(tu, 0, sizeof(struct unixuser));
476     tu->next = afs_users[i];
477     afs_users[i] = tu;
478     if (RmtUser) {
479         /*
480          * This is for the case where an additional unixuser struct is
481          * created because the remote client is accessing a different cell;
482          * we simply rerecord relevant information from the original
483          * structure
484          */
485         if (pu && pu->exporter) {
486             tu->exporter = pu->exporter;
487             (void)EXP_HOLD(tu->exporter);
488         }
489     }
490     tu->uid = auid;
491     tu->cell = acell;
492     tu->viceId = UNDEFVID;
493     tu->refCount = 1;
494     tu->tokenTime = osi_Time();
495     ReleaseWriteLock(&afs_xuser);
496     return tu;
497
498 }                               /*afs_GetUser */
499
500
501 void
502 afs_PutUser(struct unixuser *au, afs_int32 locktype)
503 {
504     AFS_STATCNT(afs_PutUser);
505     --au->refCount;
506 }                               /*afs_PutUser */
507
508
509 /*
510  * Set the primary flag on a unixuser structure, ensuring that exactly one
511  * dude has the flag set at any time for a particular unix uid.
512  */
513 void
514 afs_SetPrimary(struct unixuser *au, int aflag)
515 {
516     struct unixuser *tu;
517     int i;
518     struct unixuser *pu;
519
520     AFS_STATCNT(afs_SetPrimary);
521     i = UHash(au->uid);
522     pu = NULL;
523     ObtainWriteLock(&afs_xuser, 105);
524     /*
525      * See if anyone is this uid's primary cell yet; recording in pu the
526      * corresponding user
527      */
528     for (tu = afs_users[i]; tu; tu = tu->next) {
529         if (tu->uid == au->uid && (tu->states & UPrimary)) {
530             pu = tu;
531         }
532     }
533     if (pu && !(pu->states & UHasTokens)) {
534         /*
535          * Primary user has unlogged, don't treat him as primary any longer;
536          * note that we want to treat him as primary until now, so that
537          * people see a primary identity until now.
538          */
539         pu->states &= ~UPrimary;
540         pu = NULL;
541     }
542     if (aflag == 1) {
543         /* setting au to be primary */
544         if (pu)
545             pu->states &= ~UPrimary;
546         au->states |= UPrimary;
547     } else if (aflag == 0) {
548         /* we don't know if we're supposed to be primary or not */
549         if (!pu || au == pu) {
550             au->states |= UPrimary;
551         } else
552             au->states &= ~UPrimary;
553     }
554     ReleaseWriteLock(&afs_xuser);
555
556 }                               /*afs_SetPrimary */
557
558 void
559 afs_NotifyUser(struct unixuser *auser, int event)
560 {
561 #ifdef AFS_DARWIN_ENV
562     darwin_notify_perms(auser, event);
563 #endif
564 }
565
566 /**
567  * Mark all of the unixuser records held for a particular PAG as
568  * expired
569  *
570  * @param[in] pag
571  *      PAG to expire records for
572  */
573 void
574 afs_MarkUserExpired(afs_int32 pag)
575 {
576     afs_int32 i;
577     struct unixuser *tu;
578
579     i = UHash(pag);
580     ObtainWriteLock(&afs_xuser, 9);
581     for (tu = afs_users[i]; tu; tu = tu->next) {
582         if (tu->uid == pag) {
583             tu->states &= ~UHasTokens;
584             tu->tokenTime = 0;
585         }
586     }
587     ReleaseWriteLock(&afs_xuser);
588 }
589
590
591 #if AFS_GCPAGS
592
593 /*
594  * Called by osi_TraverseProcTable (from afs_GCPAGs) for each
595  * process in the system.
596  * If the specified process uses a PAG, clear that PAG's temporary
597  * 'deleteme' flag.
598  */
599
600 /*
601  * This variable keeps track of the number of UID-base
602  * tokens in the afs_users table. When it's zero
603  * the per process loop in GCPAGs doesn't have to
604  * check processes without pags against the afs_users table.
605  */
606 static afs_int32 afs_GCPAGs_UIDBaseTokenCount = 0;
607
608 /*
609  * These variables keep track of the number of times
610  * afs_GCPAGs_perproc_func() is called.  If it is not called at all when
611  * walking the process table, there is something wrong and we should not
612  * prematurely expire any tokens.
613  */
614 static size_t afs_GCPAGs_perproc_count = 0;
615 static size_t afs_GCPAGs_cred_count = 0;
616
617 /*
618  * LOCKS: afs_GCPAGs_perproc_func requires write lock on afs_xuser
619  */
620 #if !defined(LINUX_KEYRING_SUPPORT) && (!defined(STRUCT_TASK_STRUCT_HAS_CRED) || defined(HAVE_LINUX_RCU_READ_LOCK))
621 void
622 afs_GCPAGs_perproc_func(afs_proc_t * pproc)
623 {
624     afs_int32 pag, hash, uid;
625     const afs_ucred_t *pcred;
626
627     afs_GCPAGs_perproc_count++;
628
629     pcred = afs_osi_proc2cred(pproc);
630     if (!pcred)
631         return;
632
633     afs_GCPAGs_cred_count++;
634
635     pag = PagInCred(pcred);
636 #if defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV) || defined(AFS_LINUX22_ENV)
637     uid = (pag != NOPAG ? pag : afs_cr_uid(pcred));
638 #elif defined(AFS_SUN510_ENV)
639     uid = (pag != NOPAG ? pag : crgetruid(pcred));
640 #else
641     uid = (pag != NOPAG ? pag : afs_cr_ruid(pcred));
642 #endif
643     hash = UHash(uid);
644
645     /* if this token is PAG based, or it's UID based and
646      * UID-based tokens exist */
647     if ((pag != NOPAG) || (afs_GCPAGs_UIDBaseTokenCount)) {
648         /* find the entries for this uid in all cells and clear the not
649          * referenced flag.  Can't use afs_FindUser, because it just returns
650          * the specific cell asked for, or the first one found.
651          */
652         struct unixuser *pu;
653         for (pu = afs_users[hash]; pu; pu = pu->next) {
654             if (pu->uid == uid) {
655                 if (pu->states & TMP_UPAGNotReferenced) {
656                     /* clear the 'deleteme' flag for this entry */
657                     pu->states &= ~TMP_UPAGNotReferenced;
658                     if (pag == NOPAG) {
659                         /* This is a uid based token that hadn't
660                          * previously been cleared, so decrement the
661                          * outstanding uid based token count */
662                         afs_GCPAGs_UIDBaseTokenCount--;
663                     }
664                 }
665             }
666         }
667     }
668 }
669 #endif
670
671 /*
672  * Go through the process table, find all unused PAGs
673  * and cause them to be deleted during the next GC.
674  *
675  * returns the number of PAGs marked for deletion
676  *
677  * On AIX we free PAGs when the last accessing process exits,
678  * so this routine is not needed.
679  *
680  * In AFS WebSecure, we explicitly call unlog when we remove
681  * an entry in the login cache, so this routine is not needed.
682  */
683
684 afs_int32
685 afs_GCPAGs(afs_int32 * ReleasedCount)
686 {
687     struct unixuser *pu;
688     int i;
689
690     if (afs_gcpags != AFS_GCPAGS_OK) {
691         return 0;
692     }
693
694     *ReleasedCount = 0;
695
696     /* first, loop through afs_users, setting the temporary 'deleteme' flag */
697     ObtainWriteLock(&afs_xuser, 419);
698     afs_GCPAGs_UIDBaseTokenCount = 0;
699     for (i = 0; i < NUSERS; i++) {
700         for (pu = afs_users[i]; pu; pu = pu->next) {
701             pu->states |= TMP_UPAGNotReferenced;
702             if (((pu->uid >> 24) & 0xff) != 'A') {
703                 /* this is a uid-based token, */
704                 /* increment the count */
705                 afs_GCPAGs_UIDBaseTokenCount++;
706             }
707         }
708     }
709
710     /* Now, iterate through the systems process table,
711      * for each process, mark it's PAGs (if any) in use.
712      * i.e. clear the temporary deleteme flag.
713      */
714     afs_GCPAGs_perproc_count = 0;
715     afs_GCPAGs_cred_count = 0;
716
717     afs_osi_TraverseProcTable();
718
719     /* If there is an internal problem and afs_GCPAGs_perproc_func()
720      * does not get called, disable gcpags so that we do not
721      * accidentally expire all the tokens in the system.
722      */
723     if (afs_gcpags == AFS_GCPAGS_OK && !afs_GCPAGs_perproc_count) {
724         afs_gcpags = AFS_GCPAGS_EPROCWALK;
725     }
726
727     if (afs_gcpags == AFS_GCPAGS_OK && !afs_GCPAGs_cred_count) {
728         afs_gcpags = AFS_GCPAGS_ECREDWALK;
729     }
730
731     /* Now, go through afs_users again, any that aren't in use
732      * (temp deleteme flag still set) will be marked for later deletion,
733      * by setting their expire times to 0.
734      */
735     for (i = 0; i < NUSERS; i++) {
736         for (pu = afs_users[i]; pu; pu = pu->next) {
737             if (pu->states & TMP_UPAGNotReferenced) {
738
739                 /* clear the temp flag */
740                 pu->states &= ~TMP_UPAGNotReferenced;
741
742                 /* Is this entry on behalf of a 'remote' user ?
743                  * i.e. nfs translator, etc.
744                  */
745                 if (!pu->exporter && afs_gcpags == AFS_GCPAGS_OK) {
746                     /* make afs_GCUserData remove this entry  */
747                     pu->states &= ~UHasTokens;
748                     pu->tokenTime = 0;
749
750                     (*ReleasedCount)++; /* remember how many we marked (info only) */
751                 }
752             }
753         }
754     }
755
756     ReleaseWriteLock(&afs_xuser);
757
758     return 0;
759 }
760
761 #endif /* AFS_GCPAGS */