1 /******************************************************************************
2 (c) 2001-2004 christine Caulfield christine.caulfield@googlemail.com
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13 ******************************************************************************/
14 #include <sys/types.h>
15 #include <sys/uio.h>
16 #include <sys/socket.h>
17 #include <sys/un.h>
18 #include <sys/ioctl.h>
19 #include <sys/wait.h>
20 #include <sys/stat.h>
21 #include <sys/time.h>
22 #include <sys/utsname.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <termios.h>
27 #include <signal.h>
28 #include <string.h>
29 #include <fcntl.h>
30 #include <dirent.h>
31 #include <ctype.h>
32 #include <regex.h>
33 #include <stdlib.h>
34 // #include <utmp.h>
35 #include <grp.h>
36 #include <signal.h>
37 #include <assert.h>
38 #ifdef HAVE_NET_IF_ETHER_H
39 #include <net/if.h>
40 #include <net/if_ether.h>
41 #endif
42 #ifdef HAVE_NET_ETHERNET_H
43 #include <net/ethernet.h>
44 #endif
45 #ifdef HAVE_NETINET_ETHER_H
46 #include <netinet/ether.h>
47 #endif
48
49 #include <string>
50
51 #include "interfaces.h"
52 #include "moprc.h"
53
54 #ifdef __FreeBSD__
55 #define ether_addr_octet octet
56 #endif
57
58 static int mop_socket;
59 static unsigned char last_message[1500];
60 static int last_message_len;
61 static int show_info = 0;
62 static int do_moprc(u_int8_t *, int, int, int);
63 static int send_boot(u_int8_t *macaddr, int interface);
64 static LATinterfaces *iface;
65
usage(FILE * f,char * cmd)66 static int usage(FILE *f, char *cmd)
67 {
68 fprintf(f, "\nUsage: %s [?hVv] [-i <interface>] <node name>|<macaddr>\n", cmd);
69
70 fprintf(f, " -? Show this usage message\n");
71 fprintf(f, " -h Show this usage message\n");
72 fprintf(f, " -V Show the version of moprc\n");
73 fprintf(f, " -i Ethernet interface to use (default to first found)\n");
74 fprintf(f, " -t Trigger (reboot) the server\n");
75 fprintf(f, " -b Make ^H send DEL\n");
76 fprintf(f, " -v Show target information\n");
77 fprintf(f, " -p <ms> Set poll interval (default 200)\n");
78 fprintf(f, "\n");
79 fprintf(f, "Node names are read from /etc/ethers\n");
80 fprintf(f, "MAC addresses in colon-seperated form. eg:\n");
81 fprintf(f, "\n%s -i eth1 08:00:2B:2B:AD:99\n", cmd);
82 if (geteuid() != 0)
83 fprintf(f, "\nYou will probably need to be root to run this program.\n");
84 fprintf(f, "\n");
85 return -1;
86 }
87
88
89 /* Send a MOP message to a specified MAC address */
send_message(unsigned char * buf,int len,int interface,u_int8_t * macaddr)90 static int send_message(unsigned char *buf, int len, int interface, u_int8_t *macaddr)
91 {
92 int status;
93 if (len < 46) len = 46;
94
95 status = iface->send_packet(interface, macaddr, buf, len);
96 if (status < 0)
97 perror("send message");
98
99 memmove(last_message, buf, len);
100 last_message_len = len;
101 return status;
102 }
103
send_last_message(int interface,u_int8_t * macaddr)104 static int send_last_message(int interface, u_int8_t *macaddr)
105 {
106 return send_message(last_message, last_message_len, interface, macaddr);
107 }
108
main(int argc,char * argv[])109 int main(int argc, char *argv[])
110 {
111 int opt;
112 int interface = -1;
113 int trigger=0;
114 int convert_bs = 0;
115 int poll_interval = 200; /* in ms */
116 char ifname_buf[255];
117 char *ifname;
118 struct ether_addr addr;
119
120 if (argc < 2)
121 {
122 return usage(stdout, argv[0]);
123 }
124
125 /* Look for MOPRC_INTERFACE environment variable */
126 ifname = getenv("MOPRC_INTERFACE");
127
128 /* Get command-line options */
129 opterr = 0;
130 while ((opt=getopt(argc,argv,"?hVvtbi:p:")) != EOF)
131 {
132 switch(opt)
133 {
134 case 'h':
135 return usage(stdout, argv[0]);
136
137 case '?':
138 return usage(stdout, argv[0]);
139
140 case 'v':
141 show_info++;
142 break;
143
144 case 't':
145 trigger++;
146 break;
147
148 case 'b':
149 convert_bs++;
150 break;
151
152 case 'i':
153 strcpy(ifname_buf, optarg);
154 ifname = ifname_buf;
155 break;
156
157 case 'p':
158 poll_interval = atoi(optarg);
159 break;
160
161 case 'V':
162 printf("\nMoprc version %s\n\n", VERSION);
163 exit(0);
164 break;
165 }
166 }
167
168 if (!argv[optind])
169 {
170 return usage(stderr, argv[0]);
171 }
172
173 /* Check for a hostname in /etc/ethers */
174 if (ether_hostton(argv[optind], &addr) != 0)
175 {
176 #ifndef __APPLE__
177 struct ether_addr *addr1;
178
179 /* Otherwise parse ethernet MAC address */
180 addr1 = ether_aton(argv[optind]);
181 if (addr1)
182 {
183 addr = *addr1;
184 }
185 else
186 #endif
187 {
188 fprintf(stderr, "unknown node name or bad MAC address %s\n", argv[optind]);
189 return 3;
190 }
191 }
192
193 /* Initialise the platform-specific interface code */
194 iface = LATinterfaces::Create();
195 if (iface->Start(LATinterfaces::ProtoMOP) == -1)
196 {
197 fprintf(stderr, "Can't create MOP protocol socket: %s\n", strerror(errno));
198 exit(1);
199 }
200
201 // If no interface on the command-line then use defaults
202 interface = iface->find_interface(ifname);
203 if (interface == -1)
204 {
205 if (ifname)
206 fprintf(stderr, "Cannot resolve interface %s\n", ifname);
207 else
208 fprintf(stderr, "Cannot find any ethernet interfaces\n");
209 return 2;
210 }
211
212 mop_socket = iface->get_fd(interface);
213 if (iface->bind_socket(interface))
214 return 3;
215
216 if (trigger)
217 {
218 return send_boot(addr.ether_addr_octet, interface);
219 }
220
221 return do_moprc(addr.ether_addr_octet, interface, convert_bs, poll_interval*1000);
222 }
223
readmop(unsigned char * buf,int buflen)224 static int readmop(unsigned char *buf, int buflen)
225 {
226 int ifn;
227 unsigned char macaddr[6];
228 bool more;
229
230 return iface->recv_packet(mop_socket, ifn, macaddr, buf, buflen, more);
231 }
232
send_reserve(u_int8_t * macaddr,int interface)233 static int send_reserve(u_int8_t *macaddr, int interface)
234 {
235 unsigned char buf[32];
236 memset(buf, 0, sizeof(buf));
237
238 buf[0] = 9;
239 buf[1] = 0;
240 buf[2] = MOPRC_CMD_RESERVE;
241
242 return send_message(buf, 11, interface, macaddr);
243 }
244
send_release(u_int8_t * macaddr,int interface)245 static int send_release(u_int8_t *macaddr, int interface)
246 {
247 unsigned char buf[32];
248 memset(buf, 0, sizeof(buf));
249
250 buf[0] = 1;
251 buf[1] = 0;
252 buf[2] = MOPRC_CMD_RELEASE;
253
254 return send_message(buf, 3, interface, macaddr);
255 }
256
send_reqid(u_int8_t * macaddr,int interface)257 static int send_reqid(u_int8_t *macaddr, int interface)
258 {
259 unsigned char buf[32];
260 memset(buf, 0, sizeof(buf));
261
262 buf[0] = 5;
263 buf[1] = 0;
264 buf[2] = MOPRC_CMD_REQUESTID;
265
266 return send_message(buf, 7, interface, macaddr);
267 }
268
send_boot(u_int8_t * macaddr,int interface)269 static int send_boot(u_int8_t *macaddr, int interface)
270 {
271 unsigned char buf[32];
272 memset(buf, 0, sizeof(buf));
273
274 buf[0] = 12;
275 buf[1] = 0;
276 buf[2] = MOPRC_CMD_BOOT;
277 buf[13] = 0xFF;
278
279 return send_message(buf, 14, interface, macaddr);
280 }
281
send_data(unsigned char * data,int len,u_int8_t * macaddr,int interface)282 static int send_data(unsigned char *data, int len, u_int8_t *macaddr, int interface)
283 {
284 unsigned char buf[len+46];
285 static char message = 0;
286
287 memset(buf, 0, sizeof(buf));
288
289 buf[0] = len+2;
290 buf[1] = 0;
291 buf[2] = MOPRC_CMD_COMMANDPOLL;
292 buf[3] = message;
293 memcpy(buf+4, data, len);
294
295 message = 1-message;
296 return send_message(buf, len+4, interface, macaddr);
297 }
298
print_ascic(unsigned char * buf,int len)299 static void print_ascic(unsigned char *buf, int len)
300 {
301 int i;
302
303 for (i=0; i <len; i++)
304 {
305 if (isprint(buf[i]))
306 printf("%c", buf[i]);
307 else
308 printf(".");
309 }
310 printf("\n");
311 }
312
show_system_info(unsigned char * info,int len)313 static int show_system_info(unsigned char *info, int len)
314 {
315 int index=0;
316 int functions = 0;
317
318 while (index < len)
319 {
320 int type = info[index] | info[index+1]<<8;
321 int infolen = info[index+2];
322
323 index += 3;
324 if (show_info || type == 2)
325 {
326 switch (type)
327 {
328 case 1: /* Maintenance version */
329 printf("Maintenance Version: %d.%d.%d\n",
330 info[index],info[index+1],info[index+2]);
331 break;
332 case 2: /* Functions */
333 functions = info[index];
334 break;
335
336 /* These are sent by terminal servers */
337 case 102:
338 printf("ROM version: ");
339 print_ascic(&info[index], infolen);
340 break;
341 case 103:
342 printf("S/W version: ");
343 print_ascic(&info[index], infolen);
344 break;
345 case 105:
346 printf("Node Name: ");
347 print_ascic(&info[index], infolen);
348 break;
349 case 106:
350 printf("Identification: ");
351 print_ascic(&info[index], infolen);
352 break;
353
354 /* Other stuff */
355 case 201:
356 printf("Operating System: ");
357 print_ascic(&info[index], infolen);
358 break;
359 case 202:
360 printf("Software Version: ");
361 print_ascic(&info[index], infolen);
362 break;
363 case 203:
364 printf("Node Name: ");
365 print_ascic(&info[index], infolen);
366 break;
367
368 default:
369 break;
370 }
371 index += infolen;
372 }
373 }
374 if (show_info) printf("\n");
375 return functions & 0x20; /* Do we do CCP? */
376 }
377
do_moprc(u_int8_t * macaddr,int interface,int convert_bs,int timeout)378 static int do_moprc(u_int8_t *macaddr, int interface, int convert_bs, int timeout)
379 {
380 enum {STARTING, CONNECTED} state=STARTING;
381 fd_set in;
382 unsigned char buf[1500];
383 struct timeval tv;
384 struct termios old_term;
385 struct termios new_term;
386 int len;
387 int last_msg_tag = 99; /* Dummy */
388 int status;
389 int waiting_ack;
390 int resends = 0;
391 int termfd = STDIN_FILENO;
392 int stdin_is_tty;
393 int last_packet_was_empty=1;
394
395 tcgetattr(termfd, &old_term);
396 new_term = old_term;
397
398 /* Set local terminal characteristics */
399 new_term.c_iflag &= ~BRKINT;
400 new_term.c_iflag |= IGNBRK | INLCR;
401 new_term.c_lflag &= ~ISIG;
402 new_term.c_cc[VMIN] = 1;
403 new_term.c_cc[VTIME] = 0;
404 new_term.c_lflag &= ~ICANON;
405 new_term.c_lflag &= ~(ECHO | ECHOCTL | ECHONL);
406 tcsetattr(termfd, TCSANOW, &new_term);
407
408 /* Send connect packets */
409 send_reserve(macaddr, interface);
410 send_reqid(macaddr, interface);
411 waiting_ack = 1;
412
413 /* Main loop */
414 FD_ZERO(&in);
415 FD_SET(mop_socket, &in);
416 tv.tv_sec = 5;
417 tv.tv_usec = 0;
418
419 stdin_is_tty = isatty(STDIN_FILENO);
420
421 /* Loop for input */
422 while ( (status = select(FD_SETSIZE, &in, NULL, NULL, &tv)) >= 0)
423 {
424 /* No response after 3 tries */
425 if (waiting_ack && resends >= 3)
426 {
427 printf("\nTarget does not respond\n");
428 break;
429 }
430
431 /* Waiting for an ACK but not got one. Resend the last
432 packet */
433 if (status == 0 && waiting_ack)
434 {
435 send_last_message(interface, macaddr);
436 resends++;
437 continue;
438 }
439
440 /* No data, poll for any input from the terminal server */
441 if (status == 0 && !waiting_ack)
442 {
443 unsigned char dummybuf[1];
444 send_data(dummybuf, 0, macaddr, interface);
445 waiting_ack = 1;
446 resends = 0;
447 }
448
449 /* Data from the terminal server */
450 if (FD_ISSET(mop_socket, &in))
451 {
452 int cmd;
453 int datalen;
454
455 len = readmop(buf, sizeof(buf));
456 if (len < 0)
457 {
458 perror("reading from Remote");
459 return -1;
460 }
461 if (len == 0) continue;
462
463 waiting_ack = 0;
464 cmd = buf[2];
465 datalen = buf[0] | buf[1] <<8;
466
467 /* Ignore duplicates */
468 if (last_msg_tag == buf[3])
469 continue;
470 last_msg_tag = buf[3];
471
472 switch (cmd)
473 {
474 case MOPRC_CMD_RESERVE:
475 case MOPRC_CMD_RELEASE:
476 case MOPRC_CMD_REQUESTID:
477 fprintf(stderr, "Got unsupported MOPRC function %d\n", cmd);
478 break;
479
480 /* Got some data to display */
481 case MOPRC_CMD_RESPONSE:
482 if (datalen > 2)
483 {
484 fwrite(buf+4, datalen-2, 1, stdout);
485 fflush(stdout);
486 last_packet_was_empty = 0;
487 }
488 else
489 {
490 last_packet_was_empty = 1;
491 }
492 break;
493
494 /* Response to our REQUESTID message means
495 we are connected */
496 case MOPRC_CMD_SYSTEMID:
497 if (state == STARTING)
498 {
499 if (show_system_info(&buf[6], datalen-1) == 0)
500 {
501 printf("target does not support remote console\n");
502 goto finished;
503 }
504
505 if (stdin_is_tty)
506 printf("Console connected (press CTRL/D when finished)\n");
507
508 state = CONNECTED;
509 }
510 break;
511 default:
512 fprintf(stderr, "Got unknown MOPRC function %d\n", cmd);
513 break;
514 }
515 }
516
517 /* Keyboard input */
518 if (FD_ISSET(STDIN_FILENO, &in))
519 {
520 int i;
521 len = read(STDIN_FILENO, buf, stdin_is_tty ? sizeof(buf) : 1);
522 if (len < 0)
523 {
524 perror("reading from stdin");
525 return -1;
526 }
527 if (len == 0) break;
528
529 /* Convert LF to CR & check for ^D */
530 for (i=0; i<len; i++)
531 {
532 if (buf[i] == 4) goto finished;
533 if (buf[i] == '\n') buf[i] = '\r';
534 if (convert_bs && buf[i] == '\b') buf[i] = 127;
535 }
536 waiting_ack = 1;
537 resends = 0;
538 send_data(buf, len, macaddr, interface);
539 }
540
541 /* Reset for another select */
542 tv.tv_usec = timeout;
543 tv.tv_sec = 0;
544
545 FD_ZERO(&in);
546 FD_SET(mop_socket, &in);
547 if (!waiting_ack && last_packet_was_empty) FD_SET(STDIN_FILENO, &in);
548 }
549
550 finished:
551 /* Send disconnect */
552 send_release(macaddr, interface);
553
554 tcsetattr(termfd, TCSANOW, &old_term);
555 printf("\n");
556 return 0;
557 }
558