bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Feature request: process title with exec -a "" for oneself


From: Emanuele Torre
Subject: Re: Feature request: process title with exec -a "" for oneself
Date: Wed, 4 Sep 2024 23:54:06 +0200
User-agent: Mutt/2.2.13 (2024-03-09)

I think your idea of using  exec -a ''  with no positional arguments to
set it is pretty cool, since  exec -a  changes the argv[0] of the
invoked process and exec with redirections an no arguments applies
redirections to the current shell permanently!

Not super relevant, but I have a bash loadable builtin that can set the
script's proctitle that I wrote for fun ~1 year ago.

It has to use an hack to figure out the right offset of argv[0]: it
abuses the fact that the global variable  shell_name  is always set to a
pointer that is an offset into argv[0], so walking backwards from it
until a NUL is reached finds the start of argv[0].

But, otherwise, it works fairly well; you can also unload it to restore
the original proctitle (enable -d setproctitle), and reload it to change
it again.

I was aware of the prctl(PR_SET_MM_ARG_{START,END}) approach, but I have
never personally tried it.  It does sound like a cleaner approach
though, especially since bash doesn't easily expose argv[0], and we have
to resort to an hack to find it; perhaps you could write your own
loadable builtin that uses that. ^^

I left my loadable builtin at the bottom of the email.

o/
 emanuele6

Build with:

  make CFLAGS="$(pkg-config --libs --cflags bash) -shared" setproctitle

Load with  enable -f ./setproctitle setproctitle

============================ setproctitle.c ============================
#include <errno.h>
#include <string.h>

#include <fcntl.h>

#include <bash/builtins.h>
#include <bash/shell.h>
#include <bash/builtins/bashgetopt.h>
#include <bash/builtins/common.h>

static char *memstart = NULL;
static char *memend = NULL;
static char *memstart_copy = NULL;
static char *original_shell_name = NULL;

static size_t
count_bytes(char const *const path)
{
    size_t size = 0;

    int fd;
    do {
        fd = open(path, O_RDONLY);
    } while (fd < 0 && errno == EINTR);
    if (fd < 0) {
        builtin_warning("%s: Open error: %s", path, strerror(errno));
        return size;
    }

    ssize_t nread;
    do {
        char buf[PIPE_BUF];
        nread = read(fd, buf, sizeof buf);
        if (nread > 0)
            size += nread;
    } while (nread > 0 || (nread != 0 && errno == EINTR));
    if (nread < 0)
        builtin_warning("%s: Read error: %s", path, strerror(errno));

    int ret;
    do {
        ret = close(fd);
    } while (ret < 0 && errno == EINTR);
    if (ret < 0)
        builtin_warning("%s: Close error: %s", path, strerror(errno));

    return size;
}

int
setproctitle_builtin_load(char const *const name)
{
    (void)name;

    /* shell_name is always an offset into argv[0] */
    memstart = shell_name;
    /* find the start of argv[0]; linux seems fine with this approach */
    while (memstart[-1])
        --memstart;

    /* save and copy shell_name to the heap */
    memstart_copy = savestring(memstart);
    original_shell_name = shell_name;
    shell_name = &memstart_copy[shell_name - memstart];

    /* figure out size of usable memory counting bytes in
       /proc/self/cmdline and /proc/self/environ */
    memend = memstart;

    memend += count_bytes("/proc/self/cmdline");
    memend += count_bytes("/proc/self/environ");

    /* fallback to length of argv[0] in case that does not work... */
    if (memend == memstart)
        memend += strlen(memstart) + 1;

    return 1;
}

void
setproctitle_builtin_unload(char const *const name)
{
    (void)name;

    /* restore shell_name */
    (void)strcpy(memstart, memstart_copy);
    shell_name = original_shell_name;
    xfree(memstart_copy);
}

static int
setproctitle_builtin(WORD_LIST *list)
{
    if (no_options(list))
        return EX_USAGE;
    list = loptend;

    (void)memset(memstart, 0, memend - memstart);
    char *memused = memstart;
    for (WORD_LIST const *l = list; l; l = l->next) {
        size_t const maxlen = memend - memused - 1;
        char const *const arg = l->word->word;
        size_t const len = strlen(arg);
        if (len > maxlen) {
            builtin_warning("Too long, trimming...");
            (void)memcpy(memused, arg, maxlen);
            break;
        }
        (void)memcpy(memused, arg, len);
        memused += len;
    }

    return EXECUTION_SUCCESS;
}

static char *setproctitle_doc[] = {
    "Changes the shell's proctitle",
    "",
    "If multiple arguments are passed, they are concatenated.",
    "",
    "Exit Status:",
    "Always returns 0 if the command is valid.",
    NULL,
};

struct builtin setproctitle_struct = {
    "setproctitle",
    setproctitle_builtin,
    BUILTIN_ENABLED,
    setproctitle_doc,
    "setproctitle [title ...]",
    0,
};
========================================================================



reply via email to

[Prev in Thread] Current Thread [Next in Thread]