Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / cmd / rpcsvc / rpc.rusersd.c
blob8066ef0a8cd554bef12a84906a9786d498655473
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
22 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
26 /* All Rights Reserved */
28 * University Copyright- Copyright (c) 1982, 1986, 1988
29 * The Regents of the University of California
30 * All Rights Reserved
32 * University Acknowledgment- Portions of this document are derived from
33 * software developed by the University of California, Berkeley, and its
34 * contributors.
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>
50 #ifdef DEBUG
51 #define RPC_SVC_FG
52 #endif
54 #define _RPCSVC_CLOSEDOWN 120
56 static void rusers_service();
57 static void closedown();
58 static void msgout();
59 static unsigned min();
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 */
67 #define DIV60(t) ((t+30)/60) /* x/60 rounded */
69 #define ALL_ENTRIES 1
70 #define REAL_USERS 0
72 utmp_array utmp_array_res;
73 int used_array_len = 0;
74 struct utmpidlearr utmpidlearr;
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);
80 int
81 main(int argc, char *argv[])
83 pid_t pid;
84 int i;
85 int connmaxrec = RPC_MAXDATASIZE;
88 * Set non-blocking mode and maximum record size for
89 * connection oriented RPC transports.
91 if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) {
92 msgout("unable to set maximum RPC record size");
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.
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();
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
119 if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) {
120 msgout("cannot create server handle");
121 exit(1);
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);
130 if (!svc_reg(transp, RUSERSPROG, RUSERSVERS_IDLE,
131 rusers_service, 0)) {
132 msgout("unable to register (RUSERSPROG, RUSERSVERS_IDLE).");
133 exit(1);
135 (void) signal(SIGALRM, closedown);
136 (void) alarm(_RPCSVC_CLOSEDOWN);
137 svc_run();
138 msgout("svc_run returned");
139 exit(1);
140 /* NOTREACHED */
142 #ifndef RPC_SVC_FG
143 pid = fork();
144 if (pid < 0) {
145 perror("rpc.rusersd: cannot fork");
146 exit(1);
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);
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);
166 svc_run();
167 msgout("svc_run returned");
168 return (1);
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.
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.
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 */
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();
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;
206 * List only user processes.
207 * XXX modified to exclude cmdtool style window entries.
209 if ((all == REAL_USERS) && ((utent->ut_type != USER_PROCESS) ||
210 nonuserx(*utent)))
211 continue;
213 if (version == RUSERSVERS_IDLE) {
215 * need to free this; done after svc_sendreply.
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));
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
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));
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
264 cnt++;
266 return (cnt);
270 * "string" is a character array with maximum size "size". Return a
271 * malloc'd string that's a duplicate of the string.
273 char *
274 s_malodup(string, size)
275 char *string;
276 int size;
278 char *tmp;
280 tmp = (char *)malloc(size+1);
281 if (tmp == NULL) {
282 msgout("rpc.rusersd: malloc failed (2)");
283 return (NULL);
285 strncpy(tmp, string, size);
286 tmp[size] = '\0';
287 return (tmp);
292 console_idle(now)
293 time_t now;
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.
302 return (min((unsigned)findidle("kbd", strlen("kbd"), now),
303 (unsigned)findidle("mouse", strlen("mouse"), now)));
306 static void
307 rusers_service(rqstp, transp)
308 register struct svc_req *rqstp;
309 register SVCXPRT *transp;
311 int i;
312 int cnt;
313 char *replyerr = "rpc.rusersd: error replying to request";
315 _rpcsvcrecent = _rpcsvcdirty = 1;
316 switch (rqstp->rq_proc) {
317 case 0:
318 if (svc_sendreply(transp, xdr_void, 0) == FALSE) {
319 msgout(replyerr);
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]);
341 free(utmpidlearr.uia_arr);
342 } else if (rqstp->rq_vers == RUSERSVERS_3) {
343 int entries, alloc_array_len;
346 * Always free strings from previous results array
348 for (i = 0; i < used_array_len; i++) {
349 free_ua_entry(&utmp_array_res.utmp_array_val[i]);
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;
362 alloc_array_len = cnt;
363 } else {
364 alloc_array_len = utmp_array_res.utmp_array_len;
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;
373 break;
374 default:
375 svcerr_noproc(transp);
376 break;
378 _rpcsvcdirty = 0;
382 static void
383 free_ua_entry(rusers_utmp *uap)
385 if (uap == NULL)
386 return;
387 free(uap->ut_user);
388 free(uap->ut_line);
389 free(uap->ut_host);
394 /* find & return number of minutes current tty has been idle */
395 static int
396 findidle(char *name, int ln, time_t now)
398 struct stat stbuf;
399 long lastaction, diff;
400 char ttyname[32];
402 strcpy(ttyname, "/dev/");
403 strncat(ttyname, name, ln);
404 if (stat(ttyname, &stbuf) < 0)
405 return (INT_MAX);
406 lastaction = stbuf.st_atime;
407 diff = now - lastaction;
408 diff = DIV60(diff);
409 if (diff < 0) diff = 0;
410 return (diff);
413 static void
414 usys5to_ru(struct utmpx *s5, struct ru_utmp *bss)
416 int i;
418 #ifdef DEBUG
419 printf("sizeof (bss->ut_host) == %d\n", sizeof (bss->ut_host));
420 #endif
421 strncpy(bss->ut_name, s5->ut_name, sizeof (bss->ut_name));
422 strncpy(bss->ut_line, s5->ut_line, sizeof (bss->ut_line));
423 strncpy(bss->ut_host, s5->ut_host, sizeof (bss->ut_host));
424 bss->ut_time = s5->ut_xtime;
427 static void
428 msgout(msg)
429 char *msg;
431 #ifdef RPC_SVC_FG
432 if (_rpcpmstart)
433 syslog(LOG_ERR, msg);
434 else
435 (void) fprintf(stderr, "%s\n", msg);
436 #else
437 syslog(LOG_ERR, msg);
438 #endif
441 static void
442 closedown(sig)
443 int sig;
445 if (_rpcsvcrecent) {
446 _rpcsvcrecent = 0;
447 } else {
448 if (_rpcsvcdirty == 0) {
449 int i, openfd;
450 struct t_info tinfo;
452 if (t_getinfo(0, &tinfo) || (tinfo.servtype == T_CLTS))
453 exit(0);
455 for (i = 0, openfd = 0;
456 i < svc_max_pollfd && openfd < 2;
457 i++) {
458 if (svc_pollfd[i].fd >= 0)
459 openfd++;
462 if (openfd <= 1)
463 exit(0);
466 (void) signal(SIGALRM, closedown);
467 (void) alarm(_RPCSVC_CLOSEDOWN);
470 unsigned
471 min(a, b)
472 unsigned a;
473 unsigned b;
475 if (a < b)
476 return (a);
477 else
478 return (b);