I am trying to simulate a sensor array in Castalia capable of using more than one sensor per node but when I run the simulation it takes the names of the sensors as one item and ends up displaying the sensor index and value of the sensor mentioned first. Please help!! how can I get it to detect more than one sensor per node
omnetpp.ini (valueReporting)
[General]
# ==========================================================
# Always include the main Castalia.ini file
# ==========================================================
include ../Parameters/Castalia.ini
sim-time-limit = 600s
SN.field_x = 100 # meters
SN.field_y = 100 # meters
SN.numNodes = 36
SN.deployment = "6x6"
SN.node[*].Communication.Radio.RadioParametersFile = "../Parameters/Radio/CC2420.txt"
SN.node[*].Communication.MACProtocolName = "TMAC"
SN.node[*].Communication.RoutingProtocolName = "MultipathRingsRouting"
#SN.node[*].Communication.Routing.collectTraceInfo = true
SN.node[*].ApplicationName = "ValueReporting"
SN.node[3].Application.isSink = true
# test 2 physical processes
SN.numPhysicalProcesses = 2
SN.physicalProcess[0].printDebugInfo = true
SN.physicalProcess[1].printDebugInfo = true
SN.physicalProcess[0].description = "Degrees Celsius"
SN.physicalProcess[1].description = "Blood Glucose"
SN.physicalProcess[0].inputType = 1
SN.physicalProcess[1].inputType = 1
#SN.physicalProcess[0].directNodeValueAssignment = "(0) 0:25 1:23 2:21 3:24 4:26"
#SN.physicalProcess[1].directNodeValueAssignment = "(0) 0:360 1:380 2:375 3:390 4:390"
SN.node[*].SensorManager.numSensingDevices = 2
SN.node[*].SensorManager.sensorTypes = "Temperature Bloodglucose"
SN.node[*].SensorManager.corrPhyProcess = "0 1"
In the SensorManager.ned file, I changed these lines as for the rest of the file everything is left as is
string sensorTypes = default ("Temperature,Bloodglucose");
// string array of comma-separated Names for the sensing devices
string corrPhyProcess = default ("0,1");
Now the temperature physical process files
TemperaturePhysicalProcess.ned
package physicalProcess.temperaturePhysicalProcess;
simple TemperaturePhysicalProcess like physicalProcess.iPhysicalProcess {
parameters:
bool collectTraceInfo = default (true);
double temperature = default (37); //normal body temperature is 37 degrees celcius
string description = default ("Degrees Celsius");
gates:
output toNode[];
input fromNode[];
}
TemperaturePhysicalProcess.h
#ifndef _TEMPERATUREPHYSICALPROCESS_H_
#define _TEMPERATUREPHYSICALPROCESS_H_
#define SIMTIME_STEP 0.01
#include "CastaliaModule.h"
#include "PhysicalProcessMessage_m.h"
using namespace std;
typedef struct {
simtime_t time;
double x;
double y;
} sourceSnapshot;
class TemperaturePhysicalProcess: public CastaliaModule {
private:
bool printDebugInfo;
int temperature;
const char *description;
protected:
virtual void initialize();
virtual void handleMessage(cMessage * msg);
virtual void finishSpecific();
};
#endif
TemperaturePhysicalProcess.cc
#include "TemperaturePhysicalProcess.h"
Define_Module(TemperaturePhysicalProcess);
void TemperaturePhysicalProcess::initialize()
{
temperature=37;
//Search for snapshots in castalia manual
}
void TemperaturePhysicalProcess::handleMessage(cMessage * msg)
{
if (msg->getKind() != PHYSICAL_PROCESS_SAMPLING)
{
opp_error("Physical Process received message other than PHYSICAL_PROCESS_SAMPLING");
}
PhysicalProcessMessage *receivedMsg = check_and_cast < PhysicalProcessMessage * >(msg);
int nodeIndex = receivedMsg->getSrcID();
// Send reply back to the node who made the request
receivedMsg->setValue(temperature);
send(receivedMsg, "toNode", nodeIndex);
}
void TemperaturePhysicalProcess::finishSpecific() {}
Now we'll go to the Blood Glucose physical process
BloodGlucoseLevelPhysicalProcess.ned
package physicalProcess.bloodGlucoseLevelPhysicalProcess;
simple BloodGlucoseLevelPhysicalProcess like physicalProcess.iPhysicalProcess {
parameters:
bool collectTraceInfo = default (false);
int averagebloodglucose = default (100); // the amount is in mg per dL
int age = default (20);
string description = default ("Blood Glucose");
gates:
output toNode[];
input fromNode[];
}
BloodGlucoseLevelPhysicalProcess.h
#ifndef _BLOOODGLUCOSELEVELPHYSICALPROCESS_H_
#define _BLOOODGLUCOSELEVELPHYSICALPROCESS_H_
#define SIMTIME_STEP 0.01
#include "CastaliaModule.h"
#include "PhysicalProcessMessage_m.h"
using namespace std;
typedef struct {
simtime_t time;
double x;
double y;
} sourceSnapshot;
class BloodGlucoseLevelPhysicalProcess: public CastaliaModule {
private:
bool printDebugInfo;
int averagebloodglucose;
double A1c;
const char *description;
protected:
virtual void initialize();
virtual void handleMessage(cMessage * msg);
virtual void finishSpecific();
};
#endif
BloodGlucoseLevelLevelPhysicalProcess.cc
#include "BloodGlucoseLevelPhysicalProcess.h"
Define_Module(BloodGlucoseLevelPhysicalProcess);
void BloodGlucoseLevelPhysicalProcess::initialize()
{
averagebloodglucose = par("averagebloodglucose");
description = par("description").stringValue();
A1c = (46.7 + averagebloodglucose) / 28.7;
//Search for snapshots in castalia manual
}
void BloodGlucoseLevelPhysicalProcess::handleMessage(cMessage * msg)
{
if (msg->getKind() != PHYSICAL_PROCESS_SAMPLING)
opp_error("Physical Process received message other than PHYSICAL_PROCESS_SAMPLING");
PhysicalProcessMessage *receivedMsg = check_and_cast < PhysicalProcessMessage * >(msg);
int nodeIndex = receivedMsg->getSrcID();
//int sensorIndex = receivedMsg->getSensorIndex();
double returnValue;
// Send reply back to the node who made the request
//receivedMsg->setValue(returnValue);
receivedMsg->setValue(A1c);
send(receivedMsg, "toNode", nodeIndex);
}
void BloodGlucoseLevelPhysicalProcess::finishSpecific() {
}
Since I'm running the simulation using the ValueReporting application
ValueReporting.h
#define _VALUEREPORTING_H_
#include "VirtualApplication.h"
#include "ValueReportingPacket_m.h"
using namespace std;
enum ValueReportingTimers {
REQUEST_SAMPLE = 1,
SEND_DATA = 2,
};
class ValueReporting: public VirtualApplication {
private:
double maxSampleInterval;
double minSampleInterval;
int routingLevel;
double lastSensedValue;
int currSentSampleSN;
double randomBackoffIntervalFraction;
bool sentOnce;
int recipientId;
string recipientAddress;
protected:
void startup();
void fromNetworkLayer(ApplicationPacket *, const char *, double, double);
void handleSensorReading(SensorReadingMessage *);
void timerFiredCallback(int);
void requestSensorReading(const char *);
};
#endif // _VALUEREPORTING_APPLICATIONMODULE_H_
ValueReporting.cc
#include "ValueReporting.h"
#include <iostream> // std::cout
#include <iomanip>
#include <string> // std::string, std::to_string
#include <stdlib.h> /* atof */
#include <math.h>
#include<sstream>
#include <cstring>
#include <vector>
#include <array>
#include <string>
#include <algorithm>
Define_Module(ValueReporting);
void ValueReporting::startup()
{
maxSampleInterval = ((double)par("maxSampleInterval")) / 1000.0;
minSampleInterval = ((double)par("minSampleInterval")) / 1000.0;
currSentSampleSN = 0;
randomBackoffIntervalFraction = genk_dblrand(0);
sentOnce = false;
setTimer(REQUEST_SAMPLE, maxSampleInterval * randomBackoffIntervalFraction);
}
void ValueReporting::timerFiredCallback(int index)
{
switch (index) {
case REQUEST_SAMPLE:{
requestSensorReading("Temperature");
//requestSensorReading("Urine");
setTimer(REQUEST_SAMPLE, maxSampleInterval);
break;
}
}
}
void ValueReporting::fromNetworkLayer(ApplicationPacket * genericPacket,
const char *source, double rssi, double lqi)
{
ValueReportingDataPacket *rcvPacket = check_and_cast<ValueReportingDataPacket*>(genericPacket);
ValueReportData theData = rcvPacket->getExtraData();
trace() << "Sink received from: " << theData.nodeID << " \tvalue=" << rcvPacket->getData();
}
void ValueReporting::handleSensorReading(SensorReadingMessage * rcvReading)
{
int sensIndex = rcvReading->getSensorIndex();
string sensType(rcvReading->getSensorType());
double sensValue = rcvReading->getSensedValue();
double x_coor = mobilityModule->getLocation().x;
double y_coor = mobilityModule->getLocation().y;
string sensorindex = to_string(sensIndex);
string sensvalue = to_string(sensValue);
string xcoor = to_string(x_coor);
string ycoor = to_string(y_coor);
string sensorinfo= sensorindex + " " + sensvalue + " " + xcoor + " " + ycoor + " " + sensType;
trace() << sensorinfo;
ValueReportData tmpData;
tmpData.nodeID = (unsigned short)self;
tmpData.locX = mobilityModule->getLocation().x;
tmpData.locY = mobilityModule->getLocation().y;
ValueReportingDataPacket *packet2Net =
new ValueReportingDataPacket("Value reporting pck", APPLICATION_PACKET);
packet2Net->setExtraData(tmpData);
packet2Net->setData(sensValue);
packet2Net->setSequenceNumber(currSentSampleSN);
currSentSampleSN++;
toNetworkLayer(packet2Net, SINK_NETWORK_ADDRESS);
//toNetworkLayer(packet2Net, "6");
sentOnce = true;
}
void ValueReporting::requestSensorReading(const char * type){
SensorReadingMessage *reqMsg =
new SensorReadingMessage("App to Sensor Mgr: sample request", SENSOR_READING_MESSAGE);
if(type == "Temperature"){
reqMsg->setSensorType(type);
reqMsg->setSensorIndex(0);
}
if(type =="BloodGlucose"){
reqMsg->setSensorType(type);
reqMsg->setSensorIndex(1);
}
send(reqMsg, "toSensorDeviceManager");
}
Some things I noticed:
There is no printDebugInfo parameter in the PhysicalProcess module. Probably what you are after is collectTraceInfo.
If you just want to update the values some parameters take, it's not a good idea to edit ned files. This is what ini files are for. So instead of changing the default values in ned files, simply assign these values in your ini file. For example, you already assign these parameters in your ini file:
SN.node[*].SensorManager.sensorTypes = "Temperature Bloodglucose"
SN.node[*].SensorManager.corrPhyProcess = "0 1"
You do not need to set them in the ned file as well. Also notice that in the ned file you set the second string as "0,1" not "0 1". The ini file will override the value of the ned file, so what you'll get it "0 1". Fortunately, space separated values is the correct syntax.
You then start defining new .ned .cc and .h files. Why? These do not have any effect. You have not set the variable SN.physicalProcessName in your ini file. This means it takes the default value which is CustomizablePhysicalProcess. You seem to be treating your physical process modules as CustomizablePhysicalProcess because I see you define their parameters inputType and directNodeValueAssignment, which only exist for the CustomizablePhysicalProcess module. So I do not understand what you expect to get by trying to define new physical process modules altogether.
Since you have 2 physical processes, it means that the SensorManager in every node needs to connect to these 2 physical process (the 2 modules that define your physical processes). To do this just set all the parameters of your SensorManager that can be defined as an array (formatted as a space-separated string) as arrays of 2 items.
You can find all relevant parameters in the SensorManager.ned (but do NOT edit the ned file, just set them in your ini file). Here are the parameters just for reference (note that you have already set sensorTypes and corrPhyProcess in your ini file, you just need to also set the rest) :
string pwrConsumptionPerDevice = default ("0.02");
string sensorTypes = default ("Temperature"); // Humidity OR Temperature OR Light
string corrPhyProcess = default ("0"); //holds the indexes of the corresponding phy processes for
//each sensor (usually it should be : "0 1 2 3")
string maxSampleRates = default ("1"); //number of samples per second for each sensor
string devicesBias = default ("0.1"); //If the output signal is not zero when the measured property is zero
string devicesDrift = default (""); //If the output signal slowly changes independent of the
//measured property
string devicesNoise = default ("0.1"); //random deviation of the signal that varies in time
string devicesHysterisis = default (""); //the sensor not instantly follows the change of the property
//being measured and therefore involves the history of the
//measured property
string devicesSensitivity = default ("0"); //holds the minimum value which can be sensed by each sensing device.
string devicesResolution = default ("0.001"); //holds the sensing resolution for each device
I am not sure whether there are other problems with multiple phyProcesses and multiple sensing modalities, because this feature has not been tested in versions after 2.0, so it might not work at all (but hopefully it's a small adjustment to make it work).
Summary
I'm creating a pseudo filesystem for Linux kernel 4.13, but my directories can't be listed with ls. I keep getting the message:
ls: cannot access 'mountedfs/data': No such file or directory.
Details
Creation and registration of the super_block and root dentry structures goes well. I can mount the file system without trouble, but I can't list the contents of my filesystem. When I try I get the error "No such file or directory".
After creating the super_block and root dentry, I call womfs_create_files() to populate the tree. Here is the full source code of the module. As you can see, I haven't even bothered with the file operations yet. I'm still stuck on inode operations.
#include <linux/kernel.h>
#include <linux/fs.h> /* libfs and most file-related headers. */
#include <linux/dcache.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pagemap.h> /* PAGE_SIZE */
#include <linux/atomic.h>
#include <linux/time.h>
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/parser.h>
#include <linux/magic.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "wombat.h"
/*
* Wombat directories are all basic. They just contain stuff, you can't link to them,
* you can't delete them, and you can't modify them. Anyone can list them, and
* they're owned by root.
*
* The file inodes are more specialized: they have to be linked to information about the
* keys they represent and the operations that can be performed on those keys.
* The same key data will be used by several inodes, but each inode needs to understand
* its particular purpose. (eg: "<key>/pub_key" lets you retrieve the public key;
* "<key>/sign" lets you sign data with the key.)
*
* Open file nodes need state information for processing reads, writes, ioctl,
* etc..
*/
/*
* Boilerplate stuff.
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("CJ Holmes");
#define WOMFS_NAME "womfs"
#define WOMFS_MAGIC 0x00ff0019 // Lear Red
/*
* Operations keygen file that uses the machine-specific KEK to create
* a shrouded key for data encryption.
*/
int womfs_keygen_open(struct inode *inode, struct file *filp)
{
return 0;
}
ssize_t womfs_keygen_read(struct file *filp, char __user *buf,
size_t count, loff_t *offset)
{
return 0;
}
int womfs_keygen_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations keygen_ops = {
.open = womfs_keygen_open,
.release = womfs_keygen_release,
.read = womfs_keygen_read,
};
/*
* Operations for encryption endpoints. The key and semantics depend on
* the key info in i_private and calls to ioctl.
*/
int womfs_encrypt_open(struct inode *inode, struct file *filp)
{
return 0;
}
ssize_t womfs_encrypt_read(struct file *filp, char *buf,
size_t count, loff_t *offset)
{
return 0;
}
ssize_t womfs_encrypt_write(struct file *filp, const char __user * buf, size_t len, loff_t *pos) {
return 0;
}
int womfs_encrypt_release(struct inode *inode, struct file *filp)
{
return 0;
}
long womfs_encrypt_ioctl(struct file *filp, unsigned int cmd, unsigned long data)
{
return 0;
}
static struct file_operations encrypt_ops = {
.open = womfs_encrypt_open,
.release = womfs_encrypt_release,
.read = womfs_encrypt_read,
.write = womfs_encrypt_write,
.unlocked_ioctl = womfs_encrypt_ioctl,
};
/*
* Stuff for building our FS structure.
*/
// Implementation borrowed from fs/stat.c:vsf_getattr_nosec()
int womfs_getattr(const struct path *path, struct kstat *stat, u32 request_mask,
unsigned int query_flags) {
struct inode *inode;
inode = d_inode(path->dentry);
printk(KERN_WARNING WOMFS_NAME " womfs_getattr(%pd4) --> %p\n", path->dentry, inode);
memset(stat, 0, sizeof(*stat));
stat->result_mask |= STATX_BASIC_STATS;
if(inode != NULL)
generic_fillattr(inode, stat); // fs/stat.c
return 0;
}
const struct inode_operations womfs_dir_inode_operations = {
.lookup = simple_lookup,
.getattr = womfs_getattr,
};
const struct inode_operations womfs_inode_operations = {
.getattr = womfs_getattr,
};
static struct inode *womfs_make_inode(struct super_block *sb, struct wombat_key_info *key,
kgid_t group, char *domain, int mode, struct file_operations *fops)
{
struct inode *ret = new_inode(sb);
struct timespec now = current_kernel_time();
if (ret) {
ret->i_mode = mode;
ret->i_uid = KUIDT_INIT(0);
ret->i_gid = group;
ret->i_blocks = 0;
ret->i_atime = now;
ret->i_mtime = now;
ret->i_ctime = now;
if (mode & S_IFDIR) {
ret->i_op = &womfs_dir_inode_operations;
ret->i_fop = (fops == NULL) ? &simple_dir_operations : fops ;
inc_nlink(ret);
} else {
ret->i_op = &womfs_inode_operations;
ret->i_fop = fops;
}
ret->i_private = key;
}
return ret;
}
struct dentry *womfs_add_node_to_dir(struct dentry *parent, struct inode *node, const char *name)
{
struct dentry *child;
struct qstr qname;
qname.name = name;
qname.len = strlen(name);
qname.hash = full_name_hash(NULL, name, qname.len);
child = d_alloc(parent, &qname);
if (child != NULL)
{
// d_instantiate(child, node);
d_add(child, node);
inode_inc_link_count(node);
}
return child;
}
static void womfs_create_files (struct super_block *sb, struct dentry *root)
{
struct dentry *subdir;
struct dentry *fentry;
struct inode *node;
kgid_t group;
/* This will eventually be a loop through all of the slots provided by the SNVS.
For now, we can just add the KEK entry. */
struct wombat_key_info *ki = kzalloc(sizeof(struct wombat_key_info), GFP_KERNEL);
ki->type = wombat_key_kek;
ki->slot = 0;
strcpy(ki->name, "data");
// leave group and domain blank, pending further implementation.
group = KGIDT_INIT(0);
/* This can be cleaned up, perhaps by combining womfs_make_inode()
and womfs_add_node_to_dir()
*/
node = womfs_make_inode(sb, ki, group, NULL, S_IFDIR | 0555, NULL);
if(node != NULL) {
subdir = womfs_add_node_to_dir(root, node, ki->name);
if (subdir != NULL) {
printk(KERN_WARNING WOMFS_NAME " %pd4", subdir);
switch(ki->type) {
case wombat_key_kek:
/* create the data/keygen file */
node = womfs_make_inode(sb, ki, group, NULL, S_IFREG | 0444, &keygen_ops);
if(node != NULL) {
fentry = womfs_add_node_to_dir(subdir, node, "keygen");
if(fentry != NULL)
printk(KERN_WARNING WOMFS_NAME " %pd4", fentry);
else
iput(node);
}
/* create the data/encrypt file */
node = womfs_make_inode(sb, ki, group, NULL, S_IFREG | 0666, &encrypt_ops);
if( node != NULL) {
fentry = womfs_add_node_to_dir(subdir, node, "encrypt");
if(fentry != NULL)
printk(KERN_WARNING WOMFS_NAME " %pd4", fentry);
else
iput(node);
}
break;
default:
/* Show some error here. */
break;
}
} else {
iput(node);
}
}
}
/*
* Superblock stuff. This is all boilerplate to give the vfs something
* that looks like a filesystem to work with.
*/
/*
* Our superblock operations, both of which are generic kernel ops
* that we don't have to write ourselves.
*/
static struct super_operations womfs_s_ops = {
.statfs = simple_statfs,
.drop_inode = generic_delete_inode,
};
/*
* "Fill" a superblock with mundane stuff.
*/
static int womfs_fill_super (struct super_block *sb, void *data, int silent)
{
int retval = 0;
struct inode *root = NULL;
struct dentry *root_dentry = NULL;
kgid_t gid = KGIDT_INIT(0);
/*
* Basic parameters.
*/
sb->s_blocksize = PAGE_SIZE;
sb->s_blocksize_bits = PAGE_SHIFT;
sb->s_magic = WOMFS_MAGIC;
sb->s_op = &womfs_s_ops;
sb->s_time_gran = 1;
/*
* We need to conjure up an inode to represent the root directory
* of this filesystem. Its operations all come from libfs, so we
* don't have to mess with actually *doing* things inside this
* directory.
*/
root = womfs_make_inode(sb, NULL, gid, NULL, S_IFDIR | 0555, NULL);
if (root != NULL) {
// make the root directory entry.
root_dentry = d_make_root(root);
if (root_dentry != NULL) {
sb->s_root = root_dentry;
womfs_create_files (sb, root_dentry);
printk(KERN_WARNING WOMFS_NAME " setup complete\n");
} else {
retval = -ENOMEM;
}
} else {
retval = -ENOMEM;
}
if (retval != 0) {
// clean up our inode and dirent
if (root != NULL) {
iput(root);
}
if (root_dentry != NULL) {
dput(root_dentry);
}
}
return retval;
}
/*
* Stuff to pass in when registering the filesystem.
*/
struct dentry *womfs_mount(struct file_system_type *fst,
int flags, const char *devname, void *data)
{
return mount_nodev(fst, flags, data, womfs_fill_super);
}
static struct file_system_type womfs_type = {
.owner = THIS_MODULE,
.name = WOMFS_NAME,
.mount = womfs_mount,
.kill_sb = kill_litter_super,
};
/*
* Get things set up.
*/
static int __init womfs_init(void)
{
return register_filesystem(&womfs_type);
}
static void __exit womfs_exit(void)
{
unregister_filesystem(&womfs_type);
}
module_init(womfs_init);
module_exit(womfs_exit);
When I load the module and mount my filesystem, I see the following lines in my syslog:
Apr 17 11:43:59 felix kernel: [ 7024.360872] womfs data
Apr 17 11:43:59 felix kernel: [ 7024.360873] womfs data/keygen
Apr 17 11:43:59 felix kernel: [ 7024.360874] womfs data/encrypt
Apr 17 11:43:59 felix kernel: [ 7024.360875] womfs setup complete
The ls command shows my mounted file system:
cholmes#felix:~/leardev/womfs$ ls -l
total 668
-rw-rw-r-- 1 cholmes cholmes 317 Apr 12 10:38 Makefile
-rw-rw-r-- 1 cholmes cholmes 46 Apr 17 11:28 modules.order
-rw-rw-r-- 1 cholmes cholmes 0 Apr 16 15:50 Module.symvers
-rw-rw-r-- 1 cholmes cholmes 6860 Apr 12 11:39 README.md
... etc ...
dr-xr-xr-x 2 root root 0 Apr 17 11:28 mountedfs
But listing the contents of mountedfs is a disaster:
cholmes#felix:~/leardev/wombat$ ls -l mountedfs
ls: cannot access 'mountedfs/data': No such file or directory
total 0
d????????? ? ? ? ? ? data
It seems obvious I'm forgetting something pretty simple. It just isn't obvious to me yet.
Update 1
The data directory uses simple_dir_inode_operations and simple_dir_operations pointers from libfs.
The two regular files have very minimal ops structures, but I'll post them after I remove all the truly interesting bits ;-)
An strace ls -l mountedfs command displays:
... all the usual linking to run ls ...
open("mountedfs/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
getdents(3, /* 3 entries */, 32768) = 72
lstat("mountedfs/data", 0xaa01a0) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en_US/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
... the rest is all about printing the error message ...
Update 2
The missing getattr() hook doesn't seem to be the problem. VFS provides a default implementation that copies attribute information from the inode, so you don't really need to implement getattr() unless you have to deal with syncing to disk or similar issues.
What I discovered was that my getattr() methods aren't even being called for subdirectories. Here's the new bits of my implementation:
// Implementation borrowed from fs/stat.c:vsf_getattr_nosec()
int womfs_getattr(const struct path *path, struct kstat *stat, u32 request_mask,
unsigned int query_flags) {
struct inode *inode;
inode = d_inode(path->dentry);
printk(KERN_WARNING WOMFS_NAME " womfs_getattr(%pd4) --> %p\n", path->dentry, inode);
memset(stat, 0, sizeof(*stat));
stat->result_mask |= STATX_BASIC_STATS;
if(inode != NULL)
generic_fillattr(inode, stat); // fs/stat.c
return 0;
}
const struct inode_operations womfs_dir_inode_operations = {
.lookup = simple_lookup,
.getattr = womfs_getattr,
};
const struct inode_operations womfs_inode_operations = {
.getattr = womfs_getattr,
};
I add these ops to the inodes in the expected way. When I mount my file system I get printk() messages as expected:
Apr 18 14:19:21 felix kernel: [20777.116214] womfs /data
Apr 18 14:19:21 felix kernel: [20777.116216] womfs /data/keygen
Apr 18 14:19:21 felix kernel: [20777.116217] womfs /data/encrypt
Apr 18 14:19:21 felix kernel: [20777.116218] womfs setup complete
And when I do an ls on the filesystem I get the same error message as before, plus some syslog messages telling me that womfs_getattr() was invoked.
Apr 18 14:19:27 felix kernel: [20782.880473] womfs womfs_getattr(/) --> ffff9e957c902960
Apr 18 14:19:27 felix kernel: [20782.880696] womfs womfs_getattr(/) --> ffff9e957c902960
So only the root of the filesystem is being stat'ed. It seems like my inode isn't being properly added to the dentry.
After reading more examples, I switched out my call to d_add() with d_instantiate() instead. Now the error messsage is gone, but the file system shows as being empty:
cholmes#felix:~/leardev/wombat$ ls -l mountedfs/
total 0
And now my syslog shows 5 accesses:
Apr 18 14:45:06 felix kernel: [22321.799115] womfs womfs_getattr(/) --> ffff9e95f95a04b0
Apr 18 14:45:06 felix kernel: [22321.800553] womfs womfs_getattr(/) --> ffff9e95f95a04b0
Apr 18 14:45:06 felix kernel: [22321.800554] womfs womfs_getattr(/) --> ffff9e95f95a04b0
Apr 18 14:45:06 felix kernel: [22322.406112] womfs womfs_getattr(/) --> ffff9e95f95a04b0
Apr 18 14:45:06 felix kernel: [22322.406341] womfs womfs_getattr(/) --> ffff9e95f95a04b0
And strace says:
open("mountedfs/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
getdents(3, /* 2 entries */, 32768) = 48
getdents(3, /* 0 entries */, 32768) = 0
close(3) = 0
At this point I'm completely confused and just trying semi-random things to see what will happen.
wombat$ ls -l mountedfs
ls: cannot access 'mountedfs/data': No such file or directory
total 0
d????????? ? ? ? ? ? data
ls output like this shows subdirectory name but not shows file mode, uid, gid or date/time. This can be when there was some correct syscall to return directory name; and where was another syscall to get additional information about inode, which had failed.
You should to check strace ls -l output to get exact syscall names and return values, but I think there was readdir/getdents to read data and there was failed stat (stat64, fstat, lstat, ...)
In your code you set fops for the inode (of directory type) in this fragment:
if (mode & S_IFDIR) {
ret->i_op = &simple_dir_inode_operations;
ret->i_fop = &simple_dir_operations;
But simple file/inode operations have not any method defined to provide stat implementation:
https://elixir.bootlin.com/linux/v4.4/source/fs/libfs.c#L189
const struct file_operations simple_dir_operations = {
.open = dcache_dir_open,
.release = dcache_dir_close,
.llseek = dcache_dir_lseek,
.read = generic_read_dir,
.iterate = dcache_readdir,
.fsync = noop_fsync,
};
const struct inode_operations simple_dir_inode_operations = {
.lookup = simple_lookup,
};
I think, getattr field of struct inode_operations is for implementing stat. Without implementation (with simple_inode_operations) stat syscall can't fill any useful data into struct kstat.
Some examples of getattr: fuse_getattr in fs/fuse/dir.c, http://pages.cpsc.ucalgary.ca/~crwth/programming/VFS/inodes.php, https://www.win.tue.nl/~aeb/linux/lk/lk-8.html ...
Documentation: https://www.kernel.org/doc/Documentation/filesystems/vfs.txt
struct inode_operations
int (*getattr) (const struct path *, struct kstat *, u32, unsigned int);
getattr: called by the VFS to get attributes of a file. This method
is called by stat(2) and related system calls.
ls sources https://github.com/coreutils/coreutils/blob/master/src/ls.c to prove that invalid return code of stat leads to ls: cannot access message
/* If true, the file listing format requires that stat be called on
each file. */
...
format_needs_stat = ... || format == long_format || ...
...
dereference = ... DEREF_NEVER;
...
gobble_file (char const *name, enum filetype type, ino_t inode,
...
if ( ... || format_needs_stat || ...) {
...
default: /* DEREF_NEVER */
err = lstat (full_name, &f->stat);
do_deref = false;
break;
}
if (err != 0)
{
/* Failure to stat a command line argument leads to
an exit status of 2. For other files, stat failure
provokes an exit status of 1. */
file_failure (command_line_arg,
_("cannot access %s"), full_name);
I HAVE
ReadBinFile () //Reads 2048 byte from bin file
{
transferlength = fileGetBinaryBlock(buffer, 2048 , fileHandle);
}
Now i want to read .hex and .mhx extension files i could not find the inbuilt function what is the option to do this in capl script.
*.hex or *.mhx format files can be read with help of OpenFileRead function.
the below piece of code will help to read the .hex file.
void readhexfile(void)
{
dword i;
char LineBuffer[0xFF];
int ByteCount;
char CountAscii[5];
int Data;
char Ascii[5];
dword readaccess = 0;
dword bufferpointer = 0;
byte buffer[1*1024*1024]; //1MB hex file size
if ((-1) != strstr_regex(FILENAME, ".hex")) //FILENAME-> Sysvariable
{
readaccess = OpenFileRead (FILENAME,0);
/* --> identified as IntelHEX-Input */
if (readaccess != 0)
{
/* read line until cr+lf */
while (fileGetString(LineBuffer, elcount(LineBuffer), readaccess) != 0)
{
// check for Record Type 00 (Data Record)
if (LineBuffer[0] == ':'
&& LineBuffer[7] == '0'
&& LineBuffer[8] == '0'
)
{
// extract ByteCount parameter
strncpy(CountAscii, "0x", elcount(CountAscii));
substr_cpy_off(CountAscii, 2, LineBuffer, 1, 2, elcount(CountAscii));
ByteCount = atol(CountAscii);
// extract Data parameter
for (i = 0; i < ByteCount; i++)
{
strncpy(Ascii, "0x", elcount(Ascii));
substr_cpy_off(Ascii, 2, LineBuffer, 9+i*2, 2, elcount(Ascii));
Data = atol(Ascii);
buffer[bufferpointer++] = Data;
};
}
}
fileClose(readaccess);
}
}
}
the Similar approach can be used for reading .mhx file format.
You can easily convert a *.hex/*.mhx file into a binary using srecord:
srec_cat file.hex −o file.bin −binary
Then you can read the resulting binary file with fileGetBinaryBlock().
If you need to process *.hex files dynamically, you can call srec_cat directly from your CAPL code using sysExec().
dword openFileRead(char filename[], dword mode);
This function opens the file named filename for the read access.
If mode=0 the file is opened in ASCII mode;
if mode=1 the file is opened in binary mode.
To open hexfile
dword HexFileHandle;
HexFileHandle = openFileRead(HEX_File_Path, 0);
The return value is the file handle that must be used for read operations.
If an error occurs, the return value is 0.
I have a program that reads from a .txt file
I use the cmd prompt to execute the program with the name of the text file to read from.
ex: program.exe myfile.txt
The problem is that sometimes it works, sometimes it doesn't.
The original file is 130KB and doesn't work.
If I copy/paste the contents, the file is 65KB and works.
If I copy/paste the file and rename it, it's 130KB and doesn't work.
Any ideas?
After more testing it shows that this is what makes it not work:
int main(int argc, char *argv[])
{
char *infile1
char tmp[1024] = { 0x0 };
FILE *in;
for (i = 1; i < argc; i++) /* Skip argv[0] (program name). */
{
if (strcmp(argv[i], "-sec") == 0) /* Process optional arguments. */
{
opt = 1; /* This is used as a boolean value. */
/*
* The last argument is argv[argc-1]. Make sure there are
* enough arguments.
*/
if (i + 1 <= argc - 1) /* There are enough arguments in argv. */
{
/*
* Increment 'i' twice so that you don't check these
* arguments the next time through the loop.
*/
i++;
optarg1 = atoi(argv[i]); /* Convert string to int. */
}
}
else /* not -sec */
{
if (infile1 == NULL) {
infile1 = argv[i];
}
else {
if (outfile == NULL) {
outfile = argv[i];
}
}
}
}
in = fopen(infile1, "r");
if (in == NULL)
{
fprintf(stderr, "Unable to open file %s: %s\n", infile1, strerror(errno));
exit(1);
}
while (fgets(tmp, sizeof(tmp), in) != 0)
{
fprintf(stderr, "string is %s.", tmp);
//Rest of code
}
}
Whether it works or not, the code inside the while loop gets executed.
When it works tmp actually has a value.
When it doesn't work tmp has no value.
EDIT:
Thanks to sneftel, we know what the problem is,
For me to use fgetws() instead of fgets(), I need tmp to be a wchar_t* instead of a char*.
Type casting seems to not work.
I tried changing the declaration of tmp to
wchar_t tmp[1024] = { 0x0 };
but I realized that tmp is a parameter in strtok() used elsewhere in my code.
I here is what I tried in that function:
//tmp is passed as the first parameter in parse()
void parse(wchar_t *record, char *delim, char arr[][MAXFLDSIZE], int *fldcnt)
{
if (*record != NULL)
{
char*p = strtok((char*)record, delim);
int fld = 0;
while (p) {
strcpy(arr[fld], p);
fld++;
p = strtok('\0', delim);
}
*fldcnt = fld;
}
else
{
fprintf(stderr, "string is null");
}
}
But typecasting to char* in strtok doesn't work either.
Now I'm looking for a way to just convert the file from UTF-16 to UTF-8 so tmp can be of type char*
I found this which looks like it can be useful but in the example it uses input from the user as UTF-16, how can that input be taken from the file instead?
http://www.cplusplus.com/reference/locale/codecvt/out/
It sounds an awful lot like the original file is UTF-16 encoded. When you copy/paste it in your text editor, you then save the result out as a new (default encoding) (ASCII or UTF-8) text file. Since a single character takes 2 bytes in a UTF-16-encode file but only 1 byte in a UTF-8-encoded file, that results in the file size being roughly halved when you save it out.
UTF-16 is fine, but you'll need to use Unicode-aware functions (that is, not fgets) to work with it. If you don't want to deal with all that Unicode jazz right now, and you don't actually have any non-ASCII characters to deal with in the file, just do the manual conversion (either with your copy/paste or with a command-line utility) before running your program.
Creating email addresses form names
You have given a file containing names of several persons. The file will have exactly one name is each line. You need to create email address ending with #bitmesra.ac.in from those names.
the rule for creating email address is defined below: A name will be expressed in the following form:
............
Let F(s) denote the first character of string s.
so, email id will be F(string 1)F(string 2)........._lastString#bitmesra.ac.in
Some names and their corresponding email id's are listed below as an example
Sachin Ramesh Tendulkar s_r_tendulkar#bitmesra.ac.in
Rahul S Dravid r_s_dravid#bitmesra.ac.in
You need to generate a grammer for this.
note: there may multiple spaces b/w names.
My Code is here
#include<cstdio>
#include<cstring>
#include<cctype>
int main()
{
char str1[100],str2[100];
char str3[] = "#bitmesra.ac.in";
while(gets(str1))
{
int index,k=0;
str2[k] = tolower(str1[0]);
for(int i=1;i<strlen(str1);i++)
{
if(str1[i]==' ')
{
index = i;
if(isalpha(str1[i+1]))
{
k++;
str2[k] = '_';
k++;
str2[k] = tolower(str1[i+1]);
}
}
}
index= index + 2;
for(int i=index;str1[i]!='\0';i++)
{
k++;
str2[k] = tolower(str1[i]);
}
str2[++k] = '\0';
strcat(str2,str3);
printf("%s\n",str2);
}
return 0;
}
How to write CFG Grammar For this.....
What about something like:
optnamelist: /* file can be empty */
| namelist /* do nothing */
namelist: nameseq NL /* process vector */
| namelist NL nameseq /* process vector */
nameseq: name /* create vector and add element 1 */
| nameseq name /* add element to vector */
The lexer should take care of white spaces (eat them). The NL token is a sequence of one or more newlines.
If you add names to the end of a vector, you'll have to process it backward.
Your code implies you're writing this in C. So you could use a fixed sized Vector, e.g.
#define MAX_NAMES 100 /* this will probably be enough :-) */
static int actpos;
static char *myVector[MAX_NAMES];
...
/* "create" vector */
memset(myVector, 0, MAX_NAMES * sizeof(char *));
actpos = 0;
...
/* add name to vector */
myVector[actpos] = strdup($1 /* or $2 */);
if (myVector[actpos) == NULL) ... /* out of memory */
actpos++;
if (actpos >= MAX_NAMES) ... /* name too long */
...
/* process vector */
for (i = actpos - 1; i > 0; --i) {
/* add myVector[i][0] to e-mail address */
free(myVector[i]);
}
/* add myVector[0] to e-mail address */
free(myVector[0]);