Btrfs-progs: add btrfs send/receive commands
[platform/upstream/btrfs-progs.git] / btrfs.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public
4  * License v2 as published by the Free Software Foundation.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
9  * General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the
13  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14  * Boston, MA 021110-1307, USA.
15  */
16
17 #define _GNU_SOURCE
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #include "commands.h"
23 #include "version.h"
24
25 static const char * const btrfs_cmd_group_usage[] = {
26         "btrfs [--help] [--version] <group> [<group>...] <command> [<args>]",
27         NULL
28 };
29
30 static const char btrfs_cmd_group_info[] =
31         "Use --help as an argument for information on a specific group or command.";
32
33 char argv0_buf[ARGV0_BUF_SIZE] = "btrfs";
34
35 static inline const char *skip_prefix(const char *str, const char *prefix)
36 {
37         size_t len = strlen(prefix);
38         return strncmp(str, prefix, len) ? NULL : str + len;
39 }
40
41 int prefixcmp(const char *str, const char *prefix)
42 {
43         for (; ; str++, prefix++)
44                 if (!*prefix)
45                         return 0;
46                 else if (*str != *prefix)
47                         return (unsigned char)*prefix - (unsigned char)*str;
48 }
49
50 static int parse_one_token(const char *arg, const struct cmd_group *grp,
51                            const struct cmd_struct **cmd_ret)
52 {
53         const struct cmd_struct *cmd = grp->commands;
54         const struct cmd_struct *abbrev_cmd = NULL, *ambiguous_cmd = NULL;
55
56         for (; cmd->token; cmd++) {
57                 const char *rest;
58
59                 rest = skip_prefix(arg, cmd->token);
60                 if (!rest) {
61                         if (!prefixcmp(cmd->token, arg)) {
62                                 if (abbrev_cmd) {
63                                         /*
64                                          * If this is abbreviated, it is
65                                          * ambiguous. So when there is no
66                                          * exact match later, we need to
67                                          * error out.
68                                          */
69                                         ambiguous_cmd = abbrev_cmd;
70                                 }
71                                 abbrev_cmd = cmd;
72                         }
73                         continue;
74                 }
75                 if (*rest)
76                         continue;
77
78                 *cmd_ret = cmd;
79                 return 0;
80         }
81
82         if (ambiguous_cmd)
83                 return -2;
84
85         if (abbrev_cmd) {
86                 *cmd_ret = abbrev_cmd;
87                 return 0;
88         }
89
90         return -1;
91 }
92
93 static const struct cmd_struct *
94 parse_command_token(const char *arg, const struct cmd_group *grp)
95 {
96         const struct cmd_struct *cmd;
97
98         switch(parse_one_token(arg, grp, &cmd)) {
99         case -1:
100                 help_unknown_token(arg, grp);
101         case -2:
102                 help_ambiguous_token(arg, grp);
103         }
104
105         return cmd;
106 }
107
108 void handle_help_options_next_level(const struct cmd_struct *cmd,
109                                     int argc, char **argv)
110 {
111         if (argc < 2)
112                 return;
113
114         if (!strcmp(argv[1], "--help")) {
115                 if (cmd->next) {
116                         argc--;
117                         argv++;
118                         help_command_group(cmd->next, argc, argv);
119                 } else {
120                         usage_command(cmd, 1, 0);
121                 }
122
123                 exit(0);
124         }
125 }
126
127 static void fixup_argv0(char **argv, const char *token)
128 {
129         int len = strlen(argv0_buf);
130
131         snprintf(argv0_buf + len, sizeof(argv0_buf) - len, " %s", token);
132         argv[0] = argv0_buf;
133 }
134
135 int handle_command_group(const struct cmd_group *grp, int argc,
136                          char **argv)
137
138 {
139         const struct cmd_struct *cmd;
140
141         argc--;
142         argv++;
143         if (argc < 1) {
144                 usage_command_group(grp, 0, 0);
145                 exit(1);
146         }
147
148         cmd = parse_command_token(argv[0], grp);
149
150         handle_help_options_next_level(cmd, argc, argv);
151
152         fixup_argv0(argv, cmd->token);
153         return cmd->fn(argc, argv);
154 }
155
156 int check_argc_exact(int nargs, int expected)
157 {
158         if (nargs < expected)
159                 fprintf(stderr, "%s: too few arguments\n", argv0_buf);
160         if (nargs > expected)
161                 fprintf(stderr, "%s: too many arguments\n", argv0_buf);
162
163         return nargs != expected;
164 }
165
166 int check_argc_min(int nargs, int expected)
167 {
168         if (nargs < expected) {
169                 fprintf(stderr, "%s: too few arguments\n", argv0_buf);
170                 return 1;
171         }
172
173         return 0;
174 }
175
176 int check_argc_max(int nargs, int expected)
177 {
178         if (nargs > expected) {
179                 fprintf(stderr, "%s: too many arguments\n", argv0_buf);
180                 return 1;
181         }
182
183         return 0;
184 }
185
186 const struct cmd_group btrfs_cmd_group;
187
188 static const char * const cmd_help_usage[] = {
189         "btrfs help [--full]",
190         "Dislay help information",
191         "",
192         "--full     display detailed help on every command",
193         NULL
194 };
195
196 static int cmd_help(int argc, char **argv)
197 {
198         help_command_group(&btrfs_cmd_group, argc, argv);
199         return 0;
200 }
201
202 static const char * const cmd_version_usage[] = {
203         "btrfs version",
204         "Display btrfs-progs version",
205         NULL
206 };
207
208 static int cmd_version(int argc, char **argv)
209 {
210         printf("%s\n", BTRFS_BUILD_VERSION);
211         return 0;
212 }
213
214 static int handle_options(int *argc, char ***argv)
215 {
216         char **orig_argv = *argv;
217
218         while (*argc > 0) {
219                 const char *arg = (*argv)[0];
220                 if (arg[0] != '-')
221                         break;
222
223                 if (!strcmp(arg, "--help")) {
224                         break;
225                 } else if (!strcmp(arg, "--version")) {
226                         break;
227                 } else {
228                         fprintf(stderr, "Unknown option: %s\n", arg);
229                         fprintf(stderr, "usage: %s\n",
230                                 btrfs_cmd_group.usagestr[0]);
231                         exit(129);
232                 }
233
234                 (*argv)++;
235                 (*argc)--;
236         }
237
238         return (*argv) - orig_argv;
239 }
240
241 const struct cmd_group btrfs_cmd_group = {
242         btrfs_cmd_group_usage, btrfs_cmd_group_info, {
243                 { "subvolume", cmd_subvolume, NULL, &subvolume_cmd_group, 0 },
244                 { "filesystem", cmd_filesystem, NULL, &filesystem_cmd_group, 0 },
245                 { "balance", cmd_balance, NULL, &balance_cmd_group, 0 },
246                 { "device", cmd_device, NULL, &device_cmd_group, 0 },
247                 { "scrub", cmd_scrub, NULL, &scrub_cmd_group, 0 },
248                 { "inspect-internal", cmd_inspect, NULL, &inspect_cmd_group, 0 },
249                 { "send", cmd_send, NULL, &send_cmd_group, 0 },
250                 { "receive", cmd_receive, NULL, &receive_cmd_group, 0 },
251                 { "help", cmd_help, cmd_help_usage, NULL, 0 },
252                 { "version", cmd_version, cmd_version_usage, NULL, 0 },
253                 { 0, 0, 0, 0, 0 }
254         },
255 };
256
257 int main(int argc, char **argv)
258 {
259         const struct cmd_struct *cmd;
260
261         argc--;
262         argv++;
263         handle_options(&argc, &argv);
264         if (argc > 0) {
265                 if (!prefixcmp(argv[0], "--"))
266                         argv[0] += 2;
267         } else {
268                 usage_command_group(&btrfs_cmd_group, 0, 0);
269                 exit(1);
270         }
271
272         cmd = parse_command_token(argv[0], &btrfs_cmd_group);
273
274         handle_help_options_next_level(cmd, argc, argv);
275
276         fixup_argv0(argv, cmd->token);
277         exit(cmd->fn(argc, argv));
278 }