2

Okay, I'm trying to build a daemon (for a Debian machine) that will take the command line arguments it receives (via cron) and pass them to different script files.

The daemon's main() is

 int main(int argc , char *argv[])
 {
        if(argc != 3)
        {
            exit(0);
        }

        daemonize(argv[1], argv[2]);

    return 0;
 }

And the function daemonize has this set up

int daemonize(const char *cmd1, const char *cmd2) {...}

The troubling part in daemonize is here:

if (strcmp(cmd1,"sample_script") == 0)
    {
        static char *argv[] = {"/etc/init.d/sample_script", ["%s",cmd2], NULL };
        execv("/etc/init.d/sample_script",argv);
        exit(127);
    }

On the line

static char *argv[] = {"/etc/init.d/sample_script", ("%s",cmd2), NULL };

I am getting this error

initializer element is not constant (near initialization for ‘argv[1]’)

Obviously ("%s",cmd2) is wrong. Because using "start" works fine.

So, how do I get cmd2 to be correctly put into *argv[]? Or is it something else I am doing wrong?

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
mycowan
  • 993
  • 8
  • 18
  • Y do u need function parameters to be const char * – Yasir Majeed Apr 01 '15 at 06:57
  • Are you sure your `argv` values are correct in `daemonize` function. Since your `*argv[] = {"/etc/init.d/sample_script", ...}` and you pass it to `execv`, so `/etc/init.d/sample_script` is repaeted twice, Its equivalent to running as `/etc/init.d/sample_script /etc/init.d/sample_script ..` in command line. If you could provide sample input and expected output, It would be easier – Saurabh Meshram Apr 01 '15 at 06:57
  • What is that `[ ...]` notation inside the initializer supposed to be? – Jens Gustedt Apr 01 '15 at 07:16
  • @JensGustedt thank you for your question. Basically the function daemonize will do a double forking so the the parent process is in _init_. Then it will call different scripts (depending on cmd1) and pass cmd2 to them. This way these scripts also only have init as the parent process. That's the plan anyway. – mycowan Apr 01 '15 at 07:22
  • My question was more basic, the syntax that you are giving makes no sense to me. In fact you are using a *designated initializer* with a comma expression in it. This is most certainly not what you want. – Jens Gustedt Apr 01 '15 at 07:24
  • @SaurabhMeshram thank you for your help. I had wondered about that too. But that's what the sample code had. **Wait!** I just had a double check of that [code] (http://stackoverflow.com/questions/5460421/how-to-write-a-c-program-to-execute-another-program). I think I have made a mistake. This _static char *argv[] = {"start", NULL};_ compiles, but the arguements are not being passed to sample_script. It does not run. But, this _static char *argv[] = {**"sample_script",**"start", NULL};_ both compiles and runs sample_script. – mycowan Apr 01 '15 at 07:41

3 Answers3

3

You need to change

static char *argv[] = {"/etc/init.d/sample_script", ["%s",cmd2], NULL };

to

const char *argv[] = {"/etc/init.d/sample_script", cmd2, NULL };

You have to remove the static keyword. As per chapter 6.7.9, C11 standard,

All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals.

Which says, in C language objects with static storage duration have to be initialized with constant expressions or with aggregate initializers containing constant expressions.

And, regarding constant expression, from chapter 6.6 of the same document

A constant expression can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be.

So, in C, a variable (name), even if declared as const is never a constant expression.


EDIT:

To resolve the latest issue, you can try the following

  1. change int daemonize(char *cmd1, char *cmd2) {..
  2. use char * const argv[] = {"/etc/init.d/sample_script", cmd2, NULL };

and you can keep the rest of the code unchanged.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • Thank you for your answer. Unfortunately that's what I originally had. But it did not work. The same error was given. – mycowan Apr 01 '15 at 06:51
  • @mycowan A pointer can be made to point to another pointer during initialization and what sourav has shown is correct. I don't see a need for `NULL` here. Get rid of it – Gopi Apr 01 '15 at 06:53
  • @Gopi, thanks for your help. I tried {"/etc/init.d/sample_script", cmd2}; but still the same error. – mycowan Apr 01 '15 at 06:59
  • @mycowan I think we're all missing chapter 6.7.9. Let me revise once. – Sourav Ghosh Apr 01 '15 at 07:01
  • @SouravGhosh - thanks for the heads up. But the new error is _warning: passing argument 2 of ‘execv’ from incompatible pointer type [enabled by default] In file included from daemonizer.c:4:0: /usr/include/unistd.h:560:12: note: expected ‘char * const*’ but argument is of type ‘const char **’_ – mycowan Apr 01 '15 at 07:09
  • @SouravGhosh :-( _warning: initialization discards ‘const’ qualifier from pointer target type [enabled by default]_ – mycowan Apr 01 '15 at 07:45
  • @mycowan did you make both the changes, `1` and `2`? – Sourav Ghosh Apr 01 '15 at 07:47
  • @SouravGhosh - please see user3386109's explanation of execv's declaration and his code. It worked. I'll give your new suggestion a try right now. Thank you. – mycowan Apr 01 '15 at 08:12
  • @mycowan My idea was almost the same, minus the requirement of temporary variables. :-) Try it and let me know the result for sure. – Sourav Ghosh Apr 01 '15 at 08:15
  • @SouravGhoshYour code works too! Thank you very much for your time and help. – mycowan Apr 01 '15 at 08:33
2

The function declaration for execv is

int execv(const char *path, char *const argv[]);

The proper interpretation of the second argument is a "const array of char *", which is different than an "array of const char *". In other words, execv reserves the right to change the contents of the strings that the array points to, but won't change any of the pointers in the array.

Which is to say that all of the strings in the argv array must be in writable memory. So to be perfectly technically correct, the code should make writable copies of all the strings, like so

if (strcmp(cmd1,"sample_script") == 0)
{
    char arg0[] = "/etc/init.d/sample_script";
    char *arg1 = strdup( cmd2 );

    char *argv[] = { arg0, arg1, NULL };
    execv( argv[0], argv );
    exit(127);
}
user3386109
  • 34,287
  • 7
  • 49
  • 68
  • @user3386106 - Hurry v(^.^)v - your code worked! I was getting a warning about _ incompatible implicit declaration of built-in function ‘strdup’_, so I had to add #include But - everything compiled and sample_script ran. Thanks a whole bunch! – mycowan Apr 01 '15 at 08:09
0

you can't initialize a static with a variable/argument only known at runtime.

leave the static out:

char *argv[] = {"/etc/init.d/sample_script", ["%s",cmd2], NULL };
DrKoch
  • 9,556
  • 2
  • 34
  • 43
  • thank you for assistance, but this was the new error - _warning: initialization discards ‘const’ qualifier from pointer target type [enabled by default]_ – mycowan Apr 01 '15 at 07:03
  • 1
    @mycowan, are you sure that you use a C compiler and not a C++ compiler? – Jens Gustedt Apr 01 '15 at 07:14
  • To be honest, I'm pulling in bits and pieces for code from all over the place. This project is at the very limit of my coding skill. The target machine is a stripped down linux machine (Debian - before anyone starts - the decision was made before I got here) for a remote sensing device, so I hope I'm not putting any C++ code on. I'm using gcc to compile. I hope that's the right one. (Java guy - who's only using C because the daemon will be running at the lowest run level and only C works down there - I believe) – mycowan Apr 01 '15 at 08:41