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