Unix CM: Generalise token storage
[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->states & UHasTokens) {
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 & UTokensBad) && tu->vid != UNDEFVID) {
182                 if (!afs_HasUsableTokens(tu->tokens, now)) {
183                     /*
184                      * This token has expired, warn users and reset access
185                      * cache.
186                      */
187 #ifdef notdef
188                     /* I really hate this message - MLK */
189                     afs_warn
190                         ("afs: Tokens for user of AFS id %d for cell %s expired now\n",
191                          tu->vid, afs_GetCell(tu->cell)->cellName);
192 #endif
193                     tu->states |= (UTokensBad | UNeedsReset);
194                 }
195             }
196             if (tu->states & UNeedsReset) {
197                 tu->states &= ~UNeedsReset;
198                 uid = tu->uid;
199                 afs_ResetAccessCache(uid, 0);
200             }
201         }
202     }
203     ReleaseReadLock(&afs_xuser);
204     ReleaseReadLock(&afs_xvcache);
205
206 }                               /*afs_CheckTokenCache */
207
208
209 void
210 afs_ResetAccessCache(afs_int32 uid, int alock)
211 {
212     int i;
213     struct vcache *tvc;
214     struct axscache *ac;
215
216     AFS_STATCNT(afs_ResetAccessCache);
217     if (alock)
218         ObtainReadLock(&afs_xvcache);
219     for (i = 0; i < VCSIZE; i++) {
220         for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
221             /* really should do this under cache write lock, but that.
222              * is hard to under locking hierarchy */
223             if (tvc->Access && (ac = afs_FindAxs(tvc->Access, uid))) {
224                 afs_RemoveAxs(&tvc->Access, ac);
225             }
226         }
227     }
228     if (alock)
229         ReleaseReadLock(&afs_xvcache);
230
231 }                               /*afs_ResetAccessCache */
232
233
234 /*
235  * Ensure all connections make use of new tokens.  Discard incorrectly-cached
236  * access info.
237  */
238 void
239 afs_ResetUserConns(struct unixuser *auser)
240 {
241     int i;
242     struct srvAddr *sa;
243     struct afs_conn *tc;
244
245     AFS_STATCNT(afs_ResetUserConns);
246     ObtainReadLock(&afs_xsrvAddr);
247     ObtainWriteLock(&afs_xconn, 98);
248
249     for (i = 0; i < NSERVERS; i++) {
250         for (sa = afs_srvAddrs[i]; sa; sa = sa->next_bkt) {
251             for (tc = sa->conns; tc; tc = tc->next) {
252                 if (tc->user == auser) {
253                     tc->forceConnectFS = 1;
254                 }
255             }
256         }
257     }
258
259     ReleaseWriteLock(&afs_xconn);
260     ReleaseReadLock(&afs_xsrvAddr);
261     afs_ResetAccessCache(auser->uid, 1);
262     auser->states &= ~UNeedsReset;
263 }                               /*afs_ResetUserConns */
264 #endif /* !AFS_PAG_MANAGER */
265
266
267 struct unixuser *
268 afs_FindUser(afs_int32 auid, afs_int32 acell, afs_int32 locktype)
269 {
270     struct unixuser *tu;
271     afs_int32 i;
272
273     AFS_STATCNT(afs_FindUser);
274     i = UHash(auid);
275     ObtainWriteLock(&afs_xuser, 99);
276     for (tu = afs_users[i]; tu; tu = tu->next) {
277         if (tu->uid == auid && ((tu->cell == acell) || (acell == -1))) {
278             tu->refCount++;
279             ReleaseWriteLock(&afs_xuser);
280             return tu;
281         }
282     }
283     ReleaseWriteLock(&afs_xuser);
284     return NULL;
285
286 }                               /*afs_FindUser */
287
288
289 /*------------------------------------------------------------------------
290  * EXPORTED afs_ComputePAGStats
291  *
292  * Description:
293  *      Compute a set of stats concerning PAGs used by this machine.
294  *
295  * Arguments:
296  *      None.
297  *
298  * Returns:
299  *      Nothing.
300  *
301  * Environment:
302  *      The results are put in the structure responsible for keeping
303  *      detailed CM stats.  Note: entries corresponding to a single PAG
304  *      will appear on the identical hash chain, so sweeping the chain
305  *      will find all entries related to a single PAG.
306  *
307  * Side Effects:
308  *      As advertised.
309  *------------------------------------------------------------------------*/
310
311 void
312 afs_ComputePAGStats(void)
313 {
314     struct unixuser *currPAGP;  /*Ptr to curr PAG */
315     struct unixuser *cmpPAGP;   /*Ptr to PAG being compared */
316     struct afs_stats_AuthentInfo *authP;        /*Ptr to stats area */
317     int curr_Record;            /*Curr record */
318     int currChain;              /*Curr hash chain */
319     int currChainLen;           /*Length of curr hash chain */
320     int currPAGRecords;         /*# records in curr PAG */
321
322     /*
323      * Lock out everyone else from scribbling on the PAG entries.
324      */
325     ObtainReadLock(&afs_xuser);
326
327     /*
328      * Initialize the tallies, then sweep through each hash chain.  We
329      * can't bzero the structure, since some fields are cumulative over
330      * the CM's lifetime.
331      */
332     curr_Record = 0;
333     authP = &(afs_stats_cmfullperf.authent);
334     authP->curr_PAGs = 0;
335     authP->curr_Records = 0;
336     authP->curr_AuthRecords = 0;
337     authP->curr_UnauthRecords = 0;
338     authP->curr_MaxRecordsInPAG = 0;
339     authP->curr_LongestChain = 0;
340
341     for (currChain = 0; currChain < NUSERS; currChain++) {
342         currChainLen = 0;
343         for (currPAGP = afs_users[currChain]; currPAGP;
344              currPAGP = currPAGP->next) {
345             /*
346              * Bump the number of records on this current chain, along with
347              * the total number of records in existence.
348              */
349             currChainLen++;
350             curr_Record++;
351             /*
352              * We've found a previously-uncounted PAG.  If it's been deleted
353              * but just not garbage-collected yet, we step over it.
354              */
355             if (currPAGP->vid == UNDEFVID)
356                 continue;
357
358             /*
359              * If this PAG record has already been ``counted', namely
360              * associated with a PAG and tallied, clear its bit and move on.
361              */
362             (authP->curr_Records)++;
363             if (currPAGP->states & UPAGCounted) {
364                 currPAGP->states &= ~UPAGCounted;
365                 continue;
366             }
367
368
369
370             /*We've counted this one already */
371             /*
372              * Jot initial info down, then sweep down the rest of the hash
373              * chain, looking for matching PAG entries.  Note: to properly
374              * ``count'' the current record, we first compare it to itself
375              * in the following loop.
376              */
377             (authP->curr_PAGs)++;
378             currPAGRecords = 0;
379
380             for (cmpPAGP = currPAGP; cmpPAGP; cmpPAGP = cmpPAGP->next) {
381                 if (currPAGP->uid == cmpPAGP->uid) {
382                     /*
383                      * The records belong to the same PAG.  First, mark the
384                      * new record as ``counted'' and bump the PAG size.
385                      * Then, record the state of its ticket, if any.
386                      */
387                     cmpPAGP->states |= UPAGCounted;
388                     currPAGRecords++;
389                     if ((cmpPAGP->states & UHasTokens)
390                         && !(cmpPAGP->states & UTokensBad))
391                         (authP->curr_AuthRecords)++;
392                     else
393                         (authP->curr_UnauthRecords)++;
394                 }               /*Records belong to same PAG */
395             }                   /*Compare to rest of PAG records in chain */
396
397             /*
398              * In the above comparisons, the current PAG record has been
399              * marked as counted.  Erase this mark before moving on.
400              */
401             currPAGP->states &= ~UPAGCounted;
402
403             /*
404              * We've compared our current PAG record with all remaining
405              * PAG records in the hash chain.  Update our tallies, and
406              * perhaps even our lifetime high water marks.  After that,
407              * remove our search mark and advance to the next comparison
408              * pair.
409              */
410             if (currPAGRecords > authP->curr_MaxRecordsInPAG) {
411                 authP->curr_MaxRecordsInPAG = currPAGRecords;
412                 if (currPAGRecords > authP->HWM_MaxRecordsInPAG)
413                     authP->HWM_MaxRecordsInPAG = currPAGRecords;
414             }
415         }                       /*Sweep a hash chain */
416
417         /*
418          * If the chain we just finished zipping through is the longest we've
419          * seen yet, remember this fact before advancing to the next chain.
420          */
421         if (currChainLen > authP->curr_LongestChain) {
422             authP->curr_LongestChain = currChainLen;
423             if (currChainLen > authP->HWM_LongestChain)
424                 authP->HWM_LongestChain = currChainLen;
425         }
426
427     }                           /*For each hash chain in afs_user */
428
429     /*
430      * Now that we've counted everything up, we can consider all-time
431      * numbers.
432      */
433     if (authP->curr_PAGs > authP->HWM_PAGs)
434         authP->HWM_PAGs = authP->curr_PAGs;
435     if (authP->curr_Records > authP->HWM_Records)
436         authP->HWM_Records = authP->curr_Records;
437
438     /*
439      * People are free to manipulate the PAG structures now.
440      */
441     ReleaseReadLock(&afs_xuser);
442
443 }                               /*afs_ComputePAGStats */
444
445
446 struct unixuser *
447 afs_GetUser(afs_int32 auid, afs_int32 acell, afs_int32 locktype)
448 {
449     struct unixuser *tu, *pu = 0;
450     afs_int32 i;
451     afs_int32 RmtUser = 0;
452
453     AFS_STATCNT(afs_GetUser);
454     i = UHash(auid);
455     ObtainWriteLock(&afs_xuser, 104);
456     for (tu = afs_users[i]; tu; tu = tu->next) {
457         if (tu->uid == auid) {
458             RmtUser = 0;
459             pu = NULL;
460             if (tu->exporter) {
461                 RmtUser = 1;
462                 pu = tu;
463             }
464             if (tu->cell == -1 && acell != -1) {
465                 /* Here we setup the real cell for the client */
466                 tu->cell = acell;
467                 tu->refCount++;
468                 ReleaseWriteLock(&afs_xuser);
469                 return tu;
470             } else if (tu->cell == acell || acell == -1) {
471                 tu->refCount++;
472                 ReleaseWriteLock(&afs_xuser);
473                 return tu;
474             }
475         }
476     }
477     tu = (struct unixuser *)afs_osi_Alloc(sizeof(struct unixuser));
478 #ifndef AFS_NOSTATS
479     afs_stats_cmfullperf.authent.PAGCreations++;
480 #endif /* AFS_NOSTATS */
481     memset(tu, 0, sizeof(struct unixuser));
482     tu->next = afs_users[i];
483     afs_users[i] = tu;
484     if (RmtUser) {
485         /*
486          * This is for the case where an additional unixuser struct is
487          * created because the remote client is accessing a different cell;
488          * we simply rerecord relevant information from the original
489          * structure
490          */
491         if (pu && pu->exporter) {
492             tu->exporter = pu->exporter;
493             (void)EXP_HOLD(tu->exporter);
494         }
495     }
496     tu->uid = auid;
497     tu->cell = acell;
498     tu->vid = UNDEFVID;
499     tu->refCount = 1;
500     tu->tokenTime = osi_Time();
501     ReleaseWriteLock(&afs_xuser);
502     return tu;
503
504 }                               /*afs_GetUser */
505
506
507 void
508 afs_PutUser(struct unixuser *au, afs_int32 locktype)
509 {
510     AFS_STATCNT(afs_PutUser);
511     --au->refCount;
512 }                               /*afs_PutUser */
513
514
515 /*
516  * Set the primary flag on a unixuser structure, ensuring that exactly one
517  * dude has the flag set at any time for a particular unix uid.
518  */
519 void
520 afs_SetPrimary(struct unixuser *au, int aflag)
521 {
522     struct unixuser *tu;
523     int i;
524     struct unixuser *pu;
525
526     AFS_STATCNT(afs_SetPrimary);
527     i = UHash(au->uid);
528     pu = NULL;
529     ObtainWriteLock(&afs_xuser, 105);
530     /*
531      * See if anyone is this uid's primary cell yet; recording in pu the
532      * corresponding user
533      */
534     for (tu = afs_users[i]; tu; tu = tu->next) {
535         if (tu->uid == au->uid && (tu->states & UPrimary)) {
536             pu = tu;
537         }
538     }
539     if (pu && !(pu->states & UHasTokens)) {
540         /*
541          * Primary user has unlogged, don't treat him as primary any longer;
542          * note that we want to treat him as primary until now, so that
543          * people see a primary identity until now.
544          */
545         pu->states &= ~UPrimary;
546         pu = NULL;
547     }
548     if (aflag == 1) {
549         /* setting au to be primary */
550         if (pu)
551             pu->states &= ~UPrimary;
552         au->states |= UPrimary;
553     } else if (aflag == 0) {
554         /* we don't know if we're supposed to be primary or not */
555         if (!pu || au == pu) {
556             au->states |= UPrimary;
557         } else
558             au->states &= ~UPrimary;
559     }
560     ReleaseWriteLock(&afs_xuser);
561
562 }                               /*afs_SetPrimary */
563
564 void
565 afs_NotifyUser(struct unixuser *auser, int event)
566 {
567 #ifdef AFS_DARWIN_ENV
568     darwin_notify_perms(auser, event);
569 #endif
570 }
571
572 /**
573  * Mark all of the unixuser records held for a particular PAG as
574  * expired
575  *
576  * @param[in] pag
577  *      PAG to expire records for
578  */
579 void
580 afs_MarkUserExpired(afs_int32 pag)
581 {
582     afs_int32 i;
583     struct unixuser *tu;
584
585     i = UHash(pag);
586     ObtainWriteLock(&afs_xuser, 9);
587     for (tu = afs_users[i]; tu; tu = tu->next) {
588         if (tu->uid == pag) {
589             tu->states &= ~UHasTokens;
590             tu->tokenTime = 0;
591         }
592     }
593     ReleaseWriteLock(&afs_xuser);
594 }
595
596
597 #if AFS_GCPAGS
598
599 /*
600  * Called by osi_TraverseProcTable (from afs_GCPAGs) for each
601  * process in the system.
602  * If the specified process uses a PAG, clear that PAG's temporary
603  * 'deleteme' flag.
604  */
605
606 /*
607  * This variable keeps track of the number of UID-base
608  * tokens in the afs_users table. When it's zero
609  * the per process loop in GCPAGs doesn't have to
610  * check processes without pags against the afs_users table.
611  */
612 static afs_int32 afs_GCPAGs_UIDBaseTokenCount = 0;
613
614 /*
615  * These variables keep track of the number of times
616  * afs_GCPAGs_perproc_func() is called.  If it is not called at all when
617  * walking the process table, there is something wrong and we should not
618  * prematurely expire any tokens.
619  */
620 static size_t afs_GCPAGs_perproc_count = 0;
621 static size_t afs_GCPAGs_cred_count = 0;
622
623 /*
624  * LOCKS: afs_GCPAGs_perproc_func requires write lock on afs_xuser
625  */
626 #if !defined(LINUX_KEYRING_SUPPORT) && (!defined(STRUCT_TASK_STRUCT_HAS_CRED) || defined(HAVE_LINUX_RCU_READ_LOCK))
627 void
628 afs_GCPAGs_perproc_func(afs_proc_t * pproc)
629 {
630     afs_int32 pag, hash, uid;
631     const afs_ucred_t *pcred;
632
633     afs_GCPAGs_perproc_count++;
634
635     pcred = afs_osi_proc2cred(pproc);
636     if (!pcred)
637         return;
638
639     afs_GCPAGs_cred_count++;
640
641     pag = PagInCred(pcred);
642 #if defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV) || defined(AFS_LINUX22_ENV)
643     uid = (pag != NOPAG ? pag : afs_cr_uid(pcred));
644 #elif defined(AFS_SUN510_ENV)
645     uid = (pag != NOPAG ? pag : crgetruid(pcred));
646 #else
647     uid = (pag != NOPAG ? pag : afs_cr_ruid(pcred));
648 #endif
649     hash = UHash(uid);
650
651     /* if this token is PAG based, or it's UID based and
652      * UID-based tokens exist */
653     if ((pag != NOPAG) || (afs_GCPAGs_UIDBaseTokenCount)) {
654         /* find the entries for this uid in all cells and clear the not
655          * referenced flag.  Can't use afs_FindUser, because it just returns
656          * the specific cell asked for, or the first one found.
657          */
658         struct unixuser *pu;
659         for (pu = afs_users[hash]; pu; pu = pu->next) {
660             if (pu->uid == uid) {
661                 if (pu->states & TMP_UPAGNotReferenced) {
662                     /* clear the 'deleteme' flag for this entry */
663                     pu->states &= ~TMP_UPAGNotReferenced;
664                     if (pag == NOPAG) {
665                         /* This is a uid based token that hadn't
666                          * previously been cleared, so decrement the
667                          * outstanding uid based token count */
668                         afs_GCPAGs_UIDBaseTokenCount--;
669                     }
670                 }
671             }
672         }
673     }
674 }
675 #endif
676
677 /*
678  * Go through the process table, find all unused PAGs
679  * and cause them to be deleted during the next GC.
680  *
681  * returns the number of PAGs marked for deletion
682  *
683  * On AIX we free PAGs when the last accessing process exits,
684  * so this routine is not needed.
685  *
686  * In AFS WebSecure, we explicitly call unlog when we remove
687  * an entry in the login cache, so this routine is not needed.
688  */
689
690 afs_int32
691 afs_GCPAGs(afs_int32 * ReleasedCount)
692 {
693     struct unixuser *pu;
694     int i;
695
696     if (afs_gcpags != AFS_GCPAGS_OK) {
697         return 0;
698     }
699
700     *ReleasedCount = 0;
701
702     /* first, loop through afs_users, setting the temporary 'deleteme' flag */
703     ObtainWriteLock(&afs_xuser, 419);
704     afs_GCPAGs_UIDBaseTokenCount = 0;
705     for (i = 0; i < NUSERS; i++) {
706         for (pu = afs_users[i]; pu; pu = pu->next) {
707             pu->states |= TMP_UPAGNotReferenced;
708             if (((pu->uid >> 24) & 0xff) != 'A') {
709                 /* this is a uid-based token, */
710                 /* increment the count */
711                 afs_GCPAGs_UIDBaseTokenCount++;
712             }
713         }
714     }
715
716     /* Now, iterate through the systems process table,
717      * for each process, mark it's PAGs (if any) in use.
718      * i.e. clear the temporary deleteme flag.
719      */
720     afs_GCPAGs_perproc_count = 0;
721     afs_GCPAGs_cred_count = 0;
722
723     afs_osi_TraverseProcTable();
724
725     /* If there is an internal problem and afs_GCPAGs_perproc_func()
726      * does not get called, disable gcpags so that we do not
727      * accidentally expire all the tokens in the system.
728      */
729     if (afs_gcpags == AFS_GCPAGS_OK && !afs_GCPAGs_perproc_count) {
730         afs_gcpags = AFS_GCPAGS_EPROCWALK;
731     }
732
733     if (afs_gcpags == AFS_GCPAGS_OK && !afs_GCPAGs_cred_count) {
734         afs_gcpags = AFS_GCPAGS_ECREDWALK;
735     }
736
737     /* Now, go through afs_users again, any that aren't in use
738      * (temp deleteme flag still set) will be marked for later deletion,
739      * by setting their expire times to 0.
740      */
741     for (i = 0; i < NUSERS; i++) {
742         for (pu = afs_users[i]; pu; pu = pu->next) {
743             if (pu->states & TMP_UPAGNotReferenced) {
744
745                 /* clear the temp flag */
746                 pu->states &= ~TMP_UPAGNotReferenced;
747
748                 /* Is this entry on behalf of a 'remote' user ?
749                  * i.e. nfs translator, etc.
750                  */
751                 if (!pu->exporter && afs_gcpags == AFS_GCPAGS_OK) {
752                     /* make afs_GCUserData remove this entry  */
753                     pu->states &= ~UHasTokens;
754                     pu->tokenTime = 0;
755
756                     (*ReleasedCount)++; /* remember how many we marked (info only) */
757                 }
758             }
759         }
760     }
761
762     ReleaseWriteLock(&afs_xuser);
763
764     return 0;
765 }
766
767 #endif /* AFS_GCPAGS */