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