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