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 30 /* 31 * rstat service: built with rstat.x and derived from rpc.rstatd.c 32 * 33 * Copyright (c) 1984 by Sun Microsystems, Inc. 34 */ 35 36 #include <sys/types.h> 37 #include <sys/socket.h> 38 #include <sys/sysctl.h> 39 #include <sys/time.h> 40 #include <sys/resource.h> 41 #include <sys/param.h> 42 43 #include <err.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <limits.h> 47 #include <signal.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <syslog.h> 52 #include <unistd.h> 53 #include <devstat.h> 54 55 #include <net/if.h> 56 #include <net/if_mib.h> 57 58 #undef FSHIFT /* Use protocol's shift and scale values */ 59 #undef FSCALE 60 #undef if_ipackets 61 #undef if_ierrors 62 #undef if_opackets 63 #undef if_oerrors 64 #undef if_collisions 65 #include <rpcsvc/rstat.h> 66 67 int haveadisk(void); 68 void updatexfers(int, int *); 69 int stats_service(void); 70 71 extern int from_inetd; 72 int sincelastreq = 0; /* number of alarms since last request */ 73 extern int closedown; 74 75 union { 76 struct stats s1; 77 struct statsswtch s2; 78 struct statstime s3; 79 } stats_all; 80 81 void updatestat(); 82 static int stat_is_init = 0; 83 84 static int cp_time_xlat[RSTAT_CPUSTATES] = { CP_USER, CP_NICE, CP_SYS, 85 CP_IDLE }; 86 static long bsd_cp_time[CPUSTATES]; 87 88 89 #ifndef FSCALE 90 #define FSCALE (1 << 8) 91 #endif 92 93 void 94 stat_init(void) 95 { 96 stat_is_init = 1; 97 alarm(0); 98 updatestat(); 99 (void) signal(SIGALRM, updatestat); 100 alarm(1); 101 } 102 103 statstime * 104 rstatproc_stats_3_svc(void *argp, struct svc_req *rqstp) 105 { 106 if (! stat_is_init) 107 stat_init(); 108 sincelastreq = 0; 109 return(&stats_all.s3); 110 } 111 112 statsswtch * 113 rstatproc_stats_2_svc(void *argp, struct svc_req *rqstp) 114 { 115 if (! stat_is_init) 116 stat_init(); 117 sincelastreq = 0; 118 return(&stats_all.s2); 119 } 120 121 stats * 122 rstatproc_stats_1_svc(void *argp, struct svc_req *rqstp) 123 { 124 if (! stat_is_init) 125 stat_init(); 126 sincelastreq = 0; 127 return(&stats_all.s1); 128 } 129 130 u_int * 131 rstatproc_havedisk_3_svc(void *argp, struct svc_req *rqstp) 132 { 133 static u_int have; 134 135 if (! stat_is_init) 136 stat_init(); 137 sincelastreq = 0; 138 have = haveadisk(); 139 return(&have); 140 } 141 142 u_int * 143 rstatproc_havedisk_2_svc(void *argp, struct svc_req *rqstp) 144 { 145 return(rstatproc_havedisk_3_svc(argp, rqstp)); 146 } 147 148 u_int * 149 rstatproc_havedisk_1_svc(void *argp, struct svc_req *rqstp) 150 { 151 return(rstatproc_havedisk_3_svc(argp, rqstp)); 152 } 153 154 void 155 updatestat(void) 156 { 157 int i, hz; 158 struct clockinfo clockrate; 159 struct ifmibdata ifmd; 160 double avrun[3]; 161 struct timeval tm, btm; 162 int mib[6]; 163 size_t len; 164 uint64_t val; 165 int ifcount; 166 167 #ifdef DEBUG 168 fprintf(stderr, "entering updatestat\n"); 169 #endif 170 if (sincelastreq >= closedown) { 171 #ifdef DEBUG 172 fprintf(stderr, "about to closedown\n"); 173 #endif 174 if (from_inetd) 175 exit(0); 176 else { 177 stat_is_init = 0; 178 return; 179 } 180 } 181 sincelastreq++; 182 183 mib[0] = CTL_KERN; 184 mib[1] = KERN_CLOCKRATE; 185 len = sizeof clockrate; 186 if (sysctl(mib, 2, &clockrate, &len, 0, 0) < 0) { 187 syslog(LOG_ERR, "sysctl(kern.clockrate): %m"); 188 exit(1); 189 } 190 hz = clockrate.hz; 191 192 len = sizeof(bsd_cp_time); 193 if (sysctlbyname("kern.cp_time", bsd_cp_time, &len, 0, 0) < 0) { 194 syslog(LOG_ERR, "sysctl(kern.cp_time): %m"); 195 exit(1); 196 } 197 for(i = 0; i < RSTAT_CPUSTATES ; i++) 198 stats_all.s1.cp_time[i] = bsd_cp_time[cp_time_xlat[i]]; 199 200 (void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0])); 201 202 stats_all.s2.avenrun[0] = avrun[0] * FSCALE; 203 stats_all.s2.avenrun[1] = avrun[1] * FSCALE; 204 stats_all.s2.avenrun[2] = avrun[2] * FSCALE; 205 206 mib[0] = CTL_KERN; 207 mib[1] = KERN_BOOTTIME; 208 len = sizeof btm; 209 if (sysctl(mib, 2, &btm, &len, 0, 0) < 0) { 210 syslog(LOG_ERR, "sysctl(kern.boottime): %m"); 211 exit(1); 212 } 213 214 stats_all.s2.boottime.tv_sec = btm.tv_sec; 215 stats_all.s2.boottime.tv_usec = btm.tv_usec; 216 217 218 #ifdef DEBUG 219 fprintf(stderr, "%d %d %d %d\n", stats_all.s1.cp_time[0], 220 stats_all.s1.cp_time[1], stats_all.s1.cp_time[2], stats_all.s1.cp_time[3]); 221 #endif 222 223 #define FETCH_CNT(stat, cnt) do { \ 224 len = sizeof(uint64_t); \ 225 if (sysctlbyname("vm.stats." #cnt , &val, &len, NULL, 0) < 0) { \ 226 syslog(LOG_ERR, "sysctl(vm.stats." #cnt "): %m"); \ 227 exit(1); \ 228 } \ 229 stat = val; \ 230 } while (0) 231 232 FETCH_CNT(stats_all.s1.v_pgpgin, vm.v_vnodepgsin); 233 FETCH_CNT(stats_all.s1.v_pgpgout, vm.v_vnodepgsout); 234 FETCH_CNT(stats_all.s1.v_pswpin, vm.v_swappgsin); 235 FETCH_CNT(stats_all.s1.v_pswpout, vm.v_swappgsout); 236 FETCH_CNT(stats_all.s1.v_intr, sys.v_intr); 237 FETCH_CNT(stats_all.s2.v_swtch, sys.v_swtch); 238 (void)gettimeofday(&tm, NULL); 239 stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) + 240 hz*(tm.tv_usec - btm.tv_usec)/1000000; 241 242 /* update disk transfers */ 243 updatexfers(RSTAT_DK_NDRIVE, stats_all.s1.dk_xfer); 244 245 mib[0] = CTL_NET; 246 mib[1] = PF_LINK; 247 mib[2] = NETLINK_GENERIC; 248 mib[3] = IFMIB_SYSTEM; 249 mib[4] = IFMIB_IFCOUNT; 250 len = sizeof ifcount; 251 if (sysctl(mib, 5, &ifcount, &len, 0, 0) < 0) { 252 syslog(LOG_ERR, "sysctl(net.link.generic.system.ifcount): %m"); 253 exit(1); 254 } 255 256 stats_all.s1.if_ipackets = 0; 257 stats_all.s1.if_opackets = 0; 258 stats_all.s1.if_ierrors = 0; 259 stats_all.s1.if_oerrors = 0; 260 stats_all.s1.if_collisions = 0; 261 for (i = 1; i <= ifcount; i++) { 262 len = sizeof ifmd; 263 mib[3] = IFMIB_IFDATA; 264 mib[4] = i; 265 mib[5] = IFDATA_GENERAL; 266 if (sysctl(mib, 6, &ifmd, &len, 0, 0) < 0) { 267 if (errno == ENOENT) 268 continue; 269 270 syslog(LOG_ERR, "sysctl(net.link.ifdata.%d.general)" 271 ": %m", i); 272 exit(1); 273 } 274 275 stats_all.s1.if_ipackets += ifmd.ifmd_data.ifi_ipackets; 276 stats_all.s1.if_opackets += ifmd.ifmd_data.ifi_opackets; 277 stats_all.s1.if_ierrors += ifmd.ifmd_data.ifi_ierrors; 278 stats_all.s1.if_oerrors += ifmd.ifmd_data.ifi_oerrors; 279 stats_all.s1.if_collisions += ifmd.ifmd_data.ifi_collisions; 280 } 281 (void)gettimeofday(&tm, NULL); 282 stats_all.s3.curtime.tv_sec = tm.tv_sec; 283 stats_all.s3.curtime.tv_usec = tm.tv_usec; 284 alarm(1); 285 } 286 287 /* 288 * returns true if have a disk 289 */ 290 int 291 haveadisk(void) 292 { 293 register int i; 294 struct statinfo stats; 295 int num_devices, retval = 0; 296 297 if ((num_devices = devstat_getnumdevs(NULL)) < 0) { 298 syslog(LOG_ERR, "rstatd: can't get number of devices: %s", 299 devstat_errbuf); 300 exit(1); 301 } 302 303 if (devstat_checkversion(NULL) < 0) { 304 syslog(LOG_ERR, "rstatd: %s", devstat_errbuf); 305 exit(1); 306 } 307 308 stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); 309 bzero(stats.dinfo, sizeof(struct devinfo)); 310 311 if (devstat_getdevs(NULL, &stats) == -1) { 312 syslog(LOG_ERR, "rstatd: can't get device list: %s", 313 devstat_errbuf); 314 exit(1); 315 } 316 for (i = 0; i < stats.dinfo->numdevs; i++) { 317 if (((stats.dinfo->devices[i].device_type 318 & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) 319 && ((stats.dinfo->devices[i].device_type 320 & DEVSTAT_TYPE_PASS) == 0)) { 321 retval = 1; 322 break; 323 } 324 } 325 326 if (stats.dinfo->mem_ptr) 327 free(stats.dinfo->mem_ptr); 328 329 free(stats.dinfo); 330 return(retval); 331 } 332 333 void 334 updatexfers(int numdevs, int *devs) 335 { 336 register int i, j, k, t; 337 struct statinfo stats; 338 int num_devices = 0; 339 u_int64_t total_transfers; 340 341 if ((num_devices = devstat_getnumdevs(NULL)) < 0) { 342 syslog(LOG_ERR, "rstatd: can't get number of devices: %s", 343 devstat_errbuf); 344 exit(1); 345 } 346 347 if (devstat_checkversion(NULL) < 0) { 348 syslog(LOG_ERR, "rstatd: %s", devstat_errbuf); 349 exit(1); 350 } 351 352 stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); 353 bzero(stats.dinfo, sizeof(struct devinfo)); 354 355 if (devstat_getdevs(NULL, &stats) == -1) { 356 syslog(LOG_ERR, "rstatd: can't get device list: %s", 357 devstat_errbuf); 358 exit(1); 359 } 360 361 for (i = 0, j = 0; i < stats.dinfo->numdevs && j < numdevs; i++) { 362 if (((stats.dinfo->devices[i].device_type 363 & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) 364 && ((stats.dinfo->devices[i].device_type 365 & DEVSTAT_TYPE_PASS) == 0)) { 366 total_transfers = 0; 367 for (k = 0; k < DEVSTAT_N_TRANS_FLAGS; k++) 368 total_transfers += 369 stats.dinfo->devices[i].operations[k]; 370 /* 371 * XXX KDM If the total transfers for this device 372 * are greater than the amount we can fit in a 373 * signed integer, just set them to the maximum 374 * amount we can fit in a signed integer. I have a 375 * feeling that the rstat protocol assumes 32-bit 376 * integers, so this could well break on a 64-bit 377 * architecture like the Alpha. 378 */ 379 if (total_transfers > INT_MAX) 380 t = INT_MAX; 381 else 382 t = total_transfers; 383 devs[j] = t; 384 j++; 385 } 386 } 387 388 if (stats.dinfo->mem_ptr) 389 free(stats.dinfo->mem_ptr); 390 391 free(stats.dinfo); 392 } 393 394 void 395 rstat_service(struct svc_req *rqstp, SVCXPRT *transp) 396 { 397 union { 398 int fill; 399 } argument; 400 void *result; 401 xdrproc_t xdr_argument, xdr_result; 402 typedef void *(svc_cb)(void *arg, struct svc_req *rqstp); 403 svc_cb *local; 404 405 switch (rqstp->rq_proc) { 406 case NULLPROC: 407 (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); 408 goto leave; 409 410 case RSTATPROC_STATS: 411 xdr_argument = (xdrproc_t)xdr_void; 412 xdr_result = (xdrproc_t)xdr_statstime; 413 switch (rqstp->rq_vers) { 414 case RSTATVERS_ORIG: 415 local = (svc_cb *)rstatproc_stats_1_svc; 416 break; 417 case RSTATVERS_SWTCH: 418 local = (svc_cb *)rstatproc_stats_2_svc; 419 break; 420 case RSTATVERS_TIME: 421 local = (svc_cb *)rstatproc_stats_3_svc; 422 break; 423 default: 424 svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); 425 goto leave; 426 /*NOTREACHED*/ 427 } 428 break; 429 430 case RSTATPROC_HAVEDISK: 431 xdr_argument = (xdrproc_t)xdr_void; 432 xdr_result = (xdrproc_t)xdr_u_int; 433 switch (rqstp->rq_vers) { 434 case RSTATVERS_ORIG: 435 local = (svc_cb *)rstatproc_havedisk_1_svc; 436 break; 437 case RSTATVERS_SWTCH: 438 local = (svc_cb *)rstatproc_havedisk_2_svc; 439 break; 440 case RSTATVERS_TIME: 441 local = (svc_cb *)rstatproc_havedisk_3_svc; 442 break; 443 default: 444 svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); 445 goto leave; 446 /*NOTREACHED*/ 447 } 448 break; 449 450 default: 451 svcerr_noproc(transp); 452 goto leave; 453 } 454 bzero((char *)&argument, sizeof(argument)); 455 if (!svc_getargs(transp, xdr_argument, &argument)) { 456 svcerr_decode(transp); 457 goto leave; 458 } 459 result = (*local)(&argument, rqstp); 460 if (result != NULL && 461 !svc_sendreply(transp, xdr_result, result)) { 462 svcerr_systemerr(transp); 463 } 464 if (!svc_freeargs(transp, xdr_argument, &argument)) 465 errx(1, "unable to free arguments"); 466 leave: 467 if (from_inetd) 468 exit(0); 469 } 470