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