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  if (fgets(buf, 256, passwd_fp) == NULL)
494    return NULL;
495  passwd_line++;
496  if (buf[0] == '\0')
497    goto readent;
498
499  /* read user name */
500  cp = strtok(buf, ":");
501  if (!cp || cp[0] == '\0') {
502    plog(XLOG_ERROR, "no user name on line %d of %s", passwd_line, passwdfile);
503    goto readent;
504  }
505  /* pw_name will show up in passwd_ent.pw_name */
506  xstrlcpy(pw_name, cp, sizeof(pw_name));
507
508  /* skip passwd */
509  strtok(NULL, ":");
510
511  /* read uid */
512  cp = strtok(NULL, ":");
513  if (!cp || cp[0] == '\0') {
514    plog(XLOG_ERROR, "no uid on line %d of %s", passwd_line, passwdfile);
515    goto readent;
516  }
517  passwd_ent.pw_uid = atoi(cp);
518
519  /* skip gid and gcos */
520  strtok(NULL, ":");
521  strtok(NULL, ":");
522
523  /* read home dir */
524  cp = strtok(NULL, ":");
525  if (!cp || cp[0] == '\0') {
526    plog(XLOG_ERROR, "no home dir on line %d of %s", passwd_line,  passwdfile);
527    goto readent;
528  }
529  /* pw_dir will show up in passwd_ent.pw_dir */
530  xstrlcpy(pw_dir, cp, sizeof(pw_dir));
531
532  /* the rest of the fields are unimportant and not being considered */
533
534  plog(XLOG_USER, "hlfsd_getpwent: name=%s, uid=%ld, dir=%s",
535       passwd_ent.pw_name, (long) passwd_ent.pw_uid, passwd_ent.pw_dir);
536
537  return &passwd_ent;
538}
539
540
541/*
542 * read and hash the passwd file or NIS map
543 */
544void
545plt_init(void)
546{
547  struct passwd *pent_p;
548
549  if (plt_reset() < 0)		/* could not reset table. skip. */
550    return;
551
552  plog(XLOG_INFO, "reading password map");
553
554  hlfsd_setpwent();			/* prepare to read passwd entries */
555  while ((pent_p = hlfsd_getpwent()) != (struct passwd *) NULL) {
556    table_add(pent_p->pw_uid, pent_p->pw_dir, pent_p->pw_name);
557    if (STREQ("root", pent_p->pw_name)) {
558      int len;
559      if (root_home)
560	XFREE(root_home);
561      root_home = xstrdup(pent_p->pw_dir);
562      len = strlen(root_home);
563      /* remove any trailing '/' chars from root's home (even if just one) */
564      while (len > 0 && root_home[len - 1] == '/') {
565	len--;
566	root_home[len] = '\0';
567      }
568    }
569  }
570  hlfsd_endpwent();
571
572  qsort((char *) pwtab, cur_pwtab_num, sizeof(uid2home_t),
573	plt_compare_fxn);
574  qsort((char *) untab, cur_pwtab_num, sizeof(username2uid_t),
575	unt_compare_fxn);
576
577  if (!root_home)
578    root_home = xstrdup("");
579
580  plog(XLOG_INFO, "password map read and sorted");
581}
582
583
584/*
585 * This is essentially so that we don't reset known good lookup tables when a
586 * YP server goes down.
587 */
588static int
589plt_reset(void)
590{
591  int i;
592
593  hlfsd_setpwent();
594  if (hlfsd_getpwent() == (struct passwd *) NULL) {
595    hlfsd_endpwent();
596    return -1;			/* did not reset table */
597  }
598  hlfsd_endpwent();
599
600  lastchild = (uid2home_t *) NULL;
601
602  if (max_pwtab_num > 0)	/* was used already. cleanup old table */
603    for (i = 0; i < cur_pwtab_num; ++i) {
604      if (pwtab[i].home) {
605	XFREE(pwtab[i].home);
606	pwtab[i].home = (char *) NULL;
607      }
608      pwtab[i].uid = INVALIDID;	/* not a valid uid (yet...) */
609      pwtab[i].child = (pid_t) 0;
610      pwtab[i].uname = (char *) NULL;	/* only a ptr to untab[i].username */
611      if (untab[i].username) {
612	XFREE(untab[i].username);
613	untab[i].username = (char *) NULL;
614      }
615      untab[i].uid = INVALIDID;	/* invalid uid */
616      untab[i].home = (char *) NULL;	/* only a ptr to pwtab[i].home  */
617    }
618  cur_pwtab_num = 0;		/* zero current size */
619
620  if (root_home)
621    XFREE(root_home);
622
623  return 0;			/* resetting ok */
624}
625
626
627/*
628 * u: uid number
629 * h: home directory
630 * n: user ID name
631 */
632static void
633table_add(u_int u, const char *h, const char *n)
634{
635  int i;
636
637  if (max_pwtab_num <= 0) {	/* was never initialized */
638    max_pwtab_num = 1;
639    pwtab = (uid2home_t *) xmalloc(max_pwtab_num *
640				   sizeof(uid2home_t));
641    memset((char *) &pwtab[0], 0, max_pwtab_num * sizeof(uid2home_t));
642    untab = (username2uid_t *) xmalloc(max_pwtab_num *
643				       sizeof(username2uid_t));
644    memset((char *) &untab[0], 0, max_pwtab_num * sizeof(username2uid_t));
645  }
646
647  /* check if need more space. */
648  if (cur_pwtab_num + 1 > max_pwtab_num) {
649    /* need more space in table */
650    max_pwtab_num *= 2;
651    plog(XLOG_INFO, "reallocating table spaces to %d entries", max_pwtab_num);
652    pwtab = (uid2home_t *) xrealloc(pwtab,
653				    sizeof(uid2home_t) * max_pwtab_num);
654    untab = (username2uid_t *) xrealloc(untab,
655					sizeof(username2uid_t) *
656					max_pwtab_num);
657    /* zero out newly added entries */
658    for (i=cur_pwtab_num; i<max_pwtab_num; ++i) {
659      memset((char *) &pwtab[i], 0, sizeof(uid2home_t));
660      memset((char *) &untab[i], 0, sizeof(username2uid_t));
661    }
662  }
663
664  /* do NOT add duplicate entries (this is an O(N^2) algorithm... */
665  for (i=0; i<cur_pwtab_num; ++i)
666    if (u == pwtab[i].uid  &&  u != 0 ) {
667      dlog("ignoring duplicate home %s for uid %d (already %s)",
668	   h, u, pwtab[i].home);
669      return;
670    }
671
672  /* add new password entry */
673  pwtab[cur_pwtab_num].home = xstrdup(h);
674  pwtab[cur_pwtab_num].child = 0;
675  pwtab[cur_pwtab_num].last_access_time = 0;
676  pwtab[cur_pwtab_num].last_status = 0;	/* assume best: used homedir */
677  pwtab[cur_pwtab_num].uid = u;
678
679  /* add new userhome entry */
680  untab[cur_pwtab_num].username = xstrdup(n);
681
682  /* just a second pointer */
683  pwtab[cur_pwtab_num].uname = untab[cur_pwtab_num].username;
684  untab[cur_pwtab_num].uid = u;
685  untab[cur_pwtab_num].home = pwtab[cur_pwtab_num].home;	/* a ptr */
686
687  /* increment counter */
688  ++cur_pwtab_num;
689}
690
691
692/*
693 * return entry in lookup table
694 */
695uid2home_t *
696plt_search(u_int u)
697{
698  int max, min, mid;
699
700  /*
701   * empty table should not happen,
702   * but I have a bug with signals to trace...
703   */
704  if (pwtab == (uid2home_t *) NULL)
705    return (uid2home_t *) NULL;
706
707  max = cur_pwtab_num - 1;
708  min = 0;
709
710  do {
711    mid = (max + min) / 2;
712    if (pwtab[mid].uid == u)	/* record found! */
713      return &pwtab[mid];
714    if (pwtab[mid].uid > u)
715      max = mid;
716    else
717      min = mid;
718  } while (max > min + 1);
719
720  if (pwtab[max].uid == u)
721    return &pwtab[max];
722  if (pwtab[min].uid == u)
723    return &pwtab[min];
724
725  /* if gets here then record was not found */
726  return (uid2home_t *) NULL;
727}
728
729
730#if defined(DEBUG) || defined(DEBUG_PRINT)
731void
732plt_print(int signum)
733{
734  FILE *dumpfile;
735  int dumpfd;
736  char dumptmp[] = "/usr/tmp/hlfsd.dump.XXXXXX";
737  int i;
738
739#ifdef HAVE_MKSTEMP
740  dumpfd = mkstemp(dumptmp);
741#else /* not HAVE_MKSTEMP */
742  mktemp(dumptmp);
743  if (!dumptmp) {
744    plog(XLOG_ERROR, "cannot create temporary dump file");
745    return;
746  }
747  dumpfd = open(dumptmp, O_RDONLY);
748#endif /* not HAVE_MKSTEMP */
749  if (dumpfd < 0) {
750    plog(XLOG_ERROR, "cannot open temporary dump file");
751    return;
752  }
753  if ((dumpfile = fdopen(dumpfd, "a")) != NULL) {
754    plog(XLOG_INFO, "dumping internal state to file %s", dumptmp);
755    fprintf(dumpfile, "\n\nNew plt_dump():\n");
756    for (i = 0; i < cur_pwtab_num; ++i)
757      fprintf(dumpfile,
758	      "%4d %5lu %10lu %1d %4lu \"%s\" uname=\"%s\"\n",
759	      i,
760	      (long) pwtab[i].child,
761	      pwtab[i].last_access_time,
762	      pwtab[i].last_status,
763	      (long) pwtab[i].uid,
764	      pwtab[i].home,
765	      pwtab[i].uname);
766    fprintf(dumpfile, "\nUserName table by plt_print():\n");
767    for (i = 0; i < cur_pwtab_num; ++i)
768      fprintf(dumpfile, "%4d : \"%s\" %4lu \"%s\"\n", i,
769	      untab[i].username, (long) untab[i].uid, untab[i].home);
770    close(dumpfd);
771    fclose(dumpfile);
772  }
773}
774
775
776void
777plt_dump(uid2home_t *lastc, pid_t this)
778{
779  FILE *dumpfile;
780  int i;
781
782  if ((dumpfile = fopen("/var/tmp/hlfsdump", "a")) != NULL) {
783    fprintf(dumpfile, "\n\nNEW PLT_DUMP -- ");
784    fprintf(dumpfile, "lastchild->child=%d ",
785	    (int) (lastc ? lastc->child : -999));
786    fprintf(dumpfile, ", child from wait3=%lu:\n", (long) this);
787    for (i = 0; i < cur_pwtab_num; ++i)
788      fprintf(dumpfile, "%4d %5lu: %4lu \"%s\" uname=\"%s\"\n", i,
789	      (long) pwtab[i].child, (long) pwtab[i].uid,
790	      pwtab[i].home, pwtab[i].uname);
791    fprintf(dumpfile, "\nUserName table by plt_dump():\n");
792    for (i = 0; i < cur_pwtab_num; ++i)
793      fprintf(dumpfile, "%4d : \"%s\" %4lu \"%s\"\n", i,
794	      untab[i].username, (long) untab[i].uid, untab[i].home);
795    fprintf(dumpfile, "ezk: ent=%d, uid=%lu, home=\"%s\"\n",
796	    untab_index("ezk"),
797	    (long) untab[untab_index("ezk")].uid,
798	    pwtab[untab[untab_index("ezk")].uid].home);
799    fprintf(dumpfile, "rezk: ent=%d, uid=%lu, home=\"%s\"\n",
800	    untab_index("rezk"),
801	    (long) untab[untab_index("rezk")].uid,
802	    pwtab[untab[untab_index("rezk")].uid].home);
803    fclose(dumpfile);
804  }
805}
806#endif /* defined(DEBUG) || defined(DEBUG_PRINT) */
807