Fix missing Changelog entries for recent work
[exim.git] / src / OS / os.c-Linux
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1997 - 2016 */
6 /* See the file NOTICE for conditions of use and distribution. */
7
8 /* Linux-specific code. This is concatenated onto the generic
9 src/os.c file. */
10
11
12 /*************************************************
13 *              Load average computation          *
14 *************************************************/
15
16 /*Linux has an apparently unique way of getting the load average, so we provide
17 a unique function here, and define OS_LOAD_AVERAGE to stop src/os.c trying to
18 provide the function. However, when compiling os.c for utilities, we may not
19 want this at all, so check that it isn't set first. */
20
21 #if !defined(OS_LOAD_AVERAGE) && defined(__linux__)
22 #define OS_LOAD_AVERAGE
23
24 /* Linux has 2 ways of returning load average:
25
26   (1) Do a read on /proc/loadavg
27   (2) Use the sysinfo library function and syscall
28
29 The latter is simpler but in Linux 2.0 - 2.2 (and probably later releases) is
30 exceptionally slow - 10-50ms per call is not unusual and about 100x slow the
31 first method. This cripples high performance mail servers by increasing CPU
32 utilisation by 3-5x.
33
34 In Exim's very early days, it used the 1st method. Later, it switched to the
35 2nd method. Now it tries the 1st method and falls back to the 2nd if /proc is
36 unavailable. */
37
38 #include <sys/sysinfo.h>
39
40 static int
41 linux_slow_getloadavg(void)
42 {
43 struct sysinfo s;
44 double avg;
45 if (sysinfo(&s) < 0) return -1;
46 avg = (double) (s.loads[0]) / (1<<SI_LOAD_SHIFT);
47 return (int)(avg * 1000.0);
48 }
49
50 int
51 os_getloadavg(void)
52 {
53 char buffer[40];
54 double avg;
55 int count;
56 int fd = open ("/proc/loadavg", O_RDONLY);
57 if (fd == -1) return linux_slow_getloadavg();
58 count = read (fd, buffer, sizeof(buffer));
59 (void)close (fd);
60 if (count <= 0) return linux_slow_getloadavg();
61 count = sscanf (buffer, "%lf", &avg);
62 if (count < 1) return linux_slow_getloadavg();
63 return (int)(avg * 1000.0);
64 }
65 #endif  /* OS_LOAD_AVERAGE */
66
67
68
69
70
71 /*************************************************
72 *         Finding interface addresses            *
73 *************************************************/
74
75 /* This function is not required for utilities; we cut it out if
76 FIND_RUNNING_INTERFACES is already defined. */
77
78 #ifndef FIND_RUNNING_INTERFACES
79
80 /* This code, contributed by Jason Gunthorpe, appears to be the current
81 way of finding IPv6 interfaces in Linux. It first calls the common function in
82 order to find IPv4 interfaces, then grobbles around to find the others. Jason
83 said, "This is so horrible, don't look. Slightly ripped from net-tools
84 ifconfig." It gets called by virtue of os_find_running_interfaces being defined
85 as a macro for os_find_running_interfaces_linux in the os.h-Linux file. */
86
87 ip_address_item *
88 os_find_running_interfaces_linux(void)
89 {
90 ip_address_item *yield = NULL;
91
92 #if HAVE_IPV6
93 ip_address_item *last = NULL;
94 ip_address_item  *next;
95 char addr6p[8][5];
96 unsigned int plen, scope, dad_status, if_idx;
97 char devname[20+1];
98 FILE *f;
99 #endif
100
101 yield = os_common_find_running_interfaces();
102
103 #if HAVE_IPV6
104
105 /* Open the /proc file; give up if we can't. */
106
107 if ((f = fopen("/proc/net/if_inet6", "r")) == NULL) return yield;
108
109 /* Pick out the data from within the file, and add it on to the chain */
110
111 last = yield;
112 if (last != NULL) while (last->next != NULL) last = last->next;
113
114 while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %20s\n",
115              addr6p[0], addr6p[1], addr6p[2], addr6p[3],
116              addr6p[4], addr6p[5], addr6p[6], addr6p[7],
117              &if_idx, &plen, &scope, &dad_status, devname) != EOF)
118   {
119   struct sockaddr_in6 addr;
120
121   /* This data has to survive for ever, so use malloc. */
122
123   next = store_malloc(sizeof(ip_address_item));
124   next->next = NULL;
125   next->port = 0;
126   sprintf(CS next->address, "%s:%s:%s:%s:%s:%s:%s:%s",
127          addr6p[0], addr6p[1], addr6p[2], addr6p[3],
128          addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
129
130   /* Normalize the representation */
131
132   inet_pton(AF_INET6, CS next->address, &addr.sin6_addr);
133   inet_ntop(AF_INET6, &addr.sin6_addr, CS next->address, sizeof(next->address));
134
135   if (yield == NULL) yield = last = next; else
136     {
137     last->next = next;
138     last = next;
139     }
140
141   DEBUG(D_interface)
142     debug_printf("Actual local interface address is %s (%s)\n", last->address,
143       devname);
144   }
145 fclose(f);
146 #endif  /* HAVE_IPV6 */
147
148 return yield;
149 }
150
151 #endif  /* FIND_RUNNING_INTERFACES */
152
153 /* End of os.c-Linux */