+ rc = llog_parse_catalog_start_end(&argc, &argv, &catalog, &start, &end);
+ if (rc)
+ return rc;
+
+ if (llog_default_device(LLOG_DFLT_MGS_SET))
+ return CMD_INCOMPLETE;
+
+ if (end == -1)
+ end = 0x7fffffff;
+
+ data.ioc_dev = cur_device;
+ data.ioc_inllen1 = strlen(catalog) + 1;
+ data.ioc_inlbuf1 = catalog;
+
+ snprintf(startbuf, sizeof(startbuf), "%lu", start);
+ snprintf(endbuf, sizeof(endbuf), "%lu", end);
+ /* start and end record numbers are passed as ASCII digits */
+ data.ioc_inllen2 = strlen(startbuf) + 1;
+ data.ioc_inlbuf2 = startbuf;
+ data.ioc_inllen3 = strlen(endbuf) + 1;
+ data.ioc_inlbuf3 = endbuf;
+
+ data.ioc_inllen4 = sizeof(rawbuf) - __ALIGN_KERNEL(sizeof(data), 8) -
+ __ALIGN_KERNEL(data.ioc_inllen1, 8) -
+ __ALIGN_KERNEL(data.ioc_inllen2, 8) -
+ __ALIGN_KERNEL(data.ioc_inllen3, 8);
+ rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
+ if (rc) {
+ fprintf(stderr, "%s: ioctl_pack failed for catalog '%s': %s\n",
+ jt_cmdname(cmd), data.ioc_inlbuf1, strerror(-rc));
+ goto err;
+ }
+
+ rc = l_ioctl(OBD_DEV_ID, OBD_IOC_LLOG_CHECK, buf);
+ if (rc == 0)
+ fprintf(stdout, "%s", ((struct obd_ioctl_data *)buf)->ioc_bulk);
+ else
+ fprintf(stderr, "%s: OBD_IOC_LLOG_CHECK failed: %s\n",
+ jt_cmdname(cmd), strerror(errno));
+err:
+ llog_default_device(LLOG_DFLT_DEV_RESET);
+ return rc;
+}
+
+int jt_llog_remove(int argc, char **argv)
+{
+ struct obd_ioctl_data data = { 0 };
+ char rawbuf[MAX_IOC_BUFLEN] = "", *buf = rawbuf;
+ char *cmd = argv[0];
+ int rc;
+
+ if (llog_default_device(LLOG_DFLT_MGS_SET))
+ return CMD_INCOMPLETE;
+
+ rc = llog_parse_catalog_log_idx(&argc, &argv, "c:hl:", 2, &data);
+ if (rc)
+ goto err;
+
+ if (argc == 1) {
+ if (data.ioc_inlbuf2) {
+ fprintf(stderr,
+ "%s: --log_id is set, unknown argument '%s'\n",
+ jt_cmdname(cmd), argv[0]);
+ rc = CMD_HELP;
+ goto err;
+ }
+
+ data.ioc_inllen2 = strlen(argv[0]) + 1;
+ data.ioc_inlbuf2 = argv[0];
+ }
+
+ rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
+ if (rc) {
+ fprintf(stderr, "%s: ioctl_pack for catalog '%s' failed: %s\n",
+ jt_cmdname(cmd), data.ioc_inlbuf1, strerror(-rc));
+ goto err;
+ }
+
+ rc = l_ioctl(OBD_DEV_ID, OBD_IOC_LLOG_REMOVE, buf);
+ if (rc)
+ fprintf(stderr, "%s: cancel catalog '%s:%s' failed: %s\n",
+ jt_cmdname(cmd), data.ioc_inlbuf1, data.ioc_inlbuf2,
+ strerror(-rc));
+
+err:
+ llog_default_device(LLOG_DFLT_DEV_RESET);
+ return rc;
+}
+
+static void signal_server(int sig)
+{
+ if (sig == SIGINT) {
+ do_disconnect("sigint", 1);
+ exit(1);
+ } else {
+ fprintf(stderr, "%s: got signal %d\n", jt_cmdname("sigint"),
+ sig);
+ }
+}
+
+int obd_initialize(int argc, char **argv)
+{
+ if (shmem_setup() != 0)
+ return -1;
+
+ register_ioc_dev(OBD_DEV_ID, OBD_DEV_PATH);
+
+ return 0;
+}
+
+void obd_finalize(int argc, char **argv)
+{
+ struct sigaction sigact;
+
+ /* sigact initialization */
+ sigact.sa_handler = signal_server;
+ sigfillset(&sigact.sa_mask);
+ sigact.sa_flags = SA_RESTART;
+ /* coverity[uninit_use_in_call] */
+ sigaction(SIGINT, &sigact, NULL);
+
+ shmem_cleanup();
+ do_disconnect(argv[0], 1);
+}
+
+/**
+ * Get the index of the last llog record
+ *
+ * logid: [0x3:0xa:0x0]:0
+ * flags: 4 (plain)
+ * records_count: 57
+ * last_index: 57
+ *
+ * \param logname[in] pointer to config log name
+ *
+ * \retval > 0 on success
+ * <= 0 on error
+ */
+static long llog_last_index(char *logname)
+{
+ struct obd_ioctl_data data = { 0 };
+ char rawbuf[MAX_IOC_BUFLEN] = "", *buf = rawbuf;
+ char *last_index;
+ long rc;
+
+ data.ioc_dev = cur_device;
+ data.ioc_inllen1 = strlen(logname) + 1;
+ data.ioc_inlbuf1 = logname;
+ data.ioc_inllen2 = sizeof(rawbuf) - __ALIGN_KERNEL(sizeof(data), 8) -
+ __ALIGN_KERNEL(data.ioc_inllen1, 8);
+ rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
+ if (rc) {
+ fprintf(stderr, "%s: ioctl_pack failed for catalog '%s': %s\n",
+ __func__, logname, strerror(-rc));
+ return rc;
+ }
+
+ rc = l_ioctl(OBD_DEV_ID, OBD_IOC_LLOG_INFO, buf);
+ if (rc == 0) {
+ last_index = strstr(((struct obd_ioctl_data *)buf)->ioc_bulk,
+ "last_index:");
+ return strtol(last_index + 11, NULL, 10);
+ }
+
+ rc = -errno;
+
+ return rc;
+}
+
+static char *get_llog_event_name(__u32 cmd)
+{
+ struct lcfg_type_data *data;
+
+ data = lcfg_cmd2data(cmd);
+ if (data)
+ return data->ltd_name;
+ return NULL;
+}
+
+static char *get_event_filter(__u32 cmd)
+{
+ char *event_name;
+ char *filter = NULL;
+ int len;
+
+ event_name = get_llog_event_name(cmd);
+ if (event_name) {
+ /* 9 bytes for "event: , " */
+ len = 9 + strlen(event_name);
+ filter = malloc(len + 1);
+ if (!filter)
+ return NULL;
+ memset(filter, 0, len + 1);
+ snprintf(filter, len, "event: %s, ", event_name);
+ return filter;
+ }
+
+ return NULL;
+}
+
+/**
+ * Callback to search ostname in llog
+ * - { index: 23, event: attach, device: lustre-OST0000-osc, type: osc,
+ * UUID: lustre-clilov_UUID }
+ * - { index: 24, event: setup, device: lustre-OST0000-osc,
+ * UUID: lustre-OST0000_UUID, node: 192.168.0.120@tcp }
+ * - { index: 25, event: add_osc, device: lustre-clilov,
+ * ost: lustre-OST0000_UUID, index: 0, gen: 1 }
+ *
+ * \param record[in] pointer to llog record
+ * \param data[in] pointer to ostname
+ *
+ * \retval 1 if ostname is found
+ * 0 if ostname is not found
+ * -ENOENT if ostname is deleted
+ */
+static int llog_search_ost_cb(const char *record, void *data)
+{
+ char *ostname = data;
+ char ost_filter[MAX_STRING_SIZE] = {'\0'};
+ char *add_osc, *del_osc, *setup, *cleanup;
+
+ add_osc = get_event_filter(LCFG_LOV_ADD_OBD);
+ del_osc = get_event_filter(LCFG_LOV_DEL_OBD);
+ setup = get_event_filter(LCFG_SETUP);
+ cleanup = get_event_filter(LCFG_CLEANUP);
+ if (!add_osc || !del_osc || !setup || !cleanup)
+ return -ENOMEM;
+
+ if (ostname && ostname[0])
+ snprintf(ost_filter, sizeof(ost_filter), " %s,", ostname);
+
+ if (strstr(record, ost_filter)) {
+ if (strstr(record, add_osc) || strstr(record, setup))
+ return 1;
+ if (strstr(record, del_osc) || strstr(record, cleanup))
+ return -ENOENT;
+ }
+
+ free(add_osc);
+ free(del_osc);
+ free(setup);
+ free(cleanup);
+
+ return 0;
+}
+
+/**
+ * Search ost in llog
+ *
+ * \param logname[in] pointer to config log name
+ * \param last_index[in] the index of the last llog record
+ * \param ostname[in] pointer to ost name
+ *
+ * \retval 1 if ostname is found
+ * 0 if ostname is not found
+ */
+static int llog_search_ost(char *logname, long last_index, char *ostname)
+{
+ long start, end, inc = MAX_IOC_BUFLEN / 128;
+ int rc = 0;
+
+ for (end = last_index; end > 1; end -= inc) {
+ start = end - inc > 0 ? end - inc : 1;
+ rc = jt_llog_print_iter(logname, start, end, llog_search_ost_cb,
+ ostname, true);
+ if (rc)
+ break;
+ }
+
+ return (rc == 1 ? 1 : 0);
+}
+
+struct llog_pool_data {
+ char lpd_fsname[LUSTRE_MAXFSNAME + 1];
+ char lpd_poolname[LOV_MAXPOOLNAME + 1];
+ char lpd_ostname[MAX_OBD_NAME + 1];
+ enum lcfg_command_type lpd_cmd_type;
+ bool lpd_pool_exists;
+ int lpd_ost_num;
+};
+
+/**
+ * Called for each formatted line in the config log (within range).
+ *
+ * - { index: 74, event: new_pool, device: tfs-clilov, fsname: tfs, pool: tmp }
+ * - { index: 77, event: add_pool, device: tfs-clilov, fsname: tfs, pool: tmp,
+ * ost: tfs-OST0000_UUID }
+ * - { index: 224, event: remove_pool, device: tfs-clilov, fsname: tfs,
+ * pool: tmp, ost: tfs-OST0003_UUID }
+ * - { index: 227, event: del_pool, device: tfs-clilov, fsname: tfs, pool: tmp }
+ *
+ * \param record[in] pointer to llog record
+ * \param data[in] pointer to llog_pool_data
+ *
+ * \retval 1 if pool or OST is found
+ * 0 if pool or OST is not found
+ * -ENOENT if pool or OST is removed
+ */
+static int llog_search_pool_cb(const char *record, void *data)
+{
+ struct llog_pool_data *lpd = data;
+ char pool_filter[MAX_STRING_SIZE] = "";
+ char *new_pool, *del_pool, *add_pool, *rem_pool;
+ char *found = NULL;
+ int fs_pool_len = 0, rc = 0;
+
+ new_pool = get_event_filter(LCFG_POOL_NEW);
+ del_pool = get_event_filter(LCFG_POOL_DEL);
+ add_pool = get_event_filter(LCFG_POOL_ADD);
+ rem_pool = get_event_filter(LCFG_POOL_REM);
+ if (!new_pool || !del_pool || !add_pool || !rem_pool) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ fs_pool_len = 16 + strlen(lpd->lpd_fsname) + strlen(lpd->lpd_poolname);
+ snprintf(pool_filter, fs_pool_len + 1, "fsname: %s, pool: %s",
+ lpd->lpd_fsname, lpd->lpd_poolname);
+
+ /* search poolname */
+ found = strstr(record, pool_filter);
+ if (found &&
+ (found[fs_pool_len] == ' ' || found[fs_pool_len] == ',')) {
+ if (strstr(record, new_pool)) {
+ lpd->lpd_pool_exists = true;
+ rc = 1;
+ goto out;
+ }
+ if (strstr(record, del_pool)) {
+ lpd->lpd_pool_exists = false;
+ rc = -ENOENT;
+ goto out;
+ }
+
+ if (lpd->lpd_cmd_type == LCFG_POOL_NEW ||
+ lpd->lpd_cmd_type == LCFG_POOL_DEL) {
+ if (strstr(record, add_pool))
+ lpd->lpd_ost_num++;
+ if (strstr(record, rem_pool))
+ lpd->lpd_ost_num--;
+ } else if (lpd->lpd_ostname && lpd->lpd_ostname[0]) {
+ if (strstr(record, lpd->lpd_ostname)) {
+ lpd->lpd_pool_exists = true;
+ if (strstr(record, add_pool)) {
+ lpd->lpd_ost_num = 1;
+ rc = 1;
+ goto out;
+ }
+ if (strstr(record, rem_pool)) {
+ lpd->lpd_ost_num = 0;
+ rc = -ENOENT;
+ goto out;
+ }
+ }
+ }
+ }
+out:
+ if (new_pool)
+ free(new_pool);
+ if (del_pool)
+ free(del_pool);
+ if (add_pool)
+ free(add_pool);
+ if (rem_pool)
+ free(rem_pool);
+
+ return rc;
+}
+
+/* Search pool and its ost in llog
+ *
+ * \param logname[in] pointer to config log name
+ * \param last_index[in] the index of the last llog record
+ * \param fsname[in] pointer to filesystem name
+ * \param poolname[in] pointer pool name
+ * \param ostname[in] pointer to OST name(OSTnnnn-UUID)
+ * \param cmd[in] pool command type
+ *
+ * \retval < 0 on error
+ * 0 if pool is empty or OST is not found
+ * 1 if pool is not empty or OST is found
+ */
+static int llog_search_pool(char *logname, long last_index, char *fsname,
+ char *poolname, char *ostname,
+ enum lcfg_command_type cmd)
+{
+ struct llog_pool_data lpd;
+ long start, end, inc = MAX_IOC_BUFLEN / 128;
+ int rc = 0;
+
+ memset(&lpd, 0, sizeof(lpd));
+ lpd.lpd_cmd_type = cmd;
+ lpd.lpd_pool_exists = false;
+ lpd.lpd_ost_num = 0;
+ strncpy(lpd.lpd_fsname, fsname, sizeof(lpd.lpd_fsname) - 1);
+ if (poolname && poolname[0])
+ strncpy(lpd.lpd_poolname, poolname,
+ sizeof(lpd.lpd_poolname) - 1);
+ if (ostname && ostname[0])
+ strncpy(lpd.lpd_ostname, ostname, sizeof(lpd.lpd_ostname) - 1);
+
+ for (end = last_index; end > 1; end -= inc) {
+ start = end - inc > 0 ? end - inc : 1;
+ rc = jt_llog_print_iter(logname, start, end,
+ llog_search_pool_cb, &lpd, true);
+ if (rc) {
+ if (rc == 1 && lpd.lpd_pool_exists)
+ rc = lpd.lpd_ost_num ? 1 : 0;
+ else if (rc == -ENOENT && lpd.lpd_pool_exists &&
+ !lpd.lpd_ost_num)
+ rc = 0;
+ goto out;
+ }
+ }
+
+ rc = -ENOENT;
+out:
+ return rc;
+}
+
+static bool combined_mgs_mds(char *fsname)
+{
+ glob_t path;
+ int rc;
+
+ rc = cfs_get_param_paths(&path, "mdt/%s-MDT0000", fsname);
+ if (!rc)
+ cfs_free_param_data(&path);
+
+ if (get_mgs_device() > 0 && !rc)
+ return true;
+
+ return false;
+}
+
+/*
+ * if pool is NULL, search ostname in target_obd
+ * if pool is not NULL:
+ * - if pool not found returns errno < 0
+ * - if ostname is NULL, returns 1 if pool is not empty and 0 if pool empty
+ * - if ostname is not NULL, returns 1 if OST is in pool and 0 if not
+ */
+int lctl_search_ost(char *fsname, char *poolname, char *ostname,
+ enum lcfg_command_type cmd)
+{
+ char logname[MAX_OBD_NAME] = {'\0'};
+ long last_index;
+
+ if (fsname && fsname[0] == '\0')
+ fsname = NULL;
+ if (!fsname)
+ return -EINVAL;
+
+ if (combined_mgs_mds(fsname))
+ return llapi_search_ost(fsname, poolname, ostname);
+
+ /* fetch the last_index of llog record */
+ snprintf(logname, sizeof(logname), "%s-client", fsname);
+ last_index = llog_last_index(logname);
+ if (last_index < 0)
+ return last_index;
+
+ /* if pool is NULL, search ostname in target_obd */
+ if (!poolname && ostname)
+ return llog_search_ost(logname, last_index, ostname);
+
+ return llog_search_pool(logname, last_index, fsname, poolname,
+ ostname, cmd);
+}
+
+static int check_pool_cmd(enum lcfg_command_type cmd, char *fsname,
+ char *poolname, char *ostname)
+{
+ int rc;
+
+ rc = lctl_search_ost(fsname, poolname, ostname, cmd);
+ if (rc < 0 && (cmd != LCFG_POOL_NEW)) {
+ fprintf(stderr, "Pool %s.%s not found\n",
+ fsname, poolname);
+ return rc;
+ }
+
+ switch (cmd) {
+ case LCFG_POOL_NEW: {
+ if (ostname)
+ return -EINVAL;
+
+ if (rc >= 0) {
+ fprintf(stderr, "Pool %s.%s already exists\n",
+ fsname, poolname);
+ return -EEXIST;
+ }
+ return 0;
+ }
+ case LCFG_POOL_DEL: {
+ if (ostname)
+ return -EINVAL;
+
+ if (rc == 1) {
+ fprintf(stderr,
+ "Pool %s.%s not empty, please remove all members\n",
+ fsname, poolname);
+ return -ENOTEMPTY;
+ }
+ return 0;
+ }
+ case LCFG_POOL_ADD: {
+ if (rc == 1) {
+ fprintf(stderr, "OST %s is already in pool %s.%s\n",
+ ostname, fsname, poolname);
+ return -EEXIST;
+ }
+ rc = lctl_search_ost(fsname, NULL, ostname, cmd);
+ if (rc == 0) {
+ fprintf(stderr, "OST %s is not part of the '%s' fs.\n",
+ ostname, fsname);
+ return -ENOENT;
+ }
+ return 0;
+ }
+ case LCFG_POOL_REM: {
+ if (rc == 0) {
+ fprintf(stderr, "OST %s not found in pool %s.%s\n",
+ ostname, fsname, poolname);
+ return -ENOENT;
+ }
+ return 0;
+ }
+ default:
+ break;
+ } /* switch */
+ return -EINVAL;
+}
+
+/*
+ * This check only verifies that the changes have been "pushed out" to
+ * the client successfully. This involves waiting for a config update,
+ * and so may fail because of problems in that code or post-command
+ * network loss. So reporting a warning is appropriate, but not a failure.
+ */
+static int check_pool_cmd_result(enum lcfg_command_type cmd, char *fsname,
+ char *poolname, char *ostname)
+{
+ int cpt = 10;
+ int rc = 0;
+
+ switch (cmd) {
+ case LCFG_POOL_NEW: {
+ do {
+ rc = lctl_search_ost(fsname, poolname, NULL, cmd);
+ if (rc == -ENODEV)
+ return rc;
+ if (rc < 0)
+ sleep(2);
+ cpt--;
+ } while ((rc < 0) && (cpt > 0));
+ if (rc >= 0) {
+ fprintf(stderr, "Pool %s.%s created\n",
+ fsname, poolname);
+ return 0;
+ }
+
+ fprintf(stderr, "Warning, pool %s.%s not found\n", fsname,
+ poolname);
+ return -ENOENT;
+ }
+ case LCFG_POOL_DEL: {
+ do {
+ rc = lctl_search_ost(fsname, poolname, NULL, cmd);
+ if (rc == -ENODEV)
+ return rc;
+ if (rc >= 0)
+ sleep(2);
+ cpt--;
+ } while ((rc >= 0) && (cpt > 0));
+ if (rc < 0) {
+ fprintf(stderr, "Pool %s.%s destroyed\n",
+ fsname, poolname);
+ return 0;
+ }
+
+ fprintf(stderr, "Warning, pool %s.%s still found\n", fsname,
+ poolname);
+ return -EEXIST;
+ }
+ case LCFG_POOL_ADD: {
+ do {
+ rc = lctl_search_ost(fsname, poolname, ostname, cmd);
+ if (rc == -ENODEV)
+ return rc;
+ if (rc != 1)
+ sleep(2);
+ cpt--;
+ } while ((rc != 1) && (cpt > 0));
+ if (rc == 1) {
+ fprintf(stderr, "OST %s added to pool %s.%s\n",
+ ostname, fsname, poolname);
+ return 0;
+ }
+ fprintf(stderr, "Warning, OST %s not found in pool %s.%s\n",
+ ostname, fsname, poolname);
+ return -ENOENT;
+ }
+ case LCFG_POOL_REM: {
+ do {
+ rc = lctl_search_ost(fsname, poolname, ostname, cmd);
+ if (rc == -ENODEV)
+ return rc;
+ if (rc == 1)
+ sleep(2);
+ cpt--;
+ } while ((rc == 1) && (cpt > 0));
+ if (rc != 1) {
+ fprintf(stderr, "OST %s removed from pool %s.%s\n",
+ ostname, fsname, poolname);
+ return 0;
+ }
+ fprintf(stderr, "Warning, OST %s still found in pool %s.%s\n",
+ ostname, fsname, poolname);
+ return -EEXIST;
+ }
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int check_and_complete_ostname(char *fsname, char *ostname)
+{
+ char *ptr;
+ char real_ostname[MAX_OBD_NAME + 1];
+ char i;
+
+ /* if OST name does not start with fsname, we add it */
+ /* if not check if the fsname is the right one */
+ ptr = strchr(ostname, '-');
+ if (!ptr) {
+ sprintf(real_ostname, "%s-%s", fsname, ostname);
+ } else if (strncmp(ostname, fsname, strlen(fsname)) != 0) {
+ fprintf(stderr, "%s does not start with fsname %s\n",
+ ostname, fsname);
+ return -EINVAL;
+ } else {
+ if (strlen(ostname) > sizeof(real_ostname) - 1)
+ return -E2BIG;
+
+ strncpy(real_ostname, ostname, sizeof(real_ostname));
+ }
+
+ /* real_ostname is fsname-????? */
+ ptr = real_ostname + strlen(fsname) + 1;
+ if (strncmp(ptr, "OST", 3) != 0) {
+ fprintf(stderr, "%s does not start by %s-OST nor OST\n",
+ ostname, fsname);
+ return -EINVAL;
+ }
+ /* real_ostname is fsname-OST????? */
+ ptr += 3;
+ for (i = 0; i < 4; i++) {
+ if (!isxdigit(*ptr)) {
+ fprintf(stderr,
+ "ost's index in %s is not an hexa number\n",
+ ostname);
+ return -EINVAL;
+ }
+ ptr++;
+ }
+ /* real_ostname is fsname-OSTXXXX????? */
+ /* if OST name does not end with _UUID, we add it */
+ if (*ptr == '\0') {
+ strcat(real_ostname, "_UUID");
+ } else if (strcmp(ptr, "_UUID") != 0) {
+ fprintf(stderr,
+ "ostname %s does not end with _UUID\n", ostname);
+ return -EINVAL;
+ }
+ /* real_ostname is fsname-OSTXXXX_UUID */
+ strcpy(ostname, real_ostname);
+ return 0;
+}
+
+/* returns 0 or -errno */
+static int pool_cmd(enum lcfg_command_type cmd, char *cmdname,
+ char *fullpoolname, char *fsname, char *poolname,
+ char *ostname)
+{
+ int rc = 0;
+ struct obd_ioctl_data data;
+ struct lustre_cfg_bufs bufs;
+ struct lustre_cfg *lcfg;
+ char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
+
+ rc = check_pool_cmd(cmd, fsname, poolname, ostname);
+ if (rc == -ENODEV)
+ fprintf(stderr,
+ "Can't verify pool command since there is no local MDT or client, proceeding anyhow...\n");
+ else if (rc)
+ return rc;
+
+ lustre_cfg_bufs_reset(&bufs, NULL);
+ lustre_cfg_bufs_set_string(&bufs, 0, cmdname);
+ lustre_cfg_bufs_set_string(&bufs, 1, fullpoolname);
+ if (ostname)
+ lustre_cfg_bufs_set_string(&bufs, 2, ostname);
+
+ lcfg = malloc(lustre_cfg_len(bufs.lcfg_bufcount, bufs.lcfg_buflen));
+ if (!lcfg)
+ return -ENOMEM;
+ lustre_cfg_init(lcfg, cmd, &bufs);
+
+ memset(&data, 0, sizeof(data));
+ rc = data.ioc_dev = get_mgs_device();
+ if (rc < 0)
+ goto out;
+
+ data.ioc_type = LUSTRE_CFG_TYPE;
+ data.ioc_plen1 = lustre_cfg_len(lcfg->lcfg_bufcount,
+ lcfg->lcfg_buflens);
+ data.ioc_pbuf1 = (void *)lcfg;
+
+ memset(buf, 0, sizeof(rawbuf));
+ rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
+ if (rc) {
+ fprintf(stderr, "error: %s: invalid ioctl\n",
+ jt_cmdname(cmdname));
+ free(lcfg);
+ return rc;
+ }
+ rc = l_ioctl(OBD_DEV_ID, OBD_IOC_POOL, buf);
+out:
+ if (rc)
+ rc = -errno;
+ switch (rc) {
+ case -ENAMETOOLONG:
+ fprintf(stderr,
+ "error: %s: either the pool or file system name is too long (max pool name len is %d and file system name is %d)\n",
+ jt_cmdname(cmdname), LOV_MAXPOOLNAME, LUSTRE_MAXFSNAME);
+ break;
+ case -EINVAL:
+ fprintf(stderr,
+ "error: %s can contain only alphanumeric characters, underscores, and dashes besides the required '.'\n",
+ jt_cmdname(cmdname));
+ default:
+ break;
+ }
+ free(lcfg);
+ return rc;
+}
+
+#ifdef HAVE_SERVER_SUPPORT
+/**
+ * Format and send the ioctl to the MGS.
+ *
+ * \param cmd IOCTL to send
+ * \param ret_data void pointer to return anything from
+ * ioctl
+ * \param num_args number of arguments to pack into the
+ * ioctl buffer
+ * \param argv[] variable number of string arguments
+ *
+ * \retval 0 on success
+ */
+static int nodemap_cmd(enum lcfg_command_type cmd, void *ret_data,
+ unsigned int ret_size, ...)
+{
+ va_list ap;
+ char *arg;
+ int i = 0;
+ struct lustre_cfg_bufs bufs;
+ struct obd_ioctl_data data;
+ struct lustre_cfg *lcfg;
+ char rawbuf[MAX_IOC_BUFLEN];
+ char *buf = rawbuf;
+ int rc = 0;
+
+ lustre_cfg_bufs_reset(&bufs, NULL);
+
+ va_start(ap, ret_size);
+ arg = va_arg(ap, char *);
+ while (arg) {
+ lustre_cfg_bufs_set_string(&bufs, i, arg);
+ i++;
+ arg = va_arg(ap, char *);
+ }
+ va_end(ap);
+
+ lcfg = malloc(lustre_cfg_len(bufs.lcfg_bufcount, bufs.lcfg_buflen));
+ if (!lcfg)
+ return -ENOMEM;
+ lustre_cfg_init(lcfg, cmd, &bufs);
+
+ memset(&data, 0, sizeof(data));
+ rc = data.ioc_dev = get_mgs_device();
+ if (rc < 0)
+ goto out;
+
+ data.ioc_type = LUSTRE_CFG_TYPE;
+ data.ioc_plen1 = lustre_cfg_len(lcfg->lcfg_bufcount,
+ lcfg->lcfg_buflens);
+ data.ioc_pbuf1 = (void *)lcfg;
+
+ memset(buf, 0, sizeof(rawbuf));
+ rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
+ if (rc != 0) {
+ fprintf(stderr,
+ "error: invalid ioctl: %08x errno: %d with rc=%d\n",
+ cmd, errno, rc);
+ goto out;
+ }
+
+ rc = l_ioctl(OBD_DEV_ID, OBD_IOC_NODEMAP, buf);
+ if (rc != 0) {
+ fprintf(stderr,
+ "error: invalid ioctl: %08x errno: %d with rc=%d\n",
+ cmd, errno, rc);
+ goto out;
+ }
+
+ if (ret_data) {
+ rc = llapi_ioctl_unpack(&data, buf, sizeof(rawbuf));
+ if (rc != 0)
+ goto out;
+
+ if (ret_size > data.ioc_plen1)
+ ret_size = data.ioc_plen1;
+
+ memcpy(ret_data, data.ioc_pbuf1, ret_size);
+ }
+out:
+ free(lcfg);
+
+ return rc;
+}
+
+/**
+ * activate nodemap functions
+ *
+ * \param argc number of args
+ * \param argv[] variable string arguments
+ *
+ * argv[0] 1 for activate or 0 for deactivate
+ *
+ * \retval 0 on success
+ */
+int jt_nodemap_activate(int argc, char **argv)
+{
+ int rc;
+
+ rc = nodemap_cmd(LCFG_NODEMAP_ACTIVATE, NULL, 0, argv[0], argv[1],
+ NULL);
+
+ if (rc != 0) {
+ errno = -rc;
+ perror(argv[0]);
+ }
+
+ return rc;
+}
+
+/**
+ * add a nodemap
+ *
+ * \param argc number of args
+ * \param argv[] variable string arguments
+ *
+ * argv[0] nodemap name
+ *
+ * \retval 0 on success
+ */
+int jt_nodemap_add(int argc, char **argv)
+{
+ int rc;
+
+ rc = llapi_nodemap_exists(argv[1]);
+ if (rc == 0) {
+ fprintf(stderr, "error: %s existing nodemap name\n", argv[1]);
+ return 1;
+ }
+
+ rc = nodemap_cmd(LCFG_NODEMAP_ADD, NULL, 0, argv[0], argv[1], NULL);
+
+ if (rc != 0) {
+ errno = -rc;
+ perror(argv[0]);
+ }
+
+ return rc;
+}
+
+/**
+ * delete a nodemap
+ *
+ * \param argc number of args
+ * \param argv[] variable string arguments
+ *
+ * argv[0] nodemap name
+ *
+ * \retval 0 on success
+ */
+int jt_nodemap_del(int argc, char **argv)
+{
+ int rc;
+
+ rc = llapi_nodemap_exists(argv[1]);
+ if (rc != 0) {
+ fprintf(stderr, "error: %s not existing nodemap name\n",
+ argv[1]);
+ return rc;
+ }
+ rc = nodemap_cmd(LCFG_NODEMAP_DEL, NULL, 0, argv[0], argv[1], NULL);
+
+ if (rc != 0) {
+ errno = -rc;
+ perror(argv[0]);
+ }
+
+ return rc;
+}
+
+/**
+ * test a nid for nodemap membership
+ *
+ * \param argc number of args
+ * \param argv[] variable string arguments
+ *
+ * argv[0] properly formatted nid
+ *
+ * \retval 0 on success
+ */
+int jt_nodemap_test_nid(int argc, char **argv)
+{
+ char rawbuf[MAX_IOC_BUFLEN];
+ int rc;
+
+ rc = nodemap_cmd(LCFG_NODEMAP_TEST_NID, &rawbuf, sizeof(rawbuf),
+ argv[0], argv[1], NULL);
+ if (rc == 0)
+ printf("%s\n", (char *)rawbuf);
+
+ return rc;
+}
+
+/**
+ * test a nodemap id pair for mapping
+ *
+ * \param argc number of args
+ * \param argv[[] variable string arguments
+ *
+ * \retval 0 on success
+ *
+ * The argv array should contain the nodemap name, the id
+ * to checking the mapping on, and the id type (UID or GID)
+ *
+ */
+int jt_nodemap_test_id(int argc, char **argv)
+{
+ char rawbuf[MAX_IOC_BUFLEN];
+ char *nidstr = NULL;
+ char *idstr = NULL;
+ char *typestr = NULL;
+ int rc = 0;
+ int c;
+
+ static struct option long_opts[] = {
+ { .val = 'i', .name = "id", .has_arg = required_argument },
+ { .val = 'n', .name = "nid", .has_arg = required_argument },
+ { .val = 't', .name = "idtype",
+ .has_arg = required_argument },
+ { .name = NULL } };
+
+ while ((c = getopt_long(argc, argv, "n:t:i:",
+ long_opts, NULL)) != -1) {
+ switch (c) {
+ case 'n':
+ nidstr = optarg;
+ break;
+ case 't':
+ typestr = optarg;
+ break;
+ case 'i':
+ idstr = optarg;
+ break;
+ }
+ }
+
+ if (!nidstr || !typestr || !idstr) {
+ fprintf(stderr,
+ "usage: nodemap_test_id --nid <nid> --idtype [uid|gid] --id <id>\n");
+ return -1;
+ }
+
+ rc = nodemap_cmd(LCFG_NODEMAP_TEST_ID, &rawbuf, sizeof(rawbuf),
+ argv[0], nidstr, typestr, idstr);
+ if (rc == 0)
+ printf("%s\n", (char *)rawbuf);
+
+ return rc;
+}
+
+/**
+ * parse nid range
+ *
+ * \param nodemap_range --range string
+ * \param nid_range nid range string, min_nid:max_nid
+ *
+ * \retval 0 on success
+ */
+static int parse_nid_range(char *nodemap_range, char *nid_range, int range_len)
+{
+ char min_nid[LNET_NIDSTR_SIZE + 1];
+ char max_nid[LNET_NIDSTR_SIZE + 1];
+ struct list_head nidlist;
+ int rc = 0;
+
+ INIT_LIST_HEAD(&nidlist);
+
+ if (cfs_parse_nidlist(nodemap_range, strlen(nodemap_range),
+ &nidlist) <= 0) {
+ fprintf(stderr,
+ "error: nodemap_xxx_range: can't parse nid range: %s\n",
+ nodemap_range);
+ return -1;
+ }
+
+ rc = cfs_nidrange_find_min_max(&nidlist, &min_nid[0], &max_nid[0],
+ LNET_NIDSTR_SIZE);
+ if (rc < 0) {
+ if (rc == -EINVAL)
+ fprintf(stderr,
+ "error: nodemap_xxx_range: nid range uses currently unsupported features\n");
+ else if (rc == -ERANGE)
+ fprintf(stderr,
+ "error: nodemap_xxx_range: nodemap ranges must be contiguous\n");
+
+ return rc;
+ }
+
+ snprintf(nid_range, range_len, "%s:%s", min_nid, max_nid);
+
+ return rc;
+}
+
+/**
+ * add an nid range to a nodemap
+ *
+ * \param argc number of args
+ * \param argv[] variable string arguments
+ *
+ * --name nodemap name
+ * --range properly formatted nid range
+ *
+ * \retval 0 on success
+ */
+int jt_nodemap_add_range(int argc, char **argv)
+{
+ char *nodemap_name = NULL;
+ char *nodemap_range = NULL;
+ char nid_range[2 * LNET_NIDSTR_SIZE + 2];
+ int rc = 0;
+ int c;
+
+ static struct option long_opts[] = {
+ { .val = 'n', .name = "name", .has_arg = required_argument },
+ { .val = 'r', .name = "range", .has_arg = required_argument },
+ { .name = NULL } };
+
+ while ((c = getopt_long(argc, argv, "n:r:",
+ long_opts, NULL)) != -1) {
+ switch (c) {
+ case 'n':
+ nodemap_name = optarg;
+ break;
+ case 'r':
+ nodemap_range = optarg;
+ break;
+ }
+ }
+
+ if (!nodemap_name || !nodemap_range) {
+ fprintf(stderr,
+ "usage: nodemap_add_range --name <name> --range <range>\n");
+ return -1;
+ }
+
+ rc = parse_nid_range(nodemap_range, nid_range, sizeof(nid_range));
+ if (rc) {
+ errno = -rc;
+ return rc;
+ }
+ rc = nodemap_cmd(LCFG_NODEMAP_ADD_RANGE, NULL, 0, argv[0],
+ nodemap_name, nid_range, NULL);
+ if (rc != 0) {
+ errno = -rc;
+ fprintf(stderr,
+ "error: %s: cannot add range '%s' to nodemap '%s': rc = %d\n",
+ jt_cmdname(argv[0]), nodemap_range, nodemap_name, rc);
+ }
+
+ return rc;
+}
+
+/**
+ * delete an nid range to a nodemap
+ *
+ * \param argc number of args
+ * \param argv[] variable string arguments
+ *
+ * --name nodemap name
+ * --range properly formatted nid range
+ *
+ * \retval 0 on success
+ */
+int jt_nodemap_del_range(int argc, char **argv)
+{
+ char *nodemap_name = NULL;
+ char *nodemap_range = NULL;
+ char nid_range[2 * LNET_NIDSTR_SIZE + 2];
+ int rc = 0;
+ int c;
+
+ static struct option long_opts[] = {
+ { .val = 'n', .name = "name", .has_arg = required_argument },
+ { .val = 'r', .name = "range", .has_arg = required_argument },
+ { .name = NULL } };
+
+ while ((c = getopt_long(argc, argv, "n:r:",
+ long_opts, NULL)) != -1) {
+ switch (c) {
+ case 'n':
+ nodemap_name = optarg;
+ break;
+ case 'r':
+ nodemap_range = optarg;
+ break;
+ }
+ }
+
+ if (!nodemap_name || !nodemap_range) {
+ fprintf(stderr,
+ "usage: nodemap_del_range --name <name> --range <range>\n");
+ return -1;
+ }
+
+ rc = parse_nid_range(nodemap_range, nid_range, sizeof(nid_range));
+ if (rc) {
+ errno = -rc;
+ return rc;
+ }
+ rc = nodemap_cmd(LCFG_NODEMAP_DEL_RANGE, NULL, 0, argv[0],
+ nodemap_name, nid_range, NULL);
+ if (rc != 0) {
+ errno = -rc;
+ fprintf(stderr,
+ "error: %s: cannot delete range '%s' to nodemap '%s': rc = %d\n",
+ jt_cmdname(argv[0]), nodemap_range, nodemap_name, rc);
+ }
+
+ return rc;
+}
+
+/**
+ * set a fileset on a nodemap
+ *
+ * \param argc number of args
+ * \param argv[] variable string arguments
+ *
+ * --name nodemap name
+ * --fileset fileset name
+ *
+ * \retval 0 on success
+ */
+int jt_nodemap_set_fileset(int argc, char **argv)
+{
+ char *nodemap_name = NULL;
+ char *fileset_name = NULL;
+ int rc = 0;
+ int c;
+
+ static struct option long_opts[] = {
+ { .val = 'f', .name = "fileset", .has_arg = required_argument },
+ { .val = 'n', .name = "name", .has_arg = required_argument },
+ { .name = NULL } };
+
+ while ((c = getopt_long(argc, argv, "n:f:",
+ long_opts, NULL)) != -1) {
+ switch (c) {
+ case 'n':
+ nodemap_name = optarg;
+ break;
+ case 'f':
+ fileset_name = optarg;
+ break;
+ }
+ }
+
+ if (!nodemap_name || !fileset_name) {
+ fprintf(stderr,
+ "usage: nodemap_set_fileset --name <name> --fileset <fileset>\n");
+ return -1;
+ }
+
+ rc = nodemap_cmd(LCFG_NODEMAP_SET_FILESET, NULL, 0, argv[0],
+ nodemap_name, fileset_name, NULL);
+ if (rc != 0) {
+ errno = -rc;
+ fprintf(stderr,
+ "error: %s: cannot set fileset '%s' on nodemap '%s': rc = %d\n",
+ jt_cmdname(argv[0]), fileset_name, nodemap_name, rc);
+ }
+
+ return rc;
+}
+
+/**
+ * set SELinux policy info on a nodemap
+ *
+ * \param argc number of args
+ * \param argv[] variable string arguments
+ *
+ * --name nodemap name
+ * --sepol SELinux policy info
+ *
+ * \retval 0 on success
+ */
+int jt_nodemap_set_sepol(int argc, char **argv)
+{
+ char *nodemap_name = NULL;
+ char *sepol = NULL;
+ int rc = 0;
+ int c;
+
+ static struct option long_options[] = {
+ {