commit
0083a3762b
|
|
@ -41,6 +41,7 @@ GNUmakefile.in
|
|||
/isotpsniffer
|
||||
/isotptun
|
||||
/jacd
|
||||
/jcat
|
||||
/jspy
|
||||
/jsr
|
||||
/log2asc
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ set(PROGRAMS_CANLIB
|
|||
|
||||
set(PROGRAMS_J1939
|
||||
jacd
|
||||
jcat
|
||||
jspy
|
||||
jsr
|
||||
testj1939
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ bin_PROGRAMS = \
|
|||
isotpsniffer \
|
||||
isotptun \
|
||||
jacd \
|
||||
jcat \
|
||||
jspy \
|
||||
jsr \
|
||||
log2asc \
|
||||
|
|
@ -67,6 +68,7 @@ bin_PROGRAMS = \
|
|||
testj1939
|
||||
|
||||
jacd_LDADD = libj1939.la
|
||||
jcat_LDADD = libj1939.la
|
||||
jspy_LDADD = libj1939.la
|
||||
jsr_LDADD = libj1939.la
|
||||
testj1939_LDADD = libj1939.la
|
||||
|
|
|
|||
4
Makefile
4
Makefile
|
|
@ -55,7 +55,7 @@ CPPFLAGS += -Iinclude \
|
|||
PROGRAMS_ISOTP = isotpdump isotprecv isotpsend isotpsniffer isotptun isotpserver isotpperf
|
||||
PROGRAMS_CANGW = cangw
|
||||
PROGRAMS_SLCAN = slcan_attach slcand
|
||||
PROGRAMS_J1939 = jacd jspy jsr testj1939
|
||||
PROGRAMS_J1939 = jacd jcat jspy jsr testj1939
|
||||
PROGRAMS = can-calc-bit-timing candump cansniffer cansend canplayer cangen canbusload\
|
||||
log2long log2asc asc2log\
|
||||
canlogserver bcmserver\
|
||||
|
|
@ -87,6 +87,7 @@ log2long.o: lib.h
|
|||
log2asc.o: lib.h
|
||||
asc2log.o: lib.h
|
||||
jacd.o: libj1939.h
|
||||
jcat.o: libj1939.h
|
||||
jspy.o: libj1939.h
|
||||
jsr.o: libj1939.h
|
||||
testj1939.o: libj1939.h
|
||||
|
|
@ -102,6 +103,7 @@ log2asc: log2asc.o lib.o
|
|||
asc2log: asc2log.o lib.o
|
||||
canbusload: canbusload.o canframelen.o
|
||||
jacd: jacd.o libj1939.o
|
||||
jcat: jcat.o libj1939.o
|
||||
jspy: jspy.o libj1939.o
|
||||
jsr: jsr.o libj1939.o
|
||||
testj1939: testj1939.o libj1939.o
|
||||
|
|
|
|||
|
|
@ -0,0 +1,369 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2018 Pengutronix, Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <net/if.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "libj1939.h"
|
||||
#define J1939_MAX_ETP_PACKET_SIZE (7 * 0x00ffffff)
|
||||
#define JCAT_BUF_SIZE (1000 * 1024)
|
||||
|
||||
/*
|
||||
* min()/max()/clamp() macros that also do
|
||||
* strict type-checking.. See the
|
||||
* "unnecessary" pointer comparison.
|
||||
*/
|
||||
#define min(x, y) ({ \
|
||||
typeof(x) _min1 = (x); \
|
||||
typeof(y) _min2 = (y); \
|
||||
(void) (&_min1 == &_min2); \
|
||||
_min1 < _min2 ? _min1 : _min2; })
|
||||
|
||||
struct jcat_priv {
|
||||
int sock;
|
||||
int infile;
|
||||
int outfile;
|
||||
unsigned int todo_send;
|
||||
int todo_prio;
|
||||
|
||||
bool valid_peername;
|
||||
bool todo_recv;
|
||||
bool todo_filesize;
|
||||
bool todo_connect;
|
||||
|
||||
struct sockaddr_can sockname;
|
||||
struct sockaddr_can peername;
|
||||
};
|
||||
|
||||
static const char help_msg[] =
|
||||
"jcat: netcat tool for j1939\n"
|
||||
"Usage: jcat FROM TO\n"
|
||||
" FROM / TO - or [IFACE][:[SA][,[PGN][,NAME]]]\n"
|
||||
"Options:\n"
|
||||
" -i <infile> (default stdin)\n"
|
||||
" -s[=LEN] Initial send of LEN bytes dummy data\n"
|
||||
" -r Receive data\n"
|
||||
"\n"
|
||||
"Example:\n"
|
||||
"jcat -i some_file_to_send can0:0x80 :0x90,0x12300\n"
|
||||
"jcat can0:0x90 -r > /tmp/some_file_to_receive\n"
|
||||
"\n"
|
||||
;
|
||||
|
||||
static const char optstring[] = "?i:vs::rp:cnw::";
|
||||
|
||||
|
||||
static void jcat_init_sockaddr_can(struct sockaddr_can *sac)
|
||||
{
|
||||
sac->can_family = AF_CAN;
|
||||
sac->can_addr.j1939.addr = J1939_NO_ADDR;
|
||||
sac->can_addr.j1939.name = J1939_NO_NAME;
|
||||
sac->can_addr.j1939.pgn = J1939_NO_PGN;
|
||||
}
|
||||
|
||||
static int jcat_sendfile(struct jcat_priv *priv, int out_fd, int in_fd,
|
||||
off_t *offset, size_t count)
|
||||
{
|
||||
int ret = EXIT_SUCCESS;
|
||||
off_t orig = 0;
|
||||
char *buf;
|
||||
size_t to_read, num_read, num_sent, tot_sent, buf_size;
|
||||
int round = 0;
|
||||
|
||||
buf_size = min((size_t)J1939_MAX_ETP_PACKET_SIZE, count);
|
||||
buf = malloc(buf_size);
|
||||
if (!buf) {
|
||||
warn("can't allocate buf");
|
||||
ret = EXIT_FAILURE;
|
||||
goto do_nofree;
|
||||
}
|
||||
|
||||
if (!offset) {
|
||||
|
||||
/* Save current file offset and set offset to value in '*offset' */
|
||||
|
||||
orig = lseek(in_fd, 0, SEEK_CUR);
|
||||
if (orig == -1) {
|
||||
ret = EXIT_FAILURE;
|
||||
goto do_free;
|
||||
}
|
||||
if (lseek(in_fd, *offset, SEEK_SET) == -1) {
|
||||
ret = EXIT_FAILURE;
|
||||
goto do_free;
|
||||
}
|
||||
}
|
||||
|
||||
tot_sent = 0;
|
||||
|
||||
while (count > 0) {
|
||||
to_read = min(buf_size, count);
|
||||
|
||||
num_read = read(in_fd, buf, to_read);
|
||||
if (num_read == -1) {
|
||||
ret = EXIT_FAILURE;
|
||||
goto do_free;
|
||||
}
|
||||
if (num_read == 0)
|
||||
break; /* EOF */
|
||||
|
||||
if (priv->valid_peername && !priv->todo_connect)
|
||||
num_sent = sendto(out_fd, buf, num_read, 0,
|
||||
(void *)&priv->peername,
|
||||
sizeof(priv->peername));
|
||||
else
|
||||
num_sent = send(out_fd, buf, num_read, 0);
|
||||
|
||||
if (num_sent == -1) {
|
||||
warn("sendfile: write() transferr error");
|
||||
ret = EXIT_FAILURE;
|
||||
goto do_free;
|
||||
}
|
||||
|
||||
if (num_sent == 0) /* Should never happen */ {
|
||||
warn("sendfile: write() transferred 0 bytes");
|
||||
ret = EXIT_FAILURE;
|
||||
goto do_free;
|
||||
}
|
||||
|
||||
if (num_sent != num_read) {
|
||||
warn("sendfile: write() not full transfer: %zi %zi",
|
||||
num_sent, num_read);
|
||||
ret = EXIT_FAILURE;
|
||||
goto do_free;
|
||||
}
|
||||
|
||||
round++;
|
||||
count -= num_sent;
|
||||
tot_sent += num_sent;
|
||||
}
|
||||
|
||||
if (!offset) {
|
||||
/* Return updated file offset in '*offset', and reset the file offset
|
||||
to the value it had when we were called. */
|
||||
|
||||
*offset = lseek(in_fd, 0, SEEK_CUR);
|
||||
if (*offset == -1) {
|
||||
ret = EXIT_FAILURE;
|
||||
goto do_free;
|
||||
}
|
||||
|
||||
if (lseek(in_fd, orig, SEEK_SET) == -1) {
|
||||
ret = EXIT_FAILURE;
|
||||
goto do_free;
|
||||
}
|
||||
}
|
||||
|
||||
do_free:
|
||||
free(buf);
|
||||
do_nofree:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t jcat_get_file_size(int fd)
|
||||
{
|
||||
off_t offset;
|
||||
|
||||
offset = lseek(fd, 0, SEEK_END);
|
||||
if (offset == -1)
|
||||
error(1, errno, "%s lseek()\n", __func__);
|
||||
|
||||
if (lseek(fd, 0, SEEK_SET) == -1)
|
||||
error(1, errno, "%s lseek() start\n", __func__);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int jcat_send(struct jcat_priv *priv)
|
||||
{
|
||||
|
||||
if (priv->todo_filesize)
|
||||
priv->todo_send = jcat_get_file_size(priv->infile);
|
||||
|
||||
if (!priv->todo_send)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
return jcat_sendfile(priv, priv->sock, priv->infile, NULL,
|
||||
priv->todo_send);
|
||||
}
|
||||
|
||||
static int jcat_recv_one(struct jcat_priv *priv, uint8_t *buf, size_t buf_size)
|
||||
{
|
||||
socklen_t peernamelen;
|
||||
int ret;
|
||||
|
||||
peernamelen = sizeof(priv->peername);
|
||||
ret = recvfrom(priv->sock, buf, buf_size, 0,
|
||||
(void *)&priv->peername, &peernamelen);
|
||||
if (ret < 0) {
|
||||
warn("recvfrom()");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ret = write(priv->outfile, buf, ret);
|
||||
if (ret < 0) {
|
||||
warn("write stdout()");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int jcat_recv(struct jcat_priv *priv)
|
||||
{
|
||||
int ret = EXIT_SUCCESS;
|
||||
size_t buf_size;
|
||||
uint8_t *buf;
|
||||
|
||||
buf_size = J1939_MAX_ETP_PACKET_SIZE;
|
||||
buf = malloc(buf_size);
|
||||
if (!buf) {
|
||||
warn("can't allocate rx buf");
|
||||
return EXIT_FAILURE;;
|
||||
}
|
||||
|
||||
while (priv->todo_recv) {
|
||||
ret = jcat_recv_one(priv, buf, buf_size);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int jcat_sock_prepare(struct jcat_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* open socket */
|
||||
priv->sock = socket(PF_CAN, SOCK_DGRAM, CAN_J1939);
|
||||
if (priv->sock < 0) {
|
||||
warn("socket(j1939)");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (priv->todo_prio >= 0) {
|
||||
ret = setsockopt(priv->sock, SOL_CAN_J1939, SO_J1939_SEND_PRIO,
|
||||
&priv->todo_prio, sizeof(priv->todo_prio));
|
||||
if (ret < 0) {
|
||||
warn("set priority %i", priv->todo_prio);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
ret = bind(priv->sock, (void *)&priv->sockname, sizeof(priv->sockname));
|
||||
if (ret < 0) {
|
||||
warn("bind()");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!priv->todo_connect)
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
if (!priv->valid_peername) {
|
||||
warn("no peername supplied");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
ret = connect(priv->sock, (void *)&priv->peername,
|
||||
sizeof(priv->peername));
|
||||
if (ret < 0) {
|
||||
warn("connect()");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int jcat_parse_args(struct jcat_priv *priv, int argc, char *argv[])
|
||||
{
|
||||
int opt;
|
||||
|
||||
/* argument parsing */
|
||||
while ((opt = getopt(argc, argv, optstring)) != -1)
|
||||
switch (opt) {
|
||||
case 'i':
|
||||
priv->infile = open(optarg, O_RDONLY);
|
||||
if (priv->infile == -1)
|
||||
error(EXIT_FAILURE, errno, "can't open input file");
|
||||
priv->todo_filesize = 1;
|
||||
break;
|
||||
case 's':
|
||||
priv->todo_send = strtoul(optarg ?: "8", NULL, 0);
|
||||
break;
|
||||
case 'r':
|
||||
priv->todo_recv = 1;
|
||||
break;
|
||||
case 'p':
|
||||
priv->todo_prio = strtoul(optarg, NULL, 0);
|
||||
break;
|
||||
case 'c':
|
||||
priv->todo_connect = 1;
|
||||
break;
|
||||
default:
|
||||
fputs(help_msg, stderr);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (argv[optind]) {
|
||||
if (strcmp("-", argv[optind]))
|
||||
libj1939_parse_canaddr(argv[optind], &priv->sockname);
|
||||
optind++;
|
||||
}
|
||||
|
||||
if (argv[optind]) {
|
||||
if (strcmp("-", argv[optind])) {
|
||||
libj1939_parse_canaddr(argv[optind], &priv->peername);
|
||||
priv->valid_peername = 1;
|
||||
}
|
||||
optind++;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct jcat_priv *priv;
|
||||
int ret;
|
||||
|
||||
priv = malloc(sizeof(*priv));
|
||||
if (!priv)
|
||||
error(EXIT_FAILURE, errno, "can't allocate priv");
|
||||
|
||||
bzero(priv, sizeof(*priv));
|
||||
|
||||
priv->todo_prio = -1;
|
||||
priv->infile = STDIN_FILENO;
|
||||
priv->outfile = STDOUT_FILENO;
|
||||
|
||||
jcat_init_sockaddr_can(&priv->sockname);
|
||||
jcat_init_sockaddr_can(&priv->peername);
|
||||
|
||||
ret = jcat_parse_args(priv, argc, argv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = jcat_sock_prepare(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (priv->todo_recv)
|
||||
ret = jcat_recv(priv);
|
||||
else
|
||||
ret = jcat_send(priv);
|
||||
|
||||
close(priv->infile);
|
||||
return ret;
|
||||
}
|
||||
|
||||
21
libj1939.c
21
libj1939.c
|
|
@ -92,6 +92,27 @@ static int libj1939_ifindex(const char *str)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void libj1939_parse_canaddr(char *spec, struct sockaddr_can *paddr)
|
||||
{
|
||||
char *str;
|
||||
|
||||
str = strsep(&spec, ":");
|
||||
if (strlen(str))
|
||||
paddr->can_ifindex = if_nametoindex(str);
|
||||
|
||||
str = strsep(&spec, ",");
|
||||
if (str && strlen(str))
|
||||
paddr->can_addr.j1939.addr = strtoul(str, NULL, 0);
|
||||
|
||||
str = strsep(&spec, ",");
|
||||
if (str && strlen(str))
|
||||
paddr->can_addr.j1939.pgn = strtoul(str, NULL, 0);
|
||||
|
||||
str = strsep(&spec, ",");
|
||||
if (str && strlen(str))
|
||||
paddr->can_addr.j1939.name = strtoul(str, NULL, 0);
|
||||
}
|
||||
|
||||
int libj1939_str2addr(const char *str, char **endp, struct sockaddr_can *can)
|
||||
{
|
||||
char *p;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
void libj1939_parse_canaddr(char *spec, struct sockaddr_can *paddr);
|
||||
extern int libj1939_str2addr(const char *str, char **endp, struct sockaddr_can *can);
|
||||
extern const char *libj1939_addr2str(const struct sockaddr_can *can);
|
||||
|
||||
|
|
|
|||
25
testj1939.c
25
testj1939.c
|
|
@ -50,27 +50,6 @@ static const char help_msg[] =
|
|||
|
||||
static const char optstring[] = "?vbos::rep:cnw::";
|
||||
|
||||
static void parse_canaddr(char *spec, struct sockaddr_can *paddr)
|
||||
{
|
||||
char *str;
|
||||
|
||||
str = strsep(&spec, ":");
|
||||
if (strlen(str))
|
||||
paddr->can_ifindex = if_nametoindex(str);
|
||||
|
||||
str = strsep(&spec, ",");
|
||||
if (str && strlen(str))
|
||||
paddr->can_addr.j1939.addr = strtoul(str, NULL, 0);
|
||||
|
||||
str = strsep(&spec, ",");
|
||||
if (str && strlen(str))
|
||||
paddr->can_addr.j1939.pgn = strtoul(str, NULL, 0);
|
||||
|
||||
str = strsep(&spec, ",");
|
||||
if (str && strlen(str))
|
||||
paddr->can_addr.j1939.name = strtoul(str, NULL, 0);
|
||||
}
|
||||
|
||||
static void onsigalrm(int sig)
|
||||
{
|
||||
error(0, 0, "exit as requested");
|
||||
|
|
@ -157,7 +136,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
if (argv[optind]) {
|
||||
if (strcmp("-", argv[optind]))
|
||||
parse_canaddr(argv[optind], &sockname);
|
||||
libj1939_parse_canaddr(argv[optind], &sockname);
|
||||
++optind;
|
||||
}
|
||||
|
||||
|
|
@ -166,7 +145,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
if (argv[optind]) {
|
||||
if (strcmp("-", argv[optind])) {
|
||||
parse_canaddr(argv[optind], &peername);
|
||||
libj1939_parse_canaddr(argv[optind], &peername);
|
||||
valid_peername = 1;
|
||||
}
|
||||
++optind;
|
||||
|
|
|
|||
Loading…
Reference in New Issue