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