Use dict for capabilities & optimizations in json output.
[rsync.git] / usage.c
1 /*
2  * Some usage & version related functions.
3  *
4  * Copyright (C) 2002-2022 Wayne Davison
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, visit the http://fsf.org website.
18  */
19
20 #include "rsync.h"
21 #include "version.h"
22 #include "latest-year.h"
23 #include "git-version.h"
24 #include "default-cvsignore.h"
25 #include "itypes.h"
26
27 extern struct name_num_obj valid_checksums, valid_compressions, valid_auth_checksums;
28
29 static char *istring(const char *fmt, int val)
30 {
31         char *str;
32         if (asprintf(&str, fmt, val) < 0)
33                 out_of_memory("istring");
34         return str;
35 }
36
37 static void print_info_flags(enum logcode f)
38 {
39         STRUCT_STAT *dumstat;
40         BOOL as_json = f == FNONE ? 1 : 0; /* We use 1 == first attribute, 2 == need closing array */
41         char line_buf[75], item_buf[32];
42         int line_len, j;
43         char *info_flags[] = {
44
45         "*Capabilities",
46
47                 istring("%d-bit files", (int)(sizeof (OFF_T) * 8)),
48                 istring("%d-bit inums", (int)(sizeof dumstat->st_ino * 8)), /* Don't check ino_t! */
49                 istring("%d-bit timestamps", (int)(sizeof (time_t) * 8)),
50                 istring("%d-bit long ints", (int)(sizeof (int64) * 8)),
51
52 #ifndef HAVE_SOCKETPAIR
53                 "no "
54 #endif
55                         "socketpairs",
56
57 #ifndef SUPPORT_LINKS
58                 "no "
59 #endif
60                         "symlinks",
61
62 #ifndef CAN_SET_SYMLINK_TIMES
63                 "no "
64 #endif
65                         "symtimes",
66
67 #ifndef SUPPORT_HARD_LINKS
68                 "no "
69 #endif
70                         "hardlinks",
71
72 #ifndef CAN_HARDLINK_SPECIAL
73                 "no "
74 #endif
75                         "hardlink-specials",
76
77 #ifndef CAN_HARDLINK_SYMLINK
78                 "no "
79 #endif
80                         "hardlink-symlinks",
81
82 #ifndef INET6
83                 "no "
84 #endif
85                         "IPv6",
86
87 #ifndef SUPPORT_ATIMES
88                 "no "
89 #endif
90                         "atimes",
91
92                 "batchfiles",
93
94 #ifndef HAVE_FTRUNCATE
95                 "no "
96 #endif
97                         "inplace",
98
99 #ifndef HAVE_FTRUNCATE
100                 "no "
101 #endif
102                         "append",
103
104 #ifndef SUPPORT_ACLS
105                 "no "
106 #endif
107                         "ACLs",
108
109 #ifndef SUPPORT_XATTRS
110                 "no "
111 #endif
112                         "xattrs",
113
114 #ifdef RSYNC_USE_SECLUDED_ARGS
115                 "default "
116 #else
117                 "optional "
118 #endif
119                         "secluded-args",
120
121 #ifndef ICONV_OPTION
122                 "no "
123 #endif
124                         "iconv",
125
126 #ifndef SUPPORT_PREALLOCATION
127                 "no "
128 #endif
129                         "prealloc",
130
131 #ifndef HAVE_MKTIME
132                 "no "
133 #endif
134                         "stop-at",
135
136 #ifndef SUPPORT_CRTIMES
137                 "no "
138 #endif
139                         "crtimes",
140
141         "*Optimizations",
142
143 #ifndef USE_ROLL_SIMD
144                 "no "
145 #endif
146                         "SIMD-roll",
147
148 #ifndef USE_ROLL_ASM
149                 "no "
150 #endif
151                         "asm-roll",
152
153 #ifndef USE_OPENSSL
154                 "no "
155 #endif
156                         "openssl-crypto",
157
158 #ifndef USE_MD5_ASM
159                 "no "
160 #endif
161                         "asm-MD5",
162
163                 NULL
164         };
165
166         for (line_len = 0, j = 0; ; j++) {
167                 char *str = info_flags[j], *next_nfo = str ? info_flags[j+1] : NULL;
168                 int need_comma = next_nfo && *next_nfo != '*' ? 1 : 0;
169                 int item_len;
170                 if (!str || *str == '*')
171                         item_len = 1000;
172                 else if (as_json) {
173                         char *space = strchr(str, ' ');
174                         int is_no = space && strncmp(str, "no ", 3) == 0;
175                         char *quot = space && !is_no ? "\"" : "";
176                         char *item = space ? space + 1 : str;
177                         char *val = !space ? "true" : is_no ? "false" : str;
178                         int val_len = !space ? 4 : is_no ? 5 : space - str;
179                         item_len = snprintf(item_buf, sizeof item_buf,
180                                            " \"%s\": %s%.*s%s%s", item, quot, val_len, val, quot,
181                                            need_comma ? "," : "");
182                         for (space = item; (space = strchr(space, ' ')) != NULL; space++)
183                                 item_buf[space - item + 2] = '_';
184                 } else
185                         item_len = snprintf(item_buf, sizeof item_buf, " %s%s", str, need_comma ? "," : "");
186                 if (line_len && line_len + item_len >= (int)sizeof line_buf) {
187                         if (as_json)
188                                 printf("   %s\n", line_buf);
189                         else
190                                 rprintf(f, "   %s\n", line_buf);
191                         line_len = 0;
192                 }
193                 if (!str)
194                         break;
195                 if (*str == '*') {
196                         if (as_json) {
197                                 if (as_json == 2)
198                                         printf("  }");
199                                 else
200                                         as_json = 2;
201                                 printf(",\n  \"%c%s\": {\n", toLower(str+1), str+2);
202                         } else
203                                 rprintf(f, "%s:\n", str+1);
204                 } else {
205                         strlcpy(line_buf + line_len, item_buf, sizeof line_buf - line_len);
206                         line_len += item_len;
207                 }
208         }
209         if (as_json == 2)
210                 printf("  }");
211 }
212
213 static void output_nno_list(enum logcode f, const char *name, struct name_num_obj *nno)
214 {
215         char namebuf[64], tmpbuf[256];
216         char *tok, *next_tok, *comma = ",";
217         char *cp;
218
219         /* Using '(' ensures that we get a trailing "none" but also includes aliases. */
220         get_default_nno_list(nno, tmpbuf, sizeof tmpbuf - 1, '(');
221         if (f != FNONE) {
222                 rprintf(f, "%s:\n", name);
223                 rprintf(f, "    %s\n", tmpbuf);
224                 return;
225         }
226
227         strlcpy(namebuf, name, sizeof namebuf);
228         for (cp = namebuf; *cp; cp++) {
229                 if (*cp == ' ')
230                         *cp = '_';
231                 else if (isUpper(cp))
232                         *cp = toLower(cp);
233         }
234
235         printf(",\n  \"%s\": [\n   ", namebuf);
236
237         for (tok = strtok(tmpbuf, " "); tok; tok = next_tok) {
238                 next_tok = strtok(NULL, " ");
239                 if (*tok != '(') /* Ignore the alises in the JSON output */
240                         printf(" \"%s\"%s", tok, comma + (next_tok ? 0 : 1));
241         }
242
243         printf("\n  ]");
244 }
245
246 /* A request of f == FNONE wants json on stdout. */
247 void print_rsync_version(enum logcode f)
248 {
249         char copyright[] = "(C) 1996-" LATEST_YEAR " by Andrew Tridgell, Wayne Davison, and others.";
250         char url[] = "https://rsync.samba.org/";
251         BOOL first_line = 1;
252
253 #define json_line(name, value) \
254         do { \
255                 printf("%c\n  \"%s\": \"%s\"", first_line ? '{' : ',', name, value); \
256                 first_line = 0; \
257         } while (0)
258
259         if (f == FNONE) {
260                 char verbuf[32];
261                 json_line("program", RSYNC_NAME);
262                 json_line("version", rsync_version());
263                 snprintf(verbuf, sizeof verbuf, "%d.%d", PROTOCOL_VERSION, SUBPROTOCOL_VERSION);
264                 json_line("protocol", verbuf);
265                 json_line("copyright", copyright);
266                 json_line("url", url);
267         } else {
268 #if SUBPROTOCOL_VERSION != 0
269                 char *subprotocol = istring(".PR%d", SUBPROTOCOL_VERSION);
270 #else
271                 char *subprotocol = "";
272 #endif
273                 rprintf(f, "%s  version %s  protocol version %d%s\n",
274                         RSYNC_NAME, rsync_version(), PROTOCOL_VERSION, subprotocol);
275                 rprintf(f, "Copyright %s\n", copyright);
276                 rprintf(f, "Web site: %s\n", url);
277         }
278
279         print_info_flags(f);
280
281         init_checksum_choices();
282
283         output_nno_list(f, "Checksum list", &valid_checksums);
284         output_nno_list(f, "Compress list", &valid_compressions);
285         output_nno_list(f, "Daemon auth list", &valid_auth_checksums);
286
287         if (f == FNONE) {
288                 json_line("license", "GPL3");
289                 json_line("caveat", "rsync comes with ABSOLUTELY NO WARRANTY");
290                 printf("\n}\n");
291                 return;
292         }
293
294 #ifdef MAINTAINER_MODE
295         rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
296 #endif
297
298 #if SIZEOF_INT64 < 8
299         rprintf(f, "WARNING: no 64-bit integers on this platform!\n");
300 #endif
301         if (sizeof (int64) != SIZEOF_INT64) {
302                 rprintf(f,
303                         "WARNING: size mismatch in SIZEOF_INT64 define (%d != %d)\n",
304                         (int) SIZEOF_INT64, (int) sizeof (int64));
305         }
306
307         rprintf(f,"\n");
308         rprintf(f,"rsync comes with ABSOLUTELY NO WARRANTY.  This is free software, and you\n");
309         rprintf(f,"are welcome to redistribute it under certain conditions.  See the GNU\n");
310         rprintf(f,"General Public Licence for details.\n");
311 }
312
313 void usage(enum logcode F)
314 {
315   print_rsync_version(F);
316
317   rprintf(F,"\n");
318   rprintf(F,"rsync is a file transfer program capable of efficient remote update\n");
319   rprintf(F,"via a fast differencing algorithm.\n");
320
321   rprintf(F,"\n");
322   rprintf(F,"Usage: rsync [OPTION]... SRC [SRC]... DEST\n");
323   rprintf(F,"  or   rsync [OPTION]... SRC [SRC]... [USER@]HOST:DEST\n");
324   rprintf(F,"  or   rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST\n");
325   rprintf(F,"  or   rsync [OPTION]... SRC [SRC]... rsync://[USER@]HOST[:PORT]/DEST\n");
326   rprintf(F,"  or   rsync [OPTION]... [USER@]HOST:SRC [DEST]\n");
327   rprintf(F,"  or   rsync [OPTION]... [USER@]HOST::SRC [DEST]\n");
328   rprintf(F,"  or   rsync [OPTION]... rsync://[USER@]HOST[:PORT]/SRC [DEST]\n");
329   rprintf(F,"The ':' usages connect via remote shell, while '::' & 'rsync://' usages connect\n");
330   rprintf(F,"to an rsync daemon, and require SRC or DEST to start with a module name.\n");
331   rprintf(F,"\n");
332   rprintf(F,"Options\n");
333 #include "help-rsync.h"
334   rprintf(F,"\n");
335   rprintf(F,"Use \"rsync --daemon --help\" to see the daemon-mode command-line options.\n");
336   rprintf(F,"Please see the rsync(1) and rsyncd.conf(5) manpages for full documentation.\n");
337   rprintf(F,"See https://rsync.samba.org/ for updates, bug reports, and answers\n");
338 }
339
340 void daemon_usage(enum logcode F)
341 {
342   print_rsync_version(F);
343
344   rprintf(F,"\n");
345   rprintf(F,"Usage: rsync --daemon [OPTION]...\n");
346 #include "help-rsyncd.h"
347   rprintf(F,"\n");
348   rprintf(F,"If you were not trying to invoke rsync as a daemon, avoid using any of the\n");
349   rprintf(F,"daemon-specific rsync options.  See also the rsyncd.conf(5) manpage.\n");
350 }
351
352 const char *rsync_version(void)
353 {
354         char *ver;
355 #ifdef RSYNC_GITVER
356         ver = RSYNC_GITVER;
357 #else
358         ver = RSYNC_VERSION;
359 #endif
360         return *ver == 'v' ? ver+1 : ver;
361 }
362
363 const char *default_cvsignore(void)
364 {
365         return DEFAULT_CVSIGNORE;
366 }