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