refined test-routine to work in vservers without 'lo' interface
[util-vserver.git] / util-vserver / src / chbind.c
1 // $Id$
2
3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 // based on chbind.cc by Jacques Gelinas
5 //  
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2, or (at your option)
9 // any later version.
10 //  
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //  
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 #ifdef HAVE_CONFIG_H
21 #  include <config.h>
22 #endif
23
24 #include "vserver.h"
25 #include "util.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <netdb.h>
31 #include <sys/socket.h>
32 #include <sys/ioctl.h>
33 #include <net/if.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <getopt.h>
37 #include <fcntl.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40
41 #define ENSC_WRAPPERS_IO        1
42 #define ENSC_WRAPPERS_UNISTD    1
43 #include "wrappers.h"
44
45 #define CMD_HELP        0x1000
46 #define CMD_VERSION     0x1001
47
48 #define CMD_SILENT      0x2000
49 #define CMD_IP          0x2001
50 #define CMD_BCAST       0x2002
51
52 int wrapper_exit_code = 255;
53
54
55 static struct option const
56 CMDLINE_OPTIONS[] = {
57   { "help",     no_argument,  0, CMD_HELP },
58   { "version",  no_argument,  0, CMD_VERSION },
59   { "silent",   no_argument,  0, CMD_SILENT },
60   { "ip",       required_argument, 0, CMD_IP },
61   { "bcast",    required_argument, 0, CMD_BCAST },
62   { 0,0,0,0 }
63 };
64
65 static void
66 showHelp(int fd, char const *cmd, int res)
67 {
68   WRITE_MSG(fd, "Usage:\n  ");
69   WRITE_STR(fd, cmd);
70   WRITE_MSG(fd,
71             " [--silent] [--ip <ip_num>[/<mask>]] [--bcast <broadcast>] [--] <commands> <args>*\n\n"
72             "Please report bugs to " PACKAGE_BUGREPORT "\n");
73
74   exit(res);
75 }
76
77 static void
78 showVersion()
79 {
80   WRITE_MSG(1,
81             "chbind " VERSION " -- bind to an ip and execute a program\n"
82             "This program is part of " PACKAGE_STRING "\n\n"
83             "Copyright (C) 2003,2004 Enrico Scholz\n"
84             VERSION_COPYRIGHT_DISCLAIMER);
85   exit(0);
86 }
87
88 /*
89         Check if a network device exist in /proc/net/dev.
90         This is used because ifconfig_ioctl triggers modprobe if requesting
91         information about non existant devices.
92
93         Return != 0 if the device exist.
94 */
95 static bool
96 existsDevice(char const *dev_raw)
97 {
98   size_t        buf_size=8192;
99   char          dev[strlen(dev_raw)+2];
100
101   strcpy(dev, dev_raw);
102   strcat(dev, ":");
103   for (;;) {
104     char        buf[buf_size];
105     char *      pos;
106     bool        too_small;
107     int         fd=open("/proc/net/dev", O_RDONLY);
108     
109     if (fd==-1) return false;
110     too_small = EreadAll(fd, buf, buf_size);
111     close(fd);
112
113     if (too_small) {
114       buf_size *= 2;
115       continue;
116     }
117
118     pos = strstr(buf, dev);
119     return (pos && (pos==buf || pos[-1]==' ' || pos[-1]=='\n'));
120   }
121 }
122
123 static int ifconfig_ioctl(
124         int fd,
125         const char *ifname,
126         int cmd,
127         struct ifreq *ifr)
128 {
129         strcpy(ifr->ifr_name, ifname);
130         return ioctl(fd, cmd,ifr);
131 }
132
133 /*
134         Fetch the IP number of an interface from the kernel.
135         Assume the device is already available in the kernel
136         Return -1 if any error.
137 */
138 int ifconfig_getaddr (
139         const char *ifname,
140         uint32_t *addr,
141         uint32_t *mask,
142         uint32_t *bcast)
143 {
144         int ret = -1;
145         if (existsDevice(ifname)){
146                 int skfd = socket(AF_INET, SOCK_DGRAM, 0);
147                 *addr = 0;
148                 *bcast = 0xffffffff;
149                 if (skfd != -1){
150                         struct ifreq ifr;
151                         if (ifconfig_ioctl(skfd,ifname,SIOCGIFADDR, &ifr) >= 0){
152                                 struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
153                                 *addr = sin->sin_addr.s_addr;
154                                 ret = 0;
155                         }
156                         if (ifconfig_ioctl(skfd,ifname,SIOCGIFNETMASK, &ifr) >= 0){
157                                 struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
158                                 *mask = sin->sin_addr.s_addr;
159                                 ret = 0;
160                         }
161                         if (ifconfig_ioctl(skfd,ifname,SIOCGIFBRDADDR, &ifr) >= 0){
162                                 struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
163                                 *bcast = sin->sin_addr.s_addr;
164                                 ret = 0;
165                         }
166                         close (skfd);
167                 }
168         }
169         return ret;
170 }
171
172 static void
173 readIP(char const *str, struct vc_ip_mask_pair *ip, uint32_t *bcast)
174 {
175   if (ifconfig_getaddr(str, &ip->ip, &ip->mask, bcast)==-1) {
176     char                *pt;
177     char                tmpopt[strlen(str)+1];
178     struct hostent      *h;
179
180     strcpy(tmpopt,str);
181     pt = strchr(tmpopt,'/');
182     
183     if (pt==0)
184       ip->mask = ntohl(0xffffff00);
185     else {
186       *pt++ = '\0';
187
188       // Ok, we have a network size, not a netmask
189       if (strchr(pt,'.')==0) {
190         int             sz = atoi(pt);
191         ;
192         for (ip->mask = 0; sz>0; --sz) {
193           ip->mask >>= 1;
194           ip->mask  |= 0x80000000;
195         }
196         ip->mask = ntohl(ip->mask);
197       }
198       else { 
199         struct hostent *h = gethostbyname (pt);
200         if (h==0) {
201           WRITE_MSG(2, "Invalid netmask '");
202           WRITE_STR(2, pt);
203           WRITE_MSG(2, "'\n");
204           exit(wrapper_exit_code);
205         }
206
207         memcpy (&ip->mask, h->h_addr, sizeof(ip->mask));
208       }
209     }
210
211     h = gethostbyname (tmpopt);
212     if (h==0) {
213       WRITE_MSG(2, "Invalid IP number or host name '");
214       WRITE_STR(2, tmpopt);
215       WRITE_MSG(2, "'\n");
216       exit(wrapper_exit_code);
217     }
218
219     memcpy (&ip->ip, h->h_addr,sizeof(ip->ip));
220   }
221 }
222
223 static void
224 readBcast(char const *str, uint32_t *bcast)
225 {
226   uint32_t      tmp;
227   if (ifconfig_getaddr(str, &tmp, &tmp, bcast)==-1){
228     struct hostent *h = gethostbyname (str);
229     if (h==0){
230       WRITE_MSG(2, "Invalid broadcast number '");
231       WRITE_STR(2, optarg);
232       WRITE_MSG(2, "'\n");
233       exit(wrapper_exit_code);
234     }
235     memcpy (bcast, h->h_addr,sizeof(*bcast));
236   }
237 }
238
239 int main (int argc, char *argv[])
240 {
241   bool                          is_silent = false;
242   struct vc_ip_mask_pair        ips[16];
243   size_t                        nbaddrs = 0;
244   uint32_t                      bcast = 0xffffffff;
245   
246   while (1) {
247     int         c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
248     if (c==-1) break;
249
250     switch (c) {
251       case CMD_HELP             :  showHelp(1, argv[0], 0);
252       case CMD_VERSION          :  showVersion();
253       case CMD_SILENT           :  is_silent = true; break;
254       case CMD_BCAST            :  readBcast(optarg, &bcast); break;
255       case CMD_IP               :
256         if (nbaddrs>=16) {
257           WRITE_MSG(2, "Too many IP numbers, max 16\n");
258           exit(wrapper_exit_code);
259         }
260         readIP(optarg, ips+nbaddrs, &bcast);
261         ++nbaddrs;
262         break;
263       default           :
264         WRITE_MSG(2, "Try '");
265         WRITE_STR(2, argv[0]);
266         WRITE_MSG(2, " --help\" for more information.\n");
267         exit(wrapper_exit_code);
268         break;
269     }
270   }
271
272   if (optind==argc) {
273     WRITE_MSG(2, "No command given; try '--help' for more information\n");
274     exit(wrapper_exit_code);
275   }
276   
277
278   if (vc_set_ipv4root(bcast,nbaddrs,ips)!=0) {
279     perror("vc_set_ipv4root()");
280     exit(wrapper_exit_code);
281   }
282
283   if (!is_silent) {
284     size_t              i;
285     
286     WRITE_MSG(1, "ipv4root is now");
287     for (i=0; i<nbaddrs; ++i) {
288       WRITE_MSG(1, " ");
289       WRITE_STR(1, inet_ntoa(*reinterpret_cast(struct in_addr *)(&ips[i].ip)));
290     }
291     WRITE_MSG(1, "\n");
292   }
293
294   Eexecvp (argv[optind],argv+optind);
295   return EXIT_SUCCESS;
296 }
297
298 #ifdef ENSC_TESTSUITE
299 #include <assert.h>
300
301 void test()
302 {
303   struct vc_ip_mask_pair        ip;
304   uint32_t                      bcast;
305   uint32_t                      tmp;
306
307   bcast = 0;
308   readIP("1.2.3.4", &ip, &bcast);
309   assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffffff00) && bcast==0);
310
311   readIP("1.2.3.4/8", &ip, &bcast);
312   assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xff000000) && bcast==0);
313
314   readIP("1.2.3.4/255.255.0.0", &ip, &bcast);
315   assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffff0000) && bcast==0);
316
317   readIP("localhost", &ip, &bcast);
318   assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xffffff00) && bcast==0);
319
320   if (ifconfig_getaddr("lo", &tmp, &tmp, &tmp)!=-1) {
321     readIP("lo", &ip, &bcast);
322     assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xff000000) && bcast==ntohl(0x7fffffff));
323   }
324 }
325 #endif