nginx learning - - setting process title(setproctitle)

Keywords: Nginx Linux less

nginx learning - setting process title(setproctitle)

Just saw the source code of nginx setting process title, so make some summaries.
linux processes actually use the value at argv[0] as the title of the process, so if you need to modify the title of the process, you only need to modify the value at argv[0].
The simple way is to copy the title you want to set directly to argv[0], as follows:

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

 extern char** environ;

 int main(int argc, char* argv[])
 {
     char s_title[] = "xiejian title for simple way!";
     size_t i_size = strlen(s_title);

     memcpy(argv[0], s_title, i_size);
     argv[0][i_size] = '\0';

     while(1){
         system("ps -ef|awk '$8 ~ /xiejian/ {print $0}'");
         sleep(10);
     }
}

The results are as follows:

But this is a disruptive way to modify the process title. Because the storage space of program parameters is closely followed by the storage location of environment variables, without considering the destructiveness of parameters.
The command line parameter argv and the environment variable information environ are represented in a continuous memory, and environ follows argv closely. As follows:

As can be seen from the figure above, too long titles can also damage the value of environ, an environmental variable. Therefore, in nginx, environment variables are shifted and stored. Here is the idea of setting titles for nginx processing processes.

/*
 * To change the process title in Linux and Solaris we have to set argv[1]
 * to NULL and to copy the title to the same place where the argv[0] points to.
 * However, argv[0] may be too small to hold a new title.  Fortunately, Linux
 * and Solaris store argv[] and environ[] one after another.  So we should
 * ensure that is the continuous memory and then we allocate the new memory
 * for environ[] and copy it.  After this we could use the memory starting
 * from argv[0] for our process title.
 *
 * The Solaris's standard /bin/ps does not show the changed process title.
 * You have to use "/usr/ucb/ps -w" instead.  Besides, the UCB ps does not
 * show a new title if its length less than the origin command line length.
 * To avoid it we append to a new title the origin command line in the
 * parenthesis.
 */

Then, because nginx considers multi-processes, it completes environ migration at initialization time. Here is the initialization function

//Point to environment variables, default
extern char **environ;
//Point to the last location of the previous space for storing parameters and environment variables.
static char *ngx_os_argv_last;

ngx_int_t
ngx_init_setproctitle(ngx_log_t *log)
{
    u_char      *p;
    size_t       size;
    ngx_uint_t   i;

    size = 0;
//Calculate the total space used for environmental variables, and then apply for sufficient space to store environmental variables
    for (i = 0; environ[i]; i++) {
        size += ngx_strlen(environ[i]) + 1;
    }
    //Allocating space for the environment
    p = ngx_alloc(size, log);
    if (p == NULL) {
        return NGX_ERROR;
    }
    //Let's start by calculating the last location used to store environment variables
ngx_os_argv_last = ngx_os_argv[0];
//First, calculate the last position of the parameters.
    for (i = 0; ngx_os_argv[i]; i++) {
        if (ngx_os_argv_last == ngx_os_argv[i]) {
            ngx_os_argv_last = ngx_os_argv[i] + ngx_strlen(ngx_os_argv[i]) + 1;
        }
    }
    //Then calculate the last position occupied by environmental variables, and complete the copy of environmental variables.
    for (i = 0; environ[i]; i++) {
        if (ngx_os_argv_last == environ[i]) {

            size = ngx_strlen(environ[i]) + 1;
            ngx_os_argv_last = environ[i] + size;

            ngx_cpystrn(p, (u_char *) environ[i], size);
            environ[i] = (char *) p;
            p += size;
        }
    }

    ngx_os_argv_last--;

    return NGX_OK;
}

Then there is the specific function to set the title:

