I have a multithread application and I would like that htop (as example) shows a different name per each thread running, at the moment what it shows is the "command line" used to run the main.
I have tried using
prctl(PR_SET_NAME, .....)
but it works only with top and with that call is only possible specify names up to 16 bytes.
I guess the trick is to modify the /proc/PID/cmdline content but that is a readonly field.
Anyone knows how to achieve it ?
Since version 0.8.4, htop has an option: Show custom thread names
Press F2 and select the Display options menu. You should see:
You have to distinguish between per-thread and per-process setting here.
prctl(PR_SET_NAME, ...) sets the name (up to 16 bytes) on a per-thread basis, and you can force "ps" to show that name with the c switch (ps Hcx for example). You can do the same with the c switch in top, so I assume htop has similar functionality.
What "ps" normally shows you (ps Hax for example) is the command line name and arguments you started your program with (indeed what /proc/PID/cmdline tells you), and you can modify those by directly modifying argv[0] (up to its original length), but that is a per-process setting, meaning you cannot give different names to different threads that way.
Following is the code I normally use to change the process name as a whole:
// procname is the new process name
char *procname = "new process name";
// Then let's directly modify the arguments
// This needs a pointer to the original arvg, as passed to main(),
// and is limited to the length of the original argv[0]
size_t argv0_len = strlen(argv[0]);
size_t procname_len = strlen(procname);
size_t max_procname_len = (argv0_len > procname_len) ? (procname_len) : (argv0_len);
// Copy the maximum
strncpy(argv[0], procname, max_procname_len);
// Clear out the rest (yes, this is needed, or the remaining part of the old
// process name will still show up in ps)
memset(&argv[0][max_procname_len], '\0', argv0_len - max_procname_len);
// Clear the other passed arguments, optional
// Needs to know argv and argc as passed to main()
//for (size_t i = 1; i < argc; i++) {
// memset(argv[i], '\0', strlen(argv[i]));
//}
Related
I'm writing a C program that uses execlp() to run the linux command-line tool, convert. This command takes optional arguments. However, when using it with execlp(), my C program doesn't recognize the flags I pass in and thus doesn't do the command properly.
For example, if I were to run this command in terminal convert -resize 10% src.jpg dst.jpg it will resize the src image by 10%, saving it to dst. However when I run my C program with this code
rc = execlp("convert", "-resize 10%", src, dst, NULL);
my computer doesn't recognize the resize -10% flag and doesn't do anything to my source image. Why is that?
By convention, the first parameter to a process (accessible as argv[0]) is the name of the process. You haven't included that, so "-resize 10%" is read as the process name instead of an option.
Also, "-resize 10%" is actually two parameters separated by a space, so you need to split them up.
rc = execlp("convert", "convert", "-resize", "10%", src, dst, NULL);
Most likely, the -resize should be one option and 10% should be another:
rc = execlp("convert", "convert", "-resize", "10%", src, dst, NULL);
Using execlp() is a bad idea if you have variable numbers of arguments — use execvp() instead, building an array of arguments terminated by a null pointer. Use execlp() only when the argument list is fixed.
char *args[6];
int i = 0;
args[i++] = "convert";
args[i++] = "-resize";
args[i++] = "10%";
args[i++] = src;
args[i++] = dst;
args[i++] = NULL;
rc = execvp(args[0], args);
Note that this formula ensures that the program name is passed correctly — once as a string that is searched for on $PATH, and once as the argv[0] of the executed program.
With execlp(), as dbush notes, you have to repeat the command name — once to specify the executable and once to specify the value for argv[0].
Note too that there is nothing to stop you from telling the program via argv[0] that it has a wholly different name from the name that you execute. This rarely happens (shells don't do it) but when you write the code yourself, it is possible.
I have a program written in C which runs on Linux only. I want to be able to change the process name as shown in the ps command. For this I am directly changing the string in argv[0] and also using prctl(PR_SET_NAME, argv[0]) call from the main thread. I also want to access the /proc/self/cmdline from dynamically loaded shared libraries and in future maybe even from other programs.
I read that for this to work, I have to use the original memory space starting at argv[0]. The ELF standard specifies that this space is \0 separated from environ space. Looking into ps_status.c from Postgres code, one can see that they are using all this space for argv strings. And really, when I memset this space to 'a', I can see over 3000 chars in ps and read it from /proc filesystem both. Problem starts when I try to use this space to dynamically (at runtime) create new arguments in this space. (I have read and from basic tests know that Chrome/Chromium does something similar - export status of it's forked processes in ps by command line arguments.) Anything which contains the NULL delimiter in space reaching into originally environment is treated as an end. (I originally had 105 chars in cmdline arguments, I am able to get 130 chars but others arguments up to this 3000 char mark are not read.) From this I gather that the system remembers the original size and is only letting me "read over" until the end of string. (Changing the char** argv pointer is no help.)
But the Chrome is somehow doing this. Looking into command_line.cc source I see no immediate way how.
Is it even possible to do this this way? And if so, how? To tell the Linux kernel that the size of argv memory and argc changed?
Thank you.
PR_SET_MM_ARG_START and PR_SET_MM_ARG_END let you do this, if you're root (more specifically, if the process has the CAP_SYS_RESOURCE capability).
Usage:
prctl(PR_SET_NAME, constructed_argv[0]);
prctl(PR_SET_MM, PR_SET_MM_ARG_START, constructed_argv, 0, 0);
prctl(PR_SET_MM, PR_SET_MM_ARG_END, end_of_constructed_argv, 0, 0);
Here's an example of well-documented usage of them from systemd:
/* Now, let's tell the kernel about this new memory */
if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
/* HACK: prctl() API is kind of dumb on this point. The existing end address may already be
* below the desired start address, in which case the kernel may have kicked this back due
* to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in
* action). The proper solution would be to have a prctl() API that could set both start+end
* simultaneously, or at least let us query the existing address to anticipate this condition
* and respond accordingly. For now, we can only guess at the cause of this failure and try
* a workaround--which will briefly expand the arg space to something potentially huge before
* resizing it to what we want. */
log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m");
if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) {
log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m");
(void) munmap(nn, nn_size);
goto use_saved_argv;
}
if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m");
goto use_saved_argv;
}
} else {
/* And update the end pointer to the new end, too. If this fails, we don't really know what
* to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure,
* and continue. */
if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0)
log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
}
Chrome doesn't have a special trick; rather, it's cheating. It is overwriting into the environ area, but it uses spaces instead of null bytes to separate arguments. This looks exactly the same in ps, but if you examine the /proc/PID/environ file with xxd or similar, you'll see what it's doing. This lets it basically ignore the constraint you found of "Anything which contains the NULL delimiter in space reaching into originally environment is treated as an end."
I'm working on an email client in C language on my linux system, and bash helped me alot with implementing some small hacks.
The C client fetches an email and redirects the string or data to a script that have other things to do with that data or line of string.
But the problem is that sometime the "line of string" that is fetched from the online email account have special punctuation characters which when piped to the shell script, in most cases the script fails to execute while rarely changes the meaning of the original task of the script.
This is very similar to SQL Injection attacks.
I believe this could be very dangerous as well, imagine an email sent to you by someone which contains some shell commands which could wipe off your HDD or something similar.?
Right now the problem that is very common to me is that most of the "Subject" and "FROM" header fields of an email contains quoted strings which when piped to that script it causes errors like:
sh: 1: cannot open myemail#gmail.com: No such file
sh: 3: Syntax error: Unterminated quoted string
These two header fields are supposed to be piped are first appended as in C:
char *Subject=getSubject();
char *From=getFrom();
char completeString[256];
sprintf(completeString,"./MyShellScript.sh \"Subject: %s and From: %s\"",Subject,From);
/* Then executed via the system call */
system(completeString);
I think the problem is that i have already provided quotes for the strings but when the "Subject" or "FROM" is already in quotes then the script gives the above shown errors.
Basically the shell script has a line for string input like this:
someOtherProgram -w=/tmp/temp.txt "$1"
This line expects a String like this "Hello Subject" and not ""Hello Subject""
What to i do.?
Don't ever call system(completeString) with a string generated from untrusted data. Instead, pass the arguments out-of-band from the invocation by using an execv-family call:
pid_t pid = fork()
int status;
if (pid < 0) { /* the fork failed */
exit(1);
} else if (pid > 0) { /* the fork succeeded, and we're the parent */
/* wait for the child to exit and collect its status */
waitpid(pid, &status, 0);
} else { /* the fork succeeded, and we're the child */
/* replace ourselves (as the child process) with an instance of the script */
/* executable argv[0] argv[1] argv[2] terminator */
execlp("./MyShellScript", "MyShellScript", Subject, From, NULL);
}
On the other hand, if you really want to use system(), you can use a constant string and pass your variables via the environment:
/* this is a hardcoded string; it includes "$Subject", **not** the actual subject
* given to you by the remote system.
*/
const char shellScript[] = "./MyShellScript \"$Subject\" \"$From\"";
/* ...then, after we have a subject, we can set them in the environment */
if(setenv("Subject", Subject, 1) == 0 && setenv("From", From, 1) == 0) {
system(shellScript);
unsetenv("Subject"); /* avoid leaking data to other parts or children of your program */
unsetenv("From");
} else { /* setenv failed -- maybe we were run with a very full environment? */
/* (or maybe the remote user provided a too-large Subject or From) */
exit(1);
}
This is safe because the expansion is performed only after the shell has been started: The code that's actually invoked by the copy of sh -c started by system() is the constant string that you've (hopefully) audited to be correct.
Note that since environment variables are automatically converted to shell variables, you don't strictly even need to pass "$Subject" and "$From" in your explicit command -- if you made the environment part of MyShellScript's documented calling convention, it could simply refer directly to "$Subject" and "$From" and see the values that were placed in the environment via setenv().
I am trying to print logs dynamically. What I have done is I have a debug variable which I set in my own stat_my.c file. Below is show_stat function.
extern int local_debug_lk;
static int show_stat(struct seq_file *p, void *v)
{
int temp=0;
if(local_debug_lk == 0)
{
seq_printf(p,"local_debug_lk=0, enabling,int_num=%d\n",int_num);
local_debug_lk=1;
}
else
{
seq_printf(p,"local_debug_lk=:%d,int_num=%d\n",local_debug_lk,int_num);
while(temp<int_num){
seq_printf(p,"%d\n",intr_list_seq[temp]);
temp++;
}
local_debug_lk=0;
int_num=0;
}
return 0;
}
Driver file
int local_debug_lk, int_num;
isr_root(...){
/*
logic to extract IRQ number, saved in vect variable
*/
if(local_debug_lk && (int_num < 50000)){
intr_list_seq[int_num]=vect;
int_num++;
}
What I expect is when I do "cat /proc/show_stat", first it will enable local_debug_lk flag and whenever an interrupt occurs in driver file, it will be stored in intr_list_seq[] array. and when I do "cat /proc/stat_my" second time, it should print IRQ sequence and disable IRQ recording by setting local_debug_lk=0.
But…what's happening is, I am always getting
"local_debug_lk=0, enabling,int_num=0" log on cat; i.e. local_debug_lk is always zero; it never gets enabled.
Also, when my driver is not running, it works fine!
On two consecutive "cat /proc/stat_my", first value is set to 1 and then 0 again.
Is it possible my driver is not picking latest updated value of local_debug_lk variable?
Could you please let me know what I am doing wrong here?
It could be more calls to .show function than readings from the file (with cat /proc/show_stat). Moreover underlying system expects stable results from .show: if called with the same parameters, the function should print the same information to the seq_file.
Because of that, switching a flag in the .show function has a little sence, and making the function's output dependent on this flag is simply wrong.
Generally, changing any kernel state when a file is read is not what expected by the user. It is better to use write functionality for that.
Function .show actually prints information into temporary kernel buffer. If everything goes OK, information from the buffer is transmitted into user buffer and eventually is printed by cat. But if kernel buffer is too small, information printed into it is discarded. In that case underlying system allocates bigger buffer, and call .show again.
Also, .show is rerun if user buffer is too small to accomodate all information printed.
I am building a Linux Shell, and my current headache is passing command line arguments to forked/exec'ed programs and system functions.
Currently all input is tokenized on spaces and new lines, in a global variable char * parsed_arguments. For example, the input dir /usa/folderb would be tokenized as:
parsed_arguments[0] = dir
parsed_arguments[1] = /usa/folderb
parsed_arguments tokenizes everything perfectly; My issue now is that i wish to only take a subset of parsed_arguments, which excludes the command/ first argument/path to executable to run in the shell, and store them in a new array, called passed_arguments.
so in the previous example dir /usa/folderb
parsed_arguments[0] = dir
parsed_arguments[1] = /usa/folderb
passed_arguments[0] = /usa/folderb
passed_arguments[1] = etc....
Currently I am not having any luck with this so I'm hoping someone could help me with this. Here is some code of what I have working so far:
How I'm trying to copy arguments:
void command_Line()
{
int i = 1;
for(i;parsed_arguments[i]!=NULL;i++)
printf("%s",parsed_arguments[i]);
}
Function to read commands:
void readCommand(char newcommand[]){
printf("readCommand: %s\n", newcommand);
//parsed_arguments = (char* malloc(MAX_ARGS));
// strcpy(newcommand,inputstring);
parsed = parsed_arguments;
*parsed++ = strtok(newcommand,SEPARATORS); // tokenize input
while ((*parsed++ = strtok(NULL,SEPARATORS)))
//printf("test1\n"); // last entry will be NULL
//passed_arguments=parsed_arguments[1];
if(parsed[0]){
char *initial_command =parsed[0];
parsed= parsed_arguments;
while (*parsed) fprintf(stdout,"%s\n ",*parsed++);
// free (parsed);
// free(parsed_arguments);
}//end of if
command_Line();
}//end of ReadCommand
Forking function:
else if(strstr(parsed_arguments[0],"./")!=NULL)
{
int pid;
switch(pid=fork()){
case -1:
printf("Fork error, aborting\n");
abort();
case 0:
execv(parsed_arguments[0],passed_arguments);
}
}
This is what my shell currently outputs. The first time I run it, it outputs something close to what I want, but every subsequent call breaks the program. In addition, each additional call appends the parsed arguments to the output.
This is what the original shell produces. Again it's close to what I want, but not quite. I want to omit the command (i.e. "./testline").
Your testline program is a sensible one to have in your toolbox; I have a similar program that I call al (for Argument List) that prints its arguments, one per line. It doesn't print argv[0] though (I know it is called al). You can easily arrange for your testline to skip argv[0] too. Note that Unix convention is that argv[0] is the name of the program; you should not try to change that (you'll be fighting against the entire system).
#include <stdio.h>
int main(int argc, char **argv)
{
while (*++argv != 0)
puts(*argv);
return 0;
}
Your function command_line() is also reasonable except that it relies unnecessarily on global variables. Think of global variables as a nasty smell (H2S, for example); avoid them when you can. It should be more like:
void command_Line(char *argv[])
{
for (int i = 1; argv[i] != NULL; i++)
printf("<<%s>>\n", argv[i]);
}
If you're stuck with C89, you'll need to declare int i; outside the loop and use just for (i = 1; ...) in the loop control. Note that the printing here separates each argument on a line on its own, and encloses it in marker characters (<< and >> — change to suit your whims and prejudices). It would be fine to skip the newline in the loop (maybe use a space instead), and then add a newline after the loop (putchar('\n');). This makes a better, more nearly general purpose debug routine. (When I write a 'dump' function, I usually use void dump_argv(FILE *fp, const char *tag, char *argv[]) so that I can print to standard error or standard output, and include a tag string to identify where the dump is written.)
Unfortunately, given the fragmentary nature of your readCommand() function, it is not possible to coherently critique it. The commented out lines are enough to elicit concern, but without the actual code you're running, we can't guess what problems or mistakes you're making. As shown, it is equivalent to:
void readCommand(char newcommand[])
{
printf("readCommand: %s\n", newcommand);
parsed = parsed_arguments;
*parsed++ = strtok(newcommand, SEPARATORS);
while ((*parsed++ = strtok(NULL, SEPARATORS)) != 0)
{
if (parsed[0])
{
char *initial_command = parsed[0];
parsed = parsed_arguments;
while (*parsed)
fprintf(stdout, "%s\n ", *parsed++);
}
}
command_Line();
}
The variables parsed and parsed_arguments are both globals and the variable initial_command is set but not used (aka 'pointless'). The if (parsed[0]) test is not safe; you incremented the pointer in the previous line, so it is pointing at indeterminate memory.
Superficially, judging from the screen shots, you are not resetting the parsed_arguments[] and/or passed_arguments[] arrays correctly on the second use; it might be an index that is not being set to zero. Without knowing how the data is allocated, it is hard to know what you might be doing wrong.
I recommend closing this question, going back to your system and producing a minimal SSCCE. It should be under about 100 lines; it need not do the execv() (or fork()), but should print the commands to be executed using a variant of the command_Line() function above. If this answer prevents you deleting (closing) this question, then edit it with your SSCCE code, and notify me with a comment to this answer so I get to see you've done that.