1/*
2 * Copyright (c) 2007-2008 Hyperic, Inc.
3 * Copyright (c) 2009 SpringSource, Inc.
4 * Copyright (c) 2010 VMware, Inc.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19/* Utility functions to provide string formatting of SIGAR data */
20
21#include "sigar.h"
22#include "sigar_private.h"
23#include "sigar_util.h"
24#include "sigar_os.h"
25#include "sigar_format.h"
26
27#include <errno.h>
28#include <stdio.h>
29
30#ifndef WIN32
31#include <netinet/in.h>
32#include <arpa/inet.h>
33#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(_AIX)
34#include <sys/socket.h>
35#endif
36#include <pwd.h>
37#include <grp.h>
38
39/* sysconf(_SC_GET{PW,GR}_R_SIZE_MAX) */
40#define R_SIZE_MAX 1024
41
42int sigar_user_name_get(sigar_t *sigar, int uid, char *buf, int buflen)
43{
44    struct passwd *pw = NULL;
45    /* XXX cache lookup */
46
47# ifdef HAVE_GETPWUID_R
48    struct passwd pwbuf;
49    char buffer[R_SIZE_MAX];
50
51    if (getpwuid_r(uid, &pwbuf, buffer, sizeof(buffer), &pw) != 0) {
52        return errno;
53    }
54    if (!pw) {
55        return ENOENT;
56    }
57# else
58    if ((pw = getpwuid(uid)) == NULL) {
59        return errno;
60    }
61# endif
62
63    strncpy(buf, pw->pw_name, buflen);
64    buf[buflen-1] = '\0';
65
66    return SIGAR_OK;
67}
68
69int sigar_group_name_get(sigar_t *sigar, int gid, char *buf, int buflen)
70{
71    struct group *gr;
72    /* XXX cache lookup */
73
74# ifdef HAVE_GETGRGID_R
75    struct group grbuf;
76    char buffer[R_SIZE_MAX];
77
78    if (getgrgid_r(gid, &grbuf, buffer, sizeof(buffer), &gr) != 0) {
79        return errno;
80    }
81# else
82    if ((gr = getgrgid(gid)) == NULL) {
83        return errno;
84    }
85# endif
86
87    if (gr && gr->gr_name) {
88        strncpy(buf, gr->gr_name, buflen);
89    }
90    else {
91        /* seen on linux.. apache httpd.conf has:
92         * Group #-1
93         * results in uid == -1 and gr == NULL.
94         * wtf getgrgid_r doesnt fail instead?
95         */
96        sprintf(buf, "%d", gid);
97    }
98    buf[buflen-1] = '\0';
99
100    return SIGAR_OK;
101}
102
103int sigar_user_id_get(sigar_t *sigar, const char *name, int *uid)
104{
105    /* XXX cache lookup */
106    struct passwd *pw;
107
108# ifdef HAVE_GETPWNAM_R
109    struct passwd pwbuf;
110    char buf[R_SIZE_MAX];
111
112    if (getpwnam_r(name, &pwbuf, buf, sizeof(buf), &pw) != 0) {
113        return errno;
114    }
115# else
116    if (!(pw = getpwnam(name))) {
117        return errno;
118    }
119# endif
120
121    *uid = (int)pw->pw_uid;
122    return SIGAR_OK;
123}
124
125#endif /* WIN32 */
126
127static char *sigar_error_string(int err)
128{
129    switch (err) {
130      case SIGAR_ENOTIMPL:
131        return "This function has not been implemented on this platform";
132      default:
133        return "Error string not specified yet";
134    }
135}
136
137SIGAR_DECLARE(char *) sigar_strerror(sigar_t *sigar, int err)
138{
139    char *buf;
140
141    if (err < 0) {
142        return sigar->errbuf;
143    }
144
145    if (err > SIGAR_OS_START_ERROR) {
146        if ((buf = sigar_os_error_string(sigar, err)) != NULL) {
147            return buf;
148        }
149        return "Unknown OS Error"; /* should never happen */
150    }
151
152    if (err > SIGAR_START_ERROR) {
153        return sigar_error_string(err);
154    }
155
156    return sigar_strerror_get(err, sigar->errbuf, sizeof(sigar->errbuf));
157}
158
159char *sigar_strerror_get(int err, char *errbuf, int buflen)
160{
161    char *buf = NULL;
162#ifdef WIN32
163    DWORD len;
164
165    len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
166                        FORMAT_MESSAGE_IGNORE_INSERTS,
167                        NULL,
168                        err,
169                        MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), /* force english */
170                        (LPTSTR)errbuf,
171                        (DWORD)buflen,
172                        NULL);
173#else
174
175#if defined(HAVE_STRERROR_R) && defined(HAVE_STRERROR_R_GLIBC)
176    /*
177     * strerror_r man page says:
178     * "The GNU version may, but need not, use the user supplied buffer"
179     */
180    buf = strerror_r(err, errbuf, buflen);
181#elif defined(HAVE_STRERROR_R)
182    if (strerror_r(err, errbuf, buflen) < 0) {
183        buf = "Unknown Error";
184    }
185#else
186    /* strerror() is thread safe on solaris and hpux */
187    buf = strerror(err);
188#endif
189
190    if (buf != NULL) {
191        SIGAR_STRNCPY(errbuf, buf, buflen);
192    }
193
194#endif
195    return errbuf;
196}
197
198void sigar_strerror_set(sigar_t *sigar, char *msg)
199{
200    SIGAR_SSTRCPY(sigar->errbuf, msg);
201}
202
203#ifdef WIN32
204#define vsnprintf _vsnprintf
205#endif
206
207void sigar_strerror_printf(sigar_t *sigar, const char *format, ...)
208{
209    va_list args;
210
211    va_start(args, format);
212    vsnprintf(sigar->errbuf, sizeof(sigar->errbuf), format, args);
213    va_end(args);
214}
215
216/* copy apr_strfsize */
217SIGAR_DECLARE(char *) sigar_format_size(sigar_uint64_t size, char *buf)
218{
219    const char ord[] = "KMGTPE";
220    const char *o = ord;
221    int remain;
222
223    if (size == SIGAR_FIELD_NOTIMPL) {
224        buf[0] = '-';
225        buf[1] = '\0';
226        return buf;
227    }
228
229    if (size < 973) {
230        sprintf(buf, "%3d ", (int) size);
231        return buf;
232    }
233
234    do {
235        remain = (int)(size & 1023);
236        size >>= 10;
237
238        if (size >= 973) {
239            ++o;
240            continue;
241        }
242
243        if (size < 9 || (size == 9 && remain < 973)) {
244            if ((remain = ((remain * 5) + 256) / 512) >= 10) {
245                ++size;
246                remain = 0;
247            }
248            sprintf(buf, "%d.%d%c", (int) size, remain, *o);
249            return buf;
250        }
251
252        if (remain >= 512) {
253            ++size;
254        }
255
256        sprintf(buf, "%3d%c", (int) size, *o);
257
258        return buf;
259    } while (1);
260}
261
262
263SIGAR_DECLARE(int) sigar_uptime_string(sigar_t *sigar,
264                                       sigar_uptime_t *uptime,
265                                       char *buffer,
266                                       int buflen)
267{
268    char *ptr = buffer;
269    int time = (int)uptime->uptime;
270    int minutes, hours, days, offset = 0;
271
272    /* XXX: get rid of sprintf and/or check for overflow */
273    days = time / (60*60*24);
274
275    if (days) {
276        offset += sprintf(ptr + offset, "%d day%s, ",
277                          days, (days > 1) ? "s" : "");
278    }
279
280    minutes = time / 60;
281    hours = minutes / 60;
282    hours = hours % 24;
283    minutes = minutes % 60;
284
285    if (hours) {
286        offset += sprintf(ptr + offset, "%2d:%02d",
287                          hours, minutes);
288    }
289    else {
290        offset += sprintf(ptr + offset, "%d min", minutes);
291    }
292
293    return SIGAR_OK;
294}
295
296/* threadsafe alternative to inet_ntoa (inet_ntop4 from apr) */
297int sigar_inet_ntoa(sigar_t *sigar,
298                    sigar_uint32_t address,
299                    char *addr_str)
300{
301    char *next=addr_str;
302    int n=0;
303    const unsigned char *src =
304        (const unsigned char *)&address;
305
306    do {
307        unsigned char u = *src++;
308        if (u > 99) {
309            *next++ = '0' + u/100;
310            u %= 100;
311            *next++ = '0' + u/10;
312            u %= 10;
313        }
314        else if (u > 9) {
315            *next++ = '0' + u/10;
316            u %= 10;
317        }
318        *next++ = '0' + u;
319        *next++ = '.';
320        n++;
321    } while (n < 4);
322
323    *--next = 0;
324
325    return SIGAR_OK;
326}
327
328static int sigar_ether_ntoa(char *buff, unsigned char *ptr)
329{
330    sprintf(buff, "%02X:%02X:%02X:%02X:%02X:%02X",
331            (ptr[0] & 0xff), (ptr[1] & 0xff), (ptr[2] & 0xff),
332            (ptr[3] & 0xff), (ptr[4] & 0xff), (ptr[5] & 0xff));
333    return SIGAR_OK;
334}
335
336SIGAR_DECLARE(int) sigar_net_address_equals(sigar_net_address_t *addr1,
337                                            sigar_net_address_t *addr2)
338
339{
340    if (addr1->family != addr2->family) {
341        return EINVAL;
342    }
343
344    switch (addr1->family) {
345      case SIGAR_AF_INET:
346        return memcmp(&addr1->addr.in, &addr2->addr.in, sizeof(addr1->addr.in));
347      case SIGAR_AF_INET6:
348        return memcmp(&addr1->addr.in6, &addr2->addr.in6, sizeof(addr1->addr.in6));
349      case SIGAR_AF_LINK:
350        return memcmp(&addr1->addr.mac, &addr2->addr.mac, sizeof(addr1->addr.mac));
351      default:
352        return EINVAL;
353    }
354}
355
356#if defined(SIGAR_USING_MSC6)
357#define sigar_inet_ntop(af, src, dst, size) NULL
358#define sigar_inet_ntop_errno SIGAR_ENOTIMPL
359#elif defined(WIN32)
360static char *sigar_inet_ntop(int af, const void *src, char *dst, int cnt)
361{
362    struct sockaddr_in6 sa; /* note only using this for AF_INET6 */
363
364    memset(&sa, '\0', sizeof(sa));
365    sa.sin6_family = af;
366    memcpy(&sa.sin6_addr, src, sizeof(sa.sin6_addr));
367
368    if (getnameinfo((struct sockaddr *)&sa, sizeof(sa),
369                    dst, cnt, NULL, 0, NI_NUMERICHOST))
370    {
371        return NULL;
372    }
373    else {
374        return dst;
375    }
376}
377#define sigar_inet_ntop_errno GetLastError()
378#else
379#define sigar_inet_ntop inet_ntop
380#define sigar_inet_ntop_errno errno
381#endif
382
383SIGAR_DECLARE(int) sigar_net_address_to_string(sigar_t *sigar,
384                                               sigar_net_address_t *address,
385                                               char *addr_str)
386{
387    *addr_str = '\0';
388    switch (address->family) {
389      case SIGAR_AF_INET6:
390        if (sigar_inet_ntop(AF_INET6, (const void *)&address->addr.in6,
391                            addr_str, SIGAR_INET6_ADDRSTRLEN))
392        {
393            return SIGAR_OK;
394        }
395        else {
396            return sigar_inet_ntop_errno;
397        }
398      case SIGAR_AF_INET:
399        return sigar_inet_ntoa(sigar, address->addr.in, addr_str);
400      case SIGAR_AF_UNSPEC:
401        return sigar_inet_ntoa(sigar, 0, addr_str); /*XXX*/
402      case SIGAR_AF_LINK:
403        return sigar_ether_ntoa(addr_str, &address->addr.mac[0]);
404      default:
405        return EINVAL;
406    }
407}
408
409SIGAR_DECLARE(const char *)sigar_net_scope_to_string(int type)
410{
411    switch (type) {
412    case SIGAR_IPV6_ADDR_ANY:
413        return "Global";
414    case SIGAR_IPV6_ADDR_LOOPBACK:
415        return "Host";
416    case SIGAR_IPV6_ADDR_LINKLOCAL:
417        return "Link";
418    case SIGAR_IPV6_ADDR_SITELOCAL:
419        return "Site";
420    case SIGAR_IPV6_ADDR_COMPATv4:
421        return "Compat";
422    default:
423        return "Unknown";
424    }
425}
426
427SIGAR_DECLARE(sigar_uint32_t) sigar_net_address_hash(sigar_net_address_t *address)
428{
429    sigar_uint32_t hash = 0;
430    unsigned char *data;
431    int i=0, size, elts;
432
433    switch (address->family) {
434      case SIGAR_AF_UNSPEC:
435      case SIGAR_AF_INET:
436        return address->addr.in;
437      case SIGAR_AF_INET6:
438        data = (unsigned char *)&address->addr.in6;
439        size = sizeof(address->addr.in6);
440        elts = 4;
441        break;
442      case SIGAR_AF_LINK:
443        data = (unsigned char *)&address->addr.mac;
444        size = sizeof(address->addr.mac);
445        elts = 2;
446        break;
447      default:
448        return -1;
449    }
450
451    while (i<size) {
452        int j=0;
453        int component=0;
454        while (j<elts && i<size) {
455            component = (component << 8) + data[i];
456            j++;
457            i++;
458        }
459        hash += component;
460    }
461
462    return hash;
463}
464
465SIGAR_DECLARE(const char *)sigar_net_connection_type_get(int type)
466{
467    switch (type) {
468      case SIGAR_NETCONN_TCP:
469        return "tcp";
470      case SIGAR_NETCONN_UDP:
471        return "udp";
472      case SIGAR_NETCONN_RAW:
473        return "raw";
474      case SIGAR_NETCONN_UNIX:
475        return "unix";
476      default:
477        return "unknown";
478    }
479}
480
481SIGAR_DECLARE(const char *)sigar_net_connection_state_get(int state)
482{
483    switch (state) {
484      case SIGAR_TCP_ESTABLISHED:
485        return "ESTABLISHED";
486      case SIGAR_TCP_SYN_SENT:
487        return "SYN_SENT";
488      case SIGAR_TCP_SYN_RECV:
489        return "SYN_RECV";
490      case SIGAR_TCP_FIN_WAIT1:
491        return "FIN_WAIT1";
492      case SIGAR_TCP_FIN_WAIT2:
493        return "FIN_WAIT2";
494      case SIGAR_TCP_TIME_WAIT:
495        return "TIME_WAIT";
496      case SIGAR_TCP_CLOSE:
497        return "CLOSE";
498      case SIGAR_TCP_CLOSE_WAIT:
499        return "CLOSE_WAIT";
500      case SIGAR_TCP_LAST_ACK:
501        return "LAST_ACK";
502      case SIGAR_TCP_LISTEN:
503        return "LISTEN";
504      case SIGAR_TCP_CLOSING:
505        return "CLOSING";
506      case SIGAR_TCP_IDLE:
507        return "IDLE";
508      case SIGAR_TCP_BOUND:
509        return "BOUND";
510      case SIGAR_TCP_UNKNOWN:
511      default:
512        return "UNKNOWN";
513    }
514}
515
516SIGAR_DECLARE(char *) sigar_net_interface_flags_to_string(sigar_uint64_t flags, char *buf)
517{
518    *buf = '\0';
519
520    if (flags == 0) {
521        strcat(buf, "[NO FLAGS] ");
522    }
523    if (flags & SIGAR_IFF_UP) {
524        strcat(buf, "UP ");
525    }
526    if (flags & SIGAR_IFF_BROADCAST) {
527        strcat(buf, "BROADCAST ");
528    }
529    if (flags & SIGAR_IFF_DEBUG) {
530        strcat(buf, "DEBUG ");
531    }
532    if (flags & SIGAR_IFF_LOOPBACK) {
533        strcat(buf, "LOOPBACK ");
534    }
535    if (flags & SIGAR_IFF_POINTOPOINT) {
536        strcat(buf, "POINTOPOINT ");
537    }
538    if (flags & SIGAR_IFF_NOTRAILERS) {
539        strcat(buf, "NOTRAILERS ");
540    }
541    if (flags & SIGAR_IFF_RUNNING) {
542        strcat(buf, "RUNNING ");
543    }
544    if (flags & SIGAR_IFF_NOARP) {
545        strcat(buf, "NOARP ");
546    }
547    if (flags & SIGAR_IFF_PROMISC) {
548        strcat(buf, "PROMISC ");
549    }
550    if (flags & SIGAR_IFF_ALLMULTI) {
551        strcat(buf, "ALLMULTI ");
552    }
553    if (flags & SIGAR_IFF_MULTICAST) {
554        strcat(buf, "MULTICAST ");
555    }
556    if (flags & SIGAR_IFF_SLAVE) {
557        strcat(buf, "SLAVE ");
558    }
559    if (flags & SIGAR_IFF_MASTER) {
560        strcat(buf, "MASTER ");
561    }
562    if (flags & SIGAR_IFF_DYNAMIC) {
563        strcat(buf, "DYNAMIC ");
564    }
565
566    return buf;
567}
568
569#ifdef WIN32
570#define NET_SERVICES_FILE "C:\\windows\\system32\\drivers\\etc\\services"
571#else
572#define NET_SERVICES_FILE "/etc/services"
573#endif
574
575static int net_services_parse(sigar_cache_t *names, char *type)
576{
577    FILE *fp;
578    char buffer[8192], *ptr;
579    char *file;
580
581
582    if (!(file = getenv("SIGAR_NET_SERVICES_FILE"))) {
583        file = NET_SERVICES_FILE;
584    }
585
586    if (!(fp = fopen(file, "r"))) {
587        return errno;
588    }
589
590    while ((ptr = fgets(buffer, sizeof(buffer), fp))) {
591        int port;
592        char name[256], proto[56];
593        sigar_cache_entry_t *entry;
594
595        while (sigar_isspace(*ptr)) {
596            ++ptr;
597        }
598        if ((*ptr == '#') || (*ptr == '\0')) {
599            continue;
600        }
601
602        if (sscanf(ptr, "%s%d/%s", name, &port, proto) != 3) {
603            continue;
604        }
605        if (!strEQ(type, proto)) {
606            continue;
607        }
608
609        entry = sigar_cache_get(names, port);
610        if (!entry->value) {
611            entry->value = strdup(name);
612        }
613    }
614
615    fclose(fp);
616    return SIGAR_OK;
617}
618
619SIGAR_DECLARE(char *)sigar_net_services_name_get(sigar_t *sigar,
620                                                 int protocol, unsigned long port)
621{
622    sigar_cache_entry_t *entry;
623    sigar_cache_t **names;
624    char *pname;
625
626    switch (protocol) {
627      case SIGAR_NETCONN_TCP:
628        names = &sigar->net_services_tcp;
629        pname = "tcp";
630        break;
631      case SIGAR_NETCONN_UDP:
632        names = &sigar->net_services_udp;
633        pname = "udp";
634        break;
635      default:
636        return NULL;
637    }
638
639    if (*names == NULL) {
640        *names = sigar_cache_new(1024);
641        net_services_parse(*names, pname);
642    }
643
644    if ((entry = sigar_cache_find(*names, port))) {
645        return (char *)entry->value;
646    }
647    else {
648        return NULL;
649    }
650}
651
652SIGAR_DECLARE(int) sigar_cpu_perc_calculate(sigar_cpu_t *prev,
653                                            sigar_cpu_t *curr,
654                                            sigar_cpu_perc_t *perc)
655{
656    double diff_user, diff_sys, diff_nice, diff_idle;
657    double diff_wait, diff_irq, diff_soft_irq, diff_stolen;
658    double diff_total;
659
660    diff_user = curr->user - prev->user;
661    diff_sys  = curr->sys  - prev->sys;
662    diff_nice = curr->nice - prev->nice;
663    diff_idle = curr->idle - prev->idle;
664    diff_wait = curr->wait - prev->wait;
665    diff_irq = curr->irq - prev->irq;
666    diff_soft_irq = curr->soft_irq - prev->soft_irq;
667    diff_stolen = curr->stolen - prev->stolen;
668
669    diff_user = diff_user < 0 ? 0 : diff_user;
670    diff_sys  = diff_sys  < 0 ? 0 : diff_sys;
671    diff_nice = diff_nice < 0 ? 0 : diff_nice;
672    diff_idle = diff_idle < 0 ? 0 : diff_idle;
673    diff_wait = diff_wait < 0 ? 0 : diff_wait;
674    diff_irq = diff_irq < 0 ? 0 : diff_irq;
675    diff_soft_irq = diff_soft_irq < 0 ? 0 : diff_soft_irq;
676    diff_stolen = diff_stolen < 0 ? 0 : diff_stolen;
677
678    diff_total =
679        diff_user + diff_sys + diff_nice + diff_idle +
680        diff_wait + diff_irq + diff_soft_irq +
681        diff_stolen;
682
683    perc->user = diff_user / diff_total;
684    perc->sys  = diff_sys / diff_total;
685    perc->nice = diff_nice / diff_total;
686    perc->idle = diff_idle / diff_total;
687    perc->wait = diff_wait / diff_total;
688    perc->irq = diff_irq / diff_total;
689    perc->soft_irq = diff_soft_irq / diff_total;
690    perc->stolen = diff_stolen / diff_total;
691
692    perc->combined =
693        perc->user + perc->sys + perc->nice + perc->wait;
694
695    return SIGAR_OK;
696}
697