blob: bc500d692b18745ed999f5c93d53eda7e820766e [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_STRINGS_H
# include <strings.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>
#if HAVE_MMAP
# include <sys/mman.h>
#endif
#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(s) to use by default
*
* May consist of several device/file names separated by colons.
* This allows you to select one of several different memory
* spaces remotely.
*
* In addition, the device name may be suffixed by ",offset,size",
* which causes the device to be mmap'ed into this process.
*/
char *MemName = "/dev/kmem"; /* Kernel memory */
typedef struct
{
char name[256];
unsigned long mmap_addr;
unsigned int mmap_size;
void *mmap_vaddr;
} COREINFO;
#define NUMCORE 16
COREINFO CoreInfo[NUMCORE];
int NumCore = 0;
/*
* 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;
int ReadOnly = 0;
int Debug = 0;
/*
* Create coreinfo data
*/
void
coreinfo(char *str)
{
int i;
int len;
char *start = str;
char *end;
for (NumCore = 0; NumCore < NUMCORE; )
{
end = strchr(start, ':');
if (!end) end = strchr(start, 0);
if (end - start == 2 && start[2] == '\\') {
end = index(start+3, ':');
if (!end) end = strchr(start+3, 0);
}
len = end - start;
if (len >= sizeof(CoreInfo[NumCore].name))
len = sizeof(CoreInfo[NumCore].name) - 1;
strncpy(CoreInfo[NumCore].name, start, len);
CoreInfo[NumCore].name[len] = 0;
start = strchr(CoreInfo[NumCore].name, ',');
if (start)
{
#if !HAVE_MMAP
fprintf("Sorry, no mmap support on this target\n");
exit(1);
#endif
*start++ = 0;
CoreInfo[NumCore].mmap_addr = strtoul(start, 0, 0);
start = strchr(start, ',');
if (start)
CoreInfo[NumCore].mmap_size = strtoul(start+1, 0, 0);
}
++NumCore;
if (*end == 0)
break;
else
start = end+1;
}
if (Debug > 1)
for (i = 0; i < NumCore; ++i)
{
fprintf(stderr, "%d '%s' %lu %lu\n",
i, CoreInfo[i].name,
CoreInfo[i].mmap_addr,
CoreInfo[i].mmap_size);
}
}
/*
* These are really simplistic read/write mem routines
*/
int
open_mem(int boardnum)
{
static int fd = -1;
static int lastboard = -1;
static int warn = 1;
static void *mapaddr;
COREINFO *cp;
if (boardnum >= NumCore)
return -1;
cp = &CoreInfo[boardnum];
/*
* If a new board needs to be opened, close the previous one
*/
if (fd != -1 && lastboard != boardnum)
{
if (mapaddr)
{
#if HAVE_MMAP
munmap(mapaddr, cp->mmap_size);
cp->mmap_vaddr = mapaddr = 0;
#endif
}
close(fd);
fd = -1;
}
if (fd == -1)
{
fd = open(cp->name, ReadOnly ? O_RDONLY : O_RDWR);
if (fd < 0)
{
if (warn)
{
perror("corefile open");
warn = 0;
}
return -1;
}
if (cp->mmap_size)
{
#if HAVE_MMAP
cp->mmap_vaddr = mmap(NULL, cp->mmap_size,
ReadOnly ? PROT_READ : (PROT_READ|PROT_WRITE),
MAP_SHARED, fd, cp->mmap_addr);
if (cp->mmap_vaddr == ((void *) -1))
perror("mmap");
else
mapaddr = cp->mmap_vaddr;
#endif
}
}
lastboard = boardnum;
return (fd);
}
/*
* Reads memory.
*/
int
read_mem(rw_t* rw)
{
int boardnum = rw->rw_module;
int fd = open_mem(boardnum);
COREINFO *cp = &CoreInfo[boardnum];
if (fd == -1)
return 0;
if (cp->mmap_size == 0 || !HAVE_MMAP)
{
lseek(fd, (off_t) rw->rw_addr + cp->mmap_addr, 0);
return read(fd, rw->rw_data, rw->rw_size);
}
else
{
#if HAVE_MMAP
if (rw->rw_addr >= cp->mmap_size)
return 0;
memcpy(rw->rw_data, ((char *)cp->mmap_vaddr) + rw->rw_addr,
rw->rw_size);
return rw->rw_size;
#else
return 0;
#endif
}
}
/*
* Write memory.
*/
int
write_mem(rw_t* rw)
{
int boardnum = rw->rw_module;
int fd = open_mem(boardnum);
COREINFO *cp = &CoreInfo[boardnum];
if (fd == -1)
return 0;
if (cp->mmap_size == 0 || !HAVE_MMAP)
{
lseek(fd, (off_t) rw->rw_addr + cp->mmap_addr, 0);
return write(fd, rw->rw_data, rw->rw_size);
}
else
{
#if HAVE_MMAP
if (rw->rw_addr >= cp->mmap_size)
return 0;
memcpy(((char *)cp->mmap_vaddr) + rw->rw_addr, rw->rw_data,
rw->rw_size);
return rw->rw_size;
#else
return 0;
#endif
}
}
/*
* 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;
size_t 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 (Debug)
fprintf(stderr, "%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 filename(s)\n"
" to mmap it, use: device,offset,size\n"
" -e endian Endianness of protocol\n"
" (0,1=net,2==autodetect) [2]\n"
" -r Open corefile(s) read-only\n",
" -U port KME access port number. [%d]\n"
" -D debug Debug level. [%d]\n"
"\n"
"Standard /etc/services entry:\n"
" kme %d/udp kme # kme server\n",
UDP_PORT, UDP_PORT, Debug
);
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:rU:D:")) != EOF)
{
switch (c)
{
case 'c':
case 'd':
MemName = optarg; break;
break;
case 'e':
RwEndianness = atoi(optarg);
break;
case 'r':
ReadOnly = 1;
break;
case 'p':
case 'U':
if (sscanf(optarg, "%d%c", &port, &ch) != 1 || port < 10)
usage();
break;
case 'D':
Debug = atoi(optarg);
break;
default:
usage();
break;
}
}
coreinfo(MemName);
/*
* 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);
}