drbd: conn_khelper() for user mode callbacks for connections
authorPhilipp Reisner <philipp.reisner@linbit.com>
Wed, 16 Mar 2011 16:39:12 +0000 (17:39 +0100)
committerPhilipp Reisner <philipp.reisner@linbit.com>
Sat, 3 Nov 2012 23:16:32 +0000 (00:16 +0100)
Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
drivers/block/drbd/drbd_nl.c

index daf388f..ac0a175 100644 (file)
@@ -252,43 +252,47 @@ static int drbd_adm_finish(struct genl_info *info, int retcode)
        return 0;
 }
 
-int drbd_khelper(struct drbd_conf *mdev, char *cmd)
+static void setup_khelper_env(struct drbd_tconn *tconn, char **envp)
 {
-       char *envp[] = { "HOME=/",
-                       "TERM=linux",
-                       "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
-                       NULL, /* Will be set to address family */
-                       NULL, /* Will be set to address */
-                       NULL };
-       char mb[12], af[20], ad[60], *afs;
-       char *argv[] = {usermode_helper, cmd, mb, NULL };
-       struct sib_info sib;
-       int ret;
-
-       snprintf(mb, 12, "minor-%d", mdev_to_minor(mdev));
+       char *afs;
 
-       if (get_net_conf(mdev->tconn)) {
-               switch (((struct sockaddr *)mdev->tconn->net_conf->peer_addr)->sa_family) {
+       if (get_net_conf(tconn)) {
+               switch (((struct sockaddr *)tconn->net_conf->peer_addr)->sa_family) {
                case AF_INET6:
                        afs = "ipv6";
-                       snprintf(ad, 60, "DRBD_PEER_ADDRESS=%pI6",
-                                &((struct sockaddr_in6 *)mdev->tconn->net_conf->peer_addr)->sin6_addr);
+                       snprintf(envp[4], 60, "DRBD_PEER_ADDRESS=%pI6",
+                                &((struct sockaddr_in6 *)tconn->net_conf->peer_addr)->sin6_addr);
                        break;
                case AF_INET:
                        afs = "ipv4";
-                       snprintf(ad, 60, "DRBD_PEER_ADDRESS=%pI4",
-                                &((struct sockaddr_in *)mdev->tconn->net_conf->peer_addr)->sin_addr);
+                       snprintf(envp[4], 60, "DRBD_PEER_ADDRESS=%pI4",
+                                &((struct sockaddr_in *)tconn->net_conf->peer_addr)->sin_addr);
                        break;
                default:
                        afs = "ssocks";
-                       snprintf(ad, 60, "DRBD_PEER_ADDRESS=%pI4",
-                                &((struct sockaddr_in *)mdev->tconn->net_conf->peer_addr)->sin_addr);
+                       snprintf(envp[4], 60, "DRBD_PEER_ADDRESS=%pI4",
+                                &((struct sockaddr_in *)tconn->net_conf->peer_addr)->sin_addr);
                }
-               snprintf(af, 20, "DRBD_PEER_AF=%s", afs);
-               envp[3]=af;
-               envp[4]=ad;
-               put_net_conf(mdev->tconn);
+               snprintf(envp[3], 20, "DRBD_PEER_AF=%s", afs);
+               put_net_conf(tconn);
        }
+}
+
+int drbd_khelper(struct drbd_conf *mdev, char *cmd)
+{
+       char *envp[] = { "HOME=/",
+                       "TERM=linux",
+                       "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
+                        (char[20]) { }, /* address family */
+                        (char[60]) { }, /* address */
+                       NULL };
+       char mb[12];
+       char *argv[] = {usermode_helper, cmd, mb, NULL };
+       struct sib_info sib;
+       int ret;
+
+       snprintf(mb, 12, "minor-%d", mdev_to_minor(mdev));
+       setup_khelper_env(mdev->tconn, envp);
 
        /* The helper may take some time.
         * write out any unsynced meta data changes now */
@@ -317,6 +321,49 @@ int drbd_khelper(struct drbd_conf *mdev, char *cmd)
        return ret;
 }
 
+static void conn_md_sync(struct drbd_tconn *tconn)
+{
+       struct drbd_conf *mdev;
+       int minor;
+
+       idr_for_each_entry(&tconn->volumes, mdev, minor)
+               drbd_md_sync(mdev);
+}
+
+int conn_khelper(struct drbd_tconn *tconn, char *cmd)
+{
+       char *envp[] = { "HOME=/",
+                       "TERM=linux",
+                       "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
+                        (char[20]) { }, /* address family */
+                        (char[60]) { }, /* address */
+                       NULL };
+       char *argv[] = {usermode_helper, cmd, tconn->name, NULL };
+       int ret;
+
+       setup_khelper_env(tconn, envp);
+       conn_md_sync(tconn);
+
+       conn_info(tconn, "helper command: %s %s %s\n", usermode_helper, cmd, tconn->name);
+       /* TODO: conn_bcast_event() ?? */
+
+       ret = call_usermodehelper(usermode_helper, argv, envp, 1);
+       if (ret)
+               conn_warn(tconn, "helper command: %s %s %s exit code %u (0x%x)\n",
+                         usermode_helper, cmd, tconn->name,
+                         (ret >> 8) & 0xff, ret);
+       else
+               conn_info(tconn, "helper command: %s %s %s exit code %u (0x%x)\n",
+                         usermode_helper, cmd, tconn->name,
+                         (ret >> 8) & 0xff, ret);
+       /* TODO: conn_bcast_event() ?? */
+
+       if (ret < 0) /* Ignore any ERRNOs we got. */
+               ret = 0;
+
+       return ret;
+}
+
 enum drbd_disk_state drbd_try_outdate_peer(struct drbd_conf *mdev)
 {
        char *ex_to_string;