0

I'm trying to have a process fork and run execve in the child process so that it will open a new terminal window and execute a custom command there.

The program I want to execute is gestore

These are the arguments I pass to execve:

char * argv_exec[5];
argv_exec[0]="/usr/bin/xfce4-terminal";   
argv_exec[1]="--geometry";
argv_exec[2]="480x320";
argv_exec[3]="-x";
argv_exec[4]="./gestore";    // the program I want to execute in new window
argv_exec[5]=NULL;

char sess_m[80];
strcat(sess_m,"SESSION_MANAGER=");
strcat(sess_m,getenv("SESSION_MANAGER"));

char * envp[3];
envp[0]="DISPLAY=:0.0";
envp[1]=sess_m;
envp[2]=NULL;

and here I call execve:

if(pid_tv==0)
    if(execve(argv_exec[0],argv_exec,&envp)==-1) {...}

but I keep getting Session manager variable not defined.

Anyone has a suggestion on why this does not work or how could I do it better?

ruakh
  • 175,680
  • 26
  • 273
  • 307
sowdust
  • 87
  • 1
  • 9
  • Could You try `env | grep SESSION_MANAGER | wc -c`? :) In my machine it's 92. So, a bit bigger then Your default `sess_m` buffer... Guess that MIGHT be a problem. – Kamiccolo Aug 30 '13 at 16:37
  • argv_exec should have size 6, not 5. And the first strcat should be strcpy instead. And not &envp in call to execve, just envp. – sjnarv Aug 30 '13 at 17:14
  • thanks for pointing out the 'typos' even if in this case i think the problem is elsewhere – sowdust Aug 31 '13 at 12:29

1 Answers1

1

Some re-writing, both to show more idiomatic use of a process's environment (see environ(7)) and to demonstrate some ways to avoid hard-wiring array sizes: always a source of fence-post errors in C.

See code comments for some of the explanations/justifications.

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern char **environ;  /* see environ(7) */

int
main(void)
{
    char *argv_exec[] = {
        "/usr/bin/xfce4-terminal",
        "--geometry",
        "480x320",
        "-x",
        "./gestore",
        NULL };

    /*
     * Structure to drive search for a few variables present in
     * current environment, and construct a new, minimal, environment
     * containing just those values. Use the direct value from the
     * current process's environ variable to set "value" (the string
     * of the form "NAME=VALUE"). Just add more entries to nv to handle
     * more variables; the code below will adjust sizes.
     *
     * Any value missing in the environment will be an error.
     */
    struct {
        char *name;
        char *value;
    } nv[] = {
        { "DISPLAY", NULL },
        { "SESSION_MANAGER", NULL }
    };

    /* size new_envp to have one more entry than nv */
    char *new_envp[sizeof(nv) / sizeof(nv[0]) + 1];

    char **e;
    int i, error_flag;
    pid_t pid;

    /*
     * For each variable needed...
     */
    for (i = 0; i < sizeof(nv) / sizeof(nv[0]); i++) {
        /* ...search in current environment */
        for (e = environ; *e; e++) {
            size_t slen = strlen(nv[i].name);
            if (strncmp(*e, nv[i].name, slen) == 0 && (*e)[slen] == '=') {
                nv[i].value = *e;
                break;
            }
        }
    }

    /*
     * Check that we found all values, setting up new_envp as we go.
     */
    error_flag = 0;
    for (i = 0; i < sizeof(nv) / sizeof(nv[0]); i++) {
        if (nv[i].value == NULL) {
            (void) fprintf(stderr, "%s not set in environment\n",
                        nv[i].name);
            error_flag = 1;
        } else {
            new_envp[i] = nv[i].value;
        }
    }
    if (error_flag) {
        return 1;
    }
    new_envp[i] = NULL;

    /* very minimal fork/exec processing */

    pid = fork();

    if (pid == -1) {
        perror("fork");
        return 1;
    }
    if (pid == 0) {
        (void) execve(argv_exec[0], argv_exec, new_envp);
        /*
         * If execve succeeded, the invoked program has
         * replaced this process, and will either run or
         * (presumably) report its own errors. If we're
         * still in control, the execve failed, so print
         * an error and exit.
         */
        perror(argv_exec[0]);
        return 1;
    } else {
        if (wait(0) != pid) {
            perror("wait");
            return 1;
        }
    }

    return 0;
}
sjnarv
  • 2,334
  • 16
  • 13
  • thank you very much for these suggestion which is very clever and useful. THe problem is that i still get an error I do not know what to do with. Also, the fork fails but it keeps forking so that I have to manually kill all processes. The error returned in termina is: – sowdust Aug 31 '13 at 12:28
  • (xfce4-terminal:3233): GLib-WARNING **: (/build/buildd/glib2.0-2.36.0/./glib/gerror.c:390):g_error_new_valist: runtime check failed: (domain != 0) Failed to connect to session manager: Failed to connect to the session manager: Authentication Rejected, reason : None of the authentication protocols specified are supported and host-based authentication failed Terminated – sowdust Aug 31 '13 at 12:28
  • Trimming the environment down to two variables most likely prevents the exec'd program from working properly. You might try execv instead of execve (no third argument to execv; it just uses the current env) to see if that behaves differently, and work from there. Unsure what's happening with fork/exec/wait handling - are you using my sample code, or something else? – sjnarv Aug 31 '13 at 14:47
  • i didn't know about execv, thanks for suggesting that. I can now fork the process without passing any environment variable. – sowdust Sep 02 '13 at 15:13
  • 1
    In the example above, `if (strncmp(*e, nv[i].name, slen) == 0) {`, if I'm looking for a variable called FOO and my environ happens to have instead a value FOO_BAR this may pick the wrong variable by accident, right? One should check if the following character is `'='`. – Hisham H M Sep 01 '16 at 21:05
  • 1
    @HishamHM - good catch! A definite bug. Fixed in the example now. Thanks. – sjnarv Sep 23 '16 at 18:59