Limiting resource manager to exclusive open

Hi

I am fairly new to QNX and have a question regarding resource managers.

I am writing a resource manager for a hardware device. In our setup, it does
not make sense, that the file representing the hardware can be opened more
than once as the clients would be contesting over the same device. I realise
that the clients could use O_EXCL in their open call to avoid this but to be
on the safe side I want to enforce this in the resource manager.

What I have done is:

struct DevAttr;
#define IOFUNC_ATTR_T struct DevAttr

#include <sys/iofunc.h>
#include <sys/dispatch.h>
#include <sys/neutrino.h>

typedef struct DevAttr
{
iofunc_attr_t attr;
int count;
} DevAttr;

int IoOpen(resmgr_context_t *ctp, io_open_t *msg, DevAttr *attr, void
*extra )
{
int result = EINVAL;
if (attr != NULL)
{
if (attr->attr.count == 0)
{
printf(“Opened connection number %d\n”,attr->attr.count);
attr->attr.count++;
result = iofunc_open_default(ctp, msg, &attr->attr, extra);
}
else
{
printf(“Cannot open device - already opened %d
times\n”,attr->attr.count);
result = EBUSY;
}
}
return result;
}

int IoCloseOcb (resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb)
{
if (ocb->ocb.attr->attr.count > 0)
{
ocb->ocb.attr->attr.count–;
printf(“Decreased counter to %d\n”, ocb->ocb.attr->attr.count);
}
else
{
printf(“Count is %d\n”, ocb->ocb.attr->attr.count);
}
return (iofunc_close_ocb_default (ctp, reserved, &ocb->ocb));
}


This works like a charm, as soon as I have opened the file once, any try to
open it again fails with the error ‘file busy’.

However, unfortunately, it also means, that whenever I do an ls on the
directory which contains the file I get an error, indicating that my device
is busy as well.

What I would like is to disallow file open calls to the file for
reading/writing but to allow opening it for stat() etc, so it will show
properly in the directory. Is there any way to do this?

Thanks for any help
/urs

Urs Beeli <invalid@unknown.xx> wrote:

Hi

I am fairly new to QNX and have a question regarding resource managers.

I am writing a resource manager for a hardware device. In our setup, it does
not make sense, that the file representing the hardware can be opened more
than once as the clients would be contesting over the same device. I realise
that the clients could use O_EXCL in their open call to avoid this but to be
on the safe side I want to enforce this in the resource manager.

O_EXCL would not do what you want. O_EXCL is only meaningful if
combined with O_CREAT (and means to fail the open for creation if
the file already exists), but I’m pretty sure you’re not doing a file system,
where you’ll be dynamically creating entries.

What I have done is:

struct DevAttr;
#define IOFUNC_ATTR_T struct DevAttr

#include <sys/iofunc.h
#include <sys/dispatch.h
#include <sys/neutrino.h

typedef struct DevAttr
{
iofunc_attr_t attr;
int count;
} DevAttr;

int IoOpen(resmgr_context_t *ctp, io_open_t *msg, DevAttr *attr, void
*extra )
{
int result = EINVAL;
if (attr != NULL)
{
if (attr->attr.count == 0)
{
printf(“Opened connection number %d\n”,attr->attr.count);
attr->attr.count++;
result = iofunc_open_default(ctp, msg, &attr->attr, extra);
}
else
{
printf(“Cannot open device - already opened %d
times\n”,attr->attr.count);
result = EBUSY;
}
}
return result;
}

int IoCloseOcb (resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb)
{
if (ocb->ocb.attr->attr.count > 0)
{
ocb->ocb.attr->attr.count–;
printf(“Decreased counter to %d\n”, ocb->ocb.attr->attr.count);
}
else
{
printf(“Count is %d\n”, ocb->ocb.attr->attr.count);
}
return (iofunc_close_ocb_default (ctp, reserved, &ocb->ocb));
}



This works like a charm, as soon as I have opened the file once, any try to
open it again fails with the error ‘file busy’.

Note: you may have to take over io_dup and fail those as well – otherwise
someone might inherit an fd to your device. (Or, maybe, you will control
the code that talks to your device and not need to worry.)


However, unfortunately, it also means, that whenever I do an ls on the
directory which contains the file I get an error, indicating that my device
is busy as well.

I assume you mean either an “ls -l”, or you have ls aliased to something
other than the default (e.g. “ls -F” which requires stat information).

What I would like is to disallow file open calls to the file for
reading/writing but to allow opening it for stat() etc, so it will show
properly in the directory. Is there any way to do this?

In the completely general case, no.

There are a number of ways to go though.

Most people who open a device for collecting information (e.g. stty, ls)
will not open for write access, and if your main client does need/want
write access, you can allow only one open for write, and any number for
read. You’d need to check the ioflag entry in the connect message.
(Experiment with this; IIRC, the mapping from RD/WR/RD-WR to ioflag
is a bit odd.)

