xref: /netbsd-src/external/bsd/am-utils/dist/hlfsd/homedir.c (revision 962766853c385b86328bab806c19ccdf4e22f287)
1 /*	$NetBSD: homedir.c,v 1.1.1.1 2008/09/19 20:07:21 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1997-2007 Erez Zadok
5  * Copyright (c) 1989 Jan-Simon Pendry
6  * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
7  * Copyright (c) 1989 The Regents of the University of California.
8  * All rights reserved.
9  *
10  * This code is derived from software contributed to Berkeley by
11  * Jan-Simon Pendry at Imperial College, London.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgment:
23  *      This product includes software developed by the University of
24  *      California, Berkeley and its contributors.
25  * 4. Neither the name of the University nor the names of its contributors
26  *    may be used to endorse or promote products derived from this software
27  *    without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.
40  *
41  *
42  * File: am-utils/hlfsd/homedir.c
43  *
44  * HLFSD was written at Columbia University Computer Science Department, by
45  * Erez Zadok <ezk@cs.columbia.edu> and Alexander Dupuy <dupuy@cs.columbia.edu>
46  * It is being distributed under the same terms and conditions as amd does.
47  */
48 
49 #ifdef HAVE_CONFIG_H
50 # include <config.h>
51 #endif /* HAVE_CONFIG_H */
52 #include <am_defs.h>
53 #include <hlfsd.h>
54 
55 
56 /*
57  * STATIC VARIABLES AND FUNCTIONS:
58  */
59 static FILE *passwd_fp = NULL;
60 static char pw_name[16], pw_dir[128];
61 static int cur_pwtab_num = 0, max_pwtab_num = 0;
62 static int hlfsd_diskspace(char *);
63 static int hlfsd_stat(char *, struct stat *);
64 static int passwd_line = 0;
65 static int plt_reset(void);
66 static struct passwd passwd_ent;
67 static uid2home_t *lastchild;
68 static uid2home_t *pwtab;
69 static void delay(uid2home_t *, int);
70 static void table_add(u_int, const char *, const char *);
71 static char mboxfile[MAXPATHLEN];
72 static char *root_home;		/* root's home directory */
73 
74 /* GLOBAL FUNCTIONS */
75 char *homeof(char *username);
76 int uidof(char *username);
77 
78 /* GLOBALS VARIABLES */
79 username2uid_t *untab;		/* user name table */
80 
81 /*
82  * Return the home directory pathname for the user with uid "userid".
83  */
84 char *
85 homedir(int userid, int groupid)
86 {
87   static char linkval[MAXPATHLEN + 1];
88   static struct timeval tp;
89   uid2home_t *found;
90   char *homename;
91   struct stat homestat;
92   int old_groupid, old_userid;
93 
94   if ((found = plt_search(userid)) == (uid2home_t *) NULL) {
95     return alt_spooldir;	/* use alt spool for unknown uid */
96   }
97   homename = found->home;
98 
99   if (homename[0] != '/' || homename[1] == '\0') {
100     found->last_status = 1;
101     return alt_spooldir;	/* use alt spool for / or rel. home */
102   }
103   if ((int) userid == 0)	/* force all uid 0 to use root's home */
104     xsnprintf(linkval, sizeof(linkval), "%s/%s", root_home, home_subdir);
105   else
106     xsnprintf(linkval, sizeof(linkval), "%s/%s", homename, home_subdir);
107 
108   if (noverify) {
109     found->last_status = 0;
110     return linkval;
111   }
112 
113   /*
114    * To optimize hlfsd, we don't actually check the validity of the
115    * symlink if it has been checked in the last N seconds.  It is
116    * very likely that the link, machine, and filesystem are still
117    * valid, as long as N is small.  But if N is large, that may not be
118    * true.  That's why the default N is 5 minutes, but we allow the
119    * user to override this value via a command line option.  Note that
120    * we do not update the last_access_time each time it is accessed,
121    * but only once every N seconds.
122    */
123   if (gettimeofday(&tp, (struct timezone *) NULL) < 0) {
124     tp.tv_sec = 0;
125   } else {
126     if ((tp.tv_sec - found->last_access_time) < cache_interval) {
127       if (found->last_status == 0) {
128 	return linkval;
129       } else {
130 	return alt_spooldir;
131       }
132     } else {
133       found->last_access_time = tp.tv_sec;
134     }
135   }
136 
137   /*
138    * Only run this forking code if ask for -D fork (default).
139    * Disable forking using -D nofork.
140    */
141   if (amuDebug(D_FORK)) {
142     /* fork child to process request if none in progress */
143     if (found->child && kill(found->child, 0))
144       found->child = 0;
145 
146     if (found->child)
147       delay(found, 5);		/* wait a bit if in progress */
148     if (found->child) {		/* better safe than sorry - maybe */
149       found->last_status = 1;
150       return alt_spooldir;
151     }
152     if ((found->child = fork()) < 0) {
153       found->last_status = 1;
154       return alt_spooldir;
155     }
156     if (found->child) {		/* PARENT */
157       if (lastchild)
158 	dlog("cache spill uid = %ld, pid = %ld, home = %s",
159 	     (long) lastchild->uid, (long) lastchild->child,
160 	     lastchild->home);
161       lastchild = found;
162       return (char *) NULL;	/* return NULL to parent, so it can continue */
163     }
164   }
165 
166   /*
167    * CHILD: (or parent if -D fork)
168    *
169    * Check and create dir if needed.
170    * Check disk space and/or quotas too.
171    *
172    * We don't need to set the _last_status field of found after the fork
173    * in the child, b/c that information would be later determined in
174    * nfsproc_readlink_2() and the correct exit status would be returned
175    * to the parent upon SIGCHLD in interlock().
176    *
177    */
178   am_set_mypid();		/* for logging routines */
179   if ((old_groupid = setgid(groupid)) < 0) {
180     plog(XLOG_WARNING, "could not setgid to %d: %m", groupid);
181     return linkval;
182   }
183   if ((old_userid = seteuid(userid)) < 0) {
184     plog(XLOG_WARNING, "could not seteuid to %d: %m", userid);
185     setgid(old_groupid);
186     return linkval;
187   }
188   if (hlfsd_stat(linkval, &homestat) < 0) {
189     if (errno == ENOENT) {	/* make the spool dir if possible */
190       /* don't use recursive mkdirs here */
191       if (mkdir(linkval, PERS_SPOOLMODE) < 0) {
192 	seteuid(old_userid);
193 	setgid(old_groupid);
194 	plog(XLOG_WARNING, "can't make directory %s: %m", linkval);
195 	return alt_spooldir;
196       }
197       /* fall through to testing the disk space / quota */
198     } else {			/* the home dir itself must not exist then */
199       seteuid(old_userid);
200       setgid(old_groupid);
201       plog(XLOG_WARNING, "bad link to %s: %m", linkval);
202       return alt_spooldir;
203     }
204   }
205 
206   /*
207    * If gets here, then either the spool dir in the home dir exists,
208    * or it was just created.  In either case, we now need to
209    * test if we can create a small file and write at least one
210    * byte into it.  This will test that we have both enough inodes
211    * and disk blocks to spare, or they fall within the user's quotas too.
212    * We are still seteuid to the user at this point.
213    */
214   if (hlfsd_diskspace(linkval) < 0) {
215     seteuid(old_userid);
216     setgid(old_groupid);
217     plog(XLOG_WARNING, "no more space in %s: %m", linkval);
218     return alt_spooldir;
219   } else {
220     seteuid(old_userid);
221     setgid(old_groupid);
222     return linkval;
223   }
224 }
225 
226 
227 static int
228 hlfsd_diskspace(char *path)
229 {
230   char buf[MAXPATHLEN];
231   int fd, len;
232 
233   xsnprintf(buf, sizeof(buf), "%s/._hlfstmp_%lu", path, (long) getpid());
234   if ((fd = open(buf, O_RDWR | O_CREAT, 0600)) < 0) {
235     plog(XLOG_ERROR, "cannot open %s: %m", buf);
236     return -1;
237   }
238   len = strlen(buf);
239   if (write(fd, buf, len) < len) {
240     plog(XLOG_ERROR, "cannot write \"%s\" (%d bytes) to %s : %m", buf, len, buf);
241     close(fd);
242     unlink(buf);		/* cleanup just in case */
243     return -1;
244   }
245   if (unlink(buf) < 0) {
246     plog(XLOG_ERROR, "cannot unlink %s : %m", buf);
247   }
248   close(fd);
249   return 0;
250 }
251 
252 
253 static int
254 hlfsd_stat(char *path, struct stat *statp)
255 {
256   if (stat(path, statp) < 0)
257     return -1;
258   else if (!S_ISDIR(statp->st_mode)) {
259     errno = ENOTDIR;
260     return -1;
261   }
262   return 0;
263 }
264 
265 
266 static void
267 delay(uid2home_t *found, int secs)
268 {
269   struct timeval tv;
270 
271   if (found)
272     dlog("delaying on child %ld for %d seconds", (long) found->child, secs);
273 
274   tv.tv_usec = 0;
275 
276   do {
277     tv.tv_sec = secs;
278     if (select(0, NULL, NULL, NULL, &tv) == 0)
279       break;
280   } while (--secs && found->child);
281 }
282 
283 
284 /*
285  * This function is called when a child has terminated after
286  * servicing an nfs request.  We need to check the exit status and
287  * update the last_status field of the requesting user.
288  */
289 RETSIGTYPE
290 interlock(int signum)
291 {
292   int child;
293   uid2home_t *lostchild;
294   int status;
295 
296 #ifdef HAVE_WAITPID
297   while ((child = waitpid((pid_t) -1, &status, WNOHANG)) > 0) {
298 #else /* not HAVE_WAITPID */
299   while ((child = wait3(&status, WNOHANG, (struct rusage *) NULL)) > 0) {
300 #endif /* not HAVE_WAITPID */
301 
302     /* high chances this was the last child forked */
303     if (lastchild && lastchild->child == child) {
304       lastchild->child = 0;
305 
306       if (WIFEXITED(status))
307 	lastchild->last_status = WEXITSTATUS(status);
308       lastchild = (uid2home_t *) NULL;
309     } else {
310       /* and if not, we have to search for it... */
311       for (lostchild = pwtab; lostchild < &pwtab[cur_pwtab_num]; lostchild++) {
312 	if (lostchild->child == child) {
313 	  if (WIFEXITED(status))
314 	    lostchild->last_status = WEXITSTATUS(status);
315 	  lostchild->child = 0;
316 	  break;
317 	}
318       }
319     }
320   }
321 }
322 
323 
324 /*
325  * PASSWORD AND USERNAME LOOKUP TABLES FUNCTIONS
326  */
327 
328 /*
329  * get index of UserName table entry which matches username.
330  * must not return uid_t because we want to return a negative number.
331  */
332 int
333 untab_index(char *username)
334 {
335   int max, min, mid, cmp;
336 
337   max = cur_pwtab_num - 1;
338   min = 0;
339 
340   do {
341     mid = (max + min) / 2;
342     cmp = strcmp(untab[mid].username, username);
343     if (cmp == 0)		/* record found! */
344       return mid;
345     if (cmp > 0)
346       max = mid;
347     else
348       min = mid;
349   } while (max > min + 1);
350 
351   if (STREQ(untab[max].username, username))
352     return max;
353   if (STREQ(untab[min].username, username))
354     return min;
355 
356   /* if gets here then record was not found */
357   return -1;
358 }
359 
360 
361 /*
362  * Don't make this return a uid_t, because we need to return negative
363  * numbers as well (error codes.)
364  */
365 int
366 uidof(char *username)
367 {
368   int idx;
369 
370   if ((idx = untab_index(username)) < 0)	/* not found */
371     return INVALIDID;			/* an invalid user id */
372   return untab[idx].uid;
373 }
374 
375 
376 /*
377  * Don't make this return a uid_t, because we need to return negative
378  * numbers as well (error codes.)
379  */
380 char *
381 homeof(char *username)
382 {
383   int idx;
384 
385   if ((idx = untab_index(username)) < 0)	/* not found */
386     return (char *) NULL;	/* an invalid user id */
387   return untab[idx].home;
388 }
389 
390 
391 char *
392 mailbox(int uid, char *username)
393 {
394   char *home;
395 
396   if (uid < 0)
397     return (char *) NULL;	/* not found */
398 
399   if ((home = homeof(username)) == (char *) NULL)
400     return (char *) NULL;
401   if (STREQ(home, "/"))
402     xsnprintf(mboxfile, sizeof(mboxfile),
403 	      "/%s/%s", home_subdir, username);
404   else
405     xsnprintf(mboxfile, sizeof(mboxfile),
406 	      "%s/%s/%s", home, home_subdir, username);
407   return mboxfile;
408 }
409 
410 
411 static int
412 plt_compare_fxn(const voidp x, const voidp y)
413 
414 {
415   uid2home_t *i = (uid2home_t *) x;
416   uid2home_t *j = (uid2home_t *) y;
417 
418   return i->uid - j->uid;
419 }
420 
421 
422 static int
423 unt_compare_fxn(const voidp x, const voidp y)
424 {
425   username2uid_t *i = (username2uid_t *) x;
426   username2uid_t *j = (username2uid_t *) y;
427 
428   return strcmp(i->username, j->username);
429 }
430 
431 
432 /* perform initialization of user passwd database */
433 static void
434 hlfsd_setpwent(void)
435 {
436   if (!passwdfile) {
437     setpwent();
438     return;
439   }
440 
441   passwd_fp = fopen(passwdfile, "r");
442   if (!passwd_fp) {
443     plog(XLOG_ERROR, "unable to read passwd file %s: %m", passwdfile);
444     return;
445   }
446   plog(XLOG_INFO, "reading password entries from file %s", passwdfile);
447 
448   passwd_line = 0;
449   memset((char *) &passwd_ent, 0, sizeof(struct passwd));
450   passwd_ent.pw_name = (char *) &pw_name;
451   passwd_ent.pw_dir = (char *) &pw_dir;
452 }
453 
454 
455 /* perform de-initialization of user passwd database */
456 static void
457 hlfsd_endpwent(void)
458 {
459   if (!passwdfile) {
460     /*
461      * Don't actually run this because we will be making more passwd calls
462      * afterwards.  On Solaris 2.5.1, making getpwent() calls after calling
463      * endpwent() results in a memory leak! (and no, even Purify didn't
464      * detect it...)
465      *
466      endpwent();
467      */
468     return;
469   }
470 
471   if (passwd_fp) {
472     fclose(passwd_fp);
473   }
474 }
475 
476 
477 /* perform record reading/parsing of individual passwd database records */
478 static struct passwd *
479 hlfsd_getpwent(void)
480 {
481   char buf[256], *cp;
482 
483   /* check if to perform standard unix function */
484   if (!passwdfile) {
485     return getpwent();
486   }
487 
488   /* return here to read another entry */
489 readent:
490 
491   /* return NULL if reached end of file */
492   if (feof(passwd_fp))
493     return NULL;
494 
495   pw_name[0] = pw_dir[0] = '\0';
496 
497   /* read records */
498   buf[0] = '\0';
499   fgets(buf, 256, passwd_fp);
500   passwd_line++;
501   if (!buf || buf[0] == '\0')
502     goto readent;
503 
504   /* read user name */
505   cp = strtok(buf, ":");
506   if (!cp || cp[0] == '\0') {
507     plog(XLOG_ERROR, "no user name on line %d of %s", passwd_line, passwdfile);
508     goto readent;
509   }
510   /* pw_name will show up in passwd_ent.pw_name */
511   xstrlcpy(pw_name, cp, sizeof(pw_name));
512 
513   /* skip passwd */
514   strtok(NULL, ":");
515 
516   /* read uid */
517   cp = strtok(NULL, ":");
518   if (!cp || cp[0] == '\0') {
519     plog(XLOG_ERROR, "no uid on line %d of %s", passwd_line, passwdfile);
520     goto readent;
521   }
522   passwd_ent.pw_uid = atoi(cp);
523 
524   /* skip gid and gcos */
525   strtok(NULL, ":");
526   strtok(NULL, ":");
527 
528   /* read home dir */
529   cp = strtok(NULL, ":");
530   if (!cp || cp[0] == '\0') {
531     plog(XLOG_ERROR, "no home dir on line %d of %s", passwd_line,  passwdfile);
532     goto readent;
533   }
534   /* pw_dir will show up in passwd_ent.pw_dir */
535   xstrlcpy(pw_dir, cp, sizeof(pw_dir));
536 
537   /* the rest of the fields are unimportant and not being considered */
538 
539   plog(XLOG_USER, "hlfsd_getpwent: name=%s, uid=%ld, dir=%s",
540        passwd_ent.pw_name, (long) passwd_ent.pw_uid, passwd_ent.pw_dir);
541 
542   return &passwd_ent;
543 }
544 
545 
546 /*
547  * read and hash the passwd file or NIS map
548  */
549 void
550 plt_init(void)
551 {
552   struct passwd *pent_p;
553 
554   if (plt_reset() < 0)		/* could not reset table. skip. */
555     return;
556 
557   plog(XLOG_INFO, "reading password map");
558 
559   hlfsd_setpwent();			/* prepare to read passwd entries */
560   while ((pent_p = hlfsd_getpwent()) != (struct passwd *) NULL) {
561     table_add(pent_p->pw_uid, pent_p->pw_dir, pent_p->pw_name);
562     if (STREQ("root", pent_p->pw_name)) {
563       int len;
564       if (root_home)
565 	XFREE(root_home);
566       root_home = strdup(pent_p->pw_dir);
567       len = strlen(root_home);
568       /* remove any trailing '/' chars from root's home (even if just one) */
569       while (len > 0 && root_home[len - 1] == '/') {
570 	len--;
571 	root_home[len] = '\0';
572       }
573     }
574   }
575   hlfsd_endpwent();
576 
577   qsort((char *) pwtab, cur_pwtab_num, sizeof(uid2home_t),
578 	plt_compare_fxn);
579   qsort((char *) untab, cur_pwtab_num, sizeof(username2uid_t),
580 	unt_compare_fxn);
581 
582   if (!root_home)
583     root_home = strdup("");
584 
585   plog(XLOG_INFO, "password map read and sorted");
586 }
587 
588 
589 /*
590  * This is essentially so that we don't reset known good lookup tables when a
591  * YP server goes down.
592  */
593 static int
594 plt_reset(void)
595 {
596   int i;
597 
598   hlfsd_setpwent();
599   if (hlfsd_getpwent() == (struct passwd *) NULL) {
600     hlfsd_endpwent();
601     return -1;			/* did not reset table */
602   }
603   hlfsd_endpwent();
604 
605   lastchild = (uid2home_t *) NULL;
606 
607   if (max_pwtab_num > 0)	/* was used already. cleanup old table */
608     for (i = 0; i < cur_pwtab_num; ++i) {
609       if (pwtab[i].home) {
610 	XFREE(pwtab[i].home);
611 	pwtab[i].home = (char *) NULL;
612       }
613       pwtab[i].uid = INVALIDID;	/* not a valid uid (yet...) */
614       pwtab[i].child = (pid_t) 0;
615       pwtab[i].uname = (char *) NULL;	/* only a ptr to untab[i].username */
616       if (untab[i].username) {
617 	XFREE(untab[i].username);
618 	untab[i].username = (char *) NULL;
619       }
620       untab[i].uid = INVALIDID;	/* invalid uid */
621       untab[i].home = (char *) NULL;	/* only a ptr to pwtab[i].home  */
622     }
623   cur_pwtab_num = 0;		/* zero current size */
624 
625   if (root_home)
626     XFREE(root_home);
627 
628   return 0;			/* resetting ok */
629 }
630 
631 
632 /*
633  * u: uid number
634  * h: home directory
635  * n: user ID name
636  */
637 static void
638 table_add(u_int u, const char *h, const char *n)
639 {
640   int i;
641 
642   if (max_pwtab_num <= 0) {	/* was never initialized */
643     max_pwtab_num = 1;
644     pwtab = (uid2home_t *) xmalloc(max_pwtab_num *
645 				   sizeof(uid2home_t));
646     memset((char *) &pwtab[0], 0, max_pwtab_num * sizeof(uid2home_t));
647     untab = (username2uid_t *) xmalloc(max_pwtab_num *
648 				       sizeof(username2uid_t));
649     memset((char *) &untab[0], 0, max_pwtab_num * sizeof(username2uid_t));
650   }
651 
652   /* check if need more space. */
653   if (cur_pwtab_num + 1 > max_pwtab_num) {
654     /* need more space in table */
655     max_pwtab_num *= 2;
656     plog(XLOG_INFO, "reallocating table spaces to %d entries", max_pwtab_num);
657     pwtab = (uid2home_t *) xrealloc(pwtab,
658 				    sizeof(uid2home_t) * max_pwtab_num);
659     untab = (username2uid_t *) xrealloc(untab,
660 					sizeof(username2uid_t) *
661 					max_pwtab_num);
662     /* zero out newly added entries */
663     for (i=cur_pwtab_num; i<max_pwtab_num; ++i) {
664       memset((char *) &pwtab[i], 0, sizeof(uid2home_t));
665       memset((char *) &untab[i], 0, sizeof(username2uid_t));
666     }
667   }
668 
669   /* do NOT add duplicate entries (this is an O(N^2) algorithm... */
670   for (i=0; i<cur_pwtab_num; ++i)
671     if (u == pwtab[i].uid  &&  u != 0 ) {
672       dlog("ignoring duplicate home %s for uid %d (already %s)",
673 	   h, u, pwtab[i].home);
674       return;
675     }
676 
677   /* add new password entry */
678   pwtab[cur_pwtab_num].home = strdup(h);
679   pwtab[cur_pwtab_num].child = 0;
680   pwtab[cur_pwtab_num].last_access_time = 0;
681   pwtab[cur_pwtab_num].last_status = 0;	/* assume best: used homedir */
682   pwtab[cur_pwtab_num].uid = u;
683 
684   /* add new userhome entry */
685   untab[cur_pwtab_num].username = strdup(n);
686 
687   /* just a second pointer */
688   pwtab[cur_pwtab_num].uname = untab[cur_pwtab_num].username;
689   untab[cur_pwtab_num].uid = u;
690   untab[cur_pwtab_num].home = pwtab[cur_pwtab_num].home;	/* a ptr */
691 
692   /* increment counter */
693   ++cur_pwtab_num;
694 }
695 
696 
697 /*
698  * return entry in lookup table
699  */
700 uid2home_t *
701 plt_search(u_int u)
702 {
703   int max, min, mid;
704 
705   /*
706    * empty table should not happen,
707    * but I have a bug with signals to trace...
708    */
709   if (pwtab == (uid2home_t *) NULL)
710     return (uid2home_t *) NULL;
711 
712   max = cur_pwtab_num - 1;
713   min = 0;
714 
715   do {
716     mid = (max + min) / 2;
717     if (pwtab[mid].uid == u)	/* record found! */
718       return &pwtab[mid];
719     if (pwtab[mid].uid > u)
720       max = mid;
721     else
722       min = mid;
723   } while (max > min + 1);
724 
725   if (pwtab[max].uid == u)
726     return &pwtab[max];
727   if (pwtab[min].uid == u)
728     return &pwtab[min];
729 
730   /* if gets here then record was not found */
731   return (uid2home_t *) NULL;
732 }
733 
734 
735 #if defined(DEBUG) || defined(DEBUG_PRINT)
736 void
737 plt_print(int signum)
738 {
739   FILE *dumpfile;
740   int dumpfd;
741   char dumptmp[] = "/usr/tmp/hlfsd.dump.XXXXXX";
742   int i;
743 
744 #ifdef HAVE_MKSTEMP
745   dumpfd = mkstemp(dumptmp);
746 #else /* not HAVE_MKSTEMP */
747   mktemp(dumptmp);
748   if (!dumptmp) {
749     plog(XLOG_ERROR, "cannot create temporary dump file");
750     return;
751   }
752   dumpfd = open(dumptmp, O_RDONLY);
753 #endif /* not HAVE_MKSTEMP */
754   if (dumpfd < 0) {
755     plog(XLOG_ERROR, "cannot open temporary dump file");
756     return;
757   }
758   if ((dumpfile = fdopen(dumpfd, "a")) != NULL) {
759     plog(XLOG_INFO, "dumping internal state to file %s", dumptmp);
760     fprintf(dumpfile, "\n\nNew plt_dump():\n");
761     for (i = 0; i < cur_pwtab_num; ++i)
762       fprintf(dumpfile,
763 	      "%4d %5lu %10lu %1d %4lu \"%s\" uname=\"%s\"\n",
764 	      i,
765 	      (long) pwtab[i].child,
766 	      pwtab[i].last_access_time,
767 	      pwtab[i].last_status,
768 	      (long) pwtab[i].uid,
769 	      pwtab[i].home,
770 	      pwtab[i].uname);
771     fprintf(dumpfile, "\nUserName table by plt_print():\n");
772     for (i = 0; i < cur_pwtab_num; ++i)
773       fprintf(dumpfile, "%4d : \"%s\" %4lu \"%s\"\n", i,
774 	      untab[i].username, (long) untab[i].uid, untab[i].home);
775     close(dumpfd);
776     fclose(dumpfile);
777   }
778 }
779 
780 
781 void
782 plt_dump(uid2home_t *lastc, pid_t this)
783 {
784   FILE *dumpfile;
785   int i;
786 
787   if ((dumpfile = fopen("/var/tmp/hlfsdump", "a")) != NULL) {
788     fprintf(dumpfile, "\n\nNEW PLT_DUMP -- ");
789     fprintf(dumpfile, "lastchild->child=%d ",
790 	    (int) (lastc ? lastc->child : -999));
791     fprintf(dumpfile, ", child from wait3=%lu:\n", (long) this);
792     for (i = 0; i < cur_pwtab_num; ++i)
793       fprintf(dumpfile, "%4d %5lu: %4lu \"%s\" uname=\"%s\"\n", i,
794 	      (long) pwtab[i].child, (long) pwtab[i].uid,
795 	      pwtab[i].home, pwtab[i].uname);
796     fprintf(dumpfile, "\nUserName table by plt_dump():\n");
797     for (i = 0; i < cur_pwtab_num; ++i)
798       fprintf(dumpfile, "%4d : \"%s\" %4lu \"%s\"\n", i,
799 	      untab[i].username, (long) untab[i].uid, untab[i].home);
800     fprintf(dumpfile, "ezk: ent=%d, uid=%lu, home=\"%s\"\n",
801 	    untab_index("ezk"),
802 	    (long) untab[untab_index("ezk")].uid,
803 	    pwtab[untab[untab_index("ezk")].uid].home);
804     fprintf(dumpfile, "rezk: ent=%d, uid=%lu, home=\"%s\"\n",
805 	    untab_index("rezk"),
806 	    (long) untab[untab_index("rezk")].uid,
807 	    pwtab[untab[untab_index("rezk")].uid].home);
808     fclose(dumpfile);
809   }
810 }
811 #endif /* defined(DEBUG) || defined(DEBUG_PRINT) */
812