/* * Copyright (c) 2011 EIA Electronics * * Authors: * Kurt Van Dijck * * This program is free software; you can redistribute it and/or modify * it under the terms of the version 2 of the GNU General Public License * as published by the Free Software Foundation */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libj1939.h" /* * getopt */ static const char help_msg[] = "jsr: An SAE J1939 send/recv utility" "\n" "Usage: jsr [OPTION...] SOURCE [DEST]" "\n" "\n" " -v, --verbose Increase verbosity" "\n" " -p, --priority=VAL J1939 priority (0..7, default 6)" "\n" " -S, --serialize Strictly serialize outgoing packets" "\n" " -s, --size Packet size, default autodetected" "\n" "\n" " SOURCE [IFACE:][NAME|SA][,PGN]" "\n" " DEST [NAME|SA]" "\n" ; #ifdef _GNU_SOURCE static struct option long_opts[] = { { "help", no_argument, NULL, '?', }, { "verbose", no_argument, NULL, 'v', }, { "priority", required_argument, NULL, 'p', }, { "size", required_argument, NULL, 's', }, { "serialize", no_argument, NULL, 'S', }, { }, }; #else #define getopt_long(argc, argv, optstring, longopts, longindex) \ getopt((argc), (argv), (optstring)) #endif static const char optstring[] = "vp:s:S?"; /* * static variables: configurations */ static struct { int verbose; int sendflags; /* flags for sendto() */ int pkt_len; int priority; int defined; #define DEF_SRC 1 #define DEF_DST 2 #define DEF_PRIO 4 struct sockaddr_can src, dst; } s = { .priority = 6, .src.can_addr.j1939 = { .name = J1939_NO_NAME, .addr = J1939_NO_ADDR, .pgn = J1939_NO_PGN, }, .dst.can_addr.j1939 = { .name = J1939_NO_NAME, .addr = J1939_NO_ADDR, .pgn = J1939_NO_PGN, }, }; int main(int argc, char **argv) { int ret, sock, opt; unsigned int len; struct pollfd pfd[2]; uint8_t *buf; #ifdef _GNU_SOURCE program_invocation_name = program_invocation_short_name; #endif /* argument parsing */ while ((opt = getopt_long(argc, argv, optstring, long_opts, NULL)) != -1) switch (opt) { case 'v': ++s.verbose; break; case 's': s.pkt_len = strtoul(optarg, 0, 0); if (!s.pkt_len) error(1, EINVAL, "packet size of %s", optarg); break; case 'p': s.priority = strtoul(optarg, 0, 0); s.defined |= DEF_PRIO; break; case 'S': s.sendflags |= MSG_SYN; break; default: fputs(help_msg, stderr); exit(1); break; } if (argv[optind]) { optarg = argv[optind++]; ret = libj1939_str2addr(optarg, 0, &s.src); if (ret < 0) error(1, 0, "bad address spec [%s]", optarg); s.defined |= DEF_SRC; } if (argv[optind]) { optarg = argv[optind++]; ret = libj1939_str2addr(optarg, 0, &s.dst); if (ret < 0) error(1, 0, "bad address spec [%s]", optarg); s.defined |= DEF_DST; } if (!s.pkt_len) { struct stat st; if (fstat(STDIN_FILENO, &st) < 0) error(1, errno, "stat stdin, could not determine buffer size"); s.pkt_len = st.st_size ?: 1024; } /* prepare */ buf = malloc(s.pkt_len); if (!buf) error(1, errno, "malloc %u", s.pkt_len); sock = socket(PF_CAN, SOCK_DGRAM, CAN_J1939); if (sock < 0) error(1, errno, "socket(can, dgram, j1939)"); if (s.defined & DEF_PRIO) { ret = setsockopt(sock, SOL_CAN_J1939, SO_J1939_SEND_PRIO, &s.priority, sizeof(s.priority)); if (ret < 0) error(1, errno, "setsockopt priority"); } if (s.defined & DEF_SRC) { s.src.can_family = AF_CAN; ret = bind(sock, (void *)&s.src, sizeof(s.src)); if (ret < 0) error(1, errno, "bind(%s), %i", libj1939_addr2str(&s.src), -errno); } if (s.defined & DEF_DST) { s.dst.can_family = AF_CAN; ret = connect(sock, (void *)&s.dst, sizeof(s.dst)); if (ret < 0) error(1, errno, "connect(%s), %i", libj1939_addr2str(&s.dst), -errno); } pfd[0].fd = STDIN_FILENO; pfd[0].events = POLLIN; pfd[1].fd = sock; pfd[1].events = POLLIN; /* run */ while (1) { ret = poll(pfd, sizeof(pfd)/sizeof(pfd[0]), -1); if (ret < 0) { if (errno == EINTR) continue; error(1, errno, "poll()"); } if (pfd[0].revents) { ret = read(pfd[0].fd, buf, s.pkt_len); if (ret < 0) error(1, errno, "read(stdin)"); if (!ret) break; len = ret; do { ret = send(pfd[1].fd, buf, len, s.sendflags); if (ret < 0) error(errno != ENOBUFS, errno, "write(%s)", libj1939_addr2str(&s.src)); } while (ret < 0); } if (pfd[1].revents) { ret = read(pfd[1].fd, buf, s.pkt_len); if (ret < 0) { ret = errno; error(0, errno, "read(%s)", libj1939_addr2str(&s.dst)); switch (ret) { case EHOSTDOWN: break; default: exit(1); } } else { write(STDOUT_FILENO, buf, ret); } } } free(buf); return 0; }