]>
Commit | Line | Data |
---|---|---|
27d44f3a QM |
1 | # tc(8) completion -*- shell-script -*- |
2 | # Copyright 2016 6WIND S.A. | |
3 | # Copyright 2016 Quentin Monnet <quentin.monnet@6wind.com> | |
4 | ||
57086f7b YG |
5 | QDISC_KIND=' choke codel bfifo pfifo pfifo_head_drop fq fq_codel gred hhf \ |
6 | mqprio multiq netem pfifo_fast pie red rr sfb sfq tbf atm cbq drr \ | |
7 | dsmark hfsc htb prio qfq ' | |
b32c0b64 | 8 | FILTER_KIND=' basic bpf cgroup flow flower fw route rsvp tcindex u32 matchall ' |
57086f7b YG |
9 | ACTION_KIND=' gact mirred bpf sample ' |
10 | ||
27d44f3a QM |
11 | # Takes a list of words in argument; each one of them is added to COMPREPLY if |
12 | # it is not already present on the command line. Returns no value. | |
13 | _tc_once_attr() | |
14 | { | |
15 | local w subcword found | |
16 | for w in $*; do | |
17 | found=0 | |
18 | for (( subcword=3; subcword < ${#words[@]}-1; subcword++ )); do | |
19 | if [[ $w == ${words[subcword]} ]]; then | |
20 | found=1 | |
21 | break | |
22 | fi | |
23 | done | |
24 | [[ $found -eq 0 ]] && \ | |
25 | COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) ) | |
26 | done | |
27 | } | |
28 | ||
26e0996a YG |
29 | # Takes a list of words in argument; each one of them is added to COMPREPLY if |
30 | # it is not already present on the command line from the provided index. Returns | |
31 | # no value. | |
32 | _tc_once_attr_from() | |
33 | { | |
34 | local w subcword found from=$1 | |
35 | shift | |
36 | for w in $*; do | |
37 | found=0 | |
38 | for (( subcword=$from; subcword < ${#words[@]}-1; subcword++ )); do | |
39 | if [[ $w == ${words[subcword]} ]]; then | |
40 | found=1 | |
41 | break | |
42 | fi | |
43 | done | |
44 | [[ $found -eq 0 ]] && \ | |
45 | COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) ) | |
46 | done | |
47 | } | |
48 | ||
27d44f3a QM |
49 | # Takes a list of words in argument; adds them all to COMPREPLY if none of them |
50 | # is already present on the command line. Returns no value. | |
51 | _tc_one_of_list() | |
52 | { | |
53 | local w subcword | |
54 | for w in $*; do | |
55 | for (( subcword=3; subcword < ${#words[@]}-1; subcword++ )); do | |
56 | [[ $w == ${words[subcword]} ]] && return 1 | |
57 | done | |
58 | done | |
59 | COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) ) | |
60 | } | |
61 | ||
26e0996a YG |
62 | # Takes a list of words in argument; adds them all to COMPREPLY if none of them |
63 | # is already present on the command line from the provided index. Returns no | |
64 | # value. | |
65 | _tc_one_of_list_from() | |
66 | { | |
67 | local w subcword from=$1 | |
68 | shift | |
69 | for w in $*; do | |
70 | for (( subcword=$from; subcword < ${#words[@]}-1; subcword++ )); do | |
71 | [[ $w == ${words[subcword]} ]] && return 1 | |
72 | done | |
73 | done | |
74 | COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) ) | |
75 | } | |
76 | ||
27d44f3a QM |
77 | # Returns "$cur ${cur}arg1 ${cur}arg2 ..." |
78 | _tc_expand_units() | |
79 | { | |
80 | [[ $cur =~ ^[0-9]+ ]] || return 1 | |
81 | local value=${cur%%[^0-9]*} | |
82 | [[ $cur == $value ]] && echo $cur | |
83 | echo ${@/#/$value} | |
84 | } | |
85 | ||
86 | # Complete based on given word, usually $prev (or possibly the word before), | |
87 | # for when an argument or an option name has but a few possible arguments (so | |
88 | # tc does not take particular commands into account here). | |
89 | # Returns 0 is completion should stop after running this function, 1 otherwise. | |
90 | _tc_direct_complete() | |
91 | { | |
92 | case $1 in | |
93 | # Command options | |
94 | dev) | |
95 | _available_interfaces | |
96 | return 0 | |
97 | ;; | |
98 | classid) | |
99 | return 0 | |
100 | ;; | |
101 | estimator) | |
102 | local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' ) | |
103 | COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) ) | |
104 | return 0 | |
105 | ;; | |
106 | handle) | |
107 | return 0 | |
108 | ;; | |
109 | parent|flowid) | |
110 | local i iface ids cmd | |
111 | for (( i=3; i < ${#words[@]}-2; i++ )); do | |
112 | [[ ${words[i]} == dev ]] && iface=${words[i+1]} | |
113 | break | |
114 | done | |
115 | for cmd in qdisc class; do | |
116 | if [[ -n $iface ]]; then | |
117 | ids+=$( tc $cmd show dev $iface 2>/dev/null | \ | |
118 | cut -d\ -f 3 )" " | |
119 | else | |
120 | ids+=$( tc $cmd show 2>/dev/null | cut -d\ -f 3 ) | |
121 | fi | |
122 | done | |
123 | [[ $ids != " " ]] && \ | |
124 | COMPREPLY+=( $( compgen -W "$ids" -- "$cur" ) ) | |
125 | return 0 | |
126 | ;; | |
127 | protocol) # list comes from lib/ll_proto.c | |
128 | COMPREPLY+=( $( compgen -W ' 802.1Q 802.1ad 802_2 802_3 LLDP aarp \ | |
129 | all aoe arp atalk atmfate atmmpoa ax25 bpq can control cust \ | |
130 | ddcmp dec diag dna_dl dna_rc dna_rt econet ieeepup ieeepupat \ | |
131 | ip ipv4 ipv6 ipx irda lat localtalk loop mobitex ppp_disc \ | |
132 | ppp_mp ppp_ses ppptalk pup pupat rarp sca snap tipc tr_802_2 \ | |
133 | wan_ppp x25' -- "$cur" ) ) | |
134 | return 0 | |
135 | ;; | |
136 | prio) | |
137 | return 0 | |
138 | ;; | |
139 | stab) | |
140 | COMPREPLY+=( $( compgen -W 'mtu tsize mpu overhead | |
141 | linklayer' -- "$cur" ) ) | |
142 | ;; | |
143 | ||
144 | # Qdiscs and classes options | |
145 | alpha|bands|beta|buckets|corrupt|debug|decrement|default|\ | |
146 | default_index|depth|direct_qlen|divisor|duplicate|ewma|flow_limit|\ | |
147 | flows|hh_limit|increment|indices|linklayer|non_hh_weight|num_tc|\ | |
148 | penalty_burst|penalty_rate|prio|priomap|probability|queues|r2q|\ | |
149 | reorder|vq|vqs) | |
150 | return 0 | |
151 | ;; | |
152 | setup) | |
153 | COMPREPLY+=( $( compgen -W 'vqs' -- "$cur" ) ) | |
154 | return 0 | |
155 | ;; | |
156 | hw) | |
157 | COMPREPLY+=( $( compgen -W '1 0' -- "$cur" ) ) | |
158 | return 0 | |
159 | ;; | |
160 | distribution) | |
161 | COMPREPLY+=( $( compgen -W 'uniform normal pareto | |
162 | paretonormal' -- "$cur" ) ) | |
163 | return 0 | |
164 | ;; | |
165 | loss) | |
166 | COMPREPLY+=( $( compgen -W 'random state gmodel' -- "$cur" ) ) | |
167 | return 0 | |
168 | ;; | |
169 | ||
170 | # Qdiscs and classes options options | |
171 | gap|gmodel|state) | |
172 | return 0 | |
173 | ;; | |
174 | ||
175 | # Filters options | |
176 | map) | |
177 | COMPREPLY+=( $( compgen -W 'key' -- "$cur" ) ) | |
178 | return 0 | |
179 | ;; | |
180 | hash) | |
181 | COMPREPLY+=( $( compgen -W 'keys' -- "$cur" ) ) | |
182 | return 0 | |
183 | ;; | |
184 | indev) | |
185 | _available_interfaces | |
186 | return 0 | |
187 | ;; | |
188 | eth_type) | |
189 | COMPREPLY+=( $( compgen -W 'ipv4 ipv6' -- "$cur" ) ) | |
190 | return 0 | |
191 | ;; | |
192 | ip_proto) | |
193 | COMPREPLY+=( $( compgen -W 'tcp udp' -- "$cur" ) ) | |
194 | return 0 | |
195 | ;; | |
196 | ||
197 | # Filters options options | |
198 | key|keys) | |
199 | [[ ${words[@]} =~ graft ]] && return 1 | |
200 | COMPREPLY+=( $( compgen -W 'src dst proto proto-src proto-dst iif \ | |
201 | priority mark nfct nfct-src nfct-dst nfct-proto-src \ | |
202 | nfct-proto-dst rt-classid sk-uid sk-gid vlan-tag rxhash' -- \ | |
203 | "$cur" ) ) | |
204 | return 0 | |
205 | ;; | |
206 | ||
207 | # BPF options - used for filters, actions, and exec | |
208 | export|bytecode|bytecode-file|object-file) | |
209 | _filedir | |
210 | return 0 | |
211 | ;; | |
212 | object-pinned|graft) # Pinned object is probably under /sys/fs/bpf/ | |
213 | [[ -n "$cur" ]] && _filedir && return 0 | |
214 | COMPREPLY=( $( compgen -G "/sys/fs/bpf/*" -- "$cur" ) ) || _filedir | |
215 | compopt -o nospace | |
216 | return 0 | |
217 | ;; | |
218 | section) | |
219 | if (type objdump > /dev/null 2>&1) ; then | |
220 | local fword objfile section_list | |
221 | for (( fword=3; fword < ${#words[@]}-3; fword++ )); do | |
222 | if [[ ${words[fword]} == object-file ]]; then | |
223 | objfile=${words[fword+1]} | |
224 | break | |
225 | fi | |
226 | done | |
227 | section_list=$( objdump -h $objfile 2>/dev/null | \ | |
228 | sed -n 's/^ *[0-9]\+ \([^ ]*\) *.*/\1/p' ) | |
229 | COMPREPLY+=( $( compgen -W "$section_list" -- "$cur" ) ) | |
230 | fi | |
231 | return 0 | |
232 | ;; | |
233 | import|run) | |
234 | _filedir | |
235 | return 0 | |
236 | ;; | |
237 | type) | |
238 | COMPREPLY+=( $( compgen -W 'cls act' -- "$cur" ) ) | |
239 | return 0 | |
240 | ;; | |
241 | ||
242 | # Actions options | |
243 | random) | |
244 | _tc_one_of_list 'netrand determ' | |
245 | return 0 | |
246 | ;; | |
247 | ||
248 | # Units for option arguments | |
249 | bandwidth|maxrate|peakrate|rate) | |
250 | local list=$( _tc_expand_units 'bit' \ | |
251 | 'kbit' 'kibit' 'kbps' 'kibps' \ | |
252 | 'mbit' 'mibit' 'mbps' 'mibps' \ | |
253 | 'gbit' 'gibit' 'gbps' 'gibps' \ | |
254 | 'tbit' 'tibit' 'tbps' 'tibps' ) | |
255 | COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) ) | |
256 | ;; | |
257 | admit_bytes|avpkt|burst|cell|initial_quantum|limit|max|min|mtu|mpu|\ | |
258 | overhead|quantum|redflowlist) | |
259 | local list=$( _tc_expand_units \ | |
260 | 'b' 'kbit' 'k' 'mbit' 'm' 'gbit' 'g' ) | |
261 | COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) ) | |
262 | ;; | |
263 | db|delay|evict_timeout|interval|latency|perturb|rehash|reset_timeout|\ | |
264 | target|tupdate) | |
265 | local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' ) | |
266 | COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) ) | |
267 | ;; | |
268 | esac | |
269 | return 1 | |
270 | } | |
271 | ||
272 | # Complete with options names for qdiscs. Each qdisc has its own set of options | |
273 | # and it seems we cannot really parse it from anywhere, so we add it manually | |
274 | # in this function. | |
275 | # Returns 0 is completion should stop after running this function, 1 otherwise. | |
276 | _tc_qdisc_options() | |
277 | { | |
278 | case $1 in | |
279 | choke) | |
280 | _tc_once_attr 'limit bandwidth ecn min max burst' | |
281 | return 0 | |
282 | ;; | |
283 | codel) | |
284 | _tc_once_attr 'limit target interval' | |
285 | _tc_one_of_list 'ecn noecn' | |
286 | return 0 | |
287 | ;; | |
288 | bfifo|pfifo|pfifo_head_drop) | |
289 | _tc_once_attr 'limit' | |
290 | return 0 | |
291 | ;; | |
292 | fq) | |
293 | _tc_once_attr 'limit flow_limit quantum initial_quantum maxrate \ | |
294 | buckets' | |
295 | _tc_one_of_list 'pacing nopacing' | |
296 | return 0 | |
297 | ;; | |
298 | fq_codel) | |
299 | _tc_once_attr 'limit flows target interval quantum' | |
300 | _tc_one_of_list 'ecn noecn' | |
301 | return 0 | |
302 | ;; | |
303 | gred) | |
304 | _tc_once_attr 'setup vqs default grio vq prio limit min max avpkt \ | |
305 | burst probability bandwidth' | |
306 | return 0 | |
307 | ;; | |
308 | hhf) | |
309 | _tc_once_attr 'limit quantum hh_limit reset_timeout admit_bytes \ | |
310 | evict_timeout non_hh_weight' | |
311 | return 0 | |
312 | ;; | |
313 | mqprio) | |
314 | _tc_once_attr 'num_tc map queues hw' | |
315 | return 0 | |
316 | ;; | |
317 | netem) | |
318 | _tc_once_attr 'delay distribution corrupt duplicate loss ecn \ | |
319 | reorder rate' | |
320 | return 0 | |
321 | ;; | |
322 | pie) | |
323 | _tc_once_attr 'limit target tupdate alpha beta' | |
324 | _tc_one_of_list 'bytemode nobytemode' | |
325 | _tc_one_of_list 'ecn noecn' | |
326 | return 0 | |
327 | ;; | |
328 | red) | |
329 | _tc_once_attr 'limit min max avpkt burst adaptive probability \ | |
330 | bandwidth ecn harddrop' | |
331 | return 0 | |
332 | ;; | |
333 | rr|prio) | |
334 | _tc_once_attr 'bands priomap multiqueue' | |
335 | return 0 | |
336 | ;; | |
337 | sfb) | |
338 | _tc_once_attr 'rehash db limit max target increment decrement \ | |
339 | penalty_rate penalty_burst' | |
340 | return 0 | |
341 | ;; | |
342 | sfq) | |
343 | _tc_once_attr 'limit perturb quantum divisor flows depth headdrop \ | |
344 | redflowlimit min max avpkt burst probability ecn harddrop' | |
345 | return 0 | |
346 | ;; | |
347 | tbf) | |
348 | _tc_once_attr 'limit burst rate mtu peakrate latency overhead \ | |
349 | linklayer' | |
350 | return 0 | |
351 | ;; | |
352 | cbq) | |
353 | _tc_once_attr 'bandwidth avpkt mpu cell ewma' | |
354 | return 0 | |
355 | ;; | |
356 | dsmark) | |
357 | _tc_once_attr 'indices default_index set_tc_index' | |
358 | return 0 | |
359 | ;; | |
360 | hfsc) | |
361 | _tc_once_attr 'default' | |
362 | return 0 | |
363 | ;; | |
364 | htb) | |
365 | _tc_once_attr 'default r2q direct_qlen debug' | |
366 | return 0 | |
367 | ;; | |
368 | multiq|pfifo_fast|atm|drr|qfq) | |
369 | return 0 | |
370 | ;; | |
371 | esac | |
372 | return 1 | |
373 | } | |
374 | ||
375 | # Complete with options names for BPF filters or actions. | |
376 | # Returns 0 is completion should stop after running this function, 1 otherwise. | |
377 | _tc_bpf_options() | |
378 | { | |
379 | [[ ${words[${#words[@]}-3]} == object-file ]] && \ | |
380 | _tc_once_attr 'section export' | |
381 | [[ ${words[${#words[@]}-5]} == object-file ]] && \ | |
382 | [[ ${words[${#words[@]}-3]} =~ (section|export) ]] && \ | |
383 | _tc_once_attr 'section export' | |
384 | _tc_one_of_list 'bytecode bytecode-file object-file object-pinned' | |
385 | _tc_once_attr 'verbose index direct-action action classid' | |
386 | return 0 | |
387 | } | |
388 | ||
a9d2f4d8 YG |
389 | # Complete with options names for filter actions. |
390 | # This function is recursive, thus allowing multiple actions statement to be | |
391 | # parsed. | |
392 | # Returns 0 is completion should stop after running this function, 1 otherwise. | |
393 | _tc_filter_action_options() | |
394 | { | |
395 | for ((acwd=$1; acwd < ${#words[@]}-1; acwd++)); | |
396 | do | |
397 | if [[ action == ${words[acwd]} ]]; then | |
398 | _tc_filter_action_options $((acwd+1)) && return 0 | |
399 | fi | |
400 | done | |
401 | ||
402 | local action acwd | |
403 | for ((acwd=$1; acwd < ${#words[@]}-1; acwd++)); do | |
404 | if [[ $ACTION_KIND =~ ' '${words[acwd]}' ' ]]; then | |
405 | _tc_one_of_list_from $acwd action | |
406 | _tc_action_options $acwd && return 0 | |
407 | fi | |
408 | done | |
409 | _tc_one_of_list_from $acwd $ACTION_KIND | |
410 | return 0 | |
411 | } | |
412 | ||
27d44f3a QM |
413 | # Complete with options names for filters. |
414 | # Returns 0 is completion should stop after running this function, 1 otherwise. | |
415 | _tc_filter_options() | |
416 | { | |
a9d2f4d8 YG |
417 | |
418 | for ((acwd=$1; acwd < ${#words[@]}-1; acwd++)); | |
419 | do | |
420 | if [[ action == ${words[acwd]} ]]; then | |
421 | _tc_filter_action_options $((acwd+1)) && return 0 | |
422 | fi | |
423 | done | |
424 | ||
425 | filter=${words[$1]} | |
426 | case $filter in | |
27d44f3a QM |
427 | basic) |
428 | _tc_once_attr 'match action classid' | |
429 | return 0 | |
430 | ;; | |
431 | bpf) | |
432 | _tc_bpf_options | |
433 | return 0 | |
434 | ;; | |
435 | cgroup) | |
436 | _tc_once_attr 'match action' | |
437 | return 0 | |
438 | ;; | |
439 | flow) | |
440 | local i | |
441 | for (( i=5; i < ${#words[@]}-1; i++ )); do | |
442 | if [[ ${words[i]} =~ ^keys?$ ]]; then | |
443 | _tc_direct_complete 'key' | |
444 | COMPREPLY+=( $( compgen -W 'or and xor rshift addend' -- \ | |
445 | "$cur" ) ) | |
446 | break | |
447 | fi | |
448 | done | |
449 | _tc_once_attr 'map hash divisor baseclass match action' | |
450 | return 0 | |
451 | ;; | |
b32c0b64 | 452 | matchall) |
88b428f0 | 453 | _tc_once_attr 'action classid skip_sw skip_hw' |
b32c0b64 YG |
454 | return 0 |
455 | ;; | |
27d44f3a QM |
456 | flower) |
457 | _tc_once_attr 'action classid indev dst_mac src_mac eth_type \ | |
458 | ip_proto dst_ip src_ip dst_port src_port' | |
459 | return 0 | |
460 | ;; | |
461 | fw) | |
462 | _tc_once_attr 'action classid' | |
463 | return 0 | |
464 | ;; | |
465 | route) | |
466 | _tc_one_of_list 'from fromif' | |
467 | _tc_once_attr 'to classid action' | |
468 | return 0 | |
469 | ;; | |
470 | rsvp) | |
471 | _tc_once_attr 'ipproto session sender classid action tunnelid \ | |
472 | tunnel flowlabel spi/ah spi/esp u8 u16 u32' | |
473 | [[ ${words[${#words[@]}-3]} == tunnel ]] && \ | |
474 | COMPREPLY+=( $( compgen -W 'skip' -- "$cur" ) ) | |
475 | [[ ${words[${#words[@]}-3]} =~ u(8|16|32) ]] && \ | |
476 | COMPREPLY+=( $( compgen -W 'mask' -- "$cur" ) ) | |
477 | [[ ${words[${#words[@]}-3]} == mask ]] && \ | |
478 | COMPREPLY+=( $( compgen -W 'at' -- "$cur" ) ) | |
479 | return 0 | |
480 | ;; | |
481 | tcindex) | |
482 | _tc_once_attr 'hash mask shift classid action' | |
483 | _tc_one_of_list 'pass_on fall_through' | |
484 | return 0 | |
485 | ;; | |
486 | u32) | |
487 | _tc_once_attr 'match link classid action offset ht hashkey sample' | |
488 | COMPREPLY+=( $( compgen -W 'ip ip6 udp tcp icmp u8 u16 u32 mark \ | |
489 | divisor' -- "$cur" ) ) | |
490 | return 0 | |
491 | ;; | |
492 | esac | |
493 | return 1 | |
494 | } | |
495 | ||
496 | # Complete with options names for actions. | |
497 | # Returns 0 is completion should stop after running this function, 1 otherwise. | |
498 | _tc_action_options() | |
499 | { | |
f62b54a1 YG |
500 | local from=$1 |
501 | local action=${words[from]} | |
502 | case $action in | |
27d44f3a QM |
503 | bpf) |
504 | _tc_bpf_options | |
505 | return 0 | |
506 | ;; | |
507 | mirred) | |
f62b54a1 YG |
508 | _tc_one_of_list_from $from 'ingress egress' |
509 | _tc_one_of_list_from $from 'mirror redirect' | |
510 | _tc_once_attr_from $from 'index dev' | |
27d44f3a QM |
511 | return 0 |
512 | ;; | |
0b1abd84 | 513 | sample) |
f62b54a1 YG |
514 | _tc_once_attr_from $from 'rate' |
515 | _tc_once_attr_from $from 'trunc' | |
516 | _tc_once_attr_from $from 'group' | |
0b1abd84 YG |
517 | return 0 |
518 | ;; | |
27d44f3a | 519 | gact) |
f62b54a1 YG |
520 | _tc_one_of_list_from $from 'reclassify drop continue pass' |
521 | _tc_once_attr_from $from 'random' | |
27d44f3a QM |
522 | return 0 |
523 | ;; | |
524 | esac | |
525 | return 1 | |
526 | } | |
527 | ||
528 | # Complete with options names for exec. | |
529 | # Returns 0 is completion should stop after running this function, 1 otherwise. | |
530 | _tc_exec_options() | |
531 | { | |
532 | case $1 in | |
533 | import) | |
534 | [[ ${words[${#words[@]}-3]} == import ]] && \ | |
535 | _tc_once_attr 'run' | |
536 | return 0 | |
537 | ;; | |
538 | graft) | |
539 | COMPREPLY+=( $( compgen -W 'key type' -- "$cur" ) ) | |
540 | [[ ${words[${#words[@]}-3]} == object-file ]] && \ | |
541 | _tc_once_attr 'type' | |
542 | _tc_bpf_options | |
543 | return 0 | |
544 | ;; | |
545 | esac | |
546 | return 1 | |
547 | } | |
548 | ||
549 | # Main completion function | |
550 | # Logic is as follows: | |
551 | # 1. Check if previous word is a global option; if so, propose arguments. | |
552 | # 2. Check if current word is a global option; if so, propose completion. | |
553 | # 3. Check for the presence of a main command (qdisc|class|filter|...). If | |
554 | # there is one, first call _tc_direct_complete to see if previous word is | |
555 | # waiting for a particular completion. If so, propose completion and exit. | |
556 | # 4. Extract main command and -- if available -- its subcommand | |
557 | # (add|delete|show|...). | |
558 | # 5. Propose completion based on main and sub- command in use. Additional | |
559 | # functions may be called for qdiscs, classes or filter options. | |
560 | _tc() | |
561 | { | |
562 | local cur prev words cword | |
563 | _init_completion || return | |
564 | ||
565 | case $prev in | |
566 | -V|-Version) | |
567 | return 0 | |
568 | ;; | |
569 | -b|-batch|-cf|-conf) | |
570 | _filedir | |
571 | return 0 | |
572 | ;; | |
573 | -force) | |
574 | COMPREPLY=( $( compgen -W '-batch' -- "$cur" ) ) | |
575 | return 0 | |
576 | ;; | |
577 | -nm|name) | |
578 | [[ -r /etc/iproute2/tc_cls ]] || \ | |
579 | COMPREPLY=( $( compgen -W '-conf' -- "$cur" ) ) | |
580 | return 0 | |
581 | ;; | |
582 | -n|-net|-netns) | |
583 | local nslist=$( ip netns list 2>/dev/null ) | |
584 | COMPREPLY+=( $( compgen -W "$nslist" -- "$cur" ) ) | |
585 | return 0 | |
586 | ;; | |
587 | -tshort) | |
588 | _tc_once_attr '-statistics' | |
589 | COMPREPLY+=( $( compgen -W 'monitor' -- "$cur" ) ) | |
590 | return 0 | |
591 | ;; | |
592 | -timestamp) | |
593 | _tc_once_attr '-statistics -tshort' | |
594 | COMPREPLY+=( $( compgen -W 'monitor' -- "$cur" ) ) | |
595 | return 0 | |
596 | ;; | |
597 | esac | |
598 | ||
599 | # Search for main commands | |
600 | local subcword cmd subcmd | |
601 | for (( subcword=1; subcword < ${#words[@]}-1; subcword++ )); do | |
602 | [[ ${words[subcword]} == -b?(atch) ]] && return 0 | |
603 | [[ -n $cmd ]] && subcmd=${words[subcword]} && break | |
604 | [[ ${words[subcword]} != -* && \ | |
605 | ${words[subcword-1]} != -@(n?(et?(ns))|c?(on)f) ]] && \ | |
606 | cmd=${words[subcword]} | |
607 | done | |
608 | ||
609 | if [[ -z $cmd ]]; then | |
610 | case $cur in | |
611 | -*) | |
612 | local c='-Version -statistics -details -raw -pretty \ | |
613 | -iec -graphe -batch -name -netns -timestamp' | |
614 | [[ $cword -eq 1 ]] && c+=' -force' | |
615 | COMPREPLY=( $( compgen -W "$c" -- "$cur" ) ) | |
616 | return 0 | |
617 | ;; | |
618 | *) | |
619 | COMPREPLY=( $( compgen -W "help $( tc help 2>&1 | \ | |
620 | command sed \ | |
621 | -e '/OBJECT := /!d' \ | |
622 | -e 's/.*{//' \ | |
623 | -e 's/}.*//' \ | |
624 | -e \ 's/|//g' )" -- "$cur" ) ) | |
625 | return 0 | |
626 | ;; | |
627 | esac | |
628 | fi | |
629 | ||
630 | [[ $subcmd == help ]] && return 0 | |
631 | ||
632 | # For this set of commands we may create COMPREPLY just by analysing the | |
633 | # previous word, if it expects for a specific list of options or values. | |
634 | if [[ $cmd =~ (qdisc|class|filter|action|exec) ]]; then | |
635 | _tc_direct_complete $prev && return 0 | |
636 | if [[ ${words[${#words[@]}-3]} == estimator ]]; then | |
637 | local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' ) | |
638 | COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) ) && return 0 | |
639 | fi | |
640 | fi | |
641 | ||
642 | # Completion depends on main command and subcommand in use. | |
643 | case $cmd in | |
644 | qdisc) | |
645 | case $subcmd in | |
646 | add|change|replace|link|del|delete) | |
647 | if [[ $(($cword-$subcword)) -eq 1 ]]; then | |
648 | COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) ) | |
649 | return 0 | |
650 | fi | |
57086f7b | 651 | local qdisc qdwd |
27d44f3a QM |
652 | for ((qdwd=$subcword; qdwd < ${#words[@]}-1; qdwd++)); do |
653 | if [[ $QDISC_KIND =~ ' '${words[qdwd]}' ' ]]; then | |
654 | qdisc=${words[qdwd]} | |
655 | _tc_qdisc_options $qdisc && return 0 | |
656 | fi | |
657 | done | |
658 | _tc_one_of_list $QDISC_KIND | |
659 | _tc_one_of_list 'root ingress parent clsact' | |
660 | _tc_once_attr 'handle estimator stab' | |
661 | ;; | |
662 | show) | |
663 | _tc_once_attr 'dev' | |
664 | _tc_one_of_list 'ingress clsact' | |
665 | _tc_once_attr '-statistics -details -raw -pretty -iec \ | |
666 | -graph -name' | |
667 | ;; | |
668 | help) | |
669 | return 0 | |
670 | ;; | |
671 | *) | |
672 | [[ $cword -eq $subcword ]] && \ | |
673 | COMPREPLY=( $( compgen -W 'help add delete change \ | |
674 | replace link show' -- "$cur" ) ) | |
675 | ;; | |
676 | esac | |
677 | ;; | |
678 | ||
679 | class) | |
680 | case $subcmd in | |
681 | add|change|replace|del|delete) | |
682 | if [[ $(($cword-$subcword)) -eq 1 ]]; then | |
683 | COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) ) | |
684 | return 0 | |
685 | fi | |
57086f7b | 686 | local qdisc qdwd |
27d44f3a QM |
687 | for ((qdwd=$subcword; qdwd < ${#words[@]}-1; qdwd++)); do |
688 | if [[ $QDISC_KIND =~ ' '${words[qdwd]}' ' ]]; then | |
689 | qdisc=${words[qdwd]} | |
690 | _tc_qdisc_options $qdisc && return 0 | |
691 | fi | |
692 | done | |
693 | _tc_one_of_list $QDISC_KIND | |
694 | _tc_one_of_list 'root parent' | |
695 | _tc_once_attr 'classid' | |
696 | ;; | |
697 | show) | |
698 | _tc_once_attr 'dev' | |
699 | _tc_one_of_list 'root parent' | |
700 | _tc_once_attr '-statistics -details -raw -pretty -iec \ | |
701 | -graph -name' | |
702 | ;; | |
703 | help) | |
704 | return 0 | |
705 | ;; | |
706 | *) | |
707 | [[ $cword -eq $subcword ]] && \ | |
708 | COMPREPLY=( $( compgen -W 'help add delete change \ | |
709 | replace show' -- "$cur" ) ) | |
710 | ;; | |
711 | esac | |
712 | ;; | |
713 | ||
714 | filter) | |
715 | case $subcmd in | |
716 | add|change|replace|del|delete) | |
717 | if [[ $(($cword-$subcword)) -eq 1 ]]; then | |
718 | COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) ) | |
719 | return 0 | |
720 | fi | |
57086f7b | 721 | local filter fltwd |
27d44f3a QM |
722 | for ((fltwd=$subcword; fltwd < ${#words[@]}-1; fltwd++)); |
723 | do | |
724 | if [[ $FILTER_KIND =~ ' '${words[fltwd]}' ' ]]; then | |
a9d2f4d8 | 725 | _tc_filter_options $fltwd && return 0 |
27d44f3a QM |
726 | fi |
727 | done | |
728 | _tc_one_of_list $FILTER_KIND | |
729 | _tc_one_of_list 'root ingress egress parent' | |
730 | _tc_once_attr 'handle estimator pref protocol' | |
731 | ;; | |
732 | show) | |
733 | _tc_once_attr 'dev' | |
734 | _tc_one_of_list 'root ingress egress parent' | |
735 | _tc_once_attr '-statistics -details -raw -pretty -iec \ | |
736 | -graph -name' | |
737 | ;; | |
738 | help) | |
739 | return 0 | |
740 | ;; | |
741 | *) | |
742 | [[ $cword -eq $subcword ]] && \ | |
743 | COMPREPLY=( $( compgen -W 'help add delete change \ | |
744 | replace show' -- "$cur" ) ) | |
745 | ;; | |
746 | esac | |
747 | ;; | |
748 | ||
749 | action) | |
750 | case $subcmd in | |
751 | add|change|replace) | |
57086f7b | 752 | local action acwd |
27d44f3a QM |
753 | for ((acwd=$subcword; acwd < ${#words[@]}-1; acwd++)); do |
754 | if [[ $ACTION_KIND =~ ' '${words[acwd]}' ' ]]; then | |
f62b54a1 | 755 | _tc_action_options $acwd && return 0 |
27d44f3a QM |
756 | fi |
757 | done | |
758 | _tc_one_of_list $ACTION_KIND | |
759 | ;; | |
760 | get|del|delete) | |
761 | _tc_once_attr 'index' | |
762 | ;; | |
763 | lst|list|flush|show) | |
764 | _tc_one_of_list $ACTION_KIND | |
765 | ;; | |
766 | *) | |
767 | [[ $cword -eq $subcword ]] && \ | |
768 | COMPREPLY=( $( compgen -W 'help add delete change \ | |
769 | replace show list flush action' -- "$cur" ) ) | |
770 | ;; | |
771 | esac | |
772 | ;; | |
773 | ||
774 | monitor) | |
775 | COMPREPLY=( $( compgen -W 'help' -- "$cur" ) ) | |
776 | ;; | |
777 | ||
778 | exec) | |
779 | case $subcmd in | |
780 | bpf) | |
781 | local excmd exwd EXEC_KIND=' import debug graft ' | |
782 | for ((exwd=$subcword; exwd < ${#words[@]}-1; exwd++)); do | |
783 | if [[ $EXEC_KIND =~ ' '${words[exwd]}' ' ]]; then | |
784 | excmd=${words[exwd]} | |
785 | _tc_exec_options $excmd && return 0 | |
786 | fi | |
787 | done | |
788 | _tc_one_of_list $EXEC_KIND | |
789 | ;; | |
790 | *) | |
791 | [[ $cword -eq $subcword ]] && \ | |
792 | COMPREPLY=( $( compgen -W 'bpf' -- "$cur" ) ) | |
793 | ;; | |
794 | esac | |
795 | ;; | |
796 | esac | |
797 | } && | |
798 | complete -F _tc tc | |
799 | ||
800 | # ex: ts=4 sw=4 et filetype=sh |