Attached are new toys.
authorSameer Pradhan <sameer.p.pradhan@gmail.com>
Sat, 25 Jul 2015 00:51:40 +0000 (19:51 -0500)
committerRob Landley <rob@landley.net>
Sat, 25 Jul 2015 00:51:40 +0000 (19:51 -0500)
tftp - Client for tftp daemon.
hostid -Print the numeric identifier for the current host.
fsync -Synchronize a file's in-core state with storage device.

toys/pending/fsync.c [new file with mode: 0644]
toys/pending/hostid.c [new file with mode: 0644]
toys/pending/tftp.c [new file with mode: 0644]

diff --git a/toys/pending/fsync.c b/toys/pending/fsync.c
new file mode 100644 (file)
index 0000000..bf673df
--- /dev/null
@@ -0,0 +1,32 @@
+/* fsync.c - Synchronize a file's in-core state with storage device.
+ *
+ * Copyright 2015 Ranjan Kumar <ranjankumar.bth@gmail.comi>
+ *
+ * No Standard.
+
+USE_FSYNC(NEWTOY(fsync, "<1d", TOYFLAG_BIN))
+
+config FSYNC
+  bool "fsync"
+  default n
+  help
+    usage: fsync [d] [FILE...]
+
+    Synchronize a file's in-core state with storage device.
+
+    -d Avoid syncing metadata.
+*/
+#define FOR_fsync
+#include "toys.h"
+
+static void do_fsync(int fd, char *name)
+{
+  if (((toys.optflags & FLAG_d) ? fdatasync(fd) : fsync(fd)))
+    perror_msg("can't sync '%s'", name);
+}
+
+void fsync_main(void)
+{
+  loopfiles_rw(toys.optargs, O_RDONLY|O_NOATIME|O_NOCTTY|O_CLOEXEC,
+      0, 0, do_fsync);
+}
diff --git a/toys/pending/hostid.c b/toys/pending/hostid.c
new file mode 100644 (file)
index 0000000..feef61b
--- /dev/null
@@ -0,0 +1,23 @@
+/* hostid.c - Print the numeric identifier for the current host.
+ *
+ * Copyright 2015 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ *
+ * No Standard.
+
+USE_HOSTID(NEWTOY(hostid, ">0", TOYFLAG_USR|TOYFLAG_BIN))
+
+config HOSTID
+  bool "hostid"
+  default n
+  help
+    usage: hostid
+
+    Print the numeric identifier for the current host.
+*/
+#define FOR_hostid
+#include "toys.h"
+
+void hostid_main(void)
+{
+  xprintf("%08lx\n", gethostid());
+}
diff --git a/toys/pending/tftp.c b/toys/pending/tftp.c
new file mode 100644 (file)
index 0000000..60d5f17
--- /dev/null
@@ -0,0 +1,454 @@
+/* tftp.c - TFTP client.
+ *
+ * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
+ * Copyright 2015 Sameer Prakash Pradhan <sameer.p.pradhan@gmail.com>
+ *
+ * No Standard.
+
+USE_TFTP(NEWTOY(tftp, "<1b#<8>65464r:l:g|p|[!gp]", TOYFLAG_USR|TOYFLAG_BIN))
+
+config TFTP
+  bool "tftp"
+  default n
+  help
+    usage: tftp [OPTIONS] HOST [PORT]
+
+    Transfer file from/to tftp server.
+
+    -l FILE Local FILE
+    -r FILE Remote FILE
+    -g    Get file
+    -p    Put file
+    -b SIZE Transfer blocks of SIZE octets(8 <= SIZE <= 65464)
+*/
+#define FOR_tftp
+#include "toys.h"
+
+GLOBALS(
+  char *local_file;
+  char *remote_file;
+  long block_size;
+
+  struct sockaddr_storage inaddr;
+  int af;
+)
+
+#define TFTP_BLKSIZE    512
+#define TFTP_RETRIES    3
+#define TFTP_DATAHEADERSIZE 4
+#define TFTP_MAXPACKETSIZE  (TFTP_DATAHEADERSIZE + TFTP_BLKSIZE)
+#define TFTP_PACKETSIZE    TFTP_MAXPACKETSIZE
+#define TFTP_DATASIZE    (TFTP_PACKETSIZE-TFTP_DATAHEADERSIZE)
+#define TFTP_IOBUFSIZE    (TFTP_PACKETSIZE+8)
+
+#define TFTP_OP_RRQ      1  /* Read Request      RFC 1350, RFC 2090 */
+#define TFTP_OP_WRQ      2  /* Write Request     RFC 1350 */
+#define TFTP_OP_DATA    3  /* Data chunk      RFC 1350 */
+#define TFTP_OP_ACK      4  /* Acknowledgement     RFC 1350 */
+#define TFTP_OP_ERR      5  /* Error Message     RFC 1350 */
+#define TFTP_OP_OACK    6  /* Option acknowledgment RFC 2347 */
+
+#define TFTP_ER_ILLEGALOP  4  /* Illegal TFTP operation */
+#define TFTP_ER_UNKID    5  /* Unknown transfer ID */
+
+#define TFTP_ES_NOSUCHFILE  "File not found"
+#define TFTP_ES_ACCESS    "Access violation"
+#define TFTP_ES_FULL    "Disk full or allocation exceeded"
+#define TFTP_ES_ILLEGALOP  "Illegal TFTP operation"
+#define TFTP_ES_UNKID    "Unknown transfer ID"
+#define TFTP_ES_EXISTS    "File already exists"
+#define TFTP_ES_UNKUSER    "No such user"
+#define TFTP_ES_NEGOTIATE  "Terminate transfer due to option negotiation"
+
+// Initializes SERVER with ADDR and returns socket.
+static int init_tftp(struct sockaddr_storage *server)
+{
+  struct timeval to = { .tv_sec = 10, //Time out
+                        .tv_usec = 0 };
+  const int set = 1;
+  int port = 69, sd = xsocket(TT.af, SOCK_DGRAM, IPPROTO_UDP);
+
+  xsetsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (void *)&to, sizeof(struct timeval));
+  xsetsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (void *)&set, sizeof(set));
+
+  if(toys.optc == 2) port = atolx_range(toys.optargs[1], 1, 65535);
+  memset(server, 0, sizeof(struct sockaddr_storage));
+  if (TT.af == AF_INET6) {
+      ((struct sockaddr_in6 *)server)->sin6_family = AF_INET6;
+      ((struct sockaddr_in6 *)server)->sin6_addr =
+        ((struct sockaddr_in6 *)&TT.inaddr)->sin6_addr;
+      ((struct sockaddr_in6 *)server)->sin6_port = htons(port);
+  }
+  else {
+      ((struct sockaddr_in *)server)->sin_family = AF_INET;
+      ((struct sockaddr_in *)server)->sin_addr.s_addr =
+        ((struct sockaddr_in *)&TT.inaddr)->sin_addr.s_addr;
+      ((struct sockaddr_in *)server)->sin_port = htons(port);
+  }
+  return sd;
+}
+
+/*
+ * Makes a request packet in BUFFER with OPCODE and file PATH of MODE
+ * and returns length of packet.
+ */
+static int mkpkt_request(uint8_t *buffer, int opcode, char *path, int mode)
+{
+  buffer[0] = opcode >> 8;
+  buffer[1] = opcode & 0xff;
+  if(strlen(path) > TFTP_BLKSIZE) error_exit("path too long");
+  return sprintf((char*) &buffer[2], "%s%c%s", path, 0, 
+    (mode ? "octet" : "netascii")) + 3;
+}
+
+/*
+ * Makes an acknowledgement packet in BUFFER of BLOCNO
+ * and returns packet length.
+ */
+static int mkpkt_ack(uint8_t *buffer, uint16_t blockno)
+{
+  buffer[0] = TFTP_OP_ACK >> 8;
+  buffer[1] = TFTP_OP_ACK & 0xff;
+  buffer[2] = blockno >> 8;
+  buffer[3] = blockno & 0xff;
+  return 4;
+}
+
+/*
+ * Makes an error packet in BUFFER with ERRORCODE and ERRORMSG.
+ * and returns packet length.
+ */
+static int mkpkt_err(uint8_t *buffer, uint16_t errorcode, char *errormsg)
+{
+  buffer[0] = TFTP_OP_ERR >> 8;
+  buffer[1] = TFTP_OP_ERR & 0xff;
+  buffer[2] = errorcode >> 8;
+  buffer[3] = errorcode & 0xff;
+  strcpy((char*) &buffer[4], errormsg);
+  return strlen(errormsg) + 5;
+}
+
+/*
+ * Recieves data from server in BUFF with socket SD and updates FROM
+ * and returns read length.
+ */
+static ssize_t read_server(int sd, void *buf, size_t len,
+  struct sockaddr_storage *from)
+{
+  socklen_t alen;
+  ssize_t nb;
+  
+  for (;;) {
+    memset(buf, 0, len);
+    alen = sizeof(struct sockaddr_storage);
+    nb = recvfrom(sd, buf, len, 0, (struct sockaddr *) from, &alen);
+    if (nb < 0) {
+      if (errno == EAGAIN) {
+        perror_msg("server read timed out");
+        return nb;
+      }else if (errno != EINTR) {
+        perror_msg("server read failed");
+        return nb;
+      }
+    }else return nb;
+  }
+  return nb;
+}
+
+/*
+ * sends data to server TO from BUFF of length LEN through socket SD
+ * and returns successfully send bytes number.
+ */
+static ssize_t write_server(int sd, void *buf, size_t len,
+  struct sockaddr_storage *to)
+{
+  ssize_t nb;
+  
+  for (;;) {
+    nb = sendto(sd, buf, len, 0, (struct sockaddr *)to,
+            sizeof(struct sockaddr_storage));
+    if (nb < 0) {
+      if (errno != EINTR) {
+        perror_msg("server write failed");
+        return nb;
+      }
+    } else return nb;
+  }
+  return nb;
+}
+
+// checks packet for data and updates block no
+static inline int check_data( uint8_t *packet, uint16_t *opcode, 
+  uint16_t *blockno)
+{
+  *opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1];
+  if (*opcode == TFTP_OP_DATA) {
+    *blockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3];
+    return 0;
+  }
+  return -1;
+}
+
+// Makes data packet through FD from file OFFSET in buffer PACKET of BLOCKNO
+static int mkpkt_data(int fd, off_t offset, uint8_t *packet, uint16_t blockno)
+{
+  off_t tmp;
+  int nbytesread;
+
+  packet[0] = TFTP_OP_DATA >> 8;
+  packet[1] = TFTP_OP_DATA & 0xff;
+  packet[2] = blockno >> 8;
+  packet[3] = blockno & 0xff;
+  tmp = lseek(fd, offset, SEEK_SET);
+  if (tmp == (off_t) -1) {
+    perror_msg("lseek failed");
+    return -1;
+  }
+  nbytesread = readall(fd, &packet[TFTP_DATAHEADERSIZE], TFTP_DATASIZE);
+  if (nbytesread < 0) return -1;
+  return nbytesread + TFTP_DATAHEADERSIZE;
+}
+
+// Receives ACK responses from server and updates blockno
+static int read_ack(int sd, uint8_t *packet, struct sockaddr_storage *server,
+  uint16_t *port, uint16_t *blockno)
+{
+  struct sockaddr_storage from;
+  ssize_t nbytes;
+  uint16_t opcode, rblockno;
+  int packetlen, retry;
+
+  for (retry = 0; retry < TFTP_RETRIES; retry++) {
+    for (;;) {
+      nbytes = read_server(sd, packet, TFTP_IOBUFSIZE, &from);
+      if (nbytes < 4) { // Ack headersize = 4
+        if (nbytes == 0) error_msg("Connection lost.");
+        else if (nbytes > 0) error_msg("Short packet: %d bytes", nbytes);
+        else error_msg("Server read ACK failure.");
+        break;
+      } else {
+        if (!*port) {
+          *port = ((struct sockaddr_in *)&from)->sin_port;
+          ((struct sockaddr_in *)server)->sin_port =
+                  ((struct sockaddr_in *)&from)->sin_port;
+        }
+        if (((struct sockaddr_in *)server)->sin_addr.s_addr !=
+                ((struct sockaddr_in *)&from)->sin_addr.s_addr) {
+          error_msg("Invalid address in DATA.");
+          continue;
+        }
+        if (*port != ((struct sockaddr_in *)server)->sin_port) {
+          error_msg("Invalid port in DATA.");
+          packetlen = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID);
+          (void) write_server(sd, packet, packetlen, server);
+          continue;
+        }
+        opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1];
+        rblockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3];
+
+        if (opcode != TFTP_OP_ACK) {
+          error_msg("Bad opcode.");
+          if (opcode > 5) {
+            packetlen = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP);
+            (void) write_server(sd, packet, packetlen, server);
+          }
+          break;
+        }
+        if (blockno) *blockno = rblockno;
+        return 0;
+      }
+    }
+  }
+  error_msg("Timeout, Waiting for ACK.");
+  return -1;
+}
+
+// receives file from server.
+static int file_get(void)
+{
+  struct sockaddr_storage server, from;
+  uint8_t *packet;
+  uint16_t blockno = 0, opcode, rblockno = 0;
+  int len, sd, fd, retry, nbytesrecvd = 0, ndatabytes, ret, result = -1;
+
+  sd = init_tftp(&server);
+
+  packet = (uint8_t*) xzalloc(TFTP_IOBUFSIZE);
+  fd = xcreate(TT.local_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+
+  len = mkpkt_request(packet, TFTP_OP_RRQ, TT.remote_file, 1);
+  ret = write_server(sd, packet, len, &server);
+  if (ret != len){
+    unlink(TT.local_file);
+    goto errout_with_sd;
+  }
+  if (TT.af == AF_INET6) ((struct sockaddr_in6 *)&server)->sin6_port = 0;
+  else ((struct sockaddr_in *)&server)->sin_port = 0;
+
+  do {
+    blockno++;
+    for (retry = 0 ; retry < TFTP_RETRIES; retry++) {
+      nbytesrecvd = read_server(sd, packet, TFTP_IOBUFSIZE, &from);
+      if (nbytesrecvd > 0) {
+        if ( ((TT.af == AF_INET) &&
+                memcmp(&((struct sockaddr_in *)&server)->sin_addr,
+                &((struct sockaddr_in *)&from)->sin_addr,
+                sizeof(struct in_addr))) ||
+             ((TT.af == AF_INET6) &&
+                memcmp(&((struct sockaddr_in6 *)&server)->sin6_addr,
+                &((struct sockaddr_in6 *)&from)->sin6_addr,
+                sizeof(struct in6_addr)))) {
+          error_msg("Invalid address in DATA.");
+          retry--;
+          continue;
+        }
+        if ( ((TT.af == AF_INET) && ((struct sockaddr_in *)&server)->sin_port
+                && (((struct sockaddr_in *)&server)->sin_port !=
+                ((struct sockaddr_in *)&from)->sin_port)) ||
+             ((TT.af == AF_INET6) && ((struct sockaddr_in6 *)&server)->sin6_port
+                && (((struct sockaddr_in6 *)&server)->sin6_port !=
+                ((struct sockaddr_in6 *)&from)->sin6_port))) {
+          error_msg("Invalid port in DATA.");
+          len = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID);
+          ret = write_server(sd, packet, len, &from);
+          retry--;
+          continue;
+        }
+        if (nbytesrecvd < TFTP_DATAHEADERSIZE) {
+          error_msg("Tiny data packet ignored.");
+          continue;
+        }
+        if (check_data(packet, &opcode, &rblockno) != 0
+            || blockno != rblockno) {
+
+        if (opcode == TFTP_OP_ERR) {
+          char *message = "DATA Check failure.";
+            char *arr[] = {TFTP_ES_NOSUCHFILE, TFTP_ES_ACCESS,
+              TFTP_ES_FULL, TFTP_ES_ILLEGALOP,
+              TFTP_ES_UNKID, TFTP_ES_EXISTS,
+              TFTP_ES_UNKUSER, TFTP_ES_NEGOTIATE};
+            if (rblockno && (rblockno < 9)) message = arr[rblockno - 1];
+            error_msg(message);
+        }
+        if (opcode > 5) {
+          len = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP);
+          ret = write_server(sd, packet, len, &from);
+        }
+        continue;
+        }
+        if ((TT.af == AF_INET6) && !((struct sockaddr_in6 *)&server)->sin6_port)
+          ((struct sockaddr_in6 *)&server)->sin6_port =
+            ((struct sockaddr_in6 *)&from)->sin6_port;
+        else if ((TT.af == AF_INET) && !((struct sockaddr_in *)&server)->sin_port)
+          ((struct sockaddr_in *)&server)->sin_port =
+            ((struct sockaddr_in *)&from)->sin_port;
+        break;
+      }
+    }
+    if (retry == TFTP_RETRIES) {
+      error_msg("Retry limit exceeded.");
+      unlink(TT.local_file);
+      goto errout_with_sd;
+    }
+    ndatabytes = nbytesrecvd - TFTP_DATAHEADERSIZE;
+    if (writeall(fd, packet + TFTP_DATAHEADERSIZE, ndatabytes) < 0){
+      unlink(TT.local_file);
+      goto errout_with_sd;
+    }
+    len = mkpkt_ack(packet, blockno);
+    ret = write_server(sd, packet, len, &server);
+    if (ret != len){
+      unlink(TT.local_file);
+      goto errout_with_sd;
+    }
+  } while (ndatabytes >= TFTP_DATASIZE);
+
+  result = 0;
+
+errout_with_sd: xclose(sd);
+  free(packet);
+  return result;
+}
+
+// Sends file to server.
+int file_put(void)
+{
+  struct sockaddr_storage server;
+  uint8_t *packet;
+  off_t offset = 0;
+  uint16_t blockno = 1, rblockno, port = 0;
+  int packetlen, sd, fd, retry = 0, ret, result = -1;
+
+  sd = init_tftp(&server);
+  packet = (uint8_t*)xzalloc(TFTP_IOBUFSIZE);
+  fd = xopen(TT.local_file, O_RDONLY);
+
+  for (;;) {  //first loop for request send and confirmation from server.
+    packetlen = mkpkt_request(packet, TFTP_OP_WRQ, TT.remote_file, 1);
+    ret = write_server(sd, packet, packetlen, &server);
+    if (ret != packetlen) goto errout_with_sd;
+    if (read_ack(sd, packet, &server, &port, NULL) == 0) break;
+    if (++retry > TFTP_RETRIES) {
+      error_msg("Retry count exceeded.");
+      goto errout_with_sd;
+    }
+  }
+  for (;;) {  // loop for data sending and receving ack from server.
+    packetlen = mkpkt_data(fd, offset, packet, blockno);
+    if (packetlen < 0) goto errout_with_sd;
+
+    ret = write_server(sd, packet, packetlen, &server);
+    if (ret != packetlen) goto errout_with_sd;
+
+    if (read_ack(sd, packet, &server, &port, &rblockno) == 0) {
+      if (rblockno == blockno) {
+        if (packetlen < TFTP_PACKETSIZE) break;
+        blockno++;
+        offset += TFTP_DATASIZE;
+        retry = 0;
+        continue;
+      }
+    }
+    if (++retry > TFTP_RETRIES) {
+      error_msg("Retry count exceeded.");
+      goto errout_with_sd;
+    }
+  }
+  result = 0;
+
+errout_with_sd: close(sd);
+  free(packet);
+  return result;
+}
+
+void tftp_main(void)
+{
+  struct addrinfo *info, rp, *res=0;
+  int ret;
+
+  if (toys.optflags & FLAG_r) {
+    if (!(toys.optflags & FLAG_l)) {
+      char *slash = strrchr(TT.remote_file, '/');
+      TT.local_file = (slash) ? slash + 1 : TT.remote_file;
+    }
+  } else if (toys.optflags & FLAG_l) TT.remote_file = TT.local_file;
+  else error_exit("Please provide some files.");
+
+  memset(&rp, 0, sizeof(rp));
+  rp.ai_family = AF_UNSPEC;
+  rp.ai_socktype = SOCK_STREAM;
+  ret = getaddrinfo(toys.optargs[0], toys.optargs[1], &rp, &info);
+  if (!ret) {
+    for (res = info; res; res = res->ai_next)
+    if ( (res->ai_family == AF_INET) || (res->ai_family == AF_INET6)) break;
+  }
+  if (!res)
+    error_exit("bad address '%s' : %s", toys.optargs[0], gai_strerror(ret));
+  TT.af = info->ai_family;
+
+  memcpy((void *)&TT.inaddr, info->ai_addr, info->ai_addrlen);
+  freeaddrinfo(info);
+
+  if (toys.optflags & FLAG_g) file_get();
+  if (toys.optflags & FLAG_p) file_put();
+}