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