How to Run C Code Block from Erlang? ( Or Call a C function from erlang? )
This is for creating a driver
Firstly you'll need to create the C/C++ files to do it.
They will need to include
#include "erl_driver.h"
#include "ei.h"
Then you'll need to set up the driver mapping
/* mapping of the drivers functions */
static ErlDrvEntry driver_entry = {
NULL, /* init */
startup_function_name, /* startup */
shutdown_function_name, /* shutdown */
NULL, /* output */
NULL, /* ready_input */
NULL, /* ready_output */
driver_name, /* the name of the driver */
NULL, /* finish */
NULL, /* handle */
NULL, /* control */
NULL, /* timeout */
outputv_function_name, /* outputv */
NULL, /* ready_async */
NULL, /* flush */
NULL, /* call */
NULL, /* event */
ERL_DRV_EXTENDED_MARKER, /* ERL_DRV_EXTENDED_MARKER */
ERL_DRV_EXTENDED_MAJOR_VERSION, /* ERL_DRV_EXTENDED_MAJOR_VERSION */
ERL_DRV_EXTENDED_MAJOR_VERSION, /* ERL_DRV_EXTENDED_MINOR_VERSION */
ERL_DRV_FLAG_USE_PORT_LOCKING /* ERL_DRV_FLAGs */
};
DRIVER_INIT(driver_name){
return &driver_entry;
}
Note: if you are trying to run C++ code instead of C you'll need
extern "C" {
DRIVER_INIT(driver_name){
return &driver_entry;
}
}
And you will need to cast any literal string with (char *)
Then it's good to define a struct that'll contain the port information
typedef struct
{
ErlDrvPort port;
} port_data;
Lastly, you'll want to set up all the functions
static ErlDrvData startup_function_name(ErlDrvPort port, char *doc)
{
/* Plus any other start up methods you need */
port_data* d = (port_data*)driver_alloc(sizeof(port_data));
d->port = port;
return (ErlDrvData)d;
}
/* Plus any other shutdown methods you need */
static void shutdown_function_name(ErlDrvData handle)
{
driver_free((char*)handle);
}
static void outputv_function_name(ErlDrvData handle, ErlIOVec *ev)
{
port_data* d = (port_data*)handle;
char* inputstring = ev->binv[1]->orig_bytes;
ErlDrvTermData spec[] = {
ERL_DRV_ATOM, driver_mk_atom("ok"),
ERL_DRV_BUF2BINARY, inputstring, strlen(inputstring)
ERL_DRV_TUPLE, 2
};
driver_send_term(d->port,driver_caller(d->port),spec,sizeof(spec)/sizeof(spec[0]));
}
You'll want to compile this C/C++ code into a shared object and link it with the erl interface
g++ -fpic -rdynamic -shared file_name -lerl_interface -lei
Now from erlang you'll want to do a couple things:
You'll need to load the driver
erl_ddll:load_driver("./location/of/driver", driver_name).
Then you'll open a port to the driver
Port = open_port({spawn, driver_name}, [binary]).
And lastly you can sent data to the port
port_command(Port, <<"String to Echo Back"),
receive
{ok, String} -> io:format("Received ~p back from the driver")
end.
The newest approach would consider NIFs http://www.erlang.org/doc/man/erl_nif.html (be careful, it can crash VM). Regular way to do it involves linked in drivers (google up the link, because anti-spam holds it)
Related
Context
I was mandated to make a char driver for a specific hardware package. This driver will need to have two devices living under the same class. For now, I'm not really minding the whole file_operations struct as I'm mainly having problem with the sysfs and attribute definition. I'm working on a VMware and the driver must be able to run on linux v5.10.X.
What has been done
So far, I have initialized my driver (outside of any functions) as such:
static struct class * dev_class;
static struct device * some_dev_1;
static struct device * some_dev_2;
static struct cdev some_cdev_1;
static struct cdev some_cdev_2;
static dev_t dev_num_1 = MKDEV(MAJOR_NUMBER_1, 0); /* Major is 235 */
static dev_t dev_num_2 = MKDEV(MAJOR_NUMBER_2, 0); /* Major is 240 */
So I have two devices (for sysfs) with their cdev (for user interface). Inside the device_bringup function (see below) , I wanted to fill up the attribute_group ** groups structure with my show/store method as seen in the struct documentation. I tried following this tutorial and this one, which yielded the following code:
int device_bringup(char * device_1_name, char * device_2_name){
int return_value = SUCCESS;
some_dev_1 = device_create(dev_class, NULL, dev_num_1, NULL, device_1_name); /* Create/Registers it on sysfs */
some_dev_2 = device_create(dev_class, NULL, dev_num_2, NULL, device_2_name);
if(some_dev_1 == NULL){
printk(KERN_ALERT KBUILD_MODNAME " Cannot create the Device #1 !\n");
return_value = init_error_manager(1);
}
printk(KERN_ALERT KBUILD_MODNAME " Device #1 was created !\n");
if(some_dev_2 == NULL){
printk(KERN_ALERT KBUILD_MODNAME " Cannot create the Device #2 !\n");
return_value = init_error_manager(2);
}
printk(KERN_ALERT KBUILD_MODNAME " Device #2 was created !\n");
DEVICE_ATTR(first, 0664, sysfs_show, sysfs_store); /* Name should be dev_attr_first */
DEVICE_ATTR(second, 0664, sysfs_show, sysfs_store); /* Name should be dev_attr_second */
struct attribute * some_dev_1_attrs[] = { /* see https://docs.kernel.org/driver-api/driver-model/device.html for info */
&dev_attr_first.attr,
&dev_attr_second.attr,
NULL
};
struct attribute * some_dev_2_attrs[] = {
&dev_attr_first.attr,
&dev_attr_second.attr,
NULL
};
ATTRIBUTE_GROUPS(some_dev_1); /* Create the some_dev_1_groups */
ATTRIBUTE_GROUPS(some_dev_2); /* Create the some_dev_2_groups */
some_dev_1->groups = some_dev_1_groups;
some_dev_2->groups = some_dev_2_groups;
return return_value;
}
The problem
After following both tutorials up there, my kernel cannot build and I receive the following error:
error: initializer element is not constant ATTRIBUTE_GROUPS(some_dev_1);
error: initializer element is not constant ATTRIBUTE_GROUPS(some_dev_2);
I then read on the problem, which seems to sprout from the fact that some_dev_1 and some_dev_2 device pointers are not initialized. However, after reading some stackoverflow threads, I've tried doing:
static struct device * some_dev_1 = kmalloc(sizeof(some_dev_1));
then
static struct device * some_dev_1 = &(struct device){.name = "test"};
and even
static struct device * some_dev_1 = NULL;
However, none of these fixed the issue. I'm pretty sure the problem is easily fixed, but I can only seem to run in circles around it.
Solution
Turns out that the problem came from the fact that the macro ATTRIBUTE_GROUPS(some_dev_1) isn't really using the some_dev_1 defined outside the scope. It merely uses it as a string for the name of the groups.
Using device_create_with_groups without assigning the attribute group directly to the device is what made it work for me. I also had to make the DEVICE_ATTR and the arrays of attribute static in order for them to be available outside of the scope. This is the final, working code:
int device_bringup(char * device_1_name, char * device_2_name){
int return_value = SUCCESS;
static DEVICE_ATTR(first, 0664, sysfs_show, sysfs_store); /* Name should be dev_attr_tape_deck_first */
static DEVICE_ATTR(second, 0664, sysfs_show, sysfs_store); /* Name should be dev_attr_tape_deck_second */
static struct attribute * some_dev_1_attrs[] = {
&dev_attr_first.attr,
&dev_attr_second.attr,
NULL
};
static struct attribute * some_dev_2_attrs[] = {
&dev_attr_first.attr,
&dev_attr_second.attr,
NULL
};
ATTRIBUTE_GROUPS(some_dev_1);
ATTRIBUTE_GROUPS(some_dev_2);
some_dev_1 = device_create_with_groups(dev_class, NULL, dev_num_1, NULL, some_dev_1_groups, device_1_name);
some_dev_2 = device_create_with_groups(dev_class, NULL, dev_num_2, NULL, some_dev_2_groups, device_2_name);
if(IS_ERR_OR_NULL(some_dev_1)){
printk(KERN_ALERT KBUILD_MODNAME " Cannot create the Device #1 !\n");
return_value = PTR_ERR(some_dev_1);
}
printk(KERN_ALERT KBUILD_MODNAME " Device #1 was created !\n");
if(IS_ERR_OR_NULL(some_dev_2)){
printk(KERN_ALERT KBUILD_MODNAME " Cannot create the Device #2 !\n");
return_value = PTR_ERR(some_dev_2);
}
printk(KERN_ALERT KBUILD_MODNAME " Device #2 was created !\n");
return return_value;
}
This is not the final version of it as I'll have to make the function more atomic and not create two devices in the same function, but only one per call. However, the code above is what fixed my problem. I hope it'll help anyone encountering this sort of issue.
I have a simple Wireshark dissector which throws the following bug warning when it is run against a capture:
13:04:12 Warn Dissector bug, protocol usbserial, in packet 353: /wireshark/epan/proto.c:5504:
failed assertion "idx >= 0 && idx < num_tree_types"
The protocol registration function looks like this:
static gint ett_myproto = -1;
void
proto_register_myproto(void)
{
/* Set up field array */
static hf_register_info hf[] = {
{ &hf_myproto_payload,
{"Payload", "myproto.payload", FT_BYTES, BASE_NONE, NULL,
0x0, NULL, HFILL }},
};
/* Register protocol */
proto_myproto = proto_register_protocol("My Protocol", "myproto", "myproto");
/* Register protocol fields */
proto_register_field_array(proto_myproto, hf, array_length(hf));
/* Register the dissector */
register_dissector("myproto", dissect_myproto, proto_myproto);
}
The dissector does some general munging of data, but the core of the problem area seems to be:
static int
dissect_myproto(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
void *data _U_)
{
proto_item *ti;
proto_tree *myproto_tree;
/* Create top tree and add to the item */
ti = proto_tree_add_protocol_format(tree, proto_myproto, tvb, 0, -1,
"My Protocol");
myproto_tree = proto_item_add_subtree(ti, ett_myproto);
proto_tree_add_bytes_format(myproto_tree, hf_myproto_payload,
tvb, 0, payload_len,
NULL, "Payload");
}
What do I need to do to get the protocol to fill the subtree correctly?
The problem here is a failure to register the subtree as part of a subtree array (hint from here).
This is done in the protocol registration function, and requires "packaging" the subtree variables (of which there is only one here: ett_myproto) into an array, and then registering that array using proto_register_subtree_array:
static gint ett_myproto = -1;
void
proto_register_myproto(void)
{
/* Set up field array */
static hf_register_info hf[] = {
....
};
/* Register protocol */
proto_myproto = proto_register_protocol("My Protocol", "myproto", "myproto");
/* Register protocol fields */
proto_register_field_array(proto_myproto, hf, array_length(hf));
/* Setup and register all protocol subtrees */
static gint *ett[] = {
&ett_myproto,
};
proto_register_subtree_array(ett, array_length(ett));
/* Register the dissector */
register_dissector("myproto", dissect_myproto, proto_myproto);
}
The ett variables are indices used to refer to GUI information about the state of the subtree (e.g. expanded or not).
Good day all, I'd like some help to change a small section in this C program which I have licensed back in the early 90's. Some of you may be familiar with it, it's called MajorBBS and as you probably have guess it is bbs software.
In the header file majorbbs.h we have
extern
struct module { /* module interface block */
char descrp[MNMSIZ]; /* description for main menu */
int (*lonrou)(); /* user logon supplemental routine */
int (*sttrou)(); /* input routine if selected */
void (*stsrou)(); /* status-input routine if selected */
int (*injrou)(); /* "injoth" routine for this module */
int (*lofrou)(); /* user logoff supplemental routine */
void (*huprou)(); /* hangup (lost carrier) routine */
void (*mcurou)(); /* midnight cleanup routine */
void (*dlarou)(); /* delete-account routine */
void (*finrou)(); /* finish-up (sys shutdown) routine */
} **module;
Also in the majorbbs.h we have some code that defines the menu variables
extern
struct usrmnu { /* user's menuing-specific variables */
char curpag[PNMSIZ]; /* current menu page */
char parpag[PNMSIZ]; /* parent menu page */
char selchrs[MAXSEL]; /* select characters currently available */
char pages[MAXSEL][PNMSIZ]; /* pages or file names for select chars */
char optdsp[MAXSEL]; /* instructions on how to display options */
int keyreq[MAXSEL]; /* key required for each select character */
FILE *fp; /* pointer to file currently being viewed */
char mnuttl[TITLSZ]; /* menu page title */
} *mnuusr;
Then in the majorbbs.c file we have
struct module module00={ /* module interface block */
"Menuing System", /* description for main menu */
NULL, /* user logon supplemental routine */
mainu, /* input routine if selected */
musthn, /* status-input routine if selected */
NULL, /* "injoth" routine for this module */
NULL, /* user logoff supplemental routine */
loscar, /* hangup (lost carrier) routine */
midnit, /* midnight cleanup routine */
NULL, /* delete-account routine */
mjrfin /* finish-up (sys shutdown) routine */
};
What I'd like is to change the value of the descrp here which is defined as "Menuing System" to something more dynamic like the Menu the user is currently on.
From the code here I think it would be mnuusr->curpag which is where the pointer is pointing to I think.
So I'm thinking of a routine. I am by no means a programmer and I've been there many sites to look for examples of how to do such a thing. I've searched here for the last couple days (before posting this). I saw some things
that sparked a "Hey this might work" but I ended up with compiler errors (more on that in a bit)
What I did was make a routine like
char *
mydescrp
{
if (strcmp(module00.descrp,"Menuing System" ) == 0 ) {
mnuusr=mnuoff(usrnum);
return(mnuusr->mnuttl);
}
}
Then if I change the module00 call above to
struct module module00={
mydescrp, /* My change */
NULL,
mainu,
musthn,
NULL,
NULL,
loscar,
midnit,
NULL,
mjrfin
};
When I compile I get some error that says:
Initalization not fully bracketed
the list goes on from there. There are some further initialization later on in majorbbs.c and will gladly supply them if you need. I am sure one would be.
int
register_module( /* register a module for online use */
struct module *mod) /* pointer to a module block */
{
if (strlen(mod->descrp) > MNMSIZ-1) {
catastro("MODULE NAME \"%s\" TOO LONG!",mod->descrp);
}
if (mod->stsrou == NULL) {
mod->stsrou=dfsthn;
}
if (nmods == 0) {
module=(struct module **)alcmem(sizeof(struct module *));
mdstats=(struct mdstats *)alcmem(sizeof(struct mdstats));
}
else {
module=(struct module **)alcrsz(module,sizeof(struct module *)*nmods,
sizeof(struct module *)*(nmods+1));
mdstats=(struct mdstats *)alcrsz(mdstats,sizeof(struct mdstats)*nmods,
sizeof(struct mdstats)*(nmods+1));
}
module[nmods]=mod;
setbtv(mstbb);
if (qeqbtv(mod->descrp,0)) {
gcrbtv(&mdstats[nmods],0);
}
else {
setmem(&mdstats[nmods],sizeof(struct mdstats),0);
strcpy(mdstats[nmods].mdname,mod->descrp);
}
rstbtv();
return(nmods++);
}
From MENUING.C mnuoff routine
struct usrmnu *
mnuoff( /* get pointer to user's menu info */
int unum) /* user number to grab */
{
#ifdef PHARLAP
return((struct usrmnu *)((long)(eclmnubas+(unum<<3))<<16));
#else
#ifdef ECLIPSE
return((struct usrmnu *)((long)(eclmnubas+(unum<<3))<<16));
#else
return((struct usrmnu *)(muusrs+(unum*(long)sizeof(struct usrmnu))));
#endif
#endif
}
Is this the routine to change for some newly code? I am simply at a loss on how to go about this. If you need more code let me know.
I even went as far as asking for help from other majorbbs programmers on usenet but this software is 20 years + old so I don't think anyone uses it anymore let alone modify code anymore. I would think since it's still C someone might have an idea to help me out. I am trying to create a new revive with some small modifications. This being one of two.
Thanks for any help.
It looks like the descrp field in struct module is expecting a char[] (or a string), and you're giving it a char *() (function returning a string) instead.
Perhaps what you want to do is not call the mydescrp function in the declaration of module00, but manually perform the check in register_module:
int register_module(struct module *mod) {
if (strcmp(mod->descrp, "Menuing System") == 0) {
struct usrmnu *menu = mnuoff(usrnum);
// Copy as much of the title to the menu description as possible.
strncpy(mod->descrp, menu->mnuttl, MNMSIZ-1);
// Terminate the string in case the title was too long.
mod->descrp[MNMSIZ-1] = '\0';
}
// Rest of method.
}
I am trying to convert a win32 application in to service. I use CreateService() to create it application as a service(using below code).
SC_HANDLE schService = CreateService
(
schSCManager, /* SCManager database */
pName, /* name of service */
pName, /* service name to display */
SERVICE_ALL_ACCESS, /* desired access */
SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS , /*service type*/
SERVICE_AUTO_START, /* start type */
SERVICE_ERROR_NORMAL, /* error control type */
pPath, /* service's binary */
NULL, /* no load ordering group */
NULL, /* no tag identifier */
NULL, /* no dependencies */
NULL, /* LocalSystem account */
NULL
); /* no password */
I am able to start the service, if there is no argument in the actual application. If i try to start the service with argument, then its creating problem.
LPCTSTR apszSvcArgv[32] = {"start","passwd"};
int nSvcArgc = 2;
if(StartService(schService, nSvcArgc,apszSvcArgv))
{
return TRUE;
}
I tried to dump the incoming argument in the main program and its always showing the no of argument as 1.
Am i doing anything wrong?. Is it possible to pass argument like this to a win32 console application.
Please correct me if i am wrong.. Thanks in advance
you need to define the args vector as const char (or wchar), then pass the vector to StartService.
here is an example for a unicode program in VS
const wchar_t *args[] = { L"arg1", L"arg2", L"arg3", L"arg4" };
StartService(schService, 4, args);
When trying to mount a VTreeFS filesystem with a set of arguments (by using options -o when mounting) we want to let it fail cleanly if the user doesn't use the predefined arguments correctly. Currently we get this nasty error message when we do not mount the filesystem and let the main return 0. We basically want the filesystem to not be mounted if the arguments are inorrect
Current situations
mount -t filesystemtest -o testarguments none /mnt/filesystemtest
Arguments invalid
RS: service 'fs_00021' exited uring initialization
filesystemtest 109710 0xab6e 0x65f1 0x618d 0x6203 0x98ba 0x1010
Request to RS failed: unknown error (error 302)
mount: couldn't run /bin/sercie up /sbin/filesystemtest -label 'fs_00021'-args ''
mount: Can't mount none on /mnt/filesystemtest/: unknown error
Preferred situation
mount -t filesystemtest -o testarguments none /mnt/filesystemtest
Arguments invalid
Basically we wan't to know how to return a clean error message, when not calling start_vtreefs like below. The below example is not our actualy code and doesn't actually use arguments, but as an example there should be a way to have this piece of code to fail always. (sorry for that):
#include <minix/drivers.h>
#include <minix/vtreefs.h>
#include <sys/stat.h>
#include <time.h>
#include <assert.h>
static void my_init_hook(void)
{
/* This hook will be called once, after VTreeFS has initialized.
*/
struct inode_stat file_stat;
struct inode *inode;
/* We create one regular file in the root directory. The file is
* readable by everyone, and owned by root. Its size as returned by for
* example stat() will be zero, but that does not mean it is empty.
* For files with dynamically generated content, the file size is
* typically set to zero.
*/
file_stat.mode = S_IFREG | 0444;
file_stat.uid = 0;
file_stat.gid = 0;
file_stat.size = 0;
file_stat.dev = NO_DEV;
/* Now create the actual file. It is called "test" and does not have an
* index number. Its callback data value is set to 1, allowing it to be
* identified with this number later.
*/
inode = add_inode(get_root_inode(), "test", NO_INDEX, &file_stat, 0,
(cbdata_t) 1);
assert(inode != NULL);
}
static int my_read_hook(struct inode *inode, off_t offset, char **ptr,
size_t *len, cbdata_t cbdata)
{
/* This hook will be called every time a regular file is read. We use
* it to dyanmically generate the contents of our file.
*/
static char data[26];
const char *str;
time_t now;
/* We have only a single file. With more files, cbdata may help
* distinguishing between them.
*/
assert((int) cbdata == 1);
/* Generate the contents of the file into the 'data' buffer. We could
* use the return value of ctime() directly, but that would make for a
* lousy example.
*/
time(&now);
str = ctime(&now);
strcpy(data, str);
/* If the offset is beyond the end of the string, return EOF. */
if (offset > strlen(data)) {
*len = 0;
return OK;
}
/* Otherwise, return a pointer into 'data'. If necessary, bound the
* returned length to the length of the rest of the string. Note that
* 'data' has to be static, because it will be used after this function
* returns.
*/
*ptr = data + offset;
if (*len > strlen(data) - offset)
*len = strlen(data) - offset;
return OK;
}
/* The table with callback hooks. */
struct fs_hooks my_hooks = {
my_init_hook,
NULL, /* cleanup_hook */
NULL, /* lookup_hook */
NULL, /* getdents_hook */
my_read_hook,
NULL, /* rdlink_hook */
NULL /* message_hook */
};
int main(int argc, char* argv[])
{
/* The call above never returns. This just keeps the compiler happy. */
if (argc == 1) {
// We want it to fail right now!!!!
printf("Arguments invalid. (pass option with -o)");
}
else {
struct inode_stat root_stat;
/* Fill in the details to be used for the root inode. It will be a
* directory, readable and searchable by anyone, and owned by root.
*/
root_stat.mode = S_IFDIR | 0555;
root_stat.uid = 0;
root_stat.gid = 0;
root_stat.size = 0;
root_stat.dev = NO_DEV;
/* Now start VTreeFS. Preallocate 10 inodes, which is more than we'll
* need for this example. No indexed entries are used.
*/
start_vtreefs(&my_hooks, 10, &root_stat, 0);
}
return 0;
}