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