btrfs-progs: send: rename thread callback to read data from kernel
[platform/upstream/btrfs-progs.git] / cmds-send.c
1 /*
2  * Copyright (C) 2012 Alexander Block.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public
6  * License v2 as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public
14  * License along with this program; if not, write to the
15  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16  * Boston, MA 021110-1307, USA.
17  */
18
19
20 #include "kerncompat.h"
21
22 #include <unistd.h>
23 #include <stdint.h>
24 #include <dirent.h>
25 #include <fcntl.h>
26 #include <pthread.h>
27 #include <math.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/ioctl.h>
31 #include <libgen.h>
32 #include <mntent.h>
33 #include <assert.h>
34 #include <getopt.h>
35 #include <uuid/uuid.h>
36 #include <limits.h>
37
38 #include "ctree.h"
39 #include "ioctl.h"
40 #include "commands.h"
41 #include "list.h"
42 #include "utils.h"
43
44 #include "send.h"
45 #include "send-utils.h"
46
47 #define SEND_BUFFER_SIZE        (64 * 1024)
48
49 /*
50  * Default is 1 for historical reasons, changing may break scripts that expect
51  * the 'At subvol' message.
52  */
53 static int g_verbose = 1;
54
55 struct btrfs_send {
56         int send_fd;
57         int dump_fd;
58         int mnt_fd;
59
60         u64 *clone_sources;
61         u64 clone_sources_count;
62
63         char *root_path;
64         struct subvol_uuid_search sus;
65 };
66
67 static int get_root_id(struct btrfs_send *sctx, const char *path, u64 *root_id)
68 {
69         struct subvol_info *si;
70
71         si = subvol_uuid_search(&sctx->sus, 0, NULL, 0, path,
72                         subvol_search_by_path);
73         if (!si)
74                 return -ENOENT;
75         *root_id = si->root_id;
76         free(si->path);
77         free(si);
78         return 0;
79 }
80
81 static struct subvol_info *get_parent(struct btrfs_send *sctx, u64 root_id)
82 {
83         struct subvol_info *si_tmp;
84         struct subvol_info *si;
85
86         si_tmp = subvol_uuid_search(&sctx->sus, root_id, NULL, 0, NULL,
87                         subvol_search_by_root_id);
88         if (!si_tmp)
89                 return NULL;
90
91         si = subvol_uuid_search(&sctx->sus, 0, si_tmp->parent_uuid, 0, NULL,
92                         subvol_search_by_uuid);
93         free(si_tmp->path);
94         free(si_tmp);
95         return si;
96 }
97
98 static int find_good_parent(struct btrfs_send *sctx, u64 root_id, u64 *found)
99 {
100         int ret;
101         struct subvol_info *parent = NULL;
102         struct subvol_info *parent2 = NULL;
103         struct subvol_info *best_parent = NULL;
104         u64 best_diff = (u64)-1;
105         int i;
106
107         parent = get_parent(sctx, root_id);
108         if (!parent) {
109                 ret = -ENOENT;
110                 goto out;
111         }
112
113         for (i = 0; i < sctx->clone_sources_count; i++) {
114                 if (sctx->clone_sources[i] == parent->root_id) {
115                         best_parent = parent;
116                         parent = NULL;
117                         goto out_found;
118                 }
119         }
120
121         for (i = 0; i < sctx->clone_sources_count; i++) {
122                 s64 tmp;
123
124                 parent2 = get_parent(sctx, sctx->clone_sources[i]);
125                 if (!parent2)
126                         continue;
127                 if (parent2->root_id != parent->root_id) {
128                         free(parent2->path);
129                         free(parent2);
130                         parent2 = NULL;
131                         continue;
132                 }
133
134                 free(parent2->path);
135                 free(parent2);
136                 parent2 = subvol_uuid_search(&sctx->sus,
137                                 sctx->clone_sources[i], NULL, 0, NULL,
138                                 subvol_search_by_root_id);
139
140                 if (!parent2) {
141                         ret = -ENOENT;
142                         goto out;
143                 }
144                 tmp = parent2->ctransid - parent->ctransid;
145                 if (tmp < 0)
146                         tmp = -tmp;
147                 if (tmp < best_diff) {
148                         if (best_parent) {
149                                 free(best_parent->path);
150                                 free(best_parent);
151                         }
152                         best_parent = parent2;
153                         parent2 = NULL;
154                         best_diff = tmp;
155                 } else {
156                         free(parent2->path);
157                         free(parent2);
158                         parent2 = NULL;
159                 }
160         }
161
162         if (!best_parent) {
163                 ret = -ENOENT;
164                 goto out;
165         }
166
167 out_found:
168         *found = best_parent->root_id;
169         ret = 0;
170
171 out:
172         if (parent) {
173                 free(parent->path);
174                 free(parent);
175         }
176         if (best_parent) {
177                 free(best_parent->path);
178                 free(best_parent);
179         }
180         return ret;
181 }
182
183 static int add_clone_source(struct btrfs_send *sctx, u64 root_id)
184 {
185         void *tmp;
186
187         tmp = sctx->clone_sources;
188         sctx->clone_sources = realloc(sctx->clone_sources,
189                 sizeof(*sctx->clone_sources) * (sctx->clone_sources_count + 1));
190
191         if (!sctx->clone_sources) {
192                 free(tmp);
193                 return -ENOMEM;
194         }
195         sctx->clone_sources[sctx->clone_sources_count++] = root_id;
196
197         return 0;
198 }
199
200 #if 0
201 static int write_buf(int fd, const char *buf, size_t size)
202 {
203         int ret;
204         size_t pos = 0;
205
206         while (pos < size) {
207                 ssize_t wbytes;
208
209                 wbytes = write(fd, buf + pos, size - pos);
210                 if (wbytes < 0) {
211                         ret = -errno;
212                         error("failed to dump stream: %s", strerror(-ret));
213                         goto out;
214                 }
215                 if (!wbytes) {
216                         ret = -EIO;
217                         error("failed to dump stream: %s", strerror(-ret));
218                         goto out;
219                 }
220                 pos += wbytes;
221         }
222         ret = 0;
223
224 out:
225         return ret;
226 }
227
228 static void* read_sent_data_copy(void *arg)
229 {
230         int ret;
231         struct btrfs_send *sctx = (struct btrfs_send*)arg;
232         char buf[SEND_BUFFER_SIZE];
233
234         while (1) {
235                 ssize_t rbytes;
236
237                 rbytes = read(sctx->send_fd, buf, sizeof(buf));
238                 if (rbytes < 0) {
239                         ret = -errno;
240                         error("failed to read stream from kernel: %s",
241                                 strerror(-ret));
242                         goto out;
243                 }
244                 if (!rbytes) {
245                         ret = 0;
246                         goto out;
247                 }
248                 ret = write_buf(sctx->dump_fd, buf, rbytes);
249                 if (ret < 0)
250                         goto out;
251         }
252
253 out:
254         if (ret < 0)
255                 exit(-ret);
256
257         return ERR_PTR(ret);
258 }
259 #endif
260
261 static void *read_sent_data(void *arg)
262 {
263         int ret;
264         struct btrfs_send *sctx = (struct btrfs_send*)arg;
265
266         while (1) {
267                 ssize_t sbytes;
268
269                 /* Source is a pipe, output is either file or stdout */
270                 sbytes = splice(sctx->send_fd, NULL, sctx->dump_fd,
271                                 NULL, SEND_BUFFER_SIZE, SPLICE_F_MORE);
272                 if (sbytes < 0) {
273                         ret = -errno;
274                         error("failed to read stream from kernel: %s",
275                                 strerror(-ret));
276                         goto out;
277                 }
278                 if (!sbytes) {
279                         ret = 0;
280                         goto out;
281                 }
282         }
283
284 out:
285         if (ret < 0)
286                 exit(-ret);
287
288         return ERR_PTR(ret);
289 }
290
291 static int do_send(struct btrfs_send *send, u64 parent_root_id,
292                    int is_first_subvol, int is_last_subvol, const char *subvol,
293                    u64 flags)
294 {
295         int ret;
296         pthread_t t_read;
297         struct btrfs_ioctl_send_args io_send;
298         void *t_err = NULL;
299         int subvol_fd = -1;
300         int pipefd[2] = {-1, -1};
301
302         subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
303         if (subvol_fd < 0) {
304                 ret = -errno;
305                 error("cannot open %s: %s", subvol, strerror(-ret));
306                 goto out;
307         }
308
309         ret = pipe(pipefd);
310         if (ret < 0) {
311                 ret = -errno;
312                 error("pipe failed: %s", strerror(-ret));
313                 goto out;
314         }
315
316         memset(&io_send, 0, sizeof(io_send));
317         io_send.send_fd = pipefd[1];
318         send->send_fd = pipefd[0];
319
320         if (!ret)
321                 ret = pthread_create(&t_read, NULL, read_sent_data, send);
322         if (ret) {
323                 ret = -ret;
324                 error("thread setup failed: %s", strerror(-ret));
325                 goto out;
326         }
327
328         io_send.flags = flags;
329         io_send.clone_sources = (__u64*)send->clone_sources;
330         io_send.clone_sources_count = send->clone_sources_count;
331         io_send.parent_root = parent_root_id;
332         if (!is_first_subvol)
333                 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
334         if (!is_last_subvol)
335                 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
336         ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
337         if (ret < 0) {
338                 ret = -errno;
339                 error("send ioctl failed with %d: %s", ret, strerror(-ret));
340                 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
341                         fprintf(stderr,
342                                 "Try upgrading your kernel or don't use -e.\n");
343                 goto out;
344         }
345         if (g_verbose > 1)
346                 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
347
348         if (g_verbose > 1)
349                 fprintf(stderr, "joining genl thread\n");
350
351         close(pipefd[1]);
352         pipefd[1] = -1;
353
354         ret = pthread_join(t_read, &t_err);
355         if (ret) {
356                 ret = -ret;
357                 error("pthread_join failed: %s", strerror(-ret));
358                 goto out;
359         }
360         if (t_err) {
361                 ret = (long int)t_err;
362                 error("failed to process send stream, ret=%ld (%s)",
363                                 (long int)t_err, strerror(-ret));
364                 goto out;
365         }
366
367         ret = 0;
368
369 out:
370         if (subvol_fd != -1)
371                 close(subvol_fd);
372         if (pipefd[0] != -1)
373                 close(pipefd[0]);
374         if (pipefd[1] != -1)
375                 close(pipefd[1]);
376         return ret;
377 }
378
379 static int init_root_path(struct btrfs_send *sctx, const char *subvol)
380 {
381         int ret = 0;
382
383         if (sctx->root_path)
384                 goto out;
385
386         ret = find_mount_root(subvol, &sctx->root_path);
387         if (ret < 0) {
388                 error("failed to determine mount point for %s: %s",
389                         subvol, strerror(-ret));
390                 ret = -EINVAL;
391                 goto out;
392         }
393         if (ret > 0) {
394                 error("%s doesn't belong to btrfs mount point", subvol);
395                 ret = -EINVAL;
396                 goto out;
397         }
398
399         sctx->mnt_fd = open(sctx->root_path, O_RDONLY | O_NOATIME);
400         if (sctx->mnt_fd < 0) {
401                 ret = -errno;
402                 error("cannot open '%s': %s", sctx->root_path, strerror(-ret));
403                 goto out;
404         }
405
406         ret = subvol_uuid_search_init(sctx->mnt_fd, &sctx->sus);
407         if (ret < 0) {
408                 error("failed to initialize subvol search: %s",
409                         strerror(-ret));
410                 goto out;
411         }
412
413 out:
414         return ret;
415
416 }
417
418 static int is_subvol_ro(struct btrfs_send *sctx, const char *subvol)
419 {
420         int ret;
421         u64 flags;
422         int fd = -1;
423
424         fd = openat(sctx->mnt_fd, subvol, O_RDONLY | O_NOATIME);
425         if (fd < 0) {
426                 ret = -errno;
427                 error("cannot open %s: %s", subvol, strerror(-ret));
428                 goto out;
429         }
430
431         ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
432         if (ret < 0) {
433                 ret = -errno;
434                 error("failed to get flags for subvolume %s: %s",
435                         subvol, strerror(-ret));
436                 goto out;
437         }
438
439         if (flags & BTRFS_SUBVOL_RDONLY)
440                 ret = 1;
441         else
442                 ret = 0;
443
444 out:
445         if (fd != -1)
446                 close(fd);
447
448         return ret;
449 }
450
451 static int set_root_info(struct btrfs_send *sctx, const char *subvol,
452                 u64 *root_id)
453 {
454         int ret;
455
456         ret = init_root_path(sctx, subvol);
457         if (ret < 0)
458                 goto out;
459
460         ret = get_root_id(sctx, subvol_strip_mountpoint(sctx->root_path, subvol),
461                 root_id);
462         if (ret < 0) {
463                 error("cannot resolve rootid for %s", subvol);
464                 goto out;
465         }
466
467 out:
468         return ret;
469 }
470
471 static void free_send_info(struct btrfs_send *sctx)
472 {
473         if (sctx->mnt_fd >= 0) {
474                 close(sctx->mnt_fd);
475                 sctx->mnt_fd = -1;
476         }
477         free(sctx->root_path);
478         sctx->root_path = NULL;
479         subvol_uuid_search_finit(&sctx->sus);
480 }
481
482 int cmd_send(int argc, char **argv)
483 {
484         char *subvol = NULL;
485         int ret;
486         char outname[PATH_MAX];
487         struct btrfs_send send;
488         u32 i;
489         char *mount_root = NULL;
490         char *snapshot_parent = NULL;
491         u64 root_id = 0;
492         u64 parent_root_id = 0;
493         int full_send = 1;
494         int new_end_cmd_semantic = 0;
495         u64 send_flags = 0;
496
497         memset(&send, 0, sizeof(send));
498         send.dump_fd = fileno(stdout);
499         outname[0] = 0;
500
501         while (1) {
502                 enum { GETOPT_VAL_SEND_NO_DATA = 256 };
503                 static const struct option long_options[] = {
504                         { "verbose", no_argument, NULL, 'v' },
505                         { "quiet", no_argument, NULL, 'q' },
506                         { "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA }
507                 };
508                 int c = getopt_long(argc, argv, "vqec:f:i:p:", long_options, NULL);
509
510                 if (c < 0)
511                         break;
512
513                 switch (c) {
514                 case 'v':
515                         g_verbose++;
516                         break;
517                 case 'q':
518                         g_verbose = 0;
519                         break;
520                 case 'e':
521                         new_end_cmd_semantic = 1;
522                         break;
523                 case 'c':
524                         subvol = realpath(optarg, NULL);
525                         if (!subvol) {
526                                 ret = -errno;
527                                 error("realpath %s failed: %s\n", optarg, strerror(-ret));
528                                 goto out;
529                         }
530
531                         ret = set_root_info(&send, subvol, &root_id);
532                         if (ret < 0)
533                                 goto out;
534
535                         ret = is_subvol_ro(&send, subvol);
536                         if (ret < 0)
537                                 goto out;
538                         if (!ret) {
539                                 ret = -EINVAL;
540                                 error("cloned subvolume %s is not read-only", subvol);
541                                 goto out;
542                         }
543
544                         ret = add_clone_source(&send, root_id);
545                         if (ret < 0) {
546                                 error("cannot add clone source: %s", strerror(-ret));
547                                 goto out;
548                         }
549                         free(subvol);
550                         subvol = NULL;
551                         free_send_info(&send);
552                         full_send = 0;
553                         break;
554                 case 'f':
555                         if (arg_copy_path(outname, optarg, sizeof(outname))) {
556                                 error("output file path too long (%zu)", strlen(optarg));
557                                 ret = 1;
558                                 goto out;
559                         }
560                         break;
561                 case 'p':
562                         if (snapshot_parent) {
563                                 error("you cannot have more than one parent (-p)");
564                                 ret = 1;
565                                 goto out;
566                         }
567                         snapshot_parent = realpath(optarg, NULL);
568                         if (!snapshot_parent) {
569                                 ret = -errno;
570                                 error("realpath %s failed: %s", optarg, strerror(-ret));
571                                 goto out;
572                         }
573
574                         ret = is_subvol_ro(&send, snapshot_parent);
575                         if (ret < 0)
576                                 goto out;
577                         if (!ret) {
578                                 ret = -EINVAL;
579                                 error("parent subvolume %s is not read-only",
580                                         snapshot_parent);
581                                 goto out;
582                         }
583
584                         full_send = 0;
585                         break;
586                 case 'i':
587                         error("option -i was removed, use -c instead");
588                         ret = 1;
589                         goto out;
590                 case GETOPT_VAL_SEND_NO_DATA:
591                         send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA;
592                         break;
593                 case '?':
594                 default:
595                         error("send arguments invalid");
596                         ret = 1;
597                         goto out;
598                 }
599         }
600
601         if (check_argc_min(argc - optind, 1))
602                 usage(cmd_send_usage);
603
604         if (outname[0]) {
605                 int tmpfd;
606
607                 /*
608                  * Try to use an existing file first. Even if send runs as
609                  * root, it might not have permissions to create file (eg. on a
610                  * NFS) but it should still be able to use a pre-created file.
611                  */
612                 tmpfd = open(outname, O_WRONLY | O_TRUNC);
613                 if (tmpfd < 0) {
614                         if (errno == ENOENT)
615                                 tmpfd = open(outname,
616                                         O_CREAT | O_WRONLY | O_TRUNC, 0600);
617                 }
618                 send.dump_fd = tmpfd;
619                 if (send.dump_fd == -1) {
620                         ret = -errno;
621                         error("cannot create '%s': %s", outname, strerror(-ret));
622                         goto out;
623                 }
624         }
625
626         if (isatty(send.dump_fd)) {
627                 error(
628             "not dumping send stream into a terminal, redirect it into a file");
629                 ret = 1;
630                 goto out;
631         }
632
633         /* use first send subvol to determine mount_root */
634         subvol = realpath(argv[optind], NULL);
635         if (!subvol) {
636                 ret = -errno;
637                 error("unable to resolve %s", argv[optind]);
638                 goto out;
639         }
640
641         ret = init_root_path(&send, subvol);
642         if (ret < 0)
643                 goto out;
644
645         if (snapshot_parent != NULL) {
646                 ret = get_root_id(&send,
647                         subvol_strip_mountpoint(send.root_path, snapshot_parent),
648                         &parent_root_id);
649                 if (ret < 0) {
650                         error("could not resolve rootid for %s", snapshot_parent);
651                         goto out;
652                 }
653
654                 ret = add_clone_source(&send, parent_root_id);
655                 if (ret < 0) {
656                         error("cannot add clone source: %s", strerror(-ret));
657                         goto out;
658                 }
659         }
660
661         for (i = optind; i < argc; i++) {
662                 free(subvol);
663                 subvol = realpath(argv[i], NULL);
664                 if (!subvol) {
665                         ret = -errno;
666                         error("unable to resolve %s", argv[i]);
667                         goto out;
668                 }
669
670                 ret = find_mount_root(subvol, &mount_root);
671                 if (ret < 0) {
672                         error("find_mount_root failed on %s: %s", subvol,
673                                 strerror(-ret));
674                         goto out;
675                 }
676                 if (ret > 0) {
677                         error("%s does not belong to btrfs mount point",
678                                 subvol);
679                         ret = -EINVAL;
680                         goto out;
681                 }
682                 if (strcmp(send.root_path, mount_root) != 0) {
683                         ret = -EINVAL;
684                         error("all subvolumes must be from the same filesystem");
685                         goto out;
686                 }
687                 free(mount_root);
688
689                 ret = is_subvol_ro(&send, subvol);
690                 if (ret < 0)
691                         goto out;
692                 if (!ret) {
693                         ret = -EINVAL;
694                         error("subvolume %s is not read-only", subvol);
695                         goto out;
696                 }
697         }
698
699         if ((send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA) && g_verbose > 1)
700                 if (g_verbose > 1)
701                         fprintf(stderr, "Mode NO_FILE_DATA enabled\n");
702
703         for (i = optind; i < argc; i++) {
704                 int is_first_subvol;
705                 int is_last_subvol;
706
707                 free(subvol);
708                 subvol = argv[i];
709
710                 if (g_verbose > 0)
711                         fprintf(stderr, "At subvol %s\n", subvol);
712
713                 subvol = realpath(subvol, NULL);
714                 if (!subvol) {
715                         ret = -errno;
716                         error("realpath %s failed: %s", argv[i], strerror(-ret));
717                         goto out;
718                 }
719
720                 if (!full_send && !snapshot_parent) {
721                         ret = set_root_info(&send, subvol, &root_id);
722                         if (ret < 0)
723                                 goto out;
724
725                         ret = find_good_parent(&send, root_id, &parent_root_id);
726                         if (ret < 0) {
727                                 error("parent determination failed for %lld",
728                                         root_id);
729                                 goto out;
730                         }
731                 }
732
733                 if (new_end_cmd_semantic) {
734                         /* require new kernel */
735                         is_first_subvol = (i == optind);
736                         is_last_subvol = (i == argc - 1);
737                 } else {
738                         /* be compatible to old and new kernel */
739                         is_first_subvol = 1;
740                         is_last_subvol = 1;
741                 }
742                 ret = do_send(&send, parent_root_id, is_first_subvol,
743                               is_last_subvol, subvol, send_flags);
744                 if (ret < 0)
745                         goto out;
746
747                 if (!full_send && !snapshot_parent) {
748                         /* done with this subvol, so add it to the clone sources */
749                         ret = add_clone_source(&send, root_id);
750                         if (ret < 0) {
751                                 error("cannot add clone source: %s", strerror(-ret));
752                                 goto out;
753                         }
754                         free_send_info(&send);
755                 }
756         }
757
758         ret = 0;
759
760 out:
761         free(subvol);
762         free(snapshot_parent);
763         free(send.clone_sources);
764         free_send_info(&send);
765         return !!ret;
766 }
767
768 const char * const cmd_send_usage[] = {
769         "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
770         "Send the subvolume(s) to stdout.",
771         "Sends the subvolume(s) specified by <subvol> to stdout.",
772         "<subvol> should be read-only here.",
773         "By default, this will send the whole subvolume. To do an incremental",
774         "send, use '-p <parent>'. If you want to allow btrfs to clone from",
775         "any additional local snapshots, use '-c <clone-src>' (multiple times",
776         "where applicable). You must not specify clone sources unless you",
777         "guarantee that these snapshots are exactly in the same state on both",
778         "sides, the sender and the receiver. It is allowed to omit the",
779         "'-p <parent>' option when '-c <clone-src>' options are given, in",
780         "which case 'btrfs send' will determine a suitable parent among the",
781         "clone sources itself.",
782         "\n",
783         "-e               If sending multiple subvols at once, use the new",
784         "                 format and omit the end-cmd between the subvols.",
785         "-p <parent>      Send an incremental stream from <parent> to",
786         "                 <subvol>.",
787         "-c <clone-src>   Use this snapshot as a clone source for an ",
788         "                 incremental send (multiple allowed)",
789         "-f <outfile>     Output is normally written to stdout. To write to",
790         "                 a file, use this option. An alternative would be to",
791         "                 use pipes.",
792         "--no-data        send in NO_FILE_DATA mode, Note: the output stream",
793         "                 does not contain any file data and thus cannot be used",
794         "                 to transfer changes. This mode is faster and useful to",
795         "                 show the differences in metadata.",
796         "-v|--verbose     enable verbose output to stderr, each occurrence of",
797         "                 this option increases verbosity",
798         "-q|--quiet       suppress all messages, except errors",
799         NULL
800 };