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