bc687119c03aff22e31cadd9eec1a14fa92b81df
[util-vserver.git] / src / chbind.c
1 // $Id: chbind.c,v 1.9 2004/03/16 14:30:10 ensc Exp $
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_PREFIX    "chbind: "
42 #define ENSC_WRAPPERS_IO        1
43 #define ENSC_WRAPPERS_UNISTD    1
44 #include "wrappers.h"
45
46 #define CMD_HELP        0x1000
47 #define CMD_VERSION     0x1001
48
49 #define CMD_SILENT      0x2000
50 #define CMD_IP          0x2001
51 #define CMD_BCAST       0x2002
52
53 int wrapper_exit_code = 255;
54
55
56 static struct option const
57 CMDLINE_OPTIONS[] = {
58   { "help",     no_argument,  0, CMD_HELP },
59   { "version",  no_argument,  0, CMD_VERSION },
60   { "silent",   no_argument,  0, CMD_SILENT },
61   { "ip",       required_argument, 0, CMD_IP },
62   { "bcast",    required_argument, 0, CMD_BCAST },
63   { 0,0,0,0 }
64 };
65
66 static void
67 showHelp(int fd, char const *cmd, int res)
68 {
69   WRITE_MSG(fd, "Usage:\n  ");
70   WRITE_STR(fd, cmd);
71   WRITE_MSG(fd,
72             " [--silent] [--ip <ip_num>[/<mask>]] [--bcast <broadcast>] [--] <commands> <args>*\n\n"
73             "Please report bugs to " PACKAGE_BUGREPORT "\n");
74
75   exit(res);
76 }
77
78 static void
79 showVersion()
80 {
81   WRITE_MSG(1,
82             "chbind " VERSION " -- bind to an ip and execute a program\n"
83             "This program is part of " PACKAGE_STRING "\n\n"
84             "Copyright (C) 2003,2004 Enrico Scholz\n"
85             VERSION_COPYRIGHT_DISCLAIMER);
86   exit(0);
87 }
88
89 /*
90         Check if a network device exist in /proc/net/dev.
91         This is used because ifconfig_ioctl triggers modprobe if requesting
92         information about non existant devices.
93
94         Return != 0 if the device exist.
95 */
96 static bool
97 existsDevice(char const *dev_raw)
98 {
99   size_t        buf_size=8192;
100   char          dev[strlen(dev_raw)+2];
101
102   strcpy(dev, dev_raw);
103   strcat(dev, ":");
104   for (;;) {
105     char        buf[buf_size];
106     char *      pos;
107     bool        too_small;
108     int         fd=open("/proc/net/dev", O_RDONLY);
109     
110     if (fd==-1) return false;
111     too_small = EreadAll(fd, buf, buf_size);
112     close(fd);
113
114     if (too_small) {
115       buf_size *= 2;
116       continue;
117     }
118
119     pos = strstr(buf, dev);
120     return (pos && (pos==buf || pos[-1]==' ' || pos[-1]=='\n'));
121   }
122 }
123
124 static int ifconfig_ioctl(
125         int fd,
126         const char *ifname,
127         int cmd,
128         struct ifreq *ifr)
129 {
130         strcpy(ifr->ifr_name, ifname);
131         return ioctl(fd, cmd,ifr);
132 }
133
134 /*
135         Fetch the IP number of an interface from the kernel.
136         Assume the device is already available in the kernel
137         Return -1 if any error.
138 */
139 int ifconfig_getaddr (
140         const char *ifname,
141         uint32_t *addr,
142         uint32_t *mask,
143         uint32_t *bcast)
144 {
145         int ret = -1;
146         if (existsDevice(ifname)){
147                 int skfd = socket(AF_INET, SOCK_DGRAM, 0);
148                 *addr = 0;
149                 *bcast = 0xffffffff;
150                 if (skfd != -1){
151                         struct ifreq ifr;
152                         if (ifconfig_ioctl(skfd,ifname,SIOCGIFADDR, &ifr) >= 0){
153                                 struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
154                                 *addr = sin->sin_addr.s_addr;
155                                 ret = 0;
156                         }
157                         if (ifconfig_ioctl(skfd,ifname,SIOCGIFNETMASK, &ifr) >= 0){
158                                 struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
159                                 *mask = sin->sin_addr.s_addr;
160                                 ret = 0;
161                         }
162                         if (ifconfig_ioctl(skfd,ifname,SIOCGIFBRDADDR, &ifr) >= 0){
163                                 struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
164                                 *bcast = sin->sin_addr.s_addr;
165                                 ret = 0;
166                         }
167                         close (skfd);
168                 }
169         }
170         return ret;
171 }
172
173 static void
174 readIP(char const *str, struct vc_ip_mask_pair *ip, uint32_t *bcast)
175 {
176   if (ifconfig_getaddr(str, &ip->ip, &ip->mask, bcast)==-1) {
177     char                *pt;
178     char                tmpopt[strlen(str)+1];
179     struct hostent      *h;
180
181     strcpy(tmpopt,str);
182     pt = strchr(tmpopt,'/');
183     
184     if (pt==0)
185       ip->mask = ntohl(0xffffff00);
186     else {
187       *pt++ = '\0';
188
189       // Ok, we have a network size, not a netmask
190       if (strchr(pt,'.')==0) {
191         int             sz = atoi(pt);
192         ;
193         for (ip->mask = 0; sz>0; --sz) {
194           ip->mask >>= 1;
195           ip->mask  |= 0x80000000;
196         }
197         ip->mask = ntohl(ip->mask);
198       }
199       else { 
200         struct hostent *h = gethostbyname (pt);
201         if (h==0) {
202           WRITE_MSG(2, "Invalid netmask '");
203           WRITE_STR(2, pt);
204           WRITE_MSG(2, "'\n");
205           exit(wrapper_exit_code);
206         }
207
208         memcpy (&ip->mask, h->h_addr, sizeof(ip->mask));
209       }
210     }
211
212     h = gethostbyname (tmpopt);
213     if (h==0) {
214       WRITE_MSG(2, "Invalid IP number or host name '");
215       WRITE_STR(2, tmpopt);
216       WRITE_MSG(2, "'\n");
217       exit(wrapper_exit_code);
218     }
219
220     memcpy (&ip->ip, h->h_addr,sizeof(ip->ip));
221   }
222 }
223
224 static void
225 readBcast(char const *str, uint32_t *bcast)
226 {
227   uint32_t      tmp;
228   if (ifconfig_getaddr(str, &tmp, &tmp, bcast)==-1){
229     struct hostent *h = gethostbyname (str);
230     if (h==0){
231       WRITE_MSG(2, "Invalid broadcast number '");
232       WRITE_STR(2, optarg);
233       WRITE_MSG(2, "'\n");
234       exit(wrapper_exit_code);
235     }
236     memcpy (bcast, h->h_addr,sizeof(*bcast));
237   }
238 }
239
240 int main (int argc, char *argv[])
241 {
242   size_t const                  nb_ipv4root = vc_get_nb_ipv4root();
243   bool                          is_silent   = false;
244   struct vc_ip_mask_pair        ips[nb_ipv4root];
245   size_t                        nbaddrs = 0;
246   uint32_t                      bcast   = 0xffffffff;
247   
248   while (1) {
249     int         c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
250     if (c==-1) break;
251
252     switch (c) {
253       case CMD_HELP             :  showHelp(1, argv[0], 0);
254       case CMD_VERSION          :  showVersion();
255       case CMD_SILENT           :  is_silent = true; break;
256       case CMD_BCAST            :  readBcast(optarg, &bcast); break;
257       case CMD_IP               :
258         if (nbaddrs>=nb_ipv4root) {
259           WRITE_MSG(2, "Too many IP numbers, max 16\n");
260           exit(wrapper_exit_code);
261         }
262         readIP(optarg, ips+nbaddrs, &bcast);
263         ++nbaddrs;
264         break;
265       default           :
266         WRITE_MSG(2, "Try '");
267         WRITE_STR(2, argv[0]);
268         WRITE_MSG(2, " --help\" for more information.\n");
269         exit(wrapper_exit_code);
270         break;
271     }
272   }
273
274   if (optind==argc) {
275     WRITE_MSG(2, "No command given; try '--help' for more information\n");
276     exit(wrapper_exit_code);
277   }
278   
279
280   if (vc_set_ipv4root(bcast,nbaddrs,ips)!=0) {
281     perror("chbind: vc_set_ipv4root()");
282     exit(wrapper_exit_code);
283   }
284
285   if (!is_silent) {
286     size_t              i;
287     
288     WRITE_MSG(1, "ipv4root is now");
289     for (i=0; i<nbaddrs; ++i) {
290       WRITE_MSG(1, " ");
291       WRITE_STR(1, inet_ntoa(*reinterpret_cast(struct in_addr *)(&ips[i].ip)));
292     }
293     WRITE_MSG(1, "\n");
294   }
295
296   Eexecvp (argv[optind],argv+optind);
297   return EXIT_SUCCESS;
298 }
299
300 #ifdef ENSC_TESTSUITE
301 #include <assert.h>
302
303 void test()
304 {
305   struct vc_ip_mask_pair        ip;
306   uint32_t                      bcast;
307
308   bcast = 0;
309   readIP("1.2.3.4", &ip, &bcast);
310   assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffffff00) && bcast==0);
311
312   readIP("1.2.3.4/8", &ip, &bcast);
313   assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xff000000) && bcast==0);
314
315   readIP("1.2.3.4/255.255.0.0", &ip, &bcast);
316   assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffff0000) && bcast==0);
317
318   readIP("localhost", &ip, &bcast);
319   assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xffffff00) && bcast==0);
320
321 #if 0
322   if (ifconfig_getaddr("lo", &tmp, &tmp, &tmp)!=-1) {
323     readIP("lo", &ip, &bcast);
324     assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xff000000) && bcast==ntohl(0x7fffffff));
325   }
326 #endif
327 }
328 #endif