blob: a4490c2a906184117bc1d995674d78ba0e0ecc43 [file] [log] [blame] [raw]
/*
* 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);
}