Another approach is to take advantage of the fact that most things that
want information about a file call stat() [they could call open(), fstat(),
close() – but don’t usually]. stat() generates what we call a “combine”
message, that is a request that includes headers for more than one
request, e.g. it will be something like an IO_OPEN followed by an IO_STAT.
If you allow through any open that has a combine flag set, but fail the
others you will be mostly ok. By that, I mean you will break the
open(), fstat(), close() case which is not generally used. To test
for this, you would check the subtype in the connect message, and
if it is either _IO_CONNECT_COMBINE or _IO_CONNECT_COMBINE_CLOSE, you
would allow it through (and, of course, not increment your access count).

Take a look at <sys/iomsg.h> for the structures and defines involved.

Hope this helps,

-David

David Gibbs
QNX Training Services
dagibbs@qnx.com

“David Gibbs” <dagibbs@qnx.com> wrote in message
news:deni07$a3p$1@inn.qnx.com

Urs Beeli <> invalid@unknown.xx> > wrote:

I am writing a resource manager for a hardware device. In our setup, it
does
not make sense, that the file representing the hardware can be opened
more
than once as the clients would be contesting over the same device. I
realise
that the clients could use O_EXCL in their open call to avoid this but to
be
on the safe side I want to enforce this in the resource manager.

O_EXCL would not do what you want. O_EXCL is only meaningful if
combined with O_CREAT (and means to fail the open for creation if
the file already exists), but I’m pretty sure you’re not doing a file
system,
where you’ll be dynamically creating entries.

thanks. it’s been a while since I programmed in a POSIX environment and
since
I was sure it wouldn’t suffice for what I wanted anyway I never bothered to
double check :slight_smile:

Note: you may have to take over io_dup and fail those as well – otherwise
someone might inherit an fd to your device. (Or, maybe, you will control
the code that talks to your device and not need to worry.)

thanks, didn’t think of that.

However, unfortunately, it also means, that whenever I do an ls on the
directory which contains the file I get an error, indicating that my
device
is busy as well.

I assume you mean either an “ls -l”, or you have ls aliased to something
other than the default (e.g. “ls -F” which requires stat information).

No. But I’m using a pattern to onyl display certain files.

alias ls

ls alias not found

File /dev/c0 not opened:

ls /dev

c0 c1 c_ctrl cd0
con1 con2 con3 cdn4
[snipped lots more]

(where c0, c1 and c_ctrl are my devices).

File /dev/c0 opened:
#ls /dev
c0 c1 c_ctrl cd0
con1 con2 con3 cdn4
[snipped lots more]

File /dev/c0 opened, filtering to /dev/c*:
#ls /dev/c*
ls: Resource busy (/dev/c0)
/dev/c1 /dev/con1 /dev/con4
/dev/c_ctrl /dev/con2 /dev/console
/dev/cd0 /dev/con3

What I would like is to disallow file open calls to the file for
reading/writing but to allow opening it for stat() etc, so it will show
properly in the directory. Is there any way to do this?

In the completely general case, no.

There are a number of ways to go though.

Most people who open a device for collecting information (e.g. stty, ls)
will not open for write access, and if your main client does need/want
write access, you can allow only one open for write, and any number for
read.

I can see how that might help. On the other hand, if I know there will
always
only be one consumer my read position/buffer handling can be a lot
simpler…

Another approach is to take advantage of the fact that most things that
want information about a file call stat() [they could call open(),
fstat(),
close() – but don’t usually]. stat() generates what we call a “combine”
message, that is a request that includes headers for more than one
request, e.g. it will be something like an IO_OPEN followed by an IO_STAT.
If you allow through any open that has a combine flag set, but fail the
others you will be mostly ok. By that, I mean you will break the
open(), fstat(), close() case which is not generally used. To test
for this, you would check the subtype in the connect message, and
if it is either _IO_CONNECT_COMBINE or _IO_CONNECT_COMBINE_CLOSE, you
would allow it through (and, of course, not increment your access count).

Great. That sounds like it would at best fix my problem (combined message
used)
and at worst behave as today (ls fails if file is open).

I’ll look into this. Thanks a lot for your hint.

/urs

Urs Beeli <invalid@unknown.xx> wrote:

“David Gibbs” <> dagibbs@qnx.com> > wrote in message

File /dev/c0 opened, filtering to /dev/c*:
#ls /dev/c*
ls: Resource busy (/dev/c0)
/dev/c1 /dev/con1 /dev/con4
/dev/c_ctrl /dev/con2 /dev/console
/dev/cd0 /dev/con3

Ah, that is a different way of doing things. What is happening is
not that ls is doing a listing of the directory (it is actually the
shell doing the listing of the directory), it is ls is given a list
of specific files to give contents on, and since it is pointed at
each specific file, it is doing a stat() on each one individually,
to determine how to treat that file.


