1 /* $NetBSD: rstat_proc.c,v 1.29 1999/03/25 08:07:47 bgrayson Exp $ */ 2 3 /* 4 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for 5 * unrestricted use provided that this legend is included on all tape 6 * media and as a part of the software program in whole or part. Users 7 * may copy or modify Sun RPC without charge, but are not authorized 8 * to license or distribute it to anyone else except as part of a product or 9 * program developed by the user. 10 * 11 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE 12 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR 13 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. 14 * 15 * Sun RPC is provided with no support and without any obligation on the 16 * part of Sun Microsystems, Inc. to assist in its use, correction, 17 * modification or enhancement. 18 * 19 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE 20 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC 21 * OR ANY PART THEREOF. 22 * 23 * In no event will Sun Microsystems, Inc. be liable for any lost revenue 24 * or profits or other special, indirect and consequential damages, even if 25 * Sun has been advised of the possibility of such damages. 26 * 27 * Sun Microsystems, Inc. 28 * 2550 Garcia Avenue 29 * Mountain View, California 94043 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "from: @(#)rpc.rstatd.c 1.1 86/09/25 Copyr 1984 Sun Micro"; 36 static char sccsid[] = "from: @(#)rstat_proc.c 2.2 88/08/01 4.0 RPCSRC"; 37 #else 38 __RCSID("$NetBSD: rstat_proc.c,v 1.29 1999/03/25 08:07:47 bgrayson Exp $"); 39 #endif 40 #endif 41 42 /* 43 * rstat service: built with rstat.x and derived from rpc.rstatd.c 44 * 45 * Copyright (c) 1984 by Sun Microsystems, Inc. 46 */ 47 48 #include <sys/param.h> 49 #include <sys/socket.h> 50 51 #include <errno.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <signal.h> 56 #include <fcntl.h> 57 #include <kvm.h> 58 #include <limits.h> 59 #include <nlist.h> 60 #include <syslog.h> 61 #ifdef BSD 62 #include <sys/sysctl.h> 63 #include <vm/vm.h> 64 #include <uvm/uvm_extern.h> 65 #include <sys/dkstat.h> 66 #include "dkstats.h" 67 #else 68 #include <sys/dk.h> 69 #endif 70 71 #include <net/if.h> 72 73 /* 74 * XXX 75 * 76 * this is a huge hack to stop `struct pmap' being 77 * defined twice! 78 */ 79 #define _RPC_PMAP_PROT_H_ 80 #include <rpc/rpc.h> 81 82 #undef FSHIFT /* Use protocol's shift and scale values */ 83 #undef FSCALE 84 #undef DK_NDRIVE 85 #undef CPUSTATES 86 #undef if_ipackets 87 #undef if_ierrors 88 #undef if_opackets 89 #undef if_oerrors 90 #undef if_collisions 91 #include <rpcsvc/rstat.h> 92 93 #ifdef BSD 94 #define BSD_CPUSTATES 5 /* Use protocol's idea of CPU states */ 95 int cp_xlat[CPUSTATES] = { CP_USER, CP_NICE, CP_SYS, CP_IDLE }; 96 #endif 97 98 struct nlist nl[] = { 99 #define X_IFNET 0 100 { "_ifnet" }, 101 { NULL }, 102 }; 103 104 extern int dk_ndrive; /* From dkstats.c */ 105 extern struct _disk cur, last; 106 int hz; 107 char *memf = NULL, *nlistf = NULL; 108 109 struct ifnet_head ifnetq; /* chain of ethernet interfaces */ 110 int numintfs; 111 112 extern int from_inetd; 113 int sincelastreq = 0; /* number of alarms since last request */ 114 extern int closedown; 115 kvm_t *kfd; 116 117 union { 118 struct stats s1; 119 struct statsswtch s2; 120 struct statstime s3; 121 } stats_all; 122 123 extern void dkreadstats __P((void)); 124 extern int dkinit __P((int, gid_t)); 125 126 void updatestat __P((int)); 127 void setup __P((void)); 128 void setup_kd_once __P((void)); 129 void stat_init __P((void)); 130 int havedisk __P((void)); 131 void rstat_service __P((struct svc_req *, SVCXPRT *)); 132 133 static int stat_is_init = 0; 134 135 #ifndef FSCALE 136 #define FSCALE (1 << 8) 137 #endif 138 139 void 140 stat_init() 141 { 142 stat_is_init = 1; 143 setup(); 144 updatestat(0); 145 (void) signal(SIGALRM, updatestat); 146 alarm(1); 147 } 148 149 statstime * 150 rstatproc_stats_3_svc(arg, rqstp) 151 void *arg; 152 struct svc_req *rqstp; 153 { 154 if (!stat_is_init) 155 stat_init(); 156 sincelastreq = 0; 157 return (&stats_all.s3); 158 } 159 160 statsswtch * 161 rstatproc_stats_2_svc(arg, rqstp) 162 void *arg; 163 struct svc_req *rqstp; 164 { 165 if (!stat_is_init) 166 stat_init(); 167 sincelastreq = 0; 168 stats_all.s2.if_opackets = stats_all.s3.if_opackets; 169 return (&stats_all.s2); 170 } 171 172 stats * 173 rstatproc_stats_1_svc(arg, rqstp) 174 void *arg; 175 struct svc_req *rqstp; 176 { 177 if (!stat_is_init) 178 stat_init(); 179 sincelastreq = 0; 180 stats_all.s1.if_opackets = stats_all.s3.if_opackets; 181 return (&stats_all.s1); 182 } 183 184 u_int * 185 rstatproc_havedisk_3_svc(arg, rqstp) 186 void *arg; 187 struct svc_req *rqstp; 188 { 189 static u_int have; 190 191 if (!stat_is_init) 192 stat_init(); 193 sincelastreq = 0; 194 have = havedisk(); 195 return (&have); 196 } 197 198 u_int * 199 rstatproc_havedisk_2_svc(arg, rqstp) 200 void *arg; 201 struct svc_req *rqstp; 202 { 203 return (rstatproc_havedisk_3_svc(arg, rqstp)); 204 } 205 206 u_int * 207 rstatproc_havedisk_1_svc(arg, rqstp) 208 void *arg; 209 struct svc_req *rqstp; 210 { 211 return (rstatproc_havedisk_3_svc(arg, rqstp)); 212 } 213 214 void 215 updatestat(dummy) 216 int dummy; 217 { 218 long off; 219 int i; 220 size_t len; 221 int mib[2]; 222 struct uvmexp uvmexp; 223 struct ifnet ifnet; 224 double avrun[3]; 225 struct timeval tm, btm; 226 227 #ifdef DEBUG 228 syslog(LOG_DEBUG, "entering updatestat"); 229 #endif 230 if (sincelastreq >= closedown) { 231 #ifdef DEBUG 232 syslog(LOG_DEBUG, "about to closedown"); 233 #endif 234 if (from_inetd) 235 exit(0); 236 else { 237 stat_is_init = 0; 238 return; 239 } 240 } 241 sincelastreq++; 242 243 /* 244 * dkreadstats reads in the "disk_count" as well as the "disklist" 245 * statistics. It also retrieves "hz" and the "cp_time" array. 246 */ 247 dkreadstats(); 248 memset(stats_all.s3.dk_xfer, 0, sizeof(stats_all.s3.dk_xfer)); 249 for (i = 0; i < dk_ndrive && i < DK_NDRIVE; i++) 250 stats_all.s3.dk_xfer[i] = cur.dk_xfer[i]; 251 252 #ifdef BSD 253 for (i = 0; i < CPUSTATES; i++) 254 stats_all.s3.cp_time[i] = cur.cp_time[cp_xlat[i]]; 255 #else 256 if (kvm_read(kfd, (long)nl[X_CPTIME].n_value, 257 (char *)stats_all.s3.cp_time, 258 sizeof (stats_all.s3.cp_time)) 259 != sizeof (stats_all.s3.cp_time)) { 260 syslog(LOG_ERR, "can't read cp_time from kmem"); 261 exit(1); 262 } 263 #endif 264 #ifdef BSD 265 (void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0])); 266 #endif 267 stats_all.s3.avenrun[0] = avrun[0] * FSCALE; 268 stats_all.s3.avenrun[1] = avrun[1] * FSCALE; 269 stats_all.s3.avenrun[2] = avrun[2] * FSCALE; 270 mib[0] = CTL_KERN; 271 mib[1] = KERN_BOOTTIME; 272 len = sizeof(btm); 273 if (sysctl(mib, 2, &btm, &len, NULL, 0) < 0) { 274 syslog(LOG_ERR, "can't sysctl kern.boottime"); 275 exit(1); 276 } 277 stats_all.s3.boottime.tv_sec = btm.tv_sec; 278 stats_all.s3.boottime.tv_usec = btm.tv_usec; 279 280 281 #ifdef DEBUG 282 syslog(LOG_DEBUG, "%d %d %d %d\n", stats_all.s3.cp_time[0], 283 stats_all.s3.cp_time[1], stats_all.s3.cp_time[2], stats_all.s3.cp_time[3]); 284 #endif 285 286 mib[0] = CTL_VM; 287 mib[1] = VM_UVMEXP; 288 len = sizeof(uvmexp); 289 if (sysctl(mib, 2, &uvmexp, &len, NULL, 0) < 0) { 290 syslog(LOG_ERR, "can't sysctl vm.uvmexp"); 291 exit(1); 292 } 293 stats_all.s3.v_pgpgin = uvmexp.fltanget; 294 stats_all.s3.v_pgpgout = uvmexp.pdpageouts; 295 stats_all.s3.v_pswpin = uvmexp.swapins; 296 stats_all.s3.v_pswpout = uvmexp.swapouts; 297 stats_all.s3.v_intr = uvmexp.intrs; 298 stats_all.s3.v_swtch = uvmexp.swtch; 299 gettimeofday(&tm, (struct timezone *) 0); 300 stats_all.s3.v_intr -= hz*(tm.tv_sec - btm.tv_sec) + 301 hz*(tm.tv_usec - btm.tv_usec)/1000000; 302 303 stats_all.s3.if_ipackets = 0; 304 stats_all.s3.if_opackets = 0; 305 stats_all.s3.if_ierrors = 0; 306 stats_all.s3.if_oerrors = 0; 307 stats_all.s3.if_collisions = 0; 308 for (off = (long)ifnetq.tqh_first, i = 0; off && i < numintfs; i++) { 309 if (kvm_read(kfd, off, (char *)&ifnet, sizeof ifnet) != 310 sizeof ifnet) { 311 syslog(LOG_ERR, "can't read ifnet from kmem"); 312 exit(1); 313 } 314 stats_all.s3.if_ipackets += ifnet.if_data.ifi_ipackets; 315 stats_all.s3.if_opackets += ifnet.if_data.ifi_opackets; 316 stats_all.s3.if_ierrors += ifnet.if_data.ifi_ierrors; 317 stats_all.s3.if_oerrors += ifnet.if_data.ifi_oerrors; 318 stats_all.s3.if_collisions += ifnet.if_data.ifi_collisions; 319 off = (long)ifnet.if_list.tqe_next; 320 } 321 gettimeofday((struct timeval *)&stats_all.s3.curtime, 322 (struct timezone *) 0); 323 alarm(1); 324 } 325 326 void 327 setup_kd_once() 328 { 329 char errbuf[_POSIX2_LINE_MAX]; 330 kfd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); 331 if (kfd == NULL) { 332 syslog(LOG_ERR, "%s", errbuf); 333 exit (1); 334 } 335 } 336 337 void 338 setup() 339 { 340 struct ifnet ifnet; 341 long off; 342 static int is_kfd_setup = 0; 343 344 /* setup() is called after each dormant->active 345 * transition. Since we never close the kvm files 346 * (there's no reason), make sure we don't open them 347 * each time, as that can lead to exhaustion of all open 348 * files! */ 349 if (!is_kfd_setup) { 350 setup_kd_once(); 351 is_kfd_setup = 1; 352 } 353 354 if (kvm_nlist(kfd, nl) != 0) { 355 syslog(LOG_ERR, "can't get namelist"); 356 exit (1); 357 } 358 359 if (kvm_read(kfd, (long)nl[X_IFNET].n_value, &ifnetq, 360 sizeof ifnetq) != sizeof ifnetq) { 361 syslog(LOG_ERR, "can't read ifnet queue head from kmem"); 362 exit(1); 363 } 364 365 numintfs = 0; 366 for (off = (long)ifnetq.tqh_first; off;) { 367 if (kvm_read(kfd, off, (char *)&ifnet, sizeof ifnet) != 368 sizeof ifnet) { 369 syslog(LOG_ERR, "can't read ifnet from kmem"); 370 exit(1); 371 } 372 numintfs++; 373 off = (long)ifnet.if_list.tqe_next; 374 } 375 dkinit(0, getgid()); 376 } 377 378 /* 379 * returns true if have a disk 380 */ 381 int 382 havedisk() 383 { 384 return dk_ndrive != 0; 385 } 386 387 void 388 rstat_service(rqstp, transp) 389 struct svc_req *rqstp; 390 SVCXPRT *transp; 391 { 392 union { 393 int fill; 394 } argument; 395 char *result; 396 xdrproc_t xdr_argument, xdr_result; 397 char *(*local) __P((void *, struct svc_req *)); 398 399 switch (rqstp->rq_proc) { 400 case NULLPROC: 401 (void)svc_sendreply(transp, xdr_void, (char *)NULL); 402 goto leave; 403 404 case RSTATPROC_STATS: 405 xdr_argument = (xdrproc_t)xdr_void; 406 xdr_result = (xdrproc_t)xdr_statstime; 407 switch (rqstp->rq_vers) { 408 case RSTATVERS_ORIG: 409 local = (char *(*) __P((void *, struct svc_req *))) 410 rstatproc_stats_1_svc; 411 break; 412 case RSTATVERS_SWTCH: 413 local = (char *(*) __P((void *, struct svc_req *))) 414 rstatproc_stats_2_svc; 415 break; 416 case RSTATVERS_TIME: 417 local = (char *(*) __P((void *, struct svc_req *))) 418 rstatproc_stats_3_svc; 419 break; 420 default: 421 svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); 422 goto leave; 423 } 424 break; 425 426 case RSTATPROC_HAVEDISK: 427 xdr_argument = (xdrproc_t)xdr_void; 428 xdr_result = (xdrproc_t)xdr_u_int; 429 switch (rqstp->rq_vers) { 430 case RSTATVERS_ORIG: 431 local = (char *(*) __P((void *, struct svc_req *))) 432 rstatproc_havedisk_1_svc; 433 break; 434 case RSTATVERS_SWTCH: 435 local = (char *(*) __P((void *, struct svc_req *))) 436 rstatproc_havedisk_2_svc; 437 break; 438 case RSTATVERS_TIME: 439 local = (char *(*) __P((void *, struct svc_req *))) 440 rstatproc_havedisk_3_svc; 441 break; 442 default: 443 svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); 444 goto leave; 445 } 446 break; 447 448 default: 449 svcerr_noproc(transp); 450 goto leave; 451 } 452 memset((char *)&argument, 0, sizeof(argument)); 453 if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) { 454 svcerr_decode(transp); 455 goto leave; 456 } 457 result = (*local)(&argument, rqstp); 458 if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { 459 svcerr_systemerr(transp); 460 } 461 if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) { 462 (void)fprintf(stderr, "unable to free arguments\n"); 463 exit(1); 464 } 465 leave: 466 if (from_inetd) 467 exit(0); 468 } 469