Q re sysopen() and more...

M

Michele Dondi

I apologize if the subject is not really appopriate to this post, but
I really couldn't think of a better one...

To come to the point, I have a process that (partially) daemonizes and
uses a mechanism to prevent having more than one instance running on
any given machine.

As of it is now, I must say that it has been working perfectly for
quite a while and I've not had any problem with it. Basically the key
code snippet is as follows:

my $lockfile = "$lockdir/$pfx-$host";
sysopen my $lock, $lockfile, O_CREAT | O_EXCL
or die "$0 already running on $host\n";
my $havelock = 1;
# ...

However (also) for learning purposes, I've been thinking of improving
it to cope with the unlikely possibility that a badly crashed machine
prevented my signal handlers or C<END> block to take care of lockfile
removal.

So the additional steps that I'm taking into account are:

(i) writing the pid of the running instance into the lockfile and
check for its existence,
(ii) once (i) is done, then also check that the process corresponding
to the stored pid corresponds to a program called like $0.

Now, the questions are: would (i) be a good idea? Would (ii) be a good
idea too?

Also, (and on a Perl-related basis, finally!) if the answers to the
above questions are positive, then when I open a file with sysopen(),
can/should I subsequently use the "standard" IO operators (i.e. those
I'd use with an open()ed file) and functions to read and write to it?

As for the point (ii) above, since the program is to be run on Linux
anyway, it would seem sensible to me to read the relevant info from
/proc myself (don't know if it could be doable under other UNICES): is
it recommended to use some module or ad hoc code instead?

Last, since I'm not much confident with this kind of programming,
could you please be so kind and post some explicit minimal code
snippet?


TIA,
Michele
 
B

Ben Morrow

Quoth Michele Dondi said:
I apologize if the subject is not really appopriate to this post, but
I really couldn't think of a better one...

How's that? :)
To come to the point, I have a process that (partially) daemonizes and
uses a mechanism to prevent having more than one instance running on
any given machine.

As of it is now, I must say that it has been working perfectly for
quite a while and I've not had any problem with it. Basically the key
code snippet is as follows:

my $lockfile = "$lockdir/$pfx-$host";
sysopen my $lock, $lockfile, O_CREAT | O_EXCL
or die "$0 already running on $host\n";
my $havelock = 1;
# ...

However (also) for learning purposes, I've been thinking of improving
it to cope with the unlikely possibility that a badly crashed machine
prevented my signal handlers or C<END> block to take care of lockfile
removal.

What I have done in the past in this situation is

sysopen my $LOCK, $lockfile, WR_ONLY | O_CREAT, 0600
or die "can't open lockfile: $!";
flock $LOCK, LOCK_EX | LOCK_NB
or die "I'm already running!";

If you wanted to be really careful you could check that $! ==
EWOULDBLOCK before concluding you were already running.

The advantage of this, obviously, is that the kernel will take care of
releasing the lock however the process terminates (and if the whole
machine goes down the lock's *certainly* gone :) ).
So the additional steps that I'm taking into account are:

(i) writing the pid of the running instance into the lockfile and
check for its existence,
(ii) once (i) is done, then also check that the process corresponding
to the stored pid corresponds to a program called like $0.

Now, the questions are: would (i) be a good idea?

Yes. It would be anyway, so you can find it and kill it easily if it
gets jammed.
Would (ii) be a good idea too?

Not really. The risk that either it will be a process that matches that
shouldn't (not *that* likely, but rather more so if someone's being
malicious) or a process that doesn't match that should (consider
starting the program as 'prog', '~/bin/prog', 'perl ../bin/prog'; I have
no idea what the kernel puts in argv (although I would hope the first
two were ('/usr/bin/perl', '/dev/fd/3') or in /proc/$$/cmdline for each
of these cases, and I suspect there is no well-defined answer).

If you make the pidfile secure, then you can simply check if that pid is
still running (kill 0 => $pid); if you're worried about pid wraparound
then you really need to get some positive check that the process
concerned is one of yours, such as (in the extreme case) connecting to a
Unix-domain socket the other copy is listening on and performing a
secure authentication exchange.
Also, (and on a Perl-related basis, finally!) if the answers to the
above questions are positive, then when I open a file with sysopen(),
can/should I subsequently use the "standard" IO operators (i.e. those
I'd use with an open()ed file) and functions to read and write to it?

Yes. Perl's sysopen does an open(2) and then a fdopen(3) (or the PerlIO
equivalent), so the FH you get back is exactly equivalent to one from
open.
As for the point (ii) above, since the program is to be run on Linux
anyway, it would seem sensible to me to read the relevant info from
/proc myself (don't know if it could be doable under other UNICES

Plan9? :)
): is it recommended to use some module or ad hoc code instead?

I'd probably recommend using a module on principle (the Proc::
hierachy); in practice, I'd probably just grep it out of
/proc/pid/cmdline :)
Last, since I'm not much confident with this kind of programming,
could you please be so kind and post some explicit minimal code
snippet?

