/* * lsstp - Display STP system information * * Copyright IBM Corp. 2020, 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #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; }