1

How would I make a GCC C program on Linux that:

  • has a PHP program script inside itself, not as a separate file
  • expands itself out to /tmp
  • shells out to php CLI command line to run the PHP script
  • passes command line arguments of the C program as command line arguments of the PHP program
  • errors out nicely with a message if php CLI is not possible on the system
Volomike
  • 23,743
  • 21
  • 113
  • 209
  • Which specific step are you having trouble with? We're not going to write the program for you... – bdonlan Jul 07 '11 at 01:45
  • I can call setenv() and system() okay from C. The problem is expanding a file resource outside of the C file. – Volomike Jul 07 '11 at 01:47

2 Answers2

3

PHP program script could be stored as a global char * use the fopen, fwrite functions to open and write the global variable out. Then use popen to execute the php interpreter (this will pretty much execute whatever command you send to it) and specify a file descriptor that you could read from to get the output. If you don't need the output for your program (or that is not the intention) than you can utilize the exec suite to execute the command and pass the /tmp/filename as an argument.

To get the arguments from the C program, you'll use the *argv[] in int main(int argc, char *argv[]) just remember that argv[0] is YOUR applications name (I always forget that).

The exec functions will return -1 and errno will be set. Man pages should answer the implementation of each of these. but just in case you get lost.

char *script = "here are your script contents";

char *php_application_name = "pear";

int main(int argc, char *argv[]) {
  FILE *fd;
  char *filename = create_random_filename("/tmp/");
  fd = fopen(filename, "rw");

  fwrite(script, sizeof(char), strlen(script), fd);

  fclose(fd);

  /* Assuming application executes like
     pear <script> <arguments>
   */
  char *script_args[argc];
  int i = 1;
  script_args[0] = php_application_name;
  script_args[1] = filename;
  for(i = 1; i < argc; i++) {
    script_args[i+1] = argv[i];
  }

  execvp(php_application_name, script_args);
}
Suroot
  • 4,315
  • 1
  • 22
  • 28
  • I would like to add that I adapted this by using the following technique to embed a data resource inside a C program: http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967 – Volomike Jul 07 '11 at 02:01
  • 1
    Only complaint on that would be that you are binding yourself to an i386 elf architecture. By including the script as an embedded char *, any arch you compile on it should work. – Suroot Jul 07 '11 at 02:03
  • Good point. So, does C have a HEREDOC format with <<< ? It's been so long since I worked with C. I'm having to crack out my old C manuals and relearn. (However, in my case, my only need is Linux, but would be an interesting discussion for those seeking other platforms.) – Volomike Jul 07 '11 at 02:06
  • No, you can use the `\\` at the end of the each line to continue a `char *` strings. Remember, adding a `\\` does NOT indicate continue to a new line in the string itself; you will still need a `\n` to indicate a newline. – Suroot Jul 07 '11 at 02:10
  • Please excuse the formatting of that above, evidently SO doesn't appreciate \ 's – Suroot Jul 07 '11 at 02:11
  • Say, doesn't your fopen() have a bug? I mean, isn't that where the filename var is supposed to go? And is there a way to tack on ".php" on the end of create_random_filename()? – Volomike Jul 07 '11 at 02:39
1

I want to give props to @Suroot and also this great article: http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967

However, the entire process is like so at Linux command line:

...first, you need to be on Linux, PHP for command line (CLI) needs to be enabled, and PHP at command line should not have any errors when you do "php -v" or pass it a test script that you know typically would work at command line.

...second, you need to have GCC installed for compiling C programs.

...create a working directory and 'cd' into it.

...create a file script.php that has this test code inside:

<?php

$s1 = @ $argv[1];
$s2 = @ $argv[2];
$s3 = @ $argv[3];

for($i=0;$i<=10;$i++){
    echo "($s1) ($s2) ($s3)\n";
}

...note, this is only our test. You can put your own PHP code in there later on once you've proven that the test works.

...note that "script.php" is critical to be named that or the main.c won't work when it hits the _binary_script_php_start line. That through me for a loop at first because I didn't make the mental connection that the variable name is glued in by this process.

...create a C code file main.c that has this code inside:

#include <stdio.h>
#include <stdlib.h>

extern char _binary_script_php_start;
extern char _binary_script_php_end;

int main(int argc, char *argv[]) {
    // EXTRACT OUR RESOURCE OBJECT INTO /tmp/test.php
    char *p = &_binary_script_php_start;
    FILE *fp = fopen("/tmp/test.php","wb");
    while ( p != &_binary_script_php_end ) {
        fputc(*p++,fp);
    }
    fclose(fp);
    // NOW READ IN OUR STANDARD ARGUMENTS AND LAUNCH OUR COMMAND
    int i = 1;
    char *cmd = "php /tmp/test.php";
    char *s = NULL;
    asprintf(&s, "%s",cmd);
    for(i = 1; i < argc; i++) {
        asprintf(&s, "%s \"%s\"",s,argv[i]);
    }
    system(s);
    free(s);
    unlink("/tmp/test.php"); // comment me out for debugging if you want
}

...note, I'm sort of rusty with C (haven't used it since the 1980s), and strcpy() is not advised because it's unsafe. So, I read up on StackOverflow that we should use asprintf() on GNU Linux systems. Evidently asprintf() handles buffer overflow and malloc issues? I sure hope so -- I don't want to lead you wrong here. Please research that!

...and now the magic begins!

...we need to embed the script.php into an object file "data.o" with this command-line command (thus starting with the $ prompt):

$ objcopy -I binary -O elf32-i386 -B i386 script.php data.o

...now we build our command "runme" where we link in "data.o" and "main.c" together:

$ gcc main.c data.o -o runme

...this then made a compiled C program for us called "runme". We can then execute it to test it:

$ ./runme "test1" "test2 test3" "test4"

...You should see an output like the following:

(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)

...as you can see, the doublequotes actually are important -- they tell the C, and therefore the PHP, that the parameters should be grouped together. That's why test3 doesn't appear in the last parentheses. Without the doublequotes, you'll see a completely different result.

At this point, you see how we can make a C program that has an embedded PHP script inside and which runs on Linux. (Mac, Windows -- you'll likely need to tweak this code somewhat.) The C program expands our PHP script into /tmp, executes it, and passes it parameters that we can read in and use. From here on out, you can revise the script.php to do as you need, and you can see about using a unique filename so that you don't clobber someone else's /tmp/test.php file, just in case.

Community
  • 1
  • 1
Volomike
  • 23,743
  • 21
  • 113
  • 209
  • 1
    You will have a memory leak, your for loop is causing asprintf to be called which allocs a new pointer to s each time. But you never free the previous s. If you were worried about buffer overflows; check out snprintf() (however you would need to know the size requirements, you could do this by summing up the strlen's of all your argv's). – Suroot Jul 07 '11 at 21:52
  • @Suroot - I'm marking this one as the answer for now, even though it has the minor memory leak, because it is the complete process. However, I will be getting with you offline to iron out the memory leak and will revise this answer. I just need to learn C string concatenation properly. – Volomike Jul 12 '11 at 05:12