Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * | 2 * |
| 3 * Connection Manager | 3 * Connection Manager |
| 4 * | 4 * |
| 5 * Copyright (C) 2007-2010 Intel Corporation. All rights reserved. | 5 * Copyright (C) 2007-2010 Intel Corporation. All rights reserved. |
| 6 * | 6 * |
| 7 * This program is free software; you can redistribute it and/or modify | 7 * This program is free software; you can redistribute it and/or modify |
| 8 * it under the terms of the GNU General Public License version 2 as | 8 * it under the terms of the GNU General Public License version 2 as |
| 9 * published by the Free Software Foundation. | 9 * published by the Free Software Foundation. |
| 10 * | 10 * |
| 11 * This program is distributed in the hope that it will be useful, | 11 * This program is distributed in the hope that it will be useful, |
| 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 * GNU General Public License for more details. | 14 * GNU General Public License for more details. |
| 15 * | 15 * |
| 16 * You should have received a copy of the GNU General Public License | 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 | 17 * along with this program; if not, write to the Free Software |
| 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 19 * | 19 * |
| 20 */ | 20 */ |
| 21 | 21 |
| 22 /* | 22 /* |
| 23 * Support for vpn plugins. Common code to manage a provider object, | 23 * Support for vpn plugins. Common code to manage a provider object, |
| 24 * tun device and a task associated with an external vpn process. The | 24 * tun device and a task associated with an external vpn process. The |
| 25 * vpn plugin is responsible for launching the external process and | 25 * vpn plugin is responsible for launching the external process and |
| 26 * handling notification callbacks to clock the provider state machine. | 26 * handling notification callbacks to clock the provider state machine. |
| 27 * | 27 * |
| 28 * TODO(sleffler) currently assumes extern vpn service uses tun but not all do | |
| 29 * TODO(sleffler) seems to make more sense in src than plugins | 28 * TODO(sleffler) seems to make more sense in src than plugins |
| 30 */ | 29 */ |
| 31 | 30 |
| 32 #ifdef HAVE_CONFIG_H | 31 #ifdef HAVE_CONFIG_H |
| 33 #include <config.h> | 32 #include <config.h> |
| 34 #endif | 33 #endif |
| 35 | 34 |
| 36 #include <string.h> | 35 #include <string.h> |
| 37 #include <fcntl.h> | 36 #include <fcntl.h> |
| 38 #include <unistd.h> | 37 #include <unistd.h> |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 70 | 69 |
| 71 struct vpn_driver_data { | 70 struct vpn_driver_data { |
| 72 const char *name; | 71 const char *name; |
| 73 const char *program; | 72 const char *program; |
| 74 struct vpn_driver *vpn_driver; | 73 struct vpn_driver *vpn_driver; |
| 75 struct connman_provider_driver provider_driver; | 74 struct connman_provider_driver provider_driver; |
| 76 }; | 75 }; |
| 77 | 76 |
| 78 static GHashTable *driver_hash = NULL; | 77 static GHashTable *driver_hash = NULL; |
| 79 | 78 |
| 80 static int kill_tun(char *tun_name) | 79 static int kill_tun(struct connman_provider *provider) |
| 81 { | 80 { |
| 81 struct vpn_data *data = connman_provider_get_data(provider); | |
| 82 struct vpn_driver_data *vpn_driver_data; | |
| 83 const char *name; | |
| 82 struct ifreq ifr; | 84 struct ifreq ifr; |
| 83 int fd, err; | 85 int fd, err; |
| 84 | 86 |
| 87 if (data == NULL) | |
| 88 return -1; | |
| 89 | |
| 90 name = connman_provider_get_driver_name(provider); | |
| 91 vpn_driver_data = g_hash_table_lookup(driver_hash, name); | |
| 92 | |
| 93 if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver !=NULL && | |
| 94 vpn_driver_data->vpn_driver->flags == VPN_FLAG_NO_TUN) | |
| 95 return 0; | |
| 96 | |
| 85 memset(&ifr, 0, sizeof(ifr)); | 97 memset(&ifr, 0, sizeof(ifr)); |
| 86 ifr.ifr_flags = IFF_TUN | IFF_NO_PI; | 98 ifr.ifr_flags = IFF_TUN | IFF_NO_PI; |
| 87 » strncpy(ifr.ifr_name, tun_name, sizeof(ifr.ifr_name)); | 99 » strncpy(ifr.ifr_name, data->if_name, sizeof(ifr.ifr_name)); |
|
thutt
2011/03/17 15:02:21
(nit) It looks like you are adding tabs in a file
kmixter1
2011/03/17 17:52:20
It's the latter. The file is mostly tabs but I di
| |
| 88 | 100 |
| 89 fd = open("/dev/net/tun", O_RDWR); | 101 fd = open("/dev/net/tun", O_RDWR); |
| 90 if (fd < 0) { | 102 if (fd < 0) { |
| 91 err = -errno; | 103 err = -errno; |
| 92 connman_error("Failed to open /dev/net/tun to device %s: %s", | 104 connman_error("Failed to open /dev/net/tun to device %s: %s", |
| 93 » » » tun_name, strerror(errno)); | 105 » » » data->if_name, strerror(errno)); |
| 94 return err; | 106 return err; |
| 95 } | 107 } |
| 96 | 108 |
| 97 if (ioctl(fd, TUNSETIFF, (void *)&ifr)) { | 109 if (ioctl(fd, TUNSETIFF, (void *)&ifr)) { |
| 98 err = -errno; | 110 err = -errno; |
| 99 connman_error("Failed to TUNSETIFF for device %s to it: %s", | 111 connman_error("Failed to TUNSETIFF for device %s to it: %s", |
| 100 » » » tun_name, strerror(errno)); | 112 » » » data->if_name, strerror(errno)); |
| 101 close(fd); | 113 close(fd); |
| 102 return err; | 114 return err; |
| 103 } | 115 } |
| 104 | 116 |
| 105 if (ioctl(fd, TUNSETPERSIST, 0)) { | 117 if (ioctl(fd, TUNSETPERSIST, 0)) { |
| 106 err = -errno; | 118 err = -errno; |
| 107 connman_error("Failed to set tun device %s nonpersistent: %s", | 119 connman_error("Failed to set tun device %s nonpersistent: %s", |
| 108 » » » tun_name, strerror(errno)); | 120 » » » data->if_name, strerror(errno)); |
| 109 close(fd); | 121 close(fd); |
| 110 return err; | 122 return err; |
| 111 } | 123 } |
| 112 close(fd); | 124 close(fd); |
| 113 » _DBG_VPN("Killed tun device %s", tun_name); | 125 » _DBG_VPN("Killed tun device %s", data->if_name); |
| 114 return 0; | 126 return 0; |
| 115 } | 127 } |
| 116 | 128 |
| 117 void vpn_died(struct connman_task *task, void *user_data) | 129 void vpn_died(struct connman_task *task, void *user_data) |
| 118 { | 130 { |
| 119 struct connman_provider *provider = user_data; | 131 struct connman_provider *provider = user_data; |
| 120 struct vpn_data *data = connman_provider_get_data(provider); | 132 struct vpn_data *data = connman_provider_get_data(provider); |
| 121 int state = data->state; | 133 int state = data->state; |
| 122 | 134 |
| 123 _DBG_VPN("provider %p data %p", provider, data); | 135 _DBG_VPN("provider %p data %p", provider, data); |
| 124 | 136 |
| 125 if (!data) | 137 if (!data) |
| 126 goto vpn_exit; | 138 goto vpn_exit; |
| 127 | 139 |
| 128 » kill_tun(data->if_name); | 140 » kill_tun(provider); |
| 129 connman_provider_set_data(provider, NULL); | 141 connman_provider_set_data(provider, NULL); |
| 130 connman_rtnl_remove_watch(data->watch); | 142 connman_rtnl_remove_watch(data->watch); |
| 131 | 143 |
| 132 vpn_exit: | 144 vpn_exit: |
| 133 if (state != VPN_STATE_READY && state != VPN_STATE_DISCONNECT) | 145 if (state != VPN_STATE_READY && state != VPN_STATE_DISCONNECT) |
| 134 connman_provider_set_state(provider, | 146 connman_provider_set_state(provider, |
| 135 CONNMAN_PROVIDER_STATE_FAILURE); | 147 CONNMAN_PROVIDER_STATE_FAILURE); |
| 136 else | 148 else |
| 137 connman_provider_set_state(provider, | 149 connman_provider_set_state(provider, |
| 138 CONNMAN_PROVIDER_STATE_IDLE); | 150 CONNMAN_PROVIDER_STATE_IDLE); |
| 139 | 151 |
| 140 connman_provider_set_index(provider, -1); | 152 connman_provider_set_index(provider, -1); |
| 141 connman_provider_unref(data->provider); | 153 connman_provider_unref(data->provider); |
| 142 g_free(data); | 154 g_free(data); |
| 143 | 155 |
| 144 connman_task_destroy(task); | 156 connman_task_destroy(task); |
| 145 } | 157 } |
| 146 | 158 |
| 159 int vpn_set_ifname(struct connman_provider *provider, const char *ifname) | |
| 160 { | |
| 161 struct vpn_data *data = connman_provider_get_data(provider); | |
| 162 int index; | |
| 163 | |
| 164 if (data == NULL) { | |
| 165 _DBG_VPN("%s: provider data not accessible", __func__); | |
| 166 return -EIO; | |
| 167 } | |
| 168 | |
| 169 if (ifname == NULL) { | |
| 170 _DBG_VPN("%s: ifname not provided", __func__); | |
| 171 return -EIO; | |
| 172 } | |
| 173 | |
| 174 index = connman_inet_ifindex(ifname); | |
| 175 if (index < 0) { | |
| 176 _DBG_VPN("%s: could not get ifindex from %s", __func__, ifname); | |
| 177 return -EIO; | |
| 178 } | |
| 179 | |
| 180 data->if_name = (char *)g_strdup(ifname); | |
| 181 connman_provider_set_index(provider, index); | |
| 182 | |
| 183 /* Set connect state to retry creating ipconfig with index above. */ | |
| 184 connman_provider_set_state(provider, CONNMAN_PROVIDER_STATE_CONNECT); | |
| 185 | |
| 186 return 0; | |
| 187 } | |
| 188 | |
| 147 static void vpn_newlink(const char *ifname, unsigned flags, unsigned change, void *user_data) | 189 static void vpn_newlink(const char *ifname, unsigned flags, unsigned change, void *user_data) |
| 148 { | 190 { |
| 149 struct connman_provider *provider = user_data; | 191 struct connman_provider *provider = user_data; |
| 150 struct vpn_data *data = connman_provider_get_data(provider); | 192 struct vpn_data *data = connman_provider_get_data(provider); |
| 151 | 193 |
| 152 if ((data->flags & IFF_UP) != (flags & IFF_UP)) { | 194 if ((data->flags & IFF_UP) != (flags & IFF_UP)) { |
| 153 if (flags & IFF_UP) { | 195 if (flags & IFF_UP) { |
| 154 data->state = VPN_STATE_READY; | 196 data->state = VPN_STATE_READY; |
| 155 connman_provider_set_state(provider, | 197 connman_provider_set_state(provider, |
| 156 CONNMAN_PROVIDER_STATE_READY); | 198 CONNMAN_PROVIDER_STATE_READY); |
| 157 } | 199 } |
| 158 } | 200 } |
| 159 data->flags = flags; | 201 data->flags = flags; |
| 160 } | 202 } |
| 161 | 203 |
| 162 static void vpn_notify(struct connman_task *task, | 204 static DBusMessage *vpn_notify(struct connman_task *task, |
|
thutt
2011/03/17 15:02:21
I don't understand why you changed this function t
kmixter1
2011/03/17 17:52:20
This is the default notification function if the p
| |
| 163 DBusMessage *msg, void *user_data) | 205 DBusMessage *msg, void *user_data) |
| 164 { | 206 { |
| 165 struct connman_provider *provider = user_data; | 207 struct connman_provider *provider = user_data; |
| 166 struct vpn_data *data; | 208 struct vpn_data *data; |
| 167 struct vpn_driver_data *vpn_driver_data; | 209 struct vpn_driver_data *vpn_driver_data; |
| 168 const char *name; | 210 const char *name; |
| 169 int state, index; | 211 int state, index; |
| 170 | 212 |
| 171 data = connman_provider_get_data(provider); | 213 data = connman_provider_get_data(provider); |
| 172 | 214 |
| 173 name = connman_provider_get_driver_name(provider); | 215 name = connman_provider_get_driver_name(provider); |
| 174 vpn_driver_data = g_hash_table_lookup(driver_hash, name); | 216 vpn_driver_data = g_hash_table_lookup(driver_hash, name); |
| 175 if (vpn_driver_data == NULL) | 217 if (vpn_driver_data == NULL) |
| 176 » » return; | 218 » » return NULL; |
| 177 | 219 |
| 178 state = vpn_driver_data->vpn_driver->notify(msg, provider); | 220 state = vpn_driver_data->vpn_driver->notify(msg, provider); |
| 179 switch (state) { | 221 switch (state) { |
| 180 case VPN_STATE_CONNECT: | 222 case VPN_STATE_CONNECT: |
| 181 case VPN_STATE_READY: | 223 case VPN_STATE_READY: |
| 182 index = connman_provider_get_index(provider); | 224 index = connman_provider_get_index(provider); |
| 183 data->watch = connman_rtnl_add_newlink_watch(index, | 225 data->watch = connman_rtnl_add_newlink_watch(index, |
| 184 vpn_newlink, provider); | 226 vpn_newlink, provider); |
| 185 connman_inet_ifup(index); | 227 connman_inet_ifup(index); |
| 186 break; | 228 break; |
| 187 | 229 |
| 188 case VPN_STATE_UNKNOWN: | 230 case VPN_STATE_UNKNOWN: |
| 189 case VPN_STATE_IDLE: | 231 case VPN_STATE_IDLE: |
| 190 case VPN_STATE_DISCONNECT: | 232 case VPN_STATE_DISCONNECT: |
| 191 case VPN_STATE_FAILURE: | 233 case VPN_STATE_FAILURE: |
| 192 connman_provider_set_state(provider, | 234 connman_provider_set_state(provider, |
| 193 CONNMAN_PROVIDER_STATE_DISCONNECT); | 235 CONNMAN_PROVIDER_STATE_DISCONNECT); |
| 194 break; | 236 break; |
| 195 } | 237 } |
| 238 | |
| 239 return NULL; | |
| 196 } | 240 } |
| 197 | 241 |
| 198 static int vpn_connect(struct connman_provider *provider) | 242 static int vpn_create_tun(struct connman_provider *provider) |
| 199 { | 243 { |
| 200 struct vpn_data *data = connman_provider_get_data(provider); | 244 struct vpn_data *data = connman_provider_get_data(provider); |
| 201 struct vpn_driver_data *vpn_driver_data; | |
| 202 struct ifreq ifr; | 245 struct ifreq ifr; |
| 203 const char *name; | |
| 204 int i, fd, index; | 246 int i, fd, index; |
| 205 int ret = 0; | 247 int ret = 0; |
| 206 | 248 |
| 207 » if (data != NULL) | 249 » if (data == NULL) |
|
thutt
2011/03/17 15:02:21
I'm not a networking person, but is this returning
kmixter1
2011/03/17 17:52:20
Done and added a message.
| |
| 208 return -EISCONN; | 250 return -EISCONN; |
| 209 | 251 |
| 210 data = g_try_new0(struct vpn_data, 1); | |
| 211 if (data == NULL) | |
| 212 return -ENOMEM; | |
| 213 | |
| 214 data->provider = connman_provider_ref(provider); | |
| 215 data->watch = 0; | |
| 216 data->flags = 0; | |
| 217 data->task = NULL; | |
| 218 data->state = VPN_STATE_IDLE; | |
| 219 | |
| 220 connman_provider_set_data(provider, data); | |
| 221 | |
| 222 name = connman_provider_get_driver_name(provider); | |
| 223 vpn_driver_data = g_hash_table_lookup(driver_hash, name); | |
| 224 | |
| 225 fd = open("/dev/net/tun", O_RDWR); | 252 fd = open("/dev/net/tun", O_RDWR); |
| 226 if (fd < 0) { | 253 if (fd < 0) { |
| 227 i = -errno; | 254 i = -errno; |
| 228 connman_error("%s: failed to open /dev/net/tun: %s", | 255 connman_error("%s: failed to open /dev/net/tun: %s", |
| 229 __func__, strerror(errno)); | 256 __func__, strerror(errno)); |
| 230 ret = i; | 257 ret = i; |
| 231 goto exist_err; | 258 goto exist_err; |
| 232 } | 259 } |
| 233 | 260 |
| 234 memset(&ifr, 0, sizeof(ifr)); | 261 memset(&ifr, 0, sizeof(ifr)); |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 262 close(fd); | 289 close(fd); |
| 263 ret = i; | 290 ret = i; |
| 264 goto exist_err; | 291 goto exist_err; |
| 265 } | 292 } |
| 266 | 293 |
| 267 close(fd); | 294 close(fd); |
| 268 | 295 |
| 269 index = connman_inet_ifindex(data->if_name); | 296 index = connman_inet_ifindex(data->if_name); |
| 270 if (index < 0) { | 297 if (index < 0) { |
| 271 connman_error("%s: failed to get tun ifindex", __func__); | 298 connman_error("%s: failed to get tun ifindex", __func__); |
| 272 » » kill_tun(data->if_name); | 299 » » kill_tun(provider); |
| 273 ret = -EIO; | 300 ret = -EIO; |
| 274 goto exist_err; | 301 goto exist_err; |
| 275 } | 302 } |
| 276 connman_provider_set_index(provider, index); | 303 connman_provider_set_index(provider, index); |
| 277 | 304 |
| 305 return 0; | |
| 306 | |
| 307 exist_err: | |
| 308 return ret; | |
| 309 } | |
| 310 | |
| 311 static int vpn_connect(struct connman_provider *provider) | |
| 312 { | |
| 313 struct vpn_data *data = connman_provider_get_data(provider); | |
| 314 struct vpn_driver_data *vpn_driver_data; | |
| 315 const char *name; | |
| 316 int ret = 0; | |
| 317 | |
| 318 if (data != NULL) { | |
|
thutt
2011/03/17 15:02:21
I'm puzzled by the condition -vs- the message here
kmixter1
2011/03/17 17:52:20
Updated the error message - I copy and pasted wron
| |
| 319 _DBG_VPN("%s: unable to access provider data", __func__); | |
| 320 return -EISCONN; | |
| 321 } | |
| 322 | |
| 323 data = g_try_new0(struct vpn_data, 1); | |
| 324 if (data == NULL) | |
| 325 return -ENOMEM; | |
| 326 | |
| 327 data->provider = connman_provider_ref(provider); | |
| 328 data->watch = 0; | |
| 329 data->flags = 0; | |
| 330 data->task = NULL; | |
| 331 data->state = VPN_STATE_IDLE; | |
| 332 | |
| 333 connman_provider_set_data(provider, data); | |
| 334 | |
| 335 name = connman_provider_get_driver_name(provider); | |
| 336 vpn_driver_data = g_hash_table_lookup(driver_hash, name); | |
| 337 | |
| 338 if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver != NULL && | |
| 339 vpn_driver_data->vpn_driver->flags != VPN_FLAG_NO_TUN) { | |
| 340 | |
| 341 ret = vpn_create_tun(provider); | |
| 342 if (ret < 0) | |
| 343 goto exist_err; | |
| 344 } | |
| 345 | |
| 278 data->task = connman_task_create(vpn_driver_data->program); | 346 data->task = connman_task_create(vpn_driver_data->program); |
| 279 | 347 |
| 280 if (data->task == NULL) { | 348 if (data->task == NULL) { |
| 281 ret = -ENOMEM; | 349 ret = -ENOMEM; |
| 282 » » kill_tun(data->if_name); | 350 » » kill_tun(provider); |
| 283 goto exist_err; | 351 goto exist_err; |
| 284 } | 352 } |
| 285 | 353 |
| 286 if (connman_task_set_notify(data->task, "notify", | 354 if (connman_task_set_notify(data->task, "notify", |
| 287 vpn_notify, provider)) { | 355 vpn_notify, provider)) { |
| 288 ret = -ENOMEM; | 356 ret = -ENOMEM; |
| 289 » » kill_tun(data->if_name); | 357 » » kill_tun(provider); |
| 290 connman_task_destroy(data->task); | 358 connman_task_destroy(data->task); |
| 291 data->task = NULL; | 359 data->task = NULL; |
| 292 goto exist_err; | 360 goto exist_err; |
| 293 } | 361 } |
| 294 | 362 |
| 295 ret = vpn_driver_data->vpn_driver->connect(provider, data->task, | 363 ret = vpn_driver_data->vpn_driver->connect(provider, data->task, |
| 296 data->if_name); | 364 data->if_name); |
| 297 if (ret < 0) { | 365 if (ret < 0) { |
| 298 » » kill_tun(data->if_name); | 366 » » kill_tun(provider); |
| 299 connman_task_destroy(data->task); | 367 connman_task_destroy(data->task); |
| 300 data->task = NULL; | 368 data->task = NULL; |
| 301 goto exist_err; | 369 goto exist_err; |
| 302 } | 370 } |
| 303 | 371 |
| 304 _DBG_VPN("%s started with dev %s", | 372 _DBG_VPN("%s started with dev %s", |
| 305 vpn_driver_data->provider_driver.name, data->if_name); | 373 vpn_driver_data->provider_driver.name, data->if_name); |
| 306 | 374 |
| 307 data->state = VPN_STATE_CONNECT; | 375 data->state = VPN_STATE_CONNECT; |
| 308 | 376 |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 348 connman_task_stop(data->task); | 416 connman_task_stop(data->task); |
| 349 | 417 |
| 350 return 0; | 418 return 0; |
| 351 } | 419 } |
| 352 | 420 |
| 353 static int vpn_remove(struct connman_provider *provider) | 421 static int vpn_remove(struct connman_provider *provider) |
| 354 { | 422 { |
| 355 struct vpn_data *data; | 423 struct vpn_data *data; |
| 356 | 424 |
| 357 data = connman_provider_get_data(provider); | 425 data = connman_provider_get_data(provider); |
| 358 connman_provider_set_data(provider, NULL); | |
| 359 if (data == NULL) | 426 if (data == NULL) |
| 360 return 0; | 427 return 0; |
| 361 | 428 |
| 362 if (data->watch != 0) | 429 if (data->watch != 0) |
| 363 connman_rtnl_remove_watch(data->watch); | 430 connman_rtnl_remove_watch(data->watch); |
| 364 data->watch = 0; | 431 data->watch = 0; |
| 365 connman_task_stop(data->task); | 432 connman_task_stop(data->task); |
| 366 | 433 |
| 367 g_usleep(G_USEC_PER_SEC); | 434 g_usleep(G_USEC_PER_SEC); |
| 368 » kill_tun(data->if_name); | 435 » kill_tun(provider); |
| 436 » connman_provider_set_data(provider, NULL); | |
| 369 return 0; | 437 return 0; |
| 370 } | 438 } |
| 371 | 439 |
| 372 int vpn_register(const char *name, struct vpn_driver *vpn_driver, | 440 int vpn_register(const char *name, struct vpn_driver *vpn_driver, |
| 373 const char *program) | 441 const char *program) |
| 374 { | 442 { |
| 375 struct vpn_driver_data *data; | 443 struct vpn_driver_data *data; |
| 376 | 444 |
| 377 _DBG_VPN("name %s program %s", name, program); | 445 _DBG_VPN("name %s program %s", name, program); |
| 378 | 446 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 412 if (data == NULL) | 480 if (data == NULL) |
| 413 return; | 481 return; |
| 414 | 482 |
| 415 connman_provider_driver_unregister(&data->provider_driver); | 483 connman_provider_driver_unregister(&data->provider_driver); |
| 416 | 484 |
| 417 g_hash_table_remove(driver_hash, name); | 485 g_hash_table_remove(driver_hash, name); |
| 418 | 486 |
| 419 if (g_hash_table_size(driver_hash) == 0) | 487 if (g_hash_table_size(driver_hash) == 0) |
| 420 g_hash_table_destroy(driver_hash); | 488 g_hash_table_destroy(driver_hash); |
| 421 } | 489 } |
| OLD | NEW |