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