|
Home > Archive > Unix Programming > July 2005 > How to check for already running program?
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
How to check for already running program?
|
|
| Nick Sinclair 2005-07-07, 2:48 am |
| <QUOTE>
I don't know of a reliable method within the scope of ANSI C, the topic of
this newsgroup. On Unix systems, file locking can be used for mutual
exclusion - you should ask about this in a platform-specific newsgroup,
perhaps news:comp.unix.programmer.
</QUOTE>
Hi all,
I'm new to C.
I have successfully written a small C program that acts as a "wrapper"
around cgi scripts. These scripts perform admin tasks such as backing up
etc. Obviously, The need for the "wrapper" was to get the scripts to run
with "root" privileges. So far so good. I simply create a "URL shortcut"
that some staff can double-click on to initiate the process.
However, because I am creating these automated tasks for management staff
of a company, I want to make sure that they don't go laucnching these tasks
twice or similar (2 or more similtaneous backup processes to the same drive
at the same time is not nice).
I thought the bast way to address this was just to grab a "process listing"
and use the available "regcomp and regexec" functions to check that there
is not already and instance of the designated script running. If so, baulk
and exit with a message.
The thing I don't understand; is that when testing this, any attempts to
run additional instances of the script using the c program don't work,
because the "new instance" will wait until the old process finishes, and
then start (which is not what I want).
BTW: I call it like this
http://some.internal.address/cgi-bi...p?fw_backup.cgi
Could someone please point me in the right direction?
TIA
/* This program was created to get around the problem */
/* of having to do tasks that require root privileges */
/* but aren't possible when run as a simple cgi script. */
/* Set this binary to run suid and make sure it's owned */
/* by root, and you're good to go. */
/* Nick Sinclair 2005 GPL it's all yours. */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <fcntl.h>
void main(int argc, char *argv[]){
/* Declare some variables */
FILE *cgi_file;
pid_t pid;
char *cgi_filename = argv[1];
/* Let's do some sanity checks on the cgi script name */
/* Argument supplied at all? */
if (argc==1)
{ printf("\nNo CGI script name supplied - Freaking out and
exiting.\n\n");
exit(172);
}
/* Too many arguments? */
if (argc>2)
{ printf("\nToo many arguments supplied - Freaking out and
exiting.\n\n");
exit(172);
}
/* Does the file exist at all? */
cgi_file = fopen(cgi_filename, "r");
if (cgi_file==NULL)
{ printf("\nSupplied file cannot be read from or doesn't exist\n");
printf(" - Freaking out and exiting -\n\n");
exit(127);
}
/* Is the script already running - doh! */
char cgi_lockfile_path[30];
strcpy( cgi_lockfile_path, "/var/lock/subsys/LOCK-" );
strcat( cgi_lockfile_path, cgi_filename );
int fd = open(cgi_lockfile_path,O_WRONLY|O_CREAT,
0600);
int rc = flock(fd,LOCK_EX|LOCK_NB);
if (rc==-1)
{
printf("Script already running! - Freaking out and exiting.\n\n");
exit(172);
}
/* Set UID to 0 (root) */
setuid(0);
/* Now run the actual ".cgi" file */
pid=getpid();
execl("/usr/bin/bash", "bash", cgi_filename, (char *)0);
}
| |
| Nick Sinclair 2005-07-07, 2:48 am |
| Let's see what Nick Sinclair <null_device@NOFRILLS.ssl-mail.com> has up
their dress.
Sorry, I have posted the revised C code.
Hoever, the question still stands, as the program does not give the error
and exit(172); but waits until the previous instance has finished before
running.
Why?
Any reply is appreciated. TIA
| |
| Maxim Yegorushkin 2005-07-07, 2:48 am |
| On Thu, 07 Jul 2005 11:45:09 +0400, Nick Sinclair
<null_device@NOFRILLS.ssl-mail.com> wrote:
> Let's see what Nick Sinclair <null_device@NOFRILLS.ssl-mail.com> has up
> their dress.
> Sorry, I have posted the revised C code.
> Hoever, the question still stands, as the program does not give the error
> and exit(172); but waits until the previous instance has finished before
> running.
Use a nonblocking flock.
fd = ::open(name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP |
S_IROTH);
if(-1 == fd)
{
::perror(name);
::exit(EXIT_FAILURE);
}
::fcntl(fd, F_SETFD, FD_CLOEXEC);
if(::flock(fd, LOCK_EX | LOCK_NB)) // <---- note LOCK_NB
{
::perror(name);
::exit(EXIT_FAILURE);
}
--
Maxim Yegorushkin
<firstname.lastname@gmail.com>
| |
| Maxim Yegorushkin 2005-07-07, 7:55 am |
| On Thu, 07 Jul 2005 12:11:13 +0400, Maxim Yegorushkin
<firstname.lastname@gmail.com> wrote:
[]
> Use a nonblocking flock.
Sorry, I've just happened to notice that you already use it. Anyway, the
code I posted is taken from a running system.
Are you sure that your code blocks on flock() rather than elsewhere?
--
Maxim Yegorushkin
<firstname.lastname@gmail.com>
| |
| Rich Teer 2005-07-07, 5:54 pm |
| On Thu, 7 Jul 2005, Nick Sinclair wrote:
> Could someone please point me in the right direction?
I'll try...
> void main(int argc, char *argv[]){
Main is NEVER void for C programs. It is always an int.
> int fd = open(cgi_lockfile_path,O_WRONLY|O_CREAT,
0600);
> int rc = flock(fd,LOCK_EX|LOCK_NB);
I think decalring variables and using them like this (halfway
through the function) is messy. Also, you don't check the
return code from open(). Checking for errors might shed some
more light on your problem.
> pid=getpid();
> execl("/usr/bin/bash", "bash", cgi_filename, (char *)0);
I'd use "NULL" rtaher than "(char *)0" here.
HTH,
--
Rich Teer, SCNA, SCSA, OpenSolaris CAB member
President,
Rite Online Inc.
Voice: +1 (250) 979-1638
URL: http://www.rite-group.com/rich
| |
| Ari Lukumies 2005-07-07, 5:54 pm |
| Rich Teer wrote:
> On Thu, 7 Jul 2005, Nick Sinclair wrote:
>
>
> I think decalring variables and using them like this (halfway
> through the function) is messy.
Not to mention that declaring variables inside a function is
restricted to beginning of a block only; this kind of declaring
variables in between statements is not C, it's C++.
-atl-
--
A multiverse is figments of its own creations
| |
| Henry Townsend 2005-07-07, 5:54 pm |
| Ari Lukumies wrote:
> Rich Teer wrote:
>
>
>
> Not to mention that declaring variables inside a function is restricted
> to beginning of a block only; this kind of declaring variables in
> between statements is not C, it's C++.
I believe this is in C99. It may not be smart C, but it is C.
--
Henry Townsend
| |
|
| Ari Lukumies pulled a bright blue crayon out of the box and scribbled this
in news:U0cze.2684$Wj1.1314@reader1.news.jippii.net:
> Rich Teer wrote:
>
> Not to mention that declaring variables inside a function is
> restricted to beginning of a block only; this kind of declaring
> variables in between statements is not C, it's C++.
It is compliant with the latest C standard. As to whether it is a good or
bad idea...? I prefer it to the old style, but then I do more work in C++
than C.
R.
--
Go not to Usenet for counsel, for they will say both yes and no.
| |
| Ulrich Hobelmann 2005-07-07, 5:54 pm |
| Henry Townsend wrote:
>
>
> I believe this is in C99. It may not be smart C, but it is C.
True. But C that won't compile with gcc 2.95 for instance. I
don't know if people still use that, but I guess some have to.
Cleaner style may also argue for declaring stuff at the beginning
of a block.
--
By claiming a patent [...], I'm saying that you are not permitted
to use your own knowledge to further your ends. By what right?
Roderick T. Long
| |
| David Schwartz 2005-07-07, 8:49 pm |
|
"Ulrich Hobelmann" <u.hobelmann@web.de> wrote in message
news:3j5n95FohbdqU1@individual.net...
> True. But C that won't compile with gcc 2.95 for instance. I don't know
> if people still use that, but I guess some have to. Cleaner style may also
> argue for declaring stuff at the beginning of a block.
Cleaner stlye is to declare stuff as close as possible to where you are
using it and to arrange for it to go out of scope after its last use.
DS
| |
|
| David Schwartz pulled a bright blue crayon out of the box and scribbled
this in news:dakdsr$1me$1@nntp.webmaster.com:
>
> "Ulrich Hobelmann" <u.hobelmann@web.de> wrote in message
> news:3j5n95FohbdqU1@individual.net...
>
>
> Cleaner stlye is to declare stuff as close as possible to where
> you are
> using it and to arrange for it to go out of scope after its last use.
<sigh!>
....religious wars....
--
Go not to Usenet for counsel, for they will say both yes and no.
| |
| Nick Sinclair 2005-07-08, 2:48 am |
| Let's see what Nick Sinclair <null_device@NOFRILLS.ssl-mail.com> has up
their dress.
>[..]
Hey,
notwithstanding the priceless guidance on programming in C, no-one actually
answered my question. I don't understand the *NIX paradigm of process /in
depth/. I can write shell scripts until the cows come home though.
Q. Why does my program wait for a previous instance of itself to finish,
before proceeding?
Again, thanks for all the feedback. It's appreciated, and I will summarily
re-write my program.
| |
| Ulrich Hobelmann 2005-07-08, 2:48 am |
| Ross wrote:
Well, I do declare stuff inside while- or for-loops, so they go
out of scope after use. I only think it's nicer to have the
couple variables you need declared at the beginning of that block,
instead of having java-style statement, then declaration, then
statement, then declaration -- butt ugly.
In practice I can even mix declarations and statements by using
meaningful initializer expressions at the beginning. Only
sometimes I need a variable after for instance an IF that returns
early.
And any block that's longer than maybe 5 lines is braindead
anyway. Yes, I often break that rule ;)
[vbcol=seagreen]
>
> <sigh!>
>
> ...religious wars....
or matters of taste and backwards compatibility.
--
By claiming a patent [...], I'm saying that you are not permitted
to use your own knowledge to further your ends. By what right?
Roderick T. Long
| |
| Pascal Bourguignon 2005-07-08, 7:48 am |
| Nick Sinclair <null_device@NOFRILLS.ssl-mail.com> writes:
> Let's see what Nick Sinclair <null_device@NOFRILLS.ssl-mail.com> has up
> their dress.
>
>
> Hey,
> notwithstanding the priceless guidance on programming in C, no-one actually
> answered my question. I don't understand the *NIX paradigm of process /in
> depth/. I can write shell scripts until the cows come home though.
>
> Q. Why does my program wait for a previous instance of itself to finish,
> before proceeding?
>
> Again, thanks for all the feedback. It's appreciated, and I will summarily
> re-write my program.
At home your program works perfectly. Well, once I substituted
"/tmp/LOCK-" for "/var/lock/subsys/LOCK-" since I don't have write
access to that later directory...
--
__Pascal Bourguignon__ http://www.informatimago.com/
-----BEGIN GEEK CODE BLOCK-----
Version: 3.12
GCS d? s++:++ a+ C+++ UL++++ P--- L+++ E+++ W++ N+++ o-- K- w---
O- M++ V PS PE++ Y++ PGP t+ 5+ X++ R !tv b+++ DI++++ D++
G e+++ h+ r-- z?
------END GEEK CODE BLOCK------
| |
| David Schwartz 2005-07-08, 7:48 am |
|
"Ulrich Hobelmann" <u.hobelmann@web.de> wrote in message
news:3j6qd6Fok0q8U1@individual.net...
> Well, I do declare stuff inside while- or for-loops, so they go out of
> scope after use. I only think it's nicer to have the couple variables you
> need declared at the beginning of that block, instead of having java-style
> statement, then declaration, then statement, then declaration -- butt
> ugly.
We might actually be in violent agreement. I also can't stand the type
of alternation you are talking about. We might just have different ideas of
what "close to" means. ;)
DS
| |
| Ralf Fassel 2005-07-08, 5:53 pm |
| * Nick Sinclair <null_device@NOFRILLS.ssl-mail.com>
| BTW: I call it like this
| http://some.internal.address/cgi-bi...p?fw_backup.cgi
--<snip-snip>--
| char *cgi_filename = argv[1];
|
| /* Let's do some sanity checks on the cgi script name */
|
| /* Argument supplied at all? */
|
| if (argc==1)
Dereferencing argv[1] before checking that argc > 1. No good idea in
a suid program.
| /* Does the file exist at all? */
|
| cgi_file = fopen(cgi_filename, "r");
The execl() can nevertheless fail later (the file may have been gone
in the meantime). I'd simply skip this check in a suid script and let
bash decide whether it can find the file.
| /* Is the script already running - doh! */
|
| char cgi_lockfile_path[30];
|
| strcpy( cgi_lockfile_path, "/var/lock/subsys/LOCK-" );
| strcat( cgi_lockfile_path, cgi_filename );
Fixed buffer len on arbitrary user input in a suid program, recipe for
desaster. What happens if someone types a longer CGI name than 8
characters?
FWIW, even your original file name already overflows that buffer...
/var/lock/subsys/LOCK-fw_backup.cgi is 35 chars. Most probably every
invocation of your program opens a different file due to the buffer
overflow here, thus the flock() always succceeds.
You need to strlen/malloc here if you use C, or use a std::string in
C++.
| int fd = open(cgi_lockfile_path,O_WRONLY|O_CREAT,
0600);
Check return value.
| int rc = flock(fd,LOCK_EX|LOCK_NB);
Should work and does work in a small test program.
| /* Set UID to 0 (root) */
|
| setuid(0);
Check return value.
| execl("/usr/bin/bash", "bash", cgi_filename, (char *)0);
Check return value.
HTH
R'
| |
| Ralf Fassel 2005-07-08, 5:53 pm |
| * Pascal Bourguignon <pjb@informatimago.com>
| At home your program works perfectly. Well, once I substituted
| "/tmp/LOCK-" for "/var/lock/subsys/LOCK-" since I don't have write
| access to that later directory...
Which makes the filename fit in the 30 character buffer.
R'
| |
|
| Ulrich Hobelmann pulled a bright blue crayon out of the box and scribbled
this in news:3j6qd6Fok0q8U1@individual.net:
> Ross wrote:
>
> Well, I do declare stuff inside while- or for-loops, so they go
> out of scope after use. I only think it's nicer to have the
> couple variables you need declared at the beginning of that block,
> instead of having java-style statement, then declaration, then
> statement, then declaration -- butt ugly.
In C++ and java, this style is a necessity in that object creation and
inititalization are often sequence dependant:
Object1 obj1;
obj1.process_some_stuff();
Object2 obj2(obj1);
When dealing with Plain Old Data (POD), then declarations can easily take
place at the top of blocks.
The change in the C rules will, I'm sure, be used properly in some cases
and abused in others: it is a function, not of the language features, but
of the skill of the programmer in their use.
> In practice I can even mix declarations and statements by using
> meaningful initializer expressions at the beginning. Only
> sometimes I need a variable after for instance an IF that returns
> early.
>
> And any block that's longer than maybe 5 lines is braindead
> anyway. Yes, I often break that rule ;)
....a perfect world....
>
> or matters of taste and backwards compatibility.
Flexibility is the key. I prefer one style but can change if required.
--
Go not to Usenet for counsel, for they will say both yes and no.
| |
| Pascal Bourguignon 2005-07-08, 5:53 pm |
| Ralf Fassel <ralfixx@gmx.de> writes:
> * Pascal Bourguignon <pjb@informatimago.com>
> | At home your program works perfectly. Well, once I substituted
> | "/tmp/LOCK-" for "/var/lock/subsys/LOCK-" since I don't have write
> | access to that later directory...
>
> Which makes the filename fit in the 30 character buffer.
;-)
--
__Pascal Bourguignon__ http://www.informatimago.com/
Nobody can fix the economy. Nobody can be trusted with their finger
on the button. Nobody's perfect. VOTE FOR NOBODY.
| |
| Floyd L. Davidson 2005-07-08, 5:53 pm |
| Ralf Fassel <ralfixx@gmx.de> wrote:
>* Nick Sinclair <null_device@NOFRILLS.ssl-mail.com>
>| char *cgi_filename = argv[1];
>|
>| /* Let's do some sanity checks on the cgi script name */
>|
>| /* Argument supplied at all? */
>|
>| if (argc==1)
>
>Dereferencing argv[1] before checking that argc > 1. No good idea in
>a suid program.
For argv[1] it is guaranteed to either point to a valid string,
or to a null pointer. Dereferencing the null pointer would be a
problem, or dereferencing any argv[2], but he didn't do that.
--
Floyd L. Davidson <http://web.newsguy.com/floyd_davidson>
Ukpeagvik (Barrow, Alaska) floyd@barrow.com
| |
| Ulrich Hobelmann 2005-07-08, 5:53 pm |
| Floyd L. Davidson wrote:
> Ralf Fassel <ralfixx@gmx.de> wrote:
>
>
>
> For argv[1] it is guaranteed to either point to a valid string,
> or to a null pointer. Dereferencing the null pointer would be a
> problem, or dereferencing any argv[2], but he didn't do that.
Is it? I think argv[0], i.e. the program name, is guaranteed to
exist, but with additional parameters I'm not too sure.
At least it's very bad style. I'm lazy, but there's always enough
time to put a little assert(argc == bla) in there 
--
By claiming a patent [...], I'm saying that you are not permitted
to use your own knowledge to further your ends. By what right?
Roderick T. Long
| |
| Rich Teer 2005-07-08, 5:53 pm |
| On Fri, 8 Jul 2005, Ulrich Hobelmann wrote:
> Is it? I think argv[0], i.e. the program name, is guaranteed to
> exist, but with additional parameters I'm not too sure.
ISO C requires that the last member of argv be NULL, so yes one
can rely on at least argv[0] and argv[1] being available (for
suitable values of available).
> At least it's very bad style. I'm lazy, but there's always enough
That (bad style) I would agree with.
--
Rich Teer, SCNA, SCSA, OpenSolaris CAB member
President,
Rite Online Inc.
Voice: +1 (250) 979-1638
URL: http://www.rite-group.com/rich
| |
| Henry Townsend 2005-07-08, 5:53 pm |
| Ross wrote:
> In C++ and java, this style is a necessity in that object creation and
> inititalization are often sequence dependant:
>
> Object1 obj1;
> obj1.process_some_stuff();
> Object2 obj2(obj1);
I don't know about C++ but in Java you can't declare an object, just an
onject *reference*. Therefore the above could easily be written as
Object1 obj1;
Object2 obj2;
obj1 = new Object1();
obj1.process_some_stuff();
obj2 = new Object2();
--
Henry Townsend
| |
| Ralf Fassel 2005-07-08, 5:53 pm |
| * Ulrich Hobelmann <u.hobelmann@web.de>
| > For argv[1] it is guaranteed to either point to a valid string,
| > or to a null pointer. Dereferencing the null pointer would be a
| > problem, or dereferencing any argv[2], but he didn't do that.
|
| Is it? I think argv[0], i.e. the program name, is guaranteed to
| exist, but with additional parameters I'm not too sure.
No, Floyd is right, the argv[] list is terminated with a NULL
pointer.
R'
| |
| Ulrich Hobelmann 2005-07-08, 5:53 pm |
| Henry Townsend wrote:
> Ross wrote:
>
>
>
> I don't know about C++ but in Java you can't declare an object, just an
> onject *reference*.
Ross' above example means that Object1+2 aren't created with new
on the heap, but allocated on the stack, but the constructor is
still called (same for destruction). Because declaration
coincides with the constructor call (you *have* to provide a
constructor parameter list, unless the object has a () default
constructor), sometimes you have to declare objects in the middle
of a block.
In languages like ObjC and Java that heap-allocate everything this
isn't necessary.
--
By claiming a patent [...], I'm saying that you are not permitted
to use your own knowledge to further your ends. By what right?
Roderick T. Long
| |
|
| Henry Townsend pulled a bright blue crayon out of the box and scribbled
this in news:eLqdnXUmhvWPJFPfRVn-rg@comcast.com:
> Ross wrote:
>
> I don't know about C++ but in Java you can't declare an object, just an
> onject *reference*. Therefore the above could easily be written as
>
> Object1 obj1;
> Object2 obj2;
>
> obj1 = new Object1();
> obj1.process_some_stuff();
> obj2 = new Object2();
You are correct wrt to java: my apologies. It's been a numbewr of years
since I've written production Java code.
Ulrick's point is well taken: C++ allows for stack allocation of objects
and it is considered good form, unless it is impractical to do so. Well
written C++ classes manage their own heap allocations/deallocations
opaquely to the client code, so the actual objects are usually quite small.
The closest C++ versioon to the Java example is:
Object1* obj1;
Object2* obj2;
obj1 = new Object1;
obj1->process_some_stuff();
obj2 = new Object2(obj1);
The added issue here is when to delete the objects. If the objects are to
be deleted at the end of the code block, there really is no point in heap
allocating them.
R.
--
Go not to Usenet for counsel, for they will say both yes and no.
| |
| Pascal Bourguignon 2005-07-08, 5:53 pm |
| Rich Teer <rich.teer@rite-group.com> writes:
> On Fri, 8 Jul 2005, Ulrich Hobelmann wrote:
>
>
> ISO C requires that the last member of argv be NULL, so yes one
> can rely on at least argv[0] and argv[1] being available (for
> suitable values of available).
>
>
> That (bad style) I would agree with.
There's no guarantee. The caller can very well use:
execl("/home/pjb/bin/pgm",0);
or:
const char* argv[1]={0};
execv("/home/pjb/bin/pgm",argv);
Don't assume your program will always be called by well behaved shells!
--
__Pascal Bourguignon__ http://www.informatimago.com/
In a World without Walls and Fences,
who needs Windows and Gates?
| |
| Alex Fraser 2005-07-09, 2:48 am |
| "Rich Teer" <rich.teer@rite-group.com> wrote in message
news:Pine.SOL.4.58.0507070747530.7710@zen.rite-group.com...
> On Thu, 7 Jul 2005, Nick Sinclair wrote:
[snip]
>
> I'd use "NULL" rtaher than "(char *)0" here.
Just "NULL" is not correct, although it will often work. The cast is still
needed, ie "(char *)NULL".
Alex
| |
| Floyd L. Davidson 2005-07-09, 7:47 am |
| Ari Lukumies <nospam.ari.lukumies@gmail.com.invalid> wrote:
>Alex Fraser wrote:
>
>What do you base 'the cast is still needed' on?
The cast is required in the parameter list when a function is
prototyped with a variable argument list. Since the prototype
does not specify the argument's type, it must explicitly match
the actual variable to which it is assigned within the
definition of the function. (A pointer to char in this case.)
Since it is a pointer to char, a pointer to void will work too,
and hence some definitions of NULL will succeed; but if NULL is
defined as a raw "0", it will fail to generate a null pointer.
--
Floyd L. Davidson <http://web.newsguy.com/floyd_davidson>
Ukpeagvik (Barrow, Alaska) floyd@barrow.com
| |
| Ari Lukumies 2005-07-09, 7:47 am |
| Floyd L. Davidson wrote:
> Since it is a pointer to char, a pointer to void will work too,
> and hence some definitions of NULL will succeed; but if NULL is
> defined as a raw "0", it will fail to generate a null pointer.
Did you look up the function execl? Wouldn't it suffice just to use 0
(or NULL)?
-atl-
--
A multiverse is figments of its own creations
| |
| Floyd L. Davidson 2005-07-09, 7:47 am |
| Ari Lukumies <nospam.ari.lukumies@gmail.com.invalid> wrote:
>Floyd L. Davidson wrote:
>
>Did you look up the function execl? Wouldn't it suffice just to
>use 0 (or NULL)?
int execl(const char *path, const char *arg, ...);
...
The const char *arg and subsequent ellipses in the execl,
execlp, and execle func- tions can be thought of as arg0,
arg1, ..., argn. Together they describe a list of one or more
pointers to null-terminated strings that represent the
argument list available to the executed program. The first
argument, by convention, should point to the file name
associated with the file being executed. The list of
arguments must be terminated by a NULL pointer.
That is the Linux man page. Note that it is technically incorrect,
as there is no such thing as a "NULL pointer". NULL is a macro
in Standard C. What they meant to say is "a null pointer".
NULL is not necessarily a null pointer... but if you put the
cast in front of it, it is.
And, see the elipse? The original looked like this:
execl("/usr/bin/bash", "bash", cgi_filename, (char *)0);
The "cgi_filename" parameter is a pointer to char, but without the
cast a 0 will be a type int. It *must* be cast to be a pointer to
char.
NULL, in Standard C, can be any expression that used in a pointer
context will be a "null pointer constant", and the two commonly
used macros are "0" and "(void *)0". The second won't be a problem
because it provides the "pointer context" and that void pointer is
guaranteed to be interchangeable with a char pointer. But the
raw 0 is just a 0. It will be assigned to a parameter list variable
as a type int. When that is used in the definition of the function,
it is *not* a null pointer.
Therefore, the cast should be used, even with the NULL macro, just
to be sure that it works on a platform where NULL is defined as just
a 0.
--
Floyd L. Davidson <http://web.newsguy.com/floyd_davidson>
Ukpeagvik (Barrow, Alaska) floyd@barrow.com
| |
| Ari Lukumies 2005-07-09, 5:49 pm |
| Floyd L. Davidson wrote:
> And, see the elipse? The original looked like this:
>
> execl("/usr/bin/bash", "bash", cgi_filename, (char *)0);
>
> The "cgi_filename" parameter is a pointer to char, but without the
> cast a 0 will be a type int. It *must* be cast to be a pointer to
> char.
No, execl requires the fourth parameter be valid; NULL is not
necessarily the same as (char*)0. When an expression requires a NULL
pointer, it's guaranteed to write either NULL or 0, without casts.
-atl-
--
A multiverse is figments of its own creations
| |
| Ralf Fassel 2005-07-09, 5:49 pm |
| * Pascal Bourguignon <pjb@informatimago.com>
| There's no guarantee. The caller can very well use:
|
| execl("/home/pjb/bin/pgm",0);
t.c
execl("/tmp/xxx",0);
xxx.c:
int main(int argc, char*argv[]) {
printf("argc %d\n", argc);
printf("%p %p\n", argv[0], argv[1]);
return 0;
}
./t
argc 0
(nil) 0xbfffecd9
Doh. Suid programming is much fun, isn't it?
R'
| |
| Floyd L. Davidson 2005-07-09, 5:49 pm |
| Ari Lukumies <nospam.ari.lukumies@gmail.com.invalid> wrote:
>Floyd L. Davidson wrote:
>
>No, execl requires the fourth parameter be valid;
What does that mean? Valid what??
Once again, here is the prototype,
int execl( const char *path, const char *arg, ...);
There is no fourth parameter specified. We know from the
description that it *must be* a series of pointers to char, and
that the last one *must be* a null pointer.
If the macro NULL is defined as 0, it will *not* produce a null
pointer in the parameter list unless it is cast to a pointer
type (which in this case should obviously be a pointer to char).
>NULL is not
>necessarily the same as (char*)0.
NULL does *not* necessarily generate a "null pointer"!
But (char*)0 *does* necessarily generate a "null pointer".
The function *requires* a "null pointer". Hence using NULL will
not necessarily be correct, but using (char*)0 will always be
correct.
>When an expression requires a
>NULL pointer,
There is no such thing, in Standard C, as a "NULL pointer". Hence I
assume you mean a "null pointer"???
>it's guaranteed to write either NULL or 0, without
>casts.
*There is no such guarantee.*
First, the ANSI/ISO Standard says that NULL is a macro which
expands to an "implementation-defined null pointer constant".
See section "7.17 Common definitions <stddef.h>" paragraph 3.
Second, in section "6.3.2.3 Pointers" paragraph 3, it defines
a "null pointer constant" and generating a "null pointer".
"An integer constant expression with the value 0,
or such an expression cast to type void *, is
called a /null/ /pointer/ /constant/. If a null
pointer constant is converted to a pointer type,
the resulting pointer, called a /null/ /pointer/, is
guaranteed to compare unequal to a pointer to any
object or function."
(Note specifically, the "integer constant expression", which is
*not* a variable with the value of 0. It is a source code only
device which generates an implementation defined /null/
/pointer/ in the output code.)
char *p = 0; /* this produces a null pointer */
int x = 0;
char *p = x; /* this does *not* produce a null pointer */
The above is the background needed to understand what is
required by a variadic function like execl(3). Since it
requires a "null pointer" for the last argument, at some point
there must exist a "null pointer constant" that is converted to
a pointer type in order to generate a "null pointer".
The problem is that since the prototype does *not* specify the
argument as a pointer, if 0 is used it will be assigned to the
function argument *as a type int*, rather than being converted
to a pointer (which would happen in any function parameter list
where the prototype specifies that the argument is a pointer,
and would result in a null pointer).
Hence the local variable the 0 is assigned to within the
function definition is not assigned a pointer, but a type int.
It is no longer a "null pointer constant", because it is *not*
an "integer constant expression", and therefore even if it is
converted to a pointer type, it is *not* a "null pointer", but
instead just a pointer addressing location 0, which could even
be a valid memory address.
--
Floyd L. Davidson <http://web.newsguy.com/floyd_davidson>
Ukpeagvik (Barrow, Alaska) floyd@barrow.com
| |
| Floyd L. Davidson 2005-07-09, 5:49 pm |
| Pascal Bourguignon <pjb@informatimago.com> wrote:
>
>There's no guarantee. The caller can very well use:
>
> execl("/home/pjb/bin/pgm",0);
>
>or:
> const char* argv[1]={0};
> execv("/home/pjb/bin/pgm",argv);
>
>Don't assume your program will always be called by well behaved shells!
That is true!
However, I'd *want* my program to fail in some obnoxious way if
anybody called it like that. ;-)
--
Floyd L. Davidson <http://web.newsguy.com/floyd_davidson>
Ukpeagvik (Barrow, Alaska) floyd@barrow.com
| |
| David Schwartz 2005-07-09, 5:49 pm |
|
"Ari Lukumies" <nospam.ari.lukumies@gmail.com.invalid> wrote in message
news:FCOze.3216$iD2.1260@reader1.news.jippii.net...
> Alex Fraser wrote:
[vbcol=seagreen]
> What do you base 'the cast is still needed' on?
He is right. This is that one weird case. ;)
DS
| |
| Alex Fraser 2005-07-09, 5:49 pm |
| "David Schwartz" <davids@webmaster.com> wrote in message
news:dapcs5$qvi$1@nntp.webmaster.com...
> "Ari Lukumies" <nospam.ari.lukumies@gmail.com.invalid> wrote in message
> news:FCOze.3216$iD2.1260@reader1.news.jippii.net...
>
>
>
> He is right. This is that one weird case. ;)
Although the underlying reason for needing the cast is essentially the same
(the compiler doesn't know you meant a null pointer not an int with value
0), there is another case: a call to a function without a prototype, passing
NULL (or 0) as an argument where a pointer is expected.
The solution in this case is to make sure you use prototypes, by having the
compiler issue a warning for calls to functions without a prototype.
However, there is no general way to check variadic functions such as execl
are being called correctly. You just have to be careful.
Alex
| |
| Ari Lukumies 2005-07-10, 2:52 am |
| Floyd L. Davidson wrote:
> Ari Lukumies <nospam.ari.lukumies@gmail.com.invalid> wrote:
>
>
>
> What does that mean? Valid what??
>
> Once again, here is the prototype,
>
> int execl( const char *path, const char *arg, ...);
>
> There is no fourth parameter specified. We know from the
> description that it *must be* a series of pointers to char, and
> that the last one *must be* a null pointer.
>
> If the macro NULL is defined as 0, it will *not* produce a null
> pointer in the parameter list unless it is cast to a pointer
> type (which in this case should obviously be a pointer to char).
>
>
>
>
> NULL does *not* necessarily generate a "null pointer"!
>
> But (char*)0 *does* necessarily generate a "null pointer".
>
> The function *requires* a "null pointer". Hence using NULL will
> not necessarily be correct, but using (char*)0 will always be
> correct.
>
>
>
>
> There is no such thing, in Standard C, as a "NULL pointer". Hence I
> assume you mean a "null pointer"???
>
>
>
>
> *There is no such guarantee.*
>
> First, the ANSI/ISO Standard says that NULL is a macro which
> expands to an "implementation-defined null pointer constant".
> See section "7.17 Common definitions <stddef.h>" paragraph 3.
>
> Second, in section "6.3.2.3 Pointers" paragraph 3, it defines
> a "null pointer constant" and generating a "null pointer".
>
> "An integer constant expression with the value 0,
> or such an expression cast to type void *, is
> called a /null/ /pointer/ /constant/. If a null
> pointer constant is converted to a pointer type,
> the resulting pointer, called a /null/ /pointer/, is
> guaranteed to compare unequal to a pointer to any
> object or function."
I do follow the rules, however I'm just lately to be accustomed to C99
(previously I was using ANSI 1982 C).
> char *p = 0; /* this produces a null pointer */
Ok.
> int x = 0;
> char *p = x; /* this does *not* produce a null pointer */
No, because that is an illegal assignmegnt.
>
> The above is the background needed to understand what is
> required by a variadic function like execl(3). Since it
> requires a "null pointer" for the last argument, at some point
> there must exist a "null pointer constant" that is converted to
> a pointer type in order to generate a "null pointer".
A NULL pointer is a pointer with the value of NULL (NULL may not be
equal to zero, but it's null whichever way you choose it to be).
-atl-
--
A multiverse is figments of its own creations
| |
| Floyd L. Davidson 2005-07-10, 2:52 am |
| Ari Lukumies <nospam.ari.lukumies@gmail.com.invalid> wrote:
>Floyd L. Davidson wrote:
>
>I do follow the rules, however I'm just lately to be accustomed
>to C99 (previously I was using ANSI 1982 C).
The above *is* from the C99 C Standard.
>
>Ok.
>
>
>No, because that is an illegal assignmegnt.
No it isn't. It is implementation defined.
>
>A NULL pointer is a pointer with the value of NULL (NULL may not
>be equal to zero, but it's null whichever way you choose it to
>be).
I've just shown you what the C99 Standard says, and *that* is
not what it says.
NULL is a macro, and it is either 0 or 0 cast to a pointer. Go
back and read my post again, carefully.
You are still not putting the pieces together, because you are
apparently holding onto some preconceived notions (which happen
to be wrong).
--
Floyd L. Davidson <http://web.newsguy.com/floyd_davidson>
Ukpeagvik (Barrow, Alaska) floyd@barrow.com
| |
| Ari Lukumies 2005-07-10, 7:48 am |
| Floyd L. Davidson wrote:
> You are still not putting the pieces together, because you are
> apparently holding onto some preconceived notions (which happen
> to be wrong).
I stand corrected, after going through N869. My apologies.
-atl-
--
A multiverse is figments of its own creations
| |
| David Schwartz 2005-07-11, 2:48 am |
|
"Floyd L. Davidson" <floyd@apaflo.com> wrote in message
news:87irzjje3q.fld@barrow.com...
[vbcol=seagreen]
> I've just shown you what the C99 Standard says, and *that* is
> not what it says.
> NULL is a macro, and it is either 0 or 0 cast to a pointer. Go
> back and read my post again, carefully.
Right, it can be zero cast to a pointer, which may or may not be equal
to zero. Obviously zero cast to a pointer is equal to zero cast to a
pointer. But zero cast to a pointer may or may not be equal to zero.
DS
| |
| Floyd L. Davidson 2005-07-11, 2:48 am |
| "David Schwartz" <davids@webmaster.com> wrote:
>"Floyd L. Davidson" <floyd@apaflo.com> wrote:
>
>
>
>
> Right, it can be zero cast to a pointer,
Note that is exactly what I said...
>which may or may not be equal
>to zero.
In *no case* is NULL anything other than a "null pointer
constant", which is a /source code/ device. It has no _value_.
>Obviously zero cast to a pointer is equal to zero cast to a
>pointer. But zero cast to a pointer may or may not be equal to zero.
Wrong. It is *guaranteed* to compare equal to zero.
I realize you are trying to play word games. But you need to
do a better job. You aren't distinguishing between 1) the macro
NULL which is what was discussed above and which a compiler's
code generator never sees, 2) a "null pointer constant" which is a
source code device, and 3) a "null pointer" which is a platform
defined value in the produced binary.
As long as you mix those terms, it's nonsense... even if it is
just a word game. (Or are you actually confused??? If so, read
my previous articles in this thread...)
--
Floyd L. Davidson <http://web.newsguy.com/floyd_davidson>
Ukpeagvik (Barrow, Alaska) floyd@barrow.com
| |
| David Schwartz 2005-07-11, 5:53 pm |
|
"Floyd L. Davidson" <floyd@apaflo.com> wrote in message
news:87mzotj45e.fld@barrow.com...
[vbcol=seagreen]
> Wrong. It is *guaranteed* to compare equal to zero.
I never said it wouldn't *compare* equal to zero. That's because when
you compare a pointer to zero, the zero is *changed* to a pointer before
anything is compared to anything else.
> As long as you mix those terms, it's nonsense... even if it is
> just a word game. (Or are you actually confused??? If so, read
> my previous articles in this thread...)
I know exactly what I'm talking about, I think.
DS
| |
| Floyd L. Davidson 2005-07-11, 8:48 pm |
| "David Schwartz" <davids@webmaster.com> wrote:
>"Floyd L. Davidson" <floyd@apaflo.com> wrote in message
>news:87mzotj45e.fld@barrow.com...
>
>
>
> I never said it wouldn't *compare* equal to zero. That's because when
>you compare a pointer to zero, the zero is *changed* to a pointer before
>anything is compared to anything else.
>
>
> I know exactly what I'm talking about, I think.
You knew you were talking nonsense.
--
Floyd L. Davidson <http://web.newsguy.com/floyd_davidson>
Ukpeagvik (Barrow, Alaska) floyd@barrow.com
| |
| Brian Raiter 2005-07-12, 8:52 pm |
| >> Don't assume your program will always be called by well behaved shells!
>
> That is true!
>
> However, I'd *want* my program to fail in some obnoxious way if
> anybody called it like that. ;-)
Of course, there's no guarantee that the program would fail. You might
wind up with a valid pointer to some stack region. In fact, depending
on your code, you might wind up turning a small bug into a security
hole later on down the road.
b
|
|
|
|
|