1 /* $NetBSD: rstat_proc.c,v 1.47 2012/06/19 06:09:36 dholland 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.47 2012/06/19 06:09:36 dholland 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/sched.h> 50 #include <sys/socket.h> 51 52 #include <errno.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <signal.h> 57 #include <fcntl.h> 58 #include <kvm.h> 59 #include <limits.h> 60 #include <nlist.h> 61 #include <syslog.h> 62 #ifdef BSD 63 #include <sys/sysctl.h> 64 #include <uvm/uvm_extern.h> 65 #include "drvstats.h" 66 #else 67 #include <sys/dk.h> 68 #endif 69 70 #include <net/if.h> 71 72 /* 73 * XXX 74 * 75 * this is a huge hack to stop `struct pmap' being 76 * defined twice! 77 */ 78 #define _RPC_PMAP_PROT_H_ 79 #include <rpc/rpc.h> 80 81 #undef FSHIFT /* Use protocol's shift and scale values */ 82 #undef FSCALE 83 #undef DK_NDRIVE 84 #undef CPUSTATES 85 #undef if_ipackets 86 #undef if_ierrors 87 #undef if_opackets 88 #undef if_oerrors 89 #undef if_collisions 90 #include <rpcsvc/rstat.h> 91 92 #ifdef BSD 93 #define BSD_CPUSTATES 5 /* Use protocol's idea of CPU states */ 94 int cp_xlat[CPUSTATES] = { CP_USER, CP_NICE, CP_SYS, CP_IDLE }; 95 #endif 96 97 struct nlist nl[] = { 98 #define X_IFNET 0 99 { "_ifnet", 0, 0, 0, 0 }, 100 { NULL, 0, 0, 0, 0 }, 101 }; 102 103 int hz; 104 char *memf = NULL, *nlistf = NULL; 105 106 struct ifnet_head ifnetq; /* chain of ethernet interfaces */ 107 unsigned int numintfs; 108 109 extern int from_inetd; 110 int sincelastreq = 0; /* number of alarms since last request */ 111 extern int closedown; 112 kvm_t *kfd; 113 114 union { 115 struct stats s1; 116 struct statsswtch s2; 117 struct statstime s3; 118 } stats_all; 119 120 void updatestat(int); 121 void setup(void); 122 void setup_kd_once(void); 123 void stat_init(void); 124 int havedisk(void); 125 void rstat_service(struct svc_req *, SVCXPRT *); 126 127 static int stat_is_init = 0; 128 129 #ifndef FSCALE 130 #define FSCALE (1 << 8) 131 #endif 132 133 void 134 stat_init(void) 135 { 136 stat_is_init = 1; 137 setup(); 138 updatestat(0); 139 (void) signal(SIGALRM, updatestat); 140 alarm(1); 141 } 142 143 statstime * 144 rstatproc_stats_3_svc(void *arg, struct svc_req *rqstp) 145 { 146 if (!stat_is_init) 147 stat_init(); 148 sincelastreq = 0; 149 return (&stats_all.s3); 150 } 151 152 statsswtch * 153 rstatproc_stats_2_svc(void *arg, struct svc_req *rqstp) 154 { 155 if (!stat_is_init) 156 stat_init(); 157 sincelastreq = 0; 158 stats_all.s2.if_opackets = stats_all.s3.if_opackets; 159 return (&stats_all.s2); 160 } 161 162 stats * 163 rstatproc_stats_1_svc(void *arg, struct svc_req *rqstp) 164 { 165 if (!stat_is_init) 166 stat_init(); 167 sincelastreq = 0; 168 stats_all.s1.if_opackets = stats_all.s3.if_opackets; 169 return (&stats_all.s1); 170 } 171 172 u_int * 173 rstatproc_havedisk_3_svc(void *arg, struct svc_req *rqstp) 174 { 175 static u_int have; 176 177 if (!stat_is_init) 178 stat_init(); 179 sincelastreq = 0; 180 have = havedisk(); 181 return (&have); 182 } 183 184 u_int * 185 rstatproc_havedisk_2_svc(void *arg, struct svc_req *rqstp) 186 { 187 return (rstatproc_havedisk_3_svc(arg, rqstp)); 188 } 189 190 u_int * 191 rstatproc_havedisk_1_svc(void *arg, struct svc_req *rqstp) 192 { 193 return (rstatproc_havedisk_3_svc(arg, rqstp)); 194 } 195 196 void 197 updatestat(int dummy) 198 { 199 long off; 200 unsigned int i; 201 size_t len; 202 int mib[2]; 203 struct uvmexp_sysctl uvmexp; 204 struct ifnet ifnet; 205 double avrun[3]; 206 struct timeval tm, btm; 207 208 #ifdef DEBUG 209 syslog(LOG_DEBUG, "entering updatestat"); 210 #endif 211 if (sincelastreq >= closedown) { 212 #ifdef DEBUG 213 syslog(LOG_DEBUG, "about to closedown"); 214 #endif 215 if (from_inetd) 216 exit(0); 217 else { 218 stat_is_init = 0; 219 return; 220 } 221 } 222 sincelastreq++; 223 224 /* 225 * drvreadstats reads in the "disk_count" as well as the "disklist" 226 * statistics. It also retrieves "hz" and the "cp_time" array. 227 */ 228 drvreadstats(); 229 memset(stats_all.s3.dk_xfer, 0, sizeof(stats_all.s3.dk_xfer)); 230 for (i = 0; i < ndrive && i < DK_NDRIVE; i++) 231 stats_all.s3.dk_xfer[i] = cur.rxfer[i] + cur.wxfer[i]; 232 233 #ifdef BSD 234 for (i = 0; i < CPUSTATES; i++) 235 stats_all.s3.cp_time[i] = cur.cp_time[cp_xlat[i]]; 236 #else 237 if (kvm_read(kfd, (long)nl[X_CPTIME].n_value, 238 (char *)stats_all.s3.cp_time, 239 sizeof (stats_all.s3.cp_time)) 240 != sizeof (stats_all.s3.cp_time)) { 241 syslog(LOG_ERR, "can't read cp_time from kmem"); 242 exit(1); 243 } 244 #endif 245 #ifdef BSD 246 (void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0])); 247 #endif 248 stats_all.s3.avenrun[0] = avrun[0] * FSCALE; 249 stats_all.s3.avenrun[1] = avrun[1] * FSCALE; 250 stats_all.s3.avenrun[2] = avrun[2] * FSCALE; 251 mib[0] = CTL_KERN; 252 mib[1] = KERN_BOOTTIME; 253 len = sizeof(btm); 254 if (sysctl(mib, 2, &btm, &len, NULL, 0) < 0) { 255 syslog(LOG_ERR, "can't sysctl kern.boottime"); 256 exit(1); 257 } 258 stats_all.s3.boottime.tv_sec = btm.tv_sec; 259 stats_all.s3.boottime.tv_usec = btm.tv_usec; 260 261 262 #ifdef DEBUG 263 syslog(LOG_DEBUG, "%d %d %d %d %d\n", stats_all.s3.cp_time[0], 264 stats_all.s3.cp_time[1], stats_all.s3.cp_time[2], 265 stats_all.s3.cp_time[3], stats_all.s3.cp_time[4]); 266 #endif 267 268 mib[0] = CTL_VM; 269 mib[1] = VM_UVMEXP2; 270 len = sizeof(uvmexp); 271 if (sysctl(mib, 2, &uvmexp, &len, NULL, 0) < 0) { 272 syslog(LOG_ERR, "can't sysctl vm.uvmexp2"); 273 exit(1); 274 } 275 stats_all.s3.v_pgpgin = uvmexp.fltanget; 276 stats_all.s3.v_pgpgout = uvmexp.pdpageouts; 277 stats_all.s3.v_pswpin = uvmexp.swapins; 278 stats_all.s3.v_pswpout = uvmexp.swapouts; 279 stats_all.s3.v_intr = uvmexp.intrs; 280 stats_all.s3.v_swtch = uvmexp.swtch; 281 gettimeofday(&tm, (struct timezone *) 0); 282 stats_all.s3.v_intr -= hz*(tm.tv_sec - btm.tv_sec) + 283 hz*(tm.tv_usec - btm.tv_usec)/1000000; 284 285 stats_all.s3.if_ipackets = 0; 286 stats_all.s3.if_opackets = 0; 287 stats_all.s3.if_ierrors = 0; 288 stats_all.s3.if_oerrors = 0; 289 stats_all.s3.if_collisions = 0; 290 for (off = (long)ifnetq.tqh_first, i = 0; off && i < numintfs; i++) { 291 if (kvm_read(kfd, off, (char *)&ifnet, sizeof ifnet) != 292 sizeof ifnet) { 293 syslog(LOG_ERR, "can't read ifnet from kmem"); 294 exit(1); 295 } 296 stats_all.s3.if_ipackets += ifnet.if_data.ifi_ipackets; 297 stats_all.s3.if_opackets += ifnet.if_data.ifi_opackets; 298 stats_all.s3.if_ierrors += ifnet.if_data.ifi_ierrors; 299 stats_all.s3.if_oerrors += ifnet.if_data.ifi_oerrors; 300 stats_all.s3.if_collisions += ifnet.if_data.ifi_collisions; 301 off = (long)ifnet.if_list.tqe_next; 302 } 303 stats_all.s3.curtime.tv_sec = tm.tv_sec; 304 stats_all.s3.curtime.tv_usec = tm.tv_usec; 305 alarm(1); 306 } 307 308 void 309 setup_kd_once(void) 310 { 311 char errbuf[_POSIX2_LINE_MAX]; 312 kfd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); 313 if (kfd == NULL) { 314 syslog(LOG_ERR, "%s", errbuf); 315 exit (1); 316 } 317 } 318 319 void 320 setup(void) 321 { 322 struct ifnet ifnet; 323 long off; 324 static int is_kfd_setup = 0; 325 326 /* setup() is called after each dormant->active 327 * transition. Since we never close the kvm files 328 * (there's no reason), make sure we don't open them 329 * each time, as that can lead to exhaustion of all open 330 * files! */ 331 if (!is_kfd_setup) { 332 setup_kd_once(); 333 is_kfd_setup = 1; 334 } 335 336 if (kvm_nlist(kfd, nl) != 0) { 337 syslog(LOG_ERR, "can't get namelist"); 338 exit (1); 339 } 340 341 if (kvm_read(kfd, (long)nl[X_IFNET].n_value, &ifnetq, 342 sizeof ifnetq) != sizeof ifnetq) { 343 syslog(LOG_ERR, "can't read ifnet queue head from kmem"); 344 exit(1); 345 } 346 347 numintfs = 0; 348 for (off = (long)ifnetq.tqh_first; off;) { 349 if (kvm_read(kfd, off, (char *)&ifnet, sizeof ifnet) != 350 sizeof ifnet) { 351 syslog(LOG_ERR, "can't read ifnet from kmem"); 352 exit(1); 353 } 354 numintfs++; 355 off = (long)ifnet.if_list.tqe_next; 356 } 357 drvinit(0); 358 } 359 360 /* 361 * returns true if have a disk 362 */ 363 int 364 havedisk(void) 365 { 366 return ndrive != 0; 367 } 368 369 void 370 rstat_service(struct svc_req *rqstp, SVCXPRT *transp) 371 { 372 union { 373 int fill; 374 } argument; 375 char *result; 376 xdrproc_t xdr_argument, xdr_result; 377 char *(*local)(void *, struct svc_req *); 378 379 switch (rqstp->rq_proc) { 380 case NULLPROC: 381 (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); 382 goto leave; 383 384 case RSTATPROC_STATS: 385 xdr_argument = (xdrproc_t)xdr_void; 386 xdr_result = (xdrproc_t)xdr_statstime; 387 switch (rqstp->rq_vers) { 388 case RSTATVERS_ORIG: 389 local = (char *(*)(void *, struct svc_req *)) 390 rstatproc_stats_1_svc; 391 break; 392 case RSTATVERS_SWTCH: 393 local = (char *(*)(void *, struct svc_req *)) 394 rstatproc_stats_2_svc; 395 break; 396 case RSTATVERS_TIME: 397 local = (char *(*)(void *, struct svc_req *)) 398 rstatproc_stats_3_svc; 399 break; 400 default: 401 svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); 402 goto leave; 403 } 404 break; 405 406 case RSTATPROC_HAVEDISK: 407 xdr_argument = (xdrproc_t)xdr_void; 408 xdr_result = (xdrproc_t)xdr_u_int; 409 switch (rqstp->rq_vers) { 410 case RSTATVERS_ORIG: 411 local = (char *(*)(void *, struct svc_req *)) 412 rstatproc_havedisk_1_svc; 413 break; 414 case RSTATVERS_SWTCH: 415 local = (char *(*)(void *, struct svc_req *)) 416 rstatproc_havedisk_2_svc; 417 break; 418 case RSTATVERS_TIME: 419 local = (char *(*)(void *, struct svc_req *)) 420 rstatproc_havedisk_3_svc; 421 break; 422 default: 423 svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); 424 goto leave; 425 } 426 break; 427 428 default: 429 svcerr_noproc(transp); 430 goto leave; 431 } 432 memset((char *)&argument, 0, sizeof(argument)); 433 if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) { 434 svcerr_decode(transp); 435 goto leave; 436 } 437 result = (*local)(&argument, rqstp); 438 if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { 439 svcerr_systemerr(transp); 440 } 441 if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) { 442 (void)fprintf(stderr, "unable to free arguments\n"); 443 exit(1); 444 } 445 leave: 446 if (from_inetd) 447 exit(0); 448 } 449