void
ngx_setproctitle(char *title)
{
    u_char     *p;

#if (NGX_SOLARIS)

    ngx_int_t   i;
    size_t      size;

#endif

    ngx_os_argv[1] = NULL;

    p = ngx_cpystrn((u_char *) ngx_os_argv[0], (u_char *) "nginx: ",
                    ngx_os_argv_last - ngx_os_argv[0]);

    p = ngx_cpystrn(p, (u_char *) title, ngx_os_argv_last - (char *) p);

#if (NGX_SOLARIS)

    size = 0;

    for (i = 0; i < ngx_argc; i++) {
        size += ngx_strlen(ngx_argv[i]) + 1;
    }

    if (size > (size_t) ((char *) p - ngx_os_argv[0])) {

        /*
         * ngx_setproctitle() is too rare operation so we use
         * the non-optimized copies
         */

        p = ngx_cpystrn(p, (u_char *) " (", ngx_os_argv_last - (char *) p);

        for (i = 0; i < ngx_argc; i++) {
            p = ngx_cpystrn(p, (u_char *) ngx_argv[i],
                            ngx_os_argv_last - (char *) p);
            p = ngx_cpystrn(p, (u_char *) " ", ngx_os_argv_last - (char *) p);
        }

        if (*(p - 1) == ' ') {
            *(p - 1) = ')';
        }
    }

#endif

    if (ngx_os_argv_last - (char *) p) {
        ngx_memset(p, NGX_SETPROCTITLE_PAD, ngx_os_argv_last - (char *) p);
    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
                   "setproctitle: \"%s\"", ngx_os_argv[0]);
}

Finally, a simple example of modifying title according to the idea of nginx is given.

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

#define  NGX_OK          0
#define  NGX_ERROR      -1
#define NGX_SETPROCTITLE_PAD       '\0'

typedef int                 ngx_int_t;
typedef unsigned int        ngx_uint_t;

extern char **environ;

static char *ngx_os_argv_last;

int              ngx_argc;
char           **ngx_os_argv;


u_char *
ngx_cpystrn(u_char *dst, u_char *src, size_t n)
{
    if (n == 0) {
        return dst;
    }

    while (--n) {
        *dst = *src;

        if (*dst == '\0') {
            return dst;
        }

        dst++;
        src++;
    }

    *dst = '\0';

    return dst;
}



ngx_int_t
ngx_init_setproctitle()
{
    u_char      *p;
    size_t       size;
    ngx_uint_t   i;

    size = 0;

    for (i = 0; environ[i]; i++) {
        size += strlen(environ[i]) + 1;
    }

    p = malloc(size);
    if (p == NULL) {
        return NGX_ERROR;
    }

    ngx_os_argv_last = ngx_os_argv[0];

    for (i = 0; ngx_os_argv[i]; i++) {
        if (ngx_os_argv_last == ngx_os_argv[i]) {
            ngx_os_argv_last = ngx_os_argv[i] + strlen(ngx_os_argv[i]) + 1;
        }
    }

    for (i = 0; environ[i]; i++) {
        if (ngx_os_argv_last == environ[i]) {

            size = strlen(environ[i]) + 1;
            ngx_os_argv_last = environ[i] + size;

            ngx_cpystrn(p, (u_char *) environ[i], size);
            environ[i] = (char *) p;
            p += size;
        }
    }

    ngx_os_argv_last--;

    return NGX_OK;
}


void
ngx_setproctitle(char *title)
{
    u_char     *p;

    ngx_os_argv[1] = NULL;

    p = ngx_cpystrn((u_char *) ngx_os_argv[0], (u_char *) "nginx: ",
                    ngx_os_argv_last - ngx_os_argv[0]);

    p = ngx_cpystrn(p, (u_char *) title, ngx_os_argv_last - (char *) p);

    if (ngx_os_argv_last - (char *) p) {
        memset(p, NGX_SETPROCTITLE_PAD, ngx_os_argv_last - (char *) p);
    }

}


static ngx_int_t
ngx_save_argv(int argc, char *const *argv)
{

    ngx_os_argv = (char **) argv;
    ngx_argc = argc;

    return NGX_OK;
}

int main(int argc, char *const *argv){  

    ngx_save_argv(argc,argv);
    ngx_init_setproctitle();

    ngx_setproctitle("xiejian test");

    while(1){
        system("ps -ef|awk '$8 ~ /nginx/ {print $0}'");
        sleep(10);
    }

  return (0);  
}

The results are as follows:

Posted by py343 on Fri, 14 Jun 2019 20:35:13 -0700