summaryrefslogtreecommitdiff
path: root/src/OS/os.c-Linux
blob: 5589a015bc1177cd450c286b8bff9c90aeb64712 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/* $Cambridge: exim/src/OS/os.c-Linux,v 1.1 2004/10/06 15:07:39 ph10 Exp $ */

/*************************************************
*     Exim - an Internet mail transport agent    *
*************************************************/

/* Copyright (c) University of Cambridge 1997 - 2001 */
/* See the file NOTICE for conditions of use and distribution. */

/* Linux-specific code. This is concatenated onto the generic
src/os.c file. */


/*************************************************
*              Load average computation          *
*************************************************/

/*Linux has an apparently unique way of getting the load average, so we provide
a unique function here, and define OS_LOAD_AVERAGE to stop src/os.c trying to
provide the function. However, when compiling os.c for utilities, we may not
want this at all, so check that it isn't set first. */

#ifndef OS_LOAD_AVERAGE
#define OS_LOAD_AVERAGE

/* Linux has 2 ways of returning load average:

  (1) Do a read on /proc/loadavg
  (2) Use the sysinfo library function and syscall

The latter is simpler but in Linux 2.0 - 2.2 (and probably later releases) is
exceptionally slow - 10-50ms per call is not unusual and about 100x slow the
first method. This cripples high performance mail servers by increasing CPU
utilisation by 3-5x.

In Exim's very early days, it used the 1st method. Later, it switched to the
2nd method. Now it tries the 1st method and falls back to the 2nd if /proc is
unavailable. */

#include <sys/sysinfo.h>

static int
linux_slow_getloadavg(void)
{
struct sysinfo s;
double avg;
if (sysinfo(&s) < 0) return -1;
avg = (double) (s.loads[0]) / (1<<SI_LOAD_SHIFT);
return (int)(avg * 1000.0);
}

int
os_getloadavg(void)
{
char buffer[40];
double avg;
int count;
int fd = open ("/proc/loadavg", O_RDONLY);
if (fd == -1) return linux_slow_getloadavg();
count = read (fd, buffer, sizeof(buffer));
(void)close (fd);
if (count <= 0) return linux_slow_getloadavg();
count = sscanf (buffer, "%lf", &avg);
if (count < 1) return linux_slow_getloadavg();
return (int)(avg * 1000.0);
}
#endif  /* OS_LOAD_AVERAGE */





/*************************************************
*         Finding interface addresses            *
*************************************************/

/* This function is not required for utilities; we cut it out if
FIND_RUNNING_INTERFACES is already defined. */

#ifndef FIND_RUNNING_INTERFACES

/* This code, contributed by Jason Gunthorpe, appears to be the current
way of finding IPv6 interfaces in Linux. It first calls the common function in
order to find IPv4 interfaces, then grobbles around to find the others. Jason
said, "This is so horrible, don't look. Slightly ripped from net-tools
ifconfig." It gets called by virtue of os_find_running_interfaces being defined
as a macro for os_find_running_interfaces_linux in the os.h-Linux file. */

ip_address_item *
os_find_running_interfaces_linux(void)
{
ip_address_item *yield = NULL;

#if HAVE_IPV6
ip_address_item *last = NULL;
ip_address_item  *next;
char addr6p[8][5];
unsigned int plen, scope, dad_status, if_idx;
char devname[20];
FILE *f;
#endif

yield = os_common_find_running_interfaces();

#if HAVE_IPV6

/* Open the /proc file; give up if we can't. */

if ((f = fopen("/proc/net/if_inet6", "r")) == NULL) return yield;

/* Pick out the data from within the file, and add it on to the chain */

last = yield;
if (last != NULL) while (last->next != NULL) last = last->next;

while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %20s\n",
	     addr6p[0], addr6p[1], addr6p[2], addr6p[3],
	     addr6p[4], addr6p[5], addr6p[6], addr6p[7],
	     &if_idx, &plen, &scope, &dad_status, devname) != EOF)
  {
  struct sockaddr_in6 addr;

  /* This data has to survive for ever, so use malloc. */

  next = store_malloc(sizeof(ip_address_item));
  next->next = NULL;
  next->port = 0;
  sprintf(CS next->address, "%s:%s:%s:%s:%s:%s:%s:%s",
	 addr6p[0], addr6p[1], addr6p[2], addr6p[3],
	 addr6p[4], addr6p[5], addr6p[6], addr6p[7]);

  /* Normalize the representation */

  inet_pton(AF_INET6, CS next->address, &addr.sin6_addr);
  inet_ntop(AF_INET6, &addr.sin6_addr, CS next->address, sizeof(next->address));

  if (yield == NULL) yield = last = next; else
    {
    last->next = next;
    last = next;
    }

  DEBUG(D_interface)
    debug_printf("Actual local interface address is %s (%s)\n", last->address,
      devname);
  }
fclose(f);
#endif  /* HAVE_IPV6 */

return yield;
}

#endif  /* FIND_RUNNING_INTERFACES */

/* End of os.c-Linux */