I can see how that might help. On the other hand, if I know there will
always
only be one consumer my read position/buffer handling can be a lot
simpler…

Generally, ocb->offset is used for read/write position in the file.
Since this is per-open, and handed to you on each read/write call,
it makes handling of multiple readers/writers not too difficult.

Also, you can extend the OCB if you want a per-open buffer, making
the handling of multiple readers/writes with a per-open buffer for
each not too difficult to handle, either.

And, you can provide ocb_alloc/ocb_free routines to properly allocate
and cleanup this memory should you choose. They are provided through
the mount structure which the attribute structure can point to.

Great. That sounds like it would at best fix my problem (combined message
used)
and at worst behave as today (ls fails if file is open).

I’ll look into this. Thanks a lot for your hint.

You’re welcome.

-David

David Gibbs
QNX Training Services
dagibbs@qnx.com

“David Gibbs” <dagibbs@qnx.com> wrote:

Urs Beeli <> invalid@unknown.xx> > wrote:
“David Gibbs” <> dagibbs@qnx.com> > wrote:

#ls /dev/c*
ls: Resource busy (/dev/c0)
/dev/c1 /dev/con1 /dev/con4

Ah, that is a different way of doing things. What is happening is
not that ls is doing a listing of the directory (it is actually the
shell doing the listing of the directory), it is ls is given a list
of specific files to give contents on, and since it is pointed at
each specific file, it is doing a stat() on each one individually,
to determine how to treat that file.

now that you spell it out I realised it as well. thanks.

I can see how that might help. On the other hand, if I know there will
always only be one consumer my read position/buffer handling can be
a lot simpler…

Generally, ocb->offset is used for read/write position in the file.
Since this is per-open, and handed to you on each read/write call,
it makes handling of multiple readers/writers not too difficult.

Also, you can extend the OCB if you want a per-open buffer, making
the handling of multiple readers/writes with a per-open buffer for
each not too difficult to handle, either.

I am aware of that. However, as I only have a very small hardware buffer
I need to get the data out of there quickly. I figured copying the data into
a ring buffer is the quickest.

If I allow parallel reads I either need a ring buffer set for each OCB,
meaning
n copies for n open connections (and n times the memory need) or I just
have one ring buffer with n read ‘pointers’ in the OCB pointing into the
ringbuffer. This minimises copying and memory need but makes the managing
of the ring buffer much more complex (i.e. what do I do if one connection
keeps up with the incoming data, nicely emptying the ring buffer, but a
second
connection just does not come fetch the data. at one point, the ring buffer
will
be full of ‘stale’ data from conn1’s point of view, because it is still
‘new’ data
for conn2.

As our application does not really need parallel reading, I hope to avoid
opening
that box of worms by not having one ring buffer with n pointers. I guess I
might
just add one ring buffer per OCB and as long as only one connection is
active,
there will be no overhead compared to one global buffer. Should we then ever
need parallel connections, all is ready and the overhead (memory and
copying)
only applies when needed.

Is this how such things are usually dealt with? Or am I missing the obvious
solution?
I have considered a dynamically grown buffer but somehow that does not
appeal
to me in an interrupt handler (or even in a pulse handler which is triggered
by an
interrupt).

Anyway, thanks for your input David.

Cheers
/urs

Urs Beeli wrote:

Is this how such things are usually dealt with? Or am I missing the obvious
solution?

The usual behaviour of things like pipes and character devices is that
they don’t try to disallow multiple readers, but any portion of incoming
data goes to just one reader. If some data arrives while readers are
waiting, you just pick an arbitrary reader and give the data to him.
The circular buffer is associated with the device, not with the OCB, and
the OCB doesn’t need to keep track of where in the buffer it’s reading
from because all the reads get their data from the current tail of the
buffer.

BTW Note that disallowing multiple opens does not prevent the
possibility of multiple concurrent reads – in a multithreaded process,
any number of threads can read from the same fd at the same time.

“David Gibbs” <dagibbs@qnx.com> wrote:

Urs Beeli <> invalid@unknown.xx> > wrote:

Hi David

Another approach is to take advantage of the fact that most things that
want information about a file call stat() [they could call open(),
fstat(),
close() – but don’t usually]. stat() generates what we call a “combine”
message, that is a request that includes headers for more than one
request, e.g. it will be something like an IO_OPEN followed by an IO_STAT.
If you allow through any open that has a combine flag set, but fail the
others you will be mostly ok. By that, I mean you will break the
open(), fstat(), close() case which is not generally used. To test
for this, you would check the subtype in the connect message, and
if it is either _IO_CONNECT_COMBINE or _IO_CONNECT_COMBINE_CLOSE, you
would allow it through (and, of course, not increment your access count).

I tried this today and it works like a charm. I realise that a separate
open, fstat and close
will break on this but before even a combined stat broke, so this is a
definite improvement.

Thanks for your hint.

/urs