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