Commit | Line | Data |
---|---|---|
61ec970d PH |
1 | /************************************************* |
2 | * Exim - an Internet mail transport agent * | |
3 | *************************************************/ | |
4 | ||
8d6d5106 | 5 | /* Copyright (c) University of Cambridge 1997 - 2014 */ |
61ec970d PH |
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 | ||
7e8bec7a | 21 | #if !defined(OS_LOAD_AVERAGE) && defined(__linux__) |
61ec970d PH |
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; | |
c3680ef0 | 97 | char devname[20+1]; |
61ec970d PH |
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", | |
7e8bec7a PH |
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) | |
61ec970d PH |
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", | |
7e8bec7a PH |
127 | addr6p[0], addr6p[1], addr6p[2], addr6p[3], |
128 | addr6p[4], addr6p[5], addr6p[6], addr6p[7]); | |
61ec970d PH |
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 */ |