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 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 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 * 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 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 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 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 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 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 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 }