1 /* 2 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for 3 * unrestricted use provided that this legend is included on all tape 4 * media and as a part of the software program in whole or part. Users 5 * may copy or modify Sun RPC without charge, but are not authorized 6 * to license or distribute it to anyone else except as part of a product or 7 * program developed by the user. 8 * 9 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE 10 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR 11 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. 12 * 13 * Sun RPC is provided with no support and without any obligation on the 14 * part of Sun Microsystems, Inc. to assist in its use, correction, 15 * modification or enhancement. 16 * 17 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE 18 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC 19 * OR ANY PART THEREOF. 20 * 21 * In no event will Sun Microsystems, Inc. be liable for any lost revenue 22 * or profits or other special, indirect and consequential damages, even if 23 * Sun has been advised of the possibility of such damages. 24 * 25 * Sun Microsystems, Inc. 26 * 2550 Garcia Avenue 27 * Mountain View, California 94043 28 * 29 * @(#)rpc.rstatd.c 1.1 86/09/25 Copyr 1984 Sun Micro 30 * @(#)rstat_proc.c 2.2 88/08/01 4.0 RPCSRC 31 * $FreeBSD: src/libexec/rpc.rstatd/rstat_proc.c,v 1.14.2.1 2002/07/11 17:17:56 alfred Exp $ 32 * $DragonFly: src/libexec/rpc.rstatd/rstat_proc.c,v 1.3 2003/11/14 03:54:31 dillon Exp $ 33 */ 34 35 /* 36 * rstat service: built with rstat.x and derived from rpc.rstatd.c 37 * 38 * Copyright (c) 1984 by Sun Microsystems, Inc. 39 */ 40 41 #include <sys/types.h> 42 #include <sys/dkstat.h> 43 #include <sys/socket.h> 44 #include <sys/sysctl.h> 45 #include <sys/time.h> 46 #include <sys/vmmeter.h> 47 #include <sys/param.h> 48 49 #include <err.h> 50 #include <fcntl.h> 51 #include <kvm.h> 52 #include <limits.h> 53 #include <signal.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <syslog.h> 58 #include <unistd.h> 59 #include <devstat.h> 60 61 #include <net/if.h> 62 #include <net/if_mib.h> 63 64 #undef FSHIFT /* Use protocol's shift and scale values */ 65 #undef FSCALE 66 #undef if_ipackets 67 #undef if_ierrors 68 #undef if_opackets 69 #undef if_oerrors 70 #undef if_collisions 71 #include <rpcsvc/rstat.h> 72 73 struct nlist nl[] = { 74 #define X_CPTIME 0 75 { "_cp_time" }, 76 #define X_CNT 1 77 { "_cnt" }, 78 { "" }, 79 }; 80 81 int havedisk (void); 82 void updatexfers (int, int *); 83 void setup (void); 84 int stats_service(); 85 86 extern int from_inetd; 87 int sincelastreq = 0; /* number of alarms since last request */ 88 extern int closedown; 89 90 union { 91 struct stats s1; 92 struct statsswtch s2; 93 struct statstime s3; 94 } stats_all; 95 96 void updatestat(); 97 static stat_is_init = 0; 98 static kvm_t *kd; 99 100 static int cp_time_xlat[RSTAT_CPUSTATES] = { CP_USER, CP_NICE, CP_SYS, 101 CP_IDLE }; 102 static long bsd_cp_time[CPUSTATES]; 103 104 105 #ifndef FSCALE 106 #define FSCALE (1 << 8) 107 #endif 108 109 void 110 stat_init() 111 { 112 stat_is_init = 1; 113 setup(); 114 alarm(0); 115 updatestat(); 116 (void) signal(SIGALRM, updatestat); 117 alarm(1); 118 } 119 120 statstime * 121 rstatproc_stats_3_svc(argp, rqstp) 122 void *argp; 123 struct svc_req *rqstp; 124 { 125 if (! stat_is_init) 126 stat_init(); 127 sincelastreq = 0; 128 return(&stats_all.s3); 129 } 130 131 statsswtch * 132 rstatproc_stats_2_svc(argp, rqstp) 133 void *argp; 134 struct svc_req *rqstp; 135 { 136 if (! stat_is_init) 137 stat_init(); 138 sincelastreq = 0; 139 return(&stats_all.s2); 140 } 141 142 stats * 143 rstatproc_stats_1_svc(argp, rqstp) 144 void *argp; 145 struct svc_req *rqstp; 146 { 147 if (! stat_is_init) 148 stat_init(); 149 sincelastreq = 0; 150 return(&stats_all.s1); 151 } 152 153 u_int * 154 rstatproc_havedisk_3_svc(argp, rqstp) 155 void *argp; 156 struct svc_req *rqstp; 157 { 158 static u_int have; 159 160 if (! stat_is_init) 161 stat_init(); 162 sincelastreq = 0; 163 have = havedisk(); 164 return(&have); 165 } 166 167 u_int * 168 rstatproc_havedisk_2_svc(argp, rqstp) 169 void *argp; 170 struct svc_req *rqstp; 171 { 172 return(rstatproc_havedisk_3_svc(argp, rqstp)); 173 } 174 175 u_int * 176 rstatproc_havedisk_1_svc(argp, rqstp) 177 void *argp; 178 struct svc_req *rqstp; 179 { 180 return(rstatproc_havedisk_3_svc(argp, rqstp)); 181 } 182 183 void 184 updatestat() 185 { 186 int i, hz; 187 struct clockinfo clockrate; 188 struct vmmeter cnt; 189 struct ifmibdata ifmd; 190 double avrun[3]; 191 struct timeval tm, btm; 192 int mib[6]; 193 size_t len; 194 int ifcount; 195 196 #ifdef DEBUG 197 fprintf(stderr, "entering updatestat\n"); 198 #endif 199 if (sincelastreq >= closedown) { 200 #ifdef DEBUG 201 fprintf(stderr, "about to closedown\n"); 202 #endif 203 kvm_close(kd); 204 if (from_inetd) 205 exit(0); 206 else { 207 stat_is_init = 0; 208 return; 209 } 210 } 211 sincelastreq++; 212 213 mib[0] = CTL_KERN; 214 mib[1] = KERN_CLOCKRATE; 215 len = sizeof clockrate; 216 if (sysctl(mib, 2, &clockrate, &len, 0, 0) < 0) { 217 syslog(LOG_ERR, "sysctl(kern.clockrate): %m"); 218 exit(1); 219 } 220 hz = clockrate.hz; 221 222 if (kvm_read(kd, (long)nl[X_CPTIME].n_value, (char *)bsd_cp_time, sizeof(bsd_cp_time)) 223 != sizeof(bsd_cp_time)) { 224 syslog(LOG_ERR, "rstat: can't read cp_time from kmem"); 225 exit(1); 226 } 227 for(i = 0; i < RSTAT_CPUSTATES ; i++) 228 stats_all.s1.cp_time[i] = bsd_cp_time[cp_time_xlat[i]]; 229 230 (void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0])); 231 232 stats_all.s2.avenrun[0] = avrun[0] * FSCALE; 233 stats_all.s2.avenrun[1] = avrun[1] * FSCALE; 234 stats_all.s2.avenrun[2] = avrun[2] * FSCALE; 235 236 mib[0] = CTL_KERN; 237 mib[1] = KERN_BOOTTIME; 238 len = sizeof btm; 239 if (sysctl(mib, 2, &btm, &len, 0, 0) < 0) { 240 syslog(LOG_ERR, "sysctl(kern.boottime): %m"); 241 exit(1); 242 } 243 244 stats_all.s2.boottime.tv_sec = btm.tv_sec; 245 stats_all.s2.boottime.tv_usec = btm.tv_usec; 246 247 248 #ifdef DEBUG 249 fprintf(stderr, "%d %d %d %d\n", stats_all.s1.cp_time[0], 250 stats_all.s1.cp_time[1], stats_all.s1.cp_time[2], stats_all.s1.cp_time[3]); 251 #endif 252 253 /* XXX - should use sysctl */ 254 if (kvm_read(kd, (long)nl[X_CNT].n_value, (char *)&cnt, sizeof cnt) != sizeof cnt) { 255 syslog(LOG_ERR, "rstat: can't read cnt from kmem"); 256 exit(1); 257 } 258 stats_all.s1.v_pgpgin = cnt.v_vnodepgsin; 259 stats_all.s1.v_pgpgout = cnt.v_vnodepgsout; 260 stats_all.s1.v_pswpin = cnt.v_swappgsin; 261 stats_all.s1.v_pswpout = cnt.v_swappgsout; 262 stats_all.s1.v_intr = cnt.v_intr; 263 gettimeofday(&tm, (struct timezone *) 0); 264 stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) + 265 hz*(tm.tv_usec - btm.tv_usec)/1000000; 266 stats_all.s2.v_swtch = cnt.v_swtch; 267 268 /* update disk transfers */ 269 updatexfers(RSTAT_DK_NDRIVE, stats_all.s1.dk_xfer); 270 271 mib[0] = CTL_NET; 272 mib[1] = PF_LINK; 273 mib[2] = NETLINK_GENERIC; 274 mib[3] = IFMIB_SYSTEM; 275 mib[4] = IFMIB_IFCOUNT; 276 len = sizeof ifcount; 277 if (sysctl(mib, 5, &ifcount, &len, 0, 0) < 0) { 278 syslog(LOG_ERR, "sysctl(net.link.generic.system.ifcount): %m"); 279 exit(1); 280 } 281 282 stats_all.s1.if_ipackets = 0; 283 stats_all.s1.if_opackets = 0; 284 stats_all.s1.if_ierrors = 0; 285 stats_all.s1.if_oerrors = 0; 286 stats_all.s1.if_collisions = 0; 287 for (i = 1; i <= ifcount; i++) { 288 len = sizeof ifmd; 289 mib[3] = IFMIB_IFDATA; 290 mib[4] = i; 291 mib[5] = IFDATA_GENERAL; 292 if (sysctl(mib, 6, &ifmd, &len, 0, 0) < 0) { 293 syslog(LOG_ERR, "sysctl(net.link.ifdata.%d.general)" 294 ": %m", i); 295 exit(1); 296 } 297 298 stats_all.s1.if_ipackets += ifmd.ifmd_data.ifi_ipackets; 299 stats_all.s1.if_opackets += ifmd.ifmd_data.ifi_opackets; 300 stats_all.s1.if_ierrors += ifmd.ifmd_data.ifi_ierrors; 301 stats_all.s1.if_oerrors += ifmd.ifmd_data.ifi_oerrors; 302 stats_all.s1.if_collisions += ifmd.ifmd_data.ifi_collisions; 303 } 304 gettimeofday((struct timeval *)&stats_all.s3.curtime, 305 (struct timezone *) 0); 306 alarm(1); 307 } 308 309 void 310 setup() 311 { 312 char errbuf[_POSIX2_LINE_MAX]; 313 314 int en; 315 316 if ((kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf)) == NULL) { 317 syslog(LOG_ERR, "rpc.rstatd, %s", errbuf); 318 exit(1); 319 } 320 321 if ((en = kvm_nlist(kd, nl)) != 0) { 322 syslog(LOG_ERR, "rstatd: Can't get namelist. %d", en); 323 exit (1); 324 } 325 } 326 327 /* 328 * returns true if have a disk 329 */ 330 int 331 havedisk() 332 { 333 register int i; 334 struct statinfo stats; 335 int num_devices, retval = 0; 336 337 if ((num_devices = getnumdevs()) < 0) { 338 syslog(LOG_ERR, "rstatd: can't get number of devices: %s", 339 devstat_errbuf); 340 exit(1); 341 } 342 343 if (checkversion() < 0) { 344 syslog(LOG_ERR, "rstatd: %s", devstat_errbuf); 345 exit(1); 346 } 347 348 stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); 349 bzero(stats.dinfo, sizeof(struct devinfo)); 350 351 if (getdevs(&stats) == -1) { 352 syslog(LOG_ERR, "rstatd: can't get device list: %s", 353 devstat_errbuf); 354 exit(1); 355 } 356 for (i = 0; i < stats.dinfo->numdevs; i++) { 357 if (((stats.dinfo->devices[i].device_type 358 & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) 359 && ((stats.dinfo->devices[i].device_type 360 & DEVSTAT_TYPE_PASS) == 0)) { 361 retval = 1; 362 break; 363 } 364 } 365 366 if (stats.dinfo->mem_ptr) 367 free(stats.dinfo->mem_ptr); 368 369 free(stats.dinfo); 370 return(retval); 371 } 372 373 void 374 updatexfers(numdevs, devs) 375 int numdevs, *devs; 376 { 377 register int i, j, t; 378 struct statinfo stats; 379 int num_devices = 0; 380 u_int64_t total_transfers; 381 382 if ((num_devices = getnumdevs()) < 0) { 383 syslog(LOG_ERR, "rstatd: can't get number of devices: %s", 384 devstat_errbuf); 385 exit(1); 386 } 387 388 if (checkversion() < 0) { 389 syslog(LOG_ERR, "rstatd: %s", devstat_errbuf); 390 exit(1); 391 } 392 393 stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); 394 bzero(stats.dinfo, sizeof(struct devinfo)); 395 396 if (getdevs(&stats) == -1) { 397 syslog(LOG_ERR, "rstatd: can't get device list: %s", 398 devstat_errbuf); 399 exit(1); 400 } 401 402 for (i = 0, j = 0; i < stats.dinfo->numdevs && j < numdevs; i++) { 403 if (((stats.dinfo->devices[i].device_type 404 & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) 405 && ((stats.dinfo->devices[i].device_type 406 & DEVSTAT_TYPE_PASS) == 0)) { 407 total_transfers = stats.dinfo->devices[i].num_reads + 408 stats.dinfo->devices[i].num_writes + 409 stats.dinfo->devices[i].num_other; 410 /* 411 * XXX KDM If the total transfers for this device 412 * are greater than the amount we can fit in a 413 * signed integer, just set them to the maximum 414 * amount we can fit in a signed integer. I have a 415 * feeling that the rstat protocol assumes 32-bit 416 * integers, so this could well break on a 64-bit 417 * architecture like the Alpha. 418 */ 419 if (total_transfers > INT_MAX) 420 t = INT_MAX; 421 else 422 t = total_transfers; 423 devs[j] = t; 424 j++; 425 } 426 } 427 428 if (stats.dinfo->mem_ptr) 429 free(stats.dinfo->mem_ptr); 430 431 free(stats.dinfo); 432 } 433 434 void 435 rstat_service(rqstp, transp) 436 struct svc_req *rqstp; 437 SVCXPRT *transp; 438 { 439 union { 440 int fill; 441 } argument; 442 char *result; 443 bool_t (*xdr_argument)(), (*xdr_result)(); 444 char *(*local)(); 445 446 switch (rqstp->rq_proc) { 447 case NULLPROC: 448 (void)svc_sendreply(transp, xdr_void, (char *)NULL); 449 goto leave; 450 451 case RSTATPROC_STATS: 452 xdr_argument = xdr_void; 453 xdr_result = xdr_statstime; 454 switch (rqstp->rq_vers) { 455 case RSTATVERS_ORIG: 456 local = (char *(*)()) rstatproc_stats_1_svc; 457 break; 458 case RSTATVERS_SWTCH: 459 local = (char *(*)()) rstatproc_stats_2_svc; 460 break; 461 case RSTATVERS_TIME: 462 local = (char *(*)()) rstatproc_stats_3_svc; 463 break; 464 default: 465 svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); 466 goto leave; 467 /*NOTREACHED*/ 468 } 469 break; 470 471 case RSTATPROC_HAVEDISK: 472 xdr_argument = xdr_void; 473 xdr_result = xdr_u_int; 474 switch (rqstp->rq_vers) { 475 case RSTATVERS_ORIG: 476 local = (char *(*)()) rstatproc_havedisk_1_svc; 477 break; 478 case RSTATVERS_SWTCH: 479 local = (char *(*)()) rstatproc_havedisk_2_svc; 480 break; 481 case RSTATVERS_TIME: 482 local = (char *(*)()) rstatproc_havedisk_3_svc; 483 break; 484 default: 485 svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); 486 goto leave; 487 /*NOTREACHED*/ 488 } 489 break; 490 491 default: 492 svcerr_noproc(transp); 493 goto leave; 494 } 495 bzero((char *)&argument, sizeof(argument)); 496 if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) { 497 svcerr_decode(transp); 498 goto leave; 499 } 500 result = (*local)(&argument, rqstp); 501 if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { 502 svcerr_systemerr(transp); 503 } 504 if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) 505 errx(1, "unable to free arguments"); 506 leave: 507 if (from_inetd) 508 exit(0); 509 } 510