]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - quota/state.c
xfs_quota: display warning limits when printing quota type information
[thirdparty/xfsprogs-dev.git] / quota / state.c
CommitLineData
959ef981 1// SPDX-License-Identifier: GPL-2.0
5aead01d 2/*
da23017d
NS
3 * Copyright (c) 2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
5aead01d 5 */
29647c8d 6#include <stdbool.h>
6b803e5a 7#include "command.h"
5aead01d
NS
8#include "init.h"
9#include "quota.h"
10
11static cmdinfo_t off_cmd;
12static cmdinfo_t state_cmd;
13static cmdinfo_t enable_cmd;
14static cmdinfo_t disable_cmd;
15static cmdinfo_t remove_cmd;
16
17static void
18off_help(void)
19{
20 printf(_(
21"\n"
22" turn filesystem quota off, both accounting and enforcement\n"
23"\n"
24" Example:\n"
25" 'off -uv' (switch off user quota on the current filesystem)\n"
26" This command is the equivalent of the traditional quotaoff command,\n"
27" which disables quota completely on a mounted filesystem.\n"
28" Note that there is no 'on' command - for XFS filesystems (with the\n"
29" exception of the root filesystem on IRIX) quota can only be enabled\n"
30" at mount time, through the use of one of the quota mount options.\n"
31"\n"
32" The state command is useful for displaying the current state. Using\n"
33" the -v (verbose) option with the 'off' command will display the quota\n"
34" state for the affected filesystem once the operation is complete.\n"
35" The affected quota type is -g (groups), -p (projects) or -u (users)\n"
36" and defaults to user quota (multiple types can be specified).\n"
37"\n"));
38}
39
40static void
41state_help(void)
42{
43 printf(_(
44"\n"
45" query the state of quota on the current filesystem\n"
46"\n"
47" This is a verbose status command, reporting whether or not accounting\n"
48" and/or enforcement are enabled for a filesystem, which inodes are in\n"
49" use as the quota state inodes, and how many extents and blocks are\n"
50" presently being used to hold that information.\n"
51" The quota type is specified via -g (groups), -p (projects) or -u (users)\n"
52" and defaults to user quota (multiple types can be specified).\n"
53"\n"));
54}
55
56static void
57enable_help(void)
58{
59 printf(_(
60"\n"
61" enable quota enforcement on a filesystem\n"
62"\n"
63" If a filesystem is mounted and has quota accounting enabled, but not\n"
64" quota enforcement, enforcement can be enabled with this command.\n"
65" With the -v (verbose) option, the status of the filesystem will be\n"
66" reported after the operation is complete.\n"
67" The affected quota type is -g (groups), -p (projects) or -u (users)\n"
68" and defaults to user quota (multiple types can be specified).\n"
69"\n"));
70}
71
72static void
73disable_help(void)
74{
75 printf(_(
76"\n"
77" disable quota enforcement on a filesystem\n"
78"\n"
79" If a filesystem is mounted and is currently enforcing quota, this\n"
80" provides a mechanism to switch off the enforcement, but continue to\n"
81" perform used space (and used inodes) accounting.\n"
82" The affected quota type is -g (groups), -p (projects) or -u (users).\n"
83"\n"));
84}
85
86static void
87remove_help(void)
88{
89 printf(_(
90"\n"
91" remove any space being used by the quota subsystem\n"
92"\n"
93" Once quota has been switched 'off' on a filesystem, the space that\n"
94" was allocated to holding quota metadata can be freed via this command.\n"
95" The affected quota type is -g (groups), -p (projects) or -u (users)\n"
96" and defaults to user quota (multiple types can be specified).\n"
97"\n"));
98}
99
100static void
101state_qfilestat(
bb92709f
ES
102 FILE *fp,
103 struct fs_path *mount,
104 uint type,
105 struct fs_qfilestatv *qfs,
106 int accounting,
107 int enforcing)
5aead01d
NS
108{
109 fprintf(fp, _("%s quota state on %s (%s)\n"), type_to_string(type),
110 mount->fs_dir, mount->fs_name);
111 fprintf(fp, _(" Accounting: %s\n"), accounting ? _("ON") : _("OFF"));
112 fprintf(fp, _(" Enforcement: %s\n"), enforcing ? _("ON") : _("OFF"));
06065b12
AE
113 if (qfs->qfs_ino != (__u64) -1)
114 fprintf(fp, _(" Inode: #%llu (%llu blocks, %lu extents)\n"),
115 (unsigned long long)qfs->qfs_ino,
116 (unsigned long long)qfs->qfs_nblks,
117 (unsigned long)qfs->qfs_nextents);
118 else
119 fprintf(fp, _(" Inode: N/A\n"));
5aead01d
NS
120}
121
122static void
123state_timelimit(
124 FILE *fp,
125 uint form,
14f8b681 126 uint32_t timelimit)
5aead01d
NS
127{
128 fprintf(fp, _("%s grace time: %s\n"),
129 form_to_string(form),
130 time_to_string(timelimit, VERBOSE_FLAG | ABSOLUTE_FLAG));
131}
132
387a96e1
DW
133static void
134state_warnlimit(
135 FILE *fp,
136 uint form,
137 uint16_t warnlimit)
138{
139 fprintf(fp, _("%s max warnings: %u\n"),
140 form_to_string(form), warnlimit);
141}
142
bb92709f
ES
143/*
144 * fs_quota_stat holds a subset of fs_quota_statv; this copies
145 * the smaller into the larger, leaving any not-present fields
146 * empty. This is so the same reporting function can be used
147 * for both XFS_GETQSTAT and XFS_GETQSTATV results.
148 */
149static void
150state_stat_to_statv(
151 struct fs_quota_stat *s,
152 struct fs_quota_statv *sv)
153{
154 memset(sv, 0, sizeof(struct fs_quota_statv));
155
156 /* shared information */
157 sv->qs_version = s->qs_version;
158 sv->qs_flags = s->qs_flags;
159 sv->qs_incoredqs = s->qs_incoredqs;
160 sv->qs_btimelimit = s->qs_btimelimit;
161 sv->qs_itimelimit = s->qs_itimelimit;
162 sv->qs_rtbtimelimit = s->qs_rtbtimelimit;
163 sv->qs_bwarnlimit = s->qs_bwarnlimit;
164 sv->qs_iwarnlimit = s->qs_iwarnlimit;
165
166 /* Always room for uquota */
167 sv->qs_uquota.qfs_ino = s->qs_uquota.qfs_ino;
168 sv->qs_uquota.qfs_nblks = s->qs_uquota.qfs_nblks;
169 sv->qs_uquota.qfs_nextents = s->qs_uquota.qfs_nextents;
170
171 /*
172 * If we are here, XFS_GETQSTATV failed and XFS_GETQSTAT passed;
173 * that is a very strong hint that we're on a kernel which predates
174 * the on-disk pquota inode; both were added in v3.12. So, we do
175 * some tricksy determination here.
176 * gs_gquota may hold either group quota inode info, or project
177 * quota if that is used instead; which one it actually holds depends
178 * on the quota flags. (If neither is set, neither is used)
179 */
180 if (s->qs_flags & XFS_QUOTA_GDQ_ACCT) {
181 /* gs_gquota holds group quota info */
182 sv->qs_gquota.qfs_ino = s->qs_gquota.qfs_ino;
183 sv->qs_gquota.qfs_nblks = s->qs_gquota.qfs_nblks;
184 sv->qs_gquota.qfs_nextents = s->qs_gquota.qfs_nextents;
185 } else if (s->qs_flags & XFS_QUOTA_PDQ_ACCT) {
186 /* gs_gquota actually holds project quota info */
187 sv->qs_pquota.qfs_ino = s->qs_gquota.qfs_ino;
188 sv->qs_pquota.qfs_nblks = s->qs_gquota.qfs_nblks;
189 sv->qs_pquota.qfs_nextents = s->qs_gquota.qfs_nextents;
190 }
191}
192
5aead01d
NS
193static void
194state_quotafile_mount(
bb92709f
ES
195 FILE *fp,
196 uint type,
197 struct fs_path *mount,
198 uint flags)
5aead01d 199{
bb92709f
ES
200 struct fs_quota_stat s;
201 struct fs_quota_statv sv;
202 char *dev = mount->fs_name;
203
204 sv.qs_version = FS_QSTATV_VERSION1;
205
206 if (xfsquotactl(XFS_GETQSTATV, dev, type, 0, (void *)&sv) < 0) {
207 if (xfsquotactl(XFS_GETQSTAT, dev, type, 0, (void *)&s) < 0) {
208 if (flags & VERBOSE_FLAG)
209 fprintf(fp,
210 _("%s quota are not enabled on %s\n"),
211 type_to_string(type), dev);
212 return;
213 }
214 state_stat_to_statv(&s, &sv);
5aead01d
NS
215 }
216
217 if (type & XFS_USER_QUOTA)
bb92709f
ES
218 state_qfilestat(fp, mount, XFS_USER_QUOTA, &sv.qs_uquota,
219 sv.qs_flags & XFS_QUOTA_UDQ_ACCT,
220 sv.qs_flags & XFS_QUOTA_UDQ_ENFD);
5aead01d 221 if (type & XFS_GROUP_QUOTA)
bb92709f
ES
222 state_qfilestat(fp, mount, XFS_GROUP_QUOTA, &sv.qs_gquota,
223 sv.qs_flags & XFS_QUOTA_GDQ_ACCT,
224 sv.qs_flags & XFS_QUOTA_GDQ_ENFD);
5aead01d 225 if (type & XFS_PROJ_QUOTA)
bb92709f
ES
226 state_qfilestat(fp, mount, XFS_PROJ_QUOTA, &sv.qs_pquota,
227 sv.qs_flags & XFS_QUOTA_PDQ_ACCT,
228 sv.qs_flags & XFS_QUOTA_PDQ_ENFD);
5aead01d 229
bb92709f 230 state_timelimit(fp, XFS_BLOCK_QUOTA, sv.qs_btimelimit);
387a96e1
DW
231 state_warnlimit(fp, XFS_BLOCK_QUOTA, sv.qs_bwarnlimit);
232
bb92709f 233 state_timelimit(fp, XFS_INODE_QUOTA, sv.qs_itimelimit);
387a96e1
DW
234 state_warnlimit(fp, XFS_INODE_QUOTA, sv.qs_iwarnlimit);
235
bb92709f 236 state_timelimit(fp, XFS_RTBLOCK_QUOTA, sv.qs_rtbtimelimit);
5aead01d
NS
237}
238
239static void
240state_quotafile(
241 FILE *fp,
242 uint type,
243 char *dir,
244 uint flags)
245{
246 fs_cursor_t cursor;
247 fs_path_t *mount;
248
249 fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
250 while ((mount = fs_cursor_next_entry(&cursor)))
251 state_quotafile_mount(fp, type, mount, flags);
252}
253
254static int
255state_f(
256 int argc,
257 char **argv)
258{
259 FILE *fp = NULL;
260 char *fname = NULL;
261 int c, flags = 0, type = 0;
262
263 while ((c = getopt(argc, argv, "af:gpuv")) != EOF) {
264 switch (c) {
265 case 'a':
266 flags |= ALL_MOUNTS_FLAG;
267 break;
268 case 'f':
269 fname = optarg;
270 break;
271 case 'g':
272 type |= XFS_GROUP_QUOTA;
273 break;
274 case 'p':
275 type |= XFS_PROJ_QUOTA;
276 break;
277 case 'u':
278 type |= XFS_USER_QUOTA;
279 break;
280 case 'v':
281 flags |= VERBOSE_FLAG;
282 break;
283 default:
284 return command_usage(&state_cmd);
285 }
286 }
287
288 if (argc != optind)
289 return command_usage(&state_cmd);
290
291 if ((fp = fopen_write_secure(fname)) == NULL)
292 return 0;
293
294 if (!type)
295 type = XFS_USER_QUOTA | XFS_GROUP_QUOTA | XFS_PROJ_QUOTA;
296
fa13a00f
NS
297 if (flags & ALL_MOUNTS_FLAG)
298 state_quotafile(fp, type, NULL, flags);
04418c59 299 else if (fs_path && fs_path->fs_flags & FS_MOUNT_POINT)
fa13a00f 300 state_quotafile(fp, type, fs_path->fs_dir, flags);
5aead01d
NS
301
302 if (fname)
303 fclose(fp);
304 return 0;
305}
306
307static void
308enable_enforcement(
309 char *dir,
310 uint type,
311 uint qflags,
312 uint flags)
313{
314 fs_path_t *mount;
5aead01d
NS
315
316 mount = fs_table_lookup(dir, FS_MOUNT_POINT);
317 if (!mount) {
e3210fd8 318 exitcode = 1;
5aead01d
NS
319 fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
320 return;
321 }
322 dir = mount->fs_name;
ca42fa70
BD
323 if (xfsquotactl(XFS_QUOTAON, dir, type, 0, (void *)&qflags) < 0) {
324 if (errno == EEXIST)
325 fprintf(stderr,
326 _("Quota enforcement already enabled.\n"));
327 else if (errno == EINVAL || errno == ENOSYS)
328 fprintf(stderr,
329 _("Can't enable enforcement when quota off.\n"));
330 else
331 perror("XFS_QUOTAON");
332 }
5aead01d
NS
333 else if (flags & VERBOSE_FLAG)
334 state_quotafile_mount(stdout, type, mount, flags);
335}
336
337static void
338disable_enforcement(
339 char *dir,
340 uint type,
341 uint qflags,
342 uint flags)
343{
344 fs_path_t *mount;
5aead01d
NS
345
346 mount = fs_table_lookup(dir, FS_MOUNT_POINT);
347 if (!mount) {
e3210fd8 348 exitcode = 1;
5aead01d
NS
349 fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
350 return;
351 }
352 dir = mount->fs_name;
ca42fa70
BD
353 if (xfsquotactl(XFS_QUOTAOFF, dir, type, 0, (void *)&qflags) < 0) {
354 if (errno == EEXIST)
355 fprintf(stderr,
356 _("Quota enforcement already disabled.\n"));
357 else if (errno == EINVAL || errno == ENOSYS)
358 fprintf(stderr,
359 _("Can't disable enforcement when quota off.\n"));
360 else
361 perror("XFS_QUOTAOFF");
362 }
5aead01d
NS
363 else if (flags & VERBOSE_FLAG)
364 state_quotafile_mount(stdout, type, mount, flags);
365}
366
367static void
368quotaoff(
369 char *dir,
370 uint type,
371 uint qflags,
372 uint flags)
373{
374 fs_path_t *mount;
5aead01d
NS
375
376 mount = fs_table_lookup(dir, FS_MOUNT_POINT);
377 if (!mount) {
e3210fd8 378 exitcode = 1;
5aead01d
NS
379 fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
380 return;
381 }
382 dir = mount->fs_name;
ca42fa70
BD
383 if (xfsquotactl(XFS_QUOTAOFF, dir, type, 0, (void *)&qflags) < 0) {
384 if (errno == EEXIST || errno == ENOSYS)
385 fprintf(stderr, _("Quota already off.\n"));
386 else
387 perror("XFS_QUOTAOFF");
388 }
5aead01d
NS
389 else if (flags & VERBOSE_FLAG)
390 state_quotafile_mount(stdout, type, mount, flags);
391}
392
79ac1ae4
DD
393static int
394remove_qtype_extents(
395 char *dir,
396 uint type)
397{
398 int error = 0;
399
400 if ((error = xfsquotactl(XFS_QUOTARM, dir, type, 0, (void *)&type)) < 0)
401 perror("XFS_QUOTARM");
402 return error;
403}
404
5aead01d
NS
405static void
406remove_extents(
407 char *dir,
408 uint type,
5aead01d
NS
409 uint flags)
410{
411 fs_path_t *mount;
5aead01d
NS
412
413 mount = fs_table_lookup(dir, FS_MOUNT_POINT);
414 if (!mount) {
e3210fd8 415 exitcode = 1;
5aead01d
NS
416 fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
417 return;
418 }
419 dir = mount->fs_name;
79ac1ae4 420 if (type & XFS_USER_QUOTA) {
f8149110 421 if (remove_qtype_extents(dir, XFS_USER_QUOTA) < 0)
79ac1ae4
DD
422 return;
423 }
424 if (type & XFS_GROUP_QUOTA) {
f8149110 425 if (remove_qtype_extents(dir, XFS_GROUP_QUOTA) < 0)
79ac1ae4
DD
426 return;
427 } else if (type & XFS_PROJ_QUOTA) {
f8149110 428 if (remove_qtype_extents(dir, XFS_PROJ_QUOTA) < 0)
79ac1ae4
DD
429 return;
430 }
431 if (flags & VERBOSE_FLAG)
5aead01d
NS
432 state_quotafile_mount(stdout, type, mount, flags);
433}
434
435static int
436enable_f(
437 int argc,
438 char **argv)
439{
440 int c, flags = 0, qflags = 0, type = 0;
441
442 while ((c = getopt(argc, argv, "gpuv")) != EOF) {
443 switch (c) {
444 case 'g':
445 type |= XFS_GROUP_QUOTA;
446 qflags |= XFS_QUOTA_GDQ_ACCT | XFS_QUOTA_GDQ_ENFD;
447 break;
448 case 'p':
449 type |= XFS_PROJ_QUOTA;
450 qflags |= XFS_QUOTA_PDQ_ACCT | XFS_QUOTA_PDQ_ENFD;
451 break;
452 case 'u':
453 type |= XFS_USER_QUOTA;
454 qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD;
455 break;
456 case 'v':
457 flags |= VERBOSE_FLAG;
458 break;
459 default:
460 return command_usage(&enable_cmd);
461 }
462 }
463
464 if (argc != optind)
465 return command_usage(&enable_cmd);
466
79ac1ae4 467 if (!type) {
5aead01d
NS
468 type |= XFS_USER_QUOTA;
469 qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD;
470 }
471
fa13a00f
NS
472 if (fs_path->fs_flags & FS_MOUNT_POINT)
473 enable_enforcement(fs_path->fs_dir, type, qflags, flags);
5aead01d
NS
474 return 0;
475}
476
477static int
478disable_f(
479 int argc,
480 char **argv)
481{
482 int c, flags = 0, qflags = 0, type = 0;
483
484 while ((c = getopt(argc, argv, "gpuv")) != EOF) {
485 switch (c) {
486 case 'g':
487 type |= XFS_GROUP_QUOTA;
79ac1ae4 488 qflags |= XFS_QUOTA_GDQ_ENFD;
5aead01d
NS
489 break;
490 case 'p':
491 type |= XFS_PROJ_QUOTA;
79ac1ae4 492 qflags |= XFS_QUOTA_PDQ_ENFD;
5aead01d
NS
493 break;
494 case 'u':
495 type |= XFS_USER_QUOTA;
79ac1ae4 496 qflags |= XFS_QUOTA_UDQ_ENFD;
5aead01d
NS
497 break;
498 case 'v':
499 flags |= VERBOSE_FLAG;
500 break;
501 default:
502 return command_usage(&disable_cmd);
503 }
504 }
505
506 if (argc != optind)
507 return command_usage(&disable_cmd);
508
79ac1ae4 509 if (!type) {
5aead01d 510 type |= XFS_USER_QUOTA;
79ac1ae4 511 qflags |= XFS_QUOTA_UDQ_ENFD;
5aead01d
NS
512 }
513
fa13a00f
NS
514 if (fs_path->fs_flags & FS_MOUNT_POINT)
515 disable_enforcement(fs_path->fs_dir, type, qflags, flags);
5aead01d
NS
516 return 0;
517}
518
519static int
520off_f(
521 int argc,
522 char **argv)
523{
524 int c, flags = 0, qflags = 0, type = 0;
525
526 while ((c = getopt(argc, argv, "gpuv")) != EOF) {
527 switch (c) {
528 case 'g':
529 type |= XFS_GROUP_QUOTA;
530 qflags |= XFS_QUOTA_GDQ_ACCT | XFS_QUOTA_GDQ_ENFD;
531 break;
532 case 'p':
533 type |= XFS_PROJ_QUOTA;
534 qflags |= XFS_QUOTA_PDQ_ACCT | XFS_QUOTA_PDQ_ENFD;
535 break;
536 case 'u':
537 type |= XFS_USER_QUOTA;
538 qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD;
539 break;
540 case 'v':
541 flags |= VERBOSE_FLAG;
542 break;
543 default:
544 return command_usage(&off_cmd);
545 }
546 }
547
548 if (argc != optind)
549 return command_usage(&off_cmd);
550
79ac1ae4 551 if (!type) {
5aead01d
NS
552 type |= XFS_USER_QUOTA;
553 qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD;
554 }
555
fa13a00f
NS
556 if (fs_path->fs_flags & FS_MOUNT_POINT)
557 quotaoff(fs_path->fs_dir, type, qflags, flags);
5aead01d
NS
558 return 0;
559}
560
561static int
562remove_f(
563 int argc,
564 char **argv)
565{
79ac1ae4 566 int c, flags = 0, type = 0;
5aead01d
NS
567
568 while ((c = getopt(argc, argv, "gpuv")) != EOF) {
569 switch (c) {
570 case 'g':
571 type |= XFS_GROUP_QUOTA;
5aead01d
NS
572 break;
573 case 'p':
574 type |= XFS_PROJ_QUOTA;
5aead01d
NS
575 break;
576 case 'u':
577 type |= XFS_USER_QUOTA;
5aead01d
NS
578 break;
579 case 'v':
580 flags |= VERBOSE_FLAG;
581 break;
582 default:
583 return command_usage(&remove_cmd);
584 }
585 }
586
587 if (argc != optind)
588 return command_usage(&remove_cmd);
589
79ac1ae4 590 if (!type) {
5aead01d 591 type |= XFS_USER_QUOTA;
5aead01d
NS
592 }
593
fa13a00f 594 if (fs_path->fs_flags & FS_MOUNT_POINT)
79ac1ae4 595 remove_extents(fs_path->fs_dir, type, flags);
5aead01d
NS
596 return 0;
597}
598
599void
600state_init(void)
601{
ad765595 602 off_cmd.name = "off";
5aead01d
NS
603 off_cmd.cfunc = off_f;
604 off_cmd.argmin = 0;
605 off_cmd.argmax = -1;
606 off_cmd.args = _("[-gpu] [-v]");
607 off_cmd.oneline = _("permanently switch quota off for a path");
608 off_cmd.help = off_help;
609
ad765595 610 state_cmd.name = "state";
5aead01d
NS
611 state_cmd.cfunc = state_f;
612 state_cmd.argmin = 0;
613 state_cmd.argmax = -1;
62790d19 614 state_cmd.args = _("[-gpu] [-a] [-v] [-f file]");
5aead01d
NS
615 state_cmd.oneline = _("get overall quota state information");
616 state_cmd.help = state_help;
29647c8d 617 state_cmd.flags = CMD_FLAG_FOREIGN_OK;
5aead01d 618
ad765595 619 enable_cmd.name = "enable";
5aead01d
NS
620 enable_cmd.cfunc = enable_f;
621 enable_cmd.argmin = 0;
622 enable_cmd.argmax = -1;
623 enable_cmd.args = _("[-gpu] [-v]");
624 enable_cmd.oneline = _("enable quota enforcement");
625 enable_cmd.help = enable_help;
626
ad765595 627 disable_cmd.name = "disable";
5aead01d
NS
628 disable_cmd.cfunc = disable_f;
629 disable_cmd.argmin = 0;
630 disable_cmd.argmax = -1;
631 disable_cmd.args = _("[-gpu] [-v]");
632 disable_cmd.oneline = _("disable quota enforcement");
633 disable_cmd.help = disable_help;
634
ad765595 635 remove_cmd.name = "remove";
5aead01d
NS
636 remove_cmd.cfunc = remove_f;
637 remove_cmd.argmin = 0;
638 remove_cmd.argmax = -1;
639 remove_cmd.args = _("[-gpu] [-v]");
640 remove_cmd.oneline = _("remove quota extents from a filesystem");
641 remove_cmd.help = remove_help;
642
643 if (expert) {
644 add_command(&off_cmd);
645 add_command(&state_cmd);
646 add_command(&enable_cmd);
647 add_command(&disable_cmd);
648 add_command(&remove_cmd);
649 }
650}