Whamcloud - gitweb
LU-14090 mgs: no local logs flag
[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) {
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) {
165                 fprintf(stderr,
166                         "%s: please use 'device name' to set the 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         return jt_lcfg_ioctl(&bufs, argv[0], LCFG_SETUP);
180 }
181
182 int jt_obd_detach(int argc, char **argv)
183 {
184         struct lustre_cfg_bufs bufs;
185
186         if (!lcfg_devname) {
187                 fprintf(stderr,
188                         "%s: please use 'device name' to set the device name for config commands.\n",
189                         jt_cmdname(argv[0]));
190                 return -EINVAL;
191         }
192
193         lustre_cfg_bufs_reset(&bufs, lcfg_devname);
194
195         if (argc != 1)
196                 return CMD_HELP;
197
198         return jt_lcfg_ioctl(&bufs, argv[0], LCFG_DETACH);
199 }
200
201 int jt_obd_cleanup(int argc, char **argv)
202 {
203         struct lustre_cfg_bufs bufs;
204         char force = 'F';
205         char failover = 'A';
206         char flags[3] = { 0 };
207         int flag_cnt = 0, n;
208
209         if (!lcfg_devname) {
210                 fprintf(stderr,
211                         "%s: please use 'device name' to set the device name for config commands.\n",
212                         jt_cmdname(argv[0]));
213                 return -EINVAL;
214         }
215
216         lustre_cfg_bufs_reset(&bufs, lcfg_devname);
217
218         if (argc < 1 || argc > 3)
219                 return CMD_HELP;
220
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         return jt_lcfg_ioctl(&bufs, argv[0], LCFG_CLEANUP);
240 }
241
242 static
243 int do_add_uuid(char *func, char *uuid, lnet_nid_t nid)
244 {
245         int rc;
246         struct lustre_cfg_bufs bufs;
247         struct lustre_cfg *lcfg;
248
249         lustre_cfg_bufs_reset(&bufs, lcfg_devname);
250         if (uuid)
251                 lustre_cfg_bufs_set_string(&bufs, 1, uuid);
252
253         lcfg = malloc(lustre_cfg_len(bufs.lcfg_bufcount, bufs.lcfg_buflen));
254         if (!lcfg) {
255                 rc = -ENOMEM;
256         } else {
257                 lustre_cfg_init(lcfg, LCFG_ADD_UUID, &bufs);
258                 lcfg->lcfg_nid = nid;
259
260                 rc = lcfg_ioctl(func, OBD_DEV_ID, lcfg);
261                 free(lcfg);
262         }
263         if (rc) {
264                 fprintf(stderr, "IOC_PORTAL_ADD_UUID failed: %s\n",
265                         strerror(errno));
266                 return -1;
267         }
268
269         if (uuid)
270                 printf("Added uuid %s: %s\n", uuid, libcfs_nid2str(nid));
271
272         return 0;
273 }
274
275 int jt_lcfg_add_uuid(int argc, char **argv)
276 {
277         lnet_nid_t nid;
278
279         if (argc != 3)
280                 return CMD_HELP;
281
282         nid = libcfs_str2nid(argv[2]);
283         if (nid == LNET_NID_ANY) {
284                 fprintf(stderr, "Can't parse NID %s\n", argv[2]);
285                 return (-1);
286         }
287
288         return do_add_uuid(argv[0], argv[1], nid);
289 }
290
291 int jt_lcfg_del_uuid(int argc, char **argv)
292 {
293         struct lustre_cfg_bufs bufs;
294
295         if (argc != 2) {
296                 fprintf(stderr, "usage: %s <uuid>\n", argv[0]);
297                 return 0;
298         }
299
300         lustre_cfg_bufs_reset(&bufs, lcfg_devname);
301         if (strcmp(argv[1], "_all_"))
302                 lustre_cfg_bufs_set_string(&bufs, 1, argv[1]);
303
304         return jt_lcfg_ioctl(&bufs, argv[0], LCFG_DEL_UUID);
305 }
306
307 int jt_lcfg_del_mount_option(int argc, char **argv)
308 {
309         struct lustre_cfg_bufs bufs;
310
311         if (argc != 2)
312                 return CMD_HELP;
313
314         lustre_cfg_bufs_reset(&bufs, lcfg_devname);
315
316         /* profile name */
317         lustre_cfg_bufs_set_string(&bufs, 1, argv[1]);
318
319         return jt_lcfg_ioctl(&bufs, argv[0], LCFG_DEL_MOUNTOPT);
320 }
321
322 int jt_lcfg_set_timeout(int argc, char **argv)
323 {
324         int rc;
325         struct lustre_cfg_bufs bufs;
326         struct lustre_cfg *lcfg;
327
328         fprintf(stderr,
329                 "%s has been deprecated. Use conf_param instead.\ne.g. conf_param lustre-MDT0000 obd_timeout=50\n",
330                 jt_cmdname(argv[0]));
331         return CMD_HELP;
332
333         if (argc != 2)
334                 return CMD_HELP;
335
336         lustre_cfg_bufs_reset(&bufs, lcfg_devname);
337
338         lcfg = malloc(lustre_cfg_len(bufs.lcfg_bufcount, bufs.lcfg_buflen));
339         if (!lcfg) {
340                 rc = -ENOMEM;
341         } else {
342                 lustre_cfg_init(lcfg, LCFG_SET_TIMEOUT, &bufs);
343                 lcfg->lcfg_num = atoi(argv[1]);
344
345                 rc = lcfg_ioctl(argv[0], OBD_DEV_ID, lcfg);
346                 free(lcfg);
347         }
348         if (rc < 0) {
349                 fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
350                         strerror(rc = errno));
351         }
352         return rc;
353 }
354
355 int jt_lcfg_add_conn(int argc, char **argv)
356 {
357         struct lustre_cfg_bufs bufs;
358         struct lustre_cfg *lcfg;
359         int priority;
360         int rc;
361
362         if (argc == 2)
363                 priority = 0;
364         else if (argc == 3)
365                 priority = 1;
366         else
367                 return CMD_HELP;
368
369         if (!lcfg_devname) {
370                 fprintf(stderr,
371                         "%s: please use 'device name' to set the device name for config commands.\n",
372                         jt_cmdname(argv[0]));
373                 return -EINVAL;
374         }
375
376         lustre_cfg_bufs_reset(&bufs, lcfg_devname);
377
378         lustre_cfg_bufs_set_string(&bufs, 1, argv[1]);
379
380         lcfg = malloc(lustre_cfg_len(bufs.lcfg_bufcount, bufs.lcfg_buflen));
381         if (!lcfg) {
382                 rc = -ENOMEM;
383         } else {
384                 lustre_cfg_init(lcfg, LCFG_ADD_CONN, &bufs);
385                 lcfg->lcfg_num = priority;
386
387                 rc = lcfg_ioctl(argv[0], OBD_DEV_ID, lcfg);
388                 free(lcfg);
389         }
390         if (rc < 0) {
391                 fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
392                         strerror(rc = errno));
393         }
394
395         return rc;
396 }
397
398 int jt_lcfg_del_conn(int argc, char **argv)
399 {
400         struct lustre_cfg_bufs bufs;
401
402         if (argc != 2)
403                 return CMD_HELP;
404
405         if (!lcfg_devname) {
406                 fprintf(stderr,
407                         "%s: please use 'device name' to set the device name for config commands.\n",
408                         jt_cmdname(argv[0]));
409                 return -EINVAL;
410         }
411
412         lustre_cfg_bufs_reset(&bufs, lcfg_devname);
413
414         /* connection uuid */
415         lustre_cfg_bufs_set_string(&bufs, 1, argv[1]);
416
417         return jt_lcfg_ioctl(&bufs, argv[0], LCFG_DEL_MOUNTOPT);
418 }
419
420 /* Param set locally, directly on target */
421 int jt_lcfg_param(int argc, char **argv)
422 {
423         struct lustre_cfg_bufs bufs;
424         int i;
425
426         if (argc >= LUSTRE_CFG_MAX_BUFCOUNT)
427                 return CMD_HELP;
428
429         lustre_cfg_bufs_reset(&bufs, NULL);
430
431         for (i = 1; i < argc; i++)
432                 lustre_cfg_bufs_set_string(&bufs, i, argv[i]);
433
434         return jt_lcfg_ioctl(&bufs, argv[0], LCFG_PARAM);
435 }
436
437 struct param_opts {
438         unsigned int po_only_path:1;
439         unsigned int po_show_path:1;
440         unsigned int po_show_type:1;
441         unsigned int po_recursive:1;
442         unsigned int po_perm:1;
443         unsigned int po_delete:1;
444         unsigned int po_only_dir:1;
445         unsigned int po_file:1;
446 };
447
448 int lcfg_setparam_perm(char *func, char *buf)
449 {
450         int rc = 0;
451         struct lustre_cfg_bufs bufs;
452         struct lustre_cfg *lcfg;
453
454         lustre_cfg_bufs_reset(&bufs, NULL);
455         /*
456          * This same command would be executed on all nodes, many
457          * of which should fail (silently) because they don't have
458          * that proc file existing locally. There would be no
459          * preprocessing on the MGS to try to figure out which
460          * parameter files to add this to, there would be nodes
461          * processing on the cluster nodes to try to figure out
462          * if they are the intended targets. They will blindly
463          * try to set the parameter, and ENOTFOUND means it wasn't
464          * for them.
465          * Target name "general" means call on all targets. It is
466          * left here in case some filtering will be added in
467          * future.
468          */
469         lustre_cfg_bufs_set_string(&bufs, 0, "general");
470
471         lustre_cfg_bufs_set_string(&bufs, 1, buf);
472
473         lcfg = malloc(lustre_cfg_len(bufs.lcfg_bufcount,
474                                      bufs.lcfg_buflen));
475         if (!lcfg) {
476                 rc = -ENOMEM;
477                 fprintf(stderr, "error: allocating lcfg for %s: %s\n",
478                         jt_cmdname(func), strerror(rc));
479
480         } else {
481                 lustre_cfg_init(lcfg, LCFG_SET_PARAM, &bufs);
482                 rc = lcfg_mgs_ioctl(func, OBD_DEV_ID, lcfg);
483                 if (rc != 0)
484                         fprintf(stderr, "error: executing %s: %s\n",
485                                 jt_cmdname(func), strerror(errno));
486                 free(lcfg);
487         }
488
489         return rc;
490 }
491
492 /*
493  * Param set to single log file, used by all clients and servers.
494  * This should be loaded after the individual config logs.
495  * Called from set param with -P option.
496  */
497 static int jt_lcfg_setparam_perm(int argc, char **argv,
498                                  struct param_opts *popt)
499 {
500         int rc;
501         int i;
502         int first_param;
503         char *buf = NULL;
504         int len;
505
506         first_param = optind;
507         if (first_param < 0 || first_param >= argc)
508                 return CMD_HELP;
509
510         for (i = first_param, rc = 0; i < argc; i++) {
511                 len = strlen(argv[i]);
512
513                 buf = argv[i];
514
515                 /* put an '=' on the end in case it doesn't have one */
516                 if (popt->po_delete && argv[i][len - 1] != '=') {
517                         buf = malloc(len + 1);
518                         if (!buf) {
519                                 rc = -ENOMEM;
520                                 break;
521                         }
522                         sprintf(buf, "%s=", argv[i]);
523                 }
524
525                 rc = lcfg_setparam_perm(argv[0], buf);
526
527                 if (buf != argv[i])
528                         free(buf);
529         }
530
531         return rc;
532 }
533
534 int lcfg_conf_param(char *func, char *buf)
535 {
536         int rc;
537         struct lustre_cfg_bufs bufs;
538         struct lustre_cfg *lcfg;
539
540         lustre_cfg_bufs_reset(&bufs, NULL);
541         lustre_cfg_bufs_set_string(&bufs, 1, buf);
542
543         /* We could put other opcodes here. */
544         lcfg = malloc(lustre_cfg_len(bufs.lcfg_bufcount, bufs.lcfg_buflen));
545         if (!lcfg) {
546                 rc = -ENOMEM;
547         } else {
548                 lustre_cfg_init(lcfg, LCFG_PARAM, &bufs);
549                 rc = lcfg_mgs_ioctl(func, OBD_DEV_ID, lcfg);
550                 if (rc < 0)
551                         rc = -errno;
552                 free(lcfg);
553         }
554
555         return rc;
556 }
557
558 /*
559  * Param set in config log on MGS
560  * conf_param key=value
561  *
562  * Note we can actually send mgc conf_params from clients, but currently
563  * that's only done for default file striping (see ll_send_mgc_param),
564  * and not here.
565  *
566  * After removal of a parameter (-d) Lustre will use the default
567  * AT NEXT REBOOT, not immediately.
568  */
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) {
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) {
650                 tmp = strstr(filename, "/lnet/");
651                 if (tmp)
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)
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) {
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) {
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) {
749                 char *find_nid = strdup(path);
750                 lnet_nid_t nid;
751
752                 if (!find_nid)
753                         return -ENOMEM;
754
755                 /*
756                  * First we need to chop off rest after nid string.
757                  * Since find_nid is a clone of path it better have '@'
758                  */
759                 tmp = strchr(find_nid, '@');
760                 tmp = strchr(tmp, '.');
761                 if (tmp)
762                         *tmp = '\0';
763
764                 /* Now chop off the front. */
765                 for (tmp = strchr(find_nid, '.'); tmp != NULL;
766                      tmp = strchr(tmp, '.')) {
767                         /* Remove MGC to make it NID format */
768                         if (!strncmp(++tmp, "MGC", 3))
769                                 tmp += 3;
770
771                         nid = libcfs_str2nid(tmp);
772                         if (nid != LNET_NID_ANY) {
773                                 nidstr = libcfs_nid2str(nid);
774                                 if (!nidstr)
775                                         return -EINVAL;
776                                 break;
777                         }
778                 }
779                 free(find_nid);
780         }
781
782         /* replace param '.' with '/' */
783         for (tmp = strchr(path, '.'); tmp != NULL; tmp = strchr(tmp, '.')) {
784                 *tmp++ = '/';
785
786                 /* Remove MGC to make it NID format */
787                 if (!strncmp(tmp, "MGC", 3))
788                         tmp += 3;
789
790                 /*
791                  * There exist cases where some of the subdirectories of the
792                  * the parameter tree has embedded in its name a NID string.
793                  * This means that it is possible that these subdirectories
794                  * could have actual '.' in its name. If this is the case we
795                  * don't want to blindly replace the '.' with '/'.
796                  */
797                 if (nidstr) {
798                         char *match = strstr(tmp, nidstr);
799
800                         if (tmp == match)
801                                 tmp += strlen(nidstr);
802                 }
803         }
804
805         return 0;
806 }
807
808 /**
809  * The application lctl can perform three operations for lustre
810  * tunables. This enum defines those three operations which are
811  *
812  * 1) LIST_PARAM        - list available tunables
813  * 2) GET_PARAM         - report the current setting of a tunable
814  * 3) SET_PARAM         - set the tunable to a new value
815  */
816 enum parameter_operation {
817         LIST_PARAM,
818         GET_PARAM,
819         SET_PARAM,
820 };
821
822 char *parameter_opname[] = {
823         [LIST_PARAM] = "list_param",
824         [GET_PARAM] = "get_param",
825         [SET_PARAM] = "set_param",
826 };
827
828 /**
829  * Read the value of parameter
830  *
831  * \param[in]   path            full path to the parameter
832  * \param[in]   param_name      lctl parameter format of the
833  *                              parameter path
834  * \param[in]   popt            set/get param options
835  *
836  * \retval 0 on success.
837  * \retval -errno on error.
838  */
839 static int
840 read_param(const char *path, const char *param_name, struct param_opts *popt)
841 {
842         int rc = 0;
843         char *buf = NULL;
844         size_t buflen;
845
846         rc = llapi_param_get_value(path, &buf, &buflen);
847         if (rc != 0) {
848                 fprintf(stderr,
849                         "error: read_param: \'%s\': %s\n",
850                         path, strerror(-rc));
851                 goto free_buf;
852         }
853         /* don't print anything for empty files */
854         if (buf[0] == '\0')
855                 goto free_buf;
856
857         if (popt->po_show_path) {
858                 bool longbuf;
859
860                 longbuf = strnchr(buf, buflen - 1, '\n') != NULL ||
861                         buflen + strlen(param_name) >= 80;
862                 printf("%s=%s", param_name, longbuf ? "\n" : "");
863         }
864         printf("%s", buf);
865
866 free_buf:
867         free(buf);
868         return rc;
869 }
870
871 /**
872  * Set a parameter to a specified value
873  *
874  * \param[in] path              full path to the parameter
875  * \param[in] param_name        lctl parameter format of the parameter path
876  * \param[in] popt              set/get param options
877  * \param[in] value             value to set the parameter to
878  *
879  * \retval number of bytes written on success.
880  * \retval -errno on error.
881  */
882 static int
883 write_param(const char *path, const char *param_name, struct param_opts *popt,
884             const char *value)
885 {
886         int fd, rc = 0;
887         ssize_t count;
888
889         if (!value)
890                 return -EINVAL;
891
892         /* Write the new value to the file */
893         fd = open(path, O_WRONLY);
894         if (fd < 0) {
895                 rc = -errno;
896                 fprintf(stderr, "error: set_param: opening '%s': %s\n",
897                         path, strerror(errno));
898                 return rc;
899         }
900
901         count = write(fd, value, strlen(value));
902         if (count < 0) {
903                 rc = -errno;
904                 if (errno != EIO) {
905                         fprintf(stderr, "error: set_param: setting %s=%s: %s\n",
906                                 path, value, strerror(errno));
907                 }
908         } else if (count < strlen(value)) { /* Truncate case */
909                 rc = -EINVAL;
910                 fprintf(stderr,
911                         "error: set_param: setting %s=%s: wrote only %zd\n",
912                         path, value, count);
913         } else if (popt->po_show_path) {
914                 printf("%s=%s\n", param_name, value);
915         }
916         close(fd);
917
918         return rc;
919 }
920
921 /**
922  * Perform a read, write or just a listing of a parameter
923  *
924  * \param[in] popt              list,set,get parameter options
925  * \param[in] pattern           search filter for the path of the parameter
926  * \param[in] value             value to set the parameter if write operation
927  * \param[in] mode              what operation to perform with the parameter
928  *
929  * \retval number of bytes written on success.
930  * \retval -errno on error and prints error message.
931  */
932 static int
933 param_display(struct param_opts *popt, char *pattern, char *value,
934               enum parameter_operation mode)
935 {
936         int dup_count = 0;
937         char **dup_cache;
938         glob_t paths;
939         char *opname = parameter_opname[mode];
940         int rc, i;
941
942         rc = llapi_param_get_paths(pattern, &paths);
943         if (rc != 0) {
944                 rc = -errno;
945                 if (!popt->po_recursive) {
946                         fprintf(stderr, "error: %s: param_path '%s': %s\n",
947                                 opname, pattern, strerror(errno));
948                 }
949                 return rc;
950         }
951
952         dup_cache = calloc(paths.gl_pathc, sizeof(char *));
953         if (!dup_cache) {
954                 rc = -ENOMEM;
955                 fprintf(stderr,
956                         "error: %s: allocating '%s' dup_cache[%zd]: %s\n",
957                         opname, pattern, paths.gl_pathc, strerror(-rc));
958                 goto out_param;
959         }
960
961         for (i = 0; i < paths.gl_pathc; i++) {
962                 char *param_name = NULL, *tmp;
963                 char pathname[PATH_MAX], param_dir[PATH_MAX + 2];
964                 struct stat st;
965                 int rc2, j;
966
967                 if (stat(paths.gl_pathv[i], &st) == -1) {
968                         fprintf(stderr, "error: %s: stat '%s': %s\n",
969                                 opname, paths.gl_pathv[i], strerror(errno));
970                         if (rc == 0)
971                                 rc = -errno;
972                         continue;
973                 }
974
975                 if (popt->po_only_dir && !S_ISDIR(st.st_mode))
976                         continue;
977
978                 param_name = display_name(paths.gl_pathv[i], &st, popt);
979                 if (!param_name) {
980                         fprintf(stderr,
981                                 "error: %s: generating name for '%s': %s\n",
982                                 opname, paths.gl_pathv[i], strerror(ENOMEM));
983                         if (rc == 0)
984                                 rc = -ENOMEM;
985                         continue;
986                 }
987
988                 switch (mode) {
989                 case GET_PARAM:
990                         /* Read the contents of file to stdout */
991                         if (S_ISREG(st.st_mode)) {
992                                 rc2 = read_param(paths.gl_pathv[i], param_name,
993                                                  popt);
994                                 if (rc2 < 0 && rc == 0)
995                                         rc = rc2;
996                         }
997                         break;
998                 case SET_PARAM:
999                         if (S_ISREG(st.st_mode)) {
1000                                 rc2 = write_param(paths.gl_pathv[i],
1001                                                   param_name, popt, value);
1002                                 if (rc2 < 0 && rc == 0)
1003                                         rc = rc2;
1004                         }
1005                         break;
1006                 case LIST_PARAM:
1007                         /**
1008                          * For the upstream client the parameter files locations
1009                          * are split between under both /sys/kernel/debug/lustre
1010                          * and /sys/fs/lustre. The parameter files containing
1011                          * small amounts of data, less than a page in size, are
1012                          * located under /sys/fs/lustre and in the case of large
1013                          * parameter data files, think stats for example, are
1014                          * located in the debugfs tree. Since the files are
1015                          * split across two trees the directories are often
1016                          * duplicated which means these directories are listed
1017                          * twice which leads to duplicate output to the user.
1018                          * To avoid scanning a directory twice we have to cache
1019                          * any directory and check if a search has been
1020                          * requested twice.
1021                          */
1022                         for (j = 0; j < dup_count; j++) {
1023                                 if (!strcmp(dup_cache[j], param_name))
1024                                         break;
1025                         }
1026                         if (j != dup_count) {
1027                                 free(param_name);
1028                                 param_name = NULL;
1029                                 continue;
1030                         }
1031                         dup_cache[dup_count++] = strdup(param_name);
1032
1033                         if (popt->po_show_path)
1034                                 printf("%s\n", param_name);
1035                         break;
1036                 }
1037
1038                 /*
1039                  * Only directories are searched recursively if
1040                  * requested by the user
1041                  */
1042                 if (!S_ISDIR(st.st_mode) || !popt->po_recursive) {
1043                         free(param_name);
1044                         param_name = NULL;
1045                         continue;
1046                 }
1047
1048                 /* Turn param_name into file path format */
1049                 rc2 = clean_path(popt, param_name);
1050                 if (rc2 < 0) {
1051                         fprintf(stderr, "error: %s: cleaning '%s': %s\n",
1052                                 opname, param_name, strerror(-rc2));
1053                         free(param_name);
1054                         param_name = NULL;
1055                         if (rc == 0)
1056                                 rc = rc2;
1057                         continue;
1058                 }
1059
1060                 /* Use param_name to grab subdirectory tree from full path */
1061                 snprintf(param_dir, sizeof(param_dir), "/%s", param_name);
1062                 tmp = strstr(paths.gl_pathv[i], param_dir);
1063
1064                 /* cleanup paramname now that we are done with it */
1065                 free(param_name);
1066                 param_name = NULL;
1067                 memset(&param_dir, '\0', sizeof(param_dir));
1068
1069                 /* Shouldn't happen but just in case */
1070                 if (!tmp) {
1071                         if (rc == 0)
1072                                 rc = -EINVAL;
1073                         continue;
1074                 }
1075                 tmp++;
1076
1077                 rc2 = snprintf(pathname, sizeof(pathname), "%s/*", tmp);
1078                 if (rc2 < 0) {
1079                         /*
1080                          * snprintf() should never an error, and if it does
1081                          * there isn't much point trying to use fprintf()
1082                          */
1083                         continue;
1084                 }
1085                 if (rc2 >= sizeof(pathname)) {
1086                         fprintf(stderr, "error: %s: overflow processing '%s'\n",
1087                                 opname, pathname);
1088                         if (rc == 0)
1089                                 rc = -EINVAL;
1090                         continue;
1091                 }
1092
1093                 rc2 = param_display(popt, pathname, value, mode);
1094                 if (rc2 != 0 && rc2 != -ENOENT) {
1095                         /* errors will be printed by param_display() */
1096                         if (rc == 0)
1097                                 rc = rc2;
1098                         continue;
1099                 }
1100         }
1101
1102         for (i = 0; i < dup_count; i++)
1103                 free(dup_cache[i]);
1104         free(dup_cache);
1105 out_param:
1106         llapi_param_paths_free(&paths);
1107         return rc;
1108 }
1109
1110 static int listparam_cmdline(int argc, char **argv, struct param_opts *popt)
1111 {
1112         int ch;
1113
1114         popt->po_show_path = 1;
1115         popt->po_only_path = 1;
1116
1117         while ((ch = getopt(argc, argv, "FRD")) != -1) {
1118                 switch (ch) {
1119                 case 'F':
1120                         popt->po_show_type = 1;
1121                         break;
1122                 case 'R':
1123                         popt->po_recursive = 1;
1124                         break;
1125                 case 'D':
1126                         popt->po_only_dir = 1;
1127                         break;
1128                 default:
1129                         return -1;
1130                 }
1131         }
1132
1133         return optind;
1134 }
1135
1136 int jt_lcfg_listparam(int argc, char **argv)
1137 {
1138         int rc = 0, index, i;
1139         struct param_opts popt;
1140         char *path;
1141
1142         memset(&popt, 0, sizeof(popt));
1143         index = listparam_cmdline(argc, argv, &popt);
1144         if (index < 0 || index >= argc)
1145                 return CMD_HELP;
1146
1147         for (i = index; i < argc; i++) {
1148                 int rc2;
1149
1150                 path = argv[i];
1151
1152                 rc2 = clean_path(&popt, path);
1153                 if (rc2 < 0) {
1154                         fprintf(stderr, "error: %s: cleaning '%s': %s\n",
1155                                 jt_cmdname(argv[0]), path, strerror(-rc2));
1156                         if (rc == 0)
1157                                 rc = rc2;
1158                         continue;
1159                 }
1160
1161                 rc2 = param_display(&popt, path, NULL, LIST_PARAM);
1162                 if (rc2 < 0) {
1163                         fprintf(stderr, "error: %s: listing '%s': %s\n",
1164                                 jt_cmdname(argv[0]), path, strerror(-rc2));
1165                         if (rc == 0)
1166                                 rc = rc2;
1167                         continue;
1168                 }
1169         }
1170
1171         return rc;
1172 }
1173
1174 static int getparam_cmdline(int argc, char **argv, struct param_opts *popt)
1175 {
1176         int ch;
1177
1178         popt->po_show_path = 1;
1179
1180         while ((ch = getopt(argc, argv, "FnNR")) != -1) {
1181                 switch (ch) {
1182                 case 'F':
1183                         popt->po_show_type = 1;
1184                         break;
1185                 case 'n':
1186                         popt->po_show_path = 0;
1187                         break;
1188                 case 'N':
1189                         popt->po_only_path = 1;
1190                         break;
1191                 case 'R':
1192                         popt->po_recursive = 1;
1193                         break;
1194                 default:
1195                         return -1;
1196                 }
1197         }
1198
1199         return optind;
1200 }
1201
1202 int jt_lcfg_getparam(int argc, char **argv)
1203 {
1204         int rc = 0, index, i;
1205         struct param_opts popt;
1206         char *path;
1207
1208         memset(&popt, 0, sizeof(popt));
1209         index = getparam_cmdline(argc, argv, &popt);
1210         if (index < 0 || index >= argc)
1211                 return CMD_HELP;
1212
1213         for (i = index; i < argc; i++) {
1214                 int rc2;
1215
1216                 path = argv[i];
1217
1218                 rc2 = clean_path(&popt, path);
1219                 if (rc2 < 0) {
1220                         fprintf(stderr, "error: %s: cleaning '%s': %s\n",
1221                                 jt_cmdname(argv[0]), path, strerror(-rc2));
1222                         if (rc == 0)
1223                                 rc = rc2;
1224                         continue;
1225                 }
1226
1227                 rc2 = param_display(&popt, path, NULL,
1228                                     popt.po_only_path ? LIST_PARAM : GET_PARAM);
1229                 if (rc2 < 0) {
1230                         if (rc == 0)
1231                                 rc = rc2;
1232                         continue;
1233                 }
1234         }
1235
1236         return rc;
1237 }
1238
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
1283 static int setparam_cmdline(int argc, char **argv, struct param_opts *popt)
1284 {
1285         int ch;
1286
1287         popt->po_show_path = 1;
1288         popt->po_only_path = 0;
1289         popt->po_show_type = 0;
1290         popt->po_recursive = 0;
1291         popt->po_perm = 0;
1292         popt->po_delete = 0;
1293         popt->po_file = 0;
1294
1295         while ((ch = getopt(argc, argv, "nPdF")) != -1) {
1296                 switch (ch) {
1297                 case 'n':
1298                         popt->po_show_path = 0;
1299                         break;
1300                 case 'P':
1301                         popt->po_perm = 1;
1302                         break;
1303                 case 'd':
1304                         popt->po_delete = 1;
1305                         break;
1306                 case 'F':
1307                         popt->po_file = 1;
1308                         break;
1309                 default:
1310                         return -1;
1311                 }
1312         }
1313         return optind;
1314 }
1315
1316 enum paramtype {
1317         PT_NONE = 0,
1318         PT_SETPARAM,
1319         PT_CONFPARAM
1320 };
1321
1322 #define PS_NONE 0
1323 #define PS_PARAM_FOUND 1
1324 #define PS_PARAM_SET 2
1325 #define PS_VAL_FOUND 4
1326 #define PS_VAL_SET 8
1327 #define PS_DEVICE_FOUND 16
1328 #define PS_DEVICE_SET 32
1329
1330 #define PARAM_SZ 256
1331
1332 static struct cfg_type_data {
1333         enum paramtype ptype;
1334         char *type_name;
1335 } cfg_type_table[] = {
1336         { PT_SETPARAM, "set_param" },
1337         { PT_CONFPARAM, "conf_param" },
1338         { PT_NONE, "none" }
1339 };
1340
1341 static struct cfg_stage_data {
1342         int pstage;
1343         char *stage_name;
1344 } cfg_stage_table[] = {
1345         { PS_PARAM_FOUND, "parameter" },
1346         { PS_VAL_FOUND, "value" },
1347         { PS_DEVICE_FOUND, "device" },
1348         { PS_NONE, "none" }
1349 };
1350
1351 void conf_to_set_param(enum paramtype confset, const char *param,
1352                        const char *device, char *buf,
1353                        int bufsize)
1354 {
1355         char *tmp;
1356
1357         if (confset == PT_SETPARAM) {
1358                 strncpy(buf, param, bufsize);
1359                 return;
1360         }
1361
1362         /*
1363          * sys.* params are top level, we just need to trim the sys.
1364          */
1365         tmp = strstr(param, "sys.");
1366         if (tmp) {
1367                 tmp += 4;
1368                 strncpy(buf, tmp, bufsize);
1369                 return;
1370         }
1371
1372         /*
1373          * parameters look like type.parameter, we need to stick the device
1374          * in the middle.  Example combine mdt.identity_upcall with device
1375          * lustre-MDT0000 for mdt.lustre-MDT0000.identity_upcall
1376          */
1377
1378         tmp = strchrnul(param, '.');
1379         snprintf(buf, tmp - param + 1, "%s", param);
1380         buf += tmp - param;
1381         bufsize -= tmp - param;
1382         snprintf(buf, bufsize, ".%s%s", device, tmp);
1383 }
1384
1385 int lcfg_setparam_yaml(char *func, char *filename)
1386 {
1387         FILE *file;
1388         yaml_parser_t parser;
1389         yaml_token_t token;
1390         int rc = 0;
1391
1392         enum paramtype confset = PT_NONE;
1393         int param = PS_NONE;
1394         char *tmp;
1395         char parameter[PARAM_SZ + 1];
1396         char value[PARAM_SZ + 1];
1397         char device[PARAM_SZ + 1];
1398
1399         file = fopen(filename, "rb");
1400         yaml_parser_initialize(&parser);
1401         yaml_parser_set_input_file(&parser, file);
1402
1403         /*
1404          * Search tokens for conf_param or set_param
1405          * The token after "parameter" goes into parameter
1406          * The token after "value" goes into value
1407          * when we have all 3, create param=val and call the
1408          * appropriate function for set/conf param
1409          */
1410         while (token.type != YAML_STREAM_END_TOKEN && rc == 0) {
1411                 int i;
1412
1413                 yaml_token_delete(&token);
1414                 if (!yaml_parser_scan(&parser, &token)) {
1415                         rc = 1;
1416                         break;
1417                 }
1418
1419                 if (token.type != YAML_SCALAR_TOKEN)
1420                         continue;
1421
1422                 for (i = 0; cfg_type_table[i].ptype != PT_NONE; i++) {
1423                         if (!strncmp((char *)token.data.alias.value,
1424                                      cfg_type_table[i].type_name,
1425                                      strlen(cfg_type_table[i].type_name))) {
1426                                 confset = cfg_type_table[i].ptype;
1427                                 break;
1428                         }
1429                 }
1430
1431                 if (confset == PT_NONE)
1432                         continue;
1433
1434                 for (i = 0; cfg_stage_table[i].pstage != PS_NONE; i++) {
1435                         if (!strncmp((char *)token.data.alias.value,
1436                                      cfg_stage_table[i].stage_name,
1437                                      strlen(cfg_stage_table[i].stage_name))) {
1438                                 param |= cfg_stage_table[i].pstage;
1439                                 break;
1440                         }
1441                 }
1442
1443                 if (cfg_stage_table[i].pstage != PS_NONE)
1444                         continue;
1445
1446                 if (param & PS_PARAM_FOUND) {
1447                         conf_to_set_param(confset,
1448                                           (char *)token.data.alias.value,
1449                                           device, parameter, PARAM_SZ);
1450                         param |= PS_PARAM_SET;
1451                         param &= ~PS_PARAM_FOUND;
1452
1453                         /*
1454                          * we're getting parameter: param=val
1455                          * copy val and mark that we've got it in case
1456                          * there is no value: tag
1457                          */
1458                         tmp = strchrnul(parameter, '=');
1459                         if (*tmp == '=') {
1460                                 strncpy(value, tmp + 1, sizeof(value) - 1);
1461                                 *tmp = '\0';
1462                                 param |= PS_VAL_SET;
1463                         } else {
1464                                 continue;
1465                         }
1466                 } else if (param & PS_VAL_FOUND) {
1467                         strncpy(value, (char *)token.data.alias.value,
1468                                 PARAM_SZ);
1469                         param |= PS_VAL_SET;
1470                         param &= ~PS_VAL_FOUND;
1471                 } else if (param & PS_DEVICE_FOUND) {
1472                         strncpy(device, (char *)token.data.alias.value,
1473                                 PARAM_SZ);
1474                         param |= PS_DEVICE_SET;
1475                         param &= ~PS_DEVICE_FOUND;
1476                 }
1477
1478                 if (confset && param & PS_VAL_SET && param & PS_PARAM_SET) {
1479                         int size = strlen(parameter) + strlen(value) + 2;
1480                         char *buf = malloc(size);
1481
1482                         if (!buf) {
1483                                 rc = 2;
1484                                 break;
1485                         }
1486                         snprintf(buf, size, "%s=%s", parameter, value);
1487
1488                         printf("set_param: %s\n", buf);
1489                         rc = lcfg_setparam_perm(func, buf);
1490
1491                         confset = PT_NONE;
1492                         param = PS_NONE;
1493                         parameter[0] = '\0';
1494                         value[0] = '\0';
1495                         device[0] = '\0';
1496                         free(buf);
1497                 }
1498         }
1499
1500         yaml_parser_delete(&parser);
1501         fclose(file);
1502
1503         return rc;
1504 }
1505
1506 int jt_lcfg_setparam(int argc, char **argv)
1507 {
1508         int rc = 0, index, i;
1509         struct param_opts popt;
1510         char *path = NULL, *value = NULL;
1511
1512         memset(&popt, 0, sizeof(popt));
1513         index = setparam_cmdline(argc, argv, &popt);
1514         if (index < 0 || index >= argc)
1515                 return CMD_HELP;
1516
1517         if (popt.po_perm)
1518                 /*
1519                  * We can't delete parameters that were
1520                  * set with old conf_param interface
1521                  */
1522                 return jt_lcfg_setparam_perm(argc, argv, &popt);
1523
1524         if (popt.po_file)
1525                 return lcfg_setparam_yaml(argv[0], argv[index]);
1526
1527         for (i = index; i < argc; i++) {
1528                 int rc2;
1529
1530                 path = argv[i];
1531                 value = strchr(path, '=');
1532                 if (value) {
1533                         /* format: set_param a=b */
1534                         *value = '\0';
1535                         value++;
1536                         if (*value == '\0') {
1537                                 fprintf(stderr,
1538                                         "error: %s: setting %s: no value\n",
1539                                         jt_cmdname(argv[0]), path);
1540                                 if (rc == 0)
1541                                         rc = -EINVAL;
1542                                 continue;
1543                         }
1544                 } else {
1545                         /* format: set_param a b */
1546                         i++;
1547                         if (i >= argc) {
1548                                 fprintf(stderr,
1549                                         "error: %s: setting %s: no value\n",
1550                                         jt_cmdname(argv[0]), path);
1551                                 if (rc == 0)
1552                                         rc = -EINVAL;
1553                                 break;
1554                         }
1555                         value = argv[i];
1556                 }
1557
1558                 rc2 = clean_path(&popt, path);
1559                 if (rc2 < 0) {
1560                         fprintf(stderr, "error: %s: cleaning %s: %s\n",
1561                                 jt_cmdname(argv[0]), path, strerror(-rc2));
1562                         if (rc == 0)
1563                                 rc = rc2;
1564                         continue;
1565                 }
1566
1567                 rc2 = param_display(&popt, path, value, SET_PARAM);
1568                 if (rc == 0)
1569                         rc = rc2;
1570         }
1571
1572         return rc;
1573 }