aboutsummaryrefslogtreecommitdiff
path: root/tcpdump.c
diff options
context:
space:
mode:
Diffstat (limited to 'tcpdump.c')
-rw-r--r--tcpdump.c358
1 files changed, 311 insertions, 47 deletions
diff --git a/tcpdump.c b/tcpdump.c
index a4cdbe076d42..bba632c4829f 100644
--- a/tcpdump.c
+++ b/tcpdump.c
@@ -34,6 +34,9 @@
*/
#include <config.h>
+#ifndef TCPDUMP_CONFIG_H_
+#error "The included config.h header is not from the tcpdump build."
+#endif
/*
* Some older versions of Mac OS X ship pcap.h from libpcap 0.6 with a
@@ -149,7 +152,6 @@ The Regents of the University of California. All rights reserved.\n";
#include <sys/sysctl.h>
#endif /* __FreeBSD__ */
-#include "netdissect-stdinc.h"
#include "netdissect.h"
#include "interface.h"
#include "addrtoname.h"
@@ -226,7 +228,9 @@ static int Uflag; /* "unbuffered" output of dump files */
#endif
static int Wflag; /* recycle output files after this number of files */
static int WflagChars;
+#if defined(HAVE_FORK) || defined(HAVE_VFORK)
static char *zflag = NULL; /* compress each savefile using a specified command (like gzip or bzip2) */
+#endif
static int timeout = 1000; /* default timeout = 1000 ms = 1 s */
#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
static int immediate_mode;
@@ -244,7 +248,9 @@ static void warning(FORMAT_STRING(const char *), ...) PRINTFLIKE(1, 2);
static NORETURN void exit_tcpdump(int);
static void (*setsignal (int sig, void (*func)(int)))(int);
static void cleanup(int);
+#if defined(HAVE_FORK) || defined(HAVE_VFORK)
static void child_cleanup(int);
+#endif
static void print_version(FILE *);
static void print_usage(FILE *);
#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
@@ -591,14 +597,11 @@ show_remote_devices_and_exit(void)
/*
* Short options.
*
- * Note that there we use all letters for short options except for g, k,
+ * Note that there we use all letters for short options except for k,
* o, and P, and those are used by other versions of tcpdump, and we should
* only use them for the same purposes that the other versions of tcpdump
* use them:
*
- * macOS tcpdump uses -g to force non--v output for IP to be on one
- * line, making it more "g"repable;
- *
* macOS tcpdump uses -k to specify that packet comments in pcapng files
* should be printed;
*
@@ -670,7 +673,27 @@ show_remote_devices_and_exit(void)
#define U_FLAG
#endif
-#define SHORTOPTS "aAb" B_FLAG "c:C:d" D_FLAG "eE:fF:G:hHi:" I_FLAG j_FLAG J_FLAG "KlLm:M:nNOpq" Q_FLAG "r:s:StT:u" U_FLAG "vV:w:W:xXy:Yz:Z:#"
+#if defined(HAVE_FORK) || defined(HAVE_VFORK)
+#define z_FLAG "z:"
+#define z_FLAG_USAGE "[ -z postrotate-command ] "
+#else
+#define z_FLAG
+#define z_FLAG_USAGE
+#endif
+
+#ifdef HAVE_LIBCRYPTO
+#define E_FLAG "E:"
+#define E_FLAG_USAGE "[ -E algo:secret ] "
+#define M_FLAG "M:"
+#define M_FLAG_USAGE "[ -M secret ] "
+#else
+#define E_FLAG
+#define E_FLAG_USAGE
+#define M_FLAG
+#define M_FLAG_USAGE
+#endif
+
+#define SHORTOPTS "aAb" B_FLAG "c:C:d" D_FLAG "e" E_FLAG "fF:gG:hHi:" I_FLAG j_FLAG J_FLAG "KlLm:" M_FLAG "nNOpq" Q_FLAG "r:s:StT:u" U_FLAG "vV:w:W:xXy:Y" z_FLAG "Z:#"
/*
* Long options.
@@ -701,6 +724,7 @@ show_remote_devices_and_exit(void)
#define OPTION_TSTAMP_NANO 134
#define OPTION_FP_TYPE 135
#define OPTION_COUNT 136
+#define OPTION_TIME_T_SIZE 139
static const struct option longopts[] = {
#if defined(HAVE_PCAP_CREATE) || defined(_WIN32)
@@ -748,6 +772,8 @@ static const struct option longopts[] = {
{ "fp-type", no_argument, NULL, OPTION_FP_TYPE },
{ "number", no_argument, NULL, '#' },
{ "print", no_argument, NULL, OPTION_PRINT },
+ { "time-t-size", no_argument, NULL, OPTION_TIME_T_SIZE },
+ { "ip-oneline", no_argument, NULL, 'g' },
{ "version", no_argument, NULL, OPTION_VERSION },
{ NULL, 0, NULL, 0 }
};
@@ -785,7 +811,7 @@ droproot(const char *username, const char *chroot_dir)
{
int ret = capng_change_id(pw->pw_uid, pw->pw_gid, CAPNG_NO_FLAG);
if (ret < 0)
- error("capng_change_id(): return %d\n", ret);
+ error("capng_change_id(): return %d", ret);
else
fprintf(stderr, "dropped privs to %s\n", username);
}
@@ -991,7 +1017,7 @@ tstamp_precision_to_string(int precision)
* that requires that it be able to do an F_GETFL fcntl() to read
* the O_ flags.
*
- * Tcpdump uses ftell() to determine how much data has been written
+ * tcpdump uses ftell() to determine how much data has been written
* to a file in order to, when used with -C, determine when it's time
* to rotate capture files. ftell() therefore needs to do an lseek()
* to find out the file offset and must, thanks to the aforementioned
@@ -1231,6 +1257,8 @@ _U_
status = pcap_findalldevs(&devlist, ebuf);
if (status < 0)
error("%s", ebuf);
+ if (devlist == NULL)
+ error("no interfaces available for capture");
/*
* Look for the devnum-th entry in the list of devices (1-based).
*/
@@ -1239,8 +1267,8 @@ _U_
;
if (dev == NULL) {
pcap_freealldevs(devlist);
- error("Invalid adapter index %ld: only %ld interfaces found",
- devnum, i);
+ error("Invalid adapter index %ld: only %ld interface%s found",
+ devnum, i, (i == 1) ? "" : "s");
}
device = strdup(dev->name);
pcap_freealldevs(devlist);
@@ -1387,7 +1415,7 @@ open_interface(const char *device, netdissect_options *ndo, char *ebuf)
*/
cp = pcap_geterr(pc);
if (status == PCAP_ERROR)
- error("%s", cp);
+ error("%s: %s", device, cp);
else if (status == PCAP_ERROR_NO_SUCH_DEVICE) {
/*
* Return an error for our caller to handle.
@@ -1397,6 +1425,11 @@ open_interface(const char *device, netdissect_options *ndo, char *ebuf)
} else if (status == PCAP_ERROR_PERM_DENIED && *cp != '\0')
error("%s: %s\n(%s)", device,
pcap_statustostr(status), cp);
+#ifdef PCAP_ERROR_CAPTURE_NOTSUP
+ else if (status == PCAP_ERROR_CAPTURE_NOTSUP && *cp != '\0')
+ error("%s: %s\n(%s)", device,
+ pcap_statustostr(status), cp);
+#endif
#ifdef __FreeBSD__
else if (status == PCAP_ERROR_RFMON_NOTSUP &&
strncmp(device, "wlan", 4) == 0) {
@@ -1414,7 +1447,7 @@ open_interface(const char *device, netdissect_options *ndo, char *ebuf)
* specific case would be an error message that looks a bit odd.
*/
newdev[strlen(newdev)-1]++;
- error("%s is not a monitor mode VAP\n"
+ error("%s is not a monitor mode VAP"
"To create a new monitor mode VAP use:\n"
" ifconfig %s create wlandev %s wlanmode monitor\n"
"and use %s as the tcpdump interface",
@@ -1515,10 +1548,162 @@ main(int argc, char **argv)
int yflag_dlt = -1;
const char *yflag_dlt_name = NULL;
int print = 0;
+ long Cflagmult;
netdissect_options Ndo;
netdissect_options *ndo = &Ndo;
+#ifdef _WIN32
+ /*
+ * We need to look for wpcap.dll in \Windows\System32\Npcap first,
+ * as either:
+ *
+ * 1) WinPcap isn't installed and Npcap isn't installed in "WinPcap
+ * API-compatible Mode", so there's no wpcap.dll in
+ * \Windows\System32, only in \Windows\System32\Npcap;
+ *
+ * 2) WinPcap is installed and Npcap isn't installed in "WinPcap
+ * API-compatible Mode", so the wpcap.dll in \Windows\System32
+ * is a WinPcap DLL, but we'd prefer an Npcap DLL (we should
+ * work with either one if we're configured against WinPcap,
+ * and we'll probably require Npcap if we're configured against
+ * it), and that's in \Windows\System32\Npcap;
+ *
+ * 3) Npcap is installed in "WinPcap API-compatible Mode", so both
+ * \Windows\System32 and \Windows\System32\Npcap have an Npcap
+ * wpcap.dll.
+ *
+ * Unfortunately, Windows has no notion of an rpath, so we can't
+ * set the rpath to include \Windows\System32\Npcap at link time;
+ * what we need to do is to link wpcap as a delay-load DLL and
+ * add \Windows\System32\Npcap to the DLL search path early in
+ * main() with a call to SetDllDirectory().
+ *
+ * The same applies to packet.dll.
+ *
+ * We add \Windows\System32\Npcap here.
+ *
+ * See https://npcap.com/guide/npcap-devguide.html#npcap-feature-native-dll-implicitly
+ */
+ WCHAR *dll_directory = NULL;
+ size_t dll_directory_buf_len = 0; /* units of bytes */
+ UINT system_directory_buf_len = 0; /* units of WCHARs */
+ UINT system_directory_len; /* units of WCHARs */
+ static const WCHAR npcap[] = L"\\Npcap";
+
+ /*
+ * Get the system directory path, in UTF-16, into a buffer that's
+ * large enough for that directory path plus "\Npcap".
+ *
+ * String manipulation in C, plus fetching a variable-length
+ * string into a buffer whose size is fixed at the time of
+ * the call, with an oddball return value (see below), is just
+ * a huge bag of fun.
+ *
+ * And it's even more fun when dealing with UTF-16, so that the
+ * buffer sizes used in GetSystemDirectoryW() are in different
+ * units from the buffer sizes used in realloc()! We maintain
+ * all sizes/length in units of bytes, not WCHARs, so that our
+ * heads don't explode.
+ */
+ for (;;) {
+ /*
+ * Try to fetch the system directory.
+ *
+ * GetSystemDirectoryW() expects a buffer size in units
+ * of WCHARs, not bytes, and returns a directory path
+ * length in units of WCHARs, not bytes.
+ *
+ * For extra fun, if GetSystemDirectoryW() succeeds,
+ * the return value is the length of the directory
+ * path in units of WCHARs, *not* including the
+ * terminating '\0', but if it fails because the
+ * path string wouldn't fit, the return value is
+ * the length of the directory path in units of WCHARs,
+ * *including* the terminating '\0'.
+ */
+ system_directory_len = GetSystemDirectoryW(dll_directory,
+ system_directory_buf_len);
+ if (system_directory_len == 0)
+ error("GetSystemDirectoryW() failed");
+
+ /*
+ * Did the directory path fit in the buffer?
+ *
+ * As per the above, this means that the return value
+ * *plus 1*, so that the terminating '\0' is counted,
+ * is <= the buffer size.
+ *
+ * (If the directory path, complete with the terminating
+ * '\0', fits *exactly*, the return value would be the
+ * size of the buffer minus 1, as it doesn't count the
+ * terminating '\0', so the test below would succeed.
+ *
+ * If everything *but* the terminating '\0' fits,
+ * the return value would be the size of the buffer + 1,
+ * i.e., the size that the string in question would
+ * have required.
+ *
+ * The astute reader will note that returning the
+ * size of the buffer is not one of the two cases
+ * above, and should never happen.)
+ */
+ if ((system_directory_len + 1) <= system_directory_buf_len) {
+ /*
+ * No. We have a buffer that's large enough
+ * for our purposes.
+ */
+ break;
+ }
+
+ /*
+ * Yes. Grow the buffer.
+ *
+ * The space we'll need in the buffer for the system
+ * directory, in units of WCHARs, is system_directory_len,
+ * as that's the length of the system directory path
+ * including the terminating '\0'.
+ */
+ system_directory_buf_len = system_directory_len;
+
+ /*
+ * The size of the DLL directory buffer, in *bytes*, must
+ * be the number of WCHARs taken by the system directory,
+ * *minus* the terminating '\0' (as we'll overwrite that
+ * with the "\" of the "\Npcap" string), multiplied by
+ * sizeof(WCHAR) to convert it to the number of bytes,
+ * plus the size of the "\Npcap" string, in bytes (which
+ * will include the terminating '\0', as that will become
+ * the DLL path's terminating '\0').
+ */
+ dll_directory_buf_len =
+ ((system_directory_len - 1)*sizeof(WCHAR)) + sizeof npcap;
+ dll_directory = realloc(dll_directory, dll_directory_buf_len);
+ if (dll_directory == NULL)
+ error("Can't allocate string for Npcap directory");
+ }
+
+ /*
+ * OK, that worked.
+ *
+ * Now append \Npcap. We add the length of the system directory path,
+ * in WCHARs, *not* including the terminating '\0' (which, since
+ * GetSystemDirectoryW() succeeded, is the return value of
+ * GetSystemDirectoryW(), as per the above), to the pointer to the
+ * beginning of the path, to go past the end of the system directory
+ * to point to the terminating '\0'.
+ */
+ memcpy(dll_directory + system_directory_len, npcap, sizeof npcap);
+
+ /*
+ * Now add that as a system DLL directory.
+ */
+ if (!SetDllDirectoryW(dll_directory))
+ error("SetDllDirectory failed");
+
+ free(dll_directory);
+#endif
+
/*
* Initialize the netdissect code.
*/
@@ -1603,19 +1788,77 @@ main(int argc, char **argv)
#else
Cflag = strtol(optarg, &endp, 10);
#endif
- if (endp == optarg || *endp != '\0' || errno != 0
- || Cflag <= 0)
+ if (endp == optarg || errno != 0 || Cflag <= 0)
error("invalid file size %s", optarg);
+
+ if (*endp == '\0') {
+ /*
+ * There's nothing after the file size,
+ * so the size is in units of 1 MB
+ * (1,000,000 bytes).
+ */
+ Cflagmult = 1000000;
+ } else {
+ /*
+ * There's something after the file
+ * size.
+ *
+ * If it's a single letter, then:
+ *
+ * if the letter is k or K, the size
+ * is in units of 1 KiB (1024 bytes);
+ *
+ * if the letter is m or M, the size
+ * is in units of 1 MiB (1,048,576 bytes);
+ *
+ * if the letter is g or G, the size
+ * is in units of 1 GiB (1,073,741,824 bytes).
+ *
+ * Otherwise, it's an error.
+ */
+ switch (*endp) {
+
+ case 'k':
+ case 'K':
+ Cflagmult = 1024;
+ break;
+
+ case 'm':
+ case 'M':
+ Cflagmult = 1024*1024;
+ break;
+
+ case 'g':
+ case 'G':
+ Cflagmult = 1024*1024*1024;
+ break;
+
+ default:
+ error("invalid file size %s", optarg);
+ }
+
+ /*
+ * OK, there was a letter that we treat
+ * as a units indication; was there
+ * anything after it?
+ */
+ endp++;
+ if (*endp != '\0') {
+ /* Yes - error */
+ error("invalid file size %s", optarg);
+ }
+ }
+
/*
- * Will multiplying it by 1000000 overflow?
+ * Will multiplying it by multiplier overflow?
*/
#ifdef HAVE_PCAP_DUMP_FTELL64
- if (Cflag > INT64_T_CONSTANT(0x7fffffffffffffff) / 1000000)
+ if (Cflag > INT64_MAX / Cflagmult)
#else
- if (Cflag > LONG_MAX / 1000000)
+ if (Cflag > LONG_MAX / Cflagmult)
#endif
error("file size %s is too large", optarg);
- Cflag *= 1000000;
+ Cflag *= Cflagmult;
break;
case 'd':
@@ -1642,12 +1885,11 @@ main(int argc, char **argv)
++ndo->ndo_eflag;
break;
+#ifdef HAVE_LIBCRYPTO
case 'E':
-#ifndef HAVE_LIBCRYPTO
- warning("crypto code not compiled in");
-#endif
ndo->ndo_espsecret = optarg;
break;
+#endif
case 'f':
++ndo->ndo_fflag;
@@ -1657,6 +1899,10 @@ main(int argc, char **argv)
infile = optarg;
break;
+ case 'g':
+ ++ndo->ndo_gflag;
+ break;
+
case 'G':
Gflag = atoi(optarg);
if (Gflag < 0)
@@ -1740,13 +1986,12 @@ main(int argc, char **argv)
}
break;
+#ifdef HAVE_LIBCRYPTO
case 'M':
/* TCP-MD5 shared secret */
-#ifndef HAVE_LIBCRYPTO
- warning("crypto code not compiled in");
-#endif
ndo->ndo_sigsecret = optarg;
break;
+#endif
case 'n':
++ndo->ndo_nflag;
@@ -1902,9 +2147,12 @@ main(int argc, char **argv)
}
break;
#endif
+
+#if defined(HAVE_FORK) || defined(HAVE_VFORK)
case 'z':
zflag = optarg;
break;
+#endif
case 'Z':
username = optarg;
@@ -1914,6 +2162,10 @@ main(int argc, char **argv)
ndo->ndo_packet_number = 1;
break;
+ case OPTION_TIME_T_SIZE:
+ printf("%zu\n", sizeof(time_t) * 8);
+ return 0;
+
case OPTION_VERSION:
print_version(stdout);
exit_tcpdump(S_SUCCESS);
@@ -1968,6 +2220,21 @@ main(int argc, char **argv)
/* NOTREACHED */
}
+ if (ndo->ndo_Aflag && ndo->ndo_xflag)
+ error("-A and -x[x] are mutually exclusive.");
+ if (ndo->ndo_Aflag && ndo->ndo_Xflag)
+ error("-A and -X[X] are mutually exclusive.");
+ if (ndo->ndo_xflag && ndo->ndo_Xflag)
+ error("-x[x] and -X[X] are mutually exclusive.");
+ if (Cflag != 0 && WFileName == NULL)
+ error("-C cannot be used without -w.");
+ if (Gflag != 0 && WFileName == NULL)
+ error("-G cannot be used without -w.");
+#if defined(HAVE_FORK) || defined(HAVE_VFORK)
+ if (zflag != NULL && (WFileName == NULL || (Cflag == 0 && Gflag == 0)))
+ error("-z cannot be used without -w and (-C or -G).");
+#endif
+
#ifdef HAVE_PCAP_FINDALLDEVS
if (Dflag)
show_devices_and_exit();
@@ -1993,7 +2260,7 @@ main(int argc, char **argv)
}
if (ndo->ndo_fflag != 0 && (VFileName != NULL || RFileName != NULL))
- error("-f can not be used with -V or -r");
+ error("-f cannot be used with -V or -r.");
if (VFileName != NULL && RFileName != NULL)
error("-V and -r are mutually exclusive.");
@@ -2061,11 +2328,11 @@ main(int argc, char **argv)
VFile = fopen(VFileName, "r");
if (VFile == NULL)
- error("Unable to open file: %s\n", pcap_strerror(errno));
+ error("Unable to open file: %s", pcap_strerror(errno));
ret = get_next_file(VFile, VFileLine);
if (!ret)
- error("Nothing in %s\n", VFileName);
+ error("Nothing in %s", VFileName);
RFileName = VFileLine;
}
@@ -2327,7 +2594,7 @@ DIAG_ON_WARN_UNUSED_RESULT
* devices, and can't just give users that permission,
* you'd make tcpdump set-UID or set-GID).
*
- * Tcpdump doesn't necessarily write only to one savefile;
+ * tcpdump doesn't necessarily write only to one savefile;
* the general only way to allow a -Z instance to write to
* savefiles as the user under whose UID it's run, rather
* than as the user specified with -Z, would thus be to switch
@@ -2642,6 +2909,7 @@ DIAG_ON_ASSIGN_ENUM
info(1);
}
pcap_close(pd);
+ pd = NULL;
if (VFileName != NULL) {
ret = get_next_file(VFile, VFileLine);
if (ret) {
@@ -2790,7 +3058,8 @@ cleanup(int signo _U_)
* to do anything with standard I/O streams in a signal handler -
* the ANSI C standard doesn't say it is).
*/
- pcap_breakloop(pd);
+ if (pd)
+ pcap_breakloop(pd);
#else
/*
* We don't have "pcap_breakloop()"; this isn't safe, but
@@ -2883,8 +3152,8 @@ compress_savefile(const char *filename)
child = fork_subprocess();
if (child == -1) {
fprintf(stderr,
- "compress_savefile: fork failed: %s\n",
- pcap_strerror(errno));
+ "%s: fork failed: %s\n",
+ __func__, pcap_strerror(errno));
return;
}
if (child != 0) {
@@ -2903,24 +3172,15 @@ compress_savefile(const char *filename)
#endif
if (execlp(zflag, zflag, filename, (char *)NULL) == -1)
fprintf(stderr,
- "compress_savefile: execlp(%s, %s) failed: %s\n",
- zflag,
- filename,
- pcap_strerror(errno));
+ "%s: execlp(%s, %s) failed: %s\n",
+ __func__, zflag, filename, pcap_strerror(errno));
#ifdef HAVE_FORK
exit(S_ERR_HOST_PROGRAM);
#else
_exit(S_ERR_HOST_PROGRAM);
#endif
}
-#else /* HAVE_FORK && HAVE_VFORK */
-static void
-compress_savefile(const char *filename)
-{
- fprintf(stderr,
- "compress_savefile failed. Functionality not implemented under your system\n");
-}
-#endif /* HAVE_FORK && HAVE_VFORK */
+#endif /* HAVE_FORK || HAVE_VFORK */
static void
dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
@@ -2968,11 +3228,13 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s
*/
pcap_dump_close(dump_info->pdd);
+#if defined(HAVE_FORK) || defined(HAVE_VFORK)
/*
* Compress the file we just closed, if the user asked for it
*/
if (zflag != NULL)
compress_savefile(dump_info->CurrentFileName);
+#endif
/*
* Check to see if we've exceeded the Wflag (when
@@ -3073,12 +3335,14 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s
*/
pcap_dump_close(dump_info->pdd);
+#if defined(HAVE_FORK) || defined(HAVE_VFORK)
/*
* Compress the file we just closed, if the user
* asked for it.
*/
if (zflag != NULL)
compress_savefile(dump_info->CurrentFileName);
+#endif
Cflag_count++;
if (Wflag > 0) {
@@ -3272,9 +3536,9 @@ print_usage(FILE *f)
{
print_version(f);
(void)fprintf(f,
-"Usage: %s [-Abd" D_FLAG "efhH" I_FLAG J_FLAG "KlLnNOpqStu" U_FLAG "vxX#]" B_FLAG_USAGE " [ -c count ] [--count]\n", program_name);
+"Usage: %s [-Abd" D_FLAG "efghH" I_FLAG J_FLAG "KlLnNOpqStu" U_FLAG "vxX#]" B_FLAG_USAGE " [ -c count ] [--count]\n", program_name);
(void)fprintf(f,
-"\t\t[ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]\n");
+"\t\t[ -C file_size ] " E_FLAG_USAGE "[ -F file ] [ -G seconds ]\n");
(void)fprintf(f,
"\t\t[ -i interface ]" IMMEDIATE_MODE_USAGE j_FLAG_USAGE "\n");
#ifdef HAVE_PCAP_FINDALLDEVS_EX
@@ -3286,7 +3550,7 @@ print_usage(FILE *f)
"\t\t" m_FLAG_USAGE "\n");
#endif
(void)fprintf(f,
-"\t\t[ -M secret ] [ --number ] [ --print ]" Q_FLAG_USAGE "\n");
+"\t\t" M_FLAG_USAGE "[ --number ] [ --print ]" Q_FLAG_USAGE "\n");
(void)fprintf(f,
"\t\t[ -r file ] [ -s snaplen ] [ -T type ] [ --version ]\n");
(void)fprintf(f,
@@ -3296,5 +3560,5 @@ print_usage(FILE *f)
"\t\t[ --time-stamp-precision precision ] [ --micro ] [ --nano ]\n");
#endif
(void)fprintf(f,
-"\t\t[ -z postrotate-command ] [ -Z user ] [ expression ]\n");
+"\t\t" z_FLAG_USAGE "[ -Z user ] [ expression ]\n");
}