1 /*
2 chronyd/chronyc - Programs for keeping computer clocks accurate.
3
4 **********************************************************************
5 * Copyright (C) Richard P. Curnow 1997-2003
6 * Copyright (C) Lonnie Abelbeck 2016, 2018
7 * Copyright (C) Miroslav Lichvar 2009-2021
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of version 2 of the GNU General Public License as
11 * published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 *
22 **********************************************************************
23
24 =======================================================================
25
26 Command line client for configuring the daemon and obtaining status
27 from it whilst running.
28 */
29
30 #include "config.h"
31
32 #include "sysincl.h"
33
34 #include "array.h"
35 #include "candm.h"
36 #include "cmac.h"
37 #include "logging.h"
38 #include "memory.h"
39 #include "nameserv.h"
40 #include "getdate.h"
41 #include "cmdparse.h"
42 #include "pktlength.h"
43 #include "socket.h"
44 #include "util.h"
45
46 #ifdef FEAT_READLINE
47 #include <editline/readline.h>
48 #endif
49
50 /* ================================================== */
51
52 struct Address {
53 SCK_AddressType type;
54 union {
55 IPSockAddr ip;
56 char *path;
57 } addr;
58 };
59
60 static ARR_Instance server_addresses;
61
62 static int sock_fd = -1;
63
64 static volatile int quit = 0;
65
66 static int on_terminal = 0;
67
68 static int no_dns = 0;
69
70 static int source_names = 0;
71
72 static int csv_mode = 0;
73
74 /* ================================================== */
75 /* Log a message. This is a minimalistic replacement of the logging.c
76 implementation to avoid linking with it and other modules. */
77
78 LOG_Severity log_min_severity = LOGS_INFO;
79
LOG_Message(LOG_Severity severity,int line_number,const char * filename,const char * function_name,const char * format,...)80 void LOG_Message(LOG_Severity severity,
81 #if DEBUG > 0
82 int line_number, const char *filename, const char *function_name,
83 #endif
84 const char *format, ...)
85 {
86 va_list ap;
87
88 if (severity < log_min_severity)
89 return;
90
91 va_start(ap, format);
92 vfprintf(stderr, format, ap);
93 putc('\n', stderr);
94 va_end(ap);
95 }
96
97 /* ================================================== */
98 /* Read a single line of commands from standard input */
99
100 #ifdef FEAT_READLINE
101 static char **command_name_completion(const char *text, int start, int end);
102 #endif
103
104 static char *
read_line(void)105 read_line(void)
106 {
107 static char line[2048];
108 static const char *prompt = "chronyc> ";
109
110 if (on_terminal) {
111 #ifdef FEAT_READLINE
112 char *cmd;
113
114 rl_attempted_completion_function = command_name_completion;
115 rl_basic_word_break_characters = " \t\n\r";
116
117 /* save line only if not empty */
118 cmd = readline(prompt);
119 if( cmd == NULL ) return( NULL );
120
121 /* user pressed return */
122 if( *cmd != '\0' ) {
123 strncpy(line, cmd, sizeof(line) - 1);
124 line[sizeof(line) - 1] = '\0';
125 add_history(cmd);
126 /* free the buffer allocated by readline */
127 Free(cmd);
128 } else {
129 /* simulate the user has entered an empty line */
130 *line = '\0';
131 }
132 return( line );
133 #else
134 printf("%s", prompt);
135 fflush(stdout);
136 #endif
137 }
138 if (fgets(line, sizeof(line), stdin)) {
139 return line;
140 } else {
141 return NULL;
142 }
143
144 }
145
146 /* ================================================== */
147
148 static ARR_Instance
get_addresses(const char * hostnames,int port)149 get_addresses(const char *hostnames, int port)
150 {
151 struct Address *addr;
152 ARR_Instance addrs;
153 char *hostname, *s1, *s2;
154 IPAddr ip_addrs[DNS_MAX_ADDRESSES];
155 int i;
156
157 addrs = ARR_CreateInstance(sizeof (*addr));
158 s1 = Strdup(hostnames);
159
160 /* Parse the comma-separated list of hostnames */
161 for (hostname = s1; hostname && *hostname; hostname = s2) {
162 s2 = strchr(hostname, ',');
163 if (s2)
164 *s2++ = '\0';
165
166 /* hostname starting with / is considered a path of Unix domain socket */
167 if (hostname[0] == '/') {
168 addr = ARR_GetNewElement(addrs);
169 addr->type = SCK_ADDR_UNIX;
170 addr->addr.path = Strdup(hostname);
171 } else {
172 if (DNS_Name2IPAddress(hostname, ip_addrs, DNS_MAX_ADDRESSES) != DNS_Success) {
173 DEBUG_LOG("Could not get IP address for %s", hostname);
174 continue;
175 }
176
177 for (i = 0; i < DNS_MAX_ADDRESSES && ip_addrs[i].family != IPADDR_UNSPEC; i++) {
178 addr = ARR_GetNewElement(addrs);
179 addr->type = SCK_ADDR_IP;
180 addr->addr.ip.ip_addr = ip_addrs[i];
181 addr->addr.ip.port = port;
182 DEBUG_LOG("Resolved %s to %s", hostname, UTI_IPToString(&ip_addrs[i]));
183 }
184 }
185 }
186
187 Free(s1);
188 return addrs;
189 }
190
191 /* ================================================== */
192
193 static void
free_addresses(ARR_Instance addresses)194 free_addresses(ARR_Instance addresses)
195 {
196 struct Address *addr;
197 unsigned int i;
198
199 for (i = 0; i < ARR_GetSize(addresses); i++) {
200 addr = ARR_GetElement(addresses, i);
201
202 if (addr->type == SCK_ADDR_UNIX)
203 Free(addr->addr.path);
204 }
205
206 ARR_DestroyInstance(addresses);
207 }
208
209 /* ================================================== */
210 /* Initialise the socket used to talk to the daemon */
211
212 static int
open_socket(struct Address * addr)213 open_socket(struct Address *addr)
214 {
215 char *dir, *local_addr;
216 size_t local_addr_len;
217
218 switch (addr->type) {
219 case SCK_ADDR_IP:
220 sock_fd = SCK_OpenUdpSocket(&addr->addr.ip, NULL, NULL, 0);
221 break;
222 case SCK_ADDR_UNIX:
223 /* Construct path of our socket. Use the same directory as the server
224 socket and include our process ID to allow multiple chronyc instances
225 running at the same time. */
226
227 dir = UTI_PathToDir(addr->addr.path);
228 local_addr_len = strlen(dir) + 50;
229 local_addr = Malloc(local_addr_len);
230
231 snprintf(local_addr, local_addr_len, "%s/chronyc.%d.sock", dir, (int)getpid());
232
233 sock_fd = SCK_OpenUnixDatagramSocket(addr->addr.path, local_addr,
234 SCK_FLAG_ALL_PERMISSIONS);
235 Free(dir);
236 Free(local_addr);
237
238 break;
239 default:
240 assert(0);
241 }
242
243 if (sock_fd < 0)
244 return 0;
245
246 return 1;
247 }
248
249 /* ================================================== */
250
251 static void
close_io(void)252 close_io(void)
253 {
254 if (sock_fd < 0)
255 return;
256
257 SCK_RemoveSocket(sock_fd);
258 SCK_CloseSocket(sock_fd);
259 sock_fd = -1;
260 }
261
262 /* ================================================== */
263
264 static int
open_io(void)265 open_io(void)
266 {
267 static unsigned int address_index = 0;
268 struct Address *addr;
269
270 /* If a socket is already opened, close it and try the next address */
271 if (sock_fd >= 0) {
272 close_io();
273 address_index++;
274 }
275
276 /* Find an address for which a socket can be opened and connected */
277 for (; address_index < ARR_GetSize(server_addresses); address_index++) {
278 addr = ARR_GetElement(server_addresses, address_index);
279
280 if (open_socket(addr))
281 return 1;
282
283 close_io();
284 }
285
286 return 0;
287 }
288
289 /* ================================================== */
290
291 static void
bits_to_mask(int bits,int family,IPAddr * mask)292 bits_to_mask(int bits, int family, IPAddr *mask)
293 {
294 int i;
295
296 mask->family = family;
297 switch (family) {
298 case IPADDR_INET4:
299 if (bits > 32 || bits < 0)
300 bits = 32;
301 if (bits > 0) {
302 mask->addr.in4 = -1;
303 mask->addr.in4 <<= 32 - bits;
304 } else {
305 mask->addr.in4 = 0;
306 }
307 break;
308 case IPADDR_INET6:
309 if (bits > 128 || bits < 0)
310 bits = 128;
311 for (i = 0; i < bits / 8; i++)
312 mask->addr.in6[i] = 0xff;
313 if (i < 16)
314 mask->addr.in6[i++] = (0xff << (8 - bits % 8)) & 0xff;
315 for (; i < 16; i++)
316 mask->addr.in6[i] = 0x0;
317 break;
318 case IPADDR_ID:
319 mask->family = IPADDR_UNSPEC;
320 break;
321 default:
322 assert(0);
323 }
324 }
325
326 /* ================================================== */
327
328 static int
parse_source_address(char * word,IPAddr * address)329 parse_source_address(char *word, IPAddr *address)
330 {
331 if (UTI_StringToIdIP(word, address))
332 return 1;
333
334 if (DNS_Name2IPAddress(word, address, 1) == DNS_Success)
335 return 1;
336
337 return 0;
338 }
339
340 /* ================================================== */
341
342 static int
read_mask_address(char * line,IPAddr * mask,IPAddr * address)343 read_mask_address(char *line, IPAddr *mask, IPAddr *address)
344 {
345 unsigned int bits;
346 char *p, *q;
347
348 p = line;
349 if (!*p) {
350 mask->family = address->family = IPADDR_UNSPEC;
351 return 1;
352 } else {
353 q = strchr(p, '/');
354 if (q) {
355 *q++ = 0;
356 if (UTI_StringToIP(p, mask)) {
357 p = q;
358 if (UTI_StringToIP(p, address)) {
359 if (address->family == mask->family)
360 return 1;
361 } else if (sscanf(p, "%u", &bits) == 1) {
362 *address = *mask;
363 bits_to_mask(bits, address->family, mask);
364 return 1;
365 }
366 }
367 } else {
368 if (parse_source_address(p, address)) {
369 bits_to_mask(-1, address->family, mask);
370 return 1;
371 } else {
372 LOG(LOGS_ERR, "Could not get address for hostname");
373 return 0;
374 }
375 }
376 }
377
378 LOG(LOGS_ERR, "Invalid syntax for mask/address");
379 return 0;
380 }
381
382 /* ================================================== */
383
384 static int
process_cmd_offline(CMD_Request * msg,char * line)385 process_cmd_offline(CMD_Request *msg, char *line)
386 {
387 IPAddr mask, address;
388 int ok;
389
390 if (read_mask_address(line, &mask, &address)) {
391 UTI_IPHostToNetwork(&mask, &msg->data.offline.mask);
392 UTI_IPHostToNetwork(&address, &msg->data.offline.address);
393 msg->command = htons(REQ_OFFLINE);
394 ok = 1;
395 } else {
396 ok = 0;
397 }
398
399 return ok;
400
401 }
402
403 /* ================================================== */
404
405
406 static int
process_cmd_online(CMD_Request * msg,char * line)407 process_cmd_online(CMD_Request *msg, char *line)
408 {
409 IPAddr mask, address;
410 int ok;
411
412 if (read_mask_address(line, &mask, &address)) {
413 UTI_IPHostToNetwork(&mask, &msg->data.online.mask);
414 UTI_IPHostToNetwork(&address, &msg->data.online.address);
415 msg->command = htons(REQ_ONLINE);
416 ok = 1;
417 } else {
418 ok = 0;
419 }
420
421 return ok;
422
423 }
424
425 /* ================================================== */
426
427 static void
process_cmd_onoffline(CMD_Request * msg,char * line)428 process_cmd_onoffline(CMD_Request *msg, char *line)
429 {
430 msg->command = htons(REQ_ONOFFLINE);
431 }
432
433 /* ================================================== */
434
435 static int
read_address_integer(char * line,IPAddr * address,int * value)436 read_address_integer(char *line, IPAddr *address, int *value)
437 {
438 char *hostname;
439 int ok = 0;
440
441 hostname = line;
442 line = CPS_SplitWord(line);
443
444 if (sscanf(line, "%d", value) != 1) {
445 LOG(LOGS_ERR, "Invalid syntax for address value");
446 ok = 0;
447 } else {
448 if (!parse_source_address(hostname, address)) {
449 LOG(LOGS_ERR, "Could not get address for hostname");
450 ok = 0;
451 } else {
452 ok = 1;
453 }
454 }
455
456 return ok;
457
458 }
459
460
461 /* ================================================== */
462
463 static int
read_address_double(char * line,IPAddr * address,double * value)464 read_address_double(char *line, IPAddr *address, double *value)
465 {
466 char *hostname;
467 int ok = 0;
468
469 hostname = line;
470 line = CPS_SplitWord(line);
471
472 if (sscanf(line, "%lf", value) != 1) {
473 LOG(LOGS_ERR, "Invalid syntax for address value");
474 ok = 0;
475 } else {
476 if (!parse_source_address(hostname, address)) {
477 LOG(LOGS_ERR, "Could not get address for hostname");
478 ok = 0;
479 } else {
480 ok = 1;
481 }
482 }
483
484 return ok;
485
486 }
487
488
489 /* ================================================== */
490
491 static int
process_cmd_minpoll(CMD_Request * msg,char * line)492 process_cmd_minpoll(CMD_Request *msg, char *line)
493 {
494 IPAddr address;
495 int minpoll;
496 int ok;
497
498 if (read_address_integer(line, &address, &minpoll)) {
499 UTI_IPHostToNetwork(&address, &msg->data.modify_minpoll.address);
500 msg->data.modify_minpoll.new_minpoll = htonl(minpoll);
501 msg->command = htons(REQ_MODIFY_MINPOLL);
502 ok = 1;
503 } else {
504 ok = 0;
505 }
506
507 return ok;
508
509 }
510
511 /* ================================================== */
512
513 static int
process_cmd_maxpoll(CMD_Request * msg,char * line)514 process_cmd_maxpoll(CMD_Request *msg, char *line)
515 {
516 IPAddr address;
517 int maxpoll;
518 int ok;
519
520 if (read_address_integer(line, &address, &maxpoll)) {
521 UTI_IPHostToNetwork(&address, &msg->data.modify_maxpoll.address);
522 msg->data.modify_maxpoll.new_maxpoll = htonl(maxpoll);
523 msg->command = htons(REQ_MODIFY_MAXPOLL);
524 ok = 1;
525 } else {
526 ok = 0;
527 }
528
529 return ok;
530
531 }
532
533 /* ================================================== */
534
535 static int
process_cmd_maxdelay(CMD_Request * msg,char * line)536 process_cmd_maxdelay(CMD_Request *msg, char *line)
537 {
538 IPAddr address;
539 double max_delay;
540 int ok;
541
542 if (read_address_double(line, &address, &max_delay)) {
543 UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelay.address);
544 msg->data.modify_maxdelay.new_max_delay = UTI_FloatHostToNetwork(max_delay);
545 msg->command = htons(REQ_MODIFY_MAXDELAY);
546 ok = 1;
547 } else {
548 ok = 0;
549 }
550
551 return ok;
552
553 }
554
555 /* ================================================== */
556
557 static int
process_cmd_maxdelaydevratio(CMD_Request * msg,char * line)558 process_cmd_maxdelaydevratio(CMD_Request *msg, char *line)
559 {
560 IPAddr address;
561 double max_delay_dev_ratio;
562 int ok;
563
564 if (read_address_double(line, &address, &max_delay_dev_ratio)) {
565 UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelaydevratio.address);
566 msg->data.modify_maxdelayratio.new_max_delay_ratio = UTI_FloatHostToNetwork(max_delay_dev_ratio);
567 msg->command = htons(REQ_MODIFY_MAXDELAYDEVRATIO);
568 ok = 1;
569 } else {
570 ok = 0;
571 }
572
573 return ok;
574
575 }
576
577 /* ================================================== */
578
579 static int
process_cmd_maxdelayratio(CMD_Request * msg,char * line)580 process_cmd_maxdelayratio(CMD_Request *msg, char *line)
581 {
582 IPAddr address;
583 double max_delay_ratio;
584 int ok;
585
586 if (read_address_double(line, &address, &max_delay_ratio)) {
587 UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelayratio.address);
588 msg->data.modify_maxdelayratio.new_max_delay_ratio = UTI_FloatHostToNetwork(max_delay_ratio);
589 msg->command = htons(REQ_MODIFY_MAXDELAYRATIO);
590 ok = 1;
591 } else {
592 ok = 0;
593 }
594
595 return ok;
596
597 }
598
599 /* ================================================== */
600
601 static int
process_cmd_minstratum(CMD_Request * msg,char * line)602 process_cmd_minstratum(CMD_Request *msg, char *line)
603 {
604 IPAddr address;
605 int min_stratum;
606 int ok;
607
608 if (read_address_integer(line, &address, &min_stratum)) {
609 UTI_IPHostToNetwork(&address, &msg->data.modify_minstratum.address);
610 msg->data.modify_minstratum.new_min_stratum = htonl(min_stratum);
611 msg->command = htons(REQ_MODIFY_MINSTRATUM);
612 ok = 1;
613 } else {
614 ok = 0;
615 }
616
617 return ok;
618
619 }
620
621 /* ================================================== */
622
623 static int
process_cmd_polltarget(CMD_Request * msg,char * line)624 process_cmd_polltarget(CMD_Request *msg, char *line)
625 {
626 IPAddr address;
627 int poll_target;
628 int ok;
629
630 if (read_address_integer(line, &address, &poll_target)) {
631 UTI_IPHostToNetwork(&address, &msg->data.modify_polltarget.address);
632 msg->data.modify_polltarget.new_poll_target = htonl(poll_target);
633 msg->command = htons(REQ_MODIFY_POLLTARGET);
634 ok = 1;
635 } else {
636 ok = 0;
637 }
638
639 return ok;
640
641 }
642
643 /* ================================================== */
644
645 static int
process_cmd_maxupdateskew(CMD_Request * msg,char * line)646 process_cmd_maxupdateskew(CMD_Request *msg, char *line)
647 {
648 int ok;
649 double new_max_update_skew;
650
651 if (sscanf(line, "%lf", &new_max_update_skew) == 1) {
652 msg->data.modify_maxupdateskew.new_max_update_skew = UTI_FloatHostToNetwork(new_max_update_skew);
653 msg->command = htons(REQ_MODIFY_MAXUPDATESKEW);
654 ok = 1;
655 } else {
656 ok = 0;
657 }
658
659 return ok;
660
661 }
662
663 /* ================================================== */
664
665 static void
process_cmd_dump(CMD_Request * msg,char * line)666 process_cmd_dump(CMD_Request *msg, char *line)
667 {
668 msg->command = htons(REQ_DUMP);
669 msg->data.dump.pad = htonl(0);
670 }
671
672 /* ================================================== */
673
674 static void
process_cmd_writertc(CMD_Request * msg,char * line)675 process_cmd_writertc(CMD_Request *msg, char *line)
676 {
677 msg->command = htons(REQ_WRITERTC);
678 }
679
680 /* ================================================== */
681
682 static void
process_cmd_trimrtc(CMD_Request * msg,char * line)683 process_cmd_trimrtc(CMD_Request *msg, char *line)
684 {
685 msg->command = htons(REQ_TRIMRTC);
686 }
687
688 /* ================================================== */
689
690 static void
process_cmd_cyclelogs(CMD_Request * msg,char * line)691 process_cmd_cyclelogs(CMD_Request *msg, char *line)
692 {
693 msg->command = htons(REQ_CYCLELOGS);
694 }
695
696 /* ================================================== */
697
698 static int
process_cmd_burst(CMD_Request * msg,char * line)699 process_cmd_burst(CMD_Request *msg, char *line)
700 {
701 int n_good_samples, n_total_samples;
702 char *s1, *s2;
703 IPAddr address, mask;
704
705 s1 = line;
706 s2 = CPS_SplitWord(s1);
707 CPS_SplitWord(s2);
708
709 if (sscanf(s1, "%d/%d", &n_good_samples, &n_total_samples) != 2) {
710 LOG(LOGS_ERR, "Invalid syntax for burst command");
711 return 0;
712 }
713
714 mask.family = address.family = IPADDR_UNSPEC;
715 if (*s2 && !read_mask_address(s2, &mask, &address)) {
716 return 0;
717 }
718
719 msg->command = htons(REQ_BURST);
720 msg->data.burst.n_good_samples = ntohl(n_good_samples);
721 msg->data.burst.n_total_samples = ntohl(n_total_samples);
722
723 UTI_IPHostToNetwork(&mask, &msg->data.burst.mask);
724 UTI_IPHostToNetwork(&address, &msg->data.burst.address);
725
726 return 1;
727 }
728
729 /* ================================================== */
730
731 static int
process_cmd_local(CMD_Request * msg,char * line)732 process_cmd_local(CMD_Request *msg, char *line)
733 {
734 int on_off, stratum = 0, orphan = 0;
735 double distance = 0.0;
736
737 if (!strcmp(line, "off")) {
738 on_off = 0;
739 } else if (CPS_ParseLocal(line, &stratum, &orphan, &distance)) {
740 on_off = 1;
741 } else {
742 LOG(LOGS_ERR, "Invalid syntax for local command");
743 return 0;
744 }
745
746 msg->command = htons(REQ_LOCAL2);
747 msg->data.local.on_off = htonl(on_off);
748 msg->data.local.stratum = htonl(stratum);
749 msg->data.local.distance = UTI_FloatHostToNetwork(distance);
750 msg->data.local.orphan = htonl(orphan);
751
752 return 1;
753 }
754
755 /* ================================================== */
756
757 static int
process_cmd_manual(CMD_Request * msg,const char * line)758 process_cmd_manual(CMD_Request *msg, const char *line)
759 {
760 const char *p;
761
762 p = line;
763
764 if (!strcmp(p, "off")) {
765 msg->data.manual.option = htonl(0);
766 } else if (!strcmp(p, "on")) {
767 msg->data.manual.option = htonl(1);
768 } else if (!strcmp(p, "reset")) {
769 msg->data.manual.option = htonl(2);
770 } else {
771 LOG(LOGS_ERR, "Invalid syntax for manual command");
772 return 0;
773 }
774 msg->command = htons(REQ_MANUAL);
775
776 return 1;
777 }
778
779 /* ================================================== */
780
781 static int
process_cmd_allowdeny(CMD_Request * msg,char * line,int cmd,int allcmd)782 process_cmd_allowdeny(CMD_Request *msg, char *line, int cmd, int allcmd)
783 {
784 int all, subnet_bits;
785 IPAddr ip;
786
787 if (!CPS_ParseAllowDeny(line, &all, &ip, &subnet_bits)) {
788 LOG(LOGS_ERR, "Could not read address");
789 return 0;
790 }
791
792 msg->command = htons(all ? allcmd : cmd);
793 UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip);
794 msg->data.allow_deny.subnet_bits = htonl(subnet_bits);
795
796 return 1;
797 }
798
799 /* ================================================== */
800
801 static int
process_cmd_accheck(CMD_Request * msg,char * line)802 process_cmd_accheck(CMD_Request *msg, char *line)
803 {
804 IPAddr ip;
805 msg->command = htons(REQ_ACCHECK);
806 if (DNS_Name2IPAddress(line, &ip, 1) == DNS_Success) {
807 UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip);
808 return 1;
809 } else {
810 LOG(LOGS_ERR, "Could not read address");
811 return 0;
812 }
813 }
814
815 /* ================================================== */
816
817 static int
process_cmd_cmdaccheck(CMD_Request * msg,char * line)818 process_cmd_cmdaccheck(CMD_Request *msg, char *line)
819 {
820 IPAddr ip;
821 msg->command = htons(REQ_CMDACCHECK);
822 if (DNS_Name2IPAddress(line, &ip, 1) == DNS_Success) {
823 UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip);
824 return 1;
825 } else {
826 LOG(LOGS_ERR, "Could not read address");
827 return 0;
828 }
829 }
830
831 /* ================================================== */
832
833 static int
process_cmd_dfreq(CMD_Request * msg,char * line)834 process_cmd_dfreq(CMD_Request *msg, char *line)
835 {
836 double dfreq;
837
838 msg->command = htons(REQ_DFREQ);
839
840 if (sscanf(line, "%lf", &dfreq) != 1) {
841 LOG(LOGS_ERR, "Invalid value");
842 return 0;
843 }
844
845 msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(dfreq);
846 return 1;
847 }
848
849 /* ================================================== */
850
851 static int
process_cmd_doffset(CMD_Request * msg,char * line)852 process_cmd_doffset(CMD_Request *msg, char *line)
853 {
854 double doffset;
855
856 msg->command = htons(REQ_DOFFSET2);
857
858 if (sscanf(line, "%lf", &doffset) != 1) {
859 LOG(LOGS_ERR, "Invalid value");
860 return 0;
861 }
862
863 msg->data.doffset.doffset = UTI_FloatHostToNetwork(doffset);
864 return 1;
865 }
866
867 /* ================================================== */
868
869 static int
process_cmd_add_source(CMD_Request * msg,char * line)870 process_cmd_add_source(CMD_Request *msg, char *line)
871 {
872 CPS_NTP_Source data;
873 IPAddr ip_addr;
874 int result = 0, status, type;
875 const char *opt_name, *word;
876
877 msg->command = htons(REQ_ADD_SOURCE);
878
879 word = line;
880 line = CPS_SplitWord(line);
881
882 if (!strcasecmp(word, "server")) {
883 type = REQ_ADDSRC_SERVER;
884 } else if (!strcasecmp(word, "peer")) {
885 type = REQ_ADDSRC_PEER;
886 } else if (!strcasecmp(word, "pool")) {
887 type = REQ_ADDSRC_POOL;
888 } else {
889 LOG(LOGS_ERR, "Invalid syntax for add command");
890 return 0;
891 }
892
893 status = CPS_ParseNTPSourceAdd(line, &data);
894 switch (status) {
895 case 0:
896 LOG(LOGS_ERR, "Invalid syntax for add command");
897 break;
898 default:
899 /* Verify that the address is resolvable (chronyc and chronyd are
900 assumed to be running on the same host) */
901 if (strlen(data.name) >= sizeof (msg->data.ntp_source.name) ||
902 DNS_Name2IPAddress(data.name, &ip_addr, 1) != DNS_Success) {
903 LOG(LOGS_ERR, "Invalid host/IP address");
904 break;
905 }
906
907 opt_name = NULL;
908 if (opt_name) {
909 LOG(LOGS_ERR, "%s can't be set in chronyc", opt_name);
910 break;
911 }
912
913 msg->data.ntp_source.type = htonl(type);
914 if (strlen(data.name) >= sizeof (msg->data.ntp_source.name))
915 assert(0);
916 strncpy((char *)msg->data.ntp_source.name, data.name,
917 sizeof (msg->data.ntp_source.name));
918 msg->data.ntp_source.port = htonl(data.port);
919 msg->data.ntp_source.minpoll = htonl(data.params.minpoll);
920 msg->data.ntp_source.maxpoll = htonl(data.params.maxpoll);
921 msg->data.ntp_source.presend_minpoll = htonl(data.params.presend_minpoll);
922 msg->data.ntp_source.min_stratum = htonl(data.params.min_stratum);
923 msg->data.ntp_source.poll_target = htonl(data.params.poll_target);
924 msg->data.ntp_source.version = htonl(data.params.version);
925 msg->data.ntp_source.max_sources = htonl(data.params.max_sources);
926 msg->data.ntp_source.min_samples = htonl(data.params.min_samples);
927 msg->data.ntp_source.max_samples = htonl(data.params.max_samples);
928 msg->data.ntp_source.authkey = htonl(data.params.authkey);
929 msg->data.ntp_source.nts_port = htonl(data.params.nts_port);
930 msg->data.ntp_source.max_delay = UTI_FloatHostToNetwork(data.params.max_delay);
931 msg->data.ntp_source.max_delay_ratio = UTI_FloatHostToNetwork(data.params.max_delay_ratio);
932 msg->data.ntp_source.max_delay_dev_ratio =
933 UTI_FloatHostToNetwork(data.params.max_delay_dev_ratio);
934 msg->data.ntp_source.min_delay = UTI_FloatHostToNetwork(data.params.min_delay);
935 msg->data.ntp_source.asymmetry = UTI_FloatHostToNetwork(data.params.asymmetry);
936 msg->data.ntp_source.offset = UTI_FloatHostToNetwork(data.params.offset);
937 msg->data.ntp_source.flags = htonl(
938 (data.params.connectivity == SRC_ONLINE ? REQ_ADDSRC_ONLINE : 0) |
939 (data.params.auto_offline ? REQ_ADDSRC_AUTOOFFLINE : 0) |
940 (data.params.iburst ? REQ_ADDSRC_IBURST : 0) |
941 (data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 0) |
942 (data.params.burst ? REQ_ADDSRC_BURST : 0) |
943 (data.params.nts ? REQ_ADDSRC_NTS : 0) |
944 (data.params.copy ? REQ_ADDSRC_COPY : 0) |
945 (data.params.ext_fields & NTP_EF_FLAG_EXP1 ? REQ_ADDSRC_EF_EXP1 : 0) |
946 (data.params.sel_options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) |
947 (data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
948 (data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
949 (data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0));
950 msg->data.ntp_source.filter_length = htonl(data.params.filter_length);
951 msg->data.ntp_source.cert_set = htonl(data.params.cert_set);
952 memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved));
953
954 result = 1;
955
956 break;
957 }
958
959 return result;
960 }
961
962 /* ================================================== */
963
964 static int
process_cmd_delete(CMD_Request * msg,char * line)965 process_cmd_delete(CMD_Request *msg, char *line)
966 {
967 char *hostname;
968 int ok = 0;
969 IPAddr address;
970
971 msg->command = htons(REQ_DEL_SOURCE);
972 hostname = line;
973 CPS_SplitWord(line);
974
975 if (!*hostname) {
976 LOG(LOGS_ERR, "Invalid syntax for address");
977 ok = 0;
978 } else {
979 if (!parse_source_address(hostname, &address)) {
980 LOG(LOGS_ERR, "Could not get address for hostname");
981 ok = 0;
982 } else {
983 UTI_IPHostToNetwork(&address, &msg->data.del_source.ip_addr);
984 ok = 1;
985 }
986 }
987
988 return ok;
989
990 }
991
992 /* ================================================== */
993
994 static void
give_help(void)995 give_help(void)
996 {
997 int line, len;
998 const char *s, cols[] =
999 "System clock:\0\0"
1000 "tracking\0Display system time information\0"
1001 "makestep\0Correct clock by stepping immediately\0"
1002 "makestep <threshold> <updates>\0Configure automatic clock stepping\0"
1003 "maxupdateskew <skew>\0Modify maximum valid skew to update frequency\0"
1004 "waitsync [<max-tries> [<max-correction> [<max-skew> [<interval>]]]]\0"
1005 "Wait until synchronised in specified limits\0"
1006 "\0\0"
1007 "Time sources:\0\0"
1008 "sources [-a] [-v]\0Display information about current sources\0"
1009 "sourcestats [-a] [-v]\0Display statistics about collected measurements\0"
1010 "selectdata [-a] [-v]\0Display information about source selection\0"
1011 "reselect\0Force reselecting synchronisation source\0"
1012 "reselectdist <dist>\0Modify reselection distance\0"
1013 "\0\0"
1014 "NTP sources:\0\0"
1015 "activity\0Check how many NTP sources are online/offline\0"
1016 "authdata [-a] [-v]\0Display information about authentication\0"
1017 "ntpdata [<address>]\0Display information about last valid measurement\0"
1018 "add server <name> [options]\0Add new NTP server\0"
1019 "add pool <name> [options]\0Add new pool of NTP servers\0"
1020 "add peer <name> [options]\0Add new NTP peer\0"
1021 "delete <address>\0Remove server or peer\0"
1022 "burst <n-good>/<n-max> [[<mask>/]<address>]\0Start rapid set of measurements\0"
1023 "maxdelay <address> <delay>\0Modify maximum valid sample delay\0"
1024 "maxdelayratio <address> <ratio>\0Modify maximum valid delay/minimum ratio\0"
1025 "maxdelaydevratio <address> <ratio>\0Modify maximum valid delay/deviation ratio\0"
1026 "minpoll <address> <poll>\0Modify minimum polling interval\0"
1027 "maxpoll <address> <poll>\0Modify maximum polling interval\0"
1028 "minstratum <address> <stratum>\0Modify minimum stratum\0"
1029 "offline [[<mask>/]<address>]\0Set sources in subnet to offline status\0"
1030 "online [[<mask>/]<address>]\0Set sources in subnet to online status\0"
1031 "onoffline\0Set all sources to online or offline status\0"
1032 "\0according to network configuration\0"
1033 "polltarget <address> <target>\0Modify poll target\0"
1034 "refresh\0Refresh IP addresses\0"
1035 "reload sources\0Re-read *.sources files\0"
1036 "sourcename <address>\0Display original name\0"
1037 "\0\0"
1038 "Manual time input:\0\0"
1039 "manual off|on|reset\0Disable/enable/reset settime command\0"
1040 "manual list\0Show previous settime entries\0"
1041 "manual delete <index>\0Delete previous settime entry\0"
1042 "settime <time>\0Set daemon time\0"
1043 "\0(e.g. Sep 25, 2015 16:30:05 or 16:30:05)\0"
1044 "\0\0NTP access:\0\0"
1045 "accheck <address>\0Check whether address is allowed\0"
1046 "clients [-p <packets>] [-k] [-r]\0Report on clients that accessed the server\0"
1047 "serverstats\0Display statistics of the server\0"
1048 "allow [<subnet>]\0Allow access to subnet as a default\0"
1049 "allow all [<subnet>]\0Allow access to subnet and all children\0"
1050 "deny [<subnet>]\0Deny access to subnet as a default\0"
1051 "deny all [<subnet>]\0Deny access to subnet and all children\0"
1052 "local [options]\0Serve time even when not synchronised\0"
1053 "local off\0Don't serve time when not synchronised\0"
1054 "smoothtime reset|activate\0Reset/activate time smoothing\0"
1055 "smoothing\0Display current time smoothing state\0"
1056 "\0\0"
1057 "Monitoring access:\0\0"
1058 "cmdaccheck <address>\0Check whether address is allowed\0"
1059 "cmdallow [<subnet>]\0Allow access to subnet as a default\0"
1060 "cmdallow all [<subnet>]\0Allow access to subnet and all children\0"
1061 "cmddeny [<subnet>]\0Deny access to subnet as a default\0"
1062 "cmddeny all [<subnet>]\0Deny access to subnet and all children\0"
1063 "\0\0"
1064 "Real-time clock:\0\0"
1065 "rtcdata\0Print current RTC performance parameters\0"
1066 "trimrtc\0Correct RTC relative to system clock\0"
1067 "writertc\0Save RTC performance parameters to file\0"
1068 "\0\0"
1069 "Other daemon commands:\0\0"
1070 "cyclelogs\0Close and re-open log files\0"
1071 "dump\0Dump measurements and NTS keys/cookies\0"
1072 "rekey\0Re-read keys\0"
1073 "reset sources\0Drop all measurements\0"
1074 "shutdown\0Stop daemon\0"
1075 "\0\0"
1076 "Client commands:\0\0"
1077 "dns -n|+n\0Disable/enable resolving IP addresses to hostnames\0"
1078 "dns -4|-6|-46\0Resolve hostnames only to IPv4/IPv6/both addresses\0"
1079 "timeout <milliseconds>\0Set initial response timeout\0"
1080 "retries <retries>\0Set maximum number of retries\0"
1081 "keygen [<id> [<type> [<bits>]]]\0Generate key for key file\0"
1082 "exit|quit\0Leave the program\0"
1083 "help\0Generate this help\0"
1084 "\0";
1085
1086 /* Indent the second column */
1087 for (s = cols, line = 0; s < cols + sizeof (cols); s += len + 1, line++) {
1088 len = strlen(s);
1089 printf(line % 2 == 0 ? (len >= 28 ? "%s\n%28s" : "%-28s%s") : "%s%s\n",
1090 s, "");
1091 }
1092 }
1093
1094 /* ================================================== */
1095 /* Tab-completion when editline is available */
1096
1097 #ifdef FEAT_READLINE
1098
1099 enum {
1100 TAB_COMPLETE_BASE_CMDS,
1101 TAB_COMPLETE_ADD_OPTS,
1102 TAB_COMPLETE_MANUAL_OPTS,
1103 TAB_COMPLETE_RELOAD_OPTS,
1104 TAB_COMPLETE_RESET_OPTS,
1105 TAB_COMPLETE_SOURCES_OPTS,
1106 TAB_COMPLETE_SOURCESTATS_OPTS,
1107 TAB_COMPLETE_AUTHDATA_OPTS,
1108 TAB_COMPLETE_SELECTDATA_OPTS,
1109 TAB_COMPLETE_MAX_INDEX
1110 };
1111
1112 static int tab_complete_index;
1113
1114 static char *
command_name_generator(const char * text,int state)1115 command_name_generator(const char *text, int state)
1116 {
1117 const char *name, **names[TAB_COMPLETE_MAX_INDEX];
1118 const char *base_commands[] = {
1119 "accheck", "activity", "add", "allow", "authdata", "burst",
1120 "clients", "cmdaccheck", "cmdallow", "cmddeny", "cyclelogs", "delete",
1121 "deny", "dns", "dump", "exit", "help", "keygen", "local", "makestep",
1122 "manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
1123 "maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline",
1124 "polltarget", "quit", "refresh", "rekey", "reload", "reselect", "reselectdist", "reset",
1125 "retries", "rtcdata", "selectdata", "serverstats", "settime", "shutdown", "smoothing",
1126 "smoothtime", "sourcename", "sources", "sourcestats",
1127 "timeout", "tracking", "trimrtc", "waitsync", "writertc",
1128 NULL
1129 };
1130 const char *add_options[] = { "peer", "pool", "server", NULL };
1131 const char *manual_options[] = { "on", "off", "delete", "list", "reset", NULL };
1132 const char *reset_options[] = { "sources", NULL };
1133 const char *reload_options[] = { "sources", NULL };
1134 const char *common_source_options[] = { "-a", "-v", NULL };
1135 static int list_index, len;
1136
1137 names[TAB_COMPLETE_BASE_CMDS] = base_commands;
1138 names[TAB_COMPLETE_ADD_OPTS] = add_options;
1139 names[TAB_COMPLETE_MANUAL_OPTS] = manual_options;
1140 names[TAB_COMPLETE_RELOAD_OPTS] = reload_options;
1141 names[TAB_COMPLETE_RESET_OPTS] = reset_options;
1142 names[TAB_COMPLETE_AUTHDATA_OPTS] = common_source_options;
1143 names[TAB_COMPLETE_SELECTDATA_OPTS] = common_source_options;
1144 names[TAB_COMPLETE_SOURCES_OPTS] = common_source_options;
1145 names[TAB_COMPLETE_SOURCESTATS_OPTS] = common_source_options;
1146
1147 if (!state) {
1148 list_index = 0;
1149 len = strlen(text);
1150 }
1151
1152 while ((name = names[tab_complete_index][list_index++])) {
1153 if (strncmp(name, text, len) == 0) {
1154 return strdup(name);
1155 }
1156 }
1157
1158 return NULL;
1159 }
1160
1161 /* ================================================== */
1162
1163 static char **
command_name_completion(const char * text,int start,int end)1164 command_name_completion(const char *text, int start, int end)
1165 {
1166 char first[32];
1167
1168 snprintf(first, MIN(sizeof (first), start + 1), "%s", rl_line_buffer);
1169 rl_attempted_completion_over = 1;
1170
1171 if (!strcmp(first, "add ")) {
1172 tab_complete_index = TAB_COMPLETE_ADD_OPTS;
1173 } else if (!strcmp(first, "authdata ")) {
1174 tab_complete_index = TAB_COMPLETE_AUTHDATA_OPTS;
1175 } else if (!strcmp(first, "manual ")) {
1176 tab_complete_index = TAB_COMPLETE_MANUAL_OPTS;
1177 } else if (!strcmp(first, "reload ")) {
1178 tab_complete_index = TAB_COMPLETE_RELOAD_OPTS;
1179 } else if (!strcmp(first, "reset ")) {
1180 tab_complete_index = TAB_COMPLETE_RESET_OPTS;
1181 } else if (!strcmp(first, "selectdata ")) {
1182 tab_complete_index = TAB_COMPLETE_SELECTDATA_OPTS;
1183 } else if (!strcmp(first, "sources ")) {
1184 tab_complete_index = TAB_COMPLETE_SOURCES_OPTS;
1185 } else if (!strcmp(first, "sourcestats ")) {
1186 tab_complete_index = TAB_COMPLETE_SOURCESTATS_OPTS;
1187 } else if (first[0] == '\0') {
1188 tab_complete_index = TAB_COMPLETE_BASE_CMDS;
1189 } else {
1190 return NULL;
1191 }
1192
1193 return rl_completion_matches(text, command_name_generator);
1194 }
1195 #endif
1196
1197 /* ================================================== */
1198
1199 static int max_retries = 2;
1200 static int initial_timeout = 1000;
1201 static int proto_version = PROTO_VERSION_NUMBER;
1202
1203 /* This is the core protocol module. Complete particular fields in
1204 the outgoing packet, send it, wait for a response, handle retries,
1205 etc. Returns a Boolean indicating whether the protocol was
1206 successful or not.*/
1207
1208 static int
submit_request(CMD_Request * request,CMD_Reply * reply)1209 submit_request(CMD_Request *request, CMD_Reply *reply)
1210 {
1211 int select_status;
1212 int recv_status;
1213 int read_length;
1214 int command_length;
1215 int padding_length;
1216 struct timespec ts_now, ts_start;
1217 struct timeval tv;
1218 int n_attempts, new_attempt;
1219 double timeout;
1220 fd_set rdfd;
1221
1222 request->pkt_type = PKT_TYPE_CMD_REQUEST;
1223 request->res1 = 0;
1224 request->res2 = 0;
1225 request->pad1 = 0;
1226 request->pad2 = 0;
1227
1228 n_attempts = 0;
1229 new_attempt = 1;
1230
1231 do {
1232 if (gettimeofday(&tv, NULL))
1233 return 0;
1234
1235 if (new_attempt) {
1236 new_attempt = 0;
1237
1238 if (n_attempts > max_retries)
1239 return 0;
1240
1241 UTI_TimevalToTimespec(&tv, &ts_start);
1242
1243 UTI_GetRandomBytes(&request->sequence, sizeof (request->sequence));
1244 request->attempt = htons(n_attempts);
1245 request->version = proto_version;
1246 command_length = PKL_CommandLength(request);
1247 padding_length = PKL_CommandPaddingLength(request);
1248 assert(command_length > 0 && command_length > padding_length);
1249
1250 n_attempts++;
1251
1252 /* Zero the padding to not send any uninitialized data */
1253 memset(((char *)request) + command_length - padding_length, 0, padding_length);
1254
1255 if (sock_fd < 0) {
1256 DEBUG_LOG("No socket to send request");
1257 return 0;
1258 }
1259
1260 if (SCK_Send(sock_fd, (void *)request, command_length, 0) < 0)
1261 return 0;
1262 }
1263
1264 UTI_TimevalToTimespec(&tv, &ts_now);
1265
1266 /* Check if the clock wasn't stepped back */
1267 if (UTI_CompareTimespecs(&ts_now, &ts_start) < 0)
1268 ts_start = ts_now;
1269
1270 timeout = initial_timeout / 1000.0 * (1U << (n_attempts - 1)) -
1271 UTI_DiffTimespecsToDouble(&ts_now, &ts_start);
1272 DEBUG_LOG("Timeout %f seconds", timeout);
1273
1274 /* Avoid calling select() with an invalid timeout */
1275 if (timeout <= 0.0) {
1276 new_attempt = 1;
1277 continue;
1278 }
1279
1280 UTI_DoubleToTimeval(timeout, &tv);
1281
1282 FD_ZERO(&rdfd);
1283 FD_SET(sock_fd, &rdfd);
1284
1285 if (quit)
1286 return 0;
1287
1288 select_status = select(sock_fd + 1, &rdfd, NULL, NULL, &tv);
1289
1290 if (select_status < 0) {
1291 DEBUG_LOG("select failed : %s", strerror(errno));
1292 return 0;
1293 } else if (select_status == 0) {
1294 /* Timeout must have elapsed, try a resend? */
1295 new_attempt = 1;
1296 } else {
1297 recv_status = SCK_Receive(sock_fd, reply, sizeof (*reply), 0);
1298
1299 if (recv_status < 0) {
1300 new_attempt = 1;
1301 } else {
1302 read_length = recv_status;
1303
1304 /* Check if the header is valid */
1305 if (read_length < offsetof(CMD_Reply, data) ||
1306 (reply->version != proto_version &&
1307 !(reply->version >= PROTO_VERSION_MISMATCH_COMPAT_CLIENT &&
1308 ntohs(reply->status) == STT_BADPKTVERSION)) ||
1309 reply->pkt_type != PKT_TYPE_CMD_REPLY ||
1310 reply->res1 != 0 ||
1311 reply->res2 != 0 ||
1312 reply->command != request->command ||
1313 reply->sequence != request->sequence) {
1314 DEBUG_LOG("Invalid reply");
1315 continue;
1316 }
1317
1318 #if PROTO_VERSION_NUMBER == 6
1319 /* Protocol version 5 is similar to 6 except there is no padding.
1320 If a version 5 reply with STT_BADPKTVERSION is received,
1321 switch our version and try again. */
1322 if (proto_version == PROTO_VERSION_NUMBER &&
1323 reply->version == PROTO_VERSION_NUMBER - 1) {
1324 proto_version = PROTO_VERSION_NUMBER - 1;
1325 n_attempts--;
1326 new_attempt = 1;
1327 continue;
1328 }
1329 #else
1330 #error unknown compatibility with PROTO_VERSION - 1
1331 #endif
1332
1333 /* Check that the packet contains all data it is supposed to have.
1334 Unknown responses will always pass this test as their expected
1335 length is zero. */
1336 if (read_length < PKL_ReplyLength(reply)) {
1337 DEBUG_LOG("Reply too short");
1338 new_attempt = 1;
1339 continue;
1340 }
1341
1342 /* Good packet received, print out results */
1343 DEBUG_LOG("Reply cmd=%d reply=%d stat=%d",
1344 ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status));
1345 break;
1346 }
1347 }
1348 } while (1);
1349
1350 return 1;
1351 }
1352
1353 /* ================================================== */
1354
1355 static int
request_reply(CMD_Request * request,CMD_Reply * reply,int requested_reply,int verbose)1356 request_reply(CMD_Request *request, CMD_Reply *reply, int requested_reply, int verbose)
1357 {
1358 int status;
1359
1360 while (!submit_request(request, reply)) {
1361 /* Try connecting to other addresses before giving up */
1362 if (open_io())
1363 continue;
1364 printf("506 Cannot talk to daemon\n");
1365 return 0;
1366 }
1367
1368 status = ntohs(reply->status);
1369
1370 if (verbose || status != STT_SUCCESS) {
1371 switch (status) {
1372 case STT_SUCCESS:
1373 printf("200 OK");
1374 break;
1375 case STT_ACCESSALLOWED:
1376 printf("208 Access allowed");
1377 break;
1378 case STT_ACCESSDENIED:
1379 printf("209 Access denied");
1380 break;
1381 case STT_FAILED:
1382 printf("500 Failure");
1383 break;
1384 case STT_UNAUTH:
1385 printf("501 Not authorised");
1386 break;
1387 case STT_INVALID:
1388 printf("502 Invalid command");
1389 break;
1390 case STT_NOSUCHSOURCE:
1391 printf("503 No such source");
1392 break;
1393 case STT_INVALIDTS:
1394 printf("504 Duplicate or stale logon detected");
1395 break;
1396 case STT_NOTENABLED:
1397 printf("505 Facility not enabled in daemon");
1398 break;
1399 case STT_BADSUBNET:
1400 printf("507 Bad subnet");
1401 break;
1402 case STT_NOHOSTACCESS:
1403 printf("510 No command access from this host");
1404 break;
1405 case STT_SOURCEALREADYKNOWN:
1406 printf("511 Source already present");
1407 break;
1408 case STT_TOOMANYSOURCES:
1409 printf("512 Too many sources present");
1410 break;
1411 case STT_NORTC:
1412 printf("513 RTC driver not running");
1413 break;
1414 case STT_BADRTCFILE:
1415 printf("514 Can't write RTC parameters");
1416 break;
1417 case STT_INVALIDAF:
1418 printf("515 Invalid address family");
1419 break;
1420 case STT_BADSAMPLE:
1421 printf("516 Sample index out of range");
1422 break;
1423 case STT_BADPKTVERSION:
1424 printf("517 Protocol version mismatch");
1425 break;
1426 case STT_BADPKTLENGTH:
1427 printf("518 Packet length mismatch");
1428 break;
1429 case STT_INACTIVE:
1430 printf("519 Client logging is not active in the daemon");
1431 break;
1432 case STT_INVALIDNAME:
1433 printf("521 Invalid name");
1434 break;
1435 default:
1436 printf("520 Got unexpected error from daemon");
1437 }
1438 printf("\n");
1439 }
1440
1441 if (status != STT_SUCCESS &&
1442 status != STT_ACCESSALLOWED && status != STT_ACCESSDENIED) {
1443 return 0;
1444 }
1445
1446 if (ntohs(reply->reply) != requested_reply) {
1447 printf("508 Bad reply from daemon\n");
1448 return 0;
1449 }
1450
1451 /* Make sure an unknown response was not requested */
1452 assert(PKL_ReplyLength(reply));
1453
1454 return 1;
1455 }
1456
1457 /* ================================================== */
1458
1459 static void
print_seconds(unsigned long s)1460 print_seconds(unsigned long s)
1461 {
1462 unsigned long d;
1463
1464 if (s == (uint32_t)-1) {
1465 printf(" -");
1466 } else if (s < 1200) {
1467 printf("%4lu", s);
1468 } else if (s < 36000) {
1469 printf("%3lum", s / 60);
1470 } else if (s < 345600) {
1471 printf("%3luh", s / 3600);
1472 } else {
1473 d = s / 86400;
1474 if (d > 999) {
1475 printf("%3luy", d / 365);
1476 } else {
1477 printf("%3lud", d);
1478 }
1479 }
1480 }
1481
1482 /* ================================================== */
1483
1484 static void
print_nanoseconds(double s)1485 print_nanoseconds(double s)
1486 {
1487 s = fabs(s);
1488
1489 if (s < 9999.5e-9) {
1490 printf("%4.0fns", s * 1e9);
1491 } else if (s < 9999.5e-6) {
1492 printf("%4.0fus", s * 1e6);
1493 } else if (s < 9999.5e-3) {
1494 printf("%4.0fms", s * 1e3);
1495 } else if (s < 999.5) {
1496 printf("%5.1fs", s);
1497 } else if (s < 99999.5) {
1498 printf("%5.0fs", s);
1499 } else if (s < 99999.5 * 60) {
1500 printf("%5.0fm", s / 60);
1501 } else if (s < 99999.5 * 3600) {
1502 printf("%5.0fh", s / 3600);
1503 } else if (s < 99999.5 * 3600 * 24) {
1504 printf("%5.0fd", s / (3600 * 24));
1505 } else {
1506 printf("%5.0fy", s / (3600 * 24 * 365));
1507 }
1508 }
1509
1510 /* ================================================== */
1511
1512 static void
print_signed_nanoseconds(double s)1513 print_signed_nanoseconds(double s)
1514 {
1515 double x;
1516
1517 x = fabs(s);
1518
1519 if (x < 9999.5e-9) {
1520 printf("%+5.0fns", s * 1e9);
1521 } else if (x < 9999.5e-6) {
1522 printf("%+5.0fus", s * 1e6);
1523 } else if (x < 9999.5e-3) {
1524 printf("%+5.0fms", s * 1e3);
1525 } else if (x < 999.5) {
1526 printf("%+6.1fs", s);
1527 } else if (x < 99999.5) {
1528 printf("%+6.0fs", s);
1529 } else if (x < 99999.5 * 60) {
1530 printf("%+6.0fm", s / 60);
1531 } else if (x < 99999.5 * 3600) {
1532 printf("%+6.0fh", s / 3600);
1533 } else if (x < 99999.5 * 3600 * 24) {
1534 printf("%+6.0fd", s / (3600 * 24));
1535 } else {
1536 printf("%+6.0fy", s / (3600 * 24 * 365));
1537 }
1538 }
1539
1540 /* ================================================== */
1541
1542 static void
print_freq_ppm(double f)1543 print_freq_ppm(double f)
1544 {
1545 if (fabs(f) < 99999.5) {
1546 printf("%10.3f", f);
1547 } else {
1548 printf("%10.0f", f);
1549 }
1550 }
1551
1552 /* ================================================== */
1553
1554 static void
print_signed_freq_ppm(double f)1555 print_signed_freq_ppm(double f)
1556 {
1557 if (fabs(f) < 99999.5) {
1558 printf("%+10.3f", f);
1559 } else {
1560 printf("%+10.0f", f);
1561 }
1562 }
1563
1564 /* ================================================== */
1565
1566 static void
print_clientlog_interval(int rate)1567 print_clientlog_interval(int rate)
1568 {
1569 if (rate >= 127) {
1570 printf(" -");
1571 } else {
1572 printf("%2d", rate);
1573 }
1574 }
1575
1576 /* ================================================== */
1577
1578 static void
print_header(const char * header)1579 print_header(const char *header)
1580 {
1581 int len;
1582
1583 if (csv_mode)
1584 return;
1585
1586 printf("%s\n", header);
1587
1588 len = strlen(header);
1589 while (len--)
1590 printf("=");
1591 printf("\n");
1592 }
1593
1594 /* ================================================== */
1595
1596 #define REPORT_END 0x1234
1597
1598 /* Print a report. The syntax of the format is similar to printf(), but not all
1599 specifiers are supported and some are different! */
1600
1601 static void
print_report(const char * format,...)1602 print_report(const char *format, ...)
1603 {
1604 char buf[256];
1605 va_list ap;
1606 int i, field, sign, width, prec, spec;
1607 const char *string;
1608 unsigned long long_uinteger;
1609 unsigned int uinteger;
1610 int integer;
1611 struct timespec *ts;
1612 struct tm *tm;
1613 double dbl;
1614
1615 va_start(ap, format);
1616
1617 for (field = 0; ; field++) {
1618 /* Search for text between format specifiers and print it
1619 if not in the CSV mode */
1620 for (i = 0; i < sizeof (buf) && format[i] != '%' && format[i] != '\0'; i++)
1621 buf[i] = format[i];
1622
1623 if (i >= sizeof (buf))
1624 break;
1625
1626 buf[i] = '\0';
1627
1628 if (!csv_mode)
1629 printf("%s", buf);
1630
1631 if (format[i] == '\0' || format[i + 1] == '\0')
1632 break;
1633
1634 format += i + 1;
1635
1636 sign = 0;
1637 width = 0;
1638 prec = 5;
1639
1640 if (*format == '+' || *format == '-') {
1641 sign = 1;
1642 format++;
1643 }
1644
1645 if (isdigit((unsigned char)*format)) {
1646 width = atoi(format);
1647 while (isdigit((unsigned char)*format))
1648 format++;
1649 }
1650
1651 if (*format == '.') {
1652 format++;
1653 prec = atoi(format);
1654 while (isdigit((unsigned char)*format))
1655 format++;
1656 }
1657
1658 spec = *format;
1659 format++;
1660
1661 /* Disable human-readable formatting in the CSV mode */
1662 if (csv_mode) {
1663 sign = width = 0;
1664
1665 if (field > 0)
1666 printf(",");
1667
1668 switch (spec) {
1669 case 'C':
1670 spec = 'd';
1671 break;
1672 case 'F':
1673 case 'P':
1674 prec = 3;
1675 spec = 'f';
1676 break;
1677 case 'O':
1678 case 'S':
1679 prec = 9;
1680 spec = 'f';
1681 break;
1682 case 'I':
1683 spec = 'U';
1684 break;
1685 case 'T':
1686 spec = 'V';
1687 break;
1688 }
1689 }
1690
1691 switch (spec) {
1692 case 'B': /* boolean */
1693 integer = va_arg(ap, int);
1694 printf("%s", integer ? "Yes" : "No");
1695 break;
1696 case 'C': /* clientlog interval */
1697 integer = va_arg(ap, int);
1698 print_clientlog_interval(integer);
1699 break;
1700 case 'F': /* absolute frequency in ppm with fast/slow keyword */
1701 case 'O': /* absolute offset in seconds with fast/slow keyword */
1702 dbl = va_arg(ap, double);
1703 printf("%*.*f %s %s", width, prec, fabs(dbl),
1704 spec == 'O' ? "seconds" : "ppm",
1705 (dbl > 0.0) ^ (spec != 'O') ? "slow" : "fast");
1706 break;
1707 case 'I': /* interval with unit */
1708 long_uinteger = va_arg(ap, unsigned long);
1709 print_seconds(long_uinteger);
1710 break;
1711 case 'L': /* leap status */
1712 integer = va_arg(ap, int);
1713 switch (integer) {
1714 case LEAP_Normal:
1715 string = width != 1 ? "Normal" : "N";
1716 break;
1717 case LEAP_InsertSecond:
1718 string = width != 1 ? "Insert second" : "+";
1719 break;
1720 case LEAP_DeleteSecond:
1721 string = width != 1 ? "Delete second" : "-";
1722 break;
1723 case LEAP_Unsynchronised:
1724 string = width != 1 ? "Not synchronised" : "?";
1725 break;
1726 default:
1727 string = width != 1 ? "Invalid" : "?";
1728 break;
1729 }
1730 printf("%s", string);
1731 break;
1732 case 'M': /* NTP mode */
1733 integer = va_arg(ap, int);
1734 switch (integer) {
1735 case MODE_ACTIVE:
1736 string = "Symmetric active";
1737 break;
1738 case MODE_PASSIVE:
1739 string = "Symmetric passive";
1740 break;
1741 case MODE_SERVER:
1742 string = "Server";
1743 break;
1744 default:
1745 string = "Invalid";
1746 break;
1747 }
1748 printf("%s", string);
1749 break;
1750 case 'N': /* Timestamp source */
1751 integer = va_arg(ap, int);
1752 switch (integer) {
1753 case 'D':
1754 string = "Daemon";
1755 break;
1756 case 'K':
1757 string = "Kernel";
1758 break;
1759 case 'H':
1760 string = "Hardware";
1761 break;
1762 default:
1763 string = "Invalid";
1764 break;
1765 }
1766 printf("%s", string);
1767 break;
1768 case 'P': /* frequency in ppm */
1769 dbl = va_arg(ap, double);
1770 if (sign)
1771 print_signed_freq_ppm(dbl);
1772 else
1773 print_freq_ppm(dbl);
1774 break;
1775 case 'R': /* reference ID in hexdecimal */
1776 long_uinteger = va_arg(ap, unsigned long);
1777 printf("%08lX", long_uinteger);
1778 break;
1779 case 'S': /* offset with unit */
1780 dbl = va_arg(ap, double);
1781 if (sign)
1782 print_signed_nanoseconds(dbl);
1783 else
1784 print_nanoseconds(dbl);
1785 break;
1786 case 'T': /* timespec as date and time in UTC */
1787 ts = va_arg(ap, struct timespec *);
1788 tm = gmtime(&ts->tv_sec);
1789 if (!tm)
1790 break;
1791 strftime(buf, sizeof (buf), "%a %b %d %T %Y", tm);
1792 printf("%s", buf);
1793 break;
1794 case 'U': /* unsigned long in decimal */
1795 long_uinteger = va_arg(ap, unsigned long);
1796 printf("%*lu", width, long_uinteger);
1797 break;
1798 case 'V': /* timespec as seconds since epoch */
1799 ts = va_arg(ap, struct timespec *);
1800 printf("%s", UTI_TimespecToString(ts));
1801 break;
1802 case 'b': /* unsigned int in binary */
1803 uinteger = va_arg(ap, unsigned int);
1804 for (i = prec - 1; i >= 0; i--)
1805 printf("%c", uinteger & 1U << i ? '1' : '0');
1806 break;
1807
1808 /* Classic printf specifiers */
1809 case 'c': /* character */
1810 integer = va_arg(ap, int);
1811 printf("%c", integer);
1812 break;
1813 case 'd': /* signed int in decimal */
1814 integer = va_arg(ap, int);
1815 printf("%*d", width, integer);
1816 break;
1817 case 'f': /* double */
1818 dbl = va_arg(ap, double);
1819 printf(sign ? "%+*.*f" : "%*.*f", width, prec, dbl);
1820 break;
1821 case 'o': /* unsigned int in octal */
1822 uinteger = va_arg(ap, unsigned int);
1823 printf("%*o", width, uinteger);
1824 break;
1825 case 's': /* string */
1826 string = va_arg(ap, const char *);
1827 if (sign)
1828 printf("%-*s", width, string);
1829 else
1830 printf("%*s", width, string);
1831 break;
1832 case 'u': /* unsigned int in decimal */
1833 uinteger = va_arg(ap, unsigned int);
1834 printf("%*u", width, uinteger);
1835 break;
1836 }
1837 }
1838
1839 /* Require terminating argument to catch bad type conversions */
1840 if (va_arg(ap, int) != REPORT_END)
1841 assert(0);
1842
1843 va_end(ap);
1844
1845 if (csv_mode)
1846 printf("\n");
1847 }
1848
1849 /* ================================================== */
1850
1851 static void
print_info_field(const char * format,...)1852 print_info_field(const char *format, ...)
1853 {
1854 va_list ap;
1855
1856 if (csv_mode)
1857 return;
1858
1859 va_start(ap, format);
1860 vprintf(format, ap);
1861 va_end(ap);
1862 }
1863
1864 /* ================================================== */
1865
1866 static int
get_source_name(IPAddr * ip_addr,char * buf,int size)1867 get_source_name(IPAddr *ip_addr, char *buf, int size)
1868 {
1869 CMD_Request request;
1870 CMD_Reply reply;
1871 int i;
1872
1873 request.command = htons(REQ_NTP_SOURCE_NAME);
1874 UTI_IPHostToNetwork(ip_addr, &request.data.ntp_source_name.ip_addr);
1875 if (!request_reply(&request, &reply, RPY_NTP_SOURCE_NAME, 0) ||
1876 reply.data.ntp_source_name.name[sizeof (reply.data.ntp_source_name.name) - 1] != '\0' ||
1877 snprintf(buf, size, "%s", (char *)reply.data.ntp_source_name.name) >= size)
1878 return 0;
1879
1880 /* Make sure the name is printable */
1881 for (i = 0; i < size && buf[i] != '\0'; i++) {
1882 if (!isgraph((unsigned char)buf[i]))
1883 return 0;
1884 }
1885
1886 return 1;
1887 }
1888
1889 /* ================================================== */
1890
1891 static void
format_name(char * buf,int size,int trunc_dns,int ref,uint32_t ref_id,int source,IPAddr * ip_addr)1892 format_name(char *buf, int size, int trunc_dns, int ref, uint32_t ref_id,
1893 int source, IPAddr *ip_addr)
1894 {
1895 if (ref) {
1896 snprintf(buf, size, "%s", UTI_RefidToString(ref_id));
1897 } else if (source && source_names) {
1898 if (!get_source_name(ip_addr, buf, size))
1899 snprintf(buf, size, "?");
1900 } else if (no_dns || csv_mode) {
1901 snprintf(buf, size, "%s", UTI_IPToString(ip_addr));
1902 } else {
1903 DNS_IPAddress2Name(ip_addr, buf, size);
1904 if (trunc_dns > 0 && strlen(buf) > trunc_dns) {
1905 buf[trunc_dns - 1] = '>';
1906 buf[trunc_dns] = '\0';
1907 }
1908 }
1909 }
1910
1911 /* ================================================== */
1912
1913 static void
parse_sources_options(char * line,int * all,int * verbose)1914 parse_sources_options(char *line, int *all, int *verbose)
1915 {
1916 char *opt;
1917
1918 *all = *verbose = 0;
1919
1920 while (*line) {
1921 opt = line;
1922 line = CPS_SplitWord(line);
1923 if (!strcmp(opt, "-a"))
1924 *all = 1;
1925 else if (!strcmp(opt, "-v"))
1926 *verbose = !csv_mode;
1927 }
1928 }
1929
1930 /* ================================================== */
1931
1932 static int
process_cmd_sourcename(char * line)1933 process_cmd_sourcename(char *line)
1934 {
1935 IPAddr ip_addr;
1936 char name[256];
1937
1938 if (!parse_source_address(line, &ip_addr)) {
1939 LOG(LOGS_ERR, "Could not read address");
1940 return 0;
1941 }
1942
1943 if (!get_source_name(&ip_addr, name, sizeof (name)))
1944 return 0;
1945
1946 print_report("%s\n", name, REPORT_END);
1947
1948 return 1;
1949 }
1950
1951 /* ================================================== */
1952
1953 static int
process_cmd_sources(char * line)1954 process_cmd_sources(char *line)
1955 {
1956 CMD_Request request;
1957 CMD_Reply reply;
1958 IPAddr ip_addr;
1959 uint32_t i, mode, n_sources;
1960 char name[256], mode_ch, state_ch;
1961 int all, verbose;
1962
1963 parse_sources_options(line, &all, &verbose);
1964
1965 request.command = htons(REQ_N_SOURCES);
1966 if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
1967 return 0;
1968
1969 n_sources = ntohl(reply.data.n_sources.n_sources);
1970
1971 if (verbose) {
1972 printf("\n");
1973 printf(" .-- Source mode '^' = server, '=' = peer, '#' = local clock.\n");
1974 printf(" / .- Source state '*' = current best, '+' = combined, '-' = not combined,\n");
1975 printf("| / 'x' = may be in error, '~' = too variable, '?' = unusable.\n");
1976 printf("|| .- xxxx [ yyyy ] +/- zzzz\n");
1977 printf("|| Reachability register (octal) -. | xxxx = adjusted offset,\n");
1978 printf("|| Log2(Polling interval) --. | | yyyy = measured offset,\n");
1979 printf("|| \\ | | zzzz = estimated error.\n");
1980 printf("|| | | \\\n");
1981 }
1982
1983 print_header("MS Name/IP address Stratum Poll Reach LastRx Last sample ");
1984
1985 /* "MS NNNNNNNNNNNNNNNNNNNNNNNNNNN SS PP RRR RRRR SSSSSSS[SSSSSSS] +/- SSSSSS" */
1986
1987 for (i = 0; i < n_sources; i++) {
1988 request.command = htons(REQ_SOURCE_DATA);
1989 request.data.source_data.index = htonl(i);
1990 if (!request_reply(&request, &reply, RPY_SOURCE_DATA, 0))
1991 return 0;
1992
1993 mode = ntohs(reply.data.source_data.mode);
1994 UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &ip_addr);
1995 if (!all && ip_addr.family == IPADDR_ID)
1996 continue;
1997
1998 format_name(name, sizeof (name), 25,
1999 mode == RPY_SD_MD_REF && ip_addr.family == IPADDR_INET4,
2000 ip_addr.addr.in4, 1, &ip_addr);
2001
2002 switch (mode) {
2003 case RPY_SD_MD_CLIENT:
2004 mode_ch = '^';
2005 break;
2006 case RPY_SD_MD_PEER:
2007 mode_ch = '=';
2008 break;
2009 case RPY_SD_MD_REF:
2010 mode_ch = '#';
2011 break;
2012 default:
2013 mode_ch = ' ';
2014 }
2015
2016 switch (ntohs(reply.data.source_data.state)) {
2017 case RPY_SD_ST_SELECTED:
2018 state_ch = '*';
2019 break;
2020 case RPY_SD_ST_NONSELECTABLE:
2021 state_ch = '?';
2022 break;
2023 case RPY_SD_ST_FALSETICKER:
2024 state_ch = 'x';
2025 break;
2026 case RPY_SD_ST_JITTERY:
2027 state_ch = '~';
2028 break;
2029 case RPY_SD_ST_UNSELECTED:
2030 state_ch = '+';
2031 break;
2032 case RPY_SD_ST_SELECTABLE:
2033 state_ch = '-';
2034 break;
2035 default:
2036 state_ch = ' ';
2037 }
2038
2039 switch (ntohs(reply.data.source_data.flags)) {
2040 default:
2041 break;
2042 }
2043
2044 print_report("%c%c %-27s %2d %2d %3o %I %+S[%+S] +/- %S\n",
2045 mode_ch, state_ch, name,
2046 ntohs(reply.data.source_data.stratum),
2047 (int16_t)ntohs(reply.data.source_data.poll),
2048 ntohs(reply.data.source_data.reachability),
2049 (unsigned long)ntohl(reply.data.source_data.since_sample),
2050 UTI_FloatNetworkToHost(reply.data.source_data.latest_meas),
2051 UTI_FloatNetworkToHost(reply.data.source_data.orig_latest_meas),
2052 UTI_FloatNetworkToHost(reply.data.source_data.latest_meas_err),
2053 REPORT_END);
2054 }
2055
2056 return 1;
2057 }
2058
2059 /* ================================================== */
2060
2061 static int
process_cmd_sourcestats(char * line)2062 process_cmd_sourcestats(char *line)
2063 {
2064 CMD_Request request;
2065 CMD_Reply reply;
2066 uint32_t i, n_sources;
2067 int all, verbose;
2068 char name[256];
2069 IPAddr ip_addr;
2070
2071 parse_sources_options(line, &all, &verbose);
2072
2073 request.command = htons(REQ_N_SOURCES);
2074 if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
2075 return 0;
2076
2077 n_sources = ntohl(reply.data.n_sources.n_sources);
2078
2079 if (verbose) {
2080 printf(" .- Number of sample points in measurement set.\n");
2081 printf(" / .- Number of residual runs with same sign.\n");
2082 printf(" | / .- Length of measurement set (time).\n");
2083 printf(" | | / .- Est. clock freq error (ppm).\n");
2084 printf(" | | | / .- Est. error in freq.\n");
2085 printf(" | | | | / .- Est. offset.\n");
2086 printf(" | | | | | | On the -.\n");
2087 printf(" | | | | | | samples. \\\n");
2088 printf(" | | | | | | |\n");
2089 }
2090
2091 print_header("Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev");
2092
2093 /* "NNNNNNNNNNNNNNNNNNNNNNNNN NP NR SSSS FFFFFFFFFF SSSSSSSSSS SSSSSSS SSSSSS" */
2094
2095 for (i = 0; i < n_sources; i++) {
2096 request.command = htons(REQ_SOURCESTATS);
2097 request.data.source_data.index = htonl(i);
2098 if (!request_reply(&request, &reply, RPY_SOURCESTATS, 0))
2099 return 0;
2100
2101 UTI_IPNetworkToHost(&reply.data.sourcestats.ip_addr, &ip_addr);
2102 if (!all && ip_addr.family == IPADDR_ID)
2103 continue;
2104
2105 format_name(name, sizeof (name), 25, ip_addr.family == IPADDR_UNSPEC,
2106 ntohl(reply.data.sourcestats.ref_id), 1, &ip_addr);
2107
2108 print_report("%-25s %3U %3U %I %+P %P %+S %S\n",
2109 name,
2110 (unsigned long)ntohl(reply.data.sourcestats.n_samples),
2111 (unsigned long)ntohl(reply.data.sourcestats.n_runs),
2112 (unsigned long)ntohl(reply.data.sourcestats.span_seconds),
2113 UTI_FloatNetworkToHost(reply.data.sourcestats.resid_freq_ppm),
2114 UTI_FloatNetworkToHost(reply.data.sourcestats.skew_ppm),
2115 UTI_FloatNetworkToHost(reply.data.sourcestats.est_offset),
2116 UTI_FloatNetworkToHost(reply.data.sourcestats.sd),
2117 REPORT_END);
2118 }
2119
2120 return 1;
2121 }
2122
2123 /* ================================================== */
2124
2125 static int
process_cmd_tracking(char * line)2126 process_cmd_tracking(char *line)
2127 {
2128 CMD_Request request;
2129 CMD_Reply reply;
2130 IPAddr ip_addr;
2131 uint32_t ref_id;
2132 char name[256];
2133 struct timespec ref_time;
2134
2135 request.command = htons(REQ_TRACKING);
2136 if (!request_reply(&request, &reply, RPY_TRACKING, 0))
2137 return 0;
2138
2139 ref_id = ntohl(reply.data.tracking.ref_id);
2140
2141 UTI_IPNetworkToHost(&reply.data.tracking.ip_addr, &ip_addr);
2142 format_name(name, sizeof (name), sizeof (name),
2143 ip_addr.family == IPADDR_UNSPEC, ref_id, 1, &ip_addr);
2144
2145 UTI_TimespecNetworkToHost(&reply.data.tracking.ref_time, &ref_time);
2146
2147 print_report("Reference ID : %R (%s)\n"
2148 "Stratum : %u\n"
2149 "Ref time (UTC) : %T\n"
2150 "System time : %.9O of NTP time\n"
2151 "Last offset : %+.9f seconds\n"
2152 "RMS offset : %.9f seconds\n"
2153 "Frequency : %.3F\n"
2154 "Residual freq : %+.3f ppm\n"
2155 "Skew : %.3f ppm\n"
2156 "Root delay : %.9f seconds\n"
2157 "Root dispersion : %.9f seconds\n"
2158 "Update interval : %.1f seconds\n"
2159 "Leap status : %L\n",
2160 (unsigned long)ref_id, name,
2161 ntohs(reply.data.tracking.stratum),
2162 &ref_time,
2163 UTI_FloatNetworkToHost(reply.data.tracking.current_correction),
2164 UTI_FloatNetworkToHost(reply.data.tracking.last_offset),
2165 UTI_FloatNetworkToHost(reply.data.tracking.rms_offset),
2166 UTI_FloatNetworkToHost(reply.data.tracking.freq_ppm),
2167 UTI_FloatNetworkToHost(reply.data.tracking.resid_freq_ppm),
2168 UTI_FloatNetworkToHost(reply.data.tracking.skew_ppm),
2169 UTI_FloatNetworkToHost(reply.data.tracking.root_delay),
2170 UTI_FloatNetworkToHost(reply.data.tracking.root_dispersion),
2171 UTI_FloatNetworkToHost(reply.data.tracking.last_update_interval),
2172 ntohs(reply.data.tracking.leap_status), REPORT_END);
2173
2174 return 1;
2175 }
2176
2177 /* ================================================== */
2178
2179 static int
process_cmd_authdata(char * line)2180 process_cmd_authdata(char *line)
2181 {
2182 CMD_Request request;
2183 CMD_Reply reply;
2184 IPAddr ip_addr;
2185 uint32_t i, source_mode, n_sources;
2186 int all, verbose;
2187 const char *mode_str;
2188 char name[256];
2189
2190 parse_sources_options(line, &all, &verbose);
2191
2192 request.command = htons(REQ_N_SOURCES);
2193 if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
2194 return 0;
2195
2196 n_sources = ntohl(reply.data.n_sources.n_sources);
2197
2198 if (verbose) {
2199 printf( " .- Auth. mechanism (NTS, SK - symmetric key)\n");
2200 printf( " | Key length -. Cookie length (bytes) -.\n");
2201 printf( " | (bits) | Num. of cookies --. |\n");
2202 printf( " | | Key est. attempts | |\n");
2203 printf( " | | | | |\n");
2204 }
2205
2206 print_header("Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen");
2207
2208 /* "NNNNNNNNNNNNNNNNNNNNNNNNNNN MMMM KKKKK AAAA LLLL LLLL AAAA NNNN CCCC LLLL" */
2209
2210 for (i = 0; i < n_sources; i++) {
2211 request.command = htons(REQ_SOURCE_DATA);
2212 request.data.source_data.index = htonl(i);
2213 if (!request_reply(&request, &reply, RPY_SOURCE_DATA, 0))
2214 return 0;
2215
2216 source_mode = ntohs(reply.data.source_data.mode);
2217 if (source_mode != RPY_SD_MD_CLIENT && source_mode != RPY_SD_MD_PEER)
2218 continue;
2219
2220 UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &ip_addr);
2221 if (!all && ip_addr.family == IPADDR_ID)
2222 continue;
2223
2224 request.command = htons(REQ_AUTH_DATA);
2225 request.data.auth_data.ip_addr = reply.data.source_data.ip_addr;
2226 if (!request_reply(&request, &reply, RPY_AUTH_DATA, 0))
2227 return 0;
2228
2229 format_name(name, sizeof (name), 25, 0, 0, 1, &ip_addr);
2230
2231 switch (ntohs(reply.data.auth_data.mode)) {
2232 case RPY_AD_MD_NONE:
2233 mode_str = "-";
2234 break;
2235 case RPY_AD_MD_SYMMETRIC:
2236 mode_str = "SK";
2237 break;
2238 case RPY_AD_MD_NTS:
2239 mode_str = "NTS";
2240 break;
2241 default:
2242 mode_str = "?";
2243 break;
2244 }
2245
2246 print_report("%-27s %4s %5U %4d %4d %I %4d %4d %4d %4d\n",
2247 name, mode_str,
2248 (unsigned long)ntohl(reply.data.auth_data.key_id),
2249 ntohs(reply.data.auth_data.key_type),
2250 ntohs(reply.data.auth_data.key_length),
2251 (unsigned long)ntohl(reply.data.auth_data.last_ke_ago),
2252 ntohs(reply.data.auth_data.ke_attempts),
2253 ntohs(reply.data.auth_data.nak),
2254 ntohs(reply.data.auth_data.cookies),
2255 ntohs(reply.data.auth_data.cookie_length),
2256 REPORT_END);
2257 }
2258
2259 return 1;
2260 }
2261
2262 /* ================================================== */
2263
2264 static int
process_cmd_ntpdata(char * line)2265 process_cmd_ntpdata(char *line)
2266 {
2267 CMD_Request request;
2268 CMD_Reply reply;
2269 IPAddr remote_addr, local_addr;
2270 struct timespec ref_time;
2271 uint32_t i, n_sources;
2272 uint16_t mode;
2273 int specified_addr;
2274
2275 if (*line) {
2276 specified_addr = 1;
2277 n_sources = 1;
2278 } else {
2279 specified_addr = 0;
2280 request.command = htons(REQ_N_SOURCES);
2281 if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
2282 return 0;
2283 n_sources = ntohl(reply.data.n_sources.n_sources);
2284 }
2285
2286 for (i = 0; i < n_sources; i++) {
2287 if (specified_addr) {
2288 if (!parse_source_address(line, &remote_addr)) {
2289 LOG(LOGS_ERR, "Could not get address for hostname");
2290 return 0;
2291 }
2292 } else {
2293 request.command = htons(REQ_SOURCE_DATA);
2294 request.data.source_data.index = htonl(i);
2295 if (!request_reply(&request, &reply, RPY_SOURCE_DATA, 0))
2296 return 0;
2297
2298 mode = ntohs(reply.data.source_data.mode);
2299 if (mode != RPY_SD_MD_CLIENT && mode != RPY_SD_MD_PEER)
2300 continue;
2301
2302 UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &remote_addr);
2303 if (!UTI_IsIPReal(&remote_addr))
2304 continue;
2305 }
2306
2307 request.command = htons(REQ_NTP_DATA);
2308 UTI_IPHostToNetwork(&remote_addr, &request.data.ntp_data.ip_addr);
2309 if (!request_reply(&request, &reply, RPY_NTP_DATA, 0))
2310 return 0;
2311
2312 UTI_IPNetworkToHost(&reply.data.ntp_data.remote_addr, &remote_addr);
2313 UTI_IPNetworkToHost(&reply.data.ntp_data.local_addr, &local_addr);
2314 UTI_TimespecNetworkToHost(&reply.data.ntp_data.ref_time, &ref_time);
2315
2316 if (!specified_addr && !csv_mode)
2317 printf("\n");
2318
2319 print_report("Remote address : %s (%R)\n"
2320 "Remote port : %u\n"
2321 "Local address : %s (%R)\n"
2322 "Leap status : %L\n"
2323 "Version : %u\n"
2324 "Mode : %M\n"
2325 "Stratum : %u\n"
2326 "Poll interval : %d (%.0f seconds)\n"
2327 "Precision : %d (%.9f seconds)\n"
2328 "Root delay : %.6f seconds\n"
2329 "Root dispersion : %.6f seconds\n"
2330 "Reference ID : %R (%s)\n"
2331 "Reference time : %T\n"
2332 "Offset : %+.9f seconds\n"
2333 "Peer delay : %.9f seconds\n"
2334 "Peer dispersion : %.9f seconds\n"
2335 "Response time : %.9f seconds\n"
2336 "Jitter asymmetry: %+.2f\n"
2337 "NTP tests : %.3b %.3b %.4b\n"
2338 "Interleaved : %B\n"
2339 "Authenticated : %B\n"
2340 "TX timestamping : %N\n"
2341 "RX timestamping : %N\n"
2342 "Total TX : %U\n"
2343 "Total RX : %U\n"
2344 "Total valid RX : %U\n",
2345 UTI_IPToString(&remote_addr), (unsigned long)UTI_IPToRefid(&remote_addr),
2346 ntohs(reply.data.ntp_data.remote_port),
2347 UTI_IPToString(&local_addr), (unsigned long)UTI_IPToRefid(&local_addr),
2348 reply.data.ntp_data.leap, reply.data.ntp_data.version,
2349 reply.data.ntp_data.mode, reply.data.ntp_data.stratum,
2350 reply.data.ntp_data.poll, UTI_Log2ToDouble(reply.data.ntp_data.poll),
2351 reply.data.ntp_data.precision, UTI_Log2ToDouble(reply.data.ntp_data.precision),
2352 UTI_FloatNetworkToHost(reply.data.ntp_data.root_delay),
2353 UTI_FloatNetworkToHost(reply.data.ntp_data.root_dispersion),
2354 (unsigned long)ntohl(reply.data.ntp_data.ref_id),
2355 reply.data.ntp_data.stratum <= 1 ?
2356 UTI_RefidToString(ntohl(reply.data.ntp_data.ref_id)) : "",
2357 &ref_time,
2358 UTI_FloatNetworkToHost(reply.data.ntp_data.offset),
2359 UTI_FloatNetworkToHost(reply.data.ntp_data.peer_delay),
2360 UTI_FloatNetworkToHost(reply.data.ntp_data.peer_dispersion),
2361 UTI_FloatNetworkToHost(reply.data.ntp_data.response_time),
2362 UTI_FloatNetworkToHost(reply.data.ntp_data.jitter_asymmetry),
2363 ntohs(reply.data.ntp_data.flags) >> 7,
2364 ntohs(reply.data.ntp_data.flags) >> 4,
2365 ntohs(reply.data.ntp_data.flags),
2366 ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_INTERLEAVED,
2367 ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_AUTHENTICATED,
2368 reply.data.ntp_data.tx_tss_char, reply.data.ntp_data.rx_tss_char,
2369 (unsigned long)ntohl(reply.data.ntp_data.total_tx_count),
2370 (unsigned long)ntohl(reply.data.ntp_data.total_rx_count),
2371 (unsigned long)ntohl(reply.data.ntp_data.total_valid_count),
2372 REPORT_END);
2373 }
2374
2375 return 1;
2376 }
2377
2378 /* ================================================== */
2379
2380 static int
process_cmd_selectdata(char * line)2381 process_cmd_selectdata(char *line)
2382 {
2383 CMD_Request request;
2384 CMD_Reply reply;
2385 uint32_t i, n_sources;
2386 int all, verbose, conf_options, eff_options;
2387 char name[256];
2388 IPAddr ip_addr;
2389
2390 parse_sources_options(line, &all, &verbose);
2391
2392 request.command = htons(REQ_N_SOURCES);
2393 if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
2394 return 0;
2395
2396 n_sources = ntohl(reply.data.n_sources.n_sources);
2397
2398 if (verbose) {
2399 printf( " .-- State: N - noselect, M - missing samples, d/D - large distance,\n");
2400 printf( " / ~ - jittery, w/W - waits for others, T - not trusted,\n");
2401 printf( "| x - falseticker, P - not preferred, U - waits for update,\n");
2402 printf( "| S - stale, O - orphan, + - combined, * - best.\n");
2403 printf( "| Effective options ------. (N - noselect, P - prefer\n");
2404 printf( "| Configured options -. \\ T - trust, R - require)\n");
2405 printf( "| Auth. enabled (Y/N) -. \\ \\ Offset interval --.\n");
2406 printf( "| | | | |\n");
2407 }
2408
2409 print_header("S Name/IP Address Auth COpts EOpts Last Score Interval Leap");
2410
2411 /* "S NNNNNNNNNNNNNNNNNNNNNNNNN A OOOO- OOOO- LLLL SSSSS IIIIIII IIIIIII L" */
2412
2413 for (i = 0; i < n_sources; i++) {
2414 request.command = htons(REQ_SELECT_DATA);
2415 request.data.source_data.index = htonl(i);
2416 if (!request_reply(&request, &reply, RPY_SELECT_DATA, 0))
2417 return 0;
2418
2419 UTI_IPNetworkToHost(&reply.data.select_data.ip_addr, &ip_addr);
2420 if (!all && ip_addr.family == IPADDR_ID)
2421 continue;
2422
2423 format_name(name, sizeof (name), 25, ip_addr.family == IPADDR_UNSPEC,
2424 ntohl(reply.data.select_data.ref_id), 1, &ip_addr);
2425
2426 conf_options = ntohs(reply.data.select_data.conf_options);
2427 eff_options = ntohs(reply.data.select_data.eff_options);
2428
2429 print_report("%c %-25s %c %c%c%c%c%c %c%c%c%c%c %I %5.1f %+S %+S %1L\n",
2430 reply.data.select_data.state_char,
2431 name,
2432 reply.data.select_data.authentication ? 'Y' : 'N',
2433 conf_options & RPY_SD_OPTION_NOSELECT ? 'N' : '-',
2434 conf_options & RPY_SD_OPTION_PREFER ? 'P' : '-',
2435 conf_options & RPY_SD_OPTION_TRUST ? 'T' : '-',
2436 conf_options & RPY_SD_OPTION_REQUIRE ? 'R' : '-',
2437 '-',
2438 eff_options & RPY_SD_OPTION_NOSELECT ? 'N' : '-',
2439 eff_options & RPY_SD_OPTION_PREFER ? 'P' : '-',
2440 eff_options & RPY_SD_OPTION_TRUST ? 'T' : '-',
2441 eff_options & RPY_SD_OPTION_REQUIRE ? 'R' : '-',
2442 '-',
2443 (unsigned long)ntohl(reply.data.select_data.last_sample_ago),
2444 UTI_FloatNetworkToHost(reply.data.select_data.score),
2445 UTI_FloatNetworkToHost(reply.data.select_data.lo_limit),
2446 UTI_FloatNetworkToHost(reply.data.select_data.hi_limit),
2447 reply.data.select_data.leap,
2448 REPORT_END);
2449 }
2450
2451 return 1;
2452 }
2453
2454 /* ================================================== */
2455
2456 static int
process_cmd_serverstats(char * line)2457 process_cmd_serverstats(char *line)
2458 {
2459 CMD_Request request;
2460 CMD_Reply reply;
2461
2462 request.command = htons(REQ_SERVER_STATS);
2463 if (!request_reply(&request, &reply, RPY_SERVER_STATS3, 0))
2464 return 0;
2465
2466 print_report("NTP packets received : %U\n"
2467 "NTP packets dropped : %U\n"
2468 "Command packets received : %U\n"
2469 "Command packets dropped : %U\n"
2470 "Client log records dropped : %U\n"
2471 "NTS-KE connections accepted: %U\n"
2472 "NTS-KE connections dropped : %U\n"
2473 "Authenticated NTP packets : %U\n"
2474 "Interleaved NTP packets : %U\n"
2475 "NTP timestamps held : %U\n"
2476 "NTP timestamp span : %U\n",
2477 (unsigned long)ntohl(reply.data.server_stats.ntp_hits),
2478 (unsigned long)ntohl(reply.data.server_stats.ntp_drops),
2479 (unsigned long)ntohl(reply.data.server_stats.cmd_hits),
2480 (unsigned long)ntohl(reply.data.server_stats.cmd_drops),
2481 (unsigned long)ntohl(reply.data.server_stats.log_drops),
2482 (unsigned long)ntohl(reply.data.server_stats.nke_hits),
2483 (unsigned long)ntohl(reply.data.server_stats.nke_drops),
2484 (unsigned long)ntohl(reply.data.server_stats.ntp_auth_hits),
2485 (unsigned long)ntohl(reply.data.server_stats.ntp_interleaved_hits),
2486 (unsigned long)ntohl(reply.data.server_stats.ntp_timestamps),
2487 (unsigned long)ntohl(reply.data.server_stats.ntp_span_seconds),
2488 REPORT_END);
2489
2490 return 1;
2491 }
2492
2493 /* ================================================== */
2494
2495 static int
process_cmd_smoothing(char * line)2496 process_cmd_smoothing(char *line)
2497 {
2498 CMD_Request request;
2499 CMD_Reply reply;
2500 uint32_t flags;
2501
2502 request.command = htons(REQ_SMOOTHING);
2503 if (!request_reply(&request, &reply, RPY_SMOOTHING, 0))
2504 return 0;
2505
2506 flags = ntohl(reply.data.smoothing.flags);
2507
2508 print_report("Active : %B %s\n"
2509 "Offset : %+.9f seconds\n"
2510 "Frequency : %+.6f ppm\n"
2511 "Wander : %+.6f ppm per second\n"
2512 "Last update : %.1f seconds ago\n"
2513 "Remaining time : %.1f seconds\n",
2514 !!(flags & RPY_SMT_FLAG_ACTIVE),
2515 flags & RPY_SMT_FLAG_LEAPONLY ? "(leap second only)" : "",
2516 UTI_FloatNetworkToHost(reply.data.smoothing.offset),
2517 UTI_FloatNetworkToHost(reply.data.smoothing.freq_ppm),
2518 UTI_FloatNetworkToHost(reply.data.smoothing.wander_ppm),
2519 UTI_FloatNetworkToHost(reply.data.smoothing.last_update_ago),
2520 UTI_FloatNetworkToHost(reply.data.smoothing.remaining_time),
2521 REPORT_END);
2522
2523 return 1;
2524 }
2525
2526 /* ================================================== */
2527
2528 static int
process_cmd_smoothtime(CMD_Request * msg,const char * line)2529 process_cmd_smoothtime(CMD_Request *msg, const char *line)
2530 {
2531 if (!strcmp(line, "reset")) {
2532 msg->data.smoothtime.option = htonl(REQ_SMOOTHTIME_RESET);
2533 } else if (!strcmp(line, "activate")) {
2534 msg->data.smoothtime.option = htonl(REQ_SMOOTHTIME_ACTIVATE);
2535 } else {
2536 LOG(LOGS_ERR, "Invalid syntax for smoothtime command");
2537 return 0;
2538 }
2539
2540 msg->command = htons(REQ_SMOOTHTIME);
2541
2542 return 1;
2543 }
2544
2545 /* ================================================== */
2546
2547 static int
process_cmd_rtcreport(char * line)2548 process_cmd_rtcreport(char *line)
2549 {
2550 CMD_Request request;
2551 CMD_Reply reply;
2552 struct timespec ref_time;
2553
2554 request.command = htons(REQ_RTCREPORT);
2555 if (!request_reply(&request, &reply, RPY_RTC, 0))
2556 return 0;
2557
2558 UTI_TimespecNetworkToHost(&reply.data.rtc.ref_time, &ref_time);
2559
2560 print_report("RTC ref time (UTC) : %T\n"
2561 "Number of samples : %u\n"
2562 "Number of runs : %u\n"
2563 "Sample span period : %I\n"
2564 "RTC is fast by : %12.6f seconds\n"
2565 "RTC gains time at : %9.3f ppm\n",
2566 &ref_time,
2567 ntohs(reply.data.rtc.n_samples),
2568 ntohs(reply.data.rtc.n_runs),
2569 (unsigned long)ntohl(reply.data.rtc.span_seconds),
2570 UTI_FloatNetworkToHost(reply.data.rtc.rtc_seconds_fast),
2571 UTI_FloatNetworkToHost(reply.data.rtc.rtc_gain_rate_ppm),
2572 REPORT_END);
2573
2574 return 1;
2575 }
2576
2577 /* ================================================== */
2578
2579 static int
process_cmd_clients(char * line)2580 process_cmd_clients(char *line)
2581 {
2582 CMD_Request request;
2583 CMD_Reply reply;
2584 IPAddr ip;
2585 uint32_t i, n_clients, next_index, n_indices, min_hits, reset;
2586 RPY_ClientAccesses_Client *client;
2587 char header[80], name[50], *opt, *arg;
2588 int nke;
2589
2590 next_index = 0;
2591 min_hits = 0;
2592 reset = 0;
2593 nke = 0;
2594
2595 while (*line) {
2596 opt = line;
2597 line = CPS_SplitWord(line);
2598 if (strcmp(opt, "-k") == 0) {
2599 nke = 1;
2600 } else if (strcmp(opt, "-p") == 0) {
2601 arg = line;
2602 line = CPS_SplitWord(line);
2603 if (sscanf(arg, "%"SCNu32, &min_hits) != 1) {
2604 LOG(LOGS_ERR, "Invalid syntax for clients command");
2605 return 0;
2606 }
2607 } else if (strcmp(opt, "-r") == 0) {
2608 reset = 1;
2609 }
2610 }
2611
2612 snprintf(header, sizeof (header),
2613 "Hostname NTP Drop Int IntL Last %6s Drop Int Last",
2614 nke ? "NTS-KE" : "Cmd");
2615 print_header(header);
2616
2617 while (1) {
2618 request.command = htons(REQ_CLIENT_ACCESSES_BY_INDEX3);
2619 request.data.client_accesses_by_index.first_index = htonl(next_index);
2620 request.data.client_accesses_by_index.n_clients = htonl(MAX_CLIENT_ACCESSES);
2621 request.data.client_accesses_by_index.min_hits = htonl(min_hits);
2622 request.data.client_accesses_by_index.reset = htonl(reset);
2623
2624 if (!request_reply(&request, &reply, RPY_CLIENT_ACCESSES_BY_INDEX3, 0))
2625 return 0;
2626
2627 n_clients = ntohl(reply.data.client_accesses_by_index.n_clients);
2628 n_indices = ntohl(reply.data.client_accesses_by_index.n_indices);
2629
2630 for (i = 0; i < n_clients && i < MAX_CLIENT_ACCESSES; i++) {
2631 client = &reply.data.client_accesses_by_index.clients[i];
2632
2633 UTI_IPNetworkToHost(&client->ip, &ip);
2634
2635 /* UNSPEC means the record could not be found in the daemon's tables.
2636 We shouldn't ever generate this case, but ignore it if we do. */
2637 if (ip.family == IPADDR_UNSPEC)
2638 continue;
2639
2640 format_name(name, sizeof (name), 25, 0, 0, 0, &ip);
2641
2642 print_report("%-25s %6U %5U %C %C %I %6U %5U %C %I\n",
2643 name,
2644 (unsigned long)ntohl(client->ntp_hits),
2645 (unsigned long)ntohl(client->ntp_drops),
2646 client->ntp_interval,
2647 client->ntp_timeout_interval,
2648 (unsigned long)ntohl(client->last_ntp_hit_ago),
2649 (unsigned long)ntohl(nke ? client->nke_hits : client->cmd_hits),
2650 (unsigned long)ntohl(nke ? client->nke_drops : client->cmd_drops),
2651 nke ? client->nke_interval : client->cmd_interval,
2652 (unsigned long)ntohl(nke ? client->last_nke_hit_ago :
2653 client->last_cmd_hit_ago),
2654 REPORT_END);
2655 }
2656
2657 /* Set the next index to probe based on what the server tells us */
2658 next_index = ntohl(reply.data.client_accesses_by_index.next_index);
2659
2660 if (next_index >= n_indices || n_clients < MAX_CLIENT_ACCESSES)
2661 break;
2662 }
2663
2664 return 1;
2665 }
2666
2667
2668 /* ================================================== */
2669 /* Process the manual list command */
2670 static int
process_cmd_manual_list(const char * line)2671 process_cmd_manual_list(const char *line)
2672 {
2673 CMD_Request request;
2674 CMD_Reply reply;
2675 uint32_t i, n_samples;
2676 RPY_ManualListSample *sample;
2677 struct timespec when;
2678
2679 request.command = htons(REQ_MANUAL_LIST);
2680 if (!request_reply(&request, &reply, RPY_MANUAL_LIST2, 0))
2681 return 0;
2682
2683 n_samples = ntohl(reply.data.manual_list.n_samples);
2684 print_info_field("210 n_samples = %lu\n", (unsigned long)n_samples);
2685
2686 print_header("# Date Time(UTC) Slewed Original Residual");
2687
2688 for (i = 0; i < n_samples && i < MAX_MANUAL_LIST_SAMPLES; i++) {
2689 sample = &reply.data.manual_list.samples[i];
2690 UTI_TimespecNetworkToHost(&sample->when, &when);
2691
2692 print_report("%2d %s %10.2f %10.2f %10.2f\n",
2693 i, UTI_TimeToLogForm(when.tv_sec),
2694 UTI_FloatNetworkToHost(sample->slewed_offset),
2695 UTI_FloatNetworkToHost(sample->orig_offset),
2696 UTI_FloatNetworkToHost(sample->residual),
2697 REPORT_END);
2698 }
2699
2700 return 1;
2701 }
2702
2703 /* ================================================== */
2704
2705 static int
process_cmd_manual_delete(CMD_Request * msg,const char * line)2706 process_cmd_manual_delete(CMD_Request *msg, const char *line)
2707 {
2708 int index;
2709
2710 if (sscanf(line, "%d", &index) != 1) {
2711 LOG(LOGS_ERR, "Bad syntax for manual delete command");
2712 return 0;
2713 }
2714
2715 msg->command = htons(REQ_MANUAL_DELETE);
2716 msg->data.manual_delete.index = htonl(index);
2717 return 1;
2718 }
2719
2720 /* ================================================== */
2721
2722 static int
process_cmd_settime(char * line)2723 process_cmd_settime(char *line)
2724 {
2725 struct timespec ts;
2726 time_t now, new_time;
2727 CMD_Request request;
2728 CMD_Reply reply;
2729 double dfreq_ppm, new_afreq_ppm;
2730 double offset;
2731
2732 now = time(NULL);
2733 new_time = get_date(line, &now);
2734
2735 if (new_time == -1) {
2736 printf("510 - Could not parse date string\n");
2737 } else {
2738 ts.tv_sec = new_time;
2739 ts.tv_nsec = 0;
2740 UTI_TimespecHostToNetwork(&ts, &request.data.settime.ts);
2741 request.command = htons(REQ_SETTIME);
2742 if (request_reply(&request, &reply, RPY_MANUAL_TIMESTAMP2, 1)) {
2743 offset = UTI_FloatNetworkToHost(reply.data.manual_timestamp.offset);
2744 dfreq_ppm = UTI_FloatNetworkToHost(reply.data.manual_timestamp.dfreq_ppm);
2745 new_afreq_ppm = UTI_FloatNetworkToHost(reply.data.manual_timestamp.new_afreq_ppm);
2746 printf("Clock was %.2f seconds fast. Frequency change = %.2fppm, new frequency = %.2fppm\n",
2747 offset, dfreq_ppm, new_afreq_ppm);
2748 return 1;
2749 }
2750 }
2751 return 0;
2752 }
2753
2754 /* ================================================== */
2755
2756 static void
process_cmd_rekey(CMD_Request * msg,char * line)2757 process_cmd_rekey(CMD_Request *msg, char *line)
2758 {
2759 msg->command = htons(REQ_REKEY);
2760 }
2761
2762 /* ================================================== */
2763
2764 static int
process_cmd_makestep(CMD_Request * msg,char * line)2765 process_cmd_makestep(CMD_Request *msg, char *line)
2766 {
2767 int limit;
2768 double threshold;
2769
2770 if (*line) {
2771 if (sscanf(line, "%lf %d", &threshold, &limit) != 2) {
2772 LOG(LOGS_ERR, "Bad syntax for makestep command");
2773 return 0;
2774 }
2775 msg->command = htons(REQ_MODIFY_MAKESTEP);
2776 msg->data.modify_makestep.limit = htonl(limit);
2777 msg->data.modify_makestep.threshold = UTI_FloatHostToNetwork(threshold);
2778 } else {
2779 msg->command = htons(REQ_MAKESTEP);
2780 }
2781
2782 return 1;
2783 }
2784
2785 /* ================================================== */
2786
2787 static int
process_cmd_activity(const char * line)2788 process_cmd_activity(const char *line)
2789 {
2790 CMD_Request request;
2791 CMD_Reply reply;
2792
2793 request.command = htons(REQ_ACTIVITY);
2794 if (!request_reply(&request, &reply, RPY_ACTIVITY, 0))
2795 return 0;
2796
2797 print_info_field("200 OK\n");
2798
2799 print_report("%U sources online\n"
2800 "%U sources offline\n"
2801 "%U sources doing burst (return to online)\n"
2802 "%U sources doing burst (return to offline)\n"
2803 "%U sources with unknown address\n",
2804 (unsigned long)ntohl(reply.data.activity.online),
2805 (unsigned long)ntohl(reply.data.activity.offline),
2806 (unsigned long)ntohl(reply.data.activity.burst_online),
2807 (unsigned long)ntohl(reply.data.activity.burst_offline),
2808 (unsigned long)ntohl(reply.data.activity.unresolved),
2809 REPORT_END);
2810
2811 return 1;
2812 }
2813
2814 /* ================================================== */
2815
2816 static int
process_cmd_reselectdist(CMD_Request * msg,char * line)2817 process_cmd_reselectdist(CMD_Request *msg, char *line)
2818 {
2819 double dist;
2820 int ok;
2821 msg->command = htons(REQ_RESELECTDISTANCE);
2822 if (sscanf(line, "%lf", &dist) == 1) {
2823 msg->data.reselect_distance.distance = UTI_FloatHostToNetwork(dist);
2824 ok = 1;
2825 } else {
2826 ok = 0;
2827 }
2828 return ok;
2829 }
2830
2831 /* ================================================== */
2832
2833 static void
process_cmd_reselect(CMD_Request * msg,char * line)2834 process_cmd_reselect(CMD_Request *msg, char *line)
2835 {
2836 msg->command = htons(REQ_RESELECT);
2837 }
2838
2839 /* ================================================== */
2840
2841 static void
process_cmd_refresh(CMD_Request * msg,char * line)2842 process_cmd_refresh(CMD_Request *msg, char *line)
2843 {
2844 msg->command = htons(REQ_REFRESH);
2845 }
2846
2847 /* ================================================== */
2848
2849 static void
process_cmd_shutdown(CMD_Request * msg,char * line)2850 process_cmd_shutdown(CMD_Request *msg, char *line)
2851 {
2852 msg->command = htons(REQ_SHUTDOWN);
2853 }
2854
2855 /* ================================================== */
2856
2857 static int
process_cmd_reload(CMD_Request * msg,char * line)2858 process_cmd_reload(CMD_Request *msg, char *line)
2859 {
2860 if (!strcmp(line, "sources")) {
2861 msg->command = htons(REQ_RELOAD_SOURCES);
2862 } else {
2863 LOG(LOGS_ERR, "Invalid syntax for reload command");
2864 return 0;
2865 }
2866
2867 return 1;
2868 }
2869
2870 /* ================================================== */
2871
2872 static int
process_cmd_reset(CMD_Request * msg,char * line)2873 process_cmd_reset(CMD_Request *msg, char *line)
2874 {
2875 if (!strcmp(line, "sources")) {
2876 msg->command = htons(REQ_RESET_SOURCES);
2877 } else {
2878 LOG(LOGS_ERR, "Invalid syntax for reset command");
2879 return 0;
2880 }
2881
2882 return 1;
2883 }
2884
2885 /* ================================================== */
2886
2887 static int
process_cmd_waitsync(char * line)2888 process_cmd_waitsync(char *line)
2889 {
2890 CMD_Request request;
2891 CMD_Reply reply;
2892 IPAddr ip_addr;
2893 uint32_t ref_id;
2894 double correction, skew_ppm, max_correction, max_skew_ppm, interval;
2895 int ret = 0, max_tries, i;
2896 struct timeval timeout;
2897
2898 max_tries = 0;
2899 max_correction = 0.0;
2900 max_skew_ppm = 0.0;
2901 interval = 10.0;
2902
2903 if (sscanf(line, "%d %lf %lf %lf", &max_tries, &max_correction, &max_skew_ppm, &interval))
2904 ;
2905
2906 /* Don't allow shorter interval than 0.1 seconds */
2907 if (interval < 0.1)
2908 interval = 0.1;
2909
2910 request.command = htons(REQ_TRACKING);
2911
2912 for (i = 1; ; i++) {
2913 if (request_reply(&request, &reply, RPY_TRACKING, 0)) {
2914 ref_id = ntohl(reply.data.tracking.ref_id);
2915 UTI_IPNetworkToHost(&reply.data.tracking.ip_addr, &ip_addr);
2916
2917 correction = UTI_FloatNetworkToHost(reply.data.tracking.current_correction);
2918 correction = fabs(correction);
2919 skew_ppm = UTI_FloatNetworkToHost(reply.data.tracking.skew_ppm);
2920
2921 print_report("try: %d, refid: %R, correction: %.9f, skew: %.3f\n",
2922 i, (unsigned long)ref_id, correction, skew_ppm, REPORT_END);
2923
2924 if ((ip_addr.family != IPADDR_UNSPEC ||
2925 (ref_id != 0 && ref_id != 0x7f7f0101L /* LOCAL refid */)) &&
2926 (max_correction == 0.0 || correction <= max_correction) &&
2927 (max_skew_ppm == 0.0 || skew_ppm <= max_skew_ppm)) {
2928 ret = 1;
2929 }
2930 }
2931
2932 if (!ret && (!max_tries || i < max_tries) && !quit) {
2933 UTI_DoubleToTimeval(interval, &timeout);
2934 if (select(0, NULL, NULL, NULL, &timeout))
2935 break;
2936 } else {
2937 break;
2938 }
2939 }
2940 return ret;
2941 }
2942
2943 /* ================================================== */
2944
2945 static int
process_cmd_dns(const char * line)2946 process_cmd_dns(const char *line)
2947 {
2948 if (!strcmp(line, "-46")) {
2949 DNS_SetAddressFamily(IPADDR_UNSPEC);
2950 } else if (!strcmp(line, "-4")) {
2951 DNS_SetAddressFamily(IPADDR_INET4);
2952 } else if (!strcmp(line, "-6")) {
2953 DNS_SetAddressFamily(IPADDR_INET6);
2954 } else if (!strcmp(line, "-n")) {
2955 no_dns = 1;
2956 } else if (!strcmp(line, "+n")) {
2957 no_dns = 0;
2958 } else {
2959 LOG(LOGS_ERR, "Unrecognized dns command");
2960 return 0;
2961 }
2962 return 1;
2963 }
2964
2965 /* ================================================== */
2966
2967 static int
process_cmd_timeout(const char * line)2968 process_cmd_timeout(const char *line)
2969 {
2970 int timeout;
2971
2972 timeout = atoi(line);
2973 if (timeout < 100) {
2974 LOG(LOGS_ERR, "Timeout %d is too short", timeout);
2975 return 0;
2976 }
2977 initial_timeout = timeout;
2978 return 1;
2979 }
2980
2981 /* ================================================== */
2982
2983 static int
process_cmd_retries(const char * line)2984 process_cmd_retries(const char *line)
2985 {
2986 int retries;
2987
2988 retries = atoi(line);
2989 if (retries < 0 || retries > 30) {
2990 LOG(LOGS_ERR, "Invalid maximum number of retries");
2991 return 0;
2992 }
2993 max_retries = retries;
2994 return 1;
2995 }
2996
2997 /* ================================================== */
2998
2999 static int
process_cmd_keygen(char * line)3000 process_cmd_keygen(char *line)
3001 {
3002 unsigned int i, args, cmac_length, length, id = 1, bits = 160;
3003 unsigned char key[512];
3004 const char *type;
3005 char *words[3];
3006
3007 #ifdef FEAT_SECHASH
3008 type = "SHA1";
3009 #else
3010 type = "MD5";
3011 #endif
3012
3013 args = UTI_SplitString(line, words, 3);
3014 if (args >= 2)
3015 type = words[1];
3016
3017 if (args > 3 ||
3018 (args >= 1 && sscanf(words[0], "%u", &id) != 1) ||
3019 (args >= 3 && sscanf(words[2], "%u", &bits) != 1)) {
3020 LOG(LOGS_ERR, "Invalid syntax for keygen command");
3021 return 0;
3022 }
3023
3024 #ifdef HAVE_CMAC
3025 cmac_length = CMC_GetKeyLength(UTI_CmacNameToAlgorithm(type));
3026 #else
3027 cmac_length = 0;
3028 #endif
3029
3030 if (HSH_GetHashId(UTI_HashNameToAlgorithm(type)) >= 0) {
3031 length = (bits + 7) / 8;
3032 } else if (cmac_length > 0) {
3033 length = cmac_length;
3034 } else {
3035 LOG(LOGS_ERR, "Unknown hash function or cipher %s", type);
3036 return 0;
3037 }
3038
3039 length = CLAMP(10, length, sizeof (key));
3040
3041 UTI_GetRandomBytesUrandom(key, length);
3042
3043 printf("%u %s HEX:", id, type);
3044 for (i = 0; i < length; i++)
3045 printf("%02hhX", key[i]);
3046 printf("\n");
3047
3048 return 1;
3049 }
3050
3051 /* ================================================== */
3052
3053 static int
process_line(char * line)3054 process_line(char *line)
3055 {
3056 char *command;
3057 int do_normal_submit;
3058 int ret;
3059 CMD_Request tx_message;
3060 CMD_Reply rx_message;
3061
3062 ret = 0;
3063
3064 do_normal_submit = 1;
3065
3066 CPS_NormalizeLine(line);
3067
3068 if (!*line) {
3069 fflush(stderr);
3070 fflush(stdout);
3071 return 1;
3072 };
3073
3074 command = line;
3075 line = CPS_SplitWord(line);
3076
3077 if (!strcmp(command, "accheck")) {
3078 do_normal_submit = process_cmd_accheck(&tx_message, line);
3079 } else if (!strcmp(command, "activity")) {
3080 do_normal_submit = 0;
3081 ret = process_cmd_activity(line);
3082 } else if (!strcmp(command, "add")) {
3083 do_normal_submit = process_cmd_add_source(&tx_message, line);
3084 } else if (!strcmp(command, "allow")) {
3085 do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_ALLOW, REQ_ALLOWALL);
3086 } else if (!strcmp(command, "authdata")) {
3087 do_normal_submit = 0;
3088 ret = process_cmd_authdata(line);
3089 } else if (!strcmp(command, "burst")) {
3090 do_normal_submit = process_cmd_burst(&tx_message, line);
3091 } else if (!strcmp(command, "clients")) {
3092 ret = process_cmd_clients(line);
3093 do_normal_submit = 0;
3094 } else if (!strcmp(command, "cmdaccheck")) {
3095 do_normal_submit = process_cmd_cmdaccheck(&tx_message, line);
3096 } else if (!strcmp(command, "cmdallow")) {
3097 do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_CMDALLOW, REQ_CMDALLOWALL);
3098 } else if (!strcmp(command, "cmddeny")) {
3099 do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_CMDDENY, REQ_CMDDENYALL);
3100 } else if (!strcmp(command, "cyclelogs")) {
3101 process_cmd_cyclelogs(&tx_message, line);
3102 } else if (!strcmp(command, "delete")) {
3103 do_normal_submit = process_cmd_delete(&tx_message, line);
3104 } else if (!strcmp(command, "deny")) {
3105 do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_DENY, REQ_DENYALL);
3106 } else if (!strcmp(command, "dfreq")) {
3107 do_normal_submit = process_cmd_dfreq(&tx_message, line);
3108 } else if (!strcmp(command, "dns")) {
3109 ret = process_cmd_dns(line);
3110 do_normal_submit = 0;
3111 } else if (!strcmp(command, "doffset")) {
3112 do_normal_submit = process_cmd_doffset(&tx_message, line);
3113 } else if (!strcmp(command, "dump")) {
3114 process_cmd_dump(&tx_message, line);
3115 } else if (!strcmp(command, "exit")) {
3116 do_normal_submit = 0;
3117 quit = 1;
3118 ret = 1;
3119 } else if (!strcmp(command, "help")) {
3120 do_normal_submit = 0;
3121 give_help();
3122 ret = 1;
3123 } else if (!strcmp(command, "keygen")) {
3124 ret = process_cmd_keygen(line);
3125 do_normal_submit = 0;
3126 } else if (!strcmp(command, "local")) {
3127 do_normal_submit = process_cmd_local(&tx_message, line);
3128 } else if (!strcmp(command, "makestep")) {
3129 do_normal_submit = process_cmd_makestep(&tx_message, line);
3130 } else if (!strcmp(command, "manual")) {
3131 if (!strncmp(line, "list", 4)) {
3132 do_normal_submit = 0;
3133 ret = process_cmd_manual_list(CPS_SplitWord(line));
3134 } else if (!strncmp(line, "delete", 6)) {
3135 do_normal_submit = process_cmd_manual_delete(&tx_message, CPS_SplitWord(line));
3136 } else {
3137 do_normal_submit = process_cmd_manual(&tx_message, line);
3138 }
3139 } else if (!strcmp(command, "maxdelay")) {
3140 do_normal_submit = process_cmd_maxdelay(&tx_message, line);
3141 } else if (!strcmp(command, "maxdelaydevratio")) {
3142 do_normal_submit = process_cmd_maxdelaydevratio(&tx_message, line);
3143 } else if (!strcmp(command, "maxdelayratio")) {
3144 do_normal_submit = process_cmd_maxdelayratio(&tx_message, line);
3145 } else if (!strcmp(command, "maxpoll")) {
3146 do_normal_submit = process_cmd_maxpoll(&tx_message, line);
3147 } else if (!strcmp(command, "maxupdateskew")) {
3148 do_normal_submit = process_cmd_maxupdateskew(&tx_message, line);
3149 } else if (!strcmp(command, "minpoll")) {
3150 do_normal_submit = process_cmd_minpoll(&tx_message, line);
3151 } else if (!strcmp(command, "minstratum")) {
3152 do_normal_submit = process_cmd_minstratum(&tx_message, line);
3153 } else if (!strcmp(command, "ntpdata")) {
3154 do_normal_submit = 0;
3155 ret = process_cmd_ntpdata(line);
3156 } else if (!strcmp(command, "offline")) {
3157 do_normal_submit = process_cmd_offline(&tx_message, line);
3158 } else if (!strcmp(command, "online")) {
3159 do_normal_submit = process_cmd_online(&tx_message, line);
3160 } else if (!strcmp(command, "onoffline")) {
3161 process_cmd_onoffline(&tx_message, line);
3162 } else if (!strcmp(command, "polltarget")) {
3163 do_normal_submit = process_cmd_polltarget(&tx_message, line);
3164 } else if (!strcmp(command, "quit")) {
3165 do_normal_submit = 0;
3166 quit = 1;
3167 ret = 1;
3168 } else if (!strcmp(command, "refresh")) {
3169 process_cmd_refresh(&tx_message, line);
3170 } else if (!strcmp(command, "rekey")) {
3171 process_cmd_rekey(&tx_message, line);
3172 } else if (!strcmp(command, "reload")) {
3173 do_normal_submit = process_cmd_reload(&tx_message, line);
3174 } else if (!strcmp(command, "reselect")) {
3175 process_cmd_reselect(&tx_message, line);
3176 } else if (!strcmp(command, "reselectdist")) {
3177 do_normal_submit = process_cmd_reselectdist(&tx_message, line);
3178 } else if (!strcmp(command, "reset")) {
3179 do_normal_submit = process_cmd_reset(&tx_message, line);
3180 } else if (!strcmp(command, "retries")) {
3181 ret = process_cmd_retries(line);
3182 do_normal_submit = 0;
3183 } else if (!strcmp(command, "rtcdata")) {
3184 do_normal_submit = 0;
3185 ret = process_cmd_rtcreport(line);
3186 } else if (!strcmp(command, "selectdata")) {
3187 do_normal_submit = 0;
3188 ret = process_cmd_selectdata(line);
3189 } else if (!strcmp(command, "serverstats")) {
3190 do_normal_submit = 0;
3191 ret = process_cmd_serverstats(line);
3192 } else if (!strcmp(command, "settime")) {
3193 do_normal_submit = 0;
3194 ret = process_cmd_settime(line);
3195 } else if (!strcmp(command, "shutdown")) {
3196 process_cmd_shutdown(&tx_message, line);
3197 } else if (!strcmp(command, "smoothing")) {
3198 do_normal_submit = 0;
3199 ret = process_cmd_smoothing(line);
3200 } else if (!strcmp(command, "smoothtime")) {
3201 do_normal_submit = process_cmd_smoothtime(&tx_message, line);
3202 } else if (!strcmp(command, "sourcename")) {
3203 do_normal_submit = 0;
3204 ret = process_cmd_sourcename(line);
3205 } else if (!strcmp(command, "sources")) {
3206 do_normal_submit = 0;
3207 ret = process_cmd_sources(line);
3208 } else if (!strcmp(command, "sourcestats")) {
3209 do_normal_submit = 0;
3210 ret = process_cmd_sourcestats(line);
3211 } else if (!strcmp(command, "timeout")) {
3212 ret = process_cmd_timeout(line);
3213 do_normal_submit = 0;
3214 } else if (!strcmp(command, "tracking")) {
3215 ret = process_cmd_tracking(line);
3216 do_normal_submit = 0;
3217 } else if (!strcmp(command, "trimrtc")) {
3218 process_cmd_trimrtc(&tx_message, line);
3219 } else if (!strcmp(command, "waitsync")) {
3220 ret = process_cmd_waitsync(line);
3221 do_normal_submit = 0;
3222 } else if (!strcmp(command, "writertc")) {
3223 process_cmd_writertc(&tx_message, line);
3224 } else if (!strcmp(command, "authhash") ||
3225 !strcmp(command, "password")) {
3226 /* Warn, but don't return error to not break scripts */
3227 LOG(LOGS_WARN, "Authentication is no longer supported");
3228 do_normal_submit = 0;
3229 ret = 1;
3230 } else {
3231 LOG(LOGS_ERR, "Unrecognized command");
3232 do_normal_submit = 0;
3233 }
3234
3235 if (do_normal_submit) {
3236 ret = request_reply(&tx_message, &rx_message, RPY_NULL, 1);
3237 }
3238 fflush(stderr);
3239 fflush(stdout);
3240 return ret;
3241 }
3242
3243 /* ================================================== */
3244
3245 static int
process_args(int argc,char ** argv,int multi)3246 process_args(int argc, char **argv, int multi)
3247 {
3248 int total_length, i, ret = 0;
3249 char *line;
3250
3251 total_length = 0;
3252 for(i=0; i<argc; i++) {
3253 total_length += strlen(argv[i]) + 1;
3254 }
3255
3256 line = (char *) Malloc((2 + total_length) * sizeof(char));
3257
3258 for (i = 0; i < argc; i++) {
3259 line[0] = '\0';
3260 if (multi) {
3261 strcat(line, argv[i]);
3262 } else {
3263 for (; i < argc; i++) {
3264 strcat(line, argv[i]);
3265 if (i + 1 < argc)
3266 strcat(line, " ");
3267 }
3268 }
3269
3270 ret = process_line(line);
3271 if (!ret || quit)
3272 break;
3273 }
3274
3275 Free(line);
3276
3277 return ret;
3278 }
3279
3280 /* ================================================== */
3281
3282 static void
signal_handler(int signum)3283 signal_handler(int signum)
3284 {
3285 quit = 1;
3286 }
3287
3288 /* ================================================== */
3289
3290 static void
display_gpl(void)3291 display_gpl(void)
3292 {
3293 printf("chrony version %s\n"
3294 "Copyright (C) 1997-2003, 2007, 2009-2021 Richard P. Curnow and others\n"
3295 "chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n"
3296 "you are welcome to redistribute it under certain conditions. See the\n"
3297 "GNU General Public License version 2 for details.\n\n",
3298 CHRONY_VERSION);
3299 }
3300
3301 /* ================================================== */
3302
3303 static void
print_help(const char * progname)3304 print_help(const char *progname)
3305 {
3306 printf("Usage: %s [OPTION]... [COMMAND]...\n\n"
3307 "Options:\n"
3308 " -4\t\tUse IPv4 addresses only\n"
3309 " -6\t\tUse IPv6 addresses only\n"
3310 " -n\t\tDon't resolve hostnames\n"
3311 " -N\t\tPrint original source names\n"
3312 " -c\t\tEnable CSV format\n"
3313 #if DEBUG > 0
3314 " -d\t\tEnable debug messages\n"
3315 #endif
3316 " -m\t\tAccept multiple commands\n"
3317 " -h HOST\tSpecify server (%s)\n"
3318 " -p PORT\tSpecify UDP port (%d)\n"
3319 " -v, --version\tPrint version and exit\n"
3320 " --help\tPrint usage and exit\n",
3321 progname, DEFAULT_COMMAND_SOCKET",127.0.0.1,::1", DEFAULT_CANDM_PORT);
3322 }
3323
3324 /* ================================================== */
3325
3326 static void
print_version(void)3327 print_version(void)
3328 {
3329 printf("chronyc (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYC_FEATURES);
3330 }
3331
3332 /* ================================================== */
3333
3334 int
main(int argc,char ** argv)3335 main(int argc, char **argv)
3336 {
3337 char *line;
3338 const char *progname = argv[0];
3339 const char *hostnames = NULL;
3340 int opt, ret = 1, multi = 0, family = IPADDR_UNSPEC;
3341 int port = DEFAULT_CANDM_PORT;
3342
3343 /* Parse long command-line options */
3344 for (optind = 1; optind < argc; optind++) {
3345 if (!strcmp("--help", argv[optind])) {
3346 print_help(progname);
3347 return 0;
3348 } else if (!strcmp("--version", argv[optind])) {
3349 print_version();
3350 return 0;
3351 }
3352 }
3353
3354 optind = 1;
3355
3356 /* Parse short command-line options */
3357 while ((opt = getopt(argc, argv, "+46acdf:h:mnNp:v")) != -1) {
3358 switch (opt) {
3359 case '4':
3360 case '6':
3361 family = opt == '4' ? IPADDR_INET4 : IPADDR_INET6;
3362 break;
3363 case 'a':
3364 case 'f':
3365 /* For compatibility only */
3366 break;
3367 case 'c':
3368 csv_mode = 1;
3369 break;
3370 case 'd':
3371 #if DEBUG > 0
3372 log_min_severity = LOGS_DEBUG;
3373 #endif
3374 break;
3375 case 'h':
3376 hostnames = optarg;
3377 break;
3378 case 'm':
3379 multi = 1;
3380 break;
3381 case 'n':
3382 no_dns = 1;
3383 break;
3384 case 'N':
3385 source_names = 1;
3386 break;
3387 case 'p':
3388 port = atoi(optarg);
3389 break;
3390 case 'v':
3391 print_version();
3392 return 0;
3393 default:
3394 print_help(progname);
3395 return 1;
3396 }
3397 }
3398
3399 if (isatty(0) && isatty(1) && isatty(2)) {
3400 on_terminal = 1;
3401 }
3402
3403 if (on_terminal && optind == argc) {
3404 display_gpl();
3405 }
3406
3407 DNS_SetAddressFamily(family);
3408
3409 if (!hostnames) {
3410 hostnames = DEFAULT_COMMAND_SOCKET",127.0.0.1,::1";
3411 }
3412
3413 UTI_SetQuitSignalsHandler(signal_handler, 0);
3414
3415 SCK_Initialise(IPADDR_UNSPEC);
3416 server_addresses = get_addresses(hostnames, port);
3417
3418 if (!open_io())
3419 LOG_FATAL("Could not open connection to daemon");
3420
3421 if (optind < argc) {
3422 ret = process_args(argc - optind, argv + optind, multi);
3423 } else {
3424 do {
3425 line = read_line();
3426 if (line && !quit) {
3427 ret = process_line(line);
3428 }else {
3429 /* supply the final '\n' when user exits via ^D */
3430 if( on_terminal ) printf("\n");
3431 }
3432 } while (line && !quit);
3433 }
3434
3435 close_io();
3436 free_addresses(server_addresses);
3437 SCK_Finalise();
3438
3439 return !ret;
3440 }
3441
3442
3443