xref: /titanic_52/usr/src/cmd/rpcsvc/rpc.rusersd.c (revision bdfc6d18da790deeec2e0eb09c625902defe2498)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  *
22  * Copyright 1998 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
26 /* All Rights Reserved */
27 /*
28  * University Copyright- Copyright (c) 1982, 1986, 1988
29  * The Regents of the University of California
30  * All Rights Reserved
31  *
32  * University Acknowledgment- Portions of this document are derived from
33  * software developed by the University of California, Berkeley, and its
34  * contributors.
35  */
36 
37 #pragma ident	"%Z%%M%	%I%	%E% SMI"
38 
39 #include <stdio.h>
40 #include <signal.h>
41 #include <sys/stat.h>
42 #include <rpc/rpc.h>
43 #include <memory.h>
44 #include <netconfig.h>
45 #include <stropts.h>
46 #include <syslog.h>
47 #include <utmpx.h>
48 #include <rpcsvc/rusers.h>
49 #include <sys/resource.h>
50 #include <limits.h>
51 
52 #ifdef	DEBUG
53 #define	RPC_SVC_FG
54 #endif
55 
56 #define	_RPCSVC_CLOSEDOWN 120
57 
58 static void rusers_service();
59 static void closedown();
60 static void msgout();
61 static unsigned min();
62 
63 static int _rpcpmstart;		/* Started by a port monitor ? */
64 static int _rpcfdtype;		/* Whether Stream or Datagram ? */
65 static int _rpcsvcdirty;	/* Still serving ? */
66 static int _rpcsvcrecent;	/* set when we serivce a request; tested */
67 				/* and cleared by closedown() routine */
68 
69 #define	DIV60(t)	((t+30)/60)	/* x/60 rounded */
70 
71 #define	ALL_ENTRIES	1
72 #define	REAL_USERS	0
73 
74 utmp_array utmp_array_res;
75 int used_array_len = 0;
76 struct utmpidlearr utmpidlearr;
77 
78 main()
79 {
80 	pid_t pid;
81 	int i;
82 	int connmaxrec = RPC_MAXDATASIZE;
83 
84 	/*
85 	 * Set non-blocking mode and maximum record size for
86 	 * connection oriented RPC transports.
87 	 */
88 	if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) {
89 		msgout("unable to set maximum RPC record size");
90 	}
91 
92 	/*
93 	 * If stdin looks like a TLI endpoint, we assume
94 	 * that we were started by a port monitor. If
95 	 * t_getstate fails with TBADF, this is not a
96 	 * TLI endpoint.
97 	 */
98 	if (t_getstate(0) != -1 || t_errno != TBADF) {
99 		char *netid;
100 		struct netconfig *nconf = NULL;
101 		SVCXPRT *transp;
102 		int pmclose;
103 		extern char *getenv();
104 
105 		_rpcpmstart = 1;
106 		openlog("rusers", LOG_PID, LOG_DAEMON);
107 		if ((netid = getenv("NLSPROVIDER")) == NULL) {
108 #ifdef DEBUG
109 			msgout("cannot get transport name");
110 #endif
111 		} else if ((nconf = getnetconfigent(netid)) == NULL) {
112 #ifdef DEBUG
113 			msgout("cannot get transport info");
114 #endif
115 		}
116 		if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) {
117 			msgout("cannot create server handle");
118 			exit(1);
119 		}
120 		if (nconf)
121 			freenetconfigent(nconf);
122 		if (!svc_reg(transp, RUSERSPROG, RUSERSVERS_3, rusers_service,
123 				0)) {
124 	msgout("unable to register (RUSERSPROG, RUSERSVERS_3).");
125 			exit(1);
126 		}
127 		if (!svc_reg(transp, RUSERSPROG, RUSERSVERS_IDLE,
128 				rusers_service, 0)) {
129 	msgout("unable to register (RUSERSPROG, RUSERSVERS_IDLE).");
130 			exit(1);
131 		}
132 		(void) signal(SIGALRM, closedown);
133 		(void) alarm(_RPCSVC_CLOSEDOWN);
134 		svc_run();
135 		msgout("svc_run returned");
136 		exit(1);
137 		/* NOTREACHED */
138 	}
139 #ifndef RPC_SVC_FG
140 	pid = fork();
141 	if (pid < 0) {
142 		perror("rpc.rusersd: cannot fork");
143 		exit(1);
144 	}
145 	if (pid)
146 		exit(0);
147 	for (i = 0; i < 20; i++)
148 		(void) close(i);
149 	setsid();
150 	openlog("rusers", LOG_PID, LOG_DAEMON);
151 #endif
152 	if (!svc_create(rusers_service, RUSERSPROG, RUSERSVERS_3, "netpath")) {
153 	    msgout("unable to create (RUSERSPROG, RUSERSVERS_3) for netpath");
154 		exit(1);
155 	}
156 	if (!svc_create(rusers_service, RUSERSPROG, RUSERSVERS_IDLE,
157 			"netpath")) {
158 	    msgout(
159 		"unable to create (RUSERSPROG, RUSERSVERS_IDLE) for netpath");
160 		exit(1);
161 	}
162 
163 	svc_run();
164 	msgout("svc_run returned");
165 	exit(1);
166 	/* NOTREACHED */
167 }
168 
169 
170 /*
171  * This routine gets the user information.
172  * "all" specifies whether all listings should be counted, or only those of
173  *	type "USER_PROCESS".
174  * "version" is either RUSERSVERS_IDLE or RUSERSVERS_3.  If anything else,
175  *	just a count is returned.
176  * "limit" specifies the maximum number of entries to be processed.
177  *
178  * For both versions, the results are placed into an external variable.
179  * For RUSERSVERS_IDLE, this routine mallocs entries in a vector as it
180  * processed each utmpx entry.  These malloc'd entries must be freed after the
181  * results are returned.
182  * For RUSERSVERS_3, this routine uses array entries that are malloc'd prior
183  * to this routine being called. "limit" is the number of elements available.
184  */
185 int
186 getutmpx_3(all, version, limit)
187 	int all;		/* give all listings? */
188 	int version;		/* version 2 or 3 */
189 	int limit;		/* limits users returned, 0 means no limit */
190 {
191 	struct utmpx *utent;
192 	struct utmpidle **q = utmpidlearr.uia_arr;
193 	int minidle;
194 	int cnt = 0;
195 	time_t now;
196 	extern char *s_malodup();
197 
198 	time(&now);		/* only one call to time() for this rpc call */
199 	setutxent();		/* reset the utmpx file */
200 	while ((utent = getutxent()) != NULL && (limit == 0 || cnt < limit)) {
201 		if (utent->ut_line[0] == '\0' || utent->ut_user[0] == '\0')
202 			continue;
203 		/*
204 		 * List only user processes.
205 		 * XXX modified to exclude cmdtool style window entries.
206 		 */
207 		if ((all == REAL_USERS) && ((utent->ut_type != USER_PROCESS) ||
208 		    nonuserx(*utent)))
209 			continue;
210 
211 		if (version == RUSERSVERS_IDLE) {
212 			/*
213 			 * need to free this; done after svc_sendreply.
214 			 */
215 			*q = (struct utmpidle *)
216 				malloc(sizeof (struct utmpidle));
217 			(*q)->ui_idle = findidle(utent->ut_line,
218 						sizeof (utent->ut_line), now);
219 			if (strncmp(utent->ut_line, "console",
220 				strlen("console")) == 0) {
221 				(*q)->ui_idle = min((*q)->ui_idle,
222 					console_idle(now));
223 			}
224 			usys5to_ru(utent, &((*q)->ui_utmp));
225 #ifdef DEBUG
226 			printf("%-*s %-*s  %s; idle %d",
227 			    sizeof (utent->ut_line),
228 			    utent->ut_line,
229 			    sizeof (utent->ut_name),
230 			    utent->ut_name,
231 			    ctime(&utent->ut_xtime),
232 			    (*q)->ui_idle);
233 #endif
234 			q++;
235 		} else if (version == RUSERSVERS_3) {
236 #define	uav	utmp_array_res.utmp_array_val
237 
238 			uav[cnt].ut_host =
239 				s_malodup(utent->ut_host, utent->ut_syslen);
240 			uav[cnt].ut_user = s_malodup(utent->ut_user,
241 				sizeof (utent->ut_user));
242 			uav[cnt].ut_line = s_malodup(utent->ut_line,
243 				sizeof (utent->ut_line));
244 			uav[cnt].ut_type = utent->ut_type;
245 			uav[cnt].ut_time = utent->ut_xtime;
246 			uav[cnt].ut_idle = findidle(utent->ut_line,
247 						sizeof (utent->ut_line), now);
248 			if (strncmp(utent->ut_line, "console",
249 				strlen("console")) == 0) {
250 				uav[cnt].ut_idle =
251 					min(uav[cnt].ut_idle,
252 							console_idle(now));
253 			}
254 #ifdef DEBUG
255 			printf("user: %-10s line: %-10s  %s; idle %d (%s)\n",
256 					uav[cnt].ut_line, uav[cnt].ut_user,
257 					ctime((time_t *)&uav[cnt].ut_time),
258 					uav[cnt].ut_idle, uav[cnt].ut_host);
259 #endif
260 #undef	uav
261 		}
262 		cnt++;
263 	}
264 	return (cnt);
265 }
266 
267 /*
268  * "string" is a character array with maximum size "size".  Return a
269  * malloc'd string that's a duplicate of the string.
270  */
271 char *
272 s_malodup(string, size)
273 char *string;
274 int size;
275 {
276 	char *tmp;
277 
278 	tmp = (char *)malloc(size+1);
279 	if (tmp == NULL) {
280 		msgout("rpc.rusersd: malloc failed (2)");
281 		return (NULL);
282 	}
283 	strncpy(tmp, string, size);
284 	tmp[size] = '\0';
285 	return (tmp);
286 }
287 
288 
289 int
290 console_idle(now)
291 	time_t now;
292 {
293 	/*
294 	 * On the console, the user may be running a window system; if so,
295 	 * their activity will show up in the last-access times of
296 	 * "/dev/kbd" and "/dev/mouse", so take the minimum of the idle
297 	 * times on those two devices and "/dev/console" and treat that as
298 	 * the idle time.
299 	 */
300 	return (min((unsigned)findidle("kbd", strlen("kbd"), now),
301 		(unsigned)findidle("mouse", strlen("mouse"), now)));
302 }
303 
304 static void
305 rusers_service(rqstp, transp)
306 	register struct svc_req *rqstp;
307 	register SVCXPRT *transp;
308 {
309 	int i;
310 	int cnt;
311 	char *replyerr = "rpc.rusersd: error replying to request";
312 
313 	_rpcsvcrecent = _rpcsvcdirty = 1;
314 	switch (rqstp->rq_proc) {
315 	case 0:
316 		if (svc_sendreply(transp, xdr_void, 0) == FALSE) {
317 			msgout(replyerr);
318 		}
319 		break;
320 	case RUSERSPROC_NUM:
321 		cnt = getutmpx_3(REAL_USERS, 0, 0);
322 		if (!svc_sendreply(transp, xdr_u_long, (caddr_t)&cnt))
323 			msgout(replyerr);
324 		break;
325 	case RUSERSPROC_NAMES:
326 	case RUSERSPROC_ALLNAMES:
327 		if (rqstp->rq_vers == RUSERSVERS_IDLE) {
328 			utmpidlearr.uia_arr = (struct utmpidle **)
329 				malloc(MAXUSERS*sizeof (struct utmpidle *));
330 			utmpidlearr.uia_cnt = getutmpx_3(rqstp->rq_proc ==
331 				RUSERSPROC_ALLNAMES,
332 				RUSERSVERS_IDLE, MAXUSERS);
333 			if (!svc_sendreply(transp, xdr_utmpidlearr,
334 					(caddr_t)&utmpidlearr))
335 				msgout(replyerr);
336 			for (i = 0; i < utmpidlearr.uia_cnt; i++) {
337 				free(utmpidlearr.uia_arr[i]);
338 			}
339 			free(utmpidlearr.uia_arr);
340 		} else if (rqstp->rq_vers == RUSERSVERS_3) {
341 			int entries, alloc_array_len;
342 
343 			/*
344 			 * Always free strings from previous results array
345 			 */
346 			for (i = 0; i < used_array_len; i++) {
347 			free_ua_entry(&utmp_array_res.utmp_array_val[i]);
348 			}
349 			entries = (rqstp->rq_proc == RUSERSPROC_ALLNAMES);
350 			cnt = getutmpx_3(entries, 0, 0);	/* get cnt */
351 			if (cnt > utmp_array_res.utmp_array_len) {
352 				free(utmp_array_res.utmp_array_val);
353 				utmp_array_res.utmp_array_len = 0;
354 				utmp_array_res.utmp_array_val = (rusers_utmp *)
355 					malloc(cnt * sizeof (rusers_utmp));
356 				if (utmp_array_res.utmp_array_val == NULL) {
357 				    msgout("rpc.rusersd: malloc failed (1)");
358 				    break;
359 				}
360 				alloc_array_len = cnt;
361 			} else {
362 				alloc_array_len = utmp_array_res.utmp_array_len;
363 			}
364 			cnt = getutmpx_3(entries, RUSERSVERS_3, cnt);
365 			utmp_array_res.utmp_array_len = used_array_len = cnt;
366 			if (!svc_sendreply(transp, xdr_utmp_array,
367 					(caddr_t)&utmp_array_res))
368 				msgout(replyerr);
369 			utmp_array_res.utmp_array_len = alloc_array_len;
370 		}
371 		break;
372 	default:
373 		svcerr_noproc(transp);
374 		break;
375 	}
376 	_rpcsvcdirty = 0;
377 
378 }
379 
380 free_ua_entry(uap)
381 rusers_utmp *uap;
382 {
383 	if (uap == NULL)
384 		return;
385 	if (uap->ut_user)
386 		free(uap->ut_user);
387 	if (uap->ut_line)
388 		free(uap->ut_line);
389 	if (uap->ut_host)
390 		free(uap->ut_host);
391 }
392 
393 
394 
395 /* find & return number of minutes current tty has been idle */
396 findidle(name, ln, now)
397 	char *name;
398 	int ln;
399 	time_t	now;
400 {
401 	struct stat stbuf;
402 	long lastaction, diff;
403 	char ttyname[32];
404 
405 	strcpy(ttyname, "/dev/");
406 	strncat(ttyname, name, ln);
407 	if (stat(ttyname, &stbuf) < 0)
408 		return (INT_MAX);
409 	lastaction = stbuf.st_atime;
410 	diff = now - lastaction;
411 	diff = DIV60(diff);
412 	if (diff < 0) diff = 0;
413 	return (diff);
414 }
415 
416 static
417 usys5to_ru(s5, bss)
418 	struct utmpx *s5;
419 	struct ru_utmp *bss;
420 {
421 	int i;
422 
423 #ifdef DEBUG
424 	printf("sizeof (bss->ut_host) == %d\n", sizeof (bss->ut_host));
425 #endif
426 	strncpy(bss->ut_name, s5->ut_name, sizeof (bss->ut_name));
427 	strncpy(bss->ut_line, s5->ut_line, sizeof (bss->ut_line));
428 	strncpy(bss->ut_host, s5->ut_host, sizeof (bss->ut_host));
429 	bss->ut_time = s5->ut_xtime;
430 }
431 
432 static void
433 msgout(msg)
434 	char *msg;
435 {
436 #ifdef RPC_SVC_FG
437 	if (_rpcpmstart)
438 		syslog(LOG_ERR, msg);
439 	else
440 		(void) fprintf(stderr, "%s\n", msg);
441 #else
442 	syslog(LOG_ERR, msg);
443 #endif
444 }
445 
446 static void
447 closedown(sig)
448 int sig;
449 {
450 	if (_rpcsvcrecent) {
451 		_rpcsvcrecent = 0;
452 	} else {
453 		if (_rpcsvcdirty == 0) {
454 			int i, openfd;
455 			struct t_info tinfo;
456 
457 			if (t_getinfo(0, &tinfo) || (tinfo.servtype == T_CLTS))
458 				exit(0);
459 
460 			for (i = 0, openfd = 0;
461 					i < svc_max_pollfd && openfd < 2;
462 					i++) {
463 				if (svc_pollfd[i].fd >= 0)
464 					openfd++;
465 			}
466 
467 			if (openfd <= 1)
468 				exit(0);
469 		}
470 	}
471 	(void) signal(SIGALRM, closedown);
472 	(void) alarm(_RPCSVC_CLOSEDOWN);
473 }
474 
475 unsigned
476 min(a, b)
477 unsigned a;
478 unsigned b;
479 {
480 	if (a < b)
481 		return (a);
482 	else
483 		return (b);
484 }
485