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