1a6d9b409Smrg/*
2a6d9b409Smrg * Copyright 1990, 1998  The Open Group
3a6d9b409Smrg *
4a6d9b409Smrg * Permission to use, copy, modify, distribute, and sell this software and its
5a6d9b409Smrg * documentation for any purpose is hereby granted without fee, provided that
6a6d9b409Smrg * the above copyright notice appear in all copies and that both that
7a6d9b409Smrg * copyright notice and this permission notice appear in supporting
8a6d9b409Smrg * documentation.
9a097bd00Smrg *
10a6d9b409Smrg * The above copyright notice and this permission notice shall be included
11a6d9b409Smrg * in all copies or substantial portions of the Software.
12a097bd00Smrg *
13a6d9b409Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14a6d9b409Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15a6d9b409Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16a6d9b409Smrg * IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
17a6d9b409Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
18a6d9b409Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
19a6d9b409Smrg * OTHER DEALINGS IN THE SOFTWARE.
20a097bd00Smrg *
21a6d9b409Smrg * Except as contained in this notice, the name of The Open Group shall
22a6d9b409Smrg * not be used in advertising or otherwise to promote the sale, use or
23a6d9b409Smrg * other dealings in this Software without prior written authorization
24a6d9b409Smrg * from The Open Group.
25a6d9b409Smrg *
26a6d9b409Smrg */
27a6d9b409Smrg
28a097bd00Smrg/*
29838f5b0bSmrg * Copyright (c) 2005, Oracle and/or its affiliates.
30a6d9b409Smrg *
31a6d9b409Smrg * Permission is hereby granted, free of charge, to any person obtaining a
321e5fa1c5Smrg * copy of this software and associated documentation files (the "Software"),
331e5fa1c5Smrg * to deal in the Software without restriction, including without limitation
341e5fa1c5Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
351e5fa1c5Smrg * and/or sell copies of the Software, and to permit persons to whom the
361e5fa1c5Smrg * Software is furnished to do so, subject to the following conditions:
371e5fa1c5Smrg *
381e5fa1c5Smrg * The above copyright notice and this permission notice (including the next
391e5fa1c5Smrg * paragraph) shall be included in all copies or substantial portions of the
401e5fa1c5Smrg * Software.
411e5fa1c5Smrg *
421e5fa1c5Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
431e5fa1c5Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
441e5fa1c5Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
451e5fa1c5Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
461e5fa1c5Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
471e5fa1c5Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
481e5fa1c5Smrg * DEALINGS IN THE SOFTWARE.
49a6d9b409Smrg */
50a6d9b409Smrg
51a6d9b409Smrg/*
52a6d9b409Smrg * Author:  Keith Packard, MIT X Consortium
53a6d9b409Smrg * Lastlog support and dynamic utmp entry allocation
54a6d9b409Smrg *   by Andreas Stolcke <stolcke@icsi.berkeley.edu>
55a6d9b409Smrg */
56a6d9b409Smrg
57a6d9b409Smrg/*
58a6d9b409Smrg * sessreg
59a6d9b409Smrg *
60a6d9b409Smrg * simple wtmp/utmp frobber
61a6d9b409Smrg *
62a6d9b409Smrg * usage: sessreg [ -w <wtmp-file> ] [ -u <utmp-file> ]
63a6d9b409Smrg *		  [ -l <line> ]
64a097bd00Smrg *		  [ -L <lastlog-file> ]		      / #ifdef USE_LASTLOG
65a6d9b409Smrg *		  [ -h <host-name> ]				/ BSD only
66a6d9b409Smrg *		  [ -s <slot-number> ] [ -x Xservers-file ]	/ BSD only
67a6d9b409Smrg *		  [ -t <ttys-file> ]				/ BSD only
68a097bd00Smrg *		  [ -a ] [ -d ] user-name
69a6d9b409Smrg *
70a6d9b409Smrg * one of -a or -d must be specified
71a6d9b409Smrg */
72a6d9b409Smrg
73a6d9b409Smrg#include "sessreg.h"
74a6d9b409Smrg
75a097bd00Smrg#include <X11/Xos.h>
76a097bd00Smrg#include <X11/Xfuncs.h>
77b91640d3Smrg#include <X11/Xfuncproto.h>
78a097bd00Smrg#include <stdio.h>
79a097bd00Smrg#include <stdlib.h>
80a6d9b409Smrg#include <time.h>
81a6d9b409Smrg
821e5fa1c5Smrg#ifdef USE_UTMP
831e5fa1c5Smrgstatic void set_utmp (struct utmp *u, char *line, char *user, char *host,
84a097bd00Smrg		      time_t date, int addp);
851e5fa1c5Smrg#endif
86a6d9b409Smrg
87a6d9b409Smrg#ifdef USE_UTMPX
88a6d9b409Smrgstatic void set_utmpx (struct utmpx *u, const char *line, const char *user,
89a097bd00Smrg		       const char *host, time_t date, int addp);
90a6d9b409Smrg#endif
91a6d9b409Smrg
92efda2da8Smrgstatic int wflag, uflag, lflag;
93a097bd00Smrgstatic const char *wtmp_file, *utmp_file;
94a6d9b409Smrg#ifdef USE_UTMPX
951e5fa1c5Smrg#ifdef HAVE_UPDWTMPX
96a097bd00Smrgstatic const char *wtmpx_file = NULL;
971e5fa1c5Smrg#endif
981e5fa1c5Smrg#ifdef HAVE_UTMPXNAME
99a097bd00Smrgstatic const char *utmpx_file = NULL;
1001e5fa1c5Smrg#endif
101a6d9b409Smrg#endif
102a097bd00Smrg
103efda2da8Smrgstatic int utmp_none, wtmp_none;
104a6d9b409Smrg/*
105a6d9b409Smrg * BSD specific variables.  To make life much easier for Xstartup/Xreset
106a6d9b409Smrg * maintainers, these arguments are accepted but ignored for sysV
107a6d9b409Smrg */
108a097bd00Smrgstatic int hflag, xflag, tflag;
109efda2da8Smrgstatic char *host_name = NULL;
110a097bd00Smrg#if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
111a097bd00Smrgstatic int sflag;
112efda2da8Smrgstatic int slot_number;
1131e5fa1c5Smrg#endif
114efda2da8Smrgstatic char *xservers_file, *ttys_file;
115efda2da8Smrgstatic char *user_name;
116efda2da8Smrgstatic int aflag, dflag;
117a097bd00Smrg#ifdef USE_LASTLOG
118a097bd00Smrgstatic const char *llog_file;
119efda2da8Smrgstatic int llog_none, Lflag;
120a6d9b409Smrg#endif
121a6d9b409Smrg
122efda2da8Smrgstatic char *program_name;
123a6d9b409Smrg
124a097bd00Smrg#if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
125a6d9b409Smrgstatic int findslot (char *line_name, char *host_name, int addp, int slot);
126a6d9b409Smrgstatic int Xslot (char *ttys_file, char *servers_file, char *tty_line,
127a6d9b409Smrg		  char *host_name, int addp);
128a6d9b409Smrg#endif
129a6d9b409Smrg
130b91640d3Smrgstatic void _X_NORETURN _X_COLD
131a6d9b409Smrgusage (int x)
132a6d9b409Smrg{
133b91640d3Smrg	fprintf (stderr,
134b91640d3Smrg		 "%s: usage %s {-a -d} [-w wtmp-file] [-u utmp-file]"
135a097bd00Smrg#ifdef USE_LASTLOG
136b91640d3Smrg		 " [-L lastlog-file]"
137a6d9b409Smrg#endif
138b91640d3Smrg		 "\n"
139b91640d3Smrg		 "             [-t ttys-file] [-l line-name] [-h host-name] [-V]\n"
140b91640d3Smrg		 "             [-s slot-number] [-x servers-file] user-name\n",
141b91640d3Smrg		 program_name, program_name);
142b91640d3Smrg	exit (x);
143a6d9b409Smrg}
144a6d9b409Smrg
145a6d9b409Smrgstatic char *
146a6d9b409Smrggetstring (char ***avp, int *flagp)
147a6d9b409Smrg{
148a6d9b409Smrg	char	**a = *avp;
149b91640d3Smrg	char	*flag = *a;
150a6d9b409Smrg
151b91640d3Smrg	if (*flagp != 0) {
152b91640d3Smrg		fprintf (stderr, "%s: cannot give more than one -%s option\n",
153b91640d3Smrg			 program_name, flag);
154b91640d3Smrg		usage (1);
155b91640d3Smrg	}
156b91640d3Smrg	*flagp = 1;
157b91640d3Smrg	/* if the argument is given immediately following the flag,
158b91640d3Smrg	   i.e. "sessreg -hfoo ...", not "sessreg -h foo ...",
159b91640d3Smrg	   then return the rest of the string as the argument value */
160a6d9b409Smrg	if (*++*a)
161a6d9b409Smrg		return *a;
162b91640d3Smrg	/* else use the next pointer in the argv list as the argument value */
163a6d9b409Smrg	++a;
164b91640d3Smrg	if (!*a) {
165b91640d3Smrg		fprintf (stderr, "%s: -%s requires an argument\n",
166b91640d3Smrg			 program_name, flag);
167b91640d3Smrg		usage (1);
168b91640d3Smrg	}
169a6d9b409Smrg	*avp = a;
170a6d9b409Smrg	return *a;
171a6d9b409Smrg}
172a6d9b409Smrg
173a097bd00Smrg#if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
174a6d9b409Smrgstatic int
175a6d9b409Smrgsyserr (int x, const char *s)
176a6d9b409Smrg{
177a6d9b409Smrg	if (x == -1) {
178a6d9b409Smrg		perror (s);
179a6d9b409Smrg		exit (1);
180a6d9b409Smrg	}
181a6d9b409Smrg	return x;
182a6d9b409Smrg}
183a6d9b409Smrg#endif
184a6d9b409Smrg
185a6d9b409Smrgstatic int
186a6d9b409Smrgsysnerr (int x, const char *s)
187a6d9b409Smrg{
188a6d9b409Smrg	if (x == 0) {
189a6d9b409Smrg		perror (s);
190a6d9b409Smrg		exit (1);
191a6d9b409Smrg	}
192a6d9b409Smrg	return x;
193a6d9b409Smrg}
194a6d9b409Smrg
1958b8e729cSmrg/*
1968b8e729cSmrg * While this looks like it could be replaced with strlcpy() on platforms
1978b8e729cSmrg * that have it, we're sticking with strncpy() so that we zero out the
1988b8e729cSmrg * whole buffer to avoid writing garbage to the fixed length fields in the
1998b8e729cSmrg * utmp/wtmp files, since strlcpy() does not write past the \0 terminator.
2008b8e729cSmrg */
2018b8e729cSmrgstatic void
2028b8e729cSmrgsafe_strncpy(char *dest, const char *src, size_t n)
2038b8e729cSmrg{
20492e95ad1Smrg    if (n > 0) {
20592e95ad1Smrg        strncpy(dest, src, n - 1);
2068b8e729cSmrg        dest[n - 1] = '\0';
20792e95ad1Smrg    }
2088b8e729cSmrg}
2098b8e729cSmrg
210a6d9b409Smrgint
211a6d9b409Smrgmain (int argc, char **argv)
212a6d9b409Smrg{
213a097bd00Smrg#if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
214a6d9b409Smrg	int		utmp;
215a6d9b409Smrg#endif
216a097bd00Smrg#ifndef USE_UTMPX
217a6d9b409Smrg	int		wtmp;
218a097bd00Smrg#endif
219a097bd00Smrg	time_t		current_time;
2201e5fa1c5Smrg#ifdef USE_UTMP
221a6d9b409Smrg	struct utmp	utmp_entry;
2221e5fa1c5Smrg#endif
223a6d9b409Smrg#ifdef USE_UTMPX
224a6d9b409Smrg	struct utmpx	utmpx_entry;
225a6d9b409Smrg#endif
226a097bd00Smrg	char *		line = NULL;
227a6d9b409Smrg
228a6d9b409Smrg	program_name = argv[0];
229a6d9b409Smrg	while (*++argv && **argv == '-') {
230a6d9b409Smrg		switch (*++*argv) {
231a6d9b409Smrg		case 'w':
232a6d9b409Smrg			wtmp_file = getstring (&argv, &wflag);
233a6d9b409Smrg			if (!strcmp (wtmp_file, "none"))
234a6d9b409Smrg				wtmp_none = 1;
235b91640d3Smrg#if defined(USE_UTMPX) && defined(HAVE_UPDWTMPX)
236b91640d3Smrg			else
237b91640d3Smrg				wtmpx_file = wtmp_file;
238b91640d3Smrg#endif
239a6d9b409Smrg			break;
240a6d9b409Smrg		case 'u':
241a6d9b409Smrg			utmp_file = getstring (&argv, &uflag);
242a6d9b409Smrg			if (!strcmp (utmp_file, "none"))
243a6d9b409Smrg				utmp_none = 1;
244b91640d3Smrg#if defined(USE_UTMPX) && defined(HAVE_UTMPXNAME)
245b91640d3Smrg			else
246b91640d3Smrg				utmpx_file = utmp_file;
247b91640d3Smrg#endif
248a6d9b409Smrg			break;
249a097bd00Smrg#ifdef USE_LASTLOG
250a6d9b409Smrg		case 'L':
251a6d9b409Smrg			llog_file = getstring (&argv, &Lflag);
252a6d9b409Smrg			if (!strcmp (llog_file, "none"))
253a6d9b409Smrg				llog_none = 1;
254a6d9b409Smrg			break;
255a6d9b409Smrg#endif
256a6d9b409Smrg		case 't':
257a6d9b409Smrg			ttys_file = getstring (&argv, &tflag);
258a6d9b409Smrg			break;
259a6d9b409Smrg		case 'l':
260a6d9b409Smrg			line = getstring (&argv, &lflag);
261a6d9b409Smrg			break;
262a6d9b409Smrg		case 'h':
263a6d9b409Smrg			host_name = getstring (&argv, &hflag);
264a6d9b409Smrg			break;
265a6d9b409Smrg		case 's':
266a097bd00Smrg#if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
267a6d9b409Smrg			slot_number = atoi (getstring (&argv, &sflag));
2681e5fa1c5Smrg#endif
269a6d9b409Smrg			break;
270a6d9b409Smrg		case 'x':
271a6d9b409Smrg			xservers_file = getstring (&argv, &xflag);
272a6d9b409Smrg			break;
273a6d9b409Smrg		case 'a':
274a6d9b409Smrg			aflag++;
275a6d9b409Smrg			break;
276a6d9b409Smrg		case 'd':
277a6d9b409Smrg			dflag++;
278a6d9b409Smrg			break;
279a097bd00Smrg		case 'V':
280838f5b0bSmrg			puts(PACKAGE_STRING);
281a097bd00Smrg			exit (0);
282838f5b0bSmrg                case '-':
283838f5b0bSmrg			if (strcmp(*argv, "-help") == 0) {
284838f5b0bSmrg				usage (0);
285838f5b0bSmrg			}
286838f5b0bSmrg			if (strcmp(*argv, "-version") == 0) {
287838f5b0bSmrg				puts (PACKAGE_STRING);
288838f5b0bSmrg				exit (0);
289838f5b0bSmrg			}
290838f5b0bSmrg			/* fallthrough */
291a6d9b409Smrg		default:
292838f5b0bSmrg			fprintf (stderr, "%s: unrecognized option '-%s'\n",
293b91640d3Smrg				 program_name, argv[0]);
294a6d9b409Smrg			usage (1);
295a6d9b409Smrg		}
296a6d9b409Smrg	}
297b91640d3Smrg	user_name = *argv++;
298b91640d3Smrg	if (user_name == NULL) {
299b91640d3Smrg		fprintf (stderr, "%s: missing required user-name argument\n",
300b91640d3Smrg			 program_name);
301b91640d3Smrg		usage (1);
302b91640d3Smrg	}
303b91640d3Smrg	if (*argv != NULL) {
304b91640d3Smrg		fprintf (stderr, "%s: unrecognized argument '%s'\n",
305b91640d3Smrg			 program_name, argv[0]);
306b91640d3Smrg		usage (1);
307b91640d3Smrg	}
308a6d9b409Smrg	/*
309a6d9b409Smrg	 * complain if neither aflag nor dflag are set,
310a6d9b409Smrg	 * or if both are set.
311a6d9b409Smrg	 */
312b91640d3Smrg	if (!(aflag ^ dflag)) {
313b91640d3Smrg		fprintf (stderr, "%s: must specify exactly one of -a or -d\n",
314b91640d3Smrg			 program_name);
315b91640d3Smrg		usage (1);
316b91640d3Smrg	}
317b91640d3Smrg	if (xflag && !lflag) {
318b91640d3Smrg		fprintf (stderr, "%s: must specify -l when -x is used\n",
319b91640d3Smrg			 program_name);
320b91640d3Smrg		usage (1);
321b91640d3Smrg	}
322a6d9b409Smrg	/* set up default file names */
323a6d9b409Smrg	if (!wflag) {
324a6d9b409Smrg		wtmp_file = WTMP_FILE;
3251e5fa1c5Smrg#if defined(USE_UTMPX) && defined(HAVE_UPDWTMPX)
326a6d9b409Smrg		wtmpx_file = WTMPX_FILE;
327a6d9b409Smrg#endif
328a6d9b409Smrg	}
329a6d9b409Smrg	if (!uflag) {
330a6d9b409Smrg		utmp_file = UTMP_FILE;
3311e5fa1c5Smrg#if defined(USE_UTMPX) && defined(HAVE_UTMPXNAME)
332a6d9b409Smrg		utmpx_file = UTMPX_FILE;
333a6d9b409Smrg#endif
334a6d9b409Smrg	}
335a097bd00Smrg#ifdef USE_LASTLOG
336a6d9b409Smrg	if (!Lflag)
337a6d9b409Smrg		llog_file = LLOG_FILE;
338a6d9b409Smrg#endif
339a097bd00Smrg#if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
340a6d9b409Smrg	if (!tflag)
341a6d9b409Smrg		ttys_file = TTYS_FILE;
342a6d9b409Smrg	if (!sflag && !utmp_none) {
343a6d9b409Smrg		if (xflag)
344a6d9b409Smrg			sysnerr (slot_number = Xslot (ttys_file, xservers_file, line, host_name, aflag), "Xslot");
345a6d9b409Smrg		else
346a6d9b409Smrg			sysnerr (slot_number = ttyslot (), "ttyslot");
347a6d9b409Smrg	}
348a6d9b409Smrg#endif
349a6d9b409Smrg	if (!lflag) {
350a097bd00Smrg		sysnerr ((line = ttyname (0)) != NULL, "ttyname");
351a097bd00Smrg		if (strncmp(line, "/dev/", 5) == 0)
352a097bd00Smrg			line += 5;
353a6d9b409Smrg	}
354a6d9b409Smrg	time (&current_time);
3551e5fa1c5Smrg#ifdef USE_UTMP
356a6d9b409Smrg	set_utmp (&utmp_entry, line, user_name, host_name, current_time, aflag);
3571e5fa1c5Smrg#endif
358a6d9b409Smrg
359a6d9b409Smrg#ifdef USE_UTMPX
360a6d9b409Smrg	/* need to set utmpxname() before calling set_utmpx() for
361a6d9b409Smrg	   UtmpxIdOpen to work */
3621e5fa1c5Smrg# ifdef HAVE_UTMPXNAME
363a6d9b409Smrg	if (utmpx_file != NULL) {
364a097bd00Smrg		utmpxname (utmpx_file);
365a6d9b409Smrg	}
3661e5fa1c5Smrg# endif
367a6d9b409Smrg	set_utmpx (&utmpx_entry, line, user_name,
368a6d9b409Smrg		   host_name, current_time, aflag);
369a097bd00Smrg#endif
370a6d9b409Smrg
371a6d9b409Smrg	if (!utmp_none) {
372a6d9b409Smrg#ifdef USE_UTMPX
373a097bd00Smrg# ifdef HAVE_UTMPXNAME
374a097bd00Smrg		if (utmpx_file != NULL)
3751e5fa1c5Smrg# endif
376a097bd00Smrg		{
377a097bd00Smrg			setutxent ();
378a097bd00Smrg			(void) getutxid (&utmpx_entry);
379a097bd00Smrg			pututxline (&utmpx_entry);
380a097bd00Smrg			endutxent ();
381a097bd00Smrg		}
382a6d9b409Smrg#endif
3831e5fa1c5Smrg#ifdef USE_UTMP
384a097bd00Smrg# ifdef HAVE_PUTUTLINE
385a6d9b409Smrg		utmpname (utmp_file);
386a6d9b409Smrg		setutent ();
387a6d9b409Smrg		(void) getutid (&utmp_entry);
388a6d9b409Smrg		pututline (&utmp_entry);
389a6d9b409Smrg		endutent ();
3901e5fa1c5Smrg# else
391a6d9b409Smrg		utmp = open (utmp_file, O_RDWR);
392a6d9b409Smrg		if (utmp != -1) {
39311a95ff3Smrg			syserr ((int) lseek (utmp, (off_t) slot_number * sizeof (struct utmp), 0), "lseek");
394a6d9b409Smrg			sysnerr (write (utmp, (char *) &utmp_entry, sizeof (utmp_entry))
395a097bd00Smrg					== sizeof (utmp_entry), "write utmp entry");
396a6d9b409Smrg			close (utmp);
397a6d9b409Smrg		}
3981e5fa1c5Smrg# endif
3991e5fa1c5Smrg#endif /* USE_UTMP */
400a6d9b409Smrg	}
401a6d9b409Smrg	if (!wtmp_none) {
402a6d9b409Smrg#ifdef USE_UTMPX
4031e5fa1c5Smrg# ifdef HAVE_UPDWTMPX
404a6d9b409Smrg		if (wtmpx_file != NULL) {
405a6d9b409Smrg			updwtmpx(wtmpx_file, &utmpx_entry);
406a6d9b409Smrg		}
4071e5fa1c5Smrg# endif
408a6d9b409Smrg#else
409a6d9b409Smrg		wtmp = open (wtmp_file, O_WRONLY|O_APPEND);
410a6d9b409Smrg		if (wtmp != -1) {
411a6d9b409Smrg			sysnerr (write (wtmp, (char *) &utmp_entry, sizeof (utmp_entry))
412a097bd00Smrg					== sizeof (utmp_entry), "write wtmp entry");
413a6d9b409Smrg			close (wtmp);
414a6d9b409Smrg		}
415a097bd00Smrg#endif
416a6d9b409Smrg	}
417a097bd00Smrg#ifdef USE_LASTLOG
418a6d9b409Smrg	if (aflag && !llog_none) {
419a097bd00Smrg		int llog;
420a097bd00Smrg		struct passwd *pwd = getpwnam(user_name);
421a6d9b409Smrg
422a097bd00Smrg		sysnerr( pwd != NULL, "get user id");
423a097bd00Smrg		llog = open (llog_file, O_RDWR);
424a6d9b409Smrg
425a6d9b409Smrg		if (llog != -1) {
426a6d9b409Smrg			struct lastlog ll;
427a6d9b409Smrg
42811a95ff3Smrg			sysnerr (lseek(llog, (off_t) (pwd->pw_uid*sizeof(ll)), 0)
429a097bd00Smrg					!= -1, "seeking lastlog entry");
430a097bd00Smrg			memset(&ll, 0, sizeof(ll));
431a6d9b409Smrg			ll.ll_time = current_time;
432a6d9b409Smrg			if (line)
4338b8e729cSmrg			 safe_strncpy (ll.ll_line, line, sizeof (ll.ll_line));
434a6d9b409Smrg			if (host_name)
4358b8e729cSmrg			 safe_strncpy (ll.ll_host, host_name, sizeof (ll.ll_host));
436a6d9b409Smrg
437a6d9b409Smrg			sysnerr (write (llog, (char *) &ll, sizeof (ll))
438a097bd00Smrg					== sizeof (ll), "write lastlog entry");
439a6d9b409Smrg			close (llog);
440a6d9b409Smrg		}
441a6d9b409Smrg	}
442a6d9b409Smrg#endif
443a6d9b409Smrg	return 0;
444a6d9b409Smrg}
445a6d9b409Smrg
446a6d9b409Smrg/*
447a6d9b409Smrg * fill in the appropriate records of the utmp entry
448a6d9b409Smrg */
449a6d9b409Smrg
4501e5fa1c5Smrg#ifdef USE_UTMP
451a6d9b409Smrgstatic void
452a097bd00Smrgset_utmp (struct utmp *u, char *line, char *user, char *host, time_t date, int addp)
453a6d9b409Smrg{
454a097bd00Smrg	memset (u, 0, sizeof (*u));
455a6d9b409Smrg	if (line)
4568b8e729cSmrg		safe_strncpy (u->ut_line, line, sizeof (u->ut_line));
457a6d9b409Smrg	else
458a097bd00Smrg		memset (u->ut_line, 0, sizeof (u->ut_line));
459a6d9b409Smrg	if (addp && user)
4608b8e729cSmrg		safe_strncpy (u->ut_name, user, sizeof (u->ut_name));
461a6d9b409Smrg	else
462a097bd00Smrg		memset (u->ut_name, 0, sizeof (u->ut_name));
463a097bd00Smrg#ifdef HAVE_STRUCT_UTMP_UT_ID
464a6d9b409Smrg	if (line) {
465a097bd00Smrg		size_t	i;
466a6d9b409Smrg		/*
467a6d9b409Smrg		 * this is a bit crufty, but
468a6d9b409Smrg		 * follows the apparent conventions in
469a6d9b409Smrg		 * the ttys file.  ut_id is only 4 bytes
470a6d9b409Smrg		 * long, and the last 4 bytes of the line
471a6d9b409Smrg		 * name are written into it, left justified.
472a6d9b409Smrg		 */
473a6d9b409Smrg		i = strlen (line);
474a6d9b409Smrg		if (i >= sizeof (u->ut_id))
475a6d9b409Smrg			i -= sizeof (u->ut_id);
476a6d9b409Smrg		else
477a6d9b409Smrg			i = 0;
4788b8e729cSmrg		safe_strncpy (u->ut_id, line + i, sizeof (u->ut_id));
479a6d9b409Smrg	} else
480a097bd00Smrg		memset (u->ut_id, 0, sizeof (u->ut_id));
481a097bd00Smrg#endif
482a097bd00Smrg#ifdef HAVE_STRUCT_UTMP_UT_PID
483a097bd00Smrg	if (addp)
484a6d9b409Smrg		u->ut_pid = getppid ();
485a097bd00Smrg	else
486a6d9b409Smrg		u->ut_pid = 0;
487a097bd00Smrg#endif
488a097bd00Smrg#ifdef HAVE_STRUCT_UTMP_UT_TYPE
489a097bd00Smrg	if (addp)
490a097bd00Smrg		u->ut_type = USER_PROCESS;
491a097bd00Smrg	else
492a6d9b409Smrg		u->ut_type = DEAD_PROCESS;
493a6d9b409Smrg#endif
494a097bd00Smrg#ifdef HAVE_STRUCT_UTMP_UT_HOST
495a6d9b409Smrg	if (addp && host)
4968b8e729cSmrg		safe_strncpy (u->ut_host, host, sizeof (u->ut_host));
497a6d9b409Smrg	else
498a097bd00Smrg		memset (u->ut_host, 0, sizeof (u->ut_host));
499a6d9b409Smrg#endif
500a6d9b409Smrg	u->ut_time = date;
501a6d9b409Smrg}
5021e5fa1c5Smrg#endif /* USE_UTMP */
503a6d9b409Smrg
504a6d9b409Smrg#ifdef USE_UTMPX
505a6d9b409Smrgstatic int
506a6d9b409SmrgUtmpxIdOpen( char *utmpId )
507a6d9b409Smrg{
508a6d9b409Smrg	struct utmpx *u;	/* pointer to entry in utmp file           */
509a6d9b409Smrg	int    status = 1;	/* return code                             */
5101e5fa1c5Smrg
5111e5fa1c5Smrg	setutxent();
512a097bd00Smrg
513a6d9b409Smrg	while ( (u = getutxent()) != NULL ) {
514a097bd00Smrg
515a6d9b409Smrg		if ( (strncmp(u->ut_id, utmpId, 4) == 0 ) &&
516a6d9b409Smrg		     u->ut_type != DEAD_PROCESS ) {
517a097bd00Smrg
518a6d9b409Smrg			status = 0;
519a6d9b409Smrg			break;
520a6d9b409Smrg		}
521a6d9b409Smrg	}
522a097bd00Smrg
5231e5fa1c5Smrg	endutxent();
524a6d9b409Smrg	return (status);
525a6d9b409Smrg}
526a6d9b409Smrg
527a6d9b409Smrgstatic void
528a6d9b409Smrgset_utmpx (struct utmpx *u, const char *line, const char *user,
529a097bd00Smrg	   const char *host, time_t date, int addp)
530a6d9b409Smrg{
531a6d9b409Smrg	static const char letters[] =
532a6d9b409Smrg	       "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
533a6d9b409Smrg
534b91640d3Smrg	memset (u, 0, sizeof (*u));
535a097bd00Smrg	if (line)
536a6d9b409Smrg	{
537a097bd00Smrg		if(strcmp(line, ":0") == 0)
538a097bd00Smrg			(void) strcpy(u->ut_line, "console");
539a097bd00Smrg		else
5408b8e729cSmrg			safe_strncpy (u->ut_line, line, sizeof (u->ut_line));
541a6d9b409Smrg
5428b8e729cSmrg		safe_strncpy(u->ut_host, line, sizeof(u->ut_host));
543a097bd00Smrg#ifdef HAVE_STRUCT_UTMPX_UT_SYSLEN
544a097bd00Smrg		u->ut_syslen = strlen(line);
5451e5fa1c5Smrg#endif
546a6d9b409Smrg	}
547a097bd00Smrg	else
548a097bd00Smrg		memset (u->ut_line, 0, sizeof (u->ut_line));
549a097bd00Smrg	if (addp && user)
5508b8e729cSmrg		safe_strncpy (u->ut_user, user, sizeof (u->ut_user));
551a097bd00Smrg	else
552a097bd00Smrg		memset (u->ut_user, 0, sizeof (u->ut_user));
553a097bd00Smrg
554a097bd00Smrg	if (line) {
555a097bd00Smrg		size_t	i;
556a097bd00Smrg		/*
557a097bd00Smrg		 * this is a bit crufty, but
558a097bd00Smrg		 * follows the apparent conventions in
559a097bd00Smrg		 * the ttys file.  ut_id is only 4 bytes
560a097bd00Smrg		 * long, and the last 4 bytes of the line
561a097bd00Smrg		 * name are written into it, left justified.
562a097bd00Smrg		 */
563a097bd00Smrg		i = strlen (line);
564a097bd00Smrg		if (i >= sizeof (u->ut_id))
565a097bd00Smrg			i -= sizeof (u->ut_id);
566a097bd00Smrg		else
567a097bd00Smrg			i = 0;
5688b8e729cSmrg		safe_strncpy (u->ut_id, line + i, sizeof (u->ut_id));
569a6d9b409Smrg
570a6d9b409Smrg		/* make sure there is no entry using identical ut_id */
571a6d9b409Smrg		if (!UtmpxIdOpen(u->ut_id) && addp) {
572a097bd00Smrg			int limit = sizeof(letters) - 1;
573a097bd00Smrg			int t = 0;
574a097bd00Smrg
575a097bd00Smrg			u->ut_id[1] = line[i];
576a097bd00Smrg			u->ut_id[2] = line[i+1];
577a097bd00Smrg			u->ut_id[3] = line[i+2];
578a097bd00Smrg			do {
579a097bd00Smrg				u->ut_id[0] = letters[t];
580a097bd00Smrg				t++;
581a097bd00Smrg			} while (!UtmpxIdOpen(u->ut_id) && (t < limit));
582a097bd00Smrg		}
583a097bd00Smrg		if (!addp && strstr(line, ":") != NULL) {
584a097bd00Smrg			struct utmpx *tmpu;
585a097bd00Smrg
586a097bd00Smrg			while ( (tmpu = getutxent()) != NULL ) {
587a097bd00Smrg				if ( (strcmp(tmpu->ut_host, line) == 0 ) &&
588a097bd00Smrg					tmpu->ut_type != DEAD_PROCESS ) {
589a097bd00Smrg					strncpy(u->ut_id, tmpu->ut_id,
590a6d9b409Smrg						sizeof(u->ut_id));
591a097bd00Smrg					break;
592a097bd00Smrg				}
593a097bd00Smrg			}
594a097bd00Smrg			endutxent();
595a097bd00Smrg		}
596a097bd00Smrg	} else
597a097bd00Smrg		memset (u->ut_id, 0, sizeof (u->ut_id));
598a097bd00Smrg
599a097bd00Smrg	if (addp) {
600a097bd00Smrg		u->ut_pid = getppid ();
601a097bd00Smrg		u->ut_type = USER_PROCESS;
602a097bd00Smrg	} else {
603a097bd00Smrg		u->ut_pid = 0;
604a097bd00Smrg		u->ut_type = DEAD_PROCESS;
605a097bd00Smrg	}
606a6d9b409Smrg	u->ut_tv.tv_sec = date;
607a6d9b409Smrg	u->ut_tv.tv_usec = 0;
608a6d9b409Smrg}
609a6d9b409Smrg#endif /* USE_UTMPX */
610a6d9b409Smrg
611a097bd00Smrg#if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
612a6d9b409Smrg/*
613a6d9b409Smrg * compute the slot-number for an X display.  This is computed
614a6d9b409Smrg * by counting the lines in /etc/ttys and adding the line-number
615a6d9b409Smrg * that the display appears on in Xservers.  This is a poor
61692e95ad1Smrg * design, but is limited by the non-existent interface to utmp.
617a6d9b409Smrg * If host_name is non-NULL, assume it contains the display name,
618a6d9b409Smrg * otherwise use the tty_line argument (i.e., the tty name).
619a6d9b409Smrg */
620a6d9b409Smrg
621a6d9b409Smrgstatic int
622a6d9b409SmrgXslot (char *ttys_file, char *servers_file, char *tty_line, char *host_name,
623a6d9b409Smrg       int addp)
624a6d9b409Smrg{
625a6d9b409Smrg	FILE	*ttys, *servers;
626a6d9b409Smrg	int	c;
627a6d9b409Smrg	int	slot = 1;
628a6d9b409Smrg	int	column0 = 1;
629a6d9b409Smrg	char	servers_line[1024];
630a6d9b409Smrg	char	disp_name[512];
631a6d9b409Smrg	int	len;
632a6d9b409Smrg	char	*pos;
633a6d9b409Smrg
634a6d9b409Smrg	/* remove screen number from the display name */
635a6d9b409Smrg	memset(disp_name, 0, sizeof(disp_name));
636a6d9b409Smrg	strncpy(disp_name, host_name ? host_name : tty_line, sizeof(disp_name)-1);
637a6d9b409Smrg	pos = strrchr(disp_name, ':');
638a6d9b409Smrg	if (pos) {
639a097bd00Smrg		pos = strchr(pos, '.');
640a097bd00Smrg		if (pos)
641a097bd00Smrg			*pos = '\0';
642a6d9b409Smrg	}
643a6d9b409Smrg	sysnerr ((int)(long)(ttys = fopen (ttys_file, "r")), ttys_file);
644a6d9b409Smrg	while ((c = getc (ttys)) != EOF)
645a6d9b409Smrg		if (c == '\n') {
646a6d9b409Smrg			++slot;
647a6d9b409Smrg			column0 = 1;
648a6d9b409Smrg		} else
649a6d9b409Smrg			column0 = 0;
650a6d9b409Smrg	if (!column0)
651a6d9b409Smrg		++slot;
652a6d9b409Smrg	(void) fclose (ttys);
653a6d9b409Smrg	sysnerr ((int)(long)(servers = fopen (servers_file, "r")), servers_file);
654a6d9b409Smrg
655a6d9b409Smrg	len = strlen (disp_name);
656a6d9b409Smrg	column0 = 1;
657a6d9b409Smrg	while (fgets (servers_line, sizeof (servers_line), servers)) {
658a6d9b409Smrg		if (column0 && *servers_line != '#') {
659a6d9b409Smrg			if (!strncmp (disp_name, servers_line, len) &&
660a6d9b409Smrg			    (servers_line[len] == ' ' ||
661a6d9b409Smrg			     servers_line[len] == '\t'))
662a6d9b409Smrg				return slot;
663a6d9b409Smrg			++slot;
664a6d9b409Smrg		}
665a6d9b409Smrg		if (servers_line[strlen(servers_line)-1] != '\n')
666a6d9b409Smrg			column0 = 0;
667a6d9b409Smrg		else
668a6d9b409Smrg			column0 = 1;
669a6d9b409Smrg	}
670a6d9b409Smrg	/*
67192e95ad1Smrg	 * display not found in Xservers file - allocate utmp entry dynamically
672a6d9b409Smrg	 */
673a6d9b409Smrg	return findslot (tty_line, host_name, addp, slot);
674a6d9b409Smrg}
675a6d9b409Smrg
676a6d9b409Smrg/*
677a6d9b409Smrg * find a free utmp slot for the X display.  This allocates a new entry
678a6d9b409Smrg * past the regular tty entries if necessary, reusing existing entries
679a6d9b409Smrg * (identified by (line,hostname)) if possible.
680a6d9b409Smrg */
681a6d9b409Smrgstatic int
682a6d9b409Smrgfindslot (char *line_name, char *host_name, int addp, int slot)
683a6d9b409Smrg{
684a6d9b409Smrg	int	utmp;
685a6d9b409Smrg	struct	utmp entry;
686a6d9b409Smrg	int	found = 0;
687a6d9b409Smrg	int	freeslot = -1;
688a6d9b409Smrg
689a6d9b409Smrg	syserr(utmp = open (utmp_file, O_RDONLY), "open utmp");
690a6d9b409Smrg
691a6d9b409Smrg	/*
692a6d9b409Smrg	 * first, try to locate a previous entry for this display
693a6d9b409Smrg	 * also record location of a free slots in case we need a new one
694a6d9b409Smrg	 */
69511a95ff3Smrg	syserr ((int) lseek (utmp, (off_t) slot * sizeof (struct utmp), 0), "lseek");
696a6d9b409Smrg
697a6d9b409Smrg	if (!host_name)
698a6d9b409Smrg		host_name = "";
699a6d9b409Smrg
700a6d9b409Smrg	while (read (utmp, (char *) &entry, sizeof (entry)) == sizeof (entry)) {
701a6d9b409Smrg		if (strncmp(entry.ut_line, line_name,
702a097bd00Smrg			sizeof(entry.ut_line)) == 0
703a097bd00Smrg#ifdef HAVE_STRUCT_UTMP_UT_HOST
704a097bd00Smrg		    &&
705a6d9b409Smrg		    strncmp(entry.ut_host, host_name,
706a6d9b409Smrg			sizeof(entry.ut_host)) == 0
707a6d9b409Smrg#endif
708a097bd00Smrg		   ) {
709a6d9b409Smrg			found = 1;
710a6d9b409Smrg			break;
711a6d9b409Smrg		}
712a6d9b409Smrg		if (freeslot < 0 && *entry.ut_name == '\0')
713a6d9b409Smrg			freeslot = slot;
714a6d9b409Smrg		++slot;
715a6d9b409Smrg	}
716a6d9b409Smrg
717a6d9b409Smrg	close (utmp);
718a6d9b409Smrg
719a6d9b409Smrg	if (found)
720a6d9b409Smrg		return slot;
721a6d9b409Smrg	else if (!addp)
722a6d9b409Smrg		return 0;	/* trying to delete a non-existing entry */
723a6d9b409Smrg	else if (freeslot < 0)
724a6d9b409Smrg		return slot;	/* first slot past current entries */
725a6d9b409Smrg	else
726a6d9b409Smrg		return freeslot;
727a6d9b409Smrg}
728a6d9b409Smrg#endif
729