Newer
Older
Import / research / other / WakeXBMC / wake_on_remote.c
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define SERVER_PORT 5000

typedef void (*callback_type)(void *callback_data);

char *PROGNAME;

void usage(int exitval) {
  fprintf(stderr, "Usage: %s <port> <mac>\n", PROGNAME);
  exit(exitval);
}

void fatal(const char *message, int exitval) {
  fprintf(stderr, "%s: FATAL: %s\n", PROGNAME, message);
  exit(exitval);
}

void error(const char *msg) {
  fprintf(stderr, "%s: ERROR: %s\n", PROGNAME, msg);
}

void debug(const char *msg) {
  fprintf(stderr, "%s: DEBUG: %s\n", PROGNAME, msg);
}

void send_wol(char *ethaddr) {
  struct sockaddr_in sap;
  int optval = 1;

  int packet;
  if ((packet = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
    error("socket failed");
    return;
  }

  struct in_addr interface_addr;
  interface_addr.s_addr = inet_addr("192.168.1.1");
  if (setsockopt(packet, IPPROTO_IP, IP_MULTICAST_IF, &interface_addr, sizeof(interface_addr)) < 0) {
    error("setsocket failed"); error(strerror(errno));
    close(packet);
    return;
  }

  if (setsockopt(packet, SOL_SOCKET, SO_BROADCAST, (char *)&optval, sizeof (optval)) < 0) {
    error("setsocket failed"); error(strerror(errno));
    close(packet);
    return;
  }

  sap.sin_family = AF_INET;
  // sap.sin_addr.s_addr = htonl(0xffffffff);  // broadcast address
  // sap.sin_addr.s_addr = htonl(0xc0a800ff);  // broadcast address
  sap.sin_addr.s_addr = inet_addr("192.168.1.255");
  sap.sin_port = htons(60000);

  // Build the message to send - 6 x 0xff then 16 x MAC address
  unsigned char buf[128], *ptr = buf;
  int i, j;
  for (i = 0; i < 6; i++)
    *ptr++ = 0xff;
  for (j = 0; j < 16; j++)
    for (i = 0; i < 6; i++)
      *ptr++ = ethaddr[i];

  if (sendto(packet, (char *)buf, 102, 0, (struct sockaddr *)&sap, sizeof(sap)) < 0) {
    error("sendto failed"); error(strerror(errno));
    close(packet);
    return;
  }
  debug("WOL sent");
  close(packet);
}

void wait_for_event(int port, callback_type callback, void *callback_data) {
  char message[1024];
  int sock;
  struct sockaddr_in name;
  struct hostent *hp, *gethostbyname();
  int bytes;

  sock = socket(AF_INET, SOCK_DGRAM, 0);
  if (sock < 0)
    fatal("Opening datagram socket", 11);
  memset((char *)&name, 0, sizeof(name));
  name.sin_family = AF_INET;
  name.sin_addr.s_addr = htonl(INADDR_ANY);
  name.sin_port = htons(port);
  if (bind(sock, (struct sockaddr *) &name, sizeof(name)))
    fatal("binding datagram socket", 12);

  struct timespec lasttime, mytime;
  clock_gettime(CLOCK_MONOTONIC, &lasttime);
  unsigned total_bytes = 0;
  while ((bytes = read(sock, message, 1024)) > 0) {
    clock_gettime(CLOCK_MONOTONIC, &mytime);
    if ((mytime.tv_sec - lasttime.tv_sec) * 1000000000 + mytime.tv_nsec - lasttime.tv_nsec < 200000000)
      total_bytes += bytes;
    else
      total_bytes = bytes;
    if (total_bytes >= 180) {
      debug("callback triggered");
      callback(callback_data);
      total_bytes = 0;
    }
    lasttime = mytime;
  }

  close(sock);
}

void main(int argc, char *argv[]) {
  PROGNAME = argv[0];
  if (argc < 3)
    usage(1);
  int port = atoi(argv[1]);
  if (port < 1)
    fatal("bad port number", 1);
  unsigned char mac[8];
  if (sscanf(argv[2], "%hhx%*c%hhx%*c%hhx%*c%hhx%*c%hhx%*c%hhx", mac, mac+1, mac+2, mac+3, mac+4, mac+5) !=6)
    if (sscanf(argv[2], "%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx", mac, mac+1, mac+2, mac+3, mac+4, mac+5) !=6)
      fatal("bad mac address", 2);
  mac[7] = mac[8] = 0;

  while(1)
    wait_for_event(port, (callback_type)&send_wol, mac);
}