Oh, come now, the advantage of /proc is that it's just like ordinary
files; and I'm quite sure you can write a program to read a pid from a
file, open another file based on that, and check if it matches
/^\Q$^X\E\s+\Q$0\E(?:\s|$)/ :).

If you want to use a module, then I know no more than that they exist,
I'm afraid...

Ben
 
A

Anno Siegel

Ben Morrow said:
Quoth Michele Dondi <[email protected]>:

[using file locks for mutual exclusion]
Yes. It would be anyway, so you can find it and kill it easily if it
gets jammed.


Not really. The risk that either it will be a process that matches that
shouldn't (not *that* likely, but rather more so if someone's being
malicious) or a process that doesn't match that should (consider
starting the program as 'prog', '~/bin/prog', 'perl ../bin/prog'; I have
no idea what the kernel puts in argv (although I would hope the first
two were ('/usr/bin/perl', '/dev/fd/3') or in /proc/$$/cmdline for each
of these cases, and I suspect there is no well-defined answer).

If you make the pidfile secure, then you can simply check if that pid is
still running (kill 0 => $pid); if you're worried about pid wraparound
then you really need to get some positive check that the process
concerned is one of yours, such as (in the extreme case) connecting to a
Unix-domain socket the other copy is listening on and performing a
secure authentication exchange.

That should only be necessary if the lock-able process must run under
various user-id's. If it is always run under the same id, the lock
file permissions should only allow the owner to open the file. Also,
(perhaps needless to say) the lock file must be specific to that process,
in particular, no other program (of yours) should ever lock that file.
Then, unless the uid is compromised, if there is a lock, it *must*
belong to the one process that can hold it.

Anno
 
M

Michele Dondi

How's that? :)

Lack of fantasy? ;-)

Also, since I had apologized, you may have continued that thread
anyway, ot at least include in the subject line an indication of the
change, a la '[was: "something else"]'.

I say so because I was inadvertently skipping over this thread... (of
course the subject eventually did catch my attention!)
What I have done in the past in this situation is

sysopen my $LOCK, $lockfile, WR_ONLY | O_CREAT, 0600
or die "can't open lockfile: $!";
flock $LOCK, LOCK_EX | LOCK_NB
or die "I'm already running!";

Well, one reason I hadn't used flock() in the first place is that I'm
on nfs and I know it not to be an atomic operation there. Also, this
is to be called sporadically (say when I login, e.g. from my
~/.bash_profile), so that doesn't seem to be a problem.
If you wanted to be really careful you could check that $! ==
EWOULDBLOCK before concluding you were already running.

Good to know, I didn't know about this...
The advantage of this, obviously, is that the kernel will take care of
releasing the lock however the process terminates (and if the whole
machine goes down the lock's *certainly* gone :) ).

I see...
Yes. It would be anyway, so you can find it and kill it easily if it
gets jammed.

Fine! This will be easily accomplished.
Not really. The risk that either it will be a process that matches that
shouldn't (not *that* likely, but rather more so if someone's being
malicious) or a process that doesn't match that should (consider
starting the program as 'prog', '~/bin/prog', 'perl ../bin/prog'; I have

