diff --git a/.gitignore b/.gitignore index 8a9796be..3d15acee 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ libutil/util_rec_example libutil/util_scandir_example libzds/libzds.a libvmcp/vmcp_example +lsstp/lsstp mon_tools/mon_fsstatd mon_tools/mon_procd osasnmpd/osasnmpd diff --git a/Makefile b/Makefile index c474398d..cc277b8e 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \ vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \ systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \ - genprotimg + genprotimg lsstp SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS) diff --git a/lsstp/Makefile b/lsstp/Makefile new file mode 100644 index 00000000..c8fb605c --- /dev/null +++ b/lsstp/Makefile @@ -0,0 +1,18 @@ +include ../common.mak + +libs = $(rootdir)/libutil/libutil.a +all: lsstp + +lsstp: lsstp.o $(libs) + +install: all + $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) \ + $(DESTDIR)$(MANDIR)/man8 + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lsstp $(DESTDIR)$(BINDIR) + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 lsstp.8 \ + $(DESTDIR)$(MANDIR)/man8 + +clean: + rm -f *.o *~ lsstp core + +.PHONY: all install clean diff --git a/lsstp/lsstp.8 b/lsstp/lsstp.8 new file mode 100644 index 00000000..dc0b2662 --- /dev/null +++ b/lsstp/lsstp.8 @@ -0,0 +1,79 @@ +.\" Copyright 2020 IBM Corp. +.\" s390-tools is free software; you can redistribute it and/or modify +.\" it under the terms of the MIT license. See LICENSE for details. +.\" +.TH LSSTP 8 "Jul 2020" "s390-tools" "Linux Administrator's Manual" +.SH NAME +.B "lsstp " +\- tool to show STP system information +.SH SYNOPSIS +.BI "lsstp " + +.SH DESCRIPTION +.B lsstp +displays information about the currenta Server Time Protocol (STP) configuration +like Coordianted Timing Network (CTN) ID, Timing state and Leap seconds. +.SH OUTPUT +.TP +.B STP online +Whether the STP subsystem is online +.TP +.B CTN ID +The ID of the Coordinated Time Network. If it can be decoded as EBCDIC it will be shown as EBCDIC String, otherwise a hexadecimal representation is shown. +.TP +.B CTN Type +The type of timing network. +.IP +.B No CTN +STP is not configured for attachment to a CTN. +.IP +.B STP-only +STP is configured and attached to a CTN with only STP nodes. +.IP +.B Mixed +STP is configured and attached to a CTN which contains both STP and External Time Reference (ETR) nodes. +.TP +.B Stratum +The Stratum level of the STP clock. This indicates the number of servers in the timing path between the local STP clock and the selected primary time server. +.TP +.B Timing mode +.IP +.B Local +The Time-of-day (TOD) clock is stepped to the local hardware oscillator and is not steered by the STP facility. +.IP +.B ETR +The TOD clock is synchronized with an attached 9037 Sysplex Timer. +.IP +.B STP +The TOD clock is steered by the STP facility to maintain synchronization with a Coordinated Server Time (CST). +.IP +.B Uninitialized +The TOD clock is not initialized. The STP facility is allowed to perform a step adjustment to the TOD clock for synchronization. +.TP +.B Timing state +The synchronization state of the STP facilty. Can be unsynchronzied, synchronized and stopped. +.TP +.B DST offset +The Daylight savings time offset relative to UTC in minutes. +.TP +.B Timezone offset +The offset of the local time relative to UTC in minutes. +.TP +.B Time offset +The total time offset at the server. This field is only valid in mixed CTN configurations. +.TP +.B Active Leap seconds +The number of Leap seconds that are currently in effect at the STP facility. +.TP +.B Leap second at +If a Leap second insertion or deletetion is scheduled in the STP facility, this field will show the Day and Time of the scheduled change. +.SH OPTIONS +.TP +.BI "-v|--version" +Print version number. +.TP +.BI "-h|--help" +Print usage text. + +.SH AUTHORS +Sven Schnelle diff --git a/lsstp/lsstp.c b/lsstp/lsstp.c new file mode 100644 index 00000000..93926cf1 --- /dev/null +++ b/lsstp/lsstp.c @@ -0,0 +1,218 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lib/util_opt.h" +#include "lib/util_file.h" +#include "lib/util_prg.h" +#include "lib/util_path.h" + +static const struct util_prg prg = { + .desc = "Display STP system information", + .args = "", + .copyright_vec = { + { + .owner = "IBM Corp.", + .pub_first = 2020, + }, + UTIL_PRG_COPYRIGHT_END + } +}; + +static struct util_opt opt_vec[] = { + UTIL_OPT_HELP, + UTIL_OPT_VERSION, + UTIL_OPT_END +}; + +struct stp_parms { + uint64_t ctn_id; + unsigned int online; + unsigned int leap_seconds; + int leap_seconds_diff; + unsigned int leap_seconds_utc; + unsigned int stratum; + unsigned int ctn_type; + unsigned int timing_mode; + unsigned int timing_state; + int dst_offset; + int time_offset; + int time_zone_offset; +}; + +static int convert_ctn_id(char *in, char *out) +{ + iconv_t ic; + size_t inlen = sizeof(unsigned long long); + size_t outlen = sizeof(unsigned long long); + + ic = iconv_open("ISO-8859-1", "EBCDIC-US"); + if (ic == (iconv_t)-1) { + warn("Could not initialize EBCDIC to ISO-8859-1 conversion table"); + return -1; + } + + if (iconv(ic, &in, &inlen, (char **)&out, &outlen) == (size_t)-1) { + warn("Code page translation EBCDIC to ISO-8859-1 failed"); + iconv_close(ic); + return -1; + } + iconv_close(ic); + return 0; +} + +static const char *ctn_type_str(int type) +{ + switch (type) { + case 0: + return "No CTN defined"; + case 1: + return "STP-only"; + case 2: + return "mixed"; + default: + return "unknown"; + } +} + +static const char *tmd_to_str(int mode) +{ + switch (mode) { + case 0: + return "Local"; + case 1: + return "ETR"; + case 2: + return "STP"; + case 15: + return "Uninitialized"; + default: + return "unknown"; + } +} + +static const char *tst_to_str(int mode) +{ + switch (mode) { + case 0: + return "Unsynchronized"; + case 1: + return "Synchronized"; + case 2: + return "Physical clock stopped"; + default: + return "unknown"; + } +} + +static const char *yesno_str(int val) +{ + return val ? "yes" : "no"; +} + +#define read_sysfs_attr(attr, parm, func, base) \ + do { \ + path = util_path_sysfs("devices/system/stp/%s", attr); \ + ret = func(parm, base, path); \ + if (ret) { \ + fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); \ + free(path); \ + exit(EXIT_FAILURE); \ + } \ + free(path); \ + } while (0) + +int main(int argc, char **argv) +{ + struct stp_parms parm = { 0 }; + char ctn_id[32] = { 0 }; + char *path; + int ret, c; + + util_prg_init(&prg); + util_opt_init(opt_vec, NULL); + + for (;;) { + c = util_opt_getopt_long(argc, argv); + if (c == -1) + break; + + switch (c) { + case 'v': + util_prg_print_version(); + exit(EXIT_SUCCESS); + case 'h': + util_prg_print_help(); + util_opt_print_help(); + exit(EXIT_SUCCESS); + default: + fprintf(stderr, "Try 'lsstp --help' for more information.\n"); + exit(EXIT_FAILURE); + } + } + + read_sysfs_attr("online", &parm.online, util_file_read_ui, 10); + if (!parm.online) { + printf("STP disabled\n"); + goto out; + } + + read_sysfs_attr("ctn_id", &parm.ctn_id, util_file_read_ul, 16); + read_sysfs_attr("ctn_type", &parm.ctn_type, util_file_read_ui, 10); + read_sysfs_attr("stratum", &parm.stratum, util_file_read_ui, 10); + read_sysfs_attr("leap_seconds", &parm.leap_seconds, util_file_read_ui, 10); + read_sysfs_attr("timing_mode", &parm.timing_mode, util_file_read_ui, 10); + read_sysfs_attr("timing_state", &parm.timing_state, util_file_read_ui, 10); + read_sysfs_attr("dst_offset", &parm.dst_offset, util_file_read_i, 10); + read_sysfs_attr("time_offset", &parm.time_offset, util_file_read_i, 10); + read_sysfs_attr("time_zone_offset", &parm.time_zone_offset, util_file_read_i, 10); + + if (convert_ctn_id((char *)&parm.ctn_id, ctn_id)) + snprintf(ctn_id, sizeof(ctn_id)-1, "%016" PRIx64, parm.ctn_id); + + printf("STP online: %s\n" + "CTN ID: %s\n" + "CTN type: %s\n" + "Stratum: %d\n" + "Timing mode: %s\n" + "Timing state: %s\n" + "DST offset: %d\n" + "Timezone offset: %d\n" + "Time offset: %d\n" + "Active leap seconds: %d\n", + yesno_str(parm.online), + ctn_id, + ctn_type_str(parm.ctn_type), + parm.stratum, + tmd_to_str(parm.timing_mode), + tst_to_str(parm.timing_state), + parm.dst_offset, + parm.time_zone_offset, + parm.time_offset, + parm.leap_seconds); + + printf("Scheduled leap second: "); + + path = util_path_sysfs("devices/system/stp/leap_seconds_scheduled"); + if (util_file_read_va(path, "%d,%d", &parm.leap_seconds_utc, + &parm.leap_seconds_diff) == 2 && + parm.leap_seconds_diff && parm.leap_seconds_utc) { + time_t lsoup = parm.leap_seconds_utc; + + printf("%s at: %s UTC", + parm.leap_seconds_diff > 0 ? "insertion" : "deletion", + ctime(&lsoup)); + } else { + printf("-\n"); + } + free(path); + return 0; +out: + return 1; +}