|
|
Log in / Subscribe / Register

Re: +1 on setproctitle

Re: +1 on setproctitle

Posted Oct 8, 2011 11:49 UTC (Sat) by ldo (guest, #40946)
In reply to: +1 on setproctitle by dskoll
Parent article: A Plumber's Wish List for Linux

Here’s a routine I came up with a while back:

#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>

extern char **
    environ;
static char *
    argstart = NULL;
static size_t
    maxarglen; /* maximum available size of argument area */
static bool
    envmoved = false;

void setproctitle
  (
    char ** argv, /* argv as passed to main, so args can be moved if necessary */
    const char * fmt,
    ...
  )
  /* something as close as possible to BSD setproctitle(3), but for Linux.
    Note I need argv as passed to main, in order to be able to poke the process
    arguments area. Also don't call routines like putenv(3) or setenv(3)
    prior to using this routine. */
  {
  /* Theory of operation: the command-line arguments and environment variables
    for a process can be found from fields arg_start, arg_end, env_start and
    env_end of the mm_struct object defined in include/linux/mm_types.h in the
    current kernel sources. These areas are set up in different ways depending
    on the executable format; for ELF, see the routine create_elf_tables in
    fs/binfmt_elf.c. This puts the envp pointer array immediately following
    the null entry at the end of the argv pointer array, both in the userspace
    stack. The actual strings are stored contiguously in the area beginning at
    mm_struct.arg_start for the argument strings, and mm_struct.env_start
    (immediately follows mm_struct.arg_end) for the environment strings.
    The current process command line is made visible (to utilities like ps etc) via
    the proc_pid_cmdline routine in fs/proc/base.c. This routine checks that the
    byte at mm_struct.arg_end is still a null; if not, it assumes the process
    has overwritten its argument and environment area with an extra-long title,
    and appends the extra data beginning at mm_struct.env_start as well.

    Limitations: this routine can only use the available argument and environment
    area. If the command-line arguments and environment are small to begin with, then
    that limits the length of process title that can be set. Also libc library routines
    like putenv(3) do their own relocation of the environ array and strings; if they
    are used before the first call of this routine that needs to overflow into the
    environment area, then I won't be able to find the original location of the latter.
  */
    char title[512]; /* big enough? */
    ssize_t titlelen;
      {
        va_list args;
        va_start(args, fmt);
        titlelen = vsnprintf(title, sizeof title, fmt, args);
        va_end(args);
        if (titlelen < 0)
          {
            titlelen = 0; /* ignore error */
            title[0] = 0;
          } /*if*/
        titlelen += 1; /* including trailing nul */
        if (titlelen > sizeof title)
          {
            title[sizeof title - 1] = '\0'; /* do I need to do this? */
            titlelen = sizeof title;
          } /*if*/
      }
    if (argstart == NULL)
      {
      /* first call, find and initialize argument area */
        char ** thisarg = argv;
        maxarglen = 0;
        argstart = *thisarg;
        while (*thisarg != NULL)
          {
            maxarglen += strlen(*thisarg++) + 1; /* including terminating nul */
          } /*while*/
        memset(argstart, 0, maxarglen); /* clear it all out */
      } /*if*/
    if (titlelen > maxarglen && !envmoved)
      {
      /* relocate the environment strings and use that area for the command line
        as well */
        char ** srcenv;
        char ** dstenv;
        char ** newenv;
        size_t envlen = 0;
        size_t nrenv = 1; /* nr env strings + 1 for terminating NULL pointer */
        if (argstart + maxarglen == environ[0]) /* not already moved by e.g. libc */
          {
            srcenv = environ;
            while (*srcenv != NULL)
              {
                envlen += strlen(*srcenv++) + 1; /* including terminating nul */
                ++nrenv; /* count 'em up */
              } /*while*/
            newenv = (char **)malloc(sizeof(char *) * nrenv); /* new env array, never freed! */
            srcenv = environ;
            dstenv = newenv;
            while (*srcenv != NULL)
              {
              /* copy the environment strings */
                *dstenv++ = strdup(*srcenv++);
              } /*while*/
            *dstenv = NULL; /* mark end of new environment array */
            memset(environ[0], 0, envlen); /* clear old environment area */
            maxarglen += envlen; /* this much extra space now available */
            environ = newenv; /* so libc etc pick up new environment location */
          } /*if*/
        envmoved = true;
      } /*if*/
    if (titlelen > maxarglen)
      {
        titlelen = maxarglen; /* truncate to fit available area */
      } /*if*/
    if (titlelen > 0)
      {
      /* set the new title */
        const size_t oldtitlelen = strlen(argstart) + 1; /* including trailing nul */
        memcpy(argstart, title, titlelen);
        argstart[titlelen - 1] = '\0'; /* if not already done */
        if (oldtitlelen > titlelen)
          {
          /* wipe out remnants of previous title */
            memset(argstart + titlelen, 0, oldtitlelen - titlelen);
          } /*if*/
      } /*if*/
  } /*setproctitle*/


to post comments

Re: +1 on setproctitle

Posted Oct 9, 2011 3:26 UTC (Sun) by neilbrown (subscriber, #359) [Link]

Neat !!

As long as no-one uses getenv() before-hand and holds on to the string - or uses putenv as you say.

I would probably have a setproctitle_prepare() which is called first-thing in main() and relocates both argv and environ so that the original arg space is not referenced by anything. But that might just be paranoia.

Also - ld-linux.*.so uses environment variables. I wonder if it holds on to any that it looked up before main() was called.. Probably not, that would be asking for trouble.

Re: +1 on setproctitle

Posted Oct 10, 2011 16:57 UTC (Mon) by mezcalero (subscriber, #45103) [Link]

This is more or less what my own Avahi does as well. But it's really broken as it breaks /proc/$PID/environ and suchlike. And it doesn't work at all if the env block passed to you is too small.

It's a hack, nothing more. And what we have been asking for is a nicer solution, that isn't just a hack.

this seems tangential to the desired effect.

Posted Oct 25, 2011 12:59 UTC (Tue) by quanstro (guest, #77996) [Link]

perhaps i'm not following along, but one assumes that changing
argv for internal purposes is already trivial, and requires no
further interface. and i don't see what the process'
view of argv/environment has to do with ps(1)'s view. why not
allow /proc/$pid/args or similar to be writable?


Copyright © 2026, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds