service: Refactor the service unref function
[platform/upstream/connman.git] / src / tethering.c
index f7732c9..0164695 100644 (file)
@@ -24,6 +24,7 @@
 #include <config.h>
 #endif
 
+#include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
 #include <gdbus.h>
 
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
 #define BRIDGE_PROC_DIR "/proc/sys/net/bridge"
 
 #define BRIDGE_NAME "tether"
 #define PRIVATE_NETWORK_SECONDARY_DNS "8.8.4.4"
 
 static char *default_interface = NULL;
-static volatile gint tethering_enabled;
+static volatile int tethering_enabled;
 static GDHCPServer *tethering_dhcp_server = NULL;
 static DBusConnection *connection;
 static GHashTable *pn_hash;
 
 struct connman_private_network {
        char *owner;
+       char *path;
        guint watch;
        DBusMessage *msg;
        DBusMessage *reply;
@@ -200,14 +206,15 @@ static int create_bridge(const char *name)
 
        DBG("name %s", name);
 
-       sk = socket(AF_INET, SOCK_STREAM, 0);
+       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -EOPNOTSUPP;
 
-       err = ioctl(sk, SIOCBRADDBR, name);
-
-       if (err < 0)
-               return -EOPNOTSUPP;
+       if (ioctl(sk, SIOCBRADDBR, name) == -1) {
+               err = -errno;
+               if (err != -EEXIST)
+                       return -EOPNOTSUPP;
+       }
 
        err = set_forward_delay(name, 0);
 
@@ -225,7 +232,7 @@ static int remove_bridge(const char *name)
 
        DBG("name %s", name);
 
-       sk = socket(AF_INET, SOCK_STREAM, 0);
+       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -EOPNOTSUPP;
 
@@ -330,39 +337,44 @@ static void disable_nat(const char *interface)
 void __connman_tethering_set_enabled(void)
 {
        int err;
+       const char *dns;
 
        DBG("enabled %d", tethering_enabled + 1);
 
-       if (g_atomic_int_exchange_and_add(&tethering_enabled, 1) == 0) {
-               err = create_bridge(BRIDGE_NAME);
-               if (err < 0)
-                       return;
+       if (__sync_fetch_and_add(&tethering_enabled, 1) != 0)
+               return;
+
+       err = create_bridge(BRIDGE_NAME);
+       if (err < 0)
+               return;
 
-               err = enable_bridge(BRIDGE_NAME);
-               if (err < 0) {
-                       remove_bridge(BRIDGE_NAME);
-                       return;
-               }
+       err = enable_bridge(BRIDGE_NAME);
+       if (err < 0 && err != -EALREADY) {
+               remove_bridge(BRIDGE_NAME);
+               return;
+       }
 
-               if (__connman_dnsproxy_add_listener(BRIDGE_NAME) < 0)
-                       connman_error("Can't add listener %s to DNS proxy",
+       dns = BRIDGE_IP;
+       if (__connman_dnsproxy_add_listener(BRIDGE_NAME) < 0) {
+               connman_error("Can't add listener %s to DNS proxy",
                                                                BRIDGE_NAME);
+               dns = BRIDGE_DNS;
+       }
 
-               tethering_dhcp_server =
-                       dhcp_server_start(BRIDGE_NAME,
-                                               BRIDGE_IP, BRIDGE_SUBNET,
-                                               BRIDGE_IP_START, BRIDGE_IP_END,
-                                                       24 * 3600, BRIDGE_IP);
-               if (tethering_dhcp_server == NULL) {
-                       disable_bridge(BRIDGE_NAME);
-                       remove_bridge(BRIDGE_NAME);
-                       return;
-               }
+       tethering_dhcp_server =
+               dhcp_server_start(BRIDGE_NAME,
+                                       BRIDGE_IP, BRIDGE_SUBNET,
+                                       BRIDGE_IP_START, BRIDGE_IP_END,
+                                       24 * 3600, dns);
+       if (tethering_dhcp_server == NULL) {
+               disable_bridge(BRIDGE_NAME);
+               remove_bridge(BRIDGE_NAME);
+               return;
+       }
 
-               enable_nat(default_interface);
+       enable_nat(default_interface);
 
-               DBG("tethering started");
-       }
+       DBG("tethering started");
 }
 
 void __connman_tethering_set_disabled(void)
@@ -371,17 +383,20 @@ void __connman_tethering_set_disabled(void)
 
        __connman_dnsproxy_remove_listener(BRIDGE_NAME);
 
-       if (g_atomic_int_dec_and_test(&tethering_enabled) == TRUE) {
-               disable_nat(default_interface);
+       if (__sync_fetch_and_sub(&tethering_enabled, 1) != 1)
+               return;
 
-               dhcp_server_stop(tethering_dhcp_server);
+       disable_nat(default_interface);
 
-               disable_bridge(BRIDGE_NAME);
+       dhcp_server_stop(tethering_dhcp_server);
 
-               remove_bridge(BRIDGE_NAME);
+       tethering_dhcp_server = NULL;
 
-               DBG("tethering stopped");
-       }
+       disable_bridge(BRIDGE_NAME);
+
+       remove_bridge(BRIDGE_NAME);
+
+       DBG("tethering stopped");
 }
 
 void __connman_tethering_update_interface(const char *interface)
@@ -399,7 +414,8 @@ void __connman_tethering_update_interface(const char *interface)
 
        default_interface = g_strdup(interface);
 
-       if (!g_atomic_int_get(&tethering_enabled))
+       __sync_synchronize();
+       if (tethering_enabled == 0)
                return;
 
        enable_nat(interface);
@@ -410,7 +426,6 @@ static void setup_tun_interface(unsigned int flags, unsigned change,
 {
        struct connman_private_network *pn = data;
        unsigned char prefixlen;
-       DBusMessage *reply;
        DBusMessageIter array, dict;
        int err;
 
@@ -438,56 +453,54 @@ static void setup_tun_interface(unsigned int flags, unsigned change,
                goto error;
        }
 
-       reply = dbus_message_new_method_return(pn->msg);
-
-       if (reply == NULL)
-               goto error;
-
-       dbus_message_iter_init_append(reply, &array);
+       dbus_message_iter_init_append(pn->reply, &array);
 
-       dbus_message_iter_append_basic(&array, DBUS_TYPE_UNIX_FD, &pn->fd);
+       dbus_message_iter_append_basic(&array, DBUS_TYPE_OBJECT_PATH,
+                                               &pn->path);
 
        connman_dbus_dict_open(&array, &dict);
 
        connman_dbus_dict_append_basic(&dict, "ServerIPv4",
-                                               DBUS_TYPE_STRING, &pn->server_ip);
+                                       DBUS_TYPE_STRING, &pn->server_ip);
        connman_dbus_dict_append_basic(&dict, "PeerIPv4",
-                                               DBUS_TYPE_STRING, &pn->peer_ip);
+                                       DBUS_TYPE_STRING, &pn->peer_ip);
        connman_dbus_dict_append_basic(&dict, "PrimaryDNS",
-                                               DBUS_TYPE_STRING, &pn->primary_dns);
+                                       DBUS_TYPE_STRING, &pn->primary_dns);
        connman_dbus_dict_append_basic(&dict, "SecondaryDNS",
-                                               DBUS_TYPE_STRING, &pn->secondary_dns);
+                                       DBUS_TYPE_STRING, &pn->secondary_dns);
 
        connman_dbus_dict_close(&array, &dict);
 
-       g_dbus_send_message(connection, reply);
+       dbus_message_iter_append_basic(&array, DBUS_TYPE_UNIX_FD, &pn->fd);
+
+       g_dbus_send_message(connection, pn->reply);
 
        return;
 
 error:
-       reply = __connman_error_failed(pn->msg, -err);
-       g_dbus_send_message(connection, reply);
+       pn->reply = __connman_error_failed(pn->msg, -err);
+       g_dbus_send_message(connection, pn->reply);
 
-       g_hash_table_remove(pn_hash, pn->owner);
+       g_hash_table_remove(pn_hash, pn->path);
 }
 
 static void remove_private_network(gpointer user_data)
 {
        struct connman_private_network *pn = user_data;
 
-       close(pn->fd);
-
-       connman_rtnl_remove_watch(pn->iface_watch);
-
        disable_nat(default_interface);
+       connman_rtnl_remove_watch(pn->iface_watch);
 
        if (pn->watch > 0) {
                g_dbus_remove_watch(connection, pn->watch);
                pn->watch = 0;
        }
 
+       close(pn->fd);
+
        g_free(pn->interface);
        g_free(pn->owner);
+       g_free(pn->path);
        g_free(pn);
 }
 
@@ -499,29 +512,39 @@ static void owner_disconnect(DBusConnection *connection, void *user_data)
 
        pn->watch = 0;
 
