3 * ===================================
4 * HARP | Host ATM Research Platform
5 * ===================================
8 * This Host ATM Research Platform ("HARP") file (the "Software") is
9 * made available by Network Computing Services, Inc. ("NetworkCS")
10 * "AS IS". NetworkCS does not provide maintenance, improvements or
11 * support of any kind.
13 * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
14 * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
15 * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
16 * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
17 * In no event shall NetworkCS be responsible for any damages, including
18 * but not limited to consequential damages, arising from or relating to
19 * any use of the Software or related support.
21 * Copyright 1994-1998 Network Computing Services, Inc.
23 * Copies of this Software may be made, however, the above copyright
24 * notice must be reproduced on all copies.
26 * @(#) $FreeBSD: src/sbin/atm/atm/atm.c,v 1.3.2.1 2000/07/01 06:02:14 ps Exp $
27 * @(#) $DragonFly: src/sbin/atm/atm/atm.c,v 1.5 2006/10/16 00:15:35 pavalos Exp $
31 * User configuration and display program
32 * --------------------------------------
38 #include <sys/param.h>
39 #include <sys/socket.h>
40 #include <sys/sockio.h>
42 #include <netinet/in.h>
43 #include <netatm/port.h>
44 #include <netatm/atm.h>
45 #include <netatm/atm_if.h>
46 #include <netatm/atm_sap.h>
47 #include <netatm/atm_sys.h>
48 #include <netatm/atm_cm.h>
49 #include <netatm/atm_sigmgr.h>
50 #include <netatm/atm_ioctl.h>
64 #define USAGE_STR "Interface management subcommands:\n\
65 attach <intf> <protocol>\n\
67 set mac <intf> <MAC/ESI address>\n\
68 set netif <intf> <prefix> <n>\n\
69 set prefix <intf> <NSAP prefix>\n\
70 show config [<intf>]\n\
71 show interface [<intf>]\n\
72 show netif [<netif>]\n\
73 show stats interface [<intf> phy | dev | atm | aal0 | aal4 |\n\
76 VC management subcommands:\n\
77 add pvc <intf> <vpi> <vci> <aal> <encaps> <owner> ...\n\
78 delete pvc <intf> <vpi> <vci>\n\
79 delete svc <intf> <vpi> <vci>\n\
80 show stats vcc [<intf> [vpi [vci]]]\n\
81 show vcc [<intf> [<vpi> [<vci>] | SVC | PVC]]\n\
83 IP management subcommands:\n\
84 add arp [<netif>] <IP addr> <ATM addr>\n\
85 add pvc <intf> <vpi> <vci> <aal> <encaps> IP <netif> <IP addr> |\n\
87 delete arp [<netif>] <IP addr>\n\
88 set arpserver <netif> <server> <IP prefix> ...\n\
90 show arpserver [<netif>]\n\
91 show ipvcc [<IP addr> | <netif>]\n\
93 Miscellaneous subcommands:\n\
102 static int do_cmd(const struct cmd
*, int, char **);
103 static void usage(const struct cmd
*, const char *);
104 static void attach(int, char **, const struct cmd
*);
105 static void detach(int, char **, const struct cmd
*);
106 static void pvc_add(int, char **, const struct cmd
*);
107 static void arp_add(int, char **, const struct cmd
*);
108 static void pvc_dlt(int, char **, const struct cmd
*);
109 static void svc_dlt(int, char **, const struct cmd
*);
110 static void vcc_dlt(int, char **, const struct cmd
*, struct atmdelreq
*);
111 static void arp_dlt(int, char **, const struct cmd
*);
112 static void help(int, char **, const struct cmd
*);
114 static const struct cmd add_subcmd
[] = {
115 { "arp", 2, 3, arp_add
, "[<netif>] <IP addr> <ATM addr>" },
116 { "pvc", 6, 12, pvc_add
, "<intf> <vpi> <vci> <aal> <encaps> <owner> ..." },
117 { 0, 0, 0, NULL
, "" }
120 static const struct cmd dlt_subcmd
[] = {
121 { "arp", 1, 2, arp_dlt
, "[<netif>] <IP addr>" },
122 { "pvc", 3, 3, pvc_dlt
, "<intf> <vpi> <vci>" },
123 { "svc", 3, 3, svc_dlt
, "<intf> <vpi> <vci>" },
124 { 0, 0, 0, NULL
, "" }
127 static const struct cmd set_subcmd
[] = {
128 { "arpserver", 2, 18, set_arpserver
, "<netif> <server>" },
129 { "mac", 2, 2, set_macaddr
, "<intf> <MAC/ESI address>" },
130 { "netif", 3, 3, set_netif
, "<intf> <prefix> <n>" },
131 { "prefix", 2, 2, set_prefix
, "<intf> <NSAP prefix>" },
135 static const struct cmd stats_subcmd
[] = {
136 { "interface", 0, 2, show_intf_stats
, "[<intf> [cfg | phy | dev | atm | aal0 | aal4 | aal5 | driver]]" },
137 { "vcc", 0, 3, show_vcc_stats
, "[<intf> [vpi [vci]]]" },
138 { 0, 0, 0, NULL
, "" }
141 static const struct cmd show_subcmd
[] = {
142 { "arp", 0, 1, show_arp
, "[<host>]" },
143 { "arpserver", 0, 1, show_arpserv
, "[<netif>]" },
144 { "config", 0, 1, show_config
, "[<intf>]" },
145 { "interface", 0, 1, show_intf
, "[<intf>]" },
146 { "ipvcc", 0, 3, show_ip_vcc
, "[<IP addr> | <netif>]" },
147 { "netif", 0, 1, show_netif
, "[<netif>]" },
148 { "stats", 0, 3, NULL
, (const char *) stats_subcmd
},
149 { "vcc", 0, 3, show_vcc
, "[<intf>] [<vpi> [<vci>] | SVC | PVC]" },
150 { "version", 0, 0, show_version
, "" },
151 { 0, 0, 0, NULL
, "" }
154 static const struct cmd cmds
[] = {
155 { "add", 0, 0, NULL
, (const char *) add_subcmd
},
156 { "attach", 2, 2, attach
, "<intf> <protocol>" },
157 { "delete", 0, 0, NULL
, (const char *) dlt_subcmd
},
158 { "detach", 1, 1, detach
, "<intf>" },
159 { "set", 0, 0, NULL
, (const char *) set_subcmd
},
160 { "show", 0, 0, NULL
, (const char *) show_subcmd
},
161 { "help", 0, 99, help
, "" },
162 { 0, 0, 0, NULL
, "" }
167 * Supported signalling protocols
169 static const struct proto protos
[] = {
170 { "SIGPVC", ATM_SIG_PVC
},
171 { "SPANS", ATM_SIG_SPANS
},
172 { "UNI30", ATM_SIG_UNI30
},
173 { "UNI31", ATM_SIG_UNI31
},
174 { "UNI40", ATM_SIG_UNI40
},
179 * Supported VCC owners
181 static const struct owner owners
[] = {
182 { "IP", ENDPT_IP
, ip_pvcadd
},
183 { "SPANS", ENDPT_SPANS_SIG
,0 },
184 { "SPANS CLS", ENDPT_SPANS_CLS
,0 },
185 { "UNI SIG", ENDPT_UNI_SIG
, 0 },
190 * Supported AAL parameters
192 const struct aal aals
[] = {
193 { "Null", ATM_AAL0
},
194 { "AAL0", ATM_AAL0
},
195 { "AAL1", ATM_AAL1
},
196 { "AAL2", ATM_AAL2
},
197 { "AAL4", ATM_AAL3_4
},
198 { "AAL3", ATM_AAL3_4
},
199 { "AAL3/4", ATM_AAL3_4
},
200 { "AAL5", ATM_AAL5
},
205 * Supported VCC encapsulations
207 const struct encaps encaps
[] = {
208 { "Null", ATM_ENC_NULL
},
209 { "None", ATM_ENC_NULL
},
210 { "LLC/SNAP", ATM_ENC_LLC
},
211 { "LLC", ATM_ENC_LLC
},
212 { "SNAP", ATM_ENC_LLC
},
218 char prefix
[128] = "";
222 main(int argc
, char **argv
)
227 * Save program name, ignoring any path components
229 if ((prog
= (char *)strrchr(argv
[0], '/')) != NULL
)
241 * Validate and process command
243 if ((error
= do_cmd(cmds
, argc
, argv
)) != 0)
251 * Validate and process user command
254 * descp pointer to command description array
255 * argc number of arguments left in command
256 * argv pointer to argument strings
263 do_cmd(const struct cmd
*descp
, int argc
, char **argv
)
265 const struct cmd
*cmdp
= NULL
;
268 * Make sure we have paramaters to process
276 * Figure out what command user wants
278 for (; descp
->name
; descp
++) {
280 * Use an exact match if there is one
282 if (!strcasecmp(descp
->name
, argv
[0])) {
287 * Look for a match on the first part of keyword
289 if (!strncasecmp(descp
->name
, argv
[0], strlen(argv
[0]))) {
291 fprintf(stderr
, "%s: Ambiguous parameter \"%s\"\n",
303 * See if this command has subcommands
305 if (cmdp
->func
== NULL
) {
306 strcat(prefix
, cmdp
->name
);
308 return(do_cmd((const struct cmd
*)cmdp
->help
, argc
, argv
));
314 if ((argc
< cmdp
->minp
) || (argc
> cmdp
->maxp
)) {
315 fprintf(stderr
, "%s: Invalid number of arguments\n",
317 fprintf(stderr
, "\tformat is: %s%s %s\n",
318 prefix
, cmdp
->name
, cmdp
->help
);
325 (*cmdp
->func
)(argc
, argv
, cmdp
);
331 * Print command usage information
334 * cmdp pointer to command description
335 * pref pointer current command prefix
342 usage(__unused
const struct cmd
*cmdp
, __unused
const char *pref
)
344 fprintf(stderr
, "usage: %s command [arg] [arg]...\n", prog
);
345 fprintf(stderr
, USAGE_STR
);
350 * Process interface attach command
353 * atm attach <interface_name> <protocol_name>
356 * argc number of arguments to command
357 * argv pointer to argument strings
358 * cmdp pointer to command description
365 attach(__unused
int argc
, char **argv
, __unused
const struct cmd
*cmdp
)
367 struct atmcfgreq aar
;
368 const struct proto
*prp
;
372 * Validate interface name
374 if (strlen(argv
[0]) > sizeof(aar
.acr_att_intf
) - 1) {
375 fprintf(stderr
, "%s: Illegal interface name\n", prog
);
380 * Find/validate requested signalling protocol
382 for (prp
= protos
; prp
->p_name
; prp
++) {
383 if (strcasecmp(prp
->p_name
, argv
[1]) == 0)
386 if (prp
->p_name
== NULL
) {
387 fprintf(stderr
, "%s: Unknown signalling protocol\n", prog
);
393 * Build ioctl request
395 aar
.acr_opcode
= AIOCS_CFG_ATT
;
396 strncpy(aar
.acr_att_intf
, argv
[0], sizeof(aar
.acr_att_intf
));
397 aar
.acr_att_proto
= prp
->p_id
;
400 * Tell the kernel to do the attach
402 s
= socket(AF_ATM
, SOCK_DGRAM
, 0);
406 if (ioctl(s
, AIOCCFG
, (caddr_t
)&aar
) < 0) {
407 fprintf(stderr
, "%s: ", prog
);
411 case EPROTONOSUPPORT
:
412 perror("Internal error");
415 fprintf(stderr
, "Kernel memory exhausted\n");
418 fprintf(stderr
, "Signalling manager already attached to %s\n",
422 fprintf(stderr
, "ATM network is inoperable\n");
425 fprintf(stderr
, "Must be super user to use attach subcommand\n");
428 fprintf(stderr
, "%s is not an ATM device\n",
432 fprintf(stderr
, "%s has too few or too many network interfaces\n",
436 perror("Ioctl (AIOCCFG) attach");
446 * Process interface detach command
449 * atm detach <interface_name>
452 * argc number of arguments to command
453 * argv pointer to argument strings
454 * cmdp pointer to command description
461 detach(__unused
int argc
, char **argv
, __unused
const struct cmd
*cmdp
)
463 struct atmcfgreq adr
;
467 * Validate interface name
469 if (strlen(argv
[0]) > sizeof(adr
.acr_det_intf
) - 1) {
470 fprintf(stderr
, "%s: Illegal interface name\n", prog
);
475 * Build ioctl request
477 adr
.acr_opcode
= AIOCS_CFG_DET
;
478 strncpy(adr
.acr_det_intf
, argv
[0], sizeof(adr
.acr_det_intf
));
481 * Tell the kernel to do the detach
483 s
= socket(AF_ATM
, SOCK_DGRAM
, 0);
487 if (ioctl(s
, AIOCCFG
, (caddr_t
)&adr
) < 0) {
488 fprintf(stderr
, "%s: ", prog
);
491 fprintf(stderr
, "Signalling manager already detaching from %s\n",
495 perror("Internal error");
498 fprintf(stderr
, "Must be super user to use detach subcommand\n");
501 perror("ioctl (AIOCCFG) detach");
511 * Process PVC add command
514 * atm add PVC <interface_name> <vpi> <vci> <aal> <encaps>
518 * argc number of arguments to command
519 * argv pointer to argument strings
520 * cmdp pointer to command description
527 pvc_add(int argc
, char **argv
, const struct cmd
*cmdp
)
529 struct atmaddreq apr
;
530 struct atminfreq air
;
531 struct air_int_rsp
*int_info
;
532 const struct owner
*owp
;
533 const struct aal
*alp
;
534 const struct encaps
*enp
;
540 * Initialize opcode and flags
542 apr
.aar_opcode
= AIOCS_ADD_PVC
;
543 apr
.aar_pvc_flags
= 0;
546 * Validate interface name and issue an information
547 * request IOCTL for the interface
549 if (strlen(argv
[0]) > sizeof(apr
.aar_pvc_intf
) - 1) {
550 fprintf(stderr
, "%s: Illegal interface name\n", prog
);
553 UM_ZERO(air
.air_int_intf
, sizeof(air
.air_int_intf
));
554 strcpy(air
.air_int_intf
, argv
[0]);
555 buf_len
= sizeof(struct air_int_rsp
);
556 air
.air_opcode
= AIOCS_INF_INT
;
557 buf_len
= do_info_ioctl(&air
, buf_len
);
559 fprintf(stderr
, "%s: ", prog
);
563 perror("Internal error");
566 fprintf(stderr
, "%s is not an ATM device\n",
570 perror("ioctl (AIOCINFO)");
575 int_info
= (struct air_int_rsp
*) air
.air_buf_addr
;
576 strcpy(apr
.aar_pvc_intf
, argv
[0]);
580 * Validate vpi/vci values
582 v
= strtol(argv
[0], &cp
, 0);
583 if ((*cp
!= '\0') || (v
< 0) || (v
>= 1 << 8)) {
584 fprintf(stderr
, "%s: Invalid VPI value\n", prog
);
587 apr
.aar_pvc_vpi
= (u_short
) v
;
590 v
= strtol(argv
[0], &cp
, 0);
591 if ((*cp
!= '\0') || (v
< MIN_VCI
) || (v
>= 1 << 16)) {
592 fprintf(stderr
, "%s: Invalid VCI value\n", prog
);
595 apr
.aar_pvc_vci
= (u_short
) v
;
599 * Validate requested PVC AAL
601 for (alp
= aals
; alp
->a_name
; alp
++) {
602 if (strcasecmp(alp
->a_name
, argv
[0]) == 0)
605 if (alp
->a_name
== NULL
) {
606 fprintf(stderr
, "%s: Invalid PVC AAL\n", prog
);
609 apr
.aar_pvc_aal
= alp
->a_id
;
613 * Validate requested PVC encapsulation
615 for (enp
= encaps
; enp
->e_name
; enp
++) {
616 if (strcasecmp(enp
->e_name
, argv
[0]) == 0)
619 if (enp
->e_name
== NULL
) {
620 fprintf(stderr
, "%s: Invalid PVC encapsulation\n", prog
);
623 apr
.aar_pvc_encaps
= enp
->e_id
;
627 * Validate requested PVC owner
629 for (owp
= owners
; owp
->o_name
; owp
++) {
630 if (strcasecmp(owp
->o_name
, argv
[0]) == 0)
633 if (owp
->o_name
== NULL
) {
634 fprintf(stderr
, "%s: Unknown PVC owner\n", prog
);
637 apr
.aar_pvc_sap
= owp
->o_sap
;
641 * Perform service user processing
644 (*owp
->o_pvcadd
)(argc
, argv
, cmdp
, &apr
, int_info
);
646 fprintf(stderr
, "%s: Unsupported PVC owner\n", prog
);
651 * Tell the kernel to add the PVC
653 s
= socket(AF_ATM
, SOCK_DGRAM
, 0);
657 if (ioctl(s
, AIOCADD
, (caddr_t
)&apr
) < 0) {
658 fprintf(stderr
, "%s: ", prog
);
660 case EPROTONOSUPPORT
:
662 perror("Internal error");
665 fprintf(stderr
, "Invalid parameter\n");
668 fprintf(stderr
, "PVC already exists\n");
671 fprintf(stderr
, "ATM network is inoperable\n");
674 fprintf(stderr
, "Kernel memory exhausted\n");
677 fprintf(stderr
, "Must be super user to use add subcommand\n");
680 fprintf(stderr
, "Invalid VPI or VCI value\n");
683 perror("ioctl (AIOCADD) add PVC");
693 * Process ARP add command
696 * atm add arp [<netif>] <IP addr> <ATM addr>
699 * argc number of arguments to command
700 * argv pointer to argument strings
701 * cmdp pointer to command description
708 arp_add(int argc
, char **argv
, __unused
const struct cmd
*cmdp
)
711 struct atmaddreq apr
;
713 struct sockaddr_in
*sin
;
715 struct sockaddr_in sin
;
720 * Initialize add request structure
722 UM_ZERO(&apr
, sizeof(apr
));
725 * Get network interface name if one is present
728 check_netif_name(argv
[0]);
729 strcpy(apr
.aar_arp_intf
, argv
[0]);
734 * Get IP address of specified host name
736 UM_ZERO(&host_ip
, sizeof(host_ip
));
737 host_ip
.sa
.sa_family
= AF_INET
;
738 sin
= get_ip_addr(argv
[0]);
739 host_ip
.sin
.sin_addr
.s_addr
= sin
->sin_addr
.s_addr
;
743 * Get specified ATM address
745 len
= get_hex_atm_addr(argv
[0], (u_char
*)host_atm
.address
,
746 sizeof(Atm_addr_nsap
));
748 case sizeof(Atm_addr_nsap
):
749 host_atm
.address_format
= T_ATM_ENDSYS_ADDR
;
750 host_atm
.address_length
= sizeof(Atm_addr_nsap
);
752 case sizeof(Atm_addr_spans
):
753 host_atm
.address_format
= T_ATM_SPANS_ADDR
;
754 host_atm
.address_length
= sizeof(Atm_addr_spans
);
757 fprintf(stderr
, "%s: Invalid ATM address\n", prog
);
762 * Build IOCTL request
764 apr
.aar_opcode
= AIOCS_ADD_ARP
;
765 apr
.aar_arp_dst
= host_ip
.sa
;
766 ATM_ADDR_COPY(&host_atm
, &apr
.aar_arp_addr
);
767 apr
.aar_arp_origin
= ARP_ORIG_PERM
;
770 * Tell the kernel to add the ARP table entry
772 s
= socket(AF_ATM
, SOCK_DGRAM
, 0);
776 if (ioctl(s
, AIOCADD
, (caddr_t
)&apr
) < 0) {
777 fprintf(stderr
, "%s: ", prog
);
780 fprintf(stderr
, "Invalid parameter\n");
783 fprintf(stderr
, "Must be super user to use add subcommand\n");
786 fprintf(stderr
, "IP address not valid for interface\n");
789 perror("ioctl (AIOCADD) add");
799 * Process PVC delete command
802 * atm delete pvc <interface_name> <vpi> <vci>
805 * argc number of arguments to command
806 * argv pointer to argument strings
807 * cmdp pointer to command description
814 pvc_dlt(int argc
, char **argv
, const struct cmd
*cmdp
)
816 struct atmdelreq apr
;
821 apr
.adr_opcode
= AIOCS_DEL_PVC
;
824 * Complete request by calling subroutine
826 vcc_dlt(argc
, argv
, cmdp
, &apr
);
831 * Process SVC delete command
834 * atm delete svc <interface_name> <vpi> <vci>
837 * argc number of arguments to command
838 * argv pointer to argument strings
839 * cmdp pointer to command description
846 svc_dlt(int argc
, char **argv
, const struct cmd
*cmdp
)
848 struct atmdelreq apr
;
853 apr
.adr_opcode
= AIOCS_DEL_SVC
;
856 * Complete request by calling subroutine
858 vcc_dlt(argc
, argv
, cmdp
, &apr
);
863 * Complete an SVC or PVC delete command
866 * argc number of arguments to command
867 * argv pointer to argument strings
868 * cmdp pointer to command description
869 * apr pointer to ATM delete IOCTL structure
876 vcc_dlt(int argc
, char **argv
, __unused
const struct cmd
*cmdp
, struct atmdelreq
*apr
)
883 * Validate interface name
885 if (strlen(argv
[0]) > sizeof(apr
->adr_pvc_intf
) - 1) {
886 fprintf(stderr
, "%s: Illegal interface name\n", prog
);
889 strcpy(apr
->adr_pvc_intf
, argv
[0]);
893 * Validate vpi/vci values
895 v
= strtol(argv
[0], &cp
, 0);
896 if ((*cp
!= '\0') || (v
< 0) || (v
>= 1 << 8)) {
897 fprintf(stderr
, "%s: Invalid VPI value\n", prog
);
900 apr
->adr_pvc_vpi
= (u_short
) v
;
903 v
= strtol(argv
[0], &cp
, 0);
904 if ((*cp
!= '\0') || (v
< MIN_VCI
) || (v
>= 1 << 16)) {
905 fprintf(stderr
, "%s: Invalid VCI value\n", prog
);
908 apr
->adr_pvc_vci
= (u_short
) v
;
912 * Tell the kernel to delete the VCC
914 s
= socket(AF_ATM
, SOCK_DGRAM
, 0);
918 if (ioctl(s
, AIOCDEL
, (caddr_t
)apr
) < 0) {
919 fprintf(stderr
, "%s: ", prog
);
922 fprintf(stderr
, "Invalid parameter\n");
925 fprintf(stderr
, "VCC not found\n");
928 fprintf(stderr
, "VCC already being closed\n");
931 fprintf(stderr
, "%s is not an ATM device\n",
935 fprintf(stderr
, "Must be super user to use delete subcommand\n");
938 perror("ioctl (AIOCDEL) delete");
948 * Process ARP delete command
951 * atm delete arp <IP addr>
954 * argc number of arguments to command
955 * argv pointer to argument strings
956 * cmdp pointer to command description
963 arp_dlt(int argc
, char **argv
, __unused
const struct cmd
*cmdp
)
966 struct atmdelreq apr
;
967 struct sockaddr_in
*sin
;
969 struct sockaddr_in sin
;
976 UM_ZERO(&apr
, sizeof(apr
));
977 apr
.adr_opcode
= AIOCS_DEL_ARP
;
980 * Get network interface name if one is present
983 check_netif_name(argv
[0]);
984 strcpy(apr
.adr_arp_intf
, argv
[0]);
989 * Get IP address of specified host name
991 UM_ZERO(&host_addr
, sizeof(host_addr
));
992 host_addr
.sa
.sa_family
= AF_INET
;
993 sin
= get_ip_addr(argv
[0]);
994 host_addr
.sin
.sin_addr
.s_addr
= sin
->sin_addr
.s_addr
;
995 apr
.adr_arp_dst
= host_addr
.sa
;
998 * Tell the kernel to delete the ARP table entry
1000 s
= socket(AF_ATM
, SOCK_DGRAM
, 0);
1004 if (ioctl(s
, AIOCDEL
, (caddr_t
)&apr
) < 0) {
1005 fprintf(stderr
, "%s: ", prog
);
1008 fprintf(stderr
, "Invalid parameter\n");
1011 fprintf(stderr
, "Must be super user to use delete subcommand\n");
1014 perror("ioctl (AIOCDEL) delete");
1024 * Process help command
1027 * argc number of arguments to command
1028 * argv pointer to argument strings
1029 * cmdp pointer to command description
1036 help(__unused
int argc
, __unused
char **argv
, __unused
const struct cmd
*cmdp
)