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