xref: /illumos-gate/usr/src/cmd/rpcsvc/rpc.rusersd.c (revision 49e7ca49)
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 2005 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 static void free_ua_entry(rusers_utmp *uap);
79 static int findidle(char *name, int ln, time_t	now);
80 static void usys5to_ru(struct utmpx *s5, struct ru_utmp *bss);
81 
82 int
main(int argc,char * argv[])83 main(int argc, char *argv[])
84 {
85 	pid_t pid;
86 	int i;
87 	int connmaxrec = RPC_MAXDATASIZE;
88 
89 	/*
90 	 * Set non-blocking mode and maximum record size for
91 	 * connection oriented RPC transports.
92 	 */
93 	if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) {
94 		msgout("unable to set maximum RPC record size");
95 	}
96 
97 	/*
98 	 * If stdin looks like a TLI endpoint, we assume
99 	 * that we were started by a port monitor. If
100 	 * t_getstate fails with TBADF, this is not a
101 	 * TLI endpoint.
102 	 */
103 	if (t_getstate(0) != -1 || t_errno != TBADF) {
104 		char *netid;
105 		struct netconfig *nconf = NULL;
106 		SVCXPRT *transp;
107 		int pmclose;
108 		extern char *getenv();
109 
110 		_rpcpmstart = 1;
111 		openlog("rusers", LOG_PID, LOG_DAEMON);
112 		if ((netid = getenv("NLSPROVIDER")) == NULL) {
113 #ifdef DEBUG
114 			msgout("cannot get transport name");
115 #endif
116 		} else if ((nconf = getnetconfigent(netid)) == NULL) {
117 #ifdef DEBUG
118 			msgout("cannot get transport info");
119 #endif
120 		}
121 		if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) {
122 			msgout("cannot create server handle");
123 			exit(1);
124 		}
125 		if (nconf)
126 			freenetconfigent(nconf);
127 		if (!svc_reg(transp, RUSERSPROG, RUSERSVERS_3, rusers_service,
128 				0)) {
129 	msgout("unable to register (RUSERSPROG, RUSERSVERS_3).");
130 			exit(1);
131 		}
132 		if (!svc_reg(transp, RUSERSPROG, RUSERSVERS_IDLE,
133 				rusers_service, 0)) {
134 	msgout("unable to register (RUSERSPROG, RUSERSVERS_IDLE).");
135 			exit(1);
136 		}
137 		(void) signal(SIGALRM, closedown);
138 		(void) alarm(_RPCSVC_CLOSEDOWN);
139 		svc_run();
140 		msgout("svc_run returned");
141 		exit(1);
142 		/* NOTREACHED */
143 	}
144 #ifndef RPC_SVC_FG
145 	pid = fork();
146 	if (pid < 0) {
147 		perror("rpc.rusersd: cannot fork");
148 		exit(1);
149 	}
150 	if (pid)
151 		exit(0);
152 	for (i = 0; i < 20; i++)
153 		(void) close(i);
154 	setsid();
155 	openlog("rusers", LOG_PID, LOG_DAEMON);
156 #endif
157 	if (!svc_create(rusers_service, RUSERSPROG, RUSERSVERS_3, "netpath")) {
158 	    msgout("unable to create (RUSERSPROG, RUSERSVERS_3) for netpath");
159 		exit(1);
160 	}
161 	if (!svc_create(rusers_service, RUSERSPROG, RUSERSVERS_IDLE,
162 			"netpath")) {
163 	    msgout(
164 		"unable to create (RUSERSPROG, RUSERSVERS_IDLE) for netpath");
165 		exit(1);
166 	}
167 
168 	svc_run();
169 	msgout("svc_run returned");
170 	return (1);
171 }
172 
173 
174 /*
175  * This routine gets the user information.
176  * "all" specifies whether all listings should be counted, or only those of
177  *	type "USER_PROCESS".
178  * "version" is either RUSERSVERS_IDLE or RUSERSVERS_3.  If anything else,
179  *	just a count is returned.
180  * "limit" specifies the maximum number of entries to be processed.
181  *
182  * For both versions, the results are placed into an external variable.
183  * For RUSERSVERS_IDLE, this routine mallocs entries in a vector as it
184  * processed each utmpx entry.  These malloc'd entries must be freed after the
185  * results are returned.
186  * For RUSERSVERS_3, this routine uses array entries that are malloc'd prior
187  * to this routine being called. "limit" is the number of elements available.
188  */
189 int
getutmpx_3(all,version,limit)190 getutmpx_3(all, version, limit)
191 	int all;		/* give all listings? */
192 	int version;		/* version 2 or 3 */
193 	int limit;		/* limits users returned, 0 means no limit */
194 {
195 	struct utmpx *utent;
196 	struct utmpidle **q = utmpidlearr.uia_arr;
197 	int minidle;
198 	int cnt = 0;
199 	time_t now;
200 	extern char *s_malodup();
201 
202 	time(&now);		/* only one call to time() for this rpc call */
203 	setutxent();		/* reset the utmpx file */
204 	while ((utent = getutxent()) != NULL && (limit == 0 || cnt < limit)) {
205 		if (utent->ut_line[0] == '\0' || utent->ut_user[0] == '\0')
206 			continue;
207 		/*
208 		 * List only user processes.
209 		 * XXX modified to exclude cmdtool style window entries.
210 		 */
211 		if ((all == REAL_USERS) && ((utent->ut_type != USER_PROCESS) ||
212 		    nonuserx(*utent)))
213 			continue;
214 
215 		if (version == RUSERSVERS_IDLE) {
216 			/*
217 			 * need to free this; done after svc_sendreply.
218 			 */
219 			*q = (struct utmpidle *)
220 				malloc(sizeof (struct utmpidle));
221 			(*q)->ui_idle = findidle(utent->ut_line,
222 						sizeof (utent->ut_line), now);
223 			if (strncmp(utent->ut_line, "console",
224 				strlen("console")) == 0) {
225 				(*q)->ui_idle = min((*q)->ui_idle,
226 					console_idle(now));
227 			}
228 			usys5to_ru(utent, &((*q)->ui_utmp));
229 #ifdef DEBUG
230 			printf("%-*s %-*s  %s; idle %d",
231 			    sizeof (utent->ut_line),
232 			    utent->ut_line,
233 			    sizeof (utent->ut_name),
234 			    utent->ut_name,
235 			    ctime(&utent->ut_xtime),
236 			    (*q)->ui_idle);
237 #endif
238 			q++;
239 		} else if (version == RUSERSVERS_3) {
240 #define	uav	utmp_array_res.utmp_array_val
241 
242 			uav[cnt].ut_host =
243 				s_malodup(utent->ut_host, utent->ut_syslen);
244 			uav[cnt].ut_user = s_malodup(utent->ut_user,
245 				sizeof (utent->ut_user));
246 			uav[cnt].ut_line = s_malodup(utent->ut_line,
247 				sizeof (utent->ut_line));
248 			uav[cnt].ut_type = utent->ut_type;
249 			uav[cnt].ut_time = utent->ut_xtime;
250 			uav[cnt].ut_idle = findidle(utent->ut_line,
251 						sizeof (utent->ut_line), now);
252 			if (strncmp(utent->ut_line, "console",
253 				strlen("console")) == 0) {
254 				uav[cnt].ut_idle =
255 					min(uav[cnt].ut_idle,
256 							console_idle(now));
257 			}
258 #ifdef DEBUG
259 			printf("user: %-10s line: %-10s  %s; idle %d (%s)\n",
260 					uav[cnt].ut_line, uav[cnt].ut_user,
261 					ctime((time_t *)&uav[cnt].ut_time),
262 					uav[cnt].ut_idle, uav[cnt].ut_host);
263 #endif
264 #undef	uav
265 		}
266 		cnt++;
267 	}
268 	return (cnt);
269 }
270 
271 /*
272  * "string" is a character array with maximum size "size".  Return a
273  * malloc'd string that's a duplicate of the string.
274  */
275 char *
s_malodup(string,size)276 s_malodup(string, size)
277 char *string;
278 int size;
279 {
280 	char *tmp;
281 
282 	tmp = (char *)malloc(size+1);
283 	if (tmp == NULL) {
284 		msgout("rpc.rusersd: malloc failed (2)");
285 		return (NULL);
286 	}
287 	strncpy(tmp, string, size);
288 	tmp[size] = '\0';
289 	return (tmp);
290 }
291 
292 
293 int
console_idle(now)294 console_idle(now)
295 	time_t now;
296 {
297 	/*
298 	 * On the console, the user may be running a window system; if so,
299 	 * their activity will show up in the last-access times of
300 	 * "/dev/kbd" and "/dev/mouse", so take the minimum of the idle
301 	 * times on those two devices and "/dev/console" and treat that as
302 	 * the idle time.
303 	 */
304 	return (min((unsigned)findidle("kbd", strlen("kbd"), now),
305 		(unsigned)findidle("mouse", strlen("mouse"), now)));
306 }
307 
308 static void
rusers_service(rqstp,transp)309 rusers_service(rqstp, transp)
310 	register struct svc_req *rqstp;
311 	register SVCXPRT *transp;
312 {
313 	int i;
314 	int cnt;
315 	char *replyerr = "rpc.rusersd: error replying to request";
316 
317 	_rpcsvcrecent = _rpcsvcdirty = 1;
318 	switch (rqstp->rq_proc) {
319 	case 0:
320 		if (svc_sendreply(transp, xdr_void, 0) == FALSE) {
321 			msgout(replyerr);
322 		}
323 		break;
324 	case RUSERSPROC_NUM:
325 		cnt = getutmpx_3(REAL_USERS, 0, 0);
326 		if (!svc_sendreply(transp, xdr_u_long, (caddr_t)&cnt))
327 			msgout(replyerr);
328 		break;
329 	case RUSERSPROC_NAMES:
330 	case RUSERSPROC_ALLNAMES:
331 		if (rqstp->rq_vers == RUSERSVERS_IDLE) {
332 			utmpidlearr.uia_arr = (struct utmpidle **)
333 				malloc(MAXUSERS*sizeof (struct utmpidle *));
334 			utmpidlearr.uia_cnt = getutmpx_3(rqstp->rq_proc ==
335 				RUSERSPROC_ALLNAMES,
336 				RUSERSVERS_IDLE, MAXUSERS);
337 			if (!svc_sendreply(transp, xdr_utmpidlearr,
338 					(caddr_t)&utmpidlearr))
339 				msgout(replyerr);
340 			for (i = 0; i < utmpidlearr.uia_cnt; i++) {
341 				free(utmpidlearr.uia_arr[i]);
342 			}
343 			free(utmpidlearr.uia_arr);
344 		} else if (rqstp->rq_vers == RUSERSVERS_3) {
345 			int entries, alloc_array_len;
346 
347 			/*
348 			 * Always free strings from previous results array
349 			 */
350 			for (i = 0; i < used_array_len; i++) {
351 			free_ua_entry(&utmp_array_res.utmp_array_val[i]);
352 			}
353 			entries = (rqstp->rq_proc == RUSERSPROC_ALLNAMES);
354 			cnt = getutmpx_3(entries, 0, 0);	/* get cnt */
355 			if (cnt > utmp_array_res.utmp_array_len) {
356 				free(utmp_array_res.utmp_array_val);
357 				utmp_array_res.utmp_array_len = 0;
358 				utmp_array_res.utmp_array_val = (rusers_utmp *)
359 					malloc(cnt * sizeof (rusers_utmp));
360 				if (utmp_array_res.utmp_array_val == NULL) {
361 				    msgout("rpc.rusersd: malloc failed (1)");
362 				    break;
363 				}
364 				alloc_array_len = cnt;
365 			} else {
366 				alloc_array_len = utmp_array_res.utmp_array_len;
367 			}
368 			cnt = getutmpx_3(entries, RUSERSVERS_3, cnt);
369 			utmp_array_res.utmp_array_len = used_array_len = cnt;
370 			if (!svc_sendreply(transp, xdr_utmp_array,
371 					(caddr_t)&utmp_array_res))
372 				msgout(replyerr);
373 			utmp_array_res.utmp_array_len = alloc_array_len;
374 		}
375 		break;
376 	default:
377 		svcerr_noproc(transp);
378 		break;
379 	}
380 	_rpcsvcdirty = 0;
381 
382 }
383 
384 static void
free_ua_entry(rusers_utmp * uap)385 free_ua_entry(rusers_utmp *uap)
386 {
387 	if (uap == NULL)
388 		return;
389 	if (uap->ut_user)
390 		free(uap->ut_user);
391 	if (uap->ut_line)
392 		free(uap->ut_line);
393 	if (uap->ut_host)
394 		free(uap->ut_host);
395 }
396 
397 
398 
399 /* find & return number of minutes current tty has been idle */
400 static int
findidle(char * name,int ln,time_t now)401 findidle(char *name, int ln, time_t	now)
402 {
403 	struct stat stbuf;
404 	long lastaction, diff;
405 	char ttyname[32];
406 
407 	strcpy(ttyname, "/dev/");
408 	strncat(ttyname, name, ln);
409 	if (stat(ttyname, &stbuf) < 0)
410 		return (INT_MAX);
411 	lastaction = stbuf.st_atime;
412 	diff = now - lastaction;
413 	diff = DIV60(diff);
414 	if (diff < 0) diff = 0;
415 	return (diff);
416 }
417 
418 static void
usys5to_ru(struct utmpx * s5,struct ru_utmp * bss)419 usys5to_ru(struct utmpx *s5, 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
msgout(msg)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
closedown(sig)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
min(a,b)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