I see your point. In this particular case the program is started from
~/bin so there's be no problem of this kind. However your
considerations are definitely worth taking into account in a (not too
much) hypothetical general case.
If you make the pidfile secure, then you can simply check if that pid is
still running (kill 0 => $pid); if you're worried about pid wraparound
then you really need to get some positive check that the process
concerned is one of yours, such as (in the extreme case) connecting to a
Unix-domain socket the other copy is listening on and performing a
secure authentication exchange.

Huh?!? No, it's not really the case for *this* particular program! :)

<OT>
What is the English idiom, provided one exists, but I strongly suspect
so, for "(ab)using an exaggerate tool for a task that doesn't really
deserve it?" In Italian there's an expression that could be roughly
translated as "to use a bazooka to shot a robin".
Yes. Perl's sysopen does an open(2) and then a fdopen(3) (or the PerlIO
equivalent), so the FH you get back is exactly equivalent to one from
open.

OK! This is (most of) all I wanted to know...

BTW: I don't know anything about Plan9, apart having heard its name
before. What do you mean? I suppose it {does,did}n't have anything
like /proc, {does,did} it?
I'd probably recommend using a module on principle (the Proc::
hierachy); in practice, I'd probably just grep it out of
/proc/pid/cmdline :)

That's what I'm about to do too, at least for experimenting.
Oh, come now, the advantage of /proc is that it's just like ordinary
files; and I'm quite sure you can write a program to read a pid from a

I meant on the whole subject (i.e. say, "(i)+(ii)"), but...
file, open another file based on that, and check if it matches
/^\Q$^X\E\s+\Q$0\E(?:\s|$)/ :).

....you're right: I realized that I can do all this myself!


TY very much,
Michele
 
M

Michele Dondi

That should only be necessary if the lock-able process must run under
various user-id's. If it is always run under the same id, the lock
file permissions should only allow the owner to open the file. Also,
(perhaps needless to say) the lock file must be specific to that process,
in particular, no other program (of yours) should ever lock that file.
Then, unless the uid is compromised, if there is a lock, it *must*
belong to the one process that can hold it.

Sorry if I'm being overly dumb, but when you say "lock" do you mean
lock() or simply a suitably sysopen()ed lockfile or both? (Intuition
suggests me the latter, but I may well be wrong!)

Also, as I said in the other post, in this particular case the program
is started ~/bin/. I can resonably imagine writing "similar" scripts
to be installed "system-wide", but then I'd probably allow one
instance per user per machine, so I'd simply stick with either using a
per HOME lockfile or a /tmp per-user lockfile (as I've seen other
programs doing).


Michele
 
A

Anno Siegel

Michele Dondi said:
Sorry if I'm being overly dumb, but when you say "lock" do you mean
lock() or simply a suitably sysopen()ed lockfile or both? (Intuition
suggests me the latter, but I may well be wrong!)

I definitely mean a file that is exclusively locked by the running
process. The file never goes out of existence. If you will, it
(not its contents) is part of the program as much as the executable.

Creation of a "flag file" at startup has the one disadvantage that
there is no safe way to remove the lock when the process ends (there's
always "kill -9" to break a scheme).
Also, as I said in the other post, in this particular case the program
is started ~/bin/. I can resonably imagine writing "similar" scripts
to be installed "system-wide", but then I'd probably allow one
instance per user per machine, so I'd simply stick with either using a
per HOME lockfile or a /tmp per-user lockfile (as I've seen other
programs doing).

If your aim is to protect users from accidentally running the
program twice, that should be fine, provided you have a procedure
to remove all outstanding lock files at system startup.

If you are fighting a mix of ignorant and malicious users, you'll
have to do more.

Anno
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top