Buffer overrun fix. fixes: bug #787
[exim.git] / src / OS / os.c-Linux
1 /* $Cambridge: exim/src/OS/os.c-Linux,v 1.2 2005/04/06 14:09:17 ph10 Exp $ */
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
23 #if !defined(OS_LOAD_AVERAGE) && defined(__linux__)
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",
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)
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",
129          addr6p[0], addr6p[1], addr6p[2], addr6p[3],
130          addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
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 */