/* darkstattype.c - Program to decode darkstat's dumpfile. $Ximalas$ Tested on FreeBSD/amd64 stable/9 r263290 with clang 3.3. Copyright © 2014, Trond Endrestøl All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include const char *progname = NULL; const char *filename = NULL; FILE *file = NULL; bool follow_specification = false; int main(int argc, char **argv) { void show_usage(int exitcode); void show_version(void); void decode_file(void); int i; progname = argv[0]; opterr = 0; while ( (i = getopt(argc, argv, "fhv")) != -1) { switch (i) { case 'f': follow_specification = !follow_specification; break; case 'h': show_usage(EXIT_SUCCESS); break; case 'v': show_version(); break; case '?': /*FALLTHROUGH*/ default: fprintf(stderr, "%s: illegal option -%c\n\n", progname, optopt); show_usage(EXIT_FAILURE); break; } // switch } // while argc -= optind; argv += optind; if (*argv == NULL) { fprintf(stderr, "%s: missing filename\n\n", progname); show_usage(EXIT_FAILURE); } // if while (*argv != NULL) { filename = *argv; decode_file(); argv++; } // while return EXIT_SUCCESS; } // main() void show_usage(int exitcode) { fprintf((exitcode == EXIT_SUCCESS) ? stdout : stderr, "Usage: %s [-f] [-h] [-v] filename ...\n" " E.g.: %s darkstat.db\n\n" "Options:\n" "-f\tToggle follow strict specification, default is false.\n" "-h\tShow this help message and exit.\n" "-v\tShow version and copyright and exit.\n", progname, progname); exit(exitcode); } // show_usage() void show_version(void) { puts("darkstattype 1.0"); puts("$Ximalas$"); puts(""); puts("Copyright © 2014, Trond Endrestøl "); puts("All rights reserved."); puts(""); puts("Redistribution and use in source and binary forms, with or without"); puts("modification, are permitted provided that the following conditions are met:"); puts(""); puts("1. Redistributions of source code must retain the above copyright notice, this"); puts(" list of conditions and the following disclaimer."); puts("2. Redistributions in binary form must reproduce the above copyright notice,"); puts(" this list of conditions and the following disclaimer in the documentation"); puts(" and/or other materials provided with the distribution."); puts(""); puts("THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND"); puts("ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED"); puts("WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE"); puts("DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR"); puts("ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES"); puts("(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;"); puts("LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND"); puts("ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT"); puts("(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS"); puts("SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."); exit(EXIT_SUCCESS); } // show_version() uint8_t read8u(void); int8_t read8s(void); uint16_t read16u(void); int16_t read16s(void); uint32_t read32u(void); int32_t read32s(void); uint64_t read64u(void); int64_t read64s(void); void indent(void); void exdent(void); void print_indentation(void); void print_time_t(time_t t); void decode_file(void) { void decode_host_db_v1(void); void decode_graph_db_v1(void); uint32_t fileheader; uint32_t sectionheader; uint32_t i; if ( (file = fopen(filename, "rb")) == NULL) { fprintf(stderr, "%s: fopen(\"%s\") = %s (%d)\n", progname, filename, strerror(errno), errno); exit(EXIT_FAILURE); } // if #define FILE_HEADER_V1 0xDA314159U if ( (fileheader = read32u()) != FILE_HEADER_V1) { // not darkstat export format fprintf(stderr, "%s:%s:%ld: file header = 0x%x, not 0x%x\n", progname, filename, ftell(file), fileheader, FILE_HEADER_V1); exit(EXIT_FAILURE); } // if printf("File header 0x%x\n", fileheader); // Possible section header for host_db v1 and later graph_db v1. indent(); #define HOST_DB_V1 0xDA485301U #define GRAPH_DB_V1 0xDA475201U for (i = 0; i < 2; i++) { if ( (sectionheader = read32u()) == HOST_DB_V1) { print_indentation(); printf("Section header host_db v1 0x%x\n", sectionheader); decode_host_db_v1(); } // if else if (sectionheader == GRAPH_DB_V1) { print_indentation(); printf("Section header graph_db v1 0x%x\n", sectionheader); decode_graph_db_v1(); } // else if else { fprintf(stderr, "%s:%s:%ld: unknown section header = 0x%x, neither 0x%x nor 0x%x\n", progname, filename, ftell(file), sectionheader, HOST_DB_V1, GRAPH_DB_V1); exit(EXIT_FAILURE); } // else } // for exdent(); if (fclose(file) != 0) { fprintf(stderr, "%s: fclose(\"%s\") = %s (%d)\n", progname, filename, strerror(errno), errno); exit(EXIT_FAILURE); } // if } // decode_file() void decode_host_db_v1(void) { void decode_host_header_v1(void); void decode_host_header_v2(void); void decode_host_header_v3(void); uint32_t hostcount; uint32_t i; indent(); hostcount = read32u(); print_indentation(); printf("Host count %u\n", hostcount); for (i = 0; i < hostcount; i++) { uint32_t hostheader; print_indentation(); printf("Host #%u of %u:\n", i + 1, hostcount); indent(); #define HOST_HEADER_V3 0x48535403U #define HOST_HEADER_V2 0x48535402U #define HOST_HEADER_V1 0x48535401U if ( (hostheader = read32u()) == HOST_HEADER_V3) { // host header v3 print_indentation(); printf("Host header v3 0x%x\n", hostheader); decode_host_header_v3(); } // if else if (hostheader == HOST_HEADER_V2) { // host header v2 print_indentation(); printf("Host header v2 0x%x\n", hostheader); decode_host_header_v2(); } // else if else if (hostheader == HOST_HEADER_V1) { // host header v1 print_indentation(); printf("Host header v1 0x%x\n", hostheader); decode_host_header_v1(); } // else if else { // unknown host header version fprintf(stderr, "%s:%s:%ld: unknown host header = 0x%x, neither 0x%x nor 0x%x nor 0x%x\n", progname, filename, ftell(file), hostheader, HOST_HEADER_V3, HOST_HEADER_V2, HOST_HEADER_V1); exit(EXIT_FAILURE); } // else exdent(); } // for exdent(); } // decode_host_db_v1() void decode_protos_data(void); void decode_tcp_data(void); void decode_udp_data(void); void decode_host_header_v1(void) { uint8_t ipv4address[4]; uint8_t macaddress[6]; uint8_t hostnamelen; uint8_t hostname[256]; uint64_t bytesin; uint64_t bytesout; uint8_t protosdata; uint8_t tcpdata; uint8_t udpdata; uint8_t i; indent(); ipv4address[0] = read8u(); ipv4address[1] = read8u(); ipv4address[2] = read8u(); ipv4address[3] = read8u(); print_indentation(); printf("IPv4 address %u.%u.%u.%u\n", ipv4address[0], ipv4address[1], ipv4address[2], ipv4address[3]); macaddress[0] = read8u(); macaddress[1] = read8u(); macaddress[2] = read8u(); macaddress[3] = read8u(); macaddress[4] = read8u(); macaddress[5] = read8u(); print_indentation(); printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x\n", macaddress[0], macaddress[1], macaddress[2], macaddress[3], macaddress[4], macaddress[5]); hostnamelen = read8u(); print_indentation(); printf("Hostname length %u\n", hostnamelen); for (i = 0; i < hostnamelen; i++) { hostname[i] = read8u(); } // for hostname[i] = '\0'; print_indentation(); printf("Hostname %s\n", hostname); bytesin = read64u(); print_indentation(); printf("Bytes in %lu\n", bytesin); bytesout = read64u(); print_indentation(); printf("Bytes out %lu\n", bytesout); if ( (protosdata = read8u()) != 'P') { // missing protos data fprintf(stderr, "%s:%s:%ld: expecting character P, not %c\n", progname, filename, ftell(file), protosdata); exit(EXIT_FAILURE); } // if decode_protos_data(); if ( (tcpdata = read8u()) != 'T') { // missing tcp data fprintf(stderr, "%s:%s:%ld: expecting character T, not %c\n", progname, filename, ftell(file), tcpdata); exit(EXIT_FAILURE); } // if decode_tcp_data(); if ( (udpdata = read8u()) != 'U') { // missing udp data fprintf(stderr, "%s:%s:%ld: expecting character U, not %c\n", progname, filename, ftell(file), udpdata); exit(EXIT_FAILURE); } // if decode_udp_data(); exdent(); } // decode_host_header_v1 void decode_host_header_v2(void) { uint8_t ipv4address[4]; uint8_t macaddress[6]; int64_t lastseen; uint8_t hostnamelen; uint8_t hostname[256]; uint64_t bytesin; uint64_t bytesout; uint8_t protosdata; uint8_t tcpdata; uint8_t udpdata; uint8_t i; indent(); ipv4address[0] = read8u(); ipv4address[1] = read8u(); ipv4address[2] = read8u(); ipv4address[3] = read8u(); print_indentation(); printf("IPv4 address %u.%u.%u.%u\n", ipv4address[0], ipv4address[1], ipv4address[2], ipv4address[3]); if (follow_specification == true) { macaddress[0] = read8u(); macaddress[1] = read8u(); macaddress[2] = read8u(); macaddress[3] = read8u(); macaddress[4] = read8u(); macaddress[5] = read8u(); print_indentation(); printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x\n", macaddress[0], macaddress[1], macaddress[2], macaddress[3], macaddress[4], macaddress[5]); lastseen = read64s(); print_indentation(); printf("Last seen 0x%lx = %ld = ", lastseen, lastseen); print_time_t(lastseen); puts(""); } // if else { lastseen = read64s(); print_indentation(); printf("Last seen 0x%lx = %ld = ", lastseen, lastseen); print_time_t(lastseen); puts(""); macaddress[0] = read8u(); macaddress[1] = read8u(); macaddress[2] = read8u(); macaddress[3] = read8u(); macaddress[4] = read8u(); macaddress[5] = read8u(); print_indentation(); printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x\n", macaddress[0], macaddress[1], macaddress[2], macaddress[3], macaddress[4], macaddress[5]); } // else hostnamelen = read8u(); print_indentation(); printf("Hostname length %u\n", hostnamelen); for (i = 0; i < hostnamelen; i++) { hostname[i] = read8u(); } // for hostname[i] = '\0'; print_indentation(); printf("Hostname %s\n", hostname); bytesin = read64u(); print_indentation(); printf("Bytes in %lu\n", bytesin); bytesout = read64u(); print_indentation(); printf("Bytes out %lu\n", bytesout); if ( (protosdata = read8u()) != 'P') { // missing protos data fprintf(stderr, "%s:%s:%ld: expecting character P, not %c\n", progname, filename, ftell(file), protosdata); exit(EXIT_FAILURE); } // if decode_protos_data(); if ( (tcpdata = read8u()) != 'T') { // missing tcp data fprintf(stderr, "%s:%s:%ld: expecting character T, not %c\n", progname, filename, ftell(file), tcpdata); exit(EXIT_FAILURE); } // if decode_tcp_data(); if ( (udpdata = read8u()) != 'U') { // missing udp data fprintf(stderr, "%s:%s:%ld: expecting character U, not %c\n", progname, filename, ftell(file), udpdata); exit(EXIT_FAILURE); } // if decode_udp_data(); exdent(); } // decode_host_header_v2 void decode_host_header_v3(void) { uint8_t addressfamily; uint8_t ipv4address[4]; uint16_t ipv6address[8]; uint8_t macaddress[6]; int64_t lastseen; uint8_t hostnamelen; uint8_t hostname[256]; uint64_t bytesin; uint64_t bytesout; uint8_t protosdata; uint8_t tcpdata; uint8_t udpdata; uint8_t i; indent(); if ( (addressfamily = read8u()) == 0x04) { // IPv4 address print_indentation(); printf("IPv4 address family (0x%02x)\n", addressfamily); ipv4address[0] = read8u(); ipv4address[1] = read8u(); ipv4address[2] = read8u(); ipv4address[3] = read8u(); print_indentation(); printf("IPv4 address %u.%u.%u.%u\n", ipv4address[0], ipv4address[1], ipv4address[2], ipv4address[3]); } // if else if (addressfamily == 0x06) { // IPv6 address print_indentation(); printf("IPv6 address family (0x%02x)\n", addressfamily); ipv6address[0] = read16u(); ipv6address[1] = read16u(); ipv6address[2] = read16u(); ipv6address[3] = read16u(); ipv6address[4] = read16u(); ipv6address[5] = read16u(); ipv6address[6] = read16u(); ipv6address[7] = read16u(); print_indentation(); printf("IPv6 address %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", ipv6address[0], ipv6address[1], ipv6address[2], ipv6address[3], ipv6address[4], ipv6address[5], ipv6address[6], ipv6address[7]); } // else if else { // unknown address family fprintf(stderr, "%s:%s:%ld: unknown address family = 0x%x, neither 0x%x nor 0x%x\n", progname, filename, ftell(file), addressfamily, 0x04, 0x06); exit(EXIT_FAILURE); } // else if (follow_specification == true) { macaddress[0] = read8u(); macaddress[1] = read8u(); macaddress[2] = read8u(); macaddress[3] = read8u(); macaddress[4] = read8u(); macaddress[5] = read8u(); print_indentation(); printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x\n", macaddress[0], macaddress[1], macaddress[2], macaddress[3], macaddress[4], macaddress[5]); lastseen = read64s(); print_indentation(); printf("Last seen 0x%lx = %ld = ", lastseen, lastseen); print_time_t(lastseen); puts(""); } // if else { lastseen = read64s(); print_indentation(); printf("Last seen 0x%lx = %ld = ", lastseen, lastseen); print_time_t(lastseen); puts(""); macaddress[0] = read8u(); macaddress[1] = read8u(); macaddress[2] = read8u(); macaddress[3] = read8u(); macaddress[4] = read8u(); macaddress[5] = read8u(); print_indentation(); printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x\n", macaddress[0], macaddress[1], macaddress[2], macaddress[3], macaddress[4], macaddress[5]); } // else hostnamelen = read8u(); print_indentation(); printf("Hostname length %u\n", hostnamelen); for (i = 0; i < hostnamelen; i++) { hostname[i] = read8u(); } // for hostname[i] = '\0'; print_indentation(); printf("Hostname %s\n", hostname); bytesin = read64u(); print_indentation(); printf("Bytes in %lu\n", bytesin); bytesout = read64u(); print_indentation(); printf("Bytes out %lu\n", bytesout); if ( (protosdata = read8u()) != 'P') { // missing protos data fprintf(stderr, "%s:%s:%ld: expecting character P, not %c\n", progname, filename, ftell(file), protosdata); exit(EXIT_FAILURE); } // if decode_protos_data(); if ( (tcpdata = read8u()) != 'T') { // missing tcp data fprintf(stderr, "%s:%s:%ld: expecting character T, not %c\n", progname, filename, ftell(file), tcpdata); exit(EXIT_FAILURE); } // if decode_tcp_data(); if ( (udpdata = read8u()) != 'U') { // missing udp data fprintf(stderr, "%s:%s:%ld: expecting character U, not %c\n", progname, filename, ftell(file), udpdata); exit(EXIT_FAILURE); } // if decode_udp_data(); exdent(); } // decode_host_header_v3 void decode_protos_data(void) { uint8_t ipprotocount; uint8_t i; ipprotocount = read8u(); print_indentation(); printf("IP Proto count %u\n", ipprotocount); indent(); for (i = 0; i < ipprotocount; i++) { uint8_t proto; uint64_t in; uint64_t out; print_indentation(); printf("Protocol #%u of %u:\n", i + 1, ipprotocount); proto = read8u(); indent(); print_indentation(); printf("Protocol 0x%02x\n", proto); in = read64u(); print_indentation(); printf("In %lu\n", in); out = read64u(); print_indentation(); printf("Out %lu\n", out); exdent(); } // for exdent(); } // decode_protos_data(); void decode_tcp_data(void) { uint8_t tcpprotocount; uint16_t i; tcpprotocount = read16u(); print_indentation(); printf("TCP proto count %u\n", tcpprotocount); indent(); for (i = 0; i < tcpprotocount; i++) { uint16_t port; uint64_t syn; uint64_t in; uint64_t out; port = read16u(); print_indentation(); printf("Port %u:\n", port); syn = read64u(); indent(); print_indentation(); printf("SYN %lu\n", syn); in = read64u(); print_indentation(); printf("In %lu\n", in); out = read64u(); print_indentation(); printf("Out %lu\n", out); exdent(); } // for exdent(); } // decode_tcp_data() void decode_udp_data(void) { uint8_t udpprotocount; uint16_t i; udpprotocount = read16u(); print_indentation(); printf("UDP proto count %u\n", udpprotocount); indent(); for (i = 0; i < udpprotocount; i++) { uint16_t port; uint64_t in; uint64_t out; port = read16u(); print_indentation(); printf("Port %u:\n", port); in = read64u(); indent(); print_indentation(); printf("In %lu\n", in); out = read64u(); print_indentation(); printf("Out %lu\n", out); exdent(); } // for exdent(); } // decode_udp_data() void decode_graph_db_v1(void) { int64_t lasttime; uint32_t i; lasttime = read64s(); indent(); print_indentation(); printf("Last time 0x%lx = %ld = ", lasttime, lasttime); print_time_t(lasttime); puts(""); for (i = 0; i < 4; i++) { uint8_t nbars; uint8_t idxlastbar; uint32_t j; print_indentation(); printf("Graph #%u of 4:\n", i + 1); nbars = read8u(); indent(); print_indentation(); printf("Number of bars %u\n", nbars); idxlastbar = read8u(); print_indentation(); printf("Index of last bar %u\n", idxlastbar); indent(); for (j = 0; j < idxlastbar; j++) { uint64_t in; uint64_t out; print_indentation(); printf("Bar #%u of %u:\n", j + 1, idxlastbar); in = read64u(); indent(); print_indentation(); printf("In %lu\n", in); out = read64u(); print_indentation(); printf("Out %lu\n", out); exdent(); } // for exdent(); exdent(); } // for exdent(); } // decode_graph_db_v1() void handle_file_error(void); uint8_t read8u(void) { size_t r; uint8_t v; if ( (r = fread((void *)&v, sizeof(v), 1, file)) != 1) { handle_file_error(); } // if return v; } // read8u() int8_t read8s(void) { size_t r; int8_t v; if ( (r = fread((void *)&v, sizeof(v), 1, file)) != 1) { handle_file_error(); } // if return v; } // read8s() #ifdef __LITTLE_ENDIAN__ #include #endif uint16_t read16u(void) { size_t r; uint16_t v; if ( (r = fread((void *)&v, sizeof(v), 1, file)) != 1) { handle_file_error(); } // if #ifdef __LITTLE_ENDIAN__ v = ntohs(v); #endif return v; } // read16u() int16_t read16s(void) { size_t r; int16_t v; if ( (r = fread((void *)&v, sizeof(v), 1, file)) != 1) { handle_file_error(); } // if #ifdef __LITTLE_ENDIAN__ v = ntohs(v); #endif return v; } // read16s() uint32_t read32u(void) { size_t r; uint32_t v; if ( (r = fread((void *)&v, sizeof(v), 1, file)) != 1) { handle_file_error(); } // if #ifdef __LITTLE_ENDIAN__ v = ntohl(v); #endif return v; } // read32u() int32_t read32s(void) { size_t r; int32_t v; if ( (r = fread((void *)&v, sizeof(v), 1, file)) != 1) { handle_file_error(); } // if #ifdef __LITTLE_ENDIAN__ v = ntohl(v); #endif return v; } // read32s() uint64_t read64u(void) { size_t r; uint64_t v; #ifdef __LITTLE_ENDIAN__ uint64_t tmp; uint32_t *p1 = (uint32_t *)&v; uint32_t *p2 = (uint32_t *)&tmp; #endif if ( (r = fread((void *)&v, sizeof(v), 1, file)) != 1) { handle_file_error(); } // if #ifdef __LITTLE_ENDIAN__ p2[1] = ntohl(p1[0]); p2[0] = ntohl(p1[1]); v = tmp; #endif return v; } // read64u() int64_t read64s(void) { size_t r; int64_t v; #ifdef __LITTLE_ENDIAN__ int64_t tmp; int32_t *p1 = (int32_t *)&v; int32_t *p2 = (int32_t *)&tmp; #endif if ( (r = fread((void *)&v, sizeof(v), 1, file)) != 1) { handle_file_error(); } // if #ifdef __LITTLE_ENDIAN__ p2[1] = ntohl(p1[0]); p2[0] = ntohl(p1[1]); v = tmp; #endif return v; } // read64s() void handle_file_error(void) { int saved_errno = errno; if (feof(file) != 0) { fprintf(stderr, "%s:%s:%ld: premature end-of-file\n", progname, filename, ftell(file)); exit(EXIT_FAILURE); } // if if (ferror(file) != 0) { fprintf(stderr, "%s:%s:%ld: file error, errno = %s (%d)\n", progname, filename, ftell(file), strerror(saved_errno), saved_errno); exit(EXIT_FAILURE); } // if } // handle_file_error() int64_t indentation = 0LL; void indent(void) { if (indentation < (INT64_MAX - 2LL)) { indentation += 2LL; } // if } // indent() void exdent(void) { indentation -= 2LL; if (indentation < 0LL) { indentation = 0LL; }// if } // exdent() void print_indentation(void) { int64_t i; for (i = 0; i < indentation; i++) { putchar(' '); } // for } // print_indentation() void print_time_t(time_t t) { struct tm *stm; char buffer[1024]; stm = gmtime(&t); // ISO 8601 format if (strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S%z", stm) == 0) { int saved_errno = errno; fprintf(stderr, "%s:%s:%ld: strftime() error, errno = %s (%d)\n", progname, filename, ftell(file), strerror(saved_errno), saved_errno); exit(EXIT_FAILURE); } // if fputs(buffer, stdout); } // print_time_t() // darkstattype.c