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