1/*
2 * Copyright (c) 1997-2006 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. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgment:
21 *      This product includes software developed by the University of
22 *      California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 *    may be used to endorse or promote products derived from this software
25 *    without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 *
40 * File: am-utils/hlfsd/homedir.c
41 *
42 * HLFSD was written at Columbia University Computer Science Department, by
43 * Erez Zadok <ezk@cs.columbia.edu> and Alexander Dupuy <dupuy@cs.columbia.edu>
44 * It is being distributed under the same terms and conditions as amd does.
45 */
46
47#ifdef HAVE_CONFIG_H
48# include <config.h>
49#endif /* HAVE_CONFIG_H */
50#include <am_defs.h>
51#include <hlfsd.h>
52
53
54/*
55 * STATIC VARIABLES AND FUNCTIONS:
56 */
57static FILE *passwd_fp = NULL;
58static char pw_name[16], pw_dir[128];
59static int cur_pwtab_num = 0, max_pwtab_num = 0;
60static int hlfsd_diskspace(char *);
61static int hlfsd_stat(char *, struct stat *);
62static int passwd_line = 0;
63static int plt_reset(void);
64static struct passwd passwd_ent;
65static uid2home_t *lastchild;
66static uid2home_t *pwtab;
67static void delay(uid2home_t *, int);
68static void table_add(u_int, const char *, const char *);
69static char mboxfile[MAXPATHLEN];
70static char *root_home;		/* root's home directory */
71
72/* GLOBAL FUNCTIONS */
73char *homeof(char *username);
74int uidof(char *username);
75
76/* GLOBALS VARIABLES */
77username2uid_t *untab;		/* user name table */
78
79/*
80 * Return the home directory pathname for the user with uid "userid".
81 */
82char *
83homedir(int userid, int groupid)
84{
85  static char linkval[MAXPATHLEN + 1];
86  static struct timeval tp;
87  uid2home_t *found;
88  char *homename;
89  struct stat homestat;
90  int old_groupid, old_userid;
91
92  if ((found = plt_search(userid)) == (uid2home_t *) NULL) {
93    return alt_spooldir;	/* use alt spool for unknown uid */
94  }
95  homename = found->home;
96
97  if (homename[0] != '/' || homename[1] == '\0') {
98    found->last_status = 1;
99    return alt_spooldir;	/* use alt spool for / or rel. home */
100  }
101  if ((int) userid == 0)	/* force all uid 0 to use root's home */
102    xsnprintf(linkval, sizeof(linkval), "%s/%s", root_home, home_subdir);
103  else
104    xsnprintf(linkval, sizeof(linkval), "%s/%s", homename, home_subdir);
105
106  if (noverify) {
107    found->last_status = 0;
108    return linkval;
109  }
110
111  /*
112   * To optimize hlfsd, we don't actually check the validity of the
113   * symlink if it has been checked in the last N seconds.  It is
114   * very likely that the link, machine, and filesystem are still
115   * valid, as long as N is small.  But if N is large, that may not be
116   * true.  That's why the default N is 5 minutes, but we allow the
117   * user to override this value via a command line option.  Note that
118   * we do not update the last_access_time each time it is accessed,
119   * but only once every N seconds.
120   */
121  if (gettimeofday(&tp, (struct timezone *) NULL) < 0) {
122    tp.tv_sec = 0;
123  } else {
124    if ((tp.tv_sec - found->last_access_time) < cache_interval) {
125      if (found->last_status == 0) {
126	return linkval;
127      } else {
128	return alt_spooldir;
129      }
130    } else {
131      found->last_access_time = tp.tv_sec;
132    }
133  }
134
135  /*
136   * only run this forking code if did not ask for -D fork
137   */
138  if (!amuDebug(D_FORK)) {
139    /* fork child to process request if none in progress */
140    if (found->child && kill(found->child, 0))
141      found->child = 0;
142
143    if (found->child)
144      delay(found, 5);		/* wait a bit if in progress */
145    if (found->child) {		/* better safe than sorry - maybe */
146      found->last_status = 1;
147      return alt_spooldir;
148    }
149    if ((found->child = fork()) < 0) {
150      found->last_status = 1;
151      return alt_spooldir;
152    }
153    if (found->child) {		/* PARENT */
154      if (lastchild)
155	dlog("cache spill uid = %ld, pid = %ld, home = %s",
156	     (long) lastchild->uid, (long) lastchild->child,
157	     lastchild->home);
158      lastchild = found;
159      return (char *) NULL;	/* return NULL to parent, so it can continue */
160    }
161  }
162
163  /*
164   * CHILD: (or parent if -D fork)
165   *
166   * Check and create dir if needed.
167   * Check disk space and/or quotas too.
168   *
169   * We don't need to set the _last_status field of found after the fork
170   * in the child, b/c that information would be later determined in
171   * nfsproc_readlink_2() and the correct exit status would be returned
172   * to the parent upon SIGCHLD in interlock().
173   *
174   */
175  am_set_mypid();		/* for logging routines */
176  if ((old_groupid = setgid(groupid)) < 0) {
177    plog(XLOG_WARNING, "could not setgid to %d: %m", groupid);
178    return linkval;
179  }
180  if ((old_userid = seteuid(userid)) < 0) {
181    plog(XLOG_WARNING, "could not seteuid to %d: %m", userid);
182    setgid(old_groupid);
183    return linkval;
184  }
185  if (hlfsd_stat(linkval, &homestat) < 0) {
186    if (errno == ENOENT) {	/* make the spool dir if possible */
187      /* don't use recursive mkdirs here */
188      if (mkdir(linkval, PERS_SPOOLMODE) < 0) {
189	seteuid(old_userid);
190	setgid(old_groupid);
191	plog(XLOG_WARNING, "can't make directory %s: %m", linkval);
192	return alt_spooldir;
193      }
194      /* fall through to testing the disk space / quota */
195    } else {			/* the home dir itself must not exist then */
196      seteuid(old_userid);
197      setgid(old_groupid);
198      plog(XLOG_WARNING, "bad link to %s: %m", linkval);
199      return alt_spooldir;
200    }
201  }
202
203  /*
204   * If gets here, then either the spool dir in the home dir exists,
205   * or it was just created.  In either case, we now need to
206   * test if we can create a small file and write at least one
207   * byte into it.  This will test that we have both enough inodes
208   * and disk blocks to spare, or they fall within the user's quotas too.
209   * We are still seteuid to the user at this point.
210   */
211  if (hlfsd_diskspace(linkval) < 0) {
212    seteuid(old_userid);
213    setgid(old_groupid);
214    plog(XLOG_WARNING, "no more space in %s: %m", linkval);
215    return alt_spooldir;
216  } else {
217    seteuid(old_userid);
218    setgid(old_groupid);
219    return linkval;
220  }
221}
222
223
224static int
225hlfsd_diskspace(char *path)
226{
227  char buf[MAXPATHLEN];
228  int fd, len;
229
230  xsnprintf(buf, sizeof(buf), "%s/._hlfstmp_%lu", path, (long) getpid());
231  if ((fd = open(buf, O_RDWR | O_CREAT, 0600)) < 0) {
232    plog(XLOG_ERROR, "cannot open %s: %m", buf);
233    return -1;
234  }
235  len = strlen(buf);
236  if (write(fd, buf, len) < len) {
237    plog(XLOG_ERROR, "cannot write \"%s\" (%d bytes) to %s : %m", buf, len, buf);
238    close(fd);
239    unlink(buf);		/* cleanup just in case */
240    return -1;
241  }
242  if (unlink(buf) < 0) {
243    plog(XLOG_ERROR, "cannot unlink %s : %m", buf);
244  }
245  close(fd);
246  return 0;
247}
248
249
250static int
251hlfsd_stat(char *path, struct stat *statp)
252{
253  if (stat(path, statp) < 0)
254    return -1;
255  else if (!S_ISDIR(statp->st_mode)) {
256    errno = ENOTDIR;
257    return -1;
258  }
259  return 0;
260}
261
262
263static void
264delay(uid2home_t *found, int secs)
265{
266  struct timeval tv;
267
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, 0, 0, 0, &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 */
285RETSIGTYPE
286interlock(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 *) 0)) > 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 */
328int
329untab_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 */
361int
362uidof(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 */
376char *
377homeof(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
387char *
388mailbox(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
407static int
408plt_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
418static int
419unt_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 */
429static void
430hlfsd_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 */
452static void
453hlfsd_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 */
474static struct passwd *
475hlfsd_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 */
485readent:
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 */
546void
547plt_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 = strdup(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 = strdup("");
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 */
590static int
591plt_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 */
634static void
635table_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 = strdup(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 = strdup(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 */
697uid2home_t *
698plt_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)
733void
734plt_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
778void
779plt_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