1/*-
2 * Copyright (c) 1993, John Brezak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static const char rcsid[] =
36  "$FreeBSD$";
37#endif /* not lint */
38
39#ifdef DEBUG
40#include <errno.h>
41#endif
42#include <stdio.h>
43#include <string.h>
44#include <sys/param.h>
45#include <sys/stat.h>
46#include <stdlib.h>
47#include <syslog.h>
48#include <utmpx.h>
49#ifdef XIDLE
50#include <setjmp.h>
51#include <X11/Xlib.h>
52#include <X11/extensions/xidle.h>
53#endif
54#include <rpcsvc/rnusers.h>
55
56#include "extern.h"
57
58#ifndef _PATH_DEV
59#define _PATH_DEV "/dev"
60#endif
61
62static utmpidle utmp_idle[MAXUSERS];
63static utmp old_utmp[MAXUSERS];
64static struct utmpx utmp_list[MAXUSERS];
65
66#ifdef XIDLE
67static Display *dpy;
68
69static jmp_buf openAbort;
70
71static void
72abortOpen(void)
73{
74    longjmp (openAbort, 1);
75}
76
77XqueryIdle(char *display)
78{
79	int first_event, first_error;
80	Time IdleTime;
81
82	(void) signal (SIGALRM, abortOpen);
83	(void) alarm ((unsigned) 10);
84	if (!setjmp (openAbort)) {
85		if (!(dpy= XOpenDisplay(display))) {
86			syslog(LOG_ERR, "Cannot open display %s", display);
87			return(-1);
88		}
89		if (XidleQueryExtension(dpy, &first_event, &first_error)) {
90			if (!XGetIdleTime(dpy, &IdleTime)) {
91				syslog(LOG_ERR, "%s: unable to get idle time", display);
92				return(-1);
93			}
94		} else {
95			syslog(LOG_ERR, "%s: Xidle extension not loaded", display);
96			return(-1);
97		}
98		XCloseDisplay(dpy);
99	} else {
100		syslog(LOG_ERR, "%s: server grabbed for over 10 seconds", display);
101		return(-1);
102	}
103	(void) signal (SIGALRM, SIG_DFL);
104	(void) alarm ((unsigned) 0);
105
106	IdleTime /= 1000;
107	return((IdleTime + 30) / 60);
108}
109#endif
110
111static u_int
112getidle(const char *tty, const char *display __unused)
113{
114	struct stat st;
115	char ttyname[PATH_MAX];
116	time_t now;
117	u_long idle;
118
119	/*
120	 * If this is an X terminal or console, then try the
121	 * XIdle extension
122	 */
123#ifdef XIDLE
124	if (display && *display && (idle = XqueryIdle(display)) >= 0)
125		return(idle);
126#endif
127	idle = 0;
128	if (*tty == 'X') {
129		u_long kbd_idle, mouse_idle;
130#if	!defined(__FreeBSD__)
131		kbd_idle = getidle("kbd", NULL);
132#else
133		kbd_idle = getidle("vga", NULL);
134#endif
135		mouse_idle = getidle("mouse", NULL);
136		idle = (kbd_idle < mouse_idle)?kbd_idle:mouse_idle;
137	} else {
138		sprintf(ttyname, "%s/%s", _PATH_DEV, tty);
139		if (stat(ttyname, &st) < 0) {
140#ifdef DEBUG
141			printf("%s: %s\n", ttyname, strerror(errno));
142#endif
143			return(-1);
144		}
145		time(&now);
146#ifdef DEBUG
147		printf("%s: now=%d atime=%d\n", ttyname, now,
148		       st.st_atime);
149#endif
150		idle = now - st.st_atime;
151		idle = (idle + 30) / 60; /* secs->mins */
152	}
153
154	return(idle);
155}
156
157static utmpidlearr *
158do_names_2(void)
159{
160	static utmpidlearr ut;
161	struct utmpx *usr;
162	int nusers = 0;
163
164	memset(&ut, 0, sizeof(ut));
165	ut.utmpidlearr_val = &utmp_idle[0];
166
167	setutxent();
168	while ((usr = getutxent()) != NULL && nusers < MAXUSERS) {
169		if (usr->ut_type != USER_PROCESS)
170			continue;
171
172		memcpy(&utmp_list[nusers], usr, sizeof(*usr));
173		utmp_idle[nusers].ui_utmp.ut_time = usr->ut_tv.tv_sec;
174		utmp_idle[nusers].ui_idle =
175		    getidle(usr->ut_line, usr->ut_host);
176		utmp_idle[nusers].ui_utmp.ut_line =
177		    utmp_list[nusers].ut_line;
178		utmp_idle[nusers].ui_utmp.ut_name =
179		    utmp_list[nusers].ut_user;
180		utmp_idle[nusers].ui_utmp.ut_host =
181		    utmp_list[nusers].ut_host;
182
183		nusers++;
184	}
185	endutxent();
186
187	ut.utmpidlearr_len = nusers;
188	return(&ut);
189}
190
191static int *
192rusers_num(void *argp __unused, struct svc_req *rqstp __unused)
193{
194	static int num_users = 0;
195	struct utmpx *usr;
196
197	setutxent();
198	while ((usr = getutxent()) != NULL) {
199		if (usr->ut_type != USER_PROCESS)
200			continue;
201		num_users++;
202	}
203	endutxent();
204
205	return(&num_users);
206}
207
208static utmparr *
209do_names_1(void)
210{
211	utmpidlearr *utidle;
212	static utmparr ut;
213	unsigned int i;
214
215	bzero((char *)&ut, sizeof(ut));
216
217	utidle = do_names_2();
218	if (utidle) {
219		ut.utmparr_len = utidle->utmpidlearr_len;
220		ut.utmparr_val = &old_utmp[0];
221		for (i = 0; i < ut.utmparr_len; i++)
222			bcopy(&utmp_idle[i].ui_utmp, &old_utmp[i],
223			      sizeof(old_utmp[0]));
224
225	}
226
227	return(&ut);
228}
229
230utmpidlearr *
231rusersproc_names_2_svc(void *argp __unused, struct svc_req *rqstp __unused)
232{
233
234	return (do_names_2());
235}
236
237utmpidlearr *
238rusersproc_allnames_2_svc(void *argp __unused, struct svc_req *rqstp __unused)
239{
240
241	return (do_names_2());
242}
243
244utmparr *
245rusersproc_names_1_svc(void *argp __unused, struct svc_req *rqstp __unused)
246{
247
248	return (do_names_1());
249}
250
251utmparr *
252rusersproc_allnames_1_svc(void *argp __unused, struct svc_req *rqstp __unused)
253{
254
255	return (do_names_1());
256}
257
258typedef void *(*rusersproc_t)(void *, struct svc_req *);
259
260void
261rusers_service(struct svc_req *rqstp, SVCXPRT *transp)
262{
263	union {
264		int fill;
265	} argument;
266	char *result;
267	xdrproc_t xdr_argument, xdr_result;
268	rusersproc_t local;
269
270	switch (rqstp->rq_proc) {
271	case NULLPROC:
272		(void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL);
273		goto leave;
274
275	case RUSERSPROC_NUM:
276		xdr_argument = (xdrproc_t)xdr_void;
277		xdr_result = (xdrproc_t)xdr_int;
278		local = (rusersproc_t)rusers_num;
279		break;
280
281	case RUSERSPROC_NAMES:
282		xdr_argument = (xdrproc_t)xdr_void;
283		xdr_result = (xdrproc_t)xdr_utmpidlearr;
284		switch (rqstp->rq_vers) {
285		case RUSERSVERS_ORIG:
286			local = (rusersproc_t)rusersproc_names_1_svc;
287			break;
288		case RUSERSVERS_IDLE:
289			local = (rusersproc_t)rusersproc_names_2_svc;
290			break;
291		default:
292			svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE);
293			goto leave;
294			/*NOTREACHED*/
295		}
296		break;
297
298	case RUSERSPROC_ALLNAMES:
299		xdr_argument = (xdrproc_t)xdr_void;
300		xdr_result = (xdrproc_t)xdr_utmpidlearr;
301		switch (rqstp->rq_vers) {
302		case RUSERSVERS_ORIG:
303			local = (rusersproc_t)rusersproc_allnames_1_svc;
304			break;
305		case RUSERSVERS_IDLE:
306			local = (rusersproc_t)rusersproc_allnames_2_svc;
307			break;
308		default:
309			svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE);
310			goto leave;
311			/*NOTREACHED*/
312		}
313		break;
314
315	default:
316		svcerr_noproc(transp);
317		goto leave;
318	}
319	bzero(&argument, sizeof(argument));
320	if (!svc_getargs(transp, (xdrproc_t)xdr_argument, &argument)) {
321		svcerr_decode(transp);
322		goto leave;
323	}
324	result = (*local)(&argument, rqstp);
325	if (result != NULL &&
326	    !svc_sendreply(transp, (xdrproc_t)xdr_result, result)) {
327		svcerr_systemerr(transp);
328	}
329	if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, &argument)) {
330		syslog(LOG_ERR, "unable to free arguments");
331		exit(1);
332	}
333leave:
334	if (from_inetd)
335		exit(0);
336}
337