| /* |
| * This is a sample kme UDP server for implementing |
| * remote kme. In this case, we interface to the |
| * linux device driver for HLC by default. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2, or (at your option) |
| * any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
| * 02111-1307, USA. |
| */ |
| |
| #include "config.h" |
| |
| #if !defined(HAVE_SOCKET) |
| #error kmed requires the system socket() call. |
| #endif |
| |
| #if HAVE_UNISTD_H |
| # include <unistd.h> |
| #endif |
| |
| #if HAVE_STDLIB_H |
| # include <stdlib.h> |
| #endif |
| |
| #if HAVE_STRING_H |
| # include <string.h> |
| #endif |
| |
| #if HAVE_GETOPT_H |
| # include <getopt.h> |
| #endif |
| |
| #include <stdio.h> |
| #include <fcntl.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <netdb.h> |
| |
| #include "kme.h" |
| |
| char kmed_c_version[] = "@Id$"; |
| |
| /* |
| * Unsigned variables. This form makes them impervious |
| * to similar defs in sys/types.h. |
| */ |
| |
| #define uchar unsigned char |
| #define ushort unsigned short |
| #define ulong unsigned long |
| |
| /* |
| * The name of the memory device to use by default |
| * |
| * May have one %d in it, which is replaced by the "board" number. |
| * This allows you to select one of several different memory |
| * spaces remotely. |
| */ |
| |
| char *MemName = "/dev/kmem"; /* Kernel memory */ |
| |
| /* |
| * The current rw_t struct leaves the endianness of rw_size and |
| * rw_addr undefined. This is not good. We can run this server |
| * in three modes to try to deal with this. |
| */ |
| |
| #define RW_ASIS 0 /* Pray its right already */ |
| #define RW_IN_NETWORK_ORDER 1 /* Assume its in network order */ |
| #define RW_DETECT 2 /* Guess, based on rw_size */ |
| |
| int RwEndianness = RW_DETECT; |
| |
| /* |
| * These are really simplistic read/write mem routines |
| * |
| * A real implementation would mmap() the board memory in. |
| * A slicker yet implementation would mmap() the board memory in, |
| * and release it after a timeout of several seconds. |
| */ |
| |
| int |
| open_mem(int boardnum) |
| { |
| static int fd = -1; |
| static int lastboard = -1; |
| |
| /* |
| * If a new board needs to be opened, close the previous one |
| */ |
| if (fd != -1 && lastboard != boardnum) |
| { |
| close(fd); |
| fd = -1; |
| } |
| |
| if (fd == -1) |
| { |
| int i; |
| char* start = MemName; |
| char* end = index(MemName, ':'); |
| char name[256]; |
| |
| /* Advance to "nth" file in file path. */ |
| |
| for (i = boardnum; i > 0; i--) |
| { |
| if (end == 0) |
| return -1; |
| |
| start = end + 1; |
| end = index(start, ':'); |
| if (end - start == 2 && start[2] == '\\') |
| end = index(start+3, ':'); |
| } |
| |
| if (end != 0) |
| { |
| i = end - start; |
| |
| if (i > sizeof(name)-1) |
| i = sizeof(name)-1; |
| |
| strncpy(name, start, i); |
| name[i] = 0; |
| |
| start = name; |
| } |
| |
| fd = open(start, O_RDWR); |
| |
| if (fd < 0) |
| return -1; |
| } |
| |
| lastboard = boardnum; |
| |
| return (fd); |
| } |
| |
| /* |
| * Reads memory. |
| */ |
| |
| int |
| read_mem(rw_t* rw) |
| { |
| int fd = open_mem(rw->rw_module); |
| |
| if (fd == -1) |
| return 0; |
| |
| lseek(fd, (off_t) rw->rw_addr, 0); |
| |
| return read(fd, rw->rw_data, rw->rw_size); |
| } |
| |
| /* |
| * Write memory. |
| */ |
| |
| int |
| write_mem(rw_t* rw) |
| { |
| int fd = open_mem(rw->rw_module); |
| |
| if (fd == -1) |
| return 0; |
| |
| lseek(fd, (off_t) rw->rw_addr, 0); |
| |
| return write(fd, rw->rw_data, rw->rw_size); |
| } |
| |
| /* |
| * swap routines |
| */ |
| |
| static ushort |
| swapshort(ushort value) |
| { |
| value &= 0xffff; |
| return ((value << 8) | (value >> 8)); |
| } |
| |
| static ulong |
| swaplong (ulong val) |
| { |
| const ulong mask = 0x00ff00ff; |
| |
| ulong d = (val << 16) | (val >> 16); |
| return ((d >> 8) & mask) | ((d & mask) << 8); |
| } |
| |
| |
| /* |
| * This is the request/response processing loop |
| */ |
| |
| void |
| process(int fd) |
| { |
| int len; |
| int rc; |
| rw_t rw; |
| struct sockaddr client_addr; |
| int client_len; |
| int swap; |
| |
| for (;;) |
| { |
| /* |
| * Get the next request from the client |
| */ |
| |
| client_len = sizeof(client_addr); |
| len = recvfrom(fd, (void*) &rw, sizeof(rw), 0, |
| (struct sockaddr *) &client_addr, &client_len); |
| if (len < 0) |
| { |
| perror("recvfrom"); |
| exit(5); |
| } |
| |
| if (0) |
| printf("%s %d bytes at %lx from board=%d module=%d\n", |
| rw.rw_req == RW_READ ? "Read" : "Write", |
| rw.rw_size, rw.rw_addr, rw.rw_board, rw.rw_module); |
| |
| /* |
| * Convert request to host byte order (hopefully) |
| */ |
| |
| switch (RwEndianness) |
| { |
| case RW_ASIS: |
| break; |
| case RW_IN_NETWORK_ORDER: |
| rw.rw_addr = ntohl(rw.rw_addr); |
| rw.rw_size = ntohs(rw.rw_size); |
| break; |
| default: |
| case RW_DETECT: |
| if (rw.rw_size & 0xff00) |
| { |
| rw.rw_size = swapshort(rw.rw_size); |
| rw.rw_addr = swaplong(rw.rw_addr); |
| swap = 1; |
| } |
| else |
| { |
| swap = 0; |
| } |
| break; |
| } |
| |
| /* |
| * Process the request |
| */ |
| |
| if (rw.rw_size > sizeof(rw.rw_data)) |
| rw.rw_size = sizeof(rw.rw_data); |
| |
| switch (rw.rw_req) |
| { |
| case RW_READ: |
| rw.rw_size = read_mem(&rw); |
| break; |
| |
| case RW_WRITE: |
| rw.rw_size = write_mem(&rw); |
| break; |
| default: |
| continue; |
| } |
| |
| if (rw.rw_size > sizeof(rw.rw_data)) |
| rw.rw_size = 0; |
| |
| /* |
| * Convert request back to proper byte order (hopefully) |
| */ |
| |
| switch (RwEndianness) |
| { |
| case RW_ASIS: |
| break; |
| case RW_IN_NETWORK_ORDER: |
| rw.rw_addr = ntohl(rw.rw_addr); |
| rw.rw_size = ntohs(rw.rw_size); |
| break; |
| default: |
| case RW_DETECT: |
| if (swap) |
| { |
| rw.rw_size = swapshort(rw.rw_size); |
| rw.rw_addr = swaplong(rw.rw_addr); |
| } |
| break; |
| } |
| |
| /* |
| * Send the reply |
| */ |
| |
| rc = sendto(fd, &rw, len, 0, |
| (struct sockaddr *) &client_addr, client_len); |
| if (rc != len) |
| { |
| perror("sendto"); |
| exit(6); |
| } |
| } |
| } |
| |
| /* |
| * The usual usage message |
| */ |
| |
| void |
| usage(void) |
| { |
| fprintf(stderr, |
| "Usage: kmed [options]\n" |
| "\n" |
| " A server to enable KME access over the network.\n" |
| "\n" |
| "Options:\n" |
| " -c corefile Colon-separated list of file names\n" |
| " -e endian Endianness of protocol\n" |
| " (0,1=net,2==autodetect) [2]\n" |
| " -U port KME access port number. [%d]\n" |
| "\n" |
| "Standard /etc/services entry:\n" |
| " kme %d/udp kme # kme server\n", |
| UDP_PORT, UDP_PORT |
| ); |
| exit(1); |
| } |
| |
| /* |
| * The main program |
| */ |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| struct sockaddr_in serv_addr; |
| char ch; |
| int sockfd; |
| int rc; |
| int c; |
| extern char *optarg; |
| int port = 0; |
| |
| /* |
| * Process options |
| */ |
| while ((c = getopt(argc, argv, "c:d:e:p:U:")) != EOF) |
| { |
| switch (c) |
| { |
| case 'c': |
| case 'd': |
| MemName = optarg; break; |
| break; |
| |
| case 'e': |
| RwEndianness = atoi(optarg); |
| break; |
| |
| case 'p': |
| case 'U': |
| if (sscanf(optarg, "%d%c", &port, &ch) != 1 || port < 10) |
| usage(); |
| break; |
| |
| default: |
| usage(); |
| break; |
| } |
| } |
| |
| /* |
| * Get a socket |
| */ |
| sockfd = socket(AF_INET, SOCK_DGRAM, 0); |
| if (sockfd < 0) |
| { |
| perror("Can't open socket"); |
| exit(2); |
| } |
| |
| /* |
| * Figure out port number we want to use |
| */ |
| if (port == 0) |
| { |
| /* |
| * Get port number from /etc/services entry: |
| * |
| * kme 2773/udp kme # kme |
| */ |
| struct servent *sep; |
| |
| sep = getservbyname("kme", "udp"); |
| |
| if (sep != 0) |
| port = sep->s_port; |
| else |
| port = htons(UDP_PORT); |
| } |
| else |
| { |
| port = htons(port) ; |
| } |
| |
| /* |
| * Fill in INET address structure |
| */ |
| memset(&serv_addr, 0, sizeof(serv_addr)); |
| serv_addr.sin_family = AF_INET; |
| serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); |
| serv_addr.sin_port = port; |
| |
| /* |
| * Bind to the socket |
| */ |
| rc = bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)); |
| if (rc < 0) |
| { |
| perror("bind failed"); |
| exit(4); |
| } |
| |
| /* |
| * Now run the actual server |
| */ |
| process(sockfd); |
| |
| exit(0); |
| } |