void handleClientRequest(int i) { ssize_t r; char * p; size_t remain; if (stream[i].len >= sizeof(stream[i].buffer) - 2) { logAbort("Input line on stream %d too long: %s\n", i, stream[i].buffer); } remain = sizeof(stream[i].buffer) - stream[i].len - 2; logDebug("handleClientRequest: read i=%d\n", i); logDebug("read %s i=%d fd=%d len=%u remain=%u\n", streamTypeName[stream[i].type], i, stream[i].fd, stream[i].len, remain); r = read(stream[i].fd, stream[i].buffer + stream[i].len, remain); if (r <= 0) { logDebug("read %s i=%d fd=%d r=%d\n", streamTypeName[stream[i].type], i, stream[i].fd, r); if (stream[i].type == DATA_INPUT_STREAM) { logAbort("EOF on reading stdin\n"); } closeStream(i); return; } stream[i].len += r; stream[i].buffer[stream[i].len] = 0; while (stream[i].len > 0) { size_t len; p = strchr(stream[i].buffer, '\n'); if (!p) { break; } len = p - stream[i].buffer; sbAppendData(&tcpMessage, stream[i].buffer, len + 1); if (stream[i].type != DATA_INPUT_STREAM || stream[outputIdx].type == DATA_OUTPUT_COPY) { /* Send all TCP client input and the main stdin stream if the mode is -o */ /* directly to stdout */ sbAppendData(&outMessage, stream[i].buffer, len + 1); } *p = 0; if (storeMessage(stream[i].buffer, len)) { convertJSONToNMEA0183(&nmeaMessage, stream[i].buffer); } p++, len++; stream[i].len -= len; /* Now remove [buffer..p> == the entire line */ memmove(stream[i].buffer, p, stream[i].len + 1); } }
static enum ReadyDescriptor isready(int fd1, int fd2) { fd_set fds; struct timeval waitfor; int setsize; int r; enum ReadyDescriptor ret = 0; FD_ZERO(&fds); if (fd1 >= 0) { FD_SET(fd1, &fds); } if (fd2 >= 0) { FD_SET(fd2, &fds); } waitfor.tv_sec = timeout ? timeout : 10; waitfor.tv_usec = 0; if (fd1 > fd2) { setsize = fd1 + 1; } else { setsize = fd2 + 1; } r = select(setsize, &fds, 0, 0, &waitfor); if (r < 0) { logAbort("I/O error; restart by quit\n"); } if (r > 0) { if (fd1 >= 0 && FD_ISSET(fd1, &fds)) { ret |= FD1_Ready; } if (fd2 >= 0 && FD_ISSET(fd2, &fds)) { ret |= FD2_Ready; } } if (!ret && timeout) { logAbort("Timeout %ld seconds; restart by quit\n", timeout); } return ret; }
int64_t epoch(void) { struct timeval t; if (gettimeofday(&t, 0)) { logAbort("Error on obtaining wall clock\n"); } return (int64_t) t.tv_sec * 1000 + t.tv_usec / 1000; }
void die (char * t) { int e = errno; char * s = 0; if (e) { s = strerror(e); } if (s) { logAbort("%s: %s\n", t, s); } else { logAbort("%s\n", t); } }
static bool storeMessage(char * line, size_t len) { char *s, *e = 0, *e2; Message * m; int i, idx, k; int src, dst, prn = 0; Pgn * pgn; time_t now; char * key2 = 0; int valid; char value[16]; now = time(0); logDebug("storeMessage(\"%s\",%u)\n", line, len); if (!strstr(line, "\"fields\":")) { logDebug("Ignore: pgn %u without fields\n", prn); return false; } if (memcmp(line, "{\"timestamp", 11) != 0) { logDebug("Ignore: no timestamp: '%s'\n", line); return false; } if (memcmp(line + len - 2, "}}", 2) != 0) { logDebug("Ignore: no line end: '%s'\n", line); return false; } if (getJSONValue(line, "src", value, sizeof(value))) { sscanf(value, "%d", &src); } if (getJSONValue(line, "dst", value, sizeof(value))) { sscanf(value, "%d", &dst); } if (getJSONValue(line, "pgn", value, sizeof(value))) { sscanf(value, "%d", &prn); } idx = PrnToIdx(prn); logDebug("src=%d dst=%d prn=%d idx=%d\n", src, dst, prn, idx); if (idx < 0) { logError("Ignore: prn %d: '%s'\n", prn, line); return false; } /* Look for a secondary key */ for (k = 0; k < ARRAYSIZE(secondaryKeyList); k++) { s = strstr(line, secondaryKeyList[k]); if (s) { logDebug("Found 2nd key %d = %s\n", k, secondaryKeyList[k]); s += strlen(secondaryKeyList[k]); while (strchr(SKIP_CHARACTERS, *s)) { s++; } e = strchr(s, ' '); e2 = strchr(s, '"'); if (!e || e2 < e) { e = e2; } if (!e) { e = s + strlen(s); } if (e > s && e[-1] == ',') { e--; } key2 = malloc(e - s + 1); if (!key2) { logAbort("Out of memory allocating %u bytes", e - s); } memcpy(key2, s, e - s); key2[e - s] = 0; break; } } pgn = pgnIdx[idx]; if (!pgn) { if (maxPgnList == ARRAYSIZE(pgnList)) { logAbort("Too many PGNs\n"); } pgn = calloc(1, sizeof(Pgn) + sizeof(Message)); if (!pgn) { logAbort("Out of memory allocating %u bytes", sizeof(Pgn) + sizeof(Message)); } pgnIdx[idx] = pgn; pgnList[maxPgnList++] = &pgnIdx[idx]; logDebug("Storing new PGN %d in index %u\n", prn, idx); } if (!pgn->p_description) { pgn->p_prn = prn; s = strstr(line, "\"description\":"); if (s) { s = s + sizeof("\"description\":"); e = strchr(s, ':'); e2 = strchr(s, '"'); if (!e || e2 < e) { e = e2; } if (!e) { logDebug("Cannot find end of description in %s\n", s); return false; } logDebug("New PGN '%.*s'\n", e - s, s); pgn->p_description = malloc(e - s + 1); if (!pgn->p_description) { logAbort("Out of memory allocating %u bytes", e - s); } memcpy(pgn->p_description, s, e - s); pgn->p_description[e - s] = 0; } } /* Find existing key */ for (i = 0; i < pgn->p_maxSrc; i++) { if (pgn->p_message[i].m_src == src) { if (pgn->p_message[i].m_key2) { if (key2 && strcmp(pgn->p_message[i].m_key2, key2) == 0) { break; } } else { break; } } } /* Reuse expired key ? */ if (i == pgn->p_maxSrc) { for (i = 0; i < pgn->p_maxSrc; i++) { if (pgn->p_message[i].m_time < now) { pgn->p_message[i].m_src = (uint8_t) src; if (pgn->p_message[i].m_key2) { free(pgn->p_message[i].m_key2); } pgn->p_message[i].m_key2 = key2; key2 = 0; break; } } } /* Create new key */ if (i == pgn->p_maxSrc) { size_t newSize; pgn->p_maxSrc++; newSize = sizeof(Pgn) + pgn->p_maxSrc * sizeof(Message); pgn = realloc(pgnIdx[idx], newSize); if (!pgn) { logAbort("Out of memory allocating %u bytes", newSize); } pgnIdx[idx] = pgn; pgn->p_message[i].m_src = (uint8_t) src; pgn->p_message[i].m_key2 = key2; key2 = 0; pgn->p_message[i].m_text = 0; } m = &pgn->p_message[i]; if (m->m_text) { m->m_text = realloc(m->m_text, len + 2); } else { m->m_text = malloc(len + 2); } if (!m->m_text) { logAbort("Out of memory allocating %u bytes", len + 1); } memcpy(m->m_text, line, len); m->m_text[len] = '\n'; m->m_text[len + 1] = 0; if (prn == 60928 || prn == 126996) { valid = CLAIM_TIMEOUT; } else if (prn == 130816) { valid = SONICHUB_TIMEOUT; } else { valid = secondaryKeyTimeout[k]; } logDebug("stored prn %d timeout=%d 2ndKey=%d\n", prn, valid, k); if (key2) { free(key2); } m->m_time = now + valid; return true; }
void writeAllClients(void) { fd_set ws; struct timeval timeout = {0, 0}; int r; int i; SOCKET fd; int64_t now = 0; char * state = 0; logDebug("writeAllClients tcp.len=%d\n", tcpMessage.len); FD_ZERO(&ws); if (socketIdxMax >= 0) { ws = writeSet; r = select(socketFdMax + 1, 0, &ws, 0, &timeout); logDebug("write to %d streams (%u..%u fdMax=%d)\n", r, socketIdxMin, socketIdxMax, socketFdMax); for (i = socketIdxMin; r > 0 && i <= socketIdxMax; i++) { fd = stream[i].fd; if (fd < 0) { continue; } if (fd > 8192) { logAbort("Stream %d contains invalid fd %d\n", i, fd); } if (fd > socketFdMax) { logAbort("Inconsistent: fd[%u]=%d, fdMax=%d\n", i, fd, socketFdMax); } if (FD_ISSET(fd, &ws)) { logDebug("%s i=%u fd=%d writable=%d\n", streamTypeName[stream[i].type], i, fd, FD_ISSET(fd, &ws)); r--; if (!now) now = epoch(); switch (stream[i].type) { case CLIENT_JSON: if (stream[i].timeout && stream[i].timeout < now) { if (!state) { state = getFullStateJSON(); logDebug("json=%s", state); } write(fd, state, strlen(state)); logDebug("JSON: wrote %u to %d, closing\n", strlen(state), fd); closeStream(i); } break; case CLIENT_NMEA0183_STREAM: logDebug("NMEA-> %d\n", nmeaMessage.len); if (nmeaMessage.len) { write(fd, nmeaMessage.data, nmeaMessage.len); } break; case CLIENT_JSON_STREAM: if (tcpMessage.len) { write(fd, tcpMessage.data, tcpMessage.len); } break; case DATA_OUTPUT_STREAM: case DATA_OUTPUT_COPY: if (outMessage.len) { write(fd, outMessage.data, outMessage.len); } break; default: break; } } } } if (state) { free(state); } outMessage.len = 0; tcpMessage.len = 0; nmeaMessage.len = 0; # ifdef NEVER { static int count = 0; if (count++ > 100) { exit(1); } } # endif }
void handleClientRequest(int i) { ssize_t r; char * p; r = read(stream[i].fd, stream[i].buffer + stream[i].len, sizeof(stream[i].buffer) - 1 - stream[i].len); logDebug("read i=%d fd=%d r=%d\n", i, stream[i].fd, r); if (r < 0) { if (stream[i].fd == stdinfd) { logAbort("Read %d on reading stdin\n", r); } closeStream(i); return; } if (r == 0) { if (stream[i].type == DATA_INPUT_STREAM) { logAbort("EOF on reading stdin\n", r); } else { closeStream(i); } return; } stream[i].len += r; stream[i].buffer[stream[i].len] = 0; while (r > 0) { p = strchr(stream[i].buffer, '\n'); if (p) { size_t len = ++p - stream[i].buffer; /* Feed it into the NMEA2000 message handler */ char stash = *p; logDebug("Got msg='%1.*s'\n", len, stream[i].buffer); *p = 0; sbAppendData(&tcpMessage, stream[i].buffer, len); if (stream[i].type != DATA_INPUT_STREAM || stream[outputIdx].type == DATA_OUTPUT_COPY) { /* Send all TCP client input and the main stdin stream if the mode is -o */ /* directly to stdout */ sbAppendData(&outMessage, stream[i].buffer, len); } convertJSONToNMEA0183(&nmeaMessage, stream[i].buffer); storeMessage(stream[i].buffer, len); *p = stash; /* Now remove [buffer..p> */ memcpy(stream[i].buffer, p, strlen(p)); stream[i].len -= len; r -= len; } else { r = 0; } } }
void storeMessage(char * line, size_t len) { char *s, *e = 0, *e2; Message * m; int i, idx, k; int src, dst, prn = 0; Pgn * pgn; time_t now; char * key2 = 0; int valid; now = time(0); if (!strstr(line, "\"fields\":")) { #ifdef DEBUG logDebug("Ignore pgn %u without fields\n", prn); #endif return; } if (memcmp(line, "{\"timestamp", 11) != 0) { logDebug("Ignore '%s'\n", line); return; } if (memcmp(line + len - 3, "}}\n", 3) != 0) { logDebug("Ignore '%s' (end)\n", line); return; } s = strstr(line, "\"src\":"); if (s) { if (sscanf(s + sizeof("\"src\":"), "%u\",\"dst\":\"%u\",\"pgn\":\"%u\"", &src, &dst, &prn)) { #ifdef DEBUG logDebug("prn=%u src=%u\n", prn, src); #endif } } if (prn == 0 || prn > MAX_PGN) { return; } /* Look for a secondary key */ for (k = 0; k < ARRAYSIZE(secondaryKeyList); k++) { s = strstr(line, secondaryKeyList[k]); if (s) { s += strlen(secondaryKeyList[k]); while (strchr(SKIP_CHARACTERS, *s)) { s++; } e = strchr(s, ' '); e2 = strchr(s, '"'); if (!e || e2 < e) { e = e2; } if (!e) { e = s + strlen(s); } key2 = malloc(e - s + 1); if (!key2) { logAbort("Out of memory allocating %u bytes", e - s); } memcpy(key2, s, e - s); key2[e - s] = 0; } } idx = PrnToIdx(prn); if (idx < 0) { logAbort("PRN %d is out of range\n", prn); } pgn = pgnIdx[idx]; if (!pgn) { if (maxPgnList == ARRAYSIZE(pgnList)) { logAbort("Too many PGNs\n"); } pgn = calloc(1, sizeof(Pgn) + sizeof(Message)); if (!pgn) { logAbort("Out of memory allocating %u bytes", sizeof(Pgn) + sizeof(Message)); } pgnIdx[idx] = pgn; pgnList[maxPgnList++] = &pgnIdx[idx]; logDebug("Storing new PGN %d in index %u\n", prn, idx); } if (!pgn->p_description) { pgn->p_prn = prn; s = strstr(line, "\"description\":"); if (s) { s = s + sizeof("\"description\":"); e = strchr(s, ':'); e2 = strchr(s, '"'); if (!e || e2 < e) { e = e2; } if (!e) { logDebug("Cannot find end of description in %s\n", s); return; } logDebug("New PGN '%.*s'\n", e - s, s); pgn->p_description = malloc(e - s + 1); if (!pgn->p_description) { logAbort("Out of memory allocating %u bytes", e - s); } memcpy(pgn->p_description, s, e - s); pgn->p_description[e - s] = 0; } } /* Find existing key */ for (i = 0; i < pgn->p_maxSrc; i++) { if (pgn->p_message[i].m_src == src) { if (pgn->p_message[i].m_key2) { if (key2 && strcmp(pgn->p_message[i].m_key2, key2) == 0) { break; } } else { break; } } } /* Reuse expired key ? */ if (i == pgn->p_maxSrc) { for (i = 0; i < pgn->p_maxSrc; i++) { if (pgn->p_message[i].m_time < now) { pgn->p_message[i].m_src = (uint8_t) src; if (pgn->p_message[i].m_key2) { free(pgn->p_message[i].m_key2); } pgn->p_message[i].m_key2 = key2; key2 = 0; break; } } } /* Create new key */ if (i == pgn->p_maxSrc) { size_t newSize; pgn->p_maxSrc++; newSize = sizeof(Pgn) + pgn->p_maxSrc * sizeof(Message); pgn = realloc(pgnIdx[idx], newSize); if (!pgn) { logAbort("Out of memory allocating %u bytes", newSize); } pgnIdx[idx] = pgn; pgn->p_message[i].m_src = (uint8_t) src; pgn->p_message[i].m_key2 = key2; key2 = 0; pgn->p_message[i].m_text = 0; } m = &pgn->p_message[i]; if (m->m_text) { if (strlen(m->m_text) < len) { m->m_text = realloc(m->m_text, len + 1); } } else { m->m_text = malloc(len + 1); } if (!m->m_text) { logAbort("Out of memory allocating %u bytes", len + 1); } strcpy(m->m_text, line); if (prn == 126996) { valid = AIS_TIMEOUT; } else if (prn == 130816) { valid = SONICHUB_TIMEOUT; } else { valid = secondaryKeyTimeout[k]; } m->m_time = now + valid; }
int main(int argc, char **argv) { int r; int handle; struct termios attr; char * name = argv[0]; char * device = 0; struct stat statbuf; int pid = 0; int speed; setProgName(argv[0]); while (argc > 1) { if (strcasecmp(argv[1], "-version") == 0) { printf("%s\n", VERSION); exit(0); } else if (strcasecmp(argv[1], "-w") == 0) { writeonly = 1; } else if (strcasecmp(argv[1], "-p") == 0) { passthru = 1; } else if (strcasecmp(argv[1], "-r") == 0) { readonly = 1; } else if (strcasecmp(argv[1], "-v") == 0) { verbose = 1; } else if (strcasecmp(argv[1], "-t") == 0 && argc > 2) { argc--; argv++; timeout = strtol(argv[1], 0, 10); logDebug("timeout set to %ld seconds\n", timeout); } else if (strcasecmp(argv[1], "-s") == 0 && argc > 2) { argc--; argv++; speed = strtol(argv[1], 0, 10); switch (speed) { case 38400: baudRate = B38400; break; case 57600: baudRate = B57600; break; case 115200: baudRate = B115200; break; case 230400: baudRate = B230400; break; #ifdef B460800 case 460800: baudRate = B460800; break; #endif #ifdef B921600 case 921600: baudRate = B921600; break; #endif default: baudRate = speed; break; } logDebug("speed set to %d (%d) baud\n", speed, baudRate); } else if (strcasecmp(argv[1], "-d") == 0) { setLogLevel(LOGLEVEL_DEBUG); } else if (strcasecmp(argv[1], "-o") == 0) { outputCommands = 1; } else if (!device) { device = argv[1]; } else { device = 0; break; } argc--; argv++; } if (!device) { fprintf(stderr, "Usage: %s [-w] -[-p] [-r] [-v] [-d] [-s <n>] [-t <n>] device\n" "\n" "Options:\n" " -w writeonly mode, no data is read from device\n" " -r readonly mode, no data is sent to device\n" " -p passthru mode, data on stdin is sent to stdout but not to device\n" " -v verbose\n" " -d debug\n" " -s <n> set baudrate to 38400, 57600, 115200, 230400" #ifdef B460800 ", 460800" #endif #ifdef B921600 ", 921600" #endif "\n" " -t <n> timeout, if no message is received after <n> seconds the program quits\n" " -o output commands sent to stdin to the stdout \n" " <device> can be a serial device, a normal file containing a raw log,\n" " or the address of a TCP server in the format tcp://<host>[:<port>]\n" "\n" " Examples: %s /dev/ttyUSB0\n" " %s tcp://192.168.1.1:10001\n" "\n" COPYRIGHT, name, name, name); exit(1); } retry: logDebug("Opening %s\n", device); if (strncmp(device, "tcp:", STRSIZE("tcp:")) == 0) { handle = open_socket_stream(device); logDebug("socket = %d\n", handle); isFile = true; if (handle < 0) { fprintf(stderr, "Cannot open NGT-1-A TCP stream %s\n", device); exit(1); } } else { handle = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK); logDebug("fd = %d\n", handle); if (handle < 0) { logAbort("Cannot open NGT-1-A device %s\n", device); } if (fstat(handle, &statbuf) < 0) { logAbort("Cannot determine device %s\n", device); } isFile = S_ISREG(statbuf.st_mode); } if (isFile) { logInfo("Device is a normal file, do not set the attributes.\n"); } else { logDebug("Device is a serial port, set the attributes.\n"); memset(&attr, 0, sizeof(attr)); if (cfsetspeed(&attr, baudRate) < 0) { logAbort("Could not set baudrate %d\n", speed); } attr.c_cflag |= CS8 | CLOCAL | CREAD; attr.c_iflag |= IGNPAR; attr.c_cc[VMIN] = 1; attr.c_cc[VTIME] = 0; tcflush(handle, TCIFLUSH); tcsetattr(handle, TCSANOW, &attr); logDebug("Device is a serial port, send the startup sequence.\n"); writeMessage(handle, NGT_MSG_SEND, NGT_STARTUP_SEQ, sizeof(NGT_STARTUP_SEQ)); sleep(2); } for (;;) { unsigned char msg[BUFFER_SIZE]; size_t msgLen; int r = isReady(writeonly ? INVALID_SOCKET : handle, readonly ? INVALID_SOCKET : 0, INVALID_SOCKET, timeout); if ((r & FD1_ReadReady) > 0) { if (!readNGT1(handle)) { break; } } if ((r & FD2_ReadReady) > 0) { if (!readIn(msg, sizeof(msg))) { break; } if (!passthru) { parseAndWriteIn(handle, msg); } if (outputCommands) { fprintf(stdout, "%s", msg); fflush(stdout); } } else if (writeonly) { break; } } close(handle); return 0; }
int main(int argc, char ** argv) { int r; int handle; struct termios attr; char * device = 0; struct stat statbuf; int pid = 0; setProgName(argv[0]); while (argc > 1) { if (strcasecmp(argv[1], "-r") == 0) { readonly = 1; } else if (strcasecmp(argv[1], "-d") == 0) { setLogLevel(LOGLEVEL_DEBUG); } else if (strcasecmp(argv[1], "-?") == 0) { break; } else if (!device) { device = argv[1]; } else { device = 0; break; } argc--; argv++; } if (!device) { fprintf(stderr, "Usage: nmea0183-serial [-r] [-d] device\n\n" "-r : read-only, do not pass stdin to stdout\n" "-d : debug mode\n\n" "Example: nmea0183-serial /dev/ttyUSB0\n\n"COPYRIGHT); exit(1); } retry: logDebug("Opening %s\n", device); handle = open(device, O_RDWR | O_NOCTTY); logDebug("fd = %d\n", handle); if (handle < 0) { logAbort("NMEA-00001: Cannot open NMEA-0183 device %s\n", device); exit(1); } if (fstat(handle, &statbuf) < 0) { logAbort("NMEA-00002: Cannot determine device %s\n", device); exit(1); } isFile = S_ISREG(statbuf.st_mode); if (!isFile) { logDebug("Device is a serial port, set the attributes.\n"); memset(&attr, 0, sizeof(attr)); attr.c_cflag = B38400 | CS8 | CLOCAL | CREAD; attr.c_iflag = IGNPAR; attr.c_oflag = 0; attr.c_lflag = 0; attr.c_cc[VMIN] = 0; attr.c_cc[VTIME] = 1; tcflush(handle, TCIFLUSH); tcsetattr(handle, TCSANOW, &attr); } for (;;) { char msg[BUFFER_SIZE]; size_t msgLen; enum ReadyDescriptor r; int b; r = isready(handle, readonly ? -1 : 0); if ((r & FD1_Ready) > 0) { b = read(handle, msg, sizeof(msg)); if (b < 0) { break; } else if (b > 0) { if (write(1, msg, b) < b) { break; } } } if ((r & FD2_Ready) > 0) { b = read(0, msg, sizeof(msg)); if (b < 0) { break; } else if (b > 0) { if (write(1, msg, b) < b) { break; } if (write(handle, msg, b) < b) { break; } } } } close(handle); return 0; }