]>
Commit | Line | Data |
---|---|---|
ad02a0eb SH |
1 | /* |
2 | Command parsing taken from brctl utility. | |
3 | Display code from stp_cli.c in rstplib. | |
4 | */ | |
5 | ||
6 | #include <stdio.h> | |
7 | #include <stdlib.h> | |
8 | #include <string.h> | |
9 | #include <sys/errno.h> | |
10 | #include <getopt.h> | |
11 | ||
12 | #include <net/if.h> | |
13 | ||
14 | /* For scanning through sysfs directories */ | |
15 | #include <dirent.h> | |
16 | #include <sys/types.h> | |
17 | #include <sys/stat.h> | |
18 | #include <unistd.h> | |
19 | ||
20 | #include "ctl_socket_client.h" | |
21 | #include "ctl_functions.h" | |
22 | ||
ad02a0eb SH |
23 | int get_index_die(const char *ifname, const char *doc, int die) |
24 | { | |
11904a35 SH |
25 | int r = if_nametoindex(ifname); |
26 | if (r == 0) { | |
27 | fprintf(stderr, | |
28 | "Can't find index for %s %s. Not a valid interface.\n", | |
29 | doc, ifname); | |
30 | if (die) | |
31 | exit(1); | |
32 | return -1; | |
33 | } | |
34 | return r; | |
ad02a0eb SH |
35 | } |
36 | ||
37 | int get_index(const char *ifname, const char *doc) | |
38 | { | |
11904a35 | 39 | return get_index_die(ifname, doc, 1); |
ad02a0eb SH |
40 | } |
41 | ||
b600a2c3 SA |
42 | #define BR_ID_FMT "%02x%02x.%02x%02x%02x%02x%02x%02x" |
43 | #define BR_ID_ARGS(x) x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7] | |
ad02a0eb | 44 | |
b600a2c3 | 45 | #define BOOL_STR(x) ((x) ? "yes" : "no") |
ad02a0eb SH |
46 | |
47 | static int do_showbridge(const char *br_name) | |
48 | { | |
b600a2c3 | 49 | STP_BridgeStatus s; |
11904a35 SH |
50 | |
51 | int br_index = get_index_die(br_name, "bridge", 0); | |
52 | if (br_index < 0) | |
53 | return -1; | |
54 | ||
b600a2c3 | 55 | int r = CTL_get_bridge_status(br_index, &s); |
11904a35 | 56 | if (r) { |
11904a35 SH |
57 | return -1; |
58 | } | |
b600a2c3 SA |
59 | printf("%s\n", br_name); |
60 | printf(" enabled\t\t%4s\n", BOOL_STR(s.enabled)); | |
61 | printf(" bridge id\t\t" BR_ID_FMT "\n", BR_ID_ARGS(s.bridge_id)); | |
62 | printf(" designated root\t" BR_ID_FMT "\n", | |
63 | BR_ID_ARGS(s.designated_root)); | |
64 | //printf(" designated bridge\t\t", BR_ID_FMT "\n", | |
65 | // BR_ID_ARGS(s.designated_bridge)); | |
66 | printf(" root port\t\t%4u", s.root_port); | |
67 | printf("\t\t\tpath cost\t\t%4u\n", s.root_path_cost); | |
68 | printf(" max age\t\t%4u", s.max_age); | |
69 | printf("\t\t\tbridge max age\t\t%4u\n", s.bridge_max_age); | |
70 | printf(" hello time\t\t%4u", s.hello_time); | |
71 | printf("\t\t\tbridge hello time\t%4u\n", s.bridge_hello_time); | |
72 | printf(" forward delay\t\t%4u", s.forward_delay); | |
73 | printf("\t\t\tbridge forward delay\t%4u\n", s.bridge_forward_delay); | |
74 | printf(" tx hold count\t\t%4u\n", s.tx_hold_count); | |
75 | printf(" protocol version\t%4u\n", s.protocol_version); | |
76 | printf(" time since topology change\t%4u\n", | |
77 | s.time_since_topology_change); | |
78 | printf(" toplogy change count\t\t%4u\n", s.topology_change_count); | |
79 | printf(" topology change\t\t%4s\n", BOOL_STR(s.topology_change)); | |
11904a35 SH |
80 | |
81 | return 0; | |
ad02a0eb SH |
82 | } |
83 | ||
84 | #define SYSFS_PATH_MAX 256 | |
85 | #define SYSFS_CLASS_NET "/sys/class/net" | |
86 | ||
87 | static int isbridge(const struct dirent *entry) | |
88 | { | |
11904a35 SH |
89 | char path[SYSFS_PATH_MAX]; |
90 | struct stat st; | |
91 | ||
92 | snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "/%s/bridge", | |
93 | entry->d_name); | |
94 | return stat(path, &st) == 0 && S_ISDIR(st.st_mode); | |
95 | } | |
96 | ||
97 | static int cmd_showbridge(int argc, char *const *argv) | |
98 | { | |
99 | int i, count = 0; | |
100 | int r = 0; | |
101 | struct dirent **namelist; | |
102 | ||
103 | if (argc > 1) { | |
104 | count = argc - 1; | |
105 | } else { | |
106 | count = | |
107 | scandir(SYSFS_CLASS_NET, &namelist, isbridge, alphasort); | |
108 | if (count < 0) { | |
109 | fprintf(stderr, "Error getting list of all bridges\n"); | |
110 | return -1; | |
111 | } | |
112 | } | |
113 | ||
114 | for (i = 0; i < count; i++) { | |
115 | const char *name; | |
116 | if (argc > 1) | |
117 | name = argv[i + 1]; | |
118 | else | |
119 | name = namelist[i]->d_name; | |
120 | ||
121 | int err = do_showbridge(name); | |
122 | if (err) | |
123 | r = err; | |
124 | } | |
125 | ||
126 | if (argc <= 1) { | |
127 | for (i = 0; i < count; i++) | |
128 | free(namelist[i]); | |
129 | free(namelist); | |
130 | } | |
131 | ||
132 | return r; | |
ad02a0eb SH |
133 | } |
134 | ||
b600a2c3 SA |
135 | |
136 | #define STATE_STR(_state) \ | |
137 | ({ \ | |
138 | int _s = _state; \ | |
139 | char *_str = "unknown"; \ | |
140 | switch (_s) \ | |
141 | { \ | |
142 | case STP_PORT_STATE_DISCARDING: _str = "discarding"; break; \ | |
143 | case STP_PORT_STATE_LEARNING: _str = "learning"; break; \ | |
144 | case STP_PORT_STATE_FORWARDING: _str = "forwarding"; break; \ | |
145 | } \ | |
146 | _str; \ | |
147 | }) | |
148 | ||
149 | #define SHORT_STATE_STR(_state) \ | |
150 | ({ \ | |
151 | int _s = _state; \ | |
152 | char *_str = "unkn"; \ | |
153 | switch (_s) \ | |
154 | { \ | |
155 | case STP_PORT_STATE_DISCARDING: _str = "disc"; break; \ | |
156 | case STP_PORT_STATE_LEARNING: _str = "lear"; break; \ | |
157 | case STP_PORT_STATE_FORWARDING: _str = "forw"; break; \ | |
158 | } \ | |
159 | _str; \ | |
160 | }) | |
161 | ||
162 | #define ADMIN_P2P_STR(_state) \ | |
163 | ({ \ | |
164 | int _s = _state; \ | |
165 | char *_str = "unkn"; \ | |
166 | switch (_s) \ | |
167 | { \ | |
168 | case STP_ADMIN_P2P_FORCE_FALSE: _str = "no"; break; \ | |
169 | case STP_ADMIN_P2P_FORCE_TRUE: _str = "yes"; break; \ | |
170 | case STP_ADMIN_P2P_AUTO: _str = "auto"; break; \ | |
171 | } \ | |
172 | _str; \ | |
173 | }) | |
174 | ||
175 | ||
ad02a0eb SH |
176 | int detail = 0; |
177 | ||
b600a2c3 | 178 | static int do_showport(int br_index, const char *port_name) |
11904a35 | 179 | { |
b600a2c3 | 180 | STP_PortStatus s; |
11904a35 SH |
181 | int r = 0; |
182 | int port_index = get_index_die(port_name, "port", 0); | |
183 | if (port_index < 0) | |
184 | return -1; | |
185 | ||
b600a2c3 | 186 | r = CTL_get_port_status(br_index, port_index, &s); |
11904a35 | 187 | if (r) { |
b600a2c3 SA |
188 | fprintf(stderr, "Failed to get port state for port %d\n", |
189 | port_index); | |
11904a35 SH |
190 | return -1; |
191 | } | |
192 | ||
193 | if (detail) { | |
b600a2c3 SA |
194 | printf("%s (%u)\n", port_name, (s.id & 0xfff)); |
195 | printf(" enabled\t\t%4s\n", BOOL_STR(s.enabled)); | |
196 | printf(" port id\t\t%04x\t\t\tstate\t\t%15s\n", | |
197 | s.id, STATE_STR(s.state)); | |
198 | printf(" path cost\t%12d\t\t\tadmin path cost\t%12d\n", | |
199 | s.path_cost, s.admin_path_cost); | |
200 | printf(" designated root\t" BR_ID_FMT, | |
201 | BR_ID_ARGS(s.designated_root)); | |
202 | printf("\tdesignated cost\t%12u\n", s.designated_cost); | |
203 | printf(" designated bridge\t" BR_ID_FMT, | |
204 | BR_ID_ARGS(s.designated_bridge)); | |
205 | printf("\tdesignated port\t\t%04x\n", s.designated_port); | |
206 | printf(" admin edge port\t%4s", BOOL_STR(s.admin_edge_port)); | |
207 | printf("\t\t\tauto edge port\t\t%4s\n", | |
208 | BOOL_STR(s.auto_edge_port)); | |
209 | printf(" oper edge port\t\t%4s", BOOL_STR(s.oper_edge_port)); | |
210 | printf("\t\t\ttoplogy change ack\t%4s\n", BOOL_STR(s.tc_ack)); | |
211 | printf(" point to point\t\t%4s", BOOL_STR(s.oper_p2p)); | |
212 | printf("\t\t\tadmin point to point\t%4s\n", | |
213 | ADMIN_P2P_STR(s.admin_p2p)); | |
11904a35 | 214 | } else { |
b600a2c3 SA |
215 | printf("%c%c %4s %04x %4s " BR_ID_FMT " " BR_ID_FMT " %04x\n", |
216 | (s.oper_p2p) ? ' ' : '*', | |
217 | (s.oper_edge_port) ? 'E' : ' ', | |
218 | port_name, | |
219 | s.id, | |
220 | s.enabled?SHORT_STATE_STR(s.state):"down", | |
221 | BR_ID_ARGS(s.designated_root), | |
222 | BR_ID_ARGS(s.designated_bridge), | |
223 | s.designated_port); | |
11904a35 SH |
224 | } |
225 | return 0; | |
ad02a0eb SH |
226 | } |
227 | ||
228 | static int not_dot_dotdot(const struct dirent *entry) | |
229 | { | |
11904a35 SH |
230 | const char *n = entry->d_name; |
231 | ||
232 | return !(n[0] == '.' && (n[1] == 0 || (n[1] == '.' && n[2] == 0))); | |
ad02a0eb SH |
233 | } |
234 | ||
11904a35 | 235 | static int cmd_showport(int argc, char *const *argv) |
ad02a0eb | 236 | { |
11904a35 SH |
237 | int r = 0; |
238 | ||
239 | int br_index = get_index(argv[1], "bridge"); | |
240 | ||
11904a35 SH |
241 | int i, count = 0; |
242 | struct dirent **namelist; | |
243 | ||
244 | if (argc > 2) { | |
245 | count = argc - 2; | |
246 | } else { | |
247 | char buf[SYSFS_PATH_MAX]; | |
248 | snprintf(buf, sizeof(buf), SYSFS_CLASS_NET "/%s/brif", argv[1]); | |
249 | count = scandir(buf, &namelist, not_dot_dotdot, alphasort); | |
250 | if (count < 0) { | |
251 | fprintf(stderr, | |
252 | "Error getting list of all ports of bridge %s\n", | |
253 | argv[1]); | |
254 | return -1; | |
255 | } | |
256 | } | |
257 | ||
258 | for (i = 0; i < count; i++) { | |
259 | const char *name; | |
260 | if (argc > 2) | |
261 | name = argv[i + 2]; | |
262 | else | |
263 | name = namelist[i]->d_name; | |
264 | ||
b600a2c3 | 265 | int err = do_showport(br_index, name); |
11904a35 SH |
266 | if (err) |
267 | r = err; | |
268 | } | |
269 | ||
270 | if (argc <= 2) { | |
271 | for (i = 0; i < count; i++) | |
272 | free(namelist[i]); | |
273 | free(namelist); | |
274 | } | |
275 | ||
276 | return r; | |
ad02a0eb SH |
277 | } |
278 | ||
11904a35 | 279 | static int cmd_showportdetail(int argc, char *const *argv) |
ad02a0eb | 280 | { |
11904a35 SH |
281 | detail = 1; |
282 | return cmd_showport(argc, argv); | |
ad02a0eb SH |
283 | } |
284 | ||
11904a35 | 285 | unsigned int getuint(const char *s) |
ad02a0eb | 286 | { |
11904a35 SH |
287 | char *end; |
288 | long l; | |
289 | l = strtoul(s, &end, 0); | |
290 | if (*s == 0 || *end != 0 || l > INT_MAX) { | |
291 | fprintf(stderr, "Invalid unsigned int arg %s\n", s); | |
292 | exit(1); | |
293 | } | |
294 | return l; | |
ad02a0eb SH |
295 | } |
296 | ||
11904a35 | 297 | int getenum(const char *s, const char *opt[]) |
ad02a0eb | 298 | { |
11904a35 SH |
299 | int i; |
300 | for (i = 0; opt[i] != NULL; i++) | |
301 | if (strcmp(s, opt[i]) == 0) | |
302 | return i; | |
303 | ||
304 | fprintf(stderr, "Invalid argument %s: expecting one of ", s); | |
305 | for (i = 0; opt[i] != NULL; i++) | |
306 | fprintf(stderr, "%s%s", opt[i], (opt[i + 1] ? ", " : "\n")); | |
307 | ||
308 | exit(1); | |
309 | } | |
310 | ||
311 | int getyesno(const char *s, const char *yes, const char *no) | |
312 | { | |
313 | /* Reverse yes and no so error message looks more normal */ | |
314 | const char *opt[] = { yes, no, NULL }; | |
315 | return 1 - getenum(s, opt); | |
316 | } | |
317 | ||
b600a2c3 SA |
318 | #define set_bridge_cfg(field, value) \ |
319 | ({ \ | |
320 | STP_BridgeConfig c; \ | |
321 | memset(&c, 0, sizeof(c)); \ | |
322 | c.field = value; \ | |
323 | c.set_ ## field = 1; \ | |
324 | int r = CTL_set_bridge_config(br_index, &c); \ | |
325 | if (r) \ | |
326 | printf("Couldn't change bridge " #field "\n"); \ | |
327 | r; \ | |
328 | }) | |
329 | ||
330 | #define set_port_cfg(field, value) \ | |
331 | ({ \ | |
332 | STP_PortConfig c; \ | |
333 | memset(&c, 0, sizeof(c)); \ | |
334 | c.field = value; \ | |
335 | c.set_ ## field = 1; \ | |
336 | int r = CTL_set_port_config(br_index, port_index, &c); \ | |
337 | if (r) \ | |
338 | printf("Couldn't change port " #field "\n"); \ | |
339 | r; \ | |
340 | }) | |
341 | ||
ad02a0eb | 342 | |
ad02a0eb | 343 | |
11904a35 | 344 | static int cmd_setbridgeprio(int argc, char *const *argv) |
ad02a0eb SH |
345 | { |
346 | ||
11904a35 | 347 | int br_index = get_index(argv[1], "bridge"); |
b600a2c3 | 348 | return set_bridge_cfg(bridge_priority, getuint(argv[2])); |
ad02a0eb SH |
349 | } |
350 | ||
11904a35 | 351 | static int cmd_setbridgemaxage(int argc, char *const *argv) |
ad02a0eb SH |
352 | { |
353 | ||
11904a35 | 354 | int br_index = get_index(argv[1], "bridge"); |
b600a2c3 | 355 | return set_bridge_cfg(bridge_max_age, getuint(argv[2])); |
ad02a0eb SH |
356 | } |
357 | ||
11904a35 | 358 | static int cmd_setbridgehello(int argc, char *const *argv) |
ad02a0eb SH |
359 | { |
360 | ||
11904a35 | 361 | int br_index = get_index(argv[1], "bridge"); |
b600a2c3 | 362 | return set_bridge_cfg(bridge_hello_time, getuint(argv[2])); |
ad02a0eb SH |
363 | } |
364 | ||
11904a35 | 365 | static int cmd_setbridgefdelay(int argc, char *const *argv) |
ad02a0eb SH |
366 | { |
367 | ||
11904a35 | 368 | int br_index = get_index(argv[1], "bridge"); |
b600a2c3 | 369 | return set_bridge_cfg(bridge_forward_delay, getuint(argv[2])); |
ad02a0eb SH |
370 | } |
371 | ||
11904a35 | 372 | static int cmd_setbridgeforcevers(int argc, char *const *argv) |
ad02a0eb SH |
373 | { |
374 | ||
11904a35 | 375 | int br_index = get_index(argv[1], "bridge"); |
b600a2c3 SA |
376 | return set_bridge_cfg(bridge_protocol_version, |
377 | 2 * getyesno(argv[2], "normal", "slow")); | |
ad02a0eb SH |
378 | } |
379 | ||
b600a2c3 | 380 | static int cmd_setbridgetxholdcount(int argc, char *const *argv) |
11904a35 | 381 | { |
ad02a0eb | 382 | |
b600a2c3 SA |
383 | int br_index = get_index(argv[1], "bridge"); |
384 | return set_bridge_cfg(bridge_tx_hold_count, getuint(argv[2])); | |
ad02a0eb SH |
385 | } |
386 | ||
b600a2c3 | 387 | |
11904a35 | 388 | static int cmd_setportprio(int argc, char *const *argv) |
ad02a0eb SH |
389 | { |
390 | ||
11904a35 SH |
391 | int br_index = get_index(argv[1], "bridge"); |
392 | int port_index = get_index(argv[2], "port"); | |
b600a2c3 | 393 | return set_port_cfg(port_priority, getuint(argv[3])); |
ad02a0eb SH |
394 | } |
395 | ||
11904a35 | 396 | static int cmd_setportpathcost(int argc, char *const *argv) |
ad02a0eb SH |
397 | { |
398 | ||
11904a35 SH |
399 | int br_index = get_index(argv[1], "bridge"); |
400 | int port_index = get_index(argv[2], "port"); | |
b600a2c3 | 401 | return set_port_cfg(port_pathcost, getuint(argv[3])); |
ad02a0eb SH |
402 | } |
403 | ||
b600a2c3 | 404 | static int cmd_setportadminedge(int argc, char *const *argv) |
ad02a0eb SH |
405 | { |
406 | ||
11904a35 SH |
407 | int br_index = get_index(argv[1], "bridge"); |
408 | int port_index = get_index(argv[2], "port"); | |
b600a2c3 | 409 | return set_port_cfg(port_admin_edge, getyesno(argv[3], "yes", "no")); |
ad02a0eb SH |
410 | } |
411 | ||
b600a2c3 | 412 | static int cmd_setportautoedge(int argc, char *const *argv) |
ad02a0eb SH |
413 | { |
414 | ||
11904a35 SH |
415 | int br_index = get_index(argv[1], "bridge"); |
416 | int port_index = get_index(argv[2], "port"); | |
b600a2c3 | 417 | return set_port_cfg(port_auto_edge, getyesno(argv[3], "yes", "no")); |
ad02a0eb SH |
418 | } |
419 | ||
11904a35 | 420 | static int cmd_setportp2p(int argc, char *const *argv) |
ad02a0eb SH |
421 | { |
422 | ||
11904a35 SH |
423 | int br_index = get_index(argv[1], "bridge"); |
424 | int port_index = get_index(argv[2], "port"); | |
b600a2c3 SA |
425 | const char *opts[] = { "no", "yes", "auto", NULL }; |
426 | int vals[] = { STP_ADMIN_P2P_FORCE_FALSE, STP_ADMIN_P2P_FORCE_TRUE, | |
427 | STP_ADMIN_P2P_AUTO }; | |
ad02a0eb | 428 | |
b600a2c3 | 429 | return set_port_cfg(port_admin_p2p, vals[getenum(argv[3], opts)]); |
ad02a0eb SH |
430 | } |
431 | ||
11904a35 | 432 | static int cmd_portmcheck(int argc, char *const *argv) |
ad02a0eb SH |
433 | { |
434 | ||
11904a35 SH |
435 | int br_index = get_index(argv[1], "bridge"); |
436 | int port_index = get_index(argv[2], "port"); | |
b600a2c3 | 437 | return CTL_port_mcheck(br_index, port_index); |
ad02a0eb SH |
438 | } |
439 | ||
11904a35 | 440 | static int cmd_debuglevel(int argc, char *const *argv) |
ad02a0eb | 441 | { |
11904a35 | 442 | return CTL_set_debug_level(getuint(argv[1])); |
ad02a0eb SH |
443 | } |
444 | ||
11904a35 SH |
445 | struct command { |
446 | int nargs; | |
447 | int optargs; | |
448 | const char *name; | |
449 | int (*func) (int argc, char *const *argv); | |
450 | const char *help; | |
ad02a0eb SH |
451 | }; |
452 | ||
453 | static const struct command commands[] = { | |
11904a35 SH |
454 | {0, 32, "showbridge", cmd_showbridge, |
455 | "[<bridge> ... ]\t\tshow bridge state"}, | |
456 | {1, 32, "showport", cmd_showport, | |
457 | "<bridge> [<port> ... ]\tshow port state"}, | |
458 | {1, 32, "showportdetail", cmd_showportdetail, | |
459 | "<bridge> [<port> ... ]\tshow port state (detail)"}, | |
11904a35 SH |
460 | {2, 0, "setbridgeprio", cmd_setbridgeprio, |
461 | "<bridge> <priority>\tset bridge priority (0-61440)"}, | |
462 | {2, 0, "sethello", cmd_setbridgehello, | |
463 | "<bridge> <hellotime>\tset bridge hello time (1-10)"}, | |
464 | {2, 0, "setmaxage", cmd_setbridgemaxage, | |
465 | "<bridge> <maxage>\tset bridge max age (6-40)"}, | |
466 | {2, 0, "setfdelay", cmd_setbridgefdelay, | |
467 | "<bridge> <fwd_delay>\tset bridge forward delay (4-30)"}, | |
468 | {2, 0, "setforcevers", cmd_setbridgeforcevers, | |
469 | "<bridge> {normal|slow}\tnormal RSTP or force to STP"}, | |
b600a2c3 SA |
470 | {2, 0, "settxholdcount", cmd_setbridgetxholdcount, |
471 | "<bridge> <tx_hold_count>\tset bridge transmit hold count (1-10)"}, | |
11904a35 SH |
472 | {3, 0, "setportprio", cmd_setportprio, |
473 | "<bridge> <port> <priority>\tset port priority (0-240)"}, | |
474 | {3, 0, "setportpathcost", cmd_setportpathcost, | |
475 | "<bridge> <port> <cost>\tset port path cost"}, | |
b600a2c3 SA |
476 | {3, 0, "setportadminedge", cmd_setportadminedge, |
477 | "<bridge> <port> {yes|no}\tconfigure if it is an admin edge port"}, | |
478 | {3, 0, "setportautoedge", cmd_setportautoedge, | |
479 | "<bridge> <port> {yes|no}\tconfigure if it is an auto edge port"}, | |
11904a35 SH |
480 | {3, 0, "setportp2p", cmd_setportp2p, |
481 | "<bridge> <port> {yes|no|auto}\tset whether p2p connection"}, | |
482 | {2, 0, "portmcheck", cmd_portmcheck, | |
483 | "<bridge> <port>\ttry to get back from STP to RSTP mode"}, | |
484 | {1, 0, "debuglevel", cmd_debuglevel, "<level>\t\tLevel of verbosity"}, | |
ad02a0eb SH |
485 | }; |
486 | ||
487 | const struct command *command_lookup(const char *cmd) | |
488 | { | |
489 | int i; | |
490 | ||
11904a35 | 491 | for (i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) { |
ad02a0eb SH |
492 | if (!strcmp(cmd, commands[i].name)) |
493 | return &commands[i]; | |
494 | } | |
495 | ||
496 | return NULL; | |
497 | } | |
498 | ||
499 | void command_helpall(void) | |
500 | { | |
501 | int i; | |
502 | ||
11904a35 | 503 | for (i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) { |
ad02a0eb SH |
504 | printf("\t%-10s\t%s\n", commands[i].name, commands[i].help); |
505 | } | |
506 | } | |
507 | ||
508 | static void help() | |
509 | { | |
510 | printf("Usage: rstpctl [commands]\n"); | |
511 | printf("commands:\n"); | |
512 | command_helpall(); | |
513 | } | |
514 | ||
515 | #define PACKAGE_VERSION2(v, b) "rstp, " #v "-" #b | |
516 | #define PACKAGE_VERSION(v, b) PACKAGE_VERSION2(v, b) | |
517 | ||
11904a35 | 518 | int main(int argc, char *const *argv) |
ad02a0eb SH |
519 | { |
520 | const struct command *cmd; | |
521 | int f; | |
522 | static const struct option options[] = { | |
11904a35 SH |
523 | {.name = "help",.val = 'h'}, |
524 | {.name = "version",.val = 'V'}, | |
525 | {0} | |
ad02a0eb SH |
526 | }; |
527 | ||
11904a35 SH |
528 | while ((f = getopt_long(argc, argv, "Vh", options, NULL)) != EOF) |
529 | switch (f) { | |
ad02a0eb SH |
530 | case 'h': |
531 | help(); | |
532 | return 0; | |
533 | case 'V': | |
534 | printf("%s\n", PACKAGE_VERSION(VERSION, BUILD)); | |
535 | return 0; | |
536 | default: | |
537 | fprintf(stderr, "Unknown option '%c'\n", f); | |
538 | goto help; | |
539 | } | |
11904a35 | 540 | |
ad02a0eb SH |
541 | if (argc == optind) |
542 | goto help; | |
11904a35 | 543 | |
ad02a0eb SH |
544 | if (ctl_client_init()) { |
545 | fprintf(stderr, "can't setup control connection\n"); | |
546 | return 1; | |
547 | } | |
548 | ||
549 | argc -= optind; | |
550 | argv += optind; | |
551 | if ((cmd = command_lookup(argv[0])) == NULL) { | |
552 | fprintf(stderr, "never heard of command [%s]\n", argv[0]); | |
553 | goto help; | |
554 | } | |
11904a35 | 555 | |
ad02a0eb SH |
556 | if (argc < cmd->nargs + 1 || argc > cmd->nargs + cmd->optargs + 1) { |
557 | printf("Incorrect number of arguments for command\n"); | |
558 | printf("Usage: rstpctl %s %s\n", cmd->name, cmd->help); | |
559 | return 1; | |
560 | } | |
561 | ||
562 | return cmd->func(argc, argv); | |
563 | ||
11904a35 | 564 | help: |
ad02a0eb SH |
565 | help(); |
566 | return 1; | |
567 | } |