/* * set_freq - set clock frequency correction * * Used to step the frequency correction at startup, possibly again once * the frequency is measured (that is, transitioning from EVNT_NSET to * EVNT_FSET), and finally to switch between daemon and kernel loop * discipline at runtime. * * When the kernel loop discipline is available but the daemon loop is * in use, the kernel frequency correction is disabled (set to 0) to * ensure drift_comp is applied by only one of the loops. */ static void set_freq( double freq /* frequency update */ ) { const char * loop_desc; int ntp_adj_ret; drift_comp = freq; loop_desc = "ntpd"; #ifdef KERNEL_PLL if (pll_control) { ZERO(ntv); ntv.modes = MOD_FREQUENCY; if (kern_enable) { loop_desc = "kernel"; ntv.freq = DTOFREQ(drift_comp); } if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) { ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, 0, 0, __LINE__ - 1); } } #endif /* KERNEL_PLL */ mprintf_event(EVNT_FSET, NULL, "%s %.3f PPM", loop_desc, drift_comp * 1e6); }
/* * unpeer - remove peer structure from hash table and free structure */ void unpeer( struct peer *peer ) { mprintf_event(PEVNT_DEMOBIL, peer, "assoc %u", peer->associd); restrict_source(&peer->srcadr, 1, 0); set_peerdstadr(peer, NULL); peer_demobilizations++; peer_associations--; if (FLAG_PREEMPT & peer->flags) peer_preempt--; #ifdef REFCLOCK /* * If this peer is actually a clock, shut it down first */ if (FLAG_REFCLOCK & peer->flags) refclock_unpeer(peer); #endif free_peer(peer, TRUE); }
/* * stats_config - configure the stats operation */ void stats_config( int item, const char *invalue /* only one type so far */ ) { FILE *fp; const char *value; int len; double old_drift; l_fp now; time_t ttnow; #ifndef VMS const char temp_ext[] = ".TEMP"; #else const char temp_ext[] = "-TEMP"; #endif /* * Expand environment strings under Windows NT, since the * command interpreter doesn't do this, the program must. */ #ifdef SYS_WINNT char newvalue[MAX_PATH], parameter[MAX_PATH]; if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) { switch (item) { case STATS_FREQ_FILE: strlcpy(parameter, "STATS_FREQ_FILE", sizeof(parameter)); break; case STATS_LEAP_FILE: strlcpy(parameter, "STATS_LEAP_FILE", sizeof(parameter)); break; case STATS_STATSDIR: strlcpy(parameter, "STATS_STATSDIR", sizeof(parameter)); break; case STATS_PID_FILE: strlcpy(parameter, "STATS_PID_FILE", sizeof(parameter)); break; default: strlcpy(parameter, "UNKNOWN", sizeof(parameter)); break; } value = invalue; msyslog(LOG_ERR, "ExpandEnvironmentStrings(%s) failed: %m\n", parameter); } else { value = newvalue; } #else value = invalue; #endif /* SYS_WINNT */ switch (item) { /* * Open and read frequency file. */ case STATS_FREQ_FILE: if (!value || (len = strlen(value)) == 0) break; stats_drift_file = erealloc(stats_drift_file, len + 1); stats_temp_file = erealloc(stats_temp_file, len + sizeof(".TEMP")); memcpy(stats_drift_file, value, (size_t)(len+1)); memcpy(stats_temp_file, value, (size_t)len); memcpy(stats_temp_file + len, temp_ext, sizeof(temp_ext)); /* * Open drift file and read frequency. If the file is * missing or contains errors, tell the loop to reset. */ if ((fp = fopen(stats_drift_file, "r")) == NULL) break; if (fscanf(fp, "%lf", &old_drift) != 1) { msyslog(LOG_ERR, "format error frequency file %s", stats_drift_file); fclose(fp); break; } fclose(fp); loop_config(LOOP_FREQ, old_drift); prev_drift_comp = drift_comp; break; /* * Specify statistics directory. */ case STATS_STATSDIR: /* - 1 since value may be missing the DIR_SEP. */ if (strlen(value) >= sizeof(statsdir) - 1) { msyslog(LOG_ERR, "statsdir too long (>%d, sigh)", (int)sizeof(statsdir) - 2); } else { int add_dir_sep; int value_l; /* Add a DIR_SEP unless we already have one. */ value_l = strlen(value); if (0 == value_l) add_dir_sep = FALSE; else add_dir_sep = (DIR_SEP != value[value_l - 1]); if (add_dir_sep) snprintf(statsdir, sizeof(statsdir), "%s%c", value, DIR_SEP); else snprintf(statsdir, sizeof(statsdir), "%s", value); filegen_statsdir(); } break; /* * Open pid file. */ case STATS_PID_FILE: if ((fp = fopen(value, "w")) == NULL) { msyslog(LOG_ERR, "pid file %s: %m", value); break; } fprintf(fp, "%d", (int)getpid()); fclose(fp); break; /* * Read leapseconds file. * * Note: Currently a leap file without SHA1 signature is * accepted, but if there is a signature line, the signature * must be valid or the file is rejected. */ case STATS_LEAP_FILE: if (!value || (len = strlen(value)) == 0) break; leapfile_name = erealloc(leapfile_name, len + 1); memcpy(leapfile_name, value, len + 1); if (leapsec_load_file( leapfile_name, &leapfile_stat, TRUE, TRUE)) { leap_signature_t lsig; get_systime(&now); time(&ttnow); leapsec_getsig(&lsig); mprintf_event(EVNT_TAI, NULL, "%d leap %s %s %s", lsig.taiof, fstostr(lsig.ttime), leapsec_expired(now.l_ui, NULL) ? "expired" : "expires", fstostr(lsig.etime)); have_leapfile = TRUE; /* force an immediate daily expiration check of * the leap seconds table */ check_leap_expiration(TRUE, now.l_ui, &ttnow); } break; default: /* oh well */ break; } }
/* * acts_timeout - called on timeout */ static void acts_timeout( struct peer *peer, teModemState dstate ) { struct actsunit *up; struct refclockproc *pp; int fd; int rc; char device[20]; char lockfile[128], pidbuf[8]; /* * The state machine is driven by messages from the modem, * when first started and at timeout. */ pp = peer->procptr; up = pp->unitptr; switch (dstate) { /* * System poll event. Lock the modem port, open the device * and send the setup command. */ case S_IDLE: if (-1 != pp->io.fd) return; /* port is already open */ /* * Lock the modem port. If busy, retry later. Note: if * something fails between here and the close, the lock * file may not be removed. */ if (pp->sloppyclockflag & CLK_FLAG2) { snprintf(lockfile, sizeof(lockfile), LOCKFILE, up->unit); fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL, 0644); if (fd < 0) { report_event(PEVNT_CLOCK, peer, "acts: port busy"); return; } snprintf(pidbuf, sizeof(pidbuf), "%d\n", (u_int)getpid()); if (write(fd, pidbuf, strlen(pidbuf)) < 0) msyslog(LOG_ERR, "acts: write lock fails %m"); close(fd); } /* * Open the device in raw mode and link the I/O. */ snprintf(device, sizeof(device), DEVICE, up->unit); fd = refclock_open(device, SPEED232, LDISC_ACTS | LDISC_RAW | LDISC_REMOTE); if (fd < 0) { msyslog(LOG_ERR, "acts: open fails %m"); return; } pp->io.fd = fd; if (!io_addclock(&pp->io)) { msyslog(LOG_ERR, "acts: addclock fails"); close(fd); pp->io.fd = -1; return; } up->msgcnt = 0; up->bufptr = up->buf; /* * If the port is directly connected to the device, skip * the modem business and send 'T' for Spectrabum. */ if (sys_phone[up->retry] == NULL) { if (write(pp->io.fd, "T", 1) < 0) msyslog(LOG_ERR, "acts: write T fails %m"); up->state = S_MSG; up->timer = TIMECODE; return; } /* * Initialize the modem. This works with Hayes- * compatible modems. */ mprintf_event(PEVNT_CLOCK, peer, "SETUP %s", modem_setup); rc = write(pp->io.fd, modem_setup, strlen(modem_setup)); if (rc < 0) msyslog(LOG_ERR, "acts: write SETUP fails %m"); write(pp->io.fd, "\r", 1); up->state = S_SETUP; up->timer = SETUP; return; /* * In SETUP state the modem did not respond OK to setup string. */ case S_SETUP: report_event(PEVNT_CLOCK, peer, "no modem"); break; /* * In CONNECT state the call did not complete. Abort the call. */ case S_CONNECT: report_event(PEVNT_CLOCK, peer, "no answer"); break; /* * In MSG states no further timecodes are expected. If any * timecodes have arrived, update the clock. In any case, * terminate the call. */ case S_MSG: if (up->msgcnt == 0) { report_event(PEVNT_CLOCK, peer, "no timecodes"); } else { pp->lastref = pp->lastrec; record_clock_stats(&peer->srcadr, pp->a_lastcode); refclock_receive(peer); } break; } acts_close(peer); }
/* * acts_message - process message */ void acts_message( struct peer *peer, const char *msg ) { struct actsunit *up; struct refclockproc *pp; char tbuf[BMAX]; int dtr = TIOCM_DTR; DPRINTF(1, ("acts: %d %s\n", (int)strlen(msg), msg)); /* * What to do depends on the state and the first token in the * message. */ pp = peer->procptr; up = pp->unitptr; /* * Extract the first token in the line. */ strlcpy(tbuf, msg, sizeof(tbuf)); strtok(tbuf, " "); switch (up->state) { /* * We are waiting for the OK response to the modem setup * command. When this happens, dial the number followed. * If anything other than OK is received, just ignore it * and wait for timeoue. */ case S_SETUP: if (strcmp(tbuf, "OK") != 0) { /* * We disable echo with MODEM_SETUP's E0 but * if the modem was previously E1, we will * see MODEM_SETUP echoed before the OK/ERROR. * Ignore it. */ if (!strcmp(tbuf, modem_setup)) return; break; } mprintf_event(PEVNT_CLOCK, peer, "DIAL #%d %s", up->retry, sys_phone[up->retry]); if (ioctl(pp->io.fd, TIOCMBIS, &dtr) < 0) msyslog(LOG_ERR, "acts: ioctl(TIOCMBIS) failed: %m"); if (write(pp->io.fd, sys_phone[up->retry], strlen(sys_phone[up->retry])) < 0) msyslog(LOG_ERR, "acts: write DIAL fails %m"); write(pp->io.fd, "\r", 1); up->retry++; up->state = S_CONNECT; up->timer = ANSWER; return; /* * We are waiting for the CONNECT response to the dial * command. When this happens, listen for timecodes. If * somthing other than CONNECT is received, like BUSY * or NO CARRIER, abort the call. */ case S_CONNECT: if (strcmp(tbuf, "CONNECT") != 0) break; report_event(PEVNT_CLOCK, peer, msg); up->state = S_MSG; up->timer = TIMECODE; return; /* * We are waiting for a timecode response. Pass it to * the parser. If NO CARRIER is received, save the * messages and abort the call. */ case S_MSG: if (strcmp(tbuf, "NO") == 0) report_event(PEVNT_CLOCK, peer, msg); if (up->msgcnt < MAXCODE) acts_timecode(peer, msg); else acts_timeout(peer, S_MSG); return; } /* * Other response. Tell us about it. */ report_event(PEVNT_CLOCK, peer, msg); acts_close(peer); }