1/* $OpenBSD: rstat_proc.c,v 1.38 2023/03/08 04:43:05 guenther Exp $ */ 2 3/* 4 * Copyright (c) 2010, Oracle America, Inc. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are 8 * met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * * Redistributions in binary form must reproduce the above 13 * copyright notice, this list of conditions and the following 14 * disclaimer in the documentation and/or other materials 15 * provided with the distribution. 16 * * Neither the name of the "Oracle America, Inc." nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34/* 35 * rstat service: built with rstat.x and derived from rpc.rstatd.c 36 */ 37 38#include <sys/types.h> 39#include <sys/time.h> 40#include <sys/sched.h> 41#include <sys/socket.h> 42#include <sys/sysctl.h> 43#include <net/if.h> 44 45#include <stdio.h> 46#include <stdlib.h> 47#include <string.h> 48#include <signal.h> 49#include <syslog.h> 50#include <fcntl.h> 51#include <limits.h> 52#include <errno.h> 53#include <ifaddrs.h> 54#include "dkstats.h" 55 56#undef FSHIFT /* Use protocol's shift and scale values */ 57#undef FSCALE 58#undef DK_NDRIVE 59#undef CPUSTATES 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 67int cp_xlat[CPUSTATES] = { CP_USER, CP_NICE, CP_SYS, CP_IDLE }; 68 69extern int dk_ndrive; /* from dkstats.c */ 70extern struct _disk cur, last; 71char *memf = NULL, *nlistf = NULL; 72int hz; 73 74extern int from_inetd; 75int sincelastreq = 0; /* number of alarms since last request */ 76extern int closedown; 77 78union { 79 struct stats s1; 80 struct statsswtch s2; 81 struct statstime s3; 82} stats_all; 83 84void updatestat(void); 85void updatestatsig(int sig); 86void setup(void); 87 88volatile sig_atomic_t wantupdatestat; 89 90static int stat_is_init = 0; 91 92#ifndef FSCALE 93#define FSCALE (1 << 8) 94#endif 95 96static void 97stat_init(void) 98{ 99 stat_is_init = 1; 100 setup(); 101 updatestat(); 102 (void) signal(SIGALRM, updatestatsig); 103 alarm(1); 104} 105 106statstime * 107rstatproc_stats_3_svc(void *arg, struct svc_req *rqstp) 108{ 109 if (!stat_is_init) 110 stat_init(); 111 sincelastreq = 0; 112 return (&stats_all.s3); 113} 114 115statsswtch * 116rstatproc_stats_2_svc(void *arg, struct svc_req *rqstp) 117{ 118 if (!stat_is_init) 119 stat_init(); 120 sincelastreq = 0; 121 return (&stats_all.s2); 122} 123 124stats * 125rstatproc_stats_1_svc(void *arg, struct svc_req *rqstp) 126{ 127 if (!stat_is_init) 128 stat_init(); 129 sincelastreq = 0; 130 return (&stats_all.s1); 131} 132 133u_int * 134rstatproc_havedisk_3_svc(void *arg, struct svc_req *rqstp) 135{ 136 static u_int have; 137 138 if (!stat_is_init) 139 stat_init(); 140 sincelastreq = 0; 141 have = dk_ndrive != 0; 142 return (&have); 143} 144 145u_int * 146rstatproc_havedisk_2_svc(void *arg, struct svc_req *rqstp) 147{ 148 return (rstatproc_havedisk_3_svc(arg, rqstp)); 149} 150 151u_int * 152rstatproc_havedisk_1_svc(void *arg, struct svc_req *rqstp) 153{ 154 return (rstatproc_havedisk_3_svc(arg, rqstp)); 155} 156 157void 158updatestatsig(int sig) 159{ 160 wantupdatestat = 1; 161} 162 163void 164updatestat(void) 165{ 166 int i, mib[2], save_errno = errno; 167 struct uvmexp uvmexp; 168 size_t len; 169 struct if_data *ifdp; 170 struct ifaddrs *ifaddrs, *ifa; 171 double avrun[3]; 172 struct timeval tm, btm; 173 long *cp_time = cur.cp_time; 174 175#ifdef DEBUG 176 syslog(LOG_DEBUG, "entering updatestat"); 177#endif 178 if (sincelastreq >= closedown) { 179#ifdef DEBUG 180 syslog(LOG_DEBUG, "about to closedown"); 181#endif 182 if (from_inetd) 183 _exit(0); 184 else { 185 stat_is_init = 0; 186 errno = save_errno; 187 return; 188 } 189 } 190 sincelastreq++; 191 192 /* 193 * dkreadstats reads in the "disk_count" as well as the "disklist" 194 * statistics. It also retrieves "hz" and the "cp_time" array. 195 */ 196 dkreadstats(); 197 memset(stats_all.s1.dk_xfer, '\0', sizeof(stats_all.s1.dk_xfer)); 198 for (i = 0; i < dk_ndrive && i < DK_NDRIVE; i++) 199 stats_all.s1.dk_xfer[i] = cur.dk_rxfer[i] + cur.dk_wxfer[i]; 200 201 for (i = 0; i < CPUSTATES; i++) 202 stats_all.s1.cp_time[i] = cp_time[cp_xlat[i]]; 203 (void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0])); 204 stats_all.s2.avenrun[0] = avrun[0] * FSCALE; 205 stats_all.s2.avenrun[1] = avrun[1] * FSCALE; 206 stats_all.s2.avenrun[2] = avrun[2] * FSCALE; 207 mib[0] = CTL_KERN; 208 mib[1] = KERN_BOOTTIME; 209 len = sizeof(btm); 210 if (sysctl(mib, 2, &btm, &len, NULL, 0) == -1) { 211 syslog(LOG_ERR, "can't sysctl kern.boottime: %m"); 212 _exit(1); 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 syslog(LOG_DEBUG, "%d %d %d %d", stats_all.s1.cp_time[0], 220 stats_all.s1.cp_time[1], stats_all.s1.cp_time[2], 221 stats_all.s1.cp_time[3]); 222#endif 223 224 mib[0] = CTL_VM; 225 mib[1] = VM_UVMEXP; 226 len = sizeof(uvmexp); 227 if (sysctl(mib, 2, &uvmexp, &len, NULL, 0) == -1) { 228 syslog(LOG_ERR, "can't sysctl vm.uvmexp: %m"); 229 _exit(1); 230 } 231 stats_all.s1.v_pgpgin = uvmexp.fltanget; 232 stats_all.s1.v_pgpgout = uvmexp.pdpageouts; 233 stats_all.s1.v_pswpin = 0; 234 stats_all.s1.v_pswpout = 0; 235 stats_all.s1.v_intr = uvmexp.intrs; 236 stats_all.s2.v_swtch = uvmexp.swtch; 237 gettimeofday(&tm, NULL); 238 stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) + 239 hz*(tm.tv_usec - btm.tv_usec)/1000000; 240 stats_all.s1.if_ipackets = 0; 241 stats_all.s1.if_opackets = 0; 242 stats_all.s1.if_ierrors = 0; 243 stats_all.s1.if_oerrors = 0; 244 stats_all.s1.if_collisions = 0; 245 if (getifaddrs(&ifaddrs) == -1) { 246 syslog(LOG_ERR, "can't getifaddrs: %m"); 247 _exit(1); 248 } 249 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { 250 if (ifa->ifa_addr->sa_family != AF_LINK) 251 continue; 252 ifdp = (struct if_data *)ifa->ifa_data; 253 stats_all.s1.if_ipackets += ifdp->ifi_ipackets; 254 stats_all.s1.if_opackets += ifdp->ifi_opackets; 255 stats_all.s1.if_ierrors += ifdp->ifi_ierrors; 256 stats_all.s1.if_oerrors += ifdp->ifi_oerrors; 257 stats_all.s1.if_collisions += ifdp->ifi_collisions; 258 } 259 freeifaddrs(ifaddrs); 260 stats_all.s3.curtime.tv_sec = tm.tv_sec; 261 stats_all.s3.curtime.tv_usec = tm.tv_usec; 262 263 alarm(1); 264 errno = save_errno; 265} 266 267void 268setup(void) 269{ 270 dkinit(0); 271} 272 273void rstat_service(struct svc_req *, SVCXPRT *); 274 275void 276rstat_service(struct svc_req *rqstp, SVCXPRT *transp) 277{ 278 char *(*local)(void *, struct svc_req *); 279 xdrproc_t xdr_argument, xdr_result; 280 union { 281 int fill; 282 } argument; 283 char *result; 284 285 switch (rqstp->rq_proc) { 286 case NULLPROC: 287 (void)svc_sendreply(transp, xdr_void, (char *)NULL); 288 return; 289 290 case RSTATPROC_STATS: 291 xdr_argument = (xdrproc_t)xdr_void; 292 xdr_result = (xdrproc_t)xdr_statstime; 293 switch (rqstp->rq_vers) { 294 case RSTATVERS_ORIG: 295 local = (char *(*)(void *, struct svc_req *)) 296 rstatproc_stats_1_svc; 297 break; 298 case RSTATVERS_SWTCH: 299 local = (char *(*)(void *, struct svc_req *)) 300 rstatproc_stats_2_svc; 301 break; 302 case RSTATVERS_TIME: 303 local = (char *(*)(void *, struct svc_req *)) 304 rstatproc_stats_3_svc; 305 break; 306 default: 307 svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); 308 return; 309 } 310 break; 311 312 case RSTATPROC_HAVEDISK: 313 xdr_argument = (xdrproc_t)xdr_void; 314 xdr_result = (xdrproc_t)xdr_u_int; 315 switch (rqstp->rq_vers) { 316 case RSTATVERS_ORIG: 317 local = (char *(*)(void *, struct svc_req *)) 318 rstatproc_havedisk_1_svc; 319 break; 320 case RSTATVERS_SWTCH: 321 local = (char *(*)(void *, struct svc_req *)) 322 rstatproc_havedisk_2_svc; 323 break; 324 case RSTATVERS_TIME: 325 local = (char *(*)(void *, struct svc_req *)) 326 rstatproc_havedisk_3_svc; 327 break; 328 default: 329 svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); 330 return; 331 } 332 break; 333 334 default: 335 svcerr_noproc(transp); 336 return; 337 } 338 memset(&argument, 0, sizeof(argument)); 339 if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) { 340 svcerr_decode(transp); 341 return; 342 } 343 result = (*local)(&argument, rqstp); 344 if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { 345 svcerr_systemerr(transp); 346 } 347 if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) { 348 syslog(LOG_ERR, "unable to free arguments"); 349 exit(1); 350 } 351} 352