-
Notifications
You must be signed in to change notification settings - Fork 2
/
logging-in.go
202 lines (177 loc) · 7.58 KB
/
logging-in.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/* Logging input server for the Internet of Chuffs.
*
* Copyright (C) u-blox Melbourn Ltd
* u-blox Melbourn Ltd, Melbourn, UK
*
* All rights reserved.
*
* This source file is the sole property of u-blox Melbourn Ltd.
* Reproduction or utilization of this source in whole or part is
* forbidden without the written consent of u-blox Melbourn Ltd.
*/
package main
/*
#cgo CFLAGS: -I.
#include "log_enum.h"
extern const char *gLogStrings;
extern const int gNumLogStrings;
*/
import "C"
import (
"unsafe"
"fmt"
"net"
"os"
"log"
"strings"
"time"
"encoding/binary"
)
//--------------------------------------------------------------------
// Types
//--------------------------------------------------------------------
// Struct to hold a log item
type LogItem struct {
Timestamp uint32 // in uSeconds
Enum uint32
Parameter uint32
}
//--------------------------------------------------------------------
// Constants
//--------------------------------------------------------------------
// The size of a single log entry in bytes
const LOG_ITEM_SIZE int = 12
// A minimum value for current Unix time
const UNIX_TIME_MIN uint32 = 1510827960
//--------------------------------------------------------------------
// Variables
//--------------------------------------------------------------------
// The base time of a log
var logTimeBase int64
// The timestamp at the base time above
var logTimestampAtBase int64
// The array of C strings as a slice
var cLogStrings []*C.char
//--------------------------------------------------------------------
// Functions
//--------------------------------------------------------------------
// Open two log files, the first for raw output the second for decoded output
func openLogFiles(directory string, clientIpAddress string) (*os.File, *os.File) {
// File name is the IP address of the client (port number removed),
// the dots replaced with dashes, followed by the UTC time
// so: 154-46-789-1_2017-11-17_15-35-01.log
baseFileName := fmt.Sprintf("%s%c%s_%s", directory, os.PathSeparator, strings.Replace(strings.Split(clientIpAddress, ":")[0], ".", "-", -1), time.Now().UTC().Format("2006-01-02_15-04-05"))
rawFileName := baseFileName + ".raw"
decodedFileName := baseFileName + ".log"
rawFile, err := os.Create(rawFileName)
if err != nil {
fmt.Fprintf(os.Stderr, "Error creating raw log file (%s).\n", err.Error())
}
decodedFile, err := os.Create(decodedFileName)
if err != nil {
fmt.Fprintf(os.Stderr, "Error creating decoded log file (%s).\n", err.Error())
}
return rawFile, decodedFile
}
// Handle a log item
func handleLogItem(itemIn []byte, decodedLogFile *os.File) {
var item LogItem
var itemString string
var enumString string
if len(itemIn) == LOG_ITEM_SIZE {
item.Timestamp = binary.LittleEndian.Uint32(itemIn[0:])
item.Enum = binary.LittleEndian.Uint32(itemIn[4:])
item.Parameter = binary.LittleEndian.Uint32(itemIn[8:])
// We have a log item, translate it to text
if item.Enum < uint32(C.gNumLogStrings) {
enumString = C.GoString(cLogStrings[item.Enum])
// If a current time marker arrives and it looks real then grab it and use it from now on
if (item.Enum == C.EVENT_CURRENT_TIME_UTC) && (item.Parameter > UNIX_TIME_MIN) && (logTimeBase == 0) {
logTimeBase = int64(item.Parameter)
logTimestampAtBase = int64(item.Timestamp)
}
} else {
enumString = fmt.Sprintf("unknown (%#x)", item.Enum)
}
microsecondTime := time.Unix(logTimeBase, (int64(item.Timestamp) - logTimestampAtBase) * 1000).UTC()
nanosecondTime := microsecondTime.Nanosecond()
timeString := fmt.Sprintf("%s_%07.3f", microsecondTime.Format("2006-01-02_15-04-05"), float64(nanosecondTime / 1000) / 1000)
itemString = fmt.Sprintf("%s: %s [%d] %d (%#x)\n", timeString, enumString, item.Enum, item.Parameter, item.Parameter)
} else {
itemString = fmt.Sprintf("!!! Item of wrong length (%d byte(s) when %d bytes expected) !!!", len(itemIn), LOG_ITEM_SIZE);
}
if (decodedLogFile != nil) && (itemString != "") {
decodedLogFile.WriteString(itemString)
}
}
// Run a TCP server forever
func loggingServer(port string, directory string) {
var decodedLogFile *os.File
var rawLogFile *os.File
var newServer net.Conn
var currentServer net.Conn
listener, err := net.Listen("tcp", ":" + port)
if err == nil {
defer listener.Close()
// Listen for a connection
for {
fmt.Printf("Logging server waiting for a [further] TCP connection on port %s.\n", port)
newServer, err = listener.Accept()
if err == nil {
if currentServer != nil {
currentServer.Close()
}
if rawLogFile != nil {
rawLogFile.Close()
}
if decodedLogFile != nil {
decodedLogFile.Close()
}
logTimeBase = 0;
logTimestampAtBase = 0;
currentServer = newServer
x, success := currentServer.(*net.TCPConn)
if success {
err1 := x.SetNoDelay(true)
if err1 != nil {
log.Printf("Unable to switch off Nagle algorithm (%s).\n", err1.Error())
}
} else {
log.Printf("Can't cast *net.Conn to *net.TCPConn in order to configure it.\n")
}
fmt.Printf("Logging connection made by %s.\n", currentServer.RemoteAddr().String())
rawLogFile, decodedLogFile = openLogFiles(directory, currentServer.RemoteAddr().String())
if decodedLogFile != nil {
// Process datagrams received items in a go routine
go func(server net.Conn) {
// Read log items until the connection is closed under us
line := make([]byte, LOG_ITEM_SIZE)
pos := 0;
for numBytesIn, err := server.Read(line[pos:]); (err == nil) && (numBytesIn > 0); numBytesIn, err = server.Read(line[pos:]) {
if pos + numBytesIn == len(line) {
handleLogItem(line[:pos + numBytesIn], decodedLogFile)
pos = 0;
} else {
pos = numBytesIn;
}
if rawLogFile != nil {
rawLogFile.Write(line[pos:numBytesIn])
}
}
fmt.Printf("[Logging connection to %s closed].\n", server.RemoteAddr().String())
}(currentServer)
}
} else {
fmt.Fprintf(os.Stderr, "Error accepting logging connection (%s).\n", err.Error())
}
}
} else {
fmt.Fprintf(os.Stderr, "Unable to listen for logging connections on port %s (%s).\n", port, err.Error())
}
}
// Run the logging input server; this function should never return
func operateLoggingInputServer(port string, directory string) {
// Convert the array of C strings into a slice to make it possible to index into it
cLogStrings = (*[1 << 30]*C.char)(unsafe.Pointer(&C.gLogStrings))[:C.gNumLogStrings]
loggingServer(port, directory)
}