common: New function mkdir_p()
[samba.git] / ctdb / common / system_common.c
1 /* 
2    ctdb system specific code to manage raw sockets on linux
3
4    Copyright (C) Ronnie Sahlberg  2007
5    Copyright (C) Andrew Tridgell  2007
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "system/network.h"
23 #include "system/filesys.h"
24 #include <libgen.h>
25
26 /*
27   uint16 checksum for n bytes
28  */
29 uint32_t uint16_checksum(uint16_t *data, size_t n)
30 {
31         uint32_t sum=0;
32         while (n>=2) {
33                 sum += (uint32_t)ntohs(*data);
34                 data++;
35                 n -= 2;
36         }
37         if (n == 1) {
38                 sum += (uint32_t)ntohs(*(uint8_t *)data);
39         }
40         return sum;
41 }
42
43 /*
44   see if we currently have an interface with the given IP
45
46   we try to bind to it, and if that fails then we don't have that IP
47   on an interface
48  */
49 bool ctdb_sys_have_ip(ctdb_sock_addr *_addr)
50 {
51         int s;
52         int ret;
53         ctdb_sock_addr __addr = *_addr;
54         ctdb_sock_addr *addr = &__addr;
55         socklen_t addrlen = 0;
56
57         switch (addr->sa.sa_family) {
58         case AF_INET:
59                 addr->ip.sin_port = 0;
60                 addrlen = sizeof(struct sockaddr_in);
61                 break;
62         case AF_INET6:
63                 addr->ip6.sin6_port = 0;
64                 addrlen = sizeof(struct sockaddr_in6);
65                 break;
66         }
67
68         s = socket(addr->sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
69         if (s == -1) {
70                 return false;
71         }
72
73         ret = bind(s, (struct sockaddr *)addr, addrlen);
74
75         close(s);
76         return ret == 0;
77 }
78
79
80 /* find which interface an ip address is currently assigned to */
81 char *ctdb_sys_find_ifname(ctdb_sock_addr *addr)
82 {
83         int s;
84         int size;
85         struct ifconf ifc;
86         char *ptr;
87
88         s = socket(AF_INET, SOCK_RAW, htons(IPPROTO_RAW));
89         if (s == -1) {
90                 DEBUG(DEBUG_CRIT,(__location__ " failed to open raw socket (%s)\n",
91                          strerror(errno)));
92                 return NULL;
93         }
94
95
96         size = sizeof(struct ifreq);
97         ifc.ifc_buf = NULL;
98         ifc.ifc_len = size;
99
100         while(ifc.ifc_len > (size - sizeof(struct ifreq))) {
101                 size *= 2;
102
103                 free(ifc.ifc_buf);      
104                 ifc.ifc_len = size;
105                 ifc.ifc_buf = malloc(size);
106                 memset(ifc.ifc_buf, 0, size);
107                 if (ioctl(s, SIOCGIFCONF, (caddr_t)&ifc) < 0) {
108                         DEBUG(DEBUG_CRIT,("Failed to read ifc buffer from socket\n"));
109                         free(ifc.ifc_buf);      
110                         close(s);
111                         return NULL;
112                 }
113         }
114
115         for (ptr =(char *)ifc.ifc_buf; ptr < ((char *)ifc.ifc_buf) + ifc.ifc_len; ) {
116                 char *ifname;
117                 struct ifreq *ifr;
118
119                 ifr = (struct ifreq *)ptr;
120
121 #ifdef HAVE_SOCKADDR_LEN
122                 if (ifr->ifr_addr.sa_len > sizeof(struct sockaddr)) {
123                         ptr += sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len;
124                 } else {
125                         ptr += sizeof(ifr->ifr_name) + sizeof(struct sockaddr);
126                 }
127 #else
128                 ptr += sizeof(struct ifreq);
129 #endif
130
131                 if (ifr->ifr_addr.sa_family != addr->sa.sa_family) {
132                         continue;
133                 }
134
135                 switch (addr->sa.sa_family) {
136                 case AF_INET:
137
138
139                         if (memcmp(&addr->ip.sin_addr, &((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr, sizeof(addr->ip.sin_addr))) {
140                                 continue;
141                         }
142                         break;
143                 case AF_INET6:
144                         if (memcmp(&addr->ip6.sin6_addr, &((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_addr, sizeof(addr->ip6.sin6_addr))) {
145                                 continue;
146                         }
147                         break;
148                 }
149
150                 ifname = strdup(ifr->ifr_name);
151                 free(ifc.ifc_buf);      
152                 close(s);
153                 return ifname;
154         }
155
156
157         free(ifc.ifc_buf);      
158         close(s);
159
160         return NULL;
161 }
162
163 int mkdir_p(const char *dir, int mode)
164 {
165         char * t;
166         int ret;
167
168         if (strcmp(dir, "/") == 0) {
169                 return 0;
170         }
171
172         t = talloc_strdup(NULL, dir);
173         if (t == NULL) {
174                 return ENOMEM;
175         }
176         ret = mkdir_p(dirname(t), mode);
177         talloc_free(t);
178
179         if (ret == 0) {
180                 ret = mkdir(dir, mode);
181                 if ((ret == -1) &&  (errno == EEXIST)) {
182                         ret = 0;
183                 }
184         }
185
186         return ret;
187 }