New GetToken pioctl
[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 = (struct unixuser *)afs_osi_Alloc(sizeof(struct unixuser));
471 #ifndef AFS_NOSTATS
472     afs_stats_cmfullperf.authent.PAGCreations++;
473 #endif /* AFS_NOSTATS */
474     memset(tu, 0, sizeof(struct unixuser));
475     tu->next = afs_users[i];
476     afs_users[i] = tu;
477     if (RmtUser) {
478         /*
479          * This is for the case where an additional unixuser struct is
480          * created because the remote client is accessing a different cell;
481          * we simply rerecord relevant information from the original
482          * structure
483          */
484         if (pu && pu->exporter) {
485             tu->exporter = pu->exporter;
486             (void)EXP_HOLD(tu->exporter);
487         }
488     }
489     tu->uid = auid;
490     tu->cell = acell;
491     tu->viceId = UNDEFVID;
492     tu->refCount = 1;
493     tu->tokenTime = osi_Time();
494     ReleaseWriteLock(&afs_xuser);
495     return tu;
496
497 }                               /*afs_GetUser */
498
499
500 void
501 afs_PutUser(struct unixuser *au, afs_int32 locktype)
502 {
503     AFS_STATCNT(afs_PutUser);
504     --au->refCount;
505 }                               /*afs_PutUser */
506
507
508 /*
509  * Set the primary flag on a unixuser structure, ensuring that exactly one
510  * dude has the flag set at any time for a particular unix uid.
511  */
512 void
513 afs_SetPrimary(struct unixuser *au, int aflag)
514 {
515     struct unixuser *tu;
516     int i;
517     struct unixuser *pu;
518
519     AFS_STATCNT(afs_SetPrimary);
520     i = UHash(au->uid);
521     pu = NULL;
522     ObtainWriteLock(&afs_xuser, 105);
523     /*
524      * See if anyone is this uid's primary cell yet; recording in pu the
525      * corresponding user
526      */
527     for (tu = afs_users[i]; tu; tu = tu->next) {
528         if (tu->uid == au->uid && (tu->states & UPrimary)) {
529             pu = tu;
530         }
531     }
532     if (pu && !(pu->states & UHasTokens)) {
533         /*
534          * Primary user has unlogged, don't treat him as primary any longer;
535          * note that we want to treat him as primary until now, so that
536          * people see a primary identity until now.
537          */
538         pu->states &= ~UPrimary;
539         pu = NULL;
540     }
541     if (aflag == 1) {
542         /* setting au to be primary */
543         if (pu)
544             pu->states &= ~UPrimary;
545         au->states |= UPrimary;
546     } else if (aflag == 0) {
547         /* we don't know if we're supposed to be primary or not */
548         if (!pu || au == pu) {
549             au->states |= UPrimary;
550         } else
551             au->states &= ~UPrimary;
552     }
553     ReleaseWriteLock(&afs_xuser);
554
555 }                               /*afs_SetPrimary */
556
557 void
558 afs_NotifyUser(struct unixuser *auser, int event)
559 {
560 #ifdef AFS_DARWIN_ENV
561     darwin_notify_perms(auser, event);
562 #endif
563 }
564
565 /**
566  * Mark all of the unixuser records held for a particular PAG as
567  * expired
568  *
569  * @param[in] pag
570  *      PAG to expire records for
571  */
572 void
573 afs_MarkUserExpired(afs_int32 pag)
574 {
575     afs_int32 i;
576     struct unixuser *tu;
577
578     i = UHash(pag);
579     ObtainWriteLock(&afs_xuser, 9);
580     for (tu = afs_users[i]; tu; tu = tu->next) {
581         if (tu->uid == pag) {
582             tu->states &= ~UHasTokens;
583             tu->tokenTime = 0;
584         }
585     }
586     ReleaseWriteLock(&afs_xuser);
587 }
588
589
590 #if AFS_GCPAGS
591
592 /*
593  * Called by osi_TraverseProcTable (from afs_GCPAGs) for each
594  * process in the system.
595  * If the specified process uses a PAG, clear that PAG's temporary
596  * 'deleteme' flag.
597  */
598
599 /*
600  * This variable keeps track of the number of UID-base
601  * tokens in the afs_users table. When it's zero
602  * the per process loop in GCPAGs doesn't have to
603  * check processes without pags against the afs_users table.
604  */
605 static afs_int32 afs_GCPAGs_UIDBaseTokenCount = 0;
606
607 /*
608  * These variables keep track of the number of times
609  * afs_GCPAGs_perproc_func() is called.  If it is not called at all when
610  * walking the process table, there is something wrong and we should not
611  * prematurely expire any tokens.
612  */
613 static size_t afs_GCPAGs_perproc_count = 0;
614 static size_t afs_GCPAGs_cred_count = 0;
615
616 /*
617  * LOCKS: afs_GCPAGs_perproc_func requires write lock on afs_xuser
618  */
619 #if !defined(LINUX_KEYRING_SUPPORT) && (!defined(STRUCT_TASK_STRUCT_HAS_CRED) || defined(HAVE_LINUX_RCU_READ_LOCK))
620 void
621 afs_GCPAGs_perproc_func(afs_proc_t * pproc)
622 {
623     afs_int32 pag, hash, uid;
624     const afs_ucred_t *pcred;
625
626     afs_GCPAGs_perproc_count++;
627
628     pcred = afs_osi_proc2cred(pproc);
629     if (!pcred)
630         return;
631
632     afs_GCPAGs_cred_count++;
633
634     pag = PagInCred(pcred);
635 #if defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV) || defined(AFS_LINUX22_ENV)
636     uid = (pag != NOPAG ? pag : afs_cr_uid(pcred));
637 #elif defined(AFS_SUN510_ENV)
638     uid = (pag != NOPAG ? pag : crgetruid(pcred));
639 #else
640     uid = (pag != NOPAG ? pag : afs_cr_ruid(pcred));
641 #endif
642     hash = UHash(uid);
643
644     /* if this token is PAG based, or it's UID based and
645      * UID-based tokens exist */
646     if ((pag != NOPAG) || (afs_GCPAGs_UIDBaseTokenCount)) {
647         /* find the entries for this uid in all cells and clear the not
648          * referenced flag.  Can't use afs_FindUser, because it just returns
649          * the specific cell asked for, or the first one found.
650          */
651         struct unixuser *pu;
652         for (pu = afs_users[hash]; pu; pu = pu->next) {
653             if (pu->uid == uid) {
654                 if (pu->states & TMP_UPAGNotReferenced) {
655                     /* clear the 'deleteme' flag for this entry */
656                     pu->states &= ~TMP_UPAGNotReferenced;
657                     if (pag == NOPAG) {
658                         /* This is a uid based token that hadn't
659                          * previously been cleared, so decrement the
660                          * outstanding uid based token count */
661                         afs_GCPAGs_UIDBaseTokenCount--;
662                     }
663                 }
664             }
665         }
666     }
667 }
668 #endif
669
670 /*
671  * Go through the process table, find all unused PAGs
672  * and cause them to be deleted during the next GC.
673  *
674  * returns the number of PAGs marked for deletion
675  *
676  * On AIX we free PAGs when the last accessing process exits,
677  * so this routine is not needed.
678  *
679  * In AFS WebSecure, we explicitly call unlog when we remove
680  * an entry in the login cache, so this routine is not needed.
681  */
682
683 afs_int32
684 afs_GCPAGs(afs_int32 * ReleasedCount)
685 {
686     struct unixuser *pu;
687     int i;
688
689     if (afs_gcpags != AFS_GCPAGS_OK) {
690         return 0;
691     }
692
693     *ReleasedCount = 0;
694
695     /* first, loop through afs_users, setting the temporary 'deleteme' flag */
696     ObtainWriteLock(&afs_xuser, 419);
697     afs_GCPAGs_UIDBaseTokenCount = 0;
698     for (i = 0; i < NUSERS; i++) {
699         for (pu = afs_users[i]; pu; pu = pu->next) {
700             pu->states |= TMP_UPAGNotReferenced;
701             if (((pu->uid >> 24) & 0xff) != 'A') {
702                 /* this is a uid-based token, */
703                 /* increment the count */
704                 afs_GCPAGs_UIDBaseTokenCount++;
705             }
706         }
707     }
708
709     /* Now, iterate through the systems process table,
710      * for each process, mark it's PAGs (if any) in use.
711      * i.e. clear the temporary deleteme flag.
712      */
713     afs_GCPAGs_perproc_count = 0;
714     afs_GCPAGs_cred_count = 0;
715
716     afs_osi_TraverseProcTable();
717
718     /* If there is an internal problem and afs_GCPAGs_perproc_func()
719      * does not get called, disable gcpags so that we do not
720      * accidentally expire all the tokens in the system.
721      */
722     if (afs_gcpags == AFS_GCPAGS_OK && !afs_GCPAGs_perproc_count) {
723         afs_gcpags = AFS_GCPAGS_EPROCWALK;
724     }
725
726     if (afs_gcpags == AFS_GCPAGS_OK && !afs_GCPAGs_cred_count) {
727         afs_gcpags = AFS_GCPAGS_ECREDWALK;
728     }
729
730     /* Now, go through afs_users again, any that aren't in use
731      * (temp deleteme flag still set) will be marked for later deletion,
732      * by setting their expire times to 0.
733      */
734     for (i = 0; i < NUSERS; i++) {
735         for (pu = afs_users[i]; pu; pu = pu->next) {
736             if (pu->states & TMP_UPAGNotReferenced) {
737
738                 /* clear the temp flag */
739                 pu->states &= ~TMP_UPAGNotReferenced;
740
741                 /* Is this entry on behalf of a 'remote' user ?
742                  * i.e. nfs translator, etc.
743                  */
744                 if (!pu->exporter && afs_gcpags == AFS_GCPAGS_OK) {
745                     /* make afs_GCUserData remove this entry  */
746                     pu->states &= ~UHasTokens;
747                     pu->tokenTime = 0;
748
749                     (*ReleasedCount)++; /* remember how many we marked (info only) */
750                 }
751             }
752         }
753     }
754
755     ReleaseWriteLock(&afs_xuser);
756
757     return 0;
758 }
759
760 #endif /* AFS_GCPAGS */