s

Openwrt15.05 how to set environment variables for process


2 background

I am porting Bluetopia app to Openwrt15.05, and I write the Bluetopia app's boot
script according to Procd style.

The Bluetopia libs used the system environment variable for setting bluetooth
address. And the pointed system environment BTHOST_BD_ADDR should exist in the
process space.

I cannot find any desired system environment by using the following checking
code:

extern char **environ;
char **env = environ;
while (*env) {
  printf("%s\n", *env);
  env++;
}
              

the result as below:

SHLVL=1
HOME=/
TERM=linux
PWD=/
PATH=/usr/sbin:/usr/bin:/sbin:/bin

Also you can check the environment variables for a running process by using

strings –a /proc/<pid_of_the_process>/environ
              

Even thouth, I used the setenv() api to set the pointed environment variable,
but the variable cannot be set (showing 'null' from getenv()).

3 analysis

Every process has its own copy of the environment variables. That they inherit
from parent is just a convention. If you use execve you can set them to whatever
you want. Manipulation of one process's copy never affects any other existing
process, and in particular, nothing a child process does can change its parent's
copy of the environment.

So the environment variables below are belong to the procd.

SHLVL=1
HOME=/
TERM=linux
PWD=/
PATH=/usr/sbin:/usr/bin:/sbin:/bin

the App we created inherit the environment variables from procd.

4 fix steps

change the boot script, pass the environment variables by using

procd_set_param env
              

sample as below:

procd_open_instance
procd_set_param env BTHOST_BD_ADDR=${BTHOST_BD_ADDR}
procd_set_param command "${APP}"
procd_set_param respawn
procd_close_instance
              

4.1 analysis the env related code

Analysis from the boot script:

  • /etc/init.d/bluetopia
    start_service()
      procd_open_instance
      procd_close_instance
                        
  • /etc/rc.common
    rc_procd start_service "$@"
      procd_open_service "$(basename ${basescript:-$initscript})" "$initscript"
      procd_close_service
                        
  • search "procd_open_service" in procd src directory
  • ipkg-ipq806x/procd/lib/functions/procd.sh
    _procd_wrapper procd_open_service
      _procd_call _procd_open_service
        json_add_object instances
                        
  • all arguments are packed into json and send over to procd via ubus
    procd_close_service
      _procd_close_service()
        _procd_ubus_call set
          ubus call service set "$(json_dump)"
                        
  • procd/service/service.c
    UBUS_METHOD("set", service_handle_set, service_set_attrs),
      service_handle_set()
        service_alloc()
          vlist_init(&s->instances, avl_strcmp, service_instance_update);
        service_update()
        service_event("service.start", s->name, NULL);
          ubus_notify(ctx, &main_object, type, b.head, -1);
                        

    Then

    service_instance_update()
      else if (in_n) {
          DEBUG(2, "Create instance %s::%s\n", in_n->srv->name, in_n->name);
          instance_start(in_n);
      }
                        
  • ubus/libubus-req.c
    ubus_notify()
      __ubus_notify_async()
        ubus_start_request()
          ubus_send_msg()
                        
  • ubus/libubus-io.c
    ubus_send_msg()
      writev_retry()
        cur_len = sendmsg(fd, &msghdr, 0);
                        
  • procd/service/instance.c
    instance_start()
      instance_run()
        blobmsg_list_for_each(&in->env, var)
          setenv(blobmsg_name(var->data), blobmsg_data(var->data), 1);
        execvp(argv[0], argv);
                        

Add some debug logs:

blobmsg_list_for_each(&in->env, var)
{
    setenv(blobmsg_name(var->data), blobmsg_data(var->data), 1);
                  char cmd[128];
    sprintf(cmd, "echo [%s - %s] >> /tmp/xx1",
            blobmsg_name(var->data), (char*)blobmsg_data(var->data));
    system(cmd);
}
                

After the system booted up, I check the /tmp/xx1

root@xx:/tmp# cat xx1
[BTHOST_BD_ADDR - 0x34AA0B7FA1CF]
                

So, It's been verified that we pass the environment virables by using
procd_set_param env, and in the end procd use setenv and execvp start
the APP, in which has the pointed environment virable we need.

5 error fix

When I do the test, I encounter a issue that I cannot boot my process by using:

root@xx:# /etc/init.d/bluetopia start
              

The reason is that I add a '\' after the last line of the env. as below:

procd_open_instance
procd_set_param command "${APP_SCANNER}"
procd_set_param respawn
procd_set_param env BTHOST_BD_ADDR=${BTHOST_BD_ADDR} \
                BTHOST_XCAL_TRIM=${BTHOST_XCAL_TRIM} \
procd_close_instance
              

To fix this issue, we need remove the last '\', the fixed code:

procd_open_instance
procd_set_param command "${APP_SCANNER}"
procd_set_param respawn
procd_set_param env BTHOST_BD_ADDR=${BTHOST_BD_ADDR} \
                BTHOST_XCAL_TRIM=${BTHOST_XCAL_TRIM}
procd_close_instance