1 /* $OpenBSD: rstat_proc.c,v 1.37 2019/08/09 21:31:07 cheloha 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 67 int cp_xlat[CPUSTATES] = { CP_USER, CP_NICE, CP_SYS, CP_IDLE }; 68 69 extern int dk_ndrive; /* from dkstats.c */ 70 extern struct _disk cur, last; 71 char *memf = NULL, *nlistf = NULL; 72 int hz; 73 74 extern int from_inetd; 75 int sincelastreq = 0; /* number of alarms since last request */ 76 extern int closedown; 77 78 union { 79 struct stats s1; 80 struct statsswtch s2; 81 struct statstime s3; 82 } stats_all; 83 84 void updatestat(void); 85 void updatestatsig(int sig); 86 void setup(void); 87 88 volatile sig_atomic_t wantupdatestat; 89 90 static int stat_is_init = 0; 91 92 #ifndef FSCALE 93 #define FSCALE (1 << 8) 94 #endif 95 96 static void 97 stat_init(void) 98 { 99 stat_is_init = 1; 100 setup(); 101 updatestat(); 102 (void) signal(SIGALRM, updatestatsig); 103 alarm(1); 104 } 105 106 statstime * 107 rstatproc_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 115 statsswtch * 116 rstatproc_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 124 stats * 125 rstatproc_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 133 u_int * 134 rstatproc_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 145 u_int * 146 rstatproc_havedisk_2_svc(void *arg, struct svc_req *rqstp) 147 { 148 return (rstatproc_havedisk_3_svc(arg, rqstp)); 149 } 150 151 u_int * 152 rstatproc_havedisk_1_svc(void *arg, struct svc_req *rqstp) 153 { 154 return (rstatproc_havedisk_3_svc(arg, rqstp)); 155 } 156 157 /* ARGSUSED */ 158 void 159 updatestatsig(int sig) 160 { 161 wantupdatestat = 1; 162 } 163 164 void 165 updatestat(void) 166 { 167 int i, mib[2], save_errno = errno; 168 struct uvmexp uvmexp; 169 size_t len; 170 struct if_data *ifdp; 171 struct ifaddrs *ifaddrs, *ifa; 172 double avrun[3]; 173 struct timeval tm, btm; 174 long *cp_time = cur.cp_time; 175 176 #ifdef DEBUG 177 syslog(LOG_DEBUG, "entering updatestat"); 178 #endif 179 if (sincelastreq >= closedown) { 180 #ifdef DEBUG 181 syslog(LOG_DEBUG, "about to closedown"); 182 #endif 183 if (from_inetd) 184 _exit(0); 185 else { 186 stat_is_init = 0; 187 errno = save_errno; 188 return; 189 } 190 } 191 sincelastreq++; 192 193 /* 194 * dkreadstats reads in the "disk_count" as well as the "disklist" 195 * statistics. It also retrieves "hz" and the "cp_time" array. 196 */ 197 dkreadstats(); 198 memset(stats_all.s1.dk_xfer, '\0', sizeof(stats_all.s1.dk_xfer)); 199 for (i = 0; i < dk_ndrive && i < DK_NDRIVE; i++) 200 stats_all.s1.dk_xfer[i] = cur.dk_rxfer[i] + cur.dk_wxfer[i]; 201 202 for (i = 0; i < CPUSTATES; i++) 203 stats_all.s1.cp_time[i] = cp_time[cp_xlat[i]]; 204 (void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0])); 205 stats_all.s2.avenrun[0] = avrun[0] * FSCALE; 206 stats_all.s2.avenrun[1] = avrun[1] * FSCALE; 207 stats_all.s2.avenrun[2] = avrun[2] * FSCALE; 208 mib[0] = CTL_KERN; 209 mib[1] = KERN_BOOTTIME; 210 len = sizeof(btm); 211 if (sysctl(mib, 2, &btm, &len, NULL, 0) == -1) { 212 syslog(LOG_ERR, "can't sysctl kern.boottime: %m"); 213 _exit(1); 214 } 215 stats_all.s2.boottime.tv_sec = btm.tv_sec; 216 stats_all.s2.boottime.tv_usec = btm.tv_usec; 217 218 219 #ifdef DEBUG 220 syslog(LOG_DEBUG, "%d %d %d %d", stats_all.s1.cp_time[0], 221 stats_all.s1.cp_time[1], stats_all.s1.cp_time[2], 222 stats_all.s1.cp_time[3]); 223 #endif 224 225 mib[0] = CTL_VM; 226 mib[1] = VM_UVMEXP; 227 len = sizeof(uvmexp); 228 if (sysctl(mib, 2, &uvmexp, &len, NULL, 0) == -1) { 229 syslog(LOG_ERR, "can't sysctl vm.uvmexp: %m"); 230 _exit(1); 231 } 232 stats_all.s1.v_pgpgin = uvmexp.fltanget; 233 stats_all.s1.v_pgpgout = uvmexp.pdpageouts; 234 stats_all.s1.v_pswpin = 0; 235 stats_all.s1.v_pswpout = 0; 236 stats_all.s1.v_intr = uvmexp.intrs; 237 stats_all.s2.v_swtch = uvmexp.swtch; 238 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 stats_all.s1.if_ipackets = 0; 242 stats_all.s1.if_opackets = 0; 243 stats_all.s1.if_ierrors = 0; 244 stats_all.s1.if_oerrors = 0; 245 stats_all.s1.if_collisions = 0; 246 if (getifaddrs(&ifaddrs) == -1) { 247 syslog(LOG_ERR, "can't getifaddrs: %m"); 248 _exit(1); 249 } 250 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { 251 if (ifa->ifa_addr->sa_family != AF_LINK) 252 continue; 253 ifdp = (struct if_data *)ifa->ifa_data; 254 stats_all.s1.if_ipackets += ifdp->ifi_ipackets; 255 stats_all.s1.if_opackets += ifdp->ifi_opackets; 256 stats_all.s1.if_ierrors += ifdp->ifi_ierrors; 257 stats_all.s1.if_oerrors += ifdp->ifi_oerrors; 258 stats_all.s1.if_collisions += ifdp->ifi_collisions; 259 } 260 freeifaddrs(ifaddrs); 261 stats_all.s3.curtime.tv_sec = tm.tv_sec; 262 stats_all.s3.curtime.tv_usec = tm.tv_usec; 263 264 alarm(1); 265 errno = save_errno; 266 } 267 268 void 269 setup(void) 270 { 271 dkinit(0); 272 } 273 274 void rstat_service(struct svc_req *, SVCXPRT *); 275 276 void 277 rstat_service(struct svc_req *rqstp, SVCXPRT *transp) 278 { 279 char *(*local)(void *, struct svc_req *); 280 xdrproc_t xdr_argument, xdr_result; 281 union { 282 int fill; 283 } argument; 284 char *result; 285 286 switch (rqstp->rq_proc) { 287 case NULLPROC: 288 (void)svc_sendreply(transp, xdr_void, (char *)NULL); 289 return; 290 291 case RSTATPROC_STATS: 292 xdr_argument = (xdrproc_t)xdr_void; 293 xdr_result = (xdrproc_t)xdr_statstime; 294 switch (rqstp->rq_vers) { 295 case RSTATVERS_ORIG: 296 local = (char *(*)(void *, struct svc_req *)) 297 rstatproc_stats_1_svc; 298 break; 299 case RSTATVERS_SWTCH: 300 local = (char *(*)(void *, struct svc_req *)) 301 rstatproc_stats_2_svc; 302 break; 303 case RSTATVERS_TIME: 304 local = (char *(*)(void *, struct svc_req *)) 305 rstatproc_stats_3_svc; 306 break; 307 default: 308 svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); 309 return; 310 } 311 break; 312 313 case RSTATPROC_HAVEDISK: 314 xdr_argument = (xdrproc_t)xdr_void; 315 xdr_result = (xdrproc_t)xdr_u_int; 316 switch (rqstp->rq_vers) { 317 case RSTATVERS_ORIG: 318 local = (char *(*)(void *, struct svc_req *)) 319 rstatproc_havedisk_1_svc; 320 break; 321 case RSTATVERS_SWTCH: 322 local = (char *(*)(void *, struct svc_req *)) 323 rstatproc_havedisk_2_svc; 324 break; 325 case RSTATVERS_TIME: 326 local = (char *(*)(void *, struct svc_req *)) 327 rstatproc_havedisk_3_svc; 328 break; 329 default: 330 svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); 331 return; 332 } 333 break; 334 335 default: 336 svcerr_noproc(transp); 337 return; 338 } 339 memset(&argument, 0, sizeof(argument)); 340 if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) { 341 svcerr_decode(transp); 342 return; 343 } 344 result = (*local)(&argument, rqstp); 345 if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { 346 svcerr_systemerr(transp); 347 } 348 if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) { 349 syslog(LOG_ERR, "unable to free arguments"); 350 exit(1); 351 } 352 } 353