-       g_hash_table_remove(pn_hash, pn->owner);
+       g_hash_table_remove(pn_hash, pn->path);
 }
 
 int __connman_private_network_request(DBusMessage *msg, const char *owner)
 {
        struct connman_private_network *pn;
        char *iface = NULL;
+       char *path = NULL;
        int index, fd, err;
 
-       pn = g_hash_table_lookup(pn_hash, owner);
-       if (pn != NULL)
-               return -EEXIST;
+       if (DBUS_TYPE_UNIX_FD < 0)
+               return -EINVAL;
 
        fd = connman_inet_create_tunnel(&iface);
        if (fd < 0)
                return fd;
 
+       path = g_strdup_printf("/tethering/%s", iface);
+
+       pn = g_hash_table_lookup(pn_hash, path);
+       if (pn) {
+               g_free(path);
+               g_free(iface);
+               close(fd);
+               return -EEXIST;
+       }
+
        index = connman_inet_ifindex(iface);
        if (index < 0) {
                err = -ENODEV;
                goto error;
        }
-       DBG("inteface %s", iface);
+       DBG("interface %s", iface);
 
        err = connman_inet_set_mtu(index, DEFAULT_MTU);
 
@@ -532,9 +555,14 @@ int __connman_private_network_request(DBusMessage *msg, const char *owner)
        }
 
        pn->owner = g_strdup(owner);
+       pn->path = path;
        pn->watch = g_dbus_add_disconnect_watch(connection, pn->owner,
                                        owner_disconnect, pn, NULL);
        pn->msg = msg;
+       pn->reply = dbus_message_new_method_return(pn->msg);
+       if (pn->reply == NULL)
+               goto error;
+
        pn->fd = fd;
        pn->interface = iface;
        pn->index = index;
@@ -546,25 +574,27 @@ int __connman_private_network_request(DBusMessage *msg, const char *owner)
        pn->iface_watch = connman_rtnl_add_newlink_watch(index,
                                                setup_tun_interface, pn);
 
-       g_hash_table_insert(pn_hash, pn->owner, pn);
+       g_hash_table_insert(pn_hash, pn->path, pn);
 
        return 0;
 
 error:
        close(fd);
        g_free(iface);
+       g_free(path);
+       g_free(pn);
        return err;
 }
 
-int __connman_private_network_release(const char *owner)
+int __connman_private_network_release(const char *path)
 {
        struct connman_private_network *pn;
 
-       pn = g_hash_table_lookup(pn_hash, owner);
+       pn = g_hash_table_lookup(pn_hash, path);
        if (pn == NULL)
                return -EACCES;
 
-       g_hash_table_remove(pn_hash, owner);
+       g_hash_table_remove(pn_hash, path);
        return 0;
 }
 
@@ -588,7 +618,8 @@ void __connman_tethering_cleanup(void)
 {
        DBG("");
 
-       if (g_atomic_int_get(&tethering_enabled)) {
+       __sync_synchronize();
+       if (tethering_enabled == 0) {
                if (tethering_dhcp_server)
                        dhcp_server_stop(tethering_dhcp_server);
                disable_bridge(BRIDGE_NAME);