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