diff options
Diffstat (limited to 'cpukit/libnetworking/lib')
-rw-r--r-- | cpukit/libnetworking/lib/README | 5 | ||||
-rw-r--r-- | cpukit/libnetworking/lib/ftpfs.c | 1345 | ||||
-rw-r--r-- | cpukit/libnetworking/lib/getprotoby.c | 50 | ||||
-rw-r--r-- | cpukit/libnetworking/lib/rtems_bsdnet_ntp.c | 218 | ||||
-rw-r--r-- | cpukit/libnetworking/lib/syslog.c | 194 | ||||
-rw-r--r-- | cpukit/libnetworking/lib/tftpDriver.c | 1119 |
6 files changed, 2931 insertions, 0 deletions
diff --git a/cpukit/libnetworking/lib/README b/cpukit/libnetworking/lib/README new file mode 100644 index 0000000000..998bd5e708 --- /dev/null +++ b/cpukit/libnetworking/lib/README @@ -0,0 +1,5 @@ +# +# $Id$ +# + +Sources from application-level (as opposed to kernel-level) libraries. diff --git a/cpukit/libnetworking/lib/ftpfs.c b/cpukit/libnetworking/lib/ftpfs.c new file mode 100644 index 0000000000..0f2183409c --- /dev/null +++ b/cpukit/libnetworking/lib/ftpfs.c @@ -0,0 +1,1345 @@ +/** + * @file + * + * File Transfer Protocol file system (FTP client). + */ + +/* + * Copyright (c) 2009, 2010, 2011 + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * (c) Copyright 2002 + * Thomas Doerfler + * IMD Ingenieurbuero fuer Microcomputertechnik + * Herbststr. 8 + * 82178 Puchheim, Germany + * <Thomas.Doerfler@imd-systems.de> + * + * Modified by Sebastian Huber <sebastian.huber@embedded-brains.de>. + * + * This code has been created after closly inspecting "tftpdriver.c" from Eric + * Norum. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <malloc.h> +#include <netdb.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include <rtems.h> +#include <rtems/ftpfs.h> +#include <rtems/imfs.h> +#include <rtems/libio.h> +#include <rtems/rtems_bsdnet.h> +#include <rtems/seterr.h> + +#ifdef DEBUG + #define DEBUG_PRINTF(...) printf(__VA_ARGS__) +#else + #define DEBUG_PRINTF(...) +#endif + +/** + * Connection entry for each open file stream. + */ +typedef struct { + /** + * Control connection socket. + */ + int ctrl_socket; + + /** + * Data transfer socket. + */ + int data_socket; + + /** + * End of file flag. + */ + bool eof; +} rtems_ftpfs_entry; + +/** + * Mount entry for each file system instance. + */ +typedef struct { + /** + * Verbose mode enabled or disabled. + */ + bool verbose; + + /** + * Timeout value + */ + struct timeval timeout; +} rtems_ftpfs_mount_entry; + +static const rtems_filesystem_operations_table rtems_ftpfs_ops; + +static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers; + +static const rtems_filesystem_file_handlers_r rtems_ftpfs_root_handlers; + +static bool rtems_ftpfs_use_timeout(const struct timeval *to) +{ + return to->tv_sec != 0 || to->tv_usec != 0; +} + +static int rtems_ftpfs_set_connection_timeout( + int socket, + const struct timeval *to +) +{ + if (rtems_ftpfs_use_timeout(to)) { + int rv = 0; + + rv = setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, to, sizeof(*to)); + if (rv != 0) { + return EIO; + } + + rv = setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, to, sizeof(*to)); + if (rv != 0) { + return EIO; + } + } + + return 0; +} + +static rtems_status_code rtems_ftpfs_do_ioctl( + const char *mount_point, + ioctl_command_t req, + ... +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + int rv = 0; + int fd = 0; + va_list ap; + + if (mount_point == NULL) { + mount_point = RTEMS_FTPFS_MOUNT_POINT_DEFAULT; + } + + fd = open(mount_point, O_RDWR); + if (fd < 0) { + return RTEMS_INVALID_NAME; + } + + va_start(ap, req); + rv = ioctl(fd, req, va_arg(ap, void *)); + va_end(ap); + if (rv != 0) { + sc = RTEMS_INVALID_NUMBER; + } + + rv = close(fd); + if (rv != 0 && sc == RTEMS_SUCCESSFUL) { + sc = RTEMS_IO_ERROR; + } + + return sc; +} + +rtems_status_code rtems_ftpfs_get_verbose(const char *mount_point, bool *verbose) +{ + return rtems_ftpfs_do_ioctl( + mount_point, + RTEMS_FTPFS_IOCTL_GET_VERBOSE, + verbose + ); +} + +rtems_status_code rtems_ftpfs_set_verbose(const char *mount_point, bool verbose) +{ + return rtems_ftpfs_do_ioctl( + mount_point, + RTEMS_FTPFS_IOCTL_SET_VERBOSE, + &verbose + ); +} + +rtems_status_code rtems_ftpfs_get_timeout( + const char *mount_point, + struct timeval *timeout +) +{ + return rtems_ftpfs_do_ioctl( + mount_point, + RTEMS_FTPFS_IOCTL_GET_TIMEOUT, + timeout + ); +} + +rtems_status_code rtems_ftpfs_set_timeout( + const char *mount_point, + const struct timeval *timeout +) +{ + return rtems_ftpfs_do_ioctl( + mount_point, + RTEMS_FTPFS_IOCTL_SET_TIMEOUT, + timeout + ); +} + +typedef void (*rtems_ftpfs_reply_parser)( + const char * /* reply fragment */, + size_t /* reply fragment length */, + void * /* parser argument */ +); + +typedef enum { + RTEMS_FTPFS_REPLY_START, + RTEMS_FTPFS_REPLY_SINGLE_LINE, + RTEMS_FTPFS_REPLY_SINGLE_LINE_DONE, + RTEMS_FTPFS_REPLY_MULTI_LINE, + RTEMS_FTPFS_REPLY_NEW_LINE, + RTEMS_FTPFS_REPLY_NEW_LINE_START +} rtems_ftpfs_reply_state; + +typedef enum { + RTEMS_FTPFS_REPLY_ERROR = 0, + RTEMS_FTPFS_REPLY_1 = '1', + RTEMS_FTPFS_REPLY_2 = '2', + RTEMS_FTPFS_REPLY_3 = '3', + RTEMS_FTPFS_REPLY_4 = '4', + RTEMS_FTPFS_REPLY_5 = '5' +} rtems_ftpfs_reply; + +#define RTEMS_FTPFS_REPLY_SIZE 3 + +static rtems_ftpfs_reply rtems_ftpfs_get_reply( + int socket, + rtems_ftpfs_reply_parser parser, + void *parser_arg, + bool verbose +) +{ + rtems_ftpfs_reply_state state = RTEMS_FTPFS_REPLY_START; + unsigned char reply_first [RTEMS_FTPFS_REPLY_SIZE] = { 'a', 'a', 'a' }; + unsigned char reply_last [RTEMS_FTPFS_REPLY_SIZE] = { 'b', 'b', 'b' }; + size_t reply_first_index = 0; + size_t reply_last_index = 0; + char buf [128]; + + while (true) { + /* Receive reply fragment from socket */ + ssize_t i = 0; + ssize_t rv = recv(socket, buf, sizeof(buf), 0); + + if (rv <= 0) { + return RTEMS_FTPFS_REPLY_ERROR; + } + + /* Be verbose if necessary */ + if (verbose) { + write(STDERR_FILENO, buf, (size_t) rv); + } + + /* Invoke parser if necessary */ + if (parser != NULL) { + parser(buf, (size_t) rv, parser_arg); + } + + /* Parse reply fragment */ + for (i = 0; i < rv; ++i) { + char c = buf [i]; + + switch (state) { + case RTEMS_FTPFS_REPLY_START: + if (reply_first_index < RTEMS_FTPFS_REPLY_SIZE) { + reply_first [reply_first_index] = c; + ++reply_first_index; + } else if (c == '-') { + state = RTEMS_FTPFS_REPLY_MULTI_LINE; + } else { + state = RTEMS_FTPFS_REPLY_SINGLE_LINE; + } + break; + case RTEMS_FTPFS_REPLY_SINGLE_LINE: + if (c == '\n') { + state = RTEMS_FTPFS_REPLY_SINGLE_LINE_DONE; + } + break; + case RTEMS_FTPFS_REPLY_MULTI_LINE: + if (c == '\n') { + state = RTEMS_FTPFS_REPLY_NEW_LINE_START; + reply_last_index = 0; + } + break; + case RTEMS_FTPFS_REPLY_NEW_LINE: + case RTEMS_FTPFS_REPLY_NEW_LINE_START: + if (reply_last_index < RTEMS_FTPFS_REPLY_SIZE) { + state = RTEMS_FTPFS_REPLY_NEW_LINE; + reply_last [reply_last_index] = c; + ++reply_last_index; + } else { + state = RTEMS_FTPFS_REPLY_MULTI_LINE; + } + break; + default: + return RTEMS_FTPFS_REPLY_ERROR; + } + } + + /* Check reply */ + if (state == RTEMS_FTPFS_REPLY_SINGLE_LINE_DONE) { + if ( + isdigit(reply_first [0]) + && isdigit(reply_first [1]) + && isdigit(reply_first [2]) + ) { + break; + } else { + return RTEMS_FTPFS_REPLY_ERROR; + } + } else if (state == RTEMS_FTPFS_REPLY_NEW_LINE_START) { + bool ok = true; + + for (i = 0; i < RTEMS_FTPFS_REPLY_SIZE; ++i) { + ok = ok + && reply_first [i] == reply_last [i] + && isdigit(reply_first [i]); + } + + if (ok) { + break; + } + } + } + + return reply_first [0]; +} + +static rtems_ftpfs_reply rtems_ftpfs_send_command_with_parser( + int socket, + const char *cmd, + const char *arg, + rtems_ftpfs_reply_parser parser, + void *parser_arg, + bool verbose +) +{ + rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR; + size_t cmd_len = strlen(cmd); + size_t arg_len = arg != NULL ? strlen(arg) : 0; + size_t len = cmd_len + arg_len + 2; + char *buf = malloc(len); + + if (buf != NULL) { + ssize_t n = 0; + char *buf_arg = buf + cmd_len; + char *buf_eol = buf_arg + arg_len; + + memcpy(buf, cmd, cmd_len); + memcpy(buf_arg, arg, arg_len); + buf_eol [0] = '\r'; + buf_eol [1] = '\n'; + + /* Send */ + n = send(socket, buf, len, 0); + if (n == (ssize_t) len) { + if (verbose) { + write(STDERR_FILENO, buf, len); + } + + /* Reply */ + reply = rtems_ftpfs_get_reply(socket, parser, parser_arg, verbose); + } + + free(buf); + } + + return reply; +} + +static rtems_ftpfs_reply rtems_ftpfs_send_command( + int socket, + const char *cmd, + const char *arg, + bool verbose +) +{ + return rtems_ftpfs_send_command_with_parser( + socket, + cmd, + arg, + NULL, + NULL, + verbose + ); +} + +typedef enum { + STATE_USER_NAME, + STATE_START_PASSWORD, + STATE_START_HOST_NAME, + STATE_START_HOST_NAME_OR_PATH, + STATE_START_PATH, + STATE_PASSWORD, + STATE_HOST_NAME, + STATE_DONE, + STATE_INVALID +} split_state; + +static bool rtems_ftpfs_split_names ( + char *s, + const char **user, + const char **password, + const char **hostname, + const char **path +) +{ + split_state state = STATE_USER_NAME; + size_t len = strlen(s); + size_t i = 0; + + *user = s; + *password = NULL; + *hostname = NULL; + *path = NULL; + + for (i = 0; i < len; ++i) { + char c = s [i]; + + switch (state) { + case STATE_USER_NAME: + if (c == ':') { + state = STATE_START_PASSWORD; + s [i] = '\0'; + } else if (c == '@') { + state = STATE_START_HOST_NAME; + s [i] = '\0'; + } else if (c == '/') { + state = STATE_START_HOST_NAME_OR_PATH; + s [i] = '\0'; + } + break; + case STATE_START_PASSWORD: + state = STATE_PASSWORD; + *password = &s [i]; + --i; + break; + case STATE_START_HOST_NAME: + state = STATE_HOST_NAME; + *hostname = &s [i]; + --i; + break; + case STATE_START_HOST_NAME_OR_PATH: + if (c == '@') { + state = STATE_START_HOST_NAME; + } else { + state = STATE_DONE; + *path = &s [i]; + goto done; + } + break; + case STATE_START_PATH: + state = STATE_DONE; + *path = &s [i]; + goto done; + case STATE_PASSWORD: + if (c == '@') { + state = STATE_START_HOST_NAME; + s [i] = '\0'; + } else if (c == '/') { + state = STATE_START_HOST_NAME_OR_PATH; + s [i] = '\0'; + } + break; + case STATE_HOST_NAME: + if (c == '/') { + state = STATE_START_PATH; + s [i] = '\0'; + } + break; + default: + state = STATE_INVALID; + goto done; + } + } + +done: + + /* This is a special case with no username and password */ + if (*hostname == NULL) { + *hostname = &s [0]; + *user = "anonymous"; + *password = *user; + } + + /* If we have no password use the user name */ + if (*password == NULL) { + *password = *user; + } + + return state == STATE_DONE; +} + +static socklen_t rtems_ftpfs_create_address( + struct sockaddr_in *sa, + unsigned long address, + unsigned short port +) +{ + memset(sa, sizeof(*sa), 0); + + sa->sin_family = AF_INET; + sa->sin_addr.s_addr = address; + sa->sin_port = port; + sa->sin_len = sizeof(*sa); + + return sizeof(*sa); +} + +static int rtems_ftpfs_terminate(rtems_libio_t *iop, bool error) +{ + int eno = 0; + int rv = 0; + rtems_ftpfs_entry *e = iop->data1; + rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info; + bool verbose = me->verbose; + rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR; + + if (e != NULL) { + /* Close data connection if necessary */ + if (e->data_socket >= 0) { + rv = close(e->data_socket); + if (rv != 0) { + eno = EIO; + } + + /* For write connections we have to obtain the transfer reply */ + if ( + e->ctrl_socket >= 0 + && (iop->flags & LIBIO_FLAGS_WRITE) != 0 + && !error + ) { + reply = rtems_ftpfs_get_reply(e->ctrl_socket, NULL, NULL, verbose); + if (reply != RTEMS_FTPFS_REPLY_2) { + eno = EIO; + } + } + } + + /* Close control connection if necessary */ + if (e->ctrl_socket >= 0) { + reply = rtems_ftpfs_send_command( + e->ctrl_socket, + "QUIT", + NULL, + verbose + ); + if (reply != RTEMS_FTPFS_REPLY_2) { + eno = EIO; + } + + rv = close(e->ctrl_socket); + if (rv != 0) { + eno = EIO; + } + } + + /* Free connection entry */ + free(e); + } + + /* Invalidate IO entry */ + iop->data1 = NULL; + + return eno; +} + +static int rtems_ftpfs_open_ctrl_connection( + rtems_ftpfs_entry *e, + const char *user, + const char *password, + const char *hostname, + uint32_t *client_address, + bool verbose, + const struct timeval *timeout +) +{ + int rv = 0; + int eno = 0; + rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR; + struct in_addr address = { .s_addr = 0 }; + struct sockaddr_in sa; + socklen_t size = 0; + + /* Create the socket for the control connection */ + e->ctrl_socket = socket(AF_INET, SOCK_STREAM, 0); + if (e->ctrl_socket < 0) { + return ENOMEM; + } + + /* Set up the server address from the hostname */ + if (inet_aton(hostname, &address) == 0) { + /* Try to get the address by name */ + struct hostent *he = gethostbyname(hostname); + + if (he != NULL) { + memcpy(&address, he->h_addr, sizeof(address)); + } else { + return ENOENT; + } + } + rtems_ftpfs_create_address(&sa, address.s_addr, htons(RTEMS_FTPFS_CTRL_PORT)); + DEBUG_PRINTF("server = %s\n", inet_ntoa(sa.sin_addr)); + + /* Open control connection */ + rv = connect( + e->ctrl_socket, + (struct sockaddr *) &sa, + sizeof(sa) + ); + if (rv != 0) { + return ENOENT; + } + + /* Set control connection timeout */ + eno = rtems_ftpfs_set_connection_timeout(e->ctrl_socket, timeout); + if (eno != 0) { + return eno; + } + + /* Get client address */ + size = rtems_ftpfs_create_address(&sa, INADDR_ANY, 0); + rv = getsockname( + e->ctrl_socket, + (struct sockaddr *) &sa, + &size + ); + if (rv != 0) { + return ENOMEM; + } + *client_address = ntohl(sa.sin_addr.s_addr); + DEBUG_PRINTF("client = %s\n", inet_ntoa(sa.sin_addr)); + + /* Now we should get a welcome message from the server */ + reply = rtems_ftpfs_get_reply(e->ctrl_socket, NULL, NULL, verbose); + if (reply != RTEMS_FTPFS_REPLY_2) { + return ENOENT; + } + + /* Send USER command */ + reply = rtems_ftpfs_send_command(e->ctrl_socket, "USER ", user, verbose); + if (reply == RTEMS_FTPFS_REPLY_3) { + /* Send PASS command */ + reply = rtems_ftpfs_send_command( + e->ctrl_socket, + "PASS ", + password, + verbose + ); + if (reply != RTEMS_FTPFS_REPLY_2) { + return EACCES; + } + + /* TODO: Some server may require an account */ + } else if (reply != RTEMS_FTPFS_REPLY_2) { + return EACCES; + } + + /* Send TYPE command to set binary mode for all data transfers */ + reply = rtems_ftpfs_send_command(e->ctrl_socket, "TYPE I", NULL, verbose); + if (reply != RTEMS_FTPFS_REPLY_2) { + return EIO; + } + + return 0; +} + +static int rtems_ftpfs_open_data_connection_active( + rtems_ftpfs_entry *e, + uint32_t client_address, + const char *file_command, + const char *filename, + bool verbose, + const struct timeval *timeout +) +{ + int rv = 0; + int eno = 0; + rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR; + struct sockaddr_in sa; + socklen_t size = 0; + int port_socket = -1; + char port_command [] = "PORT 000,000,000,000,000,000"; + uint16_t data_port = 0; + + /* Create port socket to establish a data data connection */ + port_socket = socket(AF_INET, SOCK_STREAM, 0); + if (port_socket < 0) { + eno = ENOMEM; + goto cleanup; + } + + /* Bind port socket */ + rtems_ftpfs_create_address(&sa, INADDR_ANY, 0); + rv = bind( + port_socket, + (struct sockaddr *) &sa, + sizeof(sa) + ); + if (rv != 0) { + eno = EBUSY; + goto cleanup; + } + + /* Get port number for data socket */ + size = rtems_ftpfs_create_address(&sa, INADDR_ANY, 0); + rv = getsockname( + port_socket, + (struct sockaddr *) &sa, + &size + ); + if (rv != 0) { + eno = ENOMEM; + goto cleanup; + } + data_port = ntohs(sa.sin_port); + + /* Send PORT command to set data connection port for server */ + snprintf( + port_command, + sizeof(port_command), + "PORT %lu,%lu,%lu,%lu,%lu,%lu", + (client_address >> 24) & 0xffUL, + (client_address >> 16) & 0xffUL, + (client_address >> 8) & 0xffUL, + (client_address >> 0) & 0xffUL, + (data_port >> 8) & 0xffUL, + (data_port >> 0) & 0xffUL + ); + reply = rtems_ftpfs_send_command( + e->ctrl_socket, + port_command, + NULL, + verbose + ); + if (reply != RTEMS_FTPFS_REPLY_2) { + eno = ENOTSUP; + goto cleanup; + } + + /* Listen on port socket for incoming data connections */ + rv = listen(port_socket, 1); + if (rv != 0) { + eno = EBUSY; + goto cleanup; + } + + /* Send RETR or STOR command with filename */ + reply = rtems_ftpfs_send_command( + e->ctrl_socket, + file_command, + filename, + verbose + ); + if (reply != RTEMS_FTPFS_REPLY_1) { + eno = EIO; + goto cleanup; + } + + /* Wait for connect on data connection if necessary */ + if (rtems_ftpfs_use_timeout(timeout)) { + struct timeval to = *timeout; + fd_set fds; + + FD_ZERO(&fds); + FD_SET(port_socket, &fds); + + rv = select(port_socket + 1, &fds, NULL, NULL, &to); + if (rv <= 0) { + eno = EIO; + goto cleanup; + } + } + + /* Accept data connection */ + size = sizeof(sa); + e->data_socket = accept( + port_socket, + (struct sockaddr *) &sa, + &size + ); + if (e->data_socket < 0) { + eno = EIO; + goto cleanup; + } + +cleanup: + + /* Close port socket if necessary */ + if (port_socket >= 0) { + rv = close(port_socket); + if (rv != 0) { + eno = EIO; + } + } + + return eno; +} + +typedef enum { + RTEMS_FTPFS_PASV_START = 0, + RTEMS_FTPFS_PASV_JUNK, + RTEMS_FTPFS_PASV_DATA, + RTEMS_FTPFS_PASV_DONE +} rtems_ftpfs_pasv_state; + +typedef struct { + rtems_ftpfs_pasv_state state; + uint8_t data [6]; + size_t index; +} rtems_ftpfs_pasv_entry; + +static void rtems_ftpfs_pasv_parser( + const char* buf, + size_t len, + void *arg +) +{ + rtems_ftpfs_pasv_entry *e = arg; + size_t i = 0; + + for (i = 0; i < len; ++i) { + int c = buf [i]; + + switch (e->state) { + case RTEMS_FTPFS_PASV_START: + if (!isdigit(c)) { + e->state = RTEMS_FTPFS_PASV_JUNK; + e->index = 0; + } + break; + case RTEMS_FTPFS_PASV_JUNK: + if (isdigit(c)) { + e->state = RTEMS_FTPFS_PASV_DATA; + e->data [e->index] = (uint8_t) (c - '0'); + } + break; + case RTEMS_FTPFS_PASV_DATA: + if (isdigit(c)) { + e->data [e->index] = (uint8_t) (e->data [e->index] * 10 + c - '0'); + } else if (c == ',') { + ++e->index; + if (e->index < sizeof(e->data)) { + e->data [e->index] = 0; + } else { + e->state = RTEMS_FTPFS_PASV_DONE; + } + } else { + e->state = RTEMS_FTPFS_PASV_DONE; + } + break; + default: + return; + } + } +} + +static int rtems_ftpfs_open_data_connection_passive( + rtems_ftpfs_entry *e, + uint32_t client_address, + const char *file_command, + const char *filename, + bool verbose, + const struct timeval *timeout +) +{ + int rv = 0; + rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR; + struct sockaddr_in sa; + uint32_t data_address = 0; + uint16_t data_port = 0; + + rtems_ftpfs_pasv_entry pe = { + .state = RTEMS_FTPFS_PASV_START + }; + + /* Send PASV command */ + reply = rtems_ftpfs_send_command_with_parser( + e->ctrl_socket, + "PASV", + NULL, + rtems_ftpfs_pasv_parser, + &pe, + verbose + ); + if (reply != RTEMS_FTPFS_REPLY_2) { + return ENOTSUP; + } + data_address = ((uint32_t)(pe.data [0]) << 24) + ((uint32_t)(pe.data [1]) << 16) + + ((uint32_t)(pe.data [2]) << 8) + ((uint32_t)(pe.data [3])); + data_port = (uint16_t) ((pe.data [4] << 8) + pe.data [5]); + rtems_ftpfs_create_address(&sa, htonl(data_address), htons(data_port)); + DEBUG_PRINTF( + "server data = %s:%u\n", + inet_ntoa(sa.sin_addr), + (unsigned) ntohs(sa.sin_port) + ); + + /* Create data socket */ + e->data_socket = socket(AF_INET, SOCK_STREAM, 0); + if (e->data_socket < 0) { + return ENOMEM; + } + + /* Open data connection */ + rv = connect( + e->data_socket, + (struct sockaddr *) &sa, + sizeof(sa) + ); + if (rv != 0) { + return EIO; + } + + /* Send RETR or STOR command with filename */ + reply = rtems_ftpfs_send_command( + e->ctrl_socket, + file_command, + filename, + verbose + ); + if (reply != RTEMS_FTPFS_REPLY_1) { + return EIO; + } + + return 0; +} + +static int rtems_ftpfs_open( + rtems_libio_t *iop, + const char *path, + uint32_t flags, + uint32_t mode +) +{ + int eno = 0; + bool ok = false; + rtems_ftpfs_entry *e = NULL; + rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info; + bool verbose = me->verbose; + const struct timeval *timeout = &me->timeout; + const char *user = NULL; + const char *password = NULL; + const char *hostname = NULL; + const char *filename = NULL; + const char *file_command = (iop->flags & LIBIO_FLAGS_WRITE) != 0 + ? "STOR " + : "RETR "; + uint32_t client_address = 0; + char *location = iop->pathinfo.node_access; + + /* Invalidate data handle */ + iop->data1 = NULL; + + /* Check location, it was allocated during path evaluation */ + if (location == NULL) { + rtems_set_errno_and_return_minus_one(ENOMEM); + } + + /* Split location into parts */ + ok = rtems_ftpfs_split_names( + location, + &user, + &password, + &hostname, + &filename + ); + if (!ok) { + if (strlen(location) == 0) { + /* + * This is an access to the root node that will be used for file system + * option settings. + */ + iop->pathinfo.handlers = &rtems_ftpfs_root_handlers; + + return 0; + } else { + rtems_set_errno_and_return_minus_one(ENOENT); + } + } + DEBUG_PRINTF( + "user = '%s', password = '%s', filename = '%s'\n", + user, + password, + filename + ); + + /* Check for either read-only or write-only flags */ + if ( + (iop->flags & LIBIO_FLAGS_WRITE) != 0 + && (iop->flags & LIBIO_FLAGS_READ) != 0 + ) { + rtems_set_errno_and_return_minus_one(ENOTSUP); + } + + /* Allocate connection entry */ + e = malloc(sizeof(*e)); + if (e == NULL) { + rtems_set_errno_and_return_minus_one(ENOMEM); + } + + /* Initialize connection entry */ + e->ctrl_socket = -1; + e->data_socket = -1; + e->eof = false; + + /* Save connection state */ + iop->data1 = e; + + /* Open control connection */ + eno = rtems_ftpfs_open_ctrl_connection( + e, + user, + password, + hostname, + &client_address, + verbose, + timeout + ); + if (eno != 0) { + goto cleanup; + } + + /* Open passive data connection */ + eno = rtems_ftpfs_open_data_connection_passive( + e, + client_address, + file_command, + filename, + verbose, + timeout + ); + if (eno == ENOTSUP) { + /* Open active data connection */ + eno = rtems_ftpfs_open_data_connection_active( + e, + client_address, + file_command, + filename, + verbose, + timeout + ); + } + if (eno != 0) { + goto cleanup; + } + + /* Set data connection timeout */ + eno = rtems_ftpfs_set_connection_timeout(e->data_socket, timeout); + +cleanup: + + if (eno == 0) { + return 0; + } else { + /* Free all resources if an error occured */ + rtems_ftpfs_terminate(iop, true); + + rtems_set_errno_and_return_minus_one(eno); + } +} + +static ssize_t rtems_ftpfs_read( + rtems_libio_t *iop, + void *buffer, + size_t count +) +{ + rtems_ftpfs_entry *e = iop->data1; + rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info; + bool verbose = me->verbose; + char *in = buffer; + size_t todo = count; + + if (e->eof) { + return 0; + } + + while (todo > 0) { + ssize_t rv = recv(e->data_socket, in, todo, 0); + + if (rv <= 0) { + if (rv == 0) { + rtems_ftpfs_reply reply = + rtems_ftpfs_get_reply(e->ctrl_socket, NULL, NULL, verbose); + + if (reply == RTEMS_FTPFS_REPLY_2) { + e->eof = true; + break; + } + } + + rtems_set_errno_and_return_minus_one(EIO); + } + + in += rv; + todo -= (size_t) rv; + } + + return (ssize_t) (count - todo); +} + +static ssize_t rtems_ftpfs_write( + rtems_libio_t *iop, + const void *buffer, + size_t count +) +{ + rtems_ftpfs_entry *e = iop->data1; + const char *out = buffer; + size_t todo = count; + + while (todo > 0) { + ssize_t rv = send(e->data_socket, out, todo, 0); + + if (rv <= 0) { + if (rv == 0) { + break; + } else { + rtems_set_errno_and_return_minus_one(EIO); + } + } + + out += rv; + todo -= (size_t) rv; + } + + return (ssize_t) (count - todo); +} + +static int rtems_ftpfs_close(rtems_libio_t *iop) +{ + int eno = rtems_ftpfs_terminate(iop, false); + + if (eno == 0) { + return 0; + } else { + rtems_set_errno_and_return_minus_one(eno); + } +} + +/* Dummy version to let fopen(*,"w") work properly */ +static int rtems_ftpfs_ftruncate(rtems_libio_t *iop, rtems_off64_t count) +{ + return 0; +} + +static int rtems_ftpfs_eval_path( + const char *pathname, + size_t pathnamelen, + int flags, + rtems_filesystem_location_info_t *pathloc +) +{ + /* + * The caller of this routine has striped off the mount prefix from the path. + * We need to store this path here or otherwise we would have to do this job + * again. The path is used in rtems_ftpfs_open() via iop->pathinfo.node_access. + */ + char *pathname_dup = malloc(pathnamelen + 1); + + if (pathname_dup != NULL) { + memcpy(pathname_dup, pathname, pathnamelen); + pathname_dup [pathnamelen] = '\0'; + } + + pathloc->node_access = pathname_dup; + + return 0; +} + +static int rtems_ftpfs_free_node(rtems_filesystem_location_info_t *pathloc) +{ + free(pathloc->node_access); + + return 0; +} + +static rtems_filesystem_node_types_t rtems_ftpfs_node_type( + rtems_filesystem_location_info_t *pathloc +) +{ + return RTEMS_FILESYSTEM_MEMORY_FILE; +} + +int rtems_ftpfs_initialize( + rtems_filesystem_mount_table_entry_t *e, + const void *d +) +{ + rtems_ftpfs_mount_entry *me = malloc(sizeof(rtems_ftpfs_mount_entry)); + + /* Mount entry for FTP file system instance */ + e->fs_info = me; + if (e->fs_info == NULL) { + rtems_set_errno_and_return_minus_one(ENOMEM); + } + me->verbose = false; + me->timeout.tv_sec = 0; + me->timeout.tv_usec = 0; + + /* Set handler and oparations table */ + e->mt_fs_root.handlers = &rtems_ftpfs_handlers; + e->mt_fs_root.ops = &rtems_ftpfs_ops; + + /* We maintain no real file system nodes, so there is no real root */ + e->mt_fs_root.node_access = NULL; + + /* Just use the limits from IMFS */ + e->pathconf_limits_and_options = IMFS_LIMITS_AND_OPTIONS; + + return 0; +} + +static int rtems_ftpfs_unmount_me( + rtems_filesystem_mount_table_entry_t *e +) +{ + free(e->fs_info); + + return 0; +} + +static int rtems_ftpfs_ioctl( + rtems_libio_t *iop, + uint32_t command, + void *arg +) +{ + rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info; + bool *verbose = arg; + struct timeval *timeout = arg; + + if (arg == NULL) { + rtems_set_errno_and_return_minus_one(EINVAL); + } + + switch (command) { + case RTEMS_FTPFS_IOCTL_GET_VERBOSE: + *verbose = me->verbose; + break; + case RTEMS_FTPFS_IOCTL_SET_VERBOSE: + me->verbose = *verbose; + break; + case RTEMS_FTPFS_IOCTL_GET_TIMEOUT: + *timeout = me->timeout; + break; + case RTEMS_FTPFS_IOCTL_SET_TIMEOUT: + me->timeout = *timeout; + break; + default: + rtems_set_errno_and_return_minus_one(EINVAL); + } + + return 0; +} + +/* + * The stat() support is intended only for the cp shell command. Each request + * will return that we have a regular file with read, write and execute + * permissions for every one. The node index uses a global counter to support + * a remote to remote copy. This is not a very sophisticated method. + */ +static int rtems_ftpfs_fstat( + rtems_filesystem_location_info_t *loc, + struct stat *st +) +{ + static unsigned ino = 0; + + memset(st, 0, sizeof(*st)); + + /* FIXME */ + st->st_ino = ++ino; + st->st_dev = rtems_filesystem_make_dev_t(0xcc494cd6U, 0x1d970b4dU); + + st->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO; + + return 0; +} + +static const rtems_filesystem_operations_table rtems_ftpfs_ops = { + .evalpath_h = rtems_ftpfs_eval_path, + .evalformake_h = rtems_filesystem_default_evalformake, + .link_h = rtems_filesystem_default_link, + .unlink_h = rtems_filesystem_default_unlink, + .node_type_h = rtems_ftpfs_node_type, + .mknod_h = rtems_filesystem_default_mknod, + .chown_h = rtems_filesystem_default_chown, + .freenod_h = rtems_ftpfs_free_node, + .mount_h = rtems_filesystem_default_mount, + .fsmount_me_h = rtems_ftpfs_initialize, + .unmount_h = rtems_filesystem_default_unmount, + .fsunmount_me_h = rtems_ftpfs_unmount_me, + .utime_h = rtems_filesystem_default_utime, + .eval_link_h = rtems_filesystem_default_evaluate_link, + .symlink_h = rtems_filesystem_default_symlink, + .readlink_h = rtems_filesystem_default_readlink +}; + +static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers = { + .open_h = rtems_ftpfs_open, + .close_h = rtems_ftpfs_close, + .read_h = rtems_ftpfs_read, + .write_h = rtems_ftpfs_write, + .ioctl_h = rtems_filesystem_default_ioctl, + .lseek_h = rtems_filesystem_default_lseek, + .fstat_h = rtems_ftpfs_fstat, + .fchmod_h = rtems_filesystem_default_fchmod, + .ftruncate_h = rtems_ftpfs_ftruncate, + .fpathconf_h = rtems_filesystem_default_fpathconf, + .fsync_h = rtems_filesystem_default_fsync, + .fdatasync_h = rtems_filesystem_default_fdatasync, + .fcntl_h = rtems_filesystem_default_fcntl, + .rmnod_h = rtems_filesystem_default_rmnod +}; + +static const rtems_filesystem_file_handlers_r rtems_ftpfs_root_handlers = { + .open_h = rtems_filesystem_default_open, + .close_h = rtems_filesystem_default_close, + .read_h = rtems_filesystem_default_read, + .write_h = rtems_filesystem_default_write, + .ioctl_h = rtems_ftpfs_ioctl, + .lseek_h = rtems_filesystem_default_lseek, + .fstat_h = rtems_filesystem_default_fstat, + .fchmod_h = rtems_filesystem_default_fchmod, + .ftruncate_h = rtems_filesystem_default_ftruncate, + .fpathconf_h = rtems_filesystem_default_fpathconf, + .fsync_h = rtems_filesystem_default_fsync, + .fdatasync_h = rtems_filesystem_default_fdatasync, + .fcntl_h = rtems_filesystem_default_fcntl, + .rmnod_h = rtems_filesystem_default_rmnod +}; diff --git a/cpukit/libnetworking/lib/getprotoby.c b/cpukit/libnetworking/lib/getprotoby.c new file mode 100644 index 0000000000..7cbbe32f1d --- /dev/null +++ b/cpukit/libnetworking/lib/getprotoby.c @@ -0,0 +1,50 @@ +/* + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <netdb.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> + +static const struct protoent prototab[] = { + { "ip", NULL, IPPROTO_IP }, + { "icmp", NULL, IPPROTO_ICMP }, + { "tcp", NULL, IPPROTO_TCP }, + { "udp", NULL, IPPROTO_UDP }, + }; + +/* + * Dummy version of BSD getprotobyname() + */ +struct protoent * +getprotobyname_static (const char *name) +{ + int i; + + for (i = 0 ; i < (sizeof prototab / sizeof prototab[0]) ; i++) { + if (strcmp (name, prototab[i].p_name) == 0) + return (struct protoent *) &prototab[i]; + } + return NULL; +} + +/* + * Dummy version of BSD getprotobynumber() + */ +struct protoent * +getprotobynumber_static (int proto) +{ + int i; + + for (i = 0 ; i < (sizeof prototab / sizeof prototab[0]) ; i++) { + if (proto == prototab[i].p_proto) + return (struct protoent *) &prototab[i]; + } + return NULL; +} diff --git a/cpukit/libnetworking/lib/rtems_bsdnet_ntp.c b/cpukit/libnetworking/lib/rtems_bsdnet_ntp.c new file mode 100644 index 0000000000..17de20ba8f --- /dev/null +++ b/cpukit/libnetworking/lib/rtems_bsdnet_ntp.c @@ -0,0 +1,218 @@ +/* + * Synchronize with an NTP server + * + * This program may be distributed and used for any purpose. + * I ask only that you: + * 1. Leave this author information intact. + * 2. Document any changes you make. + * + * W. Eric Norum + * Canadian Light Source + * University of Saskatchewan + * Saskatoon, Saskatchewan, CANADA + * eric@cls.usask.ca + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <unistd.h> /* close */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <limits.h> +#include <rtems.h> +#include <rtems/rtems_bsdnet.h> +#include <rtems/error.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include <rtems/bsdnet/servers.h> + +/* + * RTEMS base: 1988, January 1 + * UNIX base: 1970, January 1 + * NTP base: 1900, January 1 + */ +#define UNIX_BASE_TO_NTP_BASE (uint32_t)(((70UL*365UL)+17UL) * (24UL*60UL*60UL)) + +struct ntpPacket { + struct ntpPacketSmall ntp; + char authenticator[96]; +}; + +static int +processPacket (struct ntpPacketSmall *p, int state, void *unused) +{ + time_t tbuf; + struct tm *lt; + rtems_time_of_day rt; + rtems_interval ticks_per_second; + + if ( state ) + return 0; + + ticks_per_second = rtems_clock_get_ticks_per_second(); + tbuf = ntohl (p->transmit_timestamp.integer) - UNIX_BASE_TO_NTP_BASE - rtems_bsdnet_timeoffset; + lt = gmtime (&tbuf); + rt.year = lt->tm_year + 1900; + rt.month = lt->tm_mon + 1; + rt.day = lt->tm_mday; + rt.hour = lt->tm_hour; + rt.minute = lt->tm_min; + rt.second = lt->tm_sec; + rt.ticks = ntohl (p->transmit_timestamp.fraction) / (ULONG_MAX / ticks_per_second); + if (rt.ticks >= ticks_per_second) + rt.ticks = ticks_per_second - 1; + rtems_clock_set (&rt); + return 0; +} + +static int +getServerTimespec(struct ntpPacketSmall *p, int state, void *usr_data) +{ +struct timespec *ts = usr_data; +unsigned long long tmp; + + if ( 0 == state ) { + ts->tv_sec = ntohl( p->transmit_timestamp.integer ); + ts->tv_sec -= rtems_bsdnet_timeoffset + UNIX_BASE_TO_NTP_BASE; + + tmp = 1000000000 * (unsigned long long)ntohl(p->transmit_timestamp.fraction); + + ts->tv_nsec = (unsigned long) (tmp>>32); + } + return 0; +} + +int rtems_bsdnet_ntp_retry_count = 5; +int rtems_bsdnet_ntp_timeout_secs = 5; +int rtems_bsdnet_ntp_bcast_timeout_secs = 80; + +static int +tryServer (int i, int s, rtems_bsdnet_ntp_callback_t callback, void *usr_data) +{ + int l = 0; + struct timeval tv; + socklen_t farlen; + struct sockaddr_in farAddr; + struct ntpPacketSmall packet; + + if (i < 0) + tv.tv_sec = rtems_bsdnet_ntp_bcast_timeout_secs; + else + tv.tv_sec = rtems_bsdnet_ntp_timeout_secs; + tv.tv_usec = 0; + if (setsockopt (s, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv) < 0) { + fprintf (stderr, "rtems_bsdnet_get_ntp() Can't set socket receive timeout: %s\n", strerror (errno)); + close (s); + return -1; + } + if (i >= 0) { + memset (&farAddr, 0, sizeof farAddr); + farAddr.sin_family = AF_INET; + farAddr.sin_port = htons (123); + farAddr.sin_addr = rtems_bsdnet_ntpserver[i]; + memset (&packet, 0, sizeof packet); + packet.li_vn_mode = (3 << 3) | 3; /* NTP version 3, client */ + if ( callback( &packet, 1, usr_data ) ) + return -1; + l = sendto (s, &packet, sizeof packet, 0, (struct sockaddr *)&farAddr, sizeof farAddr); + if (l != sizeof packet) { + fprintf (stderr, "rtems_bsdnet_get_ntp() Can't send: %s\n", strerror (errno)); + return -1; + } + } else { + if ( callback( &packet, -1, usr_data ) ) + return -1; + } + farlen = sizeof farAddr; + i = recvfrom (s, &packet, sizeof packet, 0, (struct sockaddr *)&farAddr, &farlen); + if (i == 0) + fprintf (stderr, "rtems_bsdnet_get_ntp() Unexpected EOF"); + if (i < 0) { + if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) + return -1; + fprintf (stderr, "rtems_bsdnet_get_ntp() Can't receive: %s\n", strerror (errno)); + } + + if ( i >= sizeof packet && + ((packet.li_vn_mode & (0x7 << 3)) == (3 << 3)) && + ((packet.transmit_timestamp.integer != 0) || (packet.transmit_timestamp.fraction != 0)) && + 0 == callback( &packet, 0 , usr_data) ) + return 0; + + return -1; +} + +int rtems_bsdnet_get_ntp(int sock, rtems_bsdnet_ntp_callback_t callback, void *usr_data) +{ +int s = -1; +int i; +int retry; +struct sockaddr_in myAddr; +int reuseFlag; +int ret; + + if ( !callback ) + callback = getServerTimespec; + + if ( sock < 0 ) { + s = socket (AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + fprintf (stderr, "rtems_bsdnet_get_ntp() Can't create socket: %s\n", strerror (errno)); + return -1; + } + reuseFlag = 1; + if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseFlag, sizeof reuseFlag) < 0) { + fprintf (stderr, "rtems_bsdnet_get_ntp() Can't set socket reuse: %s\n", strerror (errno)); + close (s); + return -1; + } + memset (&myAddr, 0, sizeof myAddr); + myAddr.sin_family = AF_INET; + myAddr.sin_port = htons (0); + myAddr.sin_addr.s_addr = htonl (INADDR_ANY); + if (bind (s, (struct sockaddr *)&myAddr, sizeof myAddr) < 0) { + fprintf (stderr, "rtems_bsdnet_get_ntp() Can't bind socket: %s\n", strerror (errno)); + close (s); + return -1; + } + sock = s; + } + ret = -1; + for (retry = 0 ; (ret == -1) && (retry < rtems_bsdnet_ntp_retry_count) ; retry++) { + /* + * If there's no server we just have to wait + * and hope that there's an NTP broadcast + * server out there somewhere. + */ + if (rtems_bsdnet_ntpserver_count < 0) { + ret = tryServer (-1, sock, callback, usr_data); + } + else { + for (i = 0 ; (ret == -1) && (i < rtems_bsdnet_ntpserver_count) ; i++) { + ret = tryServer (i, sock, callback, usr_data); + } + } + } + if ( s >= 0 ) + close (s); + return ret; +} + +int +rtems_bsdnet_synchronize_ntp (int interval, rtems_task_priority priority) +{ + if (interval != 0) { + fprintf (stderr, "Daemon-mode note yet supported.\n"); + errno = EINVAL; + return -1; + } + return rtems_bsdnet_get_ntp( -1, processPacket, 0); +} diff --git a/cpukit/libnetworking/lib/syslog.c b/cpukit/libnetworking/lib/syslog.c new file mode 100644 index 0000000000..309d2d78f8 --- /dev/null +++ b/cpukit/libnetworking/lib/syslog.c @@ -0,0 +1,194 @@ +/* + * RTEMS version of syslog and associated routines + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <syslog.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> + +#include <unistd.h> + +static int LogStatus = LOG_CONS; +static const char *LogTag = "syslog"; +static int LogFacility = LOG_USER; +static int LogMask = 0xff; + +static int LogFd = -1; +static rtems_id LogSemaphore; +extern struct in_addr rtems_bsdnet_log_host_address; + +#define SYSLOG_PORT 514 + +void +syslog (int pri, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + vsyslog (pri, fmt, ap); + va_end (ap); +} + +/* + * FIXME: Should cbuf be static? It could be if we put the mutex + * around the entire body of this routine. Then we wouldn't + * have to worry about blowing stacks with a local variable + * that large. Could make cbuf bigger, too. + */ +void +vsyslog (int pri, const char *fmt, va_list ap) +{ + int cnt; + char *cp; + char *msgp, cbuf[200]; + int sent; + + if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) { + syslog (LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID, + "syslog: unknown facility/priority: %#x", pri); + pri &= LOG_PRIMASK|LOG_FACMASK; + } + + if (!(LOG_MASK(LOG_PRI(pri)) & LogMask)) + return; + + if ((pri & LOG_FACMASK) == 0) + pri |= LogFacility; + + cnt = sprintf (cbuf, "<%d>", pri); + cp = msgp = cbuf + cnt; + if (LogTag) { + const char *lp = LogTag; + while ((*cp = *lp++) != '\0') + cp++; + } + if (LogStatus & LOG_PID) { + rtems_id tid; + rtems_task_ident (RTEMS_SELF, 0, &tid); + cnt = sprintf (cp, "[%#lx]", (unsigned long)tid); + cp += cnt; + } + if (LogTag) { + *cp++ = ':'; + *cp++ = ' '; + } + cnt = vsprintf (cp, fmt, ap); + cnt += cp - cbuf; + if (cbuf[cnt-1] == '\n') + cbuf[--cnt] = '\0'; + + if (LogStatus & LOG_PERROR) + printf ("%s\n", cbuf); + + /* + * Grab the mutex + */ + sent = 0; + if ((rtems_bsdnet_log_host_address.s_addr != INADDR_ANY) + && (LogFd >= 0) + && (rtems_semaphore_obtain (LogSemaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT) == RTEMS_SUCCESSFUL)) { + /* + * Set the destination address/port + */ + struct sockaddr_in farAddress; + farAddress.sin_family = AF_INET; + farAddress.sin_port = htons (SYSLOG_PORT); + farAddress.sin_addr = rtems_bsdnet_log_host_address; + memset (farAddress.sin_zero, '\0', sizeof farAddress.sin_zero); + + /* + * Send the message + */ + if (sendto (LogFd, cbuf, cnt, 0, (struct sockaddr *)&farAddress, sizeof farAddress) >= 0) + sent = 1; + rtems_semaphore_release (LogSemaphore); + } + if (!sent && (LogStatus & LOG_CONS) && !(LogStatus & LOG_PERROR)) + printf ("%s\n", msgp); +} + +void +openlog (const char *ident, int logstat, int logfac) +{ + rtems_status_code sc; + struct sockaddr_in myAddress; + + if (ident != NULL) + LogTag = ident; + LogStatus = logstat; + if (logfac != 0 && (logfac & ~LOG_FACMASK) == 0) + LogFacility = logfac; + + /* + * Create the socket + */ + if ((LogFd = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { + printf ("Can't create syslog socket: %d\n", errno); + return; + } + + /* + * Bind socket to name + */ + myAddress.sin_family = AF_INET; + myAddress.sin_addr.s_addr = INADDR_ANY; + myAddress.sin_port = 0; + memset (myAddress.sin_zero, '\0', sizeof myAddress.sin_zero); + if (bind (LogFd, (struct sockaddr *)&myAddress, sizeof (myAddress)) < 0) { + close (LogFd); + LogFd = -1; + printf ("Can't bind syslog socket: %d\n", errno); + return; + } + + /* + * Create the mutex + */ + sc = rtems_semaphore_create (rtems_build_name('s', 'L', 'o', 'g'), + 1, + RTEMS_PRIORITY | + RTEMS_BINARY_SEMAPHORE | + RTEMS_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | + RTEMS_LOCAL, + 0, + &LogSemaphore); + if (sc != RTEMS_SUCCESSFUL) { + printf ("Can't create syslog semaphore: %d\n", sc); + close (LogFd); + LogFd = -1; + } +} + +void +closelog(void) +{ + if (LogFd >= 0) { + close (LogFd); + LogFd = -1; + rtems_semaphore_delete (LogSemaphore); + } +} + +int +setlogmask (int pmask) +{ + int omask; + + omask = LogMask; + if (pmask != 0) + LogMask = pmask; + return (omask); +} diff --git a/cpukit/libnetworking/lib/tftpDriver.c b/cpukit/libnetworking/lib/tftpDriver.c new file mode 100644 index 0000000000..4122c3d96f --- /dev/null +++ b/cpukit/libnetworking/lib/tftpDriver.c @@ -0,0 +1,1119 @@ +/* + * Trivial File Transfer Protocol (RFC 1350) + * + * Transfer file to/from remote host + * + * W. Eric Norum + * Saskatchewan Accelerator Laboratory + * University of Saskatchewan + * Saskatoon, Saskatchewan, CANADA + * eric@skatter.usask.ca + * + * $Id$ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <malloc.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <rtems.h> +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/seterr.h> +#include <rtems/rtems_bsdnet.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#ifdef RTEMS_TFTP_DRIVER_DEBUG +int rtems_tftp_driver_debug = 1; +#endif + +/* + * Range of UDP ports to try + */ +#define UDP_PORT_BASE 3180 + +/* + * Default limits + */ +#define PACKET_FIRST_TIMEOUT_MILLISECONDS 400L +#define PACKET_TIMEOUT_MILLISECONDS 6000L +#define OPEN_RETRY_LIMIT 10 +#define IO_RETRY_LIMIT 10 + +/* + * TFTP opcodes + */ +#define TFTP_OPCODE_RRQ 1 +#define TFTP_OPCODE_WRQ 2 +#define TFTP_OPCODE_DATA 3 +#define TFTP_OPCODE_ACK 4 +#define TFTP_OPCODE_ERROR 5 + +/* + * Largest data transfer + */ +#define TFTP_BUFSIZE 512 + +/* + * Packets transferred between machines + */ +union tftpPacket { + /* + * RRQ/WRQ packet + */ + struct tftpRWRQ { + uint16_t opcode; + char filename_mode[TFTP_BUFSIZE]; + } tftpRWRQ; + + /* + * DATA packet + */ + struct tftpDATA { + uint16_t opcode; + uint16_t blocknum; + uint8_t data[TFTP_BUFSIZE]; + } tftpDATA; + + /* + * ACK packet + */ + struct tftpACK { + uint16_t opcode; + uint16_t blocknum; + } tftpACK; + + /* + * ERROR packet + */ + struct tftpERROR { + uint16_t opcode; + uint16_t errorCode; + char errorMessage[TFTP_BUFSIZE]; + } tftpERROR; +}; + +/* + * State of each TFTP stream + */ +struct tftpStream { + /* + * Buffer for storing most recently-received packet + */ + union tftpPacket pkbuf; + + /* + * Last block number transferred + */ + uint16_t blocknum; + + /* + * Data transfer socket + */ + int socket; + struct sockaddr_in myAddress; + struct sockaddr_in farAddress; + + /* + * Indices into buffer + */ + int nleft; + int nused; + + /* + * Flags + */ + int firstReply; + int eof; + int writing; +}; + +/* + * Flags for filesystem info. + */ +#define TFTPFS_VERBOSE (1 << 0) + +/* + * Root node_access value + * By using the address of the file system + * we ensure a unique value for this identifier. + */ +#define ROOT_NODE_ACCESS(_fs) (_fs) + +/* + * TFTP File system info. + */ +typedef struct tftpfs_info_s { + uint32_t flags; + rtems_id tftp_mutex; + int nStreams; + struct tftpStream ** volatile tftpStreams; +} tftpfs_info_t; + +#define tftpfs_info_mount_table(_mt) ((tftpfs_info_t*) ((_mt)->fs_info)) +#define tftpfs_info_pathloc(_pl) ((tftpfs_info_t*) ((_pl)->mt_entry->fs_info)) +#define tftpfs_info_iop(_iop) (tftpfs_info_pathloc (&((_iop)->pathinfo))) + +/* + * Number of streams open at the same time + */ + +typedef const char *tftp_node; +static const rtems_filesystem_operations_table rtems_tftp_ops; +static const rtems_filesystem_file_handlers_r rtems_tftp_handlers; + +int rtems_tftpfs_initialize( + rtems_filesystem_mount_table_entry_t *mt_entry, + const void *data +) +{ + tftpfs_info_t *fs; + rtems_status_code sc; + + mt_entry->mt_fs_root.handlers = &rtems_tftp_handlers; + mt_entry->mt_fs_root.ops = &rtems_tftp_ops; + + /* + * We have no tftp filesystem specific data to maintain. This + * filesystem may only be mounted ONCE. + * + * And we maintain no real filesystem nodes, so there is no real root. + */ + + fs = malloc (sizeof (tftpfs_info_t)); + if (!fs) + rtems_set_errno_and_return_minus_one (ENOMEM); + + fs->flags = 0; + fs->nStreams = 0; + fs->tftpStreams = 0; + + mt_entry->fs_info = fs; + mt_entry->mt_fs_root.node_access = ROOT_NODE_ACCESS (fs); + mt_entry->mt_fs_root.node_access_2 = NULL; + + /* + * Now allocate a semaphore for mutual exclusion. + * + * NOTE: This could be in an fsinfo for this filesystem type. + */ + + sc = rtems_semaphore_create ( + rtems_build_name('T', 'F', 'T', 'P'), + 1, + RTEMS_FIFO | + RTEMS_BINARY_SEMAPHORE | + RTEMS_NO_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | + RTEMS_LOCAL, + 0, + &fs->tftp_mutex + ); + + if (sc != RTEMS_SUCCESSFUL) + rtems_set_errno_and_return_minus_one (ENOMEM); + + if (data) { + char* config = (char*) data; + char* token; + char* saveptr; + token = strtok_r (config, " ", &saveptr); + while (token) { + if (strcmp (token, "verbose") == 0) + fs->flags |= TFTPFS_VERBOSE; + token = strtok_r (NULL, " ", &saveptr); + } + } + + return 0; +} + +/* + * Release a stream and clear the pointer to it + */ +static void +releaseStream (tftpfs_info_t *fs, int s) +{ + if (fs->tftpStreams[s] && (fs->tftpStreams[s]->socket >= 0)) + close (fs->tftpStreams[s]->socket); + rtems_semaphore_obtain (fs->tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + free (fs->tftpStreams[s]); + fs->tftpStreams[s] = NULL; + rtems_semaphore_release (fs->tftp_mutex); +} + +static int +rtems_tftpfs_shutdown (rtems_filesystem_mount_table_entry_t* mt_entry) +{ + tftpfs_info_t *fs = tftpfs_info_mount_table (mt_entry); + int s; + for (s = 0; s < fs->nStreams; s++) + releaseStream (fs, s); + rtems_semaphore_delete (fs->tftp_mutex); + free (fs); + return 0; +} + +/* + * Map error message + */ +static int +tftpErrno (struct tftpStream *tp) +{ + unsigned int tftpError; + static const int errorMap[] = { + EINVAL, + ENOENT, + EPERM, + ENOSPC, + EINVAL, + ENXIO, + EEXIST, + ESRCH, + }; + + tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode); + if (tftpError < (sizeof errorMap / sizeof errorMap[0])) + return errorMap[tftpError]; + else + return 1000 + tftpError; +} + +/* + * Send a message to make the other end shut up + */ +static void +sendStifle (struct tftpStream *tp, struct sockaddr_in *to) +{ + int len; + struct { + uint16_t opcode; + uint16_t errorCode; + char errorMessage[12]; + } msg; + + /* + * Create the error packet (Unknown transfer ID). + */ + msg.opcode = htons (TFTP_OPCODE_ERROR); + msg.errorCode = htons (5); + len = sizeof msg.opcode + sizeof msg.errorCode + 1; + len += sprintf (msg.errorMessage, "GO AWAY"); + + /* + * Send it + */ + sendto (tp->socket, (char *)&msg, len, 0, (struct sockaddr *)to, sizeof *to); +} + +/* + * Wait for a data packet + */ +static int +getPacket (struct tftpStream *tp, int retryCount) +{ + int len; + struct timeval tv; + + if (retryCount == 0) { + tv.tv_sec = PACKET_FIRST_TIMEOUT_MILLISECONDS / 1000L; + tv.tv_usec = (PACKET_FIRST_TIMEOUT_MILLISECONDS % 1000L) * 1000L; + } + else { + tv.tv_sec = PACKET_TIMEOUT_MILLISECONDS / 1000L; + tv.tv_usec = (PACKET_TIMEOUT_MILLISECONDS % 1000L) * 1000L; + } + setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); + for (;;) { + union { + struct sockaddr s; + struct sockaddr_in i; + } from; + socklen_t fromlen = sizeof from; + len = recvfrom (tp->socket, &tp->pkbuf, + sizeof tp->pkbuf, 0, + &from.s, &fromlen); + if (len < 0) + break; + if (from.i.sin_addr.s_addr == tp->farAddress.sin_addr.s_addr) { + if (tp->firstReply) { + tp->firstReply = 0; + tp->farAddress.sin_port = from.i.sin_port; + } + if (tp->farAddress.sin_port == from.i.sin_port) + break; + } + + /* + * Packet is from someone with whom we are + * not interested. Tell them to go away. + */ + sendStifle (tp, &from.i); + } + tv.tv_sec = 0; + tv.tv_usec = 0; + setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); +#ifdef RTEMS_TFTP_DRIVER_DEBUG + if (rtems_tftp_driver_debug) { + if (len >= (int) sizeof tp->pkbuf.tftpACK) { + int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); + switch (opcode) { + default: + printf ("TFTP: OPCODE %d\n", opcode); + break; + + case TFTP_OPCODE_DATA: + printf ("TFTP: RECV %d\n", ntohs (tp->pkbuf.tftpDATA.blocknum)); + break; + + case TFTP_OPCODE_ACK: + printf ("TFTP: GOT ACK %d\n", ntohs (tp->pkbuf.tftpACK.blocknum)); + break; + } + } + else { + printf ("TFTP: %d-byte packet\n", len); + } + } +#endif + return len; +} + +/* + * Send an acknowledgement + */ +static int +sendAck (struct tftpStream *tp) +{ +#ifdef RTEMS_TFTP_DRIVER_DEBUG + if (rtems_tftp_driver_debug) + printf ("TFTP: ACK %d\n", tp->blocknum); +#endif + + /* + * Create the acknowledgement + */ + tp->pkbuf.tftpACK.opcode = htons (TFTP_OPCODE_ACK); + tp->pkbuf.tftpACK.blocknum = htons (tp->blocknum); + + /* + * Send it + */ + if (sendto (tp->socket, (char *)&tp->pkbuf, sizeof tp->pkbuf.tftpACK, 0, + (struct sockaddr *)&tp->farAddress, + sizeof tp->farAddress) < 0) + return errno; + return 0; +} + +static int rtems_tftp_evaluate_for_make( + const char *path __attribute__((unused)), /* IN */ + rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ + const char **name __attribute__((unused)) /* OUT */ +) +{ + pathloc->node_access = NULL; + pathloc->node_access_2 = NULL; + rtems_set_errno_and_return_minus_one (EIO); +} + +/* + * Convert a path to canonical form + */ +static void +fixPath (char *path) +{ + char *inp, *outp, *base; + + outp = inp = path; + base = NULL; + for (;;) { + if (inp[0] == '.') { + if (inp[1] == '\0') + break; + if (inp[1] == '/') { + inp += 2; + continue; + } + if (inp[1] == '.') { + if (inp[2] == '\0') { + if ((base != NULL) && (outp > base)) { + outp--; + while ((outp > base) && (outp[-1] != '/')) + outp--; + } + break; + } + if (inp[2] == '/') { + inp += 3; + if (base == NULL) + continue; + if (outp > base) { + outp--; + while ((outp > base) && (outp[-1] != '/')) + outp--; + } + continue; + } + } + } + if (base == NULL) + base = inp; + while (inp[0] != '/') { + if ((*outp++ = *inp++) == '\0') + return; + } + *outp++ = '/'; + while (inp[0] == '/') + inp++; + } + *outp = '\0'; + return; +} + +static int rtems_tftp_eval_path( + const char *pathname, /* IN */ + size_t pathnamelen, /* IN */ + int flags, /* IN */ + rtems_filesystem_location_info_t *pathloc /* IN/OUT */ +) +{ + tftpfs_info_t *fs; + char *cp; + + /* + * Get the file system info. + */ + fs = tftpfs_info_pathloc (pathloc); + + pathloc->handlers = &rtems_tftp_handlers; + + /* + * Hack to provide the illusion of directories inside the TFTP file system. + * Paths ending in a / are assumed to be directories. + */ + if (pathname[strlen(pathname)-1] == '/') { + int nal = 0; + if (pathloc->node_access != ROOT_NODE_ACCESS (fs)) + nal = strlen(pathloc->node_access); + cp = malloc(nal + pathnamelen + 1); + if (cp == NULL) + rtems_set_errno_and_return_minus_one(ENOMEM); + if (nal) + memcpy (cp, pathloc->node_access, nal); + memcpy(cp + nal, pathname, pathnamelen); + cp[nal + pathnamelen] = '\0'; + fixPath (cp); + pathloc->node_access = cp; + } + else { + if (pathnamelen) { + /* + * Reject it if it's not read-only or write-only. + */ + flags &= RTEMS_LIBIO_PERMS_READ | RTEMS_LIBIO_PERMS_WRITE; + if ((flags != RTEMS_LIBIO_PERMS_READ) \ + && (flags != RTEMS_LIBIO_PERMS_WRITE)) + rtems_set_errno_and_return_minus_one(EINVAL); + + cp = malloc(pathnamelen + 1); + if (cp == NULL) + rtems_set_errno_and_return_minus_one(ENOMEM); + memcpy(cp, pathname, pathnamelen); + cp[pathnamelen] = '\0'; + fixPath (cp); + pathloc->node_access_2 = cp; + } + } + + return 0; +} + +/* + * The routine which does most of the work for the IMFS open handler + */ +static int rtems_tftp_open_worker( + rtems_libio_t *iop, + char *full_path_name, + uint32_t flags, + uint32_t mode __attribute__((unused)) +) +{ + tftpfs_info_t *fs; + struct tftpStream *tp; + int retryCount; + struct in_addr farAddress; + int s; + int len; + char *cp1; + char *cp2; + char *remoteFilename; + rtems_interval now; + rtems_status_code sc; + char *hostname; + + /* + * Get the file system info. + */ + fs = tftpfs_info_iop (iop); + + /* + * Extract the host name component + */ + hostname = full_path_name; + cp1 = strchr (full_path_name, ':'); + if (!cp1) + hostname = "BOOTP_HOST"; + else { + *cp1 = '\0'; + ++cp1; + } + + /* + * Convert hostname to Internet address + */ + if (strcmp (hostname, "BOOTP_HOST") == 0) + farAddress = rtems_bsdnet_bootp_server_address; + else if (inet_aton (hostname, &farAddress) == 0) { + struct hostent *he = gethostbyname(hostname); + if (he == NULL) + return ENOENT; + memcpy (&farAddress, he->h_addr, sizeof (farAddress)); + } else { + return ENOENT; + } + + /* + * Extract file pathname component + */ + if (strcmp (cp1, "BOOTP_FILE") == 0) { + cp1 = rtems_bsdnet_bootp_boot_file_name; + } + if (*cp1 == '\0') + return ENOENT; + remoteFilename = cp1; + if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10)) + return ENOENT; + + /* + * Find a free stream + */ + sc = rtems_semaphore_obtain (fs->tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + return EBUSY; + for (s = 0 ; s < fs->nStreams ; s++) { + if (fs->tftpStreams[s] == NULL) + break; + } + if (s == fs->nStreams) { + /* + * Reallocate stream pointers + * Guard against the case where realloc() returns NULL. + */ + struct tftpStream **np; + + np = realloc (fs->tftpStreams, ++fs->nStreams * sizeof *fs->tftpStreams); + if (np == NULL) { + rtems_semaphore_release (fs->tftp_mutex); + return ENOMEM; + } + fs->tftpStreams = np; + } + tp = fs->tftpStreams[s] = malloc (sizeof (struct tftpStream)); + rtems_semaphore_release (fs->tftp_mutex); + if (tp == NULL) + return ENOMEM; + iop->data0 = s; + iop->data1 = tp; + + /* + * Create the socket + */ + if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { + releaseStream (fs, s); + return ENOMEM; + } + + /* + * Bind the socket to a local address + */ + retryCount = 0; + now = rtems_clock_get_ticks_since_boot(); + for (;;) { + int try = (now + retryCount) % 10; + + tp->myAddress.sin_family = AF_INET; + tp->myAddress.sin_port = htons (UDP_PORT_BASE + fs->nStreams * try + s); + tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY); + if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof tp->myAddress) >= 0) + break; + if (++retryCount == 10) { + releaseStream (fs, s); + return EBUSY; + } + } + + /* + * Set the UDP destination to the TFTP server + * port on the remote machine. + */ + tp->farAddress.sin_family = AF_INET; + tp->farAddress.sin_addr = farAddress; + tp->farAddress.sin_port = htons (69); + + /* + * Start the transfer + */ + tp->firstReply = 1; + retryCount = 0; + for (;;) { + /* + * Create the request + */ + if ((flags & O_ACCMODE) == O_RDONLY) { + tp->writing = 0; + tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ); + } + else { + tp->writing = 1; + tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_WRQ); + } + cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode; + cp2 = (char *) remoteFilename; + while ((*cp1++ = *cp2++) != '\0') + continue; + cp2 = "octet"; + while ((*cp1++ = *cp2++) != '\0') + continue; + len = cp1 - (char *)&tp->pkbuf.tftpRWRQ; + + /* + * Send the request + */ + if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0, + (struct sockaddr *)&tp->farAddress, + sizeof tp->farAddress) < 0) { + releaseStream (fs, s); + return EIO; + } + + /* + * Get reply + */ + len = getPacket (tp, retryCount); + if (len >= (int) sizeof tp->pkbuf.tftpACK) { + int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); + if (!tp->writing + && (opcode == TFTP_OPCODE_DATA) + && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) { + tp->nused = 0; + tp->blocknum = 1; + tp->nleft = len - 2 * sizeof (uint16_t ); + tp->eof = (tp->nleft < TFTP_BUFSIZE); + if (sendAck (tp) != 0) { + releaseStream (fs, s); + return EIO; + } + break; + } + if (tp->writing + && (opcode == TFTP_OPCODE_ACK) + && (ntohs (tp->pkbuf.tftpACK.blocknum) == 0)) { + tp->nused = 0; + tp->blocknum = 1; + break; + } + if (opcode == TFTP_OPCODE_ERROR) { + int e = tftpErrno (tp); + releaseStream (fs, s); + return e; + } + } + + /* + * Keep trying + */ + if (++retryCount >= OPEN_RETRY_LIMIT) { + releaseStream (fs, s); + return EIO; + } + } + return 0; +} + +/* + * The IMFS open handler + */ +static int rtems_tftp_open( + rtems_libio_t *iop, + const char *new_name, + uint32_t flags, + uint32_t mode +) +{ + tftpfs_info_t *fs; + const char *device; + char *full_path_name; + char *na; + char *na2; + int dlen; + int nalen; + int na2len; + int sep1; + int err; + + /* + * Get the file system info. + */ + fs = tftpfs_info_iop (iop); + + /* + * Tack the prefix directory if one exists from the device name. + */ + device = + rtems_filesystem_mount_device (rtems_filesystem_location_mount (&iop->pathinfo)); + dlen = strlen (device); + if (dlen == 0) + rtems_set_errno_and_return_minus_one (ENOENT); + + if (iop->pathinfo.node_access_2 == NULL) + rtems_set_errno_and_return_minus_one (ENOENT); + + if (iop->pathinfo.node_access != ROOT_NODE_ACCESS (fs)) { + na = iop->pathinfo.node_access; + nalen = strlen (na); + } + else { + na = NULL; + nalen = 0; + } + + na2 = iop->pathinfo.node_access_2; + + na2len = strlen (na2); + + if (nalen) { + sep1 = 1; + if (na[nalen] == '/') { + sep1 = 0; + if (na2[0] == '/') + ++na2; + } + else { + if (na2[0] == '/') + sep1 = 0; + else + sep1 = 1; + } + } + else + sep1 = 0; + + full_path_name = malloc (dlen + nalen + sep1 + na2len + 1); + if (full_path_name == NULL) + rtems_set_errno_and_return_minus_one(ENOMEM); + strcpy (full_path_name, device); + if (nalen) + strcat (full_path_name, na); + if (sep1) + strcat (full_path_name, "/"); + strcat (full_path_name, na2); + fixPath (full_path_name); + + if (fs->flags & TFTPFS_VERBOSE) + printf ("TFTPFS: %s %s %s -> %s\n", device, na, na2, full_path_name); + + err = rtems_tftp_open_worker (iop, full_path_name, flags, mode); + free (full_path_name); + rtems_set_errno_and_return_minus_one(err); +} + +/* + * Read from a TFTP stream + */ +static ssize_t rtems_tftp_read( + rtems_libio_t *iop, + void *buffer, + size_t count +) +{ + char *bp; + struct tftpStream *tp = iop->data1; + int retryCount; + int nwant; + + if (!tp) + rtems_set_errno_and_return_minus_one( EIO ); + + /* + * Read till user request is satisfied or EOF is reached + */ + bp = buffer; + nwant = count; + while (nwant) { + if (tp->nleft) { + int ncopy; + if (nwant < tp->nleft) + ncopy = nwant; + else + ncopy = tp->nleft; + memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], ncopy); + tp->nused += ncopy; + tp->nleft -= ncopy; + bp += ncopy; + nwant -= ncopy; + if (nwant == 0) + break; + } + if (tp->eof) + break; + + /* + * Wait for the next packet + */ + retryCount = 0; + for (;;) { + int len = getPacket (tp, retryCount); + if (len >= (int)sizeof tp->pkbuf.tftpACK) { + int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); + uint16_t nextBlock = tp->blocknum + 1; + if ((opcode == TFTP_OPCODE_DATA) + && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) { + tp->nused = 0; + tp->nleft = len - 2 * sizeof (uint16_t); + tp->eof = (tp->nleft < TFTP_BUFSIZE); + tp->blocknum++; + if (sendAck (tp) != 0) + rtems_set_errno_and_return_minus_one (EIO); + break; + } + if (opcode == TFTP_OPCODE_ERROR) + rtems_set_errno_and_return_minus_one (tftpErrno (tp)); + } + + /* + * Keep trying? + */ + if (++retryCount == IO_RETRY_LIMIT) + rtems_set_errno_and_return_minus_one (EIO); + if (sendAck (tp) != 0) + rtems_set_errno_and_return_minus_one (EIO); + } + } + return count - nwant; +} + +/* + * Flush a write buffer and wait for acknowledgement + */ +static int rtems_tftp_flush (struct tftpStream *tp) +{ + int wlen, rlen; + int retryCount = 0; + + wlen = tp->nused + 2 * sizeof (uint16_t ); + for (;;) { + tp->pkbuf.tftpDATA.opcode = htons (TFTP_OPCODE_DATA); + tp->pkbuf.tftpDATA.blocknum = htons (tp->blocknum); +#ifdef RTEMS_TFTP_DRIVER_DEBUG + if (rtems_tftp_driver_debug) + printf ("TFTP: SEND %d (%d)\n", tp->blocknum, tp->nused); +#endif + if (sendto (tp->socket, (char *)&tp->pkbuf, wlen, 0, + (struct sockaddr *)&tp->farAddress, + sizeof tp->farAddress) < 0) + return EIO; + rlen = getPacket (tp, retryCount); + /* + * Our last packet won't necessarily be acknowledged! + */ + if ((rlen < 0) && (tp->nused < sizeof tp->pkbuf.tftpDATA.data)) + return 0; + if (rlen >= (int)sizeof tp->pkbuf.tftpACK) { + int opcode = ntohs (tp->pkbuf.tftpACK.opcode); + if ((opcode == TFTP_OPCODE_ACK) + && (ntohs (tp->pkbuf.tftpACK.blocknum) == tp->blocknum)) { + tp->nused = 0; + tp->blocknum++; + return 0; + } + if (opcode == TFTP_OPCODE_ERROR) + return tftpErrno (tp); + } + + /* + * Keep trying? + */ + if (++retryCount == IO_RETRY_LIMIT) + return EIO; + } +} + +/* + * Close a TFTP stream + */ +static int rtems_tftp_close( + rtems_libio_t *iop +) +{ + tftpfs_info_t *fs; + struct tftpStream *tp = iop->data1; + int e = 0; + + /* + * Get the file system info. + */ + fs = tftpfs_info_iop (iop); + + if (!tp) + rtems_set_errno_and_return_minus_one (EIO); + + if (tp->writing) + e = rtems_tftp_flush (tp); + if (!tp->eof && !tp->firstReply) { + /* + * Tell the other end to stop + */ + rtems_interval ticksPerSecond; + sendStifle (tp, &tp->farAddress); + ticksPerSecond = rtems_clock_get_ticks_per_second(); + rtems_task_wake_after (1 + ticksPerSecond / 10); + } + releaseStream (fs, iop->data0); + rtems_set_errno_and_return_minus_one (e); +} + +static ssize_t rtems_tftp_write( + rtems_libio_t *iop, + const void *buffer, + size_t count +) +{ + const char *bp; + struct tftpStream *tp = iop->data1; + int nleft, nfree, ncopy; + + /* + * Bail out if an error has occurred + */ + if (!tp->writing) + rtems_set_errno_and_return_minus_one (EIO); + + /* + * Write till user request is satisfied + * Notice that the buffer is flushed as soon as it is filled rather + * than waiting for the next write or a close. This ensures that + * the flush in close writes a less than full buffer so the far + * end can detect the end-of-file condition. + */ + bp = buffer; + nleft = count; + while (nleft) { + nfree = sizeof tp->pkbuf.tftpDATA.data - tp->nused; + if (nleft < nfree) + ncopy = nleft; + else + ncopy = nfree; + memcpy (&tp->pkbuf.tftpDATA.data[tp->nused], bp, ncopy); + tp->nused += ncopy; + nleft -= ncopy; + bp += ncopy; + if (tp->nused == sizeof tp->pkbuf.tftpDATA.data) { + int e = rtems_tftp_flush (tp); + if (e) { + tp->writing = 0; + rtems_set_errno_and_return_minus_one (e); + } + } + } + return count; +} + +/* + * Dummy version to let fopen(xxxx,"w") work properly. + */ +static int rtems_tftp_ftruncate( + rtems_libio_t *iop __attribute__((unused)), + rtems_off64_t count __attribute__((unused)) +) +{ + return 0; +} + +static rtems_filesystem_node_types_t rtems_tftp_node_type( + rtems_filesystem_location_info_t *pathloc /* IN */ +) +{ + tftpfs_info_t *fs = tftpfs_info_pathloc (pathloc); + if ((pathloc->node_access == NULL) + || (pathloc->node_access_2 != NULL) + || (pathloc->node_access == ROOT_NODE_ACCESS (fs))) + return RTEMS_FILESYSTEM_MEMORY_FILE; + return RTEMS_FILESYSTEM_DIRECTORY; +} + +static int rtems_tftp_free_node_info( + rtems_filesystem_location_info_t *pathloc /* IN */ +) +{ + tftpfs_info_t *fs = tftpfs_info_pathloc (pathloc); + if (pathloc->node_access && \ + (pathloc->node_access != ROOT_NODE_ACCESS (fs))) { + free (pathloc->node_access); + pathloc->node_access = NULL; + } + if (pathloc->node_access_2) { + free (pathloc->node_access_2); + pathloc->node_access_2 = NULL; + } + return 0; +} + + +static const rtems_filesystem_operations_table rtems_tftp_ops = { + .evalpath_h = rtems_tftp_eval_path, + .evalformake_h = rtems_tftp_evaluate_for_make, + .link_h = rtems_filesystem_default_link, + .unlink_h = rtems_filesystem_default_unlink, + .node_type_h = rtems_tftp_node_type, + .mknod_h = rtems_filesystem_default_mknod, + .chown_h = rtems_filesystem_default_chown, + .freenod_h = rtems_tftp_free_node_info, + .mount_h = rtems_filesystem_default_mount, + .fsmount_me_h = rtems_tftpfs_initialize, + .unmount_h = rtems_filesystem_default_unmount, + .fsunmount_me_h = rtems_tftpfs_shutdown, + .utime_h = rtems_filesystem_default_utime, + .eval_link_h = rtems_filesystem_default_evaluate_link, + .symlink_h = rtems_filesystem_default_symlink, + .readlink_h = rtems_filesystem_default_readlink +}; + +static const rtems_filesystem_file_handlers_r rtems_tftp_handlers = { + .open_h = rtems_tftp_open, + .close_h = rtems_tftp_close, + .read_h = rtems_tftp_read, + .write_h = rtems_tftp_write, + .ioctl_h = rtems_filesystem_default_ioctl, + .lseek_h = rtems_filesystem_default_lseek, + .fstat_h = rtems_filesystem_default_fstat, + .fchmod_h = rtems_filesystem_default_fchmod, + .ftruncate_h = rtems_tftp_ftruncate, + .fpathconf_h = rtems_filesystem_default_fpathconf, + .fsync_h = rtems_filesystem_default_fsync, + .fdatasync_h = rtems_filesystem_default_fdatasync, + .fcntl_h = rtems_filesystem_default_fcntl, + .rmnod_h = rtems_filesystem_default_rmnod +}; |