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