]> CyberLeo.Net >> Repos - CDN/bash-config.git/blob - bash_completion.d/zfs
Do not export tracking variable: it only makes sense in the current context, and...
[CDN/bash-config.git] / bash_completion.d / zfs
1 #
2 # CDDL HEADER START
3 #
4 # The contents of this file are subject to the terms of the
5 # Common Development and Distribution License (the "License").
6 # You may not use this file except in compliance with the License.
7 #
8 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 # or http://www.opensolaris.org/os/licensing.
10 # See the License for the specific language governing permissions
11 # and limitations under the License.
12 #
13 # When distributing Covered Code, include this CDDL HEADER in each
14 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 # If applicable, add the following below this CDDL HEADER, with the
16 # fields enclosed by brackets "[]" replaced with your own identifying
17 # information: Portions Copyright [yyyy] [name of copyright owner]
18 #
19 # CDDL HEADER END
20 #
21 # Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
22 # Use is subject to license terms.
23 #
24 #----------------------------------------------------------------------
25
26 #
27 # bash command line completion for zfs and zpool
28 #
29 # There are a few restrictions to bash's ability to complete command
30 # lines, and there is at least one bug.
31 #
32 # * Command line arguments:
33 #       Most of zfs's commands take various arguments.  I decided
34 #       against writing a full command line parser for each command,
35 #       the completion code will complete some things that are not
36 #       actually allowed
37 #
38 # * Option lists
39 #       Some of the option arguments are in the form of lists (e.g.
40 #       option,option or even option=val,option=val).  There doesn't
41 #       seem to be a facility in base to handle this properly, so
42 #       this still has to be done manually.
43 #
44 # * @-sign bug
45 #       ZFS uses the '@' character for snapshot names.  This apparently
46 #       confuses the completion code.  To reproduce the bug, create
47 #       a snapshot of a filesystem, e.g. tank/foo@snap, and then
48 #       perform the following steps:
49 #               # zfs send tank/foo<TAB><BACKSPACE><TAB>
50 #       The first <TAB> will cause the snapshot's name to be expanded
51 #       properly.  The <BACKSPACE> will put the cursor at the end of the
52 #       name, and the second <TAB> will cause the completion code to
53 #       replace 'tank/foo@snap' with 'tank/footank/foo@snap'.  I suspect
54 #       the built-in hostname completion code is interfering with the
55 #       snapshot name.
56 #
57 # * Trailing spaces
58 #       Some arguments, such as the <dev> 'zpool create <pool> <dev>'
59 #       work better without trailing spaces, whereas they are expected
60 #       in others, such as the 'create' in that very same command.  There
61 #       does not seem to be a way in bash to specify which arguments
62 #       get spaces and which don't.  This can be frustrating for the user.
63 #
64
65
66 __zfs_get_iprops()
67 {
68         # discover all inheritable properties
69         zfs get 2>&1 | grep YES | awk '{ if ($3 == "YES") print $1 }'
70 }
71
72
73 __zfs_comp()
74 {
75         local cur prev cmds iprops base cmd
76
77         COMPREPLY=()
78         cur="${COMP_WORDS[COMP_CWORD]}"
79         prev="${COMP_WORDS[COMP_CWORD-1]}"
80         base="${COMP_WORDS[1]}"
81
82         # zfs commands
83         cmds="create destroy snapshot rollback clone promote rename \
84                 list set get inherit mount unmount share unshare \
85                 send receive"
86         
87         if [ "${prev##*/}" = "zfs" ]
88         then
89                 COMPREPLY=($(compgen -W "$cmds" -- "$cur"))
90                 return 0
91         fi
92
93         local results
94
95         case "${base}" in
96         create)
97                 #
98                 # To create a filesystem, we need at least a pool name.  We
99                 # use the "-S /" option to add trailing /'s to the names.  It
100                 # would be nice to selectively turn on '-o nospace' here, but
101                 # it doesn't appear possible.  It would also be nice to be
102                 # able to restrict the command line to a single filesystem or
103                 # volume, but that would require extensive command line
104                 # parsing.
105                 #
106                 results=$(/sbin/zfs list -H -o name -t filesystem,volume)
107                 COMPREPLY=($(compgen -S / -W "$results" -- "$cur"))
108                 return 0
109                 ;;
110         destroy|list|set|get)
111                 #
112                 # Destroy differs from create in that we don't need to
113                 # restrict options to filesystems & volumes.  The list, set,
114                 # and get commands kinda fall into this category too.  As
115                 # these three commands can take multiple datasets, we don't
116                 # try to restrict them to one.
117                 #
118                 results=$(/sbin/zfs list -H -o name)
119                 ;;
120         snapshot)
121                 #
122                 # We can only snapshot filesystems and volumes, and we
123                 # *dont* want to add trailing /'s.
124                 #
125                 results=$(/sbin/zfs list -H -o name -t filesystem,volume)
126                 ;;
127         rollback)
128                 #
129                 # Rollback only works with snapshots.
130                 #
131                 results=$(/sbin/zfs list -H -o name -t snapshot)
132                 ;;
133         clone)
134                 #
135                 # Clone takes no options, so this is easier to handle.  The
136                 # first argument (where $prev == $base) has to be a snapshot,
137                 # whereas the second argument has to be a filesystem in
138                 # the same pool.
139                 #
140                 if [ "x$prev" = "x$base" ]
141                 then
142                         results=$(/sbin/zfs list -H -o name -t snapshot)
143                 else
144                         results=$(/sbin/zfs list -H -ro name \
145                             -t filesystem ${prev%%/*} 2>/dev/null)
146                 fi
147                 ;;
148         promote)
149                 #
150                 # Promotions can only be done of cloned filesystems.  We
151                 # check the 'origin' property to see if the filesystem is
152                 # a clone.
153                 #
154                 if [ "x$prev" = "x$base" ]
155                 then
156                         results=$(/sbin/zfs get -rH -o name,value origin |
157                             grep -v -- '-$' | awk '{print $1}')
158                 else
159                         return 1
160                 fi
161                 ;;
162         rename)
163                 #
164                 # The first argument to rename, besides the -r, can be
165                 # of any type.  The second argument has to be of the same
166                 # type as the first.  If -r is specified, then the first
167                 # and second type must be snapshots
168                 #
169                 if [ "x$prev" = "x-r" ]
170                 then
171                         results=$(/sbin/zfs list -H -o name -t snapshot)
172                 else
173                         results=$(/sbin/zfs list -H -o name)
174                 fi
175                 ;;
176         inherit)
177                 #
178                 # Inherit takes a property and one or more filesystems or
179                 # volumes.
180                 #
181                 if [ "x$prev" = "x-r" ] || [ "x$prev" = "x$base" ]
182                 then
183                         results=$(__zfs_get_iprops)
184                 else
185                         results=$(/sbin/zfs list -H -o name \
186                             -t filesystem,volume)
187                 fi
188                 ;;
189         mount|share)
190                 #
191                 # Mount and share can be -a, or a filesystem
192                 #
193                 results=$(/sbin/zfs list -H -o name -t filesystem)
194                 ;;
195         unmount|unshare)
196                 #
197                 # Unmount and unshare can take a dataset filesystem, or a
198                 # mountpoint.  Happily, the output from 'zfs mount' lists
199                 # all of these without any cruft to strip off.
200                 #
201                 results=$(/sbin/zfs mount)
202                 ;;
203         send)
204                 #
205                 # Send can only send snapshots
206                 #
207                 results=$(/sbin/zfs list -H -o name -t snapshot)
208                 ;;
209         receive)
210                 #
211                 # Receive can take any dataset type
212
213                 results=$(/sbin/zfs list -H -o name)
214                 ;;
215         *)
216                 return 1
217                 ;;
218         esac
219
220         #
221         # A quick filter until CR 6540584 is fixed
222         #
223         if [ "$results" = "no datasets available" ]
224         then
225                 results=""
226         fi
227
228         COMPREPLY=($(compgen -W "$results" -- "$cur"))
229         return 0
230 }
231
232 __zpool_devices()
233 {
234         local line found
235
236         found=""
237         /sbin/zpool status -v $1 | while read line
238         do
239                 line="${line%% *}"
240                 if [ "${line}" = "NAME" ]
241                 then
242                         found=1
243                         continue
244                 elif [ ! -z "$found" ] && [ "${line}" = "" ]
245                 then
246                         return 0
247                 fi
248                 if [ ! -z "$found" ]
249                 then
250                         if [ "${line}" = "$1" ] || [ "${line}" = "mirror" ] ||
251                             [ "${line}" = "raidz" ] ||
252                             [ "${line}" = "raidz2" ] || [ "${line}" = "spares" ]
253                         then
254                                 continue
255                         fi
256                         echo ${line}
257                 fi
258         done
259         return 0
260 }
261
262 __zpool_is_pool()
263 {
264         /sbin/zpool list -H -o name | while read pname
265         do
266                 if [ $pname = $1 ]
267                 then
268                         echo yes
269                         break
270                 fi
271         done
272 }
273
274 __zpool_comp()
275 {
276         local cur prev cmds iprops base cmd
277
278         COMPREPLY=()
279         cur="${COMP_WORDS[COMP_CWORD]}"
280         prev="${COMP_WORDS[COMP_CWORD-1]}"
281         base="${COMP_WORDS[1]}"
282
283         # zpool commands
284         cmds="create destroy add remove list iostat status online offline \
285             clear attach detach replace scrub import export upgrade \
286             history get set"
287         
288         if [ "${prev##*/}" = "zpool" ]
289         then
290                 COMPREPLY=($(compgen -W "$cmds" -- "$cur"))
291                 return 0
292         fi
293
294         local results xtra
295
296         xtra=""
297
298         case "${base}" in
299         create)
300                 #
301                 # Pool creation can be a bit tricky to complete, so we just
302                 # go with the built-in options ('mirror', 'raidz', 'raidz2',
303                 # and 'spare') and filename completion (which unfortunately is
304                 # space-happy).
305                 #
306                 if [ "${cur%%/*}" = "" ]
307                 then
308                         results=$(compgen -f "${cur}")
309                 else
310                         results="mirror raidz raidz2 spare"
311                 fi
312                 ;;
313         destroy|history|set|upgrade)
314                 #
315                 # Pool names are only allowed here, plus the -f force option
316                 # for destroy.
317                 #
318                 if [ "x$prev" = "x$base" ] || [ "x$prev" = "x-f" ]
319                 then
320                         results=$(/sbin/zpool list -H -o name)
321                 else
322                         return 1
323                 fi
324                 ;;
325         add)
326                 #
327                 # We can add vdevs to existing pools.  
328                 #
329                 local isopt
330                 isopt=${prev#-}
331                 if [ "x$prev" = "x$base" ] || [ "x$isopt" != "x$prev" ]
332                 then
333                         results=$(/sbin/zpool list -H -o name)
334                 else
335                         results=$(compgen -f "${cur}")
336                 fi
337                 ;;
338         remove|online|offline|clear|detach)
339                 #
340                 # Removing a device from a pool gives us a little more room
341                 # for cleverness: once we know the pool name, we can
342                 # provide a list of devices.  Online, offline, clear, and
343                 # detach work similarly, except that offline takes a -t option
344                 # as well.
345                 #
346                 if [ "x$prev" = "x$base" ]
347                 then
348                         results=$(/sbin/zpool list -H -o name)
349                 elif [ "$base" = "offline" ] && [ "x$prev" = "x-t" ]
350                 then
351                         results=$(/sbin/zpool list -H -o name)
352                 else
353                         results=$(__zpool_devices $prev)
354                 fi
355                 ;;
356                         
357         list|scrub|export|get|iostat|status)
358                 #
359                 # These commands can take any number of pools.
360                 #
361                 results=$(/sbin/zpool list -H -o name)
362                 ;;
363
364         attach|replace)
365                 #
366                 # Attach takes a pool, an existing pool's device, and a
367                 # new device.  Optional -f adds complexity, of course.
368                 # Replace looks nearly the same.
369                 #
370                 if [ "x$prev" = "x$base" ] || [ "x$prev" = "x-f" ]
371                 then
372                         results=$(/sbin/zpool list -H -o name)
373                 elif [ "$(__zpool_is_pool ${prev})" = "yes" ]
374                 then
375                         results=$(__zpool_devices $prev)
376                 else
377                         results=$(compgen -f "${cur}")
378                 fi
379                 ;;
380         *)
381                 return 1
382                 ;;
383         esac
384
385         #
386         # A quick filter until CR 6540584 is fixed
387         #
388         if [ "$results" = "no pools available" ]
389         then
390                 results=""
391         fi
392
393         COMPREPLY=($(compgen $xtra -W "$results" -- "$cur"))
394         return 0
395 }
396
397 complete -F __zfs_comp zfs
398 complete -F __zpool_comp zpool
399
400