blob: e2ba2b4c75123a1506714f0a0393d8e07916a104 [file] [log] [blame]
Brian Silverman771b4b82013-04-12 16:36:56 -07001#include <stdio.h>
2#include <sys/types.h>
3#include <sys/socket.h>
4#include <netinet/in.h>
5#include <arpa/inet.h>
6#include <unistd.h>
7#include <fcntl.h>
8#include <pthread.h>
9#include <sys/stat.h>
10#include <assert.h>
11
12#include "aos/common/logging/logging_impl.h"
13#include "aos/common/Configuration.h"
14
15namespace aos {
16namespace {
17
18struct FDsToCopy {
19 const int input;
20 const int output;
Brian Silvermane1514fc2013-04-13 14:57:35 -070021
22 const struct sockaddr_in *const interface_address;
Brian Silverman771b4b82013-04-12 16:36:56 -070023};
24
25void *FDCopyThread(void *to_copy_in) {
26 FDsToCopy *to_copy = static_cast<FDsToCopy *>(to_copy_in);
27
28 char buffer[32768];
29 ssize_t position = 0;
30 while (true) {
31 assert(position >= 0);
32 assert(position <= static_cast<ssize_t>(sizeof(buffer)));
33 if (position != sizeof(buffer)) {
Brian Silvermane1514fc2013-04-13 14:57:35 -070034 ssize_t read_bytes;
35 bool good_data = true;
36 if (to_copy->interface_address != NULL) {
37 char control_buffer[0x100];
38 struct msghdr header;
39 memset(static_cast<void *>(&header), 0, sizeof(header));
40 header.msg_control = control_buffer;
41 header.msg_controllen = sizeof(control_buffer);
42 struct iovec iovecs[1];
43 iovecs[0].iov_base = buffer + position;
44 iovecs[0].iov_len = position - sizeof(buffer);
45 header.msg_iov = iovecs;
46 header.msg_iovlen = sizeof(iovecs) / sizeof(iovecs[0]);
47 read_bytes = recvmsg(to_copy->input, &header, 0);
48 if (read_bytes != -1) {
49 for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&header);
50 cmsg != NULL;
51 cmsg = CMSG_NXTHDR(&header, cmsg)) {
52 if (cmsg->cmsg_level == IPPROTO_IP &&
53 cmsg->cmsg_type == IP_PKTINFO) {
Brian Silverman1c8670d2013-04-13 18:59:13 -070054 unsigned char *data = CMSG_DATA(cmsg);
55 struct in_pktinfo *pktinfo;
56 memcpy(&pktinfo, &data, sizeof(void *));
Brian Silvermane1514fc2013-04-13 14:57:35 -070057 good_data = pktinfo->ipi_spec_dst.s_addr ==
58 to_copy->interface_address->sin_addr.s_addr;
59 }
60 }
61 }
62 } else {
63 read_bytes = read(to_copy->input,
64 buffer + position, position - sizeof(buffer));
65 }
Brian Silverman771b4b82013-04-12 16:36:56 -070066 if (read_bytes == -1) {
67 if (errno != EINTR) {
68 LOG(FATAL, "read(%d, %p, %zd) failed with %d: %s\n",
69 to_copy->input, buffer + position, position - sizeof(buffer),
70 errno, strerror(errno));
71 }
Brian Silvermane1514fc2013-04-13 14:57:35 -070072 } else if (read_bytes == 0 && to_copy->interface_address == NULL) {
Brian Silverman771b4b82013-04-12 16:36:56 -070073 // read(2) says that this means EOF
74 return NULL;
75 }
Brian Silvermane1514fc2013-04-13 14:57:35 -070076 if (good_data) {
77 position += read_bytes;
78 }
Brian Silverman771b4b82013-04-12 16:36:56 -070079 }
80
81 assert(position >= 0);
82 assert(position <= static_cast<ssize_t>(sizeof(buffer)));
83 if (position > 0) {
84 ssize_t sent_bytes = write(to_copy->output, buffer, position);
85 if (sent_bytes == -1) {
86 if (errno != EINTR) {
87 LOG(FATAL, "write(%d, %p, %zd) failed with %d: %s\n",
88 to_copy->output, buffer, position, errno, strerror(errno));
89 }
90 } else if (sent_bytes != 0) {
Brian Silverman1c8670d2013-04-13 18:59:13 -070091 if (sent_bytes == position) {
92 position = 0;
93 } else {
94 memmove(buffer, buffer + sent_bytes, position - sent_bytes);
95 position -= sent_bytes;
96 }
Brian Silverman771b4b82013-04-12 16:36:56 -070097 }
98 }
99 }
100}
101
102int NetconsoleMain(int argc, char **argv) {
103 logging::Init();
104
Brian Silvermancb9637c2013-04-13 22:19:43 -0700105 int input, output;
Brian Silverman771b4b82013-04-12 16:36:56 -0700106 if (argc > 1) {
Brian Silvermancb9637c2013-04-13 22:19:43 -0700107 output = open(argv[1], O_APPEND | O_CREAT | O_WRONLY | O_TRUNC, 0666);
Brian Silverman771b4b82013-04-12 16:36:56 -0700108 if (output == -1) {
109 if (errno == EACCES || errno == ELOOP || errno == ENOSPC ||
110 errno == ENOTDIR || errno == EROFS || errno == ETXTBSY) {
111 fprintf(stderr, "Opening output file '%s' failed because of %s.\n",
112 argv[1], strerror(errno));
113 exit(EXIT_FAILURE);
114 }
115 LOG(FATAL, "open('%s', stuff, 0644) failed with %d: %s\n", argv[1],
116 errno, strerror(errno));
117 }
118 fprintf(stderr, "Writing output to '%s'.\n", argv[1]);
Brian Silvermancb9637c2013-04-13 22:19:43 -0700119 input = -1;
120 fprintf(stderr, "Not taking any input.\n");
Brian Silverman771b4b82013-04-12 16:36:56 -0700121 } else {
122 output = STDOUT_FILENO;
123 fprintf(stderr, "Writing output to stdout.\n");
Brian Silvermancb9637c2013-04-13 22:19:43 -0700124 input = STDIN_FILENO;
125 fprintf(stderr, "Reading stdin.\n");
Brian Silverman771b4b82013-04-12 16:36:56 -0700126 }
127
Brian Silverman771b4b82013-04-12 16:36:56 -0700128 int on = 1;
129
130 int from_crio = socket(AF_INET, SOCK_DGRAM, 0);
131 if (from_crio == -1) {
132 LOG(FATAL, "socket(AF_INET, SOCK_DGRAM, 0) failed with %d: %s\n",
133 errno, strerror(errno));
134 }
135 if (setsockopt(from_crio, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
136 LOG(FATAL, "SOL_SOCKET::SO_REUSEADDR=%d(%d) failed with %d: %s\n",
137 on, from_crio, errno, strerror(errno));
138 }
139 if (setsockopt(from_crio, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) == -1) {
140 LOG(FATAL, "SOL_SOCKET::SO_BROADCAST=%d(%d) failed with %d: %s\n",
141 on, from_crio, errno, strerror(errno));
142 }
Brian Silvermane1514fc2013-04-13 14:57:35 -0700143 if (setsockopt(from_crio, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)) == -1) {
144 LOG(FATAL, "IPROTO_IP::IP_PKTINFO=%d(%d) failed with %d: %s\n",
145 on, from_crio, errno, strerror(errno));
146 }
Brian Silverman771b4b82013-04-12 16:36:56 -0700147 union {
148 struct sockaddr_in in;
149 struct sockaddr addr;
150 } address;
151 address.in.sin_family = AF_INET;
152 address.in.sin_port = htons(6666);
153 address.in.sin_addr.s_addr = INADDR_ANY;
154 if (bind(from_crio, &address.addr, sizeof(address)) == -1) {
155 LOG(FATAL, "bind(%d, %p, %zu) failed with %d: %s\n",
156 from_crio, &address.addr, sizeof(address), errno, strerror(errno));
157 }
158
Brian Silvermancb9637c2013-04-13 22:19:43 -0700159 pthread_t input_thread, output_thread;
160
161 if (input != -1) {
162 int to_crio = socket(AF_INET, SOCK_DGRAM, 0);
163 if (to_crio == -1) {
164 LOG(FATAL, "socket(AF_INET, SOCK_DGRAM, 0) failed with %d: %s\n",
165 errno, strerror(errno));
166 }
167 if (setsockopt(to_crio, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
168 LOG(FATAL, "SOL_SOCKET::SO_REUSEADDR=%d(%d) failed with %d: %s\n",
169 on, to_crio, errno, strerror(errno));
170 }
171 address.in.sin_port = htons(6668);
172 if (inet_aton(
173 configuration::GetIPAddress(configuration::NetworkDevice::kCRIO),
174 &address.in.sin_addr) == 0) {
175 LOG(FATAL, "inet_aton(%s, %p) failed with %d: %s\n",
Brian Silverman771b4b82013-04-12 16:36:56 -0700176 configuration::GetIPAddress(configuration::NetworkDevice::kCRIO),
Brian Silvermancb9637c2013-04-13 22:19:43 -0700177 &address.in.sin_addr, errno, strerror(errno));
178 }
179 if (connect(to_crio, &address.addr, sizeof(address)) == -1) {
180 LOG(FATAL, "connect(%d, %p, %zu) failed with %d: %s\n",
181 to_crio, &address.addr, sizeof(address), errno, strerror(errno));
182 }
183 FDsToCopy input_fds{input, to_crio, NULL};
184 if (pthread_create(&input_thread, NULL, FDCopyThread, &input_fds) == -1) {
185 LOG(FATAL, "pthread_create(%p, NULL, %p, %p) failed with %d: %s\n",
186 &input_thread, FDCopyThread, &input_fds, errno, strerror(errno));
187 }
Brian Silverman771b4b82013-04-12 16:36:56 -0700188 }
189
190 fprintf(stderr, "Using cRIO IP %s.\n",
191 configuration::GetIPAddress(configuration::NetworkDevice::kCRIO));
192
Brian Silvermane1514fc2013-04-13 14:57:35 -0700193 if (inet_aton(
194 configuration::GetIPAddress(configuration::NetworkDevice::kSelf),
195 &address.in.sin_addr) == 0) {
196 LOG(FATAL, "inet_aton(%s, %p) failed with %d: %s\n",
197 configuration::GetIPAddress(configuration::NetworkDevice::kSelf),
198 &address.in.sin_addr, errno, strerror(errno));
199 }
200 FDsToCopy output_fds{from_crio, output, &address.in};
Brian Silverman771b4b82013-04-12 16:36:56 -0700201 if (pthread_create(&output_thread, NULL, FDCopyThread, &output_fds) == -1) {
202 LOG(FATAL, "pthread_create(%p, NULL, %p, %p) failed with %d: %s\n",
203 &output_thread, FDCopyThread, &output_fds, errno, strerror(errno));
204 }
Brian Silverman771b4b82013-04-12 16:36:56 -0700205
206 // input_thread will finish when stdin gets an EOF
Brian Silvermancb9637c2013-04-13 22:19:43 -0700207 if (pthread_join((input == -1) ? output_thread : input_thread, NULL) == -1) {
208 LOG(FATAL, "pthread_join(a_thread, NULL) failed with %d: %s\n",
Brian Silverman771b4b82013-04-12 16:36:56 -0700209 errno, strerror(errno));
210 }
211 exit(EXIT_SUCCESS);
212}
213
214} // namespace
215} // namespace aos
216
217int main(int argc, char **argv) {
218 return ::aos::NetconsoleMain(argc, argv);
219}