rusers_proc.c revision 201146
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: head/libexec/rpc.rusersd/rusers_proc.c 201146 2009-12-28 23:01:24Z ed $";
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 <utmp.h>
49#ifdef XIDLE
50#include <setjmp.h>
51#include <X11/Xlib.h>
52#include <X11/extensions/xidle.h>
53#endif
54#define utmp rutmp
55#include <rpcsvc/rnusers.h>
56#undef utmp
57
58#define	IGNOREUSER	"sleeper"
59
60#ifdef OSF
61#define _PATH_UTMP UTMP_FILE
62#endif
63
64#ifndef _PATH_UTMP
65#define _PATH_UTMP "/etc/utmp"
66#endif
67
68#ifndef _PATH_DEV
69#define _PATH_DEV "/dev"
70#endif
71
72#ifndef UT_LINESIZE
73#define UT_LINESIZE sizeof(((struct utmp *)0)->ut_line)
74#endif
75#ifndef UT_NAMESIZE
76#define UT_NAMESIZE sizeof(((struct utmp *)0)->ut_name)
77#endif
78#ifndef UT_HOSTSIZE
79#define UT_HOSTSIZE sizeof(((struct utmp *)0)->ut_host)
80#endif
81
82typedef char ut_line_t[UT_LINESIZE+1];
83typedef char ut_name_t[UT_NAMESIZE+1];
84typedef char ut_host_t[UT_HOSTSIZE+1];
85
86static utmpidle utmp_idle[MAXUSERS];
87static rutmp old_utmp[MAXUSERS];
88static ut_line_t line[MAXUSERS];
89static ut_name_t name[MAXUSERS];
90static ut_host_t host[MAXUSERS];
91
92extern int from_inetd;
93
94void rusers_service(struct svc_req *, SVCXPRT *);
95
96static FILE *ufp;
97
98#ifdef XIDLE
99static Display *dpy;
100
101static jmp_buf openAbort;
102
103static void
104abortOpen(void)
105{
106    longjmp (openAbort, 1);
107}
108
109XqueryIdle(char *display)
110{
111	int first_event, first_error;
112	Time IdleTime;
113
114	(void) signal (SIGALRM, abortOpen);
115	(void) alarm ((unsigned) 10);
116	if (!setjmp (openAbort)) {
117		if (!(dpy= XOpenDisplay(display))) {
118			syslog(LOG_ERR, "Cannot open display %s", display);
119			return(-1);
120		}
121		if (XidleQueryExtension(dpy, &first_event, &first_error)) {
122			if (!XGetIdleTime(dpy, &IdleTime)) {
123				syslog(LOG_ERR, "%s: unable to get idle time", display);
124				return(-1);
125			}
126		} else {
127			syslog(LOG_ERR, "%s: Xidle extension not loaded", display);
128			return(-1);
129		}
130		XCloseDisplay(dpy);
131	} else {
132		syslog(LOG_ERR, "%s: server grabbed for over 10 seconds", display);
133		return(-1);
134	}
135	(void) signal (SIGALRM, SIG_DFL);
136	(void) alarm ((unsigned) 0);
137
138	IdleTime /= 1000;
139	return((IdleTime + 30) / 60);
140}
141#endif
142
143static u_int
144getidle(const char *tty, const char *display __unused)
145{
146	struct stat st;
147	char ttyname[PATH_MAX];
148	time_t now;
149	u_long idle;
150
151	/*
152	 * If this is an X terminal or console, then try the
153	 * XIdle extension
154	 */
155#ifdef XIDLE
156	if (display && *display && (idle = XqueryIdle(display)) >= 0)
157		return(idle);
158#endif
159	idle = 0;
160	if (*tty == 'X') {
161		u_long kbd_idle, mouse_idle;
162#if	!defined(__FreeBSD__)
163		kbd_idle = getidle("kbd", NULL);
164#else
165		kbd_idle = getidle("vga", NULL);
166#endif
167		mouse_idle = getidle("mouse", NULL);
168		idle = (kbd_idle < mouse_idle)?kbd_idle:mouse_idle;
169	} else {
170		sprintf(ttyname, "%s/%s", _PATH_DEV, tty);
171		if (stat(ttyname, &st) < 0) {
172#ifdef DEBUG
173			printf("%s: %s\n", ttyname, strerror(errno));
174#endif
175			return(-1);
176		}
177		time(&now);
178#ifdef DEBUG
179		printf("%s: now=%d atime=%d\n", ttyname, now,
180		       st.st_atime);
181#endif
182		idle = now - st.st_atime;
183		idle = (idle + 30) / 60; /* secs->mins */
184	}
185
186	return(idle);
187}
188
189static utmpidlearr *
190do_names_2(void)
191{
192	static utmpidlearr ut;
193	struct utmp usr;
194	int nusers = 0;
195
196	bzero((char *)&ut, sizeof(ut));
197	ut.utmpidlearr_val = &utmp_idle[0];
198
199	ufp = fopen(_PATH_UTMP, "r");
200	if (!ufp) {
201		syslog(LOG_ERR, "%m");
202		return(&ut);
203	}
204
205	/* only entries with both name and line fields */
206	while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1 &&
207	       nusers < MAXUSERS)
208		if (*usr.ut_name && *usr.ut_line &&
209		    strncmp(usr.ut_name, IGNOREUSER,
210			    sizeof(usr.ut_name))
211#ifdef OSF
212		    && usr.ut_type == USER_PROCESS
213#endif
214		    ) {
215			utmp_idle[nusers].ui_utmp.ut_time =
216				usr.ut_time;
217			utmp_idle[nusers].ui_idle =
218				getidle(usr.ut_line, usr.ut_host);
219			utmp_idle[nusers].ui_utmp.ut_line = line[nusers];
220			strncpy(line[nusers], usr.ut_line, UT_LINESIZE);
221			utmp_idle[nusers].ui_utmp.ut_name = name[nusers];
222			strncpy(name[nusers], usr.ut_name, UT_NAMESIZE);
223			utmp_idle[nusers].ui_utmp.ut_host = host[nusers];
224			strncpy(host[nusers], usr.ut_host, UT_HOSTSIZE);
225
226			/* Make sure entries are NUL terminated */
227			line[nusers][UT_LINESIZE] =
228			name[nusers][UT_NAMESIZE] =
229			host[nusers][UT_HOSTSIZE] = '\0';
230			nusers++;
231		}
232
233	ut.utmpidlearr_len = nusers;
234	fclose(ufp);
235	return(&ut);
236}
237
238static int *
239rusers_num(void *argp __unused, struct svc_req *rqstp __unused)
240{
241	static int num_users = 0;
242	struct utmp usr;
243
244	ufp = fopen(_PATH_UTMP, "r");
245	if (!ufp) {
246		syslog(LOG_ERR, "%m");
247		return(NULL);
248	}
249
250	/* only entries with both name and line fields */
251	while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
252		if (*usr.ut_name && *usr.ut_line &&
253		    strncmp(usr.ut_name, IGNOREUSER,
254			    sizeof(usr.ut_name))
255#ifdef OSF
256		    && usr.ut_type == USER_PROCESS
257#endif
258		    ) {
259			num_users++;
260		}
261
262	fclose(ufp);
263	return(&num_users);
264}
265
266static utmparr *
267do_names_1(void)
268{
269	utmpidlearr *utidle;
270	static utmparr ut;
271	unsigned int i;
272
273	bzero((char *)&ut, sizeof(ut));
274
275	utidle = do_names_2();
276	if (utidle) {
277		ut.utmparr_len = utidle->utmpidlearr_len;
278		ut.utmparr_val = &old_utmp[0];
279		for (i = 0; i < ut.utmparr_len; i++)
280			bcopy(&utmp_idle[i].ui_utmp, &old_utmp[i],
281			      sizeof(old_utmp[0]));
282
283	}
284
285	return(&ut);
286}
287
288utmpidlearr *
289rusersproc_names_2_svc(void *argp __unused, struct svc_req *rqstp __unused)
290{
291
292	return (do_names_2());
293}
294
295utmpidlearr *
296rusersproc_allnames_2_svc(void *argp __unused, struct svc_req *rqstp __unused)
297{
298
299	return (do_names_2());
300}
301
302utmparr *
303rusersproc_names_1_svc(void *argp __unused, struct svc_req *rqstp __unused)
304{
305
306	return (do_names_1());
307}
308
309utmparr *
310rusersproc_allnames_1_svc(void *argp __unused, struct svc_req *rqstp __unused)
311{
312
313	return (do_names_1());
314}
315
316typedef void *(*rusersproc_t)(void *, struct svc_req *);
317
318void
319rusers_service(struct svc_req *rqstp, SVCXPRT *transp)
320{
321	union {
322		int fill;
323	} argument;
324	char *result;
325	xdrproc_t xdr_argument, xdr_result;
326	rusersproc_t local;
327
328	switch (rqstp->rq_proc) {
329	case NULLPROC:
330		(void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL);
331		goto leave;
332
333	case RUSERSPROC_NUM:
334		xdr_argument = (xdrproc_t)xdr_void;
335		xdr_result = (xdrproc_t)xdr_int;
336		local = (rusersproc_t)rusers_num;
337		break;
338
339	case RUSERSPROC_NAMES:
340		xdr_argument = (xdrproc_t)xdr_void;
341		xdr_result = (xdrproc_t)xdr_utmpidlearr;
342		switch (rqstp->rq_vers) {
343		case RUSERSVERS_ORIG:
344			local = (rusersproc_t)rusersproc_names_1_svc;
345			break;
346		case RUSERSVERS_IDLE:
347			local = (rusersproc_t)rusersproc_names_2_svc;
348			break;
349		default:
350			svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE);
351			goto leave;
352			/*NOTREACHED*/
353		}
354		break;
355
356	case RUSERSPROC_ALLNAMES:
357		xdr_argument = (xdrproc_t)xdr_void;
358		xdr_result = (xdrproc_t)xdr_utmpidlearr;
359		switch (rqstp->rq_vers) {
360		case RUSERSVERS_ORIG:
361			local = (rusersproc_t)rusersproc_allnames_1_svc;
362			break;
363		case RUSERSVERS_IDLE:
364			local = (rusersproc_t)rusersproc_allnames_2_svc;
365			break;
366		default:
367			svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE);
368			goto leave;
369			/*NOTREACHED*/
370		}
371		break;
372
373	default:
374		svcerr_noproc(transp);
375		goto leave;
376	}
377	bzero(&argument, sizeof(argument));
378	if (!svc_getargs(transp, (xdrproc_t)xdr_argument, &argument)) {
379		svcerr_decode(transp);
380		goto leave;
381	}
382	result = (*local)(&argument, rqstp);
383	if (result != NULL &&
384	    !svc_sendreply(transp, (xdrproc_t)xdr_result, result)) {
385		svcerr_systemerr(transp);
386	}
387	if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, &argument)) {
388		syslog(LOG_ERR, "unable to free arguments");
389		exit(1);
390	}
391leave:
392	if (from_inetd)
393		exit(0);
394}
395