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