Whamcloud - gitweb
60f2ef891ba40141e685c8cfb014607678954d15
[fs/lustre-release.git] / lustre / utils / lustre_cfg.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright (c) 2011, 2016, Intel Corporation.
27  */
28 /*
29  * This file is part of Lustre, http://www.lustre.org/
30  * Lustre is a trademark of Sun Microsystems, Inc.
31  *
32  * lustre/utils/lustre_cfg.c
33  *
34  * Author: Peter J. Braam <braam@clusterfs.com>
35  * Author: Phil Schwan <phil@clusterfs.com>
36  * Author: Andreas Dilger <adilger@clusterfs.com>
37  * Author: Robert Read <rread@clusterfs.com>
38  */
39
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <limits.h>
43 #include <stdbool.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <sys/ioctl.h>
47 #include <sys/stat.h>
48 #include <unistd.h>
49 #include <stdio.h>
50 #include <stdarg.h>
51 #include <ctype.h>
52
53 #include <libcfs/util/ioctl.h>
54 #include <libcfs/util/string.h>
55 #include <libcfs/util/param.h>
56 #include <libcfs/util/parser.h>
57 #include <lustre/lustreapi.h>
58 #include <linux/lnet/nidstr.h>
59 #include <linux/lnet/lnetctl.h>
60 #include <linux/lustre/lustre_cfg.h>
61 #include <linux/lustre/lustre_ioctl.h>
62 #include <linux/lustre/lustre_ver.h>
63
64 #include <sys/un.h>
65 #include <time.h>
66 #include <sys/time.h>
67 #include <errno.h>
68 #include <string.h>
69
70 #include "obdctl.h"
71 #include <stdio.h>
72 #include <yaml.h>
73
74 static char * lcfg_devname;
75
76 int lcfg_set_devname(char *name)
77 {
78         char *ptr;
79         int digit = 1;
80
81         if (name) {
82                 if (lcfg_devname)
83                         free(lcfg_devname);
84                 /* quietly strip the unnecessary '$' */
85                 if (*name == '$' || *name == '%')
86                         name++;
87
88                 ptr = name;
89                 while (*ptr != '\0') {
90                         if (!isdigit(*ptr)) {
91                             digit = 0;
92                             break;
93                         }
94                         ptr++;
95                 }
96
97                 if (digit) {
98                         /* We can't translate from dev # to name */
99                         lcfg_devname = NULL;
100                 } else {
101                         lcfg_devname = strdup(name);
102                 }
103         } else {
104                 lcfg_devname = NULL;
105         }
106         return 0;
107 }
108
109 char * lcfg_get_devname(void)
110 {
111         return lcfg_devname;
112 }
113
114 int jt_lcfg_device(int argc, char **argv)
115 {
116         return jt_obd_device(argc, argv);
117 }
118
119 static int jt_lcfg_ioctl(struct lustre_cfg_bufs *bufs, char *arg, int cmd)
120 {
121         struct lustre_cfg *lcfg;
122         int rc;
123
124         lcfg = malloc(lustre_cfg_len(bufs->lcfg_bufcount, bufs->lcfg_buflen));
125         if (lcfg == NULL) {
126                 rc = -ENOMEM;
127         } else {
128                 lustre_cfg_init(lcfg, cmd, bufs);
129                 rc = lcfg_ioctl(arg, OBD_DEV_ID, lcfg);
130                 free(lcfg);
131         }
132         if (rc < 0)
133                 fprintf(stderr, "error: %s: %s\n", jt_cmdname(arg),
134                         strerror(rc = errno));
135         return rc;
136 }
137
138 int jt_lcfg_attach(int argc, char **argv)
139 {
140         struct lustre_cfg_bufs bufs;
141         int rc;
142
143         if (argc != 4)
144                 return CMD_HELP;
145
146         lustre_cfg_bufs_reset(&bufs, NULL);
147
148         lustre_cfg_bufs_set_string(&bufs, 1, argv[1]);
149         lustre_cfg_bufs_set_string(&bufs, 0, argv[2]);
150         lustre_cfg_bufs_set_string(&bufs, 2, argv[3]);
151
152         rc = jt_lcfg_ioctl(&bufs, argv[0], LCFG_ATTACH);
153         if (rc == 0)
154                 lcfg_set_devname(argv[2]);
155
156         return rc;
157 }
158
159 int jt_lcfg_setup(int argc, char **argv)
160 {
161         struct lustre_cfg_bufs bufs;
162         int i;
163
164         if (lcfg_devname == NULL) {
165                 fprintf(stderr, "%s: please use 'device name' to set the "
166                         "device name for config commands.\n",
167                         jt_cmdname(argv[0]));
168                 return -EINVAL;
169         }
170
171         lustre_cfg_bufs_reset(&bufs, lcfg_devname);
172
173         if (argc > 6)
174                 return CMD_HELP;
175
176         for (i = 1; i < argc; i++) {
177                 lustre_cfg_bufs_set_string(&bufs, i, argv[i]);
178         }
179
180         return jt_lcfg_ioctl(&bufs, argv[0], LCFG_SETUP);
181 }
182
183 int jt_obd_detach(int argc, char **argv)
184 {
185         struct lustre_cfg_bufs bufs;
186
187         if (lcfg_devname == NULL) {
188                 fprintf(stderr, "%s: please use 'device name' to set the "
189                         "device name for config commands.\n",
190                         jt_cmdname(argv[0]));
191                 return -EINVAL;
192         }
193
194         lustre_cfg_bufs_reset(&bufs, lcfg_devname);
195
196         if (argc != 1)
197                 return CMD_HELP;
198
199         return jt_lcfg_ioctl(&bufs, argv[0], LCFG_DETACH);
200 }
201
202 int jt_obd_cleanup(int argc, char **argv)
203 {
204         struct lustre_cfg_bufs bufs;
205         char force = 'F';
206         char failover = 'A';
207         char flags[3] = { 0 };
208         int flag_cnt = 0, n;
209
210         if (lcfg_devname == NULL) {
211                 fprintf(stderr, "%s: please use 'device name' to set the "
212                         "device name for config commands.\n",
213                         jt_cmdname(argv[0]));
214                 return -EINVAL;
215         }
216
217         lustre_cfg_bufs_reset(&bufs, lcfg_devname);
218
219         if (argc < 1 || argc > 3)
220                 return CMD_HELP;
221
222         /* we are protected from overflowing our buffer by the argc
223          * check above
224          */
225         for (n = 1; n < argc; n++) {
226                 if (strcmp(argv[n], "force") == 0) {
227                         flags[flag_cnt++] = force;
228                 } else if (strcmp(argv[n], "failover") == 0) {
229                         flags[flag_cnt++] = failover;
230                 } else {
231                         fprintf(stderr, "unknown option: %s\n", argv[n]);
232                         return CMD_HELP;
233                 }
234         }
235
236         if (flag_cnt) {
237                 lustre_cfg_bufs_set_string(&bufs, 1, flags);
238         }
239
240         return jt_lcfg_ioctl(&bufs, argv[0], LCFG_CLEANUP);
241 }
242
243 static
244 int do_add_uuid(char *func, char *uuid, lnet_nid_t nid)
245 {
246         int rc;
247         struct lustre_cfg_bufs bufs;
248         struct lustre_cfg *lcfg;
249
250         lustre_cfg_bufs_reset(&bufs, lcfg_devname);
251         if (uuid != NULL)
252                 lustre_cfg_bufs_set_string(&bufs, 1, uuid);
253
254         lcfg = malloc(lustre_cfg_len(bufs.lcfg_bufcount, bufs.lcfg_buflen));
255         if (lcfg == NULL) {
256                 rc = -ENOMEM;
257         } else {
258                 lustre_cfg_init(lcfg, LCFG_ADD_UUID, &bufs);
259                 lcfg->lcfg_nid = nid;
260
261                 rc = lcfg_ioctl(func, OBD_DEV_ID, lcfg);
262                 free(lcfg);
263         }
264         if (rc) {
265                 fprintf(stderr, "IOC_PORTAL_ADD_UUID failed: %s\n",
266                         strerror(errno));
267                 return -1;
268         }
269
270         if (uuid != NULL)
271                 printf("Added uuid %s: %s\n", uuid, libcfs_nid2str(nid));
272
273         return 0;
274 }
275
276 int jt_lcfg_add_uuid(int argc, char **argv)
277 {
278         lnet_nid_t nid;
279
280         if (argc != 3) {
281                 return CMD_HELP;
282         }
283
284         nid = libcfs_str2nid(argv[2]);
285         if (nid == LNET_NID_ANY) {
286                 fprintf (stderr, "Can't parse NID %s\n", argv[2]);
287                 return (-1);
288         }
289
290         return do_add_uuid(argv[0], argv[1], nid);
291 }
292
293 int jt_lcfg_del_uuid(int argc, char **argv)
294 {
295         struct lustre_cfg_bufs bufs;
296
297         if (argc != 2) {
298                 fprintf(stderr, "usage: %s <uuid>\n", argv[0]);
299                 return 0;
300         }
301
302         lustre_cfg_bufs_reset(&bufs, lcfg_devname);
303         if (strcmp (argv[1], "_all_"))
304                 lustre_cfg_bufs_set_string(&bufs, 1, argv[1]);
305
306         return jt_lcfg_ioctl(&bufs, argv[0], LCFG_DEL_UUID);
307 }
308
309 int jt_lcfg_del_mount_option(int argc, char **argv)
310 {
311         struct lustre_cfg_bufs bufs;
312
313         if (argc != 2)
314                 return CMD_HELP;
315
316         lustre_cfg_bufs_reset(&bufs, lcfg_devname);
317
318         /* profile name */
319         lustre_cfg_bufs_set_string(&bufs, 1, argv[1]);
320
321         return jt_lcfg_ioctl(&bufs, argv[0], LCFG_DEL_MOUNTOPT);
322 }
323
324 int jt_lcfg_set_timeout(int argc, char **argv)
325 {
326         int rc;
327         struct lustre_cfg_bufs bufs;
328         struct lustre_cfg *lcfg;
329
330         fprintf(stderr, "%s has been deprecated. Use conf_param instead.\n"
331                 "e.g. conf_param lustre-MDT0000 obd_timeout=50\n",
332                 jt_cmdname(argv[0]));
333         return CMD_HELP;
334
335
336         if (argc != 2)
337                 return CMD_HELP;
338
339         lustre_cfg_bufs_reset(&bufs, lcfg_devname);
340
341         lcfg = malloc(lustre_cfg_len(bufs.lcfg_bufcount, bufs.lcfg_buflen));
342         if (lcfg == NULL) {
343                 rc = -ENOMEM;
344         } else {
345                 lustre_cfg_init(lcfg, LCFG_SET_TIMEOUT, &bufs);
346                 lcfg->lcfg_num = atoi(argv[1]);
347
348                 rc = lcfg_ioctl(argv[0], OBD_DEV_ID, lcfg);
349                 free(lcfg);
350         }
351         if (rc < 0) {
352                 fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
353                         strerror(rc = errno));
354         }
355         return rc;
356 }
357
358 int jt_lcfg_add_conn(int argc, char **argv)
359 {
360         struct lustre_cfg_bufs bufs;
361         struct lustre_cfg *lcfg;
362         int priority;
363         int rc;
364
365         if (argc == 2)
366                 priority = 0;
367         else if (argc == 3)
368                 priority = 1;
369         else
370                 return CMD_HELP;
371
372         if (lcfg_devname == NULL) {
373                 fprintf(stderr, "%s: please use 'device name' to set the "
374                         "device name for config commands.\n",
375                         jt_cmdname(argv[0]));
376                 return -EINVAL;
377         }
378
379         lustre_cfg_bufs_reset(&bufs, lcfg_devname);
380
381         lustre_cfg_bufs_set_string(&bufs, 1, argv[1]);
382
383         lcfg = malloc(lustre_cfg_len(bufs.lcfg_bufcount, bufs.lcfg_buflen));
384         if (lcfg == NULL) {
385                 rc = -ENOMEM;
386         } else {
387                 lustre_cfg_init(lcfg, LCFG_ADD_CONN, &bufs);
388                 lcfg->lcfg_num = priority;
389
390                 rc = lcfg_ioctl(argv[0], OBD_DEV_ID, lcfg);
391                 free(lcfg);
392         }
393         if (rc < 0) {
394                 fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
395                         strerror(rc = errno));
396         }
397
398         return rc;
399 }
400
401 int jt_lcfg_del_conn(int argc, char **argv)
402 {
403         struct lustre_cfg_bufs bufs;
404
405         if (argc != 2)
406                 return CMD_HELP;
407
408         if (lcfg_devname == NULL) {
409                 fprintf(stderr, "%s: please use 'device name' to set the "
410                         "device name for config commands.\n",
411                         jt_cmdname(argv[0]));
412                 return -EINVAL;
413         }
414
415         lustre_cfg_bufs_reset(&bufs, lcfg_devname);
416
417         /* connection uuid */
418         lustre_cfg_bufs_set_string(&bufs, 1, argv[1]);
419
420         return jt_lcfg_ioctl(&bufs, argv[0], LCFG_DEL_MOUNTOPT);
421 }
422
423 /* Param set locally, directly on target */
424 int jt_lcfg_param(int argc, char **argv)
425 {
426         struct lustre_cfg_bufs bufs;
427         int i;
428
429         if (argc >= LUSTRE_CFG_MAX_BUFCOUNT)
430                 return CMD_HELP;
431
432         lustre_cfg_bufs_reset(&bufs, NULL);
433
434         for (i = 1; i < argc; i++) {
435                 lustre_cfg_bufs_set_string(&bufs, i, argv[i]);
436         }
437
438         return jt_lcfg_ioctl(&bufs, argv[0], LCFG_PARAM);
439 }
440
441 struct param_opts {
442         unsigned int po_only_path:1;
443         unsigned int po_show_path:1;
444         unsigned int po_show_type:1;
445         unsigned int po_recursive:1;
446         unsigned int po_perm:1;
447         unsigned int po_delete:1;
448         unsigned int po_only_dir:1;
449         unsigned int po_file:1;
450 };
451
452 int lcfg_setparam_perm(char *func, char *buf)
453 {
454         int     rc = 0;
455         struct lustre_cfg_bufs bufs;
456         struct lustre_cfg *lcfg;
457
458         lustre_cfg_bufs_reset(&bufs, NULL);
459         /* This same command would be executed on all nodes, many
460          * of which should fail (silently) because they don't have
461          * that proc file existing locally. There would be no
462          * preprocessing on the MGS to try to figure out which
463          * parameter files to add this to, there would be nodes
464          * processing on the cluster nodes to try to figure out
465          * if they are the intended targets. They will blindly
466          * try to set the parameter, and ENOTFOUND means it wasn't
467          * for them.
468          * Target name "general" means call on all targets. It is
469          * left here in case some filtering will be added in
470          * future.
471          */
472         lustre_cfg_bufs_set_string(&bufs, 0, "general");
473
474         lustre_cfg_bufs_set_string(&bufs, 1, buf);
475
476
477         lcfg = malloc(lustre_cfg_len(bufs.lcfg_bufcount,
478                                      bufs.lcfg_buflen));
479         if (lcfg == NULL) {
480                 rc = -ENOMEM;
481                 fprintf(stderr, "error: allocating lcfg for %s: %s\n",
482                         jt_cmdname(func), strerror(rc));
483
484         } else {
485                 lustre_cfg_init(lcfg, LCFG_SET_PARAM, &bufs);
486                 rc = lcfg_mgs_ioctl(func, OBD_DEV_ID, lcfg);
487                 if (rc != 0)
488                         fprintf(stderr, "error: executing %s: %s\n",
489                                 jt_cmdname(func), strerror(errno));
490                 free(lcfg);
491         }
492
493         return rc;
494 }
495
496 /* Param set to single log file, used by all clients and servers.
497  * This should be loaded after the individual config logs.
498  * Called from set param with -P option.
499  */
500 static int jt_lcfg_setparam_perm(int argc, char **argv,
501                                  struct param_opts *popt)
502 {
503         int rc;
504         int i;
505         int first_param;
506         char *buf = NULL;
507         int len;
508
509         first_param = optind;
510         if (first_param < 0 || first_param >= argc)
511                 return CMD_HELP;
512
513         for (i = first_param, rc = 0; i < argc; i++) {
514
515                 len = strlen(argv[i]);
516
517                 buf = argv[i];
518
519                 /* put an '=' on the end in case it doesn't have one */
520                 if (popt->po_delete && argv[i][len - 1] != '=') {
521                         buf = malloc(len + 1);
522                         if (buf == NULL) {
523                                 rc = -ENOMEM;
524                                 break;
525                         }
526                         sprintf(buf, "%s=", argv[i]);
527                 }
528
529                 rc = lcfg_setparam_perm(argv[0], buf);
530
531                 if (buf != argv[i])
532                         free(buf);
533         }
534
535         return rc;
536 }
537
538 int lcfg_conf_param(char *func, char *buf)
539 {
540         int rc;
541         struct lustre_cfg_bufs bufs;
542         struct lustre_cfg *lcfg;
543
544         lustre_cfg_bufs_reset(&bufs, NULL);
545         lustre_cfg_bufs_set_string(&bufs, 1, buf);
546
547         /* We could put other opcodes here. */
548         lcfg = malloc(lustre_cfg_len(bufs.lcfg_bufcount, bufs.lcfg_buflen));
549         if (lcfg == NULL) {
550                 rc = -ENOMEM;
551         } else {
552                 lustre_cfg_init(lcfg, LCFG_PARAM, &bufs);
553                 rc = lcfg_mgs_ioctl(func, OBD_DEV_ID, lcfg);
554                 if (rc < 0)
555                         rc = -errno;
556                 free(lcfg);
557         }
558
559         return rc;
560 }
561
562 /* Param set in config log on MGS */
563 /* conf_param key=value */
564 /* Note we can actually send mgc conf_params from clients, but currently
565  * that's only done for default file striping (see ll_send_mgc_param),
566  * and not here. */
567 /* After removal of a parameter (-d) Lustre will use the default
568  * AT NEXT REBOOT, not immediately. */
569 int jt_lcfg_confparam(int argc, char **argv)
570 {
571         int rc;
572         int del = 0;
573         char *buf = NULL;
574
575         /* mgs_setparam processes only lctl buf #1 */
576         if ((argc > 3) || (argc <= 1))
577                 return CMD_HELP;
578
579         while ((rc = getopt(argc, argv, "d")) != -1) {
580                 switch (rc) {
581                 case 'd':
582                         del = 1;
583                         break;
584                 default:
585                         return CMD_HELP;
586                 }
587         }
588
589         buf = argv[optind];
590
591         if (del) {
592                 char *ptr;
593
594                 /* for delete, make it "<param>=\0" */
595                 buf = malloc(strlen(argv[optind]) + 2);
596                 if (buf == NULL) {
597                         rc = -ENOMEM;
598                         goto out;
599                 }
600                 /* put an '=' on the end in case it doesn't have one */
601                 sprintf(buf, "%s=", argv[optind]);
602                 /* then truncate after the first '=' */
603                 ptr = strchr(buf, '=');
604                 *(++ptr) = '\0';
605         }
606
607         rc = lcfg_conf_param(argv[0], buf);
608
609         if (buf != argv[optind])
610                 free(buf);
611 out:
612         if (rc < 0) {
613                 fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
614                         strerror(-rc));
615         }
616
617         return rc;
618 }
619
620 /**
621  * Display a parameter path in the same format as sysctl.
622  * E.g. obdfilter.lustre-OST0000.stats
623  *
624  * \param[in] filename  file name of the parameter
625  * \param[in] st        parameter file stats
626  * \param[in] popt      set/get param options
627  *
628  * \retval allocated pointer containing modified filename
629  */
630 static char *
631 display_name(const char *filename, struct stat *st, struct param_opts *popt)
632 {
633         size_t suffix_len = 0;
634         char *suffix = NULL;
635         char *param_name;
636         char *tmp;
637
638         if (popt->po_show_type) {
639                 if (S_ISDIR(st->st_mode))
640                         suffix = "/";
641                 else if (S_ISLNK(st->st_mode))
642                         suffix = "@";
643                 else if (st->st_mode & S_IWUSR)
644                         suffix = "=";
645         }
646
647         /* Take the original filename string and chop off the glob addition */
648         tmp = strstr(filename, "/lustre/");
649         if (tmp == NULL) {
650                 tmp = strstr(filename, "/lnet/");
651                 if (tmp != NULL)
652                         tmp += strlen("/lnet/");
653         } else {
654                 tmp += strlen("/lustre/");
655         }
656
657         /* Allocate return string */
658         param_name = strdup(tmp);
659         if (param_name == NULL)
660                 return NULL;
661
662         /* replace '/' with '.' to match conf_param and sysctl */
663         for (tmp = strchr(param_name, '/'); tmp != NULL; tmp = strchr(tmp, '/'))
664                 *tmp = '.';
665
666         /* Append the indicator to entries if needed. */
667         if (popt->po_show_type && suffix != NULL) {
668                 suffix_len = strlen(suffix);
669
670                 tmp = realloc(param_name, suffix_len + strlen(param_name) + 1);
671                 if (tmp != NULL) {
672                         param_name = tmp;
673                         strncat(param_name, suffix,
674                                 strlen(param_name) + suffix_len);
675                 }
676         }
677
678         return param_name;
679 }
680
681 /* Find a character in a length limited string */
682 /* BEWARE - kernel definition of strnchr has args in different order! */
683 static char *strnchr(const char *p, char c, size_t n)
684 {
685        if (!p)
686                return (0);
687
688        while (n-- > 0) {
689                if (*p == c)
690                        return ((char *)p);
691                p++;
692        }
693        return (0);
694 }
695
696 /**
697  * Turns a lctl parameter string into a procfs/sysfs subdirectory path pattern.
698  *
699  * \param[in] popt              Used to control parameter usage. For this
700  *                              function it is used to see if the path has
701  *                              a added suffix.
702  * \param[in,out] path          lctl parameter string that is turned into
703  *                              the subdirectory path pattern that is used
704  *                              to search the procfs/sysfs tree.
705  *
706  * \retval -errno on error.
707  */
708 static int
709 clean_path(struct param_opts *popt, char *path)
710 {
711         char *nidstr = NULL;
712         char *tmp;
713
714         if (popt == NULL || path == NULL || strlen(path) == 0)
715                 return -EINVAL;
716
717         /* If path contains a suffix we need to remove it */
718         if (popt->po_show_type) {
719                 size_t path_end = strlen(path) - 1;
720
721                 tmp = path + path_end;
722                 switch (*tmp) {
723                 case '@':
724                 case '=':
725                 case '/':
726                         *tmp = '\0';
727                 default:
728                         break;
729                 }
730         }
731
732         /* get rid of '\', glob doesn't like it */
733         tmp = strrchr(path, '\\');
734         if (tmp != NULL) {
735                 char *tail = path + strlen(path);
736
737                 while (tmp != path) {
738                         if (*tmp == '\\') {
739                                 memmove(tmp, tmp + 1, tail - tmp);
740                                 --tail;
741                         }
742                         --tmp;
743                 }
744         }
745
746         /* Does this path contain a NID string ? */
747         tmp = strchr(path, '@');
748         if (tmp != NULL) {
749                 char *find_nid = strdup(path);
750                 lnet_nid_t nid;
751
752                 if (find_nid == NULL)
753                         return -ENOMEM;
754
755                 /* First we need to chop off rest after nid string.
756                  * Since find_nid is a clone of path it better have
757                  * '@' */
758                 tmp = strchr(find_nid, '@');
759                 tmp = strchr(tmp, '.');
760                 if (tmp != NULL)
761                         *tmp = '\0';
762
763                 /* Now chop off the front. */
764                 for (tmp = strchr(find_nid, '.'); tmp != NULL;
765                      tmp = strchr(tmp, '.')) {
766                         /* Remove MGC to make it NID format */
767                         if (!strncmp(++tmp, "MGC", 3))
768                                 tmp += 3;
769
770                         nid = libcfs_str2nid(tmp);
771                         if (nid != LNET_NID_ANY) {
772                                 nidstr = libcfs_nid2str(nid);
773                                 if (nidstr == NULL)
774                                         return -EINVAL;
775                                 break;
776                         }
777                 }
778                 free(find_nid);
779         }
780
781         /* replace param '.' with '/' */
782         for (tmp = strchr(path, '.'); tmp != NULL; tmp = strchr(tmp, '.')) {
783                 *tmp++ = '/';
784
785                 /* Remove MGC to make it NID format */
786                 if (!strncmp(tmp, "MGC", 3))
787                         tmp += 3;
788
789                 /* There exist cases where some of the subdirectories of the
790                  * the parameter tree has embedded in its name a NID string.
791                  * This means that it is possible that these subdirectories
792                  * could have actual '.' in its name. If this is the case we
793                  * don't want to blindly replace the '.' with '/'. */
794                 if (nidstr != NULL) {
795                         char *match = strstr(tmp, nidstr);
796
797                         if (tmp == match)
798                                 tmp += strlen(nidstr);
799                 }
800         }
801
802         return 0;
803 }
804
805 /**
806  * The application lctl can perform three operations for lustre
807  * tunables. This enum defines those three operations which are
808  *
809  * 1) LIST_PARAM        - list available tunables
810  * 2) GET_PARAM         - report the current setting of a tunable
811  * 3) SET_PARAM         - set the tunable to a new value
812  */
813 enum parameter_operation {
814         LIST_PARAM,
815         GET_PARAM,
816         SET_PARAM,
817 };
818
819 char *parameter_opname[] = {
820         [LIST_PARAM] = "list_param",
821         [GET_PARAM] = "get_param",
822         [SET_PARAM] = "set_param",
823 };
824
825 /**
826  * Read the value of parameter
827  *
828  * \param[in]   path            full path to the parameter
829  * \param[in]   param_name      lctl parameter format of the
830  *                              parameter path
831  * \param[in]   popt            set/get param options
832  *
833  * \retval 0 on success.
834  * \retval -errno on error.
835  */
836 static int
837 read_param(const char *path, const char *param_name, struct param_opts *popt)
838 {
839         int rc = 0;
840         char *buf = NULL;
841         size_t buflen;
842
843         rc = llapi_param_get_value(path, &buf, &buflen);
844         if (rc != 0) {
845                 fprintf(stderr,
846                         "error: read_param: \'%s\': %s\n",
847                         path, strerror(-rc));
848                 goto free_buf;
849         }
850         /* don't print anything for empty files */
851         if (buf[0] == '\0')
852                 goto free_buf;
853
854         if (popt->po_show_path) {
855                 bool longbuf;
856
857                 longbuf = strnchr(buf, buflen - 1, '\n') != NULL ||
858                         buflen + strlen(param_name) >= 80;
859                 printf("%s=%s", param_name, longbuf ? "\n" : "");
860         }
861         printf("%s", buf);
862
863 free_buf:
864         free(buf);
865         return rc;
866 }
867
868 /**
869  * Set a parameter to a specified value
870  *
871  * \param[in] path              full path to the parameter
872  * \param[in] param_name        lctl parameter format of the parameter path
873  * \param[in] popt              set/get param options
874  * \param[in] value             value to set the parameter to
875  *
876  * \retval number of bytes written on success.
877  * \retval -errno on error.
878  */
879 static int
880 write_param(const char *path, const char *param_name, struct param_opts *popt,
881             const char *value)
882 {
883         int fd, rc = 0;
884         ssize_t count;
885
886         if (value == NULL)
887                 return -EINVAL;
888
889         /* Write the new value to the file */
890         fd = open(path, O_WRONLY);
891         if (fd < 0) {
892                 rc = -errno;
893                 fprintf(stderr, "error: set_param: opening '%s': %s\n",
894                         path, strerror(errno));
895                 return rc;
896         }
897
898         count = write(fd, value, strlen(value));
899         if (count < 0) {
900                 rc = -errno;
901                 if (errno != EIO) {
902                         fprintf(stderr, "error: set_param: setting %s=%s: %s\n",
903                                 path, value, strerror(errno));
904                 }
905         } else if (count < strlen(value)) { /* Truncate case */
906                 rc = -EINVAL;
907                 fprintf(stderr, "error: set_param: setting %s=%s: "
908                         "wrote only %zd\n", path, value, count);
909         } else if (popt->po_show_path) {
910                 printf("%s=%s\n", param_name, value);
911         }
912         close(fd);
913
914         return rc;
915 }
916
917 /**
918  * Perform a read, write or just a listing of a parameter
919  *
920  * \param[in] popt              list,set,get parameter options
921  * \param[in] pattern           search filter for the path of the parameter
922  * \param[in] value             value to set the parameter if write operation
923  * \param[in] mode              what operation to perform with the parameter
924  *
925  * \retval number of bytes written on success.
926  * \retval -errno on error and prints error message.
927  */
928 static int
929 param_display(struct param_opts *popt, char *pattern, char *value,
930               enum parameter_operation mode)
931 {
932         int dup_count = 0;
933         char **dup_cache;
934         glob_t paths;
935         char *opname = parameter_opname[mode];
936         int rc, i;
937
938         rc = llapi_param_get_paths(pattern, &paths);
939         if (rc != 0) {
940                 rc = -errno;
941                 if (!popt->po_recursive) {
942                         fprintf(stderr, "error: %s: param_path '%s': %s\n",
943                                 opname, pattern, strerror(errno));
944                 }
945                 return rc;
946         }
947
948         dup_cache = calloc(paths.gl_pathc, sizeof(char *));
949         if (dup_cache == NULL) {
950                 rc = -ENOMEM;
951                 fprintf(stderr,
952                         "error: %s: allocating '%s' dup_cache[%zd]: %s\n",
953                         opname, pattern, paths.gl_pathc, strerror(-rc));
954                 goto out_param;
955         }
956
957         for (i = 0; i < paths.gl_pathc; i++) {
958                 char *param_name = NULL, *tmp;
959                 char pathname[PATH_MAX], param_dir[PATH_MAX+2];
960                 struct stat st;
961                 int rc2, j;
962
963                 if (stat(paths.gl_pathv[i], &st) == -1) {
964                         fprintf(stderr, "error: %s: stat '%s': %s\n",
965                                 opname, paths.gl_pathv[i], strerror(errno));
966                         if (rc == 0)
967                                 rc = -errno;
968                         continue;
969                 }
970
971                 if (popt->po_only_dir && !S_ISDIR(st.st_mode))
972                         continue;
973
974                 param_name = display_name(paths.gl_pathv[i], &st, popt);
975                 if (param_name == NULL) {
976                         fprintf(stderr,
977                                 "error: %s: generating name for '%s': %s\n",
978                                 opname, paths.gl_pathv[i], strerror(ENOMEM));
979                         if (rc == 0)
980                                 rc = -ENOMEM;
981                         continue;
982                 }
983
984                 switch (mode) {
985                 case GET_PARAM:
986                         /* Read the contents of file to stdout */
987                         if (S_ISREG(st.st_mode)) {
988                                 rc2 = read_param(paths.gl_pathv[i], param_name,
989                                                  popt);
990                                 if (rc2 < 0 && rc == 0)
991                                         rc = rc2;
992                         }
993                         break;
994                 case SET_PARAM:
995                         if (S_ISREG(st.st_mode)) {
996                                 rc2 = write_param(paths.gl_pathv[i],
997                                                   param_name, popt, value);
998                                 if (rc2 < 0 && rc == 0)
999                                         rc = rc2;
1000                         }
1001                         break;
1002                 case LIST_PARAM:
1003                         /**
1004                          * For the upstream client the parameter files locations
1005                          * are split between under both /sys/kernel/debug/lustre
1006                          * and /sys/fs/lustre. The parameter files containing
1007                          * small amounts of data, less than a page in size, are
1008                          * located under /sys/fs/lustre and in the case of large
1009                          * parameter data files, think stats for example, are
1010                          * located in the debugfs tree. Since the files are split
1011                          * across two trees the directories are often duplicated
1012                          * which means these directories are listed twice which
1013                          * leads to duplicate output to the user. To avoid
1014                          * scanning a directory twice we have to cache any
1015                          * directory and check if a search has been requested
1016                          * twice.
1017                          */
1018                         for (j = 0; j < dup_count; j++) {
1019                                 if (!strcmp(dup_cache[j], param_name))
1020                                         break;
1021                         }
1022                         if (j != dup_count) {
1023                                 free(param_name);
1024                                 param_name = NULL;
1025                                 continue;
1026                         }
1027                         dup_cache[dup_count++] = strdup(param_name);
1028
1029                         if (popt->po_show_path)
1030                                 printf("%s\n", param_name);
1031                         break;
1032                 }
1033
1034                 /* Only directories are searched recursively if
1035                  * requested by the user */
1036                 if (!S_ISDIR(st.st_mode) || !popt->po_recursive) {
1037                         free(param_name);
1038                         param_name = NULL;
1039                         continue;
1040                 }
1041
1042                 /* Turn param_name into file path format */
1043                 rc2 = clean_path(popt, param_name);
1044                 if (rc2 < 0) {
1045                         fprintf(stderr, "error: %s: cleaning '%s': %s\n",
1046                                 opname, param_name, strerror(-rc2));
1047                         free(param_name);
1048                         param_name = NULL;
1049                         if (rc == 0)
1050                                 rc = rc2;
1051                         continue;
1052                 }
1053
1054                 /* Use param_name to grab subdirectory tree from full path */
1055                 snprintf(param_dir, sizeof(param_dir), "/%s", param_name);
1056                 tmp = strstr(paths.gl_pathv[i], param_dir);
1057
1058                 /* cleanup paramname now that we are done with it */
1059                 free(param_name);
1060                 param_name = NULL;
1061                 memset(&param_dir, '\0', sizeof(param_dir));
1062
1063                 /* Shouldn't happen but just in case */
1064                 if (tmp == NULL) {
1065                         if (rc == 0)
1066                                 rc = -EINVAL;
1067                         continue;
1068                 }
1069                 tmp++;
1070
1071                 rc2 = snprintf(pathname, sizeof(pathname), "%s/*", tmp);
1072                 if (rc2 < 0) {
1073                         /* snprintf() should never an error, and if it does
1074                          * there isn't much point trying to use fprintf() */
1075                         continue;
1076                 }
1077                 if (rc2 >= sizeof(pathname)) {
1078                         fprintf(stderr, "error: %s: overflow processing '%s'\n",
1079                                 opname, pathname);
1080                         if (rc == 0)
1081                                 rc = -EINVAL;
1082                         continue;
1083                 }
1084
1085                 rc2 = param_display(popt, pathname, value, mode);
1086                 if (rc2 != 0 && rc2 != -ENOENT) {
1087                         /* errors will be printed by param_display() */
1088                         if (rc == 0)
1089                                 rc = rc2;
1090                         continue;
1091                 }
1092         }
1093
1094         for (i = 0; i < dup_count; i++)
1095                 free(dup_cache[i]);
1096         free(dup_cache);
1097 out_param:
1098         llapi_param_paths_free(&paths);
1099         return rc;
1100 }
1101
1102 static int listparam_cmdline(int argc, char **argv, struct param_opts *popt)
1103 {
1104         int ch;
1105
1106         popt->po_show_path = 1;
1107         popt->po_only_path = 1;
1108
1109         while ((ch = getopt(argc, argv, "FRD")) != -1) {
1110                 switch (ch) {
1111                 case 'F':
1112                         popt->po_show_type = 1;
1113                         break;
1114                 case 'R':
1115                         popt->po_recursive = 1;
1116                         break;
1117                 case 'D':
1118                         popt->po_only_dir = 1;
1119                         break;
1120                 default:
1121                         return -1;
1122                 }
1123         }
1124
1125         return optind;
1126 }
1127
1128 int jt_lcfg_listparam(int argc, char **argv)
1129 {
1130         int rc = 0, index, i;
1131         struct param_opts popt;
1132         char *path;
1133
1134         memset(&popt, 0, sizeof(popt));
1135         index = listparam_cmdline(argc, argv, &popt);
1136         if (index < 0 || index >= argc)
1137                 return CMD_HELP;
1138
1139         for (i = index; i < argc; i++) {
1140                 int rc2;
1141
1142                 path = argv[i];
1143
1144                 rc2 = clean_path(&popt, path);
1145                 if (rc2 < 0) {
1146                         fprintf(stderr, "error: %s: cleaning '%s': %s\n",
1147                                 jt_cmdname(argv[0]), path, strerror(-rc2));
1148                         if (rc == 0)
1149                                 rc = rc2;
1150                         continue;
1151                 }
1152
1153                 rc2 = param_display(&popt, path, NULL, LIST_PARAM);
1154                 if (rc2 < 0) {
1155                         fprintf(stderr, "error: %s: listing '%s': %s\n",
1156                                 jt_cmdname(argv[0]), path, strerror(-rc2));
1157                         if (rc == 0)
1158                                 rc = rc2;
1159                         continue;
1160                 }
1161         }
1162
1163         return rc;
1164 }
1165
1166 static int getparam_cmdline(int argc, char **argv, struct param_opts *popt)
1167 {
1168         int ch;
1169
1170         popt->po_show_path = 1;
1171
1172         while ((ch = getopt(argc, argv, "FnNR")) != -1) {
1173                 switch (ch) {
1174                 case 'F':
1175                         popt->po_show_type = 1;
1176                         break;
1177                 case 'n':
1178                         popt->po_show_path = 0;
1179                         break;
1180                 case 'N':
1181                         popt->po_only_path = 1;
1182                         break;
1183                 case 'R':
1184                         popt->po_recursive = 1;
1185                         break;
1186                 default:
1187                         return -1;
1188                 }
1189         }
1190
1191         return optind;
1192 }
1193
1194 int jt_lcfg_getparam(int argc, char **argv)
1195 {
1196         int rc = 0, index, i;
1197         struct param_opts popt;
1198         char *path;
1199
1200         memset(&popt, 0, sizeof(popt));
1201         index = getparam_cmdline(argc, argv, &popt);
1202         if (index < 0 || index >= argc)
1203                 return CMD_HELP;
1204
1205         for (i = index; i < argc; i++) {
1206                 int rc2;
1207
1208                 path = argv[i];
1209
1210                 rc2 = clean_path(&popt, path);
1211                 if (rc2 < 0) {
1212                         fprintf(stderr, "error: %s: cleaning '%s': %s\n",
1213                                 jt_cmdname(argv[0]), path, strerror(-rc2));
1214                         if (rc == 0)
1215                                 rc = rc2;
1216                         continue;
1217                 }
1218
1219                 rc2 = param_display(&popt, path, NULL,
1220                                    popt.po_only_path ? LIST_PARAM : GET_PARAM);
1221                 if (rc2 < 0) {
1222                         if (rc == 0)
1223                                 rc = rc2;
1224                         continue;
1225                 }
1226         }
1227
1228         return rc;
1229 }
1230
1231 /**
1232  * Output information about nodemaps.
1233  * \param       argc            number of args
1234  * \param       argv[]          variable string arguments
1235  *
1236  * [list|nodemap_name|all]      \a list will list all nodemaps (default).
1237  *                              Specifying a \a nodemap_name will
1238  *                              display info about that specific nodemap.
1239  *                              \a all will display info for all nodemaps.
1240  * \retval                      0 on success
1241  */
1242 int jt_nodemap_info(int argc, char **argv)
1243 {
1244         const char              usage_str[] = "usage: nodemap_info "
1245                                               "[list|nodemap_name|all]\n";
1246         struct param_opts       popt;
1247         int                     rc = 0;
1248
1249         memset(&popt, 0, sizeof(popt));
1250         popt.po_show_path = 1;
1251
1252         if (argc > 2) {
1253                 fprintf(stderr, usage_str);
1254                 return -1;
1255         }
1256
1257         if (argc == 1 || strcmp("list", argv[1]) == 0) {
1258                 popt.po_only_path = 1;
1259                 popt.po_only_dir = 1;
1260                 rc = param_display(&popt, "nodemap/*", NULL, LIST_PARAM);
1261         } else if (strcmp("all", argv[1]) == 0) {
1262                 rc = param_display(&popt, "nodemap/*/*", NULL, LIST_PARAM);
1263         } else {
1264                 char    pattern[PATH_MAX];
1265
1266                 snprintf(pattern, sizeof(pattern), "nodemap/%s/*", argv[1]);
1267                 rc = param_display(&popt, pattern, NULL, LIST_PARAM);
1268                 if (rc == -ESRCH)
1269                         fprintf(stderr, "error: nodemap_info: cannot find "
1270                                         "nodemap %s\n", argv[1]);
1271         }
1272         return rc;
1273 }
1274
1275 static int setparam_cmdline(int argc, char **argv, struct param_opts *popt)
1276 {
1277         int ch;
1278
1279         popt->po_show_path = 1;
1280         popt->po_only_path = 0;
1281         popt->po_show_type = 0;
1282         popt->po_recursive = 0;
1283         popt->po_perm = 0;
1284         popt->po_delete = 0;
1285         popt->po_file = 0;
1286
1287         while ((ch = getopt(argc, argv, "nPdF")) != -1) {
1288                 switch (ch) {
1289                 case 'n':
1290                         popt->po_show_path = 0;
1291                         break;
1292                 case 'P':
1293                         popt->po_perm = 1;
1294                         break;
1295                 case 'd':
1296                         popt->po_delete = 1;
1297                         break;
1298                 case 'F':
1299                         popt->po_file = 1;
1300                         break;
1301                 default:
1302                         return -1;
1303                 }
1304         }
1305         return optind;
1306 }
1307
1308 enum paramtype {
1309         PT_NONE = 0,
1310         PT_SETPARAM,
1311         PT_CONFPARAM
1312 };
1313
1314
1315 #define PS_NONE 0
1316 #define PS_PARAM_FOUND 1
1317 #define PS_PARAM_SET 2
1318 #define PS_VAL_FOUND 4
1319 #define PS_VAL_SET 8
1320 #define PS_DEVICE_FOUND 16
1321 #define PS_DEVICE_SET 32
1322
1323 #define PARAM_SZ 256
1324
1325 static struct cfg_type_data {
1326         enum paramtype ptype;
1327         char *type_name;
1328 } cfg_type_table[] = {
1329         { PT_SETPARAM, "set_param" },
1330         { PT_CONFPARAM, "conf_param" },
1331         { PT_NONE, "none" }
1332 };
1333
1334 static struct cfg_stage_data {
1335         int pstage;
1336         char *stage_name;
1337 } cfg_stage_table[] = {
1338         { PS_PARAM_FOUND, "parameter" },
1339         { PS_VAL_FOUND, "value" },
1340         { PS_DEVICE_FOUND, "device" },
1341         { PS_NONE, "none" }
1342 };
1343
1344
1345 void conf_to_set_param(enum paramtype confset, const char *param,
1346                        const char *device, char *buf,
1347                        int bufsize)
1348 {
1349         char *tmp;
1350
1351         if (confset == PT_SETPARAM) {
1352                 strncpy(buf, param, bufsize);
1353                 return;
1354         }
1355
1356         /*
1357          * sys.* params are top level, we just need to trim the sys.
1358          */
1359         tmp = strstr(param, "sys.");
1360         if (tmp != NULL) {
1361                 tmp += 4;
1362                 strncpy(buf, tmp, bufsize);
1363                 return;
1364         }
1365
1366         /*
1367          * parameters look like type.parameter, we need to stick the device
1368          * in the middle.  Example combine mdt.identity_upcall with device
1369          * lustre-MDT0000 for mdt.lustre-MDT0000.identity_upcall
1370          */
1371
1372         tmp = strchrnul(param, '.');
1373         snprintf(buf, tmp - param + 1, "%s", param);
1374         buf += tmp - param;
1375         bufsize -= tmp - param;
1376         snprintf(buf, bufsize, ".%s%s", device, tmp);
1377 }
1378
1379 int lcfg_setparam_yaml(char *func, char *filename)
1380 {
1381         FILE *file;
1382         yaml_parser_t parser;
1383         yaml_token_t token;
1384         int rc = 0;
1385
1386         enum paramtype confset = PT_NONE;
1387         int param = PS_NONE;
1388         char *tmp;
1389         char parameter[PARAM_SZ + 1];
1390         char value[PARAM_SZ + 1];
1391         char device[PARAM_SZ + 1];
1392
1393         file = fopen(filename, "rb");
1394         yaml_parser_initialize(&parser);
1395         yaml_parser_set_input_file(&parser, file);
1396
1397         /*
1398          * Search tokens for conf_param or set_param
1399          * The token after "parameter" goes into parameter
1400          * The token after "value" goes into value
1401          * when we have all 3, create param=val and call the
1402          * appropriate function for set/conf param
1403          */
1404         while (token.type != YAML_STREAM_END_TOKEN && rc == 0) {
1405                 int i;
1406
1407                 yaml_token_delete(&token);
1408                 if (!yaml_parser_scan(&parser, &token)) {
1409                         rc = 1;
1410                         break;
1411                 }
1412
1413                 if (token.type != YAML_SCALAR_TOKEN)
1414                         continue;
1415
1416                 for (i = 0; cfg_type_table[i].ptype != PT_NONE; i++) {
1417                         if (!strncmp((char *)token.data.alias.value,
1418                                      cfg_type_table[i].type_name,
1419                                      strlen(cfg_type_table[i].type_name))) {
1420                                 confset = cfg_type_table[i].ptype;
1421                                 break;
1422                         }
1423                 }
1424
1425                 if (confset == PT_NONE)
1426                         continue;
1427
1428                 for (i = 0; cfg_stage_table[i].pstage != PS_NONE; i++) {
1429                         if (!strncmp((char *)token.data.alias.value,
1430                                      cfg_stage_table[i].stage_name,
1431                                      strlen(cfg_stage_table[i].stage_name))) {
1432                                 param |= cfg_stage_table[i].pstage;
1433                                 break;
1434                         }
1435                 }
1436
1437                 if (cfg_stage_table[i].pstage != PS_NONE)
1438                         continue;
1439
1440                 if (param & PS_PARAM_FOUND) {
1441                         conf_to_set_param(confset,
1442                                           (char *)token.data.alias.value,
1443                                           device, parameter, PARAM_SZ);
1444                         param |= PS_PARAM_SET;
1445                         param &= ~PS_PARAM_FOUND;
1446
1447                         /*
1448                          * we're getting parameter: param=val
1449                          * copy val and mark that we've got it in case
1450                          * there is no value: tag
1451                          */
1452                         tmp = strchrnul(parameter, '=');
1453                         if (*tmp == '=') {
1454                                 strncpy(value, tmp + 1, sizeof(value) - 1);
1455                                 *tmp = '\0';
1456                                 param |= PS_VAL_SET;
1457                         } else {
1458                                 continue;
1459                         }
1460                 } else if (param & PS_VAL_FOUND) {
1461                         strncpy(value, (char *)token.data.alias.value,
1462                                 PARAM_SZ);
1463                         param |= PS_VAL_SET;
1464                         param &= ~PS_VAL_FOUND;
1465                 } else if (param & PS_DEVICE_FOUND) {
1466                         strncpy(device, (char *)token.data.alias.value,
1467                                 PARAM_SZ);
1468                         param |= PS_DEVICE_SET;
1469                         param &= ~PS_DEVICE_FOUND;
1470                 }
1471
1472                 if (confset && param & PS_VAL_SET && param & PS_PARAM_SET) {
1473                         int size = strlen(parameter) + strlen(value) + 2;
1474                         char *buf = malloc(size);
1475
1476                         if (buf == NULL) {
1477                                 rc = 2;
1478                                 break;
1479                         }
1480                         snprintf(buf, size, "%s=%s", parameter, value);
1481
1482                         printf("set_param: %s\n", buf);
1483                         rc = lcfg_setparam_perm(func, buf);
1484
1485                         confset = PT_NONE;
1486                         param = PS_NONE;
1487                         parameter[0] = '\0';
1488                         value[0] = '\0';
1489                         device[0] = '\0';
1490                         free(buf);
1491                 }
1492         }
1493
1494         yaml_parser_delete(&parser);
1495         fclose(file);
1496
1497         return rc;
1498 }
1499
1500 int jt_lcfg_setparam(int argc, char **argv)
1501 {
1502         int rc = 0, index, i;
1503         struct param_opts popt;
1504         char *path = NULL, *value = NULL;
1505
1506         memset(&popt, 0, sizeof(popt));
1507         index = setparam_cmdline(argc, argv, &popt);
1508         if (index < 0 || index >= argc)
1509                 return CMD_HELP;
1510
1511         if (popt.po_perm)
1512                 /* We can't delete parameters that were
1513                  * set with old conf_param interface */
1514                 return jt_lcfg_setparam_perm(argc, argv, &popt);
1515
1516         if (popt.po_file)
1517                 return lcfg_setparam_yaml(argv[0], argv[index]);
1518
1519         for (i = index; i < argc; i++) {
1520                 int rc2;
1521                 path = argv[i];
1522
1523                 value = strchr(path, '=');
1524                 if (value != NULL) {
1525                         /* format: set_param a=b */
1526                         *value = '\0';
1527                         value++;
1528                         if (*value == '\0') {
1529                                 fprintf(stderr,
1530                                         "error: %s: setting %s: no value\n",
1531                                         jt_cmdname(argv[0]), path);
1532                                 if (rc == 0)
1533                                         rc = -EINVAL;
1534                                 continue;
1535                         }
1536                 } else {
1537                         /* format: set_param a b */
1538                         i++;
1539                         if (i >= argc) {
1540                                 fprintf(stderr,
1541                                         "error: %s: setting %s: no value\n",
1542                                         jt_cmdname(argv[0]), path);
1543                                 if (rc == 0)
1544                                         rc = -EINVAL;
1545                                 break;
1546                         } else {
1547                                 value = argv[i];
1548                         }
1549                 }
1550
1551                 rc2 = clean_path(&popt, path);
1552                 if (rc2 < 0) {
1553                         fprintf(stderr, "error: %s: cleaning %s: %s\n",
1554                                 jt_cmdname(argv[0]), path, strerror(-rc2));
1555                         if (rc == 0)
1556                                 rc = rc2;
1557                         continue;
1558                 }
1559
1560                 rc2 = param_display(&popt, path, value, SET_PARAM);
1561                 if (rc == 0)
1562                         rc = rc2;
1563         }
1564
1565         return rc;
1566 }