github.com/tompreston/snapd@v0.0.0-20210817193607-954edfcb9611/cmd/snap-device-helper/snap-device-helper-test.c (about)

     1  /*
     2   * Copyright (C) 2021 Canonical Ltd
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License version 3 as
     6   * published by the Free Software Foundation.
     7   *
     8   * This program is distributed in the hope that it will be useful,
     9   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11   * GNU General Public License for more details.
    12   *
    13   * You should have received a copy of the GNU General Public License
    14   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15   *
    16   */
    17  
    18  #include "../libsnap-confine-private/test-utils.h"
    19  
    20  #include <fcntl.h>
    21  #include <glib.h>
    22  #include <glib/gstdio.h>
    23  #include <string.h>
    24  #include <sys/stat.h>
    25  #include <sys/types.h>
    26  #include <sys/wait.h>
    27  #include <unistd.h>
    28  
    29  #include "snap-device-helper.c"
    30  
    31  #include "../libsnap-confine-private/device-cgroup-support.h"
    32  
    33  typedef struct _sdh_test_fixture {
    34      char *sysroot;
    35  } sdh_test_fixture;
    36  
    37  static void mkdir_in_sysroot(sdh_test_fixture *fixture, const char *path) {
    38      char *p = g_build_filename(fixture->sysroot, path, NULL);
    39      g_assert(g_mkdir_with_parents(p, 0755) == 0);
    40      g_free(p);
    41  }
    42  
    43  static void symlink_in_sysroot(sdh_test_fixture *fixture, const char *from, const char *to) {
    44      g_debug("mock symlink from %s to %s", from, to);
    45      char *pfrom = g_build_filename(fixture->sysroot, from, NULL);
    46      g_assert(g_path_is_absolute(to) == FALSE);
    47      g_assert_cmpint(symlink(to, pfrom), ==, 0);
    48      g_free(pfrom);
    49  }
    50  
    51  static void sdh_test_set_up(sdh_test_fixture *fixture, gconstpointer user_data) {
    52      gchar *mock_dir = g_dir_make_tmp(NULL, NULL);
    53      g_assert_nonnull(mock_dir);
    54  
    55      fixture->sysroot = mock_dir;
    56      sysroot = mock_dir;
    57  
    58      char *sys_devices = g_build_filename(fixture->sysroot, "sys", "devices", NULL);
    59      g_assert(g_mkdir_with_parents(sys_devices, 0755) == 0);
    60      g_free(sys_devices);
    61      char *sys_class_block = g_build_filename(fixture->sysroot, "sys", "class", "block", NULL);
    62      g_assert(g_mkdir_with_parents(sys_class_block, 0755) == 0);
    63      g_free(sys_class_block);
    64      char *sys_class_other = g_build_filename(fixture->sysroot, "sys", "class", "other", NULL);
    65      g_assert(g_mkdir_with_parents(sys_class_other, 0755) == 0);
    66      g_free(sys_class_other);
    67  
    68      g_debug("mock sysroot dir: %s", mock_dir);
    69  }
    70  
    71  static void mocks_reset(void);
    72  
    73  static void sdh_test_tear_down(sdh_test_fixture *fixture, gconstpointer user_data) {
    74      sysroot = "";
    75      if (!g_strcmp0(sysroot, "/")) {
    76          rm_rf_tmp(fixture->sysroot);
    77      }
    78      mocks_reset();
    79      g_free(fixture->sysroot);
    80  }
    81  
    82  static struct mocks {
    83      size_t cgorup_new_calls;
    84      void *new_ret;
    85      char *new_tag;
    86      int new_flags;
    87  
    88      size_t cgroup_allow_calls;
    89      size_t cgroup_deny_calls;
    90      int device_type;
    91      int device_major;
    92      int device_minor;
    93      int device_ret;
    94  
    95  } mocks;
    96  
    97  static void mocks_reset(void) {
    98      if (mocks.new_tag != NULL) {
    99          g_free(mocks.new_tag);
   100      }
   101      memset(&mocks, 0, sizeof(mocks));
   102  }
   103  
   104  /* mocked in test */
   105  sc_device_cgroup *sc_device_cgroup_new(const char *security_tag, int flags) {
   106      g_debug("cgroup new called");
   107      mocks.cgorup_new_calls++;
   108      mocks.new_tag = g_strdup(security_tag);
   109      mocks.new_flags = flags;
   110      return (sc_device_cgroup *)mocks.new_ret;
   111  }
   112  
   113  int sc_device_cgroup_allow(sc_device_cgroup *self, int kind, int major, int minor) {
   114      mocks.cgroup_allow_calls++;
   115      mocks.device_type = kind;
   116      mocks.device_major = major;
   117      mocks.device_minor = minor;
   118      return 0;
   119  }
   120  
   121  int sc_device_cgroup_deny(sc_device_cgroup *self, int kind, int major, int minor) {
   122      mocks.cgroup_deny_calls++;
   123      mocks.device_type = kind;
   124      mocks.device_major = major;
   125      mocks.device_minor = minor;
   126      return 0;
   127  }
   128  
   129  struct sdh_test_data {
   130      char *action;
   131      // snap.foo.bar
   132      char *app;
   133      // snap_foo_bar
   134      char *mangled_appname;
   135  };
   136  
   137  static void test_sdh_action(sdh_test_fixture *fixture, gconstpointer test_data) {
   138      struct sdh_test_data *td = (struct sdh_test_data *)test_data;
   139  
   140      struct sdh_invocation inv_block = {
   141          .action = td->action,
   142          .tagname = td->mangled_appname,
   143          .devpath = "/devices/foo/block/sda/sda4",
   144          .majmin = "8:4",
   145      };
   146  
   147      mkdir_in_sysroot(fixture, "/sys/devices/foo/block/sda/sda4");
   148      symlink_in_sysroot(fixture, "/sys/devices/foo/block/sda/sda4/subsystem", "../../../../../class/block");
   149  
   150      int bogus = 0;
   151      /* make cgroup_device_new return a non-NULL */
   152      mocks.new_ret = &bogus;
   153  
   154      int ret = snap_device_helper_run(&inv_block);
   155      g_assert_cmpint(ret, ==, 0);
   156      g_assert_cmpint(mocks.cgorup_new_calls, ==, 1);
   157      if (g_strcmp0(td->action, "add") == 0 || g_strcmp0(td->action, "change") == 0) {
   158          g_assert_cmpint(mocks.cgroup_allow_calls, ==, 1);
   159          g_assert_cmpint(mocks.cgroup_deny_calls, ==, 0);
   160      } else if (g_strcmp0(td->action, "remove") == 0) {
   161          g_assert_cmpint(mocks.cgroup_allow_calls, ==, 0);
   162          g_assert_cmpint(mocks.cgroup_deny_calls, ==, 1);
   163      }
   164      g_assert_cmpint(mocks.device_major, ==, 8);
   165      g_assert_cmpint(mocks.device_minor, ==, 4);
   166      g_assert_cmpint(mocks.device_type, ==, S_IFBLK);
   167      g_assert_nonnull(mocks.new_tag);
   168      g_assert_nonnull(td->app);
   169      g_assert_cmpstr(mocks.new_tag, ==, td->app);
   170      g_assert_cmpint(mocks.new_flags, !=, 0);
   171      g_assert_cmpint(mocks.new_flags, ==, SC_DEVICE_CGROUP_FROM_EXISTING);
   172  
   173      g_debug("reset");
   174      mocks_reset();
   175      mocks.new_ret = &bogus;
   176  
   177      struct sdh_invocation inv_serial = {
   178          .action = td->action,
   179          .tagname = td->mangled_appname,
   180          .devpath = "/devices/foo/tty/ttyS0",
   181          .majmin = "6:64",
   182      };
   183      mkdir_in_sysroot(fixture, "/sys/devices/foo/tty/ttyS0");
   184      symlink_in_sysroot(fixture, "/sys/devices/foo/tty/ttyS0/subsystem", "../../../../class/other");
   185      ret = snap_device_helper_run(&inv_serial);
   186      g_assert_cmpint(ret, ==, 0);
   187      g_assert_cmpint(mocks.cgorup_new_calls, ==, 1);
   188      if (g_strcmp0(td->action, "add") == 0 || g_strcmp0(td->action, "change") == 0) {
   189          g_assert_cmpint(mocks.cgroup_allow_calls, ==, 1);
   190          g_assert_cmpint(mocks.cgroup_deny_calls, ==, 0);
   191      } else if (g_strcmp0(td->action, "remove") == 0) {
   192          g_assert_cmpint(mocks.cgroup_allow_calls, ==, 0);
   193          g_assert_cmpint(mocks.cgroup_deny_calls, ==, 1);
   194      }
   195      g_assert_cmpint(mocks.device_major, ==, 6);
   196      g_assert_cmpint(mocks.device_minor, ==, 64);
   197      g_assert_cmpint(mocks.device_type, ==, S_IFCHR);
   198      g_assert_nonnull(mocks.new_tag);
   199      g_assert_nonnull(td->app);
   200      g_assert_cmpstr(mocks.new_tag, ==, td->app);
   201      g_assert_cmpint(mocks.new_flags, !=, 0);
   202      g_assert_cmpint(mocks.new_flags, ==, SC_DEVICE_CGROUP_FROM_EXISTING);
   203  }
   204  
   205  static void test_sdh_action_nvme(sdh_test_fixture *fixture, gconstpointer test_data) {
   206      /* hierarchy from an actual system with a nvme disk */
   207      mkdir_in_sysroot(fixture, "/sys/devices/pci0000:00/0000:00:01.1/0000:01:00.0/nvme/nvme0/nvme0n1");
   208      mkdir_in_sysroot(fixture, "/sys/devices/pci0000:00/0000:00:01.1/0000:01:00.0/nvme/nvme0/nvme0n1p1");
   209      mkdir_in_sysroot(fixture, "/sys/devices/pci0000:00/0000:00:01.1/0000:01:00.0/nvme/nvme0/ng0n1");
   210      mkdir_in_sysroot(fixture, "/sys/devices/pci0000:00/0000:00:01.1/0000:01:00.0/nvme/nvme0/hwmon0");
   211      symlink_in_sysroot(fixture, "/sys//devices/pci0000:00/0000:00:01.1/0000:01:00.0/nvme/nvme0/nvme0n1/subsystem",
   212                         "../../../../../../../class/block");
   213      symlink_in_sysroot(fixture, "/sys//devices/pci0000:00/0000:00:01.1/0000:01:00.0/nvme/nvme0/nvme0n1p1/subsystem",
   214                         "../../../../../../../class/block");
   215      symlink_in_sysroot(fixture, "/sys//devices/pci0000:00/0000:00:01.1/0000:01:00.0/nvme/nvme0/subsystem",
   216                         "../../../../../../class/nvme");
   217      symlink_in_sysroot(fixture, "/sys//devices/pci0000:00/0000:00:01.1/0000:01:00.0/nvme/nvme0/ng0n1/subsystem",
   218                         "../../../../../../class/nvme-generic");
   219      symlink_in_sysroot(fixture, "/sys//devices/pci0000:00/0000:00:01.1/0000:01:00.0/nvme/nvme0/hwmon0/subsystem",
   220                         "../../../../../../class/hwmon");
   221  
   222      struct {
   223          const char *dev;
   224          const char *majmin;
   225          int expected_maj;
   226          int expected_min;
   227          int expected_type;
   228      } tcs[] = {
   229          {
   230              .dev = "/devices/pci0000:00/0000:00:01.1/0000:01:00.0/nvme/nvme0/nvme0n1",
   231              .majmin = "259:0",
   232              .expected_maj = 259,
   233              .expected_min = 0,
   234              .expected_type = S_IFBLK,
   235          },
   236          {
   237              .dev = "/devices/pci0000:00/0000:00:01.1/0000:01:00.0/nvme/nvme0/nvme0n1p1",
   238              .majmin = "259:1",
   239              .expected_maj = 259,
   240              .expected_min = 1,
   241              .expected_type = S_IFBLK,
   242          },
   243          {
   244              .dev = "/devices/pci0000:00/0000:00:01.1/0000:01:00.0/nvme/nvme0",
   245              .majmin = "242:0",
   246              .expected_maj = 242,
   247              .expected_min = 0,
   248              .expected_type = S_IFCHR,
   249          },
   250          {
   251              .dev = "/devices/pci0000:00/0000:00:01.1/0000:01:00.0/nvme/nvme0/hwmon0",
   252              .majmin = "241:0",
   253              .expected_maj = 241,
   254              .expected_min = 0,
   255              .expected_type = S_IFCHR,
   256          },
   257      };
   258  
   259      int bogus = 0;
   260  
   261      for (size_t i = 0; i < sizeof(tcs) / sizeof(tcs[0]); i++) {
   262          mocks_reset();
   263          /* make cgroup_device_new return a non-NULL */
   264          mocks.new_ret = &bogus;
   265  
   266          struct sdh_invocation inv_block = {
   267              .action = "add",
   268              .tagname = "snap_foo_bar",
   269              .devpath = tcs[i].dev,
   270              .majmin = tcs[i].majmin,
   271          };
   272          int ret = snap_device_helper_run(&inv_block);
   273          g_assert_cmpint(ret, ==, 0);
   274          g_assert_cmpint(mocks.cgorup_new_calls, ==, 1);
   275          g_assert_cmpint(mocks.cgroup_allow_calls, ==, 1);
   276          g_assert_cmpint(mocks.cgroup_deny_calls, ==, 0);
   277          g_assert_cmpint(mocks.device_major, ==, tcs[i].expected_maj);
   278          g_assert_cmpint(mocks.device_minor, ==, tcs[i].expected_min);
   279          g_assert_cmpint(mocks.device_type, ==, tcs[i].expected_type);
   280          g_assert_cmpint(mocks.new_flags, !=, 0);
   281          g_assert_cmpint(mocks.new_flags, ==, SC_DEVICE_CGROUP_FROM_EXISTING);
   282      }
   283  }
   284  
   285  static void run_sdh_die(const char *action, const char *tagname, const char *devpath, const char *majmin,
   286                          const char *msg) {
   287      struct sdh_invocation inv = {
   288          .action = action,
   289          .tagname = tagname,
   290          .devpath = devpath,
   291          .majmin = majmin,
   292      };
   293      if (g_test_subprocess()) {
   294          errno = 0;
   295          snap_device_helper_run(&inv);
   296      }
   297      g_test_trap_subprocess(NULL, 0, 0);
   298      g_test_trap_assert_failed();
   299      g_test_trap_assert_stderr(msg);
   300  }
   301  
   302  static void test_sdh_err_noappname(sdh_test_fixture *fixture, gconstpointer test_data) {
   303      // missing appname
   304      run_sdh_die("add", "", "/devices/foo/block/sda/sda4", "8:4", "malformed tag \"\"\n");
   305  }
   306  
   307  static void test_sdh_err_badappname(sdh_test_fixture *fixture, gconstpointer test_data) {
   308      // malformed appname
   309      run_sdh_die("add", "foo_bar", "/devices/foo/block/sda/sda4", "8:4", "malformed tag \"foo_bar\"\n");
   310  }
   311  static void test_sdh_err_nodevpath(sdh_test_fixture *fixture, gconstpointer test_data) {
   312      // missing devpath
   313      run_sdh_die("add", "snap_foo_bar", "", "8:4", "no or malformed devpath \"\"\n");
   314  }
   315  
   316  static void test_sdh_err_wrongdevmajorminor1(sdh_test_fixture *fixture, gconstpointer test_data) {
   317      // missing device major:minor numbers
   318      run_sdh_die("add", "snap_foo_bar", "/devices/foo/block/sda/sda4", "", "no or malformed major/minor \"\"\n");
   319  }
   320  
   321  static void test_sdh_err_wrongdevmajorminor2(sdh_test_fixture *fixture, gconstpointer test_data) {
   322      // too short major:minor numbers
   323      run_sdh_die("add", "snap_foo_bar", "/devices/foo/block/sda/sda4", "8", "no or malformed major/minor \"8\"\n");
   324  }
   325  
   326  static void test_sdh_err_wrongdevmajorminor_late1(sdh_test_fixture *fixture, gconstpointer test_data) {
   327      // mock enough to the major:minor extraction in the code
   328      mkdir_in_sysroot(fixture, "/sys/devices/foo/block/sda/sda4");
   329      symlink_in_sysroot(fixture, "/sys/devices/foo/block/sda/sda4/subsystem", "../../../../../class/block");
   330  
   331      // ensure mocked sc_device_cgroup_new() returns non-NULL
   332      int bogus = 0;
   333      mocks.new_ret = &bogus;
   334  
   335      // missing ":"
   336      run_sdh_die("add", "snap_foo_bar", "/devices/foo/block/sda/sda4", "100", "malformed major:minor string: 100\n");
   337  }
   338  
   339  static void test_sdh_err_wrongdevmajorminor_late2(sdh_test_fixture *fixture, gconstpointer test_data) {
   340      // mock enough to the major:minor extraction in the code
   341      mkdir_in_sysroot(fixture, "/sys/devices/foo/block/sda/sda4");
   342      symlink_in_sysroot(fixture, "/sys/devices/foo/block/sda/sda4/subsystem", "../../../../../class/block");
   343  
   344      // ensure mocked sc_device_cgroup_new() returns non-NULL
   345      int bogus = 0;
   346      mocks.new_ret = &bogus;
   347  
   348      // missing part after ":"
   349      run_sdh_die("add", "snap_foo_bar", "/devices/foo/block/sda/sda4", "88:", "malformed major:minor string: 88:\n");
   350  }
   351  
   352  static void test_sdh_err_badaction(sdh_test_fixture *fixture, gconstpointer test_data) {
   353      // bogus action
   354      run_sdh_die("badaction", "snap_foo_bar", "/devices/foo/block/sda/sda4", "8:4",
   355                  "ERROR: unknown action \"badaction\"\n");
   356  }
   357  
   358  static void test_sdh_err_nosymlink(sdh_test_fixture *fixture, gconstpointer test_data) {
   359      // missing symlink
   360      run_sdh_die("add", "snap_foo_bar", "/devices/foo/block/sda/sda4", "8:4",
   361                  "cannot read symlink */sys//devices/foo/block/sda/sda4/subsystem*\n");
   362  }
   363  
   364  static void test_sdh_err_funtag1(sdh_test_fixture *fixture, gconstpointer test_data) {
   365      run_sdh_die("add", "snap___bar", "/devices/foo/block/sda/sda4", "8:4",
   366                  "security tag \"snap._.bar\" for snap \"_\" is not valid\n");
   367  }
   368  
   369  static void test_sdh_err_funtag2(sdh_test_fixture *fixture, gconstpointer test_data) {
   370      run_sdh_die("add", "snap_foobar", "/devices/foo/block/sda/sda4", "8:4",
   371                  "missing app name in tag \"snap_foobar\"\n");
   372  }
   373  
   374  static void test_sdh_err_funtag3(sdh_test_fixture *fixture, gconstpointer test_data) {
   375      run_sdh_die("add", "snap_", "/devices/foo/block/sda/sda4", "8:4", "tag \"snap_\" length 5 is incorrect\n");
   376  }
   377  
   378  static void test_sdh_err_funtag4(sdh_test_fixture *fixture, gconstpointer test_data) {
   379      run_sdh_die("add", "snap_foo_", "/devices/foo/block/sda/sda4", "8:4",
   380                  "security tag \"snap.foo.\" for snap \"foo\" is not valid\n");
   381  }
   382  
   383  static void test_sdh_err_funtag5(sdh_test_fixture *fixture, gconstpointer test_data) {
   384      run_sdh_die(
   385          "add", "snap_thisisverylonginstancenameabovelengthlimit_instancekey_bar", "/devices/foo/block/sda/sda4", "8:4",
   386          "snap instance of tag \"snap_thisisverylonginstancenameabovelengthlimit_instancekey_bar\" is too long\n");
   387  }
   388  
   389  static void test_sdh_err_funtag6(sdh_test_fixture *fixture, gconstpointer test_data) {
   390      run_sdh_die("add", "snap__barbar", "/devices/foo/block/sda/sda4", "8:4",
   391                  "missing snap name in tag \"snap__barbar\"\n");
   392  }
   393  
   394  static void test_sdh_err_funtag7(sdh_test_fixture *fixture, gconstpointer test_data) {
   395      run_sdh_die("add", "snap_barbarbarbar", "/devices/foo/block/sda/sda4", "8:4",
   396                  "missing app name in tag \"snap_barbarbarbar\"\n");
   397  }
   398  
   399  static void test_sdh_err_funtag8(sdh_test_fixture *fixture, gconstpointer test_data) {
   400      run_sdh_die("add", "snap_#_barbar", "/devices/foo/block/sda/sda4", "8:4",
   401                  "security tag \"snap.#.barbar\" for snap \"#\" is not valid\n");
   402  }
   403  
   404  static struct sdh_test_data add_data = {"add", "snap.foo.bar", "snap_foo_bar"};
   405  static struct sdh_test_data change_data = {"change", "snap.foo.bar", "snap_foo_bar"};
   406  
   407  static struct sdh_test_data remove_data = {"remove", "snap.foo.bar", "snap_foo_bar"};
   408  
   409  static struct sdh_test_data instance_add_data = {"add", "snap.foo_bar.baz", "snap_foo_bar_baz"};
   410  
   411  static struct sdh_test_data instance_change_data = {"change", "snap.foo_bar.baz", "snap_foo_bar_baz"};
   412  
   413  static struct sdh_test_data instance_remove_data = {"remove", "snap.foo_bar.baz", "snap_foo_bar_baz"};
   414  
   415  static struct sdh_test_data add_hook_data = {"add", "snap.foo.hook.configure", "snap_foo_hook_configure"};
   416  
   417  static struct sdh_test_data instance_add_hook_data = {"add", "snap.foo_bar.hook.configure",
   418                                                        "snap_foo_bar_hook_configure"};
   419  
   420  static struct sdh_test_data instance_add_instance_name_is_hook_data = {"add", "snap.foo_hook.hook.configure",
   421                                                                         "snap_foo_hook_hook_configure"};
   422  
   423  static void __attribute__((constructor)) init(void) {
   424  #define _test_add(_name, _data, _func) \
   425      g_test_add(_name, sdh_test_fixture, _data, sdh_test_set_up, _func, sdh_test_tear_down)
   426  
   427      _test_add("/snap-device-helper/add", &add_data, test_sdh_action);
   428      _test_add("/snap-device-helper/change", &change_data, test_sdh_action);
   429      _test_add("/snap-device-helper/remove", &remove_data, test_sdh_action);
   430  
   431      _test_add("/snap-device-helper/err/no-appname", NULL, test_sdh_err_noappname);
   432      _test_add("/snap-device-helper/err/bad-appname", NULL, test_sdh_err_badappname);
   433      _test_add("/snap-device-helper/err/no-devpath", NULL, test_sdh_err_nodevpath);
   434      _test_add("/snap-device-helper/err/wrong-devmajorminor1", NULL, test_sdh_err_wrongdevmajorminor1);
   435      _test_add("/snap-device-helper/err/wrong-devmajorminor2", NULL, test_sdh_err_wrongdevmajorminor2);
   436      _test_add("/snap-device-helper/err/wrong-devmajorminor_late1", NULL, test_sdh_err_wrongdevmajorminor_late1);
   437      _test_add("/snap-device-helper/err/wrong-devmajorminor_late2", NULL, test_sdh_err_wrongdevmajorminor_late2);
   438      _test_add("/snap-device-helper/err/bad-action", NULL, test_sdh_err_badaction);
   439      _test_add("/snap-device-helper/err/no-symlink", NULL, test_sdh_err_nosymlink);
   440      _test_add("/snap-device-helper/err/funtag1", NULL, test_sdh_err_funtag1);
   441      _test_add("/snap-device-helper/err/funtag2", NULL, test_sdh_err_funtag2);
   442      _test_add("/snap-device-helper/err/funtag3", NULL, test_sdh_err_funtag3);
   443      _test_add("/snap-device-helper/err/funtag4", NULL, test_sdh_err_funtag4);
   444      _test_add("/snap-device-helper/err/funtag5", NULL, test_sdh_err_funtag5);
   445      _test_add("/snap-device-helper/err/funtag6", NULL, test_sdh_err_funtag6);
   446      _test_add("/snap-device-helper/err/funtag7", NULL, test_sdh_err_funtag7);
   447      _test_add("/snap-device-helper/err/funtag8", NULL, test_sdh_err_funtag8);
   448      // parallel instances
   449      _test_add("/snap-device-helper/parallel/add", &instance_add_data, test_sdh_action);
   450      _test_add("/snap-device-helper/parallel/change", &instance_change_data, test_sdh_action);
   451      _test_add("/snap-device-helper/parallel/remove", &instance_remove_data, test_sdh_action);
   452      // hooks
   453      _test_add("/snap-device-helper/hook/add", &add_hook_data, test_sdh_action);
   454      _test_add("/snap-device-helper/hook/parallel/add", &instance_add_hook_data, test_sdh_action);
   455      _test_add("/snap-device-helper/hook-name-hook/parallel/add", &instance_add_instance_name_is_hook_data,
   456                test_sdh_action);
   457  
   458      _test_add("/snap-device-helper/nvme", NULL, test_sdh_action_nvme);
   459  }