Commit b1aae16b authored by Aurelio Colosimo's avatar Aurelio Colosimo Committed by Alessandro Rubini

arch-wrs: copy mini-rpc module from its git repository

mini-rpc is a copy of commit 4c87062d from
repository git://github.com/a-rubini/mini-rpc.gitSigned-off-by: Aurelio Colosimo's avatarAurelio Colosimo <aurelio@aureliocolosimo.it>
parent c232328f
This diff is collapsed.
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
CFLAGS = -Wall -ggdb -O2 -fno-strict-aliasing
LDFLAGS = -L. -lminipc -lm
# We need to support freestading environments: an embedded CPU that
# sits as a server on its own memory are and awaits commands
# The user can pass IPC_FREESTANDING=y
IPC_FREESTANDING ?= $(shell ./check-freestanding $(CC))
IPC_HOSTED ?= $(shell ./check-freestanding -n $(CC))
# Hosted is the opposite of freestanding, and cflags change too
ifeq ($(IPC_FREESTANDING),y)
IPC_HOSTED = n
CFLAGS += -ffreestanding -Iarch-$(ARCH)
else
IPC_HOSTED = y
endif
OBJ-$(IPC_HOSTED) = minipc-core.o minipc-server.o minipc-client.o
OBJ-$(IPC_FREESTANDING) = minipc-mem-server.o
LIB = libminipc.a
# export these to the examples (but if you make there IPC_HOSTED is default
export IPC_FREESTANDING IPC_HOSTED
all: $(LIB)
$(MAKE) -C examples
$(LIB): $(OBJ-y)
$(AR) r $@ $^
# the default puts LDFLAGS too early. Bah...
%: %.c
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
# This is stupid, it won't apply the first time, but, well... it works
$(wildcard *.o): $(wildcard *.h)
clean:
rm -f *.o *~ $(LIB)
$(MAKE) -C examples clean
install:
@echo "We have no install rule by now"
All documentation is now under the doc/ directory.
The input file is called doc/mini-ipc.in (it is readable by itself).
To have all usual output formats you can run "make" withing ./doc
but you need TexInfo installed.
#ifndef __ARCH_LM32_STDINT_H__
#define __ARCH_LM32_STDINT_H__
/*
* We miss a stdint.h in our compiler, so provide some types here,
* knowing the CPU is 32-bits and uses LP32 model
*/
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned long uint32_t;
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed long int32_t;
#endif /* __ARCH_LM32_STDINT_H__ */
#!/bin/sh
# I used to just check __STDC_HOSTED__ without special CFLAGS, but now I
# want to force freestanding compilation on otherwise hosted processors.
# Thus, the user can set IPC_FREESTANDING=y . If unset, this gets called.
# Accept "-n" to invert the result (select second field, not first)
if [ "x$1" = "x-n" ]; then select=2; shift; else select=1; fi
CC=$1
if [ "x$CC" = "x" ]; then
echo "$0: pass the compiler path (\$CC) as argument" >& 2
exit 1
fi
# Check the compiler: if it has no matches is prints the unadorned string
if [ "$($CC -print-file-name=libc.so)" = "libc.so" ]; then
res=y,n
else
res=n,y
fi
# If passed from the user, override the autodetected value
if [ "$IPC_FREESTANDING" = "y" ]; then
res=y,n
fi
echo $res | cut -d, -f $select
*~
*.aux
*.cp
*.fn
*.html
*.info
*.ky
*.log
*.pdf
*.pg
*.texi
*.toc
*.tp
*.txt
*.vr
#
# Makefile for the documentation directory
#
# Copyright 1994,2000,2010,2011 Alessandro Rubini <rubini@linux.it>
#
#################
# There is not basenames here, all *.in are considered input
INPUT = $(wildcard *.in)
TEXI = $(INPUT:.in=.texi)
INFO = $(INPUT:.in=.info)
HTML = $(INPUT:.in=.html)
TXT = $(INPUT:.in=.txt)
PDF = $(INPUT:.in=.pdf)
ALL = $(INFO) $(HTML) $(TXT) $(PDF)
MAKEINFO ?= makeinfo
%.texi: %.in
@rm -f $@
sed -f ./infofilter $< > $@
emacs -batch --no-site-file -l fixinfo $@
chmod -w $@
%.pdf: %.texi
texi2pdf --batch $<
%.info: %.texi
$(MAKEINFO) $< -o $@
%.html: %.texi
$(MAKEINFO) --html --no-split -o $@ $<
%.txt: %.texi
$(MAKEINFO) --no-headers $< > $@
##############################################
.PHONY: all images check terse clean install
.INTERMEDIATE: $(TEXI)
all: images $(ALL)
$(MAKE) terse
images::
if [ -d images ]; then $(MAKE) -C images || exit 1; fi
check: _err.ps
gs -sDEVICE=linux -r320x200x16 $<
terse:
for n in cp fn ky pg toc tp vr aux log; do rm -f *.$$n; done
rm -f *~
clean: terse
rm -f $(ALL) $(TEXI)
install:
;; use:
;; emacs -batch -l ./fixinfo.el <file>
;; or, better:
;; emacs -batch --no-site-file -l ./fixinfo.el <file>
(defun fixinfo (file)
(find-file-other-window file)
(message (concat "Maxing texinfo tree in " file))
(texinfo-all-menus-update)
(texinfo-every-node-update)
(save-buffer)
(kill-buffer (current-buffer))
)
;; loop over command line arguments
(mapcar 'fixinfo command-line-args-left)
(kill-emacs)
#! /usr/bin/sed -f
# allow "%" as a comment char, but only at the beginning of the line
s/^%/@c /
#s/[^\\]%.*$//
s/^\\%/%/
#preserve blanks and braces in @example blocks
/@example/,/@end example/ s/{/@{/g
/@example/,/@end example/ s/}/@}/g
/@example/,/@end example/ p
/@example/,/@end example/ d
/@smallexample/,/@end smallexample/ s/{/@{/g
/@smallexample/,/@end smallexample/ s/}/@}/g
/@smallexample/,/@end smallexample/ p
/@smallexample/,/@end smallexample/ d
# remove leading blanks
s/^[ ]*//
This diff is collapsed.
trivial-server
trivial-client
pty-server
pty-client
mbox-bridge
mbox-client
mbox-process
shmem-server
shmem-client
freestanding-server
# examples for mini-rpc; depends on ..
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
CFLAGS = -Wall -ggdb -I.. -O2
LDFLAGS = -L.. -lminipc -lm
# we may be hosted or freestanding. For freestanding there is one
# example only. The following variables are exported by the upper
# Makefile, but if you "make" in this directory, it checks by itself
IPC_FREESTANDING ?= $(shell ../check-freestanding $(CC))
IPC_HOSTED ?= $(shell ../check-freestanding -n $(CC))
# Hosted is the opposite of freestanding, and cflags change too
ifeq ($(IPC_FREESTANDING),y)
IPC_HOSTED = n
CFLAGS += -ffreestanding -I../arch-$(ARCH)
else
IPC_HOSTED = y
endif
PROGS-$(IPC_HOSTED) = trivial-server trivial-client
PROGS-$(IPC_HOSTED) += pty-server pty-client
PROGS-$(IPC_HOSTED) += mbox-process mbox-bridge mbox-client
PROGS-$(IPC_HOSTED) += shmem-server shmem-client
IPC_FREESTANDING ?= n
PROGS-$(IPC_FREESTANDING) += freestanding-server
all: $(PROGS-y)
# the default puts LDFLAGS too early. Bah...
%: %.c
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
pty-server: pty-server.o pty-rpc_server.o pty-rpc_structs.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -lutil -o $@
pty-client: pty-client.o pty-rpc_structs.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
shmem-server: shmem-server.o shmem-structs.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
shmem-client: shmem-client.o shmem-structs.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
# This is stupid, it won't apply the first time, but, well... it works
$(PROGS-y) $(wildcard *.o): $(wildcard ../*.h ../*.a)
clean:
rm -f *.o *~ $(PROGS)
All documentation is now under the ../doc/ directory.
The input file is called doc/mini-ipc.in (it is readable by itself).
To have all usual output formats you can run "make" withing ./doc
but you need TexInfo installed.
/*
* Mini-ipc: an example freestanding server, based in memory
*
* Copyright (C) 2011,2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This code is copied from trivial-server, and made even more trivial
*/
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include "minipc.h"
/* A function that ignores the RPC and is written normally */
static int ss_do_sum(int a, int b)
{
return a + b;
}
/* The following ones are RPC-aware */
static int ss_sum_function(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
int i;
i = ss_do_sum(args[0], args[1]);
*(int *)ret = i;
return 0;
}
static int ss_mul_function(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
int a, b;
a = *(int *)args;
b = *(int *)(args + 1);
*(int *)ret = a * b;
return 0;
}
/* Describe the two functions above */
const struct minipc_pd ss_sum_struct = {
.f = ss_sum_function,
.name = "sum",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_END,
},
};
const struct minipc_pd ss_mul_struct = {
.f = ss_mul_function,
.name = "mul",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_END,
},
};
int main(int argc, char **argv)
{
struct minipc_ch *server;
server = minipc_server_create("mem:f000", 0);
if (!server)
return 1;
minipc_export(server, &ss_sum_struct);
minipc_export(server, &ss_mul_struct);
while (1) {
/* do something else... */
minipc_server_action(server, 1000);
}
}
/*
* Example bridge beteen mini-ipc and a mailbox memory area
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
/*
* This example uses a shared memory area to talk with mbox-process.
* The same technique can be used to communicate with an os-less
* soft-core within an FPGA, and actually this is the reason why I'm
* writing this demo.
*
* The bridge can export functions in both ways: as a mini-ipc server
* it exports "stat", and as a client it calls gettimeofday in the
* trivial-server.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/shm.h>
#include <sys/select.h>
#include "minipc.h"
#include "minipc-shmem.h" /* The shared data structures */
struct minipc_mbox_info *info; /* unfortunately global... */
/* This function implements the "stat" mini-ipc server, by asking mbox */
static int mb_stat_server(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *fname = (void *)args;
/* pass this to the shmem */
memset(info->fname, 0, sizeof(info->fname));
strncpy(info->fname, fname, sizeof(info->fname) -1 );
/* ask and wait */
info->bridge_req = 1;
while (info->bridge_req)
usleep(1000);
memcpy(ret, &info->stbuf, sizeof(info->stbuf));
return 0;
}
/* The description here is the same as in the server */
const struct minipc_pd mb_tod_struct = {
.name = "gettimeofday",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, struct timeval),
.args = {
MINIPC_ARG_END,
},
};
/* And here it's the same as in the client */
struct minipc_pd mb_stat_struct = {
.f = mb_stat_server,
.name = "stat",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, struct stat),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/* No arguments */
int main(int argc, char **argv)
{
struct minipc_ch *server;
struct minipc_ch *client;
void *shmem;
int ret;
if (argc > 1) {
fprintf(stderr, "%s: no arguments please\n", argv[0]);
exit(1);
}
/* Create your shared memory and/or attach to it */
ret = shmget(MINIPC_SHM, sizeof(struct minipc_mbox_info),
IPC_CREAT | 0666);
if (ret < 0) {
fprintf(stderr, "%s: shmget(): %s\n", argv[0],
strerror(errno));
exit(1);
}
shmem = shmat(ret, NULL, SHM_RND);
if (shmem == (void *)-1) {
fprintf(stderr, "%s: shmat(): %s\n", argv[0],
strerror(errno));
exit(1);
}
info = shmem;
/* Create a server socket for mini-ipc */
server = minipc_server_create("mbox", 0);
if (!server) {
fprintf(stderr, "%s: server_create(): %s\n", argv[0],
strerror(errno));
exit(1);
}
minipc_set_logfile(server, stderr);
minipc_export(server, &mb_stat_struct);
/* Connect as a client to the trivial-server */
client = minipc_client_create("trivial", 0);
if (!client) {
fprintf(stderr, "%s: client_create(): %s\n", argv[0],
strerror(errno));
exit(1);
}
/* Loop serving both mini-ipc and the mailbox */
while (1) {
fd_set set;
struct timeval to = {
MBOX_POLL_US / (1000*1000),
MBOX_POLL_US % (1000*1000)
};
minipc_server_get_fdset(server, &set);
/* Wait for any server, with the defined timeout */
ret = select(16 /* hack */, &set, NULL, NULL, &to);
if (ret > 0) {
if (minipc_server_action(server, 0) < 0) {
fprintf(stderr, "%s: server_action(): %s\n",
argv[0], strerror(errno));
exit(1);
}
}
/* No IPC request: if needed act as IPC client */
if (info->proc_req) {
memset(&info->tv, 0, sizeof(info->tv));
minipc_call(client, 100 /* ms */,
&mb_tod_struct, &info->tv, NULL);
info->proc_req = 0;
}
}
}
/*
* Example mbox client
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*
* This process requests "stat" calls to the mbox-bridge, which in turn
* asks it to the "remote" process that lives behing an mbox (shmem).
* The program reads filenames from stdin, in order to allow several clients
* connected to the same bridge.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/stat.h>
#include "minipc.h"
#define CLIENT_TIMEOUT 100 /* ms */
/* The description here is the same as in the server */
struct minipc_pd rpc_stat = {
.name = "stat",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, struct stat),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
int main(int argc, char **argv)
{
struct minipc_ch *client;
struct stat stbuf;
int ret;
char s[80];
client = minipc_client_create("mbox", 0);
if (!client) {
fprintf(stderr, "%s: client_create(): %s\n", argv[0],
strerror(errno));
exit(1);
}
minipc_set_logfile(client, stderr);
while (fgets(s, sizeof(s), stdin)) {
/* strip trailing newline */
s[strlen(s)-1] = '\0';
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_stat,
&stbuf, s);
if (ret < 0) {
fprintf(stderr, "stat(%s): %s\n", s, strerror(errno));
continue;
}
printf("%s:\n", s);
printf("mode %o, size %li, owner %i, atime %li\n",
stbuf.st_mode, (long)stbuf.st_size, stbuf.st_uid,
stbuf.st_atime);
}
return 0;
}
/*
* Example process connected to the world through a mailbox
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
/*
* This example uses a shared memory area to talk with the mbox-bridge.
* In my real-world application this will be an os-less
* soft-core within an FPGA.
*
* The process is both a server and a client. As a server iIt replies
* to "stat" calls coming from the bridge process; as a client it asks
* the time of day once a second or so.
*
* It doesn't need minipc.h because it is not directly connected to the ipc
* mechanism
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "minipc-shmem.h" /* The shared data structures */
struct minipc_mbox_info *info; /* unfortunately global... */
/* No arguments */
int main(int argc, char **argv)
{
void *shmem;
int ret, i;
if (argc > 1) {
fprintf(stderr, "%s: no arguments please\n", argv[0]);
exit(1);
}
/* Create your shared memory and/or attach to it */
ret = shmget(MINIPC_SHM, sizeof(struct minipc_mbox_info),
IPC_CREAT | 0666);
if (ret < 0) {
fprintf(stderr, "%s: shmget(): %s\n", argv[0],
strerror(errno));
exit(1);
}
shmem = shmat(ret, NULL, SHM_RND);
if (shmem == (void *)-1) {
fprintf(stderr, "%s: shmat(): %s\n", argv[0],
strerror(errno));
exit(1);
}
info = shmem;
/* Loop forever */
for (i = 0; 1; i++) {
if (info->bridge_req) {
/* We got a request: serve it */
printf("Serving stat(%s)\n", info->fname);
memset(&info->stbuf, 0, sizeof(info->stbuf));
stat(info->fname, &info->stbuf);
info->bridge_req = 0;
}
if (i * MBOX_POLL_US >= 1000 * 1000) {
/* 1s passed, ask for the date */
info->proc_req = 1;
while (info->proc_req)
;
printf("time: %li.%06li\n", (long)info->tv.tv_sec,
(long)info->tv.tv_usec);
i = 0;
}
usleep(MBOX_POLL_US);
}
}
#ifndef __MINIPC_SHMEM_H__
#define __MINIPC_SHMEM_H__
#include <sys/stat.h>
#include <sys/time.h>
#define MINIPC_SHM 0x3465 /* Random */
struct minipc_mbox_info {
volatile int bridge_req; /* A request is asked by the bridge */
volatile int proc_req; /* A request is asked by the process */
/* The bridge can ask "stat" to the bare process */
char fname[64];
struct stat stbuf;
/* The bare process can ask timeofday to the bridge */
struct timeval tv;
};
#define MBOX_POLL_US (10*1000) /* 10ms */
#endif /* __MINIPC_SHMEM_H__ */
/*
* Example mini-ipc server
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include "minipc.h"
#include "pty-server.h"
#define CLIENT_TIMEOUT 200 /* ms */
/* Retrieve the counters of the pty file descriptors */
static int do_count(struct minipc_ch *client, char **argv)
{
int ret;
struct pty_counts c;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_count, &c);
if (ret < 0)
return ret;
printf("counts: %i %i\n", c.in, c.out);
return 0;
}
/* getenv and setenv in the remote process */
static int do_getenv(struct minipc_ch *client, char **argv)
{
int ret;
char buf[256]; /* FIXME: size limits? */
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_getenv, buf, argv[2]);
if (ret < 0)
return ret;
printf("getenv(%s) = %s\n", argv[2], buf);
return 0;
}
static int do_setenv(struct minipc_ch *client, char **argv)
{
int ret, ret2;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_setenv, &ret2, argv[2],
argv[3]);
if (ret < 0)
return ret;
printf("setenv %s = %s : success\n", argv[2], argv[3]);
return 0;
}
/* send a string to the subshell of the server */
static int do_feed(struct minipc_ch *client, char **argv)
{
int ret, ret2;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_feed, &ret2, argv[2]);
if (ret < 0)
return ret;
printf("feeded %s : success\n", argv[2]);
return 0;
}
/* calculate a remote strlen */
static int do_strlen(struct minipc_ch *client, char **argv)
{
int ret, len;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_strlen, &len, argv[2]);
if (ret < 0)
return ret;
printf("strlen(%s) = %i\n", argv[2], len);
return 0;
}
/* calculate a remote strcat */
static int do_strcat(struct minipc_ch *client, char **argv)
{
int ret;
char buf[256]; /* FIXME: size limits? */
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_strcat, buf,
argv[2], argv[3]);
if (ret < 0)
return ret;
printf("strcat(%s,%s) = %s\n", argv[2], argv[3], buf);
return 0;
}
/* ask for a remote stat on a passed filename */
static int do_stat(struct minipc_ch *client, char **argv)
{
int ret;
struct stat stbuf;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_stat, &stbuf,
argv[2]);
if (ret < 0)
return ret;
printf("stat(\"%s\"):\n", argv[2]);
printf("dev %04x, inode %i, mode %o (rdev %04x), size %i\n",
(int)stbuf.st_dev, (int)stbuf.st_ino, stbuf.st_mode,
(int)stbuf.st_rdev, (int)stbuf.st_size);
return 0;
}
/*
* This is a parsing table for argv[1]
*/
struct {
char *name;
int (*f)(struct minipc_ch *client, char **argv);
int argc;
} *cp, calls[] = {
{ "count", do_count, 2},
{ "getenv", do_getenv, 3},
{ "setenv", do_setenv, 4},
{ "feed", do_feed, 3},
{ "strlen", do_strlen, 3},
{ "strcat", do_strcat, 4},
{ "stat", do_stat, 3},
{NULL, },
};
/*
* And this is the trivial main function -- more error checking than code
*/
int main(int argc, char **argv)
{
struct minipc_ch *client;
if (argc < 2) {
fprintf(stderr, "%s: use \"%s <cmd> [<arg> ...]\"\n",
argv[0], argv[0]);
exit(1);
}
client = minipc_client_create(PTY_RPC_NAME, 0);
if (!client) {
fprintf(stderr, "%s: client_create(): %s\n", argv[0],
strerror(errno));
exit(1);
}
/* scan the table */
for (cp = calls; cp->name; cp++)
if (!strcmp(argv[1], cp->name))
break;
if (!cp->name) {
fprintf(stderr, "%s: no such command \"%s\"\n", argv[0],
argv[1]);
exit(1);
}
if (argc != cp->argc) {
fprintf(stderr, "%s: %s: wrong number of arguments\n",
argv[0], argv[1]);
exit(1);
}
/* run the RPC we found */
if (cp->f(client, argv) < 0) {
fprintf(stderr, "%s: remote \"%s\": %s\n",
argv[0], argv[1], strerror(errno));
exit(1);
}
exit(0);
}
/*
* Example mini-ipc server
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
/*
* This file includes the RPC functions exported by the server.
* RPC-related stuff has been split to a different file for clarity.
*/
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "minipc.h"
#include "pty-server.h"
static int saved_fdm; /* file descriptor of pty master, used by "feed" */
static struct pty_counts *saved_pc; /* counters, used by "count" */
/* Return the count accessing the pointer to the structure within main() */
static int pty_server_do_count(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
*(struct pty_counts *)ret = *saved_pc;
return 0;
}
/* Run getenv and setenv on behalf of the client */
static int pty_server_do_getenv(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *envname = (void *)args;
char *res = getenv(envname);
strcpy(ret, res); /* FIXME: max size */
return 0;
}
static int pty_server_do_setenv(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *envname = (void *)args;
char *envval;
args = minipc_get_next_arg(args, pd->args[0]);
envval = (void *)args;
setenv(envname, envval, 1);
return 0;
}
/* feed a string to the sub-shell (adding a newline) */
static int pty_server_do_feed(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *command = (void *)args;
int wrote;
wrote = write(saved_fdm, command, strlen(command));
wrote += write(saved_fdm, "\n", 1);
return 0;
}
/* calculate strlen for a string of the client */
static int pty_server_do_strlen(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *s = (void *)args;
*(int *)ret = strlen(s);
return 0;
}
/* strcat two remote strings and pass the result back */
static int pty_server_do_strcat(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *s, *t;
char scat[256];
s = (void *)args;
args = minipc_get_next_arg(args, pd->args[0]);
t = (void *)args;
strncpy(scat, s, sizeof(scat));
strncat(scat, t, sizeof(scat) - strlen(s));
strcpy(ret, scat); /* FIXME: max size */
return 0;
}
/* stat a pathname received from the remote party */
static int pty_server_do_stat(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
struct stat stbuf;
char *fname = (void *)args;
if (stat(fname, &stbuf) < 0)
return -1;
memcpy(ret, &stbuf, sizeof(stbuf));
return 0;
}
/*
* The following is called by the main function, and exports stuff.
* All the rest happens through callbacks from the library
*/
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
int pty_export_functions(struct minipc_ch *ch, int fdm, struct pty_counts *pc)
{
int i;
/* save data for the callbacks */
saved_fdm = fdm;
saved_pc = pc;
/* Use a handy table for the registration loop */
static struct {
struct minipc_pd *desc;
minipc_f *f;
} export_list [] = {
{&rpc_count, pty_server_do_count},
{&rpc_getenv, pty_server_do_getenv},
{&rpc_setenv, pty_server_do_setenv},
{&rpc_feed, pty_server_do_feed},
{&rpc_strlen, pty_server_do_strlen},
{&rpc_strcat, pty_server_do_strcat},
{&rpc_stat, pty_server_do_stat},
};
/*
* Complete the data structures by filling the function pointers
* and then register each of the exported procedures
*/
for (i = 0; i < ARRAY_SIZE(export_list); i++) {
export_list[i].desc->f = export_list[i].f;
if (minipc_export(ch, export_list[i].desc) < 0)
return -1;
}
return 0;
}
/*
* Example mini-ipc server
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
/*
* This file defines the data structures used to describe the RPC calls
* between server and client. Note that the function pointers are only
* instantiated in the server
*/
#include <sys/stat.h> /* for the structure */
#include "minipc.h"
#include "pty-server.h"
/* retrieve in and out counts from the pty-server */
struct minipc_pd rpc_count = {
.name = "count",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, struct pty_counts),
.args = {
MINIPC_ARG_END,
},
};
/* run getenv in the server */
struct minipc_pd rpc_getenv = {
.name = "getenv",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/* run setenv in the server */
struct minipc_pd rpc_setenv = {
.name = "setenv",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/* feed a string to the underlying shell */
struct minipc_pd rpc_feed = {
.name = "feed",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/* horribly run strlen and strcat */
struct minipc_pd rpc_strlen = {
.name = "strlen",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
struct minipc_pd rpc_strcat = {
.name = "strcat",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/* run "stat" on a file name */
struct minipc_pd rpc_stat = {
.name = "stat",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, struct stat),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/*
* Example mini-ipc server
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
/*
* This is the pty-server. It only does the pty-master work, all RPC stuff
* has been confined to prt-rpc_server.c, which is called by our main loop
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pty.h>
#include <fcntl.h>
#include <utmp.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include "minipc.h"
#include "pty-server.h"
int main(int argc, char **argv)
{
int fdm, fds, pid, exitval = 0;
struct minipc_ch *ch;
struct pty_counts counters = {0,};
/* First, open the pty */
if (openpty(&fdm, &fds, NULL, NULL, NULL) < 0) {
fprintf(stderr, "%s: openpty(): %s\n", argv[0],
strerror(errno));
exit(1);
}
/* Run a shell and let it go by itself before we open the rpc */
fprintf(stderr, "%s: Running a sub-shell in a new pty\n", argv[0]);
if ((pid = fork()) < 0) {
fprintf(stderr, "%s: fork(): %s\n", argv[0],
strerror(errno));
exit(1);
}
if (!pid) {
/* Child: become a shell and disappear... */
close(fdm);
login_tty(fds);
execl("/bin/sh", "sh", NULL);
fprintf(stderr, "%s: exec(/bin/sh): %s\n", argv[0],
strerror(errno));
exit(1);
}
/* Open the RPC server channel */
ch = minipc_server_create(PTY_RPC_NAME, 0);
if (!ch) {
fprintf(stderr, "%s: rpc_open(): %s\n", argv[0],
strerror(errno));
exit(1);
}
/* Log file for diagnostics */
{
char name[] = "/tmp/pty-server.XXXXXX";
int logfd;
FILE *logf;
logfd = mkstemp(name);
if (logfd >= 0) {
logf = fdopen(logfd, "w");
if (logf)
minipc_set_logfile(ch, logf);
}
}
/* Register your functions: all our RPC is split to another source */
if (pty_export_functions(ch, fdm, &counters)) {
fprintf(stderr, "%s: exporting RPC functions: %s\n", argv[0],
strerror(errno));
exit(1);
}
/*
* Now, we must mirror stdin/stdout to the pty master, with RPC too.
* The first step is horribly changing the termios of our tty
*/
close(fds);
if (system("stty raw -echo") < 0) {
fprintf(stderr, "%s: can't run \"stty\"\n", argv[0]);
exit(1);
}
while (waitpid(pid, NULL, WNOHANG) != pid) {
fd_set set;
int nfd, i, j;
char buf[256];
/* ask the RPC engine its current fdset and augment it */
minipc_server_get_fdset(ch, &set);
FD_SET(STDIN_FILENO, &set);
FD_SET(fdm, &set);
/* wait for any of the FD to be active */
nfd = select(64 /* Hmmm... */, &set, NULL, NULL, NULL);
if (nfd < 0 && errno == EINTR)
continue;
if (nfd < 0) {
fprintf(stderr, "%s: select(): %s\n", argv[0],
strerror(errno));
exitval = 1;
break;
}
/* Handle fdm and fds by just mirroring stuff and counting */
if (FD_ISSET(STDIN_FILENO, &set)) {
i = read(0, buf, sizeof(buf));
if (i > 0) {
counters.in += i;
do {
j = write(fdm, buf, i);
i -= j;
} while (i && j >= 0);
}
nfd--;
}
if (FD_ISSET(fdm, &set)) {
i = read(fdm, buf, sizeof(buf));
if (i > 0) {
counters.out += i;
do {
j = write(1, buf, i);
i -= j;
} while (i && j >= 0);
}
nfd--;
}
/* If there are no more active fd, loop over */
if (!nfd)
continue;
/*
* If we are there, there has been an RPC call.
* We tell the library to use a 0 timeout, since we know
* for sure that at least one of its descriptors is pending.
*/
minipc_server_action(ch, 0);
}
/* The child shell exited, reset the tty and exit. Let RPC die out */
if (system("stty sane") < 0)
fprintf(stderr, "%s: can't restore tty settings\n", argv[0]);
exit(exitval);
}
#include "minipc.h"
struct pty_counts {
int in, out;
};
#define PTY_RPC_NAME "pty-server"
/* structures are in pty-rpc_structs.c */
extern struct minipc_pd
rpc_count, rpc_getenv, rpc_setenv, rpc_feed, rpc_strlen, rpc_strcat, rpc_stat;
/* The server needs to call out to a different file */
extern int pty_export_functions(struct minipc_ch *ch,
int fdm, struct pty_counts *pc);
/*
* Example mini-ipc client for shmem
* Mostly copied and adapted from pty-client.c
*
* Copyright (C) 2011,2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include "minipc.h"
#include "shmem-structs.h"
#define CLIENT_TIMEOUT 200 /* ms */
/* getenv and setenv in the remote process */
static int do_getenv(struct minipc_ch *client, char **argv)
{
int ret;
char buf[256]; /* FIXME: size limits? */
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_getenv, buf, argv[2]);
if (ret < 0)
return ret;
printf("getenv(%s) = %s\n", argv[2], buf);
return 0;
}
static int do_setenv(struct minipc_ch *client, char **argv)
{
int ret, ret2;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_setenv, &ret2, argv[2],
argv[3]);
if (ret < 0)
return ret;
printf("setenv %s = %s : success\n", argv[2], argv[3]);
return 0;
}
static int do_add(struct minipc_ch *client, char **argv)
{
int ret;
int a = atoi(argv[2]);
int b = atoi(argv[3]);
int sum;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_add, &sum, a, b);
if (ret < 0)
return ret;
printf("add %i + %i = %i : success\n", a, b, sum);
return 0;
}
/* calculate a remote strlen */
static int do_strlen(struct minipc_ch *client, char **argv)
{
int ret, len;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_strlen, &len, argv[2]);
if (ret < 0)
return ret;
printf("strlen(%s) = %i\n", argv[2], len);
return 0;
}
/* calculate a remote strcat */
static int do_strcat(struct minipc_ch *client, char **argv)
{
int ret;
char buf[256]; /* FIXME: size limits? */
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_strcat, buf,
argv[2], argv[3]);
if (ret < 0)
return ret;
printf("strcat(%s,%s) = %s\n", argv[2], argv[3], buf);
return 0;
}
/* ask for a remote stat on a passed filename */
static int do_stat(struct minipc_ch *client, char **argv)
{
int ret;
struct stat stbuf;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_stat, &stbuf,
argv[2]);
if (ret < 0)
return ret;
printf("stat(\"%s\"):\n", argv[2]);
printf("dev %04x, inode %i, mode %o (rdev %04x), size %i\n",
(int)stbuf.st_dev, (int)stbuf.st_ino, stbuf.st_mode,
(int)stbuf.st_rdev, (int)stbuf.st_size);
return 0;
}
/*
* This is a parsing table for argv[1]
*/
struct {
char *name;
int (*f)(struct minipc_ch *client, char **argv);
int argc;
} *cp, calls[] = {
{ "getenv", do_getenv, 3},
{ "setenv", do_setenv, 4},
{ "add", do_add, 4},
{ "strlen", do_strlen, 3},
{ "strcat", do_strcat, 4},
{ "stat", do_stat, 3},
{NULL, },
};
/*
* And this is the trivial main function -- more error checking than code
*/
int main(int argc, char **argv)
{
struct minipc_ch *client;
if (argc <= 2) {
fprintf(stderr, "%s: use \"%s <shmid> <cmd> [<arg> ...]\n",
argv[0], argv[0]);
exit(1);
}
client = minipc_client_create(argv[1], 0);
if (!client) {
fprintf(stderr, "%s: rpc_open(%s): %s\n", argv[0], argv[1],
strerror(errno));
exit(1);
}
minipc_set_logfile(client, stderr);
argv[1] = argv[0]; argv++; argc--;
/* scan the table */
for (cp = calls; cp->name; cp++)
if (!strcmp(argv[1], cp->name))
break;
if (!cp->name) {
fprintf(stderr, "%s: no such command \"%s\"\n", argv[0],
argv[1]);
exit(1);
}
if (argc != cp->argc) {
fprintf(stderr, "%s: %s: wrong number of arguments\n",
argv[0], argv[1]);
exit(1);
}
/* run the RPC we found */
if (cp->f(client, argv) < 0) {
fprintf(stderr, "%s: remote \"%s\": %s\n",
argv[0], argv[1], strerror(errno));
exit(1);
}
exit(0);
}
/*
* Example mini-ipc server for shmem
* (mostly copied from the pty server code)
*
* Copyright (C) 2011-2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
/*
* This file includes the RPC functions exported by the server.
* RPC-related stuff has been split to a different file for clarity.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "minipc.h"
#include "shmem-structs.h"
/* Run getenv and setenv on behalf of the client */
static int shm_server_do_getenv(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *envname = (void *)args;
char *res = getenv(envname);
strcpy(ret, res); /* FIXME: max size */
return 0;
}
static int shm_server_do_setenv(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *envname = (void *)args;
char *envval;
args = minipc_get_next_arg(args, pd->args[0]);
envval = (void *)args;
setenv(envname, envval, 1);
return 0;
}
/* add two integer numbers */
static int shm_server_do_add(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
int a, b, sum;
a = args[0];
args = minipc_get_next_arg(args, pd->args[0]);
b = args[0];
sum = a + b;
*(int *)ret = sum;
return 0;
}
/* calculate strlen for a string of the client */
static int shm_server_do_strlen(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *s = (void *)args;
*(int *)ret = strlen(s);
return 0;
}
/* strcat two remote strings and pass the result back */
static int shm_server_do_strcat(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *s, *t;
char scat[256];
s = (void *)args;
args = minipc_get_next_arg(args, pd->args[0]);
t = (void *)args;
strncpy(scat, s, sizeof(scat));
strncat(scat, t, sizeof(scat) - strlen(s));
strcpy(ret, scat); /* FIXME: max size */
return 0;
}
/* stat a pathname received from the remote party */
static int shm_server_do_stat(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
struct stat stbuf;
char *fname = (void *)args;
if (stat(fname, &stbuf) < 0)
return -1;
memcpy(ret, &stbuf, sizeof(stbuf));
return 0;
}
/*
* The following is called by the main function, and exports stuff.
* All the rest happens through callbacks from the library
*/
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
int shm_export_functions(struct minipc_ch *ch)
{
int i;
/* Use a handy table for the registration loop */
static struct {
struct minipc_pd *desc;
minipc_f *f;
} export_list [] = {
{&rpc_getenv, shm_server_do_getenv},
{&rpc_setenv, shm_server_do_setenv},
{&rpc_add, shm_server_do_add},
{&rpc_strlen, shm_server_do_strlen},
{&rpc_strcat, shm_server_do_strcat},
{&rpc_stat, shm_server_do_stat},
};
/*
* Complete the data structures by filling the function pointers
* and then register each of the exported procedures
*/
for (i = 0; i < ARRAY_SIZE(export_list); i++) {
export_list[i].desc->f = export_list[i].f;
if (minipc_export(ch, export_list[i].desc) < 0)
return -1;
}
return 0;
}
/* the main function only runs the server */
int main(int argc, char **argv)
{
struct minipc_ch *ch;
if (argc != 2) {
fprintf(stderr, "%s: use \"%s <shmid>\n", argv[0], argv[0]);
exit(1);
}
ch = minipc_server_create(argv[1], 0);
if (!ch) {
fprintf(stderr, "%s: rpc_open(%s): %s\n", argv[0], argv[1],
strerror(errno));
exit(1);
}
minipc_set_logfile(ch, stderr);
/* Register your functions */
if (shm_export_functions(ch)) {
fprintf(stderr, "%s: exporting RPC functions: %s\n", argv[0],
strerror(errno));
exit(1);
}
/* Then just obey requests */
while (1) {
minipc_server_action(ch, 1000);
}
}
/*
* Structures for example shmem mini-ipc backends
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
/*
* This file defines the data structures used to describe the RPC calls
* between server and client. Note that the function pointers are only
* instantiated in the server
*/
#include "minipc.h"
#include "shmem-structs.h"
#include <sys/stat.h>
/* run getenv in the server */
struct minipc_pd rpc_getenv = {
.name = "getenv",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/* run setenv in the server */
struct minipc_pd rpc_setenv = {
.name = "setenv",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/* addition of two integers */
struct minipc_pd rpc_add = {
.name = "add",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_END,
},
};
/* horribly run strlen and strcat */
struct minipc_pd rpc_strlen = {
.name = "strlen",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
struct minipc_pd rpc_strcat = {
.name = "strcat",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/* run "stat" on a file name */
struct minipc_pd rpc_stat = {
.name = "stat",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, struct stat),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
extern struct minipc_pd rpc_getenv;
extern struct minipc_pd rpc_setenv;
extern struct minipc_pd rpc_add;
extern struct minipc_pd rpc_strlen;
extern struct minipc_pd rpc_strcat;
extern struct minipc_pd rpc_stat;
/*
* Example mini-ipc client
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include "minipc.h"
#define TRIVIAL_TIMEOUT 100 /* ms */
/* The description here is the same as in the server */
const struct minipc_pd ss_sum_struct = {
.name = "sum",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_END,
},
};
const struct minipc_pd ss_tod_struct = {
.name = "gettimeofday",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, struct timeval),
.args = {
MINIPC_ARG_END,
},
};
const struct minipc_pd ss_sqrt_struct = {
.name = "sqrt",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_DOUBLE, double),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_DOUBLE, double),
MINIPC_ARG_END,
},
};
int main(int argc, char **argv)
{
struct minipc_ch *client;
int a, b, c, ret;
struct timeval tv;
double rt_in, rt_out;
client = minipc_client_create("trivial", 0);
if (!client) {
fprintf(stderr, "%s: client_create(): %s\n", argv[0],
strerror(errno));
exit(1);
}
minipc_set_logfile(client, stderr);
/*
* gettod, sum, sum, gettod
* pause a while in-between, so several clients can be run
* concurrently as a load test on the server
*/
ret = minipc_call(client, TRIVIAL_TIMEOUT, &ss_tod_struct, &tv, NULL);
if (ret < 0) {
goto error;
}
printf("tv: %li.%06li\n", tv.tv_sec, tv.tv_usec);
usleep(500*1000);
a = 345; b = 628;
ret = minipc_call(client, TRIVIAL_TIMEOUT, &ss_sum_struct, &c, a, b);
if (ret < 0) {
goto error;
}
printf("%i + %i = %i\n", a, b, c);
usleep(500*1000);
a = 10; b = 20;
ret = minipc_call(client, TRIVIAL_TIMEOUT, &ss_sum_struct, &c, a, b);
if (ret < 0) {
goto error;
}
printf("%i + %i = %i\n", a, b, c);
usleep(500*1000);
rt_in = 2.0;
ret = minipc_call(client, TRIVIAL_TIMEOUT, &ss_sqrt_struct, &rt_out, rt_in);
if (ret < 0) {
goto error;
}
printf("sqrt(%lf) = %lf\n", rt_in, rt_out);
usleep(500*1000);
return 0;
error:
fprintf(stderr, "Error in rpc: %s\n", strerror(errno));
exit(1);
}
/*
* Example mini-ipc server
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <sys/time.h>
#include "minipc.h"
/* A function that ignores the RPC and is written normally */
static int ss_do_sum(int a, int b)
{
return a + b;
}
/* The following ones are RPC-aware */
static int ss_sum_function(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
int i;
i = ss_do_sum(args[0], args[1]);
*(int *)ret = i;
return 0;
}
static int ss_tod_function(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
struct timeval tv;
gettimeofday(&tv, NULL);
*(struct timeval *)ret = tv;
return 0;
}
static int ss_sqrt_function(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
double input, output;
input = *(double *)args;
output = sqrt(input);
*(double *)ret = output;
return 0;
}
/* Describe the three functions above */
const struct minipc_pd ss_sum_struct = {
.f = ss_sum_function,
.name = "sum",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_END,
},
};
const struct minipc_pd ss_tod_struct = {
.f = ss_tod_function,
.name = "gettimeofday",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, struct timeval),
.args = {
MINIPC_ARG_END,
},
};
const struct minipc_pd ss_sqrt_struct = {
.f = ss_sqrt_function,
.name = "sqrt",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_DOUBLE, double),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_DOUBLE, double),
MINIPC_ARG_END,
},
};
int main(int argc, char **argv)
{
struct minipc_ch *server;
server = minipc_server_create("trivial", 0);
if (!server) {
fprintf(stderr, "%s: server_create(): %s\n", argv[0],
strerror(errno));
exit(1);
}
minipc_set_logfile(server, stderr);
minipc_export(server, &ss_sum_struct);
minipc_export(server, &ss_tod_struct);
minipc_export(server, &ss_sqrt_struct);
while (1) {
if (minipc_server_action(server, 1000) < 0) {
fprintf(stderr, "%s: server_action(): %s\n", argv[0],
strerror(errno));
exit(1);
}
fprintf(stdout, "%s: looping...\n", __func__);
}
}
/*
* Mini-ipc: Exported functions for client operation
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Based on ideas by Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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 of the License, or
* (at your option) any later version.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <poll.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "minipc-int.h"
struct minipc_ch *minipc_client_create(const char *name, int f)
{
return __minipc_link_create(name, MPC_USER_FLAGS(f) | MPC_FLAG_CLIENT);
}
int minipc_call(struct minipc_ch *ch, int millisec_timeout,
const struct minipc_pd *pd, void *ret, ...)
{
struct mpc_link *link = mpc_get_link(ch);
struct mpc_shmem *shm = link->memaddr;
struct pollfd pfd;
int i, narg, size, retsize, pollnr;
int atype, asize;
va_list ap;
struct mpc_req_packet *p_out, _pkt_out = {"",};
struct mpc_rep_packet *p_in, _pkt_in;
CHECK_LINK(link);
if (shm) {
p_out = &shm->request;
p_in = &shm->reply;
} else {
p_out = & _pkt_out;
p_in = & _pkt_in;
}
/* Build the packet to send out -- marshall args */
if (link->logf) {
fprintf(link->logf, "%s: calling \"%s\"\n",
__func__, pd->name);
}
memcpy(p_out->name, pd->name, MINIPC_MAX_NAME);
va_start(ap, ret);
for (i = narg = 0; ; i++) {
int next_narg = narg;
atype = MINIPC_GET_ATYPE(pd->args[i]);
asize = MINIPC_GET_ASIZE(pd->args[i]);
next_narg += MINIPC_GET_ANUM(asize);
if (next_narg >= MINIPC_MAX_ARGUMENTS) /* unlikely */
goto doesnt_fit;
switch (atype) {
case MINIPC_ATYPE_NONE:
goto out; /* end of list */
case MINIPC_ATYPE_INT:
p_out->args[narg++] = va_arg(ap, int);
break;
case MINIPC_ATYPE_INT64:
*(uint64_t *)(p_out->args + narg)
= va_arg(ap, uint64_t);
narg = next_narg;
break;
case MINIPC_ATYPE_DOUBLE:
*(double *)(p_out->args + narg) = va_arg(ap, double);
narg = next_narg;
break;
case MINIPC_ATYPE_STRING:
{
char *sin = va_arg(ap, void *);
char *sout = (void *)(p_out->args + narg);
int slen = strlen(sin);
int alen;
/*
* argument len is arbitrary, terminate and 4-align
* we can't use next_narg here, as len changes
*/
alen = MINIPC_GET_ANUM(slen + 1);
if (narg + alen >= MINIPC_MAX_ARGUMENTS)
goto doesnt_fit;
strcpy(sout, sin);
narg += alen;
break;
}
case MINIPC_ATYPE_STRUCT:
memcpy(p_out->args + narg, va_arg(ap, void *), asize);
narg = next_narg;
break;
default:
if (link->logf) {
fprintf(link->logf, "%s: unkown type 0x%x\n",
__func__, atype);
}
errno = EPROTO;
return -1;
}
}
out:
va_end(ap);
if (shm) {
shm->nrequest++;
} else {
size = sizeof(p_out->name) + sizeof(p_out->args[0]) * narg;
if (send(ch->fd, p_out, size, 0) < 0) {
/* errno already set */
return -1;
}
}
/* Wait for the reply packet */
pfd.fd = ch->fd;
pfd.events = POLLIN | POLLHUP;
pfd.revents = 0;
pollnr = poll(&pfd, 1, millisec_timeout);
if (pollnr < 0) {
/* errno already set */
return -1;
}
if (pollnr == 0) {
errno = ETIMEDOUT;
return -1;
}
if (shm) {
read(ch->fd, &i, 1);
size = retsize = sizeof(shm->reply);
} else {
/* this "size" is wrong for strings, so recv the max size */
size = MINIPC_GET_ASIZE(pd->retval) + sizeof(uint32_t);
retsize = recv(ch->fd, p_in, sizeof(*p_in), 0);
}
/* if very short, we have a problem */
if (retsize < (sizeof(p_in->type)) + sizeof(int))
goto too_short;
/* remote error reported */
if (MINIPC_GET_ATYPE(p_in->type) == MINIPC_ATYPE_ERROR) {
int remoteerr = *(int *)&p_in->val;
if (link->logf) {
fprintf(link->logf, "%s: remote error \"%s\"\n",
__func__, strerror(remoteerr));
}
*(int *)ret = remoteerr;
errno = EREMOTEIO;
return -1;
}
/* another check: the return type must match */
if (MINIPC_GET_ATYPE(p_in->type) != MINIPC_GET_ATYPE(pd->retval)) {
if (link->logf) {
fprintf(link->logf, "%s: wrong code %08x (not %08x)\n",
__func__, p_in->type, pd->retval);
}
errno = EPROTO;
return -1;
}
/* check size */
if (retsize < size)
goto too_short;
/* all good */
memcpy(ret, &p_in->val, MINIPC_GET_ASIZE(p_in->type));
return 0;
too_short:
if (link->logf) {
fprintf(link->logf, "%s: short reply (%i bytes)\n",
__func__, retsize);
}
errno = EPROTO;
return -1;
doesnt_fit:
if (link->logf) {
fprintf(link->logf, "%s: rpc call \"%s\" won't fit %i slots\n",
__func__, pd->name, MINIPC_MAX_ARGUMENTS);
}
errno = EPROTO;
return -1;
}
/*
* Mini-ipc: Core library functions and data
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Based on ideas by Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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 of the License, or
* (at your option) any later version.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include "minipc-int.h"
struct mpc_link *__mpc_base;
static int __mpc_poll_usec = MINIPC_DEFAULT_POLL;
void mpc_free_flist(struct mpc_link *link, struct mpc_flist *flist)
{
struct mpc_flist **nextp;
/* Look for flist and release it*/
for (nextp = &link->flist; (*nextp); nextp = &(*nextp)->next)
if (*nextp == flist)
break;
if (!*nextp) {
if (link->logf)
fprintf(link->logf, "%s: function not found %p (%s)\n",
__func__, flist, flist->pd->name);
return;
}
*nextp = flist->next;
if (link->logf)
fprintf(link->logf, "%s: unexported function %p (%s)\n",
__func__, flist->pd->f, flist->pd->name);
free(flist);
}
int minipc_close(struct minipc_ch *ch)
{
struct mpc_link *link = mpc_get_link(ch);
struct mpc_link **nextp;
CHECK_LINK(link);
/* Look for link in our list */
for (nextp = &__mpc_base; (*nextp); nextp = &(*nextp)->nextl)
if (*nextp == link)
break;
if (!*nextp) {
errno = ENOENT;
return -1;
}
(*nextp)->nextl = link->nextl;
if (link->logf) {
fprintf(link->logf, "%s: found link %p (fd %i)\n",
__func__, link, link->ch.fd);
}
close(ch->fd);
if (link->pid)
kill(link->pid, SIGINT);
if (link->flags & MPC_FLAG_SHMEM)
shmdt(link->memaddr);
if (link->flags & MPC_FLAG_DEVMEM)
munmap(link->memaddr, link->memsize);
/* Release allocated functions */
while (link->flist)
mpc_free_flist(link, link->flist);
free(link);
return 0;
}
int minipc_set_poll(int usec)
{
int ret = __mpc_poll_usec;
if (usec <= 0) {
errno = EINVAL;
return -1;
}
__mpc_poll_usec = usec;
return ret;
}
int minipc_set_logfile(struct minipc_ch *ch, FILE *logf)
{
struct mpc_link *link = mpc_get_link(ch);
CHECK_LINK(link);
link->logf = logf;
return 0;
}
/* the child for memory-based channels just polls */
void __minipc_child(void *addr, int fd, int flags)
{
int i;
uint32_t prev, *vptr;
struct mpc_shmem *shm = addr;
for (i = 0; i < 256; i++)
if (i != fd) close(i);
/* check the parent: if it changes, then we exit */
i = getppid();
/* the process must only send one byte when the value changes */
if (flags & MPC_FLAG_SERVER)
vptr = &shm->nrequest;
else
vptr = &shm->nreply;
prev = *vptr;
/* Ok, unlock the parent: we are ready */
write(fd, "-", 1);
while (1) {
if (*vptr != prev) {
write(fd, "", 1);
prev++;
}
if (getppid() != i)
exit(0);
usleep(__mpc_poll_usec);
}
}
/* helper function for memory-based channels */
static struct mpc_link *__minipc_memlink_create(struct mpc_link *link)
{
void *addr;
long offset;
int memsize, pid, ret;
int pagesize = getpagesize();
int pfd[2];
char msg;
memsize = (sizeof(struct mpc_shmem) + pagesize - 1) & ~pagesize;
/* Warning: no check for trailing garbage in name */
if (sscanf(link->name, "shm:%li", &offset)) {
ret = shmget(offset, memsize, IPC_CREAT | 0666);
if (ret < 0)
return NULL;
addr = shmat(ret, NULL, SHM_RND);
if (addr == (void *)-1)
return NULL;
link->flags |= MPC_FLAG_SHMEM;
}
/* Warning: no check for trailing garbage in name -- hex mandatory */
if (sscanf(link->name, "mem:%lx", &offset)) {
int fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd < 0)
return NULL;
addr = mmap(0, memsize, PROT_READ | PROT_WRITE, MAP_SHARED,
fd, offset);
close(fd);
if (addr == (MAP_FAILED))
return NULL;
link->flags |= MPC_FLAG_DEVMEM;
}
link->memaddr = addr;
link->memsize = memsize;
if (link->flags & MPC_FLAG_SERVER)
memset(addr, 0, sizeof(struct mpc_shmem));
/* fork a polling process */
if (pipe(pfd) < 0)
goto err_unmap;
switch ( (pid = fork()) ) {
case 0: /* child */
close(pfd[0]);
__minipc_child(addr, pfd[1], link->flags);
exit(1);
default: /* father */
close(pfd[1]);
link->ch.fd = pfd[0];
link->pid = pid;
/* Before operating, wait for the child to ping us */
read (pfd[0], &msg, 1); /* must be '-' ... check? */
return link;
case -1:
break; /* error... */
}
close(pfd[0]);
close(pfd[1]);
err_unmap:
if (link->flags & MPC_FLAG_SHMEM)
shmdt(link->memaddr);
if (link->flags & MPC_FLAG_DEVMEM)
munmap(link->memaddr, link->memsize);
return NULL;
}
/* create a link, either server or client */
struct minipc_ch *__minipc_link_create(const char *name, int flags)
{
struct mpc_link *link, *next;
struct sockaddr_un sun;
int fd, i;
link = calloc(1, sizeof(*link));
if (!link) return NULL;
link->magic = MPC_MAGIC;
link->flags = flags;
strncpy(link->name, name, sizeof(link->name) -1);
/* special-case the memory-based channels */
if (!strncmp(name, "shm:", 4) || !strncmp(name, "mem:", 4)) {
if (!__minipc_memlink_create(link))
goto out_free;
goto out_success;
}
/* now create the socket and prepare the service */
fd = socket(SOCK_STREAM, AF_UNIX, 0);
if(fd < 0)
goto out_free;
link->ch.fd = fd;
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, MINIPC_BASE_PATH);
strcat(sun.sun_path, "/");
strcat(sun.sun_path, link->name);
mkdir(MINIPC_BASE_PATH, 0777); /* may exist, ignore errors */
if (flags & MPC_FLAG_SERVER) {
unlink(sun.sun_path);
if (bind (fd, (struct sockaddr *)&sun, sizeof(sun)) < 0)
goto out_close;
if (listen(fd, 5) < 0)
goto out_close;
} else { /* client */
if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) < 0)
goto out_close;
}
/* success: fix your fd values, link to the list and return */
out_success:
if (flags & MPC_FLAG_SERVER) {
for (i = 0; i < MINIPC_MAX_CLIENTS; i++)
link->fd[i] = -1;
FD_ZERO(&link->fdset);
FD_SET(link->ch.fd, &link->fdset);
}
link->addr = sun;
next = __mpc_base;
link->nextl = __mpc_base;
__mpc_base = link;
return &link->ch;
out_close:
close(fd);
out_free:
free(link);
return NULL;
}
/*
* Private definition for mini-ipc
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Based on ideas by Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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 of the License, or
* (at your option) any later version.
*/
#ifndef __MINIPC_INT_H__
#define __MINIPC_INT_H__
#include <sys/types.h>
#if __STDC_HOSTED__ /* freestanding servers have less material */
#include <sys/un.h>
#include <sys/select.h>
#endif
#include "minipc.h"
/* be safe, in case some other header had them slightly differntly */
#undef container_of
#undef offsetof
#undef ARRAY_SIZE
/* We are strongly based on container_of internally */
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/*
* While public symbols are minipc_* internal ones are mpc_* to be shorter.
* The connection includes an fd, which is the only thing returned back
*/
/* The list of functions attached to a service */
struct mpc_flist {
const struct minipc_pd *pd;
struct mpc_flist *next;
};
/*
* The main server or client structure. Server links have client sockets
* hooking on it.
*/
struct mpc_link {
struct minipc_ch ch;
int magic;
int pid;
int flags;
struct mpc_link *nextl;
struct mpc_flist *flist;
void *memaddr;
int memsize;
#if __STDC_HOSTED__ /* these fields are not used in freestanding uC */
FILE *logf;
struct sockaddr_un addr;
int fd[MINIPC_MAX_CLIENTS];
fd_set fdset;
#endif
char name[MINIPC_MAX_NAME];
};
#define MPC_MAGIC 0xc0ffee99
#define MPC_FLAG_SERVER 0x00010000
#define MPC_FLAG_CLIENT 0x00020000
#define MPC_FLAG_SHMEM 0x00040000
#define MPC_FLAG_DEVMEM 0x00080000
#define MPC_USER_FLAGS(x) ((x) & 0xffff)
/* The request packet being transferred */
struct mpc_req_packet {
char name[MINIPC_MAX_NAME];
uint32_t args[MINIPC_MAX_ARGUMENTS];
};
/* The reply packet being transferred */
struct mpc_rep_packet {
uint32_t type;
uint8_t val[MINIPC_MAX_REPLY];
};
/* A structure for shared memory (takes more than 2kB) */
struct mpc_shmem {
uint32_t nrequest; /* incremented at each request */
uint32_t nreply; /* incremented at each reply */
struct mpc_req_packet request;
struct mpc_rep_packet reply;
};
#define MPC_TIMEOUT 1000 /* msec, hardwired */
static inline struct mpc_link *mpc_get_link(struct minipc_ch *ch)
{
return container_of(ch, struct mpc_link, ch);
}
#define CHECK_LINK(link) /* Horrible shortcut, don't tell around... */ \
if ((link)->magic != MPC_MAGIC) { \
errno = EINVAL; \
return -1; \
}
extern struct mpc_link *__mpc_base;
extern void mpc_free_flist(struct mpc_link *link, struct mpc_flist *flist);
extern struct minipc_ch *__minipc_link_create(const char *name, int flags);
/* Used for lists and structures -- sizeof(uint32_t) is 4, is it? */
#define MINIPC_GET_ANUM(len) (((len) + 3) >> 2)
#endif /* __MINIPC_INT_H__ */
/*
* Mini-ipc: Exported functions for freestanding server
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This replicates some code of minipc-core and minipc-server.
* It implements the functions needed to make a freestanding server
* (for example, an lm32 running on an FPGA -- the case I actually need).
*/
#include "minipc-int.h"
#include <string.h>
#include <sys/errno.h>
/* HACK: use a static link, one only */
static struct mpc_link __static_link;
/* The create function just picks an hex address from the name "mem:AABBCC" */
struct minipc_ch *minipc_server_create(const char *name, int flags)
{
struct mpc_link *link = &__static_link;
int i, c, addr = 0;
int memsize = sizeof(struct mpc_shmem);
if (link->magic) {
errno = EBUSY;
return NULL;
}
/* Most code from __minipc_link_create and __minipc_memlink_create */
flags |= MPC_FLAG_SERVER;
if (strncmp(name, "mem:", 4)) {
errno = EINVAL;
return NULL;
}
/* Ok, valid name. Hopefully */
link->magic = MPC_MAGIC;
link->flags = flags;
strncpy(link->name, name, sizeof(link->name) -1);
/* Parse the hex address */
for (i = 4; (c = name[i]); i++) {
addr *= 0x10;
if (c >= '0' && c <= '9') addr += c - '0';
if (c >= 'a' && c <= 'f') addr += c - 'a' + 10;
if (c >= 'A' && c <= 'F') addr += c - 'A' + 10;
}
link->flags |= MPC_FLAG_SHMEM; /* needed? */
link->memaddr = (void *)addr;
link->memsize = memsize;
link->pid = 0; /* hack: nrequest */
if (link->flags & MPC_FLAG_SERVER)
memset(link->memaddr, 0, memsize);
return &link->ch;
}
/* Close only marks the link as available */
int minipc_close(struct minipc_ch *ch)
{
struct mpc_link *link = mpc_get_link(ch);
CHECK_LINK(link);
link->magic = 0; /* available */
return 0;
}
/* HACK: use a static array of flist, to avoid malloc and free */
static struct mpc_flist __static_flist[MINIPC_MAX_EXPORT];
static void *calloc(size_t unused, size_t unused2)
{
int i;
struct mpc_flist *p;
for (p = __static_flist, i = 0; i < MINIPC_MAX_EXPORT; p++, i++)
if (!p->pd)
break;
if (i == MINIPC_MAX_EXPORT) {
errno = ENOMEM;
return NULL;
}
return p;
}
static void free(void *ptr)
{
struct mpc_flist *p = ptr;
p->pd = NULL;
}
/* From: minipc-core.c, but relying on fake free above */
void mpc_free_flist(struct mpc_link *link, struct mpc_flist *flist)
{
struct mpc_flist **nextp;
/* Look for flist and release it*/
for (nextp = &link->flist; (*nextp); nextp = &(*nextp)->next)
if (*nextp == flist)
break;
if (!*nextp) {
return;
}
*nextp = flist->next;
free(flist);
}
/* From: minipc-server.c -- but no log and relies on fake calloc above */
int minipc_export(struct minipc_ch *ch, const struct minipc_pd *pd)
{
struct mpc_link *link = mpc_get_link(ch);
struct mpc_flist *flist;
CHECK_LINK(link);
flist = calloc(1, sizeof(*flist));
if (!flist)
return -1;
flist->pd = pd;
flist->next = link->flist;
link->flist = flist;
return 0;
}
/* From: minipc-server.c -- but no log file */
int minipc_unexport(struct minipc_ch *ch, const struct minipc_pd *pd)
{
struct mpc_link *link = mpc_get_link(ch);
struct mpc_flist *flist;
CHECK_LINK(link);
/* We must find the flist that points to pd */
for (flist = link->flist; flist; flist = flist->next)
if (flist->pd == pd)
break;
if (!flist) {
errno = EINVAL;
return -1;
}
flist = container_of(&pd, struct mpc_flist, pd);
mpc_free_flist(link, flist);
return 0;
}
/* From: minipc-server.c */
uint32_t *minipc_get_next_arg(uint32_t arg[], uint32_t atype)
{
int asize;
char *s = (void *)arg;
if (MINIPC_GET_ATYPE(atype) != MINIPC_ATYPE_STRING)
asize = MINIPC_GET_ANUM(MINIPC_GET_ASIZE(atype));
else
asize = MINIPC_GET_ANUM(strlen(s) + 1);
return arg + asize;
}
/* From: minipc-server.c (mostly: mpc_handle_client) */
int minipc_server_action(struct minipc_ch *ch, int timeoutms)
{
struct mpc_link *link = mpc_get_link(ch);
struct mpc_req_packet *p_in;
struct mpc_rep_packet *p_out;
struct mpc_shmem *shm = link->memaddr;
const struct minipc_pd *pd;
struct mpc_flist *flist;
int i;
CHECK_LINK(link);
/* keep track of the request in an otherwise unused field */
if (shm->nrequest == link->pid)
return 0;
link->pid = shm->nrequest;
p_in = &shm->request;
p_out = &shm->reply;
/* use p_in->name to look for the function */
for (flist = link->flist; flist; flist = flist->next)
if (!(strcmp(p_in->name, flist->pd->name)))
break;
if (!flist) {
p_out->type = MINIPC_ARG_ENCODE(MINIPC_ATYPE_ERROR, int);
*(int *)(&p_out->val) = EOPNOTSUPP;
goto send_reply;
}
pd = flist->pd;
/* call the function and send back stuff */
i = pd->f(pd, p_in->args, p_out->val);
if (i < 0) {
p_out->type = MINIPC_ARG_ENCODE(MINIPC_ATYPE_ERROR, int);
*(int *)(&p_out->val) = errno;
} else {
/* Use retval, but fix the length for strings */
if (MINIPC_GET_ATYPE(pd->retval) == MINIPC_ATYPE_STRING) {
int size = strlen((char *)p_out->val) + 1;
size = (size + 3) & ~3; /* align */
p_out->type =
__MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, size);
} else {
p_out->type = pd->retval;
}
}
send_reply:
shm->nreply++; /* message already in place */
return 0;
}
/*
* Mini-ipc: Exported functions for server operation
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Based on ideas and code by Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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 of the License, or
* (at your option) any later version.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/select.h>
#include "minipc-int.h"
/*
* This function creates a server structure and links it to the
* process-wide list of links
*/
struct minipc_ch *minipc_server_create(const char *name, int f)
{
return __minipc_link_create(name, MPC_USER_FLAGS(f) | MPC_FLAG_SERVER);
}
/*
* The following ones add to the export list and remove from it
*/
int minipc_export(struct minipc_ch *ch, const struct minipc_pd *pd)
{
struct mpc_link *link = mpc_get_link(ch);
struct mpc_flist *flist;
CHECK_LINK(link);
flist = calloc(1, sizeof(*flist));
if (!flist)
return -1;
flist->pd = pd;
flist->next = link->flist;
link->flist = flist;
if (link->logf)
fprintf(link->logf, "%s: exported %p (%s) with pd %p --"
" retval %08x, args %08x...\n", __func__,
flist, pd->name, pd, pd->retval, pd->args[0]);
return 0;
}
int minipc_unexport(struct minipc_ch *ch, const struct minipc_pd *pd)
{
struct mpc_link *link = mpc_get_link(ch);
struct mpc_flist *flist;
CHECK_LINK(link);
/* We must find the flist that points to pd */
for (flist = link->flist; flist; flist = flist->next)
if (flist->pd == pd)
break;
if (!flist) {
if (link->logf)
fprintf(link->logf, "%s: not found pd %p\n",
__func__, pd);
errno = EINVAL;
return -1;
}
flist = container_of(&pd, struct mpc_flist, pd);
mpc_free_flist(link, flist);
return 0;
}
/* Return the current fdset associated to the service */
int minipc_server_get_fdset(struct minipc_ch *ch, fd_set *setptr)
{
struct mpc_link *link = mpc_get_link(ch);
CHECK_LINK(link);
*setptr = link->fdset;
return 0;
}
/* Get a pointer to the next argument in the array */
uint32_t *minipc_get_next_arg(uint32_t arg[], uint32_t atype)
{
int asize;
char *s = (void *)arg;
if (MINIPC_GET_ATYPE(atype) != MINIPC_ATYPE_STRING)
asize = MINIPC_GET_ANUM(MINIPC_GET_ASIZE(atype));
else
asize = MINIPC_GET_ANUM(strlen(s) + 1);
return arg + asize;
}
/*
* Internal functions used by server action below: handle a request
* or the arrival of a new client
*/
static void mpc_handle_client(struct mpc_link *link, int pos, int fd)
{
struct mpc_req_packet *p_in, _pkt_in;
struct mpc_rep_packet *p_out, _pkt_out;
struct mpc_shmem *shm = link->memaddr;
const struct minipc_pd *pd;
struct mpc_flist *flist;
int i;
if (shm) {
p_in = &shm->request;
p_out = &shm->reply;
/* read one byte, it's just a signal */
read(fd, &i, 1);
} else {
p_in = & _pkt_in;
p_out = & _pkt_out;
/* receive the packet and manage errors */
i = recv(fd, p_in, sizeof(*p_in), 0);
if (i < 0 && errno == EINTR)
return;
if (i <= 0)
goto close_client;
}
/* use p_in->name to look for the function */
for (flist = link->flist; flist; flist = flist->next)
if (!(strcmp(p_in->name, flist->pd->name)))
break;
if (!flist) {
if (link->logf)
fprintf(link->logf, "%s: function %s not found\n",
__func__, p_in->name);
p_out->type = MINIPC_ARG_ENCODE(MINIPC_ATYPE_ERROR, int);
*(int *)(&p_out->val) = EOPNOTSUPP;
goto send_reply;
}
pd = flist->pd;
if (link->logf)
fprintf(link->logf, "%s: request for %s\n",
__func__, pd->name);
/* call the function and send back stuff */
i = pd->f(pd, p_in->args, p_out->val);
if (i < 0) {
p_out->type = MINIPC_ARG_ENCODE(MINIPC_ATYPE_ERROR, int);
*(int *)(&p_out->val) = errno;
} else {
/* Use retval, but fix the length for strings */
if (MINIPC_GET_ATYPE(pd->retval) == MINIPC_ATYPE_STRING) {
int size = strlen((char *)p_out->val) + 1;
size = (size + 3) & ~3; /* align */
p_out->type =
__MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, size);
} else {
p_out->type = pd->retval;
}
}
send_reply:
if (shm) {
shm->nreply++; /* message already in place */
return;
}
/* send a 32-bit value plus the declared return length */
if (send(fd, p_out, sizeof(p_out->type)
+ MINIPC_GET_ASIZE(p_out->type), MSG_NOSIGNAL) < 0)
goto close_client;
return;
close_client:
if (link->logf)
fprintf(link->logf, "%s: error %i in fd %i, closing\n",
__func__, i < 0 ? errno : 0, fd);
close(fd);
FD_CLR(fd, &link->fdset);
link->fd[pos] = -1;
return;
}
static void mpc_handle_connection(struct mpc_link *link, int fd)
{
int i, newfd;
struct sockaddr_un sun;
socklen_t slen = sizeof(sun);
newfd = accept(fd, (struct sockaddr *)&sun, &slen);
if (link->logf)
fprintf(link->logf, "%s: accept returned fd %i (error %i)\n",
__func__, newfd, newfd < 0 ? errno : 0);
if (newfd < 0)
return;
/* Lookf for a place for this */
for (i = 0; i < MINIPC_MAX_CLIENTS; i++)
if (link->fd[i] < 0)
break;
if (i == MINIPC_MAX_CLIENTS) {
if (link->logf)
fprintf(link->logf, "%s: refused: too many clients\n",
__func__);
close(newfd);
return;
}
link->fd[i] = newfd;
FD_SET(newfd, &link->fdset);
}
/*
* The server action returns an error or zero. If the user wants
* the list of active descriptors, it must ask for the filemask
* (at this point there is no support for poll, only select)
*/
int minipc_server_action(struct minipc_ch *ch, int timeoutms)
{
struct mpc_link *link = mpc_get_link(ch);
struct timeval to;
fd_set set;
int i;
CHECK_LINK(link);
to.tv_sec = timeoutms/1000;
to.tv_usec = (timeoutms % 1000) * 1000;
set = link->fdset;
i = select(64 /* FIXME: hard constant */, &set, NULL, NULL, &to);
if (!i)
return 0;
if (i < 0 && errno == EINTR)
return 0;
if (i < 0)
return -1;
if (i == 0) {
errno = EAGAIN;
return -1;
}
/* A shmem server has only one descriptor, for one client */
if (link->memaddr) {
mpc_handle_client(link, -1, ch->fd);
return 0;
}
/* First, look for all clients */
for (i = 0; i < MINIPC_MAX_CLIENTS; i++) {
if (link->fd[i] < 0)
continue;
if (!FD_ISSET(link->fd[i], &set))
continue;
mpc_handle_client(link, i, link->fd[i]);
}
/* Finally, look for a new client */
if (FD_ISSET(ch->fd, &set))
mpc_handle_connection(link, ch->fd);
return 0;
}
/*
* Public definition for mini-ipc
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Based on ideas by Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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 of the License, or
* (at your option) any later version.
*/
#ifndef __MINIPC_H__
#define __MINIPC_H__
#include <stdint.h>
#if __STDC_HOSTED__ /* freestanding servers have less material */
#include <stdio.h>
#include <sys/select.h>
#endif
/* Hard limits */
#define MINIPC_MAX_NAME 20 /* includes trailing 0 */
#define MINIPC_MAX_CLIENTS 64
#define MINIPC_MAX_ARGUMENTS 256 /* Also, max size of packet words -- 1k */
#define MINIPC_MAX_REPLY 1024 /* bytes */
#if !__STDC_HOSTED__
#define MINIPC_MAX_EXPORT 12 /* freestanding: static allocation */
#endif
/* The base pathname, mkdir is performed as needed */
#define MINIPC_BASE_PATH "/tmp/.minipc"
/* Default polling interval for memory-based channels */
#define MINIPC_DEFAULT_POLL (10*1000)
/* Argument type (and retval type). The size is encoded in the same word */
enum minipc_at {
MINIPC_ATYPE_ERROR = 0xffff,
MINIPC_ATYPE_NONE = 0, /* used as terminator */
MINIPC_ATYPE_INT = 1,
MINIPC_ATYPE_INT64,
MINIPC_ATYPE_DOUBLE, /* float is promoted to double */
MINIPC_ATYPE_STRING, /* size of strings is strlen() each time */
MINIPC_ATYPE_STRUCT
};
/* Encoding of argument type and size in one word */
#define __MINIPC_ARG_ENCODE(atype, asize) (((atype) << 16) | (asize))
#define MINIPC_ARG_ENCODE(atype, type) __MINIPC_ARG_ENCODE(atype, sizeof(type))
#define MINIPC_GET_ATYPE(word) ((word) >> 16)
#define MINIPC_GET_ASIZE(word) ((word) & 0xffff)
#define MINIPC_ARG_END __MINIPC_ARG_ENCODE(MINIPC_ATYPE_NONE, 0) /* zero */
/* The exported procedure looks like this */
struct minipc_pd;
typedef int (minipc_f)(const struct minipc_pd *, uint32_t *args, void *retval);
/* This is the "procedure definition" */
struct minipc_pd {
minipc_f *f; /* pointer to the function */
char name[MINIPC_MAX_NAME]; /* name of the function */
uint32_t flags;
uint32_t retval; /* type of return value */
uint32_t args[]; /* zero-terminated */
};
/* Flags: verbosity is about argument and retval marshall/unmarshall */
#define MINIPC_FLAG_VERBOSE 1
/* This is the channel definition */
struct minipc_ch {
int fd;
};
static inline int minipc_fileno(struct minipc_ch *ch) {return ch->fd;}
/* These return NULL with errno on error, name is the socket pathname */
struct minipc_ch *minipc_server_create(const char *name, int flags);
struct minipc_ch *minipc_client_create(const char *name, int flags);
int minipc_close(struct minipc_ch *ch);
/* Generic: set the default polling interval for mem-based channels */
int minipc_set_poll(int usec);
/* Server: register exported functions */
int minipc_export(struct minipc_ch *ch, const struct minipc_pd *pd);
int minipc_unexport(struct minipc_ch *ch, const struct minipc_pd *pd);
/* Server: helpers to unmarshall a string or struct from a request */
uint32_t *minipc_get_next_arg(uint32_t arg[], uint32_t atype);
/* Handle a request if pending, otherwise -1 and EAGAIN */
int minipc_server_action(struct minipc_ch *ch, int timeoutms);
#if __STDC_HOSTED__
/* Generic: attach diagnostics to a log file */
int minipc_set_logfile(struct minipc_ch *ch, FILE *logf);
/* Return an fdset for the user to select() on the service */
int minipc_server_get_fdset(struct minipc_ch *ch, fd_set *setptr);
/* Client: make requests */
int minipc_call(struct minipc_ch *ch, int millisec_timeout,
const struct minipc_pd *pd, void *ret, ...);
#endif /* __STDC_HOSTED__ */
#endif /* __MINIPC_H__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment