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