sessreg.c revision 1e5fa1c5
1/*
2 * Copyright 1990, 1998  The Open Group
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation.
9 *
10 * The above copyright notice and this permission notice shall be included
11 * in all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16 * IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
17 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
18 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
19 * OTHER DEALINGS IN THE SOFTWARE.
20 *
21 * Except as contained in this notice, the name of The Open Group shall
22 * not be used in advertising or otherwise to promote the sale, use or
23 * other dealings in this Software without prior written authorization
24 * from The Open Group.
25 *
26 */
27
28/* Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
29 *
30 * Permission is hereby granted, free of charge, to any person obtaining a
31 * copy of this software and associated documentation files (the "Software"),
32 * to deal in the Software without restriction, including without limitation
33 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
34 * and/or sell copies of the Software, and to permit persons to whom the
35 * Software is furnished to do so, subject to the following conditions:
36 *
37 * The above copyright notice and this permission notice (including the next
38 * paragraph) shall be included in all copies or substantial portions of the
39 * Software.
40 *
41 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
44 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
46 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
47 * DEALINGS IN THE SOFTWARE.
48 */
49
50/*
51 * Author:  Keith Packard, MIT X Consortium
52 * Lastlog support and dynamic utmp entry allocation
53 *   by Andreas Stolcke <stolcke@icsi.berkeley.edu>
54 */
55
56/*
57 * sessreg
58 *
59 * simple wtmp/utmp frobber
60 *
61 * usage: sessreg [ -w <wtmp-file> ] [ -u <utmp-file> ]
62 *		  [ -l <line> ]
63 *		  [ -L <lastlog-file> ]		      / #ifndef NO_LASTLOG
64 *		  [ -h <host-name> ]				/ BSD only
65 *		  [ -s <slot-number> ] [ -x Xservers-file ]	/ BSD only
66 *		  [ -t <ttys-file> ]				/ BSD only
67 *	          [ -a ] [ -d ] user-name
68 *
69 * one of -a or -d must be specified
70 */
71
72#include "sessreg.h"
73
74# include	<X11/Xos.h>
75# include	<X11/Xfuncs.h>
76# include	<stdio.h>
77# include	<stdlib.h>
78
79#if defined(__SVR4) || defined(SVR4) || defined(linux) || defined(__GLIBC__)
80# define SYSV
81#endif
82
83#include <time.h>
84#define Time_t time_t
85
86#ifdef USE_UTMP
87static void set_utmp (struct utmp *u, char *line, char *user, char *host,
88		      Time_t date, int addp);
89#endif
90
91#ifdef USE_UTMPX
92static void set_utmpx (struct utmpx *u, const char *line, const char *user,
93		       const char *host, Time_t date, int addp);
94#endif
95
96static int wflag, uflag, lflag;
97static char *wtmp_file, *utmp_file, *line;
98#ifdef USE_UTMPX
99#ifdef HAVE_UPDWTMPX
100static char *wtmpx_file = NULL;
101#endif
102#ifdef HAVE_UTMPXNAME
103static char *utmpx_file = NULL;
104#endif
105#endif
106static int utmp_none, wtmp_none;
107/*
108 * BSD specific variables.  To make life much easier for Xstartup/Xreset
109 * maintainers, these arguments are accepted but ignored for sysV
110 */
111static int hflag, sflag, xflag, tflag;
112static char *host_name = NULL;
113#ifdef USE_UTMP
114static int slot_number;
115#endif
116static char *xservers_file, *ttys_file;
117static char *user_name;
118static int aflag, dflag;
119#ifndef NO_LASTLOG
120static char *llog_file;
121static int llog_none, Lflag;
122#endif
123
124static char *program_name;
125
126#ifndef SYSV
127static int findslot (char *line_name, char *host_name, int addp, int slot);
128static int Xslot (char *ttys_file, char *servers_file, char *tty_line,
129		  char *host_name, int addp);
130#endif
131
132static int
133usage (int x)
134{
135	if (x) {
136		fprintf (stderr, "%s: usage %s {-a -d} [-w wtmp-file] [-u utmp-file]", program_name, program_name);
137#ifndef NO_LASTLOG
138		fprintf (stderr, " [-L lastlog-file]");
139#endif
140		fprintf (stderr, "\n");
141		fprintf (stderr, "             [-t ttys-file] [-l line-name] [-h host-name]\n");
142		fprintf (stderr, "             [-s slot-number] [-x servers-file] user-name\n");
143		exit (1);
144	}
145	return x;
146}
147
148static char *
149getstring (char ***avp, int *flagp)
150{
151	char	**a = *avp;
152
153	usage ((*flagp)++);
154	if (*++*a)
155		return *a;
156	++a;
157	usage (!*a);
158	*avp = a;
159	return *a;
160}
161
162#ifndef SYSV
163static int
164syserr (int x, const char *s)
165{
166	if (x == -1) {
167		perror (s);
168		exit (1);
169	}
170	return x;
171}
172#endif
173
174static int
175sysnerr (int x, const char *s)
176{
177	if (x == 0) {
178		perror (s);
179		exit (1);
180	}
181	return x;
182}
183
184int
185main (int argc, char **argv)
186{
187#if defined(USE_UTMP) && !defined(SYSV)
188	int		utmp;
189#endif
190	char		*line_tmp;
191#ifndef USE_UTMPX
192	int		wtmp;
193#endif
194	Time_t		current_time;
195#ifdef USE_UTMP
196	struct utmp	utmp_entry;
197#endif
198#ifdef USE_UTMPX
199	struct utmpx	utmpx_entry;
200#endif
201
202	program_name = argv[0];
203	while (*++argv && **argv == '-') {
204		switch (*++*argv) {
205		case 'w':
206			wtmp_file = getstring (&argv, &wflag);
207			if (!strcmp (wtmp_file, "none"))
208				wtmp_none = 1;
209			break;
210		case 'u':
211			utmp_file = getstring (&argv, &uflag);
212			if (!strcmp (utmp_file, "none"))
213				utmp_none = 1;
214			break;
215#ifndef NO_LASTLOG
216		case 'L':
217			llog_file = getstring (&argv, &Lflag);
218			if (!strcmp (llog_file, "none"))
219				llog_none = 1;
220			break;
221#endif
222		case 't':
223			ttys_file = getstring (&argv, &tflag);
224			break;
225		case 'l':
226			line = getstring (&argv, &lflag);
227			break;
228		case 'h':
229			host_name = getstring (&argv, &hflag);
230			break;
231		case 's':
232#ifdef USE_UTMP
233			slot_number = atoi (getstring (&argv, &sflag));
234#endif
235			break;
236		case 'x':
237			xservers_file = getstring (&argv, &xflag);
238			break;
239		case 'a':
240			aflag++;
241			break;
242		case 'd':
243			dflag++;
244			break;
245		default:
246			usage (1);
247		}
248	}
249	usage (!(user_name = *argv++));
250	usage (*argv != NULL);
251	/*
252	 * complain if neither aflag nor dflag are set,
253	 * or if both are set.
254	 */
255	usage (!(aflag ^ dflag));
256	usage (xflag && !lflag);
257	/* set up default file names */
258	if (!wflag) {
259		wtmp_file = WTMP_FILE;
260#if defined(USE_UTMPX) && defined(HAVE_UPDWTMPX)
261		wtmpx_file = WTMPX_FILE;
262#endif
263	}
264#ifndef NO_UTMP
265	if (!uflag) {
266		utmp_file = UTMP_FILE;
267#if defined(USE_UTMPX) && defined(HAVE_UTMPXNAME)
268		utmpx_file = UTMPX_FILE;
269#endif
270	}
271#else
272	utmp_none = 1;
273#endif
274#ifndef NO_LASTLOG
275	if (!Lflag)
276		llog_file = LLOG_FILE;
277#endif
278#if defined(USE_UTMP) && !defined(SYSV) && !defined(linux) && !defined(__QNX__)
279	if (!tflag)
280		ttys_file = TTYS_FILE;
281	if (!sflag && !utmp_none) {
282		if (xflag)
283			sysnerr (slot_number = Xslot (ttys_file, xservers_file, line, host_name, aflag), "Xslot");
284		else
285			sysnerr (slot_number = ttyslot (), "ttyslot");
286	}
287#endif
288	if (!lflag) {
289		sysnerr ((line_tmp = ttyname (0)) != NULL, "ttyname");
290		line = strrchr(line_tmp, '/');
291		if (line)
292			line = line + 1;
293		else
294			line = line_tmp;
295	}
296	time (&current_time);
297#ifdef USE_UTMP
298	set_utmp (&utmp_entry, line, user_name, host_name, current_time, aflag);
299#endif
300
301#ifdef USE_UTMPX
302	/* need to set utmpxname() before calling set_utmpx() for
303	   UtmpxIdOpen to work */
304# ifdef HAVE_UTMPXNAME
305	if (utmpx_file != NULL) {
306	        utmpxname (utmpx_file);
307	}
308# endif
309	set_utmpx (&utmpx_entry, line, user_name,
310		   host_name, current_time, aflag);
311#endif
312
313	if (!utmp_none) {
314#ifdef USE_UTMPX
315# ifdef HAVE_UTMPX_NAME
316	    if (utmpx_file != NULL)
317# endif
318	    {
319		setutxent ();
320		(void) getutxid (&utmpx_entry);
321		pututxline (&utmpx_entry);
322		endutxent ();
323	    }
324#endif
325#ifdef USE_UTMP
326# ifdef SYSV
327		utmpname (utmp_file);
328		setutent ();
329		(void) getutid (&utmp_entry);
330		pututline (&utmp_entry);
331		endutent ();
332# else
333		utmp = open (utmp_file, O_RDWR);
334		if (utmp != -1) {
335			syserr ((int) lseek (utmp, (long) slot_number * sizeof (struct utmp), 0), "lseek");
336			sysnerr (write (utmp, (char *) &utmp_entry, sizeof (utmp_entry))
337				        == sizeof (utmp_entry), "write utmp entry");
338			close (utmp);
339		}
340# endif
341#endif /* USE_UTMP */
342	}
343	if (!wtmp_none) {
344#ifdef USE_UTMPX
345# ifdef HAVE_UPDWTMPX
346		if (wtmpx_file != NULL) {
347			updwtmpx(wtmpx_file, &utmpx_entry);
348		}
349# endif
350#else
351		wtmp = open (wtmp_file, O_WRONLY|O_APPEND);
352		if (wtmp != -1) {
353			sysnerr (write (wtmp, (char *) &utmp_entry, sizeof (utmp_entry))
354				        == sizeof (utmp_entry), "write wtmp entry");
355			close (wtmp);
356		}
357#endif
358	}
359#ifndef NO_LASTLOG
360	if (aflag && !llog_none) {
361	        int llog;
362	        struct passwd *pwd = getpwnam(user_name);
363
364	        sysnerr( pwd != NULL, "get user id");
365	        llog = open (llog_file, O_RDWR);
366
367		if (llog != -1) {
368			struct lastlog ll;
369
370			sysnerr (lseek(llog, (long) pwd->pw_uid*sizeof(ll), 0)
371				        != -1, "seeking lastlog entry");
372			bzero((char *)&ll, sizeof(ll));
373			ll.ll_time = current_time;
374			if (line)
375			 (void) strncpy (ll.ll_line, line, sizeof (ll.ll_line));
376			if (host_name)
377			 (void) strncpy (ll.ll_host, host_name, sizeof (ll.ll_host));
378
379			sysnerr (write (llog, (char *) &ll, sizeof (ll))
380				        == sizeof (ll), "write lastlog entry");
381			close (llog);
382		}
383	}
384#endif
385	return 0;
386}
387
388/*
389 * fill in the appropriate records of the utmp entry
390 */
391
392#ifdef USE_UTMP
393static void
394set_utmp (struct utmp *u, char *line, char *user, char *host, Time_t date, int addp)
395{
396	bzero (u, sizeof (*u));
397	if (line)
398		(void) strncpy (u->ut_line, line, sizeof (u->ut_line));
399	else
400		bzero (u->ut_line, sizeof (u->ut_line));
401	if (addp && user)
402		(void) strncpy (u->ut_name, user, sizeof (u->ut_name));
403	else
404		bzero (u->ut_name, sizeof (u->ut_name));
405#ifdef SYSV
406	if (line) {
407		int	i;
408		/*
409		 * this is a bit crufty, but
410		 * follows the apparent conventions in
411		 * the ttys file.  ut_id is only 4 bytes
412		 * long, and the last 4 bytes of the line
413		 * name are written into it, left justified.
414		 */
415		i = strlen (line);
416		if (i >= sizeof (u->ut_id))
417			i -= sizeof (u->ut_id);
418		else
419			i = 0;
420		(void) strncpy (u->ut_id, line + i, sizeof (u->ut_id));
421	} else
422		bzero (u->ut_id, sizeof (u->ut_id));
423	if (addp) {
424		u->ut_pid = getppid ();
425		u->ut_type = USER_PROCESS;
426	} else {
427		u->ut_pid = 0;
428		u->ut_type = DEAD_PROCESS;
429	}
430#endif
431#if (!defined(SYSV) && !defined(__QNX__)) || defined(linux)
432	if (addp && host)
433		(void) strncpy (u->ut_host, host, sizeof (u->ut_host));
434	else
435		bzero (u->ut_host, sizeof (u->ut_host));
436#endif
437	u->ut_time = date;
438}
439#endif /* USE_UTMP */
440
441#ifdef USE_UTMPX
442static int
443UtmpxIdOpen( char *utmpId )
444{
445	struct utmpx *u;	/* pointer to entry in utmp file           */
446	int    status = 1;	/* return code                             */
447
448	setutxent();
449
450	while ( (u = getutxent()) != NULL ) {
451
452		if ( (strncmp(u->ut_id, utmpId, 4) == 0 ) &&
453		     u->ut_type != DEAD_PROCESS ) {
454
455			status = 0;
456			break;
457		}
458	}
459
460	endutxent();
461	return (status);
462}
463
464static void
465set_utmpx (struct utmpx *u, const char *line, const char *user,
466	   const char *host, Time_t date, int addp)
467{
468	static const char letters[] =
469	       "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
470
471        if (line)
472	{
473                if(strcmp(line, ":0") == 0)
474                        (void) strcpy(u->ut_line, "console");
475                else
476                        (void) strncpy (u->ut_line, line, sizeof (u->ut_line));
477
478		strncpy(u->ut_host, line, sizeof(u->ut_host));
479#if HAVE_UTMPX_UT_SYSLEN
480		u->ut_syslen = strlen(line);
481#endif
482	}
483        else
484                bzero (u->ut_line, sizeof (u->ut_line));
485        if (addp && user)
486                (void) strncpy (u->ut_user, user, sizeof (u->ut_user));
487        else
488                bzero (u->ut_user, sizeof (u->ut_user));
489
490        if (line) {
491                int     i;
492                /*
493                 * this is a bit crufty, but
494                 * follows the apparent conventions in
495                 * the ttys file.  ut_id is only 4 bytes
496                 * long, and the last 4 bytes of the line
497                 * name are written into it, left justified.
498                 */
499                i = strlen (line);
500                if (i >= sizeof (u->ut_id))
501                        i -= sizeof (u->ut_id);
502                else
503                        i = 0;
504                (void) strncpy (u->ut_id, line + i, sizeof (u->ut_id));
505
506		/* make sure there is no entry using identical ut_id */
507		if (!UtmpxIdOpen(u->ut_id) && addp) {
508                        int limit = sizeof(letters) - 1;
509                        int t = 0;
510
511                        u->ut_id[1] = line[i];
512                        u->ut_id[2] = line[i+1];
513                        u->ut_id[3] = line[i+2];
514                        do {
515                                u->ut_id[0] = letters[t];
516                                t++;
517                        } while (!UtmpxIdOpen(u->ut_id) && (t < limit));
518                }
519                if (!addp && strstr(line, ":") != NULL) {
520                        struct utmpx *tmpu;
521
522                        while ( (tmpu = getutxent()) != NULL ) {
523                                if ( (strcmp(tmpu->ut_host, line) == 0 ) &&
524                                        tmpu->ut_type != DEAD_PROCESS ) {
525                                        strncpy(u->ut_id, tmpu->ut_id,
526						sizeof(u->ut_id));
527                                        break;
528                                }
529                        }
530                        endutxent();
531                }
532        } else
533                bzero (u->ut_id, sizeof (u->ut_id));
534
535        if (addp) {
536                u->ut_pid = getppid ();
537                u->ut_type = USER_PROCESS;
538        } else {
539                u->ut_pid = 0;
540                u->ut_type = DEAD_PROCESS;
541        }
542	u->ut_tv.tv_sec = date;
543	u->ut_tv.tv_usec = 0;
544}
545#endif /* USE_UTMPX */
546
547#if defined(USE_UTMP) && !defined(SYSV)
548/*
549 * compute the slot-number for an X display.  This is computed
550 * by counting the lines in /etc/ttys and adding the line-number
551 * that the display appears on in Xservers.  This is a poor
552 * design, but is limited by the non-existant interface to utmp.
553 * If host_name is non-NULL, assume it contains the display name,
554 * otherwise use the tty_line argument (i.e., the tty name).
555 */
556
557static int
558Xslot (char *ttys_file, char *servers_file, char *tty_line, char *host_name,
559       int addp)
560{
561	FILE	*ttys, *servers;
562	int	c;
563	int	slot = 1;
564	int	column0 = 1;
565	char	servers_line[1024];
566	char	disp_name[512];
567	int	len;
568	char	*pos;
569
570	/* remove screen number from the display name */
571	memset(disp_name, 0, sizeof(disp_name));
572	strncpy(disp_name, host_name ? host_name : tty_line, sizeof(disp_name)-1);
573	pos = strrchr(disp_name, ':');
574	if (pos) {
575	    pos = strchr(pos, '.');
576	    if (pos)
577		*pos = '\0';
578	}
579	sysnerr ((int)(long)(ttys = fopen (ttys_file, "r")), ttys_file);
580	while ((c = getc (ttys)) != EOF)
581		if (c == '\n') {
582			++slot;
583			column0 = 1;
584		} else
585			column0 = 0;
586	if (!column0)
587		++slot;
588	(void) fclose (ttys);
589	sysnerr ((int)(long)(servers = fopen (servers_file, "r")), servers_file);
590
591	len = strlen (disp_name);
592	column0 = 1;
593	while (fgets (servers_line, sizeof (servers_line), servers)) {
594		if (column0 && *servers_line != '#') {
595			if (!strncmp (disp_name, servers_line, len) &&
596			    (servers_line[len] == ' ' ||
597			     servers_line[len] == '\t'))
598				return slot;
599			++slot;
600		}
601		if (servers_line[strlen(servers_line)-1] != '\n')
602			column0 = 0;
603		else
604			column0 = 1;
605	}
606	/*
607	 * display not found in Xservers file - allocate utmp entry dinamically
608	 */
609	return findslot (tty_line, host_name, addp, slot);
610}
611
612/*
613 * find a free utmp slot for the X display.  This allocates a new entry
614 * past the regular tty entries if necessary, reusing existing entries
615 * (identified by (line,hostname)) if possible.
616 */
617static int
618findslot (char *line_name, char *host_name, int addp, int slot)
619{
620	int	utmp;
621	struct	utmp entry;
622	int	found = 0;
623	int	freeslot = -1;
624
625	syserr(utmp = open (utmp_file, O_RDONLY), "open utmp");
626
627	/*
628	 * first, try to locate a previous entry for this display
629	 * also record location of a free slots in case we need a new one
630	 */
631	syserr ((int) lseek (utmp, (long) slot * sizeof (struct utmp), 0), "lseek");
632
633	if (!host_name)
634		host_name = "";
635
636	while (read (utmp, (char *) &entry, sizeof (entry)) == sizeof (entry)) {
637		if (strncmp(entry.ut_line, line_name,
638			sizeof(entry.ut_line)) == 0
639#ifndef __QNX__
640                    &&
641		    strncmp(entry.ut_host, host_name,
642			sizeof(entry.ut_host)) == 0
643#endif
644                   ) {
645			found = 1;
646			break;
647		}
648		if (freeslot < 0 && *entry.ut_name == '\0')
649			freeslot = slot;
650		++slot;
651	}
652
653	close (utmp);
654
655	if (found)
656		return slot;
657	else if (!addp)
658		return 0;	/* trying to delete a non-existing entry */
659	else if (freeslot < 0)
660		return slot;	/* first slot past current entries */
661	else
662		return freeslot;
663}
664#endif
665